From cced8ba4245a061ab047a0a6882468d75d619dd9 Mon Sep 17 00:00:00 2001 From: Artem Serov Date: Wed, 19 Jul 2017 18:18:09 +0100 Subject: [PATCH 001/226] ART: Introduce individual HInstruction cloning. Introduce API for HInstruction cloning, support it for a few instructions. add a gtest. Test: cloner_test.cc, test-art-target, test-art-host. Change-Id: I8b6299be5d04a26390d9ef13a20ce82ee5ae4afe --- compiler/Android.bp | 1 + compiler/optimizing/cloner_test.cc | 185 ++++++ compiler/optimizing/instruction_simplifier.cc | 9 + compiler/optimizing/nodes.cc | 29 + compiler/optimizing/nodes.h | 554 ++++++++++++------ compiler/optimizing/nodes_mips.h | 13 +- compiler/optimizing/nodes_shared.h | 23 +- compiler/optimizing/nodes_vector.h | 127 ++-- compiler/optimizing/nodes_x86.h | 17 +- 9 files changed, 696 insertions(+), 262 deletions(-) create mode 100644 compiler/optimizing/cloner_test.cc diff --git a/compiler/Android.bp b/compiler/Android.bp index 859947108e..249aaf5632 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -323,6 +323,7 @@ art_cc_test { "linker/method_bss_mapping_encoder_test.cc", "linker/output_stream_test.cc", "optimizing/bounds_check_elimination_test.cc", + "optimizing/cloner_test.cc", "optimizing/data_type_test.cc", "optimizing/dominator_test.cc", "optimizing/find_loops_test.cc", diff --git a/compiler/optimizing/cloner_test.cc b/compiler/optimizing/cloner_test.cc new file mode 100644 index 0000000000..d34dd81767 --- /dev/null +++ b/compiler/optimizing/cloner_test.cc @@ -0,0 +1,185 @@ +/* + * 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. + */ + +#include "graph_checker.h" +#include "nodes.h" +#include "optimizing_unit_test.h" + +#include "gtest/gtest.h" + +namespace art { + +// 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 ClonerTest : public OptimizingUnitTest { + public: + ClonerTest() + : graph_(CreateGraph()), entry_block_(nullptr), exit_block_(nullptr), parameter_(nullptr) {} + + void CreateBasicLoopControlFlow(/* out */ HBasicBlock** header_p, + /* out */ HBasicBlock** body_p) { + entry_block_ = new (GetAllocator()) HBasicBlock(graph_); + graph_->AddBlock(entry_block_); + graph_->SetEntryBlock(entry_block_); + + 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_); + + 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(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) { + uint32_t dex_pc = 0; + + // Entry block. + HIntConstant* const_0 = graph_->GetIntConstant(0); + HIntConstant* const_1 = graph_->GetIntConstant(1); + HIntConstant* const_128 = graph_->GetIntConstant(128); + + // Header block. + HPhi* phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32); + HInstruction* suspend_check = new (GetAllocator()) HSuspendCheck(); + + 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 body block. + HInstruction* null_check = new (GetAllocator()) HNullCheck(parameter_, dex_pc); + HInstruction* array_length = new (GetAllocator()) HArrayLength(null_check, dex_pc); + HInstruction* bounds_check = new (GetAllocator()) HBoundsCheck(phi, array_length, dex_pc); + 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* induction_inc = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi, const_1); + + loop_body->AddInstruction(null_check); + loop_body->AddInstruction(array_length); + loop_body->AddInstruction(bounds_check); + loop_body->AddInstruction(array_get); + loop_body->AddInstruction(add); + loop_body->AddInstruction(array_set); + loop_body->AddInstruction(induction_inc); + loop_body->AddInstruction(new (GetAllocator()) HGoto()); + + phi->AddInput(const_0); + phi->AddInput(induction_inc); + + graph_->SetHasBoundsChecks(true); + + // Adjust HEnvironment for each instruction which require that. + ArenaVector current_locals({phi, const_128, parameter_}, + GetAllocator()->Adapter(kArenaAllocInstruction)); + + HEnvironment* env = ManuallyBuildEnvFor(suspend_check, ¤t_locals); + null_check->CopyEnvironmentFrom(env); + bounds_check->CopyEnvironmentFrom(env); + } + + HEnvironment* ManuallyBuildEnvFor(HInstruction* instruction, + ArenaVector* current_locals) { + HEnvironment* environment = new (GetAllocator()) HEnvironment( + (GetAllocator()), + current_locals->size(), + graph_->GetArtMethod(), + instruction->GetDexPc(), + instruction); + + environment->CopyFrom(ArrayRef(*current_locals)); + instruction->SetRawEnvironment(environment); + return environment; + } + + bool CheckGraph() { + GraphChecker checker(graph_); + checker.Run(); + if (!checker.IsValid()) { + for (const std::string& error : checker.GetErrors()) { + std::cout << error << std::endl; + } + return false; + } + return true; + } + + HGraph* graph_; + + HBasicBlock* entry_block_; + HBasicBlock* exit_block_; + + HInstruction* parameter_; +}; + +TEST_F(ClonerTest, IndividualInstrCloner) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + + CreateBasicLoopControlFlow(&header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + graph_->BuildDominatorTree(); + ASSERT_TRUE(CheckGraph()); + + HSuspendCheck* old_suspend_check = header->GetLoopInformation()->GetSuspendCheck(); + CloneAndReplaceInstructionVisitor visitor(graph_); + // Do instruction cloning and replacement twice with different visiting order. + + visitor.VisitInsertionOrder(); + size_t instr_replaced_by_clones_count = visitor.GetInstrReplacedByClonesCount(); + EXPECT_EQ(instr_replaced_by_clones_count, 12u); + EXPECT_TRUE(CheckGraph()); + + visitor.VisitReversePostOrder(); + instr_replaced_by_clones_count = visitor.GetInstrReplacedByClonesCount(); + EXPECT_EQ(instr_replaced_by_clones_count, 24u); + EXPECT_TRUE(CheckGraph()); + + HSuspendCheck* new_suspend_check = header->GetLoopInformation()->GetSuspendCheck(); + EXPECT_NE(new_suspend_check, old_suspend_check); + EXPECT_NE(new_suspend_check, nullptr); +} + +} // namespace art diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 2bd2d5f0a1..7a1659e9d5 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -27,6 +27,10 @@ namespace art { +// Whether to run an exhaustive test of individual HInstructions cloning when each instruction +// is replaced with its copy if it is clonable. +static constexpr bool kTestInstructionClonerExhaustively = false; + class InstructionSimplifierVisitor : public HGraphDelegateVisitor { public: InstructionSimplifierVisitor(HGraph* graph, @@ -130,6 +134,11 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { }; void InstructionSimplifier::Run() { + if (kTestInstructionClonerExhaustively) { + CloneAndReplaceInstructionVisitor visitor(graph_); + visitor.VisitReversePostOrder(); + } + InstructionSimplifierVisitor visitor(graph_, codegen_, compiler_driver_, stats_); visitor.Run(); } diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index f4f6434678..b90ff590dd 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -845,6 +845,13 @@ static void UpdateInputsUsers(HInstruction* instruction) { DCHECK(!instruction->HasEnvironment()); } +void HBasicBlock::ReplaceAndRemovePhiWith(HPhi* initial, HPhi* replacement) { + DCHECK(initial->GetBlock() == this); + InsertPhiAfter(replacement, initial); + initial->ReplaceWith(replacement); + RemovePhi(initial); +} + void HBasicBlock::ReplaceAndRemoveInstructionWith(HInstruction* initial, HInstruction* replacement) { DCHECK(initial->GetBlock() == this); @@ -2902,6 +2909,28 @@ void HInstruction::RemoveEnvironmentUsers() { env_uses_.clear(); } +HInstruction* ReplaceInstrOrPhiByClone(HInstruction* instr) { + HInstruction* clone = instr->Clone(instr->GetBlock()->GetGraph()->GetAllocator()); + HBasicBlock* block = instr->GetBlock(); + + if (instr->IsPhi()) { + HPhi* phi = instr->AsPhi(); + DCHECK(!phi->HasEnvironment()); + HPhi* phi_clone = clone->AsPhi(); + block->ReplaceAndRemovePhiWith(phi, phi_clone); + } else { + block->ReplaceAndRemoveInstructionWith(instr, clone); + if (instr->HasEnvironment()) { + clone->CopyEnvironmentFrom(instr->GetEnvironment()); + HLoopInformation* loop_info = block->GetLoopInformation(); + if (instr->IsSuspendCheck() && loop_info != nullptr) { + loop_info->SetSuspendCheck(clone->AsSuspendCheck()); + } + } + } + return clone; +} + // Returns an instruction with the opposite Boolean value from 'cond'. HInstruction* HGraph::InsertOppositeCondition(HInstruction* cond, HInstruction* cursor) { ArenaAllocator* allocator = GetAllocator(); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 29c78a1e34..5392ba6936 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1159,6 +1159,8 @@ class HBasicBlock : public ArenaObject { // Insert `instruction` before/after an existing instruction `cursor`. void InsertInstructionBefore(HInstruction* instruction, HInstruction* cursor); void InsertInstructionAfter(HInstruction* instruction, HInstruction* cursor); + // Replace phi `initial` with `replacement` within this block. + void ReplaceAndRemovePhiWith(HPhi* initial, HPhi* replacement); // Replace instruction `initial` with `replacement` within this block. void ReplaceAndRemoveInstructionWith(HInstruction* initial, HInstruction* replacement); @@ -1479,18 +1481,31 @@ FOR_EACH_INSTRUCTION(FORWARD_DECLARATION) #undef FORWARD_DECLARATION #define DECLARE_INSTRUCTION(type) \ + private: \ + H##type& operator=(const H##type&) = delete; \ + public: \ InstructionKind GetKindInternal() const OVERRIDE { return k##type; } \ 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()); \ + } \ void Accept(HGraphVisitor* visitor) OVERRIDE #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; } +#define DEFAULT_COPY_CONSTRUCTOR(type) \ + explicit H##type(const H##type& other) = default; + template class HUseListNode : public ArenaObject, public IntrusiveForwardListNode> { @@ -2181,6 +2196,25 @@ class HInstruction : public ArenaObject { FOR_EACH_ABSTRACT_INSTRUCTION(INSTRUCTION_TYPE_CHECK) #undef INSTRUCTION_TYPE_CHECK + // 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 + // the instruction then the behaviour of this function is undefined. + // + // Note: It is semantically valid to create a clone of the instruction only until + // prepare_for_register_allocator phase as lifetime, intervals and codegen info are not + // copied. + // + // Note: HEnvironment and some other fields are not copied and are set to default values, see + // 'explicit HInstruction(const HInstruction& other)' for details. + virtual HInstruction* Clone(ArenaAllocator* arena ATTRIBUTE_UNUSED) const { + LOG(FATAL) << "Cloning is not implemented for the instruction " << + DebugName() << " " << GetId(); + UNREACHABLE(); + } + + // Return whether instruction can be cloned (copied). + virtual bool IsClonable() const { return false; } + // Returns whether the instruction can be moved within the graph. // TODO: this method is used by LICM and GVN with possibly different // meanings? split and rename? @@ -2297,6 +2331,30 @@ class HInstruction : public ArenaObject { packed_fields_ = BitFieldType::Update(value, packed_fields_); } + // Copy construction for the instruction (used for Clone function). + // + // Fields (e.g. lifetime, intervals and codegen info) associated with phases starting from + // prepare_for_register_allocator are not copied (set to default values). + // + // Copy constructors must be provided for every HInstruction type; default copy constructor is + // fine for most of them. However for some of the instructions a custom copy constructor must be + // specified (when instruction has non-trivially copyable fields and must have a special behaviour + // for copying them). + explicit HInstruction(const HInstruction& other) + : previous_(nullptr), + next_(nullptr), + block_(nullptr), + dex_pc_(other.dex_pc_), + id_(-1), + ssa_index_(-1), + packed_fields_(other.packed_fields_), + environment_(nullptr), + locations_(nullptr), + live_interval_(nullptr), + lifetime_position_(kNoLifetime), + side_effects_(other.side_effects_), + reference_type_handle_(other.reference_type_handle_) {} + private: void FixUpUserRecordsAfterUseInsertion(HUseList::iterator fixup_end) { auto before_use_node = uses_.before_begin(); @@ -2386,8 +2444,6 @@ class HInstruction : public ArenaObject { friend class HEnvironment; friend class HGraph; friend class HInstructionList; - - DISALLOW_COPY_AND_ASSIGN(HInstruction); }; std::ostream& operator<<(std::ostream& os, const HInstruction::InstructionKind& rhs); @@ -2483,10 +2539,9 @@ class HVariableInputSizeInstruction : public HInstruction { : HInstruction(side_effects, dex_pc), inputs_(number_of_inputs, allocator->Adapter(kind)) {} - ArenaVector> inputs_; + DEFAULT_COPY_CONSTRUCTOR(VariableInputSizeInstruction); - private: - DISALLOW_COPY_AND_ASSIGN(HVariableInputSizeInstruction); + ArenaVector> inputs_; }; template @@ -2501,6 +2556,9 @@ class HTemplateInstruction: public HInstruction { return ArrayRef>(inputs_); } + protected: + DEFAULT_COPY_CONSTRUCTOR(TemplateInstruction); + private: std::array, N> inputs_; @@ -2521,6 +2579,9 @@ class HTemplateInstruction<0>: public HInstruction { return ArrayRef>(); } + protected: + DEFAULT_COPY_CONSTRUCTOR(TemplateInstruction<0>); + private: friend class SsaBuilder; }; @@ -2546,6 +2607,7 @@ class HExpression : public HTemplateInstruction { 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 @@ -2559,8 +2621,8 @@ class HReturnVoid FINAL : public HTemplateInstruction<0> { DECLARE_INSTRUCTION(ReturnVoid); - private: - DISALLOW_COPY_AND_ASSIGN(HReturnVoid); + protected: + DEFAULT_COPY_CONSTRUCTOR(ReturnVoid); }; // Represents dex's RETURN opcodes. A HReturn is a control flow @@ -2576,8 +2638,8 @@ class HReturn FINAL : public HTemplateInstruction<1> { DECLARE_INSTRUCTION(Return); - private: - DISALLOW_COPY_AND_ASSIGN(HReturn); + protected: + DEFAULT_COPY_CONSTRUCTOR(Return); }; class HPhi FINAL : public HVariableInputSizeInstruction { @@ -2603,6 +2665,8 @@ class HPhi FINAL : public HVariableInputSizeInstruction { SetPackedFlag(true); } + bool IsClonable() const OVERRIDE { return true; } + // Returns a type equivalent to the given `type`, but that a `HPhi` can hold. static DataType::Type ToPhiType(DataType::Type type) { return DataType::Kind(type); @@ -2665,6 +2729,9 @@ class HPhi FINAL : public HVariableInputSizeInstruction { DECLARE_INSTRUCTION(Phi); + protected: + DEFAULT_COPY_CONSTRUCTOR(Phi); + private: static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits; static constexpr size_t kFieldTypeSize = @@ -2676,8 +2743,6 @@ class HPhi FINAL : public HVariableInputSizeInstruction { using TypeField = BitField; const uint32_t reg_number_; - - DISALLOW_COPY_AND_ASSIGN(HPhi); }; // The exit instruction is the only instruction of the exit block. @@ -2691,8 +2756,8 @@ class HExit FINAL : public HTemplateInstruction<0> { DECLARE_INSTRUCTION(Exit); - private: - DISALLOW_COPY_AND_ASSIGN(HExit); + protected: + DEFAULT_COPY_CONSTRUCTOR(Exit); }; // Jumps from one block to another. @@ -2700,6 +2765,7 @@ class HGoto FINAL : public HTemplateInstruction<0> { public: explicit HGoto(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) {} + bool IsClonable() const OVERRIDE { return true; } bool IsControlFlow() const OVERRIDE { return true; } HBasicBlock* GetSuccessor() const { @@ -2708,8 +2774,8 @@ class HGoto FINAL : public HTemplateInstruction<0> { DECLARE_INSTRUCTION(Goto); - private: - DISALLOW_COPY_AND_ASSIGN(HGoto); + protected: + DEFAULT_COPY_CONSTRUCTOR(Goto); }; class HConstant : public HExpression<0> { @@ -2732,8 +2798,8 @@ class HConstant : public HExpression<0> { DECLARE_ABSTRACT_INSTRUCTION(Constant); - private: - DISALLOW_COPY_AND_ASSIGN(HConstant); + protected: + DEFAULT_COPY_CONSTRUCTOR(Constant); }; class HNullConstant FINAL : public HConstant { @@ -2751,12 +2817,14 @@ class HNullConstant FINAL : public HConstant { DECLARE_INSTRUCTION(NullConstant); + protected: + DEFAULT_COPY_CONSTRUCTOR(NullConstant); + private: explicit HNullConstant(uint32_t dex_pc = kNoDexPc) : HConstant(DataType::Type::kReference, dex_pc) {} friend class HGraph; - DISALLOW_COPY_AND_ASSIGN(HNullConstant); }; // Constants of the type int. Those can be from Dex instructions, or @@ -2788,6 +2856,9 @@ class HIntConstant FINAL : public HConstant { DECLARE_INSTRUCTION(IntConstant); + protected: + DEFAULT_COPY_CONSTRUCTOR(IntConstant); + private: explicit HIntConstant(int32_t value, uint32_t dex_pc = kNoDexPc) : HConstant(DataType::Type::kInt32, dex_pc), value_(value) {} @@ -2799,7 +2870,6 @@ class HIntConstant FINAL : public HConstant { friend class HGraph; ART_FRIEND_TEST(GraphTest, InsertInstructionBefore); ART_FRIEND_TYPED_TEST(ParallelMoveTest, ConstantLast); - DISALLOW_COPY_AND_ASSIGN(HIntConstant); }; class HLongConstant FINAL : public HConstant { @@ -2822,6 +2892,9 @@ class HLongConstant FINAL : public HConstant { DECLARE_INSTRUCTION(LongConstant); + protected: + DEFAULT_COPY_CONSTRUCTOR(LongConstant); + private: explicit HLongConstant(int64_t value, uint32_t dex_pc = kNoDexPc) : HConstant(DataType::Type::kInt64, dex_pc), value_(value) {} @@ -2829,7 +2902,6 @@ class HLongConstant FINAL : public HConstant { const int64_t value_; friend class HGraph; - DISALLOW_COPY_AND_ASSIGN(HLongConstant); }; class HFloatConstant FINAL : public HConstant { @@ -2871,6 +2943,9 @@ class HFloatConstant FINAL : public HConstant { DECLARE_INSTRUCTION(FloatConstant); + protected: + DEFAULT_COPY_CONSTRUCTOR(FloatConstant); + private: explicit HFloatConstant(float value, uint32_t dex_pc = kNoDexPc) : HConstant(DataType::Type::kFloat32, dex_pc), value_(value) {} @@ -2882,7 +2957,6 @@ class HFloatConstant FINAL : public HConstant { // Only the SsaBuilder and HGraph can create floating-point constants. friend class SsaBuilder; friend class HGraph; - DISALLOW_COPY_AND_ASSIGN(HFloatConstant); }; class HDoubleConstant FINAL : public HConstant { @@ -2922,6 +2996,9 @@ class HDoubleConstant FINAL : public HConstant { DECLARE_INSTRUCTION(DoubleConstant); + protected: + DEFAULT_COPY_CONSTRUCTOR(DoubleConstant); + private: explicit HDoubleConstant(double value, uint32_t dex_pc = kNoDexPc) : HConstant(DataType::Type::kFloat64, dex_pc), value_(value) {} @@ -2933,7 +3010,6 @@ class HDoubleConstant FINAL : public HConstant { // Only the SsaBuilder and HGraph can create floating-point constants. friend class SsaBuilder; friend class HGraph; - DISALLOW_COPY_AND_ASSIGN(HDoubleConstant); }; // Conditional branch. A block ending with an HIf instruction must have @@ -2945,6 +3021,7 @@ class HIf FINAL : public HTemplateInstruction<1> { SetRawInputAt(0, input); } + bool IsClonable() const OVERRIDE { return true; } bool IsControlFlow() const OVERRIDE { return true; } HBasicBlock* IfTrueSuccessor() const { @@ -2957,8 +3034,8 @@ class HIf FINAL : public HTemplateInstruction<1> { DECLARE_INSTRUCTION(If); - private: - DISALLOW_COPY_AND_ASSIGN(HIf); + protected: + DEFAULT_COPY_CONSTRUCTOR(If); }; @@ -3011,6 +3088,9 @@ class HTryBoundary FINAL : public HTemplateInstruction<0> { DECLARE_INSTRUCTION(TryBoundary); + protected: + DEFAULT_COPY_CONSTRUCTOR(TryBoundary); + private: static constexpr size_t kFieldBoundaryKind = kNumberOfGenericPackedBits; static constexpr size_t kFieldBoundaryKindSize = @@ -3020,8 +3100,6 @@ class HTryBoundary FINAL : public HTemplateInstruction<0> { static_assert(kNumberOfTryBoundaryPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using BoundaryKindField = BitField; - - DISALLOW_COPY_AND_ASSIGN(HTryBoundary); }; // Deoptimize to interpreter, upon checking a condition. @@ -3044,6 +3122,8 @@ class HDeoptimize FINAL : public HVariableInputSizeInstruction { SetRawInputAt(0, cond); } + bool IsClonable() const OVERRIDE { return true; } + // Use this constructor when the `HDeoptimize` guards an instruction, and any user // that relies on the deoptimization to pass should have its input be the `HDeoptimize` // instead of `guard`. @@ -3097,6 +3177,9 @@ class HDeoptimize FINAL : public HVariableInputSizeInstruction { DECLARE_INSTRUCTION(Deoptimize); + protected: + DEFAULT_COPY_CONSTRUCTOR(Deoptimize); + private: static constexpr size_t kFieldCanBeMoved = kNumberOfGenericPackedBits; static constexpr size_t kFieldDeoptimizeKind = kNumberOfGenericPackedBits + 1; @@ -3108,8 +3191,6 @@ class HDeoptimize FINAL : public HVariableInputSizeInstruction { "Too many packed fields."); using DeoptimizeKindField = BitField; - - DISALLOW_COPY_AND_ASSIGN(HDeoptimize); }; // Represents a should_deoptimize flag. Currently used for CHA-based devirtualization. @@ -3135,8 +3216,8 @@ class HShouldDeoptimizeFlag FINAL : public HVariableInputSizeInstruction { DECLARE_INSTRUCTION(ShouldDeoptimizeFlag); - private: - DISALLOW_COPY_AND_ASSIGN(HShouldDeoptimizeFlag); + protected: + DEFAULT_COPY_CONSTRUCTOR(ShouldDeoptimizeFlag); }; // Represents the ArtMethod that was passed as a first argument to @@ -3149,8 +3230,8 @@ class HCurrentMethod FINAL : public HExpression<0> { DECLARE_INSTRUCTION(CurrentMethod); - private: - DISALLOW_COPY_AND_ASSIGN(HCurrentMethod); + protected: + DEFAULT_COPY_CONSTRUCTOR(CurrentMethod); }; // Fetches an ArtMethod from the virtual table or the interface method table @@ -3173,6 +3254,7 @@ class HClassTableGet FINAL : public HExpression<1> { SetRawInputAt(0, cls); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { return other->AsClassTableGet()->GetIndex() == index_ && @@ -3184,6 +3266,9 @@ class HClassTableGet FINAL : public HExpression<1> { DECLARE_INSTRUCTION(ClassTableGet); + protected: + DEFAULT_COPY_CONSTRUCTOR(ClassTableGet); + private: static constexpr size_t kFieldTableKind = kNumberOfExpressionPackedBits; static constexpr size_t kFieldTableKindSize = @@ -3195,8 +3280,6 @@ class HClassTableGet FINAL : public HExpression<1> { // The index of the ArtMethod in the table. const size_t index_; - - DISALLOW_COPY_AND_ASSIGN(HClassTableGet); }; // PackedSwitch (jump table). A block ending with a PackedSwitch instruction will @@ -3214,6 +3297,8 @@ class HPackedSwitch FINAL : public HTemplateInstruction<1> { SetRawInputAt(0, input); } + bool IsClonable() const OVERRIDE { return true; } + bool IsControlFlow() const OVERRIDE { return true; } int32_t GetStartValue() const { return start_value_; } @@ -3226,11 +3311,12 @@ class HPackedSwitch FINAL : public HTemplateInstruction<1> { } DECLARE_INSTRUCTION(PackedSwitch); + protected: + DEFAULT_COPY_CONSTRUCTOR(PackedSwitch); + private: const int32_t start_value_; const uint32_t num_entries_; - - DISALLOW_COPY_AND_ASSIGN(HPackedSwitch); }; class HUnaryOperation : public HExpression<1> { @@ -3240,6 +3326,9 @@ class HUnaryOperation : public HExpression<1> { SetRawInputAt(0, input); } + // All of the UnaryOperation instructions are clonable. + bool IsClonable() const OVERRIDE { return true; } + HInstruction* GetInput() const { return InputAt(0); } DataType::Type GetResultType() const { return GetType(); } @@ -3261,8 +3350,8 @@ class HUnaryOperation : public HExpression<1> { DECLARE_ABSTRACT_INSTRUCTION(UnaryOperation); - private: - DISALLOW_COPY_AND_ASSIGN(HUnaryOperation); + protected: + DEFAULT_COPY_CONSTRUCTOR(UnaryOperation); }; class HBinaryOperation : public HExpression<2> { @@ -3277,6 +3366,9 @@ class HBinaryOperation : public HExpression<2> { SetRawInputAt(1, right); } + // All of the BinaryOperation instructions are clonable. + bool IsClonable() const OVERRIDE { return true; } + HInstruction* GetLeft() const { return InputAt(0); } HInstruction* GetRight() const { return InputAt(1); } DataType::Type GetResultType() const { return GetType(); } @@ -3351,8 +3443,8 @@ class HBinaryOperation : public HExpression<2> { DECLARE_ABSTRACT_INSTRUCTION(BinaryOperation); - private: - DISALLOW_COPY_AND_ASSIGN(HBinaryOperation); + protected: + DEFAULT_COPY_CONSTRUCTOR(BinaryOperation); }; // The comparison bias applies for floating point operations and indicates how NaN @@ -3442,8 +3534,7 @@ class HCondition : public HBinaryOperation { return GetBlock()->GetGraph()->GetIntConstant(value, dex_pc); } - private: - DISALLOW_COPY_AND_ASSIGN(HCondition); + DEFAULT_COPY_CONSTRUCTOR(Condition); }; // Instruction to check if two inputs are equal to each other. @@ -3485,10 +3576,11 @@ class HEqual FINAL : public HCondition { return kCondNE; } + protected: + DEFAULT_COPY_CONSTRUCTOR(Equal); + private: template static bool Compute(T x, T y) { return x == y; } - - DISALLOW_COPY_AND_ASSIGN(HEqual); }; class HNotEqual FINAL : public HCondition { @@ -3528,10 +3620,11 @@ class HNotEqual FINAL : public HCondition { return kCondEQ; } + protected: + DEFAULT_COPY_CONSTRUCTOR(NotEqual); + private: template static bool Compute(T x, T y) { return x != y; } - - DISALLOW_COPY_AND_ASSIGN(HNotEqual); }; class HLessThan FINAL : public HCondition { @@ -3565,10 +3658,11 @@ class HLessThan FINAL : public HCondition { return kCondGE; } + protected: + DEFAULT_COPY_CONSTRUCTOR(LessThan); + private: template static bool Compute(T x, T y) { return x < y; } - - DISALLOW_COPY_AND_ASSIGN(HLessThan); }; class HLessThanOrEqual FINAL : public HCondition { @@ -3602,10 +3696,11 @@ class HLessThanOrEqual FINAL : public HCondition { return kCondGT; } + protected: + DEFAULT_COPY_CONSTRUCTOR(LessThanOrEqual); + private: template static bool Compute(T x, T y) { return x <= y; } - - DISALLOW_COPY_AND_ASSIGN(HLessThanOrEqual); }; class HGreaterThan FINAL : public HCondition { @@ -3639,10 +3734,11 @@ class HGreaterThan FINAL : public HCondition { return kCondLE; } + protected: + DEFAULT_COPY_CONSTRUCTOR(GreaterThan); + private: template static bool Compute(T x, T y) { return x > y; } - - DISALLOW_COPY_AND_ASSIGN(HGreaterThan); }; class HGreaterThanOrEqual FINAL : public HCondition { @@ -3676,10 +3772,11 @@ class HGreaterThanOrEqual FINAL : public HCondition { return kCondLT; } + protected: + DEFAULT_COPY_CONSTRUCTOR(GreaterThanOrEqual); + private: template static bool Compute(T x, T y) { return x >= y; } - - DISALLOW_COPY_AND_ASSIGN(HGreaterThanOrEqual); }; class HBelow FINAL : public HCondition { @@ -3714,12 +3811,13 @@ class HBelow FINAL : public HCondition { return kCondAE; } + protected: + DEFAULT_COPY_CONSTRUCTOR(Below); + private: template static bool Compute(T x, T y) { return MakeUnsigned(x) < MakeUnsigned(y); } - - DISALLOW_COPY_AND_ASSIGN(HBelow); }; class HBelowOrEqual FINAL : public HCondition { @@ -3754,12 +3852,13 @@ class HBelowOrEqual FINAL : public HCondition { return kCondA; } + protected: + DEFAULT_COPY_CONSTRUCTOR(BelowOrEqual); + private: template static bool Compute(T x, T y) { return MakeUnsigned(x) <= MakeUnsigned(y); } - - DISALLOW_COPY_AND_ASSIGN(HBelowOrEqual); }; class HAbove FINAL : public HCondition { @@ -3794,12 +3893,13 @@ class HAbove FINAL : public HCondition { return kCondBE; } + protected: + DEFAULT_COPY_CONSTRUCTOR(Above); + private: template static bool Compute(T x, T y) { return MakeUnsigned(x) > MakeUnsigned(y); } - - DISALLOW_COPY_AND_ASSIGN(HAbove); }; class HAboveOrEqual FINAL : public HCondition { @@ -3834,12 +3934,13 @@ class HAboveOrEqual FINAL : public HCondition { return kCondB; } + protected: + DEFAULT_COPY_CONSTRUCTOR(AboveOrEqual); + private: template static bool Compute(T x, T y) { return MakeUnsigned(x) >= MakeUnsigned(y); } - - DISALLOW_COPY_AND_ASSIGN(HAboveOrEqual); }; // Instruction to check how two inputs compare to each other. @@ -3929,8 +4030,7 @@ class HCompare FINAL : public HBinaryOperation { return GetBlock()->GetGraph()->GetIntConstant(value, dex_pc); } - private: - DISALLOW_COPY_AND_ASSIGN(HCompare); + DEFAULT_COPY_CONSTRUCTOR(Compare); }; class HNewInstance FINAL : public HExpression<1> { @@ -3949,6 +4049,8 @@ class HNewInstance FINAL : public HExpression<1> { SetRawInputAt(0, cls); } + bool IsClonable() const OVERRIDE { return true; } + dex::TypeIndex GetTypeIndex() const { return type_index_; } const DexFile& GetDexFile() const { return dex_file_; } @@ -3985,6 +4087,9 @@ class HNewInstance FINAL : public HExpression<1> { DECLARE_INSTRUCTION(NewInstance); + protected: + DEFAULT_COPY_CONSTRUCTOR(NewInstance); + private: static constexpr size_t kFlagFinalizable = kNumberOfExpressionPackedBits; static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1; @@ -3994,8 +4099,6 @@ class HNewInstance FINAL : public HExpression<1> { const dex::TypeIndex type_index_; const DexFile& dex_file_; QuickEntrypointEnum entrypoint_; - - DISALLOW_COPY_AND_ASSIGN(HNewInstance); }; enum IntrinsicNeedsEnvironmentOrCache { @@ -4113,6 +4216,8 @@ class HInvoke : public HVariableInputSizeInstruction { SetPackedFlag(true); } + DEFAULT_COPY_CONSTRUCTOR(Invoke); + uint32_t number_of_arguments_; ArtMethod* resolved_method_; const uint32_t dex_method_index_; @@ -4120,9 +4225,6 @@ class HInvoke : public HVariableInputSizeInstruction { // A magic word holding optimizations for intrinsics. See intrinsics.h. uint32_t intrinsic_optimizations_; - - private: - DISALLOW_COPY_AND_ASSIGN(HInvoke); }; class HInvokeUnresolved FINAL : public HInvoke { @@ -4143,10 +4245,12 @@ class HInvokeUnresolved FINAL : public HInvoke { invoke_type) { } + bool IsClonable() const OVERRIDE { return true; } + DECLARE_INSTRUCTION(InvokeUnresolved); - private: - DISALLOW_COPY_AND_ASSIGN(HInvokeUnresolved); + protected: + DEFAULT_COPY_CONSTRUCTOR(InvokeUnresolved); }; class HInvokePolymorphic FINAL : public HInvoke { @@ -4165,10 +4269,12 @@ class HInvokePolymorphic FINAL : public HInvoke { nullptr, kVirtual) {} + bool IsClonable() const OVERRIDE { return true; } + DECLARE_INSTRUCTION(InvokePolymorphic); - private: - DISALLOW_COPY_AND_ASSIGN(HInvokePolymorphic); + protected: + DEFAULT_COPY_CONSTRUCTOR(InvokePolymorphic); }; class HInvokeStaticOrDirect FINAL : public HInvoke { @@ -4255,6 +4361,8 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { SetPackedField(clinit_check_requirement); } + bool IsClonable() const OVERRIDE { return true; } + void SetDispatchInfo(const DispatchInfo& dispatch_info) { bool had_current_method_input = HasCurrentMethodInput(); bool needs_current_method_input = NeedsCurrentMethodInput(dispatch_info.method_load_kind); @@ -4400,6 +4508,9 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { DECLARE_INSTRUCTION(InvokeStaticOrDirect); + protected: + DEFAULT_COPY_CONSTRUCTOR(InvokeStaticOrDirect); + private: static constexpr size_t kFieldClinitCheckRequirement = kNumberOfInvokePackedBits; static constexpr size_t kFieldClinitCheckRequirementSize = @@ -4415,8 +4526,6 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { // Cached values of the resolved method, to avoid needing the mutator lock. MethodReference target_method_; DispatchInfo dispatch_info_; - - DISALLOW_COPY_AND_ASSIGN(HInvokeStaticOrDirect); }; std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::MethodLoadKind rhs); std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::ClinitCheckRequirement rhs); @@ -4440,6 +4549,8 @@ class HInvokeVirtual FINAL : public HInvoke { kVirtual), vtable_index_(vtable_index) {} + bool IsClonable() const OVERRIDE { return true; } + bool CanBeNull() const OVERRIDE { switch (GetIntrinsic()) { case Intrinsics::kThreadCurrentThread: @@ -4462,11 +4573,12 @@ class HInvokeVirtual FINAL : public HInvoke { DECLARE_INSTRUCTION(InvokeVirtual); + protected: + DEFAULT_COPY_CONSTRUCTOR(InvokeVirtual); + private: // Cached value of the resolved method, to avoid needing the mutator lock. const uint32_t vtable_index_; - - DISALLOW_COPY_AND_ASSIGN(HInvokeVirtual); }; class HInvokeInterface FINAL : public HInvoke { @@ -4488,6 +4600,8 @@ class HInvokeInterface FINAL : public HInvoke { kInterface), imt_index_(imt_index) {} + bool IsClonable() const OVERRIDE { return true; } + bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { // TODO: Add implicit null checks in intrinsics. return (obj == InputAt(0)) && !GetLocations()->Intrinsified(); @@ -4503,11 +4617,12 @@ class HInvokeInterface FINAL : public HInvoke { DECLARE_INSTRUCTION(InvokeInterface); + protected: + DEFAULT_COPY_CONSTRUCTOR(InvokeInterface); + private: // Cached value of the resolved method, to avoid needing the mutator lock. const uint32_t imt_index_; - - DISALLOW_COPY_AND_ASSIGN(HInvokeInterface); }; class HNeg FINAL : public HUnaryOperation { @@ -4534,8 +4649,8 @@ class HNeg FINAL : public HUnaryOperation { DECLARE_INSTRUCTION(Neg); - private: - DISALLOW_COPY_AND_ASSIGN(HNeg); + protected: + DEFAULT_COPY_CONSTRUCTOR(Neg); }; class HNewArray FINAL : public HExpression<2> { @@ -4546,6 +4661,8 @@ class HNewArray FINAL : public HExpression<2> { SetRawInputAt(1, length); } + bool IsClonable() const OVERRIDE { return true; } + // Calls runtime so needs an environment. bool NeedsEnvironment() const OVERRIDE { return true; } @@ -4565,8 +4682,8 @@ class HNewArray FINAL : public HExpression<2> { DECLARE_INSTRUCTION(NewArray); - private: - DISALLOW_COPY_AND_ASSIGN(HNewArray); + protected: + DEFAULT_COPY_CONSTRUCTOR(NewArray); }; class HAdd FINAL : public HBinaryOperation { @@ -4600,8 +4717,8 @@ class HAdd FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Add); - private: - DISALLOW_COPY_AND_ASSIGN(HAdd); + protected: + DEFAULT_COPY_CONSTRUCTOR(Add); }; class HSub FINAL : public HBinaryOperation { @@ -4633,8 +4750,8 @@ class HSub FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Sub); - private: - DISALLOW_COPY_AND_ASSIGN(HSub); + protected: + DEFAULT_COPY_CONSTRUCTOR(Sub); }; class HMul FINAL : public HBinaryOperation { @@ -4668,8 +4785,8 @@ class HMul FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Mul); - private: - DISALLOW_COPY_AND_ASSIGN(HMul); + protected: + DEFAULT_COPY_CONSTRUCTOR(Mul); }; class HDiv FINAL : public HBinaryOperation { @@ -4715,8 +4832,8 @@ class HDiv FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Div); - private: - DISALLOW_COPY_AND_ASSIGN(HDiv); + protected: + DEFAULT_COPY_CONSTRUCTOR(Div); }; class HRem FINAL : public HBinaryOperation { @@ -4762,8 +4879,8 @@ class HRem FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Rem); - private: - DISALLOW_COPY_AND_ASSIGN(HRem); + protected: + DEFAULT_COPY_CONSTRUCTOR(Rem); }; class HDivZeroCheck FINAL : public HExpression<1> { @@ -4788,8 +4905,8 @@ class HDivZeroCheck FINAL : public HExpression<1> { DECLARE_INSTRUCTION(DivZeroCheck); - private: - DISALLOW_COPY_AND_ASSIGN(HDivZeroCheck); + protected: + DEFAULT_COPY_CONSTRUCTOR(DivZeroCheck); }; class HShl FINAL : public HBinaryOperation { @@ -4834,8 +4951,8 @@ class HShl FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Shl); - private: - DISALLOW_COPY_AND_ASSIGN(HShl); + protected: + DEFAULT_COPY_CONSTRUCTOR(Shl); }; class HShr FINAL : public HBinaryOperation { @@ -4880,8 +4997,8 @@ class HShr FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Shr); - private: - DISALLOW_COPY_AND_ASSIGN(HShr); + protected: + DEFAULT_COPY_CONSTRUCTOR(Shr); }; class HUShr FINAL : public HBinaryOperation { @@ -4928,8 +5045,8 @@ class HUShr FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(UShr); - private: - DISALLOW_COPY_AND_ASSIGN(HUShr); + protected: + DEFAULT_COPY_CONSTRUCTOR(UShr); }; class HAnd FINAL : public HBinaryOperation { @@ -4965,8 +5082,8 @@ class HAnd FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(And); - private: - DISALLOW_COPY_AND_ASSIGN(HAnd); + protected: + DEFAULT_COPY_CONSTRUCTOR(And); }; class HOr FINAL : public HBinaryOperation { @@ -5002,8 +5119,8 @@ class HOr FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Or); - private: - DISALLOW_COPY_AND_ASSIGN(HOr); + protected: + DEFAULT_COPY_CONSTRUCTOR(Or); }; class HXor FINAL : public HBinaryOperation { @@ -5039,8 +5156,8 @@ class HXor FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Xor); - private: - DISALLOW_COPY_AND_ASSIGN(HXor); + protected: + DEFAULT_COPY_CONSTRUCTOR(Xor); }; class HRor FINAL : public HBinaryOperation { @@ -5090,8 +5207,8 @@ class HRor FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Ror); - private: - DISALLOW_COPY_AND_ASSIGN(HRor); + protected: + DEFAULT_COPY_CONSTRUCTOR(Ror); }; // The value of a parameter in this method. Its location depends on @@ -5121,6 +5238,9 @@ class HParameterValue FINAL : public HExpression<0> { DECLARE_INSTRUCTION(ParameterValue); + protected: + DEFAULT_COPY_CONSTRUCTOR(ParameterValue); + private: // Whether or not the parameter value corresponds to 'this' argument. static constexpr size_t kFlagIsThis = kNumberOfExpressionPackedBits; @@ -5134,8 +5254,6 @@ class HParameterValue FINAL : public HExpression<0> { // The index of this parameter in the parameters list. Must be less // than HGraph::number_of_in_vregs_. const uint8_t index_; - - DISALLOW_COPY_AND_ASSIGN(HParameterValue); }; class HNot FINAL : public HUnaryOperation { @@ -5167,8 +5285,8 @@ class HNot FINAL : public HUnaryOperation { DECLARE_INSTRUCTION(Not); - private: - DISALLOW_COPY_AND_ASSIGN(HNot); + protected: + DEFAULT_COPY_CONSTRUCTOR(Not); }; class HBooleanNot FINAL : public HUnaryOperation { @@ -5204,8 +5322,8 @@ class HBooleanNot FINAL : public HUnaryOperation { DECLARE_INSTRUCTION(BooleanNot); - private: - DISALLOW_COPY_AND_ASSIGN(HBooleanNot); + protected: + DEFAULT_COPY_CONSTRUCTOR(BooleanNot); }; class HTypeConversion FINAL : public HExpression<1> { @@ -5233,8 +5351,8 @@ class HTypeConversion FINAL : public HExpression<1> { DECLARE_INSTRUCTION(TypeConversion); - private: - DISALLOW_COPY_AND_ASSIGN(HTypeConversion); + protected: + DEFAULT_COPY_CONSTRUCTOR(TypeConversion); }; static constexpr uint32_t kNoRegNumber = -1; @@ -5248,6 +5366,7 @@ class HNullCheck FINAL : public HExpression<1> { SetRawInputAt(0, value); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; @@ -5259,11 +5378,10 @@ class HNullCheck FINAL : public HExpression<1> { bool CanBeNull() const OVERRIDE { return false; } - DECLARE_INSTRUCTION(NullCheck); - private: - DISALLOW_COPY_AND_ASSIGN(HNullCheck); + protected: + DEFAULT_COPY_CONSTRUCTOR(NullCheck); }; // Embeds an ArtField and all the information required by the compiler. We cache @@ -5325,6 +5443,7 @@ class HInstanceFieldGet FINAL : public HExpression<1> { SetRawInputAt(0, value); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return !IsVolatile(); } bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { @@ -5354,10 +5473,11 @@ class HInstanceFieldGet FINAL : public HExpression<1> { DECLARE_INSTRUCTION(InstanceFieldGet); + protected: + DEFAULT_COPY_CONSTRUCTOR(InstanceFieldGet); + private: const FieldInfo field_info_; - - DISALLOW_COPY_AND_ASSIGN(HInstanceFieldGet); }; class HInstanceFieldSet FINAL : public HTemplateInstruction<2> { @@ -5385,6 +5505,8 @@ class HInstanceFieldSet FINAL : public HTemplateInstruction<2> { SetRawInputAt(1, value); } + bool IsClonable() const OVERRIDE { return true; } + bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { return (obj == InputAt(0)) && art::CanDoImplicitNullCheckOn(GetFieldOffset().Uint32Value()); } @@ -5399,6 +5521,9 @@ class HInstanceFieldSet FINAL : public HTemplateInstruction<2> { DECLARE_INSTRUCTION(InstanceFieldSet); + protected: + DEFAULT_COPY_CONSTRUCTOR(InstanceFieldSet); + private: static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits; static constexpr size_t kNumberOfInstanceFieldSetPackedBits = kFlagValueCanBeNull + 1; @@ -5406,8 +5531,6 @@ class HInstanceFieldSet FINAL : public HTemplateInstruction<2> { "Too many packed fields."); const FieldInfo field_info_; - - DISALLOW_COPY_AND_ASSIGN(HInstanceFieldSet); }; class HArrayGet FINAL : public HExpression<2> { @@ -5435,6 +5558,7 @@ class HArrayGet FINAL : public HExpression<2> { SetRawInputAt(1, index); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; @@ -5484,6 +5608,9 @@ class HArrayGet FINAL : public HExpression<2> { DECLARE_INSTRUCTION(ArrayGet); + protected: + DEFAULT_COPY_CONSTRUCTOR(ArrayGet); + private: // We treat a String as an array, creating the HArrayGet from String.charAt() // intrinsic in the instruction simplifier. We can always determine whether @@ -5494,8 +5621,6 @@ class HArrayGet FINAL : public HExpression<2> { static constexpr size_t kNumberOfArrayGetPackedBits = kFlagIsStringCharAt + 1; static_assert(kNumberOfArrayGetPackedBits <= HInstruction::kMaxNumberOfPackedBits, "Too many packed fields."); - - DISALLOW_COPY_AND_ASSIGN(HArrayGet); }; class HArraySet FINAL : public HTemplateInstruction<3> { @@ -5529,6 +5654,8 @@ class HArraySet FINAL : public HTemplateInstruction<3> { SetRawInputAt(2, value); } + bool IsClonable() const OVERRIDE { return true; } + bool NeedsEnvironment() const OVERRIDE { // We call a runtime method to throw ArrayStoreException. return NeedsTypeCheck(); @@ -5594,6 +5721,9 @@ class HArraySet FINAL : public HTemplateInstruction<3> { DECLARE_INSTRUCTION(ArraySet); + protected: + DEFAULT_COPY_CONSTRUCTOR(ArraySet); + private: static constexpr size_t kFieldExpectedComponentType = kNumberOfGenericPackedBits; static constexpr size_t kFieldExpectedComponentTypeSize = @@ -5609,8 +5739,6 @@ class HArraySet FINAL : public HTemplateInstruction<3> { static_assert(kNumberOfArraySetPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using ExpectedComponentTypeField = BitField; - - DISALLOW_COPY_AND_ASSIGN(HArraySet); }; class HArrayLength FINAL : public HExpression<1> { @@ -5623,6 +5751,7 @@ class HArrayLength FINAL : public HExpression<1> { SetRawInputAt(0, array); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; @@ -5635,6 +5764,9 @@ class HArrayLength FINAL : public HExpression<1> { DECLARE_INSTRUCTION(ArrayLength); + protected: + DEFAULT_COPY_CONSTRUCTOR(ArrayLength); + private: // We treat a String as an array, creating the HArrayLength from String.length() // or String.isEmpty() intrinsic in the instruction simplifier. We can always @@ -5645,8 +5777,6 @@ class HArrayLength FINAL : public HExpression<1> { static constexpr size_t kNumberOfArrayLengthPackedBits = kFlagIsStringLength + 1; static_assert(kNumberOfArrayLengthPackedBits <= HInstruction::kMaxNumberOfPackedBits, "Too many packed fields."); - - DISALLOW_COPY_AND_ASSIGN(HArrayLength); }; class HBoundsCheck FINAL : public HExpression<2> { @@ -5664,6 +5794,7 @@ class HBoundsCheck FINAL : public HExpression<2> { SetRawInputAt(1, length); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; @@ -5679,10 +5810,11 @@ class HBoundsCheck FINAL : public HExpression<2> { DECLARE_INSTRUCTION(BoundsCheck); + protected: + DEFAULT_COPY_CONSTRUCTOR(BoundsCheck); + private: static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits; - - DISALLOW_COPY_AND_ASSIGN(HBoundsCheck); }; class HSuspendCheck FINAL : public HTemplateInstruction<0> { @@ -5690,6 +5822,8 @@ class HSuspendCheck FINAL : public HTemplateInstruction<0> { explicit HSuspendCheck(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc), slow_path_(nullptr) {} + bool IsClonable() const OVERRIDE { return true; } + bool NeedsEnvironment() const OVERRIDE { return true; } @@ -5699,12 +5833,13 @@ class HSuspendCheck FINAL : public HTemplateInstruction<0> { DECLARE_INSTRUCTION(SuspendCheck); + protected: + DEFAULT_COPY_CONSTRUCTOR(SuspendCheck); + private: // Only used for code generation, in order to share the same slow path between back edges // of a same loop. SlowPathCode* slow_path_; - - DISALLOW_COPY_AND_ASSIGN(HSuspendCheck); }; // Pseudo-instruction which provides the native debugger with mapping information. @@ -5720,8 +5855,8 @@ class HNativeDebugInfo : public HTemplateInstruction<0> { DECLARE_INSTRUCTION(NativeDebugInfo); - private: - DISALLOW_COPY_AND_ASSIGN(HNativeDebugInfo); + protected: + DEFAULT_COPY_CONSTRUCTOR(NativeDebugInfo); }; /** @@ -5787,6 +5922,8 @@ class HLoadClass FINAL : public HInstruction { SetPackedFlag(false); } + bool IsClonable() const OVERRIDE { return true; } + void SetLoadKind(LoadKind load_kind); LoadKind GetLoadKind() const { @@ -5878,6 +6015,9 @@ class HLoadClass FINAL : public HInstruction { DECLARE_INSTRUCTION(LoadClass); + protected: + DEFAULT_COPY_CONSTRUCTOR(LoadClass); + private: static constexpr size_t kFlagNeedsAccessCheck = kNumberOfGenericPackedBits; static constexpr size_t kFlagIsInBootImage = kFlagNeedsAccessCheck + 1; @@ -5917,8 +6057,6 @@ class HLoadClass FINAL : public HInstruction { Handle klass_; ReferenceTypeInfo loaded_class_rti_; - - DISALLOW_COPY_AND_ASSIGN(HLoadClass); }; std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs); @@ -5976,6 +6114,8 @@ class HLoadString FINAL : public HInstruction { SetPackedField(LoadKind::kRuntimeCall); } + bool IsClonable() const OVERRIDE { return true; } + void SetLoadKind(LoadKind load_kind); LoadKind GetLoadKind() const { @@ -6042,6 +6182,9 @@ class HLoadString FINAL : public HInstruction { DECLARE_INSTRUCTION(LoadString); + protected: + DEFAULT_COPY_CONSTRUCTOR(LoadString); + private: static constexpr size_t kFieldLoadKind = kNumberOfGenericPackedBits; static constexpr size_t kFieldLoadKindSize = @@ -6061,8 +6204,6 @@ class HLoadString FINAL : public HInstruction { const DexFile& dex_file_; Handle string_; - - DISALLOW_COPY_AND_ASSIGN(HLoadString); }; std::ostream& operator<<(std::ostream& os, HLoadString::LoadKind rhs); @@ -6094,6 +6235,7 @@ class HClinitCheck FINAL : public HExpression<1> { SetRawInputAt(0, constant); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; @@ -6113,8 +6255,9 @@ class HClinitCheck FINAL : public HExpression<1> { DECLARE_INSTRUCTION(ClinitCheck); - private: - DISALLOW_COPY_AND_ASSIGN(HClinitCheck); + + protected: + DEFAULT_COPY_CONSTRUCTOR(ClinitCheck); }; class HStaticFieldGet FINAL : public HExpression<1> { @@ -6140,6 +6283,7 @@ class HStaticFieldGet FINAL : public HExpression<1> { } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return !IsVolatile(); } bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { @@ -6165,10 +6309,11 @@ class HStaticFieldGet FINAL : public HExpression<1> { DECLARE_INSTRUCTION(StaticFieldGet); + protected: + DEFAULT_COPY_CONSTRUCTOR(StaticFieldGet); + private: const FieldInfo field_info_; - - DISALLOW_COPY_AND_ASSIGN(HStaticFieldGet); }; class HStaticFieldSet FINAL : public HTemplateInstruction<2> { @@ -6196,6 +6341,7 @@ class HStaticFieldSet FINAL : public HTemplateInstruction<2> { SetRawInputAt(1, value); } + bool IsClonable() const OVERRIDE { return true; } const FieldInfo& GetFieldInfo() const { return field_info_; } MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); } DataType::Type GetFieldType() const { return field_info_.GetFieldType(); } @@ -6207,6 +6353,9 @@ class HStaticFieldSet FINAL : public HTemplateInstruction<2> { DECLARE_INSTRUCTION(StaticFieldSet); + protected: + DEFAULT_COPY_CONSTRUCTOR(StaticFieldSet); + private: static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits; static constexpr size_t kNumberOfStaticFieldSetPackedBits = kFlagValueCanBeNull + 1; @@ -6214,8 +6363,6 @@ class HStaticFieldSet FINAL : public HTemplateInstruction<2> { "Too many packed fields."); const FieldInfo field_info_; - - DISALLOW_COPY_AND_ASSIGN(HStaticFieldSet); }; class HUnresolvedInstanceFieldGet FINAL : public HExpression<1> { @@ -6229,6 +6376,7 @@ class HUnresolvedInstanceFieldGet FINAL : public HExpression<1> { SetRawInputAt(0, obj); } + bool IsClonable() const OVERRIDE { return true; } bool NeedsEnvironment() const OVERRIDE { return true; } bool CanThrow() const OVERRIDE { return true; } @@ -6237,10 +6385,11 @@ class HUnresolvedInstanceFieldGet FINAL : public HExpression<1> { DECLARE_INSTRUCTION(UnresolvedInstanceFieldGet); + protected: + DEFAULT_COPY_CONSTRUCTOR(UnresolvedInstanceFieldGet); + private: const uint32_t field_index_; - - DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldGet); }; class HUnresolvedInstanceFieldSet FINAL : public HTemplateInstruction<2> { @@ -6258,6 +6407,7 @@ class HUnresolvedInstanceFieldSet FINAL : public HTemplateInstruction<2> { SetRawInputAt(1, value); } + bool IsClonable() const OVERRIDE { return true; } bool NeedsEnvironment() const OVERRIDE { return true; } bool CanThrow() const OVERRIDE { return true; } @@ -6266,6 +6416,9 @@ class HUnresolvedInstanceFieldSet FINAL : public HTemplateInstruction<2> { DECLARE_INSTRUCTION(UnresolvedInstanceFieldSet); + protected: + DEFAULT_COPY_CONSTRUCTOR(UnresolvedInstanceFieldSet); + private: static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits; static constexpr size_t kFieldFieldTypeSize = @@ -6277,8 +6430,6 @@ class HUnresolvedInstanceFieldSet FINAL : public HTemplateInstruction<2> { using FieldTypeField = BitField; const uint32_t field_index_; - - DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldSet); }; class HUnresolvedStaticFieldGet FINAL : public HExpression<0> { @@ -6290,6 +6441,7 @@ class HUnresolvedStaticFieldGet FINAL : public HExpression<0> { field_index_(field_index) { } + bool IsClonable() const OVERRIDE { return true; } bool NeedsEnvironment() const OVERRIDE { return true; } bool CanThrow() const OVERRIDE { return true; } @@ -6298,10 +6450,11 @@ class HUnresolvedStaticFieldGet FINAL : public HExpression<0> { DECLARE_INSTRUCTION(UnresolvedStaticFieldGet); + protected: + DEFAULT_COPY_CONSTRUCTOR(UnresolvedStaticFieldGet); + private: const uint32_t field_index_; - - DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldGet); }; class HUnresolvedStaticFieldSet FINAL : public HTemplateInstruction<1> { @@ -6317,6 +6470,7 @@ class HUnresolvedStaticFieldSet FINAL : public HTemplateInstruction<1> { SetRawInputAt(0, value); } + bool IsClonable() const OVERRIDE { return true; } bool NeedsEnvironment() const OVERRIDE { return true; } bool CanThrow() const OVERRIDE { return true; } @@ -6325,6 +6479,9 @@ class HUnresolvedStaticFieldSet FINAL : public HTemplateInstruction<1> { DECLARE_INSTRUCTION(UnresolvedStaticFieldSet); + protected: + DEFAULT_COPY_CONSTRUCTOR(UnresolvedStaticFieldSet); + private: static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits; static constexpr size_t kFieldFieldTypeSize = @@ -6336,8 +6493,6 @@ class HUnresolvedStaticFieldSet FINAL : public HTemplateInstruction<1> { using FieldTypeField = BitField; const uint32_t field_index_; - - DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldSet); }; // Implement the move-exception DEX instruction. @@ -6350,8 +6505,8 @@ class HLoadException FINAL : public HExpression<0> { DECLARE_INSTRUCTION(LoadException); - private: - DISALLOW_COPY_AND_ASSIGN(HLoadException); + protected: + DEFAULT_COPY_CONSTRUCTOR(LoadException); }; // Implicit part of move-exception which clears thread-local exception storage. @@ -6363,8 +6518,8 @@ class HClearException FINAL : public HTemplateInstruction<0> { DECLARE_INSTRUCTION(ClearException); - private: - DISALLOW_COPY_AND_ASSIGN(HClearException); + protected: + DEFAULT_COPY_CONSTRUCTOR(ClearException); }; class HThrow FINAL : public HTemplateInstruction<1> { @@ -6380,11 +6535,10 @@ class HThrow FINAL : public HTemplateInstruction<1> { bool CanThrow() const OVERRIDE { return true; } - DECLARE_INSTRUCTION(Throw); - private: - DISALLOW_COPY_AND_ASSIGN(HThrow); + protected: + DEFAULT_COPY_CONSTRUCTOR(Throw); }; /** @@ -6419,6 +6573,7 @@ class HInstanceOf FINAL : public HExpression<2> { SetRawInputAt(1, constant); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { @@ -6446,6 +6601,9 @@ class HInstanceOf FINAL : public HExpression<2> { DECLARE_INSTRUCTION(InstanceOf); + protected: + DEFAULT_COPY_CONSTRUCTOR(InstanceOf); + private: static constexpr size_t kFieldTypeCheckKind = kNumberOfExpressionPackedBits; static constexpr size_t kFieldTypeCheckKindSize = @@ -6454,8 +6612,6 @@ class HInstanceOf FINAL : public HExpression<2> { static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagMustDoNullCheck + 1; static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using TypeCheckKindField = BitField; - - DISALLOW_COPY_AND_ASSIGN(HInstanceOf); }; class HBoundType FINAL : public HExpression<1> { @@ -6469,6 +6625,8 @@ class HBoundType FINAL : public HExpression<1> { SetRawInputAt(0, input); } + bool IsClonable() const OVERRIDE { return true; } + // {Get,Set}Upper* should only be used in reference type propagation. const ReferenceTypeInfo& GetUpperBound() const { return upper_bound_; } bool GetUpperCanBeNull() const { return GetPackedFlag(); } @@ -6483,6 +6641,9 @@ class HBoundType FINAL : public HExpression<1> { DECLARE_INSTRUCTION(BoundType); + protected: + DEFAULT_COPY_CONSTRUCTOR(BoundType); + private: // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this // is false then CanBeNull() cannot be true). @@ -6498,8 +6659,6 @@ class HBoundType FINAL : public HExpression<1> { // // uper_bound_ will be ClassX // } ReferenceTypeInfo upper_bound_; - - DISALLOW_COPY_AND_ASSIGN(HBoundType); }; class HCheckCast FINAL : public HTemplateInstruction<2> { @@ -6515,6 +6674,7 @@ class HCheckCast FINAL : public HTemplateInstruction<2> { SetRawInputAt(1, constant); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { @@ -6535,6 +6695,9 @@ class HCheckCast FINAL : public HTemplateInstruction<2> { DECLARE_INSTRUCTION(CheckCast); + protected: + DEFAULT_COPY_CONSTRUCTOR(CheckCast); + private: static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits; static constexpr size_t kFieldTypeCheckKindSize = @@ -6543,8 +6706,6 @@ class HCheckCast FINAL : public HTemplateInstruction<2> { static constexpr size_t kNumberOfCheckCastPackedBits = kFlagMustDoNullCheck + 1; static_assert(kNumberOfCheckCastPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using TypeCheckKindField = BitField; - - DISALLOW_COPY_AND_ASSIGN(HCheckCast); }; /** @@ -6581,10 +6742,15 @@ class HMemoryBarrier FINAL : public HTemplateInstruction<0> { SetPackedField(barrier_kind); } + bool IsClonable() const OVERRIDE { return true; } + MemBarrierKind GetBarrierKind() { return GetPackedField(); } DECLARE_INSTRUCTION(MemoryBarrier); + protected: + DEFAULT_COPY_CONSTRUCTOR(MemoryBarrier); + private: static constexpr size_t kFieldBarrierKind = HInstruction::kNumberOfGenericPackedBits; static constexpr size_t kFieldBarrierKindSize = @@ -6594,8 +6760,6 @@ class HMemoryBarrier FINAL : public HTemplateInstruction<0> { static_assert(kNumberOfMemoryBarrierPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using BarrierKindField = BitField; - - DISALLOW_COPY_AND_ASSIGN(HMemoryBarrier); }; // A constructor fence orders all prior stores to fields that could be accessed via a final field of @@ -6746,8 +6910,8 @@ class HConstructorFence FINAL : public HVariableInputSizeInstruction { DECLARE_INSTRUCTION(ConstructorFence); - private: - DISALLOW_COPY_AND_ASSIGN(HConstructorFence); + protected: + DEFAULT_COPY_CONSTRUCTOR(ConstructorFence); }; class HMonitorOperation FINAL : public HTemplateInstruction<1> { @@ -6781,6 +6945,9 @@ class HMonitorOperation FINAL : public HTemplateInstruction<1> { DECLARE_INSTRUCTION(MonitorOperation); + protected: + DEFAULT_COPY_CONSTRUCTOR(MonitorOperation); + private: static constexpr size_t kFieldOperationKind = HInstruction::kNumberOfGenericPackedBits; static constexpr size_t kFieldOperationKindSize = @@ -6790,9 +6957,6 @@ class HMonitorOperation FINAL : public HTemplateInstruction<1> { static_assert(kNumberOfMonitorOperationPackedBits <= HInstruction::kMaxNumberOfPackedBits, "Too many packed fields."); using OperationKindField = BitField; - - private: - DISALLOW_COPY_AND_ASSIGN(HMonitorOperation); }; class HSelect FINAL : public HExpression<3> { @@ -6813,6 +6977,7 @@ class HSelect FINAL : public HExpression<3> { SetRawInputAt(2, condition); } + bool IsClonable() const OVERRIDE { return true; } HInstruction* GetFalseValue() const { return InputAt(0); } HInstruction* GetTrueValue() const { return InputAt(1); } HInstruction* GetCondition() const { return InputAt(2); } @@ -6828,8 +6993,8 @@ class HSelect FINAL : public HExpression<3> { DECLARE_INSTRUCTION(Select); - private: - DISALLOW_COPY_AND_ASSIGN(HSelect); + protected: + DEFAULT_COPY_CONSTRUCTOR(Select); }; class MoveOperands : public ArenaObject { @@ -6960,10 +7125,11 @@ class HParallelMove FINAL : public HTemplateInstruction<0> { DECLARE_INSTRUCTION(ParallelMove); + protected: + DEFAULT_COPY_CONSTRUCTOR(ParallelMove); + private: ArenaVector moves_; - - DISALLOW_COPY_AND_ASSIGN(HParallelMove); }; // This instruction computes an intermediate address pointing in the 'middle' of an object. The @@ -6982,6 +7148,7 @@ class HIntermediateAddress FINAL : public HExpression<2> { SetRawInputAt(1, offset); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; @@ -6993,8 +7160,8 @@ class HIntermediateAddress FINAL : public HExpression<2> { DECLARE_INSTRUCTION(IntermediateAddress); - private: - DISALLOW_COPY_AND_ASSIGN(HIntermediateAddress); + protected: + DEFAULT_COPY_CONSTRUCTOR(IntermediateAddress); }; @@ -7069,6 +7236,33 @@ class HGraphDelegateVisitor : public HGraphVisitor { DISALLOW_COPY_AND_ASSIGN(HGraphDelegateVisitor); }; +// Create a clone of the instruction, insert it into the graph; replace the old one with a new +// and remove the old instruction. +HInstruction* ReplaceInstrOrPhiByClone(HInstruction* instr); + +// Create a clone for each clonable instructions/phis and replace the original with the clone. +// +// Used for testing individual instruction cloner. +class CloneAndReplaceInstructionVisitor : public HGraphDelegateVisitor { + public: + explicit CloneAndReplaceInstructionVisitor(HGraph* graph) + : HGraphDelegateVisitor(graph), instr_replaced_by_clones_count(0) {} + + void VisitInstruction(HInstruction* instruction) OVERRIDE { + if (instruction->IsClonable()) { + ReplaceInstrOrPhiByClone(instruction); + instr_replaced_by_clones_count++; + } + } + + size_t GetInstrReplacedByClonesCount() const { return instr_replaced_by_clones_count; } + + private: + size_t instr_replaced_by_clones_count; + + DISALLOW_COPY_AND_ASSIGN(CloneAndReplaceInstructionVisitor); +}; + // Iterator over the blocks that art part of the loop. Includes blocks part // of an inner loop. The order in which the blocks are iterated is on their // block id. diff --git a/compiler/optimizing/nodes_mips.h b/compiler/optimizing/nodes_mips.h index ef388c30d5..2c0595e3d8 100644 --- a/compiler/optimizing/nodes_mips.h +++ b/compiler/optimizing/nodes_mips.h @@ -30,8 +30,8 @@ class HMipsComputeBaseMethodAddress : public HExpression<0> { DECLARE_INSTRUCTION(MipsComputeBaseMethodAddress); - private: - DISALLOW_COPY_AND_ASSIGN(HMipsComputeBaseMethodAddress); + protected: + DEFAULT_COPY_CONSTRUCTOR(MipsComputeBaseMethodAddress); }; // Mips version of HPackedSwitch that holds a pointer to the base method address. @@ -62,11 +62,12 @@ class HMipsPackedSwitch FINAL : public HTemplateInstruction<2> { DECLARE_INSTRUCTION(MipsPackedSwitch); + protected: + DEFAULT_COPY_CONSTRUCTOR(MipsPackedSwitch); + private: const int32_t start_value_; const int32_t num_entries_; - - DISALLOW_COPY_AND_ASSIGN(HMipsPackedSwitch); }; // This instruction computes part of the array access offset (index offset). @@ -105,8 +106,8 @@ class HIntermediateArrayAddressIndex FINAL : public HExpression<2> { DECLARE_INSTRUCTION(IntermediateArrayAddressIndex); - private: - DISALLOW_COPY_AND_ASSIGN(HIntermediateArrayAddressIndex); + protected: + DEFAULT_COPY_CONSTRUCTOR(IntermediateArrayAddressIndex); }; } // namespace art diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h index 7b4f5f7cbb..e837f1e7e0 100644 --- a/compiler/optimizing/nodes_shared.h +++ b/compiler/optimizing/nodes_shared.h @@ -38,6 +38,8 @@ class HMultiplyAccumulate FINAL : public HExpression<3> { SetRawInputAt(kInputMulRightIndex, mul_right); } + bool IsClonable() const OVERRIDE { return true; } + static constexpr int kInputAccumulatorIndex = 0; static constexpr int kInputMulLeftIndex = 1; static constexpr int kInputMulRightIndex = 2; @@ -51,11 +53,12 @@ class HMultiplyAccumulate FINAL : public HExpression<3> { DECLARE_INSTRUCTION(MultiplyAccumulate); + protected: + DEFAULT_COPY_CONSTRUCTOR(MultiplyAccumulate); + private: // Indicates if this is a MADD or MSUB. const InstructionKind op_kind_; - - DISALLOW_COPY_AND_ASSIGN(HMultiplyAccumulate); }; class HBitwiseNegatedRight FINAL : public HBinaryOperation { @@ -111,11 +114,12 @@ class HBitwiseNegatedRight FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(BitwiseNegatedRight); + protected: + DEFAULT_COPY_CONSTRUCTOR(BitwiseNegatedRight); + private: // Specifies the bitwise operation, which will be then negated. const InstructionKind op_kind_; - - DISALLOW_COPY_AND_ASSIGN(HBitwiseNegatedRight); }; // This instruction computes part of the array access offset (data and index offset). @@ -145,6 +149,7 @@ class HIntermediateAddressIndex FINAL : public HExpression<3> { SetRawInputAt(2, shift); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; @@ -157,8 +162,8 @@ class HIntermediateAddressIndex FINAL : public HExpression<3> { DECLARE_INSTRUCTION(IntermediateAddressIndex); - private: - DISALLOW_COPY_AND_ASSIGN(HIntermediateAddressIndex); + protected: + DEFAULT_COPY_CONSTRUCTOR(IntermediateAddressIndex); }; class HDataProcWithShifterOp FINAL : public HExpression<2> { @@ -198,6 +203,7 @@ class HDataProcWithShifterOp FINAL : public HExpression<2> { SetRawInputAt(1, right); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other_instr) const OVERRIDE { const HDataProcWithShifterOp* other = other_instr->AsDataProcWithShifterOp(); @@ -225,14 +231,15 @@ class HDataProcWithShifterOp FINAL : public HExpression<2> { DECLARE_INSTRUCTION(DataProcWithShifterOp); + protected: + DEFAULT_COPY_CONSTRUCTOR(DataProcWithShifterOp); + private: InstructionKind instr_kind_; OpKind op_kind_; int shift_amount_; friend std::ostream& operator<<(std::ostream& os, OpKind op); - - DISALLOW_COPY_AND_ASSIGN(HDataProcWithShifterOp); }; std::ostream& operator<<(std::ostream& os, const HDataProcWithShifterOp::OpKind op); diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h index 17540b9770..59d5b9f847 100644 --- a/compiler/optimizing/nodes_vector.h +++ b/compiler/optimizing/nodes_vector.h @@ -161,10 +161,10 @@ class HVecOperation : public HVariableInputSizeInstruction { static_assert(kNumberOfVectorOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using TypeField = BitField; + DEFAULT_COPY_CONSTRUCTOR(VecOperation); + private: const size_t vector_length_; - - DISALLOW_COPY_AND_ASSIGN(HVecOperation); }; // Abstraction of a unary vector operation. @@ -188,8 +188,8 @@ class HVecUnaryOperation : public HVecOperation { DECLARE_ABSTRACT_INSTRUCTION(VecUnaryOperation); - private: - DISALLOW_COPY_AND_ASSIGN(HVecUnaryOperation); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecUnaryOperation); }; // Abstraction of a binary vector operation. @@ -216,8 +216,8 @@ class HVecBinaryOperation : public HVecOperation { DECLARE_ABSTRACT_INSTRUCTION(VecBinaryOperation); - private: - DISALLOW_COPY_AND_ASSIGN(HVecBinaryOperation); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecBinaryOperation); }; // Abstraction of a vector operation that references memory, with an alignment. @@ -255,10 +255,11 @@ class HVecMemoryOperation : public HVecOperation { DECLARE_ABSTRACT_INSTRUCTION(VecMemoryOperation); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecMemoryOperation); + private: Alignment alignment_; - - DISALLOW_COPY_AND_ASSIGN(HVecMemoryOperation); }; // Packed type consistency checker ("same vector length" integral types may mix freely). @@ -296,8 +297,8 @@ class HVecReplicateScalar FINAL : public HVecUnaryOperation { DECLARE_INSTRUCTION(VecReplicateScalar); - private: - DISALLOW_COPY_AND_ASSIGN(HVecReplicateScalar); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecReplicateScalar); }; // Extracts a particular scalar from the given vector, @@ -329,8 +330,8 @@ class HVecExtractScalar FINAL : public HVecUnaryOperation { DECLARE_INSTRUCTION(VecExtractScalar); - private: - DISALLOW_COPY_AND_ASSIGN(HVecExtractScalar); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecExtractScalar); }; // Reduces the given vector into the first element as sum/min/max, @@ -367,10 +368,11 @@ class HVecReduce FINAL : public HVecUnaryOperation { DECLARE_INSTRUCTION(VecReduce); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecReduce); + private: const ReductionKind kind_; - - DISALLOW_COPY_AND_ASSIGN(HVecReduce); }; // Converts every component in the vector, @@ -394,8 +396,8 @@ class HVecCnv FINAL : public HVecUnaryOperation { DECLARE_INSTRUCTION(VecCnv); - private: - DISALLOW_COPY_AND_ASSIGN(HVecCnv); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecCnv); }; // Negates every component in the vector, @@ -415,8 +417,8 @@ class HVecNeg FINAL : public HVecUnaryOperation { DECLARE_INSTRUCTION(VecNeg); - private: - DISALLOW_COPY_AND_ASSIGN(HVecNeg); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecNeg); }; // Takes absolute value of every component in the vector, @@ -437,8 +439,8 @@ class HVecAbs FINAL : public HVecUnaryOperation { DECLARE_INSTRUCTION(VecAbs); - private: - DISALLOW_COPY_AND_ASSIGN(HVecAbs); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecAbs); }; // Bitwise- or boolean-nots every component in the vector, @@ -459,8 +461,8 @@ class HVecNot FINAL : public HVecUnaryOperation { DECLARE_INSTRUCTION(VecNot); - private: - DISALLOW_COPY_AND_ASSIGN(HVecNot); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecNot); }; // @@ -486,8 +488,8 @@ class HVecAdd FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecAdd); - private: - DISALLOW_COPY_AND_ASSIGN(HVecAdd); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecAdd); }; // Performs halving add on every component in the two vectors, viz. @@ -531,14 +533,15 @@ class HVecHalvingAdd FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecHalvingAdd); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecHalvingAdd); + private: // Additional packed bits. static constexpr size_t kFieldHAddIsUnsigned = HVecOperation::kNumberOfVectorOpPackedBits; static constexpr size_t kFieldHAddIsRounded = kFieldHAddIsUnsigned + 1; static constexpr size_t kNumberOfHAddPackedBits = kFieldHAddIsRounded + 1; static_assert(kNumberOfHAddPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - - DISALLOW_COPY_AND_ASSIGN(HVecHalvingAdd); }; // Subtracts every component in the two vectors, @@ -560,8 +563,8 @@ class HVecSub FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecSub); - private: - DISALLOW_COPY_AND_ASSIGN(HVecSub); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecSub); }; // Multiplies every component in the two vectors, @@ -583,8 +586,8 @@ class HVecMul FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecMul); - private: - DISALLOW_COPY_AND_ASSIGN(HVecMul); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecMul); }; // Divides every component in the two vectors, @@ -606,8 +609,8 @@ class HVecDiv FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecDiv); - private: - DISALLOW_COPY_AND_ASSIGN(HVecDiv); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecDiv); }; // Takes minimum of every component in the two vectors, @@ -645,13 +648,14 @@ class HVecMin FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecMin); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecMin); + private: // Additional packed bits. static constexpr size_t kFieldMinOpIsUnsigned = HVecOperation::kNumberOfVectorOpPackedBits; static constexpr size_t kNumberOfMinOpPackedBits = kFieldMinOpIsUnsigned + 1; static_assert(kNumberOfMinOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - - DISALLOW_COPY_AND_ASSIGN(HVecMin); }; // Takes maximum of every component in the two vectors, @@ -689,13 +693,14 @@ class HVecMax FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecMax); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecMax); + private: // Additional packed bits. static constexpr size_t kFieldMaxOpIsUnsigned = HVecOperation::kNumberOfVectorOpPackedBits; static constexpr size_t kNumberOfMaxOpPackedBits = kFieldMaxOpIsUnsigned + 1; static_assert(kNumberOfMaxOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - - DISALLOW_COPY_AND_ASSIGN(HVecMax); }; // Bitwise-ands every component in the two vectors, @@ -716,8 +721,8 @@ class HVecAnd FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecAnd); - private: - DISALLOW_COPY_AND_ASSIGN(HVecAnd); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecAnd); }; // Bitwise-and-nots every component in the two vectors, @@ -738,8 +743,8 @@ class HVecAndNot FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecAndNot); - private: - DISALLOW_COPY_AND_ASSIGN(HVecAndNot); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecAndNot); }; // Bitwise-ors every component in the two vectors, @@ -760,8 +765,8 @@ class HVecOr FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecOr); - private: - DISALLOW_COPY_AND_ASSIGN(HVecOr); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecOr); }; // Bitwise-xors every component in the two vectors, @@ -782,8 +787,8 @@ class HVecXor FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecXor); - private: - DISALLOW_COPY_AND_ASSIGN(HVecXor); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecXor); }; // Logically shifts every component in the vector left by the given distance, @@ -804,8 +809,8 @@ class HVecShl FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecShl); - private: - DISALLOW_COPY_AND_ASSIGN(HVecShl); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecShl); }; // Arithmetically shifts every component in the vector right by the given distance, @@ -826,8 +831,8 @@ class HVecShr FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecShr); - private: - DISALLOW_COPY_AND_ASSIGN(HVecShr); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecShr); }; // Logically shifts every component in the vector right by the given distance, @@ -848,8 +853,8 @@ class HVecUShr FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecUShr); - private: - DISALLOW_COPY_AND_ASSIGN(HVecUShr); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecUShr); }; // @@ -885,8 +890,8 @@ class HVecSetScalars FINAL : public HVecOperation { DECLARE_INSTRUCTION(VecSetScalars); - private: - DISALLOW_COPY_AND_ASSIGN(HVecSetScalars); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecSetScalars); }; // Multiplies every component in the two vectors, adds the result vector to the accumulator vector, @@ -929,11 +934,12 @@ class HVecMultiplyAccumulate FINAL : public HVecOperation { DECLARE_INSTRUCTION(VecMultiplyAccumulate); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecMultiplyAccumulate); + private: // Indicates if this is a MADD or MSUB. const InstructionKind op_kind_; - - DISALLOW_COPY_AND_ASSIGN(HVecMultiplyAccumulate); }; // Takes the absolute difference of two vectors, and adds the results to @@ -968,8 +974,8 @@ class HVecSADAccumulate FINAL : public HVecOperation { DECLARE_INSTRUCTION(VecSADAccumulate); - private: - DISALLOW_COPY_AND_ASSIGN(HVecSADAccumulate); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecSADAccumulate); }; // Loads a vector from memory, viz. load(mem, 1) @@ -1007,13 +1013,14 @@ class HVecLoad FINAL : public HVecMemoryOperation { DECLARE_INSTRUCTION(VecLoad); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecLoad); + private: // Additional packed bits. static constexpr size_t kFieldIsStringCharAt = HVecOperation::kNumberOfVectorOpPackedBits; static constexpr size_t kNumberOfVecLoadPackedBits = kFieldIsStringCharAt + 1; static_assert(kNumberOfVecLoadPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - - DISALLOW_COPY_AND_ASSIGN(HVecLoad); }; // Stores a vector to memory, viz. store(m, 1, [x1, .. , xn] ) @@ -1045,8 +1052,8 @@ class HVecStore FINAL : public HVecMemoryOperation { DECLARE_INSTRUCTION(VecStore); - private: - DISALLOW_COPY_AND_ASSIGN(HVecStore); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecStore) }; } // namespace art diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h index 22e92eab31..6326065fe2 100644 --- a/compiler/optimizing/nodes_x86.h +++ b/compiler/optimizing/nodes_x86.h @@ -30,8 +30,8 @@ class HX86ComputeBaseMethodAddress FINAL : public HExpression<0> { DECLARE_INSTRUCTION(X86ComputeBaseMethodAddress); - private: - DISALLOW_COPY_AND_ASSIGN(HX86ComputeBaseMethodAddress); + protected: + DEFAULT_COPY_CONSTRUCTOR(X86ComputeBaseMethodAddress); }; // Load a constant value from the constant table. @@ -54,8 +54,8 @@ class HX86LoadFromConstantTable FINAL : public HExpression<2> { DECLARE_INSTRUCTION(X86LoadFromConstantTable); - private: - DISALLOW_COPY_AND_ASSIGN(HX86LoadFromConstantTable); + protected: + DEFAULT_COPY_CONSTRUCTOR(X86LoadFromConstantTable); }; // Version of HNeg with access to the constant table for FP types. @@ -77,8 +77,8 @@ class HX86FPNeg FINAL : public HExpression<2> { DECLARE_INSTRUCTION(X86FPNeg); - private: - DISALLOW_COPY_AND_ASSIGN(HX86FPNeg); + protected: + DEFAULT_COPY_CONSTRUCTOR(X86FPNeg); }; // X86 version of HPackedSwitch that holds a pointer to the base method address. @@ -113,11 +113,12 @@ class HX86PackedSwitch FINAL : public HTemplateInstruction<2> { DECLARE_INSTRUCTION(X86PackedSwitch); + protected: + DEFAULT_COPY_CONSTRUCTOR(X86PackedSwitch); + private: const int32_t start_value_; const int32_t num_entries_; - - DISALLOW_COPY_AND_ASSIGN(HX86PackedSwitch); }; } // namespace art -- GitLab From f8efae7fa4d1441cd639d81d3354711f157569e3 Mon Sep 17 00:00:00 2001 From: Alan Leung Date: Wed, 1 Nov 2017 15:00:36 -0700 Subject: [PATCH 002/226] Add D8 reference file for 913-heaps Bug: 68764399 Test: ./art/test/run-test ... --build-with-javac-dx --build-with-d8 913-heaps Change-Id: I9be46703510a838ab78ca3c5d38e27996065c402 --- test/913-heaps/build | 20 --------- test/913-heaps/check | 5 ++- test/913-heaps/expected_d8.diff | 76 +++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 21 deletions(-) delete mode 100644 test/913-heaps/build create mode 100644 test/913-heaps/expected_d8.diff diff --git a/test/913-heaps/build b/test/913-heaps/build deleted file mode 100644 index 10ffcc537d..0000000000 --- a/test/913-heaps/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/913-heaps/check b/test/913-heaps/check index 835850004a..f4892d0a07 100644 --- a/test/913-heaps/check +++ b/test/913-heaps/check @@ -14,9 +14,12 @@ # 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 +# 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 + patch -p0 expected.txt < expected_d8.diff fi ./default-check "$@" diff --git a/test/913-heaps/expected_d8.diff b/test/913-heaps/expected_d8.diff new file mode 100644 index 0000000000..83694220a3 --- /dev/null +++ b/test/913-heaps/expected_d8.diff @@ -0,0 +1,76 @@ +4,5c4 +< 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] +--- +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1] +49c48 +< 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=10,location= 8])--> 1@1000 [size=16, length=-1] +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] +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] +--- +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1] +115c114 +< 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=10,location= 8])--> 1@1000 [size=16, length=-1] +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] +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=8,location= 31])--> 1@1000 [size=16, length=-1] +177c177 +< 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=10,location= 8])--> 1@1000 [size=16, length=-1] +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] +201,202c202,203 +< 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] +--- +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=4,method=runFollowReferences,vreg=3,location= 164])--> 1000@0 [size=123456780050, length=-1] +246c247 +< 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=10,location= 8])--> 1@1000 [size=16, length=-1] +248c249,251 +< 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=4,method=runFollowReferences,vreg=3,location= 164])--> 1000@0 [size=123456780055, length=-1] +292c295 +< root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1] +--- +> root@root --(stack-local[id=1,tag=3000,depth=4,method=runFollowReferences,vreg=3,location= 181])--> 1000@0 [size=123456780060, length=-1] +319a323 +> root@root --(stack-local[id=1,tag=3000,depth=4,method=runFollowReferences,vreg=3,location= 181])--> 1000@0 [size=123456780065, length=-1] +347c351 +< 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=8,location= 31])--> 1@1000 [size=16, length=-1] +366c370 +< 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=10,location= 8])--> 1@1000 [size=16, length=-1] +368c372,373 +< 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] -- GitLab From da283050a1a3ddbb7cefae3f36e8c8c1a6acedb7 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 7 Nov 2017 21:17:24 +0000 Subject: [PATCH 003/226] Fix String.equals() for moveable String.class. If the String.class is moveable (i.e. running without boot image), the instanceof check emitted by the JIT in the String.equals() intrinsic would require read barriers. As we do not really care about the performance of running without the boot image, disable the intrinsic in this case. Test: 669-moveable-string-class-equals (--jit) Bug: 68181300 Change-Id: I39c9f9935e0482b3b30f1ae5cd23515cbda0603b --- compiler/optimizing/instruction_simplifier.cc | 14 ++++ compiler/optimizing/intrinsics.h | 1 + compiler/optimizing/intrinsics_arm64.cc | 7 ++ compiler/optimizing/intrinsics_arm_vixl.cc | 7 ++ compiler/optimizing/intrinsics_mips.cc | 7 ++ compiler/optimizing/intrinsics_mips64.cc | 7 ++ compiler/optimizing/intrinsics_x86.cc | 7 ++ compiler/optimizing/intrinsics_x86_64.cc | 7 ++ .../expected.txt | 1 + .../669-moveable-string-class-equals/info.txt | 2 + test/669-moveable-string-class-equals/run | 19 +++++ .../src/Main.java | 78 +++++++++++++++++++ test/common/runtime_state.cc | 9 +++ 13 files changed, 166 insertions(+) create mode 100644 test/669-moveable-string-class-equals/expected.txt create mode 100644 test/669-moveable-string-class-equals/info.txt create mode 100755 test/669-moveable-string-class-equals/run create mode 100644 test/669-moveable-string-class-equals/src/Main.java diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 2bd2d5f0a1..fbfee12be9 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -2024,6 +2024,20 @@ void InstructionSimplifierVisitor::SimplifyStringEquals(HInvoke* instruction) { ReferenceTypeInfo argument_rti = argument->GetReferenceTypeInfo(); if (argument_rti.IsValid() && argument_rti.IsStringClass()) { optimizations.SetArgumentIsString(); + } else if (kUseReadBarrier) { + DCHECK(instruction->GetResolvedMethod() != nullptr); + DCHECK(instruction->GetResolvedMethod()->GetDeclaringClass()->IsStringClass()); + Runtime* runtime = Runtime::Current(); + // For AOT, we always assume that the boot image shall contain the String.class and + // we do not need a read barrier for boot image classes as they are non-moveable. + // For JIT, check if we actually have a boot image; if we do, the String.class + // should also be non-moveable. + if (runtime->IsAotCompiler() || runtime->GetHeap()->HasBootImageSpace()) { + DCHECK(runtime->IsAotCompiler() || + !runtime->GetHeap()->IsMovableObject( + instruction->GetResolvedMethod()->GetDeclaringClass())); + optimizations.SetNoReadBarrierForStringClass(); + } } } } diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index bdeb261dbe..707ff3408e 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -203,6 +203,7 @@ class StringEqualsOptimizations : public IntrinsicOptimizations { INTRINSIC_OPTIMIZATION(ArgumentNotNull, 0); INTRINSIC_OPTIMIZATION(ArgumentIsString, 1); + INTRINSIC_OPTIMIZATION(NoReadBarrierForStringClass, 2); private: DISALLOW_COPY_AND_ASSIGN(StringEqualsOptimizations); diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index ef85f9ccc4..ca1b451e6b 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1519,6 +1519,13 @@ static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_lengt } void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) { + if (kEmitCompilerReadBarrier && + !StringEqualsOptimizations(invoke).GetArgumentIsString() && + !StringEqualsOptimizations(invoke).GetNoReadBarrierForStringClass()) { + // No support for this odd case (String class is moveable, not in the boot image). + return; + } + LocationSummary* locations = new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index e0874d9549..1d8ea092a4 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -1723,6 +1723,13 @@ static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_lengt } void IntrinsicLocationsBuilderARMVIXL::VisitStringEquals(HInvoke* invoke) { + if (kEmitCompilerReadBarrier && + !StringEqualsOptimizations(invoke).GetArgumentIsString() && + !StringEqualsOptimizations(invoke).GetNoReadBarrierForStringClass()) { + // No support for this odd case (String class is moveable, not in the boot image). + return; + } + LocationSummary* locations = new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); InvokeRuntimeCallingConventionARMVIXL calling_convention; diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 4a8fbf26ce..140526a018 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -2062,6 +2062,13 @@ void IntrinsicCodeGeneratorMIPS::VisitStringCompareTo(HInvoke* invoke) { // boolean java.lang.String.equals(Object anObject) void IntrinsicLocationsBuilderMIPS::VisitStringEquals(HInvoke* invoke) { + if (kEmitCompilerReadBarrier && + !StringEqualsOptimizations(invoke).GetArgumentIsString() && + !StringEqualsOptimizations(invoke).GetNoReadBarrierForStringClass()) { + // No support for this odd case (String class is moveable, not in the boot image). + return; + } + LocationSummary* locations = new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 512fb68fad..a58ff7c7f2 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1637,6 +1637,13 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringCompareTo(HInvoke* invoke) { // boolean java.lang.String.equals(Object anObject) void IntrinsicLocationsBuilderMIPS64::VisitStringEquals(HInvoke* invoke) { + if (kEmitCompilerReadBarrier && + !StringEqualsOptimizations(invoke).GetArgumentIsString() && + !StringEqualsOptimizations(invoke).GetNoReadBarrierForStringClass()) { + // No support for this odd case (String class is moveable, not in the boot image). + return; + } + LocationSummary* locations = new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 8a0b6aeb0e..baa410b884 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -1345,6 +1345,13 @@ void IntrinsicCodeGeneratorX86::VisitStringCompareTo(HInvoke* invoke) { } void IntrinsicLocationsBuilderX86::VisitStringEquals(HInvoke* invoke) { + if (kEmitCompilerReadBarrier && + !StringEqualsOptimizations(invoke).GetArgumentIsString() && + !StringEqualsOptimizations(invoke).GetNoReadBarrierForStringClass()) { + // No support for this odd case (String class is moveable, not in the boot image). + return; + } + LocationSummary* locations = new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 92ffda427b..6dd8b8e1f5 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -1520,6 +1520,13 @@ void IntrinsicCodeGeneratorX86_64::VisitStringCompareTo(HInvoke* invoke) { } void IntrinsicLocationsBuilderX86_64::VisitStringEquals(HInvoke* invoke) { + if (kEmitCompilerReadBarrier && + !StringEqualsOptimizations(invoke).GetArgumentIsString() && + !StringEqualsOptimizations(invoke).GetNoReadBarrierForStringClass()) { + // No support for this odd case (String class is moveable, not in the boot image). + return; + } + LocationSummary* locations = new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); diff --git a/test/669-moveable-string-class-equals/expected.txt b/test/669-moveable-string-class-equals/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/669-moveable-string-class-equals/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/669-moveable-string-class-equals/info.txt b/test/669-moveable-string-class-equals/info.txt new file mode 100644 index 0000000000..1d3202ef46 --- /dev/null +++ b/test/669-moveable-string-class-equals/info.txt @@ -0,0 +1,2 @@ +Regression test for String.equals() intrinsic instanceof check +when the String.class is moveable. diff --git a/test/669-moveable-string-class-equals/run b/test/669-moveable-string-class-equals/run new file mode 100755 index 0000000000..d0ab6f8d55 --- /dev/null +++ b/test/669-moveable-string-class-equals/run @@ -0,0 +1,19 @@ +#!/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 image, so that String.class is moveable. +# Reduce heap size to force more frequent GCs. +${RUN} --no-image --no-dex2oat --runtime-option -Xmx16m "$@" diff --git a/test/669-moveable-string-class-equals/src/Main.java b/test/669-moveable-string-class-equals/src/Main.java new file mode 100644 index 0000000000..4badadeae4 --- /dev/null +++ b/test/669-moveable-string-class-equals/src/Main.java @@ -0,0 +1,78 @@ +/* + * 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) { + System.loadLibrary(args[0]); + if (!hasJit()) { + // Make the test pass if not using JIT. + return; + } + if (hasImage()) { + throw new Error("The `run` script should prevent this test from running with an image!"); + } + if (!isClassMoveable(String.class)) { + throw new Error("String.class not moveable despite running without image!"); + } + + // Make sure the Main.test() is JIT-compiled and then call it. + ensureJitCompiled(Main.class, "test"); + test(); + } + + public static void test() { + int length = 5; + + // Hide the type of these strings in an Object array, + // so that we treat them as Object for the String.equals() below. + Object[] array = new Object[length]; + for (int i = 0; i != length; ++i) { + array[i] = "V" + i; + } + + for (int count = 0; count != 128 * 1024; ++count) { + for (int i = 0; i != length; ++i) { + allocateAtLeast1KiB(); + assertTrue(("V" + i).equals(array[i])); + } + } + } + + public static void allocateAtLeast1KiB() { + // Give GC more work by allocating Object arrays. + memory[allocationIndex] = new Object[1024 / 4]; + ++allocationIndex; + if (allocationIndex == memory.length) { + allocationIndex = 0; + } + } + + public static void assertTrue(boolean value) { + if (!value) { + throw new Error("Assertion failed!"); + } + } + + private native static boolean hasJit(); + private native static boolean hasImage(); + private native static boolean isClassMoveable(Class cls); + private static native void ensureJitCompiled(Class itf, String method_name); + + // We shall retain some allocated memory and release old allocations + // so that the GC has something to do. + public static Object[] memory = new Object[4096]; + public static int allocationIndex = 0; +} diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index 60c7315b6f..2b940ab7c7 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -267,4 +267,13 @@ extern "C" JNIEXPORT void JNICALL Java_Main_fetchProfiles(JNIEnv*, jclass) { code_cache->GetProfiledMethods(unused_locations, unused_vector); } +extern "C" JNIEXPORT jboolean JNICALL Java_Main_isClassMoveable(JNIEnv*, + jclass, + jclass cls) { + Runtime* runtime = Runtime::Current(); + ScopedObjectAccess soa(Thread::Current()); + ObjPtr klass = soa.Decode(cls); + return runtime->GetHeap()->IsMovableObject(klass); +} + } // namespace art -- GitLab From 0db16e00e3927445585a588499731c58c1ae1bef Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 8 Nov 2017 14:32:33 +0000 Subject: [PATCH 004/226] Use strcmp() for checking @{Fast,Critical}Native annotations. Instead of looking up the descriptor in boot class path loader (where @{Fast,Critical}Native are guaranteed to be already resolved) and then checking if it's the @{Fast,Critical}Native annotation, just check the descriptor with strcmp(). Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Bug: 65574695 Change-Id: I765590d039981d169fb3c606b6166580a84303b6 --- runtime/art_method.cc | 27 ++---- runtime/art_method.h | 5 -- runtime/dex_file_annotations.cc | 144 +++++++++++++++++++++----------- runtime/dex_file_annotations.h | 15 +++- runtime/image.cc | 2 +- 5 files changed, 112 insertions(+), 81 deletions(-) diff --git a/runtime/art_method.cc b/runtime/art_method.cc index b5e0f66575..8709643c3e 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -429,30 +429,15 @@ bool ArtMethod::IsPolymorphicSignature() { } bool ArtMethod::IsAnnotatedWithFastNative() { - return IsAnnotatedWith(WellKnownClasses::dalvik_annotation_optimization_FastNative, - DexFile::kDexVisibilityBuild, - /* lookup_in_resolved_boot_classes */ true); + ScopedObjectAccess soa(Thread::Current()); + return annotations::HasFastNativeMethodBuildAnnotation( + *GetDexFile(), GetClassDef(), GetDexMethodIndex()); } bool ArtMethod::IsAnnotatedWithCriticalNative() { - return IsAnnotatedWith(WellKnownClasses::dalvik_annotation_optimization_CriticalNative, - DexFile::kDexVisibilityBuild, - /* lookup_in_resolved_boot_classes */ true); -} - -bool ArtMethod::IsAnnotatedWith(jclass klass, - uint32_t visibility, - bool lookup_in_resolved_boot_classes) { - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); - StackHandleScope<1> shs(self); - - ObjPtr annotation = soa.Decode(klass); - DCHECK(annotation->IsAnnotation()); - Handle annotation_handle(shs.NewHandle(annotation)); - - return annotations::IsMethodAnnotationPresent( - this, annotation_handle, visibility, lookup_in_resolved_boot_classes); + ScopedObjectAccess soa(Thread::Current()); + return annotations::HasCriticalNativeMethodBuildAnnotation( + *GetDexFile(), GetClassDef(), GetDexMethodIndex()); } static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, diff --git a/runtime/art_method.h b/runtime/art_method.h index ca2e34e071..8927481e46 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -751,11 +751,6 @@ class ArtMethod FINAL { private: uint16_t FindObsoleteDexClassDefIndex() REQUIRES_SHARED(Locks::mutator_lock_); - // If `lookup_in_resolved_boot_classes` is true, look up any of the - // method's annotations' classes in the bootstrap class loader's - // resolved types; otherwise, resolve them as a side effect. - bool IsAnnotatedWith(jclass klass, uint32_t visibility, bool lookup_in_resolved_boot_classes); - static constexpr size_t PtrSizedFieldsOffset(PointerSize pointer_size) { // Round up to pointer size for padding field. Tested in art_method.cc. return RoundUp(offsetof(ArtMethod, hotness_count_) + sizeof(hotness_count_), diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index 845202ff72..5496efd108 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -261,32 +261,38 @@ const uint8_t* SearchEncodedAnnotation(const DexFile& dex_file, return nullptr; } -const DexFile::AnnotationSetItem* FindAnnotationSetForMethod(ArtMethod* method) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (method->IsProxyMethod()) { - return nullptr; - } - const DexFile* dex_file = method->GetDexFile(); +const DexFile::AnnotationSetItem* FindAnnotationSetForMethod(const DexFile& dex_file, + const DexFile::ClassDef& class_def, + uint32_t method_index) { const DexFile::AnnotationsDirectoryItem* annotations_dir = - dex_file->GetAnnotationsDirectory(method->GetClassDef()); + dex_file.GetAnnotationsDirectory(class_def); if (annotations_dir == nullptr) { return nullptr; } const DexFile::MethodAnnotationsItem* method_annotations = - dex_file->GetMethodAnnotations(annotations_dir); + dex_file.GetMethodAnnotations(annotations_dir); if (method_annotations == nullptr) { return nullptr; } - uint32_t method_index = method->GetDexMethodIndex(); uint32_t method_count = annotations_dir->methods_size_; for (uint32_t i = 0; i < method_count; ++i) { if (method_annotations[i].method_idx_ == method_index) { - return dex_file->GetMethodAnnotationSetItem(method_annotations[i]); + return dex_file.GetMethodAnnotationSetItem(method_annotations[i]); } } return nullptr; } +inline const DexFile::AnnotationSetItem* FindAnnotationSetForMethod(ArtMethod* method) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (method->IsProxyMethod()) { + return nullptr; + } + return FindAnnotationSetForMethod(*method->GetDexFile(), + method->GetClassDef(), + method->GetDexMethodIndex()); +} + const DexFile::ParameterAnnotationsItem* FindAnnotationsItemForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile* dex_file = method->GetDexFile(); @@ -764,8 +770,7 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet( const ClassData& klass, const DexFile::AnnotationSetItem* annotation_set, uint32_t visibility, - Handle annotation_class, - bool lookup_in_resolved_boot_classes = false) + Handle annotation_class) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile& dex_file = klass.GetDexFile(); for (uint32_t i = 0; i < annotation_set->size_; ++i) { @@ -778,35 +783,19 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet( ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Thread* self = Thread::Current(); mirror::Class* resolved_class; - if (lookup_in_resolved_boot_classes) { - // Note: We cannot use ClassLinker::LookupResolvedType() because the current DexCache - // may not be registered with the boot class path ClassLoader and we must not pollute - // the DexCache with classes that are not in the associated ClassLoader's ClassTable. - const char* descriptor = dex_file.StringByTypeIdx(dex::TypeIndex(type_index)); - ObjPtr looked_up_class = - class_linker->LookupClass(self, descriptor, /* class_loader */ nullptr); - resolved_class = looked_up_class.Ptr(); - if (resolved_class == nullptr) { - // If `resolved_class` is null, this is fine: just ignore that - // annotation item. We expect this to happen, as we do not - // attempt to resolve the annotation's class in this code path. - continue; - } - } else { - StackHandleScope<2> hs(self); - resolved_class = class_linker->ResolveType( - klass.GetDexFile(), - dex::TypeIndex(type_index), - hs.NewHandle(klass.GetDexCache()), - hs.NewHandle(klass.GetClassLoader())); - if (resolved_class == nullptr) { - std::string temp; - LOG(WARNING) << StringPrintf("Unable to resolve %s annotation class %d", - klass.GetRealClass()->GetDescriptor(&temp), type_index); - CHECK(self->IsExceptionPending()); - self->ClearException(); - continue; - } + StackHandleScope<2> hs(self); + resolved_class = class_linker->ResolveType( + klass.GetDexFile(), + dex::TypeIndex(type_index), + hs.NewHandle(klass.GetDexCache()), + hs.NewHandle(klass.GetClassLoader())); + if (resolved_class == nullptr) { + std::string temp; + LOG(WARNING) << StringPrintf("Unable to resolve %s annotation class %d", + klass.GetRealClass()->GetDescriptor(&temp), type_index); + CHECK(self->IsExceptionPending()); + self->ClearException(); + continue; } if (resolved_class == annotation_class.Get()) { return annotation_item; @@ -822,8 +811,8 @@ mirror::Object* GetAnnotationObjectFromAnnotationSet( uint32_t visibility, Handle annotation_class) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile::AnnotationItem* annotation_item = - GetAnnotationItemFromAnnotationSet(klass, annotation_set, visibility, annotation_class); + const DexFile::AnnotationItem* annotation_item = GetAnnotationItemFromAnnotationSet( + klass, annotation_set, visibility, annotation_class); if (annotation_item == nullptr) { return nullptr; } @@ -1235,21 +1224,74 @@ mirror::ObjectArray* GetSignatureAnnotationForMethod(ArtMethod* bool IsMethodAnnotationPresent(ArtMethod* method, Handle annotation_class, - uint32_t visibility /* = DexFile::kDexVisibilityRuntime */, - bool lookup_in_resolved_boot_classes /* = false */) { + uint32_t visibility /* = DexFile::kDexVisibilityRuntime */) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method); if (annotation_set == nullptr) { return false; } - const DexFile::AnnotationItem* annotation_item = - GetAnnotationItemFromAnnotationSet(ClassData(method), - annotation_set, - visibility, - annotation_class, - lookup_in_resolved_boot_classes); + const DexFile::AnnotationItem* annotation_item = GetAnnotationItemFromAnnotationSet( + ClassData(method), annotation_set, visibility, annotation_class); return annotation_item != nullptr; } +static void DCheckNativeAnnotation(const char* descriptor, jclass cls) { + if (kIsDebugBuild) { + ScopedObjectAccess soa(Thread::Current()); + ObjPtr klass = soa.Decode(cls); + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + // Lookup using the boot class path loader should yield the annotation class. + CHECK_EQ(klass, linker->LookupClass(soa.Self(), descriptor, /* class_loader */ nullptr)); + } +} + +// Check whether a method from the `dex_file` with the given `annotation_set` +// is annotated with `annotation_descriptor` with build visibility. +static bool IsMethodBuildAnnotationPresent(const DexFile& dex_file, + const DexFile::AnnotationSetItem& annotation_set, + const char* annotation_descriptor, + jclass annotation_class) { + for (uint32_t i = 0; i < annotation_set.size_; ++i) { + const DexFile::AnnotationItem* annotation_item = dex_file.GetAnnotationItem(&annotation_set, i); + if (!IsVisibilityCompatible(annotation_item->visibility_, DexFile::kDexVisibilityBuild)) { + continue; + } + const uint8_t* annotation = annotation_item->annotation_; + uint32_t type_index = DecodeUnsignedLeb128(&annotation); + const char* descriptor = dex_file.StringByTypeIdx(dex::TypeIndex(type_index)); + if (strcmp(descriptor, annotation_descriptor) == 0) { + DCheckNativeAnnotation(descriptor, annotation_class); + return true; + } + } + return false; +} + +uint32_t HasFastNativeMethodBuildAnnotation(const DexFile& dex_file, + const DexFile::ClassDef& class_def, + uint32_t method_index) { + const DexFile::AnnotationSetItem* annotation_set = + FindAnnotationSetForMethod(dex_file, class_def, method_index); + return annotation_set != nullptr && + IsMethodBuildAnnotationPresent( + dex_file, + *annotation_set, + "Ldalvik/annotation/optimization/FastNative;", + WellKnownClasses::dalvik_annotation_optimization_FastNative); +} + +uint32_t HasCriticalNativeMethodBuildAnnotation(const DexFile& dex_file, + const DexFile::ClassDef& class_def, + uint32_t method_index) { + const DexFile::AnnotationSetItem* annotation_set = + FindAnnotationSetForMethod(dex_file, class_def, method_index); + return annotation_set != nullptr && + IsMethodBuildAnnotationPresent( + dex_file, + *annotation_set, + "Ldalvik/annotation/optimization/CriticalNative;", + WellKnownClasses::dalvik_annotation_optimization_CriticalNative); +} + mirror::Object* GetAnnotationForClass(Handle klass, Handle annotation_class) { ClassData data(klass); diff --git a/runtime/dex_file_annotations.h b/runtime/dex_file_annotations.h index e1088823c3..04ff3a11a5 100644 --- a/runtime/dex_file_annotations.h +++ b/runtime/dex_file_annotations.h @@ -72,9 +72,18 @@ mirror::ObjectArray* GetSignatureAnnotationForMethod(ArtMethod* // side effect. bool IsMethodAnnotationPresent(ArtMethod* method, Handle annotation_class, - uint32_t visibility = DexFile::kDexVisibilityRuntime, - bool lookup_in_resolved_boot_classes = false) - REQUIRES_SHARED(Locks::mutator_lock_); + uint32_t visibility = DexFile::kDexVisibilityRuntime) + REQUIRES_SHARED(Locks::mutator_lock_); +// Check whether a method from the `dex_file` with the given `method_index` +// is annotated with @dalvik.annotation.optimization.FastNative with build visibility. +uint32_t HasFastNativeMethodBuildAnnotation(const DexFile& dex_file, + const DexFile::ClassDef& class_def, + uint32_t method_index); +// Check whether a method from the `dex_file` with the given `method_index` +// is annotated with @dalvik.annotation.optimization.CriticalNative with build visibility. +uint32_t HasCriticalNativeMethodBuildAnnotation(const DexFile& dex_file, + const DexFile::ClassDef& class_def, + uint32_t method_index); // Class annotations. mirror::Object* GetAnnotationForClass(Handle klass, diff --git a/runtime/image.cc b/runtime/image.cc index aae572b68d..cf5feaca56 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', '4', 'A', '\0' }; // VarHandle fence intrinsics +const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '0', '\0' }; // strcmp() @FastNative. ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, -- GitLab From 293f1c006cc283345630ea38266f3ee38b624d5d Mon Sep 17 00:00:00 2001 From: Mingyao Yang Date: Wed, 8 Nov 2017 15:22:17 -0800 Subject: [PATCH 005/226] Check invocation's side effects for LSE. More optimization is allowed if an invocation doesn't do read/write. Test: run-test on host. Change-Id: Id80e2fa90b8c843afd852778e8f7e6c69c765ad5 --- compiler/optimizing/escape.cc | 4 +++- compiler/optimizing/load_store_elimination.cc | 12 +++++++--- test/530-checker-lse/src/Main.java | 22 +++++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/compiler/optimizing/escape.cc b/compiler/optimizing/escape.cc index 9df5bf1017..88e9326093 100644 --- a/compiler/optimizing/escape.cc +++ b/compiler/optimizing/escape.cc @@ -51,7 +51,9 @@ void CalculateEscape(HInstruction* reference, *is_singleton_and_not_returned = false; *is_singleton_and_not_deopt_visible = false; return; - } else if (user->IsPhi() || user->IsSelect() || user->IsInvoke() || + } else if (user->IsPhi() || + user->IsSelect() || + (user->IsInvoke() && user->GetSideEffects().DoesAnyWrite()) || (user->IsInstanceFieldSet() && (reference == user->InputAt(1))) || (user->IsUnresolvedInstanceFieldSet() && (reference == user->InputAt(1))) || (user->IsStaticFieldSet() && (reference == user->InputAt(1))) || diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 7dff696e32..c041f56548 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -528,15 +528,21 @@ class LSEVisitor : public HGraphDelegateVisitor { } } - void HandleInvoke(HInstruction* invoke) { + void HandleInvoke(HInstruction* instruction) { + SideEffects side_effects = instruction->GetSideEffects(); ScopedArenaVector& heap_values = - heap_values_for_[invoke->GetBlock()->GetBlockId()]; + heap_values_for_[instruction->GetBlock()->GetBlockId()]; for (size_t i = 0; i < heap_values.size(); i++) { ReferenceInfo* ref_info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo(); if (ref_info->IsSingleton()) { // Singleton references cannot be seen by the callee. } else { - heap_values[i] = kUnknownHeapValue; + if (side_effects.DoesAnyRead()) { + KeepIfIsStore(heap_values[i]); + } + if (side_effects.DoesAnyWrite()) { + heap_values[i] = kUnknownHeapValue; + } } } } diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java index 7ae873af54..58abbee289 100644 --- a/test/530-checker-lse/src/Main.java +++ b/test/530-checker-lse/src/Main.java @@ -810,6 +810,23 @@ public class Main { return arr[0] + arr[1] + arr[2] + arr[3]; } + /// CHECK-START: int Main.testNoSideEffects(int[]) load_store_elimination (before) + /// CHECK: ArraySet + /// CHECK: ArraySet + /// CHECK: ArrayGet + + /// CHECK-START: int Main.testNoSideEffects(int[]) load_store_elimination (after) + /// CHECK: ArraySet + /// CHECK: ArraySet + /// CHECK-NOT: ArrayGet + + private static int testNoSideEffects(int[] array) { + array[0] = 101; + int bitCount = Integer.bitCount(0x3456); + array[1] = array[0] + 1; + return array[0] + bitCount; + } + /// CHECK-START: double Main.getCircleArea(double, boolean) load_store_elimination (before) /// CHECK: NewInstance @@ -1017,6 +1034,11 @@ public class Main { assertIntEquals(testStoreStore().i, 41); assertIntEquals(testStoreStore().j, 43); assertIntEquals(testStoreStoreWithDeoptimize(new int[4]), 4); + + int ret = testNoSideEffects(iarray); + assertIntEquals(iarray[0], 101); + assertIntEquals(iarray[1], 102); + assertIntEquals(ret, 108); } static boolean sFlag; -- GitLab From f44a60ae9a4326c0bb335bd592601621113fe8b3 Mon Sep 17 00:00:00 2001 From: Przemyslaw Szczepaniak Date: Thu, 9 Nov 2017 09:47:32 +0000 Subject: [PATCH 006/226] Update art libcore_failures after test package move http://meme/5247198547935232 Test: vogar Bug: 68932217 Change-Id: I608ec3451b2686e026ab40232b456ccda1d29d39 --- tools/libcore_failures.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index 23a70f7ae7..68aeedde85 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -33,15 +33,15 @@ 777 all directories on path to socket (on device without su).", result: EXEC_FAILED, modes: [device], - names: ["libcore.io.OsTest#testUnixDomainSockets_in_file_system"] + names: ["libcore.libcore.io.OsTest#testUnixDomainSockets_in_file_system"] }, { description: "TCP_USER_TIMEOUT is not defined on host's tcp.h (glibc-2.15-4.8).", result: EXEC_FAILED, modes: [host], names: ["libcore.android.system.OsConstantsTest#testTcpUserTimeoutIsDefined", - "libcore.io.OsTest#test_socket_tcpUserTimeout_setAndGet", - "libcore.io.OsTest#test_socket_tcpUserTimeout_doesNotWorkOnDatagramSocket"], + "libcore.libcore.io.OsTest#test_socket_tcpUserTimeout_setAndGet", + "libcore.libcore.io.OsTest#test_socket_tcpUserTimeout_doesNotWorkOnDatagramSocket"], bug: 30402085 }, { @@ -113,7 +113,7 @@ { description: "Needs kernel updates on host/device", result: EXEC_FAILED, - names: ["libcore.io.OsTest#test_socketPing"] + names: ["libcore.libcore.io.OsTest#test_socketPing"] }, { description: "Linker issues in chrooted environment", @@ -131,7 +131,7 @@ description: "test_xattr fails on arm64 on the buildbots only: needs investigation", result: EXEC_FAILED, modes: [device], - names: ["libcore.io.OsTest#test_xattr"], + names: ["libcore.libcore.io.OsTest#test_xattr"], bug: 22258911 }, { @@ -143,9 +143,9 @@ { description: "Lack of IPv6 on some buildbot slaves", result: EXEC_FAILED, - names: ["libcore.io.OsTest#test_byteBufferPositions_sendto_recvfrom_af_inet6", - "libcore.io.OsTest#test_sendtoSocketAddress_af_inet6", - "libcore.io.OsTest#test_recvfrom_EmptyPacket"], + names: ["libcore.libcore.io.OsTest#test_byteBufferPositions_sendto_recvfrom_af_inet6", + "libcore.libcore.io.OsTest#test_sendtoSocketAddress_af_inet6", + "libcore.libcore.io.OsTest#test_recvfrom_EmptyPacket"], bug: 25178637 }, { @@ -220,6 +220,6 @@ result: EXEC_FAILED, modes: [device], bug: 69023954, - names: ["libcore.io.OsTest#test_setgroups"] + names: ["libcore.libcore.io.OsTest#test_setgroups"] } ] -- GitLab From 589853739e97a87faa7f9c12f12b5c74596b0312 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 9 Nov 2017 14:38:02 +0000 Subject: [PATCH 007/226] Remove --no-stream from script. --no-stream runs art executions on parallel, which doesn't work when all processes want to generate a boot image at the same location. Test: run-libcore-tests Change-Id: Ica93393f9cb4fb35465e0b432cb5af62b4289f4f --- tools/run-libcore-tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh index 51c7348c97..673eea8cd9 100755 --- a/tools/run-libcore-tests.sh +++ b/tools/run-libcore-tests.sh @@ -169,4 +169,4 @@ fi # Run the tests using vogar. echo "Running tests for the following test packages:" echo ${working_packages[@]} | tr " " "\n" -vogar --no-stream $vogar_args $expectations $(cparg $DEPS) ${working_packages[@]} +vogar $vogar_args $expectations $(cparg $DEPS) ${working_packages[@]} -- GitLab From 0e9e85316e41860dff3ff8d54b42801c51b69b78 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 9 Nov 2017 15:58:36 +0000 Subject: [PATCH 008/226] Clean up 669-moveable-string-class-equals. This is a follow-up addressing late comments on https://android-review.googlesource.com/531015 . The --no-dex2oat in unnecessary since https://android-review.googlesource.com/531270 . Test: 669-moveable-string-class-equals (--jit) Bug: 68181300 Change-Id: Ia978a1cacec7a3aec78e4c132d78c0031f3f5cd9 --- test/669-moveable-string-class-equals/run | 2 +- test/669-moveable-string-class-equals/src/Main.java | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/test/669-moveable-string-class-equals/run b/test/669-moveable-string-class-equals/run index d0ab6f8d55..7c74d8ca0e 100755 --- a/test/669-moveable-string-class-equals/run +++ b/test/669-moveable-string-class-equals/run @@ -16,4 +16,4 @@ # Run without image, so that String.class is moveable. # Reduce heap size to force more frequent GCs. -${RUN} --no-image --no-dex2oat --runtime-option -Xmx16m "$@" +${RUN} --no-image --runtime-option -Xmx16m "$@" diff --git a/test/669-moveable-string-class-equals/src/Main.java b/test/669-moveable-string-class-equals/src/Main.java index 4badadeae4..d182d51a25 100644 --- a/test/669-moveable-string-class-equals/src/Main.java +++ b/test/669-moveable-string-class-equals/src/Main.java @@ -43,6 +43,10 @@ public class Main { array[i] = "V" + i; } + // Continually check string equality between a newly allocated String and an + // already allocated String with the same contents while allocating over 128MiB + // memory (with heap size limited to 16MiB), ensuring we run GC and stress the + // instanceof check in the String.equals() implementation. for (int count = 0; count != 128 * 1024; ++count) { for (int i = 0; i != length; ++i) { allocateAtLeast1KiB(); -- GitLab From 446c6b240dadab9893e4dc0a4b81ba284da3bafa Mon Sep 17 00:00:00 2001 From: David Sehr Date: Thu, 9 Nov 2017 14:08:19 -0800 Subject: [PATCH 009/226] Comment lack of return check on usleep As the usleep calls used in thread_list are during shutdown and other non-correctness related locations, add a comment to that effect. Bug: 29527582 Test: make -j 50 test-art-host Change-Id: I9c72564a13e28db6681b21d676a62f63ba052937 --- runtime/thread_list.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 88f1fc6991..7c4a9ab0b1 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -302,6 +302,8 @@ NO_RETURN static void UnsafeLogFatalForThreadSuspendAllTimeout() { // Unlike suspending all threads where we can wait to acquire the mutator_lock_, suspending an // individual thread requires polling. delay_us is the requested sleep wait. If delay_us is 0 then // we use sched_yield instead of calling usleep. +// Although there is the possibility, here and elsewhere, that usleep could return -1 and +// errno = EINTR, there should be no problem if interrupted, so we do not check. static void ThreadSuspendSleep(useconds_t delay_us) { if (delay_us == 0) { sched_yield(); -- GitLab From 3e5fecdeacdacf847d376adb05a9ad5587648139 Mon Sep 17 00:00:00 2001 From: Chris Larsen Date: Thu, 9 Nov 2017 14:21:28 -0800 Subject: [PATCH 010/226] MIPS32: Use conditional moves to compute 64-bit shifts. Use conditional moves in InstructionCodeGeneratorMIPS::HandleShift()'s 64-bit variable shifts to avoid conditional branches (Beqz(TMP, &done)). Also, on R6 use Beqzc(TMP, &done, /* is_bare */ true) in place of Beqz(TMP, &done). Test: Boot & run tests on MIPS32r6 QEMU & on CI-20 hardware (MIPS32r2). Test: test/testrunner/testrunner.py --target --optimizing Change-Id: I4d34a51cd2397c845f936af853cb5f30e82de438 --- compiler/optimizing/code_generator_mips.cc | 51 ++++++++++++++++------ 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index f9f5a4da56..ddec0cc453 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -2474,6 +2474,7 @@ void InstructionCodeGeneratorMIPS::HandleShift(HBinaryOperation* instr) { } } } else { + const bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); MipsLabel done; if (instr->IsShl()) { __ Sllv(dst_low, lhs_low, rhs_reg); @@ -2483,9 +2484,14 @@ void InstructionCodeGeneratorMIPS::HandleShift(HBinaryOperation* instr) { __ Sllv(dst_high, lhs_high, rhs_reg); __ Or(dst_high, dst_high, TMP); __ Andi(TMP, rhs_reg, kMipsBitsPerWord); - __ Beqz(TMP, &done); - __ Move(dst_high, dst_low); - __ Move(dst_low, ZERO); + if (isR6) { + __ Beqzc(TMP, &done, /* is_bare */ true); + __ Move(dst_high, dst_low); + __ Move(dst_low, ZERO); + } else { + __ Movn(dst_high, dst_low, TMP); + __ Movn(dst_low, ZERO, TMP); + } } else if (instr->IsShr()) { __ Srav(dst_high, lhs_high, rhs_reg); __ Nor(AT, ZERO, rhs_reg); @@ -2494,9 +2500,15 @@ void InstructionCodeGeneratorMIPS::HandleShift(HBinaryOperation* instr) { __ Srlv(dst_low, lhs_low, rhs_reg); __ Or(dst_low, dst_low, TMP); __ Andi(TMP, rhs_reg, kMipsBitsPerWord); - __ Beqz(TMP, &done); - __ Move(dst_low, dst_high); - __ Sra(dst_high, dst_high, 31); + if (isR6) { + __ Beqzc(TMP, &done, /* is_bare */ true); + __ Move(dst_low, dst_high); + __ Sra(dst_high, dst_high, 31); + } else { + __ Sra(AT, dst_high, 31); + __ Movn(dst_low, dst_high, TMP); + __ Movn(dst_high, AT, TMP); + } } else if (instr->IsUShr()) { __ Srlv(dst_high, lhs_high, rhs_reg); __ Nor(AT, ZERO, rhs_reg); @@ -2505,10 +2517,15 @@ void InstructionCodeGeneratorMIPS::HandleShift(HBinaryOperation* instr) { __ Srlv(dst_low, lhs_low, rhs_reg); __ Or(dst_low, dst_low, TMP); __ Andi(TMP, rhs_reg, kMipsBitsPerWord); - __ Beqz(TMP, &done); - __ Move(dst_low, dst_high); - __ Move(dst_high, ZERO); - } else { + if (isR6) { + __ Beqzc(TMP, &done, /* is_bare */ true); + __ Move(dst_low, dst_high); + __ Move(dst_high, ZERO); + } else { + __ Movn(dst_low, dst_high, TMP); + __ Movn(dst_high, ZERO, TMP); + } + } else { // Rotate. __ Nor(AT, ZERO, rhs_reg); __ Srlv(TMP, lhs_low, rhs_reg); __ Sll(dst_low, lhs_high, 1); @@ -2519,10 +2536,16 @@ void InstructionCodeGeneratorMIPS::HandleShift(HBinaryOperation* instr) { __ Sllv(dst_high, dst_high, AT); __ Or(dst_high, dst_high, TMP); __ Andi(TMP, rhs_reg, kMipsBitsPerWord); - __ Beqz(TMP, &done); - __ Move(TMP, dst_high); - __ Move(dst_high, dst_low); - __ Move(dst_low, TMP); + if (isR6) { + __ Beqzc(TMP, &done, /* is_bare */ true); + __ Move(TMP, dst_high); + __ Move(dst_high, dst_low); + __ Move(dst_low, TMP); + } else { + __ Movn(AT, dst_high, TMP); + __ Movn(dst_high, dst_low, TMP); + __ Movn(dst_low, AT, TMP); + } } __ Bind(&done); } -- GitLab From 6c3e1a03eb3f6ff5d759bd7959b3e11e65a9d1d1 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 9 Nov 2017 10:29:32 -0800 Subject: [PATCH 011/226] ART: Refactor build_bot.sh Correctly refer to host and target specific versions of libraries, so as to avoid building the other mode. Test: art/tools/buildbot-build.sh --mode host Test: art/tools/buildbot-build.sh --mode target Change-Id: I80f7b774cc0570428cff9ec691720da21384c38a --- tools/buildbot-build.sh | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh index 53b509336e..fd9ad0bb0f 100755 --- a/tools/buildbot-build.sh +++ b/tools/buildbot-build.sh @@ -35,7 +35,7 @@ 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 libjdwp" +common_targets="vogar core-tests apache-harmony-jdwp-tests-hostdex jsr166-tests mockito-target" mode="target" j_arg="-j$(nproc)" showcommands= @@ -70,16 +70,23 @@ fi extra_args=SOONG_ALLOW_MISSING_DEPENDENCIES=true if [[ $mode == "host" ]]; then - make_command="make $j_arg $extra_args $showcommands build-art-host-tests $common_targets dx-tests" - make_command+=" ${out_dir}/host/linux-x86/lib/libjavacoretests.so " - make_command+=" ${out_dir}/host/linux-x86/lib64/libjavacoretests.so" - make_command+=" libwrapagentpropertiesd libwrapagentproperties" + make_command="make $j_arg $extra_args $showcommands build-art-host-tests $common_targets" + make_command+=" dx-tests" + mode_suffix="-host" elif [[ $mode == "target" ]]; then make_command="make $j_arg $extra_args $showcommands build-art-target-tests $common_targets" - make_command+=" libjavacrypto libjavacoretests libnetd_client linker toybox toolbox sh" + make_command+=" libjavacrypto-target libnetd_client-target linker toybox toolbox sh" 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" fi +mode_specific_libraries="libjavacoretests libjdwp libwrapagentproperties libwrapagentpropertiesd" +for LIB in ${mode_specific_libraries} ; do + make_command+=" $LIB${mode_suffix}" +done + + + echo "Executing $make_command" $make_command -- GitLab From 767d0397b07e3f1e75c4fd754632e73369b9399e Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 9 Nov 2017 14:06:43 -0800 Subject: [PATCH 012/226] Increment gAborting for UnsafeLogFatalForThreadSuspendAllTimeout Increment gAborting before doing the thread list dump since we don't want any failures from AssertThreadSuspensionIsAllowable in cases where thread suspension is not allowed. Bug: 69044468 Test: m Change-Id: I87d09f07fa8379fb121ced5c2086d1de78c6eec6 --- runtime/thread_list.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 88f1fc6991..7b0ef80024 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -288,12 +288,17 @@ void ThreadList::AssertThreadsAreSuspended(Thread* self, Thread* ignore1, Thread #if HAVE_TIMED_RWLOCK // Attempt to rectify locks so that we dump thread list with required locks before exiting. NO_RETURN static void UnsafeLogFatalForThreadSuspendAllTimeout() { + // Increment gAborting before doing the thread list dump since we don't want any failures from + // AssertThreadSuspensionIsAllowable in cases where thread suspension is not allowed. + // See b/69044468. + ++gAborting; Runtime* runtime = Runtime::Current(); std::ostringstream ss; ss << "Thread suspend timeout\n"; Locks::mutator_lock_->Dump(ss); ss << "\n"; runtime->GetThreadList()->Dump(ss); + --gAborting; LOG(FATAL) << ss.str(); exit(0); } -- GitLab From b50b16a68ababbc9acab6102bf0bb63bd5083763 Mon Sep 17 00:00:00 2001 From: "xueliang.zhong" Date: Tue, 19 Sep 2017 17:43:29 +0100 Subject: [PATCH 013/226] Support VecLoad and VecStore in LSA. Test: test-art-host Test: test-art-target Test: load_store_analysis_test Change-Id: I7d819061ec9ea12f86a926566c3845231fce6e26 --- compiler/optimizing/load_store_analysis.cc | 179 ++++++------ compiler/optimizing/load_store_analysis.h | 129 +++++++-- .../optimizing/load_store_analysis_test.cc | 272 +++++++++++++++--- compiler/optimizing/load_store_elimination.cc | 38 ++- compiler/optimizing/scheduler.cc | 9 +- compiler/optimizing/scheduler_test.cc | 24 +- 6 files changed, 482 insertions(+), 169 deletions(-) diff --git a/compiler/optimizing/load_store_analysis.cc b/compiler/optimizing/load_store_analysis.cc index 5a8ac59195..8b1812a6de 100644 --- a/compiler/optimizing/load_store_analysis.cc +++ b/compiler/optimizing/load_store_analysis.cc @@ -22,111 +22,130 @@ namespace art { // The number of heap locations for most of the methods stays below this threshold. constexpr size_t kMaxNumberOfHeapLocations = 32; -// Check if array indices array[idx1 +/- CONST] and array[idx2] MAY alias. -static bool BinaryOpAndIndexMayAlias(const HBinaryOperation* idx1, const HInstruction* idx2) { - DCHECK(idx1 != nullptr); - DCHECK(idx2 != nullptr); +// Test if two integer ranges [l1,h1] and [l2,h2] overlap. +// Note that the ranges are inclusive on both ends. +// l1|------|h1 +// l2|------|h2 +static bool CanIntegerRangesOverlap(int64_t l1, int64_t h1, int64_t l2, int64_t h2) { + return std::max(l1, l2) <= std::min(h1, h2); +} - if (!idx1->IsAdd() && !idx1->IsSub()) { +static bool IsAddOrSub(const HInstruction* instruction) { + return instruction->IsAdd() || instruction->IsSub(); +} + +static bool CanBinaryOpAndIndexAlias(const HBinaryOperation* idx1, + const size_t vector_length1, + const HInstruction* idx2, + const size_t vector_length2) { + if (!IsAddOrSub(idx1)) { // We currently only support Add and Sub operations. return true; } - - HConstant* cst = idx1->GetConstantRight(); - if (cst == nullptr || cst->IsArithmeticZero()) { + if (idx1->AsBinaryOperation()->GetLeastConstantLeft() != idx2) { + // Cannot analyze [i+CONST1] and [j]. return true; } - - if (idx1->GetLeastConstantLeft() == idx2) { - // for example, array[idx1 + 1] and array[idx1] - return false; + if (!idx1->GetConstantRight()->IsIntConstant()) { + return true; } - return true; + // Since 'i' are the same in [i+CONST] and [i], + // further compare [CONST] and [0]. + int64_t l1 = idx1->IsAdd() ? + idx1->GetConstantRight()->AsIntConstant()->GetValue() : + -idx1->GetConstantRight()->AsIntConstant()->GetValue(); + int64_t l2 = 0; + int64_t h1 = l1 + (vector_length1 - 1); + int64_t h2 = l2 + (vector_length2 - 1); + return CanIntegerRangesOverlap(l1, h1, l2, h2); } -// Check if Add and Sub MAY alias when used as indices in arrays. -static bool BinaryOpsMayAlias(const HBinaryOperation* idx1, const HBinaryOperation* idx2) { - DCHECK(idx1!= nullptr); - DCHECK(idx2 != nullptr); - - HConstant* idx1_cst = idx1->GetConstantRight(); - HInstruction* idx1_other = idx1->GetLeastConstantLeft(); - HConstant* idx2_cst = idx2->GetConstantRight(); - HInstruction* idx2_other = idx2->GetLeastConstantLeft(); - - if (idx1_cst == nullptr || idx1_other == nullptr || - idx2_cst == nullptr || idx2_other == nullptr) { - // We only analyze patterns like [i +/- CONST]. +static bool CanBinaryOpsAlias(const HBinaryOperation* idx1, + const size_t vector_length1, + const HBinaryOperation* idx2, + const size_t vector_length2) { + if (!IsAddOrSub(idx1) || !IsAddOrSub(idx2)) { + // We currently only support Add and Sub operations. return true; } - - if (idx1_other != idx2_other) { - // For example, [j+1] and [k+1] MAY alias. + if (idx1->AsBinaryOperation()->GetLeastConstantLeft() != + idx2->AsBinaryOperation()->GetLeastConstantLeft()) { + // Cannot analyze [i+CONST1] and [j+CONST2]. return true; } - - if ((idx1->IsAdd() && idx2->IsAdd()) || - (idx1->IsSub() && idx2->IsSub())) { - return idx1_cst->AsIntConstant()->GetValue() == idx2_cst->AsIntConstant()->GetValue(); - } - - if ((idx1->IsAdd() && idx2->IsSub()) || - (idx1->IsSub() && idx2->IsAdd())) { - // [i + CONST1] and [i - CONST2] MAY alias iff CONST1 == -CONST2. - // By checking CONST1 == -CONST2, following cases are handled: - // - Zero constants case [i+0] and [i-0] is handled. - // - Overflow cases are handled, for example: - // [i+0x80000000] and [i-0x80000000]; - // [i+0x10] and [i-0xFFFFFFF0]. - // - Other cases [i+CONST1] and [i-CONST2] without any overflow is handled. - return idx1_cst->AsIntConstant()->GetValue() == -(idx2_cst->AsIntConstant()->GetValue()); + if (!idx1->GetConstantRight()->IsIntConstant() || + !idx2->GetConstantRight()->IsIntConstant()) { + return true; } - // All other cases, MAY alias. - return true; + // Since 'i' are the same in [i+CONST1] and [i+CONST2], + // further compare [CONST1] and [CONST2]. + int64_t l1 = idx1->IsAdd() ? + idx1->GetConstantRight()->AsIntConstant()->GetValue() : + -idx1->GetConstantRight()->AsIntConstant()->GetValue(); + int64_t l2 = idx2->IsAdd() ? + idx2->GetConstantRight()->AsIntConstant()->GetValue() : + -idx2->GetConstantRight()->AsIntConstant()->GetValue(); + int64_t h1 = l1 + (vector_length1 - 1); + int64_t h2 = l2 + (vector_length2 - 1); + return CanIntegerRangesOverlap(l1, h1, l2, h2); } -// The following array index cases are handled: -// [i] and [i] -// [CONST1] and [CONST2] -// [i] and [i+CONST] -// [i] and [i-CONST] -// [i+CONST1] and [i+CONST2] -// [i-CONST1] and [i-CONST2] -// [i+CONST1] and [i-CONST2] -// [i-CONST1] and [i+CONST2] -// For other complicated cases, we rely on other passes like GVN and simpilfier -// to optimize these cases before this pass. -// For example: [i+j+k+10] and [i+k+10+j] shall be optimized to [i7+10] and [i7+10]. -bool HeapLocationCollector::CanArrayIndicesAlias(const HInstruction* idx1, - const HInstruction* idx2) const { +bool HeapLocationCollector::CanArrayElementsAlias(const HInstruction* idx1, + const size_t vector_length1, + const HInstruction* idx2, + const size_t vector_length2) const { DCHECK(idx1 != nullptr); DCHECK(idx2 != nullptr); + DCHECK_GE(vector_length1, HeapLocation::kScalar); + DCHECK_GE(vector_length2, HeapLocation::kScalar); + // [i] and [i]. if (idx1 == idx2) { - // [i] and [i] return true; } - if (idx1->IsIntConstant() && idx2->IsIntConstant()) { - // [CONST1] and [CONST2] - return idx1->AsIntConstant()->GetValue() == idx2->AsIntConstant()->GetValue(); - } - - if (idx1->IsBinaryOperation() && !BinaryOpAndIndexMayAlias(idx1->AsBinaryOperation(), idx2)) { - // [i] and [i+/-CONST] - return false; - } - if (idx2->IsBinaryOperation() && !BinaryOpAndIndexMayAlias(idx2->AsBinaryOperation(), idx1)) { - // [i+/-CONST] and [i] - return false; - } - if (idx1->IsBinaryOperation() && idx2->IsBinaryOperation()) { - // [i+/-CONST1] and [i+/-CONST2] - if (!BinaryOpsMayAlias(idx1->AsBinaryOperation(), idx2->AsBinaryOperation())) { - return false; - } + // [CONST1] and [CONST2]. + if (idx1->IsIntConstant() && idx2->IsIntConstant()) { + int64_t l1 = idx1->AsIntConstant()->GetValue(); + int64_t l2 = idx2->AsIntConstant()->GetValue(); + // To avoid any overflow in following CONST+vector_length calculation, + // use int64_t instead of int32_t. + int64_t h1 = l1 + (vector_length1 - 1); + int64_t h2 = l2 + (vector_length2 - 1); + return CanIntegerRangesOverlap(l1, h1, l2, h2); + } + + // [i+CONST] and [i]. + if (idx1->IsBinaryOperation() && + idx1->AsBinaryOperation()->GetConstantRight() != nullptr && + idx1->AsBinaryOperation()->GetLeastConstantLeft() == idx2) { + return CanBinaryOpAndIndexAlias(idx1->AsBinaryOperation(), + vector_length1, + idx2, + vector_length2); + } + + // [i] and [i+CONST]. + if (idx2->IsBinaryOperation() && + idx2->AsBinaryOperation()->GetConstantRight() != nullptr && + idx2->AsBinaryOperation()->GetLeastConstantLeft() == idx1) { + return CanBinaryOpAndIndexAlias(idx2->AsBinaryOperation(), + vector_length2, + idx1, + vector_length1); + } + + // [i+CONST1] and [i+CONST2]. + if (idx1->IsBinaryOperation() && + idx1->AsBinaryOperation()->GetConstantRight() != nullptr && + idx2->IsBinaryOperation() && + idx2->AsBinaryOperation()->GetConstantRight() != nullptr) { + return CanBinaryOpsAlias(idx1->AsBinaryOperation(), + vector_length1, + idx2->AsBinaryOperation(), + vector_length2); } // By default, MAY alias. diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h index 5a1df45914..999026cb6a 100644 --- a/compiler/optimizing/load_store_analysis.h +++ b/compiler/optimizing/load_store_analysis.h @@ -102,23 +102,26 @@ class ReferenceInfo : public ArenaObject { class HeapLocation : public ArenaObject { public: static constexpr size_t kInvalidFieldOffset = -1; - + // Default value for heap locations which are not vector data. + static constexpr size_t kScalar = 1; // TODO: more fine-grained array types. static constexpr int16_t kDeclaringClassDefIndexForArrays = -1; HeapLocation(ReferenceInfo* ref_info, size_t offset, HInstruction* index, + size_t vector_length, int16_t declaring_class_def_index) : ref_info_(ref_info), offset_(offset), index_(index), + vector_length_(vector_length), declaring_class_def_index_(declaring_class_def_index), value_killed_by_loop_side_effects_(true) { DCHECK(ref_info != nullptr); DCHECK((offset == kInvalidFieldOffset && index != nullptr) || (offset != kInvalidFieldOffset && index == nullptr)); - if (ref_info->IsSingleton() && !IsArrayElement()) { + if (ref_info->IsSingleton() && !IsArray()) { // Assume this location's value cannot be killed by loop side effects // until proven otherwise. value_killed_by_loop_side_effects_ = false; @@ -128,6 +131,7 @@ class HeapLocation : public ArenaObject { ReferenceInfo* GetReferenceInfo() const { return ref_info_; } size_t GetOffset() const { return offset_; } HInstruction* GetIndex() const { return index_; } + size_t GetVectorLength() const { return vector_length_; } // Returns the definition of declaring class' dex index. // It's kDeclaringClassDefIndexForArrays for an array element. @@ -135,7 +139,7 @@ class HeapLocation : public ArenaObject { return declaring_class_def_index_; } - bool IsArrayElement() const { + bool IsArray() const { return index_ != nullptr; } @@ -148,15 +152,26 @@ class HeapLocation : public ArenaObject { } private: - ReferenceInfo* const ref_info_; // reference for instance/static field or array access. - const size_t offset_; // offset of static/instance field. - HInstruction* const index_; // index of an array element. - const int16_t declaring_class_def_index_; // declaring class's def's dex index. - bool value_killed_by_loop_side_effects_; // value of this location may be killed by loop - // side effects because this location is stored - // into inside a loop. This gives - // better info on whether a singleton's location - // value may be killed by loop side effects. + // Reference for instance/static field, array element or vector data. + ReferenceInfo* const ref_info_; + // Offset of static/instance field. + // Invalid when this HeapLocation is not field. + const size_t offset_; + // Index of an array element or starting index of vector data. + // Invalid when this HeapLocation is not array. + HInstruction* const index_; + // Vector length of vector data. + // When this HeapLocation is not vector data, it's value is kScalar. + const size_t vector_length_; + // Declaring class's def's dex index. + // Invalid when this HeapLocation is not field access. + const int16_t declaring_class_def_index_; + + // Value of this location may be killed by loop side effects + // because this location is stored into inside a loop. + // This gives better info on whether a singleton's location + // value may be killed by loop side effects. + bool value_killed_by_loop_side_effects_; DISALLOW_COPY_AND_ASSIGN(HeapLocation); }; @@ -218,14 +233,26 @@ class HeapLocationCollector : public HGraphVisitor { return nullptr; } - size_t GetArrayAccessHeapLocation(HInstruction* array, HInstruction* index) const { + size_t GetFieldHeapLocation(HInstruction* object, const FieldInfo* field) const { + DCHECK(object != nullptr); + DCHECK(field != nullptr); + return FindHeapLocationIndex(FindReferenceInfoOf(HuntForOriginalReference(object)), + 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); - HInstruction* original_ref = HuntForOriginalReference(array); - ReferenceInfo* ref_info = FindReferenceInfoOf(original_ref); - return FindHeapLocationIndex(ref_info, + DCHECK_GE(vector_length, HeapLocation::kScalar); + return FindHeapLocationIndex(FindReferenceInfoOf(HuntForOriginalReference(array)), HeapLocation::kInvalidFieldOffset, index, + vector_length, HeapLocation::kDeclaringClassDefIndexForArrays); } @@ -242,15 +269,26 @@ class HeapLocationCollector : public HGraphVisitor { } // Find and return the heap location index in heap_locations_. + // NOTE: When heap locations are created, potentially aliasing/overlapping + // accesses are given different indexes. This find function also + // doesn't take aliasing/overlapping into account. For example, + // this function returns three different indexes for: + // - ref_info=array, index=i, vector_length=kScalar; + // - ref_info=array, index=i, vector_length=2; + // - ref_info=array, index=i, vector_length=4; + // In later analysis, ComputeMayAlias() and MayAlias() compute and tell whether + // these indexes alias. size_t FindHeapLocationIndex(ReferenceInfo* ref_info, size_t offset, HInstruction* index, + size_t vector_length, int16_t declaring_class_def_index) const { for (size_t i = 0; i < heap_locations_.size(); i++) { HeapLocation* loc = heap_locations_[i]; if (loc->GetReferenceInfo() == ref_info && loc->GetOffset() == offset && loc->GetIndex() == index && + loc->GetVectorLength() == vector_length && loc->GetDeclaringClassDefIndex() == declaring_class_def_index) { return i; } @@ -315,7 +353,10 @@ class HeapLocationCollector : public HGraphVisitor { return true; } - bool CanArrayIndicesAlias(const HInstruction* i1, const HInstruction* i2) const; + bool CanArrayElementsAlias(const HInstruction* idx1, + const size_t vector_length1, + const HInstruction* idx2, + const size_t vector_length2) const; // `index1` and `index2` are indices in the array of collected heap locations. // Returns the position in the bit vector that tracks whether the two heap @@ -340,7 +381,7 @@ class HeapLocationCollector : public HGraphVisitor { HeapLocation* loc2 = heap_locations_[index2]; if (loc1->GetOffset() != loc2->GetOffset()) { // Either two different instance fields, or one is an instance - // field and the other is an array element. + // field and the other is an array data. return false; } if (loc1->GetDeclaringClassDefIndex() != loc2->GetDeclaringClassDefIndex()) { @@ -350,10 +391,12 @@ class HeapLocationCollector : public HGraphVisitor { if (!CanReferencesAlias(loc1->GetReferenceInfo(), loc2->GetReferenceInfo())) { return false; } - if (loc1->IsArrayElement() && loc2->IsArrayElement()) { - HInstruction* array_index1 = loc1->GetIndex(); - HInstruction* array_index2 = loc2->GetIndex(); - if (!CanArrayIndicesAlias(array_index1, array_index2)) { + if (loc1->IsArray() && loc2->IsArray()) { + HInstruction* idx1 = loc1->GetIndex(); + HInstruction* idx2 = loc2->GetIndex(); + size_t vector_length1 = loc1->GetVectorLength(); + size_t vector_length2 = loc2->GetVectorLength(); + if (!CanArrayElementsAlias(idx1, vector_length1, idx2, vector_length2)) { return false; } ReferenceInfo* ref_info = loc1->GetReferenceInfo(); @@ -383,14 +426,15 @@ class HeapLocationCollector : public HGraphVisitor { HeapLocation* GetOrCreateHeapLocation(HInstruction* ref, size_t offset, HInstruction* index, + size_t vector_length, int16_t declaring_class_def_index) { HInstruction* original_ref = HuntForOriginalReference(ref); ReferenceInfo* ref_info = GetOrCreateReferenceInfo(original_ref); size_t heap_location_idx = FindHeapLocationIndex( - ref_info, offset, index, declaring_class_def_index); + ref_info, offset, index, vector_length, declaring_class_def_index); if (heap_location_idx == kHeapLocationNotFound) { HeapLocation* heap_loc = new (GetGraph()->GetAllocator()) - HeapLocation(ref_info, offset, index, declaring_class_def_index); + HeapLocation(ref_info, offset, index, vector_length, declaring_class_def_index); heap_locations_.push_back(heap_loc); return heap_loc; } @@ -403,12 +447,19 @@ class HeapLocationCollector : public HGraphVisitor { } const uint16_t declaring_class_def_index = field_info.GetDeclaringClassDefIndex(); const size_t offset = field_info.GetFieldOffset().SizeValue(); - return GetOrCreateHeapLocation(ref, offset, nullptr, declaring_class_def_index); + return GetOrCreateHeapLocation(ref, + offset, + nullptr, + HeapLocation::kScalar, + declaring_class_def_index); } - void VisitArrayAccess(HInstruction* array, HInstruction* index) { - GetOrCreateHeapLocation(array, HeapLocation::kInvalidFieldOffset, - index, HeapLocation::kDeclaringClassDefIndexForArrays); + void VisitArrayAccess(HInstruction* array, HInstruction* index, size_t vector_length) { + GetOrCreateHeapLocation(array, + HeapLocation::kInvalidFieldOffset, + index, + vector_length, + HeapLocation::kDeclaringClassDefIndexForArrays); } void VisitInstanceFieldGet(HInstanceFieldGet* instruction) OVERRIDE { @@ -456,12 +507,30 @@ class HeapLocationCollector : public HGraphVisitor { // since we cannot accurately track the fields. void VisitArrayGet(HArrayGet* instruction) OVERRIDE { - VisitArrayAccess(instruction->InputAt(0), instruction->InputAt(1)); + HInstruction* array = instruction->InputAt(0); + HInstruction* index = instruction->InputAt(1); + VisitArrayAccess(array, index, HeapLocation::kScalar); CreateReferenceInfoForReferenceType(instruction); } void VisitArraySet(HArraySet* instruction) OVERRIDE { - VisitArrayAccess(instruction->InputAt(0), instruction->InputAt(1)); + HInstruction* array = instruction->InputAt(0); + HInstruction* index = instruction->InputAt(1); + VisitArrayAccess(array, index, 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()); + CreateReferenceInfoForReferenceType(instruction); + } + + void VisitVecStore(HVecStore* instruction) OVERRIDE { + HInstruction* array = instruction->InputAt(0); + HInstruction* index = instruction->InputAt(1); + VisitArrayAccess(array, index, 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 b41e1e4d00..56361a8c90 100644 --- a/compiler/optimizing/load_store_analysis_test.cc +++ b/compiler/optimizing/load_store_analysis_test.cc @@ -78,11 +78,12 @@ TEST_F(LoadStoreAnalysisTest, ArrayHeapLocations) { // Test queries on HeapLocationCollector's ref info and index records. ReferenceInfo* ref = heap_location_collector.FindReferenceInfoOf(array); - size_t field_off = HeapLocation::kInvalidFieldOffset; + 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_off, c1, class_def); - size_t loc2 = heap_location_collector.FindHeapLocationIndex(ref, field_off, c2, class_def); - size_t loc3 = heap_location_collector.FindHeapLocationIndex(ref, field_off, index, class_def); + 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); // must find this reference info for array in HeapLocationCollector. ASSERT_TRUE(ref != nullptr); // must find these heap locations; @@ -167,10 +168,8 @@ TEST_F(LoadStoreAnalysisTest, FieldHeapLocations) { // Test queries on HeapLocationCollector's ref info and index records. ReferenceInfo* ref = heap_location_collector.FindReferenceInfoOf(object); - size_t loc1 = heap_location_collector.FindHeapLocationIndex( - ref, 10, nullptr, kUnknownClassDefIndex); - size_t loc2 = heap_location_collector.FindHeapLocationIndex( - ref, 20, nullptr, kUnknownClassDefIndex); + size_t loc1 = heap_location_collector.GetFieldHeapLocation(object, &get_field10->GetFieldInfo()); + size_t loc2 = heap_location_collector.GetFieldHeapLocation(object, &get_field20->GetFieldInfo()); // must find references info for object and in HeapLocationCollector. ASSERT_TRUE(ref != nullptr); // must find these heap locations. @@ -247,31 +246,236 @@ TEST_F(LoadStoreAnalysisTest, ArrayIndexAliasingTest) { size_t loc2 = HeapLocationCollector::kHeapLocationNotFound; // Test alias: array[0] and array[1] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, c0); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, c1); + loc1 = heap_location_collector.GetArrayHeapLocation(array, c0); + loc2 = heap_location_collector.GetArrayHeapLocation(array, c1); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+0] and array[i-0] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add0); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub0); + loc1 = heap_location_collector.GetArrayHeapLocation(array, add0); + loc2 = heap_location_collector.GetArrayHeapLocation(array, sub0); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+1] and array[i-1] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add1); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub1); + loc1 = heap_location_collector.GetArrayHeapLocation(array, add1); + loc2 = heap_location_collector.GetArrayHeapLocation(array, sub1); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+1] and array[1-i] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add1); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, rev_sub1); + loc1 = heap_location_collector.GetArrayHeapLocation(array, add1); + loc2 = heap_location_collector.GetArrayHeapLocation(array, rev_sub1); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+1] and array[i-(-1)] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add1); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_neg1); + loc1 = heap_location_collector.GetArrayHeapLocation(array, add1); + loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_neg1); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); } +TEST_F(LoadStoreAnalysisTest, ArrayAliasingTest) { + HBasicBlock* entry = new (GetAllocator()) HBasicBlock(graph_); + graph_->AddBlock(entry); + graph_->SetEntryBlock(entry); + graph_->BuildDominatorTree(); + + HInstruction* array = new (GetAllocator()) HParameterValue( + graph_->GetDexFile(), dex::TypeIndex(0), 0, DataType::Type::kReference); + HInstruction* index = new (GetAllocator()) HParameterValue( + graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kInt32); + HInstruction* c0 = graph_->GetIntConstant(0); + HInstruction* c1 = graph_->GetIntConstant(1); + HInstruction* c6 = graph_->GetIntConstant(6); + HInstruction* c8 = graph_->GetIntConstant(8); + + HInstruction* arr_set_0 = new (GetAllocator()) HArraySet(array, + c0, + c0, + DataType::Type::kInt32, + 0); + HInstruction* arr_set_1 = new (GetAllocator()) HArraySet(array, + c1, + c0, + DataType::Type::kInt32, + 0); + HInstruction* arr_set_i = new (GetAllocator()) HArraySet(array, + index, + c0, + DataType::Type::kInt32, + 0); + + HVecOperation* v1 = new (GetAllocator()) HVecReplicateScalar(GetAllocator(), + c1, + DataType::Type::kInt32, + 4, + kNoDexPc); + HVecOperation* v2 = new (GetAllocator()) HVecReplicateScalar(GetAllocator(), + c1, + DataType::Type::kInt32, + 2, + kNoDexPc); + HInstruction* i_add6 = new (GetAllocator()) HAdd(DataType::Type::kInt32, index, c6); + HInstruction* i_add8 = new (GetAllocator()) HAdd(DataType::Type::kInt32, index, c8); + + HInstruction* vstore_0 = new (GetAllocator()) HVecStore( + GetAllocator(), + array, + c0, + v1, + DataType::Type::kInt32, + SideEffects::ArrayWriteOfType(DataType::Type::kInt32), + 4, + kNoDexPc); + HInstruction* vstore_1 = new (GetAllocator()) HVecStore( + GetAllocator(), + array, + c1, + v1, + DataType::Type::kInt32, + SideEffects::ArrayWriteOfType(DataType::Type::kInt32), + 4, + kNoDexPc); + HInstruction* vstore_8 = new (GetAllocator()) HVecStore( + GetAllocator(), + array, + c8, + v1, + DataType::Type::kInt32, + SideEffects::ArrayWriteOfType(DataType::Type::kInt32), + 4, + kNoDexPc); + HInstruction* vstore_i = new (GetAllocator()) HVecStore( + GetAllocator(), + array, + index, + v1, + DataType::Type::kInt32, + SideEffects::ArrayWriteOfType(DataType::Type::kInt32), + 4, + kNoDexPc); + HInstruction* vstore_i_add6 = new (GetAllocator()) HVecStore( + GetAllocator(), + array, + i_add6, + v1, + DataType::Type::kInt32, + SideEffects::ArrayWriteOfType(DataType::Type::kInt32), + 4, + kNoDexPc); + HInstruction* vstore_i_add8 = new (GetAllocator()) HVecStore( + GetAllocator(), + array, + i_add8, + v1, + DataType::Type::kInt32, + SideEffects::ArrayWriteOfType(DataType::Type::kInt32), + 4, + kNoDexPc); + HInstruction* vstore_i_add6_vlen2 = new (GetAllocator()) HVecStore( + GetAllocator(), + array, + i_add6, + v2, + DataType::Type::kInt32, + SideEffects::ArrayWriteOfType(DataType::Type::kInt32), + 2, + kNoDexPc); + + entry->AddInstruction(array); + entry->AddInstruction(index); + + entry->AddInstruction(arr_set_0); + entry->AddInstruction(arr_set_1); + entry->AddInstruction(arr_set_i); + entry->AddInstruction(v1); + entry->AddInstruction(v2); + entry->AddInstruction(i_add6); + entry->AddInstruction(i_add8); + entry->AddInstruction(vstore_0); + entry->AddInstruction(vstore_1); + entry->AddInstruction(vstore_8); + entry->AddInstruction(vstore_i); + entry->AddInstruction(vstore_i_add6); + entry->AddInstruction(vstore_i_add8); + entry->AddInstruction(vstore_i_add6_vlen2); + + LoadStoreAnalysis lsa(graph_); + lsa.Run(); + const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector(); + + // LSA/HeapLocationCollector should see those instructions. + ASSERT_EQ(heap_location_collector.GetNumberOfHeapLocations(), 10U); + ASSERT_TRUE(heap_location_collector.HasHeapStores()); + + // Test queries on HeapLocationCollector's aliasing matrix after load store analysis. + 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); + ASSERT_TRUE(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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); +} + TEST_F(LoadStoreAnalysisTest, ArrayIndexCalculationOverflowTest) { HBasicBlock* entry = new (GetAllocator()) HBasicBlock(graph_); graph_->AddBlock(entry); @@ -359,33 +563,33 @@ TEST_F(LoadStoreAnalysisTest, ArrayIndexCalculationOverflowTest) { size_t loc2 = HeapLocationCollector::kHeapLocationNotFound; // Test alias: array[i+0x80000000] and array[i-0x80000000] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add_0x80000000); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0x80000000); + loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0x80000000); + loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000000); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+0x10] and array[i-0xFFFFFFF0] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add_0x10); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0xFFFFFFF0); + loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0x10); + loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0xFFFFFFF0); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+0x7FFFFFFF] and array[i-0x80000001] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add_0x7FFFFFFF); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0x80000001); + loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0x7FFFFFFF); + loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000001); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+0] and array[i-0] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add_0); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0); + loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0); + loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Should not alias: - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0x80000000); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0x80000001); + loc1 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000000); + loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000001); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Should not alias: - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add_0); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0x80000000); + loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0); + loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000000); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); } @@ -443,10 +647,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.GetArrayAccessHeapLocation(array, c1); - size_t loc2 = heap_location_collector.GetArrayAccessHeapLocation(bound_type, c1); - size_t loc3 = heap_location_collector.GetArrayAccessHeapLocation(null_check, c1); - size_t loc4 = heap_location_collector.GetArrayAccessHeapLocation(inter_addr, c1); + 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); 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 c89961353b..8678fab655 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -302,11 +302,12 @@ class LSEVisitor : public HGraphDelegateVisitor { 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, declaring_class_def_index); + ref_info, offset, index, vector_length, declaring_class_def_index); DCHECK_NE(idx, HeapLocationCollector::kHeapLocationNotFound); ScopedArenaVector& heap_values = heap_values_for_[instruction->GetBlock()->GetBlockId()]; @@ -367,12 +368,13 @@ class LSEVisitor : public HGraphDelegateVisitor { HInstruction* ref, size_t offset, HInstruction* index, + size_t vector_length, int16_t declaring_class_def_index, HInstruction* 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, declaring_class_def_index); + ref_info, offset, index, vector_length, declaring_class_def_index); DCHECK_NE(idx, HeapLocationCollector::kHeapLocationNotFound); ScopedArenaVector& heap_values = heap_values_for_[instruction->GetBlock()->GetBlockId()]; @@ -446,7 +448,12 @@ class LSEVisitor : public HGraphDelegateVisitor { 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, declaring_class_def_index); + VisitGetLocation(instruction, + obj, + offset, + nullptr, + HeapLocation::kScalar, + declaring_class_def_index); } void VisitInstanceFieldSet(HInstanceFieldSet* instruction) OVERRIDE { @@ -454,14 +461,25 @@ class LSEVisitor : public HGraphDelegateVisitor { size_t offset = instruction->GetFieldInfo().GetFieldOffset().SizeValue(); int16_t declaring_class_def_index = instruction->GetFieldInfo().GetDeclaringClassDefIndex(); HInstruction* value = instruction->InputAt(1); - VisitSetLocation(instruction, obj, offset, nullptr, declaring_class_def_index, value); + VisitSetLocation(instruction, + obj, + offset, + nullptr, + HeapLocation::kScalar, + declaring_class_def_index, + 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, declaring_class_def_index); + VisitGetLocation(instruction, + cls, + offset, + nullptr, + HeapLocation::kScalar, + declaring_class_def_index); } void VisitStaticFieldSet(HStaticFieldSet* instruction) OVERRIDE { @@ -469,7 +487,13 @@ class LSEVisitor : public HGraphDelegateVisitor { 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, declaring_class_def_index, value); + VisitSetLocation(instruction, + cls, + offset, + nullptr, + HeapLocation::kScalar, + declaring_class_def_index, + value); } void VisitArrayGet(HArrayGet* instruction) OVERRIDE { @@ -479,6 +503,7 @@ class LSEVisitor : public HGraphDelegateVisitor { array, HeapLocation::kInvalidFieldOffset, index, + HeapLocation::kScalar, HeapLocation::kDeclaringClassDefIndexForArrays); } @@ -490,6 +515,7 @@ class LSEVisitor : public HGraphDelegateVisitor { array, HeapLocation::kInvalidFieldOffset, index, + HeapLocation::kScalar, HeapLocation::kDeclaringClassDefIndexForArrays, value); } diff --git a/compiler/optimizing/scheduler.cc b/compiler/optimizing/scheduler.cc index 8cc376c3a6..bb28d50b56 100644 --- a/compiler/optimizing/scheduler.cc +++ b/compiler/optimizing/scheduler.cc @@ -72,7 +72,7 @@ static bool MayHaveReorderingDependency(SideEffects node, SideEffects other) { size_t SchedulingGraph::ArrayAccessHeapLocation(HInstruction* array, HInstruction* index) const { DCHECK(heap_location_collector_ != nullptr); - size_t heap_loc = heap_location_collector_->GetArrayAccessHeapLocation(array, index); + size_t heap_loc = heap_location_collector_->GetArrayHeapLocation(array, index); // This array access should be analyzed and added to HeapLocationCollector before. DCHECK(heap_loc != HeapLocationCollector::kHeapLocationNotFound); return heap_loc; @@ -153,12 +153,7 @@ size_t SchedulingGraph::FieldAccessHeapLocation(HInstruction* obj, const FieldIn DCHECK(field != nullptr); DCHECK(heap_location_collector_ != nullptr); - size_t heap_loc = heap_location_collector_->FindHeapLocationIndex( - heap_location_collector_->FindReferenceInfoOf( - heap_location_collector_->HuntForOriginalReference(obj)), - field->GetFieldOffset().SizeValue(), - nullptr, - field->GetDeclaringClassDefIndex()); + size_t heap_loc = heap_location_collector_->GetFieldHeapLocation(obj, field); // This field access should be analyzed and added to HeapLocationCollector before. DCHECK(heap_loc != HeapLocationCollector::kHeapLocationNotFound); diff --git a/compiler/optimizing/scheduler_test.cc b/compiler/optimizing/scheduler_test.cc index 75dce81550..104ebc79c2 100644 --- a/compiler/optimizing/scheduler_test.cc +++ b/compiler/optimizing/scheduler_test.cc @@ -294,38 +294,38 @@ class SchedulerTest : public OptimizingUnitTest { size_t loc2 = HeapLocationCollector::kHeapLocationNotFound; // Test side effect dependency: array[0] and array[1] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(arr, c0); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(arr, c1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr, c0); + loc2 = heap_location_collector.GetArrayHeapLocation(arr, c1); 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.GetArrayAccessHeapLocation(arr, i); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(arr, j); + loc1 = heap_location_collector.GetArrayHeapLocation(arr, i); + loc2 = heap_location_collector.GetArrayHeapLocation(arr, 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.GetArrayAccessHeapLocation(arr, i); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(arr, add0); + loc1 = heap_location_collector.GetArrayHeapLocation(arr, i); + loc2 = heap_location_collector.GetArrayHeapLocation(arr, 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.GetArrayAccessHeapLocation(arr, i); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(arr, sub0); + loc1 = heap_location_collector.GetArrayHeapLocation(arr, i); + loc2 = heap_location_collector.GetArrayHeapLocation(arr, 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.GetArrayAccessHeapLocation(arr, i); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(arr, add1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr, i); + loc2 = heap_location_collector.GetArrayHeapLocation(arr, 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.GetArrayAccessHeapLocation(arr, add1); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(arr, sub1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr, add1); + loc2 = heap_location_collector.GetArrayHeapLocation(arr, sub1); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_sub1, arr_set_add1)); -- GitLab From 69147f165efaa9da152bb37da3a16dd5d8c6cf3c Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 6 Nov 2017 20:02:24 -0800 Subject: [PATCH 014/226] Add code item accessor helper classes Add classes to abstract accesses to code item data. These classes handle both standard dex and compact dex. Added: - CodeItemInstructionsAccessor to handle code item instructions. - CodeItemDataAccessor to handle code item data excluding debug info. Moved inline_method_analyzer to use the new classes to test the new APIs. Bug: 63756964 Test: test-art-host Change-Id: I9926acb77b81fa64ed4a3b49b7bed1aab30a0f33 --- compiler/dex/inline_method_analyser.cc | 104 ++++++++++---------- compiler/dex/inline_method_analyser.h | 12 ++- dex2oat/dex2oat_test.cc | 2 +- runtime/Android.bp | 1 + runtime/art_method-inl.h | 13 +++ runtime/art_method.h | 11 +++ runtime/base/casts.h | 8 ++ runtime/cdex/compact_dex_file.h | 23 +++-- runtime/code_item_accessors-inl.h | 130 +++++++++++++++++++++++++ runtime/code_item_accessors.h | 122 +++++++++++++++++++++++ runtime/code_item_accessors_test.cc | 108 ++++++++++++++++++++ runtime/dex_file-inl.h | 12 +++ runtime/dex_file.cc | 6 +- runtime/dex_file.h | 20 ++-- runtime/jit/profiling_info.cc | 3 +- runtime/standard_dex_file.cc | 10 ++ runtime/standard_dex_file.h | 24 ++++- 17 files changed, 528 insertions(+), 81 deletions(-) create mode 100644 runtime/code_item_accessors-inl.h create mode 100644 runtime/code_item_accessors.h create mode 100644 runtime/code_item_accessors_test.cc diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc index 518b0ece73..b409eb2dbb 100644 --- a/compiler/dex/inline_method_analyser.cc +++ b/compiler/dex/inline_method_analyser.cc @@ -20,6 +20,7 @@ #include "art_method-inl.h" #include "base/enums.h" #include "class_linker-inl.h" +#include "code_item_accessors-inl.h" #include "dex_file-inl.h" #include "dex_instruction-inl.h" #include "dex_instruction.h" @@ -43,7 +44,7 @@ class Matcher { typedef bool MatchFn(Matcher* matcher); template - static bool Match(const DexFile::CodeItem* code_item, MatchFn* const (&pattern)[size]); + static bool Match(const CodeItemDataAccessor* code_item, MatchFn* const (&pattern)[size]); // Match and advance. @@ -62,22 +63,20 @@ class Matcher { bool IPutOnThis(); private: - explicit Matcher(const DexFile::CodeItem* code_item) + explicit Matcher(const CodeItemDataAccessor* code_item) : code_item_(code_item), - instruction_(code_item->Instructions().begin()), - pos_(0u), - mark_(0u) { } + instruction_(code_item->begin()) {} - static bool DoMatch(const DexFile::CodeItem* code_item, MatchFn* const* pattern, size_t size); + static bool DoMatch(const CodeItemDataAccessor* code_item, MatchFn* const* pattern, size_t size); - const DexFile::CodeItem* const code_item_; + const CodeItemDataAccessor* const code_item_; DexInstructionIterator instruction_; - size_t pos_; - size_t mark_; + size_t pos_ = 0u; + size_t mark_ = 0u; }; template -bool Matcher::Match(const DexFile::CodeItem* code_item, MatchFn* const (&pattern)[size]) { +bool Matcher::Match(const CodeItemDataAccessor* code_item, MatchFn* const (&pattern)[size]) { return DoMatch(code_item, pattern, size); } @@ -122,12 +121,12 @@ bool Matcher::Const0() { } bool Matcher::IPutOnThis() { - DCHECK_NE(code_item_->ins_size_, 0u); + DCHECK_NE(code_item_->InsSize(), 0u); return IsInstructionIPut(instruction_->Opcode()) && - instruction_->VRegB_22c() == code_item_->registers_size_ - code_item_->ins_size_; + instruction_->VRegB_22c() == code_item_->RegistersSize() - code_item_->InsSize(); } -bool Matcher::DoMatch(const DexFile::CodeItem* code_item, MatchFn* const* pattern, size_t size) { +bool Matcher::DoMatch(const CodeItemDataAccessor* code_item, MatchFn* const* pattern, size_t size) { Matcher matcher(code_item); while (matcher.pos_ != size) { if (!pattern[matcher.pos_](&matcher)) { @@ -158,7 +157,7 @@ ArtMethod* GetTargetConstructor(ArtMethod* method, const Instruction* invoke_dir // Return the forwarded arguments and check that all remaining arguments are zero. // If the check fails, return static_cast(-1). -size_t CountForwardedConstructorArguments(const DexFile::CodeItem* code_item, +size_t CountForwardedConstructorArguments(const CodeItemDataAccessor* code_item, const Instruction* invoke_direct, uint16_t zero_vreg_mask) { DCHECK_EQ(invoke_direct->Opcode(), Instruction::INVOKE_DIRECT); @@ -167,7 +166,7 @@ size_t CountForwardedConstructorArguments(const DexFile::CodeItem* code_item, uint32_t args[Instruction::kMaxVarArgRegs]; invoke_direct->GetVarArgs(args); uint16_t this_vreg = args[0]; - DCHECK_EQ(this_vreg, code_item->registers_size_ - code_item->ins_size_); // Checked by verifier. + DCHECK_EQ(this_vreg, code_item->RegistersSize() - code_item->InsSize()); // Checked by verifier. size_t forwarded = 1u; while (forwarded < number_of_args && args[forwarded] == this_vreg + forwarded && @@ -249,7 +248,7 @@ bool RecordConstructorIPut(ArtMethod* method, return true; } -bool DoAnalyseConstructor(const DexFile::CodeItem* code_item, +bool DoAnalyseConstructor(const CodeItemDataAccessor* code_item, ArtMethod* method, /*inout*/ ConstructorIPutData (&iputs)[kMaxConstructorIPuts]) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -292,17 +291,17 @@ bool DoAnalyseConstructor(const DexFile::CodeItem* code_item, DCHECK(method->IsConstructor()); DCHECK(code_item != nullptr); if (!method->GetDeclaringClass()->IsVerified() || - code_item->insns_size_in_code_units_ > kMaxCodeUnits || - code_item->registers_size_ > kMaxVRegs || + code_item->InsnsSizeInCodeUnits() > kMaxCodeUnits || + code_item->RegistersSize() > kMaxVRegs || !Matcher::Match(code_item, kConstructorPattern)) { return false; } // Verify the invoke, prevent a few odd cases and collect IPUTs. - uint16_t this_vreg = code_item->registers_size_ - code_item->ins_size_; + uint16_t this_vreg = code_item->RegistersSize() - code_item->InsSize(); uint16_t zero_vreg_mask = 0u; - for (const DexInstructionPcPair& pair : code_item->Instructions()) { + for (const DexInstructionPcPair& pair : *code_item) { const Instruction& instruction = pair.Inst(); if (instruction.Opcode() == Instruction::RETURN_VOID) { break; @@ -314,7 +313,7 @@ bool DoAnalyseConstructor(const DexFile::CodeItem* code_item, // We allow forwarding constructors only if they pass more arguments // to prevent infinite recursion. if (target_method->GetDeclaringClass() == method->GetDeclaringClass() && - instruction.VRegA_35c() <= code_item->ins_size_) { + instruction.VRegA_35c() <= code_item->InsSize()) { return false; } size_t forwarded = CountForwardedConstructorArguments(code_item, &instruction, zero_vreg_mask); @@ -322,14 +321,13 @@ bool DoAnalyseConstructor(const DexFile::CodeItem* code_item, return false; } if (target_method->GetDeclaringClass()->IsObjectClass()) { - DCHECK_EQ(target_method->GetCodeItem()->Instructions().begin()->Opcode(), - Instruction::RETURN_VOID); + DCHECK_EQ(CodeItemDataAccessor(target_method).begin()->Opcode(), Instruction::RETURN_VOID); } else { - const DexFile::CodeItem* target_code_item = target_method->GetCodeItem(); - if (target_code_item == nullptr) { + CodeItemDataAccessor target_code_item = CodeItemDataAccessor::CreateNullable(target_method); + if (!target_code_item.HasCodeItem()) { return false; // Native constructor? } - if (!DoAnalyseConstructor(target_code_item, target_method, iputs)) { + if (!DoAnalyseConstructor(&target_code_item, target_method, iputs)) { return false; } // Prune IPUTs with zero input. @@ -365,7 +363,7 @@ bool DoAnalyseConstructor(const DexFile::CodeItem* code_item, } // anonymous namespace -bool AnalyseConstructor(const DexFile::CodeItem* code_item, +bool AnalyseConstructor(const CodeItemDataAccessor* code_item, ArtMethod* method, InlineMethod* result) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -429,27 +427,27 @@ static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) == InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT), "iget/iput_short variant"); bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method, InlineMethod* result) { - const DexFile::CodeItem* code_item = method->GetCodeItem(); - if (code_item == nullptr) { + CodeItemDataAccessor code_item = CodeItemDataAccessor::CreateNullable(method); + if (!code_item.HasCodeItem()) { // Native or abstract. return false; } - return AnalyseMethodCode(code_item, + return AnalyseMethodCode(&code_item, MethodReference(method->GetDexFile(), method->GetDexMethodIndex()), method->IsStatic(), method, result); } -bool InlineMethodAnalyser::AnalyseMethodCode(const DexFile::CodeItem* code_item, +bool InlineMethodAnalyser::AnalyseMethodCode(const CodeItemDataAccessor* code_item, const MethodReference& method_ref, bool is_static, ArtMethod* method, InlineMethod* result) { // We currently support only plain return or 2-instruction methods. - DCHECK_NE(code_item->insns_size_in_code_units_, 0u); - Instruction::Code opcode = code_item->Instructions().begin()->Opcode(); + DCHECK_NE(code_item->InsnsSizeInCodeUnits(), 0u); + Instruction::Code opcode = code_item->begin()->Opcode(); switch (opcode) { case Instruction::RETURN_VOID: @@ -518,15 +516,15 @@ bool InlineMethodAnalyser::IsSyntheticAccessor(MethodReference ref) { strncmp(method_name, "-", strlen("-")) == 0; } -bool InlineMethodAnalyser::AnalyseReturnMethod(const DexFile::CodeItem* code_item, +bool InlineMethodAnalyser::AnalyseReturnMethod(const CodeItemDataAccessor* code_item, InlineMethod* result) { - DexInstructionIterator return_instruction = code_item->Instructions().begin(); + DexInstructionIterator return_instruction = code_item->begin(); Instruction::Code return_opcode = return_instruction->Opcode(); uint32_t reg = return_instruction->VRegA_11x(); - uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_; + uint32_t arg_start = code_item->RegistersSize() - code_item->InsSize(); DCHECK_GE(reg, arg_start); DCHECK_LT((return_opcode == Instruction::RETURN_WIDE) ? reg + 1 : reg, - code_item->registers_size_); + code_item->RegistersSize()); if (result != nullptr) { result->opcode = kInlineOpReturnArg; @@ -540,9 +538,9 @@ bool InlineMethodAnalyser::AnalyseReturnMethod(const DexFile::CodeItem* code_ite return true; } -bool InlineMethodAnalyser::AnalyseConstMethod(const DexFile::CodeItem* code_item, +bool InlineMethodAnalyser::AnalyseConstMethod(const CodeItemDataAccessor* code_item, InlineMethod* result) { - DexInstructionIterator instruction = code_item->Instructions().begin(); + DexInstructionIterator instruction = code_item->begin(); const Instruction* return_instruction = instruction->Next(); Instruction::Code return_opcode = return_instruction->Opcode(); if (return_opcode != Instruction::RETURN && @@ -551,13 +549,13 @@ bool InlineMethodAnalyser::AnalyseConstMethod(const DexFile::CodeItem* code_item } int32_t return_reg = return_instruction->VRegA_11x(); - DCHECK_LT(return_reg, code_item->registers_size_); + DCHECK_LT(return_reg, code_item->RegistersSize()); int32_t const_value = instruction->VRegB(); if (instruction->Opcode() == Instruction::CONST_HIGH16) { const_value <<= 16; } - DCHECK_LT(instruction->VRegA(), code_item->registers_size_); + DCHECK_LT(instruction->VRegA(), code_item->RegistersSize()); if (instruction->VRegA() != return_reg) { return false; // Not returning the value set by const? } @@ -571,12 +569,12 @@ bool InlineMethodAnalyser::AnalyseConstMethod(const DexFile::CodeItem* code_item return true; } -bool InlineMethodAnalyser::AnalyseIGetMethod(const DexFile::CodeItem* code_item, +bool InlineMethodAnalyser::AnalyseIGetMethod(const CodeItemDataAccessor* code_item, const MethodReference& method_ref, bool is_static, ArtMethod* method, InlineMethod* result) { - DexInstructionIterator instruction = code_item->Instructions().begin(); + DexInstructionIterator instruction = code_item->begin(); Instruction::Code opcode = instruction->Opcode(); DCHECK(IsInstructionIGet(opcode)); @@ -591,17 +589,17 @@ bool InlineMethodAnalyser::AnalyseIGetMethod(const DexFile::CodeItem* code_item, uint32_t return_reg = return_instruction->VRegA_11x(); DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1 : return_reg, - code_item->registers_size_); + code_item->RegistersSize()); uint32_t dst_reg = instruction->VRegA_22c(); uint32_t object_reg = instruction->VRegB_22c(); uint32_t field_idx = instruction->VRegC_22c(); - uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_; + uint32_t arg_start = code_item->RegistersSize() - code_item->InsSize(); DCHECK_GE(object_reg, arg_start); - DCHECK_LT(object_reg, code_item->registers_size_); + DCHECK_LT(object_reg, code_item->RegistersSize()); uint32_t object_arg = object_reg - arg_start; - DCHECK_LT(opcode == Instruction::IGET_WIDE ? dst_reg + 1 : dst_reg, code_item->registers_size_); + DCHECK_LT(opcode == Instruction::IGET_WIDE ? dst_reg + 1 : dst_reg, code_item->RegistersSize()); if (dst_reg != return_reg) { return false; // Not returning the value retrieved by IGET? } @@ -635,18 +633,18 @@ bool InlineMethodAnalyser::AnalyseIGetMethod(const DexFile::CodeItem* code_item, return true; } -bool InlineMethodAnalyser::AnalyseIPutMethod(const DexFile::CodeItem* code_item, +bool InlineMethodAnalyser::AnalyseIPutMethod(const CodeItemDataAccessor* code_item, const MethodReference& method_ref, bool is_static, ArtMethod* method, InlineMethod* result) { - DexInstructionIterator instruction = code_item->Instructions().begin(); + DexInstructionIterator instruction = code_item->begin(); Instruction::Code opcode = instruction->Opcode(); DCHECK(IsInstructionIPut(opcode)); const Instruction* return_instruction = instruction->Next(); Instruction::Code return_opcode = return_instruction->Opcode(); - uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_; + uint32_t arg_start = code_item->RegistersSize() - code_item->InsSize(); uint16_t return_arg_plus1 = 0u; if (return_opcode != Instruction::RETURN_VOID) { if (return_opcode != Instruction::RETURN && @@ -658,7 +656,7 @@ bool InlineMethodAnalyser::AnalyseIPutMethod(const DexFile::CodeItem* code_item, uint32_t return_reg = return_instruction->VRegA_11x(); DCHECK_GE(return_reg, arg_start); DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1u : return_reg, - code_item->registers_size_); + code_item->RegistersSize()); return_arg_plus1 = return_reg - arg_start + 1u; } @@ -666,9 +664,9 @@ bool InlineMethodAnalyser::AnalyseIPutMethod(const DexFile::CodeItem* code_item, uint32_t object_reg = instruction->VRegB_22c(); uint32_t field_idx = instruction->VRegC_22c(); DCHECK_GE(object_reg, arg_start); - DCHECK_LT(object_reg, code_item->registers_size_); + DCHECK_LT(object_reg, code_item->RegistersSize()); DCHECK_GE(src_reg, arg_start); - DCHECK_LT(opcode == Instruction::IPUT_WIDE ? src_reg + 1 : src_reg, code_item->registers_size_); + DCHECK_LT(opcode == Instruction::IPUT_WIDE ? src_reg + 1 : src_reg, code_item->RegistersSize()); uint32_t object_arg = object_reg - arg_start; uint32_t src_arg = src_reg - arg_start; diff --git a/compiler/dex/inline_method_analyser.h b/compiler/dex/inline_method_analyser.h index a35e97fa11..cde2147995 100644 --- a/compiler/dex/inline_method_analyser.h +++ b/compiler/dex/inline_method_analyser.h @@ -30,6 +30,8 @@ namespace art { +class CodeItemDataAccessor; + namespace verifier { class MethodVerifier; } // namespace verifier @@ -121,21 +123,21 @@ class InlineMethodAnalyser { static bool IsSyntheticAccessor(MethodReference ref); private: - static bool AnalyseMethodCode(const DexFile::CodeItem* code_item, + static bool AnalyseMethodCode(const CodeItemDataAccessor* code_item, const MethodReference& method_ref, bool is_static, ArtMethod* method, InlineMethod* result) REQUIRES_SHARED(Locks::mutator_lock_); - static bool AnalyseReturnMethod(const DexFile::CodeItem* code_item, InlineMethod* result); - static bool AnalyseConstMethod(const DexFile::CodeItem* code_item, InlineMethod* result); - static bool AnalyseIGetMethod(const DexFile::CodeItem* code_item, + static bool AnalyseReturnMethod(const CodeItemDataAccessor* code_item, InlineMethod* result); + static bool AnalyseConstMethod(const CodeItemDataAccessor* code_item, InlineMethod* result); + static bool AnalyseIGetMethod(const CodeItemDataAccessor* code_item, const MethodReference& method_ref, bool is_static, ArtMethod* method, InlineMethod* result) REQUIRES_SHARED(Locks::mutator_lock_); - static bool AnalyseIPutMethod(const DexFile::CodeItem* code_item, + static bool AnalyseIPutMethod(const CodeItemDataAccessor* code_item, const MethodReference& method_ref, bool is_static, ArtMethod* method, diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index afca26db71..c2556aa4a6 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -941,7 +941,7 @@ class Dex2oatUnquickenTest : public Dex2oatTest { class_it.Next()) { if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) { for (const DexInstructionPcPair& inst : - class_it.GetMethodCodeItem()->Instructions()) { + class_it.GetMethodCodeItem()->Instructions()) { ASSERT_FALSE(inst->IsQuickened()); } } diff --git a/runtime/Android.bp b/runtime/Android.bp index e032238324..69e4434fbe 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -567,6 +567,7 @@ art_cc_test { "class_linker_test.cc", "class_loader_context_test.cc", "class_table_test.cc", + "code_item_accessors_test.cc", "compiler_filter_test.cc", "dex_file_test.cc", "dex_file_verifier_test.cc", diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 12b4d16b37..eb16e6eaa2 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -23,6 +23,7 @@ #include "base/callee_save_type.h" #include "base/logging.h" #include "class_linker-inl.h" +#include "code_item_accessors-inl.h" #include "common_throws.h" #include "dex_file-inl.h" #include "dex_file_annotations.h" @@ -457,6 +458,18 @@ inline void ArtMethod::UpdateEntrypoints(const Visitor& visitor, PointerSize poi } } +inline IterationRange ArtMethod::DexInstructions() { + CodeItemInstructionAccessor accessor(this); + return { accessor.begin(), + accessor.end() }; +} + +inline IterationRange ArtMethod::NullableDexInstructions() { + CodeItemInstructionAccessor accessor(CodeItemInstructionAccessor::CreateNullable(this)); + return { accessor.begin(), + accessor.end() }; +} + } // namespace art #endif // ART_RUNTIME_ART_METHOD_INL_H_ diff --git a/runtime/art_method.h b/runtime/art_method.h index 8927481e46..df9b3aa852 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -22,8 +22,10 @@ #include "base/bit_utils.h" #include "base/casts.h" #include "base/enums.h" +#include "base/iteration_range.h" #include "base/logging.h" #include "dex_file.h" +#include "dex_instruction_iterator.h" #include "gc_root.h" #include "modifiers.h" #include "obj_ptr.h" @@ -700,6 +702,15 @@ class ArtMethod FINAL { "ptr_sized_fields_.entry_point_from_quick_compiled_code_"); } + // Returns the dex instructions of the code item for the art method. Must not be called on null + // code items. + ALWAYS_INLINE IterationRange DexInstructions() + REQUIRES_SHARED(Locks::mutator_lock_); + + // Handles a null code item by returning iterators that have a null address. + ALWAYS_INLINE IterationRange NullableDexInstructions() + REQUIRES_SHARED(Locks::mutator_lock_); + protected: // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". // The class we are a part of. diff --git a/runtime/base/casts.h b/runtime/base/casts.h index 0cbabba6df..92c493ace7 100644 --- a/runtime/base/casts.h +++ b/runtime/base/casts.h @@ -77,6 +77,14 @@ inline To down_cast(From* f) { // so we only accept pointers return static_cast(f); } +template // use like this: down_cast(foo); +inline To down_cast(From& f) { // so we only accept references + static_assert(std::is_base_of::type>::value, + "down_cast unsafe as To is not a subtype of From"); + + return static_cast(f); +} + template inline Dest bit_cast(const Source& source) { // Compile time assertion: sizeof(Dest) == sizeof(Source) diff --git a/runtime/cdex/compact_dex_file.h b/runtime/cdex/compact_dex_file.h index 8ab9247125..f17f8cf68a 100644 --- a/runtime/cdex/compact_dex_file.h +++ b/runtime/cdex/compact_dex_file.h @@ -24,11 +24,18 @@ namespace art { // CompactDex is a currently ART internal dex file format that aims to reduce storage/RAM usage. class CompactDexFile : public DexFile { public: + static constexpr uint8_t kDexMagic[kDexMagicSize] = { 'c', 'd', 'e', 'x' }; + static constexpr uint8_t kDexMagicVersion[] = {'0', '0', '1', '\0'}; + class Header : public DexFile::Header { // Same for now. }; - static constexpr uint8_t kDexMagic[kDexMagicSize] = { 'c', 'd', 'e', 'x' }; - static constexpr uint8_t kDexMagicVersion[] = {'0', '0', '1', '\0'}; + + struct CodeItem : public DexFile::CodeItem { + private: + // TODO: Insert compact dex specific fields here. + DISALLOW_COPY_AND_ASSIGN(CodeItem); + }; // Write the compact dex specific magic. static void WriteMagic(uint8_t* magic); @@ -44,10 +51,6 @@ class CompactDexFile : public DexFile { static bool IsVersionValid(const uint8_t* magic); virtual bool IsVersionValid() const OVERRIDE; - bool IsCompactDexFile() const OVERRIDE { - return true; - } - private: // Not supported yet. CompactDexFile(const uint8_t* base, @@ -56,7 +59,13 @@ class CompactDexFile : public DexFile { uint32_t location_checksum, const OatDexFile* oat_dex_file, DexFileContainer* container) - : DexFile(base, size, location, location_checksum, oat_dex_file, container) {} + : DexFile(base, + size, + location, + location_checksum, + oat_dex_file, + container, + /*is_compact_dex*/ true) {} friend class DexFile; friend class DexFileLoader; diff --git a/runtime/code_item_accessors-inl.h b/runtime/code_item_accessors-inl.h new file mode 100644 index 0000000000..61b517526d --- /dev/null +++ b/runtime/code_item_accessors-inl.h @@ -0,0 +1,130 @@ +/* + * 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_CODE_ITEM_ACCESSORS_INL_H_ +#define ART_RUNTIME_CODE_ITEM_ACCESSORS_INL_H_ + +#include "code_item_accessors.h" + +#include "art_method-inl.h" +#include "cdex/compact_dex_file.h" +#include "standard_dex_file.h" + +namespace art { + +inline void CodeItemInstructionAccessor::Init(const CompactDexFile::CodeItem& code_item) { + insns_size_in_code_units_ = code_item.insns_size_in_code_units_; + insns_ = code_item.insns_; +} + +inline void CodeItemInstructionAccessor::Init(const StandardDexFile::CodeItem& code_item) { + insns_size_in_code_units_ = code_item.insns_size_in_code_units_; + insns_ = code_item.insns_; +} + +inline void CodeItemInstructionAccessor::Init(const DexFile* dex_file, + const DexFile::CodeItem* code_item) { + DCHECK(dex_file != nullptr); + DCHECK(code_item != nullptr); + if (dex_file->IsCompactDexFile()) { + Init(down_cast(*code_item)); + } else { + DCHECK(dex_file->IsStandardDexFile()); + Init(down_cast(*code_item)); + } +} + +inline CodeItemInstructionAccessor::CodeItemInstructionAccessor( + const DexFile* dex_file, + const DexFile::CodeItem* code_item) { + Init(dex_file, code_item); +} + +inline CodeItemInstructionAccessor::CodeItemInstructionAccessor(ArtMethod* method) + : CodeItemInstructionAccessor(method->GetDexFile(), method->GetCodeItem()) {} + +inline DexInstructionIterator CodeItemInstructionAccessor::begin() const { + return DexInstructionIterator(insns_, 0u); +} + +inline DexInstructionIterator CodeItemInstructionAccessor::end() const { + return DexInstructionIterator(insns_, insns_size_in_code_units_); +} + +inline CodeItemInstructionAccessor CodeItemInstructionAccessor::CreateNullable( + ArtMethod* method) { + DCHECK(method != nullptr); + CodeItemInstructionAccessor ret; + const DexFile::CodeItem* code_item = method->GetCodeItem(); + if (code_item != nullptr) { + ret.Init(method->GetDexFile(), code_item); + } else { + DCHECK(!ret.HasCodeItem()) << "Should be null initialized"; + } + return ret; +} + +inline void CodeItemDataAccessor::Init(const CompactDexFile::CodeItem& code_item) { + CodeItemInstructionAccessor::Init(code_item); + registers_size_ = code_item.registers_size_; + ins_size_ = code_item.ins_size_; + outs_size_ = code_item.outs_size_; + tries_size_ = code_item.tries_size_; +} + +inline void CodeItemDataAccessor::Init(const StandardDexFile::CodeItem& code_item) { + CodeItemInstructionAccessor::Init(code_item); + registers_size_ = code_item.registers_size_; + ins_size_ = code_item.ins_size_; + outs_size_ = code_item.outs_size_; + tries_size_ = code_item.tries_size_; +} + +inline void CodeItemDataAccessor::Init(const DexFile* dex_file, + const DexFile::CodeItem* code_item) { + DCHECK(dex_file != nullptr); + DCHECK(code_item != nullptr); + if (dex_file->IsCompactDexFile()) { + CodeItemDataAccessor::Init(down_cast(*code_item)); + } else { + DCHECK(dex_file->IsStandardDexFile()); + CodeItemDataAccessor::Init(down_cast(*code_item)); + } +} + +inline CodeItemDataAccessor::CodeItemDataAccessor(const DexFile* dex_file, + const DexFile::CodeItem* code_item) { + Init(dex_file, code_item); +} + +inline CodeItemDataAccessor::CodeItemDataAccessor(ArtMethod* method) + : CodeItemDataAccessor(method->GetDexFile(), method->GetCodeItem()) {} + +inline CodeItemDataAccessor CodeItemDataAccessor::CreateNullable(ArtMethod* method) { + DCHECK(method != nullptr); + CodeItemDataAccessor ret; + const DexFile::CodeItem* code_item = method->GetCodeItem(); + if (code_item != nullptr) { + ret.Init(method->GetDexFile(), code_item); + } else { + DCHECK(!ret.HasCodeItem()) << "Should be null initialized"; + } + return ret; +} + +} // namespace art + +#endif // ART_RUNTIME_CODE_ITEM_ACCESSORS_INL_H_ diff --git a/runtime/code_item_accessors.h b/runtime/code_item_accessors.h new file mode 100644 index 0000000000..fcece3e0ac --- /dev/null +++ b/runtime/code_item_accessors.h @@ -0,0 +1,122 @@ +/* + * 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. + */ + +// TODO: Dex helpers have ART specific APIs, we may want to refactor these for use in dexdump. + +#ifndef ART_RUNTIME_CODE_ITEM_ACCESSORS_H_ +#define ART_RUNTIME_CODE_ITEM_ACCESSORS_H_ + +#include "base/mutex.h" +#include "cdex/compact_dex_file.h" +#include "dex_file.h" +#include "dex_instruction_iterator.h" +#include "standard_dex_file.h" + +namespace art { + +class ArtMethod; + +// Abstracts accesses to the instruction fields of code items for CompactDexFile and +// StandardDexFile. +class CodeItemInstructionAccessor { + public: + ALWAYS_INLINE CodeItemInstructionAccessor(const DexFile* dex_file, + const DexFile::CodeItem* code_item); + + ALWAYS_INLINE explicit CodeItemInstructionAccessor(ArtMethod* method); + + ALWAYS_INLINE DexInstructionIterator begin() const; + + ALWAYS_INLINE DexInstructionIterator end() const; + + uint32_t InsnsSizeInCodeUnits() const { + return insns_size_in_code_units_; + } + + const uint16_t* Insns() const { + return insns_; + } + + // Return true if the accessor has a code item. + bool HasCodeItem() const { + return Insns() != nullptr; + } + + // CreateNullable allows ArtMethods that have a null code item. + ALWAYS_INLINE static CodeItemInstructionAccessor CreateNullable(ArtMethod* method) + REQUIRES_SHARED(Locks::mutator_lock_); + + protected: + CodeItemInstructionAccessor() = default; + + ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item); + ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item); + ALWAYS_INLINE void Init(const DexFile* dex_file, const DexFile::CodeItem* code_item); + + private: + // size of the insns array, in 2 byte code units. 0 if there is no code item. + uint32_t insns_size_in_code_units_ = 0; + + // Pointer to the instructions, null if there is no code item. + const uint16_t* insns_ = 0; +}; + +// Abstracts accesses to code item fields other than debug info for CompactDexFile and +// StandardDexFile. +class CodeItemDataAccessor : public CodeItemInstructionAccessor { + public: + ALWAYS_INLINE CodeItemDataAccessor(const DexFile* dex_file, const DexFile::CodeItem* code_item); + + ALWAYS_INLINE explicit CodeItemDataAccessor(ArtMethod* method); + + uint16_t RegistersSize() const { + return registers_size_; + } + + uint16_t InsSize() const { + return ins_size_; + } + + uint16_t OutsSize() const { + return outs_size_; + } + + uint16_t TriesSize() const { + return tries_size_; + } + + // CreateNullable allows ArtMethods that have a null code item. + ALWAYS_INLINE static CodeItemDataAccessor CreateNullable(ArtMethod* method) + REQUIRES_SHARED(Locks::mutator_lock_); + + protected: + CodeItemDataAccessor() = default; + + ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item); + ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item); + ALWAYS_INLINE void Init(const DexFile* dex_file, const DexFile::CodeItem* code_item); + + private: + // Fields mirrored from the dex/cdex code item. + uint16_t registers_size_; + uint16_t ins_size_; + uint16_t outs_size_; + uint16_t tries_size_; +}; + +} // namespace art + +#endif // ART_RUNTIME_CODE_ITEM_ACCESSORS_H_ diff --git a/runtime/code_item_accessors_test.cc b/runtime/code_item_accessors_test.cc new file mode 100644 index 0000000000..ef5d246a54 --- /dev/null +++ b/runtime/code_item_accessors_test.cc @@ -0,0 +1,108 @@ +/* + * 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. + */ + +#include "code_item_accessors-inl.h" + +#include + +#include "common_runtime_test.h" +#include "dex_file_loader.h" +#include "mem_map.h" + +namespace art { + +class CodeItemAccessorsTest : public CommonRuntimeTest {}; + +std::unique_ptr CreateFakeDex(bool compact_dex) { + std::string error_msg; + std::unique_ptr map( + MemMap::MapAnonymous(/*name*/ "map", + /*addr*/ nullptr, + /*byte_count*/ kPageSize, + PROT_READ | PROT_WRITE, + /*low_4gb*/ false, + /*reuse*/ false, + &error_msg)); + CHECK(map != nullptr) << error_msg; + if (compact_dex) { + CompactDexFile::WriteMagic(map->Begin()); + CompactDexFile::WriteCurrentVersion(map->Begin()); + } else { + StandardDexFile::WriteMagic(map->Begin()); + StandardDexFile::WriteCurrentVersion(map->Begin()); + } + std::unique_ptr dex( + DexFileLoader::Open("location", + /*location_checksum*/ 123, + std::move(map), + /*verify*/false, + /*verify_checksum*/false, + &error_msg)); + CHECK(dex != nullptr) << error_msg; + return dex; +} + +TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor) { + MemMap::Init(); + std::unique_ptr standard_dex(CreateFakeDex(/*compact_dex*/false)); + ASSERT_TRUE(standard_dex != nullptr); + std::unique_ptr compact_dex(CreateFakeDex(/*compact_dex*/true)); + ASSERT_TRUE(compact_dex != nullptr); + static constexpr uint16_t kRegisterSize = 1; + static constexpr uint16_t kInsSize = 2; + static constexpr uint16_t kOutsSize = 3; + static constexpr uint16_t kTriesSize = 4; + // debug_info_off_ is not accessible from the helpers yet. + static constexpr size_t kInsnsSizeInCodeUnits = 5; + + auto verify_code_item = [&](const DexFile* dex, + const DexFile::CodeItem* item, + const uint16_t* insns) { + CodeItemInstructionAccessor insns_accessor(dex, item); + EXPECT_TRUE(insns_accessor.HasCodeItem()); + ASSERT_EQ(insns_accessor.InsnsSizeInCodeUnits(), kInsnsSizeInCodeUnits); + EXPECT_EQ(insns_accessor.Insns(), insns); + + CodeItemDataAccessor data_accessor(dex, item); + EXPECT_TRUE(data_accessor.HasCodeItem()); + EXPECT_EQ(data_accessor.InsnsSizeInCodeUnits(), kInsnsSizeInCodeUnits); + EXPECT_EQ(data_accessor.Insns(), insns); + EXPECT_EQ(data_accessor.RegistersSize(), kRegisterSize); + EXPECT_EQ(data_accessor.InsSize(), kInsSize); + EXPECT_EQ(data_accessor.OutsSize(), kOutsSize); + EXPECT_EQ(data_accessor.TriesSize(), kTriesSize); + }; + + uint8_t buffer1[sizeof(CompactDexFile::CodeItem) + kInsnsSizeInCodeUnits * sizeof(uint16_t)] = {}; + CompactDexFile::CodeItem* dex_code_item = reinterpret_cast(buffer1); + dex_code_item->registers_size_ = kRegisterSize; + dex_code_item->ins_size_ = kInsSize; + dex_code_item->outs_size_ = kOutsSize; + dex_code_item->tries_size_ = kTriesSize; + dex_code_item->insns_size_in_code_units_ = kInsnsSizeInCodeUnits; + verify_code_item(compact_dex.get(), dex_code_item, dex_code_item->insns_); + + uint8_t buffer2[sizeof(CompactDexFile::CodeItem) + kInsnsSizeInCodeUnits * sizeof(uint16_t)] = {}; + CompactDexFile::CodeItem* cdex_code_item = reinterpret_cast(buffer2); + cdex_code_item->registers_size_ = kRegisterSize; + cdex_code_item->ins_size_ = kInsSize; + cdex_code_item->outs_size_ = kOutsSize; + cdex_code_item->tries_size_ = kTriesSize; + cdex_code_item->insns_size_in_code_units_ = kInsnsSizeInCodeUnits; + verify_code_item(compact_dex.get(), cdex_code_item, cdex_code_item->insns_); +} + +} // namespace art diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h index 5dfbd9b6a1..58cd48631d 100644 --- a/runtime/dex_file-inl.h +++ b/runtime/dex_file-inl.h @@ -21,9 +21,11 @@ #include "base/casts.h" #include "base/logging.h" #include "base/stringpiece.h" +#include "cdex/compact_dex_file.h" #include "dex_file.h" #include "invoke_type.h" #include "leb128.h" +#include "standard_dex_file.h" namespace art { @@ -495,6 +497,16 @@ bool DexFile::DecodeDebugPositionInfo(const CodeItem* code_item, context); } +inline const CompactDexFile* DexFile::AsCompactDexFile() const { + DCHECK(IsCompactDexFile()); + return down_cast(this); +} + +inline const StandardDexFile* DexFile::AsStandardDexFile() const { + DCHECK(IsStandardDexFile()); + return down_cast(this); +} + } // namespace art #endif // ART_RUNTIME_DEX_FILE_INL_H_ diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 974c7acbb2..7b0c46bcfe 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -77,7 +77,8 @@ DexFile::DexFile(const uint8_t* base, const std::string& location, uint32_t location_checksum, const OatDexFile* oat_dex_file, - DexFileContainer* container) + DexFileContainer* container, + bool is_compact_dex) : begin_(base), size_(size), location_(location), @@ -94,7 +95,8 @@ DexFile::DexFile(const uint8_t* base, call_site_ids_(nullptr), num_call_site_ids_(0), oat_dex_file_(oat_dex_file), - container_(container) { + container_(container), + is_compact_dex_(is_compact_dex) { CHECK(begin_ != nullptr) << GetLocation(); CHECK_GT(size_, 0U) << GetLocation(); // Check base (=header) alignment. diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 5c9b2585eb..5c0093f323 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -32,10 +32,12 @@ namespace art { +class CompactDexFile; enum InvokeType : uint32_t; class MemMap; class OatDexFile; class Signature; +class StandardDexFile; class StringPiece; class ZipArchive; @@ -993,13 +995,15 @@ class DexFile { // Returns a human-readable form of the type at an index. std::string PrettyType(dex::TypeIndex type_idx) const; - // Helper functions. - virtual bool IsCompactDexFile() const { - return false; + // Not virtual for performance reasons. + ALWAYS_INLINE bool IsCompactDexFile() const { + return is_compact_dex_; } - virtual bool IsStandardDexFile() const { - return false; + ALWAYS_INLINE bool IsStandardDexFile() const { + return !is_compact_dex_; } + ALWAYS_INLINE const StandardDexFile* AsStandardDexFile() const; + ALWAYS_INLINE const CompactDexFile* AsCompactDexFile() const; protected: DexFile(const uint8_t* base, @@ -1007,7 +1011,8 @@ class DexFile { const std::string& location, uint32_t location_checksum, const OatDexFile* oat_dex_file, - DexFileContainer* container); + DexFileContainer* container, + bool is_compact_dex); // Top-level initializer that calls other Init methods. bool Init(std::string* error_msg); @@ -1073,6 +1078,9 @@ class DexFile { // Manages the underlying memory allocation. std::unique_ptr container_; + // 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_; + friend class DexFileLoader; friend class DexFileVerifierTest; friend class OatWriter; diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc index 1344ca05b4..e54a0179e1 100644 --- a/runtime/jit/profiling_info.cc +++ b/runtime/jit/profiling_info.cc @@ -44,8 +44,7 @@ bool ProfilingInfo::Create(Thread* self, ArtMethod* method, bool retry_allocatio DCHECK(!method->IsNative()); std::vector entries; - - for (const DexInstructionPcPair& inst : method->GetCodeItem()->Instructions()) { + for (const DexInstructionPcPair& inst : method->DexInstructions()) { switch (inst->Opcode()) { case Instruction::INVOKE_VIRTUAL: case Instruction::INVOKE_VIRTUAL_RANGE: diff --git a/runtime/standard_dex_file.cc b/runtime/standard_dex_file.cc index 36bb37afe9..4c1d3081d8 100644 --- a/runtime/standard_dex_file.cc +++ b/runtime/standard_dex_file.cc @@ -31,6 +31,16 @@ const uint8_t StandardDexFile::kDexMagicVersions[StandardDexFile::kNumDexVersion {'0', '3', '9', '\0'}, }; +void StandardDexFile::WriteMagic(uint8_t* magic) { + std::copy_n(kDexMagic, kDexMagicSize, magic); +} + +void StandardDexFile::WriteCurrentVersion(uint8_t* magic) { + std::copy_n(kDexMagicVersions[StandardDexFile::kDexVersionLen - 1], + kDexVersionLen, + magic + kDexMagicSize); +} + bool StandardDexFile::IsMagicValid(const uint8_t* magic) { return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0); } diff --git a/runtime/standard_dex_file.h b/runtime/standard_dex_file.h index 784ab31821..5d5359776d 100644 --- a/runtime/standard_dex_file.h +++ b/runtime/standard_dex_file.h @@ -32,6 +32,18 @@ class StandardDexFile : public DexFile { // Same for now. }; + struct CodeItem : public DexFile::CodeItem { + private: + // TODO: Insert standard dex specific fields here. + DISALLOW_COPY_AND_ASSIGN(CodeItem); + }; + + // Write the standard dex specific magic. + static void WriteMagic(uint8_t* magic); + + // Write the current version, note that the input is the address of the magic. + static void WriteCurrentVersion(uint8_t* magic); + static const uint8_t kDexMagic[kDexMagicSize]; static constexpr size_t kNumDexVersions = 4; static const uint8_t kDexMagicVersions[kNumDexVersions][kDexVersionLen]; @@ -44,10 +56,6 @@ class StandardDexFile : public DexFile { static bool IsVersionValid(const uint8_t* magic); virtual bool IsVersionValid() const OVERRIDE; - bool IsStandardDexFile() const OVERRIDE { - return true; - } - private: StandardDexFile(const uint8_t* base, size_t size, @@ -55,7 +63,13 @@ class StandardDexFile : public DexFile { uint32_t location_checksum, const OatDexFile* oat_dex_file, DexFileContainer* container) - : DexFile(base, size, location, location_checksum, oat_dex_file, container) {} + : DexFile(base, + size, + location, + location_checksum, + oat_dex_file, + container, + /*is_compact_dex*/ false) {} friend class DexFileLoader; friend class DexFileVerifierTest; -- GitLab From 27dae5f3ce2d00b84eabf4cc4b7b2144af37a43a Mon Sep 17 00:00:00 2001 From: "xueliang.zhong" Date: Thu, 21 Sep 2017 13:48:55 +0100 Subject: [PATCH 015/226] Support VecLoad and VecStore in LSE. Test: test-art-host Test: test-art-target Test: load_store_elimination_test Change-Id: I95ac13207a0f57225bf0bc62b1e57a629ae24e02 --- compiler/Android.bp | 1 + compiler/optimizing/load_store_elimination.cc | 75 +++- .../optimizing/load_store_elimination_test.cc | 406 ++++++++++++++++++ 3 files changed, 469 insertions(+), 13 deletions(-) create mode 100644 compiler/optimizing/load_store_elimination_test.cc diff --git a/compiler/Android.bp b/compiler/Android.bp index 859947108e..1a2d9aa5e6 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -355,6 +355,7 @@ art_cc_test { "jni/jni_cfi_test.cc", "optimizing/codegen_test.cc", "optimizing/load_store_analysis_test.cc", + "optimizing/load_store_elimination_test.cc", "optimizing/optimizing_cfi_test.cc", "optimizing/scheduler_test.cc", ], diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 8678fab655..66806d8afc 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -83,7 +83,8 @@ class LSEVisitor : public HGraphDelegateVisitor { DCHECK(load != nullptr); DCHECK(load->IsInstanceFieldGet() || load->IsStaticFieldGet() || - load->IsArrayGet()); + load->IsArrayGet() || + load->IsVecLoad()); HInstruction* substitute = substitute_instructions_for_loads_[i]; DCHECK(substitute != nullptr); // Keep tracing substitute till one that's not removed. @@ -98,7 +99,10 @@ class LSEVisitor : public HGraphDelegateVisitor { // At this point, stores in possibly_removed_stores_ can be safely removed. for (HInstruction* store : possibly_removed_stores_) { - DCHECK(store->IsInstanceFieldSet() || store->IsStaticFieldSet() || store->IsArraySet()); + DCHECK(store->IsInstanceFieldSet() || + store->IsStaticFieldSet() || + store->IsArraySet() || + store->IsVecStore()); store->GetBlock()->RemoveInstruction(store); } @@ -137,7 +141,9 @@ class LSEVisitor : public HGraphDelegateVisitor { void KeepIfIsStore(HInstruction* heap_value) { if (heap_value == kDefaultHeapValue || heap_value == kUnknownHeapValue || - !(heap_value->IsInstanceFieldSet() || heap_value->IsArraySet())) { + !(heap_value->IsInstanceFieldSet() || + heap_value->IsArraySet() || + heap_value->IsVecStore())) { return; } auto idx = std::find(possibly_removed_stores_.begin(), @@ -320,7 +326,9 @@ class LSEVisitor : public HGraphDelegateVisitor { return; } if (heap_value != kUnknownHeapValue) { - if (heap_value->IsInstanceFieldSet() || heap_value->IsArraySet()) { + if (heap_value->IsInstanceFieldSet() || + heap_value->IsArraySet() || + heap_value->IsVecStore()) { HInstruction* store = heap_value; // This load must be from a singleton since it's from the same // field/element that a "removed" store puts the value. That store @@ -416,7 +424,9 @@ class LSEVisitor : public HGraphDelegateVisitor { if (!same_value) { if (possibly_redundant) { - DCHECK(instruction->IsInstanceFieldSet() || instruction->IsArraySet()); + DCHECK(instruction->IsInstanceFieldSet() || + instruction->IsArraySet() || + instruction->IsVecStore()); // Put the store as the heap value. If the value is loaded from heap // by a load later, this store isn't really redundant. heap_values[idx] = instruction; @@ -429,8 +439,24 @@ class LSEVisitor : public HGraphDelegateVisitor { if (i == idx) { continue; } - if (heap_values[i] == value) { - // Same value should be kept even if aliasing happens. + if (heap_values[i] == value && !instruction->IsVecOperation()) { + // For field/array, same value should be kept even if aliasing happens. + // + // For vector values , this is NOT safe. For example: + // packed_data = [0xA, 0xB, 0xC, 0xD]; <-- Different values in each lane. + // VecStore array[i ,i+1,i+2,i+3] = packed_data; + // VecStore array[i+1,i+2,i+3,i+4] = packed_data; <-- We are here (partial overlap). + // VecLoad vx = array[i,i+1,i+2,i+3]; <-- Cannot be eliminated. + // + // TODO: to allow such 'same value' optimization on vector data, + // LSA needs to report more fine-grain MAY alias information: + // (1) May alias due to two vector data partial overlap. + // e.g. a[i..i+3] and a[i+1,..,i+4]. + // (2) May alias due to two vector data may complete overlap each other. + // e.g. a[i..i+3] and b[i..i+3]. + // (3) May alias but the exact relationship between two locations is unknown. + // e.g. a[i..i+3] and b[j..j+3], where values of a,b,i,j are all unknown. + // This 'same value' optimization can apply only on case (2). continue; } if (heap_values[i] == kUnknownHeapValue) { @@ -520,6 +546,32 @@ class LSEVisitor : public HGraphDelegateVisitor { value); } + void VisitVecLoad(HVecLoad* instruction) OVERRIDE { + HInstruction* array = instruction->InputAt(0); + HInstruction* index = instruction->InputAt(1); + size_t vector_length = instruction->GetVectorLength(); + VisitGetLocation(instruction, + array, + HeapLocation::kInvalidFieldOffset, + index, + vector_length, + HeapLocation::kDeclaringClassDefIndexForArrays); + } + + void VisitVecStore(HVecStore* instruction) OVERRIDE { + HInstruction* array = instruction->InputAt(0); + HInstruction* index = instruction->InputAt(1); + HInstruction* value = instruction->InputAt(2); + size_t vector_length = instruction->GetVectorLength(); + VisitSetLocation(instruction, + array, + HeapLocation::kInvalidFieldOffset, + index, + vector_length, + HeapLocation::kDeclaringClassDefIndexForArrays, + value); + } + void VisitDeoptimize(HDeoptimize* instruction) { const ScopedArenaVector& heap_values = heap_values_for_[instruction->GetBlock()->GetBlockId()]; @@ -529,7 +581,9 @@ class LSEVisitor : public HGraphDelegateVisitor { continue; } // A store is kept as the heap value for possibly removed stores. - if (heap_value->IsInstanceFieldSet() || heap_value->IsArraySet()) { + if (heap_value->IsInstanceFieldSet() || + heap_value->IsArraySet() || + heap_value->IsVecStore()) { // Check whether the reference for a store is used by an environment local of // HDeoptimize. HInstruction* reference = heap_value->InputAt(0); @@ -687,11 +741,6 @@ void LoadStoreElimination::Run() { return; } - // TODO: analyze VecLoad/VecStore better. - if (graph_->HasSIMD()) { - return; - } - LSEVisitor lse_visitor(graph_, heap_location_collector, side_effects_, stats_); for (HBasicBlock* block : graph_->GetReversePostOrder()) { lse_visitor.VisitBasicBlock(block); diff --git a/compiler/optimizing/load_store_elimination_test.cc b/compiler/optimizing/load_store_elimination_test.cc new file mode 100644 index 0000000000..6f42d96154 --- /dev/null +++ b/compiler/optimizing/load_store_elimination_test.cc @@ -0,0 +1,406 @@ +/* + * 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. + */ + +#include "side_effects_analysis.h" +#include "load_store_analysis.h" +#include "load_store_elimination.h" +#include "nodes.h" +#include "optimizing_unit_test.h" + +#include "gtest/gtest.h" + +namespace art { + +class LoadStoreEliminationTest : public OptimizingUnitTest { + public: + LoadStoreEliminationTest() : pool_() {} + + void PerformLSE() { + graph_->BuildDominatorTree(); + SideEffectsAnalysis side_effects(graph_); + side_effects.Run(); + LoadStoreAnalysis lsa(graph_); + lsa.Run(); + LoadStoreElimination lse(graph_, side_effects, lsa, nullptr); + lse.Run(); + } + + void CreateTestControlFlowGraph() { + graph_ = CreateGraph(); + + entry_ = new (GetAllocator()) HBasicBlock(graph_); + pre_header_ = new (GetAllocator()) HBasicBlock(graph_); + loop_header_ = new (GetAllocator()) HBasicBlock(graph_); + loop_body_ = new (GetAllocator()) HBasicBlock(graph_); + exit_ = new (GetAllocator()) HBasicBlock(graph_); + + graph_->AddBlock(entry_); + graph_->AddBlock(pre_header_); + graph_->AddBlock(loop_header_); + graph_->AddBlock(loop_body_); + graph_->AddBlock(exit_); + + graph_->SetEntryBlock(entry_); + + // This common CFG has been used by all cases in this load_store_elimination_test. + // entry + // | + // pre_header + // | + // loop_header <--+ + // | | + // loop_body -----+ + // | + // exit + + entry_->AddSuccessor(pre_header_); + pre_header_->AddSuccessor(loop_header_); + loop_header_->AddSuccessor(exit_); // true successor + loop_header_->AddSuccessor(loop_body_); // false successor + loop_body_->AddSuccessor(loop_header_); + + HInstruction* c0 = graph_->GetIntConstant(0); + HInstruction* c1 = graph_->GetIntConstant(1); + HInstruction* c4 = graph_->GetIntConstant(4); + HInstruction* c128 = graph_->GetIntConstant(128); + + // entry block has following instructions: + // array, i, j, i+1, i+4. + array_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(), + dex::TypeIndex(0), + 0, + DataType::Type::kReference); + i_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(), + dex::TypeIndex(1), + 1, + DataType::Type::kInt32); + j_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(), + dex::TypeIndex(1), + 2, + DataType::Type::kInt32); + i_add1_ = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_, c1); + i_add4_ = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_, c4); + entry_->AddInstruction(array_); + entry_->AddInstruction(i_); + entry_->AddInstruction(j_); + entry_->AddInstruction(i_add1_); + entry_->AddInstruction(i_add4_); + entry_->AddInstruction(new (GetAllocator()) HGoto()); + + // pre_header block + pre_header_->AddInstruction(new (GetAllocator()) HGoto()); + + // loop header block has following instructions: + // phi = 0; + // if (phi >= 128); + phi_ = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32); + cmp_ = new (GetAllocator()) HGreaterThanOrEqual(phi_, c128); + if_ = new (GetAllocator()) HIf(cmp_); + loop_header_->AddPhi(phi_); + loop_header_->AddInstruction(cmp_); + loop_header_->AddInstruction(if_); + phi_->AddInput(c0); + + // loop body block has following instructions: + // phi++; + HInstruction* inc_phi = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi_, c1); + loop_body_->AddInstruction(inc_phi); + loop_body_->AddInstruction(new (GetAllocator()) HGoto()); + phi_->AddInput(inc_phi); + + // exit block + exit_->AddInstruction(new (GetAllocator()) HExit()); + } + + // To avoid tedious HIR assembly in test functions. + HInstruction* AddVecLoad(HBasicBlock* block, HInstruction* array, HInstruction* index) { + DCHECK(block != nullptr); + DCHECK(array != nullptr); + DCHECK(index != nullptr); + HInstruction* vload = new (GetAllocator()) HVecLoad( + GetAllocator(), + array, + index, + DataType::Type::kInt32, + SideEffects::ArrayReadOfType(DataType::Type::kInt32), + 4, + /*is_string_char_at*/ false, + kNoDexPc); + block->InsertInstructionBefore(vload, block->GetLastInstruction()); + return vload; + } + + HInstruction* AddVecStore(HBasicBlock* block, + HInstruction* array, + HInstruction* index, + HVecOperation* vdata = nullptr) { + DCHECK(block != nullptr); + DCHECK(array != nullptr); + DCHECK(index != nullptr); + if (vdata == nullptr) { + HInstruction* c1 = graph_->GetIntConstant(1); + vdata = new (GetAllocator()) HVecReplicateScalar(GetAllocator(), + c1, + DataType::Type::kInt32, + 4, + kNoDexPc); + block->InsertInstructionBefore(vdata, block->GetLastInstruction()); + } + HInstruction* vstore = new (GetAllocator()) HVecStore( + GetAllocator(), + array, + index, + vdata, + DataType::Type::kInt32, + SideEffects::ArrayWriteOfType(DataType::Type::kInt32), + 4, + kNoDexPc); + block->InsertInstructionBefore(vstore, block->GetLastInstruction()); + return vstore; + } + + HInstruction* AddArrayGet(HBasicBlock* block, HInstruction* array, HInstruction* index) { + DCHECK(block != nullptr); + DCHECK(array != nullptr); + DCHECK(index != nullptr); + HInstruction* get = new (GetAllocator()) HArrayGet(array, index, DataType::Type::kInt32, 0); + block->InsertInstructionBefore(get, block->GetLastInstruction()); + return get; + } + + HInstruction* AddArraySet(HBasicBlock* block, + HInstruction* array, + HInstruction* index, + HInstruction* data = nullptr) { + DCHECK(block != nullptr); + DCHECK(array != nullptr); + DCHECK(index != nullptr); + if (data == nullptr) { + data = graph_->GetIntConstant(1); + } + HInstruction* store = new (GetAllocator()) HArraySet(array, + index, + data, + DataType::Type::kInt32, + 0); + block->InsertInstructionBefore(store, block->GetLastInstruction()); + return store; + } + + ArenaPool pool_; + + HGraph* graph_; + HBasicBlock* entry_; + HBasicBlock* pre_header_; + HBasicBlock* loop_header_; + HBasicBlock* loop_body_; + HBasicBlock* exit_; + + HInstruction* array_; + HInstruction* i_; + HInstruction* j_; + HInstruction* i_add1_; + HInstruction* i_add4_; + + HPhi* phi_; + HInstruction* cmp_; + HInstruction* if_; +}; + +TEST_F(LoadStoreEliminationTest, ArrayGetSetElimination) { + CreateTestControlFlowGraph(); + + HInstruction* c1 = graph_->GetIntConstant(1); + HInstruction* c2 = graph_->GetIntConstant(2); + HInstruction* c3 = graph_->GetIntConstant(3); + + // array[1] = 1; + // x = array[1]; <--- Remove. + // y = array[2]; + // array[1] = 1; <--- Remove, since it stores same value. + // array[i] = 3; <--- MAY alias. + // array[1] = 1; <--- Cannot remove, even if it stores the same value. + AddArraySet(entry_, array_, c1, c1); + HInstruction* load1 = AddArrayGet(entry_, array_, c1); + HInstruction* load2 = AddArrayGet(entry_, array_, c2); + HInstruction* store1 = AddArraySet(entry_, array_, c1, c1); + AddArraySet(entry_, array_, i_, c3); + HInstruction* store2 = AddArraySet(entry_, array_, c1, c1); + + PerformLSE(); + + ASSERT_TRUE(IsRemoved(load1)); + ASSERT_FALSE(IsRemoved(load2)); + ASSERT_TRUE(IsRemoved(store1)); + ASSERT_FALSE(IsRemoved(store2)); +} + +TEST_F(LoadStoreEliminationTest, SameHeapValue) { + CreateTestControlFlowGraph(); + + HInstruction* c1 = graph_->GetIntConstant(1); + HInstruction* c2 = graph_->GetIntConstant(2); + + // Test LSE handling same value stores on array. + // array[1] = 1; + // array[2] = 1; + // array[1] = 1; <--- Can remove. + // array[1] = 2; <--- Can NOT remove. + AddArraySet(entry_, array_, c1, c1); + AddArraySet(entry_, array_, c2, c1); + HInstruction* store1 = AddArraySet(entry_, array_, c1, c1); + HInstruction* store2 = AddArraySet(entry_, array_, c1, c2); + + // Test LSE handling same value stores on vector. + // vdata = [0x1, 0x2, 0x3, 0x4, ...] + // VecStore array[i...] = vdata; + // VecStore array[j...] = vdata; <--- MAY ALIAS. + // VecStore array[i...] = vdata; <--- Cannot Remove, even if it's same value. + AddVecStore(entry_, array_, i_); + AddVecStore(entry_, array_, j_); + HInstruction* vstore1 = AddVecStore(entry_, array_, i_); + + // VecStore array[i...] = vdata; + // VecStore array[i+1...] = vdata; <--- MAY alias due to partial overlap. + // VecStore array[i...] = vdata; <--- Cannot remove, even if it's same value. + AddVecStore(entry_, array_, i_); + AddVecStore(entry_, array_, i_add1_); + HInstruction* vstore2 = AddVecStore(entry_, array_, i_); + + PerformLSE(); + + ASSERT_TRUE(IsRemoved(store1)); + ASSERT_FALSE(IsRemoved(store2)); + ASSERT_FALSE(IsRemoved(vstore1)); + ASSERT_FALSE(IsRemoved(vstore2)); +} + +TEST_F(LoadStoreEliminationTest, OverlappingLoadStore) { + CreateTestControlFlowGraph(); + + HInstruction* c1 = graph_->GetIntConstant(1); + + // Test LSE handling array LSE when there is vector store in between. + // a[i] = 1; + // .. = a[i]; <-- Remove. + // a[i,i+1,i+2,i+3] = data; <-- PARTIAL OVERLAP ! + // .. = a[i]; <-- Cannot remove. + AddArraySet(entry_, array_, i_, c1); + HInstruction* load1 = AddArrayGet(entry_, array_, i_); + AddVecStore(entry_, array_, i_); + HInstruction* load2 = AddArrayGet(entry_, array_, i_); + + // Test LSE handling vector load/store partial overlap. + // a[i,i+1,i+2,i+3] = data; + // a[i+4,i+5,i+6,i+7] = data; + // .. = a[i,i+1,i+2,i+3]; + // .. = a[i+4,i+5,i+6,i+7]; + // a[i+1,i+2,i+3,i+4] = data; <-- PARTIAL OVERLAP ! + // .. = a[i,i+1,i+2,i+3]; + // .. = a[i+4,i+5,i+6,i+7]; + AddVecStore(entry_, array_, i_); + AddVecStore(entry_, array_, i_add4_); + HInstruction* vload1 = AddVecLoad(entry_, array_, i_); + HInstruction* vload2 = AddVecLoad(entry_, array_, i_add4_); + AddVecStore(entry_, array_, i_add1_); + HInstruction* vload3 = AddVecLoad(entry_, array_, i_); + HInstruction* vload4 = AddVecLoad(entry_, array_, i_add4_); + + // Test LSE handling vector LSE when there is array store in between. + // a[i,i+1,i+2,i+3] = data; + // a[i+1] = 1; <-- PARTIAL OVERLAP ! + // .. = a[i,i+1,i+2,i+3]; + AddVecStore(entry_, array_, i_); + AddArraySet(entry_, array_, i_, c1); + HInstruction* vload5 = AddVecLoad(entry_, array_, i_); + + PerformLSE(); + + ASSERT_TRUE(IsRemoved(load1)); + ASSERT_FALSE(IsRemoved(load2)); + + ASSERT_TRUE(IsRemoved(vload1)); + ASSERT_TRUE(IsRemoved(vload2)); + ASSERT_FALSE(IsRemoved(vload3)); + ASSERT_FALSE(IsRemoved(vload4)); + + ASSERT_FALSE(IsRemoved(vload5)); +} + +// function (int[] a, int j) { +// a[j] = 1; +// for (int i=0; i<128; i++) { +// /* doesn't do any write */ +// } +// a[j] = 1; +TEST_F(LoadStoreEliminationTest, Loop1) { + CreateTestControlFlowGraph(); + + HInstruction* c1 = graph_->GetIntConstant(1); + + // a[j] = 1 + AddArraySet(pre_header_, array_, j_, c1); + + // LOOP BODY: + // .. = a[i,i+1,i+2,i+3]; + AddVecLoad(loop_body_, array_, phi_); + + // a[j] = 1; + HInstruction* array_set = AddArraySet(exit_, array_, j_, c1); + + PerformLSE(); + + ASSERT_TRUE(IsRemoved(array_set)); +} + +// function (int[] a, int index) { +// a[index] = 1; +// int[] b = new int[128]; +// for (int i=0; i<128; i++) { +// a[i,i+1,i+2,i+3] = vdata; +// b[i,i+1,i+2,i+3] = a[i,i+1,i+2,i+3]; +// } +// a[index] = 1; +// } +TEST_F(LoadStoreEliminationTest, Loop2) { + CreateTestControlFlowGraph(); + + HInstruction* c0 = graph_->GetIntConstant(0); + HInstruction* c1 = graph_->GetIntConstant(1); + HInstruction* c128 = graph_->GetIntConstant(128); + + HInstruction* array_b = new (GetAllocator()) HNewArray(c0, c128, 0); + entry_->AddInstruction(array_b); + + // a[index] = 1; + AddArraySet(pre_header_, array_, i_, c1); + + // a[i,i+1,i+2,i+3] = vdata; + // b[i,i+1,i+2,i+3] = a[i,i+1,i+2,i+3]; + AddVecStore(loop_body_, array_, phi_); + HInstruction* vload = AddVecLoad(loop_body_, array_, phi_); + AddVecStore(loop_body_, array_b, phi_, vload->AsVecLoad()); + + // a[index] = 1; + HInstruction* a_set = AddArraySet(exit_, array_, i_, c1); + + PerformLSE(); + + ASSERT_TRUE(IsRemoved(vload)); + ASSERT_FALSE(IsRemoved(a_set)); // Cannot remove due to side effect in loop. +} + +} // namespace art -- GitLab From b0a6aeee250945b1d156ebab94053380f2e5a3c5 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 27 Oct 2017 10:34:04 +0100 Subject: [PATCH 016/226] Record @{Fast,Critical}Native in method's access flags. Repurpose the old kAccFastNative flag (which wasn't actually used for some time) and define a new kAccCriticalNative flag to record the native method's annotation-based kind. This avoids repeated determination of the kind from GenericJNI. And making two transitions to runnable and back (using the ScopedObjectAccess) from GenericJniMethodEnd() for normal native methods just to determine that we need to transition to runnable was really weird. Since the IsFastNative() function now records the presence of the @FastNative annotation, synchronized @FastNative method calls now avoid thread state transitions. When initializing the Runtime without a boot image, the WellKnowClasses may not yet be initialized, so relax the DCheckNativeAnnotation() to take that into account. Also revert https://android-review.googlesource.com/509715 as the annotation checks are now much faster. Bug: 65574695 Bug: 35644369 Test: m test-art-host-gtest Test: testrunner.py --host Change-Id: I2fc5ba192b9ce710a0e9202977b4f9543e387efe --- compiler/compiler.h | 9 +--- compiler/driver/compiler_driver.cc | 36 ++----------- compiler/jni/jni_compiler_test.cc | 20 +++---- compiler/jni/quick/jni_compiler.cc | 16 +++--- compiler/jni/quick/jni_compiler.h | 3 +- compiler/optimizing/optimizing_compiler.cc | 6 +-- openjdkjvmti/ti_redefine.cc | 4 +- profman/boot_image_profile.cc | 2 +- runtime/art_method-inl.h | 2 + runtime/art_method.cc | 21 +------- runtime/art_method.h | 38 +++++++++----- runtime/class_linker.cc | 7 +++ runtime/dex_file_annotations.cc | 52 ++++++++++--------- runtime/dex_file_annotations.h | 15 +++--- runtime/entrypoints/jni/jni_entrypoints.cc | 2 +- .../quick/quick_jni_entrypoints.cc | 39 ++++++-------- .../quick/quick_trampoline_entrypoints.cc | 26 ++-------- runtime/image.cc | 2 +- runtime/jni_internal.cc | 2 +- runtime/mirror/class.cc | 16 +++--- runtime/mirror/class.h | 2 +- runtime/modifiers.h | 15 ++++-- .../scoped_fast_native_object_access-inl.h | 2 +- 23 files changed, 138 insertions(+), 199 deletions(-) diff --git a/compiler/compiler.h b/compiler/compiler.h index 6c542c841a..9179e9c7c6 100644 --- a/compiler/compiler.h +++ b/compiler/compiler.h @@ -46,12 +46,6 @@ class Compiler { kOptimizing }; - enum JniOptimizationFlags { - kNone = 0x0, - kFastNative = 0x1, - kCriticalNative = 0x2, - }; - static Compiler* Create(CompilerDriver* driver, Kind kind); virtual void Init() = 0; @@ -71,8 +65,7 @@ class Compiler { virtual CompiledMethod* JniCompile(uint32_t access_flags, uint32_t method_idx, - const DexFile& dex_file, - JniOptimizationFlags optimization_flags) const = 0; + const DexFile& dex_file) const = 0; virtual bool JitCompile(Thread* self ATTRIBUTE_UNUSED, jit::JitCodeCache* code_cache ATTRIBUTE_UNUSED, diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index a9d27ef0cc..32d0bbe495 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -46,6 +46,7 @@ #include "dex/verified_method.h" #include "dex_compilation_unit.h" #include "dex_file-inl.h" +#include "dex_file_annotations.h" #include "dex_instruction-inl.h" #include "driver/compiler_options.h" #include "gc/accounting/card_table-inl.h" @@ -511,40 +512,11 @@ static void CompileMethod(Thread* self, InstructionSetHasGenericJniStub(driver->GetInstructionSet())) { // Leaving this empty will trigger the generic JNI version } else { - // Look-up the ArtMethod associated with this code_item (if any) - // -- It is later used to lookup any [optimization] annotations for this method. - ScopedObjectAccess soa(self); - - // TODO: Lookup annotation from DexFile directly without resolving method. - ArtMethod* method = - Runtime::Current()->GetClassLinker()->ResolveMethod( - dex_file, - method_idx, - dex_cache, - class_loader, - /* referrer */ nullptr, - invoke_type); - // Query any JNI optimization annotations such as @FastNative or @CriticalNative. - Compiler::JniOptimizationFlags optimization_flags = Compiler::kNone; - if (UNLIKELY(method == nullptr)) { - // Failed method resolutions happen very rarely, e.g. ancestor class cannot be resolved. - DCHECK(self->IsExceptionPending()); - self->ClearException(); - } else if (method->IsAnnotatedWithFastNative()) { - // TODO: Will no longer need this CHECK once we have verifier checking this. - CHECK(!method->IsAnnotatedWithCriticalNative()); - optimization_flags = Compiler::kFastNative; - } else if (method->IsAnnotatedWithCriticalNative()) { - // TODO: Will no longer need this CHECK once we have verifier checking this. - CHECK(!method->IsAnnotatedWithFastNative()); - optimization_flags = Compiler::kCriticalNative; - } + access_flags |= annotations::GetNativeMethodAnnotationAccessFlags( + dex_file, dex_file.GetClassDef(class_def_idx), method_idx); - compiled_method = driver->GetCompiler()->JniCompile(access_flags, - method_idx, - dex_file, - optimization_flags); + compiled_method = driver->GetCompiler()->JniCompile(access_flags, method_idx, dex_file); CHECK(compiled_method != nullptr); } } else if ((access_flags & kAccAbstract) != 0) { diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index 3460efe474..daf64d1298 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -55,10 +55,10 @@ extern "C" JNIEXPORT jint JNICALL Java_MyClassNatives_sbar(JNIEnv*, jclass, jint namespace art { enum class JniKind { - kNormal = Compiler::kNone, // Regular kind of un-annotated natives. - kFast = Compiler::kFastNative, // Native method annotated with @FastNative. - kCritical = Compiler::kCriticalNative, // Native method annotated with @CriticalNative. - kCount = Compiler::kCriticalNative + 1 // How many different types of JNIs we can have. + kNormal, // Regular kind of un-annotated natives. + kFast, // Native method annotated with @FastNative. + kCritical, // Native method annotated with @CriticalNative. + kCount // How many different types of JNIs we can have. }; // Used to initialize array sizes that want to have different state per current jni. @@ -2205,8 +2205,8 @@ void JniCompilerTest::NormalNativeImpl() { ArtMethod* method = jni::DecodeArtMethod(jmethod_); ASSERT_TRUE(method != nullptr); - EXPECT_FALSE(method->IsAnnotatedWithCriticalNative()); - EXPECT_FALSE(method->IsAnnotatedWithFastNative()); + EXPECT_FALSE(method->IsCriticalNative()); + EXPECT_FALSE(method->IsFastNative()); } // TODO: just rename the java functions to the standard convention and remove duplicated tests @@ -2227,8 +2227,8 @@ void JniCompilerTest::FastNativeImpl() { ArtMethod* method = jni::DecodeArtMethod(jmethod_); ASSERT_TRUE(method != nullptr); - EXPECT_FALSE(method->IsAnnotatedWithCriticalNative()); - EXPECT_TRUE(method->IsAnnotatedWithFastNative()); + EXPECT_FALSE(method->IsCriticalNative()); + EXPECT_TRUE(method->IsFastNative()); } // TODO: just rename the java functions to the standard convention and remove duplicated tests @@ -2256,8 +2256,8 @@ void JniCompilerTest::CriticalNativeImpl() { ArtMethod* method = jni::DecodeArtMethod(jmethod_); ASSERT_TRUE(method != nullptr); - EXPECT_TRUE(method->IsAnnotatedWithCriticalNative()); - EXPECT_FALSE(method->IsAnnotatedWithFastNative()); + EXPECT_TRUE(method->IsCriticalNative()); + EXPECT_FALSE(method->IsFastNative()); EXPECT_EQ(0, gJava_myClassNatives_criticalNative_calls[gCurrentJni]); env_->CallStaticVoidMethod(jklass_, jmethod_); diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index b3177aa471..b93b05cbd4 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -52,8 +52,6 @@ namespace art { -using JniOptimizationFlags = Compiler::JniOptimizationFlags; - template static void CopyParameter(JNIMacroAssembler* jni_asm, ManagedRuntimeCallingConvention* mr_conv, @@ -120,8 +118,7 @@ template static CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver, uint32_t access_flags, uint32_t method_idx, - const DexFile& dex_file, - JniOptimizationFlags optimization_flags) { + const DexFile& dex_file) { const bool is_native = (access_flags & kAccNative) != 0; CHECK(is_native); const bool is_static = (access_flags & kAccStatic) != 0; @@ -131,10 +128,10 @@ static CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver, const InstructionSetFeatures* instruction_set_features = driver->GetInstructionSetFeatures(); // i.e. if the method was annotated with @FastNative - const bool is_fast_native = (optimization_flags == Compiler::kFastNative); + const bool is_fast_native = (access_flags & kAccFastNative) != 0u; // i.e. if the method was annotated with @CriticalNative - bool is_critical_native = (optimization_flags == Compiler::kCriticalNative); + bool is_critical_native = (access_flags & kAccCriticalNative) != 0u; VLOG(jni) << "JniCompile: Method :: " << dex_file.PrettyMethod(method_idx, /* with signature */ true) @@ -781,14 +778,13 @@ static void SetNativeParameter(JNIMacroAssembler* jni_asm, CompiledMethod* ArtQuickJniCompileMethod(CompilerDriver* compiler, uint32_t access_flags, uint32_t method_idx, - const DexFile& dex_file, - Compiler::JniOptimizationFlags optimization_flags) { + const DexFile& dex_file) { if (Is64BitInstructionSet(compiler->GetInstructionSet())) { return ArtJniCompileMethodInternal( - compiler, access_flags, method_idx, dex_file, optimization_flags); + compiler, access_flags, method_idx, dex_file); } else { return ArtJniCompileMethodInternal( - compiler, access_flags, method_idx, dex_file, optimization_flags); + compiler, access_flags, method_idx, dex_file); } } diff --git a/compiler/jni/quick/jni_compiler.h b/compiler/jni/quick/jni_compiler.h index 26c32a31b8..3fcce55b5a 100644 --- a/compiler/jni/quick/jni_compiler.h +++ b/compiler/jni/quick/jni_compiler.h @@ -28,8 +28,7 @@ class CompiledMethod; CompiledMethod* ArtQuickJniCompileMethod(CompilerDriver* compiler, uint32_t access_flags, uint32_t method_idx, - const DexFile& dex_file, - Compiler::JniOptimizationFlags optimization_flags); + const DexFile& dex_file); } // namespace art diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 9233eb5baf..252d53823a 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -311,13 +311,11 @@ class OptimizingCompiler FINAL : public Compiler { CompiledMethod* JniCompile(uint32_t access_flags, uint32_t method_idx, - const DexFile& dex_file, - JniOptimizationFlags optimization_flags) const OVERRIDE { + const DexFile& dex_file) const OVERRIDE { return ArtQuickJniCompileMethod(GetCompilerDriver(), access_flags, method_idx, - dex_file, - optimization_flags); + dex_file); } uintptr_t GetEntryPointOf(ArtMethod* method) const OVERRIDE diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index c4f16f5e2d..dcc237d6d6 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -629,8 +629,8 @@ bool Redefiner::ClassRedefinition::CheckSameMethods() { // Since direct methods have different flags than virtual ones (specifically direct methods must // have kAccPrivate or kAccStatic or kAccConstructor flags) we can tell if a method changes from // virtual to direct. - uint32_t new_flags = new_iter.GetMethodAccessFlags() & ~art::kAccPreviouslyWarm; - if (new_flags != (old_method->GetAccessFlags() & (art::kAccValidMethodFlags ^ art::kAccPreviouslyWarm))) { + uint32_t new_flags = new_iter.GetMethodAccessFlags(); + if (new_flags != (old_method->GetAccessFlags() & art::kAccValidMethodFlags)) { RecordFailure(ERR(UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED), StringPrintf("method '%s' (sig: %s) had different access flags", new_method_name, diff --git a/profman/boot_image_profile.cc b/profman/boot_image_profile.cc index 4092f6ed98..e5645d370c 100644 --- a/profman/boot_image_profile.cc +++ b/profman/boot_image_profile.cc @@ -92,7 +92,7 @@ void GenerateBootImageProfile( it.SkipInstanceFields(); while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) { const uint32_t flags = it.GetMethodAccessFlags(); - if ((flags & kAccNative) != 0 || (flags & kAccFastNative) != 0) { + if ((flags & kAccNative) != 0) { // Native method will get dirtied. is_clean = false; break; diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 12b4d16b37..e1671c93b3 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -392,6 +392,7 @@ inline void ArtMethod::SetIntrinsic(uint32_t intrinsic) { bool is_synchronized = IsSynchronized(); bool skip_access_checks = SkipAccessChecks(); bool is_fast_native = IsFastNative(); + bool is_critical_native = IsCriticalNative(); bool is_copied = IsCopied(); bool is_miranda = IsMiranda(); bool is_default = IsDefault(); @@ -404,6 +405,7 @@ inline void ArtMethod::SetIntrinsic(uint32_t intrinsic) { DCHECK_EQ(is_synchronized, IsSynchronized()); DCHECK_EQ(skip_access_checks, SkipAccessChecks()); DCHECK_EQ(is_fast_native, IsFastNative()); + DCHECK_EQ(is_critical_native, IsCriticalNative()); DCHECK_EQ(is_copied, IsCopied()); DCHECK_EQ(is_miranda, IsMiranda()); DCHECK_EQ(is_default, IsDefault()); diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 8709643c3e..0a108f93c5 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -26,7 +26,6 @@ #include "class_linker-inl.h" #include "debugger.h" #include "dex_file-inl.h" -#include "dex_file_annotations.h" #include "dex_instruction.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/card_table-inl.h" @@ -392,13 +391,9 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* self->PopManagedStackFragment(fragment); } -const void* ArtMethod::RegisterNative(const void* native_method, bool is_fast) { +const void* ArtMethod::RegisterNative(const void* native_method) { CHECK(IsNative()) << PrettyMethod(); - CHECK(!IsFastNative()) << PrettyMethod(); CHECK(native_method != nullptr) << PrettyMethod(); - if (is_fast) { - AddAccessFlags(kAccFastNative); - } void* new_native_method = nullptr; Runtime::Current()->GetRuntimeCallbacks()->RegisterNativeMethod(this, native_method, @@ -408,7 +403,7 @@ const void* ArtMethod::RegisterNative(const void* native_method, bool is_fast) { } void ArtMethod::UnregisterNative() { - CHECK(IsNative() && !IsFastNative()) << PrettyMethod(); + CHECK(IsNative()) << PrettyMethod(); // restore stub to lookup native pointer via dlsym SetEntryPointFromJni(GetJniDlsymLookupStub()); } @@ -428,18 +423,6 @@ bool ArtMethod::IsPolymorphicSignature() { cls == WellKnownClasses::ToClass(WellKnownClasses::java_lang_invoke_VarHandle)); } -bool ArtMethod::IsAnnotatedWithFastNative() { - ScopedObjectAccess soa(Thread::Current()); - return annotations::HasFastNativeMethodBuildAnnotation( - *GetDexFile(), GetClassDef(), GetDexMethodIndex()); -} - -bool ArtMethod::IsAnnotatedWithCriticalNative() { - ScopedObjectAccess soa(Thread::Current()); - return annotations::HasCriticalNativeMethodBuildAnnotation( - *GetDexFile(), GetClassDef(), GetDexMethodIndex()); -} - static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx) { diff --git a/runtime/art_method.h b/runtime/art_method.h index 8927481e46..0e98d47f16 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -200,9 +200,9 @@ class ArtMethod FINAL { } bool IsMiranda() { - static_assert((kAccMiranda & (kAccIntrinsic | kAccIntrinsicBits)) == 0, - "kAccMiranda conflicts with intrinsic modifier"); - return (GetAccessFlags() & kAccMiranda) != 0; + // The kAccMiranda flag value is used with a different meaning for native methods, + // so we need to check the kAccNative flag as well. + return (GetAccessFlags() & (kAccNative | kAccMiranda)) == kAccMiranda; } // Returns true if invoking this method will not throw an AbstractMethodError or @@ -213,6 +213,7 @@ class ArtMethod FINAL { bool IsCompilable() { if (IsIntrinsic()) { + // kAccCompileDontBother overlaps with kAccIntrinsicBits. return true; } return (GetAccessFlags() & kAccCompileDontBother) == 0; @@ -252,11 +253,24 @@ class ArtMethod FINAL { return (GetAccessFlags() & kAccNative) != 0; } + // Checks to see if the method was annotated with @dalvik.annotation.optimization.FastNative. bool IsFastNative() { + // The presence of the annotation is checked by ClassLinker and recorded in access flags. + // The kAccFastNative flag value is used with a different meaning for non-native methods, + // so we need to check the kAccNative flag as well. constexpr uint32_t mask = kAccFastNative | kAccNative; return (GetAccessFlags() & mask) == mask; } + // Checks to see if the method was annotated with @dalvik.annotation.optimization.CriticalNative. + bool IsCriticalNative() { + // The presence of the annotation is checked by ClassLinker and recorded in access flags. + // The kAccCriticalNative flag value is used with a different meaning for non-native methods, + // so we need to check the kAccNative flag as well. + constexpr uint32_t mask = kAccCriticalNative | kAccNative; + return (GetAccessFlags() & mask) == mask; + } + bool IsAbstract() { return (GetAccessFlags() & kAccAbstract) != 0; } @@ -274,10 +288,14 @@ class ArtMethod FINAL { bool IsPolymorphicSignature() REQUIRES_SHARED(Locks::mutator_lock_); bool SkipAccessChecks() { - return (GetAccessFlags() & kAccSkipAccessChecks) != 0; + // The kAccSkipAccessChecks flag value is used with a different meaning for native methods, + // so we need to check the kAccNative flag as well. + return (GetAccessFlags() & (kAccSkipAccessChecks | kAccNative)) == kAccSkipAccessChecks; } void SetSkipAccessChecks() { + // SkipAccessChecks() is applicable only to non-native methods. + DCHECK(!IsNative()); AddAccessFlags(kAccSkipAccessChecks); } @@ -310,14 +328,6 @@ class ArtMethod FINAL { AddAccessFlags(kAccMustCountLocks); } - // Checks to see if the method was annotated with @dalvik.annotation.optimization.FastNative - // -- Independent of kAccFastNative access flags. - bool IsAnnotatedWithFastNative(); - - // Checks to see if the method was annotated with @dalvik.annotation.optimization.CriticalNative - // -- Unrelated to the GC notion of "critical". - bool IsAnnotatedWithCriticalNative(); - // Returns true if this method could be overridden by a default method. bool IsOverridableByDefaultMethod() REQUIRES_SHARED(Locks::mutator_lock_); @@ -417,7 +427,7 @@ class ArtMethod FINAL { // Registers the native method and returns the new entry point. NB The returned entry point might // be different from the native_method argument if some MethodCallback modifies it. - const void* RegisterNative(const void* native_method, bool is_fast) + const void* RegisterNative(const void* native_method) REQUIRES_SHARED(Locks::mutator_lock_) WARN_UNUSED; void UnregisterNative() REQUIRES_SHARED(Locks::mutator_lock_); @@ -452,7 +462,7 @@ class ArtMethod FINAL { // where the declaring class is treated as a weak reference (accessing it with // a read barrier would either prevent unloading the class, or crash the runtime if // the GC wants to unload it). - DCHECK(!IsNative()); + DCHECK(!IsNative()); if (UNLIKELY(IsProxyMethod())) { return nullptr; } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index bd5e18493e..6f4b9a5e32 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3340,6 +3340,11 @@ void ClassLinker::LoadMethod(const DexFile& dex_file, } } } + if (UNLIKELY((access_flags & kAccNative) != 0u)) { + // Check if the native method is annotated with @FastNative or @CriticalNative. + access_flags |= annotations::GetNativeMethodAnnotationAccessFlags( + dex_file, dst->GetClassDef(), dex_method_idx); + } dst->SetAccessFlags(access_flags); } @@ -7048,6 +7053,7 @@ void ClassLinker::LinkInterfaceMethodsHelper::ReallocMethods() { // verified yet it shouldn't have methods that are skipping access checks. // TODO This is rather arbitrary. We should maybe support classes where only some of its // methods are skip_access_checks. + DCHECK_EQ(new_method.GetAccessFlags() & kAccNative, 0u); constexpr uint32_t kSetFlags = kAccDefault | kAccCopied; constexpr uint32_t kMaskFlags = ~kAccSkipAccessChecks; new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags); @@ -7070,6 +7076,7 @@ void ClassLinker::LinkInterfaceMethodsHelper::ReallocMethods() { // mark this as a default, non-abstract method, since thats what it is. Also clear the // kAccSkipAccessChecks bit since this class hasn't been verified yet it shouldn't have // methods that are skipping access checks. + DCHECK_EQ(new_method.GetAccessFlags() & kAccNative, 0u); constexpr uint32_t kSetFlags = kAccDefault | kAccDefaultConflict | kAccCopied; constexpr uint32_t kMaskFlags = ~(kAccAbstract | kAccSkipAccessChecks); new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags); diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index 5496efd108..27060aeff6 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -1239,8 +1239,11 @@ static void DCheckNativeAnnotation(const char* descriptor, jclass cls) { ScopedObjectAccess soa(Thread::Current()); ObjPtr klass = soa.Decode(cls); ClassLinker* linker = Runtime::Current()->GetClassLinker(); - // Lookup using the boot class path loader should yield the annotation class. - CHECK_EQ(klass, linker->LookupClass(soa.Self(), descriptor, /* class_loader */ nullptr)); + // WellKnownClasses may not be initialized yet, so `klass` may be null. + if (klass != nullptr) { + // Lookup using the boot class path loader should yield the annotation class. + CHECK_EQ(klass, linker->LookupClass(soa.Self(), descriptor, /* class_loader */ nullptr)); + } } } @@ -1266,30 +1269,31 @@ static bool IsMethodBuildAnnotationPresent(const DexFile& dex_file, return false; } -uint32_t HasFastNativeMethodBuildAnnotation(const DexFile& dex_file, - const DexFile::ClassDef& class_def, - uint32_t method_index) { - const DexFile::AnnotationSetItem* annotation_set = - FindAnnotationSetForMethod(dex_file, class_def, method_index); - return annotation_set != nullptr && - IsMethodBuildAnnotationPresent( - dex_file, - *annotation_set, - "Ldalvik/annotation/optimization/FastNative;", - WellKnownClasses::dalvik_annotation_optimization_FastNative); -} - -uint32_t HasCriticalNativeMethodBuildAnnotation(const DexFile& dex_file, - const DexFile::ClassDef& class_def, - uint32_t method_index) { +uint32_t GetNativeMethodAnnotationAccessFlags(const DexFile& dex_file, + const DexFile::ClassDef& class_def, + uint32_t method_index) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(dex_file, class_def, method_index); - return annotation_set != nullptr && - IsMethodBuildAnnotationPresent( - dex_file, - *annotation_set, - "Ldalvik/annotation/optimization/CriticalNative;", - WellKnownClasses::dalvik_annotation_optimization_CriticalNative); + if (annotation_set == nullptr) { + return 0u; + } + uint32_t access_flags = 0u; + if (IsMethodBuildAnnotationPresent( + dex_file, + *annotation_set, + "Ldalvik/annotation/optimization/FastNative;", + WellKnownClasses::dalvik_annotation_optimization_FastNative)) { + access_flags |= kAccFastNative; + } + if (IsMethodBuildAnnotationPresent( + dex_file, + *annotation_set, + "Ldalvik/annotation/optimization/CriticalNative;", + WellKnownClasses::dalvik_annotation_optimization_CriticalNative)) { + access_flags |= kAccCriticalNative; + } + CHECK_NE(access_flags, kAccFastNative | kAccCriticalNative); + return access_flags; } mirror::Object* GetAnnotationForClass(Handle klass, diff --git a/runtime/dex_file_annotations.h b/runtime/dex_file_annotations.h index 04ff3a11a5..243f30f48b 100644 --- a/runtime/dex_file_annotations.h +++ b/runtime/dex_file_annotations.h @@ -75,15 +75,12 @@ bool IsMethodAnnotationPresent(ArtMethod* method, uint32_t visibility = DexFile::kDexVisibilityRuntime) REQUIRES_SHARED(Locks::mutator_lock_); // Check whether a method from the `dex_file` with the given `method_index` -// is annotated with @dalvik.annotation.optimization.FastNative with build visibility. -uint32_t HasFastNativeMethodBuildAnnotation(const DexFile& dex_file, - const DexFile::ClassDef& class_def, - uint32_t method_index); -// Check whether a method from the `dex_file` with the given `method_index` -// is annotated with @dalvik.annotation.optimization.CriticalNative with build visibility. -uint32_t HasCriticalNativeMethodBuildAnnotation(const DexFile& dex_file, - const DexFile::ClassDef& class_def, - uint32_t method_index); +// is annotated with @dalvik.annotation.optimization.FastNative or +// @dalvik.annotation.optimization.CriticalNative with build visibility. +// If yes, return the associated access flags, i.e. kAccFastNative or kAccCriticalNative. +uint32_t GetNativeMethodAnnotationAccessFlags(const DexFile& dex_file, + const DexFile::ClassDef& class_def, + uint32_t method_index); // Class annotations. mirror::Object* GetAnnotationForClass(Handle klass, diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc index dd0819ed8f..7ec360a93c 100644 --- a/runtime/entrypoints/jni/jni_entrypoints.cc +++ b/runtime/entrypoints/jni/jni_entrypoints.cc @@ -46,7 +46,7 @@ extern "C" const void* artFindNativeMethod(Thread* self) { return nullptr; } // Register so that future calls don't come here - return method->RegisterNative(native_code, false); + return method->RegisterNative(native_code); } } // namespace art diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc index a8d2a34853..29a62c86ee 100644 --- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc @@ -28,10 +28,7 @@ namespace art { static_assert(sizeof(IRTSegmentState) == sizeof(uint32_t), "IRTSegmentState size unexpected"); static_assert(std::is_trivial::value, "IRTSegmentState not trivial"); -static bool kEnableAnnotationChecks = RegisterRuntimeDebugFlag(&kEnableAnnotationChecks); - -template -static inline void GoToRunnableFast(Thread* self) NO_THREAD_SAFETY_ANALYSIS; +static inline void GoToRunnableFast(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); extern void ReadBarrierJni(mirror::CompressedReference* handle_on_stack, Thread* self ATTRIBUTE_UNUSED) { @@ -56,9 +53,9 @@ extern uint32_t JniMethodFastStart(Thread* self) { uint32_t saved_local_ref_cookie = bit_cast(env->local_ref_cookie); env->local_ref_cookie = env->locals.GetSegmentState(); - if (kIsDebugBuild && kEnableAnnotationChecks) { + if (kIsDebugBuild) { ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); - CHECK(native_method->IsAnnotatedWithFastNative()) << native_method->PrettyMethod(); + CHECK(native_method->IsFastNative()) << native_method->PrettyMethod(); } return saved_local_ref_cookie; @@ -71,6 +68,9 @@ extern uint32_t JniMethodStart(Thread* self) { uint32_t saved_local_ref_cookie = bit_cast(env->local_ref_cookie); env->local_ref_cookie = env->locals.GetSegmentState(); ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); + // TODO: Introduce special entrypoint for synchronized @FastNative methods? + // Or ban synchronized @FastNative outright to avoid the extra check here? + DCHECK(!native_method->IsFastNative() || native_method->IsSynchronized()); if (!native_method->IsFastNative()) { // When not fast JNI we transition out of runnable. self->TransitionFromRunnableToSuspended(kNative); @@ -90,25 +90,18 @@ static void GoToRunnable(Thread* self) NO_THREAD_SAFETY_ANALYSIS { if (!is_fast) { self->TransitionFromSuspendedToRunnable(); } else { - GoToRunnableFast(self); + GoToRunnableFast(self); } } -// TODO: NO_THREAD_SAFETY_ANALYSIS due to different control paths depending on fast JNI. -template -ALWAYS_INLINE static inline void GoToRunnableFast(Thread* self) NO_THREAD_SAFETY_ANALYSIS { - if (kIsDebugBuild && kEnableAnnotationChecks) { - // Should only enter here if the method is !Fast JNI or @FastNative. +ALWAYS_INLINE static inline void GoToRunnableFast(Thread* self) { + if (kIsDebugBuild) { + // Should only enter here if the method is @FastNative. ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); - - if (kDynamicFast) { - CHECK(native_method->IsFastNative()) << native_method->PrettyMethod(); - } else { - CHECK(native_method->IsAnnotatedWithFastNative()) << native_method->PrettyMethod(); - } + CHECK(native_method->IsFastNative()) << native_method->PrettyMethod(); } - // When we are in "fast" JNI or @FastNative, we are already Runnable. + // When we are in @FastNative, we are already Runnable. // Only do a suspend check on the way out of JNI. if (UNLIKELY(self->TestAllFlags())) { // In fast JNI mode we never transitioned out of runnable. Perform a suspend check if there @@ -138,7 +131,7 @@ extern void JniMethodEnd(uint32_t saved_local_ref_cookie, Thread* self) { } extern void JniMethodFastEnd(uint32_t saved_local_ref_cookie, Thread* self) { - GoToRunnableFast(self); + GoToRunnableFast(self); PopLocalReferences(saved_local_ref_cookie, self); } @@ -175,7 +168,7 @@ static mirror::Object* JniMethodEndWithReferenceHandleResult(jobject result, extern mirror::Object* JniMethodFastEndWithReference(jobject result, uint32_t saved_local_ref_cookie, Thread* self) { - GoToRunnableFast(self); + GoToRunnableFast(self); return JniMethodEndWithReferenceHandleResult(result, saved_local_ref_cookie, self); } @@ -203,8 +196,8 @@ extern uint64_t GenericJniMethodEnd(Thread* self, HandleScope* handle_scope) // TODO: NO_THREAD_SAFETY_ANALYSIS as GoToRunnable() is NO_THREAD_SAFETY_ANALYSIS NO_THREAD_SAFETY_ANALYSIS { - bool critical_native = called->IsAnnotatedWithCriticalNative(); - bool fast_native = called->IsAnnotatedWithFastNative(); + bool critical_native = called->IsCriticalNative(); + bool fast_native = called->IsFastNative(); bool normal_native = !critical_native && !fast_native; // @Fast and @CriticalNative do not do a state transition. diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index a4a8c349a3..127b5d7028 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -2171,32 +2171,14 @@ static void artQuickGenericJniEndJNINonRef(Thread* self, */ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) { + // Note: We cannot walk the stack properly until fixed up below. ArtMethod* called = *sp; DCHECK(called->IsNative()) << called->PrettyMethod(true); - // Fix up a callee-save frame at the bottom of the stack (at `*sp`, - // above the alloca region) while we check for optimization - // annotations, thus allowing stack walking until the completion of - // the JNI frame creation. - // - // Note however that the Generic JNI trampoline does not expect - // exception being thrown at that stage. - *sp = Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs); - self->SetTopOfStack(sp); uint32_t shorty_len = 0; const char* shorty = called->GetShorty(&shorty_len); - // Optimization annotations lookup does not try to resolve classes, - // as this may throw an exception, which is not supported by the - // Generic JNI trampoline at this stage; instead, method's - // annotations' classes are looked up in the bootstrap class - // loader's resolved types (which won't trigger an exception). - CHECK(!self->IsExceptionPending()); - bool critical_native = called->IsAnnotatedWithCriticalNative(); - CHECK(!self->IsExceptionPending()); - bool fast_native = called->IsAnnotatedWithFastNative(); - CHECK(!self->IsExceptionPending()); + bool critical_native = called->IsCriticalNative(); + bool fast_native = called->IsFastNative(); bool normal_native = !critical_native && !fast_native; - // Restore the initial ArtMethod pointer at `*sp`. - *sp = called; // Run the visitor and update sp. BuildGenericJniFrameVisitor visitor(self, @@ -2212,7 +2194,7 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod** visitor.FinalizeHandleScope(self); } - // Fix up managed-stack things in Thread. + // Fix up managed-stack things in Thread. After this we can walk the stack. self->SetTopOfStack(sp); self->VerifyStack(); diff --git a/runtime/image.cc b/runtime/image.cc index cf5feaca56..8f35d8474c 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', '0', '\0' }; // strcmp() @FastNative. +const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '1', '\0' }; // @FastNative access flags. ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 5164c85b60..1e55158a34 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -2364,7 +2364,7 @@ class JNI { // TODO: make this a hard register error in the future. } - const void* final_function_ptr = m->RegisterNative(fnPtr, is_fast); + const void* final_function_ptr = m->RegisterNative(fnPtr); UNUSED(final_function_ptr); } return JNI_OK; diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 4d810dbce0..892c03912a 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -1244,7 +1244,6 @@ ObjPtr Class::GetDeclaredMethodInternal( // still return a synthetic method to handle situations like // escalated visibility. We never return miranda methods that // were synthesized by the runtime. - constexpr uint32_t kSkipModifiers = kAccMiranda | kAccSynthetic; StackHandleScope<3> hs(self); auto h_method_name = hs.NewHandle(name); if (UNLIKELY(h_method_name == nullptr)) { @@ -1264,11 +1263,10 @@ ObjPtr Class::GetDeclaredMethodInternal( } continue; } - auto modifiers = m.GetAccessFlags(); - if ((modifiers & kSkipModifiers) == 0) { - return Method::CreateFromArtMethod(self, &m); - } - if ((modifiers & kAccMiranda) == 0) { + if (!m.IsMiranda()) { + if (!m.IsSynthetic()) { + return Method::CreateFromArtMethod(self, &m); + } result = &m; // Remember as potential result if it's not a miranda method. } } @@ -1291,11 +1289,11 @@ ObjPtr Class::GetDeclaredMethodInternal( } continue; } - if ((modifiers & kSkipModifiers) == 0) { + DCHECK(!m.IsMiranda()); // Direct methods cannot be miranda methods. + if ((modifiers & kAccSynthetic) == 0) { return Method::CreateFromArtMethod(self, &m); } - // Direct methods cannot be miranda methods, so this potential result must be synthetic. - result = &m; + result = &m; // Remember as potential result. } } return result != nullptr diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index bf49f51339..c545a9b7d5 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -286,7 +286,7 @@ class MANAGED Class FINAL : public Object { // This does not necessarily mean that access checks are avoidable, // since the class methods might still need to be run with access checks. bool WasVerificationAttempted() REQUIRES_SHARED(Locks::mutator_lock_) { - return (GetAccessFlags() & kAccSkipAccessChecks) != 0; + return (GetAccessFlags() & kAccVerificationAttempted) != 0; } // Mark the class as having gone through a verification attempt. diff --git a/runtime/modifiers.h b/runtime/modifiers.h index 4b790a0f03..d7d647b8fd 100644 --- a/runtime/modifiers.h +++ b/runtime/modifiers.h @@ -49,17 +49,21 @@ static constexpr uint32_t kAccClassIsProxy = 0x00040000; // class (de // declaring class. This flag may only be applied to methods. static constexpr uint32_t kAccObsoleteMethod = 0x00040000; // method (runtime) // Used by a method to denote that its execution does not need to go through slow path interpreter. -static constexpr uint32_t kAccSkipAccessChecks = 0x00080000; // method (dex only) +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 kAccFastNative = 0x00080000; // method (dex only) // 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_ // array of a concrete class will also have this bit set. static constexpr uint32_t kAccCopied = 0x00100000; // method (runtime) -static constexpr uint32_t kAccMiranda = 0x00200000; // method (dex only) +static constexpr uint32_t kAccMiranda = 0x00200000; // method (runtime, not native) static constexpr uint32_t kAccDefault = 0x00400000; // method (runtime) +// Native method flags are set when linking the methods based on the presence of the +// @dalvik.annotation.optimization.{Fast,Critical}Native annotations with build visibility. +// Reuse the values of kAccSkipAccessChecks and kAccMiranda which are not used for native methods. +static constexpr uint32_t kAccFastNative = 0x00080000; // method (runtime; native only) +static constexpr uint32_t kAccCriticalNative = 0x00200000; // method (runtime; native only) // Set by the JIT when clearing profiling infos to denote that a method was previously warm. static constexpr uint32_t kAccPreviouslyWarm = 0x00800000; // method (runtime) @@ -106,8 +110,9 @@ static constexpr uint32_t kAccValidFieldFlags = kAccPublic | kAccPrivate | kAccP // Valid (meaningful) bits for a method. static constexpr uint32_t kAccValidMethodFlags = kAccPublic | kAccPrivate | kAccProtected | kAccStatic | kAccFinal | kAccSynchronized | kAccBridge | kAccVarargs | kAccNative | - kAccAbstract | kAccStrict | kAccSynthetic | kAccMiranda | kAccConstructor | - kAccDeclaredSynchronized | kAccPreviouslyWarm; + kAccAbstract | kAccStrict | kAccSynthetic | kAccConstructor | kAccDeclaredSynchronized; +static_assert(((kAccIntrinsic | kAccIntrinsicBits) & kAccValidMethodFlags) == 0, + "Intrinsic bits and valid dex file method access flags must not overlap."); // Valid (meaningful) bits for a class (not interface). // Note 1. These are positive bits. Other bits may have to be zero. diff --git a/runtime/native/scoped_fast_native_object_access-inl.h b/runtime/native/scoped_fast_native_object_access-inl.h index b2abc4691a..20ff76ea27 100644 --- a/runtime/native/scoped_fast_native_object_access-inl.h +++ b/runtime/native/scoped_fast_native_object_access-inl.h @@ -27,7 +27,7 @@ namespace art { inline ScopedFastNativeObjectAccess::ScopedFastNativeObjectAccess(JNIEnv* env) : ScopedObjectAccessAlreadyRunnable(env) { Locks::mutator_lock_->AssertSharedHeld(Self()); - DCHECK((*Self()->GetManagedStack()->GetTopQuickFrame())->IsAnnotatedWithFastNative()); + DCHECK((*Self()->GetManagedStack()->GetTopQuickFrame())->IsFastNative()); // Don't work with raw objects in non-runnable states. DCHECK_EQ(Self()->GetState(), kRunnable); } -- GitLab From a759f02277ec4a220631593a0bf16b37e614266d Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 10 Nov 2017 09:26:42 +0000 Subject: [PATCH 017/226] Blacklist lbjdwp flaky test. bug: 69121056 Change-Id: I3239964425c1ca274e33518fe91f5ef11be1d018 --- tools/libjdwp_art_failures.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/libjdwp_art_failures.txt b/tools/libjdwp_art_failures.txt index 646a96adbb..354bee81af 100644 --- a/tools/libjdwp_art_failures.txt +++ b/tools/libjdwp_art_failures.txt @@ -91,5 +91,11 @@ "org.apache.harmony.jpda.tests.jdwp.ThreadGroupReference.NameTest#testName001_NullObject", "org.apache.harmony.jpda.tests.jdwp.ThreadGroupReference.ParentTest#testParent_NullObject", "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.CapabilitiesNewTest#testCapabilitiesNew001" ] +}, +{ + description: "Test is flaky", + result: EXEC_FAILED, + bug: 69121056, + name: "org.apache.harmony.jpda.tests.jdwp.ObjectReference.IsCollectedTest#testIsCollected001" } ] -- GitLab From f97702ce1a3cb5dfdab174712eedba8e6028bb2a Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 10 Nov 2017 16:18:59 +0000 Subject: [PATCH 018/226] Blacklist test in file actually used by the script. bug: 69121056 Change-Id: I4312e2b4f45e0dc1eaadaf483ee9e0eac0dc49bd --- tools/libjdwp_oj_art_failures.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/libjdwp_oj_art_failures.txt b/tools/libjdwp_oj_art_failures.txt index e0f243ccfb..787c4d28fb 100644 --- a/tools/libjdwp_oj_art_failures.txt +++ b/tools/libjdwp_oj_art_failures.txt @@ -61,5 +61,11 @@ "org.apache.harmony.jpda.tests.jdwp.ThreadGroupReference.NameTest#testName001_NullObject", "org.apache.harmony.jpda.tests.jdwp.ThreadGroupReference.ParentTest#testParent_NullObject", "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.CapabilitiesNewTest#testCapabilitiesNew001" ] +}, +{ + description: "Test is flaky", + result: EXEC_FAILED, + bug: 69121056, + name: "org.apache.harmony.jpda.tests.jdwp.ObjectReference.IsCollectedTest#testIsCollected001" } ] -- GitLab From 8d8299d926d7fd5309d55d6beedd092e741c6e80 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Fri, 10 Nov 2017 08:41:04 -0800 Subject: [PATCH 019/226] Fix run-jdwp-tests.sh when running against RI. We were incorrectly trying to pass --toolchain dx instead of --toolchain javac when running against the RI. Test: ./art/tools/run-jdwp-tests.sh --mode=jvm Change-Id: Ie370afd6cb60fbad8e4171b94dd8885f674fface --- 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 db8c54056d..f5fbcd8def 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -284,8 +284,10 @@ fi if [[ $using_jack == "true" ]]; then toolchain_args="--toolchain jack --language JN --jack-arg -g" -else +elif [[ $mode != "ri" ]]; then toolchain_args="--toolchain dx --language CUR" +else + toolchain_args="--toolchain javac --language CUR" fi # Run the tests using vogar. -- GitLab From 62a2f27f02e23c28bf010ca22bfd28b36a69c8f2 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 10 Nov 2017 16:46:43 +0000 Subject: [PATCH 020/226] Move code around to fix --inline-max-code-units in JIT. Test: set --inline-max-code-units=0 and --verbose:compiler, and see all inlining attempts abort. bug: 65622511 Change-Id: I11a512cc690a8db3a092962b618d96f64cffd264 --- compiler/jit/jit_compiler.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index 5c89869e00..0c82d601a7 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -80,6 +80,9 @@ extern "C" void jit_types_loaded(void* handle, mirror::Class** types, size_t cou JitCompiler::JitCompiler() { compiler_options_.reset(new CompilerOptions()); + // Special case max code units for inlining, whose default is "unset" (implictly + // meaning no limit). Do this before parsing the actuall passed options. + compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits); { std::string error_msg; if (!compiler_options_->ParseCompilerOptions(Runtime::Current()->GetCompilerOptions(), @@ -95,10 +98,6 @@ JitCompiler::JitCompiler() { // Set debuggability based on the runtime value. compiler_options_->SetDebuggable(Runtime::Current()->IsJavaDebuggable()); - // Special case max code units for inlining, whose default is "unset" (implictly - // meaning no limit). - compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits); - const InstructionSet instruction_set = kRuntimeISA; for (const StringPiece option : Runtime::Current()->GetCompilerOptions()) { VLOG(compiler) << "JIT compiler option " << option; -- GitLab From c9b46d5594ddec0a0685d5adccab67596a3e1252 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Fri, 10 Nov 2017 09:43:43 -0800 Subject: [PATCH 021/226] Remove unused declarations of dead code. Bug: N/A Test: N/A Change-Id: I555f9892e5833c15c77a157365343f7314a7d027 --- runtime/gc/allocator/dlmalloc.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/runtime/gc/allocator/dlmalloc.h b/runtime/gc/allocator/dlmalloc.h index c07da5da4d..29b96ee96c 100644 --- a/runtime/gc/allocator/dlmalloc.h +++ b/runtime/gc/allocator/dlmalloc.h @@ -35,13 +35,6 @@ #include "../../external/dlmalloc/malloc.h" #pragma GCC diagnostic pop -#ifdef ART_TARGET_ANDROID -// Define dlmalloc routines from bionic that cannot be included directly because of redefining -// symbols from the include above. -extern "C" void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), void* arg); -extern "C" int dlmalloc_trim(size_t); -#endif - // Callback for dlmalloc_inspect_all or mspace_inspect_all that will madvise(2) unused // pages back to the kernel. extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* /*arg*/); -- GitLab From 4720046dfa1c3f396ac7dbcd3fe6179c1230bed1 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 9 Nov 2017 11:40:03 -0800 Subject: [PATCH 022/226] ART: Fix test dependencies Ensure we only depend on (installed) target binaries, to avoid building host dependencies when not necessary. Use module names plus -host/-target to refer to the installed files, simplifying the test Makefiles. Test: art/tools/buildbot-build.sh --mode host Test: art/tools/buildbot-build.sh --mode target Change-Id: If6299487284af0b160c67408f9da421c69ca0e85 --- build/Android.gtest.mk | 58 ++++++++++++++++++++-------------------- test/Android.run-test.mk | 36 +++++-------------------- 2 files changed, 35 insertions(+), 59 deletions(-) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 7769aad1df..454caa938b 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -146,13 +146,13 @@ ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \ $(HOST_CORE_IMAGE_optimizing_32) \ $(HOST_CORE_IMAGE_interpreter_64) \ $(HOST_CORE_IMAGE_interpreter_32) \ - $(HOST_OUT_EXECUTABLES)/patchoatd + patchoatd-host ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_optimizing_64) \ $(TARGET_CORE_IMAGE_optimizing_32) \ $(TARGET_CORE_IMAGE_interpreter_64) \ $(TARGET_CORE_IMAGE_interpreter_32) \ - $(TARGET_OUT_EXECUTABLES)/patchoatd + patchoatd-target ART_GTEST_oat_file_assistant_test_HOST_DEPS := \ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) @@ -161,10 +161,10 @@ ART_GTEST_oat_file_assistant_test_TARGET_DEPS := \ ART_GTEST_dexoptanalyzer_test_HOST_DEPS := \ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) \ - $(HOST_OUT_EXECUTABLES)/dexoptanalyzerd + dexoptanalyzerd-host ART_GTEST_dexoptanalyzer_test_TARGET_DEPS := \ $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \ - dexoptanalyzerd + dexoptanalyzerd-target ART_GTEST_image_space_test_HOST_DEPS := \ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) @@ -172,57 +172,59 @@ ART_GTEST_image_space_test_TARGET_DEPS := \ $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) ART_GTEST_dex2oat_test_HOST_DEPS := \ - $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) + $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) \ + dex2oatd-host ART_GTEST_dex2oat_test_TARGET_DEPS := \ - $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) + $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \ + dex2oatd-target ART_GTEST_dex2oat_image_test_HOST_DEPS := \ - $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) + $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) \ + dex2oatd-host ART_GTEST_dex2oat_image_test_TARGET_DEPS := \ - $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) + $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \ + dex2oatd-target # TODO: document why this is needed. ART_GTEST_proxy_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE_IMAGE_DEFAULT_32) # The dexdiag test requires the dexdiag utility. -ART_GTEST_dexdiag_test_HOST_DEPS := \ - $(HOST_OUT_EXECUTABLES)/dexdiag -ART_GTEST_dexdiag_test_TARGET_DEPS := \ - dexdiag +ART_GTEST_dexdiag_test_HOST_DEPS := dexdiag-host +ART_GTEST_dexdiag_test_TARGET_DEPS := dexdiag-target # The dexdump test requires an image and the dexdump utility. # TODO: rename into dexdump when migration completes ART_GTEST_dexdump_test_HOST_DEPS := \ $(HOST_CORE_IMAGE_DEFAULT_64) \ $(HOST_CORE_IMAGE_DEFAULT_32) \ - $(HOST_OUT_EXECUTABLES)/dexdump2 + dexdump2-host ART_GTEST_dexdump_test_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_DEFAULT_64) \ $(TARGET_CORE_IMAGE_DEFAULT_32) \ - dexdump2 + dexdump2-target # The dexlayout test requires an image and the dexlayout utility. # TODO: rename into dexdump when migration completes ART_GTEST_dexlayout_test_HOST_DEPS := \ $(HOST_CORE_IMAGE_DEFAULT_64) \ $(HOST_CORE_IMAGE_DEFAULT_32) \ - $(HOST_OUT_EXECUTABLES)/dexlayout \ - $(HOST_OUT_EXECUTABLES)/dexdump2 + dexlayout-host \ + dexdump2-host ART_GTEST_dexlayout_test_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_DEFAULT_64) \ $(TARGET_CORE_IMAGE_DEFAULT_32) \ - dexlayout \ - dexdump2 + dexlayout-target \ + dexdump2-target # The dexlist test requires an image and the dexlist utility. ART_GTEST_dexlist_test_HOST_DEPS := \ $(HOST_CORE_IMAGE_DEFAULT_64) \ $(HOST_CORE_IMAGE_DEFAULT_32) \ - $(HOST_OUT_EXECUTABLES)/dexlist + dexlist-host ART_GTEST_dexlist_test_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_DEFAULT_64) \ $(TARGET_CORE_IMAGE_DEFAULT_32) \ - dexlist + dexlist-target # The imgdiag test has dependencies on core.oat since it needs to load it during the test. # For the host, also add the installed tool (in the base size, that should suffice). For the @@ -230,30 +232,28 @@ ART_GTEST_dexlist_test_TARGET_DEPS := \ ART_GTEST_imgdiag_test_HOST_DEPS := \ $(HOST_CORE_IMAGE_DEFAULT_64) \ $(HOST_CORE_IMAGE_DEFAULT_32) \ - $(HOST_OUT_EXECUTABLES)/imgdiagd + imgdiagd-host ART_GTEST_imgdiag_test_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_DEFAULT_64) \ $(TARGET_CORE_IMAGE_DEFAULT_32) \ - imgdiagd + imgdiagd-target # Oatdump test requires an image and oatfile to dump. ART_GTEST_oatdump_test_HOST_DEPS := \ $(HOST_CORE_IMAGE_DEFAULT_64) \ $(HOST_CORE_IMAGE_DEFAULT_32) \ - $(HOST_OUT_EXECUTABLES)/oatdumpd \ - $(HOST_OUT_EXECUTABLES)/oatdumpds + oatdumpd-host \ + oatdumpds-host ART_GTEST_oatdump_test_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_DEFAULT_64) \ $(TARGET_CORE_IMAGE_DEFAULT_32) \ - oatdump + 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) # Profile assistant tests requires profman utility. -ART_GTEST_profile_assistant_test_HOST_DEPS := \ - $(HOST_OUT_EXECUTABLES)/profmand -ART_GTEST_profile_assistant_test_TARGET_DEPS := \ - profman +ART_GTEST_profile_assistant_test_HOST_DEPS := profmand-host +ART_GTEST_profile_assistant_test_TARGET_DEPS := profman-target # The path for which all the source files are relative, not actually the current directory. LOCAL_PATH := art diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 69e4b874f8..8755f04320 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -40,46 +40,22 @@ endef # name-to-var TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUTS) # Also need libartagent. -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libartagent) -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libartagentd) -ifdef TARGET_2ND_ARCH -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libartagent) -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libartagentd) -endif +TEST_ART_TARGET_SYNC_DEPS += libartagent-target libartagentd-target # Also need libtiagent. -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libtiagent) -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libtiagentd) -ifdef TARGET_2ND_ARCH -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libtiagent) -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libtiagentd) -endif +TEST_ART_TARGET_SYNC_DEPS += libtiagent-target libtiagentd-target # Also need libtistress. -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libtistress) -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libtistressd) -ifdef TARGET_2ND_ARCH -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libtistress) -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libtistressd) -endif +TEST_ART_TARGET_SYNC_DEPS += libtistress-target libtistressd-target # Also need libarttest. -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libarttest) -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libarttestd) -ifdef TARGET_2ND_ARCH -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libarttest) -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libarttestd) -endif +TEST_ART_TARGET_SYNC_DEPS += libarttest-target libarttestd-target # Also need libnativebridgetest. -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libnativebridgetest) -ifdef TARGET_2ND_ARCH -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libnativebridgetest) -endif +TEST_ART_TARGET_SYNC_DEPS += libnativebridgetest-target # Also need libopenjdkjvmti. -TEST_ART_TARGET_SYNC_DEPS += libopenjdkjvmti -TEST_ART_TARGET_SYNC_DEPS += libopenjdkjvmtid +TEST_ART_TARGET_SYNC_DEPS += libopenjdkjvmti-target libopenjdkjvmtid-target TEST_ART_TARGET_SYNC_DEPS += $(TARGET_OUT_JAVA_LIBRARIES)/core-libart-testdex.jar TEST_ART_TARGET_SYNC_DEPS += $(TARGET_OUT_JAVA_LIBRARIES)/core-oj-testdex.jar -- GitLab From 0e3151b70450ce3ed81543c4b22d99069713f979 Mon Sep 17 00:00:00 2001 From: Mingyao Yang Date: Mon, 30 Oct 2017 11:19:57 -0700 Subject: [PATCH 023/226] Improvement on array element aliasing analysis. More robust array element aliasing analysis which covers aliasing between all heap locations. Test: run-test on host. Test: 530-checker-lse/load_store_analysis_test.cc cover array index aliasing. Change-Id: Ie0d1696bedb7811c6a4bc0786ef93ca724493de2 --- compiler/optimizing/load_store_analysis.h | 37 ++++++++++--------- compiler/optimizing/load_store_elimination.cc | 6 ++- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h index 999026cb6a..aa8b5bbdc9 100644 --- a/compiler/optimizing/load_store_analysis.h +++ b/compiler/optimizing/load_store_analysis.h @@ -32,8 +32,7 @@ class ReferenceInfo : public ArenaObject { position_(pos), is_singleton_(true), is_singleton_and_not_returned_(true), - is_singleton_and_not_deopt_visible_(true), - has_index_aliasing_(false) { + is_singleton_and_not_deopt_visible_(true) { CalculateEscape(reference_, nullptr, &is_singleton_, @@ -70,16 +69,6 @@ class ReferenceInfo : public ArenaObject { (!is_singleton_and_not_returned_ || !is_singleton_and_not_deopt_visible_); } - bool HasIndexAliasing() { - return has_index_aliasing_; - } - - void SetHasIndexAliasing(bool has_index_aliasing) { - // Only allow setting to true. - DCHECK(has_index_aliasing); - has_index_aliasing_ = has_index_aliasing; - } - private: HInstruction* const reference_; const size_t position_; // position in HeapLocationCollector's ref_info_array_. @@ -90,9 +79,6 @@ class ReferenceInfo : public ArenaObject { bool is_singleton_and_not_returned_; // Is singleton and not used as an environment local of HDeoptimize. bool is_singleton_and_not_deopt_visible_; - // Some heap locations with reference_ have array index aliasing, - // e.g. arr[i] and arr[j] may be the same location. - bool has_index_aliasing_; DISALLOW_COPY_AND_ASSIGN(ReferenceInfo); }; @@ -117,7 +103,8 @@ class HeapLocation : public ArenaObject { index_(index), vector_length_(vector_length), declaring_class_def_index_(declaring_class_def_index), - value_killed_by_loop_side_effects_(true) { + value_killed_by_loop_side_effects_(true), + has_aliased_locations_(false) { DCHECK(ref_info != nullptr); DCHECK((offset == kInvalidFieldOffset && index != nullptr) || (offset != kInvalidFieldOffset && index == nullptr)); @@ -151,6 +138,14 @@ class HeapLocation : public ArenaObject { value_killed_by_loop_side_effects_ = val; } + bool HasAliasedLocations() const { + return has_aliased_locations_; + } + + void SetHasAliasedLocations(bool val) { + has_aliased_locations_ = val; + } + private: // Reference for instance/static field, array element or vector data. ReferenceInfo* const ref_info_; @@ -173,6 +168,11 @@ class HeapLocation : public ArenaObject { // value may be killed by loop side effects. bool value_killed_by_loop_side_effects_; + // Has aliased heap locations in the method, due to either the + // reference is aliased or the array element is aliased via different + // index names. + bool has_aliased_locations_; + DISALLOW_COPY_AND_ASSIGN(HeapLocation); }; @@ -377,6 +377,7 @@ class HeapLocationCollector : public HGraphVisitor { // Compute if two locations may alias to each other. bool ComputeMayAlias(size_t index1, size_t index2) const { + DCHECK_NE(index1, index2); HeapLocation* loc1 = heap_locations_[index1]; HeapLocation* loc2 = heap_locations_[index2]; if (loc1->GetOffset() != loc2->GetOffset()) { @@ -399,9 +400,9 @@ class HeapLocationCollector : public HGraphVisitor { if (!CanArrayElementsAlias(idx1, vector_length1, idx2, vector_length2)) { return false; } - ReferenceInfo* ref_info = loc1->GetReferenceInfo(); - ref_info->SetHasIndexAliasing(true); } + loc1->SetHasAliasedLocations(true); + loc2->SetHasAliasedLocations(true); return true; } diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 66806d8afc..7d953c0963 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -392,8 +392,10 @@ class LSEVisitor : public HGraphDelegateVisitor { if (Equal(heap_value, value)) { // Store into the heap location with the same value. same_value = true; - } else if (index != nullptr && ref_info->HasIndexAliasing()) { - // For array element, don't eliminate stores if the index can be aliased. + } else if (index != nullptr && + heap_location_collector_.GetHeapLocation(idx)->HasAliasedLocations()) { + // For array element, don't eliminate stores if the location can be aliased + // (due to either ref or index aliasing). } else if (ref_info->IsSingleton()) { // Store into a field/element of a singleton. The value cannot be killed due to // aliasing/invocation. It can be redundant since future loads can -- GitLab From 693bfbfce9867f19573d352bd754c81c2f0f91a4 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Fri, 10 Nov 2017 12:23:31 -0800 Subject: [PATCH 024/226] ART: Fix test dependencies and code Let profman target test correctly depend on profmand. Create a debug version of dexlayout to be used in dexlayout_test. Fix a bug in ProfileCompilationInfo referring to a nonexisting element. Test: m test-art-host Change-Id: If2220f26c37c25a600399b2637f081c62cae9718 --- build/Android.gtest.mk | 6 +- dexlayout/Android.bp | 26 ++++- dexlayout/dexlayout_test.cc | 129 ++++++++++-------------- runtime/jit/profile_compilation_info.cc | 2 +- 4 files changed, 80 insertions(+), 83 deletions(-) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 454caa938b..42d0ba57cf 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -208,12 +208,12 @@ ART_GTEST_dexdump_test_TARGET_DEPS := \ ART_GTEST_dexlayout_test_HOST_DEPS := \ $(HOST_CORE_IMAGE_DEFAULT_64) \ $(HOST_CORE_IMAGE_DEFAULT_32) \ - dexlayout-host \ + dexlayoutd-host \ dexdump2-host ART_GTEST_dexlayout_test_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_DEFAULT_64) \ $(TARGET_CORE_IMAGE_DEFAULT_32) \ - dexlayout-target \ + dexlayoutd-target \ dexdump2-target # The dexlist test requires an image and the dexlist utility. @@ -253,7 +253,7 @@ ART_GTEST_oatdump_image_test_TARGET_DEPS := $(ART_GTEST_oatdump_test_TARGET_DEPS # Profile assistant tests requires profman utility. ART_GTEST_profile_assistant_test_HOST_DEPS := profmand-host -ART_GTEST_profile_assistant_test_TARGET_DEPS := profman-target +ART_GTEST_profile_assistant_test_TARGET_DEPS := profmand-target # The path for which all the source files are relative, not actually the current directory. LOCAL_PATH := art diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp index fabe6e785c..a02f75ad00 100644 --- a/dexlayout/Android.bp +++ b/dexlayout/Android.bp @@ -45,16 +45,34 @@ art_cc_library { shared_libs: ["libartd"], } -art_cc_binary { - name: "dexlayout", +cc_defaults { + name: "dexlayout-defaults", defaults: ["art_defaults"], host_supported: true, srcs: ["dexlayout_main.cc"], - cflags: ["-Wall"], + shared_libs: [ + "libbase", + ], +} + +art_cc_binary { + name: "dexlayout", + defaults: ["dexlayout-defaults"], shared_libs: [ "libart", "libart-dexlayout", - "libbase", + ], +} + +art_cc_binary { + name: "dexlayoutd", + defaults: [ + "art_debug_defaults", + "dexlayout-defaults", + ], + shared_libs: [ + "libartd", + "libartd-dexlayout", ], } diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index 08673056d9..2448d0b1aa 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -241,8 +241,8 @@ static void WriteFileBase64(const char* base64, const char* location) { class DexLayoutTest : public CommonRuntimeTest { protected: - virtual void SetUp() { - CommonRuntimeTest::SetUp(); + std::string GetDexLayoutPath() { + return GetTestAndroidRoot() + "/bin/dexlayoutd"; } // Runs FullPlainOutput test. @@ -255,18 +255,16 @@ class DexLayoutTest : public CommonRuntimeTest { ScratchFile dexlayout_output; const std::string& dexlayout_filename = dexlayout_output.GetFilename(); - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; for (const std::string &dex_file : GetLibCoreDexFileNames()) { std::vector dexdump_exec_argv = { dexdump, "-d", "-f", "-h", "-l", "plain", "-o", dexdump_filename, dex_file }; - std::vector dexlayout_exec_argv = - { dexlayout, "-d", "-f", "-h", "-l", "plain", "-o", dexlayout_filename, dex_file }; + std::vector dexlayout_args = + { "-d", "-f", "-h", "-l", "plain", "-o", dexlayout_filename, dex_file }; if (!::art::Exec(dexdump_exec_argv, error_msg)) { return false; } - if (!::art::Exec(dexlayout_exec_argv, error_msg)) { + if (!DexLayoutExec(dexlayout_args, error_msg)) { return false; } std::vector diff_exec_argv = @@ -284,13 +282,11 @@ class DexLayoutTest : public CommonRuntimeTest { const std::string& tmp_name = tmp_file.GetFilename(); size_t tmp_last_slash = tmp_name.rfind('/'); std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1); - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; for (const std::string &dex_file : GetLibCoreDexFileNames()) { - std::vector dexlayout_exec_argv = - { dexlayout, "-w", tmp_dir, "-o", tmp_name, dex_file }; - if (!::art::Exec(dexlayout_exec_argv, error_msg)) { + std::vector dexlayout_args = + { "-w", tmp_dir, "-o", tmp_name, dex_file }; + if (!DexLayoutExec(dexlayout_args, error_msg)) { return false; } size_t dex_file_last_slash = dex_file.rfind('/'); @@ -418,12 +414,9 @@ class DexLayoutTest : public CommonRuntimeTest { // WriteFileBase64(kDexFileLayoutInputProfile, profile_file.c_str()); std::string output_dex = tmp_dir + "classes.dex.new"; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - - std::vector dexlayout_exec_argv = - { dexlayout, "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, dex_file }; - if (!::art::Exec(dexlayout_exec_argv, error_msg)) { + std::vector dexlayout_args = + { "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, dex_file }; + if (!DexLayoutExec(dexlayout_args, error_msg)) { return false; } @@ -466,13 +459,10 @@ class DexLayoutTest : public CommonRuntimeTest { std::string output_dex = tmp_dir + "classes.dex.new"; std::string second_output_dex = tmp_dir + "classes.dex.new.new"; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - // -v makes sure that the layout did not corrupt the dex file. - std::vector dexlayout_exec_argv = - { dexlayout, "-i", "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, dex_file }; - if (!::art::Exec(dexlayout_exec_argv, error_msg)) { + std::vector dexlayout_args = + { "-i", "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, dex_file }; + if (!DexLayoutExec(dexlayout_args, error_msg)) { return false; } @@ -482,9 +472,9 @@ class DexLayoutTest : public CommonRuntimeTest { // -v makes sure that the layout did not corrupt the dex file. // -i since the checksum won't match from the first layout. - std::vector second_dexlayout_exec_argv = - { dexlayout, "-i", "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, output_dex }; - if (!::art::Exec(second_dexlayout_exec_argv, error_msg)) { + std::vector second_dexlayout_args = + { "-i", "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, output_dex }; + if (!DexLayoutExec(second_dexlayout_args, error_msg)) { return false; } @@ -516,12 +506,8 @@ class DexLayoutTest : public CommonRuntimeTest { WriteFileBase64(filename, input_dex.c_str()); std::string output_dex = tmp_dir + "classes.dex.new"; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - - std::vector dexlayout_exec_argv = - { dexlayout, "-w", tmp_dir, "-o", "/dev/null", input_dex }; - if (!::art::Exec(dexlayout_exec_argv, error_msg)) { + std::vector dexlayout_args = { "-w", tmp_dir, "-o", "/dev/null", input_dex }; + if (!DexLayoutExec(dexlayout_args, error_msg)) { return false; } @@ -541,7 +527,7 @@ class DexLayoutTest : public CommonRuntimeTest { bool DexLayoutExec(ScratchFile* dex_file, const char* dex_filename, ScratchFile* profile_file, - std::vector& dexlayout_exec_argv) { + const std::vector& dexlayout_args) { if (dex_filename != nullptr) { WriteBase64ToFile(dex_filename, dex_file->GetFile()); EXPECT_EQ(dex_file->GetFile()->Flush(), 0); @@ -549,14 +535,27 @@ class DexLayoutTest : public CommonRuntimeTest { if (profile_file != nullptr) { CreateProfile(dex_file->GetFilename(), profile_file->GetFilename(), dex_file->GetFilename()); } + std::string error_msg; - const bool result = ::art::Exec(dexlayout_exec_argv, &error_msg); + const bool result = DexLayoutExec(dexlayout_args, &error_msg); if (!result) { LOG(ERROR) << "Error: " << error_msg; return false; } return true; } + + bool DexLayoutExec(const std::vector& dexlayout_args, std::string* error_msg) { + std::vector argv; + + std::string dexlayout = GetDexLayoutPath(); + CHECK(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; + argv.push_back(dexlayout); + + argv.insert(argv.end(), dexlayout_args.begin(), dexlayout_args.end()); + + return ::art::Exec(argv, error_msg); + } }; @@ -614,89 +613,72 @@ TEST_F(DexLayoutTest, UnreferencedEndingCatchHandler) { TEST_F(DexLayoutTest, DuplicateOffset) { ScratchFile temp_dex; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - std::vector dexlayout_exec_argv = - { dexlayout, "-a", "-i", "-o", "/dev/null", temp_dex.GetFilename() }; + std::vector dexlayout_args = + { "-a", "-i", "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kDexFileDuplicateOffset, nullptr /* profile_file */, - dexlayout_exec_argv)); + dexlayout_args)); } TEST_F(DexLayoutTest, NullSetRefListElement) { ScratchFile temp_dex; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - std::vector dexlayout_exec_argv = - { dexlayout, "-o", "/dev/null", temp_dex.GetFilename() }; + std::vector dexlayout_args = { "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kNullSetRefListElementInputDex, nullptr /* profile_file */, - dexlayout_exec_argv)); + dexlayout_args)); } TEST_F(DexLayoutTest, MultiClassData) { ScratchFile temp_dex; ScratchFile temp_profile; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - std::vector dexlayout_exec_argv = - { dexlayout, "-p", temp_profile.GetFilename(), "-o", "/dev/null", temp_dex.GetFilename() }; + std::vector dexlayout_args = + { "-p", temp_profile.GetFilename(), "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kMultiClassDataInputDex, &temp_profile, - dexlayout_exec_argv)); + dexlayout_args)); } TEST_F(DexLayoutTest, UnalignedCodeInfo) { ScratchFile temp_dex; ScratchFile temp_profile; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - std::vector dexlayout_exec_argv = - { dexlayout, "-p", temp_profile.GetFilename(), "-o", "/dev/null", temp_dex.GetFilename() }; + std::vector dexlayout_args = + { "-p", temp_profile.GetFilename(), "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kUnalignedCodeInfoInputDex, &temp_profile, - dexlayout_exec_argv)); + dexlayout_args)); } TEST_F(DexLayoutTest, ClassDataBeforeCode) { ScratchFile temp_dex; ScratchFile temp_profile; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - std::vector dexlayout_exec_argv = - { dexlayout, "-p", temp_profile.GetFilename(), "-o", "/dev/null", temp_dex.GetFilename() }; + std::vector dexlayout_args = + { "-p", temp_profile.GetFilename(), "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kClassDataBeforeCodeInputDex, &temp_profile, - dexlayout_exec_argv)); + dexlayout_args)); } TEST_F(DexLayoutTest, UnknownTypeDebugInfo) { ScratchFile temp_dex; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - std::vector dexlayout_exec_argv = - { dexlayout, "-o", "/dev/null", temp_dex.GetFilename() }; + std::vector dexlayout_args = { "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kUnknownTypeDebugInfoInputDex, nullptr /* profile_file */, - dexlayout_exec_argv)); + dexlayout_args)); } TEST_F(DexLayoutTest, DuplicateCodeItem) { ScratchFile temp_dex; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - std::vector dexlayout_exec_argv = - { dexlayout, "-o", "/dev/null", temp_dex.GetFilename() }; + std::vector dexlayout_args = { "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kDuplicateCodeItemInputDex, nullptr /* profile_file */, - dexlayout_exec_argv)); + dexlayout_args)); } // Test that instructions that go past the end of the code items don't cause crashes. @@ -743,14 +725,11 @@ TEST_F(DexLayoutTest, CodeItemOverrun) { CHECK(mutated_successfully) << "Failed to find candidate code item with only one code unit in last instruction."; }); - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - std::vector dexlayout_exec_argv = - { dexlayout, "-i", "-o", "/dev/null", temp_dex.GetFilename() }; + std::vector dexlayout_args = { "-i", "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, /*dex_filename*/ nullptr, nullptr /* profile_file */, - dexlayout_exec_argv)); + dexlayout_args)); } } // namespace art diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index 805b9c185a..bb8e5e5c15 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -1034,7 +1034,7 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine return kProfileLoadBadData; } const uint8_t* base_ptr = buffer.GetCurrentPtr(); - std::copy_n(base_ptr, bytes, &data->bitmap_storage[0]); + std::copy_n(base_ptr, bytes, data->bitmap_storage.data()); buffer.Advance(bytes); // Read method bitmap. return kProfileLoadSuccess; -- GitLab From 66b223c840a9385a6163ad6d4ef6e31b257a9102 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 9 Nov 2017 14:47:57 -0800 Subject: [PATCH 025/226] ART: Fix run-test 165 Add OOME try-catch. Bug: 69121347 Test: art/test/testrunner/testrunner.py -b --host -t 165 Change-Id: Idf4a037c1df7a5f9838b38cb4d7e26438ae7acc6 --- test/165-lock-owner-proxy/src/Main.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/test/165-lock-owner-proxy/src/Main.java b/test/165-lock-owner-proxy/src/Main.java index 1b1694d2c1..fff8e58606 100644 --- a/test/165-lock-owner-proxy/src/Main.java +++ b/test/165-lock-owner-proxy/src/Main.java @@ -65,8 +65,13 @@ public class Main { int count = totalOperations; while (count > 0) { synchronized (lockObject) { - inf.foo(10000 - count, 11000 - count, 12000 - count, 13000 - count, - 14000 - count, 15000 - count); + try { + inf.foo(10000 - count, 11000 - count, 12000 - count, 13000 - count, + 14000 - count, 15000 - count); + } catch (OutOfMemoryError e) { + // Ignore errors. This is the test for b/69121347 - see an exception + // instead of native abort. + } } count--; } @@ -96,7 +101,12 @@ public class Main { while (!finish) { // Some random allocations adding up to almost 2M. for (int i = 0; i < 188; i++) { - byte b[] = new byte[i * 100 + 10]; + try { + byte b[] = new byte[i * 100 + 10]; + } catch (OutOfMemoryError e) { + // Ignore. This is just to improve chances that an OOME is thrown during + // proxy invocation. + } } try { Thread.sleep(10); -- GitLab From d5c0a0a7f911d8954ad58cc35eedd330a95a6eb3 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 10 Nov 2017 14:16:34 -0800 Subject: [PATCH 026/226] Move oatdump to use code item accessor Bug: 63756964 Test: test-art-host Test: clean-oat-host && dump-oat-boot Change-Id: I26dae94761ae38d44ccaa372a468013a9a13ce3d --- oatdump/oatdump.cc | 115 ++++++++++++++++-------------- runtime/code_item_accessors-inl.h | 13 ++-- runtime/code_item_accessors.h | 3 + 3 files changed, 75 insertions(+), 56 deletions(-) diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index b20fa902a2..8334a627f7 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -36,6 +36,7 @@ #include "base/unix_file/fd_file.h" #include "class_linker-inl.h" #include "class_linker.h" +#include "code_item_accessors-inl.h" #include "compiled_method.h" #include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" @@ -994,14 +995,15 @@ class OatDumper { if (code_item == nullptr) { return; } + CodeItemInstructionAccessor instructions(&dex_file, code_item); - const uint16_t* code_ptr = code_item->insns_; // 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_ += code_item->insns_size_in_code_units_ * sizeof(code_ptr[0]); + dex_code_bytes_ += instructions.InsnsSizeInCodeUnits() * sizeof(code_ptr[0]); } - for (const DexInstructionPcPair& inst : code_item->Instructions()) { + for (const DexInstructionPcPair& inst : instructions) { switch (inst->Opcode()) { case Instruction::CONST_STRING: { const dex::StringIndex string_index(inst->VRegB_21c()); @@ -1266,11 +1268,17 @@ class OatDumper { bool DumpOatMethod(VariableIndentationOutputStream* vios, const DexFile::ClassDef& class_def, uint32_t class_method_index, - const OatFile::OatClass& oat_class, const DexFile& dex_file, - uint32_t dex_method_idx, const DexFile::CodeItem* code_item, - uint32_t method_access_flags, bool* addr_found) { + const OatFile::OatClass& oat_class, + const DexFile& dex_file, + uint32_t dex_method_idx, + const DexFile::CodeItem* code_item, + uint32_t method_access_flags, + bool* addr_found) { bool success = true; + CodeItemDataAccessor code_item_accessor(CodeItemDataAccessor::CreateNullable(&dex_file, + code_item)); + // TODO: Support regex std::string method_name = dex_file.GetMethodName(dex_file.GetMethodId(dex_method_idx)); if (method_name.find(options_.method_filter_) == std::string::npos) { @@ -1281,7 +1289,9 @@ class OatDumper { vios->Stream() << StringPrintf("%d: %s (dex_method_idx=%d)\n", class_method_index, pretty_method.c_str(), dex_method_idx); - if (options_.list_methods_) return success; + if (options_.list_methods_) { + return success; + } uint32_t oat_method_offsets_offset = oat_class.GetOatMethodOffsetsOffset(class_method_index); const OatMethodOffsets* oat_method_offsets = oat_class.GetOatMethodOffsets(class_method_index); @@ -1302,7 +1312,12 @@ class OatDumper { { vios->Stream() << "DEX CODE:\n"; ScopedIndentation indent2(vios); - DumpDexCode(vios->Stream(), dex_file, code_item); + if (code_item_accessor.HasCodeItem()) { + for (const DexInstructionPcPair& inst : code_item_accessor) { + vios->Stream() << StringPrintf("0x%04x: ", inst.DexPc()) << inst->DumpHexLE(5) + << StringPrintf("\t| %s\n", inst->DumpString(&dex_file).c_str()); + } + } } std::unique_ptr> hs; @@ -1373,7 +1388,7 @@ class OatDumper { vios->Stream() << StringPrintf("(offset=0x%08x)\n", vmap_table_offset); size_t vmap_table_offset_limit = - (kIsVdexEnabled && IsMethodGeneratedByDexToDexCompiler(oat_method, code_item)) + (kIsVdexEnabled && IsMethodGeneratedByDexToDexCompiler(oat_method, code_item_accessor)) ? oat_file_.GetVdexFile()->Size() : method_header->GetCode() - oat_file_.Begin(); if (vmap_table_offset >= vmap_table_offset_limit) { @@ -1385,7 +1400,7 @@ class OatDumper { oat_method.GetVmapTableOffsetOffset()); success = false; } else if (options_.dump_vmap_) { - DumpVmapData(vios, oat_method, code_item); + DumpVmapData(vios, oat_method, code_item_accessor); } } { @@ -1406,7 +1421,7 @@ class OatDumper { // after it is dumped, but useful for understanding quick // code, so dumped here. ScopedIndentation indent2(vios); - DumpVregLocations(vios->Stream(), oat_method, code_item); + DumpVregLocations(vios->Stream(), oat_method, code_item_accessor); } { vios->Stream() << "CODE: "; @@ -1448,7 +1463,7 @@ class OatDumper { success = false; if (options_.disassemble_code_) { if (code_size_offset + kPrologueBytes <= oat_file_.Size()) { - DumpCode(vios, oat_method, code_item, true, kPrologueBytes); + DumpCode(vios, oat_method, code_item_accessor, true, kPrologueBytes); } } } else if (code_size > kMaxCodeSize) { @@ -1461,11 +1476,11 @@ class OatDumper { success = false; if (options_.disassemble_code_) { if (code_size_offset + kPrologueBytes <= oat_file_.Size()) { - DumpCode(vios, oat_method, code_item, true, kPrologueBytes); + DumpCode(vios, oat_method, code_item_accessor, true, kPrologueBytes); } } } else if (options_.disassemble_code_) { - DumpCode(vios, oat_method, code_item, !success, 0); + DumpCode(vios, oat_method, code_item_accessor, !success, 0); } } } @@ -1499,18 +1514,18 @@ class OatDumper { // Display data stored at the the vmap offset of an oat method. void DumpVmapData(VariableIndentationOutputStream* vios, const OatFile::OatMethod& oat_method, - const DexFile::CodeItem* code_item) { - if (IsMethodGeneratedByOptimizingCompiler(oat_method, code_item)) { + const CodeItemDataAccessor& code_item_accessor) { + if (IsMethodGeneratedByOptimizingCompiler(oat_method, code_item_accessor)) { // The optimizing compiler outputs its CodeInfo data in the vmap table. const void* raw_code_info = oat_method.GetVmapTable(); if (raw_code_info != nullptr) { CodeInfo code_info(raw_code_info); - DCHECK(code_item != nullptr); + DCHECK(code_item_accessor.HasCodeItem()); ScopedIndentation indent1(vios); MethodInfo method_info = oat_method.GetOatQuickMethodHeader()->GetOptimizedMethodInfo(); - DumpCodeInfo(vios, code_info, oat_method, *code_item, method_info); + DumpCodeInfo(vios, code_info, oat_method, code_item_accessor, method_info); } - } else if (IsMethodGeneratedByDexToDexCompiler(oat_method, code_item)) { + } else if (IsMethodGeneratedByDexToDexCompiler(oat_method, code_item_accessor)) { // We don't encode the size in the table, so just emit that we have quickened // information. ScopedIndentation indent(vios); @@ -1524,11 +1539,11 @@ class OatDumper { void DumpCodeInfo(VariableIndentationOutputStream* vios, const CodeInfo& code_info, const OatFile::OatMethod& oat_method, - const DexFile::CodeItem& code_item, + const CodeItemDataAccessor& code_item_accessor, const MethodInfo& method_info) { code_info.Dump(vios, oat_method.GetCodeOffset(), - code_item.registers_size_, + code_item_accessor.RegistersSize(), options_.dump_code_info_stack_maps_, instruction_set_, method_info); @@ -1539,7 +1554,7 @@ class OatDumper { return static_cast(InstructionSetPointerSize(isa)) + out_num * sizeof(uint32_t); } - static uint32_t GetVRegOffsetFromQuickCode(const DexFile::CodeItem* code_item, + static uint32_t GetVRegOffsetFromQuickCode(const CodeItemDataAccessor& code_item_accessor, uint32_t core_spills, uint32_t fp_spills, size_t frame_size, @@ -1557,8 +1572,8 @@ class OatDumper { int spill_size = POPCOUNT(core_spills) * GetBytesPerGprSpillLocation(isa) + POPCOUNT(fp_spills) * GetBytesPerFprSpillLocation(isa) + sizeof(uint32_t); // Filler. - int num_regs = code_item->registers_size_ - code_item->ins_size_; - int temp_threshold = code_item->registers_size_; + int num_regs = code_item_accessor.RegistersSize() - code_item_accessor.InsSize(); + int temp_threshold = code_item_accessor.RegistersSize(); const int max_num_special_temps = 1; if (reg == temp_threshold) { // The current method pointer corresponds to special location on stack. @@ -1568,7 +1583,7 @@ class OatDumper { * Special temporaries may have custom locations and the logic above deals with that. * However, non-special temporaries are placed relative to the outs. */ - int temps_start = code_item->outs_size_ * sizeof(uint32_t) + int temps_start = code_item_accessor.OutsSize() * sizeof(uint32_t) + static_cast(pointer_size) /* art method */; int relative_offset = (reg - (temp_threshold + max_num_special_temps)) * sizeof(uint32_t); return temps_start + relative_offset; @@ -1583,12 +1598,12 @@ class OatDumper { } void DumpVregLocations(std::ostream& os, const OatFile::OatMethod& oat_method, - const DexFile::CodeItem* code_item) { - if (code_item != nullptr) { - size_t num_locals_ins = code_item->registers_size_; - size_t num_ins = code_item->ins_size_; + const CodeItemDataAccessor& code_item_accessor) { + if (code_item_accessor.HasCodeItem()) { + size_t num_locals_ins = code_item_accessor.RegistersSize(); + size_t num_ins = code_item_accessor.InsSize(); size_t num_locals = num_locals_ins - num_ins; - size_t num_outs = code_item->outs_size_; + size_t num_outs = code_item_accessor.OutsSize(); os << "vr_stack_locations:"; for (size_t reg = 0; reg <= num_locals_ins; reg++) { @@ -1601,7 +1616,7 @@ class OatDumper { os << "\n\tlocals:"; } - uint32_t offset = GetVRegOffsetFromQuickCode(code_item, + uint32_t offset = GetVRegOffsetFromQuickCode(code_item_accessor, oat_method.GetCoreSpillMask(), oat_method.GetFpSpillMask(), oat_method.GetFrameSizeInBytes(), @@ -1623,37 +1638,30 @@ class OatDumper { } } - void DumpDexCode(std::ostream& os, const DexFile& dex_file, const DexFile::CodeItem* code_item) { - if (code_item != nullptr) { - for (const DexInstructionPcPair& inst : code_item->Instructions()) { - os << StringPrintf("0x%04x: ", inst.DexPc()) << inst->DumpHexLE(5) - << StringPrintf("\t| %s\n", inst->DumpString(&dex_file).c_str()); - } - } - } - // Has `oat_method` -- corresponding to the Dex `code_item` -- been compiled by // the optimizing compiler? - static bool IsMethodGeneratedByOptimizingCompiler(const OatFile::OatMethod& oat_method, - const DexFile::CodeItem* code_item) { + static bool IsMethodGeneratedByOptimizingCompiler( + const OatFile::OatMethod& oat_method, + const CodeItemDataAccessor& code_item_accessor) { // If the native GC map is null and the Dex `code_item` is not // null, then this method has been compiled with the optimizing // compiler. return oat_method.GetQuickCode() != nullptr && oat_method.GetVmapTable() != nullptr && - code_item != nullptr; + code_item_accessor.HasCodeItem(); } // Has `oat_method` -- corresponding to the Dex `code_item` -- been compiled by // the dextodex compiler? - static bool IsMethodGeneratedByDexToDexCompiler(const OatFile::OatMethod& oat_method, - const DexFile::CodeItem* code_item) { + static bool IsMethodGeneratedByDexToDexCompiler( + const OatFile::OatMethod& oat_method, + const CodeItemDataAccessor& code_item_accessor) { // If the quick code is null, the Dex `code_item` is not // null, and the vmap table is not null, then this method has been compiled // with the dextodex compiler. return oat_method.GetQuickCode() == nullptr && oat_method.GetVmapTable() != nullptr && - code_item != nullptr; + code_item_accessor.HasCodeItem(); } verifier::MethodVerifier* DumpVerifier(VariableIndentationOutputStream* vios, @@ -1770,7 +1778,8 @@ class OatDumper { }; void DumpCode(VariableIndentationOutputStream* vios, - const OatFile::OatMethod& oat_method, const DexFile::CodeItem* code_item, + const OatFile::OatMethod& oat_method, + const CodeItemDataAccessor& code_item_accessor, bool bad_input, size_t code_size) { const void* quick_code = oat_method.GetQuickCode(); @@ -1780,7 +1789,8 @@ class OatDumper { if (code_size == 0 || quick_code == nullptr) { vios->Stream() << "NO CODE!\n"; return; - } else if (!bad_input && IsMethodGeneratedByOptimizingCompiler(oat_method, code_item)) { + } else if (!bad_input && IsMethodGeneratedByOptimizingCompiler(oat_method, + code_item_accessor)) { // The optimizing compiler outputs its CodeInfo data in the vmap table. StackMapsHelper helper(oat_method.GetVmapTable(), instruction_set_); MethodInfo method_info(oat_method.GetOatQuickMethodHeader()->GetOptimizedMethodInfo()); @@ -1835,7 +1845,8 @@ class OatDumper { kBitsPerByte * location_catalog_bytes); // Dex register bytes. const size_t dex_register_bytes = - helper.GetCodeInfo().GetDexRegisterMapsSize(encoding, code_item->registers_size_); + helper.GetCodeInfo().GetDexRegisterMapsSize(encoding, + code_item_accessor.RegistersSize()); stats_.AddBits( Stats::kByteKindCodeInfoDexRegisterMap, kBitsPerByte * dex_register_bytes); @@ -1874,7 +1885,7 @@ class OatDumper { helper.GetEncoding(), method_info, oat_method.GetCodeOffset(), - code_item->registers_size_, + code_item_accessor.RegistersSize(), instruction_set_); do { helper.Next(); @@ -2502,8 +2513,8 @@ class ImageDumper { } } } else { - const DexFile::CodeItem* code_item = method->GetCodeItem(); - size_t dex_instruction_bytes = code_item->insns_size_in_code_units_ * 2; + CodeItemDataAccessor code_item_accessor(method); + size_t dex_instruction_bytes = code_item_accessor.InsnsSizeInCodeUnits() * 2; stats_.dex_instruction_bytes += dex_instruction_bytes; bool first_occurrence; diff --git a/runtime/code_item_accessors-inl.h b/runtime/code_item_accessors-inl.h index 61b517526d..2c5bc2eb62 100644 --- a/runtime/code_item_accessors-inl.h +++ b/runtime/code_item_accessors-inl.h @@ -113,18 +113,23 @@ inline CodeItemDataAccessor::CodeItemDataAccessor(const DexFile* dex_file, inline CodeItemDataAccessor::CodeItemDataAccessor(ArtMethod* method) : CodeItemDataAccessor(method->GetDexFile(), method->GetCodeItem()) {} -inline CodeItemDataAccessor CodeItemDataAccessor::CreateNullable(ArtMethod* method) { - DCHECK(method != nullptr); +inline CodeItemDataAccessor CodeItemDataAccessor::CreateNullable( + const DexFile* dex_file, + const DexFile::CodeItem* code_item) { CodeItemDataAccessor ret; - const DexFile::CodeItem* code_item = method->GetCodeItem(); if (code_item != nullptr) { - ret.Init(method->GetDexFile(), code_item); + ret.Init(dex_file, code_item); } else { DCHECK(!ret.HasCodeItem()) << "Should be null initialized"; } return ret; } +inline CodeItemDataAccessor CodeItemDataAccessor::CreateNullable(ArtMethod* method) { + DCHECK(method != nullptr); + return CreateNullable(method->GetDexFile(), method->GetCodeItem()); +} + } // namespace art #endif // ART_RUNTIME_CODE_ITEM_ACCESSORS_INL_H_ diff --git a/runtime/code_item_accessors.h b/runtime/code_item_accessors.h index fcece3e0ac..fdc8ac507f 100644 --- a/runtime/code_item_accessors.h +++ b/runtime/code_item_accessors.h @@ -102,6 +102,9 @@ class CodeItemDataAccessor : public CodeItemInstructionAccessor { ALWAYS_INLINE static CodeItemDataAccessor CreateNullable(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); + ALWAYS_INLINE static CodeItemDataAccessor CreateNullable(const DexFile* dex_file, + const DexFile::CodeItem* code_item); + protected: CodeItemDataAccessor() = default; -- GitLab From b7c273cb44fcbdab3c17ec69124fe4bbea2696b1 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 10 Nov 2017 18:07:56 -0800 Subject: [PATCH 027/226] Add ClassDataItemIterator::HasNextMethod Returns true if there are either static of virtual methods remaining, changed most places to use this where possible. Slight behavioral change for duplicate method checking, we not persist the method index across the static method / virtual method boundary. Motivation: Generic cleanup to remove copy paste. Test: test-art-host Change-Id: I7a1b507e681b2c40452f8a9913b53a96b181e171 --- compiler/dex/dex_to_dex_decompiler_test.cc | 14 +--- compiler/driver/compiler_driver.cc | 76 ++++------------------ dex2oat/dex2oat_test.cc | 6 +- dex2oat/linker/oat_writer.cc | 10 +-- dexdump/dexdump.cc | 8 +-- dexdump/dexdump_cfg.cc | 2 +- dexlayout/dexlayout_test.cc | 2 +- dexlist/dexlist.cc | 12 +--- oatdump/oatdump.cc | 29 ++------- openjdkjvmti/ti_redefine.cc | 2 +- profman/boot_image_profile.cc | 2 +- profman/profman.cc | 2 +- runtime/dex_file.h | 5 ++ runtime/dex_file_tracking_registrar.cc | 8 +-- runtime/dex_file_verifier.cc | 4 +- runtime/dex_file_verifier_test.cc | 2 +- 16 files changed, 42 insertions(+), 142 deletions(-) diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc index 6637be2811..979c4c4ce2 100644 --- a/compiler/dex/dex_to_dex_decompiler_test.cc +++ b/compiler/dex/dex_to_dex_decompiler_test.cc @@ -91,19 +91,7 @@ class DexToDexDecompilerTest : public CommonCompilerTest { it.SkipAllFields(); // Unquicken each method. - while (it.HasNextDirectMethod()) { - uint32_t method_idx = it.GetMemberIndex(); - CompiledMethod* compiled_method = - compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx)); - ArrayRef table; - if (compiled_method != nullptr) { - table = compiled_method->GetVmapTable(); - } - optimizer::ArtDecompileDEX( - *it.GetMethodCodeItem(), table, /* decompile_return_instruction */ true); - it.Next(); - } - while (it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { uint32_t method_idx = it.GetMemberIndex(); CompiledMethod* compiled_method = compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx)); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 32d0bbe495..f4700d4040 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -762,31 +762,17 @@ static void ResolveConstStrings(CompilerDriver* driver, continue; } - // Direct methods. - int64_t previous_direct_method_idx = -1; - while (it.HasNextDirectMethod()) { + // Direct and virtual methods. + int64_t previous_method_idx = -1; + while (it.HasNextMethod()) { uint32_t method_idx = it.GetMemberIndex(); - if (method_idx == previous_direct_method_idx) { + 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_direct_method_idx = method_idx; - ResolveConstStrings(dex_cache, *dex_file, it.GetMethodCodeItem()); - it.Next(); - } - // Virtual methods. - int64_t previous_virtual_method_idx = -1; - while (it.HasNextVirtualMethod()) { - uint32_t method_idx = it.GetMemberIndex(); - if (method_idx == previous_virtual_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_virtual_method_idx = method_idx; + previous_method_idx = method_idx; ResolveConstStrings(dex_cache, *dex_file, it.GetMethodCodeItem()); it.Next(); } @@ -1702,16 +1688,7 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { it.Next(); } if (resolve_fields_and_methods) { - while (it.HasNextDirectMethod()) { - ArtMethod* method = class_linker->ResolveMethod( - dex_file, it.GetMemberIndex(), dex_cache, class_loader, nullptr, - it.GetMethodInvokeType(class_def)); - if (method == nullptr) { - CheckAndClearResolveException(soa.Self()); - } - it.Next(); - } - while (it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { ArtMethod* method = class_linker->ResolveMethod( dex_file, it.GetMemberIndex(), dex_cache, class_loader, nullptr, it.GetMethodInvokeType(class_def)); @@ -1820,12 +1797,7 @@ static void PopulateVerifiedMethods(const DexFile& dex_file, ClassDataItemIterator it(dex_file, class_data); it.SkipAllFields(); - while (it.HasNextDirectMethod()) { - verification_results->CreateVerifiedMethodFor(MethodReference(&dex_file, it.GetMemberIndex())); - it.Next(); - } - - while (it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { verification_results->CreateVerifiedMethodFor(MethodReference(&dex_file, it.GetMemberIndex())); it.Next(); } @@ -2752,17 +2724,17 @@ class CompileClassVisitor : public CompilationVisitor { bool compilation_enabled = driver->IsClassToCompile( dex_file.StringByTypeIdx(class_def.class_idx_)); - // Compile direct methods - int64_t previous_direct_method_idx = -1; - while (it.HasNextDirectMethod()) { + // Compile direct and virtual methods. + int64_t previous_method_idx = -1; + while (it.HasNextMethod()) { uint32_t method_idx = it.GetMemberIndex(); - if (method_idx == previous_direct_method_idx) { + 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_direct_method_idx = method_idx; + previous_method_idx = method_idx; CompileMethod(soa.Self(), driver, it.GetMethodCodeItem(), @@ -2777,30 +2749,6 @@ class CompileClassVisitor : public CompilationVisitor { dex_cache); it.Next(); } - // Compile virtual methods - int64_t previous_virtual_method_idx = -1; - while (it.HasNextVirtualMethod()) { - uint32_t method_idx = it.GetMemberIndex(); - if (method_idx == previous_virtual_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_virtual_method_idx = method_idx; - CompileMethod(soa.Self(), - driver, it.GetMethodCodeItem(), - it.GetMethodAccessFlags(), - it.GetMethodInvokeType(class_def), - class_def_index, - method_idx, - class_loader, - dex_file, - dex_to_dex_compilation_level, - compilation_enabled, - dex_cache); - it.Next(); - } DCHECK(!it.HasNext()); } diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index c2556aa4a6..fdada8f926 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -1244,7 +1244,7 @@ TEST_F(Dex2oatTest, LayoutSections) { ClassDataItemIterator it(*dex, dex->GetClassData(*class_def)); it.SkipAllFields(); std::set code_item_offsets; - for (; it.HasNextDirectMethod() || it.HasNextVirtualMethod(); it.Next()) { + for (; it.HasNextMethod(); it.Next()) { const uint16_t method_idx = it.GetMemberIndex(); const size_t code_item_offset = it.GetMethodCodeItemOffset(); if (code_item_offsets.insert(code_item_offset).second) { @@ -1356,7 +1356,7 @@ TEST_F(Dex2oatTest, LayoutSections) { // corresponding code item offsets to verify the layout. ClassDataItemIterator it(*dex_file, dex_file->GetClassData(*class_def)); it.SkipAllFields(); - for (; it.HasNextDirectMethod() || it.HasNextVirtualMethod(); it.Next()) { + for (; it.HasNextMethod(); it.Next()) { const size_t method_idx = it.GetMemberIndex(); const size_t code_item_offset = it.GetMethodCodeItemOffset(); const bool is_hot = ContainsElement(hot_methods, method_idx); @@ -1382,7 +1382,7 @@ TEST_F(Dex2oatTest, LayoutSections) { // or this method is part of the last code item and the end is 4 byte aligned. ClassDataItemIterator it2(*dex_file, dex_file->GetClassData(*class_def)); it2.SkipAllFields(); - for (; it2.HasNextDirectMethod() || it2.HasNextVirtualMethod(); it2.Next()) { + for (; it2.HasNextMethod(); it2.Next()) { EXPECT_LE(it2.GetMethodCodeItemOffset(), code_item_offset); } uint32_t code_item_size = dex_file->FindCodeItemOffset(*class_def, method_idx); diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index d8671d2dd0..1eec59279e 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -2126,20 +2126,14 @@ bool OatWriter::VisitDexMethods(DexMethodVisitor* visitor) { ClassDataItemIterator it(*dex_file, class_data); it.SkipAllFields(); size_t class_def_method_index = 0u; - while (it.HasNextDirectMethod()) { + while (it.HasNextMethod()) { if (!visitor->VisitMethod(class_def_method_index, it)) { return false; } ++class_def_method_index; it.Next(); } - while (it.HasNextVirtualMethod()) { - if (UNLIKELY(!visitor->VisitMethod(class_def_method_index, it))) { - return false; - } - ++class_def_method_index; - it.Next(); - } + DCHECK(!it.HasNext()); } } if (UNLIKELY(!visitor->EndClass())) { diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 4bfd91fdd9..84ccaa03ab 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -1391,18 +1391,12 @@ static void dumpCfg(const DexFile* dex_file, int idx) { } ClassDataItemIterator it(*dex_file, class_data); it.SkipAllFields(); - while (it.HasNextDirectMethod()) { + while (it.HasNextMethod()) { dumpCfg(dex_file, it.GetMemberIndex(), it.GetMethodCodeItem()); it.Next(); } - while (it.HasNextVirtualMethod()) { - dumpCfg(dex_file, - it.GetMemberIndex(), - it.GetMethodCodeItem()); - it.Next(); - } } /* diff --git a/dexdump/dexdump_cfg.cc b/dexdump/dexdump_cfg.cc index 28317071dd..62c970de8a 100644 --- a/dexdump/dexdump_cfg.cc +++ b/dexdump/dexdump_cfg.cc @@ -377,7 +377,7 @@ void DumpMethodCFG(const DexFile* dex_file, uint32_t dex_method_idx, std::ostrea it.SkipAllFields(); // Find method, and dump it. - while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { uint32_t method_idx = it.GetMemberIndex(); if (method_idx == dex_method_idx) { dumpMethodCFGImpl(dex_file, dex_method_idx, it.GetMethodCodeItem(), os); diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index 2448d0b1aa..c4f7accc8c 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -695,7 +695,7 @@ TEST_F(DexLayoutTest, CodeItemOverrun) { } ClassDataItemIterator it(*dex, data); it.SkipAllFields(); - while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { DexFile::CodeItem* item = const_cast(it.GetMethodCodeItem()); if (item != nullptr) { IterationRange instructions = item->Instructions(); diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc index c8bc132da0..e3ca59c8bf 100644 --- a/dexlist/dexlist.cc +++ b/dexlist/dexlist.cc @@ -151,16 +151,8 @@ void dumpClass(const DexFile* pDexFile, u4 idx) { if (pEncodedData != nullptr) { ClassDataItemIterator pClassData(*pDexFile, pEncodedData); pClassData.SkipAllFields(); - // Direct methods. - for (; pClassData.HasNextDirectMethod(); pClassData.Next()) { - dumpMethod(pDexFile, fileName, - pClassData.GetMemberIndex(), - pClassData.GetRawMemberAccessFlags(), - pClassData.GetMethodCodeItem(), - pClassData.GetMethodCodeItemOffset()); - } - // Virtual methods. - for (; pClassData.HasNextVirtualMethod(); pClassData.Next()) { + // Direct and virtual methods. + for (; pClassData.HasNextMethod(); pClassData.Next()) { dumpMethod(pDexFile, fileName, pClassData.GetMemberIndex(), pClassData.GetRawMemberAccessFlags(), diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index b20fa902a2..108fa95225 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -274,7 +274,7 @@ class OatSymbolizer FINAL { ClassDataItemIterator it(dex_file, class_data); uint32_t class_method_idx = 0; it.SkipAllFields(); - for (; it.HasNextDirectMethod() || it.HasNextVirtualMethod(); it.Next()) { + for (; it.HasNextMethod(); it.Next()) { WalkOatMethod(oat_class.GetOatMethod(class_method_idx++), dex_file, class_def_index, @@ -893,11 +893,7 @@ class OatDumper { ClassDataItemIterator it(*dex_file, class_data); it.SkipAllFields(); uint32_t class_method_index = 0; - while (it.HasNextDirectMethod()) { - AddOffsets(oat_class.GetOatMethod(class_method_index++)); - it.Next(); - } - while (it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { AddOffsets(oat_class.GetOatMethod(class_method_index++)); it.Next(); } @@ -979,11 +975,7 @@ class OatDumper { } ClassDataItemIterator it(dex_file, class_data); it.SkipAllFields(); - while (it.HasNextDirectMethod()) { - WalkCodeItem(dex_file, it.GetMethodCodeItem()); - it.Next(); - } - while (it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { WalkCodeItem(dex_file, it.GetMethodCodeItem()); it.Next(); } @@ -1227,20 +1219,7 @@ class OatDumper { ClassDataItemIterator it(dex_file, class_data); it.SkipAllFields(); uint32_t class_method_index = 0; - while (it.HasNextDirectMethod()) { - if (!DumpOatMethod(vios, class_def, class_method_index, oat_class, dex_file, - it.GetMemberIndex(), it.GetMethodCodeItem(), - it.GetRawMemberAccessFlags(), &addr_found)) { - success = false; - } - if (addr_found) { - *stop_analysis = true; - return success; - } - class_method_index++; - it.Next(); - } - while (it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { if (!DumpOatMethod(vios, class_def, class_method_index, oat_class, dex_file, it.GetMemberIndex(), it.GetMethodCodeItem(), it.GetRawMemberAccessFlags(), &addr_found)) { diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index dcc237d6d6..5b125f6990 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -610,7 +610,7 @@ bool Redefiner::ClassRedefinition::CheckSameMethods() { // Check each of the methods. NB we don't need to specifically check for removals since the 2 dex // files have the same number of methods, which means there must be an equal amount of additions // and removals. - for (; new_iter.HasNextVirtualMethod() || new_iter.HasNextDirectMethod(); new_iter.Next()) { + for (; new_iter.HasNextMethod(); new_iter.Next()) { // Get the data on the method we are searching for const art::DexFile::MethodId& new_method_id = dex_file_->GetMethodId(new_iter.GetMemberIndex()); const char* new_method_name = dex_file_->GetMethodName(new_method_id); diff --git a/profman/boot_image_profile.cc b/profman/boot_image_profile.cc index e5645d370c..48b87c9a6d 100644 --- a/profman/boot_image_profile.cc +++ b/profman/boot_image_profile.cc @@ -90,7 +90,7 @@ void GenerateBootImageProfile( it.Next(); } it.SkipInstanceFields(); - while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { const uint32_t flags = it.GetMethodAccessFlags(); if ((flags & kAccNative) != 0) { // Native method will get dirtied. diff --git a/profman/profman.cc b/profman/profman.cc index 1c50645073..31d28e4be0 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -810,7 +810,7 @@ class ProfMan FINAL { if (class_data != nullptr) { ClassDataItemIterator it(*dex_file, class_data); it.SkipAllFields(); - while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) { + 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(); diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 5c0093f323..2c6a430020 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -1186,6 +1186,11 @@ class ClassDataItemIterator { bool HasNextVirtualMethod() const { return pos_ >= EndOfDirectMethodsPos() && pos_ < EndOfVirtualMethodsPos(); } + bool HasNextMethod() const { + const bool result = pos_ >= EndOfInstanceFieldsPos() && pos_ < EndOfVirtualMethodsPos(); + DCHECK_EQ(result, HasNextDirectMethod() || HasNextVirtualMethod()); + return result; + } void SkipStaticFields() { while (HasNextStaticField()) { Next(); diff --git a/runtime/dex_file_tracking_registrar.cc b/runtime/dex_file_tracking_registrar.cc index 341158671b..874d8ea905 100644 --- a/runtime/dex_file_tracking_registrar.cc +++ b/runtime/dex_file_tracking_registrar.cc @@ -158,7 +158,7 @@ void DexFileTrackingRegistrar::SetAllCodeItemRegistration(bool should_poison) { if (class_data != nullptr) { ClassDataItemIterator cdit(*dex_file_, class_data); cdit.SkipAllFields(); - while (cdit.HasNextDirectMethod()) { + while (cdit.HasNextMethod()) { const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); if (code_item != nullptr) { const void* code_item_begin = reinterpret_cast(code_item); @@ -178,7 +178,7 @@ void DexFileTrackingRegistrar::SetAllCodeItemStartRegistration(bool should_poiso if (class_data != nullptr) { ClassDataItemIterator cdit(*dex_file_, class_data); cdit.SkipAllFields(); - while (cdit.HasNextDirectMethod()) { + while (cdit.HasNextMethod()) { const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); if (code_item != nullptr) { const void* code_item_begin = reinterpret_cast(code_item); @@ -200,7 +200,7 @@ void DexFileTrackingRegistrar::SetAllInsnsRegistration(bool should_poison) { if (class_data != nullptr) { ClassDataItemIterator cdit(*dex_file_, class_data); cdit.SkipAllFields(); - while (cdit.HasNextDirectMethod()) { + while (cdit.HasNextMethod()) { const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); if (code_item != nullptr) { const void* insns_begin = reinterpret_cast(&code_item->insns_); @@ -221,7 +221,7 @@ void DexFileTrackingRegistrar::SetCodeItemRegistration(const char* class_name, b if (class_data != nullptr) { ClassDataItemIterator cdit(*dex_file_, class_data); cdit.SkipAllFields(); - while (cdit.HasNextDirectMethod()) { + while (cdit.HasNextMethod()) { const DexFile::MethodId& methodid_item = dex_file_->GetMethodId(cdit.GetMemberIndex()); const char * methodid_name = dex_file_->GetMethodName(methodid_item); const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc index 50f56c799a..025952f7a9 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex_file_verifier.cc @@ -1970,7 +1970,7 @@ dex::TypeIndex DexFileVerifier::FindFirstClassDataDefiner(const uint8_t* ptr, bo return field->class_idx_; } - if (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) { + if (it.HasNextMethod()) { LOAD_METHOD(method, it.GetMemberIndex(), "first_class_data_definer method_id", *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16)) return method->class_idx_; @@ -2566,7 +2566,7 @@ bool DexFileVerifier::CheckInterClassDataItem() { return false; } } - for (; it.HasNextDirectMethod() || it.HasNextVirtualMethod(); it.Next()) { + for (; it.HasNextMethod(); it.Next()) { uint32_t code_off = it.GetMethodCodeItemOffset(); if (code_off != 0 && !CheckOffsetToTypeMap(code_off, DexFile::kDexTypeCodeItem)) { return false; diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc index ee577e7d9a..d4d912cbfb 100644 --- a/runtime/dex_file_verifier_test.cc +++ b/runtime/dex_file_verifier_test.cc @@ -249,7 +249,7 @@ static const uint8_t* FindMethodData(const DexFile* dex_file, it.Next(); } - while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { uint32_t method_index = it.GetMemberIndex(); dex::StringIndex name_index = dex_file->GetMethodId(method_index).name_idx_; const DexFile::StringId& string_id = dex_file->GetStringId(name_index); -- GitLab From 7c201b8cc3965294a4a0c005477ccf379a8eb5f2 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 10 Nov 2017 15:37:19 +0000 Subject: [PATCH 028/226] Remove obsolete environment variables. I believe they became obsolete after: https://android-review.googlesource.com/#/c/platform/art/+/494123/ Test: test.py Change-Id: Ia9f7085edd2c6287dbd4a694c26fbbad58d11e3f --- build/Android.common_test.mk | 83 ------------------------------------ 1 file changed, 83 deletions(-) diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk index 37e6d4230d..0f29f2901b 100644 --- a/build/Android.common_test.mk +++ b/build/Android.common_test.mk @@ -33,13 +33,6 @@ endif # rule name such as test-art-host-oat-optimizing-HelloWorld64. ART_TEST_KNOWN_BROKEN := -# List of run-tests to skip running in any configuration. This needs to be the full name of the -# run-test such as '457-regs'. -ART_TEST_RUN_TEST_SKIP ?= - -# Failing valgrind tests. -# Note: *all* 64b tests involving the runtime do not work currently. b/15170219. - # List of known failing tests that when executed won't cause test execution to not finish. # The test name must be the full rule name such as test-art-host-oat-optimizing-HelloWorld64. ART_TEST_KNOWN_FAILING := @@ -47,85 +40,9 @@ ART_TEST_KNOWN_FAILING := # Keep going after encountering a test failure? ART_TEST_KEEP_GOING ?= true -# Do you want all tests, even those that are time consuming? -ART_TEST_FULL ?= false - # Do you want run-test to be quieter? run-tests will only show output if they fail. ART_TEST_QUIET ?= true -# Do you want interpreter tests run? -ART_TEST_INTERPRETER ?= true -ART_TEST_INTERPRETER_ACCESS_CHECKS ?= true - -# Do you want JIT tests run? -ART_TEST_JIT ?= true - -# Do you want optimizing compiler tests run? -ART_TEST_OPTIMIZING ?= true - -# Do you want to test the optimizing compiler with graph coloring register allocation? -ART_TEST_OPTIMIZING_GRAPH_COLOR ?= $(ART_TEST_FULL) - -# Do you want to do run-tests with profiles? -ART_TEST_SPEED_PROFILE ?= $(ART_TEST_FULL) - -# Do we want to test PIC-compiled tests ("apps")? -ART_TEST_PIC_TEST ?= $(ART_TEST_FULL) - -# Do you want tracing tests run? -ART_TEST_TRACE ?= $(ART_TEST_FULL) - -# Do you want tracing tests (streaming mode) run? -ART_TEST_TRACE_STREAM ?= $(ART_TEST_FULL) - -# Do you want tests with GC verification enabled run? -ART_TEST_GC_VERIFY ?= $(ART_TEST_FULL) - -# Do you want tests with the GC stress mode enabled run? -ART_TEST_GC_STRESS ?= $(ART_TEST_FULL) - -# Do you want tests with the JNI forcecopy mode enabled run? -ART_TEST_JNI_FORCECOPY ?= $(ART_TEST_FULL) - -# Do you want run-tests with relocation enabled run? -ART_TEST_RUN_TEST_RELOCATE ?= $(ART_TEST_FULL) - -# Do you want run-tests with prebuilding? -ART_TEST_RUN_TEST_PREBUILD ?= true - -# Do you want run-tests with no prebuilding enabled run? -ART_TEST_RUN_TEST_NO_PREBUILD ?= $(ART_TEST_FULL) - -# Do you want run-tests with a pregenerated core.art? -ART_TEST_RUN_TEST_IMAGE ?= true - -# Do you want run-tests without a pregenerated core.art? -ART_TEST_RUN_TEST_NO_IMAGE ?= $(ART_TEST_FULL) - -# Do you want run-tests with relocation enabled but patchoat failing? -ART_TEST_RUN_TEST_RELOCATE_NO_PATCHOAT ?= $(ART_TEST_FULL) - -# Do you want run-tests without a dex2oat? -ART_TEST_RUN_TEST_NO_DEX2OAT ?= $(ART_TEST_FULL) - -# Do you want run-tests with libartd.so? -ART_TEST_RUN_TEST_DEBUG ?= true - -# Do you want run-tests with libart.so? -ART_TEST_RUN_TEST_NDEBUG ?= $(ART_TEST_FULL) - -# Do you want run-tests with the host/target's second arch? -ART_TEST_RUN_TEST_2ND_ARCH ?= true - -# Do you want failed tests to have their artifacts cleaned up? -ART_TEST_RUN_TEST_ALWAYS_CLEAN ?= true - -# Do you want run-tests with the --debuggable flag -ART_TEST_RUN_TEST_DEBUGGABLE ?= $(ART_TEST_FULL) - -# Do you want to test multi-part boot-image functionality? -ART_TEST_RUN_TEST_MULTI_IMAGE ?= $(ART_TEST_FULL) - # Define the command run on test failure. $(1) is the name of the test. Executed by the shell. # If the test was a top-level make target (e.g. `test-art-host-gtest-codegen_test64`), the command # fails with exit status 1 (returned by the last `grep` statement below). -- GitLab From 960d4f7c5f6a464aa00b8f393cc88996c55464f3 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Fri, 10 Nov 2017 15:32:38 +0000 Subject: [PATCH 029/226] ART: Simplify MethodHandle invocations Use an operand iterator rather than passing arguments for both range and varargs operands. Test: art/test.py --host -j32 Change-Id: Ia42398773bd3732d917e19c25aa431b1e1369320 --- runtime/dex_instruction.cc | 10 + runtime/dex_instruction.h | 47 +++ .../quick/quick_trampoline_entrypoints.cc | 30 +- runtime/interpreter/interpreter_common.cc | 103 +++--- runtime/method_handles.cc | 295 +++++++----------- runtime/method_handles.h | 58 ++-- runtime/mirror/emulated_stack_frame.cc | 24 +- runtime/mirror/emulated_stack_frame.h | 4 +- 8 files changed, 265 insertions(+), 306 deletions(-) diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc index e64c0f62c2..6ebe2286e8 100644 --- a/runtime/dex_instruction.cc +++ b/runtime/dex_instruction.cc @@ -548,4 +548,14 @@ std::ostream& operator<<(std::ostream& os, const Instruction::Code& code) { return os << Instruction::Name(code); } +uint32_t RangeInstructionOperands::GetOperand(size_t operand_index) const { + DCHECK_LT(operand_index, GetNumberOfOperands()); + return first_operand_ + operand_index; +} + +uint32_t VarArgsInstructionOperands::GetOperand(size_t operand_index) const { + DCHECK_LT(operand_index, GetNumberOfOperands()); + return operands_[operand_index]; +} + } // namespace art diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h index 09c78b2428..4041820616 100644 --- a/runtime/dex_instruction.h +++ b/runtime/dex_instruction.h @@ -689,6 +689,53 @@ std::ostream& operator<<(std::ostream& os, const Instruction::Format& format); std::ostream& operator<<(std::ostream& os, const Instruction::Flags& flags); std::ostream& operator<<(std::ostream& os, const Instruction::VerifyFlag& vflags); +// Base class for accessing instruction operands. Unifies operand +// access for instructions that have range and varargs forms +// (e.g. invoke-polymoprhic/range and invoke-polymorphic). +class InstructionOperands { + public: + explicit InstructionOperands(size_t num_operands) : num_operands_(num_operands) {} + virtual ~InstructionOperands() {} + virtual uint32_t GetOperand(size_t index) const = 0; + size_t GetNumberOfOperands() const { return num_operands_; } + + private: + size_t num_operands_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(InstructionOperands); +}; + +// Class for accessing operands for instructions with a range format +// (e.g. 3rc and 4rcc). +class RangeInstructionOperands FINAL : public InstructionOperands { + public: + RangeInstructionOperands(uint32_t first_operand, size_t num_operands) + : InstructionOperands(num_operands), first_operand_(first_operand) {} + ~RangeInstructionOperands() {} + uint32_t GetOperand(size_t operand_index) const OVERRIDE; + + private: + const uint32_t first_operand_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(RangeInstructionOperands); +}; + +// Class for accessing operands for instructions with a variable +// number of arguments format (e.g. 35c and 45cc). +class VarArgsInstructionOperands FINAL : public InstructionOperands { + public: + VarArgsInstructionOperands(const uint32_t (&operands)[Instruction::kMaxVarArgRegs], + size_t num_operands) + : InstructionOperands(num_operands), operands_(operands) {} + ~VarArgsInstructionOperands() {} + uint32_t GetOperand(size_t operand_index) const OVERRIDE; + + private: + const uint32_t (&operands_)[Instruction::kMaxVarArgRegs]; + + DISALLOW_IMPLICIT_CONSTRUCTORS(VarArgsInstructionOperands); +}; + } // namespace art #endif // ART_RUNTIME_DEX_INSTRUCTION_H_ diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 127b5d7028..22c9a1d31a 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -2649,28 +2649,24 @@ extern "C" uintptr_t artInvokePolymorphic( // Call DoInvokePolymorphic with |is_range| = true, as shadow frame has argument registers in // consecutive order. - uint32_t unused_args[Instruction::kMaxVarArgRegs] = {}; - uint32_t first_callee_arg = first_arg + 1; - + RangeInstructionOperands operands(first_arg + 1, num_vregs - 1); bool isExact = (jni::EncodeArtMethod(resolved_method) == WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact); bool success = false; if (isExact) { - success = MethodHandleInvokeExact(self, - *shadow_frame, - method_handle, - method_type, - unused_args, - first_callee_arg, - result); + success = MethodHandleInvokeExact(self, + *shadow_frame, + method_handle, + method_type, + &operands, + result); } else { - success = MethodHandleInvoke(self, - *shadow_frame, - method_handle, - method_type, - unused_args, - first_callee_arg, - result); + success = MethodHandleInvoke(self, + *shadow_frame, + method_handle, + method_type, + &operands, + result); } DCHECK(success || self->IsExceptionPending()); diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 0a1ae36167..d2d017e118 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -644,52 +644,47 @@ static bool DoMethodHandleInvokeCommon(Thread* self, // arguments either from a range or an array of arguments depending // on whether the DEX instruction is invoke-polymorphic/range or // invoke-polymorphic. The array here is for the latter. - uint32_t args[Instruction::kMaxVarArgRegs] = {}; if (UNLIKELY(is_range)) { // VRegC is the register holding the method handle. Arguments passed // to the method handle's target do not include the method handle. - uint32_t first_arg = inst->VRegC_4rcc() + 1; - static const bool kIsRange = true; + RangeInstructionOperands operands(inst->VRegC_4rcc() + 1, inst->VRegA_4rcc() - 1); if (invoke_exact) { - return art::MethodHandleInvokeExact(self, - shadow_frame, - method_handle, - callsite_type, - args /* unused */, - first_arg, - result); + return MethodHandleInvokeExact(self, + shadow_frame, + method_handle, + callsite_type, + &operands, + result); } else { - return art::MethodHandleInvoke(self, - shadow_frame, - method_handle, - callsite_type, - args /* unused */, - first_arg, - result); + return MethodHandleInvoke(self, + shadow_frame, + method_handle, + callsite_type, + &operands, + result); } } else { // Get the register arguments for the invoke. + uint32_t args[Instruction::kMaxVarArgRegs] = {}; inst->GetVarArgs(args, inst_data); // Drop the first register which is the method handle performing the invoke. memmove(args, args + 1, sizeof(args[0]) * (Instruction::kMaxVarArgRegs - 1)); args[Instruction::kMaxVarArgRegs - 1] = 0; - static const bool kIsRange = false; + VarArgsInstructionOperands operands(args, inst->VRegA_45cc() - 1); if (invoke_exact) { - return art::MethodHandleInvokeExact(self, - shadow_frame, - method_handle, - callsite_type, - args, - args[0], - result); + return MethodHandleInvokeExact(self, + shadow_frame, + method_handle, + callsite_type, + &operands, + result); } else { - return art::MethodHandleInvoke(self, - shadow_frame, - method_handle, - callsite_type, - args, - args[0], - result); + return MethodHandleInvoke(self, + shadow_frame, + method_handle, + callsite_type, + &operands, + result); } } } @@ -1180,17 +1175,13 @@ static ObjPtr InvokeBootstrapMethod(Thread* self, // Invoke the bootstrap method handle. JValue result; - - // This array of arguments is unused. DoMethodHandleInvokeExact() operates on either a - // an argument array or a range, but always takes an array argument. - uint32_t args_unused[Instruction::kMaxVarArgRegs]; - bool invoke_success = art::MethodHandleInvokeExact(self, - *bootstrap_frame, - bootstrap, - bootstrap_method_type, - args_unused, - 0, - &result); + RangeInstructionOperands operands(0, vreg); + bool invoke_success = MethodHandleInvokeExact(self, + *bootstrap_frame, + bootstrap, + bootstrap_method_type, + &operands, + &result); if (!invoke_success) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -1273,21 +1264,25 @@ bool DoInvokeCustom(Thread* 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()); - - uint32_t args[Instruction::kMaxVarArgRegs]; if (is_range) { - args[0] = inst->VRegC_3rc(); + 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); } - - return art::MethodHandleInvokeExact(self, - shadow_frame, - target, - target_method_type, - args, - args[0], - result); } template diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 5a5d5713a8..8eb31c1ef8 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -299,17 +299,14 @@ bool ConvertJValueCommon( namespace { -template inline void CopyArgumentsFromCallerFrame(const ShadowFrame& caller_frame, ShadowFrame* callee_frame, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, - const size_t first_dst_reg, - const size_t num_regs) + const InstructionOperands* const operands, + const size_t first_dst_reg) REQUIRES_SHARED(Locks::mutator_lock_) { - for (size_t i = 0; i < num_regs; ++i) { + for (size_t i = 0; i < operands->GetNumberOfOperands(); ++i) { size_t dst_reg = first_dst_reg + i; - size_t src_reg = is_range ? (first_arg + i) : args[i]; + size_t src_reg = operands->GetOperand(i); // Uint required, so that sign extension does not make this wrong on 64-bit systems uint32_t src_value = caller_frame.GetVReg(src_reg); ObjPtr o = caller_frame.GetVRegReference(src_reg); @@ -324,15 +321,13 @@ inline void CopyArgumentsFromCallerFrame(const ShadowFrame& caller_frame, } } -template inline bool ConvertAndCopyArgumentsFromCallerFrame( Thread* self, Handle callsite_type, Handle callee_type, const ShadowFrame& caller_frame, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, - uint32_t first_dst_reg, + uint32_t first_dest_reg, + const InstructionOperands* const operands, ShadowFrame* callee_frame) REQUIRES_SHARED(Locks::mutator_lock_) { ObjPtr> from_types(callsite_type->GetPTypes()); @@ -344,15 +339,14 @@ inline bool ConvertAndCopyArgumentsFromCallerFrame( return false; } - ShadowFrameGetter getter(first_arg, args, caller_frame); - ShadowFrameSetter setter(callee_frame, first_dst_reg); - - return PerformConversions, ShadowFrameSetter>(self, - callsite_type, - callee_type, - &getter, - &setter, - num_method_params); + ShadowFrameGetter getter(operands, caller_frame); + ShadowFrameSetter setter(callee_frame, first_dest_reg); + return PerformConversions(self, + callsite_type, + callee_type, + &getter, + &setter, + num_method_params); } inline bool IsInvoke(const mirror::MethodHandle::Kind handle_kind) { @@ -406,14 +400,12 @@ static inline bool IsCallerTransformer(Handle callsite_type) return false; } -template static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, Handle callsite_type, Handle target_type, Thread* self, ShadowFrame& shadow_frame, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { // Compute method information. const DexFile::CodeItem* code_item = called_method->GetCodeItem(); @@ -455,12 +447,10 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, if (callsite_type->IsExactMatch(target_type.Get())) { // This is an exact invoke, we can take the fast path of just copying all // registers without performing any argument conversions. - CopyArgumentsFromCallerFrame(shadow_frame, - new_shadow_frame, - args, - first_arg, - first_dest_reg, - num_input_regs); + CopyArgumentsFromCallerFrame(shadow_frame, + new_shadow_frame, + operands, + first_dest_reg); } else { // This includes the case where we're entering this invoke-polymorphic // from a transformer method. In that case, the callsite_type will contain @@ -471,7 +461,7 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, is_caller_transformer = true; // The emulated stack frame is the first and only argument when we're coming // through from a transformer. - size_t first_arg_register = (is_range) ? first_arg : args[0]; + size_t first_arg_register = operands->GetOperand(0); ObjPtr emulated_stack_frame( reinterpret_cast( shadow_frame.GetVRegReference(first_arg_register))); @@ -488,14 +478,13 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, ThrowWrongMethodTypeException(target_type.Get(), callsite_type.Get()); return false; } - if (!ConvertAndCopyArgumentsFromCallerFrame(self, - callsite_type, - target_type, - shadow_frame, - args, - first_arg, - first_dest_reg, - new_shadow_frame)) { + if (!ConvertAndCopyArgumentsFromCallerFrame(self, + callsite_type, + target_type, + shadow_frame, + first_dest_reg, + operands, + new_shadow_frame)) { DCHECK(self->IsExceptionPending()); result->SetL(0); return false; @@ -521,7 +510,7 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, // we need to copy the result back out to the emulated stack frame. if (is_caller_transformer) { StackHandleScope<2> hs(self); - size_t first_callee_register = is_range ? (first_arg) : args[0]; + size_t first_callee_register = operands->GetOperand(0); Handle emulated_stack_frame( hs.NewHandle(reinterpret_cast( shadow_frame.GetVRegReference(first_callee_register)))); @@ -541,15 +530,13 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, return ConvertReturnValue(callsite_type, target_type, result); } -template static inline bool MethodHandleInvokeTransform(ArtMethod* called_method, Handle callsite_type, Handle callee_type, Thread* self, ShadowFrame& shadow_frame, Handle receiver, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { // This can be fixed to two, because the method we're calling here @@ -578,16 +565,15 @@ static inline bool MethodHandleInvokeTransform(ArtMethod* called_method, // If we're entering this transformer from another transformer, we can pass // through the handle directly to the callee, instead of having to // instantiate a new stack frame based on the shadow frame. - size_t first_callee_register = is_range ? first_arg : args[0]; + size_t first_callee_register = operands->GetOperand(0); sf.Assign(reinterpret_cast( shadow_frame.GetVRegReference(first_callee_register))); } else { - sf.Assign(mirror::EmulatedStackFrame::CreateFromShadowFrameAndArgs(self, - callsite_type, - callee_type, - shadow_frame, - first_arg, - args)); + sf.Assign(mirror::EmulatedStackFrame::CreateFromShadowFrameAndArgs(self, + callsite_type, + callee_type, + shadow_frame, + operands)); // Something went wrong while creating the emulated stack frame, we should // throw the pending exception. @@ -699,13 +685,11 @@ ArtMethod* RefineTargetMethod(Thread* self, return target_method; } -template bool DoInvokePolymorphicMethod(Thread* self, ShadowFrame& shadow_frame, Handle method_handle, Handle callsite_type, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<1> hs(self); @@ -718,7 +702,7 @@ bool DoInvokePolymorphicMethod(Thread* self, // point because they would have been performed on our behalf at the point // of creation of the method handle. ArtMethod* target_method = method_handle->GetTargetMethod(); - uint32_t receiver_reg = is_range ? first_arg: args[0]; + uint32_t receiver_reg = (operands->GetNumberOfOperands() > 0) ? operands->GetOperand(0) : 0u; ArtMethod* called_method = RefineTargetMethod(self, shadow_frame, handle_kind, @@ -743,24 +727,22 @@ bool DoInvokePolymorphicMethod(Thread* self, Handle callee_type = (handle_kind == mirror::MethodHandle::Kind::kInvokeCallSiteTransform) ? callsite_type : handle_type; - return MethodHandleInvokeTransform(called_method, - callsite_type, - callee_type, - self, - shadow_frame, - method_handle /* receiver */, - args, - first_arg, - result); + return MethodHandleInvokeTransform(called_method, + callsite_type, + callee_type, + self, + shadow_frame, + method_handle /* receiver */, + operands, + result); } else { - return MethodHandleInvokeMethod(called_method, - callsite_type, - handle_type, - self, - shadow_frame, - args, - first_arg, - result); + return MethodHandleInvokeMethod(called_method, + callsite_type, + handle_type, + self, + shadow_frame, + operands, + result); } } @@ -884,23 +866,21 @@ static JValue GetValueFromShadowFrame(const ShadowFrame& shadow_frame, return field_value; } -template +template bool MethodHandleFieldAccess(Thread* self, ShadowFrame& shadow_frame, Handle method_handle, Handle callsite_type, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<1> hs(self); Handle handle_type(hs.NewHandle(method_handle->GetMethodType())); const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind(); ArtField* field = method_handle->GetTargetField(); Primitive::Type field_type = field->GetTypeAsPrimitiveType(); - switch (handle_kind) { case mirror::MethodHandle::kInstanceGet: { - size_t obj_reg = is_range ? first_arg : args[0]; + size_t obj_reg = operands->GetOperand(0); ObjPtr obj = shadow_frame.GetVRegReference(obj_reg); MethodHandleFieldGet(self, shadow_frame, obj, field, field_type, result); if (do_conversions && !ConvertReturnValue(callsite_type, handle_type, result)) { @@ -923,8 +903,8 @@ bool MethodHandleFieldAccess(Thread* self, return true; } case mirror::MethodHandle::kInstancePut: { - size_t obj_reg = is_range ? first_arg : args[0]; - size_t value_reg = is_range ? (first_arg + 1) : args[1]; + size_t obj_reg = operands->GetOperand(0); + size_t value_reg = operands->GetOperand(1); const size_t kPTypeIndex = 1; // Use ptypes instead of field type since we may be unboxing a reference for a primitive // field. The field type is incorrect for this case. @@ -948,7 +928,7 @@ bool MethodHandleFieldAccess(Thread* self, DCHECK(self->IsExceptionPending()); return false; } - size_t value_reg = is_range ? first_arg : args[0]; + size_t value_reg = operands->GetOperand(0); const size_t kPTypeIndex = 0; // Use ptypes instead of field type since we may be unboxing a reference for a primitive // field. The field type is incorrect for this case. @@ -971,13 +951,11 @@ bool MethodHandleFieldAccess(Thread* self, } } -template static inline bool MethodHandleInvokeInternal(Thread* self, ShadowFrame& shadow_frame, Handle method_handle, Handle callsite_type, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind(); @@ -989,32 +967,28 @@ static inline bool MethodHandleInvokeInternal(Thread* self, return false; } const bool do_convert = true; - return MethodHandleFieldAccess( + return MethodHandleFieldAccess( self, shadow_frame, method_handle, callsite_type, - args, - first_arg, + operands, result); } - return DoInvokePolymorphicMethod(self, - shadow_frame, - method_handle, - callsite_type, - args, - first_arg, - result); + return DoInvokePolymorphicMethod(self, + shadow_frame, + method_handle, + callsite_type, + operands, + result); } -template static inline bool MethodHandleInvokeExactInternal( Thread* self, ShadowFrame& shadow_frame, Handle method_handle, Handle callsite_type, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<1> hs(self); @@ -1027,29 +1001,27 @@ static inline bool MethodHandleInvokeExactInternal( const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind(); if (IsFieldAccess(handle_kind)) { const bool do_convert = false; - return MethodHandleFieldAccess(self, - shadow_frame, - method_handle, - callsite_type, - args, - first_arg, - result); - } - - // Slow-path check. - if (IsInvokeTransform(handle_kind) || IsCallerTransformer(callsite_type)) { - return DoInvokePolymorphicMethod(self, + return MethodHandleFieldAccess(self, shadow_frame, method_handle, callsite_type, - args, - first_arg, + operands, result); } + // Slow-path check. + if (IsInvokeTransform(handle_kind) || IsCallerTransformer(callsite_type)) { + return DoInvokePolymorphicMethod(self, + shadow_frame, + method_handle, + callsite_type, + operands, + result); + } + // On the fast-path. This is equivalent to DoCallPolymoprhic without the conversion paths. ArtMethod* target_method = method_handle->GetTargetMethod(); - uint32_t receiver_reg = is_range ? first_arg : args[0]; + uint32_t receiver_reg = (operands->GetNumberOfOperands() > 0) ? operands->GetOperand(0) : 0u; ArtMethod* called_method = RefineTargetMethod(self, shadow_frame, handle_kind, @@ -1085,12 +1057,10 @@ static inline bool MethodHandleInvokeExactInternal( ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr = CREATE_SHADOW_FRAME(num_regs, &shadow_frame, called_method, /* dex pc */ 0); ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get(); - CopyArgumentsFromCallerFrame(shadow_frame, - new_shadow_frame, - args, - first_arg, - first_dest_reg, - num_input_regs); + CopyArgumentsFromCallerFrame(shadow_frame, + new_shadow_frame, + operands, + first_dest_reg); self->EndAssertNoThreadSuspension(old_cause); bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint( @@ -1110,43 +1080,37 @@ static inline bool MethodHandleInvokeExactInternal( } // namespace -template -inline bool MethodHandleInvoke(Thread* self, - ShadowFrame& shadow_frame, - Handle method_handle, - Handle callsite_type, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, - JValue* result) +bool MethodHandleInvoke(Thread* self, + ShadowFrame& shadow_frame, + Handle method_handle, + Handle callsite_type, + const InstructionOperands* const operands, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { if (UNLIKELY(callsite_type->IsExactMatch(method_handle->GetMethodType()))) { // A non-exact invoke that can be invoked exactly. - return MethodHandleInvokeExactInternal(self, - shadow_frame, - method_handle, - callsite_type, - args, - first_arg, - result); + return MethodHandleInvokeExactInternal(self, + shadow_frame, + method_handle, + callsite_type, + operands, + result); } else { - return MethodHandleInvokeInternal(self, - shadow_frame, - method_handle, - callsite_type, - args, - first_arg, - result); + return MethodHandleInvokeInternal(self, + shadow_frame, + method_handle, + callsite_type, + operands, + result); } } -template bool MethodHandleInvokeExact(Thread* self, - ShadowFrame& shadow_frame, - Handle method_handle, - Handle callsite_type, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, - JValue* result) + ShadowFrame& shadow_frame, + Handle method_handle, + Handle callsite_type, + const InstructionOperands* const operands, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { // We need to check the nominal type of the handle in addition to the // real type. The "nominal" type is present when MethodHandle.asType is @@ -1160,39 +1124,20 @@ bool MethodHandleInvokeExact(Thread* self, } if (LIKELY(!nominal_type->IsExactMatch(method_handle->GetMethodType()))) { // Different nominal type means we have to treat as non-exact. - return MethodHandleInvokeInternal(self, - shadow_frame, - method_handle, - callsite_type, - args, - first_arg, - result); + return MethodHandleInvokeInternal(self, + shadow_frame, + method_handle, + callsite_type, + operands, + result); } } - return MethodHandleInvokeExactInternal(self, - shadow_frame, - method_handle, - callsite_type, - args, - first_arg, - result); + return MethodHandleInvokeExactInternal(self, + shadow_frame, + method_handle, + callsite_type, + operands, + result); } -#define EXPLICIT_DO_METHOD_HANDLE_METHOD(_name, _is_range) \ - template REQUIRES_SHARED(Locks::mutator_lock_) \ - bool MethodHandle##_name<_is_range>( \ - Thread* self, \ - ShadowFrame& shadow_frame, \ - Handle method_handle, \ - Handle callsite_type, \ - const uint32_t (&args)[Instruction::kMaxVarArgRegs], \ - uint32_t first_arg, \ - JValue* result) - -EXPLICIT_DO_METHOD_HANDLE_METHOD(Invoke, true); -EXPLICIT_DO_METHOD_HANDLE_METHOD(Invoke, false); -EXPLICIT_DO_METHOD_HANDLE_METHOD(InvokeExact, true); -EXPLICIT_DO_METHOD_HANDLE_METHOD(InvokeExact, false); -#undef EXPLICIT_DO_METHOD_HANDLE_METHOD - } // namespace art diff --git a/runtime/method_handles.h b/runtime/method_handles.h index 930b8db63e..bc74bf23d2 100644 --- a/runtime/method_handles.h +++ b/runtime/method_handles.h @@ -126,50 +126,40 @@ bool PerformConversions(Thread* self, int32_t num_conversions) REQUIRES_SHARED(Locks::mutator_lock_); // A convenience class that allows for iteration through a list of -// input argument registers |arg| for non-range invokes or a list of -// consecutive registers starting with a given based for range -// invokes. -// -// This is used to iterate over input arguments while performing standard -// argument conversions. -template +// input argument registers. This is used to iterate over input +// arguments while performing standard argument conversions. class ShadowFrameGetter { public: - ShadowFrameGetter(size_t first_src_reg, - const uint32_t (&arg)[Instruction::kMaxVarArgRegs], - const ShadowFrame& shadow_frame) : - first_src_reg_(first_src_reg), - arg_(arg), - shadow_frame_(shadow_frame), - arg_index_(0) { - } + ShadowFrameGetter(const InstructionOperands* const operands, const ShadowFrame& shadow_frame) + : operands_(operands), operand_index_(0), shadow_frame_(shadow_frame) {} ALWAYS_INLINE uint32_t Get() REQUIRES_SHARED(Locks::mutator_lock_) { - const uint32_t next = (is_range ? first_src_reg_ + arg_index_ : arg_[arg_index_]); - ++arg_index_; - - return shadow_frame_.GetVReg(next); + return shadow_frame_.GetVReg(Next()); } ALWAYS_INLINE int64_t GetLong() REQUIRES_SHARED(Locks::mutator_lock_) { - const uint32_t next = (is_range ? first_src_reg_ + arg_index_ : arg_[arg_index_]); - arg_index_ += 2; - - return shadow_frame_.GetVRegLong(next); + return shadow_frame_.GetVRegLong(NextLong()); } ALWAYS_INLINE ObjPtr GetReference() REQUIRES_SHARED(Locks::mutator_lock_) { - const uint32_t next = (is_range ? first_src_reg_ + arg_index_ : arg_[arg_index_]); - ++arg_index_; - - return shadow_frame_.GetVRegReference(next); + return shadow_frame_.GetVRegReference(Next()); } private: - const size_t first_src_reg_; - const uint32_t (&arg_)[Instruction::kMaxVarArgRegs]; + 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 InstructionOperands* const operands_; + size_t operand_index_; // the next register operand to read from frame const ShadowFrame& shadow_frame_; - size_t arg_index_; }; // A convenience class that allows values to be written to a given shadow frame, @@ -201,23 +191,19 @@ class ShadowFrameSetter { size_t arg_index_; }; -template bool MethodHandleInvoke(Thread* self, ShadowFrame& shadow_frame, Handle method_handle, Handle callsite_type, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, + const InstructionOperands* const args, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); -template bool MethodHandleInvokeExact(Thread* self, ShadowFrame& shadow_frame, Handle method_handle, Handle callsite_type, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, + const InstructionOperands* const args, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc index f82bfbfaef..5757992167 100644 --- a/runtime/mirror/emulated_stack_frame.cc +++ b/runtime/mirror/emulated_stack_frame.cc @@ -139,14 +139,12 @@ class EmulatedStackFrameAccessor { DISALLOW_COPY_AND_ASSIGN(EmulatedStackFrameAccessor); }; -template mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs( Thread* self, Handle caller_type, Handle callee_type, const ShadowFrame& caller_frame, - const uint32_t first_src_reg, - const uint32_t (&arg)[Instruction::kMaxVarArgRegs]) { + const InstructionOperands* const operands) { StackHandleScope<6> hs(self); // Step 1: We must throw a WrongMethodTypeException if there's a mismatch in the @@ -185,9 +183,9 @@ mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs( } // Step 4 : Perform argument conversions (if required). - ShadowFrameGetter getter(first_src_reg, arg, caller_frame); + ShadowFrameGetter getter(operands, caller_frame); EmulatedStackFrameAccessor setter(references, stack_frame, stack_frame->GetLength()); - if (!PerformConversions, EmulatedStackFrameAccessor>( + if (!PerformConversions( self, caller_type, callee_type, &getter, &setter, num_method_params)) { return nullptr; } @@ -289,21 +287,5 @@ void EmulatedStackFrame::VisitRoots(RootVisitor* visitor) { static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); } -// Explicit CreateFromShadowFrameAndArgs template function declarations. -#define EXPLICIT_CREATE_FROM_SHADOW_FRAME_AND_ARGS_DECL(_is_range) \ - template REQUIRES_SHARED(Locks::mutator_lock_) \ - mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs<_is_range>( \ - Thread* self, \ - Handle caller_type, \ - Handle callee_type, \ - const ShadowFrame& caller_frame, \ - const uint32_t first_src_reg, \ - const uint32_t (&arg)[Instruction::kMaxVarArgRegs]) \ - -EXPLICIT_CREATE_FROM_SHADOW_FRAME_AND_ARGS_DECL(true); -EXPLICIT_CREATE_FROM_SHADOW_FRAME_AND_ARGS_DECL(false); -#undef EXPLICIT_CREATE_FROM_SHADOW_FRAME_AND_ARGS_DECL - - } // namespace mirror } // namespace art diff --git a/runtime/mirror/emulated_stack_frame.h b/runtime/mirror/emulated_stack_frame.h index 76859ef1c8..b6aa949ec3 100644 --- a/runtime/mirror/emulated_stack_frame.h +++ b/runtime/mirror/emulated_stack_frame.h @@ -35,14 +35,12 @@ class MANAGED EmulatedStackFrame : public Object { public: // Creates an emulated stack frame whose type is |frame_type| from // a shadow frame. - template static mirror::EmulatedStackFrame* CreateFromShadowFrameAndArgs( Thread* self, Handle args_type, Handle frame_type, const ShadowFrame& caller_frame, - const uint32_t first_src_reg, - const uint32_t (&arg)[Instruction::kMaxVarArgRegs]) REQUIRES_SHARED(Locks::mutator_lock_); + const InstructionOperands* const operands) REQUIRES_SHARED(Locks::mutator_lock_); // Writes the contents of this emulated stack frame to the |callee_frame| // whose type is |callee_type|, starting at |first_dest_reg|. -- GitLab From 8c4ddb242f48dad200fc0a306cb2d97b28b33325 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 13 Nov 2017 11:49:53 +0000 Subject: [PATCH 030/226] Revert "Support VecLoad and VecStore in LSE." Somehow, this breaks: ./art/test/run-test --optimizing --64 --gcstress 667-checker-simd-alignment This reverts commit 27dae5f3ce2d00b84eabf4cc4b7b2144af37a43a. Change-Id: If552ebd8c14535c2eafbdc2ed0ff1f76ad29da35 --- compiler/Android.bp | 1 - compiler/optimizing/load_store_elimination.cc | 75 +--- .../optimizing/load_store_elimination_test.cc | 406 ------------------ 3 files changed, 13 insertions(+), 469 deletions(-) delete mode 100644 compiler/optimizing/load_store_elimination_test.cc diff --git a/compiler/Android.bp b/compiler/Android.bp index 1a2d9aa5e6..859947108e 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -355,7 +355,6 @@ art_cc_test { "jni/jni_cfi_test.cc", "optimizing/codegen_test.cc", "optimizing/load_store_analysis_test.cc", - "optimizing/load_store_elimination_test.cc", "optimizing/optimizing_cfi_test.cc", "optimizing/scheduler_test.cc", ], diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 66806d8afc..8678fab655 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -83,8 +83,7 @@ class LSEVisitor : public HGraphDelegateVisitor { DCHECK(load != nullptr); DCHECK(load->IsInstanceFieldGet() || load->IsStaticFieldGet() || - load->IsArrayGet() || - load->IsVecLoad()); + load->IsArrayGet()); HInstruction* substitute = substitute_instructions_for_loads_[i]; DCHECK(substitute != nullptr); // Keep tracing substitute till one that's not removed. @@ -99,10 +98,7 @@ class LSEVisitor : public HGraphDelegateVisitor { // At this point, stores in possibly_removed_stores_ can be safely removed. for (HInstruction* store : possibly_removed_stores_) { - DCHECK(store->IsInstanceFieldSet() || - store->IsStaticFieldSet() || - store->IsArraySet() || - store->IsVecStore()); + DCHECK(store->IsInstanceFieldSet() || store->IsStaticFieldSet() || store->IsArraySet()); store->GetBlock()->RemoveInstruction(store); } @@ -141,9 +137,7 @@ class LSEVisitor : public HGraphDelegateVisitor { void KeepIfIsStore(HInstruction* heap_value) { if (heap_value == kDefaultHeapValue || heap_value == kUnknownHeapValue || - !(heap_value->IsInstanceFieldSet() || - heap_value->IsArraySet() || - heap_value->IsVecStore())) { + !(heap_value->IsInstanceFieldSet() || heap_value->IsArraySet())) { return; } auto idx = std::find(possibly_removed_stores_.begin(), @@ -326,9 +320,7 @@ class LSEVisitor : public HGraphDelegateVisitor { return; } if (heap_value != kUnknownHeapValue) { - if (heap_value->IsInstanceFieldSet() || - heap_value->IsArraySet() || - heap_value->IsVecStore()) { + if (heap_value->IsInstanceFieldSet() || heap_value->IsArraySet()) { HInstruction* store = heap_value; // This load must be from a singleton since it's from the same // field/element that a "removed" store puts the value. That store @@ -424,9 +416,7 @@ class LSEVisitor : public HGraphDelegateVisitor { if (!same_value) { if (possibly_redundant) { - DCHECK(instruction->IsInstanceFieldSet() || - instruction->IsArraySet() || - instruction->IsVecStore()); + DCHECK(instruction->IsInstanceFieldSet() || instruction->IsArraySet()); // Put the store as the heap value. If the value is loaded from heap // by a load later, this store isn't really redundant. heap_values[idx] = instruction; @@ -439,24 +429,8 @@ class LSEVisitor : public HGraphDelegateVisitor { if (i == idx) { continue; } - if (heap_values[i] == value && !instruction->IsVecOperation()) { - // For field/array, same value should be kept even if aliasing happens. - // - // For vector values , this is NOT safe. For example: - // packed_data = [0xA, 0xB, 0xC, 0xD]; <-- Different values in each lane. - // VecStore array[i ,i+1,i+2,i+3] = packed_data; - // VecStore array[i+1,i+2,i+3,i+4] = packed_data; <-- We are here (partial overlap). - // VecLoad vx = array[i,i+1,i+2,i+3]; <-- Cannot be eliminated. - // - // TODO: to allow such 'same value' optimization on vector data, - // LSA needs to report more fine-grain MAY alias information: - // (1) May alias due to two vector data partial overlap. - // e.g. a[i..i+3] and a[i+1,..,i+4]. - // (2) May alias due to two vector data may complete overlap each other. - // e.g. a[i..i+3] and b[i..i+3]. - // (3) May alias but the exact relationship between two locations is unknown. - // e.g. a[i..i+3] and b[j..j+3], where values of a,b,i,j are all unknown. - // This 'same value' optimization can apply only on case (2). + if (heap_values[i] == value) { + // Same value should be kept even if aliasing happens. continue; } if (heap_values[i] == kUnknownHeapValue) { @@ -546,32 +520,6 @@ class LSEVisitor : public HGraphDelegateVisitor { value); } - void VisitVecLoad(HVecLoad* instruction) OVERRIDE { - HInstruction* array = instruction->InputAt(0); - HInstruction* index = instruction->InputAt(1); - size_t vector_length = instruction->GetVectorLength(); - VisitGetLocation(instruction, - array, - HeapLocation::kInvalidFieldOffset, - index, - vector_length, - HeapLocation::kDeclaringClassDefIndexForArrays); - } - - void VisitVecStore(HVecStore* instruction) OVERRIDE { - HInstruction* array = instruction->InputAt(0); - HInstruction* index = instruction->InputAt(1); - HInstruction* value = instruction->InputAt(2); - size_t vector_length = instruction->GetVectorLength(); - VisitSetLocation(instruction, - array, - HeapLocation::kInvalidFieldOffset, - index, - vector_length, - HeapLocation::kDeclaringClassDefIndexForArrays, - value); - } - void VisitDeoptimize(HDeoptimize* instruction) { const ScopedArenaVector& heap_values = heap_values_for_[instruction->GetBlock()->GetBlockId()]; @@ -581,9 +529,7 @@ class LSEVisitor : public HGraphDelegateVisitor { continue; } // A store is kept as the heap value for possibly removed stores. - if (heap_value->IsInstanceFieldSet() || - heap_value->IsArraySet() || - heap_value->IsVecStore()) { + if (heap_value->IsInstanceFieldSet() || heap_value->IsArraySet()) { // Check whether the reference for a store is used by an environment local of // HDeoptimize. HInstruction* reference = heap_value->InputAt(0); @@ -741,6 +687,11 @@ void LoadStoreElimination::Run() { return; } + // TODO: analyze VecLoad/VecStore better. + if (graph_->HasSIMD()) { + return; + } + LSEVisitor lse_visitor(graph_, heap_location_collector, side_effects_, stats_); for (HBasicBlock* block : graph_->GetReversePostOrder()) { lse_visitor.VisitBasicBlock(block); diff --git a/compiler/optimizing/load_store_elimination_test.cc b/compiler/optimizing/load_store_elimination_test.cc deleted file mode 100644 index 6f42d96154..0000000000 --- a/compiler/optimizing/load_store_elimination_test.cc +++ /dev/null @@ -1,406 +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. - */ - -#include "side_effects_analysis.h" -#include "load_store_analysis.h" -#include "load_store_elimination.h" -#include "nodes.h" -#include "optimizing_unit_test.h" - -#include "gtest/gtest.h" - -namespace art { - -class LoadStoreEliminationTest : public OptimizingUnitTest { - public: - LoadStoreEliminationTest() : pool_() {} - - void PerformLSE() { - graph_->BuildDominatorTree(); - SideEffectsAnalysis side_effects(graph_); - side_effects.Run(); - LoadStoreAnalysis lsa(graph_); - lsa.Run(); - LoadStoreElimination lse(graph_, side_effects, lsa, nullptr); - lse.Run(); - } - - void CreateTestControlFlowGraph() { - graph_ = CreateGraph(); - - entry_ = new (GetAllocator()) HBasicBlock(graph_); - pre_header_ = new (GetAllocator()) HBasicBlock(graph_); - loop_header_ = new (GetAllocator()) HBasicBlock(graph_); - loop_body_ = new (GetAllocator()) HBasicBlock(graph_); - exit_ = new (GetAllocator()) HBasicBlock(graph_); - - graph_->AddBlock(entry_); - graph_->AddBlock(pre_header_); - graph_->AddBlock(loop_header_); - graph_->AddBlock(loop_body_); - graph_->AddBlock(exit_); - - graph_->SetEntryBlock(entry_); - - // This common CFG has been used by all cases in this load_store_elimination_test. - // entry - // | - // pre_header - // | - // loop_header <--+ - // | | - // loop_body -----+ - // | - // exit - - entry_->AddSuccessor(pre_header_); - pre_header_->AddSuccessor(loop_header_); - loop_header_->AddSuccessor(exit_); // true successor - loop_header_->AddSuccessor(loop_body_); // false successor - loop_body_->AddSuccessor(loop_header_); - - HInstruction* c0 = graph_->GetIntConstant(0); - HInstruction* c1 = graph_->GetIntConstant(1); - HInstruction* c4 = graph_->GetIntConstant(4); - HInstruction* c128 = graph_->GetIntConstant(128); - - // entry block has following instructions: - // array, i, j, i+1, i+4. - array_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(), - dex::TypeIndex(0), - 0, - DataType::Type::kReference); - i_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(), - dex::TypeIndex(1), - 1, - DataType::Type::kInt32); - j_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(), - dex::TypeIndex(1), - 2, - DataType::Type::kInt32); - i_add1_ = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_, c1); - i_add4_ = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_, c4); - entry_->AddInstruction(array_); - entry_->AddInstruction(i_); - entry_->AddInstruction(j_); - entry_->AddInstruction(i_add1_); - entry_->AddInstruction(i_add4_); - entry_->AddInstruction(new (GetAllocator()) HGoto()); - - // pre_header block - pre_header_->AddInstruction(new (GetAllocator()) HGoto()); - - // loop header block has following instructions: - // phi = 0; - // if (phi >= 128); - phi_ = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32); - cmp_ = new (GetAllocator()) HGreaterThanOrEqual(phi_, c128); - if_ = new (GetAllocator()) HIf(cmp_); - loop_header_->AddPhi(phi_); - loop_header_->AddInstruction(cmp_); - loop_header_->AddInstruction(if_); - phi_->AddInput(c0); - - // loop body block has following instructions: - // phi++; - HInstruction* inc_phi = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi_, c1); - loop_body_->AddInstruction(inc_phi); - loop_body_->AddInstruction(new (GetAllocator()) HGoto()); - phi_->AddInput(inc_phi); - - // exit block - exit_->AddInstruction(new (GetAllocator()) HExit()); - } - - // To avoid tedious HIR assembly in test functions. - HInstruction* AddVecLoad(HBasicBlock* block, HInstruction* array, HInstruction* index) { - DCHECK(block != nullptr); - DCHECK(array != nullptr); - DCHECK(index != nullptr); - HInstruction* vload = new (GetAllocator()) HVecLoad( - GetAllocator(), - array, - index, - DataType::Type::kInt32, - SideEffects::ArrayReadOfType(DataType::Type::kInt32), - 4, - /*is_string_char_at*/ false, - kNoDexPc); - block->InsertInstructionBefore(vload, block->GetLastInstruction()); - return vload; - } - - HInstruction* AddVecStore(HBasicBlock* block, - HInstruction* array, - HInstruction* index, - HVecOperation* vdata = nullptr) { - DCHECK(block != nullptr); - DCHECK(array != nullptr); - DCHECK(index != nullptr); - if (vdata == nullptr) { - HInstruction* c1 = graph_->GetIntConstant(1); - vdata = new (GetAllocator()) HVecReplicateScalar(GetAllocator(), - c1, - DataType::Type::kInt32, - 4, - kNoDexPc); - block->InsertInstructionBefore(vdata, block->GetLastInstruction()); - } - HInstruction* vstore = new (GetAllocator()) HVecStore( - GetAllocator(), - array, - index, - vdata, - DataType::Type::kInt32, - SideEffects::ArrayWriteOfType(DataType::Type::kInt32), - 4, - kNoDexPc); - block->InsertInstructionBefore(vstore, block->GetLastInstruction()); - return vstore; - } - - HInstruction* AddArrayGet(HBasicBlock* block, HInstruction* array, HInstruction* index) { - DCHECK(block != nullptr); - DCHECK(array != nullptr); - DCHECK(index != nullptr); - HInstruction* get = new (GetAllocator()) HArrayGet(array, index, DataType::Type::kInt32, 0); - block->InsertInstructionBefore(get, block->GetLastInstruction()); - return get; - } - - HInstruction* AddArraySet(HBasicBlock* block, - HInstruction* array, - HInstruction* index, - HInstruction* data = nullptr) { - DCHECK(block != nullptr); - DCHECK(array != nullptr); - DCHECK(index != nullptr); - if (data == nullptr) { - data = graph_->GetIntConstant(1); - } - HInstruction* store = new (GetAllocator()) HArraySet(array, - index, - data, - DataType::Type::kInt32, - 0); - block->InsertInstructionBefore(store, block->GetLastInstruction()); - return store; - } - - ArenaPool pool_; - - HGraph* graph_; - HBasicBlock* entry_; - HBasicBlock* pre_header_; - HBasicBlock* loop_header_; - HBasicBlock* loop_body_; - HBasicBlock* exit_; - - HInstruction* array_; - HInstruction* i_; - HInstruction* j_; - HInstruction* i_add1_; - HInstruction* i_add4_; - - HPhi* phi_; - HInstruction* cmp_; - HInstruction* if_; -}; - -TEST_F(LoadStoreEliminationTest, ArrayGetSetElimination) { - CreateTestControlFlowGraph(); - - HInstruction* c1 = graph_->GetIntConstant(1); - HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* c3 = graph_->GetIntConstant(3); - - // array[1] = 1; - // x = array[1]; <--- Remove. - // y = array[2]; - // array[1] = 1; <--- Remove, since it stores same value. - // array[i] = 3; <--- MAY alias. - // array[1] = 1; <--- Cannot remove, even if it stores the same value. - AddArraySet(entry_, array_, c1, c1); - HInstruction* load1 = AddArrayGet(entry_, array_, c1); - HInstruction* load2 = AddArrayGet(entry_, array_, c2); - HInstruction* store1 = AddArraySet(entry_, array_, c1, c1); - AddArraySet(entry_, array_, i_, c3); - HInstruction* store2 = AddArraySet(entry_, array_, c1, c1); - - PerformLSE(); - - ASSERT_TRUE(IsRemoved(load1)); - ASSERT_FALSE(IsRemoved(load2)); - ASSERT_TRUE(IsRemoved(store1)); - ASSERT_FALSE(IsRemoved(store2)); -} - -TEST_F(LoadStoreEliminationTest, SameHeapValue) { - CreateTestControlFlowGraph(); - - HInstruction* c1 = graph_->GetIntConstant(1); - HInstruction* c2 = graph_->GetIntConstant(2); - - // Test LSE handling same value stores on array. - // array[1] = 1; - // array[2] = 1; - // array[1] = 1; <--- Can remove. - // array[1] = 2; <--- Can NOT remove. - AddArraySet(entry_, array_, c1, c1); - AddArraySet(entry_, array_, c2, c1); - HInstruction* store1 = AddArraySet(entry_, array_, c1, c1); - HInstruction* store2 = AddArraySet(entry_, array_, c1, c2); - - // Test LSE handling same value stores on vector. - // vdata = [0x1, 0x2, 0x3, 0x4, ...] - // VecStore array[i...] = vdata; - // VecStore array[j...] = vdata; <--- MAY ALIAS. - // VecStore array[i...] = vdata; <--- Cannot Remove, even if it's same value. - AddVecStore(entry_, array_, i_); - AddVecStore(entry_, array_, j_); - HInstruction* vstore1 = AddVecStore(entry_, array_, i_); - - // VecStore array[i...] = vdata; - // VecStore array[i+1...] = vdata; <--- MAY alias due to partial overlap. - // VecStore array[i...] = vdata; <--- Cannot remove, even if it's same value. - AddVecStore(entry_, array_, i_); - AddVecStore(entry_, array_, i_add1_); - HInstruction* vstore2 = AddVecStore(entry_, array_, i_); - - PerformLSE(); - - ASSERT_TRUE(IsRemoved(store1)); - ASSERT_FALSE(IsRemoved(store2)); - ASSERT_FALSE(IsRemoved(vstore1)); - ASSERT_FALSE(IsRemoved(vstore2)); -} - -TEST_F(LoadStoreEliminationTest, OverlappingLoadStore) { - CreateTestControlFlowGraph(); - - HInstruction* c1 = graph_->GetIntConstant(1); - - // Test LSE handling array LSE when there is vector store in between. - // a[i] = 1; - // .. = a[i]; <-- Remove. - // a[i,i+1,i+2,i+3] = data; <-- PARTIAL OVERLAP ! - // .. = a[i]; <-- Cannot remove. - AddArraySet(entry_, array_, i_, c1); - HInstruction* load1 = AddArrayGet(entry_, array_, i_); - AddVecStore(entry_, array_, i_); - HInstruction* load2 = AddArrayGet(entry_, array_, i_); - - // Test LSE handling vector load/store partial overlap. - // a[i,i+1,i+2,i+3] = data; - // a[i+4,i+5,i+6,i+7] = data; - // .. = a[i,i+1,i+2,i+3]; - // .. = a[i+4,i+5,i+6,i+7]; - // a[i+1,i+2,i+3,i+4] = data; <-- PARTIAL OVERLAP ! - // .. = a[i,i+1,i+2,i+3]; - // .. = a[i+4,i+5,i+6,i+7]; - AddVecStore(entry_, array_, i_); - AddVecStore(entry_, array_, i_add4_); - HInstruction* vload1 = AddVecLoad(entry_, array_, i_); - HInstruction* vload2 = AddVecLoad(entry_, array_, i_add4_); - AddVecStore(entry_, array_, i_add1_); - HInstruction* vload3 = AddVecLoad(entry_, array_, i_); - HInstruction* vload4 = AddVecLoad(entry_, array_, i_add4_); - - // Test LSE handling vector LSE when there is array store in between. - // a[i,i+1,i+2,i+3] = data; - // a[i+1] = 1; <-- PARTIAL OVERLAP ! - // .. = a[i,i+1,i+2,i+3]; - AddVecStore(entry_, array_, i_); - AddArraySet(entry_, array_, i_, c1); - HInstruction* vload5 = AddVecLoad(entry_, array_, i_); - - PerformLSE(); - - ASSERT_TRUE(IsRemoved(load1)); - ASSERT_FALSE(IsRemoved(load2)); - - ASSERT_TRUE(IsRemoved(vload1)); - ASSERT_TRUE(IsRemoved(vload2)); - ASSERT_FALSE(IsRemoved(vload3)); - ASSERT_FALSE(IsRemoved(vload4)); - - ASSERT_FALSE(IsRemoved(vload5)); -} - -// function (int[] a, int j) { -// a[j] = 1; -// for (int i=0; i<128; i++) { -// /* doesn't do any write */ -// } -// a[j] = 1; -TEST_F(LoadStoreEliminationTest, Loop1) { - CreateTestControlFlowGraph(); - - HInstruction* c1 = graph_->GetIntConstant(1); - - // a[j] = 1 - AddArraySet(pre_header_, array_, j_, c1); - - // LOOP BODY: - // .. = a[i,i+1,i+2,i+3]; - AddVecLoad(loop_body_, array_, phi_); - - // a[j] = 1; - HInstruction* array_set = AddArraySet(exit_, array_, j_, c1); - - PerformLSE(); - - ASSERT_TRUE(IsRemoved(array_set)); -} - -// function (int[] a, int index) { -// a[index] = 1; -// int[] b = new int[128]; -// for (int i=0; i<128; i++) { -// a[i,i+1,i+2,i+3] = vdata; -// b[i,i+1,i+2,i+3] = a[i,i+1,i+2,i+3]; -// } -// a[index] = 1; -// } -TEST_F(LoadStoreEliminationTest, Loop2) { - CreateTestControlFlowGraph(); - - HInstruction* c0 = graph_->GetIntConstant(0); - HInstruction* c1 = graph_->GetIntConstant(1); - HInstruction* c128 = graph_->GetIntConstant(128); - - HInstruction* array_b = new (GetAllocator()) HNewArray(c0, c128, 0); - entry_->AddInstruction(array_b); - - // a[index] = 1; - AddArraySet(pre_header_, array_, i_, c1); - - // a[i,i+1,i+2,i+3] = vdata; - // b[i,i+1,i+2,i+3] = a[i,i+1,i+2,i+3]; - AddVecStore(loop_body_, array_, phi_); - HInstruction* vload = AddVecLoad(loop_body_, array_, phi_); - AddVecStore(loop_body_, array_b, phi_, vload->AsVecLoad()); - - // a[index] = 1; - HInstruction* a_set = AddArraySet(exit_, array_, i_, c1); - - PerformLSE(); - - ASSERT_TRUE(IsRemoved(vload)); - ASSERT_FALSE(IsRemoved(a_set)); // Cannot remove due to side effect in loop. -} - -} // namespace art -- GitLab From 3da1d0f0881e130ebab95e2d06abe7d2beff57f0 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 6 Nov 2017 20:02:24 -0800 Subject: [PATCH 031/226] Use CodeItemAccessor helpers for method verifier Create a code_item_accessor_ in the verifier and replace the existing code_item field. Added some handling in DexFile to deal with try/catch items. Bug: 63756964 Test: test-art-host Change-Id: I4e073c9cb29f94518f0016fccbe1628185884df4 --- compiler/dex/verified_method.cc | 3 +- compiler/optimizing/block_builder.cc | 4 +- runtime/code_item_accessors-inl.h | 20 +++ runtime/code_item_accessors.h | 16 +- runtime/dex_file-inl.h | 8 +- runtime/dex_file.cc | 28 ++-- runtime/dex_file.h | 12 +- runtime/verifier/method_verifier-inl.h | 4 - runtime/verifier/method_verifier.cc | 198 ++++++++++++------------- runtime/verifier/method_verifier.h | 7 +- 10 files changed, 163 insertions(+), 137 deletions(-) diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc index df75e07c3f..524b0a6911 100644 --- a/compiler/dex/verified_method.cc +++ b/compiler/dex/verified_method.cc @@ -20,6 +20,7 @@ #include #include "base/logging.h" +#include "code_item_accessors-inl.h" #include "dex_file.h" #include "dex_instruction-inl.h" #include "runtime.h" @@ -64,7 +65,7 @@ void VerifiedMethod::GenerateSafeCastSet(verifier::MethodVerifier* method_verifi if (method_verifier->HasFailures()) { return; } - for (const DexInstructionPcPair& pair : method_verifier->CodeItem()->Instructions()) { + for (const DexInstructionPcPair& pair : method_verifier->CodeItem()) { const Instruction& inst = pair.Inst(); const Instruction::Code code = inst.Opcode(); if (code == Instruction::CHECK_CAST) { diff --git a/compiler/optimizing/block_builder.cc b/compiler/optimizing/block_builder.cc index 595dd4d226..2432f13044 100644 --- a/compiler/optimizing/block_builder.cc +++ b/compiler/optimizing/block_builder.cc @@ -269,7 +269,9 @@ void HBasicBlockBuilder::InsertTryBoundaryBlocks() { // loop for synchronized blocks. if (ContainsElement(throwing_blocks_, block)) { // Try to find a TryItem covering the block. - const int32_t try_item_idx = DexFile::FindTryItem(code_item_, block->GetDexPc()); + const int32_t try_item_idx = DexFile::FindTryItem(DexFile::GetTryItems(code_item_, 0u), + code_item_.tries_size_, + block->GetDexPc()); if (try_item_idx != -1) { // Block throwing and in a TryItem. Store the try block information. try_block_info.Put(block->GetBlockId(), DexFile::GetTryItems(code_item_, try_item_idx)); diff --git a/runtime/code_item_accessors-inl.h b/runtime/code_item_accessors-inl.h index 2c5bc2eb62..d04849d09a 100644 --- a/runtime/code_item_accessors-inl.h +++ b/runtime/code_item_accessors-inl.h @@ -21,6 +21,7 @@ #include "art_method-inl.h" #include "cdex/compact_dex_file.h" +#include "dex_file-inl.h" #include "standard_dex_file.h" namespace art { @@ -130,6 +131,25 @@ inline CodeItemDataAccessor CodeItemDataAccessor::CreateNullable(ArtMethod* meth return CreateNullable(method->GetDexFile(), method->GetCodeItem()); } +inline IterationRange CodeItemDataAccessor::TryItems() const { + const DexFile::TryItem* try_items = DexFile::GetTryItems(end(), 0u); + return { + try_items, + try_items + TriesSize() }; +} + +inline const uint8_t* CodeItemDataAccessor::GetCatchHandlerData(size_t offset) const { + return DexFile::GetCatchHandlerData(end(), TriesSize(), offset); +} + +inline const DexFile::TryItem* CodeItemDataAccessor::FindTryItem(uint32_t try_dex_pc) const { + IterationRange try_items(TryItems()); + int32_t index = DexFile::FindTryItem(try_items.begin(), + try_items.end() - try_items.begin(), + try_dex_pc); + return index != -1 ? &try_items.begin()[index] : nullptr; +} + } // namespace art #endif // ART_RUNTIME_CODE_ITEM_ACCESSORS_INL_H_ diff --git a/runtime/code_item_accessors.h b/runtime/code_item_accessors.h index fdc8ac507f..aa1305acad 100644 --- a/runtime/code_item_accessors.h +++ b/runtime/code_item_accessors.h @@ -50,6 +50,11 @@ class CodeItemInstructionAccessor { return insns_; } + // Return the instruction for a dex pc. + const Instruction& InstructionAt(uint32_t dex_pc) const { + return *Instruction::At(insns_ + dex_pc); + } + // Return true if the accessor has a code item. bool HasCodeItem() const { return Insns() != nullptr; @@ -98,12 +103,19 @@ class CodeItemDataAccessor : public CodeItemInstructionAccessor { return tries_size_; } + IterationRange TryItems() const; + + const uint8_t* GetCatchHandlerData(size_t offset = 0) const; + + const DexFile::TryItem* FindTryItem(uint32_t try_dex_pc) const; + // CreateNullable allows ArtMethods that have a null code item. ALWAYS_INLINE static CodeItemDataAccessor CreateNullable(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); - ALWAYS_INLINE static CodeItemDataAccessor CreateNullable(const DexFile* dex_file, - const DexFile::CodeItem* code_item); + ALWAYS_INLINE static CodeItemDataAccessor CreateNullable( + const DexFile* dex_file, + const DexFile::CodeItem* code_item); protected: CodeItemDataAccessor() = default; diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h index 58cd48631d..c926a0d7fc 100644 --- a/runtime/dex_file-inl.h +++ b/runtime/dex_file-inl.h @@ -135,9 +135,13 @@ inline const char* DexFile::GetShorty(uint32_t proto_idx) const { } inline const DexFile::TryItem* DexFile::GetTryItems(const CodeItem& code_item, uint32_t offset) { - const uint16_t* insns_end_ = &code_item.insns_[code_item.insns_size_in_code_units_]; + return GetTryItems(code_item.Instructions().end(), offset); +} + +inline const DexFile::TryItem* DexFile::GetTryItems(const DexInstructionIterator& code_item_end, + uint32_t offset) { return reinterpret_cast - (RoundUp(reinterpret_cast(insns_end_), 4)) + offset; + (RoundUp(reinterpret_cast(&code_item_end.Inst()), 4)) + offset; } static inline bool DexFileStringEquals(const DexFile* df1, dex::StringIndex sidx1, diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 7b0c46bcfe..af79207834 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -486,20 +486,18 @@ const Signature DexFile::CreateSignature(const StringPiece& signature) const { return Signature(this, *proto_id); } -int32_t DexFile::FindTryItem(const CodeItem &code_item, uint32_t address) { - // Note: Signed type is important for max and min. - int32_t min = 0; - int32_t max = code_item.tries_size_ - 1; +int32_t DexFile::FindTryItem(const TryItem* try_items, uint32_t tries_size, uint32_t address) { + uint32_t min = 0; + uint32_t max = tries_size; + while (min < max) { + const uint32_t mid = (min + max) / 2; - while (min <= max) { - int32_t mid = min + ((max - min) / 2); - - const art::DexFile::TryItem* ti = GetTryItems(code_item, mid); - uint32_t start = ti->start_addr_; - uint32_t end = start + ti->insn_count_; + const art::DexFile::TryItem& ti = try_items[mid]; + const uint32_t start = ti.start_addr_; + const uint32_t end = start + ti.insn_count_; if (address < start) { - max = mid - 1; + max = mid; } else if (address >= end) { min = mid + 1; } else { // We have a winner! @@ -511,12 +509,8 @@ int32_t DexFile::FindTryItem(const CodeItem &code_item, uint32_t address) { } int32_t DexFile::FindCatchHandlerOffset(const CodeItem &code_item, uint32_t address) { - int32_t try_item = FindTryItem(code_item, address); - if (try_item == -1) { - return -1; - } else { - return DexFile::GetTryItems(code_item, try_item)->handler_off_; - } + int32_t try_item = FindTryItem(GetTryItems(code_item, 0), code_item.tries_size_, address); + return (try_item == -1) ? -1 : DexFile::GetTryItems(code_item, try_item)->handler_off_; } bool DexFile::LineNumForPcCb(void* raw_context, const PositionInfo& entry) { diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 2c6a430020..cdefb23400 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -753,17 +753,23 @@ class DexFile { return begin_ + call_site_id.data_off_; } + static const TryItem* GetTryItems(const DexInstructionIterator& code_item_end, uint32_t offset); static const TryItem* GetTryItems(const CodeItem& code_item, uint32_t offset); // Get the base of the encoded data for the given DexCode. - static const uint8_t* GetCatchHandlerData(const CodeItem& code_item, uint32_t offset) { + static const uint8_t* GetCatchHandlerData(const DexInstructionIterator& code_item_end, + uint32_t tries_size, + uint32_t offset) { const uint8_t* handler_data = - reinterpret_cast(GetTryItems(code_item, code_item.tries_size_)); + reinterpret_cast(GetTryItems(code_item_end, tries_size)); return handler_data + offset; } + static const uint8_t* GetCatchHandlerData(const CodeItem& code_item, uint32_t offset) { + return GetCatchHandlerData(code_item.Instructions().end(), code_item.tries_size_, offset); + } // Find which try region is associated with the given address (ie dex pc). Returns -1 if none. - static int32_t FindTryItem(const CodeItem &code_item, uint32_t address); + static int32_t FindTryItem(const TryItem* try_items, uint32_t tries_size, uint32_t address); // Find the handler offset associated with the given address (ie dex pc). Returns -1 if none. static int32_t FindCatchHandlerOffset(const CodeItem &code_item, uint32_t address); diff --git a/runtime/verifier/method_verifier-inl.h b/runtime/verifier/method_verifier-inl.h index 9bb875c033..a7fa9f34d1 100644 --- a/runtime/verifier/method_verifier-inl.h +++ b/runtime/verifier/method_verifier-inl.h @@ -27,10 +27,6 @@ namespace art { namespace verifier { -inline const DexFile::CodeItem* MethodVerifier::CodeItem() const { - return code_item_; -} - inline RegisterLine* MethodVerifier::GetRegLine(uint32_t dex_pc) { return reg_table_.GetLine(dex_pc); } diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 121f3cf364..a75157d6b3 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -564,7 +564,7 @@ MethodVerifier::MethodVerifier(Thread* self, dex_cache_(dex_cache), class_loader_(class_loader), class_def_(class_def), - code_item_(code_item), + code_item_accessor_(CodeItemDataAccessor::CreateNullable(dex_file, code_item)), declaring_class_(nullptr), interesting_dex_pc_(-1), monitor_enter_dex_pcs_(nullptr), @@ -616,29 +616,21 @@ void MethodVerifier::FindLocksAtDexPc(ArtMethod* m, uint32_t dex_pc, verifier.FindLocksAtDexPc(); } -static bool HasMonitorEnterInstructions(const DexFile::CodeItem* const code_item) { - for (const DexInstructionPcPair& inst : code_item->Instructions()) { - if (inst->Opcode() == Instruction::MONITOR_ENTER) { - return true; - } - } - return false; -} - void MethodVerifier::FindLocksAtDexPc() { CHECK(monitor_enter_dex_pcs_ != nullptr); - CHECK(code_item_ != nullptr); // This only makes sense for methods with code. + CHECK(code_item_accessor_.HasCodeItem()); // This only makes sense for methods with code. - // Quick check whether there are any monitor_enter instructions at all. - if (!HasMonitorEnterInstructions(code_item_)) { - return; + // Quick check whether there are any monitor_enter instructions before verifying. + for (const DexInstructionPcPair& inst : code_item_accessor_) { + if (inst->Opcode() == Instruction::MONITOR_ENTER) { + // 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. + Verify(); + return; + } } - - // 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. - Verify(); } ArtField* MethodVerifier::FindAccessedFieldAtDexPc(ArtMethod* m, uint32_t dex_pc) { @@ -663,7 +655,7 @@ ArtField* MethodVerifier::FindAccessedFieldAtDexPc(ArtMethod* m, uint32_t dex_pc } ArtField* MethodVerifier::FindAccessedFieldAtDexPc(uint32_t dex_pc) { - CHECK(code_item_ != nullptr); // This only makes sense for methods with code. + 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 @@ -677,7 +669,7 @@ ArtField* MethodVerifier::FindAccessedFieldAtDexPc(uint32_t dex_pc) { if (register_line == nullptr) { return nullptr; } - const Instruction* inst = &code_item_->InstructionAt(dex_pc); + const Instruction* inst = &code_item_accessor_.InstructionAt(dex_pc); return GetQuickFieldAccess(inst, register_line); } @@ -703,7 +695,7 @@ ArtMethod* MethodVerifier::FindInvokedMethodAtDexPc(ArtMethod* m, uint32_t dex_p } ArtMethod* MethodVerifier::FindInvokedMethodAtDexPc(uint32_t dex_pc) { - CHECK(code_item_ != nullptr); // This only makes sense for methods with code. + 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 @@ -717,7 +709,7 @@ ArtMethod* MethodVerifier::FindInvokedMethodAtDexPc(uint32_t dex_pc) { if (register_line == nullptr) { return nullptr; } - const Instruction* inst = &code_item_->InstructionAt(dex_pc); + 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); } @@ -769,7 +761,7 @@ bool MethodVerifier::Verify() { } // If there aren't any instructions, make sure that's expected, then exit successfully. - if (code_item_ == nullptr) { + if (!code_item_accessor_.HasCodeItem()) { // Only native or abstract methods may not have code. if ((method_access_flags_ & (kAccNative | kAccAbstract)) == 0) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "zero-length code in concrete non-native method"; @@ -861,17 +853,19 @@ bool MethodVerifier::Verify() { } // Sanity-check the register counts. ins + locals = registers, so make sure that ins <= registers. - if (code_item_->ins_size_ > code_item_->registers_size_) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad register counts (ins=" << code_item_->ins_size_ - << " regs=" << code_item_->registers_size_; + if (code_item_accessor_.InsSize() > code_item_accessor_.RegistersSize()) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad register counts (ins=" + << code_item_accessor_.InsSize() + << " regs=" << code_item_accessor_.RegistersSize(); return false; } // Allocate and initialize an array to hold instruction data. - insn_flags_.reset(allocator_.AllocArray(code_item_->insns_size_in_code_units_)); + insn_flags_.reset(allocator_.AllocArray( + code_item_accessor_.InsnsSizeInCodeUnits())); DCHECK(insn_flags_ != nullptr); std::uninitialized_fill_n(insn_flags_.get(), - code_item_->insns_size_in_code_units_, + code_item_accessor_.InsnsSizeInCodeUnits(), InstructionFlags()); // Run through the instructions and see if the width checks out. bool result = ComputeWidthsAndCountOps(); @@ -923,7 +917,7 @@ std::ostream& MethodVerifier::Fail(VerifyError error) { // Note: this can fail before we touch any instruction, for the signature of a method. So // add a check. if (work_insn_idx_ < dex::kDexNoIndex) { - const Instruction& inst = code_item_->InstructionAt(work_insn_idx_); + const Instruction& inst = code_item_accessor_.InstructionAt(work_insn_idx_); int opcode_flags = Instruction::FlagsOf(inst.Opcode()); if ((opcode_flags & Instruction::kThrow) == 0 && CurrentInsnFlags()->IsInTry()) { @@ -986,15 +980,14 @@ bool MethodVerifier::ComputeWidthsAndCountOps() { size_t new_instance_count = 0; size_t monitor_enter_count = 0; - IterationRange instructions = code_item_->Instructions(); // We can't assume the instruction is well formed, handle the case where calculating the size // goes past the end of the code item. - SafeDexInstructionIterator it(instructions.begin(), instructions.end()); - for ( ; !it.IsErrorState() && it < instructions.end(); ++it) { + SafeDexInstructionIterator it(code_item_accessor_.begin(), code_item_accessor_.end()); + for ( ; !it.IsErrorState() && it < code_item_accessor_.end(); ++it) { // In case the instruction goes past the end of the code item, make sure to not process it. SafeDexInstructionIterator next = it; ++next; - if (next.IsErrorState() || next > instructions.end()) { + if (next.IsErrorState()) { break; } Instruction::Code opcode = it->Opcode(); @@ -1021,8 +1014,8 @@ bool MethodVerifier::ComputeWidthsAndCountOps() { GetInstructionFlags(it.DexPc()).SetIsOpcode(); } - if (it != instructions.end()) { - const size_t insns_size = code_item_->insns_size_in_code_units_; + if (it != code_item_accessor_.end()) { + const size_t insns_size = code_item_accessor_.InsnsSizeInCodeUnits(); Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "code did not end where expected (" << it.DexPc() << " vs. " << insns_size << ")"; return false; @@ -1034,17 +1027,14 @@ bool MethodVerifier::ComputeWidthsAndCountOps() { } bool MethodVerifier::ScanTryCatchBlocks() { - uint32_t tries_size = code_item_->tries_size_; + const uint32_t tries_size = code_item_accessor_.TriesSize(); if (tries_size == 0) { return true; } - uint32_t insns_size = code_item_->insns_size_in_code_units_; - const DexFile::TryItem* tries = DexFile::GetTryItems(*code_item_, 0); - - for (uint32_t idx = 0; idx < tries_size; idx++) { - const DexFile::TryItem* try_item = &tries[idx]; - uint32_t start = try_item->start_addr_; - uint32_t end = start + try_item->insn_count_; + const uint32_t insns_size = code_item_accessor_.InsnsSizeInCodeUnits(); + for (const DexFile::TryItem& try_item : code_item_accessor_.TryItems()) { + const uint32_t start = try_item.start_addr_; + const uint32_t end = start + try_item.insn_count_; if ((start >= end) || (start >= insns_size) || (end > insns_size)) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad exception entry: startAddr=" << start << " endAddr=" << end << " (size=" << insns_size << ")"; @@ -1055,18 +1045,14 @@ bool MethodVerifier::ScanTryCatchBlocks() { << "'try' block starts inside an instruction (" << start << ")"; return false; } - uint32_t dex_pc = start; - const Instruction* inst = Instruction::At(code_item_->insns_ + dex_pc); - while (dex_pc < end) { - GetInstructionFlags(dex_pc).SetInTry(); - size_t insn_size = inst->SizeInCodeUnits(); - dex_pc += insn_size; - inst = inst->RelativeAt(insn_size); + DexInstructionIterator end_it(code_item_accessor_.Insns(), end); + for (DexInstructionIterator it(code_item_accessor_.Insns(), start); it < end_it; ++it) { + GetInstructionFlags(it.DexPc()).SetInTry(); } } // Iterate over each of the handlers to verify target addresses. - const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*code_item_, 0); - uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); + const uint8_t* handlers_ptr = code_item_accessor_.GetCatchHandlerData(); + const uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); ClassLinker* linker = Runtime::Current()->GetClassLinker(); for (uint32_t idx = 0; idx < handlers_size; idx++) { CatchHandlerIterator iterator(handlers_ptr); @@ -1077,7 +1063,7 @@ bool MethodVerifier::ScanTryCatchBlocks() { << "exception handler starts at bad address (" << dex_pc << ")"; return false; } - if (!CheckNotMoveResult(code_item_->insns_, dex_pc)) { + if (!CheckNotMoveResult(code_item_accessor_.Insns(), dex_pc)) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "exception handler begins with move-result* (" << dex_pc << ")"; return false; @@ -1105,7 +1091,7 @@ bool MethodVerifier::VerifyInstructions() { /* Flag the start of the method as a branch target, and a GC point due to stack overflow errors */ GetInstructionFlags(0).SetBranchTarget(); GetInstructionFlags(0).SetCompileTimeInfoPoint(); - for (const DexInstructionPcPair& inst : code_item_->Instructions()) { + for (const DexInstructionPcPair& inst : code_item_accessor_) { const uint32_t dex_pc = inst.DexPc(); if (!VerifyInstruction(&inst.Inst(), dex_pc)) { DCHECK_NE(failures_.size(), 0U); @@ -1259,18 +1245,18 @@ bool MethodVerifier::VerifyInstruction(const Instruction* inst, uint32_t code_of } inline bool MethodVerifier::CheckRegisterIndex(uint32_t idx) { - if (UNLIKELY(idx >= code_item_->registers_size_)) { + if (UNLIKELY(idx >= code_item_accessor_.RegistersSize())) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "register index out of range (" << idx << " >= " - << code_item_->registers_size_ << ")"; + << code_item_accessor_.RegistersSize() << ")"; return false; } return true; } inline bool MethodVerifier::CheckWideRegisterIndex(uint32_t idx) { - if (UNLIKELY(idx + 1 >= code_item_->registers_size_)) { + if (UNLIKELY(idx + 1 >= code_item_accessor_.RegistersSize())) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "wide register index out of range (" << idx - << "+1 >= " << code_item_->registers_size_ << ")"; + << "+1 >= " << code_item_accessor_.RegistersSize() << ")"; return false; } return true; @@ -1387,8 +1373,8 @@ bool MethodVerifier::CheckNewArray(dex::TypeIndex idx) { } bool MethodVerifier::CheckArrayData(uint32_t cur_offset) { - const uint32_t insn_count = code_item_->insns_size_in_code_units_; - const uint16_t* insns = code_item_->insns_ + cur_offset; + const uint32_t insn_count = code_item_accessor_.InsnsSizeInCodeUnits(); + const uint16_t* insns = code_item_accessor_.Insns() + cur_offset; const uint16_t* array_data; int32_t array_data_offset; @@ -1451,10 +1437,9 @@ bool MethodVerifier::CheckBranchTarget(uint32_t cur_offset) { << reinterpret_cast(cur_offset) << " +" << offset; return false; } - const uint32_t insn_count = code_item_->insns_size_in_code_units_; int32_t abs_offset = cur_offset + offset; if (UNLIKELY(abs_offset < 0 || - (uint32_t) abs_offset >= insn_count || + (uint32_t) abs_offset >= code_item_accessor_.InsnsSizeInCodeUnits() || !GetInstructionFlags(abs_offset).IsOpcode())) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid branch target " << offset << " (-> " << reinterpret_cast(abs_offset) << ") at " @@ -1467,7 +1452,7 @@ bool MethodVerifier::CheckBranchTarget(uint32_t cur_offset) { bool MethodVerifier::GetBranchOffset(uint32_t cur_offset, int32_t* pOffset, bool* pConditional, bool* selfOkay) { - const uint16_t* insns = code_item_->insns_ + cur_offset; + const uint16_t* insns = code_item_accessor_.Insns() + cur_offset; *pConditional = false; *selfOkay = false; switch (*insns & 0xff) { @@ -1503,9 +1488,9 @@ bool MethodVerifier::GetBranchOffset(uint32_t cur_offset, int32_t* pOffset, bool } bool MethodVerifier::CheckSwitchTargets(uint32_t cur_offset) { - const uint32_t insn_count = code_item_->insns_size_in_code_units_; + const uint32_t insn_count = code_item_accessor_.InsnsSizeInCodeUnits(); DCHECK_LT(cur_offset, insn_count); - const uint16_t* insns = code_item_->insns_ + cur_offset; + const uint16_t* insns = code_item_accessor_.Insns() + cur_offset; /* make sure the start of the switch is in range */ int32_t switch_offset = insns[1] | (static_cast(insns[2]) << 16); if (UNLIKELY(static_cast(cur_offset) + switch_offset < 0 || @@ -1610,7 +1595,7 @@ bool MethodVerifier::CheckSwitchTargets(uint32_t cur_offset) { } bool MethodVerifier::CheckVarArgRegs(uint32_t vA, uint32_t arg[]) { - uint16_t registers_size = code_item_->registers_size_; + uint16_t registers_size = code_item_accessor_.RegistersSize(); for (uint32_t idx = 0; idx < vA; idx++) { if (UNLIKELY(arg[idx] >= registers_size)) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid reg index (" << arg[idx] @@ -1623,7 +1608,7 @@ bool MethodVerifier::CheckVarArgRegs(uint32_t vA, uint32_t arg[]) { } bool MethodVerifier::CheckVarArgRangeRegs(uint32_t vA, uint32_t vC) { - uint16_t registers_size = code_item_->registers_size_; + uint16_t registers_size = code_item_accessor_.RegistersSize(); // vA/vC are unsigned 8-bit/16-bit quantities for /range instructions, so there's no risk of // integer overflow when adding them here. if (UNLIKELY(vA + vC > registers_size)) { @@ -1635,13 +1620,12 @@ bool MethodVerifier::CheckVarArgRangeRegs(uint32_t vA, uint32_t vC) { } bool MethodVerifier::VerifyCodeFlow() { - uint16_t registers_size = code_item_->registers_size_; - uint32_t insns_size = code_item_->insns_size_in_code_units_; + const uint16_t registers_size = code_item_accessor_.RegistersSize(); /* Create and initialize table holding register status */ reg_table_.Init(kTrackCompilerInterestPoints, insn_flags_.get(), - insns_size, + code_item_accessor_.InsnsSizeInCodeUnits(), registers_size, this); @@ -1681,7 +1665,7 @@ void MethodVerifier::Dump(std::ostream& os) { } void MethodVerifier::Dump(VariableIndentationOutputStream* vios) { - if (code_item_ == nullptr) { + if (!code_item_accessor_.HasCodeItem()) { vios->Stream() << "Native method\n"; return; } @@ -1693,7 +1677,7 @@ void MethodVerifier::Dump(VariableIndentationOutputStream* vios) { vios->Stream() << "Dumping instructions and register lines:\n"; ScopedIndentation indent1(vios); - for (const DexInstructionPcPair& inst : code_item_->Instructions()) { + for (const DexInstructionPcPair& inst : code_item_accessor_) { const size_t dex_pc = inst.DexPc(); RegisterLine* reg_line = reg_table_.GetLine(dex_pc); if (reg_line != nullptr) { @@ -1729,10 +1713,10 @@ bool MethodVerifier::SetTypesFromSignature() { RegisterLine* reg_line = reg_table_.GetLine(0); // Should have been verified earlier. - DCHECK_GE(code_item_->registers_size_, code_item_->ins_size_); + DCHECK_GE(code_item_accessor_.RegistersSize(), code_item_accessor_.InsSize()); - uint32_t arg_start = code_item_->registers_size_ - code_item_->ins_size_; - size_t expected_args = code_item_->ins_size_; /* long/double count as two */ + uint32_t arg_start = code_item_accessor_.RegistersSize() - code_item_accessor_.InsSize(); + size_t expected_args = code_item_accessor_.InsSize(); /* long/double count as two */ // Include the "this" pointer. size_t cur_arg = 0; @@ -1884,8 +1868,8 @@ bool MethodVerifier::SetTypesFromSignature() { } bool MethodVerifier::CodeFlowVerifyMethod() { - const uint16_t* insns = code_item_->insns_; - const uint32_t insns_size = code_item_->insns_size_in_code_units_; + const uint16_t* insns = code_item_accessor_.Insns(); + const uint32_t insns_size = code_item_accessor_.InsnsSizeInCodeUnits(); /* Begin by marking the first instruction as "changed". */ GetInstructionFlags(0).SetChanged(); @@ -1960,7 +1944,7 @@ bool MethodVerifier::CodeFlowVerifyMethod() { */ int dead_start = -1; - for (const DexInstructionPcPair& inst : code_item_->Instructions()) { + for (const DexInstructionPcPair& inst : code_item_accessor_) { const uint32_t insn_idx = inst.DexPc(); /* * Switch-statement data doesn't get "visited" by scanner. It @@ -1989,7 +1973,7 @@ bool MethodVerifier::CodeFlowVerifyMethod() { if (dead_start >= 0) { LogVerifyInfo() << "dead code " << reinterpret_cast(dead_start) - << "-" << reinterpret_cast(code_item_->insns_size_in_code_units_ - 1); + << "-" << reinterpret_cast(code_item_accessor_.InsnsSizeInCodeUnits() - 1); } // To dump the state of the verify after a method, do something like: // if (dex_file_->PrettyMethod(dex_method_idx_) == @@ -2075,7 +2059,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * * The behavior can be determined from the opcode flags. */ - const uint16_t* insns = code_item_->insns_ + work_insn_idx_; + const uint16_t* insns = code_item_accessor_.Insns() + work_insn_idx_; const Instruction* inst = Instruction::At(insns); int opcode_flags = Instruction::FlagsOf(inst->Opcode()); @@ -2375,7 +2359,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { while (0 != prev_idx && !GetInstructionFlags(prev_idx).IsOpcode()) { prev_idx--; } - const Instruction& prev_inst = code_item_->InstructionAt(prev_idx); + const Instruction& prev_inst = code_item_accessor_.InstructionAt(prev_idx); switch (prev_inst.Opcode()) { case Instruction::MOVE_OBJECT: case Instruction::MOVE_OBJECT_16: @@ -2683,7 +2667,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { break; } - const Instruction& instance_of_inst = code_item_->InstructionAt(instance_of_idx); + const Instruction& instance_of_inst = code_item_accessor_.InstructionAt(instance_of_idx); /* Check for peep-hole pattern of: * ...; @@ -2722,7 +2706,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { (orig_type.IsZero() || orig_type.IsStrictlyAssignableFrom( cast_type.Merge(orig_type, ®_types_, this), this))) { - RegisterLine* update_line = RegisterLine::Create(code_item_->registers_size_, this); + RegisterLine* update_line = RegisterLine::Create(code_item_accessor_.RegistersSize(), + this); if (inst->Opcode() == Instruction::IF_EQZ) { fallthrough_line.reset(update_line); } else { @@ -2745,7 +2730,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { work_insn_idx_)) { break; } - const Instruction& move_inst = code_item_->InstructionAt(move_idx); + const Instruction& move_inst = code_item_accessor_.InstructionAt(move_idx); switch (move_inst.Opcode()) { case Instruction::MOVE_OBJECT: if (move_inst.VRegA_12x() == instance_of_inst.VRegB_22c()) { @@ -3564,7 +3549,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { return false; } DCHECK_EQ(isConditional, (opcode_flags & Instruction::kContinue) != 0); - if (!CheckNotMoveExceptionOrMoveResult(code_item_->insns_, work_insn_idx_ + branch_target)) { + if (!CheckNotMoveExceptionOrMoveResult(code_item_accessor_.Insns(), + work_insn_idx_ + branch_target)) { return false; } /* update branch target, set "changed" if appropriate */ @@ -3609,8 +3595,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { offset = switch_insns[offset_to_targets + targ * 2] | (static_cast(switch_insns[offset_to_targets + targ * 2 + 1]) << 16); abs_offset = work_insn_idx_ + offset; - DCHECK_LT(abs_offset, code_item_->insns_size_in_code_units_); - if (!CheckNotMoveExceptionOrMoveResult(code_item_->insns_, abs_offset)) { + DCHECK_LT(abs_offset, code_item_accessor_.InsnsSizeInCodeUnits()); + if (!CheckNotMoveExceptionOrMoveResult(code_item_accessor_.Insns(), abs_offset)) { return false; } if (!UpdateRegisters(abs_offset, work_line_.get(), false)) { @@ -3625,7 +3611,9 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { */ if ((opcode_flags & Instruction::kThrow) != 0 && GetInstructionFlags(work_insn_idx_).IsInTry()) { bool has_catch_all_handler = false; - CatchHandlerIterator iterator(*code_item_, work_insn_idx_); + const DexFile::TryItem* try_item = code_item_accessor_.FindTryItem(work_insn_idx_); + CHECK(try_item != nullptr); + CatchHandlerIterator iterator(code_item_accessor_.GetCatchHandlerData(try_item->handler_off_)); // Need the linker to try and resolve the handled class to check if it's Throwable. ClassLinker* linker = Runtime::Current()->GetClassLinker(); @@ -3682,15 +3670,15 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * and this change should not be used in those cases. */ if ((opcode_flags & Instruction::kContinue) != 0) { - DCHECK_EQ(&code_item_->InstructionAt(work_insn_idx_), inst); + DCHECK_EQ(&code_item_accessor_.InstructionAt(work_insn_idx_), inst); uint32_t next_insn_idx = work_insn_idx_ + inst->SizeInCodeUnits(); - if (next_insn_idx >= code_item_->insns_size_in_code_units_) { + if (next_insn_idx >= code_item_accessor_.InsnsSizeInCodeUnits()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Execution can walk off end of code area"; return false; } // The only way to get to a move-exception instruction is to get thrown there. Make sure the // next instruction isn't one. - if (!CheckNotMoveException(code_item_->insns_, next_insn_idx)) { + if (!CheckNotMoveException(code_item_accessor_.Insns(), next_insn_idx)) { return false; } if (nullptr != fallthrough_line) { @@ -3699,7 +3687,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } if (GetInstructionFlags(next_insn_idx).IsReturn()) { // For returns we only care about the operand to the return, all other registers are dead. - const Instruction* ret_inst = Instruction::At(code_item_->insns_ + next_insn_idx); + const Instruction* ret_inst = &code_item_accessor_.InstructionAt(next_insn_idx); AdjustReturnLine(this, ret_inst, work_line_.get()); } RegisterLine* next_line = reg_table_.GetLine(next_insn_idx); @@ -3731,14 +3719,14 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * alone and let the caller sort it out. */ if ((opcode_flags & Instruction::kContinue) != 0) { - DCHECK_EQ(Instruction::At(code_item_->insns_ + work_insn_idx_), inst); + DCHECK_EQ(&code_item_accessor_.InstructionAt(work_insn_idx_), inst); *start_guess = work_insn_idx_ + inst->SizeInCodeUnits(); } else if ((opcode_flags & Instruction::kBranch) != 0) { /* we're still okay if branch_target is zero */ *start_guess = work_insn_idx_ + branch_target; } - DCHECK_LT(*start_guess, code_item_->insns_size_in_code_units_); + DCHECK_LT(*start_guess, code_item_accessor_.InsnsSizeInCodeUnits()); DCHECK(GetInstructionFlags(*start_guess).IsOpcode()); if (have_pending_runtime_throw_failure_) { @@ -3821,8 +3809,8 @@ template const RegType& MethodVerifier::ResolveClasstries_size_ != 0) { - const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*code_item_, 0); + if (code_item_accessor_.TriesSize() != 0) { + const uint8_t* handlers_ptr = code_item_accessor_.GetCatchHandlerData(); uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); for (uint32_t i = 0; i < handlers_size; i++) { CatchHandlerIterator iterator(handlers_ptr); @@ -4041,9 +4029,10 @@ ArtMethod* MethodVerifier::VerifyInvocationArgsFromIterator( /* caught by static verifier */ DCHECK(is_range || expected_args <= 5); - if (expected_args > code_item_->outs_size_) { + if (expected_args > code_item_accessor_.OutsSize()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid argument count (" << expected_args - << ") exceeds outsSize (" << code_item_->outs_size_ << ")"; + << ") exceeds outsSize (" + << code_item_accessor_.OutsSize() << ")"; return nullptr; } @@ -4554,9 +4543,9 @@ ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst, 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_->outs_size_) { + if (expected_args > code_item_accessor_.OutsSize()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid argument count (" << expected_args - << ") exceeds outsSize (" << code_item_->outs_size_ << ")"; + << ") exceeds outsSize (" << code_item_accessor_.OutsSize() << ")"; return nullptr; } @@ -5142,8 +5131,7 @@ void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType& } } -ArtField* MethodVerifier::GetQuickFieldAccess(const Instruction* inst, - RegisterLine* reg_line) { +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()) { @@ -5330,7 +5318,7 @@ bool MethodVerifier::UpdateRegisters(uint32_t next_insn, RegisterLine* merge_lin // For returns we only care about the operand to the return, all other registers are dead. // Initialize them as conflicts so they don't add to GC and deoptimization information. - const Instruction* ret_inst = Instruction::At(code_item_->insns_ + next_insn); + const Instruction* ret_inst = &code_item_accessor_.InstructionAt(next_insn); AdjustReturnLine(this, ret_inst, target_line); // Directly bail if a hard failure was found. if (have_pending_hard_failure_) { diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 1f1d7c1f03..813ce87175 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -25,6 +25,7 @@ #include "base/macros.h" #include "base/scoped_arena_containers.h" #include "base/value_object.h" +#include "code_item_accessors.h" #include "dex_file.h" #include "dex_file_types.h" #include "handle.h" @@ -186,7 +187,9 @@ class MethodVerifier { REQUIRES_SHARED(Locks::mutator_lock_); // Accessors used by the compiler via CompilerCallback - const DexFile::CodeItem* CodeItem() const; + const CodeItemDataAccessor& CodeItem() const { + return code_item_accessor_; + } RegisterLine* GetRegLine(uint32_t dex_pc); ALWAYS_INLINE const InstructionFlags& GetInstructionFlags(size_t index) const; ALWAYS_INLINE InstructionFlags& GetInstructionFlags(size_t index); @@ -738,7 +741,7 @@ class MethodVerifier { // The class loader for the declaring class of the method. Handle class_loader_ GUARDED_BY(Locks::mutator_lock_); const DexFile::ClassDef& class_def_; // The class def of the declaring class of the method. - const DexFile::CodeItem* const code_item_; // The code item containing the code for the method. + const CodeItemDataAccessor code_item_accessor_; const RegType* declaring_class_; // Lazily computed reg type of the method's declaring class. // Instruction widths and flags, one entry per code unit. // Owned, but not unique_ptr since insn_flags_ are allocated in arenas. -- GitLab From 9311297a763d9d6cf6059f110a9aa136c0c8d81f Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 13 Nov 2017 10:38:59 -0800 Subject: [PATCH 032/226] Make agent threads retain their thread names We were previously ignoring the thread-names given to agent threads. This could make debugging issues difficult as every thread was called "JVMTI Agent thread". Change the RunAgentThread code so instead we will use the same thread-name as the java thread. Bug: 68839405 Test: ./test.py --host -j50 Change-Id: Ifdd58e4eebc1877bde0ba49d4fbedbd3935b312f --- openjdkjvmti/ti_thread.cc | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc index 6d075a6b7b..b7b81ce358 100644 --- a/openjdkjvmti/ti_thread.cc +++ b/openjdkjvmti/ti_thread.cc @@ -47,6 +47,7 @@ #include "mirror/object-inl.h" #include "mirror/string.h" #include "nativehelper/scoped_local_ref.h" +#include "nativehelper/scoped_utf_chars.h" #include "obj_ptr.h" #include "runtime.h" #include "runtime_callbacks.h" @@ -701,6 +702,7 @@ struct AgentData { JavaVM* java_vm; jvmtiEnv* jvmti_env; jint priority; + std::string name; }; static void* AgentCallback(void* arg) { @@ -708,13 +710,13 @@ static void* AgentCallback(void* arg) { CHECK(data->thread != nullptr); // We already have a peer. So call our special Attach function. - art::Thread* self = art::Thread::Attach("JVMTI Agent thread", true, data->thread); + art::Thread* self = art::Thread::Attach(data->name.c_str(), true, data->thread); CHECK(self != nullptr) << "threads_being_born_ should have ensured thread could be attached."; // The name in Attach() is only for logging. Set the thread name. This is important so // that the thread is no longer seen as starting up. { art::ScopedObjectAccess soa(self); - self->SetThreadName("JVMTI Agent thread"); + self->SetThreadName(data->name.c_str()); } // Release the peer. @@ -781,6 +783,16 @@ jvmtiError ThreadUtil::RunAgentThread(jvmtiEnv* jvmti_env, data->java_vm = art::Runtime::Current()->GetJavaVM(); data->jvmti_env = jvmti_env; data->priority = priority; + ScopedLocalRef s( + env, + reinterpret_cast( + env->GetObjectField(thread, art::WellKnownClasses::java_lang_Thread_name))); + if (s == nullptr) { + data->name = "JVMTI Agent Thread"; + } else { + ScopedUtfChars name(env, s.get()); + data->name = name.c_str(); + } pthread_t pthread; int pthread_create_result = pthread_create(&pthread, -- GitLab From 099f8d6955700913af2610793928ae3e29f3002d Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Tue, 5 Sep 2017 19:04:04 -0700 Subject: [PATCH 033/226] Do not use realpath in oat file assistant Installd logic was updated to compile secondary dex files next to their specified loading path (instead of the realpath). It simplifies cleaning secondary dex files artifacts from sym link locations. Bug: 64460009 Test: m test-art-host-gtest Change-Id: I79aea4932672c6c25aff8e03136efef39bc93fe6 --- runtime/oat_file_assistant.cc | 23 +--------------- runtime/oat_file_assistant_test.cc | 44 ------------------------------ 2 files changed, 1 insertion(+), 66 deletions(-) diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 378ce2cff2..b8a13da9b3 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -99,28 +99,7 @@ OatFileAssistant::OatFileAssistant(const char* dex_location, << " vdex_fd=" << vdex_fd;; } - // Try to get the realpath for the dex location. - // - // This is OK with respect to dalvik cache naming scheme because we never - // generate oat files starting from symlinks which go into dalvik cache. - // (recall that the oat files in dalvik cache are encoded by replacing '/' - // with '@' in the path). - // The boot image oat files (which are symlinked in dalvik-cache) are not - // loaded via the oat file assistant. - // - // The only case when the dex location may resolve to a different path - // is for secondary dex files (e.g. /data/user/0 symlinks to /data/data and - // the app is free to create its own internal layout). Related to this it is - // worthwhile to mention that installd resolves the secondary dex location - // before calling dex2oat. - UniqueCPtr dex_location_real(realpath(dex_location, nullptr)); - if (dex_location_real != nullptr) { - dex_location_.assign(dex_location_real.get()); - } else { - // If we can't get the realpath of the location there's not much point in trying to move on. - PLOG(ERROR) << "Could not get the realpath of dex_location " << dex_location; - return; - } + dex_location_.assign(dex_location); if (load_executable_ && isa != kRuntimeISA) { LOG(WARNING) << "OatFileAssistant: Load executable specified, " diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 65d01a4d52..7694f45e78 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -351,50 +351,6 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexVdexFd) { EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); } -// Case: We have a DEX file and up-to-date OAT file for it. We load the dex file -// via a symlink. -// Expect: The status is kNoDexOptNeeded. -TEST_F(OatFileAssistantTest, OatUpToDateSymLink) { - if (IsExecutedAsRoot()) { - // We cannot simulate non writable locations when executed as root: b/38000545. - LOG(ERROR) << "Test skipped because it's running as root"; - return; - } - - std::string real = GetScratchDir() + "/real"; - ASSERT_EQ(0, mkdir(real.c_str(), 0700)); - std::string link = GetScratchDir() + "/link"; - ASSERT_EQ(0, symlink(real.c_str(), link.c_str())); - - std::string dex_location = real + "/OatUpToDate.jar"; - - Copy(GetDexSrc1(), dex_location); - GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); - - // Update the dex location to point to the symlink. - dex_location = link + "/OatUpToDate.jar"; - - // For the use of oat location by making the dex parent not writable. - ScopedNonWritable scoped_non_writable(dex_location); - ASSERT_TRUE(scoped_non_writable.IsSuccessful()); - - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); - - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken)); - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kExtract)); - EXPECT_EQ(OatFileAssistant::kDex2OatForFilter, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything)); - - EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); - EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); -} - // Case: We have a DEX file and up-to-date (ODEX) VDEX file for it, but no // ODEX file. TEST_F(OatFileAssistantTest, VdexUpToDateNoOdex) { -- GitLab From 92003fe2d5f9031f56c8b0407d8d934023fb4cb2 Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Wed, 6 Sep 2017 02:22:57 +0000 Subject: [PATCH 034/226] Revert "Use real locations when opening dex files from the context" This reverts commit 821a2595e2438554424879d6cb3594810ca8e636. Reason for revert: Oat file assistant logic was updated to not use realpaths anymore. So this is no longer needed. Test: m test-art-host-gtest Change-Id: Ia63a50f60b82ec3e20606d0be852607c9d2eb26e --- runtime/class_loader_context.cc | 28 ++++------- runtime/class_loader_context_test.cc | 70 ++++++++-------------------- 2 files changed, 30 insertions(+), 68 deletions(-) diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index 38f59efdf7..b62764f4c6 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -16,9 +16,6 @@ #include "class_loader_context.h" -#include - -#include "android-base/file.h" #include "art_field-inl.h" #include "base/dchecked_vector.h" #include "base/stl_util.h" @@ -210,19 +207,9 @@ bool ClassLoaderContext::OpenDexFiles(InstructionSet isa, const std::string& cla size_t opened_dex_files_index = info.opened_dex_files.size(); for (const std::string& cp_elem : info.classpath) { // If path is relative, append it to the provided base directory. - std::string raw_location = cp_elem; - if (raw_location[0] != '/' && !classpath_dir.empty()) { - raw_location = classpath_dir + '/' + raw_location; - } - - std::string location; // the real location of the class path element. - - if (!android::base::Realpath(raw_location, &location)) { - // If we can't get the realpath of the location there might be something wrong with the - // classpath (maybe the file was deleted). - // Do not continue in this case and return false. - PLOG(WARNING) << "Could not get the realpath of dex location " << raw_location; - return false; + std::string location = cp_elem; + if (location[0] != '/' && !classpath_dir.empty()) { + location = classpath_dir + '/' + location; } std::string error_msg; @@ -728,15 +715,20 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex dex_name = info.classpath[k]; expected_dex_name = OatFile::ResolveRelativeEncodedDexLocation( info.classpath[k].c_str(), expected_info.classpath[k]); - } else { + } else if (is_expected_dex_name_absolute) { // The runtime name is relative but the compiled name is absolute. // There is no expected use case that would end up here as dex files are always loaded // with their absolute location. However, be tolerant and do the best effort (in case // there are unexpected new use case...). - DCHECK(is_expected_dex_name_absolute); dex_name = OatFile::ResolveRelativeEncodedDexLocation( expected_info.classpath[k].c_str(), info.classpath[k]); expected_dex_name = expected_info.classpath[k]; + } else { + // Both locations are relative. In this case there's not much we can be sure about + // except that the names are the same. The checksum will ensure that the files are + // are same. This should not happen outside testing and manual invocations. + dex_name = info.classpath[k]; + expected_dex_name = expected_info.classpath[k]; } // Compare the locations. diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc index be6acde4a9..fc3446c3f9 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -17,17 +17,13 @@ #include "class_loader_context.h" #include -#include #include "android-base/strings.h" - #include "base/dchecked_vector.h" #include "base/stl_util.h" -#include "class_loader_context.h" #include "class_linker.h" #include "common_runtime_test.h" #include "dex_file.h" -#include "dex2oat_environment_test.h" #include "handle_scope-inl.h" #include "mirror/class.h" #include "mirror/class_loader.h" @@ -84,6 +80,10 @@ class ClassLoaderContextTest : public CommonRuntimeTest { kEndsWith }; + static bool IsAbsoluteLocation(const std::string& location) { + return !location.empty() && location[0] == '/'; + } + void VerifyOpenDexFiles( ClassLoaderContext* context, size_t index, @@ -100,17 +100,22 @@ class ClassLoaderContextTest : public CommonRuntimeTest { info.opened_dex_files[cur_open_dex_index++]; std::unique_ptr& expected_dex_file = (*all_dex_files)[k]; - std::string expected_location = - DexFileLoader::GetBaseLocation(expected_dex_file->GetLocation()); - UniqueCPtr expected_real_location( - realpath(expected_location.c_str(), nullptr)); - ASSERT_TRUE(expected_real_location != nullptr) << expected_location; - expected_location.assign(expected_real_location.get()); - expected_location += DexFileLoader::GetMultiDexSuffix(expected_dex_file->GetLocation()); - - ASSERT_EQ(expected_location, opened_dex_file->GetLocation()); + std::string expected_location = expected_dex_file->GetLocation(); + + const std::string& opened_location = opened_dex_file->GetLocation(); + if (!IsAbsoluteLocation(opened_location)) { + // If the opened location is relative (it was open from a relative path without a + // classpath_dir) it might not match the expected location which is absolute in tests). + // So we compare the endings (the checksum will validate it's actually the same file). + ASSERT_EQ(0, expected_location.compare( + expected_location.length() - opened_location.length(), + opened_location.length(), + opened_location)); + } else { + ASSERT_EQ(expected_location, opened_location); + } ASSERT_EQ(expected_dex_file->GetLocationChecksum(), opened_dex_file->GetLocationChecksum()); - ASSERT_EQ(info.classpath[k], opened_dex_file->GetLocation()); + ASSERT_EQ(info.classpath[k], opened_location); } } @@ -252,6 +257,7 @@ TEST_F(ClassLoaderContextTest, OpenValidDexFiles) { std::string myclass_dex_name = GetTestDexFileName("MyClass"); std::string dex_name = GetTestDexFileName("Main"); + std::unique_ptr context = ClassLoaderContext::Create( "PCL[" + multidex_name + ":" + myclass_dex_name + "];" + @@ -272,42 +278,6 @@ TEST_F(ClassLoaderContextTest, OpenValidDexFiles) { VerifyOpenDexFiles(context.get(), 1, &all_dex_files1); } -class ScratchSymLink { - public: - explicit ScratchSymLink(const std::string& file) { - // Use a temporary scratch file to get a unique name for the link. - ScratchFile scratchFile; - scratch_link_name_ = scratchFile.GetFilename() + ".link.jar"; - CHECK_EQ(0, symlink(file.c_str(), scratch_link_name_.c_str())); - } - - ~ScratchSymLink() { - CHECK_EQ(0, unlink(scratch_link_name_.c_str())); - } - - const std::string& GetFilename() { return scratch_link_name_; } - - private: - std::string scratch_link_name_; -}; - -TEST_F(ClassLoaderContextTest, OpenValidDexFilesSymLink) { - std::string myclass_dex_name = GetTestDexFileName("MyClass"); - // Now replace the dex location with a symlink. - ScratchSymLink link(myclass_dex_name); - - std::unique_ptr context = - ClassLoaderContext::Create("PCL[" + link.GetFilename() + "]"); - - ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, /*classpath_dir*/ "")); - - VerifyContextSize(context.get(), 1); - - std::vector> myclass_dex_files = OpenTestDexFiles("MyClass"); - - VerifyOpenDexFiles(context.get(), 0, &myclass_dex_files); -} - static std::string CreateRelativeString(const std::string& in, const char* cwd) { int cwd_len = strlen(cwd); if (!android::base::StartsWith(in, cwd) || (cwd_len < 1)) { -- GitLab From 87c9317d22d544dea96e6d6dbe31647e7ff270f9 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 13 Nov 2017 16:26:02 -0800 Subject: [PATCH 035/226] Make sure VMClassLoader is initialized before eating up the RAM Otherwise there may be an OOME when trying to resolve the class. When this happens, it causes an abort in WrapExceptionInInitializer since the class is in the bootstrap classloader. Test: test-art-host Bug: 69149957 Change-Id: I3cb79e8cffef758799d8c57ca5132d65a10e75ea --- test/163-app-image-methods/src/Main.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/163-app-image-methods/src/Main.java b/test/163-app-image-methods/src/Main.java index a995bb8412..c513470b7b 100644 --- a/test/163-app-image-methods/src/Main.java +++ b/test/163-app-image-methods/src/Main.java @@ -22,6 +22,9 @@ public class Main { // Allocate memory for the "AAA.Derived" class name before eating memory. String aaaDerivedName = "AAA.Derived"; System.out.println("Eating all memory."); + // Resolve VMClassLoader before eating all the memory since we can not fail + // initializtaion of boot classpath classes. + Class.forName("java.lang.VMClassLoader"); Object memory = eatAllMemory(); // This test assumes that Derived is not yet resolved. In some configurations -- GitLab From 8eaa8e59c95aac26cc072cdbaaccd8f3976f113d Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 13 Nov 2017 17:47:50 +0000 Subject: [PATCH 036/226] Remove kIsVdexEnabled. It is now always assumed there is one. Test: test.py Change-Id: I8f3f5c722fb8c4a0f9ad8ea685d1a956bd0ac9ae --- build/art.go | 4 - compiler/optimizing/inliner.cc | 2 +- compiler/optimizing/optimizing_compiler.cc | 2 +- dex2oat/dex2oat.cc | 2 +- dex2oat/linker/image_test.h | 16 ++-- dex2oat/linker/oat_writer.cc | 103 ++++++--------------- dex2oat/linker/oat_writer_test.cc | 18 ++-- oatdump/oatdump.cc | 66 ++++++------- runtime/art_method.cc | 23 ++--- runtime/art_method.h | 2 +- runtime/globals.h | 6 -- runtime/oat_file.cc | 10 +- runtime/oat_file_assistant.cc | 15 +-- runtime/oat_file_assistant_test.cc | 19 ---- 14 files changed, 92 insertions(+), 196 deletions(-) diff --git a/build/art.go b/build/art.go index 4e48d2d932..5704b43834 100644 --- a/build/art.go +++ b/build/art.go @@ -46,10 +46,6 @@ func globalFlags(ctx android.BaseContext) ([]string, []string) { cflags = append(cflags, "-DART_USE_TLAB=1") } - if !envFalse(ctx, "ART_ENABLE_VDEX") { - cflags = append(cflags, "-DART_ENABLE_VDEX") - } - imtSize := envDefault(ctx, "ART_IMT_SIZE", "43") cflags = append(cflags, "-DIMT_SIZE="+imtSize) diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 4d846fa4ed..7adb196d14 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -1672,7 +1672,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, compiler_driver_, codegen_, inline_stats_, - resolved_method->GetQuickenedInfo(class_linker->GetImagePointerSize()), + resolved_method->GetQuickenedInfo(), handles_); if (builder.BuildGraph() != kAnalysisSuccess) { diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 252d53823a..2bba985c34 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -1007,7 +1007,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, if (method != nullptr) { graph->SetArtMethod(method); ScopedObjectAccess soa(Thread::Current()); - interpreter_metadata = method->GetQuickenedInfo(class_linker->GetImagePointerSize()); + interpreter_metadata = method->GetQuickenedInfo(); } std::unique_ptr codegen( diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 9fa7f697d9..8137fb1f11 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1565,7 +1565,7 @@ class Dex2Oat FINAL { // 2) when we have a vdex file, which means it was already verified. const bool verify = !DoDexLayoutOptimizations() && (input_vdex_file_ == nullptr); if (!oat_writers_[i]->WriteAndOpenDexFiles( - kIsVdexEnabled ? vdex_files_[i].get() : oat_files_[i].get(), + vdex_files_[i].get(), rodata_.back(), instruction_set_, instruction_set_features_.get(), diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index d3d42b98bb..cedbccf7cc 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -269,7 +269,7 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, std::unique_ptr cur_opened_dex_files_map; std::vector> cur_opened_dex_files; bool dex_files_ok = oat_writers[i]->WriteAndOpenDexFiles( - kIsVdexEnabled ? vdex_files[i].GetFile() : oat_files[i].GetFile(), + vdex_files[i].GetFile(), rodata.back(), driver->GetInstructionSet(), driver->GetInstructionSetFeatures(), @@ -293,14 +293,12 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, bool image_space_ok = writer->PrepareImageAddressSpace(); ASSERT_TRUE(image_space_ok); - if (kIsVdexEnabled) { - for (size_t i = 0, size = vdex_files.size(); i != size; ++i) { - std::unique_ptr vdex_out = - std::make_unique( - std::make_unique(vdex_files[i].GetFile())); - oat_writers[i]->WriteVerifierDeps(vdex_out.get(), nullptr); - oat_writers[i]->WriteChecksumsAndVdexHeader(vdex_out.get()); - } + for (size_t i = 0, size = vdex_files.size(); i != size; ++i) { + std::unique_ptr vdex_out = + std::make_unique( + std::make_unique(vdex_files[i].GetFile())); + oat_writers[i]->WriteVerifierDeps(vdex_out.get(), nullptr); + oat_writers[i]->WriteChecksumsAndVdexHeader(vdex_out.get()); } for (size_t i = 0, size = oat_files.size(); i != size; ++i) { diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 1eec59279e..d3e920f9a0 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -571,10 +571,9 @@ bool OatWriter::WriteAndOpenDexFiles( std::vector> dex_files; // Initialize VDEX and OAT headers. - if (kIsVdexEnabled) { - // Reserve space for Vdex header and checksums. - vdex_size_ = sizeof(VdexFile::Header) + oat_dex_files_.size() * sizeof(VdexFile::VdexChecksum); - } + + // Reserve space for Vdex header and checksums. + vdex_size_ = sizeof(VdexFile::Header) + oat_dex_files_.size() * sizeof(VdexFile::VdexChecksum); oat_size_ = InitOatHeader(instruction_set, instruction_set_features, dchecked_integral_cast(oat_dex_files_.size()), @@ -582,28 +581,12 @@ bool OatWriter::WriteAndOpenDexFiles( ChecksumUpdatingOutputStream checksum_updating_rodata(oat_rodata, oat_header_.get()); - if (kIsVdexEnabled) { - std::unique_ptr vdex_out = - std::make_unique(std::make_unique(vdex_file)); - // Write DEX files into VDEX, mmap and open them. - if (!WriteDexFiles(vdex_out.get(), vdex_file, update_input_vdex) || - !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) { - return false; - } - } else { - DCHECK(!update_input_vdex); - // Write DEX files into OAT, mmap and open them. - if (!WriteDexFiles(oat_rodata, vdex_file, update_input_vdex) || - !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) { - return false; - } - - // Do a bulk checksum update for Dex[]. Doing it piece by piece would be - // difficult because we're not using the OutputStream directly. - if (!oat_dex_files_.empty()) { - size_t size = oat_size_ - oat_dex_files_[0].dex_file_offset_; - oat_header_->UpdateChecksum(dex_files_map->Begin(), size); - } + std::unique_ptr vdex_out = + std::make_unique(std::make_unique(vdex_file)); + // Write DEX files into VDEX, mmap and open them. + if (!WriteDexFiles(vdex_out.get(), vdex_file, update_input_vdex) || + !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) { + return false; } // Write type lookup tables into the oat file. @@ -755,13 +738,12 @@ class OatWriter::OatDexMethodVisitor : public DexMethodVisitor { }; static bool HasCompiledCode(const CompiledMethod* method) { - // The dextodexcompiler puts the quickening info table into the CompiledMethod - // for simplicity. For such methods, we will emit an OatQuickMethodHeader - // only when vdex is disabled. - return method != nullptr && (!method->GetQuickCode().empty() || !kIsVdexEnabled); + return method != nullptr && !method->GetQuickCode().empty(); } static bool HasQuickeningInfo(const CompiledMethod* method) { + // The dextodexcompiler puts the quickening info table into the CompiledMethod + // for simplicity. return method != nullptr && method->GetQuickCode().empty() && !method->GetVmapTable().empty(); } @@ -1214,23 +1196,17 @@ class OatWriter::LayoutReserveOffsetCodeMethodVisitor : public OrderedMethodVisi // The code offset was 0 when the mapping/vmap table offset was set, so it's set // to 0-offset and we need to adjust it by code_offset. uint32_t code_offset = quick_code_offset - thumb_offset; - if (!compiled_method->GetQuickCode().empty()) { - // If the code is compiled, we write the offset of the stack map relative - // to the code, - if (vmap_table_offset != 0u) { - vmap_table_offset += code_offset; - DCHECK_LT(vmap_table_offset, code_offset); - } - if (method_info_offset != 0u) { - method_info_offset += code_offset; - DCHECK_LT(method_info_offset, code_offset); - } - } else { - CHECK(!kIsVdexEnabled); - // We write the offset of the quickening info relative to the code. + CHECK(!compiled_method->GetQuickCode().empty()); + // If the code is compiled, we write the offset of the stack map relative + // to the code. + if (vmap_table_offset != 0u) { vmap_table_offset += code_offset; DCHECK_LT(vmap_table_offset, code_offset); } + if (method_info_offset != 0u) { + method_info_offset += code_offset; + DCHECK_LT(method_info_offset, code_offset); + } uint32_t frame_size_in_bytes = compiled_method->GetFrameSizeInBytes(); uint32_t core_spill_mask = compiled_method->GetCoreSpillMask(); uint32_t fp_spill_mask = compiled_method->GetFpSpillMask(); @@ -2593,10 +2569,6 @@ class OatWriter::WriteQuickeningIndicesMethodVisitor { }; bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) { - if (!kIsVdexEnabled) { - return true; - } - size_t initial_offset = vdex_size_; size_t start_offset = RoundUp(initial_offset, 4u); @@ -2655,10 +2627,6 @@ bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) { } bool OatWriter::WriteVerifierDeps(OutputStream* vdex_out, verifier::VerifierDeps* verifier_deps) { - if (!kIsVdexEnabled) { - return true; - } - if (verifier_deps == nullptr) { // Nothing to write. Record the offset, but no need // for alignment. @@ -3170,23 +3138,17 @@ bool OatWriter::WriteDexFile(OutputStream* out, } // Update current size and account for the written data. - if (kIsVdexEnabled) { - DCHECK_EQ(vdex_size_, oat_dex_file->dex_file_offset_); - vdex_size_ += oat_dex_file->dex_file_size_; - } else { - DCHECK(!update_input_vdex); - DCHECK_EQ(oat_size_, oat_dex_file->dex_file_offset_); - oat_size_ += oat_dex_file->dex_file_size_; - } + DCHECK_EQ(vdex_size_, oat_dex_file->dex_file_offset_); + vdex_size_ += oat_dex_file->dex_file_size_; size_dex_file_ += oat_dex_file->dex_file_size_; return true; } bool OatWriter::SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file) { // Dex files are required to be 4 byte aligned. - size_t initial_offset = kIsVdexEnabled ? vdex_size_ : oat_size_; + size_t initial_offset = vdex_size_; size_t start_offset = RoundUp(initial_offset, 4); - size_t file_offset = kIsVdexEnabled ? start_offset : (oat_data_offset_ + start_offset); + size_t file_offset = start_offset; size_dex_file_alignment_ += start_offset - initial_offset; // Seek to the start of the dex file and flush any pending operations in the stream. @@ -3211,11 +3173,7 @@ bool OatWriter::SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex return false; } - if (kIsVdexEnabled) { - vdex_size_ = start_offset; - } else { - oat_size_ = start_offset; - } + vdex_size_ = start_offset; oat_dex_file->dex_file_offset_ = start_offset; return true; } @@ -3291,7 +3249,7 @@ bool OatWriter::WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file, ZipEntry* dex_file) { - size_t start_offset = kIsVdexEnabled ? vdex_size_ : oat_data_offset_ + oat_size_; + size_t start_offset = vdex_size_; DCHECK_EQ(static_cast(start_offset), out->Seek(0, kSeekCurrent)); // Extract the dex file and get the extracted size. @@ -3384,7 +3342,7 @@ bool OatWriter::WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file, File* dex_file) { - size_t start_offset = kIsVdexEnabled ? vdex_size_ : oat_data_offset_ + oat_size_; + size_t start_offset = vdex_size_; DCHECK_EQ(static_cast(start_offset), out->Seek(0, kSeekCurrent)); off_t input_offset = lseek(dex_file->Fd(), 0, SEEK_SET); @@ -3480,7 +3438,7 @@ bool OatWriter::OpenDexFiles( } size_t map_offset = oat_dex_files_[0].dex_file_offset_; - size_t length = kIsVdexEnabled ? (vdex_size_ - map_offset) : (oat_size_ - map_offset); + size_t length = vdex_size_ - map_offset; std::string error_msg; std::unique_ptr dex_files_map(MemMap::MapFile( @@ -3488,7 +3446,7 @@ bool OatWriter::OpenDexFiles( PROT_READ | PROT_WRITE, MAP_SHARED, file->Fd(), - kIsVdexEnabled ? map_offset : (oat_data_offset_ + map_offset), + map_offset, /* low_4gb */ false, file->GetPath().c_str(), &error_msg)); @@ -3692,9 +3650,6 @@ bool OatWriter::WriteDexLayoutSections( } bool OatWriter::WriteChecksumsAndVdexHeader(OutputStream* vdex_out) { - if (!kIsVdexEnabled) { - return true; - } // Write checksums off_t actual_offset = vdex_out->Seek(sizeof(VdexFile::Header), kSeekSet); if (actual_offset != sizeof(VdexFile::Header)) { diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 1ee2e4efd0..022aa1b58c 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -192,7 +192,7 @@ class OatTest : public CommonCompilerTest { OutputStream* oat_rodata = elf_writer->StartRoData(); std::unique_ptr opened_dex_files_map; std::vector> opened_dex_files; - if (!oat_writer.WriteAndOpenDexFiles(kIsVdexEnabled ? vdex_file : oat_file, + if (!oat_writer.WriteAndOpenDexFiles(vdex_file, oat_rodata, compiler_driver_->GetInstructionSet(), compiler_driver_->GetInstructionSetFeatures(), @@ -224,15 +224,13 @@ class OatTest : public CommonCompilerTest { oat_writer.GetBssMethodsOffset(), oat_writer.GetBssRootsOffset()); - if (kIsVdexEnabled) { - std::unique_ptr vdex_out = - std::make_unique(std::make_unique(vdex_file)); - if (!oat_writer.WriteVerifierDeps(vdex_out.get(), nullptr)) { - return false; - } - if (!oat_writer.WriteChecksumsAndVdexHeader(vdex_out.get())) { - return false; - } + std::unique_ptr vdex_out = + std::make_unique(std::make_unique(vdex_file)); + if (!oat_writer.WriteVerifierDeps(vdex_out.get(), nullptr)) { + return false; + } + if (!oat_writer.WriteChecksumsAndVdexHeader(vdex_out.get())) { + return false; } if (!oat_writer.WriteRodata(oat_rodata)) { diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index cd2e894e7d..5a3d34c7a5 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -573,46 +573,36 @@ class OatDumper { } if (options_.export_dex_location_) { - if (kIsVdexEnabled) { - std::string error_msg; - std::string vdex_filename = GetVdexFilename(oat_file_.GetLocation()); - if (!OS::FileExists(vdex_filename.c_str())) { - os << "File " << vdex_filename.c_str() << " does not exist\n"; - return false; - } + std::string error_msg; + std::string vdex_filename = GetVdexFilename(oat_file_.GetLocation()); + if (!OS::FileExists(vdex_filename.c_str())) { + os << "File " << vdex_filename.c_str() << " does not exist\n"; + return false; + } - DexFileUniqV vdex_dex_files; - std::unique_ptr vdex_file = OpenVdexUnquicken(vdex_filename, - &vdex_dex_files, - &error_msg); - if (vdex_file.get() == nullptr) { - os << "Failed to open vdex file: " << error_msg << "\n"; - return false; - } - if (oat_dex_files_.size() != vdex_dex_files.size()) { - os << "Dex files number in Vdex file does not match Dex files number in Oat file: " - << vdex_dex_files.size() << " vs " << oat_dex_files_.size() << '\n'; - return false; - } + DexFileUniqV vdex_dex_files; + std::unique_ptr vdex_file = OpenVdexUnquicken(vdex_filename, + &vdex_dex_files, + &error_msg); + if (vdex_file.get() == nullptr) { + os << "Failed to open vdex file: " << error_msg << "\n"; + return false; + } + if (oat_dex_files_.size() != vdex_dex_files.size()) { + os << "Dex files number in Vdex file does not match Dex files number in Oat file: " + << vdex_dex_files.size() << " vs " << oat_dex_files_.size() << '\n'; + return false; + } - size_t i = 0; - for (const auto& vdex_dex_file : vdex_dex_files) { - const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; - CHECK(oat_dex_file != nullptr); - CHECK(vdex_dex_file != nullptr); - if (!ExportDexFile(os, *oat_dex_file, vdex_dex_file.get())) { - success = false; - } - i++; - } - } else { - 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); - if (!ExportDexFile(os, *oat_dex_file, /* vdex_dex_file */ nullptr)) { - success = false; - } + size_t i = 0; + for (const auto& vdex_dex_file : vdex_dex_files) { + const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; + CHECK(oat_dex_file != nullptr); + CHECK(vdex_dex_file != nullptr); + if (!ExportDexFile(os, *oat_dex_file, vdex_dex_file.get())) { + success = false; } + i++; } } @@ -1367,7 +1357,7 @@ class OatDumper { vios->Stream() << StringPrintf("(offset=0x%08x)\n", vmap_table_offset); size_t vmap_table_offset_limit = - (kIsVdexEnabled && IsMethodGeneratedByDexToDexCompiler(oat_method, code_item_accessor)) + IsMethodGeneratedByDexToDexCompiler(oat_method, code_item_accessor) ? oat_file_.GetVdexFile()->Size() : method_header->GetCode() - oat_file_.Begin(); if (vmap_table_offset >= vmap_table_offset_limit) { diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 0a108f93c5..fa0c501e31 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -563,23 +563,14 @@ bool ArtMethod::EqualParameters(Handle> param return true; } -const uint8_t* ArtMethod::GetQuickenedInfo(PointerSize pointer_size) { - if (kIsVdexEnabled) { - const DexFile& dex_file = GetDeclaringClass()->GetDexFile(); - const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); - if (oat_dex_file == nullptr || (oat_dex_file->GetOatFile() == nullptr)) { - return nullptr; - } - return oat_dex_file->GetOatFile()->GetVdexFile()->GetQuickenedInfoOf( - dex_file, GetCodeItemOffset()); - } else { - bool found = false; - OatFile::OatMethod oat_method = FindOatMethodFor(this, pointer_size, &found); - if (!found || (oat_method.GetQuickCode() != nullptr)) { - return nullptr; - } - return oat_method.GetVmapTable(); +const uint8_t* ArtMethod::GetQuickenedInfo() { + const DexFile& dex_file = GetDeclaringClass()->GetDexFile(); + const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); + if (oat_dex_file == nullptr || (oat_dex_file->GetOatFile() == nullptr)) { + return nullptr; } + return oat_dex_file->GetOatFile()->GetVdexFile()->GetQuickenedInfoOf( + dex_file, GetCodeItemOffset()); } const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { diff --git a/runtime/art_method.h b/runtime/art_method.h index c17eef1834..c5c1c513c1 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -657,7 +657,7 @@ class ArtMethod FINAL { return hotness_count_; } - const uint8_t* GetQuickenedInfo(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); + const uint8_t* GetQuickenedInfo() 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/globals.h b/runtime/globals.h index 53932fd8cd..f14d6e95a6 100644 --- a/runtime/globals.h +++ b/runtime/globals.h @@ -125,12 +125,6 @@ static constexpr TraceClockSource kDefaultTraceClockSource = TraceClockSource::k static constexpr bool kDefaultMustRelocate = true; -#ifdef ART_ENABLE_VDEX -static constexpr bool kIsVdexEnabled = true; -#else -static constexpr bool kIsVdexEnabled = false; -#endif - // Size of a heap reference. static constexpr size_t kHeapReferenceSize = sizeof(uint32_t); diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 4429adeb81..69bd46d4c1 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -193,7 +193,7 @@ OatFileBase* OatFileBase::OpenOatFile(const std::string& vdex_filename, ret->PreLoad(); - if (kIsVdexEnabled && !ret->LoadVdex(vdex_filename, writable, low_4gb, error_msg)) { + if (!ret->LoadVdex(vdex_filename, writable, low_4gb, error_msg)) { return nullptr; } @@ -233,7 +233,7 @@ OatFileBase* OatFileBase::OpenOatFile(int vdex_fd, std::string* error_msg) { std::unique_ptr ret(new kOatFileBaseSubType(oat_location, executable)); - if (kIsVdexEnabled && !ret->LoadVdex(vdex_fd, vdex_location, writable, low_4gb, error_msg)) { + if (!ret->LoadVdex(vdex_fd, vdex_location, writable, low_4gb, error_msg)) { return nullptr; } @@ -1275,7 +1275,7 @@ OatFile* OatFile::Open(const std::string& oat_filename, std::string vdex_filename = GetVdexFilename(oat_filename); // Check that the files even exist, fast-fail. - if (kIsVdexEnabled && !OS::FileExists(vdex_filename.c_str())) { + if (!OS::FileExists(vdex_filename.c_str())) { *error_msg = StringPrintf("File %s does not exist.", vdex_filename.c_str()); return nullptr; } else if (!OS::FileExists(oat_filename.c_str())) { @@ -1427,11 +1427,11 @@ const uint8_t* OatFile::BssEnd() const { } const uint8_t* OatFile::DexBegin() const { - return kIsVdexEnabled ? vdex_->Begin() : Begin(); + return vdex_->Begin(); } const uint8_t* OatFile::DexEnd() const { - return kIsVdexEnabled ? vdex_->End() : End(); + return vdex_->End(); } ArrayRef OatFile::GetBssMethods() const { diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 378ce2cff2..113b7b984a 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -496,17 +496,10 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& // Verify the dex checksum. std::string error_msg; - if (kIsVdexEnabled) { - VdexFile* vdex = file.GetVdexFile(); - if (!DexChecksumUpToDate(*vdex, &error_msg)) { - LOG(ERROR) << error_msg; - return kOatDexOutOfDate; - } - } else { - if (!DexChecksumUpToDate(file, &error_msg)) { - LOG(ERROR) << error_msg; - return kOatDexOutOfDate; - } + VdexFile* vdex = file.GetVdexFile(); + if (!DexChecksumUpToDate(*vdex, &error_msg)) { + LOG(ERROR) << error_msg; + return kOatDexOutOfDate; } CompilerFilter::Filter current_compiler_filter = file.GetCompilerFilter(); diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 65d01a4d52..f1f35dd8b5 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -398,11 +398,6 @@ TEST_F(OatFileAssistantTest, OatUpToDateSymLink) { // Case: We have a DEX file and up-to-date (ODEX) VDEX file for it, but no // ODEX file. TEST_F(OatFileAssistantTest, VdexUpToDateNoOdex) { - // This test case is only meaningful if vdex is enabled. - if (!kIsVdexEnabled) { - return; - } - std::string dex_location = GetScratchDir() + "/VdexUpToDateNoOdex.jar"; std::string odex_location = GetOdexDir() + "/VdexUpToDateNoOdex.oat"; @@ -446,10 +441,6 @@ TEST_F(OatFileAssistantTest, EmptyVdexOdex) { // Case: We have a DEX file and up-to-date (OAT) VDEX file for it, but no OAT // file. TEST_F(OatFileAssistantTest, VdexUpToDateNoOat) { - // This test case is only meaningful if vdex is enabled. - if (!kIsVdexEnabled) { - return; - } if (IsExecutedAsRoot()) { // We cannot simulate non writable locations when executed as root: b/38000545. LOG(ERROR) << "Test skipped because it's running as root"; @@ -668,11 +659,6 @@ TEST_F(OatFileAssistantTest, OatDexOutOfDate) { // Case: We have a DEX file and an (ODEX) VDEX file out of date with respect // to the dex checksum, but no ODEX file. TEST_F(OatFileAssistantTest, VdexDexOutOfDate) { - // This test case is only meaningful if vdex is enabled. - if (!kIsVdexEnabled) { - return; - } - std::string dex_location = GetScratchDir() + "/VdexDexOutOfDate.jar"; std::string odex_location = GetOdexDir() + "/VdexDexOutOfDate.oat"; @@ -690,11 +676,6 @@ TEST_F(OatFileAssistantTest, VdexDexOutOfDate) { // Case: We have a MultiDEX (ODEX) VDEX file where the non-main multidex entry // is out of date and there is no corresponding ODEX file. TEST_F(OatFileAssistantTest, VdexMultiDexNonMainOutOfDate) { - // This test case is only meaningful if vdex is enabled. - if (!kIsVdexEnabled) { - return; - } - std::string dex_location = GetScratchDir() + "/VdexMultiDexNonMainOutOfDate.jar"; std::string odex_location = GetOdexDir() + "/VdexMultiDexNonMainOutOfDate.odex"; -- GitLab From 88aa6900f62421906af0c3f7a0c245b4f2aef50c Mon Sep 17 00:00:00 2001 From: Richard Uhler Date: Mon, 6 Nov 2017 10:08:27 +0000 Subject: [PATCH 037/226] Fix bug in proguard deobfuscation of file names. Configure proguard to preserve stack traces in the test-dump so we can properly test ahat's deobfuscation of stack traces. Fix a bug in proguard deobfuscation of file names. Specifically, we should always compute the file name based on the class associated with a stack frame, regardless of whether or not the method has been obfuscated. Restructure test-dump program to test more cases of stack frame deobfuscation. Test: m ahat-test, with expanded proguard deobufscation test. Change-Id: Idbb564147c6aea6b9c2091612ecf17c01070d5ac --- tools/ahat/etc/test-dump.pro | 9 + .../android/ahat/proguard/ProguardMap.java | 17 +- tools/ahat/src/test-dump/DumpedStuff.java | 160 ++++++++++++++++++ tools/ahat/src/test-dump/Main.java | 137 --------------- .../ahat/src/test-dump/SuperDumpedStuff.java | 36 ++++ .../com/android/ahat/ProguardMapTest.java | 13 +- .../src/test/com/android/ahat/SiteTest.java | 69 +++++--- 7 files changed, 259 insertions(+), 182 deletions(-) create mode 100644 tools/ahat/src/test-dump/DumpedStuff.java create mode 100644 tools/ahat/src/test-dump/SuperDumpedStuff.java diff --git a/tools/ahat/etc/test-dump.pro b/tools/ahat/etc/test-dump.pro index 284e4b8621..296d411cfd 100644 --- a/tools/ahat/etc/test-dump.pro +++ b/tools/ahat/etc/test-dump.pro @@ -3,3 +3,12 @@ public static void main(java.lang.String[]); } +-keep public class SuperDumpedStuff { + public void allocateObjectAtUnObfSuperSite(); +} + +# Produce useful obfuscated stack traces so we can test useful deobfuscation +# of stack traces. +-renamesourcefileattribute SourceFile +-keepattributes SourceFile,LineNumberTable + 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 131bbf3cf6..32bb209dc6 100644 --- a/tools/ahat/src/main/com/android/ahat/proguard/ProguardMap.java +++ b/tools/ahat/src/main/com/android/ahat/proguard/ProguardMap.java @@ -88,12 +88,10 @@ public class ProguardMap { String key = obfuscatedMethodName + clearSignature; FrameData frame = mFrames.get(key); if (frame == null) { - return new Frame(obfuscatedMethodName, clearSignature, - obfuscatedFilename, obfuscatedLine); + frame = new FrameData(obfuscatedMethodName, 0); } return new Frame(frame.clearMethodName, clearSignature, - getFileName(clearClassName, frame.clearMethodName), - obfuscatedLine - frame.lineDelta); + getFileName(clearClassName), obfuscatedLine - frame.lineDelta); } } @@ -313,15 +311,10 @@ public class ProguardMap { return builder.toString(); } - // Return a file name for the given clear class name and method. - private static String getFileName(String clearClass, String method) { - int dot = method.lastIndexOf('.'); - if (dot != -1) { - clearClass = method.substring(0, dot); - } - + // Return a file name for the given clear class name. + private static String getFileName(String clearClass) { String filename = clearClass; - dot = filename.lastIndexOf('.'); + int dot = filename.lastIndexOf('.'); if (dot != -1) { filename = filename.substring(dot + 1); } diff --git a/tools/ahat/src/test-dump/DumpedStuff.java b/tools/ahat/src/test-dump/DumpedStuff.java new file mode 100644 index 0000000000..98ead07492 --- /dev/null +++ b/tools/ahat/src/test-dump/DumpedStuff.java @@ -0,0 +1,160 @@ +/* + * 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.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import libcore.util.NativeAllocationRegistry; + +// We take a heap dump that includes a single instance of this +// DumpedStuff class. Objects stored as fields in this class can be easily +// found in the hprof dump by searching for the instance of the DumpedStuff +// class and reading the desired field. +public class DumpedStuff extends SuperDumpedStuff { + private void allocateObjectAtKnownSite() { + objectAllocatedAtKnownSite = new Object(); + allocateObjectAtKnownSubSite(); + allocateObjectAtObfSuperSite(); + allocateObjectAtUnObfSuperSite(); + allocateObjectAtOverriddenSite(); + } + + private void allocateObjectAtKnownSubSite() { + objectAllocatedAtKnownSubSite = new Object(); + } + + public void allocateObjectAtOverriddenSite() { + objectAllocatedAtOverriddenSite = new Object(); + } + + DumpedStuff(boolean baseline) { + allocateObjectAtKnownSite(); + + int n = baseline ? 400000 : 1000000; + bigArray = new byte[n]; + for (int i = 0; i < n; i++) { + bigArray[i] = (byte)((i * i) & 0xFF); + } + + // 0x12345, 50000, and 0xABCDABCD are arbitrary values. + NativeAllocationRegistry registry = new NativeAllocationRegistry( + Main.class.getClassLoader(), 0x12345, 50000); + registry.registerNativeAllocation(anObject, 0xABCDABCD); + + { + Object object = new Object(); + aLongStrongPathToSamplePathObject = new Reference(new Reference(new Reference(object))); + aShortWeakPathToSamplePathObject = new WeakReference(new Reference(object)); + } + + addedObject = baseline ? null : new AddedObject(); + removedObject = baseline ? new RemovedObject() : null; + modifiedObject = new ModifiedObject(); + modifiedObject.value = baseline ? 5 : 8; + modifiedObject.modifiedRefField = baseline ? "A1" : "A2"; + modifiedObject.unmodifiedRefField = "B"; + modifiedStaticField = baseline ? "C1" : "C2"; + modifiedArray = baseline ? new int[]{0, 1, 2, 3} : new int[]{3, 1, 2, 0}; + + // Deep matching dominator trees shouldn't smash the stack when we try + // to diff them. Make some deep dominator trees to help test it. + for (int i = 0; i < 10000; i++) { + StackSmasher smasher = new StackSmasher(); + smasher.child = stackSmasher; + stackSmasher = smasher; + + if (!baseline) { + smasher = new StackSmasher(); + smasher.child = stackSmasherAdded; + stackSmasherAdded = smasher; + } + } + + gcPathArray[2].right.left = gcPathArray[2].left.right; + } + + public static class ObjectTree { + public ObjectTree left; + public ObjectTree right; + + public ObjectTree(ObjectTree left, ObjectTree right) { + this.left = left; + this.right = right; + } + } + + public static class AddedObject { + } + + public static class RemovedObject { + } + + public static class UnchangedObject { + } + + public static class ModifiedObject { + public int value; + public String modifiedRefField; + public String unmodifiedRefField; + } + + public static class StackSmasher { + public StackSmasher child; + } + + public static class Reference { + public Object referent; + + public Reference(Object referent) { + this.referent = referent; + } + } + + public String basicString = "hello, world"; + public String nonAscii = "Sigma (Æ©) is not ASCII"; + public String embeddedZero = "embedded\0..."; // Non-ASCII for string compression purposes. + public char[] charArray = "char thing".toCharArray(); + public String nullString = null; + public Object anObject = new Object(); + public Reference aReference = new Reference(anObject); + public ReferenceQueue referenceQueue = new ReferenceQueue(); + public PhantomReference aPhantomReference = new PhantomReference(anObject, referenceQueue); + public WeakReference aWeakReference = new WeakReference(anObject, referenceQueue); + public WeakReference aNullReferentReference = new WeakReference(null, referenceQueue); + public SoftReference aSoftReference = new SoftReference(new Object()); + public byte[] bigArray; + public ObjectTree[] gcPathArray = new ObjectTree[]{null, null, + new ObjectTree( + new ObjectTree(null, new ObjectTree(null, null)), + new ObjectTree(null, null)), + null}; + public Reference aLongStrongPathToSamplePathObject; + public WeakReference aShortWeakPathToSamplePathObject; + public WeakReference aWeakRefToGcRoot = new WeakReference(Main.class); + public SoftReference aWeakChain = new SoftReference(new Reference(new Reference(new Object()))); + public Object[] basicStringRef; + public AddedObject addedObject; + public UnchangedObject unchangedObject = new UnchangedObject(); + public RemovedObject removedObject; + public ModifiedObject modifiedObject; + public StackSmasher stackSmasher; + public StackSmasher stackSmasherAdded; + public static String modifiedStaticField; + public int[] modifiedArray; + public Object objectAllocatedAtKnownSite; + public Object objectAllocatedAtKnownSubSite; +} diff --git a/tools/ahat/src/test-dump/Main.java b/tools/ahat/src/test-dump/Main.java index 079be7da81..de3674846b 100644 --- a/tools/ahat/src/test-dump/Main.java +++ b/tools/ahat/src/test-dump/Main.java @@ -16,11 +16,6 @@ import dalvik.system.VMDebug; import java.io.IOException; -import java.lang.ref.PhantomReference; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.SoftReference; -import java.lang.ref.WeakReference; -import libcore.util.NativeAllocationRegistry; import org.apache.harmony.dalvik.ddmc.DdmVmInternal; /** @@ -31,138 +26,6 @@ public class Main { // collected before we take the heap dump. public static DumpedStuff stuff; - public static class ObjectTree { - public ObjectTree left; - public ObjectTree right; - - public ObjectTree(ObjectTree left, ObjectTree right) { - this.left = left; - this.right = right; - } - } - - public static class AddedObject { - } - - public static class RemovedObject { - } - - public static class UnchangedObject { - } - - public static class ModifiedObject { - public int value; - public String modifiedRefField; - public String unmodifiedRefField; - } - - public static class StackSmasher { - public StackSmasher child; - } - - public static class Reference { - public Object referent; - - public Reference(Object referent) { - this.referent = referent; - } - } - - // We will take a heap dump that includes a single instance of this - // DumpedStuff class. Objects stored as fields in this class can be easily - // found in the hprof dump by searching for the instance of the DumpedStuff - // class and reading the desired field. - public static class DumpedStuff { - public String basicString = "hello, world"; - public String nonAscii = "Sigma (Æ©) is not ASCII"; - public String embeddedZero = "embedded\0..."; // Non-ASCII for string compression purposes. - public char[] charArray = "char thing".toCharArray(); - public String nullString = null; - public Object anObject = new Object(); - public Reference aReference = new Reference(anObject); - public ReferenceQueue referenceQueue = new ReferenceQueue(); - public PhantomReference aPhantomReference = new PhantomReference(anObject, referenceQueue); - public WeakReference aWeakReference = new WeakReference(anObject, referenceQueue); - public WeakReference aNullReferentReference = new WeakReference(null, referenceQueue); - public SoftReference aSoftReference = new SoftReference(new Object()); - public byte[] bigArray; - public ObjectTree[] gcPathArray = new ObjectTree[]{null, null, - new ObjectTree( - new ObjectTree(null, new ObjectTree(null, null)), - new ObjectTree(null, null)), - null}; - public Reference aLongStrongPathToSamplePathObject; - public WeakReference aShortWeakPathToSamplePathObject; - public WeakReference aWeakRefToGcRoot = new WeakReference(Main.class); - public SoftReference aWeakChain = new SoftReference(new Reference(new Reference(new Object()))); - public Object[] basicStringRef; - public AddedObject addedObject; - public UnchangedObject unchangedObject = new UnchangedObject(); - public RemovedObject removedObject; - public ModifiedObject modifiedObject; - public StackSmasher stackSmasher; - public StackSmasher stackSmasherAdded; - public static String modifiedStaticField; - public int[] modifiedArray; - public Object objectAllocatedAtKnownSite1; - public Object objectAllocatedAtKnownSite2; - - private void allocateObjectAtKnownSite1() { - objectAllocatedAtKnownSite1 = new Object(); - allocateObjectAtKnownSite2(); - } - - private void allocateObjectAtKnownSite2() { - objectAllocatedAtKnownSite2 = new Object(); - } - - DumpedStuff(boolean baseline) { - int n = baseline ? 400000 : 1000000; - bigArray = new byte[n]; - for (int i = 0; i < n; i++) { - bigArray[i] = (byte)((i * i) & 0xFF); - } - - // 0x12345, 50000, and 0xABCDABCD are arbitrary values. - NativeAllocationRegistry registry = new NativeAllocationRegistry( - Main.class.getClassLoader(), 0x12345, 50000); - registry.registerNativeAllocation(anObject, 0xABCDABCD); - - { - Object object = new Object(); - aLongStrongPathToSamplePathObject = new Reference(new Reference(new Reference(object))); - aShortWeakPathToSamplePathObject = new WeakReference(new Reference(object)); - } - - addedObject = baseline ? null : new AddedObject(); - removedObject = baseline ? new RemovedObject() : null; - modifiedObject = new ModifiedObject(); - modifiedObject.value = baseline ? 5 : 8; - modifiedObject.modifiedRefField = baseline ? "A1" : "A2"; - modifiedObject.unmodifiedRefField = "B"; - modifiedStaticField = baseline ? "C1" : "C2"; - modifiedArray = baseline ? new int[]{0, 1, 2, 3} : new int[]{3, 1, 2, 0}; - - allocateObjectAtKnownSite1(); - - // Deep matching dominator trees shouldn't smash the stack when we try - // to diff them. Make some deep dominator trees to help test it. - for (int i = 0; i < 10000; i++) { - StackSmasher smasher = new StackSmasher(); - smasher.child = stackSmasher; - stackSmasher = smasher; - - if (!baseline) { - smasher = new StackSmasher(); - smasher.child = stackSmasherAdded; - stackSmasherAdded = smasher; - } - } - - gcPathArray[2].right.left = gcPathArray[2].left.right; - } - } - public static void main(String[] args) throws IOException { if (args.length < 1) { System.err.println("no output file specified"); diff --git a/tools/ahat/src/test-dump/SuperDumpedStuff.java b/tools/ahat/src/test-dump/SuperDumpedStuff.java new file mode 100644 index 0000000000..5ec62aecb1 --- /dev/null +++ b/tools/ahat/src/test-dump/SuperDumpedStuff.java @@ -0,0 +1,36 @@ +/* + * 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. + */ + +// A super class for DumpedStuff to test deobfuscation of methods inherited +// from the super class. +public class SuperDumpedStuff { + + public void allocateObjectAtObfSuperSite() { + objectAllocatedAtObfSuperSite = new Object(); + } + + public void allocateObjectAtUnObfSuperSite() { + objectAllocatedAtUnObfSuperSite = new Object(); + } + + public void allocateObjectAtOverriddenSite() { + objectAllocatedAtOverriddenSite = new Object(); + } + + public Object objectAllocatedAtObfSuperSite; + public Object objectAllocatedAtUnObfSuperSite; + public Object objectAllocatedAtOverriddenSite; +} diff --git a/tools/ahat/src/test/com/android/ahat/ProguardMapTest.java b/tools/ahat/src/test/com/android/ahat/ProguardMapTest.java index ad40f45665..02976b5285 100644 --- a/tools/ahat/src/test/com/android/ahat/ProguardMapTest.java +++ b/tools/ahat/src/test/com/android/ahat/ProguardMapTest.java @@ -45,7 +45,6 @@ public class ProguardMapTest { + " 64:66:class.with.only.Fields methodWithObfRes() -> n\n" + " 80:80:void lineObfuscatedMethod():8:8 -> o\n" + " 90:90:void lineObfuscatedMethod2():9 -> p\n" - + " 120:121:void method.from.a.Superclass.supermethod() -> q\n" ; @Test @@ -160,12 +159,10 @@ public class ProguardMapTest { assertEquals("Methods.java", frame.filename); assertEquals(13, frame.line); - frame = map.getFrame("class.with.Methods", "q", "()V", "SourceFile.java", 120); - // TODO: Should this be "supermethod", instead of - // "method.from.a.Superclass.supermethod"? - assertEquals("method.from.a.Superclass.supermethod", frame.method); - assertEquals("()V", frame.signature); - assertEquals("Superclass.java", frame.filename); - assertEquals(120, frame.line); + // Some methods may not have been obfuscated. We should still be able + // to compute the filename properly. + frame = map.getFrame("class.with.Methods", "unObfuscatedMethodName", + "()V", "SourceFile.java", 0); + assertEquals("Methods.java", frame.filename); } } diff --git a/tools/ahat/src/test/com/android/ahat/SiteTest.java b/tools/ahat/src/test/com/android/ahat/SiteTest.java index dc0fe08297..0443d7f264 100644 --- a/tools/ahat/src/test/com/android/ahat/SiteTest.java +++ b/tools/ahat/src/test/com/android/ahat/SiteTest.java @@ -23,6 +23,7 @@ import java.io.IOException; import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; public class SiteTest { @@ -31,36 +32,54 @@ public class SiteTest { TestDump dump = TestDump.getTestDump(); AhatSnapshot snapshot = dump.getAhatSnapshot(); - AhatInstance obj1 = dump.getDumpedAhatInstance("objectAllocatedAtKnownSite1"); - AhatInstance obj2 = dump.getDumpedAhatInstance("objectAllocatedAtKnownSite2"); - Site s2 = obj2.getSite(); - Site s1b = s2.getParent(); - Site s1a = obj1.getSite(); - Site s = s1a.getParent(); + AhatInstance oKnownSite = dump.getDumpedAhatInstance("objectAllocatedAtKnownSite"); + Site sKnownSite = oKnownSite.getSite(); + assertEquals("DumpedStuff.java", sKnownSite.getFilename()); + assertEquals("allocateObjectAtKnownSite", sKnownSite.getMethodName()); + assertEquals(29, sKnownSite.getLineNumber()); + assertSame(sKnownSite, snapshot.getSite(sKnownSite.getId())); - // TODO: The following commented out assertion fails due to a problem with - // proguard deobfuscation. That bug should be fixed. - // assertEquals("Main.java", s.getFilename()); + AhatInstance oKnownSubSite = dump.getDumpedAhatInstance("objectAllocatedAtKnownSubSite"); + Site sKnownSubSite = oKnownSubSite.getSite(); + assertEquals("DumpedStuff.java", sKnownSubSite.getFilename()); + assertEquals("allocateObjectAtKnownSubSite", sKnownSubSite.getMethodName()); + assertEquals(37, sKnownSubSite.getLineNumber()); + assertSame(sKnownSubSite, snapshot.getSite(sKnownSubSite.getId())); - assertEquals("Main.java", s1a.getFilename()); - assertEquals("Main.java", s1b.getFilename()); - assertEquals("Main.java", s2.getFilename()); + Site sKnownSubSiteParent = sKnownSubSite.getParent(); + assertEquals("DumpedStuff.java", sKnownSubSiteParent.getFilename()); + assertEquals("allocateObjectAtKnownSite", sKnownSubSiteParent.getMethodName()); + assertEquals(30, sKnownSubSiteParent.getLineNumber()); + assertSame(sKnownSubSiteParent, snapshot.getSite(sKnownSubSiteParent.getId())); - assertEquals("allocateObjectAtKnownSite1", s1a.getMethodName()); - assertEquals("allocateObjectAtKnownSite1", s1b.getMethodName()); - assertEquals("allocateObjectAtKnownSite2", s2.getMethodName()); + assertNotSame(sKnownSite, sKnownSubSiteParent); + assertSame(sKnownSite.getParent(), sKnownSubSiteParent.getParent()); - // TODO: The following commented out assertion fails due to a problem with - // stack frame line numbers - we don't get different line numbers - // for the different sites, so they are indistiguishable. The - // problem with line numbers should be understood and fixed. - // assertNotSame(s1a, s1b); + Site sKnownSiteParent = sKnownSite.getParent(); + assertEquals("DumpedStuff.java", sKnownSiteParent.getFilename()); + assertEquals("", sKnownSiteParent.getMethodName()); + assertEquals(45, sKnownSiteParent.getLineNumber()); + assertSame(sKnownSiteParent, snapshot.getSite(sKnownSiteParent.getId())); - assertSame(s1a.getParent(), s1b.getParent()); + AhatInstance oObfSuperSite = dump.getDumpedAhatInstance("objectAllocatedAtObfSuperSite"); + Site sObfSuperSite = oObfSuperSite.getSite(); + assertEquals("SuperDumpedStuff.java", sObfSuperSite.getFilename()); + assertEquals("allocateObjectAtObfSuperSite", sObfSuperSite.getMethodName()); + assertEquals(22, sObfSuperSite.getLineNumber()); + assertSame(sObfSuperSite, snapshot.getSite(sObfSuperSite.getId())); - assertSame(s, snapshot.getSite(s.getId())); - assertSame(s1a, snapshot.getSite(s1a.getId())); - assertSame(s1b, snapshot.getSite(s1b.getId())); - assertSame(s2, snapshot.getSite(s2.getId())); + AhatInstance oUnObfSuperSite = dump.getDumpedAhatInstance("objectAllocatedAtUnObfSuperSite"); + Site sUnObfSuperSite = oUnObfSuperSite.getSite(); + assertEquals("SuperDumpedStuff.java", sUnObfSuperSite.getFilename()); + assertEquals("allocateObjectAtUnObfSuperSite", sUnObfSuperSite.getMethodName()); + assertEquals(26, sUnObfSuperSite.getLineNumber()); + assertSame(sUnObfSuperSite, snapshot.getSite(sUnObfSuperSite.getId())); + + AhatInstance oOverriddenSite = dump.getDumpedAhatInstance("objectAllocatedAtOverriddenSite"); + Site sOverriddenSite = oOverriddenSite.getSite(); + assertEquals("DumpedStuff.java", sOverriddenSite.getFilename()); + assertEquals("allocateObjectAtOverriddenSite", sOverriddenSite.getMethodName()); + assertEquals(41, sOverriddenSite.getLineNumber()); + assertSame(sOverriddenSite, snapshot.getSite(sOverriddenSite.getId())); } } -- GitLab From 06ffecfe4260d64850df23023ed94b167438d996 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 14 Nov 2017 10:31:54 +0000 Subject: [PATCH 038/226] Don't add a '/' if it's already there. Test: class_loader_context_test Change-Id: I3fd310cf9c2da6c295ba479d0f8f4ba3137feda1 --- runtime/class_loader_context.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index b62764f4c6..54e75588b9 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -209,7 +209,7 @@ bool ClassLoaderContext::OpenDexFiles(InstructionSet isa, const std::string& cla // If path is relative, append it to the provided base directory. std::string location = cp_elem; if (location[0] != '/' && !classpath_dir.empty()) { - location = classpath_dir + '/' + location; + location = classpath_dir + (classpath_dir.back() == '/' ? "" : "/") + location; } std::string error_msg; -- GitLab From 103fa6ed700f4efca4ccf17eeef3fef231ceb7b5 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 9 Nov 2017 17:18:05 +0000 Subject: [PATCH 039/226] ART: Destroy unprocessed tasks in TaskProcessor. In the past I've seen valgrind complain about leaked tasks in the TaskProcessor, so make sure we clean up properly. Also hold the mutex and condition variable directly in the object instead of allocating separate objects on the heap. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Change-Id: If0d4ac39bf3d8e2aa9b465186d4fa7c3cb746718 --- runtime/gc/task_processor.cc | 39 ++++++++++++++++++++---------------- runtime/gc/task_processor.h | 20 +++++++++--------- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/runtime/gc/task_processor.cc b/runtime/gc/task_processor.cc index e928644054..64e8322a0d 100644 --- a/runtime/gc/task_processor.cc +++ b/runtime/gc/task_processor.cc @@ -23,32 +23,37 @@ namespace art { namespace gc { TaskProcessor::TaskProcessor() - : lock_(new Mutex("Task processor lock", kReferenceProcessorLock)), is_running_(false), + : lock_("Task processor lock", kReferenceProcessorLock), + cond_("Task processor condition", lock_), + is_running_(false), running_thread_(nullptr) { - // Piggyback off the reference processor lock level. - cond_.reset(new ConditionVariable("Task processor condition", *lock_)); } TaskProcessor::~TaskProcessor() { - delete lock_; + if (!tasks_.empty()) { + LOG(WARNING) << "TaskProcessor: Finalizing " << tasks_.size() << " unprocessed tasks."; + for (HeapTask* task : tasks_) { + task->Finalize(); + } + } } void TaskProcessor::AddTask(Thread* self, HeapTask* task) { ScopedThreadStateChange tsc(self, kWaitingForTaskProcessor); - MutexLock mu(self, *lock_); + MutexLock mu(self, lock_); tasks_.insert(task); - cond_->Signal(self); + cond_.Signal(self); } HeapTask* TaskProcessor::GetTask(Thread* self) { ScopedThreadStateChange tsc(self, kWaitingForTaskProcessor); - MutexLock mu(self, *lock_); + MutexLock mu(self, lock_); while (true) { if (tasks_.empty()) { if (!is_running_) { return nullptr; } - cond_->Wait(self); // Empty queue, wait until we are signalled. + cond_.Wait(self); // Empty queue, wait until we are signalled. } else { // Non empty queue, look at the top element and see if we are ready to run it. const uint64_t current_time = NanoTime(); @@ -61,18 +66,18 @@ HeapTask* TaskProcessor::GetTask(Thread* self) { return task; } DCHECK_GT(target_time, current_time); - // Wait untl we hit the target run time. + // Wait until we hit the target run time. const uint64_t delta_time = target_time - current_time; const uint64_t ms_delta = NsToMs(delta_time); const uint64_t ns_delta = delta_time - MsToNs(ms_delta); - cond_->TimedWait(self, static_cast(ms_delta), static_cast(ns_delta)); + cond_.TimedWait(self, static_cast(ms_delta), static_cast(ns_delta)); } } UNREACHABLE(); } void TaskProcessor::UpdateTargetRunTime(Thread* self, HeapTask* task, uint64_t new_target_time) { - MutexLock mu(self, *lock_); + MutexLock mu(self, lock_); // Find the task. auto range = tasks_.equal_range(task); for (auto it = range.first; it != range.second; ++it) { @@ -85,7 +90,7 @@ void TaskProcessor::UpdateTargetRunTime(Thread* self, HeapTask* task, uint64_t n // If we became the first task then we may need to signal since we changed the task that we // are sleeping on. if (*tasks_.begin() == task) { - cond_->Signal(self); + cond_.Signal(self); } return; } @@ -94,24 +99,24 @@ void TaskProcessor::UpdateTargetRunTime(Thread* self, HeapTask* task, uint64_t n } bool TaskProcessor::IsRunning() const { - MutexLock mu(Thread::Current(), *lock_); + MutexLock mu(Thread::Current(), lock_); return is_running_; } Thread* TaskProcessor::GetRunningThread() const { - MutexLock mu(Thread::Current(), *lock_); + MutexLock mu(Thread::Current(), lock_); return running_thread_; } void TaskProcessor::Stop(Thread* self) { - MutexLock mu(self, *lock_); + MutexLock mu(self, lock_); is_running_ = false; running_thread_ = nullptr; - cond_->Broadcast(self); + cond_.Broadcast(self); } void TaskProcessor::Start(Thread* self) { - MutexLock mu(self, *lock_); + MutexLock mu(self, lock_); is_running_ = true; running_thread_ = self; } diff --git a/runtime/gc/task_processor.h b/runtime/gc/task_processor.h index e40fa06319..f6b5607037 100644 --- a/runtime/gc/task_processor.h +++ b/runtime/gc/task_processor.h @@ -54,17 +54,17 @@ class TaskProcessor { public: TaskProcessor(); virtual ~TaskProcessor(); - void AddTask(Thread* self, HeapTask* task) REQUIRES(!*lock_); - HeapTask* GetTask(Thread* self) REQUIRES(!*lock_); - void Start(Thread* self) REQUIRES(!*lock_); + void AddTask(Thread* self, HeapTask* task) REQUIRES(!lock_); + HeapTask* GetTask(Thread* self) REQUIRES(!lock_); + void Start(Thread* self) REQUIRES(!lock_); // Stop tells the RunAllTasks to finish up the remaining tasks as soon as // possible then return. - void Stop(Thread* self) REQUIRES(!*lock_); - void RunAllTasks(Thread* self) REQUIRES(!*lock_); - bool IsRunning() const REQUIRES(!*lock_); + void Stop(Thread* self) REQUIRES(!lock_); + void RunAllTasks(Thread* self) REQUIRES(!lock_); + bool IsRunning() const REQUIRES(!lock_); void UpdateTargetRunTime(Thread* self, HeapTask* target_time, uint64_t new_target_time) - REQUIRES(!*lock_); - Thread* GetRunningThread() const REQUIRES(!*lock_); + REQUIRES(!lock_); + Thread* GetRunningThread() const REQUIRES(!lock_); private: class CompareByTargetRunTime { @@ -74,9 +74,9 @@ class TaskProcessor { } }; - mutable Mutex* lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + ConditionVariable cond_ GUARDED_BY(lock_); bool is_running_ GUARDED_BY(lock_); - std::unique_ptr cond_ GUARDED_BY(lock_); std::multiset tasks_ GUARDED_BY(lock_); Thread* running_thread_ GUARDED_BY(lock_); -- GitLab From b8db20098ea13d51e697b7a27931ba5d300639f4 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 14 Nov 2017 14:02:23 +0000 Subject: [PATCH 040/226] Put back kWithoutReadBarrier. It was mistakenly removed in: https://android-review.googlesource.com/#/c/platform/art/+/518395/ Bug: 65574695 Bug: 35644369 Test: test.py Change-Id: Iafe8d3187fb9a609d7531a85127cd05bf1a1eaf3 --- runtime/art_method.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/art_method.h b/runtime/art_method.h index c17eef1834..fb6174e867 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -464,7 +464,7 @@ class ArtMethod FINAL { // where the declaring class is treated as a weak reference (accessing it with // a read barrier would either prevent unloading the class, or crash the runtime if // the GC wants to unload it). - DCHECK(!IsNative()); + DCHECK(!IsNative()); if (UNLIKELY(IsProxyMethod())) { return nullptr; } -- GitLab From b0b68cfc0d2444e2333adcc5a6bc6f670b89fe83 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 14 Nov 2017 18:11:50 +0000 Subject: [PATCH 041/226] ART: Clean up #includes in jit_code_cache.h . And fix code that relied on those indirect #includes. Also remove forward declaration of art::LinkerPatch because we actually use art::linker::LinkerPatch. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Bug: 65574695 Change-Id: I102affed9a3eacbd21b79c370fbc72b5fc762c96 --- dex2oat/linker/multi_oat_relative_patcher.h | 1 - runtime/jit/jit_code_cache.cc | 5 ++++ runtime/jit/jit_code_cache.h | 25 +++++++++++-------- runtime/oat_file_manager.cc | 3 +++ runtime/oat_file_manager.h | 2 +- .../polymorphic_inline.cc | 1 + test/common/runtime_state.cc | 1 + 7 files changed, 26 insertions(+), 12 deletions(-) diff --git a/dex2oat/linker/multi_oat_relative_patcher.h b/dex2oat/linker/multi_oat_relative_patcher.h index 6683366467..d97be8d3e3 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 LinkerPatch; class InstructionSetFeatures; namespace linker { diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index e1807525ea..32205138bd 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -30,6 +30,7 @@ #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/bitmap-inl.h" #include "gc/scoped_gc_critical_section.h" +#include "handle.h" #include "intern_table.h" #include "jit/jit.h" #include "jit/profiling_info.h" @@ -38,8 +39,10 @@ #include "oat_file-inl.h" #include "oat_quick_method_header.h" #include "object_callbacks.h" +#include "profile_compilation_info.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" +#include "thread-current-inl.h" #include "thread_list.h" namespace art { @@ -182,6 +185,8 @@ JitCodeCache::JitCodeCache(MemMap* code_map, << PrettySize(initial_code_capacity); } +JitCodeCache::~JitCodeCache() {} + bool JitCodeCache::ContainsPc(const void* ptr) const { return code_map_->Begin() <= ptr && ptr < code_map_->End(); } diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 9790e3aa43..46a408590b 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -24,24 +24,33 @@ #include "base/histogram-inl.h" #include "base/macros.h" #include "base/mutex.h" -#include "gc/accounting/bitmap.h" #include "gc_root.h" -#include "jni.h" #include "method_reference.h" -#include "oat_file.h" -#include "profile_compilation_info.h" #include "safe_map.h" -#include "thread_pool.h" namespace art { class ArtMethod; +template class Handle; class LinearAlloc; class InlineCache; class IsMarkedVisitor; class OatQuickMethodHeader; +struct ProfileMethodInfo; class ProfilingInfo; +namespace gc { +namespace accounting { +template class MemoryRangeBitmap; +} // namespace accounting +} // namespace gc + +namespace mirror { +class Class; +class Object; +template class ObjectArray; +} // namespace mirror + namespace jit { class JitInstrumentationCache; @@ -66,6 +75,7 @@ class JitCodeCache { size_t max_capacity, bool generate_debug_info, std::string* error_msg); + ~JitCodeCache(); // Number of bytes allocated in the code cache. size_t CodeCacheSize() REQUIRES(!lock_); @@ -210,11 +220,6 @@ class JitCodeCache { uint64_t GetLastUpdateTimeNs() const; - size_t GetCurrentCapacity() REQUIRES(!lock_) { - MutexLock lock(Thread::Current(), lock_); - return current_capacity_; - } - size_t GetMemorySizeOfCodePointer(const void* ptr) REQUIRES(!lock_); void InvalidateCompiledCodeFor(ArtMethod* method, const OatQuickMethodHeader* code) diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index ee35d9cd12..3071348435 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -144,6 +144,9 @@ const OatFile* OatFileManager::GetPrimaryOatFile() const { return nullptr; } +OatFileManager::OatFileManager() + : have_non_pic_oat_file_(false), only_use_system_oat_files_(false) {} + OatFileManager::~OatFileManager() { // Explicitly clear oat_files_ since the OatFile destructor calls back into OatFileManager for // UnRegisterOatFileLocation. diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h index 12057735eb..dd6b7ba2ff 100644 --- a/runtime/oat_file_manager.h +++ b/runtime/oat_file_manager.h @@ -45,7 +45,7 @@ class OatFile; // pointers returned from functions are always valid. class OatFileManager { public: - OatFileManager() : have_non_pic_oat_file_(false), only_use_system_oat_files_(false) {} + OatFileManager(); ~OatFileManager(); // Add an oat file to the internal accounting, std::aborts if there already exists an oat file diff --git a/test/566-polymorphic-inlining/polymorphic_inline.cc b/test/566-polymorphic-inlining/polymorphic_inline.cc index b75becffcb..e2b8aa037f 100644 --- a/test/566-polymorphic-inlining/polymorphic_inline.cc +++ b/test/566-polymorphic-inlining/polymorphic_inline.cc @@ -19,6 +19,7 @@ #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "jit/profiling_info.h" +#include "mirror/class.h" #include "oat_quick_method_header.h" #include "scoped_thread_state_change-inl.h" #include "stack_map.h" diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index 2b940ab7c7..df497c1181 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -26,6 +26,7 @@ #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 "runtime.h" #include "scoped_thread_state_change-inl.h" -- GitLab From c82745f28c3e4bef01030e4c7c8324aff0d2e5f7 Mon Sep 17 00:00:00 2001 From: Alan Leung Date: Mon, 13 Nov 2017 15:27:01 -0800 Subject: [PATCH 042/226] Make arm64-scratch-register less dexer dependent Make test 626-checker-arm64-scratch-register less dexer dependent Test: run-test .. --build-with-javac-dx && run-test .. --build-with-javac-dx --build-with-d8 Change-Id: If3d6103938c085a798655bee3c338103bed2efff --- .../smali/Smali.smali | 2119 +++++++++++++++++ .../src/Main.java | 61 +- 2 files changed, 2141 insertions(+), 39 deletions(-) create mode 100644 test/626-checker-arm64-scratch-register/smali/Smali.smali diff --git a/test/626-checker-arm64-scratch-register/smali/Smali.smali b/test/626-checker-arm64-scratch-register/smali/Smali.smali new file mode 100644 index 0000000000..e6943cf717 --- /dev/null +++ b/test/626-checker-arm64-scratch-register/smali/Smali.smali @@ -0,0 +1,2119 @@ +# 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. + +.class public LSmali; +.super Ljava/lang/Object; +.field b00:Z +.field b01:Z +.field b02:Z +.field b03:Z +.field b04:Z +.field b05:Z +.field b06:Z +.field b07:Z +.field b08:Z +.field b09:Z +.field b10:Z +.field b11:Z +.field b12:Z +.field b13:Z +.field b14:Z +.field b15:Z +.field b16:Z +.field b17:Z +.field b18:Z +.field b19:Z +.field b20:Z +.field b21:Z +.field b22:Z +.field b23:Z +.field b24:Z +.field b25:Z +.field b26:Z +.field b27:Z +.field b28:Z +.field b29:Z +.field b30:Z +.field b31:Z +.field b32:Z +.field b33:Z +.field b34:Z +.field b35:Z +.field b36:Z + +.field conditionA:Z +.field conditionB:Z +.field conditionC:Z + +.method public constructor ()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;->()V + return-void +.end method + +## CHECK-START-ARM64: void Smali.test() register (after) +## CHECK: begin_block +## CHECK: name "B0" +## CHECK: <> ParameterValue +## CHECK: end_block +## CHECK: begin_block +## CHECK: successors "<>" "<>" +## CHECK: <> InstanceFieldGet [<>] field_name:Smali.conditionB +## CHECK: If [<>] +## CHECK: end_block +## CHECK: begin_block +## CHECK: name "<>" +## CHECK: ParallelMove moves:[40(sp)->d0,24(sp)->32(sp),28(sp)->36(sp),d0->d3,d3->d4,d2->d5,d4->d6,d5->d7,d6->d18,d7->d19,d18->d20,d19->d21,d20->d22,d21->d23,d22->d10,d23->d11,16(sp)->24(sp),20(sp)->28(sp),d10->d14,d11->d12,d12->d13,d13->d1,d14->d2,32(sp)->16(sp),36(sp)->20(sp)] +## CHECK: end_block + +## CHECK-START-ARM64: void Smali.test() disassembly (after) +## CHECK: begin_block +## CHECK: name "B0" +## CHECK: <> ParameterValue +## CHECK: end_block +## CHECK: begin_block +## CHECK: successors "<>" "<>" +## CHECK: <> InstanceFieldGet [<>] field_name:Smali.conditionB +## CHECK: If [<>] +## CHECK: end_block +## CHECK: begin_block +## CHECK: name "<>" +## CHECK: ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid] +## CHECK: fmov d31, d2 +## CHECK: ldr s2, [sp, #36] +## CHECK: ldr w16, [sp, #16] +## CHECK: str w16, [sp, #36] +## CHECK: str s14, [sp, #16] +## CHECK: ldr s14, [sp, #28] +## CHECK: str s1, [sp, #28] +## CHECK: ldr s1, [sp, #32] +## CHECK: str s31, [sp, #32] +## CHECK: ldr s31, [sp, #20] +## CHECK: str s31, [sp, #40] +## CHECK: str s12, [sp, #20] +## CHECK: fmov d12, d11 +## CHECK: fmov d11, d10 +## CHECK: fmov d10, d23 +## CHECK: fmov d23, d22 +## CHECK: fmov d22, d21 +## CHECK: fmov d21, d20 +## CHECK: fmov d20, d19 +## CHECK: fmov d19, d18 +## CHECK: fmov d18, d7 +## CHECK: fmov d7, d6 +## CHECK: fmov d6, d5 +## CHECK: fmov d5, d4 +## CHECK: fmov d4, d3 +## CHECK: fmov d3, d13 +## CHECK: ldr s13, [sp, #24] +## CHECK: str s3, [sp, #24] +## CHECK: ldr s3, pc+{{\d+}} (addr {{0x[0-9a-f]+}}) (100) +## CHECK: end_block +.method public test()V + .registers 45 + + const-string v39, "" + + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b17:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_367 + + const/16 v19, 0x0 + + :goto_c + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b16:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_36b + + const/16 v18, 0x0 + + :goto_16 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b18:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_36f + + const/16 v20, 0x0 + + :goto_20 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b19:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_373 + + const/16 v21, 0x0 + + :goto_2a + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b20:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_377 + + const/16 v22, 0x0 + + :goto_34 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b21:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_37b + + const/16 v23, 0x0 + + :goto_3e + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b15:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_37f + + const/16 v17, 0x0 + + :goto_48 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b00:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_383 + + const/4 v2, 0x0 + + :goto_51 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b22:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_387 + + const/16 v24, 0x0 + + :goto_5b + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b23:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_38b + + const/16 v25, 0x0 + + :goto_65 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b24:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_38f + + const/16 v26, 0x0 + + :goto_6f + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b25:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_393 + + const/16 v27, 0x0 + + :goto_79 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b26:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_397 + + const/16 v28, 0x0 + + :goto_83 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b27:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_39b + + const/16 v29, 0x0 + + :goto_8d + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b29:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_39f + + const/16 v31, 0x0 + + :goto_97 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b28:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3a3 + + const/16 v30, 0x0 + + :goto_a1 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b01:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3a7 + + const/4 v3, 0x0 + + :goto_aa + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b02:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3ab + + const/4 v4, 0x0 + + :goto_b3 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b03:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3af + + const/4 v5, 0x0 + + :goto_bc + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b04:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3b3 + + const/4 v6, 0x0 + + :goto_c5 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b05:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3b7 + + const/4 v7, 0x0 + + :goto_ce + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b07:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3bb + + const/4 v9, 0x0 + + :goto_d7 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b06:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3bf + + const/4 v8, 0x0 + + :goto_e0 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b30:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3c3 + + const/16 v32, 0x0 + + :goto_ea + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b31:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3c7 + + const/16 v33, 0x0 + + :goto_f4 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b32:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3cb + + const/16 v34, 0x0 + + :goto_fe + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b33:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3cf + + const/16 v35, 0x0 + + :goto_108 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b34:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3d3 + + const/16 v36, 0x0 + + :goto_112 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b36:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3d7 + + const/16 v38, 0x0 + + :goto_11c + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b35:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3db + + const/16 v37, 0x0 + + :goto_126 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b08:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3df + + const/4 v10, 0x0 + + :goto_12f + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b09:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3e3 + + const/4 v11, 0x0 + + :goto_138 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b10:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3e7 + + const/4 v12, 0x0 + + :goto_141 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b11:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3eb + + const/4 v13, 0x0 + + :goto_14a + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b12:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3ef + + const/4 v14, 0x0 + + :goto_153 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b14:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3f3 + + const/16 v16, 0x0 + + :goto_15d + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b13:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3f7 + + const/4 v15, 0x0 + + :goto_166 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->conditionA:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_202 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v18, v18, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v19, v19, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v20, v20, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v21, v21, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v22, v22, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v23, v23, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v17, v17, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v10, v10, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v11, v11, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v12, v12, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v13, v13, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v14, v14, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v32, v32, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v33, v33, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v34, v34, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v35, v35, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v36, v36, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v3, v3, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v4, v4, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v5, v5, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v6, v6, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v7, v7, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v25, v25, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v26, v26, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v27, v27, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v28, v28, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v29, v29, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v24, v24, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v2, v2, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v16, v16, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v15, v15, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v38, v38, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v37, v37, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v9, v9, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v8, v8, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v31, v31, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v30, v30, v42 + + :cond_202 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->conditionB:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_29e + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v18, v18, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v19, v19, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v20, v20, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v21, v21, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v22, v22, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v23, v23, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v17, v17, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v10, v10, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v11, v11, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v12, v12, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v13, v13, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v14, v14, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v32, v32, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v33, v33, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v34, v34, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v35, v35, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v36, v36, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v3, v3, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v4, v4, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v5, v5, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v6, v6, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v7, v7, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v25, v25, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v26, v26, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v27, v27, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v28, v28, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v29, v29, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v24, v24, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v2, v2, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v16, v16, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v15, v15, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v38, v38, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v37, v37, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v9, v9, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v8, v8, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v31, v31, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v30, v30, v42 + + :cond_29e + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->conditionC:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_33a + + const/high16 v42, 0x41400000 # 12.0f + + div-float v18, v18, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v19, v19, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v20, v20, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v21, v21, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v22, v22, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v23, v23, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v17, v17, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v10, v10, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v11, v11, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v12, v12, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v13, v13, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v14, v14, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v32, v32, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v33, v33, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v34, v34, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v35, v35, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v36, v36, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v3, v3, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v4, v4, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v5, v5, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v6, v6, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v7, v7, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v25, v25, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v26, v26, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v27, v27, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v28, v28, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v29, v29, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v24, v24, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v2, v2, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v16, v16, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v15, v15, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v38, v38, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v37, v37, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v9, v9, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v8, v8, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v31, v31, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v30, v30, v42 + + :cond_33a + const/16 v41, 0x0 + + const/high16 v42, 0x42c80000 # 100.0f + + mul-float v42, v42, v41 + + invoke-static/range {v42 .. v42}, Ljava/lang/Math;->round(F)I + + move-result v42 + + move/from16 v0, v42 + + int-to-float v0, v0 + + move/from16 v42, v0 + + const/high16 v43, 0x42c80000 # 100.0f + + div-float v41, v42, v43 + + new-instance v42, Ljava/lang/StringBuilder; + + invoke-direct/range {v42 .. v42}, Ljava/lang/StringBuilder;->()V + + move-object/from16 v0, v42 + + move/from16 v1, v41 + + invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(F)Ljava/lang/StringBuilder; + + move-result-object v42 + + move-object/from16 v0, v42 + + move-object/from16 v1, v39 + + invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v42 + + invoke-virtual/range {v42 .. v42}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + + move-result-object v40 + + return-void + + :cond_367 + const/high16 v19, 0x3f800000 # 1.0f + + goto/16 :goto_c + + :cond_36b + const/high16 v18, 0x3f800000 # 1.0f + + goto/16 :goto_16 + + :cond_36f + const/high16 v20, 0x3f800000 # 1.0f + + goto/16 :goto_20 + + :cond_373 + const/high16 v21, 0x3f800000 # 1.0f + + goto/16 :goto_2a + + :cond_377 + const/high16 v22, 0x3f800000 # 1.0f + + goto/16 :goto_34 + + :cond_37b + const/high16 v23, 0x3f800000 # 1.0f + + goto/16 :goto_3e + + :cond_37f + const/high16 v17, 0x3f800000 # 1.0f + + goto/16 :goto_48 + + :cond_383 + const/high16 v2, 0x3f800000 # 1.0f + + goto/16 :goto_51 + + :cond_387 + const/high16 v24, 0x3f800000 # 1.0f + + goto/16 :goto_5b + + :cond_38b + const/high16 v25, 0x3f800000 # 1.0f + + goto/16 :goto_65 + + :cond_38f + const/high16 v26, 0x3f800000 # 1.0f + + goto/16 :goto_6f + + :cond_393 + const/high16 v27, 0x3f800000 # 1.0f + + goto/16 :goto_79 + + :cond_397 + const/high16 v28, 0x3f800000 # 1.0f + + goto/16 :goto_83 + + :cond_39b + const/high16 v29, 0x3f800000 # 1.0f + + goto/16 :goto_8d + + :cond_39f + const/high16 v31, 0x3f800000 # 1.0f + + goto/16 :goto_97 + + :cond_3a3 + const/high16 v30, 0x3f800000 # 1.0f + + goto/16 :goto_a1 + + :cond_3a7 + const/high16 v3, 0x3f800000 # 1.0f + + goto/16 :goto_aa + + :cond_3ab + const/high16 v4, 0x3f800000 # 1.0f + + goto/16 :goto_b3 + + :cond_3af + const/high16 v5, 0x3f800000 # 1.0f + + goto/16 :goto_bc + + :cond_3b3 + const/high16 v6, 0x3f800000 # 1.0f + + goto/16 :goto_c5 + + :cond_3b7 + const/high16 v7, 0x3f800000 # 1.0f + + goto/16 :goto_ce + + :cond_3bb + const/high16 v9, 0x3f800000 # 1.0f + + goto/16 :goto_d7 + + :cond_3bf + const/high16 v8, 0x3f800000 # 1.0f + + goto/16 :goto_e0 + + :cond_3c3 + const/high16 v32, 0x3f800000 # 1.0f + + goto/16 :goto_ea + + :cond_3c7 + const/high16 v33, 0x3f800000 # 1.0f + + goto/16 :goto_f4 + + :cond_3cb + const/high16 v34, 0x3f800000 # 1.0f + + goto/16 :goto_fe + + :cond_3cf + const/high16 v35, 0x3f800000 # 1.0f + + goto/16 :goto_108 + + :cond_3d3 + const/high16 v36, 0x3f800000 # 1.0f + + goto/16 :goto_112 + + :cond_3d7 + const/high16 v38, 0x3f800000 # 1.0f + + goto/16 :goto_11c + + :cond_3db + const/high16 v37, 0x3f800000 # 1.0f + + goto/16 :goto_126 + + :cond_3df + const/high16 v10, 0x3f800000 # 1.0f + + goto/16 :goto_12f + + :cond_3e3 + const/high16 v11, 0x3f800000 # 1.0f + + goto/16 :goto_138 + + :cond_3e7 + const/high16 v12, 0x3f800000 # 1.0f + + goto/16 :goto_141 + + :cond_3eb + const/high16 v13, 0x3f800000 # 1.0f + + goto/16 :goto_14a + + :cond_3ef + const/high16 v14, 0x3f800000 # 1.0f + + goto/16 :goto_153 + + :cond_3f3 + const/high16 v16, 0x3f800000 # 1.0f + + goto/16 :goto_15d + + :cond_3f7 + const/high16 v15, 0x3f800000 # 1.0f + + goto/16 :goto_166 +.end method + +## CHECK-START-ARM64: void Smali.testD8() register (after) +## CHECK: begin_block +## CHECK: name "B0" +## CHECK: <> ParameterValue +## CHECK: end_block + +## CHECK: begin_block +## CHECK: successors "<>" "<>" +## CHECK: <> InstanceFieldGet [<>] field_name:Smali.conditionA +## CHECK: If [<>] +## CHECK: end_block + +## CHECK: begin_block +## CHECK: name "<>" +## CHECK: end_block +## CHECK: begin_block +## CHECK: name "<>" +## CHECK: ParallelMove moves:[d2->d0,40(sp)->d17,d20->d26,d19->d27,d27->d1,d26->d2,d14->d20,d13->d19,d17->d14,d0->d13] +## CHECK: end_block + +## CHECK: begin_block +## CHECK: successors "<>" "<>" +## CHECK: <> InstanceFieldGet [<>] field_name:Smali.conditionB +## CHECK: If [<>] +## CHECK: end_block + +## CHECK: begin_block +## CHECK: name "<>" +## CHECK: end_block +## CHECK: begin_block +## CHECK: name "<>" +## CHECK: ParallelMove moves:[#100->d13,16(sp)->d1,20(sp)->d2,28(sp)->d19,24(sp)->d20,36(sp)->d14,32(sp)->16(sp),d1->20(sp),d2->24(sp),d20->28(sp),d19->32(sp),d14->36(sp),d13->40(sp)] +## CHECK: end_block + +## CHECK-START-ARM64: void Smali.testD8() disassembly (after) +## CHECK: begin_block +## CHECK: name "B0" +## CHECK: <> ParameterValue +## CHECK: end_block + +## CHECK: begin_block +## CHECK: successors "<>" "<>" +## CHECK: <> InstanceFieldGet [<>] field_name:Smali.conditionA +## CHECK: If [<>] +## CHECK: end_block + +## CHECK: begin_block +## CHECK: name "<>" +## CHECK: end_block +## CHECK: begin_block +## CHECK: name "<>" +## CHECK: end_block + +## CHECK: begin_block +## CHECK: successors "<>" "<>" +## CHECK: <> InstanceFieldGet [<>] field_name:Smali.conditionB +## CHECK: If [<>] +## CHECK: end_block + +## CHECK: begin_block +## CHECK: name "<>" +## CHECK: end_block +## CHECK: begin_block +## CHECK: name "<>" +## CHECK: ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid] +## CHECK: ldr w16, [sp, #32] +## CHECK: str s19, [sp, #32] +## CHECK: ldr s19, [sp, #28] +## CHECK: str s20, [sp, #28] +## CHECK: ldr s20, [sp, #24] +## CHECK: str s2, [sp, #24] +## CHECK: ldr s2, [sp, #20] +## CHECK: str s1, [sp, #20] +## CHECK: ldr s1, [sp, #16] +## CHECK: str w16, [sp, #16] +## CHECK: fmov d31, d14 +## CHECK: ldr s14, [sp, #36] +## CHECK: str s31, [sp, #36] +## CHECK: str s13, [sp, #40] +## CHECK: ldr s13, pc+580 (addr 0x61c) (100) +## CHECK: end_block +.method public testD8()V + .registers 47 + + move-object/from16 v0, p0 + + const-string v1, "" + + iget-boolean v2, v0, LSmali;->b17:Z + + if-eqz v2, :cond_a + + const/4 v2, 0x0 + + goto :goto_c + + :cond_a + const/high16 v2, 0x3f800000 # 1.0f + + :goto_c + iget-boolean v5, v0, LSmali;->b16:Z + + if-eqz v5, :cond_12 + + const/4 v5, 0x0 + + goto :goto_14 + + :cond_12 + const/high16 v5, 0x3f800000 # 1.0f + + :goto_14 + iget-boolean v6, v0, LSmali;->b18:Z + + if-eqz v6, :cond_1a + + const/4 v6, 0x0 + + goto :goto_1c + + :cond_1a + const/high16 v6, 0x3f800000 # 1.0f + + :goto_1c + iget-boolean v7, v0, LSmali;->b19:Z + + if-eqz v7, :cond_22 + + const/4 v7, 0x0 + + goto :goto_24 + + :cond_22 + const/high16 v7, 0x3f800000 # 1.0f + + :goto_24 + iget-boolean v8, v0, LSmali;->b20:Z + + if-eqz v8, :cond_2a + + const/4 v8, 0x0 + + goto :goto_2c + + :cond_2a + const/high16 v8, 0x3f800000 # 1.0f + + :goto_2c + iget-boolean v9, v0, LSmali;->b21:Z + + if-eqz v9, :cond_32 + + const/4 v9, 0x0 + + goto :goto_34 + + :cond_32 + const/high16 v9, 0x3f800000 # 1.0f + + :goto_34 + iget-boolean v10, v0, LSmali;->b15:Z + + if-eqz v10, :cond_3a + + const/4 v10, 0x0 + + goto :goto_3c + + :cond_3a + const/high16 v10, 0x3f800000 # 1.0f + + :goto_3c + iget-boolean v11, v0, LSmali;->b00:Z + + if-eqz v11, :cond_42 + + const/4 v11, 0x0 + + goto :goto_44 + + :cond_42 + const/high16 v11, 0x3f800000 # 1.0f + + :goto_44 + iget-boolean v12, v0, LSmali;->b22:Z + + if-eqz v12, :cond_4a + + const/4 v12, 0x0 + + goto :goto_4c + + :cond_4a + const/high16 v12, 0x3f800000 # 1.0f + + :goto_4c + iget-boolean v13, v0, LSmali;->b23:Z + + if-eqz v13, :cond_52 + + const/4 v13, 0x0 + + goto :goto_54 + + :cond_52 + const/high16 v13, 0x3f800000 # 1.0f + + :goto_54 + iget-boolean v14, v0, LSmali;->b24:Z + + if-eqz v14, :cond_5a + + const/4 v14, 0x0 + + goto :goto_5c + + :cond_5a + const/high16 v14, 0x3f800000 # 1.0f + + :goto_5c + iget-boolean v15, v0, LSmali;->b25:Z + + if-eqz v15, :cond_62 + + const/4 v15, 0x0 + + goto :goto_64 + + :cond_62 + const/high16 v15, 0x3f800000 # 1.0f + + :goto_64 + iget-boolean v3, v0, LSmali;->b26:Z + + if-eqz v3, :cond_6a + + const/4 v3, 0x0 + + goto :goto_6c + + :cond_6a + const/high16 v3, 0x3f800000 # 1.0f + + :goto_6c + iget-boolean v4, v0, LSmali;->b27:Z + + if-eqz v4, :cond_72 + + const/4 v4, 0x0 + + goto :goto_74 + + :cond_72 + const/high16 v4, 0x3f800000 # 1.0f + + :goto_74 + move-object/from16 v18, v1 + + iget-boolean v1, v0, LSmali;->b29:Z + + if-eqz v1, :cond_7c + + const/4 v1, 0x0 + + goto :goto_7e + + :cond_7c + const/high16 v1, 0x3f800000 # 1.0f + + :goto_7e + move/from16 v19, v1 + + iget-boolean v1, v0, LSmali;->b28:Z + + if-eqz v1, :cond_86 + + const/4 v1, 0x0 + + goto :goto_88 + + :cond_86 + const/high16 v1, 0x3f800000 # 1.0f + + :goto_88 + move/from16 v20, v1 + + iget-boolean v1, v0, LSmali;->b01:Z + + if-eqz v1, :cond_90 + + const/4 v1, 0x0 + + goto :goto_92 + + :cond_90 + const/high16 v1, 0x3f800000 # 1.0f + + :goto_92 + move/from16 v21, v11 + + iget-boolean v11, v0, LSmali;->b02:Z + + if-eqz v11, :cond_9a + + const/4 v11, 0x0 + + goto :goto_9c + + :cond_9a + const/high16 v11, 0x3f800000 # 1.0f + + :goto_9c + move/from16 v22, v12 + + iget-boolean v12, v0, LSmali;->b03:Z + + if-eqz v12, :cond_a4 + + const/4 v12, 0x0 + + goto :goto_a6 + + :cond_a4 + const/high16 v12, 0x3f800000 # 1.0f + + :goto_a6 + move/from16 v23, v4 + + iget-boolean v4, v0, LSmali;->b04:Z + + if-eqz v4, :cond_ae + + const/4 v4, 0x0 + + goto :goto_b0 + + :cond_ae + const/high16 v4, 0x3f800000 # 1.0f + + :goto_b0 + move/from16 v24, v3 + + iget-boolean v3, v0, LSmali;->b05:Z + + if-eqz v3, :cond_b8 + + const/4 v3, 0x0 + + goto :goto_ba + + :cond_b8 + const/high16 v3, 0x3f800000 # 1.0f + + :goto_ba + move/from16 v25, v15 + + iget-boolean v15, v0, LSmali;->b07:Z + + if-eqz v15, :cond_c2 + + const/4 v15, 0x0 + + goto :goto_c4 + + :cond_c2 + const/high16 v15, 0x3f800000 # 1.0f + + :goto_c4 + move/from16 v26, v15 + + iget-boolean v15, v0, LSmali;->b06:Z + + if-eqz v15, :cond_cc + + const/4 v15, 0x0 + + goto :goto_ce + + :cond_cc + const/high16 v15, 0x3f800000 # 1.0f + + :goto_ce + move/from16 v27, v15 + + iget-boolean v15, v0, LSmali;->b30:Z + + if-eqz v15, :cond_d6 + + const/4 v15, 0x0 + + goto :goto_d8 + + :cond_d6 + const/high16 v15, 0x3f800000 # 1.0f + + :goto_d8 + move/from16 v28, v14 + + iget-boolean v14, v0, LSmali;->b31:Z + + if-eqz v14, :cond_e0 + + const/4 v14, 0x0 + + goto :goto_e2 + + :cond_e0 + const/high16 v14, 0x3f800000 # 1.0f + + :goto_e2 + move/from16 v29, v13 + + iget-boolean v13, v0, LSmali;->b32:Z + + if-eqz v13, :cond_ea + + const/4 v13, 0x0 + + goto :goto_ec + + :cond_ea + const/high16 v13, 0x3f800000 # 1.0f + + :goto_ec + move/from16 v30, v3 + + iget-boolean v3, v0, LSmali;->b33:Z + + if-eqz v3, :cond_f4 + + const/4 v3, 0x0 + + goto :goto_f6 + + :cond_f4 + const/high16 v3, 0x3f800000 # 1.0f + + :goto_f6 + move/from16 v31, v4 + + iget-boolean v4, v0, LSmali;->b34:Z + + if-eqz v4, :cond_fe + + const/4 v4, 0x0 + + goto :goto_100 + + :cond_fe + const/high16 v4, 0x3f800000 # 1.0f + + :goto_100 + move/from16 v32, v12 + + iget-boolean v12, v0, LSmali;->b36:Z + + if-eqz v12, :cond_108 + + const/4 v12, 0x0 + + goto :goto_10a + + :cond_108 + const/high16 v12, 0x3f800000 # 1.0f + + :goto_10a + move/from16 v33, v12 + + iget-boolean v12, v0, LSmali;->b35:Z + + if-eqz v12, :cond_112 + + const/4 v12, 0x0 + + goto :goto_114 + + :cond_112 + const/high16 v12, 0x3f800000 # 1.0f + + :goto_114 + move/from16 v34, v12 + + iget-boolean v12, v0, LSmali;->b08:Z + + if-eqz v12, :cond_11c + + const/4 v12, 0x0 + + goto :goto_11e + + :cond_11c + const/high16 v12, 0x3f800000 # 1.0f + + :goto_11e + move/from16 v35, v11 + + iget-boolean v11, v0, LSmali;->b09:Z + + if-eqz v11, :cond_126 + + const/4 v11, 0x0 + + goto :goto_128 + + :cond_126 + const/high16 v11, 0x3f800000 # 1.0f + + :goto_128 + move/from16 v36, v1 + + iget-boolean v1, v0, LSmali;->b10:Z + + if-eqz v1, :cond_130 + + const/4 v1, 0x0 + + goto :goto_132 + + :cond_130 + const/high16 v1, 0x3f800000 # 1.0f + + :goto_132 + move/from16 v37, v4 + + iget-boolean v4, v0, LSmali;->b11:Z + + if-eqz v4, :cond_13a + + const/4 v4, 0x0 + + goto :goto_13c + + :cond_13a + const/high16 v4, 0x3f800000 # 1.0f + + :goto_13c + move/from16 v38, v3 + + iget-boolean v3, v0, LSmali;->b12:Z + + if-eqz v3, :cond_144 + + const/4 v3, 0x0 + + goto :goto_146 + + :cond_144 + const/high16 v3, 0x3f800000 # 1.0f + + :goto_146 + move/from16 v39, v13 + + iget-boolean v13, v0, LSmali;->b14:Z + + if-eqz v13, :cond_14e + + const/4 v13, 0x0 + + goto :goto_150 + + :cond_14e + const/high16 v13, 0x3f800000 # 1.0f + + :goto_150 + move/from16 v40, v13 + + iget-boolean v13, v0, LSmali;->b13:Z + + if-eqz v13, :cond_159 + + const/16 v16, 0x0 + + goto :goto_15b + + :cond_159 + const/high16 v16, 0x3f800000 # 1.0f + + :goto_15b + move/from16 v13, v16 + + move/from16 v41, v13 + + iget-boolean v13, v0, LSmali;->conditionA:Z + + if-eqz v13, :cond_1a2 + + const/high16 v13, 0x447a0000 # 1000.0f + + div-float/2addr v5, v13 + + div-float/2addr v2, v13 + + div-float/2addr v6, v13 + + div-float/2addr v7, v13 + + div-float/2addr v8, v13 + + div-float/2addr v9, v13 + + div-float/2addr v10, v13 + + div-float/2addr v12, v13 + + div-float/2addr v11, v13 + + div-float/2addr v1, v13 + + div-float/2addr v4, v13 + + div-float/2addr v3, v13 + + div-float/2addr v15, v13 + + div-float/2addr v14, v13 + + div-float v16, v39, v13 + + div-float v38, v38, v13 + + div-float v37, v37, v13 + + div-float v36, v36, v13 + + div-float v35, v35, v13 + + div-float v32, v32, v13 + + div-float v31, v31, v13 + + div-float v30, v30, v13 + + div-float v29, v29, v13 + + div-float v28, v28, v13 + + div-float v25, v25, v13 + + div-float v24, v24, v13 + + div-float v23, v23, v13 + + div-float v22, v22, v13 + + div-float v21, v21, v13 + + div-float v39, v40, v13 + + div-float v40, v41, v13 + + div-float v33, v33, v13 + + div-float v34, v34, v13 + + div-float v26, v26, v13 + + div-float v27, v27, v13 + + div-float v19, v19, v13 + + div-float v13, v20, v13 + + goto :goto_1aa + + :cond_1a2 + move/from16 v13, v20 + + move/from16 v16, v39 + + move/from16 v39, v40 + + move/from16 v40, v41 + + :goto_1aa + move/from16 v42, v13 + + iget-boolean v13, v0, LSmali;->conditionB:Z + + const/high16 v20, 0x42c80000 # 100.0f + + if-eqz v13, :cond_1fd + + div-float v5, v5, v20 + + div-float v2, v2, v20 + + div-float v6, v6, v20 + + div-float v7, v7, v20 + + div-float v8, v8, v20 + + div-float v9, v9, v20 + + div-float v10, v10, v20 + + div-float v12, v12, v20 + + div-float v11, v11, v20 + + div-float v1, v1, v20 + + div-float v4, v4, v20 + + div-float v3, v3, v20 + + div-float v15, v15, v20 + + div-float v14, v14, v20 + + div-float v16, v16, v20 + + div-float v38, v38, v20 + + div-float v37, v37, v20 + + div-float v36, v36, v20 + + div-float v35, v35, v20 + + div-float v32, v32, v20 + + div-float v31, v31, v20 + + div-float v30, v30, v20 + + div-float v29, v29, v20 + + div-float v28, v28, v20 + + div-float v25, v25, v20 + + div-float v24, v24, v20 + + div-float v23, v23, v20 + + div-float v22, v22, v20 + + div-float v21, v21, v20 + + div-float v39, v39, v20 + + div-float v40, v40, v20 + + div-float v33, v33, v20 + + div-float v34, v34, v20 + + div-float v26, v26, v20 + + div-float v27, v27, v20 + + div-float v19, v19, v20 + + div-float v13, v42, v20 + + goto :goto_1ff + + :cond_1fd + move/from16 v13, v42 + + :goto_1ff + move/from16 v43, v13 + + iget-boolean v13, v0, LSmali;->conditionC:Z + + if-eqz v13, :cond_244 + + const/high16 v13, 0x41400000 # 12.0f + + div-float/2addr v5, v13 + + div-float/2addr v2, v13 + + div-float/2addr v6, v13 + + div-float/2addr v7, v13 + + div-float/2addr v8, v13 + + div-float/2addr v9, v13 + + div-float/2addr v10, v13 + + div-float/2addr v12, v13 + + div-float/2addr v11, v13 + + div-float/2addr v1, v13 + + div-float/2addr v4, v13 + + div-float/2addr v3, v13 + + div-float/2addr v15, v13 + + div-float/2addr v14, v13 + + div-float v16, v16, v13 + + div-float v38, v38, v13 + + div-float v37, v37, v13 + + div-float v36, v36, v13 + + div-float v35, v35, v13 + + div-float v32, v32, v13 + + div-float v31, v31, v13 + + div-float v30, v30, v13 + + div-float v29, v29, v13 + + div-float v28, v28, v13 + + div-float v25, v25, v13 + + div-float v24, v24, v13 + + div-float v23, v23, v13 + + div-float v22, v22, v13 + + div-float v21, v21, v13 + + div-float v39, v39, v13 + + div-float v40, v40, v13 + + div-float v33, v33, v13 + + div-float v34, v34, v13 + + div-float v26, v26, v13 + + div-float v27, v27, v13 + + div-float v19, v19, v13 + + div-float v13, v43, v13 + + goto :goto_246 + + :cond_244 + move/from16 v13, v43 + + :goto_246 + const/16 v17, 0x0 + + mul-float v0, v20, v17 + + invoke-static {v0}, Ljava/lang/Math;->round(F)I + + move-result v0 + + int-to-float v0, v0 + + div-float v0, v0, v20 + + move/from16 v44, v1 + + new-instance v1, Ljava/lang/StringBuilder; + + invoke-direct {v1}, Ljava/lang/StringBuilder;->()V + + invoke-virtual {v1, v0}, Ljava/lang/StringBuilder;->append(F)Ljava/lang/StringBuilder; + + move/from16 v45, v0 + + move-object/from16 v0, v18 + + invoke-virtual {v1, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + + move-result-object v1 + + return-void +.end method diff --git a/test/626-checker-arm64-scratch-register/src/Main.java b/test/626-checker-arm64-scratch-register/src/Main.java index 139491769e..cf94b87666 100644 --- a/test/626-checker-arm64-scratch-register/src/Main.java +++ b/test/626-checker-arm64-scratch-register/src/Main.java @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; public class Main { @@ -63,14 +65,17 @@ public class Main { /// CHECK: name "B0" /// CHECK: <> ParameterValue /// CHECK: end_block + /// CHECK: begin_block - /// CHECK: successors "<>" "<>" + /// CHECK: successors "<>" "<>" + /// CHECK: <> InstanceFieldGet [<>] field_name:Main.conditionA + /// CHECK: If [<>] + /// CHECK: end_block + + /// CHECK: begin_block + /// CHECK: successors "<>" "<>" /// CHECK: <> InstanceFieldGet [<>] field_name:Main.conditionB /// CHECK: If [<>] - /// CHECK: end_block - /// CHECK: begin_block - /// CHECK: name "<>" - /// CHECK: ParallelMove moves:[40(sp)->d0,24(sp)->32(sp),28(sp)->36(sp),d0->d3,d3->d4,d2->d5,d4->d6,d5->d7,d6->d18,d7->d19,d18->d20,d19->d21,d20->d22,d21->d23,d22->d10,d23->d11,16(sp)->24(sp),20(sp)->28(sp),d10->d14,d11->d12,d12->d13,d13->d1,d14->d2,32(sp)->16(sp),36(sp)->20(sp)] /// CHECK: end_block /// CHECK-START-ARM64: void Main.test() disassembly (after) @@ -82,39 +87,6 @@ public class Main { /// CHECK: successors "<>" "<>" /// CHECK: <> InstanceFieldGet [<>] field_name:Main.conditionB /// CHECK: If [<>] - /// CHECK: end_block - /// CHECK: begin_block - /// CHECK: name "<>" - /// CHECK: ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid] - /// CHECK: fmov d31, d2 - /// CHECK: ldr s2, [sp, #36] - /// CHECK: ldr w16, [sp, #16] - /// CHECK: str w16, [sp, #36] - /// CHECK: str s14, [sp, #16] - /// CHECK: ldr s14, [sp, #28] - /// CHECK: str s1, [sp, #28] - /// CHECK: ldr s1, [sp, #32] - /// CHECK: str s31, [sp, #32] - /// CHECK: ldr s31, [sp, #20] - /// CHECK: str s31, [sp, #40] - /// CHECK: str s12, [sp, #20] - /// CHECK: fmov d12, d11 - /// CHECK: fmov d11, d10 - /// CHECK: fmov d10, d23 - /// CHECK: fmov d23, d22 - /// CHECK: fmov d22, d21 - /// CHECK: fmov d21, d20 - /// CHECK: fmov d20, d19 - /// CHECK: fmov d19, d18 - /// CHECK: fmov d18, d7 - /// CHECK: fmov d7, d6 - /// CHECK: fmov d6, d5 - /// CHECK: fmov d5, d4 - /// CHECK: fmov d4, d3 - /// CHECK: fmov d3, d13 - /// CHECK: ldr s13, [sp, #24] - /// CHECK: str s3, [sp, #24] - /// CHECK: ldr s3, pc+{{\d+}} (addr {{0x[0-9a-f]+}}) (100) /// CHECK: end_block public void test() { @@ -289,9 +261,20 @@ public class Main { String res = s + r; } - public static void main(String[] args) { + public static void main(String[] args) throws Exception { Main main = new Main(); main.test(); + + Class cl = Class.forName("Smali"); + Constructor cons = cl.getConstructor(); + Object o = cons.newInstance(); + + Method test = cl.getMethod("test"); + test.invoke(o); + + Method testD8 = cl.getMethod("testD8"); + testD8.invoke(o); + System.out.println("passed"); } } -- GitLab From 2a2d3117e3912743527455ec2b7056eb66215ae1 Mon Sep 17 00:00:00 2001 From: Igor Murashkin Date: Tue, 14 Nov 2017 15:03:25 -0800 Subject: [PATCH 043/226] cpplint: Use upstream cpplint Also use CPPLINT.cfg instead of passing in extra flags to cpplint.py. This unifies the handling and allows us to remove cpplint_presubmit without loss of functionality. Bug: 68951293 Change-Id: I6ece835440e3ac3f84fadc544307a9a5126a3e1c --- CPPLINT.cfg | 32 + PREUPLOAD.cfg | 8 +- build/Android.cpplint.mk | 38 +- openjdkjvmti/include/CPPLINT.cfg | 18 + runtime/CPPLINT.cfg | 18 + tools/cpplint.py | 4095 ------------------------------ tools/cpplint_presubmit.py | 67 - 7 files changed, 96 insertions(+), 4180 deletions(-) create mode 100644 CPPLINT.cfg create mode 100644 openjdkjvmti/include/CPPLINT.cfg create mode 100644 runtime/CPPLINT.cfg delete mode 100755 tools/cpplint.py delete mode 100755 tools/cpplint_presubmit.py diff --git a/CPPLINT.cfg b/CPPLINT.cfg new file mode 100644 index 0000000000..bf0ad58320 --- /dev/null +++ b/CPPLINT.cfg @@ -0,0 +1,32 @@ +# +# 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. +# + +# Don't search for additional CPPLINT.cfg in parent directories. +set noparent + +# Use 'ART_' as the cpp header guard prefix (e.g. #ifndef ART_PATH_TO_FILE_H_). +root=.. + +# Limit line length. +linelength=100 + +# Ignore the following categories of errors, as specified by the filter: +# (the filter settings are concatenated together) +filter=-build/include +filter=-readability/function,-readability/streams,-readability/todo +filter=-runtime/printf,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn +# TODO: this should be re-enabled. +filter=-whitespace/line_length diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 8a8df36101..7e492c7453 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,4 +1,10 @@ [Hook Scripts] check_generated_files_up_to_date = tools/cpp-define-generator/presubmit-check-files-up-to-date check_generated_tests_up_to_date = tools/test_presubmit.py -check_cpplint_on_changed_files = tools/cpplint_presubmit.py + +[Builtin Hooks] +cpplint = true + +[Builtin Hooks Options] +# Cpplint prints nothing unless there were errors. +cpplint = --quiet ${PREUPLOAD_FILES} diff --git a/build/Android.cpplint.mk b/build/Android.cpplint.mk index 247f4e3470..964a4c896f 100644 --- a/build/Android.cpplint.mk +++ b/build/Android.cpplint.mk @@ -16,42 +16,46 @@ include art/build/Android.common_build.mk -ART_CPPLINT := $(LOCAL_PATH)/tools/cpplint.py -ART_CPPLINT_FILTER := --filter=-whitespace/line_length,-build/include,-readability/function,-readability/streams,-readability/todo,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn,-runtime/printf -# Use `pwd` instead of $TOP for root, $TOP is always . and --root doesn't seem -# to work with a relative path (b/34787652). -ART_CPPLINT_FLAGS := --root=`pwd` +# Use upstream cpplint (toolpath from .repo/manifests/GLOBAL-PREUPLOAD.cfg). +ART_CPPLINT := external/google-styleguide/cpplint/cpplint.py + +# This file previously configured many cpplint settings. +# Everything that could be moved to CPPLINT.cfg has moved there. +# Please add new settings to CPPLINT.cfg over adding new flags in this file. + +ART_CPPLINT_FLAGS := +# No output when there are no errors. ART_CPPLINT_QUIET := --quiet -ART_CPPLINT_INGORED := \ - runtime/elf.h \ - openjdkjvmti/include/jvmti.h -# This: -# 1) Gets a list of all .h & .cc files in the art directory. +# 1) Get list of all .h & .cc files in the art directory. +# 2) Prepends 'art/' to each of them to make the full name. +ART_CPPLINT_SRC := $(addprefix $(LOCAL_PATH)/, $(call all-subdir-named-files,*.h) $(call all-subdir-named-files,*$(ART_CPP_EXTENSION))) + +# 1) Get list of all CPPLINT.cfg files in the art directory. # 2) Prepends 'art/' to each of them to make the full name. -# 3) removes art/runtime/elf.h from the list. -ART_CPPLINT_SRC := $(filter-out $(patsubst %,$(LOCAL_PATH)/%,$(ART_CPPLINT_INGORED)), $(addprefix $(LOCAL_PATH)/, $(call all-subdir-named-files,*.h) $(call all-subdir-named-files,*$(ART_CPP_EXTENSION)))) +ART_CPPLINT_CFG := $(addprefix $(LOCAL_PATH)/, $(call all-subdir-named-files,CPPLINT.cfg)) # "mm cpplint-art" to verify we aren't regressing +# - files not touched since the last build are skipped (quite fast). .PHONY: cpplint-art -cpplint-art: - $(ART_CPPLINT) $(ART_CPPLINT_FLAGS) $(ART_CPPLINT_FILTER) $(ART_CPPLINT_SRC) +cpplint-art: cpplint-art-phony -# "mm cpplint-art-all" to see all warnings +# "mm cpplint-art-all" to manually execute cpplint.py on all files (very slow). .PHONY: cpplint-art-all cpplint-art-all: $(ART_CPPLINT) $(ART_CPPLINT_FLAGS) $(ART_CPPLINT_SRC) OUT_CPPLINT := $(TARGET_COMMON_OUT_ROOT)/cpplint +# Build up the list of all targets for linting the ART source files. ART_CPPLINT_TARGETS := define declare-art-cpplint-target art_cpplint_file := $(1) art_cpplint_touch := $$(OUT_CPPLINT)/$$(subst /,__,$$(art_cpplint_file)) -$$(art_cpplint_touch): $$(art_cpplint_file) $(ART_CPPLINT) art/build/Android.cpplint.mk - $(hide) $(ART_CPPLINT) $(ART_CPPLINT_QUIET) $(ART_CPPLINT_FLAGS) $(ART_CPPLINT_FILTER) $$< +$$(art_cpplint_touch): $$(art_cpplint_file) $(ART_CPPLINT) $(ART_CPPLINT_CFG) art/build/Android.cpplint.mk + $(hide) $(ART_CPPLINT) $(ART_CPPLINT_QUIET) $(ART_CPPLINT_FLAGS) $$< $(hide) mkdir -p $$(dir $$@) $(hide) touch $$@ diff --git a/openjdkjvmti/include/CPPLINT.cfg b/openjdkjvmti/include/CPPLINT.cfg new file mode 100644 index 0000000000..3e717a697f --- /dev/null +++ b/openjdkjvmti/include/CPPLINT.cfg @@ -0,0 +1,18 @@ +# +# 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. +# + +# External headers, not subject to our lint rules. +exclude_files=^jvmti[.]h$ diff --git a/runtime/CPPLINT.cfg b/runtime/CPPLINT.cfg new file mode 100644 index 0000000000..6f274994a9 --- /dev/null +++ b/runtime/CPPLINT.cfg @@ -0,0 +1,18 @@ +# +# 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. +# + +# External headers, not subject to our lint rules. +exclude_files=^elf[.]h$ diff --git a/tools/cpplint.py b/tools/cpplint.py deleted file mode 100755 index 308dd8c479..0000000000 --- a/tools/cpplint.py +++ /dev/null @@ -1,4095 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2009 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -# Here are some issues that I've had people identify in my code during reviews, -# that I think are possible to flag automatically in a lint tool. If these were -# caught by lint, it would save time both for myself and that of my reviewers. -# Most likely, some of these are beyond the scope of the current lint framework, -# but I think it is valuable to retain these wish-list items even if they cannot -# be immediately implemented. -# -# Suggestions -# ----------- -# - Check for no 'explicit' for multi-arg ctor -# - Check for boolean assign RHS in parens -# - Check for ctor initializer-list colon position and spacing -# - Check that if there's a ctor, there should be a dtor -# - Check accessors that return non-pointer member variables are -# declared const -# - Check accessors that return non-const pointer member vars are -# *not* declared const -# - Check for using public includes for testing -# - Check for spaces between brackets in one-line inline method -# - Check for no assert() -# - Check for spaces surrounding operators -# - Check for 0 in pointer context (should be NULL) -# - Check for 0 in char context (should be '\0') -# - Check for camel-case method name conventions for methods -# that are not simple inline getters and setters -# - Do not indent namespace contents -# - Avoid inlining non-trivial constructors in header files -# - Check for old-school (void) cast for call-sites of functions -# ignored return value -# - Check gUnit usage of anonymous namespace -# - Check for class declaration order (typedefs, consts, enums, -# ctor(s?), dtor, friend declarations, methods, member vars) -# - -"""Does google-lint on c++ files. - -The goal of this script is to identify places in the code that *may* -be in non-compliance with google style. It does not attempt to fix -up these problems -- the point is to educate. It does also not -attempt to find all problems, or to ensure that everything it does -find is legitimately a problem. - -In particular, we can get very confused by /* and // inside strings! -We do a small hack, which is to ignore //'s with "'s after them on the -same line, but it is far from perfect (in either direction). -""" - -import codecs -import copy -import getopt -import math # for log -import os -import re -import sre_compile -import string -import sys -import unicodedata - - -_USAGE = """ -Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] - [--counting=total|toplevel|detailed] - [--quiet] - [file] ... - - The style guidelines this tries to follow are those in - http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml - - Every problem is given a confidence score from 1-5, with 5 meaning we are - certain of the problem, and 1 meaning it could be a legitimate construct. - This will miss some errors, and is not a substitute for a code review. - - To suppress false-positive errors of a certain category, add a - 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) - suppresses errors of all categories on that line. - - The files passed in will be linted; at least one file must be provided. - Linted extensions are .cc, .cpp, and .h. Other file types will be ignored. - - Flags: - - output=vs7 - By default, the output is formatted to ease emacs parsing. Visual Studio - compatible output (vs7) may also be used. Other formats are unsupported. - - verbose=# - Specify a number 0-5 to restrict errors to certain verbosity levels. - - quiet - Don't print anything if no errors are found. - - filter=-x,+y,... - Specify a comma-separated list of category-filters to apply: only - error messages whose category names pass the filters will be printed. - (Category names are printed with the message and look like - "[whitespace/indent]".) Filters are evaluated left to right. - "-FOO" and "FOO" means "do not print categories that start with FOO". - "+FOO" means "do print categories that start with FOO". - - Examples: --filter=-whitespace,+whitespace/braces - --filter=whitespace,runtime/printf,+runtime/printf_format - --filter=-,+build/include_what_you_use - - To see a list of all the categories used in cpplint, pass no arg: - --filter= - - counting=total|toplevel|detailed - The total number of errors found is always printed. If - 'toplevel' is provided, then the count of errors in each of - the top-level categories like 'build' and 'whitespace' will - also be printed. If 'detailed' is provided, then a count - is provided for each category like 'build/class'. - - root=subdir - The root directory used for deriving header guard CPP variable. - By default, the header guard CPP variable is calculated as the relative - path to the directory that contains .git, .hg, or .svn. When this flag - is specified, the relative path is calculated from the specified - directory. If the specified directory does not exist, this flag is - ignored. - - Examples: - Assuing that src/.git exists, the header guard CPP variables for - src/chrome/browser/ui/browser.h are: - - No flag => CHROME_BROWSER_UI_BROWSER_H_ - --root=chrome => BROWSER_UI_BROWSER_H_ - --root=chrome/browser => UI_BROWSER_H_ -""" - -# We categorize each error message we print. Here are the categories. -# We want an explicit list so we can list them all in cpplint --filter=. -# If you add a new error message with a new category, add it to the list -# here! cpplint_unittest.py should tell you if you forget to do this. -# \ used for clearer layout -- pylint: disable-msg=C6013 -_ERROR_CATEGORIES = [ - 'build/class', - 'build/deprecated', - 'build/endif_comment', - 'build/explicit_make_pair', - 'build/forward_decl', - 'build/header_guard', - 'build/include', - 'build/include_alpha', - 'build/include_order', - 'build/include_what_you_use', - 'build/namespaces', - 'build/printf_format', - 'build/storage_class', - 'legal/copyright', - 'readability/alt_tokens', - 'readability/braces', - 'readability/casting', - 'readability/check', - 'readability/constructors', - 'readability/fn_size', - 'readability/function', - 'readability/multiline_comment', - 'readability/multiline_string', - 'readability/namespace', - 'readability/nolint', - 'readability/streams', - 'readability/todo', - 'readability/utf8', - 'runtime/arrays', - 'runtime/casting', - 'runtime/explicit', - 'runtime/int', - 'runtime/init', - 'runtime/invalid_increment', - 'runtime/member_string_references', - 'runtime/memset', - 'runtime/operator', - 'runtime/printf', - 'runtime/printf_format', - 'runtime/references', - 'runtime/rtti', - 'runtime/sizeof', - 'runtime/string', - 'runtime/threadsafe_fn', - 'whitespace/blank_line', - 'whitespace/braces', - 'whitespace/comma', - 'whitespace/comments', - 'whitespace/empty_loop_body', - 'whitespace/end_of_line', - 'whitespace/ending_newline', - 'whitespace/forcolon', - 'whitespace/indent', - 'whitespace/labels', - 'whitespace/line_length', - 'whitespace/newline', - 'whitespace/operators', - 'whitespace/parens', - 'whitespace/semicolon', - 'whitespace/tab', - 'whitespace/todo' - ] - -# The default state of the category filter. This is overrided by the --filter= -# flag. By default all errors are on, so only add here categories that should be -# off by default (i.e., categories that must be enabled by the --filter= flags). -# All entries here should start with a '-' or '+', as in the --filter= flag. -_DEFAULT_FILTERS = ['-build/include_alpha'] - -# We used to check for high-bit characters, but after much discussion we -# decided those were OK, as long as they were in UTF-8 and didn't represent -# hard-coded international strings, which belong in a separate i18n file. - -# Headers that we consider STL headers. -_STL_HEADERS = frozenset([ - 'algobase.h', 'algorithm', 'alloc.h', 'bitset', 'deque', 'exception', - 'function.h', 'functional', 'hash_map', 'hash_map.h', 'hash_set', - 'hash_set.h', 'iterator', 'list', 'list.h', 'map', 'memory', 'new', - 'pair.h', 'pthread_alloc', 'queue', 'set', 'set.h', 'sstream', 'stack', - 'stl_alloc.h', 'stl_relops.h', 'type_traits.h', - 'utility', 'vector', 'vector.h', - ]) - - -# Non-STL C++ system headers. -_CPP_HEADERS = frozenset([ - 'algo.h', 'builtinbuf.h', 'bvector.h', 'cassert', 'cctype', - 'cerrno', 'cfloat', 'ciso646', 'climits', 'clocale', 'cmath', - 'complex', 'complex.h', 'csetjmp', 'csignal', 'cstdarg', 'cstddef', - 'cstdio', 'cstdlib', 'cstring', 'ctime', 'cwchar', 'cwctype', - 'defalloc.h', 'deque.h', 'editbuf.h', 'exception', 'fstream', - 'fstream.h', 'hashtable.h', 'heap.h', 'indstream.h', 'iomanip', - 'iomanip.h', 'ios', 'iosfwd', 'iostream', 'iostream.h', 'istream', - 'istream.h', 'iterator.h', 'limits', 'map.h', 'multimap.h', 'multiset.h', - 'numeric', 'ostream', 'ostream.h', 'parsestream.h', 'pfstream.h', - 'PlotFile.h', 'procbuf.h', 'pthread_alloc.h', 'rope', 'rope.h', - 'ropeimpl.h', 'SFile.h', 'slist', 'slist.h', 'stack.h', 'stdexcept', - 'stdiostream.h', 'streambuf', 'streambuf.h', 'stream.h', 'strfile.h', - 'string', 'strstream', 'strstream.h', 'tempbuf.h', 'tree.h', 'typeinfo', - 'valarray', - ]) - - -# Assertion macros. These are defined in base/logging.h and -# testing/base/gunit.h. Note that the _M versions need to come first -# for substring matching to work. -_CHECK_MACROS = [ - 'DCHECK', 'CHECK', - 'EXPECT_TRUE_M', 'EXPECT_TRUE', - 'ASSERT_TRUE_M', 'ASSERT_TRUE', - 'EXPECT_FALSE_M', 'EXPECT_FALSE', - 'ASSERT_FALSE_M', 'ASSERT_FALSE', - ] - -# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE -_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) - -for op, replacement in [('==', 'EQ'), ('!=', 'NE'), - ('>=', 'GE'), ('>', 'GT'), - ('<=', 'LE'), ('<', 'LT')]: - _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement - _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement - _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement - _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement - _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement - _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement - -for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), - ('>=', 'LT'), ('>', 'LE'), - ('<=', 'GT'), ('<', 'GE')]: - _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement - _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement - _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement - _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement - -# Alternative tokens and their replacements. For full list, see section 2.5 -# Alternative tokens [lex.digraph] in the C++ standard. -# -# Digraphs (such as '%:') are not included here since it's a mess to -# match those on a word boundary. -_ALT_TOKEN_REPLACEMENT = { - 'and': '&&', - 'bitor': '|', - 'or': '||', - 'xor': '^', - 'compl': '~', - 'bitand': '&', - 'and_eq': '&=', - 'or_eq': '|=', - 'xor_eq': '^=', - 'not': '!', - 'not_eq': '!=' - } - -# Compile regular expression that matches all the above keywords. The "[ =()]" -# bit is meant to avoid matching these keywords outside of boolean expressions. -# -# False positives include C-style multi-line comments (http://go/nsiut ) -# and multi-line strings (http://go/beujw ), but those have always been -# troublesome for cpplint. -_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( - r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)') - - -# These constants define types of headers for use with -# _IncludeState.CheckNextIncludeOrder(). -_C_SYS_HEADER = 1 -_CPP_SYS_HEADER = 2 -_LIKELY_MY_HEADER = 3 -_POSSIBLE_MY_HEADER = 4 -_OTHER_HEADER = 5 - -# These constants define the current inline assembly state -_NO_ASM = 0 # Outside of inline assembly block -_INSIDE_ASM = 1 # Inside inline assembly block -_END_ASM = 2 # Last line of inline assembly block -_BLOCK_ASM = 3 # The whole block is an inline assembly block - -# Match start of assembly blocks -_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)' - r'(?:\s+(volatile|__volatile__))?' - r'\s*[{(]') - - -_regexp_compile_cache = {} - -# Finds occurrences of NOLINT or NOLINT(...). -_RE_SUPPRESSION = re.compile(r'\bNOLINT\b(\([^)]*\))?') - -# {str, set(int)}: a map from error categories to sets of linenumbers -# on which those errors are expected and should be suppressed. -_error_suppressions = {} - -# The root directory used for deriving header guard CPP variable. -# This is set by --root flag. -_root = None - -def ParseNolintSuppressions(filename, raw_line, linenum, error): - """Updates the global list of error-suppressions. - - Parses any NOLINT comments on the current line, updating the global - error_suppressions store. Reports an error if the NOLINT comment - was malformed. - - Args: - filename: str, the name of the input file. - raw_line: str, the line of input text, with comments. - linenum: int, the number of the current line. - error: function, an error handler. - """ - # FIXME(adonovan): "NOLINT(" is misparsed as NOLINT(*). - matched = _RE_SUPPRESSION.search(raw_line) - if matched: - category = matched.group(1) - if category in (None, '(*)'): # => "suppress all" - _error_suppressions.setdefault(None, set()).add(linenum) - else: - if category.startswith('(') and category.endswith(')'): - category = category[1:-1] - if category in _ERROR_CATEGORIES: - _error_suppressions.setdefault(category, set()).add(linenum) - else: - error(filename, linenum, 'readability/nolint', 5, - 'Unknown NOLINT error category: %s' % category) - - -def ResetNolintSuppressions(): - "Resets the set of NOLINT suppressions to empty." - _error_suppressions.clear() - - -def IsErrorSuppressedByNolint(category, linenum): - """Returns true if the specified error category is suppressed on this line. - - Consults the global error_suppressions map populated by - ParseNolintSuppressions/ResetNolintSuppressions. - - Args: - category: str, the category of the error. - linenum: int, the current line number. - Returns: - bool, True iff the error should be suppressed due to a NOLINT comment. - """ - return (linenum in _error_suppressions.get(category, set()) or - linenum in _error_suppressions.get(None, set())) - -def Match(pattern, s): - """Matches the string with the pattern, caching the compiled regexp.""" - # The regexp compilation caching is inlined in both Match and Search for - # performance reasons; factoring it out into a separate function turns out - # to be noticeably expensive. - if not pattern in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].match(s) - - -def Search(pattern, s): - """Searches the string for the pattern, caching the compiled regexp.""" - if not pattern in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].search(s) - - -class _IncludeState(dict): - """Tracks line numbers for includes, and the order in which includes appear. - - As a dict, an _IncludeState object serves as a mapping between include - filename and line number on which that file was included. - - Call CheckNextIncludeOrder() once for each header in the file, passing - in the type constants defined above. Calls in an illegal order will - raise an _IncludeError with an appropriate error message. - - """ - # self._section will move monotonically through this set. If it ever - # needs to move backwards, CheckNextIncludeOrder will raise an error. - _INITIAL_SECTION = 0 - _MY_H_SECTION = 1 - _C_SECTION = 2 - _CPP_SECTION = 3 - _OTHER_H_SECTION = 4 - - _TYPE_NAMES = { - _C_SYS_HEADER: 'C system header', - _CPP_SYS_HEADER: 'C++ system header', - _LIKELY_MY_HEADER: 'header this file implements', - _POSSIBLE_MY_HEADER: 'header this file may implement', - _OTHER_HEADER: 'other header', - } - _SECTION_NAMES = { - _INITIAL_SECTION: "... nothing. (This can't be an error.)", - _MY_H_SECTION: 'a header this file implements', - _C_SECTION: 'C system header', - _CPP_SECTION: 'C++ system header', - _OTHER_H_SECTION: 'other header', - } - - def __init__(self): - dict.__init__(self) - # The name of the current section. - self._section = self._INITIAL_SECTION - # The path of last found header. - self._last_header = '' - - def CanonicalizeAlphabeticalOrder(self, header_path): - """Returns a path canonicalized for alphabetical comparison. - - - replaces "-" with "_" so they both cmp the same. - - removes '-inl' since we don't require them to be after the main header. - - lowercase everything, just in case. - - Args: - header_path: Path to be canonicalized. - - Returns: - Canonicalized path. - """ - return header_path.replace('-inl.h', '.h').replace('-', '_').lower() - - def IsInAlphabeticalOrder(self, header_path): - """Check if a header is in alphabetical order with the previous header. - - Args: - header_path: Header to be checked. - - Returns: - Returns true if the header is in alphabetical order. - """ - canonical_header = self.CanonicalizeAlphabeticalOrder(header_path) - if self._last_header > canonical_header: - return False - self._last_header = canonical_header - return True - - def CheckNextIncludeOrder(self, header_type): - """Returns a non-empty error message if the next header is out of order. - - This function also updates the internal state to be ready to check - the next include. - - Args: - header_type: One of the _XXX_HEADER constants defined above. - - Returns: - The empty string if the header is in the right order, or an - error message describing what's wrong. - - """ - error_message = ('Found %s after %s' % - (self._TYPE_NAMES[header_type], - self._SECTION_NAMES[self._section])) - - last_section = self._section - - if header_type == _C_SYS_HEADER: - if self._section <= self._C_SECTION: - self._section = self._C_SECTION - else: - self._last_header = '' - return error_message - elif header_type == _CPP_SYS_HEADER: - if self._section <= self._CPP_SECTION: - self._section = self._CPP_SECTION - else: - self._last_header = '' - return error_message - elif header_type == _LIKELY_MY_HEADER: - if self._section <= self._MY_H_SECTION: - self._section = self._MY_H_SECTION - else: - self._section = self._OTHER_H_SECTION - elif header_type == _POSSIBLE_MY_HEADER: - if self._section <= self._MY_H_SECTION: - self._section = self._MY_H_SECTION - else: - # This will always be the fallback because we're not sure - # enough that the header is associated with this file. - self._section = self._OTHER_H_SECTION - else: - assert header_type == _OTHER_HEADER - self._section = self._OTHER_H_SECTION - - if last_section != self._section: - self._last_header = '' - - return '' - - -class _CppLintState(object): - """Maintains module-wide state..""" - - def __init__(self): - self.verbose_level = 1 # global setting. - self.error_count = 0 # global count of reported errors - # filters to apply when emitting error messages - self.filters = _DEFAULT_FILTERS[:] - self.counting = 'total' # In what way are we counting errors? - self.errors_by_category = {} # string to int dict storing error counts - # BEGIN android-added - self.quiet = False # global setting. - # END android-added - - # output format: - # "emacs" - format that emacs can parse (default) - # "vs7" - format that Microsoft Visual Studio 7 can parse - self.output_format = 'emacs' - - def SetOutputFormat(self, output_format): - """Sets the output format for errors.""" - self.output_format = output_format - - # BEGIN android-added - def SetQuiet(self, level): - """Sets the module's quiet setting, and returns the previous setting.""" - last_quiet = self.quiet - self.quiet = level - return last_quiet - # END android-added - - def SetVerboseLevel(self, level): - """Sets the module's verbosity, and returns the previous setting.""" - last_verbose_level = self.verbose_level - self.verbose_level = level - return last_verbose_level - - def SetCountingStyle(self, counting_style): - """Sets the module's counting options.""" - self.counting = counting_style - - def SetFilters(self, filters): - """Sets the error-message filters. - - These filters are applied when deciding whether to emit a given - error message. - - Args: - filters: A string of comma-separated filters (eg "+whitespace/indent"). - Each filter should start with + or -; else we die. - - Raises: - ValueError: The comma-separated filters did not all start with '+' or '-'. - E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" - """ - # Default filters always have less priority than the flag ones. - self.filters = _DEFAULT_FILTERS[:] - for filt in filters.split(','): - clean_filt = filt.strip() - if clean_filt: - self.filters.append(clean_filt) - for filt in self.filters: - if not (filt.startswith('+') or filt.startswith('-')): - raise ValueError('Every filter in --filters must start with + or -' - ' (%s does not)' % filt) - - def ResetErrorCounts(self): - """Sets the module's error statistic back to zero.""" - self.error_count = 0 - self.errors_by_category = {} - - def IncrementErrorCount(self, category): - """Bumps the module's error statistic.""" - self.error_count += 1 - if self.counting in ('toplevel', 'detailed'): - if self.counting != 'detailed': - category = category.split('/')[0] - if category not in self.errors_by_category: - self.errors_by_category[category] = 0 - self.errors_by_category[category] += 1 - - def PrintErrorCounts(self): - """Print a summary of errors by category, and the total.""" - for category, count in self.errors_by_category.iteritems(): - sys.stderr.write('Category \'%s\' errors found: %d\n' % - (category, count)) - sys.stderr.write('Total errors found: %d\n' % self.error_count) - -_cpplint_state = _CppLintState() - - -def _OutputFormat(): - """Gets the module's output format.""" - return _cpplint_state.output_format - - -def _SetOutputFormat(output_format): - """Sets the module's output format.""" - _cpplint_state.SetOutputFormat(output_format) - - -# BEGIN android-added -def _Quiet(): - """Returns the module's quiet setting.""" - return _cpplint_state.quiet - - -def _SetQuiet(level): - """Sets the module's quiet status, and returns the previous setting.""" - return _cpplint_state.SetQuiet(level) -# END android-added - -def _VerboseLevel(): - """Returns the module's verbosity setting.""" - return _cpplint_state.verbose_level - - -def _SetVerboseLevel(level): - """Sets the module's verbosity, and returns the previous setting.""" - return _cpplint_state.SetVerboseLevel(level) - - -def _SetCountingStyle(level): - """Sets the module's counting options.""" - _cpplint_state.SetCountingStyle(level) - - -def _Filters(): - """Returns the module's list of output filters, as a list.""" - return _cpplint_state.filters - - -def _SetFilters(filters): - """Sets the module's error-message filters. - - These filters are applied when deciding whether to emit a given - error message. - - Args: - filters: A string of comma-separated filters (eg "whitespace/indent"). - Each filter should start with + or -; else we die. - """ - _cpplint_state.SetFilters(filters) - - -class _FunctionState(object): - """Tracks current function name and the number of lines in its body.""" - - _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. - _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. - - def __init__(self): - self.in_a_function = False - self.lines_in_function = 0 - self.current_function = '' - - def Begin(self, function_name): - """Start analyzing function body. - - Args: - function_name: The name of the function being tracked. - """ - self.in_a_function = True - self.lines_in_function = 0 - self.current_function = function_name - - def Count(self): - """Count line in current function body.""" - if self.in_a_function: - self.lines_in_function += 1 - - def Check(self, error, filename, linenum): - """Report if too many lines in function body. - - Args: - error: The function to call with any errors found. - filename: The name of the current file. - linenum: The number of the line to check. - """ - # BEGIN android-added - if not self.in_a_function: - return - # END android-added - if Match(r'T(EST|est)', self.current_function): - base_trigger = self._TEST_TRIGGER - else: - base_trigger = self._NORMAL_TRIGGER - trigger = base_trigger * 2**_VerboseLevel() - - if self.lines_in_function > trigger: - error_level = int(math.log(self.lines_in_function / base_trigger, 2)) - # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... - if error_level > 5: - error_level = 5 - error(filename, linenum, 'readability/fn_size', error_level, - 'Small and focused functions are preferred:' - ' %s has %d non-comment lines' - ' (error triggered by exceeding %d lines).' % ( - self.current_function, self.lines_in_function, trigger)) - - def End(self): - """Stop analyzing function body.""" - self.in_a_function = False - - -class _IncludeError(Exception): - """Indicates a problem with the include order in a file.""" - pass - - -class FileInfo: - """Provides utility functions for filenames. - - FileInfo provides easy access to the components of a file's path - relative to the project root. - """ - - def __init__(self, filename): - self._filename = filename - - def FullName(self): - """Make Windows paths like Unix.""" - return os.path.abspath(self._filename).replace('\\', '/') - - def RepositoryName(self): - """FullName after removing the local path to the repository. - - If we have a real absolute path name here we can try to do something smart: - detecting the root of the checkout and truncating /path/to/checkout from - the name so that we get header guards that don't include things like - "C:\Documents and Settings\..." or "/home/username/..." in them and thus - people on different computers who have checked the source out to different - locations won't see bogus errors. - """ - fullname = self.FullName() - - if os.path.exists(fullname): - project_dir = os.path.dirname(fullname) - - if os.path.exists(os.path.join(project_dir, ".svn")): - # If there's a .svn file in the current directory, we recursively look - # up the directory tree for the top of the SVN checkout - root_dir = project_dir - one_up_dir = os.path.dirname(root_dir) - while os.path.exists(os.path.join(one_up_dir, ".svn")): - root_dir = os.path.dirname(root_dir) - one_up_dir = os.path.dirname(one_up_dir) - - prefix = os.path.commonprefix([root_dir, project_dir]) - return fullname[len(prefix) + 1:] - - # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by - # searching up from the current path. - root_dir = os.path.dirname(fullname) - while (root_dir != os.path.dirname(root_dir) and - not os.path.exists(os.path.join(root_dir, ".git")) and - not os.path.exists(os.path.join(root_dir, ".hg")) and - not os.path.exists(os.path.join(root_dir, ".svn"))): - root_dir = os.path.dirname(root_dir) - - if (os.path.exists(os.path.join(root_dir, ".git")) or - os.path.exists(os.path.join(root_dir, ".hg")) or - os.path.exists(os.path.join(root_dir, ".svn"))): - prefix = os.path.commonprefix([root_dir, project_dir]) - # BEGIN android-changed - # return fullname[len(prefix) + 1:] - return "art/" + fullname[len(prefix) + 1:] - # END android-changed - - # Don't know what to do; header guard warnings may be wrong... - return fullname - - def Split(self): - """Splits the file into the directory, basename, and extension. - - For 'chrome/browser/browser.cc', Split() would - return ('chrome/browser', 'browser', '.cc') - - Returns: - A tuple of (directory, basename, extension). - """ - - googlename = self.RepositoryName() - project, rest = os.path.split(googlename) - return (project,) + os.path.splitext(rest) - - def BaseName(self): - """File base name - text after the final slash, before the final period.""" - return self.Split()[1] - - def Extension(self): - """File extension - text following the final period.""" - return self.Split()[2] - - def NoExtension(self): - """File has no source file extension.""" - return '/'.join(self.Split()[0:2]) - - def IsSource(self): - """File has a source file extension.""" - return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx') - - -def _ShouldPrintError(category, confidence, linenum): - """If confidence >= verbose, category passes filter and is not suppressed.""" - - # There are three ways we might decide not to print an error message: - # a "NOLINT(category)" comment appears in the source, - # the verbosity level isn't high enough, or the filters filter it out. - if IsErrorSuppressedByNolint(category, linenum): - return False - if confidence < _cpplint_state.verbose_level: - return False - - is_filtered = False - for one_filter in _Filters(): - if one_filter.startswith('-'): - if category.startswith(one_filter[1:]): - is_filtered = True - elif one_filter.startswith('+'): - if category.startswith(one_filter[1:]): - is_filtered = False - else: - assert False # should have been checked for in SetFilter. - if is_filtered: - return False - - return True - - -def Error(filename, linenum, category, confidence, message): - """Logs the fact we've found a lint error. - - We log where the error was found, and also our confidence in the error, - that is, how certain we are this is a legitimate style regression, and - not a misidentification or a use that's sometimes justified. - - False positives can be suppressed by the use of - "cpplint(category)" comments on the offending line. These are - parsed into _error_suppressions. - - Args: - filename: The name of the file containing the error. - linenum: The number of the line containing the error. - category: A string used to describe the "category" this bug - falls under: "whitespace", say, or "runtime". Categories - may have a hierarchy separated by slashes: "whitespace/indent". - confidence: A number from 1-5 representing a confidence score for - the error, with 5 meaning that we are certain of the problem, - and 1 meaning that it could be a legitimate construct. - message: The error message. - """ - if _ShouldPrintError(category, confidence, linenum): - _cpplint_state.IncrementErrorCount(category) - if _cpplint_state.output_format == 'vs7': - sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) - elif _cpplint_state.output_format == 'eclipse': - sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) - else: - sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) - - -# Matches standard C++ escape esequences per 2.13.2.3 of the C++ standard. -_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( - r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') -# Matches strings. Escape codes should already be removed by ESCAPES. -_RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES = re.compile(r'"[^"]*"') -# Matches characters. Escape codes should already be removed by ESCAPES. -_RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES = re.compile(r"'.'") -# Matches multi-line C++ comments. -# This RE is a little bit more complicated than one might expect, because we -# have to take care of space removals tools so we can handle comments inside -# statements better. -# The current rule is: We only clear spaces from both sides when we're at the -# end of the line. Otherwise, we try to remove spaces from the right side, -# if this doesn't work we try on left side but only if there's a non-character -# on the right. -_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( - r"""(\s*/\*.*\*/\s*$| - /\*.*\*/\s+| - \s+/\*.*\*/(?=\W)| - /\*.*\*/)""", re.VERBOSE) - - -def IsCppString(line): - """Does line terminate so, that the next symbol is in string constant. - - This function does not consider single-line nor multi-line comments. - - Args: - line: is a partial line of code starting from the 0..n. - - Returns: - True, if next character appended to 'line' is inside a - string constant. - """ - - line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" - return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 - - -def FindNextMultiLineCommentStart(lines, lineix): - """Find the beginning marker for a multiline comment.""" - while lineix < len(lines): - if lines[lineix].strip().startswith('/*'): - # Only return this marker if the comment goes beyond this line - if lines[lineix].strip().find('*/', 2) < 0: - return lineix - lineix += 1 - return len(lines) - - -def FindNextMultiLineCommentEnd(lines, lineix): - """We are inside a comment, find the end marker.""" - while lineix < len(lines): - if lines[lineix].strip().endswith('*/'): - return lineix - lineix += 1 - return len(lines) - - -def RemoveMultiLineCommentsFromRange(lines, begin, end): - """Clears a range of lines for multi-line comments.""" - # Having // dummy comments makes the lines non-empty, so we will not get - # unnecessary blank line warnings later in the code. - for i in range(begin, end): - lines[i] = '// dummy' - - -def RemoveMultiLineComments(filename, lines, error): - """Removes multiline (c-style) comments from lines.""" - lineix = 0 - while lineix < len(lines): - lineix_begin = FindNextMultiLineCommentStart(lines, lineix) - if lineix_begin >= len(lines): - return - lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) - if lineix_end >= len(lines): - error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, - 'Could not find end of multi-line comment') - return - RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) - lineix = lineix_end + 1 - - -def CleanseComments(line): - """Removes //-comments and single-line C-style /* */ comments. - - Args: - line: A line of C++ source. - - Returns: - The line with single-line comments removed. - """ - commentpos = line.find('//') - if commentpos != -1 and not IsCppString(line[:commentpos]): - line = line[:commentpos].rstrip() - # get rid of /* ... */ - return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) - - -class CleansedLines(object): - """Holds 3 copies of all lines with different preprocessing applied to them. - - 1) elided member contains lines without strings and comments, - 2) lines member contains lines without comments, and - 3) raw_lines member contains all the lines without processing. - All these three members are of , and of the same length. - """ - - def __init__(self, lines): - self.elided = [] - self.lines = [] - self.raw_lines = lines - self.num_lines = len(lines) - for linenum in range(len(lines)): - self.lines.append(CleanseComments(lines[linenum])) - elided = self._CollapseStrings(lines[linenum]) - self.elided.append(CleanseComments(elided)) - - def NumLines(self): - """Returns the number of lines represented.""" - return self.num_lines - - @staticmethod - def _CollapseStrings(elided): - """Collapses strings and chars on a line to simple "" or '' blocks. - - We nix strings first so we're not fooled by text like '"http://"' - - Args: - elided: The line being processed. - - Returns: - The line with collapsed strings. - """ - if not _RE_PATTERN_INCLUDE.match(elided): - # Remove escaped characters first to make quote/single quote collapsing - # basic. Things that look like escaped characters shouldn't occur - # outside of strings and chars. - elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) - elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided) - elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided) - return elided - - -def FindEndOfExpressionInLine(line, startpos, depth, startchar, endchar): - """Find the position just after the matching endchar. - - Args: - line: a CleansedLines line. - startpos: start searching at this position. - depth: nesting level at startpos. - startchar: expression opening character. - endchar: expression closing character. - - Returns: - Index just after endchar. - """ - for i in xrange(startpos, len(line)): - if line[i] == startchar: - depth += 1 - elif line[i] == endchar: - depth -= 1 - if depth == 0: - return i + 1 - return -1 - - -def CloseExpression(clean_lines, linenum, pos): - """If input points to ( or { or [, finds the position that closes it. - - If lines[linenum][pos] points to a '(' or '{' or '[', finds the - linenum/pos that correspond to the closing of the expression. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - pos: A position on the line. - - Returns: - A tuple (line, linenum, pos) pointer *past* the closing brace, or - (line, len(lines), -1) if we never find a close. Note we ignore - strings and comments when matching; and the line we return is the - 'cleansed' line at linenum. - """ - - line = clean_lines.elided[linenum] - startchar = line[pos] - if startchar not in '({[': - return (line, clean_lines.NumLines(), -1) - if startchar == '(': endchar = ')' - if startchar == '[': endchar = ']' - if startchar == '{': endchar = '}' - - # Check first line - end_pos = FindEndOfExpressionInLine(line, pos, 0, startchar, endchar) - if end_pos > -1: - return (line, linenum, end_pos) - tail = line[pos:] - num_open = tail.count(startchar) - tail.count(endchar) - while linenum < clean_lines.NumLines() - 1: - linenum += 1 - line = clean_lines.elided[linenum] - delta = line.count(startchar) - line.count(endchar) - if num_open + delta <= 0: - return (line, linenum, - FindEndOfExpressionInLine(line, 0, num_open, startchar, endchar)) - num_open += delta - - # Did not find endchar before end of file, give up - return (line, clean_lines.NumLines(), -1) - -def CheckForCopyright(filename, lines, error): - """Logs an error if no Copyright message appears at the top of the file.""" - - # We'll say it should occur by line 10. Don't forget there's a - # dummy line at the front. - for line in xrange(1, min(len(lines), 11)): - if re.search(r'Copyright', lines[line], re.I): break - else: # means no copyright line was found - error(filename, 0, 'legal/copyright', 5, - 'No copyright message found. ' - 'You should have a line: "Copyright [year] "') - - -def GetHeaderGuardCPPVariable(filename): - """Returns the CPP variable that should be used as a header guard. - - Args: - filename: The name of a C++ header file. - - Returns: - The CPP variable that should be used as a header guard in the - named file. - - """ - - # Restores original filename in case that cpplint is invoked from Emacs's - # flymake. - filename = re.sub(r'_flymake\.h$', '.h', filename) - filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename) - - fileinfo = FileInfo(filename) - file_path_from_root = fileinfo.RepositoryName() - if _root: - file_path_from_root = re.sub('^' + _root + os.sep, '', file_path_from_root) - return re.sub(r'[-./\s]', '_', file_path_from_root).upper() + '_' - - -def CheckForHeaderGuard(filename, lines, error): - """Checks that the file contains a header guard. - - Logs an error if no #ifndef header guard is present. For other - headers, checks that the full pathname is used. - - Args: - filename: The name of the C++ header file. - lines: An array of strings, each representing a line of the file. - error: The function to call with any errors found. - """ - - cppvar = GetHeaderGuardCPPVariable(filename) - - ifndef = None - ifndef_linenum = 0 - define = None - endif = None - endif_linenum = 0 - for linenum, line in enumerate(lines): - linesplit = line.split() - if len(linesplit) >= 2: - # find the first occurrence of #ifndef and #define, save arg - if not ifndef and linesplit[0] == '#ifndef': - # set ifndef to the header guard presented on the #ifndef line. - ifndef = linesplit[1] - ifndef_linenum = linenum - if not define and linesplit[0] == '#define': - define = linesplit[1] - # find the last occurrence of #endif, save entire line - if line.startswith('#endif'): - endif = line - endif_linenum = linenum - - if not ifndef: - error(filename, 0, 'build/header_guard', 5, - 'No #ifndef header guard found, suggested CPP variable is: %s' % - cppvar) - return - - if not define: - error(filename, 0, 'build/header_guard', 5, - 'No #define header guard found, suggested CPP variable is: %s' % - cppvar) - return - - # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ - # for backward compatibility. - if ifndef != cppvar: - error_level = 0 - if ifndef != cppvar + '_': - error_level = 5 - - ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum, - error) - error(filename, ifndef_linenum, 'build/header_guard', error_level, - '#ifndef header guard has wrong style, please use: %s' % cppvar) - - if define != ifndef: - error(filename, 0, 'build/header_guard', 5, - '#ifndef and #define don\'t match, suggested CPP variable is: %s' % - cppvar) - return - - if endif != ('#endif // %s' % cppvar): - error_level = 0 - if endif != ('#endif // %s' % (cppvar + '_')): - error_level = 5 - - ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum, - error) - error(filename, endif_linenum, 'build/header_guard', error_level, - '#endif line should be "#endif // %s"' % cppvar) - - -def CheckForUnicodeReplacementCharacters(filename, lines, error): - """Logs an error for each line containing Unicode replacement characters. - - These indicate that either the file contained invalid UTF-8 (likely) - or Unicode replacement characters (which it shouldn't). Note that - it's possible for this to throw off line numbering if the invalid - UTF-8 occurred adjacent to a newline. - - Args: - filename: The name of the current file. - lines: An array of strings, each representing a line of the file. - error: The function to call with any errors found. - """ - for linenum, line in enumerate(lines): - if u'\ufffd' in line: - error(filename, linenum, 'readability/utf8', 5, - 'Line contains invalid UTF-8 (or Unicode replacement character).') - - -def CheckForNewlineAtEOF(filename, lines, error): - """Logs an error if there is no newline char at the end of the file. - - Args: - filename: The name of the current file. - lines: An array of strings, each representing a line of the file. - error: The function to call with any errors found. - """ - - # The array lines() was created by adding two newlines to the - # original file (go figure), then splitting on \n. - # To verify that the file ends in \n, we just have to make sure the - # last-but-two element of lines() exists and is empty. - if len(lines) < 3 or lines[-2]: - error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, - 'Could not find a newline character at the end of the file.') - - -def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): - """Logs an error if we see /* ... */ or "..." that extend past one line. - - /* ... */ comments are legit inside macros, for one line. - Otherwise, we prefer // comments, so it's ok to warn about the - other. Likewise, it's ok for strings to extend across multiple - lines, as long as a line continuation character (backslash) - terminates each line. Although not currently prohibited by the C++ - style guide, it's ugly and unnecessary. We don't do well with either - in this lint program, so we warn about both. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Remove all \\ (escaped backslashes) from the line. They are OK, and the - # second (escaped) slash may trigger later \" detection erroneously. - line = line.replace('\\\\', '') - - if line.count('/*') > line.count('*/'): - error(filename, linenum, 'readability/multiline_comment', 5, - 'Complex multi-line /*...*/-style comment found. ' - 'Lint may give bogus warnings. ' - 'Consider replacing these with //-style comments, ' - 'with #if 0...#endif, ' - 'or with more clearly structured multi-line comments.') - - if (line.count('"') - line.count('\\"')) % 2: - error(filename, linenum, 'readability/multiline_string', 5, - 'Multi-line string ("...") found. This lint script doesn\'t ' - 'do well with such strings, and may give bogus warnings. They\'re ' - 'ugly and unnecessary, and you should use concatenation instead".') - - -threading_list = ( - ('asctime(', 'asctime_r('), - ('ctime(', 'ctime_r('), - ('getgrgid(', 'getgrgid_r('), - ('getgrnam(', 'getgrnam_r('), - ('getlogin(', 'getlogin_r('), - ('getpwnam(', 'getpwnam_r('), - ('getpwuid(', 'getpwuid_r('), - ('gmtime(', 'gmtime_r('), - ('localtime(', 'localtime_r('), - ('rand(', 'rand_r('), - ('readdir(', 'readdir_r('), - ('strtok(', 'strtok_r('), - ('ttyname(', 'ttyname_r('), - ) - - -def CheckPosixThreading(filename, clean_lines, linenum, error): - """Checks for calls to thread-unsafe functions. - - Much code has been originally written without consideration of - multi-threading. Also, engineers are relying on their old experience; - they have learned posix before threading extensions were added. These - tests guide the engineers to use thread-safe functions (when using - posix directly). - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - for single_thread_function, multithread_safe_function in threading_list: - ix = line.find(single_thread_function) - # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 - if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and - line[ix - 1] not in ('_', '.', '>'))): - error(filename, linenum, 'runtime/threadsafe_fn', 2, - 'Consider using ' + multithread_safe_function + - '...) instead of ' + single_thread_function + - '...) for improved thread safety.') - - -# Matches invalid increment: *count++, which moves pointer instead of -# incrementing a value. -_RE_PATTERN_INVALID_INCREMENT = re.compile( - r'^\s*\*\w+(\+\+|--);') - - -def CheckInvalidIncrement(filename, clean_lines, linenum, error): - """Checks for invalid increment *count++. - - For example following function: - void increment_counter(int* count) { - *count++; - } - is invalid, because it effectively does count++, moving pointer, and should - be replaced with ++*count, (*count)++ or *count += 1. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - if _RE_PATTERN_INVALID_INCREMENT.match(line): - error(filename, linenum, 'runtime/invalid_increment', 5, - 'Changing pointer instead of value (or unused value of operator*).') - - -class _BlockInfo(object): - """Stores information about a generic block of code.""" - - def __init__(self, seen_open_brace): - self.seen_open_brace = seen_open_brace - self.open_parentheses = 0 - self.inline_asm = _NO_ASM - - def CheckBegin(self, filename, clean_lines, linenum, error): - """Run checks that applies to text up to the opening brace. - - This is mostly for checking the text after the class identifier - and the "{", usually where the base class is specified. For other - blocks, there isn't much to check, so we always pass. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - pass - - def CheckEnd(self, filename, clean_lines, linenum, error): - """Run checks that applies to text after the closing brace. - - This is mostly used for checking end of namespace comments. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - pass - - -class _ClassInfo(_BlockInfo): - """Stores information about a class.""" - - def __init__(self, name, class_or_struct, clean_lines, linenum): - _BlockInfo.__init__(self, False) - self.name = name - self.starting_linenum = linenum - self.is_derived = False - if class_or_struct == 'struct': - self.access = 'public' - else: - self.access = 'private' - - # Try to find the end of the class. This will be confused by things like: - # class A { - # } *x = { ... - # - # But it's still good enough for CheckSectionSpacing. - self.last_line = 0 - depth = 0 - for i in range(linenum, clean_lines.NumLines()): - line = clean_lines.elided[i] - depth += line.count('{') - line.count('}') - if not depth: - self.last_line = i - break - - def CheckBegin(self, filename, clean_lines, linenum, error): - # Look for a bare ':' - if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): - self.is_derived = True - - -class _NamespaceInfo(_BlockInfo): - """Stores information about a namespace.""" - - def __init__(self, name, linenum): - _BlockInfo.__init__(self, False) - self.name = name or '' - self.starting_linenum = linenum - - def CheckEnd(self, filename, clean_lines, linenum, error): - """Check end of namespace comments.""" - line = clean_lines.raw_lines[linenum] - - # Check how many lines is enclosed in this namespace. Don't issue - # warning for missing namespace comments if there aren't enough - # lines. However, do apply checks if there is already an end of - # namespace comment and it's incorrect. - # - # TODO(unknown): We always want to check end of namespace comments - # if a namespace is large, but sometimes we also want to apply the - # check if a short namespace contained nontrivial things (something - # other than forward declarations). There is currently no logic on - # deciding what these nontrivial things are, so this check is - # triggered by namespace size only, which works most of the time. - if (linenum - self.starting_linenum < 10 - and not Match(r'};*\s*(//|/\*).*\bnamespace\b', line)): - return - - # Look for matching comment at end of namespace. - # - # Note that we accept C style "/* */" comments for terminating - # namespaces, so that code that terminate namespaces inside - # preprocessor macros can be cpplint clean. Example: http://go/nxpiz - # - # We also accept stuff like "// end of namespace ." with the - # period at the end. - # - # Besides these, we don't accept anything else, otherwise we might - # get false negatives when existing comment is a substring of the - # expected namespace. Example: http://go/ldkdc, http://cl/23548205 - if self.name: - # Named namespace - if not Match((r'};*\s*(//|/\*).*\bnamespace\s+' + re.escape(self.name) + - r'[\*/\.\\\s]*$'), - line): - error(filename, linenum, 'readability/namespace', 5, - 'Namespace should be terminated with "// namespace %s"' % - self.name) - else: - # Anonymous namespace - if not Match(r'};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line): - error(filename, linenum, 'readability/namespace', 5, - 'Namespace should be terminated with "// namespace"') - - -class _PreprocessorInfo(object): - """Stores checkpoints of nesting stacks when #if/#else is seen.""" - - def __init__(self, stack_before_if): - # The entire nesting stack before #if - self.stack_before_if = stack_before_if - - # The entire nesting stack up to #else - self.stack_before_else = [] - - # Whether we have already seen #else or #elif - self.seen_else = False - - -class _NestingState(object): - """Holds states related to parsing braces.""" - - def __init__(self): - # Stack for tracking all braces. An object is pushed whenever we - # see a "{", and popped when we see a "}". Only 3 types of - # objects are possible: - # - _ClassInfo: a class or struct. - # - _NamespaceInfo: a namespace. - # - _BlockInfo: some other type of block. - self.stack = [] - - # Stack of _PreprocessorInfo objects. - self.pp_stack = [] - - def SeenOpenBrace(self): - """Check if we have seen the opening brace for the innermost block. - - Returns: - True if we have seen the opening brace, False if the innermost - block is still expecting an opening brace. - """ - return (not self.stack) or self.stack[-1].seen_open_brace - - def InNamespaceBody(self): - """Check if we are currently one level inside a namespace body. - - Returns: - True if top of the stack is a namespace block, False otherwise. - """ - return self.stack and isinstance(self.stack[-1], _NamespaceInfo) - - def UpdatePreprocessor(self, line): - """Update preprocessor stack. - - We need to handle preprocessors due to classes like this: - #ifdef SWIG - struct ResultDetailsPageElementExtensionPoint { - #else - struct ResultDetailsPageElementExtensionPoint : public Extension { - #endif - (see http://go/qwddn for original example) - - We make the following assumptions (good enough for most files): - - Preprocessor condition evaluates to true from #if up to first - #else/#elif/#endif. - - - Preprocessor condition evaluates to false from #else/#elif up - to #endif. We still perform lint checks on these lines, but - these do not affect nesting stack. - - Args: - line: current line to check. - """ - if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): - # Beginning of #if block, save the nesting stack here. The saved - # stack will allow us to restore the parsing state in the #else case. - self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) - elif Match(r'^\s*#\s*(else|elif)\b', line): - # Beginning of #else block - if self.pp_stack: - if not self.pp_stack[-1].seen_else: - # This is the first #else or #elif block. Remember the - # whole nesting stack up to this point. This is what we - # keep after the #endif. - self.pp_stack[-1].seen_else = True - self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack) - - # Restore the stack to how it was before the #if - self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) - else: - # TODO(unknown): unexpected #else, issue warning? - pass - elif Match(r'^\s*#\s*endif\b', line): - # End of #if or #else blocks. - if self.pp_stack: - # If we saw an #else, we will need to restore the nesting - # stack to its former state before the #else, otherwise we - # will just continue from where we left off. - if self.pp_stack[-1].seen_else: - # Here we can just use a shallow copy since we are the last - # reference to it. - self.stack = self.pp_stack[-1].stack_before_else - # Drop the corresponding #if - self.pp_stack.pop() - else: - # TODO(unknown): unexpected #endif, issue warning? - pass - - def Update(self, filename, clean_lines, linenum, error): - """Update nesting state with current line. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Update pp_stack first - self.UpdatePreprocessor(line) - - # Count parentheses. This is to avoid adding struct arguments to - # the nesting stack. - if self.stack: - inner_block = self.stack[-1] - depth_change = line.count('(') - line.count(')') - inner_block.open_parentheses += depth_change - - # Also check if we are starting or ending an inline assembly block. - if inner_block.inline_asm in (_NO_ASM, _END_ASM): - if (depth_change != 0 and - inner_block.open_parentheses == 1 and - _MATCH_ASM.match(line)): - # Enter assembly block - inner_block.inline_asm = _INSIDE_ASM - else: - # Not entering assembly block. If previous line was _END_ASM, - # we will now shift to _NO_ASM state. - inner_block.inline_asm = _NO_ASM - elif (inner_block.inline_asm == _INSIDE_ASM and - inner_block.open_parentheses == 0): - # Exit assembly block - inner_block.inline_asm = _END_ASM - - # Consume namespace declaration at the beginning of the line. Do - # this in a loop so that we catch same line declarations like this: - # namespace proto2 { namespace bridge { class MessageSet; } } - while True: - # Match start of namespace. The "\b\s*" below catches namespace - # declarations even if it weren't followed by a whitespace, this - # is so that we don't confuse our namespace checker. The - # missing spaces will be flagged by CheckSpacing. - namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) - if not namespace_decl_match: - break - - new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum) - self.stack.append(new_namespace) - - line = namespace_decl_match.group(2) - if line.find('{') != -1: - new_namespace.seen_open_brace = True - line = line[line.find('{') + 1:] - - # Look for a class declaration in whatever is left of the line - # after parsing namespaces. The regexp accounts for decorated classes - # such as in: - # class LOCKABLE API Object { - # }; - # - # Templates with class arguments may confuse the parser, for example: - # template , - # class Vector = vector > - # class HeapQueue { - # - # Because this parser has no nesting state about templates, by the - # time it saw "class Comparator", it may think that it's a new class. - # Nested templates have a similar problem: - # template < - # typename ExportedType, - # typename TupleType, - # template class ImplTemplate> - # - # To avoid these cases, we ignore classes that are followed by '=' or '>' - class_decl_match = Match( - r'\s*(template\s*<[\w\s<>,:]*>\s*)?' - '(class|struct)\s+([A-Z_]+\s+)*(\w+(?:::\w+)*)' - '(([^=>]|<[^<>]*>)*)$', line) - if (class_decl_match and - (not self.stack or self.stack[-1].open_parentheses == 0)): - self.stack.append(_ClassInfo( - class_decl_match.group(4), class_decl_match.group(2), - clean_lines, linenum)) - line = class_decl_match.group(5) - - # If we have not yet seen the opening brace for the innermost block, - # run checks here. - if not self.SeenOpenBrace(): - self.stack[-1].CheckBegin(filename, clean_lines, linenum, error) - - # Update access control if we are inside a class/struct - if self.stack and isinstance(self.stack[-1], _ClassInfo): - access_match = Match(r'\s*(public|private|protected)\s*:', line) - if access_match: - self.stack[-1].access = access_match.group(1) - - # Consume braces or semicolons from what's left of the line - while True: - # Match first brace, semicolon, or closed parenthesis. - matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) - if not matched: - break - - token = matched.group(1) - if token == '{': - # If namespace or class hasn't seen a opening brace yet, mark - # namespace/class head as complete. Push a new block onto the - # stack otherwise. - if not self.SeenOpenBrace(): - self.stack[-1].seen_open_brace = True - else: - self.stack.append(_BlockInfo(True)) - if _MATCH_ASM.match(line): - self.stack[-1].inline_asm = _BLOCK_ASM - elif token == ';' or token == ')': - # If we haven't seen an opening brace yet, but we already saw - # a semicolon, this is probably a forward declaration. Pop - # the stack for these. - # - # Similarly, if we haven't seen an opening brace yet, but we - # already saw a closing parenthesis, then these are probably - # function arguments with extra "class" or "struct" keywords. - # Also pop these stack for these. - if not self.SeenOpenBrace(): - self.stack.pop() - else: # token == '}' - # Perform end of block checks and pop the stack. - if self.stack: - self.stack[-1].CheckEnd(filename, clean_lines, linenum, error) - self.stack.pop() - line = matched.group(2) - - def InnermostClass(self): - """Get class info on the top of the stack. - - Returns: - A _ClassInfo object if we are inside a class, or None otherwise. - """ - for i in range(len(self.stack), 0, -1): - classinfo = self.stack[i - 1] - if isinstance(classinfo, _ClassInfo): - return classinfo - return None - - def CheckClassFinished(self, filename, error): - """Checks that all classes have been completely parsed. - - Call this when all lines in a file have been processed. - Args: - filename: The name of the current file. - error: The function to call with any errors found. - """ - # Note: This test can result in false positives if #ifdef constructs - # get in the way of brace matching. See the testBuildClass test in - # cpplint_unittest.py for an example of this. - for obj in self.stack: - if isinstance(obj, _ClassInfo): - error(filename, obj.starting_linenum, 'build/class', 5, - 'Failed to find complete declaration of class %s' % - obj.name) - - -def CheckForNonStandardConstructs(filename, clean_lines, linenum, - nesting_state, error): - """Logs an error if we see certain non-ANSI constructs ignored by gcc-2. - - Complain about several constructs which gcc-2 accepts, but which are - not standard C++. Warning about these in lint is one way to ease the - transition to new compilers. - - put storage class first (e.g. "static const" instead of "const static"). - - "%lld" instead of %qd" in printf-type functions. - - "%1$d" is non-standard in printf-type functions. - - "\%" is an undefined character escape sequence. - - text after #endif is not allowed. - - invalid inner-style forward declaration. - - >? and ?= and )\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', - line): - error(filename, linenum, 'build/deprecated', 3, - '>? and ))?' - # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;' - error(filename, linenum, 'runtime/member_string_references', 2, - 'const string& members are dangerous. It is much better to use ' - 'alternatives, such as pointers or simple constants.') - - # Everything else in this function operates on class declarations. - # Return early if the top of the nesting stack is not a class, or if - # the class head is not completed yet. - classinfo = nesting_state.InnermostClass() - if not classinfo or not classinfo.seen_open_brace: - return - - # The class may have been declared with namespace or classname qualifiers. - # The constructor and destructor will not have those qualifiers. - base_classname = classinfo.name.split('::')[-1] - - # Look for single-argument constructors that aren't marked explicit. - # Technically a valid construct, but against style. - args = Match(r'\s+(?:inline\s+)?%s\s*\(([^,()]+)\)' - % re.escape(base_classname), - line) - if (args and - args.group(1) != 'void' and - not Match(r'(const\s+)?%s\s*(?:<\w+>\s*)?&' % re.escape(base_classname), - args.group(1).strip())): - error(filename, linenum, 'runtime/explicit', 5, - 'Single-argument constructors should be marked explicit.') - - -def CheckSpacingForFunctionCall(filename, line, linenum, error): - """Checks for the correctness of various spacing around function calls. - - Args: - filename: The name of the current file. - line: The text of the line to check. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - # Since function calls often occur inside if/for/while/switch - # expressions - which have their own, more liberal conventions - we - # first see if we should be looking inside such an expression for a - # function call, to which we can apply more strict standards. - fncall = line # if there's no control flow construct, look at whole line - for pattern in (r'\bif\s*\((.*)\)\s*{', - r'\bfor\s*\((.*)\)\s*{', - r'\bwhile\s*\((.*)\)\s*[{;]', - r'\bswitch\s*\((.*)\)\s*{'): - match = Search(pattern, line) - if match: - fncall = match.group(1) # look inside the parens for function calls - break - - # Except in if/for/while/switch, there should never be space - # immediately inside parens (eg "f( 3, 4 )"). We make an exception - # for nested parens ( (a+b) + c ). Likewise, there should never be - # a space before a ( when it's a function argument. I assume it's a - # function argument when the char before the whitespace is legal in - # a function name (alnum + _) and we're not starting a macro. Also ignore - # pointers and references to arrays and functions coz they're too tricky: - # we use a very simple way to recognize these: - # " (something)(maybe-something)" or - # " (something)(maybe-something," or - # " (something)[something]" - # Note that we assume the contents of [] to be short enough that - # they'll never need to wrap. - if ( # Ignore control structures. - # BEGIN android-changed - # not Search(r'\b(if|for|while|switch|return|delete)\b', fncall) and - not Search(r'\b(if|for|while|switch|return|delete|new)\b', fncall) and - # END android-changed - # Ignore pointers/references to functions. - not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and - # Ignore pointers/references to arrays. - not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): - if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call - error(filename, linenum, 'whitespace/parens', 4, - 'Extra space after ( in function call') - elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): - error(filename, linenum, 'whitespace/parens', 2, - 'Extra space after (') - if (Search(r'\w\s+\(', fncall) and - not Search(r'#\s*define|typedef', fncall) and - not Search(r'\w\s+\((\w+::)?\*\w+\)\(', fncall)): - error(filename, linenum, 'whitespace/parens', 4, - 'Extra space before ( in function call') - # If the ) is followed only by a newline or a { + newline, assume it's - # part of a control statement (if/while/etc), and don't complain - if Search(r'[^)]\s+\)\s*[^{\s]', fncall): - # If the closing parenthesis is preceded by only whitespaces, - # try to give a more descriptive error message. - if Search(r'^\s+\)', fncall): - error(filename, linenum, 'whitespace/parens', 2, - 'Closing ) should be moved to the previous line') - else: - error(filename, linenum, 'whitespace/parens', 2, - 'Extra space before )') - - -def IsBlankLine(line): - """Returns true if the given line is blank. - - We consider a line to be blank if the line is empty or consists of - only white spaces. - - Args: - line: A line of a string. - - Returns: - True, if the given line is blank. - """ - return not line or line.isspace() - - -def CheckForFunctionLengths(filename, clean_lines, linenum, - function_state, error): - """Reports for long function bodies. - - For an overview why this is done, see: - http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions - - Uses a simplistic algorithm assuming other style guidelines - (especially spacing) are followed. - Only checks unindented functions, so class members are unchecked. - Trivial bodies are unchecked, so constructors with huge initializer lists - may be missed. - Blank/comment lines are not counted so as to avoid encouraging the removal - of vertical space and comments just to get through a lint check. - NOLINT *on the last line of a function* disables this check. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - function_state: Current function name and lines in body so far. - error: The function to call with any errors found. - """ - lines = clean_lines.lines - line = lines[linenum] - raw = clean_lines.raw_lines - raw_line = raw[linenum] - joined_line = '' - - starting_func = False - regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... - match_result = Match(regexp, line) - if match_result: - # If the name is all caps and underscores, figure it's a macro and - # ignore it, unless it's TEST or TEST_F. - function_name = match_result.group(1).split()[-1] - if function_name == 'TEST' or function_name == 'TEST_F' or ( - not Match(r'[A-Z_]+$', function_name)): - starting_func = True - - if starting_func: - body_found = False - for start_linenum in xrange(linenum, clean_lines.NumLines()): - start_line = lines[start_linenum] - joined_line += ' ' + start_line.lstrip() - if Search(r'(;|})', start_line): # Declarations and trivial functions - body_found = True - break # ... ignore - elif Search(r'{', start_line): - body_found = True - function = Search(r'((\w|:)*)\(', line).group(1) - if Match(r'TEST', function): # Handle TEST... macros - parameter_regexp = Search(r'(\(.*\))', joined_line) - if parameter_regexp: # Ignore bad syntax - function += parameter_regexp.group(1) - else: - function += '()' - function_state.Begin(function) - break - if not body_found: - # No body for the function (or evidence of a non-function) was found. - error(filename, linenum, 'readability/fn_size', 5, - 'Lint failed to find start of function body.') - elif Match(r'^\}\s*$', line): # function end - function_state.Check(error, filename, linenum) - function_state.End() - elif not Match(r'^\s*$', line): - function_state.Count() # Count non-blank/non-comment lines. - - -_RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?') - - -def CheckComment(comment, filename, linenum, error): - """Checks for common mistakes in TODO comments. - - Args: - comment: The text of the comment from the line in question. - filename: The name of the current file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - match = _RE_PATTERN_TODO.match(comment) - if match: - # One whitespace is correct; zero whitespace is handled elsewhere. - leading_whitespace = match.group(1) - if len(leading_whitespace) > 1: - error(filename, linenum, 'whitespace/todo', 2, - 'Too many spaces before TODO') - - username = match.group(2) - if not username: - error(filename, linenum, 'readability/todo', 2, - 'Missing username in TODO; it should look like ' - '"// TODO(my_username): Stuff."') - - middle_whitespace = match.group(3) - # Comparisons made explicit for correctness -- pylint: disable-msg=C6403 - if middle_whitespace != ' ' and middle_whitespace != '': - error(filename, linenum, 'whitespace/todo', 2, - 'TODO(my_username) should be followed by a space') - -def CheckAccess(filename, clean_lines, linenum, nesting_state, error): - """Checks for improper use of DISALLOW* macros. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A _NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] # get rid of comments and strings - - matched = Match((r'\s*(DISALLOW_COPY_AND_ASSIGN|' - r'DISALLOW_EVIL_CONSTRUCTORS|' - r'DISALLOW_IMPLICIT_CONSTRUCTORS)'), line) - if not matched: - return - if nesting_state.stack and isinstance(nesting_state.stack[-1], _ClassInfo): - if nesting_state.stack[-1].access != 'private': - error(filename, linenum, 'readability/constructors', 3, - '%s must be in the private: section' % matched.group(1)) - - else: - # Found DISALLOW* macro outside a class declaration, or perhaps it - # was used inside a function when it should have been part of the - # class declaration. We could issue a warning here, but it - # probably resulted in a compiler error already. - pass - - -def FindNextMatchingAngleBracket(clean_lines, linenum, init_suffix): - """Find the corresponding > to close a template. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: Current line number. - init_suffix: Remainder of the current line after the initial <. - - Returns: - True if a matching bracket exists. - """ - line = init_suffix - nesting_stack = ['<'] - while True: - # Find the next operator that can tell us whether < is used as an - # opening bracket or as a less-than operator. We only want to - # warn on the latter case. - # - # We could also check all other operators and terminate the search - # early, e.g. if we got something like this "a(),;\[\]]*([<>(),;\[\]])(.*)$', line) - if match: - # Found an operator, update nesting stack - operator = match.group(1) - line = match.group(2) - - if nesting_stack[-1] == '<': - # Expecting closing angle bracket - if operator in ('<', '(', '['): - nesting_stack.append(operator) - elif operator == '>': - nesting_stack.pop() - if not nesting_stack: - # Found matching angle bracket - return True - elif operator == ',': - # Got a comma after a bracket, this is most likely a template - # argument. We have not seen a closing angle bracket yet, but - # it's probably a few lines later if we look for it, so just - # return early here. - return True - else: - # Got some other operator. - return False - - else: - # Expecting closing parenthesis or closing bracket - if operator in ('<', '(', '['): - nesting_stack.append(operator) - elif operator in (')', ']'): - # We don't bother checking for matching () or []. If we got - # something like (] or [), it would have been a syntax error. - nesting_stack.pop() - - else: - # Scan the next line - linenum += 1 - if linenum >= len(clean_lines.elided): - break - line = clean_lines.elided[linenum] - - # Exhausted all remaining lines and still no matching angle bracket. - # Most likely the input was incomplete, otherwise we should have - # seen a semicolon and returned early. - return True - - -def FindPreviousMatchingAngleBracket(clean_lines, linenum, init_prefix): - """Find the corresponding < that started a template. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: Current line number. - init_prefix: Part of the current line before the initial >. - - Returns: - True if a matching bracket exists. - """ - line = init_prefix - nesting_stack = ['>'] - while True: - # Find the previous operator - match = Search(r'^(.*)([<>(),;\[\]])[^<>(),;\[\]]*$', line) - if match: - # Found an operator, update nesting stack - operator = match.group(2) - line = match.group(1) - - if nesting_stack[-1] == '>': - # Expecting opening angle bracket - if operator in ('>', ')', ']'): - nesting_stack.append(operator) - elif operator == '<': - nesting_stack.pop() - if not nesting_stack: - # Found matching angle bracket - return True - elif operator == ',': - # Got a comma before a bracket, this is most likely a - # template argument. The opening angle bracket is probably - # there if we look for it, so just return early here. - return True - else: - # Got some other operator. - return False - - else: - # Expecting opening parenthesis or opening bracket - if operator in ('>', ')', ']'): - nesting_stack.append(operator) - elif operator in ('(', '['): - nesting_stack.pop() - - else: - # Scan the previous line - linenum -= 1 - if linenum < 0: - break - line = clean_lines.elided[linenum] - - # Exhausted all earlier lines and still no matching angle bracket. - return False - - -def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): - """Checks for the correctness of various spacing issues in the code. - - Things we check for: spaces around operators, spaces after - if/for/while/switch, no spaces around parens in function calls, two - spaces between code and comment, don't start a block with a blank - line, don't end a function with a blank line, don't add a blank line - after public/protected/private, don't have too many blank lines in a row. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A _NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - - raw = clean_lines.raw_lines - line = raw[linenum] - - # Before nixing comments, check if the line is blank for no good - # reason. This includes the first line after a block is opened, and - # blank lines at the end of a function (ie, right before a line like '}' - # - # Skip all the blank line checks if we are immediately inside a - # namespace body. In other words, don't issue blank line warnings - # for this block: - # namespace { - # - # } - # - # A warning about missing end of namespace comments will be issued instead. - if IsBlankLine(line) and not nesting_state.InNamespaceBody(): - elided = clean_lines.elided - prev_line = elided[linenum - 1] - prevbrace = prev_line.rfind('{') - # TODO(unknown): Don't complain if line before blank line, and line after, - # both start with alnums and are indented the same amount. - # This ignores whitespace at the start of a namespace block - # because those are not usually indented. - if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1: - # OK, we have a blank line at the start of a code block. Before we - # complain, we check if it is an exception to the rule: The previous - # non-empty line has the parameters of a function header that are indented - # 4 spaces (because they did not fit in a 80 column line when placed on - # the same line as the function name). We also check for the case where - # the previous line is indented 6 spaces, which may happen when the - # initializers of a constructor do not fit into a 80 column line. - exception = False - if Match(r' {6}\w', prev_line): # Initializer list? - # We are looking for the opening column of initializer list, which - # should be indented 4 spaces to cause 6 space indentation afterwards. - search_position = linenum-2 - while (search_position >= 0 - and Match(r' {6}\w', elided[search_position])): - search_position -= 1 - exception = (search_position >= 0 - and elided[search_position][:5] == ' :') - else: - # Search for the function arguments or an initializer list. We use a - # simple heuristic here: If the line is indented 4 spaces; and we have a - # closing paren, without the opening paren, followed by an opening brace - # or colon (for initializer lists) we assume that it is the last line of - # a function header. If we have a colon indented 4 spaces, it is an - # initializer list. - exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', - prev_line) - or Match(r' {4}:', prev_line)) - - if not exception: - error(filename, linenum, 'whitespace/blank_line', 2, - 'Blank line at the start of a code block. Is this needed?') - # Ignore blank lines at the end of a block in a long if-else - # chain, like this: - # if (condition1) { - # // Something followed by a blank line - # - # } else if (condition2) { - # // Something else - # } - if linenum + 1 < clean_lines.NumLines(): - next_line = raw[linenum + 1] - if (next_line - and Match(r'\s*}', next_line) - and next_line.find('} else ') == -1): - error(filename, linenum, 'whitespace/blank_line', 3, - 'Blank line at the end of a code block. Is this needed?') - - matched = Match(r'\s*(public|protected|private):', prev_line) - if matched: - error(filename, linenum, 'whitespace/blank_line', 3, - 'Do not leave a blank line after "%s:"' % matched.group(1)) - - # Next, we complain if there's a comment too near the text - commentpos = line.find('//') - if commentpos != -1: - # Check if the // may be in quotes. If so, ignore it - # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 - if (line.count('"', 0, commentpos) - - line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes - # Allow one space for new scopes, two spaces otherwise: - if (not Match(r'^\s*{ //', line) and - ((commentpos >= 1 and - line[commentpos-1] not in string.whitespace) or - (commentpos >= 2 and - line[commentpos-2] not in string.whitespace))): - error(filename, linenum, 'whitespace/comments', 2, - 'At least two spaces is best between code and comments') - # There should always be a space between the // and the comment - commentend = commentpos + 2 - if commentend < len(line) and not line[commentend] == ' ': - # but some lines are exceptions -- e.g. if they're big - # comment delimiters like: - # //---------------------------------------------------------- - # or are an empty C++ style Doxygen comment, like: - # /// - # or they begin with multiple slashes followed by a space: - # //////// Header comment - match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or - Search(r'^/$', line[commentend:]) or - Search(r'^/+ ', line[commentend:])) - if not match: - error(filename, linenum, 'whitespace/comments', 4, - 'Should have a space between // and comment') - CheckComment(line[commentpos:], filename, linenum, error) - - line = clean_lines.elided[linenum] # get rid of comments and strings - - # Don't try to do spacing checks for operator methods - line = re.sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line) - - # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". - # Otherwise not. Note we only check for non-spaces on *both* sides; - # sometimes people put non-spaces on one side when aligning ='s among - # many lines (not that this is behavior that I approve of...) - if Search(r'[\w.]=[\w.]', line) and not Search(r'\b(if|while) ', line): - error(filename, linenum, 'whitespace/operators', 4, - 'Missing spaces around =') - - # It's ok not to have spaces around binary operators like + - * /, but if - # there's too little whitespace, we get concerned. It's hard to tell, - # though, so we punt on this one for now. TODO. - - # You should always have whitespace around binary operators. - # - # Check <= and >= first to avoid false positives with < and >, then - # check non-include lines for spacing around < and >. - match = Search(r'[^<>=!\s](==|!=|<=|>=)[^<>=!\s]', line) - if match: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around %s' % match.group(1)) - # We allow no-spaces around << when used like this: 10<<20, but - # not otherwise (particularly, not when used as streams) - match = Search(r'(\S)(?:L|UL|ULL|l|ul|ull)?<<(\S)', line) - if match and not (match.group(1).isdigit() and match.group(2).isdigit()): - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around <<') - elif not Match(r'#.*include', line): - # Avoid false positives on -> - reduced_line = line.replace('->', '') - - # Look for < that is not surrounded by spaces. This is only - # triggered if both sides are missing spaces, even though - # technically should should flag if at least one side is missing a - # space. This is done to avoid some false positives with shifts. - match = Search(r'[^\s<]<([^\s=<].*)', reduced_line) - if (match and - not FindNextMatchingAngleBracket(clean_lines, linenum, match.group(1))): - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around <') - - # Look for > that is not surrounded by spaces. Similar to the - # above, we only trigger if both sides are missing spaces to avoid - # false positives with shifts. - match = Search(r'^(.*[^\s>])>[^\s=>]', reduced_line) - if (match and - not FindPreviousMatchingAngleBracket(clean_lines, linenum, - match.group(1))): - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around >') - - # We allow no-spaces around >> for almost anything. This is because - # C++11 allows ">>" to close nested templates, which accounts for - # most cases when ">>" is not followed by a space. - # - # We still warn on ">>" followed by alpha character, because that is - # likely due to ">>" being used for right shifts, e.g.: - # value >> alpha - # - # When ">>" is used to close templates, the alphanumeric letter that - # follows would be part of an identifier, and there should still be - # a space separating the template type and the identifier. - # type> alpha - match = Search(r'>>[a-zA-Z_]', line) - if match: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around >>') - - # There shouldn't be space around unary operators - match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) - if match: - error(filename, linenum, 'whitespace/operators', 4, - 'Extra space for operator %s' % match.group(1)) - - # A pet peeve of mine: no spaces after an if, while, switch, or for - match = Search(r' (if\(|for\(|while\(|switch\()', line) - if match: - error(filename, linenum, 'whitespace/parens', 5, - 'Missing space before ( in %s' % match.group(1)) - - # For if/for/while/switch, the left and right parens should be - # consistent about how many spaces are inside the parens, and - # there should either be zero or one spaces inside the parens. - # We don't want: "if ( foo)" or "if ( foo )". - # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. - match = Search(r'\b(if|for|while|switch)\s*' - r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', - line) - if match: - if len(match.group(2)) != len(match.group(4)): - if not (match.group(3) == ';' and - len(match.group(2)) == 1 + len(match.group(4)) or - not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): - error(filename, linenum, 'whitespace/parens', 5, - 'Mismatching spaces inside () in %s' % match.group(1)) - if not len(match.group(2)) in [0, 1]: - error(filename, linenum, 'whitespace/parens', 5, - 'Should have zero or one spaces inside ( and ) in %s' % - match.group(1)) - - # You should always have a space after a comma (either as fn arg or operator) - if Search(r',[^\s]', line): - error(filename, linenum, 'whitespace/comma', 3, - 'Missing space after ,') - - # You should always have a space after a semicolon - # except for few corner cases - # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more - # space after ; - if Search(r';[^\s};\\)/]', line): - error(filename, linenum, 'whitespace/semicolon', 3, - 'Missing space after ;') - - # Next we will look for issues with function calls. - CheckSpacingForFunctionCall(filename, line, linenum, error) - - # Except after an opening paren, or after another opening brace (in case of - # an initializer list, for instance), you should have spaces before your - # braces. And since you should never have braces at the beginning of a line, - # this is an easy test. - if Search(r'[^ ({]{', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Missing space before {') - - # Make sure '} else {' has spaces. - if Search(r'}else', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Missing space before else') - - # You shouldn't have spaces before your brackets, except maybe after - # 'delete []' or 'new char * []'. - if Search(r'\w\s+\[', line) and not Search(r'delete\s+\[', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Extra space before [') - - # You shouldn't have a space before a semicolon at the end of the line. - # There's a special case for "for" since the style guide allows space before - # the semicolon there. - if Search(r':\s*;\s*$', line): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Semicolon defining empty statement. Use {} instead.') - elif Search(r'^\s*;\s*$', line): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Line contains only semicolon. If this should be an empty statement, ' - 'use {} instead.') - elif (Search(r'\s+;\s*$', line) and - not Search(r'\bfor\b', line)): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Extra space before last semicolon. If this should be an empty ' - 'statement, use {} instead.') - - # In range-based for, we wanted spaces before and after the colon, but - # not around "::" tokens that might appear. - if (Search('for *\(.*[^:]:[^: ]', line) or - Search('for *\(.*[^: ]:[^:]', line)): - error(filename, linenum, 'whitespace/forcolon', 2, - 'Missing space around colon in range-based for loop') - - -def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): - """Checks for additional blank line issues related to sections. - - Currently the only thing checked here is blank line before protected/private. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - class_info: A _ClassInfo objects. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - # Skip checks if the class is small, where small means 25 lines or less. - # 25 lines seems like a good cutoff since that's the usual height of - # terminals, and any class that can't fit in one screen can't really - # be considered "small". - # - # Also skip checks if we are on the first line. This accounts for - # classes that look like - # class Foo { public: ... }; - # - # If we didn't find the end of the class, last_line would be zero, - # and the check will be skipped by the first condition. - if (class_info.last_line - class_info.starting_linenum <= 24 or - linenum <= class_info.starting_linenum): - return - - matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) - if matched: - # Issue warning if the line before public/protected/private was - # not a blank line, but don't do this if the previous line contains - # "class" or "struct". This can happen two ways: - # - We are at the beginning of the class. - # - We are forward-declaring an inner class that is semantically - # private, but needed to be public for implementation reasons. - # Also ignores cases where the previous line ends with a backslash as can be - # common when defining classes in C macros. - prev_line = clean_lines.lines[linenum - 1] - if (not IsBlankLine(prev_line) and - not Search(r'\b(class|struct)\b', prev_line) and - not Search(r'\\$', prev_line)): - # Try a bit harder to find the beginning of the class. This is to - # account for multi-line base-specifier lists, e.g.: - # class Derived - # : public Base { - end_class_head = class_info.starting_linenum - for i in range(class_info.starting_linenum, linenum): - if Search(r'\{\s*$', clean_lines.lines[i]): - end_class_head = i - break - if end_class_head < linenum - 1: - error(filename, linenum, 'whitespace/blank_line', 3, - '"%s:" should be preceded by a blank line' % matched.group(1)) - - -def GetPreviousNonBlankLine(clean_lines, linenum): - """Return the most recent non-blank line and its line number. - - Args: - clean_lines: A CleansedLines instance containing the file contents. - linenum: The number of the line to check. - - Returns: - A tuple with two elements. The first element is the contents of the last - non-blank line before the current line, or the empty string if this is the - first non-blank line. The second is the line number of that line, or -1 - if this is the first non-blank line. - """ - - prevlinenum = linenum - 1 - while prevlinenum >= 0: - prevline = clean_lines.elided[prevlinenum] - if not IsBlankLine(prevline): # if not a blank line... - return (prevline, prevlinenum) - prevlinenum -= 1 - return ('', -1) - - -def CheckBraces(filename, clean_lines, linenum, error): - """Looks for misplaced braces (e.g. at the end of line). - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - line = clean_lines.elided[linenum] # get rid of comments and strings - - if Match(r'\s*{\s*$', line): - # We allow an open brace to start a line in the case where someone - # is using braces in a block to explicitly create a new scope, - # which is commonly used to control the lifetime of - # stack-allocated variables. We don't detect this perfectly: we - # just don't complain if the last non-whitespace character on the - # previous non-blank line is ';', ':', '{', or '}', or if the previous - # line starts a preprocessor block. - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if (not Search(r'[;:}{]\s*$', prevline) and - not Match(r'\s*#', prevline)): - error(filename, linenum, 'whitespace/braces', 4, - '{ should almost always be at the end of the previous line') - - # An else clause should be on the same line as the preceding closing brace. - if Match(r'\s*else\s*', line): - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if Match(r'\s*}\s*$', prevline): - error(filename, linenum, 'whitespace/newline', 4, - 'An else should appear on the same line as the preceding }') - - # If braces come on one side of an else, they should be on both. - # However, we have to worry about "else if" that spans multiple lines! - if Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): - if Search(r'}\s*else if([^{]*)$', line): # could be multi-line if - # find the ( after the if - pos = line.find('else if') - pos = line.find('(', pos) - if pos > 0: - (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) - if endline[endpos:].find('{') == -1: # must be brace after if - error(filename, linenum, 'readability/braces', 5, - 'If an else has a brace on one side, it should have it on both') - else: # common case: else not followed by a multi-line if - error(filename, linenum, 'readability/braces', 5, - 'If an else has a brace on one side, it should have it on both') - - # Likewise, an else should never have the else clause on the same line - if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): - error(filename, linenum, 'whitespace/newline', 4, - 'Else clause should never be on same line as else (use 2 lines)') - - # In the same way, a do/while should never be on one line - if Match(r'\s*do [^\s{]', line): - error(filename, linenum, 'whitespace/newline', 4, - 'do/while clauses should not be on a single line') - - # Braces shouldn't be followed by a ; unless they're defining a struct - # or initializing an array. - # We can't tell in general, but we can for some common cases. - prevlinenum = linenum - while True: - (prevline, prevlinenum) = GetPreviousNonBlankLine(clean_lines, prevlinenum) - if Match(r'\s+{.*}\s*;', line) and not prevline.count(';'): - line = prevline + line - else: - break - if (Search(r'{.*}\s*;', line) and - line.count('{') == line.count('}') and - not Search(r'struct|class|enum|\s*=\s*{', line)): - error(filename, linenum, 'readability/braces', 4, - "You don't need a ; after a }") - - -def CheckEmptyLoopBody(filename, clean_lines, linenum, error): - """Loop for empty loop body with only a single semicolon. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - # Search for loop keywords at the beginning of the line. Because only - # whitespaces are allowed before the keywords, this will also ignore most - # do-while-loops, since those lines should start with closing brace. - line = clean_lines.elided[linenum] - if Match(r'\s*(for|while)\s*\(', line): - # Find the end of the conditional expression - (end_line, end_linenum, end_pos) = CloseExpression( - clean_lines, linenum, line.find('(')) - - # Output warning if what follows the condition expression is a semicolon. - # No warning for all other cases, including whitespace or newline, since we - # have a separate check for semicolons preceded by whitespace. - if end_pos >= 0 and Match(r';', end_line[end_pos:]): - error(filename, end_linenum, 'whitespace/empty_loop_body', 5, - 'Empty loop bodies should use {} or continue') - - -def ReplaceableCheck(operator, macro, line): - """Determine whether a basic CHECK can be replaced with a more specific one. - - For example suggest using CHECK_EQ instead of CHECK(a == b) and - similarly for CHECK_GE, CHECK_GT, CHECK_LE, CHECK_LT, CHECK_NE. - - Args: - operator: The C++ operator used in the CHECK. - macro: The CHECK or EXPECT macro being called. - line: The current source line. - - Returns: - True if the CHECK can be replaced with a more specific one. - """ - - # This matches decimal and hex integers, strings, and chars (in that order). - match_constant = r'([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')' - - # Expression to match two sides of the operator with something that - # looks like a literal, since CHECK(x == iterator) won't compile. - # This means we can't catch all the cases where a more specific - # CHECK is possible, but it's less annoying than dealing with - # extraneous warnings. - match_this = (r'\s*' + macro + r'\((\s*' + - match_constant + r'\s*' + operator + r'[^<>].*|' - r'.*[^<>]' + operator + r'\s*' + match_constant + - r'\s*\))') - - # Don't complain about CHECK(x == NULL) or similar because - # CHECK_EQ(x, NULL) won't compile (requires a cast). - # Also, don't complain about more complex boolean expressions - # involving && or || such as CHECK(a == b || c == d). - return Match(match_this, line) and not Search(r'NULL|&&|\|\|', line) - - -def CheckCheck(filename, clean_lines, linenum, error): - """Checks the use of CHECK and EXPECT macros. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - # Decide the set of replacement macros that should be suggested - raw_lines = clean_lines.raw_lines - current_macro = '' - for macro in _CHECK_MACROS: - if raw_lines[linenum].find(macro) >= 0: - current_macro = macro - break - if not current_macro: - # Don't waste time here if line doesn't contain 'CHECK' or 'EXPECT' - return - - line = clean_lines.elided[linenum] # get rid of comments and strings - - # Encourage replacing plain CHECKs with CHECK_EQ/CHECK_NE/etc. - for operator in ['==', '!=', '>=', '>', '<=', '<']: - if ReplaceableCheck(operator, current_macro, line): - error(filename, linenum, 'readability/check', 2, - 'Consider using %s instead of %s(a %s b)' % ( - _CHECK_REPLACEMENT[current_macro][operator], - current_macro, operator)) - break - - -def CheckAltTokens(filename, clean_lines, linenum, error): - """Check alternative keywords being used in boolean expressions. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Avoid preprocessor lines - if Match(r'^\s*#', line): - return - - # Last ditch effort to avoid multi-line comments. This will not help - # if the comment started before the current line or ended after the - # current line, but it catches most of the false positives. At least, - # it provides a way to workaround this warning for people who use - # multi-line comments in preprocessor macros. - # - # TODO(unknown): remove this once cpplint has better support for - # multi-line comments. - if line.find('/*') >= 0 or line.find('*/') >= 0: - return - - for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): - error(filename, linenum, 'readability/alt_tokens', 2, - 'Use operator %s instead of %s' % ( - _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1))) - - -def GetLineWidth(line): - """Determines the width of the line in column positions. - - Args: - line: A string, which may be a Unicode string. - - Returns: - The width of the line in column positions, accounting for Unicode - combining characters and wide characters. - """ - if isinstance(line, unicode): - width = 0 - for uc in unicodedata.normalize('NFC', line): - if unicodedata.east_asian_width(uc) in ('W', 'F'): - width += 2 - elif not unicodedata.combining(uc): - width += 1 - return width - else: - return len(line) - - -def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, - error): - """Checks rules from the 'C++ style rules' section of cppguide.html. - - Most of these rules are hard to test (naming, comment style), but we - do what we can. In particular we check for 2-space indents, line lengths, - tab usage, spaces inside code, etc. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - file_extension: The extension (without the dot) of the filename. - nesting_state: A _NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - - raw_lines = clean_lines.raw_lines - line = raw_lines[linenum] - - if line.find('\t') != -1: - error(filename, linenum, 'whitespace/tab', 1, - 'Tab found; better to use spaces') - - # One or three blank spaces at the beginning of the line is weird; it's - # hard to reconcile that with 2-space indents. - # NOTE: here are the conditions rob pike used for his tests. Mine aren't - # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces - # if(RLENGTH > 20) complain = 0; - # if(match($0, " +(error|private|public|protected):")) complain = 0; - # if(match(prev, "&& *$")) complain = 0; - # if(match(prev, "\\|\\| *$")) complain = 0; - # if(match(prev, "[\",=><] *$")) complain = 0; - # if(match($0, " <<")) complain = 0; - # if(match(prev, " +for \\(")) complain = 0; - # if(prevodd && match(prevprev, " +for \\(")) complain = 0; - initial_spaces = 0 - cleansed_line = clean_lines.elided[linenum] - while initial_spaces < len(line) and line[initial_spaces] == ' ': - initial_spaces += 1 - if line and line[-1].isspace(): - error(filename, linenum, 'whitespace/end_of_line', 4, - 'Line ends in whitespace. Consider deleting these extra spaces.') - # There are certain situations we allow one space, notably for labels - elif ((initial_spaces == 1 or initial_spaces == 3) and - not Match(r'\s*\w+\s*:\s*$', cleansed_line)): - error(filename, linenum, 'whitespace/indent', 3, - 'Weird number of spaces at line-start. ' - 'Are you using a 2-space indent?') - # Labels should always be indented at least one space. - elif not initial_spaces and line[:2] != '//' and Search(r'[^:]:\s*$', - line): - error(filename, linenum, 'whitespace/labels', 4, - 'Labels should always be indented at least one space. ' - 'If this is a member-initializer list in a constructor or ' - 'the base class list in a class definition, the colon should ' - 'be on the following line.') - - - # Check if the line is a header guard. - is_header_guard = False - if file_extension == 'h': - cppvar = GetHeaderGuardCPPVariable(filename) - if (line.startswith('#ifndef %s' % cppvar) or - line.startswith('#define %s' % cppvar) or - line.startswith('#endif // %s' % cppvar)): - is_header_guard = True - # #include lines and header guards can be long, since there's no clean way to - # split them. - # - # URLs can be long too. It's possible to split these, but it makes them - # harder to cut&paste. - # - # The "$Id:...$" comment may also get very long without it being the - # developers fault. - if (not line.startswith('#include') and not is_header_guard and - not Match(r'^\s*//.*http(s?)://\S*$', line) and - not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): - line_width = GetLineWidth(line) - if line_width > 100: - error(filename, linenum, 'whitespace/line_length', 4, - 'Lines should very rarely be longer than 100 characters') - elif line_width > 80: - error(filename, linenum, 'whitespace/line_length', 2, - 'Lines should be <= 80 characters long') - - if (cleansed_line.count(';') > 1 and - # for loops are allowed two ;'s (and may run over two lines). - cleansed_line.find('for') == -1 and - (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or - GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and - # It's ok to have many commands in a switch case that fits in 1 line - not ((cleansed_line.find('case ') != -1 or - cleansed_line.find('default:') != -1) and - cleansed_line.find('break;') != -1)): - error(filename, linenum, 'whitespace/newline', 0, - 'More than one command on the same line') - - # Some more style checks - CheckBraces(filename, clean_lines, linenum, error) - CheckEmptyLoopBody(filename, clean_lines, linenum, error) - CheckAccess(filename, clean_lines, linenum, nesting_state, error) - CheckSpacing(filename, clean_lines, linenum, nesting_state, error) - CheckCheck(filename, clean_lines, linenum, error) - CheckAltTokens(filename, clean_lines, linenum, error) - classinfo = nesting_state.InnermostClass() - if classinfo: - CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error) - - -_RE_PATTERN_INCLUDE_NEW_STYLE = re.compile(r'#include +"[^/]+\.h"') -_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') -# Matches the first component of a filename delimited by -s and _s. That is: -# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' -_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') - - -def _DropCommonSuffixes(filename): - """Drops common suffixes like _test.cc or -inl.h from filename. - - For example: - >>> _DropCommonSuffixes('foo/foo-inl.h') - 'foo/foo' - >>> _DropCommonSuffixes('foo/bar/foo.cc') - 'foo/bar/foo' - >>> _DropCommonSuffixes('foo/foo_internal.h') - 'foo/foo' - >>> _DropCommonSuffixes('foo/foo_unusualinternal.h') - 'foo/foo_unusualinternal' - - Args: - filename: The input filename. - - Returns: - The filename with the common suffix removed. - """ - for suffix in ('test.cc', 'regtest.cc', 'unittest.cc', - 'inl.h', 'impl.h', 'internal.h'): - if (filename.endswith(suffix) and len(filename) > len(suffix) and - filename[-len(suffix) - 1] in ('-', '_')): - return filename[:-len(suffix) - 1] - return os.path.splitext(filename)[0] - - -def _IsTestFilename(filename): - """Determines if the given filename has a suffix that identifies it as a test. - - Args: - filename: The input filename. - - Returns: - True if 'filename' looks like a test, False otherwise. - """ - if (filename.endswith('_test.cc') or - filename.endswith('_unittest.cc') or - filename.endswith('_regtest.cc')): - return True - else: - return False - - -def _ClassifyInclude(fileinfo, include, is_system): - """Figures out what kind of header 'include' is. - - Args: - fileinfo: The current file cpplint is running over. A FileInfo instance. - include: The path to a #included file. - is_system: True if the #include used <> rather than "". - - Returns: - One of the _XXX_HEADER constants. - - For example: - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True) - _C_SYS_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) - _CPP_SYS_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) - _LIKELY_MY_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), - ... 'bar/foo_other_ext.h', False) - _POSSIBLE_MY_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False) - _OTHER_HEADER - """ - # This is a list of all standard c++ header files, except - # those already checked for above. - is_stl_h = include in _STL_HEADERS - is_cpp_h = is_stl_h or include in _CPP_HEADERS - - if is_system: - if is_cpp_h: - return _CPP_SYS_HEADER - else: - return _C_SYS_HEADER - - # If the target file and the include we're checking share a - # basename when we drop common extensions, and the include - # lives in . , then it's likely to be owned by the target file. - target_dir, target_base = ( - os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) - include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) - if target_base == include_base and ( - include_dir == target_dir or - include_dir == os.path.normpath(target_dir + '/../public')): - return _LIKELY_MY_HEADER - - # If the target and include share some initial basename - # component, it's possible the target is implementing the - # include, so it's allowed to be first, but we'll never - # complain if it's not there. - target_first_component = _RE_FIRST_COMPONENT.match(target_base) - include_first_component = _RE_FIRST_COMPONENT.match(include_base) - if (target_first_component and include_first_component and - target_first_component.group(0) == - include_first_component.group(0)): - return _POSSIBLE_MY_HEADER - - return _OTHER_HEADER - - - -def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): - """Check rules that are applicable to #include lines. - - Strings on #include lines are NOT removed from elided line, to make - certain tasks easier. However, to prevent false positives, checks - applicable to #include lines in CheckLanguage must be put here. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - include_state: An _IncludeState instance in which the headers are inserted. - error: The function to call with any errors found. - """ - fileinfo = FileInfo(filename) - - line = clean_lines.lines[linenum] - - # "include" should use the new style "foo/bar.h" instead of just "bar.h" - if _RE_PATTERN_INCLUDE_NEW_STYLE.search(line): - error(filename, linenum, 'build/include', 4, - 'Include the directory when naming .h files') - - # we shouldn't include a file more than once. actually, there are a - # handful of instances where doing so is okay, but in general it's - # not. - match = _RE_PATTERN_INCLUDE.search(line) - if match: - include = match.group(2) - is_system = (match.group(1) == '<') - if include in include_state: - error(filename, linenum, 'build/include', 4, - '"%s" already included at %s:%s' % - (include, filename, include_state[include])) - else: - include_state[include] = linenum - - # We want to ensure that headers appear in the right order: - # 1) for foo.cc, foo.h (preferred location) - # 2) c system files - # 3) cpp system files - # 4) for foo.cc, foo.h (deprecated location) - # 5) other google headers - # - # We classify each include statement as one of those 5 types - # using a number of techniques. The include_state object keeps - # track of the highest type seen, and complains if we see a - # lower type after that. - error_message = include_state.CheckNextIncludeOrder( - _ClassifyInclude(fileinfo, include, is_system)) - if error_message: - error(filename, linenum, 'build/include_order', 4, - '%s. Should be: %s.h, c system, c++ system, other.' % - (error_message, fileinfo.BaseName())) - if not include_state.IsInAlphabeticalOrder(include): - error(filename, linenum, 'build/include_alpha', 4, - 'Include "%s" not in alphabetical order' % include) - - # Look for any of the stream classes that are part of standard C++. - match = _RE_PATTERN_INCLUDE.match(line) - if match: - include = match.group(2) - if Match(r'(f|ind|io|i|o|parse|pf|stdio|str|)?stream$', include): - # Many unit tests use cout, so we exempt them. - if not _IsTestFilename(filename): - error(filename, linenum, 'readability/streams', 3, - 'Streams are highly discouraged.') - - -def _GetTextInside(text, start_pattern): - """Retrieves all the text between matching open and close parentheses. - - Given a string of lines and a regular expression string, retrieve all the text - following the expression and between opening punctuation symbols like - (, [, or {, and the matching close-punctuation symbol. This properly nested - occurrences of the punctuations, so for the text like - printf(a(), b(c())); - a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. - start_pattern must match string having an open punctuation symbol at the end. - - Args: - text: The lines to extract text. Its comments and strings must be elided. - It can be single line and can span multiple lines. - start_pattern: The regexp string indicating where to start extracting - the text. - Returns: - The extracted text. - None if either the opening string or ending punctuation could not be found. - """ - # TODO(sugawarayu): Audit cpplint.py to see what places could be profitably - # rewritten to use _GetTextInside (and use inferior regexp matching today). - - # Give opening punctuations to get the matching close-punctuations. - matching_punctuation = {'(': ')', '{': '}', '[': ']'} - closing_punctuation = set(matching_punctuation.itervalues()) - - # Find the position to start extracting text. - match = re.search(start_pattern, text, re.M) - if not match: # start_pattern not found in text. - return None - start_position = match.end(0) - - assert start_position > 0, ( - 'start_pattern must ends with an opening punctuation.') - assert text[start_position - 1] in matching_punctuation, ( - 'start_pattern must ends with an opening punctuation.') - # Stack of closing punctuations we expect to have in text after position. - punctuation_stack = [matching_punctuation[text[start_position - 1]]] - position = start_position - while punctuation_stack and position < len(text): - if text[position] == punctuation_stack[-1]: - punctuation_stack.pop() - elif text[position] in closing_punctuation: - # A closing punctuation without matching opening punctuations. - return None - elif text[position] in matching_punctuation: - punctuation_stack.append(matching_punctuation[text[position]]) - position += 1 - if punctuation_stack: - # Opening punctuations left without matching close-punctuations. - return None - # punctuations match. - return text[start_position:position - 1] - - -def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, - error): - """Checks rules from the 'C++ language rules' section of cppguide.html. - - Some of these rules are hard to test (function overloading, using - uint32 inappropriately), but we do the best we can. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - file_extension: The extension (without the dot) of the filename. - include_state: An _IncludeState instance in which the headers are inserted. - error: The function to call with any errors found. - """ - # If the line is empty or consists of entirely a comment, no need to - # check it. - line = clean_lines.elided[linenum] - if not line: - return - - match = _RE_PATTERN_INCLUDE.search(line) - if match: - CheckIncludeLine(filename, clean_lines, linenum, include_state, error) - return - - # Create an extended_line, which is the concatenation of the current and - # next lines, for more effective checking of code that may span more than one - # line. - if linenum + 1 < clean_lines.NumLines(): - extended_line = line + clean_lines.elided[linenum + 1] - else: - extended_line = line - - # Make Windows paths like Unix. - fullname = os.path.abspath(filename).replace('\\', '/') - - # TODO(unknown): figure out if they're using default arguments in fn proto. - - # Check for non-const references in functions. This is tricky because & - # is also used to take the address of something. We allow <> for templates, - # (ignoring whatever is between the braces) and : for classes. - # These are complicated re's. They try to capture the following: - # paren (for fn-prototype start), typename, &, varname. For the const - # version, we're willing for const to be before typename or after - # Don't check the implementation on same line. - fnline = line.split('{', 1)[0] - if (len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) > - len(re.findall(r'\([^()]*\bconst\s+(?:typename\s+)?(?:struct\s+)?' - r'(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) + - len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+\s+const(\s?&|&\s?)[\w]+', - fnline))): - - # We allow non-const references in a few standard places, like functions - # called "swap()" or iostream operators like "<<" or ">>". We also filter - # out for loops, which lint otherwise mistakenly thinks are functions. - if not Search( - r'(for|swap|Swap|operator[<>][<>])\s*\(\s*' - r'(?:(?:typename\s*)?[\w:]|<.*>)+\s*&', - fnline): - error(filename, linenum, 'runtime/references', 2, - 'Is this a non-const reference? ' - 'If so, make const or use a pointer.') - - # Check to see if they're using an conversion function cast. - # I just try to capture the most common basic types, though there are more. - # Parameterless conversion functions, such as bool(), are allowed as they are - # probably a member operator declaration or default constructor. - match = Search( - r'(\bnew\s+)?\b' # Grab 'new' operator, if it's there - r'(int|float|double|bool|char|int32|uint32|int64|uint64)\([^)]', line) - if match: - # gMock methods are defined using some variant of MOCK_METHODx(name, type) - # where type may be float(), int(string), etc. Without context they are - # virtually indistinguishable from int(x) casts. Likewise, gMock's - # MockCallback takes a template parameter of the form return_type(arg_type), - # which looks much like the cast we're trying to detect. - # BEGIN android-added - # The C++ 2011 std::function class template exhibits a similar issue. - # END android-added - if (match.group(1) is None and # If new operator, then this isn't a cast - not (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or - # BEGIN android-changed - # Match(r'^\s*MockCallback<.*>', line))): - Match(r'^\s*MockCallback<.*>', line) or - Match(r'^\s*std::function<.*>', line))): - # END android-changed - # Try a bit harder to catch gmock lines: the only place where - # something looks like an old-style cast is where we declare the - # return type of the mocked method, and the only time when we - # are missing context is if MOCK_METHOD was split across - # multiple lines (for example http://go/hrfhr ), so we only need - # to check the previous line for MOCK_METHOD. - if (linenum == 0 or - not Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(\S+,\s*$', - clean_lines.elided[linenum - 1])): - error(filename, linenum, 'readability/casting', 4, - 'Using deprecated casting style. ' - 'Use static_cast<%s>(...) instead' % - match.group(2)) - - CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], - 'static_cast', - r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) - - # This doesn't catch all cases. Consider (const char * const)"hello". - # - # (char *) "foo" should always be a const_cast (reinterpret_cast won't - # compile). - if CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], - 'const_cast', r'\((char\s?\*+\s?)\)\s*"', error): - pass - else: - # Check pointer casts for other than string constants - CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], - 'reinterpret_cast', r'\((\w+\s?\*+\s?)\)', error) - - # In addition, we look for people taking the address of a cast. This - # is dangerous -- casts can assign to temporaries, so the pointer doesn't - # point where you think. - if Search( - r'(&\([^)]+\)[\w(])|(&(static|dynamic|reinterpret)_cast\b)', line): - error(filename, linenum, 'runtime/casting', 4, - ('Are you taking an address of a cast? ' - 'This is dangerous: could be a temp var. ' - 'Take the address before doing the cast, rather than after')) - - # Check for people declaring static/global STL strings at the top level. - # This is dangerous because the C++ language does not guarantee that - # globals with constructors are initialized before the first access. - match = Match( - r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)', - line) - # Make sure it's not a function. - # Function template specialization looks like: "string foo(...". - # Class template definitions look like: "string Foo::Method(...". - if match and not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)?\s*\(([^"]|$)', - match.group(3)): - error(filename, linenum, 'runtime/string', 4, - 'For a static/global string constant, use a C style string instead: ' - '"%schar %s[]".' % - (match.group(1), match.group(2))) - - # Check that we're not using RTTI outside of testing code. - if Search(r'\bdynamic_cast<', line) and not _IsTestFilename(filename): - error(filename, linenum, 'runtime/rtti', 5, - 'Do not use dynamic_cast<>. If you need to cast within a class ' - "hierarchy, use static_cast<> to upcast. Google doesn't support " - 'RTTI.') - - if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line): - error(filename, linenum, 'runtime/init', 4, - 'You seem to be initializing a member variable with itself.') - - if file_extension == 'h': - # TODO(unknown): check that 1-arg constructors are explicit. - # How to tell it's a constructor? - # (handled in CheckForNonStandardConstructs for now) - # TODO(unknown): check that classes have DISALLOW_EVIL_CONSTRUCTORS - # (level 1 error) - pass - - # Check if people are using the verboten C basic types. The only exception - # we regularly allow is "unsigned short port" for port. - if Search(r'\bshort port\b', line): - if not Search(r'\bunsigned short port\b', line): - error(filename, linenum, 'runtime/int', 4, - 'Use "unsigned short" for ports, not "short"') - else: - match = Search(r'\b(short|long(?! +double)|long long)\b', line) - if match: - error(filename, linenum, 'runtime/int', 4, - 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) - - # When snprintf is used, the second argument shouldn't be a literal. - match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) - if match and match.group(2) != '0': - # If 2nd arg is zero, snprintf is used to calculate size. - error(filename, linenum, 'runtime/printf', 3, - 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' - 'to snprintf.' % (match.group(1), match.group(2))) - - # Check if some verboten C functions are being used. - if Search(r'\bsprintf\b', line): - error(filename, linenum, 'runtime/printf', 5, - 'Never use sprintf. Use snprintf instead.') - match = Search(r'\b(strcpy|strcat)\b', line) - if match: - error(filename, linenum, 'runtime/printf', 4, - 'Almost always, snprintf is better than %s' % match.group(1)) - - if Search(r'\bsscanf\b', line): - error(filename, linenum, 'runtime/printf', 1, - 'sscanf can be ok, but is slow and can overflow buffers.') - - # Check if some verboten operator overloading is going on - # TODO(unknown): catch out-of-line unary operator&: - # class X {}; - # int operator&(const X& x) { return 42; } // unary operator& - # The trick is it's hard to tell apart from binary operator&: - # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& - if Search(r'\boperator\s*&\s*\(\s*\)', line): - error(filename, linenum, 'runtime/operator', 4, - 'Unary operator& is dangerous. Do not use it.') - - # Check for suspicious usage of "if" like - # } if (a == b) { - if Search(r'\}\s*if\s*\(', line): - error(filename, linenum, 'readability/braces', 4, - 'Did you mean "else if"? If not, start a new line for "if".') - - # Check for potential format string bugs like printf(foo). - # We constrain the pattern not to pick things like DocidForPrintf(foo). - # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) - # TODO(sugawarayu): Catch the following case. Need to change the calling - # convention of the whole function to process multiple line to handle it. - # printf( - # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); - printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') - if printf_args: - match = Match(r'([\w.\->()]+)$', printf_args) - if match and match.group(1) != '__VA_ARGS__': - function_name = re.search(r'\b((?:string)?printf)\s*\(', - line, re.I).group(1) - error(filename, linenum, 'runtime/printf', 4, - 'Potential format string bug. Do %s("%%s", %s) instead.' - % (function_name, match.group(1))) - - # Check for potential memset bugs like memset(buf, sizeof(buf), 0). - match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) - if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): - error(filename, linenum, 'runtime/memset', 4, - 'Did you mean "memset(%s, 0, %s)"?' - % (match.group(1), match.group(2))) - - if Search(r'\busing namespace\b', line): - error(filename, linenum, 'build/namespaces', 5, - 'Do not use namespace using-directives. ' - 'Use using-declarations instead.') - - # Detect variable-length arrays. - match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) - if (match and match.group(2) != 'return' and match.group(2) != 'delete' and - match.group(3).find(']') == -1): - # Split the size using space and arithmetic operators as delimiters. - # If any of the resulting tokens are not compile time constants then - # report the error. - tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) - is_const = True - skip_next = False - for tok in tokens: - if skip_next: - skip_next = False - continue - - if Search(r'sizeof\(.+\)', tok): continue - if Search(r'arraysize\(\w+\)', tok): continue - - tok = tok.lstrip('(') - tok = tok.rstrip(')') - if not tok: continue - if Match(r'\d+', tok): continue - if Match(r'0[xX][0-9a-fA-F]+', tok): continue - if Match(r'k[A-Z0-9]\w*', tok): continue - if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue - if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue - # A catch all for tricky sizeof cases, including 'sizeof expression', - # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' - # requires skipping the next token because we split on ' ' and '*'. - if tok.startswith('sizeof'): - skip_next = True - continue - is_const = False - break - if not is_const: - error(filename, linenum, 'runtime/arrays', 1, - 'Do not use variable-length arrays. Use an appropriately named ' - "('k' followed by CamelCase) compile-time constant for the size.") - - # If DISALLOW_EVIL_CONSTRUCTORS, DISALLOW_COPY_AND_ASSIGN, or - # DISALLOW_IMPLICIT_CONSTRUCTORS is present, then it should be the last thing - # in the class declaration. - match = Match( - (r'\s*' - r'(DISALLOW_(EVIL_CONSTRUCTORS|COPY_AND_ASSIGN|IMPLICIT_CONSTRUCTORS))' - r'\(.*\);$'), - line) - if match and linenum + 1 < clean_lines.NumLines(): - next_line = clean_lines.elided[linenum + 1] - # We allow some, but not all, declarations of variables to be present - # in the statement that defines the class. The [\w\*,\s]* fragment of - # the regular expression below allows users to declare instances of - # the class or pointers to instances, but not less common types such - # as function pointers or arrays. It's a tradeoff between allowing - # reasonable code and avoiding trying to parse more C++ using regexps. - if not Search(r'^\s*}[\w\*,\s]*;', next_line): - error(filename, linenum, 'readability/constructors', 3, - match.group(1) + ' should be the last thing in the class') - - # Check for use of unnamed namespaces in header files. Registration - # macros are typically OK, so we allow use of "namespace {" on lines - # that end with backslashes. - if (file_extension == 'h' - and Search(r'\bnamespace\s*{', line) - and line[-1] != '\\'): - error(filename, linenum, 'build/namespaces', 4, - 'Do not use unnamed namespaces in header files. See ' - 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' - ' for more information.') - - -def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern, - error): - """Checks for a C-style cast by looking for the pattern. - - This also handles sizeof(type) warnings, due to similarity of content. - - Args: - filename: The name of the current file. - linenum: The number of the line to check. - line: The line of code to check. - raw_line: The raw line of code to check, with comments. - cast_type: The string for the C++ cast to recommend. This is either - reinterpret_cast, static_cast, or const_cast, depending. - pattern: The regular expression used to find C-style casts. - error: The function to call with any errors found. - - Returns: - True if an error was emitted. - False otherwise. - """ - match = Search(pattern, line) - if not match: - return False - - # e.g., sizeof(int) - sizeof_match = Match(r'.*sizeof\s*$', line[0:match.start(1) - 1]) - if sizeof_match: - error(filename, linenum, 'runtime/sizeof', 1, - 'Using sizeof(type). Use sizeof(varname) instead if possible') - return True - - # operator++(int) and operator--(int) - if (line[0:match.start(1) - 1].endswith(' operator++') or - line[0:match.start(1) - 1].endswith(' operator--')): - return False - - remainder = line[match.end(0):] - - # The close paren is for function pointers as arguments to a function. - # eg, void foo(void (*bar)(int)); - # The semicolon check is a more basic function check; also possibly a - # function pointer typedef. - # eg, void foo(int); or void foo(int) const; - # The equals check is for function pointer assignment. - # eg, void *(*foo)(int) = ... - # The > is for MockCallback<...> ... - # - # Right now, this will only catch cases where there's a single argument, and - # it's unnamed. It should probably be expanded to check for multiple - # arguments with some unnamed. - function_match = Match(r'\s*(\)|=|(const)?\s*(;|\{|throw\(\)|>))', remainder) - if function_match: - if (not function_match.group(3) or - function_match.group(3) == ';' or - ('MockCallback<' not in raw_line and - '/*' not in raw_line)): - error(filename, linenum, 'readability/function', 3, - 'All parameters should be named in a function') - return True - - # At this point, all that should be left is actual casts. - error(filename, linenum, 'readability/casting', 4, - 'Using C-style cast. Use %s<%s>(...) instead' % - (cast_type, match.group(1))) - - return True - - -_HEADERS_CONTAINING_TEMPLATES = ( - ('', ('deque',)), - ('', ('unary_function', 'binary_function', - 'plus', 'minus', 'multiplies', 'divides', 'modulus', - 'negate', - 'equal_to', 'not_equal_to', 'greater', 'less', - 'greater_equal', 'less_equal', - 'logical_and', 'logical_or', 'logical_not', - 'unary_negate', 'not1', 'binary_negate', 'not2', - 'bind1st', 'bind2nd', - 'pointer_to_unary_function', - 'pointer_to_binary_function', - 'ptr_fun', - 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t', - 'mem_fun_ref_t', - 'const_mem_fun_t', 'const_mem_fun1_t', - 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t', - 'mem_fun_ref', - )), - ('', ('numeric_limits',)), - ('', ('list',)), - ('', ('map', 'multimap',)), - ('', ('allocator',)), - ('', ('queue', 'priority_queue',)), - ('', ('set', 'multiset',)), - ('', ('stack',)), - ('', ('char_traits', 'basic_string',)), - ('', ('pair',)), - ('', ('vector',)), - - # gcc extensions. - # Note: std::hash is their hash, ::hash is our hash - ('', ('hash_map', 'hash_multimap',)), - ('', ('hash_set', 'hash_multiset',)), - ('', ('slist',)), - ) - -_RE_PATTERN_STRING = re.compile(r'\bstring\b') - -_re_pattern_algorithm_header = [] -for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap', - 'transform'): - # Match max(..., ...), max(..., ...), but not foo->max, foo.max or - # type::max(). - _re_pattern_algorithm_header.append( - (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), - _template, - '')) - -_re_pattern_templates = [] -for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: - for _template in _templates: - _re_pattern_templates.append( - (re.compile(r'(\<|\b)' + _template + r'\s*\<'), - _template + '<>', - _header)) - - -def FilesBelongToSameModule(filename_cc, filename_h): - """Check if these two filenames belong to the same module. - - The concept of a 'module' here is a as follows: - foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the - same 'module' if they are in the same directory. - some/path/public/xyzzy and some/path/internal/xyzzy are also considered - to belong to the same module here. - - If the filename_cc contains a longer path than the filename_h, for example, - '/absolute/path/to/base/sysinfo.cc', and this file would include - 'base/sysinfo.h', this function also produces the prefix needed to open the - header. This is used by the caller of this function to more robustly open the - header file. We don't have access to the real include paths in this context, - so we need this guesswork here. - - Known bugs: tools/base/bar.cc and base/bar.h belong to the same module - according to this implementation. Because of this, this function gives - some false positives. This should be sufficiently rare in practice. - - Args: - filename_cc: is the path for the .cc file - filename_h: is the path for the header path - - Returns: - Tuple with a bool and a string: - bool: True if filename_cc and filename_h belong to the same module. - string: the additional prefix needed to open the header file. - """ - - if not filename_cc.endswith('.cc'): - return (False, '') - filename_cc = filename_cc[:-len('.cc')] - if filename_cc.endswith('_unittest'): - filename_cc = filename_cc[:-len('_unittest')] - elif filename_cc.endswith('_test'): - filename_cc = filename_cc[:-len('_test')] - filename_cc = filename_cc.replace('/public/', '/') - filename_cc = filename_cc.replace('/internal/', '/') - - if not filename_h.endswith('.h'): - return (False, '') - filename_h = filename_h[:-len('.h')] - if filename_h.endswith('-inl'): - filename_h = filename_h[:-len('-inl')] - filename_h = filename_h.replace('/public/', '/') - filename_h = filename_h.replace('/internal/', '/') - - files_belong_to_same_module = filename_cc.endswith(filename_h) - common_path = '' - if files_belong_to_same_module: - common_path = filename_cc[:-len(filename_h)] - return files_belong_to_same_module, common_path - - -def UpdateIncludeState(filename, include_state, io=codecs): - """Fill up the include_state with new includes found from the file. - - Args: - filename: the name of the header to read. - include_state: an _IncludeState instance in which the headers are inserted. - io: The io factory to use to read the file. Provided for testability. - - Returns: - True if a header was succesfully added. False otherwise. - """ - headerfile = None - try: - headerfile = io.open(filename, 'r', 'utf8', 'replace') - except IOError: - return False - linenum = 0 - for line in headerfile: - linenum += 1 - clean_line = CleanseComments(line) - match = _RE_PATTERN_INCLUDE.search(clean_line) - if match: - include = match.group(2) - # The value formatting is cute, but not really used right now. - # What matters here is that the key is in include_state. - include_state.setdefault(include, '%s:%d' % (filename, linenum)) - return True - - -def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, - io=codecs): - """Reports for missing stl includes. - - This function will output warnings to make sure you are including the headers - necessary for the stl containers and functions that you use. We only give one - reason to include a header. For example, if you use both equal_to<> and - less<> in a .h file, only one (the latter in the file) of these will be - reported as a reason to include the . - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - include_state: An _IncludeState instance. - error: The function to call with any errors found. - io: The IO factory to use to read the header file. Provided for unittest - injection. - """ - required = {} # A map of header name to linenumber and the template entity. - # Example of required: { '': (1219, 'less<>') } - - for linenum in xrange(clean_lines.NumLines()): - line = clean_lines.elided[linenum] - if not line or line[0] == '#': - continue - - # String is special -- it is a non-templatized type in STL. - matched = _RE_PATTERN_STRING.search(line) - if matched: - # Don't warn about strings in non-STL namespaces: - # (We check only the first match per line; good enough.) - prefix = line[:matched.start()] - if prefix.endswith('std::') or not prefix.endswith('::'): - required[''] = (linenum, 'string') - - for pattern, template, header in _re_pattern_algorithm_header: - if pattern.search(line): - required[header] = (linenum, template) - - # The following function is just a speed up, no semantics are changed. - if not '<' in line: # Reduces the cpu time usage by skipping lines. - continue - - for pattern, template, header in _re_pattern_templates: - if pattern.search(line): - required[header] = (linenum, template) - - # The policy is that if you #include something in foo.h you don't need to - # include it again in foo.cc. Here, we will look at possible includes. - # Let's copy the include_state so it is only messed up within this function. - include_state = include_state.copy() - - # Did we find the header for this file (if any) and succesfully load it? - header_found = False - - # Use the absolute path so that matching works properly. - abs_filename = FileInfo(filename).FullName() - - # For Emacs's flymake. - # If cpplint is invoked from Emacs's flymake, a temporary file is generated - # by flymake and that file name might end with '_flymake.cc'. In that case, - # restore original file name here so that the corresponding header file can be - # found. - # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' - # instead of 'foo_flymake.h' - abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) - - # include_state is modified during iteration, so we iterate over a copy of - # the keys. - header_keys = include_state.keys() - for header in header_keys: - (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) - fullpath = common_path + header - if same_module and UpdateIncludeState(fullpath, include_state, io): - header_found = True - - # If we can't find the header file for a .cc, assume it's because we don't - # know where to look. In that case we'll give up as we're not sure they - # didn't include it in the .h file. - # TODO(unknown): Do a better job of finding .h files so we are confident that - # not having the .h file means there isn't one. - if filename.endswith('.cc') and not header_found: - return - - # All the lines have been processed, report the errors found. - for required_header_unstripped in required: - template = required[required_header_unstripped][1] - if required_header_unstripped.strip('<>"') not in include_state: - error(filename, required[required_header_unstripped][0], - 'build/include_what_you_use', 4, - 'Add #include ' + required_header_unstripped + ' for ' + template) - - -_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') - - -def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error): - """Check that make_pair's template arguments are deduced. - - G++ 4.6 in C++0x mode fails badly if make_pair's template arguments are - specified explicitly, and such use isn't intended in any case. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - raw = clean_lines.raw_lines - line = raw[linenum] - match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line) - if match: - error(filename, linenum, 'build/explicit_make_pair', - 4, # 4 = high confidence - 'For C++11-compatibility, omit template arguments from make_pair' - ' OR use pair directly OR if appropriate, construct a pair directly') - - -def ProcessLine(filename, file_extension, clean_lines, line, - include_state, function_state, nesting_state, error, - extra_check_functions=[]): - """Processes a single line in the file. - - Args: - filename: Filename of the file that is being processed. - file_extension: The extension (dot not included) of the file. - clean_lines: An array of strings, each representing a line of the file, - with comments stripped. - line: Number of line being processed. - include_state: An _IncludeState instance in which the headers are inserted. - function_state: A _FunctionState instance which counts function lines, etc. - nesting_state: A _NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: A callable to which errors are reported, which takes 4 arguments: - filename, line number, error level, and message - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - raw_lines = clean_lines.raw_lines - ParseNolintSuppressions(filename, raw_lines[line], line, error) - nesting_state.Update(filename, clean_lines, line, error) - if nesting_state.stack and nesting_state.stack[-1].inline_asm != _NO_ASM: - return - CheckForFunctionLengths(filename, clean_lines, line, function_state, error) - CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) - CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error) - CheckLanguage(filename, clean_lines, line, file_extension, include_state, - error) - CheckForNonStandardConstructs(filename, clean_lines, line, - nesting_state, error) - CheckPosixThreading(filename, clean_lines, line, error) - CheckInvalidIncrement(filename, clean_lines, line, error) - CheckMakePairUsesDeduction(filename, clean_lines, line, error) - for check_fn in extra_check_functions: - check_fn(filename, clean_lines, line, error) - -def ProcessFileData(filename, file_extension, lines, error, - extra_check_functions=[]): - """Performs lint checks and reports any errors to the given error function. - - Args: - filename: Filename of the file that is being processed. - file_extension: The extension (dot not included) of the file. - lines: An array of strings, each representing a line of the file, with the - last element being empty if the file is terminated with a newline. - error: A callable to which errors are reported, which takes 4 arguments: - filename, line number, error level, and message - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - lines = (['// marker so line numbers and indices both start at 1'] + lines + - ['// marker so line numbers end in a known way']) - - include_state = _IncludeState() - function_state = _FunctionState() - nesting_state = _NestingState() - - ResetNolintSuppressions() - - CheckForCopyright(filename, lines, error) - - if file_extension == 'h': - CheckForHeaderGuard(filename, lines, error) - - RemoveMultiLineComments(filename, lines, error) - clean_lines = CleansedLines(lines) - for line in xrange(clean_lines.NumLines()): - ProcessLine(filename, file_extension, clean_lines, line, - include_state, function_state, nesting_state, error, - extra_check_functions) - nesting_state.CheckClassFinished(filename, error) - - CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) - - # We check here rather than inside ProcessLine so that we see raw - # lines rather than "cleaned" lines. - CheckForUnicodeReplacementCharacters(filename, lines, error) - - CheckForNewlineAtEOF(filename, lines, error) - -def ProcessFile(filename, vlevel, extra_check_functions=[]): - """Does google-lint on a single file. - - Args: - filename: The name of the file to parse. - - vlevel: The level of errors to report. Every error of confidence - >= verbose_level will be reported. 0 is a good default. - - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - - _SetVerboseLevel(vlevel) -# BEGIN android-added - old_errors = _cpplint_state.error_count -# END android-added - - try: - # Support the UNIX convention of using "-" for stdin. Note that - # we are not opening the file with universal newline support - # (which codecs doesn't support anyway), so the resulting lines do - # contain trailing '\r' characters if we are reading a file that - # has CRLF endings. - # If after the split a trailing '\r' is present, it is removed - # below. If it is not expected to be present (i.e. os.linesep != - # '\r\n' as in Windows), a warning is issued below if this file - # is processed. - - if filename == '-': - lines = codecs.StreamReaderWriter(sys.stdin, - codecs.getreader('utf8'), - codecs.getwriter('utf8'), - 'replace').read().split('\n') - else: - lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') - - carriage_return_found = False - # Remove trailing '\r'. - for linenum in range(len(lines)): - if lines[linenum].endswith('\r'): - lines[linenum] = lines[linenum].rstrip('\r') - carriage_return_found = True - - except IOError: - sys.stderr.write( - "Skipping input '%s': Can't open for reading\n" % filename) - return - - # Note, if no dot is found, this will give the entire filename as the ext. - file_extension = filename[filename.rfind('.') + 1:] - - # When reading from stdin, the extension is unknown, so no cpplint tests - # should rely on the extension. - if (filename != '-' and file_extension != 'cc' and file_extension != 'h' - and file_extension != 'cpp'): - sys.stderr.write('Ignoring %s; not a .cc or .h file\n' % filename) - else: - ProcessFileData(filename, file_extension, lines, Error, - extra_check_functions) - if carriage_return_found and os.linesep != '\r\n': - # Use 0 for linenum since outputting only one error for potentially - # several lines. - Error(filename, 0, 'whitespace/newline', 1, - 'One or more unexpected \\r (^M) found;' - 'better to use only a \\n') - -# BEGIN android-changed - # sys.stderr.write('Done processing %s\n' % filename) - if not _cpplint_state.quiet or old_errors != _cpplint_state.error_count: - sys.stderr.write('Done processing %s\n' % filename) -# END android-changed - -def PrintUsage(message): - """Prints a brief usage string and exits, optionally with an error message. - - Args: - message: The optional error message. - """ - sys.stderr.write(_USAGE) - if message: - sys.exit('\nFATAL ERROR: ' + message) - else: - sys.exit(1) - - -def PrintCategories(): - """Prints a list of all the error-categories used by error messages. - - These are the categories used to filter messages via --filter. - """ - sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) - sys.exit(0) - - -def ParseArguments(args): - """Parses the command line arguments. - - This may set the output format and verbosity level as side-effects. - - Args: - args: The command line arguments: - - Returns: - The list of filenames to lint. - """ - try: - (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', - 'stdout', # TODO(enh): added --stdout - # BEGIN android-added - 'quiet', - # END android-added - 'counting=', - 'filter=', - 'root=']) - except getopt.GetoptError: - PrintUsage('Invalid arguments.') - - verbosity = _VerboseLevel() - output_format = _OutputFormat() - output_stream = sys.stderr # TODO(enh): added --stdout - filters = '' - # BEGIN android-added - quiet = _Quiet() - # END android-added - counting_style = '' - - for (opt, val) in opts: - if opt == '--help': - PrintUsage(None) - elif opt == '--stdout': # TODO(enh): added --stdout - output_stream = sys.stdout # TODO(enh): added --stdout - # BEGIN android-added - elif opt == '--quiet': - quiet = True - # END android-added - elif opt == '--output': - if not val in ('emacs', 'vs7', 'eclipse'): - PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') - output_format = val - elif opt == '--verbose': - verbosity = int(val) - elif opt == '--filter': - filters = val - if not filters: - PrintCategories() - elif opt == '--counting': - if val not in ('total', 'toplevel', 'detailed'): - PrintUsage('Valid counting options are total, toplevel, and detailed') - counting_style = val - elif opt == '--root': - global _root - _root = val - - if not filenames: - PrintUsage('No files were specified.') - - _SetOutputFormat(output_format) - _SetVerboseLevel(verbosity) - _SetFilters(filters) - _SetCountingStyle(counting_style) - # BEGIN android-added - _SetQuiet(quiet) - # END android-added - sys.stderr = output_stream # TODO(enh): added --stdout - - return filenames - - -def main(): - filenames = ParseArguments(sys.argv[1:]) - - # Change stderr to write with replacement characters so we don't die - # if we try to print something containing non-ASCII characters. - sys.stderr = codecs.StreamReaderWriter(sys.stderr, - codecs.getreader('utf8'), - codecs.getwriter('utf8'), - 'replace') - - _cpplint_state.ResetErrorCounts() - for filename in filenames: - ProcessFile(filename, _cpplint_state.verbose_level) - # BEGIN android-changed - # _cpplint_state.PrintErrorCounts() - if not _cpplint_state.quiet or _cpplint_state.error_count > 0: - _cpplint_state.PrintErrorCounts() - # END android-changed - - sys.exit(_cpplint_state.error_count > 0) - - -if __name__ == '__main__': - main() diff --git a/tools/cpplint_presubmit.py b/tools/cpplint_presubmit.py deleted file mode 100755 index b42a6913dc..0000000000 --- a/tools/cpplint_presubmit.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/python3 -# -# 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. - -# TODO We should unify this with build/Android.cpplint.mk. - -import os -import pathlib -import subprocess -import sys - -IGNORED_FILES = {"runtime/elf.h", "openjdkjvmti/include/jvmti.h"} - -INTERESTING_SUFFIXES = {".h", ".cc"} - -CPPLINT_FLAGS = [ - '--filter=-whitespace/line_length,-build/include,-readability/function,-readability/streams,-readability/todo,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn,-runtime/printf', - '--quiet', -] - -def is_interesting(f): - """ - Returns true if this is a file we want to run through cpplint before uploading. False otherwise. - """ - path = pathlib.Path(f) - return f not in IGNORED_FILES and path.suffix in INTERESTING_SUFFIXES and path.exists() - -def get_changed_files(commit): - """ - Gets the files changed in the given commit. - """ - return subprocess.check_output( - ["git", 'diff-tree', '--no-commit-id', '--name-only', '-r', commit], - stderr=subprocess.STDOUT, - universal_newlines=True).split() - -def run_cpplint(files): - """ - Runs cpplint on the given files. - """ - if len(files) == 0: - return - sys.exit(subprocess.call(['tools/cpplint.py'] + CPPLINT_FLAGS + files)) - -def main(): - if 'PREUPLOAD_COMMIT' in os.environ: - commit = os.environ['PREUPLOAD_COMMIT'] - else: - print("WARNING: Not running as a pre-upload hook. Assuming commit to check = 'HEAD'") - commit = "HEAD" - files_to_check = [f for f in get_changed_files(commit) if is_interesting(f)] - run_cpplint(files_to_check) - -if __name__ == '__main__': - main() -- GitLab From 8c2b929696cac235e8fd8bf4cae0ca751603b570 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Thu, 9 Nov 2017 13:21:01 -0800 Subject: [PATCH 044/226] Add JVMTI DDMS extension method and event. Add a new jvmti extension method 'com.android.art.internal.ddm.process_chunk' that can be used to request that the system process a DDMS chunk with a given type and data payload. It returns the processed chunk type and data. Agents can use this to interact with DDMS. Also add a new jvmti extension event 'com.android.art.internal.ddm.publish_chunk' that will be called whenever the system wishes to send an unrequested chunk of data to be processed. This is triggered by code executing 'DdmServer#sendChunk' or by other internal mechanisms. Both of these extensions are provided mainly to aid in the maintenence of backwards compatibility with existing DDMS applications. Generally agents should prefer to use the normal JVMTI events and controls over interpreting DDMS data or calling DDMS functions. Bug: 62821960 Test: ./test.py --host -j50 Test: ./art/tools/run-jdwp-tests.sh --mode=host \ --test org.apache.harmony.jpda.tests.jdwp.DDM.DDMTest Change-Id: I39f22d3d096d12b59713ec7b8b0c08d0d68ff422 --- openjdkjvmti/Android.bp | 1 + openjdkjvmti/OpenjdkJvmTi.cc | 19 +- openjdkjvmti/art_jvmti.h | 2 +- openjdkjvmti/events-inl.h | 11 +- openjdkjvmti/events.cc | 75 +++++++ openjdkjvmti/events.h | 30 ++- openjdkjvmti/ti_ddms.cc | 87 ++++++++ openjdkjvmti/ti_ddms.h | 53 +++++ openjdkjvmti/ti_extension.cc | 155 ++++++++++++- openjdkjvmti/ti_extension.h | 5 +- runtime/debugger.cc | 151 ++++++++----- runtime/debugger.h | 15 ++ ...rg_apache_harmony_dalvik_ddmc_DdmServer.cc | 5 +- runtime/runtime_callbacks.cc | 14 ++ runtime/runtime_callbacks.h | 17 ++ test/1940-ddms-ext/ddm_ext.cc | 207 ++++++++++++++++++ test/1940-ddms-ext/expected.txt | 7 + test/1940-ddms-ext/info.txt | 1 + test/1940-ddms-ext/run | 17 ++ test/1940-ddms-ext/src-art/art/Test1940.java | 101 +++++++++ test/1940-ddms-ext/src/Main.java | 21 ++ test/1940-ddms-ext/src/art/Test1940.java | 23 ++ test/Android.bp | 6 +- tools/libjdwp_art_failures.txt | 6 + tools/libjdwp_oj_art_failures.txt | 6 + 25 files changed, 957 insertions(+), 78 deletions(-) create mode 100644 openjdkjvmti/ti_ddms.cc create mode 100644 openjdkjvmti/ti_ddms.h create mode 100644 test/1940-ddms-ext/ddm_ext.cc create mode 100644 test/1940-ddms-ext/expected.txt create mode 100644 test/1940-ddms-ext/info.txt create mode 100755 test/1940-ddms-ext/run create mode 100644 test/1940-ddms-ext/src-art/art/Test1940.java create mode 100644 test/1940-ddms-ext/src/Main.java create mode 100644 test/1940-ddms-ext/src/art/Test1940.java diff --git a/openjdkjvmti/Android.bp b/openjdkjvmti/Android.bp index aef4dfc201..9ba7068176 100644 --- a/openjdkjvmti/Android.bp +++ b/openjdkjvmti/Android.bp @@ -34,6 +34,7 @@ cc_defaults { "ti_class.cc", "ti_class_definition.cc", "ti_class_loader.cc", + "ti_ddms.cc", "ti_dump.cc", "ti_extension.cc", "ti_field.cc", diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc index 481492402b..62f723dec1 100644 --- a/openjdkjvmti/OpenjdkJvmTi.cc +++ b/openjdkjvmti/OpenjdkJvmTi.cc @@ -1014,14 +1014,21 @@ class JvmtiFunctions { return ERR(NONE); } - std::unique_ptr tmp(new jvmtiEventCallbacks()); - memset(tmp.get(), 0, sizeof(jvmtiEventCallbacks)); + // Lock the event_info_mutex_ while we replace the callbacks. + ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(env); + art::WriterMutexLock lk(art::Thread::Current(), art_env->event_info_mutex_); + std::unique_ptr tmp(new ArtJvmtiEventCallbacks()); + // Copy over the extension events. + tmp->CopyExtensionsFrom(art_env->event_callbacks.get()); + // Never overwrite the extension events. size_t copy_size = std::min(sizeof(jvmtiEventCallbacks), static_cast(size_of_callbacks)); copy_size = art::RoundDown(copy_size, sizeof(void*)); + // Copy non-extension events. memcpy(tmp.get(), callbacks, copy_size); - ArtJvmTiEnv::AsArtJvmTiEnv(env)->event_callbacks = std::move(tmp); + // replace the event table. + art_env->event_callbacks = std::move(tmp); return ERR(NONE); } @@ -1077,8 +1084,10 @@ class JvmtiFunctions { jint extension_event_index, jvmtiExtensionEvent callback) { ENSURE_VALID_ENV(env); - // We do not have any extension events, so any call is illegal. - return ExtensionUtil::SetExtensionEventCallback(env, extension_event_index, callback); + return ExtensionUtil::SetExtensionEventCallback(env, + extension_event_index, + callback, + &gEventHandler); } static jvmtiError GetPotentialCapabilities(jvmtiEnv* env, jvmtiCapabilities* capabilities_ptr) { diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h index 97801e09aa..682b82b5cd 100644 --- a/openjdkjvmti/art_jvmti.h +++ b/openjdkjvmti/art_jvmti.h @@ -68,7 +68,7 @@ struct ArtJvmTiEnv : public jvmtiEnv { jvmtiCapabilities capabilities; EventMasks event_masks; - std::unique_ptr event_callbacks; + std::unique_ptr event_callbacks; // Tagging is specific to the jvmtiEnv. std::unique_ptr object_tag_table; diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h index 7f77f90862..5344e0fbde 100644 --- a/openjdkjvmti/events-inl.h +++ b/openjdkjvmti/events-inl.h @@ -80,16 +80,17 @@ namespace impl { fn(GarbageCollectionStart, ArtJvmtiEvent::kGarbageCollectionStart) \ fn(GarbageCollectionFinish, ArtJvmtiEvent::kGarbageCollectionFinish) \ fn(ObjectFree, ArtJvmtiEvent::kObjectFree) \ - fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc) + fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc) \ + fn(DdmPublishChunk, ArtJvmtiEvent::kDdmPublishChunk) template struct EventFnType { }; -#define EVENT_FN_TYPE(name, enum_name) \ -template <> \ -struct EventFnType { \ - using type = decltype(jvmtiEventCallbacks().name); \ +#define EVENT_FN_TYPE(name, enum_name) \ +template <> \ +struct EventFnType { \ + using type = decltype(ArtJvmtiEventCallbacks().name); \ }; FORALL_EVENT_TYPES(EVENT_FN_TYPE) diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index 6a64441a4a..d1d606de48 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -60,6 +60,45 @@ namespace openjdkjvmti { +void ArtJvmtiEventCallbacks::CopyExtensionsFrom(const ArtJvmtiEventCallbacks* cb) { + if (art::kIsDebugBuild) { + ArtJvmtiEventCallbacks clean; + DCHECK_EQ(memcmp(&clean, this, sizeof(clean)), 0) + << "CopyExtensionsFrom called with initialized eventsCallbacks!"; + } + if (cb != nullptr) { + memcpy(this, cb, sizeof(*this)); + } else { + memset(this, 0, sizeof(*this)); + } +} + +jvmtiError ArtJvmtiEventCallbacks::Set(jint index, jvmtiExtensionEvent cb) { + switch (index) { + case static_cast(ArtJvmtiEvent::kDdmPublishChunk): + DdmPublishChunk = reinterpret_cast(cb); + return OK; + default: + return ERR(ILLEGAL_ARGUMENT); + } +} + + +bool IsExtensionEvent(jint e) { + return e >= static_cast(ArtJvmtiEvent::kMinEventTypeVal) && + e <= static_cast(ArtJvmtiEvent::kMaxEventTypeVal) && + IsExtensionEvent(static_cast(e)); +} + +bool IsExtensionEvent(ArtJvmtiEvent e) { + switch (e) { + case ArtJvmtiEvent::kDdmPublishChunk: + return true; + default: + return false; + } +} + bool EventMasks::IsEnabledAnywhere(ArtJvmtiEvent event) { return global_event_mask.Test(event) || unioned_thread_event_mask.Test(event); } @@ -213,6 +252,38 @@ static void RunEventCallback(EventHandler* handler, args...); } +static void SetupDdmTracking(art::DdmCallback* listener, bool enable) { + art::ScopedObjectAccess soa(art::Thread::Current()); + if (enable) { + art::Runtime::Current()->GetRuntimeCallbacks()->AddDdmCallback(listener); + } else { + art::Runtime::Current()->GetRuntimeCallbacks()->RemoveDdmCallback(listener); + } +} + +class JvmtiDdmChunkListener : public art::DdmCallback { + public: + explicit JvmtiDdmChunkListener(EventHandler* handler) : handler_(handler) {} + + void DdmPublishChunk(uint32_t type, const art::ArrayRef& data) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kDdmPublishChunk)) { + art::Thread* self = art::Thread::Current(); + handler_->DispatchEvent( + self, + static_cast(self->GetJniEnv()), + static_cast(type), + static_cast(data.size()), + reinterpret_cast(data.data())); + } + } + + private: + EventHandler* handler_; + + DISALLOW_COPY_AND_ASSIGN(JvmtiDdmChunkListener); +}; + class JvmtiAllocationListener : public art::gc::AllocationListener { public: explicit JvmtiAllocationListener(EventHandler* handler) : handler_(handler) {} @@ -924,6 +995,9 @@ bool EventHandler::OtherMonitorEventsEnabledAnywhere(ArtJvmtiEvent event) { // Handle special work for the given event type, if necessary. void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { switch (event) { + case ArtJvmtiEvent::kDdmPublishChunk: + SetupDdmTracking(ddm_listener_.get(), enable); + return; case ArtJvmtiEvent::kVmObjectAlloc: SetupObjectAllocationTracking(alloc_listener_.get(), enable); return; @@ -1104,6 +1178,7 @@ void EventHandler::Shutdown() { EventHandler::EventHandler() { alloc_listener_.reset(new JvmtiAllocationListener(this)); + ddm_listener_.reset(new JvmtiDdmChunkListener(this)); gc_pause_listener_.reset(new JvmtiGcPauseListener(this)); method_trace_listener_.reset(new JvmtiMethodTraceListener(this)); monitor_listener_.reset(new JvmtiMonitorListener(this)); diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h index aed24e59f3..a99ed7b212 100644 --- a/openjdkjvmti/events.h +++ b/openjdkjvmti/events.h @@ -28,13 +28,14 @@ namespace openjdkjvmti { struct ArtJvmTiEnv; class JvmtiAllocationListener; +class JvmtiDdmChunkListener; class JvmtiGcPauseListener; class JvmtiMethodTraceListener; class JvmtiMonitorListener; // an enum for ArtEvents. This differs from the JVMTI events only in that we distinguish between // retransformation capable and incapable loading -enum class ArtJvmtiEvent { +enum class ArtJvmtiEvent : jint { kMinEventTypeVal = JVMTI_MIN_EVENT_TYPE_VAL, kVmInit = JVMTI_EVENT_VM_INIT, kVmDeath = JVMTI_EVENT_VM_DEATH, @@ -68,9 +69,33 @@ enum class ArtJvmtiEvent { kObjectFree = JVMTI_EVENT_OBJECT_FREE, kVmObjectAlloc = JVMTI_EVENT_VM_OBJECT_ALLOC, kClassFileLoadHookRetransformable = JVMTI_MAX_EVENT_TYPE_VAL + 1, - kMaxEventTypeVal = kClassFileLoadHookRetransformable, + kDdmPublishChunk = JVMTI_MAX_EVENT_TYPE_VAL + 2, + kMaxEventTypeVal = kDdmPublishChunk, }; +using ArtJvmtiEventDdmPublishChunk = void (*)(jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jint data_type, + jint data_len, + const jbyte* data); + +struct ArtJvmtiEventCallbacks : jvmtiEventCallbacks { + ArtJvmtiEventCallbacks() : DdmPublishChunk(nullptr) { + memset(this, 0, sizeof(jvmtiEventCallbacks)); + } + + // Copies extension functions from other callback struct if it exists. There must not have been + // any modifications to this struct when it is called. + void CopyExtensionsFrom(const ArtJvmtiEventCallbacks* cb); + + jvmtiError Set(jint index, jvmtiExtensionEvent cb); + + ArtJvmtiEventDdmPublishChunk DdmPublishChunk; +}; + +bool IsExtensionEvent(jint e); +bool IsExtensionEvent(ArtJvmtiEvent e); + // Convert a jvmtiEvent into a ArtJvmtiEvent ALWAYS_INLINE static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e); @@ -245,6 +270,7 @@ class EventHandler { EventMask global_mask; std::unique_ptr alloc_listener_; + std::unique_ptr ddm_listener_; std::unique_ptr gc_pause_listener_; std::unique_ptr method_trace_listener_; std::unique_ptr monitor_listener_; diff --git a/openjdkjvmti/ti_ddms.cc b/openjdkjvmti/ti_ddms.cc new file mode 100644 index 0000000000..be7e65d638 --- /dev/null +++ b/openjdkjvmti/ti_ddms.cc @@ -0,0 +1,87 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include + +#include "ti_ddms.h" + +#include + +#include "art_jvmti.h" +#include "base/array_ref.h" +#include "debugger.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-inl.h" + +namespace openjdkjvmti { + +jvmtiError DDMSUtil::HandleChunk(jvmtiEnv* env, + jint type_in, + jint length_in, + const jbyte* data_in, + /*out*/jint* type_out, + /*out*/jint* data_length_out, + /*out*/jbyte** data_out) { + constexpr uint32_t kDdmHeaderSize = sizeof(uint32_t) * 2; + if (env == nullptr || data_in == nullptr || data_out == nullptr || data_length_out == nullptr) { + return ERR(NULL_POINTER); + } else if (length_in < static_cast(kDdmHeaderSize)) { + // need to get type and length at least. + return ERR(ILLEGAL_ARGUMENT); + } + + art::Thread* self = art::Thread::Current(); + art::ScopedThreadStateChange(self, art::ThreadState::kNative); + + art::ArrayRef data_arr(data_in, length_in); + std::vector out_data; + if (!art::Dbg::DdmHandleChunk(self->GetJniEnv(), + type_in, + data_arr, + /*out*/reinterpret_cast(type_out), + /*out*/&out_data)) { + LOG(WARNING) << "Something went wrong with handling the ddm chunk."; + return ERR(INTERNAL); + } else { + jvmtiError error = OK; + JvmtiUniquePtr ret = AllocJvmtiUniquePtr(env, out_data.size(), &error); + if (error != OK) { + return error; + } + memcpy(ret.get(), out_data.data(), out_data.size()); + *data_out = ret.release(); + *data_length_out = static_cast(out_data.size()); + return OK; + } +} + +} // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_ddms.h b/openjdkjvmti/ti_ddms.h new file mode 100644 index 0000000000..1ea7548607 --- /dev/null +++ b/openjdkjvmti/ti_ddms.h @@ -0,0 +1,53 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_OPENJDKJVMTI_TI_DDMS_H_ +#define ART_OPENJDKJVMTI_TI_DDMS_H_ + +#include "jni.h" +#include "jvmti.h" + +namespace openjdkjvmti { + +class DDMSUtil { + public: + static jvmtiError HandleChunk(jvmtiEnv* env, + jint type_in, + jint length_in, + const jbyte* data_in, + /*out*/ jint* type_out, + /*out*/ jint* data_length_out, + /*out*/ jbyte** data_out); +}; + +} // namespace openjdkjvmti + +#endif // ART_OPENJDKJVMTI_TI_DDMS_H_ diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc index fbed9640a0..d3e0912190 100644 --- a/openjdkjvmti/ti_extension.cc +++ b/openjdkjvmti/ti_extension.cc @@ -34,8 +34,11 @@ #include "ti_extension.h" #include "art_jvmti.h" +#include "events.h" #include "ti_allocator.h" +#include "ti_ddms.h" #include "ti_heap.h" +#include "thread-inl.h" namespace openjdkjvmti { @@ -202,6 +205,27 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, return error; } + // DDMS extension + error = add_extension( + reinterpret_cast(DDMSUtil::HandleChunk), + "com.android.art.internal.ddm.process_chunk", + "Handles a single ddms chunk request and returns a response. The reply data is in the ddms" + " chunk format. It returns the processed chunk. This is provided for backwards compatibility" + " reasons only. Agents should avoid making use of this extension when possible and instead" + " use the other JVMTI entrypoints explicitly.", + { // NOLINT[whitespace/braces] [4] + { "type_in", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, + { "length_in", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, + { "data_in", JVMTI_KIND_IN_BUF, JVMTI_TYPE_JBYTE, false }, + { "type_out", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false }, + { "data_len_out", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false }, + { "data_out", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_JBYTE, false } + }, + { ERR(NULL_POINTER), ERR(ILLEGAL_ARGUMENT), ERR(OUT_OF_MEMORY) }); + if (error != ERR(NONE)) { + return error; + } + // Copy into output buffer. *extension_count_ptr = ext_vector.size(); @@ -230,20 +254,133 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, } -jvmtiError ExtensionUtil::GetExtensionEvents(jvmtiEnv* env ATTRIBUTE_UNUSED, +jvmtiError ExtensionUtil::GetExtensionEvents(jvmtiEnv* env, jint* extension_count_ptr, jvmtiExtensionEventInfo** extensions) { - // We don't have any extension events at the moment. - *extension_count_ptr = 0; - *extensions = nullptr; + std::vector ext_vector; + + // Holders for allocated values. + std::vector> char_buffers; + std::vector> param_buffers; + + auto add_extension = [&](ArtJvmtiEvent extension_event_index, + const char* id, + const char* short_description, + const std::vector& params) { + DCHECK(IsExtensionEvent(extension_event_index)); + jvmtiExtensionEventInfo event_info; + jvmtiError error; + + event_info.extension_event_index = static_cast(extension_event_index); + + JvmtiUniquePtr id_ptr = CopyString(env, id, &error); + if (id_ptr == nullptr) { + return error; + } + event_info.id = id_ptr.get(); + char_buffers.push_back(std::move(id_ptr)); + + JvmtiUniquePtr descr = CopyString(env, short_description, &error); + if (descr == nullptr) { + return error; + } + event_info.short_description = descr.get(); + char_buffers.push_back(std::move(descr)); + + event_info.param_count = params.size(); + if (!params.empty()) { + JvmtiUniquePtr params_ptr = + AllocJvmtiUniquePtr(env, params.size(), &error); + if (params_ptr == nullptr) { + return error; + } + event_info.params = params_ptr.get(); + param_buffers.push_back(std::move(params_ptr)); + + for (jint i = 0; i != event_info.param_count; ++i) { + event_info.params[i] = params[i].ToParamInfo(env, &char_buffers, &error); + if (error != OK) { + return error; + } + } + } else { + event_info.params = nullptr; + } + + ext_vector.push_back(event_info); + + return ERR(NONE); + }; + + jvmtiError error; + error = add_extension( + ArtJvmtiEvent::kDdmPublishChunk, + "com.android.art.internal.ddm.publish_chunk", + "Called when there is new ddms information that the agent or other clients can use. The" + " agent is given the 'type' of the ddms chunk and a 'data_size' byte-buffer in 'data'." + " The 'data' pointer is only valid for the duration of the publish_chunk event. The agent" + " is responsible for interpreting the information present in the 'data' buffer. This is" + " provided for backwards-compatibility support only. Agents should prefer to use relevant" + " JVMTI events and functions above listening for this event.", + { // NOLINT[whitespace/braces] [4] + { "jni_env", JVMTI_KIND_IN_PTR, JVMTI_TYPE_JNIENV, false }, + { "type", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, + { "data_size", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, + { "data", JVMTI_KIND_IN_BUF, JVMTI_TYPE_JBYTE, false }, + }); + if (error != OK) { + return error; + } + + // Copy into output buffer. + + *extension_count_ptr = ext_vector.size(); + JvmtiUniquePtr out_data = + AllocJvmtiUniquePtr(env, ext_vector.size(), &error); + if (out_data == nullptr) { + return error; + } + memcpy(out_data.get(), + ext_vector.data(), + ext_vector.size() * sizeof(jvmtiExtensionEventInfo)); + *extensions = out_data.release(); + + // Release all the buffer holders, we're OK now. + for (auto& holder : char_buffers) { + holder.release(); + } + for (auto& holder : param_buffers) { + holder.release(); + } + return OK; } -jvmtiError ExtensionUtil::SetExtensionEventCallback(jvmtiEnv* env ATTRIBUTE_UNUSED, - jint extension_event_index ATTRIBUTE_UNUSED, - jvmtiExtensionEvent callback ATTRIBUTE_UNUSED) { - // We do not have any extension events, so any call is illegal. - return ERR(ILLEGAL_ARGUMENT); +jvmtiError ExtensionUtil::SetExtensionEventCallback(jvmtiEnv* env, + jint extension_event_index, + jvmtiExtensionEvent callback, + EventHandler* event_handler) { + if (!IsExtensionEvent(extension_event_index)) { + return ERR(ILLEGAL_ARGUMENT); + } + ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(env); + jvmtiEventMode mode = callback == nullptr ? JVMTI_DISABLE : JVMTI_ENABLE; + // Lock the event_info_mutex_ while we set the event to make sure it isn't lost by a concurrent + // change to the normal callbacks. + { + art::WriterMutexLock lk(art::Thread::Current(), art_env->event_info_mutex_); + if (art_env->event_callbacks.get() == nullptr) { + art_env->event_callbacks.reset(new ArtJvmtiEventCallbacks()); + } + jvmtiError err = art_env->event_callbacks->Set(extension_event_index, callback); + if (err != OK) { + return err; + } + } + return event_handler->SetEvent(art_env, + /*event_thread*/nullptr, + static_cast(extension_event_index), + mode); } } // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_extension.h b/openjdkjvmti/ti_extension.h index d705ba7f59..18133e98e2 100644 --- a/openjdkjvmti/ti_extension.h +++ b/openjdkjvmti/ti_extension.h @@ -37,6 +37,8 @@ namespace openjdkjvmti { +class EventHandler; + class ExtensionUtil { public: static jvmtiError GetExtensionFunctions(jvmtiEnv* env, @@ -49,7 +51,8 @@ class ExtensionUtil { static jvmtiError SetExtensionEventCallback(jvmtiEnv* env, jint extension_event_index, - jvmtiExtensionEvent callback); + jvmtiExtensionEvent callback, + EventHandler* event_handler); }; } // namespace openjdkjvmti diff --git a/runtime/debugger.cc b/runtime/debugger.cc index c7f245309f..613e4fe7c7 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -18,6 +18,7 @@ #include +#include #include #include #include @@ -325,6 +326,7 @@ bool Dbg::gDebuggerActive = false; bool Dbg::gDisposed = false; ObjectRegistry* Dbg::gRegistry = nullptr; DebuggerActiveMethodInspectionCallback Dbg::gDebugActiveCallback; +DebuggerDdmCallback Dbg::gDebugDdmCallback; // Deoptimization support. std::vector Dbg::deoptimization_requests_; @@ -342,6 +344,10 @@ uint32_t Dbg::instrumentation_events_ = 0; Dbg::DbgThreadLifecycleCallback Dbg::thread_lifecycle_callback_; Dbg::DbgClassLoadCallback Dbg::class_load_callback_; +void DebuggerDdmCallback::DdmPublishChunk(uint32_t type, const ArrayRef& data) { + Dbg::DdmSendChunk(type, data); +} + bool DebuggerActiveMethodInspectionCallback::IsMethodBeingInspected(ArtMethod* m ATTRIBUTE_UNUSED) { return Dbg::IsDebuggerActive(); } @@ -531,6 +537,12 @@ void Dbg::StartJdwp() { CHECK(gRegistry == nullptr); gRegistry = new ObjectRegistry; + { + // Setup the Ddm listener + ScopedObjectAccess soa(Thread::Current()); + Runtime::Current()->GetRuntimeCallbacks()->AddDdmCallback(&gDebugDdmCallback); + } + // Init JDWP if the debugger is enabled. This may connect out to a // debugger, passively listen for a debugger, or block waiting for a // debugger. @@ -4285,47 +4297,28 @@ void Dbg::FinishInvokeMethod(DebugInvokeReq* pReq) { } } -/* - * "request" contains a full JDWP packet, possibly with multiple chunks. We - * need to process each, accumulate the replies, and ship the whole thing - * back. - * - * Returns "true" if we have a reply. The reply buffer is newly allocated, - * and includes the chunk type/length, followed by the data. - * - * OLD-TODO: we currently assume that the request and reply include a single - * chunk. If this becomes inconvenient we will need to adapt. - */ -bool Dbg::DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pReplyLen) { - Thread* self = Thread::Current(); - JNIEnv* env = self->GetJniEnv(); - - uint32_t type = request->ReadUnsigned32("type"); - uint32_t length = request->ReadUnsigned32("length"); - - // Create a byte[] corresponding to 'request'. - size_t request_length = request->size(); - ScopedLocalRef dataArray(env, env->NewByteArray(request_length)); +bool Dbg::DdmHandleChunk(JNIEnv* env, + uint32_t type, + const ArrayRef& data, + /*out*/uint32_t* out_type, + /*out*/std::vector* out_data) { + ScopedLocalRef dataArray(env, env->NewByteArray(data.size())); if (dataArray.get() == nullptr) { - LOG(WARNING) << "byte[] allocation failed: " << request_length; + LOG(WARNING) << "byte[] allocation failed: " << data.size(); env->ExceptionClear(); return false; } - env->SetByteArrayRegion(dataArray.get(), 0, request_length, - reinterpret_cast(request->data())); - request->Skip(request_length); - - // Run through and find all chunks. [Currently just find the first.] - ScopedByteArrayRO contents(env, dataArray.get()); - if (length != request_length) { - LOG(WARNING) << StringPrintf("bad chunk found (len=%u pktLen=%zd)", length, request_length); - return false; - } - + env->SetByteArrayRegion(dataArray.get(), + 0, + data.size(), + reinterpret_cast(data.data())); // Call "private static Chunk dispatch(int type, byte[] data, int offset, int length)". - ScopedLocalRef chunk(env, env->CallStaticObjectMethod(WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer, - WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch, - type, dataArray.get(), 0, length)); + ScopedLocalRef chunk( + env, + env->CallStaticObjectMethod( + WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer, + 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(); @@ -4349,30 +4342,78 @@ bool Dbg::DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pRep * * So we're pretty much stuck with copying data around multiple times. */ - ScopedLocalRef replyData(env, reinterpret_cast(env->GetObjectField(chunk.get(), WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data))); - jint offset = env->GetIntField(chunk.get(), WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_offset); - length = env->GetIntField(chunk.get(), WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_length); - type = env->GetIntField(chunk.get(), WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_type); - - VLOG(jdwp) << StringPrintf("DDM reply: type=0x%08x data=%p offset=%d length=%d", type, replyData.get(), offset, length); + ScopedLocalRef replyData( + env, + reinterpret_cast( + env->GetObjectField( + chunk.get(), WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data))); + jint offset = env->GetIntField(chunk.get(), + WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_offset); + jint length = env->GetIntField(chunk.get(), + WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_length); + *out_type = env->GetIntField(chunk.get(), + WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_type); + + VLOG(jdwp) << StringPrintf("DDM reply: type=0x%08x data=%p offset=%d length=%d", + type, + replyData.get(), + offset, + length); if (length == 0 || replyData.get() == nullptr) { return false; } - const int kChunkHdrLen = 8; - uint8_t* reply = new uint8_t[length + kChunkHdrLen]; - if (reply == nullptr) { - LOG(WARNING) << "malloc failed: " << (length + kChunkHdrLen); + out_data->resize(length); + env->GetByteArrayRegion(replyData.get(), + offset, + length, + reinterpret_cast(out_data->data())); + return true; +} + +/* + * "request" contains a full JDWP packet, possibly with multiple chunks. We + * need to process each, accumulate the replies, and ship the whole thing + * back. + * + * Returns "true" if we have a reply. The reply buffer is newly allocated, + * and includes the chunk type/length, followed by the data. + * + * OLD-TODO: we currently assume that the request and reply include a single + * chunk. If this becomes inconvenient we will need to adapt. + */ +bool Dbg::DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pReplyLen) { + Thread* self = Thread::Current(); + JNIEnv* env = self->GetJniEnv(); + + uint32_t type = request->ReadUnsigned32("type"); + uint32_t length = request->ReadUnsigned32("length"); + + // Create a byte[] corresponding to 'request'. + size_t request_length = request->size(); + // Run through and find all chunks. [Currently just find the first.] + if (length != request_length) { + LOG(WARNING) << StringPrintf("bad chunk found (len=%u pktLen=%zd)", length, request_length); return false; } - JDWP::Set4BE(reply + 0, type); - JDWP::Set4BE(reply + 4, length); - env->GetByteArrayRegion(replyData.get(), offset, length, reinterpret_cast(reply + kChunkHdrLen)); - *pReplyBuf = reply; - *pReplyLen = length + kChunkHdrLen; - - VLOG(jdwp) << StringPrintf("dvmHandleDdm returning type=%.4s %p len=%d", reinterpret_cast(reply), reply, length); + ArrayRef data(reinterpret_cast(request->data()), request_length); + std::vector out_data; + uint32_t out_type = 0; + request->Skip(request_length); + if (!DdmHandleChunk(env, type, data, &out_type, &out_data)) { + return false; + } + const uint32_t kDdmHeaderSize = 8; + *pReplyLen = out_data.size() + kDdmHeaderSize; + *pReplyBuf = new uint8_t[out_data.size() + kDdmHeaderSize]; + memcpy((*pReplyBuf) + kDdmHeaderSize, out_data.data(), out_data.size()); + JDWP::Set4BE(*pReplyBuf, out_type); + JDWP::Set4BE((*pReplyBuf) + 4, static_cast(out_data.size())); + VLOG(jdwp) + << StringPrintf("dvmHandleDdm returning type=%.4s", reinterpret_cast(*pReplyBuf)) + << "0x" << std::hex << reinterpret_cast(*pReplyBuf) << std::dec + << " len= " << out_data.size(); return true; } @@ -4482,6 +4523,10 @@ void Dbg::PostThreadDeath(Thread* t) { Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THDE")); } +void Dbg::DdmSendChunk(uint32_t type, const ArrayRef& data) { + DdmSendChunk(type, data.size(), data.data()); +} + void Dbg::DdmSendChunk(uint32_t type, size_t byte_count, const uint8_t* buf) { CHECK(buf != nullptr); iovec vec[1]; diff --git a/runtime/debugger.h b/runtime/debugger.h index ec37833f6d..c3184e8374 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -27,6 +27,7 @@ #include #include +#include "base/array_ref.h" #include "class_linker.h" #include "gc_root.h" #include "handle.h" @@ -52,6 +53,11 @@ class ScopedObjectAccessUnchecked; class StackVisitor; class Thread; +struct DebuggerDdmCallback : public DdmCallback { + void DdmPublishChunk(uint32_t type, const ArrayRef& data) + OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); +}; + struct DebuggerActiveMethodInspectionCallback : public MethodInspectionCallback { bool IsMethodBeingInspected(ArtMethod* m ATTRIBUTE_UNUSED) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); @@ -647,9 +653,17 @@ class Dbg { REQUIRES_SHARED(Locks::mutator_lock_); static void DdmSetThreadNotification(bool enable) REQUIRES(!Locks::thread_list_lock_); + static bool DdmHandleChunk( + JNIEnv* env, + uint32_t type, + const ArrayRef& data, + /*out*/uint32_t* out_type, + /*out*/std::vector* out_data); static bool DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pReplyLen); static void DdmConnected() REQUIRES_SHARED(Locks::mutator_lock_); static void DdmDisconnected() REQUIRES_SHARED(Locks::mutator_lock_); + static void DdmSendChunk(uint32_t type, const ArrayRef& bytes) + REQUIRES_SHARED(Locks::mutator_lock_); static void DdmSendChunk(uint32_t type, const std::vector& bytes) REQUIRES_SHARED(Locks::mutator_lock_); static void DdmSendChunk(uint32_t type, size_t len, const uint8_t* buf) @@ -782,6 +796,7 @@ class Dbg { static bool gDebuggerActive; static DebuggerActiveMethodInspectionCallback gDebugActiveCallback; + static DebuggerDdmCallback gDebugDdmCallback; // Indicates whether we should drop the JDWP connection because the runtime stops or the // debugger called VirtualMachine.Dispose. diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc index f8f4b1f0ad..c79f51b51e 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc @@ -16,6 +16,7 @@ #include "org_apache_harmony_dalvik_ddmc_DdmServer.h" +#include "base/array_ref.h" #include "base/logging.h" #include "debugger.h" #include "jni_internal.h" @@ -31,7 +32,9 @@ static void DdmServer_nativeSendChunk(JNIEnv* env, jclass, jint type, ScopedFastNativeObjectAccess soa(env); ScopedByteArrayRO data(env, javaData); DCHECK_LE(offset + length, static_cast(data.size())); - Dbg::DdmSendChunk(type, length, reinterpret_cast(&data[offset])); + ArrayRef chunk(reinterpret_cast(&data[offset]), + static_cast(length)); + Runtime::Current()->GetRuntimeCallbacks()->DdmPublishChunk(static_cast(type), chunk); } static JNINativeMethod gMethods[] = { diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc index 339fe822fd..40d7889565 100644 --- a/runtime/runtime_callbacks.cc +++ b/runtime/runtime_callbacks.cc @@ -35,6 +35,20 @@ static inline void Remove(T* cb, std::vector* data) { } } +void RuntimeCallbacks::AddDdmCallback(DdmCallback* cb) { + ddm_callbacks_.push_back(cb); +} + +void RuntimeCallbacks::RemoveDdmCallback(DdmCallback* cb) { + Remove(cb, &ddm_callbacks_); +} + +void RuntimeCallbacks::DdmPublishChunk(uint32_t type, const ArrayRef& data) { + for (DdmCallback* cb : ddm_callbacks_) { + cb->DdmPublishChunk(type, data); + } +} + void RuntimeCallbacks::AddMethodInspectionCallback(MethodInspectionCallback* cb) { method_inspection_callbacks_.push_back(cb); } diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h index c1ba9643a7..baf941a8e1 100644 --- a/runtime/runtime_callbacks.h +++ b/runtime/runtime_callbacks.h @@ -19,6 +19,7 @@ #include +#include "base/array_ref.h" #include "base/macros.h" #include "base/mutex.h" #include "dex_file.h" @@ -54,6 +55,13 @@ class ThreadLifecycleCallback; // any state checking (is the listener enabled) in the listener itself. For an example, see // Dbg. +class DdmCallback { + public: + virtual ~DdmCallback() {} + virtual void DdmPublishChunk(uint32_t type, const ArrayRef& data) + REQUIRES_SHARED(Locks::mutator_lock_) = 0; +}; + class RuntimeSigQuitCallback { public: virtual ~RuntimeSigQuitCallback() {} @@ -182,6 +190,13 @@ class RuntimeCallbacks { void RemoveMethodInspectionCallback(MethodInspectionCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_); + // DDMS callbacks + void DdmPublishChunk(uint32_t type, const ArrayRef& data) + REQUIRES_SHARED(Locks::mutator_lock_); + + void AddDdmCallback(DdmCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_); + void RemoveDdmCallback(DdmCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_); + private: std::vector thread_callbacks_ GUARDED_BY(Locks::mutator_lock_); @@ -197,6 +212,8 @@ class RuntimeCallbacks { GUARDED_BY(Locks::mutator_lock_); std::vector method_inspection_callbacks_ GUARDED_BY(Locks::mutator_lock_); + std::vector ddm_callbacks_ + GUARDED_BY(Locks::mutator_lock_); }; } // namespace art diff --git a/test/1940-ddms-ext/ddm_ext.cc b/test/1940-ddms-ext/ddm_ext.cc new file mode 100644 index 0000000000..cc29df9a49 --- /dev/null +++ b/test/1940-ddms-ext/ddm_ext.cc @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jvmti.h" + +// Test infrastructure +#include "jvmti_helper.h" +#include "nativehelper/scoped_local_ref.h" +#include "nativehelper/scoped_primitive_array.h" +#include "test_env.h" + +namespace art { +namespace Test1940DdmExt { + +typedef jvmtiError (*DdmHandleChunk)(jvmtiEnv* env, + jint type_in, + jint len_in, + const jbyte* data_in, + jint* type_out, + jint* len_data_out, + jbyte** data_out); + +struct DdmsTrackingData { + DdmHandleChunk send_ddm_chunk; + jclass test_klass; + jmethodID publish_method; +}; + +template +static void Dealloc(T* t) { + jvmti_env->Deallocate(reinterpret_cast(t)); +} + +template +static void Dealloc(T* t, Rest... rs) { + Dealloc(t); + Dealloc(rs...); +} + +extern "C" JNIEXPORT jobject JNICALL Java_art_Test1940_processChunk(JNIEnv* env, + jclass, + jobject chunk) { + DdmsTrackingData* data = nullptr; + if (JvmtiErrorToException( + env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast(&data)))) { + return nullptr; + } + CHECK(chunk != nullptr); + CHECK(data != nullptr); + CHECK(data->send_ddm_chunk != nullptr); + ScopedLocalRef chunk_class(env, env->FindClass("org/apache/harmony/dalvik/ddmc/Chunk")); + if (env->ExceptionCheck()) { + return nullptr; + } + jfieldID type_field_id = env->GetFieldID(chunk_class.get(), "type", "I"); + jfieldID offset_field_id = env->GetFieldID(chunk_class.get(), "offset", "I"); + jfieldID length_field_id = env->GetFieldID(chunk_class.get(), "length", "I"); + jfieldID data_field_id = env->GetFieldID(chunk_class.get(), "data", "[B"); + jint type = env->GetIntField(chunk, type_field_id); + jint off = env->GetIntField(chunk, offset_field_id); + jint len = env->GetIntField(chunk, length_field_id); + ScopedLocalRef chunk_buf( + env, reinterpret_cast(env->GetObjectField(chunk, data_field_id))); + if (env->ExceptionCheck()) { + return nullptr; + } + ScopedByteArrayRO byte_data(env, chunk_buf.get()); + jint out_type; + jint out_size; + jbyte* out_data; + if (JvmtiErrorToException(env, jvmti_env, data->send_ddm_chunk(jvmti_env, + type, + len, + &byte_data[off], + /*out*/&out_type, + /*out*/&out_size, + /*out*/&out_data))) { + return nullptr; + } else { + ScopedLocalRef chunk_data(env, env->NewByteArray(out_size)); + env->SetByteArrayRegion(chunk_data.get(), 0, out_size, out_data); + Dealloc(out_data); + ScopedLocalRef res(env, env->NewObject(chunk_class.get(), + env->GetMethodID(chunk_class.get(), + "", + "(I[BII)V"), + out_type, + chunk_data.get(), + 0, + out_size)); + return res.release(); + } +} + +static void DeallocParams(jvmtiParamInfo* params, jint n_params) { + for (jint i = 0; i < n_params; i++) { + Dealloc(params[i].name); + } +} + +static void JNICALL PublishCB(jvmtiEnv* jvmti, JNIEnv* jnienv, jint type, jint size, jbyte* bytes) { + DdmsTrackingData* data = nullptr; + if (JvmtiErrorToException(jnienv, jvmti, + jvmti->GetEnvironmentLocalStorage(reinterpret_cast(&data)))) { + return; + } + ScopedLocalRef res(jnienv, jnienv->NewByteArray(size)); + jnienv->SetByteArrayRegion(res.get(), 0, size, bytes); + jnienv->CallStaticVoidMethod(data->test_klass, data->publish_method, type, res.get()); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1940_initializeTest(JNIEnv* env, + jclass, + jclass method_klass, + jobject publish_method) { + void* old_data = nullptr; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) { + return; + } else if (old_data != nullptr) { + ScopedLocalRef rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), "Environment already has local storage set!"); + return; + } + DdmsTrackingData* data = nullptr; + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->Allocate(sizeof(DdmsTrackingData), + reinterpret_cast(&data)))) { + return; + } + memset(data, 0, sizeof(DdmsTrackingData)); + data->test_klass = reinterpret_cast(env->NewGlobalRef(method_klass)); + data->publish_method = env->FromReflectedMethod(publish_method); + if (env->ExceptionCheck()) { + return; + } + // Get the extensions. + jint n_ext = 0; + jvmtiExtensionFunctionInfo* infos = nullptr; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetExtensionFunctions(&n_ext, &infos))) { + return; + } + for (jint i = 0; i < n_ext; i++) { + jvmtiExtensionFunctionInfo* cur_info = &infos[i]; + if (strcmp("com.android.art.internal.ddm.process_chunk", cur_info->id) == 0) { + data->send_ddm_chunk = reinterpret_cast(cur_info->func); + } + // Cleanup the cur_info + DeallocParams(cur_info->params, cur_info->param_count); + Dealloc(cur_info->id, cur_info->short_description, cur_info->params, cur_info->errors); + } + // Cleanup the array. + Dealloc(infos); + if (data->send_ddm_chunk == nullptr) { + ScopedLocalRef rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), "Unable to find memory tracking extensions."); + return; + } + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) { + return; + } + + jint event_index = -1; + bool found_event = false; + jvmtiExtensionEventInfo* events = nullptr; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetExtensionEvents(&n_ext, &events))) { + return; + } + for (jint i = 0; i < n_ext; i++) { + jvmtiExtensionEventInfo* cur_info = &events[i]; + if (strcmp("com.android.art.internal.ddm.publish_chunk", cur_info->id) == 0) { + found_event = true; + event_index = cur_info->extension_event_index; + } + // Cleanup the cur_info + DeallocParams(cur_info->params, cur_info->param_count); + Dealloc(cur_info->id, cur_info->short_description, cur_info->params); + } + // Cleanup the array. + Dealloc(events); + if (!found_event) { + ScopedLocalRef rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), "Unable to find ddms extension event."); + return; + } + JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetExtensionEventCallback( + event_index, reinterpret_cast(PublishCB))); + return; +} + +} // namespace Test1940DdmExt +} // namespace art diff --git a/test/1940-ddms-ext/expected.txt b/test/1940-ddms-ext/expected.txt new file mode 100644 index 0000000000..cf4ad50e90 --- /dev/null +++ b/test/1940-ddms-ext/expected.txt @@ -0,0 +1,7 @@ +Sending data [1, 2, 3, 4, 5, 6, 7, 8] +MyDdmHandler: Chunk received: Chunk(Type: 0xDEADBEEF, Len: 8, data: [1, 2, 3, 4, 5, 6, 7, 8]) +MyDdmHandler: Putting value 0x800025 +MyDdmHandler: Chunk returned: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, -128, 0, 37]) +JVMTI returned chunk: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, -128, 0, 37]) +Sending chunk: Chunk(Type: 0xDEADBEEF, Len: 8, data: [9, 10, 11, 12, 13, 14, 15, 16]) +Chunk published: Chunk(Type: 0xDEADBEEF, Len: 8, data: [9, 10, 11, 12, 13, 14, 15, 16]) diff --git a/test/1940-ddms-ext/info.txt b/test/1940-ddms-ext/info.txt new file mode 100644 index 0000000000..e1d35ae026 --- /dev/null +++ b/test/1940-ddms-ext/info.txt @@ -0,0 +1 @@ +Tests the jvmti-extension to get allocated memory snapshot. diff --git a/test/1940-ddms-ext/run b/test/1940-ddms-ext/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/1940-ddms-ext/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/1940-ddms-ext/src-art/art/Test1940.java b/test/1940-ddms-ext/src-art/art/Test1940.java new file mode 100644 index 0000000000..f0ee7102a9 --- /dev/null +++ b/test/1940-ddms-ext/src-art/art/Test1940.java @@ -0,0 +1,101 @@ +/* + * 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 org.apache.harmony.dalvik.ddmc.*; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.zip.Adler32; +import java.nio.*; + +public class Test1940 { + public static final int DDMS_TYPE_INDEX = 0; + public static final int DDMS_LEN_INDEX = 4; + public static final int DDMS_HEADER_LENGTH = 8; + public static final int MY_DDMS_TYPE = 0xDEADBEEF; + public static final int MY_DDMS_RESPONSE_TYPE = 0xFADE7357; + + public static final class TestError extends Error { + public TestError(String s) { super(s); } + } + + private static void checkEq(Object a, Object b) { + if (!a.equals(b)) { + throw new TestError("Failure: " + a + " != " + b); + } + } + + private static String printChunk(Chunk k) { + byte[] out = new byte[k.length]; + System.arraycopy(k.data, k.offset, out, 0, k.length); + return String.format("Chunk(Type: 0x%X, Len: %d, data: %s)", + k.type, k.length, Arrays.toString(out)); + } + + private static final class MyDdmHandler extends ChunkHandler { + public void connected() {} + public void disconnected() {} + public Chunk handleChunk(Chunk req) { + // For this test we will simply calculate the checksum + checkEq(req.type, MY_DDMS_TYPE); + System.out.println("MyDdmHandler: Chunk received: " + printChunk(req)); + ByteBuffer b = ByteBuffer.wrap(new byte[8]); + Adler32 a = new Adler32(); + a.update(req.data, req.offset, req.length); + b.order(ByteOrder.BIG_ENDIAN); + long val = a.getValue(); + b.putLong(val); + System.out.printf("MyDdmHandler: Putting value 0x%X\n", val); + Chunk ret = new Chunk(MY_DDMS_RESPONSE_TYPE, b.array(), 0, 8); + System.out.println("MyDdmHandler: Chunk returned: " + printChunk(ret)); + return ret; + } + } + + public static final ChunkHandler SINGLE_HANDLER = new MyDdmHandler(); + + public static void HandlePublish(int type, byte[] data) { + System.out.println("Chunk published: " + printChunk(new Chunk(type, data, 0, data.length))); + } + + public static void run() throws Exception { + initializeTest( + Test1940.class, + Test1940.class.getDeclaredMethod("HandlePublish", Integer.TYPE, new byte[0].getClass())); + // Test sending chunk directly. + DdmServer.registerHandler(MY_DDMS_TYPE, SINGLE_HANDLER); + DdmServer.registrationComplete(); + byte[] data = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; + System.out.println("Sending data " + Arrays.toString(data)); + Chunk res = processChunk(data); + System.out.println("JVMTI returned chunk: " + printChunk(res)); + + // Test sending chunk through DdmServer#sendChunk + Chunk c = new Chunk( + MY_DDMS_TYPE, new byte[] { 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }, 0, 8); + System.out.println("Sending chunk: " + printChunk(c)); + DdmServer.sendChunk(c); + } + + private static Chunk processChunk(byte[] val) { + return processChunk(new Chunk(MY_DDMS_TYPE, val, 0, val.length)); + } + + private static native void initializeTest(Class k, Method m); + private static native Chunk processChunk(Chunk val); +} diff --git a/test/1940-ddms-ext/src/Main.java b/test/1940-ddms-ext/src/Main.java new file mode 100644 index 0000000000..1fd4cd3e41 --- /dev/null +++ b/test/1940-ddms-ext/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.Test1940.run(); + } +} diff --git a/test/1940-ddms-ext/src/art/Test1940.java b/test/1940-ddms-ext/src/art/Test1940.java new file mode 100644 index 0000000000..c8dc19ca7f --- /dev/null +++ b/test/1940-ddms-ext/src/art/Test1940.java @@ -0,0 +1,23 @@ +/* + * 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; + +public class Test1940 { + public static void run() throws Exception { + throw new RuntimeException("Should not be called. Should use src-art/art/Test1940.java"); + } +} diff --git a/test/Android.bp b/test/Android.bp index 17ef1141df..ba24119e9c 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -263,7 +263,10 @@ art_cc_defaults { shared_libs: [ "libbase", ], - header_libs: ["libopenjdkjvmti_headers"], + header_libs: [ + "libnativehelper_header_only", + "libopenjdkjvmti_headers", + ], include_dirs: ["art/test/ti-agent"], } @@ -282,6 +285,7 @@ art_cc_defaults { "912-classes/classes_art.cc", "936-search-onload/search_onload.cc", "983-source-transform-verify/source_transform.cc", + "1940-ddms-ext/ddm_ext.cc", ], } diff --git a/tools/libjdwp_art_failures.txt b/tools/libjdwp_art_failures.txt index 354bee81af..fd711bbd8b 100644 --- a/tools/libjdwp_art_failures.txt +++ b/tools/libjdwp_art_failures.txt @@ -97,5 +97,11 @@ result: EXEC_FAILED, bug: 69121056, name: "org.apache.harmony.jpda.tests.jdwp.ObjectReference.IsCollectedTest#testIsCollected001" +}, +{ + description: "Test for ddms extensions that are not implemented for prebuilt-libjdwp", + result: EXEC_FAILED, + bug: 69169846, + name: "org.apache.harmony.jpda.tests.jdwp.DDM.DDMTest#testChunk001" } ] diff --git a/tools/libjdwp_oj_art_failures.txt b/tools/libjdwp_oj_art_failures.txt index 787c4d28fb..3d06bcf100 100644 --- a/tools/libjdwp_oj_art_failures.txt +++ b/tools/libjdwp_oj_art_failures.txt @@ -67,5 +67,11 @@ result: EXEC_FAILED, bug: 69121056, name: "org.apache.harmony.jpda.tests.jdwp.ObjectReference.IsCollectedTest#testIsCollected001" +}, +{ + description: "Test for ddms extensions that are not yet implemented", + result: EXEC_FAILED, + bug: 69169846, + name: "org.apache.harmony.jpda.tests.jdwp.DDM.DDMTest#testChunk001" } ] -- GitLab From 38e380b3c5139c0993495a7a0f040bfe8aa1e9e9 Mon Sep 17 00:00:00 2001 From: Lena Djokic Date: Mon, 30 Oct 2017 16:17:10 +0100 Subject: [PATCH 045/226] MIPS: Implement Sum-of-Abs-Differences Test: test-art-host test-art-target Change-Id: I32a3e21f96cdcbab2e108d71746670408deb901a --- .../optimizing/code_generator_vector_mips.cc | 188 +++++++++++++++++- .../code_generator_vector_mips64.cc | 188 +++++++++++++++++- compiler/optimizing/loop_optimization.cc | 16 +- .../src/Main.java | 7 + .../src/Main.java | 7 + .../src/Main.java | 7 + test/656-checker-simd-opt/src/Main.java | 36 ++++ test/660-checker-simd-sad-byte/src/Main.java | 57 ++++++ test/660-checker-simd-sad-char/src/Main.java | 10 +- test/660-checker-simd-sad-int/src/Main.java | 47 ++++- test/660-checker-simd-sad-long/src/Main.java | 36 ++++ test/660-checker-simd-sad-short/src/Main.java | 57 ++++++ .../660-checker-simd-sad-short2/src/Main.java | 57 ++++++ .../660-checker-simd-sad-short3/src/Main.java | 95 +++++++++ 14 files changed, 787 insertions(+), 21 deletions(-) diff --git a/compiler/optimizing/code_generator_vector_mips.cc b/compiler/optimizing/code_generator_vector_mips.cc index 384b642145..3cf150a6b8 100644 --- a/compiler/optimizing/code_generator_vector_mips.cc +++ b/compiler/optimizing/code_generator_vector_mips.cc @@ -1071,11 +1071,195 @@ void InstructionCodeGeneratorMIPS::VisitVecMultiplyAccumulate(HVecMultiplyAccumu void LocationsBuilderMIPS::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { CreateVecAccumLocations(GetGraph()->GetAllocator(), instruction); + LocationSummary* locations = instruction->GetLocations(); + // All conversions require at least one temporary register. + locations->AddTemp(Location::RequiresFpuRegister()); + // Some conversions require a second temporary register. + HVecOperation* a = instruction->InputAt(1)->AsVecOperation(); + HVecOperation* b = instruction->InputAt(2)->AsVecOperation(); + DCHECK_EQ(HVecOperation::ToSignedType(a->GetPackedType()), + HVecOperation::ToSignedType(b->GetPackedType())); + switch (a->GetPackedType()) { + case DataType::Type::kInt32: + if (instruction->GetPackedType() == DataType::Type::kInt32) { + break; + } + FALLTHROUGH_INTENDED; + case DataType::Type::kUint8: + case DataType::Type::kInt8: + case DataType::Type::kUint16: + case DataType::Type::kInt16: + locations->AddTemp(Location::RequiresFpuRegister()); + break; + default: + break; + } } void InstructionCodeGeneratorMIPS::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { - LOG(FATAL) << "No SIMD for " << instruction->GetId(); - // TODO: implement this, location helper already filled out (shared with MulAcc). + LocationSummary* locations = instruction->GetLocations(); + VectorRegister acc = VectorRegisterFrom(locations->InAt(0)); + VectorRegister left = VectorRegisterFrom(locations->InAt(1)); + VectorRegister right = VectorRegisterFrom(locations->InAt(2)); + VectorRegister tmp = static_cast(FTMP); + VectorRegister tmp1 = VectorRegisterFrom(locations->GetTemp(0)); + + DCHECK(locations->InAt(0).Equals(locations->Out())); + + // Handle all feasible acc_T += sad(a_S, b_S) type combinations (T x S). + HVecOperation* a = instruction->InputAt(1)->AsVecOperation(); + HVecOperation* b = instruction->InputAt(2)->AsVecOperation(); + DCHECK_EQ(HVecOperation::ToSignedType(a->GetPackedType()), + HVecOperation::ToSignedType(b->GetPackedType())); + switch (a->GetPackedType()) { + case DataType::Type::kUint8: + case DataType::Type::kInt8: + DCHECK_EQ(16u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint16: + case DataType::Type::kInt16: { + DCHECK_EQ(8u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillB(tmp, ZERO); + __ Hadd_sH(tmp1, left, tmp); + __ Hadd_sH(tmp2, right, tmp); + __ Asub_sH(tmp1, tmp1, tmp2); + __ AddvH(acc, acc, tmp1); + __ Hadd_sH(tmp1, tmp, left); + __ Hadd_sH(tmp2, tmp, right); + __ Asub_sH(tmp1, tmp1, tmp2); + __ AddvH(acc, acc, tmp1); + break; + } + case DataType::Type::kInt32: { + DCHECK_EQ(4u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillB(tmp, ZERO); + __ Hadd_sH(tmp1, left, tmp); + __ Hadd_sH(tmp2, right, tmp); + __ Asub_sH(tmp1, tmp1, tmp2); + __ Hadd_sW(tmp1, tmp1, tmp1); + __ AddvW(acc, acc, tmp1); + __ Hadd_sH(tmp1, tmp, left); + __ Hadd_sH(tmp2, tmp, right); + __ Asub_sH(tmp1, tmp1, tmp2); + __ Hadd_sW(tmp1, tmp1, tmp1); + __ AddvW(acc, acc, tmp1); + break; + } + case DataType::Type::kInt64: { + DCHECK_EQ(2u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillB(tmp, ZERO); + __ Hadd_sH(tmp1, left, tmp); + __ Hadd_sH(tmp2, right, tmp); + __ Asub_sH(tmp1, tmp1, tmp2); + __ Hadd_sW(tmp1, tmp1, tmp1); + __ Hadd_sD(tmp1, tmp1, tmp1); + __ AddvD(acc, acc, tmp1); + __ Hadd_sH(tmp1, tmp, left); + __ Hadd_sH(tmp2, tmp, right); + __ Asub_sH(tmp1, tmp1, tmp2); + __ Hadd_sW(tmp1, tmp1, tmp1); + __ Hadd_sD(tmp1, tmp1, tmp1); + __ AddvD(acc, acc, tmp1); + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + case DataType::Type::kUint16: + case DataType::Type::kInt16: + DCHECK_EQ(8u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case DataType::Type::kInt32: { + DCHECK_EQ(4u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillH(tmp, ZERO); + __ Hadd_sW(tmp1, left, tmp); + __ Hadd_sW(tmp2, right, tmp); + __ Asub_sW(tmp1, tmp1, tmp2); + __ AddvW(acc, acc, tmp1); + __ Hadd_sW(tmp1, tmp, left); + __ Hadd_sW(tmp2, tmp, right); + __ Asub_sW(tmp1, tmp1, tmp2); + __ AddvW(acc, acc, tmp1); + break; + } + case DataType::Type::kInt64: { + DCHECK_EQ(2u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillH(tmp, ZERO); + __ Hadd_sW(tmp1, left, tmp); + __ Hadd_sW(tmp2, right, tmp); + __ Asub_sW(tmp1, tmp1, tmp2); + __ Hadd_sD(tmp1, tmp1, tmp1); + __ AddvD(acc, acc, tmp1); + __ Hadd_sW(tmp1, tmp, left); + __ Hadd_sW(tmp2, tmp, right); + __ Asub_sW(tmp1, tmp1, tmp2); + __ Hadd_sD(tmp1, tmp1, tmp1); + __ AddvD(acc, acc, tmp1); + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + case DataType::Type::kInt32: + DCHECK_EQ(4u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case DataType::Type::kInt32: { + DCHECK_EQ(4u, instruction->GetVectorLength()); + __ FillW(tmp, ZERO); + __ SubvW(tmp1, left, right); + __ Add_aW(tmp1, tmp1, tmp); + __ AddvW(acc, acc, tmp1); + break; + } + case DataType::Type::kInt64: { + DCHECK_EQ(2u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillW(tmp, ZERO); + __ Hadd_sD(tmp1, left, tmp); + __ Hadd_sD(tmp2, right, tmp); + __ Asub_sD(tmp1, tmp1, tmp2); + __ AddvD(acc, acc, tmp1); + __ Hadd_sD(tmp1, tmp, left); + __ Hadd_sD(tmp2, tmp, right); + __ Asub_sD(tmp1, tmp1, tmp2); + __ AddvD(acc, acc, tmp1); + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + case DataType::Type::kInt64: { + DCHECK_EQ(2u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case DataType::Type::kInt64: { + DCHECK_EQ(2u, instruction->GetVectorLength()); + __ FillW(tmp, ZERO); + __ SubvD(tmp1, left, right); + __ Add_aD(tmp1, tmp1, tmp); + __ AddvD(acc, acc, tmp1); + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } } // Helper to set up locations for vector memory operations. diff --git a/compiler/optimizing/code_generator_vector_mips64.cc b/compiler/optimizing/code_generator_vector_mips64.cc index 0c59b7344a..2d69533f21 100644 --- a/compiler/optimizing/code_generator_vector_mips64.cc +++ b/compiler/optimizing/code_generator_vector_mips64.cc @@ -1069,11 +1069,195 @@ void InstructionCodeGeneratorMIPS64::VisitVecMultiplyAccumulate(HVecMultiplyAccu void LocationsBuilderMIPS64::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { CreateVecAccumLocations(GetGraph()->GetAllocator(), instruction); + LocationSummary* locations = instruction->GetLocations(); + // All conversions require at least one temporary register. + locations->AddTemp(Location::RequiresFpuRegister()); + // Some conversions require a second temporary register. + HVecOperation* a = instruction->InputAt(1)->AsVecOperation(); + HVecOperation* b = instruction->InputAt(2)->AsVecOperation(); + DCHECK_EQ(HVecOperation::ToSignedType(a->GetPackedType()), + HVecOperation::ToSignedType(b->GetPackedType())); + switch (a->GetPackedType()) { + case DataType::Type::kInt32: + if (instruction->GetPackedType() == DataType::Type::kInt32) { + break; + } + FALLTHROUGH_INTENDED; + case DataType::Type::kUint8: + case DataType::Type::kInt8: + case DataType::Type::kUint16: + case DataType::Type::kInt16: + locations->AddTemp(Location::RequiresFpuRegister()); + break; + default: + break; + } } void InstructionCodeGeneratorMIPS64::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { - LOG(FATAL) << "No SIMD for " << instruction->GetId(); - // TODO: implement this, location helper already filled out (shared with MulAcc). + LocationSummary* locations = instruction->GetLocations(); + VectorRegister acc = VectorRegisterFrom(locations->InAt(0)); + VectorRegister left = VectorRegisterFrom(locations->InAt(1)); + VectorRegister right = VectorRegisterFrom(locations->InAt(2)); + VectorRegister tmp = static_cast(FTMP); + VectorRegister tmp1 = VectorRegisterFrom(locations->GetTemp(0)); + + DCHECK(locations->InAt(0).Equals(locations->Out())); + + // Handle all feasible acc_T += sad(a_S, b_S) type combinations (T x S). + HVecOperation* a = instruction->InputAt(1)->AsVecOperation(); + HVecOperation* b = instruction->InputAt(2)->AsVecOperation(); + DCHECK_EQ(HVecOperation::ToSignedType(a->GetPackedType()), + HVecOperation::ToSignedType(b->GetPackedType())); + switch (a->GetPackedType()) { + case DataType::Type::kUint8: + case DataType::Type::kInt8: + DCHECK_EQ(16u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint16: + case DataType::Type::kInt16: { + DCHECK_EQ(8u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillB(tmp, ZERO); + __ Hadd_sH(tmp1, left, tmp); + __ Hadd_sH(tmp2, right, tmp); + __ Asub_sH(tmp1, tmp1, tmp2); + __ AddvH(acc, acc, tmp1); + __ Hadd_sH(tmp1, tmp, left); + __ Hadd_sH(tmp2, tmp, right); + __ Asub_sH(tmp1, tmp1, tmp2); + __ AddvH(acc, acc, tmp1); + break; + } + case DataType::Type::kInt32: { + DCHECK_EQ(4u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillB(tmp, ZERO); + __ Hadd_sH(tmp1, left, tmp); + __ Hadd_sH(tmp2, right, tmp); + __ Asub_sH(tmp1, tmp1, tmp2); + __ Hadd_sW(tmp1, tmp1, tmp1); + __ AddvW(acc, acc, tmp1); + __ Hadd_sH(tmp1, tmp, left); + __ Hadd_sH(tmp2, tmp, right); + __ Asub_sH(tmp1, tmp1, tmp2); + __ Hadd_sW(tmp1, tmp1, tmp1); + __ AddvW(acc, acc, tmp1); + break; + } + case DataType::Type::kInt64: { + DCHECK_EQ(2u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillB(tmp, ZERO); + __ Hadd_sH(tmp1, left, tmp); + __ Hadd_sH(tmp2, right, tmp); + __ Asub_sH(tmp1, tmp1, tmp2); + __ Hadd_sW(tmp1, tmp1, tmp1); + __ Hadd_sD(tmp1, tmp1, tmp1); + __ AddvD(acc, acc, tmp1); + __ Hadd_sH(tmp1, tmp, left); + __ Hadd_sH(tmp2, tmp, right); + __ Asub_sH(tmp1, tmp1, tmp2); + __ Hadd_sW(tmp1, tmp1, tmp1); + __ Hadd_sD(tmp1, tmp1, tmp1); + __ AddvD(acc, acc, tmp1); + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + case DataType::Type::kUint16: + case DataType::Type::kInt16: + DCHECK_EQ(8u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case DataType::Type::kInt32: { + DCHECK_EQ(4u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillH(tmp, ZERO); + __ Hadd_sW(tmp1, left, tmp); + __ Hadd_sW(tmp2, right, tmp); + __ Asub_sW(tmp1, tmp1, tmp2); + __ AddvW(acc, acc, tmp1); + __ Hadd_sW(tmp1, tmp, left); + __ Hadd_sW(tmp2, tmp, right); + __ Asub_sW(tmp1, tmp1, tmp2); + __ AddvW(acc, acc, tmp1); + break; + } + case DataType::Type::kInt64: { + DCHECK_EQ(2u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillH(tmp, ZERO); + __ Hadd_sW(tmp1, left, tmp); + __ Hadd_sW(tmp2, right, tmp); + __ Asub_sW(tmp1, tmp1, tmp2); + __ Hadd_sD(tmp1, tmp1, tmp1); + __ AddvD(acc, acc, tmp1); + __ Hadd_sW(tmp1, tmp, left); + __ Hadd_sW(tmp2, tmp, right); + __ Asub_sW(tmp1, tmp1, tmp2); + __ Hadd_sD(tmp1, tmp1, tmp1); + __ AddvD(acc, acc, tmp1); + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + case DataType::Type::kInt32: + DCHECK_EQ(4u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case DataType::Type::kInt32: { + DCHECK_EQ(4u, instruction->GetVectorLength()); + __ FillW(tmp, ZERO); + __ SubvW(tmp1, left, right); + __ Add_aW(tmp1, tmp1, tmp); + __ AddvW(acc, acc, tmp1); + break; + } + case DataType::Type::kInt64: { + DCHECK_EQ(2u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillW(tmp, ZERO); + __ Hadd_sD(tmp1, left, tmp); + __ Hadd_sD(tmp2, right, tmp); + __ Asub_sD(tmp1, tmp1, tmp2); + __ AddvD(acc, acc, tmp1); + __ Hadd_sD(tmp1, tmp, left); + __ Hadd_sD(tmp2, tmp, right); + __ Asub_sD(tmp1, tmp1, tmp2); + __ AddvD(acc, acc, tmp1); + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + case DataType::Type::kInt64: { + DCHECK_EQ(2u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case DataType::Type::kInt64: { + DCHECK_EQ(2u, instruction->GetVectorLength()); + __ FillD(tmp, ZERO); + __ SubvD(tmp1, left, right); + __ Add_aD(tmp1, tmp1, tmp); + __ AddvD(acc, acc, tmp1); + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } } // Helper to set up locations for vector memory operations. diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 69c58275b4..fcc59ea3f9 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -1512,17 +1512,17 @@ bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrict case DataType::Type::kBool: case DataType::Type::kUint8: case DataType::Type::kInt8: - *restrictions |= kNoDiv | kNoReduction | kNoSAD; + *restrictions |= kNoDiv; return TrySetVectorLength(16); case DataType::Type::kUint16: case DataType::Type::kInt16: - *restrictions |= kNoDiv | kNoStringCharAt | kNoReduction | kNoSAD; + *restrictions |= kNoDiv | kNoStringCharAt; return TrySetVectorLength(8); case DataType::Type::kInt32: - *restrictions |= kNoDiv | kNoSAD; + *restrictions |= kNoDiv; return TrySetVectorLength(4); case DataType::Type::kInt64: - *restrictions |= kNoDiv | kNoSAD; + *restrictions |= kNoDiv; return TrySetVectorLength(2); case DataType::Type::kFloat32: *restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN) @@ -1541,17 +1541,17 @@ bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrict case DataType::Type::kBool: case DataType::Type::kUint8: case DataType::Type::kInt8: - *restrictions |= kNoDiv | kNoReduction | kNoSAD; + *restrictions |= kNoDiv; return TrySetVectorLength(16); case DataType::Type::kUint16: case DataType::Type::kInt16: - *restrictions |= kNoDiv | kNoStringCharAt | kNoReduction | kNoSAD; + *restrictions |= kNoDiv | kNoStringCharAt; return TrySetVectorLength(8); case DataType::Type::kInt32: - *restrictions |= kNoDiv | kNoSAD; + *restrictions |= kNoDiv; return TrySetVectorLength(4); case DataType::Type::kInt64: - *restrictions |= kNoDiv | kNoSAD; + *restrictions |= kNoDiv; return TrySetVectorLength(2); case DataType::Type::kFloat32: *restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN) diff --git a/test/651-checker-byte-simd-minmax/src/Main.java b/test/651-checker-byte-simd-minmax/src/Main.java index d365689f5d..2188346aa9 100644 --- a/test/651-checker-byte-simd-minmax/src/Main.java +++ b/test/651-checker-byte-simd-minmax/src/Main.java @@ -183,6 +183,13 @@ public class Main { /// 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 + // + /// CHECK-START-MIPS64: void Main.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++) { diff --git a/test/651-checker-char-simd-minmax/src/Main.java b/test/651-checker-char-simd-minmax/src/Main.java index 72e8958ad8..d92bdaf808 100644 --- a/test/651-checker-char-simd-minmax/src/Main.java +++ b/test/651-checker-char-simd-minmax/src/Main.java @@ -97,6 +97,13 @@ public class Main { /// 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 + // + /// CHECK-START-MIPS64: void Main.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++) { diff --git a/test/651-checker-short-simd-minmax/src/Main.java b/test/651-checker-short-simd-minmax/src/Main.java index d8c4d1e87e..91f2a2dbdb 100644 --- a/test/651-checker-short-simd-minmax/src/Main.java +++ b/test/651-checker-short-simd-minmax/src/Main.java @@ -183,6 +183,13 @@ public class Main { /// 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 + // + /// CHECK-START-MIPS64: void Main.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++) { diff --git a/test/656-checker-simd-opt/src/Main.java b/test/656-checker-simd-opt/src/Main.java index 39a126f5d3..31d28e851e 100644 --- a/test/656-checker-simd-opt/src/Main.java +++ b/test/656-checker-simd-opt/src/Main.java @@ -114,6 +114,19 @@ public class Main { /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: long Main.longInductionReduction(long[]) loop_optimization (after) + /// CHECK-DAG: <> LongConstant 0 loop:none + /// CHECK-DAG: <> LongConstant 1 loop:none + /// CHECK-DAG: <> LongConstant 2 loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: <> VecSetScalars [<>] loop:none + /// CHECK-DAG: <> Phi [<>,{{j\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none static long longInductionReduction(long[] y) { long x = 1; for (long i = 0; i < 10; i++) { @@ -141,6 +154,17 @@ public class Main { /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: void Main.intVectorLongInvariant(int[], long[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:none + /// CHECK-DAG: <> TypeConversion [<>] loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none static void intVectorLongInvariant(int[] x, long[] y) { for (int i = 0; i < 100; i++) { x[i] = (int) y[0]; @@ -170,6 +194,18 @@ public class Main { /// CHECK-DAG: <> VecAdd [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: void Main.longCanBeDoneWithInt(int[], int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-DAG: <> LongConstant 1 loop:none + /// CHECK-DAG: <> TypeConversion [<>] loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecAdd [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none static void longCanBeDoneWithInt(int[] x, int[] y) { for (int i = 0; i < 100; i++) { x[i] = (int) (y[i] + 1L); diff --git a/test/660-checker-simd-sad-byte/src/Main.java b/test/660-checker-simd-sad-byte/src/Main.java index 72d1c24dbe..877d7189a2 100644 --- a/test/660-checker-simd-sad-byte/src/Main.java +++ b/test/660-checker-simd-sad-byte/src/Main.java @@ -109,6 +109,17 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: int Main.sadByte2Int(byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 16 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static int sadByte2Int(byte[] b1, byte[] b2) { int min_length = Math.min(b1.length, b2.length); int sad = 0; @@ -140,6 +151,17 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: int Main.sadByte2IntAlt(byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 16 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static int sadByte2IntAlt(byte[] b1, byte[] b2) { int min_length = Math.min(b1.length, b2.length); int sad = 0; @@ -173,6 +195,17 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: int Main.sadByte2IntAlt2(byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 16 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static int sadByte2IntAlt2(byte[] b1, byte[] b2) { int min_length = Math.min(b1.length, b2.length); int sad = 0; @@ -212,6 +245,18 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: long Main.sadByte2Long(byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 16 loop:none + /// CHECK-DAG: <> LongConstant 0 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static long sadByte2Long(byte[] b1, byte[] b2) { int min_length = Math.min(b1.length, b2.length); long sad = 0; @@ -249,6 +294,18 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: long Main.sadByte2LongAt1(byte[], byte[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 16 loop:none + /// CHECK-DAG: <> LongConstant 1 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static long sadByte2LongAt1(byte[] b1, byte[] b2) { int min_length = Math.min(b1.length, b2.length); long sad = 1; // starts at 1 diff --git a/test/660-checker-simd-sad-char/src/Main.java b/test/660-checker-simd-sad-char/src/Main.java index 2535d49b9c..ba226149b4 100644 --- a/test/660-checker-simd-sad-char/src/Main.java +++ b/test/660-checker-simd-sad-char/src/Main.java @@ -68,7 +68,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadChar2Int(char[], char[]) loop_optimization (after) + /// CHECK-START: int Main.sadChar2Int(char[], char[]) loop_optimization (after) /// CHECK-NOT: VecSADAccumulate private static int sadChar2Int(char[] s1, char[] s2) { int min_length = Math.min(s1.length, s2.length); @@ -91,7 +91,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadChar2IntAlt(char[], char[]) loop_optimization (after) + /// CHECK-START: int Main.sadChar2IntAlt(char[], char[]) loop_optimization (after) /// CHECK-NOT: VecSADAccumulate private static int sadChar2IntAlt(char[] s1, char[] s2) { int min_length = Math.min(s1.length, s2.length); @@ -116,7 +116,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadChar2IntAlt2(char[], char[]) loop_optimization (after) + /// CHECK-START: int Main.sadChar2IntAlt2(char[], char[]) loop_optimization (after) /// CHECK-NOT: VecSADAccumulate private static int sadChar2IntAlt2(char[] s1, char[] s2) { int min_length = Math.min(s1.length, s2.length); @@ -146,7 +146,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadChar2Long(char[], char[]) loop_optimization (after) + /// CHECK-START: long Main.sadChar2Long(char[], char[]) loop_optimization (after) /// CHECK-NOT: VecSADAccumulate private static long sadChar2Long(char[] s1, char[] s2) { int min_length = Math.min(s1.length, s2.length); @@ -174,7 +174,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadChar2LongAt1(char[], char[]) loop_optimization (after) + /// CHECK-START: long Main.sadChar2LongAt1(char[], char[]) loop_optimization (after) /// CHECK-NOT: VecSADAccumulate private static long sadChar2LongAt1(char[] s1, char[] s2) { int min_length = Math.min(s1.length, s2.length); diff --git a/test/660-checker-simd-sad-int/src/Main.java b/test/660-checker-simd-sad-int/src/Main.java index 388bfba0d2..d7d5a959d7 100644 --- a/test/660-checker-simd-sad-int/src/Main.java +++ b/test/660-checker-simd-sad-int/src/Main.java @@ -48,6 +48,15 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: int Main.sadInt2Int(int[], int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none + /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\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 sadInt2Int(int[] x, int[] y) { int min_length = Math.min(x.length, y.length); int sad = 0; @@ -72,10 +81,7 @@ public class Main { // // No ABS? No SAD! // - /// CHECK-START-ARM: int Main.sadInt2IntAlt(int[], int[]) loop_optimization (after) - /// CHECK-NOT: VecSADAccumulate - // - /// CHECK-START-ARM64: int Main.sadInt2IntAlt(int[], int[]) loop_optimization (after) + /// CHECK-START: int Main.sadInt2IntAlt(int[], int[]) loop_optimization (after) /// CHECK-NOT: VecSADAccumulate private static int sadInt2IntAlt(int[] x, int[] y) { int min_length = Math.min(x.length, y.length); @@ -117,6 +123,15 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: int Main.sadInt2IntAlt2(int[], int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none + /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad [{{l\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 sadInt2IntAlt2(int[] x, int[] y) { int min_length = Math.min(x.length, y.length); int sad = 0; @@ -156,6 +171,18 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: long Main.sadInt2Long(int[], int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-DAG: <> LongConstant 0 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static long sadInt2Long(int[] x, int[] y) { int min_length = Math.min(x.length, y.length); long sad = 0; @@ -193,6 +220,18 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: long Main.sadInt2LongAt1(int[], int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-DAG: <> LongConstant 1 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static long sadInt2LongAt1(int[] x, int[] y) { int min_length = Math.min(x.length, y.length); long sad = 1; // starts at 1 diff --git a/test/660-checker-simd-sad-long/src/Main.java b/test/660-checker-simd-sad-long/src/Main.java index 06f62bd031..d080e0cf3a 100644 --- a/test/660-checker-simd-sad-long/src/Main.java +++ b/test/660-checker-simd-sad-long/src/Main.java @@ -43,6 +43,18 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: long Main.sadLong2Long(long[], long[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> LongConstant 0 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static long sadLong2Long(long[] x, long[] y) { int min_length = Math.min(x.length, y.length); long sad = 0; @@ -105,6 +117,18 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: long Main.sadLong2LongAlt2(long[], long[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> LongConstant 0 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static long sadLong2LongAlt2(long[] x, long[] y) { int min_length = Math.min(x.length, y.length); long sad = 0; @@ -142,6 +166,18 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: long Main.sadLong2LongAt1(long[], long[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> LongConstant 1 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static long sadLong2LongAt1(long[] x, long[] y) { int min_length = Math.min(x.length, y.length); long sad = 1; // starts at 1 diff --git a/test/660-checker-simd-sad-short/src/Main.java b/test/660-checker-simd-sad-short/src/Main.java index d94308e24d..4ab66827b6 100644 --- a/test/660-checker-simd-sad-short/src/Main.java +++ b/test/660-checker-simd-sad-short/src/Main.java @@ -76,6 +76,17 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: int Main.sadShort2Int(short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 8 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static int sadShort2Int(short[] s1, short[] s2) { int min_length = Math.min(s1.length, s2.length); int sad = 0; @@ -107,6 +118,17 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: int Main.sadShort2IntAlt(short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 8 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static int sadShort2IntAlt(short[] s1, short[] s2) { int min_length = Math.min(s1.length, s2.length); int sad = 0; @@ -140,6 +162,17 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: int Main.sadShort2IntAlt2(short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 8 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static int sadShort2IntAlt2(short[] s1, short[] s2) { int min_length = Math.min(s1.length, s2.length); int sad = 0; @@ -179,6 +212,18 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: long Main.sadShort2Long(short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 8 loop:none + /// CHECK-DAG: <> LongConstant 0 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static long sadShort2Long(short[] s1, short[] s2) { int min_length = Math.min(s1.length, s2.length); long sad = 0; @@ -216,6 +261,18 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: long Main.sadShort2LongAt1(short[], short[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 8 loop:none + /// CHECK-DAG: <> LongConstant 1 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static long sadShort2LongAt1(short[] s1, short[] s2) { int min_length = Math.min(s1.length, s2.length); long sad = 1; // starts at 1 diff --git a/test/660-checker-simd-sad-short2/src/Main.java b/test/660-checker-simd-sad-short2/src/Main.java index 708f3aa145..331f5ce690 100644 --- a/test/660-checker-simd-sad-short2/src/Main.java +++ b/test/660-checker-simd-sad-short2/src/Main.java @@ -94,6 +94,17 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: int Main.sadCastedChar2Int(char[], char[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 8 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static int sadCastedChar2Int(char[] s1, char[] s2) { int min_length = Math.min(s1.length, s2.length); int sad = 0; @@ -144,6 +155,17 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: int Main.sadCastedChar2IntAlt(char[], char[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 8 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static int sadCastedChar2IntAlt(char[] s1, char[] s2) { int min_length = Math.min(s1.length, s2.length); int sad = 0; @@ -196,6 +218,17 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: int Main.sadCastedChar2IntAlt2(char[], char[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 8 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static int sadCastedChar2IntAlt2(char[] s1, char[] s2) { int min_length = Math.min(s1.length, s2.length); int sad = 0; @@ -254,6 +287,18 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: long Main.sadCastedChar2Long(char[], char[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 8 loop:none + /// CHECK-DAG: <> LongConstant 0 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static long sadCastedChar2Long(char[] s1, char[] s2) { int min_length = Math.min(s1.length, s2.length); long sad = 0; @@ -310,6 +355,18 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: long Main.sadCastedChar2LongAt1(char[], char[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 8 loop:none + /// CHECK-DAG: <> LongConstant 1 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static long sadCastedChar2LongAt1(char[] s1, char[] s2) { int min_length = Math.min(s1.length, s2.length); long sad = 1; // starts at 1 diff --git a/test/660-checker-simd-sad-short3/src/Main.java b/test/660-checker-simd-sad-short3/src/Main.java index c8850b41e4..ecda8848c3 100644 --- a/test/660-checker-simd-sad-short3/src/Main.java +++ b/test/660-checker-simd-sad-short3/src/Main.java @@ -44,6 +44,18 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: int Main.sadShort2IntParamRight(short[], short) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 8 loop:none + /// CHECK-DAG: <> ParameterValue 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 sadShort2IntParamRight(short[] s, short param) { int sad = 0; for (int i = 0; i < s.length; i++) { @@ -75,6 +87,18 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: int Main.sadShort2IntParamLeft(short[], short) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 8 loop:none + /// CHECK-DAG: <> ParameterValue 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 sadShort2IntParamLeft(short[] s, short param) { int sad = 0; for (int i = 0; i < s.length; i++) { @@ -106,6 +130,18 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: int Main.sadShort2IntConstRight(short[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 8 loop:none + /// CHECK-DAG: <> IntConstant 32767 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 sadShort2IntConstRight(short[] s) { int sad = 0; for (int i = 0; i < s.length; i++) { @@ -137,6 +173,18 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: int Main.sadShort2IntConstLeft(short[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 8 loop:none + /// CHECK-DAG: <> IntConstant 32767 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 sadShort2IntConstLeft(short[] s) { int sad = 0; for (int i = 0; i < s.length; i++) { @@ -168,6 +216,18 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: int Main.sadShort2IntInvariantRight(short[], int) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 8 loop:none + /// CHECK-DAG: <> TypeConversion [{{i\d+}}] 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 sadShort2IntInvariantRight(short[] s, int val) { int sad = 0; short x = (short) (val + 1); @@ -199,6 +259,17 @@ public class Main { /// 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-START-MIPS64: int Main.sadShort2IntInvariantLeft(short[], int) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 8 loop:none + /// CHECK-DAG: <> TypeConversion [{{i\d+}}] 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 private static int sadShort2IntInvariantLeft(short[] s, int val) { int sad = 0; short x = (short) (val + 1); @@ -233,6 +304,18 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecAdd [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: int Main.sadShort2IntCastedExprRight(short[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 8 loop:none + /// CHECK-DAG: <> IntConstant 110 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: <> VecAdd [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none private static int sadShort2IntCastedExprRight(short[] s) { int sad = 0; for (int i = 0; i < s.length; i++) { @@ -267,6 +350,18 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecAdd [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-MIPS64: int Main.sadShort2IntCastedExprLeft(short[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 8 loop:none + /// CHECK-DAG: <> IntConstant 110 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: <> VecAdd [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none private static int sadShort2IntCastedExprLeft(short[] s) { int sad = 0; for (int i = 0; i < s.length; i++) { -- GitLab From 92f7f3ce3b01f7c7df1c15b81c900e087248093f Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 31 Oct 2017 11:38:30 +0000 Subject: [PATCH 046/226] Use intrinsic codegen for compiling intrinsic methods. When compiling an intrinsic method, generate a graph that invokes the same method and try to compile it. If the call is actually intrinsified (or simplified to other HIR) and yields a leaf method, use the result of this compilation attempt, otherwise compile the actual code or JNI stub. Note that CodeGenerator::CreateThrowingSlowPathLocations() actually marks the locations as kNoCall if the throw is not in a catch block, thus considering some throwing methods (for example, String.charAt()) as leaf methods. We would ideally want to use the intrinsic codegen for all intrinsics that do not generate a slow-path call to the default implementation. Relying on the leaf method is suboptimal as we're missing out on methods that do other types of calls, for example runtime calls. This shall be fixed in a subsequent CL. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Bug: 67717501 Change-Id: I640fda7c22d4ff494b5ff77ebec3b7f5f75af652 --- compiler/compiled_method.cc | 8 +- compiler/compiled_method.h | 48 ++- compiler/compiler.h | 3 +- compiler/debug/elf_debug_info_writer.h | 2 +- compiler/driver/compiler_driver.cc | 3 +- compiler/optimizing/block_builder.cc | 54 ++- compiler/optimizing/block_builder.h | 10 +- compiler/optimizing/builder.cc | 84 ++++- compiler/optimizing/builder.h | 11 +- compiler/optimizing/code_generator.cc | 6 +- compiler/optimizing/code_generator.h | 2 +- compiler/optimizing/inliner.cc | 1 + compiler/optimizing/instruction_builder.cc | 78 ++++- compiler/optimizing/instruction_builder.h | 5 +- compiler/optimizing/optimizing_compiler.cc | 320 +++++++++++++----- compiler/optimizing/optimizing_unit_test.h | 4 +- .../prepare_for_register_allocation.cc | 20 +- compiler/optimizing/stack_map_stream.cc | 6 +- compiler/optimizing/stack_map_stream.h | 2 +- dex2oat/linker/oat_writer.cc | 7 +- runtime/stack_map.h | 6 +- 21 files changed, 528 insertions(+), 152 deletions(-) diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc index fc6a717aa6..e41371855d 100644 --- a/compiler/compiled_method.cc +++ b/compiler/compiled_method.cc @@ -26,8 +26,8 @@ CompiledCode::CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set, const ArrayRef& quick_code) : compiler_driver_(compiler_driver), - instruction_set_(instruction_set), - quick_code_(compiler_driver_->GetCompiledMethodStorage()->DeduplicateCode(quick_code)) { + quick_code_(compiler_driver_->GetCompiledMethodStorage()->DeduplicateCode(quick_code)), + packed_fields_(InstructionSetField::Encode(instruction_set)) { } CompiledCode::~CompiledCode() { @@ -48,7 +48,7 @@ bool CompiledCode::operator==(const CompiledCode& rhs) const { } size_t CompiledCode::AlignCode(size_t offset) const { - return AlignCode(offset, instruction_set_); + return AlignCode(offset, GetInstructionSet()); } size_t CompiledCode::AlignCode(size_t offset, InstructionSet instruction_set) { @@ -56,7 +56,7 @@ size_t CompiledCode::AlignCode(size_t offset, InstructionSet instruction_set) { } size_t CompiledCode::CodeDelta() const { - return CodeDelta(instruction_set_); + return CodeDelta(GetInstructionSet()); } size_t CompiledCode::CodeDelta(InstructionSet instruction_set) { diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h index 892bc592db..acdce260e5 100644 --- a/compiler/compiled_method.h +++ b/compiler/compiled_method.h @@ -22,6 +22,8 @@ #include #include "arch/instruction_set.h" +#include "base/bit_field.h" +#include "base/bit_utils.h" namespace art { @@ -44,7 +46,7 @@ class CompiledCode { virtual ~CompiledCode(); InstructionSet GetInstructionSet() const { - return instruction_set_; + return GetPackedField(); } ArrayRef GetQuickCode() const; @@ -68,6 +70,11 @@ class CompiledCode { static const void* CodePointer(const void* code_pointer, InstructionSet instruction_set); protected: + static constexpr size_t kInstructionSetFieldSize = + MinimumBitsToStore(static_cast(InstructionSet::kLast)); + static constexpr size_t kNumberOfCompiledCodePackedBits = kInstructionSetFieldSize; + static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte; + template static ArrayRef GetArray(const LengthPrefixedArray* array); @@ -75,13 +82,26 @@ class CompiledCode { return compiler_driver_; } + template + typename BitFieldType::value_type GetPackedField() const { + return BitFieldType::Decode(packed_fields_); + } + + template + void SetPackedField(typename BitFieldType::value_type value) { + DCHECK(IsUint(static_cast(value))); + packed_fields_ = BitFieldType::Update(value, packed_fields_); + } + private: - CompilerDriver* const compiler_driver_; + using InstructionSetField = BitField; - const InstructionSet instruction_set_; + CompilerDriver* const compiler_driver_; - // Used to store the PIC code for Quick. + // Used to store the compiled code. const LengthPrefixedArray* const quick_code_; + + uint32_t packed_fields_; }; class CompiledMethod FINAL : public CompiledCode { @@ -116,6 +136,18 @@ class CompiledMethod FINAL : public CompiledCode { static void ReleaseSwapAllocatedCompiledMethod(CompilerDriver* driver, CompiledMethod* m); + bool IsIntrinsic() const { + return GetPackedField(); + } + + // Marks the compiled method as being generated using an intrinsic codegen. + // Such methods have no relationships to their code items. + // This affects debug information generated at link time. + void MarkAsIntrinsic() { + DCHECK(!IsIntrinsic()); + SetPackedField(/* value */ true); + } + size_t GetFrameSizeInBytes() const { return frame_size_in_bytes_; } @@ -137,6 +169,14 @@ class CompiledMethod FINAL : public CompiledCode { ArrayRef GetPatches() const; private: + static constexpr size_t kIsIntrinsicLsb = kNumberOfCompiledCodePackedBits; + static constexpr size_t kIsIntrinsicSize = 1u; + static constexpr size_t kNumberOfCompiledMethodPackedBits = kIsIntrinsicLsb + kIsIntrinsicSize; + static_assert(kNumberOfCompiledMethodPackedBits <= CompiledCode::kMaxNumberOfPackedBits, + "Too many packed fields."); + + using IsIntrinsicField = BitField; + // For quick code, the size of the activation used by the code. const size_t frame_size_in_bytes_; // For quick code, a bit mask describing spilled GPR callee-save registers. diff --git a/compiler/compiler.h b/compiler/compiler.h index 3aa84f8e2b..85abd6654c 100644 --- a/compiler/compiler.h +++ b/compiler/compiler.h @@ -65,7 +65,8 @@ class Compiler { virtual CompiledMethod* JniCompile(uint32_t access_flags, uint32_t method_idx, - const DexFile& dex_file) const = 0; + const DexFile& dex_file, + Handle dex_cache) const = 0; virtual bool JitCompile(Thread* self ATTRIBUTE_UNUSED, jit::JitCodeCache* code_cache ATTRIBUTE_UNUSED, diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index 2b617273b5..37c2d32091 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -202,7 +202,7 @@ class ElfCompilationUnitWriter { // Decode dex register locations for all stack maps. // It might be expensive, so do it just once and reuse the result. std::vector dex_reg_maps; - if (mi->code_info != nullptr) { + if (dex_code != nullptr && 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) { diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index f4700d4040..726401d09e 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -516,7 +516,8 @@ static void CompileMethod(Thread* self, access_flags |= annotations::GetNativeMethodAnnotationAccessFlags( dex_file, dex_file.GetClassDef(class_def_idx), method_idx); - compiled_method = driver->GetCompiler()->JniCompile(access_flags, method_idx, dex_file); + compiled_method = driver->GetCompiler()->JniCompile( + access_flags, method_idx, dex_file, dex_cache); CHECK(compiled_method != nullptr); } } else if ((access_flags & kAccAbstract) != 0) { diff --git a/compiler/optimizing/block_builder.cc b/compiler/optimizing/block_builder.cc index 2432f13044..a6687fe258 100644 --- a/compiler/optimizing/block_builder.cc +++ b/compiler/optimizing/block_builder.cc @@ -40,20 +40,20 @@ bool HBasicBlockBuilder::CreateBranchTargets() { // Create the first block for the dex instructions, single successor of the entry block. MaybeCreateBlockAt(0u); - if (code_item_.tries_size_ != 0) { + if (code_item_->tries_size_ != 0) { // Create branch targets at the start/end of the TryItem range. These are // places where the program might fall through into/out of the a block and // where TryBoundary instructions will be inserted later. Other edges which // enter/exit the try blocks are a result of branches/switches. - for (size_t idx = 0; idx < code_item_.tries_size_; ++idx) { - const DexFile::TryItem* try_item = DexFile::GetTryItems(code_item_, idx); + for (size_t idx = 0; idx < code_item_->tries_size_; ++idx) { + const DexFile::TryItem* try_item = DexFile::GetTryItems(*code_item_, idx); uint32_t dex_pc_start = try_item->start_addr_; uint32_t dex_pc_end = dex_pc_start + try_item->insn_count_; MaybeCreateBlockAt(dex_pc_start); - if (dex_pc_end < code_item_.insns_size_in_code_units_) { + if (dex_pc_end < code_item_->insns_size_in_code_units_) { // TODO: Do not create block if the last instruction cannot fall through. MaybeCreateBlockAt(dex_pc_end); - } else if (dex_pc_end == code_item_.insns_size_in_code_units_) { + } else if (dex_pc_end == code_item_->insns_size_in_code_units_) { // The TryItem spans until the very end of the CodeItem and therefore // cannot have any code afterwards. } else { @@ -63,7 +63,7 @@ bool HBasicBlockBuilder::CreateBranchTargets() { } // Create branch targets for exception handlers. - const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(code_item_, 0); + const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*code_item_, 0); uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); for (uint32_t idx = 0; idx < handlers_size; ++idx) { CatchHandlerIterator iterator(handlers_ptr); @@ -76,7 +76,7 @@ bool HBasicBlockBuilder::CreateBranchTargets() { // Iterate over all instructions and find branching instructions. Create blocks for // the locations these instructions branch to. - IterationRange instructions = code_item_.Instructions(); + IterationRange instructions = code_item_->Instructions(); for (const DexInstructionPcPair& pair : instructions) { const uint32_t dex_pc = pair.DexPc(); const Instruction& instruction = pair.Inst(); @@ -127,7 +127,7 @@ void HBasicBlockBuilder::ConnectBasicBlocks() { bool is_throwing_block = false; // Calculate the qucikening index here instead of CreateBranchTargets since it's easier to // calculate in dex_pc order. - for (const DexInstructionPcPair& pair : code_item_.Instructions()) { + for (const DexInstructionPcPair& pair : code_item_->Instructions()) { const uint32_t dex_pc = pair.DexPc(); const Instruction& instruction = pair.Inst(); @@ -229,7 +229,7 @@ bool HBasicBlockBuilder::MightHaveLiveNormalPredecessors(HBasicBlock* catch_bloc } } - const Instruction& first = code_item_.InstructionAt(catch_block->GetDexPc()); + const Instruction& first = code_item_->InstructionAt(catch_block->GetDexPc()); if (first.Opcode() == Instruction::MOVE_EXCEPTION) { // Verifier guarantees that if a catch block begins with MOVE_EXCEPTION then // it has no live normal predecessors. @@ -247,7 +247,7 @@ bool HBasicBlockBuilder::MightHaveLiveNormalPredecessors(HBasicBlock* catch_bloc } void HBasicBlockBuilder::InsertTryBoundaryBlocks() { - if (code_item_.tries_size_ == 0) { + if (code_item_->tries_size_ == 0) { return; } @@ -269,12 +269,12 @@ void HBasicBlockBuilder::InsertTryBoundaryBlocks() { // loop for synchronized blocks. if (ContainsElement(throwing_blocks_, block)) { // Try to find a TryItem covering the block. - const int32_t try_item_idx = DexFile::FindTryItem(DexFile::GetTryItems(code_item_, 0u), - code_item_.tries_size_, + const int32_t try_item_idx = DexFile::FindTryItem(DexFile::GetTryItems(*code_item_, 0u), + code_item_->tries_size_, block->GetDexPc()); if (try_item_idx != -1) { // Block throwing and in a TryItem. Store the try block information. - try_block_info.Put(block->GetBlockId(), DexFile::GetTryItems(code_item_, try_item_idx)); + try_block_info.Put(block->GetBlockId(), DexFile::GetTryItems(*code_item_, try_item_idx)); } } } @@ -285,7 +285,7 @@ void HBasicBlockBuilder::InsertTryBoundaryBlocks() { // Iterate over catch blocks, create artifical landing pads if necessary to // simplify the CFG, and set metadata. - const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(code_item_, 0); + const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*code_item_, 0); uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); for (uint32_t idx = 0; idx < handlers_size; ++idx) { CatchHandlerIterator iterator(handlers_ptr); @@ -333,7 +333,7 @@ void HBasicBlockBuilder::InsertTryBoundaryBlocks() { HTryBoundary* try_entry = new (allocator_) HTryBoundary( HTryBoundary::BoundaryKind::kEntry, try_block->GetDexPc()); try_block->CreateImmediateDominator()->AddInstruction(try_entry); - LinkToCatchBlocks(try_entry, code_item_, try_item, catch_blocks); + LinkToCatchBlocks(try_entry, *code_item_, try_item, catch_blocks); break; } } @@ -361,12 +361,13 @@ void HBasicBlockBuilder::InsertTryBoundaryBlocks() { HTryBoundary* try_exit = new (allocator_) HTryBoundary(HTryBoundary::BoundaryKind::kExit, successor->GetDexPc()); graph_->SplitEdge(try_block, successor)->AddInstruction(try_exit); - LinkToCatchBlocks(try_exit, code_item_, try_item, catch_blocks); + LinkToCatchBlocks(try_exit, *code_item_, try_item, catch_blocks); } } } bool HBasicBlockBuilder::Build() { + DCHECK(code_item_ != nullptr); DCHECK(graph_->GetBlocks().empty()); graph_->SetEntryBlock(new (allocator_) HBasicBlock(graph_, kNoDexPc)); @@ -383,6 +384,27 @@ bool HBasicBlockBuilder::Build() { return true; } +void HBasicBlockBuilder::BuildIntrinsic() { + DCHECK(code_item_ == nullptr); + DCHECK(graph_->GetBlocks().empty()); + + // Create blocks. + HBasicBlock* entry_block = new (allocator_) HBasicBlock(graph_, kNoDexPc); + HBasicBlock* exit_block = new (allocator_) HBasicBlock(graph_, kNoDexPc); + HBasicBlock* body = MaybeCreateBlockAt(/* semantic_dex_pc */ kNoDexPc, /* store_dex_pc */ 0u); + + // Add blocks to the graph. + graph_->AddBlock(entry_block); + graph_->AddBlock(body); + graph_->AddBlock(exit_block); + graph_->SetEntryBlock(entry_block); + graph_->SetExitBlock(exit_block); + + // Connect blocks. + entry_block->AddSuccessor(body); + body->AddSuccessor(exit_block); +} + size_t HBasicBlockBuilder::GetQuickenIndex(uint32_t dex_pc) const { return quicken_index_for_dex_pc_.Get(dex_pc); } diff --git a/compiler/optimizing/block_builder.h b/compiler/optimizing/block_builder.h index 79f7a7bc81..7d0f56db34 100644 --- a/compiler/optimizing/block_builder.h +++ b/compiler/optimizing/block_builder.h @@ -28,14 +28,15 @@ class HBasicBlockBuilder : public ValueObject { public: HBasicBlockBuilder(HGraph* graph, const DexFile* const dex_file, - const DexFile::CodeItem& code_item, + const DexFile::CodeItem* code_item, ScopedArenaAllocator* local_allocator) : allocator_(graph->GetAllocator()), graph_(graph), dex_file_(dex_file), code_item_(code_item), local_allocator_(local_allocator), - branch_targets_(code_item.insns_size_in_code_units_, + branch_targets_(code_item != nullptr ? code_item->insns_size_in_code_units_ + : /* fake dex_pc=0 for intrinsic graph */ 1u, nullptr, local_allocator->Adapter(kArenaAllocGraphBuilder)), throwing_blocks_(kDefaultNumberOfThrowingBlocks, @@ -50,6 +51,9 @@ class HBasicBlockBuilder : public ValueObject { // exits a try block. bool Build(); + // Creates basic blocks in `graph_` for compiling an intrinsic. + void BuildIntrinsic(); + size_t GetNumberOfBranches() const { return number_of_branches_; } HBasicBlock* GetBlockAt(uint32_t dex_pc) const { return branch_targets_[dex_pc]; } @@ -79,7 +83,7 @@ class HBasicBlockBuilder : public ValueObject { HGraph* const graph_; const DexFile* const dex_file_; - const DexFile::CodeItem& code_item_; + const DexFile::CodeItem* const code_item_; // null for intrinsic graph. ScopedArenaAllocator* const local_allocator_; ScopedArenaVector branch_targets_; diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 4ed1612220..d73ef1f3a1 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -24,6 +24,7 @@ #include "data_type-inl.h" #include "dex/verified_method.h" #include "driver/compiler_options.h" +#include "driver/dex_compilation_unit.h" #include "instruction_builder.h" #include "mirror/class_loader.h" #include "mirror/dex_cache.h" @@ -36,6 +37,7 @@ namespace art { HGraphBuilder::HGraphBuilder(HGraph* graph, + const DexFile::CodeItem* code_item, const DexCompilationUnit* dex_compilation_unit, const DexCompilationUnit* outer_compilation_unit, CompilerDriver* driver, @@ -45,7 +47,7 @@ HGraphBuilder::HGraphBuilder(HGraph* graph, VariableSizedHandleScope* handles) : graph_(graph), dex_file_(&graph->GetDexFile()), - code_item_(*dex_compilation_unit->GetCodeItem()), + code_item_(code_item), dex_compilation_unit_(dex_compilation_unit), outer_compilation_unit_(outer_compilation_unit), compiler_driver_(driver), @@ -67,23 +69,21 @@ bool HGraphBuilder::SkipCompilation(size_t number_of_branches) { return false; } - if (compiler_options.IsHugeMethod(code_item_.insns_size_in_code_units_)) { + if (compiler_options.IsHugeMethod(code_item_->insns_size_in_code_units_)) { VLOG(compiler) << "Skip compilation of huge method " << dex_file_->PrettyMethod(dex_compilation_unit_->GetDexMethodIndex()) - << ": " << code_item_.insns_size_in_code_units_ << " code units"; - MaybeRecordStat(compilation_stats_, - MethodCompilationStat::kNotCompiledHugeMethod); + << ": " << code_item_->insns_size_in_code_units_ << " code units"; + MaybeRecordStat(compilation_stats_, MethodCompilationStat::kNotCompiledHugeMethod); return true; } // If it's large and contains no branches, it's likely to be machine generated initialization. - if (compiler_options.IsLargeMethod(code_item_.insns_size_in_code_units_) + if (compiler_options.IsLargeMethod(code_item_->insns_size_in_code_units_) && (number_of_branches == 0)) { VLOG(compiler) << "Skip compilation of large method with no branch " << dex_file_->PrettyMethod(dex_compilation_unit_->GetDexMethodIndex()) - << ": " << code_item_.insns_size_in_code_units_ << " code units"; - MaybeRecordStat(compilation_stats_, - MethodCompilationStat::kNotCompiledLargeMethodNoBranches); + << ": " << code_item_->insns_size_in_code_units_ << " code units"; + MaybeRecordStat(compilation_stats_, MethodCompilationStat::kNotCompiledLargeMethodNoBranches); return true; } @@ -91,12 +91,13 @@ bool HGraphBuilder::SkipCompilation(size_t number_of_branches) { } GraphAnalysisResult HGraphBuilder::BuildGraph() { + DCHECK(code_item_ != nullptr); DCHECK(graph_->GetBlocks().empty()); - graph_->SetNumberOfVRegs(code_item_.registers_size_); - graph_->SetNumberOfInVRegs(code_item_.ins_size_); - graph_->SetMaximumNumberOfOutVRegs(code_item_.outs_size_); - graph_->SetHasTryCatch(code_item_.tries_size_ != 0); + graph_->SetNumberOfVRegs(code_item_->registers_size_); + graph_->SetNumberOfInVRegs(code_item_->ins_size_); + graph_->SetMaximumNumberOfOutVRegs(code_item_->outs_size_); + graph_->SetHasTryCatch(code_item_->tries_size_ != 0); // Use ScopedArenaAllocator for all local allocations. ScopedArenaAllocator local_allocator(graph_->GetArenaStack()); @@ -148,4 +149,61 @@ GraphAnalysisResult HGraphBuilder::BuildGraph() { return ssa_builder.BuildSsa(); } +void HGraphBuilder::BuildIntrinsicGraph(ArtMethod* method) { + DCHECK(code_item_ == nullptr); + DCHECK(graph_->GetBlocks().empty()); + + // Determine the number of arguments and associated vregs. + uint32_t method_idx = dex_compilation_unit_->GetDexMethodIndex(); + const char* shorty = dex_file_->GetMethodShorty(dex_file_->GetMethodId(method_idx)); + size_t num_args = strlen(shorty + 1); + size_t num_wide_args = std::count(shorty + 1, shorty + 1 + num_args, 'J') + + std::count(shorty + 1, shorty + 1 + num_args, 'D'); + size_t num_arg_vregs = num_args + num_wide_args + (dex_compilation_unit_->IsStatic() ? 0u : 1u); + + // For simplicity, reserve 2 vregs (the maximum) for return value regardless of the return type. + size_t return_vregs = 2u; + graph_->SetNumberOfVRegs(return_vregs + num_arg_vregs); + graph_->SetNumberOfInVRegs(num_arg_vregs); + graph_->SetMaximumNumberOfOutVRegs(num_arg_vregs); + graph_->SetHasTryCatch(false); + + // Use ScopedArenaAllocator for all local allocations. + ScopedArenaAllocator local_allocator(graph_->GetArenaStack()); + HBasicBlockBuilder block_builder(graph_, dex_file_, /* code_item */ nullptr, &local_allocator); + SsaBuilder ssa_builder(graph_, + dex_compilation_unit_->GetClassLoader(), + dex_compilation_unit_->GetDexCache(), + handles_, + &local_allocator); + HInstructionBuilder instruction_builder(graph_, + &block_builder, + &ssa_builder, + dex_file_, + /* code_item */ nullptr, + return_type_, + dex_compilation_unit_, + outer_compilation_unit_, + compiler_driver_, + code_generator_, + interpreter_metadata_, + compilation_stats_, + handles_, + &local_allocator); + + // 1) Create basic blocks for the intrinsic and link them together. + block_builder.BuildIntrinsic(); + + // 2) Build the trivial dominator tree. + GraphAnalysisResult bdt_result = graph_->BuildDominatorTree(); + DCHECK_EQ(bdt_result, kAnalysisSuccess); + + // 3) Populate basic blocks with instructions for the intrinsic. + instruction_builder.BuildIntrinsic(method); + + // 4) Type the graph (no dead/redundant phis to eliminate). + GraphAnalysisResult build_ssa_result = ssa_builder.BuildSsa(); + DCHECK_EQ(build_ssa_result, kAnalysisSuccess); +} + } // namespace art diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 5a860f1e43..0bb3a051f7 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -21,17 +21,19 @@ #include "dex_file-inl.h" #include "dex_file.h" #include "driver/compiler_driver.h" -#include "driver/dex_compilation_unit.h" #include "nodes.h" namespace art { +class ArtMethod; class CodeGenerator; +class DexCompilationUnit; class OptimizingCompilerStats; class HGraphBuilder : public ValueObject { public: HGraphBuilder(HGraph* graph, + const DexFile::CodeItem* code_item, const DexCompilationUnit* dex_compilation_unit, const DexCompilationUnit* outer_compilation_unit, CompilerDriver* driver, @@ -47,8 +49,8 @@ class HGraphBuilder : public ValueObject { VariableSizedHandleScope* handles, DataType::Type return_type = DataType::Type::kInt32) : graph_(graph), - dex_file_(dex_compilation_unit->GetDexFile()), - code_item_(code_item), + dex_file_(&graph->GetDexFile()), + code_item_(&code_item), dex_compilation_unit_(dex_compilation_unit), outer_compilation_unit_(nullptr), compiler_driver_(nullptr), @@ -59,6 +61,7 @@ class HGraphBuilder : public ValueObject { return_type_(return_type) {} GraphAnalysisResult BuildGraph(); + void BuildIntrinsicGraph(ArtMethod* method); static constexpr const char* kBuilderPassName = "builder"; @@ -67,7 +70,7 @@ class HGraphBuilder : public ValueObject { HGraph* const graph_; const DexFile* const dex_file_; - const DexFile::CodeItem& code_item_; + const DexFile::CodeItem* const code_item_; // null for intrinsic graph. // The compilation unit of the current method being compiled. Note that // it can be an inlined method. diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 015a6a04d3..0bd3ce937a 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -946,12 +946,12 @@ static void CheckLoopEntriesCanBeUsedForOsr(const HGraph& graph, void CodeGenerator::BuildStackMaps(MemoryRegion stack_map_region, MemoryRegion method_info_region, - const DexFile::CodeItem& code_item) { + const DexFile::CodeItem* code_item_for_osr_check) { StackMapStream* stack_map_stream = GetStackMapStream(); stack_map_stream->FillInCodeInfo(stack_map_region); stack_map_stream->FillInMethodInfo(method_info_region); - if (kIsDebugBuild) { - CheckLoopEntriesCanBeUsedForOsr(*graph_, CodeInfo(stack_map_region), code_item); + if (kIsDebugBuild && code_item_for_osr_check != nullptr) { + CheckLoopEntriesCanBeUsedForOsr(*graph_, CodeInfo(stack_map_region), *code_item_for_osr_check); } } diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 18ad60db87..08e4462356 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -346,7 +346,7 @@ class CodeGenerator : public DeletableArenaObject { void BuildStackMaps(MemoryRegion stack_map_region, MemoryRegion method_info_region, - const DexFile::CodeItem& code_item); + const DexFile::CodeItem* code_item_for_osr_check); void ComputeStackMapAndMethodInfoSize(size_t* stack_map_size, size_t* method_info_size); size_t GetNumberOfJitRoots() const; diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 7adb196d14..3f4a3d8b8e 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -1667,6 +1667,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, } } HGraphBuilder builder(callee_graph, + code_item, &dex_compilation_unit, &outer_compilation_unit_, compiler_driver_, diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 8e9b818722..61840cc20f 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -272,6 +272,7 @@ static bool IsBlockPopulated(HBasicBlock* block) { } bool HInstructionBuilder::Build() { + DCHECK(code_item_ != nullptr); locals_for_.resize( graph_->GetBlocks().size(), ScopedArenaVector(local_allocator_->Adapter(kArenaAllocGraphBuilder))); @@ -321,7 +322,7 @@ bool HInstructionBuilder::Build() { quicken_index = block_builder_->GetQuickenIndex(block_dex_pc); } - for (const DexInstructionPcPair& pair : code_item_.Instructions(block_dex_pc)) { + for (const DexInstructionPcPair& pair : code_item_->Instructions(block_dex_pc)) { if (current_block_ == nullptr) { // The previous instruction ended this block. break; @@ -364,6 +365,73 @@ bool HInstructionBuilder::Build() { return true; } +void HInstructionBuilder::BuildIntrinsic(ArtMethod* method) { + DCHECK(code_item_ == nullptr); + DCHECK(method->IsIntrinsic()); + + locals_for_.resize( + graph_->GetBlocks().size(), + ScopedArenaVector(local_allocator_->Adapter(kArenaAllocGraphBuilder))); + + // Fill the entry block. Do not add suspend check, we do not want a suspend + // check in intrinsics; intrinsic methods are supposed to be fast. + current_block_ = graph_->GetEntryBlock(); + InitializeBlockLocals(); + InitializeParameters(); + AppendInstruction(new (allocator_) HGoto(0u)); + + // Fill the body. + current_block_ = current_block_->GetSingleSuccessor(); + InitializeBlockLocals(); + DCHECK(!IsBlockPopulated(current_block_)); + + // Add the invoke and return instruction. Use HInvokeStaticOrDirect even + // for methods that would normally use an HInvokeVirtual (sharpen the call). + size_t in_vregs = graph_->GetNumberOfInVRegs(); + size_t number_of_arguments = + in_vregs - std::count(current_locals_->end() - in_vregs, current_locals_->end(), nullptr); + uint32_t method_idx = dex_compilation_unit_->GetDexMethodIndex(); + MethodReference target_method(dex_file_, method_idx); + HInvokeStaticOrDirect::DispatchInfo dispatch_info = { + HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall, + HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod, + /* method_load_data */ 0u + }; + InvokeType invoke_type = dex_compilation_unit_->IsStatic() ? kStatic : kDirect; + HInvokeStaticOrDirect* invoke = new (allocator_) HInvokeStaticOrDirect( + allocator_, + number_of_arguments, + return_type_, + kNoDexPc, + method_idx, + method, + dispatch_info, + invoke_type, + target_method, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HandleInvoke(invoke, + in_vregs, + /* args */ nullptr, + graph_->GetNumberOfVRegs() - in_vregs, + /* is_range */ true, + dex_file_->GetMethodShorty(method_idx), + /* clinit_check */ nullptr, + /* is_unresolved */ false); + + // Add the return instruction. + if (return_type_ == DataType::Type::kVoid) { + AppendInstruction(new (allocator_) HReturnVoid()); + } else { + AppendInstruction(new (allocator_) HReturn(invoke)); + } + + // Fill the exit block. + DCHECK_EQ(current_block_->GetSingleSuccessor(), graph_->GetExitBlock()); + current_block_ = graph_->GetExitBlock(); + InitializeBlockLocals(); + AppendInstruction(new (allocator_) HExit()); +} + ArenaBitVector* HInstructionBuilder::FindNativeDebugInfoLocations() { // The callback gets called when the line number changes. // In other words, it marks the start of new java statement. @@ -373,15 +441,15 @@ ArenaBitVector* HInstructionBuilder::FindNativeDebugInfoLocations() { return false; } }; - const uint32_t num_instructions = code_item_.insns_size_in_code_units_; + const uint32_t num_instructions = code_item_->insns_size_in_code_units_; ArenaBitVector* locations = ArenaBitVector::Create(local_allocator_, num_instructions, /* expandable */ false, kArenaAllocGraphBuilder); locations->ClearAllBits(); - dex_file_->DecodeDebugPositionInfo(&code_item_, Callback::Position, locations); + dex_file_->DecodeDebugPositionInfo(code_item_, Callback::Position, locations); // Instruction-specific tweaks. - IterationRange instructions = code_item_.Instructions(); + IterationRange instructions = code_item_->Instructions(); for (const DexInstructionPcPair& inst : instructions) { switch (inst->Opcode()) { case Instruction::MOVE_EXCEPTION: { @@ -1641,7 +1709,7 @@ void HInstructionBuilder::BuildFillArrayData(const Instruction& instruction, uin int32_t payload_offset = instruction.VRegB_31t() + dex_pc; const Instruction::ArrayDataPayload* payload = - reinterpret_cast(code_item_.insns_ + payload_offset); + reinterpret_cast(code_item_->insns_ + payload_offset); const uint8_t* data = payload->data; uint32_t element_count = payload->element_count; diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index 058b711687..f551ac4280 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -50,7 +50,7 @@ class HInstructionBuilder : public ValueObject { HBasicBlockBuilder* block_builder, SsaBuilder* ssa_builder, const DexFile* dex_file, - const DexFile::CodeItem& code_item, + const DexFile::CodeItem* code_item, DataType::Type return_type, const DexCompilationUnit* dex_compilation_unit, const DexCompilationUnit* outer_compilation_unit, @@ -85,6 +85,7 @@ class HInstructionBuilder : public ValueObject { } bool Build(); + void BuildIntrinsic(ArtMethod* method); private: void InitializeBlockLocals(); @@ -327,7 +328,7 @@ class HInstructionBuilder : public ValueObject { // The dex file where the method being compiled is, and the bytecode data. const DexFile* const dex_file_; - const DexFile::CodeItem& code_item_; + const DexFile::CodeItem* const code_item_; // null for intrinsic graph. // The return type of the method being compiled. const DataType::Type return_type_; diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 2bba985c34..4974ed0ec5 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -311,12 +311,8 @@ class OptimizingCompiler FINAL : public Compiler { CompiledMethod* JniCompile(uint32_t access_flags, uint32_t method_idx, - const DexFile& dex_file) const OVERRIDE { - return ArtQuickJniCompileMethod(GetCompilerDriver(), - access_flags, - method_idx, - dex_file); - } + const DexFile& dex_file, + Handle dex_cache) const OVERRIDE; uintptr_t GetEntryPointOf(ArtMethod* method) const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { @@ -366,18 +362,18 @@ class OptimizingCompiler FINAL : public Compiler { CodeGenerator* TryCompile(ArenaAllocator* allocator, ArenaStack* arena_stack, CodeVectorAllocator* code_allocator, - const DexFile::CodeItem* code_item, - uint32_t access_flags, - InvokeType invoke_type, - uint16_t class_def_idx, - uint32_t method_idx, - Handle class_loader, - const DexFile& dex_file, - Handle dex_cache, + const DexCompilationUnit& dex_compilation_unit, ArtMethod* method, bool osr, VariableSizedHandleScope* handles) const; + CodeGenerator* TryCompileIntrinsic(ArenaAllocator* allocator, + ArenaStack* arena_stack, + CodeVectorAllocator* code_allocator, + const DexCompilationUnit& dex_compilation_unit, + ArtMethod* method, + VariableSizedHandleScope* handles) const; + void MaybeRunInliner(HGraph* graph, CodeGenerator* codegen, CompilerDriver* driver, @@ -790,7 +786,7 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph, driver, dex_compilation_unit, handles); - RunOptimizations(&optimizations[0], optimizations.size(), pass_observer); + RunOptimizations(optimizations.data(), optimizations.size(), pass_observer); return; } @@ -895,7 +891,7 @@ CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, CodeVectorAllocator* code_allocator, CodeGenerator* codegen, CompilerDriver* compiler_driver, - const DexFile::CodeItem* code_item) const { + const DexFile::CodeItem* code_item_for_osr_check) const { ArenaVector linker_patches = EmitAndSortLinkerPatches(codegen); ArenaVector stack_map(allocator->Adapter(kArenaAllocStackMaps)); ArenaVector method_info(allocator->Adapter(kArenaAllocStackMaps)); @@ -906,7 +902,7 @@ CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, method_info.resize(method_info_size); codegen->BuildStackMaps(MemoryRegion(stack_map.data(), stack_map.size()), MemoryRegion(method_info.data(), method_info.size()), - *code_item); + code_item_for_osr_check); CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod( compiler_driver, @@ -929,21 +925,16 @@ CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, ArenaStack* arena_stack, CodeVectorAllocator* code_allocator, - const DexFile::CodeItem* code_item, - uint32_t access_flags, - InvokeType invoke_type, - uint16_t class_def_idx, - uint32_t method_idx, - Handle class_loader, - const DexFile& dex_file, - Handle dex_cache, + const DexCompilationUnit& dex_compilation_unit, ArtMethod* method, bool osr, VariableSizedHandleScope* handles) const { - MaybeRecordStat(compilation_stats_.get(), - MethodCompilationStat::kAttemptCompilation); + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kAttemptCompilation); CompilerDriver* compiler_driver = GetCompilerDriver(); InstructionSet instruction_set = compiler_driver->GetInstructionSet(); + const DexFile& dex_file = *dex_compilation_unit.GetDexFile(); + uint32_t method_idx = dex_compilation_unit.GetDexMethodIndex(); + const DexFile::CodeItem* code_item = dex_compilation_unit.GetCodeItem(); // Always use the Thumb-2 assembler: some runtime functionality // (like implicit stack overflow checks) assume Thumb-2. @@ -973,18 +964,6 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, return nullptr; } - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - DexCompilationUnit dex_compilation_unit( - class_loader, - class_linker, - dex_file, - code_item, - class_def_idx, - method_idx, - access_flags, - /* verified_method */ nullptr, - dex_cache); - HGraph* graph = new (allocator) HGraph( allocator, arena_stack, @@ -996,11 +975,6 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, osr); const uint8_t* interpreter_metadata = nullptr; - if (method == nullptr) { - ScopedObjectAccess soa(Thread::Current()); - method = compiler_driver->ResolveMethod( - soa, dex_cache, class_loader, &dex_compilation_unit, method_idx, invoke_type); - } // For AOT compilation, we may not get a method, for example if its class is erroneous. // JIT should always have a method. DCHECK(Runtime::Current()->IsAotCompiler() || method != nullptr); @@ -1034,6 +1008,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, VLOG(compiler) << "Building " << pass_observer.GetMethodName(); PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer); HGraphBuilder builder(graph, + code_item, &dex_compilation_unit, &dex_compilation_unit, compiler_driver, @@ -1093,6 +1068,112 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, return codegen.release(); } +CodeGenerator* OptimizingCompiler::TryCompileIntrinsic( + ArenaAllocator* allocator, + ArenaStack* arena_stack, + CodeVectorAllocator* code_allocator, + const DexCompilationUnit& dex_compilation_unit, + ArtMethod* method, + VariableSizedHandleScope* handles) const { + CompilerDriver* compiler_driver = GetCompilerDriver(); + InstructionSet instruction_set = compiler_driver->GetInstructionSet(); + const DexFile& dex_file = *dex_compilation_unit.GetDexFile(); + uint32_t method_idx = dex_compilation_unit.GetDexMethodIndex(); + + // Always use the Thumb-2 assembler: some runtime functionality + // (like implicit stack overflow checks) assume Thumb-2. + DCHECK_NE(instruction_set, InstructionSet::kArm); + + // Do not attempt to compile on architectures we do not support. + if (!IsInstructionSetSupported(instruction_set)) { + MaybeRecordStat(compilation_stats_.get(), + MethodCompilationStat::kNotCompiledUnsupportedIsa); + return nullptr; + } + + HGraph* graph = new (allocator) HGraph( + allocator, + arena_stack, + dex_file, + method_idx, + compiler_driver->GetInstructionSet(), + kInvalidInvokeType, + compiler_driver->GetCompilerOptions().GetDebuggable(), + /* osr */ false); + + DCHECK(Runtime::Current()->IsAotCompiler()); + DCHECK(method != nullptr); + graph->SetArtMethod(method); + + std::unique_ptr codegen( + CodeGenerator::Create(graph, + instruction_set, + *compiler_driver->GetInstructionSetFeatures(), + compiler_driver->GetCompilerOptions(), + compilation_stats_.get())); + if (codegen.get() == nullptr) { + MaybeRecordStat(compilation_stats_.get(), + MethodCompilationStat::kNotCompiledNoCodegen); + return nullptr; + } + codegen->GetAssembler()->cfi().SetEnabled( + compiler_driver->GetCompilerOptions().GenerateAnyDebugInfo()); + + PassObserver pass_observer(graph, + codegen.get(), + visualizer_output_.get(), + compiler_driver, + dump_mutex_); + + { + VLOG(compiler) << "Building intrinsic graph " << pass_observer.GetMethodName(); + PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer); + HGraphBuilder builder(graph, + /* code_item */ nullptr, + &dex_compilation_unit, + &dex_compilation_unit, + compiler_driver, + codegen.get(), + compilation_stats_.get(), + /* interpreter_metadata */ nullptr, + handles); + builder.BuildIntrinsicGraph(method); + } + + OptimizingCompilerStats* stats = compilation_stats_.get(); + InstructionSimplifier* simplify = new (allocator) InstructionSimplifier( + graph, codegen.get(), compiler_driver, stats); + IntrinsicsRecognizer* intrinsics = new (allocator) IntrinsicsRecognizer(graph, stats); + + HOptimization* optimizations[] = { + intrinsics, + // Some intrinsics are converted to HIR by the simplifier and the codegen also + // has a few assumptions that only the instruction simplifier can satisfy. + simplify, + }; + RunOptimizations(optimizations, arraysize(optimizations), &pass_observer); + + RunArchOptimizations(compiler_driver->GetInstructionSet(), graph, codegen.get(), &pass_observer); + + AllocateRegisters(graph, + codegen.get(), + &pass_observer, + compiler_driver->GetCompilerOptions().GetRegisterAllocationStrategy(), + compilation_stats_.get()); + if (!codegen->IsLeafMethod()) { + VLOG(compiler) << "Intrinsic method is not leaf: " << method->GetIntrinsic() + << " " << graph->PrettyMethod(); + return nullptr; + } + + codegen->Compile(code_allocator); + pass_observer.DumpDisassembly(); + + VLOG(compiler) << "Compiled intrinsic: " << method->GetIntrinsic() + << " " << graph->PrettyMethod(); + return codegen.release(); +} + CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, uint32_t access_flags, InvokeType invoke_type, @@ -1102,42 +1183,71 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, const DexFile& dex_file, Handle dex_cache) const { CompilerDriver* compiler_driver = GetCompilerDriver(); - CompiledMethod* method = nullptr; - DCHECK(Runtime::Current()->IsAotCompiler()); + CompiledMethod* compiled_method = nullptr; + Runtime* runtime = Runtime::Current(); + DCHECK(runtime->IsAotCompiler()); const VerifiedMethod* verified_method = compiler_driver->GetVerifiedMethod(&dex_file, method_idx); DCHECK(!verified_method->HasRuntimeThrow()); if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file) || verifier::CanCompilerHandleVerificationFailure( verified_method->GetEncounteredVerificationFailures())) { - ArenaAllocator allocator(Runtime::Current()->GetArenaPool()); - ArenaStack arena_stack(Runtime::Current()->GetArenaPool()); + ArenaAllocator allocator(runtime->GetArenaPool()); + ArenaStack arena_stack(runtime->GetArenaPool()); CodeVectorAllocator code_allocator(&allocator); std::unique_ptr codegen; + bool compiled_intrinsic = false; { + DexCompilationUnit dex_compilation_unit( + jclass_loader, + runtime->GetClassLinker(), + dex_file, + code_item, + class_def_idx, + method_idx, + access_flags, + /* verified_method */ nullptr, // Not needed by the Optimizing compiler. + dex_cache); ScopedObjectAccess soa(Thread::Current()); + ArtMethod* method = compiler_driver->ResolveMethod( + soa, dex_cache, jclass_loader, &dex_compilation_unit, method_idx, invoke_type); VariableSizedHandleScope handles(soa.Self()); // Go to native so that we don't block GC during compilation. ScopedThreadSuspension sts(soa.Self(), kNative); - codegen.reset( - TryCompile(&allocator, - &arena_stack, - &code_allocator, - code_item, - access_flags, - invoke_type, - class_def_idx, - method_idx, - jclass_loader, - dex_file, - dex_cache, - nullptr, - /* osr */ false, - &handles)); + if (method != nullptr && UNLIKELY(method->IsIntrinsic())) { + DCHECK(compiler_driver->GetCompilerOptions().IsBootImage()); + codegen.reset( + TryCompileIntrinsic(&allocator, + &arena_stack, + &code_allocator, + dex_compilation_unit, + method, + &handles)); + if (codegen != nullptr) { + compiled_intrinsic = true; + } + } + if (codegen == nullptr) { + codegen.reset( + TryCompile(&allocator, + &arena_stack, + &code_allocator, + dex_compilation_unit, + method, + /* osr */ false, + &handles)); + } } if (codegen.get() != nullptr) { MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kCompiled); - method = Emit(&allocator, &code_allocator, codegen.get(), compiler_driver, code_item); + compiled_method = Emit(&allocator, + &code_allocator, + codegen.get(), + compiler_driver, + compiled_intrinsic ? nullptr : code_item); + if (compiled_intrinsic) { + compiled_method->MarkAsIntrinsic(); + } if (kArenaAllocatorCountAllocations) { codegen.reset(); // Release codegen's ScopedArenaAllocator for memory accounting. @@ -1171,10 +1281,62 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, // regressing. std::string method_name = dex_file.PrettyMethod(method_idx); bool shouldCompile = method_name.find("$opt$") != std::string::npos; - DCHECK((method != nullptr) || !shouldCompile) << "Didn't compile " << method_name; + DCHECK((compiled_method != nullptr) || !shouldCompile) << "Didn't compile " << method_name; } - return method; + return compiled_method; +} + +CompiledMethod* OptimizingCompiler::JniCompile(uint32_t access_flags, + uint32_t method_idx, + const DexFile& dex_file, + Handle dex_cache) const { + if (GetCompilerDriver()->GetCompilerOptions().IsBootImage()) { + ScopedObjectAccess soa(Thread::Current()); + Runtime* runtime = Runtime::Current(); + ArtMethod* method = runtime->GetClassLinker()->LookupResolvedMethod( + method_idx, dex_cache.Get(), /* class_loader */ nullptr); + if (method != nullptr && UNLIKELY(method->IsIntrinsic())) { + ScopedNullHandle class_loader; // null means boot class path loader. + DexCompilationUnit dex_compilation_unit( + class_loader, + runtime->GetClassLinker(), + dex_file, + /* code_item */ nullptr, + /* class_def_idx */ DexFile::kDexNoIndex16, + method_idx, + access_flags, + /* verified_method */ nullptr, + dex_cache); + ArenaAllocator allocator(runtime->GetArenaPool()); + ArenaStack arena_stack(runtime->GetArenaPool()); + CodeVectorAllocator code_allocator(&allocator); + VariableSizedHandleScope handles(soa.Self()); + // Go to native so that we don't block GC during compilation. + ScopedThreadSuspension sts(soa.Self(), kNative); + std::unique_ptr codegen( + TryCompileIntrinsic(&allocator, + &arena_stack, + &code_allocator, + dex_compilation_unit, + method, + &handles)); + if (codegen != nullptr) { + CompiledMethod* compiled_method = Emit(&allocator, + &code_allocator, + codegen.get(), + GetCompilerDriver(), + /* code_item_for_osr_check */ nullptr); + compiled_method->MarkAsIntrinsic(); + return compiled_method; + } + } + } + + return ArtQuickJniCompileMethod(GetCompilerDriver(), + access_flags, + method_idx, + dex_file); } Compiler* CreateOptimizingCompiler(CompilerDriver* driver) { @@ -1221,29 +1383,33 @@ bool OptimizingCompiler::JitCompile(Thread* self, const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset()); const uint32_t method_idx = method->GetDexMethodIndex(); const uint32_t access_flags = method->GetAccessFlags(); - const InvokeType invoke_type = method->GetInvokeType(); - ArenaAllocator allocator(Runtime::Current()->GetJitArenaPool()); + Runtime* runtime = Runtime::Current(); + ArenaAllocator allocator(runtime->GetJitArenaPool()); ArenaStack arena_stack(Runtime::Current()->GetJitArenaPool()); CodeVectorAllocator code_allocator(&allocator); VariableSizedHandleScope handles(self); std::unique_ptr codegen; { + DexCompilationUnit dex_compilation_unit( + class_loader, + runtime->GetClassLinker(), + *dex_file, + code_item, + class_def_idx, + method_idx, + access_flags, + /* verified_method */ nullptr, + dex_cache); + // Go to native so that we don't block GC during compilation. ScopedThreadSuspension sts(self, kNative); codegen.reset( TryCompile(&allocator, &arena_stack, &code_allocator, - code_item, - access_flags, - invoke_type, - class_def_idx, - method_idx, - class_loader, - *dex_file, - dex_cache, + dex_compilation_unit, method, osr, &handles)); @@ -1286,7 +1452,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kCompiled); codegen->BuildStackMaps(MemoryRegion(stack_map_data, stack_map_size), MemoryRegion(method_info_data, method_info_size), - *code_item); + code_item); codegen->EmitJitRoots(code_allocator.GetData(), roots, roots_data); const void* code = code_cache->CommitCode( diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h index e90c30d5ca..158c252f45 100644 --- a/compiler/optimizing/optimizing_unit_test.h +++ b/compiler/optimizing/optimizing_unit_test.h @@ -22,6 +22,7 @@ #include "common_compiler_test.h" #include "dex_file.h" #include "dex_instruction.h" +#include "driver/dex_compilation_unit.h" #include "handle_scope-inl.h" #include "mirror/class_loader.h" #include "mirror/dex_cache.h" @@ -133,12 +134,11 @@ class OptimizingUnitTest : public CommonCompilerTest { if (handles_ == nullptr) { handles_.reset(new VariableSizedHandleScope(soa.Self())); } - const DexFile* dex_file = graph->GetAllocator()->Alloc(); const DexCompilationUnit* dex_compilation_unit = new (graph->GetAllocator()) DexCompilationUnit( handles_->NewHandle(nullptr), /* class_linker */ nullptr, - *dex_file, + graph->GetDexFile(), code_item, /* class_def_index */ DexFile::kDexNoIndex16, /* method_idx */ dex::kDexNoIndex, diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index fe98aa9561..1ed190d328 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -53,16 +53,18 @@ void PrepareForRegisterAllocation::VisitDeoptimize(HDeoptimize* deoptimize) { void PrepareForRegisterAllocation::VisitBoundsCheck(HBoundsCheck* check) { check->ReplaceWith(check->InputAt(0)); if (check->IsStringCharAt()) { - // Add a fake environment for String.charAt() inline info as we want - // the exception to appear as being thrown from there. + // Add a fake environment for String.charAt() inline info as we want the exception + // to appear as being thrown from there. Skip if we're compiling String.charAt() itself. ArtMethod* char_at_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt); - ArenaAllocator* allocator = GetGraph()->GetAllocator(); - HEnvironment* environment = new (allocator) HEnvironment(allocator, - /* number_of_vregs */ 0u, - char_at_method, - /* dex_pc */ dex::kDexNoIndex, - check); - check->InsertRawEnvironment(environment); + if (GetGraph()->GetArtMethod() != char_at_method) { + ArenaAllocator* allocator = GetGraph()->GetAllocator(); + HEnvironment* environment = new (allocator) HEnvironment(allocator, + /* number_of_vregs */ 0u, + char_at_method, + /* dex_pc */ dex::kDexNoIndex, + check); + check->InsertRawEnvironment(environment); + } } } diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 9bc80457a3..4f43eb374c 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -32,7 +32,6 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, uint32_t num_dex_registers, uint8_t inlining_depth) { DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry"; - DCHECK_NE(dex_pc, static_cast(-1)) << "invalid dex_pc"; current_entry_.dex_pc = dex_pc; current_entry_.native_pc_code_offset = CodeOffset::FromOffset(native_pc_offset, instruction_set_); current_entry_.register_mask = register_mask; @@ -56,7 +55,10 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, number_of_stack_maps_with_inline_info_++; } - dex_pc_max_ = std::max(dex_pc_max_, dex_pc); + // 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; } diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index e126609dba..579aabdb5f 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -73,7 +73,7 @@ class StackMapStream : public ValueObject { method_indices_(allocator->Adapter(kArenaAllocStackMapStream)), dex_register_entries_(allocator->Adapter(kArenaAllocStackMapStream)), stack_mask_max_(-1), - dex_pc_max_(0), + dex_pc_max_(kNoDexPc), register_mask_max_(0), number_of_stack_maps_with_inline_info_(0), dex_map_hash_to_stack_map_indices_(std::less(), diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index d3e920f9a0..663a8896ff 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -1143,7 +1143,6 @@ class OatWriter::LayoutReserveOffsetCodeMethodVisitor : public OrderedMethodVisi uint16_t method_offsets_index_ = method_data.method_offsets_index; size_t class_def_index = method_data.class_def_index; uint32_t access_flags = method_data.access_flags; - const DexFile::CodeItem* code_item = method_data.code_item; bool has_debug_info = method_data.HasDebugInfo(); size_t debug_info_idx = method_data.debug_info_idx; @@ -1244,7 +1243,8 @@ class OatWriter::LayoutReserveOffsetCodeMethodVisitor : public OrderedMethodVisi info.class_def_index = class_def_index; info.dex_method_index = method_ref.index; info.access_flags = access_flags; - info.code_item = code_item; + // For intrinsics emitted by codegen, the code has no relation to the original code item. + info.code_item = compiled_method->IsIntrinsic() ? nullptr : method_data.code_item; info.isa = compiled_method->GetInstructionSet(); info.deduped = deduped; info.is_native_debuggable = native_debuggable_; @@ -1303,6 +1303,9 @@ class OatWriter::LayoutReserveOffsetCodeMethodVisitor : public OrderedMethodVisi if (UNLIKELY(lhs->GetPatches().data() != rhs->GetPatches().data())) { return lhs->GetPatches().data() < rhs->GetPatches().data(); } + if (UNLIKELY(lhs->IsIntrinsic() != rhs->IsIntrinsic())) { + return rhs->IsIntrinsic(); + } return false; } }; diff --git a/runtime/stack_map.h b/runtime/stack_map.h index db776eaa1a..85c734ee4c 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -711,7 +711,11 @@ class StackMapEncoding { total_bit_size_ += MinimumBitsToStore(native_pc_max); dex_pc_bit_offset_ = total_bit_size_; - total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max); + // 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. -- GitLab From cf053a57cad0cb4f3083e41f5e0617bffeba6cb1 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 15 Nov 2017 15:10:09 +0000 Subject: [PATCH 047/226] Add crash_dump to the list of golem unbundled dependencies. As we're compiling libbacktrace, we also need to compile crash_dump. Test: m -j32 build-art-unbundled-golem Change-Id: I08a9c6406440cc47585d26a770852d19fe098822 --- Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 7081f7bec5..174cde36ef 100644 --- a/Android.mk +++ b/Android.mk @@ -484,7 +484,7 @@ build-art-host-golem: build-art-host \ ######################################################################## # Phony target for building what go/lem requires for syncing /system to target. .PHONY: build-art-unbundled-golem -build-art-unbundled-golem: art-runtime linker oatdump $(TARGET_CORE_JARS) +build-art-unbundled-golem: art-runtime linker oatdump $(TARGET_CORE_JARS) crash_dump ######################################################################## # Rules for building all dependencies for tests. -- GitLab From 237c113e573b624baf31c43d230e9f9c04fa9de4 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Wed, 15 Nov 2017 20:50:38 -0800 Subject: [PATCH 048/226] Remove unneeded endian.h include This was left in accidentally and is breaking the mac build. Test: ./test/run-test --host 1940 Change-Id: I309dae788ad7d9032671452011b7d9c052e7e055 --- openjdkjvmti/ti_ddms.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/openjdkjvmti/ti_ddms.cc b/openjdkjvmti/ti_ddms.cc index be7e65d638..500a453f78 100644 --- a/openjdkjvmti/ti_ddms.cc +++ b/openjdkjvmti/ti_ddms.cc @@ -34,8 +34,6 @@ #include "ti_ddms.h" -#include - #include "art_jvmti.h" #include "base/array_ref.h" #include "debugger.h" -- GitLab From 94cd28a6e353890882fa6755e947ca710cf968f3 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 16 Nov 2017 09:03:45 +0000 Subject: [PATCH 049/226] Revert "Make arm64-scratch-register less dexer dependent" Fails with: /b/build/slave/angler-armv8-ndebug/build/art/test/626-checker-arm64-scratch-register: FAILED! #################### info # Regression test checking that the ARM64 scratch register pool is not # exhausted during moves between stack slots (b/32545705). #################### diffs --- expected.txt 2017-01-04 09:57:07.060588086 -0800 +++ output.txt 2017-11-15 15:38:39.610475786 -0800 @@ -1 +1,10 @@ passed +error: Assertion could not be matched starting from line 222106 +Smali.smali:1317: ldr s13, pc+580 (addr 0x61c) (100) +CondA = z229 +CondB = z347 +ElseBlockA = B114 +ElseBlockB = B117 +ThenBlockA = B113 +ThenBlockB = B116 +This = l0 #################### 626-checker-arm64-scratch-register files deleted from host and from target This reverts commit c82745f28c3e4bef01030e4c7c8324aff0d2e5f7. Change-Id: I4af4b1ec0b91c59d87b50d73ebc3b183e958bae5 --- .../smali/Smali.smali | 2119 ----------------- .../src/Main.java | 61 +- 2 files changed, 39 insertions(+), 2141 deletions(-) delete mode 100644 test/626-checker-arm64-scratch-register/smali/Smali.smali diff --git a/test/626-checker-arm64-scratch-register/smali/Smali.smali b/test/626-checker-arm64-scratch-register/smali/Smali.smali deleted file mode 100644 index e6943cf717..0000000000 --- a/test/626-checker-arm64-scratch-register/smali/Smali.smali +++ /dev/null @@ -1,2119 +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. - -.class public LSmali; -.super Ljava/lang/Object; -.field b00:Z -.field b01:Z -.field b02:Z -.field b03:Z -.field b04:Z -.field b05:Z -.field b06:Z -.field b07:Z -.field b08:Z -.field b09:Z -.field b10:Z -.field b11:Z -.field b12:Z -.field b13:Z -.field b14:Z -.field b15:Z -.field b16:Z -.field b17:Z -.field b18:Z -.field b19:Z -.field b20:Z -.field b21:Z -.field b22:Z -.field b23:Z -.field b24:Z -.field b25:Z -.field b26:Z -.field b27:Z -.field b28:Z -.field b29:Z -.field b30:Z -.field b31:Z -.field b32:Z -.field b33:Z -.field b34:Z -.field b35:Z -.field b36:Z - -.field conditionA:Z -.field conditionB:Z -.field conditionC:Z - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -## CHECK-START-ARM64: void Smali.test() register (after) -## CHECK: begin_block -## CHECK: name "B0" -## CHECK: <> ParameterValue -## CHECK: end_block -## CHECK: begin_block -## CHECK: successors "<>" "<>" -## CHECK: <> InstanceFieldGet [<>] field_name:Smali.conditionB -## CHECK: If [<>] -## CHECK: end_block -## CHECK: begin_block -## CHECK: name "<>" -## CHECK: ParallelMove moves:[40(sp)->d0,24(sp)->32(sp),28(sp)->36(sp),d0->d3,d3->d4,d2->d5,d4->d6,d5->d7,d6->d18,d7->d19,d18->d20,d19->d21,d20->d22,d21->d23,d22->d10,d23->d11,16(sp)->24(sp),20(sp)->28(sp),d10->d14,d11->d12,d12->d13,d13->d1,d14->d2,32(sp)->16(sp),36(sp)->20(sp)] -## CHECK: end_block - -## CHECK-START-ARM64: void Smali.test() disassembly (after) -## CHECK: begin_block -## CHECK: name "B0" -## CHECK: <> ParameterValue -## CHECK: end_block -## CHECK: begin_block -## CHECK: successors "<>" "<>" -## CHECK: <> InstanceFieldGet [<>] field_name:Smali.conditionB -## CHECK: If [<>] -## CHECK: end_block -## CHECK: begin_block -## CHECK: name "<>" -## CHECK: ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid] -## CHECK: fmov d31, d2 -## CHECK: ldr s2, [sp, #36] -## CHECK: ldr w16, [sp, #16] -## CHECK: str w16, [sp, #36] -## CHECK: str s14, [sp, #16] -## CHECK: ldr s14, [sp, #28] -## CHECK: str s1, [sp, #28] -## CHECK: ldr s1, [sp, #32] -## CHECK: str s31, [sp, #32] -## CHECK: ldr s31, [sp, #20] -## CHECK: str s31, [sp, #40] -## CHECK: str s12, [sp, #20] -## CHECK: fmov d12, d11 -## CHECK: fmov d11, d10 -## CHECK: fmov d10, d23 -## CHECK: fmov d23, d22 -## CHECK: fmov d22, d21 -## CHECK: fmov d21, d20 -## CHECK: fmov d20, d19 -## CHECK: fmov d19, d18 -## CHECK: fmov d18, d7 -## CHECK: fmov d7, d6 -## CHECK: fmov d6, d5 -## CHECK: fmov d5, d4 -## CHECK: fmov d4, d3 -## CHECK: fmov d3, d13 -## CHECK: ldr s13, [sp, #24] -## CHECK: str s3, [sp, #24] -## CHECK: ldr s3, pc+{{\d+}} (addr {{0x[0-9a-f]+}}) (100) -## CHECK: end_block -.method public test()V - .registers 45 - - const-string v39, "" - - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b17:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_367 - - const/16 v19, 0x0 - - :goto_c - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b16:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_36b - - const/16 v18, 0x0 - - :goto_16 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b18:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_36f - - const/16 v20, 0x0 - - :goto_20 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b19:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_373 - - const/16 v21, 0x0 - - :goto_2a - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b20:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_377 - - const/16 v22, 0x0 - - :goto_34 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b21:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_37b - - const/16 v23, 0x0 - - :goto_3e - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b15:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_37f - - const/16 v17, 0x0 - - :goto_48 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b00:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_383 - - const/4 v2, 0x0 - - :goto_51 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b22:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_387 - - const/16 v24, 0x0 - - :goto_5b - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b23:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_38b - - const/16 v25, 0x0 - - :goto_65 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b24:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_38f - - const/16 v26, 0x0 - - :goto_6f - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b25:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_393 - - const/16 v27, 0x0 - - :goto_79 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b26:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_397 - - const/16 v28, 0x0 - - :goto_83 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b27:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_39b - - const/16 v29, 0x0 - - :goto_8d - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b29:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_39f - - const/16 v31, 0x0 - - :goto_97 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b28:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3a3 - - const/16 v30, 0x0 - - :goto_a1 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b01:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3a7 - - const/4 v3, 0x0 - - :goto_aa - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b02:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3ab - - const/4 v4, 0x0 - - :goto_b3 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b03:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3af - - const/4 v5, 0x0 - - :goto_bc - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b04:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3b3 - - const/4 v6, 0x0 - - :goto_c5 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b05:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3b7 - - const/4 v7, 0x0 - - :goto_ce - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b07:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3bb - - const/4 v9, 0x0 - - :goto_d7 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b06:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3bf - - const/4 v8, 0x0 - - :goto_e0 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b30:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3c3 - - const/16 v32, 0x0 - - :goto_ea - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b31:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3c7 - - const/16 v33, 0x0 - - :goto_f4 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b32:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3cb - - const/16 v34, 0x0 - - :goto_fe - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b33:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3cf - - const/16 v35, 0x0 - - :goto_108 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b34:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3d3 - - const/16 v36, 0x0 - - :goto_112 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b36:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3d7 - - const/16 v38, 0x0 - - :goto_11c - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b35:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3db - - const/16 v37, 0x0 - - :goto_126 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b08:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3df - - const/4 v10, 0x0 - - :goto_12f - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b09:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3e3 - - const/4 v11, 0x0 - - :goto_138 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b10:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3e7 - - const/4 v12, 0x0 - - :goto_141 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b11:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3eb - - const/4 v13, 0x0 - - :goto_14a - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b12:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3ef - - const/4 v14, 0x0 - - :goto_153 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b14:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3f3 - - const/16 v16, 0x0 - - :goto_15d - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->b13:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_3f7 - - const/4 v15, 0x0 - - :goto_166 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->conditionA:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_202 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v18, v18, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v19, v19, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v20, v20, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v21, v21, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v22, v22, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v23, v23, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v17, v17, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v10, v10, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v11, v11, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v12, v12, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v13, v13, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v14, v14, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v32, v32, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v33, v33, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v34, v34, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v35, v35, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v36, v36, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v3, v3, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v4, v4, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v5, v5, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v6, v6, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v7, v7, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v25, v25, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v26, v26, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v27, v27, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v28, v28, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v29, v29, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v24, v24, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v2, v2, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v16, v16, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v15, v15, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v38, v38, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v37, v37, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v9, v9, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v8, v8, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v31, v31, v42 - - const/high16 v42, 0x447a0000 # 1000.0f - - div-float v30, v30, v42 - - :cond_202 - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->conditionB:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_29e - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v18, v18, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v19, v19, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v20, v20, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v21, v21, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v22, v22, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v23, v23, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v17, v17, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v10, v10, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v11, v11, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v12, v12, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v13, v13, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v14, v14, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v32, v32, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v33, v33, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v34, v34, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v35, v35, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v36, v36, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v3, v3, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v4, v4, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v5, v5, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v6, v6, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v7, v7, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v25, v25, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v26, v26, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v27, v27, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v28, v28, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v29, v29, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v24, v24, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v2, v2, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v16, v16, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v15, v15, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v38, v38, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v37, v37, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v9, v9, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v8, v8, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v31, v31, v42 - - const/high16 v42, 0x42c80000 # 100.0f - - div-float v30, v30, v42 - - :cond_29e - move-object/from16 v0, p0 - - iget-boolean v0, v0, LSmali;->conditionC:Z - - move/from16 v42, v0 - - if-eqz v42, :cond_33a - - const/high16 v42, 0x41400000 # 12.0f - - div-float v18, v18, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v19, v19, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v20, v20, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v21, v21, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v22, v22, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v23, v23, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v17, v17, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v10, v10, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v11, v11, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v12, v12, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v13, v13, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v14, v14, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v32, v32, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v33, v33, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v34, v34, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v35, v35, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v36, v36, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v3, v3, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v4, v4, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v5, v5, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v6, v6, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v7, v7, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v25, v25, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v26, v26, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v27, v27, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v28, v28, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v29, v29, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v24, v24, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v2, v2, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v16, v16, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v15, v15, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v38, v38, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v37, v37, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v9, v9, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v8, v8, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v31, v31, v42 - - const/high16 v42, 0x41400000 # 12.0f - - div-float v30, v30, v42 - - :cond_33a - const/16 v41, 0x0 - - const/high16 v42, 0x42c80000 # 100.0f - - mul-float v42, v42, v41 - - invoke-static/range {v42 .. v42}, Ljava/lang/Math;->round(F)I - - move-result v42 - - move/from16 v0, v42 - - int-to-float v0, v0 - - move/from16 v42, v0 - - const/high16 v43, 0x42c80000 # 100.0f - - div-float v41, v42, v43 - - new-instance v42, Ljava/lang/StringBuilder; - - invoke-direct/range {v42 .. v42}, Ljava/lang/StringBuilder;->()V - - move-object/from16 v0, v42 - - move/from16 v1, v41 - - invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(F)Ljava/lang/StringBuilder; - - move-result-object v42 - - move-object/from16 v0, v42 - - move-object/from16 v1, v39 - - invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - - move-result-object v42 - - invoke-virtual/range {v42 .. v42}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; - - move-result-object v40 - - return-void - - :cond_367 - const/high16 v19, 0x3f800000 # 1.0f - - goto/16 :goto_c - - :cond_36b - const/high16 v18, 0x3f800000 # 1.0f - - goto/16 :goto_16 - - :cond_36f - const/high16 v20, 0x3f800000 # 1.0f - - goto/16 :goto_20 - - :cond_373 - const/high16 v21, 0x3f800000 # 1.0f - - goto/16 :goto_2a - - :cond_377 - const/high16 v22, 0x3f800000 # 1.0f - - goto/16 :goto_34 - - :cond_37b - const/high16 v23, 0x3f800000 # 1.0f - - goto/16 :goto_3e - - :cond_37f - const/high16 v17, 0x3f800000 # 1.0f - - goto/16 :goto_48 - - :cond_383 - const/high16 v2, 0x3f800000 # 1.0f - - goto/16 :goto_51 - - :cond_387 - const/high16 v24, 0x3f800000 # 1.0f - - goto/16 :goto_5b - - :cond_38b - const/high16 v25, 0x3f800000 # 1.0f - - goto/16 :goto_65 - - :cond_38f - const/high16 v26, 0x3f800000 # 1.0f - - goto/16 :goto_6f - - :cond_393 - const/high16 v27, 0x3f800000 # 1.0f - - goto/16 :goto_79 - - :cond_397 - const/high16 v28, 0x3f800000 # 1.0f - - goto/16 :goto_83 - - :cond_39b - const/high16 v29, 0x3f800000 # 1.0f - - goto/16 :goto_8d - - :cond_39f - const/high16 v31, 0x3f800000 # 1.0f - - goto/16 :goto_97 - - :cond_3a3 - const/high16 v30, 0x3f800000 # 1.0f - - goto/16 :goto_a1 - - :cond_3a7 - const/high16 v3, 0x3f800000 # 1.0f - - goto/16 :goto_aa - - :cond_3ab - const/high16 v4, 0x3f800000 # 1.0f - - goto/16 :goto_b3 - - :cond_3af - const/high16 v5, 0x3f800000 # 1.0f - - goto/16 :goto_bc - - :cond_3b3 - const/high16 v6, 0x3f800000 # 1.0f - - goto/16 :goto_c5 - - :cond_3b7 - const/high16 v7, 0x3f800000 # 1.0f - - goto/16 :goto_ce - - :cond_3bb - const/high16 v9, 0x3f800000 # 1.0f - - goto/16 :goto_d7 - - :cond_3bf - const/high16 v8, 0x3f800000 # 1.0f - - goto/16 :goto_e0 - - :cond_3c3 - const/high16 v32, 0x3f800000 # 1.0f - - goto/16 :goto_ea - - :cond_3c7 - const/high16 v33, 0x3f800000 # 1.0f - - goto/16 :goto_f4 - - :cond_3cb - const/high16 v34, 0x3f800000 # 1.0f - - goto/16 :goto_fe - - :cond_3cf - const/high16 v35, 0x3f800000 # 1.0f - - goto/16 :goto_108 - - :cond_3d3 - const/high16 v36, 0x3f800000 # 1.0f - - goto/16 :goto_112 - - :cond_3d7 - const/high16 v38, 0x3f800000 # 1.0f - - goto/16 :goto_11c - - :cond_3db - const/high16 v37, 0x3f800000 # 1.0f - - goto/16 :goto_126 - - :cond_3df - const/high16 v10, 0x3f800000 # 1.0f - - goto/16 :goto_12f - - :cond_3e3 - const/high16 v11, 0x3f800000 # 1.0f - - goto/16 :goto_138 - - :cond_3e7 - const/high16 v12, 0x3f800000 # 1.0f - - goto/16 :goto_141 - - :cond_3eb - const/high16 v13, 0x3f800000 # 1.0f - - goto/16 :goto_14a - - :cond_3ef - const/high16 v14, 0x3f800000 # 1.0f - - goto/16 :goto_153 - - :cond_3f3 - const/high16 v16, 0x3f800000 # 1.0f - - goto/16 :goto_15d - - :cond_3f7 - const/high16 v15, 0x3f800000 # 1.0f - - goto/16 :goto_166 -.end method - -## CHECK-START-ARM64: void Smali.testD8() register (after) -## CHECK: begin_block -## CHECK: name "B0" -## CHECK: <> ParameterValue -## CHECK: end_block - -## CHECK: begin_block -## CHECK: successors "<>" "<>" -## CHECK: <> InstanceFieldGet [<>] field_name:Smali.conditionA -## CHECK: If [<>] -## CHECK: end_block - -## CHECK: begin_block -## CHECK: name "<>" -## CHECK: end_block -## CHECK: begin_block -## CHECK: name "<>" -## CHECK: ParallelMove moves:[d2->d0,40(sp)->d17,d20->d26,d19->d27,d27->d1,d26->d2,d14->d20,d13->d19,d17->d14,d0->d13] -## CHECK: end_block - -## CHECK: begin_block -## CHECK: successors "<>" "<>" -## CHECK: <> InstanceFieldGet [<>] field_name:Smali.conditionB -## CHECK: If [<>] -## CHECK: end_block - -## CHECK: begin_block -## CHECK: name "<>" -## CHECK: end_block -## CHECK: begin_block -## CHECK: name "<>" -## CHECK: ParallelMove moves:[#100->d13,16(sp)->d1,20(sp)->d2,28(sp)->d19,24(sp)->d20,36(sp)->d14,32(sp)->16(sp),d1->20(sp),d2->24(sp),d20->28(sp),d19->32(sp),d14->36(sp),d13->40(sp)] -## CHECK: end_block - -## CHECK-START-ARM64: void Smali.testD8() disassembly (after) -## CHECK: begin_block -## CHECK: name "B0" -## CHECK: <> ParameterValue -## CHECK: end_block - -## CHECK: begin_block -## CHECK: successors "<>" "<>" -## CHECK: <> InstanceFieldGet [<>] field_name:Smali.conditionA -## CHECK: If [<>] -## CHECK: end_block - -## CHECK: begin_block -## CHECK: name "<>" -## CHECK: end_block -## CHECK: begin_block -## CHECK: name "<>" -## CHECK: end_block - -## CHECK: begin_block -## CHECK: successors "<>" "<>" -## CHECK: <> InstanceFieldGet [<>] field_name:Smali.conditionB -## CHECK: If [<>] -## CHECK: end_block - -## CHECK: begin_block -## CHECK: name "<>" -## CHECK: end_block -## CHECK: begin_block -## CHECK: name "<>" -## CHECK: ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid] -## CHECK: ldr w16, [sp, #32] -## CHECK: str s19, [sp, #32] -## CHECK: ldr s19, [sp, #28] -## CHECK: str s20, [sp, #28] -## CHECK: ldr s20, [sp, #24] -## CHECK: str s2, [sp, #24] -## CHECK: ldr s2, [sp, #20] -## CHECK: str s1, [sp, #20] -## CHECK: ldr s1, [sp, #16] -## CHECK: str w16, [sp, #16] -## CHECK: fmov d31, d14 -## CHECK: ldr s14, [sp, #36] -## CHECK: str s31, [sp, #36] -## CHECK: str s13, [sp, #40] -## CHECK: ldr s13, pc+580 (addr 0x61c) (100) -## CHECK: end_block -.method public testD8()V - .registers 47 - - move-object/from16 v0, p0 - - const-string v1, "" - - iget-boolean v2, v0, LSmali;->b17:Z - - if-eqz v2, :cond_a - - const/4 v2, 0x0 - - goto :goto_c - - :cond_a - const/high16 v2, 0x3f800000 # 1.0f - - :goto_c - iget-boolean v5, v0, LSmali;->b16:Z - - if-eqz v5, :cond_12 - - const/4 v5, 0x0 - - goto :goto_14 - - :cond_12 - const/high16 v5, 0x3f800000 # 1.0f - - :goto_14 - iget-boolean v6, v0, LSmali;->b18:Z - - if-eqz v6, :cond_1a - - const/4 v6, 0x0 - - goto :goto_1c - - :cond_1a - const/high16 v6, 0x3f800000 # 1.0f - - :goto_1c - iget-boolean v7, v0, LSmali;->b19:Z - - if-eqz v7, :cond_22 - - const/4 v7, 0x0 - - goto :goto_24 - - :cond_22 - const/high16 v7, 0x3f800000 # 1.0f - - :goto_24 - iget-boolean v8, v0, LSmali;->b20:Z - - if-eqz v8, :cond_2a - - const/4 v8, 0x0 - - goto :goto_2c - - :cond_2a - const/high16 v8, 0x3f800000 # 1.0f - - :goto_2c - iget-boolean v9, v0, LSmali;->b21:Z - - if-eqz v9, :cond_32 - - const/4 v9, 0x0 - - goto :goto_34 - - :cond_32 - const/high16 v9, 0x3f800000 # 1.0f - - :goto_34 - iget-boolean v10, v0, LSmali;->b15:Z - - if-eqz v10, :cond_3a - - const/4 v10, 0x0 - - goto :goto_3c - - :cond_3a - const/high16 v10, 0x3f800000 # 1.0f - - :goto_3c - iget-boolean v11, v0, LSmali;->b00:Z - - if-eqz v11, :cond_42 - - const/4 v11, 0x0 - - goto :goto_44 - - :cond_42 - const/high16 v11, 0x3f800000 # 1.0f - - :goto_44 - iget-boolean v12, v0, LSmali;->b22:Z - - if-eqz v12, :cond_4a - - const/4 v12, 0x0 - - goto :goto_4c - - :cond_4a - const/high16 v12, 0x3f800000 # 1.0f - - :goto_4c - iget-boolean v13, v0, LSmali;->b23:Z - - if-eqz v13, :cond_52 - - const/4 v13, 0x0 - - goto :goto_54 - - :cond_52 - const/high16 v13, 0x3f800000 # 1.0f - - :goto_54 - iget-boolean v14, v0, LSmali;->b24:Z - - if-eqz v14, :cond_5a - - const/4 v14, 0x0 - - goto :goto_5c - - :cond_5a - const/high16 v14, 0x3f800000 # 1.0f - - :goto_5c - iget-boolean v15, v0, LSmali;->b25:Z - - if-eqz v15, :cond_62 - - const/4 v15, 0x0 - - goto :goto_64 - - :cond_62 - const/high16 v15, 0x3f800000 # 1.0f - - :goto_64 - iget-boolean v3, v0, LSmali;->b26:Z - - if-eqz v3, :cond_6a - - const/4 v3, 0x0 - - goto :goto_6c - - :cond_6a - const/high16 v3, 0x3f800000 # 1.0f - - :goto_6c - iget-boolean v4, v0, LSmali;->b27:Z - - if-eqz v4, :cond_72 - - const/4 v4, 0x0 - - goto :goto_74 - - :cond_72 - const/high16 v4, 0x3f800000 # 1.0f - - :goto_74 - move-object/from16 v18, v1 - - iget-boolean v1, v0, LSmali;->b29:Z - - if-eqz v1, :cond_7c - - const/4 v1, 0x0 - - goto :goto_7e - - :cond_7c - const/high16 v1, 0x3f800000 # 1.0f - - :goto_7e - move/from16 v19, v1 - - iget-boolean v1, v0, LSmali;->b28:Z - - if-eqz v1, :cond_86 - - const/4 v1, 0x0 - - goto :goto_88 - - :cond_86 - const/high16 v1, 0x3f800000 # 1.0f - - :goto_88 - move/from16 v20, v1 - - iget-boolean v1, v0, LSmali;->b01:Z - - if-eqz v1, :cond_90 - - const/4 v1, 0x0 - - goto :goto_92 - - :cond_90 - const/high16 v1, 0x3f800000 # 1.0f - - :goto_92 - move/from16 v21, v11 - - iget-boolean v11, v0, LSmali;->b02:Z - - if-eqz v11, :cond_9a - - const/4 v11, 0x0 - - goto :goto_9c - - :cond_9a - const/high16 v11, 0x3f800000 # 1.0f - - :goto_9c - move/from16 v22, v12 - - iget-boolean v12, v0, LSmali;->b03:Z - - if-eqz v12, :cond_a4 - - const/4 v12, 0x0 - - goto :goto_a6 - - :cond_a4 - const/high16 v12, 0x3f800000 # 1.0f - - :goto_a6 - move/from16 v23, v4 - - iget-boolean v4, v0, LSmali;->b04:Z - - if-eqz v4, :cond_ae - - const/4 v4, 0x0 - - goto :goto_b0 - - :cond_ae - const/high16 v4, 0x3f800000 # 1.0f - - :goto_b0 - move/from16 v24, v3 - - iget-boolean v3, v0, LSmali;->b05:Z - - if-eqz v3, :cond_b8 - - const/4 v3, 0x0 - - goto :goto_ba - - :cond_b8 - const/high16 v3, 0x3f800000 # 1.0f - - :goto_ba - move/from16 v25, v15 - - iget-boolean v15, v0, LSmali;->b07:Z - - if-eqz v15, :cond_c2 - - const/4 v15, 0x0 - - goto :goto_c4 - - :cond_c2 - const/high16 v15, 0x3f800000 # 1.0f - - :goto_c4 - move/from16 v26, v15 - - iget-boolean v15, v0, LSmali;->b06:Z - - if-eqz v15, :cond_cc - - const/4 v15, 0x0 - - goto :goto_ce - - :cond_cc - const/high16 v15, 0x3f800000 # 1.0f - - :goto_ce - move/from16 v27, v15 - - iget-boolean v15, v0, LSmali;->b30:Z - - if-eqz v15, :cond_d6 - - const/4 v15, 0x0 - - goto :goto_d8 - - :cond_d6 - const/high16 v15, 0x3f800000 # 1.0f - - :goto_d8 - move/from16 v28, v14 - - iget-boolean v14, v0, LSmali;->b31:Z - - if-eqz v14, :cond_e0 - - const/4 v14, 0x0 - - goto :goto_e2 - - :cond_e0 - const/high16 v14, 0x3f800000 # 1.0f - - :goto_e2 - move/from16 v29, v13 - - iget-boolean v13, v0, LSmali;->b32:Z - - if-eqz v13, :cond_ea - - const/4 v13, 0x0 - - goto :goto_ec - - :cond_ea - const/high16 v13, 0x3f800000 # 1.0f - - :goto_ec - move/from16 v30, v3 - - iget-boolean v3, v0, LSmali;->b33:Z - - if-eqz v3, :cond_f4 - - const/4 v3, 0x0 - - goto :goto_f6 - - :cond_f4 - const/high16 v3, 0x3f800000 # 1.0f - - :goto_f6 - move/from16 v31, v4 - - iget-boolean v4, v0, LSmali;->b34:Z - - if-eqz v4, :cond_fe - - const/4 v4, 0x0 - - goto :goto_100 - - :cond_fe - const/high16 v4, 0x3f800000 # 1.0f - - :goto_100 - move/from16 v32, v12 - - iget-boolean v12, v0, LSmali;->b36:Z - - if-eqz v12, :cond_108 - - const/4 v12, 0x0 - - goto :goto_10a - - :cond_108 - const/high16 v12, 0x3f800000 # 1.0f - - :goto_10a - move/from16 v33, v12 - - iget-boolean v12, v0, LSmali;->b35:Z - - if-eqz v12, :cond_112 - - const/4 v12, 0x0 - - goto :goto_114 - - :cond_112 - const/high16 v12, 0x3f800000 # 1.0f - - :goto_114 - move/from16 v34, v12 - - iget-boolean v12, v0, LSmali;->b08:Z - - if-eqz v12, :cond_11c - - const/4 v12, 0x0 - - goto :goto_11e - - :cond_11c - const/high16 v12, 0x3f800000 # 1.0f - - :goto_11e - move/from16 v35, v11 - - iget-boolean v11, v0, LSmali;->b09:Z - - if-eqz v11, :cond_126 - - const/4 v11, 0x0 - - goto :goto_128 - - :cond_126 - const/high16 v11, 0x3f800000 # 1.0f - - :goto_128 - move/from16 v36, v1 - - iget-boolean v1, v0, LSmali;->b10:Z - - if-eqz v1, :cond_130 - - const/4 v1, 0x0 - - goto :goto_132 - - :cond_130 - const/high16 v1, 0x3f800000 # 1.0f - - :goto_132 - move/from16 v37, v4 - - iget-boolean v4, v0, LSmali;->b11:Z - - if-eqz v4, :cond_13a - - const/4 v4, 0x0 - - goto :goto_13c - - :cond_13a - const/high16 v4, 0x3f800000 # 1.0f - - :goto_13c - move/from16 v38, v3 - - iget-boolean v3, v0, LSmali;->b12:Z - - if-eqz v3, :cond_144 - - const/4 v3, 0x0 - - goto :goto_146 - - :cond_144 - const/high16 v3, 0x3f800000 # 1.0f - - :goto_146 - move/from16 v39, v13 - - iget-boolean v13, v0, LSmali;->b14:Z - - if-eqz v13, :cond_14e - - const/4 v13, 0x0 - - goto :goto_150 - - :cond_14e - const/high16 v13, 0x3f800000 # 1.0f - - :goto_150 - move/from16 v40, v13 - - iget-boolean v13, v0, LSmali;->b13:Z - - if-eqz v13, :cond_159 - - const/16 v16, 0x0 - - goto :goto_15b - - :cond_159 - const/high16 v16, 0x3f800000 # 1.0f - - :goto_15b - move/from16 v13, v16 - - move/from16 v41, v13 - - iget-boolean v13, v0, LSmali;->conditionA:Z - - if-eqz v13, :cond_1a2 - - const/high16 v13, 0x447a0000 # 1000.0f - - div-float/2addr v5, v13 - - div-float/2addr v2, v13 - - div-float/2addr v6, v13 - - div-float/2addr v7, v13 - - div-float/2addr v8, v13 - - div-float/2addr v9, v13 - - div-float/2addr v10, v13 - - div-float/2addr v12, v13 - - div-float/2addr v11, v13 - - div-float/2addr v1, v13 - - div-float/2addr v4, v13 - - div-float/2addr v3, v13 - - div-float/2addr v15, v13 - - div-float/2addr v14, v13 - - div-float v16, v39, v13 - - div-float v38, v38, v13 - - div-float v37, v37, v13 - - div-float v36, v36, v13 - - div-float v35, v35, v13 - - div-float v32, v32, v13 - - div-float v31, v31, v13 - - div-float v30, v30, v13 - - div-float v29, v29, v13 - - div-float v28, v28, v13 - - div-float v25, v25, v13 - - div-float v24, v24, v13 - - div-float v23, v23, v13 - - div-float v22, v22, v13 - - div-float v21, v21, v13 - - div-float v39, v40, v13 - - div-float v40, v41, v13 - - div-float v33, v33, v13 - - div-float v34, v34, v13 - - div-float v26, v26, v13 - - div-float v27, v27, v13 - - div-float v19, v19, v13 - - div-float v13, v20, v13 - - goto :goto_1aa - - :cond_1a2 - move/from16 v13, v20 - - move/from16 v16, v39 - - move/from16 v39, v40 - - move/from16 v40, v41 - - :goto_1aa - move/from16 v42, v13 - - iget-boolean v13, v0, LSmali;->conditionB:Z - - const/high16 v20, 0x42c80000 # 100.0f - - if-eqz v13, :cond_1fd - - div-float v5, v5, v20 - - div-float v2, v2, v20 - - div-float v6, v6, v20 - - div-float v7, v7, v20 - - div-float v8, v8, v20 - - div-float v9, v9, v20 - - div-float v10, v10, v20 - - div-float v12, v12, v20 - - div-float v11, v11, v20 - - div-float v1, v1, v20 - - div-float v4, v4, v20 - - div-float v3, v3, v20 - - div-float v15, v15, v20 - - div-float v14, v14, v20 - - div-float v16, v16, v20 - - div-float v38, v38, v20 - - div-float v37, v37, v20 - - div-float v36, v36, v20 - - div-float v35, v35, v20 - - div-float v32, v32, v20 - - div-float v31, v31, v20 - - div-float v30, v30, v20 - - div-float v29, v29, v20 - - div-float v28, v28, v20 - - div-float v25, v25, v20 - - div-float v24, v24, v20 - - div-float v23, v23, v20 - - div-float v22, v22, v20 - - div-float v21, v21, v20 - - div-float v39, v39, v20 - - div-float v40, v40, v20 - - div-float v33, v33, v20 - - div-float v34, v34, v20 - - div-float v26, v26, v20 - - div-float v27, v27, v20 - - div-float v19, v19, v20 - - div-float v13, v42, v20 - - goto :goto_1ff - - :cond_1fd - move/from16 v13, v42 - - :goto_1ff - move/from16 v43, v13 - - iget-boolean v13, v0, LSmali;->conditionC:Z - - if-eqz v13, :cond_244 - - const/high16 v13, 0x41400000 # 12.0f - - div-float/2addr v5, v13 - - div-float/2addr v2, v13 - - div-float/2addr v6, v13 - - div-float/2addr v7, v13 - - div-float/2addr v8, v13 - - div-float/2addr v9, v13 - - div-float/2addr v10, v13 - - div-float/2addr v12, v13 - - div-float/2addr v11, v13 - - div-float/2addr v1, v13 - - div-float/2addr v4, v13 - - div-float/2addr v3, v13 - - div-float/2addr v15, v13 - - div-float/2addr v14, v13 - - div-float v16, v16, v13 - - div-float v38, v38, v13 - - div-float v37, v37, v13 - - div-float v36, v36, v13 - - div-float v35, v35, v13 - - div-float v32, v32, v13 - - div-float v31, v31, v13 - - div-float v30, v30, v13 - - div-float v29, v29, v13 - - div-float v28, v28, v13 - - div-float v25, v25, v13 - - div-float v24, v24, v13 - - div-float v23, v23, v13 - - div-float v22, v22, v13 - - div-float v21, v21, v13 - - div-float v39, v39, v13 - - div-float v40, v40, v13 - - div-float v33, v33, v13 - - div-float v34, v34, v13 - - div-float v26, v26, v13 - - div-float v27, v27, v13 - - div-float v19, v19, v13 - - div-float v13, v43, v13 - - goto :goto_246 - - :cond_244 - move/from16 v13, v43 - - :goto_246 - const/16 v17, 0x0 - - mul-float v0, v20, v17 - - invoke-static {v0}, Ljava/lang/Math;->round(F)I - - move-result v0 - - int-to-float v0, v0 - - div-float v0, v0, v20 - - move/from16 v44, v1 - - new-instance v1, Ljava/lang/StringBuilder; - - invoke-direct {v1}, Ljava/lang/StringBuilder;->()V - - invoke-virtual {v1, v0}, Ljava/lang/StringBuilder;->append(F)Ljava/lang/StringBuilder; - - move/from16 v45, v0 - - move-object/from16 v0, v18 - - invoke-virtual {v1, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - - invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; - - move-result-object v1 - - return-void -.end method diff --git a/test/626-checker-arm64-scratch-register/src/Main.java b/test/626-checker-arm64-scratch-register/src/Main.java index cf94b87666..139491769e 100644 --- a/test/626-checker-arm64-scratch-register/src/Main.java +++ b/test/626-checker-arm64-scratch-register/src/Main.java @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; public class Main { @@ -65,17 +63,14 @@ public class Main { /// CHECK: name "B0" /// CHECK: <> ParameterValue /// CHECK: end_block - - /// CHECK: begin_block - /// CHECK: successors "<>" "<>" - /// CHECK: <> InstanceFieldGet [<>] field_name:Main.conditionA - /// CHECK: If [<>] - /// CHECK: end_block - /// CHECK: begin_block - /// CHECK: successors "<>" "<>" + /// CHECK: successors "<>" "<>" /// CHECK: <> InstanceFieldGet [<>] field_name:Main.conditionB /// CHECK: If [<>] + /// CHECK: end_block + /// CHECK: begin_block + /// CHECK: name "<>" + /// CHECK: ParallelMove moves:[40(sp)->d0,24(sp)->32(sp),28(sp)->36(sp),d0->d3,d3->d4,d2->d5,d4->d6,d5->d7,d6->d18,d7->d19,d18->d20,d19->d21,d20->d22,d21->d23,d22->d10,d23->d11,16(sp)->24(sp),20(sp)->28(sp),d10->d14,d11->d12,d12->d13,d13->d1,d14->d2,32(sp)->16(sp),36(sp)->20(sp)] /// CHECK: end_block /// CHECK-START-ARM64: void Main.test() disassembly (after) @@ -87,6 +82,39 @@ public class Main { /// CHECK: successors "<>" "<>" /// CHECK: <> InstanceFieldGet [<>] field_name:Main.conditionB /// CHECK: If [<>] + /// CHECK: end_block + /// CHECK: begin_block + /// CHECK: name "<>" + /// CHECK: ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid] + /// CHECK: fmov d31, d2 + /// CHECK: ldr s2, [sp, #36] + /// CHECK: ldr w16, [sp, #16] + /// CHECK: str w16, [sp, #36] + /// CHECK: str s14, [sp, #16] + /// CHECK: ldr s14, [sp, #28] + /// CHECK: str s1, [sp, #28] + /// CHECK: ldr s1, [sp, #32] + /// CHECK: str s31, [sp, #32] + /// CHECK: ldr s31, [sp, #20] + /// CHECK: str s31, [sp, #40] + /// CHECK: str s12, [sp, #20] + /// CHECK: fmov d12, d11 + /// CHECK: fmov d11, d10 + /// CHECK: fmov d10, d23 + /// CHECK: fmov d23, d22 + /// CHECK: fmov d22, d21 + /// CHECK: fmov d21, d20 + /// CHECK: fmov d20, d19 + /// CHECK: fmov d19, d18 + /// CHECK: fmov d18, d7 + /// CHECK: fmov d7, d6 + /// CHECK: fmov d6, d5 + /// CHECK: fmov d5, d4 + /// CHECK: fmov d4, d3 + /// CHECK: fmov d3, d13 + /// CHECK: ldr s13, [sp, #24] + /// CHECK: str s3, [sp, #24] + /// CHECK: ldr s3, pc+{{\d+}} (addr {{0x[0-9a-f]+}}) (100) /// CHECK: end_block public void test() { @@ -261,20 +289,9 @@ public class Main { String res = s + r; } - public static void main(String[] args) throws Exception { + public static void main(String[] args) { Main main = new Main(); main.test(); - - Class cl = Class.forName("Smali"); - Constructor cons = cl.getConstructor(); - Object o = cons.newInstance(); - - Method test = cl.getMethod("test"); - test.invoke(o); - - Method testD8 = cl.getMethod("testD8"); - testD8.invoke(o); - System.out.println("passed"); } } -- GitLab From a4ba9b53d8c102369a316c93ff14eaa488493435 Mon Sep 17 00:00:00 2001 From: Alan Leung Date: Thu, 16 Nov 2017 00:19:18 -0800 Subject: [PATCH 050/226] Rename d8 to d8-compat-dx Bug: 69368371 Test: ./art/test/testrunner/run_build_test_target.py -j110 art-test Change-Id: If06113796469393682bc5cd376fac917850bf384 --- build/Android.gtest.mk | 2 +- test/Android.run-test.mk | 2 +- test/etc/default-build | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 42d0ba57cf..3c8eade773 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -608,7 +608,7 @@ define define-test-art-gtest-combination endif .PHONY: $$(rule_name) -$$(rule_name): $$(dependencies) dx d8 +$$(rule_name): $$(dependencies) dx d8-compat-dx $(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) # Clear locally defined variables. diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 8755f04320..fe4a327a1f 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -27,7 +27,7 @@ TEST_ART_RUN_TEST_DEPENDENCIES := \ # Add d8 dependency, if enabled. ifeq ($(USE_D8),true) TEST_ART_RUN_TEST_DEPENDENCIES += \ - $(HOST_OUT_EXECUTABLES)/d8 + $(HOST_OUT_EXECUTABLES)/d8-compat-dx endif # Convert's a rule name to the form used in variables, e.g. no-relocate to NO_RELOCATE diff --git a/test/etc/default-build b/test/etc/default-build index f14424ecf9..5c8257f210 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -294,7 +294,7 @@ function make_dex() { local dexer="${DX}" if [ ${USE_D8} = "true" ]; then - dexer="${ANDROID_HOST_OUT}/bin/d8" + dexer="${ANDROID_HOST_OUT}/bin/d8-compat-dx" fi # Make dex file from desugared JAR. -- GitLab From eecced8b2b2de3f74c317fa574f17e2fad5b40b9 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 16 Nov 2017 09:08:14 +0000 Subject: [PATCH 051/226] Update test names after package changes. Test: run-libcore-tests Change-Id: I7bb8b4818662607122e929a1e57ebf3ce086678b --- tools/libcore_gcstress_debug_failures.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt index d27b8fc06c..0029b0a01a 100644 --- a/tools/libcore_gcstress_debug_failures.txt +++ b/tools/libcore_gcstress_debug_failures.txt @@ -9,9 +9,9 @@ result: EXEC_FAILED, modes: [device], names: ["jsr166.CompletableFutureTest#testCompleteOnTimeout_completed", - "libcore.icu.TransliteratorTest#testAll", - "libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045", - "libcore.icu.RelativeDateTimeFormatterTest#test_bug25883157", + "libcore.libcore.icu.TransliteratorTest#testAll", + "libcore.libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045", + "libcore.libcore.icu.RelativeDateTimeFormatterTest#test_bug25883157", "libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndTimeout", "libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndNoTimeout", "libcore.java.util.TimeZoneTest#testSetDefaultDeadlock", -- GitLab From 46721ef33e8f5cd405c291d72e3f259e3085fb5f Mon Sep 17 00:00:00 2001 From: Mingyao Yang Date: Thu, 5 Oct 2017 14:45:17 -0700 Subject: [PATCH 052/226] Don't merge values for exit block in LSE. This enables some additional optimizations since exit block doesn't really merge values. Test: run-test on host. Change-Id: I21ed7e0e43a3bc5d9ed2dabfad8462129b904eb7 --- compiler/optimizing/load_store_elimination.cc | 55 ++++++++++++++++--- compiler/optimizing/nodes.cc | 5 ++ compiler/optimizing/nodes.h | 1 + test/530-checker-lse/src/Main.java | 54 ++++++++++++++++++ 4 files changed, 107 insertions(+), 8 deletions(-) diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 605fdae0f8..89ad85e0b4 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -199,6 +199,12 @@ class LSEVisitor : public HGraphDelegateVisitor { if (predecessors.size() == 0) { return; } + if (block->IsExitBlock()) { + // Exit block doesn't really merge values since the control flow ends in + // its predecessors. Each predecessor needs to make sure stores are kept + // if necessary. + return; + } ScopedArenaVector& heap_values = heap_values_for_[block->GetBlockId()]; for (size_t i = 0; i < heap_values.size(); i++) { @@ -233,15 +239,23 @@ class LSEVisitor : public HGraphDelegateVisitor { } } - if (merged_value == kUnknownHeapValue || ref_info->IsSingletonAndNonRemovable()) { - // There are conflicting heap values from different predecessors, - // or the heap value may be needed after method return or deoptimization. - // Keep the last store in each predecessor since future loads cannot be eliminated. - for (HBasicBlock* predecessor : predecessors) { - ScopedArenaVector& pred_values = - heap_values_for_[predecessor->GetBlockId()]; - KeepIfIsStore(pred_values[i]); + if (ref_info->IsSingleton()) { + if (ref_info->IsSingletonAndNonRemovable() || + (merged_value == kUnknownHeapValue && + !block->IsSingleReturnOrReturnVoidAllowingPhis())) { + // The heap value may be needed after method return or deoptimization, + // or there are conflicting heap values from different predecessors and + // this block is not a single return, + // keep the last store in each predecessor since future loads may not + // be eliminated. + for (HBasicBlock* predecessor : predecessors) { + ScopedArenaVector& pred_values = + heap_values_for_[predecessor->GetBlockId()]; + KeepIfIsStore(pred_values[i]); + } } + } else { + // Currenctly we don't eliminate stores to non-singletons. } if ((merged_value == nullptr) || !from_all_predecessors) { @@ -549,6 +563,31 @@ class LSEVisitor : public HGraphDelegateVisitor { } } + // Keep necessary stores before exiting a method via return/throw. + void HandleExit(HBasicBlock* block) { + const ScopedArenaVector& heap_values = + heap_values_for_[block->GetBlockId()]; + for (size_t i = 0; i < heap_values.size(); i++) { + HInstruction* heap_value = heap_values[i]; + ReferenceInfo* ref_info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo(); + if (!ref_info->IsSingletonAndRemovable()) { + KeepIfIsStore(heap_value); + } + } + } + + void VisitReturn(HReturn* instruction) OVERRIDE { + HandleExit(instruction->GetBlock()); + } + + void VisitReturnVoid(HReturnVoid* return_void) OVERRIDE { + HandleExit(return_void->GetBlock()); + } + + void VisitThrow(HThrow* throw_instruction) OVERRIDE { + HandleExit(throw_instruction->GetBlock()); + } + void HandleInvoke(HInstruction* instruction) { SideEffects side_effects = instruction->GetSideEffects(); ScopedArenaVector& heap_values = diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index f4f6434678..fff61f5727 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1810,6 +1810,11 @@ bool HBasicBlock::IsSingleReturn() const { return HasOnlyOneInstruction(*this) && GetLastInstruction()->IsReturn(); } +bool HBasicBlock::IsSingleReturnOrReturnVoidAllowingPhis() const { + return (GetFirstInstruction() == GetLastInstruction()) && + (GetLastInstruction()->IsReturn() || GetLastInstruction()->IsReturnVoid()); +} + bool HBasicBlock::IsSingleTryBoundary() const { return HasOnlyOneInstruction(*this) && GetLastInstruction()->IsTryBoundary(); } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 29c78a1e34..6672901781 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -968,6 +968,7 @@ class HBasicBlock : public ArenaObject { bool IsSingleGoto() const; bool IsSingleReturn() const; + bool IsSingleReturnOrReturnVoidAllowingPhis() const; bool IsSingleTryBoundary() const; // Returns true if this block emits nothing but a jump. diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java index ca8108f058..c4cc3b0121 100644 --- a/test/530-checker-lse/src/Main.java +++ b/test/530-checker-lse/src/Main.java @@ -949,6 +949,56 @@ public class Main { return array[1] + array[i]; } + /// CHECK-START: int Main.testExitMerge(boolean) load_store_elimination (before) + /// CHECK: NewInstance + /// CHECK: InstanceFieldSet + /// CHECK: InstanceFieldGet + /// CHECK: Return + /// CHECK: InstanceFieldSet + /// CHECK: Throw + + /// CHECK-START: int Main.testExitMerge(boolean) load_store_elimination (after) + /// CHECK-NOT: NewInstance + /// CHECK-NOT: InstanceFieldSet + /// CHECK-NOT: InstanceFieldGet + /// CHECK: Return + /// CHECK-NOT: InstanceFieldSet + /// CHECK: Throw + private static int testExitMerge(boolean cond) { + TestClass obj = new TestClass(); + if (cond) { + obj.i = 1; + return obj.i + 1; + } else { + obj.i = 2; + throw new Error(); + } + } + + /// CHECK-START: int Main.testExitMerge2(boolean) load_store_elimination (before) + /// CHECK: NewInstance + /// CHECK: InstanceFieldSet + /// CHECK: InstanceFieldGet + /// CHECK: InstanceFieldSet + /// CHECK: InstanceFieldGet + + /// CHECK-START: int Main.testExitMerge2(boolean) load_store_elimination (after) + /// CHECK-NOT: NewInstance + /// CHECK-NOT: InstanceFieldSet + /// CHECK-NOT: InstanceFieldGet + private static int testExitMerge2(boolean cond) { + TestClass obj = new TestClass(); + int res; + if (cond) { + obj.i = 1; + res = obj.i + 1; + } else { + obj.i = 2; + res = obj.j + 2; + } + return res; + } + static void assertIntEquals(int result, int expected) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); @@ -1039,6 +1089,10 @@ public class Main { assertIntEquals(testStoreStore().j, 43); assertIntEquals(testStoreStoreWithDeoptimize(new int[4]), 4); + assertIntEquals(testExitMerge(true), 2); + assertIntEquals(testExitMerge2(true), 2); + assertIntEquals(testExitMerge2(false), 2); + int ret = testNoSideEffects(iarray); assertIntEquals(iarray[0], 101); assertIntEquals(iarray[1], 102); -- GitLab From 32f20735131e720b8ccca82e38f98c8b97435e20 Mon Sep 17 00:00:00 2001 From: Chih-Hung Hsieh Date: Thu, 16 Nov 2017 11:33:14 -0800 Subject: [PATCH 053/226] Use -Werror in art/libart_fake Bug: 66996870 Test: build with WITH_TIDY=1 Change-Id: Ic04b16f2441a7694e39ebb9aafe04b2f70053cf5 --- libart_fake/Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/libart_fake/Android.mk b/libart_fake/Android.mk index ed868a5bd5..96e6a14903 100644 --- a/libart_fake/Android.mk +++ b/libart_fake/Android.mk @@ -22,6 +22,7 @@ LOCAL_INSTALLED_MODULE_STEM := libart.so LOCAL_SDK_VERSION := 9 LOCAL_CPP_EXTENSION := .cc LOCAL_SRC_FILES := fake.cc +LOCAL_CFLAGS := -Wall -Werror LOCAL_SHARED_LIBRARIES := liblog ifdef TARGET_2ND_ARCH -- GitLab From 5573c37e795668eca81a8488078f798d977685c3 Mon Sep 17 00:00:00 2001 From: Igor Murashkin Date: Thu, 16 Nov 2017 13:34:30 -0800 Subject: [PATCH 054/226] cpplint: Remove many unnecessary NOLINT Now that we updated to upstream cpplint, a lot of these NOLINTs are no longer necessary. Bug: 68951293 Change-Id: If8ed5ffe89727f313f907a214b6d8fd2a2eddbad --- CPPLINT.cfg | 1 + cmdline/cmdline_parser.h | 2 +- cmdline/cmdline_parser_test.cc | 12 ++++----- cmdline/cmdline_types.h | 4 +-- compiler/debug/dwarf/dwarf_test.cc | 10 +++---- .../driver/compiler_options_map-storage.h | 2 +- .../optimizing/induction_var_analysis_test.cc | 2 +- compiler/utils/intrusive_forward_list_test.cc | 6 ++--- compiler/utils/swap_space.h | 4 +-- compiler/utils/x86/assembler_x86_test.cc | 4 +-- dex2oat/dex2oat_image_test.cc | 2 +- dex2oat/dex2oat_options.cc | 2 +- dex2oat/dex2oat_test.cc | 2 +- dex2oat/linker/oat_writer_test.cc | 4 +-- imgdiag/imgdiag.cc | 2 +- openjdkjvmti/jvmti_allocator.h | 4 +-- openjdkjvmti/ti_class.cc | 2 +- openjdkjvmti/ti_extension.cc | 16 ++++++------ openjdkjvmti/ti_monitor.cc | 6 ++--- runtime/base/allocator.h | 2 +- runtime/base/arena_containers.h | 4 +-- runtime/base/bit_string.h | 2 +- runtime/base/bit_string_test.cc | 4 +-- runtime/base/bit_struct.h | 2 +- runtime/base/bit_struct_detail.h | 2 +- runtime/base/bit_struct_test.cc | 26 +++++++++---------- runtime/base/mutex.cc | 2 +- runtime/base/scoped_arena_containers.h | 4 +-- runtime/base/transform_array_ref.h | 2 +- runtime/base/transform_array_ref_test.cc | 4 +-- runtime/base/transform_iterator.h | 2 +- runtime/base/transform_iterator_test.cc | 12 ++++----- runtime/base/variant_map.h | 2 +- runtime/base/variant_map_test.cc | 4 +-- runtime/common_runtime_test.cc | 8 +++--- runtime/dex_file_test.cc | 20 +++++++------- runtime/experimental_flags.h | 4 +-- runtime/indenter.h | 2 +- runtime/mem_map.h | 2 +- runtime/obj_ptr.h | 9 +++---- runtime/parsed_options.cc | 2 +- runtime/prebuilt_tools_test.cc | 4 +-- runtime/reference_table_test.cc | 2 +- runtime/runtime_callbacks_test.cc | 2 +- runtime/runtime_options.cc | 2 +- runtime/subtype_check.h | 2 +- runtime/subtype_check_info.h | 6 ++--- runtime/subtype_check_info_test.cc | 10 +++---- sigchainlib/sigchain.cc | 2 +- .../1919-vminit-thread-start-timing/vminit.cc | 4 +-- test/901-hello-ti-agent/basics.cc | 2 +- test/904-object-allocation/tracking.cc | 2 +- test/912-classes/classes.cc | 4 +-- test/912-classes/classes_art.cc | 2 +- test/924-threads/threads.cc | 2 +- tools/breakpoint-logger/breakpoint_logger.cc | 4 +-- tools/titrace/titrace.cc | 8 +++--- .../wrapagentproperties.cc | 2 +- 58 files changed, 134 insertions(+), 134 deletions(-) diff --git a/CPPLINT.cfg b/CPPLINT.cfg index bf0ad58320..83288421e2 100644 --- a/CPPLINT.cfg +++ b/CPPLINT.cfg @@ -25,6 +25,7 @@ linelength=100 # Ignore the following categories of errors, as specified by the filter: # (the filter settings are concatenated together) +filter=-build/c++11 filter=-build/include filter=-readability/function,-readability/streams,-readability/todo filter=-runtime/printf,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn diff --git a/cmdline/cmdline_parser.h b/cmdline/cmdline_parser.h index fef39ad930..82c04e70f5 100644 --- a/cmdline/cmdline_parser.h +++ b/cmdline/cmdline_parser.h @@ -340,7 +340,7 @@ struct CmdlineParser { typename std::enable_if::value>::type InitializeTypedBuilder(ArgumentBuilder* arg_builder) { // Every Unit argument implicitly maps to a runtime value of Unit{} - std::vector values(names_.size(), Unit{}); // NOLINT [whitespace/braces] [5] + std::vector values(names_.size(), Unit{}); arg_builder->SetValuesInternal(std::move(values)); } diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index 34aea55f5b..1536339515 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -190,7 +190,7 @@ class CmdlineParserTest : public ::testing::Test { #define EXPECT_SINGLE_PARSE_VALUE(expected, argv, key) \ _EXPECT_SINGLE_PARSE_EXISTS(argv, key); \ EXPECT_KEY_VALUE(args, key, expected); \ - } while (false) // NOLINT [readability/namespace] [5] + } while (false) #define EXPECT_SINGLE_PARSE_VALUE_STR(expected, argv, key) \ EXPECT_SINGLE_PARSE_VALUE(std::string(expected), argv, key) @@ -318,7 +318,7 @@ TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) { * Test success */ { - XGcOption option_all_true{}; // NOLINT [readability/braces] [4] + XGcOption option_all_true{}; option_all_true.collector_type_ = gc::CollectorType::kCollectorTypeCMS; option_all_true.verify_pre_gc_heap_ = true; option_all_true.verify_pre_sweeping_heap_ = true; @@ -335,7 +335,7 @@ TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) { EXPECT_SINGLE_PARSE_VALUE(option_all_true, xgc_args_all_true, M::GcOption); - XGcOption option_all_false{}; // NOLINT [readability/braces] [4] + XGcOption option_all_false{}; option_all_false.collector_type_ = gc::CollectorType::kCollectorTypeMS; option_all_false.verify_pre_gc_heap_ = false; option_all_false.verify_pre_sweeping_heap_ = false; @@ -350,7 +350,7 @@ TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) { EXPECT_SINGLE_PARSE_VALUE(option_all_false, xgc_args_all_false, M::GcOption); - XGcOption option_all_default{}; // NOLINT [readability/braces] [4] + XGcOption option_all_default{}; const char* xgc_args_blank = "-Xgc:"; EXPECT_SINGLE_PARSE_VALUE(option_all_default, xgc_args_blank, M::GcOption); @@ -566,10 +566,10 @@ TEST_F(CmdlineParserTest, MultipleArguments) { auto&& map = parser_->ReleaseArgumentsMap(); EXPECT_EQ(5u, map.Size()); - EXPECT_KEY_VALUE(map, M::Help, Unit{}); // NOLINT [whitespace/braces] [5] + EXPECT_KEY_VALUE(map, M::Help, Unit{}); EXPECT_KEY_VALUE(map, M::ForegroundHeapGrowthMultiplier, 0.5); EXPECT_KEY_VALUE(map, M::Dex2Oat, false); - EXPECT_KEY_VALUE(map, M::MethodTrace, Unit{}); // NOLINT [whitespace/braces] [5] + EXPECT_KEY_VALUE(map, M::MethodTrace, Unit{}); EXPECT_KEY_VALUE(map, M::LargeObjectSpace, gc::space::LargeObjectSpaceType::kMap); } // TEST_F } // namespace art diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index 87bf1c4d43..37bdcdc5e2 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -57,7 +57,7 @@ template <> struct CmdlineType : CmdlineTypeParser { Result Parse(const std::string& args) { if (args == "") { - return Result::Success(Unit{}); // NOLINT [whitespace/braces] [5] + return Result::Success(Unit{}); } return Result::Failure("Unexpected extra characters " + args); } @@ -532,7 +532,7 @@ struct XGcOption { template <> struct CmdlineType : CmdlineTypeParser { Result Parse(const std::string& option) { // -Xgc: already stripped - XGcOption xgc{}; // NOLINT [readability/braces] [4] + XGcOption xgc{}; std::vector gc_options; Split(option, ',', &gc_options); diff --git a/compiler/debug/dwarf/dwarf_test.cc b/compiler/debug/dwarf/dwarf_test.cc index 866bf4394d..933034f593 100644 --- a/compiler/debug/dwarf/dwarf_test.cc +++ b/compiler/debug/dwarf/dwarf_test.cc @@ -125,7 +125,7 @@ TEST_F(DwarfTest, DebugFrame) { WriteCIE(is64bit, Reg(is64bit ? 16 : 8), initial_opcodes, kCFIFormat, &debug_frame_data_); std::vector debug_frame_patches; - std::vector expected_patches { 28 }; // NOLINT + std::vector expected_patches = { 28 }; WriteFDE(is64bit, 0, 0, 0x01000000, 0x01000000, ArrayRef(*opcodes.data()), kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches); @@ -140,7 +140,7 @@ TEST_F(DwarfTest, DebugFrame64) { initial_opcodes, kCFIFormat, &debug_frame_data_); DebugFrameOpCodeWriter<> opcodes; std::vector debug_frame_patches; - std::vector expected_patches { 32 }; // NOLINT + std::vector expected_patches = { 32 }; WriteFDE(is64bit, 0, 0, 0x0100000000000000, 0x0200000000000000, ArrayRef(*opcodes.data()), kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches); @@ -237,7 +237,7 @@ TEST_F(DwarfTest, DebugLine) { DW_CHECK_NEXT("1\t0\t1000\t2000\tfile.c"); std::vector debug_line_patches; - std::vector expected_patches { 87 }; // NOLINT + std::vector expected_patches = { 87 }; WriteDebugLineTable(include_directories, files, opcodes, 0, &debug_line_data_, &debug_line_patches); @@ -275,7 +275,7 @@ TEST_F(DwarfTest, DebugLineSpecialOpcodes) { EXPECT_LT(opcodes.data()->size(), num_rows * 3); std::vector directories; - std::vector files { { "file.c", 0, 1000, 2000 } }; // NOLINT + std::vector files = { { "file.c", 0, 1000, 2000 } }; std::vector debug_line_patches; WriteDebugLineTable(directories, files, opcodes, 0, &debug_line_data_, &debug_line_patches); @@ -333,7 +333,7 @@ TEST_F(DwarfTest, DebugInfo) { DW_CHECK("3 DW_TAG_compile_unit [no children]"); std::vector debug_info_patches; - std::vector expected_patches { 16, 20, 29, 33, 42, 46 }; // NOLINT + std::vector expected_patches = { 16, 20, 29, 33, 42, 46 }; dwarf::WriteDebugInfoCU(0 /* debug_abbrev_offset */, info, 0, &debug_info_data_, &debug_info_patches); diff --git a/compiler/driver/compiler_options_map-storage.h b/compiler/driver/compiler_options_map-storage.h index 756598de05..01f32e0c9c 100644 --- a/compiler/driver/compiler_options_map-storage.h +++ b/compiler/driver/compiler_options_map-storage.h @@ -36,7 +36,7 @@ #define COMPILER_OPTIONS_KEY(Type, Name, ...) \ template class KeyType> \ - const KeyType CompilerOptionsMap::Name {__VA_ARGS__}; // NOLINT [readability/braces] [4] + const KeyType CompilerOptionsMap::Name {__VA_ARGS__}; #include template struct CompilerOptionsMap; diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc index f87b46d4ee..4c11ad4643 100644 --- a/compiler/optimizing/induction_var_analysis_test.cc +++ b/compiler/optimizing/induction_var_analysis_test.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include // NOLINT [build/c++11] [5] +#include #include "base/arena_allocator.h" #include "builder.h" diff --git a/compiler/utils/intrusive_forward_list_test.cc b/compiler/utils/intrusive_forward_list_test.cc index 939676cdc8..e97c3044d0 100644 --- a/compiler/utils/intrusive_forward_list_test.cc +++ b/compiler/utils/intrusive_forward_list_test.cc @@ -574,11 +574,11 @@ void IntrusiveForwardListTest::Remove() { ref.remove(4); ifl.remove(4); ASSERT_LISTS_EQUAL(ref, ifl); - auto odd = [](ValueType value) { return (value.value & 1) != 0; }; // NOLINT(readability/braces) + auto odd = [](ValueType value) { return (value.value & 1) != 0; }; ref.remove_if(odd); ifl.remove_if(odd); ASSERT_LISTS_EQUAL(ref, ifl); - auto all = [](ValueType value ATTRIBUTE_UNUSED) { return true; }; // NOLINT(readability/braces) + auto all = [](ValueType value ATTRIBUTE_UNUSED) { return true; }; ref.remove_if(all); ifl.remove_if(all); ASSERT_LISTS_EQUAL(ref, ifl); @@ -721,7 +721,7 @@ void IntrusiveForwardListTest::ModifyValue() { ListType ifl(storage.begin(), storage.end()); ASSERT_LISTS_EQUAL(ref, ifl); - auto add1 = [](const ValueType& value) { return value.value + 1; }; // NOLINT [readability/braces] + auto add1 = [](const ValueType& value) { return value.value + 1; }; std::transform(ref.begin(), ref.end(), ref.begin(), add1); std::transform(ifl.begin(), ifl.end(), ifl.begin(), add1); ASSERT_LISTS_EQUAL(ref, ifl); diff --git a/compiler/utils/swap_space.h b/compiler/utils/swap_space.h index 08e243b644..2280f8b993 100644 --- a/compiler/utils/swap_space.h +++ b/compiler/utils/swap_space.h @@ -124,7 +124,7 @@ class SwapAllocator { explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {} template - SwapAllocator(const SwapAllocator& other) // NOLINT, implicit + SwapAllocator(const SwapAllocator& other) : swap_space_(other.swap_space_) {} SwapAllocator(const SwapAllocator& other) = default; @@ -160,7 +160,7 @@ class SwapAllocator { explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {} template - SwapAllocator(const SwapAllocator& other) // NOLINT, implicit + SwapAllocator(const SwapAllocator& other) : swap_space_(other.swap_space_) {} SwapAllocator(const SwapAllocator& other) = default; diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index e232addd26..36c5c3c0c4 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -90,7 +90,7 @@ class AssemblerX86Test : public AssemblerTest // NOLINT [build/c++11] [5] +#include #include #include #include diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc index 2cf070155f..7f177b9ff6 100644 --- a/dex2oat/dex2oat_options.cc +++ b/dex2oat/dex2oat_options.cc @@ -43,7 +43,7 @@ struct CmdlineType : CmdlineTypeParser { // Specify storage for the Dex2oatOptions keys. #define DEX2OAT_OPTIONS_KEY(Type, Name, ...) \ - const Dex2oatArgumentMap::Key Dex2oatArgumentMap::Name {__VA_ARGS__}; // NOLINT [readability/braces] [4] + const Dex2oatArgumentMap::Key Dex2oatArgumentMap::Name {__VA_ARGS__}; #include "dex2oat_options.def" #pragma GCC diagnostic push diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index fdada8f926..bd8583bd41 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include // NOLINT [build/c++11] [5] +#include #include #include #include diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 022aa1b58c..7509d91677 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -718,7 +718,7 @@ void OatTest::TestZipFileInput(bool verify) { key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); { // Test using the AddDexFileSource() interface with the zip file. - std::vector input_filenames { zip_file.GetFilename().c_str() }; // NOLINT [readability/braces] [4] + 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, @@ -829,7 +829,7 @@ void OatTest::TestZipFileInputWithEmptyDex() { SafeMap key_value_store; key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); - std::vector input_filenames { zip_file.GetFilename().c_str() }; // NOLINT [readability/braces] [4] + 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, diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc index f0c9158748..05fce96780 100644 --- a/imgdiag/imgdiag.cc +++ b/imgdiag/imgdiag.cc @@ -1610,7 +1610,7 @@ class ImgDiagDumper { // BacktraceMap used for finding the memory mapping of the image file. std::unique_ptr proc_maps_; // Boot image mapping. - backtrace_map_t boot_map_{}; // NOLINT + backtrace_map_t boot_map_{}; // The size of the boot image mapping. size_t boot_map_size_; // The contents of /proc//maps. diff --git a/openjdkjvmti/jvmti_allocator.h b/openjdkjvmti/jvmti_allocator.h index e6cbc85170..11af7b67e7 100644 --- a/openjdkjvmti/jvmti_allocator.h +++ b/openjdkjvmti/jvmti_allocator.h @@ -58,7 +58,7 @@ class JvmtiAllocator { JvmtiAllocator() : env_(nullptr) {} template - JvmtiAllocator(const JvmtiAllocator& other) // NOLINT, implicit + JvmtiAllocator(const JvmtiAllocator& other) : env_(other.env_) {} JvmtiAllocator(const JvmtiAllocator& other) = default; @@ -95,7 +95,7 @@ class JvmtiAllocator { JvmtiAllocator() : env_(nullptr) {} template - JvmtiAllocator(const JvmtiAllocator& other) // NOLINT, implicit + JvmtiAllocator(const JvmtiAllocator& other) : env_(other.env_) {} JvmtiAllocator(const JvmtiAllocator& other) = default; diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc index 2c5e5f9ccf..e69c78bab1 100644 --- a/openjdkjvmti/ti_class.cc +++ b/openjdkjvmti/ti_class.cc @@ -33,7 +33,7 @@ #include "android-base/stringprintf.h" -#include // NOLINT [build/c++11] [5] +#include #include #include "art_jvmti.h" diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc index d3e0912190..afd0723d0f 100644 --- a/openjdkjvmti/ti_extension.cc +++ b/openjdkjvmti/ti_extension.cc @@ -54,7 +54,7 @@ struct CParamInfo { JvmtiUniquePtr param_name = CopyString(env, name, err); char* name_ptr = param_name.get(); char_buffers->push_back(std::move(param_name)); - return jvmtiParamInfo { name_ptr, kind, base_type, null_ok }; // NOLINT [whitespace/braces] [4] + return jvmtiParamInfo{ name_ptr, kind, base_type, null_ok }; } }; @@ -146,7 +146,7 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, "com.android.art.heap.get_object_heap_id", "Retrieve the heap id of the the object tagged with the given argument. An " "arbitrary object is chosen if multiple objects exist with the same tag.", - { // NOLINT [whitespace/braces] [4] + { { "tag", JVMTI_KIND_IN, JVMTI_TYPE_JLONG, false}, { "heap_id", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false} }, @@ -159,7 +159,7 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, reinterpret_cast(HeapExtensions::GetHeapName), "com.android.art.heap.get_heap_name", "Retrieve the name of the heap with the given id.", - { // NOLINT [whitespace/braces] [4] + { { "heap_id", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false}, { "heap_name", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_CCHAR, false} }, @@ -175,13 +175,13 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, " except for additionally passing the heap id of the current object. The jvmtiHeapCallbacks" " structure is reused, with the callbacks field overloaded to a signature of " "jint (*)(jlong, jlong, jlong*, jint length, void*, jint).", - { // NOLINT [whitespace/braces] [4] + { { "heap_filter", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false}, { "klass", JVMTI_KIND_IN, JVMTI_TYPE_JCLASS, true}, { "callbacks", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, false}, { "user_data", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, true} }, - { // NOLINT [whitespace/braces] [4] + { ERR(MUST_POSSESS_CAPABILITY), ERR(INVALID_CLASS), ERR(NULL_POINTER), @@ -197,7 +197,7 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, " 'Allocate' jvmti function. This does not include any memory that has been deallocated" " through the 'Deallocate' function. This number is approximate and might not correspond" " exactly to the sum of the sizes of all not freed allocations.", - { // NOLINT [whitespace/braces] [4] + { { "currently_allocated", JVMTI_KIND_OUT, JVMTI_TYPE_JLONG, false}, }, { ERR(NULL_POINTER) }); @@ -213,7 +213,7 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, " chunk format. It returns the processed chunk. This is provided for backwards compatibility" " reasons only. Agents should avoid making use of this extension when possible and instead" " use the other JVMTI entrypoints explicitly.", - { // NOLINT[whitespace/braces] [4] + { { "type_in", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, { "length_in", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, { "data_in", JVMTI_KIND_IN_BUF, JVMTI_TYPE_JBYTE, false }, @@ -322,7 +322,7 @@ jvmtiError ExtensionUtil::GetExtensionEvents(jvmtiEnv* env, " is responsible for interpreting the information present in the 'data' buffer. This is" " provided for backwards-compatibility support only. Agents should prefer to use relevant" " JVMTI events and functions above listening for this event.", - { // NOLINT[whitespace/braces] [4] + { { "jni_env", JVMTI_KIND_IN_PTR, JVMTI_TYPE_JNIENV, false }, { "type", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, { "data_size", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, diff --git a/openjdkjvmti/ti_monitor.cc b/openjdkjvmti/ti_monitor.cc index b31e9f239d..7db0566a2e 100644 --- a/openjdkjvmti/ti_monitor.cc +++ b/openjdkjvmti/ti_monitor.cc @@ -32,9 +32,9 @@ #include "ti_monitor.h" #include -#include // NOLINT [build/c++11] [5] -#include // NOLINT [build/c++11] [5] -#include // NOLINT [build/c++11] [5] +#include +#include +#include #include "art_jvmti.h" #include "monitor.h" diff --git a/runtime/base/allocator.h b/runtime/base/allocator.h index fba9308e8e..99cdb49984 100644 --- a/runtime/base/allocator.h +++ b/runtime/base/allocator.h @@ -111,7 +111,7 @@ class TrackingAllocatorImpl : public std::allocator { // Used internally by STL data structures. template - TrackingAllocatorImpl( // NOLINT, implicit + TrackingAllocatorImpl( const TrackingAllocatorImpl& alloc ATTRIBUTE_UNUSED) noexcept {} // Used internally by STL data structures. diff --git a/runtime/base/arena_containers.h b/runtime/base/arena_containers.h index 2e71156ee8..dcdb92b9d8 100644 --- a/runtime/base/arena_containers.h +++ b/runtime/base/arena_containers.h @@ -143,7 +143,7 @@ class ArenaAllocatorAdapter : private ArenaAllocatorAdapterKind { allocator_(allocator) { } template - ArenaAllocatorAdapter(const ArenaAllocatorAdapter& other) // NOLINT, implicit + ArenaAllocatorAdapter(const ArenaAllocatorAdapter& other) : ArenaAllocatorAdapterKind(other), allocator_(other.allocator_) { } @@ -179,7 +179,7 @@ class ArenaAllocatorAdapter : private ArenaAllocatorAdapterKind { allocator_(allocator) { } template - ArenaAllocatorAdapter(const ArenaAllocatorAdapter& other) // NOLINT, implicit + ArenaAllocatorAdapter(const ArenaAllocatorAdapter& other) : ArenaAllocatorAdapterKind(other), allocator_(other.allocator_) { } diff --git a/runtime/base/bit_string.h b/runtime/base/bit_string.h index 1cda021017..a2164f335d 100644 --- a/runtime/base/bit_string.h +++ b/runtime/base/bit_string.h @@ -248,7 +248,7 @@ struct BitString { // Does this bitstring contain exactly 0 characters? bool IsEmpty() const { - return (*this) == BitString{}; // NOLINT + return (*this) == BitString{}; } // Remove all BitStringChars starting at end. diff --git a/runtime/base/bit_string_test.cc b/runtime/base/bit_string_test.cc index d5610e7a73..96aa154ef3 100644 --- a/runtime/base/bit_string_test.cc +++ b/runtime/base/bit_string_test.cc @@ -47,7 +47,7 @@ BitStringChar MakeBitStringChar(size_t val) { BitString MakeBitString(std::initializer_list values = {}) { CHECK_GE(BitString::kCapacity, values.size()); - BitString bs{}; // NOLINT + BitString bs{}; size_t i = 0; for (size_t val : values) { @@ -68,7 +68,7 @@ size_t AsUint(const T& value) { // Make max bitstring, e.g. BitString[4095,7,255] for {12,3,8} template BitString MakeBitStringMax() { - BitString bs{}; // NOLINT + BitString bs{}; for (size_t i = 0; i < kCount; ++i) { bs.SetAt(i, diff --git a/runtime/base/bit_struct.h b/runtime/base/bit_struct.h index 16b555e1c6..b207459419 100644 --- a/runtime/base/bit_struct.h +++ b/runtime/base/bit_struct.h @@ -288,7 +288,7 @@ using BitStructUint = // // See top of file for usage example. #define BITSTRUCT_DEFINE_END(name) \ - }; /* NOLINT [readability/braces] [4] */ \ + }; \ static_assert(art::detail::ValidateBitStructSize(), \ #name "bitsize incorrect: " \ "did you insert extra fields that weren't BitStructX, " \ diff --git a/runtime/base/bit_struct_detail.h b/runtime/base/bit_struct_detail.h index 49d432e69c..912f51c7b0 100644 --- a/runtime/base/bit_struct_detail.h +++ b/runtime/base/bit_struct_detail.h @@ -79,7 +79,7 @@ struct HasUnderscoreField { using FalseT = std::integral_constant::type; template - static constexpr auto Test(void*) -> decltype(std::declval()._, TrueT{}); // NOLINT + static constexpr auto Test(void*) -> decltype(std::declval()._, TrueT{}); template static constexpr FalseT Test(...); diff --git a/runtime/base/bit_struct_test.cc b/runtime/base/bit_struct_test.cc index a80d39eb91..577682ccce 100644 --- a/runtime/base/bit_struct_test.cc +++ b/runtime/base/bit_struct_test.cc @@ -73,7 +73,7 @@ struct CustomBitStruct { TEST(BitStructs, Custom) { CustomBitStruct expected(0b1111); - BitStructField f{}; // NOLINT + BitStructField f{}; EXPECT_EQ(1u, sizeof(f)); @@ -95,7 +95,7 @@ TEST(BitStructs, TwoCustom) { VALIDATE_BITSTRUCT_SIZE(TestTwoCustom); - TestTwoCustom cst{}; // NOLINT + TestTwoCustom cst{}; // Test the write to most-significant field doesn't clobber least-significant. cst.f4_a = CustomBitStruct(0b0110); @@ -122,7 +122,7 @@ TEST(BitStructs, TwoCustom) { } TEST(BitStructs, Number) { - BitStructNumber bsn{}; // NOLINT + BitStructNumber bsn{}; EXPECT_EQ(2u, sizeof(bsn)); bsn = 0b1111; @@ -154,7 +154,7 @@ TEST(BitStructs, Test1) { EXPECT_EQ(1u, sizeof(u4)); EXPECT_EQ(1u, sizeof(alias_all)); } - TestBitStruct tst{}; // NOLINT + TestBitStruct tst{}; // Check minimal size selection is correct. EXPECT_EQ(1u, sizeof(TestBitStruct)); @@ -229,7 +229,7 @@ BITSTRUCT_DEFINE_END(MixedSizeBitStruct); TEST(BitStructs, Mixed) { EXPECT_EQ(4u, sizeof(MixedSizeBitStruct)); - MixedSizeBitStruct tst{}; // NOLINT + MixedSizeBitStruct tst{}; // Check operator assignment. tst.u3 = 0b111u; @@ -263,11 +263,11 @@ BITSTRUCT_DEFINE_START(TestBitStruct_u8, /* size */ 8) BITSTRUCT_DEFINE_END(TestBitStruct_u8); TEST(BitStructs, FieldAssignment) { - TestBitStruct_u8 all_1s{}; // NOLINT + TestBitStruct_u8 all_1s{}; all_1s.alias_all = 0xffu; { - TestBitStruct_u8 tst{}; // NOLINT + TestBitStruct_u8 tst{}; tst.i3 = all_1s.i3; // Copying a single bitfield does not copy all bitfields. @@ -275,7 +275,7 @@ TEST(BitStructs, FieldAssignment) { } { - TestBitStruct_u8 tst{}; // NOLINT + TestBitStruct_u8 tst{}; tst.u4 = all_1s.u4; // Copying a single bitfield does not copy all bitfields. @@ -291,13 +291,13 @@ BITSTRUCT_DEFINE_START(NestedStruct, /* size */ 64) BITSTRUCT_DEFINE_END(NestedStruct); TEST(BitStructs, NestedFieldAssignment) { - MixedSizeBitStruct mixed_all_1s{}; // NOLINT + MixedSizeBitStruct mixed_all_1s{}; mixed_all_1s.alias_all = 0xFFFFFFFFu; { - NestedStruct xyz{}; // NOLINT + NestedStruct xyz{}; - NestedStruct other{}; // NOLINT + NestedStruct other{}; other.mixed_upper = mixed_all_1s; other.mixed_lower = mixed_all_1s; @@ -307,9 +307,9 @@ TEST(BitStructs, NestedFieldAssignment) { } { - NestedStruct xyz{}; // NOLINT + NestedStruct xyz{}; - NestedStruct other{}; // NOLINT + NestedStruct other{}; other.mixed_upper = mixed_all_1s; other.mixed_lower = mixed_all_1s; diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 877f052006..3adebe7e00 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -584,7 +584,7 @@ ReaderWriterMutex::ReaderWriterMutex(const char* name, LockLevel level) #if ART_USE_FUTEXES , state_(0), num_pending_readers_(0), num_pending_writers_(0) #endif -{ // NOLINT(whitespace/braces) +{ #if !ART_USE_FUTEXES CHECK_MUTEX_CALL(pthread_rwlock_init, (&rwlock_, nullptr)); #endif diff --git a/runtime/base/scoped_arena_containers.h b/runtime/base/scoped_arena_containers.h index fccaaeaa42..756089f438 100644 --- a/runtime/base/scoped_arena_containers.h +++ b/runtime/base/scoped_arena_containers.h @@ -110,7 +110,7 @@ class ScopedArenaAllocatorAdapter arena_stack_(allocator->arena_stack_) { } template - ScopedArenaAllocatorAdapter(const ScopedArenaAllocatorAdapter& other) // NOLINT, implicit + ScopedArenaAllocatorAdapter(const ScopedArenaAllocatorAdapter& other) : DebugStackReference(other), DebugStackIndirectTopRef(other), ArenaAllocatorAdapterKind(other), @@ -153,7 +153,7 @@ class ScopedArenaAllocatorAdapter arena_stack_(allocator->arena_stack_) { } template - ScopedArenaAllocatorAdapter(const ScopedArenaAllocatorAdapter& other) // NOLINT, implicit + ScopedArenaAllocatorAdapter(const ScopedArenaAllocatorAdapter& other) : DebugStackReference(other), DebugStackIndirectTopRef(other), ArenaAllocatorAdapterKind(other), diff --git a/runtime/base/transform_array_ref.h b/runtime/base/transform_array_ref.h index b432f86d77..a4e0bc27ed 100644 --- a/runtime/base/transform_array_ref.h +++ b/runtime/base/transform_array_ref.h @@ -72,7 +72,7 @@ class TransformArrayRef { template ::value>::type> - TransformArrayRef(const TransformArrayRef& other) // NOLINT, implicit + TransformArrayRef(const TransformArrayRef& other) : TransformArrayRef(other.base(), other.GetFunction()) { } // Assignment operators. diff --git a/runtime/base/transform_array_ref_test.cc b/runtime/base/transform_array_ref_test.cc index 494dbb29aa..da0340d36b 100644 --- a/runtime/base/transform_array_ref_test.cc +++ b/runtime/base/transform_array_ref_test.cc @@ -38,7 +38,7 @@ ATTRIBUTE_UNUSED bool operator==(const ValueHolder& lhs, const ValueHolder& rhs) } // anonymous namespace TEST(TransformArrayRef, ConstRefAdd1) { - auto add1 = [](const ValueHolder& h) { return h.value + 1; }; // NOLINT [readability/braces] + auto add1 = [](const ValueHolder& h) { return h.value + 1; }; std::vector input({ 7, 6, 4, 0 }); std::vector output; @@ -79,7 +79,7 @@ TEST(TransformArrayRef, ConstRefAdd1) { } TEST(TransformArrayRef, NonConstRefSub1) { - auto sub1 = [](ValueHolder& h) { return h.value - 1; }; // NOLINT [readability/braces] + auto sub1 = [](ValueHolder& h) { return h.value - 1; }; std::vector input({ 4, 4, 5, 7, 10 }); std::vector output; diff --git a/runtime/base/transform_iterator.h b/runtime/base/transform_iterator.h index f1a8a52ceb..9c8f822b71 100644 --- a/runtime/base/transform_iterator.h +++ b/runtime/base/transform_iterator.h @@ -62,7 +62,7 @@ class TransformIterator { : data_(base, fn) { } template - TransformIterator(const TransformIterator& other) // NOLINT, implicit + TransformIterator(const TransformIterator& other) : data_(other.base(), other.GetFunction()) { } diff --git a/runtime/base/transform_iterator_test.cc b/runtime/base/transform_iterator_test.cc index a85dda8958..63b6e4f531 100644 --- a/runtime/base/transform_iterator_test.cc +++ b/runtime/base/transform_iterator_test.cc @@ -41,7 +41,7 @@ bool operator==(const ValueHolder& lhs, const ValueHolder& rhs) { } // anonymous namespace TEST(TransformIterator, VectorAdd1) { - auto add1 = [](const ValueHolder& h) { return h.value + 1; }; // NOLINT [readability/braces] + auto add1 = [](const ValueHolder& h) { return h.value + 1; }; std::vector input({ 1, 7, 3, 8 }); std::vector output; @@ -144,7 +144,7 @@ TEST(TransformIterator, VectorAdd1) { } TEST(TransformIterator, ListSub1) { - auto sub1 = [](const ValueHolder& h) { return h.value - 1; }; // NOLINT [readability/braces] + auto sub1 = [](const ValueHolder& h) { return h.value - 1; }; std::list input({ 2, 3, 5, 7, 11 }); std::vector output; @@ -208,7 +208,7 @@ TEST(TransformIterator, ListSub1) { } TEST(TransformIterator, ForwardListSub1) { - auto mul3 = [](const ValueHolder& h) { return h.value * 3; }; // NOLINT [readability/braces] + auto mul3 = [](const ValueHolder& h) { return h.value * 3; }; std::forward_list input({ 1, 1, 2, 3, 5, 8 }); std::vector output; @@ -246,7 +246,7 @@ TEST(TransformIterator, ForwardListSub1) { } TEST(TransformIterator, VectorConstReference) { - auto ref = [](const ValueHolder& h) -> const int& { return h.value; }; // NOLINT [readability/braces] + auto ref = [](const ValueHolder& h) -> const int& { return h.value; }; std::vector input({ 7, 3, 1, 2, 4, 8 }); std::vector output; @@ -339,7 +339,7 @@ TEST(TransformIterator, VectorConstReference) { } TEST(TransformIterator, VectorNonConstReference) { - auto ref = [](ValueHolder& h) -> int& { return h.value; }; // NOLINT [readability/braces] + auto ref = [](ValueHolder& h) -> int& { return h.value; }; std::vector input({ 7, 3, 1, 2, 4, 8 }); std::vector output; @@ -519,7 +519,7 @@ TEST(TransformIterator, VectorConstAndNonConstReference) { } TEST(TransformIterator, TransformRange) { - auto ref = [](ValueHolder& h) -> int& { return h.value; }; // NOLINT [readability/braces] + auto ref = [](ValueHolder& h) -> int& { return h.value; }; std::vector data({ 1, 0, 1, 3, 1, 0 }); for (int& v : MakeTransformRange(data, ref)) { diff --git a/runtime/base/variant_map.h b/runtime/base/variant_map.h index fe332d1e3b..c480b5162d 100644 --- a/runtime/base/variant_map.h +++ b/runtime/base/variant_map.h @@ -139,7 +139,7 @@ struct VariantMapKey : detail::VariantMapKeyRaw { // then that is used. Otherwise, the default value for the type TValue{} is returned. TValue CreateDefaultValue() const { if (default_value_ == nullptr) { - return TValue{}; // NOLINT [readability/braces] [4] + return TValue{}; } else { return TValue(*default_value_); } diff --git a/runtime/base/variant_map_test.cc b/runtime/base/variant_map_test.cc index 9dd29baf48..4677b6d3b3 100644 --- a/runtime/base/variant_map_test.cc +++ b/runtime/base/variant_map_test.cc @@ -107,8 +107,8 @@ TEST(VariantMaps, RuleOfFive) { fmFilled.Set(FruitMap::Orange, 555.0); EXPECT_EQ(size_t(2), fmFilled.Size()); - // Test copy constructor (NOLINT as a reference is suggested, instead) - FruitMap fmEmptyCopy(fmEmpty); // NOLINT + // Test copy constructor + FruitMap fmEmptyCopy(fmEmpty); EXPECT_EQ(size_t(0), fmEmptyCopy.Size()); // Test copy constructor diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 493468bb3d..ef1647caf3 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -78,11 +78,11 @@ static const uint8_t kBase64Map[256] = { 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, // NOLINT - 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, // NOLINT + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, // NOLINT - 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, // NOLINT + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index 90bc4b8f94..c963f6e111 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -487,52 +487,52 @@ TEST_F(DexFileTest, GetMethodSignature) { "(IDJLjava/lang/Object;)Ljava/lang/Float;", "java.lang.Float GetMethodSignature.m1(int, double, long, java.lang.Object)" }, - { // NOLINT [whitespace/braces] [4] + { "m2", "(ZSC)LGetMethodSignature;", "GetMethodSignature GetMethodSignature.m2(boolean, short, char)" }, - { // NOLINT [whitespace/braces] [4] + { "m3", "()V", "void GetMethodSignature.m3()" }, - { // NOLINT [whitespace/braces] [4] + { "m4", "(I)V", "void GetMethodSignature.m4(int)" }, - { // NOLINT [whitespace/braces] [4] + { "m5", "(II)V", "void GetMethodSignature.m5(int, int)" }, - { // NOLINT [whitespace/braces] [4] + { "m6", "(II[[I)V", "void GetMethodSignature.m6(int, int, int[][])" }, - { // NOLINT [whitespace/braces] [4] + { "m7", "(II[[ILjava/lang/Object;)V", "void GetMethodSignature.m7(int, int, int[][], java.lang.Object)" }, - { // NOLINT [whitespace/braces] [4] + { "m8", "(II[[ILjava/lang/Object;[[Ljava/lang/Object;)V", "void GetMethodSignature.m8(int, int, int[][], java.lang.Object, java.lang.Object[][])" }, - { // NOLINT [whitespace/braces] [4] + { "m9", "()I", "int GetMethodSignature.m9()" }, - { // NOLINT [whitespace/braces] [4] + { "mA", "()[[I", "int[][] GetMethodSignature.mA()" }, - { // NOLINT [whitespace/braces] [4] + { "mB", "()[[Ljava/lang/Object;", "java.lang.Object[][] GetMethodSignature.mB()" diff --git a/runtime/experimental_flags.h b/runtime/experimental_flags.h index b7f40372db..5f1443814a 100644 --- a/runtime/experimental_flags.h +++ b/runtime/experimental_flags.h @@ -30,10 +30,10 @@ struct ExperimentalFlags { }; constexpr ExperimentalFlags() : value_(0x0000) {} - constexpr ExperimentalFlags(decltype(kNone) t) // NOLINT, implicit + constexpr ExperimentalFlags(decltype(kNone) t) // NOLINT [runtime/explicit] : value_(static_cast(t)) {} - constexpr operator decltype(kNone)() const { // NOLINT, implicit + constexpr operator decltype(kNone)() const { return static_cast(value_); } diff --git a/runtime/indenter.h b/runtime/indenter.h index cc6d4c4e96..69b973252d 100644 --- a/runtime/indenter.h +++ b/runtime/indenter.h @@ -32,7 +32,7 @@ class Indenter : public std::streambuf { public: Indenter(std::streambuf* out, char text, size_t count) : indent_next_(true), out_sbuf_(out), - text_{text, text, text, text, text, text, text, text}, // NOLINT(whitespace/braces) + text_{text, text, text, text, text, text, text, text}, count_(count) {} private: diff --git a/runtime/mem_map.h b/runtime/mem_map.h index 36a24169d5..5603963eac 100644 --- a/runtime/mem_map.h +++ b/runtime/mem_map.h @@ -21,7 +21,7 @@ #include #include -#include // NOLINT [build/c++11] [5] +#include #include #include "android-base/thread_annotations.h" diff --git a/runtime/obj_ptr.h b/runtime/obj_ptr.h index 9a66983de7..70e767acf6 100644 --- a/runtime/obj_ptr.h +++ b/runtime/obj_ptr.h @@ -49,23 +49,22 @@ class ObjPtr { // Note: The following constructors allow implicit conversion. This simplifies code that uses // them, e.g., for parameter passing. However, in general, implicit-conversion constructors - // are discouraged and detected by cpplint and clang-tidy. So mark these constructors - // as NOLINT (without category, as the categories are different). + // are discouraged and detected by clang-tidy. - ALWAYS_INLINE ObjPtr(std::nullptr_t) // NOLINT + ALWAYS_INLINE ObjPtr(std::nullptr_t) REQUIRES_SHARED(Locks::mutator_lock_) : reference_(0u) {} template ::value>::type> - ALWAYS_INLINE ObjPtr(Type* ptr) // NOLINT + ALWAYS_INLINE ObjPtr(Type* ptr) REQUIRES_SHARED(Locks::mutator_lock_) : reference_(Encode(static_cast(ptr))) { } template ::value>::type> - ALWAYS_INLINE ObjPtr(const ObjPtr& other) // NOLINT + ALWAYS_INLINE ObjPtr(const ObjPtr& other) REQUIRES_SHARED(Locks::mutator_lock_) : reference_(kObjPtrPoisoningValidateOnCopy ? Encode(static_cast(other.Ptr())) diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index cc09a776b8..85af560ce3 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -548,7 +548,7 @@ bool ParsedOptions::DoParse(const RuntimeOptions& options, // If not low memory mode, semispace otherwise. gc::CollectorType background_collector_type_; - gc::CollectorType collector_type_ = (XGcOption{}).collector_type_; // NOLINT [whitespace/braces] [5] + gc::CollectorType collector_type_ = (XGcOption{}).collector_type_; bool low_memory_mode_ = args.Exists(M::LowMemoryMode); background_collector_type_ = args.GetOrDefault(M::BackgroundGc); diff --git a/runtime/prebuilt_tools_test.cc b/runtime/prebuilt_tools_test.cc index 6fa9b3424d..158d9d6e69 100644 --- a/runtime/prebuilt_tools_test.cc +++ b/runtime/prebuilt_tools_test.cc @@ -29,7 +29,7 @@ class PrebuiltToolsTest : public CommonRuntimeTest { }; static void CheckToolsExist(const std::string& tools_dir) { - const char* tools[] { "as", "objcopy", "objdump" }; // NOLINT + const char* tools[] = { "as", "objcopy", "objdump" }; for (const char* tool : tools) { struct stat exec_st; std::string exec_path = tools_dir + tool; @@ -50,7 +50,7 @@ TEST_F(PrebuiltToolsTest, CheckHostTools) { TEST_F(PrebuiltToolsTest, CheckTargetTools) { // Other prebuilts are missing from the build server's repo manifest. - InstructionSet isas[] = { InstructionSet::kThumb2 }; // NOLINT + InstructionSet isas[] = { InstructionSet::kThumb2 }; for (InstructionSet isa : isas) { std::string tools_dir = GetAndroidTargetToolsDir(isa); if (tools_dir.empty()) { diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc index 720a9d6219..1e7fc3ee92 100644 --- a/runtime/reference_table_test.cc +++ b/runtime/reference_table_test.cc @@ -16,7 +16,7 @@ #include "reference_table.h" -#include // NOLINT [build/c++11] [5] +#include #include "android-base/stringprintf.h" diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc index d283c79960..0b69851a55 100644 --- a/runtime/runtime_callbacks_test.cc +++ b/runtime/runtime_callbacks_test.cc @@ -22,7 +22,7 @@ #include #include -#include // NOLINT [build/c++11] [5] +#include #include #include "jni.h" diff --git a/runtime/runtime_options.cc b/runtime/runtime_options.cc index b072bb0c37..bce0d81cfb 100644 --- a/runtime/runtime_options.cc +++ b/runtime/runtime_options.cc @@ -30,7 +30,7 @@ namespace art { // Specify storage for the RuntimeOptions keys. -#define RUNTIME_OPTIONS_KEY(Type, Name, ...) const RuntimeArgumentMap::Key RuntimeArgumentMap::Name {__VA_ARGS__}; // NOLINT [readability/braces] [4] +#define RUNTIME_OPTIONS_KEY(Type, Name, ...) const RuntimeArgumentMap::Key RuntimeArgumentMap::Name {__VA_ARGS__}; #include "runtime_options.def" } // namespace art diff --git a/runtime/subtype_check.h b/runtime/subtype_check.h index 24f07632ce..1556abe67f 100644 --- a/runtime/subtype_check.h +++ b/runtime/subtype_check.h @@ -274,7 +274,7 @@ struct SubtypeCheck { // more complicated (e.g. ObjPtr or Depth call) will fail dchecks. // OK. zero-initializing subtype_check_info_ puts us into the kUninitialized state. - SubtypeCheckBits scb_uninitialized = SubtypeCheckBits{}; // NOLINT + SubtypeCheckBits scb_uninitialized = SubtypeCheckBits{}; WriteSubtypeCheckBits(klass, scb_uninitialized); // Do not use "SubtypeCheckInfo" API here since that requires Depth() diff --git a/runtime/subtype_check_info.h b/runtime/subtype_check_info.h index d10d4728ad..cd579c3a5c 100644 --- a/runtime/subtype_check_info.h +++ b/runtime/subtype_check_info.h @@ -188,7 +188,7 @@ struct SubtypeCheckInfo { // Returns a new root SubtypeCheckInfo with a blank PathToRoot. // Post-condition: The return valued has an Assigned state. static SubtypeCheckInfo CreateRoot() { - SubtypeCheckInfo io{}; // NOLINT + SubtypeCheckInfo io{}; io.depth_ = 0u; io.SetNext(io.GetNext() + 1u); @@ -220,7 +220,7 @@ struct SubtypeCheckInfo { child.MaybeInitNext(); // Always clear the inherited Parent's next Value on the child. - OverwriteNextValueFromParent(/*inout*/&child, BitStringChar{}); // NOLINT + OverwriteNextValueFromParent(/*inout*/&child, BitStringChar{}); // The state is now Initialized | Overflowed. DCHECK_NE(kAssigned, child.GetState()) << child.GetBitString(); @@ -392,7 +392,7 @@ struct SubtypeCheckInfo { // Clearing out the "Next" value like this // is often an intermediate operation which temporarily // violates the invariants. Do not do the extra dchecks. - SetNextUnchecked(BitStringChar{}); // NOLINT + SetNextUnchecked(BitStringChar{}); SetNextUnchecked(GetNext()+1u); } } diff --git a/runtime/subtype_check_info_test.cc b/runtime/subtype_check_info_test.cc index bc2e84e37d..338d75a285 100644 --- a/runtime/subtype_check_info_test.cc +++ b/runtime/subtype_check_info_test.cc @@ -47,7 +47,7 @@ BitStringChar MakeBitStringChar(size_t val) { BitString MakeBitString(std::initializer_list values = {}) { CHECK_GE(BitString::kCapacity, values.size()); - BitString bs{}; // NOLINT + BitString bs{}; size_t i = 0; for (size_t val : values) { @@ -68,7 +68,7 @@ size_t AsUint(const T& value) { // Make max bistring, e.g. BitString[4095,7,255] for {12,3,8} template BitString MakeBitStringMax() { - BitString bs{}; // NOLINT + BitString bs{}; for (size_t i = 0; i < kCount; ++i) { bs.SetAt(i, @@ -132,7 +132,7 @@ struct SubtypeCheckInfoTest : public ::testing::Test { // Create an SubtypeCheckInfo with the same depth, but with everything else reset. // Returns: SubtypeCheckInfo in the Uninitialized state. static SubtypeCheckInfo CopyCleared(SubtypeCheckInfo sc) { - SubtypeCheckInfo cleared_copy{}; // NOLINT + SubtypeCheckInfo cleared_copy{}; cleared_copy.depth_ = sc.depth_; DCHECK_EQ(SubtypeCheckInfo::kUninitialized, cleared_copy.GetState()); return cleared_copy; @@ -260,7 +260,7 @@ TEST_F(SubtypeCheckInfoTest, EncodedPathToRoot) { SubtypeCheckInfo io = MakeSubtypeCheckInfo(/*path_to_root*/MakeBitStringMax(), - /*next*/BitStringChar{}, // NOLINT + /*next*/BitStringChar{}, /*overflow*/false, /*depth*/BitString::kCapacity); // 0b11111...000 where MSB == 1, and leading 1s = the maximum bitstring representation. @@ -329,7 +329,7 @@ TEST_F(SubtypeCheckInfoTest, CopyCleared) { // CopyCleared is just a thin wrapper around value-init and providing the depth. SubtypeCheckInfo cleared_copy_value = - SubtypeCheckInfo::Create(SubtypeCheckBits{}, /*depth*/1u); // NOLINT + SubtypeCheckInfo::Create(SubtypeCheckBits{}, /*depth*/1u); EXPECT_EQ(SubtypeCheckInfo::kUninitialized, cleared_copy_value.GetState()); EXPECT_EQ(MakeBitString({}), GetPathToRoot(cleared_copy_value)); } diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc index 6800d027f8..b8ab51b629 100644 --- a/sigchainlib/sigchain.cc +++ b/sigchainlib/sigchain.cc @@ -30,7 +30,7 @@ #include #include -#include // NOLINT [build/c++11] [5] +#include #include #include "sigchain.h" diff --git a/test/1919-vminit-thread-start-timing/vminit.cc b/test/1919-vminit-thread-start-timing/vminit.cc index c492e8bb62..109c61f05c 100644 --- a/test/1919-vminit-thread-start-timing/vminit.cc +++ b/test/1919-vminit-thread-start-timing/vminit.cc @@ -16,8 +16,8 @@ #include "1919-vminit-thread-start-timing/vminit.h" -#include // NOLINT [build/c++11] [5] -#include // NOLINT [build/c++11] [5] +#include +#include #include #include diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc index 11b32e46c3..472f2b768e 100644 --- a/test/901-hello-ti-agent/basics.cc +++ b/test/901-hello-ti-agent/basics.cc @@ -16,7 +16,7 @@ #include "901-hello-ti-agent/basics.h" -#include // NOLINT [build/c++11] [5] +#include #include #include diff --git a/test/904-object-allocation/tracking.cc b/test/904-object-allocation/tracking.cc index 81d1b2cda9..9d2592a675 100644 --- a/test/904-object-allocation/tracking.cc +++ b/test/904-object-allocation/tracking.cc @@ -18,7 +18,7 @@ #include #include -#include // NOLINT [build/c++11] [5] +#include #include #include "android-base/logging.h" diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc index a84747289f..1f6954ef99 100644 --- a/test/912-classes/classes.cc +++ b/test/912-classes/classes.cc @@ -16,8 +16,8 @@ #include -#include // NOLINT [build/c++11] [5] -#include // NOLINT [build/c++11] [5] +#include +#include #include #include "android-base/macros.h" diff --git a/test/912-classes/classes_art.cc b/test/912-classes/classes_art.cc index 92a3ba0c97..de2e456a53 100644 --- a/test/912-classes/classes_art.cc +++ b/test/912-classes/classes_art.cc @@ -16,7 +16,7 @@ #include -#include // NOLINT [build/c++11] [5] +#include #include #include "android-base/macros.h" diff --git a/test/924-threads/threads.cc b/test/924-threads/threads.cc index 8330179fbf..8caff768c1 100644 --- a/test/924-threads/threads.cc +++ b/test/924-threads/threads.cc @@ -16,7 +16,7 @@ #include -#include // NOLINT [build/c++11] [5] +#include #include #include diff --git a/tools/breakpoint-logger/breakpoint_logger.cc b/tools/breakpoint-logger/breakpoint_logger.cc index b48a1788e3..2f8b68239b 100644 --- a/tools/breakpoint-logger/breakpoint_logger.cc +++ b/tools/breakpoint-logger/breakpoint_logger.cc @@ -383,7 +383,7 @@ static jint AgentStart(StartType start, return JNI_ERR; } - jvmtiCapabilities caps {}; // NOLINT [readability/braces] + jvmtiCapabilities caps{}; caps.can_generate_breakpoint_events = JNI_TRUE; caps.can_get_line_numbers = JNI_TRUE; caps.can_get_source_file_name = JNI_TRUE; @@ -394,7 +394,7 @@ static jint AgentStart(StartType start, return JNI_ERR; } - jvmtiEventCallbacks callbacks {}; // NOLINT [readability/braces] + jvmtiEventCallbacks callbacks{}; callbacks.Breakpoint = &BreakpointCB; callbacks.VMInit = &VMInitCB; diff --git a/tools/titrace/titrace.cc b/tools/titrace/titrace.cc index e8122807c1..981ad56e86 100644 --- a/tools/titrace/titrace.cc +++ b/tools/titrace/titrace.cc @@ -21,7 +21,7 @@ #include #include #include -#include // NOLINT [build/c++11] [5] +#include // We could probably return a JNI_ERR here but lets crash instead if something fails. #define CHECK_JVMTI_ERROR(jvmti, errnum) \ @@ -195,8 +195,8 @@ struct TraceStatistics { std::unique_ptr instruction_decoder_; - std::atomic single_step_counter_{0u}; // NOLINT [readability/braces] [4] [whitespace/braces] [5] - std::atomic instruction_counter_[256]{}; // NOLINT [whitespace/braces] [5] + std::atomic single_step_counter_{0u}; + std::atomic instruction_counter_[256]{}; // Cache the bytecode to avoid calling into JVMTI repeatedly. // TODO: invalidate if the bytecode was updated? @@ -256,7 +256,7 @@ JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, TraceStatistics::Initialize(jvmti); } - jvmtiError error{}; // NOLINT [readability/braces] [4] [whitespace/braces] [5] + jvmtiError error{}; // Set capabilities. { diff --git a/tools/wrapagentproperties/wrapagentproperties.cc b/tools/wrapagentproperties/wrapagentproperties.cc index 9eaffbb240..8b4b062cf5 100644 --- a/tools/wrapagentproperties/wrapagentproperties.cc +++ b/tools/wrapagentproperties/wrapagentproperties.cc @@ -24,7 +24,7 @@ #include #include #include -#include // NOLINT [build/c++11] [5] +#include #include #include #include -- GitLab From 5686c4fbe268ca5ae5229c6195b56162ffffcd6a Mon Sep 17 00:00:00 2001 From: Alan Leung Date: Thu, 16 Nov 2017 00:19:18 -0800 Subject: [PATCH 055/226] Rename d8 to d8-compat-dx Bug: 69368371 Test: ./art/test/testrunner/run_build_test_target.py -j110 art-test Change-Id: If06113796469393682bc5cd376fac917850bf384 (cherry picked from commit a4ba9b53d8c102369a316c93ff14eaa488493435) --- build/Android.gtest.mk | 2 +- test/Android.run-test.mk | 2 +- test/etc/default-build | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 42d0ba57cf..3c8eade773 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -608,7 +608,7 @@ define define-test-art-gtest-combination endif .PHONY: $$(rule_name) -$$(rule_name): $$(dependencies) dx d8 +$$(rule_name): $$(dependencies) dx d8-compat-dx $(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) # Clear locally defined variables. diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 8755f04320..fe4a327a1f 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -27,7 +27,7 @@ TEST_ART_RUN_TEST_DEPENDENCIES := \ # Add d8 dependency, if enabled. ifeq ($(USE_D8),true) TEST_ART_RUN_TEST_DEPENDENCIES += \ - $(HOST_OUT_EXECUTABLES)/d8 + $(HOST_OUT_EXECUTABLES)/d8-compat-dx endif # Convert's a rule name to the form used in variables, e.g. no-relocate to NO_RELOCATE diff --git a/test/etc/default-build b/test/etc/default-build index f14424ecf9..5c8257f210 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -294,7 +294,7 @@ function make_dex() { local dexer="${DX}" if [ ${USE_D8} = "true" ]; then - dexer="${ANDROID_HOST_OUT}/bin/d8" + dexer="${ANDROID_HOST_OUT}/bin/d8-compat-dx" fi # Make dex file from desugared JAR. -- GitLab From 1ea6d15d47f5b4e0859659cd43df0ee10e6c79a7 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Wed, 15 Nov 2017 20:50:38 -0800 Subject: [PATCH 056/226] Remove unneeded endian.h include This was left in accidentally and is breaking the mac build. Test: ./test/run-test --host 1940 Change-Id: I309dae788ad7d9032671452011b7d9c052e7e055 (cherry picked from commit 237c113e573b624baf31c43d230e9f9c04fa9de4) --- openjdkjvmti/ti_ddms.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/openjdkjvmti/ti_ddms.cc b/openjdkjvmti/ti_ddms.cc index be7e65d638..500a453f78 100644 --- a/openjdkjvmti/ti_ddms.cc +++ b/openjdkjvmti/ti_ddms.cc @@ -34,8 +34,6 @@ #include "ti_ddms.h" -#include - #include "art_jvmti.h" #include "base/array_ref.h" #include "debugger.h" -- GitLab From 97a042ec6e57025bbc47b480a7753fbf2307b5b8 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 16 Nov 2017 10:49:59 +0000 Subject: [PATCH 057/226] Try to be consistent when setting fields of OatWriter::OatDexFile. Also add a few comments. Test: test.py Change-Id: I9f808e4c62f7cc3585a84cc0df5eecb5144f2d0e --- dex2oat/linker/oat_writer.cc | 80 ++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index d3e920f9a0..909ba22f21 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -295,29 +295,46 @@ class OatWriter::OatDexFile { // Whether to create the type lookup table. CreateTypeLookupTable create_type_lookup_table_; - // Dex file size. Initialized when writing the dex file. + // Dex file size. Initialized when copying the dex file in the + // WriteDexFile methods. size_t dex_file_size_; // Offset of start of OatDexFile from beginning of OatHeader. It is // used to validate file position when writing. size_t offset_; - // Data to write. - uint32_t dex_file_location_size_; - const char* dex_file_location_data_; + ///// Start of data to write to vdex/oat file. + + const uint32_t dex_file_location_size_; + const char* const dex_file_location_data_; + + // The checksum of the dex file. Initialized when adding a DexFile + // source. uint32_t dex_file_location_checksum_; + + // Offset of the dex file in the vdex file. Set when writing dex files in + // SeekToDexFile. uint32_t dex_file_offset_; - uint32_t class_offsets_offset_; + + // The lookup table offset in the oat file. Set in WriteTypeLookupTables. uint32_t lookup_table_offset_; - uint32_t method_bss_mapping_offset_; + + // Offset of dex sections that will have different runtime madvise states. + // Set in WriteDexLayoutSections. uint32_t dex_sections_layout_offset_; - // Data to write to a separate section. + // Class and BSS offsets set in PrepareLayout. + uint32_t class_offsets_offset_; + uint32_t method_bss_mapping_offset_; + + // Data to write to a separate section. We set the length + // of the vector in OpenDexFiles. dchecked_vector class_offsets_; // Dex section layout info to serialize. DexLayoutSections dex_sections_layout_; + ///// End of data to write to vdex/oat file. private: DISALLOW_COPY_AND_ASSIGN(OatDexFile); }; @@ -464,6 +481,8 @@ bool OatWriter::AddZippedDexFilesSource(File&& zip_fd, oat_dex_files_.emplace_back(full_location, DexFileSource(zipped_dex_files_.back().get()), create_type_lookup_table); + // Override the checksum from header with the CRC from ZIP entry. + oat_dex_files_.back().dex_file_location_checksum_ = zipped_dex_files_.back()->GetCrc32(); } if (zipped_dex_file_locations_.empty()) { LOG(ERROR) << "No dex files in zip file '" << location << "': " << error_msg; @@ -3058,8 +3077,6 @@ bool OatWriter::ReadDexFileHeader(File* file, OatDexFile* oat_dex_file) { const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_header); oat_dex_file->dex_file_size_ = header->file_size_; - oat_dex_file->dex_file_location_checksum_ = header->checksum_; - oat_dex_file->class_offsets_.resize(header->class_defs_size_); return true; } @@ -3240,8 +3257,6 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil return false; } oat_dex_file->dex_sections_layout_ = dex_layout.GetSections(); - // Set the checksum of the new oat dex file to be the original file's checksum. - oat_dex_file->dex_file_location_checksum_ = dex_file->GetLocationChecksum(); return true; } @@ -3291,6 +3306,7 @@ bool OatWriter::WriteDexFile(OutputStream* out, << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); return false; } + // Read the dex file header to get the dex file size. if (!ReadDexFileHeader(file, oat_dex_file)) { return false; } @@ -3301,9 +3317,6 @@ bool OatWriter::WriteDexFile(OutputStream* out, return false; } - // Override the checksum from header with the CRC from ZIP entry. - oat_dex_file->dex_file_location_checksum_ = dex_file->GetCrc32(); - // Seek both file and stream to the end offset. size_t end_offset = start_offset + oat_dex_file->dex_file_size_; actual_offset = lseek(file->Fd(), end_offset, SEEK_SET); @@ -3352,6 +3365,7 @@ bool OatWriter::WriteDexFile(OutputStream* out, << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); return false; } + // Read the dex file header to get the dex file size. if (!ReadDexFileHeader(dex_file, oat_dex_file)) { return false; } @@ -3418,10 +3432,7 @@ bool OatWriter::WriteDexFile(OutputStream* out, } // Update dex file size and resize class offsets in the OatDexFile. - // Note: For raw data, the checksum is passed directly to AddRawDexFileSource(). - // Note: For vdex, the checksum is copied from the existing vdex file. oat_dex_file->dex_file_size_ = header->file_size_; - oat_dex_file->class_offsets_.resize(header->class_defs_size_); return true; } @@ -3457,29 +3468,22 @@ bool OatWriter::OpenDexFiles( } std::vector> dex_files; for (OatDexFile& oat_dex_file : oat_dex_files_) { - // Make sure no one messed with input files while we were copying data. - // At the very least we need consistent file size and number of class definitions. const uint8_t* raw_dex_file = dex_files_map->Begin() + oat_dex_file.dex_file_offset_ - map_offset; - if (!ValidateDexFileHeader(raw_dex_file, oat_dex_file.GetLocation())) { - // Note: ValidateDexFileHeader() already logged an error message. - LOG(ERROR) << "Failed to verify written dex file header!" + + if (kIsDebugBuild) { + // Sanity check our input files. + // Note that ValidateDexFileHeader() logs error messages. + CHECK(ValidateDexFileHeader(raw_dex_file, oat_dex_file.GetLocation())) + << "Failed to verify written dex file header!" << " Output: " << file->GetPath() << " ~ " << std::hex << map_offset << " ~ " << static_cast(raw_dex_file); - return false; - } - const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file); - if (header->file_size_ != oat_dex_file.dex_file_size_) { - LOG(ERROR) << "File size mismatch in written dex file header! Expected: " + + const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file); + CHECK_EQ(header->file_size_, oat_dex_file.dex_file_size_) + << "File size mismatch in written dex file header! Expected: " << oat_dex_file.dex_file_size_ << " Actual: " << header->file_size_ << " Output: " << file->GetPath(); - return false; - } - if (header->class_defs_size_ != oat_dex_file.class_offsets_.size()) { - LOG(ERROR) << "Class defs size mismatch in written dex file header! Expected: " - << oat_dex_file.class_offsets_.size() << " Actual: " << header->class_defs_size_ - << " Output: " << file->GetPath(); - return false; } // Now, open the dex file. @@ -3496,6 +3500,10 @@ bool OatWriter::OpenDexFiles( << " Error: " << error_msg; return false; } + + // Set the class_offsets size now that we have easy access to the DexFile and + // it has been verified in DexFileLoader::Open. + oat_dex_file.class_offsets_.resize(dex_files.back()->GetHeader().class_defs_size_); } *opened_dex_files_map = std::move(dex_files_map); @@ -3742,10 +3750,10 @@ OatWriter::OatDexFile::OatDexFile(const char* dex_file_location, dex_file_location_data_(dex_file_location), dex_file_location_checksum_(0u), dex_file_offset_(0u), - class_offsets_offset_(0u), lookup_table_offset_(0u), - method_bss_mapping_offset_(0u), dex_sections_layout_offset_(0u), + class_offsets_offset_(0u), + method_bss_mapping_offset_(0u), class_offsets_() { } -- GitLab From ec4a10c5191b2db5a23ea14f0c5278afc77d9c0a Mon Sep 17 00:00:00 2001 From: Alex Light Date: Fri, 17 Nov 2017 09:06:26 -0800 Subject: [PATCH 058/226] Increase timeout for libjdwp tests Since libjdwp has more threads running concurrently then the old internal jdwp implementation does it can sometimes take slightly longer to finish certain test tasks (most notably shutdown). This was causing the tests to timeout occasionally on the chrome-buildbots. Bug: 68839405 Test: ./art/tools/run-libjdwp-tests.sh --mode=host Change-Id: I8d443662c40de27d490248e8df5d77b43f78267c --- tools/run-jdwp-tests.sh | 8 ++++++++ tools/run-libjdwp-tests.sh | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index f5fbcd8def..6a846aee17 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -96,6 +96,14 @@ while true; do # We don't care about jit with the RI use_jit=false shift + elif [[ $1 == --test-timeout-ms ]]; then + # Remove the --test-timeout-ms from the arguments. + args=${args/$1} + shift + jdwp_test_timeout=$1 + # Remove the argument + args=${args/$1} + shift elif [[ $1 == --agent-wrapper ]]; then # Remove the --agent-wrapper from the arguments. args=${args/$1} diff --git a/tools/run-libjdwp-tests.sh b/tools/run-libjdwp-tests.sh index 964bb386ef..47e7c4595d 100755 --- a/tools/run-libjdwp-tests.sh +++ b/tools/run-libjdwp-tests.sh @@ -29,11 +29,16 @@ debug="no" has_variant="no" has_mode="no" mode="target" +has_timeout="no" while true; do if [[ $1 == "--debug" ]]; then debug="yes" shift + elif [[ $1 == --test-timeout-ms ]]; then + has_timeout="yes" + shift + shift elif [[ "$1" == "--mode=jvm" ]]; then has_mode="yes" mode="ri" @@ -60,6 +65,12 @@ if [[ "$has_variant" = "no" ]]; then args+=(--variant=X32) fi +if [[ "$has_timeout" = "no" ]]; then + # Double the timeout to 20 seconds + args+=(--test-timeout-ms) + args+=(20000) +fi + # We don't use full paths since it is difficult to determine them for device # tests and not needed due to resolution rules of dlopen. if [[ "$debug" = "yes" ]]; then -- GitLab From 2ca10eb3f47ef3c2535c137853f7a63d10bb908b Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Wed, 15 Nov 2017 15:17:53 -0800 Subject: [PATCH 059/226] Refactored optimization passes setup. Rationale: Refactors the way we set up optimization passes in the compiler into a more centralized approach. The refactoring also found some "holes" in the existing mechanism (missing string lookup in the debugging mechanism, or inablity to set alternative name for optimizations that may repeat). Bug: 64538565 Test: test-art-host test-art-target Change-Id: Ie5e0b70f67ac5acc706db91f64612dff0e561f83 --- .../optimizing/bounds_check_elimination.h | 5 +- compiler/optimizing/cha_guard_optimization.h | 5 +- compiler/optimizing/code_sinking.h | 6 +- ...constructor_fence_redundancy_elimination.h | 7 +- compiler/optimizing/induction_var_analysis.cc | 4 +- compiler/optimizing/induction_var_analysis.h | 2 +- compiler/optimizing/inliner.h | 5 +- compiler/optimizing/intrinsics.h | 6 +- compiler/optimizing/licm.h | 7 +- compiler/optimizing/load_store_analysis.h | 4 +- compiler/optimizing/load_store_elimination.h | 5 +- compiler/optimizing/loop_optimization.cc | 5 +- compiler/optimizing/loop_optimization.h | 3 +- compiler/optimizing/optimization.cc | 312 +++++++++ compiler/optimizing/optimization.h | 79 +++ compiler/optimizing/optimizing_compiler.cc | 596 ++++++------------ compiler/optimizing/pc_relative_fixups_mips.h | 2 +- compiler/optimizing/scheduler.h | 9 +- compiler/optimizing/select_generator.cc | 5 +- compiler/optimizing/select_generator.h | 3 +- compiler/optimizing/sharpening.h | 5 +- 21 files changed, 646 insertions(+), 429 deletions(-) diff --git a/compiler/optimizing/bounds_check_elimination.h b/compiler/optimizing/bounds_check_elimination.h index 6dc53207ea..79c67a8c7a 100644 --- a/compiler/optimizing/bounds_check_elimination.h +++ b/compiler/optimizing/bounds_check_elimination.h @@ -28,8 +28,9 @@ class BoundsCheckElimination : public HOptimization { public: BoundsCheckElimination(HGraph* graph, const SideEffectsAnalysis& side_effects, - HInductionVarAnalysis* induction_analysis) - : HOptimization(graph, kBoundsCheckEliminationPassName), + HInductionVarAnalysis* induction_analysis, + const char* name = kBoundsCheckEliminationPassName) + : HOptimization(graph, name), side_effects_(side_effects), induction_analysis_(induction_analysis) {} diff --git a/compiler/optimizing/cha_guard_optimization.h b/compiler/optimizing/cha_guard_optimization.h index ba0cdb81fd..f14e07bd6c 100644 --- a/compiler/optimizing/cha_guard_optimization.h +++ b/compiler/optimizing/cha_guard_optimization.h @@ -26,8 +26,9 @@ namespace art { */ class CHAGuardOptimization : public HOptimization { public: - explicit CHAGuardOptimization(HGraph* graph) - : HOptimization(graph, kCHAGuardOptimizationPassName) {} + explicit CHAGuardOptimization(HGraph* graph, + const char* name = kCHAGuardOptimizationPassName) + : HOptimization(graph, name) {} void Run() OVERRIDE; diff --git a/compiler/optimizing/code_sinking.h b/compiler/optimizing/code_sinking.h index 59cda52a8c..836d9d4f67 100644 --- a/compiler/optimizing/code_sinking.h +++ b/compiler/optimizing/code_sinking.h @@ -28,8 +28,10 @@ namespace art { */ class CodeSinking : public HOptimization { public: - CodeSinking(HGraph* graph, OptimizingCompilerStats* stats) - : HOptimization(graph, kCodeSinkingPassName, stats) {} + CodeSinking(HGraph* graph, + OptimizingCompilerStats* stats, + const char* name = kCodeSinkingPassName) + : HOptimization(graph, name, stats) {} void Run() OVERRIDE; diff --git a/compiler/optimizing/constructor_fence_redundancy_elimination.h b/compiler/optimizing/constructor_fence_redundancy_elimination.h index d89210cd1c..f4b06d5544 100644 --- a/compiler/optimizing/constructor_fence_redundancy_elimination.h +++ b/compiler/optimizing/constructor_fence_redundancy_elimination.h @@ -48,12 +48,13 @@ namespace art { class ConstructorFenceRedundancyElimination : public HOptimization { public: ConstructorFenceRedundancyElimination(HGraph* graph, - OptimizingCompilerStats* stats) - : HOptimization(graph, kPassName, stats) {} + OptimizingCompilerStats* stats, + const char* name = kCFREPassName) + : HOptimization(graph, name, stats) {} void Run() OVERRIDE; - static constexpr const char* kPassName = "constructor_fence_redundancy_elimination"; + static constexpr const char* kCFREPassName = "constructor_fence_redundancy_elimination"; private: DISALLOW_COPY_AND_ASSIGN(ConstructorFenceRedundancyElimination); diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index e2747afd85..ad29ba56ab 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -97,8 +97,8 @@ static DataType::Type ImplicitConversion(DataType::Type type) { // Class methods. // -HInductionVarAnalysis::HInductionVarAnalysis(HGraph* graph) - : HOptimization(graph, kInductionPassName), +HInductionVarAnalysis::HInductionVarAnalysis(HGraph* graph, const char* name) + : HOptimization(graph, name), global_depth_(0), stack_(graph->GetAllocator()->Adapter(kArenaAllocInductionVarAnalysis)), map_(std::less(), diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h index a2d302ae81..8737b890d9 100644 --- a/compiler/optimizing/induction_var_analysis.h +++ b/compiler/optimizing/induction_var_analysis.h @@ -35,7 +35,7 @@ namespace art { */ class HInductionVarAnalysis : public HOptimization { public: - explicit HInductionVarAnalysis(HGraph* graph); + explicit HInductionVarAnalysis(HGraph* graph, const char* name = kInductionPassName); void Run() OVERRIDE; diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index c4b3a32d91..042eee3204 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -44,8 +44,9 @@ class HInliner : public HOptimization { size_t total_number_of_dex_registers, size_t total_number_of_instructions, HInliner* parent, - size_t depth = 0) - : HOptimization(outer_graph, kInlinerPassName, stats), + size_t depth = 0, + const char* name = kInlinerPassName) + : HOptimization(outer_graph, name, stats), outermost_graph_(outermost_graph), outer_compilation_unit_(outer_compilation_unit), caller_compilation_unit_(caller_compilation_unit), diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index 707ff3408e..818d7f63a3 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -37,8 +37,10 @@ static constexpr uint64_t kNanDouble = 0x7ff8000000000000; // Recognize intrinsics from HInvoke nodes. class IntrinsicsRecognizer : public HOptimization { public: - IntrinsicsRecognizer(HGraph* graph, OptimizingCompilerStats* stats) - : HOptimization(graph, kIntrinsicsRecognizerPassName, stats) {} + IntrinsicsRecognizer(HGraph* graph, + OptimizingCompilerStats* stats, + const char* name = kIntrinsicsRecognizerPassName) + : HOptimization(graph, name, stats) {} void Run() OVERRIDE; diff --git a/compiler/optimizing/licm.h b/compiler/optimizing/licm.h index bf56f53d46..ee567aeb20 100644 --- a/compiler/optimizing/licm.h +++ b/compiler/optimizing/licm.h @@ -26,8 +26,11 @@ class SideEffectsAnalysis; class LICM : public HOptimization { public: - LICM(HGraph* graph, const SideEffectsAnalysis& side_effects, OptimizingCompilerStats* stats) - : HOptimization(graph, kLoopInvariantCodeMotionPassName, stats), + LICM(HGraph* graph, + const SideEffectsAnalysis& side_effects, + OptimizingCompilerStats* stats, + const char* name = kLoopInvariantCodeMotionPassName) + : HOptimization(graph, name, stats), side_effects_(side_effects) {} void Run() OVERRIDE; diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h index aa8b5bbdc9..437e6be418 100644 --- a/compiler/optimizing/load_store_analysis.h +++ b/compiler/optimizing/load_store_analysis.h @@ -564,8 +564,8 @@ class HeapLocationCollector : public HGraphVisitor { class LoadStoreAnalysis : public HOptimization { public: - explicit LoadStoreAnalysis(HGraph* graph) - : HOptimization(graph, kLoadStoreAnalysisPassName), + explicit LoadStoreAnalysis(HGraph* graph, const char* name = kLoadStoreAnalysisPassName) + : HOptimization(graph, name), heap_location_collector_(graph) {} const HeapLocationCollector& GetHeapLocationCollector() const { diff --git a/compiler/optimizing/load_store_elimination.h b/compiler/optimizing/load_store_elimination.h index 20a8a769c0..7153541baf 100644 --- a/compiler/optimizing/load_store_elimination.h +++ b/compiler/optimizing/load_store_elimination.h @@ -29,8 +29,9 @@ class LoadStoreElimination : public HOptimization { LoadStoreElimination(HGraph* graph, const SideEffectsAnalysis& side_effects, const LoadStoreAnalysis& lsa, - OptimizingCompilerStats* stats) - : HOptimization(graph, kLoadStoreEliminationPassName, stats), + OptimizingCompilerStats* stats, + const char* name = kLoadStoreEliminationPassName) + : HOptimization(graph, name, stats), side_effects_(side_effects), lsa_(lsa) {} diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index fcc59ea3f9..1ca096035e 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -448,8 +448,9 @@ static bool CheckInductionSetFullyRemoved(ScopedArenaSet* iset) { HLoopOptimization::HLoopOptimization(HGraph* graph, CompilerDriver* compiler_driver, HInductionVarAnalysis* induction_analysis, - OptimizingCompilerStats* stats) - : HOptimization(graph, kLoopOptimizationPassName, stats), + OptimizingCompilerStats* stats, + const char* name) + : HOptimization(graph, name, stats), compiler_driver_(compiler_driver), induction_range_(induction_analysis), loop_allocator_(nullptr), diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h index 51e0a986b8..a707ad1358 100644 --- a/compiler/optimizing/loop_optimization.h +++ b/compiler/optimizing/loop_optimization.h @@ -37,7 +37,8 @@ class HLoopOptimization : public HOptimization { HLoopOptimization(HGraph* graph, CompilerDriver* compiler_driver, HInductionVarAnalysis* induction_analysis, - OptimizingCompilerStats* stats); + OptimizingCompilerStats* stats, + const char* name = kLoopOptimizationPassName); void Run() OVERRIDE; diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc index 1e68ca2802..7edb642c5b 100644 --- a/compiler/optimizing/optimization.cc +++ b/compiler/optimizing/optimization.cc @@ -16,5 +16,317 @@ #include "optimization.h" +#ifdef ART_ENABLE_CODEGEN_arm +#include "instruction_simplifier_arm.h" +#endif +#ifdef ART_ENABLE_CODEGEN_arm64 +#include "instruction_simplifier_arm64.h" +#endif +#ifdef ART_ENABLE_CODEGEN_mips +#include "instruction_simplifier_mips.h" +#include "pc_relative_fixups_mips.h" +#endif +#ifdef ART_ENABLE_CODEGEN_x86 +#include "pc_relative_fixups_x86.h" +#endif +#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64) +#include "x86_memory_gen.h" +#endif + +#include "bounds_check_elimination.h" +#include "cha_guard_optimization.h" +#include "code_sinking.h" +#include "constant_folding.h" +#include "constructor_fence_redundancy_elimination.h" +#include "dead_code_elimination.h" +#include "driver/dex_compilation_unit.h" +#include "gvn.h" +#include "induction_var_analysis.h" +#include "inliner.h" +#include "instruction_simplifier.h" +#include "intrinsics.h" +#include "licm.h" +#include "load_store_analysis.h" +#include "load_store_elimination.h" +#include "loop_optimization.h" +#include "scheduler.h" +#include "select_generator.h" +#include "sharpening.h" +#include "side_effects_analysis.h" + +// Decide between default or alternative pass name. + namespace art { + +const char* OptimizationPassName(OptimizationPass pass) { + switch (pass) { + case OptimizationPass::kSideEffectsAnalysis: + return SideEffectsAnalysis::kSideEffectsAnalysisPassName; + case OptimizationPass::kInductionVarAnalysis: + return HInductionVarAnalysis::kInductionPassName; + case OptimizationPass::kLoadStoreAnalysis: + return LoadStoreAnalysis::kLoadStoreAnalysisPassName; + case OptimizationPass::kGlobalValueNumbering: + return GVNOptimization::kGlobalValueNumberingPassName; + case OptimizationPass::kInvariantCodeMotion: + return LICM::kLoopInvariantCodeMotionPassName; + case OptimizationPass::kLoopOptimization: + return HLoopOptimization::kLoopOptimizationPassName; + case OptimizationPass::kBoundsCheckElimination: + return BoundsCheckElimination::kBoundsCheckEliminationPassName; + case OptimizationPass::kLoadStoreElimination: + return LoadStoreElimination::kLoadStoreEliminationPassName; + case OptimizationPass::kConstantFolding: + return HConstantFolding::kConstantFoldingPassName; + case OptimizationPass::kDeadCodeElimination: + return HDeadCodeElimination::kDeadCodeEliminationPassName; + case OptimizationPass::kInliner: + return HInliner::kInlinerPassName; + case OptimizationPass::kSharpening: + return HSharpening::kSharpeningPassName; + case OptimizationPass::kSelectGenerator: + return HSelectGenerator::kSelectGeneratorPassName; + case OptimizationPass::kInstructionSimplifier: + return InstructionSimplifier::kInstructionSimplifierPassName; + case OptimizationPass::kIntrinsicsRecognizer: + return IntrinsicsRecognizer::kIntrinsicsRecognizerPassName; + case OptimizationPass::kCHAGuardOptimization: + return CHAGuardOptimization::kCHAGuardOptimizationPassName; + case OptimizationPass::kCodeSinking: + return CodeSinking::kCodeSinkingPassName; + case OptimizationPass::kConstructorFenceRedundancyElimination: + return ConstructorFenceRedundancyElimination::kCFREPassName; + case OptimizationPass::kScheduling: + return HInstructionScheduling::kInstructionSchedulingPassName; +#ifdef ART_ENABLE_CODEGEN_arm + case OptimizationPass::kInstructionSimplifierArm: + return arm::InstructionSimplifierArm::kInstructionSimplifierArmPassName; +#endif +#ifdef ART_ENABLE_CODEGEN_arm64 + case OptimizationPass::kInstructionSimplifierArm64: + return arm64::InstructionSimplifierArm64::kInstructionSimplifierArm64PassName; +#endif +#ifdef ART_ENABLE_CODEGEN_mips + case OptimizationPass::kPcRelativeFixupsMips: + return mips::PcRelativeFixups::kPcRelativeFixupsMipsPassName; + case OptimizationPass::kInstructionSimplifierMips: + return mips::InstructionSimplifierMips::kInstructionSimplifierMipsPassName; +#endif +#ifdef ART_ENABLE_CODEGEN_x86 + case OptimizationPass::kPcRelativeFixupsX86: + return x86::PcRelativeFixups::kPcRelativeFixupsX86PassName; +#endif +#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64) + case OptimizationPass::kX86MemoryOperandGeneration: + return x86::X86MemoryOperandGeneration::kX86MemoryOperandGenerationPassName; +#endif + } +} + +#define X(x) if (name == OptimizationPassName((x))) return (x) + +OptimizationPass OptimizationPassByName(const std::string& name) { + X(OptimizationPass::kBoundsCheckElimination); + X(OptimizationPass::kCHAGuardOptimization); + X(OptimizationPass::kCodeSinking); + X(OptimizationPass::kConstantFolding); + X(OptimizationPass::kConstructorFenceRedundancyElimination); + X(OptimizationPass::kDeadCodeElimination); + X(OptimizationPass::kGlobalValueNumbering); + X(OptimizationPass::kInductionVarAnalysis); + X(OptimizationPass::kInliner); + X(OptimizationPass::kInstructionSimplifier); + X(OptimizationPass::kIntrinsicsRecognizer); + X(OptimizationPass::kInvariantCodeMotion); + X(OptimizationPass::kLoadStoreAnalysis); + X(OptimizationPass::kLoadStoreElimination); + X(OptimizationPass::kLoopOptimization); + X(OptimizationPass::kScheduling); + X(OptimizationPass::kSelectGenerator); + X(OptimizationPass::kSharpening); + X(OptimizationPass::kSideEffectsAnalysis); +#ifdef ART_ENABLE_CODEGEN_arm + X(OptimizationPass::kInstructionSimplifierArm); +#endif +#ifdef ART_ENABLE_CODEGEN_arm64 + X(OptimizationPass::kInstructionSimplifierArm64); +#endif +#ifdef ART_ENABLE_CODEGEN_mips + X(OptimizationPass::kPcRelativeFixupsMips); + X(OptimizationPass::kInstructionSimplifierMips); +#endif +#ifdef ART_ENABLE_CODEGEN_x86 + X(OptimizationPass::kPcRelativeFixupsX86); + X(OptimizationPass::kX86MemoryOperandGeneration); +#endif + LOG(FATAL) << "Cannot find optimization " << name; + UNREACHABLE(); +} + +#undef X + +ArenaVector ConstructOptimizations( + const OptimizationDef definitions[], + size_t length, + ArenaAllocator* allocator, + HGraph* graph, + OptimizingCompilerStats* stats, + CodeGenerator* codegen, + CompilerDriver* driver, + const DexCompilationUnit& dex_compilation_unit, + VariableSizedHandleScope* handles) { + ArenaVector optimizations(allocator->Adapter()); + + // Some optimizations require SideEffectsAnalysis or HInductionVarAnalysis + // instances. This method uses the nearest instance preceeding it in the pass + // name list or fails fatally if no such analysis can be found. + SideEffectsAnalysis* most_recent_side_effects = nullptr; + HInductionVarAnalysis* most_recent_induction = nullptr; + LoadStoreAnalysis* most_recent_lsa = nullptr; + + // 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 + ? alt_name + : OptimizationPassName(pass); + HOptimization* opt = nullptr; + + switch (pass) { + // + // Analysis passes (kept in most recent for subsequent passes). + // + case OptimizationPass::kSideEffectsAnalysis: + opt = most_recent_side_effects = new (allocator) SideEffectsAnalysis(graph, name); + break; + case OptimizationPass::kInductionVarAnalysis: + opt = most_recent_induction = new (allocator) HInductionVarAnalysis(graph, name); + break; + case OptimizationPass::kLoadStoreAnalysis: + opt = most_recent_lsa = new (allocator) LoadStoreAnalysis(graph, 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); + break; + case OptimizationPass::kInvariantCodeMotion: + CHECK(most_recent_side_effects != nullptr); + opt = new (allocator) LICM(graph, *most_recent_side_effects, stats, name); + break; + case OptimizationPass::kLoopOptimization: + CHECK(most_recent_induction != nullptr); + opt = new (allocator) HLoopOptimization(graph, driver, most_recent_induction, stats, 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); + 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); + break; + // + // Regular passes. + // + case OptimizationPass::kConstantFolding: + opt = new (allocator) HConstantFolding(graph, name); + break; + case OptimizationPass::kDeadCodeElimination: + opt = new (allocator) HDeadCodeElimination(graph, stats, name); + break; + case OptimizationPass::kInliner: { + size_t number_of_dex_registers = dex_compilation_unit.GetCodeItem()->registers_size_; + opt = new (allocator) HInliner(graph, // outer_graph + graph, // outermost_graph + codegen, + dex_compilation_unit, // outer_compilation_unit + dex_compilation_unit, // outermost_compilation_unit + driver, + handles, + stats, + number_of_dex_registers, + /* total_number_of_instructions */ 0, + /* parent */ nullptr, + /* depth */ 0, + name); + break; + } + case OptimizationPass::kSharpening: + opt = new (allocator) HSharpening( + graph, codegen, dex_compilation_unit, driver, handles, name); + break; + case OptimizationPass::kSelectGenerator: + opt = new (allocator) HSelectGenerator(graph, handles, stats, name); + break; + case OptimizationPass::kInstructionSimplifier: + opt = new (allocator) InstructionSimplifier(graph, codegen, driver, stats, name); + break; + case OptimizationPass::kIntrinsicsRecognizer: + opt = new (allocator) IntrinsicsRecognizer(graph, stats, name); + break; + case OptimizationPass::kCHAGuardOptimization: + opt = new (allocator) CHAGuardOptimization(graph, name); + break; + case OptimizationPass::kCodeSinking: + opt = new (allocator) CodeSinking(graph, stats, name); + break; + case OptimizationPass::kConstructorFenceRedundancyElimination: + opt = new (allocator) ConstructorFenceRedundancyElimination(graph, stats, name); + break; + case OptimizationPass::kScheduling: + opt = new (allocator) HInstructionScheduling( + graph, driver->GetInstructionSet(), codegen, name); + break; + // + // Arch-specific passes. + // +#ifdef ART_ENABLE_CODEGEN_arm + case OptimizationPass::kInstructionSimplifierArm: + DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name"; + opt = new (allocator) arm::InstructionSimplifierArm(graph, stats); + break; +#endif +#ifdef ART_ENABLE_CODEGEN_arm64 + case OptimizationPass::kInstructionSimplifierArm64: + DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name"; + opt = new (allocator) arm64::InstructionSimplifierArm64(graph, stats); + break; +#endif +#ifdef ART_ENABLE_CODEGEN_mips + case OptimizationPass::kPcRelativeFixupsMips: + DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name"; + opt = new (allocator) mips::PcRelativeFixups(graph, codegen, stats); + break; + case OptimizationPass::kInstructionSimplifierMips: + DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name"; + opt = new (allocator) mips::InstructionSimplifierMips(graph, codegen, stats); + break; +#endif +#ifdef ART_ENABLE_CODEGEN_x86 + case OptimizationPass::kPcRelativeFixupsX86: + DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name"; + opt = new (allocator) x86::PcRelativeFixups(graph, codegen, stats); + break; + case OptimizationPass::kX86MemoryOperandGeneration: + DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name"; + opt = new (allocator) x86::X86MemoryOperandGeneration(graph, codegen, stats); + break; +#endif + } // switch + + // Add each next optimization to result vector. + CHECK(opt != nullptr); + DCHECK_STREQ(name, opt->GetPassName()); // sanity + optimizations.push_back(opt); + } + + return optimizations; +} + } // namespace art diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h index ce41a2e512..c170f155fa 100644 --- a/compiler/optimizing/optimization.h +++ b/compiler/optimizing/optimization.h @@ -23,6 +23,10 @@ namespace art { +class CodeGenerator; +class CompilerDriver; +class DexCompilationUnit; + /** * Abstraction to implement an optimization pass. */ @@ -58,6 +62,81 @@ class HOptimization : public ArenaObject { DISALLOW_COPY_AND_ASSIGN(HOptimization); }; +// Optimization passes that can be constructed by the helper method below. An enum +// field is preferred over a string lookup at places where performance matters. +// TODO: generate this table and lookup methods below automatically? +enum class OptimizationPass { + kBoundsCheckElimination, + kCHAGuardOptimization, + kCodeSinking, + kConstantFolding, + kConstructorFenceRedundancyElimination, + kDeadCodeElimination, + kGlobalValueNumbering, + kInductionVarAnalysis, + kInliner, + kInstructionSimplifier, + kIntrinsicsRecognizer, + kInvariantCodeMotion, + kLoadStoreAnalysis, + kLoadStoreElimination, + kLoopOptimization, + kScheduling, + kSelectGenerator, + kSharpening, + kSideEffectsAnalysis, +#ifdef ART_ENABLE_CODEGEN_arm + kInstructionSimplifierArm, +#endif +#ifdef ART_ENABLE_CODEGEN_arm64 + kInstructionSimplifierArm64, +#endif +#ifdef ART_ENABLE_CODEGEN_mips + kPcRelativeFixupsMips, + kInstructionSimplifierMips, +#endif +#ifdef ART_ENABLE_CODEGEN_x86 + kPcRelativeFixupsX86, +#endif +#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64) + kX86MemoryOperandGeneration, +#endif +}; + +// Lookup name of optimization pass. +const char* OptimizationPassName(OptimizationPass pass); + +// Lookup optimization pass by name. +OptimizationPass OptimizationPassByName(const std::string& name); + +// Optimization definition consisting of an optimization pass +// and an optional alternative name (nullptr denotes default). +typedef std::pair OptimizationDef; + +// Helper method for optimization definition array entries. +inline OptimizationDef OptDef(OptimizationPass pass, const char* name = nullptr) { + return std::make_pair(pass, name); +} + +// Helper method to construct series of optimization passes. +// The array should consist of the requested optimizations +// and optional alternative names for repeated passes. +// Example: +// { OptPass(kConstantFolding), +// OptPass(Inliner), +// OptPass(kConstantFolding, "constant_folding$after_inlining") +// } +ArenaVector ConstructOptimizations( + const OptimizationDef definitions[], + size_t length, + ArenaAllocator* allocator, + HGraph* graph, + OptimizingCompilerStats* stats, + CodeGenerator* codegen, + CompilerDriver* driver, + const DexCompilationUnit& dex_compilation_unit, + VariableSizedHandleScope* handles); + } // namespace art #endif // ART_COMPILER_OPTIMIZING_OPTIMIZATION_H_ diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 4974ed0ec5..53f9ec413b 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -22,23 +22,6 @@ #include -#ifdef ART_ENABLE_CODEGEN_arm64 -#include "instruction_simplifier_arm64.h" -#endif - -#ifdef ART_ENABLE_CODEGEN_mips -#include "instruction_simplifier_mips.h" -#include "pc_relative_fixups_mips.h" -#endif - -#ifdef ART_ENABLE_CODEGEN_x86 -#include "pc_relative_fixups_x86.h" -#endif - -#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64) -#include "x86_memory_gen.h" -#endif - #include "art_method-inl.h" #include "base/arena_allocator.h" #include "base/arena_containers.h" @@ -47,16 +30,10 @@ #include "base/mutex.h" #include "base/scoped_arena_allocator.h" #include "base/timing_logger.h" -#include "bounds_check_elimination.h" #include "builder.h" -#include "cha_guard_optimization.h" #include "code_generator.h" -#include "code_sinking.h" #include "compiled_method.h" #include "compiler.h" -#include "constant_folding.h" -#include "constructor_fence_redundancy_elimination.h" -#include "dead_code_elimination.h" #include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" #include "dex/verification_results.h" @@ -67,31 +44,19 @@ #include "driver/dex_compilation_unit.h" #include "graph_checker.h" #include "graph_visualizer.h" -#include "gvn.h" -#include "induction_var_analysis.h" #include "inliner.h" -#include "instruction_simplifier.h" -#include "instruction_simplifier_arm.h" -#include "intrinsics.h" #include "jit/debugger_interface.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "jit/jit_logger.h" #include "jni/quick/jni_compiler.h" -#include "licm.h" #include "linker/linker_patch.h" -#include "load_store_analysis.h" -#include "load_store_elimination.h" -#include "loop_optimization.h" #include "nodes.h" #include "oat_quick_method_header.h" #include "prepare_for_register_allocation.h" #include "reference_type_propagation.h" #include "register_allocator_linear_scan.h" -#include "scheduler.h" #include "select_generator.h" -#include "sharpening.h" -#include "side_effects_analysis.h" #include "ssa_builder.h" #include "ssa_liveness_analysis.h" #include "ssa_phi_elimination.h" @@ -335,21 +300,52 @@ class OptimizingCompiler FINAL : public Compiler { private: void RunOptimizations(HGraph* graph, CodeGenerator* codegen, - CompilerDriver* driver, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, - VariableSizedHandleScope* handles) const; + VariableSizedHandleScope* handles, + const OptimizationDef definitions[], + size_t length) const { + // Convert definitions to optimization passes. + ArenaVector optimizations = ConstructOptimizations( + definitions, + length, + graph->GetAllocator(), + graph, + compilation_stats_.get(), + codegen, + GetCompilerDriver(), + dex_compilation_unit, + handles); + DCHECK_EQ(length, optimizations.size()); + // Run the optimization passes one by one. + for (size_t i = 0; i < length; ++i) { + PassScope scope(optimizations[i]->GetPassName(), pass_observer); + optimizations[i]->Run(); + } + } - void RunOptimizations(HOptimization* optimizations[], - size_t length, - PassObserver* pass_observer) const; + template void RunOptimizations( + HGraph* graph, + CodeGenerator* codegen, + const DexCompilationUnit& dex_compilation_unit, + PassObserver* pass_observer, + VariableSizedHandleScope* handles, + const OptimizationDef (&definitions)[length]) const { + RunOptimizations( + graph, codegen, dex_compilation_unit, pass_observer, handles, definitions, length); + } + + void RunOptimizations(HGraph* graph, + CodeGenerator* codegen, + const DexCompilationUnit& dex_compilation_unit, + PassObserver* pass_observer, + VariableSizedHandleScope* handles) const; private: // Create a 'CompiledMethod' for an optimized graph. CompiledMethod* Emit(ArenaAllocator* allocator, CodeVectorAllocator* code_allocator, CodeGenerator* codegen, - CompilerDriver* driver, const DexFile::CodeItem* item) const; // Try compiling a method and return the code generator used for @@ -376,15 +372,15 @@ class OptimizingCompiler FINAL : public Compiler { void MaybeRunInliner(HGraph* graph, CodeGenerator* codegen, - CompilerDriver* driver, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, VariableSizedHandleScope* handles) const; - void RunArchOptimizations(InstructionSet instruction_set, - HGraph* graph, + void RunArchOptimizations(HGraph* graph, CodeGenerator* codegen, - PassObserver* pass_observer) const; + const DexCompilationUnit& dex_compilation_unit, + PassObserver* pass_observer, + VariableSizedHandleScope* handles) const; std::unique_ptr compilation_stats_; @@ -440,299 +436,130 @@ static bool IsInstructionSetSupported(InstructionSet instruction_set) { || instruction_set == InstructionSet::kX86_64; } -// Strip pass name suffix to get optimization name. -static std::string ConvertPassNameToOptimizationName(const std::string& pass_name) { - size_t pos = pass_name.find(kPassNameSeparator); - return pos == std::string::npos ? pass_name : pass_name.substr(0, pos); -} - -static HOptimization* BuildOptimization( - const std::string& pass_name, - ArenaAllocator* allocator, - HGraph* graph, - OptimizingCompilerStats* stats, - CodeGenerator* codegen, - CompilerDriver* driver, - const DexCompilationUnit& dex_compilation_unit, - VariableSizedHandleScope* handles, - SideEffectsAnalysis* most_recent_side_effects, - HInductionVarAnalysis* most_recent_induction, - LoadStoreAnalysis* most_recent_lsa) { - std::string opt_name = ConvertPassNameToOptimizationName(pass_name); - if (opt_name == BoundsCheckElimination::kBoundsCheckEliminationPassName) { - CHECK(most_recent_side_effects != nullptr && most_recent_induction != nullptr); - return new (allocator) BoundsCheckElimination(graph, - *most_recent_side_effects, - most_recent_induction); - } else if (opt_name == GVNOptimization::kGlobalValueNumberingPassName) { - CHECK(most_recent_side_effects != nullptr); - return new (allocator) GVNOptimization(graph, *most_recent_side_effects, pass_name.c_str()); - } else if (opt_name == HConstantFolding::kConstantFoldingPassName) { - return new (allocator) HConstantFolding(graph, pass_name.c_str()); - } else if (opt_name == HDeadCodeElimination::kDeadCodeEliminationPassName) { - return new (allocator) HDeadCodeElimination(graph, stats, pass_name.c_str()); - } else if (opt_name == HInliner::kInlinerPassName) { - size_t number_of_dex_registers = dex_compilation_unit.GetCodeItem()->registers_size_; - return new (allocator) HInliner(graph, // outer_graph - graph, // outermost_graph - codegen, - dex_compilation_unit, // outer_compilation_unit - dex_compilation_unit, // outermost_compilation_unit - driver, - handles, - stats, - number_of_dex_registers, - /* total_number_of_instructions */ 0, - /* parent */ nullptr); - } else if (opt_name == HSharpening::kSharpeningPassName) { - return new (allocator) HSharpening(graph, codegen, dex_compilation_unit, driver, handles); - } else if (opt_name == HSelectGenerator::kSelectGeneratorPassName) { - return new (allocator) HSelectGenerator(graph, handles, stats); - } else if (opt_name == HInductionVarAnalysis::kInductionPassName) { - return new (allocator) HInductionVarAnalysis(graph); - } else if (opt_name == InstructionSimplifier::kInstructionSimplifierPassName) { - return new (allocator) InstructionSimplifier(graph, codegen, driver, stats, pass_name.c_str()); - } else if (opt_name == IntrinsicsRecognizer::kIntrinsicsRecognizerPassName) { - return new (allocator) IntrinsicsRecognizer(graph, stats); - } else if (opt_name == LICM::kLoopInvariantCodeMotionPassName) { - CHECK(most_recent_side_effects != nullptr); - return new (allocator) LICM(graph, *most_recent_side_effects, stats); - } else if (opt_name == LoadStoreAnalysis::kLoadStoreAnalysisPassName) { - return new (allocator) LoadStoreAnalysis(graph); - } else if (opt_name == LoadStoreElimination::kLoadStoreEliminationPassName) { - CHECK(most_recent_side_effects != nullptr); - CHECK(most_recent_lsa != nullptr); - return new (allocator) LoadStoreElimination(graph, - *most_recent_side_effects, - *most_recent_lsa, stats); - } else if (opt_name == SideEffectsAnalysis::kSideEffectsAnalysisPassName) { - return new (allocator) SideEffectsAnalysis(graph); - } else if (opt_name == HLoopOptimization::kLoopOptimizationPassName) { - return new (allocator) HLoopOptimization(graph, driver, most_recent_induction, stats); - } else if (opt_name == CHAGuardOptimization::kCHAGuardOptimizationPassName) { - return new (allocator) CHAGuardOptimization(graph); - } else if (opt_name == CodeSinking::kCodeSinkingPassName) { - return new (allocator) CodeSinking(graph, stats); - } else if (opt_name == ConstructorFenceRedundancyElimination::kPassName) { - return new (allocator) ConstructorFenceRedundancyElimination(graph, stats); -#ifdef ART_ENABLE_CODEGEN_arm - } else if (opt_name == arm::InstructionSimplifierArm::kInstructionSimplifierArmPassName) { - return new (allocator) arm::InstructionSimplifierArm(graph, stats); -#endif -#ifdef ART_ENABLE_CODEGEN_arm64 - } else if (opt_name == arm64::InstructionSimplifierArm64::kInstructionSimplifierArm64PassName) { - return new (allocator) arm64::InstructionSimplifierArm64(graph, stats); -#endif -#ifdef ART_ENABLE_CODEGEN_mips - } else if (opt_name == mips::PcRelativeFixups::kPcRelativeFixupsMipsPassName) { - return new (allocator) mips::PcRelativeFixups(graph, codegen, stats); - } else if (opt_name == mips::InstructionSimplifierMips::kInstructionSimplifierMipsPassName) { - return new (allocator) mips::InstructionSimplifierMips(graph, codegen, stats); -#endif -#ifdef ART_ENABLE_CODEGEN_x86 - } else if (opt_name == x86::PcRelativeFixups::kPcRelativeFixupsX86PassName) { - return new (allocator) x86::PcRelativeFixups(graph, codegen, stats); - } else if (opt_name == x86::X86MemoryOperandGeneration::kX86MemoryOperandGenerationPassName) { - return new (allocator) x86::X86MemoryOperandGeneration(graph, codegen, stats); -#endif - } - return nullptr; -} - -static ArenaVector BuildOptimizations( - const std::vector& pass_names, - ArenaAllocator* allocator, - HGraph* graph, - OptimizingCompilerStats* stats, - CodeGenerator* codegen, - CompilerDriver* driver, - const DexCompilationUnit& dex_compilation_unit, - VariableSizedHandleScope* handles) { - // Few HOptimizations constructors require SideEffectsAnalysis or HInductionVarAnalysis - // instances. This method assumes that each of them expects the nearest instance preceeding it - // in the pass name list. - SideEffectsAnalysis* most_recent_side_effects = nullptr; - HInductionVarAnalysis* most_recent_induction = nullptr; - LoadStoreAnalysis* most_recent_lsa = nullptr; - ArenaVector ret(allocator->Adapter()); - for (const std::string& pass_name : pass_names) { - HOptimization* opt = BuildOptimization( - pass_name, - allocator, - graph, - stats, - codegen, - driver, - dex_compilation_unit, - handles, - most_recent_side_effects, - most_recent_induction, - most_recent_lsa); - CHECK(opt != nullptr) << "Couldn't build optimization: \"" << pass_name << "\""; - ret.push_back(opt); - - std::string opt_name = ConvertPassNameToOptimizationName(pass_name); - if (opt_name == SideEffectsAnalysis::kSideEffectsAnalysisPassName) { - most_recent_side_effects = down_cast(opt); - } else if (opt_name == HInductionVarAnalysis::kInductionPassName) { - most_recent_induction = down_cast(opt); - } else if (opt_name == LoadStoreAnalysis::kLoadStoreAnalysisPassName) { - most_recent_lsa = down_cast(opt); - } - } - return ret; -} - -void OptimizingCompiler::RunOptimizations(HOptimization* optimizations[], - size_t length, - PassObserver* pass_observer) const { - for (size_t i = 0; i < length; ++i) { - PassScope scope(optimizations[i]->GetPassName(), pass_observer); - optimizations[i]->Run(); - } -} - void OptimizingCompiler::MaybeRunInliner(HGraph* graph, CodeGenerator* codegen, - CompilerDriver* driver, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, VariableSizedHandleScope* handles) const { - OptimizingCompilerStats* stats = compilation_stats_.get(); - const CompilerOptions& compiler_options = driver->GetCompilerOptions(); + const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); bool should_inline = (compiler_options.GetInlineMaxCodeUnits() > 0); if (!should_inline) { return; } - size_t number_of_dex_registers = dex_compilation_unit.GetCodeItem()->registers_size_; - HInliner* inliner = new (graph->GetAllocator()) HInliner( - graph, // outer_graph - graph, // outermost_graph - codegen, - dex_compilation_unit, // outer_compilation_unit - dex_compilation_unit, // outermost_compilation_unit - driver, - handles, - stats, - number_of_dex_registers, - /* total_number_of_instructions */ 0, - /* parent */ nullptr); - HOptimization* optimizations[] = { inliner }; - - RunOptimizations(optimizations, arraysize(optimizations), pass_observer); + OptimizationDef optimizations[] = { + OptDef(OptimizationPass::kInliner) + }; + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + optimizations); } -void OptimizingCompiler::RunArchOptimizations(InstructionSet instruction_set, - HGraph* graph, +void OptimizingCompiler::RunArchOptimizations(HGraph* graph, CodeGenerator* codegen, - PassObserver* pass_observer) const { - UNUSED(codegen); // To avoid compilation error when compiling for svelte - OptimizingCompilerStats* stats = compilation_stats_.get(); - ArenaAllocator* allocator = graph->GetAllocator(); - switch (instruction_set) { + const DexCompilationUnit& dex_compilation_unit, + PassObserver* pass_observer, + VariableSizedHandleScope* handles) const { + switch (GetCompilerDriver()->GetInstructionSet()) { #if defined(ART_ENABLE_CODEGEN_arm) case InstructionSet::kThumb2: case InstructionSet::kArm: { - arm::InstructionSimplifierArm* simplifier = - new (allocator) arm::InstructionSimplifierArm(graph, stats); - SideEffectsAnalysis* side_effects = new (allocator) SideEffectsAnalysis(graph); - GVNOptimization* gvn = - new (allocator) GVNOptimization(graph, *side_effects, "GVN$after_arch"); - HInstructionScheduling* scheduling = - new (allocator) HInstructionScheduling(graph, instruction_set, codegen); - HOptimization* arm_optimizations[] = { - simplifier, - side_effects, - gvn, - scheduling, + OptimizationDef arm_optimizations[] = { + OptDef(OptimizationPass::kInstructionSimplifierArm), + OptDef(OptimizationPass::kSideEffectsAnalysis), + OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), + OptDef(OptimizationPass::kScheduling) }; - RunOptimizations(arm_optimizations, arraysize(arm_optimizations), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + arm_optimizations); break; } #endif #ifdef ART_ENABLE_CODEGEN_arm64 case InstructionSet::kArm64: { - arm64::InstructionSimplifierArm64* simplifier = - new (allocator) arm64::InstructionSimplifierArm64(graph, stats); - SideEffectsAnalysis* side_effects = new (allocator) SideEffectsAnalysis(graph); - GVNOptimization* gvn = - new (allocator) GVNOptimization(graph, *side_effects, "GVN$after_arch"); - HInstructionScheduling* scheduling = - new (allocator) HInstructionScheduling(graph, instruction_set); - HOptimization* arm64_optimizations[] = { - simplifier, - side_effects, - gvn, - scheduling, + OptimizationDef arm64_optimizations[] = { + OptDef(OptimizationPass::kInstructionSimplifierArm64), + OptDef(OptimizationPass::kSideEffectsAnalysis), + OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), + OptDef(OptimizationPass::kScheduling) }; - RunOptimizations(arm64_optimizations, arraysize(arm64_optimizations), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + arm64_optimizations); break; } #endif #ifdef ART_ENABLE_CODEGEN_mips case InstructionSet::kMips: { - mips::InstructionSimplifierMips* simplifier = - new (allocator) mips::InstructionSimplifierMips(graph, codegen, stats); - SideEffectsAnalysis* side_effects = new (allocator) SideEffectsAnalysis(graph); - GVNOptimization* gvn = - new (allocator) GVNOptimization(graph, *side_effects, "GVN$after_arch"); - mips::PcRelativeFixups* pc_relative_fixups = - new (allocator) mips::PcRelativeFixups(graph, codegen, stats); - HOptimization* mips_optimizations[] = { - simplifier, - side_effects, - gvn, - pc_relative_fixups, + OptimizationDef mips_optimizations[] = { + OptDef(OptimizationPass::kInstructionSimplifierMips), + OptDef(OptimizationPass::kSideEffectsAnalysis), + OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), + OptDef(OptimizationPass::kPcRelativeFixupsMips) }; - RunOptimizations(mips_optimizations, arraysize(mips_optimizations), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + mips_optimizations); break; } #endif #ifdef ART_ENABLE_CODEGEN_mips64 case InstructionSet::kMips64: { - SideEffectsAnalysis* side_effects = new (allocator) SideEffectsAnalysis(graph); - GVNOptimization* gvn = - new (allocator) GVNOptimization(graph, *side_effects, "GVN$after_arch"); - HOptimization* mips64_optimizations[] = { - side_effects, - gvn, + OptimizationDef mips64_optimizations[] = { + OptDef(OptimizationPass::kSideEffectsAnalysis), + OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch") }; - RunOptimizations(mips64_optimizations, arraysize(mips64_optimizations), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + mips64_optimizations); break; } #endif #ifdef ART_ENABLE_CODEGEN_x86 case InstructionSet::kX86: { - SideEffectsAnalysis* side_effects = new (allocator) SideEffectsAnalysis(graph); - GVNOptimization* gvn = - new (allocator) GVNOptimization(graph, *side_effects, "GVN$after_arch"); - x86::PcRelativeFixups* pc_relative_fixups = - new (allocator) x86::PcRelativeFixups(graph, codegen, stats); - x86::X86MemoryOperandGeneration* memory_gen = - new (allocator) x86::X86MemoryOperandGeneration(graph, codegen, stats); - HOptimization* x86_optimizations[] = { - side_effects, - gvn, - pc_relative_fixups, - memory_gen + OptimizationDef x86_optimizations[] = { + OptDef(OptimizationPass::kSideEffectsAnalysis), + OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), + OptDef(OptimizationPass::kPcRelativeFixupsX86), + OptDef(OptimizationPass::kX86MemoryOperandGeneration) }; - RunOptimizations(x86_optimizations, arraysize(x86_optimizations), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + x86_optimizations); break; } #endif #ifdef ART_ENABLE_CODEGEN_x86_64 case InstructionSet::kX86_64: { - SideEffectsAnalysis* side_effects = new (allocator) SideEffectsAnalysis(graph); - GVNOptimization* gvn = - new (allocator) GVNOptimization(graph, *side_effects, "GVN$after_arch"); - x86::X86MemoryOperandGeneration* memory_gen = - new (allocator) x86::X86MemoryOperandGeneration(graph, codegen, stats); - HOptimization* x86_64_optimizations[] = { - side_effects, - gvn, - memory_gen + OptimizationDef x86_64_optimizations[] = { + OptDef(OptimizationPass::kSideEffectsAnalysis), + OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), + OptDef(OptimizationPass::kX86MemoryOperandGeneration) }; - RunOptimizations(x86_64_optimizations, arraysize(x86_64_optimizations), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + x86_64_optimizations); break; } #endif @@ -768,110 +595,93 @@ static void AllocateRegisters(HGraph* graph, } } +// Strip pass name suffix to get optimization name. +static std::string ConvertPassNameToOptimizationName(const std::string& pass_name) { + size_t pos = pass_name.find(kPassNameSeparator); + return pos == std::string::npos ? pass_name : pass_name.substr(0, pos); +} + void OptimizingCompiler::RunOptimizations(HGraph* graph, CodeGenerator* codegen, - CompilerDriver* driver, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, VariableSizedHandleScope* handles) const { - OptimizingCompilerStats* stats = compilation_stats_.get(); - ArenaAllocator* allocator = graph->GetAllocator(); - if (driver->GetCompilerOptions().GetPassesToRun() != nullptr) { - ArenaVector optimizations = BuildOptimizations( - *driver->GetCompilerOptions().GetPassesToRun(), - allocator, - graph, - stats, - codegen, - driver, - dex_compilation_unit, - handles); - RunOptimizations(optimizations.data(), optimizations.size(), pass_observer); + const std::vector* pass_names = + GetCompilerDriver()->GetCompilerOptions().GetPassesToRun(); + if (pass_names != nullptr) { + // If passes were defined on command-line, build the optimization + // passes and run these instead of the built-in optimizations. + const size_t length = pass_names->size(); + std::vector optimizations; + for (const std::string& pass_name : *pass_names) { + std::string opt_name = ConvertPassNameToOptimizationName(pass_name); + optimizations.push_back(OptDef(OptimizationPassByName(opt_name.c_str()), pass_name.c_str())); + } + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + optimizations.data(), + length); return; } - HDeadCodeElimination* dce1 = new (allocator) HDeadCodeElimination( - graph, stats, "dead_code_elimination$initial"); - HDeadCodeElimination* dce2 = new (allocator) HDeadCodeElimination( - graph, stats, "dead_code_elimination$after_inlining"); - HDeadCodeElimination* dce3 = new (allocator) HDeadCodeElimination( - graph, stats, "dead_code_elimination$final"); - HConstantFolding* fold1 = new (allocator) HConstantFolding(graph, "constant_folding"); - InstructionSimplifier* simplify1 = new (allocator) InstructionSimplifier( - graph, codegen, driver, stats); - HSelectGenerator* select_generator = new (allocator) HSelectGenerator(graph, handles, stats); - HConstantFolding* fold2 = new (allocator) HConstantFolding( - graph, "constant_folding$after_inlining"); - HConstantFolding* fold3 = new (allocator) HConstantFolding(graph, "constant_folding$after_bce"); - SideEffectsAnalysis* side_effects1 = new (allocator) SideEffectsAnalysis( - graph, "side_effects$before_gvn"); - SideEffectsAnalysis* side_effects2 = new (allocator) SideEffectsAnalysis( - graph, "side_effects$before_lse"); - GVNOptimization* gvn = new (allocator) GVNOptimization(graph, *side_effects1); - LICM* licm = new (allocator) LICM(graph, *side_effects1, stats); - HInductionVarAnalysis* induction = new (allocator) HInductionVarAnalysis(graph); - BoundsCheckElimination* bce = - new (allocator) BoundsCheckElimination(graph, *side_effects1, induction); - HLoopOptimization* loop = new (allocator) HLoopOptimization(graph, driver, induction, stats); - LoadStoreAnalysis* lsa = new (allocator) LoadStoreAnalysis(graph); - LoadStoreElimination* lse = - new (allocator) LoadStoreElimination(graph, *side_effects2, *lsa, stats); - HSharpening* sharpening = new (allocator) HSharpening( - graph, codegen, dex_compilation_unit, driver, handles); - InstructionSimplifier* simplify2 = new (allocator) InstructionSimplifier( - graph, codegen, driver, stats, "instruction_simplifier$after_inlining"); - InstructionSimplifier* simplify3 = new (allocator) InstructionSimplifier( - graph, codegen, driver, stats, "instruction_simplifier$after_bce"); - InstructionSimplifier* simplify4 = new (allocator) InstructionSimplifier( - graph, codegen, driver, stats, "instruction_simplifier$before_codegen"); - IntrinsicsRecognizer* intrinsics = new (allocator) IntrinsicsRecognizer(graph, stats); - CHAGuardOptimization* cha_guard = new (allocator) CHAGuardOptimization(graph); - CodeSinking* code_sinking = new (allocator) CodeSinking(graph, stats); - ConstructorFenceRedundancyElimination* cfre = - new (allocator) ConstructorFenceRedundancyElimination(graph, stats); - - HOptimization* optimizations1[] = { - intrinsics, - sharpening, - fold1, - simplify1, - dce1, + OptimizationDef optimizations1[] = { + OptDef(OptimizationPass::kIntrinsicsRecognizer), + OptDef(OptimizationPass::kSharpening), + OptDef(OptimizationPass::kConstantFolding), + OptDef(OptimizationPass::kInstructionSimplifier), + OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$initial") }; - RunOptimizations(optimizations1, arraysize(optimizations1), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + optimizations1); - MaybeRunInliner(graph, codegen, driver, dex_compilation_unit, pass_observer, handles); + MaybeRunInliner(graph, codegen, dex_compilation_unit, pass_observer, handles); - HOptimization* optimizations2[] = { + OptimizationDef optimizations2[] = { // SelectGenerator depends on the InstructionSimplifier removing // redundant suspend checks to recognize empty blocks. - select_generator, - fold2, // TODO: if we don't inline we can also skip fold2. - simplify2, - dce2, - side_effects1, - gvn, - licm, - induction, - bce, - loop, - fold3, // evaluates code generated by dynamic bce - simplify3, - side_effects2, - lsa, - lse, - cha_guard, - dce3, - code_sinking, + 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::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"), + OptDef(OptimizationPass::kLoadStoreAnalysis), + OptDef(OptimizationPass::kLoadStoreElimination), + OptDef(OptimizationPass::kCHAGuardOptimization), + 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. - simplify4, - cfre, // Eliminate constructor fences after code sinking to avoid - // complicated sinking logic to split a fence with many inputs. + 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) }; - RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + optimizations2); - RunArchOptimizations(driver->GetInstructionSet(), graph, codegen, pass_observer); + RunArchOptimizations(graph, codegen, dex_compilation_unit, pass_observer, handles); } static ArenaVector EmitAndSortLinkerPatches(CodeGenerator* codegen) { @@ -890,7 +700,6 @@ static ArenaVector EmitAndSortLinkerPatches(CodeGenerator* CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, CodeVectorAllocator* code_allocator, CodeGenerator* codegen, - CompilerDriver* compiler_driver, const DexFile::CodeItem* code_item_for_osr_check) const { ArenaVector linker_patches = EmitAndSortLinkerPatches(codegen); ArenaVector stack_map(allocator->Adapter(kArenaAllocStackMaps)); @@ -905,7 +714,7 @@ CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, code_item_for_osr_check); CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod( - compiler_driver, + GetCompilerDriver(), codegen->GetInstructionSet(), ArrayRef(code_allocator->GetMemory()), // Follow Quick's behavior and set the frame size to zero if it is @@ -1049,7 +858,6 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, RunOptimizations(graph, codegen.get(), - compiler_driver, dex_compilation_unit, &pass_observer, handles); @@ -1140,20 +948,20 @@ CodeGenerator* OptimizingCompiler::TryCompileIntrinsic( builder.BuildIntrinsicGraph(method); } - OptimizingCompilerStats* stats = compilation_stats_.get(); - InstructionSimplifier* simplify = new (allocator) InstructionSimplifier( - graph, codegen.get(), compiler_driver, stats); - IntrinsicsRecognizer* intrinsics = new (allocator) IntrinsicsRecognizer(graph, stats); - - HOptimization* optimizations[] = { - intrinsics, - // Some intrinsics are converted to HIR by the simplifier and the codegen also - // has a few assumptions that only the instruction simplifier can satisfy. - simplify, + OptimizationDef optimizations[] = { + OptDef(OptimizationPass::kIntrinsicsRecognizer), + // Some intrinsics are converted to HIR by the simplifier and the codegen also + // has a few assumptions that only the instruction simplifier can satisfy. + OptDef(OptimizationPass::kInstructionSimplifier), }; - RunOptimizations(optimizations, arraysize(optimizations), &pass_observer); + RunOptimizations(graph, + codegen.get(), + dex_compilation_unit, + &pass_observer, + handles, + optimizations); - RunArchOptimizations(compiler_driver->GetInstructionSet(), graph, codegen.get(), &pass_observer); + RunArchOptimizations(graph, codegen.get(), dex_compilation_unit, &pass_observer, handles); AllocateRegisters(graph, codegen.get(), @@ -1243,7 +1051,6 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, compiled_method = Emit(&allocator, &code_allocator, codegen.get(), - compiler_driver, compiled_intrinsic ? nullptr : code_item); if (compiled_intrinsic) { compiled_method->MarkAsIntrinsic(); @@ -1325,7 +1132,6 @@ CompiledMethod* OptimizingCompiler::JniCompile(uint32_t access_flags, CompiledMethod* compiled_method = Emit(&allocator, &code_allocator, codegen.get(), - GetCompilerDriver(), /* code_item_for_osr_check */ nullptr); compiled_method->MarkAsIntrinsic(); return compiled_method; diff --git a/compiler/optimizing/pc_relative_fixups_mips.h b/compiler/optimizing/pc_relative_fixups_mips.h index 5a7397bf9d..ec2c711f8d 100644 --- a/compiler/optimizing/pc_relative_fixups_mips.h +++ b/compiler/optimizing/pc_relative_fixups_mips.h @@ -29,7 +29,7 @@ namespace mips { class PcRelativeFixups : public HOptimization { public: PcRelativeFixups(HGraph* graph, CodeGenerator* codegen, OptimizingCompilerStats* stats) - : HOptimization(graph, "pc_relative_fixups_mips", stats), + : HOptimization(graph, kPcRelativeFixupsMipsPassName, stats), codegen_(codegen) {} static constexpr const char* kPcRelativeFixupsMipsPassName = "pc_relative_fixups_mips"; diff --git a/compiler/optimizing/scheduler.h b/compiler/optimizing/scheduler.h index a6e160379b..bb7c353bc2 100644 --- a/compiler/optimizing/scheduler.h +++ b/compiler/optimizing/scheduler.h @@ -495,8 +495,11 @@ inline bool SchedulingGraph::IsSchedulingBarrier(const HInstruction* instruction class HInstructionScheduling : public HOptimization { public: - HInstructionScheduling(HGraph* graph, InstructionSet instruction_set, CodeGenerator* cg = nullptr) - : HOptimization(graph, kInstructionScheduling), + HInstructionScheduling(HGraph* graph, + InstructionSet instruction_set, + CodeGenerator* cg = nullptr, + const char* name = kInstructionSchedulingPassName) + : HOptimization(graph, name), codegen_(cg), instruction_set_(instruction_set) {} @@ -505,7 +508,7 @@ class HInstructionScheduling : public HOptimization { } void Run(bool only_optimize_loop_blocks, bool schedule_randomly); - static constexpr const char* kInstructionScheduling = "scheduler"; + static constexpr const char* kInstructionSchedulingPassName = "scheduler"; private: CodeGenerator* const codegen_; diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc index 77ec9a6285..66e51421ca 100644 --- a/compiler/optimizing/select_generator.cc +++ b/compiler/optimizing/select_generator.cc @@ -24,8 +24,9 @@ static constexpr size_t kMaxInstructionsInBranch = 1u; HSelectGenerator::HSelectGenerator(HGraph* graph, VariableSizedHandleScope* handles, - OptimizingCompilerStats* stats) - : HOptimization(graph, kSelectGeneratorPassName, stats), + OptimizingCompilerStats* stats, + const char* name) + : HOptimization(graph, name, stats), handle_scope_(handles) { } diff --git a/compiler/optimizing/select_generator.h b/compiler/optimizing/select_generator.h index f8cf00e35a..bda57fd5c8 100644 --- a/compiler/optimizing/select_generator.h +++ b/compiler/optimizing/select_generator.h @@ -65,7 +65,8 @@ class HSelectGenerator : public HOptimization { public: HSelectGenerator(HGraph* graph, VariableSizedHandleScope* handles, - OptimizingCompilerStats* stats); + OptimizingCompilerStats* stats, + const char* name = kSelectGeneratorPassName); void Run() OVERRIDE; diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h index f74b0afdbf..bb1954eeeb 100644 --- a/compiler/optimizing/sharpening.h +++ b/compiler/optimizing/sharpening.h @@ -36,8 +36,9 @@ class HSharpening : public HOptimization { CodeGenerator* codegen, const DexCompilationUnit& compilation_unit, CompilerDriver* compiler_driver, - VariableSizedHandleScope* handles) - : HOptimization(graph, kSharpeningPassName), + VariableSizedHandleScope* handles, + const char* name = kSharpeningPassName) + : HOptimization(graph, name), codegen_(codegen), compilation_unit_(compilation_unit), compiler_driver_(compiler_driver), -- GitLab From e5a2ae30bdbe379695dc886861b23dce57de0825 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Fri, 17 Nov 2017 16:39:01 -0800 Subject: [PATCH 060/226] Make JVMTI DisposeEnvironment and GetEnv thread safe. Previously we were relying on the mutator lock to keep these safe but it turns out this was not sufficient. We give the list of active jvmtiEnv's it's own lock to synchronize access. We also changed it so that during events we would collect all the environments and callbacks prior to actually calling any of them. This is required for making sure that we don't hold locks across user code or potentially miss any environments. This does have implications for when one is last able to prevent an environment from getting an event but since the spec is vague about this anyway this is not an issue. Doing this required a major re-write of our event-dispatch system. Test: ./test.py --host -j50 Test: ./art/tools/run-libjdwp-tests.sh --mode=host Bug: 69465262 Change-Id: I170950db6c6e43b5f3c8bdca1b8d087937070496 --- openjdkjvmti/art_jvmti.h | 4 + openjdkjvmti/events-inl.h | 277 +++++++++++------- openjdkjvmti/events.cc | 21 +- openjdkjvmti/events.h | 67 +++-- openjdkjvmti/object_tagging.cc | 3 +- openjdkjvmti/ti_dump.cc | 2 +- openjdkjvmti/ti_phase.cc | 11 +- runtime/base/mutex-inl.h | 4 +- runtime/base/mutex.h | 5 + runtime/ti/agent.cc | 3 + test/1941-dispose-stress/dispose_stress.cc | 59 ++++ test/1941-dispose-stress/expected.txt | 1 + test/1941-dispose-stress/info.txt | 3 + test/1941-dispose-stress/run | 18 ++ test/1941-dispose-stress/src/Main.java | 21 ++ .../src/art/Breakpoint.java | 202 +++++++++++++ .../1941-dispose-stress/src/art/Test1941.java | 72 +++++ test/1941-dispose-stress/src/art/Trace.java | 68 +++++ test/Android.bp | 1 + 19 files changed, 700 insertions(+), 142 deletions(-) create mode 100644 test/1941-dispose-stress/dispose_stress.cc create mode 100644 test/1941-dispose-stress/expected.txt create mode 100644 test/1941-dispose-stress/info.txt create mode 100755 test/1941-dispose-stress/run create mode 100644 test/1941-dispose-stress/src/Main.java create mode 100644 test/1941-dispose-stress/src/art/Breakpoint.java create mode 100644 test/1941-dispose-stress/src/art/Test1941.java create mode 100644 test/1941-dispose-stress/src/art/Trace.java diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h index 682b82b5cd..e8e62c2b40 100644 --- a/openjdkjvmti/art_jvmti.h +++ b/openjdkjvmti/art_jvmti.h @@ -94,6 +94,10 @@ struct ArtJvmTiEnv : public jvmtiEnv { static ArtJvmTiEnv* AsArtJvmTiEnv(jvmtiEnv* env) { return art::down_cast(env); } + + // Top level lock. Nothing can be held when we get this except for mutator lock for full + // thread-suspension. + static art::Mutex *gEnvMutex ACQUIRED_AFTER(art::Locks::mutator_lock_); }; // Macro and constexpr to make error values less annoying to write. diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h index 5344e0fbde..007669b50f 100644 --- a/openjdkjvmti/events-inl.h +++ b/openjdkjvmti/events-inl.h @@ -46,6 +46,45 @@ static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e) { namespace impl { +// Helper for ensuring that the dispatch environment is sane. Events with JNIEnvs need to stash +// pending exceptions since they can cause new ones to be thrown. In accordance with the JVMTI +// specification we allow exceptions originating from events to overwrite the current exception, +// including exceptions originating from earlier events. +class ScopedEventDispatchEnvironment FINAL : public art::ValueObject { + public: + ScopedEventDispatchEnvironment() : env_(nullptr), throw_(nullptr, nullptr) { + DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative); + } + + explicit ScopedEventDispatchEnvironment(JNIEnv* env) + : env_(env), + throw_(env_, env_->ExceptionOccurred()) { + DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative); + // The spec doesn't say how much local data should be there, so we just give 128 which seems + // likely to be enough for most cases. + env_->PushLocalFrame(128); + env_->ExceptionClear(); + } + + ~ScopedEventDispatchEnvironment() { + if (env_ != nullptr) { + if (throw_.get() != nullptr && !env_->ExceptionCheck()) { + // TODO It would be nice to add the overwritten exceptions to the suppressed exceptions list + // of the newest exception. + env_->Throw(throw_.get()); + } + env_->PopLocalFrame(nullptr); + } + DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative); + } + + private: + JNIEnv* env_; + ScopedLocalRef throw_; + + DISALLOW_COPY_AND_ASSIGN(ScopedEventDispatchEnvironment); +}; + // Infrastructure to achieve type safety for event dispatch. #define FORALL_EVENT_TYPES(fn) \ @@ -97,27 +136,68 @@ FORALL_EVENT_TYPES(EVENT_FN_TYPE) #undef EVENT_FN_TYPE -template -ALWAYS_INLINE inline typename EventFnType::type GetCallback(ArtJvmTiEnv* env); - -#define GET_CALLBACK(name, enum_name) \ -template <> \ -ALWAYS_INLINE inline EventFnType::type GetCallback( \ - ArtJvmTiEnv* env) { \ - if (env->event_callbacks == nullptr) { \ - return nullptr; \ - } \ - return env->event_callbacks->name; \ -} +#define MAKE_EVENT_HANDLER_FUNC(name, enum_name) \ +template<> \ +struct EventHandlerFunc { \ + using EventFnType = typename impl::EventFnType::type; \ + explicit EventHandlerFunc(ArtJvmTiEnv* env) \ + : env_(env), \ + fn_(env_->event_callbacks == nullptr ? nullptr : env_->event_callbacks->name) { } \ + \ + template \ + ALWAYS_INLINE \ + void ExecuteCallback(JNIEnv* jnienv, Args... args) const { \ + if (fn_ != nullptr) { \ + ScopedEventDispatchEnvironment sede(jnienv); \ + DoExecute(jnienv, args...); \ + } \ + } \ + \ + template \ + ALWAYS_INLINE \ + void ExecuteCallback(Args... args) const { \ + if (fn_ != nullptr) { \ + ScopedEventDispatchEnvironment sede; \ + DoExecute(args...); \ + } \ + } \ + \ + private: \ + template \ + ALWAYS_INLINE \ + inline void DoExecute(Args... args) const { \ + static_assert(std::is_same::value, \ + "Unexpected different type of ExecuteCallback"); \ + fn_(env_, args...); \ + } \ + \ + public: \ + ArtJvmTiEnv* env_; \ + EventFnType fn_; \ +}; -FORALL_EVENT_TYPES(GET_CALLBACK) +FORALL_EVENT_TYPES(MAKE_EVENT_HANDLER_FUNC) -#undef GET_CALLBACK +#undef MAKE_EVENT_HANDLER_FUNC #undef FORALL_EVENT_TYPES } // namespace impl +template +inline std::vector> EventHandler::CollectEvents(art::Thread* thread, + Args... args) const { + art::MutexLock mu(thread, envs_lock_); + std::vector> handlers; + for (ArtJvmTiEnv* env : envs) { + if (ShouldDispatch(env, thread, args...)) { + impl::EventHandlerFunc h(env); + handlers.push_back(h); + } + } + return handlers; +} + // C++ does not allow partial template function specialization. The dispatch for our separated // ClassFileLoadHook event types is the same, so use this helper for code deduplication. template @@ -131,29 +211,37 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, const unsigned char* class_data, jint* new_class_data_len, unsigned char** new_class_data) const { + art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable || kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable, "Unsupported event"); DCHECK(*new_class_data == nullptr); jint current_len = class_data_len; unsigned char* current_class_data = const_cast(class_data); + std::vector> handlers = + CollectEvents(thread, + jnienv, + class_being_redefined, + loader, + name, + protection_domain, + class_data_len, + class_data, + new_class_data_len, + new_class_data); ArtJvmTiEnv* last_env = nullptr; - for (ArtJvmTiEnv* env : envs) { - if (env == nullptr) { - continue; - } + for (const impl::EventHandlerFunc& event : handlers) { jint new_len = 0; unsigned char* new_data = nullptr; - DispatchEventOnEnv(env, - thread, - jnienv, - class_being_redefined, - loader, - name, - protection_domain, - current_len, - static_cast(current_class_data), - &new_len, - &new_data); + ExecuteCallback(event, + jnienv, + class_being_redefined, + loader, + name, + protection_domain, + current_len, + static_cast(current_class_data), + &new_len, + &new_data); if (new_data != nullptr && new_data != current_class_data) { // Destroy the data the last transformer made. We skip this if the previous state was the // initial one since we don't know here which jvmtiEnv allocated it. @@ -162,7 +250,7 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, if (last_env != nullptr) { last_env->Deallocate(current_class_data); } - last_env = env; + last_env = event.env_; current_class_data = new_data; current_len = new_len; } @@ -176,70 +264,28 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, // Our goal for DispatchEvent: Do not allow implicit type conversion. Types of ...args must match // exactly the argument types of the corresponding Jvmti kEvent function pointer. -template -inline void EventHandler::ExecuteCallback(ArtJvmTiEnv* env, Args... args) { - using FnType = typename impl::EventFnType::type; - FnType callback = impl::GetCallback(env); - if (callback != nullptr) { - (*callback)(env, args...); - } -} - template inline void EventHandler::DispatchEvent(art::Thread* thread, Args... args) const { + art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); static_assert(!std::is_same>>>::value, "Should be calling DispatchEvent with explicit JNIEnv* argument!"); DCHECK(thread == nullptr || !thread->IsExceptionPending()); - for (ArtJvmTiEnv* env : envs) { - if (env != nullptr) { - DispatchEventOnEnv(env, thread, args...); - } + std::vector> events = CollectEvents(thread, args...); + for (auto event : events) { + ExecuteCallback(event, args...); } } -// Helper for ensuring that the dispatch environment is sane. Events with JNIEnvs need to stash -// pending exceptions since they can cause new ones to be thrown. In accordance with the JVMTI -// specification we allow exceptions originating from events to overwrite the current exception, -// including exceptions originating from earlier events. -class ScopedEventDispatchEnvironment FINAL : public art::ValueObject { - public: - explicit ScopedEventDispatchEnvironment(JNIEnv* env) - : env_(env), - thr_(env_, env_->ExceptionOccurred()), - suspend_(art::Thread::Current(), art::kNative) { - // The spec doesn't say how much local data should be there, so we just give 128 which seems - // likely to be enough for most cases. - env_->PushLocalFrame(128); - env_->ExceptionClear(); - UNUSED(suspend_); - } - - ~ScopedEventDispatchEnvironment() { - if (thr_.get() != nullptr && !env_->ExceptionCheck()) { - // TODO It would be nice to add the overwritten exceptions to the suppressed exceptions list - // of the newest exception. - env_->Throw(thr_.get()); - } - env_->PopLocalFrame(nullptr); - } - - private: - JNIEnv* env_; - ScopedLocalRef thr_; - // Not actually unused. The destructor/constructor does important work. - art::ScopedThreadStateChange suspend_; - - DISALLOW_COPY_AND_ASSIGN(ScopedEventDispatchEnvironment); -}; - template inline void EventHandler::DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const { - for (ArtJvmTiEnv* env : envs) { - if (env != nullptr) { - DispatchEventOnEnv(env, thread, jnienv, args...); - } + art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); + std::vector> events = CollectEvents(thread, + jnienv, + args...); + for (auto event : events) { + ExecuteCallback(event, jnienv, args...); } } @@ -248,8 +294,9 @@ inline void EventHandler::DispatchEventOnEnv( ArtJvmTiEnv* env, art::Thread* thread, JNIEnv* jnienv, Args... args) const { DCHECK(env != nullptr); if (ShouldDispatch(env, thread, jnienv, args...)) { - ScopedEventDispatchEnvironment sede(jnienv); - ExecuteCallback(env, jnienv, args...); + art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); + impl::EventHandlerFunc func(env); + ExecuteCallback(func, jnienv, args...); } } @@ -260,11 +307,26 @@ inline void EventHandler::DispatchEventOnEnv( typename std::decay_t< std::tuple_element_t<0, std::tuple>>>::value, "Should be calling DispatchEventOnEnv with explicit JNIEnv* argument!"); - if (ShouldDispatch(env, thread, args...)) { - ExecuteCallback(env, args...); + DCHECK(env != nullptr); + if (ShouldDispatch(env, thread, args...)) { + art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); + impl::EventHandlerFunc func(env); + ExecuteCallback(func, args...); } } +template +inline void EventHandler::ExecuteCallback(impl::EventHandlerFunc handler, Args... args) { + handler.ExecuteCallback(args...); +} + +template +inline void EventHandler::ExecuteCallback(impl::EventHandlerFunc handler, + JNIEnv* jnienv, + Args... args) { + handler.ExecuteCallback(jnienv, args...); +} + // Events that need custom logic for if we send the event but are otherwise normal. This includes // the kBreakpoint, kFramePop, kFieldAccess, and kFieldModification events. @@ -347,14 +409,13 @@ inline bool EventHandler::ShouldDispatch( // something. template <> inline void EventHandler::ExecuteCallback( - ArtJvmTiEnv* env, + impl::EventHandlerFunc event, JNIEnv* jnienv, jthread jni_thread, jmethodID jmethod, jboolean is_exception, const art::ShadowFrame* frame ATTRIBUTE_UNUSED) { - ExecuteCallback( - env, jnienv, jni_thread, jmethod, is_exception); + ExecuteCallback(event, jnienv, jni_thread, jmethod, is_exception); } // Need to give a custom specialization for NativeMethodBind since it has to deal with an out @@ -366,20 +427,25 @@ inline void EventHandler::DispatchEvent(art::T jmethodID method, void* cur_method, void** new_method) const { + art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); + std::vector> events = + CollectEvents(thread, + jnienv, + jni_thread, + method, + cur_method, + new_method); *new_method = cur_method; - for (ArtJvmTiEnv* env : envs) { - if (env != nullptr) { - *new_method = cur_method; - DispatchEventOnEnv(env, - thread, - jnienv, - jni_thread, - method, - cur_method, - new_method); - if (*new_method != nullptr) { - cur_method = *new_method; - } + for (auto event : events) { + *new_method = cur_method; + ExecuteCallback(event, + jnienv, + jni_thread, + method, + cur_method, + new_method); + if (*new_method != nullptr) { + cur_method = *new_method; } } *new_method = cur_method; @@ -439,7 +505,7 @@ inline void EventHandler::DispatchEvent -inline bool EventHandler::ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) { +inline bool EventHandler::ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) const { bool dispatch = env->event_masks.global_event_mask.Test(kEvent); if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(kEvent)) { @@ -461,6 +527,11 @@ inline bool EventHandler::ShouldDispatch(ArtJvmTiEnv* env, } inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) { + art::MutexLock mu(art::Thread::Current(), envs_lock_); + RecalculateGlobalEventMaskLocked(event); +} + +inline void EventHandler::RecalculateGlobalEventMaskLocked(ArtJvmtiEvent event) { bool union_value = false; for (const ArtJvmTiEnv* stored_env : envs) { if (stored_env == nullptr) { diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index d1d606de48..be4ebbc85e 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -193,25 +193,21 @@ void EventMasks::HandleChangedCapabilities(const jvmtiCapabilities& caps, bool c } void EventHandler::RegisterArtJvmTiEnv(ArtJvmTiEnv* env) { - // Since we never shrink this array we might as well try to fill gaps. - auto it = std::find(envs.begin(), envs.end(), nullptr); - if (it != envs.end()) { - *it = env; - } else { - envs.push_back(env); - } + art::MutexLock mu(art::Thread::Current(), envs_lock_); + envs.push_back(env); } void EventHandler::RemoveArtJvmTiEnv(ArtJvmTiEnv* env) { + art::MutexLock mu(art::Thread::Current(), envs_lock_); // Since we might be currently iterating over the envs list we cannot actually erase elements. // Instead we will simply replace them with 'nullptr' and skip them manually. auto it = std::find(envs.begin(), envs.end(), env); if (it != envs.end()) { - *it = nullptr; + envs.erase(it); for (size_t i = static_cast(ArtJvmtiEvent::kMinEventTypeVal); i <= static_cast(ArtJvmtiEvent::kMaxEventTypeVal); ++i) { - RecalculateGlobalEventMask(static_cast(i)); + RecalculateGlobalEventMaskLocked(static_cast(i)); } } } @@ -431,11 +427,11 @@ class JvmtiGcPauseListener : public art::gc::GcPauseListener { finish_enabled_(false) {} void StartPause() OVERRIDE { - handler_->DispatchEvent(nullptr); + handler_->DispatchEvent(art::Thread::Current()); } void EndPause() OVERRIDE { - handler_->DispatchEvent(nullptr); + handler_->DispatchEvent(art::Thread::Current()); } bool IsEnabled() { @@ -1176,7 +1172,8 @@ void EventHandler::Shutdown() { art::Runtime::Current()->GetInstrumentation()->RemoveListener(method_trace_listener_.get(), ~0); } -EventHandler::EventHandler() { +EventHandler::EventHandler() : envs_lock_("JVMTI Environment List Lock", + art::LockLevel::kTopLockLevel) { alloc_listener_.reset(new JvmtiAllocationListener(this)); ddm_listener_.reset(new JvmtiDdmChunkListener(this)); gc_pause_listener_.reset(new JvmtiGcPauseListener(this)); diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h index a99ed7b212..c73215f07b 100644 --- a/openjdkjvmti/events.h +++ b/openjdkjvmti/events.h @@ -158,6 +158,10 @@ struct EventMasks { void HandleChangedCapabilities(const jvmtiCapabilities& caps, bool caps_added); }; +namespace impl { +template struct EventHandlerFunc { }; +} // namespace impl + // Helper class for event handling. class EventHandler { public: @@ -169,10 +173,10 @@ class EventHandler { // Register an env. It is assumed that this happens on env creation, that is, no events are // enabled, yet. - void RegisterArtJvmTiEnv(ArtJvmTiEnv* env); + void RegisterArtJvmTiEnv(ArtJvmTiEnv* env) REQUIRES(!envs_lock_); // Remove an env. - void RemoveArtJvmTiEnv(ArtJvmTiEnv* env); + void RemoveArtJvmTiEnv(ArtJvmTiEnv* env) REQUIRES(!envs_lock_); bool IsEventEnabledAnywhere(ArtJvmtiEvent event) const { if (!EventMask::EventIsInRange(event)) { @@ -184,13 +188,15 @@ class EventHandler { jvmtiError SetEvent(ArtJvmTiEnv* env, art::Thread* thread, ArtJvmtiEvent event, - jvmtiEventMode mode); + jvmtiEventMode mode) + REQUIRES(!envs_lock_); // Dispatch event to all registered environments. Since this one doesn't have a JNIEnv* it doesn't // matter if it has the mutator_lock. template ALWAYS_INLINE - inline void DispatchEvent(art::Thread* thread, Args... args) const; + inline void DispatchEvent(art::Thread* thread, Args... args) const + REQUIRES(!envs_lock_); // Dispatch event to all registered environments stashing exceptions as needed. This works since // JNIEnv* is always the second argument if it is passed to an event. Needed since C++ does not @@ -200,7 +206,8 @@ class EventHandler { // the event to allocate local references. template ALWAYS_INLINE - inline void DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const; + inline void DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const + REQUIRES(!envs_lock_); // Tell the event handler capabilities were added/lost so it can adjust the sent events.If // caps_added is true then caps is all the newly set capabilities of the jvmtiEnv. If it is false @@ -208,28 +215,48 @@ class EventHandler { ALWAYS_INLINE inline void HandleChangedCapabilities(ArtJvmTiEnv* env, const jvmtiCapabilities& caps, - bool added); + bool added) + REQUIRES(!envs_lock_); // Dispatch event to the given environment, only. template ALWAYS_INLINE - inline void DispatchEventOnEnv( - ArtJvmTiEnv* env, art::Thread* thread, JNIEnv* jnienv, Args... args) const; + inline void DispatchEventOnEnv(ArtJvmTiEnv* env, + art::Thread* thread, + JNIEnv* jnienv, + Args... args) const + REQUIRES(!envs_lock_); // Dispatch event to the given environment, only. template ALWAYS_INLINE - inline void DispatchEventOnEnv(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const; + inline void DispatchEventOnEnv(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const + REQUIRES(!envs_lock_); private: + template + ALWAYS_INLINE + inline std::vector> CollectEvents(art::Thread* thread, + Args... args) const + REQUIRES(!envs_lock_); + template ALWAYS_INLINE - static inline bool ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread); + inline bool ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) const; template ALWAYS_INLINE - static inline void ExecuteCallback(ArtJvmTiEnv* env, Args... args); + static inline void ExecuteCallback(impl::EventHandlerFunc handler, + JNIEnv* env, + Args... args) + REQUIRES(!envs_lock_); + template + ALWAYS_INLINE + static inline void ExecuteCallback(impl::EventHandlerFunc handler, Args... args) + REQUIRES(!envs_lock_); + + // Public for use to collect dispatches template ALWAYS_INLINE inline bool ShouldDispatch(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const; @@ -241,7 +268,9 @@ class EventHandler { // Recalculates the event mask for the given event. ALWAYS_INLINE - inline void RecalculateGlobalEventMask(ArtJvmtiEvent event); + inline void RecalculateGlobalEventMask(ArtJvmtiEvent event) REQUIRES(!envs_lock_); + ALWAYS_INLINE + inline void RecalculateGlobalEventMaskLocked(ArtJvmtiEvent event) REQUIRES(envs_lock_); template ALWAYS_INLINE inline void DispatchClassFileLoadHookEvent(art::Thread* thread, @@ -253,7 +282,8 @@ class EventHandler { jint class_data_len, const unsigned char* class_data, jint* new_class_data_len, - unsigned char** new_class_data) const; + unsigned char** new_class_data) const + REQUIRES(!envs_lock_); void HandleEventType(ArtJvmtiEvent event, bool enable); void HandleLocalAccessCapabilityAdded(); @@ -261,10 +291,13 @@ class EventHandler { bool OtherMonitorEventsEnabledAnywhere(ArtJvmtiEvent event); - // List of all JvmTiEnv objects that have been created, in their creation order. - // NB Some elements might be null representing envs that have been deleted. They should be skipped - // anytime this list is used. - std::vector envs; + // List of all JvmTiEnv objects that have been created, in their creation order. It is a std::list + // since we mostly access it by iterating over the entire thing, only ever append to the end, and + // need to be able to remove arbitrary elements from it. + std::list envs GUARDED_BY(envs_lock_); + + // Top level lock. Nothing at all should be held when we lock this. + mutable art::Mutex envs_lock_ ACQUIRED_BEFORE(art::Locks::instrument_entrypoints_lock_); // A union of all enabled events, anywhere. EventMask global_mask; diff --git a/openjdkjvmti/object_tagging.cc b/openjdkjvmti/object_tagging.cc index 6ba7165577..ba242ef1e8 100644 --- a/openjdkjvmti/object_tagging.cc +++ b/openjdkjvmti/object_tagging.cc @@ -61,7 +61,8 @@ bool ObjectTagTable::DoesHandleNullOnSweep() { return event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kObjectFree); } void ObjectTagTable::HandleNullSweep(jlong tag) { - event_handler_->DispatchEventOnEnv(jvmti_env_, nullptr, tag); + event_handler_->DispatchEventOnEnv( + jvmti_env_, art::Thread::Current(), tag); } } // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_dump.cc b/openjdkjvmti/ti_dump.cc index 809a5e47bb..253580e0e1 100644 --- a/openjdkjvmti/ti_dump.cc +++ b/openjdkjvmti/ti_dump.cc @@ -47,7 +47,7 @@ struct DumpCallback : public art::RuntimeSigQuitCallback { void SigQuit() OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { art::Thread* thread = art::Thread::Current(); art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative); - event_handler->DispatchEvent(nullptr); + event_handler->DispatchEvent(art::Thread::Current()); } EventHandler* event_handler = nullptr; diff --git a/openjdkjvmti/ti_phase.cc b/openjdkjvmti/ti_phase.cc index 23df27fbda..7157974c13 100644 --- a/openjdkjvmti/ti_phase.cc +++ b/openjdkjvmti/ti_phase.cc @@ -57,6 +57,7 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { } void NextRuntimePhase(RuntimePhase phase) REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE { + art::Thread* self = art::Thread::Current(); switch (phase) { case RuntimePhase::kInitialAgents: PhaseUtil::current_phase_ = JVMTI_PHASE_PRIMORDIAL; @@ -64,8 +65,7 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { case RuntimePhase::kStart: { PhaseUtil::current_phase_ = JVMTI_PHASE_START; - art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); - event_handler->DispatchEvent(nullptr, GetJniEnv()); + event_handler->DispatchEvent(self, GetJniEnv()); } break; case RuntimePhase::kInit: @@ -74,9 +74,7 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE; { ScopedLocalRef thread(GetJniEnv(), GetCurrentJThread()); - art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); - event_handler->DispatchEvent( - nullptr, GetJniEnv(), thread.get()); + event_handler->DispatchEvent(self, GetJniEnv(), thread.get()); } // We need to have these events be ordered to match behavior expected by some real-world // agents. The spec does not really require this but compatibility is a useful property to @@ -86,8 +84,7 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { break; case RuntimePhase::kDeath: { - art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); - event_handler->DispatchEvent(nullptr, GetJniEnv()); + event_handler->DispatchEvent(self, GetJniEnv()); PhaseUtil::current_phase_ = JVMTI_PHASE_DEAD; } // TODO: Block events now. diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h index c9d48ff7f7..39ad3d0c89 100644 --- a/runtime/base/mutex-inl.h +++ b/runtime/base/mutex-inl.h @@ -80,7 +80,9 @@ static inline void CheckUnattachedThread(LockLevel level) NO_THREAD_SAFETY_ANALY // (see Thread::TransitionFromSuspendedToRunnable). level == kThreadSuspendCountLock || // Avoid recursive death. - level == kAbortLock) << level; + level == kAbortLock || + // Locks at the absolute top of the stack can be locked at any time. + level == kTopLockLevel) << level; } } diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 87c4afe96f..43ea3a2053 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -122,6 +122,11 @@ enum LockLevel { kInstrumentEntrypointsLock, kZygoteCreationLock, + // The highest valid lock level. Use this if there is code that should only be called with no + // other locks held. Since this is the highest lock level we also allow it to be held even if the + // runtime or current thread is not fully set-up yet (for example during thread attach). + kTopLockLevel, + kLockLevelCount // Must come last. }; std::ostream& operator<<(std::ostream& os, const LockLevel& rhs); diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc index 3bf169ad40..548752e980 100644 --- a/runtime/ti/agent.cc +++ b/runtime/ti/agent.cc @@ -21,6 +21,8 @@ #include "base/strlcpy.h" #include "java_vm_ext.h" #include "runtime.h" +#include "thread-current-inl.h" +#include "scoped_thread_state_change-inl.h" namespace art { namespace ti { @@ -35,6 +37,7 @@ const char* AGENT_ON_UNLOAD_FUNCTION_NAME = "Agent_OnUnload"; Agent::LoadError Agent::DoLoadHelper(bool attaching, /*out*/jint* call_res, /*out*/std::string* error_msg) { + ScopedThreadStateChange stsc(Thread::Current(), ThreadState::kNative); DCHECK(call_res != nullptr); DCHECK(error_msg != nullptr); diff --git a/test/1941-dispose-stress/dispose_stress.cc b/test/1941-dispose-stress/dispose_stress.cc new file mode 100644 index 0000000000..e8fcc775e9 --- /dev/null +++ b/test/1941-dispose-stress/dispose_stress.cc @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#include + +#include "android-base/logging.h" +#include "jni.h" +#include "scoped_local_ref.h" +#include "scoped_primitive_array.h" + +#include "jvmti.h" + +// Test infrastructure +#include "jvmti_helper.h" +#include "test_env.h" + +namespace art { +namespace Test1941DisposeStress { + +extern "C" JNIEXPORT jlong JNICALL Java_art_Test1941_AllocEnv(JNIEnv* env, jclass) { + JavaVM* vm = nullptr; + if (env->GetJavaVM(&vm) != 0) { + ScopedLocalRef rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), "Unable to get JavaVM"); + return -1; + } + jvmtiEnv* new_env = nullptr; + if (vm->GetEnv(reinterpret_cast(&new_env), JVMTI_VERSION_1_0) != 0) { + ScopedLocalRef rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), "Unable to create new jvmtiEnv"); + return -1; + } + return static_cast(reinterpret_cast(new_env)); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1941_FreeEnv(JNIEnv* env, + jclass, + jlong jvmti_env_ptr) { + JvmtiErrorToException(env, + jvmti_env, + reinterpret_cast(jvmti_env_ptr)->DisposeEnvironment()); +} + +} // namespace Test1941DisposeStress +} // namespace art + diff --git a/test/1941-dispose-stress/expected.txt b/test/1941-dispose-stress/expected.txt new file mode 100644 index 0000000000..ca2eddc7b8 --- /dev/null +++ b/test/1941-dispose-stress/expected.txt @@ -0,0 +1 @@ +fib(20) is 6765 diff --git a/test/1941-dispose-stress/info.txt b/test/1941-dispose-stress/info.txt new file mode 100644 index 0000000000..e4a584e46f --- /dev/null +++ b/test/1941-dispose-stress/info.txt @@ -0,0 +1,3 @@ +Test basic JVMTI single step functionality. + +Ensures that we can receive single step events from JVMTI. diff --git a/test/1941-dispose-stress/run b/test/1941-dispose-stress/run new file mode 100755 index 0000000000..51875a7e86 --- /dev/null +++ b/test/1941-dispose-stress/run @@ -0,0 +1,18 @@ +#!/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. + +# Ask for stack traces to be dumped to a file rather than to stdout. +./default-run "$@" --jvmti diff --git a/test/1941-dispose-stress/src/Main.java b/test/1941-dispose-stress/src/Main.java new file mode 100644 index 0000000000..2fe6b818a0 --- /dev/null +++ b/test/1941-dispose-stress/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.Test1941.run(); + } +} diff --git a/test/1941-dispose-stress/src/art/Breakpoint.java b/test/1941-dispose-stress/src/art/Breakpoint.java new file mode 100644 index 0000000000..bbb89f707f --- /dev/null +++ b/test/1941-dispose-stress/src/art/Breakpoint.java @@ -0,0 +1,202 @@ +/* + * 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.lang.reflect.Executable; +import java.util.HashSet; +import java.util.Set; +import java.util.Objects; + +public class Breakpoint { + public static class Manager { + public static class BP { + public final Executable method; + public final long location; + + public BP(Executable method) { + this(method, getStartLocation(method)); + } + + public BP(Executable method, long location) { + this.method = method; + this.location = location; + } + + @Override + public boolean equals(Object other) { + return (other instanceof BP) && + method.equals(((BP)other).method) && + location == ((BP)other).location; + } + + @Override + public String toString() { + return method.toString() + " @ " + getLine(); + } + + @Override + public int hashCode() { + return Objects.hash(method, location); + } + + public int getLine() { + try { + LineNumber[] lines = getLineNumberTable(method); + int best = -1; + for (LineNumber l : lines) { + if (l.location > location) { + break; + } else { + best = l.line; + } + } + return best; + } catch (Exception e) { + return -1; + } + } + } + + private Set breaks = new HashSet<>(); + + public void setBreakpoints(BP... bs) { + for (BP b : bs) { + if (breaks.add(b)) { + Breakpoint.setBreakpoint(b.method, b.location); + } + } + } + public void setBreakpoint(Executable method, long location) { + setBreakpoints(new BP(method, location)); + } + + public void clearBreakpoints(BP... bs) { + for (BP b : bs) { + if (breaks.remove(b)) { + Breakpoint.clearBreakpoint(b.method, b.location); + } + } + } + public void clearBreakpoint(Executable method, long location) { + clearBreakpoints(new BP(method, location)); + } + + public void clearAllBreakpoints() { + clearBreakpoints(breaks.toArray(new BP[0])); + } + } + + public static void startBreakpointWatch(Class methodClass, + Executable breakpointReached, + Thread thr) { + startBreakpointWatch(methodClass, breakpointReached, false, thr); + } + + /** + * Enables the trapping of breakpoint events. + * + * If allowRecursive == true then breakpoints will be sent even if one is currently being handled. + */ + public static native void startBreakpointWatch(Class methodClass, + Executable breakpointReached, + boolean allowRecursive, + Thread thr); + public static native void stopBreakpointWatch(Thread thr); + + public static final class LineNumber implements Comparable { + public final long location; + public final int line; + + private LineNumber(long loc, int line) { + this.location = loc; + this.line = line; + } + + public boolean equals(Object other) { + return other instanceof LineNumber && ((LineNumber)other).line == line && + ((LineNumber)other).location == location; + } + + public int compareTo(LineNumber other) { + int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line)); + if (v != 0) { + return v; + } else { + return Long.valueOf(location).compareTo(Long.valueOf(other.location)); + } + } + } + + public static native void setBreakpoint(Executable m, long loc); + public static void setBreakpoint(Executable m, LineNumber l) { + setBreakpoint(m, l.location); + } + + public static native void clearBreakpoint(Executable m, long loc); + public static void clearBreakpoint(Executable m, LineNumber l) { + clearBreakpoint(m, l.location); + } + + private static native Object[] getLineNumberTableNative(Executable m); + public static LineNumber[] getLineNumberTable(Executable m) { + Object[] nativeTable = getLineNumberTableNative(m); + long[] location = (long[])(nativeTable[0]); + int[] lines = (int[])(nativeTable[1]); + if (lines.length != location.length) { + throw new Error("Lines and locations have different lengths!"); + } + LineNumber[] out = new LineNumber[lines.length]; + for (int i = 0; i < lines.length; i++) { + out[i] = new LineNumber(location[i], lines[i]); + } + return out; + } + + public static native long getStartLocation(Executable m); + + public static int locationToLine(Executable m, long location) { + try { + Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m); + int best = -1; + for (Breakpoint.LineNumber l : lines) { + if (l.location > location) { + break; + } else { + best = l.line; + } + } + return best; + } catch (Exception e) { + return -1; + } + } + + public static long lineToLocation(Executable m, int line) throws Exception { + try { + Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m); + for (Breakpoint.LineNumber l : lines) { + if (l.line == line) { + return l.location; + } + } + throw new Exception("Unable to find line " + line + " in " + m); + } catch (Exception e) { + throw new Exception("Unable to get line number info for " + m, e); + } + } +} + diff --git a/test/1941-dispose-stress/src/art/Test1941.java b/test/1941-dispose-stress/src/art/Test1941.java new file mode 100644 index 0000000000..d5a9de6cab --- /dev/null +++ b/test/1941-dispose-stress/src/art/Test1941.java @@ -0,0 +1,72 @@ +/* + * 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.Arrays; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; + +public class Test1941 { + public static final boolean PRINT_CNT = false; + public static long CNT = 0; + + // Method with multiple paths we can break on. + public static long fib(long f) { + if (f < 0) { + throw new IllegalArgumentException("Bad argument f < 0: f = " + f); + } else if (f == 0) { + return 0; + } else if (f == 1) { + return 1; + } else { + return fib(f - 1) + fib(f - 2); + } + } + + public static void notifySingleStep(Thread thr, Executable e, long loc) { + // Don't bother actually doing anything. + } + + public static void LoopAllocFreeEnv() { + while (!Thread.interrupted()) { + CNT++; + long env = AllocEnv(); + FreeEnv(env); + } + } + + public static native long AllocEnv(); + public static native void FreeEnv(long env); + + public static void run() throws Exception { + Thread thr = new Thread(Test1941::LoopAllocFreeEnv, "LoopNative"); + thr.start(); + Trace.enableSingleStepTracing(Test1941.class, + Test1941.class.getDeclaredMethod( + "notifySingleStep", Thread.class, Executable.class, Long.TYPE), + null); + + System.out.println("fib(20) is " + fib(20)); + + thr.interrupt(); + thr.join(); + Trace.disableTracing(null); + if (PRINT_CNT) { + System.out.println("Number of envs created/destroyed: " + CNT); + } + } +} diff --git a/test/1941-dispose-stress/src/art/Trace.java b/test/1941-dispose-stress/src/art/Trace.java new file mode 100644 index 0000000000..8999bb1368 --- /dev/null +++ b/test/1941-dispose-stress/src/art/Trace.java @@ -0,0 +1,68 @@ +/* + * 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.lang.reflect.Field; +import java.lang.reflect.Method; + +public class Trace { + public static native void enableTracing(Class methodClass, + Method entryMethod, + Method exitMethod, + Method fieldAccess, + Method fieldModify, + Method singleStep, + Thread thr); + public static native void disableTracing(Thread thr); + + public static void enableFieldTracing(Class methodClass, + Method fieldAccess, + Method fieldModify, + Thread thr) { + enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr); + } + + public static void enableMethodTracing(Class methodClass, + Method entryMethod, + Method exitMethod, + Thread thr) { + enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr); + } + + public static void enableSingleStepTracing(Class methodClass, + Method singleStep, + Thread thr) { + enableTracing(methodClass, null, null, null, null, singleStep, thr); + } + + public static native void watchFieldAccess(Field f); + public static native void watchFieldModification(Field f); + public static native void watchAllFieldAccesses(); + public static native void watchAllFieldModifications(); + + // the names, arguments, and even line numbers of these functions are embedded in the tests so we + // need to add to the bottom and not modify old ones to maintain compat. + public static native void enableTracing2(Class methodClass, + Method entryMethod, + Method exitMethod, + Method fieldAccess, + Method fieldModify, + Method singleStep, + Method ThreadStart, + Method ThreadEnd, + Thread thr); +} diff --git a/test/Android.bp b/test/Android.bp index ba24119e9c..8f29251907 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -259,6 +259,7 @@ art_cc_defaults { "1932-monitor-events-misc/monitor_misc.cc", "1934-jvmti-signal-thread/signal_threads.cc", "1939-proxy-frames/local_instance.cc", + "1941-dispose-stress/dispose_stress.cc", ], shared_libs: [ "libbase", -- GitLab From ceb06932313e5146c39fc80d6c3911daa10c534c Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Mon, 13 Nov 2017 10:31:17 -0800 Subject: [PATCH 061/226] Recognize countable "break" loops Rationale: A particular break loop is generated by e.g. Kotlin (or it can be expressed in Java as well) if the upper (or lower) bound is inclusive, but a comparison test would be too dangerous. The ART compiler often has better range analysis (e.g. after inlining) to convert such constructs back to countable loops, which are more amenable to optimizations. For instance, we get more than 200% improvement on the KotlinMicroLoops benchmark, while close to 70 loops are recognized in the Kotlin support library itself. Bug: 67601686 Test: test-art-host test-art-target Change-Id: I67e5c832df57e096efe2cf43a8579d9c10ca33e6 --- compiler/optimizing/induction_var_analysis.cc | 240 ++++++++++++- compiler/optimizing/induction_var_analysis.h | 13 + test/669-checker-break/expected.txt | 1 + test/669-checker-break/info.txt | 1 + test/669-checker-break/src/Main.java | 328 ++++++++++++++++++ 5 files changed, 565 insertions(+), 18 deletions(-) create mode 100644 test/669-checker-break/expected.txt create mode 100644 test/669-checker-break/info.txt create mode 100644 test/669-checker-break/src/Main.java diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index ad29ba56ab..d270c6a28e 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -93,6 +93,136 @@ static DataType::Type ImplicitConversion(DataType::Type type) { } } +/** + * Returns true if loop is guarded by "a cmp b" on entry. + */ +static bool IsGuardedBy(HLoopInformation* loop, + IfCondition cmp, + HInstruction* a, + HInstruction* b) { + // Chase back through straightline code to the first potential + // block that has a control dependence. + // guard: if (x) bypass + // | + // entry: straightline code + // | + // preheader + // | + // header + HBasicBlock* guard = loop->GetPreHeader(); + HBasicBlock* entry = loop->GetHeader(); + while (guard->GetPredecessors().size() == 1 && + guard->GetSuccessors().size() == 1) { + entry = guard; + guard = guard->GetSinglePredecessor(); + } + // Find guard. + HInstruction* control = guard->GetLastInstruction(); + if (!control->IsIf()) { + return false; + } + HIf* ifs = control->AsIf(); + HInstruction* if_expr = ifs->InputAt(0); + if (if_expr->IsCondition()) { + IfCondition other_cmp = ifs->IfTrueSuccessor() == entry + ? if_expr->AsCondition()->GetCondition() + : if_expr->AsCondition()->GetOppositeCondition(); + if (if_expr->InputAt(0) == a && if_expr->InputAt(1) == b) { + return cmp == other_cmp; + } else if (if_expr->InputAt(1) == a && if_expr->InputAt(0) == b) { + switch (cmp) { + case kCondLT: return other_cmp == kCondGT; + case kCondLE: return other_cmp == kCondGE; + case kCondGT: return other_cmp == kCondLT; + case kCondGE: return other_cmp == kCondLE; + default: LOG(FATAL) << "unexpected cmp: " << cmp; + } + } + } + return false; +} + +/* Finds first loop header phi use. */ +HInstruction* FindFirstLoopHeaderPhiUse(HLoopInformation* loop, HInstruction* instruction) { + for (const HUseListNode& use : instruction->GetUses()) { + if (use.GetUser()->GetBlock() == loop->GetHeader() && + use.GetUser()->IsPhi() && + use.GetUser()->InputAt(1) == instruction) { + return use.GetUser(); + } + } + return nullptr; +} + +/** + * Relinks the Phi structure after break-loop rewriting. + */ +bool FixOutsideUse(HLoopInformation* loop, + HInstruction* instruction, + HInstruction* replacement, + bool rewrite) { + // Deal with regular uses. + const HUseList& uses = instruction->GetUses(); + for (auto it = uses.begin(), end = uses.end(); it != end; ) { + HInstruction* user = it->GetUser(); + size_t index = it->GetIndex(); + ++it; // increment prior to potential removal + if (user->GetBlock()->GetLoopInformation() != loop) { + if (replacement == nullptr) { + return false; + } else if (rewrite) { + user->ReplaceInput(replacement, index); + } + } + } + // Deal with environment uses. + const HUseList& env_uses = instruction->GetEnvUses(); + for (auto it = env_uses.begin(), end = env_uses.end(); it != end;) { + HEnvironment* user = it->GetUser(); + size_t index = it->GetIndex(); + ++it; // increment prior to potential removal + if (user->GetHolder()->GetBlock()->GetLoopInformation() != loop) { + if (replacement == nullptr) { + return false; + } else if (rewrite) { + user->RemoveAsUserOfInput(index); + user->SetRawEnvAt(index, replacement); + replacement->AddEnvUseAt(user, index); + } + } + } + return true; +} + +/** + * Test and rewrite the loop body of a break-loop. Returns true on success. + */ +bool RewriteBreakLoopBody(HLoopInformation* loop, + HBasicBlock* body, + HInstruction* cond, + HInstruction* index, + HInstruction* upper, + bool rewrite) { + // Deal with Phis. Outside use prohibited, except for index (which gets exit value). + for (HInstructionIterator it(loop->GetHeader()->GetPhis()); !it.Done(); it.Advance()) { + HInstruction* exit_value = it.Current() == index ? upper : nullptr; + if (!FixOutsideUse(loop, it.Current(), exit_value, rewrite)) { + return false; + } + } + // Deal with other statements in header. + for (HInstruction* m = cond->GetPrevious(), *p = nullptr; m && !m->IsSuspendCheck(); m = p) { + p = m->GetPrevious(); + if (rewrite) { + m->MoveBefore(body->GetFirstInstruction(), false); + } + if (!FixOutsideUse(loop, m, FindFirstLoopHeaderPhiUse(loop, m), rewrite)) { + return false; + } + } + return true; +} + // // Class methods. // @@ -754,6 +884,10 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveConversion( return nullptr; } +// +// Loop trip count analysis methods. +// + void HInductionVarAnalysis::VisitControl(HLoopInformation* loop) { HInstruction* control = loop->GetHeader()->GetLastInstruction(); if (control->IsIf()) { @@ -774,15 +908,16 @@ void HInductionVarAnalysis::VisitControl(HLoopInformation* loop) { if (a == nullptr || b == nullptr) { return; // Loop control is not a sequence. } else if (if_true->GetLoopInformation() != loop && if_false->GetLoopInformation() == loop) { - VisitCondition(loop, a, b, type, condition->GetOppositeCondition()); + VisitCondition(loop, if_false, a, b, type, condition->GetOppositeCondition()); } else if (if_true->GetLoopInformation() == loop && if_false->GetLoopInformation() != loop) { - VisitCondition(loop, a, b, type, condition->GetCondition()); + VisitCondition(loop, if_true, a, b, type, condition->GetCondition()); } } } } void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop, + HBasicBlock* body, InductionInfo* a, InductionInfo* b, DataType::Type type, @@ -790,11 +925,11 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop, if (a->induction_class == kInvariant && b->induction_class == kLinear) { // Swap condition if induction is at right-hand-side (e.g. U > i is same as i < U). switch (cmp) { - case kCondLT: VisitCondition(loop, b, a, type, kCondGT); break; - case kCondLE: VisitCondition(loop, b, a, type, kCondGE); break; - case kCondGT: VisitCondition(loop, b, a, type, kCondLT); break; - case kCondGE: VisitCondition(loop, b, a, type, kCondLE); break; - case kCondNE: VisitCondition(loop, b, a, type, kCondNE); break; + case kCondLT: VisitCondition(loop, body, b, a, type, kCondGT); break; + case kCondLE: VisitCondition(loop, body, b, a, type, kCondGE); break; + case kCondGT: VisitCondition(loop, body, b, a, type, kCondLT); break; + case kCondGE: VisitCondition(loop, body, b, a, type, kCondLE); break; + case kCondNE: VisitCondition(loop, body, b, a, type, kCondNE); break; default: break; } } else if (a->induction_class == kLinear && b->induction_class == kInvariant) { @@ -802,24 +937,30 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop, InductionInfo* lower_expr = a->op_b; InductionInfo* upper_expr = b; InductionInfo* stride_expr = a->op_a; - // Constant stride? + // Test for constant stride and integral condition. int64_t stride_value = 0; if (!IsExact(stride_expr, &stride_value)) { - return; + return; // unknown stride + } else if (type != DataType::Type::kInt32 && type != DataType::Type::kInt64) { + return; // not integral } - // Rewrite condition i != U into strict end condition i < U or i > U if this end condition - // is reached exactly (tested by verifying if the loop has a unit stride and the non-strict - // condition would be always taken). + // Since loops with a i != U condition will not be normalized by the method below, first + // try to rewrite a break-loop with terminating condition i != U into an equivalent loop + // with non-strict end condition i <= U or i >= U if such a rewriting is possible and safe. + if (cmp == kCondNE && RewriteBreakLoop(loop, body, stride_value, type)) { + cmp = stride_value > 0 ? kCondLE : kCondGE; + } + // If this rewriting failed, try to rewrite condition i != U into strict end condition i < U + // or i > U if this end condition is reached exactly (tested by verifying if the loop has a + // unit stride and the non-strict condition would be always taken). if (cmp == kCondNE && ((stride_value == +1 && IsTaken(lower_expr, upper_expr, kCondLE)) || (stride_value == -1 && IsTaken(lower_expr, upper_expr, kCondGE)))) { cmp = stride_value > 0 ? kCondLT : kCondGT; } - // Only accept integral condition. A mismatch between the type of condition and the induction - // is only allowed if the, necessarily narrower, induction range fits the narrower control. - if (type != DataType::Type::kInt32 && type != DataType::Type::kInt64) { - return; // not integral - } else if (type != a->type && - !FitsNarrowerControl(lower_expr, upper_expr, stride_value, a->type, cmp)) { + // A mismatch between the type of condition and the induction is only allowed if the, + // necessarily narrower, induction range fits the narrower control. + if (type != a->type && + !FitsNarrowerControl(lower_expr, upper_expr, stride_value, a->type, cmp)) { return; // mismatched type } // Normalize a linear loop control with a nonzero stride: @@ -984,6 +1125,69 @@ bool HInductionVarAnalysis::FitsNarrowerControl(InductionInfo* lower_expr, IsAtMost(upper_expr, &value) && value <= max; } +bool HInductionVarAnalysis::RewriteBreakLoop(HLoopInformation* loop, + HBasicBlock* body, + int64_t stride_value, + DataType::Type type) { + // Only accept unit stride. + if (std::abs(stride_value) != 1) { + return false; + } + // Simple terminating i != U condition, used nowhere else. + HIf* ifs = loop->GetHeader()->GetLastInstruction()->AsIf(); + HInstruction* cond = ifs->InputAt(0); + if (ifs->GetPrevious() != cond || !cond->HasOnlyOneNonEnvironmentUse()) { + return false; + } + int c = LookupInfo(loop, cond->InputAt(0))->induction_class == kLinear ? 0 : 1; + HInstruction* index = cond->InputAt(c); + HInstruction* upper = cond->InputAt(1 - c); + // Safe to rewrite into i <= U? + IfCondition cmp = stride_value > 0 ? kCondLE : kCondGE; + if (!index->IsPhi() || !IsFinite(LookupInfo(loop, upper), stride_value, type, cmp)) { + return false; + } + // Body consists of update to index i only, used nowhere else. + if (body->GetSuccessors().size() != 1 || + body->GetSingleSuccessor() != loop->GetHeader() || + !body->GetPhis().IsEmpty() || + body->GetInstructions().IsEmpty() || + body->GetFirstInstruction() != index->InputAt(1) || + !body->GetFirstInstruction()->HasOnlyOneNonEnvironmentUse() || + !body->GetFirstInstruction()->GetNext()->IsGoto()) { + return false; + } + // Always taken or guarded by enclosing condition. + if (!IsTaken(LookupInfo(loop, index)->op_b, LookupInfo(loop, upper), cmp) && + !IsGuardedBy(loop, cmp, index->InputAt(0), upper)) { + return false; + } + // Test if break-loop body can be written, and do so on success. + if (RewriteBreakLoopBody(loop, body, cond, index, upper, /*rewrite*/ false)) { + RewriteBreakLoopBody(loop, body, cond, index, upper, /*rewrite*/ true); + } else { + return false; + } + // Rewrite condition in HIR. + if (ifs->IfTrueSuccessor() != body) { + cmp = (cmp == kCondLE) ? kCondGT : kCondLT; + } + HInstruction* rep = nullptr; + switch (cmp) { + case kCondLT: rep = new (graph_->GetAllocator()) HLessThan(index, upper); break; + case kCondGT: rep = new (graph_->GetAllocator()) HGreaterThan(index, upper); break; + case kCondLE: rep = new (graph_->GetAllocator()) HLessThanOrEqual(index, upper); break; + case kCondGE: rep = new (graph_->GetAllocator()) HGreaterThanOrEqual(index, upper); break; + default: LOG(FATAL) << cmp; UNREACHABLE(); + } + loop->GetHeader()->ReplaceAndRemoveInstructionWith(cond, rep); + return true; +} + +// +// Helper methods. +// + void HInductionVarAnalysis::AssignInfo(HLoopInformation* loop, HInstruction* instruction, InductionInfo* info) { diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h index 8737b890d9..acad77d35f 100644 --- a/compiler/optimizing/induction_var_analysis.h +++ b/compiler/optimizing/induction_var_analysis.h @@ -195,9 +195,14 @@ class HInductionVarAnalysis : public HOptimization { HInstruction* entry_phi, HTypeConversion* conversion); + // + // Loop trip count analysis methods. + // + // Trip count information. void VisitControl(HLoopInformation* loop); void VisitCondition(HLoopInformation* loop, + HBasicBlock* body, InductionInfo* a, InductionInfo* b, DataType::Type type, @@ -219,6 +224,14 @@ class HInductionVarAnalysis : public HOptimization { int64_t stride_value, DataType::Type type, IfCondition cmp); + bool RewriteBreakLoop(HLoopInformation* loop, + HBasicBlock* body, + int64_t stride_value, + DataType::Type type); + + // + // Helper methods. + // // Assign and lookup. void AssignInfo(HLoopInformation* loop, HInstruction* instruction, InductionInfo* info); diff --git a/test/669-checker-break/expected.txt b/test/669-checker-break/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/669-checker-break/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/669-checker-break/info.txt b/test/669-checker-break/info.txt new file mode 100644 index 0000000000..3408b3b238 --- /dev/null +++ b/test/669-checker-break/info.txt @@ -0,0 +1 @@ +Test optimizations of "break" loops. diff --git a/test/669-checker-break/src/Main.java b/test/669-checker-break/src/Main.java new file mode 100644 index 0000000000..e59061b1aa --- /dev/null +++ b/test/669-checker-break/src/Main.java @@ -0,0 +1,328 @@ +/* + * 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 optimizations of break-loops, i.e. loops that break + * out of a while-true loop when the end condition is satisfied. + * In particular, the tests focus on break-loops that can be + * rewritten into regular countable loops (this may improve certain + * loops generated by the Kotlin compiler for inclusive ranges). + */ +public class Main { + + /// CHECK-START: int Main.breakLoop(int[]) induction_var_analysis (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [{{i\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START: int Main.breakLoop(int[]) induction_var_analysis (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> LessThanOrEqual [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-ARM64: int Main.breakLoop(int[]) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: VecStore [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + static int breakLoop(int[] a) { + int l = 0; + int u = a.length - 1; + int i = l; + if (l <= u) { + while (true) { + a[i] = 1; + if (i == u) break; + i++; + } + } + return i; + } + + /// CHECK-START: int Main.breakLoopDown(int[]) induction_var_analysis (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: <> NullCheck [<>] loop:none + /// CHECK-DAG: <> Phi [{{i\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] 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-START: int Main.breakLoopDown(int[]) induction_var_analysis (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: <> NullCheck [<>] 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: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + static int breakLoopDown(int[] a) { + int l = 0; + int u = a.length - 1; + int i = u; + if (u >= l) { + while (true) { + a[i] = 2; + if (i == l) break; + i--; + } + } + return i; + } + + /// CHECK-START: int Main.breakLoopSafeConst(int[]) induction_var_analysis (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 3 loop:none + /// CHECK-DAG: <> IntConstant 2147483631 loop:none + /// CHECK-DAG: <> IntConstant 2147483646 loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] 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-START: int Main.breakLoopSafeConst(int[]) induction_var_analysis (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 3 loop:none + /// CHECK-DAG: <> IntConstant 2147483631 loop:none + /// CHECK-DAG: <> IntConstant 2147483646 loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> LessThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-ARM64: int Main.breakLoopSafeConst(int[]) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 3 loop:none + /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: VecStore [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + static int breakLoopSafeConst(int[] a) { + int l = Integer.MAX_VALUE - 16; + int u = Integer.MAX_VALUE - 1; + int i = l; + if (l <= u) { // will be removed by simplifier + while (true) { + a[i - l] = 3; + if (i == u) break; + i++; + } + } + return i; + } + + /// CHECK-START: int Main.breakLoopUnsafeConst(int[]) induction_var_analysis (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-DAG: <> IntConstant 2147483632 loop:none + /// CHECK-DAG: <> IntConstant 2147483647 loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] 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-START: int Main.breakLoopUnsafeConst(int[]) induction_var_analysis (after) + /// CHECK-DAG: NotEqual + /// CHECK-NOT: LessThanOrEqual + static int breakLoopUnsafeConst(int[] a) { + int l = Integer.MAX_VALUE - 15; + int u = Integer.MAX_VALUE; + int i = l; + if (l <= u) { // will be removed by simplifier + while (true) { + a[i - l] = 4; + if (i == u) break; // rewriting exit not safe! + i++; + } + } + return i; + } + + /// CHECK-START: int Main.breakLoopNastyPhi(int[]) induction_var_analysis (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 5 loop:none + /// CHECK-DAG: <> IntConstant -123 loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [{{i\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:none + /// CHECK-DAG: Return [<>] loop:none + // + /// CHECK-START: int Main.breakLoopNastyPhi(int[]) induction_var_analysis (after) + /// CHECK-DAG: NotEqual + /// CHECK-NOT: LessThanOrEqual + static int breakLoopNastyPhi(int[] a) { + int l = 0; + int u = a.length - 1; + int x = -123; + if (l <= u) { + int i = l; + while (true) { + a[i] = 5; + if (i == u) break; + x = i; + i++; + } + } + return x; // keep another phi live + } + + /// CHECK-START: int Main.breakLoopReduction(int[]) induction_var_analysis (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [{{i\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:none + /// CHECK-DAG: Return [<>] loop:none + // + /// CHECK-START: int Main.breakLoopReduction(int[]) induction_var_analysis (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> LessThanOrEqual [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] 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: <> Phi [<>,<>] loop:none + /// CHECK-DAG: Return [<>] loop:none + // + /// CHECK-START-ARM64: int Main.breakLoopReduction(int[]) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> VecSetScalars [<>] loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none + /// CHECK-DAG: <> VecAdd [<>,<>] loop:<> outer_loop:none + static int breakLoopReduction(int[] a) { + int l = 0; + int u = a.length - 1; + int x = 0; + if (l <= u) { + int i = l; + while (true) { + x += a[i]; + if (i == u) break; + i++; + } + } + return x; + } + + // + // Test driver. + // + + public static void main(String[] args) { + int[] a = new int[100]; + + expectEquals(99, breakLoop(a)); + for (int i = 0; i < a.length; i++) { + expectEquals(1, a[i]); + } + + expectEquals(0, breakLoopDown(a)); + for (int i = 0; i < a.length; i++) { + expectEquals(2, a[i]); + } + + expectEquals(Integer.MAX_VALUE - 1, breakLoopSafeConst(a)); + for (int i = 0; i < a.length; i++) { + int e = i < 16 ? 3 : 2; + expectEquals(e, a[i]); + } + + expectEquals(Integer.MAX_VALUE, breakLoopUnsafeConst(a)); + for (int i = 0; i < a.length; i++) { + int e = i < 16 ? 4 : 2; + expectEquals(e, a[i]); + } + + expectEquals(98, breakLoopNastyPhi(a)); + for (int i = 0; i < a.length; i++) { + expectEquals(5, a[i]); + } + + expectEquals(500, breakLoopReduction(a)); + + System.out.println("passed"); + } + + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} -- GitLab From 3e0c5170c87b39c88c7f3b5fbd7b07e60547bfd7 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Sun, 12 Nov 2017 12:58:40 -0800 Subject: [PATCH 062/226] Add logic for calculating offsets during writing Delete all the offset related layout logic, move the logic to writing. Layout is now controlled by changing the order of the data vectors. Calculate all offsets during writing instead of reading. This allows more flexible layout optimizations without needing to be as careful about offsets in other sections changing. Maintained logic for using existing offsets in the case where changing the layout is not required. Areas to improve: How to size the memmap, do we want 2 passes? Currently the algorithm reserves 10% extra space in case required. This is sufficient for top 1000 apps in the play store. Will consider a two pass approach later. Bug: 63756964 Bug: 68948395 Bug: 68867570 Bug: 68864106 Bug: 68864168 Test: test/testrunner/testrunner.py --host -j40 Test: test-art-host-gtest Test: Dexlayout speed profile top 1000 apps Change-Id: I7dee869da3a010559547f8cfdf93e9aa4c3f47ff --- dex2oat/dex2oat_test.cc | 16 +- dexlayout/compact_dex_writer.cc | 2 +- dexlayout/compact_dex_writer.h | 9 +- dexlayout/dex_ir.cc | 196 +++++----- dexlayout/dex_ir.h | 236 ++++++++---- dexlayout/dex_ir_builder.cc | 7 +- dexlayout/dex_ir_builder.h | 4 +- dexlayout/dex_writer.cc | 647 +++++++++++++++++++++----------- dexlayout/dex_writer.h | 121 ++++-- dexlayout/dexdiag.cc | 3 +- dexlayout/dexlayout.cc | 389 +++++++------------ dexlayout/dexlayout.h | 28 +- runtime/dex_file_layout.cc | 10 +- runtime/dex_file_layout.h | 40 +- runtime/dex_file_verifier.cc | 4 +- 15 files changed, 1019 insertions(+), 693 deletions(-) diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index bd8583bd41..ad287b0745 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -1332,10 +1332,10 @@ TEST_F(Dex2oatTest, LayoutSections) { code_section.parts_[static_cast(LayoutType::kLayoutTypeUnused)]; // All the sections should be non-empty. - EXPECT_GT(section_hot_code.size_, 0u); - EXPECT_GT(section_sometimes_used.size_, 0u); - EXPECT_GT(section_startup_only.size_, 0u); - EXPECT_GT(section_unused.size_, 0u); + EXPECT_GT(section_hot_code.Size(), 0u); + EXPECT_GT(section_sometimes_used.Size(), 0u); + EXPECT_GT(section_startup_only.Size(), 0u); + EXPECT_GT(section_unused.Size(), 0u); // Open the dex file since we need to peek at the code items to verify the layout matches what // we expect. @@ -1364,18 +1364,18 @@ TEST_F(Dex2oatTest, LayoutSections) { const bool is_post_startup = ContainsElement(post_methods, method_idx); if (is_hot) { // Hot is highest precedence, check that the hot methods are in the hot section. - EXPECT_LT(code_item_offset - section_hot_code.offset_, section_hot_code.size_); + EXPECT_TRUE(section_hot_code.Contains(code_item_offset)); ++hot_count; } else if (is_post_startup) { // Post startup is sometimes used section. - EXPECT_LT(code_item_offset - section_sometimes_used.offset_, section_sometimes_used.size_); + EXPECT_TRUE(section_sometimes_used.Contains(code_item_offset)); ++post_startup_count; } else if (is_startup) { // Startup at this point means not hot or post startup, these must be startup only then. - EXPECT_LT(code_item_offset - section_startup_only.offset_, section_startup_only.size_); + EXPECT_TRUE(section_startup_only.Contains(code_item_offset)); ++startup_count; } else { - if (code_item_offset - section_unused.offset_ < section_unused.size_) { + if (section_unused.Contains(code_item_offset)) { // If no flags are set, the method should be unused ... ++unused_count; } else { diff --git a/dexlayout/compact_dex_writer.cc b/dexlayout/compact_dex_writer.cc index b089c1d4b3..f2d46199a2 100644 --- a/dexlayout/compact_dex_writer.cc +++ b/dexlayout/compact_dex_writer.cc @@ -47,7 +47,7 @@ void CompactDexWriter::WriteHeader() { header.class_defs_off_ = collections.ClassDefsOffset(); header.data_size_ = header_->DataSize(); header.data_off_ = header_->DataOffset(); - Write(reinterpret_cast(&header), sizeof(header), 0u); + UNUSED(Write(reinterpret_cast(&header), sizeof(header), 0u)); } } // namespace art diff --git a/dexlayout/compact_dex_writer.h b/dexlayout/compact_dex_writer.h index 1c77202c9a..e28efab5c1 100644 --- a/dexlayout/compact_dex_writer.h +++ b/dexlayout/compact_dex_writer.h @@ -25,9 +25,12 @@ namespace art { class CompactDexWriter : public DexWriter { public: - CompactDexWriter(dex_ir::Header* header, MemMap* mem_map, CompactDexLevel compact_dex_level) - : DexWriter(header, mem_map), - compact_dex_level_(compact_dex_level) { } + CompactDexWriter(dex_ir::Header* header, + MemMap* mem_map, + DexLayout* dex_layout, + CompactDexLevel compact_dex_level) + : DexWriter(header, mem_map, dex_layout, /*compute_offsets*/ true), + compact_dex_level_(compact_dex_level) {} protected: void WriteHeader() OVERRIDE; diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc index 3edb0a44f2..a8ba950c28 100644 --- a/dexlayout/dex_ir.cc +++ b/dexlayout/dex_ir.cc @@ -186,22 +186,28 @@ static bool GetIdsFromByteCode(Collections& collections, return has_id; } -EncodedValue* Collections::ReadEncodedValue(const uint8_t** data) { +EncodedValue* Collections::ReadEncodedValue(const DexFile& dex_file, const uint8_t** data) { const uint8_t encoded_value = *(*data)++; const uint8_t type = encoded_value & 0x1f; EncodedValue* item = new EncodedValue(type); - ReadEncodedValue(data, type, encoded_value >> 5, item); + ReadEncodedValue(dex_file, data, type, encoded_value >> 5, item); return item; } -EncodedValue* Collections::ReadEncodedValue(const uint8_t** data, uint8_t type, uint8_t length) { +EncodedValue* Collections::ReadEncodedValue(const DexFile& dex_file, + const uint8_t** data, + uint8_t type, + uint8_t length) { EncodedValue* item = new EncodedValue(type); - ReadEncodedValue(data, type, length, item); + ReadEncodedValue(dex_file, data, type, length, item); return item; } -void Collections::ReadEncodedValue( - const uint8_t** data, uint8_t type, uint8_t length, EncodedValue* item) { +void Collections::ReadEncodedValue(const DexFile& dex_file, + const uint8_t** data, + uint8_t type, + uint8_t length, + EncodedValue* item) { switch (type) { case DexFile::kDexAnnotationByte: item->SetByte(static_cast(ReadVarWidth(data, length, false))); @@ -271,12 +277,17 @@ void Collections::ReadEncodedValue( } case DexFile::kDexAnnotationArray: { EncodedValueVector* values = new EncodedValueVector(); + const uint32_t offset = *data - dex_file.Begin(); const uint32_t size = DecodeUnsignedLeb128(data); // Decode all elements. for (uint32_t i = 0; i < size; i++) { - values->push_back(std::unique_ptr(ReadEncodedValue(data))); + values->push_back(std::unique_ptr(ReadEncodedValue(dex_file, data))); } - item->SetEncodedArray(new EncodedArrayItem(values)); + EncodedArrayItem* array_item = new EncodedArrayItem(values); + if (eagerly_assign_offsets_) { + array_item->SetOffset(offset); + } + item->SetEncodedArray(array_item); break; } case DexFile::kDexAnnotationAnnotation: { @@ -287,7 +298,7 @@ void Collections::ReadEncodedValue( for (uint32_t i = 0; i < size; i++) { const uint32_t name_index = DecodeUnsignedLeb128(data); elements->push_back(std::unique_ptr( - new AnnotationElement(GetStringId(name_index), ReadEncodedValue(data)))); + new AnnotationElement(GetStringId(name_index), ReadEncodedValue(dex_file, data)))); } item->SetEncodedAnnotation(new EncodedAnnotation(GetTypeId(type_idx), elements)); break; @@ -305,16 +316,16 @@ void Collections::ReadEncodedValue( void Collections::CreateStringId(const DexFile& dex_file, uint32_t i) { const DexFile::StringId& disk_string_id = dex_file.GetStringId(dex::StringIndex(i)); StringData* string_data = new StringData(dex_file.GetStringData(disk_string_id)); - string_datas_.AddItem(string_data, disk_string_id.string_data_off_); + AddItem(string_datas_map_, string_datas_, string_data, disk_string_id.string_data_off_); StringId* string_id = new StringId(string_data); - string_ids_.AddIndexedItem(string_id, StringIdsOffset() + i * StringId::ItemSize(), i); + AddIndexedItem(string_ids_, string_id, StringIdsOffset() + i * StringId::ItemSize(), i); } void Collections::CreateTypeId(const DexFile& dex_file, uint32_t i) { const DexFile::TypeId& disk_type_id = dex_file.GetTypeId(dex::TypeIndex(i)); TypeId* type_id = new TypeId(GetStringId(disk_type_id.descriptor_idx_.index_)); - type_ids_.AddIndexedItem(type_id, TypeIdsOffset() + i * TypeId::ItemSize(), i); + AddIndexedItem(type_ids_, type_id, TypeIdsOffset() + i * TypeId::ItemSize(), i); } void Collections::CreateProtoId(const DexFile& dex_file, uint32_t i) { @@ -325,7 +336,7 @@ void Collections::CreateProtoId(const DexFile& dex_file, uint32_t i) { ProtoId* proto_id = new ProtoId(GetStringId(disk_proto_id.shorty_idx_.index_), GetTypeId(disk_proto_id.return_type_idx_.index_), parameter_type_list); - proto_ids_.AddIndexedItem(proto_id, ProtoIdsOffset() + i * ProtoId::ItemSize(), i); + AddIndexedItem(proto_ids_, proto_id, ProtoIdsOffset() + i * ProtoId::ItemSize(), i); } void Collections::CreateFieldId(const DexFile& dex_file, uint32_t i) { @@ -333,7 +344,7 @@ void Collections::CreateFieldId(const DexFile& dex_file, uint32_t i) { FieldId* field_id = new FieldId(GetTypeId(disk_field_id.class_idx_.index_), GetTypeId(disk_field_id.type_idx_.index_), GetStringId(disk_field_id.name_idx_.index_)); - field_ids_.AddIndexedItem(field_id, FieldIdsOffset() + i * FieldId::ItemSize(), i); + AddIndexedItem(field_ids_, field_id, FieldIdsOffset() + i * FieldId::ItemSize(), i); } void Collections::CreateMethodId(const DexFile& dex_file, uint32_t i) { @@ -341,7 +352,7 @@ void Collections::CreateMethodId(const DexFile& dex_file, uint32_t i) { MethodId* method_id = new MethodId(GetTypeId(disk_method_id.class_idx_.index_), GetProtoId(disk_method_id.proto_idx_), GetStringId(disk_method_id.name_idx_.index_)); - method_ids_.AddIndexedItem(method_id, MethodIdsOffset() + i * MethodId::ItemSize(), i); + AddIndexedItem(method_ids_, method_id, MethodIdsOffset() + i * MethodId::ItemSize(), i); } void Collections::CreateClassDef(const DexFile& dex_file, uint32_t i) { @@ -365,48 +376,48 @@ void Collections::CreateClassDef(const DexFile& dex_file, uint32_t i) { // Static field initializers. const uint8_t* static_data = dex_file.GetEncodedStaticFieldValuesArray(disk_class_def); EncodedArrayItem* static_values = - CreateEncodedArrayItem(static_data, disk_class_def.static_values_off_); + CreateEncodedArrayItem(dex_file, static_data, disk_class_def.static_values_off_); ClassData* class_data = CreateClassData( dex_file, dex_file.GetClassData(disk_class_def), disk_class_def.class_data_off_); ClassDef* class_def = new ClassDef(class_type, access_flags, superclass, interfaces_type_list, source_file, annotations, static_values, class_data); - class_defs_.AddIndexedItem(class_def, ClassDefsOffset() + i * ClassDef::ItemSize(), i); + AddIndexedItem(class_defs_, class_def, ClassDefsOffset() + i * ClassDef::ItemSize(), i); } TypeList* Collections::CreateTypeList(const DexFile::TypeList* dex_type_list, uint32_t offset) { if (dex_type_list == nullptr) { return nullptr; } - auto found_type_list = TypeLists().find(offset); - if (found_type_list != TypeLists().end()) { - return found_type_list->second.get(); - } - TypeIdVector* type_vector = new TypeIdVector(); - uint32_t size = dex_type_list->Size(); - for (uint32_t index = 0; index < size; ++index) { - type_vector->push_back(GetTypeId(dex_type_list->GetTypeItem(index).type_idx_.index_)); + TypeList* type_list = type_lists_map_.GetExistingObject(offset); + if (type_list == nullptr) { + TypeIdVector* type_vector = new TypeIdVector(); + uint32_t size = dex_type_list->Size(); + for (uint32_t index = 0; index < size; ++index) { + type_vector->push_back(GetTypeId(dex_type_list->GetTypeItem(index).type_idx_.index_)); + } + type_list = new TypeList(type_vector); + AddItem(type_lists_map_, type_lists_, type_list, offset); } - TypeList* new_type_list = new TypeList(type_vector); - type_lists_.AddItem(new_type_list, offset); - return new_type_list; + return type_list; } -EncodedArrayItem* Collections::CreateEncodedArrayItem(const uint8_t* static_data, uint32_t offset) { +EncodedArrayItem* Collections::CreateEncodedArrayItem(const DexFile& dex_file, + const uint8_t* static_data, + uint32_t offset) { if (static_data == nullptr) { return nullptr; } - auto found_encoded_array_item = EncodedArrayItems().find(offset); - if (found_encoded_array_item != EncodedArrayItems().end()) { - return found_encoded_array_item->second.get(); - } - uint32_t size = DecodeUnsignedLeb128(&static_data); - EncodedValueVector* values = new EncodedValueVector(); - for (uint32_t i = 0; i < size; ++i) { - values->push_back(std::unique_ptr(ReadEncodedValue(&static_data))); + EncodedArrayItem* encoded_array_item = encoded_array_items_map_.GetExistingObject(offset); + if (encoded_array_item == nullptr) { + uint32_t size = DecodeUnsignedLeb128(&static_data); + EncodedValueVector* values = new EncodedValueVector(); + for (uint32_t i = 0; i < size; ++i) { + values->push_back(std::unique_ptr(ReadEncodedValue(dex_file, &static_data))); + } + // TODO: Calculate the size of the encoded array. + encoded_array_item = new EncodedArrayItem(values); + AddItem(encoded_array_items_map_, encoded_array_items_, encoded_array_item, offset); } - // TODO: Calculate the size of the encoded array. - EncodedArrayItem* encoded_array_item = new EncodedArrayItem(values); - encoded_array_items_.AddItem(encoded_array_item, offset); return encoded_array_item; } @@ -427,19 +438,16 @@ AnnotationItem* Collections::CreateAnnotationItem(const DexFile& dex_file, const DexFile::AnnotationItem* annotation) { const uint8_t* const start_data = reinterpret_cast(annotation); const uint32_t offset = start_data - dex_file.Begin(); - auto found_annotation_item = AnnotationItems().find(offset); - if (found_annotation_item != AnnotationItems().end()) { - return found_annotation_item->second.get(); + AnnotationItem* annotation_item = annotation_items_map_.GetExistingObject(offset); + if (annotation_item == nullptr) { + uint8_t visibility = annotation->visibility_; + const uint8_t* annotation_data = annotation->annotation_; + std::unique_ptr encoded_value( + ReadEncodedValue(dex_file, &annotation_data, DexFile::kDexAnnotationAnnotation, 0)); + annotation_item = new AnnotationItem(visibility, encoded_value->ReleaseEncodedAnnotation()); + annotation_item->SetSize(annotation_data - start_data); + AddItem(annotation_items_map_, annotation_items_, annotation_item, offset); } - uint8_t visibility = annotation->visibility_; - const uint8_t* annotation_data = annotation->annotation_; - std::unique_ptr encoded_value( - ReadEncodedValue(&annotation_data, DexFile::kDexAnnotationAnnotation, 0)); - AnnotationItem* annotation_item = - new AnnotationItem(visibility, encoded_value->ReleaseEncodedAnnotation()); - annotation_item->SetOffset(offset); - annotation_item->SetSize(annotation_data - start_data); - annotation_items_.AddItem(annotation_item, annotation_item->GetOffset()); return annotation_item; } @@ -449,30 +457,30 @@ AnnotationSetItem* Collections::CreateAnnotationSetItem(const DexFile& dex_file, if (disk_annotations_item == nullptr || (disk_annotations_item->size_ == 0 && offset == 0)) { return nullptr; } - auto found_anno_set_item = AnnotationSetItems().find(offset); - if (found_anno_set_item != AnnotationSetItems().end()) { - return found_anno_set_item->second.get(); - } - std::vector* items = new std::vector(); - for (uint32_t i = 0; i < disk_annotations_item->size_; ++i) { - const DexFile::AnnotationItem* annotation = - dex_file.GetAnnotationItem(disk_annotations_item, i); - if (annotation == nullptr) { - continue; + AnnotationSetItem* annotation_set_item = annotation_set_items_map_.GetExistingObject(offset); + if (annotation_set_item == nullptr) { + std::vector* items = new std::vector(); + for (uint32_t i = 0; i < disk_annotations_item->size_; ++i) { + const DexFile::AnnotationItem* annotation = + dex_file.GetAnnotationItem(disk_annotations_item, i); + if (annotation == nullptr) { + continue; + } + AnnotationItem* annotation_item = CreateAnnotationItem(dex_file, annotation); + items->push_back(annotation_item); } - AnnotationItem* annotation_item = CreateAnnotationItem(dex_file, annotation); - items->push_back(annotation_item); + annotation_set_item = new AnnotationSetItem(items); + AddItem(annotation_set_items_map_, annotation_set_items_, annotation_set_item, offset); } - AnnotationSetItem* annotation_set_item = new AnnotationSetItem(items); - annotation_set_items_.AddItem(annotation_set_item, offset); return annotation_set_item; } AnnotationsDirectoryItem* Collections::CreateAnnotationsDirectoryItem(const DexFile& dex_file, const DexFile::AnnotationsDirectoryItem* disk_annotations_item, uint32_t offset) { - auto found_anno_dir_item = AnnotationsDirectoryItems().find(offset); - if (found_anno_dir_item != AnnotationsDirectoryItems().end()) { - return found_anno_dir_item->second.get(); + AnnotationsDirectoryItem* annotations_directory_item = + annotations_directory_items_map_.GetExistingObject(offset); + if (annotations_directory_item != nullptr) { + return annotations_directory_item; } const DexFile::AnnotationSetItem* class_set_item = dex_file.GetClassAnnotationSet(disk_annotations_item); @@ -527,20 +535,19 @@ AnnotationsDirectoryItem* Collections::CreateAnnotationsDirectoryItem(const DexF } } // TODO: Calculate the size of the annotations directory. - AnnotationsDirectoryItem* annotations_directory_item = new AnnotationsDirectoryItem( +annotations_directory_item = new AnnotationsDirectoryItem( class_annotation, field_annotations, method_annotations, parameter_annotations); - annotations_directory_items_.AddItem(annotations_directory_item, offset); + AddItem(annotations_directory_items_map_, + annotations_directory_items_, + annotations_directory_item, + offset); return annotations_directory_item; } ParameterAnnotation* Collections::GenerateParameterAnnotation( const DexFile& dex_file, MethodId* method_id, const DexFile::AnnotationSetRefList* annotation_set_ref_list, uint32_t offset) { - AnnotationSetRefList* set_ref_list = nullptr; - auto found_set_ref_list = AnnotationSetRefLists().find(offset); - if (found_set_ref_list != AnnotationSetRefLists().end()) { - set_ref_list = found_set_ref_list->second.get(); - } + AnnotationSetRefList* set_ref_list = annotation_set_ref_lists_map_.GetExistingObject(offset); if (set_ref_list == nullptr) { std::vector* annotations = new std::vector(); for (uint32_t i = 0; i < annotation_set_ref_list->size_; ++i) { @@ -550,7 +557,7 @@ ParameterAnnotation* Collections::GenerateParameterAnnotation( annotations->push_back(CreateAnnotationSetItem(dex_file, annotation_set_item, set_offset)); } set_ref_list = new AnnotationSetRefList(annotations); - annotation_set_ref_lists_.AddItem(set_ref_list, offset); + AddItem(annotation_set_ref_lists_map_, annotation_set_ref_lists_, set_ref_list, offset); } return new ParameterAnnotation(method_id, set_ref_list); } @@ -566,13 +573,13 @@ CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(&disk_code_item); DebugInfoItem* debug_info = nullptr; if (debug_info_stream != nullptr) { - debug_info = debug_info_items_.GetExistingObject(disk_code_item.debug_info_off_); + debug_info = debug_info_items_map_.GetExistingObject(disk_code_item.debug_info_off_); if (debug_info == nullptr) { uint32_t debug_info_size = GetDebugInfoStreamSize(debug_info_stream); uint8_t* debug_info_buffer = new uint8_t[debug_info_size]; memcpy(debug_info_buffer, debug_info_stream, debug_info_size); debug_info = new DebugInfoItem(debug_info_size, debug_info_buffer); - debug_info_items_.AddItem(debug_info, disk_code_item.debug_info_off_); + AddItem(debug_info_items_map_, debug_info_items_, debug_info, disk_code_item.debug_info_off_); } } @@ -662,7 +669,7 @@ CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, CodeItem* code_item = new CodeItem( registers_size, ins_size, outs_size, debug_info, insns_size, insns, tries, handler_list); code_item->SetSize(size); - code_items_.AddItem(code_item, offset); + AddItem(code_items_map_, code_items_, code_item, offset); // Add "fixup" references to types, strings, methods, and fields. // This is temporary, as we will probably want more detailed parsing of the // instructions here. @@ -690,7 +697,7 @@ MethodItem* Collections::GenerateMethodItem(const DexFile& dex_file, ClassDataIt MethodId* method_id = GetMethodId(cdii.GetMemberIndex()); uint32_t access_flags = cdii.GetRawMemberAccessFlags(); const DexFile::CodeItem* disk_code_item = cdii.GetMethodCodeItem(); - CodeItem* code_item = code_items_.GetExistingObject(cdii.GetMethodCodeItemOffset()); + CodeItem* code_item = code_items_map_.GetExistingObject(cdii.GetMethodCodeItemOffset()); DebugInfoItem* debug_info = nullptr; if (disk_code_item != nullptr) { if (code_item == nullptr) { @@ -705,7 +712,7 @@ ClassData* Collections::CreateClassData( const DexFile& dex_file, const uint8_t* encoded_data, uint32_t offset) { // Read the fields and methods defined by the class, resolving the circular reference from those // to classes by setting class at the same time. - ClassData* class_data = class_datas_.GetExistingObject(offset); + ClassData* class_data = class_datas_map_.GetExistingObject(offset); if (class_data == nullptr && encoded_data != nullptr) { ClassDataItemIterator cdii(dex_file, encoded_data); // Static fields. @@ -735,7 +742,7 @@ ClassData* Collections::CreateClassData( } class_data = new ClassData(static_fields, instance_fields, direct_methods, virtual_methods); class_data->SetSize(cdii.EndDataPointer() - encoded_data); - class_datas_.AddItem(class_data, offset); + AddItem(class_datas_map_, class_datas_, class_data, offset); } return class_data; } @@ -771,10 +778,10 @@ void Collections::CreateCallSiteId(const DexFile& dex_file, uint32_t i) { const DexFile::CallSiteIdItem& disk_call_site_id = dex_file.GetCallSiteId(i); const uint8_t* disk_call_item_ptr = dex_file.Begin() + disk_call_site_id.data_off_; EncodedArrayItem* call_site_item = - CreateEncodedArrayItem(disk_call_item_ptr, disk_call_site_id.data_off_); + CreateEncodedArrayItem(dex_file, disk_call_item_ptr, disk_call_site_id.data_off_); CallSiteId* call_site_id = new CallSiteId(call_site_item); - call_site_ids_.AddIndexedItem(call_site_id, CallSiteIdsOffset() + i * CallSiteId::ItemSize(), i); + AddIndexedItem(call_site_ids_, call_site_id, CallSiteIdsOffset() + i * CallSiteId::ItemSize(), i); } void Collections::CreateMethodHandleItem(const DexFile& dex_file, uint32_t i) { @@ -796,8 +803,23 @@ void Collections::CreateMethodHandleItem(const DexFile& dex_file, uint32_t i) { field_or_method_id = GetFieldId(index); } MethodHandleItem* method_handle = new MethodHandleItem(type, field_or_method_id); - method_handle_items_.AddIndexedItem( - method_handle, MethodHandleItemsOffset() + i * MethodHandleItem::ItemSize(), i); + AddIndexedItem(method_handle_items_, + method_handle, + MethodHandleItemsOffset() + i * MethodHandleItem::ItemSize(), + i); +} + +void Collections::SortVectorsByMapOrder() { + string_datas_map_.SortVectorByMapOrder(string_datas_); + type_lists_map_.SortVectorByMapOrder(type_lists_); + encoded_array_items_map_.SortVectorByMapOrder(encoded_array_items_); + annotation_items_map_.SortVectorByMapOrder(annotation_items_); + annotation_set_items_map_.SortVectorByMapOrder(annotation_set_items_); + annotation_set_ref_lists_map_.SortVectorByMapOrder(annotation_set_ref_lists_); + annotations_directory_items_map_.SortVectorByMapOrder(annotations_directory_items_); + debug_info_items_map_.SortVectorByMapOrder(debug_info_items_); + code_items_map_.SortVectorByMapOrder(code_items_); + class_datas_map_.SortVectorByMapOrder(class_datas_); } static uint32_t HeaderOffset(const dex_ir::Collections& collections ATTRIBUTE_UNUSED) { diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h index 179d3b96e0..61a4eae9b1 100644 --- a/dexlayout/dex_ir.h +++ b/dexlayout/dex_ir.h @@ -112,33 +112,55 @@ template class CollectionBase { public: CollectionBase() = default; - uint32_t GetOffset() const { return offset_; } - void SetOffset(uint32_t new_offset) { offset_ = new_offset; } + uint32_t GetOffset() const { + return offset_; + } + void SetOffset(uint32_t new_offset) { + offset_ = new_offset; + } private: - uint32_t offset_ = 0; + // Start out unassigned. + uint32_t offset_ = 0u; DISALLOW_COPY_AND_ASSIGN(CollectionBase); }; template class CollectionVector : public CollectionBase { public: + using Vector = std::vector>; CollectionVector() = default; - void AddIndexedItem(T* object, uint32_t offset, uint32_t index) { - object->SetOffset(offset); - object->SetIndex(index); + uint32_t Size() const { return collection_.size(); } + Vector& Collection() { return collection_; } + + protected: + Vector collection_; + + void AddItem(T* object) { collection_.push_back(std::unique_ptr(object)); } - uint32_t Size() const { return collection_.size(); } - std::vector>& Collection() { return collection_; } private: - std::vector> collection_; - + friend class Collections; DISALLOW_COPY_AND_ASSIGN(CollectionVector); }; +template class IndexedCollectionVector : public CollectionVector { + public: + using Vector = std::vector>; + IndexedCollectionVector() = default; + + private: + void AddIndexedItem(T* object, uint32_t index) { + object->SetIndex(index); + CollectionVector::collection_.push_back(std::unique_ptr(object)); + } + + friend class Collections; + DISALLOW_COPY_AND_ASSIGN(IndexedCollectionVector); +}; + template class CollectionMap : public CollectionBase { public: CollectionMap() = default; @@ -146,21 +168,35 @@ template class CollectionMap : public CollectionBase { // Returns the existing item if it is already inserted, null otherwise. T* GetExistingObject(uint32_t offset) { auto it = collection_.find(offset); - return it != collection_.end() ? it->second.get() : nullptr; + return it != collection_.end() ? it->second : nullptr; } - void AddItem(T* object, uint32_t offset) { - object->SetOffset(offset); - auto it = collection_.emplace(offset, std::unique_ptr(object)); - CHECK(it.second) << "CollectionMap already has an object with offset " << offset << " " - << " and address " << it.first->second.get(); - } uint32_t Size() const { return collection_.size(); } - std::map>& Collection() { return collection_; } + std::map& Collection() { return collection_; } + + // Sort the vector by copying pointers over. + void SortVectorByMapOrder(CollectionVector& vector) { + auto it = collection_.begin(); + CHECK_EQ(vector.Size(), Size()); + for (size_t i = 0; i < Size(); ++i) { + // There are times when the array will temporarily contain the same pointer twice, doing the + // release here sure there is no double free errors. + vector.Collection()[i].release(); + vector.Collection()[i].reset(it->second); + ++it; + } + } private: - std::map> collection_; + std::map collection_; + void AddItem(T* object, uint32_t offset) { + auto it = collection_.emplace(offset, object); + CHECK(it.second) << "CollectionMap already has an object with offset " << offset << " " + << " and address " << it.first->second; + } + + friend class Collections; DISALLOW_COPY_AND_ASSIGN(CollectionMap); }; @@ -168,32 +204,31 @@ class Collections { public: Collections() = default; - std::vector>& StringIds() { return string_ids_.Collection(); } - std::vector>& TypeIds() { return type_ids_.Collection(); } - std::vector>& ProtoIds() { return proto_ids_.Collection(); } - std::vector>& FieldIds() { return field_ids_.Collection(); } - std::vector>& MethodIds() { return method_ids_.Collection(); } - std::vector>& ClassDefs() { return class_defs_.Collection(); } - std::vector>& CallSiteIds() { return call_site_ids_.Collection(); } - std::vector>& MethodHandleItems() + CollectionVector::Vector& StringIds() { return string_ids_.Collection(); } + CollectionVector::Vector& TypeIds() { return type_ids_.Collection(); } + CollectionVector::Vector& ProtoIds() { return proto_ids_.Collection(); } + CollectionVector::Vector& FieldIds() { return field_ids_.Collection(); } + CollectionVector::Vector& MethodIds() { return method_ids_.Collection(); } + CollectionVector::Vector& ClassDefs() { return class_defs_.Collection(); } + CollectionVector::Vector& CallSiteIds() { return call_site_ids_.Collection(); } + CollectionVector::Vector& MethodHandleItems() { return method_handle_items_.Collection(); } - std::map>& StringDatas() - { return string_datas_.Collection(); } - std::map>& TypeLists() { return type_lists_.Collection(); } - std::map>& EncodedArrayItems() + CollectionVector::Vector& StringDatas() { return string_datas_.Collection(); } + CollectionVector::Vector& TypeLists() { return type_lists_.Collection(); } + CollectionVector::Vector& EncodedArrayItems() { return encoded_array_items_.Collection(); } - std::map>& AnnotationItems() + CollectionVector::Vector& AnnotationItems() { return annotation_items_.Collection(); } - std::map>& AnnotationSetItems() + CollectionVector::Vector& AnnotationSetItems() { return annotation_set_items_.Collection(); } - std::map>& AnnotationSetRefLists() + CollectionVector::Vector& AnnotationSetRefLists() { return annotation_set_ref_lists_.Collection(); } - std::map>& AnnotationsDirectoryItems() + CollectionVector::Vector& AnnotationsDirectoryItems() { return annotations_directory_items_.Collection(); } - std::map>& DebugInfoItems() + CollectionVector::Vector& DebugInfoItems() { return debug_info_items_.Collection(); } - std::map>& CodeItems() { return code_items_.Collection(); } - std::map>& ClassDatas() { return class_datas_.Collection(); } + CollectionVector::Vector& CodeItems() { return code_items_.Collection(); } + CollectionVector::Vector& ClassDatas() { return class_datas_.Collection(); } void CreateStringId(const DexFile& dex_file, uint32_t i); void CreateTypeId(const DexFile& dex_file, uint32_t i); @@ -207,7 +242,9 @@ class Collections { void CreateCallSitesAndMethodHandles(const DexFile& dex_file); TypeList* CreateTypeList(const DexFile::TypeList* type_list, uint32_t offset); - EncodedArrayItem* CreateEncodedArrayItem(const uint8_t* static_data, uint32_t offset); + EncodedArrayItem* CreateEncodedArrayItem(const DexFile& dex_file, + const uint8_t* static_data, + uint32_t offset); AnnotationItem* CreateAnnotationItem(const DexFile& dex_file, const DexFile::AnnotationItem* annotation); AnnotationSetItem* CreateAnnotationSetItem(const DexFile& dex_file, @@ -326,37 +363,99 @@ class Collections { uint32_t CodeItemsSize() const { return code_items_.Size(); } uint32_t ClassDatasSize() const { return class_datas_.Size(); } + // Sort the vectors buy map order (same order that was used in the input file). + void SortVectorsByMapOrder(); + + template + void AddItem(CollectionMap& map, + CollectionVector& vector, + Type* item, + uint32_t offset) { + DCHECK(!map.GetExistingObject(offset)); + DCHECK(!item->OffsetAssigned()); + if (eagerly_assign_offsets_) { + item->SetOffset(offset); + } + map.AddItem(item, offset); + vector.AddItem(item); + } + + template + void AddIndexedItem(IndexedCollectionVector& vector, + Type* item, + uint32_t offset, + uint32_t index) { + DCHECK(!item->OffsetAssigned()); + if (eagerly_assign_offsets_) { + item->SetOffset(offset); + } + vector.AddIndexedItem(item, index); + } + + void SetEagerlyAssignOffsets(bool eagerly_assign_offsets) { + eagerly_assign_offsets_ = eagerly_assign_offsets; + } + private: - EncodedValue* ReadEncodedValue(const uint8_t** data); - EncodedValue* ReadEncodedValue(const uint8_t** data, uint8_t type, uint8_t length); - void ReadEncodedValue(const uint8_t** data, uint8_t type, uint8_t length, EncodedValue* item); + EncodedValue* ReadEncodedValue(const DexFile& dex_file, const uint8_t** data); + EncodedValue* ReadEncodedValue(const DexFile& dex_file, + const uint8_t** data, + uint8_t type, + uint8_t length); + void ReadEncodedValue(const DexFile& dex_file, + const uint8_t** data, + uint8_t type, + uint8_t length, + EncodedValue* item); ParameterAnnotation* GenerateParameterAnnotation(const DexFile& dex_file, MethodId* method_id, const DexFile::AnnotationSetRefList* annotation_set_ref_list, uint32_t offset); MethodItem* GenerateMethodItem(const DexFile& dex_file, ClassDataItemIterator& cdii); - CollectionVector string_ids_; - CollectionVector type_ids_; - CollectionVector proto_ids_; - CollectionVector field_ids_; - CollectionVector method_ids_; - CollectionVector class_defs_; - CollectionVector call_site_ids_; - CollectionVector method_handle_items_; - - CollectionMap string_datas_; - CollectionMap type_lists_; - CollectionMap encoded_array_items_; - CollectionMap annotation_items_; - CollectionMap annotation_set_items_; - CollectionMap annotation_set_ref_lists_; - CollectionMap annotations_directory_items_; - CollectionMap debug_info_items_; - CollectionMap code_items_; - CollectionMap class_datas_; + // Collection vectors own the IR data. + IndexedCollectionVector string_ids_; + IndexedCollectionVector type_ids_; + IndexedCollectionVector proto_ids_; + IndexedCollectionVector field_ids_; + IndexedCollectionVector method_ids_; + IndexedCollectionVector call_site_ids_; + IndexedCollectionVector method_handle_items_; + IndexedCollectionVector string_datas_; + IndexedCollectionVector type_lists_; + IndexedCollectionVector encoded_array_items_; + IndexedCollectionVector annotation_items_; + IndexedCollectionVector annotation_set_items_; + IndexedCollectionVector annotation_set_ref_lists_; + IndexedCollectionVector annotations_directory_items_; + IndexedCollectionVector class_defs_; + // The order of the vectors controls the layout of the output file by index order, to change the + // layout just sort the vector. Note that you may only change the order of the non indexed vectors + // below. Indexed vectors are accessed by indices in other places, changing the sorting order will + // invalidate the existing indices and is not currently supported. + CollectionVector debug_info_items_; + CollectionVector code_items_; + CollectionVector class_datas_; + + // Note that the maps do not have ownership, the vectors do. + // TODO: These maps should only be required for building the IR and should be put in a separate + // IR builder class. + CollectionMap string_datas_map_; + CollectionMap type_lists_map_; + CollectionMap encoded_array_items_map_; + CollectionMap annotation_items_map_; + CollectionMap annotation_set_items_map_; + CollectionMap annotation_set_ref_lists_map_; + CollectionMap annotations_directory_items_map_; + CollectionMap debug_info_items_map_; + CollectionMap code_items_map_; + CollectionMap class_datas_map_; uint32_t map_list_offset_ = 0; + // If we eagerly assign offsets during IR building or later after layout. Must be false if + // changing the layout is enabled. + bool eagerly_assign_offsets_; + DISALLOW_COPY_AND_ASSIGN(Collections); }; @@ -365,15 +464,26 @@ class Item { Item() { } virtual ~Item() { } - uint32_t GetOffset() const { return offset_; } + // Return the assigned offset. + uint32_t GetOffset() const { + CHECK(OffsetAssigned()); + return offset_; + } uint32_t GetSize() const { return size_; } void SetOffset(uint32_t offset) { offset_ = offset; } void SetSize(uint32_t size) { size_ = size; } + bool OffsetAssigned() const { + return offset_ != kOffsetUnassigned; + } protected: Item(uint32_t offset, uint32_t size) : offset_(offset), size_(size) { } - uint32_t offset_ = 0; + // 0 is the dex file header and shouldn't be a valid offset for any part of the dex file. + static constexpr uint32_t kOffsetUnassigned = 0u; + + // Start out unassigned. + uint32_t offset_ = kOffsetUnassigned; uint32_t size_ = 0; }; diff --git a/dexlayout/dex_ir_builder.cc b/dexlayout/dex_ir_builder.cc index bd3e1fa718..924dfe076a 100644 --- a/dexlayout/dex_ir_builder.cc +++ b/dexlayout/dex_ir_builder.cc @@ -26,7 +26,7 @@ namespace dex_ir { static void CheckAndSetRemainingOffsets(const DexFile& dex_file, Collections* collections); -Header* DexIrBuilder(const DexFile& dex_file) { +Header* DexIrBuilder(const DexFile& dex_file, bool eagerly_assign_offsets) { const DexFile::Header& disk_header = dex_file.GetHeader(); Header* header = new Header(disk_header.magic_, disk_header.checksum_, @@ -39,6 +39,7 @@ Header* DexIrBuilder(const DexFile& dex_file) { disk_header.data_size_, disk_header.data_off_); Collections& collections = header->GetCollections(); + collections.SetEagerlyAssignOffsets(eagerly_assign_offsets); // Walk the rest of the header fields. // StringId table. collections.SetStringIdsOffset(disk_header.string_ids_off_); @@ -74,9 +75,11 @@ Header* DexIrBuilder(const DexFile& dex_file) { collections.SetMapListOffset(disk_header.map_off_); // CallSiteIds and MethodHandleItems. collections.CreateCallSitesAndMethodHandles(dex_file); - CheckAndSetRemainingOffsets(dex_file, &collections); + // Sort the vectors by the map order (same order as the file). + collections.SortVectorsByMapOrder(); + return header; } diff --git a/dexlayout/dex_ir_builder.h b/dexlayout/dex_ir_builder.h index c53157b5fc..4d4b4e8699 100644 --- a/dexlayout/dex_ir_builder.h +++ b/dexlayout/dex_ir_builder.h @@ -24,7 +24,9 @@ namespace art { namespace dex_ir { -dex_ir::Header* DexIrBuilder(const DexFile& dex_file); +// Eagerly assign offsets assigns offsets based on the original offsets in the input dex file. If +// this not done, dex_ir::Item::GetOffset will abort when reading uninitialized offsets. +dex_ir::Header* DexIrBuilder(const DexFile& dex_file, bool eagerly_assign_offsets); } // namespace dex_ir } // namespace art diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc index 4895ab6957..c85bca0d92 100644 --- a/dexlayout/dex_writer.cc +++ b/dexlayout/dex_writer.cc @@ -18,17 +18,37 @@ #include -#include #include #include "cdex/compact_dex_file.h" #include "compact_dex_writer.h" +#include "dex_file_layout.h" #include "dex_file_types.h" +#include "dexlayout.h" #include "standard_dex_file.h" #include "utf.h" namespace art { +static constexpr uint32_t kDataSectionAlignment = sizeof(uint32_t) * 2; +static constexpr uint32_t kDexSectionWordAlignment = 4; + +static constexpr uint32_t SectionAlignment(DexFile::MapItemType type) { + switch (type) { + case DexFile::kDexTypeClassDataItem: + case DexFile::kDexTypeStringDataItem: + case DexFile::kDexTypeDebugInfoItem: + case DexFile::kDexTypeAnnotationItem: + case DexFile::kDexTypeEncodedArrayItem: + return alignof(uint8_t); + + default: + // All other sections are kDexAlignedSection. + return kDexSectionWordAlignment; + } +} + + size_t EncodeIntValue(int32_t value, uint8_t* buffer) { size_t length = 0; if (value >= 0) { @@ -245,130 +265,213 @@ size_t DexWriter::WriteEncodedMethods(dex_ir::MethodItemVector* methods, size_t return offset - original_offset; } -void DexWriter::WriteStrings() { - uint32_t string_data_off[1]; +// TODO: Refactor this to remove duplicated boiler plate. One way to do this is adding +// function that takes a CollectionVector and uses overloading. +uint32_t DexWriter::WriteStringIds(uint32_t offset, bool reserve_only) { + const uint32_t start = offset; for (std::unique_ptr& string_id : header_->GetCollections().StringIds()) { - string_data_off[0] = string_id->DataItem()->GetOffset(); - Write(string_data_off, string_id->GetSize(), string_id->GetOffset()); + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeStringIdItem)); + if (reserve_only) { + offset += string_id->GetSize(); + } else { + uint32_t string_data_off = string_id->DataItem()->GetOffset(); + offset += Write(&string_data_off, string_id->GetSize(), offset); + } } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetStringIdsOffset(start); + } + return offset - start; +} - for (auto& string_data_pair : header_->GetCollections().StringDatas()) { - std::unique_ptr& string_data = string_data_pair.second; - uint32_t offset = string_data->GetOffset(); +uint32_t DexWriter::WriteStringDatas(uint32_t offset) { + const uint32_t start = offset; + for (std::unique_ptr& string_data : header_->GetCollections().StringDatas()) { + ProcessOffset(&offset, string_data.get()); + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeStringDataItem)); offset += WriteUleb128(CountModifiedUtf8Chars(string_data->Data()), offset); - Write(string_data->Data(), strlen(string_data->Data()), offset); + // Skip null terminator (already zeroed out, no need to write). + offset += Write(string_data->Data(), strlen(string_data->Data()), offset) + 1u; + } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetStringDatasOffset(start); } + return offset - start; } -void DexWriter::WriteTypes() { +uint32_t DexWriter::WriteTypeIds(uint32_t offset) { uint32_t descriptor_idx[1]; + const uint32_t start = offset; for (std::unique_ptr& type_id : header_->GetCollections().TypeIds()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeTypeIdItem)); + ProcessOffset(&offset, type_id.get()); descriptor_idx[0] = type_id->GetStringId()->GetIndex(); - Write(descriptor_idx, type_id->GetSize(), type_id->GetOffset()); + offset += Write(descriptor_idx, type_id->GetSize(), offset); } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetTypeIdsOffset(start); + } + return offset - start; } -void DexWriter::WriteTypeLists() { +uint32_t DexWriter::WriteTypeLists(uint32_t offset) { uint32_t size[1]; uint16_t list[1]; - for (auto& type_list_pair : header_->GetCollections().TypeLists()) { - std::unique_ptr& type_list = type_list_pair.second; + const uint32_t start = offset; + for (std::unique_ptr& type_list : header_->GetCollections().TypeLists()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeTypeList)); size[0] = type_list->GetTypeList()->size(); - uint32_t offset = type_list->GetOffset(); + ProcessOffset(&offset, type_list.get()); offset += Write(size, sizeof(uint32_t), offset); for (const dex_ir::TypeId* type_id : *type_list->GetTypeList()) { list[0] = type_id->GetIndex(); offset += Write(list, sizeof(uint16_t), offset); } } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetTypeListsOffset(start); + } + return offset - start; } -void DexWriter::WriteProtos() { +uint32_t DexWriter::WriteProtoIds(uint32_t offset, bool reserve_only) { uint32_t buffer[3]; + const uint32_t start = offset; for (std::unique_ptr& proto_id : header_->GetCollections().ProtoIds()) { - buffer[0] = proto_id->Shorty()->GetIndex(); - buffer[1] = proto_id->ReturnType()->GetIndex(); - buffer[2] = proto_id->Parameters() == nullptr ? 0 : proto_id->Parameters()->GetOffset(); - Write(buffer, proto_id->GetSize(), proto_id->GetOffset()); + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeProtoIdItem)); + ProcessOffset(&offset, proto_id.get()); + if (reserve_only) { + offset += proto_id->GetSize(); + } else { + buffer[0] = proto_id->Shorty()->GetIndex(); + buffer[1] = proto_id->ReturnType()->GetIndex(); + buffer[2] = proto_id->Parameters() == nullptr ? 0 : proto_id->Parameters()->GetOffset(); + offset += Write(buffer, proto_id->GetSize(), offset); + } } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetProtoIdsOffset(start); + } + return offset - start; } -void DexWriter::WriteFields() { +uint32_t DexWriter::WriteFieldIds(uint32_t offset) { uint16_t buffer[4]; + const uint32_t start = offset; for (std::unique_ptr& field_id : header_->GetCollections().FieldIds()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeFieldIdItem)); + ProcessOffset(&offset, field_id.get()); buffer[0] = field_id->Class()->GetIndex(); buffer[1] = field_id->Type()->GetIndex(); buffer[2] = field_id->Name()->GetIndex(); buffer[3] = field_id->Name()->GetIndex() >> 16; - Write(buffer, field_id->GetSize(), field_id->GetOffset()); + offset += Write(buffer, field_id->GetSize(), offset); + } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetFieldIdsOffset(start); } + return offset - start; } -void DexWriter::WriteMethods() { +uint32_t DexWriter::WriteMethodIds(uint32_t offset) { uint16_t buffer[4]; + const uint32_t start = offset; for (std::unique_ptr& method_id : header_->GetCollections().MethodIds()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeMethodIdItem)); + ProcessOffset(&offset, method_id.get()); buffer[0] = method_id->Class()->GetIndex(); buffer[1] = method_id->Proto()->GetIndex(); buffer[2] = method_id->Name()->GetIndex(); buffer[3] = method_id->Name()->GetIndex() >> 16; - Write(buffer, method_id->GetSize(), method_id->GetOffset()); + offset += Write(buffer, method_id->GetSize(), offset); + } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetMethodIdsOffset(start); } + return offset - start; } -void DexWriter::WriteEncodedArrays() { - for (auto& encoded_array_pair : header_->GetCollections().EncodedArrayItems()) { - std::unique_ptr& encoded_array = encoded_array_pair.second; - WriteEncodedArray(encoded_array->GetEncodedValues(), encoded_array->GetOffset()); +uint32_t DexWriter::WriteEncodedArrays(uint32_t offset) { + const uint32_t start = offset; + for (std::unique_ptr& encoded_array : + header_->GetCollections().EncodedArrayItems()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeEncodedArrayItem)); + ProcessOffset(&offset, encoded_array.get()); + offset += WriteEncodedArray(encoded_array->GetEncodedValues(), offset); } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetEncodedArrayItemsOffset(start); + } + return offset - start; } -void DexWriter::WriteAnnotations() { +uint32_t DexWriter::WriteAnnotations(uint32_t offset) { uint8_t visibility[1]; - for (auto& annotation_pair : header_->GetCollections().AnnotationItems()) { - std::unique_ptr& annotation = annotation_pair.second; + const uint32_t start = offset; + for (std::unique_ptr& annotation : + header_->GetCollections().AnnotationItems()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeAnnotationItem)); visibility[0] = annotation->GetVisibility(); - size_t offset = annotation->GetOffset(); + ProcessOffset(&offset, annotation.get()); offset += Write(visibility, sizeof(uint8_t), offset); - WriteEncodedAnnotation(annotation->GetAnnotation(), offset); + offset += WriteEncodedAnnotation(annotation->GetAnnotation(), offset); + } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetAnnotationItemsOffset(start); } + return offset - start; } -void DexWriter::WriteAnnotationSets() { +uint32_t DexWriter::WriteAnnotationSets(uint32_t offset) { uint32_t size[1]; uint32_t annotation_off[1]; - for (auto& annotation_set_pair : header_->GetCollections().AnnotationSetItems()) { - std::unique_ptr& annotation_set = annotation_set_pair.second; + const uint32_t start = offset; + for (std::unique_ptr& annotation_set : + header_->GetCollections().AnnotationSetItems()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeAnnotationSetItem)); size[0] = annotation_set->GetItems()->size(); - size_t offset = annotation_set->GetOffset(); + ProcessOffset(&offset, annotation_set.get()); offset += Write(size, sizeof(uint32_t), offset); for (dex_ir::AnnotationItem* annotation : *annotation_set->GetItems()) { annotation_off[0] = annotation->GetOffset(); offset += Write(annotation_off, sizeof(uint32_t), offset); } } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetAnnotationSetItemsOffset(start); + } + return offset - start; } -void DexWriter::WriteAnnotationSetRefs() { +uint32_t DexWriter::WriteAnnotationSetRefs(uint32_t offset) { uint32_t size[1]; uint32_t annotations_off[1]; - for (auto& anno_set_ref_pair : header_->GetCollections().AnnotationSetRefLists()) { - std::unique_ptr& annotation_set_ref = anno_set_ref_pair.second; + const uint32_t start = offset; + for (std::unique_ptr& annotation_set_ref : + header_->GetCollections().AnnotationSetRefLists()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeAnnotationSetRefList)); size[0] = annotation_set_ref->GetItems()->size(); - size_t offset = annotation_set_ref->GetOffset(); + ProcessOffset(&offset, annotation_set_ref.get()); offset += Write(size, sizeof(uint32_t), offset); for (dex_ir::AnnotationSetItem* annotation_set : *annotation_set_ref->GetItems()) { annotations_off[0] = annotation_set == nullptr ? 0 : annotation_set->GetOffset(); offset += Write(annotations_off, sizeof(uint32_t), offset); } } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetAnnotationSetRefListsOffset(start); + } + return offset - start; } -void DexWriter::WriteAnnotationsDirectories() { +uint32_t DexWriter::WriteAnnotationsDirectories(uint32_t offset) { uint32_t directory_buffer[4]; uint32_t annotation_buffer[2]; - for (auto& annotations_directory_pair : header_->GetCollections().AnnotationsDirectoryItems()) { - std::unique_ptr& annotations_directory = - annotations_directory_pair.second; + const uint32_t start = offset; + for (std::unique_ptr& annotations_directory : + header_->GetCollections().AnnotationsDirectoryItems()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeAnnotationsDirectoryItem)); + ProcessOffset(&offset, annotations_directory.get()); directory_buffer[0] = annotations_directory->GetClassAnnotation() == nullptr ? 0 : annotations_directory->GetClassAnnotation()->GetOffset(); directory_buffer[1] = annotations_directory->GetFieldAnnotations() == nullptr ? 0 : @@ -377,7 +480,6 @@ void DexWriter::WriteAnnotationsDirectories() { annotations_directory->GetMethodAnnotations()->size(); directory_buffer[3] = annotations_directory->GetParameterAnnotations() == nullptr ? 0 : annotations_directory->GetParameterAnnotations()->size(); - uint32_t offset = annotations_directory->GetOffset(); offset += Write(directory_buffer, 4 * sizeof(uint32_t), offset); if (annotations_directory->GetFieldAnnotations() != nullptr) { for (std::unique_ptr& field : @@ -404,27 +506,55 @@ void DexWriter::WriteAnnotationsDirectories() { } } } -} - -void DexWriter::WriteDebugInfoItems() { - for (auto& debug_info_pair : header_->GetCollections().DebugInfoItems()) { - std::unique_ptr& debug_info = debug_info_pair.second; - Write(debug_info->GetDebugInfo(), debug_info->GetDebugInfoSize(), debug_info->GetOffset()); - } -} - -void DexWriter::WriteCodeItems() { - uint16_t uint16_buffer[4]; - uint32_t uint32_buffer[2]; - for (auto& code_item_pair : header_->GetCollections().CodeItems()) { - std::unique_ptr& code_item = code_item_pair.second; - uint16_buffer[0] = code_item->RegistersSize(); - uint16_buffer[1] = code_item->InsSize(); - uint16_buffer[2] = code_item->OutsSize(); - uint16_buffer[3] = code_item->TriesSize(); - uint32_buffer[0] = code_item->DebugInfo() == nullptr ? 0 : code_item->DebugInfo()->GetOffset(); - uint32_buffer[1] = code_item->InsnsSize(); - size_t offset = code_item->GetOffset(); + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetAnnotationsDirectoryItemsOffset(start); + } + return offset - start; +} + +uint32_t DexWriter::WriteDebugInfoItems(uint32_t offset) { + const uint32_t start = offset; + for (std::unique_ptr& debug_info : + header_->GetCollections().DebugInfoItems()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeDebugInfoItem)); + ProcessOffset(&offset, debug_info.get()); + offset += Write(debug_info->GetDebugInfo(), debug_info->GetDebugInfoSize(), offset); + } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetDebugInfoItemsOffset(start); + } + return offset - start; +} + +uint32_t DexWriter::WriteCodeItems(uint32_t offset, bool reserve_only) { + DexLayoutSection* code_section = nullptr; + if (!reserve_only && dex_layout_ != nullptr) { + code_section = &dex_layout_->GetSections().sections_[static_cast( + DexLayoutSections::SectionType::kSectionTypeCode)]; + } + uint16_t uint16_buffer[4] = {}; + uint32_t uint32_buffer[2] = {}; + uint32_t start = offset; + for (auto& code_item : header_->GetCollections().CodeItems()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeCodeItem)); + ProcessOffset(&offset, code_item.get()); + if (!reserve_only) { + uint16_buffer[0] = code_item->RegistersSize(); + uint16_buffer[1] = code_item->InsSize(); + uint16_buffer[2] = code_item->OutsSize(); + uint16_buffer[3] = code_item->TriesSize(); + uint32_buffer[0] = code_item->DebugInfo() == nullptr ? 0 : + code_item->DebugInfo()->GetOffset(); + uint32_buffer[1] = code_item->InsnsSize(); + // Only add the section hotness info once. + if (code_section != nullptr) { + auto it = dex_layout_->LayoutHotnessInfo().code_item_layout_.find(code_item.get()); + if (it != dex_layout_->LayoutHotnessInfo().code_item_layout_.end()) { + code_section->parts_[static_cast(it->second)].CombineSection( + code_item->GetOffset(), code_item->GetOffset() + code_item->GetSize()); + } + } + } offset += Write(uint16_buffer, 4 * sizeof(uint16_t), offset); offset += Write(uint32_buffer, 2 * sizeof(uint32_t), offset); offset += Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t), offset); @@ -443,7 +573,7 @@ void DexWriter::WriteCodeItems() { offset += Write(insn_count_and_handler_off, 2 * sizeof(uint16_t), offset); } // Leave offset pointing to the end of the try items. - WriteUleb128(code_item->Handlers()->size(), offset); + UNUSED(WriteUleb128(code_item->Handlers()->size(), offset)); for (std::unique_ptr& handlers : *code_item->Handlers()) { size_t list_offset = offset + handlers->GetListOffset(); uint32_t size = handlers->HasCatchAll() ? (handlers->GetHandlers()->size() - 1) * -1 : @@ -457,32 +587,52 @@ void DexWriter::WriteCodeItems() { } } } + // TODO: Clean this up to properly calculate the size instead of assuming it doesn't change. + offset = code_item->GetOffset() + code_item->GetSize(); } + + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetCodeItemsOffset(start); + } + return offset - start; } -void DexWriter::WriteClasses() { +uint32_t DexWriter::WriteClassDefs(uint32_t offset, bool reserve_only) { + const uint32_t start = offset; uint32_t class_def_buffer[8]; for (std::unique_ptr& class_def : header_->GetCollections().ClassDefs()) { - class_def_buffer[0] = class_def->ClassType()->GetIndex(); - class_def_buffer[1] = class_def->GetAccessFlags(); - class_def_buffer[2] = class_def->Superclass() == nullptr ? dex::kDexNoIndex : - class_def->Superclass()->GetIndex(); - class_def_buffer[3] = class_def->InterfacesOffset(); - class_def_buffer[4] = class_def->SourceFile() == nullptr ? dex::kDexNoIndex : - class_def->SourceFile()->GetIndex(); - class_def_buffer[5] = class_def->Annotations() == nullptr ? 0 : - class_def->Annotations()->GetOffset(); - class_def_buffer[6] = class_def->GetClassData() == nullptr ? 0 : - class_def->GetClassData()->GetOffset(); - class_def_buffer[7] = class_def->StaticValues() == nullptr ? 0 : - class_def->StaticValues()->GetOffset(); - size_t offset = class_def->GetOffset(); - Write(class_def_buffer, class_def->GetSize(), offset); - } - - for (auto& class_data_pair : header_->GetCollections().ClassDatas()) { - std::unique_ptr& class_data = class_data_pair.second; - size_t offset = class_data->GetOffset(); + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeClassDefItem)); + if (reserve_only) { + offset += class_def->GetSize(); + } else { + class_def_buffer[0] = class_def->ClassType()->GetIndex(); + class_def_buffer[1] = class_def->GetAccessFlags(); + class_def_buffer[2] = class_def->Superclass() == nullptr ? dex::kDexNoIndex : + class_def->Superclass()->GetIndex(); + class_def_buffer[3] = class_def->InterfacesOffset(); + class_def_buffer[4] = class_def->SourceFile() == nullptr ? dex::kDexNoIndex : + class_def->SourceFile()->GetIndex(); + class_def_buffer[5] = class_def->Annotations() == nullptr ? 0 : + class_def->Annotations()->GetOffset(); + class_def_buffer[6] = class_def->GetClassData() == nullptr ? 0 : + class_def->GetClassData()->GetOffset(); + class_def_buffer[7] = class_def->StaticValues() == nullptr ? 0 : + class_def->StaticValues()->GetOffset(); + offset += Write(class_def_buffer, class_def->GetSize(), offset); + } + } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetClassDefsOffset(start); + } + return offset - start; +} + +uint32_t DexWriter::WriteClassDatas(uint32_t offset) { + const uint32_t start = offset; + for (const std::unique_ptr& class_data : + header_->GetCollections().ClassDatas()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeClassDataItem)); + ProcessOffset(&offset, class_data.get()); offset += WriteUleb128(class_data->StaticFields()->size(), offset); offset += WriteUleb128(class_data->InstanceFields()->size(), offset); offset += WriteUleb128(class_data->DirectMethods()->size(), offset); @@ -492,139 +642,134 @@ void DexWriter::WriteClasses() { offset += WriteEncodedMethods(class_data->DirectMethods(), offset); offset += WriteEncodedMethods(class_data->VirtualMethods(), offset); } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetClassDatasOffset(start); + } + return offset - start; } -void DexWriter::WriteCallSites() { +uint32_t DexWriter::WriteCallSiteIds(uint32_t offset, bool reserve_only) { + const uint32_t start = offset; uint32_t call_site_off[1]; for (std::unique_ptr& call_site_id : header_->GetCollections().CallSiteIds()) { - call_site_off[0] = call_site_id->CallSiteItem()->GetOffset(); - Write(call_site_off, call_site_id->GetSize(), call_site_id->GetOffset()); + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeCallSiteIdItem)); + if (reserve_only) { + offset += call_site_id->GetSize(); + } else { + call_site_off[0] = call_site_id->CallSiteItem()->GetOffset(); + offset += Write(call_site_off, call_site_id->GetSize(), offset); + } } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetCallSiteIdsOffset(start); + } + return offset - start; } -void DexWriter::WriteMethodHandles() { +uint32_t DexWriter::WriteMethodHandles(uint32_t offset) { + const uint32_t start = offset; uint16_t method_handle_buff[4]; for (std::unique_ptr& method_handle : header_->GetCollections().MethodHandleItems()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeMethodHandleItem)); method_handle_buff[0] = static_cast(method_handle->GetMethodHandleType()); method_handle_buff[1] = 0; // unused. method_handle_buff[2] = method_handle->GetFieldOrMethodId()->GetIndex(); method_handle_buff[3] = 0; // unused. - Write(method_handle_buff, method_handle->GetSize(), method_handle->GetOffset()); + offset += Write(method_handle_buff, method_handle->GetSize(), offset); } -} - -struct MapItemContainer { - MapItemContainer(uint32_t type, uint32_t size, uint32_t offset) - : type_(type), size_(size), offset_(offset) { } - - bool operator<(const MapItemContainer& other) const { - return offset_ > other.offset_; - } - - uint32_t type_; - uint32_t size_; - uint32_t offset_; -}; - -void DexWriter::WriteMapItem() { - dex_ir::Collections& collection = header_->GetCollections(); - std::priority_queue queue; - - // Header and index section. - queue.push(MapItemContainer(DexFile::kDexTypeHeaderItem, 1, 0)); - if (collection.StringIdsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeStringIdItem, collection.StringIdsSize(), - collection.StringIdsOffset())); - } - if (collection.TypeIdsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeTypeIdItem, collection.TypeIdsSize(), - collection.TypeIdsOffset())); - } - if (collection.ProtoIdsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeProtoIdItem, collection.ProtoIdsSize(), - collection.ProtoIdsOffset())); - } - if (collection.FieldIdsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeFieldIdItem, collection.FieldIdsSize(), - collection.FieldIdsOffset())); - } - if (collection.MethodIdsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeMethodIdItem, collection.MethodIdsSize(), - collection.MethodIdsOffset())); - } - if (collection.ClassDefsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeClassDefItem, collection.ClassDefsSize(), - collection.ClassDefsOffset())); - } - if (collection.CallSiteIdsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeCallSiteIdItem, collection.CallSiteIdsSize(), - collection.CallSiteIdsOffset())); - } - if (collection.MethodHandleItemsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeMethodHandleItem, - collection.MethodHandleItemsSize(), collection.MethodHandleItemsOffset())); - } - - // Data section. - queue.push(MapItemContainer(DexFile::kDexTypeMapList, 1, collection.MapListOffset())); - if (collection.TypeListsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeTypeList, collection.TypeListsSize(), - collection.TypeListsOffset())); - } - if (collection.AnnotationSetRefListsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeAnnotationSetRefList, - collection.AnnotationSetRefListsSize(), collection.AnnotationSetRefListsOffset())); - } - if (collection.AnnotationSetItemsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeAnnotationSetItem, - collection.AnnotationSetItemsSize(), collection.AnnotationSetItemsOffset())); - } - if (collection.ClassDatasSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeClassDataItem, collection.ClassDatasSize(), - collection.ClassDatasOffset())); - } - if (collection.CodeItemsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeCodeItem, collection.CodeItemsSize(), - collection.CodeItemsOffset())); - } - if (collection.StringDatasSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeStringDataItem, collection.StringDatasSize(), - collection.StringDatasOffset())); - } - if (collection.DebugInfoItemsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeDebugInfoItem, collection.DebugInfoItemsSize(), - collection.DebugInfoItemsOffset())); - } - if (collection.AnnotationItemsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeAnnotationItem, collection.AnnotationItemsSize(), - collection.AnnotationItemsOffset())); - } - if (collection.EncodedArrayItemsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeEncodedArrayItem, - collection.EncodedArrayItemsSize(), collection.EncodedArrayItemsOffset())); - } - if (collection.AnnotationsDirectoryItemsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeAnnotationsDirectoryItem, - collection.AnnotationsDirectoryItemsSize(), collection.AnnotationsDirectoryItemsOffset())); + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetMethodHandleItemsOffset(start); } + return offset - start; +} - uint32_t offset = collection.MapListOffset(); +uint32_t DexWriter::WriteMapItems(uint32_t offset, MapItemQueue* queue) { + // All the sections should already have been added. uint16_t uint16_buffer[2]; uint32_t uint32_buffer[2]; uint16_buffer[1] = 0; - uint32_buffer[0] = queue.size(); + uint32_buffer[0] = queue->size(); + const uint32_t start = offset; offset += Write(uint32_buffer, sizeof(uint32_t), offset); - while (!queue.empty()) { - const MapItemContainer& map_item = queue.top(); + while (!queue->empty()) { + const MapItem& map_item = queue->top(); uint16_buffer[0] = map_item.type_; uint32_buffer[0] = map_item.size_; uint32_buffer[1] = map_item.offset_; offset += Write(uint16_buffer, 2 * sizeof(uint16_t), offset); offset += Write(uint32_buffer, 2 * sizeof(uint32_t), offset); - queue.pop(); + queue->pop(); } + return offset - start; +} + +uint32_t DexWriter::GenerateAndWriteMapItems(uint32_t offset) { + dex_ir::Collections& collection = header_->GetCollections(); + MapItemQueue queue; + + // Header and index section. + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeHeaderItem, 1, 0)); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeStringIdItem, + collection.StringIdsSize(), + collection.StringIdsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeTypeIdItem, + collection.TypeIdsSize(), + collection.TypeIdsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeProtoIdItem, + collection.ProtoIdsSize(), + collection.ProtoIdsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeFieldIdItem, + collection.FieldIdsSize(), + collection.FieldIdsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeMethodIdItem, + collection.MethodIdsSize(), + collection.MethodIdsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeClassDefItem, + collection.ClassDefsSize(), + collection.ClassDefsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeCallSiteIdItem, + collection.CallSiteIdsSize(), + collection.CallSiteIdsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeMethodHandleItem, + collection.MethodHandleItemsSize(), + collection.MethodHandleItemsOffset())); + // Data section. + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeMapList, 1, collection.MapListOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeTypeList, + collection.TypeListsSize(), + collection.TypeListsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationSetRefList, + collection.AnnotationSetRefListsSize(), + collection.AnnotationSetRefListsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationSetItem, + collection.AnnotationSetItemsSize(), + collection.AnnotationSetItemsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeClassDataItem, + collection.ClassDatasSize(), + collection.ClassDatasOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeCodeItem, + collection.CodeItemsSize(), + collection.CodeItemsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeStringDataItem, + collection.StringDatasSize(), + collection.StringDatasOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeDebugInfoItem, + collection.DebugInfoItemsSize(), + collection.DebugInfoItemsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationItem, + collection.AnnotationItemsSize(), + collection.AnnotationItemsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeEncodedArrayItem, + collection.EncodedArrayItemsSize(), + collection.EncodedArrayItemsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationsDirectoryItem, + collection.AnnotationsDirectoryItemsSize(), + collection.AnnotationsDirectoryItemsOffset())); + + // Write the map items. + return WriteMapItems(offset, &queue); } void DexWriter::WriteHeader() { @@ -657,38 +802,110 @@ void DexWriter::WriteHeader() { header.data_off_ = header_->DataOffset(); static_assert(sizeof(header) == 0x70, "Size doesn't match dex spec"); - Write(reinterpret_cast(&header), sizeof(header), 0u); + UNUSED(Write(reinterpret_cast(&header), sizeof(header), 0u)); } void DexWriter::WriteMemMap() { - WriteStrings(); - WriteTypes(); - WriteTypeLists(); - WriteProtos(); - WriteFields(); - WriteMethods(); - WriteEncodedArrays(); - WriteAnnotations(); - WriteAnnotationSets(); - WriteAnnotationSetRefs(); - WriteAnnotationsDirectories(); - WriteDebugInfoItems(); - WriteCodeItems(); - WriteClasses(); - WriteCallSites(); - WriteMethodHandles(); - WriteMapItem(); + // Starting offset is right after the header. + uint32_t offset = sizeof(StandardDexFile::Header); + + dex_ir::Collections& collection = header_->GetCollections(); + + // Based on: https://source.android.com/devices/tech/dalvik/dex-format + // Since the offsets may not be calculated already, the writing must be done in the correct order. + const uint32_t string_ids_offset = offset; + offset += WriteStringIds(offset, /*reserve_only*/ true); + offset += WriteTypeIds(offset); + const uint32_t proto_ids_offset = offset; + offset += WriteProtoIds(offset, /*reserve_only*/ true); + offset += WriteFieldIds(offset); + offset += WriteMethodIds(offset); + const uint32_t class_defs_offset = offset; + offset += WriteClassDefs(offset, /*reserve_only*/ true); + const uint32_t call_site_ids_offset = offset; + offset += WriteCallSiteIds(offset, /*reserve_only*/ true); + offset += WriteMethodHandles(offset); + + uint32_t data_offset_ = 0u; + if (compute_offsets_) { + // Data section. + offset = RoundUp(offset, kDataSectionAlignment); + data_offset_ = offset; + } + + // Write code item first to minimize the space required for encoded methods. + // Reserve code item space since we need the debug offsets to actually write them. + const uint32_t code_items_offset = offset; + offset += WriteCodeItems(offset, /*reserve_only*/ true); + // Write debug info section. + offset += WriteDebugInfoItems(offset); + // Actually write code items since debug info offsets are calculated now. + WriteCodeItems(code_items_offset, /*reserve_only*/ false); + + offset += WriteEncodedArrays(offset); + offset += WriteAnnotations(offset); + offset += WriteAnnotationSets(offset); + offset += WriteAnnotationSetRefs(offset); + offset += WriteAnnotationsDirectories(offset); + offset += WriteTypeLists(offset); + offset += WriteClassDatas(offset); + offset += WriteStringDatas(offset); + + // Write delayed id sections that depend on data sections. + WriteStringIds(string_ids_offset, /*reserve_only*/ false); + WriteProtoIds(proto_ids_offset, /*reserve_only*/ false); + WriteClassDefs(class_defs_offset, /*reserve_only*/ false); + WriteCallSiteIds(call_site_ids_offset, /*reserve_only*/ false); + + // Write the map list. + if (compute_offsets_) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeMapList)); + collection.SetMapListOffset(offset); + } else { + offset = collection.MapListOffset(); + } + offset += GenerateAndWriteMapItems(offset); + offset = RoundUp(offset, kDataSectionAlignment); + + // Map items are included in the data section. + if (compute_offsets_) { + header_->SetDataSize(offset - data_offset_); + if (header_->DataSize() != 0) { + // Offset must be zero when the size is zero. + header_->SetDataOffset(data_offset_); + } else { + header_->SetDataOffset(0u); + } + } + + // TODO: Write link data? + + // Write header last. + if (compute_offsets_) { + header_->SetFileSize(offset); + } WriteHeader(); } -void DexWriter::Output(dex_ir::Header* header, MemMap* mem_map, CompactDexLevel compact_dex_level) { +void DexWriter::Output(dex_ir::Header* header, + MemMap* mem_map, + DexLayout* dex_layout, + bool compute_offsets, + CompactDexLevel compact_dex_level) { + CHECK(dex_layout != nullptr); std::unique_ptr writer; if (compact_dex_level != CompactDexLevel::kCompactDexLevelNone) { - writer.reset(new CompactDexWriter(header, mem_map, compact_dex_level)); + writer.reset(new CompactDexWriter(header, mem_map, dex_layout, compact_dex_level)); } else { - writer.reset(new DexWriter(header, mem_map)); + writer.reset(new DexWriter(header, mem_map, dex_layout, compute_offsets)); } writer->WriteMemMap(); } +void MapItemQueue::AddIfNotEmpty(const MapItem& item) { + if (item.size_ != 0) { + push(item); + } +} + } // namespace art diff --git a/dexlayout/dex_writer.h b/dexlayout/dex_writer.h index 85d3e7ebf3..c47898e533 100644 --- a/dexlayout/dex_writer.h +++ b/dexlayout/dex_writer.h @@ -19,56 +19,119 @@ #ifndef ART_DEXLAYOUT_DEX_WRITER_H_ #define ART_DEXLAYOUT_DEX_WRITER_H_ +#include + #include "base/unix_file/fd_file.h" #include "cdex/compact_dex_level.h" #include "dex_ir.h" #include "mem_map.h" #include "os.h" +#include + namespace art { +class DexLayout; +class DexLayoutHotnessInfo; + +struct MapItem { + // Not using DexFile::MapItemType since compact dex and standard dex file may have different + // sections. + MapItem() = default; + MapItem(uint32_t type, uint32_t size, uint32_t offset) + : type_(type), size_(size), offset_(offset) { } + + // Sort by decreasing order since the priority_queue puts largest elements first. + bool operator>(const MapItem& other) const { + return offset_ > other.offset_; + } + + uint32_t type_ = 0u; + uint32_t size_ = 0u; + uint32_t offset_ = 0u; +}; + +class MapItemQueue : public + std::priority_queue, std::greater> { + public: + void AddIfNotEmpty(const MapItem& item); +}; + class DexWriter { public: - DexWriter(dex_ir::Header* header, MemMap* mem_map) : header_(header), mem_map_(mem_map) {} + DexWriter(dex_ir::Header* header, + MemMap* mem_map, + DexLayout* dex_layout, + bool compute_offsets) + : header_(header), + mem_map_(mem_map), + dex_layout_(dex_layout), + compute_offsets_(compute_offsets) {} - static void Output(dex_ir::Header* header, MemMap* mem_map, CompactDexLevel compact_dex_level); + static void Output(dex_ir::Header* header, + MemMap* mem_map, + DexLayout* dex_layout, + bool compute_offsets, + CompactDexLevel compact_dex_level); virtual ~DexWriter() {} protected: void WriteMemMap(); - size_t Write(const void* buffer, size_t length, size_t offset); - size_t WriteSleb128(uint32_t value, size_t offset); - size_t WriteUleb128(uint32_t value, size_t offset); - size_t WriteEncodedValue(dex_ir::EncodedValue* encoded_value, size_t offset); - size_t WriteEncodedValueHeader(int8_t value_type, size_t value_arg, size_t offset); - size_t WriteEncodedArray(dex_ir::EncodedValueVector* values, size_t offset); - size_t WriteEncodedAnnotation(dex_ir::EncodedAnnotation* annotation, size_t offset); - size_t WriteEncodedFields(dex_ir::FieldItemVector* fields, size_t offset); - size_t WriteEncodedMethods(dex_ir::MethodItemVector* methods, size_t offset); - - void WriteStrings(); - void WriteTypes(); - void WriteTypeLists(); - void WriteProtos(); - void WriteFields(); - void WriteMethods(); - void WriteEncodedArrays(); - void WriteAnnotations(); - void WriteAnnotationSets(); - void WriteAnnotationSetRefs(); - void WriteAnnotationsDirectories(); - void WriteDebugInfoItems(); - void WriteCodeItems(); - void WriteClasses(); - void WriteCallSites(); - void WriteMethodHandles(); - void WriteMapItem(); + size_t Write(const void* buffer, size_t length, size_t offset) WARN_UNUSED; + size_t WriteSleb128(uint32_t value, size_t offset) WARN_UNUSED; + size_t WriteUleb128(uint32_t value, size_t offset) WARN_UNUSED; + size_t WriteEncodedValue(dex_ir::EncodedValue* encoded_value, size_t offset) WARN_UNUSED; + size_t WriteEncodedValueHeader(int8_t value_type, size_t value_arg, size_t offset) WARN_UNUSED; + size_t WriteEncodedArray(dex_ir::EncodedValueVector* values, size_t offset) WARN_UNUSED; + size_t WriteEncodedAnnotation(dex_ir::EncodedAnnotation* annotation, size_t offset) WARN_UNUSED; + size_t WriteEncodedFields(dex_ir::FieldItemVector* fields, size_t offset) WARN_UNUSED; + size_t WriteEncodedMethods(dex_ir::MethodItemVector* methods, size_t offset) WARN_UNUSED; + + // Header and id section virtual void WriteHeader(); + // reserve_only means don't write, only reserve space. This is required since the string data + // offsets must be assigned. + uint32_t WriteStringIds(uint32_t offset, bool reserve_only); + uint32_t WriteTypeIds(uint32_t offset); + uint32_t WriteProtoIds(uint32_t offset, bool reserve_only); + uint32_t WriteFieldIds(uint32_t offset); + uint32_t WriteMethodIds(uint32_t offset); + uint32_t WriteClassDefs(uint32_t offset, bool reserve_only); + uint32_t WriteCallSiteIds(uint32_t offset, bool reserve_only); + + uint32_t WriteEncodedArrays(uint32_t offset); + uint32_t WriteAnnotations(uint32_t offset); + uint32_t WriteAnnotationSets(uint32_t offset); + uint32_t WriteAnnotationSetRefs(uint32_t offset); + uint32_t WriteAnnotationsDirectories(uint32_t offset); + + // Data section. + uint32_t WriteDebugInfoItems(uint32_t offset); + uint32_t WriteCodeItems(uint32_t offset, bool reserve_only); + uint32_t WriteTypeLists(uint32_t offset); + uint32_t WriteStringDatas(uint32_t offset); + uint32_t WriteClassDatas(uint32_t offset); + uint32_t WriteMethodHandles(uint32_t offset); + uint32_t WriteMapItems(uint32_t offset, MapItemQueue* queue); + uint32_t GenerateAndWriteMapItems(uint32_t offset); + + // Process an offset, if compute_offset is set, write into the dex ir item, otherwise read the + // existing offset and use that for writing. + void ProcessOffset(uint32_t* const offset, dex_ir::Item* item) { + if (compute_offsets_) { + item->SetOffset(*offset); + } else { + // Not computing offsets, just use the one in the item. + *offset = item->GetOffset(); + } + } dex_ir::Header* const header_; MemMap* const mem_map_; + DexLayout* const dex_layout_; + bool compute_offsets_; private: DISALLOW_COPY_AND_ASSIGN(DexWriter); diff --git a/dexlayout/dexdiag.cc b/dexlayout/dexdiag.cc index c14cd5f807..e83f98ee6b 100644 --- a/dexlayout/dexdiag.cc +++ b/dexlayout/dexdiag.cc @@ -289,7 +289,8 @@ static void ProcessOneDexMapping(uint64_t* pagemap, // Build a list of the dex file section types, sorted from highest offset to lowest. std::vector sections; { - std::unique_ptr header(dex_ir::DexIrBuilder(*dex_file)); + std::unique_ptr header(dex_ir::DexIrBuilder(*dex_file, + /*eagerly_assign_offsets*/ true)); sections = dex_ir::GetSortedDexFileSections(header.get(), dex_ir::SortDirection::kSortDescending); } diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index dd2e809a92..d904a52f0c 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -56,9 +56,6 @@ using android::base::StringPrintf; // necessary to ensure the partial order w.r.t. class derivation. TODO: Re-enable (b/68317550). static constexpr bool kChangeClassDefOrder = false; -static constexpr uint32_t kDataSectionAlignment = sizeof(uint32_t) * 2; -static constexpr uint32_t kDexCodeItemAlignment = 4; - /* * Flags for use with createAccessFlagStr(). */ @@ -1564,7 +1561,7 @@ void DexLayout::DumpDexFile() { } } -std::vector DexLayout::LayoutClassDefsAndClassData(const DexFile* dex_file) { +void DexLayout::LayoutClassDefsAndClassData(const DexFile* dex_file) { std::vector new_class_def_order; for (std::unique_ptr& class_def : header_->GetCollections().ClassDefs()) { dex::TypeIndex type_idx(class_def->ClassType()->GetIndex()); @@ -1578,31 +1575,41 @@ std::vector DexLayout::LayoutClassDefsAndClassData(const Dex new_class_def_order.push_back(class_def.get()); } } - uint32_t class_defs_offset = header_->GetCollections().ClassDefsOffset(); - uint32_t class_data_offset = header_->GetCollections().ClassDatasOffset(); std::unordered_set visited_class_data; - std::vector new_class_data_order; - for (uint32_t i = 0; i < new_class_def_order.size(); ++i) { - dex_ir::ClassDef* class_def = new_class_def_order[i]; - if (kChangeClassDefOrder) { - // This produces dex files that violate the spec since the super class class_def is supposed - // to occur before any subclasses. - class_def->SetIndex(i); - class_def->SetOffset(class_defs_offset); - class_defs_offset += dex_ir::ClassDef::ItemSize(); - } + size_t class_data_index = 0; + dex_ir::CollectionVector::Vector& class_datas = + header_->GetCollections().ClassDatas(); + for (dex_ir::ClassDef* class_def : new_class_def_order) { dex_ir::ClassData* class_data = class_def->GetClassData(); if (class_data != nullptr && visited_class_data.find(class_data) == visited_class_data.end()) { - class_data->SetOffset(class_data_offset); - class_data_offset += class_data->GetSize(); visited_class_data.insert(class_data); - new_class_data_order.push_back(class_data); + // Overwrite the existing vector with the new ordering, note that the sets of objects are + // equivalent, but the order changes. This is why this is not a memory leak. + // TODO: Consider cleaning this up with a shared_ptr. + class_datas[class_data_index].release(); + class_datas[class_data_index].reset(class_data); + ++class_data_index; + } + } + CHECK_EQ(class_data_index, class_datas.size()); + + if (kChangeClassDefOrder) { + // This currently produces dex files that violate the spec since the super class class_def is + // supposed to occur before any subclasses. + dex_ir::CollectionVector::Vector& class_defs = + header_->GetCollections().ClassDefs(); + CHECK_EQ(new_class_def_order.size(), class_defs.size()); + for (size_t i = 0; i < class_defs.size(); ++i) { + // Overwrite the existing vector with the new ordering, note that the sets of objects are + // equivalent, but the order changes. This is why this is not a memory leak. + // TODO: Consider cleaning this up with a shared_ptr. + class_defs[i].release(); + class_defs[i].reset(new_class_def_order[i]); } } - return new_class_data_order; } -int32_t DexLayout::LayoutStringData(const DexFile* dex_file) { +void DexLayout::LayoutStringData(const DexFile* dex_file) { const size_t num_strings = header_->GetCollections().StringIds().size(); std::vector is_shorty(num_strings, false); std::vector from_hot_method(num_strings, false); @@ -1672,23 +1679,9 @@ int32_t DexLayout::LayoutStringData(const DexFile* dex_file) { } // Sort string data by specified order. std::vector string_ids; - size_t min_offset = std::numeric_limits::max(); - size_t max_offset = 0; - size_t hot_bytes = 0; for (auto& string_id : header_->GetCollections().StringIds()) { string_ids.push_back(string_id.get()); - const size_t cur_offset = string_id->DataItem()->GetOffset(); - CHECK_NE(cur_offset, 0u); - min_offset = std::min(min_offset, cur_offset); - dex_ir::StringData* data = string_id->DataItem(); - const size_t element_size = data->GetSize() + 1; // Add one extra for null. - size_t end_offset = cur_offset + element_size; - if (is_shorty[string_id->GetIndex()] || from_hot_method[string_id->GetIndex()]) { - hot_bytes += element_size; - } - max_offset = std::max(max_offset, end_offset); - } - VLOG(compiler) << "Hot string data bytes " << hot_bytes << "/" << max_offset - min_offset; + } std::sort(string_ids.begin(), string_ids.end(), [&is_shorty, &from_hot_method](const dex_ir::StringId* a, @@ -1704,59 +1697,41 @@ int32_t DexLayout::LayoutStringData(const DexFile* dex_file) { if (a_is_shorty != b_is_shorty) { return a_is_shorty < b_is_shorty; } - // Preserve order. - return a->DataItem()->GetOffset() < b->DataItem()->GetOffset(); + // Order by index by default. + return a->GetIndex() < b->GetIndex(); }); - // Now we know what order we want the string data, reorder the offsets. - size_t offset = min_offset; + dex_ir::CollectionVector::Vector& string_datas = + header_->GetCollections().StringDatas(); + // Now we know what order we want the string data, reorder them. + size_t data_index = 0; for (dex_ir::StringId* string_id : string_ids) { - dex_ir::StringData* data = string_id->DataItem(); - data->SetOffset(offset); - offset += data->GetSize() + 1; // Add one extra for null. + string_datas[data_index].release(); + string_datas[data_index].reset(string_id->DataItem()); + ++data_index; } - if (offset > max_offset) { - return offset - max_offset; - // If we expanded the string data section, we need to update the offsets or else we will - // corrupt the next section when writing out. + if (kIsDebugBuild) { + std::unordered_set visited; + for (const std::unique_ptr& data : string_datas) { + visited.insert(data.get()); + } + for (auto& string_id : header_->GetCollections().StringIds()) { + CHECK(visited.find(string_id->DataItem()) != visited.end()); + } } - return 0; + CHECK_EQ(data_index, string_datas.size()); } // Orders code items according to specified class data ordering. -// NOTE: If the section following the code items is byte aligned, the last code item is left in -// place to preserve alignment. Layout needs an overhaul to handle movement of other sections. -int32_t DexLayout::LayoutCodeItems(const DexFile* dex_file, - std::vector new_class_data_order) { - // Do not move code items if class data section precedes code item section. - // ULEB encoding is variable length, causing problems determining the offset of the code items. - // TODO: We should swap the order of these sections in the future to avoid this issue. - uint32_t class_data_offset = header_->GetCollections().ClassDatasOffset(); - uint32_t code_item_offset = header_->GetCollections().CodeItemsOffset(); - if (class_data_offset < code_item_offset) { - return 0; - } - - // Find the last code item so we can leave it in place if the next section is not 4 byte aligned. - dex_ir::CodeItem* last_code_item = nullptr; - std::unordered_set visited_code_items; - bool is_code_item_aligned = IsNextSectionCodeItemAligned(code_item_offset); - if (!is_code_item_aligned) { - for (auto& code_item_pair : header_->GetCollections().CodeItems()) { - std::unique_ptr& code_item = code_item_pair.second; - if (last_code_item == nullptr - || last_code_item->GetOffset() < code_item->GetOffset()) { - last_code_item = code_item.get(); - } - } - } - +void DexLayout::LayoutCodeItems(const DexFile* dex_file) { static constexpr InvokeType invoke_types[] = { kDirect, kVirtual }; - const size_t num_layout_types = static_cast(LayoutType::kLayoutTypeCount); - std::unordered_set code_items[num_layout_types]; + std::unordered_map& code_item_layout = + layout_hotness_info_.code_item_layout_; + + // Assign hotness flags to all code items. for (InvokeType invoke_type : invoke_types) { for (std::unique_ptr& class_def : header_->GetCollections().ClassDefs()) { const bool is_profile_class = @@ -1772,7 +1747,7 @@ int32_t DexLayout::LayoutCodeItems(const DexFile* dex_file, : class_data->VirtualMethods())) { const dex_ir::MethodId *method_id = method->GetMethodId(); dex_ir::CodeItem *code_item = method->GetCodeItem(); - if (code_item == last_code_item || code_item == nullptr) { + if (code_item == nullptr) { continue; } // Separate executed methods (clinits and profiled methods) from unexecuted methods. @@ -1794,194 +1769,61 @@ int32_t DexLayout::LayoutCodeItems(const DexFile* dex_file, } else if (hotness.IsInProfile()) { state = LayoutType::kLayoutTypeSometimesUsed; } - code_items[static_cast(state)].insert(code_item); - } - } - } - - // Removing duplicate CodeItems may expose other issues with downstream - // optimizations such as quickening. But we need to ensure at least the weak - // forms of it currently in use do not break layout optimizations. - std::map original_code_item_offset; - // Total_diff includes diffs generated by clinits, executed, and non-executed methods. - int32_t total_diff = 0; - // The relative placement has no effect on correctness; it is used to ensure - // the layout is deterministic - for (size_t index = 0; index < num_layout_types; ++index) { - const std::unordered_set& code_items_set = code_items[index]; - // diff is reset for each class of code items. - int32_t diff = 0; - const uint32_t start_offset = code_item_offset; - for (dex_ir::ClassData* data : new_class_data_order) { - data->SetOffset(data->GetOffset() + diff); - for (InvokeType invoke_type : invoke_types) { - for (auto &method : *(invoke_type == InvokeType::kDirect - ? data->DirectMethods() - : data->VirtualMethods())) { - dex_ir::CodeItem* code_item = method->GetCodeItem(); - if (code_item != nullptr && - code_items_set.find(code_item) != code_items_set.end()) { - // Compute where the CodeItem was originally laid out. - uint32_t original_offset = code_item->GetOffset(); - auto it = original_code_item_offset.find(code_item); - if (it != original_code_item_offset.end()) { - original_offset = it->second; - } else { - original_code_item_offset[code_item] = code_item->GetOffset(); - // Assign the new offset and move the pointer to allocate space. - code_item->SetOffset(code_item_offset); - code_item_offset += - RoundUp(code_item->GetSize(), kDexCodeItemAlignment); - } - // Update the size of the encoded methods to reflect that the offset difference - // may have changed the ULEB128 length. - diff += - UnsignedLeb128Size(code_item->GetOffset()) - UnsignedLeb128Size(original_offset); - } + auto it = code_item_layout.emplace(code_item, state); + if (!it.second) { + LayoutType& layout_type = it.first->second; + // Already exists, merge the hotness. + layout_type = MergeLayoutType(layout_type, state); } } } - DexLayoutSection& code_section = dex_sections_.sections_[static_cast( - DexLayoutSections::SectionType::kSectionTypeCode)]; - code_section.parts_[index].offset_ = start_offset; - code_section.parts_[index].size_ = code_item_offset - start_offset; - for (size_t i = 0; i < num_layout_types; ++i) { - VLOG(dex) << "Code item layout bucket " << i << " count=" << code_items[i].size() - << " bytes=" << code_section.parts_[i].size_; - } - total_diff += diff; } - // Adjust diff to be 4-byte aligned. - return RoundUp(total_diff, kDexCodeItemAlignment); -} -bool DexLayout::IsNextSectionCodeItemAligned(uint32_t offset) { - dex_ir::Collections& collections = header_->GetCollections(); - std::set section_offsets; - section_offsets.insert(collections.MapListOffset()); - section_offsets.insert(collections.TypeListsOffset()); - section_offsets.insert(collections.AnnotationSetRefListsOffset()); - section_offsets.insert(collections.AnnotationSetItemsOffset()); - section_offsets.insert(collections.ClassDatasOffset()); - section_offsets.insert(collections.CodeItemsOffset()); - section_offsets.insert(collections.StringDatasOffset()); - section_offsets.insert(collections.DebugInfoItemsOffset()); - section_offsets.insert(collections.AnnotationItemsOffset()); - section_offsets.insert(collections.EncodedArrayItemsOffset()); - section_offsets.insert(collections.AnnotationsDirectoryItemsOffset()); - - auto found = section_offsets.find(offset); - if (found != section_offsets.end()) { - found++; - if (found != section_offsets.end()) { - return *found % kDexCodeItemAlignment == 0; + dex_ir::CollectionVector::Vector& code_items = + header_->GetCollections().CodeItems(); + if (VLOG_IS_ON(dex)) { + size_t layout_count[static_cast(LayoutType::kLayoutTypeCount)] = {}; + for (const std::unique_ptr& code_item : code_items) { + auto it = code_item_layout.find(code_item.get()); + DCHECK(it != code_item_layout.end()); + ++layout_count[static_cast(it->second)]; + } + for (size_t i = 0; i < static_cast(LayoutType::kLayoutTypeCount); ++i) { + LOG(INFO) << "Code items in category " << i << " count=" << layout_count[i]; } - } - return false; -} - -// Adjust offsets of every item in the specified section by diff bytes. -template void DexLayout::FixupSection(std::map>& map, - uint32_t diff) { - for (auto& pair : map) { - std::unique_ptr& item = pair.second; - item->SetOffset(item->GetOffset() + diff); - } -} - -// Adjust offsets of all sections with an address after the specified offset by diff bytes. -void DexLayout::FixupSections(uint32_t offset, uint32_t diff) { - dex_ir::Collections& collections = header_->GetCollections(); - uint32_t map_list_offset = collections.MapListOffset(); - if (map_list_offset > offset) { - collections.SetMapListOffset(map_list_offset + diff); - } - - uint32_t type_lists_offset = collections.TypeListsOffset(); - if (type_lists_offset > offset) { - collections.SetTypeListsOffset(type_lists_offset + diff); - FixupSection(collections.TypeLists(), diff); - } - - uint32_t annotation_set_ref_lists_offset = collections.AnnotationSetRefListsOffset(); - if (annotation_set_ref_lists_offset > offset) { - collections.SetAnnotationSetRefListsOffset(annotation_set_ref_lists_offset + diff); - FixupSection(collections.AnnotationSetRefLists(), diff); - } - - uint32_t annotation_set_items_offset = collections.AnnotationSetItemsOffset(); - if (annotation_set_items_offset > offset) { - collections.SetAnnotationSetItemsOffset(annotation_set_items_offset + diff); - FixupSection(collections.AnnotationSetItems(), diff); - } - - uint32_t class_datas_offset = collections.ClassDatasOffset(); - if (class_datas_offset > offset) { - collections.SetClassDatasOffset(class_datas_offset + diff); - FixupSection(collections.ClassDatas(), diff); - } - - uint32_t code_items_offset = collections.CodeItemsOffset(); - if (code_items_offset > offset) { - collections.SetCodeItemsOffset(code_items_offset + diff); - FixupSection(collections.CodeItems(), diff); - } - - uint32_t string_datas_offset = collections.StringDatasOffset(); - if (string_datas_offset > offset) { - collections.SetStringDatasOffset(string_datas_offset + diff); - FixupSection(collections.StringDatas(), diff); - } - - uint32_t debug_info_items_offset = collections.DebugInfoItemsOffset(); - if (debug_info_items_offset > offset) { - collections.SetDebugInfoItemsOffset(debug_info_items_offset + diff); - FixupSection(collections.DebugInfoItems(), diff); - } - - uint32_t annotation_items_offset = collections.AnnotationItemsOffset(); - if (annotation_items_offset > offset) { - collections.SetAnnotationItemsOffset(annotation_items_offset + diff); - FixupSection(collections.AnnotationItems(), diff); - } - - uint32_t encoded_array_items_offset = collections.EncodedArrayItemsOffset(); - if (encoded_array_items_offset > offset) { - collections.SetEncodedArrayItemsOffset(encoded_array_items_offset + diff); - FixupSection(collections.EncodedArrayItems(), diff); } - uint32_t annotations_directory_items_offset = collections.AnnotationsDirectoryItemsOffset(); - if (annotations_directory_items_offset > offset) { - collections.SetAnnotationsDirectoryItemsOffset(annotations_directory_items_offset + diff); - FixupSection(collections.AnnotationsDirectoryItems(), diff); - } + // Sort the code items vector by new layout. The writing process will take care of calculating + // all the offsets. Stable sort to preserve any existing locality that might be there. + std::stable_sort(code_items.begin(), + code_items.end(), + [&](const std::unique_ptr& a, + const std::unique_ptr& b) { + auto it_a = code_item_layout.find(a.get()); + auto it_b = code_item_layout.find(b.get()); + DCHECK(it_a != code_item_layout.end()); + DCHECK(it_b != code_item_layout.end()); + const LayoutType layout_type_a = it_a->second; + const LayoutType layout_type_b = it_b->second; + return layout_type_a < layout_type_b; + }); } void DexLayout::LayoutOutputFile(const DexFile* dex_file) { - const int32_t string_diff = LayoutStringData(dex_file); - // If we expanded the string data section, we need to update the offsets or else we will - // corrupt the next section when writing out. - FixupSections(header_->GetCollections().StringDatasOffset(), string_diff); - // Update file size. - header_->SetFileSize(header_->FileSize() + string_diff); - - std::vector new_class_data_order = LayoutClassDefsAndClassData(dex_file); - const int32_t code_item_diff = LayoutCodeItems(dex_file, new_class_data_order); - // Move sections after ClassData by diff bytes. - FixupSections(header_->GetCollections().ClassDatasOffset(), code_item_diff); - - // Update file and data size. - // The data size must be aligned to kDataSectionAlignment. - const int32_t total_diff = code_item_diff + string_diff; - header_->SetDataSize(RoundUp(header_->DataSize() + total_diff, kDataSectionAlignment)); - header_->SetFileSize(header_->FileSize() + total_diff); + LayoutStringData(dex_file); + LayoutClassDefsAndClassData(dex_file); + LayoutCodeItems(dex_file); } -void DexLayout::OutputDexFile(const DexFile* dex_file) { +void DexLayout::OutputDexFile(const DexFile* dex_file, bool compute_offsets) { const std::string& dex_file_location = dex_file->GetLocation(); std::string error_msg; std::unique_ptr new_file; + // Since we allow dex growth, we need to size the map larger than the original input to be safe. + // Reserve an extra 10% to add some buffer room. Note that this is probably more than + // necessary. + constexpr size_t kReserveFraction = 10; + const size_t max_size = header_->FileSize() + header_->FileSize() / kReserveFraction; if (!options_.output_to_memmap_) { std::string output_location(options_.output_dex_directory_); size_t last_slash = dex_file_location.rfind('/'); @@ -1998,15 +1840,15 @@ void DexLayout::OutputDexFile(const DexFile* dex_file) { LOG(ERROR) << "Could not create dex writer output file: " << output_location; return; } - if (ftruncate(new_file->Fd(), header_->FileSize()) != 0) { + if (ftruncate(new_file->Fd(), max_size) != 0) { LOG(ERROR) << "Could not grow dex writer output file: " << output_location;; new_file->Erase(); return; } - mem_map_.reset(MemMap::MapFile(header_->FileSize(), PROT_READ | PROT_WRITE, MAP_SHARED, + mem_map_.reset(MemMap::MapFile(max_size, PROT_READ | PROT_WRITE, MAP_SHARED, new_file->Fd(), 0, /*low_4gb*/ false, output_location.c_str(), &error_msg)); } else { - mem_map_.reset(MemMap::MapAnonymous("layout dex", nullptr, header_->FileSize(), + mem_map_.reset(MemMap::MapAnonymous("layout dex", nullptr, max_size, PROT_READ | PROT_WRITE, /* low_4gb */ false, /* reuse */ false, &error_msg)); } if (mem_map_ == nullptr) { @@ -2016,8 +1858,14 @@ void DexLayout::OutputDexFile(const DexFile* dex_file) { } return; } - DexWriter::Output(header_, mem_map_.get(), options_.compact_dex_level_); + DexWriter::Output(header_, mem_map_.get(), this, compute_offsets, options_.compact_dex_level_); if (new_file != nullptr) { + // Since we make the memmap larger than needed, shrink the file back down to not leave extra + // padding. + int res = new_file->SetLength(header_->FileSize()); + if (res != 0) { + LOG(ERROR) << "Truncating file resulted in " << res; + } UNUSED(new_file->FlushCloseOrErase()); } } @@ -2028,7 +1876,15 @@ void DexLayout::OutputDexFile(const DexFile* dex_file) { void DexLayout::ProcessDexFile(const char* file_name, const DexFile* dex_file, size_t dex_file_index) { - std::unique_ptr header(dex_ir::DexIrBuilder(*dex_file)); + const bool output = options_.output_dex_directory_ != nullptr || options_.output_to_memmap_; + // Try to avoid eagerly assigning offsets to find bugs since GetOffset will abort if the offset + // is unassigned. + bool eagerly_assign_offsets = false; + if (options_.visualize_pattern_ || options_.show_section_statistics_ || options_.dump_) { + // These options required the offsets for dumping purposes. + eagerly_assign_offsets = true; + } + std::unique_ptr header(dex_ir::DexIrBuilder(*dex_file, eagerly_assign_offsets)); SetHeader(header.get()); if (options_.verbose_) { @@ -2052,13 +1908,17 @@ void DexLayout::ProcessDexFile(const char* file_name, } // In case we are outputting to a file, keep it open so we can verify. - if (options_.output_dex_directory_ != nullptr || options_.output_to_memmap_) { - if (info_ != nullptr) { + if (output) { + // Layout information about what strings and code items are hot. Used by the writing process + // to generate the sections that are stored in the oat file. + bool do_layout = info_ != nullptr; + if (do_layout) { LayoutOutputFile(dex_file); } - OutputDexFile(dex_file); + OutputDexFile(dex_file, do_layout); // Clear header before verifying to reduce peak RAM usage. + const size_t file_size = header_->FileSize(); header.reset(); // Verify the output dex file's structure, only enabled by default for debug builds. @@ -2066,7 +1926,7 @@ void DexLayout::ProcessDexFile(const char* file_name, std::string error_msg; std::string location = "memory mapped file for " + std::string(file_name); std::unique_ptr output_dex_file(DexFileLoader::Open(mem_map_->Begin(), - mem_map_->Size(), + file_size, location, /* checksum */ 0, /*oat_dex_file*/ nullptr, @@ -2076,11 +1936,16 @@ void DexLayout::ProcessDexFile(const char* file_name, CHECK(output_dex_file != nullptr) << "Failed to re-open output file:" << error_msg; // Do IR-level comparison between input and output. This check ignores potential differences - // due to layout, so offsets are not checked. Instead, it checks the data contents of each item. + // due to layout, so offsets are not checked. Instead, it checks the data contents of each + // item. // // Regenerate output IR to catch any bugs that might happen during writing. - std::unique_ptr output_header(dex_ir::DexIrBuilder(*output_dex_file)); - std::unique_ptr orig_header(dex_ir::DexIrBuilder(*dex_file)); + std::unique_ptr output_header( + dex_ir::DexIrBuilder(*output_dex_file, + /*eagerly_assign_offsets*/ true)); + std::unique_ptr orig_header( + dex_ir::DexIrBuilder(*dex_file, + /*eagerly_assign_offsets*/ true)); CHECK(VerifyOutputDexFile(output_header.get(), orig_header.get(), &error_msg)) << error_msg; } } diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h index 2e897739cc..8a277b7afe 100644 --- a/dexlayout/dexlayout.h +++ b/dexlayout/dexlayout.h @@ -25,6 +25,7 @@ #include #include +#include #include "cdex/compact_dex_level.h" #include "dex_file_layout.h" @@ -69,6 +70,13 @@ class Options { const char* profile_file_name_ = nullptr; }; +// Hotness info +class DexLayoutHotnessInfo { + public: + // Store layout information so that the offset calculation can specify the section sizes. + std::unordered_map code_item_layout_; +}; + class DexLayout { public: DexLayout(Options& options, @@ -86,10 +94,14 @@ class DexLayout { MemMap* GetAndReleaseMemMap() { return mem_map_.release(); } - const DexLayoutSections& GetSections() const { + DexLayoutSections& GetSections() { return dex_sections_; } + const DexLayoutHotnessInfo& LayoutHotnessInfo() const { + return layout_hotness_info_; + } + private: void DumpAnnotationSetItem(dex_ir::AnnotationSetItem* set_item); void DumpBytecodes(uint32_t idx, const dex_ir::CodeItem* code, uint32_t code_offset); @@ -120,18 +132,14 @@ class DexLayout { void DumpSField(uint32_t idx, uint32_t flags, int i, dex_ir::EncodedValue* init); void DumpDexFile(); - std::vector LayoutClassDefsAndClassData(const DexFile* dex_file); - int32_t LayoutCodeItems(const DexFile* dex_file, - std::vector new_class_data_order); - int32_t LayoutStringData(const DexFile* dex_file); - bool IsNextSectionCodeItemAligned(uint32_t offset); - template void FixupSection(std::map>& map, uint32_t diff); - void FixupSections(uint32_t offset, uint32_t diff); + void LayoutClassDefsAndClassData(const DexFile* dex_file); + void LayoutCodeItems(const DexFile* dex_file); + void LayoutStringData(const DexFile* dex_file); // Creates a new layout for the dex file based on profile info. // Currently reorders ClassDefs, ClassDataItems, and CodeItems. void LayoutOutputFile(const DexFile* dex_file); - void OutputDexFile(const DexFile* dex_file); + void OutputDexFile(const DexFile* dex_file, bool compute_offsets); void DumpCFG(const DexFile* dex_file, int idx); void DumpCFG(const DexFile* dex_file, uint32_t dex_method_idx, const DexFile::CodeItem* code); @@ -142,6 +150,8 @@ class DexLayout { dex_ir::Header* header_; std::unique_ptr mem_map_; DexLayoutSections dex_sections_; + // Layout hotness information is only calculated when dexlayout is enabled. + DexLayoutHotnessInfo layout_hotness_info_; DISALLOW_COPY_AND_ASSIGN(DexLayout); }; diff --git a/runtime/dex_file_layout.cc b/runtime/dex_file_layout.cc index c3fae15b14..1973440d55 100644 --- a/runtime/dex_file_layout.cc +++ b/runtime/dex_file_layout.cc @@ -26,10 +26,10 @@ namespace art { void DexLayoutSection::Subsection::Madvise(const DexFile* dex_file, int advice) const { DCHECK(dex_file != nullptr); - DCHECK_LE(size_, dex_file->Size()); - DCHECK_LE(offset_ + size_, dex_file->Size()); - MadviseLargestPageAlignedRegion(dex_file->Begin() + offset_, - dex_file->Begin() + offset_ + size_, + DCHECK_LT(start_offset_, dex_file->Size()); + DCHECK_LE(end_offset_, dex_file->Size()); + MadviseLargestPageAlignedRegion(dex_file->Begin() + start_offset_, + dex_file->Begin() + end_offset_, advice); } @@ -69,7 +69,7 @@ std::ostream& operator<<(std::ostream& os, const DexLayoutSection& section) { for (size_t i = 0; i < static_cast(LayoutType::kLayoutTypeCount); ++i) { const DexLayoutSection::Subsection& part = section.parts_[i]; os << static_cast(i) << "(" - << part.offset_ << "-" << part.offset_ + part.size_ << ") "; + << part.start_offset_ << "-" << part.end_offset_ << ") "; } return os; } diff --git a/runtime/dex_file_layout.h b/runtime/dex_file_layout.h index 40cc91232e..4c960c3ff5 100644 --- a/runtime/dex_file_layout.h +++ b/runtime/dex_file_layout.h @@ -17,22 +17,25 @@ #ifndef ART_RUNTIME_DEX_FILE_LAYOUT_H_ #define ART_RUNTIME_DEX_FILE_LAYOUT_H_ +#include #include #include +#include "base/logging.h" + namespace art { class DexFile; enum class LayoutType : uint8_t { + // Layout of things that are hot (commonly accessed), these should be pinned or madvised will + // need. + kLayoutTypeHot, // Layout of things that are randomly used. These should be advised to random access. // Without layout, this is the default mode when loading a dex file. kLayoutTypeSometimesUsed, // Layout of things that are only used during startup, these can be madvised after launch. kLayoutTypeStartupOnly, - // Layout of things that are hot (commonly accessed), these should be pinned or madvised will - // need. - kLayoutTypeHot, // Layout of things that are needed probably only once (class initializers). These can be // madvised during trim events. kLayoutTypeUsedOnce, @@ -44,6 +47,11 @@ enum class LayoutType : uint8_t { }; std::ostream& operator<<(std::ostream& os, const LayoutType& collector_type); +// Return the "best" layout option if the same item has multiple different layouts. +static inline LayoutType MergeLayoutType(LayoutType a, LayoutType b) { + return std::min(a, b); +} + enum class MadviseState : uint8_t { // Madvise based on a file that was just loaded. kMadviseStateAtLoad, @@ -55,15 +63,35 @@ enum class MadviseState : uint8_t { std::ostream& operator<<(std::ostream& os, const MadviseState& collector_type); // A dex layout section such as code items or strings. Each section is composed of subsections -// that are layed out ajacently to each other such as (hot, unused, startup, etc...). +// that are laid out adjacently to each other such as (hot, unused, startup, etc...). class DexLayoutSection { public: // A subsection is a a continuous range of dex file that is all part of the same layout hint. class Subsection { public: // Use uint32_t to handle 32/64 bit cross compilation. - uint32_t offset_ = 0u; - uint32_t size_ = 0u; + uint32_t start_offset_ = 0u; + uint32_t end_offset_ = 0u; + + bool Contains(uint32_t offset) const { + return start_offset_ <= offset && offset < end_offset_; + } + + bool Size() const { + DCHECK_LE(start_offset_, end_offset_); + return end_offset_ - start_offset_; + } + + void CombineSection(uint32_t start_offset, uint32_t end_offset) { + DCHECK_LT(start_offset, end_offset); + if (start_offset_ == end_offset_) { + start_offset_ = start_offset; + end_offset_ = end_offset; + } else { + start_offset_ = std::min(start_offset_, start_offset); + end_offset_ = std::max(end_offset_, end_offset); + } + } void Madvise(const DexFile* dex_file, int advice) const; }; diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc index 025952f7a9..edf5650df1 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex_file_verifier.cc @@ -471,7 +471,9 @@ bool DexFileVerifier::CheckMap() { if (IsDataSectionType(item_type)) { uint32_t icount = item->size_; if (UNLIKELY(icount > data_items_left)) { - ErrorStringPrintf("Too many items in data section: %ud", data_item_count + icount); + ErrorStringPrintf("Too many items in data section: %ud item_type %zx", + data_item_count + icount, + static_cast(item_type)); return false; } data_items_left -= icount; -- GitLab From 715d672e22953eb4d4a10b1b51a5584ee9b0e94e Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 20 Nov 2017 22:28:46 +0000 Subject: [PATCH 063/226] Revert "Try to be consistent when setting fields of OatWriter::OatDexFile." Failures seen on go/lem. This reverts commit 97a042ec6e57025bbc47b480a7753fbf2307b5b8. Change-Id: I17352f6a0956d6ab95bffdfd2aa5286334cc1206 --- dex2oat/linker/oat_writer.cc | 80 ++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 44 deletions(-) diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 909ba22f21..d3e920f9a0 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -295,46 +295,29 @@ class OatWriter::OatDexFile { // Whether to create the type lookup table. CreateTypeLookupTable create_type_lookup_table_; - // Dex file size. Initialized when copying the dex file in the - // WriteDexFile methods. + // Dex file size. Initialized when writing the dex file. size_t dex_file_size_; // Offset of start of OatDexFile from beginning of OatHeader. It is // used to validate file position when writing. size_t offset_; - ///// Start of data to write to vdex/oat file. - - const uint32_t dex_file_location_size_; - const char* const dex_file_location_data_; - - // The checksum of the dex file. Initialized when adding a DexFile - // source. + // Data to write. + uint32_t dex_file_location_size_; + const char* dex_file_location_data_; uint32_t dex_file_location_checksum_; - - // Offset of the dex file in the vdex file. Set when writing dex files in - // SeekToDexFile. uint32_t dex_file_offset_; - - // The lookup table offset in the oat file. Set in WriteTypeLookupTables. - uint32_t lookup_table_offset_; - - // Offset of dex sections that will have different runtime madvise states. - // Set in WriteDexLayoutSections. - uint32_t dex_sections_layout_offset_; - - // Class and BSS offsets set in PrepareLayout. uint32_t class_offsets_offset_; + uint32_t lookup_table_offset_; uint32_t method_bss_mapping_offset_; + uint32_t dex_sections_layout_offset_; - // Data to write to a separate section. We set the length - // of the vector in OpenDexFiles. + // Data to write to a separate section. dchecked_vector class_offsets_; // Dex section layout info to serialize. DexLayoutSections dex_sections_layout_; - ///// End of data to write to vdex/oat file. private: DISALLOW_COPY_AND_ASSIGN(OatDexFile); }; @@ -481,8 +464,6 @@ bool OatWriter::AddZippedDexFilesSource(File&& zip_fd, oat_dex_files_.emplace_back(full_location, DexFileSource(zipped_dex_files_.back().get()), create_type_lookup_table); - // Override the checksum from header with the CRC from ZIP entry. - oat_dex_files_.back().dex_file_location_checksum_ = zipped_dex_files_.back()->GetCrc32(); } if (zipped_dex_file_locations_.empty()) { LOG(ERROR) << "No dex files in zip file '" << location << "': " << error_msg; @@ -3077,6 +3058,8 @@ bool OatWriter::ReadDexFileHeader(File* file, OatDexFile* oat_dex_file) { const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_header); oat_dex_file->dex_file_size_ = header->file_size_; + oat_dex_file->dex_file_location_checksum_ = header->checksum_; + oat_dex_file->class_offsets_.resize(header->class_defs_size_); return true; } @@ -3257,6 +3240,8 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil return false; } oat_dex_file->dex_sections_layout_ = dex_layout.GetSections(); + // Set the checksum of the new oat dex file to be the original file's checksum. + oat_dex_file->dex_file_location_checksum_ = dex_file->GetLocationChecksum(); return true; } @@ -3306,7 +3291,6 @@ bool OatWriter::WriteDexFile(OutputStream* out, << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); return false; } - // Read the dex file header to get the dex file size. if (!ReadDexFileHeader(file, oat_dex_file)) { return false; } @@ -3317,6 +3301,9 @@ bool OatWriter::WriteDexFile(OutputStream* out, return false; } + // Override the checksum from header with the CRC from ZIP entry. + oat_dex_file->dex_file_location_checksum_ = dex_file->GetCrc32(); + // Seek both file and stream to the end offset. size_t end_offset = start_offset + oat_dex_file->dex_file_size_; actual_offset = lseek(file->Fd(), end_offset, SEEK_SET); @@ -3365,7 +3352,6 @@ bool OatWriter::WriteDexFile(OutputStream* out, << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); return false; } - // Read the dex file header to get the dex file size. if (!ReadDexFileHeader(dex_file, oat_dex_file)) { return false; } @@ -3432,7 +3418,10 @@ bool OatWriter::WriteDexFile(OutputStream* out, } // Update dex file size and resize class offsets in the OatDexFile. + // Note: For raw data, the checksum is passed directly to AddRawDexFileSource(). + // Note: For vdex, the checksum is copied from the existing vdex file. oat_dex_file->dex_file_size_ = header->file_size_; + oat_dex_file->class_offsets_.resize(header->class_defs_size_); return true; } @@ -3468,22 +3457,29 @@ bool OatWriter::OpenDexFiles( } std::vector> dex_files; for (OatDexFile& oat_dex_file : oat_dex_files_) { + // Make sure no one messed with input files while we were copying data. + // At the very least we need consistent file size and number of class definitions. const uint8_t* raw_dex_file = dex_files_map->Begin() + oat_dex_file.dex_file_offset_ - map_offset; - - if (kIsDebugBuild) { - // Sanity check our input files. - // Note that ValidateDexFileHeader() logs error messages. - CHECK(ValidateDexFileHeader(raw_dex_file, oat_dex_file.GetLocation())) - << "Failed to verify written dex file header!" + if (!ValidateDexFileHeader(raw_dex_file, oat_dex_file.GetLocation())) { + // Note: ValidateDexFileHeader() already logged an error message. + LOG(ERROR) << "Failed to verify written dex file header!" << " Output: " << file->GetPath() << " ~ " << std::hex << map_offset << " ~ " << static_cast(raw_dex_file); - - const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file); - CHECK_EQ(header->file_size_, oat_dex_file.dex_file_size_) - << "File size mismatch in written dex file header! Expected: " + return false; + } + const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file); + if (header->file_size_ != oat_dex_file.dex_file_size_) { + LOG(ERROR) << "File size mismatch in written dex file header! Expected: " << oat_dex_file.dex_file_size_ << " Actual: " << header->file_size_ << " Output: " << file->GetPath(); + return false; + } + if (header->class_defs_size_ != oat_dex_file.class_offsets_.size()) { + LOG(ERROR) << "Class defs size mismatch in written dex file header! Expected: " + << oat_dex_file.class_offsets_.size() << " Actual: " << header->class_defs_size_ + << " Output: " << file->GetPath(); + return false; } // Now, open the dex file. @@ -3500,10 +3496,6 @@ bool OatWriter::OpenDexFiles( << " Error: " << error_msg; return false; } - - // Set the class_offsets size now that we have easy access to the DexFile and - // it has been verified in DexFileLoader::Open. - oat_dex_file.class_offsets_.resize(dex_files.back()->GetHeader().class_defs_size_); } *opened_dex_files_map = std::move(dex_files_map); @@ -3750,10 +3742,10 @@ OatWriter::OatDexFile::OatDexFile(const char* dex_file_location, dex_file_location_data_(dex_file_location), dex_file_location_checksum_(0u), dex_file_offset_(0u), - lookup_table_offset_(0u), - dex_sections_layout_offset_(0u), class_offsets_offset_(0u), + lookup_table_offset_(0u), method_bss_mapping_offset_(0u), + dex_sections_layout_offset_(0u), class_offsets_() { } -- GitLab From af9341087aab0146b8323ece156bde8130948465 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Mon, 20 Nov 2017 23:58:35 +0000 Subject: [PATCH 064/226] Revert "Make JVMTI DisposeEnvironment and GetEnv thread safe." This reverts commit e5a2ae30bdbe379695dc886861b23dce57de0825. Reason for revert: fails art-gc-gss-tlab column. Test: None Bug: 69465262 Change-Id: I70af77297bc7870d281ed8ffb319d144ddb12838 --- openjdkjvmti/art_jvmti.h | 4 - openjdkjvmti/events-inl.h | 277 +++++++----------- openjdkjvmti/events.cc | 21 +- openjdkjvmti/events.h | 67 ++--- openjdkjvmti/object_tagging.cc | 3 +- openjdkjvmti/ti_dump.cc | 2 +- openjdkjvmti/ti_phase.cc | 11 +- runtime/base/mutex-inl.h | 4 +- runtime/base/mutex.h | 5 - runtime/ti/agent.cc | 3 - test/1941-dispose-stress/dispose_stress.cc | 59 ---- test/1941-dispose-stress/expected.txt | 1 - test/1941-dispose-stress/info.txt | 3 - test/1941-dispose-stress/run | 18 -- test/1941-dispose-stress/src/Main.java | 21 -- .../src/art/Breakpoint.java | 202 ------------- .../1941-dispose-stress/src/art/Test1941.java | 72 ----- test/1941-dispose-stress/src/art/Trace.java | 68 ----- test/Android.bp | 1 - 19 files changed, 142 insertions(+), 700 deletions(-) delete mode 100644 test/1941-dispose-stress/dispose_stress.cc delete mode 100644 test/1941-dispose-stress/expected.txt delete mode 100644 test/1941-dispose-stress/info.txt delete mode 100755 test/1941-dispose-stress/run delete mode 100644 test/1941-dispose-stress/src/Main.java delete mode 100644 test/1941-dispose-stress/src/art/Breakpoint.java delete mode 100644 test/1941-dispose-stress/src/art/Test1941.java delete mode 100644 test/1941-dispose-stress/src/art/Trace.java diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h index e8e62c2b40..682b82b5cd 100644 --- a/openjdkjvmti/art_jvmti.h +++ b/openjdkjvmti/art_jvmti.h @@ -94,10 +94,6 @@ struct ArtJvmTiEnv : public jvmtiEnv { static ArtJvmTiEnv* AsArtJvmTiEnv(jvmtiEnv* env) { return art::down_cast(env); } - - // Top level lock. Nothing can be held when we get this except for mutator lock for full - // thread-suspension. - static art::Mutex *gEnvMutex ACQUIRED_AFTER(art::Locks::mutator_lock_); }; // Macro and constexpr to make error values less annoying to write. diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h index 007669b50f..5344e0fbde 100644 --- a/openjdkjvmti/events-inl.h +++ b/openjdkjvmti/events-inl.h @@ -46,45 +46,6 @@ static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e) { namespace impl { -// Helper for ensuring that the dispatch environment is sane. Events with JNIEnvs need to stash -// pending exceptions since they can cause new ones to be thrown. In accordance with the JVMTI -// specification we allow exceptions originating from events to overwrite the current exception, -// including exceptions originating from earlier events. -class ScopedEventDispatchEnvironment FINAL : public art::ValueObject { - public: - ScopedEventDispatchEnvironment() : env_(nullptr), throw_(nullptr, nullptr) { - DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative); - } - - explicit ScopedEventDispatchEnvironment(JNIEnv* env) - : env_(env), - throw_(env_, env_->ExceptionOccurred()) { - DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative); - // The spec doesn't say how much local data should be there, so we just give 128 which seems - // likely to be enough for most cases. - env_->PushLocalFrame(128); - env_->ExceptionClear(); - } - - ~ScopedEventDispatchEnvironment() { - if (env_ != nullptr) { - if (throw_.get() != nullptr && !env_->ExceptionCheck()) { - // TODO It would be nice to add the overwritten exceptions to the suppressed exceptions list - // of the newest exception. - env_->Throw(throw_.get()); - } - env_->PopLocalFrame(nullptr); - } - DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative); - } - - private: - JNIEnv* env_; - ScopedLocalRef throw_; - - DISALLOW_COPY_AND_ASSIGN(ScopedEventDispatchEnvironment); -}; - // Infrastructure to achieve type safety for event dispatch. #define FORALL_EVENT_TYPES(fn) \ @@ -136,68 +97,27 @@ FORALL_EVENT_TYPES(EVENT_FN_TYPE) #undef EVENT_FN_TYPE -#define MAKE_EVENT_HANDLER_FUNC(name, enum_name) \ -template<> \ -struct EventHandlerFunc { \ - using EventFnType = typename impl::EventFnType::type; \ - explicit EventHandlerFunc(ArtJvmTiEnv* env) \ - : env_(env), \ - fn_(env_->event_callbacks == nullptr ? nullptr : env_->event_callbacks->name) { } \ - \ - template \ - ALWAYS_INLINE \ - void ExecuteCallback(JNIEnv* jnienv, Args... args) const { \ - if (fn_ != nullptr) { \ - ScopedEventDispatchEnvironment sede(jnienv); \ - DoExecute(jnienv, args...); \ - } \ - } \ - \ - template \ - ALWAYS_INLINE \ - void ExecuteCallback(Args... args) const { \ - if (fn_ != nullptr) { \ - ScopedEventDispatchEnvironment sede; \ - DoExecute(args...); \ - } \ - } \ - \ - private: \ - template \ - ALWAYS_INLINE \ - inline void DoExecute(Args... args) const { \ - static_assert(std::is_same::value, \ - "Unexpected different type of ExecuteCallback"); \ - fn_(env_, args...); \ - } \ - \ - public: \ - ArtJvmTiEnv* env_; \ - EventFnType fn_; \ -}; +template +ALWAYS_INLINE inline typename EventFnType::type GetCallback(ArtJvmTiEnv* env); + +#define GET_CALLBACK(name, enum_name) \ +template <> \ +ALWAYS_INLINE inline EventFnType::type GetCallback( \ + ArtJvmTiEnv* env) { \ + if (env->event_callbacks == nullptr) { \ + return nullptr; \ + } \ + return env->event_callbacks->name; \ +} -FORALL_EVENT_TYPES(MAKE_EVENT_HANDLER_FUNC) +FORALL_EVENT_TYPES(GET_CALLBACK) -#undef MAKE_EVENT_HANDLER_FUNC +#undef GET_CALLBACK #undef FORALL_EVENT_TYPES } // namespace impl -template -inline std::vector> EventHandler::CollectEvents(art::Thread* thread, - Args... args) const { - art::MutexLock mu(thread, envs_lock_); - std::vector> handlers; - for (ArtJvmTiEnv* env : envs) { - if (ShouldDispatch(env, thread, args...)) { - impl::EventHandlerFunc h(env); - handlers.push_back(h); - } - } - return handlers; -} - // C++ does not allow partial template function specialization. The dispatch for our separated // ClassFileLoadHook event types is the same, so use this helper for code deduplication. template @@ -211,37 +131,29 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, const unsigned char* class_data, jint* new_class_data_len, unsigned char** new_class_data) const { - art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable || kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable, "Unsupported event"); DCHECK(*new_class_data == nullptr); jint current_len = class_data_len; unsigned char* current_class_data = const_cast(class_data); - std::vector> handlers = - CollectEvents(thread, - jnienv, - class_being_redefined, - loader, - name, - protection_domain, - class_data_len, - class_data, - new_class_data_len, - new_class_data); ArtJvmTiEnv* last_env = nullptr; - for (const impl::EventHandlerFunc& event : handlers) { + for (ArtJvmTiEnv* env : envs) { + if (env == nullptr) { + continue; + } jint new_len = 0; unsigned char* new_data = nullptr; - ExecuteCallback(event, - jnienv, - class_being_redefined, - loader, - name, - protection_domain, - current_len, - static_cast(current_class_data), - &new_len, - &new_data); + DispatchEventOnEnv(env, + thread, + jnienv, + class_being_redefined, + loader, + name, + protection_domain, + current_len, + static_cast(current_class_data), + &new_len, + &new_data); if (new_data != nullptr && new_data != current_class_data) { // Destroy the data the last transformer made. We skip this if the previous state was the // initial one since we don't know here which jvmtiEnv allocated it. @@ -250,7 +162,7 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, if (last_env != nullptr) { last_env->Deallocate(current_class_data); } - last_env = event.env_; + last_env = env; current_class_data = new_data; current_len = new_len; } @@ -264,28 +176,70 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, // Our goal for DispatchEvent: Do not allow implicit type conversion. Types of ...args must match // exactly the argument types of the corresponding Jvmti kEvent function pointer. +template +inline void EventHandler::ExecuteCallback(ArtJvmTiEnv* env, Args... args) { + using FnType = typename impl::EventFnType::type; + FnType callback = impl::GetCallback(env); + if (callback != nullptr) { + (*callback)(env, args...); + } +} + template inline void EventHandler::DispatchEvent(art::Thread* thread, Args... args) const { - art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); static_assert(!std::is_same>>>::value, "Should be calling DispatchEvent with explicit JNIEnv* argument!"); DCHECK(thread == nullptr || !thread->IsExceptionPending()); - std::vector> events = CollectEvents(thread, args...); - for (auto event : events) { - ExecuteCallback(event, args...); + for (ArtJvmTiEnv* env : envs) { + if (env != nullptr) { + DispatchEventOnEnv(env, thread, args...); + } } } +// Helper for ensuring that the dispatch environment is sane. Events with JNIEnvs need to stash +// pending exceptions since they can cause new ones to be thrown. In accordance with the JVMTI +// specification we allow exceptions originating from events to overwrite the current exception, +// including exceptions originating from earlier events. +class ScopedEventDispatchEnvironment FINAL : public art::ValueObject { + public: + explicit ScopedEventDispatchEnvironment(JNIEnv* env) + : env_(env), + thr_(env_, env_->ExceptionOccurred()), + suspend_(art::Thread::Current(), art::kNative) { + // The spec doesn't say how much local data should be there, so we just give 128 which seems + // likely to be enough for most cases. + env_->PushLocalFrame(128); + env_->ExceptionClear(); + UNUSED(suspend_); + } + + ~ScopedEventDispatchEnvironment() { + if (thr_.get() != nullptr && !env_->ExceptionCheck()) { + // TODO It would be nice to add the overwritten exceptions to the suppressed exceptions list + // of the newest exception. + env_->Throw(thr_.get()); + } + env_->PopLocalFrame(nullptr); + } + + private: + JNIEnv* env_; + ScopedLocalRef thr_; + // Not actually unused. The destructor/constructor does important work. + art::ScopedThreadStateChange suspend_; + + DISALLOW_COPY_AND_ASSIGN(ScopedEventDispatchEnvironment); +}; + template inline void EventHandler::DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const { - art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); - std::vector> events = CollectEvents(thread, - jnienv, - args...); - for (auto event : events) { - ExecuteCallback(event, jnienv, args...); + for (ArtJvmTiEnv* env : envs) { + if (env != nullptr) { + DispatchEventOnEnv(env, thread, jnienv, args...); + } } } @@ -294,9 +248,8 @@ inline void EventHandler::DispatchEventOnEnv( ArtJvmTiEnv* env, art::Thread* thread, JNIEnv* jnienv, Args... args) const { DCHECK(env != nullptr); if (ShouldDispatch(env, thread, jnienv, args...)) { - art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); - impl::EventHandlerFunc func(env); - ExecuteCallback(func, jnienv, args...); + ScopedEventDispatchEnvironment sede(jnienv); + ExecuteCallback(env, jnienv, args...); } } @@ -307,26 +260,11 @@ inline void EventHandler::DispatchEventOnEnv( typename std::decay_t< std::tuple_element_t<0, std::tuple>>>::value, "Should be calling DispatchEventOnEnv with explicit JNIEnv* argument!"); - DCHECK(env != nullptr); - if (ShouldDispatch(env, thread, args...)) { - art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); - impl::EventHandlerFunc func(env); - ExecuteCallback(func, args...); + if (ShouldDispatch(env, thread, args...)) { + ExecuteCallback(env, args...); } } -template -inline void EventHandler::ExecuteCallback(impl::EventHandlerFunc handler, Args... args) { - handler.ExecuteCallback(args...); -} - -template -inline void EventHandler::ExecuteCallback(impl::EventHandlerFunc handler, - JNIEnv* jnienv, - Args... args) { - handler.ExecuteCallback(jnienv, args...); -} - // Events that need custom logic for if we send the event but are otherwise normal. This includes // the kBreakpoint, kFramePop, kFieldAccess, and kFieldModification events. @@ -409,13 +347,14 @@ inline bool EventHandler::ShouldDispatch( // something. template <> inline void EventHandler::ExecuteCallback( - impl::EventHandlerFunc event, + ArtJvmTiEnv* env, JNIEnv* jnienv, jthread jni_thread, jmethodID jmethod, jboolean is_exception, const art::ShadowFrame* frame ATTRIBUTE_UNUSED) { - ExecuteCallback(event, jnienv, jni_thread, jmethod, is_exception); + ExecuteCallback( + env, jnienv, jni_thread, jmethod, is_exception); } // Need to give a custom specialization for NativeMethodBind since it has to deal with an out @@ -427,25 +366,20 @@ inline void EventHandler::DispatchEvent(art::T jmethodID method, void* cur_method, void** new_method) const { - art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); - std::vector> events = - CollectEvents(thread, - jnienv, - jni_thread, - method, - cur_method, - new_method); *new_method = cur_method; - for (auto event : events) { - *new_method = cur_method; - ExecuteCallback(event, - jnienv, - jni_thread, - method, - cur_method, - new_method); - if (*new_method != nullptr) { - cur_method = *new_method; + for (ArtJvmTiEnv* env : envs) { + if (env != nullptr) { + *new_method = cur_method; + DispatchEventOnEnv(env, + thread, + jnienv, + jni_thread, + method, + cur_method, + new_method); + if (*new_method != nullptr) { + cur_method = *new_method; + } } } *new_method = cur_method; @@ -505,7 +439,7 @@ inline void EventHandler::DispatchEvent -inline bool EventHandler::ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) const { +inline bool EventHandler::ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) { bool dispatch = env->event_masks.global_event_mask.Test(kEvent); if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(kEvent)) { @@ -527,11 +461,6 @@ inline bool EventHandler::ShouldDispatch(ArtJvmTiEnv* env, } inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) { - art::MutexLock mu(art::Thread::Current(), envs_lock_); - RecalculateGlobalEventMaskLocked(event); -} - -inline void EventHandler::RecalculateGlobalEventMaskLocked(ArtJvmtiEvent event) { bool union_value = false; for (const ArtJvmTiEnv* stored_env : envs) { if (stored_env == nullptr) { diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index be4ebbc85e..d1d606de48 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -193,21 +193,25 @@ void EventMasks::HandleChangedCapabilities(const jvmtiCapabilities& caps, bool c } void EventHandler::RegisterArtJvmTiEnv(ArtJvmTiEnv* env) { - art::MutexLock mu(art::Thread::Current(), envs_lock_); - envs.push_back(env); + // Since we never shrink this array we might as well try to fill gaps. + auto it = std::find(envs.begin(), envs.end(), nullptr); + if (it != envs.end()) { + *it = env; + } else { + envs.push_back(env); + } } void EventHandler::RemoveArtJvmTiEnv(ArtJvmTiEnv* env) { - art::MutexLock mu(art::Thread::Current(), envs_lock_); // Since we might be currently iterating over the envs list we cannot actually erase elements. // Instead we will simply replace them with 'nullptr' and skip them manually. auto it = std::find(envs.begin(), envs.end(), env); if (it != envs.end()) { - envs.erase(it); + *it = nullptr; for (size_t i = static_cast(ArtJvmtiEvent::kMinEventTypeVal); i <= static_cast(ArtJvmtiEvent::kMaxEventTypeVal); ++i) { - RecalculateGlobalEventMaskLocked(static_cast(i)); + RecalculateGlobalEventMask(static_cast(i)); } } } @@ -427,11 +431,11 @@ class JvmtiGcPauseListener : public art::gc::GcPauseListener { finish_enabled_(false) {} void StartPause() OVERRIDE { - handler_->DispatchEvent(art::Thread::Current()); + handler_->DispatchEvent(nullptr); } void EndPause() OVERRIDE { - handler_->DispatchEvent(art::Thread::Current()); + handler_->DispatchEvent(nullptr); } bool IsEnabled() { @@ -1172,8 +1176,7 @@ void EventHandler::Shutdown() { art::Runtime::Current()->GetInstrumentation()->RemoveListener(method_trace_listener_.get(), ~0); } -EventHandler::EventHandler() : envs_lock_("JVMTI Environment List Lock", - art::LockLevel::kTopLockLevel) { +EventHandler::EventHandler() { alloc_listener_.reset(new JvmtiAllocationListener(this)); ddm_listener_.reset(new JvmtiDdmChunkListener(this)); gc_pause_listener_.reset(new JvmtiGcPauseListener(this)); diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h index c73215f07b..a99ed7b212 100644 --- a/openjdkjvmti/events.h +++ b/openjdkjvmti/events.h @@ -158,10 +158,6 @@ struct EventMasks { void HandleChangedCapabilities(const jvmtiCapabilities& caps, bool caps_added); }; -namespace impl { -template struct EventHandlerFunc { }; -} // namespace impl - // Helper class for event handling. class EventHandler { public: @@ -173,10 +169,10 @@ class EventHandler { // Register an env. It is assumed that this happens on env creation, that is, no events are // enabled, yet. - void RegisterArtJvmTiEnv(ArtJvmTiEnv* env) REQUIRES(!envs_lock_); + void RegisterArtJvmTiEnv(ArtJvmTiEnv* env); // Remove an env. - void RemoveArtJvmTiEnv(ArtJvmTiEnv* env) REQUIRES(!envs_lock_); + void RemoveArtJvmTiEnv(ArtJvmTiEnv* env); bool IsEventEnabledAnywhere(ArtJvmtiEvent event) const { if (!EventMask::EventIsInRange(event)) { @@ -188,15 +184,13 @@ class EventHandler { jvmtiError SetEvent(ArtJvmTiEnv* env, art::Thread* thread, ArtJvmtiEvent event, - jvmtiEventMode mode) - REQUIRES(!envs_lock_); + jvmtiEventMode mode); // Dispatch event to all registered environments. Since this one doesn't have a JNIEnv* it doesn't // matter if it has the mutator_lock. template ALWAYS_INLINE - inline void DispatchEvent(art::Thread* thread, Args... args) const - REQUIRES(!envs_lock_); + inline void DispatchEvent(art::Thread* thread, Args... args) const; // Dispatch event to all registered environments stashing exceptions as needed. This works since // JNIEnv* is always the second argument if it is passed to an event. Needed since C++ does not @@ -206,8 +200,7 @@ class EventHandler { // the event to allocate local references. template ALWAYS_INLINE - inline void DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const - REQUIRES(!envs_lock_); + inline void DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const; // Tell the event handler capabilities were added/lost so it can adjust the sent events.If // caps_added is true then caps is all the newly set capabilities of the jvmtiEnv. If it is false @@ -215,48 +208,28 @@ class EventHandler { ALWAYS_INLINE inline void HandleChangedCapabilities(ArtJvmTiEnv* env, const jvmtiCapabilities& caps, - bool added) - REQUIRES(!envs_lock_); + bool added); // Dispatch event to the given environment, only. template ALWAYS_INLINE - inline void DispatchEventOnEnv(ArtJvmTiEnv* env, - art::Thread* thread, - JNIEnv* jnienv, - Args... args) const - REQUIRES(!envs_lock_); + inline void DispatchEventOnEnv( + ArtJvmTiEnv* env, art::Thread* thread, JNIEnv* jnienv, Args... args) const; // Dispatch event to the given environment, only. template ALWAYS_INLINE - inline void DispatchEventOnEnv(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const - REQUIRES(!envs_lock_); + inline void DispatchEventOnEnv(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const; private: - template - ALWAYS_INLINE - inline std::vector> CollectEvents(art::Thread* thread, - Args... args) const - REQUIRES(!envs_lock_); - template ALWAYS_INLINE - inline bool ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) const; + static inline bool ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread); template ALWAYS_INLINE - static inline void ExecuteCallback(impl::EventHandlerFunc handler, - JNIEnv* env, - Args... args) - REQUIRES(!envs_lock_); + static inline void ExecuteCallback(ArtJvmTiEnv* env, Args... args); - template - ALWAYS_INLINE - static inline void ExecuteCallback(impl::EventHandlerFunc handler, Args... args) - REQUIRES(!envs_lock_); - - // Public for use to collect dispatches template ALWAYS_INLINE inline bool ShouldDispatch(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const; @@ -268,9 +241,7 @@ class EventHandler { // Recalculates the event mask for the given event. ALWAYS_INLINE - inline void RecalculateGlobalEventMask(ArtJvmtiEvent event) REQUIRES(!envs_lock_); - ALWAYS_INLINE - inline void RecalculateGlobalEventMaskLocked(ArtJvmtiEvent event) REQUIRES(envs_lock_); + inline void RecalculateGlobalEventMask(ArtJvmtiEvent event); template ALWAYS_INLINE inline void DispatchClassFileLoadHookEvent(art::Thread* thread, @@ -282,8 +253,7 @@ class EventHandler { jint class_data_len, const unsigned char* class_data, jint* new_class_data_len, - unsigned char** new_class_data) const - REQUIRES(!envs_lock_); + unsigned char** new_class_data) const; void HandleEventType(ArtJvmtiEvent event, bool enable); void HandleLocalAccessCapabilityAdded(); @@ -291,13 +261,10 @@ class EventHandler { bool OtherMonitorEventsEnabledAnywhere(ArtJvmtiEvent event); - // List of all JvmTiEnv objects that have been created, in their creation order. It is a std::list - // since we mostly access it by iterating over the entire thing, only ever append to the end, and - // need to be able to remove arbitrary elements from it. - std::list envs GUARDED_BY(envs_lock_); - - // Top level lock. Nothing at all should be held when we lock this. - mutable art::Mutex envs_lock_ ACQUIRED_BEFORE(art::Locks::instrument_entrypoints_lock_); + // List of all JvmTiEnv objects that have been created, in their creation order. + // NB Some elements might be null representing envs that have been deleted. They should be skipped + // anytime this list is used. + std::vector envs; // A union of all enabled events, anywhere. EventMask global_mask; diff --git a/openjdkjvmti/object_tagging.cc b/openjdkjvmti/object_tagging.cc index ba242ef1e8..6ba7165577 100644 --- a/openjdkjvmti/object_tagging.cc +++ b/openjdkjvmti/object_tagging.cc @@ -61,8 +61,7 @@ bool ObjectTagTable::DoesHandleNullOnSweep() { return event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kObjectFree); } void ObjectTagTable::HandleNullSweep(jlong tag) { - event_handler_->DispatchEventOnEnv( - jvmti_env_, art::Thread::Current(), tag); + event_handler_->DispatchEventOnEnv(jvmti_env_, nullptr, tag); } } // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_dump.cc b/openjdkjvmti/ti_dump.cc index 253580e0e1..809a5e47bb 100644 --- a/openjdkjvmti/ti_dump.cc +++ b/openjdkjvmti/ti_dump.cc @@ -47,7 +47,7 @@ struct DumpCallback : public art::RuntimeSigQuitCallback { void SigQuit() OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { art::Thread* thread = art::Thread::Current(); art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative); - event_handler->DispatchEvent(art::Thread::Current()); + event_handler->DispatchEvent(nullptr); } EventHandler* event_handler = nullptr; diff --git a/openjdkjvmti/ti_phase.cc b/openjdkjvmti/ti_phase.cc index 7157974c13..23df27fbda 100644 --- a/openjdkjvmti/ti_phase.cc +++ b/openjdkjvmti/ti_phase.cc @@ -57,7 +57,6 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { } void NextRuntimePhase(RuntimePhase phase) REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE { - art::Thread* self = art::Thread::Current(); switch (phase) { case RuntimePhase::kInitialAgents: PhaseUtil::current_phase_ = JVMTI_PHASE_PRIMORDIAL; @@ -65,7 +64,8 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { case RuntimePhase::kStart: { PhaseUtil::current_phase_ = JVMTI_PHASE_START; - event_handler->DispatchEvent(self, GetJniEnv()); + art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); + event_handler->DispatchEvent(nullptr, GetJniEnv()); } break; case RuntimePhase::kInit: @@ -74,7 +74,9 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE; { ScopedLocalRef thread(GetJniEnv(), GetCurrentJThread()); - event_handler->DispatchEvent(self, GetJniEnv(), thread.get()); + art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); + event_handler->DispatchEvent( + nullptr, GetJniEnv(), thread.get()); } // We need to have these events be ordered to match behavior expected by some real-world // agents. The spec does not really require this but compatibility is a useful property to @@ -84,7 +86,8 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { break; case RuntimePhase::kDeath: { - event_handler->DispatchEvent(self, GetJniEnv()); + art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); + event_handler->DispatchEvent(nullptr, GetJniEnv()); PhaseUtil::current_phase_ = JVMTI_PHASE_DEAD; } // TODO: Block events now. diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h index 39ad3d0c89..c9d48ff7f7 100644 --- a/runtime/base/mutex-inl.h +++ b/runtime/base/mutex-inl.h @@ -80,9 +80,7 @@ static inline void CheckUnattachedThread(LockLevel level) NO_THREAD_SAFETY_ANALY // (see Thread::TransitionFromSuspendedToRunnable). level == kThreadSuspendCountLock || // Avoid recursive death. - level == kAbortLock || - // Locks at the absolute top of the stack can be locked at any time. - level == kTopLockLevel) << level; + level == kAbortLock) << level; } } diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 43ea3a2053..87c4afe96f 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -122,11 +122,6 @@ enum LockLevel { kInstrumentEntrypointsLock, kZygoteCreationLock, - // The highest valid lock level. Use this if there is code that should only be called with no - // other locks held. Since this is the highest lock level we also allow it to be held even if the - // runtime or current thread is not fully set-up yet (for example during thread attach). - kTopLockLevel, - kLockLevelCount // Must come last. }; std::ostream& operator<<(std::ostream& os, const LockLevel& rhs); diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc index 548752e980..3bf169ad40 100644 --- a/runtime/ti/agent.cc +++ b/runtime/ti/agent.cc @@ -21,8 +21,6 @@ #include "base/strlcpy.h" #include "java_vm_ext.h" #include "runtime.h" -#include "thread-current-inl.h" -#include "scoped_thread_state_change-inl.h" namespace art { namespace ti { @@ -37,7 +35,6 @@ const char* AGENT_ON_UNLOAD_FUNCTION_NAME = "Agent_OnUnload"; Agent::LoadError Agent::DoLoadHelper(bool attaching, /*out*/jint* call_res, /*out*/std::string* error_msg) { - ScopedThreadStateChange stsc(Thread::Current(), ThreadState::kNative); DCHECK(call_res != nullptr); DCHECK(error_msg != nullptr); diff --git a/test/1941-dispose-stress/dispose_stress.cc b/test/1941-dispose-stress/dispose_stress.cc deleted file mode 100644 index e8fcc775e9..0000000000 --- a/test/1941-dispose-stress/dispose_stress.cc +++ /dev/null @@ -1,59 +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. - */ - -#include - -#include "android-base/logging.h" -#include "jni.h" -#include "scoped_local_ref.h" -#include "scoped_primitive_array.h" - -#include "jvmti.h" - -// Test infrastructure -#include "jvmti_helper.h" -#include "test_env.h" - -namespace art { -namespace Test1941DisposeStress { - -extern "C" JNIEXPORT jlong JNICALL Java_art_Test1941_AllocEnv(JNIEnv* env, jclass) { - JavaVM* vm = nullptr; - if (env->GetJavaVM(&vm) != 0) { - ScopedLocalRef rt_exception(env, env->FindClass("java/lang/RuntimeException")); - env->ThrowNew(rt_exception.get(), "Unable to get JavaVM"); - return -1; - } - jvmtiEnv* new_env = nullptr; - if (vm->GetEnv(reinterpret_cast(&new_env), JVMTI_VERSION_1_0) != 0) { - ScopedLocalRef rt_exception(env, env->FindClass("java/lang/RuntimeException")); - env->ThrowNew(rt_exception.get(), "Unable to create new jvmtiEnv"); - return -1; - } - return static_cast(reinterpret_cast(new_env)); -} - -extern "C" JNIEXPORT void JNICALL Java_art_Test1941_FreeEnv(JNIEnv* env, - jclass, - jlong jvmti_env_ptr) { - JvmtiErrorToException(env, - jvmti_env, - reinterpret_cast(jvmti_env_ptr)->DisposeEnvironment()); -} - -} // namespace Test1941DisposeStress -} // namespace art - diff --git a/test/1941-dispose-stress/expected.txt b/test/1941-dispose-stress/expected.txt deleted file mode 100644 index ca2eddc7b8..0000000000 --- a/test/1941-dispose-stress/expected.txt +++ /dev/null @@ -1 +0,0 @@ -fib(20) is 6765 diff --git a/test/1941-dispose-stress/info.txt b/test/1941-dispose-stress/info.txt deleted file mode 100644 index e4a584e46f..0000000000 --- a/test/1941-dispose-stress/info.txt +++ /dev/null @@ -1,3 +0,0 @@ -Test basic JVMTI single step functionality. - -Ensures that we can receive single step events from JVMTI. diff --git a/test/1941-dispose-stress/run b/test/1941-dispose-stress/run deleted file mode 100755 index 51875a7e86..0000000000 --- a/test/1941-dispose-stress/run +++ /dev/null @@ -1,18 +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. - -# Ask for stack traces to be dumped to a file rather than to stdout. -./default-run "$@" --jvmti diff --git a/test/1941-dispose-stress/src/Main.java b/test/1941-dispose-stress/src/Main.java deleted file mode 100644 index 2fe6b818a0..0000000000 --- a/test/1941-dispose-stress/src/Main.java +++ /dev/null @@ -1,21 +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. - */ - -public class Main { - public static void main(String[] args) throws Exception { - art.Test1941.run(); - } -} diff --git a/test/1941-dispose-stress/src/art/Breakpoint.java b/test/1941-dispose-stress/src/art/Breakpoint.java deleted file mode 100644 index bbb89f707f..0000000000 --- a/test/1941-dispose-stress/src/art/Breakpoint.java +++ /dev/null @@ -1,202 +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.lang.reflect.Executable; -import java.util.HashSet; -import java.util.Set; -import java.util.Objects; - -public class Breakpoint { - public static class Manager { - public static class BP { - public final Executable method; - public final long location; - - public BP(Executable method) { - this(method, getStartLocation(method)); - } - - public BP(Executable method, long location) { - this.method = method; - this.location = location; - } - - @Override - public boolean equals(Object other) { - return (other instanceof BP) && - method.equals(((BP)other).method) && - location == ((BP)other).location; - } - - @Override - public String toString() { - return method.toString() + " @ " + getLine(); - } - - @Override - public int hashCode() { - return Objects.hash(method, location); - } - - public int getLine() { - try { - LineNumber[] lines = getLineNumberTable(method); - int best = -1; - for (LineNumber l : lines) { - if (l.location > location) { - break; - } else { - best = l.line; - } - } - return best; - } catch (Exception e) { - return -1; - } - } - } - - private Set breaks = new HashSet<>(); - - public void setBreakpoints(BP... bs) { - for (BP b : bs) { - if (breaks.add(b)) { - Breakpoint.setBreakpoint(b.method, b.location); - } - } - } - public void setBreakpoint(Executable method, long location) { - setBreakpoints(new BP(method, location)); - } - - public void clearBreakpoints(BP... bs) { - for (BP b : bs) { - if (breaks.remove(b)) { - Breakpoint.clearBreakpoint(b.method, b.location); - } - } - } - public void clearBreakpoint(Executable method, long location) { - clearBreakpoints(new BP(method, location)); - } - - public void clearAllBreakpoints() { - clearBreakpoints(breaks.toArray(new BP[0])); - } - } - - public static void startBreakpointWatch(Class methodClass, - Executable breakpointReached, - Thread thr) { - startBreakpointWatch(methodClass, breakpointReached, false, thr); - } - - /** - * Enables the trapping of breakpoint events. - * - * If allowRecursive == true then breakpoints will be sent even if one is currently being handled. - */ - public static native void startBreakpointWatch(Class methodClass, - Executable breakpointReached, - boolean allowRecursive, - Thread thr); - public static native void stopBreakpointWatch(Thread thr); - - public static final class LineNumber implements Comparable { - public final long location; - public final int line; - - private LineNumber(long loc, int line) { - this.location = loc; - this.line = line; - } - - public boolean equals(Object other) { - return other instanceof LineNumber && ((LineNumber)other).line == line && - ((LineNumber)other).location == location; - } - - public int compareTo(LineNumber other) { - int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line)); - if (v != 0) { - return v; - } else { - return Long.valueOf(location).compareTo(Long.valueOf(other.location)); - } - } - } - - public static native void setBreakpoint(Executable m, long loc); - public static void setBreakpoint(Executable m, LineNumber l) { - setBreakpoint(m, l.location); - } - - public static native void clearBreakpoint(Executable m, long loc); - public static void clearBreakpoint(Executable m, LineNumber l) { - clearBreakpoint(m, l.location); - } - - private static native Object[] getLineNumberTableNative(Executable m); - public static LineNumber[] getLineNumberTable(Executable m) { - Object[] nativeTable = getLineNumberTableNative(m); - long[] location = (long[])(nativeTable[0]); - int[] lines = (int[])(nativeTable[1]); - if (lines.length != location.length) { - throw new Error("Lines and locations have different lengths!"); - } - LineNumber[] out = new LineNumber[lines.length]; - for (int i = 0; i < lines.length; i++) { - out[i] = new LineNumber(location[i], lines[i]); - } - return out; - } - - public static native long getStartLocation(Executable m); - - public static int locationToLine(Executable m, long location) { - try { - Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m); - int best = -1; - for (Breakpoint.LineNumber l : lines) { - if (l.location > location) { - break; - } else { - best = l.line; - } - } - return best; - } catch (Exception e) { - return -1; - } - } - - public static long lineToLocation(Executable m, int line) throws Exception { - try { - Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m); - for (Breakpoint.LineNumber l : lines) { - if (l.line == line) { - return l.location; - } - } - throw new Exception("Unable to find line " + line + " in " + m); - } catch (Exception e) { - throw new Exception("Unable to get line number info for " + m, e); - } - } -} - diff --git a/test/1941-dispose-stress/src/art/Test1941.java b/test/1941-dispose-stress/src/art/Test1941.java deleted file mode 100644 index d5a9de6cab..0000000000 --- a/test/1941-dispose-stress/src/art/Test1941.java +++ /dev/null @@ -1,72 +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.Arrays; -import java.lang.reflect.Executable; -import java.lang.reflect.Method; - -public class Test1941 { - public static final boolean PRINT_CNT = false; - public static long CNT = 0; - - // Method with multiple paths we can break on. - public static long fib(long f) { - if (f < 0) { - throw new IllegalArgumentException("Bad argument f < 0: f = " + f); - } else if (f == 0) { - return 0; - } else if (f == 1) { - return 1; - } else { - return fib(f - 1) + fib(f - 2); - } - } - - public static void notifySingleStep(Thread thr, Executable e, long loc) { - // Don't bother actually doing anything. - } - - public static void LoopAllocFreeEnv() { - while (!Thread.interrupted()) { - CNT++; - long env = AllocEnv(); - FreeEnv(env); - } - } - - public static native long AllocEnv(); - public static native void FreeEnv(long env); - - public static void run() throws Exception { - Thread thr = new Thread(Test1941::LoopAllocFreeEnv, "LoopNative"); - thr.start(); - Trace.enableSingleStepTracing(Test1941.class, - Test1941.class.getDeclaredMethod( - "notifySingleStep", Thread.class, Executable.class, Long.TYPE), - null); - - System.out.println("fib(20) is " + fib(20)); - - thr.interrupt(); - thr.join(); - Trace.disableTracing(null); - if (PRINT_CNT) { - System.out.println("Number of envs created/destroyed: " + CNT); - } - } -} diff --git a/test/1941-dispose-stress/src/art/Trace.java b/test/1941-dispose-stress/src/art/Trace.java deleted file mode 100644 index 8999bb1368..0000000000 --- a/test/1941-dispose-stress/src/art/Trace.java +++ /dev/null @@ -1,68 +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.lang.reflect.Field; -import java.lang.reflect.Method; - -public class Trace { - public static native void enableTracing(Class methodClass, - Method entryMethod, - Method exitMethod, - Method fieldAccess, - Method fieldModify, - Method singleStep, - Thread thr); - public static native void disableTracing(Thread thr); - - public static void enableFieldTracing(Class methodClass, - Method fieldAccess, - Method fieldModify, - Thread thr) { - enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr); - } - - public static void enableMethodTracing(Class methodClass, - Method entryMethod, - Method exitMethod, - Thread thr) { - enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr); - } - - public static void enableSingleStepTracing(Class methodClass, - Method singleStep, - Thread thr) { - enableTracing(methodClass, null, null, null, null, singleStep, thr); - } - - public static native void watchFieldAccess(Field f); - public static native void watchFieldModification(Field f); - public static native void watchAllFieldAccesses(); - public static native void watchAllFieldModifications(); - - // the names, arguments, and even line numbers of these functions are embedded in the tests so we - // need to add to the bottom and not modify old ones to maintain compat. - public static native void enableTracing2(Class methodClass, - Method entryMethod, - Method exitMethod, - Method fieldAccess, - Method fieldModify, - Method singleStep, - Method ThreadStart, - Method ThreadEnd, - Thread thr); -} diff --git a/test/Android.bp b/test/Android.bp index 8f29251907..ba24119e9c 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -259,7 +259,6 @@ art_cc_defaults { "1932-monitor-events-misc/monitor_misc.cc", "1934-jvmti-signal-thread/signal_threads.cc", "1939-proxy-frames/local_instance.cc", - "1941-dispose-stress/dispose_stress.cc", ], shared_libs: [ "libbase", -- GitLab From b284f8d775ac32d8109744d94b99da451570beef Mon Sep 17 00:00:00 2001 From: Alex Light Date: Tue, 21 Nov 2017 00:00:48 +0000 Subject: [PATCH 065/226] Revert "Revert "Make JVMTI DisposeEnvironment and GetEnv thread safe."" This reverts commit af9341087aab0146b8323ece156bde8130948465. We needed to allow TopLockLevel locks to be acquired when the mutator_lock_ is exclusive held. This is required for spec conformance. To ensure there are no deadlocks the mutator_lock_ is the only lock level with this exception and one cannot acquire the mutator_lock_ when one holds any kTopLockLevel locks. Reason for revert: Fixed issue causing test 913 failure in art-gc-gss-tlab Test: ART_DEFAULT_GC_TYPE=GSS \ ART_USE_TLAB=true \ ART_USE_READ_BARRIER=false ./test.py --host -j50 Bug: 69465262 Change-Id: Ic1a4d9bb3ff64382ba7ae22ba27a4f44628ed095 --- openjdkjvmti/art_jvmti.h | 4 + openjdkjvmti/events-inl.h | 277 +++++++++++------- openjdkjvmti/events.cc | 21 +- openjdkjvmti/events.h | 67 +++-- openjdkjvmti/object_tagging.cc | 3 +- openjdkjvmti/ti_dump.cc | 2 +- openjdkjvmti/ti_phase.cc | 11 +- runtime/base/mutex-inl.h | 30 +- runtime/base/mutex.h | 10 + runtime/ti/agent.cc | 3 + test/1941-dispose-stress/dispose_stress.cc | 59 ++++ test/1941-dispose-stress/expected.txt | 1 + test/1941-dispose-stress/info.txt | 3 + test/1941-dispose-stress/run | 18 ++ test/1941-dispose-stress/src/Main.java | 21 ++ .../src/art/Breakpoint.java | 202 +++++++++++++ .../1941-dispose-stress/src/art/Test1941.java | 72 +++++ test/1941-dispose-stress/src/art/Trace.java | 68 +++++ test/Android.bp | 1 + 19 files changed, 730 insertions(+), 143 deletions(-) create mode 100644 test/1941-dispose-stress/dispose_stress.cc create mode 100644 test/1941-dispose-stress/expected.txt create mode 100644 test/1941-dispose-stress/info.txt create mode 100755 test/1941-dispose-stress/run create mode 100644 test/1941-dispose-stress/src/Main.java create mode 100644 test/1941-dispose-stress/src/art/Breakpoint.java create mode 100644 test/1941-dispose-stress/src/art/Test1941.java create mode 100644 test/1941-dispose-stress/src/art/Trace.java diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h index 682b82b5cd..e8e62c2b40 100644 --- a/openjdkjvmti/art_jvmti.h +++ b/openjdkjvmti/art_jvmti.h @@ -94,6 +94,10 @@ struct ArtJvmTiEnv : public jvmtiEnv { static ArtJvmTiEnv* AsArtJvmTiEnv(jvmtiEnv* env) { return art::down_cast(env); } + + // Top level lock. Nothing can be held when we get this except for mutator lock for full + // thread-suspension. + static art::Mutex *gEnvMutex ACQUIRED_AFTER(art::Locks::mutator_lock_); }; // Macro and constexpr to make error values less annoying to write. diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h index 5344e0fbde..007669b50f 100644 --- a/openjdkjvmti/events-inl.h +++ b/openjdkjvmti/events-inl.h @@ -46,6 +46,45 @@ static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e) { namespace impl { +// Helper for ensuring that the dispatch environment is sane. Events with JNIEnvs need to stash +// pending exceptions since they can cause new ones to be thrown. In accordance with the JVMTI +// specification we allow exceptions originating from events to overwrite the current exception, +// including exceptions originating from earlier events. +class ScopedEventDispatchEnvironment FINAL : public art::ValueObject { + public: + ScopedEventDispatchEnvironment() : env_(nullptr), throw_(nullptr, nullptr) { + DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative); + } + + explicit ScopedEventDispatchEnvironment(JNIEnv* env) + : env_(env), + throw_(env_, env_->ExceptionOccurred()) { + DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative); + // The spec doesn't say how much local data should be there, so we just give 128 which seems + // likely to be enough for most cases. + env_->PushLocalFrame(128); + env_->ExceptionClear(); + } + + ~ScopedEventDispatchEnvironment() { + if (env_ != nullptr) { + if (throw_.get() != nullptr && !env_->ExceptionCheck()) { + // TODO It would be nice to add the overwritten exceptions to the suppressed exceptions list + // of the newest exception. + env_->Throw(throw_.get()); + } + env_->PopLocalFrame(nullptr); + } + DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative); + } + + private: + JNIEnv* env_; + ScopedLocalRef throw_; + + DISALLOW_COPY_AND_ASSIGN(ScopedEventDispatchEnvironment); +}; + // Infrastructure to achieve type safety for event dispatch. #define FORALL_EVENT_TYPES(fn) \ @@ -97,27 +136,68 @@ FORALL_EVENT_TYPES(EVENT_FN_TYPE) #undef EVENT_FN_TYPE -template -ALWAYS_INLINE inline typename EventFnType::type GetCallback(ArtJvmTiEnv* env); - -#define GET_CALLBACK(name, enum_name) \ -template <> \ -ALWAYS_INLINE inline EventFnType::type GetCallback( \ - ArtJvmTiEnv* env) { \ - if (env->event_callbacks == nullptr) { \ - return nullptr; \ - } \ - return env->event_callbacks->name; \ -} +#define MAKE_EVENT_HANDLER_FUNC(name, enum_name) \ +template<> \ +struct EventHandlerFunc { \ + using EventFnType = typename impl::EventFnType::type; \ + explicit EventHandlerFunc(ArtJvmTiEnv* env) \ + : env_(env), \ + fn_(env_->event_callbacks == nullptr ? nullptr : env_->event_callbacks->name) { } \ + \ + template \ + ALWAYS_INLINE \ + void ExecuteCallback(JNIEnv* jnienv, Args... args) const { \ + if (fn_ != nullptr) { \ + ScopedEventDispatchEnvironment sede(jnienv); \ + DoExecute(jnienv, args...); \ + } \ + } \ + \ + template \ + ALWAYS_INLINE \ + void ExecuteCallback(Args... args) const { \ + if (fn_ != nullptr) { \ + ScopedEventDispatchEnvironment sede; \ + DoExecute(args...); \ + } \ + } \ + \ + private: \ + template \ + ALWAYS_INLINE \ + inline void DoExecute(Args... args) const { \ + static_assert(std::is_same::value, \ + "Unexpected different type of ExecuteCallback"); \ + fn_(env_, args...); \ + } \ + \ + public: \ + ArtJvmTiEnv* env_; \ + EventFnType fn_; \ +}; -FORALL_EVENT_TYPES(GET_CALLBACK) +FORALL_EVENT_TYPES(MAKE_EVENT_HANDLER_FUNC) -#undef GET_CALLBACK +#undef MAKE_EVENT_HANDLER_FUNC #undef FORALL_EVENT_TYPES } // namespace impl +template +inline std::vector> EventHandler::CollectEvents(art::Thread* thread, + Args... args) const { + art::MutexLock mu(thread, envs_lock_); + std::vector> handlers; + for (ArtJvmTiEnv* env : envs) { + if (ShouldDispatch(env, thread, args...)) { + impl::EventHandlerFunc h(env); + handlers.push_back(h); + } + } + return handlers; +} + // C++ does not allow partial template function specialization. The dispatch for our separated // ClassFileLoadHook event types is the same, so use this helper for code deduplication. template @@ -131,29 +211,37 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, const unsigned char* class_data, jint* new_class_data_len, unsigned char** new_class_data) const { + art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable || kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable, "Unsupported event"); DCHECK(*new_class_data == nullptr); jint current_len = class_data_len; unsigned char* current_class_data = const_cast(class_data); + std::vector> handlers = + CollectEvents(thread, + jnienv, + class_being_redefined, + loader, + name, + protection_domain, + class_data_len, + class_data, + new_class_data_len, + new_class_data); ArtJvmTiEnv* last_env = nullptr; - for (ArtJvmTiEnv* env : envs) { - if (env == nullptr) { - continue; - } + for (const impl::EventHandlerFunc& event : handlers) { jint new_len = 0; unsigned char* new_data = nullptr; - DispatchEventOnEnv(env, - thread, - jnienv, - class_being_redefined, - loader, - name, - protection_domain, - current_len, - static_cast(current_class_data), - &new_len, - &new_data); + ExecuteCallback(event, + jnienv, + class_being_redefined, + loader, + name, + protection_domain, + current_len, + static_cast(current_class_data), + &new_len, + &new_data); if (new_data != nullptr && new_data != current_class_data) { // Destroy the data the last transformer made. We skip this if the previous state was the // initial one since we don't know here which jvmtiEnv allocated it. @@ -162,7 +250,7 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, if (last_env != nullptr) { last_env->Deallocate(current_class_data); } - last_env = env; + last_env = event.env_; current_class_data = new_data; current_len = new_len; } @@ -176,70 +264,28 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, // Our goal for DispatchEvent: Do not allow implicit type conversion. Types of ...args must match // exactly the argument types of the corresponding Jvmti kEvent function pointer. -template -inline void EventHandler::ExecuteCallback(ArtJvmTiEnv* env, Args... args) { - using FnType = typename impl::EventFnType::type; - FnType callback = impl::GetCallback(env); - if (callback != nullptr) { - (*callback)(env, args...); - } -} - template inline void EventHandler::DispatchEvent(art::Thread* thread, Args... args) const { + art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); static_assert(!std::is_same>>>::value, "Should be calling DispatchEvent with explicit JNIEnv* argument!"); DCHECK(thread == nullptr || !thread->IsExceptionPending()); - for (ArtJvmTiEnv* env : envs) { - if (env != nullptr) { - DispatchEventOnEnv(env, thread, args...); - } + std::vector> events = CollectEvents(thread, args...); + for (auto event : events) { + ExecuteCallback(event, args...); } } -// Helper for ensuring that the dispatch environment is sane. Events with JNIEnvs need to stash -// pending exceptions since they can cause new ones to be thrown. In accordance with the JVMTI -// specification we allow exceptions originating from events to overwrite the current exception, -// including exceptions originating from earlier events. -class ScopedEventDispatchEnvironment FINAL : public art::ValueObject { - public: - explicit ScopedEventDispatchEnvironment(JNIEnv* env) - : env_(env), - thr_(env_, env_->ExceptionOccurred()), - suspend_(art::Thread::Current(), art::kNative) { - // The spec doesn't say how much local data should be there, so we just give 128 which seems - // likely to be enough for most cases. - env_->PushLocalFrame(128); - env_->ExceptionClear(); - UNUSED(suspend_); - } - - ~ScopedEventDispatchEnvironment() { - if (thr_.get() != nullptr && !env_->ExceptionCheck()) { - // TODO It would be nice to add the overwritten exceptions to the suppressed exceptions list - // of the newest exception. - env_->Throw(thr_.get()); - } - env_->PopLocalFrame(nullptr); - } - - private: - JNIEnv* env_; - ScopedLocalRef thr_; - // Not actually unused. The destructor/constructor does important work. - art::ScopedThreadStateChange suspend_; - - DISALLOW_COPY_AND_ASSIGN(ScopedEventDispatchEnvironment); -}; - template inline void EventHandler::DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const { - for (ArtJvmTiEnv* env : envs) { - if (env != nullptr) { - DispatchEventOnEnv(env, thread, jnienv, args...); - } + art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); + std::vector> events = CollectEvents(thread, + jnienv, + args...); + for (auto event : events) { + ExecuteCallback(event, jnienv, args...); } } @@ -248,8 +294,9 @@ inline void EventHandler::DispatchEventOnEnv( ArtJvmTiEnv* env, art::Thread* thread, JNIEnv* jnienv, Args... args) const { DCHECK(env != nullptr); if (ShouldDispatch(env, thread, jnienv, args...)) { - ScopedEventDispatchEnvironment sede(jnienv); - ExecuteCallback(env, jnienv, args...); + art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); + impl::EventHandlerFunc func(env); + ExecuteCallback(func, jnienv, args...); } } @@ -260,11 +307,26 @@ inline void EventHandler::DispatchEventOnEnv( typename std::decay_t< std::tuple_element_t<0, std::tuple>>>::value, "Should be calling DispatchEventOnEnv with explicit JNIEnv* argument!"); - if (ShouldDispatch(env, thread, args...)) { - ExecuteCallback(env, args...); + DCHECK(env != nullptr); + if (ShouldDispatch(env, thread, args...)) { + art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); + impl::EventHandlerFunc func(env); + ExecuteCallback(func, args...); } } +template +inline void EventHandler::ExecuteCallback(impl::EventHandlerFunc handler, Args... args) { + handler.ExecuteCallback(args...); +} + +template +inline void EventHandler::ExecuteCallback(impl::EventHandlerFunc handler, + JNIEnv* jnienv, + Args... args) { + handler.ExecuteCallback(jnienv, args...); +} + // Events that need custom logic for if we send the event but are otherwise normal. This includes // the kBreakpoint, kFramePop, kFieldAccess, and kFieldModification events. @@ -347,14 +409,13 @@ inline bool EventHandler::ShouldDispatch( // something. template <> inline void EventHandler::ExecuteCallback( - ArtJvmTiEnv* env, + impl::EventHandlerFunc event, JNIEnv* jnienv, jthread jni_thread, jmethodID jmethod, jboolean is_exception, const art::ShadowFrame* frame ATTRIBUTE_UNUSED) { - ExecuteCallback( - env, jnienv, jni_thread, jmethod, is_exception); + ExecuteCallback(event, jnienv, jni_thread, jmethod, is_exception); } // Need to give a custom specialization for NativeMethodBind since it has to deal with an out @@ -366,20 +427,25 @@ inline void EventHandler::DispatchEvent(art::T jmethodID method, void* cur_method, void** new_method) const { + art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); + std::vector> events = + CollectEvents(thread, + jnienv, + jni_thread, + method, + cur_method, + new_method); *new_method = cur_method; - for (ArtJvmTiEnv* env : envs) { - if (env != nullptr) { - *new_method = cur_method; - DispatchEventOnEnv(env, - thread, - jnienv, - jni_thread, - method, - cur_method, - new_method); - if (*new_method != nullptr) { - cur_method = *new_method; - } + for (auto event : events) { + *new_method = cur_method; + ExecuteCallback(event, + jnienv, + jni_thread, + method, + cur_method, + new_method); + if (*new_method != nullptr) { + cur_method = *new_method; } } *new_method = cur_method; @@ -439,7 +505,7 @@ inline void EventHandler::DispatchEvent -inline bool EventHandler::ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) { +inline bool EventHandler::ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) const { bool dispatch = env->event_masks.global_event_mask.Test(kEvent); if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(kEvent)) { @@ -461,6 +527,11 @@ inline bool EventHandler::ShouldDispatch(ArtJvmTiEnv* env, } inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) { + art::MutexLock mu(art::Thread::Current(), envs_lock_); + RecalculateGlobalEventMaskLocked(event); +} + +inline void EventHandler::RecalculateGlobalEventMaskLocked(ArtJvmtiEvent event) { bool union_value = false; for (const ArtJvmTiEnv* stored_env : envs) { if (stored_env == nullptr) { diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index d1d606de48..be4ebbc85e 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -193,25 +193,21 @@ void EventMasks::HandleChangedCapabilities(const jvmtiCapabilities& caps, bool c } void EventHandler::RegisterArtJvmTiEnv(ArtJvmTiEnv* env) { - // Since we never shrink this array we might as well try to fill gaps. - auto it = std::find(envs.begin(), envs.end(), nullptr); - if (it != envs.end()) { - *it = env; - } else { - envs.push_back(env); - } + art::MutexLock mu(art::Thread::Current(), envs_lock_); + envs.push_back(env); } void EventHandler::RemoveArtJvmTiEnv(ArtJvmTiEnv* env) { + art::MutexLock mu(art::Thread::Current(), envs_lock_); // Since we might be currently iterating over the envs list we cannot actually erase elements. // Instead we will simply replace them with 'nullptr' and skip them manually. auto it = std::find(envs.begin(), envs.end(), env); if (it != envs.end()) { - *it = nullptr; + envs.erase(it); for (size_t i = static_cast(ArtJvmtiEvent::kMinEventTypeVal); i <= static_cast(ArtJvmtiEvent::kMaxEventTypeVal); ++i) { - RecalculateGlobalEventMask(static_cast(i)); + RecalculateGlobalEventMaskLocked(static_cast(i)); } } } @@ -431,11 +427,11 @@ class JvmtiGcPauseListener : public art::gc::GcPauseListener { finish_enabled_(false) {} void StartPause() OVERRIDE { - handler_->DispatchEvent(nullptr); + handler_->DispatchEvent(art::Thread::Current()); } void EndPause() OVERRIDE { - handler_->DispatchEvent(nullptr); + handler_->DispatchEvent(art::Thread::Current()); } bool IsEnabled() { @@ -1176,7 +1172,8 @@ void EventHandler::Shutdown() { art::Runtime::Current()->GetInstrumentation()->RemoveListener(method_trace_listener_.get(), ~0); } -EventHandler::EventHandler() { +EventHandler::EventHandler() : envs_lock_("JVMTI Environment List Lock", + art::LockLevel::kTopLockLevel) { alloc_listener_.reset(new JvmtiAllocationListener(this)); ddm_listener_.reset(new JvmtiDdmChunkListener(this)); gc_pause_listener_.reset(new JvmtiGcPauseListener(this)); diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h index a99ed7b212..c73215f07b 100644 --- a/openjdkjvmti/events.h +++ b/openjdkjvmti/events.h @@ -158,6 +158,10 @@ struct EventMasks { void HandleChangedCapabilities(const jvmtiCapabilities& caps, bool caps_added); }; +namespace impl { +template struct EventHandlerFunc { }; +} // namespace impl + // Helper class for event handling. class EventHandler { public: @@ -169,10 +173,10 @@ class EventHandler { // Register an env. It is assumed that this happens on env creation, that is, no events are // enabled, yet. - void RegisterArtJvmTiEnv(ArtJvmTiEnv* env); + void RegisterArtJvmTiEnv(ArtJvmTiEnv* env) REQUIRES(!envs_lock_); // Remove an env. - void RemoveArtJvmTiEnv(ArtJvmTiEnv* env); + void RemoveArtJvmTiEnv(ArtJvmTiEnv* env) REQUIRES(!envs_lock_); bool IsEventEnabledAnywhere(ArtJvmtiEvent event) const { if (!EventMask::EventIsInRange(event)) { @@ -184,13 +188,15 @@ class EventHandler { jvmtiError SetEvent(ArtJvmTiEnv* env, art::Thread* thread, ArtJvmtiEvent event, - jvmtiEventMode mode); + jvmtiEventMode mode) + REQUIRES(!envs_lock_); // Dispatch event to all registered environments. Since this one doesn't have a JNIEnv* it doesn't // matter if it has the mutator_lock. template ALWAYS_INLINE - inline void DispatchEvent(art::Thread* thread, Args... args) const; + inline void DispatchEvent(art::Thread* thread, Args... args) const + REQUIRES(!envs_lock_); // Dispatch event to all registered environments stashing exceptions as needed. This works since // JNIEnv* is always the second argument if it is passed to an event. Needed since C++ does not @@ -200,7 +206,8 @@ class EventHandler { // the event to allocate local references. template ALWAYS_INLINE - inline void DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const; + inline void DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const + REQUIRES(!envs_lock_); // Tell the event handler capabilities were added/lost so it can adjust the sent events.If // caps_added is true then caps is all the newly set capabilities of the jvmtiEnv. If it is false @@ -208,28 +215,48 @@ class EventHandler { ALWAYS_INLINE inline void HandleChangedCapabilities(ArtJvmTiEnv* env, const jvmtiCapabilities& caps, - bool added); + bool added) + REQUIRES(!envs_lock_); // Dispatch event to the given environment, only. template ALWAYS_INLINE - inline void DispatchEventOnEnv( - ArtJvmTiEnv* env, art::Thread* thread, JNIEnv* jnienv, Args... args) const; + inline void DispatchEventOnEnv(ArtJvmTiEnv* env, + art::Thread* thread, + JNIEnv* jnienv, + Args... args) const + REQUIRES(!envs_lock_); // Dispatch event to the given environment, only. template ALWAYS_INLINE - inline void DispatchEventOnEnv(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const; + inline void DispatchEventOnEnv(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const + REQUIRES(!envs_lock_); private: + template + ALWAYS_INLINE + inline std::vector> CollectEvents(art::Thread* thread, + Args... args) const + REQUIRES(!envs_lock_); + template ALWAYS_INLINE - static inline bool ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread); + inline bool ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) const; template ALWAYS_INLINE - static inline void ExecuteCallback(ArtJvmTiEnv* env, Args... args); + static inline void ExecuteCallback(impl::EventHandlerFunc handler, + JNIEnv* env, + Args... args) + REQUIRES(!envs_lock_); + template + ALWAYS_INLINE + static inline void ExecuteCallback(impl::EventHandlerFunc handler, Args... args) + REQUIRES(!envs_lock_); + + // Public for use to collect dispatches template ALWAYS_INLINE inline bool ShouldDispatch(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const; @@ -241,7 +268,9 @@ class EventHandler { // Recalculates the event mask for the given event. ALWAYS_INLINE - inline void RecalculateGlobalEventMask(ArtJvmtiEvent event); + inline void RecalculateGlobalEventMask(ArtJvmtiEvent event) REQUIRES(!envs_lock_); + ALWAYS_INLINE + inline void RecalculateGlobalEventMaskLocked(ArtJvmtiEvent event) REQUIRES(envs_lock_); template ALWAYS_INLINE inline void DispatchClassFileLoadHookEvent(art::Thread* thread, @@ -253,7 +282,8 @@ class EventHandler { jint class_data_len, const unsigned char* class_data, jint* new_class_data_len, - unsigned char** new_class_data) const; + unsigned char** new_class_data) const + REQUIRES(!envs_lock_); void HandleEventType(ArtJvmtiEvent event, bool enable); void HandleLocalAccessCapabilityAdded(); @@ -261,10 +291,13 @@ class EventHandler { bool OtherMonitorEventsEnabledAnywhere(ArtJvmtiEvent event); - // List of all JvmTiEnv objects that have been created, in their creation order. - // NB Some elements might be null representing envs that have been deleted. They should be skipped - // anytime this list is used. - std::vector envs; + // List of all JvmTiEnv objects that have been created, in their creation order. It is a std::list + // since we mostly access it by iterating over the entire thing, only ever append to the end, and + // need to be able to remove arbitrary elements from it. + std::list envs GUARDED_BY(envs_lock_); + + // Top level lock. Nothing at all should be held when we lock this. + mutable art::Mutex envs_lock_ ACQUIRED_BEFORE(art::Locks::instrument_entrypoints_lock_); // A union of all enabled events, anywhere. EventMask global_mask; diff --git a/openjdkjvmti/object_tagging.cc b/openjdkjvmti/object_tagging.cc index 6ba7165577..ba242ef1e8 100644 --- a/openjdkjvmti/object_tagging.cc +++ b/openjdkjvmti/object_tagging.cc @@ -61,7 +61,8 @@ bool ObjectTagTable::DoesHandleNullOnSweep() { return event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kObjectFree); } void ObjectTagTable::HandleNullSweep(jlong tag) { - event_handler_->DispatchEventOnEnv(jvmti_env_, nullptr, tag); + event_handler_->DispatchEventOnEnv( + jvmti_env_, art::Thread::Current(), tag); } } // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_dump.cc b/openjdkjvmti/ti_dump.cc index 809a5e47bb..253580e0e1 100644 --- a/openjdkjvmti/ti_dump.cc +++ b/openjdkjvmti/ti_dump.cc @@ -47,7 +47,7 @@ struct DumpCallback : public art::RuntimeSigQuitCallback { void SigQuit() OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { art::Thread* thread = art::Thread::Current(); art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative); - event_handler->DispatchEvent(nullptr); + event_handler->DispatchEvent(art::Thread::Current()); } EventHandler* event_handler = nullptr; diff --git a/openjdkjvmti/ti_phase.cc b/openjdkjvmti/ti_phase.cc index 23df27fbda..7157974c13 100644 --- a/openjdkjvmti/ti_phase.cc +++ b/openjdkjvmti/ti_phase.cc @@ -57,6 +57,7 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { } void NextRuntimePhase(RuntimePhase phase) REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE { + art::Thread* self = art::Thread::Current(); switch (phase) { case RuntimePhase::kInitialAgents: PhaseUtil::current_phase_ = JVMTI_PHASE_PRIMORDIAL; @@ -64,8 +65,7 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { case RuntimePhase::kStart: { PhaseUtil::current_phase_ = JVMTI_PHASE_START; - art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); - event_handler->DispatchEvent(nullptr, GetJniEnv()); + event_handler->DispatchEvent(self, GetJniEnv()); } break; case RuntimePhase::kInit: @@ -74,9 +74,7 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE; { ScopedLocalRef thread(GetJniEnv(), GetCurrentJThread()); - art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); - event_handler->DispatchEvent( - nullptr, GetJniEnv(), thread.get()); + event_handler->DispatchEvent(self, GetJniEnv(), thread.get()); } // We need to have these events be ordered to match behavior expected by some real-world // agents. The spec does not really require this but compatibility is a useful property to @@ -86,8 +84,7 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { break; case RuntimePhase::kDeath: { - art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); - event_handler->DispatchEvent(nullptr, GetJniEnv()); + event_handler->DispatchEvent(self, GetJniEnv()); PhaseUtil::current_phase_ = JVMTI_PHASE_DEAD; } // TODO: Block events now. diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h index c9d48ff7f7..587b092ab7 100644 --- a/runtime/base/mutex-inl.h +++ b/runtime/base/mutex-inl.h @@ -80,7 +80,9 @@ static inline void CheckUnattachedThread(LockLevel level) NO_THREAD_SAFETY_ANALY // (see Thread::TransitionFromSuspendedToRunnable). level == kThreadSuspendCountLock || // Avoid recursive death. - level == kAbortLock) << level; + level == kAbortLock || + // Locks at the absolute top of the stack can be locked at any time. + level == kTopLockLevel) << level; } } @@ -92,10 +94,34 @@ inline void BaseMutex::RegisterAsLocked(Thread* self) { if (kDebugLocking) { // Check if a bad Mutex of this level or lower is held. bool bad_mutexes_held = false; + // Specifically allow a kTopLockLevel lock to be gained when the current thread holds the + // mutator_lock_ exclusive. This is because we suspending when holding locks at this level is + // not allowed and if we hold the mutator_lock_ exclusive we must unsuspend stuff eventually + // so there are no deadlocks. + if (level_ == kTopLockLevel && + Locks::mutator_lock_->IsSharedHeld(self) && + !Locks::mutator_lock_->IsExclusiveHeld(self)) { + LOG(ERROR) << "Lock level violation: holding \"" << Locks::mutator_lock_->name_ << "\" " + << "(level " << kMutatorLock << " - " << static_cast(kMutatorLock) + << ") non-exclusive while locking \"" << name_ << "\" " + << "(level " << level_ << " - " << static_cast(level_) << ") a top level" + << "mutex. This is not allowed."; + bad_mutexes_held = true; + } else if (this == Locks::mutator_lock_ && self->GetHeldMutex(kTopLockLevel) != nullptr) { + LOG(ERROR) << "Lock level violation. Locking mutator_lock_ while already having a " + << "kTopLevelLock (" << self->GetHeldMutex(kTopLockLevel)->name_ << "held is " + << "not allowed."; + bad_mutexes_held = true; + } for (int i = level_; i >= 0; --i) { LockLevel lock_level_i = static_cast(i); BaseMutex* held_mutex = self->GetHeldMutex(lock_level_i); - if (UNLIKELY(held_mutex != nullptr) && lock_level_i != kAbortLock) { + if (level_ == kTopLockLevel && + lock_level_i == kMutatorLock && + Locks::mutator_lock_->IsExclusiveHeld(self)) { + // This is checked above. + continue; + } else if (UNLIKELY(held_mutex != nullptr) && lock_level_i != kAbortLock) { LOG(ERROR) << "Lock level violation: holding \"" << held_mutex->name_ << "\" " << "(level " << lock_level_i << " - " << i << ") while locking \"" << name_ << "\" " diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 87c4afe96f..c0cf4872de 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -122,6 +122,16 @@ enum LockLevel { kInstrumentEntrypointsLock, kZygoteCreationLock, + // The highest valid lock level. Use this if there is code that should only be called with no + // other locks held. Since this is the highest lock level we also allow it to be held even if the + // runtime or current thread is not fully set-up yet (for example during thread attach). Note that + // this lock also has special behavior around the mutator_lock_. Since the mutator_lock_ is not + // really a 'real' lock we allow this to be locked when the mutator_lock_ is held exclusive. + // Furthermore, the mutator_lock_ may not be acquired in any form when a lock of this level is + // held. Since the mutator_lock_ being held strong means that all other threads are suspended this + // will prevent deadlocks while still allowing this lock level to function as a "highest" level. + kTopLockLevel, + kLockLevelCount // Must come last. }; std::ostream& operator<<(std::ostream& os, const LockLevel& rhs); diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc index 3bf169ad40..548752e980 100644 --- a/runtime/ti/agent.cc +++ b/runtime/ti/agent.cc @@ -21,6 +21,8 @@ #include "base/strlcpy.h" #include "java_vm_ext.h" #include "runtime.h" +#include "thread-current-inl.h" +#include "scoped_thread_state_change-inl.h" namespace art { namespace ti { @@ -35,6 +37,7 @@ const char* AGENT_ON_UNLOAD_FUNCTION_NAME = "Agent_OnUnload"; Agent::LoadError Agent::DoLoadHelper(bool attaching, /*out*/jint* call_res, /*out*/std::string* error_msg) { + ScopedThreadStateChange stsc(Thread::Current(), ThreadState::kNative); DCHECK(call_res != nullptr); DCHECK(error_msg != nullptr); diff --git a/test/1941-dispose-stress/dispose_stress.cc b/test/1941-dispose-stress/dispose_stress.cc new file mode 100644 index 0000000000..e8fcc775e9 --- /dev/null +++ b/test/1941-dispose-stress/dispose_stress.cc @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#include + +#include "android-base/logging.h" +#include "jni.h" +#include "scoped_local_ref.h" +#include "scoped_primitive_array.h" + +#include "jvmti.h" + +// Test infrastructure +#include "jvmti_helper.h" +#include "test_env.h" + +namespace art { +namespace Test1941DisposeStress { + +extern "C" JNIEXPORT jlong JNICALL Java_art_Test1941_AllocEnv(JNIEnv* env, jclass) { + JavaVM* vm = nullptr; + if (env->GetJavaVM(&vm) != 0) { + ScopedLocalRef rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), "Unable to get JavaVM"); + return -1; + } + jvmtiEnv* new_env = nullptr; + if (vm->GetEnv(reinterpret_cast(&new_env), JVMTI_VERSION_1_0) != 0) { + ScopedLocalRef rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), "Unable to create new jvmtiEnv"); + return -1; + } + return static_cast(reinterpret_cast(new_env)); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1941_FreeEnv(JNIEnv* env, + jclass, + jlong jvmti_env_ptr) { + JvmtiErrorToException(env, + jvmti_env, + reinterpret_cast(jvmti_env_ptr)->DisposeEnvironment()); +} + +} // namespace Test1941DisposeStress +} // namespace art + diff --git a/test/1941-dispose-stress/expected.txt b/test/1941-dispose-stress/expected.txt new file mode 100644 index 0000000000..ca2eddc7b8 --- /dev/null +++ b/test/1941-dispose-stress/expected.txt @@ -0,0 +1 @@ +fib(20) is 6765 diff --git a/test/1941-dispose-stress/info.txt b/test/1941-dispose-stress/info.txt new file mode 100644 index 0000000000..e4a584e46f --- /dev/null +++ b/test/1941-dispose-stress/info.txt @@ -0,0 +1,3 @@ +Test basic JVMTI single step functionality. + +Ensures that we can receive single step events from JVMTI. diff --git a/test/1941-dispose-stress/run b/test/1941-dispose-stress/run new file mode 100755 index 0000000000..51875a7e86 --- /dev/null +++ b/test/1941-dispose-stress/run @@ -0,0 +1,18 @@ +#!/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. + +# Ask for stack traces to be dumped to a file rather than to stdout. +./default-run "$@" --jvmti diff --git a/test/1941-dispose-stress/src/Main.java b/test/1941-dispose-stress/src/Main.java new file mode 100644 index 0000000000..2fe6b818a0 --- /dev/null +++ b/test/1941-dispose-stress/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.Test1941.run(); + } +} diff --git a/test/1941-dispose-stress/src/art/Breakpoint.java b/test/1941-dispose-stress/src/art/Breakpoint.java new file mode 100644 index 0000000000..bbb89f707f --- /dev/null +++ b/test/1941-dispose-stress/src/art/Breakpoint.java @@ -0,0 +1,202 @@ +/* + * 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.lang.reflect.Executable; +import java.util.HashSet; +import java.util.Set; +import java.util.Objects; + +public class Breakpoint { + public static class Manager { + public static class BP { + public final Executable method; + public final long location; + + public BP(Executable method) { + this(method, getStartLocation(method)); + } + + public BP(Executable method, long location) { + this.method = method; + this.location = location; + } + + @Override + public boolean equals(Object other) { + return (other instanceof BP) && + method.equals(((BP)other).method) && + location == ((BP)other).location; + } + + @Override + public String toString() { + return method.toString() + " @ " + getLine(); + } + + @Override + public int hashCode() { + return Objects.hash(method, location); + } + + public int getLine() { + try { + LineNumber[] lines = getLineNumberTable(method); + int best = -1; + for (LineNumber l : lines) { + if (l.location > location) { + break; + } else { + best = l.line; + } + } + return best; + } catch (Exception e) { + return -1; + } + } + } + + private Set breaks = new HashSet<>(); + + public void setBreakpoints(BP... bs) { + for (BP b : bs) { + if (breaks.add(b)) { + Breakpoint.setBreakpoint(b.method, b.location); + } + } + } + public void setBreakpoint(Executable method, long location) { + setBreakpoints(new BP(method, location)); + } + + public void clearBreakpoints(BP... bs) { + for (BP b : bs) { + if (breaks.remove(b)) { + Breakpoint.clearBreakpoint(b.method, b.location); + } + } + } + public void clearBreakpoint(Executable method, long location) { + clearBreakpoints(new BP(method, location)); + } + + public void clearAllBreakpoints() { + clearBreakpoints(breaks.toArray(new BP[0])); + } + } + + public static void startBreakpointWatch(Class methodClass, + Executable breakpointReached, + Thread thr) { + startBreakpointWatch(methodClass, breakpointReached, false, thr); + } + + /** + * Enables the trapping of breakpoint events. + * + * If allowRecursive == true then breakpoints will be sent even if one is currently being handled. + */ + public static native void startBreakpointWatch(Class methodClass, + Executable breakpointReached, + boolean allowRecursive, + Thread thr); + public static native void stopBreakpointWatch(Thread thr); + + public static final class LineNumber implements Comparable { + public final long location; + public final int line; + + private LineNumber(long loc, int line) { + this.location = loc; + this.line = line; + } + + public boolean equals(Object other) { + return other instanceof LineNumber && ((LineNumber)other).line == line && + ((LineNumber)other).location == location; + } + + public int compareTo(LineNumber other) { + int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line)); + if (v != 0) { + return v; + } else { + return Long.valueOf(location).compareTo(Long.valueOf(other.location)); + } + } + } + + public static native void setBreakpoint(Executable m, long loc); + public static void setBreakpoint(Executable m, LineNumber l) { + setBreakpoint(m, l.location); + } + + public static native void clearBreakpoint(Executable m, long loc); + public static void clearBreakpoint(Executable m, LineNumber l) { + clearBreakpoint(m, l.location); + } + + private static native Object[] getLineNumberTableNative(Executable m); + public static LineNumber[] getLineNumberTable(Executable m) { + Object[] nativeTable = getLineNumberTableNative(m); + long[] location = (long[])(nativeTable[0]); + int[] lines = (int[])(nativeTable[1]); + if (lines.length != location.length) { + throw new Error("Lines and locations have different lengths!"); + } + LineNumber[] out = new LineNumber[lines.length]; + for (int i = 0; i < lines.length; i++) { + out[i] = new LineNumber(location[i], lines[i]); + } + return out; + } + + public static native long getStartLocation(Executable m); + + public static int locationToLine(Executable m, long location) { + try { + Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m); + int best = -1; + for (Breakpoint.LineNumber l : lines) { + if (l.location > location) { + break; + } else { + best = l.line; + } + } + return best; + } catch (Exception e) { + return -1; + } + } + + public static long lineToLocation(Executable m, int line) throws Exception { + try { + Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m); + for (Breakpoint.LineNumber l : lines) { + if (l.line == line) { + return l.location; + } + } + throw new Exception("Unable to find line " + line + " in " + m); + } catch (Exception e) { + throw new Exception("Unable to get line number info for " + m, e); + } + } +} + diff --git a/test/1941-dispose-stress/src/art/Test1941.java b/test/1941-dispose-stress/src/art/Test1941.java new file mode 100644 index 0000000000..d5a9de6cab --- /dev/null +++ b/test/1941-dispose-stress/src/art/Test1941.java @@ -0,0 +1,72 @@ +/* + * 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.Arrays; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; + +public class Test1941 { + public static final boolean PRINT_CNT = false; + public static long CNT = 0; + + // Method with multiple paths we can break on. + public static long fib(long f) { + if (f < 0) { + throw new IllegalArgumentException("Bad argument f < 0: f = " + f); + } else if (f == 0) { + return 0; + } else if (f == 1) { + return 1; + } else { + return fib(f - 1) + fib(f - 2); + } + } + + public static void notifySingleStep(Thread thr, Executable e, long loc) { + // Don't bother actually doing anything. + } + + public static void LoopAllocFreeEnv() { + while (!Thread.interrupted()) { + CNT++; + long env = AllocEnv(); + FreeEnv(env); + } + } + + public static native long AllocEnv(); + public static native void FreeEnv(long env); + + public static void run() throws Exception { + Thread thr = new Thread(Test1941::LoopAllocFreeEnv, "LoopNative"); + thr.start(); + Trace.enableSingleStepTracing(Test1941.class, + Test1941.class.getDeclaredMethod( + "notifySingleStep", Thread.class, Executable.class, Long.TYPE), + null); + + System.out.println("fib(20) is " + fib(20)); + + thr.interrupt(); + thr.join(); + Trace.disableTracing(null); + if (PRINT_CNT) { + System.out.println("Number of envs created/destroyed: " + CNT); + } + } +} diff --git a/test/1941-dispose-stress/src/art/Trace.java b/test/1941-dispose-stress/src/art/Trace.java new file mode 100644 index 0000000000..8999bb1368 --- /dev/null +++ b/test/1941-dispose-stress/src/art/Trace.java @@ -0,0 +1,68 @@ +/* + * 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.lang.reflect.Field; +import java.lang.reflect.Method; + +public class Trace { + public static native void enableTracing(Class methodClass, + Method entryMethod, + Method exitMethod, + Method fieldAccess, + Method fieldModify, + Method singleStep, + Thread thr); + public static native void disableTracing(Thread thr); + + public static void enableFieldTracing(Class methodClass, + Method fieldAccess, + Method fieldModify, + Thread thr) { + enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr); + } + + public static void enableMethodTracing(Class methodClass, + Method entryMethod, + Method exitMethod, + Thread thr) { + enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr); + } + + public static void enableSingleStepTracing(Class methodClass, + Method singleStep, + Thread thr) { + enableTracing(methodClass, null, null, null, null, singleStep, thr); + } + + public static native void watchFieldAccess(Field f); + public static native void watchFieldModification(Field f); + public static native void watchAllFieldAccesses(); + public static native void watchAllFieldModifications(); + + // the names, arguments, and even line numbers of these functions are embedded in the tests so we + // need to add to the bottom and not modify old ones to maintain compat. + public static native void enableTracing2(Class methodClass, + Method entryMethod, + Method exitMethod, + Method fieldAccess, + Method fieldModify, + Method singleStep, + Method ThreadStart, + Method ThreadEnd, + Thread thr); +} diff --git a/test/Android.bp b/test/Android.bp index ba24119e9c..8f29251907 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -259,6 +259,7 @@ art_cc_defaults { "1932-monitor-events-misc/monitor_misc.cc", "1934-jvmti-signal-thread/signal_threads.cc", "1939-proxy-frames/local_instance.cc", + "1941-dispose-stress/dispose_stress.cc", ], shared_libs: [ "libbase", -- GitLab From 2f36d2fdf277e2579e55cd3f54c1683c1b1a84bf Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 20 Nov 2017 15:45:25 -0800 Subject: [PATCH 066/226] Write link data for dexlayout Instead of not writing out the link data and leaving an invalid link_offset, write it out. Fixes dex verifier failures for a few APKs. Added test. Test: test-art-host Bug: 69561363 Change-Id: Iec1c331f74f9fd58658d4c13465a3bcb6295ce24 --- dexlayout/dex_ir.h | 11 ++++++++++ dexlayout/dex_ir_builder.cc | 5 +++++ dexlayout/dex_writer.cc | 10 ++++++++- dexlayout/dexlayout_test.cc | 41 +++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 1 deletion(-) diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h index 61a4eae9b1..8421774104 100644 --- a/dexlayout/dex_ir.h +++ b/dexlayout/dex_ir.h @@ -396,6 +396,14 @@ class Collections { eagerly_assign_offsets_ = eagerly_assign_offsets; } + void SetLinkData(std::vector&& link_data) { + link_data_ = std::move(link_data); + } + + const std::vector& LinkData() const { + return link_data_; + } + private: EncodedValue* ReadEncodedValue(const DexFile& dex_file, const uint8_t** data); EncodedValue* ReadEncodedValue(const DexFile& dex_file, @@ -452,6 +460,9 @@ class Collections { uint32_t map_list_offset_ = 0; + // Link data. + std::vector link_data_; + // If we eagerly assign offsets during IR building or later after layout. Must be false if // changing the layout is enabled. bool eagerly_assign_offsets_; diff --git a/dexlayout/dex_ir_builder.cc b/dexlayout/dex_ir_builder.cc index 924dfe076a..1fd963fe22 100644 --- a/dexlayout/dex_ir_builder.cc +++ b/dexlayout/dex_ir_builder.cc @@ -80,6 +80,11 @@ Header* DexIrBuilder(const DexFile& dex_file, bool eagerly_assign_offsets) { // Sort the vectors by the map order (same order as the file). collections.SortVectorsByMapOrder(); + // Load the link data if it exists. + collections.SetLinkData(std::vector( + dex_file.Begin() + dex_file.GetHeader().link_off_, + dex_file.Begin() + dex_file.GetHeader().link_off_ + dex_file.GetHeader().link_size_)); + return header; } diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc index c85bca0d92..1fac2359b0 100644 --- a/dexlayout/dex_writer.cc +++ b/dexlayout/dex_writer.cc @@ -878,7 +878,15 @@ void DexWriter::WriteMemMap() { } } - // TODO: Write link data? + // Write link data if it exists. + const std::vector& link_data = collection.LinkData(); + if (link_data.size() > 0) { + CHECK_EQ(header_->LinkSize(), static_cast(link_data.size())); + if (compute_offsets_) { + header_->SetLinkOffset(offset); + } + offset += Write(&link_data[0], link_data.size(), header_->LinkOffset()); + } // Write header last. if (compute_offsets_) { diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index c4f7accc8c..19c9038ee6 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -732,4 +732,45 @@ TEST_F(DexLayoutTest, CodeItemOverrun) { dexlayout_args)); } +// Test that link data is written out (or at least the header is updated). +TEST_F(DexLayoutTest, LinkData) { + ScratchFile temp_dex; + size_t file_size = 0; + MutateDexFile(temp_dex.GetFile(), GetTestDexFileName("ManyMethods"), [&] (DexFile* dex) { + DexFile::Header& header = const_cast(dex->GetHeader()); + header.link_off_ = header.file_size_; + header.link_size_ = 16 * KB; + header.file_size_ += header.link_size_; + file_size = header.file_size_; + }); + TEMP_FAILURE_RETRY(temp_dex.GetFile()->SetLength(file_size)); + + std::string error_msg; + + ScratchFile tmp_file; + const std::string& tmp_name = tmp_file.GetFilename(); + size_t tmp_last_slash = tmp_name.rfind('/'); + std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1); + ScratchFile profile_file; + + std::vector dexlayout_args = + { "-i", + "-v", + "-w", tmp_dir, + "-o", tmp_name, + "-p", profile_file.GetFilename(), + temp_dex.GetFilename() + }; + // -v makes sure that the layout did not corrupt the dex file. + ASSERT_TRUE(DexLayoutExec(&temp_dex, + /*dex_filename*/ nullptr, + &profile_file, + dexlayout_args)); + + std::string output_dex = temp_dex.GetFilename() + ".new"; + std::vector rm_exec_argv = + { "/bin/rm", output_dex }; + ASSERT_TRUE(::art::Exec(rm_exec_argv, &error_msg)); +} + } // namespace art -- GitLab From f3c52b42a035902245d00a619fed0275afb063d2 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 17 Nov 2017 17:32:12 +0000 Subject: [PATCH 067/226] Fill Class and String .bss slots in runtime. Shift the responsibility for filling Class and String .bss slots from compiled code to runtime. This reduces the size of the compiled code. Make oatdump list .bss slot mappings (ArtMethod, Class and String) for each dex file. aosp_taimen-userdebug boot image size: - before: arm boot*.oat: 36534524 arm64 boot*.oat: 42723256 - after: arm boot*.oat: 36431448 (-101KiB, -0.3%) arm64 boot*.oat: 42645016 (-76KiB, -0.2%) Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Test: Pixel 2 XL boots. Test: testrunner.py --target --optimizing Test: m dump-oat, manually inspect output. Bug: 65737953 Change-Id: I1330d070307410107e12c309d4c7f8121baba83c --- compiler/Android.bp | 1 - compiler/linker/method_bss_mapping_encoder.h | 79 ---- .../linker/method_bss_mapping_encoder_test.cc | 50 --- compiler/optimizing/code_generator_arm64.cc | 101 +---- .../optimizing/code_generator_arm_vixl.cc | 86 +---- compiler/optimizing/code_generator_mips.cc | 131 +------ compiler/optimizing/code_generator_mips64.cc | 131 +------ compiler/optimizing/code_generator_x86.cc | 17 - compiler/optimizing/code_generator_x86_64.cc | 15 - dex2oat/Android.bp | 1 + dex2oat/linker/index_bss_mapping_encoder.h | 86 +++++ .../linker/index_bss_mapping_encoder_test.cc | 104 ++++++ dex2oat/linker/oat_writer.cc | 353 +++++++++++++----- dex2oat/linker/oat_writer.h | 14 +- oatdump/oatdump.cc | 59 +++ profman/profman.cc | 5 +- runtime/Android.bp | 1 + .../quick/quick_dexcache_entrypoints.cc | 94 +++-- .../quick/quick_trampoline_entrypoints.cc | 37 +- runtime/index_bss_mapping.cc | 72 ++++ runtime/index_bss_mapping.h | 81 ++++ runtime/method_bss_mapping.h | 57 --- runtime/oat.h | 4 +- runtime/oat_file.cc | 142 ++++--- runtime/oat_file.h | 20 +- runtime/type_reference.h | 2 +- 26 files changed, 924 insertions(+), 819 deletions(-) delete mode 100644 compiler/linker/method_bss_mapping_encoder.h delete mode 100644 compiler/linker/method_bss_mapping_encoder_test.cc create mode 100644 dex2oat/linker/index_bss_mapping_encoder.h create mode 100644 dex2oat/linker/index_bss_mapping_encoder_test.cc create mode 100644 runtime/index_bss_mapping.cc create mode 100644 runtime/index_bss_mapping.h delete mode 100644 runtime/method_bss_mapping.h diff --git a/compiler/Android.bp b/compiler/Android.bp index 859947108e..19abcd1659 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -320,7 +320,6 @@ art_cc_test { "exception_test.cc", "jni/jni_compiler_test.cc", "linker/linker_patch_test.cc", - "linker/method_bss_mapping_encoder_test.cc", "linker/output_stream_test.cc", "optimizing/bounds_check_elimination_test.cc", "optimizing/data_type_test.cc", diff --git a/compiler/linker/method_bss_mapping_encoder.h b/compiler/linker/method_bss_mapping_encoder.h deleted file mode 100644 index b2922ec6d2..0000000000 --- a/compiler/linker/method_bss_mapping_encoder.h +++ /dev/null @@ -1,79 +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_COMPILER_LINKER_METHOD_BSS_MAPPING_ENCODER_H_ -#define ART_COMPILER_LINKER_METHOD_BSS_MAPPING_ENCODER_H_ - -#include "base/enums.h" -#include "base/logging.h" -#include "dex_file.h" -#include "method_bss_mapping.h" - -namespace art { -namespace linker { - -// Helper class for encoding compressed MethodBssMapping. -class MethodBssMappingEncoder { - public: - explicit MethodBssMappingEncoder(PointerSize pointer_size) - : pointer_size_(static_cast(pointer_size)) { - entry_.method_index = DexFile::kDexNoIndex16; - entry_.index_mask = 0u; - entry_.bss_offset = static_cast(-1); - } - - // Try to merge the next method_index -> bss_offset mapping into the current entry. - // Return true on success, false on failure. - bool TryMerge(uint32_t method_index, uint32_t bss_offset) { - DCHECK_NE(method_index, entry_.method_index); - if (entry_.bss_offset + pointer_size_ != bss_offset) { - return false; - } - uint32_t diff = method_index - entry_.method_index; - if (diff > 16u) { - return false; - } - if ((entry_.index_mask & ~(static_cast(-1) << diff)) != 0u) { - return false; - } - entry_.method_index = method_index; - // Insert the bit indicating the method index we've just overwritten - // and shift bits indicating method indexes before that. - entry_.index_mask = dchecked_integral_cast( - (static_cast(entry_.index_mask) | 0x10000u) >> diff); - entry_.bss_offset = bss_offset; - return true; - } - - void Reset(uint32_t method_index, uint32_t bss_offset) { - entry_.method_index = method_index; - entry_.index_mask = 0u; - entry_.bss_offset = bss_offset; - } - - MethodBssMappingEntry GetEntry() { - return entry_; - } - - private: - size_t pointer_size_; - MethodBssMappingEntry entry_; -}; - -} // namespace linker -} // namespace art - -#endif // ART_COMPILER_LINKER_METHOD_BSS_MAPPING_ENCODER_H_ diff --git a/compiler/linker/method_bss_mapping_encoder_test.cc b/compiler/linker/method_bss_mapping_encoder_test.cc deleted file mode 100644 index 1240389bef..0000000000 --- a/compiler/linker/method_bss_mapping_encoder_test.cc +++ /dev/null @@ -1,50 +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. - */ - -#include "method_bss_mapping_encoder.h" - -#include "gtest/gtest.h" - -namespace art { -namespace linker { - -TEST(MethodBssMappingEncoder, TryMerge) { - for (PointerSize pointer_size : {PointerSize::k32, PointerSize::k64}) { - size_t raw_pointer_size = static_cast(pointer_size); - MethodBssMappingEncoder encoder(pointer_size); - encoder.Reset(1u, 0u); - ASSERT_FALSE(encoder.TryMerge(5u, raw_pointer_size + 1)); // Wrong bss_offset difference. - ASSERT_FALSE(encoder.TryMerge(18u, raw_pointer_size)); // Method index out of range. - ASSERT_TRUE(encoder.TryMerge(5u, raw_pointer_size)); - ASSERT_TRUE(encoder.GetEntry().CoversIndex(1u)); - ASSERT_TRUE(encoder.GetEntry().CoversIndex(5u)); - ASSERT_FALSE(encoder.GetEntry().CoversIndex(17u)); - ASSERT_FALSE(encoder.TryMerge(17u, 2 * raw_pointer_size + 1)); // Wrong bss_offset difference. - ASSERT_FALSE(encoder.TryMerge(18u, 2 * raw_pointer_size)); // Method index out of range. - ASSERT_TRUE(encoder.TryMerge(17u, 2 * raw_pointer_size)); - ASSERT_TRUE(encoder.GetEntry().CoversIndex(1u)); - ASSERT_TRUE(encoder.GetEntry().CoversIndex(5u)); - ASSERT_TRUE(encoder.GetEntry().CoversIndex(17u)); - ASSERT_EQ(0u, encoder.GetEntry().GetBssOffset(1u, raw_pointer_size)); - ASSERT_EQ(raw_pointer_size, encoder.GetEntry().GetBssOffset(5u, raw_pointer_size)); - ASSERT_EQ(2 * raw_pointer_size, encoder.GetEntry().GetBssOffset(17u, raw_pointer_size)); - ASSERT_EQ(0x0011u, encoder.GetEntry().index_mask); - ASSERT_FALSE(encoder.TryMerge(18u, 2 * raw_pointer_size)); // Method index out of range. - } -} - -} // namespace linker -} // namespace art diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index a0cb43ee01..5054a299d3 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -311,40 +311,23 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { LoadClassSlowPathARM64(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, - bool do_clinit, - vixl::aarch64::Register bss_entry_temp = vixl::aarch64::Register(), - vixl::aarch64::Label* bss_entry_adrp_label = nullptr) + bool do_clinit) : SlowPathCodeARM64(at), cls_(cls), dex_pc_(dex_pc), - do_clinit_(do_clinit), - bss_entry_temp_(bss_entry_temp), - bss_entry_adrp_label_(bss_entry_adrp_label) { + do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); Location out = locations->Out(); - constexpr bool call_saves_everything_except_r0_ip0 = (!kUseReadBarrier || kUseBakerReadBarrier); CodeGeneratorARM64* arm64_codegen = down_cast(codegen); - InvokeRuntimeCallingConvention calling_convention; - // For HLoadClass/kBssEntry/kSaveEverything, the page address of the entry is in a temp - // register, make sure it's not clobbered by the call or by saving/restoring registers. - DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); - bool is_load_class_bss_entry = - (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry); - if (is_load_class_bss_entry) { - DCHECK(bss_entry_temp_.IsValid()); - DCHECK(!bss_entry_temp_.Is(calling_convention.GetRegisterAt(0))); - DCHECK( - !UseScratchRegisterScope(arm64_codegen->GetVIXLAssembler()).IsAvailable(bss_entry_temp_)); - } - __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); + InvokeRuntimeCallingConvention calling_convention; dex::TypeIndex type_index = cls_->GetTypeIndex(); __ Mov(calling_convention.GetRegisterAt(0).W(), type_index.index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage @@ -363,26 +346,6 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { arm64_codegen->MoveLocation(out, calling_convention.GetReturnLocation(type), type); } RestoreLiveRegisters(codegen, locations); - // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. - if (is_load_class_bss_entry) { - DCHECK(out.IsValid()); - const DexFile& dex_file = cls_->GetDexFile(); - if (call_saves_everything_except_r0_ip0) { - // The class entry page address was preserved in bss_entry_temp_ thanks to kSaveEverything. - } else { - // For non-Baker read barrier, we need to re-calculate the address of the class entry page. - bss_entry_adrp_label_ = arm64_codegen->NewBssEntryTypePatch(dex_file, type_index); - arm64_codegen->EmitAdrpPlaceholder(bss_entry_adrp_label_, bss_entry_temp_); - } - vixl::aarch64::Label* strp_label = - arm64_codegen->NewBssEntryTypePatch(dex_file, type_index, bss_entry_adrp_label_); - { - SingleEmissionCheckScope guard(arm64_codegen->GetVIXLAssembler()); - __ Bind(strp_label); - __ str(RegisterFrom(locations->Out(), DataType::Type::kReference), - MemOperand(bss_entry_temp_, /* offset placeholder */ 0)); - } - } __ B(GetExitLabel()); } @@ -398,34 +361,23 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { // Whether to initialize the class. const bool do_clinit_; - // For HLoadClass/kBssEntry, the temp register and the label of the ADRP where it was loaded. - vixl::aarch64::Register bss_entry_temp_; - vixl::aarch64::Label* bss_entry_adrp_label_; - DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM64); }; class LoadStringSlowPathARM64 : public SlowPathCodeARM64 { public: - LoadStringSlowPathARM64(HLoadString* instruction, Register temp, vixl::aarch64::Label* adrp_label) - : SlowPathCodeARM64(instruction), - temp_(temp), - adrp_label_(adrp_label) {} + explicit LoadStringSlowPathARM64(HLoadString* instruction) + : SlowPathCodeARM64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); CodeGeneratorARM64* arm64_codegen = down_cast(codegen); - InvokeRuntimeCallingConvention calling_convention; - // Make sure `temp_` is not clobbered by the call or by saving/restoring registers. - DCHECK(temp_.IsValid()); - DCHECK(!temp_.Is(calling_convention.GetRegisterAt(0))); - DCHECK(!UseScratchRegisterScope(arm64_codegen->GetVIXLAssembler()).IsAvailable(temp_)); - __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); + InvokeRuntimeCallingConvention calling_convention; const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex(); __ Mov(calling_convention.GetRegisterAt(0).W(), string_index.index_); arm64_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this); @@ -435,33 +387,12 @@ class LoadStringSlowPathARM64 : public SlowPathCodeARM64 { RestoreLiveRegisters(codegen, locations); - // Store the resolved String to the BSS entry. - const DexFile& dex_file = instruction_->AsLoadString()->GetDexFile(); - if (!kUseReadBarrier || kUseBakerReadBarrier) { - // The string entry page address was preserved in temp_ thanks to kSaveEverything. - } else { - // For non-Baker read barrier, we need to re-calculate the address of the string entry page. - adrp_label_ = arm64_codegen->NewStringBssEntryPatch(dex_file, string_index); - arm64_codegen->EmitAdrpPlaceholder(adrp_label_, temp_); - } - vixl::aarch64::Label* strp_label = - arm64_codegen->NewStringBssEntryPatch(dex_file, string_index, adrp_label_); - { - SingleEmissionCheckScope guard(arm64_codegen->GetVIXLAssembler()); - __ Bind(strp_label); - __ str(RegisterFrom(locations->Out(), DataType::Type::kReference), - MemOperand(temp_, /* offset placeholder */ 0)); - } - __ B(GetExitLabel()); } const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM64"; } private: - const Register temp_; - vixl::aarch64::Label* adrp_label_; - DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM64); }; @@ -4883,7 +4814,6 @@ void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) { if (cls->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { // Rely on the type resolution or initialization and marking to save everything we need. - locations->AddTemp(FixedTempLocation()); RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConvention calling_convention; caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode())); @@ -4910,8 +4840,6 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA Location out_loc = cls->GetLocations()->Out(); Register out = OutputRegister(cls); - Register bss_entry_temp; - vixl::aarch64::Label* bss_entry_adrp_label = nullptr; const ReadBarrierOption read_barrier_option = cls->IsInBootImage() ? kWithoutReadBarrier @@ -4975,16 +4903,16 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA // Add ADRP with its PC-relative Class .bss entry patch. const DexFile& dex_file = cls->GetDexFile(); dex::TypeIndex type_index = cls->GetTypeIndex(); - bss_entry_temp = XRegisterFrom(cls->GetLocations()->GetTemp(0)); - bss_entry_adrp_label = codegen_->NewBssEntryTypePatch(dex_file, type_index); - codegen_->EmitAdrpPlaceholder(bss_entry_adrp_label, bss_entry_temp); + 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. vixl::aarch64::Label* ldr_label = - codegen_->NewBssEntryTypePatch(dex_file, type_index, bss_entry_adrp_label); + codegen_->NewBssEntryTypePatch(dex_file, type_index, adrp_label); // /* GcRoot */ out = *(base_address + offset) /* PC-relative */ GenerateGcRootFieldLoad(cls, out_loc, - bss_entry_temp, + temp, /* offset placeholder */ 0u, ldr_label, read_barrier_option); @@ -5013,7 +4941,7 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA if (generate_null_check || do_clinit) { DCHECK(cls->CanCallRuntime()); SlowPathCodeARM64* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathARM64( - cls, cls, cls->GetDexPc(), do_clinit, bss_entry_temp, bss_entry_adrp_label); + cls, cls, cls->GetDexPc(), do_clinit); codegen_->AddSlowPath(slow_path); if (generate_null_check) { __ Cbz(out, slow_path->GetEntryLabel()); @@ -5078,7 +5006,6 @@ void LocationsBuilderARM64::VisitLoadString(HLoadString* load) { if (load->GetLoadKind() == HLoadString::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { // Rely on the pResolveString and marking to save everything we need. - locations->AddTemp(FixedTempLocation()); RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConvention calling_convention; caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode())); @@ -5138,7 +5065,7 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD const DexFile& dex_file = load->GetDexFile(); const dex::StringIndex string_index = load->GetStringIndex(); DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); - Register temp = XRegisterFrom(load->GetLocations()->GetTemp(0)); + 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. @@ -5152,7 +5079,7 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD ldr_label, kCompilerReadBarrierOption); SlowPathCodeARM64* slow_path = - new (codegen_->GetScopedAllocator()) LoadStringSlowPathARM64(load, temp, adrp_label); + new (codegen_->GetScopedAllocator()) LoadStringSlowPathARM64(load); codegen_->AddSlowPath(slow_path); __ Cbz(out.X(), slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 9e7455d488..3f8f0c44f3 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -532,29 +532,12 @@ class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); Location out = locations->Out(); - constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier); CodeGeneratorARMVIXL* arm_codegen = down_cast(codegen); __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConventionARMVIXL calling_convention; - // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the address of the entry. - DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); - bool is_load_class_bss_entry = - (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry); - vixl32::Register entry_address; - if (is_load_class_bss_entry && call_saves_everything_except_r0) { - vixl32::Register temp = RegisterFrom(locations->GetTemp(0)); - // In the unlucky case that the `temp` is R0, we preserve the address in `out` across - // the kSaveEverything call. - bool temp_is_r0 = temp.Is(calling_convention.GetRegisterAt(0)); - entry_address = temp_is_r0 ? RegisterFrom(out) : temp; - DCHECK(!entry_address.Is(calling_convention.GetRegisterAt(0))); - if (temp_is_r0) { - __ Mov(entry_address, temp); - } - } dex::TypeIndex type_index = cls_->GetTypeIndex(); __ Mov(calling_convention.GetRegisterAt(0), type_index.index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage @@ -566,22 +549,6 @@ class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL { CheckEntrypointTypes(); } - // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. - if (is_load_class_bss_entry) { - if (call_saves_everything_except_r0) { - // The class entry address was preserved in `entry_address` thanks to kSaveEverything. - __ Str(r0, MemOperand(entry_address)); - } else { - // For non-Baker read barrier, we need to re-calculate the address of the string entry. - UseScratchRegisterScope temps( - down_cast(codegen)->GetVIXLAssembler()); - vixl32::Register temp = temps.Acquire(); - CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = - arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index); - arm_codegen->EmitMovwMovtPlaceholder(labels, temp); - __ Str(r0, MemOperand(temp)); - } - } // Move the class to the desired location. if (out.IsValid()) { DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); @@ -616,48 +583,17 @@ class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL { DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry); LocationSummary* locations = instruction_->GetLocations(); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); - HLoadString* load = instruction_->AsLoadString(); - const dex::StringIndex string_index = load->GetStringIndex(); - vixl32::Register out = OutputRegister(load); - constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier); + const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex(); CodeGeneratorARMVIXL* arm_codegen = down_cast(codegen); __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConventionARMVIXL calling_convention; - // In the unlucky case that the `temp` is R0, we preserve the address in `out` across - // the kSaveEverything call. - vixl32::Register entry_address; - if (call_saves_everything_except_r0) { - vixl32::Register temp = RegisterFrom(locations->GetTemp(0)); - bool temp_is_r0 = (temp.Is(calling_convention.GetRegisterAt(0))); - entry_address = temp_is_r0 ? out : temp; - DCHECK(!entry_address.Is(calling_convention.GetRegisterAt(0))); - if (temp_is_r0) { - __ Mov(entry_address, temp); - } - } - __ Mov(calling_convention.GetRegisterAt(0), string_index.index_); arm_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes(); - // Store the resolved String to the .bss entry. - if (call_saves_everything_except_r0) { - // The string entry address was preserved in `entry_address` thanks to kSaveEverything. - __ Str(r0, MemOperand(entry_address)); - } else { - // For non-Baker read barrier, we need to re-calculate the address of the string entry. - UseScratchRegisterScope temps( - down_cast(codegen)->GetVIXLAssembler()); - vixl32::Register temp = temps.Acquire(); - CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = - arm_codegen->NewStringBssEntryPatch(load->GetDexFile(), string_index); - arm_codegen->EmitMovwMovtPlaceholder(labels, temp); - __ Str(r0, MemOperand(temp)); - } - arm_codegen->Move32(locations->Out(), LocationFrom(r0)); RestoreLiveRegisters(codegen, locations); @@ -7104,9 +7040,6 @@ void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) { if (load_kind == HLoadClass::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { // Rely on the type resolution or initialization and marking to save everything we need. - // Note that IP may be clobbered by saving/restoring the live register (only one thanks - // to the custom calling convention) or by marking, so we request a different temp. - locations->AddTemp(Location::RequiresRegister()); RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConventionARMVIXL calling_convention; caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0))); @@ -7189,13 +7122,10 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ break; } case HLoadClass::LoadKind::kBssEntry: { - vixl32::Register temp = (!kUseReadBarrier || kUseBakerReadBarrier) - ? RegisterFrom(locations->GetTemp(0)) - : out; CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); - codegen_->EmitMovwMovtPlaceholder(labels, temp); - GenerateGcRootFieldLoad(cls, out_loc, temp, /* offset */ 0, read_barrier_option); + codegen_->EmitMovwMovtPlaceholder(labels, out); + GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); generate_null_check = true; break; } @@ -7296,9 +7226,6 @@ void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) { if (load_kind == HLoadString::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { // Rely on the pResolveString and marking to save everything we need, including temps. - // Note that IP may be clobbered by saving/restoring the live register (only one thanks - // to the custom calling convention) or by marking, so we request a different temp. - locations->AddTemp(Location::RequiresRegister()); RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConventionARMVIXL calling_convention; caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0))); @@ -7348,13 +7275,10 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE } case HLoadString::LoadKind::kBssEntry: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); - vixl32::Register temp = (!kUseReadBarrier || kUseBakerReadBarrier) - ? RegisterFrom(locations->GetTemp(0)) - : out; CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex()); - codegen_->EmitMovwMovtPlaceholder(labels, temp); - GenerateGcRootFieldLoad(load, out_loc, temp, /* offset */ 0, kCompilerReadBarrierOption); + codegen_->EmitMovwMovtPlaceholder(labels, out); + GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); LoadStringSlowPathARMVIXL* slow_path = new (codegen_->GetScopedAllocator()) LoadStringSlowPathARMVIXL(load); codegen_->AddSlowPath(slow_path); diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index ddec0cc453..d6922d2f3f 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -220,13 +220,11 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { LoadClassSlowPathMIPS(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, - bool do_clinit, - const CodeGeneratorMIPS::PcRelativePatchInfo* bss_info_high = nullptr) + bool do_clinit) : SlowPathCodeMIPS(at), cls_(cls), dex_pc_(dex_pc), - do_clinit_(do_clinit), - bss_info_high_(bss_info_high) { + do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } @@ -234,28 +232,11 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { LocationSummary* locations = instruction_->GetLocations(); Location out = locations->Out(); CodeGeneratorMIPS* mips_codegen = down_cast(codegen); - const bool baker_or_no_read_barriers = (!kUseReadBarrier || kUseBakerReadBarrier); InvokeRuntimeCallingConvention calling_convention; DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); - const bool is_load_class_bss_entry = - (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry); __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); - // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the address of the entry. - Register entry_address = kNoRegister; - if (is_load_class_bss_entry && baker_or_no_read_barriers) { - Register temp = locations->GetTemp(0).AsRegister(); - bool temp_is_a0 = (temp == calling_convention.GetRegisterAt(0)); - // In the unlucky case that `temp` is A0, we preserve the address in `out` across the - // kSaveEverything call. - entry_address = temp_is_a0 ? out.AsRegister() : temp; - DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0)); - if (temp_is_a0) { - __ Move(entry_address, temp); - } - } - dex::TypeIndex type_index = cls_->GetTypeIndex(); __ LoadConst32(calling_convention.GetRegisterAt(0), type_index.index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage @@ -267,18 +248,6 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { CheckEntrypointTypes(); } - // For HLoadClass/kBssEntry, store the resolved class to the BSS entry. - if (is_load_class_bss_entry && baker_or_no_read_barriers) { - // The class entry address was preserved in `entry_address` thanks to kSaveEverything. - DCHECK(bss_info_high_); - CodeGeneratorMIPS::PcRelativePatchInfo* info_low = - mips_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index, bss_info_high_); - __ Sw(calling_convention.GetRegisterAt(0), - entry_address, - /* placeholder */ 0x5678, - &info_low->label); - } - // Move the class to the desired location. if (out.IsValid()) { DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); @@ -289,21 +258,6 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { } RestoreLiveRegisters(codegen, locations); - // For HLoadClass/kBssEntry, store the resolved class to the BSS entry. - if (is_load_class_bss_entry && !baker_or_no_read_barriers) { - // For non-Baker read barriers we need to re-calculate the address of - // the class entry. - const bool isR6 = mips_codegen->GetInstructionSetFeatures().IsR6(); - const bool has_irreducible_loops = codegen->GetGraph()->HasIrreducibleLoops(); - Register base = - (isR6 || has_irreducible_loops) ? ZERO : locations->InAt(0).AsRegister(); - CodeGeneratorMIPS::PcRelativePatchInfo* info_high = - mips_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index); - CodeGeneratorMIPS::PcRelativePatchInfo* info_low = - mips_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index, info_high); - mips_codegen->EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base); - __ Sw(out.AsRegister(), TMP, /* placeholder */ 0x5678, &info_low->label); - } __ B(GetExitLabel()); } @@ -319,92 +273,41 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { // Whether to initialize the class. const bool do_clinit_; - // Pointer to the high half PC-relative patch info for HLoadClass/kBssEntry. - const CodeGeneratorMIPS::PcRelativePatchInfo* bss_info_high_; - DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathMIPS); }; class LoadStringSlowPathMIPS : public SlowPathCodeMIPS { public: - explicit LoadStringSlowPathMIPS(HLoadString* instruction, - const CodeGeneratorMIPS::PcRelativePatchInfo* bss_info_high) - : SlowPathCodeMIPS(instruction), bss_info_high_(bss_info_high) {} + explicit LoadStringSlowPathMIPS(HLoadString* instruction) + : SlowPathCodeMIPS(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { DCHECK(instruction_->IsLoadString()); DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry); LocationSummary* locations = instruction_->GetLocations(); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); - HLoadString* load = instruction_->AsLoadString(); - const dex::StringIndex string_index = load->GetStringIndex(); - Register out = locations->Out().AsRegister(); + const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex(); CodeGeneratorMIPS* mips_codegen = down_cast(codegen); - const bool baker_or_no_read_barriers = (!kUseReadBarrier || kUseBakerReadBarrier); InvokeRuntimeCallingConvention calling_convention; __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); - // For HLoadString/kBssEntry/kSaveEverything, make sure we preserve the address of the entry. - Register entry_address = kNoRegister; - if (baker_or_no_read_barriers) { - Register temp = locations->GetTemp(0).AsRegister(); - bool temp_is_a0 = (temp == calling_convention.GetRegisterAt(0)); - // In the unlucky case that `temp` is A0, we preserve the address in `out` across the - // kSaveEverything call. - entry_address = temp_is_a0 ? out : temp; - DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0)); - if (temp_is_a0) { - __ Move(entry_address, temp); - } - } - __ LoadConst32(calling_convention.GetRegisterAt(0), string_index.index_); mips_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes(); - // Store the resolved string to the BSS entry. - if (baker_or_no_read_barriers) { - // The string entry address was preserved in `entry_address` thanks to kSaveEverything. - DCHECK(bss_info_high_); - CodeGeneratorMIPS::PcRelativePatchInfo* info_low = - mips_codegen->NewStringBssEntryPatch(load->GetDexFile(), string_index, bss_info_high_); - __ Sw(calling_convention.GetRegisterAt(0), - entry_address, - /* placeholder */ 0x5678, - &info_low->label); - } - DataType::Type type = instruction_->GetType(); mips_codegen->MoveLocation(locations->Out(), Location::RegisterLocation(calling_convention.GetRegisterAt(0)), type); RestoreLiveRegisters(codegen, locations); - // Store the resolved string to the BSS entry. - if (!baker_or_no_read_barriers) { - // For non-Baker read barriers we need to re-calculate the address of - // the string entry. - const bool isR6 = mips_codegen->GetInstructionSetFeatures().IsR6(); - const bool has_irreducible_loops = codegen->GetGraph()->HasIrreducibleLoops(); - Register base = - (isR6 || has_irreducible_loops) ? ZERO : locations->InAt(0).AsRegister(); - CodeGeneratorMIPS::PcRelativePatchInfo* info_high = - mips_codegen->NewStringBssEntryPatch(load->GetDexFile(), string_index); - CodeGeneratorMIPS::PcRelativePatchInfo* info_low = - mips_codegen->NewStringBssEntryPatch(load->GetDexFile(), string_index, info_high); - mips_codegen->EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base); - __ Sw(out, TMP, /* placeholder */ 0x5678, &info_low->label); - } __ B(GetExitLabel()); } const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathMIPS"; } private: - // Pointer to the high half PC-relative patch info. - const CodeGeneratorMIPS::PcRelativePatchInfo* bss_info_high_; - DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathMIPS); }; @@ -7736,8 +7639,6 @@ void LocationsBuilderMIPS::VisitLoadClass(HLoadClass* cls) { if (load_kind == HLoadClass::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { // Rely on the type resolution or initialization and marking to save everything we need. - // Request a temp to hold the BSS entry location for the slow path. - locations->AddTemp(Location::RequiresRegister()); RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConvention calling_convention; caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); @@ -7786,7 +7687,6 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF ? kWithoutReadBarrier : kCompilerReadBarrierOption; bool generate_null_check = false; - CodeGeneratorMIPS::PcRelativePatchInfo* bss_info_high = nullptr; switch (load_kind) { case HLoadClass::LoadKind::kReferrersClass: { DCHECK(!cls->CanCallRuntime()); @@ -7845,17 +7745,16 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF break; } case HLoadClass::LoadKind::kBssEntry: { - bss_info_high = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); + CodeGeneratorMIPS::PcRelativePatchInfo* bss_info_high = + codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); CodeGeneratorMIPS::PcRelativePatchInfo* info_low = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex(), bss_info_high); - constexpr bool non_baker_read_barrier = kUseReadBarrier && !kUseBakerReadBarrier; - Register temp = non_baker_read_barrier ? out : locations->GetTemp(0).AsRegister(); codegen_->EmitPcRelativeAddressPlaceholderHigh(bss_info_high, - temp, + out, base_or_current_method_reg); GenerateGcRootFieldLoad(cls, out_loc, - temp, + out, /* placeholder */ 0x5678, read_barrier_option, &info_low->label); @@ -7887,7 +7786,7 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF if (generate_null_check || cls->MustGenerateClinitCheck()) { DCHECK(cls->CanCallRuntime()); SlowPathCodeMIPS* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathMIPS( - cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck(), bss_info_high); + cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); codegen_->AddSlowPath(slow_path); if (generate_null_check) { __ Beqz(out, slow_path->GetEntryLabel()); @@ -7960,8 +7859,6 @@ void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) { if (load_kind == HLoadString::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { // Rely on the pResolveString and marking to save everything we need. - // Request a temp to hold the BSS entry location for the slow path. - locations->AddTemp(Location::RequiresRegister()); RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConvention calling_convention; caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); @@ -8041,19 +7938,17 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_ codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex()); CodeGeneratorMIPS::PcRelativePatchInfo* info_low = codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex(), info_high); - constexpr bool non_baker_read_barrier = kUseReadBarrier && !kUseBakerReadBarrier; - Register temp = non_baker_read_barrier ? out : locations->GetTemp(0).AsRegister(); codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, - temp, + out, base_or_current_method_reg); GenerateGcRootFieldLoad(load, out_loc, - temp, + out, /* placeholder */ 0x5678, kCompilerReadBarrierOption, &info_low->label); SlowPathCodeMIPS* slow_path = - new (codegen_->GetScopedAllocator()) LoadStringSlowPathMIPS(load, info_high); + new (codegen_->GetScopedAllocator()) LoadStringSlowPathMIPS(load); codegen_->AddSlowPath(slow_path); __ Beqz(out, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 0a6d9159d1..ee33b3f335 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -175,13 +175,11 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { LoadClassSlowPathMIPS64(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, - bool do_clinit, - const CodeGeneratorMIPS64::PcRelativePatchInfo* bss_info_high = nullptr) + bool do_clinit) : SlowPathCodeMIPS64(at), cls_(cls), dex_pc_(dex_pc), - do_clinit_(do_clinit), - bss_info_high_(bss_info_high) { + do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } @@ -189,28 +187,11 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { LocationSummary* locations = instruction_->GetLocations(); Location out = locations->Out(); CodeGeneratorMIPS64* mips64_codegen = down_cast(codegen); - const bool baker_or_no_read_barriers = (!kUseReadBarrier || kUseBakerReadBarrier); InvokeRuntimeCallingConvention calling_convention; DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); - const bool is_load_class_bss_entry = - (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry); __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); - // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the address of the entry. - GpuRegister entry_address = kNoGpuRegister; - if (is_load_class_bss_entry && baker_or_no_read_barriers) { - GpuRegister temp = locations->GetTemp(0).AsRegister(); - bool temp_is_a0 = (temp == calling_convention.GetRegisterAt(0)); - // In the unlucky case that `temp` is A0, we preserve the address in `out` across the - // kSaveEverything call. - entry_address = temp_is_a0 ? out.AsRegister() : temp; - DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0)); - if (temp_is_a0) { - __ Move(entry_address, temp); - } - } - dex::TypeIndex type_index = cls_->GetTypeIndex(); __ LoadConst32(calling_convention.GetRegisterAt(0), type_index.index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage @@ -222,19 +203,6 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { CheckEntrypointTypes(); } - // For HLoadClass/kBssEntry, store the resolved class to the BSS entry. - if (is_load_class_bss_entry && baker_or_no_read_barriers) { - // The class entry address was preserved in `entry_address` thanks to kSaveEverything. - DCHECK(bss_info_high_); - CodeGeneratorMIPS64::PcRelativePatchInfo* info_low = - mips64_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index, bss_info_high_); - __ Bind(&info_low->label); - __ StoreToOffset(kStoreWord, - calling_convention.GetRegisterAt(0), - entry_address, - /* placeholder */ 0x5678); - } - // Move the class to the desired location. if (out.IsValid()) { DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); @@ -245,17 +213,6 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { } RestoreLiveRegisters(codegen, locations); - // For HLoadClass/kBssEntry, store the resolved class to the BSS entry. - if (is_load_class_bss_entry && !baker_or_no_read_barriers) { - // For non-Baker read barriers we need to re-calculate the address of - // the class entry. - CodeGeneratorMIPS64::PcRelativePatchInfo* info_high = - mips64_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index); - CodeGeneratorMIPS64::PcRelativePatchInfo* info_low = - mips64_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index, info_high); - mips64_codegen->EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, info_low); - __ StoreToOffset(kStoreWord, out.AsRegister(), TMP, /* placeholder */ 0x5678); - } __ Bc(GetExitLabel()); } @@ -271,46 +228,25 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { // Whether to initialize the class. const bool do_clinit_; - // Pointer to the high half PC-relative patch info for HLoadClass/kBssEntry. - const CodeGeneratorMIPS64::PcRelativePatchInfo* bss_info_high_; - DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathMIPS64); }; class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 { public: - explicit LoadStringSlowPathMIPS64(HLoadString* instruction, - const CodeGeneratorMIPS64::PcRelativePatchInfo* bss_info_high) - : SlowPathCodeMIPS64(instruction), bss_info_high_(bss_info_high) {} + explicit LoadStringSlowPathMIPS64(HLoadString* instruction) + : SlowPathCodeMIPS64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { DCHECK(instruction_->IsLoadString()); DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry); LocationSummary* locations = instruction_->GetLocations(); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); - HLoadString* load = instruction_->AsLoadString(); - const dex::StringIndex string_index = load->GetStringIndex(); - GpuRegister out = locations->Out().AsRegister(); + const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex(); CodeGeneratorMIPS64* mips64_codegen = down_cast(codegen); - const bool baker_or_no_read_barriers = (!kUseReadBarrier || kUseBakerReadBarrier); InvokeRuntimeCallingConvention calling_convention; __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); - // For HLoadString/kBssEntry/kSaveEverything, make sure we preserve the address of the entry. - GpuRegister entry_address = kNoGpuRegister; - if (baker_or_no_read_barriers) { - GpuRegister temp = locations->GetTemp(0).AsRegister(); - bool temp_is_a0 = (temp == calling_convention.GetRegisterAt(0)); - // In the unlucky case that `temp` is A0, we preserve the address in `out` across the - // kSaveEverything call. - entry_address = temp_is_a0 ? out : temp; - DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0)); - if (temp_is_a0) { - __ Move(entry_address, temp); - } - } - __ LoadConst32(calling_convention.GetRegisterAt(0), string_index.index_); mips64_codegen->InvokeRuntime(kQuickResolveString, instruction_, @@ -318,47 +254,18 @@ class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 { this); CheckEntrypointTypes(); - // Store the resolved string to the BSS entry. - if (baker_or_no_read_barriers) { - // The string entry address was preserved in `entry_address` thanks to kSaveEverything. - DCHECK(bss_info_high_); - CodeGeneratorMIPS64::PcRelativePatchInfo* info_low = - mips64_codegen->NewStringBssEntryPatch(load->GetDexFile(), - string_index, - bss_info_high_); - __ Bind(&info_low->label); - __ StoreToOffset(kStoreWord, - calling_convention.GetRegisterAt(0), - entry_address, - /* placeholder */ 0x5678); - } - DataType::Type type = instruction_->GetType(); mips64_codegen->MoveLocation(locations->Out(), Location::RegisterLocation(calling_convention.GetRegisterAt(0)), type); RestoreLiveRegisters(codegen, locations); - // Store the resolved string to the BSS entry. - if (!baker_or_no_read_barriers) { - // For non-Baker read barriers we need to re-calculate the address of - // the string entry. - CodeGeneratorMIPS64::PcRelativePatchInfo* info_high = - mips64_codegen->NewStringBssEntryPatch(load->GetDexFile(), string_index); - CodeGeneratorMIPS64::PcRelativePatchInfo* info_low = - mips64_codegen->NewStringBssEntryPatch(load->GetDexFile(), string_index, info_high); - mips64_codegen->EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, info_low); - __ StoreToOffset(kStoreWord, out, TMP, /* placeholder */ 0x5678); - } __ Bc(GetExitLabel()); } const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathMIPS64"; } private: - // Pointer to the high half PC-relative patch info. - const CodeGeneratorMIPS64::PcRelativePatchInfo* bss_info_high_; - DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathMIPS64); }; @@ -5979,8 +5886,6 @@ void LocationsBuilderMIPS64::VisitLoadClass(HLoadClass* cls) { if (load_kind == HLoadClass::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { // Rely on the type resolution or initialization and marking to save everything we need. - // Request a temp to hold the BSS entry location for the slow path. - locations->AddTemp(Location::RequiresRegister()); RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConvention calling_convention; caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); @@ -6014,7 +5919,6 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S ? kWithoutReadBarrier : kCompilerReadBarrierOption; bool generate_null_check = false; - CodeGeneratorMIPS64::PcRelativePatchInfo* bss_info_high = nullptr; switch (load_kind) { case HLoadClass::LoadKind::kReferrersClass: DCHECK(!cls->CanCallRuntime()); @@ -6064,17 +5968,14 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S break; } case HLoadClass::LoadKind::kBssEntry: { - bss_info_high = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); + CodeGeneratorMIPS64::PcRelativePatchInfo* bss_info_high = + codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); CodeGeneratorMIPS64::PcRelativePatchInfo* info_low = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex(), bss_info_high); - constexpr bool non_baker_read_barrier = kUseReadBarrier && !kUseBakerReadBarrier; - GpuRegister temp = non_baker_read_barrier - ? out - : locations->GetTemp(0).AsRegister(); - codegen_->EmitPcRelativeAddressPlaceholderHigh(bss_info_high, temp); + codegen_->EmitPcRelativeAddressPlaceholderHigh(bss_info_high, out); GenerateGcRootFieldLoad(cls, out_loc, - temp, + out, /* placeholder */ 0x5678, read_barrier_option, &info_low->label); @@ -6098,7 +5999,7 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S if (generate_null_check || cls->MustGenerateClinitCheck()) { DCHECK(cls->CanCallRuntime()); SlowPathCodeMIPS64* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathMIPS64( - cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck(), bss_info_high); + cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); codegen_->AddSlowPath(slow_path); if (generate_null_check) { __ Beqzc(out, slow_path->GetEntryLabel()); @@ -6146,8 +6047,6 @@ void LocationsBuilderMIPS64::VisitLoadString(HLoadString* load) { if (load_kind == HLoadString::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { // Rely on the pResolveString and marking to save everything we need. - // Request a temp to hold the BSS entry location for the slow path. - locations->AddTemp(Location::RequiresRegister()); RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConvention calling_convention; caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); @@ -6203,19 +6102,15 @@ void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) NO_THREA codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex()); CodeGeneratorMIPS64::PcRelativePatchInfo* info_low = codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex(), info_high); - constexpr bool non_baker_read_barrier = kUseReadBarrier && !kUseBakerReadBarrier; - GpuRegister temp = non_baker_read_barrier - ? out - : locations->GetTemp(0).AsRegister(); - codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, temp); + codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, out); GenerateGcRootFieldLoad(load, out_loc, - temp, + out, /* placeholder */ 0x5678, kCompilerReadBarrierOption, &info_low->label); SlowPathCodeMIPS64* slow_path = - new (codegen_->GetScopedAllocator()) LoadStringSlowPathMIPS64(load, info_high); + new (codegen_->GetScopedAllocator()) LoadStringSlowPathMIPS64(load); codegen_->AddSlowPath(slow_path); __ Beqzc(out, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index ad0e71aaf4..2e8170ecc4 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -240,13 +240,6 @@ class LoadStringSlowPathX86 : public SlowPathCode { x86_codegen->Move32(locations->Out(), Location::RegisterLocation(EAX)); RestoreLiveRegisters(codegen, locations); - // Store the resolved String to the BSS entry. - Register method_address = locations->InAt(0).AsRegister(); - __ movl(Address(method_address, CodeGeneratorX86::kDummy32BitOffset), - locations->Out().AsRegister()); - Label* fixup_label = x86_codegen->NewStringBssEntryPatch(instruction_->AsLoadString()); - __ Bind(fixup_label); - __ jmp(GetExitLabel()); } @@ -293,16 +286,6 @@ class LoadClassSlowPathX86 : public SlowPathCode { x86_codegen->Move32(out, Location::RegisterLocation(EAX)); } RestoreLiveRegisters(codegen, locations); - // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. - DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); - if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) { - DCHECK(out.IsValid()); - Register method_address = locations->InAt(0).AsRegister(); - __ movl(Address(method_address, CodeGeneratorX86::kDummy32BitOffset), - locations->Out().AsRegister()); - Label* fixup_label = x86_codegen->NewTypeBssEntryPatch(cls_); - __ Bind(fixup_label); - } __ jmp(GetExitLabel()); } diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index d64a49704e..e25688c9a3 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -273,15 +273,6 @@ class LoadClassSlowPathX86_64 : public SlowPathCode { } RestoreLiveRegisters(codegen, locations); - // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. - DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); - if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) { - DCHECK(out.IsValid()); - __ movl(Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false), - locations->Out().AsRegister()); - Label* fixup_label = x86_64_codegen->NewTypeBssEntryPatch(cls_); - __ Bind(fixup_label); - } __ jmp(GetExitLabel()); } @@ -323,12 +314,6 @@ class LoadStringSlowPathX86_64 : public SlowPathCode { x86_64_codegen->Move(locations->Out(), Location::RegisterLocation(RAX)); RestoreLiveRegisters(codegen, locations); - // Store the resolved String to the BSS entry. - __ movl(Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false), - locations->Out().AsRegister()); - Label* fixup_label = x86_64_codegen->NewStringBssEntryPatch(instruction_->AsLoadString()); - __ Bind(fixup_label); - __ jmp(GetExitLabel()); } diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index 13d2655917..6bebf7d2da 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -220,6 +220,7 @@ art_cc_test { "linker/elf_writer_test.cc", "linker/image_test.cc", "linker/image_write_read_test.cc", + "linker/index_bss_mapping_encoder_test.cc", "linker/multi_oat_relative_patcher_test.cc", "linker/oat_writer_test.cc", ], diff --git a/dex2oat/linker/index_bss_mapping_encoder.h b/dex2oat/linker/index_bss_mapping_encoder.h new file mode 100644 index 0000000000..9bc14329ff --- /dev/null +++ b/dex2oat/linker/index_bss_mapping_encoder.h @@ -0,0 +1,86 @@ +/* + * 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_DEX2OAT_LINKER_INDEX_BSS_MAPPING_ENCODER_H_ +#define ART_DEX2OAT_LINKER_INDEX_BSS_MAPPING_ENCODER_H_ + +#include "base/bit_utils.h" +#include "base/bit_vector-inl.h" +#include "base/logging.h" +#include "index_bss_mapping.h" + +namespace art { +namespace linker { + +// Helper class for encoding compressed IndexBssMapping. +class IndexBssMappingEncoder { + public: + IndexBssMappingEncoder(size_t number_of_indexes, size_t slot_size) + : index_bits_(IndexBssMappingEntry::IndexBits(number_of_indexes)), + slot_size_(slot_size) { + entry_.index_and_mask = static_cast(-1); + entry_.bss_offset = static_cast(-1); + DCHECK_NE(number_of_indexes, 0u); + } + + // Try to merge the next index -> bss_offset mapping into the current entry. + // Return true on success, false on failure. + bool TryMerge(uint32_t index, uint32_t bss_offset) { + DCHECK_LE(MinimumBitsToStore(index), index_bits_); + DCHECK_NE(index, entry_.GetIndex(index_bits_)); + if (entry_.bss_offset + slot_size_ != bss_offset) { + return false; + } + uint32_t diff = index - entry_.GetIndex(index_bits_); + if (diff > 32u - index_bits_) { + return false; + } + uint32_t mask = entry_.GetMask(index_bits_); + if ((mask & ~(static_cast(-1) << diff)) != 0u) { + return false; + } + // Insert the bit indicating the index we've just overwritten + // and shift bits indicating indexes before that. + mask = ((mask << index_bits_) >> diff) | (static_cast(1u) << (32 - diff)); + entry_.index_and_mask = mask | index; + entry_.bss_offset = bss_offset; + return true; + } + + void Reset(uint32_t method_index, uint32_t bss_offset) { + DCHECK_LE(MinimumBitsToStore(method_index), index_bits_); + entry_.index_and_mask = method_index; // Mask bits set to 0. + entry_.bss_offset = bss_offset; + } + + IndexBssMappingEntry GetEntry() { + return entry_; + } + + size_t GetIndexBits() const { + return index_bits_; + } + + private: + const size_t index_bits_; + const size_t slot_size_; + IndexBssMappingEntry entry_; +}; + +} // namespace linker +} // namespace art + +#endif // ART_DEX2OAT_LINKER_INDEX_BSS_MAPPING_ENCODER_H_ diff --git a/dex2oat/linker/index_bss_mapping_encoder_test.cc b/dex2oat/linker/index_bss_mapping_encoder_test.cc new file mode 100644 index 0000000000..d7ca2a5742 --- /dev/null +++ b/dex2oat/linker/index_bss_mapping_encoder_test.cc @@ -0,0 +1,104 @@ +/* + * 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. + */ + +#include "index_bss_mapping_encoder.h" + +#include "base/enums.h" +#include "gtest/gtest.h" + +namespace art { +namespace linker { + +TEST(IndexBssMappingEncoder, TryMerge16BitIndex) { + for (PointerSize pointer_size : {PointerSize::k32, PointerSize::k64}) { + size_t raw_pointer_size = static_cast(pointer_size); + IndexBssMappingEncoder encoder(/* number_of_indexes */ 0x10000, raw_pointer_size); + encoder.Reset(1u, 0u); + ASSERT_FALSE(encoder.TryMerge(5u, raw_pointer_size + 1)); // Wrong bss_offset difference. + ASSERT_FALSE(encoder.TryMerge(18u, raw_pointer_size)); // Index out of range. + ASSERT_TRUE(encoder.TryMerge(5u, raw_pointer_size)); + ASSERT_EQ(0u, encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 1u, raw_pointer_size)); + ASSERT_EQ(raw_pointer_size, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 5u, raw_pointer_size)); + ASSERT_EQ(IndexBssMappingLookup::npos, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 17u, raw_pointer_size)); + ASSERT_FALSE(encoder.TryMerge(17u, 2 * raw_pointer_size + 1)); // Wrong bss_offset difference. + ASSERT_FALSE(encoder.TryMerge(18u, 2 * raw_pointer_size)); // Index out of range. + ASSERT_TRUE(encoder.TryMerge(17u, 2 * raw_pointer_size)); + ASSERT_EQ(0u, encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 1u, raw_pointer_size)); + ASSERT_EQ(raw_pointer_size, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 5u, raw_pointer_size)); + ASSERT_EQ(2 * raw_pointer_size, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 17u, raw_pointer_size)); + ASSERT_EQ(0x00110000u | 17u, encoder.GetEntry().index_and_mask); + ASSERT_FALSE(encoder.TryMerge(18u, 3 * raw_pointer_size)); // Index out of range. + } +} + +TEST(IndexBssMappingEncoder, TryMerge8BitIndex) { + for (PointerSize pointer_size : {PointerSize::k32, PointerSize::k64}) { + size_t raw_pointer_size = static_cast(pointer_size); + IndexBssMappingEncoder encoder(/* number_of_indexes */ 0x100, raw_pointer_size); + encoder.Reset(1u, 0u); + ASSERT_FALSE(encoder.TryMerge(5u, raw_pointer_size + 1)); // Wrong bss_offset difference. + ASSERT_FALSE(encoder.TryMerge(26u, raw_pointer_size)); // Index out of range. + ASSERT_TRUE(encoder.TryMerge(5u, raw_pointer_size)); + ASSERT_EQ(0u, encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 1u, raw_pointer_size)); + ASSERT_EQ(raw_pointer_size, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 5u, raw_pointer_size)); + ASSERT_EQ(IndexBssMappingLookup::npos, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 17u, raw_pointer_size)); + ASSERT_FALSE(encoder.TryMerge(25u, 2 * raw_pointer_size + 1)); // Wrong bss_offset difference. + ASSERT_FALSE(encoder.TryMerge(26u, 2 * raw_pointer_size)); // Index out of range. + ASSERT_TRUE(encoder.TryMerge(25u, 2 * raw_pointer_size)); + ASSERT_EQ(0u, encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 1u, raw_pointer_size)); + ASSERT_EQ(raw_pointer_size, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 5u, raw_pointer_size)); + ASSERT_EQ(2 * raw_pointer_size, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 25u, raw_pointer_size)); + ASSERT_EQ(0x00001100u | 25u, encoder.GetEntry().index_and_mask); + ASSERT_FALSE(encoder.TryMerge(26u, 3 * raw_pointer_size)); // Index out of range. + } +} + +TEST(IndexBssMappingEncoder, TryMerge20BitIndex) { + for (PointerSize pointer_size : {PointerSize::k32, PointerSize::k64}) { + size_t raw_pointer_size = static_cast(pointer_size); + IndexBssMappingEncoder encoder(/* number_of_indexes */ 0x100000, raw_pointer_size); + encoder.Reset(1u, 0u); + ASSERT_FALSE(encoder.TryMerge(5u, raw_pointer_size + 1)); // Wrong bss_offset difference. + ASSERT_FALSE(encoder.TryMerge(14u, raw_pointer_size)); // Index out of range. + ASSERT_TRUE(encoder.TryMerge(5u, raw_pointer_size)); + ASSERT_EQ(0u, encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 1u, raw_pointer_size)); + ASSERT_EQ(raw_pointer_size, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 5u, raw_pointer_size)); + ASSERT_EQ(IndexBssMappingLookup::npos, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 17u, raw_pointer_size)); + ASSERT_FALSE(encoder.TryMerge(13u, 2 * raw_pointer_size + 1)); // Wrong bss_offset difference. + ASSERT_FALSE(encoder.TryMerge(14u, 2 * raw_pointer_size)); // Index out of range. + ASSERT_TRUE(encoder.TryMerge(13u, 2 * raw_pointer_size)); + ASSERT_EQ(0u, encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 1u, raw_pointer_size)); + ASSERT_EQ(raw_pointer_size, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 5u, raw_pointer_size)); + ASSERT_EQ(2 * raw_pointer_size, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 13u, raw_pointer_size)); + ASSERT_EQ(0x01100000u | 13u, encoder.GetEntry().index_and_mask); + ASSERT_FALSE(encoder.TryMerge(14u, 3 * raw_pointer_size)); // Index out of range. + } +} + +} // namespace linker +} // namespace art diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 663a8896ff..99c62584ff 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -45,8 +45,8 @@ #include "image_writer.h" #include "linker/buffered_output_stream.h" #include "linker/file_output_stream.h" +#include "linker/index_bss_mapping_encoder.h" #include "linker/linker_patch.h" -#include "linker/method_bss_mapping_encoder.h" #include "linker/multi_oat_relative_patcher.h" #include "linker/output_stream.h" #include "mirror/array.h" @@ -310,6 +310,8 @@ class OatWriter::OatDexFile { uint32_t class_offsets_offset_; uint32_t lookup_table_offset_; uint32_t method_bss_mapping_offset_; + uint32_t type_bss_mapping_offset_; + uint32_t string_bss_mapping_offset_; uint32_t dex_sections_layout_offset_; // Data to write to a separate section. @@ -396,6 +398,8 @@ OatWriter::OatWriter(bool compiling_boot_image, size_oat_dex_file_dex_layout_sections_(0), size_oat_dex_file_dex_layout_sections_alignment_(0), size_oat_dex_file_method_bss_mapping_offset_(0), + size_oat_dex_file_type_bss_mapping_offset_(0), + size_oat_dex_file_string_bss_mapping_offset_(0), size_oat_lookup_table_alignment_(0), size_oat_lookup_table_(0), size_oat_class_offsets_alignment_(0), @@ -405,6 +409,8 @@ OatWriter::OatWriter(bool compiling_boot_image, size_oat_class_method_bitmaps_(0), size_oat_class_method_offsets_(0), size_method_bss_mappings_(0u), + size_type_bss_mappings_(0u), + size_string_bss_mappings_(0u), relative_patcher_(nullptr), absolute_patch_locations_(), profile_compilation_info_(info), @@ -632,8 +638,8 @@ void OatWriter::PrepareLayout(MultiOatRelativePatcher* relative_patcher) { offset = InitOatClasses(offset); } { - TimingLogger::ScopedTiming split("InitMethodBssMappings", timings_); - offset = InitMethodBssMappings(offset); + TimingLogger::ScopedTiming split("InitIndexBssMappings", timings_); + offset = InitIndexBssMappings(offset); } { TimingLogger::ScopedTiming split("InitOatMaps", timings_); @@ -761,23 +767,22 @@ class OatWriter::InitBssLayoutMethodVisitor : public DexMethodVisitor { for (const LinkerPatch& patch : compiled_method->GetPatches()) { if (patch.GetType() == LinkerPatch::Type::kMethodBssEntry) { MethodReference target_method = patch.TargetMethod(); - auto refs_it = writer_->bss_method_entry_references_.find(target_method.dex_file); - if (refs_it == writer_->bss_method_entry_references_.end()) { - refs_it = writer_->bss_method_entry_references_.Put( - target_method.dex_file, - BitVector(target_method.dex_file->NumMethodIds(), - /* expandable */ false, - Allocator::GetMallocAllocator())); - refs_it->second.ClearAllBits(); - } - refs_it->second.SetBit(target_method.index); + AddBssReference(target_method, + target_method.dex_file->NumMethodIds(), + &writer_->bss_method_entry_references_); writer_->bss_method_entries_.Overwrite(target_method, /* placeholder */ 0u); } else if (patch.GetType() == LinkerPatch::Type::kTypeBssEntry) { - TypeReference ref(patch.TargetTypeDexFile(), patch.TargetTypeIndex()); - writer_->bss_type_entries_.Overwrite(ref, /* placeholder */ 0u); + TypeReference target_type(patch.TargetTypeDexFile(), patch.TargetTypeIndex()); + AddBssReference(target_type, + target_type.dex_file->NumTypeIds(), + &writer_->bss_type_entry_references_); + writer_->bss_type_entries_.Overwrite(target_type, /* placeholder */ 0u); } else if (patch.GetType() == LinkerPatch::Type::kStringBssEntry) { - StringReference ref(patch.TargetStringDexFile(), patch.TargetStringIndex()); - writer_->bss_string_entries_.Overwrite(ref, /* placeholder */ 0u); + StringReference target_string(patch.TargetStringDexFile(), patch.TargetStringIndex()); + AddBssReference(target_string, + 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; @@ -788,6 +793,24 @@ class OatWriter::InitBssLayoutMethodVisitor : public DexMethodVisitor { } return true; } + + private: + void AddBssReference(const DexFileReference& ref, + size_t number_of_indexes, + /*inout*/ SafeMap* references) { + // We currently support inlining of throwing instructions only when they originate in the + // same dex file as the outer method. All .bss references are used by throwing instructions. + DCHECK_EQ(dex_file_, ref.dex_file); + + auto refs_it = references->find(ref.dex_file); + if (refs_it == references->end()) { + refs_it = references->Put( + ref.dex_file, + BitVector(number_of_indexes, /* expandable */ false, Allocator::GetMallocAllocator())); + refs_it->second.ClearAllBits(); + } + refs_it->second.SetBit(ref.index); + } }; class OatWriter::InitOatClassesMethodVisitor : public DexMethodVisitor { @@ -2192,38 +2215,101 @@ size_t OatWriter::InitOatMaps(size_t offset) { return offset; } -size_t OatWriter::InitMethodBssMappings(size_t offset) { - size_t number_of_dex_files = 0u; +template +static size_t CalculateNumberOfIndexBssMappingEntries(size_t number_of_indexes, + size_t slot_size, + const BitVector& indexes, + GetBssOffset get_bss_offset) { + IndexBssMappingEncoder encoder(number_of_indexes, slot_size); + size_t number_of_entries = 0u; + bool first_index = true; + for (uint32_t index : indexes.Indexes()) { + uint32_t bss_offset = get_bss_offset(index); + if (first_index || !encoder.TryMerge(index, bss_offset)) { + encoder.Reset(index, bss_offset); + ++number_of_entries; + first_index = false; + } + } + DCHECK_NE(number_of_entries, 0u); + return number_of_entries; +} + +template +static size_t CalculateIndexBssMappingSize(size_t number_of_indexes, + size_t slot_size, + const BitVector& indexes, + GetBssOffset get_bss_offset) { + size_t number_of_entries = CalculateNumberOfIndexBssMappingEntries(number_of_indexes, + slot_size, + indexes, + get_bss_offset); + return IndexBssMapping::ComputeSize(number_of_entries); +} + +size_t OatWriter::InitIndexBssMappings(size_t offset) { + if (bss_method_entry_references_.empty() && + bss_type_entry_references_.empty() && + bss_string_entry_references_.empty()) { + return offset; + } + // If there are any classes, the class offsets allocation aligns the offset + // and we cannot have any index bss mappings without class offsets. + static_assert(alignof(IndexBssMapping) == 4u, "IndexBssMapping alignment check."); + DCHECK_ALIGNED(offset, 4u); + + size_t number_of_method_dex_files = 0u; + size_t number_of_type_dex_files = 0u; + size_t number_of_string_dex_files = 0u; + PointerSize pointer_size = GetInstructionSetPointerSize(oat_header_->GetInstructionSet()); for (size_t i = 0, size = dex_files_->size(); i != size; ++i) { const DexFile* dex_file = (*dex_files_)[i]; - auto it = bss_method_entry_references_.find(dex_file); - if (it != bss_method_entry_references_.end()) { - const BitVector& method_indexes = it->second; - ++number_of_dex_files; - // If there are any classes, the class offsets allocation aligns the offset - // and we cannot have method bss mappings without class offsets. - static_assert(alignof(MethodBssMapping) == 4u, "MethodBssMapping alignment check."); - DCHECK_ALIGNED(offset, 4u); + auto method_it = bss_method_entry_references_.find(dex_file); + if (method_it != bss_method_entry_references_.end()) { + const BitVector& method_indexes = method_it->second; + ++number_of_method_dex_files; oat_dex_files_[i].method_bss_mapping_offset_ = offset; + offset += CalculateIndexBssMappingSize( + dex_file->NumMethodIds(), + static_cast(pointer_size), + method_indexes, + [=](uint32_t index) { + return bss_method_entries_.Get({dex_file, index}); + }); + } - MethodBssMappingEncoder encoder( - GetInstructionSetPointerSize(oat_header_->GetInstructionSet())); - size_t number_of_entries = 0u; - bool first_index = true; - for (uint32_t method_index : method_indexes.Indexes()) { - uint32_t bss_offset = bss_method_entries_.Get(MethodReference(dex_file, method_index)); - if (first_index || !encoder.TryMerge(method_index, bss_offset)) { - encoder.Reset(method_index, bss_offset); - ++number_of_entries; - first_index = false; - } - } - DCHECK_NE(number_of_entries, 0u); - offset += MethodBssMapping::ComputeSize(number_of_entries); + auto type_it = bss_type_entry_references_.find(dex_file); + if (type_it != bss_type_entry_references_.end()) { + const BitVector& type_indexes = type_it->second; + ++number_of_type_dex_files; + oat_dex_files_[i].type_bss_mapping_offset_ = offset; + offset += CalculateIndexBssMappingSize( + dex_file->NumTypeIds(), + sizeof(GcRoot), + type_indexes, + [=](uint32_t index) { + return bss_type_entries_.Get({dex_file, dex::TypeIndex(index)}); + }); + } + + auto string_it = bss_string_entry_references_.find(dex_file); + if (string_it != bss_string_entry_references_.end()) { + const BitVector& string_indexes = string_it->second; + ++number_of_string_dex_files; + oat_dex_files_[i].string_bss_mapping_offset_ = offset; + offset += CalculateIndexBssMappingSize( + dex_file->NumStringIds(), + sizeof(GcRoot), + string_indexes, + [=](uint32_t index) { + return bss_string_entries_.Get({dex_file, dex::StringIndex(index)}); + }); } } - // Check that all dex files targeted by method bss entries are in `*dex_files_`. - CHECK_EQ(number_of_dex_files, bss_method_entry_references_.size()); + // Check that all dex files targeted by bss entries are in `*dex_files_`. + CHECK_EQ(number_of_method_dex_files, bss_method_entry_references_.size()); + CHECK_EQ(number_of_type_dex_files, bss_type_entry_references_.size()); + CHECK_EQ(number_of_string_dex_files, bss_string_entry_references_.size()); return offset; } @@ -2423,7 +2509,7 @@ bool OatWriter::WriteRodata(OutputStream* out) { return false; } - relative_offset = WriteMethodBssMappings(out, file_offset, relative_offset); + relative_offset = WriteIndexBssMappings(out, file_offset, relative_offset); if (relative_offset == 0) { PLOG(ERROR) << "Failed to write method bss mappings to " << out->GetLocation(); return false; @@ -2744,6 +2830,8 @@ bool OatWriter::WriteCode(OutputStream* out) { DO_STAT(size_oat_dex_file_dex_layout_sections_); DO_STAT(size_oat_dex_file_dex_layout_sections_alignment_); DO_STAT(size_oat_dex_file_method_bss_mapping_offset_); + DO_STAT(size_oat_dex_file_type_bss_mapping_offset_); + DO_STAT(size_oat_dex_file_string_bss_mapping_offset_); DO_STAT(size_oat_lookup_table_alignment_); DO_STAT(size_oat_lookup_table_); DO_STAT(size_oat_class_offsets_alignment_); @@ -2753,6 +2841,8 @@ bool OatWriter::WriteCode(OutputStream* out) { DO_STAT(size_oat_class_method_bitmaps_); DO_STAT(size_oat_class_method_offsets_); DO_STAT(size_method_bss_mappings_); + DO_STAT(size_type_bss_mappings_); + DO_STAT(size_string_bss_mappings_); #undef DO_STAT VLOG(compiler) << "size_total=" << PrettySize(size_total) << " (" << size_total << "B)"; @@ -2892,64 +2982,131 @@ size_t OatWriter::WriteMaps(OutputStream* out, size_t file_offset, size_t relati return relative_offset; } -size_t OatWriter::WriteMethodBssMappings(OutputStream* out, - size_t file_offset, - size_t relative_offset) { + +template +size_t WriteIndexBssMapping(OutputStream* out, + size_t number_of_indexes, + size_t slot_size, + const BitVector& indexes, + GetBssOffset get_bss_offset) { + // Allocate the IndexBssMapping. + size_t number_of_entries = CalculateNumberOfIndexBssMappingEntries( + number_of_indexes, slot_size, indexes, get_bss_offset); + size_t mappings_size = IndexBssMapping::ComputeSize(number_of_entries); + DCHECK_ALIGNED(mappings_size, sizeof(uint32_t)); + std::unique_ptr storage(new uint32_t[mappings_size / sizeof(uint32_t)]); + IndexBssMapping* mappings = new(storage.get()) IndexBssMapping(number_of_entries); + mappings->ClearPadding(); + // Encode the IndexBssMapping. + IndexBssMappingEncoder encoder(number_of_indexes, slot_size); + auto init_it = mappings->begin(); + bool first_index = true; + for (uint32_t index : indexes.Indexes()) { + size_t bss_offset = get_bss_offset(index); + if (first_index) { + first_index = false; + encoder.Reset(index, bss_offset); + } else if (!encoder.TryMerge(index, bss_offset)) { + *init_it = encoder.GetEntry(); + ++init_it; + encoder.Reset(index, bss_offset); + } + } + // Store the last entry. + *init_it = encoder.GetEntry(); + ++init_it; + DCHECK(init_it == mappings->end()); + + if (!out->WriteFully(storage.get(), mappings_size)) { + return 0u; + } + return mappings_size; +} + +size_t OatWriter::WriteIndexBssMappings(OutputStream* out, + size_t file_offset, + size_t relative_offset) { TimingLogger::ScopedTiming split("WriteMethodBssMappings", timings_); + if (bss_method_entry_references_.empty() && + bss_type_entry_references_.empty() && + bss_string_entry_references_.empty()) { + return relative_offset; + } + // If there are any classes, the class offsets allocation aligns the offset + // and we cannot have method bss mappings without class offsets. + static_assert(alignof(IndexBssMapping) == sizeof(uint32_t), + "IndexBssMapping alignment check."); + DCHECK_ALIGNED(relative_offset, sizeof(uint32_t)); + PointerSize pointer_size = GetInstructionSetPointerSize(oat_header_->GetInstructionSet()); for (size_t i = 0, size = dex_files_->size(); i != size; ++i) { const DexFile* dex_file = (*dex_files_)[i]; OatDexFile* oat_dex_file = &oat_dex_files_[i]; - auto it = bss_method_entry_references_.find(dex_file); - if (it != bss_method_entry_references_.end()) { - const BitVector& method_indexes = it->second; - // If there are any classes, the class offsets allocation aligns the offset - // and we cannot have method bss mappings without class offsets. - static_assert(alignof(MethodBssMapping) == sizeof(uint32_t), - "MethodBssMapping alignment check."); - DCHECK_ALIGNED(relative_offset, sizeof(uint32_t)); - - MethodBssMappingEncoder encoder( - GetInstructionSetPointerSize(oat_header_->GetInstructionSet())); - // Allocate a sufficiently large MethodBssMapping. - size_t number_of_method_indexes = method_indexes.NumSetBits(); - DCHECK_NE(number_of_method_indexes, 0u); - size_t max_mappings_size = MethodBssMapping::ComputeSize(number_of_method_indexes); - DCHECK_ALIGNED(max_mappings_size, sizeof(uint32_t)); - std::unique_ptr storage(new uint32_t[max_mappings_size / sizeof(uint32_t)]); - MethodBssMapping* mappings = new(storage.get()) MethodBssMapping(number_of_method_indexes); - mappings->ClearPadding(); - // Encode the MethodBssMapping. - auto init_it = mappings->begin(); - bool first_index = true; - for (uint32_t method_index : method_indexes.Indexes()) { - size_t bss_offset = bss_method_entries_.Get(MethodReference(dex_file, method_index)); - if (first_index) { - first_index = false; - encoder.Reset(method_index, bss_offset); - } else if (!encoder.TryMerge(method_index, bss_offset)) { - *init_it = encoder.GetEntry(); - ++init_it; - encoder.Reset(method_index, bss_offset); - } - } - // Store the last entry and shrink the mapping to the actual size. - *init_it = encoder.GetEntry(); - ++init_it; - DCHECK(init_it <= mappings->end()); - mappings->SetSize(std::distance(mappings->begin(), init_it)); - size_t mappings_size = MethodBssMapping::ComputeSize(mappings->size()); - + auto method_it = bss_method_entry_references_.find(dex_file); + if (method_it != bss_method_entry_references_.end()) { + const BitVector& method_indexes = method_it->second; DCHECK_EQ(relative_offset, oat_dex_file->method_bss_mapping_offset_); DCHECK_OFFSET(); - if (!out->WriteFully(storage.get(), mappings_size)) { + size_t method_mappings_size = WriteIndexBssMapping( + out, + dex_file->NumMethodIds(), + static_cast(pointer_size), + method_indexes, + [=](uint32_t index) { + return bss_method_entries_.Get({dex_file, index}); + }); + if (method_mappings_size == 0u) { return 0u; } - size_method_bss_mappings_ += mappings_size; - relative_offset += mappings_size; + size_method_bss_mappings_ += method_mappings_size; + relative_offset += method_mappings_size; } else { DCHECK_EQ(0u, oat_dex_file->method_bss_mapping_offset_); } + + auto type_it = bss_type_entry_references_.find(dex_file); + if (type_it != bss_type_entry_references_.end()) { + const BitVector& type_indexes = type_it->second; + DCHECK_EQ(relative_offset, oat_dex_file->type_bss_mapping_offset_); + DCHECK_OFFSET(); + size_t type_mappings_size = WriteIndexBssMapping( + out, + dex_file->NumTypeIds(), + sizeof(GcRoot), + type_indexes, + [=](uint32_t index) { + return bss_type_entries_.Get({dex_file, dex::TypeIndex(index)}); + }); + if (type_mappings_size == 0u) { + return 0u; + } + size_type_bss_mappings_ += type_mappings_size; + relative_offset += type_mappings_size; + } else { + DCHECK_EQ(0u, oat_dex_file->type_bss_mapping_offset_); + } + + auto string_it = bss_string_entry_references_.find(dex_file); + if (string_it != bss_string_entry_references_.end()) { + const BitVector& string_indexes = string_it->second; + DCHECK_EQ(relative_offset, oat_dex_file->string_bss_mapping_offset_); + DCHECK_OFFSET(); + size_t string_mappings_size = WriteIndexBssMapping( + out, + dex_file->NumStringIds(), + sizeof(GcRoot), + string_indexes, + [=](uint32_t index) { + return bss_string_entries_.Get({dex_file, dex::StringIndex(index)}); + }); + if (string_mappings_size == 0u) { + return 0u; + } + size_string_bss_mappings_ += string_mappings_size; + relative_offset += string_mappings_size; + } else { + DCHECK_EQ(0u, oat_dex_file->string_bss_mapping_offset_); + } } return relative_offset; } @@ -3748,6 +3905,8 @@ OatWriter::OatDexFile::OatDexFile(const char* dex_file_location, class_offsets_offset_(0u), lookup_table_offset_(0u), method_bss_mapping_offset_(0u), + type_bss_mapping_offset_(0u), + string_bss_mapping_offset_(0u), dex_sections_layout_offset_(0u), class_offsets_() { } @@ -3760,6 +3919,8 @@ size_t OatWriter::OatDexFile::SizeOf() const { + sizeof(class_offsets_offset_) + sizeof(lookup_table_offset_) + sizeof(method_bss_mapping_offset_) + + sizeof(type_bss_mapping_offset_) + + sizeof(string_bss_mapping_offset_) + sizeof(dex_sections_layout_offset_); } @@ -3815,6 +3976,18 @@ bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, OutputStream* out) cons } oat_writer->size_oat_dex_file_method_bss_mapping_offset_ += sizeof(method_bss_mapping_offset_); + if (!out->WriteFully(&type_bss_mapping_offset_, sizeof(type_bss_mapping_offset_))) { + PLOG(ERROR) << "Failed to write type bss mapping offset to " << out->GetLocation(); + return false; + } + oat_writer->size_oat_dex_file_type_bss_mapping_offset_ += sizeof(type_bss_mapping_offset_); + + if (!out->WriteFully(&string_bss_mapping_offset_, sizeof(string_bss_mapping_offset_))) { + PLOG(ERROR) << "Failed to write string bss mapping offset to " << out->GetLocation(); + return false; + } + oat_writer->size_oat_dex_file_string_bss_mapping_offset_ += sizeof(string_bss_mapping_offset_); + return true; } diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index 6a82fd1d59..e0cb7ecc9f 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -310,7 +310,7 @@ class OatWriter { size_t InitClassOffsets(size_t offset); size_t InitOatClasses(size_t offset); size_t InitOatMaps(size_t offset); - size_t InitMethodBssMappings(size_t offset); + size_t InitIndexBssMappings(size_t offset); size_t InitOatDexFiles(size_t offset); size_t InitOatCode(size_t offset); size_t InitOatCodeDexFiles(size_t offset); @@ -319,7 +319,7 @@ class OatWriter { size_t WriteClassOffsets(OutputStream* out, size_t file_offset, size_t relative_offset); size_t WriteClasses(OutputStream* out, size_t file_offset, size_t relative_offset); size_t WriteMaps(OutputStream* out, size_t file_offset, size_t relative_offset); - size_t WriteMethodBssMappings(OutputStream* out, size_t file_offset, size_t relative_offset); + size_t WriteIndexBssMappings(OutputStream* out, size_t file_offset, size_t relative_offset); 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); @@ -403,6 +403,12 @@ class OatWriter { // Map for recording references to ArtMethod entries in .bss. SafeMap bss_method_entry_references_; + // Map for recording references to GcRoot entries in .bss. + SafeMap bss_type_entry_references_; + + // Map for recording references to GcRoot entries in .bss. + SafeMap bss_string_entry_references_; + // Map for allocating ArtMethod entries in .bss. Indexed by MethodReference for the target // method in the dex file with the "method reference value comparator" for deduplication. // The value is the target offset for patching, starting at `bss_start_ + bss_methods_offset_`. @@ -476,6 +482,8 @@ class OatWriter { uint32_t size_oat_dex_file_dex_layout_sections_; uint32_t size_oat_dex_file_dex_layout_sections_alignment_; uint32_t size_oat_dex_file_method_bss_mapping_offset_; + uint32_t size_oat_dex_file_type_bss_mapping_offset_; + uint32_t size_oat_dex_file_string_bss_mapping_offset_; uint32_t size_oat_lookup_table_alignment_; uint32_t size_oat_lookup_table_; uint32_t size_oat_class_offsets_alignment_; @@ -485,6 +493,8 @@ class OatWriter { uint32_t size_oat_class_method_bitmaps_; uint32_t size_oat_class_method_offsets_; uint32_t size_method_bss_mappings_; + uint32_t size_type_bss_mappings_; + uint32_t size_string_bss_mappings_; // The helper for processing relative patches is external so that we can patch across oat files. MultiOatRelativePatcher* relative_patcher_; diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 5a3d34c7a5..2c15087612 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -32,6 +32,7 @@ #include "arch/instruction_set_features.h" #include "art_field-inl.h" #include "art_method-inl.h" +#include "base/bit_utils_iterator.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "class_linker-inl.h" @@ -51,6 +52,7 @@ #include "imtable-inl.h" #include "indenter.h" #include "subtype_check.h" +#include "index_bss_mapping.h" #include "interpreter/unstarted_runtime.h" #include "linker/buffered_output_stream.h" #include "linker/elf_builder.h" @@ -535,6 +537,29 @@ class OatDumper { } cumulative.Add(data); + + // Dump .bss entries. + DumpBssEntries( + os, + "ArtMethod", + oat_dex_file->GetMethodBssMapping(), + dex_file->NumMethodIds(), + static_cast(GetInstructionSetPointerSize(instruction_set_)), + [=](uint32_t index) { return dex_file->PrettyMethod(index); }); + DumpBssEntries( + os, + "Class", + oat_dex_file->GetTypeBssMapping(), + dex_file->NumTypeIds(), + sizeof(GcRoot), + [=](uint32_t index) { return dex_file->PrettyType(dex::TypeIndex(index)); }); + DumpBssEntries( + os, + "String", + oat_dex_file->GetStringBssMapping(), + dex_file->NumStringIds(), + sizeof(GcRoot), + [=](uint32_t index) { return dex_file->StringDataByIdx(dex::StringIndex(index)); }); } os << "Cumulative dex file data\n"; cumulative.Dump(os); @@ -1872,6 +1897,40 @@ class OatDumper { } } + template + void DumpBssEntries(std::ostream& os, + const char* slot_type, + const IndexBssMapping* mapping, + uint32_t number_of_indexes, + size_t slot_size, + NameGetter name) { + os << ".bss mapping for " << slot_type << ": "; + if (mapping == nullptr) { + os << "empty.\n"; + return; + } + size_t index_bits = IndexBssMappingEntry::IndexBits(number_of_indexes); + size_t num_valid_indexes = 0u; + for (const IndexBssMappingEntry& entry : *mapping) { + num_valid_indexes += 1u + POPCOUNT(entry.GetMask(index_bits)); + } + os << mapping->size() << " entries for " << num_valid_indexes << " valid indexes.\n"; + os << std::hex; + for (const IndexBssMappingEntry& entry : *mapping) { + uint32_t index = entry.GetIndex(index_bits); + uint32_t mask = entry.GetMask(index_bits); + size_t bss_offset = entry.bss_offset - POPCOUNT(mask) * slot_size; + for (uint32_t n : LowToHighBits(mask)) { + size_t current_index = index - (32u - index_bits) + n; + os << " 0x" << bss_offset << ": " << slot_type << ": " << name(current_index) << "\n"; + bss_offset += slot_size; + } + DCHECK_EQ(bss_offset, entry.bss_offset); + os << " 0x" << bss_offset << ": " << slot_type << ": " << name(index) << "\n"; + } + os << std::dec; + } + const OatFile& oat_file_; const std::vector oat_dex_files_; const OatDumperOptions& options_; diff --git a/profman/profman.cc b/profman/profman.cc index 31d28e4be0..a5a5546323 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -786,7 +786,7 @@ class ProfMan FINAL { method_str = line.substr(method_sep_index + kMethodSep.size()); } - TypeReference class_ref; + TypeReference class_ref(/* dex_file */ nullptr, dex::TypeIndex()); if (!FindClass(dex_files, klass, &class_ref)) { LOG(WARNING) << "Could not find class: " << klass; return false; @@ -860,7 +860,8 @@ class ProfMan FINAL { if (!HasSingleInvoke(class_ref, method_index, &dex_pc)) { return false; } - std::vector classes(inline_cache_elems.size()); + std::vector classes(inline_cache_elems.size(), + TypeReference(/* dex_file */ nullptr, dex::TypeIndex())); size_t class_it = 0; for (const std::string& ic_class : inline_cache_elems) { if (!FindClass(dex_files, ic_class, &(classes[class_it++]))) { diff --git a/runtime/Android.bp b/runtime/Android.bp index 69e4434fbe..a136ccb9d0 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -103,6 +103,7 @@ cc_defaults { "gc/verification.cc", "hprof/hprof.cc", "image.cc", + "index_bss_mapping.cc", "indirect_reference_table.cc", "instrumentation.cc", "intern_table.cc", diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index 5355267b07..4ac99673ee 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -32,29 +32,74 @@ namespace art { -static inline void BssWriteBarrier(ArtMethod* outer_method) REQUIRES_SHARED(Locks::mutator_lock_) { - // For AOT code, we need a write barrier for the class loader that holds the - // GC roots in the .bss. - const DexFile* dex_file = outer_method->GetDexFile(); - if (dex_file != nullptr && - dex_file->GetOatDexFile() != nullptr && - !dex_file->GetOatDexFile()->GetOatFile()->GetBssGcRoots().empty()) { +static void StoreObjectInBss(ArtMethod* outer_method, + const OatFile* oat_file, + size_t bss_offset, + ObjPtr object) REQUIRES_SHARED(Locks::mutator_lock_) { + // Used for storing Class or String in .bss GC roots. + static_assert(sizeof(GcRoot) == sizeof(GcRoot), "Size check."); + static_assert(sizeof(GcRoot) == sizeof(GcRoot), "Size check."); + DCHECK_NE(bss_offset, IndexBssMappingLookup::npos); + DCHECK_ALIGNED(bss_offset, sizeof(GcRoot)); + GcRoot* slot = reinterpret_cast*>( + const_cast(oat_file->BssBegin() + bss_offset)); + DCHECK_GE(slot, oat_file->GetBssGcRoots().data()); + DCHECK_LT(slot, oat_file->GetBssGcRoots().data() + oat_file->GetBssGcRoots().size()); + if (slot->IsNull()) { + // This may race with another thread trying to store the very same value but that's OK. + *slot = GcRoot(object); + // We need a write barrier for the class loader that holds the GC roots in the .bss. ObjPtr class_loader = outer_method->GetClassLoader(); + Runtime* runtime = Runtime::Current(); if (kIsDebugBuild) { - ClassTable* class_table = - Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(class_loader); - CHECK(class_table != nullptr && - !class_table->InsertOatFile(dex_file->GetOatDexFile()->GetOatFile())) + ClassTable* class_table = runtime->GetClassLinker()->ClassTableForClassLoader(class_loader); + CHECK(class_table != nullptr && !class_table->InsertOatFile(oat_file)) << "Oat file with .bss GC roots was not registered in class table: " - << dex_file->GetOatDexFile()->GetOatFile()->GetLocation(); + << oat_file->GetLocation(); } if (class_loader != nullptr) { - // Note that we emit the barrier before the compiled code stores the String or Class - // as a GC root. This is OK as there is no suspend point point in between. - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader); + runtime->GetHeap()->WriteBarrierEveryFieldOf(class_loader); } else { - Runtime::Current()->GetClassLinker()->WriteBarrierForBootOatFileBssRoots( - dex_file->GetOatDexFile()->GetOatFile()); + runtime->GetClassLinker()->WriteBarrierForBootOatFileBssRoots(oat_file); + } + } else { + // Each slot serves to store exactly one Class or String. + DCHECK_EQ(object, slot->Read()); + } +} + +static inline void StoreTypeInBss(ArtMethod* outer_method, + dex::TypeIndex type_idx, + ObjPtr resolved_type) + REQUIRES_SHARED(Locks::mutator_lock_) { + const DexFile* dex_file = outer_method->GetDexFile(); + DCHECK(dex_file != nullptr); + const OatDexFile* oat_dex_file = dex_file->GetOatDexFile(); + if (oat_dex_file != nullptr) { + size_t bss_offset = IndexBssMappingLookup::GetBssOffset(oat_dex_file->GetTypeBssMapping(), + type_idx.index_, + dex_file->NumTypeIds(), + sizeof(GcRoot)); + if (bss_offset != IndexBssMappingLookup::npos) { + StoreObjectInBss(outer_method, oat_dex_file->GetOatFile(), bss_offset, resolved_type); + } + } +} + +static inline void StoreStringInBss(ArtMethod* outer_method, + dex::StringIndex string_idx, + ObjPtr resolved_string) + REQUIRES_SHARED(Locks::mutator_lock_) __attribute__((optnone)) { + const DexFile* dex_file = outer_method->GetDexFile(); + DCHECK(dex_file != nullptr); + const OatDexFile* oat_dex_file = dex_file->GetOatDexFile(); + if (oat_dex_file != nullptr) { + size_t bss_offset = IndexBssMappingLookup::GetBssOffset(oat_dex_file->GetStringBssMapping(), + string_idx.index_, + dex_file->NumStringIds(), + sizeof(GcRoot)); + if (bss_offset != IndexBssMappingLookup::npos) { + StoreObjectInBss(outer_method, oat_dex_file->GetOatFile(), bss_offset, resolved_string); } } } @@ -71,14 +116,14 @@ extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx, mirror::Class* result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, true, false); if (LIKELY(result != nullptr)) { - BssWriteBarrier(caller_and_outer.outer_method); + StoreTypeInBss(caller_and_outer.outer_method, dex::TypeIndex(type_idx), result); } return result; } extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - // Called when method->dex_cache_resolved_types_[] misses. + // Called when the .bss slot was empty or for main-path runtime call. ScopedQuickEntrypointChecks sqec(self); auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod( self, CalleeSaveType::kSaveEverythingForClinit); @@ -86,24 +131,21 @@ extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, Thread* s mirror::Class* result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, false); if (LIKELY(result != nullptr)) { - BssWriteBarrier(caller_and_outer.outer_method); + StoreTypeInBss(caller_and_outer.outer_method, dex::TypeIndex(type_idx), result); } return result; } extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - // Called when caller isn't guaranteed to have access to a type and the dex cache may be - // unpopulated. + // Called when caller isn't guaranteed to have access to a type. ScopedQuickEntrypointChecks sqec(self); auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, CalleeSaveType::kSaveEverything); ArtMethod* caller = caller_and_outer.caller; mirror::Class* result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, true); - if (LIKELY(result != nullptr)) { - BssWriteBarrier(caller_and_outer.outer_method); - } + // Do not StoreTypeInBss(); access check entrypoint is never used together with .bss. return result; } @@ -115,7 +157,7 @@ extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* ArtMethod* caller = caller_and_outer.caller; mirror::String* result = ResolveStringFromCode(caller, dex::StringIndex(string_idx)); if (LIKELY(result != nullptr)) { - BssWriteBarrier(caller_and_outer.outer_method); + StoreStringInBss(caller_and_outer.outer_method, dex::StringIndex(string_idx), result); } return result; } diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 22c9a1d31a..2496aa0f58 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -28,10 +28,10 @@ #include "gc/accounting/card_table-inl.h" #include "imt_conflict_table.h" #include "imtable-inl.h" +#include "index_bss_mapping.h" #include "instrumentation.h" #include "interpreter/interpreter.h" #include "linear_alloc.h" -#include "method_bss_mapping.h" #include "method_handles.h" #include "method_reference.h" #include "mirror/class-inl.h" @@ -1214,27 +1214,20 @@ extern "C" const void* artQuickResolutionTrampoline( // Update .bss entry in oat file if any. if (called != nullptr && called_method.dex_file->GetOatDexFile() != nullptr) { - const MethodBssMapping* mapping = - called_method.dex_file->GetOatDexFile()->GetMethodBssMapping(); - if (mapping != nullptr) { - auto pp = std::partition_point( - mapping->begin(), - mapping->end(), - [called_method](const MethodBssMappingEntry& entry) { - return entry.method_index < called_method.index; - }); - if (pp != mapping->end() && pp->CoversIndex(called_method.index)) { - size_t bss_offset = pp->GetBssOffset(called_method.index, - static_cast(kRuntimePointerSize)); - DCHECK_ALIGNED(bss_offset, static_cast(kRuntimePointerSize)); - const OatFile* oat_file = called_method.dex_file->GetOatDexFile()->GetOatFile(); - ArtMethod** method_entry = reinterpret_cast(const_cast( - oat_file->BssBegin() + bss_offset)); - DCHECK_GE(method_entry, oat_file->GetBssMethods().data()); - DCHECK_LT(method_entry, - oat_file->GetBssMethods().data() + oat_file->GetBssMethods().size()); - *method_entry = called; - } + size_t bss_offset = IndexBssMappingLookup::GetBssOffset( + called_method.dex_file->GetOatDexFile()->GetMethodBssMapping(), + called_method.index, + called_method.dex_file->NumMethodIds(), + static_cast(kRuntimePointerSize)); + if (bss_offset != IndexBssMappingLookup::npos) { + DCHECK_ALIGNED(bss_offset, static_cast(kRuntimePointerSize)); + const OatFile* oat_file = called_method.dex_file->GetOatDexFile()->GetOatFile(); + ArtMethod** method_entry = reinterpret_cast(const_cast( + oat_file->BssBegin() + bss_offset)); + DCHECK_GE(method_entry, oat_file->GetBssMethods().data()); + DCHECK_LT(method_entry, + oat_file->GetBssMethods().data() + oat_file->GetBssMethods().size()); + *method_entry = called; } } } diff --git a/runtime/index_bss_mapping.cc b/runtime/index_bss_mapping.cc new file mode 100644 index 0000000000..8d9d8cfe63 --- /dev/null +++ b/runtime/index_bss_mapping.cc @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#include + +#include "index_bss_mapping.h" + +#include "base/bit_utils.h" +#include "base/length_prefixed_array.h" + +namespace art { + +size_t IndexBssMappingEntry::GetBssOffset(size_t index_bits, + uint32_t index, + size_t slot_size) const { + uint32_t diff = GetIndex(index_bits) - index; + if (diff == 0u) { + return bss_offset; + } + size_t mask_bits = 32u - index_bits; + if (diff > mask_bits) { + return IndexBssMappingLookup::npos; + } + // Shift out the index bits and bits for lower indexes. + // Note that `index_bits + (mask_bits - diff) == 32 - diff`. + uint32_t mask_from_index = index_and_mask >> (32u - diff); + if ((mask_from_index & 1u) != 0u) { + return bss_offset - POPCOUNT(mask_from_index) * slot_size; + } else { + return IndexBssMappingLookup::npos; + } +} + +constexpr size_t IndexBssMappingLookup::npos; + +size_t IndexBssMappingLookup::GetBssOffset(const IndexBssMapping* mapping, + uint32_t index, + uint32_t number_of_indexes, + size_t slot_size) { + DCHECK_LT(index, number_of_indexes); + if (mapping == nullptr) { + return npos; + } + size_t index_bits = IndexBssMappingEntry::IndexBits(number_of_indexes); + uint32_t index_mask = IndexBssMappingEntry::IndexMask(index_bits); + auto it = std::partition_point( + mapping->begin(), + mapping->end(), + [=](const struct IndexBssMappingEntry& entry) { + return (entry.index_and_mask & index_mask) < index; + }); + if (it == mapping->end()) { + return npos; + } + const IndexBssMappingEntry& entry = *it; + return entry.GetBssOffset(index_bits, index, slot_size); +} + +} // namespace art diff --git a/runtime/index_bss_mapping.h b/runtime/index_bss_mapping.h new file mode 100644 index 0000000000..d9f4e663a7 --- /dev/null +++ b/runtime/index_bss_mapping.h @@ -0,0 +1,81 @@ +/* + * 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_INDEX_BSS_MAPPING_H_ +#define ART_RUNTIME_INDEX_BSS_MAPPING_H_ + +#include "base/bit_utils.h" +#include "base/logging.h" + +namespace art { + +template class LengthPrefixedArray; + +// IndexBssMappingEntry describes a mapping of one or more indexes to their offsets in the .bss. +// A sorted array of IndexBssMappingEntry is used to describe the mapping of method indexes, +// type indexes or string indexes to offsets of their assigned slots in the .bss. +// +// The highest index and a mask are stored in a single `uint32_t index_and_mask` and the split +// between the index and the mask is provided externally. The "mask" bits specify whether some +// of the previous indexes are mapped to immediately preceding slots. This is permissible only +// if the slots are consecutive and in the same order as indexes. +// +// The .bss offset of the slot associated with the highest index is stored in plain form as +// `bss_offset`. If the mask specifies any smaller indexes being mapped to immediately +// preceding slots, their offsets are calculated using an externally supplied size of the slot. +struct IndexBssMappingEntry { + static size_t IndexBits(uint32_t number_of_indexes) { + DCHECK_NE(number_of_indexes, 0u); + return MinimumBitsToStore(number_of_indexes - 1u); + } + + static uint32_t IndexMask(size_t index_bits) { + DCHECK_LE(index_bits, 32u); + constexpr uint32_t kAllOnes = static_cast(-1); + // Handle `index_bits == 32u` explicitly; shifting uint32_t left by 32 is undefined behavior. + return (index_bits == 32u) ? kAllOnes : ~(kAllOnes << index_bits); + } + + uint32_t GetIndex(size_t index_bits) const { + return index_and_mask & IndexMask(index_bits); + } + + uint32_t GetMask(size_t index_bits) const { + DCHECK_LT(index_bits, 32u); // GetMask() is valid only if there is at least 1 mask bit. + return index_and_mask >> index_bits; + } + + size_t GetBssOffset(size_t index_bits, uint32_t index, size_t slot_size) const; + + uint32_t index_and_mask; + uint32_t bss_offset; +}; + +using IndexBssMapping = LengthPrefixedArray; + +class IndexBssMappingLookup { + public: + static constexpr size_t npos = static_cast(-1); + + static size_t GetBssOffset(const IndexBssMapping* mapping, + uint32_t index, + uint32_t number_of_indexes, + size_t slot_size); +}; + +} // namespace art + +#endif // ART_RUNTIME_INDEX_BSS_MAPPING_H_ diff --git a/runtime/method_bss_mapping.h b/runtime/method_bss_mapping.h deleted file mode 100644 index 1476f93e21..0000000000 --- a/runtime/method_bss_mapping.h +++ /dev/null @@ -1,57 +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_METHOD_BSS_MAPPING_H_ -#define ART_RUNTIME_METHOD_BSS_MAPPING_H_ - -#include "base/bit_utils.h" -#include "base/length_prefixed_array.h" - -namespace art { - -// MethodBssMappingEntry describes a mapping of up to 17 method indexes to their offsets -// in the .bss. The highest index and its associated .bss offset are stored in plain form -// as `method_index` and `bss_offset`, respectively, while the additional indexes can be -// stored in compressed form if their associated .bss entries are consecutive and in the -// method index order. Each of the 16 bits of the `index_mask` corresponds to one of the -// previous 16 method indexes and indicates whether there is a .bss entry for that index. -// -struct MethodBssMappingEntry { - bool CoversIndex(uint32_t method_idx) const { - uint32_t diff = method_index - method_idx; - return (diff == 0) || (diff <= 16 && ((index_mask >> (16u - diff)) & 1u) != 0); - } - - uint32_t GetBssOffset(uint32_t method_idx, size_t entry_size) const { - DCHECK(CoversIndex(method_idx)); - uint32_t diff = method_index - method_idx; - if (diff == 0) { - return bss_offset; - } else { - return bss_offset - POPCOUNT(index_mask >> (16u - diff)) * entry_size; - } - } - - uint16_t method_index; - uint16_t index_mask; - uint32_t bss_offset; -}; - -using MethodBssMapping = LengthPrefixedArray; - -} // namespace art - -#endif // ART_RUNTIME_METHOD_BSS_MAPPING_H_ diff --git a/runtime/oat.h b/runtime/oat.h index a3e8eef91a..9d2118064d 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: Map boot image InternTable and ClassTable into app .bss. - static constexpr uint8_t kOatVersion[] = { '1', '3', '4', '\0' }; + // Last oat version changed reason: .bss index mapping change. + static constexpr uint8_t kOatVersion[] = { '1', '3', '5', '\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 69bd46d4c1..fbac3480bb 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -404,6 +404,79 @@ static inline bool MapConstantTables(const gc::space::ImageSpace* space, return true; } +static bool ReadIndexBssMapping(OatFile* oat_file, + /*inout*/const uint8_t** oat, + size_t dex_file_index, + const std::string& dex_file_location, + const char* tag, + /*out*/const IndexBssMapping** mapping, + std::string* error_msg) { + uint32_t index_bss_mapping_offset; + if (UNLIKELY(!ReadOatDexFileData(*oat_file, oat, &index_bss_mapping_offset))) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' truncated " + "after %s bss mapping offset", + oat_file->GetLocation().c_str(), + dex_file_index, + dex_file_location.c_str(), + tag); + return false; + } + const bool readable_index_bss_mapping_size = + index_bss_mapping_offset != 0u && + index_bss_mapping_offset <= oat_file->Size() && + IsAligned(index_bss_mapping_offset) && + oat_file->Size() - index_bss_mapping_offset >= IndexBssMapping::ComputeSize(0); + const IndexBssMapping* index_bss_mapping = readable_index_bss_mapping_size + ? reinterpret_cast(oat_file->Begin() + index_bss_mapping_offset) + : nullptr; + if (index_bss_mapping_offset != 0u && + (UNLIKELY(index_bss_mapping == nullptr) || + UNLIKELY(index_bss_mapping->size() == 0u) || + UNLIKELY(oat_file->Size() - index_bss_mapping_offset < + IndexBssMapping::ComputeSize(index_bss_mapping->size())))) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with unaligned or " + " truncated %s bss mapping, offset %u of %zu, length %zu", + oat_file->GetLocation().c_str(), + dex_file_index, + dex_file_location.c_str(), + tag, + index_bss_mapping_offset, + oat_file->Size(), + index_bss_mapping != nullptr ? index_bss_mapping->size() : 0u); + return false; + } + + *mapping = index_bss_mapping; + return true; +} + +static void DCheckIndexToBssMapping(OatFile* oat_file, + uint32_t number_of_indexes, + size_t slot_size, + const IndexBssMapping* index_bss_mapping) { + if (kIsDebugBuild && index_bss_mapping != nullptr) { + size_t index_bits = IndexBssMappingEntry::IndexBits(number_of_indexes); + const IndexBssMappingEntry* prev_entry = nullptr; + for (const IndexBssMappingEntry& entry : *index_bss_mapping) { + CHECK_ALIGNED_PARAM(entry.bss_offset, slot_size); + // When loading a non-executable ElfOatFile, .bss symbols are not even + // looked up, so we cannot verify the offset against BssSize(). + if (oat_file->IsExecutable()) { + CHECK_LT(entry.bss_offset, oat_file->BssSize()); + } + uint32_t mask = entry.GetMask(index_bits); + CHECK_LE(POPCOUNT(mask) * slot_size, entry.bss_offset); + size_t index_mask_span = (mask != 0u) ? 32u - index_bits - CTZ(mask) : 0u; + CHECK_LE(index_mask_span, entry.GetIndex(index_bits)); + if (prev_entry != nullptr) { + CHECK_LT(prev_entry->GetIndex(index_bits), entry.GetIndex(index_bits) - index_mask_span); + } + prev_entry = &entry; + } + CHECK_LT(prev_entry->GetIndex(index_bits), number_of_indexes); + } +} + bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { if (!GetOatHeader().IsValid()) { std::string cause = GetOatHeader().GetValidationErrorMessage(); @@ -658,54 +731,23 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { ? reinterpret_cast(Begin() + dex_layout_sections_offset) : nullptr; - uint32_t method_bss_mapping_offset; - if (UNLIKELY(!ReadOatDexFileData(*this, &oat, &method_bss_mapping_offset))) { - *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' truncated " - "after method bss mapping offset", - GetLocation().c_str(), - i, - dex_file_location.c_str()); + const IndexBssMapping* method_bss_mapping; + const IndexBssMapping* type_bss_mapping; + const IndexBssMapping* string_bss_mapping; + if (!ReadIndexBssMapping( + this, &oat, i, dex_file_location, "method", &method_bss_mapping, error_msg) || + !ReadIndexBssMapping( + this, &oat, i, dex_file_location, "type", &type_bss_mapping, error_msg) || + !ReadIndexBssMapping( + this, &oat, i, dex_file_location, "string", &string_bss_mapping, error_msg)) { return false; } - const bool readable_method_bss_mapping_size = - method_bss_mapping_offset != 0u && - method_bss_mapping_offset <= Size() && - IsAligned(method_bss_mapping_offset) && - Size() - method_bss_mapping_offset >= MethodBssMapping::ComputeSize(0); - const MethodBssMapping* method_bss_mapping = readable_method_bss_mapping_size - ? reinterpret_cast(Begin() + method_bss_mapping_offset) - : nullptr; - if (method_bss_mapping_offset != 0u && - (UNLIKELY(method_bss_mapping == nullptr) || - UNLIKELY(method_bss_mapping->size() == 0u) || - UNLIKELY(Size() - method_bss_mapping_offset < - MethodBssMapping::ComputeSize(method_bss_mapping->size())))) { - *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with unaligned or " - " truncated method bss mapping, offset %u of %zu, length %zu", - GetLocation().c_str(), - i, - dex_file_location.c_str(), - method_bss_mapping_offset, - Size(), - method_bss_mapping != nullptr ? method_bss_mapping->size() : 0u); - return false; - } - if (kIsDebugBuild && method_bss_mapping != nullptr) { - const MethodBssMappingEntry* prev_entry = nullptr; - for (const MethodBssMappingEntry& entry : *method_bss_mapping) { - CHECK_ALIGNED_PARAM(entry.bss_offset, static_cast(pointer_size)); - CHECK_LT(entry.bss_offset, BssSize()); - CHECK_LE(POPCOUNT(entry.index_mask) * static_cast(pointer_size), entry.bss_offset); - size_t index_mask_span = (entry.index_mask != 0u) ? 16u - CTZ(entry.index_mask) : 0u; - CHECK_LE(index_mask_span, entry.method_index); - if (prev_entry != nullptr) { - CHECK_LT(prev_entry->method_index, entry.method_index - index_mask_span); - } - prev_entry = &entry; - } - CHECK_LT(prev_entry->method_index, - reinterpret_cast(dex_file_pointer)->method_ids_size_); - } + DCheckIndexToBssMapping( + this, header->method_ids_size_, static_cast(pointer_size), method_bss_mapping); + DCheckIndexToBssMapping( + this, header->type_ids_size_, sizeof(GcRoot), type_bss_mapping); + DCheckIndexToBssMapping( + this, header->string_ids_size_, sizeof(GcRoot), string_bss_mapping); std::string canonical_location = DexFileLoader::GetDexCanonicalLocation(dex_file_location.c_str()); @@ -718,6 +760,8 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { dex_file_pointer, lookup_table_data, method_bss_mapping, + type_bss_mapping, + string_bss_mapping, class_offsets_pointer, dex_layout_sections); oat_dex_files_storage_.push_back(oat_dex_file); @@ -1530,7 +1574,9 @@ OatFile::OatDexFile::OatDexFile(const OatFile* oat_file, uint32_t dex_file_location_checksum, const uint8_t* dex_file_pointer, const uint8_t* lookup_table_data, - const MethodBssMapping* method_bss_mapping_data, + const IndexBssMapping* method_bss_mapping_data, + const IndexBssMapping* type_bss_mapping_data, + const IndexBssMapping* string_bss_mapping_data, const uint32_t* oat_class_offsets_pointer, const DexLayoutSections* dex_layout_sections) : oat_file_(oat_file), @@ -1540,6 +1586,8 @@ OatFile::OatDexFile::OatDexFile(const OatFile* oat_file, dex_file_pointer_(dex_file_pointer), lookup_table_data_(lookup_table_data), method_bss_mapping_(method_bss_mapping_data), + type_bss_mapping_(type_bss_mapping_data), + string_bss_mapping_(string_bss_mapping_data), oat_class_offsets_pointer_(oat_class_offsets_pointer), dex_layout_sections_(dex_layout_sections) { // Initialize TypeLookupTable. diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 7d4e6dfd6b..d06cf1bfd6 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -27,7 +27,7 @@ #include "compiler_filter.h" #include "dex_file.h" #include "dex_file_layout.h" -#include "method_bss_mapping.h" +#include "index_bss_mapping.h" #include "mirror/class.h" #include "oat.h" #include "os.h" @@ -440,10 +440,18 @@ class OatDexFile FINAL { return lookup_table_data_; } - const MethodBssMapping* GetMethodBssMapping() const { + const IndexBssMapping* GetMethodBssMapping() const { return method_bss_mapping_; } + const IndexBssMapping* GetTypeBssMapping() const { + return type_bss_mapping_; + } + + const IndexBssMapping* GetStringBssMapping() const { + return string_bss_mapping_; + } + const uint8_t* GetDexFilePointer() const { return dex_file_pointer_; } @@ -478,7 +486,9 @@ class OatDexFile FINAL { uint32_t dex_file_checksum, const uint8_t* dex_file_pointer, const uint8_t* lookup_table_data, - const MethodBssMapping* method_bss_mapping, + const IndexBssMapping* method_bss_mapping, + const IndexBssMapping* type_bss_mapping, + const IndexBssMapping* string_bss_mapping, const uint32_t* oat_class_offsets_pointer, const DexLayoutSections* dex_layout_sections); @@ -490,7 +500,9 @@ class OatDexFile FINAL { const uint32_t dex_file_location_checksum_ = 0u; const uint8_t* const dex_file_pointer_ = nullptr; const uint8_t* const lookup_table_data_ = nullptr; - const MethodBssMapping* const method_bss_mapping_ = nullptr; + const IndexBssMapping* const method_bss_mapping_ = nullptr; + 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_; const DexLayoutSections* const dex_layout_sections_ = nullptr; diff --git a/runtime/type_reference.h b/runtime/type_reference.h index 70bdc325f0..f7daa2bd58 100644 --- a/runtime/type_reference.h +++ b/runtime/type_reference.h @@ -30,7 +30,7 @@ class DexFile; // A type is located by its DexFile and the string_ids_ table index into that DexFile. class TypeReference : public DexFileReference { public: - explicit TypeReference(const DexFile* file = nullptr, dex::TypeIndex index = dex::TypeIndex()) + TypeReference(const DexFile* file, dex::TypeIndex index) : DexFileReference(file, index.index_) {} dex::TypeIndex TypeIndex() const { -- GitLab From 772099a8976cb8341475c42bfc595373778217dd Mon Sep 17 00:00:00 2001 From: Alex Light Date: Tue, 21 Nov 2017 14:05:04 -0800 Subject: [PATCH 068/226] Add remaining DDMS messages into DdmPublishChunk This ensures that one can get notified of every DDMS chunk that would be published. Add a test to ensure that appropriate ddms events are sent. We also refactor the underlying code so that the only way these events are sent is through the DdmPublishChunk callback. Test: ./test.py --host -j50 Bug: 62821960 Change-Id: I0c4a60b75615c206a27aff17df853b53cf5e8c96 --- runtime/debugger.cc | 51 ++++------ runtime/debugger.h | 8 -- runtime/trace.cc | 64 +++++++------ test/1940-ddms-ext/expected.txt | 3 + test/1940-ddms-ext/src-art/art/Test1940.java | 98 +++++++++++++++++++- test/knownfailures.json | 6 ++ 6 files changed, 157 insertions(+), 73 deletions(-) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 613e4fe7c7..3784212ef0 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -345,7 +345,14 @@ Dbg::DbgThreadLifecycleCallback Dbg::thread_lifecycle_callback_; Dbg::DbgClassLoadCallback Dbg::class_load_callback_; void DebuggerDdmCallback::DdmPublishChunk(uint32_t type, const ArrayRef& data) { - Dbg::DdmSendChunk(type, data); + if (gJdwpState == nullptr) { + VLOG(jdwp) << "Debugger thread not active, ignoring DDM send: " << type; + } else { + iovec vec[1]; + vec[0].iov_base = reinterpret_cast(const_cast(data.data())); + vec[0].iov_len = data.size(); + gJdwpState->DdmSendChunkV(type, vec, 1); + } } bool DebuggerActiveMethodInspectionCallback::IsMethodBeingInspected(ArtMethod* m ATTRIBUTE_UNUSED) { @@ -4458,10 +4465,11 @@ void Dbg::DdmSendThreadNotification(Thread* t, uint32_t type) { return; } + RuntimeCallbacks* cb = Runtime::Current()->GetRuntimeCallbacks(); if (type == CHUNK_TYPE("THDE")) { uint8_t buf[4]; JDWP::Set4BE(&buf[0], t->GetThreadId()); - Dbg::DdmSendChunk(CHUNK_TYPE("THDE"), 4, buf); + cb->DdmPublishChunk(CHUNK_TYPE("THDE"), ArrayRef(buf)); } else { CHECK(type == CHUNK_TYPE("THCR") || type == CHUNK_TYPE("THNM")) << type; ScopedObjectAccessUnchecked soa(Thread::Current()); @@ -4480,7 +4488,7 @@ void Dbg::DdmSendThreadNotification(Thread* t, uint32_t type) { JDWP::AppendUtf16BE(bytes, chars, char_count); } CHECK_EQ(bytes.size(), char_count*2 + sizeof(uint32_t)*2); - Dbg::DdmSendChunk(type, bytes); + cb->DdmPublishChunk(type, ArrayRef(bytes)); } } @@ -4523,30 +4531,6 @@ void Dbg::PostThreadDeath(Thread* t) { Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THDE")); } -void Dbg::DdmSendChunk(uint32_t type, const ArrayRef& data) { - DdmSendChunk(type, data.size(), data.data()); -} - -void Dbg::DdmSendChunk(uint32_t type, size_t byte_count, const uint8_t* buf) { - CHECK(buf != nullptr); - iovec vec[1]; - vec[0].iov_base = reinterpret_cast(const_cast(buf)); - vec[0].iov_len = byte_count; - Dbg::DdmSendChunkV(type, vec, 1); -} - -void Dbg::DdmSendChunk(uint32_t type, const std::vector& bytes) { - DdmSendChunk(type, bytes.size(), &bytes[0]); -} - -void Dbg::DdmSendChunkV(uint32_t type, const iovec* iov, int iov_count) { - if (gJdwpState == nullptr) { - VLOG(jdwp) << "Debugger thread not active, ignoring DDM send: " << type; - } else { - gJdwpState->DdmSendChunkV(type, iov, iov_count); - } -} - JDWP::JdwpState* Dbg::GetJdwpState() { return gJdwpState; } @@ -4624,7 +4608,8 @@ void Dbg::DdmSendHeapInfo(HpifWhen reason) { JDWP::Append4BE(bytes, heap->GetBytesAllocated()); JDWP::Append4BE(bytes, heap->GetObjectsAllocated()); CHECK_EQ(bytes.size(), 4U + (heap_count * (4 + 8 + 1 + 4 + 4 + 4 + 4))); - Dbg::DdmSendChunk(CHUNK_TYPE("HPIF"), bytes); + Runtime::Current()->GetRuntimeCallbacks()->DdmPublishChunk(CHUNK_TYPE("HPIF"), + ArrayRef(bytes)); } enum HpsgSolidity { @@ -4710,7 +4695,8 @@ class HeapChunkContext { CHECK_LE(pieceLenField_, p_); JDWP::Set4BE(pieceLenField_, totalAllocationUnits_); - Dbg::DdmSendChunk(type_, p_ - &buf_[0], &buf_[0]); + ArrayRef out(&buf_[0], p_ - &buf_[0]); + Runtime::Current()->GetRuntimeCallbacks()->DdmPublishChunk(type_, out); Reset(); } @@ -4892,6 +4878,7 @@ void Dbg::DdmSendHeapSegments(bool native) { if (when == HPSG_WHEN_NEVER) { return; } + RuntimeCallbacks* cb = Runtime::Current()->GetRuntimeCallbacks(); // Figure out what kind of chunks we'll be sending. CHECK(what == HPSG_WHAT_MERGED_OBJECTS || what == HPSG_WHAT_DISTINCT_OBJECTS) << static_cast(what); @@ -4899,7 +4886,8 @@ void Dbg::DdmSendHeapSegments(bool native) { // First, send a heap start chunk. uint8_t heap_id[4]; JDWP::Set4BE(&heap_id[0], 1); // Heap id (bogus; we only have one heap). - Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"), sizeof(heap_id), heap_id); + cb->DdmPublishChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"), + ArrayRef(heap_id)); Thread* self = Thread::Current(); Locks::mutator_lock_->AssertSharedHeld(self); @@ -4958,7 +4946,8 @@ void Dbg::DdmSendHeapSegments(bool native) { } // Finally, send a heap end chunk. - Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"), sizeof(heap_id), heap_id); + cb->DdmPublishChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"), + ArrayRef(heap_id)); } void Dbg::SetAllocTrackingEnabled(bool enable) { diff --git a/runtime/debugger.h b/runtime/debugger.h index c3184e8374..d5bad8dc67 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -662,14 +662,6 @@ class Dbg { static bool DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pReplyLen); static void DdmConnected() REQUIRES_SHARED(Locks::mutator_lock_); static void DdmDisconnected() REQUIRES_SHARED(Locks::mutator_lock_); - static void DdmSendChunk(uint32_t type, const ArrayRef& bytes) - REQUIRES_SHARED(Locks::mutator_lock_); - static void DdmSendChunk(uint32_t type, const std::vector& bytes) - REQUIRES_SHARED(Locks::mutator_lock_); - static void DdmSendChunk(uint32_t type, size_t len, const uint8_t* buf) - REQUIRES_SHARED(Locks::mutator_lock_); - static void DdmSendChunkV(uint32_t type, const iovec* iov, int iov_count) - REQUIRES_SHARED(Locks::mutator_lock_); // Visit breakpoint roots, used to prevent unloading of methods with breakpoints. static void VisitRoots(RootVisitor* visitor) diff --git a/runtime/trace.cc b/runtime/trace.cc index 4b5a7610a3..a113ab5cc8 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -413,42 +413,40 @@ void Trace::StopTracing(bool finish_tracing, bool flush_file) { sampling_pthread_ = 0U; } - { + if (the_trace != nullptr) { + stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0; + if (finish_tracing) { + the_trace->FinishTracing(); + } gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseInstrumentation, gc::kCollectorTypeInstrumentation); ScopedSuspendAll ssa(__FUNCTION__); - if (the_trace != nullptr) { - stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0; - if (finish_tracing) { - the_trace->FinishTracing(); - } - if (the_trace->trace_mode_ == TraceMode::kSampling) { - MutexLock mu(self, *Locks::thread_list_lock_); - runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr); + 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) { + if (the_trace->trace_file_->Flush() != 0) { + PLOG(WARNING) << "Could not flush trace file."; + } } else { - runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey); - runtime->GetInstrumentation()->RemoveListener( - the_trace, instrumentation::Instrumentation::kMethodEntered | - instrumentation::Instrumentation::kMethodExited | - instrumentation::Instrumentation::kMethodUnwind); + the_trace->trace_file_->MarkUnchecked(); // Do not trigger guard. } - if (the_trace->trace_file_.get() != nullptr) { - // Do not try to erase, so flush and close explicitly. - if (flush_file) { - if (the_trace->trace_file_->Flush() != 0) { - PLOG(WARNING) << "Could not flush trace file."; - } - } else { - the_trace->trace_file_->MarkUnchecked(); // Do not trigger guard. - } - if (the_trace->trace_file_->Close() != 0) { - PLOG(ERROR) << "Could not close trace file."; - } + if (the_trace->trace_file_->Close() != 0) { + PLOG(ERROR) << "Could not close trace file."; } - delete the_trace; } + delete the_trace; } if (stop_alloc_counting) { // Can be racy since SetStatsEnabled is not guarded by any locks. @@ -717,12 +715,12 @@ void Trace::FinishTracing() { FlushBuf(); } else { if (trace_file_.get() == nullptr) { - iovec iov[2]; - iov[0].iov_base = reinterpret_cast(const_cast(header.c_str())); - iov[0].iov_len = header.length(); - iov[1].iov_base = buf_.get(); - iov[1].iov_len = final_offset; - Dbg::DdmSendChunkV(CHUNK_TYPE("MPSE"), iov, 2); + std::vector data; + data.resize(header.length() + final_offset); + memcpy(data.data(), header.c_str(), header.length()); + memcpy(data.data() + header.length(), buf_.get(), final_offset); + Runtime::Current()->GetRuntimeCallbacks()->DdmPublishChunk(CHUNK_TYPE("MPSE"), + ArrayRef(data)); const bool kDumpTraceInfo = false; if (kDumpTraceInfo) { LOG(INFO) << "Trace sent:\n" << header; diff --git a/test/1940-ddms-ext/expected.txt b/test/1940-ddms-ext/expected.txt index cf4ad50e90..62d3b7bd4c 100644 --- a/test/1940-ddms-ext/expected.txt +++ b/test/1940-ddms-ext/expected.txt @@ -5,3 +5,6 @@ MyDdmHandler: Chunk returned: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, JVMTI returned chunk: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, -128, 0, 37]) Sending chunk: Chunk(Type: 0xDEADBEEF, Len: 8, data: [9, 10, 11, 12, 13, 14, 15, 16]) Chunk published: Chunk(Type: 0xDEADBEEF, Len: 8, data: [9, 10, 11, 12, 13, 14, 15, 16]) +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 f0ee7102a9..9f79eaebba 100644 --- a/test/1940-ddms-ext/src-art/art/Test1940.java +++ b/test/1940-ddms-ext/src-art/art/Test1940.java @@ -17,6 +17,7 @@ package art; import org.apache.harmony.dalvik.ddmc.*; +import dalvik.system.VMDebug; import java.lang.reflect.Method; import java.util.Arrays; @@ -30,6 +31,12 @@ public class Test1940 { public static final int MY_DDMS_TYPE = 0xDEADBEEF; public static final int MY_DDMS_RESPONSE_TYPE = 0xFADE7357; + public static final boolean PRINT_ALL_CHUNKS = false; + + public static interface DdmHandler { + public void HandleChunk(int type, byte[] data); + } + public static final class TestError extends Error { public TestError(String s) { super(s); } } @@ -69,11 +76,38 @@ public class Test1940 { public static final ChunkHandler SINGLE_HANDLER = new MyDdmHandler(); + public static DdmHandler CURRENT_HANDLER; + public static void HandlePublish(int type, byte[] data) { - System.out.println("Chunk published: " + printChunk(new Chunk(type, data, 0, data.length))); + if (PRINT_ALL_CHUNKS) { + System.out.println( + "Unknown Chunk published: " + printChunk(new Chunk(type, data, 0, data.length))); + } + CURRENT_HANDLER.HandleChunk(type, data); + } + + // TYPE Thread Create + public static final int TYPE_THCR = 0x54484352; + // Type Thread name + public static final int TYPE_THNM = 0x54484E4D; + // Type Thread death. + public static final int TYPE_THDE = 0x54484445; + // Type Heap info + public static final int TYPE_HPIF = 0x48504946; + // Type Trace Results + public static final int TYPE_MPSE = 0x4D505345; + + public static boolean IsFromThread(Thread t, byte[] data) { + // DDMS always puts the thread-id as the first 4 bytes. + ByteBuffer b = ByteBuffer.wrap(data); + b.order(ByteOrder.BIG_ENDIAN); + return b.getInt() == (int) t.getId(); } public static void run() throws Exception { + CURRENT_HANDLER = (type, data) -> { + System.out.println("Chunk published: " + printChunk(new Chunk(type, data, 0, data.length))); + }; initializeTest( Test1940.class, Test1940.class.getDeclaredMethod("HandlePublish", Integer.TYPE, new byte[0].getClass())); @@ -90,8 +124,70 @@ public class Test1940 { MY_DDMS_TYPE, new byte[] { 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }, 0, 8); System.out.println("Sending chunk: " + printChunk(c)); DdmServer.sendChunk(c); + + // Test thread chunks are sent. + final boolean[] types_seen = new boolean[] { false, false, false }; + CURRENT_HANDLER = (type, cdata) -> { + switch (type) { + case TYPE_THCR: + types_seen[0] = true; + break; + case TYPE_THNM: + types_seen[1] = true; + break; + case TYPE_THDE: + types_seen[2] = true; + break; + default: + // We don't want to print other types. + break; + } + }; + DdmVmInternal.threadNotify(true); + final Thread thr = new Thread(() -> { return; }, "THREAD"); + thr.start(); + thr.join(); + DdmVmInternal.threadNotify(false); + // 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: " + + Arrays.toString(types_seen)); + } else { + System.out.println("Saw expected thread events."); + } + + // Test heap chunks are sent. + CURRENT_HANDLER = (type, cdata) -> { + // The actual data is far to noisy for this test as it includes information about global heap + // state. + if (type == TYPE_HPIF) { + System.out.println("Expected chunk type published: " + type); + } + }; + final int HPIF_WHEN_NOW = 1; + if (!DdmVmInternal.heapInfoNotify(HPIF_WHEN_NOW)) { + System.out.println("Unexpected failure for heapInfoNotify!"); + } + + // method Tracing + CURRENT_HANDLER = (type, cdata) -> { + // This chunk includes timing and thread information so we just check the type. + if (type == TYPE_MPSE) { + System.out.println("Expected chunk type published: " + type); + } + }; + VMDebug.startMethodTracingDdms(/*size: default*/0, + /*flags: none*/ 0, + /*sampling*/ false, + /*interval*/ 0); + doNothing(); + doNothing(); + doNothing(); + doNothing(); + VMDebug.stopMethodTracing(); } + private static void doNothing() {} private static Chunk processChunk(byte[] val) { return processChunk(new Chunk(MY_DDMS_TYPE, val, 0, val.length)); } diff --git a/test/knownfailures.json b/test/knownfailures.json index c6b6574f1b..5b2ebf58a4 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -201,6 +201,12 @@ "time out."], "bug": "http://b/34369284" }, + { + "tests": "1940-ddms-ext", + "description": ["Test expects to be able to start tracing but we cannot", + "do that if tracing is already ongoing."], + "variant": "trace | stream" + }, { "tests": "137-cfi", "description": ["This test unrolls and expects managed frames, but", -- GitLab From fe9185aa7635ad4885a8f158f7a8feb770f08d7e Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Tue, 21 Nov 2017 19:01:10 -0800 Subject: [PATCH 069/226] Disable LinkData test for target Seems to be failing on the target, disable for now until we figure out why this is the case. Bug: 69561363 Test: make Change-Id: I940055a8576164af5c3a1678d14106595ca540cb --- dexlayout/dexlayout_test.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index 19c9038ee6..f994fd6533 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -734,6 +734,7 @@ TEST_F(DexLayoutTest, CodeItemOverrun) { // Test that link data is written out (or at least the header is updated). TEST_F(DexLayoutTest, LinkData) { + TEST_DISABLED_FOR_TARGET(); ScratchFile temp_dex; size_t file_size = 0; MutateDexFile(temp_dex.GetFile(), GetTestDexFileName("ManyMethods"), [&] (DexFile* dex) { -- GitLab From d77f8631c428f433e2b8a7ffdeb1659591c1ed74 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 22 Nov 2017 09:35:46 +0000 Subject: [PATCH 070/226] Blacklist org.apache.harmony.jpda.tests.jdwp.VirtualMachine.ExitTest#testExit001 bug: 69591477 Change-Id: Ic00045a385cb634c6f6b80a655f85d4f5281569b --- tools/libjdwp_art_failures.txt | 6 ++++++ tools/libjdwp_oj_art_failures.txt | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/tools/libjdwp_art_failures.txt b/tools/libjdwp_art_failures.txt index fd711bbd8b..abcc728890 100644 --- a/tools/libjdwp_art_failures.txt +++ b/tools/libjdwp_art_failures.txt @@ -103,5 +103,11 @@ result: EXEC_FAILED, bug: 69169846, name: "org.apache.harmony.jpda.tests.jdwp.DDM.DDMTest#testChunk001" +}, +{ + description: "Test crashes", + result: EXEC_FAILED, + bug: 69591477, + name: "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.ExitTest#testExit001" } ] diff --git a/tools/libjdwp_oj_art_failures.txt b/tools/libjdwp_oj_art_failures.txt index 3d06bcf100..e1cc831303 100644 --- a/tools/libjdwp_oj_art_failures.txt +++ b/tools/libjdwp_oj_art_failures.txt @@ -73,5 +73,11 @@ result: EXEC_FAILED, bug: 69169846, name: "org.apache.harmony.jpda.tests.jdwp.DDM.DDMTest#testChunk001" +}, +{ + description: "Test crashes", + result: EXEC_FAILED, + bug: 69591477, + name: "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.ExitTest#testExit001" } ] -- GitLab From cebb5e709af5ed5b475a56743f984967d991e7b9 Mon Sep 17 00:00:00 2001 From: Mingyao Yang Date: Tue, 21 Nov 2017 14:44:54 -0800 Subject: [PATCH 071/226] type conversion elimination for constant input type conversion on constant input can be eliminated if the constant value falls in the result type's range. Test: run-test on host, 711-checker-type-conversion Change-Id: I372139d681aa06fa6e760d7814c86ac949292813 --- compiler/optimizing/data_type.h | 13 ++++ compiler/optimizing/instruction_simplifier.cc | 10 +++ test/711-checker-type-conversion/expected.txt | 0 test/711-checker-type-conversion/info.txt | 1 + .../711-checker-type-conversion/src/Main.java | 71 +++++++++++++++++++ 5 files changed, 95 insertions(+) create mode 100644 test/711-checker-type-conversion/expected.txt create mode 100644 test/711-checker-type-conversion/info.txt create mode 100644 test/711-checker-type-conversion/src/Main.java diff --git a/compiler/optimizing/data_type.h b/compiler/optimizing/data_type.h index 75a7fbe6ca..d253036479 100644 --- a/compiler/optimizing/data_type.h +++ b/compiler/optimizing/data_type.h @@ -186,6 +186,7 @@ class DataType { } static bool IsTypeConversionImplicit(Type input_type, Type result_type); + static bool IsTypeConversionImplicit(int64_t value, Type result_type); static const char* PrettyDescriptor(Type type); @@ -213,6 +214,18 @@ inline bool DataType::IsTypeConversionImplicit(Type input_type, Type result_type MaxValueOfIntegralType(input_type) <= MaxValueOfIntegralType(result_type)); } +inline bool DataType::IsTypeConversionImplicit(int64_t value, Type result_type) { + if (IsIntegralType(result_type) && result_type != Type::kInt64) { + // If the constant value falls in the range of the result_type, type + // conversion isn't needed. + return value >= MinValueOfIntegralType(result_type) && + value <= MaxValueOfIntegralType(result_type); + } + // Conversion isn't implicit if it's into non-integer types, or 64-bit int + // which may have different number of registers. + return false; +} + } // namespace art #endif // ART_COMPILER_OPTIMIZING_DATA_TYPE_H_ diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index fbfee12be9..a6dfa475eb 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -1159,6 +1159,16 @@ void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruct RecordSimplification(); return; } + } else if (input->IsIntConstant()) { + // Try to eliminate type conversion on int constant whose value falls into + // the range of the result type. + int32_t value = input->AsIntConstant()->GetValue(); + if (DataType::IsTypeConversionImplicit(value, result_type)) { + instruction->ReplaceWith(input); + instruction->GetBlock()->RemoveInstruction(instruction); + RecordSimplification(); + return; + } } } diff --git a/test/711-checker-type-conversion/expected.txt b/test/711-checker-type-conversion/expected.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/711-checker-type-conversion/info.txt b/test/711-checker-type-conversion/info.txt new file mode 100644 index 0000000000..5b63572a76 --- /dev/null +++ b/test/711-checker-type-conversion/info.txt @@ -0,0 +1 @@ +Tests for type conversion elimination. diff --git a/test/711-checker-type-conversion/src/Main.java b/test/711-checker-type-conversion/src/Main.java new file mode 100644 index 0000000000..64ffcd2f1f --- /dev/null +++ b/test/711-checker-type-conversion/src/Main.java @@ -0,0 +1,71 @@ +/* + * 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 assertByteEquals(byte expected, byte result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + /// CHECK-START: byte Main.getByte1() instruction_simplifier (before) + /// CHECK: TypeConversion + /// CHECK: TypeConversion + /// CHECK: Add + /// CHECK: TypeConversion + + /// CHECK-START: byte Main.getByte1() instruction_simplifier (after) + /// CHECK-NOT: TypeConversion + /// CHECK: Add + /// CHECK: TypeConversion + + /// CHECK-START: byte Main.getByte1() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: Add + /// CHECK-NOT: TypeConversion + + static byte getByte1() { + int i = -2; + int j = -3; + return (byte)((byte)i + (byte)j); + } + + /// CHECK-START: byte Main.getByte2() instruction_simplifier (before) + /// CHECK: TypeConversion + /// CHECK: TypeConversion + /// CHECK: Add + /// CHECK: TypeConversion + + /// CHECK-START: byte Main.getByte2() instruction_simplifier (after) + /// CHECK-NOT: TypeConversion + /// CHECK: Add + /// CHECK: TypeConversion + + /// CHECK-START: byte Main.getByte2() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: Add + /// CHECK: TypeConversion + + static byte getByte2() { + int i = -100; + int j = -101; + return (byte)((byte)i + (byte)j); + } + + public static void main(String[] args) { + assertByteEquals(getByte1(), (byte)-5); + assertByteEquals(getByte2(), (byte)(-201)); + } +} -- GitLab From d278cb48e91bf06f325663a14f026608cc904355 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 22 Nov 2017 14:13:00 -0800 Subject: [PATCH 072/226] ART: Remove implicit boot image error aborts There are now explicit arguments that induce the same invariants. Move the configuration to the build system. This allows certain configurations to disable these checks. WARNING: Disabling the checks is highly discouraged. Bug: 69106371 Test: m Change-Id: I63e915005fcda588b223ec60ef8c9db28d42e577 --- compiler/driver/compiler_driver.cc | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 726401d09e..e4dd544890 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -2025,28 +2025,19 @@ class VerifyClassVisitor : public CompilationVisitor { ClassReference ref(manager_->GetDexFile(), class_def_index); manager_->GetCompiler()->RecordClassStatus(ref, klass->GetStatus()); - // It is *very* problematic if there are verification errors in the boot classpath. - // For example, we rely on things working OK without verification when the decryption dialog - // is brought up. So abort in a debug build if we find this violated. + // It is *very* problematic if there are resolution errors in the boot classpath. + // + // It is also bad if classes fail verification. For example, we rely on things working + // OK without verification when the decryption dialog is brought up. It is thus highly + // recommended to compile the boot classpath with + // --abort-on-hard-verifier-error --abort-on-soft-verifier-error + // which is the default build system configuration. if (kIsDebugBuild) { if (manager_->GetCompiler()->GetCompilerOptions().IsBootImage()) { - if (!klass->IsVerified()) { - // Re-run verification to get all failure messages if it soft-failed. - if (!klass->IsErroneous()) { - gLogVerbosity.verifier = true; - // Note: We can't call ClassLinker::VerifyClass, as it will elide the second - // verification. - Runtime* runtime = Runtime::Current(); - std::string v_error; - verifier::MethodVerifier::VerifyClass(soa.Self(), - klass.Get(), - runtime->GetCompilerCallbacks(), - runtime->IsAotCompiler(), - verifier::HardFailLogMode::kLogInternalFatal, - &v_error); - } + if (!klass->IsResolved() || klass->IsErroneous()) { LOG(FATAL) << "Boot classpath class " << klass->PrettyClass() - << " failed to fully verify: state= " << klass->GetStatus(); + << " failed to resolve/is erroneous: state= " << klass->GetStatus(); + UNREACHABLE(); } } if (klass->IsVerified()) { -- GitLab From 58cc1cb66c1a96ffba4a314edb2c5b4e8b235d5b Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 20 Nov 2017 13:27:29 +0000 Subject: [PATCH 073/226] Pass the debug_info_offset explicitly. In order to use debug_info_offset for encoding implementation details, rewrite all indirect users of it to fetch it before calling DexFile methods. This allows keeping the DexFile interface clean of runtime considerations. Test: test.py Change-Id: I4591e0039b5f822f4409aae411071ecbe97082b1 --- compiler/debug/elf_debug_info_writer.h | 6 +++++- compiler/debug/elf_debug_line_writer.h | 5 ++++- compiler/optimizing/instruction_builder.cc | 4 +++- dexdump/dexdump.cc | 6 ++++-- dexlayout/dex_ir.cc | 7 ++++--- dexlist/Android.bp | 2 +- dexlist/dexlist.cc | 3 ++- openjdkjvmti/ti_method.cc | 9 ++++++++- runtime/debugger.cc | 14 ++++++++++---- runtime/dex_file-inl.h | 6 ++++-- runtime/dex_file.h | 14 ++++++++++++-- runtime/dex_file_annotations.cc | 5 ++++- runtime/dex_file_test.cc | 3 ++- runtime/oat_file.cc | 8 ++++++++ runtime/oat_file.h | 6 +++++- 15 files changed, 76 insertions(+), 22 deletions(-) diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index 37c2d32091..d5999941d7 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -35,6 +35,7 @@ #include "mirror/array.h" #include "mirror/class-inl.h" #include "mirror/class.h" +#include "oat_file.h" namespace art { namespace debug { @@ -49,7 +50,8 @@ static std::vector GetParamNames(const MethodDebugInfo* mi) { std::vector names; if (mi->code_item != nullptr) { DCHECK(mi->dex_file != nullptr); - const uint8_t* stream = mi->dex_file->GetDebugInfoStream(mi->code_item); + uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*mi->dex_file, mi->code_item); + const uint8_t* stream = mi->dex_file->GetDebugInfoStream(debug_info_offset); if (stream != nullptr) { DecodeUnsignedLeb128(&stream); // line. uint32_t parameters_size = DecodeUnsignedLeb128(&stream); @@ -257,7 +259,9 @@ class ElfCompilationUnitWriter { // Write local variables. LocalInfos local_infos; + uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex, dex_code); if (dex->DecodeDebugLocalInfo(dex_code, + debug_info_offset, is_static, mi->dex_method_index, LocalInfoCallback, diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index 6e72b46174..943e03a765 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -26,6 +26,7 @@ #include "debug/src_map_elem.h" #include "dex_file-inl.h" #include "linker/elf_builder.h" +#include "oat_file.h" #include "stack_map.h" namespace art { @@ -158,7 +159,9 @@ class ElfDebugLineWriter { PositionInfos dex2line_map; DCHECK(mi->dex_file != nullptr); const DexFile* dex = mi->dex_file; - if (!dex->DecodeDebugPositionInfo(mi->code_item, PositionInfoCallback, &dex2line_map)) { + uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex, mi->code_item); + if (!dex->DecodeDebugPositionInfo( + mi->code_item, debug_info_offset, PositionInfoCallback, &dex2line_map)) { continue; } diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 61840cc20f..978d0c2225 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -29,6 +29,7 @@ #include "driver/compiler_options.h" #include "imtable-inl.h" #include "mirror/dex_cache.h" +#include "oat_file.h" #include "optimizing_compiler_stats.h" #include "quicken_info.h" #include "scoped_thread_state_change-inl.h" @@ -447,7 +448,8 @@ ArenaBitVector* HInstructionBuilder::FindNativeDebugInfoLocations() { /* expandable */ false, kArenaAllocGraphBuilder); locations->ClearAllBits(); - dex_file_->DecodeDebugPositionInfo(code_item_, Callback::Position, locations); + uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex_file_, code_item_); + dex_file_->DecodeDebugPositionInfo(code_item_, debug_info_offset, Callback::Position, locations); // Instruction-specific tweaks. IterationRange instructions = code_item_->Instructions(); for (const DexInstructionPcPair& inst : instructions) { diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 84ccaa03ab..a7af193f0a 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -1202,9 +1202,11 @@ static void dumpCode(const DexFile* pDexFile, u4 idx, u4 flags, // Positions and locals table in the debug info. bool is_static = (flags & kAccStatic) != 0; fprintf(gOutFile, " positions : \n"); - pDexFile->DecodeDebugPositionInfo(pCode, dumpPositionsCb, nullptr); + uint32_t debug_info_offset = pDexFile->GetDebugInfoOffset(pCode); + pDexFile->DecodeDebugPositionInfo(pCode, debug_info_offset, dumpPositionsCb, nullptr); fprintf(gOutFile, " locals : \n"); - pDexFile->DecodeDebugLocalInfo(pCode, is_static, idx, dumpLocalsCb, nullptr); + pDexFile->DecodeDebugLocalInfo( + pCode, debug_info_offset, is_static, idx, dumpLocalsCb, nullptr); } /* diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc index a8ba950c28..2af579c73c 100644 --- a/dexlayout/dex_ir.cc +++ b/dexlayout/dex_ir.cc @@ -570,16 +570,17 @@ CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, uint32_t tries_size = disk_code_item.tries_size_; // TODO: Calculate the size of the debug info. - const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(&disk_code_item); + uint32_t debug_info_offset = dex_file.GetDebugInfoOffset(&disk_code_item); + const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(debug_info_offset); DebugInfoItem* debug_info = nullptr; if (debug_info_stream != nullptr) { - debug_info = debug_info_items_map_.GetExistingObject(disk_code_item.debug_info_off_); + debug_info = debug_info_items_map_.GetExistingObject(debug_info_offset); if (debug_info == nullptr) { uint32_t debug_info_size = GetDebugInfoStreamSize(debug_info_stream); uint8_t* debug_info_buffer = new uint8_t[debug_info_size]; memcpy(debug_info_buffer, debug_info_stream, debug_info_size); debug_info = new DebugInfoItem(debug_info_size, debug_info_buffer); - AddItem(debug_info_items_map_, debug_info_items_, debug_info, disk_code_item.debug_info_off_); + AddItem(debug_info_items_map_, debug_info_items_, debug_info, debug_info_offset); } } diff --git a/dexlist/Android.bp b/dexlist/Android.bp index 03943bf0f2..8ecff4210e 100644 --- a/dexlist/Android.bp +++ b/dexlist/Android.bp @@ -17,7 +17,7 @@ art_cc_binary { host_supported: true, srcs: ["dexlist.cc"], cflags: ["-Wall", "-Werror"], - shared_libs: ["libart"], + shared_libs: ["libart", "libbase"], } art_cc_test { diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc index e3ca59c8bf..3bd903de5b 100644 --- a/dexlist/dexlist.cc +++ b/dexlist/dexlist.cc @@ -120,7 +120,8 @@ static void dumpMethod(const DexFile* pDexFile, // Find the first line. int firstLine = -1; - pDexFile->DecodeDebugPositionInfo(pCode, positionsCb, &firstLine); + uint32_t debug_info_offset = pDexFile->GetDebugInfoOffset(pCode); + pDexFile->DecodeDebugPositionInfo(pCode, debug_info_offset, positionsCb, &firstLine); // Method signature. const Signature signature = pDexFile->GetMethodSignature(pMethodId); diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index cf93bf0fb0..448ce41d0f 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -48,6 +48,7 @@ #include "mirror/object_array-inl.h" #include "modifiers.h" #include "nativehelper/scoped_local_ref.h" +#include "oat_file.h" #include "runtime_callbacks.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" @@ -259,7 +260,9 @@ jvmtiError MethodUtil::GetLocalVariableTable(jvmtiEnv* env, }; LocalVariableContext context(env); + uint32_t debug_info_offset = art::OatFile::GetDebugInfoOffset(*dex_file, code_item); if (!dex_file->DecodeDebugLocalInfo(code_item, + debug_info_offset, art_method->IsStatic(), art_method->GetDexMethodIndex(), LocalVariableContext::Callback, @@ -480,7 +483,9 @@ jvmtiError MethodUtil::GetLineNumberTable(jvmtiEnv* env, } LineNumberContext context; - bool success = dex_file->DecodeDebugPositionInfo(code_item, CollectLineNumbers, &context); + uint32_t debug_info_offset = art::OatFile::GetDebugInfoOffset(*dex_file, code_item); + bool success = dex_file->DecodeDebugPositionInfo( + code_item, debug_info_offset, CollectLineNumbers, &context); if (!success) { return ERR(ABSENT_INFORMATION); } @@ -648,7 +653,9 @@ class CommonLocalVariableClosure : public art::Closure { }; GetLocalVariableInfoContext context(slot_, dex_pc, descriptor, type); + uint32_t debug_info_offset = art::OatFile::GetDebugInfoOffset(*dex_file, code_item); if (!dex_file->DecodeDebugLocalInfo(code_item, + debug_info_offset, method->IsStatic(), method->GetDexMethodIndex(), GetLocalVariableInfoContext::Callback, diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 3784212ef0..1dcd935eea 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -58,6 +58,7 @@ #include "mirror/throwable.h" #include "nativehelper/scoped_local_ref.h" #include "nativehelper/scoped_primitive_array.h" +#include "oat_file.h" #include "obj_ptr-inl.h" #include "reflection.h" #include "safe_map.h" @@ -1680,7 +1681,9 @@ void Dbg::OutputLineTable(JDWP::RefTypeId, JDWP::MethodId method_id, JDWP::Expan context.pReply = pReply; if (code_item != nullptr) { - m->GetDexFile()->DecodeDebugPositionInfo(code_item, DebugCallbackContext::Callback, &context); + uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*(m->GetDexFile()), code_item); + m->GetDexFile()->DecodeDebugPositionInfo( + code_item, debug_info_offset, DebugCallbackContext::Callback, &context); } JDWP::Set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems); @@ -1737,9 +1740,10 @@ void Dbg::OutputVariableTable(JDWP::RefTypeId, JDWP::MethodId method_id, bool wi const DexFile::CodeItem* code_item = m->GetCodeItem(); if (code_item != nullptr) { + uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*(m->GetDexFile()), code_item); m->GetDexFile()->DecodeDebugLocalInfo( - code_item, m->IsStatic(), m->GetDexMethodIndex(), DebugCallbackContext::Callback, - &context); + code_item, debug_info_offset, m->IsStatic(), m->GetDexMethodIndex(), + DebugCallbackContext::Callback, &context); } JDWP::Set4BE(expandBufGetBuffer(pReply) + variable_count_offset, context.variable_count); @@ -3886,7 +3890,9 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize if (m != nullptr && !m->IsNative()) { const DexFile::CodeItem* const code_item = m->GetCodeItem(); DebugCallbackContext context(single_step_control, line_number, code_item); - m->GetDexFile()->DecodeDebugPositionInfo(code_item, DebugCallbackContext::Callback, &context); + uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*(m->GetDexFile()), code_item); + m->GetDexFile()->DecodeDebugPositionInfo( + code_item, debug_info_offset, DebugCallbackContext::Callback, &context); } // Activate single-step in the thread. diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h index c926a0d7fc..18809683cc 100644 --- a/runtime/dex_file-inl.h +++ b/runtime/dex_file-inl.h @@ -386,6 +386,7 @@ bool DexFile::DecodeDebugLocalInfo(const uint8_t* stream, template bool DexFile::DecodeDebugLocalInfo(const CodeItem* code_item, + uint32_t debug_info_offset, bool is_static, uint32_t method_idx, NewLocalCallback new_local_callback, @@ -398,7 +399,7 @@ bool DexFile::DecodeDebugLocalInfo(const CodeItem* code_item, for (; it.HasNext(); it.Next()) { arg_descriptors.push_back(it.GetDescriptor()); } - return DecodeDebugLocalInfo(GetDebugInfoStream(code_item), + return DecodeDebugLocalInfo(GetDebugInfoStream(debug_info_offset), GetLocation(), GetMethodDeclaringClassDescriptor(GetMethodId(method_idx)), arg_descriptors, @@ -488,12 +489,13 @@ bool DexFile::DecodeDebugPositionInfo(const uint8_t* stream, template bool DexFile::DecodeDebugPositionInfo(const CodeItem* code_item, + uint32_t debug_info_offset, DexDebugNewPosition position_functor, void* context) const { if (code_item == nullptr) { return false; } - return DecodeDebugPositionInfo(GetDebugInfoStream(code_item), + return DecodeDebugPositionInfo(GetDebugInfoStream(debug_info_offset), [this](uint32_t idx) { return StringDataByIdx(dex::StringIndex(idx)); }, diff --git a/runtime/dex_file.h b/runtime/dex_file.h index cdefb23400..2166ed15e6 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -698,6 +698,15 @@ class DexFile { return reinterpret_cast(addr); } + uint32_t GetDebugInfoOffset(const CodeItem* code_item) const { + if (code_item == nullptr) { + return 0; + } + CHECK(oat_dex_file_ == nullptr) + << "Should only use GetDebugInfoOffset in a non runtime setup"; + return code_item->debug_info_off_; + } + const char* GetReturnTypeDescriptor(const ProtoId& proto_id) const; // Returns the number of prototype identifiers in the .dex file. @@ -775,11 +784,10 @@ class DexFile { static int32_t FindCatchHandlerOffset(const CodeItem &code_item, uint32_t address); // Get the pointer to the start of the debugging data - const uint8_t* GetDebugInfoStream(const CodeItem* code_item) const { + const uint8_t* GetDebugInfoStream(uint32_t debug_info_off) const { // Check that the offset is in bounds. // Note that although the specification says that 0 should be used if there // is no debug information, some applications incorrectly use 0xFFFFFFFF. - const uint32_t debug_info_off = code_item->debug_info_off_; return (debug_info_off == 0 || debug_info_off >= size_) ? nullptr : begin_ + debug_info_off; } @@ -936,6 +944,7 @@ class DexFile { void* context); template bool DecodeDebugLocalInfo(const CodeItem* code_item, + uint32_t debug_info_offset, bool is_static, uint32_t method_idx, NewLocalCallback new_local, @@ -949,6 +958,7 @@ class DexFile { void* context); template bool DecodeDebugPositionInfo(const CodeItem* code_item, + uint32_t debug_info_offset, DexDebugNewPosition position_functor, void* context) const; diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index 27060aeff6..b44bd51643 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -28,6 +28,7 @@ #include "jvalue-inl.h" #include "mirror/field.h" #include "mirror/method.h" +#include "oat_file.h" #include "reflection.h" #include "thread.h" #include "well_known_classes.h" @@ -1571,7 +1572,9 @@ int32_t GetLineNumFromPC(const DexFile* dex_file, ArtMethod* method, uint32_t re // A method with no line number info should return -1 DexFile::LineNumFromPcContext context(rel_pc, -1); - dex_file->DecodeDebugPositionInfo(code_item, DexFile::LineNumForPcCb, &context); + uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex_file, code_item); + dex_file->DecodeDebugPositionInfo( + code_item, debug_info_offset, DexFile::LineNumForPcCb, &context); return context.line_num_; } diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index c963f6e111..14c36b4538 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -730,7 +730,8 @@ TEST_F(DexFileTest, OpenDexDebugInfoLocalNullType) { kRawDexDebugInfoLocalNullType, tmp.GetFilename().c_str(), 0xf25f2b38U, true); const DexFile::ClassDef& class_def = raw->GetClassDef(0); const DexFile::CodeItem* code_item = raw->GetCodeItem(raw->FindCodeItemOffset(class_def, 1)); - ASSERT_TRUE(raw->DecodeDebugLocalInfo(code_item, true, 1, Callback, nullptr)); + uint32_t debug_info_offset = raw->GetDebugInfoOffset(code_item); + ASSERT_TRUE(raw->DecodeDebugLocalInfo(code_item, debug_info_offset, true, 1, Callback, nullptr)); } } // namespace art diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 69bd46d4c1..726fbd0b0e 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -1455,6 +1455,14 @@ ArrayRef> OatFile::GetBssGcRoots() const { } } +uint32_t OatFile::GetDebugInfoOffset(const DexFile& dex_file ATTRIBUTE_UNUSED, + const DexFile::CodeItem* code_item) { + if (code_item == nullptr) { + return 0; + } + return code_item->debug_info_off_; +} + const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location, const uint32_t* dex_location_checksum, std::string* error_msg) const { diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 7d4e6dfd6b..73d64e0df1 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -109,11 +109,15 @@ class OatFile { static OatFile* OpenWritable(File* file, const std::string& location, const char* abs_dex_location, std::string* error_msg); - // Opens an oat file from an already opened File. Maps it PROT_READ, MAP_PRIVATE. + // Open an oat file from an already opened File. Maps it PROT_READ, MAP_PRIVATE. static OatFile* OpenReadable(File* file, const std::string& location, const char* abs_dex_location, std::string* error_msg); + // Return the debug info offset of the code item `item` located in `dex_file`. + static uint32_t GetDebugInfoOffset(const DexFile& dex_file, + const DexFile::CodeItem* item); + virtual ~OatFile(); bool IsExecutable() const { -- GitLab From 4e08fad9c97cd73d4363f9ed3a7e53e3ef0bfd64 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 23 Nov 2017 12:52:36 +0000 Subject: [PATCH 074/226] ART: Fix .bss index lookup in wrong dex file. Test: testrunner.py --host --jit --gcstress \ -t 096-array-copy-concurrent-gc (repeat 100x) Bug: 65737953 Change-Id: Ida3a3ef8a5366748f1fc8e048211d05eab4390a8 --- .../quick/quick_dexcache_entrypoints.cc | 46 +++++++++++++++---- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index 4ac99673ee..f756312983 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -104,6 +104,25 @@ static inline void StoreStringInBss(ArtMethod* outer_method, } } +static ALWAYS_INLINE bool CanReferenceBss(ArtMethod* outer_method, ArtMethod* caller) + REQUIRES_SHARED(Locks::mutator_lock_) { + // .bss references are used only for AOT-compiled code and only when the instruction + // originates from the outer method's dex file and the type or string index is tied to + // that dex file. As we do not want to check if the call is coming from AOT-compiled + // code (that could be expensive), simply check if the caller has the same dex file. + // + // If we've accepted running AOT-compiled code despite the runtime class loader + // resolving the caller to a different dex file, this check shall prevent us from + // filling the .bss slot and we shall keep going through the slow path. This is slow + // but correct; we do not really care that much about performance in this odd case. + // + // JIT can inline throwing instructions across dex files and this check prevents + // looking up the index in the wrong dex file in that case. If the caller and outer + // method have the same dex file, we may or may not find a .bss slot to update; + // if we do, this can still benefit AOT-compiled code executed later. + return outer_method->GetDexFile() == caller->GetDexFile(); +} + extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { // Called to ensure static storage base is initialized for direct static field reads and writes. @@ -113,9 +132,12 @@ extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx, auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod( self, CalleeSaveType::kSaveEverythingForClinit); ArtMethod* caller = caller_and_outer.caller; - mirror::Class* result = - ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, true, false); - if (LIKELY(result != nullptr)) { + mirror::Class* result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), + caller, + self, + /* can_run_clinit */ true, + /* verify_access */ false); + if (LIKELY(result != nullptr) && CanReferenceBss(caller_and_outer.outer_method, caller)) { StoreTypeInBss(caller_and_outer.outer_method, dex::TypeIndex(type_idx), result); } return result; @@ -128,9 +150,12 @@ extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, Thread* s auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod( self, CalleeSaveType::kSaveEverythingForClinit); ArtMethod* caller = caller_and_outer.caller; - mirror::Class* result = - ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, false); - if (LIKELY(result != nullptr)) { + mirror::Class* result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), + caller, + self, + /* can_run_clinit */ false, + /* verify_access */ false); + if (LIKELY(result != nullptr) && CanReferenceBss(caller_and_outer.outer_method, caller)) { StoreTypeInBss(caller_and_outer.outer_method, dex::TypeIndex(type_idx), result); } return result; @@ -143,8 +168,11 @@ extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, CalleeSaveType::kSaveEverything); ArtMethod* caller = caller_and_outer.caller; - mirror::Class* result = - ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, true); + mirror::Class* result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), + caller, + self, + /* can_run_clinit */ false, + /* verify_access */ true); // Do not StoreTypeInBss(); access check entrypoint is never used together with .bss. return result; } @@ -156,7 +184,7 @@ extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* CalleeSaveType::kSaveEverything); ArtMethod* caller = caller_and_outer.caller; mirror::String* result = ResolveStringFromCode(caller, dex::StringIndex(string_idx)); - if (LIKELY(result != nullptr)) { + if (LIKELY(result != nullptr) && CanReferenceBss(caller_and_outer.outer_method, caller)) { StoreStringInBss(caller_and_outer.outer_method, dex::StringIndex(string_idx), result); } return result; -- GitLab From b4c6acbf281c1cf960444e35bcac254a1f77c3ed Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 10 Nov 2017 12:48:14 +0000 Subject: [PATCH 075/226] Optimize lookup of quickening data. Use the debug_info_off_ of CodeItem to store the quickening offset in the vdex. Impact: - Code size almost unchanged (1 word saved per dex file in a vdex) - GetQuickenedInfoOf doesn't show up in simpleperf during app startup Test: test.py, run-libcore-tests, run-jdwp-tests Test: 628-vdex Change-Id: I15c3151feb58980a4c4d7469ca02728e94d36c07 --- compiler/driver/compiler_driver.cc | 4 + dex2oat/linker/oat_writer.cc | 84 ++++++------ openjdkjvmti/fixed_up_dex_file.cc | 3 +- runtime/dex_file.h | 13 +- runtime/oat_file.cc | 15 +- runtime/vdex_file.cc | 212 ++++++++++------------------- runtime/vdex_file.h | 32 +++-- 7 files changed, 169 insertions(+), 194 deletions(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index e4dd544890..fd7ae9f570 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -429,6 +429,10 @@ static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel( // optimizations that could break that. max_level = optimizer::DexToDexCompilationLevel::kDontDexToDexCompile; } + if (!VdexFile::CanEncodeQuickenedData(dex_file)) { + // Don't do any dex level optimizations if we cannot encode the quickening. + return optimizer::DexToDexCompilationLevel::kDontDexToDexCompile; + } if (klass->IsVerified()) { // Class is verified so we can enable DEX-to-DEX compilation for performance. return max_level; diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 99c62584ff..3e7a7cd67c 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -2596,20 +2596,15 @@ class OatWriter::WriteQuickeningInfoMethodVisitor : public DexMethodVisitor { class OatWriter::WriteQuickeningIndicesMethodVisitor { public: WriteQuickeningIndicesMethodVisitor(OutputStream* out, - uint32_t indices_offset, - const SafeMap& offset_map, - std::vector* dex_files_offset) + uint32_t quickening_info_bytes, + const SafeMap& offset_map) : out_(out), - indices_offset_(indices_offset), + quickening_info_bytes_(quickening_info_bytes), written_bytes_(0u), - dex_files_offset_(dex_files_offset), offset_map_(offset_map) {} bool VisitDexMethods(const std::vector& dex_files, const CompilerDriver& driver) { for (const DexFile* dex_file : dex_files) { - // Record the offset for this current dex file. It will be written in the vdex file - // later. - dex_files_offset_->push_back(indices_offset_ + GetNumberOfWrittenBytes()); const 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); @@ -2620,23 +2615,38 @@ class OatWriter::WriteQuickeningIndicesMethodVisitor { for (ClassDataItemIterator class_it(*dex_file, class_data); class_it.HasNext(); class_it.Next()) { - if (!class_it.IsAtMethod()) { + if (!class_it.IsAtMethod() || class_it.GetMethodCodeItem() == nullptr) { continue; } uint32_t method_idx = class_it.GetMemberIndex(); CompiledMethod* compiled_method = driver.GetCompiledMethod(MethodReference(dex_file, method_idx)); - if (HasQuickeningInfo(compiled_method)) { - uint32_t code_item_offset = class_it.GetMethodCodeItemOffset(); - uint32_t offset = offset_map_.Get(compiled_method->GetVmapTable().data()); - if (!out_->WriteFully(&code_item_offset, sizeof(code_item_offset)) || - !out_->WriteFully(&offset, sizeof(offset))) { + const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem(); + uint32_t existing_debug_info_offset = OatFile::GetDebugInfoOffset(*dex_file, code_item); + // If the existing offset is already out of bounds (and not magic marker 0xFFFFFFFF) + // we will pretend the method has been quickened. + bool existing_offset_out_of_bounds = + (existing_debug_info_offset >= dex_file->Size() && + existing_debug_info_offset != 0xFFFFFFFF); + bool has_quickening_info = HasQuickeningInfo(compiled_method); + if (has_quickening_info || existing_offset_out_of_bounds) { + uint32_t new_debug_info_offset = + dex_file->Size() + quickening_info_bytes_ + written_bytes_; + // Abort if overflow. + CHECK_GE(new_debug_info_offset, dex_file->Size()); + const_cast(code_item)->SetDebugInfoOffset(new_debug_info_offset); + uint32_t quickening_offset = has_quickening_info + ? offset_map_.Get(compiled_method->GetVmapTable().data()) + : VdexFile::kNoQuickeningInfoOffset; + if (!out_->WriteFully(&existing_debug_info_offset, + sizeof(existing_debug_info_offset)) || + !out_->WriteFully(&quickening_offset, sizeof(quickening_offset))) { PLOG(ERROR) << "Failed to write quickening info for " << dex_file->PrettyMethod(method_idx) << " to " << out_->GetLocation(); return false; } - written_bytes_ += sizeof(code_item_offset) + sizeof(offset); + written_bytes_ += sizeof(existing_debug_info_offset) + sizeof(quickening_offset); } } } @@ -2650,9 +2660,8 @@ class OatWriter::WriteQuickeningIndicesMethodVisitor { private: OutputStream* const out_; - const uint32_t indices_offset_; + const uint32_t quickening_info_bytes_; size_t written_bytes_; - std::vector* dex_files_offset_; // Maps quickening map to its offset in the file. const SafeMap& offset_map_; }; @@ -2682,30 +2691,27 @@ bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) { return false; } - WriteQuickeningIndicesMethodVisitor visitor2(vdex_out, - visitor1.GetNumberOfWrittenBytes(), - offset_map, - &dex_files_indices); - if (!visitor2.VisitDexMethods(*dex_files_, *compiler_driver_)) { - PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation(); - return false; - } - - DCHECK_EQ(dex_files_->size(), dex_files_indices.size()); - if (!vdex_out->WriteFully( - dex_files_indices.data(), sizeof(dex_files_indices[0]) * dex_files_indices.size())) { - PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation(); - return false; - } + if (visitor1.GetNumberOfWrittenBytes() > 0) { + WriteQuickeningIndicesMethodVisitor visitor2(vdex_out, + visitor1.GetNumberOfWrittenBytes(), + offset_map); + if (!visitor2.VisitDexMethods(*dex_files_, *compiler_driver_)) { + PLOG(ERROR) << "Failed to write the vdex quickening info. File: " + << vdex_out->GetLocation(); + return false; + } - if (!vdex_out->Flush()) { - PLOG(ERROR) << "Failed to flush stream after writing quickening info." - << " File: " << vdex_out->GetLocation(); - return false; + if (!vdex_out->Flush()) { + PLOG(ERROR) << "Failed to flush stream after writing quickening info." + << " File: " << vdex_out->GetLocation(); + return false; + } + size_quickening_info_ = visitor1.GetNumberOfWrittenBytes() + + visitor2.GetNumberOfWrittenBytes(); + } else { + // We know we did not quicken. + size_quickening_info_ = 0; } - size_quickening_info_ = visitor1.GetNumberOfWrittenBytes() + - visitor2.GetNumberOfWrittenBytes() + - dex_files_->size() * sizeof(uint32_t); } else { // We know we did not quicken. size_quickening_info_ = 0; diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc index c4988695f1..64dc3a5f02 100644 --- a/openjdkjvmti/fixed_up_dex_file.cc +++ b/openjdkjvmti/fixed_up_dex_file.cc @@ -60,7 +60,8 @@ static void DoDexUnquicken(const art::DexFile& new_dex_file, const art::DexFile& if (vdex == nullptr) { return; } - vdex->FullyUnquickenDexFile(new_dex_file, original_dex_file); + art::VdexFile::UnquickenDexFile( + new_dex_file, vdex->GetQuickeningInfo(), /* decompile_return_instruction */true); } std::unique_ptr FixedUpDexFile::Create(const art::DexFile& original) { diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 2166ed15e6..944a30849f 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -313,6 +313,11 @@ class DexFile { return *Instruction::At(insns_ + dex_pc); } + // Used when quickening / unquickening. + void SetDebugInfoOffset(uint32_t new_offset) { + debug_info_off_ = new_offset; + } + uint16_t registers_size_; // the number of registers used by this code // (locals + parameters) uint16_t ins_size_; // the number of words of incoming arguments to the method @@ -322,7 +327,13 @@ class DexFile { uint16_t tries_size_; // the number of try_items for this instance. If non-zero, // then these appear as the tries array just after the // insns in this instance. - uint32_t debug_info_off_; // file offset to debug info stream + // Normally holds file offset to debug info stream. In case the method has been quickened + // holds an offset in the Vdex file containing both the actual debug_info_off and the + // quickening info offset. + // Don't use this field directly, use OatFile::GetDebugInfoOffset in general ART code, + // or DexFile::GetDebugInfoOffset in code that are not using a Runtime. + uint32_t debug_info_off_; + uint32_t insns_size_in_code_units_; // size of the insns array, in 2 byte code units uint16_t insns_[1]; // actual array of bytecode. diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 5f54d5dbac..c82df7119f 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -1499,12 +1499,23 @@ ArrayRef> OatFile::GetBssGcRoots() const { } } -uint32_t OatFile::GetDebugInfoOffset(const DexFile& dex_file ATTRIBUTE_UNUSED, +uint32_t OatFile::GetDebugInfoOffset(const DexFile& dex_file, const DexFile::CodeItem* code_item) { if (code_item == nullptr) { return 0; } - return code_item->debug_info_off_; + const uint32_t debug_info_off = code_item->debug_info_off_; + // Note that although the specification says that 0 should be used if there + // is no debug information, some applications incorrectly use 0xFFFFFFFF. + if (debug_info_off < dex_file.Size() || debug_info_off == 0xFFFFFFFF) { + return debug_info_off; + } + const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); + if (oat_dex_file == nullptr || (oat_dex_file->GetOatFile() == nullptr)) { + return debug_info_off; + } + return oat_dex_file->GetOatFile()->GetVdexFile()->GetDebugInfoOffset( + dex_file, debug_info_off); } const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location, diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index 955098d8c2..fb9d24f9bc 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -171,56 +171,8 @@ bool VdexFile::OpenAllDexFiles(std::vector>* dex_ return true; } -// Utility class to easily iterate over the quickening data. -class QuickeningInfoIterator { - public: - QuickeningInfoIterator(uint32_t dex_file_index, - uint32_t number_of_dex_files, - const ArrayRef& quickening_info) - : quickening_info_(quickening_info) { - const unaligned_uint32_t* dex_file_indices = reinterpret_cast( - quickening_info.data() + - quickening_info.size() - - number_of_dex_files * sizeof(uint32_t)); - current_code_item_end_ = (dex_file_index == number_of_dex_files - 1) - ? dex_file_indices - : reinterpret_cast( - quickening_info_.data() + dex_file_indices[dex_file_index + 1]); - current_code_item_ptr_ = reinterpret_cast( - quickening_info_.data() + dex_file_indices[dex_file_index]); - } - - bool Done() const { - return current_code_item_ptr_ == current_code_item_end_; - } - - void Advance() { - current_code_item_ptr_ += 2; - } - - uint32_t GetCurrentCodeItemOffset() const { - return current_code_item_ptr_[0]; - } - - const ArrayRef GetCurrentQuickeningInfo() const { - return ArrayRef( - // Add sizeof(uint32_t) to remove the length from the data pointer. - quickening_info_.data() + current_code_item_ptr_[1] + sizeof(uint32_t), - *reinterpret_cast( - quickening_info_.data() + current_code_item_ptr_[1])); - } - - private: - typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t; - const ArrayRef& quickening_info_; - const unaligned_uint32_t* current_code_item_ptr_; - const unaligned_uint32_t* current_code_item_end_; - - DISALLOW_COPY_AND_ASSIGN(QuickeningInfoIterator); -}; - void VdexFile::Unquicken(const std::vector& dex_files, - const ArrayRef& quickening_info, + ArrayRef quickening_info, bool decompile_return_instruction) { if (quickening_info.size() == 0 && !decompile_return_instruction) { // Bail early if there is no quickening info and no need to decompile @@ -228,77 +180,60 @@ void VdexFile::Unquicken(const std::vector& dex_files, return; } - // When we do not decompile RETURN_VOID_NO_BARRIER use the faster - // QuickeningInfoIterator, otherwise use the slower ClassDataItemIterator - if (!decompile_return_instruction) { - for (uint32_t i = 0; i < dex_files.size(); ++i) { - for (QuickeningInfoIterator it(i, dex_files.size(), quickening_info); - !it.Done(); - it.Advance()) { - optimizer::ArtDecompileDEX( - *dex_files[i]->GetCodeItem(it.GetCurrentCodeItemOffset()), - it.GetCurrentQuickeningInfo(), - decompile_return_instruction); - } - } - } else { - for (uint32_t i = 0; i < dex_files.size(); ++i) { - QuickeningInfoIterator quick_it(i, dex_files.size(), quickening_info); - for (uint32_t j = 0; j < dex_files[i]->NumClassDefs(); ++j) { - const DexFile::ClassDef& class_def = dex_files[i]->GetClassDef(j); - const uint8_t* class_data = dex_files[i]->GetClassData(class_def); - if (class_data != nullptr) { - for (ClassDataItemIterator class_it(*dex_files[i], class_data); - class_it.HasNext(); - class_it.Next()) { - if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) { - uint32_t offset = class_it.GetMethodCodeItemOffset(); - if (!quick_it.Done() && offset == quick_it.GetCurrentCodeItemOffset()) { - optimizer::ArtDecompileDEX( - *class_it.GetMethodCodeItem(), - quick_it.GetCurrentQuickeningInfo(), - decompile_return_instruction); - quick_it.Advance(); - } else { - optimizer::ArtDecompileDEX(*class_it.GetMethodCodeItem(), - /* quickened_info */ {}, - decompile_return_instruction); - } - } - } - } - } - DCHECK(quick_it.Done()) << "Failed to use all quickening info"; - } + for (uint32_t i = 0; i < dex_files.size(); ++i) { + UnquickenDexFile(*dex_files[i], quickening_info, decompile_return_instruction); } } -static constexpr uint32_t kNoDexFile = -1; +typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t; -uint32_t VdexFile::GetDexFileIndex(const DexFile& dex_file) const { - uint32_t dex_index = 0; - for (const uint8_t* dex_file_start = GetNextDexFileData(nullptr); - dex_file_start != dex_file.Begin(); - dex_file_start = GetNextDexFileData(dex_file_start)) { - if (dex_file_start == nullptr) { - return kNoDexFile; - } - dex_index++; +static uint32_t GetDebugInfoOffsetInternal(const DexFile& dex_file, + uint32_t offset_in_code_item, + const ArrayRef& quickening_info) { + if (quickening_info.size() == 0) { + // No quickening info: offset is the right one, return it. + return offset_in_code_item; } - return dex_index; + uint32_t quickening_offset = offset_in_code_item - dex_file.Size(); + return *reinterpret_cast(quickening_info.data() + quickening_offset); } -void VdexFile::FullyUnquickenDexFile(const DexFile& target_dex_file, - const DexFile& original_dex_file) const { - uint32_t dex_index = GetDexFileIndex(original_dex_file); - if (dex_index == kNoDexFile) { - return; +static uint32_t GetQuickeningInfoOffsetFrom(const DexFile& dex_file, + uint32_t offset_in_code_item, + const ArrayRef& quickening_info) { + if (offset_in_code_item < dex_file.Size()) { + return VdexFile::kNoQuickeningInfoOffset; + } + if (quickening_info.size() == 0) { + // No quickening info. + return VdexFile::kNoQuickeningInfoOffset; } + uint32_t quickening_offset = offset_in_code_item - dex_file.Size(); + + // Add 2 * sizeof(uint32_t) for the debug info offset and the data offset. + CHECK_LE(quickening_offset + 2 * sizeof(uint32_t), quickening_info.size()); + return *reinterpret_cast( + quickening_info.data() + quickening_offset + sizeof(uint32_t)); +} + +static ArrayRef GetQuickeningInfoAt(const ArrayRef& quickening_info, + uint32_t quickening_offset) { + return (quickening_offset == VdexFile::kNoQuickeningInfoOffset) + ? ArrayRef(nullptr, 0) + : quickening_info.SubArray( + quickening_offset + sizeof(uint32_t), + *reinterpret_cast( + quickening_info.data() + quickening_offset)); +} - constexpr bool kDecompileReturnInstruction = true; - QuickeningInfoIterator it(dex_index, GetHeader().GetNumberOfDexFiles(), GetQuickeningInfo()); - // Iterate over the class definitions. Even if there is no quickening info, - // we want to unquicken RETURN_VOID_NO_BARRIER instruction. +void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, + ArrayRef quickening_info, + bool decompile_return_instruction) { + if (quickening_info.size() == 0 && !decompile_return_instruction) { + // Bail early if there is no quickening info and no need to decompile + // RETURN_VOID_NO_BARRIER instructions to RETURN_VOID instructions. + return; + } 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); @@ -307,44 +242,45 @@ void VdexFile::FullyUnquickenDexFile(const DexFile& target_dex_file, class_it.HasNext(); class_it.Next()) { if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) { - uint32_t offset = class_it.GetMethodCodeItemOffset(); - if (!it.Done() && offset == it.GetCurrentCodeItemOffset()) { - optimizer::ArtDecompileDEX( - *class_it.GetMethodCodeItem(), - it.GetCurrentQuickeningInfo(), - kDecompileReturnInstruction); - it.Advance(); - } else { - optimizer::ArtDecompileDEX(*class_it.GetMethodCodeItem(), - ArrayRef(nullptr, 0), - kDecompileReturnInstruction); + const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem(); + uint32_t quickening_offset = GetQuickeningInfoOffsetFrom( + target_dex_file, code_item->debug_info_off_, quickening_info); + if (quickening_offset != VdexFile::kNoQuickeningInfoOffset) { + // If we have quickening data, put back the original debug_info_off. + const_cast(code_item)->SetDebugInfoOffset( + GetDebugInfoOffsetInternal(target_dex_file, + code_item->debug_info_off_, + quickening_info)); } + optimizer::ArtDecompileDEX( + *code_item, + GetQuickeningInfoAt(quickening_info, quickening_offset), + decompile_return_instruction); } } } } } +uint32_t VdexFile::GetDebugInfoOffset(const DexFile& dex_file, uint32_t offset_in_code_item) const { + return GetDebugInfoOffsetInternal(dex_file, offset_in_code_item, GetQuickeningInfo()); +} + const uint8_t* VdexFile::GetQuickenedInfoOf(const DexFile& dex_file, uint32_t code_item_offset) const { - if (GetQuickeningInfo().size() == 0) { - // Bail early if there is no quickening info. - return nullptr; - } + ArrayRef quickening_info = GetQuickeningInfo(); + uint32_t quickening_offset = GetQuickeningInfoOffsetFrom( + dex_file, dex_file.GetCodeItem(code_item_offset)->debug_info_off_, quickening_info); - uint32_t dex_index = GetDexFileIndex(dex_file); - if (dex_index == kNoDexFile) { - return nullptr; - } + return GetQuickeningInfoAt(quickening_info, quickening_offset).data(); +} - for (QuickeningInfoIterator it(dex_index, GetHeader().GetNumberOfDexFiles(), GetQuickeningInfo()); - !it.Done(); - it.Advance()) { - if (code_item_offset == it.GetCurrentCodeItemOffset()) { - return it.GetCurrentQuickeningInfo().data(); - } - } - return nullptr; +bool VdexFile::CanEncodeQuickenedData(const DexFile& dex_file) { + // We are going to use the debug_info_off_ to signal there is + // quickened data, by putting a value greater than dex_file.Size(). So + // make sure we have some room in the offset by checking that we have at least + // half of the range of a uint32_t. + return dex_file.Size() <= (std::numeric_limits::max() >> 1); } } // namespace art diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index 11f1f527c1..3e0882693a 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -39,14 +39,14 @@ class DexFile; // DEX[1] the bytecode may have been quickened // ... // DEX[D] +// VerifierDeps +// uint8[D][] verification dependencies // QuickeningInfo -// uint8[] quickening data -// unaligned_uint32_t[2][] table of offsets pair: -// uint32_t[0] contains code_item_offset -// uint32_t[1] contains quickening data offset from the start +// uint8[D][] quickening data +// unaligned_uint32_t[D][2][] table of offsets pair: +// uint32_t[0] contains original CodeItem::debug_info_off_ +// uint32_t[1] contains quickening data offset from the start // of QuickeningInfo -// unalgined_uint32_t[D] start offsets (from the start of QuickeningInfo) in previous -// table for each dex file class VdexFile { public: @@ -72,8 +72,8 @@ class VdexFile { private: static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' }; - // Last update: Use set for unverified_classes_. - static constexpr uint8_t kVdexVersion[] = { '0', '1', '0', '\0' }; + // Last update: Lookup-friendly encoding for quickening info. + static constexpr uint8_t kVdexVersion[] = { '0', '1', '1', '\0' }; uint8_t magic_[4]; uint8_t version_[4]; @@ -149,17 +149,23 @@ class VdexFile { // decompiled to RETURN_VOID instructions using the slower ClassDataItemIterator // instead of the faster QuickeningInfoIterator. static void Unquicken(const std::vector& dex_files, - const ArrayRef& quickening_info, + ArrayRef quickening_info, bool decompile_return_instruction); - // Fully unquicken `target_dex_file` based on quickening info stored - // in this vdex file for `original_dex_file`. - void FullyUnquickenDexFile(const DexFile& target_dex_file, - const DexFile& original_dex_file) const; + // Fully unquicken `target_dex_file` based on `quickening_info`. + static void UnquickenDexFile(const DexFile& target_dex_file, + ArrayRef quickening_info, + bool decompile_return_instruction); // Return the quickening info of the given code item. const uint8_t* GetQuickenedInfoOf(const DexFile& dex_file, uint32_t code_item_offset) const; + uint32_t GetDebugInfoOffset(const DexFile& dex_file, uint32_t offset_in_code_item) const; + + static bool CanEncodeQuickenedData(const DexFile& dex_file); + + static constexpr uint32_t kNoQuickeningInfoOffset = -1; + private: bool HasDexSection() const { return GetHeader().GetDexSize() != 0; -- GitLab From 6f73f4aa8ebb388bf82141e0273dc76fbbc18896 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 23 Nov 2017 12:51:47 +0000 Subject: [PATCH 076/226] Revert "Revert "Try to be consistent when setting fields of OatWriter::OatDexFile."" This reverts commit 715d672e22953eb4d4a10b1b51a5584ee9b0e94e. The bug in the initial CL was forgetting to set the checksum in AddDexFileSource. I've added a test in oat_writer_test to prevent this problem. Test: oat_writer_test.cc Change-Id: Ic9f4a723be32ee1ad00068f230763e587d56aa2f --- dex2oat/linker/oat_writer.cc | 215 ++++++++++++++++-------------- dex2oat/linker/oat_writer.h | 2 - dex2oat/linker/oat_writer_test.cc | 5 + 3 files changed, 123 insertions(+), 99 deletions(-) diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 99c62584ff..93636a6b44 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -274,7 +274,9 @@ class OatWriter::OatDexFile { public: OatDexFile(const char* dex_file_location, DexFileSource source, - CreateTypeLookupTable create_type_lookup_table); + CreateTypeLookupTable create_type_lookup_table, + uint32_t dex_file_location_checksun, + size_t dex_file_size); OatDexFile(OatDexFile&& src) = default; const char* GetLocation() const { @@ -295,31 +297,47 @@ class OatWriter::OatDexFile { // Whether to create the type lookup table. CreateTypeLookupTable create_type_lookup_table_; - // Dex file size. Initialized when writing the dex file. + // Dex file size. Passed in the constructor, but could be + // overwritten by LayoutAndWriteDexFile. size_t dex_file_size_; // Offset of start of OatDexFile from beginning of OatHeader. It is // used to validate file position when writing. size_t offset_; - // Data to write. - uint32_t dex_file_location_size_; - const char* dex_file_location_data_; - uint32_t dex_file_location_checksum_; + ///// Start of data to write to vdex/oat file. + + const uint32_t dex_file_location_size_; + const char* const dex_file_location_data_; + + // The checksum of the dex file. + const uint32_t dex_file_location_checksum_; + + // Offset of the dex file in the vdex file. Set when writing dex files in + // SeekToDexFile. uint32_t dex_file_offset_; - uint32_t class_offsets_offset_; + + // The lookup table offset in the oat file. Set in WriteTypeLookupTables. uint32_t lookup_table_offset_; + + // Class and BSS offsets set in PrepareLayout. + uint32_t class_offsets_offset_; uint32_t method_bss_mapping_offset_; uint32_t type_bss_mapping_offset_; uint32_t string_bss_mapping_offset_; + + // Offset of dex sections that will have different runtime madvise states. + // Set in WriteDexLayoutSections. uint32_t dex_sections_layout_offset_; - // Data to write to a separate section. + // Data to write to a separate section. We set the length + // of the vector in OpenDexFiles. dchecked_vector class_offsets_; // Dex section layout info to serialize. DexLayoutSections dex_sections_layout_; + ///// End of data to write to vdex/oat file. private: DISALLOW_COPY_AND_ASSIGN(OatDexFile); }; @@ -417,6 +435,41 @@ OatWriter::OatWriter(bool compiling_boot_image, compact_dex_level_(compact_dex_level) { } +static bool ValidateDexFileHeader(const uint8_t* raw_header, const char* location) { + const bool valid_standard_dex_magic = DexFileLoader::IsMagicValid(raw_header); + if (!valid_standard_dex_magic) { + LOG(ERROR) << "Invalid magic number in dex file header. " << " File: " << location; + return false; + } + if (!DexFileLoader::IsVersionAndMagicValid(raw_header)) { + LOG(ERROR) << "Invalid version number in dex file header. " << " File: " << location; + return false; + } + const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_header); + if (header->file_size_ < sizeof(DexFile::Header)) { + LOG(ERROR) << "Dex file header specifies file size insufficient to contain the header." + << " File: " << location; + return false; + } + return true; +} + +static const UnalignedDexFileHeader* GetDexFileHeader(File* file, + uint8_t* raw_header, + const char* location) { + // Read the dex file header and perform minimal verification. + if (!file->ReadFully(raw_header, sizeof(DexFile::Header))) { + PLOG(ERROR) << "Failed to read dex file header. Actual: " + << " File: " << location << " Output: " << file->GetPath(); + return nullptr; + } + if (!ValidateDexFileHeader(raw_header, location)) { + return nullptr; + } + + return AsUnalignedDexFileHeader(raw_header); +} + bool OatWriter::AddDexFileSource(const char* filename, const char* location, CreateTypeLookupTable create_type_lookup_table) { @@ -428,12 +481,20 @@ bool OatWriter::AddDexFileSource(const char* filename, PLOG(ERROR) << "Failed to read magic number from dex file: '" << filename << "'"; return false; } else if (DexFileLoader::IsMagicValid(magic)) { + uint8_t raw_header[sizeof(DexFile::Header)]; + const UnalignedDexFileHeader* header = GetDexFileHeader(&fd, raw_header, location); + if (header == nullptr) { + return false; + } // The file is open for reading, not writing, so it's OK to let the File destructor // close it without checking for explicit Close(), so pass checkUsage = false. raw_dex_files_.emplace_back(new File(fd.Release(), location, /* checkUsage */ false)); - oat_dex_files_.emplace_back(location, - DexFileSource(raw_dex_files_.back().get()), - create_type_lookup_table); + oat_dex_files_.emplace_back(/* OatDexFile */ + location, + DexFileSource(raw_dex_files_.back().get()), + create_type_lookup_table, + header->checksum_, + header->file_size_); } else if (IsZipMagic(magic)) { if (!AddZippedDexFilesSource(std::move(fd), location, create_type_lookup_table)) { return false; @@ -467,9 +528,13 @@ bool OatWriter::AddZippedDexFilesSource(File&& zip_fd, zipped_dex_files_.push_back(std::move(entry)); zipped_dex_file_locations_.push_back(DexFileLoader::GetMultiDexLocation(i, location)); const char* full_location = zipped_dex_file_locations_.back().c_str(); - oat_dex_files_.emplace_back(full_location, - DexFileSource(zipped_dex_files_.back().get()), - create_type_lookup_table); + // We override the checksum from header with the CRC from ZIP entry. + oat_dex_files_.emplace_back(/* OatDexFile */ + full_location, + DexFileSource(zipped_dex_files_.back().get()), + create_type_lookup_table, + zipped_dex_files_.back()->GetCrc32(), + zipped_dex_files_.back()->GetUncompressedLength()); } if (zipped_dex_file_locations_.empty()) { LOG(ERROR) << "No dex files in zip file '" << location << "': " << error_msg; @@ -498,10 +563,13 @@ bool OatWriter::AddVdexDexFilesSource(const VdexFile& vdex_file, // We used `zipped_dex_file_locations_` to keep the strings in memory. zipped_dex_file_locations_.push_back(DexFileLoader::GetMultiDexLocation(i, location)); const char* full_location = zipped_dex_file_locations_.back().c_str(); - oat_dex_files_.emplace_back(full_location, - DexFileSource(current_dex_data), - create_type_lookup_table); - oat_dex_files_.back().dex_file_location_checksum_ = vdex_file.GetLocationChecksum(i); + const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(current_dex_data); + oat_dex_files_.emplace_back(/* OatDexFile */ + full_location, + DexFileSource(current_dex_data), + create_type_lookup_table, + vdex_file.GetLocationChecksum(i), + header->file_size_); } if (vdex_file.GetNextDexFileData(current_dex_data) != nullptr) { @@ -537,8 +605,12 @@ bool OatWriter::AddRawDexFileSource(const ArrayRef& data, return false; } - oat_dex_files_.emplace_back(location, DexFileSource(data.data()), create_type_lookup_table); - oat_dex_files_.back().dex_file_location_checksum_ = location_checksum; + oat_dex_files_.emplace_back(/* OatDexFile */ + location, + DexFileSource(data.data()), + create_type_lookup_table, + location_checksum, + header->file_size_); return true; } @@ -3204,44 +3276,6 @@ bool OatWriter::RecordOatDataOffset(OutputStream* out) { return true; } -bool OatWriter::ReadDexFileHeader(File* file, OatDexFile* oat_dex_file) { - // Read the dex file header and perform minimal verification. - uint8_t raw_header[sizeof(DexFile::Header)]; - if (!file->ReadFully(&raw_header, sizeof(DexFile::Header))) { - PLOG(ERROR) << "Failed to read dex file header. Actual: " - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - if (!ValidateDexFileHeader(raw_header, oat_dex_file->GetLocation())) { - return false; - } - - const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_header); - oat_dex_file->dex_file_size_ = header->file_size_; - oat_dex_file->dex_file_location_checksum_ = header->checksum_; - oat_dex_file->class_offsets_.resize(header->class_defs_size_); - return true; -} - -bool OatWriter::ValidateDexFileHeader(const uint8_t* raw_header, const char* location) { - const bool valid_standard_dex_magic = DexFileLoader::IsMagicValid(raw_header); - if (!valid_standard_dex_magic) { - LOG(ERROR) << "Invalid magic number in dex file header. " << " File: " << location; - return false; - } - if (!DexFileLoader::IsVersionAndMagicValid(raw_header)) { - LOG(ERROR) << "Invalid version number in dex file header. " << " File: " << location; - return false; - } - const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_header); - if (header->file_size_ < sizeof(DexFile::Header)) { - LOG(ERROR) << "Dex file header specifies file size insufficient to contain the header." - << " File: " << location; - return false; - } - return true; -} - bool OatWriter::WriteDexFiles(OutputStream* out, File* file, bool update_input_vdex) { TimingLogger::ScopedTiming split("Write Dex files", timings_); @@ -3396,12 +3430,15 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil DexLayout dex_layout(options, profile_compilation_info_, nullptr); dex_layout.ProcessDexFile(location.c_str(), dex_file.get(), 0); std::unique_ptr mem_map(dex_layout.GetAndReleaseMemMap()); + 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(mem_map->Begin()); + oat_dex_file->dex_file_size_ = header->file_size_; if (!WriteDexFile(out, oat_dex_file, mem_map->Begin(), /* update_input_vdex */ false)) { return false; } - oat_dex_file->dex_sections_layout_ = dex_layout.GetSections(); - // Set the checksum of the new oat dex file to be the original file's checksum. - oat_dex_file->dex_file_location_checksum_ = dex_file->GetLocationChecksum(); + CHECK_EQ(oat_dex_file->dex_file_location_checksum_, dex_file->GetLocationChecksum()); return true; } @@ -3451,9 +3488,6 @@ bool OatWriter::WriteDexFile(OutputStream* out, << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); return false; } - if (!ReadDexFileHeader(file, oat_dex_file)) { - return false; - } if (extracted_size < oat_dex_file->dex_file_size_) { LOG(ERROR) << "Extracted truncated dex file. Extracted size: " << extracted_size << " file size from header: " << oat_dex_file->dex_file_size_ @@ -3461,9 +3495,6 @@ bool OatWriter::WriteDexFile(OutputStream* out, return false; } - // Override the checksum from header with the CRC from ZIP entry. - oat_dex_file->dex_file_location_checksum_ = dex_file->GetCrc32(); - // Seek both file and stream to the end offset. size_t end_offset = start_offset + oat_dex_file->dex_file_size_; actual_offset = lseek(file->Fd(), end_offset, SEEK_SET); @@ -3512,9 +3543,6 @@ bool OatWriter::WriteDexFile(OutputStream* out, << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); return false; } - if (!ReadDexFileHeader(dex_file, oat_dex_file)) { - return false; - } // Copy the input dex file using sendfile(). if (!file->Copy(dex_file, 0, oat_dex_file->dex_file_size_)) { @@ -3576,12 +3604,6 @@ bool OatWriter::WriteDexFile(OutputStream* out, return false; } } - - // Update dex file size and resize class offsets in the OatDexFile. - // Note: For raw data, the checksum is passed directly to AddRawDexFileSource(). - // Note: For vdex, the checksum is copied from the existing vdex file. - oat_dex_file->dex_file_size_ = header->file_size_; - oat_dex_file->class_offsets_.resize(header->class_defs_size_); return true; } @@ -3617,29 +3639,22 @@ bool OatWriter::OpenDexFiles( } std::vector> dex_files; for (OatDexFile& oat_dex_file : oat_dex_files_) { - // Make sure no one messed with input files while we were copying data. - // At the very least we need consistent file size and number of class definitions. const uint8_t* raw_dex_file = dex_files_map->Begin() + oat_dex_file.dex_file_offset_ - map_offset; - if (!ValidateDexFileHeader(raw_dex_file, oat_dex_file.GetLocation())) { - // Note: ValidateDexFileHeader() already logged an error message. - LOG(ERROR) << "Failed to verify written dex file header!" + + if (kIsDebugBuild) { + // Sanity check our input files. + // Note that ValidateDexFileHeader() logs error messages. + CHECK(ValidateDexFileHeader(raw_dex_file, oat_dex_file.GetLocation())) + << "Failed to verify written dex file header!" << " Output: " << file->GetPath() << " ~ " << std::hex << map_offset << " ~ " << static_cast(raw_dex_file); - return false; - } - const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file); - if (header->file_size_ != oat_dex_file.dex_file_size_) { - LOG(ERROR) << "File size mismatch in written dex file header! Expected: " + + const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file); + CHECK_EQ(header->file_size_, oat_dex_file.dex_file_size_) + << "File size mismatch in written dex file header! Expected: " << oat_dex_file.dex_file_size_ << " Actual: " << header->file_size_ << " Output: " << file->GetPath(); - return false; - } - if (header->class_defs_size_ != oat_dex_file.class_offsets_.size()) { - LOG(ERROR) << "Class defs size mismatch in written dex file header! Expected: " - << oat_dex_file.class_offsets_.size() << " Actual: " << header->class_defs_size_ - << " Output: " << file->GetPath(); - return false; } // Now, open the dex file. @@ -3656,6 +3671,10 @@ bool OatWriter::OpenDexFiles( << " Error: " << error_msg; return false; } + + // Set the class_offsets size now that we have easy access to the DexFile and + // it has been verified in DexFileLoader::Open. + oat_dex_file.class_offsets_.resize(dex_files.back()->GetHeader().class_defs_size_); } *opened_dex_files_map = std::move(dex_files_map); @@ -3893,17 +3912,19 @@ void OatWriter::SetMultiOatRelativePatcherAdjustment() { OatWriter::OatDexFile::OatDexFile(const char* dex_file_location, DexFileSource source, - CreateTypeLookupTable create_type_lookup_table) + CreateTypeLookupTable create_type_lookup_table, + uint32_t dex_file_location_checksum, + size_t dex_file_size) : source_(source), create_type_lookup_table_(create_type_lookup_table), - dex_file_size_(0), + dex_file_size_(dex_file_size), offset_(0), dex_file_location_size_(strlen(dex_file_location)), dex_file_location_data_(dex_file_location), - dex_file_location_checksum_(0u), + dex_file_location_checksum_(dex_file_location_checksum), dex_file_offset_(0u), - class_offsets_offset_(0u), lookup_table_offset_(0u), + class_offsets_offset_(0u), method_bss_mapping_offset_(0u), type_bss_mapping_offset_(0u), string_bss_mapping_offset_(0u), diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index e0cb7ecc9f..4055878b55 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -325,8 +325,6 @@ class OatWriter { size_t WriteCodeDexFiles(OutputStream* out, size_t file_offset, size_t relative_offset); bool RecordOatDataOffset(OutputStream* out); - bool ReadDexFileHeader(File* oat_file, OatDexFile* oat_dex_file); - bool ValidateDexFileHeader(const uint8_t* raw_header, const char* location); bool WriteTypeLookupTables(OutputStream* oat_rodata, const std::vector>& opened_dex_files); bool WriteDexLayoutSections(OutputStream* oat_rodata, diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 7509d91677..b8286e808c 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -640,6 +640,11 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) { std::unique_ptr opened_dex_file2 = opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg); + ASSERT_EQ(opened_oat_file->GetOatDexFiles()[0]->GetDexFileLocationChecksum(), + dex_file1_data->GetHeader().checksum_); + ASSERT_EQ(opened_oat_file->GetOatDexFiles()[1]->GetDexFileLocationChecksum(), + dex_file2_data->GetHeader().checksum_); + ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_); ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(), &opened_dex_file1->GetHeader(), -- GitLab From f0010dd946b17490d2f792d845ea4f304a0bea28 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Tue, 21 Nov 2017 16:31:53 -0800 Subject: [PATCH 077/226] Apply individual intrinsic recognition during inliner. Rationale: Inliner could introduce new method calls, in particular it could change invoke-interface to invoke-virtual, which could expose new intrinsics. This situation happens, for example, in Kotlin generated code where String operations first go through the CharSequence interface. Rather than running a full new phase, we just recognize intrinsics when interface calls are replaced by virtual calls. This optimization boosts KotlinMicroItems by 100% Test: test-art-host test-art-target Change-Id: Ibd0519283d67ed6997b056e34b4eafdd49fcbc2d --- compiler/optimizing/inliner.cc | 7 ++++++ compiler/optimizing/intrinsics.cc | 42 ++++++++++++++++++------------- compiler/optimizing/intrinsics.h | 5 ++++ 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 3f4a3d8b8e..1eb1f2e46b 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -1258,6 +1258,13 @@ bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, new_invoke->SetReferenceTypeInfo(invoke_instruction->GetReferenceTypeInfo()); } return_replacement = new_invoke; + // Directly check if the new virtual can be recognized as an intrinsic. + // This way, we avoid running a full recognition pass just to detect + // these relative rare cases. + bool wrong_invoke_type = false; + if (IntrinsicsRecognizer::Recognize(new_invoke, &wrong_invoke_type)) { + MaybeRecordStat(stats_, kIntrinsicRecognized); + } } else { // TODO: Consider sharpening an invoke virtual once it is not dependent on the // compiler driver. diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index dfae534555..210607f88b 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -144,6 +144,23 @@ static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) { } } +bool IntrinsicsRecognizer::Recognize(HInvoke* invoke, /*out*/ bool* wrong_invoke_type) { + ArtMethod* art_method = invoke->GetResolvedMethod(); + if (art_method != nullptr && art_method->IsIntrinsic()) { + Intrinsics intrinsic = static_cast(art_method->GetIntrinsic()); + if (CheckInvokeType(intrinsic, invoke)) { + invoke->SetIntrinsic(intrinsic, + NeedsEnvironmentOrCache(intrinsic), + GetSideEffects(intrinsic), + GetExceptions(intrinsic)); + return true; + } else { + *wrong_invoke_type = true; + } + } + return false; +} + void IntrinsicsRecognizer::Run() { ScopedObjectAccess soa(Thread::Current()); for (HBasicBlock* block : graph_->GetReversePostOrder()) { @@ -151,23 +168,14 @@ void IntrinsicsRecognizer::Run() { inst_it.Advance()) { HInstruction* inst = inst_it.Current(); if (inst->IsInvoke()) { - HInvoke* invoke = inst->AsInvoke(); - ArtMethod* art_method = invoke->GetResolvedMethod(); - if (art_method != nullptr && art_method->IsIntrinsic()) { - Intrinsics intrinsic = static_cast(art_method->GetIntrinsic()); - if (!CheckInvokeType(intrinsic, invoke)) { - LOG(WARNING) << "Found an intrinsic with unexpected invoke type: " - << static_cast(intrinsic) << " for " - << art_method->PrettyMethod() - << invoke->DebugName(); - } else { - invoke->SetIntrinsic(intrinsic, - NeedsEnvironmentOrCache(intrinsic), - GetSideEffects(intrinsic), - GetExceptions(intrinsic)); - MaybeRecordStat(stats_, - MethodCompilationStat::kIntrinsicRecognized); - } + bool wrong_invoke_type = false; + if (Recognize(inst->AsInvoke(), &wrong_invoke_type)) { + MaybeRecordStat(stats_, MethodCompilationStat::kIntrinsicRecognized); + } else if (wrong_invoke_type) { + LOG(WARNING) + << "Found an intrinsic with unexpected invoke type: " + << inst->AsInvoke()->GetResolvedMethod()->PrettyMethod() << " " + << inst->DebugName(); } } } diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index 818d7f63a3..8088ab25a7 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -44,6 +44,11 @@ class IntrinsicsRecognizer : public HOptimization { void 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. + // Useful to recognize intrinsics on invidual calls outside this full pass. + static bool Recognize(HInvoke* invoke, /*out*/ bool* wrong_invoke_type); + static constexpr const char* kIntrinsicsRecognizerPassName = "intrinsics_recognition"; private: -- GitLab From 55f39ed15652f2215e0d6369c3628435694e51d4 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 24 Nov 2017 10:52:05 +0000 Subject: [PATCH 078/226] Pass class loader context in ART standalone. Otherwise we pessimize the code and get surprising AOT results. bug: 69623006 Test: performance of KotlinAutoReversiBench becomes expected Change-Id: I1c5b7152c016a721a12d05b5eee4857c2ba5a51e --- runtime/oat_file_manager.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 3071348435..b86f479eed 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -445,8 +445,13 @@ std::vector> OatFileManager::OpenDexFilesFromOat( // if it's in the class path). Note this trades correctness for performance // since the resulting slow down is unacceptable in some cases until b/64530081 // is fixed. + // We still pass the class loader context when the classpath string of the runtime + // is not empty, which is the situation when ART is invoked standalone. + ClassLoaderContext* actual_context = Runtime::Current()->GetClassPathString().empty() + ? nullptr + : context.get(); switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/ false, - /*class_loader_context*/ nullptr, + actual_context, /*out*/ &error_msg)) { case OatFileAssistant::kUpdateFailed: LOG(WARNING) << error_msg; -- GitLab From 49086971304e00347af177d5a23350b8063ebaa4 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 27 Nov 2017 15:00:34 +0000 Subject: [PATCH 079/226] Revert "ART: Disable part of ImageSpace loading test" This reverts commit 4b3fdfe0919e8de954924ec6b785743eb4895e80. The bug 63622587 was fixed by https://android-review.googlesource.com/524979 . Test: m test-art-host-gtest-image_space_test Bug: 63622587 Change-Id: I7d2e8b6902d01b0ac7b7813d48a550341583601b --- runtime/gc/space/image_space_test.cc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc index 429abf3311..fcc47d45fc 100644 --- a/runtime/gc/space/image_space_test.cc +++ b/runtime/gc/space/image_space_test.cc @@ -139,11 +139,10 @@ TEST_F(ImageSpaceDex2oatTest, Test) { EXPECT_FALSE(Runtime::Current()->GetHeap()->GetBootImageSpaces().empty()); } -// Disabled for b/63622587. -// using ImageSpaceNoDex2oatNoPatchoatTest = ImageSpaceLoadingTest; -// TEST_F(ImageSpaceNoDex2oatNoPatchoatTest, Test) { -// EXPECT_TRUE(Runtime::Current()->GetHeap()->GetBootImageSpaces().empty()); -// } +using ImageSpaceNoDex2oatNoPatchoatTest = ImageSpaceLoadingTest; +TEST_F(ImageSpaceNoDex2oatNoPatchoatTest, Test) { + EXPECT_TRUE(Runtime::Current()->GetHeap()->GetBootImageSpaces().empty()); +} using ImageSpaceNoRelocateNoDex2oatNoPatchoatTest = ImageSpaceLoadingTest; TEST_F(ImageSpaceNoRelocateNoDex2oatNoPatchoatTest, Test) { -- GitLab From cd09e1f4f9902b82fa62cb2da984ea499e3b2d70 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 24 Nov 2017 15:02:40 +0000 Subject: [PATCH 080/226] Fix stats reporting over 100% methods compiled. Add statistics for intrinsic and native stub compilation and JIT failing to allocate memory for committing the code. Clean up recording of compilation statistics. New statistics when building aosp_taimen-userdebug boot image with --dump-stats: Attempted compilation of 94304 methods: 99.99% (94295) compiled. OptStat#AttemptBytecodeCompilation: 89487 OptStat#AttemptIntrinsicCompilation: 160 OptStat#CompiledNativeStub: 4733 OptStat#CompiledIntrinsic: 84 OptStat#CompiledBytecode: 89478 ... where 94304=89487+4733+84 and 94295=89478+4733+84. Test: testrunner.py -b --host --optimizing Test: Manually inspect output of building boot image with --dump-stats. Bug: 69627511 Change-Id: I15eb2b062a96f09a7721948bcc77b83ee4f18efd --- compiler/Android.bp | 1 + compiler/optimizing/code_generator.cc | 4 +- compiler/optimizing/inliner.cc | 71 ++++----- compiler/optimizing/instruction_simplifier.cc | 6 +- .../optimizing/instruction_simplifier_arm.cc | 4 +- .../instruction_simplifier_arm64.cc | 4 +- .../optimizing/instruction_simplifier_mips.cc | 4 +- compiler/optimizing/optimizing_compiler.cc | 34 +++-- .../optimizing/optimizing_compiler_stats.h | 136 ++++++------------ 9 files changed, 102 insertions(+), 162 deletions(-) diff --git a/compiler/Android.bp b/compiler/Android.bp index 3699d668f7..37a18cb9e9 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -211,6 +211,7 @@ gensrcs { "driver/compiler_options.h", "linker/linker_patch.h", "optimizing/locations.h", + "optimizing/optimizing_compiler_stats.h", "utils/arm/constants_arm.h", "utils/mips/assembler_mips.h", diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 0bd3ce937a..aff6f9f64f 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -1411,10 +1411,10 @@ LocationSummary* CodeGenerator::CreateThrowingSlowPathLocations(HInstruction* in void CodeGenerator::GenerateNullCheck(HNullCheck* instruction) { if (compiler_options_.GetImplicitNullChecks()) { - MaybeRecordStat(stats_, kImplicitNullCheckGenerated); + MaybeRecordStat(stats_, MethodCompilationStat::kImplicitNullCheckGenerated); GenerateImplicitNullCheck(instruction); } else { - MaybeRecordStat(stats_, kExplicitNullCheckGenerated); + MaybeRecordStat(stats_, MethodCompilationStat::kExplicitNullCheckGenerated); GenerateExplicitNullCheck(instruction); } } diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 1eb1f2e46b..2444e43d64 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -441,9 +441,9 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) { // Add dependency due to devirtulization. We've assumed resolved_method // has single implementation. outermost_graph_->AddCHASingleImplementationDependency(resolved_method); - MaybeRecordStat(stats_, kCHAInline); + MaybeRecordStat(stats_, MethodCompilationStat::kCHAInline); } else { - MaybeRecordStat(stats_, kInlinedInvokeVirtualOrInterface); + MaybeRecordStat(stats_, MethodCompilationStat::kInlinedInvokeVirtualOrInterface); } } return result; @@ -533,7 +533,7 @@ bool HInliner::TryInlineFromInlineCache(const DexFile& caller_dex_file, } case kInlineCacheMonomorphic: { - MaybeRecordStat(stats_, kMonomorphicCall); + MaybeRecordStat(stats_, MethodCompilationStat::kMonomorphicCall); if (UseOnlyPolymorphicInliningWithNoDeopt()) { return TryInlinePolymorphicCall(invoke_instruction, resolved_method, inline_cache); } else { @@ -542,7 +542,7 @@ bool HInliner::TryInlineFromInlineCache(const DexFile& caller_dex_file, } case kInlineCachePolymorphic: { - MaybeRecordStat(stats_, kPolymorphicCall); + MaybeRecordStat(stats_, MethodCompilationStat::kPolymorphicCall); return TryInlinePolymorphicCall(invoke_instruction, resolved_method, inline_cache); } @@ -551,7 +551,7 @@ bool HInliner::TryInlineFromInlineCache(const DexFile& caller_dex_file, << "Interface or virtual call to " << caller_dex_file.PrettyMethod(invoke_instruction->GetDexMethodIndex()) << " is megamorphic and not inlined"; - MaybeRecordStat(stats_, kMegamorphicCall); + MaybeRecordStat(stats_, MethodCompilationStat::kMegamorphicCall); return false; } @@ -755,7 +755,7 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, dex::TypeIndex class_index = FindClassIndexIn( GetMonomorphicType(classes), caller_compilation_unit_); if (!class_index.IsValid()) { - LOG_FAIL(stats_, kNotInlinedDexCache) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedDexCache) << "Call to " << ArtMethod::PrettyMethod(resolved_method) << " from inline cache is not inlined because its class is not" << " accessible to the caller"; @@ -804,7 +804,7 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, /* is_first_run */ false); rtp_fixup.Run(); - MaybeRecordStat(stats_, kInlinedMonomorphicCall); + MaybeRecordStat(stats_, MethodCompilationStat::kInlinedMonomorphicCall); return true; } @@ -994,7 +994,7 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, return false; } - MaybeRecordStat(stats_, kInlinedPolymorphicCall); + MaybeRecordStat(stats_, MethodCompilationStat::kInlinedPolymorphicCall); // Run type propagation to get the guards typed. ReferenceTypePropagation rtp_fixup(graph_, @@ -1200,7 +1200,7 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget( /* is_first_run */ false); rtp_fixup.Run(); - MaybeRecordStat(stats_, kInlinedPolymorphicCall); + MaybeRecordStat(stats_, MethodCompilationStat::kInlinedPolymorphicCall); LOG_SUCCESS() << "Inlined same polymorphic target " << actual_method->PrettyMethod(); return true; @@ -1263,7 +1263,7 @@ bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, // these relative rare cases. bool wrong_invoke_type = false; if (IntrinsicsRecognizer::Recognize(new_invoke, &wrong_invoke_type)) { - MaybeRecordStat(stats_, kIntrinsicRecognized); + MaybeRecordStat(stats_, MethodCompilationStat::kIntrinsicRecognized); } } else { // TODO: Consider sharpening an invoke virtual once it is not dependent on the @@ -1308,14 +1308,14 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, ReferenceTypeInfo receiver_type, HInstruction** return_replacement) { if (method->IsProxyMethod()) { - LOG_FAIL(stats_, kNotInlinedProxy) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedProxy) << "Method " << method->PrettyMethod() << " is not inlined because of unimplemented inline support for proxy methods."; return false; } if (CountRecursiveCallsOf(method) > kMaximumNumberOfRecursiveCalls) { - LOG_FAIL(stats_, kNotInlinedRecursiveBudget) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedRecursiveBudget) << "Method " << method->PrettyMethod() << " is not inlined because it has reached its recursive call budget."; @@ -1329,10 +1329,10 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, if (TryPatternSubstitution(invoke_instruction, method, return_replacement)) { LOG_SUCCESS() << "Successfully replaced pattern of invoke " << method->PrettyMethod(); - MaybeRecordStat(stats_, kReplacedInvokeWithSimplePattern); + MaybeRecordStat(stats_, MethodCompilationStat::kReplacedInvokeWithSimplePattern); return true; } - LOG_FAIL(stats_, kNotInlinedWont) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedWont) << "Won't inline " << method->PrettyMethod() << " in " << outer_compilation_unit_.GetDexFile()->GetLocation() << " (" << caller_compilation_unit_.GetDexFile()->GetLocation() << ") from " @@ -1352,7 +1352,7 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, size_t inline_max_code_units = compiler_driver_->GetCompilerOptions().GetInlineMaxCodeUnits(); if (code_item->insns_size_in_code_units_ > inline_max_code_units) { - LOG_FAIL(stats_, kNotInlinedCodeItem) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedCodeItem) << "Method " << method->PrettyMethod() << " is not inlined because its code item is too big: " << code_item->insns_size_in_code_units_ @@ -1362,13 +1362,13 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, } if (code_item->tries_size_ != 0) { - LOG_FAIL(stats_, kNotInlinedTryCatch) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedTryCatch) << "Method " << method->PrettyMethod() << " is not inlined because of try block"; return false; } if (!method->IsCompilable()) { - LOG_FAIL(stats_, kNotInlinedNotVerified) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedNotVerified) << "Method " << method->PrettyMethod() << " has soft failures un-handled by the compiler, so it cannot be inlined"; } @@ -1378,7 +1378,7 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, if (Runtime::Current()->UseJitCompilation() || !compiler_driver_->IsMethodVerifiedWithoutFailures( method->GetDexMethodIndex(), class_def_idx, *method->GetDexFile())) { - LOG_FAIL(stats_, kNotInlinedNotVerified) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedNotVerified) << "Method " << method->PrettyMethod() << " couldn't be verified, so it cannot be inlined"; return false; @@ -1389,9 +1389,10 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, invoke_instruction->AsInvokeStaticOrDirect()->IsStaticWithImplicitClinitCheck()) { // Case of a static method that cannot be inlined because it implicitly // requires an initialization check of its declaring class. - LOG_FAIL(stats_, kNotInlinedDexCache) << "Method " << method->PrettyMethod() - << " is not inlined because it is static and requires a clinit" - << " check that cannot be emitted due to Dex cache limitations"; + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedDexCache) + << "Method " << method->PrettyMethod() + << " is not inlined because it is static and requires a clinit" + << " check that cannot be emitted due to Dex cache limitations"; return false; } @@ -1401,7 +1402,7 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, } LOG_SUCCESS() << method->PrettyMethod(); - MaybeRecordStat(stats_, kInlinedInvoke); + MaybeRecordStat(stats_, MethodCompilationStat::kInlinedInvoke); return true; } @@ -1684,7 +1685,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, handles_); if (builder.BuildGraph() != kAnalysisSuccess) { - LOG_FAIL(stats_, kNotInlinedCannotBuild) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedCannotBuild) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be built, so cannot be inlined"; return false; @@ -1692,7 +1693,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, if (!RegisterAllocator::CanAllocateRegistersFor(*callee_graph, compiler_driver_->GetInstructionSet())) { - LOG_FAIL(stats_, kNotInlinedRegisterAllocator) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedRegisterAllocator) << "Method " << callee_dex_file.PrettyMethod(method_index) << " cannot be inlined because of the register allocator"; return false; @@ -1745,7 +1746,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, HBasicBlock* exit_block = callee_graph->GetExitBlock(); if (exit_block == nullptr) { - LOG_FAIL(stats_, kNotInlinedInfiniteLoop) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedInfiniteLoop) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be inlined because it has an infinite loop"; return false; @@ -1756,14 +1757,14 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, if (predecessor->GetLastInstruction()->IsThrow()) { if (invoke_instruction->GetBlock()->IsTryBlock()) { // TODO(ngeoffray): Support adding HTryBoundary in Hgraph::InlineInto. - LOG_FAIL(stats_, kNotInlinedTryCatch) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedTryCatch) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be inlined because one branch always throws and" << " caller is in a try/catch block"; return false; } else if (graph_->GetExitBlock() == nullptr) { // TODO(ngeoffray): Support adding HExit in the caller graph. - LOG_FAIL(stats_, kNotInlinedInfiniteLoop) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedInfiniteLoop) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be inlined because one branch always throws and" << " caller does not have an exit block"; @@ -1782,7 +1783,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, } if (!has_one_return) { - LOG_FAIL(stats_, kNotInlinedAlwaysThrows) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedAlwaysThrows) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be inlined because it always throws"; return false; @@ -1795,7 +1796,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, if (block->GetLoopInformation()->IsIrreducible()) { // Don't inline methods with irreducible loops, they could prevent some // optimizations to run. - LOG_FAIL(stats_, kNotInlinedIrreducibleLoop) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedIrreducibleLoop) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be inlined because it contains an irreducible loop"; return false; @@ -1804,7 +1805,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, // Don't inline methods with loops without exit, since they cause the // loop information to be computed incorrectly when updating after // inlining. - LOG_FAIL(stats_, kNotInlinedLoopWithoutExit) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedLoopWithoutExit) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be inlined because it contains a loop with no exit"; return false; @@ -1815,7 +1816,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, !instr_it.Done(); instr_it.Advance()) { if (++number_of_instructions >= inlining_budget_) { - LOG_FAIL(stats_, kNotInlinedInstructionBudget) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedInstructionBudget) << "Method " << callee_dex_file.PrettyMethod(method_index) << " is not inlined because the outer method has reached" << " its instruction budget limit."; @@ -1824,7 +1825,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, HInstruction* current = instr_it.Current(); if (current->NeedsEnvironment() && (total_number_of_dex_registers_ >= kMaximumNumberOfCumulatedDexRegisters)) { - LOG_FAIL(stats_, kNotInlinedEnvironmentBudget) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedEnvironmentBudget) << "Method " << callee_dex_file.PrettyMethod(method_index) << " is not inlined because its caller has reached" << " its environment budget limit."; @@ -1834,7 +1835,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, if (current->NeedsEnvironment() && !CanEncodeInlinedMethodInStackMap(*caller_compilation_unit_.GetDexFile(), resolved_method)) { - LOG_FAIL(stats_, kNotInlinedStackMaps) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedStackMaps) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be inlined because " << current->DebugName() << " needs an environment, is in a different dex file" @@ -1843,7 +1844,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, } if (!same_dex_file && current->NeedsDexCacheOfDeclaringClass()) { - LOG_FAIL(stats_, kNotInlinedDexCache) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedDexCache) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be inlined because " << current->DebugName() << " it is in a different dex file and requires access to the dex cache"; @@ -1855,7 +1856,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, current->IsUnresolvedStaticFieldSet() || current->IsUnresolvedInstanceFieldSet()) { // Entrypoint for unresolved fields does not handle inlined frames. - LOG_FAIL(stats_, kNotInlinedUnresolvedEntrypoint) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedUnresolvedEntrypoint) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be inlined because it is using an unresolved" << " entrypoint"; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 4c18e16c48..7fa0c2be3d 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -48,7 +48,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void RecordSimplification() { simplification_occurred_ = true; simplifications_at_current_position_++; - MaybeRecordStat(stats_, kInstructionSimplifications); + MaybeRecordStat(stats_, MethodCompilationStat::kInstructionSimplifications); } bool ReplaceRotateWithRor(HBinaryOperation* op, HUShr* ushr, HShl* shl); @@ -663,7 +663,7 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { HGraph* graph = GetGraph(); if (object->IsNullConstant()) { - MaybeRecordStat(stats_, kRemovedInstanceOf); + MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf); instruction->ReplaceWith(graph->GetIntConstant(0)); instruction->GetBlock()->RemoveInstruction(instruction); RecordSimplification(); @@ -674,7 +674,7 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { // the return value check with the `outcome` check, b/27651442 . bool outcome = false; if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) { - MaybeRecordStat(stats_, kRemovedInstanceOf); + MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf); if (outcome && can_be_null) { // Type test will succeed, we just need a null test. HNotEqual* test = new (graph->GetAllocator()) HNotEqual(graph->GetNullConstant(), object); diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc index d41e49a0f3..92081e30b1 100644 --- a/compiler/optimizing/instruction_simplifier_arm.cc +++ b/compiler/optimizing/instruction_simplifier_arm.cc @@ -37,9 +37,7 @@ class InstructionSimplifierArmVisitor : public HGraphVisitor { private: void RecordSimplification() { - if (stats_ != nullptr) { - stats_->RecordStat(kInstructionSimplificationsArch); - } + MaybeRecordStat(stats_, MethodCompilationStat::kInstructionSimplificationsArch); } bool TryMergeIntoUsersShifterOperand(HInstruction* instruction); diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc index 69e1463ac4..1c44e5ac49 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.cc +++ b/compiler/optimizing/instruction_simplifier_arm64.cc @@ -37,9 +37,7 @@ class InstructionSimplifierArm64Visitor : public HGraphVisitor { private: void RecordSimplification() { - if (stats_ != nullptr) { - stats_->RecordStat(kInstructionSimplificationsArch); - } + MaybeRecordStat(stats_, MethodCompilationStat::kInstructionSimplificationsArch); } bool TryMergeIntoUsersShifterOperand(HInstruction* instruction); diff --git a/compiler/optimizing/instruction_simplifier_mips.cc b/compiler/optimizing/instruction_simplifier_mips.cc index 6a0d8a60c4..fa97401a0c 100644 --- a/compiler/optimizing/instruction_simplifier_mips.cc +++ b/compiler/optimizing/instruction_simplifier_mips.cc @@ -33,9 +33,7 @@ class InstructionSimplifierMipsVisitor : public HGraphVisitor { private: void RecordSimplification() { - if (stats_ != nullptr) { - stats_->RecordStat(kInstructionSimplificationsArch); - } + MaybeRecordStat(stats_, MethodCompilationStat::kInstructionSimplificationsArch); } bool TryExtractArrayAccessIndex(HInstruction* access, diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 53f9ec413b..095ca6372e 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -738,7 +738,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, ArtMethod* method, bool osr, VariableSizedHandleScope* handles) const { - MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kAttemptCompilation); + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kAttemptBytecodeCompilation); CompilerDriver* compiler_driver = GetCompilerDriver(); InstructionSet instruction_set = compiler_driver->GetInstructionSet(); const DexFile& dex_file = *dex_compilation_unit.GetDexFile(); @@ -757,8 +757,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, } if (Compiler::IsPathologicalCase(*code_item, method_idx, dex_file)) { - MaybeRecordStat(compilation_stats_.get(), - MethodCompilationStat::kNotCompiledPathological); + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kNotCompiledPathological); return nullptr; } @@ -768,8 +767,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, const CompilerOptions& compiler_options = compiler_driver->GetCompilerOptions(); if ((compiler_options.GetCompilerFilter() == CompilerFilter::kSpace) && (code_item->insns_size_in_code_units_ > kSpaceFilterOptimizingThreshold)) { - MaybeRecordStat(compilation_stats_.get(), - MethodCompilationStat::kNotCompiledSpaceFilter); + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kNotCompiledSpaceFilter); return nullptr; } @@ -800,8 +798,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, compiler_driver->GetCompilerOptions(), compilation_stats_.get())); if (codegen.get() == nullptr) { - MaybeRecordStat(compilation_stats_.get(), - MethodCompilationStat::kNotCompiledNoCodegen); + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kNotCompiledNoCodegen); return nullptr; } codegen->GetAssembler()->cfi().SetEnabled( @@ -873,6 +870,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, codegen->Compile(code_allocator); pass_observer.DumpDisassembly(); + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kCompiledBytecode); return codegen.release(); } @@ -883,6 +881,7 @@ CodeGenerator* OptimizingCompiler::TryCompileIntrinsic( const DexCompilationUnit& dex_compilation_unit, ArtMethod* method, VariableSizedHandleScope* handles) const { + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kAttemptIntrinsicCompilation); CompilerDriver* compiler_driver = GetCompilerDriver(); InstructionSet instruction_set = compiler_driver->GetInstructionSet(); const DexFile& dex_file = *dex_compilation_unit.GetDexFile(); @@ -894,8 +893,6 @@ CodeGenerator* OptimizingCompiler::TryCompileIntrinsic( // Do not attempt to compile on architectures we do not support. if (!IsInstructionSetSupported(instruction_set)) { - MaybeRecordStat(compilation_stats_.get(), - MethodCompilationStat::kNotCompiledUnsupportedIsa); return nullptr; } @@ -920,8 +917,6 @@ CodeGenerator* OptimizingCompiler::TryCompileIntrinsic( compiler_driver->GetCompilerOptions(), compilation_stats_.get())); if (codegen.get() == nullptr) { - MaybeRecordStat(compilation_stats_.get(), - MethodCompilationStat::kNotCompiledNoCodegen); return nullptr; } codegen->GetAssembler()->cfi().SetEnabled( @@ -979,6 +974,7 @@ CodeGenerator* OptimizingCompiler::TryCompileIntrinsic( VLOG(compiler) << "Compiled intrinsic: " << method->GetIntrinsic() << " " << graph->PrettyMethod(); + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kCompiledIntrinsic); return codegen.release(); } @@ -1046,8 +1042,6 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, } } if (codegen.get() != nullptr) { - MaybeRecordStat(compilation_stats_.get(), - MethodCompilationStat::kCompiled); compiled_method = Emit(&allocator, &code_allocator, codegen.get(), @@ -1139,10 +1133,12 @@ CompiledMethod* OptimizingCompiler::JniCompile(uint32_t access_flags, } } - return ArtQuickJniCompileMethod(GetCompilerDriver(), - access_flags, - method_idx, - dex_file); + CompiledMethod* compiled_method = ArtQuickJniCompileMethod(GetCompilerDriver(), + access_flags, + method_idx, + dex_file); + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kCompiledNativeStub); + return compiled_method; } Compiler* CreateOptimizingCompiler(CompilerDriver* driver) { @@ -1237,6 +1233,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, self, class_linker->GetClassRoot(ClassLinker::kObjectArrayClass), 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); DCHECK(self->IsExceptionPending()); self->ClearException(); return false; @@ -1253,9 +1250,9 @@ bool OptimizingCompiler::JitCompile(Thread* self, &method_info_data, &roots_data); if (stack_map_data == nullptr || roots_data == nullptr) { + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kJitOutOfMemoryForCommit); return false; } - MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kCompiled); codegen->BuildStackMaps(MemoryRegion(stack_map_data, stack_map_size), MemoryRegion(method_info_data, method_info_size), code_item); @@ -1279,6 +1276,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, codegen->GetGraph()->GetCHASingleImplementationList()); if (code == nullptr) { + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kJitOutOfMemoryForCommit); code_cache->ClearData(self, stack_map_data, roots_data); return false; } diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 07f9635aba..a2e92d2931 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -27,10 +27,13 @@ namespace art { -enum MethodCompilationStat { - kAttemptCompilation = 0, +enum class MethodCompilationStat { + kAttemptBytecodeCompilation = 0, + kAttemptIntrinsicCompilation, + kCompiledNativeStub, + kCompiledIntrinsic, + kCompiledBytecode, kCHAInline, - kCompiled, kInlinedInvoke, kReplacedInvokeWithSimplePattern, kInstructionSimplifications, @@ -94,8 +97,10 @@ enum MethodCompilationStat { kConstructorFenceRemovedLSE, kConstructorFenceRemovedPFRA, kConstructorFenceRemovedCFRE, + kJitOutOfMemoryForCommit, kLastStat }; +std::ostream& operator<<(std::ostream& os, const MethodCompilationStat& rhs); class OptimizingCompilerStats { public: @@ -105,7 +110,15 @@ class OptimizingCompilerStats { } void RecordStat(MethodCompilationStat stat, uint32_t count = 1) { - compile_stats_[stat] += count; + size_t stat_index = static_cast(stat); + DCHECK_LT(stat_index, arraysize(compile_stats_)); + compile_stats_[stat_index] += count; + } + + uint32_t GetStat(MethodCompilationStat stat) const { + size_t stat_index = static_cast(stat); + DCHECK_LT(stat_index, arraysize(compile_stats_)); + return compile_stats_[stat_index]; } void Log() const { @@ -114,18 +127,29 @@ class OptimizingCompilerStats { return; } - if (compile_stats_[kAttemptCompilation] == 0) { + uint32_t compiled_intrinsics = GetStat(MethodCompilationStat::kCompiledIntrinsic); + uint32_t compiled_native_stubs = GetStat(MethodCompilationStat::kCompiledNativeStub); + uint32_t bytecode_attempts = + GetStat(MethodCompilationStat::kAttemptBytecodeCompilation); + if (compiled_intrinsics == 0u && compiled_native_stubs == 0u && bytecode_attempts == 0u) { LOG(INFO) << "Did not compile any method."; } else { - float compiled_percent = - compile_stats_[kCompiled] * 100.0f / compile_stats_[kAttemptCompilation]; - LOG(INFO) << "Attempted compilation of " << compile_stats_[kAttemptCompilation] - << " methods: " << std::fixed << std::setprecision(2) - << compiled_percent << "% (" << compile_stats_[kCompiled] << ") compiled."; - - for (size_t i = 0; i < kLastStat; i++) { + uint32_t compiled_bytecode_methods = + GetStat(MethodCompilationStat::kCompiledBytecode); + // Successful intrinsic compilation preempts other compilation attempts but failed intrinsic + // compilation shall still count towards bytecode or native stub compilation attempts. + uint32_t num_compilation_attempts = + compiled_intrinsics + compiled_native_stubs + bytecode_attempts; + uint32_t num_successful_compilations = + compiled_intrinsics + compiled_native_stubs + compiled_bytecode_methods; + float compiled_percent = num_successful_compilations * 100.0f / num_compilation_attempts; + LOG(INFO) << "Attempted compilation of " + << num_compilation_attempts << " methods: " << std::fixed << std::setprecision(2) + << compiled_percent << "% (" << num_successful_compilations << ") compiled."; + + for (size_t i = 0; i < arraysize(compile_stats_); ++i) { if (compile_stats_[i] != 0) { - LOG(INFO) << PrintMethodCompilationStat(static_cast(i)) << ": " + LOG(INFO) << "OptStat#" << static_cast(i) << ": " << compile_stats_[i]; } } @@ -133,7 +157,7 @@ class OptimizingCompilerStats { } void AddTo(OptimizingCompilerStats* other_stats) { - for (size_t i = 0; i != kLastStat; ++i) { + for (size_t i = 0; i != arraysize(compile_stats_); ++i) { uint32_t count = compile_stats_[i]; if (count != 0) { other_stats->RecordStat(static_cast(i), count); @@ -142,91 +166,13 @@ class OptimizingCompilerStats { } void Reset() { - for (size_t i = 0; i != kLastStat; ++i) { - compile_stats_[i] = 0u; + for (std::atomic& stat : compile_stats_) { + stat = 0u; } } private: - std::string PrintMethodCompilationStat(MethodCompilationStat stat) const { - std::string name; - switch (stat) { - case kAttemptCompilation : name = "AttemptCompilation"; break; - case kCHAInline : name = "CHAInline"; break; - case kCompiled : name = "Compiled"; break; - case kInlinedInvoke : name = "InlinedInvoke"; break; - case kReplacedInvokeWithSimplePattern: name = "ReplacedInvokeWithSimplePattern"; break; - case kInstructionSimplifications: name = "InstructionSimplifications"; break; - case kInstructionSimplificationsArch: name = "InstructionSimplificationsArch"; break; - case kUnresolvedMethod : name = "UnresolvedMethod"; break; - case kUnresolvedField : name = "UnresolvedField"; break; - case kUnresolvedFieldNotAFastAccess : name = "UnresolvedFieldNotAFastAccess"; break; - case kRemovedCheckedCast: name = "RemovedCheckedCast"; break; - case kRemovedDeadInstruction: name = "RemovedDeadInstruction"; break; - case kRemovedNullCheck: name = "RemovedNullCheck"; break; - case kNotCompiledSkipped: name = "NotCompiledSkipped"; break; - case kNotCompiledInvalidBytecode: name = "NotCompiledInvalidBytecode"; break; - case kNotCompiledThrowCatchLoop : name = "NotCompiledThrowCatchLoop"; break; - case kNotCompiledAmbiguousArrayOp : name = "NotCompiledAmbiguousArrayOp"; break; - case kNotCompiledHugeMethod : name = "NotCompiledHugeMethod"; break; - case kNotCompiledLargeMethodNoBranches : name = "NotCompiledLargeMethodNoBranches"; break; - case kNotCompiledMalformedOpcode : name = "NotCompiledMalformedOpcode"; break; - case kNotCompiledNoCodegen : name = "NotCompiledNoCodegen"; break; - case kNotCompiledPathological : name = "NotCompiledPathological"; break; - case kNotCompiledSpaceFilter : name = "NotCompiledSpaceFilter"; break; - case kNotCompiledUnhandledInstruction : name = "NotCompiledUnhandledInstruction"; break; - case kNotCompiledUnsupportedIsa : name = "NotCompiledUnsupportedIsa"; break; - case kNotCompiledVerificationError : name = "NotCompiledVerificationError"; break; - case kNotCompiledVerifyAtRuntime : name = "NotCompiledVerifyAtRuntime"; break; - case kInlinedMonomorphicCall: name = "InlinedMonomorphicCall"; break; - case kInlinedPolymorphicCall: name = "InlinedPolymorphicCall"; break; - case kMonomorphicCall: name = "MonomorphicCall"; break; - case kPolymorphicCall: name = "PolymorphicCall"; break; - case kMegamorphicCall: name = "MegamorphicCall"; break; - case kBooleanSimplified : name = "BooleanSimplified"; break; - case kIntrinsicRecognized : name = "IntrinsicRecognized"; break; - case kLoopInvariantMoved : name = "LoopInvariantMoved"; break; - case kLoopVectorized : name = "LoopVectorized"; break; - case kLoopVectorizedIdiom : name = "LoopVectorizedIdiom"; break; - case kSelectGenerated : name = "SelectGenerated"; break; - case kRemovedInstanceOf: name = "RemovedInstanceOf"; break; - case kInlinedInvokeVirtualOrInterface: name = "InlinedInvokeVirtualOrInterface"; break; - case kImplicitNullCheckGenerated: name = "ImplicitNullCheckGenerated"; break; - case kExplicitNullCheckGenerated: name = "ExplicitNullCheckGenerated"; break; - case kSimplifyIf: name = "SimplifyIf"; break; - case kInstructionSunk: name = "InstructionSunk"; break; - case kNotInlinedUnresolvedEntrypoint: name = "NotInlinedUnresolvedEntrypoint"; break; - case kNotInlinedDexCache: name = "NotInlinedDexCache"; break; - case kNotInlinedStackMaps: name = "NotInlinedStackMaps"; break; - case kNotInlinedEnvironmentBudget: name = "NotInlinedEnvironmentBudget"; break; - case kNotInlinedInstructionBudget: name = "NotInlinedInstructionBudget"; break; - case kNotInlinedLoopWithoutExit: name = "NotInlinedLoopWithoutExit"; break; - case kNotInlinedIrreducibleLoop: name = "NotInlinedIrreducibleLoop"; break; - case kNotInlinedAlwaysThrows: name = "NotInlinedAlwaysThrows"; break; - case kNotInlinedInfiniteLoop: name = "NotInlinedInfiniteLoop"; break; - case kNotInlinedTryCatch: name = "NotInlinedTryCatch"; break; - case kNotInlinedRegisterAllocator: name = "NotInlinedRegisterAllocator"; break; - case kNotInlinedCannotBuild: name = "NotInlinedCannotBuild"; break; - case kNotInlinedNotVerified: name = "NotInlinedNotVerified"; break; - case kNotInlinedCodeItem: name = "NotInlinedCodeItem"; break; - case kNotInlinedWont: name = "NotInlinedWont"; break; - case kNotInlinedRecursiveBudget: name = "NotInlinedRecursiveBudget"; break; - case kNotInlinedProxy: name = "NotInlinedProxy"; break; - case kConstructorFenceGeneratedNew: name = "ConstructorFenceGeneratedNew"; break; - case kConstructorFenceGeneratedFinal: name = "ConstructorFenceGeneratedFinal"; break; - case kConstructorFenceRemovedLSE: name = "ConstructorFenceRemovedLSE"; break; - case kConstructorFenceRemovedPFRA: name = "ConstructorFenceRemovedPFRA"; break; - case kConstructorFenceRemovedCFRE: name = "ConstructorFenceRemovedCFRE"; break; - - case kLastStat: - LOG(FATAL) << "invalid stat " - << static_cast::type>(stat); - UNREACHABLE(); - } - return "OptStat#" + name; - } - - std::atomic compile_stats_[kLastStat]; + std::atomic compile_stats_[static_cast(MethodCompilationStat::kLastStat)]; DISALLOW_COPY_AND_ASSIGN(OptimizingCompilerStats); }; -- GitLab From 24ed94f83404bff97663142326762506458fd8cc Mon Sep 17 00:00:00 2001 From: Richard Uhler Date: Thu, 16 Nov 2017 11:54:29 +0000 Subject: [PATCH 081/226] Add DexFile.getStaticSizeOfFile function. Bug: b/69729799 Test: art/test/testrunner/testrunner.py -b -t 071-dexfile-get-static-size Change-Id: I7d524bd0f8c8bab74b1831b1db35f5952e8ce2aa --- runtime/native/dalvik_system_DexFile.cc | 20 +++++- test/071-dexfile-get-static-size/build | 30 +++++++++ test/071-dexfile-get-static-size/expected.txt | 4 ++ test/071-dexfile-get-static-size/info.txt | 3 + .../071-dexfile-get-static-size/src/Main.java | 62 ++++++++++++++++++ test/071-dexfile-get-static-size/test1.dex | Bin 0 -> 1864 bytes test/071-dexfile-get-static-size/test2.dex | Bin 0 -> 1264 bytes 7 files changed, 118 insertions(+), 1 deletion(-) create mode 100755 test/071-dexfile-get-static-size/build create mode 100644 test/071-dexfile-get-static-size/expected.txt create mode 100644 test/071-dexfile-get-static-size/info.txt create mode 100644 test/071-dexfile-get-static-size/src/Main.java create mode 100644 test/071-dexfile-get-static-size/test1.dex create mode 100644 test/071-dexfile-get-static-size/test2.dex diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 22355638cd..c0de374904 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -744,6 +744,23 @@ static jobjectArray DexFile_getDexFileOutputPaths(JNIEnv* env, return result; } +static jlong DexFile_getStaticSizeOfDexFile(JNIEnv* env, jclass, jobject cookie) { + const OatFile* oat_file = nullptr; + std::vector dex_files; + if (!ConvertJavaArrayToDexFiles(env, cookie, /*out */ dex_files, /* out */ oat_file)) { + DCHECK(env->ExceptionCheck()); + return 0; + } + + uint64_t file_size = 0; + for (auto& dex_file : dex_files) { + if (dex_file) { + file_size += dex_file->GetHeader().file_size_; + } + } + return static_cast(file_size); +} + static JNINativeMethod gMethods[] = { NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"), NATIVE_METHOD(DexFile, @@ -779,7 +796,8 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(DexFile, getDexFileStatus, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"), NATIVE_METHOD(DexFile, getDexFileOutputPaths, - "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;") + "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"), + NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J") }; void register_dalvik_system_DexFile(JNIEnv* env) { diff --git a/test/071-dexfile-get-static-size/build b/test/071-dexfile-get-static-size/build new file mode 100755 index 0000000000..0bba66d065 --- /dev/null +++ b/test/071-dexfile-get-static-size/build @@ -0,0 +1,30 @@ +#!/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. + +./default-build "$@" + +# Create and add as resources to the test jar file: +# 1. test1.dex +# 2. test2.dex +# 3. test-jar.jar, containing test1.dex as classes.dex +# 4. multi-jar.jar, containing test1.dex as classes.dex and test2.dex as classes2.dex +mkdir test-jar +cp test1.dex test-jar/classes.dex +cp test2.dex test-jar/classes2.dex +zip -j test-jar.jar test-jar/classes.dex +zip -j multi-jar.jar test-jar/classes.dex test-jar/classes2.dex +jar uf ${TEST_NAME}.jar test1.dex test2.dex test-jar.jar multi-jar.jar + diff --git a/test/071-dexfile-get-static-size/expected.txt b/test/071-dexfile-get-static-size/expected.txt new file mode 100644 index 0000000000..dfb77c3a2f --- /dev/null +++ b/test/071-dexfile-get-static-size/expected.txt @@ -0,0 +1,4 @@ +Size for test1.dex: 1864 +Size for test2.dex: 1264 +Size for test-jar.jar: 1864 +Size for multi-jar.jar: 3128 diff --git a/test/071-dexfile-get-static-size/info.txt b/test/071-dexfile-get-static-size/info.txt new file mode 100644 index 0000000000..5b528e81b4 --- /dev/null +++ b/test/071-dexfile-get-static-size/info.txt @@ -0,0 +1,3 @@ +Test DexFile.getStaticSizeOfDexFile API. + +test1.dex and test2.dex are arbitrary valid dex files. diff --git a/test/071-dexfile-get-static-size/src/Main.java b/test/071-dexfile-get-static-size/src/Main.java new file mode 100644 index 0000000000..4bf453801e --- /dev/null +++ b/test/071-dexfile-get-static-size/src/Main.java @@ -0,0 +1,62 @@ +/* + * 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.io.InputStream; +import java.io.OutputStream; +import java.io.FileOutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +public class Main { + private static void extractResource(String resource, String filename) throws Exception { + ClassLoader loader = Main.class.getClassLoader(); + InputStream is = loader.getResourceAsStream(resource); + OutputStream os = new FileOutputStream(filename); + int read; + byte[] buf = new byte[4096]; + while ((read = is.read(buf)) >= 0) { + os.write(buf, 0, read); + } + is.close(); + os.close(); + } + + private static long getDexFileSize(String filename) throws Exception { + ClassLoader loader = Main.class.getClassLoader(); + Class DexFile = loader.loadClass("dalvik.system.DexFile"); + Method DexFile_loadDex = DexFile.getMethod("loadDex", + String.class, + String.class, + Integer.TYPE); + Method DexFile_getStaticSizeOfDexFile = DexFile.getMethod("getStaticSizeOfDexFile"); + Object dexFile = DexFile_loadDex.invoke(null, filename, null, 0); + return (Long) DexFile_getStaticSizeOfDexFile.invoke(dexFile); + } + + private static void test(String resource) throws Exception { + String filename = System.getenv("DEX_LOCATION") + "/" + resource; + extractResource(resource, filename); + long size = getDexFileSize(filename); + System.out.println("Size for " + resource + ": " + size); + } + + public static void main(String[] args) throws Exception { + test("test1.dex"); + test("test2.dex"); + test("test-jar.jar"); + test("multi-jar.jar"); + } +} diff --git a/test/071-dexfile-get-static-size/test1.dex b/test/071-dexfile-get-static-size/test1.dex new file mode 100644 index 0000000000000000000000000000000000000000..84602d03c2c1692d5d5a77f8740732641c38933b GIT binary patch literal 1864 zcmYdEt>7{+Hf1>QjV5nqaV5nnZU}$Dx zVCZ0BVCZFGV3@?hz%ZSKfng2{1H(cV28LxU3=FGT7#KFNFfeRoVPM$B!oaYfg@NG+ z3j@PR76yiMEDQ{nSr`~@urM&(Wno}=#KOSvoP~km4GROqM-~Q#Z!8Q9zgZX<7+4t? zSXmhuxL6q&_*oekL|7RZBv~04UYgMop8lYxPOi-CcGn}LA=6xMtU z3=I4X3=AL|Bo2xaka__I2CzOsC=H4%khn0I&&VJGrA48%7?c)=(h^Ww5==8ONI_|7 zC@lk}Wudejl$M9m3SgRr{c6H04AX>BO21EqDL zv>ue!2h*$!22k1%N*h6GV<>F`rA?tUD4nt~urioK`4&*x5=vV^X=^ae&R_$kIT>uB zv>lYThtdvE+7U`SfoTo~XE4pp-~y#x!89aafZ~S<8rL8i#0TXgP~3y!l@~-XFfeGb zF)$ReLiB)y(pec8KxsvYA(KIefscU^3>g@5pyCArAk+95xNX#wy!<`FVp*boPGVlVzJF3y zYBHKwa7j^SUb-~{V=x0JNQWLsIRj%D17j2ebFPyCh%f{ZMj*nNfjQS1B<2hfa|Vey zgT+ih5~d8yxh^1C7m%zANY(`;>jIK>0n3^(Fz32~l(>PExPg?oft0v`l(>PExPg>- zf|PiIlz4)ac!HF8f|PiIlz4)bm@_cvdV$n=fz)|{)Omr_d4beCsBpK-j89EwR*03`+Hn1FFXJCNk1yB%xoC%^~br2}EgUUuw zJp`(EKyskE2$V-a`5aUqu`)o)SWuk=%V(f^iBW+87{+Hf6~Db0sLR=|$UaUXFc7ev4eOz9qi8;+?+w2Nni~0tN<#iZBx)1}F$% zVPKG9fQa)jFfcq|U|?WmU|3usY8WBnJ&X(tF3b!JYZw_AjxaJX zoMB{OIM2wyaEXzD;VL5o!wp6ThTDt`4EGor7~X;OGBGfSF)=VmGBGgdFflObGchn2 zF)=VWGBGfOGchn!GchnMU}9i6%*4R(jERAPiWLFle(dFz~Q|{K5zhXLc5dUkX40&cMgOW|NthSz^b)=)}P2 z%)scv!05)n=*ht7#lYtiT#{Ilne3LCnv+s&%^>8Hl~|UjpOcuEuJ50em6}|FDi&N) zl$n=q&A=GUAc(9}52THOF^qvRih(yVDOn-L&PGXHjgyy)K_oRdv!o=o$VwqAF*#eo z(A>=2z<@!iB(=E2z#zV$G$|)DIldU|iukn5yu_UNq{@=icoQQAWw07UtZIrg@{3C1 zO^g{7!HSHqDoV~sEQ*hSDlx{YBr~rh-oymz922Zca`N-i<4sH%)W9~GVpWrtlb=`; zZ(dpm z7})t3xEZvVw3#DVbl7wYJRVv)eUxu~9k|2J!$F)+&Ys1H2bB0E88M+0qa;IP1Irpv z9s}heP_hN3V;Bu9D?s@WR6c;p3y@ln8c-4j Date: Tue, 28 Nov 2017 12:37:13 +0000 Subject: [PATCH 082/226] ART: Minor refactoring of JNI stub compilation. Introduce JniCompiledMethod to avoid JNI compiler dependency on CompiledMethod. This is done in preparation for compiling JNI stubs in JIT as the CompiledMethod class should be used exclusively for AOT compilation. Test: m test-art-host-gtest Bug: 65574695 Change-Id: I1d047d4aebc55057efb7ed3d39ea65600f5fb6ab --- compiler/jni/quick/jni_compiler.cc | 33 ++++++-------- compiler/jni/quick/jni_compiler.h | 51 +++++++++++++++++++--- compiler/optimizing/optimizing_compiler.cc | 18 +++++--- 3 files changed, 71 insertions(+), 31 deletions(-) diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index b93b05cbd4..37f7d632ca 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -29,7 +29,6 @@ #include "base/macros.h" #include "calling_convention.h" #include "class_linker.h" -#include "compiled_method.h" #include "debug/dwarf/debug_frame_opcode_writer.h" #include "dex_file-inl.h" #include "driver/compiler_driver.h" @@ -115,10 +114,10 @@ static ThreadOffset GetJniEntrypointThreadOffset(JniEntrypoint whi // convention. // template -static CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver, - uint32_t access_flags, - uint32_t method_idx, - const DexFile& dex_file) { +static JniCompiledMethod ArtJniCompileMethodInternal(CompilerDriver* driver, + uint32_t access_flags, + uint32_t method_idx, + const DexFile& dex_file) { const bool is_native = (access_flags & kAccNative) != 0; CHECK(is_native); const bool is_static = (access_flags & kAccStatic) != 0; @@ -657,16 +656,12 @@ static CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver, MemoryRegion code(&managed_code[0], managed_code.size()); __ FinalizeInstructions(code); - return CompiledMethod::SwapAllocCompiledMethod(driver, - instruction_set, - ArrayRef(managed_code), - frame_size, - main_jni_conv->CoreSpillMask(), - main_jni_conv->FpSpillMask(), - /* method_info */ ArrayRef(), - /* vmap_table */ ArrayRef(), - ArrayRef(*jni_asm->cfi().data()), - ArrayRef()); + return JniCompiledMethod(instruction_set, + std::move(managed_code), + frame_size, + main_jni_conv->CoreSpillMask(), + main_jni_conv->FpSpillMask(), + ArrayRef(*jni_asm->cfi().data())); } // Copy a single parameter from the managed to the JNI calling convention. @@ -775,10 +770,10 @@ static void SetNativeParameter(JNIMacroAssembler* jni_asm, } } -CompiledMethod* ArtQuickJniCompileMethod(CompilerDriver* compiler, - uint32_t access_flags, - uint32_t method_idx, - const DexFile& dex_file) { +JniCompiledMethod ArtQuickJniCompileMethod(CompilerDriver* compiler, + uint32_t access_flags, + uint32_t method_idx, + const DexFile& dex_file) { if (Is64BitInstructionSet(compiler->GetInstructionSet())) { return ArtJniCompileMethodInternal( compiler, access_flags, method_idx, dex_file); diff --git a/compiler/jni/quick/jni_compiler.h b/compiler/jni/quick/jni_compiler.h index 3fcce55b5a..11419947a0 100644 --- a/compiler/jni/quick/jni_compiler.h +++ b/compiler/jni/quick/jni_compiler.h @@ -17,18 +17,55 @@ #ifndef ART_COMPILER_JNI_QUICK_JNI_COMPILER_H_ #define ART_COMPILER_JNI_QUICK_JNI_COMPILER_H_ -#include "compiler.h" -#include "dex_file.h" +#include + +#include "arch/instruction_set.h" +#include "base/array_ref.h" namespace art { +class ArtMethod; class CompilerDriver; -class CompiledMethod; +class DexFile; + +class JniCompiledMethod { + public: + JniCompiledMethod(InstructionSet instruction_set, + std::vector&& code, + uint32_t frame_size, + uint32_t core_spill_mask, + uint32_t fp_spill_mask, + ArrayRef cfi) + : instruction_set_(instruction_set), + code_(std::move(code)), + frame_size_(frame_size), + core_spill_mask_(core_spill_mask), + fp_spill_mask_(fp_spill_mask), + cfi_(cfi.begin(), cfi.end()) {} + + JniCompiledMethod(JniCompiledMethod&& other) = default; + ~JniCompiledMethod() = default; + + InstructionSet GetInstructionSet() const { return instruction_set_; } + ArrayRef GetCode() const { return ArrayRef(code_); } + uint32_t GetFrameSize() const { return frame_size_; } + uint32_t GetCoreSpillMask() const { return core_spill_mask_; } + uint32_t GetFpSpillMask() const { return fp_spill_mask_; } + ArrayRef GetCfi() const { return ArrayRef(cfi_); } + + private: + InstructionSet instruction_set_; + std::vector code_; + uint32_t frame_size_; + uint32_t core_spill_mask_; + uint32_t fp_spill_mask_; + std::vector cfi_; +}; -CompiledMethod* ArtQuickJniCompileMethod(CompilerDriver* compiler, - uint32_t access_flags, - uint32_t method_idx, - const DexFile& dex_file); +JniCompiledMethod ArtQuickJniCompileMethod(CompilerDriver* compiler, + uint32_t access_flags, + uint32_t method_idx, + const DexFile& dex_file); } // namespace art diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 095ca6372e..5a9e2c59b2 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -1133,12 +1133,20 @@ CompiledMethod* OptimizingCompiler::JniCompile(uint32_t access_flags, } } - CompiledMethod* compiled_method = ArtQuickJniCompileMethod(GetCompilerDriver(), - access_flags, - method_idx, - dex_file); + JniCompiledMethod jni_compiled_method = ArtQuickJniCompileMethod( + GetCompilerDriver(), access_flags, method_idx, dex_file); MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kCompiledNativeStub); - return compiled_method; + return CompiledMethod::SwapAllocCompiledMethod( + GetCompilerDriver(), + jni_compiled_method.GetInstructionSet(), + jni_compiled_method.GetCode(), + jni_compiled_method.GetFrameSize(), + jni_compiled_method.GetCoreSpillMask(), + jni_compiled_method.GetFpSpillMask(), + /* method_info */ ArrayRef(), + /* vmap_table */ ArrayRef(), + jni_compiled_method.GetCfi(), + /* patches */ ArrayRef()); } Compiler* CreateOptimizingCompiler(CompilerDriver* driver) { -- GitLab From 3417eaefe4e714c489a6fb0cb89b4810d81bdf4d Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 21 Sep 2017 18:14:28 +0100 Subject: [PATCH 083/226] JIT JNI stubs. Allow the JIT compiler to compile JNI stubs and make sure they can be collected once they are not in use anymore. Test: 667-jit-jni-stub Test: Pixel 2 XL boots. Test: m test-art-host-gtest Test: testrunner.py --host --jit Test: testrunner.py --target --jit Bug: 65574695 Change-Id: Idf81f50bcfa68c0c403ad2b49058be62b21b7b1f --- compiler/optimizing/optimizing_compiler.cc | 64 ++- runtime/arch/arm/quick_entrypoints_arm.S | 4 +- runtime/arch/arm64/quick_entrypoints_arm64.S | 2 +- runtime/arch/mips/quick_entrypoints_mips.S | 3 +- .../arch/mips64/quick_entrypoints_mips64.S | 3 +- runtime/arch/x86/quick_entrypoints_x86.S | 4 +- .../arch/x86_64/quick_entrypoints_x86_64.S | 4 +- runtime/art_method.cc | 28 +- runtime/art_method.h | 11 +- runtime/entrypoints/entrypoint_utils.cc | 4 +- .../quick/quick_trampoline_entrypoints.cc | 9 +- runtime/jit/jit.cc | 6 +- runtime/jit/jit_code_cache.cc | 493 ++++++++++++++---- runtime/jit/jit_code_cache.h | 39 +- runtime/jit/profile_saver.cc | 4 +- runtime/managed_stack-inl.h | 4 +- runtime/managed_stack.h | 80 ++- runtime/stack.cc | 59 ++- runtime/stack.h | 3 +- runtime/thread.cc | 4 +- runtime/thread.h | 9 +- test/655-jit-clinit/src/Main.java | 4 +- test/667-jit-jni-stub/expected.txt | 1 + test/667-jit-jni-stub/info.txt | 1 + test/667-jit-jni-stub/jit_jni_stub_test.cc | 63 +++ test/667-jit-jni-stub/run | 18 + test/667-jit-jni-stub/src/Main.java | 180 +++++++ test/Android.bp | 1 + test/common/runtime_state.cc | 25 +- 29 files changed, 945 insertions(+), 185 deletions(-) create mode 100644 test/667-jit-jni-stub/expected.txt create mode 100644 test/667-jit-jni-stub/info.txt create mode 100644 test/667-jit-jni-stub/jit_jni_stub_test.cc create mode 100755 test/667-jit-jni-stub/run create mode 100644 test/667-jit-jni-stub/src/Main.java diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 5a9e2c59b2..b6d3294037 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -1196,7 +1196,69 @@ bool OptimizingCompiler::JitCompile(Thread* self, Runtime* runtime = Runtime::Current(); ArenaAllocator allocator(runtime->GetJitArenaPool()); - ArenaStack arena_stack(Runtime::Current()->GetJitArenaPool()); + + if (UNLIKELY(method->IsNative())) { + JniCompiledMethod jni_compiled_method = ArtQuickJniCompileMethod( + GetCompilerDriver(), access_flags, method_idx, *dex_file); + ScopedNullHandle> roots; + ArenaSet> cha_single_implementation_list( + allocator.Adapter(kArenaAllocCHA)); + const void* code = code_cache->CommitCode( + self, + method, + /* stack_map_data */ nullptr, + /* method_info_data */ nullptr, + /* roots_data */ nullptr, + jni_compiled_method.GetFrameSize(), + jni_compiled_method.GetCoreSpillMask(), + jni_compiled_method.GetFpSpillMask(), + jni_compiled_method.GetCode().data(), + jni_compiled_method.GetCode().size(), + /* data_size */ 0u, + osr, + roots, + /* has_should_deoptimize_flag */ false, + cha_single_implementation_list); + if (code == nullptr) { + return false; + } + + const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); + if (compiler_options.GetGenerateDebugInfo()) { + const auto* method_header = reinterpret_cast(code); + const uintptr_t code_address = reinterpret_cast(method_header->GetCode()); + debug::MethodDebugInfo info = {}; + DCHECK(info.trampoline_name.empty()); + info.dex_file = dex_file; + info.class_def_index = class_def_idx; + info.dex_method_index = method_idx; + info.access_flags = access_flags; + info.code_item = code_item; + info.isa = jni_compiled_method.GetInstructionSet(); + info.deduped = false; + info.is_native_debuggable = compiler_options.GetNativeDebuggable(); + info.is_optimized = true; + info.is_code_address_text_relative = false; + info.code_address = code_address; + info.code_size = jni_compiled_method.GetCode().size(); + info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); + info.code_info = nullptr; + info.cfi = jni_compiled_method.GetCfi(); + std::vector elf_file = debug::WriteDebugElfFileForMethods( + GetCompilerDriver()->GetInstructionSet(), + GetCompilerDriver()->GetInstructionSetFeatures(), + ArrayRef(&info, 1)); + CreateJITCodeEntryForAddress(code_address, std::move(elf_file)); + } + + Runtime::Current()->GetJit()->AddMemoryUsage(method, allocator.BytesUsed()); + if (jit_logger != nullptr) { + jit_logger->WriteLog(code, jni_compiled_method.GetCode().size(), method); + } + return true; + } + + ArenaStack arena_stack(runtime->GetJitArenaPool()); CodeVectorAllocator code_allocator(&allocator); VariableSizedHandleScope handles(self); diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 6ff8dd60b8..6ec9c48b92 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -1783,7 +1783,9 @@ 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 sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] + ldr ip, [r9, #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 # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 280e5937c6..47efeb9200 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -2299,7 +2299,7 @@ ENTRY art_quick_generic_jni_trampoline .Lexception_in_native: // Move to x1 then sp to please assembler. ldr x1, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET] - mov sp, x1 + add sp, x1, #-1 // Remove the GenericJNI tag. .cfi_def_cfa_register sp # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 489c52c0d2..fc77a641b3 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -2283,7 +2283,8 @@ ENTRY art_quick_generic_jni_trampoline nop 2: - lw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) + lw $t0, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) + addiu $sp, $t0, -1 // Remove the GenericJNI tag. move $gp, $s3 # restore $gp from $s3 # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 98ffe6504a..3fb83d9232 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -2158,7 +2158,8 @@ ENTRY art_quick_generic_jni_trampoline dmtc1 $v0, $f0 # place return value to FP return value 1: - ld $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) + ld $t0, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) + daddiu $sp, $t0, -1 // Remove the GenericJNI tag. # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION END art_quick_generic_jni_trampoline diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 25716dc1bb..a46ceeba12 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1969,7 +1969,9 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline punpckldq %xmm1, %xmm0 ret .Lexception_in_native: - movl %fs:THREAD_TOP_QUICK_FRAME_OFFSET, %esp + pushl %fs:THREAD_TOP_QUICK_FRAME_OFFSET + addl LITERAL(-1), (%esp) // Remove the GenericJNI tag. + movl (%esp), %esp // Do a call to push a new save-all frame required by the runtime. call .Lexception_call .Lexception_call: diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 2c3da90f25..463e5a279f 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -1958,7 +1958,9 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline movq %rax, %xmm0 ret .Lexception_in_native: - movq %gs:THREAD_TOP_QUICK_FRAME_OFFSET, %rsp + pushq %gs:THREAD_TOP_QUICK_FRAME_OFFSET + addq LITERAL(-1), (%rsp) // Remove the GenericJNI tag. + movq (%rsp), %rsp CFI_DEF_CFA_REGISTER(rsp) // Do a call to push a new save-all frame required by the runtime. call .Lexception_call diff --git a/runtime/art_method.cc b/runtime/art_method.cc index fa0c501e31..bdbc4509f3 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -587,11 +587,6 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { CHECK(existing_entry_point != nullptr) << PrettyMethod() << "@" << this; ClassLinker* class_linker = runtime->GetClassLinker(); - if (class_linker->IsQuickGenericJniStub(existing_entry_point)) { - // The generic JNI does not have any method header. - return nullptr; - } - if (existing_entry_point == GetQuickProxyInvokeHandler()) { DCHECK(IsProxyMethod() && !IsConstructor()); // The proxy entry point does not have any method header. @@ -599,7 +594,8 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { } // Check whether the current entry point contains this pc. - if (!class_linker->IsQuickResolutionStub(existing_entry_point) && + if (!class_linker->IsQuickGenericJniStub(existing_entry_point) && + !class_linker->IsQuickResolutionStub(existing_entry_point) && !class_linker->IsQuickToInterpreterBridge(existing_entry_point)) { OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromEntryPoint(existing_entry_point); @@ -632,19 +628,13 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { OatFile::OatMethod oat_method = FindOatMethodFor(this, class_linker->GetImagePointerSize(), &found); if (!found) { - if (class_linker->IsQuickResolutionStub(existing_entry_point)) { - // We are running the generic jni stub, but the entry point of the method has not - // been updated yet. - DCHECK_EQ(pc, 0u) << "Should be a downcall"; - DCHECK(IsNative()); - return nullptr; - } - if (existing_entry_point == GetQuickInstrumentationEntryPoint()) { - // We are running the generic jni stub, but the method is being instrumented. - // NB We would normally expect the pc to be zero but we can have non-zero pc's if - // instrumentation is installed or removed during the call which is using the generic jni - // trampoline. - DCHECK(IsNative()); + if (IsNative()) { + // We are running the GenericJNI stub. The entrypoint may point + // to different entrypoints or to a JIT-compiled JNI stub. + DCHECK(class_linker->IsQuickGenericJniStub(existing_entry_point) || + class_linker->IsQuickResolutionStub(existing_entry_point) || + existing_entry_point == GetQuickInstrumentationEntryPoint() || + (jit != nullptr && jit->GetCodeCache()->ContainsPc(existing_entry_point))); return nullptr; } // Only for unit tests. diff --git a/runtime/art_method.h b/runtime/art_method.h index dca6f37254..6c3bb1074d 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -460,12 +460,11 @@ class ArtMethod FINAL { } ProfilingInfo* GetProfilingInfo(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_) { - // Don't do a read barrier in the DCHECK, as GetProfilingInfo is called in places - // where the declaring class is treated as a weak reference (accessing it with - // a read barrier would either prevent unloading the class, or crash the runtime if - // the GC wants to unload it). - DCHECK(!IsNative()); - if (UNLIKELY(IsProxyMethod())) { + // Don't do a read barrier in the DCHECK() inside GetAccessFlags() called by IsNative(), + // as GetProfilingInfo is called in places where the declaring class is treated as a weak + // reference (accessing it with a read barrier would either prevent unloading the class, + // or crash the runtime if the GC wants to unload it). + if (UNLIKELY(IsNative()) || UNLIKELY(IsProxyMethod())) { return nullptr; } return reinterpret_cast(GetDataPtrSize(pointer_size)); diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 2bf4372b1f..f3450da306 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -245,7 +245,7 @@ ArtMethod* GetCalleeSaveMethodCaller(ArtMethod** sp, CalleeSaveType type, bool d CallerAndOuterMethod GetCalleeSaveMethodCallerAndOuterMethod(Thread* self, CalleeSaveType type) { CallerAndOuterMethod result; ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame(); + ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrameKnownNotTagged(); auto outer_caller_and_pc = DoGetCalleeSaveMethodOuterCallerAndPc(sp, type); result.outer_method = outer_caller_and_pc.first; uintptr_t caller_pc = outer_caller_and_pc.second; @@ -256,7 +256,7 @@ CallerAndOuterMethod GetCalleeSaveMethodCallerAndOuterMethod(Thread* self, Calle ArtMethod* GetCalleeSaveOuterMethod(Thread* self, CalleeSaveType type) { ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame(); + ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrameKnownNotTagged(); return DoGetCalleeSaveMethodOuterCallerAndPc(sp, type).first; } diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 2496aa0f58..0a76cddf5e 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -31,6 +31,7 @@ #include "index_bss_mapping.h" #include "instrumentation.h" #include "interpreter/interpreter.h" +#include "jit/jit.h" #include "linear_alloc.h" #include "method_handles.h" #include "method_reference.h" @@ -2167,6 +2168,11 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod** // Note: We cannot walk the stack properly until fixed up below. 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(); @@ -2188,7 +2194,7 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod** } // Fix up managed-stack things in Thread. After this we can walk the stack. - self->SetTopOfStack(sp); + self->SetTopOfStackTagged(sp); self->VerifyStack(); @@ -2308,6 +2314,7 @@ extern "C" uint64_t artQuickGenericJniEndTrampoline(Thread* self, // anything that requires a mutator lock before that would cause problems as GC may have the // exclusive mutator lock and may be moving objects, etc. ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame(); + DCHECK(self->GetManagedStack()->GetTopQuickFrameTag()); uint32_t* sp32 = reinterpret_cast(sp); ArtMethod* called = *sp; uint32_t cookie = *(sp32 - 1); diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 953e195480..0d95bc6e64 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -643,7 +643,7 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ return; } - if (method->IsClassInitializer() || method->IsNative() || !method->IsCompilable()) { + if (method->IsClassInitializer() || !method->IsCompilable()) { // We do not want to compile such methods. return; } @@ -659,7 +659,8 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ count *= priority_thread_weight_; } int32_t new_count = starting_count + count; // int32 here to avoid wrap-around; - if (starting_count < warm_method_threshold_) { + // 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_) && (method->GetProfilingInfo(kRuntimePointerSize) == nullptr)) { bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false); @@ -696,6 +697,7 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ // 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)) { DCHECK(thread_pool_ != nullptr); thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr)); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 32205138bd..a5c167eee8 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -55,6 +55,107 @@ static constexpr int kProtCode = PROT_READ | PROT_EXEC; static constexpr size_t kCodeSizeLogThreshold = 50 * KB; static constexpr size_t kStackMapSizeLogThreshold = 50 * KB; +class JitCodeCache::JniStubKey { + public: + explicit JniStubKey(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) + : shorty_(method->GetShorty()), + is_static_(method->IsStatic()), + is_fast_native_(method->IsFastNative()), + is_critical_native_(method->IsCriticalNative()), + is_synchronized_(method->IsSynchronized()) { + DCHECK(!(is_fast_native_ && is_critical_native_)); + } + + bool operator<(const JniStubKey& rhs) const { + if (is_static_ != rhs.is_static_) { + return rhs.is_static_; + } + if (is_synchronized_ != rhs.is_synchronized_) { + return rhs.is_synchronized_; + } + if (is_fast_native_ != rhs.is_fast_native_) { + return rhs.is_fast_native_; + } + if (is_critical_native_ != rhs.is_critical_native_) { + return rhs.is_critical_native_; + } + return strcmp(shorty_, rhs.shorty_) < 0; + } + + // Update the shorty to point to another method's shorty. Call this function when removing + // the method that references the old shorty from JniCodeData and not removing the entire + // JniCodeData; the old shorty may become a dangling pointer when that method is unloaded. + void UpdateShorty(ArtMethod* method) const REQUIRES_SHARED(Locks::mutator_lock_) { + const char* shorty = method->GetShorty(); + DCHECK_STREQ(shorty_, shorty); + shorty_ = shorty; + } + + private: + // The shorty points to a DexFile data and may need to change + // to point to the same shorty in a different DexFile. + mutable const char* shorty_; + + const bool is_static_; + const bool is_fast_native_; + const bool is_critical_native_; + const bool is_synchronized_; +}; + +class JitCodeCache::JniStubData { + public: + JniStubData() : code_(nullptr), methods_() {} + + void SetCode(const void* code) { + DCHECK(code != nullptr); + code_ = code; + } + + const void* GetCode() const { + return code_; + } + + bool IsCompiled() const { + return GetCode() != nullptr; + } + + void AddMethod(ArtMethod* method) { + if (!ContainsElement(methods_, method)) { + methods_.push_back(method); + } + } + + const std::vector& GetMethods() const { + return methods_; + } + + void RemoveMethodsIn(const LinearAlloc& alloc) { + auto kept_end = std::remove_if( + methods_.begin(), + methods_.end(), + [&alloc](ArtMethod* method) { return alloc.ContainsUnsafe(method); }); + methods_.erase(kept_end, methods_.end()); + } + + bool RemoveMethod(ArtMethod* method) { + auto it = std::find(methods_.begin(), methods_.end(), method); + if (it != methods_.end()) { + methods_.erase(it); + return true; + } else { + return false; + } + } + + void MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) { + std::replace(methods_.begin(), methods_.end(), old_method, new_method); + } + + private: + const void* code_; + std::vector methods_; +}; + JitCodeCache* JitCodeCache::Create(size_t initial_capacity, size_t max_capacity, bool generate_debug_info, @@ -193,14 +294,36 @@ bool JitCodeCache::ContainsPc(const void* ptr) const { bool JitCodeCache::ContainsMethod(ArtMethod* method) { MutexLock mu(Thread::Current(), lock_); - for (auto& it : method_code_map_) { - if (it.second == method) { + if (UNLIKELY(method->IsNative())) { + auto it = jni_stubs_map_.find(JniStubKey(method)); + if (it != jni_stubs_map_.end() && + it->second.IsCompiled() && + ContainsElement(it->second.GetMethods(), method)) { return true; } + } else { + for (const auto& it : method_code_map_) { + if (it.second == method) { + return true; + } + } } return false; } +const void* JitCodeCache::GetJniStubCode(ArtMethod* method) { + DCHECK(method->IsNative()); + MutexLock mu(Thread::Current(), lock_); + auto it = jni_stubs_map_.find(JniStubKey(method)); + if (it != jni_stubs_map_.end()) { + JniStubData& data = it->second; + if (data.IsCompiled() && ContainsElement(data.GetMethods(), method)) { + return data.GetCode(); + } + } + return nullptr; +} + class ScopedCodeCacheWrite : ScopedTrace { public: explicit ScopedCodeCacheWrite(MemMap* code_map, bool only_for_tlb_shootdown = false) @@ -426,7 +549,9 @@ void JitCodeCache::FreeCode(const void* code_ptr) { // Notify native debugger that we are about to remove the code. // It does nothing if we are not using native debugger. DeleteJITCodeEntryForAddress(reinterpret_cast(code_ptr)); - FreeData(GetRootTable(code_ptr)); + if (OatQuickMethodHeader::FromCodePointer(code_ptr)->IsOptimized()) { + FreeData(GetRootTable(code_ptr)); + } // else this is a JNI stub without any data. FreeCode(reinterpret_cast(allocation)); } @@ -463,6 +588,16 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { // lead to a deadlock. { ScopedCodeCacheWrite scc(code_map_.get()); + for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) { + it->second.RemoveMethodsIn(alloc); + if (it->second.GetMethods().empty()) { + method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->second.GetCode())); + it = jni_stubs_map_.erase(it); + } else { + it->first.UpdateShorty(it->second.GetMethods().front()); + ++it; + } + } for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { if (alloc.ContainsUnsafe(it->second)) { method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->first)); @@ -572,7 +707,8 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, bool has_should_deoptimize_flag, const ArenaSet& cha_single_implementation_list) { - DCHECK(stack_map != nullptr); + DCHECK_NE(stack_map != nullptr, method->IsNative()); + DCHECK(!method->IsNative() || !osr); size_t alignment = GetInstructionSetAlignment(kRuntimeISA); // Ensure the header ends up at expected instruction alignment. size_t header_size = RoundUp(sizeof(OatQuickMethodHeader), alignment); @@ -596,8 +732,8 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, std::copy(code, code + code_size, code_ptr); method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); new (method_header) OatQuickMethodHeader( - code_ptr - stack_map, - code_ptr - method_info, + (stack_map != nullptr) ? code_ptr - stack_map : 0u, + (method_info != nullptr) ? code_ptr - method_info : 0u, frame_size_in_bytes, core_spill_mask, fp_spill_mask, @@ -652,24 +788,40 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, // possible that the compiled code is considered invalidated by some class linking, // but below we still make the compiled code valid for the method. MutexLock mu(self, lock_); - // Fill the root table before updating the entry point. - DCHECK_EQ(FromStackMapToRoots(stack_map), roots_data); - DCHECK_LE(roots_data, stack_map); - 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)); - } - method_code_map_.Put(code_ptr, method); - if (osr) { - number_of_osr_compilations_++; - osr_code_map_.Put(method, code_ptr); + if (UNLIKELY(method->IsNative())) { + DCHECK(stack_map == nullptr); + DCHECK(roots_data == nullptr); + auto it = jni_stubs_map_.find(JniStubKey(method)); + DCHECK(it != jni_stubs_map_.end()) + << "Entry inserted in NotifyCompilationOf() should be alive."; + JniStubData* data = &it->second; + DCHECK(ContainsElement(data->GetMethods(), method)) + << "Entry inserted in NotifyCompilationOf() should contain this method."; + data->SetCode(code_ptr); + instrumentation::Instrumentation* instrum = Runtime::Current()->GetInstrumentation(); + for (ArtMethod* m : data->GetMethods()) { + instrum->UpdateMethodsCode(m, method_header->GetEntryPoint()); + } } else { - Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( - method, method_header->GetEntryPoint()); + // Fill the root table before updating the entry point. + DCHECK_EQ(FromStackMapToRoots(stack_map), roots_data); + DCHECK_LE(roots_data, stack_map); + 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)); + } + method_code_map_.Put(code_ptr, method); + if (osr) { + number_of_osr_compilations_++; + osr_code_map_.Put(method, code_ptr); + } else { + Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( + method, method_header->GetEntryPoint()); + } } if (collection_in_progress_) { // We need to update the live bitmap if there is a GC to ensure it sees this new @@ -703,45 +855,18 @@ size_t JitCodeCache::CodeCacheSize() { } bool JitCodeCache::RemoveMethod(ArtMethod* method, bool release_memory) { - MutexLock mu(Thread::Current(), lock_); - if (method->IsNative()) { - return false; - } + // This function is used only for testing and only with non-native methods. + CHECK(!method->IsNative()); - bool in_cache = false; - { - ScopedCodeCacheWrite ccw(code_map_.get()); - for (auto code_iter = method_code_map_.begin(); code_iter != method_code_map_.end();) { - if (code_iter->second == method) { - if (release_memory) { - FreeCode(code_iter->first); - } - code_iter = method_code_map_.erase(code_iter); - in_cache = true; - continue; - } - ++code_iter; - } - } + MutexLock mu(Thread::Current(), lock_); - bool osr = false; - auto code_map = osr_code_map_.find(method); - if (code_map != osr_code_map_.end()) { - osr_code_map_.erase(code_map); - osr = true; - } + bool osr = osr_code_map_.find(method) != osr_code_map_.end(); + bool in_cache = RemoveMethodLocked(method, release_memory); if (!in_cache) { return false; } - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - if (info != nullptr) { - auto profile = std::find(profiling_infos_.begin(), profiling_infos_.end(), info); - DCHECK(profile != profiling_infos_.end()); - profiling_infos_.erase(profile); - } - method->SetProfilingInfo(nullptr); method->ClearCounter(); Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( method, GetQuickToInterpreterBridge()); @@ -753,34 +878,58 @@ bool JitCodeCache::RemoveMethod(ArtMethod* method, bool release_memory) { return true; } +bool JitCodeCache::RemoveMethodLocked(ArtMethod* method, bool release_memory) { + if (LIKELY(!method->IsNative())) { + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + if (info != nullptr) { + RemoveElement(profiling_infos_, info); + } + method->SetProfilingInfo(nullptr); + } + + bool in_cache = false; + ScopedCodeCacheWrite ccw(code_map_.get()); + if (UNLIKELY(method->IsNative())) { + auto it = jni_stubs_map_.find(JniStubKey(method)); + if (it != jni_stubs_map_.end() && it->second.RemoveMethod(method)) { + in_cache = true; + if (it->second.GetMethods().empty()) { + if (release_memory) { + FreeCode(it->second.GetCode()); + } + jni_stubs_map_.erase(it); + } else { + it->first.UpdateShorty(it->second.GetMethods().front()); + } + } + } else { + for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { + if (it->second == method) { + in_cache = true; + if (release_memory) { + FreeCode(it->first); + } + it = method_code_map_.erase(it); + } else { + ++it; + } + } + + auto osr_it = osr_code_map_.find(method); + if (osr_it != osr_code_map_.end()) { + osr_code_map_.erase(osr_it); + } + } + + return in_cache; +} + // This notifies the code cache that the given method has been redefined and that it should remove // any cached information it has on the method. All threads must be suspended before calling this // method. The compiled code for the method (if there is any) must not be in any threads call stack. void JitCodeCache::NotifyMethodRedefined(ArtMethod* method) { MutexLock mu(Thread::Current(), lock_); - if (method->IsNative()) { - return; - } - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - if (info != nullptr) { - auto profile = std::find(profiling_infos_.begin(), profiling_infos_.end(), info); - DCHECK(profile != profiling_infos_.end()); - profiling_infos_.erase(profile); - } - method->SetProfilingInfo(nullptr); - ScopedCodeCacheWrite ccw(code_map_.get()); - for (auto code_iter = method_code_map_.begin(); code_iter != method_code_map_.end();) { - if (code_iter->second == method) { - FreeCode(code_iter->first); - code_iter = method_code_map_.erase(code_iter); - continue; - } - ++code_iter; - } - auto code_map = osr_code_map_.find(method); - if (code_map != osr_code_map_.end()) { - osr_code_map_.erase(code_map); - } + RemoveMethodLocked(method, /* release_memory */ true); } // This invalidates old_method. Once this function returns one can no longer use old_method to @@ -790,11 +939,15 @@ void JitCodeCache::NotifyMethodRedefined(ArtMethod* method) { // shouldn't be used since it is no longer logically in the jit code cache. // TODO We should add DCHECKS that validate that the JIT is paused when this method is entered. void JitCodeCache::MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) { - // Native methods have no profiling info and need no special handling from the JIT code cache. + MutexLock mu(Thread::Current(), lock_); if (old_method->IsNative()) { + // Update methods in jni_stubs_map_. + for (auto& entry : jni_stubs_map_) { + JniStubData& data = entry.second; + data.MoveObsoleteMethod(old_method, new_method); + } return; } - MutexLock mu(Thread::Current(), lock_); // Update ProfilingInfo to the new one and remove it from the old_method. if (old_method->GetProfilingInfo(kRuntimePointerSize) != nullptr) { DCHECK_EQ(old_method->GetProfilingInfo(kRuntimePointerSize)->GetMethod(), old_method); @@ -936,7 +1089,7 @@ class MarkCodeClosure FINAL : public Closure { // its stack frame, it is not the method owning return_pc_. We just pass null to // LookupMethodHeader: the method is only checked against in debug builds. OatQuickMethodHeader* method_header = - code_cache_->LookupMethodHeader(frame.return_pc_, nullptr); + code_cache_->LookupMethodHeader(frame.return_pc_, /* method */ nullptr); if (method_header != nullptr) { const void* code = method_header->GetCode(); CHECK(code_cache_->GetLiveBitmap()->Test(FromCodeToAllocation(code))); @@ -1089,7 +1242,7 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { const void* entry_point = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); if (ContainsPc(entry_point)) { info->SetSavedEntryPoint(entry_point); - // Don't call Instrumentation::UpdateMethods, as it can check the declaring + // Don't call Instrumentation::UpdateMethodsCode(), as it can check the declaring // class of the method. We may be concurrently running a GC which makes accessing // the class unsafe. We know it is OK to bypass the instrumentation as we've just // checked that the current entry point is JIT compiled code. @@ -1098,6 +1251,25 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { } DCHECK(CheckLiveCompiledCodeHasProfilingInfo()); + + // Change entry points of native methods back to the GenericJNI entrypoint. + for (const auto& entry : jni_stubs_map_) { + const JniStubData& data = entry.second; + if (!data.IsCompiled()) { + continue; + } + // Make sure a single invocation of the GenericJNI trampoline tries to recompile. + uint16_t new_counter = Runtime::Current()->GetJit()->HotMethodThreshold() - 1u; + const OatQuickMethodHeader* method_header = + OatQuickMethodHeader::FromCodePointer(data.GetCode()); + for (ArtMethod* method : data.GetMethods()) { + if (method->GetEntryPointFromQuickCompiledCode() == method_header->GetEntryPoint()) { + // Don't call Instrumentation::UpdateMethodsCode(), same as for normal methods above. + method->SetCounter(new_counter); + method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub()); + } + } + } } live_bitmap_.reset(nullptr); NotifyCollectionDone(self); @@ -1113,13 +1285,22 @@ void JitCodeCache::RemoveUnmarkedCode(Thread* self) { MutexLock mu(self, lock_); ScopedCodeCacheWrite scc(code_map_.get()); // 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; + if (!data->IsCompiled() || GetLiveBitmap()->Test(FromCodeToAllocation(data->GetCode()))) { + ++it; + } else { + method_headers.insert(OatQuickMethodHeader::FromCodePointer(data->GetCode())); + it = jni_stubs_map_.erase(it); + } + } for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { const void* code_ptr = it->first; uintptr_t allocation = FromCodeToAllocation(code_ptr); if (GetLiveBitmap()->Test(allocation)) { ++it; } else { - method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->first)); + method_headers.insert(OatQuickMethodHeader::FromCodePointer(code_ptr)); it = method_code_map_.erase(it); } } @@ -1158,6 +1339,17 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { // an entry point is either: // - an osr compiled code, that will be removed if not in a thread call stack. // - discarded compiled code, that will be removed if not in a thread call stack. + for (const auto& entry : jni_stubs_map_) { + const JniStubData& data = entry.second; + const void* code_ptr = data.GetCode(); + const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + for (ArtMethod* method : data.GetMethods()) { + if (method_header->GetEntryPoint() == method->GetEntryPointFromQuickCompiledCode()) { + GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr)); + break; + } + } + } for (const auto& it : method_code_map_) { ArtMethod* method = it.second; const void* code_ptr = it.first; @@ -1237,19 +1429,51 @@ OatQuickMethodHeader* JitCodeCache::LookupMethodHeader(uintptr_t pc, ArtMethod* return nullptr; } - MutexLock mu(Thread::Current(), lock_); - if (method_code_map_.empty()) { - return nullptr; + if (!kIsDebugBuild) { + // Called with null `method` only from MarkCodeClosure::Run() in debug build. + CHECK(method != nullptr); } - auto it = method_code_map_.lower_bound(reinterpret_cast(pc)); - --it; - const void* code_ptr = it->first; - OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); - if (!method_header->Contains(pc)) { - return nullptr; + MutexLock mu(Thread::Current(), lock_); + OatQuickMethodHeader* method_header = nullptr; + ArtMethod* found_method = nullptr; // Only for DCHECK(), not for JNI stubs. + if (method != nullptr && UNLIKELY(method->IsNative())) { + auto it = jni_stubs_map_.find(JniStubKey(method)); + if (it == jni_stubs_map_.end() || !ContainsElement(it->second.GetMethods(), method)) { + return nullptr; + } + const void* code_ptr = it->second.GetCode(); + method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + if (!method_header->Contains(pc)) { + return nullptr; + } + } else { + auto it = method_code_map_.lower_bound(reinterpret_cast(pc)); + if (it != method_code_map_.begin()) { + --it; + const void* code_ptr = it->first; + if (OatQuickMethodHeader::FromCodePointer(code_ptr)->Contains(pc)) { + method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + found_method = it->second; + } + } + if (method_header == nullptr && method == nullptr) { + // Scan all compiled JNI stubs as well. This slow search is used only + // for checks in debug build, for release builds the `method` is not null. + for (auto&& entry : jni_stubs_map_) { + const JniStubData& data = entry.second; + if (data.IsCompiled() && + OatQuickMethodHeader::FromCodePointer(data.GetCode())->Contains(pc)) { + method_header = OatQuickMethodHeader::FromCodePointer(data.GetCode()); + } + } + } + if (method_header == nullptr) { + return nullptr; + } } - if (kIsDebugBuild && method != nullptr) { + + if (kIsDebugBuild && method != nullptr && !method->IsNative()) { // When we are walking the stack to redefine classes and creating obsolete methods it is // possible that we might have updated the method_code_map by making this method obsolete in a // previous frame. Therefore we should just check that the non-obsolete version of this method @@ -1258,9 +1482,9 @@ OatQuickMethodHeader* JitCodeCache::LookupMethodHeader(uintptr_t pc, ArtMethod* // occur when we are in the process of allocating and setting up obsolete methods. Otherwise // method and it->second should be identical. (See openjdkjvmti/ti_redefine.cc for more // information.) - DCHECK_EQ(it->second->GetNonObsoleteMethod(), method->GetNonObsoleteMethod()) + DCHECK_EQ(found_method->GetNonObsoleteMethod(), method->GetNonObsoleteMethod()) << ArtMethod::PrettyMethod(method->GetNonObsoleteMethod()) << " " - << ArtMethod::PrettyMethod(it->second->GetNonObsoleteMethod()) << " " + << ArtMethod::PrettyMethod(found_method->GetNonObsoleteMethod()) << " " << std::hex << pc; } return method_header; @@ -1449,21 +1673,51 @@ bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr return false; } - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - if (info == nullptr) { - VLOG(jit) << method->PrettyMethod() << " needs a ProfilingInfo to be compiled"; - // Because the counter is not atomic, there are some rare cases where we may not hit the - // threshold for creating the ProfilingInfo. Reset the counter now to "correct" this. - ClearMethodCounter(method, /*was_warm*/ false); - return false; - } + if (UNLIKELY(method->IsNative())) { + JniStubKey key(method); + auto it = jni_stubs_map_.find(key); + bool new_compilation = false; + if (it == jni_stubs_map_.end()) { + // Create a new entry to mark the stub as being compiled. + it = jni_stubs_map_.Put(key, JniStubData{}); + new_compilation = true; + } + JniStubData* data = &it->second; + data->AddMethod(method); + if (data->IsCompiled()) { + OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(data->GetCode()); + const void* entrypoint = method_header->GetEntryPoint(); + // Update also entrypoints of other methods held by the JniStubData. + // We could simply update the entrypoint of `method` but if the last JIT GC has + // changed these entrypoints to GenericJNI in preparation for a full GC, we may + // as well change them back as this stub shall not be collected anyway and this + // can avoid a few expensive GenericJNI calls. + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + for (ArtMethod* m : data->GetMethods()) { + instrumentation->UpdateMethodsCode(m, entrypoint); + } + if (collection_in_progress_) { + GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(data->GetCode())); + } + } + return new_compilation; + } else { + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + if (info == nullptr) { + VLOG(jit) << method->PrettyMethod() << " needs a ProfilingInfo to be compiled"; + // Because the counter is not atomic, there are some rare cases where we may not hit the + // threshold for creating the ProfilingInfo. Reset the counter now to "correct" this. + ClearMethodCounter(method, /*was_warm*/ false); + return false; + } - if (info->IsMethodBeingCompiled(osr)) { - return false; - } + if (info->IsMethodBeingCompiled(osr)) { + return false; + } - info->SetIsMethodBeingCompiled(true, osr); - return true; + info->SetIsMethodBeingCompiled(true, osr); + return true; + } } ProfilingInfo* JitCodeCache::NotifyCompilerUse(ArtMethod* method, Thread* self) { @@ -1485,10 +1739,23 @@ void JitCodeCache::DoneCompilerUse(ArtMethod* method, Thread* self) { info->DecrementInlineUse(); } -void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self ATTRIBUTE_UNUSED, bool osr) { - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - DCHECK(info->IsMethodBeingCompiled(osr)); - info->SetIsMethodBeingCompiled(false, osr); +void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self, bool osr) { + DCHECK_EQ(Thread::Current(), self); + MutexLock mu(self, lock_); + if (UNLIKELY(method->IsNative())) { + auto it = jni_stubs_map_.find(JniStubKey(method)); + DCHECK(it != jni_stubs_map_.end()); + JniStubData* data = &it->second; + DCHECK(ContainsElement(data->GetMethods(), method)); + if (UNLIKELY(!data->IsCompiled())) { + // Failed to compile; the JNI compiler never fails, but the cache may be full. + jni_stubs_map_.erase(it); // Remove the entry added in NotifyCompilationOf(). + } // else CommitCodeInternal() updated entrypoints of all methods in the JniStubData. + } else { + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + DCHECK(info->IsMethodBeingCompiled(osr)); + info->SetIsMethodBeingCompiled(false, osr); + } } size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) { @@ -1498,6 +1765,7 @@ size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) { void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method, const OatQuickMethodHeader* header) { + DCHECK(!method->IsNative()); ProfilingInfo* profiling_info = method->GetProfilingInfo(kRuntimePointerSize); if ((profiling_info != nullptr) && (profiling_info->GetSavedEntryPoint() == header->GetEntryPoint())) { @@ -1553,6 +1821,7 @@ void JitCodeCache::Dump(std::ostream& os) { os << "Current JIT code cache size: " << PrettySize(used_memory_for_code_) << "\n" << "Current JIT data cache size: " << PrettySize(used_memory_for_data_) << "\n" << "Current JIT capacity: " << PrettySize(current_capacity_) << "\n" + << "Current number of JIT JNI stub entries: " << jni_stubs_map_.size() << "\n" << "Current number of JIT code cache entries: " << method_code_map_.size() << "\n" << "Total number of JIT compilations: " << number_of_compilations_ << "\n" << "Total number of JIT compilations for on stack replacement: " diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 46a408590b..fc011ddb96 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -35,9 +35,23 @@ template class Handle; class LinearAlloc; class InlineCache; class IsMarkedVisitor; +class JitJniStubTestHelper; class OatQuickMethodHeader; struct ProfileMethodInfo; class ProfilingInfo; +class Thread; + +namespace gc { +namespace accounting { +template class MemoryRangeBitmap; +} // namespace accounting +} // namespace gc + +namespace mirror { +class Class; +class Object; +template class ObjectArray; +} // namespace mirror namespace gc { namespace accounting { @@ -137,6 +151,9 @@ class JitCodeCache { // Return true if the code cache contains this method. bool ContainsMethod(ArtMethod* method) REQUIRES(!lock_); + // Return the code pointer for a JNI-compiled stub if the method is in the cache, null otherwise. + const void* GetJniStubCode(ArtMethod* method) REQUIRES(!lock_); + // Allocate a region of data that contain `size` bytes, and potentially space // for storing `number_of_roots` roots. Returns null if there is no more room. // Return the number of bytes allocated. @@ -160,11 +177,6 @@ class JitCodeCache { return live_bitmap_.get(); } - // Return whether we should do a full collection given the current state of the cache. - bool ShouldDoFullCollection() - REQUIRES(lock_) - REQUIRES_SHARED(Locks::mutator_lock_); - // Perform a collection on the code cache. void GarbageCollectCache(Thread* self) REQUIRES(!lock_) @@ -296,6 +308,12 @@ class JitCodeCache { REQUIRES(!lock_) REQUIRES(!Locks::cha_lock_); + // Removes method from the cache. The caller must ensure that all threads + // are suspended and the method should not be in any thread's stack. + bool RemoveMethodLocked(ArtMethod* method, bool release_memory) + REQUIRES(lock_) + REQUIRES(Locks::mutator_lock_); + // Free in the mspace allocations for `code_ptr`. void FreeCode(const void* code_ptr) REQUIRES(lock_); @@ -315,6 +333,11 @@ class JitCodeCache { // Set the footprint limit of the code cache. void SetFootprintLimit(size_t new_footprint) REQUIRES(lock_); + // Return whether we should do a full collection given the current state of the cache. + bool ShouldDoFullCollection() + REQUIRES(lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + void DoCollection(Thread* self, bool collect_profiling_info) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -341,6 +364,9 @@ class JitCodeCache { REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); + class JniStubKey; + class JniStubData; + // Lock for guarding allocations, collections, and the method_code_map_. Mutex lock_; // Condition to wait on during collection. @@ -357,6 +383,8 @@ class JitCodeCache { void* data_mspace_ GUARDED_BY(lock_); // Bitmap for collecting code and data. std::unique_ptr live_bitmap_; + // Holds compiled code associated with the shorty for a JNI stub. + SafeMap jni_stubs_map_ GUARDED_BY(lock_); // Holds compiled code associated to the ArtMethod. SafeMap method_code_map_ GUARDED_BY(lock_); // Holds osr compiled code associated to the ArtMethod. @@ -418,6 +446,7 @@ class JitCodeCache { // Condition to wait on for accessing inline caches. ConditionVariable inline_cache_cond_ GUARDED_BY(lock_); + friend class art::JitJniStubTestHelper; DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache); }; diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 01853de403..acbc6e63a4 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -357,8 +357,8 @@ static void SampleClassesAndExecutedMethods(pthread_t profiler_pthread, sampled_methods->AddReference(method.GetDexFile(), method.GetDexMethodIndex()); } } else { - CHECK_EQ(method.GetCounter(), 0u) << method.PrettyMethod() - << " access_flags=" << method.GetAccessFlags(); + // We do not record native methods. Once we AOT-compile the app, all native + // methods shall have their thunks compiled. } } } diff --git a/runtime/managed_stack-inl.h b/runtime/managed_stack-inl.h index 689dd8009a..678be8e098 100644 --- a/runtime/managed_stack-inl.h +++ b/runtime/managed_stack-inl.h @@ -24,7 +24,7 @@ namespace art { inline ShadowFrame* ManagedStack::PushShadowFrame(ShadowFrame* new_top_frame) { - DCHECK(top_quick_frame_ == nullptr); + DCHECK(!HasTopQuickFrame()); ShadowFrame* old_frame = top_shadow_frame_; top_shadow_frame_ = new_top_frame; new_top_frame->SetLink(old_frame); @@ -32,7 +32,7 @@ inline ShadowFrame* ManagedStack::PushShadowFrame(ShadowFrame* new_top_frame) { } inline ShadowFrame* ManagedStack::PopShadowFrame() { - DCHECK(top_quick_frame_ == nullptr); + DCHECK(!HasTopQuickFrame()); CHECK(top_shadow_frame_ != nullptr); ShadowFrame* frame = top_shadow_frame_; top_shadow_frame_ = frame->GetLink(); diff --git a/runtime/managed_stack.h b/runtime/managed_stack.h index 4f1984d55a..07078ecb13 100644 --- a/runtime/managed_stack.h +++ b/runtime/managed_stack.h @@ -24,6 +24,7 @@ #include "base/logging.h" #include "base/macros.h" #include "base/mutex.h" +#include "base/bit_utils.h" namespace art { @@ -42,7 +43,9 @@ template class StackReference; class PACKED(4) ManagedStack { public: ManagedStack() - : top_quick_frame_(nullptr), link_(nullptr), top_shadow_frame_(nullptr) {} + : tagged_top_quick_frame_(TaggedTopQuickFrame::CreateNotTagged(nullptr)), + link_(nullptr), + top_shadow_frame_(nullptr) {} void PushManagedStackFragment(ManagedStack* fragment) { // Copy this top fragment into given fragment. @@ -63,17 +66,36 @@ class PACKED(4) ManagedStack { return link_; } + ArtMethod** GetTopQuickFrameKnownNotTagged() const { + return tagged_top_quick_frame_.GetSpKnownNotTagged(); + } + ArtMethod** GetTopQuickFrame() const { - return top_quick_frame_; + return tagged_top_quick_frame_.GetSp(); + } + + bool GetTopQuickFrameTag() const { + return tagged_top_quick_frame_.GetTag(); + } + + bool HasTopQuickFrame() const { + return tagged_top_quick_frame_.GetTaggedSp() != 0u; } void SetTopQuickFrame(ArtMethod** top) { DCHECK(top_shadow_frame_ == nullptr); - top_quick_frame_ = top; + DCHECK_ALIGNED(top, 4u); + tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateNotTagged(top); } - static size_t TopQuickFrameOffset() { - return OFFSETOF_MEMBER(ManagedStack, top_quick_frame_); + void SetTopQuickFrameTagged(ArtMethod** top) { + DCHECK(top_shadow_frame_ == nullptr); + DCHECK_ALIGNED(top, 4u); + tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateTagged(top); + } + + static size_t TaggedTopQuickFrameOffset() { + return OFFSETOF_MEMBER(ManagedStack, tagged_top_quick_frame_); } ALWAYS_INLINE ShadowFrame* PushShadowFrame(ShadowFrame* new_top_frame); @@ -83,8 +105,12 @@ class PACKED(4) ManagedStack { return top_shadow_frame_; } + bool HasTopShadowFrame() const { + return GetTopShadowFrame() != nullptr; + } + void SetTopShadowFrame(ShadowFrame* top) { - DCHECK(top_quick_frame_ == nullptr); + DCHECK_EQ(tagged_top_quick_frame_.GetTaggedSp(), 0u); top_shadow_frame_ = top; } @@ -97,7 +123,47 @@ class PACKED(4) ManagedStack { bool ShadowFramesContain(StackReference* shadow_frame_entry) const; private: - ArtMethod** top_quick_frame_; + // Encodes the top quick frame (which must be at least 4-byte aligned) + // and a flag that marks the GenericJNI trampoline. + class TaggedTopQuickFrame { + public: + static TaggedTopQuickFrame CreateNotTagged(ArtMethod** sp) { + DCHECK_ALIGNED(sp, 4u); + return TaggedTopQuickFrame(reinterpret_cast(sp)); + } + + static TaggedTopQuickFrame CreateTagged(ArtMethod** sp) { + DCHECK_ALIGNED(sp, 4u); + return TaggedTopQuickFrame(reinterpret_cast(sp) | 1u); + } + + // Get SP known to be not tagged and non-null. + ArtMethod** GetSpKnownNotTagged() const { + DCHECK(!GetTag()); + DCHECK_NE(tagged_sp_, 0u); + return reinterpret_cast(tagged_sp_); + } + + ArtMethod** GetSp() const { + return reinterpret_cast(tagged_sp_ & ~static_cast(1u)); + } + + bool GetTag() const { + return (tagged_sp_ & 1u) != 0u; + } + + uintptr_t GetTaggedSp() const { + return tagged_sp_; + } + + private: + explicit TaggedTopQuickFrame(uintptr_t tagged_sp) : tagged_sp_(tagged_sp) { } + + uintptr_t tagged_sp_; + }; + static_assert(sizeof(TaggedTopQuickFrame) == sizeof(uintptr_t), "TaggedTopQuickFrame size check"); + + TaggedTopQuickFrame tagged_top_quick_frame_; ManagedStack* link_; ShadowFrame* top_shadow_frame_; }; diff --git a/runtime/stack.cc b/runtime/stack.cc index ab9fb0d73f..5ad1f7c9c5 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -735,12 +735,19 @@ QuickMethodFrameInfo StackVisitor::GetCurrentQuickFrameInfo() const { return runtime->GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); } - // The only remaining case is if the method is native and uses the generic JNI stub. + // The only remaining case is if the method is native and uses the generic JNI stub, + // called either directly or through some (resolution, instrumentation) trampoline. DCHECK(method->IsNative()); - ClassLinker* class_linker = runtime->GetClassLinker(); - const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(method, - kRuntimePointerSize); - DCHECK(class_linker->IsQuickGenericJniStub(entry_point)) << method->PrettyMethod(); + if (kIsDebugBuild) { + ClassLinker* class_linker = runtime->GetClassLinker(); + const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(method, + kRuntimePointerSize); + CHECK(class_linker->IsQuickGenericJniStub(entry_point) || + // The current entrypoint (after filtering out trampolines) may have changed + // from GenericJNI to JIT-compiled stub since we have entered this frame. + (runtime->GetJit() != nullptr && + runtime->GetJit()->GetCodeCache()->ContainsPc(entry_point))) << method->PrettyMethod(); + } // Generic JNI frame. uint32_t handle_refs = GetNumberOfReferenceArgsWithoutReceiver(method) + 1; size_t scope_size = HandleScope::SizeOf(handle_refs); @@ -776,8 +783,48 @@ void StackVisitor::WalkStack(bool include_transitions) { // Can't be both a shadow and a quick fragment. DCHECK(current_fragment->GetTopShadowFrame() == nullptr); ArtMethod* method = *cur_quick_frame_; + DCHECK(method != nullptr); + bool header_retrieved = false; + if (method->IsNative()) { + // We do not have a PC for the first frame, so we cannot simply use + // ArtMethod::GetOatQuickMethodHeader() as we're unable to distinguish there + // between GenericJNI frame and JIT-compiled JNI stub; the entrypoint may have + // changed since the frame was entered. The top quick frame tag indicates + // GenericJNI here, otherwise it's either AOT-compiled or JNI-compiled JNI stub. + if (UNLIKELY(current_fragment->GetTopQuickFrameTag())) { + // The generic JNI does not have any method header. + cur_oat_quick_method_header_ = nullptr; + } else { + const void* existing_entry_point = method->GetEntryPointFromQuickCompiledCode(); + CHECK(existing_entry_point != nullptr); + Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + // Check whether we can quickly get the header from the current entrypoint. + if (!class_linker->IsQuickGenericJniStub(existing_entry_point) && + !class_linker->IsQuickResolutionStub(existing_entry_point) && + existing_entry_point != GetQuickInstrumentationEntryPoint()) { + cur_oat_quick_method_header_ = + OatQuickMethodHeader::FromEntryPoint(existing_entry_point); + } else { + const void* code = method->GetOatMethodQuickCode(class_linker->GetImagePointerSize()); + if (code != nullptr) { + cur_oat_quick_method_header_ = OatQuickMethodHeader::FromEntryPoint(code); + } else { + // This must be a JITted JNI stub frame. + CHECK(runtime->GetJit() != nullptr); + code = runtime->GetJit()->GetCodeCache()->GetJniStubCode(method); + CHECK(code != nullptr) << method->PrettyMethod(); + cur_oat_quick_method_header_ = OatQuickMethodHeader::FromCodePointer(code); + } + } + } + header_retrieved = true; + } while (method != nullptr) { - cur_oat_quick_method_header_ = method->GetOatQuickMethodHeader(cur_quick_frame_pc_); + if (!header_retrieved) { + cur_oat_quick_method_header_ = method->GetOatQuickMethodHeader(cur_quick_frame_pc_); + } + header_retrieved = false; // Force header retrieval in next iteration. SanityCheckFrame(); if ((walk_kind_ == StackWalkKind::kIncludeInlinedFrames) diff --git a/runtime/stack.h b/runtime/stack.h index bd6204f8d2..a16930bba0 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -140,8 +140,7 @@ class StackVisitor { }; template - void WalkStack(bool include_transitions = false) - REQUIRES_SHARED(Locks::mutator_lock_); + void WalkStack(bool include_transitions = false) REQUIRES_SHARED(Locks::mutator_lock_); Thread* GetThread() const { return thread_; diff --git a/runtime/thread.cc b/runtime/thread.cc index 712eabc888..bec1c908ad 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1884,9 +1884,7 @@ static bool ShouldShowNativeStack(const Thread* thread) } // Threads with no managed stack frames should be shown. - const ManagedStack* managed_stack = thread->GetManagedStack(); - if (managed_stack == nullptr || (managed_stack->GetTopQuickFrame() == nullptr && - managed_stack->GetTopShadowFrame() == nullptr)) { + if (!thread->HasManagedStack()) { return true; } diff --git a/runtime/thread.h b/runtime/thread.h index 39be66d5c2..0803975d26 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -474,13 +474,16 @@ class Thread { tlsPtr_.managed_stack.SetTopQuickFrame(top_method); } + void SetTopOfStackTagged(ArtMethod** top_method) { + tlsPtr_.managed_stack.SetTopQuickFrameTagged(top_method); + } + void SetTopOfShadowStack(ShadowFrame* top) { tlsPtr_.managed_stack.SetTopShadowFrame(top); } bool HasManagedStack() const { - return (tlsPtr_.managed_stack.GetTopQuickFrame() != nullptr) || - (tlsPtr_.managed_stack.GetTopShadowFrame() != nullptr); + return tlsPtr_.managed_stack.HasTopQuickFrame() || tlsPtr_.managed_stack.HasTopShadowFrame(); } // If 'msg' is null, no detail message is set. @@ -833,7 +836,7 @@ class Thread { static ThreadOffset TopOfManagedStackOffset() { return ThreadOffsetFromTlsPtr( OFFSETOF_MEMBER(tls_ptr_sized_values, managed_stack) + - ManagedStack::TopQuickFrameOffset()); + ManagedStack::TaggedTopQuickFrameOffset()); } const ManagedStack* GetManagedStack() const { diff --git a/test/655-jit-clinit/src/Main.java b/test/655-jit-clinit/src/Main.java index 44b315478f..2fb8f2a86e 100644 --- a/test/655-jit-clinit/src/Main.java +++ b/test/655-jit-clinit/src/Main.java @@ -23,7 +23,7 @@ public class Main { Foo.hotMethod(); } - public native static boolean isJitCompiled(Class cls, String methodName); + public native static boolean hasJitCompiledEntrypoint(Class cls, String methodName); private native static boolean hasJit(); } @@ -36,7 +36,7 @@ class Foo { static { array = new Object[10000]; - while (!Main.isJitCompiled(Foo.class, "hotMethod")) { + while (!Main.hasJitCompiledEntrypoint(Foo.class, "hotMethod")) { Foo.hotMethod(); try { // Sleep to give a chance for the JIT to compile `hotMethod`. diff --git a/test/667-jit-jni-stub/expected.txt b/test/667-jit-jni-stub/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/667-jit-jni-stub/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/667-jit-jni-stub/info.txt b/test/667-jit-jni-stub/info.txt new file mode 100644 index 0000000000..6f25c44592 --- /dev/null +++ b/test/667-jit-jni-stub/info.txt @@ -0,0 +1 @@ +Tests for JITting and collecting JNI stubs. diff --git a/test/667-jit-jni-stub/jit_jni_stub_test.cc b/test/667-jit-jni-stub/jit_jni_stub_test.cc new file mode 100644 index 0000000000..82e06fc018 --- /dev/null +++ b/test/667-jit-jni-stub/jit_jni_stub_test.cc @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#include + +#include "jit/jit.h" +#include "jit/jit_code_cache.h" +#include "mirror/class.h" +#include "mirror/string.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" + +namespace art { + +// Local class declared as a friend of JitCodeCache so that we can access its internals. +class JitJniStubTestHelper { + public: + static bool isNextJitGcFull(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { + CHECK(Runtime::Current()->GetJit() != nullptr); + jit::JitCodeCache* cache = Runtime::Current()->GetJit()->GetCodeCache(); + MutexLock mu(self, cache->lock_); + return cache->ShouldDoFullCollection(); + } +}; + +// Calls through to a static method with signature "()V". +extern "C" JNIEXPORT +void Java_Main_callThrough(JNIEnv* env, jclass, jclass klass, jstring methodName) { + ScopedObjectAccess soa(Thread::Current()); + std::string name = soa.Decode(methodName)->ToModifiedUtf8(); + jmethodID method = env->GetStaticMethodID(klass, name.c_str(), "()V"); + CHECK(method != nullptr) << soa.Decode(klass)->PrettyDescriptor() << "." << name; + env->CallStaticVoidMethod(klass, method); +} + +extern "C" JNIEXPORT +void Java_Main_jitGc(JNIEnv*, jclass) { + CHECK(Runtime::Current()->GetJit() != nullptr); + jit::JitCodeCache* cache = Runtime::Current()->GetJit()->GetCodeCache(); + ScopedObjectAccess soa(Thread::Current()); + cache->GarbageCollectCache(Thread::Current()); +} + +extern "C" JNIEXPORT +jboolean Java_Main_isNextJitGcFull(JNIEnv*, jclass) { + ScopedObjectAccess soa(Thread::Current()); + return JitJniStubTestHelper::isNextJitGcFull(soa.Self()); +} + +} // namespace art diff --git a/test/667-jit-jni-stub/run b/test/667-jit-jni-stub/run new file mode 100755 index 0000000000..1877be482e --- /dev/null +++ b/test/667-jit-jni-stub/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. + +# Disable AOT compilation of JNI stubs. +${RUN} "${@}" --no-prebuild --no-dex2oat diff --git a/test/667-jit-jni-stub/src/Main.java b/test/667-jit-jni-stub/src/Main.java new file mode 100644 index 0000000000..b867970eab --- /dev/null +++ b/test/667-jit-jni-stub/src/Main.java @@ -0,0 +1,180 @@ +/* + * 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 { + System.loadLibrary(args[0]); + if (isAotCompiled(Main.class, "hasJit")) { + throw new Error("This test must be run with --no-prebuild --no-dex2oat!"); + } + if (!hasJit()) { + return; + } + + testCompilationUseAndCollection(); + testMixedFramesOnStack(); + } + + public static void testCompilationUseAndCollection() { + // Test that callThrough() can be JIT-compiled. + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertFalse(hasJitCompiledCode(Main.class, "callThrough")); + ensureCompiledCallThroughEntrypoint(/* call */ true); + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + + // Use callThrough() once again now that the method has a JIT-compiled stub. + callThrough(Main.class, "doNothing"); + + // Test that GC with the JIT-compiled stub on the stack does not collect it. + // Also tests stack walk over the JIT-compiled stub. + callThrough(Main.class, "testGcWithCallThroughStubOnStack"); + + // Test that, when marking used methods before a full JIT GC, a single execution + // of the GenericJNI trampoline can save the compiled stub from being collected. + testSingleInvocationTriggersRecompilation(); + + // Test that the JNI compiled stub can actually be collected. + testStubCanBeCollected(); + } + + public static void testGcWithCallThroughStubOnStack() { + // Check that this method was called via JIT-compiled callThrough() stub. + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + // This assertion also exercises stack walk over the JIT-compiled callThrough() stub. + assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); + + doJitGcsUntilFullJitGcIsScheduled(); + // The callThrough() on the stack above this method is using the compiled stub, + // so the JIT GC should not remove the compiled code. + jitGc(); + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + } + + public static void testSingleInvocationTriggersRecompilation() { + // After scheduling a full JIT GC, single call through the GenericJNI + // trampoline should ensure that the compiled stub is used again. + doJitGcsUntilFullJitGcIsScheduled(); + callThrough(Main.class, "doNothing"); + ensureCompiledCallThroughEntrypoint(/* call */ false); // Wait for the compilation task to run. + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + jitGc(); // This JIT GC should not collect the callThrough() stub. + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + } + + public static void testMixedFramesOnStack() { + // Starts without a compiled JNI stub for callThrough(). + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertFalse(hasJitCompiledCode(Main.class, "callThrough")); + callThrough(Main.class, "testMixedFramesOnStackStage2"); + // We have just returned through the JIT-compiled JNI stub, so it must still + // be compiled (though not necessarily with the entrypoint pointing to it). + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + // Though the callThrough() is on the stack, that frame is using the GenericJNI + // and does not prevent the collection of the JNI stub. + testStubCanBeCollected(); + } + + public static void testMixedFramesOnStackStage2() { + // We cannot assert that callThrough() has no JIT compiled stub as that check + // may race against the compilation task. Just check the caller. + assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); + // Now ensure that the JNI stub is compiled and used. + ensureCompiledCallThroughEntrypoint(/* call */ true); + callThrough(Main.class, "testMixedFramesOnStackStage3"); + } + + public static void testMixedFramesOnStackStage3() { + // Check that this method was called via JIT-compiled callThrough() stub. + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + // This assertion also exercises stack walk over the JIT-compiled callThrough() stub. + assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); + // For a good measure, try a JIT GC. + jitGc(); + } + + public static void testStubCanBeCollected() { + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + doJitGcsUntilFullJitGcIsScheduled(); + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + jitGc(); // JIT GC without callThrough() on the stack should collect the callThrough() stub. + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertFalse(hasJitCompiledCode(Main.class, "callThrough")); + } + + public static void doJitGcsUntilFullJitGcIsScheduled() { + // We enter with a compiled stub for callThrough() but we also need the entrypoint to be set. + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + ensureCompiledCallThroughEntrypoint(/* call */ true); + // Perform JIT GC until the next GC is marked to do full collection. + do { + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + callThrough(Main.class, "jitGc"); // JIT GC with callThrough() safely on the stack. + } while (!isNextJitGcFull()); + // The JIT GC before the full collection resets entrypoints and waits to see + // if the methods are still in use. + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + } + + public static void ensureCompiledCallThroughEntrypoint(boolean call) { + int count = 0; + while (!hasJitCompiledEntrypoint(Main.class, "callThrough")) { + // If `call` is true, also exercise the `callThrough()` method to increase hotness. + int limit = call ? 1 << Math.min(count, 12) : 0; + for (int i = 0; i < limit; ++i) { + callThrough(Main.class, "doNothing"); + } + try { + // Sleep to give a chance for the JIT to compile `hasJit` stub. + Thread.sleep(100); + } catch (Exception e) { + // Ignore + } + if (++count == 50) { + throw new Error("TIMEOUT"); + } + }; + } + + public static void assertTrue(boolean value) { + if (!value) { + throw new AssertionError("Expected true!"); + } + } + + public static void assertFalse(boolean value) { + if (value) { + throw new AssertionError("Expected false!"); + } + } + + public static void doNothing() { } + public static void throwError() { throw new Error(); } + + // Note that the callThrough()'s shorty differs from shorties of the other + // native methods used in this test because of the return type `void.` + public native static void callThrough(Class cls, String methodName); + + public native static void jitGc(); + public native static boolean isNextJitGcFull(); + + public native static boolean isAotCompiled(Class cls, String methodName); + public native static boolean hasJitCompiledEntrypoint(Class cls, String methodName); + public native static boolean hasJitCompiledCode(Class cls, String methodName); + private native static boolean hasJit(); +} diff --git a/test/Android.bp b/test/Android.bp index 8f29251907..2d526d256c 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -384,6 +384,7 @@ cc_defaults { "656-annotation-lookup-generic-jni/test.cc", "661-oat-writer-layout/oat_writer_layout.cc", "664-aget-verifier/aget-verifier.cc", + "667-jit-jni-stub/jit_jni_stub_test.cc", "708-jit-cache-churn/jit.cc", ], shared_libs: [ diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index df497c1181..34580800cc 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -152,10 +152,10 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isAotCompiled(JNIEnv* env, return method->GetOatMethodQuickCode(kRuntimePointerSize) != nullptr; } -extern "C" JNIEXPORT jboolean JNICALL Java_Main_isJitCompiled(JNIEnv* env, - jclass, - jclass cls, - jstring method_name) { +extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledEntrypoint(JNIEnv* env, + jclass, + jclass cls, + jstring method_name) { jit::Jit* jit = GetJitIfEnabled(); if (jit == nullptr) { return false; @@ -169,6 +169,23 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isJitCompiled(JNIEnv* env, return jit->GetCodeCache()->ContainsPc(method->GetEntryPointFromQuickCompiledCode()); } +extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledCode(JNIEnv* env, + jclass, + jclass cls, + jstring method_name) { + jit::Jit* jit = GetJitIfEnabled(); + if (jit == nullptr) { + return false; + } + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + ScopedUtfChars chars(env, method_name); + CHECK(chars.c_str() != nullptr); + ArtMethod* method = soa.Decode(cls)->FindDeclaredDirectMethodByName( + chars.c_str(), kRuntimePointerSize); + return jit->GetCodeCache()->ContainsMethod(method); +} + extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env, jclass, jclass cls, -- GitLab From b1b52069dcb1228f89511ffb41592beebe4d8ea3 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Mon, 27 Nov 2017 11:51:42 +0000 Subject: [PATCH 084/226] ART: Fix invoke-polymorphic compiler warnings Stops recognizing polymorphic signature methods as potential compiler intrinsics until implemented. Test: m -j32 Bug: 69622155 Bug: 65872996 Change-Id: I1392e7a91dfbb29d526dbe561f3a8c89e3218da8 --- compiler/optimizing/intrinsics.cc | 39 +++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 210607f88b..9bf10f58fd 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -139,26 +139,41 @@ static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) { // Call might be devirtualized. return (invoke_type == kVirtual || invoke_type == kDirect); - default: + case kSuper: + case kInterface: + case kPolymorphic: return false; } + LOG(FATAL) << "Unknown intrinsic invoke type: " << intrinsic_type; + UNREACHABLE(); } bool IntrinsicsRecognizer::Recognize(HInvoke* invoke, /*out*/ bool* wrong_invoke_type) { ArtMethod* art_method = invoke->GetResolvedMethod(); - if (art_method != nullptr && art_method->IsIntrinsic()) { - Intrinsics intrinsic = static_cast(art_method->GetIntrinsic()); - if (CheckInvokeType(intrinsic, invoke)) { - invoke->SetIntrinsic(intrinsic, - NeedsEnvironmentOrCache(intrinsic), - GetSideEffects(intrinsic), - GetExceptions(intrinsic)); - return true; - } else { - *wrong_invoke_type = true; + *wrong_invoke_type = false; + if (art_method == nullptr || !art_method->IsIntrinsic()) { + return false; + } + + { + // TODO: b/65872996 Polymorphic signature methods should be compiler intrinsics. + ScopedObjectAccess soa(Thread::Current()); + if (art_method->IsPolymorphicSignature()) { + return false; } } - return false; + + Intrinsics intrinsic = static_cast(art_method->GetIntrinsic()); + if (CheckInvokeType(intrinsic, invoke) == false) { + *wrong_invoke_type = true; + return false; + } + + invoke->SetIntrinsic(intrinsic, + NeedsEnvironmentOrCache(intrinsic), + GetSideEffects(intrinsic), + GetExceptions(intrinsic)); + return true; } void IntrinsicsRecognizer::Run() { -- GitLab From 1fcae9f0cc4995e473c39c2e2b3afd53bc1272a0 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 28 Nov 2017 14:14:19 +0000 Subject: [PATCH 085/226] ART: Interfaces must declare superclass j.l.Object. Test: 166-bad-interface-super Bug: 69442553 Change-Id: I13b11fb4831f3a2780e6d621676c807907587346 --- runtime/class_linker.cc | 4 +++ test/166-bad-interface-super/expected.txt | 2 ++ test/166-bad-interface-super/info.txt | 1 + .../jasmin/BadSuper1.j | 17 ++++++++++ .../jasmin/BadSuper2.j | 17 ++++++++++ .../src/BaseClass.java | 18 +++++++++++ .../src/BaseInterface.java | 18 +++++++++++ test/166-bad-interface-super/src/Main.java | 31 +++++++++++++++++++ 8 files changed, 108 insertions(+) create mode 100644 test/166-bad-interface-super/expected.txt create mode 100644 test/166-bad-interface-super/info.txt create mode 100644 test/166-bad-interface-super/jasmin/BadSuper1.j create mode 100644 test/166-bad-interface-super/jasmin/BadSuper2.j create mode 100644 test/166-bad-interface-super/src/BaseClass.java create mode 100644 test/166-bad-interface-super/src/BaseInterface.java create mode 100644 test/166-bad-interface-super/src/Main.java diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 38dd7612f2..ccf431969a 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -5523,6 +5523,10 @@ bool ClassLinker::LinkSuperClass(Handle klass) { return false; } // Verify + if (klass->IsInterface() && super != GetClassRoot(kJavaLangObject)) { + ThrowClassFormatError(klass.Get(), "Interfaces must have java.lang.Object as superclass"); + return false; + } if (super->IsFinal()) { ThrowVerifyError(klass.Get(), "Superclass %s of %s is declared final", diff --git a/test/166-bad-interface-super/expected.txt b/test/166-bad-interface-super/expected.txt new file mode 100644 index 0000000000..c49f6d255f --- /dev/null +++ b/test/166-bad-interface-super/expected.txt @@ -0,0 +1,2 @@ +Caught java.lang.ClassFormatError when trying to resolve BadSuper1. +Caught java.lang.ClassFormatError when trying to resolve BadSuper2. diff --git a/test/166-bad-interface-super/info.txt b/test/166-bad-interface-super/info.txt new file mode 100644 index 0000000000..bcba8c0d89 --- /dev/null +++ b/test/166-bad-interface-super/info.txt @@ -0,0 +1 @@ +Test that linking an interface declaring a superclass other than j.l.Object throws CFE. diff --git a/test/166-bad-interface-super/jasmin/BadSuper1.j b/test/166-bad-interface-super/jasmin/BadSuper1.j new file mode 100644 index 0000000000..f96564ec73 --- /dev/null +++ b/test/166-bad-interface-super/jasmin/BadSuper1.j @@ -0,0 +1,17 @@ +; 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. + +.interface public BadSuper1 +.super BaseInterface + diff --git a/test/166-bad-interface-super/jasmin/BadSuper2.j b/test/166-bad-interface-super/jasmin/BadSuper2.j new file mode 100644 index 0000000000..584bd2094d --- /dev/null +++ b/test/166-bad-interface-super/jasmin/BadSuper2.j @@ -0,0 +1,17 @@ +; 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. + +.interface public BadSuper2 +.super BaseClass + diff --git a/test/166-bad-interface-super/src/BaseClass.java b/test/166-bad-interface-super/src/BaseClass.java new file mode 100644 index 0000000000..6ea1ad3f3b --- /dev/null +++ b/test/166-bad-interface-super/src/BaseClass.java @@ -0,0 +1,18 @@ +/* + * 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. + */ + +class BaseClass { +} diff --git a/test/166-bad-interface-super/src/BaseInterface.java b/test/166-bad-interface-super/src/BaseInterface.java new file mode 100644 index 0000000000..7872a43794 --- /dev/null +++ b/test/166-bad-interface-super/src/BaseInterface.java @@ -0,0 +1,18 @@ +/* + * 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. + */ + +interface BaseInterface { +} diff --git a/test/166-bad-interface-super/src/Main.java b/test/166-bad-interface-super/src/Main.java new file mode 100644 index 0000000000..3df2574678 --- /dev/null +++ b/test/166-bad-interface-super/src/Main.java @@ -0,0 +1,31 @@ +/* + * 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. + */ + +class Main { + public static void main(String[] args) throws Exception { + tryResolveClassExpectingCFE("BadSuper1"); + tryResolveClassExpectingCFE("BadSuper2"); + } + + public static void tryResolveClassExpectingCFE(String className) throws Exception { + try { + Class.forName(className); + } catch (ClassFormatError e) { + System.out.println( + "Caught " + e.getClass().getName() + " when trying to resolve " + className + "."); + } + } +} -- GitLab From 2d8801f7b932496d5c2606294ff8fdea60e05b30 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 28 Nov 2017 15:50:07 +0000 Subject: [PATCH 086/226] Clean some dex2oat options. Remove dump-passes inherited from Quick days, and move dump-timings and dump-stats to CompilerStats. Test: test.py Change-Id: Ie79be858a141e59dc0b2a87d8cb5a5248a5bc7af --- compiler/common_compiler_test.cc | 5 ----- compiler/common_compiler_test.h | 1 - .../driver/compiled_method_storage_test.cc | 3 --- compiler/driver/compiler_driver.cc | 8 +------ compiler/driver/compiler_driver.h | 20 ----------------- compiler/driver/compiler_options.cc | 2 ++ compiler/driver/compiler_options.h | 10 +++++++++ compiler/driver/compiler_options_map-inl.h | 14 ++++++++++++ compiler/driver/compiler_options_map.def | 2 ++ compiler/jit/jit_compiler.cc | 4 ---- compiler/jit/jit_compiler.h | 1 - compiler/linker/relative_patcher_test.h | 3 --- compiler/optimizing/optimizing_compiler.cc | 4 ++-- dex2oat/dex2oat.cc | 22 ++----------------- dex2oat/dex2oat_options.cc | 6 ----- dex2oat/linker/oat_writer_test.cc | 4 ---- 16 files changed, 33 insertions(+), 76 deletions(-) diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index 500fc4ae9a..40a5370ec7 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -174,7 +174,6 @@ void CommonCompilerTest::SetUp() { } } - timer_.reset(new CumulativeLogger("Compilation times")); CreateCompilerDriver(compiler_kind_, instruction_set); } } @@ -193,9 +192,6 @@ void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, GetCompiledClasses(), GetCompiledMethods(), number_of_threads, - /* dump_stats */ true, - /* dump_passes */ true, - timer_.get(), /* swap_fd */ -1, GetProfileCompilationInfo())); // We typically don't generate an image in unit tests, disable this optimization by default. @@ -227,7 +223,6 @@ InstructionSet CommonCompilerTest::GetInstructionSet() const { } void CommonCompilerTest::TearDown() { - timer_.reset(); compiler_driver_.reset(); callbacks_.reset(); verification_results_.reset(); diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index bcda41a9b8..05fdc97e07 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -106,7 +106,6 @@ class CommonCompilerTest : public CommonRuntimeTest { std::unique_ptr compiler_options_; std::unique_ptr verification_results_; std::unique_ptr compiler_driver_; - std::unique_ptr timer_; std::unique_ptr instruction_set_features_; diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc index de481caf07..0769561d0e 100644 --- a/compiler/driver/compiled_method_storage_test.cc +++ b/compiler/driver/compiled_method_storage_test.cc @@ -37,9 +37,6 @@ TEST(CompiledMethodStorage, Deduplicate) { /* compiled_classes */ nullptr, /* compiled_methods */ nullptr, /* thread_count */ 1u, - /* dump_stats */ false, - /* dump_passes */ false, - /* timer */ nullptr, /* swap_fd */ -1, /* profile_compilation_info */ nullptr); CompiledMethodStorage* storage = driver.GetCompiledMethodStorage(); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index fd7ae9f570..0ca3c8f613 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -282,9 +282,6 @@ CompilerDriver::CompilerDriver( std::unordered_set* compiled_classes, std::unordered_set* compiled_methods, size_t thread_count, - bool dump_stats, - bool dump_passes, - CumulativeLogger* timer, int swap_fd, const ProfileCompilationInfo* profile_compilation_info) : compiler_options_(compiler_options), @@ -303,9 +300,6 @@ CompilerDriver::CompilerDriver( had_hard_verifier_failure_(false), parallel_thread_count_(thread_count), stats_(new AOTCompilationStats), - dump_stats_(dump_stats), - dump_passes_(dump_passes), - timings_logger_(timer), compiler_context_(nullptr), support_boot_image_fixup_(true), compiled_method_storage_(swap_fd), @@ -396,7 +390,7 @@ void CompilerDriver::CompileAll(jobject class_loader, if (GetCompilerOptions().IsAnyCompilationEnabled()) { Compile(class_loader, dex_files, timings); } - if (dump_stats_) { + if (GetCompilerOptions().GetDumpStats()) { stats_->Dump(); } diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index da4a580bf2..d2141e8bc7 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -97,9 +97,6 @@ class CompilerDriver { std::unordered_set* compiled_classes, std::unordered_set* compiled_methods, size_t thread_count, - bool dump_stats, - bool dump_passes, - CumulativeLogger* timer, int swap_fd, const ProfileCompilationInfo* profile_compilation_info); @@ -302,18 +299,6 @@ class CompilerDriver { return parallel_thread_count_; } - bool GetDumpStats() const { - return dump_stats_; - } - - bool GetDumpPasses() const { - return dump_passes_; - } - - CumulativeLogger* GetTimingsLogger() const { - return timings_logger_; - } - void SetDedupeEnabled(bool dedupe_enabled) { compiled_method_storage_.SetDedupeEnabled(dedupe_enabled); } @@ -536,11 +521,6 @@ class CompilerDriver { class AOTCompilationStats; std::unique_ptr stats_; - bool dump_stats_; - const bool dump_passes_; - - CumulativeLogger* const timings_logger_; - typedef void (*CompilerCallbackFn)(CompilerDriver& driver); typedef MutexLock* (*CompilerMutexLockFn)(CompilerDriver& driver); diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc index 032763cdff..c0a9a05aa6 100644 --- a/compiler/driver/compiler_options.cc +++ b/compiler/driver/compiler_options.cc @@ -49,6 +49,8 @@ CompilerOptions::CompilerOptions() implicit_so_checks_(true), implicit_suspend_checks_(false), compile_pic_(false), + dump_timings_(false), + dump_stats_(false), verbose_methods_(), abort_on_hard_verifier_failure_(false), abort_on_soft_verifier_failure_(false), diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index a71f61a9e3..3f660293d2 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -266,6 +266,14 @@ class CompilerOptions FINAL { return passes_to_run_; } + bool GetDumpTimings() const { + return dump_timings_; + } + + bool GetDumpStats() const { + return dump_stats_; + } + private: bool ParseDumpInitFailures(const std::string& option, std::string* error_msg); void ParseDumpCfgPasses(const StringPiece& option, UsageFn Usage); @@ -303,6 +311,8 @@ class CompilerOptions FINAL { bool implicit_so_checks_; bool implicit_suspend_checks_; bool compile_pic_; + bool dump_timings_; + bool dump_stats_; // Vector of methods to have verbose output enabled for. std::vector verbose_methods_; diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h index e28d49974a..f97ab08600 100644 --- a/compiler/driver/compiler_options_map-inl.h +++ b/compiler/driver/compiler_options_map-inl.h @@ -78,6 +78,14 @@ inline bool ReadCompilerOptions(Base& map, CompilerOptions* options, std::string map.AssignIfExists(Base::VerboseMethods, &options->verbose_methods_); options->deduplicate_code_ = map.GetOrDefault(Base::DeduplicateCode); + if (map.Exists(Base::DumpTimings)) { + options->dump_timings_ = true; + } + + if (map.Exists(Base::DumpStats)) { + options->dump_stats_ = true; + } + return true; } @@ -129,6 +137,12 @@ inline void AddCompilerOptionsArgumentParserOptions(Builder& b) { .WithValueMap({{"false", false}, {"true", true}}) .IntoKey(Map::DeduplicateCode) + .Define({"--dump-timings"}) + .IntoKey(Map::DumpTimings) + + .Define({"--dump-stats"}) + .IntoKey(Map::DumpStats) + .Define("--debuggable") .IntoKey(Map::Debuggable) diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def index cccd6184c6..2c56fd7974 100644 --- a/compiler/driver/compiler_options_map.def +++ b/compiler/driver/compiler_options_map.def @@ -58,5 +58,7 @@ COMPILER_OPTIONS_KEY (Unit, DumpCFGAppend) COMPILER_OPTIONS_KEY (std::string, RegisterAllocationStrategy) COMPILER_OPTIONS_KEY (ParseStringList<','>, VerboseMethods) COMPILER_OPTIONS_KEY (bool, DeduplicateCode, true) +COMPILER_OPTIONS_KEY (Unit, DumpTimings) +COMPILER_OPTIONS_KEY (Unit, DumpStats) #undef COMPILER_OPTIONS_KEY diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index 0c82d601a7..f33c5e1b97 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -130,7 +130,6 @@ JitCompiler::JitCompiler() { if (instruction_set_features_ == nullptr) { instruction_set_features_ = InstructionSetFeatures::FromCppDefines(); } - cumulative_logger_.reset(new CumulativeLogger("jit times")); compiler_driver_.reset(new CompilerDriver( compiler_options_.get(), /* verification_results */ nullptr, @@ -141,9 +140,6 @@ JitCompiler::JitCompiler() { /* compiled_classes */ nullptr, /* compiled_methods */ nullptr, /* thread_count */ 1, - /* dump_stats */ false, - /* dump_passes */ false, - cumulative_logger_.get(), /* swap_fd */ -1, /* profile_compilation_info */ nullptr)); // Disable dedupe so we can remove compiled methods. diff --git a/compiler/jit/jit_compiler.h b/compiler/jit/jit_compiler.h index 1e1838efd5..31dc9e2fe5 100644 --- a/compiler/jit/jit_compiler.h +++ b/compiler/jit/jit_compiler.h @@ -48,7 +48,6 @@ class JitCompiler { private: std::unique_ptr compiler_options_; - std::unique_ptr cumulative_logger_; std::unique_ptr compiler_driver_; std::unique_ptr instruction_set_features_; std::unique_ptr jit_logger_; diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h index 6297dd0481..9e9d14af9e 100644 --- a/compiler/linker/relative_patcher_test.h +++ b/compiler/linker/relative_patcher_test.h @@ -52,9 +52,6 @@ class RelativePatcherTest : public testing::Test { /* compiled_classes */ nullptr, /* compiled_methods */ nullptr, /* thread_count */ 1u, - /* dump_stats */ false, - /* dump_passes */ false, - /* timer */ nullptr, /* swap_fd */ -1, /* profile_compilation_info */ nullptr), error_msg_(), diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 53f9ec413b..47f7b125b7 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -112,7 +112,7 @@ class PassObserver : public ValueObject { Mutex& dump_mutex) : graph_(graph), cached_method_name_(), - timing_logger_enabled_(compiler_driver->GetDumpPasses()), + timing_logger_enabled_(compiler_driver->GetCompilerOptions().GetDumpTimings()), timing_logger_(timing_logger_enabled_ ? GetMethodName() : "", true, true), disasm_info_(graph->GetAllocator()), visualizer_oss_(), @@ -407,7 +407,7 @@ void OptimizingCompiler::Init() { driver->GetCompilerOptions().GetDumpCfgAppend() ? std::ofstream::app : std::ofstream::out; visualizer_output_.reset(new std::ofstream(cfg_file_name, cfg_file_mode)); } - if (driver->GetDumpStats()) { + if (driver->GetCompilerOptions().GetDumpStats()) { compilation_stats_.reset(new OptimizingCompilerStats()); } } diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 8137fb1f11..27bec1d3e1 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -628,10 +628,6 @@ class Dex2Oat FINAL { opened_dex_files_maps_(), opened_dex_files_(), no_inline_from_dex_files_(), - dump_stats_(false), - dump_passes_(false), - dump_timing_(false), - dump_slow_timing_(kIsDebugBuild), avoid_storing_invocation_(false), swap_fd_(kInvalidFd), app_image_fd_(kInvalidFd), @@ -1221,9 +1217,6 @@ class Dex2Oat FINAL { } AssignTrueIfExists(args, M::Host, &is_host_); - AssignTrueIfExists(args, M::DumpTiming, &dump_timing_); - AssignTrueIfExists(args, M::DumpPasses, &dump_passes_); - AssignTrueIfExists(args, M::DumpStats, &dump_stats_); AssignTrueIfExists(args, M::AvoidStoringInvocation, &avoid_storing_invocation_); AssignTrueIfExists(args, M::MultiImage, &multi_image_); @@ -1726,7 +1719,6 @@ class Dex2Oat FINAL { ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); TimingLogger::ScopedTiming t("dex2oat Compile", timings_); - compiler_phases_timings_.reset(new CumulativeLogger("compilation times")); // Find the dex files we should not inline from. std::vector no_inline_filters; @@ -1787,9 +1779,6 @@ class Dex2Oat FINAL { compiled_classes_.release(), compiled_methods_.release(), thread_count_, - dump_stats_, - dump_passes_, - compiler_phases_timings_.get(), swap_fd_, profile_compilation_info_.get())); driver_->SetDexFilesForOatFile(dex_files_); @@ -2202,12 +2191,10 @@ class Dex2Oat FINAL { } void DumpTiming() { - if (dump_timing_ || (dump_slow_timing_ && timings_->GetTotalNs() > MsToNs(1000))) { + if (compiler_options_->GetDumpTimings() || + (kIsDebugBuild && timings_->GetTotalNs() > MsToNs(1000))) { LOG(INFO) << Dumpable(*timings_); } - if (dump_passes_) { - LOG(INFO) << Dumpable(*driver_->GetTimingsLogger()); - } } bool IsImage() const { @@ -2842,10 +2829,6 @@ class Dex2Oat FINAL { // Note that this might contain pointers owned by class_loader_context_. std::vector no_inline_from_dex_files_; - bool dump_stats_; - bool dump_passes_; - bool dump_timing_; - bool dump_slow_timing_; bool avoid_storing_invocation_; std::string swap_file_name_; int swap_fd_; @@ -2858,7 +2841,6 @@ class Dex2Oat FINAL { int profile_file_fd_; std::unique_ptr profile_compilation_info_; TimingLogger* timings_; - std::unique_ptr compiler_phases_timings_; std::vector> dex_files_per_oat_file_; std::unordered_map dex_file_oat_index_map_; diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc index 7f177b9ff6..d9b4ea7835 100644 --- a/dex2oat/dex2oat_options.cc +++ b/dex2oat/dex2oat_options.cc @@ -220,12 +220,6 @@ static Parser CreateArgumentParser() { .IntoKey(M::Backend) .Define("--host") .IntoKey(M::Host) - .Define("--dump-timing") - .IntoKey(M::DumpTiming) - .Define("--dump-passes") - .IntoKey(M::DumpPasses) - .Define("--dump-stats") - .IntoKey(M::DumpStats) .Define("--avoid-storing-invocation") .IntoKey(M::AvoidStoringInvocation) .Define("--very-large-app-threshold=_") diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 7509d91677..8e5f2a93d6 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -102,7 +102,6 @@ class OatTest : public CommonCompilerTest { callbacks_.reset(new QuickCompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileApp)); callbacks_->SetVerificationResults(verification_results_.get()); Runtime::Current()->SetCompilerCallbacks(callbacks_.get()); - timer_.reset(new CumulativeLogger("Compilation times")); compiler_driver_.reset(new CompilerDriver(compiler_options_.get(), verification_results_.get(), compiler_kind, @@ -112,9 +111,6 @@ class OatTest : public CommonCompilerTest { /* compiled_classes */ nullptr, /* compiled_methods */ nullptr, /* thread_count */ 2, - /* dump_stats */ true, - /* dump_passes */ true, - timer_.get(), /* swap_fd */ -1, /* profile_compilation_info */ nullptr)); } -- GitLab From 47d31853e16a95393d760e6be2ffeeb0193f94a1 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 28 Nov 2017 18:36:12 +0000 Subject: [PATCH 087/226] Revert "JIT JNI stubs." Seems to break 998-redefine-use-after-free in some --no-image configuration. Bug: 65574695 Bug: 69843562 This reverts commit 3417eaefe4e714c489a6fb0cb89b4810d81bdf4d. Change-Id: I2dd157b931c17c791522ea2544c1982ed3519b86 --- compiler/optimizing/optimizing_compiler.cc | 64 +-- runtime/arch/arm/quick_entrypoints_arm.S | 4 +- runtime/arch/arm64/quick_entrypoints_arm64.S | 2 +- runtime/arch/mips/quick_entrypoints_mips.S | 3 +- .../arch/mips64/quick_entrypoints_mips64.S | 3 +- runtime/arch/x86/quick_entrypoints_x86.S | 4 +- .../arch/x86_64/quick_entrypoints_x86_64.S | 4 +- runtime/art_method.cc | 28 +- runtime/art_method.h | 11 +- runtime/entrypoints/entrypoint_utils.cc | 4 +- .../quick/quick_trampoline_entrypoints.cc | 9 +- runtime/jit/jit.cc | 6 +- runtime/jit/jit_code_cache.cc | 493 ++++-------------- runtime/jit/jit_code_cache.h | 39 +- runtime/jit/profile_saver.cc | 4 +- runtime/managed_stack-inl.h | 4 +- runtime/managed_stack.h | 80 +-- runtime/stack.cc | 59 +-- runtime/stack.h | 3 +- runtime/thread.cc | 4 +- runtime/thread.h | 9 +- test/655-jit-clinit/src/Main.java | 4 +- test/667-jit-jni-stub/expected.txt | 1 - test/667-jit-jni-stub/info.txt | 1 - test/667-jit-jni-stub/jit_jni_stub_test.cc | 63 --- test/667-jit-jni-stub/run | 18 - test/667-jit-jni-stub/src/Main.java | 180 ------- test/Android.bp | 1 - test/common/runtime_state.cc | 25 +- 29 files changed, 185 insertions(+), 945 deletions(-) delete mode 100644 test/667-jit-jni-stub/expected.txt delete mode 100644 test/667-jit-jni-stub/info.txt delete mode 100644 test/667-jit-jni-stub/jit_jni_stub_test.cc delete mode 100755 test/667-jit-jni-stub/run delete mode 100644 test/667-jit-jni-stub/src/Main.java diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index b6d3294037..5a9e2c59b2 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -1196,69 +1196,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, Runtime* runtime = Runtime::Current(); ArenaAllocator allocator(runtime->GetJitArenaPool()); - - if (UNLIKELY(method->IsNative())) { - JniCompiledMethod jni_compiled_method = ArtQuickJniCompileMethod( - GetCompilerDriver(), access_flags, method_idx, *dex_file); - ScopedNullHandle> roots; - ArenaSet> cha_single_implementation_list( - allocator.Adapter(kArenaAllocCHA)); - const void* code = code_cache->CommitCode( - self, - method, - /* stack_map_data */ nullptr, - /* method_info_data */ nullptr, - /* roots_data */ nullptr, - jni_compiled_method.GetFrameSize(), - jni_compiled_method.GetCoreSpillMask(), - jni_compiled_method.GetFpSpillMask(), - jni_compiled_method.GetCode().data(), - jni_compiled_method.GetCode().size(), - /* data_size */ 0u, - osr, - roots, - /* has_should_deoptimize_flag */ false, - cha_single_implementation_list); - if (code == nullptr) { - return false; - } - - const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); - if (compiler_options.GetGenerateDebugInfo()) { - const auto* method_header = reinterpret_cast(code); - const uintptr_t code_address = reinterpret_cast(method_header->GetCode()); - debug::MethodDebugInfo info = {}; - DCHECK(info.trampoline_name.empty()); - info.dex_file = dex_file; - info.class_def_index = class_def_idx; - info.dex_method_index = method_idx; - info.access_flags = access_flags; - info.code_item = code_item; - info.isa = jni_compiled_method.GetInstructionSet(); - info.deduped = false; - info.is_native_debuggable = compiler_options.GetNativeDebuggable(); - info.is_optimized = true; - info.is_code_address_text_relative = false; - info.code_address = code_address; - info.code_size = jni_compiled_method.GetCode().size(); - info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); - info.code_info = nullptr; - info.cfi = jni_compiled_method.GetCfi(); - std::vector elf_file = debug::WriteDebugElfFileForMethods( - GetCompilerDriver()->GetInstructionSet(), - GetCompilerDriver()->GetInstructionSetFeatures(), - ArrayRef(&info, 1)); - CreateJITCodeEntryForAddress(code_address, std::move(elf_file)); - } - - Runtime::Current()->GetJit()->AddMemoryUsage(method, allocator.BytesUsed()); - if (jit_logger != nullptr) { - jit_logger->WriteLog(code, jni_compiled_method.GetCode().size(), method); - } - return true; - } - - ArenaStack arena_stack(runtime->GetJitArenaPool()); + ArenaStack arena_stack(Runtime::Current()->GetJitArenaPool()); CodeVectorAllocator code_allocator(&allocator); VariableSizedHandleScope handles(self); diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 6ec9c48b92..6ff8dd60b8 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -1783,9 +1783,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] - add ip, ip, #-1 // Remove the GenericJNI tag. ADD/SUB writing directly to SP is UNPREDICTABLE. - mov sp, ip + ldr sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] .cfi_def_cfa_register sp # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 47efeb9200..280e5937c6 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -2299,7 +2299,7 @@ ENTRY art_quick_generic_jni_trampoline .Lexception_in_native: // Move to x1 then sp to please assembler. ldr x1, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET] - add sp, x1, #-1 // Remove the GenericJNI tag. + mov sp, x1 .cfi_def_cfa_register sp # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index fc77a641b3..489c52c0d2 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -2283,8 +2283,7 @@ ENTRY art_quick_generic_jni_trampoline nop 2: - lw $t0, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) - addiu $sp, $t0, -1 // Remove the GenericJNI tag. + lw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) move $gp, $s3 # restore $gp from $s3 # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 3fb83d9232..98ffe6504a 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -2158,8 +2158,7 @@ ENTRY art_quick_generic_jni_trampoline dmtc1 $v0, $f0 # place return value to FP return value 1: - ld $t0, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) - daddiu $sp, $t0, -1 // Remove the GenericJNI tag. + ld $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION END art_quick_generic_jni_trampoline diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index a46ceeba12..25716dc1bb 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1969,9 +1969,7 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline punpckldq %xmm1, %xmm0 ret .Lexception_in_native: - pushl %fs:THREAD_TOP_QUICK_FRAME_OFFSET - addl LITERAL(-1), (%esp) // Remove the GenericJNI tag. - movl (%esp), %esp + movl %fs:THREAD_TOP_QUICK_FRAME_OFFSET, %esp // Do a call to push a new save-all frame required by the runtime. call .Lexception_call .Lexception_call: diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 463e5a279f..2c3da90f25 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -1958,9 +1958,7 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline movq %rax, %xmm0 ret .Lexception_in_native: - pushq %gs:THREAD_TOP_QUICK_FRAME_OFFSET - addq LITERAL(-1), (%rsp) // Remove the GenericJNI tag. - movq (%rsp), %rsp + movq %gs:THREAD_TOP_QUICK_FRAME_OFFSET, %rsp CFI_DEF_CFA_REGISTER(rsp) // Do a call to push a new save-all frame required by the runtime. call .Lexception_call diff --git a/runtime/art_method.cc b/runtime/art_method.cc index bdbc4509f3..fa0c501e31 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -587,6 +587,11 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { CHECK(existing_entry_point != nullptr) << PrettyMethod() << "@" << this; ClassLinker* class_linker = runtime->GetClassLinker(); + if (class_linker->IsQuickGenericJniStub(existing_entry_point)) { + // The generic JNI does not have any method header. + return nullptr; + } + if (existing_entry_point == GetQuickProxyInvokeHandler()) { DCHECK(IsProxyMethod() && !IsConstructor()); // The proxy entry point does not have any method header. @@ -594,8 +599,7 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { } // Check whether the current entry point contains this pc. - if (!class_linker->IsQuickGenericJniStub(existing_entry_point) && - !class_linker->IsQuickResolutionStub(existing_entry_point) && + if (!class_linker->IsQuickResolutionStub(existing_entry_point) && !class_linker->IsQuickToInterpreterBridge(existing_entry_point)) { OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromEntryPoint(existing_entry_point); @@ -628,13 +632,19 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { OatFile::OatMethod oat_method = FindOatMethodFor(this, class_linker->GetImagePointerSize(), &found); if (!found) { - if (IsNative()) { - // We are running the GenericJNI stub. The entrypoint may point - // to different entrypoints or to a JIT-compiled JNI stub. - DCHECK(class_linker->IsQuickGenericJniStub(existing_entry_point) || - class_linker->IsQuickResolutionStub(existing_entry_point) || - existing_entry_point == GetQuickInstrumentationEntryPoint() || - (jit != nullptr && jit->GetCodeCache()->ContainsPc(existing_entry_point))); + if (class_linker->IsQuickResolutionStub(existing_entry_point)) { + // We are running the generic jni stub, but the entry point of the method has not + // been updated yet. + DCHECK_EQ(pc, 0u) << "Should be a downcall"; + DCHECK(IsNative()); + return nullptr; + } + if (existing_entry_point == GetQuickInstrumentationEntryPoint()) { + // We are running the generic jni stub, but the method is being instrumented. + // NB We would normally expect the pc to be zero but we can have non-zero pc's if + // instrumentation is installed or removed during the call which is using the generic jni + // trampoline. + DCHECK(IsNative()); return nullptr; } // Only for unit tests. diff --git a/runtime/art_method.h b/runtime/art_method.h index 6c3bb1074d..dca6f37254 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -460,11 +460,12 @@ class ArtMethod FINAL { } ProfilingInfo* GetProfilingInfo(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_) { - // Don't do a read barrier in the DCHECK() inside GetAccessFlags() called by IsNative(), - // as GetProfilingInfo is called in places where the declaring class is treated as a weak - // reference (accessing it with a read barrier would either prevent unloading the class, - // or crash the runtime if the GC wants to unload it). - if (UNLIKELY(IsNative()) || UNLIKELY(IsProxyMethod())) { + // Don't do a read barrier in the DCHECK, as GetProfilingInfo is called in places + // where the declaring class is treated as a weak reference (accessing it with + // a read barrier would either prevent unloading the class, or crash the runtime if + // the GC wants to unload it). + DCHECK(!IsNative()); + if (UNLIKELY(IsProxyMethod())) { return nullptr; } return reinterpret_cast(GetDataPtrSize(pointer_size)); diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index f3450da306..2bf4372b1f 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -245,7 +245,7 @@ ArtMethod* GetCalleeSaveMethodCaller(ArtMethod** sp, CalleeSaveType type, bool d CallerAndOuterMethod GetCalleeSaveMethodCallerAndOuterMethod(Thread* self, CalleeSaveType type) { CallerAndOuterMethod result; ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrameKnownNotTagged(); + ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame(); auto outer_caller_and_pc = DoGetCalleeSaveMethodOuterCallerAndPc(sp, type); result.outer_method = outer_caller_and_pc.first; uintptr_t caller_pc = outer_caller_and_pc.second; @@ -256,7 +256,7 @@ CallerAndOuterMethod GetCalleeSaveMethodCallerAndOuterMethod(Thread* self, Calle ArtMethod* GetCalleeSaveOuterMethod(Thread* self, CalleeSaveType type) { ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrameKnownNotTagged(); + ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame(); return DoGetCalleeSaveMethodOuterCallerAndPc(sp, type).first; } diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 0a76cddf5e..2496aa0f58 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -31,7 +31,6 @@ #include "index_bss_mapping.h" #include "instrumentation.h" #include "interpreter/interpreter.h" -#include "jit/jit.h" #include "linear_alloc.h" #include "method_handles.h" #include "method_reference.h" @@ -2168,11 +2167,6 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod** // Note: We cannot walk the stack properly until fixed up below. 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(); @@ -2194,7 +2188,7 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod** } // Fix up managed-stack things in Thread. After this we can walk the stack. - self->SetTopOfStackTagged(sp); + self->SetTopOfStack(sp); self->VerifyStack(); @@ -2314,7 +2308,6 @@ extern "C" uint64_t artQuickGenericJniEndTrampoline(Thread* self, // anything that requires a mutator lock before that would cause problems as GC may have the // exclusive mutator lock and may be moving objects, etc. ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame(); - DCHECK(self->GetManagedStack()->GetTopQuickFrameTag()); uint32_t* sp32 = reinterpret_cast(sp); ArtMethod* called = *sp; uint32_t cookie = *(sp32 - 1); diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 0d95bc6e64..953e195480 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -643,7 +643,7 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ return; } - if (method->IsClassInitializer() || !method->IsCompilable()) { + if (method->IsClassInitializer() || method->IsNative() || !method->IsCompilable()) { // We do not want to compile such methods. return; } @@ -659,8 +659,7 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ count *= priority_thread_weight_; } int32_t new_count = starting_count + count; // int32 here to avoid wrap-around; - // Note: Native method have no "warm" state or profiling info. - if (LIKELY(!method->IsNative()) && starting_count < warm_method_threshold_) { + if (starting_count < warm_method_threshold_) { if ((new_count >= warm_method_threshold_) && (method->GetProfilingInfo(kRuntimePointerSize) == nullptr)) { bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false); @@ -697,7 +696,6 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ // 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)) { DCHECK(thread_pool_ != nullptr); thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr)); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index a5c167eee8..32205138bd 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -55,107 +55,6 @@ static constexpr int kProtCode = PROT_READ | PROT_EXEC; static constexpr size_t kCodeSizeLogThreshold = 50 * KB; static constexpr size_t kStackMapSizeLogThreshold = 50 * KB; -class JitCodeCache::JniStubKey { - public: - explicit JniStubKey(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) - : shorty_(method->GetShorty()), - is_static_(method->IsStatic()), - is_fast_native_(method->IsFastNative()), - is_critical_native_(method->IsCriticalNative()), - is_synchronized_(method->IsSynchronized()) { - DCHECK(!(is_fast_native_ && is_critical_native_)); - } - - bool operator<(const JniStubKey& rhs) const { - if (is_static_ != rhs.is_static_) { - return rhs.is_static_; - } - if (is_synchronized_ != rhs.is_synchronized_) { - return rhs.is_synchronized_; - } - if (is_fast_native_ != rhs.is_fast_native_) { - return rhs.is_fast_native_; - } - if (is_critical_native_ != rhs.is_critical_native_) { - return rhs.is_critical_native_; - } - return strcmp(shorty_, rhs.shorty_) < 0; - } - - // Update the shorty to point to another method's shorty. Call this function when removing - // the method that references the old shorty from JniCodeData and not removing the entire - // JniCodeData; the old shorty may become a dangling pointer when that method is unloaded. - void UpdateShorty(ArtMethod* method) const REQUIRES_SHARED(Locks::mutator_lock_) { - const char* shorty = method->GetShorty(); - DCHECK_STREQ(shorty_, shorty); - shorty_ = shorty; - } - - private: - // The shorty points to a DexFile data and may need to change - // to point to the same shorty in a different DexFile. - mutable const char* shorty_; - - const bool is_static_; - const bool is_fast_native_; - const bool is_critical_native_; - const bool is_synchronized_; -}; - -class JitCodeCache::JniStubData { - public: - JniStubData() : code_(nullptr), methods_() {} - - void SetCode(const void* code) { - DCHECK(code != nullptr); - code_ = code; - } - - const void* GetCode() const { - return code_; - } - - bool IsCompiled() const { - return GetCode() != nullptr; - } - - void AddMethod(ArtMethod* method) { - if (!ContainsElement(methods_, method)) { - methods_.push_back(method); - } - } - - const std::vector& GetMethods() const { - return methods_; - } - - void RemoveMethodsIn(const LinearAlloc& alloc) { - auto kept_end = std::remove_if( - methods_.begin(), - methods_.end(), - [&alloc](ArtMethod* method) { return alloc.ContainsUnsafe(method); }); - methods_.erase(kept_end, methods_.end()); - } - - bool RemoveMethod(ArtMethod* method) { - auto it = std::find(methods_.begin(), methods_.end(), method); - if (it != methods_.end()) { - methods_.erase(it); - return true; - } else { - return false; - } - } - - void MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) { - std::replace(methods_.begin(), methods_.end(), old_method, new_method); - } - - private: - const void* code_; - std::vector methods_; -}; - JitCodeCache* JitCodeCache::Create(size_t initial_capacity, size_t max_capacity, bool generate_debug_info, @@ -294,36 +193,14 @@ bool JitCodeCache::ContainsPc(const void* ptr) const { bool JitCodeCache::ContainsMethod(ArtMethod* method) { MutexLock mu(Thread::Current(), lock_); - if (UNLIKELY(method->IsNative())) { - auto it = jni_stubs_map_.find(JniStubKey(method)); - if (it != jni_stubs_map_.end() && - it->second.IsCompiled() && - ContainsElement(it->second.GetMethods(), method)) { + for (auto& it : method_code_map_) { + if (it.second == method) { return true; } - } else { - for (const auto& it : method_code_map_) { - if (it.second == method) { - return true; - } - } } return false; } -const void* JitCodeCache::GetJniStubCode(ArtMethod* method) { - DCHECK(method->IsNative()); - MutexLock mu(Thread::Current(), lock_); - auto it = jni_stubs_map_.find(JniStubKey(method)); - if (it != jni_stubs_map_.end()) { - JniStubData& data = it->second; - if (data.IsCompiled() && ContainsElement(data.GetMethods(), method)) { - return data.GetCode(); - } - } - return nullptr; -} - class ScopedCodeCacheWrite : ScopedTrace { public: explicit ScopedCodeCacheWrite(MemMap* code_map, bool only_for_tlb_shootdown = false) @@ -549,9 +426,7 @@ void JitCodeCache::FreeCode(const void* code_ptr) { // Notify native debugger that we are about to remove the code. // It does nothing if we are not using native debugger. DeleteJITCodeEntryForAddress(reinterpret_cast(code_ptr)); - if (OatQuickMethodHeader::FromCodePointer(code_ptr)->IsOptimized()) { - FreeData(GetRootTable(code_ptr)); - } // else this is a JNI stub without any data. + FreeData(GetRootTable(code_ptr)); FreeCode(reinterpret_cast(allocation)); } @@ -588,16 +463,6 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { // lead to a deadlock. { ScopedCodeCacheWrite scc(code_map_.get()); - for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) { - it->second.RemoveMethodsIn(alloc); - if (it->second.GetMethods().empty()) { - method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->second.GetCode())); - it = jni_stubs_map_.erase(it); - } else { - it->first.UpdateShorty(it->second.GetMethods().front()); - ++it; - } - } for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { if (alloc.ContainsUnsafe(it->second)) { method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->first)); @@ -707,8 +572,7 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, bool has_should_deoptimize_flag, const ArenaSet& cha_single_implementation_list) { - DCHECK_NE(stack_map != nullptr, method->IsNative()); - DCHECK(!method->IsNative() || !osr); + DCHECK(stack_map != nullptr); size_t alignment = GetInstructionSetAlignment(kRuntimeISA); // Ensure the header ends up at expected instruction alignment. size_t header_size = RoundUp(sizeof(OatQuickMethodHeader), alignment); @@ -732,8 +596,8 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, std::copy(code, code + code_size, code_ptr); method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); new (method_header) OatQuickMethodHeader( - (stack_map != nullptr) ? code_ptr - stack_map : 0u, - (method_info != nullptr) ? code_ptr - method_info : 0u, + code_ptr - stack_map, + code_ptr - method_info, frame_size_in_bytes, core_spill_mask, fp_spill_mask, @@ -788,40 +652,24 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, // possible that the compiled code is considered invalidated by some class linking, // but below we still make the compiled code valid for the method. MutexLock mu(self, lock_); - if (UNLIKELY(method->IsNative())) { - DCHECK(stack_map == nullptr); - DCHECK(roots_data == nullptr); - auto it = jni_stubs_map_.find(JniStubKey(method)); - DCHECK(it != jni_stubs_map_.end()) - << "Entry inserted in NotifyCompilationOf() should be alive."; - JniStubData* data = &it->second; - DCHECK(ContainsElement(data->GetMethods(), method)) - << "Entry inserted in NotifyCompilationOf() should contain this method."; - data->SetCode(code_ptr); - instrumentation::Instrumentation* instrum = Runtime::Current()->GetInstrumentation(); - for (ArtMethod* m : data->GetMethods()) { - instrum->UpdateMethodsCode(m, method_header->GetEntryPoint()); - } + // Fill the root table before updating the entry point. + DCHECK_EQ(FromStackMapToRoots(stack_map), roots_data); + DCHECK_LE(roots_data, stack_map); + 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)); + } + method_code_map_.Put(code_ptr, method); + if (osr) { + number_of_osr_compilations_++; + osr_code_map_.Put(method, code_ptr); } else { - // Fill the root table before updating the entry point. - DCHECK_EQ(FromStackMapToRoots(stack_map), roots_data); - DCHECK_LE(roots_data, stack_map); - 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)); - } - method_code_map_.Put(code_ptr, method); - if (osr) { - number_of_osr_compilations_++; - osr_code_map_.Put(method, code_ptr); - } else { - Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( - method, method_header->GetEntryPoint()); - } + Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( + method, method_header->GetEntryPoint()); } if (collection_in_progress_) { // We need to update the live bitmap if there is a GC to ensure it sees this new @@ -855,18 +703,45 @@ size_t JitCodeCache::CodeCacheSize() { } bool JitCodeCache::RemoveMethod(ArtMethod* method, bool release_memory) { - // This function is used only for testing and only with non-native methods. - CHECK(!method->IsNative()); - MutexLock mu(Thread::Current(), lock_); + if (method->IsNative()) { + return false; + } + + bool in_cache = false; + { + ScopedCodeCacheWrite ccw(code_map_.get()); + for (auto code_iter = method_code_map_.begin(); code_iter != method_code_map_.end();) { + if (code_iter->second == method) { + if (release_memory) { + FreeCode(code_iter->first); + } + code_iter = method_code_map_.erase(code_iter); + in_cache = true; + continue; + } + ++code_iter; + } + } - bool osr = osr_code_map_.find(method) != osr_code_map_.end(); - bool in_cache = RemoveMethodLocked(method, release_memory); + bool osr = false; + auto code_map = osr_code_map_.find(method); + if (code_map != osr_code_map_.end()) { + osr_code_map_.erase(code_map); + osr = true; + } if (!in_cache) { return false; } + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + if (info != nullptr) { + auto profile = std::find(profiling_infos_.begin(), profiling_infos_.end(), info); + DCHECK(profile != profiling_infos_.end()); + profiling_infos_.erase(profile); + } + method->SetProfilingInfo(nullptr); method->ClearCounter(); Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( method, GetQuickToInterpreterBridge()); @@ -878,58 +753,34 @@ bool JitCodeCache::RemoveMethod(ArtMethod* method, bool release_memory) { return true; } -bool JitCodeCache::RemoveMethodLocked(ArtMethod* method, bool release_memory) { - if (LIKELY(!method->IsNative())) { - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - if (info != nullptr) { - RemoveElement(profiling_infos_, info); - } - method->SetProfilingInfo(nullptr); - } - - bool in_cache = false; - ScopedCodeCacheWrite ccw(code_map_.get()); - if (UNLIKELY(method->IsNative())) { - auto it = jni_stubs_map_.find(JniStubKey(method)); - if (it != jni_stubs_map_.end() && it->second.RemoveMethod(method)) { - in_cache = true; - if (it->second.GetMethods().empty()) { - if (release_memory) { - FreeCode(it->second.GetCode()); - } - jni_stubs_map_.erase(it); - } else { - it->first.UpdateShorty(it->second.GetMethods().front()); - } - } - } else { - for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { - if (it->second == method) { - in_cache = true; - if (release_memory) { - FreeCode(it->first); - } - it = method_code_map_.erase(it); - } else { - ++it; - } - } - - auto osr_it = osr_code_map_.find(method); - if (osr_it != osr_code_map_.end()) { - osr_code_map_.erase(osr_it); - } - } - - return in_cache; -} - // This notifies the code cache that the given method has been redefined and that it should remove // any cached information it has on the method. All threads must be suspended before calling this // method. The compiled code for the method (if there is any) must not be in any threads call stack. void JitCodeCache::NotifyMethodRedefined(ArtMethod* method) { MutexLock mu(Thread::Current(), lock_); - RemoveMethodLocked(method, /* release_memory */ true); + if (method->IsNative()) { + return; + } + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + if (info != nullptr) { + auto profile = std::find(profiling_infos_.begin(), profiling_infos_.end(), info); + DCHECK(profile != profiling_infos_.end()); + profiling_infos_.erase(profile); + } + method->SetProfilingInfo(nullptr); + ScopedCodeCacheWrite ccw(code_map_.get()); + for (auto code_iter = method_code_map_.begin(); code_iter != method_code_map_.end();) { + if (code_iter->second == method) { + FreeCode(code_iter->first); + code_iter = method_code_map_.erase(code_iter); + continue; + } + ++code_iter; + } + auto code_map = osr_code_map_.find(method); + if (code_map != osr_code_map_.end()) { + osr_code_map_.erase(code_map); + } } // This invalidates old_method. Once this function returns one can no longer use old_method to @@ -939,15 +790,11 @@ void JitCodeCache::NotifyMethodRedefined(ArtMethod* method) { // shouldn't be used since it is no longer logically in the jit code cache. // TODO We should add DCHECKS that validate that the JIT is paused when this method is entered. void JitCodeCache::MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) { - MutexLock mu(Thread::Current(), lock_); + // Native methods have no profiling info and need no special handling from the JIT code cache. if (old_method->IsNative()) { - // Update methods in jni_stubs_map_. - for (auto& entry : jni_stubs_map_) { - JniStubData& data = entry.second; - data.MoveObsoleteMethod(old_method, new_method); - } return; } + MutexLock mu(Thread::Current(), lock_); // Update ProfilingInfo to the new one and remove it from the old_method. if (old_method->GetProfilingInfo(kRuntimePointerSize) != nullptr) { DCHECK_EQ(old_method->GetProfilingInfo(kRuntimePointerSize)->GetMethod(), old_method); @@ -1089,7 +936,7 @@ class MarkCodeClosure FINAL : public Closure { // its stack frame, it is not the method owning return_pc_. We just pass null to // LookupMethodHeader: the method is only checked against in debug builds. OatQuickMethodHeader* method_header = - code_cache_->LookupMethodHeader(frame.return_pc_, /* method */ nullptr); + code_cache_->LookupMethodHeader(frame.return_pc_, nullptr); if (method_header != nullptr) { const void* code = method_header->GetCode(); CHECK(code_cache_->GetLiveBitmap()->Test(FromCodeToAllocation(code))); @@ -1242,7 +1089,7 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { const void* entry_point = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); if (ContainsPc(entry_point)) { info->SetSavedEntryPoint(entry_point); - // Don't call Instrumentation::UpdateMethodsCode(), as it can check the declaring + // Don't call Instrumentation::UpdateMethods, as it can check the declaring // class of the method. We may be concurrently running a GC which makes accessing // the class unsafe. We know it is OK to bypass the instrumentation as we've just // checked that the current entry point is JIT compiled code. @@ -1251,25 +1098,6 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { } DCHECK(CheckLiveCompiledCodeHasProfilingInfo()); - - // Change entry points of native methods back to the GenericJNI entrypoint. - for (const auto& entry : jni_stubs_map_) { - const JniStubData& data = entry.second; - if (!data.IsCompiled()) { - continue; - } - // Make sure a single invocation of the GenericJNI trampoline tries to recompile. - uint16_t new_counter = Runtime::Current()->GetJit()->HotMethodThreshold() - 1u; - const OatQuickMethodHeader* method_header = - OatQuickMethodHeader::FromCodePointer(data.GetCode()); - for (ArtMethod* method : data.GetMethods()) { - if (method->GetEntryPointFromQuickCompiledCode() == method_header->GetEntryPoint()) { - // Don't call Instrumentation::UpdateMethodsCode(), same as for normal methods above. - method->SetCounter(new_counter); - method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub()); - } - } - } } live_bitmap_.reset(nullptr); NotifyCollectionDone(self); @@ -1285,22 +1113,13 @@ void JitCodeCache::RemoveUnmarkedCode(Thread* self) { MutexLock mu(self, lock_); ScopedCodeCacheWrite scc(code_map_.get()); // 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; - if (!data->IsCompiled() || GetLiveBitmap()->Test(FromCodeToAllocation(data->GetCode()))) { - ++it; - } else { - method_headers.insert(OatQuickMethodHeader::FromCodePointer(data->GetCode())); - it = jni_stubs_map_.erase(it); - } - } for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { const void* code_ptr = it->first; uintptr_t allocation = FromCodeToAllocation(code_ptr); if (GetLiveBitmap()->Test(allocation)) { ++it; } else { - method_headers.insert(OatQuickMethodHeader::FromCodePointer(code_ptr)); + method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->first)); it = method_code_map_.erase(it); } } @@ -1339,17 +1158,6 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { // an entry point is either: // - an osr compiled code, that will be removed if not in a thread call stack. // - discarded compiled code, that will be removed if not in a thread call stack. - for (const auto& entry : jni_stubs_map_) { - const JniStubData& data = entry.second; - const void* code_ptr = data.GetCode(); - const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); - for (ArtMethod* method : data.GetMethods()) { - if (method_header->GetEntryPoint() == method->GetEntryPointFromQuickCompiledCode()) { - GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr)); - break; - } - } - } for (const auto& it : method_code_map_) { ArtMethod* method = it.second; const void* code_ptr = it.first; @@ -1429,51 +1237,19 @@ OatQuickMethodHeader* JitCodeCache::LookupMethodHeader(uintptr_t pc, ArtMethod* return nullptr; } - if (!kIsDebugBuild) { - // Called with null `method` only from MarkCodeClosure::Run() in debug build. - CHECK(method != nullptr); - } - MutexLock mu(Thread::Current(), lock_); - OatQuickMethodHeader* method_header = nullptr; - ArtMethod* found_method = nullptr; // Only for DCHECK(), not for JNI stubs. - if (method != nullptr && UNLIKELY(method->IsNative())) { - auto it = jni_stubs_map_.find(JniStubKey(method)); - if (it == jni_stubs_map_.end() || !ContainsElement(it->second.GetMethods(), method)) { - return nullptr; - } - const void* code_ptr = it->second.GetCode(); - method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); - if (!method_header->Contains(pc)) { - return nullptr; - } - } else { - auto it = method_code_map_.lower_bound(reinterpret_cast(pc)); - if (it != method_code_map_.begin()) { - --it; - const void* code_ptr = it->first; - if (OatQuickMethodHeader::FromCodePointer(code_ptr)->Contains(pc)) { - method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); - found_method = it->second; - } - } - if (method_header == nullptr && method == nullptr) { - // Scan all compiled JNI stubs as well. This slow search is used only - // for checks in debug build, for release builds the `method` is not null. - for (auto&& entry : jni_stubs_map_) { - const JniStubData& data = entry.second; - if (data.IsCompiled() && - OatQuickMethodHeader::FromCodePointer(data.GetCode())->Contains(pc)) { - method_header = OatQuickMethodHeader::FromCodePointer(data.GetCode()); - } - } - } - if (method_header == nullptr) { - return nullptr; - } + if (method_code_map_.empty()) { + return nullptr; } + auto it = method_code_map_.lower_bound(reinterpret_cast(pc)); + --it; - if (kIsDebugBuild && method != nullptr && !method->IsNative()) { + const void* code_ptr = it->first; + OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + if (!method_header->Contains(pc)) { + return nullptr; + } + if (kIsDebugBuild && method != nullptr) { // When we are walking the stack to redefine classes and creating obsolete methods it is // possible that we might have updated the method_code_map by making this method obsolete in a // previous frame. Therefore we should just check that the non-obsolete version of this method @@ -1482,9 +1258,9 @@ OatQuickMethodHeader* JitCodeCache::LookupMethodHeader(uintptr_t pc, ArtMethod* // occur when we are in the process of allocating and setting up obsolete methods. Otherwise // method and it->second should be identical. (See openjdkjvmti/ti_redefine.cc for more // information.) - DCHECK_EQ(found_method->GetNonObsoleteMethod(), method->GetNonObsoleteMethod()) + DCHECK_EQ(it->second->GetNonObsoleteMethod(), method->GetNonObsoleteMethod()) << ArtMethod::PrettyMethod(method->GetNonObsoleteMethod()) << " " - << ArtMethod::PrettyMethod(found_method->GetNonObsoleteMethod()) << " " + << ArtMethod::PrettyMethod(it->second->GetNonObsoleteMethod()) << " " << std::hex << pc; } return method_header; @@ -1673,51 +1449,21 @@ bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr return false; } - if (UNLIKELY(method->IsNative())) { - JniStubKey key(method); - auto it = jni_stubs_map_.find(key); - bool new_compilation = false; - if (it == jni_stubs_map_.end()) { - // Create a new entry to mark the stub as being compiled. - it = jni_stubs_map_.Put(key, JniStubData{}); - new_compilation = true; - } - JniStubData* data = &it->second; - data->AddMethod(method); - if (data->IsCompiled()) { - OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(data->GetCode()); - const void* entrypoint = method_header->GetEntryPoint(); - // Update also entrypoints of other methods held by the JniStubData. - // We could simply update the entrypoint of `method` but if the last JIT GC has - // changed these entrypoints to GenericJNI in preparation for a full GC, we may - // as well change them back as this stub shall not be collected anyway and this - // can avoid a few expensive GenericJNI calls. - instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); - for (ArtMethod* m : data->GetMethods()) { - instrumentation->UpdateMethodsCode(m, entrypoint); - } - if (collection_in_progress_) { - GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(data->GetCode())); - } - } - return new_compilation; - } else { - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - if (info == nullptr) { - VLOG(jit) << method->PrettyMethod() << " needs a ProfilingInfo to be compiled"; - // Because the counter is not atomic, there are some rare cases where we may not hit the - // threshold for creating the ProfilingInfo. Reset the counter now to "correct" this. - ClearMethodCounter(method, /*was_warm*/ false); - return false; - } - - if (info->IsMethodBeingCompiled(osr)) { - return false; - } + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + if (info == nullptr) { + VLOG(jit) << method->PrettyMethod() << " needs a ProfilingInfo to be compiled"; + // Because the counter is not atomic, there are some rare cases where we may not hit the + // threshold for creating the ProfilingInfo. Reset the counter now to "correct" this. + ClearMethodCounter(method, /*was_warm*/ false); + return false; + } - info->SetIsMethodBeingCompiled(true, osr); - return true; + if (info->IsMethodBeingCompiled(osr)) { + return false; } + + info->SetIsMethodBeingCompiled(true, osr); + return true; } ProfilingInfo* JitCodeCache::NotifyCompilerUse(ArtMethod* method, Thread* self) { @@ -1739,23 +1485,10 @@ void JitCodeCache::DoneCompilerUse(ArtMethod* method, Thread* self) { info->DecrementInlineUse(); } -void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self, bool osr) { - DCHECK_EQ(Thread::Current(), self); - MutexLock mu(self, lock_); - if (UNLIKELY(method->IsNative())) { - auto it = jni_stubs_map_.find(JniStubKey(method)); - DCHECK(it != jni_stubs_map_.end()); - JniStubData* data = &it->second; - DCHECK(ContainsElement(data->GetMethods(), method)); - if (UNLIKELY(!data->IsCompiled())) { - // Failed to compile; the JNI compiler never fails, but the cache may be full. - jni_stubs_map_.erase(it); // Remove the entry added in NotifyCompilationOf(). - } // else CommitCodeInternal() updated entrypoints of all methods in the JniStubData. - } else { - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - DCHECK(info->IsMethodBeingCompiled(osr)); - info->SetIsMethodBeingCompiled(false, osr); - } +void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self ATTRIBUTE_UNUSED, bool osr) { + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + DCHECK(info->IsMethodBeingCompiled(osr)); + info->SetIsMethodBeingCompiled(false, osr); } size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) { @@ -1765,7 +1498,6 @@ size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) { void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method, const OatQuickMethodHeader* header) { - DCHECK(!method->IsNative()); ProfilingInfo* profiling_info = method->GetProfilingInfo(kRuntimePointerSize); if ((profiling_info != nullptr) && (profiling_info->GetSavedEntryPoint() == header->GetEntryPoint())) { @@ -1821,7 +1553,6 @@ void JitCodeCache::Dump(std::ostream& os) { os << "Current JIT code cache size: " << PrettySize(used_memory_for_code_) << "\n" << "Current JIT data cache size: " << PrettySize(used_memory_for_data_) << "\n" << "Current JIT capacity: " << PrettySize(current_capacity_) << "\n" - << "Current number of JIT JNI stub entries: " << jni_stubs_map_.size() << "\n" << "Current number of JIT code cache entries: " << method_code_map_.size() << "\n" << "Total number of JIT compilations: " << number_of_compilations_ << "\n" << "Total number of JIT compilations for on stack replacement: " diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index fc011ddb96..46a408590b 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -35,23 +35,9 @@ template class Handle; class LinearAlloc; class InlineCache; class IsMarkedVisitor; -class JitJniStubTestHelper; class OatQuickMethodHeader; struct ProfileMethodInfo; class ProfilingInfo; -class Thread; - -namespace gc { -namespace accounting { -template class MemoryRangeBitmap; -} // namespace accounting -} // namespace gc - -namespace mirror { -class Class; -class Object; -template class ObjectArray; -} // namespace mirror namespace gc { namespace accounting { @@ -151,9 +137,6 @@ class JitCodeCache { // Return true if the code cache contains this method. bool ContainsMethod(ArtMethod* method) REQUIRES(!lock_); - // Return the code pointer for a JNI-compiled stub if the method is in the cache, null otherwise. - const void* GetJniStubCode(ArtMethod* method) REQUIRES(!lock_); - // Allocate a region of data that contain `size` bytes, and potentially space // for storing `number_of_roots` roots. Returns null if there is no more room. // Return the number of bytes allocated. @@ -177,6 +160,11 @@ class JitCodeCache { return live_bitmap_.get(); } + // Return whether we should do a full collection given the current state of the cache. + bool ShouldDoFullCollection() + REQUIRES(lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + // Perform a collection on the code cache. void GarbageCollectCache(Thread* self) REQUIRES(!lock_) @@ -308,12 +296,6 @@ class JitCodeCache { REQUIRES(!lock_) REQUIRES(!Locks::cha_lock_); - // Removes method from the cache. The caller must ensure that all threads - // are suspended and the method should not be in any thread's stack. - bool RemoveMethodLocked(ArtMethod* method, bool release_memory) - REQUIRES(lock_) - REQUIRES(Locks::mutator_lock_); - // Free in the mspace allocations for `code_ptr`. void FreeCode(const void* code_ptr) REQUIRES(lock_); @@ -333,11 +315,6 @@ class JitCodeCache { // Set the footprint limit of the code cache. void SetFootprintLimit(size_t new_footprint) REQUIRES(lock_); - // Return whether we should do a full collection given the current state of the cache. - bool ShouldDoFullCollection() - REQUIRES(lock_) - REQUIRES_SHARED(Locks::mutator_lock_); - void DoCollection(Thread* self, bool collect_profiling_info) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -364,9 +341,6 @@ class JitCodeCache { REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); - class JniStubKey; - class JniStubData; - // Lock for guarding allocations, collections, and the method_code_map_. Mutex lock_; // Condition to wait on during collection. @@ -383,8 +357,6 @@ class JitCodeCache { void* data_mspace_ GUARDED_BY(lock_); // Bitmap for collecting code and data. std::unique_ptr live_bitmap_; - // Holds compiled code associated with the shorty for a JNI stub. - SafeMap jni_stubs_map_ GUARDED_BY(lock_); // Holds compiled code associated to the ArtMethod. SafeMap method_code_map_ GUARDED_BY(lock_); // Holds osr compiled code associated to the ArtMethod. @@ -446,7 +418,6 @@ class JitCodeCache { // Condition to wait on for accessing inline caches. ConditionVariable inline_cache_cond_ GUARDED_BY(lock_); - friend class art::JitJniStubTestHelper; DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache); }; diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index acbc6e63a4..01853de403 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -357,8 +357,8 @@ static void SampleClassesAndExecutedMethods(pthread_t profiler_pthread, sampled_methods->AddReference(method.GetDexFile(), method.GetDexMethodIndex()); } } else { - // We do not record native methods. Once we AOT-compile the app, all native - // methods shall have their thunks compiled. + CHECK_EQ(method.GetCounter(), 0u) << method.PrettyMethod() + << " access_flags=" << method.GetAccessFlags(); } } } diff --git a/runtime/managed_stack-inl.h b/runtime/managed_stack-inl.h index 678be8e098..689dd8009a 100644 --- a/runtime/managed_stack-inl.h +++ b/runtime/managed_stack-inl.h @@ -24,7 +24,7 @@ namespace art { inline ShadowFrame* ManagedStack::PushShadowFrame(ShadowFrame* new_top_frame) { - DCHECK(!HasTopQuickFrame()); + DCHECK(top_quick_frame_ == nullptr); ShadowFrame* old_frame = top_shadow_frame_; top_shadow_frame_ = new_top_frame; new_top_frame->SetLink(old_frame); @@ -32,7 +32,7 @@ inline ShadowFrame* ManagedStack::PushShadowFrame(ShadowFrame* new_top_frame) { } inline ShadowFrame* ManagedStack::PopShadowFrame() { - DCHECK(!HasTopQuickFrame()); + DCHECK(top_quick_frame_ == nullptr); CHECK(top_shadow_frame_ != nullptr); ShadowFrame* frame = top_shadow_frame_; top_shadow_frame_ = frame->GetLink(); diff --git a/runtime/managed_stack.h b/runtime/managed_stack.h index 07078ecb13..4f1984d55a 100644 --- a/runtime/managed_stack.h +++ b/runtime/managed_stack.h @@ -24,7 +24,6 @@ #include "base/logging.h" #include "base/macros.h" #include "base/mutex.h" -#include "base/bit_utils.h" namespace art { @@ -43,9 +42,7 @@ template class StackReference; class PACKED(4) ManagedStack { public: ManagedStack() - : tagged_top_quick_frame_(TaggedTopQuickFrame::CreateNotTagged(nullptr)), - link_(nullptr), - top_shadow_frame_(nullptr) {} + : top_quick_frame_(nullptr), link_(nullptr), top_shadow_frame_(nullptr) {} void PushManagedStackFragment(ManagedStack* fragment) { // Copy this top fragment into given fragment. @@ -66,36 +63,17 @@ class PACKED(4) ManagedStack { return link_; } - ArtMethod** GetTopQuickFrameKnownNotTagged() const { - return tagged_top_quick_frame_.GetSpKnownNotTagged(); - } - ArtMethod** GetTopQuickFrame() const { - return tagged_top_quick_frame_.GetSp(); - } - - bool GetTopQuickFrameTag() const { - return tagged_top_quick_frame_.GetTag(); - } - - bool HasTopQuickFrame() const { - return tagged_top_quick_frame_.GetTaggedSp() != 0u; + return top_quick_frame_; } void SetTopQuickFrame(ArtMethod** top) { DCHECK(top_shadow_frame_ == nullptr); - DCHECK_ALIGNED(top, 4u); - tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateNotTagged(top); + top_quick_frame_ = top; } - void SetTopQuickFrameTagged(ArtMethod** top) { - DCHECK(top_shadow_frame_ == nullptr); - DCHECK_ALIGNED(top, 4u); - tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateTagged(top); - } - - static size_t TaggedTopQuickFrameOffset() { - return OFFSETOF_MEMBER(ManagedStack, tagged_top_quick_frame_); + static size_t TopQuickFrameOffset() { + return OFFSETOF_MEMBER(ManagedStack, top_quick_frame_); } ALWAYS_INLINE ShadowFrame* PushShadowFrame(ShadowFrame* new_top_frame); @@ -105,12 +83,8 @@ class PACKED(4) ManagedStack { return top_shadow_frame_; } - bool HasTopShadowFrame() const { - return GetTopShadowFrame() != nullptr; - } - void SetTopShadowFrame(ShadowFrame* top) { - DCHECK_EQ(tagged_top_quick_frame_.GetTaggedSp(), 0u); + DCHECK(top_quick_frame_ == nullptr); top_shadow_frame_ = top; } @@ -123,47 +97,7 @@ class PACKED(4) ManagedStack { bool ShadowFramesContain(StackReference* shadow_frame_entry) const; private: - // Encodes the top quick frame (which must be at least 4-byte aligned) - // and a flag that marks the GenericJNI trampoline. - class TaggedTopQuickFrame { - public: - static TaggedTopQuickFrame CreateNotTagged(ArtMethod** sp) { - DCHECK_ALIGNED(sp, 4u); - return TaggedTopQuickFrame(reinterpret_cast(sp)); - } - - static TaggedTopQuickFrame CreateTagged(ArtMethod** sp) { - DCHECK_ALIGNED(sp, 4u); - return TaggedTopQuickFrame(reinterpret_cast(sp) | 1u); - } - - // Get SP known to be not tagged and non-null. - ArtMethod** GetSpKnownNotTagged() const { - DCHECK(!GetTag()); - DCHECK_NE(tagged_sp_, 0u); - return reinterpret_cast(tagged_sp_); - } - - ArtMethod** GetSp() const { - return reinterpret_cast(tagged_sp_ & ~static_cast(1u)); - } - - bool GetTag() const { - return (tagged_sp_ & 1u) != 0u; - } - - uintptr_t GetTaggedSp() const { - return tagged_sp_; - } - - private: - explicit TaggedTopQuickFrame(uintptr_t tagged_sp) : tagged_sp_(tagged_sp) { } - - uintptr_t tagged_sp_; - }; - static_assert(sizeof(TaggedTopQuickFrame) == sizeof(uintptr_t), "TaggedTopQuickFrame size check"); - - TaggedTopQuickFrame tagged_top_quick_frame_; + ArtMethod** top_quick_frame_; ManagedStack* link_; ShadowFrame* top_shadow_frame_; }; diff --git a/runtime/stack.cc b/runtime/stack.cc index 5ad1f7c9c5..ab9fb0d73f 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -735,19 +735,12 @@ QuickMethodFrameInfo StackVisitor::GetCurrentQuickFrameInfo() const { return runtime->GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); } - // The only remaining case is if the method is native and uses the generic JNI stub, - // called either directly or through some (resolution, instrumentation) trampoline. + // The only remaining case is if the method is native and uses the generic JNI stub. DCHECK(method->IsNative()); - if (kIsDebugBuild) { - ClassLinker* class_linker = runtime->GetClassLinker(); - const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(method, - kRuntimePointerSize); - CHECK(class_linker->IsQuickGenericJniStub(entry_point) || - // The current entrypoint (after filtering out trampolines) may have changed - // from GenericJNI to JIT-compiled stub since we have entered this frame. - (runtime->GetJit() != nullptr && - runtime->GetJit()->GetCodeCache()->ContainsPc(entry_point))) << method->PrettyMethod(); - } + ClassLinker* class_linker = runtime->GetClassLinker(); + const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(method, + kRuntimePointerSize); + DCHECK(class_linker->IsQuickGenericJniStub(entry_point)) << method->PrettyMethod(); // Generic JNI frame. uint32_t handle_refs = GetNumberOfReferenceArgsWithoutReceiver(method) + 1; size_t scope_size = HandleScope::SizeOf(handle_refs); @@ -783,48 +776,8 @@ void StackVisitor::WalkStack(bool include_transitions) { // Can't be both a shadow and a quick fragment. DCHECK(current_fragment->GetTopShadowFrame() == nullptr); ArtMethod* method = *cur_quick_frame_; - DCHECK(method != nullptr); - bool header_retrieved = false; - if (method->IsNative()) { - // We do not have a PC for the first frame, so we cannot simply use - // ArtMethod::GetOatQuickMethodHeader() as we're unable to distinguish there - // between GenericJNI frame and JIT-compiled JNI stub; the entrypoint may have - // changed since the frame was entered. The top quick frame tag indicates - // GenericJNI here, otherwise it's either AOT-compiled or JNI-compiled JNI stub. - if (UNLIKELY(current_fragment->GetTopQuickFrameTag())) { - // The generic JNI does not have any method header. - cur_oat_quick_method_header_ = nullptr; - } else { - const void* existing_entry_point = method->GetEntryPointFromQuickCompiledCode(); - CHECK(existing_entry_point != nullptr); - Runtime* runtime = Runtime::Current(); - ClassLinker* class_linker = runtime->GetClassLinker(); - // Check whether we can quickly get the header from the current entrypoint. - if (!class_linker->IsQuickGenericJniStub(existing_entry_point) && - !class_linker->IsQuickResolutionStub(existing_entry_point) && - existing_entry_point != GetQuickInstrumentationEntryPoint()) { - cur_oat_quick_method_header_ = - OatQuickMethodHeader::FromEntryPoint(existing_entry_point); - } else { - const void* code = method->GetOatMethodQuickCode(class_linker->GetImagePointerSize()); - if (code != nullptr) { - cur_oat_quick_method_header_ = OatQuickMethodHeader::FromEntryPoint(code); - } else { - // This must be a JITted JNI stub frame. - CHECK(runtime->GetJit() != nullptr); - code = runtime->GetJit()->GetCodeCache()->GetJniStubCode(method); - CHECK(code != nullptr) << method->PrettyMethod(); - cur_oat_quick_method_header_ = OatQuickMethodHeader::FromCodePointer(code); - } - } - } - header_retrieved = true; - } while (method != nullptr) { - if (!header_retrieved) { - cur_oat_quick_method_header_ = method->GetOatQuickMethodHeader(cur_quick_frame_pc_); - } - header_retrieved = false; // Force header retrieval in next iteration. + cur_oat_quick_method_header_ = method->GetOatQuickMethodHeader(cur_quick_frame_pc_); SanityCheckFrame(); if ((walk_kind_ == StackWalkKind::kIncludeInlinedFrames) diff --git a/runtime/stack.h b/runtime/stack.h index a16930bba0..bd6204f8d2 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -140,7 +140,8 @@ class StackVisitor { }; template - void WalkStack(bool include_transitions = false) REQUIRES_SHARED(Locks::mutator_lock_); + void WalkStack(bool include_transitions = false) + REQUIRES_SHARED(Locks::mutator_lock_); Thread* GetThread() const { return thread_; diff --git a/runtime/thread.cc b/runtime/thread.cc index bec1c908ad..712eabc888 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1884,7 +1884,9 @@ static bool ShouldShowNativeStack(const Thread* thread) } // Threads with no managed stack frames should be shown. - if (!thread->HasManagedStack()) { + const ManagedStack* managed_stack = thread->GetManagedStack(); + if (managed_stack == nullptr || (managed_stack->GetTopQuickFrame() == nullptr && + managed_stack->GetTopShadowFrame() == nullptr)) { return true; } diff --git a/runtime/thread.h b/runtime/thread.h index 0803975d26..39be66d5c2 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -474,16 +474,13 @@ class Thread { tlsPtr_.managed_stack.SetTopQuickFrame(top_method); } - void SetTopOfStackTagged(ArtMethod** top_method) { - tlsPtr_.managed_stack.SetTopQuickFrameTagged(top_method); - } - void SetTopOfShadowStack(ShadowFrame* top) { tlsPtr_.managed_stack.SetTopShadowFrame(top); } bool HasManagedStack() const { - return tlsPtr_.managed_stack.HasTopQuickFrame() || tlsPtr_.managed_stack.HasTopShadowFrame(); + return (tlsPtr_.managed_stack.GetTopQuickFrame() != nullptr) || + (tlsPtr_.managed_stack.GetTopShadowFrame() != nullptr); } // If 'msg' is null, no detail message is set. @@ -836,7 +833,7 @@ class Thread { static ThreadOffset TopOfManagedStackOffset() { return ThreadOffsetFromTlsPtr( OFFSETOF_MEMBER(tls_ptr_sized_values, managed_stack) + - ManagedStack::TaggedTopQuickFrameOffset()); + ManagedStack::TopQuickFrameOffset()); } const ManagedStack* GetManagedStack() const { diff --git a/test/655-jit-clinit/src/Main.java b/test/655-jit-clinit/src/Main.java index 2fb8f2a86e..44b315478f 100644 --- a/test/655-jit-clinit/src/Main.java +++ b/test/655-jit-clinit/src/Main.java @@ -23,7 +23,7 @@ public class Main { Foo.hotMethod(); } - public native static boolean hasJitCompiledEntrypoint(Class cls, String methodName); + public native static boolean isJitCompiled(Class cls, String methodName); private native static boolean hasJit(); } @@ -36,7 +36,7 @@ class Foo { static { array = new Object[10000]; - while (!Main.hasJitCompiledEntrypoint(Foo.class, "hotMethod")) { + while (!Main.isJitCompiled(Foo.class, "hotMethod")) { Foo.hotMethod(); try { // Sleep to give a chance for the JIT to compile `hotMethod`. diff --git a/test/667-jit-jni-stub/expected.txt b/test/667-jit-jni-stub/expected.txt deleted file mode 100644 index 6a5618ebc6..0000000000 --- a/test/667-jit-jni-stub/expected.txt +++ /dev/null @@ -1 +0,0 @@ -JNI_OnLoad called diff --git a/test/667-jit-jni-stub/info.txt b/test/667-jit-jni-stub/info.txt deleted file mode 100644 index 6f25c44592..0000000000 --- a/test/667-jit-jni-stub/info.txt +++ /dev/null @@ -1 +0,0 @@ -Tests for JITting and collecting JNI stubs. diff --git a/test/667-jit-jni-stub/jit_jni_stub_test.cc b/test/667-jit-jni-stub/jit_jni_stub_test.cc deleted file mode 100644 index 82e06fc018..0000000000 --- a/test/667-jit-jni-stub/jit_jni_stub_test.cc +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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. - */ - -#include - -#include "jit/jit.h" -#include "jit/jit_code_cache.h" -#include "mirror/class.h" -#include "mirror/string.h" -#include "runtime.h" -#include "scoped_thread_state_change-inl.h" - -namespace art { - -// Local class declared as a friend of JitCodeCache so that we can access its internals. -class JitJniStubTestHelper { - public: - static bool isNextJitGcFull(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - CHECK(Runtime::Current()->GetJit() != nullptr); - jit::JitCodeCache* cache = Runtime::Current()->GetJit()->GetCodeCache(); - MutexLock mu(self, cache->lock_); - return cache->ShouldDoFullCollection(); - } -}; - -// Calls through to a static method with signature "()V". -extern "C" JNIEXPORT -void Java_Main_callThrough(JNIEnv* env, jclass, jclass klass, jstring methodName) { - ScopedObjectAccess soa(Thread::Current()); - std::string name = soa.Decode(methodName)->ToModifiedUtf8(); - jmethodID method = env->GetStaticMethodID(klass, name.c_str(), "()V"); - CHECK(method != nullptr) << soa.Decode(klass)->PrettyDescriptor() << "." << name; - env->CallStaticVoidMethod(klass, method); -} - -extern "C" JNIEXPORT -void Java_Main_jitGc(JNIEnv*, jclass) { - CHECK(Runtime::Current()->GetJit() != nullptr); - jit::JitCodeCache* cache = Runtime::Current()->GetJit()->GetCodeCache(); - ScopedObjectAccess soa(Thread::Current()); - cache->GarbageCollectCache(Thread::Current()); -} - -extern "C" JNIEXPORT -jboolean Java_Main_isNextJitGcFull(JNIEnv*, jclass) { - ScopedObjectAccess soa(Thread::Current()); - return JitJniStubTestHelper::isNextJitGcFull(soa.Self()); -} - -} // namespace art diff --git a/test/667-jit-jni-stub/run b/test/667-jit-jni-stub/run deleted file mode 100755 index 1877be482e..0000000000 --- a/test/667-jit-jni-stub/run +++ /dev/null @@ -1,18 +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. - -# Disable AOT compilation of JNI stubs. -${RUN} "${@}" --no-prebuild --no-dex2oat diff --git a/test/667-jit-jni-stub/src/Main.java b/test/667-jit-jni-stub/src/Main.java deleted file mode 100644 index b867970eab..0000000000 --- a/test/667-jit-jni-stub/src/Main.java +++ /dev/null @@ -1,180 +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. - */ - -public class Main { - public static void main(String[] args) throws Exception { - System.loadLibrary(args[0]); - if (isAotCompiled(Main.class, "hasJit")) { - throw new Error("This test must be run with --no-prebuild --no-dex2oat!"); - } - if (!hasJit()) { - return; - } - - testCompilationUseAndCollection(); - testMixedFramesOnStack(); - } - - public static void testCompilationUseAndCollection() { - // Test that callThrough() can be JIT-compiled. - assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); - assertFalse(hasJitCompiledCode(Main.class, "callThrough")); - ensureCompiledCallThroughEntrypoint(/* call */ true); - assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); - assertTrue(hasJitCompiledCode(Main.class, "callThrough")); - - // Use callThrough() once again now that the method has a JIT-compiled stub. - callThrough(Main.class, "doNothing"); - - // Test that GC with the JIT-compiled stub on the stack does not collect it. - // Also tests stack walk over the JIT-compiled stub. - callThrough(Main.class, "testGcWithCallThroughStubOnStack"); - - // Test that, when marking used methods before a full JIT GC, a single execution - // of the GenericJNI trampoline can save the compiled stub from being collected. - testSingleInvocationTriggersRecompilation(); - - // Test that the JNI compiled stub can actually be collected. - testStubCanBeCollected(); - } - - public static void testGcWithCallThroughStubOnStack() { - // Check that this method was called via JIT-compiled callThrough() stub. - assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); - // This assertion also exercises stack walk over the JIT-compiled callThrough() stub. - assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); - - doJitGcsUntilFullJitGcIsScheduled(); - // The callThrough() on the stack above this method is using the compiled stub, - // so the JIT GC should not remove the compiled code. - jitGc(); - assertTrue(hasJitCompiledCode(Main.class, "callThrough")); - } - - public static void testSingleInvocationTriggersRecompilation() { - // After scheduling a full JIT GC, single call through the GenericJNI - // trampoline should ensure that the compiled stub is used again. - doJitGcsUntilFullJitGcIsScheduled(); - callThrough(Main.class, "doNothing"); - ensureCompiledCallThroughEntrypoint(/* call */ false); // Wait for the compilation task to run. - assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); - jitGc(); // This JIT GC should not collect the callThrough() stub. - assertTrue(hasJitCompiledCode(Main.class, "callThrough")); - } - - public static void testMixedFramesOnStack() { - // Starts without a compiled JNI stub for callThrough(). - assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); - assertFalse(hasJitCompiledCode(Main.class, "callThrough")); - callThrough(Main.class, "testMixedFramesOnStackStage2"); - // We have just returned through the JIT-compiled JNI stub, so it must still - // be compiled (though not necessarily with the entrypoint pointing to it). - assertTrue(hasJitCompiledCode(Main.class, "callThrough")); - // Though the callThrough() is on the stack, that frame is using the GenericJNI - // and does not prevent the collection of the JNI stub. - testStubCanBeCollected(); - } - - public static void testMixedFramesOnStackStage2() { - // We cannot assert that callThrough() has no JIT compiled stub as that check - // may race against the compilation task. Just check the caller. - assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); - // Now ensure that the JNI stub is compiled and used. - ensureCompiledCallThroughEntrypoint(/* call */ true); - callThrough(Main.class, "testMixedFramesOnStackStage3"); - } - - public static void testMixedFramesOnStackStage3() { - // Check that this method was called via JIT-compiled callThrough() stub. - assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); - // This assertion also exercises stack walk over the JIT-compiled callThrough() stub. - assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); - // For a good measure, try a JIT GC. - jitGc(); - } - - public static void testStubCanBeCollected() { - assertTrue(hasJitCompiledCode(Main.class, "callThrough")); - doJitGcsUntilFullJitGcIsScheduled(); - assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); - assertTrue(hasJitCompiledCode(Main.class, "callThrough")); - jitGc(); // JIT GC without callThrough() on the stack should collect the callThrough() stub. - assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); - assertFalse(hasJitCompiledCode(Main.class, "callThrough")); - } - - public static void doJitGcsUntilFullJitGcIsScheduled() { - // We enter with a compiled stub for callThrough() but we also need the entrypoint to be set. - assertTrue(hasJitCompiledCode(Main.class, "callThrough")); - ensureCompiledCallThroughEntrypoint(/* call */ true); - // Perform JIT GC until the next GC is marked to do full collection. - do { - assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); - callThrough(Main.class, "jitGc"); // JIT GC with callThrough() safely on the stack. - } while (!isNextJitGcFull()); - // The JIT GC before the full collection resets entrypoints and waits to see - // if the methods are still in use. - assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); - assertTrue(hasJitCompiledCode(Main.class, "callThrough")); - } - - public static void ensureCompiledCallThroughEntrypoint(boolean call) { - int count = 0; - while (!hasJitCompiledEntrypoint(Main.class, "callThrough")) { - // If `call` is true, also exercise the `callThrough()` method to increase hotness. - int limit = call ? 1 << Math.min(count, 12) : 0; - for (int i = 0; i < limit; ++i) { - callThrough(Main.class, "doNothing"); - } - try { - // Sleep to give a chance for the JIT to compile `hasJit` stub. - Thread.sleep(100); - } catch (Exception e) { - // Ignore - } - if (++count == 50) { - throw new Error("TIMEOUT"); - } - }; - } - - public static void assertTrue(boolean value) { - if (!value) { - throw new AssertionError("Expected true!"); - } - } - - public static void assertFalse(boolean value) { - if (value) { - throw new AssertionError("Expected false!"); - } - } - - public static void doNothing() { } - public static void throwError() { throw new Error(); } - - // Note that the callThrough()'s shorty differs from shorties of the other - // native methods used in this test because of the return type `void.` - public native static void callThrough(Class cls, String methodName); - - public native static void jitGc(); - public native static boolean isNextJitGcFull(); - - public native static boolean isAotCompiled(Class cls, String methodName); - public native static boolean hasJitCompiledEntrypoint(Class cls, String methodName); - public native static boolean hasJitCompiledCode(Class cls, String methodName); - private native static boolean hasJit(); -} diff --git a/test/Android.bp b/test/Android.bp index 2d526d256c..8f29251907 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -384,7 +384,6 @@ cc_defaults { "656-annotation-lookup-generic-jni/test.cc", "661-oat-writer-layout/oat_writer_layout.cc", "664-aget-verifier/aget-verifier.cc", - "667-jit-jni-stub/jit_jni_stub_test.cc", "708-jit-cache-churn/jit.cc", ], shared_libs: [ diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index 34580800cc..df497c1181 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -152,10 +152,10 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isAotCompiled(JNIEnv* env, return method->GetOatMethodQuickCode(kRuntimePointerSize) != nullptr; } -extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledEntrypoint(JNIEnv* env, - jclass, - jclass cls, - jstring method_name) { +extern "C" JNIEXPORT jboolean JNICALL Java_Main_isJitCompiled(JNIEnv* env, + jclass, + jclass cls, + jstring method_name) { jit::Jit* jit = GetJitIfEnabled(); if (jit == nullptr) { return false; @@ -169,23 +169,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledEntrypoint(JNIEnv* return jit->GetCodeCache()->ContainsPc(method->GetEntryPointFromQuickCompiledCode()); } -extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledCode(JNIEnv* env, - jclass, - jclass cls, - jstring method_name) { - jit::Jit* jit = GetJitIfEnabled(); - if (jit == nullptr) { - return false; - } - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); - ScopedUtfChars chars(env, method_name); - CHECK(chars.c_str() != nullptr); - ArtMethod* method = soa.Decode(cls)->FindDeclaredDirectMethodByName( - chars.c_str(), kRuntimePointerSize); - return jit->GetCodeCache()->ContainsMethod(method); -} - extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env, jclass, jclass cls, -- GitLab From aaf0d38d9e3bd35aaf5ea2be6409bf2f3575f0ad Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 27 Nov 2017 14:10:21 -0800 Subject: [PATCH 088/226] ART: Change lock dumping Return dex registers as well as dex PCs from FindLocksAtDexPc. It is necessary to return the registers as the register used for a monitor-enter operation may be aliased and overwritten by the time the requested dex PC is reached. It is at this point necessary to return a set of registers as optimizations may have eliminated dead registers, and the verifier has no notion of liveness. A client of FindLocksAtDexPc should not assume that all registers can be successfully retrieved from a stack frame, with the possibility of none. Only a properly verified method (i.e., lock verification succeeded) implies that at least one register should work for all locks held at any point in the program. Bug: 68703210 Test: art/test/testrunner/testrunner.py -b --host -t 167 Change-Id: I9027787e395cf8df0e7699a606665edb2ecb5136 --- runtime/monitor.cc | 50 +++++---- runtime/verifier/method_verifier.cc | 22 +++- runtime/verifier/method_verifier.h | 15 ++- runtime/verifier/register_line.h | 17 ++++ test/167-visit-locks/expected.txt | 3 + test/167-visit-locks/info.txt | 1 + test/167-visit-locks/run | 18 ++++ test/167-visit-locks/smali/TestSync.smali | 119 ++++++++++++++++++++++ test/167-visit-locks/src/Main.java | 29 ++++++ test/167-visit-locks/visit_locks.cc | 74 ++++++++++++++ test/Android.bp | 3 +- 11 files changed, 325 insertions(+), 26 deletions(-) create mode 100644 test/167-visit-locks/expected.txt create mode 100644 test/167-visit-locks/info.txt create mode 100644 test/167-visit-locks/run create mode 100644 test/167-visit-locks/smali/TestSync.smali create mode 100644 test/167-visit-locks/src/Main.java create mode 100644 test/167-visit-locks/visit_locks.cc diff --git a/runtime/monitor.cc b/runtime/monitor.cc index cdc55bd45c..d5520d9aca 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -1401,26 +1401,38 @@ void Monitor::VisitLocks(StackVisitor* stack_visitor, void (*callback)(mirror::O // Ask the verifier for the dex pcs of all the monitor-enter instructions corresponding to // the locks held in this stack frame. - std::vector monitor_enter_dex_pcs; + std::vector monitor_enter_dex_pcs; verifier::MethodVerifier::FindLocksAtDexPc(m, dex_pc, &monitor_enter_dex_pcs); - for (uint32_t monitor_dex_pc : monitor_enter_dex_pcs) { - // The verifier works in terms of the dex pcs of the monitor-enter instructions. - // We want the registers used by those instructions (so we can read the values out of them). - const Instruction* monitor_enter_instruction = - Instruction::At(&code_item->insns_[monitor_dex_pc]); - - // Quick sanity check. - CHECK_EQ(monitor_enter_instruction->Opcode(), Instruction::MONITOR_ENTER) - << "expected monitor-enter @" << monitor_dex_pc << "; was " - << reinterpret_cast(monitor_enter_instruction); - - uint16_t monitor_register = monitor_enter_instruction->VRegA(); - uint32_t value; - bool success = stack_visitor->GetVReg(m, monitor_register, kReferenceVReg, &value); - CHECK(success) << "Failed to read v" << monitor_register << " of kind " - << kReferenceVReg << " in method " << m->PrettyMethod(); - mirror::Object* o = reinterpret_cast(value); - callback(o, callback_context); + for (verifier::MethodVerifier::DexLockInfo& dex_lock_info : monitor_enter_dex_pcs) { + // As a debug check, check that dex PC corresponds to a monitor-enter. + if (kIsDebugBuild) { + const Instruction* monitor_enter_instruction = + Instruction::At(&code_item->insns_[dex_lock_info.dex_pc]); + CHECK_EQ(monitor_enter_instruction->Opcode(), Instruction::MONITOR_ENTER) + << "expected monitor-enter @" << dex_lock_info.dex_pc << "; was " + << reinterpret_cast(monitor_enter_instruction); + } + + // Iterate through the set of dex registers, as the compiler may not have held all of them + // live. + bool success = false; + for (uint32_t dex_reg : dex_lock_info.dex_registers) { + uint32_t value; + success = stack_visitor->GetVReg(m, dex_reg, kReferenceVReg, &value); + if (success) { + mirror::Object* o = reinterpret_cast(value); + callback(o, callback_context); + break; + } + } + DCHECK(success) << "Failed to find/read reference for monitor-enter at dex pc " + << dex_lock_info.dex_pc + << " in method " + << m->PrettyMethod(); + if (!success) { + LOG(WARNING) << "Had a lock reported for dex pc " << dex_lock_info.dex_pc + << " but was not able to fetch a corresponding object!"; + } } } diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index a75157d6b3..c6ba2f7e43 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -592,8 +592,10 @@ MethodVerifier::~MethodVerifier() { STLDeleteElements(&failure_messages_); } -void MethodVerifier::FindLocksAtDexPc(ArtMethod* m, uint32_t dex_pc, - std::vector* monitor_enter_dex_pcs) { +void MethodVerifier::FindLocksAtDexPc( + ArtMethod* m, + uint32_t dex_pc, + std::vector* monitor_enter_dex_pcs) { StackHandleScope<2> hs(Thread::Current()); Handle dex_cache(hs.NewHandle(m->GetDexCache())); Handle class_loader(hs.NewHandle(m->GetClassLoader())); @@ -2036,8 +2038,20 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { // for a thread to be suspended). if (monitor_enter_dex_pcs_ != nullptr && work_insn_idx_ == interesting_dex_pc_) { monitor_enter_dex_pcs_->clear(); // The new work line is more accurate than the previous one. - for (size_t i = 0; i < work_line_->GetMonitorEnterCount(); ++i) { - monitor_enter_dex_pcs_->push_back(work_line_->GetMonitorEnterDexPc(i)); + + std::map depth_to_lock_info; + auto collector = [&](uint32_t dex_reg, uint32_t depth) { + auto insert_pair = depth_to_lock_info.emplace(depth, DexLockInfo(depth)); + auto it = insert_pair.first; + auto set_insert_pair = it->second.dex_registers.insert(dex_reg); + DCHECK(set_insert_pair.second); + }; + work_line_->IterateRegToLockDepths(collector); + for (auto& pair : depth_to_lock_info) { + monitor_enter_dex_pcs_->push_back(pair.second); + // Map depth to dex PC. + (*monitor_enter_dex_pcs_)[monitor_enter_dex_pcs_->size() - 1].dex_pc = + work_line_->GetMonitorEnterDexPc(pair.second.dex_pc); } } diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 813ce87175..c885914633 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -149,10 +149,21 @@ class MethodVerifier { void Dump(std::ostream& os) REQUIRES_SHARED(Locks::mutator_lock_); void Dump(VariableIndentationOutputStream* vios) REQUIRES_SHARED(Locks::mutator_lock_); + // Information structure for a lock held at a certain point in time. + struct DexLockInfo { + // The registers aliasing the lock. + std::set dex_registers; + // The dex PC of the monitor-enter instruction. + uint32_t dex_pc; + + explicit DexLockInfo(uint32_t dex_pc_in) { + dex_pc = dex_pc_in; + } + }; // 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'. static void FindLocksAtDexPc(ArtMethod* m, uint32_t dex_pc, - std::vector* monitor_enter_dex_pcs) + std::vector* monitor_enter_dex_pcs) REQUIRES_SHARED(Locks::mutator_lock_); // Returns the accessed field corresponding to the quick instruction's field @@ -750,7 +761,7 @@ class MethodVerifier { uint32_t interesting_dex_pc_; // The container into which FindLocksAtDexPc should write the registers containing held locks, // null if we're not doing FindLocksAtDexPc. - std::vector* monitor_enter_dex_pcs_; + std::vector* monitor_enter_dex_pcs_; // The types of any error that occurs. std::vector failures_; diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h index 221aa80e43..71eb4d6ac7 100644 --- a/runtime/verifier/register_line.h +++ b/runtime/verifier/register_line.h @@ -353,6 +353,23 @@ class RegisterLine { return monitors_[i]; } + // We give access to the lock depth map to avoid an expensive poll loop for FindLocksAtDexPC. + template + void IterateRegToLockDepths(T fn) const { + for (const auto& pair : reg_to_lock_depths_) { + const uint32_t reg = pair.first; + uint32_t depths = pair.second; + uint32_t depth = 0; + while (depths != 0) { + if ((depths & 1) != 0) { + fn(reg, depth); + } + depths >>= 1; + depth++; + } + } + } + private: void CopyRegToLockDepth(size_t dst, size_t src) { auto it = reg_to_lock_depths_.find(src); diff --git a/test/167-visit-locks/expected.txt b/test/167-visit-locks/expected.txt new file mode 100644 index 0000000000..5157c64c83 --- /dev/null +++ b/test/167-visit-locks/expected.txt @@ -0,0 +1,3 @@ +JNI_OnLoad called +First +Second diff --git a/test/167-visit-locks/info.txt b/test/167-visit-locks/info.txt new file mode 100644 index 0000000000..d849bc31ed --- /dev/null +++ b/test/167-visit-locks/info.txt @@ -0,0 +1 @@ +Regression test for b/68703210 diff --git a/test/167-visit-locks/run b/test/167-visit-locks/run new file mode 100644 index 0000000000..93654113e6 --- /dev/null +++ b/test/167-visit-locks/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. + +# Use a smaller heap so it's easier to potentially fill up. +exec ${RUN} $@ --runtime-option -Xmx2m diff --git a/test/167-visit-locks/smali/TestSync.smali b/test/167-visit-locks/smali/TestSync.smali new file mode 100644 index 0000000000..5e68ad7003 --- /dev/null +++ b/test/167-visit-locks/smali/TestSync.smali @@ -0,0 +1,119 @@ +.class LTestSync; +.super Ljava/lang/Object; +.source "Main.java" + + +# direct methods +.method constructor ()V + .registers 1 + + .prologue + .line 6 + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + +.method public static run()V + # v0-v2 were generated by javac+dx for the original src code, keeping them. + # v10..v19 are for tracking, aliasing and manipulating the first lock. + # v20..v29 are for tracking, aliasing and manipulating the second lock. + .registers 30 + + .prologue + .line 8 + const-string v1, "First" + + .line 9 + const-string v2, "Second" + + move-object v10, v1 + const v1, 0x1 + + .line 10 + monitor-enter v10 + + # Introduce a range of dead copies. + move-object v11, v10 + move-object v12, v10 + move-object v13, v10 + move-object v14, v10 + move-object v15, v10 + move-object/16 v16, v10 + move-object/16 v17, v10 + move-object/16 v18, v10 + + # Introduce a copy that we'll use for unlock. + move-object/16 v19, v10 + + # Clobber the original alias. + const v10, 0x3 + + move-object/16 v20, v2 + const v2, 0x2 + + .line 11 + :try_start_b + monitor-enter v20 + :try_end_c + + # Introduce a range of dead copies. + move-object/16 v21, v20 + move-object/16 v22, v20 + move-object/16 v23, v20 + move-object/16 v24, v20 + move-object/16 v25, v20 + move-object/16 v26, v20 + move-object/16 v27, v20 + + # Introduce another copy that we will hold live. + move-object/16 v28, v20 + + # Clobber the original alias. + const v20, 0x5 + + # Introduce another copy that we'll use for unlock. + move-object/16 v29, v28 + + .catchall {:try_start_b .. :try_end_c} :catchall_15 + + .line 12 + :try_start_c + invoke-static/range { v28 }, LMain;->run(Ljava/lang/Object;)V + + .line 13 + monitor-exit v29 + :try_end_10 + .catchall {:try_start_c .. :try_end_10} :catchall_12 + + .line 14 + :try_start_10 + monitor-exit v19 + :try_end_11 + .catchall {:try_start_10 .. :try_end_11} :catchall_15 + + .line 15 + return-void + + .line 13 + :catchall_12 + move-exception v0 + + :try_start_13 + monitor-exit v29 + :try_end_14 + .catchall {:try_start_13 .. :try_end_14} :catchall_12 + + :try_start_14 + throw v0 + + .line 14 + :catchall_15 + move-exception v0 + + monitor-exit v19 + :try_end_17 + .catchall {:try_start_14 .. :try_end_17} :catchall_15 + + throw v0 +.end method diff --git a/test/167-visit-locks/src/Main.java b/test/167-visit-locks/src/Main.java new file mode 100644 index 0000000000..d8da92715a --- /dev/null +++ b/test/167-visit-locks/src/Main.java @@ -0,0 +1,29 @@ +/* + * 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 { + System.loadLibrary(args[0]); + + Class.forName("TestSync").getMethod("run").invoke(null); + } + + public static void run(Object o) { + testVisitLocks(); + } + + public static native void testVisitLocks(); +} diff --git a/test/167-visit-locks/visit_locks.cc b/test/167-visit-locks/visit_locks.cc new file mode 100644 index 0000000000..e79c880639 --- /dev/null +++ b/test/167-visit-locks/visit_locks.cc @@ -0,0 +1,74 @@ +/* + * 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. + */ + +#include "jni.h" + +#include + +#include "android-base/logging.h" + +#include "arch/context.h" +#include "art_method.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "mirror/object-inl.h" +#include "mirror/string.h" +#include "monitor.h" +#include "scoped_thread_state_change-inl.h" +#include "stack.h" +#include "thread-current-inl.h" + +namespace art { + +extern "C" JNIEXPORT void JNICALL Java_Main_testVisitLocks(JNIEnv*, jclass) { + ScopedObjectAccess soa(Thread::Current()); + + class VisitLocks : public StackVisitor { + public: + VisitLocks(Thread* thread, Context* context) + : StackVisitor(thread, context, StackWalkKind::kIncludeInlinedFrames) { + } + + bool VisitFrame() OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* m = GetMethod(); + + // Ignore runtime methods. + if (m == nullptr || m->IsRuntimeMethod()) { + return true; + } + + if (m->PrettyMethod() == "void TestSync.run()") { + // Interesting frame. + Monitor::VisitLocks(this, Callback, nullptr); + return false; + } + + return true; + } + + static void Callback(mirror::Object* obj, void*) REQUIRES_SHARED(Locks::mutator_lock_) { + CHECK(obj != nullptr); + CHECK(obj->IsString()); + std::cerr << obj->AsString()->ToModifiedUtf8() << std::endl; + } + }; + Context* context = Context::Create(); + VisitLocks vl(soa.Self(), context); + vl.WalkStack(); + delete context; +} + +} // namespace art diff --git a/test/Android.bp b/test/Android.bp index 2d526d256c..01e424d5e3 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -364,8 +364,9 @@ cc_defaults { "141-class-unload/jni_unload.cc", "148-multithread-gc-annotations/gc_coverage.cc", "149-suspend-all-stress/suspend_all.cc", - "203-multi-checkpoint/multi_checkpoint.cc", "154-gc-loop/heap_interface.cc", + "167-visit-locks/visit_locks.cc", + "203-multi-checkpoint/multi_checkpoint.cc", "454-get-vreg/get_vreg_jni.cc", "457-regs/regs_jni.cc", "461-get-reference-vreg/get_reference_vreg_jni.cc", -- GitLab From 4c71d00f3bee9ae7a3e14ca6629e9e0cb4e25fd8 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Wed, 29 Nov 2017 11:03:25 +0000 Subject: [PATCH 089/226] ART: Remove nested SOA in intrinsics code Removes a couple of unnecessary SOA instances. Bug: 69622155 Test: m -j32 Change-Id: I92e194096faeb4a83e4972d745bd8182ce8f820c --- compiler/optimizing/intrinsics.cc | 14 ++++++-------- compiler/optimizing/intrinsics.h | 5 +++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 9bf10f58fd..77199242f5 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -104,7 +104,8 @@ static inline IntrinsicExceptions GetExceptions(Intrinsics i) { return kCanThrow; } -static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) { +static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) + REQUIRES_SHARED(Locks::mutator_lock_) { // Whenever the intrinsic is marked as static, report an error if we find an InvokeVirtual. // // Whenever the intrinsic is marked as direct and we find an InvokeVirtual, a devirtualization @@ -130,7 +131,6 @@ static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) { } if (invoke_type == kVirtual) { ArtMethod* art_method = invoke->GetResolvedMethod(); - ScopedObjectAccess soa(Thread::Current()); return (art_method->IsFinal() || art_method->GetDeclaringClass()->IsFinal()); } return false; @@ -155,12 +155,10 @@ bool IntrinsicsRecognizer::Recognize(HInvoke* invoke, /*out*/ bool* wrong_invoke return false; } - { - // TODO: b/65872996 Polymorphic signature methods should be compiler intrinsics. - ScopedObjectAccess soa(Thread::Current()); - if (art_method->IsPolymorphicSignature()) { - return false; - } + // TODO: b/65872996 The intent is that polymorphic signature methods should + // be compiler intrinsics. At present, they are only interpreter intrinsics. + if (art_method->IsPolymorphicSignature()) { + return false; } Intrinsics intrinsic = static_cast(art_method->GetIntrinsic()); diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index 8088ab25a7..c07a99032a 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -46,8 +46,9 @@ class IntrinsicsRecognizer : public HOptimization { // Static helper that recognizes intrinsic call. Returns true on success. // If it fails due to invoke type mismatch, wrong_invoke_type is set. - // Useful to recognize intrinsics on invidual calls outside this full pass. - static bool Recognize(HInvoke* invoke, /*out*/ bool* wrong_invoke_type); + // Useful to recognize intrinsics on individual calls outside this full pass. + static bool Recognize(HInvoke* invoke, /*out*/ bool* wrong_invoke_type) + REQUIRES_SHARED(Locks::mutator_lock_); static constexpr const char* kIntrinsicsRecognizerPassName = "intrinsics_recognition"; -- GitLab From e7441631a11e2e07ce863255a59ee4de29c6a56f Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 29 Nov 2017 13:00:56 +0000 Subject: [PATCH 090/226] Revert "Revert "JIT JNI stubs."" The original CL, https://android-review.googlesource.com/513417 , had a bug for class unloading where a read barrier was executed at the wrong time from ConcurrentCopying::MarkingPhase() -> ClassLinker::CleanupClassLoaders() -> ClassLinker::DeleteClassLoader() -> JitCodeCache::RemoveMethodsIn() -> JitCodeCache::JniStubKey::UpdateShorty() -> ArtMethod::GetShorty(). This has been fixed by removing sources of the read barrier from ArtMethod::GetShorty(). Test: testrunner.py --host --prebuild --jit --no-relocate \ --no-image -t 998-redefine-use-after-free Bug: 65574695 Bug: 69843562 This reverts commit 47d31853e16a95393d760e6be2ffeeb0193f94a1. Change-Id: I06e7a15b09d9ff11cde15a7d1529644bfeca15e0 --- compiler/optimizing/optimizing_compiler.cc | 64 ++- runtime/arch/arm/quick_entrypoints_arm.S | 4 +- runtime/arch/arm64/quick_entrypoints_arm64.S | 2 +- runtime/arch/mips/quick_entrypoints_mips.S | 3 +- .../arch/mips64/quick_entrypoints_mips64.S | 3 +- runtime/arch/x86/quick_entrypoints_x86.S | 4 +- .../arch/x86_64/quick_entrypoints_x86_64.S | 4 +- runtime/art_method-inl.h | 17 +- runtime/art_method.cc | 28 +- runtime/art_method.h | 15 +- runtime/entrypoints/entrypoint_utils.cc | 4 +- .../quick/quick_trampoline_entrypoints.cc | 9 +- runtime/jit/jit.cc | 6 +- runtime/jit/jit_code_cache.cc | 493 ++++++++++++++---- runtime/jit/jit_code_cache.h | 39 +- runtime/jit/profile_saver.cc | 4 +- runtime/managed_stack-inl.h | 4 +- runtime/managed_stack.h | 80 ++- runtime/stack.cc | 59 ++- runtime/stack.h | 3 +- runtime/thread.cc | 4 +- runtime/thread.h | 9 +- test/655-jit-clinit/src/Main.java | 4 +- test/667-jit-jni-stub/expected.txt | 1 + test/667-jit-jni-stub/info.txt | 1 + test/667-jit-jni-stub/jit_jni_stub_test.cc | 63 +++ test/667-jit-jni-stub/run | 18 + test/667-jit-jni-stub/src/Main.java | 180 +++++++ test/Android.bp | 1 + test/common/runtime_state.cc | 25 +- 30 files changed, 961 insertions(+), 190 deletions(-) create mode 100644 test/667-jit-jni-stub/expected.txt create mode 100644 test/667-jit-jni-stub/info.txt create mode 100644 test/667-jit-jni-stub/jit_jni_stub_test.cc create mode 100755 test/667-jit-jni-stub/run create mode 100644 test/667-jit-jni-stub/src/Main.java diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index a281c4a310..73c72fc57a 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -1196,7 +1196,69 @@ bool OptimizingCompiler::JitCompile(Thread* self, Runtime* runtime = Runtime::Current(); ArenaAllocator allocator(runtime->GetJitArenaPool()); - ArenaStack arena_stack(Runtime::Current()->GetJitArenaPool()); + + if (UNLIKELY(method->IsNative())) { + JniCompiledMethod jni_compiled_method = ArtQuickJniCompileMethod( + GetCompilerDriver(), access_flags, method_idx, *dex_file); + ScopedNullHandle> roots; + ArenaSet> cha_single_implementation_list( + allocator.Adapter(kArenaAllocCHA)); + const void* code = code_cache->CommitCode( + self, + method, + /* stack_map_data */ nullptr, + /* method_info_data */ nullptr, + /* roots_data */ nullptr, + jni_compiled_method.GetFrameSize(), + jni_compiled_method.GetCoreSpillMask(), + jni_compiled_method.GetFpSpillMask(), + jni_compiled_method.GetCode().data(), + jni_compiled_method.GetCode().size(), + /* data_size */ 0u, + osr, + roots, + /* has_should_deoptimize_flag */ false, + cha_single_implementation_list); + if (code == nullptr) { + return false; + } + + const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); + if (compiler_options.GetGenerateDebugInfo()) { + const auto* method_header = reinterpret_cast(code); + const uintptr_t code_address = reinterpret_cast(method_header->GetCode()); + debug::MethodDebugInfo info = {}; + DCHECK(info.trampoline_name.empty()); + info.dex_file = dex_file; + info.class_def_index = class_def_idx; + info.dex_method_index = method_idx; + info.access_flags = access_flags; + info.code_item = code_item; + info.isa = jni_compiled_method.GetInstructionSet(); + info.deduped = false; + info.is_native_debuggable = compiler_options.GetNativeDebuggable(); + info.is_optimized = true; + info.is_code_address_text_relative = false; + info.code_address = code_address; + info.code_size = jni_compiled_method.GetCode().size(); + info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); + info.code_info = nullptr; + info.cfi = jni_compiled_method.GetCfi(); + std::vector elf_file = debug::WriteDebugElfFileForMethods( + GetCompilerDriver()->GetInstructionSet(), + GetCompilerDriver()->GetInstructionSetFeatures(), + ArrayRef(&info, 1)); + CreateJITCodeEntryForAddress(code_address, std::move(elf_file)); + } + + Runtime::Current()->GetJit()->AddMemoryUsage(method, allocator.BytesUsed()); + if (jit_logger != nullptr) { + jit_logger->WriteLog(code, jni_compiled_method.GetCode().size(), method); + } + return true; + } + + ArenaStack arena_stack(runtime->GetJitArenaPool()); CodeVectorAllocator code_allocator(&allocator); VariableSizedHandleScope handles(self); diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 6ff8dd60b8..6ec9c48b92 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -1783,7 +1783,9 @@ 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 sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] + ldr ip, [r9, #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 # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 280e5937c6..47efeb9200 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -2299,7 +2299,7 @@ ENTRY art_quick_generic_jni_trampoline .Lexception_in_native: // Move to x1 then sp to please assembler. ldr x1, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET] - mov sp, x1 + add sp, x1, #-1 // Remove the GenericJNI tag. .cfi_def_cfa_register sp # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 489c52c0d2..fc77a641b3 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -2283,7 +2283,8 @@ ENTRY art_quick_generic_jni_trampoline nop 2: - lw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) + lw $t0, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) + addiu $sp, $t0, -1 // Remove the GenericJNI tag. move $gp, $s3 # restore $gp from $s3 # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 98ffe6504a..3fb83d9232 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -2158,7 +2158,8 @@ ENTRY art_quick_generic_jni_trampoline dmtc1 $v0, $f0 # place return value to FP return value 1: - ld $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) + ld $t0, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) + daddiu $sp, $t0, -1 // Remove the GenericJNI tag. # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION END art_quick_generic_jni_trampoline diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 25716dc1bb..a46ceeba12 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1969,7 +1969,9 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline punpckldq %xmm1, %xmm0 ret .Lexception_in_native: - movl %fs:THREAD_TOP_QUICK_FRAME_OFFSET, %esp + pushl %fs:THREAD_TOP_QUICK_FRAME_OFFSET + addl LITERAL(-1), (%esp) // Remove the GenericJNI tag. + movl (%esp), %esp // Do a call to push a new save-all frame required by the runtime. call .Lexception_call .Lexception_call: diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 2c3da90f25..463e5a279f 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -1958,7 +1958,9 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline movq %rax, %xmm0 ret .Lexception_in_native: - movq %gs:THREAD_TOP_QUICK_FRAME_OFFSET, %rsp + pushq %gs:THREAD_TOP_QUICK_FRAME_OFFSET + addq LITERAL(-1), (%rsp) // Remove the GenericJNI tag. + movq (%rsp), %rsp CFI_DEF_CFA_REGISTER(rsp) // Do a call to push a new save-all frame required by the runtime. call .Lexception_call diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 50913def93..31abf94889 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -95,10 +95,12 @@ inline uint16_t ArtMethod::GetMethodIndexDuringLinking() { return method_index_; } +template inline uint32_t ArtMethod::GetDexMethodIndex() { if (kCheckDeclaringClassState) { - CHECK(IsRuntimeMethod() || GetDeclaringClass()->IsIdxLoaded() || - GetDeclaringClass()->IsErroneous()); + CHECK(IsRuntimeMethod() || + GetDeclaringClass()->IsIdxLoaded() || + GetDeclaringClass()->IsErroneous()); } return GetDexMethodIndexUnchecked(); } @@ -202,7 +204,14 @@ inline const char* ArtMethod::GetShorty() { inline const char* ArtMethod::GetShorty(uint32_t* out_length) { DCHECK(!IsProxyMethod()); const DexFile* dex_file = GetDexFile(); - return dex_file->GetMethodShorty(dex_file->GetMethodId(GetDexMethodIndex()), out_length); + // Don't do a read barrier in the DCHECK() inside GetDexMethodIndex() as GetShorty() + // can be called when the declaring class is about to be unloaded and cannot be added + // to the mark stack (subsequent GC assertion would fail). + // It is safe to avoid the read barrier as the ArtMethod is constructed with a declaring + // Class already satisfying the DCHECK() inside GetDexMethodIndex(), so even if that copy + // of declaring class becomes a from-space object, it shall satisfy the DCHECK(). + return dex_file->GetMethodShorty(dex_file->GetMethodId(GetDexMethodIndex()), + out_length); } inline const Signature ArtMethod::GetSignature() { @@ -319,7 +328,7 @@ inline mirror::ClassLoader* ArtMethod::GetClassLoader() { template inline mirror::DexCache* ArtMethod::GetDexCache() { - if (LIKELY(!IsObsolete())) { + if (LIKELY(!IsObsolete())) { mirror::Class* klass = GetDeclaringClass(); return klass->GetDexCache(); } else { diff --git a/runtime/art_method.cc b/runtime/art_method.cc index fa0c501e31..bdbc4509f3 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -587,11 +587,6 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { CHECK(existing_entry_point != nullptr) << PrettyMethod() << "@" << this; ClassLinker* class_linker = runtime->GetClassLinker(); - if (class_linker->IsQuickGenericJniStub(existing_entry_point)) { - // The generic JNI does not have any method header. - return nullptr; - } - if (existing_entry_point == GetQuickProxyInvokeHandler()) { DCHECK(IsProxyMethod() && !IsConstructor()); // The proxy entry point does not have any method header. @@ -599,7 +594,8 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { } // Check whether the current entry point contains this pc. - if (!class_linker->IsQuickResolutionStub(existing_entry_point) && + if (!class_linker->IsQuickGenericJniStub(existing_entry_point) && + !class_linker->IsQuickResolutionStub(existing_entry_point) && !class_linker->IsQuickToInterpreterBridge(existing_entry_point)) { OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromEntryPoint(existing_entry_point); @@ -632,19 +628,13 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { OatFile::OatMethod oat_method = FindOatMethodFor(this, class_linker->GetImagePointerSize(), &found); if (!found) { - if (class_linker->IsQuickResolutionStub(existing_entry_point)) { - // We are running the generic jni stub, but the entry point of the method has not - // been updated yet. - DCHECK_EQ(pc, 0u) << "Should be a downcall"; - DCHECK(IsNative()); - return nullptr; - } - if (existing_entry_point == GetQuickInstrumentationEntryPoint()) { - // We are running the generic jni stub, but the method is being instrumented. - // NB We would normally expect the pc to be zero but we can have non-zero pc's if - // instrumentation is installed or removed during the call which is using the generic jni - // trampoline. - DCHECK(IsNative()); + if (IsNative()) { + // We are running the GenericJNI stub. The entrypoint may point + // to different entrypoints or to a JIT-compiled JNI stub. + DCHECK(class_linker->IsQuickGenericJniStub(existing_entry_point) || + class_linker->IsQuickResolutionStub(existing_entry_point) || + existing_entry_point == GetQuickInstrumentationEntryPoint() || + (jit != nullptr && jit->GetCodeCache()->ContainsPc(existing_entry_point))); return nullptr; } // Only for unit tests. diff --git a/runtime/art_method.h b/runtime/art_method.h index dca6f37254..0a592e0528 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -242,8 +242,9 @@ class ArtMethod FINAL { return (GetAccessFlags() & kAccDefault) != 0; } + template bool IsObsolete() { - return (GetAccessFlags() & kAccObsoleteMethod) != 0; + return (GetAccessFlags() & kAccObsoleteMethod) != 0; } void SetIsObsolete() { @@ -376,6 +377,7 @@ class ArtMethod FINAL { ALWAYS_INLINE uint32_t GetDexMethodIndexUnchecked() { return dex_method_index_; } + template ALWAYS_INLINE uint32_t GetDexMethodIndex() REQUIRES_SHARED(Locks::mutator_lock_); void SetDexMethodIndex(uint32_t new_idx) { @@ -460,12 +462,11 @@ class ArtMethod FINAL { } ProfilingInfo* GetProfilingInfo(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_) { - // Don't do a read barrier in the DCHECK, as GetProfilingInfo is called in places - // where the declaring class is treated as a weak reference (accessing it with - // a read barrier would either prevent unloading the class, or crash the runtime if - // the GC wants to unload it). - DCHECK(!IsNative()); - if (UNLIKELY(IsProxyMethod())) { + // Don't do a read barrier in the DCHECK() inside GetAccessFlags() called by IsNative(), + // as GetProfilingInfo is called in places where the declaring class is treated as a weak + // reference (accessing it with a read barrier would either prevent unloading the class, + // or crash the runtime if the GC wants to unload it). + if (UNLIKELY(IsNative()) || UNLIKELY(IsProxyMethod())) { return nullptr; } return reinterpret_cast(GetDataPtrSize(pointer_size)); diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 2bf4372b1f..f3450da306 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -245,7 +245,7 @@ ArtMethod* GetCalleeSaveMethodCaller(ArtMethod** sp, CalleeSaveType type, bool d CallerAndOuterMethod GetCalleeSaveMethodCallerAndOuterMethod(Thread* self, CalleeSaveType type) { CallerAndOuterMethod result; ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame(); + ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrameKnownNotTagged(); auto outer_caller_and_pc = DoGetCalleeSaveMethodOuterCallerAndPc(sp, type); result.outer_method = outer_caller_and_pc.first; uintptr_t caller_pc = outer_caller_and_pc.second; @@ -256,7 +256,7 @@ CallerAndOuterMethod GetCalleeSaveMethodCallerAndOuterMethod(Thread* self, Calle ArtMethod* GetCalleeSaveOuterMethod(Thread* self, CalleeSaveType type) { ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame(); + ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrameKnownNotTagged(); return DoGetCalleeSaveMethodOuterCallerAndPc(sp, type).first; } diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 2496aa0f58..0a76cddf5e 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -31,6 +31,7 @@ #include "index_bss_mapping.h" #include "instrumentation.h" #include "interpreter/interpreter.h" +#include "jit/jit.h" #include "linear_alloc.h" #include "method_handles.h" #include "method_reference.h" @@ -2167,6 +2168,11 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod** // Note: We cannot walk the stack properly until fixed up below. 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(); @@ -2188,7 +2194,7 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod** } // Fix up managed-stack things in Thread. After this we can walk the stack. - self->SetTopOfStack(sp); + self->SetTopOfStackTagged(sp); self->VerifyStack(); @@ -2308,6 +2314,7 @@ extern "C" uint64_t artQuickGenericJniEndTrampoline(Thread* self, // anything that requires a mutator lock before that would cause problems as GC may have the // exclusive mutator lock and may be moving objects, etc. ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame(); + DCHECK(self->GetManagedStack()->GetTopQuickFrameTag()); uint32_t* sp32 = reinterpret_cast(sp); ArtMethod* called = *sp; uint32_t cookie = *(sp32 - 1); diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 953e195480..0d95bc6e64 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -643,7 +643,7 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ return; } - if (method->IsClassInitializer() || method->IsNative() || !method->IsCompilable()) { + if (method->IsClassInitializer() || !method->IsCompilable()) { // We do not want to compile such methods. return; } @@ -659,7 +659,8 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ count *= priority_thread_weight_; } int32_t new_count = starting_count + count; // int32 here to avoid wrap-around; - if (starting_count < warm_method_threshold_) { + // 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_) && (method->GetProfilingInfo(kRuntimePointerSize) == nullptr)) { bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false); @@ -696,6 +697,7 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ // 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)) { DCHECK(thread_pool_ != nullptr); thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr)); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 32205138bd..a5c167eee8 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -55,6 +55,107 @@ static constexpr int kProtCode = PROT_READ | PROT_EXEC; static constexpr size_t kCodeSizeLogThreshold = 50 * KB; static constexpr size_t kStackMapSizeLogThreshold = 50 * KB; +class JitCodeCache::JniStubKey { + public: + explicit JniStubKey(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) + : shorty_(method->GetShorty()), + is_static_(method->IsStatic()), + is_fast_native_(method->IsFastNative()), + is_critical_native_(method->IsCriticalNative()), + is_synchronized_(method->IsSynchronized()) { + DCHECK(!(is_fast_native_ && is_critical_native_)); + } + + bool operator<(const JniStubKey& rhs) const { + if (is_static_ != rhs.is_static_) { + return rhs.is_static_; + } + if (is_synchronized_ != rhs.is_synchronized_) { + return rhs.is_synchronized_; + } + if (is_fast_native_ != rhs.is_fast_native_) { + return rhs.is_fast_native_; + } + if (is_critical_native_ != rhs.is_critical_native_) { + return rhs.is_critical_native_; + } + return strcmp(shorty_, rhs.shorty_) < 0; + } + + // Update the shorty to point to another method's shorty. Call this function when removing + // the method that references the old shorty from JniCodeData and not removing the entire + // JniCodeData; the old shorty may become a dangling pointer when that method is unloaded. + void UpdateShorty(ArtMethod* method) const REQUIRES_SHARED(Locks::mutator_lock_) { + const char* shorty = method->GetShorty(); + DCHECK_STREQ(shorty_, shorty); + shorty_ = shorty; + } + + private: + // The shorty points to a DexFile data and may need to change + // to point to the same shorty in a different DexFile. + mutable const char* shorty_; + + const bool is_static_; + const bool is_fast_native_; + const bool is_critical_native_; + const bool is_synchronized_; +}; + +class JitCodeCache::JniStubData { + public: + JniStubData() : code_(nullptr), methods_() {} + + void SetCode(const void* code) { + DCHECK(code != nullptr); + code_ = code; + } + + const void* GetCode() const { + return code_; + } + + bool IsCompiled() const { + return GetCode() != nullptr; + } + + void AddMethod(ArtMethod* method) { + if (!ContainsElement(methods_, method)) { + methods_.push_back(method); + } + } + + const std::vector& GetMethods() const { + return methods_; + } + + void RemoveMethodsIn(const LinearAlloc& alloc) { + auto kept_end = std::remove_if( + methods_.begin(), + methods_.end(), + [&alloc](ArtMethod* method) { return alloc.ContainsUnsafe(method); }); + methods_.erase(kept_end, methods_.end()); + } + + bool RemoveMethod(ArtMethod* method) { + auto it = std::find(methods_.begin(), methods_.end(), method); + if (it != methods_.end()) { + methods_.erase(it); + return true; + } else { + return false; + } + } + + void MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) { + std::replace(methods_.begin(), methods_.end(), old_method, new_method); + } + + private: + const void* code_; + std::vector methods_; +}; + JitCodeCache* JitCodeCache::Create(size_t initial_capacity, size_t max_capacity, bool generate_debug_info, @@ -193,14 +294,36 @@ bool JitCodeCache::ContainsPc(const void* ptr) const { bool JitCodeCache::ContainsMethod(ArtMethod* method) { MutexLock mu(Thread::Current(), lock_); - for (auto& it : method_code_map_) { - if (it.second == method) { + if (UNLIKELY(method->IsNative())) { + auto it = jni_stubs_map_.find(JniStubKey(method)); + if (it != jni_stubs_map_.end() && + it->second.IsCompiled() && + ContainsElement(it->second.GetMethods(), method)) { return true; } + } else { + for (const auto& it : method_code_map_) { + if (it.second == method) { + return true; + } + } } return false; } +const void* JitCodeCache::GetJniStubCode(ArtMethod* method) { + DCHECK(method->IsNative()); + MutexLock mu(Thread::Current(), lock_); + auto it = jni_stubs_map_.find(JniStubKey(method)); + if (it != jni_stubs_map_.end()) { + JniStubData& data = it->second; + if (data.IsCompiled() && ContainsElement(data.GetMethods(), method)) { + return data.GetCode(); + } + } + return nullptr; +} + class ScopedCodeCacheWrite : ScopedTrace { public: explicit ScopedCodeCacheWrite(MemMap* code_map, bool only_for_tlb_shootdown = false) @@ -426,7 +549,9 @@ void JitCodeCache::FreeCode(const void* code_ptr) { // Notify native debugger that we are about to remove the code. // It does nothing if we are not using native debugger. DeleteJITCodeEntryForAddress(reinterpret_cast(code_ptr)); - FreeData(GetRootTable(code_ptr)); + if (OatQuickMethodHeader::FromCodePointer(code_ptr)->IsOptimized()) { + FreeData(GetRootTable(code_ptr)); + } // else this is a JNI stub without any data. FreeCode(reinterpret_cast(allocation)); } @@ -463,6 +588,16 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { // lead to a deadlock. { ScopedCodeCacheWrite scc(code_map_.get()); + for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) { + it->second.RemoveMethodsIn(alloc); + if (it->second.GetMethods().empty()) { + method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->second.GetCode())); + it = jni_stubs_map_.erase(it); + } else { + it->first.UpdateShorty(it->second.GetMethods().front()); + ++it; + } + } for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { if (alloc.ContainsUnsafe(it->second)) { method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->first)); @@ -572,7 +707,8 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, bool has_should_deoptimize_flag, const ArenaSet& cha_single_implementation_list) { - DCHECK(stack_map != nullptr); + DCHECK_NE(stack_map != nullptr, method->IsNative()); + DCHECK(!method->IsNative() || !osr); size_t alignment = GetInstructionSetAlignment(kRuntimeISA); // Ensure the header ends up at expected instruction alignment. size_t header_size = RoundUp(sizeof(OatQuickMethodHeader), alignment); @@ -596,8 +732,8 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, std::copy(code, code + code_size, code_ptr); method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); new (method_header) OatQuickMethodHeader( - code_ptr - stack_map, - code_ptr - method_info, + (stack_map != nullptr) ? code_ptr - stack_map : 0u, + (method_info != nullptr) ? code_ptr - method_info : 0u, frame_size_in_bytes, core_spill_mask, fp_spill_mask, @@ -652,24 +788,40 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, // possible that the compiled code is considered invalidated by some class linking, // but below we still make the compiled code valid for the method. MutexLock mu(self, lock_); - // Fill the root table before updating the entry point. - DCHECK_EQ(FromStackMapToRoots(stack_map), roots_data); - DCHECK_LE(roots_data, stack_map); - 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)); - } - method_code_map_.Put(code_ptr, method); - if (osr) { - number_of_osr_compilations_++; - osr_code_map_.Put(method, code_ptr); + if (UNLIKELY(method->IsNative())) { + DCHECK(stack_map == nullptr); + DCHECK(roots_data == nullptr); + auto it = jni_stubs_map_.find(JniStubKey(method)); + DCHECK(it != jni_stubs_map_.end()) + << "Entry inserted in NotifyCompilationOf() should be alive."; + JniStubData* data = &it->second; + DCHECK(ContainsElement(data->GetMethods(), method)) + << "Entry inserted in NotifyCompilationOf() should contain this method."; + data->SetCode(code_ptr); + instrumentation::Instrumentation* instrum = Runtime::Current()->GetInstrumentation(); + for (ArtMethod* m : data->GetMethods()) { + instrum->UpdateMethodsCode(m, method_header->GetEntryPoint()); + } } else { - Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( - method, method_header->GetEntryPoint()); + // Fill the root table before updating the entry point. + DCHECK_EQ(FromStackMapToRoots(stack_map), roots_data); + DCHECK_LE(roots_data, stack_map); + 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)); + } + method_code_map_.Put(code_ptr, method); + if (osr) { + number_of_osr_compilations_++; + osr_code_map_.Put(method, code_ptr); + } else { + Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( + method, method_header->GetEntryPoint()); + } } if (collection_in_progress_) { // We need to update the live bitmap if there is a GC to ensure it sees this new @@ -703,45 +855,18 @@ size_t JitCodeCache::CodeCacheSize() { } bool JitCodeCache::RemoveMethod(ArtMethod* method, bool release_memory) { - MutexLock mu(Thread::Current(), lock_); - if (method->IsNative()) { - return false; - } + // This function is used only for testing and only with non-native methods. + CHECK(!method->IsNative()); - bool in_cache = false; - { - ScopedCodeCacheWrite ccw(code_map_.get()); - for (auto code_iter = method_code_map_.begin(); code_iter != method_code_map_.end();) { - if (code_iter->second == method) { - if (release_memory) { - FreeCode(code_iter->first); - } - code_iter = method_code_map_.erase(code_iter); - in_cache = true; - continue; - } - ++code_iter; - } - } + MutexLock mu(Thread::Current(), lock_); - bool osr = false; - auto code_map = osr_code_map_.find(method); - if (code_map != osr_code_map_.end()) { - osr_code_map_.erase(code_map); - osr = true; - } + bool osr = osr_code_map_.find(method) != osr_code_map_.end(); + bool in_cache = RemoveMethodLocked(method, release_memory); if (!in_cache) { return false; } - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - if (info != nullptr) { - auto profile = std::find(profiling_infos_.begin(), profiling_infos_.end(), info); - DCHECK(profile != profiling_infos_.end()); - profiling_infos_.erase(profile); - } - method->SetProfilingInfo(nullptr); method->ClearCounter(); Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( method, GetQuickToInterpreterBridge()); @@ -753,34 +878,58 @@ bool JitCodeCache::RemoveMethod(ArtMethod* method, bool release_memory) { return true; } +bool JitCodeCache::RemoveMethodLocked(ArtMethod* method, bool release_memory) { + if (LIKELY(!method->IsNative())) { + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + if (info != nullptr) { + RemoveElement(profiling_infos_, info); + } + method->SetProfilingInfo(nullptr); + } + + bool in_cache = false; + ScopedCodeCacheWrite ccw(code_map_.get()); + if (UNLIKELY(method->IsNative())) { + auto it = jni_stubs_map_.find(JniStubKey(method)); + if (it != jni_stubs_map_.end() && it->second.RemoveMethod(method)) { + in_cache = true; + if (it->second.GetMethods().empty()) { + if (release_memory) { + FreeCode(it->second.GetCode()); + } + jni_stubs_map_.erase(it); + } else { + it->first.UpdateShorty(it->second.GetMethods().front()); + } + } + } else { + for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { + if (it->second == method) { + in_cache = true; + if (release_memory) { + FreeCode(it->first); + } + it = method_code_map_.erase(it); + } else { + ++it; + } + } + + auto osr_it = osr_code_map_.find(method); + if (osr_it != osr_code_map_.end()) { + osr_code_map_.erase(osr_it); + } + } + + return in_cache; +} + // This notifies the code cache that the given method has been redefined and that it should remove // any cached information it has on the method. All threads must be suspended before calling this // method. The compiled code for the method (if there is any) must not be in any threads call stack. void JitCodeCache::NotifyMethodRedefined(ArtMethod* method) { MutexLock mu(Thread::Current(), lock_); - if (method->IsNative()) { - return; - } - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - if (info != nullptr) { - auto profile = std::find(profiling_infos_.begin(), profiling_infos_.end(), info); - DCHECK(profile != profiling_infos_.end()); - profiling_infos_.erase(profile); - } - method->SetProfilingInfo(nullptr); - ScopedCodeCacheWrite ccw(code_map_.get()); - for (auto code_iter = method_code_map_.begin(); code_iter != method_code_map_.end();) { - if (code_iter->second == method) { - FreeCode(code_iter->first); - code_iter = method_code_map_.erase(code_iter); - continue; - } - ++code_iter; - } - auto code_map = osr_code_map_.find(method); - if (code_map != osr_code_map_.end()) { - osr_code_map_.erase(code_map); - } + RemoveMethodLocked(method, /* release_memory */ true); } // This invalidates old_method. Once this function returns one can no longer use old_method to @@ -790,11 +939,15 @@ void JitCodeCache::NotifyMethodRedefined(ArtMethod* method) { // shouldn't be used since it is no longer logically in the jit code cache. // TODO We should add DCHECKS that validate that the JIT is paused when this method is entered. void JitCodeCache::MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) { - // Native methods have no profiling info and need no special handling from the JIT code cache. + MutexLock mu(Thread::Current(), lock_); if (old_method->IsNative()) { + // Update methods in jni_stubs_map_. + for (auto& entry : jni_stubs_map_) { + JniStubData& data = entry.second; + data.MoveObsoleteMethod(old_method, new_method); + } return; } - MutexLock mu(Thread::Current(), lock_); // Update ProfilingInfo to the new one and remove it from the old_method. if (old_method->GetProfilingInfo(kRuntimePointerSize) != nullptr) { DCHECK_EQ(old_method->GetProfilingInfo(kRuntimePointerSize)->GetMethod(), old_method); @@ -936,7 +1089,7 @@ class MarkCodeClosure FINAL : public Closure { // its stack frame, it is not the method owning return_pc_. We just pass null to // LookupMethodHeader: the method is only checked against in debug builds. OatQuickMethodHeader* method_header = - code_cache_->LookupMethodHeader(frame.return_pc_, nullptr); + code_cache_->LookupMethodHeader(frame.return_pc_, /* method */ nullptr); if (method_header != nullptr) { const void* code = method_header->GetCode(); CHECK(code_cache_->GetLiveBitmap()->Test(FromCodeToAllocation(code))); @@ -1089,7 +1242,7 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { const void* entry_point = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); if (ContainsPc(entry_point)) { info->SetSavedEntryPoint(entry_point); - // Don't call Instrumentation::UpdateMethods, as it can check the declaring + // Don't call Instrumentation::UpdateMethodsCode(), as it can check the declaring // class of the method. We may be concurrently running a GC which makes accessing // the class unsafe. We know it is OK to bypass the instrumentation as we've just // checked that the current entry point is JIT compiled code. @@ -1098,6 +1251,25 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { } DCHECK(CheckLiveCompiledCodeHasProfilingInfo()); + + // Change entry points of native methods back to the GenericJNI entrypoint. + for (const auto& entry : jni_stubs_map_) { + const JniStubData& data = entry.second; + if (!data.IsCompiled()) { + continue; + } + // Make sure a single invocation of the GenericJNI trampoline tries to recompile. + uint16_t new_counter = Runtime::Current()->GetJit()->HotMethodThreshold() - 1u; + const OatQuickMethodHeader* method_header = + OatQuickMethodHeader::FromCodePointer(data.GetCode()); + for (ArtMethod* method : data.GetMethods()) { + if (method->GetEntryPointFromQuickCompiledCode() == method_header->GetEntryPoint()) { + // Don't call Instrumentation::UpdateMethodsCode(), same as for normal methods above. + method->SetCounter(new_counter); + method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub()); + } + } + } } live_bitmap_.reset(nullptr); NotifyCollectionDone(self); @@ -1113,13 +1285,22 @@ void JitCodeCache::RemoveUnmarkedCode(Thread* self) { MutexLock mu(self, lock_); ScopedCodeCacheWrite scc(code_map_.get()); // 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; + if (!data->IsCompiled() || GetLiveBitmap()->Test(FromCodeToAllocation(data->GetCode()))) { + ++it; + } else { + method_headers.insert(OatQuickMethodHeader::FromCodePointer(data->GetCode())); + it = jni_stubs_map_.erase(it); + } + } for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { const void* code_ptr = it->first; uintptr_t allocation = FromCodeToAllocation(code_ptr); if (GetLiveBitmap()->Test(allocation)) { ++it; } else { - method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->first)); + method_headers.insert(OatQuickMethodHeader::FromCodePointer(code_ptr)); it = method_code_map_.erase(it); } } @@ -1158,6 +1339,17 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { // an entry point is either: // - an osr compiled code, that will be removed if not in a thread call stack. // - discarded compiled code, that will be removed if not in a thread call stack. + for (const auto& entry : jni_stubs_map_) { + const JniStubData& data = entry.second; + const void* code_ptr = data.GetCode(); + const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + for (ArtMethod* method : data.GetMethods()) { + if (method_header->GetEntryPoint() == method->GetEntryPointFromQuickCompiledCode()) { + GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr)); + break; + } + } + } for (const auto& it : method_code_map_) { ArtMethod* method = it.second; const void* code_ptr = it.first; @@ -1237,19 +1429,51 @@ OatQuickMethodHeader* JitCodeCache::LookupMethodHeader(uintptr_t pc, ArtMethod* return nullptr; } - MutexLock mu(Thread::Current(), lock_); - if (method_code_map_.empty()) { - return nullptr; + if (!kIsDebugBuild) { + // Called with null `method` only from MarkCodeClosure::Run() in debug build. + CHECK(method != nullptr); } - auto it = method_code_map_.lower_bound(reinterpret_cast(pc)); - --it; - const void* code_ptr = it->first; - OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); - if (!method_header->Contains(pc)) { - return nullptr; + MutexLock mu(Thread::Current(), lock_); + OatQuickMethodHeader* method_header = nullptr; + ArtMethod* found_method = nullptr; // Only for DCHECK(), not for JNI stubs. + if (method != nullptr && UNLIKELY(method->IsNative())) { + auto it = jni_stubs_map_.find(JniStubKey(method)); + if (it == jni_stubs_map_.end() || !ContainsElement(it->second.GetMethods(), method)) { + return nullptr; + } + const void* code_ptr = it->second.GetCode(); + method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + if (!method_header->Contains(pc)) { + return nullptr; + } + } else { + auto it = method_code_map_.lower_bound(reinterpret_cast(pc)); + if (it != method_code_map_.begin()) { + --it; + const void* code_ptr = it->first; + if (OatQuickMethodHeader::FromCodePointer(code_ptr)->Contains(pc)) { + method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + found_method = it->second; + } + } + if (method_header == nullptr && method == nullptr) { + // Scan all compiled JNI stubs as well. This slow search is used only + // for checks in debug build, for release builds the `method` is not null. + for (auto&& entry : jni_stubs_map_) { + const JniStubData& data = entry.second; + if (data.IsCompiled() && + OatQuickMethodHeader::FromCodePointer(data.GetCode())->Contains(pc)) { + method_header = OatQuickMethodHeader::FromCodePointer(data.GetCode()); + } + } + } + if (method_header == nullptr) { + return nullptr; + } } - if (kIsDebugBuild && method != nullptr) { + + if (kIsDebugBuild && method != nullptr && !method->IsNative()) { // When we are walking the stack to redefine classes and creating obsolete methods it is // possible that we might have updated the method_code_map by making this method obsolete in a // previous frame. Therefore we should just check that the non-obsolete version of this method @@ -1258,9 +1482,9 @@ OatQuickMethodHeader* JitCodeCache::LookupMethodHeader(uintptr_t pc, ArtMethod* // occur when we are in the process of allocating and setting up obsolete methods. Otherwise // method and it->second should be identical. (See openjdkjvmti/ti_redefine.cc for more // information.) - DCHECK_EQ(it->second->GetNonObsoleteMethod(), method->GetNonObsoleteMethod()) + DCHECK_EQ(found_method->GetNonObsoleteMethod(), method->GetNonObsoleteMethod()) << ArtMethod::PrettyMethod(method->GetNonObsoleteMethod()) << " " - << ArtMethod::PrettyMethod(it->second->GetNonObsoleteMethod()) << " " + << ArtMethod::PrettyMethod(found_method->GetNonObsoleteMethod()) << " " << std::hex << pc; } return method_header; @@ -1449,21 +1673,51 @@ bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr return false; } - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - if (info == nullptr) { - VLOG(jit) << method->PrettyMethod() << " needs a ProfilingInfo to be compiled"; - // Because the counter is not atomic, there are some rare cases where we may not hit the - // threshold for creating the ProfilingInfo. Reset the counter now to "correct" this. - ClearMethodCounter(method, /*was_warm*/ false); - return false; - } + if (UNLIKELY(method->IsNative())) { + JniStubKey key(method); + auto it = jni_stubs_map_.find(key); + bool new_compilation = false; + if (it == jni_stubs_map_.end()) { + // Create a new entry to mark the stub as being compiled. + it = jni_stubs_map_.Put(key, JniStubData{}); + new_compilation = true; + } + JniStubData* data = &it->second; + data->AddMethod(method); + if (data->IsCompiled()) { + OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(data->GetCode()); + const void* entrypoint = method_header->GetEntryPoint(); + // Update also entrypoints of other methods held by the JniStubData. + // We could simply update the entrypoint of `method` but if the last JIT GC has + // changed these entrypoints to GenericJNI in preparation for a full GC, we may + // as well change them back as this stub shall not be collected anyway and this + // can avoid a few expensive GenericJNI calls. + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + for (ArtMethod* m : data->GetMethods()) { + instrumentation->UpdateMethodsCode(m, entrypoint); + } + if (collection_in_progress_) { + GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(data->GetCode())); + } + } + return new_compilation; + } else { + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + if (info == nullptr) { + VLOG(jit) << method->PrettyMethod() << " needs a ProfilingInfo to be compiled"; + // Because the counter is not atomic, there are some rare cases where we may not hit the + // threshold for creating the ProfilingInfo. Reset the counter now to "correct" this. + ClearMethodCounter(method, /*was_warm*/ false); + return false; + } - if (info->IsMethodBeingCompiled(osr)) { - return false; - } + if (info->IsMethodBeingCompiled(osr)) { + return false; + } - info->SetIsMethodBeingCompiled(true, osr); - return true; + info->SetIsMethodBeingCompiled(true, osr); + return true; + } } ProfilingInfo* JitCodeCache::NotifyCompilerUse(ArtMethod* method, Thread* self) { @@ -1485,10 +1739,23 @@ void JitCodeCache::DoneCompilerUse(ArtMethod* method, Thread* self) { info->DecrementInlineUse(); } -void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self ATTRIBUTE_UNUSED, bool osr) { - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - DCHECK(info->IsMethodBeingCompiled(osr)); - info->SetIsMethodBeingCompiled(false, osr); +void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self, bool osr) { + DCHECK_EQ(Thread::Current(), self); + MutexLock mu(self, lock_); + if (UNLIKELY(method->IsNative())) { + auto it = jni_stubs_map_.find(JniStubKey(method)); + DCHECK(it != jni_stubs_map_.end()); + JniStubData* data = &it->second; + DCHECK(ContainsElement(data->GetMethods(), method)); + if (UNLIKELY(!data->IsCompiled())) { + // Failed to compile; the JNI compiler never fails, but the cache may be full. + jni_stubs_map_.erase(it); // Remove the entry added in NotifyCompilationOf(). + } // else CommitCodeInternal() updated entrypoints of all methods in the JniStubData. + } else { + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + DCHECK(info->IsMethodBeingCompiled(osr)); + info->SetIsMethodBeingCompiled(false, osr); + } } size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) { @@ -1498,6 +1765,7 @@ size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) { void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method, const OatQuickMethodHeader* header) { + DCHECK(!method->IsNative()); ProfilingInfo* profiling_info = method->GetProfilingInfo(kRuntimePointerSize); if ((profiling_info != nullptr) && (profiling_info->GetSavedEntryPoint() == header->GetEntryPoint())) { @@ -1553,6 +1821,7 @@ void JitCodeCache::Dump(std::ostream& os) { os << "Current JIT code cache size: " << PrettySize(used_memory_for_code_) << "\n" << "Current JIT data cache size: " << PrettySize(used_memory_for_data_) << "\n" << "Current JIT capacity: " << PrettySize(current_capacity_) << "\n" + << "Current number of JIT JNI stub entries: " << jni_stubs_map_.size() << "\n" << "Current number of JIT code cache entries: " << method_code_map_.size() << "\n" << "Total number of JIT compilations: " << number_of_compilations_ << "\n" << "Total number of JIT compilations for on stack replacement: " diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 46a408590b..fc011ddb96 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -35,9 +35,23 @@ template class Handle; class LinearAlloc; class InlineCache; class IsMarkedVisitor; +class JitJniStubTestHelper; class OatQuickMethodHeader; struct ProfileMethodInfo; class ProfilingInfo; +class Thread; + +namespace gc { +namespace accounting { +template class MemoryRangeBitmap; +} // namespace accounting +} // namespace gc + +namespace mirror { +class Class; +class Object; +template class ObjectArray; +} // namespace mirror namespace gc { namespace accounting { @@ -137,6 +151,9 @@ class JitCodeCache { // Return true if the code cache contains this method. bool ContainsMethod(ArtMethod* method) REQUIRES(!lock_); + // Return the code pointer for a JNI-compiled stub if the method is in the cache, null otherwise. + const void* GetJniStubCode(ArtMethod* method) REQUIRES(!lock_); + // Allocate a region of data that contain `size` bytes, and potentially space // for storing `number_of_roots` roots. Returns null if there is no more room. // Return the number of bytes allocated. @@ -160,11 +177,6 @@ class JitCodeCache { return live_bitmap_.get(); } - // Return whether we should do a full collection given the current state of the cache. - bool ShouldDoFullCollection() - REQUIRES(lock_) - REQUIRES_SHARED(Locks::mutator_lock_); - // Perform a collection on the code cache. void GarbageCollectCache(Thread* self) REQUIRES(!lock_) @@ -296,6 +308,12 @@ class JitCodeCache { REQUIRES(!lock_) REQUIRES(!Locks::cha_lock_); + // Removes method from the cache. The caller must ensure that all threads + // are suspended and the method should not be in any thread's stack. + bool RemoveMethodLocked(ArtMethod* method, bool release_memory) + REQUIRES(lock_) + REQUIRES(Locks::mutator_lock_); + // Free in the mspace allocations for `code_ptr`. void FreeCode(const void* code_ptr) REQUIRES(lock_); @@ -315,6 +333,11 @@ class JitCodeCache { // Set the footprint limit of the code cache. void SetFootprintLimit(size_t new_footprint) REQUIRES(lock_); + // Return whether we should do a full collection given the current state of the cache. + bool ShouldDoFullCollection() + REQUIRES(lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + void DoCollection(Thread* self, bool collect_profiling_info) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -341,6 +364,9 @@ class JitCodeCache { REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); + class JniStubKey; + class JniStubData; + // Lock for guarding allocations, collections, and the method_code_map_. Mutex lock_; // Condition to wait on during collection. @@ -357,6 +383,8 @@ class JitCodeCache { void* data_mspace_ GUARDED_BY(lock_); // Bitmap for collecting code and data. std::unique_ptr live_bitmap_; + // Holds compiled code associated with the shorty for a JNI stub. + SafeMap jni_stubs_map_ GUARDED_BY(lock_); // Holds compiled code associated to the ArtMethod. SafeMap method_code_map_ GUARDED_BY(lock_); // Holds osr compiled code associated to the ArtMethod. @@ -418,6 +446,7 @@ class JitCodeCache { // Condition to wait on for accessing inline caches. ConditionVariable inline_cache_cond_ GUARDED_BY(lock_); + friend class art::JitJniStubTestHelper; DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache); }; diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 01853de403..acbc6e63a4 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -357,8 +357,8 @@ static void SampleClassesAndExecutedMethods(pthread_t profiler_pthread, sampled_methods->AddReference(method.GetDexFile(), method.GetDexMethodIndex()); } } else { - CHECK_EQ(method.GetCounter(), 0u) << method.PrettyMethod() - << " access_flags=" << method.GetAccessFlags(); + // We do not record native methods. Once we AOT-compile the app, all native + // methods shall have their thunks compiled. } } } diff --git a/runtime/managed_stack-inl.h b/runtime/managed_stack-inl.h index 689dd8009a..678be8e098 100644 --- a/runtime/managed_stack-inl.h +++ b/runtime/managed_stack-inl.h @@ -24,7 +24,7 @@ namespace art { inline ShadowFrame* ManagedStack::PushShadowFrame(ShadowFrame* new_top_frame) { - DCHECK(top_quick_frame_ == nullptr); + DCHECK(!HasTopQuickFrame()); ShadowFrame* old_frame = top_shadow_frame_; top_shadow_frame_ = new_top_frame; new_top_frame->SetLink(old_frame); @@ -32,7 +32,7 @@ inline ShadowFrame* ManagedStack::PushShadowFrame(ShadowFrame* new_top_frame) { } inline ShadowFrame* ManagedStack::PopShadowFrame() { - DCHECK(top_quick_frame_ == nullptr); + DCHECK(!HasTopQuickFrame()); CHECK(top_shadow_frame_ != nullptr); ShadowFrame* frame = top_shadow_frame_; top_shadow_frame_ = frame->GetLink(); diff --git a/runtime/managed_stack.h b/runtime/managed_stack.h index 4f1984d55a..07078ecb13 100644 --- a/runtime/managed_stack.h +++ b/runtime/managed_stack.h @@ -24,6 +24,7 @@ #include "base/logging.h" #include "base/macros.h" #include "base/mutex.h" +#include "base/bit_utils.h" namespace art { @@ -42,7 +43,9 @@ template class StackReference; class PACKED(4) ManagedStack { public: ManagedStack() - : top_quick_frame_(nullptr), link_(nullptr), top_shadow_frame_(nullptr) {} + : tagged_top_quick_frame_(TaggedTopQuickFrame::CreateNotTagged(nullptr)), + link_(nullptr), + top_shadow_frame_(nullptr) {} void PushManagedStackFragment(ManagedStack* fragment) { // Copy this top fragment into given fragment. @@ -63,17 +66,36 @@ class PACKED(4) ManagedStack { return link_; } + ArtMethod** GetTopQuickFrameKnownNotTagged() const { + return tagged_top_quick_frame_.GetSpKnownNotTagged(); + } + ArtMethod** GetTopQuickFrame() const { - return top_quick_frame_; + return tagged_top_quick_frame_.GetSp(); + } + + bool GetTopQuickFrameTag() const { + return tagged_top_quick_frame_.GetTag(); + } + + bool HasTopQuickFrame() const { + return tagged_top_quick_frame_.GetTaggedSp() != 0u; } void SetTopQuickFrame(ArtMethod** top) { DCHECK(top_shadow_frame_ == nullptr); - top_quick_frame_ = top; + DCHECK_ALIGNED(top, 4u); + tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateNotTagged(top); } - static size_t TopQuickFrameOffset() { - return OFFSETOF_MEMBER(ManagedStack, top_quick_frame_); + void SetTopQuickFrameTagged(ArtMethod** top) { + DCHECK(top_shadow_frame_ == nullptr); + DCHECK_ALIGNED(top, 4u); + tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateTagged(top); + } + + static size_t TaggedTopQuickFrameOffset() { + return OFFSETOF_MEMBER(ManagedStack, tagged_top_quick_frame_); } ALWAYS_INLINE ShadowFrame* PushShadowFrame(ShadowFrame* new_top_frame); @@ -83,8 +105,12 @@ class PACKED(4) ManagedStack { return top_shadow_frame_; } + bool HasTopShadowFrame() const { + return GetTopShadowFrame() != nullptr; + } + void SetTopShadowFrame(ShadowFrame* top) { - DCHECK(top_quick_frame_ == nullptr); + DCHECK_EQ(tagged_top_quick_frame_.GetTaggedSp(), 0u); top_shadow_frame_ = top; } @@ -97,7 +123,47 @@ class PACKED(4) ManagedStack { bool ShadowFramesContain(StackReference* shadow_frame_entry) const; private: - ArtMethod** top_quick_frame_; + // Encodes the top quick frame (which must be at least 4-byte aligned) + // and a flag that marks the GenericJNI trampoline. + class TaggedTopQuickFrame { + public: + static TaggedTopQuickFrame CreateNotTagged(ArtMethod** sp) { + DCHECK_ALIGNED(sp, 4u); + return TaggedTopQuickFrame(reinterpret_cast(sp)); + } + + static TaggedTopQuickFrame CreateTagged(ArtMethod** sp) { + DCHECK_ALIGNED(sp, 4u); + return TaggedTopQuickFrame(reinterpret_cast(sp) | 1u); + } + + // Get SP known to be not tagged and non-null. + ArtMethod** GetSpKnownNotTagged() const { + DCHECK(!GetTag()); + DCHECK_NE(tagged_sp_, 0u); + return reinterpret_cast(tagged_sp_); + } + + ArtMethod** GetSp() const { + return reinterpret_cast(tagged_sp_ & ~static_cast(1u)); + } + + bool GetTag() const { + return (tagged_sp_ & 1u) != 0u; + } + + uintptr_t GetTaggedSp() const { + return tagged_sp_; + } + + private: + explicit TaggedTopQuickFrame(uintptr_t tagged_sp) : tagged_sp_(tagged_sp) { } + + uintptr_t tagged_sp_; + }; + static_assert(sizeof(TaggedTopQuickFrame) == sizeof(uintptr_t), "TaggedTopQuickFrame size check"); + + TaggedTopQuickFrame tagged_top_quick_frame_; ManagedStack* link_; ShadowFrame* top_shadow_frame_; }; diff --git a/runtime/stack.cc b/runtime/stack.cc index ab9fb0d73f..5ad1f7c9c5 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -735,12 +735,19 @@ QuickMethodFrameInfo StackVisitor::GetCurrentQuickFrameInfo() const { return runtime->GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); } - // The only remaining case is if the method is native and uses the generic JNI stub. + // The only remaining case is if the method is native and uses the generic JNI stub, + // called either directly or through some (resolution, instrumentation) trampoline. DCHECK(method->IsNative()); - ClassLinker* class_linker = runtime->GetClassLinker(); - const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(method, - kRuntimePointerSize); - DCHECK(class_linker->IsQuickGenericJniStub(entry_point)) << method->PrettyMethod(); + if (kIsDebugBuild) { + ClassLinker* class_linker = runtime->GetClassLinker(); + const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(method, + kRuntimePointerSize); + CHECK(class_linker->IsQuickGenericJniStub(entry_point) || + // The current entrypoint (after filtering out trampolines) may have changed + // from GenericJNI to JIT-compiled stub since we have entered this frame. + (runtime->GetJit() != nullptr && + runtime->GetJit()->GetCodeCache()->ContainsPc(entry_point))) << method->PrettyMethod(); + } // Generic JNI frame. uint32_t handle_refs = GetNumberOfReferenceArgsWithoutReceiver(method) + 1; size_t scope_size = HandleScope::SizeOf(handle_refs); @@ -776,8 +783,48 @@ void StackVisitor::WalkStack(bool include_transitions) { // Can't be both a shadow and a quick fragment. DCHECK(current_fragment->GetTopShadowFrame() == nullptr); ArtMethod* method = *cur_quick_frame_; + DCHECK(method != nullptr); + bool header_retrieved = false; + if (method->IsNative()) { + // We do not have a PC for the first frame, so we cannot simply use + // ArtMethod::GetOatQuickMethodHeader() as we're unable to distinguish there + // between GenericJNI frame and JIT-compiled JNI stub; the entrypoint may have + // changed since the frame was entered. The top quick frame tag indicates + // GenericJNI here, otherwise it's either AOT-compiled or JNI-compiled JNI stub. + if (UNLIKELY(current_fragment->GetTopQuickFrameTag())) { + // The generic JNI does not have any method header. + cur_oat_quick_method_header_ = nullptr; + } else { + const void* existing_entry_point = method->GetEntryPointFromQuickCompiledCode(); + CHECK(existing_entry_point != nullptr); + Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + // Check whether we can quickly get the header from the current entrypoint. + if (!class_linker->IsQuickGenericJniStub(existing_entry_point) && + !class_linker->IsQuickResolutionStub(existing_entry_point) && + existing_entry_point != GetQuickInstrumentationEntryPoint()) { + cur_oat_quick_method_header_ = + OatQuickMethodHeader::FromEntryPoint(existing_entry_point); + } else { + const void* code = method->GetOatMethodQuickCode(class_linker->GetImagePointerSize()); + if (code != nullptr) { + cur_oat_quick_method_header_ = OatQuickMethodHeader::FromEntryPoint(code); + } else { + // This must be a JITted JNI stub frame. + CHECK(runtime->GetJit() != nullptr); + code = runtime->GetJit()->GetCodeCache()->GetJniStubCode(method); + CHECK(code != nullptr) << method->PrettyMethod(); + cur_oat_quick_method_header_ = OatQuickMethodHeader::FromCodePointer(code); + } + } + } + header_retrieved = true; + } while (method != nullptr) { - cur_oat_quick_method_header_ = method->GetOatQuickMethodHeader(cur_quick_frame_pc_); + if (!header_retrieved) { + cur_oat_quick_method_header_ = method->GetOatQuickMethodHeader(cur_quick_frame_pc_); + } + header_retrieved = false; // Force header retrieval in next iteration. SanityCheckFrame(); if ((walk_kind_ == StackWalkKind::kIncludeInlinedFrames) diff --git a/runtime/stack.h b/runtime/stack.h index bd6204f8d2..a16930bba0 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -140,8 +140,7 @@ class StackVisitor { }; template - void WalkStack(bool include_transitions = false) - REQUIRES_SHARED(Locks::mutator_lock_); + void WalkStack(bool include_transitions = false) REQUIRES_SHARED(Locks::mutator_lock_); Thread* GetThread() const { return thread_; diff --git a/runtime/thread.cc b/runtime/thread.cc index 712eabc888..bec1c908ad 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1884,9 +1884,7 @@ static bool ShouldShowNativeStack(const Thread* thread) } // Threads with no managed stack frames should be shown. - const ManagedStack* managed_stack = thread->GetManagedStack(); - if (managed_stack == nullptr || (managed_stack->GetTopQuickFrame() == nullptr && - managed_stack->GetTopShadowFrame() == nullptr)) { + if (!thread->HasManagedStack()) { return true; } diff --git a/runtime/thread.h b/runtime/thread.h index 39be66d5c2..0803975d26 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -474,13 +474,16 @@ class Thread { tlsPtr_.managed_stack.SetTopQuickFrame(top_method); } + void SetTopOfStackTagged(ArtMethod** top_method) { + tlsPtr_.managed_stack.SetTopQuickFrameTagged(top_method); + } + void SetTopOfShadowStack(ShadowFrame* top) { tlsPtr_.managed_stack.SetTopShadowFrame(top); } bool HasManagedStack() const { - return (tlsPtr_.managed_stack.GetTopQuickFrame() != nullptr) || - (tlsPtr_.managed_stack.GetTopShadowFrame() != nullptr); + return tlsPtr_.managed_stack.HasTopQuickFrame() || tlsPtr_.managed_stack.HasTopShadowFrame(); } // If 'msg' is null, no detail message is set. @@ -833,7 +836,7 @@ class Thread { static ThreadOffset TopOfManagedStackOffset() { return ThreadOffsetFromTlsPtr( OFFSETOF_MEMBER(tls_ptr_sized_values, managed_stack) + - ManagedStack::TopQuickFrameOffset()); + ManagedStack::TaggedTopQuickFrameOffset()); } const ManagedStack* GetManagedStack() const { diff --git a/test/655-jit-clinit/src/Main.java b/test/655-jit-clinit/src/Main.java index 44b315478f..2fb8f2a86e 100644 --- a/test/655-jit-clinit/src/Main.java +++ b/test/655-jit-clinit/src/Main.java @@ -23,7 +23,7 @@ public class Main { Foo.hotMethod(); } - public native static boolean isJitCompiled(Class cls, String methodName); + public native static boolean hasJitCompiledEntrypoint(Class cls, String methodName); private native static boolean hasJit(); } @@ -36,7 +36,7 @@ class Foo { static { array = new Object[10000]; - while (!Main.isJitCompiled(Foo.class, "hotMethod")) { + while (!Main.hasJitCompiledEntrypoint(Foo.class, "hotMethod")) { Foo.hotMethod(); try { // Sleep to give a chance for the JIT to compile `hotMethod`. diff --git a/test/667-jit-jni-stub/expected.txt b/test/667-jit-jni-stub/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/667-jit-jni-stub/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/667-jit-jni-stub/info.txt b/test/667-jit-jni-stub/info.txt new file mode 100644 index 0000000000..6f25c44592 --- /dev/null +++ b/test/667-jit-jni-stub/info.txt @@ -0,0 +1 @@ +Tests for JITting and collecting JNI stubs. diff --git a/test/667-jit-jni-stub/jit_jni_stub_test.cc b/test/667-jit-jni-stub/jit_jni_stub_test.cc new file mode 100644 index 0000000000..82e06fc018 --- /dev/null +++ b/test/667-jit-jni-stub/jit_jni_stub_test.cc @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#include + +#include "jit/jit.h" +#include "jit/jit_code_cache.h" +#include "mirror/class.h" +#include "mirror/string.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" + +namespace art { + +// Local class declared as a friend of JitCodeCache so that we can access its internals. +class JitJniStubTestHelper { + public: + static bool isNextJitGcFull(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { + CHECK(Runtime::Current()->GetJit() != nullptr); + jit::JitCodeCache* cache = Runtime::Current()->GetJit()->GetCodeCache(); + MutexLock mu(self, cache->lock_); + return cache->ShouldDoFullCollection(); + } +}; + +// Calls through to a static method with signature "()V". +extern "C" JNIEXPORT +void Java_Main_callThrough(JNIEnv* env, jclass, jclass klass, jstring methodName) { + ScopedObjectAccess soa(Thread::Current()); + std::string name = soa.Decode(methodName)->ToModifiedUtf8(); + jmethodID method = env->GetStaticMethodID(klass, name.c_str(), "()V"); + CHECK(method != nullptr) << soa.Decode(klass)->PrettyDescriptor() << "." << name; + env->CallStaticVoidMethod(klass, method); +} + +extern "C" JNIEXPORT +void Java_Main_jitGc(JNIEnv*, jclass) { + CHECK(Runtime::Current()->GetJit() != nullptr); + jit::JitCodeCache* cache = Runtime::Current()->GetJit()->GetCodeCache(); + ScopedObjectAccess soa(Thread::Current()); + cache->GarbageCollectCache(Thread::Current()); +} + +extern "C" JNIEXPORT +jboolean Java_Main_isNextJitGcFull(JNIEnv*, jclass) { + ScopedObjectAccess soa(Thread::Current()); + return JitJniStubTestHelper::isNextJitGcFull(soa.Self()); +} + +} // namespace art diff --git a/test/667-jit-jni-stub/run b/test/667-jit-jni-stub/run new file mode 100755 index 0000000000..1877be482e --- /dev/null +++ b/test/667-jit-jni-stub/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. + +# Disable AOT compilation of JNI stubs. +${RUN} "${@}" --no-prebuild --no-dex2oat diff --git a/test/667-jit-jni-stub/src/Main.java b/test/667-jit-jni-stub/src/Main.java new file mode 100644 index 0000000000..b867970eab --- /dev/null +++ b/test/667-jit-jni-stub/src/Main.java @@ -0,0 +1,180 @@ +/* + * 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 { + System.loadLibrary(args[0]); + if (isAotCompiled(Main.class, "hasJit")) { + throw new Error("This test must be run with --no-prebuild --no-dex2oat!"); + } + if (!hasJit()) { + return; + } + + testCompilationUseAndCollection(); + testMixedFramesOnStack(); + } + + public static void testCompilationUseAndCollection() { + // Test that callThrough() can be JIT-compiled. + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertFalse(hasJitCompiledCode(Main.class, "callThrough")); + ensureCompiledCallThroughEntrypoint(/* call */ true); + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + + // Use callThrough() once again now that the method has a JIT-compiled stub. + callThrough(Main.class, "doNothing"); + + // Test that GC with the JIT-compiled stub on the stack does not collect it. + // Also tests stack walk over the JIT-compiled stub. + callThrough(Main.class, "testGcWithCallThroughStubOnStack"); + + // Test that, when marking used methods before a full JIT GC, a single execution + // of the GenericJNI trampoline can save the compiled stub from being collected. + testSingleInvocationTriggersRecompilation(); + + // Test that the JNI compiled stub can actually be collected. + testStubCanBeCollected(); + } + + public static void testGcWithCallThroughStubOnStack() { + // Check that this method was called via JIT-compiled callThrough() stub. + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + // This assertion also exercises stack walk over the JIT-compiled callThrough() stub. + assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); + + doJitGcsUntilFullJitGcIsScheduled(); + // The callThrough() on the stack above this method is using the compiled stub, + // so the JIT GC should not remove the compiled code. + jitGc(); + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + } + + public static void testSingleInvocationTriggersRecompilation() { + // After scheduling a full JIT GC, single call through the GenericJNI + // trampoline should ensure that the compiled stub is used again. + doJitGcsUntilFullJitGcIsScheduled(); + callThrough(Main.class, "doNothing"); + ensureCompiledCallThroughEntrypoint(/* call */ false); // Wait for the compilation task to run. + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + jitGc(); // This JIT GC should not collect the callThrough() stub. + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + } + + public static void testMixedFramesOnStack() { + // Starts without a compiled JNI stub for callThrough(). + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertFalse(hasJitCompiledCode(Main.class, "callThrough")); + callThrough(Main.class, "testMixedFramesOnStackStage2"); + // We have just returned through the JIT-compiled JNI stub, so it must still + // be compiled (though not necessarily with the entrypoint pointing to it). + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + // Though the callThrough() is on the stack, that frame is using the GenericJNI + // and does not prevent the collection of the JNI stub. + testStubCanBeCollected(); + } + + public static void testMixedFramesOnStackStage2() { + // We cannot assert that callThrough() has no JIT compiled stub as that check + // may race against the compilation task. Just check the caller. + assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); + // Now ensure that the JNI stub is compiled and used. + ensureCompiledCallThroughEntrypoint(/* call */ true); + callThrough(Main.class, "testMixedFramesOnStackStage3"); + } + + public static void testMixedFramesOnStackStage3() { + // Check that this method was called via JIT-compiled callThrough() stub. + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + // This assertion also exercises stack walk over the JIT-compiled callThrough() stub. + assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); + // For a good measure, try a JIT GC. + jitGc(); + } + + public static void testStubCanBeCollected() { + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + doJitGcsUntilFullJitGcIsScheduled(); + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + jitGc(); // JIT GC without callThrough() on the stack should collect the callThrough() stub. + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertFalse(hasJitCompiledCode(Main.class, "callThrough")); + } + + public static void doJitGcsUntilFullJitGcIsScheduled() { + // We enter with a compiled stub for callThrough() but we also need the entrypoint to be set. + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + ensureCompiledCallThroughEntrypoint(/* call */ true); + // Perform JIT GC until the next GC is marked to do full collection. + do { + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + callThrough(Main.class, "jitGc"); // JIT GC with callThrough() safely on the stack. + } while (!isNextJitGcFull()); + // The JIT GC before the full collection resets entrypoints and waits to see + // if the methods are still in use. + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + } + + public static void ensureCompiledCallThroughEntrypoint(boolean call) { + int count = 0; + while (!hasJitCompiledEntrypoint(Main.class, "callThrough")) { + // If `call` is true, also exercise the `callThrough()` method to increase hotness. + int limit = call ? 1 << Math.min(count, 12) : 0; + for (int i = 0; i < limit; ++i) { + callThrough(Main.class, "doNothing"); + } + try { + // Sleep to give a chance for the JIT to compile `hasJit` stub. + Thread.sleep(100); + } catch (Exception e) { + // Ignore + } + if (++count == 50) { + throw new Error("TIMEOUT"); + } + }; + } + + public static void assertTrue(boolean value) { + if (!value) { + throw new AssertionError("Expected true!"); + } + } + + public static void assertFalse(boolean value) { + if (value) { + throw new AssertionError("Expected false!"); + } + } + + public static void doNothing() { } + public static void throwError() { throw new Error(); } + + // Note that the callThrough()'s shorty differs from shorties of the other + // native methods used in this test because of the return type `void.` + public native static void callThrough(Class cls, String methodName); + + public native static void jitGc(); + public native static boolean isNextJitGcFull(); + + public native static boolean isAotCompiled(Class cls, String methodName); + public native static boolean hasJitCompiledEntrypoint(Class cls, String methodName); + public native static boolean hasJitCompiledCode(Class cls, String methodName); + private native static boolean hasJit(); +} diff --git a/test/Android.bp b/test/Android.bp index 8f29251907..2d526d256c 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -384,6 +384,7 @@ cc_defaults { "656-annotation-lookup-generic-jni/test.cc", "661-oat-writer-layout/oat_writer_layout.cc", "664-aget-verifier/aget-verifier.cc", + "667-jit-jni-stub/jit_jni_stub_test.cc", "708-jit-cache-churn/jit.cc", ], shared_libs: [ diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index df497c1181..34580800cc 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -152,10 +152,10 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isAotCompiled(JNIEnv* env, return method->GetOatMethodQuickCode(kRuntimePointerSize) != nullptr; } -extern "C" JNIEXPORT jboolean JNICALL Java_Main_isJitCompiled(JNIEnv* env, - jclass, - jclass cls, - jstring method_name) { +extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledEntrypoint(JNIEnv* env, + jclass, + jclass cls, + jstring method_name) { jit::Jit* jit = GetJitIfEnabled(); if (jit == nullptr) { return false; @@ -169,6 +169,23 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isJitCompiled(JNIEnv* env, return jit->GetCodeCache()->ContainsPc(method->GetEntryPointFromQuickCompiledCode()); } +extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledCode(JNIEnv* env, + jclass, + jclass cls, + jstring method_name) { + jit::Jit* jit = GetJitIfEnabled(); + if (jit == nullptr) { + return false; + } + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + ScopedUtfChars chars(env, method_name); + CHECK(chars.c_str() != nullptr); + ArtMethod* method = soa.Decode(cls)->FindDeclaredDirectMethodByName( + chars.c_str(), kRuntimePointerSize); + return jit->GetCodeCache()->ContainsMethod(method); +} + extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env, jclass, jclass cls, -- GitLab From ee58bd494668720447c5d662fc0b8ffbed81dc5d Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Fri, 24 Nov 2017 14:27:03 +0000 Subject: [PATCH 091/226] Re-enable in-process unwind test. The test was skipped for PIC boot images (the check was added in times when those were not really used). Consequently, the test became effectively disabled since the switch to PIC boot images. Fortunately, the new unwind library seems to be able to handle manually loaded libraries (not by dlopen) just fine, so the original reason for skipping PIC images doesn't hold any more. Test: ./test.py -r -b -t 137 -v --32 Test: ./test.py -r -b -t 137 -v --64 Change-Id: Ib48ee9f677321b91e6643debc5c0e8dfab980f00 --- test/137-cfi/cfi.cc | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc index 58b33be573..66270fd5c7 100644 --- a/test/137-cfi/cfi.cc +++ b/test/137-cfi/cfi.cc @@ -104,20 +104,6 @@ static void MoreErrorInfo(pid_t pid, bool sig_quit_on_fail) { } #endif -// Currently we have to fall back to our own loader for the boot image when it's compiled PIC -// because its base is zero. Thus in-process unwinding through it won't work. This is a helper -// detecting this. -#if __linux__ -static bool IsPicImage() { - std::vector image_spaces = - Runtime::Current()->GetHeap()->GetBootImageSpaces(); - CHECK(!image_spaces.empty()); // We should be running with an image. - const OatFile* oat_file = image_spaces[0]->GetOatFile(); - CHECK(oat_file != nullptr); // We should have an oat file to go with the image. - return oat_file->IsPic(); -} -#endif - extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess( JNIEnv*, jobject, @@ -125,11 +111,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess( jint, jboolean) { #if __linux__ - if (IsPicImage()) { - LOG(INFO) << "Image is pic, in-process unwinding check bypassed."; - return JNI_TRUE; - } - // TODO: What to do on Valgrind? std::unique_ptr bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid())); -- GitLab From 566865df6a6292355f061ea857f8df5dcba44ca3 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Wed, 29 Nov 2017 14:32:43 +0000 Subject: [PATCH 092/226] Reduce DWARF local variable error to just warning. Test: m build-art Change-Id: Iaeeb2a7880e6891f5a5c0a5ed554515f0bc5c716 --- compiler/debug/elf_debug_loc_writer.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h index bb856b29f4..1d609af4e6 100644 --- a/compiler/debug/elf_debug_loc_writer.h +++ b/compiler/debug/elf_debug_loc_writer.h @@ -251,7 +251,10 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, // kInStackLargeOffset and kConstantLargeValue are hidden by GetKind(). // kInRegisterHigh and kInFpuRegisterHigh should be handled by // the special cases above and they should not occur alone. - LOG(ERROR) << "Unexpected register location kind: " << kind; + LOG(WARNING) << "Unexpected register location: " << kind + << " (This can indicate either a bug in the dexer when generating" + << " local variable information, or a bug in ART compiler." + << " Please file a bug at go/art-bug)"; break; } if (is64bitValue) { -- GitLab From cd5b86d9e08e04a04e289df881fd2a94c7c8dad1 Mon Sep 17 00:00:00 2001 From: buzbee Date: Fri, 20 Oct 2017 14:00:52 -0700 Subject: [PATCH 093/226] Frame tracking cfi for mterp Add cfi information to allow libunwind to find Java context in native tracebacks. Test: m test-art-host Test: testrunner.py --host --interpreter Change-Id: Ib51b7a692bcc20a0662ae2ff3b8946f409574d57 --- runtime/interpreter/mterp/arm/entry.S | 3 +- runtime/interpreter/mterp/arm/header.S | 1 + runtime/interpreter/mterp/arm64/entry.S | 3 +- runtime/interpreter/mterp/arm64/header.S | 1 + runtime/interpreter/mterp/cfi_asm_support.h | 31 ++++++++++++++++++++ runtime/interpreter/mterp/mips/entry.S | 1 + runtime/interpreter/mterp/mips/header.S | 1 + runtime/interpreter/mterp/mips64/entry.S | 1 + runtime/interpreter/mterp/mips64/header.S | 1 + runtime/interpreter/mterp/out/mterp_arm.S | 4 ++- runtime/interpreter/mterp/out/mterp_arm64.S | 4 ++- runtime/interpreter/mterp/out/mterp_mips.S | 2 ++ runtime/interpreter/mterp/out/mterp_mips64.S | 2 ++ runtime/interpreter/mterp/out/mterp_x86.S | 4 ++- runtime/interpreter/mterp/out/mterp_x86_64.S | 4 ++- runtime/interpreter/mterp/x86/entry.S | 3 +- runtime/interpreter/mterp/x86/header.S | 1 + runtime/interpreter/mterp/x86_64/entry.S | 3 +- runtime/interpreter/mterp/x86_64/header.S | 1 + 19 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 runtime/interpreter/mterp/cfi_asm_support.h diff --git a/runtime/interpreter/mterp/arm/entry.S b/runtime/interpreter/mterp/arm/entry.S index de617a90d7..df4bcc66f3 100644 --- a/runtime/interpreter/mterp/arm/entry.S +++ b/runtime/interpreter/mterp/arm/entry.S @@ -23,7 +23,7 @@ /* * On entry: * r0 Thread* self/ - * r1 code_item + * r1 insns_ * r2 ShadowFrame * r3 JValue* result_register * @@ -56,6 +56,7 @@ ENTRY ExecuteMterpImpl VREG_INDEX_TO_ADDR rREFS, r0 @ point to reference array in shadow frame ldr r0, [r2, #SHADOWFRAME_DEX_PC_OFFSET] @ Get starting dex_pc. add rPC, r1, r0, lsl #1 @ Create direct pointer to 1st dex opcode + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/arm/header.S b/runtime/interpreter/mterp/arm/header.S index 51c2ba4c03..64ab9efa19 100644 --- a/runtime/interpreter/mterp/arm/header.S +++ b/runtime/interpreter/mterp/arm/header.S @@ -85,6 +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" #define MTERP_PROFILE_BRANCHES 1 #define MTERP_LOGGING 0 diff --git a/runtime/interpreter/mterp/arm64/entry.S b/runtime/interpreter/mterp/arm64/entry.S index f3d40ff6f7..8d61210be8 100644 --- a/runtime/interpreter/mterp/arm64/entry.S +++ b/runtime/interpreter/mterp/arm64/entry.S @@ -20,7 +20,7 @@ * Interpreter entry point. * On entry: * x0 Thread* self/ - * x1 code_item + * x1 insns_ * x2 ShadowFrame * x3 JValue* result_register * @@ -46,6 +46,7 @@ ENTRY ExecuteMterpImpl add xREFS, xFP, w0, lsl #2 // point to reference array in shadow frame ldr w0, [x2, #SHADOWFRAME_DEX_PC_OFFSET] // Get starting dex_pc. add xPC, x1, w0, lsl #1 // Create direct pointer to 1st dex opcode + .cfi_register DPC_PSEUDO_REG, xPC EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/arm64/header.S b/runtime/interpreter/mterp/arm64/header.S index 47f12d2f5d..9261b770d6 100644 --- a/runtime/interpreter/mterp/arm64/header.S +++ b/runtime/interpreter/mterp/arm64/header.S @@ -87,6 +87,7 @@ codes. * to expand the macros into assembler assignment statements. */ #include "asm_support.h" +#include "interpreter/mterp/cfi_asm_support.h" #define MTERP_PROFILE_BRANCHES 1 #define MTERP_LOGGING 0 diff --git a/runtime/interpreter/mterp/cfi_asm_support.h b/runtime/interpreter/mterp/cfi_asm_support.h new file mode 100644 index 0000000000..a97e153993 --- /dev/null +++ b/runtime/interpreter/mterp/cfi_asm_support.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_INTERPRETER_MTERP_CFI_ASM_SUPPORT_H_ +#define ART_RUNTIME_INTERPRETER_MTERP_CFI_ASM_SUPPORT_H_ + +/* + * To keep track of the Dalvik PC, give assign it a magic register number that + * won't be confused with a pysical register. Then, standard .cfi directives + * will track the location of it so that it may be extracted during a stack + * unwind. + * + * The Dalvik PC will be in either a physical registor, or the frame. + * Encoded from the ASCII string " DEX" -> 0x20 0x44 0x45 0x58 + */ +#define DPC_PSEUDO_REG 0x20444558 + +#endif // ART_RUNTIME_INTERPRETER_MTERP_CFI_ASM_SUPPORT_H_ diff --git a/runtime/interpreter/mterp/mips/entry.S b/runtime/interpreter/mterp/mips/entry.S index 03de985cd0..3908cb506e 100644 --- a/runtime/interpreter/mterp/mips/entry.S +++ b/runtime/interpreter/mterp/mips/entry.S @@ -53,6 +53,7 @@ ExecuteMterpImpl: EAS2(rREFS, rFP, a0) # point to reference array in shadow frame lw a0, SHADOWFRAME_DEX_PC_OFFSET(a2) # Get starting dex_pc EAS1(rPC, a1, a0) # Create direct pointer to 1st dex opcode + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC() diff --git a/runtime/interpreter/mterp/mips/header.S b/runtime/interpreter/mterp/mips/header.S index e4552ddf3d..1ccaa6443f 100644 --- a/runtime/interpreter/mterp/mips/header.S +++ b/runtime/interpreter/mterp/mips/header.S @@ -32,6 +32,7 @@ */ #include "asm_support.h" +#include "interpreter/mterp/cfi_asm_support.S" #if (__mips==32) && (__mips_isa_rev>=2) #define MIPS32REVGE2 /* mips32r2 and greater */ diff --git a/runtime/interpreter/mterp/mips64/entry.S b/runtime/interpreter/mterp/mips64/entry.S index 436b88dbd0..841a817569 100644 --- a/runtime/interpreter/mterp/mips64/entry.S +++ b/runtime/interpreter/mterp/mips64/entry.S @@ -73,6 +73,7 @@ ExecuteMterpImpl: dlsa rREFS, v0, rFP, 2 lw v0, SHADOWFRAME_DEX_PC_OFFSET(a2) dlsa rPC, v0, a1, 1 + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/mips64/header.S b/runtime/interpreter/mterp/mips64/header.S index d1acefd338..2b550cb533 100644 --- a/runtime/interpreter/mterp/mips64/header.S +++ b/runtime/interpreter/mterp/mips64/header.S @@ -102,6 +102,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" /* * 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 69d7edbe8a..f3c1124ec4 100644 --- a/runtime/interpreter/mterp/out/mterp_arm.S +++ b/runtime/interpreter/mterp/out/mterp_arm.S @@ -92,6 +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" #define MTERP_PROFILE_BRANCHES 1 #define MTERP_LOGGING 0 @@ -341,7 +342,7 @@ unspecified registers or condition codes. /* * On entry: * r0 Thread* self/ - * r1 code_item + * r1 insns_ * r2 ShadowFrame * r3 JValue* result_register * @@ -374,6 +375,7 @@ ENTRY ExecuteMterpImpl VREG_INDEX_TO_ADDR rREFS, r0 @ point to reference array in shadow frame ldr r0, [r2, #SHADOWFRAME_DEX_PC_OFFSET] @ Get starting dex_pc. add rPC, r1, r0, lsl #1 @ Create direct pointer to 1st dex opcode + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S index 82edab465e..347d54f705 100644 --- a/runtime/interpreter/mterp/out/mterp_arm64.S +++ b/runtime/interpreter/mterp/out/mterp_arm64.S @@ -94,6 +94,7 @@ codes. * to expand the macros into assembler assignment statements. */ #include "asm_support.h" +#include "interpreter/mterp/cfi_asm_support.h" #define MTERP_PROFILE_BRANCHES 1 #define MTERP_LOGGING 0 @@ -378,7 +379,7 @@ codes. * Interpreter entry point. * On entry: * x0 Thread* self/ - * x1 code_item + * x1 insns_ * x2 ShadowFrame * x3 JValue* result_register * @@ -404,6 +405,7 @@ ENTRY ExecuteMterpImpl add xREFS, xFP, w0, lsl #2 // point to reference array in shadow frame ldr w0, [x2, #SHADOWFRAME_DEX_PC_OFFSET] // Get starting dex_pc. add xPC, x1, w0, lsl #1 // Create direct pointer to 1st dex opcode + .cfi_register DPC_PSEUDO_REG, xPC EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/out/mterp_mips.S b/runtime/interpreter/mterp/out/mterp_mips.S index 8cc1b19128..9535e254e7 100644 --- a/runtime/interpreter/mterp/out/mterp_mips.S +++ b/runtime/interpreter/mterp/out/mterp_mips.S @@ -39,6 +39,7 @@ */ #include "asm_support.h" +#include "interpreter/mterp/cfi_asm_support.S" #if (__mips==32) && (__mips_isa_rev>=2) #define MIPS32REVGE2 /* mips32r2 and greater */ @@ -786,6 +787,7 @@ ExecuteMterpImpl: EAS2(rREFS, rFP, a0) # point to reference array in shadow frame lw a0, SHADOWFRAME_DEX_PC_OFFSET(a2) # Get starting dex_pc EAS1(rPC, a1, a0) # Create direct pointer to 1st dex opcode + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC() diff --git a/runtime/interpreter/mterp/out/mterp_mips64.S b/runtime/interpreter/mterp/out/mterp_mips64.S index 139ee25904..559c72bb0c 100644 --- a/runtime/interpreter/mterp/out/mterp_mips64.S +++ b/runtime/interpreter/mterp/out/mterp_mips64.S @@ -109,6 +109,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" /* * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, @@ -407,6 +408,7 @@ ExecuteMterpImpl: dlsa rREFS, v0, rFP, 2 lw v0, SHADOWFRAME_DEX_PC_OFFSET(a2) dlsa rPC, v0, a1, 1 + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S index cbab61ebf6..0613c9d12e 100644 --- a/runtime/interpreter/mterp/out/mterp_x86.S +++ b/runtime/interpreter/mterp/out/mterp_x86.S @@ -95,6 +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" /* * Handle mac compiler specific @@ -342,7 +343,7 @@ unspecified registers or condition codes. /* * On entry: * 0 Thread* self - * 1 code_item + * 1 insns_ * 2 ShadowFrame * 3 JValue* result_register * @@ -379,6 +380,7 @@ SYMBOL(ExecuteMterpImpl): leal (rFP, %eax, 4), rREFS movl SHADOWFRAME_DEX_PC_OFFSET(%edx), %eax lea (%ecx, %eax, 2), rPC + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC /* Set up for backwards branches & osr profiling */ diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S index 83c3e4fb91..aa91db3b61 100644 --- a/runtime/interpreter/mterp/out/mterp_x86_64.S +++ b/runtime/interpreter/mterp/out/mterp_x86_64.S @@ -91,6 +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" /* * Handle mac compiler specific @@ -328,7 +329,7 @@ unspecified registers or condition codes. /* * On entry: * 0 Thread* self - * 1 code_item + * 1 insns_ * 2 ShadowFrame * 3 JValue* result_register * @@ -362,6 +363,7 @@ SYMBOL(ExecuteMterpImpl): leaq (rFP, %rax, 4), rREFS movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax leaq (IN_ARG1, %rax, 2), rPC + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/x86/entry.S b/runtime/interpreter/mterp/x86/entry.S index 055e834fed..10ca8366de 100644 --- a/runtime/interpreter/mterp/x86/entry.S +++ b/runtime/interpreter/mterp/x86/entry.S @@ -24,7 +24,7 @@ /* * On entry: * 0 Thread* self - * 1 code_item + * 1 insns_ * 2 ShadowFrame * 3 JValue* result_register * @@ -61,6 +61,7 @@ SYMBOL(ExecuteMterpImpl): leal (rFP, %eax, 4), rREFS movl SHADOWFRAME_DEX_PC_OFFSET(%edx), %eax lea (%ecx, %eax, 2), rPC + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC /* Set up for backwards branches & osr profiling */ diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S index 370012f324..0e585e86f0 100644 --- a/runtime/interpreter/mterp/x86/header.S +++ b/runtime/interpreter/mterp/x86/header.S @@ -88,6 +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" /* * Handle mac compiler specific diff --git a/runtime/interpreter/mterp/x86_64/entry.S b/runtime/interpreter/mterp/x86_64/entry.S index 83b845b702..d85ef7fe24 100644 --- a/runtime/interpreter/mterp/x86_64/entry.S +++ b/runtime/interpreter/mterp/x86_64/entry.S @@ -24,7 +24,7 @@ /* * On entry: * 0 Thread* self - * 1 code_item + * 1 insns_ * 2 ShadowFrame * 3 JValue* result_register * @@ -58,6 +58,7 @@ SYMBOL(ExecuteMterpImpl): leaq (rFP, %rax, 4), rREFS movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax leaq (IN_ARG1, %rax, 2), rPC + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/x86_64/header.S b/runtime/interpreter/mterp/x86_64/header.S index 9d21f3f1a1..a3ef8953ca 100644 --- a/runtime/interpreter/mterp/x86_64/header.S +++ b/runtime/interpreter/mterp/x86_64/header.S @@ -84,6 +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" /* * Handle mac compiler specific -- GitLab From f6df1b5bf2fe2c0aa330df98326a6f360edadba6 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Wed, 29 Nov 2017 14:46:53 -0800 Subject: [PATCH 094/226] Ensure single-step always causes global deopt We were incorrectly failing to globally deopt when the single-step event is enabled if we have an active Breakpoint event at the same time. This could cause single-step events to be missed in some circumstances. This is likely the cause of some rare flakes in the jdwp tests. Bug: 69241796 Bug: 69243589 Test: ./test.py --host -j50 Test: ./test/run-test --host \ --with-agent \ 'libbreakpointlogger.so=LMain;->main([Ljava/lang/String;)V@0' \ 997-single-step Change-Id: I24750625f0a22e61342b336935e32b0d18e9e4d6 --- openjdkjvmti/events.cc | 35 +++++++++++++++++++---------------- openjdkjvmti/events.h | 2 ++ 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index be4ebbc85e..05f9125df9 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -899,9 +899,9 @@ static bool EventNeedsFullDeopt(ArtJvmtiEvent event) { } } -static void SetupTraceListener(JvmtiMethodTraceListener* listener, - ArtJvmtiEvent event, - bool enable) { +void EventHandler::SetupTraceListener(JvmtiMethodTraceListener* listener, + ArtJvmtiEvent event, + bool enable) { bool needs_full_deopt = EventNeedsFullDeopt(event); // Make sure we can deopt. { @@ -921,8 +921,21 @@ static void SetupTraceListener(JvmtiMethodTraceListener* listener, } // Add the actual listeners. - art::ScopedThreadStateChange stsc(art::Thread::Current(), art::ThreadState::kNative); uint32_t new_events = GetInstrumentationEventsFor(event); + if (new_events == art::instrumentation::Instrumentation::kDexPcMoved) { + // Need to skip adding the listeners if the event is breakpoint/single-step since those events + // share the same art-instrumentation underlying event. We need to give them their own deopt + // request though so the test waits until here. + DCHECK(event == ArtJvmtiEvent::kBreakpoint || event == ArtJvmtiEvent::kSingleStep); + ArtJvmtiEvent other = event == ArtJvmtiEvent::kBreakpoint ? ArtJvmtiEvent::kSingleStep + : ArtJvmtiEvent::kBreakpoint; + if (IsEventEnabledAnywhere(other)) { + // The event needs to be kept around/is already enabled by the other jvmti event that uses the + // same instrumentation event. + return; + } + } + 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, @@ -1002,18 +1015,6 @@ void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { case ArtJvmtiEvent::kGarbageCollectionFinish: SetupGcPauseTracking(gc_pause_listener_.get(), event, enable); return; - - case ArtJvmtiEvent::kBreakpoint: - case ArtJvmtiEvent::kSingleStep: { - ArtJvmtiEvent other = (event == ArtJvmtiEvent::kBreakpoint) ? ArtJvmtiEvent::kSingleStep - : ArtJvmtiEvent::kBreakpoint; - // We only need to do anything if there isn't already a listener installed/held-on by the - // other jvmti event that uses DexPcMoved. - if (!IsEventEnabledAnywhere(other)) { - SetupTraceListener(method_trace_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. @@ -1030,6 +1031,8 @@ void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { case ArtJvmtiEvent::kFieldModification: case ArtJvmtiEvent::kException: case ArtJvmtiEvent::kExceptionCatch: + case ArtJvmtiEvent::kBreakpoint: + case ArtJvmtiEvent::kSingleStep: SetupTraceListener(method_trace_listener_.get(), event, enable); return; case ArtJvmtiEvent::kMonitorContendedEnter: diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h index c73215f07b..b05136661b 100644 --- a/openjdkjvmti/events.h +++ b/openjdkjvmti/events.h @@ -234,6 +234,8 @@ class EventHandler { REQUIRES(!envs_lock_); private: + void SetupTraceListener(JvmtiMethodTraceListener* listener, ArtJvmtiEvent event, bool enable); + template ALWAYS_INLINE inline std::vector> CollectEvents(art::Thread* thread, -- GitLab From 4e30b902edd68e7008685afe014ba6443b33b7a7 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Wed, 29 Nov 2017 16:53:25 -0800 Subject: [PATCH 095/226] Use symbolic rather than duplicate string for pass. Test: none Change-Id: Ia912d75e72e7002d098db80ec88f82775d88f6cc --- compiler/optimizing/instruction_simplifier_mips.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/optimizing/instruction_simplifier_mips.h b/compiler/optimizing/instruction_simplifier_mips.h index 22cc2efc1a..6cb8affe85 100644 --- a/compiler/optimizing/instruction_simplifier_mips.h +++ b/compiler/optimizing/instruction_simplifier_mips.h @@ -30,7 +30,7 @@ namespace mips { class InstructionSimplifierMips : public HOptimization { public: InstructionSimplifierMips(HGraph* graph, CodeGenerator* codegen, OptimizingCompilerStats* stats) - : HOptimization(graph, "instruction_simplifier_mips", stats), + : HOptimization(graph, kInstructionSimplifierMipsPassName, stats), codegen_(down_cast(codegen)) {} static constexpr const char* kInstructionSimplifierMipsPassName = "instruction_simplifier_mips"; -- GitLab From 09b2d5022c77a5d24d47323d513dc0b6357ffc6c Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 29 Nov 2017 19:08:16 -0800 Subject: [PATCH 096/226] ART: Allow multiple tests to be requested Allow repeatable "-t" arguments for the testrunner. Bug: 37118012 Test: m Test: art/test/testrunner/testrunner.py -b --host Test: art/test/testrunner/testrunner.py -b --host -t 001-HelloWorld Test: art/test/testrunner/testrunner.py -b --host -t 001-HelloWorld -t 001-Main Change-Id: Iba2bb6de0addb5751df54c483e1a5cfb9b4d11db --- test/testrunner/testrunner.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index e7503827f2..554b8a5429 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -874,7 +874,7 @@ def parse_option(): global run_all_configs parser = argparse.ArgumentParser(description="Runs all or a subset of the ART test suite.") - parser.add_argument('-t', '--test', dest='test', help='name of the test') + 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: @@ -906,10 +906,12 @@ def parse_option(): options = setup_env_for_build_target(target_config[options['build_target']], parser, options) - test = '' + tests = None env.EXTRA_DISABLED_TESTS.update(set(options['skips'])) - if options['test']: - test = parse_test_name(options['test']) + if options['tests']: + tests = set() + for test_name in options['tests']: + tests |= parse_test_name(test_name) for variant_type in VARIANT_TYPE_DICT: for variant in VARIANT_TYPE_DICT[variant_type]: @@ -935,11 +937,11 @@ def parse_option(): if options['run_all']: run_all_configs = True - return test + return tests def main(): gather_test_info() - user_requested_test = parse_option() + user_requested_tests = parse_option() setup_test_env() if build: build_targets = '' @@ -956,8 +958,8 @@ def main(): build_command += ' dist' if subprocess.call(build_command.split()): sys.exit(1) - if user_requested_test: - test_runner_thread = threading.Thread(target=run_tests, args=(user_requested_test,)) + if user_requested_tests: + test_runner_thread = threading.Thread(target=run_tests, args=(user_requested_tests,)) else: test_runner_thread = threading.Thread(target=run_tests, args=(RUN_TEST_SET,)) test_runner_thread.daemon = True -- GitLab From 056d7756152bb3ced81dd57781be5028428ce2bd Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 30 Nov 2017 09:12:13 +0000 Subject: [PATCH 097/226] Revert "Revert "Revert "JIT JNI stubs.""" Still seeing occasional failures on 667-jit-jni-stub Bug: 65574695 Bug: 69843562 This reverts commit e7441631a11e2e07ce863255a59ee4de29c6a56f. Change-Id: I3db751679ef7bdf31c933208aaffe4fac749a14b --- compiler/optimizing/optimizing_compiler.cc | 64 +-- runtime/arch/arm/quick_entrypoints_arm.S | 4 +- runtime/arch/arm64/quick_entrypoints_arm64.S | 2 +- runtime/arch/mips/quick_entrypoints_mips.S | 3 +- .../arch/mips64/quick_entrypoints_mips64.S | 3 +- runtime/arch/x86/quick_entrypoints_x86.S | 4 +- .../arch/x86_64/quick_entrypoints_x86_64.S | 4 +- runtime/art_method-inl.h | 17 +- runtime/art_method.cc | 28 +- runtime/art_method.h | 15 +- runtime/entrypoints/entrypoint_utils.cc | 4 +- .../quick/quick_trampoline_entrypoints.cc | 9 +- runtime/jit/jit.cc | 6 +- runtime/jit/jit_code_cache.cc | 493 ++++-------------- runtime/jit/jit_code_cache.h | 39 +- runtime/jit/profile_saver.cc | 4 +- runtime/managed_stack-inl.h | 4 +- runtime/managed_stack.h | 80 +-- runtime/stack.cc | 59 +-- runtime/stack.h | 3 +- runtime/thread.cc | 4 +- runtime/thread.h | 9 +- test/655-jit-clinit/src/Main.java | 4 +- test/667-jit-jni-stub/expected.txt | 1 - test/667-jit-jni-stub/info.txt | 1 - test/667-jit-jni-stub/jit_jni_stub_test.cc | 63 --- test/667-jit-jni-stub/run | 18 - test/667-jit-jni-stub/src/Main.java | 180 ------- test/Android.bp | 1 - test/common/runtime_state.cc | 25 +- 30 files changed, 190 insertions(+), 961 deletions(-) delete mode 100644 test/667-jit-jni-stub/expected.txt delete mode 100644 test/667-jit-jni-stub/info.txt delete mode 100644 test/667-jit-jni-stub/jit_jni_stub_test.cc delete mode 100755 test/667-jit-jni-stub/run delete mode 100644 test/667-jit-jni-stub/src/Main.java diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 73c72fc57a..a281c4a310 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -1196,69 +1196,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, Runtime* runtime = Runtime::Current(); ArenaAllocator allocator(runtime->GetJitArenaPool()); - - if (UNLIKELY(method->IsNative())) { - JniCompiledMethod jni_compiled_method = ArtQuickJniCompileMethod( - GetCompilerDriver(), access_flags, method_idx, *dex_file); - ScopedNullHandle> roots; - ArenaSet> cha_single_implementation_list( - allocator.Adapter(kArenaAllocCHA)); - const void* code = code_cache->CommitCode( - self, - method, - /* stack_map_data */ nullptr, - /* method_info_data */ nullptr, - /* roots_data */ nullptr, - jni_compiled_method.GetFrameSize(), - jni_compiled_method.GetCoreSpillMask(), - jni_compiled_method.GetFpSpillMask(), - jni_compiled_method.GetCode().data(), - jni_compiled_method.GetCode().size(), - /* data_size */ 0u, - osr, - roots, - /* has_should_deoptimize_flag */ false, - cha_single_implementation_list); - if (code == nullptr) { - return false; - } - - const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); - if (compiler_options.GetGenerateDebugInfo()) { - const auto* method_header = reinterpret_cast(code); - const uintptr_t code_address = reinterpret_cast(method_header->GetCode()); - debug::MethodDebugInfo info = {}; - DCHECK(info.trampoline_name.empty()); - info.dex_file = dex_file; - info.class_def_index = class_def_idx; - info.dex_method_index = method_idx; - info.access_flags = access_flags; - info.code_item = code_item; - info.isa = jni_compiled_method.GetInstructionSet(); - info.deduped = false; - info.is_native_debuggable = compiler_options.GetNativeDebuggable(); - info.is_optimized = true; - info.is_code_address_text_relative = false; - info.code_address = code_address; - info.code_size = jni_compiled_method.GetCode().size(); - info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); - info.code_info = nullptr; - info.cfi = jni_compiled_method.GetCfi(); - std::vector elf_file = debug::WriteDebugElfFileForMethods( - GetCompilerDriver()->GetInstructionSet(), - GetCompilerDriver()->GetInstructionSetFeatures(), - ArrayRef(&info, 1)); - CreateJITCodeEntryForAddress(code_address, std::move(elf_file)); - } - - Runtime::Current()->GetJit()->AddMemoryUsage(method, allocator.BytesUsed()); - if (jit_logger != nullptr) { - jit_logger->WriteLog(code, jni_compiled_method.GetCode().size(), method); - } - return true; - } - - ArenaStack arena_stack(runtime->GetJitArenaPool()); + ArenaStack arena_stack(Runtime::Current()->GetJitArenaPool()); CodeVectorAllocator code_allocator(&allocator); VariableSizedHandleScope handles(self); diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 6ec9c48b92..6ff8dd60b8 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -1783,9 +1783,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] - add ip, ip, #-1 // Remove the GenericJNI tag. ADD/SUB writing directly to SP is UNPREDICTABLE. - mov sp, ip + ldr sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] .cfi_def_cfa_register sp # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 47efeb9200..280e5937c6 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -2299,7 +2299,7 @@ ENTRY art_quick_generic_jni_trampoline .Lexception_in_native: // Move to x1 then sp to please assembler. ldr x1, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET] - add sp, x1, #-1 // Remove the GenericJNI tag. + mov sp, x1 .cfi_def_cfa_register sp # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index fc77a641b3..489c52c0d2 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -2283,8 +2283,7 @@ ENTRY art_quick_generic_jni_trampoline nop 2: - lw $t0, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) - addiu $sp, $t0, -1 // Remove the GenericJNI tag. + lw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) move $gp, $s3 # restore $gp from $s3 # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 3fb83d9232..98ffe6504a 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -2158,8 +2158,7 @@ ENTRY art_quick_generic_jni_trampoline dmtc1 $v0, $f0 # place return value to FP return value 1: - ld $t0, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) - daddiu $sp, $t0, -1 // Remove the GenericJNI tag. + ld $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION END art_quick_generic_jni_trampoline diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index a46ceeba12..25716dc1bb 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1969,9 +1969,7 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline punpckldq %xmm1, %xmm0 ret .Lexception_in_native: - pushl %fs:THREAD_TOP_QUICK_FRAME_OFFSET - addl LITERAL(-1), (%esp) // Remove the GenericJNI tag. - movl (%esp), %esp + movl %fs:THREAD_TOP_QUICK_FRAME_OFFSET, %esp // Do a call to push a new save-all frame required by the runtime. call .Lexception_call .Lexception_call: diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 463e5a279f..2c3da90f25 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -1958,9 +1958,7 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline movq %rax, %xmm0 ret .Lexception_in_native: - pushq %gs:THREAD_TOP_QUICK_FRAME_OFFSET - addq LITERAL(-1), (%rsp) // Remove the GenericJNI tag. - movq (%rsp), %rsp + movq %gs:THREAD_TOP_QUICK_FRAME_OFFSET, %rsp CFI_DEF_CFA_REGISTER(rsp) // Do a call to push a new save-all frame required by the runtime. call .Lexception_call diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 31abf94889..50913def93 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -95,12 +95,10 @@ inline uint16_t ArtMethod::GetMethodIndexDuringLinking() { return method_index_; } -template inline uint32_t ArtMethod::GetDexMethodIndex() { if (kCheckDeclaringClassState) { - CHECK(IsRuntimeMethod() || - GetDeclaringClass()->IsIdxLoaded() || - GetDeclaringClass()->IsErroneous()); + CHECK(IsRuntimeMethod() || GetDeclaringClass()->IsIdxLoaded() || + GetDeclaringClass()->IsErroneous()); } return GetDexMethodIndexUnchecked(); } @@ -204,14 +202,7 @@ inline const char* ArtMethod::GetShorty() { inline const char* ArtMethod::GetShorty(uint32_t* out_length) { DCHECK(!IsProxyMethod()); const DexFile* dex_file = GetDexFile(); - // Don't do a read barrier in the DCHECK() inside GetDexMethodIndex() as GetShorty() - // can be called when the declaring class is about to be unloaded and cannot be added - // to the mark stack (subsequent GC assertion would fail). - // It is safe to avoid the read barrier as the ArtMethod is constructed with a declaring - // Class already satisfying the DCHECK() inside GetDexMethodIndex(), so even if that copy - // of declaring class becomes a from-space object, it shall satisfy the DCHECK(). - return dex_file->GetMethodShorty(dex_file->GetMethodId(GetDexMethodIndex()), - out_length); + return dex_file->GetMethodShorty(dex_file->GetMethodId(GetDexMethodIndex()), out_length); } inline const Signature ArtMethod::GetSignature() { @@ -328,7 +319,7 @@ inline mirror::ClassLoader* ArtMethod::GetClassLoader() { template inline mirror::DexCache* ArtMethod::GetDexCache() { - if (LIKELY(!IsObsolete())) { + if (LIKELY(!IsObsolete())) { mirror::Class* klass = GetDeclaringClass(); return klass->GetDexCache(); } else { diff --git a/runtime/art_method.cc b/runtime/art_method.cc index bdbc4509f3..fa0c501e31 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -587,6 +587,11 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { CHECK(existing_entry_point != nullptr) << PrettyMethod() << "@" << this; ClassLinker* class_linker = runtime->GetClassLinker(); + if (class_linker->IsQuickGenericJniStub(existing_entry_point)) { + // The generic JNI does not have any method header. + return nullptr; + } + if (existing_entry_point == GetQuickProxyInvokeHandler()) { DCHECK(IsProxyMethod() && !IsConstructor()); // The proxy entry point does not have any method header. @@ -594,8 +599,7 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { } // Check whether the current entry point contains this pc. - if (!class_linker->IsQuickGenericJniStub(existing_entry_point) && - !class_linker->IsQuickResolutionStub(existing_entry_point) && + if (!class_linker->IsQuickResolutionStub(existing_entry_point) && !class_linker->IsQuickToInterpreterBridge(existing_entry_point)) { OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromEntryPoint(existing_entry_point); @@ -628,13 +632,19 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { OatFile::OatMethod oat_method = FindOatMethodFor(this, class_linker->GetImagePointerSize(), &found); if (!found) { - if (IsNative()) { - // We are running the GenericJNI stub. The entrypoint may point - // to different entrypoints or to a JIT-compiled JNI stub. - DCHECK(class_linker->IsQuickGenericJniStub(existing_entry_point) || - class_linker->IsQuickResolutionStub(existing_entry_point) || - existing_entry_point == GetQuickInstrumentationEntryPoint() || - (jit != nullptr && jit->GetCodeCache()->ContainsPc(existing_entry_point))); + if (class_linker->IsQuickResolutionStub(existing_entry_point)) { + // We are running the generic jni stub, but the entry point of the method has not + // been updated yet. + DCHECK_EQ(pc, 0u) << "Should be a downcall"; + DCHECK(IsNative()); + return nullptr; + } + if (existing_entry_point == GetQuickInstrumentationEntryPoint()) { + // We are running the generic jni stub, but the method is being instrumented. + // NB We would normally expect the pc to be zero but we can have non-zero pc's if + // instrumentation is installed or removed during the call which is using the generic jni + // trampoline. + DCHECK(IsNative()); return nullptr; } // Only for unit tests. diff --git a/runtime/art_method.h b/runtime/art_method.h index 0a592e0528..dca6f37254 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -242,9 +242,8 @@ class ArtMethod FINAL { return (GetAccessFlags() & kAccDefault) != 0; } - template bool IsObsolete() { - return (GetAccessFlags() & kAccObsoleteMethod) != 0; + return (GetAccessFlags() & kAccObsoleteMethod) != 0; } void SetIsObsolete() { @@ -377,7 +376,6 @@ class ArtMethod FINAL { ALWAYS_INLINE uint32_t GetDexMethodIndexUnchecked() { return dex_method_index_; } - template ALWAYS_INLINE uint32_t GetDexMethodIndex() REQUIRES_SHARED(Locks::mutator_lock_); void SetDexMethodIndex(uint32_t new_idx) { @@ -462,11 +460,12 @@ class ArtMethod FINAL { } ProfilingInfo* GetProfilingInfo(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_) { - // Don't do a read barrier in the DCHECK() inside GetAccessFlags() called by IsNative(), - // as GetProfilingInfo is called in places where the declaring class is treated as a weak - // reference (accessing it with a read barrier would either prevent unloading the class, - // or crash the runtime if the GC wants to unload it). - if (UNLIKELY(IsNative()) || UNLIKELY(IsProxyMethod())) { + // Don't do a read barrier in the DCHECK, as GetProfilingInfo is called in places + // where the declaring class is treated as a weak reference (accessing it with + // a read barrier would either prevent unloading the class, or crash the runtime if + // the GC wants to unload it). + DCHECK(!IsNative()); + if (UNLIKELY(IsProxyMethod())) { return nullptr; } return reinterpret_cast(GetDataPtrSize(pointer_size)); diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index f3450da306..2bf4372b1f 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -245,7 +245,7 @@ ArtMethod* GetCalleeSaveMethodCaller(ArtMethod** sp, CalleeSaveType type, bool d CallerAndOuterMethod GetCalleeSaveMethodCallerAndOuterMethod(Thread* self, CalleeSaveType type) { CallerAndOuterMethod result; ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrameKnownNotTagged(); + ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame(); auto outer_caller_and_pc = DoGetCalleeSaveMethodOuterCallerAndPc(sp, type); result.outer_method = outer_caller_and_pc.first; uintptr_t caller_pc = outer_caller_and_pc.second; @@ -256,7 +256,7 @@ CallerAndOuterMethod GetCalleeSaveMethodCallerAndOuterMethod(Thread* self, Calle ArtMethod* GetCalleeSaveOuterMethod(Thread* self, CalleeSaveType type) { ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrameKnownNotTagged(); + ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame(); return DoGetCalleeSaveMethodOuterCallerAndPc(sp, type).first; } diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 0a76cddf5e..2496aa0f58 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -31,7 +31,6 @@ #include "index_bss_mapping.h" #include "instrumentation.h" #include "interpreter/interpreter.h" -#include "jit/jit.h" #include "linear_alloc.h" #include "method_handles.h" #include "method_reference.h" @@ -2168,11 +2167,6 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod** // Note: We cannot walk the stack properly until fixed up below. 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(); @@ -2194,7 +2188,7 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod** } // Fix up managed-stack things in Thread. After this we can walk the stack. - self->SetTopOfStackTagged(sp); + self->SetTopOfStack(sp); self->VerifyStack(); @@ -2314,7 +2308,6 @@ extern "C" uint64_t artQuickGenericJniEndTrampoline(Thread* self, // anything that requires a mutator lock before that would cause problems as GC may have the // exclusive mutator lock and may be moving objects, etc. ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame(); - DCHECK(self->GetManagedStack()->GetTopQuickFrameTag()); uint32_t* sp32 = reinterpret_cast(sp); ArtMethod* called = *sp; uint32_t cookie = *(sp32 - 1); diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 0d95bc6e64..953e195480 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -643,7 +643,7 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ return; } - if (method->IsClassInitializer() || !method->IsCompilable()) { + if (method->IsClassInitializer() || method->IsNative() || !method->IsCompilable()) { // We do not want to compile such methods. return; } @@ -659,8 +659,7 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ count *= priority_thread_weight_; } int32_t new_count = starting_count + count; // int32 here to avoid wrap-around; - // Note: Native method have no "warm" state or profiling info. - if (LIKELY(!method->IsNative()) && starting_count < warm_method_threshold_) { + if (starting_count < warm_method_threshold_) { if ((new_count >= warm_method_threshold_) && (method->GetProfilingInfo(kRuntimePointerSize) == nullptr)) { bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false); @@ -697,7 +696,6 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ // 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)) { DCHECK(thread_pool_ != nullptr); thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr)); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index a5c167eee8..32205138bd 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -55,107 +55,6 @@ static constexpr int kProtCode = PROT_READ | PROT_EXEC; static constexpr size_t kCodeSizeLogThreshold = 50 * KB; static constexpr size_t kStackMapSizeLogThreshold = 50 * KB; -class JitCodeCache::JniStubKey { - public: - explicit JniStubKey(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) - : shorty_(method->GetShorty()), - is_static_(method->IsStatic()), - is_fast_native_(method->IsFastNative()), - is_critical_native_(method->IsCriticalNative()), - is_synchronized_(method->IsSynchronized()) { - DCHECK(!(is_fast_native_ && is_critical_native_)); - } - - bool operator<(const JniStubKey& rhs) const { - if (is_static_ != rhs.is_static_) { - return rhs.is_static_; - } - if (is_synchronized_ != rhs.is_synchronized_) { - return rhs.is_synchronized_; - } - if (is_fast_native_ != rhs.is_fast_native_) { - return rhs.is_fast_native_; - } - if (is_critical_native_ != rhs.is_critical_native_) { - return rhs.is_critical_native_; - } - return strcmp(shorty_, rhs.shorty_) < 0; - } - - // Update the shorty to point to another method's shorty. Call this function when removing - // the method that references the old shorty from JniCodeData and not removing the entire - // JniCodeData; the old shorty may become a dangling pointer when that method is unloaded. - void UpdateShorty(ArtMethod* method) const REQUIRES_SHARED(Locks::mutator_lock_) { - const char* shorty = method->GetShorty(); - DCHECK_STREQ(shorty_, shorty); - shorty_ = shorty; - } - - private: - // The shorty points to a DexFile data and may need to change - // to point to the same shorty in a different DexFile. - mutable const char* shorty_; - - const bool is_static_; - const bool is_fast_native_; - const bool is_critical_native_; - const bool is_synchronized_; -}; - -class JitCodeCache::JniStubData { - public: - JniStubData() : code_(nullptr), methods_() {} - - void SetCode(const void* code) { - DCHECK(code != nullptr); - code_ = code; - } - - const void* GetCode() const { - return code_; - } - - bool IsCompiled() const { - return GetCode() != nullptr; - } - - void AddMethod(ArtMethod* method) { - if (!ContainsElement(methods_, method)) { - methods_.push_back(method); - } - } - - const std::vector& GetMethods() const { - return methods_; - } - - void RemoveMethodsIn(const LinearAlloc& alloc) { - auto kept_end = std::remove_if( - methods_.begin(), - methods_.end(), - [&alloc](ArtMethod* method) { return alloc.ContainsUnsafe(method); }); - methods_.erase(kept_end, methods_.end()); - } - - bool RemoveMethod(ArtMethod* method) { - auto it = std::find(methods_.begin(), methods_.end(), method); - if (it != methods_.end()) { - methods_.erase(it); - return true; - } else { - return false; - } - } - - void MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) { - std::replace(methods_.begin(), methods_.end(), old_method, new_method); - } - - private: - const void* code_; - std::vector methods_; -}; - JitCodeCache* JitCodeCache::Create(size_t initial_capacity, size_t max_capacity, bool generate_debug_info, @@ -294,36 +193,14 @@ bool JitCodeCache::ContainsPc(const void* ptr) const { bool JitCodeCache::ContainsMethod(ArtMethod* method) { MutexLock mu(Thread::Current(), lock_); - if (UNLIKELY(method->IsNative())) { - auto it = jni_stubs_map_.find(JniStubKey(method)); - if (it != jni_stubs_map_.end() && - it->second.IsCompiled() && - ContainsElement(it->second.GetMethods(), method)) { + for (auto& it : method_code_map_) { + if (it.second == method) { return true; } - } else { - for (const auto& it : method_code_map_) { - if (it.second == method) { - return true; - } - } } return false; } -const void* JitCodeCache::GetJniStubCode(ArtMethod* method) { - DCHECK(method->IsNative()); - MutexLock mu(Thread::Current(), lock_); - auto it = jni_stubs_map_.find(JniStubKey(method)); - if (it != jni_stubs_map_.end()) { - JniStubData& data = it->second; - if (data.IsCompiled() && ContainsElement(data.GetMethods(), method)) { - return data.GetCode(); - } - } - return nullptr; -} - class ScopedCodeCacheWrite : ScopedTrace { public: explicit ScopedCodeCacheWrite(MemMap* code_map, bool only_for_tlb_shootdown = false) @@ -549,9 +426,7 @@ void JitCodeCache::FreeCode(const void* code_ptr) { // Notify native debugger that we are about to remove the code. // It does nothing if we are not using native debugger. DeleteJITCodeEntryForAddress(reinterpret_cast(code_ptr)); - if (OatQuickMethodHeader::FromCodePointer(code_ptr)->IsOptimized()) { - FreeData(GetRootTable(code_ptr)); - } // else this is a JNI stub without any data. + FreeData(GetRootTable(code_ptr)); FreeCode(reinterpret_cast(allocation)); } @@ -588,16 +463,6 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { // lead to a deadlock. { ScopedCodeCacheWrite scc(code_map_.get()); - for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) { - it->second.RemoveMethodsIn(alloc); - if (it->second.GetMethods().empty()) { - method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->second.GetCode())); - it = jni_stubs_map_.erase(it); - } else { - it->first.UpdateShorty(it->second.GetMethods().front()); - ++it; - } - } for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { if (alloc.ContainsUnsafe(it->second)) { method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->first)); @@ -707,8 +572,7 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, bool has_should_deoptimize_flag, const ArenaSet& cha_single_implementation_list) { - DCHECK_NE(stack_map != nullptr, method->IsNative()); - DCHECK(!method->IsNative() || !osr); + DCHECK(stack_map != nullptr); size_t alignment = GetInstructionSetAlignment(kRuntimeISA); // Ensure the header ends up at expected instruction alignment. size_t header_size = RoundUp(sizeof(OatQuickMethodHeader), alignment); @@ -732,8 +596,8 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, std::copy(code, code + code_size, code_ptr); method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); new (method_header) OatQuickMethodHeader( - (stack_map != nullptr) ? code_ptr - stack_map : 0u, - (method_info != nullptr) ? code_ptr - method_info : 0u, + code_ptr - stack_map, + code_ptr - method_info, frame_size_in_bytes, core_spill_mask, fp_spill_mask, @@ -788,40 +652,24 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, // possible that the compiled code is considered invalidated by some class linking, // but below we still make the compiled code valid for the method. MutexLock mu(self, lock_); - if (UNLIKELY(method->IsNative())) { - DCHECK(stack_map == nullptr); - DCHECK(roots_data == nullptr); - auto it = jni_stubs_map_.find(JniStubKey(method)); - DCHECK(it != jni_stubs_map_.end()) - << "Entry inserted in NotifyCompilationOf() should be alive."; - JniStubData* data = &it->second; - DCHECK(ContainsElement(data->GetMethods(), method)) - << "Entry inserted in NotifyCompilationOf() should contain this method."; - data->SetCode(code_ptr); - instrumentation::Instrumentation* instrum = Runtime::Current()->GetInstrumentation(); - for (ArtMethod* m : data->GetMethods()) { - instrum->UpdateMethodsCode(m, method_header->GetEntryPoint()); - } + // Fill the root table before updating the entry point. + DCHECK_EQ(FromStackMapToRoots(stack_map), roots_data); + DCHECK_LE(roots_data, stack_map); + 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)); + } + method_code_map_.Put(code_ptr, method); + if (osr) { + number_of_osr_compilations_++; + osr_code_map_.Put(method, code_ptr); } else { - // Fill the root table before updating the entry point. - DCHECK_EQ(FromStackMapToRoots(stack_map), roots_data); - DCHECK_LE(roots_data, stack_map); - 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)); - } - method_code_map_.Put(code_ptr, method); - if (osr) { - number_of_osr_compilations_++; - osr_code_map_.Put(method, code_ptr); - } else { - Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( - method, method_header->GetEntryPoint()); - } + Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( + method, method_header->GetEntryPoint()); } if (collection_in_progress_) { // We need to update the live bitmap if there is a GC to ensure it sees this new @@ -855,18 +703,45 @@ size_t JitCodeCache::CodeCacheSize() { } bool JitCodeCache::RemoveMethod(ArtMethod* method, bool release_memory) { - // This function is used only for testing and only with non-native methods. - CHECK(!method->IsNative()); - MutexLock mu(Thread::Current(), lock_); + if (method->IsNative()) { + return false; + } + + bool in_cache = false; + { + ScopedCodeCacheWrite ccw(code_map_.get()); + for (auto code_iter = method_code_map_.begin(); code_iter != method_code_map_.end();) { + if (code_iter->second == method) { + if (release_memory) { + FreeCode(code_iter->first); + } + code_iter = method_code_map_.erase(code_iter); + in_cache = true; + continue; + } + ++code_iter; + } + } - bool osr = osr_code_map_.find(method) != osr_code_map_.end(); - bool in_cache = RemoveMethodLocked(method, release_memory); + bool osr = false; + auto code_map = osr_code_map_.find(method); + if (code_map != osr_code_map_.end()) { + osr_code_map_.erase(code_map); + osr = true; + } if (!in_cache) { return false; } + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + if (info != nullptr) { + auto profile = std::find(profiling_infos_.begin(), profiling_infos_.end(), info); + DCHECK(profile != profiling_infos_.end()); + profiling_infos_.erase(profile); + } + method->SetProfilingInfo(nullptr); method->ClearCounter(); Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( method, GetQuickToInterpreterBridge()); @@ -878,58 +753,34 @@ bool JitCodeCache::RemoveMethod(ArtMethod* method, bool release_memory) { return true; } -bool JitCodeCache::RemoveMethodLocked(ArtMethod* method, bool release_memory) { - if (LIKELY(!method->IsNative())) { - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - if (info != nullptr) { - RemoveElement(profiling_infos_, info); - } - method->SetProfilingInfo(nullptr); - } - - bool in_cache = false; - ScopedCodeCacheWrite ccw(code_map_.get()); - if (UNLIKELY(method->IsNative())) { - auto it = jni_stubs_map_.find(JniStubKey(method)); - if (it != jni_stubs_map_.end() && it->second.RemoveMethod(method)) { - in_cache = true; - if (it->second.GetMethods().empty()) { - if (release_memory) { - FreeCode(it->second.GetCode()); - } - jni_stubs_map_.erase(it); - } else { - it->first.UpdateShorty(it->second.GetMethods().front()); - } - } - } else { - for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { - if (it->second == method) { - in_cache = true; - if (release_memory) { - FreeCode(it->first); - } - it = method_code_map_.erase(it); - } else { - ++it; - } - } - - auto osr_it = osr_code_map_.find(method); - if (osr_it != osr_code_map_.end()) { - osr_code_map_.erase(osr_it); - } - } - - return in_cache; -} - // This notifies the code cache that the given method has been redefined and that it should remove // any cached information it has on the method. All threads must be suspended before calling this // method. The compiled code for the method (if there is any) must not be in any threads call stack. void JitCodeCache::NotifyMethodRedefined(ArtMethod* method) { MutexLock mu(Thread::Current(), lock_); - RemoveMethodLocked(method, /* release_memory */ true); + if (method->IsNative()) { + return; + } + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + if (info != nullptr) { + auto profile = std::find(profiling_infos_.begin(), profiling_infos_.end(), info); + DCHECK(profile != profiling_infos_.end()); + profiling_infos_.erase(profile); + } + method->SetProfilingInfo(nullptr); + ScopedCodeCacheWrite ccw(code_map_.get()); + for (auto code_iter = method_code_map_.begin(); code_iter != method_code_map_.end();) { + if (code_iter->second == method) { + FreeCode(code_iter->first); + code_iter = method_code_map_.erase(code_iter); + continue; + } + ++code_iter; + } + auto code_map = osr_code_map_.find(method); + if (code_map != osr_code_map_.end()) { + osr_code_map_.erase(code_map); + } } // This invalidates old_method. Once this function returns one can no longer use old_method to @@ -939,15 +790,11 @@ void JitCodeCache::NotifyMethodRedefined(ArtMethod* method) { // shouldn't be used since it is no longer logically in the jit code cache. // TODO We should add DCHECKS that validate that the JIT is paused when this method is entered. void JitCodeCache::MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) { - MutexLock mu(Thread::Current(), lock_); + // Native methods have no profiling info and need no special handling from the JIT code cache. if (old_method->IsNative()) { - // Update methods in jni_stubs_map_. - for (auto& entry : jni_stubs_map_) { - JniStubData& data = entry.second; - data.MoveObsoleteMethod(old_method, new_method); - } return; } + MutexLock mu(Thread::Current(), lock_); // Update ProfilingInfo to the new one and remove it from the old_method. if (old_method->GetProfilingInfo(kRuntimePointerSize) != nullptr) { DCHECK_EQ(old_method->GetProfilingInfo(kRuntimePointerSize)->GetMethod(), old_method); @@ -1089,7 +936,7 @@ class MarkCodeClosure FINAL : public Closure { // its stack frame, it is not the method owning return_pc_. We just pass null to // LookupMethodHeader: the method is only checked against in debug builds. OatQuickMethodHeader* method_header = - code_cache_->LookupMethodHeader(frame.return_pc_, /* method */ nullptr); + code_cache_->LookupMethodHeader(frame.return_pc_, nullptr); if (method_header != nullptr) { const void* code = method_header->GetCode(); CHECK(code_cache_->GetLiveBitmap()->Test(FromCodeToAllocation(code))); @@ -1242,7 +1089,7 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { const void* entry_point = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); if (ContainsPc(entry_point)) { info->SetSavedEntryPoint(entry_point); - // Don't call Instrumentation::UpdateMethodsCode(), as it can check the declaring + // Don't call Instrumentation::UpdateMethods, as it can check the declaring // class of the method. We may be concurrently running a GC which makes accessing // the class unsafe. We know it is OK to bypass the instrumentation as we've just // checked that the current entry point is JIT compiled code. @@ -1251,25 +1098,6 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { } DCHECK(CheckLiveCompiledCodeHasProfilingInfo()); - - // Change entry points of native methods back to the GenericJNI entrypoint. - for (const auto& entry : jni_stubs_map_) { - const JniStubData& data = entry.second; - if (!data.IsCompiled()) { - continue; - } - // Make sure a single invocation of the GenericJNI trampoline tries to recompile. - uint16_t new_counter = Runtime::Current()->GetJit()->HotMethodThreshold() - 1u; - const OatQuickMethodHeader* method_header = - OatQuickMethodHeader::FromCodePointer(data.GetCode()); - for (ArtMethod* method : data.GetMethods()) { - if (method->GetEntryPointFromQuickCompiledCode() == method_header->GetEntryPoint()) { - // Don't call Instrumentation::UpdateMethodsCode(), same as for normal methods above. - method->SetCounter(new_counter); - method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub()); - } - } - } } live_bitmap_.reset(nullptr); NotifyCollectionDone(self); @@ -1285,22 +1113,13 @@ void JitCodeCache::RemoveUnmarkedCode(Thread* self) { MutexLock mu(self, lock_); ScopedCodeCacheWrite scc(code_map_.get()); // 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; - if (!data->IsCompiled() || GetLiveBitmap()->Test(FromCodeToAllocation(data->GetCode()))) { - ++it; - } else { - method_headers.insert(OatQuickMethodHeader::FromCodePointer(data->GetCode())); - it = jni_stubs_map_.erase(it); - } - } for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { const void* code_ptr = it->first; uintptr_t allocation = FromCodeToAllocation(code_ptr); if (GetLiveBitmap()->Test(allocation)) { ++it; } else { - method_headers.insert(OatQuickMethodHeader::FromCodePointer(code_ptr)); + method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->first)); it = method_code_map_.erase(it); } } @@ -1339,17 +1158,6 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { // an entry point is either: // - an osr compiled code, that will be removed if not in a thread call stack. // - discarded compiled code, that will be removed if not in a thread call stack. - for (const auto& entry : jni_stubs_map_) { - const JniStubData& data = entry.second; - const void* code_ptr = data.GetCode(); - const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); - for (ArtMethod* method : data.GetMethods()) { - if (method_header->GetEntryPoint() == method->GetEntryPointFromQuickCompiledCode()) { - GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr)); - break; - } - } - } for (const auto& it : method_code_map_) { ArtMethod* method = it.second; const void* code_ptr = it.first; @@ -1429,51 +1237,19 @@ OatQuickMethodHeader* JitCodeCache::LookupMethodHeader(uintptr_t pc, ArtMethod* return nullptr; } - if (!kIsDebugBuild) { - // Called with null `method` only from MarkCodeClosure::Run() in debug build. - CHECK(method != nullptr); - } - MutexLock mu(Thread::Current(), lock_); - OatQuickMethodHeader* method_header = nullptr; - ArtMethod* found_method = nullptr; // Only for DCHECK(), not for JNI stubs. - if (method != nullptr && UNLIKELY(method->IsNative())) { - auto it = jni_stubs_map_.find(JniStubKey(method)); - if (it == jni_stubs_map_.end() || !ContainsElement(it->second.GetMethods(), method)) { - return nullptr; - } - const void* code_ptr = it->second.GetCode(); - method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); - if (!method_header->Contains(pc)) { - return nullptr; - } - } else { - auto it = method_code_map_.lower_bound(reinterpret_cast(pc)); - if (it != method_code_map_.begin()) { - --it; - const void* code_ptr = it->first; - if (OatQuickMethodHeader::FromCodePointer(code_ptr)->Contains(pc)) { - method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); - found_method = it->second; - } - } - if (method_header == nullptr && method == nullptr) { - // Scan all compiled JNI stubs as well. This slow search is used only - // for checks in debug build, for release builds the `method` is not null. - for (auto&& entry : jni_stubs_map_) { - const JniStubData& data = entry.second; - if (data.IsCompiled() && - OatQuickMethodHeader::FromCodePointer(data.GetCode())->Contains(pc)) { - method_header = OatQuickMethodHeader::FromCodePointer(data.GetCode()); - } - } - } - if (method_header == nullptr) { - return nullptr; - } + if (method_code_map_.empty()) { + return nullptr; } + auto it = method_code_map_.lower_bound(reinterpret_cast(pc)); + --it; - if (kIsDebugBuild && method != nullptr && !method->IsNative()) { + const void* code_ptr = it->first; + OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + if (!method_header->Contains(pc)) { + return nullptr; + } + if (kIsDebugBuild && method != nullptr) { // When we are walking the stack to redefine classes and creating obsolete methods it is // possible that we might have updated the method_code_map by making this method obsolete in a // previous frame. Therefore we should just check that the non-obsolete version of this method @@ -1482,9 +1258,9 @@ OatQuickMethodHeader* JitCodeCache::LookupMethodHeader(uintptr_t pc, ArtMethod* // occur when we are in the process of allocating and setting up obsolete methods. Otherwise // method and it->second should be identical. (See openjdkjvmti/ti_redefine.cc for more // information.) - DCHECK_EQ(found_method->GetNonObsoleteMethod(), method->GetNonObsoleteMethod()) + DCHECK_EQ(it->second->GetNonObsoleteMethod(), method->GetNonObsoleteMethod()) << ArtMethod::PrettyMethod(method->GetNonObsoleteMethod()) << " " - << ArtMethod::PrettyMethod(found_method->GetNonObsoleteMethod()) << " " + << ArtMethod::PrettyMethod(it->second->GetNonObsoleteMethod()) << " " << std::hex << pc; } return method_header; @@ -1673,51 +1449,21 @@ bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr return false; } - if (UNLIKELY(method->IsNative())) { - JniStubKey key(method); - auto it = jni_stubs_map_.find(key); - bool new_compilation = false; - if (it == jni_stubs_map_.end()) { - // Create a new entry to mark the stub as being compiled. - it = jni_stubs_map_.Put(key, JniStubData{}); - new_compilation = true; - } - JniStubData* data = &it->second; - data->AddMethod(method); - if (data->IsCompiled()) { - OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(data->GetCode()); - const void* entrypoint = method_header->GetEntryPoint(); - // Update also entrypoints of other methods held by the JniStubData. - // We could simply update the entrypoint of `method` but if the last JIT GC has - // changed these entrypoints to GenericJNI in preparation for a full GC, we may - // as well change them back as this stub shall not be collected anyway and this - // can avoid a few expensive GenericJNI calls. - instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); - for (ArtMethod* m : data->GetMethods()) { - instrumentation->UpdateMethodsCode(m, entrypoint); - } - if (collection_in_progress_) { - GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(data->GetCode())); - } - } - return new_compilation; - } else { - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - if (info == nullptr) { - VLOG(jit) << method->PrettyMethod() << " needs a ProfilingInfo to be compiled"; - // Because the counter is not atomic, there are some rare cases where we may not hit the - // threshold for creating the ProfilingInfo. Reset the counter now to "correct" this. - ClearMethodCounter(method, /*was_warm*/ false); - return false; - } - - if (info->IsMethodBeingCompiled(osr)) { - return false; - } + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + if (info == nullptr) { + VLOG(jit) << method->PrettyMethod() << " needs a ProfilingInfo to be compiled"; + // Because the counter is not atomic, there are some rare cases where we may not hit the + // threshold for creating the ProfilingInfo. Reset the counter now to "correct" this. + ClearMethodCounter(method, /*was_warm*/ false); + return false; + } - info->SetIsMethodBeingCompiled(true, osr); - return true; + if (info->IsMethodBeingCompiled(osr)) { + return false; } + + info->SetIsMethodBeingCompiled(true, osr); + return true; } ProfilingInfo* JitCodeCache::NotifyCompilerUse(ArtMethod* method, Thread* self) { @@ -1739,23 +1485,10 @@ void JitCodeCache::DoneCompilerUse(ArtMethod* method, Thread* self) { info->DecrementInlineUse(); } -void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self, bool osr) { - DCHECK_EQ(Thread::Current(), self); - MutexLock mu(self, lock_); - if (UNLIKELY(method->IsNative())) { - auto it = jni_stubs_map_.find(JniStubKey(method)); - DCHECK(it != jni_stubs_map_.end()); - JniStubData* data = &it->second; - DCHECK(ContainsElement(data->GetMethods(), method)); - if (UNLIKELY(!data->IsCompiled())) { - // Failed to compile; the JNI compiler never fails, but the cache may be full. - jni_stubs_map_.erase(it); // Remove the entry added in NotifyCompilationOf(). - } // else CommitCodeInternal() updated entrypoints of all methods in the JniStubData. - } else { - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - DCHECK(info->IsMethodBeingCompiled(osr)); - info->SetIsMethodBeingCompiled(false, osr); - } +void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self ATTRIBUTE_UNUSED, bool osr) { + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + DCHECK(info->IsMethodBeingCompiled(osr)); + info->SetIsMethodBeingCompiled(false, osr); } size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) { @@ -1765,7 +1498,6 @@ size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) { void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method, const OatQuickMethodHeader* header) { - DCHECK(!method->IsNative()); ProfilingInfo* profiling_info = method->GetProfilingInfo(kRuntimePointerSize); if ((profiling_info != nullptr) && (profiling_info->GetSavedEntryPoint() == header->GetEntryPoint())) { @@ -1821,7 +1553,6 @@ void JitCodeCache::Dump(std::ostream& os) { os << "Current JIT code cache size: " << PrettySize(used_memory_for_code_) << "\n" << "Current JIT data cache size: " << PrettySize(used_memory_for_data_) << "\n" << "Current JIT capacity: " << PrettySize(current_capacity_) << "\n" - << "Current number of JIT JNI stub entries: " << jni_stubs_map_.size() << "\n" << "Current number of JIT code cache entries: " << method_code_map_.size() << "\n" << "Total number of JIT compilations: " << number_of_compilations_ << "\n" << "Total number of JIT compilations for on stack replacement: " diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index fc011ddb96..46a408590b 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -35,23 +35,9 @@ template class Handle; class LinearAlloc; class InlineCache; class IsMarkedVisitor; -class JitJniStubTestHelper; class OatQuickMethodHeader; struct ProfileMethodInfo; class ProfilingInfo; -class Thread; - -namespace gc { -namespace accounting { -template class MemoryRangeBitmap; -} // namespace accounting -} // namespace gc - -namespace mirror { -class Class; -class Object; -template class ObjectArray; -} // namespace mirror namespace gc { namespace accounting { @@ -151,9 +137,6 @@ class JitCodeCache { // Return true if the code cache contains this method. bool ContainsMethod(ArtMethod* method) REQUIRES(!lock_); - // Return the code pointer for a JNI-compiled stub if the method is in the cache, null otherwise. - const void* GetJniStubCode(ArtMethod* method) REQUIRES(!lock_); - // Allocate a region of data that contain `size` bytes, and potentially space // for storing `number_of_roots` roots. Returns null if there is no more room. // Return the number of bytes allocated. @@ -177,6 +160,11 @@ class JitCodeCache { return live_bitmap_.get(); } + // Return whether we should do a full collection given the current state of the cache. + bool ShouldDoFullCollection() + REQUIRES(lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + // Perform a collection on the code cache. void GarbageCollectCache(Thread* self) REQUIRES(!lock_) @@ -308,12 +296,6 @@ class JitCodeCache { REQUIRES(!lock_) REQUIRES(!Locks::cha_lock_); - // Removes method from the cache. The caller must ensure that all threads - // are suspended and the method should not be in any thread's stack. - bool RemoveMethodLocked(ArtMethod* method, bool release_memory) - REQUIRES(lock_) - REQUIRES(Locks::mutator_lock_); - // Free in the mspace allocations for `code_ptr`. void FreeCode(const void* code_ptr) REQUIRES(lock_); @@ -333,11 +315,6 @@ class JitCodeCache { // Set the footprint limit of the code cache. void SetFootprintLimit(size_t new_footprint) REQUIRES(lock_); - // Return whether we should do a full collection given the current state of the cache. - bool ShouldDoFullCollection() - REQUIRES(lock_) - REQUIRES_SHARED(Locks::mutator_lock_); - void DoCollection(Thread* self, bool collect_profiling_info) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -364,9 +341,6 @@ class JitCodeCache { REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); - class JniStubKey; - class JniStubData; - // Lock for guarding allocations, collections, and the method_code_map_. Mutex lock_; // Condition to wait on during collection. @@ -383,8 +357,6 @@ class JitCodeCache { void* data_mspace_ GUARDED_BY(lock_); // Bitmap for collecting code and data. std::unique_ptr live_bitmap_; - // Holds compiled code associated with the shorty for a JNI stub. - SafeMap jni_stubs_map_ GUARDED_BY(lock_); // Holds compiled code associated to the ArtMethod. SafeMap method_code_map_ GUARDED_BY(lock_); // Holds osr compiled code associated to the ArtMethod. @@ -446,7 +418,6 @@ class JitCodeCache { // Condition to wait on for accessing inline caches. ConditionVariable inline_cache_cond_ GUARDED_BY(lock_); - friend class art::JitJniStubTestHelper; DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache); }; diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index acbc6e63a4..01853de403 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -357,8 +357,8 @@ static void SampleClassesAndExecutedMethods(pthread_t profiler_pthread, sampled_methods->AddReference(method.GetDexFile(), method.GetDexMethodIndex()); } } else { - // We do not record native methods. Once we AOT-compile the app, all native - // methods shall have their thunks compiled. + CHECK_EQ(method.GetCounter(), 0u) << method.PrettyMethod() + << " access_flags=" << method.GetAccessFlags(); } } } diff --git a/runtime/managed_stack-inl.h b/runtime/managed_stack-inl.h index 678be8e098..689dd8009a 100644 --- a/runtime/managed_stack-inl.h +++ b/runtime/managed_stack-inl.h @@ -24,7 +24,7 @@ namespace art { inline ShadowFrame* ManagedStack::PushShadowFrame(ShadowFrame* new_top_frame) { - DCHECK(!HasTopQuickFrame()); + DCHECK(top_quick_frame_ == nullptr); ShadowFrame* old_frame = top_shadow_frame_; top_shadow_frame_ = new_top_frame; new_top_frame->SetLink(old_frame); @@ -32,7 +32,7 @@ inline ShadowFrame* ManagedStack::PushShadowFrame(ShadowFrame* new_top_frame) { } inline ShadowFrame* ManagedStack::PopShadowFrame() { - DCHECK(!HasTopQuickFrame()); + DCHECK(top_quick_frame_ == nullptr); CHECK(top_shadow_frame_ != nullptr); ShadowFrame* frame = top_shadow_frame_; top_shadow_frame_ = frame->GetLink(); diff --git a/runtime/managed_stack.h b/runtime/managed_stack.h index 07078ecb13..4f1984d55a 100644 --- a/runtime/managed_stack.h +++ b/runtime/managed_stack.h @@ -24,7 +24,6 @@ #include "base/logging.h" #include "base/macros.h" #include "base/mutex.h" -#include "base/bit_utils.h" namespace art { @@ -43,9 +42,7 @@ template class StackReference; class PACKED(4) ManagedStack { public: ManagedStack() - : tagged_top_quick_frame_(TaggedTopQuickFrame::CreateNotTagged(nullptr)), - link_(nullptr), - top_shadow_frame_(nullptr) {} + : top_quick_frame_(nullptr), link_(nullptr), top_shadow_frame_(nullptr) {} void PushManagedStackFragment(ManagedStack* fragment) { // Copy this top fragment into given fragment. @@ -66,36 +63,17 @@ class PACKED(4) ManagedStack { return link_; } - ArtMethod** GetTopQuickFrameKnownNotTagged() const { - return tagged_top_quick_frame_.GetSpKnownNotTagged(); - } - ArtMethod** GetTopQuickFrame() const { - return tagged_top_quick_frame_.GetSp(); - } - - bool GetTopQuickFrameTag() const { - return tagged_top_quick_frame_.GetTag(); - } - - bool HasTopQuickFrame() const { - return tagged_top_quick_frame_.GetTaggedSp() != 0u; + return top_quick_frame_; } void SetTopQuickFrame(ArtMethod** top) { DCHECK(top_shadow_frame_ == nullptr); - DCHECK_ALIGNED(top, 4u); - tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateNotTagged(top); + top_quick_frame_ = top; } - void SetTopQuickFrameTagged(ArtMethod** top) { - DCHECK(top_shadow_frame_ == nullptr); - DCHECK_ALIGNED(top, 4u); - tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateTagged(top); - } - - static size_t TaggedTopQuickFrameOffset() { - return OFFSETOF_MEMBER(ManagedStack, tagged_top_quick_frame_); + static size_t TopQuickFrameOffset() { + return OFFSETOF_MEMBER(ManagedStack, top_quick_frame_); } ALWAYS_INLINE ShadowFrame* PushShadowFrame(ShadowFrame* new_top_frame); @@ -105,12 +83,8 @@ class PACKED(4) ManagedStack { return top_shadow_frame_; } - bool HasTopShadowFrame() const { - return GetTopShadowFrame() != nullptr; - } - void SetTopShadowFrame(ShadowFrame* top) { - DCHECK_EQ(tagged_top_quick_frame_.GetTaggedSp(), 0u); + DCHECK(top_quick_frame_ == nullptr); top_shadow_frame_ = top; } @@ -123,47 +97,7 @@ class PACKED(4) ManagedStack { bool ShadowFramesContain(StackReference* shadow_frame_entry) const; private: - // Encodes the top quick frame (which must be at least 4-byte aligned) - // and a flag that marks the GenericJNI trampoline. - class TaggedTopQuickFrame { - public: - static TaggedTopQuickFrame CreateNotTagged(ArtMethod** sp) { - DCHECK_ALIGNED(sp, 4u); - return TaggedTopQuickFrame(reinterpret_cast(sp)); - } - - static TaggedTopQuickFrame CreateTagged(ArtMethod** sp) { - DCHECK_ALIGNED(sp, 4u); - return TaggedTopQuickFrame(reinterpret_cast(sp) | 1u); - } - - // Get SP known to be not tagged and non-null. - ArtMethod** GetSpKnownNotTagged() const { - DCHECK(!GetTag()); - DCHECK_NE(tagged_sp_, 0u); - return reinterpret_cast(tagged_sp_); - } - - ArtMethod** GetSp() const { - return reinterpret_cast(tagged_sp_ & ~static_cast(1u)); - } - - bool GetTag() const { - return (tagged_sp_ & 1u) != 0u; - } - - uintptr_t GetTaggedSp() const { - return tagged_sp_; - } - - private: - explicit TaggedTopQuickFrame(uintptr_t tagged_sp) : tagged_sp_(tagged_sp) { } - - uintptr_t tagged_sp_; - }; - static_assert(sizeof(TaggedTopQuickFrame) == sizeof(uintptr_t), "TaggedTopQuickFrame size check"); - - TaggedTopQuickFrame tagged_top_quick_frame_; + ArtMethod** top_quick_frame_; ManagedStack* link_; ShadowFrame* top_shadow_frame_; }; diff --git a/runtime/stack.cc b/runtime/stack.cc index 5ad1f7c9c5..ab9fb0d73f 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -735,19 +735,12 @@ QuickMethodFrameInfo StackVisitor::GetCurrentQuickFrameInfo() const { return runtime->GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); } - // The only remaining case is if the method is native and uses the generic JNI stub, - // called either directly or through some (resolution, instrumentation) trampoline. + // The only remaining case is if the method is native and uses the generic JNI stub. DCHECK(method->IsNative()); - if (kIsDebugBuild) { - ClassLinker* class_linker = runtime->GetClassLinker(); - const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(method, - kRuntimePointerSize); - CHECK(class_linker->IsQuickGenericJniStub(entry_point) || - // The current entrypoint (after filtering out trampolines) may have changed - // from GenericJNI to JIT-compiled stub since we have entered this frame. - (runtime->GetJit() != nullptr && - runtime->GetJit()->GetCodeCache()->ContainsPc(entry_point))) << method->PrettyMethod(); - } + ClassLinker* class_linker = runtime->GetClassLinker(); + const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(method, + kRuntimePointerSize); + DCHECK(class_linker->IsQuickGenericJniStub(entry_point)) << method->PrettyMethod(); // Generic JNI frame. uint32_t handle_refs = GetNumberOfReferenceArgsWithoutReceiver(method) + 1; size_t scope_size = HandleScope::SizeOf(handle_refs); @@ -783,48 +776,8 @@ void StackVisitor::WalkStack(bool include_transitions) { // Can't be both a shadow and a quick fragment. DCHECK(current_fragment->GetTopShadowFrame() == nullptr); ArtMethod* method = *cur_quick_frame_; - DCHECK(method != nullptr); - bool header_retrieved = false; - if (method->IsNative()) { - // We do not have a PC for the first frame, so we cannot simply use - // ArtMethod::GetOatQuickMethodHeader() as we're unable to distinguish there - // between GenericJNI frame and JIT-compiled JNI stub; the entrypoint may have - // changed since the frame was entered. The top quick frame tag indicates - // GenericJNI here, otherwise it's either AOT-compiled or JNI-compiled JNI stub. - if (UNLIKELY(current_fragment->GetTopQuickFrameTag())) { - // The generic JNI does not have any method header. - cur_oat_quick_method_header_ = nullptr; - } else { - const void* existing_entry_point = method->GetEntryPointFromQuickCompiledCode(); - CHECK(existing_entry_point != nullptr); - Runtime* runtime = Runtime::Current(); - ClassLinker* class_linker = runtime->GetClassLinker(); - // Check whether we can quickly get the header from the current entrypoint. - if (!class_linker->IsQuickGenericJniStub(existing_entry_point) && - !class_linker->IsQuickResolutionStub(existing_entry_point) && - existing_entry_point != GetQuickInstrumentationEntryPoint()) { - cur_oat_quick_method_header_ = - OatQuickMethodHeader::FromEntryPoint(existing_entry_point); - } else { - const void* code = method->GetOatMethodQuickCode(class_linker->GetImagePointerSize()); - if (code != nullptr) { - cur_oat_quick_method_header_ = OatQuickMethodHeader::FromEntryPoint(code); - } else { - // This must be a JITted JNI stub frame. - CHECK(runtime->GetJit() != nullptr); - code = runtime->GetJit()->GetCodeCache()->GetJniStubCode(method); - CHECK(code != nullptr) << method->PrettyMethod(); - cur_oat_quick_method_header_ = OatQuickMethodHeader::FromCodePointer(code); - } - } - } - header_retrieved = true; - } while (method != nullptr) { - if (!header_retrieved) { - cur_oat_quick_method_header_ = method->GetOatQuickMethodHeader(cur_quick_frame_pc_); - } - header_retrieved = false; // Force header retrieval in next iteration. + cur_oat_quick_method_header_ = method->GetOatQuickMethodHeader(cur_quick_frame_pc_); SanityCheckFrame(); if ((walk_kind_ == StackWalkKind::kIncludeInlinedFrames) diff --git a/runtime/stack.h b/runtime/stack.h index a16930bba0..bd6204f8d2 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -140,7 +140,8 @@ class StackVisitor { }; template - void WalkStack(bool include_transitions = false) REQUIRES_SHARED(Locks::mutator_lock_); + void WalkStack(bool include_transitions = false) + REQUIRES_SHARED(Locks::mutator_lock_); Thread* GetThread() const { return thread_; diff --git a/runtime/thread.cc b/runtime/thread.cc index bec1c908ad..712eabc888 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1884,7 +1884,9 @@ static bool ShouldShowNativeStack(const Thread* thread) } // Threads with no managed stack frames should be shown. - if (!thread->HasManagedStack()) { + const ManagedStack* managed_stack = thread->GetManagedStack(); + if (managed_stack == nullptr || (managed_stack->GetTopQuickFrame() == nullptr && + managed_stack->GetTopShadowFrame() == nullptr)) { return true; } diff --git a/runtime/thread.h b/runtime/thread.h index 0803975d26..39be66d5c2 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -474,16 +474,13 @@ class Thread { tlsPtr_.managed_stack.SetTopQuickFrame(top_method); } - void SetTopOfStackTagged(ArtMethod** top_method) { - tlsPtr_.managed_stack.SetTopQuickFrameTagged(top_method); - } - void SetTopOfShadowStack(ShadowFrame* top) { tlsPtr_.managed_stack.SetTopShadowFrame(top); } bool HasManagedStack() const { - return tlsPtr_.managed_stack.HasTopQuickFrame() || tlsPtr_.managed_stack.HasTopShadowFrame(); + return (tlsPtr_.managed_stack.GetTopQuickFrame() != nullptr) || + (tlsPtr_.managed_stack.GetTopShadowFrame() != nullptr); } // If 'msg' is null, no detail message is set. @@ -836,7 +833,7 @@ class Thread { static ThreadOffset TopOfManagedStackOffset() { return ThreadOffsetFromTlsPtr( OFFSETOF_MEMBER(tls_ptr_sized_values, managed_stack) + - ManagedStack::TaggedTopQuickFrameOffset()); + ManagedStack::TopQuickFrameOffset()); } const ManagedStack* GetManagedStack() const { diff --git a/test/655-jit-clinit/src/Main.java b/test/655-jit-clinit/src/Main.java index 2fb8f2a86e..44b315478f 100644 --- a/test/655-jit-clinit/src/Main.java +++ b/test/655-jit-clinit/src/Main.java @@ -23,7 +23,7 @@ public class Main { Foo.hotMethod(); } - public native static boolean hasJitCompiledEntrypoint(Class cls, String methodName); + public native static boolean isJitCompiled(Class cls, String methodName); private native static boolean hasJit(); } @@ -36,7 +36,7 @@ class Foo { static { array = new Object[10000]; - while (!Main.hasJitCompiledEntrypoint(Foo.class, "hotMethod")) { + while (!Main.isJitCompiled(Foo.class, "hotMethod")) { Foo.hotMethod(); try { // Sleep to give a chance for the JIT to compile `hotMethod`. diff --git a/test/667-jit-jni-stub/expected.txt b/test/667-jit-jni-stub/expected.txt deleted file mode 100644 index 6a5618ebc6..0000000000 --- a/test/667-jit-jni-stub/expected.txt +++ /dev/null @@ -1 +0,0 @@ -JNI_OnLoad called diff --git a/test/667-jit-jni-stub/info.txt b/test/667-jit-jni-stub/info.txt deleted file mode 100644 index 6f25c44592..0000000000 --- a/test/667-jit-jni-stub/info.txt +++ /dev/null @@ -1 +0,0 @@ -Tests for JITting and collecting JNI stubs. diff --git a/test/667-jit-jni-stub/jit_jni_stub_test.cc b/test/667-jit-jni-stub/jit_jni_stub_test.cc deleted file mode 100644 index 82e06fc018..0000000000 --- a/test/667-jit-jni-stub/jit_jni_stub_test.cc +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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. - */ - -#include - -#include "jit/jit.h" -#include "jit/jit_code_cache.h" -#include "mirror/class.h" -#include "mirror/string.h" -#include "runtime.h" -#include "scoped_thread_state_change-inl.h" - -namespace art { - -// Local class declared as a friend of JitCodeCache so that we can access its internals. -class JitJniStubTestHelper { - public: - static bool isNextJitGcFull(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - CHECK(Runtime::Current()->GetJit() != nullptr); - jit::JitCodeCache* cache = Runtime::Current()->GetJit()->GetCodeCache(); - MutexLock mu(self, cache->lock_); - return cache->ShouldDoFullCollection(); - } -}; - -// Calls through to a static method with signature "()V". -extern "C" JNIEXPORT -void Java_Main_callThrough(JNIEnv* env, jclass, jclass klass, jstring methodName) { - ScopedObjectAccess soa(Thread::Current()); - std::string name = soa.Decode(methodName)->ToModifiedUtf8(); - jmethodID method = env->GetStaticMethodID(klass, name.c_str(), "()V"); - CHECK(method != nullptr) << soa.Decode(klass)->PrettyDescriptor() << "." << name; - env->CallStaticVoidMethod(klass, method); -} - -extern "C" JNIEXPORT -void Java_Main_jitGc(JNIEnv*, jclass) { - CHECK(Runtime::Current()->GetJit() != nullptr); - jit::JitCodeCache* cache = Runtime::Current()->GetJit()->GetCodeCache(); - ScopedObjectAccess soa(Thread::Current()); - cache->GarbageCollectCache(Thread::Current()); -} - -extern "C" JNIEXPORT -jboolean Java_Main_isNextJitGcFull(JNIEnv*, jclass) { - ScopedObjectAccess soa(Thread::Current()); - return JitJniStubTestHelper::isNextJitGcFull(soa.Self()); -} - -} // namespace art diff --git a/test/667-jit-jni-stub/run b/test/667-jit-jni-stub/run deleted file mode 100755 index 1877be482e..0000000000 --- a/test/667-jit-jni-stub/run +++ /dev/null @@ -1,18 +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. - -# Disable AOT compilation of JNI stubs. -${RUN} "${@}" --no-prebuild --no-dex2oat diff --git a/test/667-jit-jni-stub/src/Main.java b/test/667-jit-jni-stub/src/Main.java deleted file mode 100644 index b867970eab..0000000000 --- a/test/667-jit-jni-stub/src/Main.java +++ /dev/null @@ -1,180 +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. - */ - -public class Main { - public static void main(String[] args) throws Exception { - System.loadLibrary(args[0]); - if (isAotCompiled(Main.class, "hasJit")) { - throw new Error("This test must be run with --no-prebuild --no-dex2oat!"); - } - if (!hasJit()) { - return; - } - - testCompilationUseAndCollection(); - testMixedFramesOnStack(); - } - - public static void testCompilationUseAndCollection() { - // Test that callThrough() can be JIT-compiled. - assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); - assertFalse(hasJitCompiledCode(Main.class, "callThrough")); - ensureCompiledCallThroughEntrypoint(/* call */ true); - assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); - assertTrue(hasJitCompiledCode(Main.class, "callThrough")); - - // Use callThrough() once again now that the method has a JIT-compiled stub. - callThrough(Main.class, "doNothing"); - - // Test that GC with the JIT-compiled stub on the stack does not collect it. - // Also tests stack walk over the JIT-compiled stub. - callThrough(Main.class, "testGcWithCallThroughStubOnStack"); - - // Test that, when marking used methods before a full JIT GC, a single execution - // of the GenericJNI trampoline can save the compiled stub from being collected. - testSingleInvocationTriggersRecompilation(); - - // Test that the JNI compiled stub can actually be collected. - testStubCanBeCollected(); - } - - public static void testGcWithCallThroughStubOnStack() { - // Check that this method was called via JIT-compiled callThrough() stub. - assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); - // This assertion also exercises stack walk over the JIT-compiled callThrough() stub. - assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); - - doJitGcsUntilFullJitGcIsScheduled(); - // The callThrough() on the stack above this method is using the compiled stub, - // so the JIT GC should not remove the compiled code. - jitGc(); - assertTrue(hasJitCompiledCode(Main.class, "callThrough")); - } - - public static void testSingleInvocationTriggersRecompilation() { - // After scheduling a full JIT GC, single call through the GenericJNI - // trampoline should ensure that the compiled stub is used again. - doJitGcsUntilFullJitGcIsScheduled(); - callThrough(Main.class, "doNothing"); - ensureCompiledCallThroughEntrypoint(/* call */ false); // Wait for the compilation task to run. - assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); - jitGc(); // This JIT GC should not collect the callThrough() stub. - assertTrue(hasJitCompiledCode(Main.class, "callThrough")); - } - - public static void testMixedFramesOnStack() { - // Starts without a compiled JNI stub for callThrough(). - assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); - assertFalse(hasJitCompiledCode(Main.class, "callThrough")); - callThrough(Main.class, "testMixedFramesOnStackStage2"); - // We have just returned through the JIT-compiled JNI stub, so it must still - // be compiled (though not necessarily with the entrypoint pointing to it). - assertTrue(hasJitCompiledCode(Main.class, "callThrough")); - // Though the callThrough() is on the stack, that frame is using the GenericJNI - // and does not prevent the collection of the JNI stub. - testStubCanBeCollected(); - } - - public static void testMixedFramesOnStackStage2() { - // We cannot assert that callThrough() has no JIT compiled stub as that check - // may race against the compilation task. Just check the caller. - assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); - // Now ensure that the JNI stub is compiled and used. - ensureCompiledCallThroughEntrypoint(/* call */ true); - callThrough(Main.class, "testMixedFramesOnStackStage3"); - } - - public static void testMixedFramesOnStackStage3() { - // Check that this method was called via JIT-compiled callThrough() stub. - assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); - // This assertion also exercises stack walk over the JIT-compiled callThrough() stub. - assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); - // For a good measure, try a JIT GC. - jitGc(); - } - - public static void testStubCanBeCollected() { - assertTrue(hasJitCompiledCode(Main.class, "callThrough")); - doJitGcsUntilFullJitGcIsScheduled(); - assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); - assertTrue(hasJitCompiledCode(Main.class, "callThrough")); - jitGc(); // JIT GC without callThrough() on the stack should collect the callThrough() stub. - assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); - assertFalse(hasJitCompiledCode(Main.class, "callThrough")); - } - - public static void doJitGcsUntilFullJitGcIsScheduled() { - // We enter with a compiled stub for callThrough() but we also need the entrypoint to be set. - assertTrue(hasJitCompiledCode(Main.class, "callThrough")); - ensureCompiledCallThroughEntrypoint(/* call */ true); - // Perform JIT GC until the next GC is marked to do full collection. - do { - assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); - callThrough(Main.class, "jitGc"); // JIT GC with callThrough() safely on the stack. - } while (!isNextJitGcFull()); - // The JIT GC before the full collection resets entrypoints and waits to see - // if the methods are still in use. - assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); - assertTrue(hasJitCompiledCode(Main.class, "callThrough")); - } - - public static void ensureCompiledCallThroughEntrypoint(boolean call) { - int count = 0; - while (!hasJitCompiledEntrypoint(Main.class, "callThrough")) { - // If `call` is true, also exercise the `callThrough()` method to increase hotness. - int limit = call ? 1 << Math.min(count, 12) : 0; - for (int i = 0; i < limit; ++i) { - callThrough(Main.class, "doNothing"); - } - try { - // Sleep to give a chance for the JIT to compile `hasJit` stub. - Thread.sleep(100); - } catch (Exception e) { - // Ignore - } - if (++count == 50) { - throw new Error("TIMEOUT"); - } - }; - } - - public static void assertTrue(boolean value) { - if (!value) { - throw new AssertionError("Expected true!"); - } - } - - public static void assertFalse(boolean value) { - if (value) { - throw new AssertionError("Expected false!"); - } - } - - public static void doNothing() { } - public static void throwError() { throw new Error(); } - - // Note that the callThrough()'s shorty differs from shorties of the other - // native methods used in this test because of the return type `void.` - public native static void callThrough(Class cls, String methodName); - - public native static void jitGc(); - public native static boolean isNextJitGcFull(); - - public native static boolean isAotCompiled(Class cls, String methodName); - public native static boolean hasJitCompiledEntrypoint(Class cls, String methodName); - public native static boolean hasJitCompiledCode(Class cls, String methodName); - private native static boolean hasJit(); -} diff --git a/test/Android.bp b/test/Android.bp index 2d526d256c..8f29251907 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -384,7 +384,6 @@ cc_defaults { "656-annotation-lookup-generic-jni/test.cc", "661-oat-writer-layout/oat_writer_layout.cc", "664-aget-verifier/aget-verifier.cc", - "667-jit-jni-stub/jit_jni_stub_test.cc", "708-jit-cache-churn/jit.cc", ], shared_libs: [ diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index 34580800cc..df497c1181 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -152,10 +152,10 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isAotCompiled(JNIEnv* env, return method->GetOatMethodQuickCode(kRuntimePointerSize) != nullptr; } -extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledEntrypoint(JNIEnv* env, - jclass, - jclass cls, - jstring method_name) { +extern "C" JNIEXPORT jboolean JNICALL Java_Main_isJitCompiled(JNIEnv* env, + jclass, + jclass cls, + jstring method_name) { jit::Jit* jit = GetJitIfEnabled(); if (jit == nullptr) { return false; @@ -169,23 +169,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledEntrypoint(JNIEnv* return jit->GetCodeCache()->ContainsPc(method->GetEntryPointFromQuickCompiledCode()); } -extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledCode(JNIEnv* env, - jclass, - jclass cls, - jstring method_name) { - jit::Jit* jit = GetJitIfEnabled(); - if (jit == nullptr) { - return false; - } - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); - ScopedUtfChars chars(env, method_name); - CHECK(chars.c_str() != nullptr); - ArtMethod* method = soa.Decode(cls)->FindDeclaredDirectMethodByName( - chars.c_str(), kRuntimePointerSize); - return jit->GetCodeCache()->ContainsMethod(method); -} - extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env, jclass, jclass cls, -- GitLab From 32ca7786f98d5e3adb73edd8dff220b40413700f Mon Sep 17 00:00:00 2001 From: Richard Uhler Date: Thu, 30 Nov 2017 09:35:52 +0000 Subject: [PATCH 098/226] ahat: fix bug parsing ids with the high bit set. With this fix, ahat will no longer generate broken links for objects with ids greater than 0x7FFFFFFF. Bug: 69706820 Test: m ahat-test Test: Manually heap dump from b/69706820 and verify ids parsed correctly. Test: Automated test planned for followup as part of b/69706820. Change-Id: Ie49f49f8959953cff54a543eb7a25bed806b7f1c --- tools/ahat/src/main/com/android/ahat/heapdump/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java b/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java index d7b1dd78d6..3bed29bafc 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java @@ -853,7 +853,7 @@ public class Parser { } public long getId() { - return mBuffer.getInt(); + return mBuffer.getInt() & 0xFFFFFFFFL; } public boolean getBool() { -- GitLab From 2196c651ecc77e49992c6c329dfce45f78ff46cb Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 30 Nov 2017 16:16:07 +0000 Subject: [PATCH 099/226] Revert^4 "JIT JNI stubs." The original CL, https://android-review.googlesource.com/513417 , has a bug fixed in the Revert^2, https://android-review.googlesource.com/550579 , and this Revert^4 adds two more fixes: - fix obsolete native method getting interpreter entrypoint in 980-redefine-object, - fix random JIT GC flakiness in 667-jit-jni-stub. Test: testrunner.py --host --prebuild --no-relocate \ --no-image --jit -t 980-redefine-object Bug: 65574695 Bug: 69843562 This reverts commit 056d7756152bb3ced81dd57781be5028428ce2bd. Change-Id: Ic778686168b90e29816fd526e23141dcbe5ea880 --- compiler/optimizing/optimizing_compiler.cc | 64 ++- runtime/arch/arm/quick_entrypoints_arm.S | 4 +- runtime/arch/arm64/quick_entrypoints_arm64.S | 2 +- runtime/arch/mips/quick_entrypoints_mips.S | 3 +- .../arch/mips64/quick_entrypoints_mips64.S | 3 +- runtime/arch/x86/quick_entrypoints_x86.S | 4 +- .../arch/x86_64/quick_entrypoints_x86_64.S | 4 +- runtime/art_method-inl.h | 17 +- runtime/art_method.cc | 38 +- runtime/art_method.h | 15 +- runtime/entrypoints/entrypoint_utils.cc | 4 +- .../quick/quick_trampoline_entrypoints.cc | 9 +- runtime/jit/jit.cc | 6 +- runtime/jit/jit_code_cache.cc | 493 ++++++++++++++---- runtime/jit/jit_code_cache.h | 39 +- runtime/jit/profile_saver.cc | 4 +- runtime/managed_stack-inl.h | 4 +- runtime/managed_stack.h | 80 ++- runtime/stack.cc | 59 ++- runtime/stack.h | 3 +- runtime/thread.cc | 4 +- runtime/thread.h | 9 +- test/655-jit-clinit/src/Main.java | 4 +- test/667-jit-jni-stub/expected.txt | 1 + test/667-jit-jni-stub/info.txt | 1 + test/667-jit-jni-stub/jit_jni_stub_test.cc | 63 +++ test/667-jit-jni-stub/run | 19 + test/667-jit-jni-stub/src/Main.java | 180 +++++++ test/Android.bp | 1 + test/common/runtime_state.cc | 25 +- 30 files changed, 968 insertions(+), 194 deletions(-) create mode 100644 test/667-jit-jni-stub/expected.txt create mode 100644 test/667-jit-jni-stub/info.txt create mode 100644 test/667-jit-jni-stub/jit_jni_stub_test.cc create mode 100755 test/667-jit-jni-stub/run create mode 100644 test/667-jit-jni-stub/src/Main.java diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index a281c4a310..73c72fc57a 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -1196,7 +1196,69 @@ bool OptimizingCompiler::JitCompile(Thread* self, Runtime* runtime = Runtime::Current(); ArenaAllocator allocator(runtime->GetJitArenaPool()); - ArenaStack arena_stack(Runtime::Current()->GetJitArenaPool()); + + if (UNLIKELY(method->IsNative())) { + JniCompiledMethod jni_compiled_method = ArtQuickJniCompileMethod( + GetCompilerDriver(), access_flags, method_idx, *dex_file); + ScopedNullHandle> roots; + ArenaSet> cha_single_implementation_list( + allocator.Adapter(kArenaAllocCHA)); + const void* code = code_cache->CommitCode( + self, + method, + /* stack_map_data */ nullptr, + /* method_info_data */ nullptr, + /* roots_data */ nullptr, + jni_compiled_method.GetFrameSize(), + jni_compiled_method.GetCoreSpillMask(), + jni_compiled_method.GetFpSpillMask(), + jni_compiled_method.GetCode().data(), + jni_compiled_method.GetCode().size(), + /* data_size */ 0u, + osr, + roots, + /* has_should_deoptimize_flag */ false, + cha_single_implementation_list); + if (code == nullptr) { + return false; + } + + const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); + if (compiler_options.GetGenerateDebugInfo()) { + const auto* method_header = reinterpret_cast(code); + const uintptr_t code_address = reinterpret_cast(method_header->GetCode()); + debug::MethodDebugInfo info = {}; + DCHECK(info.trampoline_name.empty()); + info.dex_file = dex_file; + info.class_def_index = class_def_idx; + info.dex_method_index = method_idx; + info.access_flags = access_flags; + info.code_item = code_item; + info.isa = jni_compiled_method.GetInstructionSet(); + info.deduped = false; + info.is_native_debuggable = compiler_options.GetNativeDebuggable(); + info.is_optimized = true; + info.is_code_address_text_relative = false; + info.code_address = code_address; + info.code_size = jni_compiled_method.GetCode().size(); + info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); + info.code_info = nullptr; + info.cfi = jni_compiled_method.GetCfi(); + std::vector elf_file = debug::WriteDebugElfFileForMethods( + GetCompilerDriver()->GetInstructionSet(), + GetCompilerDriver()->GetInstructionSetFeatures(), + ArrayRef(&info, 1)); + CreateJITCodeEntryForAddress(code_address, std::move(elf_file)); + } + + Runtime::Current()->GetJit()->AddMemoryUsage(method, allocator.BytesUsed()); + if (jit_logger != nullptr) { + jit_logger->WriteLog(code, jni_compiled_method.GetCode().size(), method); + } + return true; + } + + ArenaStack arena_stack(runtime->GetJitArenaPool()); CodeVectorAllocator code_allocator(&allocator); VariableSizedHandleScope handles(self); diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 6ff8dd60b8..6ec9c48b92 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -1783,7 +1783,9 @@ 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 sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] + ldr ip, [r9, #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 # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 280e5937c6..47efeb9200 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -2299,7 +2299,7 @@ ENTRY art_quick_generic_jni_trampoline .Lexception_in_native: // Move to x1 then sp to please assembler. ldr x1, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET] - mov sp, x1 + add sp, x1, #-1 // Remove the GenericJNI tag. .cfi_def_cfa_register sp # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 489c52c0d2..fc77a641b3 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -2283,7 +2283,8 @@ ENTRY art_quick_generic_jni_trampoline nop 2: - lw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) + lw $t0, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) + addiu $sp, $t0, -1 // Remove the GenericJNI tag. move $gp, $s3 # restore $gp from $s3 # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 98ffe6504a..3fb83d9232 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -2158,7 +2158,8 @@ ENTRY art_quick_generic_jni_trampoline dmtc1 $v0, $f0 # place return value to FP return value 1: - ld $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) + ld $t0, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) + daddiu $sp, $t0, -1 // Remove the GenericJNI tag. # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION END art_quick_generic_jni_trampoline diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 25716dc1bb..a46ceeba12 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1969,7 +1969,9 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline punpckldq %xmm1, %xmm0 ret .Lexception_in_native: - movl %fs:THREAD_TOP_QUICK_FRAME_OFFSET, %esp + pushl %fs:THREAD_TOP_QUICK_FRAME_OFFSET + addl LITERAL(-1), (%esp) // Remove the GenericJNI tag. + movl (%esp), %esp // Do a call to push a new save-all frame required by the runtime. call .Lexception_call .Lexception_call: diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 2c3da90f25..463e5a279f 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -1958,7 +1958,9 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline movq %rax, %xmm0 ret .Lexception_in_native: - movq %gs:THREAD_TOP_QUICK_FRAME_OFFSET, %rsp + pushq %gs:THREAD_TOP_QUICK_FRAME_OFFSET + addq LITERAL(-1), (%rsp) // Remove the GenericJNI tag. + movq (%rsp), %rsp CFI_DEF_CFA_REGISTER(rsp) // Do a call to push a new save-all frame required by the runtime. call .Lexception_call diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 50913def93..31abf94889 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -95,10 +95,12 @@ inline uint16_t ArtMethod::GetMethodIndexDuringLinking() { return method_index_; } +template inline uint32_t ArtMethod::GetDexMethodIndex() { if (kCheckDeclaringClassState) { - CHECK(IsRuntimeMethod() || GetDeclaringClass()->IsIdxLoaded() || - GetDeclaringClass()->IsErroneous()); + CHECK(IsRuntimeMethod() || + GetDeclaringClass()->IsIdxLoaded() || + GetDeclaringClass()->IsErroneous()); } return GetDexMethodIndexUnchecked(); } @@ -202,7 +204,14 @@ inline const char* ArtMethod::GetShorty() { inline const char* ArtMethod::GetShorty(uint32_t* out_length) { DCHECK(!IsProxyMethod()); const DexFile* dex_file = GetDexFile(); - return dex_file->GetMethodShorty(dex_file->GetMethodId(GetDexMethodIndex()), out_length); + // Don't do a read barrier in the DCHECK() inside GetDexMethodIndex() as GetShorty() + // can be called when the declaring class is about to be unloaded and cannot be added + // to the mark stack (subsequent GC assertion would fail). + // It is safe to avoid the read barrier as the ArtMethod is constructed with a declaring + // Class already satisfying the DCHECK() inside GetDexMethodIndex(), so even if that copy + // of declaring class becomes a from-space object, it shall satisfy the DCHECK(). + return dex_file->GetMethodShorty(dex_file->GetMethodId(GetDexMethodIndex()), + out_length); } inline const Signature ArtMethod::GetSignature() { @@ -319,7 +328,7 @@ inline mirror::ClassLoader* ArtMethod::GetClassLoader() { template inline mirror::DexCache* ArtMethod::GetDexCache() { - if (LIKELY(!IsObsolete())) { + if (LIKELY(!IsObsolete())) { mirror::Class* klass = GetDeclaringClass(); return klass->GetDexCache(); } else { diff --git a/runtime/art_method.cc b/runtime/art_method.cc index fa0c501e31..43a51391b9 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -587,11 +587,6 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { CHECK(existing_entry_point != nullptr) << PrettyMethod() << "@" << this; ClassLinker* class_linker = runtime->GetClassLinker(); - if (class_linker->IsQuickGenericJniStub(existing_entry_point)) { - // The generic JNI does not have any method header. - return nullptr; - } - if (existing_entry_point == GetQuickProxyInvokeHandler()) { DCHECK(IsProxyMethod() && !IsConstructor()); // The proxy entry point does not have any method header. @@ -599,7 +594,8 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { } // Check whether the current entry point contains this pc. - if (!class_linker->IsQuickResolutionStub(existing_entry_point) && + if (!class_linker->IsQuickGenericJniStub(existing_entry_point) && + !class_linker->IsQuickResolutionStub(existing_entry_point) && !class_linker->IsQuickToInterpreterBridge(existing_entry_point)) { OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromEntryPoint(existing_entry_point); @@ -632,19 +628,13 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { OatFile::OatMethod oat_method = FindOatMethodFor(this, class_linker->GetImagePointerSize(), &found); if (!found) { - if (class_linker->IsQuickResolutionStub(existing_entry_point)) { - // We are running the generic jni stub, but the entry point of the method has not - // been updated yet. - DCHECK_EQ(pc, 0u) << "Should be a downcall"; - DCHECK(IsNative()); - return nullptr; - } - if (existing_entry_point == GetQuickInstrumentationEntryPoint()) { - // We are running the generic jni stub, but the method is being instrumented. - // NB We would normally expect the pc to be zero but we can have non-zero pc's if - // instrumentation is installed or removed during the call which is using the generic jni - // trampoline. - DCHECK(IsNative()); + if (IsNative()) { + // We are running the GenericJNI stub. The entrypoint may point + // to different entrypoints or to a JIT-compiled JNI stub. + DCHECK(class_linker->IsQuickGenericJniStub(existing_entry_point) || + class_linker->IsQuickResolutionStub(existing_entry_point) || + existing_entry_point == GetQuickInstrumentationEntryPoint() || + (jit != nullptr && jit->GetCodeCache()->ContainsPc(existing_entry_point))); return nullptr; } // Only for unit tests. @@ -702,13 +692,15 @@ void ArtMethod::CopyFrom(ArtMethod* src, PointerSize image_pointer_size) { declaring_class_ = GcRoot(const_cast(src)->GetDeclaringClass()); // If the entry point of the method we are copying from is from JIT code, we just - // put the entry point of the new method to interpreter. We could set the entry point - // to the JIT code, but this would require taking the JIT code cache lock to notify - // it, which we do not want at this level. + // put the entry point of the new method to interpreter or GenericJNI. We could set + // the entry point to the JIT code, but this would require taking the JIT code cache + // lock to notify it, which we do not want at this level. Runtime* runtime = Runtime::Current(); if (runtime->UseJitCompilation()) { if (runtime->GetJit()->GetCodeCache()->ContainsPc(GetEntryPointFromQuickCompiledCode())) { - SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(), image_pointer_size); + SetEntryPointFromQuickCompiledCodePtrSize( + src->IsNative() ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge(), + image_pointer_size); } } // Clear the profiling info for the same reasons as the JIT code. diff --git a/runtime/art_method.h b/runtime/art_method.h index dca6f37254..0a592e0528 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -242,8 +242,9 @@ class ArtMethod FINAL { return (GetAccessFlags() & kAccDefault) != 0; } + template bool IsObsolete() { - return (GetAccessFlags() & kAccObsoleteMethod) != 0; + return (GetAccessFlags() & kAccObsoleteMethod) != 0; } void SetIsObsolete() { @@ -376,6 +377,7 @@ class ArtMethod FINAL { ALWAYS_INLINE uint32_t GetDexMethodIndexUnchecked() { return dex_method_index_; } + template ALWAYS_INLINE uint32_t GetDexMethodIndex() REQUIRES_SHARED(Locks::mutator_lock_); void SetDexMethodIndex(uint32_t new_idx) { @@ -460,12 +462,11 @@ class ArtMethod FINAL { } ProfilingInfo* GetProfilingInfo(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_) { - // Don't do a read barrier in the DCHECK, as GetProfilingInfo is called in places - // where the declaring class is treated as a weak reference (accessing it with - // a read barrier would either prevent unloading the class, or crash the runtime if - // the GC wants to unload it). - DCHECK(!IsNative()); - if (UNLIKELY(IsProxyMethod())) { + // Don't do a read barrier in the DCHECK() inside GetAccessFlags() called by IsNative(), + // as GetProfilingInfo is called in places where the declaring class is treated as a weak + // reference (accessing it with a read barrier would either prevent unloading the class, + // or crash the runtime if the GC wants to unload it). + if (UNLIKELY(IsNative()) || UNLIKELY(IsProxyMethod())) { return nullptr; } return reinterpret_cast(GetDataPtrSize(pointer_size)); diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 2bf4372b1f..f3450da306 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -245,7 +245,7 @@ ArtMethod* GetCalleeSaveMethodCaller(ArtMethod** sp, CalleeSaveType type, bool d CallerAndOuterMethod GetCalleeSaveMethodCallerAndOuterMethod(Thread* self, CalleeSaveType type) { CallerAndOuterMethod result; ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame(); + ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrameKnownNotTagged(); auto outer_caller_and_pc = DoGetCalleeSaveMethodOuterCallerAndPc(sp, type); result.outer_method = outer_caller_and_pc.first; uintptr_t caller_pc = outer_caller_and_pc.second; @@ -256,7 +256,7 @@ CallerAndOuterMethod GetCalleeSaveMethodCallerAndOuterMethod(Thread* self, Calle ArtMethod* GetCalleeSaveOuterMethod(Thread* self, CalleeSaveType type) { ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame(); + ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrameKnownNotTagged(); return DoGetCalleeSaveMethodOuterCallerAndPc(sp, type).first; } diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 2496aa0f58..0a76cddf5e 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -31,6 +31,7 @@ #include "index_bss_mapping.h" #include "instrumentation.h" #include "interpreter/interpreter.h" +#include "jit/jit.h" #include "linear_alloc.h" #include "method_handles.h" #include "method_reference.h" @@ -2167,6 +2168,11 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod** // Note: We cannot walk the stack properly until fixed up below. 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(); @@ -2188,7 +2194,7 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod** } // Fix up managed-stack things in Thread. After this we can walk the stack. - self->SetTopOfStack(sp); + self->SetTopOfStackTagged(sp); self->VerifyStack(); @@ -2308,6 +2314,7 @@ extern "C" uint64_t artQuickGenericJniEndTrampoline(Thread* self, // anything that requires a mutator lock before that would cause problems as GC may have the // exclusive mutator lock and may be moving objects, etc. ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame(); + DCHECK(self->GetManagedStack()->GetTopQuickFrameTag()); uint32_t* sp32 = reinterpret_cast(sp); ArtMethod* called = *sp; uint32_t cookie = *(sp32 - 1); diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 953e195480..0d95bc6e64 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -643,7 +643,7 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ return; } - if (method->IsClassInitializer() || method->IsNative() || !method->IsCompilable()) { + if (method->IsClassInitializer() || !method->IsCompilable()) { // We do not want to compile such methods. return; } @@ -659,7 +659,8 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ count *= priority_thread_weight_; } int32_t new_count = starting_count + count; // int32 here to avoid wrap-around; - if (starting_count < warm_method_threshold_) { + // 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_) && (method->GetProfilingInfo(kRuntimePointerSize) == nullptr)) { bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false); @@ -696,6 +697,7 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ // 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)) { DCHECK(thread_pool_ != nullptr); thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr)); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 32205138bd..a5c167eee8 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -55,6 +55,107 @@ static constexpr int kProtCode = PROT_READ | PROT_EXEC; static constexpr size_t kCodeSizeLogThreshold = 50 * KB; static constexpr size_t kStackMapSizeLogThreshold = 50 * KB; +class JitCodeCache::JniStubKey { + public: + explicit JniStubKey(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) + : shorty_(method->GetShorty()), + is_static_(method->IsStatic()), + is_fast_native_(method->IsFastNative()), + is_critical_native_(method->IsCriticalNative()), + is_synchronized_(method->IsSynchronized()) { + DCHECK(!(is_fast_native_ && is_critical_native_)); + } + + bool operator<(const JniStubKey& rhs) const { + if (is_static_ != rhs.is_static_) { + return rhs.is_static_; + } + if (is_synchronized_ != rhs.is_synchronized_) { + return rhs.is_synchronized_; + } + if (is_fast_native_ != rhs.is_fast_native_) { + return rhs.is_fast_native_; + } + if (is_critical_native_ != rhs.is_critical_native_) { + return rhs.is_critical_native_; + } + return strcmp(shorty_, rhs.shorty_) < 0; + } + + // Update the shorty to point to another method's shorty. Call this function when removing + // the method that references the old shorty from JniCodeData and not removing the entire + // JniCodeData; the old shorty may become a dangling pointer when that method is unloaded. + void UpdateShorty(ArtMethod* method) const REQUIRES_SHARED(Locks::mutator_lock_) { + const char* shorty = method->GetShorty(); + DCHECK_STREQ(shorty_, shorty); + shorty_ = shorty; + } + + private: + // The shorty points to a DexFile data and may need to change + // to point to the same shorty in a different DexFile. + mutable const char* shorty_; + + const bool is_static_; + const bool is_fast_native_; + const bool is_critical_native_; + const bool is_synchronized_; +}; + +class JitCodeCache::JniStubData { + public: + JniStubData() : code_(nullptr), methods_() {} + + void SetCode(const void* code) { + DCHECK(code != nullptr); + code_ = code; + } + + const void* GetCode() const { + return code_; + } + + bool IsCompiled() const { + return GetCode() != nullptr; + } + + void AddMethod(ArtMethod* method) { + if (!ContainsElement(methods_, method)) { + methods_.push_back(method); + } + } + + const std::vector& GetMethods() const { + return methods_; + } + + void RemoveMethodsIn(const LinearAlloc& alloc) { + auto kept_end = std::remove_if( + methods_.begin(), + methods_.end(), + [&alloc](ArtMethod* method) { return alloc.ContainsUnsafe(method); }); + methods_.erase(kept_end, methods_.end()); + } + + bool RemoveMethod(ArtMethod* method) { + auto it = std::find(methods_.begin(), methods_.end(), method); + if (it != methods_.end()) { + methods_.erase(it); + return true; + } else { + return false; + } + } + + void MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) { + std::replace(methods_.begin(), methods_.end(), old_method, new_method); + } + + private: + const void* code_; + std::vector methods_; +}; + JitCodeCache* JitCodeCache::Create(size_t initial_capacity, size_t max_capacity, bool generate_debug_info, @@ -193,14 +294,36 @@ bool JitCodeCache::ContainsPc(const void* ptr) const { bool JitCodeCache::ContainsMethod(ArtMethod* method) { MutexLock mu(Thread::Current(), lock_); - for (auto& it : method_code_map_) { - if (it.second == method) { + if (UNLIKELY(method->IsNative())) { + auto it = jni_stubs_map_.find(JniStubKey(method)); + if (it != jni_stubs_map_.end() && + it->second.IsCompiled() && + ContainsElement(it->second.GetMethods(), method)) { return true; } + } else { + for (const auto& it : method_code_map_) { + if (it.second == method) { + return true; + } + } } return false; } +const void* JitCodeCache::GetJniStubCode(ArtMethod* method) { + DCHECK(method->IsNative()); + MutexLock mu(Thread::Current(), lock_); + auto it = jni_stubs_map_.find(JniStubKey(method)); + if (it != jni_stubs_map_.end()) { + JniStubData& data = it->second; + if (data.IsCompiled() && ContainsElement(data.GetMethods(), method)) { + return data.GetCode(); + } + } + return nullptr; +} + class ScopedCodeCacheWrite : ScopedTrace { public: explicit ScopedCodeCacheWrite(MemMap* code_map, bool only_for_tlb_shootdown = false) @@ -426,7 +549,9 @@ void JitCodeCache::FreeCode(const void* code_ptr) { // Notify native debugger that we are about to remove the code. // It does nothing if we are not using native debugger. DeleteJITCodeEntryForAddress(reinterpret_cast(code_ptr)); - FreeData(GetRootTable(code_ptr)); + if (OatQuickMethodHeader::FromCodePointer(code_ptr)->IsOptimized()) { + FreeData(GetRootTable(code_ptr)); + } // else this is a JNI stub without any data. FreeCode(reinterpret_cast(allocation)); } @@ -463,6 +588,16 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { // lead to a deadlock. { ScopedCodeCacheWrite scc(code_map_.get()); + for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) { + it->second.RemoveMethodsIn(alloc); + if (it->second.GetMethods().empty()) { + method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->second.GetCode())); + it = jni_stubs_map_.erase(it); + } else { + it->first.UpdateShorty(it->second.GetMethods().front()); + ++it; + } + } for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { if (alloc.ContainsUnsafe(it->second)) { method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->first)); @@ -572,7 +707,8 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, bool has_should_deoptimize_flag, const ArenaSet& cha_single_implementation_list) { - DCHECK(stack_map != nullptr); + DCHECK_NE(stack_map != nullptr, method->IsNative()); + DCHECK(!method->IsNative() || !osr); size_t alignment = GetInstructionSetAlignment(kRuntimeISA); // Ensure the header ends up at expected instruction alignment. size_t header_size = RoundUp(sizeof(OatQuickMethodHeader), alignment); @@ -596,8 +732,8 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, std::copy(code, code + code_size, code_ptr); method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); new (method_header) OatQuickMethodHeader( - code_ptr - stack_map, - code_ptr - method_info, + (stack_map != nullptr) ? code_ptr - stack_map : 0u, + (method_info != nullptr) ? code_ptr - method_info : 0u, frame_size_in_bytes, core_spill_mask, fp_spill_mask, @@ -652,24 +788,40 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, // possible that the compiled code is considered invalidated by some class linking, // but below we still make the compiled code valid for the method. MutexLock mu(self, lock_); - // Fill the root table before updating the entry point. - DCHECK_EQ(FromStackMapToRoots(stack_map), roots_data); - DCHECK_LE(roots_data, stack_map); - 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)); - } - method_code_map_.Put(code_ptr, method); - if (osr) { - number_of_osr_compilations_++; - osr_code_map_.Put(method, code_ptr); + if (UNLIKELY(method->IsNative())) { + DCHECK(stack_map == nullptr); + DCHECK(roots_data == nullptr); + auto it = jni_stubs_map_.find(JniStubKey(method)); + DCHECK(it != jni_stubs_map_.end()) + << "Entry inserted in NotifyCompilationOf() should be alive."; + JniStubData* data = &it->second; + DCHECK(ContainsElement(data->GetMethods(), method)) + << "Entry inserted in NotifyCompilationOf() should contain this method."; + data->SetCode(code_ptr); + instrumentation::Instrumentation* instrum = Runtime::Current()->GetInstrumentation(); + for (ArtMethod* m : data->GetMethods()) { + instrum->UpdateMethodsCode(m, method_header->GetEntryPoint()); + } } else { - Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( - method, method_header->GetEntryPoint()); + // Fill the root table before updating the entry point. + DCHECK_EQ(FromStackMapToRoots(stack_map), roots_data); + DCHECK_LE(roots_data, stack_map); + 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)); + } + method_code_map_.Put(code_ptr, method); + if (osr) { + number_of_osr_compilations_++; + osr_code_map_.Put(method, code_ptr); + } else { + Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( + method, method_header->GetEntryPoint()); + } } if (collection_in_progress_) { // We need to update the live bitmap if there is a GC to ensure it sees this new @@ -703,45 +855,18 @@ size_t JitCodeCache::CodeCacheSize() { } bool JitCodeCache::RemoveMethod(ArtMethod* method, bool release_memory) { - MutexLock mu(Thread::Current(), lock_); - if (method->IsNative()) { - return false; - } + // This function is used only for testing and only with non-native methods. + CHECK(!method->IsNative()); - bool in_cache = false; - { - ScopedCodeCacheWrite ccw(code_map_.get()); - for (auto code_iter = method_code_map_.begin(); code_iter != method_code_map_.end();) { - if (code_iter->second == method) { - if (release_memory) { - FreeCode(code_iter->first); - } - code_iter = method_code_map_.erase(code_iter); - in_cache = true; - continue; - } - ++code_iter; - } - } + MutexLock mu(Thread::Current(), lock_); - bool osr = false; - auto code_map = osr_code_map_.find(method); - if (code_map != osr_code_map_.end()) { - osr_code_map_.erase(code_map); - osr = true; - } + bool osr = osr_code_map_.find(method) != osr_code_map_.end(); + bool in_cache = RemoveMethodLocked(method, release_memory); if (!in_cache) { return false; } - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - if (info != nullptr) { - auto profile = std::find(profiling_infos_.begin(), profiling_infos_.end(), info); - DCHECK(profile != profiling_infos_.end()); - profiling_infos_.erase(profile); - } - method->SetProfilingInfo(nullptr); method->ClearCounter(); Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( method, GetQuickToInterpreterBridge()); @@ -753,34 +878,58 @@ bool JitCodeCache::RemoveMethod(ArtMethod* method, bool release_memory) { return true; } +bool JitCodeCache::RemoveMethodLocked(ArtMethod* method, bool release_memory) { + if (LIKELY(!method->IsNative())) { + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + if (info != nullptr) { + RemoveElement(profiling_infos_, info); + } + method->SetProfilingInfo(nullptr); + } + + bool in_cache = false; + ScopedCodeCacheWrite ccw(code_map_.get()); + if (UNLIKELY(method->IsNative())) { + auto it = jni_stubs_map_.find(JniStubKey(method)); + if (it != jni_stubs_map_.end() && it->second.RemoveMethod(method)) { + in_cache = true; + if (it->second.GetMethods().empty()) { + if (release_memory) { + FreeCode(it->second.GetCode()); + } + jni_stubs_map_.erase(it); + } else { + it->first.UpdateShorty(it->second.GetMethods().front()); + } + } + } else { + for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { + if (it->second == method) { + in_cache = true; + if (release_memory) { + FreeCode(it->first); + } + it = method_code_map_.erase(it); + } else { + ++it; + } + } + + auto osr_it = osr_code_map_.find(method); + if (osr_it != osr_code_map_.end()) { + osr_code_map_.erase(osr_it); + } + } + + return in_cache; +} + // This notifies the code cache that the given method has been redefined and that it should remove // any cached information it has on the method. All threads must be suspended before calling this // method. The compiled code for the method (if there is any) must not be in any threads call stack. void JitCodeCache::NotifyMethodRedefined(ArtMethod* method) { MutexLock mu(Thread::Current(), lock_); - if (method->IsNative()) { - return; - } - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - if (info != nullptr) { - auto profile = std::find(profiling_infos_.begin(), profiling_infos_.end(), info); - DCHECK(profile != profiling_infos_.end()); - profiling_infos_.erase(profile); - } - method->SetProfilingInfo(nullptr); - ScopedCodeCacheWrite ccw(code_map_.get()); - for (auto code_iter = method_code_map_.begin(); code_iter != method_code_map_.end();) { - if (code_iter->second == method) { - FreeCode(code_iter->first); - code_iter = method_code_map_.erase(code_iter); - continue; - } - ++code_iter; - } - auto code_map = osr_code_map_.find(method); - if (code_map != osr_code_map_.end()) { - osr_code_map_.erase(code_map); - } + RemoveMethodLocked(method, /* release_memory */ true); } // This invalidates old_method. Once this function returns one can no longer use old_method to @@ -790,11 +939,15 @@ void JitCodeCache::NotifyMethodRedefined(ArtMethod* method) { // shouldn't be used since it is no longer logically in the jit code cache. // TODO We should add DCHECKS that validate that the JIT is paused when this method is entered. void JitCodeCache::MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) { - // Native methods have no profiling info and need no special handling from the JIT code cache. + MutexLock mu(Thread::Current(), lock_); if (old_method->IsNative()) { + // Update methods in jni_stubs_map_. + for (auto& entry : jni_stubs_map_) { + JniStubData& data = entry.second; + data.MoveObsoleteMethod(old_method, new_method); + } return; } - MutexLock mu(Thread::Current(), lock_); // Update ProfilingInfo to the new one and remove it from the old_method. if (old_method->GetProfilingInfo(kRuntimePointerSize) != nullptr) { DCHECK_EQ(old_method->GetProfilingInfo(kRuntimePointerSize)->GetMethod(), old_method); @@ -936,7 +1089,7 @@ class MarkCodeClosure FINAL : public Closure { // its stack frame, it is not the method owning return_pc_. We just pass null to // LookupMethodHeader: the method is only checked against in debug builds. OatQuickMethodHeader* method_header = - code_cache_->LookupMethodHeader(frame.return_pc_, nullptr); + code_cache_->LookupMethodHeader(frame.return_pc_, /* method */ nullptr); if (method_header != nullptr) { const void* code = method_header->GetCode(); CHECK(code_cache_->GetLiveBitmap()->Test(FromCodeToAllocation(code))); @@ -1089,7 +1242,7 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { const void* entry_point = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); if (ContainsPc(entry_point)) { info->SetSavedEntryPoint(entry_point); - // Don't call Instrumentation::UpdateMethods, as it can check the declaring + // Don't call Instrumentation::UpdateMethodsCode(), as it can check the declaring // class of the method. We may be concurrently running a GC which makes accessing // the class unsafe. We know it is OK to bypass the instrumentation as we've just // checked that the current entry point is JIT compiled code. @@ -1098,6 +1251,25 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { } DCHECK(CheckLiveCompiledCodeHasProfilingInfo()); + + // Change entry points of native methods back to the GenericJNI entrypoint. + for (const auto& entry : jni_stubs_map_) { + const JniStubData& data = entry.second; + if (!data.IsCompiled()) { + continue; + } + // Make sure a single invocation of the GenericJNI trampoline tries to recompile. + uint16_t new_counter = Runtime::Current()->GetJit()->HotMethodThreshold() - 1u; + const OatQuickMethodHeader* method_header = + OatQuickMethodHeader::FromCodePointer(data.GetCode()); + for (ArtMethod* method : data.GetMethods()) { + if (method->GetEntryPointFromQuickCompiledCode() == method_header->GetEntryPoint()) { + // Don't call Instrumentation::UpdateMethodsCode(), same as for normal methods above. + method->SetCounter(new_counter); + method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub()); + } + } + } } live_bitmap_.reset(nullptr); NotifyCollectionDone(self); @@ -1113,13 +1285,22 @@ void JitCodeCache::RemoveUnmarkedCode(Thread* self) { MutexLock mu(self, lock_); ScopedCodeCacheWrite scc(code_map_.get()); // 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; + if (!data->IsCompiled() || GetLiveBitmap()->Test(FromCodeToAllocation(data->GetCode()))) { + ++it; + } else { + method_headers.insert(OatQuickMethodHeader::FromCodePointer(data->GetCode())); + it = jni_stubs_map_.erase(it); + } + } for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { const void* code_ptr = it->first; uintptr_t allocation = FromCodeToAllocation(code_ptr); if (GetLiveBitmap()->Test(allocation)) { ++it; } else { - method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->first)); + method_headers.insert(OatQuickMethodHeader::FromCodePointer(code_ptr)); it = method_code_map_.erase(it); } } @@ -1158,6 +1339,17 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { // an entry point is either: // - an osr compiled code, that will be removed if not in a thread call stack. // - discarded compiled code, that will be removed if not in a thread call stack. + for (const auto& entry : jni_stubs_map_) { + const JniStubData& data = entry.second; + const void* code_ptr = data.GetCode(); + const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + for (ArtMethod* method : data.GetMethods()) { + if (method_header->GetEntryPoint() == method->GetEntryPointFromQuickCompiledCode()) { + GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr)); + break; + } + } + } for (const auto& it : method_code_map_) { ArtMethod* method = it.second; const void* code_ptr = it.first; @@ -1237,19 +1429,51 @@ OatQuickMethodHeader* JitCodeCache::LookupMethodHeader(uintptr_t pc, ArtMethod* return nullptr; } - MutexLock mu(Thread::Current(), lock_); - if (method_code_map_.empty()) { - return nullptr; + if (!kIsDebugBuild) { + // Called with null `method` only from MarkCodeClosure::Run() in debug build. + CHECK(method != nullptr); } - auto it = method_code_map_.lower_bound(reinterpret_cast(pc)); - --it; - const void* code_ptr = it->first; - OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); - if (!method_header->Contains(pc)) { - return nullptr; + MutexLock mu(Thread::Current(), lock_); + OatQuickMethodHeader* method_header = nullptr; + ArtMethod* found_method = nullptr; // Only for DCHECK(), not for JNI stubs. + if (method != nullptr && UNLIKELY(method->IsNative())) { + auto it = jni_stubs_map_.find(JniStubKey(method)); + if (it == jni_stubs_map_.end() || !ContainsElement(it->second.GetMethods(), method)) { + return nullptr; + } + const void* code_ptr = it->second.GetCode(); + method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + if (!method_header->Contains(pc)) { + return nullptr; + } + } else { + auto it = method_code_map_.lower_bound(reinterpret_cast(pc)); + if (it != method_code_map_.begin()) { + --it; + const void* code_ptr = it->first; + if (OatQuickMethodHeader::FromCodePointer(code_ptr)->Contains(pc)) { + method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + found_method = it->second; + } + } + if (method_header == nullptr && method == nullptr) { + // Scan all compiled JNI stubs as well. This slow search is used only + // for checks in debug build, for release builds the `method` is not null. + for (auto&& entry : jni_stubs_map_) { + const JniStubData& data = entry.second; + if (data.IsCompiled() && + OatQuickMethodHeader::FromCodePointer(data.GetCode())->Contains(pc)) { + method_header = OatQuickMethodHeader::FromCodePointer(data.GetCode()); + } + } + } + if (method_header == nullptr) { + return nullptr; + } } - if (kIsDebugBuild && method != nullptr) { + + if (kIsDebugBuild && method != nullptr && !method->IsNative()) { // When we are walking the stack to redefine classes and creating obsolete methods it is // possible that we might have updated the method_code_map by making this method obsolete in a // previous frame. Therefore we should just check that the non-obsolete version of this method @@ -1258,9 +1482,9 @@ OatQuickMethodHeader* JitCodeCache::LookupMethodHeader(uintptr_t pc, ArtMethod* // occur when we are in the process of allocating and setting up obsolete methods. Otherwise // method and it->second should be identical. (See openjdkjvmti/ti_redefine.cc for more // information.) - DCHECK_EQ(it->second->GetNonObsoleteMethod(), method->GetNonObsoleteMethod()) + DCHECK_EQ(found_method->GetNonObsoleteMethod(), method->GetNonObsoleteMethod()) << ArtMethod::PrettyMethod(method->GetNonObsoleteMethod()) << " " - << ArtMethod::PrettyMethod(it->second->GetNonObsoleteMethod()) << " " + << ArtMethod::PrettyMethod(found_method->GetNonObsoleteMethod()) << " " << std::hex << pc; } return method_header; @@ -1449,21 +1673,51 @@ bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr return false; } - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - if (info == nullptr) { - VLOG(jit) << method->PrettyMethod() << " needs a ProfilingInfo to be compiled"; - // Because the counter is not atomic, there are some rare cases where we may not hit the - // threshold for creating the ProfilingInfo. Reset the counter now to "correct" this. - ClearMethodCounter(method, /*was_warm*/ false); - return false; - } + if (UNLIKELY(method->IsNative())) { + JniStubKey key(method); + auto it = jni_stubs_map_.find(key); + bool new_compilation = false; + if (it == jni_stubs_map_.end()) { + // Create a new entry to mark the stub as being compiled. + it = jni_stubs_map_.Put(key, JniStubData{}); + new_compilation = true; + } + JniStubData* data = &it->second; + data->AddMethod(method); + if (data->IsCompiled()) { + OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(data->GetCode()); + const void* entrypoint = method_header->GetEntryPoint(); + // Update also entrypoints of other methods held by the JniStubData. + // We could simply update the entrypoint of `method` but if the last JIT GC has + // changed these entrypoints to GenericJNI in preparation for a full GC, we may + // as well change them back as this stub shall not be collected anyway and this + // can avoid a few expensive GenericJNI calls. + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + for (ArtMethod* m : data->GetMethods()) { + instrumentation->UpdateMethodsCode(m, entrypoint); + } + if (collection_in_progress_) { + GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(data->GetCode())); + } + } + return new_compilation; + } else { + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + if (info == nullptr) { + VLOG(jit) << method->PrettyMethod() << " needs a ProfilingInfo to be compiled"; + // Because the counter is not atomic, there are some rare cases where we may not hit the + // threshold for creating the ProfilingInfo. Reset the counter now to "correct" this. + ClearMethodCounter(method, /*was_warm*/ false); + return false; + } - if (info->IsMethodBeingCompiled(osr)) { - return false; - } + if (info->IsMethodBeingCompiled(osr)) { + return false; + } - info->SetIsMethodBeingCompiled(true, osr); - return true; + info->SetIsMethodBeingCompiled(true, osr); + return true; + } } ProfilingInfo* JitCodeCache::NotifyCompilerUse(ArtMethod* method, Thread* self) { @@ -1485,10 +1739,23 @@ void JitCodeCache::DoneCompilerUse(ArtMethod* method, Thread* self) { info->DecrementInlineUse(); } -void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self ATTRIBUTE_UNUSED, bool osr) { - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - DCHECK(info->IsMethodBeingCompiled(osr)); - info->SetIsMethodBeingCompiled(false, osr); +void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self, bool osr) { + DCHECK_EQ(Thread::Current(), self); + MutexLock mu(self, lock_); + if (UNLIKELY(method->IsNative())) { + auto it = jni_stubs_map_.find(JniStubKey(method)); + DCHECK(it != jni_stubs_map_.end()); + JniStubData* data = &it->second; + DCHECK(ContainsElement(data->GetMethods(), method)); + if (UNLIKELY(!data->IsCompiled())) { + // Failed to compile; the JNI compiler never fails, but the cache may be full. + jni_stubs_map_.erase(it); // Remove the entry added in NotifyCompilationOf(). + } // else CommitCodeInternal() updated entrypoints of all methods in the JniStubData. + } else { + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + DCHECK(info->IsMethodBeingCompiled(osr)); + info->SetIsMethodBeingCompiled(false, osr); + } } size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) { @@ -1498,6 +1765,7 @@ size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) { void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method, const OatQuickMethodHeader* header) { + DCHECK(!method->IsNative()); ProfilingInfo* profiling_info = method->GetProfilingInfo(kRuntimePointerSize); if ((profiling_info != nullptr) && (profiling_info->GetSavedEntryPoint() == header->GetEntryPoint())) { @@ -1553,6 +1821,7 @@ void JitCodeCache::Dump(std::ostream& os) { os << "Current JIT code cache size: " << PrettySize(used_memory_for_code_) << "\n" << "Current JIT data cache size: " << PrettySize(used_memory_for_data_) << "\n" << "Current JIT capacity: " << PrettySize(current_capacity_) << "\n" + << "Current number of JIT JNI stub entries: " << jni_stubs_map_.size() << "\n" << "Current number of JIT code cache entries: " << method_code_map_.size() << "\n" << "Total number of JIT compilations: " << number_of_compilations_ << "\n" << "Total number of JIT compilations for on stack replacement: " diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 46a408590b..fc011ddb96 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -35,9 +35,23 @@ template class Handle; class LinearAlloc; class InlineCache; class IsMarkedVisitor; +class JitJniStubTestHelper; class OatQuickMethodHeader; struct ProfileMethodInfo; class ProfilingInfo; +class Thread; + +namespace gc { +namespace accounting { +template class MemoryRangeBitmap; +} // namespace accounting +} // namespace gc + +namespace mirror { +class Class; +class Object; +template class ObjectArray; +} // namespace mirror namespace gc { namespace accounting { @@ -137,6 +151,9 @@ class JitCodeCache { // Return true if the code cache contains this method. bool ContainsMethod(ArtMethod* method) REQUIRES(!lock_); + // Return the code pointer for a JNI-compiled stub if the method is in the cache, null otherwise. + const void* GetJniStubCode(ArtMethod* method) REQUIRES(!lock_); + // Allocate a region of data that contain `size` bytes, and potentially space // for storing `number_of_roots` roots. Returns null if there is no more room. // Return the number of bytes allocated. @@ -160,11 +177,6 @@ class JitCodeCache { return live_bitmap_.get(); } - // Return whether we should do a full collection given the current state of the cache. - bool ShouldDoFullCollection() - REQUIRES(lock_) - REQUIRES_SHARED(Locks::mutator_lock_); - // Perform a collection on the code cache. void GarbageCollectCache(Thread* self) REQUIRES(!lock_) @@ -296,6 +308,12 @@ class JitCodeCache { REQUIRES(!lock_) REQUIRES(!Locks::cha_lock_); + // Removes method from the cache. The caller must ensure that all threads + // are suspended and the method should not be in any thread's stack. + bool RemoveMethodLocked(ArtMethod* method, bool release_memory) + REQUIRES(lock_) + REQUIRES(Locks::mutator_lock_); + // Free in the mspace allocations for `code_ptr`. void FreeCode(const void* code_ptr) REQUIRES(lock_); @@ -315,6 +333,11 @@ class JitCodeCache { // Set the footprint limit of the code cache. void SetFootprintLimit(size_t new_footprint) REQUIRES(lock_); + // Return whether we should do a full collection given the current state of the cache. + bool ShouldDoFullCollection() + REQUIRES(lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + void DoCollection(Thread* self, bool collect_profiling_info) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -341,6 +364,9 @@ class JitCodeCache { REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); + class JniStubKey; + class JniStubData; + // Lock for guarding allocations, collections, and the method_code_map_. Mutex lock_; // Condition to wait on during collection. @@ -357,6 +383,8 @@ class JitCodeCache { void* data_mspace_ GUARDED_BY(lock_); // Bitmap for collecting code and data. std::unique_ptr live_bitmap_; + // Holds compiled code associated with the shorty for a JNI stub. + SafeMap jni_stubs_map_ GUARDED_BY(lock_); // Holds compiled code associated to the ArtMethod. SafeMap method_code_map_ GUARDED_BY(lock_); // Holds osr compiled code associated to the ArtMethod. @@ -418,6 +446,7 @@ class JitCodeCache { // Condition to wait on for accessing inline caches. ConditionVariable inline_cache_cond_ GUARDED_BY(lock_); + friend class art::JitJniStubTestHelper; DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache); }; diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 01853de403..acbc6e63a4 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -357,8 +357,8 @@ static void SampleClassesAndExecutedMethods(pthread_t profiler_pthread, sampled_methods->AddReference(method.GetDexFile(), method.GetDexMethodIndex()); } } else { - CHECK_EQ(method.GetCounter(), 0u) << method.PrettyMethod() - << " access_flags=" << method.GetAccessFlags(); + // We do not record native methods. Once we AOT-compile the app, all native + // methods shall have their thunks compiled. } } } diff --git a/runtime/managed_stack-inl.h b/runtime/managed_stack-inl.h index 689dd8009a..678be8e098 100644 --- a/runtime/managed_stack-inl.h +++ b/runtime/managed_stack-inl.h @@ -24,7 +24,7 @@ namespace art { inline ShadowFrame* ManagedStack::PushShadowFrame(ShadowFrame* new_top_frame) { - DCHECK(top_quick_frame_ == nullptr); + DCHECK(!HasTopQuickFrame()); ShadowFrame* old_frame = top_shadow_frame_; top_shadow_frame_ = new_top_frame; new_top_frame->SetLink(old_frame); @@ -32,7 +32,7 @@ inline ShadowFrame* ManagedStack::PushShadowFrame(ShadowFrame* new_top_frame) { } inline ShadowFrame* ManagedStack::PopShadowFrame() { - DCHECK(top_quick_frame_ == nullptr); + DCHECK(!HasTopQuickFrame()); CHECK(top_shadow_frame_ != nullptr); ShadowFrame* frame = top_shadow_frame_; top_shadow_frame_ = frame->GetLink(); diff --git a/runtime/managed_stack.h b/runtime/managed_stack.h index 4f1984d55a..07078ecb13 100644 --- a/runtime/managed_stack.h +++ b/runtime/managed_stack.h @@ -24,6 +24,7 @@ #include "base/logging.h" #include "base/macros.h" #include "base/mutex.h" +#include "base/bit_utils.h" namespace art { @@ -42,7 +43,9 @@ template class StackReference; class PACKED(4) ManagedStack { public: ManagedStack() - : top_quick_frame_(nullptr), link_(nullptr), top_shadow_frame_(nullptr) {} + : tagged_top_quick_frame_(TaggedTopQuickFrame::CreateNotTagged(nullptr)), + link_(nullptr), + top_shadow_frame_(nullptr) {} void PushManagedStackFragment(ManagedStack* fragment) { // Copy this top fragment into given fragment. @@ -63,17 +66,36 @@ class PACKED(4) ManagedStack { return link_; } + ArtMethod** GetTopQuickFrameKnownNotTagged() const { + return tagged_top_quick_frame_.GetSpKnownNotTagged(); + } + ArtMethod** GetTopQuickFrame() const { - return top_quick_frame_; + return tagged_top_quick_frame_.GetSp(); + } + + bool GetTopQuickFrameTag() const { + return tagged_top_quick_frame_.GetTag(); + } + + bool HasTopQuickFrame() const { + return tagged_top_quick_frame_.GetTaggedSp() != 0u; } void SetTopQuickFrame(ArtMethod** top) { DCHECK(top_shadow_frame_ == nullptr); - top_quick_frame_ = top; + DCHECK_ALIGNED(top, 4u); + tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateNotTagged(top); } - static size_t TopQuickFrameOffset() { - return OFFSETOF_MEMBER(ManagedStack, top_quick_frame_); + void SetTopQuickFrameTagged(ArtMethod** top) { + DCHECK(top_shadow_frame_ == nullptr); + DCHECK_ALIGNED(top, 4u); + tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateTagged(top); + } + + static size_t TaggedTopQuickFrameOffset() { + return OFFSETOF_MEMBER(ManagedStack, tagged_top_quick_frame_); } ALWAYS_INLINE ShadowFrame* PushShadowFrame(ShadowFrame* new_top_frame); @@ -83,8 +105,12 @@ class PACKED(4) ManagedStack { return top_shadow_frame_; } + bool HasTopShadowFrame() const { + return GetTopShadowFrame() != nullptr; + } + void SetTopShadowFrame(ShadowFrame* top) { - DCHECK(top_quick_frame_ == nullptr); + DCHECK_EQ(tagged_top_quick_frame_.GetTaggedSp(), 0u); top_shadow_frame_ = top; } @@ -97,7 +123,47 @@ class PACKED(4) ManagedStack { bool ShadowFramesContain(StackReference* shadow_frame_entry) const; private: - ArtMethod** top_quick_frame_; + // Encodes the top quick frame (which must be at least 4-byte aligned) + // and a flag that marks the GenericJNI trampoline. + class TaggedTopQuickFrame { + public: + static TaggedTopQuickFrame CreateNotTagged(ArtMethod** sp) { + DCHECK_ALIGNED(sp, 4u); + return TaggedTopQuickFrame(reinterpret_cast(sp)); + } + + static TaggedTopQuickFrame CreateTagged(ArtMethod** sp) { + DCHECK_ALIGNED(sp, 4u); + return TaggedTopQuickFrame(reinterpret_cast(sp) | 1u); + } + + // Get SP known to be not tagged and non-null. + ArtMethod** GetSpKnownNotTagged() const { + DCHECK(!GetTag()); + DCHECK_NE(tagged_sp_, 0u); + return reinterpret_cast(tagged_sp_); + } + + ArtMethod** GetSp() const { + return reinterpret_cast(tagged_sp_ & ~static_cast(1u)); + } + + bool GetTag() const { + return (tagged_sp_ & 1u) != 0u; + } + + uintptr_t GetTaggedSp() const { + return tagged_sp_; + } + + private: + explicit TaggedTopQuickFrame(uintptr_t tagged_sp) : tagged_sp_(tagged_sp) { } + + uintptr_t tagged_sp_; + }; + static_assert(sizeof(TaggedTopQuickFrame) == sizeof(uintptr_t), "TaggedTopQuickFrame size check"); + + TaggedTopQuickFrame tagged_top_quick_frame_; ManagedStack* link_; ShadowFrame* top_shadow_frame_; }; diff --git a/runtime/stack.cc b/runtime/stack.cc index ab9fb0d73f..5ad1f7c9c5 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -735,12 +735,19 @@ QuickMethodFrameInfo StackVisitor::GetCurrentQuickFrameInfo() const { return runtime->GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); } - // The only remaining case is if the method is native and uses the generic JNI stub. + // The only remaining case is if the method is native and uses the generic JNI stub, + // called either directly or through some (resolution, instrumentation) trampoline. DCHECK(method->IsNative()); - ClassLinker* class_linker = runtime->GetClassLinker(); - const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(method, - kRuntimePointerSize); - DCHECK(class_linker->IsQuickGenericJniStub(entry_point)) << method->PrettyMethod(); + if (kIsDebugBuild) { + ClassLinker* class_linker = runtime->GetClassLinker(); + const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(method, + kRuntimePointerSize); + CHECK(class_linker->IsQuickGenericJniStub(entry_point) || + // The current entrypoint (after filtering out trampolines) may have changed + // from GenericJNI to JIT-compiled stub since we have entered this frame. + (runtime->GetJit() != nullptr && + runtime->GetJit()->GetCodeCache()->ContainsPc(entry_point))) << method->PrettyMethod(); + } // Generic JNI frame. uint32_t handle_refs = GetNumberOfReferenceArgsWithoutReceiver(method) + 1; size_t scope_size = HandleScope::SizeOf(handle_refs); @@ -776,8 +783,48 @@ void StackVisitor::WalkStack(bool include_transitions) { // Can't be both a shadow and a quick fragment. DCHECK(current_fragment->GetTopShadowFrame() == nullptr); ArtMethod* method = *cur_quick_frame_; + DCHECK(method != nullptr); + bool header_retrieved = false; + if (method->IsNative()) { + // We do not have a PC for the first frame, so we cannot simply use + // ArtMethod::GetOatQuickMethodHeader() as we're unable to distinguish there + // between GenericJNI frame and JIT-compiled JNI stub; the entrypoint may have + // changed since the frame was entered. The top quick frame tag indicates + // GenericJNI here, otherwise it's either AOT-compiled or JNI-compiled JNI stub. + if (UNLIKELY(current_fragment->GetTopQuickFrameTag())) { + // The generic JNI does not have any method header. + cur_oat_quick_method_header_ = nullptr; + } else { + const void* existing_entry_point = method->GetEntryPointFromQuickCompiledCode(); + CHECK(existing_entry_point != nullptr); + Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + // Check whether we can quickly get the header from the current entrypoint. + if (!class_linker->IsQuickGenericJniStub(existing_entry_point) && + !class_linker->IsQuickResolutionStub(existing_entry_point) && + existing_entry_point != GetQuickInstrumentationEntryPoint()) { + cur_oat_quick_method_header_ = + OatQuickMethodHeader::FromEntryPoint(existing_entry_point); + } else { + const void* code = method->GetOatMethodQuickCode(class_linker->GetImagePointerSize()); + if (code != nullptr) { + cur_oat_quick_method_header_ = OatQuickMethodHeader::FromEntryPoint(code); + } else { + // This must be a JITted JNI stub frame. + CHECK(runtime->GetJit() != nullptr); + code = runtime->GetJit()->GetCodeCache()->GetJniStubCode(method); + CHECK(code != nullptr) << method->PrettyMethod(); + cur_oat_quick_method_header_ = OatQuickMethodHeader::FromCodePointer(code); + } + } + } + header_retrieved = true; + } while (method != nullptr) { - cur_oat_quick_method_header_ = method->GetOatQuickMethodHeader(cur_quick_frame_pc_); + if (!header_retrieved) { + cur_oat_quick_method_header_ = method->GetOatQuickMethodHeader(cur_quick_frame_pc_); + } + header_retrieved = false; // Force header retrieval in next iteration. SanityCheckFrame(); if ((walk_kind_ == StackWalkKind::kIncludeInlinedFrames) diff --git a/runtime/stack.h b/runtime/stack.h index bd6204f8d2..a16930bba0 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -140,8 +140,7 @@ class StackVisitor { }; template - void WalkStack(bool include_transitions = false) - REQUIRES_SHARED(Locks::mutator_lock_); + void WalkStack(bool include_transitions = false) REQUIRES_SHARED(Locks::mutator_lock_); Thread* GetThread() const { return thread_; diff --git a/runtime/thread.cc b/runtime/thread.cc index 712eabc888..bec1c908ad 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1884,9 +1884,7 @@ static bool ShouldShowNativeStack(const Thread* thread) } // Threads with no managed stack frames should be shown. - const ManagedStack* managed_stack = thread->GetManagedStack(); - if (managed_stack == nullptr || (managed_stack->GetTopQuickFrame() == nullptr && - managed_stack->GetTopShadowFrame() == nullptr)) { + if (!thread->HasManagedStack()) { return true; } diff --git a/runtime/thread.h b/runtime/thread.h index 39be66d5c2..0803975d26 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -474,13 +474,16 @@ class Thread { tlsPtr_.managed_stack.SetTopQuickFrame(top_method); } + void SetTopOfStackTagged(ArtMethod** top_method) { + tlsPtr_.managed_stack.SetTopQuickFrameTagged(top_method); + } + void SetTopOfShadowStack(ShadowFrame* top) { tlsPtr_.managed_stack.SetTopShadowFrame(top); } bool HasManagedStack() const { - return (tlsPtr_.managed_stack.GetTopQuickFrame() != nullptr) || - (tlsPtr_.managed_stack.GetTopShadowFrame() != nullptr); + return tlsPtr_.managed_stack.HasTopQuickFrame() || tlsPtr_.managed_stack.HasTopShadowFrame(); } // If 'msg' is null, no detail message is set. @@ -833,7 +836,7 @@ class Thread { static ThreadOffset TopOfManagedStackOffset() { return ThreadOffsetFromTlsPtr( OFFSETOF_MEMBER(tls_ptr_sized_values, managed_stack) + - ManagedStack::TopQuickFrameOffset()); + ManagedStack::TaggedTopQuickFrameOffset()); } const ManagedStack* GetManagedStack() const { diff --git a/test/655-jit-clinit/src/Main.java b/test/655-jit-clinit/src/Main.java index 44b315478f..2fb8f2a86e 100644 --- a/test/655-jit-clinit/src/Main.java +++ b/test/655-jit-clinit/src/Main.java @@ -23,7 +23,7 @@ public class Main { Foo.hotMethod(); } - public native static boolean isJitCompiled(Class cls, String methodName); + public native static boolean hasJitCompiledEntrypoint(Class cls, String methodName); private native static boolean hasJit(); } @@ -36,7 +36,7 @@ class Foo { static { array = new Object[10000]; - while (!Main.isJitCompiled(Foo.class, "hotMethod")) { + while (!Main.hasJitCompiledEntrypoint(Foo.class, "hotMethod")) { Foo.hotMethod(); try { // Sleep to give a chance for the JIT to compile `hotMethod`. diff --git a/test/667-jit-jni-stub/expected.txt b/test/667-jit-jni-stub/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/667-jit-jni-stub/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/667-jit-jni-stub/info.txt b/test/667-jit-jni-stub/info.txt new file mode 100644 index 0000000000..6f25c44592 --- /dev/null +++ b/test/667-jit-jni-stub/info.txt @@ -0,0 +1 @@ +Tests for JITting and collecting JNI stubs. diff --git a/test/667-jit-jni-stub/jit_jni_stub_test.cc b/test/667-jit-jni-stub/jit_jni_stub_test.cc new file mode 100644 index 0000000000..82e06fc018 --- /dev/null +++ b/test/667-jit-jni-stub/jit_jni_stub_test.cc @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#include + +#include "jit/jit.h" +#include "jit/jit_code_cache.h" +#include "mirror/class.h" +#include "mirror/string.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" + +namespace art { + +// Local class declared as a friend of JitCodeCache so that we can access its internals. +class JitJniStubTestHelper { + public: + static bool isNextJitGcFull(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { + CHECK(Runtime::Current()->GetJit() != nullptr); + jit::JitCodeCache* cache = Runtime::Current()->GetJit()->GetCodeCache(); + MutexLock mu(self, cache->lock_); + return cache->ShouldDoFullCollection(); + } +}; + +// Calls through to a static method with signature "()V". +extern "C" JNIEXPORT +void Java_Main_callThrough(JNIEnv* env, jclass, jclass klass, jstring methodName) { + ScopedObjectAccess soa(Thread::Current()); + std::string name = soa.Decode(methodName)->ToModifiedUtf8(); + jmethodID method = env->GetStaticMethodID(klass, name.c_str(), "()V"); + CHECK(method != nullptr) << soa.Decode(klass)->PrettyDescriptor() << "." << name; + env->CallStaticVoidMethod(klass, method); +} + +extern "C" JNIEXPORT +void Java_Main_jitGc(JNIEnv*, jclass) { + CHECK(Runtime::Current()->GetJit() != nullptr); + jit::JitCodeCache* cache = Runtime::Current()->GetJit()->GetCodeCache(); + ScopedObjectAccess soa(Thread::Current()); + cache->GarbageCollectCache(Thread::Current()); +} + +extern "C" JNIEXPORT +jboolean Java_Main_isNextJitGcFull(JNIEnv*, jclass) { + ScopedObjectAccess soa(Thread::Current()); + return JitJniStubTestHelper::isNextJitGcFull(soa.Self()); +} + +} // namespace art diff --git a/test/667-jit-jni-stub/run b/test/667-jit-jni-stub/run new file mode 100755 index 0000000000..f235c6bc90 --- /dev/null +++ b/test/667-jit-jni-stub/run @@ -0,0 +1,19 @@ +#!/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. + +# Disable AOT compilation of JNI stubs. +# Ensure this test is not subject to unexpected code collection. +${RUN} "${@}" --no-prebuild --no-dex2oat --runtime-option -Xjitinitialsize:32M diff --git a/test/667-jit-jni-stub/src/Main.java b/test/667-jit-jni-stub/src/Main.java new file mode 100644 index 0000000000..b867970eab --- /dev/null +++ b/test/667-jit-jni-stub/src/Main.java @@ -0,0 +1,180 @@ +/* + * 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 { + System.loadLibrary(args[0]); + if (isAotCompiled(Main.class, "hasJit")) { + throw new Error("This test must be run with --no-prebuild --no-dex2oat!"); + } + if (!hasJit()) { + return; + } + + testCompilationUseAndCollection(); + testMixedFramesOnStack(); + } + + public static void testCompilationUseAndCollection() { + // Test that callThrough() can be JIT-compiled. + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertFalse(hasJitCompiledCode(Main.class, "callThrough")); + ensureCompiledCallThroughEntrypoint(/* call */ true); + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + + // Use callThrough() once again now that the method has a JIT-compiled stub. + callThrough(Main.class, "doNothing"); + + // Test that GC with the JIT-compiled stub on the stack does not collect it. + // Also tests stack walk over the JIT-compiled stub. + callThrough(Main.class, "testGcWithCallThroughStubOnStack"); + + // Test that, when marking used methods before a full JIT GC, a single execution + // of the GenericJNI trampoline can save the compiled stub from being collected. + testSingleInvocationTriggersRecompilation(); + + // Test that the JNI compiled stub can actually be collected. + testStubCanBeCollected(); + } + + public static void testGcWithCallThroughStubOnStack() { + // Check that this method was called via JIT-compiled callThrough() stub. + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + // This assertion also exercises stack walk over the JIT-compiled callThrough() stub. + assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); + + doJitGcsUntilFullJitGcIsScheduled(); + // The callThrough() on the stack above this method is using the compiled stub, + // so the JIT GC should not remove the compiled code. + jitGc(); + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + } + + public static void testSingleInvocationTriggersRecompilation() { + // After scheduling a full JIT GC, single call through the GenericJNI + // trampoline should ensure that the compiled stub is used again. + doJitGcsUntilFullJitGcIsScheduled(); + callThrough(Main.class, "doNothing"); + ensureCompiledCallThroughEntrypoint(/* call */ false); // Wait for the compilation task to run. + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + jitGc(); // This JIT GC should not collect the callThrough() stub. + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + } + + public static void testMixedFramesOnStack() { + // Starts without a compiled JNI stub for callThrough(). + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertFalse(hasJitCompiledCode(Main.class, "callThrough")); + callThrough(Main.class, "testMixedFramesOnStackStage2"); + // We have just returned through the JIT-compiled JNI stub, so it must still + // be compiled (though not necessarily with the entrypoint pointing to it). + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + // Though the callThrough() is on the stack, that frame is using the GenericJNI + // and does not prevent the collection of the JNI stub. + testStubCanBeCollected(); + } + + public static void testMixedFramesOnStackStage2() { + // We cannot assert that callThrough() has no JIT compiled stub as that check + // may race against the compilation task. Just check the caller. + assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); + // Now ensure that the JNI stub is compiled and used. + ensureCompiledCallThroughEntrypoint(/* call */ true); + callThrough(Main.class, "testMixedFramesOnStackStage3"); + } + + public static void testMixedFramesOnStackStage3() { + // Check that this method was called via JIT-compiled callThrough() stub. + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + // This assertion also exercises stack walk over the JIT-compiled callThrough() stub. + assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); + // For a good measure, try a JIT GC. + jitGc(); + } + + public static void testStubCanBeCollected() { + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + doJitGcsUntilFullJitGcIsScheduled(); + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + jitGc(); // JIT GC without callThrough() on the stack should collect the callThrough() stub. + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertFalse(hasJitCompiledCode(Main.class, "callThrough")); + } + + public static void doJitGcsUntilFullJitGcIsScheduled() { + // We enter with a compiled stub for callThrough() but we also need the entrypoint to be set. + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + ensureCompiledCallThroughEntrypoint(/* call */ true); + // Perform JIT GC until the next GC is marked to do full collection. + do { + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + callThrough(Main.class, "jitGc"); // JIT GC with callThrough() safely on the stack. + } while (!isNextJitGcFull()); + // The JIT GC before the full collection resets entrypoints and waits to see + // if the methods are still in use. + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + } + + public static void ensureCompiledCallThroughEntrypoint(boolean call) { + int count = 0; + while (!hasJitCompiledEntrypoint(Main.class, "callThrough")) { + // If `call` is true, also exercise the `callThrough()` method to increase hotness. + int limit = call ? 1 << Math.min(count, 12) : 0; + for (int i = 0; i < limit; ++i) { + callThrough(Main.class, "doNothing"); + } + try { + // Sleep to give a chance for the JIT to compile `hasJit` stub. + Thread.sleep(100); + } catch (Exception e) { + // Ignore + } + if (++count == 50) { + throw new Error("TIMEOUT"); + } + }; + } + + public static void assertTrue(boolean value) { + if (!value) { + throw new AssertionError("Expected true!"); + } + } + + public static void assertFalse(boolean value) { + if (value) { + throw new AssertionError("Expected false!"); + } + } + + public static void doNothing() { } + public static void throwError() { throw new Error(); } + + // Note that the callThrough()'s shorty differs from shorties of the other + // native methods used in this test because of the return type `void.` + public native static void callThrough(Class cls, String methodName); + + public native static void jitGc(); + public native static boolean isNextJitGcFull(); + + public native static boolean isAotCompiled(Class cls, String methodName); + public native static boolean hasJitCompiledEntrypoint(Class cls, String methodName); + public native static boolean hasJitCompiledCode(Class cls, String methodName); + private native static boolean hasJit(); +} diff --git a/test/Android.bp b/test/Android.bp index ace62c23b8..01e424d5e3 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -385,6 +385,7 @@ cc_defaults { "656-annotation-lookup-generic-jni/test.cc", "661-oat-writer-layout/oat_writer_layout.cc", "664-aget-verifier/aget-verifier.cc", + "667-jit-jni-stub/jit_jni_stub_test.cc", "708-jit-cache-churn/jit.cc", ], shared_libs: [ diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index df497c1181..34580800cc 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -152,10 +152,10 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isAotCompiled(JNIEnv* env, return method->GetOatMethodQuickCode(kRuntimePointerSize) != nullptr; } -extern "C" JNIEXPORT jboolean JNICALL Java_Main_isJitCompiled(JNIEnv* env, - jclass, - jclass cls, - jstring method_name) { +extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledEntrypoint(JNIEnv* env, + jclass, + jclass cls, + jstring method_name) { jit::Jit* jit = GetJitIfEnabled(); if (jit == nullptr) { return false; @@ -169,6 +169,23 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isJitCompiled(JNIEnv* env, return jit->GetCodeCache()->ContainsPc(method->GetEntryPointFromQuickCompiledCode()); } +extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledCode(JNIEnv* env, + jclass, + jclass cls, + jstring method_name) { + jit::Jit* jit = GetJitIfEnabled(); + if (jit == nullptr) { + return false; + } + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + ScopedUtfChars chars(env, method_name); + CHECK(chars.c_str() != nullptr); + ArtMethod* method = soa.Decode(cls)->FindDeclaredDirectMethodByName( + chars.c_str(), kRuntimePointerSize); + return jit->GetCodeCache()->ContainsMethod(method); +} + extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env, jclass, jclass cls, -- GitLab From d12c3b01bab8d3190515e2ae02e8542518815245 Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Thu, 30 Nov 2017 18:03:15 +0000 Subject: [PATCH 100/226] Track Proxy change in libcore Track removal of two static methods from java.lang.reflect.Proxy. Bug: 35910877 Test: make test-art-host Change-Id: I23f069f2d4d29890cbea8b69f6a6cd1f80e826c3 --- runtime/class_linker.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index ccf431969a..e5bb7862cb 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -4515,7 +4515,7 @@ 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(), 23u); + CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->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. -- GitLab From 75bb2f3c85330b2aeba9e0a4a25f7eb059bcd754 Mon Sep 17 00:00:00 2001 From: Mingyao Yang Date: Thu, 30 Nov 2017 14:45:44 -0800 Subject: [PATCH 101/226] Type conversion elimination of constants A better way of eliminating type conversion for constants. Test: run-test on host. 711-checker-type-conversion. Change-Id: I457bc091542a5ac4cc4e77cadb012ee7cb040ce8 --- compiler/optimizing/constant_folding.cc | 2 +- compiler/optimizing/instruction_simplifier.cc | 10 ------ compiler/optimizing/nodes.cc | 16 +++++++++ .../711-checker-type-conversion/src/Main.java | 34 +++++++++++-------- 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc index bb586bf096..6f11e628ee 100644 --- a/compiler/optimizing/constant_folding.cc +++ b/compiler/optimizing/constant_folding.cc @@ -113,7 +113,7 @@ void HConstantFoldingVisitor::VisitBinaryOperation(HBinaryOperation* inst) { void HConstantFoldingVisitor::VisitTypeConversion(HTypeConversion* inst) { // Constant folding: replace `TypeConversion(a)' with a constant at // compile time if `a' is a constant. - HConstant* constant = inst->AsTypeConversion()->TryStaticEvaluation(); + HConstant* constant = inst->TryStaticEvaluation(); if (constant != nullptr) { inst->ReplaceWith(constant); inst->GetBlock()->RemoveInstruction(inst); diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index bd20d28992..7fa0c2be3d 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -1168,16 +1168,6 @@ void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruct RecordSimplification(); return; } - } else if (input->IsIntConstant()) { - // Try to eliminate type conversion on int constant whose value falls into - // the range of the result type. - int32_t value = input->AsIntConstant()->GetValue(); - if (DataType::IsTypeConversionImplicit(value, result_type)) { - instruction->ReplaceWith(input); - instruction->GetBlock()->RemoveInstruction(instruction); - RecordSimplification(); - return; - } } } diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index fa580d9bed..4a9da7ece1 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1403,6 +1403,14 @@ HConstant* HTypeConversion::TryStaticEvaluation() const { if (GetInput()->IsIntConstant()) { int32_t value = GetInput()->AsIntConstant()->GetValue(); switch (GetResultType()) { + case DataType::Type::kInt8: + return graph->GetIntConstant(static_cast(value), GetDexPc()); + case DataType::Type::kUint8: + return graph->GetIntConstant(static_cast(value), GetDexPc()); + case DataType::Type::kInt16: + return graph->GetIntConstant(static_cast(value), GetDexPc()); + case DataType::Type::kUint16: + return graph->GetIntConstant(static_cast(value), GetDexPc()); case DataType::Type::kInt64: return graph->GetLongConstant(static_cast(value), GetDexPc()); case DataType::Type::kFloat32: @@ -1415,6 +1423,14 @@ HConstant* HTypeConversion::TryStaticEvaluation() const { } else if (GetInput()->IsLongConstant()) { int64_t value = GetInput()->AsLongConstant()->GetValue(); switch (GetResultType()) { + case DataType::Type::kInt8: + return graph->GetIntConstant(static_cast(value), GetDexPc()); + case DataType::Type::kUint8: + return graph->GetIntConstant(static_cast(value), GetDexPc()); + case DataType::Type::kInt16: + return graph->GetIntConstant(static_cast(value), GetDexPc()); + case DataType::Type::kUint16: + return graph->GetIntConstant(static_cast(value), GetDexPc()); case DataType::Type::kInt32: return graph->GetIntConstant(static_cast(value), GetDexPc()); case DataType::Type::kFloat32: diff --git a/test/711-checker-type-conversion/src/Main.java b/test/711-checker-type-conversion/src/Main.java index 64ffcd2f1f..2c9c3a157e 100644 --- a/test/711-checker-type-conversion/src/Main.java +++ b/test/711-checker-type-conversion/src/Main.java @@ -22,20 +22,15 @@ public class Main { } } - /// CHECK-START: byte Main.getByte1() instruction_simplifier (before) + /// CHECK-START: byte Main.getByte1() constant_folding (before) /// CHECK: TypeConversion /// CHECK: TypeConversion /// CHECK: Add /// CHECK: TypeConversion - /// CHECK-START: byte Main.getByte1() instruction_simplifier (after) + /// CHECK-START: byte Main.getByte1() constant_folding (after) /// CHECK-NOT: TypeConversion - /// CHECK: Add - /// CHECK: TypeConversion - - /// CHECK-START: byte Main.getByte1() instruction_simplifier$before_codegen (after) /// CHECK-NOT: Add - /// CHECK-NOT: TypeConversion static byte getByte1() { int i = -2; @@ -43,20 +38,15 @@ public class Main { return (byte)((byte)i + (byte)j); } - /// CHECK-START: byte Main.getByte2() instruction_simplifier (before) + /// CHECK-START: byte Main.getByte2() constant_folding (before) /// CHECK: TypeConversion /// CHECK: TypeConversion /// CHECK: Add /// CHECK: TypeConversion - /// CHECK-START: byte Main.getByte2() instruction_simplifier (after) + /// CHECK-START: byte Main.getByte2() constant_folding (after) /// CHECK-NOT: TypeConversion - /// CHECK: Add - /// CHECK: TypeConversion - - /// CHECK-START: byte Main.getByte2() instruction_simplifier$before_codegen (after) /// CHECK-NOT: Add - /// CHECK: TypeConversion static byte getByte2() { int i = -100; @@ -64,8 +54,24 @@ public class Main { return (byte)((byte)i + (byte)j); } + /// CHECK-START: byte Main.getByte3() constant_folding (before) + /// CHECK: TypeConversion + /// CHECK: TypeConversion + /// CHECK: Add + /// CHECK: TypeConversion + + /// CHECK-START: byte Main.getByte2() constant_folding (after) + /// CHECK-NOT: TypeConversion + /// CHECK-NOT: Add + + static byte getByte3() { + long i = 0xabcdabcdabcdL; + return (byte)((byte)i + (byte)i); + } + public static void main(String[] args) { assertByteEquals(getByte1(), (byte)-5); assertByteEquals(getByte2(), (byte)(-201)); + assertByteEquals(getByte3(), (byte)(0xcd + 0xcd)); } } -- GitLab From 660be6ff67870d2512230b13effefdbc5abe43e4 Mon Sep 17 00:00:00 2001 From: Richard Uhler Date: Wed, 22 Nov 2017 16:12:29 +0000 Subject: [PATCH 102/226] Add VMDebug.getInstancesOfClasses API. The API can be used to iterate over instances of a given type on the heap. The GetInstancesOfClassesBenchmark run on bullhead shows the run time of the API grows linearly in the number of classes, C, to get instances of and the number of total instances, N, allocated on the heap. C N=~2^18 N=~2^19 1 13ms 21ms 2 26ms 43ms 4 53ms 87ms Bug: 69729799 Test: ./test/testrunner/testrunner.py -t 099-vmdebug -b --host Change-Id: Ied053d19760e656012e2577776f75a1cc0a14ac3 --- runtime/debugger.cc | 6 ++- runtime/gc/heap.cc | 25 ++++++---- runtime/gc/heap.h | 3 +- runtime/native/dalvik_system_VMDebug.cc | 48 +++++++++++++++++++ test/099-vmdebug/expected.txt | 6 +++ test/099-vmdebug/info.txt | 2 +- test/099-vmdebug/src/Main.java | 61 +++++++++++++++++++++++++ 7 files changed, 139 insertions(+), 12 deletions(-) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 1dcd935eea..13029fb958 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -963,7 +963,11 @@ JDWP::JdwpError Dbg::GetInstances(JDWP::RefTypeId class_id, int32_t max_count, } VariableSizedHandleScope hs(Thread::Current()); std::vector> raw_instances; - Runtime::Current()->GetHeap()->GetInstances(hs, hs.NewHandle(c), max_count, raw_instances); + Runtime::Current()->GetHeap()->GetInstances(hs, + hs.NewHandle(c), + /* use_is_assignable_from */ false, + max_count, + raw_instances); for (size_t i = 0; i < raw_instances.size(); ++i) { instances->push_back(gRegistry->Add(raw_instances[i].Get())); } diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 9f6266612a..f29ae92e2d 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -1796,19 +1796,25 @@ uint64_t Heap::GetBytesAllocatedEver() const { return GetBytesFreedEver() + GetBytesAllocated(); } +// Check whether the given object is an instance of the given class. +static bool MatchesClass(mirror::Object* obj, + Handle h_class, + bool use_is_assignable_from) REQUIRES_SHARED(Locks::mutator_lock_) { + mirror::Class* instance_class = obj->GetClass(); + CHECK(instance_class != nullptr); + ObjPtr klass = h_class.Get(); + if (use_is_assignable_from) { + return klass != nullptr && klass->IsAssignableFrom(instance_class); + } + return instance_class == klass; +} + void Heap::CountInstances(const std::vector>& classes, bool use_is_assignable_from, uint64_t* counts) { auto instance_counter = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::Class* instance_class = obj->GetClass(); - CHECK(instance_class != nullptr); for (size_t i = 0; i < classes.size(); ++i) { - ObjPtr klass = classes[i].Get(); - if (use_is_assignable_from) { - if (klass != nullptr && klass->IsAssignableFrom(instance_class)) { - ++counts[i]; - } - } else if (instance_class == klass) { + if (MatchesClass(obj, classes[i], use_is_assignable_from)) { ++counts[i]; } } @@ -1818,11 +1824,12 @@ void Heap::CountInstances(const std::vector>& classes, void Heap::GetInstances(VariableSizedHandleScope& scope, Handle h_class, + bool use_is_assignable_from, int32_t max_count, std::vector>& instances) { DCHECK_GE(max_count, 0); auto instance_collector = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { - if (obj->GetClass() == h_class.Get()) { + if (MatchesClass(obj, h_class, use_is_assignable_from)) { if (max_count == 0 || instances.size() < static_cast(max_count)) { instances.push_back(scope.NewHandle(obj)); } diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 4d7424c7ef..ac0d82e12a 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -346,9 +346,10 @@ class Heap { REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - // Implements JDWP RT_Instances. + // Implements VMDebug.getInstancesOfClasses and JDWP RT_Instances. void GetInstances(VariableSizedHandleScope& scope, Handle c, + bool use_is_assignable_from, int32_t max_count, std::vector>& instances) REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_) diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 2663bea344..88a78ab4be 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -319,6 +319,53 @@ static jlongArray VMDebug_countInstancesOfClasses(JNIEnv* env, return soa.AddLocalReference(long_counts); } +static jobjectArray VMDebug_getInstancesOfClasses(JNIEnv* env, + jclass, + jobjectArray javaClasses, + jboolean includeAssignable) { + ScopedObjectAccess soa(env); + StackHandleScope<2> hs(soa.Self()); + Handle> classes = hs.NewHandle( + soa.Decode>(javaClasses)); + if (classes == nullptr) { + return nullptr; + } + + jclass object_array_class = env->FindClass("[Ljava/lang/Object;"); + if (env->ExceptionCheck() == JNI_TRUE) { + return nullptr; + } + CHECK(object_array_class != nullptr); + + size_t num_classes = classes->GetLength(); + jobjectArray result = env->NewObjectArray(num_classes, object_array_class, nullptr); + if (env->ExceptionCheck() == JNI_TRUE) { + return nullptr; + } + + gc::Heap* const heap = Runtime::Current()->GetHeap(); + MutableHandle h_class(hs.NewHandle(nullptr)); + for (size_t i = 0; i < num_classes; ++i) { + h_class.Assign(classes->Get(i)); + + VariableSizedHandleScope hs2(soa.Self()); + std::vector> raw_instances; + heap->GetInstances(hs2, h_class, includeAssignable, /* max_count */ 0, raw_instances); + jobjectArray array = env->NewObjectArray(raw_instances.size(), + WellKnownClasses::java_lang_Object, + nullptr); + if (env->ExceptionCheck() == JNI_TRUE) { + return nullptr; + } + + for (size_t j = 0; j < raw_instances.size(); ++j) { + env->SetObjectArrayElement(array, j, raw_instances[j].ToJObject()); + } + env->SetObjectArrayElement(result, i, array); + } + return result; +} + // We export the VM internal per-heap-space size/alloc/free metrics // for the zygote space, alloc space (application heap), and the large // object space for dumpsys meminfo. The other memory region data such @@ -534,6 +581,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"), NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"), NATIVE_METHOD(VMDebug, getHeapSpaceStats, "([J)V"), + NATIVE_METHOD(VMDebug, getInstancesOfClasses, "([Ljava/lang/Class;Z)[[Ljava/lang/Object;"), NATIVE_METHOD(VMDebug, getInstructionCount, "([I)V"), FAST_NATIVE_METHOD(VMDebug, getLoadedClassCount, "()I"), NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"), diff --git a/test/099-vmdebug/expected.txt b/test/099-vmdebug/expected.txt index b8d72f66f8..f7801de62f 100644 --- a/test/099-vmdebug/expected.txt +++ b/test/099-vmdebug/expected.txt @@ -23,3 +23,9 @@ Instances of null 0 Instances of ClassA assignable 3 Array counts [2, 1, 0] Array counts assignable [3, 1, 0] +ClassD got 3, combined mask: 13 +ClassE got 2, combined mask: 18 +null got 0 +ClassD assignable got 5, combined mask: 31 +ClassE assignable got 2, combined mask: 18 +null assignable got 0 diff --git a/test/099-vmdebug/info.txt b/test/099-vmdebug/info.txt index 7f88086986..873429e076 100644 --- a/test/099-vmdebug/info.txt +++ b/test/099-vmdebug/info.txt @@ -1 +1 @@ -Tests of private dalvik.system.VMDebug support for method tracing. +Tests of dalvik.system.VMDebug APIs. diff --git a/test/099-vmdebug/src/Main.java b/test/099-vmdebug/src/Main.java index 90ad3155ca..e0d829a0d6 100644 --- a/test/099-vmdebug/src/Main.java +++ b/test/099-vmdebug/src/Main.java @@ -33,6 +33,7 @@ public class Main { } testMethodTracing(); testCountInstances(); + testGetInstances(); testRuntimeStat(); testRuntimeStats(); } @@ -249,6 +250,59 @@ public class Main { System.out.println("Array counts assignable " + Arrays.toString(counts)); } + static class ClassD { + public int mask; + + public ClassD(int mask) { + this.mask = mask; + } + } + + static class ClassE extends ClassD { + public ClassE(int mask) { + super(mask); + } + } + + private static void testGetInstances() throws Exception { + ArrayList l = new ArrayList(); + l.add(new ClassD(0x01)); + l.add(new ClassE(0x02)); + l.add(new ClassD(0x04)); + l.add(new ClassD(0x08)); + l.add(new ClassE(0x10)); + Runtime.getRuntime().gc(); + Class[] classes = new Class[] {ClassD.class, ClassE.class, null}; + Object[][] instances = VMDebug.getInstancesOfClasses(classes, false); + + int mask = 0; + for (Object instance : instances[0]) { + mask |= ((ClassD)instance).mask; + } + System.out.println("ClassD got " + instances[0].length + ", combined mask: " + mask); + + mask = 0; + for (Object instance : instances[1]) { + mask |= ((ClassD)instance).mask; + } + System.out.println("ClassE got " + instances[1].length + ", combined mask: " + mask); + System.out.println("null got " + instances[2].length); + + instances = VMDebug.getInstancesOfClasses(classes, true); + mask = 0; + for (Object instance : instances[0]) { + mask |= ((ClassD)instance).mask; + } + System.out.println("ClassD assignable got " + instances[0].length + ", combined mask: " + mask); + + mask = 0; + for (Object instance : instances[1]) { + mask |= ((ClassD)instance).mask; + } + System.out.println("ClassE assignable got " + instances[1].length + ", combined mask: " + mask); + System.out.println("null assignable got " + instances[2].length); + } + private static class VMDebug { private static final Method startMethodTracingMethod; private static final Method stopMethodTracingMethod; @@ -257,6 +311,7 @@ public class Main { private static final Method getRuntimeStatsMethod; private static final Method countInstancesOfClassMethod; private static final Method countInstancesOfClassesMethod; + private static final Method getInstancesOfClassesMethod; static { try { Class c = Class.forName("dalvik.system.VMDebug"); @@ -270,6 +325,8 @@ public class Main { Class.class, Boolean.TYPE); countInstancesOfClassesMethod = c.getDeclaredMethod("countInstancesOfClasses", Class[].class, Boolean.TYPE); + getInstancesOfClassesMethod = c.getDeclaredMethod("getInstancesOfClasses", + Class[].class, Boolean.TYPE); } catch (Exception e) { throw new RuntimeException(e); } @@ -300,5 +357,9 @@ public class Main { return (long[]) countInstancesOfClassesMethod.invoke( null, new Object[]{classes, assignable}); } + public static Object[][] getInstancesOfClasses(Class[] classes, boolean assignable) throws Exception { + return (Object[][]) getInstancesOfClassesMethod.invoke( + null, new Object[]{classes, assignable}); + } } } -- GitLab From 8bb72b6fe4a566692fbe57dbfd86c133a67f28fc Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 30 Nov 2017 17:09:50 +0000 Subject: [PATCH 103/226] ART: Fix crash accessing .bss for non-executable oat file. There are situations where we're running and JIT-compiling bytecode from a dex file tied to a non-executable oat file (i.e. its AOT-compiled code cannot be executed) and the JIT-compiled code can try to store the resolved class or string in its .bss section which has not been mmapped. Check whether the oat file is executable before doing the .bss store. Note that clearing the pointers to index bss mappings when loading the oat file as non-executable would also solve the problem. However, that would prevent oatdump from listing .bss entries. Test: No regression test provided, oat files are loaded as non-executable only in some odd situations that are very difficult to replicate in a test. Bug: 69928566 Change-Id: Ib91d22f6510dc0d7cc9abe8247f2743706aaa45e --- runtime/entrypoints/quick/quick_dexcache_entrypoints.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index f756312983..238ada94ff 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -41,6 +41,12 @@ static void StoreObjectInBss(ArtMethod* outer_method, static_assert(sizeof(GcRoot) == sizeof(GcRoot), "Size check."); DCHECK_NE(bss_offset, IndexBssMappingLookup::npos); DCHECK_ALIGNED(bss_offset, sizeof(GcRoot)); + if (UNLIKELY(!oat_file->IsExecutable())) { + // There are situations where we execute bytecode tied to an oat file opened + // as non-executable (i.e. the AOT-compiled code cannot be executed) and we + // can JIT that bytecode and get here without the .bss being mmapped. + return; + } GcRoot* slot = reinterpret_cast*>( const_cast(oat_file->BssBegin() + bss_offset)); DCHECK_GE(slot, oat_file->GetBssGcRoots().data()); -- GitLab From 54149772111907eb88a6c74b0cbb43c51c503a6b Mon Sep 17 00:00:00 2001 From: Goran Jakovljevic Date: Fri, 1 Dec 2017 13:27:09 +0100 Subject: [PATCH 104/226] Fix MIPS32 mterp Include .h instead of non-existing .S file. Also add .cfi_startproc and .cfi_endproc directives for ExecuteMterpImpl. This fixes aosp_mips-eng build. Test: successful aosp_mips-eng build Change-Id: Ifc7860cbb4155332ff5f6867824bc8e36ae3b6ae --- runtime/interpreter/mterp/mips/entry.S | 1 + runtime/interpreter/mterp/mips/footer.S | 1 + runtime/interpreter/mterp/mips/header.S | 2 +- runtime/interpreter/mterp/out/mterp_mips.S | 4 +++- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/runtime/interpreter/mterp/mips/entry.S b/runtime/interpreter/mterp/mips/entry.S index 3908cb506e..41b5d5650d 100644 --- a/runtime/interpreter/mterp/mips/entry.S +++ b/runtime/interpreter/mterp/mips/entry.S @@ -32,6 +32,7 @@ */ ExecuteMterpImpl: + .cfi_startproc .set noreorder .cpload t9 .set reorder diff --git a/runtime/interpreter/mterp/mips/footer.S b/runtime/interpreter/mterp/mips/footer.S index 6e1ba1c882..1c784ef188 100644 --- a/runtime/interpreter/mterp/mips/footer.S +++ b/runtime/interpreter/mterp/mips/footer.S @@ -284,4 +284,5 @@ MterpProfileActive: STACK_LOAD_FULL() jalr zero, ra + .cfi_endproc .end ExecuteMterpImpl diff --git a/runtime/interpreter/mterp/mips/header.S b/runtime/interpreter/mterp/mips/header.S index 1ccaa6443f..0f7a6f1116 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.S" +#include "interpreter/mterp/cfi_asm_support.h" #if (__mips==32) && (__mips_isa_rev>=2) #define MIPS32REVGE2 /* mips32r2 and greater */ diff --git a/runtime/interpreter/mterp/out/mterp_mips.S b/runtime/interpreter/mterp/out/mterp_mips.S index 9535e254e7..1687afa58a 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.S" +#include "interpreter/mterp/cfi_asm_support.h" #if (__mips==32) && (__mips_isa_rev>=2) #define MIPS32REVGE2 /* mips32r2 and greater */ @@ -766,6 +766,7 @@ */ ExecuteMterpImpl: + .cfi_startproc .set noreorder .cpload t9 .set reorder @@ -12844,5 +12845,6 @@ MterpProfileActive: STACK_LOAD_FULL() jalr zero, ra + .cfi_endproc .end ExecuteMterpImpl -- GitLab From dbb9aef046301940d0b253c918a5c78b277330ba Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 23 Nov 2017 10:44:11 +0000 Subject: [PATCH 105/226] Log at places we fail to compile. Useful when diagnosing some compiler issues / limitations. Test: test.py Change-Id: I8759d0e78b0682b300ddcadfe02793432cab2036 --- compiler/optimizing/block_builder.cc | 2 ++ compiler/optimizing/instruction_builder.cc | 26 +++++++++++----------- compiler/optimizing/instruction_builder.h | 4 ++-- compiler/optimizing/nodes.cc | 1 + compiler/optimizing/ssa_builder.cc | 4 ++++ 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/compiler/optimizing/block_builder.cc b/compiler/optimizing/block_builder.cc index a6687fe258..ed000327ff 100644 --- a/compiler/optimizing/block_builder.cc +++ b/compiler/optimizing/block_builder.cc @@ -58,6 +58,7 @@ bool HBasicBlockBuilder::CreateBranchTargets() { // cannot have any code afterwards. } else { // The TryItem spans beyond the end of the CodeItem. This is invalid code. + VLOG(compiler) << "Not compiled: TryItem spans beyond the end of the CodeItem"; return false; } } @@ -110,6 +111,7 @@ bool HBasicBlockBuilder::CreateBranchTargets() { if (next == instructions.end()) { // In the normal case we should never hit this but someone can artificially forge a dex // file to fall-through out the method code. In this case we bail out compilation. + VLOG(compiler) << "Not compiled: Fall-through beyond the CodeItem"; return false; } MaybeCreateBlockAt(next.DexPc()); diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 61840cc20f..214b2539e0 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1349,6 +1349,8 @@ bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instructio uint16_t field_index; if (instruction.IsQuickened()) { if (!CanDecodeQuickenedInfo()) { + VLOG(compiler) << "Not compiled: Could not decode quickened instruction " + << instruction.Opcode(); return false; } field_index = LookupQuickenedInfo(quicken_index); @@ -1485,7 +1487,6 @@ ArtField* HInstructionBuilder::ResolveField(uint16_t field_idx, bool is_static, dex_compilation_unit_->GetDexCache(), class_loader, is_static); - if (UNLIKELY(resolved_field == nullptr)) { // Clean up any exception left by type resolution. soa.Self()->ClearException(); @@ -1521,7 +1522,7 @@ ArtField* HInstructionBuilder::ResolveField(uint16_t field_idx, bool is_static, return resolved_field; } -bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, +void HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put) { uint32_t source_or_dest_reg = instruction.VRegA_21c(); @@ -1535,7 +1536,7 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, MethodCompilationStat::kUnresolvedField); DataType::Type field_type = GetFieldAccessType(*dex_file_, field_index); BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type); - return true; + return; } DataType::Type field_type = GetFieldAccessType(*dex_file_, field_index); @@ -1553,7 +1554,7 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, MaybeRecordStat(compilation_stats_, MethodCompilationStat::kUnresolvedFieldNotAFastAccess); BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type); - return true; + return; } HInstruction* cls = constant; @@ -1589,7 +1590,6 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, dex_pc)); UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction()); } - return true; } void HInstructionBuilder::BuildCheckedDivRem(uint16_t out_vreg, @@ -2056,6 +2056,8 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, uint16_t method_idx; if (instruction.Opcode() == Instruction::INVOKE_VIRTUAL_QUICK) { if (!CanDecodeQuickenedInfo()) { + VLOG(compiler) << "Not compiled: Could not decode quickened instruction " + << instruction.Opcode(); return false; } method_idx = LookupQuickenedInfo(quicken_index); @@ -2081,6 +2083,8 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, uint16_t method_idx; if (instruction.Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK) { if (!CanDecodeQuickenedInfo()) { + VLOG(compiler) << "Not compiled: Could not decode quickened instruction " + << instruction.Opcode(); return false; } method_idx = LookupQuickenedInfo(quicken_index); @@ -2756,7 +2760,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::IGET_CHAR_QUICK: case Instruction::IGET_SHORT: case Instruction::IGET_SHORT_QUICK: { - if (!BuildInstanceFieldAccess(instruction, dex_pc, false, quicken_index)) { + if (!BuildInstanceFieldAccess(instruction, dex_pc, /* is_put */ false, quicken_index)) { return false; } break; @@ -2776,7 +2780,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::IPUT_CHAR_QUICK: case Instruction::IPUT_SHORT: case Instruction::IPUT_SHORT_QUICK: { - if (!BuildInstanceFieldAccess(instruction, dex_pc, true, quicken_index)) { + if (!BuildInstanceFieldAccess(instruction, dex_pc, /* is_put */ true, quicken_index)) { return false; } break; @@ -2789,9 +2793,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::SGET_BYTE: case Instruction::SGET_CHAR: case Instruction::SGET_SHORT: { - if (!BuildStaticFieldAccess(instruction, dex_pc, false)) { - return false; - } + BuildStaticFieldAccess(instruction, dex_pc, /* is_put */ false); break; } @@ -2802,9 +2804,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::SPUT_BYTE: case Instruction::SPUT_CHAR: case Instruction::SPUT_SHORT: { - if (!BuildStaticFieldAccess(instruction, dex_pc, true)) { - return false; - } + BuildStaticFieldAccess(instruction, dex_pc, /* is_put */ true); break; } diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index f551ac4280..2446ddb86a 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -175,8 +175,8 @@ class HInstructionBuilder : public ValueObject { uint32_t dex_pc, bool is_put, DataType::Type field_type); - // Builds a static field access node and returns whether the instruction is supported. - bool BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put); + // Builds a static field access node. + void BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put); void BuildArrayAccess(const Instruction& instruction, uint32_t dex_pc, diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index fa580d9bed..ff4e9aa510 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -507,6 +507,7 @@ GraphAnalysisResult HGraph::AnalyzeLoops() const { if (block->IsCatchBlock()) { // TODO: Dealing with exceptional back edges could be tricky because // they only approximate the real control flow. Bail out for now. + VLOG(compiler) << "Not compiled: Exceptional back edges"; return kAnalysisFailThrowCatchLoop; } block->GetLoopInformation()->Populate(); diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index e4edbfdc24..cb384768b7 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -328,6 +328,8 @@ bool SsaBuilder::FixAmbiguousArrayOps() { HInstruction* array = aget_int->GetArray(); if (!array->GetReferenceTypeInfo().IsPrimitiveArrayClass()) { // RTP did not type the input array. Bail. + VLOG(compiler) << "Not compiled: Could not infer an array type for array operation at " + << aget_int->GetDexPc(); return false; } @@ -368,6 +370,8 @@ bool SsaBuilder::FixAmbiguousArrayOps() { HInstruction* array = aset->GetArray(); if (!array->GetReferenceTypeInfo().IsPrimitiveArrayClass()) { // RTP did not type the input array. Bail. + VLOG(compiler) << "Not compiled: Could not infer an array type for array operation at " + << aset->GetDexPc(); return false; } -- GitLab From dbd4303b0da3bd30f53479c14ef541441c8d01f7 Mon Sep 17 00:00:00 2001 From: Goran Jakovljevic Date: Wed, 15 Nov 2017 16:31:56 +0100 Subject: [PATCH 106/226] MIPS: Improve BoundsCheck for constant inputs Note: All tests were executed on CI20 (MIPS32R2) and in QEMU (MIPS32R6 and MIPS64R6). Test: ./testrunner.py --optimizing --target Test: mma test-art-target-gtest Change-Id: I012fb1013af43d5669a9b0080d481da28ffa7ef2 --- compiler/optimizing/code_generator_mips.cc | 93 +++++++++++++++++--- compiler/optimizing/code_generator_mips64.cc | 93 +++++++++++++++++--- 2 files changed, 162 insertions(+), 24 deletions(-) diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index d6922d2f3f..6376f03b26 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -3103,23 +3103,92 @@ void LocationsBuilderMIPS::VisitBoundsCheck(HBoundsCheck* instruction) { caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1))); LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + + HInstruction* index = instruction->InputAt(0); + HInstruction* length = instruction->InputAt(1); + + bool const_index = false; + bool const_length = false; + + if (index->IsConstant()) { + if (length->IsConstant()) { + const_index = true; + const_length = true; + } else { + int32_t index_value = index->AsIntConstant()->GetValue(); + if (index_value < 0 || IsInt<16>(index_value + 1)) { + const_index = true; + } + } + } else if (length->IsConstant()) { + int32_t length_value = length->AsIntConstant()->GetValue(); + if (IsUint<15>(length_value)) { + const_length = true; + } + } + + locations->SetInAt(0, const_index + ? Location::ConstantLocation(index->AsConstant()) + : Location::RequiresRegister()); + locations->SetInAt(1, const_length + ? Location::ConstantLocation(length->AsConstant()) + : Location::RequiresRegister()); } void InstructionCodeGeneratorMIPS::VisitBoundsCheck(HBoundsCheck* instruction) { LocationSummary* locations = instruction->GetLocations(); - BoundsCheckSlowPathMIPS* slow_path = - new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS(instruction); - codegen_->AddSlowPath(slow_path); - - Register index = locations->InAt(0).AsRegister(); - Register length = locations->InAt(1).AsRegister(); + Location index_loc = locations->InAt(0); + Location length_loc = locations->InAt(1); + + if (length_loc.IsConstant()) { + int32_t length = length_loc.GetConstant()->AsIntConstant()->GetValue(); + if (index_loc.IsConstant()) { + int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue(); + if (index < 0 || index >= length) { + BoundsCheckSlowPathMIPS* slow_path = + new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS(instruction); + codegen_->AddSlowPath(slow_path); + __ B(slow_path->GetEntryLabel()); + } else { + // Nothing to be done. + } + return; + } - // length is limited by the maximum positive signed 32-bit integer. - // Unsigned comparison of length and index checks for index < 0 - // and for length <= index simultaneously. - __ Bgeu(index, length, slow_path->GetEntryLabel()); + BoundsCheckSlowPathMIPS* slow_path = + new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS(instruction); + codegen_->AddSlowPath(slow_path); + Register index = index_loc.AsRegister(); + if (length == 0) { + __ B(slow_path->GetEntryLabel()); + } else if (length == 1) { + __ Bnez(index, slow_path->GetEntryLabel()); + } else { + DCHECK(IsUint<15>(length)) << length; + __ Sltiu(TMP, index, length); + __ Beqz(TMP, slow_path->GetEntryLabel()); + } + } else { + Register length = length_loc.AsRegister(); + BoundsCheckSlowPathMIPS* slow_path = + new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS(instruction); + codegen_->AddSlowPath(slow_path); + if (index_loc.IsConstant()) { + int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue(); + if (index < 0) { + __ B(slow_path->GetEntryLabel()); + } else if (index == 0) { + __ Blez(length, slow_path->GetEntryLabel()); + } else { + DCHECK(IsInt<16>(index + 1)) << index; + __ Sltiu(TMP, length, index + 1); + __ Bnez(TMP, slow_path->GetEntryLabel()); + } + } else { + Register index = index_loc.AsRegister(); + __ Bgeu(index, length, slow_path->GetEntryLabel()); + } + } } // Temp is used for read barrier. diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index ee33b3f335..03a719f445 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -2614,23 +2614,92 @@ void LocationsBuilderMIPS64::VisitBoundsCheck(HBoundsCheck* instruction) { caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1))); LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + + HInstruction* index = instruction->InputAt(0); + HInstruction* length = instruction->InputAt(1); + + bool const_index = false; + bool const_length = false; + + if (index->IsConstant()) { + if (length->IsConstant()) { + const_index = true; + const_length = true; + } else { + int32_t index_value = index->AsIntConstant()->GetValue(); + if (index_value < 0 || IsInt<16>(index_value + 1)) { + const_index = true; + } + } + } else if (length->IsConstant()) { + int32_t length_value = length->AsIntConstant()->GetValue(); + if (IsUint<15>(length_value)) { + const_length = true; + } + } + + locations->SetInAt(0, const_index + ? Location::ConstantLocation(index->AsConstant()) + : Location::RequiresRegister()); + locations->SetInAt(1, const_length + ? Location::ConstantLocation(length->AsConstant()) + : Location::RequiresRegister()); } void InstructionCodeGeneratorMIPS64::VisitBoundsCheck(HBoundsCheck* instruction) { LocationSummary* locations = instruction->GetLocations(); - BoundsCheckSlowPathMIPS64* slow_path = - new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS64(instruction); - codegen_->AddSlowPath(slow_path); - - GpuRegister index = locations->InAt(0).AsRegister(); - GpuRegister length = locations->InAt(1).AsRegister(); + Location index_loc = locations->InAt(0); + Location length_loc = locations->InAt(1); + + if (length_loc.IsConstant()) { + int32_t length = length_loc.GetConstant()->AsIntConstant()->GetValue(); + if (index_loc.IsConstant()) { + int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue(); + if (index < 0 || index >= length) { + BoundsCheckSlowPathMIPS64* slow_path = + new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS64(instruction); + codegen_->AddSlowPath(slow_path); + __ Bc(slow_path->GetEntryLabel()); + } else { + // Nothing to be done. + } + return; + } - // length is limited by the maximum positive signed 32-bit integer. - // Unsigned comparison of length and index checks for index < 0 - // and for length <= index simultaneously. - __ Bgeuc(index, length, slow_path->GetEntryLabel()); + BoundsCheckSlowPathMIPS64* slow_path = + new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS64(instruction); + codegen_->AddSlowPath(slow_path); + GpuRegister index = index_loc.AsRegister(); + if (length == 0) { + __ Bc(slow_path->GetEntryLabel()); + } else if (length == 1) { + __ Bnezc(index, slow_path->GetEntryLabel()); + } else { + DCHECK(IsUint<15>(length)) << length; + __ Sltiu(TMP, index, length); + __ Beqzc(TMP, slow_path->GetEntryLabel()); + } + } else { + GpuRegister length = length_loc.AsRegister(); + BoundsCheckSlowPathMIPS64* slow_path = + new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS64(instruction); + codegen_->AddSlowPath(slow_path); + if (index_loc.IsConstant()) { + int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue(); + if (index < 0) { + __ Bc(slow_path->GetEntryLabel()); + } else if (index == 0) { + __ Blezc(length, slow_path->GetEntryLabel()); + } else { + DCHECK(IsInt<16>(index + 1)) << index; + __ Sltiu(TMP, length, index + 1); + __ Bnezc(TMP, slow_path->GetEntryLabel()); + } + } else { + GpuRegister index = index_loc.AsRegister(); + __ Bgeuc(index, length, slow_path->GetEntryLabel()); + } + } } // Temp is used for read barrier. -- GitLab From 7ee76e6504804e80df95d79e18988cfec400d909 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Thu, 30 Nov 2017 16:39:28 -0800 Subject: [PATCH 107/226] Add TestVMDebug to prebuilt libjdwp skips This test relies on functionality that is not implemented in the prebuilt libjdwp. Test: ./art/tools/run-prebuilt-libjdwp-tests.sh --mode=host Bug: 62821960 Change-Id: I83dc8ce3ebbdaafbc8563a7496f662800424e92a --- tools/libjdwp_art_failures.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/libjdwp_art_failures.txt b/tools/libjdwp_art_failures.txt index fd711bbd8b..66842c3251 100644 --- a/tools/libjdwp_art_failures.txt +++ b/tools/libjdwp_art_failures.txt @@ -64,6 +64,11 @@ "org.apache.harmony.jpda.tests.jdwp.EventModifiers.InstanceOnlyModifierTest#testMethodExit", "org.apache.harmony.jpda.tests.jdwp.EventModifiers.InstanceOnlyModifierTest#testMethodExitWithReturnValue" ] }, +{ + description: "Tests for VMDebug functionality not implemented in the upstream libjdwp", + result: EXEC_FAILED, + name: "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest#testVMDebug" +}, /* TODO Categorize these failures more. */ { description: "Tests that fail on both ART and RI. These tests are likely incorrect", -- GitLab From 7919db947bc41f6f5d194c50b88d0cba47319a1c Mon Sep 17 00:00:00 2001 From: Alex Light Date: Wed, 29 Nov 2017 09:00:55 -0800 Subject: [PATCH 108/226] Speed up MterpShouldSwitchInterpreters check We were often performing a pair of TLS reads in order to determine in the current thread has any pending asynchronous exceptions (exceptions thrown by the JVMTI StopThread function). This is quite slow and was impacting some benchmarks. Since it is expected that asynchronous exceptions are extremely rare we will first check to see if any asynchronous exceptions have been sent on the current process. Only if at least one asynchronous exception has been thrown will we do the expensive TLS lookups to determine if one has been thrown on the current thread. Using a global instance value without synchronization or atomics is ok here since the checkpoint that actually sets the async_exception_thrown flag provides synchronization by either occurring on the target thread or passing the checkpoint. According to go/lem this gives us a 7% increase on the caffeine string benchmark. Test: go/lem runs Test: ./test.py --host -j50 Bug: 68010816 Change-Id: I62684a5b3a7fc7cc600f5efd2a2393d9c4025917 --- runtime/interpreter/mterp/mterp.cc | 10 ++++++++-- runtime/runtime.cc | 1 + runtime/runtime.h | 12 ++++++++++++ runtime/thread.cc | 1 + 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 92dd19ed2f..987298bd8a 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -151,8 +151,14 @@ extern "C" size_t MterpShouldSwitchInterpreters() Dbg::IsDebuggerActive() || // An async exception has been thrown. We need to go to the switch interpreter. MTerp doesn't // know how to deal with these so we could end up never dealing with it if we are in an - // infinite loop. - UNLIKELY(Thread::Current()->IsAsyncExceptionPending()); + // infinite loop. Since this can be called in a tight loop and getting the current thread + // requires a TLS read we instead first check a short-circuit runtime flag that will only be + // set if something tries to set an async exception. This will make this function faster in + // the common case where no async exception has ever been sent. We don't need to worry about + // synchronization on the runtime flag since it is only set in a checkpoint which will either + // take place on the current thread or act as a synchronization point. + (UNLIKELY(runtime->AreAsyncExceptionsThrown()) && + Thread::Current()->IsAsyncExceptionPending()); } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index f09b6c9825..d15de38b0a 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -256,6 +256,7 @@ Runtime::Runtime() force_native_bridge_(false), is_native_bridge_loaded_(false), is_native_debuggable_(false), + async_exceptions_thrown_(false), is_java_debuggable_(false), zygote_max_failed_boots_(0), experimental_flags_(ExperimentalFlags::kNone), diff --git a/runtime/runtime.h b/runtime/runtime.h index 6b01cc220f..476b71f169 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -586,6 +586,14 @@ class Runtime { is_native_debuggable_ = value; } + bool AreAsyncExceptionsThrown() const { + return async_exceptions_thrown_; + } + + void SetAsyncExceptionsThrown() { + async_exceptions_thrown_ = true; + } + // Returns the build fingerprint, if set. Otherwise an empty string is returned. std::string GetFingerprint() { return fingerprint_; @@ -899,6 +907,10 @@ class Runtime { // Whether we are running under native debugger. bool is_native_debuggable_; + // whether or not any async exceptions have ever been thrown. This is used to speed up the + // MterpShouldSwitchInterpreters function. + bool async_exceptions_thrown_; + // Whether Java code needs to be debuggable. bool is_java_debuggable_; diff --git a/runtime/thread.cc b/runtime/thread.cc index bec1c908ad..cb350edb5f 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -3700,6 +3700,7 @@ void Thread::DeoptimizeWithDeoptimizationException(JValue* result) { void Thread::SetAsyncException(ObjPtr new_exception) { CHECK(new_exception != nullptr); + Runtime::Current()->SetAsyncExceptionsThrown(); if (kIsDebugBuild) { // Make sure we are in a checkpoint. MutexLock mu(Thread::Current(), *Locks::thread_suspend_count_lock_); -- GitLab From 6b1aebe3612a6e87d7d1847ccca0d7a213cd22a2 Mon Sep 17 00:00:00 2001 From: Mingyao Yang Date: Mon, 27 Nov 2017 15:39:04 -0800 Subject: [PATCH 109/226] Allow devirtualized method to be intrinsified. For a invocation that's devirtualized to a different method, try to give intrinsics matching an opportunity before trying to inline it. Test: run-test on host. 638-checker-inline-cache-intrinsic. Change-Id: I51f70835db4c07575c58872a64a603a38dbcb89c --- compiler/optimizing/inliner.cc | 57 +++++++++-- compiler/optimizing/instruction_simplifier.cc | 4 +- compiler/optimizing/intrinsics.cc | 12 ++- compiler/optimizing/intrinsics.h | 2 +- .../expected.txt | 1 + .../info.txt | 1 + test/638-checker-inline-cache-intrinsic/run | 17 ++++ .../src/Main.java | 95 +++++++++++++++++++ 8 files changed, 174 insertions(+), 15 deletions(-) create mode 100644 test/638-checker-inline-cache-intrinsic/expected.txt create mode 100644 test/638-checker-inline-cache-intrinsic/info.txt create mode 100644 test/638-checker-inline-cache-intrinsic/run create mode 100644 test/638-checker-inline-cache-intrinsic/src/Main.java diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 2444e43d64..560372e22e 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -1211,11 +1211,49 @@ bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, ReferenceTypeInfo receiver_type, bool do_rtp, bool cha_devirtualize) { + DCHECK(!invoke_instruction->IsIntrinsic()); HInstruction* return_replacement = nullptr; uint32_t dex_pc = invoke_instruction->GetDexPc(); HInstruction* cursor = invoke_instruction->GetPrevious(); HBasicBlock* bb_cursor = invoke_instruction->GetBlock(); - if (!TryBuildAndInline(invoke_instruction, method, receiver_type, &return_replacement)) { + bool should_remove_invoke_instruction = false; + + // If invoke_instruction is devirtualized to a different method, give intrinsics + // another chance before we try to inline it. + bool wrong_invoke_type = false; + if (invoke_instruction->GetResolvedMethod() != method && + IntrinsicsRecognizer::Recognize(invoke_instruction, method, &wrong_invoke_type)) { + MaybeRecordStat(stats_, MethodCompilationStat::kIntrinsicRecognized); + if (invoke_instruction->IsInvokeInterface()) { + // We don't intrinsify an invoke-interface directly. + // Replace the invoke-interface with an invoke-virtual. + HInvokeVirtual* new_invoke = new (graph_->GetAllocator()) HInvokeVirtual( + graph_->GetAllocator(), + invoke_instruction->GetNumberOfArguments(), + invoke_instruction->GetType(), + invoke_instruction->GetDexPc(), + invoke_instruction->GetDexMethodIndex(), // Use interface method's dex method index. + method, + method->GetMethodIndex()); + HInputsRef inputs = invoke_instruction->GetInputs(); + for (size_t index = 0; index != inputs.size(); ++index) { + new_invoke->SetArgumentAt(index, inputs[index]); + } + invoke_instruction->GetBlock()->InsertInstructionBefore(new_invoke, invoke_instruction); + new_invoke->CopyEnvironmentFrom(invoke_instruction->GetEnvironment()); + if (invoke_instruction->GetType() == DataType::Type::kReference) { + new_invoke->SetReferenceTypeInfo(invoke_instruction->GetReferenceTypeInfo()); + } + // Run intrinsic recognizer again to set new_invoke's intrinsic. + IntrinsicsRecognizer::Recognize(new_invoke, method, &wrong_invoke_type); + DCHECK_NE(new_invoke->GetIntrinsic(), Intrinsics::kNone); + return_replacement = new_invoke; + // invoke_instruction is replaced with new_invoke. + should_remove_invoke_instruction = true; + } else { + // invoke_instruction is intrinsified and stays. + } + } else if (!TryBuildAndInline(invoke_instruction, method, receiver_type, &return_replacement)) { if (invoke_instruction->IsInvokeInterface()) { DCHECK(!method->IsProxyMethod()); // Turn an invoke-interface into an invoke-virtual. An invoke-virtual is always @@ -1258,26 +1296,27 @@ bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, new_invoke->SetReferenceTypeInfo(invoke_instruction->GetReferenceTypeInfo()); } return_replacement = new_invoke; - // Directly check if the new virtual can be recognized as an intrinsic. - // This way, we avoid running a full recognition pass just to detect - // these relative rare cases. - bool wrong_invoke_type = false; - if (IntrinsicsRecognizer::Recognize(new_invoke, &wrong_invoke_type)) { - MaybeRecordStat(stats_, MethodCompilationStat::kIntrinsicRecognized); - } + // invoke_instruction is replaced with new_invoke. + should_remove_invoke_instruction = true; } else { // TODO: Consider sharpening an invoke virtual once it is not dependent on the // compiler driver. return false; } + } else { + // invoke_instruction is inlined. + should_remove_invoke_instruction = true; } + if (cha_devirtualize) { AddCHAGuard(invoke_instruction, dex_pc, cursor, bb_cursor); } if (return_replacement != nullptr) { invoke_instruction->ReplaceWith(return_replacement); } - invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction); + if (should_remove_invoke_instruction) { + invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction); + } FixUpReturnReferenceType(method, return_replacement); if (do_rtp && ReturnTypeMoreSpecific(invoke_instruction, return_replacement)) { // Actual return value has a more specific type than the method's declared diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 7fa0c2be3d..089e41b4f4 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -2035,7 +2035,9 @@ void InstructionSimplifierVisitor::SimplifyStringEquals(HInvoke* instruction) { optimizations.SetArgumentIsString(); } else if (kUseReadBarrier) { DCHECK(instruction->GetResolvedMethod() != nullptr); - DCHECK(instruction->GetResolvedMethod()->GetDeclaringClass()->IsStringClass()); + DCHECK(instruction->GetResolvedMethod()->GetDeclaringClass()->IsStringClass() || + // Object.equals() can be devirtualized to String.equals(). + instruction->GetResolvedMethod()->GetDeclaringClass()->IsObjectClass()); Runtime* runtime = Runtime::Current(); // For AOT, we always assume that the boot image shall contain the String.class and // we do not need a read barrier for boot image classes as they are non-moveable. diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 77199242f5..6928b70df7 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -137,7 +137,7 @@ static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) case kVirtual: // Call might be devirtualized. - return (invoke_type == kVirtual || invoke_type == kDirect); + return (invoke_type == kVirtual || invoke_type == kDirect || invoke_type == kInterface); case kSuper: case kInterface: @@ -148,8 +148,12 @@ static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) UNREACHABLE(); } -bool IntrinsicsRecognizer::Recognize(HInvoke* invoke, /*out*/ bool* wrong_invoke_type) { - ArtMethod* art_method = invoke->GetResolvedMethod(); +bool IntrinsicsRecognizer::Recognize(HInvoke* invoke, + ArtMethod* art_method, + /*out*/ bool* wrong_invoke_type) { + if (art_method == nullptr) { + art_method = invoke->GetResolvedMethod(); + } *wrong_invoke_type = false; if (art_method == nullptr || !art_method->IsIntrinsic()) { return false; @@ -182,7 +186,7 @@ void IntrinsicsRecognizer::Run() { HInstruction* inst = inst_it.Current(); if (inst->IsInvoke()) { bool wrong_invoke_type = false; - if (Recognize(inst->AsInvoke(), &wrong_invoke_type)) { + if (Recognize(inst->AsInvoke(), /* art_method */ nullptr, &wrong_invoke_type)) { MaybeRecordStat(stats_, MethodCompilationStat::kIntrinsicRecognized); } else if (wrong_invoke_type) { LOG(WARNING) diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index c07a99032a..62991435c7 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -47,7 +47,7 @@ class IntrinsicsRecognizer : public HOptimization { // Static helper that recognizes intrinsic call. Returns true on success. // If it fails due to invoke type mismatch, wrong_invoke_type is set. // Useful to recognize intrinsics on individual calls outside this full pass. - static bool Recognize(HInvoke* invoke, /*out*/ bool* wrong_invoke_type) + static bool Recognize(HInvoke* invoke, ArtMethod* method, /*out*/ bool* wrong_invoke_type) REQUIRES_SHARED(Locks::mutator_lock_); static constexpr const char* kIntrinsicsRecognizerPassName = "intrinsics_recognition"; diff --git a/test/638-checker-inline-cache-intrinsic/expected.txt b/test/638-checker-inline-cache-intrinsic/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/638-checker-inline-cache-intrinsic/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/638-checker-inline-cache-intrinsic/info.txt b/test/638-checker-inline-cache-intrinsic/info.txt new file mode 100644 index 0000000000..764577be54 --- /dev/null +++ b/test/638-checker-inline-cache-intrinsic/info.txt @@ -0,0 +1 @@ +Verify the devirtualization of a method that should be intrinsified. diff --git a/test/638-checker-inline-cache-intrinsic/run b/test/638-checker-inline-cache-intrinsic/run new file mode 100644 index 0000000000..f43681dd56 --- /dev/null +++ b/test/638-checker-inline-cache-intrinsic/run @@ -0,0 +1,17 @@ +#!/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. + +exec ${RUN} --jit --runtime-option -Xjitthreshold:100 -Xcompiler-option --verbose-methods=inlineMonomorphic,knownReceiverType,stringEquals $@ diff --git a/test/638-checker-inline-cache-intrinsic/src/Main.java b/test/638-checker-inline-cache-intrinsic/src/Main.java new file mode 100644 index 0000000000..472cbf68bc --- /dev/null +++ b/test/638-checker-inline-cache-intrinsic/src/Main.java @@ -0,0 +1,95 @@ +/* + * 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 { + + /// CHECK-START: char Main.$noinline$inlineMonomorphic(java.lang.CharSequence) inliner (before) + /// CHECK: InvokeInterface method_name:java.lang.CharSequence.charAt + + /// CHECK-START: char Main.$noinline$inlineMonomorphic(java.lang.CharSequence) inliner (after) + /// CHECK: Deoptimize + /// CHECK: InvokeVirtual method_name:java.lang.String.charAt intrinsic:StringCharAt + + /// CHECK-START: char Main.$noinline$inlineMonomorphic(java.lang.CharSequence) instruction_simplifier$after_inlining (after) + /// CHECK: Deoptimize + /// CHECK-NOT: InvokeInterface + /// CHECK-NOT: InvokeVirtual + + public static char $noinline$inlineMonomorphic(CharSequence cs) { + return cs.charAt(0); + } + + /// CHECK-START: char Main.$noinline$knownReceiverType() inliner (before) + /// CHECK: InvokeInterface method_name:java.lang.CharSequence.charAt + + /// CHECK-START: char Main.$noinline$knownReceiverType() inliner (after) + /// CHECK: InvokeVirtual method_name:java.lang.String.charAt intrinsic:StringCharAt + + /// CHECK-START: char Main.$noinline$knownReceiverType() instruction_simplifier$after_inlining (after) + /// CHECK-NOT: InvokeInterface + /// CHECK-NOT: InvokeVirtual + + public static char $noinline$knownReceiverType() { + CharSequence cs = "abc"; + return cs.charAt(1); + } + + /// CHECK-START: boolean Main.$noinline$stringEquals(java.lang.Object) inliner (before) + /// CHECK: InvokeVirtual method_name:java.lang.Object.equals intrinsic:None + + /// CHECK-START: boolean Main.$noinline$stringEquals(java.lang.Object) inliner (after) + /// CHECK: Deoptimize + /// CHECK: InvokeVirtual method_name:java.lang.Object.equals intrinsic:StringEquals + + /// CHECK-START: boolean Main.$noinline$stringEquals(java.lang.Object) instruction_simplifier$after_inlining (after) + /// CHECK: Deoptimize + /// CHECK: InvokeVirtual method_name:java.lang.Object.equals intrinsic:StringEquals + + public static boolean $noinline$stringEquals(Object obj) { + return obj.equals("def"); + } + + public static void test() { + // Warm up inline cache. + for (int i = 0; i < 45; i++) { + $noinline$inlineMonomorphic(str); + } + for (int i = 0; i < 60; i++) { + $noinline$stringEquals(str); + } + ensureJitCompiled(Main.class, "$noinline$stringEquals"); + ensureJitCompiled(Main.class, "$noinline$inlineMonomorphic"); + ensureJitCompiled(Main.class, "$noinline$knownReceiverType"); + if ($noinline$inlineMonomorphic(str) != 'x') { + throw new Error("Expected x"); + } + if ($noinline$knownReceiverType() != 'b') { + throw new Error("Expected b"); + } + if ($noinline$stringEquals("abc")) { + throw new Error("Expected false"); + } + } + + public static void main(String[] args) { + System.loadLibrary(args[0]); + test(); + } + + static String str = "xyz"; + + private static native void ensureJitCompiled(Class itf, String method_name); +} -- GitLab From c6387088d77c5e7eb3f3c0e3c8178e84d654f687 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 30 Nov 2017 21:00:49 -0800 Subject: [PATCH 110/226] ART: Add more comprehensive merge test Add a more comprehensive test starting from a covering relation and derive expectations algorithmically. Fix a missing collapse of UnresolvedMergedReference types with Object as the resolved component. Test: m test-art-host Change-Id: I20ab059195a0130c48b51f92f9a8ff5a3e9d4e10 --- runtime/verifier/reg_type_cache.cc | 3 + runtime/verifier/reg_type_test.cc | 356 +++++++++++++++++++++++++++++ 2 files changed, 359 insertions(+) diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index 0029eb90a3..e1456dfa5a 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -396,6 +396,9 @@ const RegType& RegTypeCache::FromUnresolvedMerge(const RegType& left, if (resolved_parts_merged.IsConflict()) { return Conflict(); } + if (resolved_parts_merged.IsJavaLangObject()) { + return resolved_parts_merged; + } bool resolved_merged_is_array = resolved_parts_merged.IsArrayTypes(); if (left_unresolved_is_array || right_unresolved_is_array || resolved_merged_is_array) { diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc index 1bc48ed71b..6edeecec02 100644 --- a/runtime/verifier/reg_type_test.cc +++ b/runtime/verifier/reg_type_test.cc @@ -664,6 +664,362 @@ TEST_F(RegTypeTest, MergingDouble) { } } +TEST_F(RegTypeTest, MergeSemiLatticeRef) { + // (Incomplete) semilattice: + // + // Excluded for now: * category-2 types + // * interfaces + // * all of category-1 primitive types, including constants. + // This is to demonstrate/codify the reference side, mostly. + // + // Note: It is not a real semilattice because int = float makes this wonky. :-( + // + // Conflict + // | + // #---------#--------------------------#-----------------------------# + // | | | + // | | Object + // | | | + // int uninit types #---------------#--------#------------------#---------# + // | | | | | | + // | unresolved-merge-types | Object[] char[] byte[] + // | | | | | | | | + // | unresolved-types | #------Number #---------# | | + // | | | | | | | | + // | | #--------Integer Number[] Number[][] | | + // | | | | | | | + // | #---------------#--------#---------#--------#---------# + // | | + // #--------------------------#----------------------------# + // | + // 0 + + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); + ScopedObjectAccess soa(Thread::Current()); + + // We cannot allow moving GC. Otherwise we'd have to ensure the reg types are updated (reference + // reg types store a class pointer in a GCRoot, which is normally updated through active verifiers + // being registered with their thread), which is unnecessarily complex. + Runtime::Current()->GetHeap()->IncrementDisableMovingGC(soa.Self()); + + RegTypeCache cache(true, allocator); + + const RegType& conflict = cache.Conflict(); + const RegType& zero = cache.Zero(); + const RegType& int_type = cache.Integer(); + + const RegType& obj = cache.JavaLangObject(false); + const RegType& obj_arr = cache.From(nullptr, "[Ljava/lang/Object;", false); + ASSERT_FALSE(obj_arr.IsUnresolvedReference()); + + const RegType& unresolved_a = cache.From(nullptr, "Ldoes/not/resolve/A;", false); + ASSERT_TRUE(unresolved_a.IsUnresolvedReference()); + const RegType& unresolved_b = cache.From(nullptr, "Ldoes/not/resolve/B;", false); + ASSERT_TRUE(unresolved_b.IsUnresolvedReference()); + const RegType& unresolved_ab = cache.FromUnresolvedMerge(unresolved_a, unresolved_b, nullptr); + ASSERT_TRUE(unresolved_ab.IsUnresolvedMergedReference()); + + const RegType& uninit_this = cache.UninitializedThisArgument(obj); + const RegType& uninit_obj_0 = cache.Uninitialized(obj, 0u); + const RegType& uninit_obj_1 = cache.Uninitialized(obj, 1u); + + const RegType& uninit_unres_this = cache.UninitializedThisArgument(unresolved_a); + const RegType& uninit_unres_a_0 = cache.Uninitialized(unresolved_a, 0); + const RegType& uninit_unres_b_0 = cache.Uninitialized(unresolved_b, 0); + + const RegType& number = cache.From(nullptr, "Ljava/lang/Number;", false); + ASSERT_FALSE(number.IsUnresolvedReference()); + const RegType& integer = cache.From(nullptr, "Ljava/lang/Integer;", false); + ASSERT_FALSE(integer.IsUnresolvedReference()); + + const RegType& uninit_number_0 = cache.Uninitialized(number, 0u); + const RegType& uninit_integer_0 = cache.Uninitialized(integer, 0u); + + const RegType& number_arr = cache.From(nullptr, "[Ljava/lang/Number;", false); + ASSERT_FALSE(number_arr.IsUnresolvedReference()); + const RegType& integer_arr = cache.From(nullptr, "[Ljava/lang/Integer;", false); + ASSERT_FALSE(integer_arr.IsUnresolvedReference()); + + const RegType& number_arr_arr = cache.From(nullptr, "[[Ljava/lang/Number;", false); + ASSERT_FALSE(number_arr_arr.IsUnresolvedReference()); + + const RegType& char_arr = cache.From(nullptr, "[C", false); + ASSERT_FALSE(char_arr.IsUnresolvedReference()); + const RegType& byte_arr = cache.From(nullptr, "[B", false); + ASSERT_FALSE(byte_arr.IsUnresolvedReference()); + + const RegType& unresolved_a_num = cache.FromUnresolvedMerge(unresolved_a, number, nullptr); + ASSERT_TRUE(unresolved_a_num.IsUnresolvedMergedReference()); + const RegType& unresolved_b_num = cache.FromUnresolvedMerge(unresolved_b, number, nullptr); + ASSERT_TRUE(unresolved_b_num.IsUnresolvedMergedReference()); + const RegType& unresolved_ab_num = cache.FromUnresolvedMerge(unresolved_ab, number, nullptr); + ASSERT_TRUE(unresolved_ab_num.IsUnresolvedMergedReference()); + + const RegType& unresolved_a_int = cache.FromUnresolvedMerge(unresolved_a, integer, nullptr); + ASSERT_TRUE(unresolved_a_int.IsUnresolvedMergedReference()); + const RegType& unresolved_b_int = cache.FromUnresolvedMerge(unresolved_b, integer, nullptr); + ASSERT_TRUE(unresolved_b_int.IsUnresolvedMergedReference()); + const RegType& unresolved_ab_int = cache.FromUnresolvedMerge(unresolved_ab, integer, nullptr); + ASSERT_TRUE(unresolved_ab_int.IsUnresolvedMergedReference()); + std::vector uninitialized_types = { + &uninit_this, &uninit_obj_0, &uninit_obj_1, &uninit_number_0, &uninit_integer_0 + }; + std::vector unresolved_types = { + &unresolved_a, + &unresolved_b, + &unresolved_ab, + &unresolved_a_num, + &unresolved_b_num, + &unresolved_ab_num, + &unresolved_a_int, + &unresolved_b_int, + &unresolved_ab_int + }; + std::vector uninit_unresolved_types = { + &uninit_unres_this, &uninit_unres_a_0, &uninit_unres_b_0 + }; + std::vector plain_nonobj_classes = { &number, &integer }; + std::vector plain_nonobj_arr_classes = { + &number_arr, + &number_arr_arr, + &integer_arr, + &char_arr, + }; + // std::vector others = { &conflict, &zero, &null, &obj, &int_type }; + + std::vector all_minus_uninit_conflict; + all_minus_uninit_conflict.insert(all_minus_uninit_conflict.end(), + unresolved_types.begin(), + unresolved_types.end()); + all_minus_uninit_conflict.insert(all_minus_uninit_conflict.end(), + plain_nonobj_classes.begin(), + plain_nonobj_classes.end()); + all_minus_uninit_conflict.insert(all_minus_uninit_conflict.end(), + plain_nonobj_arr_classes.begin(), + plain_nonobj_arr_classes.end()); + all_minus_uninit_conflict.push_back(&zero); + all_minus_uninit_conflict.push_back(&obj); + + std::vector all_minus_uninit; + all_minus_uninit.insert(all_minus_uninit.end(), + all_minus_uninit_conflict.begin(), + all_minus_uninit_conflict.end()); + all_minus_uninit.push_back(&conflict); + + + std::vector all; + all.insert(all.end(), uninitialized_types.begin(), uninitialized_types.end()); + all.insert(all.end(), uninit_unresolved_types.begin(), uninit_unresolved_types.end()); + all.insert(all.end(), all_minus_uninit.begin(), all_minus_uninit.end()); + all.push_back(&int_type); + + auto check = [&](const RegType& in1, const RegType& in2, const RegType& expected_out) + REQUIRES_SHARED(Locks::mutator_lock_) { + const RegType& merge_result = in1.SafeMerge(in2, &cache, nullptr); + EXPECT_EQ(&expected_out, &merge_result) + << in1.Dump() << " x " << in2.Dump() << " = " << merge_result.Dump() + << " != " << expected_out.Dump(); + }; + + // Identity. + { + for (auto r : all) { + check(*r, *r, *r); + } + } + + // Define a covering relation through a list of Edges. We'll then derive LUBs from this and + // create checks for every pair of types. + + struct Edge { + const RegType& from; + const RegType& to; + + Edge(const RegType& from_, const RegType& to_) : from(from_), to(to_) {} + }; + std::vector edges; +#define ADD_EDGE(from, to) edges.emplace_back((from), (to)) + + // To Conflict. + { + for (auto r : uninitialized_types) { + ADD_EDGE(*r, conflict); + } + for (auto r : uninit_unresolved_types) { + ADD_EDGE(*r, conflict); + } + ADD_EDGE(obj, conflict); + ADD_EDGE(int_type, conflict); + } + + // Unresolved. + { + ADD_EDGE(zero, unresolved_a); + ADD_EDGE(zero, unresolved_b); + ADD_EDGE(unresolved_a, unresolved_ab); + ADD_EDGE(unresolved_b, unresolved_ab); + + ADD_EDGE(number, unresolved_a_num); + ADD_EDGE(unresolved_a, unresolved_a_num); + ADD_EDGE(number, unresolved_b_num); + ADD_EDGE(unresolved_b, unresolved_b_num); + ADD_EDGE(number, unresolved_ab_num); + ADD_EDGE(unresolved_a_num, unresolved_ab_num); + ADD_EDGE(unresolved_b_num, unresolved_ab_num); + ADD_EDGE(unresolved_ab, unresolved_ab_num); + + ADD_EDGE(integer, unresolved_a_int); + ADD_EDGE(unresolved_a, unresolved_a_int); + ADD_EDGE(integer, unresolved_b_int); + ADD_EDGE(unresolved_b, unresolved_b_int); + ADD_EDGE(integer, unresolved_ab_int); + ADD_EDGE(unresolved_a_int, unresolved_ab_int); + ADD_EDGE(unresolved_b_int, unresolved_ab_int); + ADD_EDGE(unresolved_ab, unresolved_ab_int); + + ADD_EDGE(unresolved_a_int, unresolved_a_num); + ADD_EDGE(unresolved_b_int, unresolved_b_num); + ADD_EDGE(unresolved_ab_int, unresolved_ab_num); + + ADD_EDGE(unresolved_ab_num, obj); + } + + // Classes. + { + ADD_EDGE(zero, integer); + ADD_EDGE(integer, number); + ADD_EDGE(number, obj); + } + + // Arrays. + { + ADD_EDGE(integer_arr, number_arr); + ADD_EDGE(number_arr, obj_arr); + ADD_EDGE(obj_arr, obj); + ADD_EDGE(number_arr_arr, obj_arr); + + ADD_EDGE(char_arr, obj); + ADD_EDGE(byte_arr, obj); + + ADD_EDGE(zero, integer_arr); + ADD_EDGE(zero, number_arr_arr); + ADD_EDGE(zero, char_arr); + ADD_EDGE(zero, byte_arr); + } + + // Primitive. + { + ADD_EDGE(zero, int_type); + } +#undef ADD_EDGE + + // Create merge triples by using the covering relation established by edges to derive the + // expected merge for any pair of types. + + // Expect merge(in1, in2) == out. + struct MergeExpectation { + const RegType& in1; + const RegType& in2; + const RegType& out; + + MergeExpectation(const RegType& in1_, const RegType& in2_, const RegType& out_) + : in1(in1_), in2(in2_), out(out_) {} + }; + std::vector expectations; + + for (auto r1 : all) { + for (auto r2 : all) { + if (r1 == r2) { + continue; + } + + // Very simple algorithm here that is usually used with adjacency lists. Our graph is + // small, it didn't make sense to have lists per node. Thus, the regular guarantees + // of O(n + |e|) don't apply, but that is acceptable. + // + // To compute r1 lub r2 = merge(r1, r2): + // 1) Generate the reachable set of r1, name it grey. + // 2) Mark all grey reachable nodes of r2 as black. + // 3) Find black nodes with no in-edges from other black nodes. + // 4) If |3)| == 1, that's the lub. + + // Generic BFS of the graph induced by edges, starting at start. new_node will be called + // with any discovered node, in order. + auto bfs = [&](auto new_node, const RegType* start) { + std::unordered_set seen; + std::queue work_list; + work_list.push(start); + while (!work_list.empty()) { + const RegType* cur = work_list.front(); + work_list.pop(); + auto it = seen.find(cur); + if (it != seen.end()) { + continue; + } + seen.insert(cur); + new_node(cur); + + for (const Edge& edge : edges) { + if (&edge.from == cur) { + work_list.push(&edge.to); + } + } + } + }; + + std::unordered_set grey; + auto compute_grey = [&](const RegType* cur) { + grey.insert(cur); // Mark discovered node as grey. + }; + bfs(compute_grey, r1); + + std::set black; + auto compute_black = [&](const RegType* cur) { + // Mark discovered grey node as black. + if (grey.find(cur) != grey.end()) { + black.insert(cur); + } + }; + bfs(compute_black, r2); + + std::set no_in_edge(black); // Copy of black, remove nodes with in-edges. + for (auto r : black) { + for (Edge& e : edges) { + if (&e.from == r) { + no_in_edge.erase(&e.to); // It doesn't matter whether "to" is black or not, just + // attempt to remove it. + } + } + } + + // Helper to print sets when something went wrong. + auto print_set = [](auto& container) REQUIRES_SHARED(Locks::mutator_lock_) { + std::string result; + for (auto r : container) { + result.append(" + "); + result.append(r->Dump()); + } + return result; + }; + ASSERT_EQ(no_in_edge.size(), 1u) << r1->Dump() << " u " << r2->Dump() + << " grey=" << print_set(grey) + << " black=" << print_set(black) + << " no-in-edge=" << print_set(no_in_edge); + expectations.emplace_back(*r1, *r2, **no_in_edge.begin()); + } + } + + // Evaluate merge expectations. The merge is expected to be commutative. + + for (auto& triple : expectations) { + check(triple.in1, triple.in2, triple.out); + check(triple.in2, triple.in1, triple.out); + } + + Runtime::Current()->GetHeap()->DecrementDisableMovingGC(soa.Self()); +} + TEST_F(RegTypeTest, ConstPrecision) { // Tests creating primitive types types. ArenaStack stack(Runtime::Current()->GetArenaPool()); -- GitLab From b6b15438e3ad8bf0c820ce7ab0c9fb0a19fb3192 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Fri, 1 Dec 2017 11:25:34 -0800 Subject: [PATCH 111/226] ART: Clean up primitive-type construction Change the scope of the helper to be a lambda. We rely on character data being available here (and passed on), which is acceptable for compile-time const char* values. Test: m test-art-host Change-Id: Ideac7e6ac22d9394cfc5c1cb8125bf71a450fc9c --- runtime/verifier/reg_type_cache.cc | 74 ++++++++++++++++++++---------- runtime/verifier/reg_type_cache.h | 3 -- 2 files changed, 49 insertions(+), 28 deletions(-) diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index e1456dfa5a..4808381d5b 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -16,6 +16,8 @@ #include "reg_type_cache-inl.h" +#include + #include "base/arena_bit_vector.h" #include "base/bit_vector-inl.h" #include "base/casts.h" @@ -51,6 +53,7 @@ ALWAYS_INLINE static inline bool MatchingPrecisionForClass(const RegType* entry, } void RegTypeCache::FillPrimitiveAndSmallConstantTypes() { + // Note: this must have the same order as CreatePrimitiveAndSmallConstantTypes. entries_.push_back(UndefinedType::GetInstance()); entries_.push_back(ConflictType::GetInstance()); entries_.push_back(BooleanType::GetInstance()); @@ -314,33 +317,54 @@ void RegTypeCache::ShutDown() { } } -template -const Type* RegTypeCache::CreatePrimitiveTypeInstance(const std::string& descriptor) { - mirror::Class* klass = nullptr; - // Try loading the class from linker. - if (!descriptor.empty()) { - klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), - descriptor.c_str()); - DCHECK(klass != nullptr); - } - const Type* entry = Type::CreateInstance(klass, descriptor, RegTypeCache::primitive_count_); - RegTypeCache::primitive_count_++; - return entry; -} +// Helper for create_primitive_type_instance lambda. +namespace { +template +struct TypeHelper { + using type = T; + static_assert(std::is_convertible::value, "T must be a RegType"); + + const char* descriptor; + + explicit TypeHelper(const char* d) : descriptor(d) {} +}; +} // namespace void RegTypeCache::CreatePrimitiveAndSmallConstantTypes() { - CreatePrimitiveTypeInstance(""); - CreatePrimitiveTypeInstance(""); - CreatePrimitiveTypeInstance("Z"); - CreatePrimitiveTypeInstance("B"); - CreatePrimitiveTypeInstance("S"); - CreatePrimitiveTypeInstance("C"); - CreatePrimitiveTypeInstance("I"); - CreatePrimitiveTypeInstance("J"); - CreatePrimitiveTypeInstance("J"); - CreatePrimitiveTypeInstance("F"); - CreatePrimitiveTypeInstance("D"); - CreatePrimitiveTypeInstance("D"); + // Note: this must have the same order as FillPrimitiveAndSmallConstantTypes. + + // It is acceptable to pass on the const char* in type to CreateInstance, as all calls below are + // with compile-time constants that will have global lifetime. Use of the lambda ensures this + // 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; + // Try loading the class from linker. + DCHECK(type.descriptor != nullptr); + if (strlen(type.descriptor) > 0) { + klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), + type.descriptor); + DCHECK(klass != nullptr); + } + const Type* entry = Type::CreateInstance(klass, + type.descriptor, + RegTypeCache::primitive_count_); + RegTypeCache::primitive_count_++; + return entry; + }; + create_primitive_type_instance(TypeHelper("")); + create_primitive_type_instance(TypeHelper("")); + create_primitive_type_instance(TypeHelper("Z")); + create_primitive_type_instance(TypeHelper("B")); + create_primitive_type_instance(TypeHelper("S")); + create_primitive_type_instance(TypeHelper("C")); + create_primitive_type_instance(TypeHelper("I")); + create_primitive_type_instance(TypeHelper("J")); + create_primitive_type_instance(TypeHelper("J")); + create_primitive_type_instance(TypeHelper("F")); + create_primitive_type_instance(TypeHelper("D")); + create_primitive_type_instance(TypeHelper("D")); + for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) { PreciseConstType* type = new PreciseConstType(value, primitive_count_); small_precise_constants_[value - kMinSmallConstant] = type; diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h index d0907564e2..054a2af458 100644 --- a/runtime/verifier/reg_type_cache.h +++ b/runtime/verifier/reg_type_cache.h @@ -171,9 +171,6 @@ class RegTypeCache { // verifier. StringPiece AddString(const StringPiece& string_piece); - template - static const Type* CreatePrimitiveTypeInstance(const std::string& descriptor) - REQUIRES_SHARED(Locks::mutator_lock_); static void CreatePrimitiveAndSmallConstantTypes() REQUIRES_SHARED(Locks::mutator_lock_); // A quick look up for popular small constants. -- GitLab From 0545d4a988290fc93d28e870b71ae66529ae4240 Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Fri, 1 Dec 2017 13:36:26 -0800 Subject: [PATCH 112/226] Pretty print startup methods in profile dump Test: manual Change-Id: Ic791a622b618013ad50893a4168b18fe39d638ed --- runtime/jit/profile_compilation_info.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index bb8e5e5c15..bb0048de0b 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -1582,7 +1582,11 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector* for (uint32_t method_idx = 0; method_idx < dex_data->num_method_ids; ++method_idx) { MethodHotness hotness_info(dex_data->GetHotnessInfo(method_idx)); if (startup ? hotness_info.IsStartup() : hotness_info.IsPostStartup()) { - os << method_idx << ", "; + if (dex_file != nullptr) { + os << "\n\t\t" << dex_file->PrettyMethod(method_idx, true); + } else { + os << method_idx << ", "; + } } } if (startup == false) { -- GitLab From 92706a8a541521d586736287f0dc6b7f338acff0 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Thu, 30 Nov 2017 11:46:45 -0800 Subject: [PATCH 113/226] Introduced CHECK-START-{x,y,z} syntax. Rationale: Many of our tests repeat CHECK-START-x with following checker lines for various architectures if some, but not all architectures match the checker lines. This is tedious and error-prone. No more of that nonsense! This CL introduces a syntax that allows specifying subsets of architectures with a single block! Bug: 62352954 Test: test-art-host test-art-target Change-Id: Ife18b3ef9eaa2540166ace045c165ba252b31c6b --- test/665-checker-simd-zero/src/Main.java | 64 +++------------------ tools/checker/README | 7 +++ tools/checker/checker.py | 3 +- tools/checker/file_format/checker/parser.py | 37 ++++++++++-- 4 files changed, 49 insertions(+), 62 deletions(-) diff --git a/test/665-checker-simd-zero/src/Main.java b/test/665-checker-simd-zero/src/Main.java index 6cd6d6465a..5c581c4fc7 100644 --- a/test/665-checker-simd-zero/src/Main.java +++ b/test/665-checker-simd-zero/src/Main.java @@ -24,13 +24,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.zeroz(boolean[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.zeroz(boolean[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.zeroz(boolean[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none @@ -46,13 +40,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.zerob(byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.zerob(byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.zerob(byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none @@ -68,13 +56,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.zeroc(char[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.zeroc(char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.zeroc(char[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none @@ -90,13 +72,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.zeros(short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.zeros(short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.zeros(short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none @@ -112,13 +88,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.zeroi(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.zeroi(int[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.zeroi(int[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none @@ -134,13 +104,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.zerol(long[]) loop_optimization (after) - /// CHECK-DAG: <> LongConstant 0 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.zerol(long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.zerol(long[]) loop_optimization (after) /// CHECK-DAG: <> LongConstant 0 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none @@ -156,13 +120,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.zerof(float[]) loop_optimization (after) - /// CHECK-DAG: <> FloatConstant 0 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.zerof(float[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.zerof(float[]) loop_optimization (after) /// CHECK-DAG: <> FloatConstant 0 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none @@ -178,13 +136,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.zerod(double[]) loop_optimization (after) - /// CHECK-DAG: <> DoubleConstant 0 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.zerod(double[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.zerod(double[]) loop_optimization (after) /// CHECK-DAG: <> DoubleConstant 0 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none diff --git a/tools/checker/README b/tools/checker/README index 65f5bd25a5..b8dd80376e 100644 --- a/tools/checker/README +++ b/tools/checker/README @@ -76,3 +76,10 @@ Example: /// CHECK-START-ARM64: int MyClass.MyMethod() constant_folding (after) /// CHECK: <> IntConstant {{11|22}} /// CHECK: Return [<>] + +For convenience, several architectures can be specified as set after the +'CHECK-START' keyword. Any listed architecture will match in that case, +thereby avoiding to repeat the check lines if some, but not all architectures +match. An example line looks like: + + /// CHECK-START-{MIPS,ARM,ARM64}: int MyClass.MyMethod() constant_folding (after) diff --git a/tools/checker/checker.py b/tools/checker/checker.py index 2e9faba9fb..65b01a7f15 100755 --- a/tools/checker/checker.py +++ b/tools/checker/checker.py @@ -90,7 +90,8 @@ def RunTests(checkPrefix, checkPath, outputFilename, targetArch, debuggableMode) for checkFilename in FindCheckerFiles(checkPath): checkerFile = ParseCheckerStream(os.path.basename(checkFilename), checkPrefix, - open(checkFilename, "r")) + open(checkFilename, "r"), + targetArch) MatchFiles(checkerFile, c1File, targetArch, debuggableMode) diff --git a/tools/checker/file_format/checker/parser.py b/tools/checker/file_format/checker/parser.py index f199a50ebe..7a5a4c8c26 100644 --- a/tools/checker/file_format/checker/parser.py +++ b/tools/checker/file_format/checker/parser.py @@ -44,7 +44,33 @@ def __extractLine(prefix, line, arch = None, debuggable = False): else: return None -def __processLine(line, lineNo, prefix, fileName): +def __preprocessLineForStart(prefix, line, targetArch): + """ This function modifies a CHECK-START-{x,y,z} into a matching + CHECK-START-y line for matching targetArch y. If no matching + architecture is found, CHECK-START-x is returned arbitrarily + to ensure all following check lines are put into a test that + is skipped. Any other line is left unmodified. + """ + if targetArch is not None: + if prefix in line: + # Find { } on the line and assume that defines the set. + s = line.find('{') + e = line.find('}') + if 0 < s and s < e: + archs = line[s+1:e].split(',') + # First verify that every archs is valid. Return the + # full line on failure to prompt error back to user. + for arch in archs: + if not arch in archs_list: + return line + # Now accept matching arch or arbitrarily return first. + if targetArch in archs: + return line[:s] + targetArch + line[e + 1:] + else: + return line[:s] + archs[0] + line[e + 1:] + return line + +def __processLine(line, lineNo, prefix, fileName, targetArch): """ This function is invoked on each line of the check file and returns a triplet which instructs the parser how the line should be handled. If the line is to be included in the current check group, it is returned in the first @@ -56,10 +82,11 @@ def __processLine(line, lineNo, prefix, fileName): return None, None, None # Lines beginning with 'CHECK-START' start a new test case. - # We currently only consider the architecture suffix in "CHECK-START" lines. + # We currently only consider the architecture suffix(es) in "CHECK-START" lines. for debuggable in [True, False]: + sline = __preprocessLineForStart(prefix + "-START", line, targetArch) for arch in [None] + archs_list: - startLine = __extractLine(prefix + "-START", line, arch, debuggable) + startLine = __extractLine(prefix + "-START", sline, arch, debuggable) if startLine is not None: return None, startLine, (arch, debuggable) @@ -164,9 +191,9 @@ def ParseCheckerAssertion(parent, line, variant, lineNo): assertion.addExpression(TestExpression.createPatternFromPlainText(text)) return assertion -def ParseCheckerStream(fileName, prefix, stream): +def ParseCheckerStream(fileName, prefix, stream, targetArch = None): checkerFile = CheckerFile(fileName) - fnProcessLine = lambda line, lineNo: __processLine(line, lineNo, prefix, fileName) + fnProcessLine = lambda line, lineNo: __processLine(line, lineNo, prefix, fileName, targetArch) fnLineOutsideChunk = lambda line, lineNo: \ Logger.fail("Checker line not inside a group", fileName, lineNo) for caseName, caseLines, startLineNo, testData in \ -- GitLab From 74c84408ad56606514304c9ecc643bebbf11d73e Mon Sep 17 00:00:00 2001 From: Alex Light Date: Wed, 29 Nov 2017 15:26:38 -0800 Subject: [PATCH 114/226] Ensure that updates to the global event mask are atomic We were setting and testing the global event mask in a way that allowed races with other threads. This could cause issues if multiple threads write to the mask at the same time and could cause changes to the current event state to be missed. Test: ./test.py --host -j50 Bug: 69657830 Change-Id: I5e2759af598e6179fd25fcbe6b211a9369217156 --- openjdkjvmti/events.cc | 36 ++++++++++++++++++++++++------------ openjdkjvmti/events.h | 12 ++++++++++-- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index be4ebbc85e..a3d9bb462c 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -139,7 +139,9 @@ EventMask* EventMasks::GetEventMaskOrNull(art::Thread* thread) { } -void EventMasks::EnableEvent(art::Thread* thread, ArtJvmtiEvent event) { +void EventMasks::EnableEvent(ArtJvmTiEnv* env, art::Thread* thread, ArtJvmtiEvent event) { + DCHECK_EQ(&env->event_masks, this); + env->event_info_mutex_.AssertExclusiveHeld(art::Thread::Current()); DCHECK(EventMask::EventIsInRange(event)); GetEventMask(thread).Set(event); if (thread != nullptr) { @@ -147,7 +149,9 @@ void EventMasks::EnableEvent(art::Thread* thread, ArtJvmtiEvent event) { } } -void EventMasks::DisableEvent(art::Thread* thread, ArtJvmtiEvent event) { +void EventMasks::DisableEvent(ArtJvmTiEnv* env, art::Thread* thread, ArtJvmtiEvent event) { + DCHECK_EQ(&env->event_masks, this); + env->event_info_mutex_.AssertExclusiveHeld(art::Thread::Current()); DCHECK(EventMask::EventIsInRange(event)); GetEventMask(thread).Set(event, false); if (thread != nullptr) { @@ -1131,20 +1135,28 @@ jvmtiError EventHandler::SetEvent(ArtJvmTiEnv* env, return ERR(MUST_POSSESS_CAPABILITY); } - bool old_state = global_mask.Test(event); + bool old_state; + bool new_state; - if (mode == JVMTI_ENABLE) { - env->event_masks.EnableEvent(thread, event); - global_mask.Set(event); - } else { - DCHECK_EQ(mode, JVMTI_DISABLE); + { + // Change the event masks atomically. + art::Thread* self = art::Thread::Current(); + art::MutexLock mu(self, envs_lock_); + art::WriterMutexLock mu_env_info(self, env->event_info_mutex_); + old_state = global_mask.Test(event); + if (mode == JVMTI_ENABLE) { + env->event_masks.EnableEvent(env, thread, event); + global_mask.Set(event); + new_state = true; + } else { + DCHECK_EQ(mode, JVMTI_DISABLE); - env->event_masks.DisableEvent(thread, event); - RecalculateGlobalEventMask(event); + env->event_masks.DisableEvent(env, thread, event); + RecalculateGlobalEventMaskLocked(event); + new_state = global_mask.Test(event); + } } - bool new_state = global_mask.Test(event); - // Handle any special work required for the event type. if (new_state != old_state) { HandleEventType(event, mode == JVMTI_ENABLE); diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h index c73215f07b..d21a587f03 100644 --- a/openjdkjvmti/events.h +++ b/openjdkjvmti/events.h @@ -149,8 +149,16 @@ struct EventMasks { EventMask& GetEventMask(art::Thread* thread); EventMask* GetEventMaskOrNull(art::Thread* thread); - void EnableEvent(art::Thread* thread, ArtJvmtiEvent event); - void DisableEvent(art::Thread* thread, ArtJvmtiEvent event); + // Circular dependencies mean we cannot see the definition of ArtJvmTiEnv so the mutex is simply + // asserted in the function. + // Note that the 'env' passed in must be the same env this EventMasks is associated with. + void EnableEvent(ArtJvmTiEnv* env, art::Thread* thread, ArtJvmtiEvent event); + // REQUIRES(env->event_info_mutex_); + // Circular dependencies mean we cannot see the definition of ArtJvmTiEnv so the mutex is simply + // asserted in the function. + // Note that the 'env' passed in must be the same env this EventMasks is associated with. + void DisableEvent(ArtJvmTiEnv* env, art::Thread* thread, ArtJvmtiEvent event); + // REQUIRES(env->event_info_mutex_); bool IsEnabledAnywhere(ArtJvmtiEvent event); // Make any changes to event masks needed for the given capability changes. If caps_added is true // then caps is all the newly set capabilities of the jvmtiEnv. If it is false then caps is the -- GitLab From eed3a5d1a6560464b3a271c85b40b1cdf8a1bfd4 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 29 Nov 2017 14:58:34 -0800 Subject: [PATCH 115/226] ART: Add null type to verifier Add a null type to distinguish zero from null. This is an infra-only change, as nothing will introduce null with this commit, yet. Bug: 22059710 Bug: 64683522 Bug: 69669661 Test: m test-art-host Change-Id: Id2549a5aefefe9471d4bdbd2c8993395150947c6 --- runtime/verifier/method_verifier.cc | 27 ++++++++-------- runtime/verifier/reg_type-inl.h | 14 +++++++- runtime/verifier/reg_type.cc | 24 ++++++++++++-- runtime/verifier/reg_type.h | 46 ++++++++++++++++++++++++++- runtime/verifier/reg_type_cache-inl.h | 3 ++ runtime/verifier/reg_type_cache.cc | 3 ++ runtime/verifier/reg_type_cache.h | 4 ++- runtime/verifier/reg_type_test.cc | 20 ++++++++---- 8 files changed, 116 insertions(+), 25 deletions(-) diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index c6ba2f7e43..978e51d7b0 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -2237,7 +2237,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-object not expected"; } else { /* return_type is the *expected* return type, not register value */ - DCHECK(!return_type.IsZero()); + DCHECK(!return_type.IsZeroOrNull()); DCHECK(!return_type.IsUninitializedReference()); const uint32_t vregA = inst->VRegA_11x(); const RegType& reg_type = work_line_->GetRegisterType(this, vregA); @@ -2485,7 +2485,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::ARRAY_LENGTH: { const RegType& res_type = work_line_->GetRegisterType(this, inst->VRegB_12x()); if (res_type.IsReferenceTypes()) { - if (!res_type.IsArrayTypes() && !res_type.IsZero()) { // ie not an array or null + if (!res_type.IsArrayTypes() && !res_type.IsZeroOrNull()) { + // ie not an array or null Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array-length on non-array " << res_type; } else { work_line_->SetRegisterType(this, @@ -2592,7 +2593,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { /* Similar to the verification done for APUT */ const RegType& array_type = work_line_->GetRegisterType(this, inst->VRegA_31t()); /* array_type can be null if the reg type is Zero */ - if (!array_type.IsZero()) { + if (!array_type.IsZeroOrNull()) { if (!array_type.IsArrayTypes()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with array type " << array_type; @@ -2632,7 +2633,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { const RegType& reg_type1 = work_line_->GetRegisterType(this, inst->VRegA_22t()); const RegType& reg_type2 = work_line_->GetRegisterType(this, inst->VRegB_22t()); bool mismatch = false; - if (reg_type1.IsZero()) { // zero then integral or reference expected + if (reg_type1.IsZeroOrNull()) { // zero then integral or reference expected mismatch = !reg_type2.IsReferenceTypes() && !reg_type2.IsIntegralTypes(); } else if (reg_type1.IsReferenceTypes()) { // both references? mismatch = !reg_type2.IsReferenceTypes(); @@ -2717,7 +2718,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { !cast_type.IsUnresolvedTypes() && !orig_type.IsUnresolvedTypes() && cast_type.HasClass() && // Could be conflict type, make sure it has a class. !cast_type.GetClass()->IsInterface() && - (orig_type.IsZero() || + (orig_type.IsZeroOrNull() || orig_type.IsStrictlyAssignableFrom( cast_type.Merge(orig_type, ®_types_, this), this))) { RegisterLine* update_line = RegisterLine::Create(code_item_accessor_.RegistersSize(), @@ -3005,7 +3006,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { break; /* no null refs allowed (?) */ - if (this_type.IsZero()) { + if (this_type.IsZeroOrNull()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unable to initialize null ref"; break; } @@ -3082,7 +3083,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * interface or Object (see comments in RegType::JoinClass). */ const RegType& this_type = work_line_->GetInvocationThis(this, inst); - if (this_type.IsZero()) { + if (this_type.IsZeroOrNull()) { /* null pointer always passes (and always fails at runtime) */ } else { if (this_type.IsUninitializedTypes()) { @@ -4081,7 +4082,7 @@ ArtMethod* MethodVerifier::VerifyInvocationArgsFromIterator( const RegType& adjusted_type = is_init ? GetRegTypeCache()->FromUninitialized(actual_arg_type) : actual_arg_type; - if (method_type != METHOD_INTERFACE && !adjusted_type.IsZero()) { + if (method_type != METHOD_INTERFACE && !adjusted_type.IsZeroOrNull()) { const RegType* res_method_class; // Miranda methods have the declaring interface as their declaring class, not the abstract // class. It would be wrong to use this for the type check (interface type checks are @@ -4454,7 +4455,7 @@ bool MethodVerifier::CheckSignaturePolymorphicMethod(ArtMethod* method) { bool MethodVerifier::CheckSignaturePolymorphicReceiver(const Instruction* inst) { const RegType& this_type = work_line_->GetInvocationThis(this, inst); - if (this_type.IsZero()) { + if (this_type.IsZeroOrNull()) { /* null pointer always passes (and always fails at run time) */ return true; } else if (!this_type.IsNonZeroReferenceTypes()) { @@ -4573,7 +4574,7 @@ ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst, Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'this' arg must be initialized"; return nullptr; } - if (!actual_arg_type.IsZero()) { + if (!actual_arg_type.IsZeroOrNull()) { mirror::Class* klass = res_method->GetDeclaringClass(); std::string temp; const RegType& res_method_class = @@ -4689,7 +4690,7 @@ void MethodVerifier::VerifyAGet(const Instruction* inst, Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Invalid reg type for array index (" << index_type << ")"; } else { const RegType& array_type = work_line_->GetRegisterType(this, inst->VRegB_23x()); - if (array_type.IsZero()) { + if (array_type.IsZeroOrNull()) { have_pending_runtime_throw_failure_ = true; // Null array class; this code path will fail at runtime. Infer a merge-able type from the // instruction type. TODO: have a proper notion of bottom here. @@ -4804,7 +4805,7 @@ void MethodVerifier::VerifyAPut(const Instruction* inst, Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Invalid reg type for array index (" << index_type << ")"; } else { const RegType& array_type = work_line_->GetRegisterType(this, inst->VRegB_23x()); - if (array_type.IsZero()) { + if (array_type.IsZeroOrNull()) { // Null array type; this code path will fail at runtime. // Still check that the given value matches the instruction's type. // Note: this is, as usual, complicated by the fact the the instruction isn't fully typed @@ -4926,7 +4927,7 @@ ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_id DCHECK(self_->IsExceptionPending()); self_->ClearException(); return nullptr; - } else if (obj_type.IsZero()) { + } else if (obj_type.IsZeroOrNull()) { // Cannot infer and check type, however, access will cause null pointer exception. // Fall through into a few last soft failure checks below. } else if (!obj_type.IsReferenceTypes()) { diff --git a/runtime/verifier/reg_type-inl.h b/runtime/verifier/reg_type-inl.h index 631c6bd7ef..f719782727 100644 --- a/runtime/verifier/reg_type-inl.h +++ b/runtime/verifier/reg_type-inl.h @@ -29,6 +29,8 @@ namespace art { namespace verifier { inline bool RegType::CanAccess(const RegType& other) const { + DCHECK(IsReferenceTypes()); + DCHECK(!IsNull()); if (Equals(other)) { return true; // Trivial accessibility. } else { @@ -45,9 +47,13 @@ inline bool RegType::CanAccess(const RegType& other) const { } inline bool RegType::CanAccessMember(ObjPtr klass, uint32_t access_flags) const { + DCHECK(IsReferenceTypes()); if ((access_flags & kAccPublic) != 0) { return true; } + if (IsNull()) { + return true; + } if (!IsUnresolvedTypes()) { return GetClass()->CanAccessMember(klass, access_flags); } else { @@ -92,7 +98,7 @@ inline bool RegType::AssignableFrom(const RegType& lhs, LOG(WARNING) << "RegType::AssignableFrom lhs is Conflict!"; return false; case AssignmentType::kReference: - if (rhs.IsZero()) { + if (rhs.IsZeroOrNull()) { return true; // All reference types can be assigned null. } else if (!rhs.IsReferenceTypes()) { return false; // Expect rhs to be a reference type. @@ -119,6 +125,7 @@ inline bool RegType::AssignableFrom(const RegType& lhs, return result; } else { // Unresolved types are only assignable for null and equality. + // Null cannot be the left-hand side. return false; } case AssignmentType::kNotAssignable: @@ -199,6 +206,11 @@ inline const UndefinedType* UndefinedType::GetInstance() { return instance_; } +inline const NullType* NullType::GetInstance() { + DCHECK(instance_ != nullptr); + return instance_; +} + inline void* RegType::operator new(size_t size, ScopedArenaAllocator* allocator) { return allocator->Alloc(size, kArenaAllocMisc); } diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index 8df2e0f50b..309c374fa8 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -51,6 +51,7 @@ const LongHiType* LongHiType::instance_ = nullptr; const DoubleLoType* DoubleLoType::instance_ = nullptr; 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) : RegType(klass, descriptor, cache_id) { @@ -581,6 +582,10 @@ static const RegType& SelectNonConstant(const RegType& a, const RegType& b) { return a.IsConstantTypes() ? b : a; } +static const RegType& SelectNonConstant2(const RegType& a, const RegType& b) { + return a.IsConstantTypes() ? (b.IsZero() ? a : b) : a; +} + const RegType& RegType::Merge(const RegType& incoming_type, RegTypeCache* reg_types, MethodVerifier* verifier) const { @@ -695,8 +700,8 @@ const RegType& RegType::Merge(const RegType& incoming_type, // special. They may only ever be merged with themselves (must be taken care of by the // caller of Merge(), see the DCHECK on entry). So mark any other merge as conflicting here. return conflict; - } else if (IsZero() || incoming_type.IsZero()) { - return SelectNonConstant(*this, incoming_type); // 0 MERGE ref => ref + } else if (IsZeroOrNull() || incoming_type.IsZeroOrNull()) { + return SelectNonConstant2(*this, incoming_type); // 0 MERGE ref => ref } else if (IsJavaLangObject() || incoming_type.IsJavaLangObject()) { return reg_types->JavaLangObject(false); // Object MERGE ref => Object } else if (IsUnresolvedTypes() || incoming_type.IsUnresolvedTypes()) { @@ -965,6 +970,21 @@ 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 StringPiece& descriptor, + uint16_t cache_id) { + CHECK(instance_ == nullptr); + instance_ = new NullType(klass, descriptor, cache_id); + return instance_; +} + +void NullType::Destroy() { + if (NullType::instance_ != nullptr) { + delete instance_; + instance_ = nullptr; + } +} + } // namespace verifier } // namespace art diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h index a2085a3f09..9055849ca0 100644 --- a/runtime/verifier/reg_type.h +++ b/runtime/verifier/reg_type.h @@ -129,8 +129,12 @@ class RegType { virtual bool IsConstantShort() const { return false; } virtual bool IsOne() const { return false; } virtual bool IsZero() const { return false; } + virtual bool IsNull() const { return false; } bool IsReferenceTypes() const { - return IsNonZeroReferenceTypes() || IsZero(); + return IsNonZeroReferenceTypes() || IsZero() || IsNull(); + } + bool IsZeroOrNull() const { + return IsZero() || IsNull(); } virtual bool IsNonZeroReferenceTypes() const { return false; } bool IsCategory1Types() const { @@ -857,6 +861,46 @@ class ImpreciseConstHiType FINAL : public ConstantType { } }; +// Special "null" type that captures the semantics of null / bottom. +class NullType FINAL : public RegType { + public: + bool IsNull() const OVERRIDE { + return true; + } + + // Get the singleton Null instance. + static const NullType* GetInstance() PURE; + + // Create the singleton instance. + static const NullType* CreateInstance(mirror::Class* klass, + const StringPiece& descriptor, + uint16_t cache_id) + REQUIRES_SHARED(Locks::mutator_lock_); + + static void Destroy(); + + std::string Dump() const OVERRIDE { + return "null"; + } + + AssignmentType GetAssignmentTypeImpl() const OVERRIDE { + return AssignmentType::kReference; + } + + bool IsConstantTypes() const OVERRIDE { + return true; + } + + private: + NullType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) + REQUIRES_SHARED(Locks::mutator_lock_) + : RegType(klass, descriptor, cache_id) { + CheckConstructorInvariants(this); + } + + static const NullType* instance_; +}; + // Common parent of all uninitialized types. Uninitialized types are created by // "new" dex // instructions and must be passed to a constructor. diff --git a/runtime/verifier/reg_type_cache-inl.h b/runtime/verifier/reg_type_cache-inl.h index 197c97671e..61f34afdac 100644 --- a/runtime/verifier/reg_type_cache-inl.h +++ b/runtime/verifier/reg_type_cache-inl.h @@ -81,6 +81,9 @@ inline const UndefinedType& RegTypeCache::Undefined() { inline const ConflictType& RegTypeCache::Conflict() { return *ConflictType::GetInstance(); } +inline const NullType& RegTypeCache::Null() { + return *NullType::GetInstance(); +} inline const ImpreciseConstType& RegTypeCache::ByteConstant() { const ConstantType& result = FromCat1Const(std::numeric_limits::min(), false); diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index 4808381d5b..a3f08c8580 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -56,6 +56,7 @@ void RegTypeCache::FillPrimitiveAndSmallConstantTypes() { // Note: this must have the same order as CreatePrimitiveAndSmallConstantTypes. entries_.push_back(UndefinedType::GetInstance()); entries_.push_back(ConflictType::GetInstance()); + entries_.push_back(NullType::GetInstance()); entries_.push_back(BooleanType::GetInstance()); entries_.push_back(ByteType::GetInstance()); entries_.push_back(ShortType::GetInstance()); @@ -307,6 +308,7 @@ void RegTypeCache::ShutDown() { FloatType::Destroy(); DoubleLoType::Destroy(); DoubleHiType::Destroy(); + NullType::Destroy(); for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) { const PreciseConstType* type = small_precise_constants_[value - kMinSmallConstant]; delete type; @@ -354,6 +356,7 @@ void RegTypeCache::CreatePrimitiveAndSmallConstantTypes() { }; create_primitive_type_instance(TypeHelper("")); create_primitive_type_instance(TypeHelper("")); + create_primitive_type_instance(TypeHelper("")); create_primitive_type_instance(TypeHelper("Z")); create_primitive_type_instance(TypeHelper("B")); create_primitive_type_instance(TypeHelper("S")); diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h index 054a2af458..52776766bc 100644 --- a/runtime/verifier/reg_type_cache.h +++ b/runtime/verifier/reg_type_cache.h @@ -49,6 +49,7 @@ class IntegerType; class LongHiType; class LongLoType; class MethodVerifier; +class NullType; class PreciseConstType; class PreciseReferenceType; class RegType; @@ -123,6 +124,7 @@ class RegTypeCache { const DoubleHiType& DoubleHi() REQUIRES_SHARED(Locks::mutator_lock_); const UndefinedType& Undefined() REQUIRES_SHARED(Locks::mutator_lock_); const ConflictType& Conflict(); + const NullType& Null(); const PreciseReferenceType& JavaLangClass() REQUIRES_SHARED(Locks::mutator_lock_); const PreciseReferenceType& JavaLangString() REQUIRES_SHARED(Locks::mutator_lock_); @@ -180,7 +182,7 @@ class RegTypeCache { kMinSmallConstant + 1]; static constexpr size_t kNumPrimitivesAndSmallConstants = - 12 + (kMaxSmallConstant - kMinSmallConstant + 1); + 13 + (kMaxSmallConstant - kMinSmallConstant + 1); // Have the well known global primitives been created? static bool primitive_initialized_; diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc index 6edeecec02..15a38f3fd7 100644 --- a/runtime/verifier/reg_type_test.cc +++ b/runtime/verifier/reg_type_test.cc @@ -690,6 +690,8 @@ TEST_F(RegTypeTest, MergeSemiLatticeRef) { // | | | | | | | // | #---------------#--------#---------#--------#---------# // | | + // | null + // | | // #--------------------------#----------------------------# // | // 0 @@ -707,6 +709,7 @@ TEST_F(RegTypeTest, MergeSemiLatticeRef) { const RegType& conflict = cache.Conflict(); const RegType& zero = cache.Zero(); + const RegType& null = cache.Null(); const RegType& int_type = cache.Integer(); const RegType& obj = cache.JavaLangObject(false); @@ -799,6 +802,7 @@ TEST_F(RegTypeTest, MergeSemiLatticeRef) { plain_nonobj_arr_classes.begin(), plain_nonobj_arr_classes.end()); all_minus_uninit_conflict.push_back(&zero); + all_minus_uninit_conflict.push_back(&null); all_minus_uninit_conflict.push_back(&obj); std::vector all_minus_uninit; @@ -853,10 +857,12 @@ TEST_F(RegTypeTest, MergeSemiLatticeRef) { ADD_EDGE(int_type, conflict); } + ADD_EDGE(zero, null); + // Unresolved. { - ADD_EDGE(zero, unresolved_a); - ADD_EDGE(zero, unresolved_b); + ADD_EDGE(null, unresolved_a); + ADD_EDGE(null, unresolved_b); ADD_EDGE(unresolved_a, unresolved_ab); ADD_EDGE(unresolved_b, unresolved_ab); @@ -887,7 +893,7 @@ TEST_F(RegTypeTest, MergeSemiLatticeRef) { // Classes. { - ADD_EDGE(zero, integer); + ADD_EDGE(null, integer); ADD_EDGE(integer, number); ADD_EDGE(number, obj); } @@ -902,10 +908,10 @@ TEST_F(RegTypeTest, MergeSemiLatticeRef) { ADD_EDGE(char_arr, obj); ADD_EDGE(byte_arr, obj); - ADD_EDGE(zero, integer_arr); - ADD_EDGE(zero, number_arr_arr); - ADD_EDGE(zero, char_arr); - ADD_EDGE(zero, byte_arr); + ADD_EDGE(null, integer_arr); + ADD_EDGE(null, number_arr_arr); + ADD_EDGE(null, char_arr); + ADD_EDGE(null, byte_arr); } // Primitive. -- GitLab From 206070c99219584d9b873a8a17aad3a213128575 Mon Sep 17 00:00:00 2001 From: Mingyao Yang Date: Wed, 29 Nov 2017 23:01:58 -0800 Subject: [PATCH 116/226] Enhance removed loads/substitutes in LSE. LSE does load removal in the end in order to maintain the validity of all the heap locations collected in the first pass. We should however proactively retrieve a removed load's substitute as its value. This simplifies the handling of substitutes by making sure the substitute itself has no substitute. It also enables some additional optimizations by using the true heap value for merging/same-value test, etc. Test: run-test on host. Change-Id: I26d97e7794d80a141ab02bf446dd07656f5cde1d --- compiler/optimizing/load_store_elimination.cc | 38 ++++++++++--------- test/530-checker-lse/src/Main.java | 18 +++++++++ .../src/Main.java | 4 +- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 89ad85e0b4..f03fffabe2 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -74,6 +74,18 @@ class LSEVisitor : public HGraphDelegateVisitor { HGraphVisitor::VisitBasicBlock(block); } + // Find an instruction's substitute if it should be removed. + // Return the same instruction if it should not be removed. + HInstruction* FindSubstitute(HInstruction* instruction) { + size_t size = removed_loads_.size(); + for (size_t i = 0; i < size; i++) { + if (removed_loads_[i] == instruction) { + return substitute_instructions_for_loads_[i]; + } + } + return instruction; + } + // Remove recorded instructions that should be eliminated. void RemoveInstructions() { size_t size = removed_loads_.size(); @@ -86,12 +98,10 @@ class LSEVisitor : public HGraphDelegateVisitor { load->IsArrayGet()); HInstruction* substitute = substitute_instructions_for_loads_[i]; DCHECK(substitute != nullptr); - // Keep tracing substitute till one that's not removed. - HInstruction* sub_sub = FindSubstitute(substitute); - while (sub_sub != substitute) { - substitute = sub_sub; - sub_sub = FindSubstitute(substitute); - } + // We proactively retrieve the substitute for a removed load, so + // a load that has a substitute should not be observed as a heap + // location value. + DCHECK_EQ(FindSubstitute(substitute), substitute); load->ReplaceWith(substitute); load->GetBlock()->RemoveInstruction(load); } @@ -342,6 +352,8 @@ class LSEVisitor : public HGraphDelegateVisitor { DCHECK(ref_info->IsSingleton()); // Get the real heap value of the store. heap_value = heap_value->IsInstanceFieldSet() ? store->InputAt(1) : store->InputAt(2); + // heap_value may already have a substitute. + heap_value = FindSubstitute(heap_value); } } if (heap_value == kUnknownHeapValue) { @@ -385,6 +397,8 @@ class LSEVisitor : public HGraphDelegateVisitor { size_t vector_length, int16_t declaring_class_def_index, HInstruction* value) { + // 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( @@ -679,18 +693,6 @@ class LSEVisitor : public HGraphDelegateVisitor { } } - // Find an instruction's substitute if it should be removed. - // Return the same instruction if it should not be removed. - HInstruction* FindSubstitute(HInstruction* instruction) { - size_t size = removed_loads_.size(); - for (size_t i = 0; i < size; i++) { - if (removed_loads_[i] == instruction) { - return substitute_instructions_for_loads_[i]; - } - } - return instruction; - } - const HeapLocationCollector& heap_location_collector_; const SideEffectsAnalysis& side_effects_; diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java index c4cc3b0121..f6332b5503 100644 --- a/test/530-checker-lse/src/Main.java +++ b/test/530-checker-lse/src/Main.java @@ -999,6 +999,24 @@ public class Main { return res; } + /// CHECK-START: void Main.testStoreSameValue() load_store_elimination (before) + /// CHECK: NewArray + /// CHECK: ArrayGet + /// CHECK: ArraySet + + /// CHECK-START: void Main.testStoreSameValue() load_store_elimination (after) + /// CHECK: NewArray + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static void testStoreSameValue() { + Object[] array = new Object[2]; + sArray = array; + Object obj = array[0]; + array[1] = obj; // store the same value as the defaut value. + } + + static Object[] sArray; + static void assertIntEquals(int result, int expected) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); diff --git a/test/532-checker-nonnull-arrayset/src/Main.java b/test/532-checker-nonnull-arrayset/src/Main.java index 61c9e88e9e..f6f877c559 100644 --- a/test/532-checker-nonnull-arrayset/src/Main.java +++ b/test/532-checker-nonnull-arrayset/src/Main.java @@ -29,9 +29,7 @@ public class Main { /// CHECK-NOT: test /// CHECK: ReturnVoid public static void test() { - Object[] array = new Object[2]; - // Storing to static to avoid some lse optimization. - sArray = array; + Object[] array = sArray; Object nonNull = array[0]; nonNull.getClass(); // Ensure nonNull has an implicit null check. array[1] = nonNull; -- GitLab From fbaf29ea5915e4072584d2288fc8735d01c5b8d7 Mon Sep 17 00:00:00 2001 From: Mingyao Yang Date: Sun, 3 Dec 2017 23:25:28 -0800 Subject: [PATCH 117/226] Fix test failure of 638-checker-inline-cache-intrinsic JIT compilation is required for the test. So tracing can not be on. Test: 638-checker-inline-cache-intrinsic Change-Id: I0f2b17656c6dd1b66e3eeecde2e633acb04cc824 --- test/knownfailures.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/knownfailures.json b/test/knownfailures.json index 5b2ebf58a4..5cd788816a 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -237,7 +237,8 @@ "tests": ["604-hot-static-interface", "612-jit-dex-cache", "613-inlining-dex-cache", - "626-set-resolved-string"], + "626-set-resolved-string", + "638-checker-inline-cache-intrinsic"], "variant": "trace | stream", "description": ["These tests expect JIT compilation, which is", "suppressed when tracing."] -- GitLab From b7732a3fcf06a55075a601dbc593b23a6fc71dbf Mon Sep 17 00:00:00 2001 From: Richard Uhler Date: Thu, 16 Nov 2017 10:23:41 +0000 Subject: [PATCH 118/226] Add javadoc for all public ahat API. And tighten up the API a little more. The only functional change is to replace the Sort.WithPriority class with a Sort.withPriority function so that the Sort.WithPriority class can be made private. Test: m ahat-test Change-Id: Iba0ac04767a20c9008f209a777294ecaccc64ffc --- tools/ahat/Android.mk | 1 + tools/ahat/etc/ahat_api.txt | 23 +- .../ahat/src/main/com/android/ahat/Main.java | 11 + .../main/com/android/ahat/SiteHandler.java | 2 +- .../dominators/DominatorsComputation.java | 70 ++++-- .../ahat/heapdump/AhatArrayInstance.java | 21 +- .../ahat/heapdump/AhatClassInstance.java | 15 +- .../android/ahat/heapdump/AhatClassObj.java | 25 ++- .../com/android/ahat/heapdump/AhatHeap.java | 12 ++ .../android/ahat/heapdump/AhatInstance.java | 204 ++++++++++++++---- .../android/ahat/heapdump/AhatSnapshot.java | 56 ++++- .../main/com/android/ahat/heapdump/Diff.java | 20 +- .../com/android/ahat/heapdump/DiffFields.java | 12 +- .../com/android/ahat/heapdump/Diffable.java | 15 +- .../ahat/heapdump/DiffedFieldValue.java | 62 +++++- .../main/com/android/ahat/heapdump/Field.java | 16 ++ .../com/android/ahat/heapdump/FieldValue.java | 21 ++ .../ahat/heapdump/HprofFormatException.java | 4 + .../com/android/ahat/heapdump/Parser.java | 72 +++++-- .../android/ahat/heapdump/PathElement.java | 40 ++++ .../com/android/ahat/heapdump/RootType.java | 60 ++++++ .../main/com/android/ahat/heapdump/Site.java | 124 +++++++++-- .../main/com/android/ahat/heapdump/Size.java | 46 +++- .../main/com/android/ahat/heapdump/Sort.java | 74 +++++-- .../main/com/android/ahat/heapdump/Type.java | 45 ++++ .../main/com/android/ahat/heapdump/Value.java | 118 ++++++++-- .../android/ahat/proguard/ProguardMap.java | 105 ++++++++- 27 files changed, 1075 insertions(+), 199 deletions(-) diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk index a9a0492fe9..34e6a9cd42 100644 --- a/tools/ahat/Android.mk +++ b/tools/ahat/Android.mk @@ -23,6 +23,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-java-files-under, src/main) LOCAL_JAR_MANIFEST := etc/ahat.mf LOCAL_JAVA_RESOURCE_FILES := $(LOCAL_PATH)/etc/style.css +LOCAL_JAVACFLAGS := -Xdoclint:all/protected LOCAL_IS_HOST_MODULE := true LOCAL_MODULE_TAGS := optional LOCAL_MODULE := ahat diff --git a/tools/ahat/etc/ahat_api.txt b/tools/ahat/etc/ahat_api.txt index 7920adae55..93fe46bf8b 100644 --- a/tools/ahat/etc/ahat_api.txt +++ b/tools/ahat/etc/ahat_api.txt @@ -9,7 +9,6 @@ package com.android.ahat { package com.android.ahat.dominators { public class DominatorsComputation { - ctor public DominatorsComputation(); method public static void computeDominators(com.android.ahat.dominators.DominatorsComputation.Node); } @@ -109,7 +108,6 @@ package com.android.ahat.heapdump { } public class Diff { - ctor public Diff(); method public static void snapshots(com.android.ahat.heapdump.AhatSnapshot, com.android.ahat.heapdump.AhatSnapshot); } @@ -159,7 +157,6 @@ package com.android.ahat.heapdump { } public class Parser { - ctor public Parser(); method public static com.android.ahat.heapdump.AhatSnapshot parseHeapDump(java.io.File, com.android.ahat.proguard.ProguardMap) throws com.android.ahat.heapdump.HprofFormatException, java.io.IOException; method public static com.android.ahat.heapdump.AhatSnapshot parseHeapDump(java.nio.ByteBuffer, com.android.ahat.proguard.ProguardMap) throws com.android.ahat.heapdump.HprofFormatException, java.io.IOException; } @@ -210,11 +207,9 @@ package com.android.ahat.heapdump { } public static class Site.ObjectsInfo implements com.android.ahat.heapdump.Diffable { - ctor public Site.ObjectsInfo(com.android.ahat.heapdump.AhatHeap, com.android.ahat.heapdump.AhatClassObj); method public com.android.ahat.heapdump.Site.ObjectsInfo getBaseline(); method public java.lang.String getClassName(); method public boolean isPlaceHolder(); - method public void setBaseline(com.android.ahat.heapdump.Site.ObjectsInfo); field public com.android.ahat.heapdump.AhatClassObj classObj; field public com.android.ahat.heapdump.AhatHeap heap; field public com.android.ahat.heapdump.Size numBytes; @@ -236,6 +231,7 @@ package com.android.ahat.heapdump { ctor public Sort(); method public static java.util.Comparator defaultInstanceCompare(com.android.ahat.heapdump.AhatSnapshot); method public static java.util.Comparator defaultSiteCompare(com.android.ahat.heapdump.AhatSnapshot); + method public static java.util.Comparator withPriority(java.util.Comparator...); field public static final java.util.Comparator FIELD_VALUE_BY_NAME; field public static final java.util.Comparator FIELD_VALUE_BY_TYPE; field public static final java.util.Comparator INSTANCE_BY_TOTAL_RETAINED_SIZE; @@ -246,22 +242,6 @@ package com.android.ahat.heapdump { field public static final java.util.Comparator SIZE_BY_SIZE; } - public static class Sort.InstanceByHeapRetainedSize implements java.util.Comparator { - ctor public Sort.InstanceByHeapRetainedSize(com.android.ahat.heapdump.AhatHeap); - method public int compare(com.android.ahat.heapdump.AhatInstance, com.android.ahat.heapdump.AhatInstance); - } - - public static class Sort.SiteByHeapSize implements java.util.Comparator { - ctor public Sort.SiteByHeapSize(com.android.ahat.heapdump.AhatHeap); - method public int compare(com.android.ahat.heapdump.Site, com.android.ahat.heapdump.Site); - } - - public static class Sort.WithPriority implements java.util.Comparator { - ctor public Sort.WithPriority(java.util.Comparator...); - ctor public Sort.WithPriority(java.util.List>); - method public int compare(T, T); - } - public final class Type extends java.lang.Enum { method public static com.android.ahat.heapdump.Type valueOf(java.lang.String); method public static final com.android.ahat.heapdump.Type[] values(); @@ -285,7 +265,6 @@ package com.android.ahat.heapdump { method public java.lang.Integer asInteger(); method public java.lang.Long asLong(); method public abstract boolean equals(java.lang.Object); - method public com.android.ahat.heapdump.Value getBaseline(); method public static com.android.ahat.heapdump.Value getBaseline(com.android.ahat.heapdump.Value); method public static com.android.ahat.heapdump.Type getType(com.android.ahat.heapdump.Value); method public boolean isAhatInstance(); diff --git a/tools/ahat/src/main/com/android/ahat/Main.java b/tools/ahat/src/main/com/android/ahat/Main.java index 048573e915..04a6012a61 100644 --- a/tools/ahat/src/main/com/android/ahat/Main.java +++ b/tools/ahat/src/main/com/android/ahat/Main.java @@ -30,6 +30,9 @@ import java.net.InetSocketAddress; import java.text.ParseException; import java.util.concurrent.Executors; +/** + * Contains the main entry point for the ahat heap dump viewer. + */ public class Main { private Main() { } @@ -70,6 +73,14 @@ public class Main { throw new AssertionError("Unreachable"); } + /** + * Main entry for ahat heap dump viewer. + * Launches an http server on localhost for viewing a given heap dump. + * See the ahat README or pass "--help" as one of the arguments to see a + * description of what arguments and options are expected. + * + * @param args the command line arguments + */ public static void main(String[] args) { int port = 7100; for (String arg : args) { diff --git a/tools/ahat/src/main/com/android/ahat/SiteHandler.java b/tools/ahat/src/main/com/android/ahat/SiteHandler.java index 543eaa376a..5093f0d43e 100644 --- a/tools/ahat/src/main/com/android/ahat/SiteHandler.java +++ b/tools/ahat/src/main/com/android/ahat/SiteHandler.java @@ -88,7 +88,7 @@ class SiteHandler implements AhatHandler { new Column("Class")); List infos = site.getObjectsInfos(); - Comparator compare = new Sort.WithPriority( + Comparator compare = Sort.withPriority( Sort.OBJECTS_INFO_BY_HEAP_NAME, Sort.OBJECTS_INFO_BY_SIZE, Sort.OBJECTS_INFO_BY_CLASS_NAME); 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 58b7b59f9a..d3fea4869a 100644 --- a/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java +++ b/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java @@ -23,38 +23,72 @@ import java.util.List; import java.util.Queue; /** - * Generic DominatorsComputation. - * - * To use the dominators computation, have your graph nodes implement the - * DominatorsComputation.Node interface, then call - * DominatorsComputation.computeDominators on the single root node. + * Provides a static method for computing the immediate dominators of a + * directed graph. It can be used with any directed graph data structure + * that implements the {@link DominatorsComputation.Node} interface and has + * some root node with no incoming edges. */ public class DominatorsComputation { + private DominatorsComputation() { + } + /** - * Interface for a directed graph to perform the dominators computation on. + * Interface for a directed graph to perform immediate dominators + * computation on. + * The dominators computation can be used with directed graph data + * structures that implement this Node interface. To use the + * dominators computation on your graph, you must make the following + * functionality available to the dominators computation: + *
    + *
  • Efficiently mapping from node to associated internal dominators + * computation state using the + * {@link #setDominatorsComputationState setDominatorsComputationState} and + * {@link #getDominatorsComputationState getDominatorsComputationState} methods. + *
  • Iterating over all outgoing edges of an node using the + * {@link #getReferencesForDominators getReferencesForDominators} method. + *
  • Setting the computed dominator for a node using the + * {@link #setDominator setDominator} method. + *
*/ public interface Node { /** - * Associate the given dominator state with this node. + * Associates the given dominator state with this node. Subsequent calls to + * {@link #getDominatorsComputationState getDominatorsComputationState} on + * this node should return the state given here. At the conclusion of the + * dominators computation, this method will be called for + * each node with state set to null. + * + * @param state the dominator state to associate with this node */ void setDominatorsComputationState(Object state); /** - * Get the most recent dominator state associated with this node using - * setDominatorsComputationState. If setDominatorsComputationState has not - * yet been called, this should return null. + * Returns the dominator state most recently associated with this node + * by a call to {@link #setDominatorsComputationState setDominatorsComputationState}. + * If setDominatorsComputationState has not yet been called + * on this node for this dominators computation, this method should return + * null. + * + * @return the associated dominator state */ Object getDominatorsComputationState(); /** - * Return a collection of nodes referenced from this node, for the - * purposes of computing dominators. + * Returns a collection of nodes referenced from this node, for the + * purposes of computing dominators. This method will be called at most + * once for each node reachable from the root node of the dominators + * computation. + * + * @return an iterable collection of the nodes with an incoming edge from + * this node. */ Iterable getReferencesForDominators(); /** - * Update this node's dominator based on the results of the dominators + * Sets the dominator for this node based on the results of the dominators * computation. + * + * @param dominator the computed immediate dominator of this node */ void setDominator(Node dominator); } @@ -112,8 +146,14 @@ public class DominatorsComputation { } /** - * Compute the dominator tree rooted at the given node. - * There must not be any incoming references to the root node. + * Computes the immediate dominators of all nodes reachable from the root node. + * There must not be any incoming references to the root node. + *

+ * The result of this function is to call the {@link Node#setDominator} + * function on every node reachable from the root node. + * + * @param root the root node of the dominators computation + * @see Node */ public static void computeDominators(Node root) { long id = 0; diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java index ccdd6e4df7..9c80802673 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java @@ -21,6 +21,12 @@ import java.util.AbstractList; import java.util.Collections; import java.util.List; +/** + * An array instance from a parsed heap dump. + * It is used for both object and primitive arrays. The class provides methods + * for accessing the length and elements of the array in addition to those + * methods inherited from {@link AhatInstance}. + */ public class AhatArrayInstance extends AhatInstance { // To save space, we store arrays as primitive arrays or AhatInstance arrays // and provide a wrapper over the arrays to expose a list of Values. @@ -186,21 +192,30 @@ public class AhatArrayInstance extends AhatInstance { } /** - * Returns the length of the array. + * Returns the number of elements in the array. + * + * @return number of elements in the array. */ public int getLength() { return mValues.size(); } /** - * Returns the array's values. + * Returns a list of all of the array's elements in order. + * The returned list does not support modification. + * + * @return list of the array's elements. */ public List getValues() { return mValues; } /** - * Returns the object at the given index of this array. + * Returns the value at the given index of this array. + * + * @param index the index of the value to retrieve + * @return the value at the given index + * @throws IndexOutOfBoundsException if the index is out of range */ public Value getValue(int index) { return mValues.get(index); diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java index cb9d959508..c82ef20e9b 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java @@ -20,6 +20,15 @@ import java.awt.image.BufferedImage; import java.util.Iterator; import java.util.NoSuchElementException; +/** + * A typical Java object from a parsed heap dump. + * Note that this is used for Java objects that are instances of classes (as + * opposed to arrays), not for class objects themselves. + * See {@link AhatClassObj } for the representation of class objects. + *

+ * This class provides a method for iterating over the instance fields of the + * object in addition to those methods inherited from {@link AhatInstance}. + */ public class AhatClassInstance extends AhatInstance { // Instance fields of the object. These are stored in order of the instance // field descriptors from the class object, starting with this class first, @@ -84,6 +93,10 @@ public class AhatClassInstance extends AhatInstance { /** * Returns the list of class instance fields for this instance. + * Includes values of field inherited from the superclass of this instance. + * The fields are returned in no particular order. + * + * @return Iterable over the instance field values. */ public Iterable getInstanceFields() { return new InstanceFieldIterator(mFields, getClassObj()); @@ -220,7 +233,7 @@ public class AhatClassInstance extends AhatInstance { } - public BufferedImage asBitmap() { + @Override public BufferedImage asBitmap() { BitmapInfo info = getBitmapInfo(); if (info == null) { return null; diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java index 3babf76842..36ada2857c 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java @@ -20,6 +20,13 @@ import java.util.AbstractList; import java.util.Arrays; import java.util.List; +/** + * A class from a parsed heap dump. + * In addition to those methods inherited from {@link AhatInstance}, the class + * provides methods for accessing information about the class object, such as + * the class loader, superclass, static field values and instance field + * descriptors. + */ public class AhatClassObj extends AhatInstance { private String mClassName; private AhatClassObj mSuperClassObj; @@ -56,6 +63,9 @@ public class AhatClassObj extends AhatInstance { /** * Returns the name of the class this is a class object for. + * For example, "java.lang.String". + * + * @return the name of the class */ public String getName() { return mClassName; @@ -63,6 +73,8 @@ public class AhatClassObj extends AhatInstance { /** * Returns the superclass of this class object. + * + * @return the superclass object */ public AhatClassObj getSuperClassObj() { return mSuperClassObj; @@ -70,14 +82,18 @@ public class AhatClassObj extends AhatInstance { /** * Returns the class loader of this class object. + * + * @return the class loader object */ public AhatInstance getClassLoader() { return mClassLoader; } /** - * Returns the size of instances of this object, as reported in the heap - * dump. + * Returns the size of instances of this object. + * The size returned is as reported in the heap dump. + * + * @return the class instance size */ public long getInstanceSize() { return mInstanceSize; @@ -85,6 +101,8 @@ public class AhatClassObj extends AhatInstance { /** * Returns the static field values for this class object. + * + * @return the static field values */ public List getStaticFieldValues() { return Arrays.asList(mStaticFieldValues); @@ -92,6 +110,9 @@ public class AhatClassObj extends AhatInstance { /** * Returns the fields of instances of this class. + * Does not include fields from the super class of this class. + * + * @return the instance fields */ public Field[] getInstanceFields() { return mInstanceFields; diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatHeap.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatHeap.java index b8897a182c..60c9a0d086 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatHeap.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatHeap.java @@ -16,6 +16,13 @@ package com.android.ahat.heapdump; +/** + * Used to identify and access basic information about a particular + * heap from the heap dump. Standard Java heap dumps have a single heap, + * called the "default" heap. Android heap dumps distinguish among "zygote", + * "image", and "app" heaps. There will be a single instance of AhatHeap for + * each different heap in the heap dump. + */ public class AhatHeap implements Diffable { private String mName; private Size mSize = Size.ZERO; @@ -61,6 +68,9 @@ public class AhatHeap implements Diffable { /** * Returns the name of this heap. + * For example, "default", "app", "image", or "zygote". + * + * @return The name of the heap. */ public String getName() { return mName; @@ -68,6 +78,8 @@ public class AhatHeap implements Diffable { /** * Returns the total number of bytes allocated on this heap. + * + * @return the total number of bytes allocated on this heap. */ public Size getSize() { return mSize; diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java index a9f819f710..67253bf0e7 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java @@ -26,6 +26,11 @@ import java.util.Deque; import java.util.List; import java.util.Queue; +/** + * A Java instance from a parsed heap dump. It is the base class used for all + * kinds of Java instances, including normal Java objects, class objects, and + * arrays. + */ public abstract class AhatInstance implements Diffable, DominatorsComputation.Node { // The id of this instance from the heap dump. @@ -80,14 +85,20 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns a unique identifier for the instance. + * Returns a unique identifier for this instance. + * + * @return id of the instance */ public long getId() { return mId; } /** - * Returns the shallow number of bytes this object takes up. + * Returns the number of bytes used for this object in the heap. + * The returned size is a shallow size for the object that does not include + * sizes of other objects dominated by this object. + * + * @return the shallow size of the object */ public Size getSize() { return new Size(mClassObj.getInstanceSize() + getExtraJavaSize(), mRegisteredNativeSize); @@ -104,8 +115,13 @@ public abstract class AhatInstance implements Diffable, abstract long getExtraJavaSize(); /** - * Returns the number of bytes belonging to the given heap that this instance - * retains. + * Returns the number of bytes retained by this object in the given heap. + * The returned size includes the shallow size of this object and the size + * of all objects directly or indirectly retained by this object. Only those + * objects allocated on the given heap are included in the reported size. + * + * @param heap the heap to get the retained size for + * @return the retained size of the object */ public Size getRetainedSize(AhatHeap heap) { int index = heap.getIndex(); @@ -116,7 +132,11 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns the total number of bytes this instance retains. + * Returns the total number of bytes retained by this object. The returned + * size includes the shallow size of this object and the size of all objects + * directly or indirectly retained by this object. + * + * @return the total retained size of the object */ public Size getTotalRetainedSize() { Size size = Size.ZERO; @@ -136,7 +156,11 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns true if this object is strongly-reachable. + * Returns true if this object is strongly reachable. An object is strongly + * reachable if there exists a path of (strong) references from some root + * object to this object. + * + * @return true if the object is strongly reachable */ public boolean isStronglyReachable() { return mImmediateDominator != null; @@ -144,14 +168,28 @@ public abstract class AhatInstance implements Diffable, /** * Returns true if this object is reachable only through a - * soft/weak/phantom/finalizer reference. + * soft/weak/phantom/finalizer reference. An object is weakly reachable if + * it is not strongly reachable but there still exists a path of references + * from some root object to this object. Because the object is not strongly + * reachable, any such path must contain a SoftReference, WeakReference, + * PhantomReference, or FinalizerReference somewhere along it. + *

+ * Unlike a strongly reachable object, a weakly reachable object is allowed + * to be garbage collected. + * + * @return true if the object is weakly reachable */ public boolean isWeaklyReachable() { return !isStronglyReachable() && mNextInstanceToGcRoot != null; } /** - * Returns true if this object is completely unreachable. + * Returns true if this object is completely unreachable. An object is + * completely unreachable if there is no path to the object from some root + * object, neither through strong nor soft/weak/phantom/finalizer + * references. + * + * @return true if the object is completely unreachable */ public boolean isUnreachable() { return !isStronglyReachable() && !isWeaklyReachable(); @@ -159,6 +197,8 @@ public abstract class AhatInstance implements Diffable, /** * Returns the heap that this instance is allocated on. + * + * @return heap the instance is allocated on */ public AhatHeap getHeap() { return mHeap; @@ -171,7 +211,10 @@ public abstract class AhatInstance implements Diffable, abstract Iterable getReferences(); /** - * Returns true if this instance is marked as a root instance. + * Returns true if this instance is a GC root. + * + * @return true if this instance is a GC root. + * @see getRootTypes */ public boolean isRoot() { return mRootTypes != 0; @@ -187,6 +230,8 @@ public abstract class AhatInstance implements Diffable, /** * Returns a list of the root types of this object. * Returns null if this object is not a root. + * + * @return list of the objects root types */ public Collection getRootTypes() { if (!isRoot()) { @@ -205,14 +250,17 @@ public abstract class AhatInstance implements Diffable, /** * Returns the immediate dominator of this instance. * Returns null if this is a root instance. + * + * @return the immediate dominator of this instance */ public AhatInstance getImmediateDominator() { return mImmediateDominator; } /** - * Returns a list of those objects immediately dominated by the given - * instance. + * Returns a list of objects immediately dominated by this instance. + * + * @return list of immediately dominated objects */ public List getDominated() { return mDominated; @@ -220,13 +268,17 @@ public abstract class AhatInstance implements Diffable, /** * Returns the site where this instance was allocated. + * + * @return the object's allocation site */ public Site getSite() { return mSite; } /** - * Returns true if the given instance is a class object + * Returns true if this instance is a class object + * + * @return true if this instance is a class object */ public boolean isClassObj() { // Overridden by AhatClassObj. @@ -236,6 +288,8 @@ public abstract class AhatInstance implements Diffable, /** * Returns this as an AhatClassObj if this is an AhatClassObj. * Returns null if this is not an AhatClassObj. + * + * @return this instance as a class object */ public AhatClassObj asClassObj() { // Overridden by AhatClassObj. @@ -243,7 +297,11 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns the class object instance for the class of this object. + * Returns the class object for this instance. + * For example, if this object is an instance of java.lang.String, this + * method returns the AhatClassObj for java.lang.String. + * + * @return the instance's class object */ public AhatClassObj getClassObj() { return mClassObj; @@ -251,6 +309,10 @@ public abstract class AhatInstance implements Diffable, /** * Returns the name of the class this object belongs to. + * For example, if this object is an instance of java.lang.String, returns + * "java.lang.String". + * + * @return the name of this instance's class */ public String getClassName() { AhatClassObj classObj = getClassObj(); @@ -258,7 +320,9 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns true if the given instance is an array instance + * Returns true if the given instance is an array instance. + * + * @return true if the given instance is an array instance */ public boolean isArrayInstance() { // Overridden by AhatArrayInstance. @@ -268,6 +332,8 @@ public abstract class AhatInstance implements Diffable, /** * Returns this as an AhatArrayInstance if this is an AhatArrayInstance. * Returns null if this is not an AhatArrayInstance. + * + * @return this instance as an array instance */ public AhatArrayInstance asArrayInstance() { // Overridden by AhatArrayInstance. @@ -275,7 +341,9 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns true if the given instance is a class instance + * Returns true if this instance is a class instance. + * + * @return true if this instance is a class instance */ public boolean isClassInstance() { return false; @@ -284,15 +352,20 @@ public abstract class AhatInstance implements Diffable, /** * Returns this as an AhatClassInstance if this is an AhatClassInstance. * Returns null if this is not an AhatClassInstance. + * + * @return this instance as a class instance */ public AhatClassInstance asClassInstance() { return null; } /** - * Return the referent associated with this instance. - * This is relevent for instances of java.lang.ref.Reference. - * Returns null if the instance has no referent associated with it. + * Returns the referent associated with this instance. + * This is only relevant for instances of java.lang.ref.Reference or its + * subclasses. Returns null if the instance has no referent associated with + * it. + * + * @return the referent associated with this instance */ public AhatInstance getReferent() { // Overridden by AhatClassInstance. @@ -300,7 +373,9 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns a list of objects with hard references to this object. + * Returns a list of objects with (strong) references to this object. + * + * @return the objects referencing this object */ public List getHardReverseReferences() { if (mHardReverseReferences != null) { @@ -310,7 +385,10 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns a list of objects with soft references to this object. + * Returns a list of objects with soft/weak/phantom/finalizer references to + * this object. + * + * @return the objects weakly referencing this object */ public List getSoftReverseReferences() { if (mSoftReverseReferences != null) { @@ -320,9 +398,12 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns the value of a field of an instance. - * Returns null if the field value is null, the field couldn't be read, or - * there are multiple fields with the same name. + * Returns the value of a field of this instance. Returns null if the field + * value is null, the field couldn't be read, or there are multiple fields + * with the same name. + * + * @param fieldName the name of the field to get the value of + * @return the field value */ public Value getField(String fieldName) { // Overridden by AhatClassInstance. @@ -330,8 +411,13 @@ public abstract class AhatInstance implements Diffable, } /** - * Reads a reference field of this instance. - * Returns null if the field value is null, or if the field couldn't be read. + * Reads a reference field of this instance. Returns null if the field value + * is null, of primitive type, or if the field couldn't be read. There is no + * way using this method to distinguish between a reference field with value + * null and an invalid field. + * + * @param fieldName the name of the reference field to get the value of + * @return the reference field value */ public AhatInstance getRefField(String fieldName) { // Overridden by AhatClassInstance. @@ -339,30 +425,41 @@ public abstract class AhatInstance implements Diffable, } /** - * Assuming inst represents a DexCache object, return the dex location for - * that dex cache. Returns null if the given instance doesn't represent a - * DexCache object or the location could not be found. + * Returns the dex location associated with this object. Only applies to + * instances of dalvik.system.DexCache. If this is an instance of DexCache, + * returns the dex location for that dex cache. Otherwise returns null. * If maxChars is non-negative, the returned location is truncated to * maxChars in length. + * + * @param maxChars the maximum length of the returned string + * @return the dex location associated with this object */ public String getDexCacheLocation(int maxChars) { return null; } /** - * Return the bitmap instance associated with this object, or null if there - * is none. This works for android.graphics.Bitmap instances and their - * underlying Byte[] instances. + * Returns the android.graphics.Bitmap instance associated with this object. + * Instances of android.graphics.Bitmap return themselves. If this is a + * byte[] array containing pixel data for an instance of + * android.graphics.Bitmap, that instance of android.graphics.Bitmap is + * returned. Otherwise null is returned. + * + * @return the bitmap instance associated with this object */ public AhatInstance getAssociatedBitmapInstance() { return null; } /** - * Read the string value from this instance. - * Returns null if this object can't be interpreted as a string. - * The returned string is truncated to maxChars characters. - * If maxChars is negative, the returned string is not truncated. + * Returns the (bounded-length) string associated with this instance. + * Applies to instances of java.lang.String, char[], and in some cases + * byte[]. Returns null if this object cannot be interpreted as a string. + * If maxChars is non-negative, the returned string is truncated to maxChars + * characters in length. + * + * @param maxChars the maximum length of the returned string + * @return the string associated with this instance */ public String asString(int maxChars) { // By default instances can't be interpreted as a string. This method is @@ -372,17 +469,23 @@ public abstract class AhatInstance implements Diffable, } /** - * Reads the string value from an hprof Instance. - * Returns null if the object can't be interpreted as a string. + * Returns the string associated with this instance. Applies to instances of + * java.lang.String, char[], and in some cases byte[]. Returns null if this + * object cannot be interpreted as a string. + * + * @return the string associated with this instance */ public String asString() { return asString(-1); } /** - * Return the bitmap associated with the given instance, if any. + * Returns the bitmap pixel data associated with this instance. * This is relevant for instances of android.graphics.Bitmap and byte[]. - * Returns null if there is no bitmap associated with the given instance. + * Returns null if there is no bitmap pixel data associated with the given + * instance. + * + * @return the bitmap pixel data associated with this image */ public BufferedImage asBitmap() { return null; @@ -402,11 +505,23 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns a sample path from a GC root to this instance. - * This instance is included as the last element of the path with an empty - * field description. + * Returns a sample path from a GC root to this instance. The first element + * of the returned path is a GC root object. This instance is included as + * the last element of the path with an empty field description. + *

+ * If the instance is strongly reachable, a path of string references will + * be returned. If the instance is weakly reachable, the returned path will + * include a soft/weak/phantom/finalizer reference somewhere along it. + * Returns null if this instance is not reachable. + * + * @return sample path from a GC root to this instance + * @see PathElement */ public List getPathFromGcRoot() { + if (isUnreachable()) { + return null; + } + List path = new ArrayList(); AhatInstance dom = this; @@ -434,12 +549,15 @@ public abstract class AhatInstance implements Diffable, return new PathElement(inst.mNextInstanceToGcRoot, inst.mNextInstanceToGcRootField); } - /** Returns a human-readable identifier for this object. + /** + * Returns a human-readable identifier for this object. * For class objects, the string is the class name. * For class instances, the string is the class name followed by '@' and the * hex id of the instance. * For array instances, the string is the array type followed by the size in * square brackets, followed by '@' and the hex id of the instance. + * + * @return human-readable identifier for this object */ @Override public abstract String toString(); diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java index 59ce5d1c6c..535db082c1 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java @@ -19,6 +19,11 @@ package com.android.ahat.heapdump; import com.android.ahat.dominators.DominatorsComputation; import java.util.List; +/** + * A parsed heap dump. + * It contains methods to access the heaps, allocation sites, roots, classes, + * and instances from the parsed heap dump. + */ public class AhatSnapshot implements Diffable { private final Site mRootSite; @@ -60,16 +65,24 @@ public class AhatSnapshot implements Diffable { } /** - * Returns the instance with given id in this snapshot. + * Returns the instance with the given id in this snapshot. + * Where the id of an instance x is x.getId(). * Returns null if no instance with the given id is found. + * + * @param id the id of the instance to find + * @return the instance with the given id */ public AhatInstance findInstance(long id) { return mInstances.get(id); } /** - * Returns the AhatClassObj with given id in this snapshot. + * Returns the AhatClassObj with the given id in this snapshot. + * Where the id of a class object x is x.getId(). * Returns null if no class object with the given id is found. + * + * @param id the id of the class object to find + * @return the class object with the given id */ public AhatClassObj findClassObj(long id) { AhatInstance inst = findInstance(id); @@ -77,8 +90,12 @@ public class AhatSnapshot implements Diffable { } /** - * Returns the heap with the given name, if any. + * Returns the heap with the given name. + * Where the name of a heap x is x.getName(). * Returns null if no heap with the given name could be found. + * + * @param name the name of the heap to get + * @return the heap with the given name */ public AhatHeap getHeap(String name) { // We expect a small number of heaps (maybe 3 or 4 total), so a linear @@ -93,30 +110,45 @@ public class AhatSnapshot implements Diffable { /** * Returns a list of heaps in the snapshot in canonical order. - * Modifications to the returned list are visible to this AhatSnapshot, - * which is used by diff to insert place holder heaps. + *

+ * Note: modifications to the returned list are visible to this + * AhatSnapshot, which is used by diff to insert place holder heaps. + * + * @return list of heaps */ public List getHeaps() { return mHeaps; } /** - * Returns a collection of instances whose immediate dominator is the - * SENTINEL_ROOT. + * Returns a collection of "rooted" instances. + * An instance is "rooted" if it is a GC root, or if it is retained by more + * than one GC root. These are reachable instances that are not immediately + * dominated by any other instance in the heap. + * + * @return collection of rooted instances */ public List getRooted() { return mSuperRoot.getDominated(); } /** - * Returns the root site for this snapshot. + * Returns the root allocation site for this snapshot. + * + * @return the root allocation site */ public Site getRootSite() { return mRootSite; } - // Get the site associated with the given id. - // Returns the root site if no such site found. + /** + * Returns the site associated with the given id. + * Where the id of a site x is x.getId(). + * Returns the root site if no site with the given id is found. + * + * @param id the id of the site to get + * @return the site with the given id + */ public Site getSite(long id) { Site site = mRootSite.findSite(id); return site == null ? mRootSite : site; @@ -127,8 +159,10 @@ public class AhatSnapshot implements Diffable { } /** - * Returns true if this snapshot has been diffed against another, different + * Returns true if this snapshot has been diffed against a different * snapshot. + * + * @return true if the snapshot has been diffed */ public boolean isDiffed() { return mBaseline != this; diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Diff.java b/tools/ahat/src/main/com/android/ahat/heapdump/Diff.java index 98c7e58d56..b35b4244ae 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Diff.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Diff.java @@ -25,9 +25,15 @@ import java.util.List; import java.util.Map; import java.util.Objects; +/** + * Provides a static method to diff two heap dumps. + */ public class Diff { + private Diff() { + } + /** - * Perform a diff between two heap lists. + * Performs a diff between two heap lists. * * Heaps are diffed based on heap name. PlaceHolder heaps will be added to * the given lists as necessary so that every heap in A has a corresponding @@ -312,8 +318,16 @@ public class Diff { } /** - * Perform a diff of the two snapshots, setting each as the baseline for the - * other. + * Performs a diff of two snapshots. + * Each snapshot will be set as the baseline for the other snapshot. + *

+ * The diff algorithm attempts to match instances in snapshot a + * to corresponding instances in snapshot b. The snapshots need + * not come from the same running process, application version, or platform + * version. + * + * @param a one of the snapshots to diff + * @param b the other of the snapshots to diff */ public static void snapshots(AhatSnapshot a, AhatSnapshot b) { a.setBaseline(b); diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/DiffFields.java b/tools/ahat/src/main/com/android/ahat/heapdump/DiffFields.java index e3c671fe21..ff07af0028 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/DiffFields.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/DiffFields.java @@ -22,12 +22,16 @@ import java.util.Comparator; import java.util.List; /** - * This class contains a routine for diffing two collections of static or - * instance fields. + * Provides a routine for diffing two collections of static or instance + * fields. */ public class DiffFields { /** - * Return the result of diffing two collections of field values. + * Returns the result of diffing two collections of field values. + * + * @param current a list of fields in the current heap dump + * @param baseline a list of fields in the baseline heap dump + * @return list of diffed fields */ public static List diff(Iterable current, Iterable baseline) { @@ -85,5 +89,5 @@ public class DiffFields { * by field name and type. */ private static final Comparator FOR_DIFF - = new Sort.WithPriority(Sort.FIELD_VALUE_BY_NAME, Sort.FIELD_VALUE_BY_TYPE); + = Sort.withPriority(Sort.FIELD_VALUE_BY_NAME, Sort.FIELD_VALUE_BY_TYPE); } diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Diffable.java b/tools/ahat/src/main/com/android/ahat/heapdump/Diffable.java index 53442c857e..09c8ee6d39 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Diffable.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Diffable.java @@ -17,12 +17,19 @@ package com.android.ahat.heapdump; /** - * An interface for objects that have corresponding objects in a baseline heap - * dump. + * An interface for instances/sites/heaps/etc in a heap dump that can be + * related to corresponding instances/sites/heaps/etc in a second heap dump + * when the two heap dumps have been diffed. */ public interface Diffable { /** - * Return the baseline object that corresponds to this one. + * Returns the object in the other heap dump that corresponds to this object. + * When two heap dumps are diffed, diffable objects from the first heap dump + * will be matched to "baseline" objects from the second heap dump, and + * diffable objects from the second heap dump will be matched to "baseline" + * objects from the first heap dump. + * + * @return the matched object from the other heap dump */ T getBaseline(); @@ -32,6 +39,8 @@ public interface Diffable { * baseline heap dump that is not in this heap dump. In that case, we create * a dummy place holder object in this heap dump as an indicator of the * object removed from the baseline heap dump. + * + * @return true if the object is a placeholder */ boolean isPlaceHolder(); } diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/DiffedFieldValue.java b/tools/ahat/src/main/com/android/ahat/heapdump/DiffedFieldValue.java index 3cd273ed98..8de337ea8c 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/DiffedFieldValue.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/DiffedFieldValue.java @@ -18,25 +18,65 @@ package com.android.ahat.heapdump; import java.util.Objects; -/** DiffedFieldValue is used by the DiffedField class to return the result of - * diffing two collections of fields. +/** + * Used by the DiffedField class to return the result of diffing two + * collections of fields. */ public class DiffedFieldValue { + /** + * The name of the field. + */ public final String name; + + /** + * The type of the field. + */ public final Type type; + + /** + * The value of the field in the current heap dump. + */ public final Value current; + + /** + * The value of the field in the baseline heap dump. + */ public final Value baseline; + /** + * Whether the field was added to, deleted from, or matched with a field in + * the baseline heap dump. + */ public final Status status; + /** + * A status enum to indicate whether a field was added to, deleted from, or + * matched with a field in the baseline heap dump. + */ public static enum Status { - ADDED, // The current field has no matching baseline value. - MATCHED, // The current field has a matching baseline value. - DELETED // The baseline field has no matching current value. + /** + * The field exists in the current heap dump but not the baseline. + */ + ADDED, + + /** + * The field exists in both the current and baseline heap dumps. + */ + MATCHED, + + /** + * The field exists in the baseline heap dump but not the current. + */ + DELETED }; /** - * Return a DiffedFieldValue where there is both a current and baseline. + * Constructs a DiffedFieldValue where there are both current and baseline + * fields. + * + * @param current the current field + * @param baseline the baseline field + * @return the constructed DiffedFieldValue */ public static DiffedFieldValue matched(FieldValue current, FieldValue baseline) { return new DiffedFieldValue(current.name, @@ -47,14 +87,20 @@ public class DiffedFieldValue { } /** - * Return a DiffedFieldValue where there is no baseline. + * Constructs a DiffedFieldValue where there is no baseline field. + * + * @param current the current field + * @return the constructed DiffedFieldValue */ public static DiffedFieldValue added(FieldValue current) { return new DiffedFieldValue(current.name, current.type, current.value, null, Status.ADDED); } /** - * Return a DiffedFieldValue where there is no current. + * Constructs a DiffedFieldValue where there is no current field. + * + * @param baseline the baseline field + * @return the constructed DiffedFieldValue */ public static DiffedFieldValue deleted(FieldValue baseline) { return new DiffedFieldValue(baseline.name, baseline.type, null, baseline.value, Status.DELETED); diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Field.java b/tools/ahat/src/main/com/android/ahat/heapdump/Field.java index dff401796a..6494069dcb 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Field.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Field.java @@ -16,10 +16,26 @@ package com.android.ahat.heapdump; +/** + * A description of a field from a heap dump. + */ public class Field { + /** + * The name of the field. + */ public final String name; + + /** + * The type of the field. + */ public final Type type; + /** + * Constructs a Field instance. + * + * @param name name of the field + * @param type type of the field + */ public Field(String name, Type type) { this.name = name; this.type = type; diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/FieldValue.java b/tools/ahat/src/main/com/android/ahat/heapdump/FieldValue.java index 20e6da7271..70314da830 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/FieldValue.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/FieldValue.java @@ -16,11 +16,32 @@ package com.android.ahat.heapdump; +/** + * A description and value of a field from a heap dump. + */ public class FieldValue { + /** + * The name of the field. + */ public final String name; + + /** + * The type of the field. + */ public final Type type; + + /** + * The value of the field. + */ public final Value value; + /** + * Constructs an instance of FieldValue. + * + * @param name name of the field + * @param type type of the field + * @param value value of the field + */ public FieldValue(String name, Type type, Value value) { this.name = name; this.type = type; diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/HprofFormatException.java b/tools/ahat/src/main/com/android/ahat/heapdump/HprofFormatException.java index 256a3b46f6..29ac9b0c5a 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/HprofFormatException.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/HprofFormatException.java @@ -16,6 +16,10 @@ package com.android.ahat.heapdump; +/** + * Exception thrown when the heap dump parser detects an improperly formatted + * heap dump file. + */ public class HprofFormatException extends Exception { HprofFormatException(String msg) { super(msg); diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java b/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java index d7b1dd78d6..58bd373294 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java @@ -31,21 +31,43 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +/** + * Provides methods for parsing heap dumps. + */ public class Parser { private static final int ID_SIZE = 4; + private Parser() { + } + /** - * Parse the given heap dump using the given proguard map for deobfuscation. - * We make the following assumptions about valid heap dumps: - * Class serial numbers, stack frames, and stack traces - * individually satisfy the following: - * - all elements are defined before they are referenced. - * - ids are densely packed in some range [a, b] where a is not - * necessarily 0. - * - there are not more than 2^31 elements defined. - * All classes are defined via a LOAD CLASS record before the first heap - * dump segment. - * The ID size used in the heap dump is 4 bytes. + * Parses a heap dump from a File. + *

+ * The heap dump should be a heap dump in the J2SE HPROF format optionally + * with Android extensions and satisfying the following additional + * constraints: + *

    + *
  • + * Class serial numbers, stack frames, and stack traces individually satisfy + * the following: + *
      + *
    • All elements are defined before they are referenced. + *
    • Ids are densely packed in some range [a, b] where a is not necessarily 0. + *
    • There are not more than 2^31 elements defined. + *
    + *
  • All classes are defined via a LOAD CLASS record before the first + * heap dump segment. + *
  • The ID size used in the heap dump is 4 bytes. + *
+ *

+ * The given proguard map will be used to deobfuscate class names, field + * names, and stack traces in the heap dump. + * + * @param hprof the hprof file to parse + * @param map the proguard map for deobfuscation + * @return the parsed heap dump + * @throws IOException if the heap dump could not be read + * @throws HprofFormatException if the heap dump is not properly formatted */ public static AhatSnapshot parseHeapDump(File hprof, ProguardMap map) throws IOException, HprofFormatException { @@ -57,7 +79,33 @@ public class Parser { } /** - * Parse a heap dump from a byte buffer. + * Parses a heap dump from a byte buffer. + *

+ * The heap dump should be a heap dump in the J2SE HPROF format optionally + * with Android extensions and satisfying the following additional + * constraints: + *

    + *
  • + * Class serial numbers, stack frames, and stack traces individually satisfy + * the following: + *
      + *
    • All elements are defined before they are referenced. + *
    • Ids are densely packed in some range [a, b] where a is not necessarily 0. + *
    • There are not more than 2^31 elements defined. + *
    + *
  • All classes are defined via a LOAD CLASS record before the first + * heap dump segment. + *
  • The ID size used in the heap dump is 4 bytes. + *
+ *

+ * The given proguard map will be used to deobfuscate class names, field + * names, and stack traces in the heap dump. + * + * @param hprof the bytes of the hprof file to parse + * @param map the proguard map for deobfuscation + * @return the parsed heap dump + * @throws IOException if the heap dump could not be read + * @throws HprofFormatException if the heap dump is not properly formatted */ public static AhatSnapshot parseHeapDump(ByteBuffer hprof, ProguardMap map) throws IOException, HprofFormatException { diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/PathElement.java b/tools/ahat/src/main/com/android/ahat/heapdump/PathElement.java index 196a24628c..5ce0b1edfe 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/PathElement.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/PathElement.java @@ -16,11 +16,51 @@ package com.android.ahat.heapdump; +/** + * A single element along a reference path from a GC root to an instance in + * the heap dump. + *

+ * For example, assuming object A is a root a path to some object X might look + * like: + *

+ *   A.x --> B.y --> C.z --> X
+ * 
+ * + * A path element is a single node of that path, such as B.y. + * @see AhatInstance#getPathFromGcRoot + */ public class PathElement implements Diffable { + /** + * The instance along the reference path that this PathElement is associated + * with. + */ public final AhatInstance instance; + + /** + * A human readable description of which field in instance is + * followed to reach the next element in the path. + * Some examples: + *
    + *
  • "mBlah" for a class instance + *
  • "[4]" for an array instance + *
  • "" for the last element of the path + *
+ */ public final String field; + + /** + * True if instance is a (not necessarily immediate) dominator + * of the final object in the path. + */ public boolean isDominator; + /** + * Constructs a PathElement object. + * isDominator is set to false. + * + * @param instance the path element instance + * @param field the path element field + */ public PathElement(AhatInstance instance, String field) { this.instance = instance; this.field = field; diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/RootType.java b/tools/ahat/src/main/com/android/ahat/heapdump/RootType.java index 734f889af6..99d85dc940 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/RootType.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/RootType.java @@ -16,20 +16,80 @@ package com.android.ahat.heapdump; +/** + * Enumeration representing object root types as defined in the binary heap + * dump format specification. + */ public enum RootType { + /** + * There is a JNI Global Reference for the object in question. + */ JNI_GLOBAL (1 << 0), + + /** + * There is a JNI Local Reference for the object in question. + */ JNI_LOCAL (1 << 1), + + /** + * The object in question is a parameter or local variable of a running + * method. + */ JAVA_FRAME (1 << 2), + + /** + * The object in question is a parameter of a running JNI method. + */ NATIVE_STACK (1 << 3), + + /** + * The object is a class object that cannot be unloaded. + */ STICKY_CLASS (1 << 4), + + /** + * The object is referenced from an active thread block. + */ THREAD_BLOCK (1 << 5), + + /** + * The object's monitor is currently in use. + */ MONITOR (1 << 6), + + /** + * The object is a running thread. + */ THREAD (1 << 7), + + /** + * The object is an interned string. + */ INTERNED_STRING (1 << 8), + + /** + * The object is being used by the debugger. + */ DEBUGGER (1 << 9), + + /** + * The object is being used by the VM internally. + */ VM_INTERNAL (1 << 10), + + /** + * The object has no given reason for being considered a root. + */ UNKNOWN (1 << 11), + + /** + * The object's monitor is currently in use from JNI. + */ JNI_MONITOR (1 << 12), + + /** + * The object is waiting to be finalized. + */ FINALIZING (1 << 13); final int mask; diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Site.java b/tools/ahat/src/main/com/android/ahat/heapdump/Site.java index 4978d52830..72c0a4a750 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Site.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Site.java @@ -24,6 +24,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +/** + * Used to collection information about objects allocated at a particular + * allocation site. + */ public class Site implements Diffable { // The site that this site was directly called from. // mParent is null for the root site. @@ -61,18 +65,39 @@ public class Site implements Diffable { private Site mBaseline; + /** + * Summary information about instances allocated at a particular allocation + * site that are instances of a particular class and allocated on a + * particular heap. + */ public static class ObjectsInfo implements Diffable { + /** + * The heap that the summarized objects belong to. + */ public AhatHeap heap; - public AhatClassObj classObj; // May be null. + + /** + * The class of the summarized objects. + */ + public AhatClassObj classObj; // May be null. Not sure why. + + /** + * The number of instances included in the summary. + */ public long numInstances; + + /** + * The sum of the shallow size of each instance included in the summary. + */ public Size numBytes; + private ObjectsInfo baseline; /** - * Construct a new, empty objects info for the given heap and class + * Constructs a new, empty objects info for the given heap and class * combination. */ - public ObjectsInfo(AhatHeap heap, AhatClassObj classObj) { + ObjectsInfo(AhatHeap heap, AhatClassObj classObj) { this.heap = heap; this.classObj = classObj; this.numInstances = 0; @@ -82,12 +107,14 @@ public class Site implements Diffable { /** * Returns the name of the class this ObjectsInfo is associated with. + * + * @return the name of this object info's class */ public String getClassName() { return classObj == null ? "???" : classObj.getName(); } - public void setBaseline(ObjectsInfo baseline) { + void setBaseline(ObjectsInfo baseline) { this.baseline = baseline; } @@ -121,11 +148,11 @@ public class Site implements Diffable { } /** - * Get a child site of this site. - * Returns the site at which the instance was allocated. - * @param frames - The list of frames in the stack trace, starting with the - * inner-most frame. May be null, in which case this site is - * returned. + * Gets a child site of this site. + * @param frames the list of frames in the stack trace, starting with the + * inner-most frame. May be null, in which case this site is + * returned. + * @return the child site */ Site getSite(ProguardMap.Frame[] frames) { return frames == null ? this : getSite(this, frames); @@ -211,22 +238,29 @@ public class Site implements Diffable { return id; } - // Get the size of a site for a specific heap. + /** + * Returns the size of all objects on the given heap allocated at this site. + * Includes objects belonging to heap allocated at this and + * child sites. + * + * @param heap the heap to query the size for + * @return the total shallow size of objects in this site + */ public Size getSize(AhatHeap heap) { return mSizesByHeap[heap.getIndex()]; } /** - * Collect the objects allocated under this site, optionally filtered by + * Collects the objects allocated under this site, optionally filtered by * heap name or class name. Includes objects allocated in children sites. - * @param heapName - The name of the heap the collected objects should - * belong to. This may be null to indicate objects of - * every heap should be collected. - * @param className - The name of the class the collected objects should - * belong to. This may be null to indicate objects of - * every class should be collected. - * @param objects - Out parameter. A collection of objects that all - * collected objects should be added to. + * @param heapName the name of the heap the collected objects should + * belong to. This may be null to indicate objects of + * every heap should be collected. + * @param className the name of the class the collected objects should + * belong to. This may be null to indicate objects of + * every class should be collected. + * @param objects out parameter. A collection of objects that all + * collected objects should be added to. */ public void getObjects(String heapName, String className, Collection objects) { for (AhatInstance inst : mObjects) { @@ -263,11 +297,24 @@ public class Site implements Diffable { return info; } + /** + * Return a summary breakdown of the objects allocated at this site. + * Objects are grouped by class and heap and summarized into a single + * {@link ObjectsInfo}. This method returns all the groups for this + * allocation site. + * + * @return all ObjectInfo summaries for instances allocated at this site + */ public List getObjectsInfos() { return mObjectsInfos; } - // Get the combined size of the site for all heaps. + /** + * Returns the combined size of the site for all heaps. + * Includes all objects allocated at this and child sites. + * + * @return total shallow size of objects in this site + */ public Size getTotalSize() { Size total = Size.ZERO; for (Size size : mSizesByHeap) { @@ -277,39 +324,70 @@ public class Site implements Diffable { } /** - * Return the site this site was called from. + * Returns the site this site was called from. * Returns null for the root site. + * + * @return the site this site was called from */ public Site getParent() { return mParent; } + /** + * Returns the name of the method this allocation site belongs to. + * For example, "equals". + * + * @return the method name of the allocation site + */ public String getMethodName() { return mMethodName; } + /** + * Returns the signature of the method this allocation site belongs to. + * For example, "(Ljava/lang/Object;)Z". + * + * @return the signature of method the allocation site belongs to + */ public String getSignature() { return mSignature; } + /** + * Returns the name of the Java file where this allocation site is found. + * + * @return the file the allocation site belongs to + */ public String getFilename() { return mFilename; } + /** + * Returns the line number of the code in the source file that the + * allocation site refers to. + * + * @return the allocation site line number + */ public int getLineNumber() { return mLineNumber; } /** * Returns the unique id of this site. + * This is an arbitrary unique id computed after processing the heap dump. + * + * @return the site id */ public long getId() { return mId; } /** - * Find the child site with the given id. + * Returns the child site with the given id. * Returns null if no such site was found. + * + * @param id the id of the child site to find + * @return the found child site */ public Site findSite(long id) { if (id == mId) { @@ -341,6 +419,8 @@ public class Site implements Diffable { /** * Returns an unmodifiable list of this site's immediate children. + * + * @return this site's child sites */ public List getChildren() { return Collections.unmodifiableList(mChildren); diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Size.java b/tools/ahat/src/main/com/android/ahat/heapdump/Size.java index 7c8db900df..a4593e195b 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Size.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Size.java @@ -17,47 +17,76 @@ package com.android.ahat.heapdump; /** - * The Size class is used to represent how much space an instance takes up. - * + * Used to represent how much space an instance takes up. * An abstraction is introduced rather than using a long directly in order to * more easily keep track of the different components of the size. For * example, some instances may have associated native, code, or graphics * sizes. - * + *

* Size objects are immutable. */ public class Size { private final long mJavaSize; private final long mRegisteredNativeSize; + /** + * An instance of Size with 0 for all categories. + */ public static Size ZERO = new Size(0, 0); + /** + * Constructs a new instance of Size. + * + * @param javaSize number of bytes in the java category + * @param registeredNativeSize number of bytes in the registeredNativeSize + * category + */ public Size(long javaSize, long registeredNativeSize) { mJavaSize = javaSize; mRegisteredNativeSize = registeredNativeSize; } + /** + * Returns the sum of the size of all categories. + * + * @return the total size + */ public long getSize() { return mJavaSize + mRegisteredNativeSize; } + /** + * Returns the size of the java category. + * + * @return the java category size + */ public long getJavaSize() { return mJavaSize; } + /** + * Returns the size of the registered native category. + * + * @return the registered native category size + */ public long getRegisteredNativeSize() { return mRegisteredNativeSize; } /** - * Returns true if all the fields of this size object are zero. + * Returns true if all categories of this size are zero. + * + * @return true if the size is zero */ public boolean isZero() { return mJavaSize == 0 && mRegisteredNativeSize == 0; } /** - * Return a new Size object that is the sum of this size and the other. + * Returns a new Size object that is the sum of this size and the other. + * + * @param other the size to sum with this size + * @return the new size object */ public Size plus(Size other) { if (isZero()) { @@ -71,8 +100,11 @@ public class Size { } /** - * Return a new Size object that has 'size' more registered native size than - * this Size object. + * Returns a new Size object that has size more registered + * native size than this Size object. + * + * @param size the size to add to the registered native category + * @return the new size object */ public Size plusRegisteredNativeSize(long size) { return new Size(mJavaSize, mRegisteredNativeSize + size); diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Sort.java b/tools/ahat/src/main/com/android/ahat/heapdump/Sort.java index efe0d6b59b..a629b3ce7f 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Sort.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Sort.java @@ -25,14 +25,14 @@ import java.util.List; /** * Provides Comparators and helper functions for sorting Instances, Sites, and * other things. - * + *

* Note: The Comparators defined here impose orderings that are inconsistent * with equals. They should not be used for element lookup or search. They * should only be used for showing elements to the user in different orders. */ public class Sort { /** - * Compare sizes by their total size. + * Compares sizes by their total size. * This sorts sizes from smaller total size to larger total size. */ public static final Comparator SIZE_BY_SIZE = new Comparator() { @@ -43,7 +43,7 @@ public class Sort { }; /** - * Compare instances by their total retained size. + * Compares instances by their total retained size. * Different instances with the same total retained size are considered * equal for the purposes of comparison. * This sorts instances from larger retained size to smaller retained size. @@ -57,12 +57,12 @@ public class Sort { }; /** - * Compare instances by their retained size for a given heap index. + * Compares instances by their retained size for a given heap index. * Different instances with the same total retained size are considered * equal for the purposes of comparison. * This sorts instances from larger retained size to smaller retained size. */ - public static class InstanceByHeapRetainedSize implements Comparator { + private static class InstanceByHeapRetainedSize implements Comparator { private AhatHeap mHeap; public InstanceByHeapRetainedSize(AhatHeap heap) { @@ -76,16 +76,28 @@ public class Sort { } /** - * Compare objects based on a list of comparators, giving priority to the + * Compares objects based on a list of comparators, giving priority to the * earlier comparators in the list. */ - public static class WithPriority implements Comparator { + private static class WithPriority implements Comparator { private List> mComparators; + /** + * Constructs a comparator giving sort priority to earlier comparators in + * the list. + * + * @param comparators the list of comparators to use for sorting + */ public WithPriority(Comparator... comparators) { mComparators = Arrays.asList(comparators); } + /** + * Constructs a comparator giving sort priority to earlier comparators in + * the list. + * + * @param comparators the list of comparators to use for sorting + */ public WithPriority(List> comparators) { mComparators = comparators; } @@ -101,6 +113,27 @@ public class Sort { } } + /** + * Returns a comparator that gives sort priority to earlier comparators in + * the list. + * + * @param the type of object being sorted + * @param comparators the list of comparators to use for sorting + * @return the composite comparator + */ + public static Comparator withPriority(Comparator... comparators) { + return new WithPriority(comparators); + } + + /** + * Returns a comparator that gives a default instance sort for the given + * snapshot. + * Objects are sorted by retained size, with priority given to the "app" + * heap if present. + * + * @param snapshot the snapshot to use the comparator with + * @return the default instance comparator + */ public static Comparator defaultInstanceCompare(AhatSnapshot snapshot) { List> comparators = new ArrayList>(); @@ -116,14 +149,19 @@ public class Sort { } /** - * Compare Sites by the size of objects allocated on a given heap. + * Compares Sites by the size of objects allocated on a given heap. * Different object infos with the same size on the given heap are * considered equal for the purposes of comparison. * This sorts sites from larger size to smaller size. */ - public static class SiteByHeapSize implements Comparator { + private static class SiteByHeapSize implements Comparator { AhatHeap mHeap; + /** + * Constructs a SiteByHeapSize comparator. + * + * @param heap the heap to use when comparing sizes + */ public SiteByHeapSize(AhatHeap heap) { mHeap = heap; } @@ -135,7 +173,7 @@ public class Sort { } /** - * Compare Sites by the total size of objects allocated. + * Compares Sites by the total size of objects allocated. * This sorts sites from larger size to smaller size. */ public static final Comparator SITE_BY_TOTAL_SIZE = new Comparator() { @@ -145,6 +183,14 @@ public class Sort { } }; + /** + * Compares Sites using a default comparison order. + * This sorts sites from larger size to smaller size, giving preference to + * sites with more allocation on the "app" heap, if present. + * + * @param snapshot the snapshot to use the comparator with + * @return the default site comparator + */ public static Comparator defaultSiteCompare(AhatSnapshot snapshot) { List> comparators = new ArrayList>(); @@ -174,7 +220,7 @@ public class Sort { }; /** - * Compare Site.ObjectsInfo by heap name. + * Compares Site.ObjectsInfo by heap name. * Different object infos with the same heap name are considered equal for * the purposes of comparison. */ @@ -187,7 +233,7 @@ public class Sort { }; /** - * Compare Site.ObjectsInfo by class name. + * Compares Site.ObjectsInfo by class name. * Different object infos with the same class name are considered equal for * the purposes of comparison. */ @@ -202,7 +248,7 @@ public class Sort { }; /** - * Compare FieldValue by field name. + * Compares FieldValue by field name. */ public static final Comparator FIELD_VALUE_BY_NAME = new Comparator() { @@ -213,7 +259,7 @@ public class Sort { }; /** - * Compare FieldValue by type name. + * Compares FieldValue by type name. */ public static final Comparator FIELD_VALUE_BY_TYPE = new Comparator() { diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Type.java b/tools/ahat/src/main/com/android/ahat/heapdump/Type.java index 40249615a2..ff79864505 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Type.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Type.java @@ -16,18 +16,63 @@ package com.android.ahat.heapdump; +/** + * Enum corresponding to basic types from the binary heap dump format. + */ public enum Type { + /** + * Type used for any Java object. + */ OBJECT("Object", 4), + + /** + * The primitive boolean type. + */ BOOLEAN("boolean", 1), + + /** + * The primitive char type. + */ CHAR("char", 2), + + /** + * The primitive float type. + */ FLOAT("float", 4), + + /** + * The primitive double type. + */ DOUBLE("double", 8), + + /** + * The primitive byte type. + */ BYTE("byte", 1), + + /** + * The primitive short type. + */ SHORT("short", 2), + + /** + * The primitive int type. + */ INT("int", 4), + + /** + * The primitive long type. + */ LONG("long", 8); + /** + * The name of the type. + */ public final String name; + + /** + * The number of bytes taken up by values of this type in the Java heap. + */ final int size; Type(String name, int size) { diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Value.java b/tools/ahat/src/main/com/android/ahat/heapdump/Value.java index eea427774b..b219bf1564 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Value.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Value.java @@ -17,48 +17,107 @@ package com.android.ahat.heapdump; /** - * Value represents a field value in a heap dump. The field value is either a - * subclass of AhatInstance or a primitive Java type. + * A Java instance or primitive value from a parsed heap dump. + * Note: To save memory, a null Value is used to represent a null Java + * instance from the heap dump. */ public abstract class Value { + /** + * Constructs a Value for an AhatInstance. + * Note: returns null for null value. + * + * @param value the AhatInstance to make into a value + * @return the constructed value. + */ public static Value pack(AhatInstance value) { return value == null ? null : new InstanceValue(value); } + /** + * Constructs a Value for a boolean. + * + * @param value the boolean to make into a value + * @return the constructed value. + */ public static Value pack(boolean value) { return new BooleanValue(value); } + /** + * Constructs a Value for a char. + * + * @param value the char to make into a value + * @return the constructed value. + */ public static Value pack(char value) { return new CharValue(value); } + /** + * Constructs a Value for a float. + * + * @param value the float to make into a value + * @return the constructed value. + */ public static Value pack(float value) { return new FloatValue(value); } + /** + * Constructs a Value for a double. + * + * @param value the double to make into a value + * @return the constructed value. + */ public static Value pack(double value) { return new DoubleValue(value); } + /** + * Constructs a Value for a byte. + * + * @param value the byte to make into a value + * @return the constructed value. + */ public static Value pack(byte value) { return new ByteValue(value); } + /** + * Constructs a Value for a short. + * + * @param value the short to make into a value + * @return the constructed value. + */ public static Value pack(short value) { return new ShortValue(value); } + /** + * Constructs a Value for a int. + * + * @param value the int to make into a value + * @return the constructed value. + */ public static Value pack(int value) { return new IntValue(value); } + /** + * Constructs a Value for a long. + * + * @param value the long to make into a value + * @return the constructed value. + */ public static Value pack(long value) { return new LongValue(value); } /** - * Return the type of the given value. + * Returns the type of the given value. + * + * @param value the value to get the type of + * @return the value's type */ public static Type getType(Value value) { return value == null ? Type.OBJECT : value.getType(); @@ -70,62 +129,78 @@ public abstract class Value { abstract Type getType(); /** - * Returns true if the Value is an AhatInstance, as opposed to a Java - * primitive value. + * Returns true if the Value is an AhatInstance rather than a primitive + * value. + * + * @return true if the value is an AhatInstance */ public boolean isAhatInstance() { return false; } /** - * Return the Value as an AhatInstance if it is one. + * Returns the Value as an AhatInstance if it is one. * Returns null if the Value represents a Java primitive value. + * + * @return the AhatInstance packed into this value */ public AhatInstance asAhatInstance() { return null; } /** - * Returns true if the Value is an Integer. + * Returns true if the Value is an int. + * + * @return true if the value is an int. */ public boolean isInteger() { return false; } /** - * Return the Value as an Integer if it is one. - * Returns null if the Value does not represent an Integer. + * Returns the Value as an int if it is one. + * Returns null if the Value does not represent an int. + * + * @return the int packed into this value */ public Integer asInteger() { return null; } /** - * Returns true if the Value is an Long. + * Returns true if the Value is an long. + * + * @return true if the value is an long. */ public boolean isLong() { return false; } /** - * Return the Value as an Long if it is one. - * Returns null if the Value does not represent an Long. + * Returns the Value as an long if it is one. + * Returns null if the Value does not represent an long. + * + * @return the long packed into this value */ public Long asLong() { return null; } /** - * Return the Value as a Byte if it is one. - * Returns null if the Value does not represent a Byte. + * Returns the Value as an byte if it is one. + * Returns null if the Value does not represent an byte. + * + * @return the byte packed into this value */ public Byte asByte() { return null; } /** - * Return the Value as a Char if it is one. - * Returns null if the Value does not represent a Char. + * Returns the Value as an char if it is one. + * Returns null if the Value does not represent an char. + * + * @return the char packed into this value */ public Character asChar() { return null; @@ -134,10 +209,18 @@ public abstract class Value { @Override public abstract String toString(); - public Value getBaseline() { + private Value getBaseline() { return this; } + /** + * Returns the baseline of the given value for the purposes of diff. + * This method can be used to handle the case when the Value is null. + * + * @param value the value to get the baseline of + * @return the baseline of the value + * @see Diffable#getBaseline + */ public static Value getBaseline(Value value) { return value == null ? null : value.getBaseline(); } @@ -313,7 +396,6 @@ public abstract class Value { return mInstance.toString(); } - @Override public Value getBaseline() { return InstanceValue.pack(mInstance.getBaseline()); } 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 32bb209dc6..79a737cc18 100644 --- a/tools/ahat/src/main/com/android/ahat/proguard/ProguardMap.java +++ b/tools/ahat/src/main/com/android/ahat/proguard/ProguardMap.java @@ -26,7 +26,10 @@ import java.text.ParseException; import java.util.HashMap; import java.util.Map; -// Class used to deobfuscate classes, fields, and stack frames. +/** + * A representation of a proguard mapping for deobfuscating class names, + * field names, and stack frames. + */ public class ProguardMap { private static final String ARRAY_SYMBOL = "[]"; @@ -98,6 +101,10 @@ public class ProguardMap { private Map mClassesFromClearName = new HashMap(); private Map mClassesFromObfuscatedName = new HashMap(); + /** + * Information associated with a stack frame that identifies a particular + * line of source code. + */ public static class Frame { Frame(String method, String signature, String filename, int line) { this.method = method; @@ -106,9 +113,28 @@ public class ProguardMap { this.line = line; } + /** + * The name of the method the stack frame belongs to. + * For example, "equals". + */ public final String method; + + /** + * The signature of the method the stack frame belongs to. + * For example, "(Ljava/lang/Object;)Z". + */ public final String signature; + + /** + * The name of the file with containing the line of source that the stack + * frame refers to. + */ public final String filename; + + /** + * The line number of the code in the source file that the stack frame + * refers to. + */ public final int line; } @@ -116,13 +142,44 @@ public class ProguardMap { throw new ParseException(msg, 0); } - // Read in proguard mapping information from the given file. + /** + * Creates a new empty proguard mapping. + * The {@link #readFromFile readFromFile} and + * {@link #readFromReader readFromReader} methods can be used to populate + * the proguard mapping with proguard mapping information. + */ + public ProguardMap() { + } + + /** + * Adds the proguard mapping information in mapFile to this + * proguard mapping. + * The mapFile should be a proguard mapping file generated with + * the -printmapping option when proguard was run. + * + * @param mapFile the name of a file with proguard mapping information + * @throws FileNotFoundException If the mapFile could not be + * found + * @throws IOException If an input exception occurred. + * @throws ParseException If the mapFile is not a properly + * formatted proguard mapping file. + */ public void readFromFile(File mapFile) throws FileNotFoundException, IOException, ParseException { readFromReader(new FileReader(mapFile)); } - // Read in proguard mapping information from the given Reader. + /** + * Adds the proguard mapping information read from mapReader to + * this proguard mapping. + * mapReader should be a Reader of a proguard mapping file + * generated with the -printmapping option when proguard was run. + * + * @param mapReader a Reader for reading the proguard mapping information + * @throws IOException If an input exception occurred. + * @throws ParseException If the mapFile is not a properly + * formatted proguard mapping file. + */ public void readFromReader(Reader mapReader) throws IOException, ParseException { BufferedReader reader = new BufferedReader(mapReader); String line = reader.readLine(); @@ -207,8 +264,15 @@ public class ProguardMap { reader.close(); } - // Returns the deobfuscated version of the given class name. If no - // deobfuscated version is known, the original string is returned. + /** + * Returns the deobfuscated version of the given obfuscated class name. + * If this proguard mapping does not include information about how to + * deobfuscate the obfuscated class name, the obfuscated class name + * is returned. + * + * @param obfuscatedClassName the obfuscated class name to deobfuscate + * @return the deobfuscated class name. + */ public String getClassName(String obfuscatedClassName) { // Class names for arrays may have trailing [] that need to be // stripped before doing the lookup. @@ -224,9 +288,17 @@ public class ProguardMap { return clearBaseName + arraySuffix; } - // Returns the deobfuscated version of the given field name for the given - // (clear) class name. If no deobfuscated version is known, the original - // string is returned. + /** + * Returns the deobfuscated version of the obfuscated field name for the + * given deobfuscated class name. + * If this proguard mapping does not include information about how to + * deobfuscate the obfuscated field name, the obfuscated field name is + * returned. + * + * @param clearClass the deobfuscated name of the class the field belongs to + * @param obfuscatedField the obfuscated field name to deobfuscate + * @return the deobfuscated field name. + */ public String getFieldName(String clearClass, String obfuscatedField) { ClassData classData = mClassesFromClearName.get(clearClass); if (classData == null) { @@ -235,8 +307,21 @@ public class ProguardMap { return classData.getField(obfuscatedField); } - // Returns the deobfuscated frame for the given obfuscated frame and (clear) - // class name. As much of the frame is deobfuscated as can be. + /** + * Returns the deobfuscated version of the obfuscated stack frame + * information for the given deobfuscated class name. + * If this proguard mapping does not include information about how to + * deobfuscate the obfuscated stack frame information, the obfuscated stack + * frame information is returned. + * + * @param clearClassName the deobfuscated name of the class the stack frame's + * method belongs to + * @param obfuscatedMethodName the obfuscated method name to deobfuscate + * @param obfuscatedSignature the obfuscated method signature to deobfuscate + * @param obfuscatedFilename the obfuscated file name to deobfuscate. + * @param obfuscatedLine the obfuscated line number to deobfuscate. + * @return the deobfuscated stack frame information. + */ public Frame getFrame(String clearClassName, String obfuscatedMethodName, String obfuscatedSignature, String obfuscatedFilename, int obfuscatedLine) { String clearSignature = getSignature(obfuscatedSignature); -- GitLab From 3853017d05d5395250882c68482d8168a0392391 Mon Sep 17 00:00:00 2001 From: Lena Djokic Date: Thu, 16 Nov 2017 11:11:50 +0100 Subject: [PATCH 119/226] MIPS: Improve HandleBinaryOp (Add/Sub) for constant inputs Test: ./testrunner.py --optimizing --target Change-Id: I35154a85f16b4f46d3b4d5827b130b1e20153461 --- compiler/optimizing/code_generator_mips.cc | 54 ++++++--- compiler/optimizing/code_generator_mips64.cc | 116 +++++++++++++++---- 2 files changed, 133 insertions(+), 37 deletions(-) diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 6376f03b26..1f6b214f11 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -1941,6 +1941,7 @@ void LocationsBuilderMIPS::HandleBinaryOp(HBinaryOperation* instruction) { DCHECK_EQ(instruction->InputCount(), 2U); LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction); DataType::Type type = instruction->GetResultType(); + bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); switch (type) { case DataType::Type::kInt32: { locations->SetInAt(0, Location::RequiresRegister()); @@ -1950,11 +1951,22 @@ void LocationsBuilderMIPS::HandleBinaryOp(HBinaryOperation* instruction) { int32_t imm = CodeGenerator::GetInt32ValueOf(right->AsConstant()); if (instruction->IsAnd() || instruction->IsOr() || instruction->IsXor()) { can_use_imm = IsUint<16>(imm); - } else if (instruction->IsAdd()) { - can_use_imm = IsInt<16>(imm); } else { - DCHECK(instruction->IsSub()); - can_use_imm = IsInt<16>(-imm); + DCHECK(instruction->IsSub() || instruction->IsAdd()); + if (instruction->IsSub()) { + imm = -imm; + } + if (isR6) { + bool single_use = right->GetUses().HasExactlyOneElement(); + int16_t imm_high = High16Bits(imm); + int16_t imm_low = Low16Bits(imm); + if (imm_low < 0) { + imm_high += 1; + } + can_use_imm = !((imm_high != 0) && (imm_low != 0)) || single_use; + } else { + can_use_imm = IsInt<16>(imm); + } } } if (can_use_imm) @@ -1988,6 +2000,7 @@ void LocationsBuilderMIPS::HandleBinaryOp(HBinaryOperation* instruction) { void InstructionCodeGeneratorMIPS::HandleBinaryOp(HBinaryOperation* instruction) { DataType::Type type = instruction->GetType(); LocationSummary* locations = instruction->GetLocations(); + bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); switch (type) { case DataType::Type::kInt32: { @@ -2019,17 +2032,32 @@ void InstructionCodeGeneratorMIPS::HandleBinaryOp(HBinaryOperation* instruction) __ Xori(dst, lhs, rhs_imm); else __ Xor(dst, lhs, rhs_reg); - } else if (instruction->IsAdd()) { - if (use_imm) - __ Addiu(dst, lhs, rhs_imm); - else - __ Addu(dst, lhs, rhs_reg); } else { - DCHECK(instruction->IsSub()); - if (use_imm) - __ Addiu(dst, lhs, -rhs_imm); - else + DCHECK(instruction->IsAdd() || instruction->IsSub()); + if (use_imm) { + if (instruction->IsSub()) { + rhs_imm = -rhs_imm; + } + if (IsInt<16>(rhs_imm)) { + __ Addiu(dst, lhs, rhs_imm); + } else { + DCHECK(isR6); + int16_t rhs_imm_high = High16Bits(rhs_imm); + int16_t rhs_imm_low = Low16Bits(rhs_imm); + if (rhs_imm_low < 0) { + rhs_imm_high += 1; + } + __ Aui(dst, lhs, rhs_imm_high); + if (rhs_imm_low != 0) { + __ Addiu(dst, dst, rhs_imm_low); + } + } + } else if (instruction->IsAdd()) { + __ Addu(dst, lhs, rhs_reg); + } else { + DCHECK(instruction->IsSub()); __ Subu(dst, lhs, rhs_reg); + } } break; } diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 03a719f445..22989c8283 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1793,11 +1793,19 @@ void LocationsBuilderMIPS64::HandleBinaryOp(HBinaryOperation* instruction) { int64_t imm = CodeGenerator::GetInt64ValueOf(right->AsConstant()); if (instruction->IsAnd() || instruction->IsOr() || instruction->IsXor()) { can_use_imm = IsUint<16>(imm); - } else if (instruction->IsAdd()) { - can_use_imm = IsInt<16>(imm); } else { - DCHECK(instruction->IsSub()); - can_use_imm = IsInt<16>(-imm); + DCHECK(instruction->IsAdd() || instruction->IsSub()); + bool single_use = right->GetUses().HasExactlyOneElement(); + if (instruction->IsSub()) { + if (!(type == DataType::Type::kInt32 && imm == INT32_MIN)) { + imm = -imm; + } + } + if (type == DataType::Type::kInt32) { + can_use_imm = IsInt<16>(imm) || (Low16Bits(imm) == 0) || single_use; + } else { + can_use_imm = IsInt<16>(imm) || (IsInt<32>(imm) && (Low16Bits(imm) == 0)) || single_use; + } } } if (can_use_imm) @@ -1855,30 +1863,90 @@ void InstructionCodeGeneratorMIPS64::HandleBinaryOp(HBinaryOperation* instructio __ Xori(dst, lhs, rhs_imm); else __ Xor(dst, lhs, rhs_reg); - } else if (instruction->IsAdd()) { - if (type == DataType::Type::kInt32) { - if (use_imm) - __ Addiu(dst, lhs, rhs_imm); - else - __ Addu(dst, lhs, rhs_reg); - } else { - if (use_imm) - __ Daddiu(dst, lhs, rhs_imm); - else - __ Daddu(dst, lhs, rhs_reg); + } else if (instruction->IsAdd() || instruction->IsSub()) { + if (instruction->IsSub()) { + rhs_imm = -rhs_imm; } - } else { - DCHECK(instruction->IsSub()); if (type == DataType::Type::kInt32) { - if (use_imm) - __ Addiu(dst, lhs, -rhs_imm); - else - __ Subu(dst, lhs, rhs_reg); + if (use_imm) { + if (IsInt<16>(rhs_imm)) { + __ Addiu(dst, lhs, rhs_imm); + } else { + int16_t rhs_imm_high = High16Bits(rhs_imm); + int16_t rhs_imm_low = Low16Bits(rhs_imm); + if (rhs_imm_low < 0) { + rhs_imm_high += 1; + } + __ Aui(dst, lhs, rhs_imm_high); + if (rhs_imm_low != 0) { + __ Addiu(dst, dst, rhs_imm_low); + } + } + } else { + if (instruction->IsAdd()) { + __ Addu(dst, lhs, rhs_reg); + } else { + DCHECK(instruction->IsSub()); + __ Subu(dst, lhs, rhs_reg); + } + } } else { - if (use_imm) - __ Daddiu(dst, lhs, -rhs_imm); - else + if (use_imm) { + if (IsInt<16>(rhs_imm)) { + __ Daddiu(dst, lhs, rhs_imm); + } else if (IsInt<32>(rhs_imm)) { + int16_t rhs_imm_high = High16Bits(rhs_imm); + int16_t rhs_imm_low = Low16Bits(rhs_imm); + bool overflow_hi16 = false; + if (rhs_imm_low < 0) { + rhs_imm_high += 1; + overflow_hi16 = (rhs_imm_high == -32768); + } + __ Daui(dst, lhs, rhs_imm_high); + if (rhs_imm_low != 0) { + __ Daddiu(dst, dst, rhs_imm_low); + } + if (overflow_hi16) { + __ Dahi(dst, 1); + } + } else { + int16_t rhs_imm_low = Low16Bits(Low32Bits(rhs_imm)); + if (rhs_imm_low < 0) { + rhs_imm += (INT64_C(1) << 16); + } + int16_t rhs_imm_upper = High16Bits(Low32Bits(rhs_imm)); + if (rhs_imm_upper < 0) { + rhs_imm += (INT64_C(1) << 32); + } + int16_t rhs_imm_high = Low16Bits(High32Bits(rhs_imm)); + if (rhs_imm_high < 0) { + rhs_imm += (INT64_C(1) << 48); + } + int16_t rhs_imm_top = High16Bits(High32Bits(rhs_imm)); + GpuRegister tmp = lhs; + if (rhs_imm_low != 0) { + __ Daddiu(dst, tmp, rhs_imm_low); + tmp = dst; + } + // Dahi and Dati must use the same input and output register, so we have to initialize + // the dst register using Daddiu or Daui, even when the intermediate value is zero: + // Daui(dst, lhs, 0). + if ((rhs_imm_upper != 0) || (rhs_imm_low == 0)) { + __ Daui(dst, tmp, rhs_imm_upper); + } + if (rhs_imm_high != 0) { + __ Dahi(dst, rhs_imm_high); + } + if (rhs_imm_top != 0) { + __ Dati(dst, rhs_imm_top); + } + } + } else if (instruction->IsAdd()) { + __ Daddu(dst, lhs, rhs_reg); + } else { + DCHECK(instruction->IsSub()); __ Dsubu(dst, lhs, rhs_reg); + } } } break; -- GitLab From 0259c24f5e83ab69b259245b645076b864b2e2ca Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 4 Dec 2017 11:27:47 +0000 Subject: [PATCH 120/226] Fix a bug in String.charAt() simplification. Do not pass method index as a bool flag indicating that the HBoundsCheck originates from a String.charAt(). This was working only thanks to the method index unlikely to be 0. This bug was introduced in https://android-review.googlesource.com/321573 . Test: Rely on TreeHugger. Bug: 30933338 Change-Id: I2a51e478ee145d342af8cd49f9fdec7adffd77ff --- compiler/optimizing/instruction_simplifier.cc | 2 +- compiler/optimizing/nodes.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index bd20d28992..13c7607cbb 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -2276,7 +2276,7 @@ void InstructionSimplifierVisitor::SimplifyStringCharAt(HInvoke* invoke) { HArrayLength* length = new (allocator) HArrayLength(str, dex_pc, /* is_string_length */ true); invoke->GetBlock()->InsertInstructionBefore(length, invoke); HBoundsCheck* bounds_check = new (allocator) HBoundsCheck( - index, length, dex_pc, invoke->GetDexMethodIndex()); + index, length, dex_pc, /* is_string_char_at */ true); invoke->GetBlock()->InsertInstructionBefore(bounds_check, invoke); HArrayGet* array_get = new (allocator) HArrayGet(str, bounds_check, diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 66d5bfea32..58030df9d4 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -5787,10 +5787,10 @@ class HBoundsCheck FINAL : public HExpression<2> { HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc, - bool string_char_at = false) + bool is_string_char_at = false) : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc) { DCHECK_EQ(DataType::Type::kInt32, DataType::Kind(index->GetType())); - SetPackedFlag(string_char_at); + SetPackedFlag(is_string_char_at); SetRawInputAt(0, index); SetRawInputAt(1, length); } -- GitLab From fec85cdfa337dfb1f1c6d5bd9a940bc2d20a0edb Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 4 Dec 2017 13:00:12 +0000 Subject: [PATCH 121/226] Minor cleanup in CodeGenerator::RecordPcInfo(). And remove HInvokeInterface::GetDexMethodIndex() as the base class version is identical. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Change-Id: I489bff5b3f624eec19269487529e29d58f068960 --- compiler/optimizing/code_generator.cc | 42 ++++++++++++--------------- compiler/optimizing/nodes.h | 1 - 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index aff6f9f64f..45eec6d744 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -981,21 +981,6 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, } } - uint32_t outer_dex_pc = dex_pc; - uint32_t outer_environment_size = 0; - uint32_t inlining_depth = 0; - if (instruction != nullptr) { - for (HEnvironment* environment = instruction->GetEnvironment(); - environment != nullptr; - environment = environment->GetParent()) { - outer_dex_pc = environment->GetDexPc(); - outer_environment_size = environment->Size(); - if (environment != instruction->GetEnvironment()) { - inlining_depth++; - } - } - } - // Collect PC infos for the mapping table. uint32_t native_pc = GetAssembler()->CodePosition(); @@ -1003,12 +988,12 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, if (instruction == nullptr) { // For stack overflow checks and native-debug-info entries without dex register // mapping (i.e. start of basic block or start of slow path). - stack_map_stream->BeginStackMapEntry(outer_dex_pc, native_pc, 0, 0, 0, 0); + stack_map_stream->BeginStackMapEntry(dex_pc, native_pc, 0, 0, 0, 0); stack_map_stream->EndStackMapEntry(); return; } - LocationSummary* locations = instruction->GetLocations(); + LocationSummary* locations = instruction->GetLocations(); uint32_t register_mask = locations->GetRegisterMask(); DCHECK_EQ(register_mask & ~locations->GetLiveRegisters()->GetCoreRegisters(), 0u); if (locations->OnlyCallsOnSlowPath()) { @@ -1023,22 +1008,33 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, // The register mask must be a subset of callee-save registers. DCHECK_EQ(register_mask & core_callee_save_mask_, register_mask); } + + uint32_t outer_dex_pc = dex_pc; + uint32_t outer_environment_size = 0u; + uint32_t inlining_depth = 0; + HEnvironment* const environment = instruction->GetEnvironment(); + if (environment != nullptr) { + HEnvironment* outer_environment = environment; + while (outer_environment->GetParent() != nullptr) { + outer_environment = outer_environment->GetParent(); + ++inlining_depth; + } + outer_dex_pc = outer_environment->GetDexPc(); + outer_environment_size = outer_environment->Size(); + } stack_map_stream->BeginStackMapEntry(outer_dex_pc, native_pc, register_mask, locations->GetStackMask(), outer_environment_size, inlining_depth); - - HEnvironment* const environment = instruction->GetEnvironment(); EmitEnvironment(environment, slow_path); // Record invoke info, the common case for the trampoline is super and static invokes. Only // record these to reduce oat file size. if (kEnableDexLayoutOptimizations) { - if (environment != nullptr && - instruction->IsInvoke() && - instruction->IsInvokeStaticOrDirect()) { - HInvoke* const invoke = instruction->AsInvoke(); + if (instruction->IsInvokeStaticOrDirect()) { + HInvoke* const invoke = instruction->AsInvokeStaticOrDirect(); + DCHECK(environment != nullptr); stack_map_stream->AddInvoke(invoke->GetInvokeType(), invoke->GetDexMethodIndex()); } } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 66d5bfea32..fa3ac01c44 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -4614,7 +4614,6 @@ class HInvokeInterface FINAL : public HInvoke { } uint32_t GetImtIndex() const { return imt_index_; } - uint32_t GetDexMethodIndex() const { return dex_method_index_; } DECLARE_INSTRUCTION(InvokeInterface); -- GitLab From d504c7ea1e63b157a8b3b9333fe832ae7e77caee Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 4 Dec 2017 15:56:51 +0000 Subject: [PATCH 122/226] Change enums in ImageWriter to `enum class`. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Change-Id: I0f6687f6b99b78a2a56064ed96cba4e238bc01cd --- dex2oat/linker/image_writer.cc | 254 +++++++++++++++++---------------- dex2oat/linker/image_writer.h | 138 +++++++++++------- 2 files changed, 217 insertions(+), 175 deletions(-) diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 68c9f80a44..5ade583b88 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -365,7 +365,7 @@ void ImageWriter::AssignImageOffset(mirror::Object* object, ImageWriter::BinSlot size_t oat_index = GetOatIndex(object); ImageInfo& image_info = GetImageInfo(oat_index); - size_t bin_slot_offset = image_info.bin_slot_offsets_[bin_slot.GetBin()]; + size_t bin_slot_offset = image_info.GetBinSlotOffset(bin_slot.GetBin()); size_t new_offset = bin_slot_offset + bin_slot.GetIndex(); DCHECK_ALIGNED(new_offset, kObjectAlignment); @@ -436,9 +436,10 @@ void ImageWriter::PrepareDexCacheArraySlots() { auto it = dex_file_oat_index_map_.find(dex_file); DCHECK(it != dex_file_oat_index_map_.end()) << dex_file->GetLocation(); ImageInfo& image_info = GetImageInfo(it->second); - image_info.dex_cache_array_starts_.Put(dex_file, image_info.bin_slot_sizes_[kBinDexCacheArray]); + image_info.dex_cache_array_starts_.Put( + dex_file, image_info.GetBinSlotSize(Bin::kDexCacheArray)); DexCacheArraysLayout layout(target_ptr_size_, dex_file); - image_info.bin_slot_sizes_[kBinDexCacheArray] += layout.Size(); + image_info.IncrementBinSlotSize(Bin::kDexCacheArray, layout.Size()); } ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); @@ -494,7 +495,7 @@ void ImageWriter::AddDexCacheArrayRelocation(void* array, DCHECK(!IsInBootImage(array)); size_t oat_index = GetOatIndexForDexCache(dex_cache); native_object_relocations_.emplace(array, - NativeObjectRelocation { oat_index, offset, kNativeObjectRelocationTypeDexCacheArray }); + NativeObjectRelocation { oat_index, offset, NativeObjectRelocationType::kDexCacheArray }); } } @@ -512,7 +513,7 @@ void ImageWriter::AddMethodPointerArray(mirror::PointerArray* arr) { } // kBinArtMethodClean picked arbitrarily, just required to differentiate between ArtFields and // ArtMethods. - pointer_arrays_.emplace(arr, kBinArtMethodClean); + pointer_arrays_.emplace(arr, Bin::kArtMethodClean); } void ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t oat_index) { @@ -528,8 +529,7 @@ void ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t oat_index) { // // This means more pages will stay either clean or shared dirty (with zygote) and // the app will use less of its own (private) memory. - Bin bin = kBinRegular; - size_t current_offset = 0u; + Bin bin = Bin::kRegular; if (kBinObjects) { // @@ -563,7 +563,7 @@ void ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t oat_index) { // so packing them together will not result in a noticeably tighter dirty-to-clean ratio. // if (object->IsClass()) { - bin = kBinClassVerified; + bin = Bin::kClassVerified; mirror::Class* klass = object->AsClass(); // Add non-embedded vtable to the pointer array table if there is one. @@ -584,15 +584,15 @@ void ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t oat_index) { // - classes with dirty static fields. if (dirty_image_objects_ != nullptr && dirty_image_objects_->find(klass->PrettyDescriptor()) != dirty_image_objects_->end()) { - bin = kBinKnownDirty; + bin = Bin::kKnownDirty; } else if (klass->GetStatus() == Class::kStatusInitialized) { - bin = kBinClassInitialized; + bin = Bin::kClassInitialized; // If the class's static fields are all final, put it into a separate bin // since it's very likely it will stay clean. uint32_t num_static_fields = klass->NumStaticFields(); if (num_static_fields == 0) { - bin = kBinClassInitializedFinalStatics; + bin = Bin::kClassInitializedFinalStatics; } else { // Maybe all the statics are final? bool all_final = true; @@ -605,20 +605,20 @@ void ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t oat_index) { } if (all_final) { - bin = kBinClassInitializedFinalStatics; + bin = Bin::kClassInitializedFinalStatics; } } } } else if (object->GetClass()->IsStringClass()) { - bin = kBinString; // Strings are almost always immutable (except for object header). + bin = Bin::kString; // Strings are almost always immutable (except for object header). } else if (object->GetClass() == Runtime::Current()->GetClassLinker()->GetClassRoot(ClassLinker::kJavaLangObject)) { // Instance of java lang object, probably a lock object. This means it will be dirty when we // synchronize on it. - bin = kBinMiscDirty; + bin = Bin::kMiscDirty; } else if (object->IsDexCache()) { // Dex file field becomes dirty when the image is loaded. - bin = kBinMiscDirty; + bin = Bin::kMiscDirty; } // else bin = kBinRegular } @@ -630,14 +630,15 @@ void ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t oat_index) { ImageInfo& image_info = GetImageInfo(oat_index); size_t offset_delta = RoundUp(object_size, kObjectAlignment); // 64-bit alignment - current_offset = image_info.bin_slot_sizes_[bin]; // How many bytes the current bin is at (aligned). + // How many bytes the current bin is at (aligned). + size_t current_offset = image_info.GetBinSlotSize(bin); // Move the current bin size up to accommodate the object we just assigned a bin slot. - image_info.bin_slot_sizes_[bin] += offset_delta; + image_info.IncrementBinSlotSize(bin, offset_delta); BinSlot new_bin_slot(bin, current_offset); SetImageBinSlot(object, new_bin_slot); - ++image_info.bin_slot_count_[bin]; + image_info.IncrementBinSlotCount(bin, 1u); // Grow the image closer to the end by the object we just assigned. image_info.image_end_ += offset_delta; @@ -665,7 +666,7 @@ bool ImageWriter::IsImageBinSlotAssigned(mirror::Object* object) const { BinSlot bin_slot(offset); size_t oat_index = GetOatIndex(object); const ImageInfo& image_info = GetImageInfo(oat_index); - DCHECK_LT(bin_slot.GetIndex(), image_info.bin_slot_sizes_[bin_slot.GetBin()]) + DCHECK_LT(bin_slot.GetIndex(), image_info.GetBinSlotSize(bin_slot.GetBin())) << "bin slot offset should not exceed the size of that bin"; } return true; @@ -682,7 +683,7 @@ ImageWriter::BinSlot ImageWriter::GetImageBinSlot(mirror::Object* object) const BinSlot bin_slot(static_cast(offset)); size_t oat_index = GetOatIndex(object); const ImageInfo& image_info = GetImageInfo(oat_index); - DCHECK_LT(bin_slot.GetIndex(), image_info.bin_slot_sizes_[bin_slot.GetBin()]); + DCHECK_LT(bin_slot.GetIndex(), image_info.GetBinSlotSize(bin_slot.GetBin())); return bin_slot; } @@ -1402,12 +1403,12 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, auto it = native_object_relocations_.find(cur_fields); CHECK(it == native_object_relocations_.end()) << "Field array " << cur_fields << " already forwarded"; - size_t& offset = image_info.bin_slot_sizes_[kBinArtField]; + size_t offset = image_info.GetBinSlotSize(Bin::kArtField); DCHECK(!IsInBootImage(cur_fields)); native_object_relocations_.emplace( cur_fields, NativeObjectRelocation { - oat_index, offset, kNativeObjectRelocationTypeArtFieldArray + oat_index, offset, NativeObjectRelocationType::kArtFieldArray }); offset += header_size; // Forward individual fields so that we can quickly find where they belong. @@ -1420,9 +1421,14 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, DCHECK(!IsInBootImage(field)); native_object_relocations_.emplace( field, - NativeObjectRelocation { oat_index, offset, kNativeObjectRelocationTypeArtField }); + NativeObjectRelocation { oat_index, + offset, + NativeObjectRelocationType::kArtField }); offset += sizeof(ArtField); } + image_info.IncrementBinSlotSize( + Bin::kArtField, header_size + cur_fields->size() * sizeof(ArtField)); + DCHECK_EQ(offset, image_info.GetBinSlotSize(Bin::kArtField)); } } // Visit and assign offsets for methods. @@ -1436,8 +1442,8 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, } } NativeObjectRelocationType type = any_dirty - ? kNativeObjectRelocationTypeArtMethodDirty - : kNativeObjectRelocationTypeArtMethodClean; + ? NativeObjectRelocationType::kArtMethodDirty + : NativeObjectRelocationType::kArtMethodClean; Bin bin_type = BinTypeForNativeRelocationType(type); // Forward the entire array at once, but header first. const size_t method_alignment = ArtMethod::Alignment(target_ptr_size_); @@ -1449,15 +1455,15 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, auto it = native_object_relocations_.find(array); CHECK(it == native_object_relocations_.end()) << "Method array " << array << " already forwarded"; - size_t& offset = image_info.bin_slot_sizes_[bin_type]; + size_t offset = image_info.GetBinSlotSize(bin_type); DCHECK(!IsInBootImage(array)); native_object_relocations_.emplace(array, NativeObjectRelocation { oat_index, offset, - any_dirty ? kNativeObjectRelocationTypeArtMethodArrayDirty - : kNativeObjectRelocationTypeArtMethodArrayClean }); - offset += header_size; + any_dirty ? NativeObjectRelocationType::kArtMethodArrayDirty + : NativeObjectRelocationType::kArtMethodArrayClean }); + image_info.IncrementBinSlotSize(bin_type, header_size); for (auto& m : as_klass->GetMethods(target_ptr_size_)) { AssignMethodOffset(&m, type, oat_index); } @@ -1476,7 +1482,7 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, if (imt_method->IsRuntimeMethod() && !IsInBootImage(imt_method) && !NativeRelocationAssigned(imt_method)) { - AssignMethodOffset(imt_method, kNativeObjectRelocationTypeRuntimeMethod, oat_index); + AssignMethodOffset(imt_method, NativeObjectRelocationType::kRuntimeMethod, oat_index); } } } @@ -1526,9 +1532,9 @@ bool ImageWriter::TryAssignImTableOffset(ImTable* imt, size_t oat_index) { imt, NativeObjectRelocation { oat_index, - image_info.bin_slot_sizes_[kBinImTable], - kNativeObjectRelocationTypeIMTable}); - image_info.bin_slot_sizes_[kBinImTable] += size; + image_info.GetBinSlotSize(Bin::kImTable), + NativeObjectRelocationType::kIMTable}); + image_info.IncrementBinSlotSize(Bin::kImTable, size); return true; } @@ -1545,9 +1551,9 @@ void ImageWriter::TryAssignConflictTableOffset(ImtConflictTable* table, size_t o table, NativeObjectRelocation { oat_index, - image_info.bin_slot_sizes_[kBinIMTConflictTable], - kNativeObjectRelocationTypeIMTConflictTable}); - image_info.bin_slot_sizes_[kBinIMTConflictTable] += size; + image_info.GetBinSlotSize(Bin::kIMTConflictTable), + NativeObjectRelocationType::kIMTConflictTable}); + image_info.IncrementBinSlotSize(Bin::kIMTConflictTable, size); } void ImageWriter::AssignMethodOffset(ArtMethod* method, @@ -1560,9 +1566,10 @@ void ImageWriter::AssignMethodOffset(ArtMethod* method, TryAssignConflictTableOffset(method->GetImtConflictTable(target_ptr_size_), oat_index); } ImageInfo& image_info = GetImageInfo(oat_index); - size_t& offset = image_info.bin_slot_sizes_[BinTypeForNativeRelocationType(type)]; + Bin bin_type = BinTypeForNativeRelocationType(type); + size_t offset = image_info.GetBinSlotSize(bin_type); native_object_relocations_.emplace(method, NativeObjectRelocation { oat_index, offset, type }); - offset += ArtMethod::Size(target_ptr_size_); + image_info.IncrementBinSlotSize(bin_type, ArtMethod::Size(target_ptr_size_)); } void ImageWriter::UnbinObjectsIntoOffset(mirror::Object* obj) { @@ -1697,7 +1704,7 @@ void ImageWriter::CalculateNewObjectOffsets() { CHECK(m->IsRuntimeMethod()); DCHECK_EQ(compile_app_image_, IsInBootImage(m)) << "Trampolines should be in boot image"; if (!IsInBootImage(m)) { - AssignMethodOffset(m, kNativeObjectRelocationTypeRuntimeMethod, GetDefaultOatIndex()); + AssignMethodOffset(m, NativeObjectRelocationType::kRuntimeMethod, GetDefaultOatIndex()); } } @@ -1803,18 +1810,18 @@ void ImageWriter::CalculateNewObjectOffsets() { // Calculate bin slot offsets. for (ImageInfo& image_info : image_infos_) { size_t bin_offset = image_objects_offset_begin_; - for (size_t i = 0; i != kBinSize; ++i) { - switch (i) { - case kBinArtMethodClean: - case kBinArtMethodDirty: { + for (size_t i = 0; i != kNumberOfBins; ++i) { + switch (static_cast(i)) { + case Bin::kArtMethodClean: + case Bin::kArtMethodDirty: { bin_offset = RoundUp(bin_offset, method_alignment); break; } - case kBinDexCacheArray: + case Bin::kDexCacheArray: bin_offset = RoundUp(bin_offset, DexCacheArraysLayout::Alignment(target_ptr_size_)); break; - case kBinImTable: - case kBinIMTConflictTable: { + case Bin::kImTable: + case Bin::kIMTConflictTable: { bin_offset = RoundUp(bin_offset, static_cast(target_ptr_size_)); break; } @@ -1827,7 +1834,7 @@ void ImageWriter::CalculateNewObjectOffsets() { } // NOTE: There may be additional padding between the bin slots and the intern table. DCHECK_EQ(image_info.image_end_, - GetBinSizeSum(image_info, kBinMirrorCount) + image_objects_offset_begin_); + image_info.GetBinSizeSum(Bin::kMirrorCount) + image_objects_offset_begin_); } // Calculate image offsets. @@ -1864,7 +1871,7 @@ void ImageWriter::CalculateNewObjectOffsets() { NativeObjectRelocation& relocation = pair.second; Bin bin_type = BinTypeForNativeRelocationType(relocation.type); ImageInfo& image_info = GetImageInfo(relocation.oat_index); - relocation.offset += image_info.bin_slot_offsets_[bin_type]; + relocation.offset += image_info.GetBinSlotOffset(bin_type); } } @@ -1881,33 +1888,32 @@ size_t ImageWriter::ImageInfo::CreateImageSections(ImageSection* out_sections, // Add field section. ImageSection* field_section = &out_sections[ImageHeader::kSectionArtFields]; - *field_section = ImageSection(bin_slot_offsets_[kBinArtField], bin_slot_sizes_[kBinArtField]); - CHECK_EQ(bin_slot_offsets_[kBinArtField], field_section->Offset()); + *field_section = ImageSection(GetBinSlotOffset(Bin::kArtField), GetBinSlotSize(Bin::kArtField)); // Add method section. ImageSection* methods_section = &out_sections[ImageHeader::kSectionArtMethods]; *methods_section = ImageSection( - bin_slot_offsets_[kBinArtMethodClean], - bin_slot_sizes_[kBinArtMethodClean] + bin_slot_sizes_[kBinArtMethodDirty]); + GetBinSlotOffset(Bin::kArtMethodClean), + GetBinSlotSize(Bin::kArtMethodClean) + GetBinSlotSize(Bin::kArtMethodDirty)); // IMT section. ImageSection* imt_section = &out_sections[ImageHeader::kSectionImTables]; - *imt_section = ImageSection(bin_slot_offsets_[kBinImTable], bin_slot_sizes_[kBinImTable]); + *imt_section = ImageSection(GetBinSlotOffset(Bin::kImTable), GetBinSlotSize(Bin::kImTable)); // Conflict tables section. ImageSection* imt_conflict_tables_section = &out_sections[ImageHeader::kSectionIMTConflictTables]; - *imt_conflict_tables_section = ImageSection(bin_slot_offsets_[kBinIMTConflictTable], - bin_slot_sizes_[kBinIMTConflictTable]); + *imt_conflict_tables_section = ImageSection(GetBinSlotOffset(Bin::kIMTConflictTable), + GetBinSlotSize(Bin::kIMTConflictTable)); // Runtime methods section. ImageSection* runtime_methods_section = &out_sections[ImageHeader::kSectionRuntimeMethods]; - *runtime_methods_section = ImageSection(bin_slot_offsets_[kBinRuntimeMethod], - bin_slot_sizes_[kBinRuntimeMethod]); + *runtime_methods_section = ImageSection(GetBinSlotOffset(Bin::kRuntimeMethod), + GetBinSlotSize(Bin::kRuntimeMethod)); // Add dex cache arrays section. ImageSection* dex_cache_arrays_section = &out_sections[ImageHeader::kSectionDexCacheArrays]; - *dex_cache_arrays_section = ImageSection(bin_slot_offsets_[kBinDexCacheArray], - bin_slot_sizes_[kBinDexCacheArray]); + *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, @@ -2060,16 +2066,16 @@ void ImageWriter::CopyAndFixupNativeData(size_t oat_index) { DCHECK_GE(dest, image_info.image_->Begin() + image_info.image_end_); DCHECK(!IsInBootImage(pair.first)); switch (relocation.type) { - case kNativeObjectRelocationTypeArtField: { + case NativeObjectRelocationType::kArtField: { memcpy(dest, pair.first, sizeof(ArtField)); CopyReference( reinterpret_cast(dest)->GetDeclaringClassAddressWithoutBarrier(), reinterpret_cast(pair.first)->GetDeclaringClass().Ptr()); break; } - case kNativeObjectRelocationTypeRuntimeMethod: - case kNativeObjectRelocationTypeArtMethodClean: - case kNativeObjectRelocationTypeArtMethodDirty: { + case NativeObjectRelocationType::kRuntimeMethod: + case NativeObjectRelocationType::kArtMethodClean: + case NativeObjectRelocationType::kArtMethodDirty: { CopyAndFixupMethod(reinterpret_cast(pair.first), reinterpret_cast(dest), image_info); @@ -2077,12 +2083,12 @@ void ImageWriter::CopyAndFixupNativeData(size_t oat_index) { } // For arrays, copy just the header since the elements will get copied by their corresponding // relocations. - case kNativeObjectRelocationTypeArtFieldArray: { + case NativeObjectRelocationType::kArtFieldArray: { memcpy(dest, pair.first, LengthPrefixedArray::ComputeSize(0)); break; } - case kNativeObjectRelocationTypeArtMethodArrayClean: - case kNativeObjectRelocationTypeArtMethodArrayDirty: { + case NativeObjectRelocationType::kArtMethodArrayClean: + case NativeObjectRelocationType::kArtMethodArrayDirty: { 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)); @@ -2090,16 +2096,16 @@ void ImageWriter::CopyAndFixupNativeData(size_t oat_index) { reinterpret_cast*>(dest)->ClearPadding(size, alignment); break; } - case kNativeObjectRelocationTypeDexCacheArray: + case NativeObjectRelocationType::kDexCacheArray: // Nothing to copy here, everything is done in FixupDexCache(). break; - case kNativeObjectRelocationTypeIMTable: { + case NativeObjectRelocationType::kIMTable: { ImTable* orig_imt = reinterpret_cast(pair.first); ImTable* dest_imt = reinterpret_cast(dest); CopyAndFixupImTable(orig_imt, dest_imt); break; } - case kNativeObjectRelocationTypeIMTConflictTable: { + case NativeObjectRelocationType::kIMTConflictTable: { auto* orig_table = reinterpret_cast(pair.first); CopyAndFixupImtConflictTable( orig_table, @@ -2197,7 +2203,7 @@ void ImageWriter::FixupPointerArray(mirror::Object* dst, << method << " idx=" << i << "/" << num_elements << " with declaring class " << Class::PrettyClass(method->GetDeclaringClass()); } else { - CHECK_EQ(array_type, kBinArtField); + CHECK_EQ(array_type, Bin::kArtField); auto* field = reinterpret_cast(elem); LOG(FATAL) << "No relocation entry for ArtField " << field->PrettyField() << " @ " << field << " idx=" << i << "/" << num_elements << " with declaring class " @@ -2518,8 +2524,8 @@ void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache, copy_dex_cache->SetDexFile(nullptr); } -const uint8_t* ImageWriter::GetOatAddress(OatAddress type) const { - DCHECK_LT(type, kOatAddressCount); +const uint8_t* ImageWriter::GetOatAddress(StubType type) const { + DCHECK_LE(type, StubType::kLast); // If we are compiling an app image, we need to use the stubs of the boot image. if (compile_app_image_) { // Use the current image pointers. @@ -2531,26 +2537,26 @@ const uint8_t* ImageWriter::GetOatAddress(OatAddress type) const { const OatHeader& header = oat_file->GetOatHeader(); switch (type) { // TODO: We could maybe clean this up if we stored them in an array in the oat header. - case kOatAddressQuickGenericJNITrampoline: + case StubType::kQuickGenericJNITrampoline: return static_cast(header.GetQuickGenericJniTrampoline()); - case kOatAddressInterpreterToInterpreterBridge: + case StubType::kInterpreterToInterpreterBridge: return static_cast(header.GetInterpreterToInterpreterBridge()); - case kOatAddressInterpreterToCompiledCodeBridge: + case StubType::kInterpreterToCompiledCodeBridge: return static_cast(header.GetInterpreterToCompiledCodeBridge()); - case kOatAddressJNIDlsymLookup: + case StubType::kJNIDlsymLookup: return static_cast(header.GetJniDlsymLookup()); - case kOatAddressQuickIMTConflictTrampoline: + case StubType::kQuickIMTConflictTrampoline: return static_cast(header.GetQuickImtConflictTrampoline()); - case kOatAddressQuickResolutionTrampoline: + case StubType::kQuickResolutionTrampoline: return static_cast(header.GetQuickResolutionTrampoline()); - case kOatAddressQuickToInterpreterBridge: + case StubType::kQuickToInterpreterBridge: return static_cast(header.GetQuickToInterpreterBridge()); default: UNREACHABLE(); } } const ImageInfo& primary_image_info = GetImageInfo(0); - return GetOatAddressForOffset(primary_image_info.oat_address_offsets_[type], primary_image_info); + return GetOatAddressForOffset(primary_image_info.GetStubOffset(type), primary_image_info); } const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, @@ -2586,16 +2592,16 @@ const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, } else if (quick_code == nullptr && method->IsNative() && (!method->IsStatic() || method->GetDeclaringClass()->IsInitialized())) { // Non-static or initialized native method missing compiled code, use generic JNI version. - quick_code = GetOatAddress(kOatAddressQuickGenericJNITrampoline); + quick_code = GetOatAddress(StubType::kQuickGenericJNITrampoline); } else if (quick_code == nullptr && !method->IsNative()) { // We don't have code at all for a non-native method, use the interpreter. - quick_code = GetOatAddress(kOatAddressQuickToInterpreterBridge); + quick_code = GetOatAddress(StubType::kQuickToInterpreterBridge); *quick_is_interpreted = true; } else { CHECK(!method->GetDeclaringClass()->IsInitialized()); // We have code for a static method, but need to go through the resolution stub for class // initialization. - quick_code = GetOatAddress(kOatAddressQuickResolutionTrampoline); + quick_code = GetOatAddress(StubType::kQuickResolutionTrampoline); } if (!IsInBootOatFile(quick_code)) { // DCHECK_GE(quick_code, oat_data_begin_); @@ -2630,11 +2636,11 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, if (orig_table != nullptr) { // Special IMT conflict method, normal IMT conflict method or unimplemented IMT method. copy->SetEntryPointFromQuickCompiledCodePtrSize( - GetOatAddress(kOatAddressQuickIMTConflictTrampoline), target_ptr_size_); + GetOatAddress(StubType::kQuickIMTConflictTrampoline), target_ptr_size_); copy->SetImtConflictTable(NativeLocationInImage(orig_table), target_ptr_size_); } else if (UNLIKELY(orig == runtime->GetResolutionMethod())) { copy->SetEntryPointFromQuickCompiledCodePtrSize( - GetOatAddress(kOatAddressQuickResolutionTrampoline), target_ptr_size_); + GetOatAddress(StubType::kQuickResolutionTrampoline), target_ptr_size_); } else { bool found_one = false; for (size_t i = 0; i < static_cast(CalleeSaveType::kLastCalleeSaveType); ++i) { @@ -2653,7 +2659,7 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, // use results in an AbstractMethodError. We use the interpreter to achieve this. if (UNLIKELY(!orig->IsInvokable())) { copy->SetEntryPointFromQuickCompiledCodePtrSize( - GetOatAddress(kOatAddressQuickToInterpreterBridge), target_ptr_size_); + GetOatAddress(StubType::kQuickToInterpreterBridge), target_ptr_size_); } else { bool quick_is_interpreted; const uint8_t* quick_code = GetQuickCode(orig, image_info, &quick_is_interpreted); @@ -2664,17 +2670,17 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, // The native method's pointer is set to a stub to lookup via dlsym. // Note this is not the code_ pointer, that is handled above. copy->SetEntryPointFromJniPtrSize( - GetOatAddress(kOatAddressJNIDlsymLookup), target_ptr_size_); + GetOatAddress(StubType::kJNIDlsymLookup), target_ptr_size_); } } } } -size_t ImageWriter::GetBinSizeSum(ImageWriter::ImageInfo& image_info, ImageWriter::Bin up_to) const { - DCHECK_LE(up_to, kBinSize); - return std::accumulate(&image_info.bin_slot_sizes_[0], - &image_info.bin_slot_sizes_[up_to], - /*init*/0); +size_t ImageWriter::ImageInfo::GetBinSizeSum(Bin up_to) const { + DCHECK_LE(static_cast(up_to), kNumberOfBins); + return std::accumulate(&bin_slot_sizes_[0], + &bin_slot_sizes_[0] + static_cast(up_to), + /*init*/ static_cast(0)); } ImageWriter::BinSlot::BinSlot(uint32_t lockword) : lockword_(lockword) { @@ -2683,7 +2689,7 @@ ImageWriter::BinSlot::BinSlot(uint32_t lockword) : lockword_(lockword) { static_assert(kBinShift == 27, "wrong number of shift"); static_assert(sizeof(BinSlot) == sizeof(LockWord), "BinSlot/LockWord must have equal sizes"); - DCHECK_LT(GetBin(), kBinSize); + DCHECK_LT(GetBin(), Bin::kMirrorCount); DCHECK_ALIGNED(GetIndex(), kObjectAlignment); } @@ -2702,23 +2708,23 @@ uint32_t ImageWriter::BinSlot::GetIndex() const { ImageWriter::Bin ImageWriter::BinTypeForNativeRelocationType(NativeObjectRelocationType type) { switch (type) { - case kNativeObjectRelocationTypeArtField: - case kNativeObjectRelocationTypeArtFieldArray: - return kBinArtField; - case kNativeObjectRelocationTypeArtMethodClean: - case kNativeObjectRelocationTypeArtMethodArrayClean: - return kBinArtMethodClean; - case kNativeObjectRelocationTypeArtMethodDirty: - case kNativeObjectRelocationTypeArtMethodArrayDirty: - return kBinArtMethodDirty; - case kNativeObjectRelocationTypeDexCacheArray: - return kBinDexCacheArray; - case kNativeObjectRelocationTypeRuntimeMethod: - return kBinRuntimeMethod; - case kNativeObjectRelocationTypeIMTable: - return kBinImTable; - case kNativeObjectRelocationTypeIMTConflictTable: - return kBinIMTConflictTable; + case NativeObjectRelocationType::kArtField: + case NativeObjectRelocationType::kArtFieldArray: + return Bin::kArtField; + case NativeObjectRelocationType::kArtMethodClean: + case NativeObjectRelocationType::kArtMethodArrayClean: + return Bin::kArtMethodClean; + case NativeObjectRelocationType::kArtMethodDirty: + case NativeObjectRelocationType::kArtMethodArrayDirty: + return Bin::kArtMethodDirty; + case NativeObjectRelocationType::kDexCacheArray: + return Bin::kDexCacheArray; + case NativeObjectRelocationType::kRuntimeMethod: + return Bin::kRuntimeMethod; + case NativeObjectRelocationType::kIMTable: + return Bin::kImTable; + case NativeObjectRelocationType::kIMTConflictTable: + return Bin::kIMTConflictTable; } UNREACHABLE(); } @@ -2782,20 +2788,20 @@ void ImageWriter::UpdateOatFileHeader(size_t oat_index, const OatHeader& oat_hea if (oat_index == GetDefaultOatIndex()) { // Primary oat file, read the trampolines. - cur_image_info.oat_address_offsets_[kOatAddressInterpreterToInterpreterBridge] = - oat_header.GetInterpreterToInterpreterBridgeOffset(); - cur_image_info.oat_address_offsets_[kOatAddressInterpreterToCompiledCodeBridge] = - oat_header.GetInterpreterToCompiledCodeBridgeOffset(); - cur_image_info.oat_address_offsets_[kOatAddressJNIDlsymLookup] = - oat_header.GetJniDlsymLookupOffset(); - cur_image_info.oat_address_offsets_[kOatAddressQuickGenericJNITrampoline] = - oat_header.GetQuickGenericJniTrampolineOffset(); - cur_image_info.oat_address_offsets_[kOatAddressQuickIMTConflictTrampoline] = - oat_header.GetQuickImtConflictTrampolineOffset(); - cur_image_info.oat_address_offsets_[kOatAddressQuickResolutionTrampoline] = - oat_header.GetQuickResolutionTrampolineOffset(); - cur_image_info.oat_address_offsets_[kOatAddressQuickToInterpreterBridge] = - oat_header.GetQuickToInterpreterBridgeOffset(); + cur_image_info.SetStubOffset(StubType::kInterpreterToInterpreterBridge, + oat_header.GetInterpreterToInterpreterBridgeOffset()); + cur_image_info.SetStubOffset(StubType::kInterpreterToCompiledCodeBridge, + oat_header.GetInterpreterToCompiledCodeBridgeOffset()); + cur_image_info.SetStubOffset(StubType::kJNIDlsymLookup, + oat_header.GetJniDlsymLookupOffset()); + cur_image_info.SetStubOffset(StubType::kQuickGenericJNITrampoline, + oat_header.GetQuickGenericJniTrampolineOffset()); + cur_image_info.SetStubOffset(StubType::kQuickIMTConflictTrampoline, + oat_header.GetQuickImtConflictTrampolineOffset()); + cur_image_info.SetStubOffset(StubType::kQuickResolutionTrampoline, + oat_header.GetQuickResolutionTrampolineOffset()); + cur_image_info.SetStubOffset(StubType::kQuickToInterpreterBridge, + oat_header.GetQuickToInterpreterBridgeOffset()); } } diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h index 68c7b594fb..3aceceb80c 100644 --- a/dex2oat/linker/image_writer.h +++ b/dex2oat/linker/image_writer.h @@ -161,70 +161,70 @@ class ImageWriter FINAL { // Classify different kinds of bins that objects end up getting packed into during image writing. // Ordered from dirtiest to cleanest (until ArtMethods). - enum Bin { - kBinKnownDirty, // Known dirty objects from --dirty-image-objects list - kBinMiscDirty, // Dex caches, object locks, etc... - kBinClassVerified, // Class verified, but initializers haven't been run + enum class Bin { + kKnownDirty, // Known dirty objects from --dirty-image-objects list + kMiscDirty, // Dex caches, object locks, etc... + kClassVerified, // Class verified, but initializers haven't been run // Unknown mix of clean/dirty: - kBinRegular, - kBinClassInitialized, // Class initializers have been run + kRegular, + kClassInitialized, // Class initializers have been run // All classes get their own bins since their fields often dirty - kBinClassInitializedFinalStatics, // Class initializers have been run, no non-final statics + kClassInitializedFinalStatics, // Class initializers have been run, no non-final statics // Likely-clean: - kBinString, // [String] Almost always immutable (except for obj header). + kString, // [String] Almost always immutable (except for obj header). // Add more bins here if we add more segregation code. // Non mirror fields must be below. // ArtFields should be always clean. - kBinArtField, + kArtField, // If the class is initialized, then the ArtMethods are probably clean. - kBinArtMethodClean, + kArtMethodClean, // ArtMethods may be dirty if the class has native methods or a declaring class that isn't // initialized. - kBinArtMethodDirty, + kArtMethodDirty, // IMT (clean) - kBinImTable, + kImTable, // Conflict tables (clean). - kBinIMTConflictTable, + kIMTConflictTable, // Runtime methods (always clean, do not have a length prefix array). - kBinRuntimeMethod, + kRuntimeMethod, // Dex cache arrays have a special slot for PC-relative addressing. Since they are // huge, and as such their dirtiness is not important for the clean/dirty separation, // we arbitrarily keep them at the end of the native data. - kBinDexCacheArray, // Arrays belonging to dex cache. - kBinSize, + kDexCacheArray, // Arrays belonging to dex cache. + kLast = kDexCacheArray, // Number of bins which are for mirror objects. - kBinMirrorCount = kBinArtField, + kMirrorCount = kArtField, }; friend std::ostream& operator<<(std::ostream& stream, const Bin& bin); - enum NativeObjectRelocationType { - kNativeObjectRelocationTypeArtField, - kNativeObjectRelocationTypeArtFieldArray, - kNativeObjectRelocationTypeArtMethodClean, - kNativeObjectRelocationTypeArtMethodArrayClean, - kNativeObjectRelocationTypeArtMethodDirty, - kNativeObjectRelocationTypeArtMethodArrayDirty, - kNativeObjectRelocationTypeRuntimeMethod, - kNativeObjectRelocationTypeIMTable, - kNativeObjectRelocationTypeIMTConflictTable, - kNativeObjectRelocationTypeDexCacheArray, + enum class NativeObjectRelocationType { + kArtField, + kArtFieldArray, + kArtMethodClean, + kArtMethodArrayClean, + kArtMethodDirty, + kArtMethodArrayDirty, + kRuntimeMethod, + kIMTable, + kIMTConflictTable, + kDexCacheArray, }; friend std::ostream& operator<<(std::ostream& stream, const NativeObjectRelocationType& type); - enum OatAddress { - kOatAddressInterpreterToInterpreterBridge, - kOatAddressInterpreterToCompiledCodeBridge, - kOatAddressJNIDlsymLookup, - kOatAddressQuickGenericJNITrampoline, - kOatAddressQuickIMTConflictTrampoline, - kOatAddressQuickResolutionTrampoline, - kOatAddressQuickToInterpreterBridge, - // Number of elements in the enum. - kOatAddressCount, + enum class StubType { + kInterpreterToInterpreterBridge, + kInterpreterToCompiledCodeBridge, + kJNIDlsymLookup, + kQuickGenericJNITrampoline, + kQuickIMTConflictTrampoline, + kQuickResolutionTrampoline, + kQuickToInterpreterBridge, + kLast = kQuickToInterpreterBridge, }; - friend std::ostream& operator<<(std::ostream& stream, const OatAddress& oat_address); + friend std::ostream& operator<<(std::ostream& stream, const StubType& stub_type); - static constexpr size_t kBinBits = MinimumBitsToStore(kBinMirrorCount - 1); + static constexpr size_t kBinBits = + MinimumBitsToStore(static_cast(Bin::kMirrorCount) - 1); // uint32 = typeof(lockword_) // Subtract read barrier bits since we want these to remain 0, or else it may result in DCHECK // failures due to invalid read barrier bits during object field reads. @@ -232,6 +232,12 @@ class ImageWriter FINAL { // 111000.....0 static const size_t kBinMask = ((static_cast(1) << kBinBits) - 1) << kBinShift; + // Number of bins, including non-mirror bins. + static constexpr size_t kNumberOfBins = static_cast(Bin::kLast) + 1u; + + // Number of stub types. + static constexpr size_t kNumberOfStubTypes = static_cast(StubType::kLast) + 1u; + // We use the lock word to store the bin # and bin index of the object in the image. // // The struct size must be exactly sizeof(LockWord), currently 32-bits, since this will end up @@ -262,6 +268,39 @@ class ImageWriter FINAL { // excluding the bitmap. size_t CreateImageSections(ImageSection* out_sections, bool app_image) const; + size_t GetStubOffset(StubType stub_type) const { + DCHECK_LT(static_cast(stub_type), kNumberOfStubTypes); + return stub_offsets_[static_cast(stub_type)]; + } + + void SetStubOffset(StubType stub_type, size_t offset) { + DCHECK_LT(static_cast(stub_type), kNumberOfStubTypes); + stub_offsets_[static_cast(stub_type)] = offset; + } + + size_t GetBinSlotOffset(Bin bin) const { + DCHECK_LT(static_cast(bin), kNumberOfBins); + return bin_slot_offsets_[static_cast(bin)]; + } + + void IncrementBinSlotSize(Bin bin, size_t size_to_add) { + DCHECK_LT(static_cast(bin), kNumberOfBins); + bin_slot_sizes_[static_cast(bin)] += size_to_add; + } + + size_t GetBinSlotSize(Bin bin) const { + DCHECK_LT(static_cast(bin), kNumberOfBins); + return bin_slot_sizes_[static_cast(bin)]; + } + + void IncrementBinSlotCount(Bin bin, size_t count_to_add) { + DCHECK_LT(static_cast(bin), kNumberOfBins); + bin_slot_count_[static_cast(bin)] += count_to_add; + } + + // Calculate the sum total of the bin slot sizes in [0, up_to). Defaults to all bins. + size_t GetBinSizeSum(Bin up_to) const; + std::unique_ptr image_; // Memory mapped for generating the image. // Target begin of this image. Notes: It is not valid to write here, this is the address @@ -300,12 +339,12 @@ class ImageWriter FINAL { SafeMap dex_cache_array_starts_; // Offset from oat_data_begin_ to the stubs. - uint32_t oat_address_offsets_[kOatAddressCount] = {}; + uint32_t stub_offsets_[kNumberOfStubTypes] = {}; // Bin slot tracking for dirty object packing. - size_t bin_slot_sizes_[kBinSize] = {}; // Number of bytes in a bin. - size_t bin_slot_offsets_[kBinSize] = {}; // Number of bytes in previous bins. - size_t bin_slot_count_[kBinSize] = {}; // Number of objects in a bin. + size_t bin_slot_sizes_[kNumberOfBins] = {}; // Number of bytes in a bin. + size_t bin_slot_offsets_[kNumberOfBins] = {}; // Number of bytes in previous bins. + size_t bin_slot_count_[kNumberOfBins] = {}; // Number of objects in a bin. // Cached size of the intern table for when we allocate memory. size_t intern_table_bytes_ = 0; @@ -367,7 +406,7 @@ class ImageWriter FINAL { } // Returns the address in the boot image if we are compiling the app image. - const uint8_t* GetOatAddress(OatAddress type) const; + const uint8_t* GetOatAddress(StubType type) const; const uint8_t* GetOatAddressForOffset(uint32_t offset, const ImageInfo& image_info) const { // With Quick, code is within the OatFile, as there are all in one @@ -443,9 +482,6 @@ class ImageWriter FINAL { bool* quick_is_interpreted) REQUIRES_SHARED(Locks::mutator_lock_); - // Calculate the sum total of the bin slot sizes in [0, up_to). Defaults to all bins. - size_t GetBinSizeSum(ImageInfo& image_info, Bin up_to = kBinSize) const; - // Return true if a method is likely to be dirtied at runtime. bool WillMethodBeDirty(ArtMethod* m) const REQUIRES_SHARED(Locks::mutator_lock_); @@ -572,9 +608,9 @@ class ImageWriter FINAL { NativeObjectRelocationType type; bool IsArtMethodRelocation() const { - return type == kNativeObjectRelocationTypeArtMethodClean || - type == kNativeObjectRelocationTypeArtMethodDirty || - type == kNativeObjectRelocationTypeRuntimeMethod; + return type == NativeObjectRelocationType::kArtMethodClean || + type == NativeObjectRelocationType::kArtMethodDirty || + type == NativeObjectRelocationType::kRuntimeMethod; } }; std::unordered_map native_object_relocations_; -- GitLab From 8ddb7f5c9a3fa2b31e87efcf785a1e427f042bbb Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 4 Dec 2017 10:14:08 -0800 Subject: [PATCH 123/226] ART: Reorder null check in verifier Do not execute DCHECK before null check. Bug: 69606743 Test: m test-art-host Change-Id: I63b49b003943d7e902adefd4763310e1541ce732 --- runtime/verifier/method_verifier.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 978e51d7b0..be58a57c24 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -5155,10 +5155,11 @@ ArtField* MethodVerifier::GetQuickFieldAccess(const Instruction* inst, RegisterL } uint32_t field_offset = static_cast(inst->VRegC_22c()); ArtField* const f = ArtField::FindInstanceFieldWithOffset(object_type.GetClass(), field_offset); - DCHECK_EQ(f->GetOffset().Uint32Value(), 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); } return f; } -- GitLab From a247dea6612156deaf17e4ab099e8ba2b2d30536 Mon Sep 17 00:00:00 2001 From: Alexey Frunze Date: Fri, 3 Nov 2017 18:43:04 -0700 Subject: [PATCH 124/226] MIPS32: Fix and refactor in/out reg mask code This is mostly for clarity and future work. This fixes the following: - aui has an out reg, not an in/out reg - maddv.df, msubv.df, fmadd.df, fmsub.df have an in/out reg, not a simply out reg This also ensures consistent marking of even-numbered 32-bit FPRs used by FPR load and store instructions (odd-numbered 32-bit FPRs remain unmarked as if there are no paired FPRs; we don't use odd-numbered 32-bit FPRs to hold single-precision values). Test: test-art-host-gtest Test: booted MIPS32R2 in QEMU Test: booted MIPS64R6 in QEMU Test: testrunner.py --target --optimizing --32 (on CI20 and MIPS32R6) Test: test-art-target-gtest32 (on CI20 and MIPS32R6) Change-Id: I408b8ac063c9b1cc6f036dda095d1e3e1e2e1ef1 --- compiler/utils/mips/assembler_mips.cc | 1394 ++++++++----------------- compiler/utils/mips/assembler_mips.h | 127 ++- 2 files changed, 521 insertions(+), 1000 deletions(-) diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc index eb75f8b67c..2218ef9af2 100644 --- a/compiler/utils/mips/assembler_mips.cc +++ b/compiler/utils/mips/assembler_mips.cc @@ -42,26 +42,13 @@ std::ostream& operator<<(std::ostream& os, const DRegister& rhs) { MipsAssembler::DelaySlot::DelaySlot() : instruction_(0), - gpr_outs_mask_(0), - gpr_ins_mask_(0), - fpr_outs_mask_(0), - fpr_ins_mask_(0), - cc_outs_mask_(0), - cc_ins_mask_(0), patcher_label_(nullptr) {} -void MipsAssembler::DsFsmInstr(uint32_t instruction, - uint32_t gpr_outs_mask, - uint32_t gpr_ins_mask, - uint32_t fpr_outs_mask, - uint32_t fpr_ins_mask, - uint32_t cc_outs_mask, - uint32_t cc_ins_mask, - MipsLabel* patcher_label) { +InOutRegMasks& MipsAssembler::DsFsmInstr(uint32_t instruction, MipsLabel* patcher_label) { if (!reordering_) { CHECK_EQ(ds_fsm_state_, kExpectingLabel); CHECK_EQ(delay_slot_.instruction_, 0u); - return; + return delay_slot_.masks_; } switch (ds_fsm_state_) { case kExpectingLabel: @@ -92,13 +79,9 @@ void MipsAssembler::DsFsmInstr(uint32_t instruction, break; } delay_slot_.instruction_ = instruction; - delay_slot_.gpr_outs_mask_ = gpr_outs_mask & ~1u; // Ignore register ZERO. - delay_slot_.gpr_ins_mask_ = gpr_ins_mask & ~1u; // Ignore register ZERO. - delay_slot_.fpr_outs_mask_ = fpr_outs_mask; - delay_slot_.fpr_ins_mask_ = fpr_ins_mask; - delay_slot_.cc_outs_mask_ = cc_outs_mask; - delay_slot_.cc_ins_mask_ = cc_ins_mask; + delay_slot_.masks_ = InOutRegMasks(); delay_slot_.patcher_label_ = patcher_label; + return delay_slot_.masks_; } void MipsAssembler::DsFsmLabel() { @@ -167,73 +150,7 @@ size_t MipsAssembler::CodePosition() { } void MipsAssembler::DsFsmInstrNop(uint32_t instruction ATTRIBUTE_UNUSED) { - DsFsmInstr(0, 0, 0, 0, 0, 0, 0); -} - -void MipsAssembler::DsFsmInstrRrr(uint32_t instruction, - Register out, - Register in1, - Register in2, - MipsLabel* patcher_label) { - DsFsmInstr(instruction, (1u << out), (1u << in1) | (1u << in2), 0, 0, 0, 0, patcher_label); -} - -void MipsAssembler::DsFsmInstrRrrr(uint32_t instruction, - Register in1_out, - Register in2, - Register in3) { - DsFsmInstr(instruction, (1u << in1_out), (1u << in1_out) | (1u << in2) | (1u << in3), 0, 0, 0, 0); -} - -void MipsAssembler::DsFsmInstrFff(uint32_t instruction, - FRegister out, - FRegister in1, - FRegister in2) { - DsFsmInstr(instruction, 0, 0, (1u << out), (1u << in1) | (1u << in2), 0, 0); -} - -void MipsAssembler::DsFsmInstrFfff(uint32_t instruction, - FRegister in1_out, - FRegister in2, - FRegister in3) { - DsFsmInstr(instruction, 0, 0, (1u << in1_out), (1u << in1_out) | (1u << in2) | (1u << in3), 0, 0); -} - -void MipsAssembler::DsFsmInstrFffr(uint32_t instruction, - FRegister in1_out, - FRegister in2, - Register in3) { - DsFsmInstr(instruction, 0, (1u << in3), (1u << in1_out), (1u << in1_out) | (1u << in2), 0, 0); -} - -void MipsAssembler::DsFsmInstrRf(uint32_t instruction, Register out, FRegister in) { - DsFsmInstr(instruction, (1u << out), 0, 0, (1u << in), 0, 0); -} - -void MipsAssembler::DsFsmInstrFr(uint32_t instruction, FRegister out, Register in) { - DsFsmInstr(instruction, 0, (1u << in), (1u << out), 0, 0, 0); -} - -void MipsAssembler::DsFsmInstrFR(uint32_t instruction, FRegister in1, Register in2) { - DsFsmInstr(instruction, 0, (1u << in2), 0, (1u << in1), 0, 0); -} - -void MipsAssembler::DsFsmInstrCff(uint32_t instruction, int cc_out, FRegister in1, FRegister in2) { - DsFsmInstr(instruction, 0, 0, 0, (1u << in1) | (1u << in2), (1 << cc_out), 0); -} - -void MipsAssembler::DsFsmInstrRrrc(uint32_t instruction, - Register in1_out, - Register in2, - int cc_in) { - DsFsmInstr(instruction, (1u << in1_out), (1u << in1_out) | (1u << in2), 0, 0, 0, (1 << cc_in)); -} - -void MipsAssembler::DsFsmInstrFffc(uint32_t instruction, - FRegister in1_out, - FRegister in2, - int cc_in) { - DsFsmInstr(instruction, 0, 0, (1u << in1_out), (1u << in1_out) | (1u << in2), 0, (1 << cc_in)); + DsFsmInstr(0); } void MipsAssembler::FinalizeCode() { @@ -535,14 +452,14 @@ uint32_t MipsAssembler::EmitMsa2RF(int operation, } void MipsAssembler::Addu(Register rd, Register rs, Register rt) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x21), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x21)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Addiu(Register rt, Register rs, uint16_t imm16, MipsLabel* patcher_label) { if (patcher_label != nullptr) { Bind(patcher_label); } - DsFsmInstrRrr(EmitI(0x9, rs, rt, imm16), rt, rs, rs, patcher_label); + DsFsmInstr(EmitI(0x9, rs, rt, imm16), patcher_label).GprOuts(rt).GprIns(rs); } void MipsAssembler::Addiu(Register rt, Register rs, uint16_t imm16) { @@ -550,32 +467,32 @@ void MipsAssembler::Addiu(Register rt, Register rs, uint16_t imm16) { } void MipsAssembler::Subu(Register rd, Register rs, Register rt) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x23), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x23)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::MultR2(Register rs, Register rt) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, static_cast(0), 0, 0x18), ZERO, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, static_cast(0), 0, 0x18)).GprIns(rs, rt); } void MipsAssembler::MultuR2(Register rs, Register rt) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, static_cast(0), 0, 0x19), ZERO, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, static_cast(0), 0, 0x19)).GprIns(rs, rt); } void MipsAssembler::DivR2(Register rs, Register rt) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, static_cast(0), 0, 0x1a), ZERO, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, static_cast(0), 0, 0x1a)).GprIns(rs, rt); } void MipsAssembler::DivuR2(Register rs, Register rt) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, static_cast(0), 0, 0x1b), ZERO, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, static_cast(0), 0, 0x1b)).GprIns(rs, rt); } void MipsAssembler::MulR2(Register rd, Register rs, Register rt) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0x1c, rs, rt, rd, 0, 2), rd, rs, rt); + DsFsmInstr(EmitR(0x1c, rs, rt, rd, 0, 2)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::DivR2(Register rd, Register rs, Register rt) { @@ -604,179 +521,181 @@ void MipsAssembler::ModuR2(Register rd, Register rs, Register rt) { void MipsAssembler::MulR6(Register rd, Register rs, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 2, 0x18), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 2, 0x18)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::MuhR6(Register rd, Register rs, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x18), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 3, 0x18)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::MuhuR6(Register rd, Register rs, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x19), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 3, 0x19)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::DivR6(Register rd, Register rs, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 2, 0x1a), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 2, 0x1a)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::ModR6(Register rd, Register rs, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x1a), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 3, 0x1a)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::DivuR6(Register rd, Register rs, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 2, 0x1b), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 2, 0x1b)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::ModuR6(Register rd, Register rs, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x1b), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 3, 0x1b)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::And(Register rd, Register rs, Register rt) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x24), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x24)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Andi(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0xc, rs, rt, imm16), rt, rs, rs); + DsFsmInstr(EmitI(0xc, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::Or(Register rd, Register rs, Register rt) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x25), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x25)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Ori(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0xd, rs, rt, imm16), rt, rs, rs); + DsFsmInstr(EmitI(0xd, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::Xor(Register rd, Register rs, Register rt) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x26), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x26)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Xori(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0xe, rs, rt, imm16), rt, rs, rs); + DsFsmInstr(EmitI(0xe, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::Nor(Register rd, Register rs, Register rt) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x27), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x27)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Movz(Register rd, Register rs, Register rt) { CHECK(!IsR6()); - DsFsmInstrRrrr(EmitR(0, rs, rt, rd, 0, 0x0A), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x0A)).GprInOuts(rd).GprIns(rs, rt); } void MipsAssembler::Movn(Register rd, Register rs, Register rt) { CHECK(!IsR6()); - DsFsmInstrRrrr(EmitR(0, rs, rt, rd, 0, 0x0B), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x0B)).GprInOuts(rd).GprIns(rs, rt); } void MipsAssembler::Seleqz(Register rd, Register rs, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x35), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x35)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Selnez(Register rd, Register rs, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x37), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x37)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::ClzR6(Register rd, Register rs) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, static_cast(0), rd, 0x01, 0x10), rd, rs, rs); + DsFsmInstr(EmitR(0, rs, static_cast(0), rd, 0x01, 0x10)).GprOuts(rd).GprIns(rs); } void MipsAssembler::ClzR2(Register rd, Register rs) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0x1C, rs, rd, rd, 0, 0x20), rd, rs, rs); + DsFsmInstr(EmitR(0x1C, rs, rd, rd, 0, 0x20)).GprOuts(rd).GprIns(rs); } void MipsAssembler::CloR6(Register rd, Register rs) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, static_cast(0), rd, 0x01, 0x11), rd, rs, rs); + DsFsmInstr(EmitR(0, rs, static_cast(0), rd, 0x01, 0x11)).GprOuts(rd).GprIns(rs); } void MipsAssembler::CloR2(Register rd, Register rs) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0x1C, rs, rd, rd, 0, 0x21), rd, rs, rs); + DsFsmInstr(EmitR(0x1C, rs, rd, rd, 0, 0x21)).GprOuts(rd).GprIns(rs); } void MipsAssembler::Seb(Register rd, Register rt) { - DsFsmInstrRrr(EmitR(0x1f, static_cast(0), rt, rd, 0x10, 0x20), rd, rt, rt); + DsFsmInstr(EmitR(0x1f, static_cast(0), rt, rd, 0x10, 0x20)).GprOuts(rd).GprIns(rt); } void MipsAssembler::Seh(Register rd, Register rt) { - DsFsmInstrRrr(EmitR(0x1f, static_cast(0), rt, rd, 0x18, 0x20), rd, rt, rt); + DsFsmInstr(EmitR(0x1f, static_cast(0), rt, rd, 0x18, 0x20)).GprOuts(rd).GprIns(rt); } void MipsAssembler::Wsbh(Register rd, Register rt) { - DsFsmInstrRrr(EmitR(0x1f, static_cast(0), rt, rd, 2, 0x20), rd, rt, rt); + DsFsmInstr(EmitR(0x1f, static_cast(0), rt, rd, 2, 0x20)).GprOuts(rd).GprIns(rt); } void MipsAssembler::Bitswap(Register rd, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0x1f, static_cast(0), rt, rd, 0x0, 0x20), rd, rt, rt); + DsFsmInstr(EmitR(0x1f, static_cast(0), rt, rd, 0x0, 0x20)).GprOuts(rd).GprIns(rt); } void MipsAssembler::Sll(Register rd, Register rt, int shamt) { CHECK(IsUint<5>(shamt)) << shamt; - DsFsmInstrRrr(EmitR(0, static_cast(0), rt, rd, shamt, 0x00), rd, rt, rt); + DsFsmInstr(EmitR(0, static_cast(0), rt, rd, shamt, 0x00)).GprOuts(rd).GprIns(rt); } void MipsAssembler::Srl(Register rd, Register rt, int shamt) { CHECK(IsUint<5>(shamt)) << shamt; - DsFsmInstrRrr(EmitR(0, static_cast(0), rt, rd, shamt, 0x02), rd, rt, rt); + DsFsmInstr(EmitR(0, static_cast(0), rt, rd, shamt, 0x02)).GprOuts(rd).GprIns(rt); } void MipsAssembler::Rotr(Register rd, Register rt, int shamt) { CHECK(IsUint<5>(shamt)) << shamt; - DsFsmInstrRrr(EmitR(0, static_cast(1), rt, rd, shamt, 0x02), rd, rt, rt); + DsFsmInstr(EmitR(0, static_cast(1), rt, rd, shamt, 0x02)).GprOuts(rd).GprIns(rt); } void MipsAssembler::Sra(Register rd, Register rt, int shamt) { CHECK(IsUint<5>(shamt)) << shamt; - DsFsmInstrRrr(EmitR(0, static_cast(0), rt, rd, shamt, 0x03), rd, rt, rt); + DsFsmInstr(EmitR(0, static_cast(0), rt, rd, shamt, 0x03)).GprOuts(rd).GprIns(rt); } void MipsAssembler::Sllv(Register rd, Register rt, Register rs) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x04), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x04)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Srlv(Register rd, Register rt, Register rs) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x06), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x06)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Rotrv(Register rd, Register rt, Register rs) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 1, 0x06), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 1, 0x06)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Srav(Register rd, Register rt, Register rs) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x07), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x07)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Ext(Register rd, Register rt, int pos, int size) { CHECK(IsUint<5>(pos)) << pos; CHECK(0 < size && size <= 32) << size; CHECK(0 < pos + size && pos + size <= 32) << pos << " + " << size; - DsFsmInstrRrr(EmitR(0x1f, rt, rd, static_cast(size - 1), pos, 0x00), rd, rt, rt); + DsFsmInstr(EmitR(0x1f, rt, rd, static_cast(size - 1), pos, 0x00)) + .GprOuts(rd).GprIns(rt); } void MipsAssembler::Ins(Register rd, Register rt, int pos, int size) { CHECK(IsUint<5>(pos)) << pos; CHECK(0 < size && size <= 32) << size; CHECK(0 < pos + size && pos + size <= 32) << pos << " + " << size; - DsFsmInstrRrr(EmitR(0x1f, rt, rd, static_cast(pos + size - 1), pos, 0x04), rd, rd, rt); + DsFsmInstr(EmitR(0x1f, rt, rd, static_cast(pos + size - 1), pos, 0x04)) + .GprInOuts(rd).GprIns(rt); } void MipsAssembler::Lsa(Register rd, Register rs, Register rt, int saPlusOne) { CHECK(IsR6() || HasMsa()); CHECK(1 <= saPlusOne && saPlusOne <= 4) << saPlusOne; int sa = saPlusOne - 1; - DsFsmInstrRrr(EmitR(0x0, rs, rt, rd, sa, 0x05), rd, rs, rt); + DsFsmInstr(EmitR(0x0, rs, rt, rd, sa, 0x05)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::ShiftAndAdd(Register dst, @@ -798,18 +717,18 @@ void MipsAssembler::ShiftAndAdd(Register dst, } void MipsAssembler::Lb(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0x20, rs, rt, imm16), rt, rs, rs); + DsFsmInstr(EmitI(0x20, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::Lh(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0x21, rs, rt, imm16), rt, rs, rs); + DsFsmInstr(EmitI(0x21, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16, MipsLabel* patcher_label) { if (patcher_label != nullptr) { Bind(patcher_label); } - DsFsmInstrRrr(EmitI(0x23, rs, rt, imm16), rt, rs, rs, patcher_label); + DsFsmInstr(EmitI(0x23, rs, rt, imm16), patcher_label).GprOuts(rt).GprIns(rs); } void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16) { @@ -818,20 +737,20 @@ void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16) { void MipsAssembler::Lwl(Register rt, Register rs, uint16_t imm16) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitI(0x22, rs, rt, imm16), rt, rt, rs); + DsFsmInstr(EmitI(0x22, rs, rt, imm16)).GprInOuts(rt).GprIns(rs); } void MipsAssembler::Lwr(Register rt, Register rs, uint16_t imm16) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitI(0x26, rs, rt, imm16), rt, rt, rs); + DsFsmInstr(EmitI(0x26, rs, rt, imm16)).GprInOuts(rt).GprIns(rs); } void MipsAssembler::Lbu(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0x24, rs, rt, imm16), rt, rs, rs); + DsFsmInstr(EmitI(0x24, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::Lhu(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0x25, rs, rt, imm16), rt, rs, rs); + DsFsmInstr(EmitI(0x25, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::Lwpc(Register rs, uint32_t imm19) { @@ -841,12 +760,12 @@ void MipsAssembler::Lwpc(Register rs, uint32_t imm19) { } void MipsAssembler::Lui(Register rt, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0xf, static_cast(0), rt, imm16), rt, ZERO, ZERO); + DsFsmInstr(EmitI(0xf, static_cast(0), rt, imm16)).GprOuts(rt); } void MipsAssembler::Aui(Register rt, Register rs, uint16_t imm16) { CHECK(IsR6()); - DsFsmInstrRrr(EmitI(0xf, rs, rt, imm16), rt, rt, rs); + DsFsmInstr(EmitI(0xf, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::AddUpper(Register rt, Register rs, uint16_t imm16, Register tmp) { @@ -871,27 +790,27 @@ void MipsAssembler::Sync(uint32_t stype) { void MipsAssembler::Mfhi(Register rd) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0, ZERO, ZERO, rd, 0, 0x10), rd, ZERO, ZERO); + DsFsmInstr(EmitR(0, ZERO, ZERO, rd, 0, 0x10)).GprOuts(rd); } void MipsAssembler::Mflo(Register rd) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0, ZERO, ZERO, rd, 0, 0x12), rd, ZERO, ZERO); + DsFsmInstr(EmitR(0, ZERO, ZERO, rd, 0, 0x12)).GprOuts(rd); } void MipsAssembler::Sb(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0x28, rs, rt, imm16), ZERO, rt, rs); + DsFsmInstr(EmitI(0x28, rs, rt, imm16)).GprIns(rt, rs); } void MipsAssembler::Sh(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0x29, rs, rt, imm16), ZERO, rt, rs); + DsFsmInstr(EmitI(0x29, rs, rt, imm16)).GprIns(rt, rs); } void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16, MipsLabel* patcher_label) { if (patcher_label != nullptr) { Bind(patcher_label); } - DsFsmInstrRrr(EmitI(0x2b, rs, rt, imm16), ZERO, rt, rs, patcher_label); + DsFsmInstr(EmitI(0x2b, rs, rt, imm16), patcher_label).GprIns(rt, rs); } void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16) { @@ -900,50 +819,50 @@ void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16) { void MipsAssembler::Swl(Register rt, Register rs, uint16_t imm16) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitI(0x2a, rs, rt, imm16), ZERO, rt, rs); + DsFsmInstr(EmitI(0x2a, rs, rt, imm16)).GprIns(rt, rs); } void MipsAssembler::Swr(Register rt, Register rs, uint16_t imm16) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitI(0x2e, rs, rt, imm16), ZERO, rt, rs); + DsFsmInstr(EmitI(0x2e, rs, rt, imm16)).GprIns(rt, rs); } void MipsAssembler::LlR2(Register rt, Register base, int16_t imm16) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitI(0x30, base, rt, imm16), rt, base, base); + DsFsmInstr(EmitI(0x30, base, rt, imm16)).GprOuts(rt).GprIns(base); } void MipsAssembler::ScR2(Register rt, Register base, int16_t imm16) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitI(0x38, base, rt, imm16), rt, rt, base); + DsFsmInstr(EmitI(0x38, base, rt, imm16)).GprInOuts(rt).GprIns(base); } void MipsAssembler::LlR6(Register rt, Register base, int16_t imm9) { CHECK(IsR6()); CHECK(IsInt<9>(imm9)); - DsFsmInstrRrr(EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x36), rt, base, base); + DsFsmInstr(EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x36)).GprOuts(rt).GprIns(base); } void MipsAssembler::ScR6(Register rt, Register base, int16_t imm9) { CHECK(IsR6()); CHECK(IsInt<9>(imm9)); - DsFsmInstrRrr(EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x26), rt, rt, base); + DsFsmInstr(EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x26)).GprInOuts(rt).GprIns(base); } void MipsAssembler::Slt(Register rd, Register rs, Register rt) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x2a), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x2a)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Sltu(Register rd, Register rs, Register rt) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x2b), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x2b)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Slti(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0xa, rs, rt, imm16), rt, rs, rs); + DsFsmInstr(EmitI(0xa, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::Sltiu(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0xb, rs, rt, imm16), rt, rs, rs); + DsFsmInstr(EmitI(0xb, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::B(uint16_t imm16) { @@ -1021,8 +940,8 @@ void MipsAssembler::Jalr(Register rd, Register rs) { uint32_t last_instruction = delay_slot_.instruction_; MipsLabel* patcher_label = delay_slot_.patcher_label_; bool exchange = (last_instruction != 0 && - (delay_slot_.gpr_outs_mask_ & (1u << rs)) == 0 && - ((delay_slot_.gpr_ins_mask_ | delay_slot_.gpr_outs_mask_) & (1u << rd)) == 0); + (delay_slot_.masks_.gpr_outs_ & (1u << rs)) == 0 && + ((delay_slot_.masks_.gpr_ins_ | delay_slot_.masks_.gpr_outs_) & (1u << rd)) == 0); if (exchange) { // The last instruction cannot be used in a different delay slot, // do not commit the label before it (if any). @@ -1305,67 +1224,67 @@ void MipsAssembler::EmitBcondR6(BranchCondition cond, Register rs, Register rt, } void MipsAssembler::AddS(FRegister fd, FRegister fs, FRegister ft) { - DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x0), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, fd, 0x0)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::SubS(FRegister fd, FRegister fs, FRegister ft) { - DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, fd, 0x1)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::MulS(FRegister fd, FRegister fs, FRegister ft) { - DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x2), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, fd, 0x2)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::DivS(FRegister fd, FRegister fs, FRegister ft) { - DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x3), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, fd, 0x3)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::AddD(FRegister fd, FRegister fs, FRegister ft) { - DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x0), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, fd, 0x0)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::SubD(FRegister fd, FRegister fs, FRegister ft) { - DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, fd, 0x1)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::MulD(FRegister fd, FRegister fs, FRegister ft) { - DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x2), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, fd, 0x2)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::DivD(FRegister fd, FRegister fs, FRegister ft) { - DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x3), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, fd, 0x3)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::SqrtS(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x4), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x4)).FprOuts(fd).FprIns(fs); } void MipsAssembler::SqrtD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x4), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x4)).FprOuts(fd).FprIns(fs); } void MipsAssembler::AbsS(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x5), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x5)).FprOuts(fd).FprIns(fs); } void MipsAssembler::AbsD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x5), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x5)).FprOuts(fd).FprIns(fs); } void MipsAssembler::MovS(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x6), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x6)).FprOuts(fd).FprIns(fs); } void MipsAssembler::MovD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x6), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x6)).FprOuts(fd).FprIns(fs); } void MipsAssembler::NegS(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x7), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x7)).FprOuts(fd).FprIns(fs); } void MipsAssembler::NegD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x7), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x7)).FprOuts(fd).FprIns(fs); } void MipsAssembler::CunS(FRegister fs, FRegister ft) { @@ -1375,7 +1294,8 @@ void MipsAssembler::CunS(FRegister fs, FRegister ft) { void MipsAssembler::CunS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x31), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x31)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CeqS(FRegister fs, FRegister ft) { @@ -1385,7 +1305,8 @@ void MipsAssembler::CeqS(FRegister fs, FRegister ft) { void MipsAssembler::CeqS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x32), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x32)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CueqS(FRegister fs, FRegister ft) { @@ -1395,7 +1316,8 @@ void MipsAssembler::CueqS(FRegister fs, FRegister ft) { void MipsAssembler::CueqS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x33), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x33)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::ColtS(FRegister fs, FRegister ft) { @@ -1405,7 +1327,8 @@ void MipsAssembler::ColtS(FRegister fs, FRegister ft) { void MipsAssembler::ColtS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x34), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x34)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CultS(FRegister fs, FRegister ft) { @@ -1415,7 +1338,8 @@ void MipsAssembler::CultS(FRegister fs, FRegister ft) { void MipsAssembler::CultS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x35), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x35)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::ColeS(FRegister fs, FRegister ft) { @@ -1425,7 +1349,8 @@ void MipsAssembler::ColeS(FRegister fs, FRegister ft) { void MipsAssembler::ColeS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x36), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x36)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CuleS(FRegister fs, FRegister ft) { @@ -1435,7 +1360,8 @@ void MipsAssembler::CuleS(FRegister fs, FRegister ft) { void MipsAssembler::CuleS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x37), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x37)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CunD(FRegister fs, FRegister ft) { @@ -1445,7 +1371,8 @@ void MipsAssembler::CunD(FRegister fs, FRegister ft) { void MipsAssembler::CunD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x31), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x31)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CeqD(FRegister fs, FRegister ft) { @@ -1455,7 +1382,8 @@ void MipsAssembler::CeqD(FRegister fs, FRegister ft) { void MipsAssembler::CeqD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x32), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x32)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CueqD(FRegister fs, FRegister ft) { @@ -1465,7 +1393,8 @@ void MipsAssembler::CueqD(FRegister fs, FRegister ft) { void MipsAssembler::CueqD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x33), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x33)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::ColtD(FRegister fs, FRegister ft) { @@ -1475,7 +1404,8 @@ void MipsAssembler::ColtD(FRegister fs, FRegister ft) { void MipsAssembler::ColtD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x34), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x34)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CultD(FRegister fs, FRegister ft) { @@ -1485,7 +1415,8 @@ void MipsAssembler::CultD(FRegister fs, FRegister ft) { void MipsAssembler::CultD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x35), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x35)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::ColeD(FRegister fs, FRegister ft) { @@ -1495,7 +1426,8 @@ void MipsAssembler::ColeD(FRegister fs, FRegister ft) { void MipsAssembler::ColeD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x36), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x36)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CuleD(FRegister fs, FRegister ft) { @@ -1505,301 +1437,323 @@ void MipsAssembler::CuleD(FRegister fs, FRegister ft) { void MipsAssembler::CuleD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x37), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x37)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CmpUnS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x01), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x01)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpEqS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x02), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x02)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpUeqS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x03), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x03)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpLtS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x04), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x04)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpUltS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x05), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x05)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpLeS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x06), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x06)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpUleS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x07), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x07)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpOrS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x11), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x11)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpUneS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x12), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x12)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpNeS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x13), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x13)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpUnD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x01), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x01)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpEqD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x02), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x02)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpUeqD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x03), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x03)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpLtD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x04), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x04)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpUltD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x05), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x05)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpLeD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x06), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x06)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpUleD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x07), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x07)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpOrD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x11), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x11)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpUneD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x12), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x12)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpNeD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x13), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x13)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::Movf(Register rd, Register rs, int cc) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrRrrc(EmitR(0, rs, static_cast(cc << 2), rd, 0, 0x01), rd, rs, cc); + DsFsmInstr(EmitR(0, rs, static_cast(cc << 2), rd, 0, 0x01)) + .GprInOuts(rd).GprIns(rs).CcIns(cc); } void MipsAssembler::Movt(Register rd, Register rs, int cc) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrRrrc(EmitR(0, rs, static_cast((cc << 2) | 1), rd, 0, 0x01), rd, rs, cc); + DsFsmInstr(EmitR(0, rs, static_cast((cc << 2) | 1), rd, 0, 0x01)) + .GprInOuts(rd).GprIns(rs).CcIns(cc); } void MipsAssembler::MovfS(FRegister fd, FRegister fs, int cc) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrFffc(EmitFR(0x11, 0x10, static_cast(cc << 2), fs, fd, 0x11), fd, fs, cc); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(cc << 2), fs, fd, 0x11)) + .FprInOuts(fd).FprIns(fs).CcIns(cc); } void MipsAssembler::MovfD(FRegister fd, FRegister fs, int cc) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrFffc(EmitFR(0x11, 0x11, static_cast(cc << 2), fs, fd, 0x11), fd, fs, cc); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(cc << 2), fs, fd, 0x11)) + .FprInOuts(fd).FprIns(fs).CcIns(cc); } void MipsAssembler::MovtS(FRegister fd, FRegister fs, int cc) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrFffc(EmitFR(0x11, 0x10, static_cast((cc << 2) | 1), fs, fd, 0x11), - fd, - fs, - cc); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast((cc << 2) | 1), fs, fd, 0x11)) + .FprInOuts(fd).FprIns(fs).CcIns(cc); } void MipsAssembler::MovtD(FRegister fd, FRegister fs, int cc) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrFffc(EmitFR(0x11, 0x11, static_cast((cc << 2) | 1), fs, fd, 0x11), - fd, - fs, - cc); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast((cc << 2) | 1), fs, fd, 0x11)) + .FprInOuts(fd).FprIns(fs).CcIns(cc); } void MipsAssembler::MovzS(FRegister fd, FRegister fs, Register rt) { CHECK(!IsR6()); - DsFsmInstrFffr(EmitFR(0x11, 0x10, static_cast(rt), fs, fd, 0x12), fd, fs, rt); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(rt), fs, fd, 0x12)) + .FprInOuts(fd).FprIns(fs).GprIns(rt); } void MipsAssembler::MovzD(FRegister fd, FRegister fs, Register rt) { CHECK(!IsR6()); - DsFsmInstrFffr(EmitFR(0x11, 0x11, static_cast(rt), fs, fd, 0x12), fd, fs, rt); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(rt), fs, fd, 0x12)) + .FprInOuts(fd).FprIns(fs).GprIns(rt); } void MipsAssembler::MovnS(FRegister fd, FRegister fs, Register rt) { CHECK(!IsR6()); - DsFsmInstrFffr(EmitFR(0x11, 0x10, static_cast(rt), fs, fd, 0x13), fd, fs, rt); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(rt), fs, fd, 0x13)) + .FprInOuts(fd).FprIns(fs).GprIns(rt); } void MipsAssembler::MovnD(FRegister fd, FRegister fs, Register rt) { CHECK(!IsR6()); - DsFsmInstrFffr(EmitFR(0x11, 0x11, static_cast(rt), fs, fd, 0x13), fd, fs, rt); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(rt), fs, fd, 0x13)) + .FprInOuts(fd).FprIns(fs).GprIns(rt); } void MipsAssembler::SelS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFfff(EmitFR(0x11, 0x10, ft, fs, fd, 0x10), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, fd, 0x10)).FprInOuts(fd).FprIns(fs, ft); } void MipsAssembler::SelD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFfff(EmitFR(0x11, 0x11, ft, fs, fd, 0x10), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, fd, 0x10)).FprInOuts(fd).FprIns(fs, ft); } void MipsAssembler::SeleqzS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x14), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, fd, 0x14)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::SeleqzD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x14), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, fd, 0x14)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::SelnezS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x17), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, fd, 0x17)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::SelnezD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x17), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, fd, 0x17)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::ClassS(FRegister fd, FRegister fs) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x1b), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x1b)).FprOuts(fd).FprIns(fs); } void MipsAssembler::ClassD(FRegister fd, FRegister fs) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x1b), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x1b)).FprOuts(fd).FprIns(fs); } void MipsAssembler::MinS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1c), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, fd, 0x1c)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::MinD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1c), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, fd, 0x1c)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::MaxS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1e), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, fd, 0x1e)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::MaxD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1e), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, fd, 0x1e)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::TruncLS(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x09), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x09)).FprOuts(fd).FprIns(fs); } void MipsAssembler::TruncLD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x09), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x09)).FprOuts(fd).FprIns(fs); } void MipsAssembler::TruncWS(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x0D), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x0D)).FprOuts(fd).FprIns(fs); } void MipsAssembler::TruncWD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x0D), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x0D)).FprOuts(fd).FprIns(fs); } void MipsAssembler::Cvtsw(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x14, static_cast(0), fs, fd, 0x20), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x14, static_cast(0), fs, fd, 0x20)).FprOuts(fd).FprIns(fs); } void MipsAssembler::Cvtdw(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x14, static_cast(0), fs, fd, 0x21), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x14, static_cast(0), fs, fd, 0x21)).FprOuts(fd).FprIns(fs); } void MipsAssembler::Cvtsd(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x20), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x20)).FprOuts(fd).FprIns(fs); } void MipsAssembler::Cvtds(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x21), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x21)).FprOuts(fd).FprIns(fs); } void MipsAssembler::Cvtsl(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x15, static_cast(0), fs, fd, 0x20), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x15, static_cast(0), fs, fd, 0x20)).FprOuts(fd).FprIns(fs); } void MipsAssembler::Cvtdl(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x15, static_cast(0), fs, fd, 0x21), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x15, static_cast(0), fs, fd, 0x21)).FprOuts(fd).FprIns(fs); } void MipsAssembler::FloorWS(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0xf), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0xf)).FprOuts(fd).FprIns(fs); } void MipsAssembler::FloorWD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0xf), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0xf)).FprOuts(fd).FprIns(fs); +} + +FRegister MipsAssembler::GetFpuRegLow(FRegister reg) { + // If FPRs are 32-bit (and get paired to hold 64-bit values), accesses to + // odd-numbered FPRs are reattributed to even-numbered FPRs. This lets us + // use only even-numbered FPRs irrespective of whether we're doing single- + // or double-precision arithmetic. (We don't use odd-numbered 32-bit FPRs + // to hold single-precision values). + return Is32BitFPU() ? static_cast(reg & ~1u) : reg; } void MipsAssembler::Mfc1(Register rt, FRegister fs) { - DsFsmInstrRf(EmitFR(0x11, 0x00, static_cast(rt), fs, static_cast(0), 0x0), - rt, - fs); + DsFsmInstr(EmitFR(0x11, 0x00, static_cast(rt), fs, static_cast(0), 0x0)) + .GprOuts(rt).FprIns(GetFpuRegLow(fs)); } +// Note, the 32 LSBs of a 64-bit value must be loaded into an FPR before the 32 MSBs +// when loading the value as 32-bit halves. void MipsAssembler::Mtc1(Register rt, FRegister fs) { - DsFsmInstrFr(EmitFR(0x11, 0x04, static_cast(rt), fs, static_cast(0), 0x0), - fs, - rt); + uint32_t encoding = + EmitFR(0x11, 0x04, static_cast(rt), fs, static_cast(0), 0x0); + if (Is32BitFPU() && (fs % 2 != 0)) { + // If mtc1 is used to simulate mthc1 by writing to the odd-numbered FPR in + // a pair of 32-bit FPRs, the associated even-numbered FPR is an in/out. + DsFsmInstr(encoding).FprInOuts(GetFpuRegLow(fs)).GprIns(rt); + } else { + // Otherwise (the FPR is 64-bit or even-numbered), the FPR is an out. + DsFsmInstr(encoding).FprOuts(fs).GprIns(rt); + } } void MipsAssembler::Mfhc1(Register rt, FRegister fs) { - DsFsmInstrRf(EmitFR(0x11, 0x03, static_cast(rt), fs, static_cast(0), 0x0), - rt, - fs); + DsFsmInstr(EmitFR(0x11, 0x03, static_cast(rt), fs, static_cast(0), 0x0)) + .GprOuts(rt).FprIns(fs); } +// Note, the 32 LSBs of a 64-bit value must be loaded into an FPR before the 32 MSBs +// when loading the value as 32-bit halves. void MipsAssembler::Mthc1(Register rt, FRegister fs) { - DsFsmInstrFr(EmitFR(0x11, 0x07, static_cast(rt), fs, static_cast(0), 0x0), - fs, - rt); + DsFsmInstr(EmitFR(0x11, 0x07, static_cast(rt), fs, static_cast(0), 0x0)) + .FprInOuts(fs).GprIns(rt); } void MipsAssembler::MoveFromFpuHigh(Register rt, FRegister fs) { @@ -1820,20 +1774,30 @@ void MipsAssembler::MoveToFpuHigh(Register rt, FRegister fs) { } } +// Note, the 32 LSBs of a 64-bit value must be loaded into an FPR before the 32 MSBs +// when loading the value as 32-bit halves. void MipsAssembler::Lwc1(FRegister ft, Register rs, uint16_t imm16) { - DsFsmInstrFr(EmitI(0x31, rs, static_cast(ft), imm16), ft, rs); + uint32_t encoding = EmitI(0x31, rs, static_cast(ft), imm16); + if (Is32BitFPU() && (ft % 2 != 0)) { + // If lwc1 is used to load the odd-numbered FPR in a pair of 32-bit FPRs, + // the associated even-numbered FPR is an in/out. + DsFsmInstr(encoding).FprInOuts(GetFpuRegLow(ft)).GprIns(rs); + } else { + // Otherwise (the FPR is 64-bit or even-numbered), the FPR is an out. + DsFsmInstr(encoding).FprOuts(ft).GprIns(rs); + } } void MipsAssembler::Ldc1(FRegister ft, Register rs, uint16_t imm16) { - DsFsmInstrFr(EmitI(0x35, rs, static_cast(ft), imm16), ft, rs); + DsFsmInstr(EmitI(0x35, rs, static_cast(ft), imm16)).FprOuts(ft).GprIns(rs); } void MipsAssembler::Swc1(FRegister ft, Register rs, uint16_t imm16) { - DsFsmInstrFR(EmitI(0x39, rs, static_cast(ft), imm16), ft, rs); + DsFsmInstr(EmitI(0x39, rs, static_cast(ft), imm16)).FprIns(GetFpuRegLow(ft)).GprIns(rs); } void MipsAssembler::Sdc1(FRegister ft, Register rs, uint16_t imm16) { - DsFsmInstrFR(EmitI(0x3d, rs, static_cast(ft), imm16), ft, rs); + DsFsmInstr(EmitI(0x3d, rs, static_cast(ft), imm16)).FprIns(ft).GprIns(rs); } void MipsAssembler::Break() { @@ -1882,1447 +1846,951 @@ void MipsAssembler::PopAndReturn(Register rd, Register rt) { void MipsAssembler::AndV(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0x1e), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::OrV(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0x1e), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::NorV(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0x1e), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::XorV(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0x1e), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::AddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::AddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::AddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::AddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::MulvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::MulvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::MulvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::MulvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Div_sB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Div_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Div_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Div_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Div_uB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Div_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Div_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Div_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Mod_sB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x0, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x0, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Mod_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x1, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x1, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Mod_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x2, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x2, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Mod_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x3, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x3, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Mod_uB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x0, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x0, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Mod_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x1, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x1, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Mod_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x2, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x2, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Mod_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x3, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x3, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Add_aB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Add_aH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Add_aW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Add_aD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Ave_sB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Ave_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Ave_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Ave_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Ave_uB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Ave_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Ave_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Ave_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Aver_sB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x0, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x0, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Aver_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x1, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x1, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Aver_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x2, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x2, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Aver_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x3, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x3, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Aver_uB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x0, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x0, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Aver_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x1, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x1, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Aver_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x2, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x2, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Aver_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x3, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x3, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Max_sB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Max_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Max_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Max_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Max_uB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x3, 0x0, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x3, 0x0, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Max_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x3, 0x1, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x3, 0x1, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Max_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x3, 0x2, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x3, 0x2, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Max_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x3, 0x3, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x3, 0x3, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Min_sB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Min_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Min_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Min_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Min_uB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Min_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Min_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Min_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FmulW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FmulD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FdivW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FdivD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FmaxW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x0, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x0, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FmaxD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x1, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x1, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FminW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x0, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x0, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FminD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x1, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x1, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Ffint_sW(VectorRegister wd, VectorRegister ws) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa2RF(0x19e, 0x0, ws, wd, 0x1e), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsa2RF(0x19e, 0x0, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws); } void MipsAssembler::Ffint_sD(VectorRegister wd, VectorRegister ws) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa2RF(0x19e, 0x1, ws, wd, 0x1e), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsa2RF(0x19e, 0x1, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws); } void MipsAssembler::Ftint_sW(VectorRegister wd, VectorRegister ws) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa2RF(0x19c, 0x0, ws, wd, 0x1e), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsa2RF(0x19c, 0x0, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws); } void MipsAssembler::Ftint_sD(VectorRegister wd, VectorRegister ws) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa2RF(0x19c, 0x1, ws, wd, 0x1e), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsa2RF(0x19c, 0x1, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SllB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SllH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SllW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SllD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SraB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SraH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SraW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SraD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SrlB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SrlH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SrlW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SrlD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SlliB(VectorRegister wd, VectorRegister ws, int shamt3) { CHECK(HasMsa()); CHECK(IsUint<3>(shamt3)) << shamt3; - DsFsmInstrFff(EmitMsaBIT(0x0, shamt3 | kMsaDfMByteMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x0, shamt3 | kMsaDfMByteMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SlliH(VectorRegister wd, VectorRegister ws, int shamt4) { CHECK(HasMsa()); CHECK(IsUint<4>(shamt4)) << shamt4; - DsFsmInstrFff(EmitMsaBIT(0x0, shamt4 | kMsaDfMHalfwordMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x0, shamt4 | kMsaDfMHalfwordMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SlliW(VectorRegister wd, VectorRegister ws, int shamt5) { CHECK(HasMsa()); CHECK(IsUint<5>(shamt5)) << shamt5; - DsFsmInstrFff(EmitMsaBIT(0x0, shamt5 | kMsaDfMWordMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x0, shamt5 | kMsaDfMWordMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SlliD(VectorRegister wd, VectorRegister ws, int shamt6) { CHECK(HasMsa()); CHECK(IsUint<6>(shamt6)) << shamt6; - DsFsmInstrFff(EmitMsaBIT(0x0, shamt6 | kMsaDfMDoublewordMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x0, shamt6 | kMsaDfMDoublewordMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SraiB(VectorRegister wd, VectorRegister ws, int shamt3) { CHECK(HasMsa()); CHECK(IsUint<3>(shamt3)) << shamt3; - DsFsmInstrFff(EmitMsaBIT(0x1, shamt3 | kMsaDfMByteMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x1, shamt3 | kMsaDfMByteMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SraiH(VectorRegister wd, VectorRegister ws, int shamt4) { CHECK(HasMsa()); CHECK(IsUint<4>(shamt4)) << shamt4; - DsFsmInstrFff(EmitMsaBIT(0x1, shamt4 | kMsaDfMHalfwordMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x1, shamt4 | kMsaDfMHalfwordMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SraiW(VectorRegister wd, VectorRegister ws, int shamt5) { CHECK(HasMsa()); CHECK(IsUint<5>(shamt5)) << shamt5; - DsFsmInstrFff(EmitMsaBIT(0x1, shamt5 | kMsaDfMWordMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x1, shamt5 | kMsaDfMWordMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SraiD(VectorRegister wd, VectorRegister ws, int shamt6) { CHECK(HasMsa()); CHECK(IsUint<6>(shamt6)) << shamt6; - DsFsmInstrFff(EmitMsaBIT(0x1, shamt6 | kMsaDfMDoublewordMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x1, shamt6 | kMsaDfMDoublewordMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SrliB(VectorRegister wd, VectorRegister ws, int shamt3) { CHECK(HasMsa()); CHECK(IsUint<3>(shamt3)) << shamt3; - DsFsmInstrFff(EmitMsaBIT(0x2, shamt3 | kMsaDfMByteMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x2, shamt3 | kMsaDfMByteMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SrliH(VectorRegister wd, VectorRegister ws, int shamt4) { CHECK(HasMsa()); CHECK(IsUint<4>(shamt4)) << shamt4; - DsFsmInstrFff(EmitMsaBIT(0x2, shamt4 | kMsaDfMHalfwordMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x2, shamt4 | kMsaDfMHalfwordMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SrliW(VectorRegister wd, VectorRegister ws, int shamt5) { CHECK(HasMsa()); CHECK(IsUint<5>(shamt5)) << shamt5; - DsFsmInstrFff(EmitMsaBIT(0x2, shamt5 | kMsaDfMWordMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x2, shamt5 | kMsaDfMWordMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SrliD(VectorRegister wd, VectorRegister ws, int shamt6) { CHECK(HasMsa()); CHECK(IsUint<6>(shamt6)) << shamt6; - DsFsmInstrFff(EmitMsaBIT(0x2, shamt6 | kMsaDfMDoublewordMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x2, shamt6 | kMsaDfMDoublewordMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::MoveV(VectorRegister wd, VectorRegister ws) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsaBIT(0x1, 0x3e, ws, wd, 0x19), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x1, 0x3e, ws, wd, 0x19)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SplatiB(VectorRegister wd, VectorRegister ws, int n4) { CHECK(HasMsa()); CHECK(IsUint<4>(n4)) << n4; - DsFsmInstrFff(EmitMsaELM(0x1, n4 | kMsaDfNByteMask, ws, wd, 0x19), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaELM(0x1, n4 | kMsaDfNByteMask, ws, wd, 0x19)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SplatiH(VectorRegister wd, VectorRegister ws, int n3) { CHECK(HasMsa()); CHECK(IsUint<3>(n3)) << n3; - DsFsmInstrFff(EmitMsaELM(0x1, n3 | kMsaDfNHalfwordMask, ws, wd, 0x19), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaELM(0x1, n3 | kMsaDfNHalfwordMask, ws, wd, 0x19)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SplatiW(VectorRegister wd, VectorRegister ws, int n2) { CHECK(HasMsa()); CHECK(IsUint<2>(n2)) << n2; - DsFsmInstrFff(EmitMsaELM(0x1, n2 | kMsaDfNWordMask, ws, wd, 0x19), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaELM(0x1, n2 | kMsaDfNWordMask, ws, wd, 0x19)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SplatiD(VectorRegister wd, VectorRegister ws, int n1) { CHECK(HasMsa()); CHECK(IsUint<1>(n1)) << n1; - DsFsmInstrFff(EmitMsaELM(0x1, n1 | kMsaDfNDoublewordMask, ws, wd, 0x19), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaELM(0x1, n1 | kMsaDfNDoublewordMask, ws, wd, 0x19)).FprOuts(wd).FprIns(ws); } void MipsAssembler::Copy_sB(Register rd, VectorRegister ws, int n4) { CHECK(HasMsa()); CHECK(IsUint<4>(n4)) << n4; - DsFsmInstrRf(EmitMsaELM(0x2, n4 | kMsaDfNByteMask, ws, static_cast(rd), 0x19), - rd, - static_cast(ws)); + DsFsmInstr(EmitMsaELM(0x2, n4 | kMsaDfNByteMask, ws, static_cast(rd), 0x19)) + .GprOuts(rd).FprIns(ws); } void MipsAssembler::Copy_sH(Register rd, VectorRegister ws, int n3) { CHECK(HasMsa()); CHECK(IsUint<3>(n3)) << n3; - DsFsmInstrRf(EmitMsaELM(0x2, n3 | kMsaDfNHalfwordMask, ws, static_cast(rd), 0x19), - rd, - static_cast(ws)); + DsFsmInstr(EmitMsaELM(0x2, n3 | kMsaDfNHalfwordMask, ws, static_cast(rd), 0x19)) + .GprOuts(rd).FprIns(ws); } void MipsAssembler::Copy_sW(Register rd, VectorRegister ws, int n2) { CHECK(HasMsa()); CHECK(IsUint<2>(n2)) << n2; - DsFsmInstrRf(EmitMsaELM(0x2, n2 | kMsaDfNWordMask, ws, static_cast(rd), 0x19), - rd, - static_cast(ws)); + DsFsmInstr(EmitMsaELM(0x2, n2 | kMsaDfNWordMask, ws, static_cast(rd), 0x19)) + .GprOuts(rd).FprIns(ws); } void MipsAssembler::Copy_uB(Register rd, VectorRegister ws, int n4) { CHECK(HasMsa()); CHECK(IsUint<4>(n4)) << n4; - DsFsmInstrRf(EmitMsaELM(0x3, n4 | kMsaDfNByteMask, ws, static_cast(rd), 0x19), - rd, - static_cast(ws)); + DsFsmInstr(EmitMsaELM(0x3, n4 | kMsaDfNByteMask, ws, static_cast(rd), 0x19)) + .GprOuts(rd).FprIns(ws); } void MipsAssembler::Copy_uH(Register rd, VectorRegister ws, int n3) { CHECK(HasMsa()); CHECK(IsUint<3>(n3)) << n3; - DsFsmInstrRf(EmitMsaELM(0x3, n3 | kMsaDfNHalfwordMask, ws, static_cast(rd), 0x19), - rd, - static_cast(ws)); + DsFsmInstr(EmitMsaELM(0x3, n3 | kMsaDfNHalfwordMask, ws, static_cast(rd), 0x19)) + .GprOuts(rd).FprIns(ws); } void MipsAssembler::InsertB(VectorRegister wd, Register rs, int n4) { CHECK(HasMsa()); CHECK(IsUint<4>(n4)) << n4; - DsFsmInstrFffr(EmitMsaELM(0x4, n4 | kMsaDfNByteMask, static_cast(rs), wd, 0x19), - static_cast(wd), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaELM(0x4, n4 | kMsaDfNByteMask, static_cast(rs), wd, 0x19)) + .FprInOuts(wd).GprIns(rs); } void MipsAssembler::InsertH(VectorRegister wd, Register rs, int n3) { CHECK(HasMsa()); CHECK(IsUint<3>(n3)) << n3; - DsFsmInstrFffr( - EmitMsaELM(0x4, n3 | kMsaDfNHalfwordMask, static_cast(rs), wd, 0x19), - static_cast(wd), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaELM(0x4, n3 | kMsaDfNHalfwordMask, static_cast(rs), wd, 0x19)) + .FprInOuts(wd).GprIns(rs); } void MipsAssembler::InsertW(VectorRegister wd, Register rs, int n2) { CHECK(HasMsa()); CHECK(IsUint<2>(n2)) << n2; - DsFsmInstrFffr(EmitMsaELM(0x4, n2 | kMsaDfNWordMask, static_cast(rs), wd, 0x19), - static_cast(wd), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaELM(0x4, n2 | kMsaDfNWordMask, static_cast(rs), wd, 0x19)) + .FprInOuts(wd).GprIns(rs); } void MipsAssembler::FillB(VectorRegister wd, Register rs) { CHECK(HasMsa()); - DsFsmInstrFr(EmitMsa2R(0xc0, 0x0, static_cast(rs), wd, 0x1e), - static_cast(wd), - rs); + DsFsmInstr(EmitMsa2R(0xc0, 0x0, static_cast(rs), wd, 0x1e)) + .FprOuts(wd).GprIns(rs); } void MipsAssembler::FillH(VectorRegister wd, Register rs) { CHECK(HasMsa()); - DsFsmInstrFr(EmitMsa2R(0xc0, 0x1, static_cast(rs), wd, 0x1e), - static_cast(wd), - rs); + DsFsmInstr(EmitMsa2R(0xc0, 0x1, static_cast(rs), wd, 0x1e)) + .FprOuts(wd).GprIns(rs); } void MipsAssembler::FillW(VectorRegister wd, Register rs) { CHECK(HasMsa()); - DsFsmInstrFr(EmitMsa2R(0xc0, 0x2, static_cast(rs), wd, 0x1e), - static_cast(wd), - rs); + DsFsmInstr(EmitMsa2R(0xc0, 0x2, static_cast(rs), wd, 0x1e)) + .FprOuts(wd).GprIns(rs); } void MipsAssembler::LdiB(VectorRegister wd, int imm8) { CHECK(HasMsa()); CHECK(IsInt<8>(imm8)) << imm8; - DsFsmInstrFr(EmitMsaI10(0x6, 0x0, imm8 & kMsaS10Mask, wd, 0x7), - static_cast(wd), - ZERO); + DsFsmInstr(EmitMsaI10(0x6, 0x0, imm8 & kMsaS10Mask, wd, 0x7)).FprOuts(wd); } void MipsAssembler::LdiH(VectorRegister wd, int imm10) { CHECK(HasMsa()); CHECK(IsInt<10>(imm10)) << imm10; - DsFsmInstrFr(EmitMsaI10(0x6, 0x1, imm10 & kMsaS10Mask, wd, 0x7), - static_cast(wd), - ZERO); + DsFsmInstr(EmitMsaI10(0x6, 0x1, imm10 & kMsaS10Mask, wd, 0x7)).FprOuts(wd); } void MipsAssembler::LdiW(VectorRegister wd, int imm10) { CHECK(HasMsa()); CHECK(IsInt<10>(imm10)) << imm10; - DsFsmInstrFr(EmitMsaI10(0x6, 0x2, imm10 & kMsaS10Mask, wd, 0x7), - static_cast(wd), - ZERO); + DsFsmInstr(EmitMsaI10(0x6, 0x2, imm10 & kMsaS10Mask, wd, 0x7)).FprOuts(wd); } void MipsAssembler::LdiD(VectorRegister wd, int imm10) { CHECK(HasMsa()); CHECK(IsInt<10>(imm10)) << imm10; - DsFsmInstrFr(EmitMsaI10(0x6, 0x3, imm10 & kMsaS10Mask, wd, 0x7), - static_cast(wd), - ZERO); + DsFsmInstr(EmitMsaI10(0x6, 0x3, imm10 & kMsaS10Mask, wd, 0x7)).FprOuts(wd); } void MipsAssembler::LdB(VectorRegister wd, Register rs, int offset) { CHECK(HasMsa()); CHECK(IsInt<10>(offset)) << offset; - DsFsmInstrFr(EmitMsaMI10(offset & kMsaS10Mask, rs, wd, 0x8, 0x0), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaMI10(offset & kMsaS10Mask, rs, wd, 0x8, 0x0)).FprOuts(wd).GprIns(rs); } void MipsAssembler::LdH(VectorRegister wd, Register rs, int offset) { CHECK(HasMsa()); CHECK(IsInt<11>(offset)) << offset; CHECK_ALIGNED(offset, kMipsHalfwordSize); - DsFsmInstrFr(EmitMsaMI10((offset >> TIMES_2) & kMsaS10Mask, rs, wd, 0x8, 0x1), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaMI10((offset >> TIMES_2) & kMsaS10Mask, rs, wd, 0x8, 0x1)) + .FprOuts(wd).GprIns(rs); } void MipsAssembler::LdW(VectorRegister wd, Register rs, int offset) { CHECK(HasMsa()); CHECK(IsInt<12>(offset)) << offset; CHECK_ALIGNED(offset, kMipsWordSize); - DsFsmInstrFr(EmitMsaMI10((offset >> TIMES_4) & kMsaS10Mask, rs, wd, 0x8, 0x2), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaMI10((offset >> TIMES_4) & kMsaS10Mask, rs, wd, 0x8, 0x2)) + .FprOuts(wd).GprIns(rs); } void MipsAssembler::LdD(VectorRegister wd, Register rs, int offset) { CHECK(HasMsa()); CHECK(IsInt<13>(offset)) << offset; CHECK_ALIGNED(offset, kMipsDoublewordSize); - DsFsmInstrFr(EmitMsaMI10((offset >> TIMES_8) & kMsaS10Mask, rs, wd, 0x8, 0x3), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaMI10((offset >> TIMES_8) & kMsaS10Mask, rs, wd, 0x8, 0x3)) + .FprOuts(wd).GprIns(rs); } void MipsAssembler::StB(VectorRegister wd, Register rs, int offset) { CHECK(HasMsa()); CHECK(IsInt<10>(offset)) << offset; - DsFsmInstrFR(EmitMsaMI10(offset & kMsaS10Mask, rs, wd, 0x9, 0x0), static_cast(wd), rs); + DsFsmInstr(EmitMsaMI10(offset & kMsaS10Mask, rs, wd, 0x9, 0x0)).FprIns(wd).GprIns(rs); } void MipsAssembler::StH(VectorRegister wd, Register rs, int offset) { CHECK(HasMsa()); CHECK(IsInt<11>(offset)) << offset; CHECK_ALIGNED(offset, kMipsHalfwordSize); - DsFsmInstrFR(EmitMsaMI10((offset >> TIMES_2) & kMsaS10Mask, rs, wd, 0x9, 0x1), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaMI10((offset >> TIMES_2) & kMsaS10Mask, rs, wd, 0x9, 0x1)) + .FprIns(wd).GprIns(rs); } void MipsAssembler::StW(VectorRegister wd, Register rs, int offset) { CHECK(HasMsa()); CHECK(IsInt<12>(offset)) << offset; CHECK_ALIGNED(offset, kMipsWordSize); - DsFsmInstrFR(EmitMsaMI10((offset >> TIMES_4) & kMsaS10Mask, rs, wd, 0x9, 0x2), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaMI10((offset >> TIMES_4) & kMsaS10Mask, rs, wd, 0x9, 0x2)) + .FprIns(wd).GprIns(rs); } void MipsAssembler::StD(VectorRegister wd, Register rs, int offset) { CHECK(HasMsa()); CHECK(IsInt<13>(offset)) << offset; CHECK_ALIGNED(offset, kMipsDoublewordSize); - DsFsmInstrFR(EmitMsaMI10((offset >> TIMES_8) & kMsaS10Mask, rs, wd, 0x9, 0x3), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaMI10((offset >> TIMES_8) & kMsaS10Mask, rs, wd, 0x9, 0x3)) + .FprIns(wd).GprIns(rs); } void MipsAssembler::IlvlB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvlH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvlW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvlD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvrB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvrH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvrW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvrD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvevB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x0, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x0, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvevH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x1, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x1, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvevW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x2, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x2, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvevD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x3, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x3, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvodB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x0, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x0, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvodH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x1, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x1, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvodW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x2, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x2, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvodD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x3, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x3, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::MaddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0x12)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::MaddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0x12)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::MaddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0x12)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::MaddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0x12)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::MsubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x12)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::MsubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x12)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::MsubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x12)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::MsubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x12)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::Asub_sB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0x11), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0x11)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Asub_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x11), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x11)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Asub_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x11), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x11)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Asub_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x11), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x11)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Asub_uB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0x11), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0x11)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Asub_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x11), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x11)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Asub_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x11), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x11)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Asub_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x11), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x11)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FmaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x1b)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::FmaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x1b)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::FmsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x1b)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::FmsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x1b)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::Hadd_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x15), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x15)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Hadd_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x15), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x15)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Hadd_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x15), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x15)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Hadd_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x15), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x15)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Hadd_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x15), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x15)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Hadd_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x15), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x15)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::ReplicateFPToVectorRegister(VectorRegister dst, @@ -4144,7 +3612,7 @@ bool MipsAssembler::Branch::CanHaveDelayedInstruction(const DelaySlot& delay_slo case kLongCall: // Instructions depending on or modifying RA should not be moved into delay slots // of branches modifying RA. - return ((delay_slot.gpr_ins_mask_ | delay_slot.gpr_outs_mask_) & (1u << RA)) == 0; + return ((delay_slot.masks_.gpr_ins_ | delay_slot.masks_.gpr_outs_) & (1u << RA)) == 0; // R2 conditional branches. case kCondBranch: @@ -4157,17 +3625,17 @@ bool MipsAssembler::Branch::CanHaveDelayedInstruction(const DelaySlot& delay_slo case kCondGTZ: case kCondEQZ: case kCondNEZ: - return (delay_slot.gpr_outs_mask_ & (1u << lhs_reg_)) == 0; + return (delay_slot.masks_.gpr_outs_ & (1u << lhs_reg_)) == 0; // Branches with two GPR sources. case kCondEQ: case kCondNE: - return (delay_slot.gpr_outs_mask_ & ((1u << lhs_reg_) | (1u << rhs_reg_))) == 0; + return (delay_slot.masks_.gpr_outs_ & ((1u << lhs_reg_) | (1u << rhs_reg_))) == 0; // Branches with one FPU condition code source. case kCondF: case kCondT: - return (delay_slot.cc_outs_mask_ & (1u << lhs_reg_)) == 0; + return (delay_slot.masks_.cc_outs_ & (1u << lhs_reg_)) == 0; default: // We don't support synthetic R2 branches (preceded with slt[u]) at this level @@ -4192,7 +3660,7 @@ bool MipsAssembler::Branch::CanHaveDelayedInstruction(const DelaySlot& delay_slo // Branches with one FPU register source. case kCondF: case kCondT: - return (delay_slot.fpr_outs_mask_ & (1u << lhs_reg_)) == 0; + return (delay_slot.masks_.fpr_outs_ & (1u << lhs_reg_)) == 0; // Others have a forbidden slot instead of a delay slot. default: return false; @@ -4858,8 +4326,8 @@ bool MipsAssembler::CanExchangeWithSlt(Register rs, Register rt) const { // Likewise, if the instruction depends on AT, it can't be exchanged with slt[u] // because slt[u] changes AT. return (delay_slot_.instruction_ != 0 && - (delay_slot_.gpr_outs_mask_ & ((1u << AT) | (1u << rs) | (1u << rt))) == 0 && - (delay_slot_.gpr_ins_mask_ & (1u << AT)) == 0); + (delay_slot_.masks_.gpr_outs_ & ((1u << AT) | (1u << rs) | (1u << rt))) == 0 && + (delay_slot_.masks_.gpr_ins_ & (1u << AT)) == 0); } void MipsAssembler::ExchangeWithSlt(const DelaySlot& forwarded_slot) { diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h index 1c3097ac58..7de8e2e366 100644 --- a/compiler/utils/mips/assembler_mips.h +++ b/compiler/utils/mips/assembler_mips.h @@ -74,6 +74,81 @@ enum FPClassMaskType { kPositiveZero = 0x200, }; +// Instruction description in terms of input and output registers. +// Used for instruction reordering. +struct InOutRegMasks { + InOutRegMasks() + : gpr_outs_(0), gpr_ins_(0), fpr_outs_(0), fpr_ins_(0), cc_outs_(0), cc_ins_(0) {} + + inline InOutRegMasks& GprOuts(Register reg) { + gpr_outs_ |= (1u << reg); + gpr_outs_ &= ~1u; // Ignore register ZERO. + return *this; + } + template + inline InOutRegMasks& GprOuts(T one, Ts... more) { GprOuts(one); GprOuts(more...); return *this; } + + inline InOutRegMasks& GprIns(Register reg) { + gpr_ins_ |= (1u << reg); + gpr_ins_ &= ~1u; // Ignore register ZERO. + return *this; + } + template + inline InOutRegMasks& GprIns(T one, Ts... more) { GprIns(one); GprIns(more...); return *this; } + + inline InOutRegMasks& GprInOuts(Register reg) { GprIns(reg); GprOuts(reg); return *this; } + template + inline InOutRegMasks& GprInOuts(T one, Ts... more) { + GprInOuts(one); + GprInOuts(more...); + return *this; + } + + inline InOutRegMasks& FprOuts(FRegister reg) { fpr_outs_ |= (1u << reg); return *this; } + inline InOutRegMasks& FprOuts(VectorRegister reg) { return FprOuts(static_cast(reg)); } + template + inline InOutRegMasks& FprOuts(T one, Ts... more) { FprOuts(one); FprOuts(more...); return *this; } + + inline InOutRegMasks& FprIns(FRegister reg) { fpr_ins_ |= (1u << reg); return *this; } + inline InOutRegMasks& FprIns(VectorRegister reg) { return FprIns(static_cast(reg)); } + template + inline InOutRegMasks& FprIns(T one, Ts... more) { FprIns(one); FprIns(more...); return *this; } + + inline InOutRegMasks& FprInOuts(FRegister reg) { FprIns(reg); FprOuts(reg); return *this; } + inline InOutRegMasks& FprInOuts(VectorRegister reg) { + return FprInOuts(static_cast(reg)); + } + template + inline InOutRegMasks& FprInOuts(T one, Ts... more) { + FprInOuts(one); + FprInOuts(more...); + return *this; + } + + inline InOutRegMasks& CcOuts(int cc) { cc_outs_ |= (1u << cc); return *this; } + template + inline InOutRegMasks& CcOuts(T one, Ts... more) { CcOuts(one); CcOuts(more...); return *this; } + + inline InOutRegMasks& CcIns(int cc) { cc_ins_ |= (1u << cc); return *this; } + template + inline InOutRegMasks& CcIns(T one, Ts... more) { CcIns(one); CcIns(more...); return *this; } + + // Mask of output GPRs for the instruction. + uint32_t gpr_outs_; + // Mask of input GPRs for the instruction. + uint32_t gpr_ins_; + // Mask of output FPRs for the instruction. + uint32_t fpr_outs_; + // Mask of input FPRs for the instruction. + uint32_t fpr_ins_; + // Mask of output FPU condition code flags for the instruction. + uint32_t cc_outs_; + // Mask of input FPU condition code flags for the instruction. + uint32_t cc_ins_; + + // TODO: add LO and HI. +}; + class MipsLabel : public Label { public: MipsLabel() : prev_branch_id_plus_one_(0) {} @@ -462,6 +537,16 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler Date: Tue, 5 Dec 2017 10:10:07 +0000 Subject: [PATCH 125/226] Update ahat version number to 1.5 Test: m ahat-test Test: open heap dump and verify version number on overview page. Change-Id: Ied11ec1973a14c0f9351adc867cc522a80a7f87d --- tools/ahat/README.txt | 11 +++++++++-- tools/ahat/etc/ahat.mf | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/tools/ahat/README.txt b/tools/ahat/README.txt index a765b17951..cdfeba45cb 100644 --- a/tools/ahat/README.txt +++ b/tools/ahat/README.txt @@ -30,7 +30,6 @@ TODO: * Show somewhere where to send bugs. * Include a link to /objects in the overview and menu? * Turn on LOCAL_JAVACFLAGS := -Xlint:unchecked -Werror - * Use hex for object ids in URLs? * [low priority] by site allocations won't line up if the stack has been truncated. Is there any way to manually line them up in that case? @@ -54,7 +53,15 @@ Reported Issues: * Request to be able to sort tables by size. Release History: - 1.5 Pending + 1.6 Pending + + 1.5 December 05, 2017 + Distinguish between weakly reachable and unreachable instances. + Allow hex ids to be used for objects in query parameters. + Restore old presentation of sample paths from gc roots. + Fix bug in selection of sample paths from gc root. + Fix bug in proguard deobfuscation of stack frames. + Tighten up and document ahat public API. 1.4 October 03, 2017 Give better error messages on failure to launch ahat. diff --git a/tools/ahat/etc/ahat.mf b/tools/ahat/etc/ahat.mf index 1753406e6e..df964838bd 100644 --- a/tools/ahat/etc/ahat.mf +++ b/tools/ahat/etc/ahat.mf @@ -1,4 +1,4 @@ Name: ahat/ Implementation-Title: ahat -Implementation-Version: 1.4 +Implementation-Version: 1.5 Main-Class: com.android.ahat.Main -- GitLab From 83e2668a9dc19d0d7fa957b41265b7b564142e4d Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 5 Dec 2017 17:58:57 +0000 Subject: [PATCH 126/226] ART: Fix dependencies of dump-oat-core-target . This was probably broken by https://android-review.googlesource.com/209369 which removed the "default" compiler type but didn't uptate the variable names to those introduced in https://android-review.googlesource.com/337923 . Test: m dump-oat-core-target Change-Id: I0fad9a78fea13bf9c7da24d69bb217a2bc57257e --- oatdump/Android.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oatdump/Android.mk b/oatdump/Android.mk index 906404b18c..667c37c33d 100644 --- a/oatdump/Android.mk +++ b/oatdump/Android.mk @@ -41,7 +41,7 @@ endif .PHONY: dump-oat-core-target-$(TARGET_ARCH) ifeq ($(ART_BUILD_TARGET),true) -dump-oat-core-target-$(TARGET_ARCH): $(TARGET_CORE_IMAGE_default_$(ART_PHONY_TEST_TARGET_SUFFIX)) $(OATDUMP) +dump-oat-core-target-$(TARGET_ARCH): $(TARGET_CORE_IMAGE_DEFAULT_$(ART_PHONY_TEST_TARGET_SUFFIX)) $(OATDUMP) $(OATDUMP) --image=$(TARGET_CORE_IMG_LOCATION) \ --output=$(ART_DUMP_OAT_PATH)/core.target.$(TARGET_ARCH).oatdump.txt --instruction-set=$(TARGET_ARCH) @echo Output in $(ART_DUMP_OAT_PATH)/core.target.$(TARGET_ARCH).oatdump.txt @@ -50,7 +50,7 @@ endif ifdef TARGET_2ND_ARCH .PHONY: dump-oat-core-target-$(TARGET_2ND_ARCH) ifeq ($(ART_BUILD_TARGET),true) -dump-oat-core-target-$(TARGET_2ND_ARCH): $(TARGET_CORE_IMAGE_default_$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)) $(OATDUMP) +dump-oat-core-target-$(TARGET_2ND_ARCH): $(TARGET_CORE_IMAGE_DEFAULT_$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)) $(OATDUMP) $(OATDUMP) --image=$(TARGET_CORE_IMG_LOCATION) \ --output=$(ART_DUMP_OAT_PATH)/core.target.$(TARGET_2ND_ARCH).oatdump.txt --instruction-set=$(TARGET_2ND_ARCH) @echo Output in $(ART_DUMP_OAT_PATH)/core.target.$(TARGET_2ND_ARCH).oatdump.txt -- GitLab From 9cb7fe4daf872fd0cb312489af263ebc622033a8 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Thu, 30 Nov 2017 11:46:45 -0800 Subject: [PATCH 127/226] Exploited CHECK-START-{x,y,z} syntax. Rationale: Previous CL introduced new check syntax to define multiple target architectures. This CL exploits the new feature. Bug: 62352954 Test: test-art-host test-art-target Change-Id: Ia2b9f210b0c1483e96e1df2d3d5e27f24420245d --- .../src/Main.java | 47 +--- test/640-checker-boolean-simd/src/Main.java | 48 +--- test/640-checker-byte-simd/src/Main.java | 84 +----- test/640-checker-char-simd/src/Main.java | 85 +----- test/640-checker-double-simd/src/Main.java | 48 +--- test/640-checker-float-simd/src/Main.java | 49 +--- test/640-checker-int-simd/src/Main.java | 134 +--------- test/640-checker-long-simd/src/Main.java | 80 ++---- test/640-checker-short-simd/src/Main.java | 85 +----- test/645-checker-abs-simd/src/Main.java | 96 +------ test/646-checker-hadd-alt-byte/src/Main.java | 88 +------ test/646-checker-hadd-alt-char/src/Main.java | 88 +------ test/646-checker-hadd-alt-short/src/Main.java | 88 +------ test/646-checker-hadd-byte/src/Main.java | 88 +------ test/646-checker-hadd-short/src/Main.java | 144 +--------- .../src/Main.java | 65 +---- .../src/Main.java | 37 +-- .../651-checker-int-simd-minmax/src/Main.java | 28 +- .../src/Main.java | 65 +---- test/656-checker-simd-opt/src/Main.java | 42 +-- test/660-checker-simd-sad-byte/src/Main.java | 67 +---- test/660-checker-simd-sad-int/src/Main.java | 76 +----- test/660-checker-simd-sad-long/src/Main.java | 42 +-- test/660-checker-simd-sad-short/src/Main.java | 67 +---- test/661-checker-simd-reduc/src/Main.java | 247 ++---------------- 25 files changed, 183 insertions(+), 1805 deletions(-) diff --git a/test/550-checker-multiply-accumulate/src/Main.java b/test/550-checker-multiply-accumulate/src/Main.java index 9e6fd3d96a..b76efeac15 100644 --- a/test/550-checker-multiply-accumulate/src/Main.java +++ b/test/550-checker-multiply-accumulate/src/Main.java @@ -424,31 +424,19 @@ public class Main { return - (left * right); } - /// CHECK-START-ARM64: void Main.SimdMulAdd(int[], int[]) instruction_simplifier$after_bce (before) + /// CHECK-START-{ARM64,MIPS64}: void Main.SimdMulAdd(int[], int[]) instruction_simplifier$after_bce (before) /// CHECK-DAG: Phi loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-START-ARM64: void Main.SimdMulAdd(int[], int[]) instruction_simplifier$after_bce (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.SimdMulAdd(int[], int[]) instruction_simplifier$after_bce (after) /// CHECK-DAG: Phi loop:<> outer_loop:none /// CHECK-DAG: VecMultiplyAccumulate kind:Add loop:<> outer_loop:none - /// CHECK-START-ARM64: void Main.SimdMulAdd(int[], int[]) instruction_simplifier$after_bce (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.SimdMulAdd(int[], int[]) instruction_simplifier$after_bce (after) /// CHECK-NOT: VecMul /// CHECK-NOT: VecAdd - /// CHECK-START-MIPS64: void Main.SimdMulAdd(int[], int[]) instruction_simplifier$after_bce (before) - /// CHECK-DAG: Phi loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - - /// CHECK-START-MIPS64: void Main.SimdMulAdd(int[], int[]) instruction_simplifier$after_bce (after) - /// CHECK-DAG: Phi loop:<> outer_loop:none - /// CHECK-DAG: VecMultiplyAccumulate kind:Add loop:<> outer_loop:none - - /// CHECK-START-MIPS64: void Main.SimdMulAdd(int[], int[]) instruction_simplifier$after_bce (after) - /// CHECK-NOT: VecMul - /// CHECK-NOT: VecAdd public static void SimdMulAdd(int[] array1, int[] array2) { for (int j = 0; j < 100; j++) { array2[j] += 12345 * array1[j]; @@ -473,31 +461,19 @@ public class Main { } } - /// CHECK-START-ARM64: void Main.SimdMulSub(int[], int[]) instruction_simplifier$after_bce (before) + /// CHECK-START-{ARM64,MIPS64}: void Main.SimdMulSub(int[], int[]) instruction_simplifier$after_bce (before) /// CHECK-DAG: Phi loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-START-ARM64: void Main.SimdMulSub(int[], int[]) instruction_simplifier$after_bce (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.SimdMulSub(int[], int[]) instruction_simplifier$after_bce (after) /// CHECK-DAG: Phi loop:<> outer_loop:none /// CHECK-DAG: VecMultiplyAccumulate kind:Sub loop:<> outer_loop:none - /// CHECK-START-ARM64: void Main.SimdMulSub(int[], int[]) instruction_simplifier$after_bce (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.SimdMulSub(int[], int[]) instruction_simplifier$after_bce (after) /// CHECK-NOT: VecMul /// CHECK-NOT: VecSub - /// CHECK-START-MIPS64: void Main.SimdMulSub(int[], int[]) instruction_simplifier$after_bce (before) - /// CHECK-DAG: Phi loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - - /// CHECK-START-MIPS64: void Main.SimdMulSub(int[], int[]) instruction_simplifier$after_bce (after) - /// CHECK-DAG: Phi loop:<> outer_loop:none - /// CHECK-DAG: VecMultiplyAccumulate kind:Sub loop:<> outer_loop:none - - /// CHECK-START-MIPS64: void Main.SimdMulSub(int[], int[]) instruction_simplifier$after_bce (after) - /// CHECK-NOT: VecMul - /// CHECK-NOT: VecSub public static void SimdMulSub(int[] array1, int[] array2) { for (int j = 0; j < 100; j++) { array2[j] -= 12345 * array1[j]; @@ -522,21 +498,14 @@ public class Main { } } - /// CHECK-START-ARM64: void Main.SimdMulMultipleUses(int[], int[]) instruction_simplifier$after_bce (before) + /// CHECK-START-{ARM64,MIPS64}: void Main.SimdMulMultipleUses(int[], int[]) instruction_simplifier$after_bce (before) /// CHECK-DAG: Phi loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-START-ARM64: void Main.SimdMulMultipleUses(int[], int[]) instruction_simplifier$after_bce (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.SimdMulMultipleUses(int[], int[]) instruction_simplifier$after_bce (after) /// CHECK-NOT: VecMultiplyAccumulate - /// CHECK-START-MIPS64: void Main.SimdMulMultipleUses(int[], int[]) instruction_simplifier$after_bce (before) - /// CHECK-DAG: Phi loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - - /// CHECK-START-MIPS64: void Main.SimdMulMultipleUses(int[], int[]) instruction_simplifier$after_bce (after) - /// CHECK-NOT: VecMultiplyAccumulate public static void SimdMulMultipleUses(int[] array1, int[] array2) { for (int j = 0; j < 100; j++) { int temp = 12345 * array1[j]; diff --git a/test/640-checker-boolean-simd/src/Main.java b/test/640-checker-boolean-simd/src/Main.java index 347f916c8d..7d98e68a5a 100644 --- a/test/640-checker-boolean-simd/src/Main.java +++ b/test/640-checker-boolean-simd/src/Main.java @@ -29,17 +29,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.and(boolean) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAnd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.and(boolean) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAnd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.and(boolean) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.and(boolean) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAnd loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -52,17 +42,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.or(boolean) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecOr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.or(boolean) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecOr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.or(boolean) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.or(boolean) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecOr loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -75,17 +55,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.xor(boolean) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecXor loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.xor(boolean) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecXor loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.xor(boolean) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.xor(boolean) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecXor loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -98,17 +68,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.not() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.not() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNot loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none diff --git a/test/640-checker-byte-simd/src/Main.java b/test/640-checker-byte-simd/src/Main.java index 5c13fc3926..6b691277b0 100644 --- a/test/640-checker-byte-simd/src/Main.java +++ b/test/640-checker-byte-simd/src/Main.java @@ -29,17 +29,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.add(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.add(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAdd loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -52,17 +42,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sub(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.sub(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -75,17 +55,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.mul(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.mul(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -111,17 +81,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.neg() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.neg() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNeg loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -134,17 +94,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.not() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.not() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNot loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -157,17 +107,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShl loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShl loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shl4() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.shl4() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecShl loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -180,17 +120,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sar2() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.sar2() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecShr loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none diff --git a/test/640-checker-char-simd/src/Main.java b/test/640-checker-char-simd/src/Main.java index b3dff1411b..317a666980 100644 --- a/test/640-checker-char-simd/src/Main.java +++ b/test/640-checker-char-simd/src/Main.java @@ -29,17 +29,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.add(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.add(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAdd loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -52,17 +42,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sub(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.sub(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -75,17 +55,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.mul(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.mul(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -99,6 +69,7 @@ public class Main { /// CHECK-DAG: ArraySet loop:<> outer_loop:none // /// CHECK-START: void Main.div(int) loop_optimization (after) + /// CHECK-NOT: VecDiv // // Not supported on any architecture. // @@ -111,17 +82,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.neg() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.neg() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNeg loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -134,17 +95,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.not() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.not() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNot loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -157,17 +108,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShl loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShl loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shl4() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.shl4() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecShl loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -192,17 +133,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.shr2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecUShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shr2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecUShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shr2() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.shr2() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecUShr loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none diff --git a/test/640-checker-double-simd/src/Main.java b/test/640-checker-double-simd/src/Main.java index 5d0899864a..0f04f734cc 100644 --- a/test/640-checker-double-simd/src/Main.java +++ b/test/640-checker-double-simd/src/Main.java @@ -30,12 +30,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.add(double) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.add(double) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.add(double) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAdd loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -48,12 +43,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.sub(double) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sub(double) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.sub(double) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -66,12 +56,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.mul(double) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.mul(double) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.mul(double) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -84,12 +69,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.div(double) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecDiv loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.div(double) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.div(double) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecDiv loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -102,12 +82,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.neg() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.neg() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNeg loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -120,12 +95,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.abs() 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-START-MIPS64: void Main.abs() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.abs() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAbs loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -138,11 +108,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.conv(long[]) loop_optimization (after) - /// CHECK-NOT: VecLoad - /// CHECK-NOT: VecStore - // - /// CHECK-START-MIPS64: void Main.conv(long[]) loop_optimization (after) + /// CHECK-START: void Main.conv(long[]) loop_optimization (after) /// CHECK-NOT: VecLoad /// CHECK-NOT: VecStore // diff --git a/test/640-checker-float-simd/src/Main.java b/test/640-checker-float-simd/src/Main.java index c7883f37a3..d4eef9f763 100644 --- a/test/640-checker-float-simd/src/Main.java +++ b/test/640-checker-float-simd/src/Main.java @@ -30,12 +30,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.add(float) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.add(float) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.add(float) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAdd loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -48,12 +43,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.sub(float) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sub(float) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.sub(float) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -66,12 +56,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.mul(float) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.mul(float) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.mul(float) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -84,12 +69,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.div(float) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecDiv loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.div(float) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.div(float) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecDiv loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -102,12 +82,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.neg() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.neg() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNeg loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -120,12 +95,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-MIPS64: void Main.abs() 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-START-ARM64: void Main.abs() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.abs() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAbs loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -138,12 +108,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.conv(int[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecCnv loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.conv(int[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.conv(int[]) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecCnv loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none diff --git a/test/640-checker-int-simd/src/Main.java b/test/640-checker-int-simd/src/Main.java index aa230bfcaf..85d8b1b70b 100644 --- a/test/640-checker-int-simd/src/Main.java +++ b/test/640-checker-int-simd/src/Main.java @@ -29,17 +29,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.add(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.add(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAdd loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -52,17 +42,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sub(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.sub(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -75,17 +55,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.mul(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.mul(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -112,17 +82,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.neg() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.neg() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNeg loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -135,17 +95,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.not() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.not() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNot loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -158,17 +108,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShl loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShl loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shl4() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.shl4() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecShl loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -181,17 +121,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sar2() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.sar2() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecShr loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -204,17 +134,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.shr2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecUShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shr2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecUShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shr2() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.shr2() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecUShr loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -242,15 +162,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.shr32() loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shr32() loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shr32() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.shr32() loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none static void shr32() { @@ -271,19 +183,7 @@ public class Main { /// CHECK-DAG: <> UShr [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.shr33() loop_optimization (after) - /// CHECK-DAG: <> IntConstant 1 loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shr33() loop_optimization (after) - /// CHECK-DAG: <> IntConstant 1 loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shr33() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.shr33() loop_optimization (after) /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none @@ -305,19 +205,7 @@ public class Main { /// CHECK-DAG: <> UShr [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.shrMinus254() loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shrMinus254() loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shrMinus254() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.shrMinus254() loop_optimization (after) /// CHECK-DAG: <> IntConstant 2 loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none diff --git a/test/640-checker-long-simd/src/Main.java b/test/640-checker-long-simd/src/Main.java index c754f2a309..bb4d0cbd67 100644 --- a/test/640-checker-long-simd/src/Main.java +++ b/test/640-checker-long-simd/src/Main.java @@ -29,12 +29,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.add(long) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.add(long) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.add(long) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAdd loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -47,12 +42,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.sub(long) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sub(long) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.sub(long) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -65,14 +55,15 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // + // Not directly supported for longs. + // + /// CHECK-START-ARM64: void Main.mul(long) loop_optimization (after) + /// CHECK-NOT: VecMul + // /// CHECK-START-MIPS64: void Main.mul(long) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - // Not supported for longs. - /// CHECK-START-ARM64: void Main.mul(long) loop_optimization (after) - /// CHECK-NOT: VecMul static void mul(long x) { for (int i = 0; i < 128; i++) a[i] *= x; @@ -96,12 +87,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.neg() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.neg() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNeg loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -114,12 +100,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.not() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.not() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNot loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -132,12 +113,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShl loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shl4() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.shl4() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecShl loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -150,12 +126,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sar2() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.sar2() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecShr loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -168,12 +139,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.shr2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecUShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shr2() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.shr2() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecUShr loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -201,11 +167,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.shr64() loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shr64() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.shr64() loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none static void shr64() { @@ -226,13 +188,7 @@ public class Main { /// CHECK-DAG: <> UShr [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.shr65() loop_optimization (after) - /// CHECK-DAG: <> IntConstant 1 loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shr65() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.shr65() loop_optimization (after) /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none @@ -254,13 +210,7 @@ public class Main { /// CHECK-DAG: <> UShr [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.shrMinus254() loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shrMinus254() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.shrMinus254() loop_optimization (after) /// CHECK-DAG: <> IntConstant 2 loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none diff --git a/test/640-checker-short-simd/src/Main.java b/test/640-checker-short-simd/src/Main.java index e187397853..2b4ba87b71 100644 --- a/test/640-checker-short-simd/src/Main.java +++ b/test/640-checker-short-simd/src/Main.java @@ -29,17 +29,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.add(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.add(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAdd loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -52,17 +42,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sub(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.sub(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -75,17 +55,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.mul(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.mul(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -99,6 +69,7 @@ public class Main { /// CHECK-DAG: ArraySet loop:<> outer_loop:none // /// CHECK-START: void Main.div(int) loop_optimization (after) + /// CHECK-NOT: VecDiv // // Not supported on any architecture. // @@ -111,17 +82,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.neg() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.neg() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNeg loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -134,17 +95,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.not() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.not() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNot loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -157,17 +108,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShl loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShl loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shl4() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.shl4() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecShl loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -180,17 +121,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sar2() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.sar2() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecShr loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none diff --git a/test/645-checker-abs-simd/src/Main.java b/test/645-checker-abs-simd/src/Main.java index 823908c20e..d498bda052 100644 --- a/test/645-checker-abs-simd/src/Main.java +++ b/test/645-checker-abs-simd/src/Main.java @@ -28,7 +28,7 @@ public class Main { /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.doitByte(byte[]) loop_optimization (after) + /// 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 @@ -38,25 +38,6 @@ public class Main { // /// CHECK-EVAL: "<>" != "<>" // - /// CHECK-START-ARM64: 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-EVAL: "<>" != "<>" - // - /// CHECK-START-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-EVAL: "<>" != "<>" private static void doitByte(byte[] x) { for (int i = 0; i < x.length; i++) { x[i] = (byte) Math.abs(x[i]); @@ -84,17 +65,7 @@ public class Main { /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: 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-EVAL: "<>" != "<>" - // - /// CHECK-START-ARM64: void Main.doitShort(short[]) loop_optimization (after) + /// 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 @@ -104,15 +75,6 @@ public class Main { // /// CHECK-EVAL: "<>" != "<>" // - /// CHECK-START-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-EVAL: "<>" != "<>" private static void doitShort(short[] x) { for (int i = 0; i < x.length; i++) { x[i] = (short) Math.abs(x[i]); @@ -147,7 +109,7 @@ public class Main { /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.doitInt(int[]) loop_optimization (after) + /// 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 @@ -157,25 +119,6 @@ public class Main { // /// CHECK-EVAL: "<>" != "<>" // - /// CHECK-START-ARM64: 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-EVAL: "<>" != "<>" - // - /// CHECK-START-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-EVAL: "<>" != "<>" private static void doitInt(int[] x) { for (int i = 0; i < x.length; i++) { x[i] = Math.abs(x[i]); @@ -188,7 +131,7 @@ public class Main { /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsLong loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.doitLong(long[]) loop_optimization (after) + /// 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 @@ -198,15 +141,6 @@ public class Main { // /// CHECK-EVAL: "<>" != "<>" // - /// CHECK-START-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-EVAL: "<>" != "<>" private static void doitLong(long[] x) { for (int i = 0; i < x.length; i++) { x[i] = Math.abs(x[i]); @@ -219,7 +153,7 @@ public class Main { /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsFloat loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.doitFloat(float[]) loop_optimization (after) + /// 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 @@ -229,15 +163,6 @@ public class Main { // /// CHECK-EVAL: "<>" != "<>" // - /// CHECK-START-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-EVAL: "<>" != "<>" private static void doitFloat(float[] x) { for (int i = 0; i < x.length; i++) { x[i] = Math.abs(x[i]); @@ -250,7 +175,7 @@ public class Main { /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsDouble loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.doitDouble(double[]) loop_optimization (after) + /// 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 @@ -260,15 +185,6 @@ public class Main { // /// CHECK-EVAL: "<>" != "<>" // - /// CHECK-START-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-EVAL: "<>" != "<>" private static void doitDouble(double[] x) { for (int i = 0; i < x.length; i++) { x[i] = Math.abs(x[i]); diff --git a/test/646-checker-hadd-alt-byte/src/Main.java b/test/646-checker-hadd-alt-byte/src/Main.java index 41aa40cd6d..2ef340ab45 100644 --- a/test/646-checker-hadd-alt-byte/src/Main.java +++ b/test/646-checker-hadd-alt-byte/src/Main.java @@ -39,19 +39,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none @@ -86,19 +74,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none @@ -121,19 +97,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:true loop:<> outer_loop:none @@ -170,19 +134,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:true loop:<> outer_loop:none @@ -204,21 +156,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 127 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 127 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 127 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none @@ -252,21 +190,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 255 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 255 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 255 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none diff --git a/test/646-checker-hadd-alt-char/src/Main.java b/test/646-checker-hadd-alt-char/src/Main.java index 8f879c77d0..2a1382dfde 100644 --- a/test/646-checker-hadd-alt-char/src/Main.java +++ b/test/646-checker-hadd-alt-char/src/Main.java @@ -39,19 +39,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned(char[], char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned(char[], char[], char[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none @@ -87,19 +75,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none @@ -125,19 +101,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_unsigned(char[], char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_unsigned(char[], char[], char[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none @@ -174,19 +138,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none @@ -211,21 +163,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 65535 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 65535 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned_constant(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned_constant(char[], char[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 65535 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none @@ -259,21 +197,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_also_unsigned_constant(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 65535 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_also_unsigned_constant(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 65535 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_also_unsigned_constant(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_also_unsigned_constant(char[], char[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 65535 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none diff --git a/test/646-checker-hadd-alt-short/src/Main.java b/test/646-checker-hadd-alt-short/src/Main.java index b591081fba..4035b97209 100644 --- a/test/646-checker-hadd-alt-short/src/Main.java +++ b/test/646-checker-hadd-alt-short/src/Main.java @@ -39,19 +39,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed(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:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed(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:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_signed(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:false loop:<> outer_loop:none @@ -86,19 +74,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned(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:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned(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:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned(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:Uint16 rounded:false loop:<> outer_loop:none @@ -121,19 +97,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_signed(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 - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_signed(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 - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_signed(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 @@ -170,19 +134,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(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:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(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:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_unsigned(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:Uint16 rounded:true loop:<> outer_loop:none @@ -204,21 +156,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 32767 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 32767 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 32767 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none @@ -252,21 +190,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 65535 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 65535 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 65535 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none diff --git a/test/646-checker-hadd-byte/src/Main.java b/test/646-checker-hadd-byte/src/Main.java index 4d259c437b..ca22200a1a 100644 --- a/test/646-checker-hadd-byte/src/Main.java +++ b/test/646-checker-hadd-byte/src/Main.java @@ -36,19 +36,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none @@ -83,19 +71,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none @@ -118,19 +94,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:true loop:<> outer_loop:none @@ -167,19 +131,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:true loop:<> outer_loop:none @@ -201,21 +153,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 127 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 127 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 127 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none @@ -249,21 +187,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 255 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 255 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 255 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none diff --git a/test/646-checker-hadd-short/src/Main.java b/test/646-checker-hadd-short/src/Main.java index 55bb958670..85c2fcaf22 100644 --- a/test/646-checker-hadd-short/src/Main.java +++ b/test/646-checker-hadd-short/src/Main.java @@ -36,19 +36,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed(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:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed(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:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_signed(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:false loop:<> outer_loop:none @@ -74,19 +62,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed_alt(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:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed_alt(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:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_signed_alt(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_signed_alt(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:false loop:<> outer_loop:none @@ -122,19 +98,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned(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:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned(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:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned(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:Uint16 rounded:false loop:<> outer_loop:none @@ -157,19 +121,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_signed(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 - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_signed(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 - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_signed(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 @@ -192,19 +144,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_signed_alt(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 - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_signed_alt(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 - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_signed_alt(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_signed_alt(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 @@ -231,19 +171,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_signed_alt2(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 - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_signed_alt2(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 - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_signed_alt2(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_signed_alt2(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 @@ -281,19 +209,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(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:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(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:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_unsigned(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:Uint16 rounded:true loop:<> outer_loop:none @@ -330,19 +246,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned_alt(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:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned_alt(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:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_unsigned_alt(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_unsigned_alt(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:Uint16 rounded:true loop:<> outer_loop:none @@ -365,21 +269,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 32767 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 32767 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 32767 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none @@ -413,21 +303,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 65535 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 65535 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 65535 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none diff --git a/test/651-checker-byte-simd-minmax/src/Main.java b/test/651-checker-byte-simd-minmax/src/Main.java index 2188346aa9..45949ae90a 100644 --- a/test/651-checker-byte-simd-minmax/src/Main.java +++ b/test/651-checker-byte-simd-minmax/src/Main.java @@ -27,19 +27,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.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 - // - /// CHECK-START-ARM64: void Main.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 - // - /// CHECK-START-MIPS64: void Main.doitMin(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.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 @@ -70,19 +58,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.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 - // - /// CHECK-START-ARM64: void Main.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 - // - /// CHECK-START-MIPS64: void Main.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.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 @@ -102,19 +78,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.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 - // - /// CHECK-START-ARM64: void Main.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 - // - /// CHECK-START-MIPS64: void Main.doitMax(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.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 @@ -145,19 +109,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.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 - // - /// CHECK-START-ARM64: void Main.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 - // - /// CHECK-START-MIPS64: void Main.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.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 @@ -177,14 +129,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.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 - // - /// CHECK-START-MIPS64: void Main.doitMin100(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.doitMin100(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 100 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad 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 d92bdaf808..9b056094a3 100644 --- a/test/651-checker-char-simd-minmax/src/Main.java +++ b/test/651-checker-char-simd-minmax/src/Main.java @@ -27,19 +27,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.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 - // - /// CHECK-START-ARM64: void Main.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 - // - /// CHECK-START-MIPS64: void Main.doitMin(char[], char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.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 @@ -59,19 +47,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.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 - // - /// CHECK-START-ARM64: void Main.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 - // - /// CHECK-START-MIPS64: void Main.doitMax(char[], char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.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 @@ -91,14 +67,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.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 - // - /// CHECK-START-MIPS64: void Main.doitMin100(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.doitMin100(char[], char[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 100 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none diff --git a/test/651-checker-int-simd-minmax/src/Main.java b/test/651-checker-int-simd-minmax/src/Main.java index 598106e604..66343adaa8 100644 --- a/test/651-checker-int-simd-minmax/src/Main.java +++ b/test/651-checker-int-simd-minmax/src/Main.java @@ -26,19 +26,7 @@ public class Main { /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMinIntInt loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMin(int[], int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] unsigned:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMin(int[], int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] unsigned:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.doitMin(int[], int[], int[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(int[], int[], int[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMin [<>,<>] unsigned:false loop:<> outer_loop:none @@ -57,19 +45,7 @@ public class Main { /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMaxIntInt loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMax(int[], int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] unsigned:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMax(int[], int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] unsigned:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.doitMax(int[], int[], int[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(int[], int[], int[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMax [<>,<>] unsigned:false loop:<> outer_loop:none diff --git a/test/651-checker-short-simd-minmax/src/Main.java b/test/651-checker-short-simd-minmax/src/Main.java index 91f2a2dbdb..5f10adab79 100644 --- a/test/651-checker-short-simd-minmax/src/Main.java +++ b/test/651-checker-short-simd-minmax/src/Main.java @@ -27,19 +27,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.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 - // - /// CHECK-START-ARM64: void Main.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 - // - /// CHECK-START-MIPS64: void Main.doitMin(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.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 @@ -70,19 +58,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.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 - // - /// CHECK-START-ARM64: void Main.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 - // - /// CHECK-START-MIPS64: void Main.doitMinUnsigned(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.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 @@ -102,19 +78,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.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 - // - /// CHECK-START-ARM64: void Main.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 - // - /// CHECK-START-MIPS64: void Main.doitMax(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.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 @@ -145,19 +109,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.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 - // - /// CHECK-START-ARM64: void Main.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 - // - /// CHECK-START-MIPS64: void Main.doitMaxUnsigned(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.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 @@ -177,14 +129,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.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 - // - /// CHECK-START-MIPS64: void Main.doitMin100(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.doitMin100(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 100 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none diff --git a/test/656-checker-simd-opt/src/Main.java b/test/656-checker-simd-opt/src/Main.java index 31d28e851e..081e421422 100644 --- a/test/656-checker-simd-opt/src/Main.java +++ b/test/656-checker-simd-opt/src/Main.java @@ -102,20 +102,7 @@ public class Main { /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.longInductionReduction(long[]) loop_optimization (after) - /// CHECK-DAG: <> LongConstant 0 loop:none - /// CHECK-DAG: <> LongConstant 1 loop:none - /// CHECK-DAG: <> LongConstant 2 loop:none - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecSetScalars [<>] loop:none - /// CHECK-DAG: <> Phi [<>,{{j\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.longInductionReduction(long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.longInductionReduction(long[]) loop_optimization (after) /// CHECK-DAG: <> LongConstant 0 loop:none /// CHECK-DAG: <> LongConstant 1 loop:none /// CHECK-DAG: <> LongConstant 2 loop:none @@ -144,18 +131,7 @@ public class Main { /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.intVectorLongInvariant(int[], long[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 1 loop:none - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> ArrayGet [{{l\d+}},<>] loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.intVectorLongInvariant(int[], long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.intVectorLongInvariant(int[], long[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> IntConstant 4 loop:none @@ -183,19 +159,7 @@ public class Main { /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.longCanBeDoneWithInt(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> LongConstant 1 loop:none - /// CHECK-DAG: <> TypeConversion [<>] loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> Phi [<>,{{i\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.longCanBeDoneWithInt(int[], int[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.longCanBeDoneWithInt(int[], int[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 4 loop:none /// CHECK-DAG: <> LongConstant 1 loop:none diff --git a/test/660-checker-simd-sad-byte/src/Main.java b/test/660-checker-simd-sad-byte/src/Main.java index 877d7189a2..594948b7f9 100644 --- a/test/660-checker-simd-sad-byte/src/Main.java +++ b/test/660-checker-simd-sad-byte/src/Main.java @@ -99,18 +99,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadByte2Int(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 16 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadByte2Int(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadByte2Int(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 16 loop:none /// CHECK-DAG: <> VecSetScalars [<>] loop:none @@ -141,18 +130,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadByte2IntAlt(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 16 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadByte2IntAlt(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadByte2IntAlt(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 16 loop:none /// CHECK-DAG: <> VecSetScalars [<>] loop:none @@ -185,18 +163,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadByte2IntAlt2(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 16 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadByte2IntAlt2(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadByte2IntAlt2(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 16 loop:none /// CHECK-DAG: <> VecSetScalars [<>] loop:none @@ -234,19 +201,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadByte2Long(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 16 loop:none - /// CHECK-DAG: <> LongConstant 0 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadByte2Long(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadByte2Long(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 16 loop:none /// CHECK-DAG: <> LongConstant 0 loop:none @@ -283,19 +238,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadByte2LongAt1(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 16 loop:none - /// CHECK-DAG: <> LongConstant 1 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadByte2LongAt1(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadByte2LongAt1(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 16 loop:none /// CHECK-DAG: <> LongConstant 1 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 d7d5a959d7..aa8431c1f5 100644 --- a/test/660-checker-simd-sad-int/src/Main.java +++ b/test/660-checker-simd-sad-int/src/Main.java @@ -31,32 +31,14 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: int Main.sadInt2Int(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.sadInt2Int(int[], int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant {{2|4}} loop:none /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\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 - // - /// CHECK-START-ARM64: int Main.sadInt2Int(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\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 - // - /// CHECK-START-MIPS64: int Main.sadInt2Int(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\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 + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static int sadInt2Int(int[] x, int[] y) { int min_length = Math.min(x.length, y.length); int sad = 0; @@ -106,32 +88,14 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: int Main.sadInt2IntAlt2(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\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 - // - /// CHECK-START-ARM64: int Main.sadInt2IntAlt2(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\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 - // - /// CHECK-START-MIPS64: int Main.sadInt2IntAlt2(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.sadInt2IntAlt2(int[], int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant {{2|4}} loop:none /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\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 + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static int sadInt2IntAlt2(int[] x, int[] y) { int min_length = Math.min(x.length, y.length); int sad = 0; @@ -160,19 +124,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadInt2Long(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> LongConstant 0 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadInt2Long(int[], int[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadInt2Long(int[], int[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 4 loop:none /// CHECK-DAG: <> LongConstant 0 loop:none @@ -209,19 +161,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadInt2LongAt1(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> LongConstant 1 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadInt2LongAt1(int[], int[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadInt2LongAt1(int[], int[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 4 loop:none /// CHECK-DAG: <> LongConstant 1 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 d080e0cf3a..8281812b5b 100644 --- a/test/660-checker-simd-sad-long/src/Main.java +++ b/test/660-checker-simd-sad-long/src/Main.java @@ -32,19 +32,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadLong2Long(long[], long[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> LongConstant 0 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadLong2Long(long[], long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadLong2Long(long[], long[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 2 loop:none /// CHECK-DAG: <> LongConstant 0 loop:none @@ -106,19 +94,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadLong2LongAlt2(long[], long[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> LongConstant 0 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadLong2LongAlt2(long[], long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadLong2LongAlt2(long[], long[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 2 loop:none /// CHECK-DAG: <> LongConstant 0 loop:none @@ -155,19 +131,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadLong2LongAt1(long[], long[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> LongConstant 1 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadLong2LongAt1(long[], long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadLong2LongAt1(long[], long[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 2 loop:none /// CHECK-DAG: <> LongConstant 1 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 4ab66827b6..16bcabac95 100644 --- a/test/660-checker-simd-sad-short/src/Main.java +++ b/test/660-checker-simd-sad-short/src/Main.java @@ -66,18 +66,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2Int(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 8 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadShort2Int(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2Int(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> VecSetScalars [<>] loop:none @@ -108,18 +97,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntAlt(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 8 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadShort2IntAlt(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntAlt(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> VecSetScalars [<>] loop:none @@ -152,18 +130,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntAlt2(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 8 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadShort2IntAlt2(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntAlt2(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> VecSetScalars [<>] loop:none @@ -201,19 +168,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadShort2Long(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 8 loop:none - /// CHECK-DAG: <> LongConstant 0 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadShort2Long(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadShort2Long(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> LongConstant 0 loop:none @@ -250,19 +205,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadShort2LongAt1(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 8 loop:none - /// CHECK-DAG: <> LongConstant 1 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadShort2LongAt1(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadShort2LongAt1(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> LongConstant 1 loop:none diff --git a/test/661-checker-simd-reduc/src/Main.java b/test/661-checker-simd-reduc/src/Main.java index 1add0f1026..3a0a0495c4 100644 --- a/test/661-checker-simd-reduc/src/Main.java +++ b/test/661-checker-simd-reduc/src/Main.java @@ -62,33 +62,13 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Return [<>] loop:none // - /// CHECK-START-ARM: int Main.reductionInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-ARM64: int Main.reductionInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionInt(int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant {{2|4}} loop:none /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-MIPS64: int Main.reductionInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecReduce [<>] loop:none /// CHECK-DAG: <> VecExtractScalar [<>] loop:none private static int reductionInt(int[] x) { @@ -116,58 +96,19 @@ public class Main { // /// CHECK-EVAL: "<>" != "<>" // - /// CHECK-START-ARM: int Main.reductionIntChain() loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-EVAL: "<>" != "<>" - // - /// CHECK-START-ARM64: int Main.reductionIntChain() loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-EVAL: "<>" != "<>" - // - /// CHECK-START-MIPS64: int Main.reductionIntChain() loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionIntChain() loop_optimization (after) /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecReduce [<>] loop:none /// CHECK-DAG: <> VecExtractScalar [<>] loop:none /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecReduce [<>] loop:none /// CHECK-DAG: <> VecExtractScalar [<>] loop:none // @@ -199,38 +140,18 @@ public class Main { // /// CHECK-EVAL: "<>" != "<>" // - /// CHECK-START-ARM: int Main.reductionIntToLoop(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-ARM64: int Main.reductionIntToLoop(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-MIPS64: int Main.reductionIntToLoop(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionIntToLoop(int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant {{2|4}} loop:none /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecReduce [<>] loop:none /// CHECK-DAG: <> VecExtractScalar [<>] loop:none private static int reductionIntToLoop(int[] x) { int r = 0; - for (int i = 0; i < 4; i++) { + for (int i = 0; i < 8; i++) { r += x[i]; } for (int i = r; i < 16; i++) { @@ -250,17 +171,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Return [<>] loop:none // - /// CHECK-START-ARM64: long Main.reductionLong(long[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecSetScalars [{{j\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-MIPS64: long Main.reductionLong(long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.reductionLong(long[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 2 loop:none /// CHECK-DAG: <> VecSetScalars [{{j\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none @@ -312,33 +223,13 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Return [<>] loop:none // - /// CHECK-START-ARM: int Main.reductionIntM1(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-ARM64: int Main.reductionIntM1(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-MIPS64: int Main.reductionIntM1(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionIntM1(int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant {{2|4}} loop:none /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecReduce [<>] loop:none /// CHECK-DAG: <> VecExtractScalar [<>] loop:none private static int reductionIntM1(int[] x) { @@ -360,17 +251,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Return [<>] loop:none // - /// CHECK-START-ARM64: long Main.reductionLongM1(long[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecSetScalars [{{j\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-MIPS64: long Main.reductionLongM1(long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.reductionLongM1(long[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 2 loop:none /// CHECK-DAG: <> VecSetScalars [{{j\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none @@ -421,33 +302,13 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Return [<>] loop:none // - /// CHECK-START-ARM: int Main.reductionMinusInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecSub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-ARM64: int Main.reductionMinusInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionMinusInt(int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant {{2|4}} loop:none /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: VecSub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-MIPS64: int Main.reductionMinusInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecSub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecReduce [<>] loop:none /// CHECK-DAG: <> VecExtractScalar [<>] loop:none private static int reductionMinusInt(int[] x) { @@ -469,17 +330,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Return [<>] loop:none // - /// CHECK-START-ARM64: long Main.reductionMinusLong(long[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecSetScalars [{{j\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecSub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-MIPS64: long Main.reductionMinusLong(long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.reductionMinusLong(long[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 2 loop:none /// CHECK-DAG: <> VecSetScalars [{{j\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none @@ -531,33 +382,13 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Return [<>] loop:none // - /// CHECK-START-ARM: int Main.reductionMinInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 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 - // - /// CHECK-START-ARM64: int Main.reductionMinInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 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 - // - /// CHECK-START-MIPS64: int Main.reductionMinInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 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: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecReduce [<>] loop:none /// CHECK-DAG: <> VecExtractScalar [<>] loop:none private static int reductionMinInt(int[] x) { @@ -611,33 +442,13 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Return [<>] loop:none // - /// CHECK-START-ARM: int Main.reductionMaxInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 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 - // - /// CHECK-START-ARM64: int Main.reductionMaxInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 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 - // - /// CHECK-START-MIPS64: int Main.reductionMaxInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 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: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecReduce [<>] loop:none /// CHECK-DAG: <> VecExtractScalar [<>] loop:none private static int reductionMaxInt(int[] x) { @@ -743,9 +554,9 @@ public class Main { } // Test various reductions in loops. - int[] x0 = { 0, 0, 0, 0 }; - int[] x1 = { 0, 0, 0, 1 }; - int[] x2 = { 1, 1, 1, 1 }; + int[] x0 = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int[] x1 = { 0, 0, 0, 1, 0, 0, 0, 0 }; + int[] x2 = { 1, 1, 1, 1, 0, 0, 0, 0 }; expectEquals(-74, reductionByte(xb)); expectEquals(-27466, reductionShort(xs)); expectEquals(38070, reductionChar(xc)); @@ -754,7 +565,7 @@ public class Main { expectEquals(120, reductionIntToLoop(x0)); expectEquals(121, reductionIntToLoop(x1)); expectEquals(118, reductionIntToLoop(x2)); - expectEquals(-1205, reductionIntToLoop(xi)); + expectEquals(-1310, reductionIntToLoop(xi)); expectEquals(365750L, reductionLong(xl)); expectEquals(-75, reductionByteM1(xb)); expectEquals(-27467, reductionShortM1(xs)); -- GitLab From 2743d756c5cdf1065740cc47d8b3af774157de00 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Tue, 5 Dec 2017 10:36:12 -0800 Subject: [PATCH 128/226] Fix races in trace_helper.cc We had an unfortunate race when stopping tracing with the trace_helper test code. This could lead to calling JNI methods with null or deleted global references. We fix this by using a JVMTI Raw Monitor and copying the global into a local ref. This allows us to safely disable tracing at any time from any thread. Test: ./test.py --host -j50 Bug: 69657830 Change-Id: I2f5daadddbde0cbd2f8453affb3871774837c47e --- test/ti-agent/trace_helper.cc | 99 +++++++++++++++++++++++++++++------ 1 file changed, 82 insertions(+), 17 deletions(-) diff --git a/test/ti-agent/trace_helper.cc b/test/ti-agent/trace_helper.cc index 8b74c7c089..bbc7754a45 100644 --- a/test/ti-agent/trace_helper.cc +++ b/test/ti-agent/trace_helper.cc @@ -39,6 +39,18 @@ struct TraceData { bool in_callback; bool access_watch_on_load; bool modify_watch_on_load; + jrawMonitorID trace_mon; + + jclass GetTestClass(jvmtiEnv* jvmti, JNIEnv* env) { + if (JvmtiErrorToException(env, jvmti, jvmti->RawMonitorEnter(trace_mon))) { + return nullptr; + } + jclass out = reinterpret_cast(env->NewLocalRef(test_klass)); + if (JvmtiErrorToException(env, jvmti, jvmti->RawMonitorExit(trace_mon))) { + return nullptr; + } + return out; + } }; static void threadStartCB(jvmtiEnv* jvmti, @@ -49,8 +61,12 @@ static void threadStartCB(jvmtiEnv* jvmti, jvmti->GetEnvironmentLocalStorage(reinterpret_cast(&data)))) { return; } + ScopedLocalRef klass(jnienv, data->GetTestClass(jvmti, jnienv)); + if (klass.get() == nullptr) { + return; + } CHECK(data->thread_start != nullptr); - jnienv->CallStaticVoidMethod(data->test_klass, data->thread_start, thread); + jnienv->CallStaticVoidMethod(klass.get(), data->thread_start, thread); } static void threadEndCB(jvmtiEnv* jvmti, JNIEnv* jnienv, @@ -60,8 +76,12 @@ static void threadEndCB(jvmtiEnv* jvmti, jvmti->GetEnvironmentLocalStorage(reinterpret_cast(&data)))) { return; } + ScopedLocalRef klass(jnienv, data->GetTestClass(jvmti, jnienv)); + if (klass.get() == nullptr) { + return; + } CHECK(data->thread_end != nullptr); - jnienv->CallStaticVoidMethod(data->test_klass, data->thread_end, thread); + jnienv->CallStaticVoidMethod(klass.get(), data->thread_end, thread); } static void singleStepCB(jvmtiEnv* jvmti, @@ -77,10 +97,14 @@ static void singleStepCB(jvmtiEnv* jvmti, if (data->in_callback) { return; } + ScopedLocalRef klass(jnienv, data->GetTestClass(jvmti, jnienv)); + if (klass.get() == nullptr) { + return; + } CHECK(data->single_step != nullptr); data->in_callback = true; jobject method_arg = GetJavaMethod(jvmti, jnienv, method); - jnienv->CallStaticVoidMethod(data->test_klass, + jnienv->CallStaticVoidMethod(klass.get(), data->single_step, thread, method_arg, @@ -106,11 +130,15 @@ static void fieldAccessCB(jvmtiEnv* jvmti, // Don't do callback for either of these to prevent an infinite loop. return; } + ScopedLocalRef klass(jnienv, data->GetTestClass(jvmti, jnienv)); + if (klass.get() == nullptr) { + return; + } CHECK(data->field_access != nullptr); data->in_callback = true; jobject method_arg = GetJavaMethod(jvmti, jnienv, method); jobject field_arg = GetJavaField(jvmti, jnienv, field_klass, field); - jnienv->CallStaticVoidMethod(data->test_klass, + jnienv->CallStaticVoidMethod(klass.get(), data->field_access, method_arg, static_cast(location), @@ -141,6 +169,10 @@ static void fieldModificationCB(jvmtiEnv* jvmti, // Don't do callback recursively to prevent an infinite loop. return; } + ScopedLocalRef klass(jnienv, data->GetTestClass(jvmti, jnienv)); + if (klass.get() == nullptr) { + return; + } CHECK(data->field_modify != nullptr); data->in_callback = true; jobject method_arg = GetJavaMethod(jvmti, jnienv, method); @@ -152,7 +184,7 @@ static void fieldModificationCB(jvmtiEnv* jvmti, jnienv->DeleteLocalRef(field_arg); return; } - jnienv->CallStaticVoidMethod(data->test_klass, + jnienv->CallStaticVoidMethod(klass.get(), data->field_modify, method_arg, static_cast(location), @@ -180,6 +212,10 @@ static void methodExitCB(jvmtiEnv* jvmti, // Don't do callback for either of these to prevent an infinite loop. return; } + ScopedLocalRef klass(jnienv, data->GetTestClass(jvmti, jnienv)); + if (klass.get() == nullptr) { + return; + } CHECK(data->exit_method != nullptr); data->in_callback = true; jobject method_arg = GetJavaMethod(jvmti, jnienv, method); @@ -189,7 +225,7 @@ static void methodExitCB(jvmtiEnv* jvmti, data->in_callback = false; return; } - jnienv->CallStaticVoidMethod(data->test_klass, + jnienv->CallStaticVoidMethod(klass.get(), data->exit_method, method_arg, was_popped_by_exception, @@ -212,12 +248,16 @@ static void methodEntryCB(jvmtiEnv* jvmti, // Don't do callback for either of these to prevent an infinite loop. return; } + ScopedLocalRef klass(jnienv, data->GetTestClass(jvmti, jnienv)); + if (klass.get() == nullptr) { + return; + } data->in_callback = true; jobject method_arg = GetJavaMethod(jvmti, jnienv, method); if (jnienv->ExceptionCheck()) { return; } - jnienv->CallStaticVoidMethod(data->test_klass, data->enter_method, method_arg); + jnienv->CallStaticVoidMethod(klass.get(), data->enter_method, method_arg); jnienv->DeleteLocalRef(method_arg); data->in_callback = false; } @@ -407,6 +447,10 @@ extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing2( return; } memset(data, 0, sizeof(TraceData)); + if (JvmtiErrorToException(env, jvmti_env, + jvmti_env->CreateRawMonitor("Trace monitor", &data->trace_mon))) { + return; + } data->test_klass = reinterpret_cast(env->NewGlobalRef(klass)); data->enter_method = enter != nullptr ? env->FromReflectedMethod(enter) : nullptr; data->exit_method = exit != nullptr ? env->FromReflectedMethod(exit) : nullptr; @@ -537,42 +581,63 @@ extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing( if (data == nullptr || data->test_klass == nullptr) { return; } - env->DeleteGlobalRef(data->test_klass); - if (env->ExceptionCheck()) { - return; - } - // Clear test_klass so we know this isn't being used - data->test_klass = nullptr; + ScopedLocalRef err(env, nullptr); + // First disable all the events. if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_FIELD_ACCESS, thr))) { - return; + env->ExceptionDescribe(); + err.reset(env->ExceptionOccurred()); + env->ExceptionClear(); } if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_FIELD_MODIFICATION, thr))) { - return; + env->ExceptionDescribe(); + err.reset(env->ExceptionOccurred()); + env->ExceptionClear(); } if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_METHOD_ENTRY, thr))) { - return; + env->ExceptionDescribe(); + err.reset(env->ExceptionOccurred()); + env->ExceptionClear(); } if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_METHOD_EXIT, thr))) { - return; + env->ExceptionDescribe(); + err.reset(env->ExceptionOccurred()); + env->ExceptionClear(); } if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_SINGLE_STEP, thr))) { + env->ExceptionDescribe(); + err.reset(env->ExceptionOccurred()); + env->ExceptionClear(); + } + if (JvmtiErrorToException(env, jvmti_env, + jvmti_env->RawMonitorEnter(data->trace_mon))) { + return; + } + // Clear test_klass so we know this isn't being used + env->DeleteGlobalRef(data->test_klass); + data->test_klass = nullptr; + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->RawMonitorExit(data->trace_mon))) { return; } + if (err.get() != nullptr) { + env->Throw(err.get()); + } } } // namespace common_trace -- GitLab From 5cf8b5369d16249fcc58cf4a845ed0c430fd45eb Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Sun, 3 Dec 2017 12:46:17 -0800 Subject: [PATCH 129/226] Modify to use new BacktraceMap iterator. Bug: 69871050 Test: Ran host unit tests. Change-Id: I3c8c9a49a2c1cf0017502a869d1af0cb2a895ebd --- imgdiag/imgdiag.cc | 8 ++++---- runtime/dexopt_test.cc | 7 ++++--- runtime/mem_map.cc | 35 +++++++++++++++++++---------------- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc index 05fce96780..8aa638a0e0 100644 --- a/imgdiag/imgdiag.cc +++ b/imgdiag/imgdiag.cc @@ -1150,10 +1150,10 @@ class ImgDiagDumper { bool found_boot_map = false; // Find the memory map only for boot.art - for (const backtrace_map_t& map : *tmp_proc_maps) { - if (EndsWith(map.name, GetImageLocationBaseName())) { - if ((map.flags & PROT_WRITE) != 0) { - boot_map_ = map; + for (const backtrace_map_t* map : *tmp_proc_maps) { + if (EndsWith(map->name, GetImageLocationBaseName())) { + if ((map->flags & PROT_WRITE) != 0) { + boot_map_ = *map; found_boot_map = true; break; } diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc index 3c8243a6c5..d93d76793f 100644 --- a/runtime/dexopt_test.cc +++ b/runtime/dexopt_test.cc @@ -218,10 +218,11 @@ void DexoptTest::ReserveImageSpace() { std::unique_ptr map(BacktraceMap::Create(getpid(), true)); ASSERT_TRUE(map.get() != nullptr) << "Failed to build process map"; - for (BacktraceMap::const_iterator it = map->begin(); + for (BacktraceMap::iterator it = map->begin(); reservation_start < reservation_end && it != map->end(); ++it) { - ReserveImageSpaceChunk(reservation_start, std::min(it->start, reservation_end)); - reservation_start = std::max(reservation_start, it->end); + const backtrace_map_t* entry = *it; + ReserveImageSpaceChunk(reservation_start, std::min(entry->start, reservation_end)); + reservation_start = std::max(reservation_start, entry->end); } ReserveImageSpaceChunk(reservation_start, reservation_end); } diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 7f68d2faa0..f5d12d5f7b 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -59,14 +59,15 @@ static Maps* gMaps GUARDED_BY(MemMap::GetMemMapsLock()) = nullptr; static std::ostream& operator<<( std::ostream& os, - std::pair iters) { - for (BacktraceMap::const_iterator it = iters.first; it != iters.second; ++it) { + 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(it->start), - static_cast(it->end), - (it->flags & PROT_READ) ? 'r' : '-', - (it->flags & PROT_WRITE) ? 'w' : '-', - (it->flags & PROT_EXEC) ? 'x' : '-', it->name.c_str()); + 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; } @@ -170,9 +171,10 @@ bool MemMap::ContainedWithinExistingMap(uint8_t* ptr, size_t size, std::string* } ScopedBacktraceMapIteratorLock lock(map.get()); - for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) { - if ((begin >= it->start && begin < it->end) // start of new within old - && (end > it->start && end <= it->end)) { // end of new within old + 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; } } @@ -194,17 +196,18 @@ static bool CheckNonOverlapping(uintptr_t begin, return false; } ScopedBacktraceMapIteratorLock lock(map.get()); - for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) { - if ((begin >= it->start && begin < it->end) // start of new within old - || (end > it->start && end < it->end) // end of new within old - || (begin <= it->start && end > it->end)) { // start/end of new includes all of old + 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(it->start), static_cast(it->end), - it->name.c_str(), + static_cast(entry->start), static_cast(entry->end), + entry->name.c_str(), map_info.str().c_str()); return false; } -- GitLab From 6f1bd4611cfdb0387fb273f304a5008b07ba9145 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 6 Dec 2017 17:45:03 +0000 Subject: [PATCH 130/226] Extra debugging output for bug 64759619. Dump the class loader chain and try to look for the referenced class in class tables of that chain. Test: Modify code to trigger the crash, check output. Bug: 64759619 Change-Id: If3fe60a187ab3dfcc6acb61f5c1672706ae58870 --- runtime/class_linker.cc | 110 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 2 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index e5bb7862cb..b633972b3d 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -7838,6 +7838,106 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, return resolved.Ptr(); } +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) { + 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 << ")"; + } + } + } + } + + return oss.str(); +} + template ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t method_idx, @@ -7864,8 +7964,14 @@ ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, // We have a valid method from the DexCache but we need to perform ICCE and IAE checks. DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); klass = LookupResolvedType(dex_file, method_id.class_idx_, dex_cache.Get(), class_loader.Get()); - CHECK(klass != nullptr) << resolved->PrettyMethod() << " " << resolved << " " - << resolved->GetAccessFlags(); + if (UNLIKELY(klass == nullptr)) { + const char* descriptor = dex_file.StringByTypeIdx(method_id.class_idx_); + LOG(FATAL) << "Check failed: klass != nullptr Bug: 64759619 Method: " + << resolved->PrettyMethod() << ";" << resolved + << "/0x" << std::hex << resolved->GetAccessFlags() + << " ReferencedClass: " << descriptor + << " ClassLoader: " << DescribeLoaders(class_loader.Get(), descriptor); + } } else { // The method was not in the DexCache, resolve the declaring class. klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); -- GitLab From 83a57108f41ad3e03c570d1d84df6bbd0dd0a14e Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Wed, 6 Dec 2017 10:32:15 -0800 Subject: [PATCH 131/226] Merged a few more ARM64/MIPS64 cases. Rationale: Somehow overlooked this in last pass. Please let me know if you see others. Test: 660*-short? Change-Id: I702f21a504656a8a8e69619a28bcd1ba9f503990 --- .../660-checker-simd-sad-short2/src/Main.java | 67 +--------- .../660-checker-simd-sad-short3/src/Main.java | 114 ++---------------- 2 files changed, 16 insertions(+), 165 deletions(-) diff --git a/test/660-checker-simd-sad-short2/src/Main.java b/test/660-checker-simd-sad-short2/src/Main.java index 331f5ce690..274892d001 100644 --- a/test/660-checker-simd-sad-short2/src/Main.java +++ b/test/660-checker-simd-sad-short2/src/Main.java @@ -84,18 +84,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadCastedChar2Int(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 8 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadCastedChar2Int(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadCastedChar2Int(char[], char[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> VecSetScalars [<>] loop:none @@ -145,18 +134,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadCastedChar2IntAlt(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 8 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadCastedChar2IntAlt(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadCastedChar2IntAlt(char[], char[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> VecSetScalars [<>] loop:none @@ -208,18 +186,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadCastedChar2IntAlt2(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 8 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadCastedChar2IntAlt2(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadCastedChar2IntAlt2(char[], char[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> VecSetScalars [<>] loop:none @@ -276,19 +243,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadCastedChar2Long(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 8 loop:none - /// CHECK-DAG: <> LongConstant 0 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadCastedChar2Long(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadCastedChar2Long(char[], char[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> LongConstant 0 loop:none @@ -344,19 +299,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadCastedChar2LongAt1(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 8 loop:none - /// CHECK-DAG: <> LongConstant 1 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: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: long Main.sadCastedChar2LongAt1(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadCastedChar2LongAt1(char[], char[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> LongConstant 1 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 ecda8848c3..5016b658e5 100644 --- a/test/660-checker-simd-sad-short3/src/Main.java +++ b/test/660-checker-simd-sad-short3/src/Main.java @@ -33,19 +33,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntParamRight(short[], short) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 8 loop:none - /// CHECK-DAG: <> ParameterValue 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 - // - /// CHECK-START-MIPS64: int Main.sadShort2IntParamRight(short[], short) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntParamRight(short[], short) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> ParameterValue loop:none @@ -76,19 +64,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntParamLeft(short[], short) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 8 loop:none - /// CHECK-DAG: <> ParameterValue 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 - // - /// CHECK-START-MIPS64: int Main.sadShort2IntParamLeft(short[], short) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntParamLeft(short[], short) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> ParameterValue loop:none @@ -119,19 +95,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntConstRight(short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 8 loop:none - /// CHECK-DAG: <> IntConstant 32767 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 - // - /// CHECK-START-MIPS64: int Main.sadShort2IntConstRight(short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstRight(short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> IntConstant 32767 loop:none @@ -162,19 +126,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntConstLeft(short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 8 loop:none - /// CHECK-DAG: <> IntConstant 32767 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 - // - /// CHECK-START-MIPS64: int Main.sadShort2IntConstLeft(short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstLeft(short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> IntConstant 32767 loop:none @@ -205,19 +157,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntInvariantRight(short[], int) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 8 loop:none - /// CHECK-DAG: <> TypeConversion [{{i\d+}}] 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 - // - /// CHECK-START-MIPS64: int Main.sadShort2IntInvariantRight(short[], int) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntInvariantRight(short[], int) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> TypeConversion [{{i\d+}}] loop:none @@ -249,18 +189,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntInvariantLeft(short[], int) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 8 loop:none - /// CHECK-DAG: <> TypeConversion [{{i\d+}}] 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-START-MIPS64: int Main.sadShort2IntInvariantLeft(short[], int) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntInvariantLeft(short[], int) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> TypeConversion [{{i\d+}}] loop:none @@ -270,6 +199,7 @@ public class Main { /// 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 sadShort2IntInvariantLeft(short[] s, int val) { int sad = 0; short x = (short) (val + 1); @@ -293,19 +223,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntCastedExprRight(short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 8 loop:none - /// CHECK-DAG: <> IntConstant 110 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: <> VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadShort2IntCastedExprRight(short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntCastedExprRight(short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> IntConstant 110 loop:none @@ -316,6 +234,7 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecAdd [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static int sadShort2IntCastedExprRight(short[] s) { int sad = 0; for (int i = 0; i < s.length; i++) { @@ -339,19 +258,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntCastedExprLeft(short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 8 loop:none - /// CHECK-DAG: <> IntConstant 110 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: <> VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: int Main.sadShort2IntCastedExprLeft(short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntCastedExprLeft(short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> IntConstant 110 loop:none @@ -362,6 +269,7 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecAdd [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static int sadShort2IntCastedExprLeft(short[] s) { int sad = 0; for (int i = 0; i < s.length; i++) { -- GitLab From 52f205a38bda70d5c63907ef354a1475b4237b21 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Fri, 1 Dec 2017 12:16:07 -0800 Subject: [PATCH 132/226] ART: Remove old aget on null workaround Use null for an aget-object of null. Ensure that other aget types on null cannot be converted to or used as a reference type. Let the verifier continue scanning after an aget on a null register, to ensure that the dead code is type-safe. Add test coverage for the new behavior. Partially reverts commit 4824c27988c8eeb302791624bb3ce1d557b0db6c Partially reverts commit 857f058d4b7bd07c5c99eda416ad91516a10b4da Bug: 22059710 Bug: 64683522 Bug: 69669661 Test: m test-art-host Change-Id: Ie0b554e8f880251d8e73ab6dfb6b41a5e63defc6 --- runtime/verifier/method_verifier.cc | 17 ++++-- test/518-null-array-get/expected.txt | 6 ++ test/518-null-array-get/info.txt | 12 +++- .../smali/NullArrayFailInt2Object.smali} | 23 +++++--- ...ay.smali => NullArrayFailObject2Int.smali} | 4 +- .../smali/NullArraySuccessInt.smali | 33 +++++++++++ .../smali/NullArraySuccessInt2Float.smali | 33 +++++++++++ .../smali/NullArraySuccessRef.smali | 33 +++++++++++ .../smali/NullArraySuccessShort.smali | 33 +++++++++++ test/518-null-array-get/src/Main.java | 36 +++++++++--- test/706-jit-skip-compilation/expected.txt | 1 - test/706-jit-skip-compilation/info.txt | 4 -- .../smali/errclass.smali | 34 ----------- test/706-jit-skip-compilation/src/Main.java | 57 ------------------- 14 files changed, 206 insertions(+), 120 deletions(-) rename test/{706-jit-skip-compilation/run => 518-null-array-get/smali/NullArrayFailInt2Object.smali} (52%) rename test/518-null-array-get/smali/{NullArray.smali => NullArrayFailObject2Int.smali} (86%) create mode 100644 test/518-null-array-get/smali/NullArraySuccessInt.smali create mode 100644 test/518-null-array-get/smali/NullArraySuccessInt2Float.smali create mode 100644 test/518-null-array-get/smali/NullArraySuccessRef.smali create mode 100644 test/518-null-array-get/smali/NullArraySuccessShort.smali delete mode 100644 test/706-jit-skip-compilation/expected.txt delete mode 100644 test/706-jit-skip-compilation/info.txt delete mode 100644 test/706-jit-skip-compilation/smali/errclass.smali delete mode 100644 test/706-jit-skip-compilation/src/Main.java diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index be58a57c24..fefb4f6526 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -4691,12 +4691,19 @@ void MethodVerifier::VerifyAGet(const Instruction* inst, } else { const RegType& array_type = work_line_->GetRegisterType(this, inst->VRegB_23x()); if (array_type.IsZeroOrNull()) { - have_pending_runtime_throw_failure_ = true; // Null array class; this code path will fail at runtime. Infer a merge-able type from the - // instruction type. TODO: have a proper notion of bottom here. - if (!is_primitive || insn_type.IsCategory1Types()) { - // Reference or category 1 - work_line_->SetRegisterType(this, inst->VRegA_23x(), reg_types_.Zero()); + // instruction type. + if (!is_primitive) { + work_line_->SetRegisterType(this, inst->VRegA_23x(), reg_types_.Null()); + } else if (insn_type.IsInteger()) { + // Pick a non-zero constant (to distinguish with null) that can fit in any primitive. + // We cannot use 'insn_type' as it could be a float array or an int array. + work_line_->SetRegisterType( + this, inst->VRegA_23x(), DetermineCat1Constant(1, need_precise_constants_)); + } else if (insn_type.IsCategory1Types()) { + // Category 1 + // The 'insn_type' is exactly the type we need. + work_line_->SetRegisterType(this, inst->VRegA_23x(), insn_type); } else { // Category 2 work_line_->SetRegisterTypeWide(this, inst->VRegA_23x(), diff --git a/test/518-null-array-get/expected.txt b/test/518-null-array-get/expected.txt index e69de29bb2..ae5318e53d 100644 --- a/test/518-null-array-get/expected.txt +++ b/test/518-null-array-get/expected.txt @@ -0,0 +1,6 @@ +NullArrayFailInt2Object +NullArrayFailObject2Int +NullArraySuccessInt +NullArraySuccessInt2Float +NullArraySuccessShort +NullArraySuccessRef diff --git a/test/518-null-array-get/info.txt b/test/518-null-array-get/info.txt index 407f590b2b..71e0332e62 100644 --- a/test/518-null-array-get/info.txt +++ b/test/518-null-array-get/info.txt @@ -1,3 +1,9 @@ -Regression test for Quick and Optimizing that used -to crash on an aget-object + int-to-byte sequence -(accepted by the verifier in the case the array was null). +Codifies that the verifier should reject type-unsafe +instructions in dead code after aget on null, but pass +type-safe dead code. + +Previously verification stopped after aget on null and +punted the method to the interpreter in an effort to avoid +compiler crashes. As broken code appears very uncommon, +ensure verifier strictness and help the compilers see more +code. diff --git a/test/706-jit-skip-compilation/run b/test/518-null-array-get/smali/NullArrayFailInt2Object.smali similarity index 52% rename from test/706-jit-skip-compilation/run rename to test/518-null-array-get/smali/NullArrayFailInt2Object.smali index 6c5720a099..ca4ed10660 100644 --- a/test/706-jit-skip-compilation/run +++ b/test/518-null-array-get/smali/NullArrayFailInt2Object.smali @@ -1,12 +1,10 @@ -#!/bin/bash -# -# Copyright (C) 2016 The Android Open Source Project +# 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 +# http://www.apache.org/licenses/LICENSE-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,6 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Run without the app image, otherwise the verification results will be cached -# in the ArtMethod of the image and the test will be skewed. -exec ${RUN} "${@}" --no-app-image +# Check that the result of aget on null cannot be used as a reference. + +.class public LNullArrayFailInt2Object; + +.super Ljava/lang/Object; + +.method public static method()V + .registers 2 + const/4 v0, 0 + const/4 v1, 0 + aget v0, v0, v1 + invoke-virtual { v0 }, Ljava/lang/Object;->toString()Ljava/lang/String; + return-void +.end method diff --git a/test/518-null-array-get/smali/NullArray.smali b/test/518-null-array-get/smali/NullArrayFailObject2Int.smali similarity index 86% rename from test/518-null-array-get/smali/NullArray.smali rename to test/518-null-array-get/smali/NullArrayFailObject2Int.smali index 52abc38473..83823a24e5 100644 --- a/test/518-null-array-get/smali/NullArray.smali +++ b/test/518-null-array-get/smali/NullArrayFailObject2Int.smali @@ -12,7 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -.class public LNullArray; +# Check that the result of aget-object on null cannot be used as an integral. + +.class public LNullArrayFailObject2Int; .super Ljava/lang/Object; diff --git a/test/518-null-array-get/smali/NullArraySuccessInt.smali b/test/518-null-array-get/smali/NullArraySuccessInt.smali new file mode 100644 index 0000000000..01cf1c92ab --- /dev/null +++ b/test/518-null-array-get/smali/NullArraySuccessInt.smali @@ -0,0 +1,33 @@ +# 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. + +# Check that the result of aget on null can be used as an int. + +.class public LNullArraySuccessInt; + +.super Ljava/lang/Object; + +.method public static intMethod(I)V + .registers 1 + return-void +.end method + +.method public static method()V + .registers 2 + const/4 v0, 0 + const/4 v1, 0 + aget v0, v0, v1 + invoke-static { v0 }, LNullArraySuccessInt;->intMethod(I)V + return-void +.end method diff --git a/test/518-null-array-get/smali/NullArraySuccessInt2Float.smali b/test/518-null-array-get/smali/NullArraySuccessInt2Float.smali new file mode 100644 index 0000000000..bd59d5f68e --- /dev/null +++ b/test/518-null-array-get/smali/NullArraySuccessInt2Float.smali @@ -0,0 +1,33 @@ +# 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. + +# Check that the result of aget on null can be used as a float. + +.class public LNullArraySuccessInt2Float; + +.super Ljava/lang/Object; + +.method public static floatMethod(F)V + .registers 1 + return-void +.end method + +.method public static method()V + .registers 2 + const/4 v0, 0 + const/4 v1, 0 + aget v0, v0, v1 + invoke-static { v0 }, LNullArraySuccessInt2Float;->floatMethod(F)V + return-void +.end method diff --git a/test/518-null-array-get/smali/NullArraySuccessRef.smali b/test/518-null-array-get/smali/NullArraySuccessRef.smali new file mode 100644 index 0000000000..2f512d4089 --- /dev/null +++ b/test/518-null-array-get/smali/NullArraySuccessRef.smali @@ -0,0 +1,33 @@ +# 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. + +# Check that the result of aget-object on null can be used as a reference. + +.class public LNullArraySuccessRef; + +.super Ljava/lang/Object; + +.method public voidMethod()V + .registers 1 + return-void +.end method + +.method public static method()V + .registers 2 + const/4 v0, 0 + const/4 v1, 0 + aget-object v0, v0, v1 + invoke-virtual { v0 }, LNullArraySuccessRef;->voidMethod()V + return-void +.end method diff --git a/test/518-null-array-get/smali/NullArraySuccessShort.smali b/test/518-null-array-get/smali/NullArraySuccessShort.smali new file mode 100644 index 0000000000..d332e51f52 --- /dev/null +++ b/test/518-null-array-get/smali/NullArraySuccessShort.smali @@ -0,0 +1,33 @@ +# 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. + +# Check that the result of aget-short on null can be used as a short. + +.class public LNullArraySuccessShort; + +.super Ljava/lang/Object; + +.method public static shortMethod(S)V + .registers 1 + return-void +.end method + +.method public static method()V + .registers 2 + const/4 v0, 0 + const/4 v1, 0 + aget-short v0, v0, v1 + invoke-static { v0 }, LNullArraySuccessShort;->shortMethod(S)V + return-void +.end method diff --git a/test/518-null-array-get/src/Main.java b/test/518-null-array-get/src/Main.java index 66e50aacd7..678aef1f43 100644 --- a/test/518-null-array-get/src/Main.java +++ b/test/518-null-array-get/src/Main.java @@ -22,16 +22,36 @@ public class Main { class InnerClass {} public static void main(String[] args) throws Exception { - Class c = Class.forName("NullArray"); - Method m = c.getMethod("method"); - Object[] arguments = { }; + checkLoad("NullArrayFailInt2Object", true); + checkLoad("NullArrayFailObject2Int", true); + checkLoad("NullArraySuccessInt", false); + checkLoad("NullArraySuccessInt2Float", false); + checkLoad("NullArraySuccessShort", false); + checkLoad("NullArraySuccessRef", false); + } + + private static void checkLoad(String className, boolean expectError) throws Exception { + Class c; try { - m.invoke(null, arguments); - throw new Error("Expected an InvocationTargetException"); - } catch (InvocationTargetException e) { - if (!(e.getCause() instanceof NullPointerException)) { - throw new Error("Expected a NullPointerException"); + c = Class.forName(className); + if (expectError) { + throw new RuntimeException("Expected error for " + className); + } + Method m = c.getMethod("method"); + try { + m.invoke(null); + throw new RuntimeException("Expected an InvocationTargetException"); + } catch (InvocationTargetException e) { + if (!(e.getCause() instanceof NullPointerException)) { + throw new RuntimeException("Expected a NullPointerException"); + } + System.out.println(className); + } + } catch (VerifyError e) { + if (!expectError) { + throw new RuntimeException(e); } + System.out.println(className); } } } diff --git a/test/706-jit-skip-compilation/expected.txt b/test/706-jit-skip-compilation/expected.txt deleted file mode 100644 index 6a5618ebc6..0000000000 --- a/test/706-jit-skip-compilation/expected.txt +++ /dev/null @@ -1 +0,0 @@ -JNI_OnLoad called diff --git a/test/706-jit-skip-compilation/info.txt b/test/706-jit-skip-compilation/info.txt deleted file mode 100644 index e9ef86bfb3..0000000000 --- a/test/706-jit-skip-compilation/info.txt +++ /dev/null @@ -1,4 +0,0 @@ -Regression test for the JIT crashing when compiling a method with invalid -dead dex code. For not compilable methods we don't gather samples and we don't -trigger JIT compilation. However kAccDontBotherCompile is not persisted in the -oat file and so we may end up compiling a method which we shouldn't. diff --git a/test/706-jit-skip-compilation/smali/errclass.smali b/test/706-jit-skip-compilation/smali/errclass.smali deleted file mode 100644 index 410504cb2f..0000000000 --- a/test/706-jit-skip-compilation/smali/errclass.smali +++ /dev/null @@ -1,34 +0,0 @@ -# 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 LErrClass; - -.super Ljava/lang/Object; - -.method public static errMethod()J - .registers 8 - const/4 v0, 0x0 - const/4 v3, 0x0 - aget v1, v0, v3 # v0 is null, this will alays throw and the invalid code - # below will not be verified. - move v3, v4 - move-wide/from16 v6, v2 # should trigger a verification error if verified as - # v3 is a single register but used as a pair here. - return v6 -.end method - -# Add a field to work around demerger bug b/18051191. -# Failure to verify dex file '...': Offset(552) should be zero when size is zero for field-ids. -.field private a:I diff --git a/test/706-jit-skip-compilation/src/Main.java b/test/706-jit-skip-compilation/src/Main.java deleted file mode 100644 index aa847248d6..0000000000 --- a/test/706-jit-skip-compilation/src/Main.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.InvocationTargetException; -import java.lang.reflect.Method; - -public class Main { - public static void main(String[] args) throws Exception { - System.loadLibrary(args[0]); - Class c = Class.forName("ErrClass"); - Method m = c.getMethod("errMethod"); - - // Print the counter before invokes. The golden file expects this to be 0. - int hotnessCounter = getHotnessCounter(c, "errMethod"); - if (hotnessCounter != 0) { - throw new RuntimeException("Unexpected hotnessCounter: " + hotnessCounter); - } - - // Loop enough to make sure the interpreter reports invocations count. - long result = 0; - for (int i = 0; i < 10000; i++) { - try { - result += (Long)m.invoke(null); - hotnessCounter = getHotnessCounter(c, "errMethod"); - if (hotnessCounter != 0) { - throw new RuntimeException( - "Unexpected hotnessCounter: " + hotnessCounter); - } - - } catch (InvocationTargetException e) { - if (!(e.getCause() instanceof NullPointerException)) { - throw e; - } - } - } - - // Not compilable methods should not increase their hotness counter. - if (hotnessCounter != 0) { - throw new RuntimeException("Unexpected hotnessCounter: " + hotnessCounter); - } - } - - public static native int getHotnessCounter(Class cls, String method_name); -} -- GitLab From 94539fde235a6dcd25af2560bb1fed5306f9f26d Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 15 Nov 2017 17:52:46 +0000 Subject: [PATCH 133/226] Fix typing bug in load store elimination Test: 530-checker-lse3 Bug: 69365271 Change-Id: I2289bed5fc7b84ee913a2015d113098777dabd4c --- compiler/optimizing/load_store_elimination.cc | 91 +++++++++++++++++-- test/530-checker-lse3/expected.txt | 0 test/530-checker-lse3/info.txt | 4 + test/530-checker-lse3/smali/StoreLoad.smali | 62 +++++++++++++ test/530-checker-lse3/src/Main.java | 48 ++++++++++ 5 files changed, 199 insertions(+), 6 deletions(-) create mode 100644 test/530-checker-lse3/expected.txt create mode 100644 test/530-checker-lse3/info.txt create mode 100644 test/530-checker-lse3/smali/StoreLoad.smali create mode 100644 test/530-checker-lse3/src/Main.java diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index f03fffabe2..88326d321b 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -74,6 +74,20 @@ class LSEVisitor : public HGraphDelegateVisitor { HGraphVisitor::VisitBasicBlock(block); } + HTypeConversion* AddTypeConversionIfNecessary(HInstruction* instruction, + HInstruction* value, + DataType::Type expected_type) { + HTypeConversion* type_conversion = nullptr; + // Should never add type conversion into boolean value. + if (expected_type != DataType::Type::kBool && + !DataType::IsTypeConversionImplicit(value->GetType(), expected_type)) { + type_conversion = new (GetGraph()->GetAllocator()) HTypeConversion( + expected_type, value, instruction->GetDexPc()); + instruction->GetBlock()->InsertInstructionBefore(type_conversion, instruction); + } + return type_conversion; + } + // Find an instruction's substitute if it should be removed. // Return the same instruction if it should not be removed. HInstruction* FindSubstitute(HInstruction* instruction) { @@ -86,13 +100,59 @@ class LSEVisitor : public HGraphDelegateVisitor { return instruction; } + void AddRemovedLoad(HInstruction* load, HInstruction* heap_value) { + DCHECK_EQ(FindSubstitute(heap_value), heap_value) << + "Unexpected heap_value that has a substitute " << heap_value->DebugName(); + removed_loads_.push_back(load); + substitute_instructions_for_loads_.push_back(heap_value); + } + + // 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`. + void TryToReuseTypeConversion(HInstruction* type_conversion, size_t index) { + size_t size = removed_loads_.size(); + HInstruction* load = removed_loads_[index]; + HInstruction* substitute = substitute_instructions_for_loads_[index]; + for (size_t j = index + 1; j < size; j++) { + HInstruction* load2 = removed_loads_[j]; + HInstruction* substitute2 = substitute_instructions_for_loads_[j]; + if (load2 == nullptr) { + DCHECK(substitute2->IsTypeConversion()); + continue; + } + DCHECK(load2->IsInstanceFieldGet() || + load2->IsStaticFieldGet() || + load2->IsArrayGet()); + DCHECK(substitute2 != nullptr); + if (substitute2 == substitute && + load2->GetType() == load->GetType() && + type_conversion->GetBlock()->Dominates(load2->GetBlock()) && + // Don't share across irreducible loop headers. + // TODO: can be more fine-grained than this by testing each dominator. + (load2->GetBlock() == type_conversion->GetBlock() || + !GetGraph()->HasIrreducibleLoops())) { + // The removed_loads_ are added in reverse post order. + DCHECK(type_conversion->StrictlyDominates(load2)); + load2->ReplaceWith(type_conversion); + load2->GetBlock()->RemoveInstruction(load2); + removed_loads_[j] = nullptr; + substitute_instructions_for_loads_[j] = type_conversion; + } + } + } + // Remove recorded instructions that should be eliminated. void RemoveInstructions() { size_t size = removed_loads_.size(); DCHECK_EQ(size, substitute_instructions_for_loads_.size()); for (size_t i = 0; i < size; i++) { HInstruction* load = removed_loads_[i]; - DCHECK(load != nullptr); + if (load == nullptr) { + // The load has been handled in the scan for type conversion below. + DCHECK(substitute_instructions_for_loads_[i]->IsTypeConversion()); + continue; + } DCHECK(load->IsInstanceFieldGet() || load->IsStaticFieldGet() || load->IsArrayGet()); @@ -102,7 +162,28 @@ class LSEVisitor : public HGraphDelegateVisitor { // a load that has a substitute should not be observed as a heap // location value. DCHECK_EQ(FindSubstitute(substitute), substitute); - load->ReplaceWith(substitute); + + // The load expects to load the heap value as type load->GetType(). + // However the tracked heap value may not be of that type. An explicit + // type conversion may be needed. + // There are actually three types involved here: + // (1) tracked heap value's type (type A) + // (2) heap location (field or element)'s type (type B) + // (3) load's type (type C) + // We guarantee that type A stored as type B and then fetched out as + // type C is the same as casting from type A to type C directly, since + // type B and type C will have the same size which is guarenteed in + // HInstanceFieldGet/HStaticFieldGet/HArrayGet's SetType(). + // So we only need one type conversion from type A to type C. + HTypeConversion* type_conversion = AddTypeConversionIfNecessary( + load, substitute, load->GetType()); + if (type_conversion != nullptr) { + TryToReuseTypeConversion(type_conversion, i); + load->ReplaceWith(type_conversion); + substitute_instructions_for_loads_[i] = type_conversion; + } else { + load->ReplaceWith(substitute); + } load->GetBlock()->RemoveInstruction(load); } @@ -338,8 +419,7 @@ class LSEVisitor : public HGraphDelegateVisitor { HInstruction* heap_value = heap_values[idx]; if (heap_value == kDefaultHeapValue) { HInstruction* constant = GetDefaultValue(instruction->GetType()); - removed_loads_.push_back(instruction); - substitute_instructions_for_loads_.push_back(constant); + AddRemovedLoad(instruction, constant); heap_values[idx] = constant; return; } @@ -374,8 +454,7 @@ class LSEVisitor : public HGraphDelegateVisitor { } return; } - removed_loads_.push_back(instruction); - substitute_instructions_for_loads_.push_back(heap_value); + AddRemovedLoad(instruction, heap_value); TryRemovingNullCheck(instruction); } } diff --git a/test/530-checker-lse3/expected.txt b/test/530-checker-lse3/expected.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/530-checker-lse3/info.txt b/test/530-checker-lse3/info.txt new file mode 100644 index 0000000000..29b4cb82ab --- /dev/null +++ b/test/530-checker-lse3/info.txt @@ -0,0 +1,4 @@ +Regression test for load store elimination not respecting the loaded type. When +a wider value is stored in a narrower field and then loaded from that field, +LSE needs to replace the value to be stored with a type conversion to the +narrower type. diff --git a/test/530-checker-lse3/smali/StoreLoad.smali b/test/530-checker-lse3/smali/StoreLoad.smali new file mode 100644 index 0000000000..7fb582c8d1 --- /dev/null +++ b/test/530-checker-lse3/smali/StoreLoad.smali @@ -0,0 +1,62 @@ +# 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. + +.class public LStoreLoad; + +.super Ljava/lang/Object; + +## CHECK-START: int StoreLoad.test(int) load_store_elimination (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: StaticFieldSet [{{l\d+}},<>] field_name:StoreLoad.byteField +## CHECK-DAG: StaticFieldSet [{{l\d+}},<>] field_name:StoreLoad.byteField2 +## CHECK-DAG: <> StaticFieldGet [{{l\d+}}] field_name:StoreLoad.byteField +## CHECK-DAG: <> StaticFieldGet [{{l\d+}}] field_name:StoreLoad.byteField2 +## CHECK-DAG: <> Add [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int StoreLoad.test(int) load_store_elimination (after) +## CHECK-NOT: StaticFieldGet + +## CHECK-START: int StoreLoad.test(int) load_store_elimination (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: StaticFieldSet [{{l\d+}},<>] field_name:StoreLoad.byteField +## CHECK-DAG: StaticFieldSet [{{l\d+}},<>] field_name:StoreLoad.byteField2 +## CHECK-DAG: <> TypeConversion [<>] +## CHECK-DAG: <> Add [<>,<>] +## CHECK-DAG: Return [<>] +.method public static test(I)I + .registers 2 + sput-byte v1, LStoreLoad;->byteField:B + sput-byte v1, LStoreLoad;->byteField2:B + sget-byte v0, LStoreLoad;->byteField:B + sget-byte v1, LStoreLoad;->byteField2:B + add-int/2addr v0, v1 + return v0 +.end method + +## CHECK-START: int StoreLoad.test2(int) load_store_elimination (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: StaticFieldSet [{{l\d+}},<>] field_name:StoreLoad.byteField +## CHECK-DAG: Return [<>] + +## CHECK-START: int StoreLoad.test2(int) load_store_elimination (after) +## CHECK-NOT: TypeConversion +.method public static test2(I)I + .registers 1 + sput-byte v0, LStoreLoad;->byteField:B + return v0 +.end method + +.field public static byteField:B +.field public static byteField2:B diff --git a/test/530-checker-lse3/src/Main.java b/test/530-checker-lse3/src/Main.java new file mode 100644 index 0000000000..caef0b33cc --- /dev/null +++ b/test/530-checker-lse3/src/Main.java @@ -0,0 +1,48 @@ +/* + * 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.Method; +import java.lang.reflect.Field; + +public class Main { + + // Workaround for b/18051191. + class InnerClass {} + + public static void main(String[] args) throws Exception { + Class c = Class.forName("StoreLoad"); + Method m = c.getMethod("test", int.class); + int result = (Integer)m.invoke(null, 0x12345678); + if (result != (0x78 + 0x78)) { + throw new Error("Expected 240, got " + result); + } + m = c.getMethod("test2", int.class); + result = (Integer)m.invoke(null, 0xdeadbeef); + if (result != 0xdeadbeef) { + throw new Error("Expected 0xdeadbeef, got " + result); + } + Field f = c.getDeclaredField("byteField"); + byte b = f.getByte(null); + if (b != (byte)0xef) { + throw new Error("Expected 0xef, got " + b); + } + f = c.getDeclaredField("byteField2"); + b = f.getByte(null); + if (b != (byte)0x78) { + throw new Error("Expected 0xef, got " + b); + } + } +} -- GitLab From 92d77208d7434f2c8c49aa368769165c4d33896f Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 6 Dec 2017 20:49:00 -0800 Subject: [PATCH 134/226] ART: Make kDebugVerifier a verbose flag To aid in debugging efforts, finally introduce a verifier-debug verbose-logging flag that takes over the compile-time kDebugVerifier flag. Collecting verification times from a well-known large app during dex2oat via dump-timings shows a difference in the noise. Logging of the steps is restricted to failure cases, to avoid spam. Test: m Test: m test-art-host Test: manual: dex2oat --runtime-arg -verbose:verifier-debug APK with failures Change-Id: Icb998cc42a531b80f20f986625869505271b7c98 --- cmdline/cmdline_parser_test.cc | 3 ++- cmdline/cmdline_types.h | 2 ++ openjdkjvmti/OpenjdkJvmTi.cc | 1 + runtime/base/logging.h | 1 + runtime/verifier/method_verifier.cc | 16 +++++++++------- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index 1536339515..c438c54cea 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -244,7 +244,7 @@ TEST_F(CmdlineParserTest, TestLogVerbosity) { { const char* log_args = "-verbose:" "class,compiler,gc,heap,jdwp,jni,monitor,profiler,signals,simulator,startup," - "third-party-jni,threads,verifier"; + "third-party-jni,threads,verifier,verifier-debug"; LogVerbosity log_verbosity = LogVerbosity(); log_verbosity.class_linker = true; @@ -261,6 +261,7 @@ TEST_F(CmdlineParserTest, TestLogVerbosity) { log_verbosity.third_party_jni = true; log_verbosity.threads = true; log_verbosity.verifier = true; + log_verbosity.verifier_debug = true; EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose); } diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index 37bdcdc5e2..f12ef971af 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -669,6 +669,8 @@ struct CmdlineType : CmdlineTypeParser { log_verbosity.threads = true; } else if (verbose_options[j] == "verifier") { log_verbosity.verifier = true; + } else if (verbose_options[j] == "verifier-debug") { + log_verbosity.verifier_debug = true; } else if (verbose_options[j] == "image") { log_verbosity.image = true; } else if (verbose_options[j] == "systrace-locks") { diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc index 62f723dec1..6b2d5d6a5c 100644 --- a/openjdkjvmti/OpenjdkJvmTi.cc +++ b/openjdkjvmti/OpenjdkJvmTi.cc @@ -1437,6 +1437,7 @@ class JvmtiFunctions { art::gLogVerbosity.third_party_jni = val; art::gLogVerbosity.threads = val; art::gLogVerbosity.verifier = val; + // Do not set verifier-debug. art::gLogVerbosity.image = val; // Note: can't switch systrace_lock_logging. That requires changing entrypoints. diff --git a/runtime/base/logging.h b/runtime/base/logging.h index 15f935395e..5703b3c746 100644 --- a/runtime/base/logging.h +++ b/runtime/base/logging.h @@ -53,6 +53,7 @@ struct LogVerbosity { bool third_party_jni; // Enabled with "-verbose:third-party-jni". bool threads; bool verifier; + bool verifier_debug; // Only works in debug builds. bool image; bool systrace_lock_logging; // Enabled with "-verbose:sys-locks". bool agents; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index fefb4f6526..f1af2529bd 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -61,8 +61,6 @@ namespace verifier { using android::base::StringPrintf; static constexpr bool kTimeVerifyMethod = !kIsDebugBuild; -static constexpr bool kDebugVerify = false; -// TODO: Add a constant to method_verifier to turn on verbose logging? // On VLOG(verifier), should we dump the whole state when we run into a hard failure? static constexpr bool kDumpRegLinesOnHardFailureIfVLOG = true; @@ -408,6 +406,10 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, verifier.DumpFailures(VLOG_STREAM(verifier) << "Soft verification failures in " << dex_file->PrettyMethod(method_idx) << "\n"); } + if (VLOG_IS_ON(verifier_debug)) { + std::cout << "\n" << verifier.info_messages_.str(); + verifier.Dump(std::cout); + } result.kind = FailureKind::kSoftFailure; if (method != nullptr && !CanCompilerHandleVerificationFailure(verifier.encountered_failure_types_)) { @@ -481,7 +483,7 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, callbacks->ClassRejected(ref); } } - if (VLOG_IS_ON(verifier)) { + if (VLOG_IS_ON(verifier) || VLOG_IS_ON(verifier_debug)) { std::cout << "\n" << verifier.info_messages_.str(); verifier.Dump(std::cout); } @@ -1935,7 +1937,7 @@ bool MethodVerifier::CodeFlowVerifyMethod() { GetInstructionFlags(insn_idx).ClearChanged(); } - if (kDebugVerify) { + if (UNLIKELY(VLOG_IS_ON(verifier_debug))) { /* * Scan for dead code. There's nothing "evil" about dead code * (besides the wasted space), but it indicates a flaw somewhere @@ -2079,7 +2081,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { int32_t branch_target = 0; bool just_set_result = false; - if (kDebugVerify) { + if (UNLIKELY(VLOG_IS_ON(verifier_debug))) { // Generate processing back trace to debug verifier LogVerifyInfo() << "Processing " << inst->DumpString(dex_file_) << "\n" << work_line_->Dump(this) << "\n"; @@ -5350,7 +5352,7 @@ bool MethodVerifier::UpdateRegisters(uint32_t next_insn, RegisterLine* merge_lin } } else { RegisterLineArenaUniquePtr copy; - if (kDebugVerify) { + if (UNLIKELY(VLOG_IS_ON(verifier_debug))) { copy.reset(RegisterLine::Create(target_line->NumRegs(), this)); copy->CopyFromLine(target_line); } @@ -5358,7 +5360,7 @@ bool MethodVerifier::UpdateRegisters(uint32_t next_insn, RegisterLine* merge_lin if (have_pending_hard_failure_) { return false; } - if (kDebugVerify && changed) { + if (UNLIKELY(VLOG_IS_ON(verifier_debug)) && changed) { LogVerifyInfo() << "Merging at [" << reinterpret_cast(work_insn_idx_) << "]" << " to [" << reinterpret_cast(next_insn) << "]: " << "\n" << copy->Dump(this) << " MERGE\n" -- GitLab From 3bcb751d0d2fa440809437a616e9326388600c8e Mon Sep 17 00:00:00 2001 From: Mingyao Yang Date: Thu, 16 Nov 2017 15:40:46 -0800 Subject: [PATCH 135/226] type conversion elimination for store value only uses. Some type conversions can be eliminated if the converted values are used as store values only. Code generation can handle the conversion implicitly. Test: run-test on host. 711-checker-type-conversion. Change-Id: I60a402cf4610683acc5ca2ceba19045b4c17b843 --- compiler/optimizing/instruction_simplifier.cc | 59 ++++++ .../711-checker-type-conversion/src/Main.java | 187 ++++++++++++++++++ 2 files changed, 246 insertions(+) diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index a65d7b1984..a42a85dc1d 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -1081,6 +1081,58 @@ static inline bool TryReplaceFieldOrArrayGetType(HInstruction* maybe_get, DataTy } } +// The type conversion is only used for storing into a field/element of the +// same/narrower size. +static bool IsTypeConversionForStoringIntoNoWiderFieldOnly(HTypeConversion* type_conversion) { + if (type_conversion->HasEnvironmentUses()) { + return false; + } + DataType::Type input_type = type_conversion->GetInputType(); + DataType::Type result_type = type_conversion->GetResultType(); + if (!DataType::IsIntegralType(input_type) || + !DataType::IsIntegralType(result_type) || + input_type == DataType::Type::kInt64 || + result_type == DataType::Type::kInt64) { + // Type conversion is needed if non-integer types are involved, or 64-bit + // types are involved, which may use different number of registers. + return false; + } + if (DataType::Size(input_type) >= DataType::Size(result_type)) { + // Type conversion is not necessary when storing to a field/element of the + // same/smaller size. + } else { + // We do not handle this case here. + return false; + } + + // Check if the converted value is only used for storing into heap. + for (const HUseListNode& use : type_conversion->GetUses()) { + HInstruction* instruction = use.GetUser(); + if (instruction->IsInstanceFieldSet() && + instruction->AsInstanceFieldSet()->GetFieldType() == result_type) { + DCHECK_EQ(instruction->AsInstanceFieldSet()->GetValue(), type_conversion); + continue; + } + if (instruction->IsStaticFieldSet() && + instruction->AsStaticFieldSet()->GetFieldType() == result_type) { + DCHECK_EQ(instruction->AsStaticFieldSet()->GetValue(), type_conversion); + continue; + } + if (instruction->IsArraySet() && + instruction->AsArraySet()->GetComponentType() == result_type && + // not index use. + instruction->AsArraySet()->GetIndex() != type_conversion) { + DCHECK_EQ(instruction->AsArraySet()->GetValue(), type_conversion); + continue; + } + // The use is not as a store value, or the field/element type is not the + // same as the result_type, keep the type conversion. + return false; + } + // Codegen automatically handles the type conversion during the store. + return true; +} + void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruction) { HInstruction* input = instruction->GetInput(); DataType::Type input_type = input->GetType(); @@ -1169,6 +1221,13 @@ void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruct return; } } + + if (IsTypeConversionForStoringIntoNoWiderFieldOnly(instruction)) { + instruction->ReplaceWith(input); + instruction->GetBlock()->RemoveInstruction(instruction); + RecordSimplification(); + return; + } } void InstructionSimplifierVisitor::VisitAdd(HAdd* instruction) { diff --git a/test/711-checker-type-conversion/src/Main.java b/test/711-checker-type-conversion/src/Main.java index 2c9c3a157e..ae58200b1b 100644 --- a/test/711-checker-type-conversion/src/Main.java +++ b/test/711-checker-type-conversion/src/Main.java @@ -22,6 +22,32 @@ public class Main { } } + public static void assertShortEquals(short expected, short 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); + } + } + + public static void assertLongEquals(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void assertCharEquals(char expected, char result) { + if (expected != result) { + // Values are cast to int to display numeric values instead of + // (UTF-16 encoded) characters. + throw new Error("Expected: " + (int)expected + ", found: " + (int)result); + } + } + /// CHECK-START: byte Main.getByte1() constant_folding (before) /// CHECK: TypeConversion /// CHECK: TypeConversion @@ -69,9 +95,170 @@ public class Main { return (byte)((byte)i + (byte)i); } + static byte byteVal = -1; + static short shortVal = -1; + static char charVal = 0xffff; + static int intVal = -1; + + static byte[] byteArr = { 0 }; + static short[] shortArr = { 0 }; + static char[] charArr = { 0 }; + static int[] intArr = { 0 }; + + static byte $noinline$getByte() { + return byteVal; + } + + static short $noinline$getShort() { + return shortVal; + } + + static char $noinline$getChar() { + return charVal; + } + + static int $noinline$getInt() { + return intVal; + } + + static boolean sFlag = true; + + /// CHECK-START: void Main.byteToShort() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void byteToShort() { + shortArr[0] = 0; + if (sFlag) { + shortArr[0] = $noinline$getByte(); + } + } + + /// CHECK-START: void Main.byteToChar() instruction_simplifier$before_codegen (after) + /// CHECK: TypeConversion + private static void byteToChar() { + charArr[0] = 0; + if (sFlag) { + charArr[0] = (char)$noinline$getByte(); + } + } + + /// CHECK-START: void Main.byteToInt() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void byteToInt() { + intArr[0] = 0; + if (sFlag) { + intArr[0] = $noinline$getByte(); + } + } + + /// CHECK-START: void Main.charToByte() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void charToByte() { + byteArr[0] = 0; + if (sFlag) { + byteArr[0] = (byte)$noinline$getChar(); + } + } + + /// CHECK-START: void Main.charToShort() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void charToShort() { + shortArr[0] = 0; + if (sFlag) { + shortArr[0] = (short)$noinline$getChar(); + } + } + + /// CHECK-START: void Main.charToInt() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void charToInt() { + intArr[0] = 0; + if (sFlag) { + intArr[0] = $noinline$getChar(); + } + } + + /// CHECK-START: void Main.shortToByte() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void shortToByte() { + byteArr[0] = 0; + if (sFlag) { + byteArr[0] = (byte)$noinline$getShort(); + } + } + + /// CHECK-START: void Main.shortToChar() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void shortToChar() { + charArr[0] = 0; + if (sFlag) { + charArr[0] = (char)$noinline$getShort(); + } + } + + /// CHECK-START: void Main.shortToInt() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void shortToInt() { + intArr[0] = 0; + if (sFlag) { + intArr[0] = $noinline$getShort(); + } + } + + /// CHECK-START: void Main.intToByte() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void intToByte() { + byteArr[0] = 0; + if (sFlag) { + byteArr[0] = (byte)$noinline$getInt(); + } + } + + /// CHECK-START: void Main.intToShort() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void intToShort() { + shortArr[0] = 0; + if (sFlag) { + shortArr[0] = (short)$noinline$getInt(); + } + } + + /// CHECK-START: void Main.intToChar() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void intToChar() { + charArr[0] = 0; + if (sFlag) { + charArr[0] = (char)$noinline$getInt(); + } + } + public static void main(String[] args) { assertByteEquals(getByte1(), (byte)-5); assertByteEquals(getByte2(), (byte)(-201)); assertByteEquals(getByte3(), (byte)(0xcd + 0xcd)); + + byteToShort(); + assertShortEquals(shortArr[0], (short)-1); + byteToChar(); + assertCharEquals(charArr[0], (char)-1); + byteToInt(); + assertIntEquals(intArr[0], -1); + charToByte(); + assertByteEquals(byteArr[0], (byte)-1); + charToShort(); + assertShortEquals(shortArr[0], (short)-1); + charToInt(); + assertIntEquals(intArr[0], 0xffff); + shortToByte(); + assertByteEquals(byteArr[0], (byte)-1); + shortToChar(); + assertCharEquals(charArr[0], (char)-1); + shortToInt(); + assertIntEquals(intArr[0], -1); + intToByte(); + assertByteEquals(byteArr[0], (byte)-1); + intToShort(); + assertShortEquals(shortArr[0], (short)-1); + intToChar(); + assertCharEquals(charArr[0], (char)-1); } } -- GitLab From 2dd7b672ea0afd7ea4448b43d24829e9886de3af Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Thu, 7 Dec 2017 11:11:22 -0800 Subject: [PATCH 136/226] Fixed spilling bug (visible on ARM64): missed SIMD type. Test: test-art-host test-art-target Change-Id: I6f321446f54943e02f250732ec9da729f633c3a9 --- compiler/optimizing/loop_optimization.cc | 3 +- compiler/optimizing/nodes_vector.h | 15 ++- compiler/optimizing/ssa_liveness_analysis.cc | 7 +- .../src/Main.java | 105 ++++++++++++++++++ 4 files changed, 125 insertions(+), 5 deletions(-) diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 1ca096035e..3dc1ef7534 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -1749,7 +1749,8 @@ void HLoopOptimization::GenerateVecReductionPhiInputs(HPhi* phi, HInstruction* r HInstruction* HLoopOptimization::ReduceAndExtractIfNeeded(HInstruction* instruction) { if (instruction->IsPhi()) { HInstruction* input = instruction->InputAt(1); - if (input->IsVecOperation() && !input->IsVecExtractScalar()) { + if (HVecOperation::ReturnsSIMDValue(input)) { + DCHECK(!input->IsPhi()); HVecOperation* input_vector = input->AsVecOperation(); uint32_t vector_length = input_vector->GetVectorLength(); DataType::Type type = input_vector->GetPackedType(); diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h index 59d5b9f847..096349fd73 100644 --- a/compiler/optimizing/nodes_vector.h +++ b/compiler/optimizing/nodes_vector.h @@ -150,6 +150,19 @@ class HVecOperation : public HVariableInputSizeInstruction { } } + // Helper method to determine if an instruction returns a SIMD value. + // TODO: This method is needed until we introduce SIMD as proper type. + static bool ReturnsSIMDValue(HInstruction* instruction) { + if (instruction->IsVecOperation()) { + return !instruction->IsVecExtractScalar(); // only scalar returning vec op + } else if (instruction->IsPhi()) { + return + instruction->GetType() == kSIMDType && + instruction->InputAt(1)->IsVecOperation(); // vectorizer does not go deeper + } + return false; + } + DECLARE_ABSTRACT_INSTRUCTION(VecOperation); protected: @@ -879,7 +892,7 @@ class HVecSetScalars FINAL : public HVecOperation { vector_length, dex_pc) { for (size_t i = 0; i < number_of_scalars; i++) { - DCHECK(!scalars[i]->IsVecOperation() || scalars[i]->IsVecExtractScalar()); + DCHECK(!ReturnsSIMDValue(scalars[i])); SetRawInputAt(0, scalars[i]); } } diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index 9ab7a89b33..f6bd05269e 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -474,9 +474,10 @@ size_t LiveInterval::NumberOfSpillSlotsNeeded() const { // For a SIMD operation, compute the number of needed spill slots. // TODO: do through vector type? HInstruction* definition = GetParent()->GetDefinedBy(); - if (definition != nullptr && - definition->IsVecOperation() && - !definition->IsVecExtractScalar()) { + if (definition != nullptr && HVecOperation::ReturnsSIMDValue(definition)) { + if (definition->IsPhi()) { + definition = definition->InputAt(1); // SIMD always appears on back-edge + } return definition->AsVecOperation()->GetVectorNumberOfBytes() / kVRegSize; } // Return number of needed spill slots based on type. diff --git a/test/623-checker-loop-regressions/src/Main.java b/test/623-checker-loop-regressions/src/Main.java index 3ef8fe64bb..29f3817afb 100644 --- a/test/623-checker-loop-regressions/src/Main.java +++ b/test/623-checker-loop-regressions/src/Main.java @@ -493,6 +493,95 @@ public class Main { } } + // Avoid bad scheduler-SIMD interaction. + static int doNotMoveSIMD() { + int sum = 0; + for (int j = 0; j <= 8; j++) { + int[] a = new int[17]; // a[i] = 0; + // ConstructorFence ? + for (int i = 0; i < a.length; i++) { + a[i] += 1; // a[i] = 1; + } + for (int i = 0; i < a.length; i++) { + sum += a[i]; // expect a[i] = 1; + } + } + return sum; + } + + // Ensure spilling saves full SIMD values. + private static final int reduction32Values(int[] a, int[] b, int[] c, int[] d) { + int s0 = 0; + int s1 = 0; + int s2 = 0; + int s3 = 0; + int s4 = 0; + int s5 = 0; + int s6 = 0; + int s7 = 0; + int s8 = 0; + int s9 = 0; + int s10 = 0; + int s11 = 0; + int s12 = 0; + int s13 = 0; + int s14 = 0; + int s15 = 0; + int s16 = 0; + int s17 = 0; + int s18 = 0; + int s19 = 0; + int s20 = 0; + int s21 = 0; + int s22 = 0; + int s23 = 0; + int s24 = 0; + int s25 = 0; + int s26 = 0; + int s27 = 0; + int s28 = 0; + int s29 = 0; + int s30 = 0; + int s31 = 0; + for (int i = 1; i < 100; i++) { + s0 += a[i]; + s1 += b[i]; + s2 += c[i]; + s3 += d[i]; + s4 += a[i]; + s5 += b[i]; + s6 += c[i]; + s7 += d[i]; + s8 += a[i]; + s9 += b[i]; + s10 += c[i]; + s11 += d[i]; + s12 += a[i]; + s13 += b[i]; + s14 += c[i]; + s15 += d[i]; + s16 += a[i]; + s17 += b[i]; + s18 += c[i]; + s19 += d[i]; + s20 += a[i]; + s21 += b[i]; + s22 += c[i]; + s23 += d[i]; + s24 += a[i]; + s25 += b[i]; + s26 += c[i]; + s27 += d[i]; + s28 += a[i]; + s29 += b[i]; + s30 += c[i]; + s31 += d[i]; + } + return s0 + s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10 + s11 + s12 + s13 + s14 + s15 + + s16 + s17 + s18 + s19 + s20 + s21 + s22 + s23 + + s24 + s25 + s26 + s27 + s28 + s29 + s30 + s31; + } + public static void main(String[] args) { expectEquals(10, earlyExitFirst(-1)); for (int i = 0; i <= 10; i++) { @@ -655,6 +744,22 @@ public class Main { expectEquals((byte)((short) cx[i] + 1), b1[i]); } + expectEquals(153, doNotMoveSIMD()); + + { + int[] a1 = new int[100]; + int[] a2 = new int[100]; + int[] a3 = new int[100]; + int[] a4 = new int[100]; + for (int i = 0; i < 100; i++) { + a1[i] = i; + a2[i] = 1; + a3[i] = 100 - i; + a4[i] = i % 16; + } + expectEquals(85800, reduction32Values(a1, a2, a3, a4)); + } + System.out.println("passed"); } -- GitLab From 57943810cfc789da890d73621741729da5feaaf8 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 6 Dec 2017 21:39:13 -0800 Subject: [PATCH 137/226] ART: Replace base/logging with android-base/logging Replace wherever possible. ART's base/logging is now mainly VLOG and initialization code that is unnecessary to pull in and makes changes to verbose logging more painful than they have to be. Test: m test-art-host Change-Id: I3e3a4672ba5b621e57590a526c7d1c8b749e4f6e --- compiler/compiler.cc | 4 +++- compiler/debug/dwarf/writer.h | 4 +++- compiler/dex/dex_to_dex_compiler.cc | 5 ++-- compiler/dex/verification_results.cc | 3 ++- compiler/dex/verified_method.cc | 3 ++- compiler/driver/compiled_method_storage.cc | 3 ++- .../jni/quick/arm/calling_convention_arm.cc | 4 +++- .../quick/arm64/calling_convention_arm64.cc | 3 ++- compiler/jni/quick/calling_convention.cc | 2 +- compiler/jni/quick/jni_compiler.cc | 2 +- .../jni/quick/mips/calling_convention_mips.cc | 3 ++- .../quick/mips64/calling_convention_mips64.cc | 3 ++- .../jni/quick/x86/calling_convention_x86.cc | 3 ++- .../quick/x86_64/calling_convention_x86_64.cc | 3 ++- .../linker/error_delaying_output_stream.h | 4 +++- compiler/linker/linker_patch.h | 3 ++- compiler/linker/output_stream_test.cc | 4 +++- compiler/linker/vector_output_stream.cc | 2 +- compiler/optimizing/block_builder.cc | 1 + compiler/optimizing/code_generator_utils.cc | 5 ++-- compiler/optimizing/data_type.h | 3 ++- .../optimizing/optimizing_compiler_stats.h | 1 + compiler/utils/arm/assembler_arm_vixl.h | 4 +++- compiler/utils/arm/constants_arm.h | 3 ++- .../utils/arm/jni_macro_assembler_arm_vixl.h | 4 +++- compiler/utils/arm/managed_register_arm.h | 3 ++- compiler/utils/arm64/assembler_arm64.cc | 1 - compiler/utils/arm64/assembler_arm64.h | 4 +++- .../utils/arm64/jni_macro_assembler_arm64.cc | 1 - .../utils/arm64/jni_macro_assembler_arm64.h | 4 +++- compiler/utils/arm64/managed_register_arm64.h | 3 ++- compiler/utils/assembler.h | 3 ++- compiler/utils/intrusive_forward_list.h | 3 ++- compiler/utils/jni_macro_assembler.h | 3 ++- compiler/utils/label.h | 4 ++-- compiler/utils/mips/constants_mips.h | 3 ++- compiler/utils/mips64/constants_mips64.h | 3 ++- compiler/utils/swap_space.cc | 1 - compiler/utils/swap_space.h | 3 ++- compiler/utils/test_dex_file_builder.h | 3 ++- compiler/utils/x86/constants_x86.h | 3 ++- compiler/utils/x86_64/constants_x86_64.h | 3 ++- dex2oat/dex2oat_image_test.cc | 3 ++- dex2oat/dex2oat_test.cc | 4 ++-- dex2oat/linker/elf_writer_quick.cc | 3 ++- dex2oat/linker/image_writer.cc | 2 +- dex2oat/linker/index_bss_mapping_encoder.h | 3 ++- dex2oat/linker/multi_oat_relative_patcher.cc | 3 ++- dexdump/dexdump_cfg.cc | 1 + dexdump/dexdump_main.cc | 4 +++- dexlayout/dexdiag.cc | 1 + dexlayout/dexlayout.cc | 1 + dexlayout/dexlayout_main.cc | 4 +++- dexlist/dexlist.cc | 1 + openjdkjvm/OpenjdkJvm.cc | 3 ++- openjdkjvmti/OpenjdkJvmTi.cc | 3 ++- openjdkjvmti/art_jvmti.h | 3 ++- openjdkjvmti/events.cc | 1 - openjdkjvmti/events.h | 5 +++- openjdkjvmti/jvmti_allocator.h | 5 ++-- openjdkjvmti/jvmti_weak_table-inl.h | 3 ++- openjdkjvmti/ti_class_loader.cc | 4 ++-- openjdkjvmti/ti_redefine.cc | 4 ++-- openjdkjvmti/ti_thread.cc | 5 ++-- profman/profman.cc | 1 + runtime/arch/arm/context_arm.h | 3 ++- runtime/arch/arm/fault_handler_arm.cc | 2 +- .../arch/arm/instruction_set_features_arm.cc | 7 +++--- runtime/arch/arm/thread_arm.cc | 3 ++- runtime/arch/arm64/context_arm64.h | 3 ++- runtime/arch/arm64/fault_handler_arm64.cc | 2 +- .../arm64/instruction_set_features_arm64.cc | 6 ++--- runtime/arch/arm64/thread_arm64.cc | 3 ++- runtime/arch/code_offset.h | 4 +++- runtime/arch/instruction_set_features_test.cc | 7 +++--- runtime/arch/mips/context_mips.h | 3 ++- runtime/arch/mips/fault_handler_mips.cc | 2 +- .../mips/instruction_set_features_mips.cc | 5 ++-- .../arch/mips/instruction_set_features_mips.h | 3 ++- runtime/arch/mips/registers_mips.h | 3 ++- runtime/arch/mips/thread_mips.cc | 3 ++- runtime/arch/mips64/context_mips64.h | 3 ++- runtime/arch/mips64/fault_handler_mips64.cc | 2 +- runtime/arch/mips64/registers_mips64.h | 3 ++- runtime/arch/mips64/thread_mips64.cc | 3 ++- runtime/arch/x86/context_x86.h | 3 ++- runtime/arch/x86/fault_handler_x86.cc | 2 +- .../arch/x86/instruction_set_features_x86.cc | 6 ++--- runtime/arch/x86/registers_x86.h | 3 ++- runtime/arch/x86_64/context_x86_64.h | 3 ++- runtime/arch/x86_64/registers_x86_64.h | 3 ++- runtime/art_field-inl.h | 3 ++- runtime/art_method-inl.h | 1 - runtime/art_method.h | 5 +++- runtime/atomic.h | 3 ++- runtime/barrier.cc | 2 +- runtime/base/allocator.cc | 3 ++- runtime/base/arena_allocator.cc | 3 ++- runtime/base/arena_allocator.h | 6 ++--- runtime/base/arena_object.h | 6 +++-- runtime/base/array_ref.h | 2 +- runtime/base/bit_field.h | 3 ++- runtime/base/bit_utils.h | 3 ++- runtime/base/bit_utils_iterator.h | 3 ++- runtime/base/bit_vector-inl.h | 6 +++-- runtime/base/bounded_fifo.h | 3 ++- runtime/base/casts.h | 3 +-- runtime/base/dchecked_vector.h | 2 +- runtime/base/debug_stack.h | 5 ++-- runtime/base/file_magic.cc | 4 ++-- runtime/base/file_utils.h | 5 ++-- runtime/base/hash_set.h | 4 +++- runtime/base/histogram-inl.h | 2 ++ runtime/base/histogram.h | 2 +- runtime/base/mutex.h | 4 +++- runtime/base/scoped_arena_allocator.h | 3 ++- runtime/base/scoped_flock.cc | 4 ++-- runtime/base/scoped_flock.h | 3 +-- runtime/base/stl_util.h | 2 +- runtime/base/stringpiece.cc | 2 +- runtime/base/timing_logger.cc | 3 ++- runtime/base/unix_file/fd_file.cc | 2 +- runtime/check_jni.cc | 5 ++-- runtime/common_throws.cc | 4 ++-- runtime/dex_file-inl.h | 1 - runtime/dex_file.cc | 1 - runtime/dex_file.h | 4 +++- runtime/dex_file_layout.h | 2 +- runtime/dex_file_tracking_registrar.cc | 3 ++- runtime/dex_instruction.h | 3 ++- runtime/dex_instruction_iterator.h | 4 +++- runtime/dex_to_dex_decompiler.cc | 4 +++- runtime/elf_file.cc | 1 - runtime/elf_utils.h | 4 ++-- runtime/entrypoints/jni/jni_entrypoints.cc | 3 ++- .../quick/quick_default_init_entrypoints.h | 2 +- .../quick/quick_deoptimization_entrypoints.cc | 2 +- .../quick/quick_jni_entrypoints.cc | 3 ++- runtime/gc/accounting/atomic_stack.h | 3 ++- runtime/gc/accounting/bitmap-inl.h | 3 ++- runtime/gc/accounting/card_table-inl.h | 6 +++-- runtime/gc/accounting/card_table.cc | 1 - runtime/gc/accounting/heap_bitmap.h | 5 +++- runtime/gc/accounting/space_bitmap-inl.h | 3 ++- runtime/gc/allocator/dlmalloc.cc | 3 ++- runtime/gc/allocator/rosalloc.h | 3 ++- runtime/gc/collector/garbage_collector.cc | 2 +- runtime/gc/collector/immune_spaces.cc | 1 + runtime/gc/collector/mark_compact.cc | 4 +++- runtime/gc/collector/mark_sweep.cc | 2 +- runtime/gc/collector/semi_space.cc | 2 +- runtime/gc/gc_cause.cc | 5 +++- runtime/gc/heap.h | 5 +++- runtime/gc/space/image_space_fs.h | 2 +- runtime/gc/space/large_object_space.cc | 4 +++- runtime/gc/space/space.cc | 4 +++- runtime/handle.h | 3 ++- runtime/handle_scope.h | 3 ++- runtime/hprof/hprof.cc | 6 +++-- runtime/indenter.h | 3 ++- runtime/index_bss_mapping.h | 3 ++- runtime/indirect_reference_table.h | 4 +++- runtime/interpreter/interpreter_common.h | 5 ++-- runtime/interpreter/unstarted_runtime.cc | 4 ++-- runtime/jdwp/jdwp.h | 1 + runtime/jdwp/jdwp_adb.cc | 2 +- runtime/jdwp/jdwp_event.cc | 2 +- runtime/jdwp/jdwp_expand_buf.cc | 3 ++- runtime/jdwp/jdwp_handler.cc | 2 +- runtime/jdwp/jdwp_main.cc | 2 +- runtime/jdwp/jdwp_socket.cc | 2 +- runtime/jit/debugger_interface.cc | 3 ++- runtime/jit/jit.cc | 2 +- runtime/jit/profile_compilation_info.cc | 1 + runtime/jni_internal.cc | 2 +- runtime/leb128.h | 4 +++- runtime/lock_word.h | 3 ++- runtime/managed_stack.h | 3 ++- runtime/mem_map.cc | 1 + runtime/memory_region.cc | 3 --- runtime/memory_region.h | 3 ++- runtime/method_info.h | 4 +++- runtime/mirror/array-inl.h | 4 ++-- runtime/mirror/dex_cache-inl.h | 3 ++- runtime/mirror/dex_cache.cc | 1 - runtime/mirror/dex_cache.h | 1 + runtime/monitor_pool.cc | 2 +- runtime/native/dalvik_system_ZygoteHooks.cc | 6 +++-- ...rg_apache_harmony_dalvik_ddmc_DdmServer.cc | 3 ++- ...pache_harmony_dalvik_ddmc_DdmVmInternal.cc | 3 ++- runtime/native_bridge_art_interface.cc | 2 +- runtime/non_debuggable_classes.cc | 1 - runtime/oat_file_assistant.cc | 2 +- runtime/oat_file_manager.cc | 2 +- runtime/os_linux.cc | 3 ++- runtime/parsed_options.cc | 4 +++- runtime/plugin.cc | 2 -- runtime/plugin.h | 3 ++- runtime/primitive.h | 3 ++- runtime/quick_exception_handler.h | 3 ++- runtime/read_barrier.h | 4 +++- runtime/runtime_common.cc | 5 ++-- runtime/safe_map.h | 3 ++- runtime/scoped_thread_state_change-inl.h | 2 ++ runtime/scoped_thread_state_change.cc | 1 - runtime/signal_set.h | 2 +- runtime/stride_iterator.h | 2 +- runtime/string_reference.h | 3 ++- runtime/thread_linux.cc | 4 +++- runtime/thread_pool.cc | 4 ++-- runtime/transaction.cc | 3 ++- runtime/type_reference.h | 3 ++- runtime/utf.cc | 3 ++- runtime/utils.h | 3 ++- runtime/utils/dex_cache_arrays_layout-inl.h | 3 ++- runtime/vdex_file.cc | 3 ++- runtime/verifier/method_verifier-inl.h | 3 ++- runtime/verifier/method_verifier.cc | 2 +- runtime/verifier/register_line-inl.h | 22 ++++++++++++++++++ runtime/verifier/register_line.h | 23 +++---------------- runtime/well_known_classes.cc | 4 ++-- runtime/zip_archive.h | 3 ++- simulator/code_simulator_arm64.cc | 2 +- simulator/code_simulator_container.cc | 1 + simulator/code_simulator_container.h | 3 ++- test/004-JniTest/jni_test.cc | 3 ++- test/044-proxy/native_proxy.cc | 2 +- test/137-cfi/cfi.cc | 4 ++-- test/642-fp-callees/fp_callees.cc | 3 ++- test/900-hello-plugin/load_unload.cc | 5 ++-- test/936-search-onload/search_onload.cc | 5 ++-- .../source_transform.cc | 1 - test/common/runtime_state.cc | 4 +++- test/common/stack_inspect.cc | 4 +++- test/ti-agent/common_load.cc | 3 ++- 235 files changed, 471 insertions(+), 286 deletions(-) diff --git a/compiler/compiler.cc b/compiler/compiler.cc index c500921ab3..bb614ae7b2 100644 --- a/compiler/compiler.cc +++ b/compiler/compiler.cc @@ -16,7 +16,9 @@ #include "compiler.h" -#include "base/logging.h" +#include + +#include "base/macros.h" #include "driver/compiler_driver.h" #include "optimizing/optimizing_compiler.h" #include "utils.h" diff --git a/compiler/debug/dwarf/writer.h b/compiler/debug/dwarf/writer.h index 95912ad6c9..afeb980352 100644 --- a/compiler/debug/dwarf/writer.h +++ b/compiler/debug/dwarf/writer.h @@ -19,8 +19,10 @@ #include #include + +#include + #include "base/bit_utils.h" -#include "base/logging.h" #include "leb128.h" namespace art { diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index cc452fc1ae..eddc8ed132 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -16,11 +16,12 @@ #include "dex_to_dex_compiler.h" -#include "android-base/stringprintf.h" +#include +#include #include "art_field-inl.h" #include "art_method-inl.h" -#include "base/logging.h" +#include "base/macros.h" #include "base/mutex.h" #include "bytecode_utils.h" #include "compiled_method.h" diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc index 03c90d82c8..1e0b94de81 100644 --- a/compiler/dex/verification_results.cc +++ b/compiler/dex/verification_results.cc @@ -16,7 +16,8 @@ #include "verification_results.h" -#include "base/logging.h" +#include + #include "base/mutex-inl.h" #include "base/stl_util.h" #include "driver/compiler_driver.h" diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc index 524b0a6911..8934201b10 100644 --- a/compiler/dex/verified_method.cc +++ b/compiler/dex/verified_method.cc @@ -19,7 +19,8 @@ #include #include -#include "base/logging.h" +#include + #include "code_item_accessors-inl.h" #include "dex_file.h" #include "dex_instruction-inl.h" diff --git a/compiler/driver/compiled_method_storage.cc b/compiler/driver/compiled_method_storage.cc index c739333cee..c8c2b6998f 100644 --- a/compiler/driver/compiled_method_storage.cc +++ b/compiler/driver/compiled_method_storage.cc @@ -19,7 +19,8 @@ #include "compiled_method_storage.h" -#include "base/logging.h" +#include + #include "compiled_method.h" #include "linker/linker_patch.h" #include "thread-current-inl.h" diff --git a/compiler/jni/quick/arm/calling_convention_arm.cc b/compiler/jni/quick/arm/calling_convention_arm.cc index 3e637bcf43..54f193b551 100644 --- a/compiler/jni/quick/arm/calling_convention_arm.cc +++ b/compiler/jni/quick/arm/calling_convention_arm.cc @@ -16,7 +16,9 @@ #include "calling_convention_arm.h" -#include "base/logging.h" +#include + +#include "base/macros.h" #include "handle_scope-inl.h" #include "utils/arm/managed_register_arm.h" diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.cc b/compiler/jni/quick/arm64/calling_convention_arm64.cc index 3afd7011ca..328ecbbc5c 100644 --- a/compiler/jni/quick/arm64/calling_convention_arm64.cc +++ b/compiler/jni/quick/arm64/calling_convention_arm64.cc @@ -16,7 +16,8 @@ #include "calling_convention_arm64.h" -#include "base/logging.h" +#include + #include "handle_scope-inl.h" #include "utils/arm64/managed_register_arm64.h" diff --git a/compiler/jni/quick/calling_convention.cc b/compiler/jni/quick/calling_convention.cc index 55c27d1a6a..ff814c8a6b 100644 --- a/compiler/jni/quick/calling_convention.cc +++ b/compiler/jni/quick/calling_convention.cc @@ -16,7 +16,7 @@ #include "calling_convention.h" -#include "base/logging.h" +#include #ifdef ART_ENABLE_CODEGEN_arm #include "jni/quick/arm/calling_convention_arm.h" diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index 37f7d632ca..136e3db062 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -25,7 +25,7 @@ #include "art_method.h" #include "base/arena_allocator.h" #include "base/enums.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "calling_convention.h" #include "class_linker.h" diff --git a/compiler/jni/quick/mips/calling_convention_mips.cc b/compiler/jni/quick/mips/calling_convention_mips.cc index 0e0716e911..5ec1addcb9 100644 --- a/compiler/jni/quick/mips/calling_convention_mips.cc +++ b/compiler/jni/quick/mips/calling_convention_mips.cc @@ -16,7 +16,8 @@ #include "calling_convention_mips.h" -#include "base/logging.h" +#include + #include "handle_scope-inl.h" #include "utils/mips/managed_register_mips.h" diff --git a/compiler/jni/quick/mips64/calling_convention_mips64.cc b/compiler/jni/quick/mips64/calling_convention_mips64.cc index afe6a762eb..a7012aefa8 100644 --- a/compiler/jni/quick/mips64/calling_convention_mips64.cc +++ b/compiler/jni/quick/mips64/calling_convention_mips64.cc @@ -16,7 +16,8 @@ #include "calling_convention_mips64.h" -#include "base/logging.h" +#include + #include "handle_scope-inl.h" #include "utils/mips64/managed_register_mips64.h" diff --git a/compiler/jni/quick/x86/calling_convention_x86.cc b/compiler/jni/quick/x86/calling_convention_x86.cc index 0bfcc3fb4d..ad58e3820d 100644 --- a/compiler/jni/quick/x86/calling_convention_x86.cc +++ b/compiler/jni/quick/x86/calling_convention_x86.cc @@ -16,7 +16,8 @@ #include "calling_convention_x86.h" -#include "base/logging.h" +#include + #include "handle_scope-inl.h" #include "utils/x86/managed_register_x86.h" diff --git a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc index ba654f4750..e5e96d01fc 100644 --- a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc +++ b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc @@ -16,8 +16,9 @@ #include "calling_convention_x86_64.h" +#include + #include "base/bit_utils.h" -#include "base/logging.h" #include "handle_scope-inl.h" #include "utils/x86_64/managed_register_x86_64.h" diff --git a/compiler/linker/error_delaying_output_stream.h b/compiler/linker/error_delaying_output_stream.h index 33e6b5ab23..659f1dc093 100644 --- a/compiler/linker/error_delaying_output_stream.h +++ b/compiler/linker/error_delaying_output_stream.h @@ -19,7 +19,9 @@ #include "output_stream.h" -#include "base/logging.h" +#include + +#include "base/macros.h" namespace art { namespace linker { diff --git a/compiler/linker/linker_patch.h b/compiler/linker/linker_patch.h index 0ac149029a..6f4e7746a6 100644 --- a/compiler/linker/linker_patch.h +++ b/compiler/linker/linker_patch.h @@ -20,8 +20,9 @@ #include #include +#include + #include "base/bit_utils.h" -#include "base/logging.h" #include "method_reference.h" namespace art { diff --git a/compiler/linker/output_stream_test.cc b/compiler/linker/output_stream_test.cc index ad298406be..f93ea7a709 100644 --- a/compiler/linker/output_stream_test.cc +++ b/compiler/linker/output_stream_test.cc @@ -17,7 +17,9 @@ #include "file_output_stream.h" #include "vector_output_stream.h" -#include "base/logging.h" +#include + +#include "base/macros.h" #include "base/unix_file/fd_file.h" #include "buffered_output_stream.h" #include "common_runtime_test.h" diff --git a/compiler/linker/vector_output_stream.cc b/compiler/linker/vector_output_stream.cc index 75f90e5f94..f2cae5b1d5 100644 --- a/compiler/linker/vector_output_stream.cc +++ b/compiler/linker/vector_output_stream.cc @@ -16,7 +16,7 @@ #include "vector_output_stream.h" -#include "base/logging.h" +#include namespace art { namespace linker { diff --git a/compiler/optimizing/block_builder.cc b/compiler/optimizing/block_builder.cc index ed000327ff..58f591bd1e 100644 --- a/compiler/optimizing/block_builder.cc +++ b/compiler/optimizing/block_builder.cc @@ -16,6 +16,7 @@ #include "block_builder.h" +#include "base/logging.h" // FOR VLOG. #include "bytecode_utils.h" #include "quicken_info.h" diff --git a/compiler/optimizing/code_generator_utils.cc b/compiler/optimizing/code_generator_utils.cc index 96fe2a17e6..dd47a1fc6c 100644 --- a/compiler/optimizing/code_generator_utils.cc +++ b/compiler/optimizing/code_generator_utils.cc @@ -15,9 +15,10 @@ */ #include "code_generator_utils.h" -#include "nodes.h" -#include "base/logging.h" +#include + +#include "nodes.h" namespace art { diff --git a/compiler/optimizing/data_type.h b/compiler/optimizing/data_type.h index d253036479..548fe28cee 100644 --- a/compiler/optimizing/data_type.h +++ b/compiler/optimizing/data_type.h @@ -19,7 +19,8 @@ #include -#include "base/logging.h" +#include + #include "base/bit_utils.h" namespace art { diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index a2e92d2931..32a94ab5e4 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -23,6 +23,7 @@ #include #include "atomic.h" +#include "base/logging.h" // For VLOG_IS_ON. #include "globals.h" namespace art { diff --git a/compiler/utils/arm/assembler_arm_vixl.h b/compiler/utils/arm/assembler_arm_vixl.h index 0e73e6bf9e..1377e64073 100644 --- a/compiler/utils/arm/assembler_arm_vixl.h +++ b/compiler/utils/arm/assembler_arm_vixl.h @@ -17,8 +17,10 @@ #ifndef ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_VIXL_H_ #define ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_VIXL_H_ +#include + #include "base/arena_containers.h" -#include "base/logging.h" +#include "base/macros.h" #include "constants_arm.h" #include "offsets.h" #include "utils/arm/assembler_arm_shared.h" diff --git a/compiler/utils/arm/constants_arm.h b/compiler/utils/arm/constants_arm.h index 5b87e3e7f8..66252bed86 100644 --- a/compiler/utils/arm/constants_arm.h +++ b/compiler/utils/arm/constants_arm.h @@ -21,9 +21,10 @@ #include +#include + #include "arch/arm/registers_arm.h" #include "base/casts.h" -#include "base/logging.h" #include "globals.h" namespace art { diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.h b/compiler/utils/arm/jni_macro_assembler_arm_vixl.h index c13c9af819..4bc5d69f4d 100644 --- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.h +++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.h @@ -17,8 +17,10 @@ #ifndef ART_COMPILER_UTILS_ARM_JNI_MACRO_ASSEMBLER_ARM_VIXL_H_ #define ART_COMPILER_UTILS_ARM_JNI_MACRO_ASSEMBLER_ARM_VIXL_H_ +#include + #include "base/arena_containers.h" -#include "base/logging.h" +#include "base/macros.h" #include "constants_arm.h" #include "offsets.h" #include "utils/arm/assembler_arm_shared.h" diff --git a/compiler/utils/arm/managed_register_arm.h b/compiler/utils/arm/managed_register_arm.h index 2be2d5638e..26f23b2ed6 100644 --- a/compiler/utils/arm/managed_register_arm.h +++ b/compiler/utils/arm/managed_register_arm.h @@ -17,7 +17,8 @@ #ifndef ART_COMPILER_UTILS_ARM_MANAGED_REGISTER_ARM_H_ #define ART_COMPILER_UTILS_ARM_MANAGED_REGISTER_ARM_H_ -#include "base/logging.h" +#include + #include "constants_arm.h" #include "debug/dwarf/register.h" #include "utils/managed_register.h" diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc index bb989588d6..c83fd4404a 100644 --- a/compiler/utils/arm64/assembler_arm64.cc +++ b/compiler/utils/arm64/assembler_arm64.cc @@ -15,7 +15,6 @@ */ #include "assembler_arm64.h" -#include "base/logging.h" #include "entrypoints/quick/quick_entrypoints.h" #include "heap_poisoning.h" #include "offsets.h" diff --git a/compiler/utils/arm64/assembler_arm64.h b/compiler/utils/arm64/assembler_arm64.h index e5ec24add0..8983af2677 100644 --- a/compiler/utils/arm64/assembler_arm64.h +++ b/compiler/utils/arm64/assembler_arm64.h @@ -21,8 +21,10 @@ #include #include +#include + #include "base/arena_containers.h" -#include "base/logging.h" +#include "base/macros.h" #include "offsets.h" #include "utils/arm64/managed_register_arm64.h" #include "utils/assembler.h" diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.cc b/compiler/utils/arm64/jni_macro_assembler_arm64.cc index 573bb6d4be..a5aa1c12b3 100644 --- a/compiler/utils/arm64/jni_macro_assembler_arm64.cc +++ b/compiler/utils/arm64/jni_macro_assembler_arm64.cc @@ -16,7 +16,6 @@ #include "jni_macro_assembler_arm64.h" -#include "base/logging.h" #include "entrypoints/quick/quick_entrypoints.h" #include "managed_register_arm64.h" #include "offsets.h" diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.h b/compiler/utils/arm64/jni_macro_assembler_arm64.h index ce39a13692..f531b2aa51 100644 --- a/compiler/utils/arm64/jni_macro_assembler_arm64.h +++ b/compiler/utils/arm64/jni_macro_assembler_arm64.h @@ -21,10 +21,12 @@ #include #include +#include + #include "assembler_arm64.h" #include "base/arena_containers.h" #include "base/enums.h" -#include "base/logging.h" +#include "base/macros.h" #include "offsets.h" #include "utils/assembler.h" #include "utils/jni_macro_assembler.h" diff --git a/compiler/utils/arm64/managed_register_arm64.h b/compiler/utils/arm64/managed_register_arm64.h index 7378a0a081..9ce7ec9a97 100644 --- a/compiler/utils/arm64/managed_register_arm64.h +++ b/compiler/utils/arm64/managed_register_arm64.h @@ -17,8 +17,9 @@ #ifndef ART_COMPILER_UTILS_ARM64_MANAGED_REGISTER_ARM64_H_ #define ART_COMPILER_UTILS_ARM64_MANAGED_REGISTER_ARM64_H_ +#include + #include "arch/arm64/registers_arm64.h" -#include "base/logging.h" #include "debug/dwarf/register.h" #include "utils/managed_register.h" diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h index e0cef859e1..5b0cd6baa8 100644 --- a/compiler/utils/assembler.h +++ b/compiler/utils/assembler.h @@ -19,6 +19,8 @@ #include +#include + #include "arch/instruction_set.h" #include "arch/instruction_set_features.h" #include "arm/constants_arm.h" @@ -26,7 +28,6 @@ #include "base/arena_object.h" #include "base/array_ref.h" #include "base/enums.h" -#include "base/logging.h" #include "base/macros.h" #include "debug/dwarf/debug_frame_opcode_writer.h" #include "label.h" diff --git a/compiler/utils/intrusive_forward_list.h b/compiler/utils/intrusive_forward_list.h index 5a358ac2c4..ccdd32aad4 100644 --- a/compiler/utils/intrusive_forward_list.h +++ b/compiler/utils/intrusive_forward_list.h @@ -23,8 +23,9 @@ #include #include +#include + #include "base/casts.h" -#include "base/logging.h" #include "base/macros.h" namespace art { diff --git a/compiler/utils/jni_macro_assembler.h b/compiler/utils/jni_macro_assembler.h index 0fc1353bf5..f5df926749 100644 --- a/compiler/utils/jni_macro_assembler.h +++ b/compiler/utils/jni_macro_assembler.h @@ -19,12 +19,13 @@ #include +#include + #include "arch/instruction_set.h" #include "base/arena_allocator.h" #include "base/arena_object.h" #include "base/array_ref.h" #include "base/enums.h" -#include "base/logging.h" #include "base/macros.h" #include "managed_register.h" #include "offsets.h" diff --git a/compiler/utils/label.h b/compiler/utils/label.h index b9d4e9c521..3c91b2ffd1 100644 --- a/compiler/utils/label.h +++ b/compiler/utils/label.h @@ -17,8 +17,8 @@ #ifndef ART_COMPILER_UTILS_LABEL_H_ #define ART_COMPILER_UTILS_LABEL_H_ -#include "base/logging.h" -#include "base/macros.h" +#include +#include namespace art { diff --git a/compiler/utils/mips/constants_mips.h b/compiler/utils/mips/constants_mips.h index b4dfdbd8d3..016c0dbb2e 100644 --- a/compiler/utils/mips/constants_mips.h +++ b/compiler/utils/mips/constants_mips.h @@ -19,8 +19,9 @@ #include +#include + #include "arch/mips/registers_mips.h" -#include "base/logging.h" #include "base/macros.h" #include "globals.h" diff --git a/compiler/utils/mips64/constants_mips64.h b/compiler/utils/mips64/constants_mips64.h index bc8e40b437..310f23c287 100644 --- a/compiler/utils/mips64/constants_mips64.h +++ b/compiler/utils/mips64/constants_mips64.h @@ -19,8 +19,9 @@ #include +#include + #include "arch/mips64/registers_mips64.h" -#include "base/logging.h" #include "base/macros.h" #include "globals.h" diff --git a/compiler/utils/swap_space.cc b/compiler/utils/swap_space.cc index 12d113d420..1f9ad4242d 100644 --- a/compiler/utils/swap_space.cc +++ b/compiler/utils/swap_space.cc @@ -22,7 +22,6 @@ #include #include "base/bit_utils.h" -#include "base/logging.h" #include "base/macros.h" #include "base/mutex.h" #include "thread-current-inl.h" diff --git a/compiler/utils/swap_space.h b/compiler/utils/swap_space.h index 2280f8b993..76df527108 100644 --- a/compiler/utils/swap_space.h +++ b/compiler/utils/swap_space.h @@ -24,7 +24,8 @@ #include #include -#include "base/logging.h" +#include + #include "base/macros.h" #include "base/mutex.h" diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h index 0da30fe768..441ef8e117 100644 --- a/compiler/utils/test_dex_file_builder.h +++ b/compiler/utils/test_dex_file_builder.h @@ -24,8 +24,9 @@ #include #include +#include + #include "base/bit_utils.h" -#include "base/logging.h" #include "dex_file_loader.h" #include "standard_dex_file.h" diff --git a/compiler/utils/x86/constants_x86.h b/compiler/utils/x86/constants_x86.h index 0bc1560ed7..2e03b9fc3c 100644 --- a/compiler/utils/x86/constants_x86.h +++ b/compiler/utils/x86/constants_x86.h @@ -19,8 +19,9 @@ #include +#include + #include "arch/x86/registers_x86.h" -#include "base/logging.h" #include "base/macros.h" #include "globals.h" diff --git a/compiler/utils/x86_64/constants_x86_64.h b/compiler/utils/x86_64/constants_x86_64.h index cc508a196b..2af3e7be16 100644 --- a/compiler/utils/x86_64/constants_x86_64.h +++ b/compiler/utils/x86_64/constants_x86_64.h @@ -19,8 +19,9 @@ #include +#include + #include "arch/x86_64/registers_x86_64.h" -#include "base/logging.h" #include "base/macros.h" #include "globals.h" diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc index a02fbf862f..035b395300 100644 --- a/dex2oat/dex2oat_image_test.cc +++ b/dex2oat/dex2oat_image_test.cc @@ -22,10 +22,11 @@ #include #include +#include + #include "common_runtime_test.h" #include "base/file_utils.h" -#include "base/logging.h" #include "base/macros.h" #include "base/unix_file/fd_file.h" #include "dex_file-inl.h" diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index ad287b0745..8805aa1d14 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -22,11 +22,11 @@ #include #include -#include "android-base/stringprintf.h" +#include +#include #include "common_runtime_test.h" -#include "base/logging.h" #include "base/macros.h" #include "base/mutex-inl.h" #include "bytecode_utils.h" diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc index b139a12fd4..5fc33ddf8b 100644 --- a/dex2oat/linker/elf_writer_quick.cc +++ b/dex2oat/linker/elf_writer_quick.cc @@ -20,8 +20,9 @@ #include #include +#include + #include "base/casts.h" -#include "base/logging.h" #include "compiled_method.h" #include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 5ade583b88..2f459136cb 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -29,7 +29,7 @@ #include "art_method-inl.h" #include "base/callee_save_type.h" #include "base/enums.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/unix_file/fd_file.h" #include "class_linker-inl.h" #include "compiled_method.h" diff --git a/dex2oat/linker/index_bss_mapping_encoder.h b/dex2oat/linker/index_bss_mapping_encoder.h index 9bc14329ff..c6326ed1cf 100644 --- a/dex2oat/linker/index_bss_mapping_encoder.h +++ b/dex2oat/linker/index_bss_mapping_encoder.h @@ -17,9 +17,10 @@ #ifndef ART_DEX2OAT_LINKER_INDEX_BSS_MAPPING_ENCODER_H_ #define ART_DEX2OAT_LINKER_INDEX_BSS_MAPPING_ENCODER_H_ +#include + #include "base/bit_utils.h" #include "base/bit_vector-inl.h" -#include "base/logging.h" #include "index_bss_mapping.h" namespace art { diff --git a/dex2oat/linker/multi_oat_relative_patcher.cc b/dex2oat/linker/multi_oat_relative_patcher.cc index 178a78f0bb..1abaf7dfd1 100644 --- a/dex2oat/linker/multi_oat_relative_patcher.cc +++ b/dex2oat/linker/multi_oat_relative_patcher.cc @@ -16,8 +16,9 @@ #include "multi_oat_relative_patcher.h" +#include + #include "base/bit_utils.h" -#include "base/logging.h" #include "globals.h" namespace art { diff --git a/dexdump/dexdump_cfg.cc b/dexdump/dexdump_cfg.cc index 62c970de8a..23ecf93447 100644 --- a/dexdump/dexdump_cfg.cc +++ b/dexdump/dexdump_cfg.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include "dex_file-inl.h" #include "dex_instruction-inl.h" diff --git a/dexdump/dexdump_main.cc b/dexdump/dexdump_main.cc index 43c3d12de5..382b551a1a 100644 --- a/dexdump/dexdump_main.cc +++ b/dexdump/dexdump_main.cc @@ -28,7 +28,9 @@ #include #include -#include "base/logging.h" +#include + +#include // For InitLogging. #include "mem_map.h" #include "runtime.h" diff --git a/dexlayout/dexdiag.cc b/dexlayout/dexdiag.cc index e83f98ee6b..b2507015e9 100644 --- a/dexlayout/dexdiag.cc +++ b/dexlayout/dexdiag.cc @@ -26,6 +26,7 @@ #include "android-base/stringprintf.h" +#include "base/logging.h" // For InitLogging. #include "base/stringpiece.h" #include "dex_file.h" diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index d904a52f0c..88fc0c0519 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -33,6 +33,7 @@ #include "android-base/stringprintf.h" +#include "base/logging.h" // For VLOG_IS_ON. #include "dex_file-inl.h" #include "dex_file_layout.h" #include "dex_file_loader.h" diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc index 17097f1728..5bb7196531 100644 --- a/dexlayout/dexlayout_main.cc +++ b/dexlayout/dexlayout_main.cc @@ -29,7 +29,9 @@ #include #include -#include "base/logging.h" +#include + +#include "base/logging.h" // For InitLogging. #include "jit/profile_compilation_info.h" #include "mem_map.h" #include "runtime.h" diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc index 3bd903de5b..630eacba9b 100644 --- a/dexlist/dexlist.cc +++ b/dexlist/dexlist.cc @@ -26,6 +26,7 @@ #include #include +#include "base/logging.h" // For InitLogging. #include "dex_file-inl.h" #include "dex_file_loader.h" #include "mem_map.h" diff --git a/openjdkjvm/OpenjdkJvm.cc b/openjdkjvm/OpenjdkJvm.cc index 29ebefddea..1b8233aae8 100644 --- a/openjdkjvm/OpenjdkJvm.cc +++ b/openjdkjvm/OpenjdkJvm.cc @@ -40,9 +40,10 @@ #include #include +#include + #include "../../libcore/ojluni/src/main/native/jvm.h" // TODO(narayan): fix it -#include "base/logging.h" #include "base/macros.h" #include "common_throws.h" #include "gc/heap.h" diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc index 6b2d5d6a5c..ac68fcf307 100644 --- a/openjdkjvmti/OpenjdkJvmTi.cc +++ b/openjdkjvmti/OpenjdkJvmTi.cc @@ -33,12 +33,13 @@ #include #include +#include + #include #include "jvmti.h" #include "art_jvmti.h" -#include "base/logging.h" #include "base/mutex.h" #include "events-inl.h" #include "jni_env_ext-inl.h" diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h index e8e62c2b40..2a8c2e91df 100644 --- a/openjdkjvmti/art_jvmti.h +++ b/openjdkjvmti/art_jvmti.h @@ -39,9 +39,10 @@ #include +#include + #include "deopt_manager.h" #include "base/casts.h" -#include "base/logging.h" #include "base/macros.h" #include "base/strlcpy.h" #include "base/mutex.h" diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index 912e7548db..330a3de99c 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -36,7 +36,6 @@ #include "art_field-inl.h" #include "art_jvmti.h" #include "art_method-inl.h" -#include "base/logging.h" #include "deopt_manager.h" #include "dex_file_types.h" #include "gc/allocation_listener.h" diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h index 7bdd9a58ec..81edb931cd 100644 --- a/openjdkjvmti/events.h +++ b/openjdkjvmti/events.h @@ -20,7 +20,10 @@ #include #include -#include "base/logging.h" +#include + +#include "base/macros.h" +#include "base/mutex.h" #include "jvmti.h" #include "thread.h" diff --git a/openjdkjvmti/jvmti_allocator.h b/openjdkjvmti/jvmti_allocator.h index 11af7b67e7..bd4c85bd7c 100644 --- a/openjdkjvmti/jvmti_allocator.h +++ b/openjdkjvmti/jvmti_allocator.h @@ -32,8 +32,9 @@ #ifndef ART_OPENJDKJVMTI_JVMTI_ALLOCATOR_H_ #define ART_OPENJDKJVMTI_JVMTI_ALLOCATOR_H_ -#include "base/logging.h" -#include "base/macros.h" +#include +#include + #include "jvmti.h" #include "ti_allocator.h" diff --git a/openjdkjvmti/jvmti_weak_table-inl.h b/openjdkjvmti/jvmti_weak_table-inl.h index 5d20946070..699004298e 100644 --- a/openjdkjvmti/jvmti_weak_table-inl.h +++ b/openjdkjvmti/jvmti_weak_table-inl.h @@ -36,8 +36,9 @@ #include +#include + #include "art_jvmti.h" -#include "base/logging.h" #include "gc/allocation_listener.h" #include "instrumentation.h" #include "jni_env_ext-inl.h" diff --git a/openjdkjvmti/ti_class_loader.cc b/openjdkjvmti/ti_class_loader.cc index b551b55e18..701ba80fd5 100644 --- a/openjdkjvmti/ti_class_loader.cc +++ b/openjdkjvmti/ti_class_loader.cc @@ -33,11 +33,11 @@ #include -#include "android-base/stringprintf.h" +#include +#include #include "art_field-inl.h" #include "art_jvmti.h" -#include "base/logging.h" #include "dex_file.h" #include "dex_file_types.h" #include "events-inl.h" diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index 5b125f6990..c18b354675 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -33,13 +33,13 @@ #include -#include "android-base/stringprintf.h" +#include +#include #include "art_field-inl.h" #include "art_jvmti.h" #include "art_method-inl.h" #include "base/array_ref.h" -#include "base/logging.h" #include "base/stringpiece.h" #include "class_linker-inl.h" #include "debugger.h" diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc index b7b81ce358..555c5a725b 100644 --- a/openjdkjvmti/ti_thread.cc +++ b/openjdkjvmti/ti_thread.cc @@ -31,10 +31,11 @@ #include "ti_thread.h" -#include "android-base/strings.h" +#include +#include + #include "art_field-inl.h" #include "art_jvmti.h" -#include "base/logging.h" #include "base/mutex.h" #include "events-inl.h" #include "gc/system_weak.h" diff --git a/profman/profman.cc b/profman/profman.cc index a5a5546323..0bef205de6 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -32,6 +32,7 @@ #include "android-base/strings.h" #include "base/dumpable.h" +#include "base/logging.h" // For InitLogging. #include "base/scoped_flock.h" #include "base/stringpiece.h" #include "base/time_utils.h" diff --git a/runtime/arch/arm/context_arm.h b/runtime/arch/arm/context_arm.h index fa9aa46d4d..b9802967fe 100644 --- a/runtime/arch/arm/context_arm.h +++ b/runtime/arch/arm/context_arm.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_ARCH_ARM_CONTEXT_ARM_H_ #define ART_RUNTIME_ARCH_ARM_CONTEXT_ARM_H_ +#include + #include "arch/context.h" -#include "base/logging.h" #include "base/macros.h" #include "registers_arm.h" diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc index ef2b34236f..315bf957cc 100644 --- a/runtime/arch/arm/fault_handler_arm.cc +++ b/runtime/arch/arm/fault_handler_arm.cc @@ -21,7 +21,7 @@ #include "art_method.h" #include "base/enums.h" #include "base/hex_dump.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "globals.h" #include "thread-current-inl.h" diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc index b789fc7481..801254fd30 100644 --- a/runtime/arch/arm/instruction_set_features_arm.cc +++ b/runtime/arch/arm/instruction_set_features_arm.cc @@ -25,10 +25,9 @@ #include -#include "android-base/stringprintf.h" -#include "android-base/strings.h" - -#include "base/logging.h" +#include +#include +#include #if defined(__arm__) extern "C" bool artCheckForArmSdivInstruction(); diff --git a/runtime/arch/arm/thread_arm.cc b/runtime/arch/arm/thread_arm.cc index ff4f81be0f..18585c7973 100644 --- a/runtime/arch/arm/thread_arm.cc +++ b/runtime/arch/arm/thread_arm.cc @@ -16,9 +16,10 @@ #include "thread.h" +#include + #include "asm_support_arm.h" #include "base/enums.h" -#include "base/logging.h" namespace art { diff --git a/runtime/arch/arm64/context_arm64.h b/runtime/arch/arm64/context_arm64.h index 36aded07c4..e64cfb86ea 100644 --- a/runtime/arch/arm64/context_arm64.h +++ b/runtime/arch/arm64/context_arm64.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_ARCH_ARM64_CONTEXT_ARM64_H_ #define ART_RUNTIME_ARCH_ARM64_CONTEXT_ARM64_H_ +#include + #include "arch/context.h" -#include "base/logging.h" #include "base/macros.h" #include "registers_arm64.h" diff --git a/runtime/arch/arm64/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc index d535c7e3c6..d282c8cfc0 100644 --- a/runtime/arch/arm64/fault_handler_arm64.cc +++ b/runtime/arch/arm64/fault_handler_arm64.cc @@ -21,7 +21,7 @@ #include "art_method.h" #include "base/enums.h" #include "base/hex_dump.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "globals.h" #include "registers_arm64.h" diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc index d830ccffbb..9e9cb16008 100644 --- a/runtime/arch/arm64/instruction_set_features_arm64.cc +++ b/runtime/arch/arm64/instruction_set_features_arm64.cc @@ -19,10 +19,10 @@ #include #include -#include "android-base/stringprintf.h" -#include "android-base/strings.h" +#include +#include +#include -#include "base/logging.h" #include "base/stl_util.h" namespace art { diff --git a/runtime/arch/arm64/thread_arm64.cc b/runtime/arch/arm64/thread_arm64.cc index 3483b704ea..19c4a6ac85 100644 --- a/runtime/arch/arm64/thread_arm64.cc +++ b/runtime/arch/arm64/thread_arm64.cc @@ -16,9 +16,10 @@ #include "thread.h" +#include + #include "asm_support_arm64.h" #include "base/enums.h" -#include "base/logging.h" namespace art { diff --git a/runtime/arch/code_offset.h b/runtime/arch/code_offset.h index ab04b1eaa7..8e8dde4c4c 100644 --- a/runtime/arch/code_offset.h +++ b/runtime/arch/code_offset.h @@ -19,8 +19,10 @@ #include +#include + #include "base/bit_utils.h" -#include "base/logging.h" +#include "base/macros.h" #include "instruction_set.h" namespace art { diff --git a/runtime/arch/instruction_set_features_test.cc b/runtime/arch/instruction_set_features_test.cc index 67e2f358c8..1e3275cc00 100644 --- a/runtime/arch/instruction_set_features_test.cc +++ b/runtime/arch/instruction_set_features_test.cc @@ -19,12 +19,11 @@ #include #ifdef ART_TARGET_ANDROID -#include "android-base/properties.h" +#include #endif -#include "android-base/stringprintf.h" - -#include "base/logging.h" +#include +#include namespace art { diff --git a/runtime/arch/mips/context_mips.h b/runtime/arch/mips/context_mips.h index 7dcff630d1..7e073b288a 100644 --- a/runtime/arch/mips/context_mips.h +++ b/runtime/arch/mips/context_mips.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_ARCH_MIPS_CONTEXT_MIPS_H_ #define ART_RUNTIME_ARCH_MIPS_CONTEXT_MIPS_H_ +#include + #include "arch/context.h" -#include "base/logging.h" #include "base/macros.h" #include "registers_mips.h" diff --git a/runtime/arch/mips/fault_handler_mips.cc b/runtime/arch/mips/fault_handler_mips.cc index 6dce54e5c5..f82dc08cb2 100644 --- a/runtime/arch/mips/fault_handler_mips.cc +++ b/runtime/arch/mips/fault_handler_mips.cc @@ -20,7 +20,7 @@ #include "art_method.h" #include "base/callee_save_type.h" #include "base/hex_dump.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "globals.h" #include "quick_method_frame_info_mips.h" diff --git a/runtime/arch/mips/instruction_set_features_mips.cc b/runtime/arch/mips/instruction_set_features_mips.cc index 6d4145bc98..952ed250d2 100644 --- a/runtime/arch/mips/instruction_set_features_mips.cc +++ b/runtime/arch/mips/instruction_set_features_mips.cc @@ -19,10 +19,9 @@ #include #include -#include "android-base/stringprintf.h" -#include "android-base/strings.h" +#include +#include -#include "base/logging.h" #include "base/stl_util.h" namespace art { diff --git a/runtime/arch/mips/instruction_set_features_mips.h b/runtime/arch/mips/instruction_set_features_mips.h index ee539edf3a..76bc639277 100644 --- a/runtime/arch/mips/instruction_set_features_mips.h +++ b/runtime/arch/mips/instruction_set_features_mips.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_ARCH_MIPS_INSTRUCTION_SET_FEATURES_MIPS_H_ #define ART_RUNTIME_ARCH_MIPS_INSTRUCTION_SET_FEATURES_MIPS_H_ +#include + #include "arch/instruction_set_features.h" -#include "base/logging.h" #include "base/macros.h" namespace art { diff --git a/runtime/arch/mips/registers_mips.h b/runtime/arch/mips/registers_mips.h index 57af150b33..f500b5881a 100644 --- a/runtime/arch/mips/registers_mips.h +++ b/runtime/arch/mips/registers_mips.h @@ -19,7 +19,8 @@ #include -#include "base/logging.h" +#include + #include "base/macros.h" #include "globals.h" diff --git a/runtime/arch/mips/thread_mips.cc b/runtime/arch/mips/thread_mips.cc index 0a9ab7aacd..0be7a7f4cb 100644 --- a/runtime/arch/mips/thread_mips.cc +++ b/runtime/arch/mips/thread_mips.cc @@ -16,9 +16,10 @@ #include "thread.h" +#include + #include "asm_support_mips.h" #include "base/enums.h" -#include "base/logging.h" namespace art { diff --git a/runtime/arch/mips64/context_mips64.h b/runtime/arch/mips64/context_mips64.h index 89fbf8ffc3..b2a6138471 100644 --- a/runtime/arch/mips64/context_mips64.h +++ b/runtime/arch/mips64/context_mips64.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_ARCH_MIPS64_CONTEXT_MIPS64_H_ #define ART_RUNTIME_ARCH_MIPS64_CONTEXT_MIPS64_H_ +#include + #include "arch/context.h" -#include "base/logging.h" #include "base/macros.h" #include "registers_mips64.h" diff --git a/runtime/arch/mips64/fault_handler_mips64.cc b/runtime/arch/mips64/fault_handler_mips64.cc index bdce520937..ba6fff05ad 100644 --- a/runtime/arch/mips64/fault_handler_mips64.cc +++ b/runtime/arch/mips64/fault_handler_mips64.cc @@ -21,7 +21,7 @@ #include "art_method.h" #include "base/callee_save_type.h" #include "base/hex_dump.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "globals.h" #include "quick_method_frame_info_mips64.h" diff --git a/runtime/arch/mips64/registers_mips64.h b/runtime/arch/mips64/registers_mips64.h index 30de2cc009..bca260a6a4 100644 --- a/runtime/arch/mips64/registers_mips64.h +++ b/runtime/arch/mips64/registers_mips64.h @@ -19,7 +19,8 @@ #include -#include "base/logging.h" +#include + #include "base/macros.h" #include "globals.h" diff --git a/runtime/arch/mips64/thread_mips64.cc b/runtime/arch/mips64/thread_mips64.cc index 3ce5e50d57..c1c390beeb 100644 --- a/runtime/arch/mips64/thread_mips64.cc +++ b/runtime/arch/mips64/thread_mips64.cc @@ -16,9 +16,10 @@ #include "thread.h" +#include + #include "asm_support_mips64.h" #include "base/enums.h" -#include "base/logging.h" namespace art { diff --git a/runtime/arch/x86/context_x86.h b/runtime/arch/x86/context_x86.h index 303dfe361c..0ebb22bd6d 100644 --- a/runtime/arch/x86/context_x86.h +++ b/runtime/arch/x86/context_x86.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_ARCH_X86_CONTEXT_X86_H_ #define ART_RUNTIME_ARCH_X86_CONTEXT_X86_H_ +#include + #include "arch/context.h" -#include "base/logging.h" #include "base/macros.h" #include "registers_x86.h" diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc index 527332fe9a..e6a91247cb 100644 --- a/runtime/arch/x86/fault_handler_x86.cc +++ b/runtime/arch/x86/fault_handler_x86.cc @@ -21,7 +21,7 @@ #include "art_method.h" #include "base/enums.h" #include "base/hex_dump.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "base/safe_copy.h" #include "globals.h" diff --git a/runtime/arch/x86/instruction_set_features_x86.cc b/runtime/arch/x86/instruction_set_features_x86.cc index ea5a90d8ee..98462512da 100644 --- a/runtime/arch/x86/instruction_set_features_x86.cc +++ b/runtime/arch/x86/instruction_set_features_x86.cc @@ -19,11 +19,11 @@ #include #include -#include "android-base/stringprintf.h" -#include "android-base/strings.h" +#include +#include +#include #include "arch/x86_64/instruction_set_features_x86_64.h" -#include "base/logging.h" namespace art { diff --git a/runtime/arch/x86/registers_x86.h b/runtime/arch/x86/registers_x86.h index 23027ed7d7..ded3520c76 100644 --- a/runtime/arch/x86/registers_x86.h +++ b/runtime/arch/x86/registers_x86.h @@ -19,7 +19,8 @@ #include -#include "base/logging.h" +#include + #include "base/macros.h" #include "globals.h" diff --git a/runtime/arch/x86_64/context_x86_64.h b/runtime/arch/x86_64/context_x86_64.h index f8e2845983..d242693f81 100644 --- a/runtime/arch/x86_64/context_x86_64.h +++ b/runtime/arch/x86_64/context_x86_64.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_ARCH_X86_64_CONTEXT_X86_64_H_ #define ART_RUNTIME_ARCH_X86_64_CONTEXT_X86_64_H_ +#include + #include "arch/context.h" -#include "base/logging.h" #include "base/macros.h" #include "registers_x86_64.h" diff --git a/runtime/arch/x86_64/registers_x86_64.h b/runtime/arch/x86_64/registers_x86_64.h index dda1d5f569..4f2243170e 100644 --- a/runtime/arch/x86_64/registers_x86_64.h +++ b/runtime/arch/x86_64/registers_x86_64.h @@ -19,7 +19,8 @@ #include -#include "base/logging.h" +#include + #include "base/macros.h" #include "globals.h" diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h index 4a328e8d60..48a89f1304 100644 --- a/runtime/art_field-inl.h +++ b/runtime/art_field-inl.h @@ -19,7 +19,8 @@ #include "art_field.h" -#include "base/logging.h" +#include + #include "class_linker.h" #include "dex_file-inl.h" #include "gc/accounting/card_table-inl.h" diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 31abf94889..327081f67a 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -21,7 +21,6 @@ #include "art_field.h" #include "base/callee_save_type.h" -#include "base/logging.h" #include "class_linker-inl.h" #include "code_item_accessors-inl.h" #include "common_throws.h" diff --git a/runtime/art_method.h b/runtime/art_method.h index 0a592e0528..4718150400 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -19,11 +19,14 @@ #include +#include + #include "base/bit_utils.h" #include "base/casts.h" #include "base/enums.h" #include "base/iteration_range.h" -#include "base/logging.h" +#include "base/logging.h" // For RUNTIME_DEBUG_FLAG. +#include "base/macros.h" #include "dex_file.h" #include "dex_instruction_iterator.h" #include "gc_root.h" diff --git a/runtime/atomic.h b/runtime/atomic.h index d8621cc2e6..ec3eb6d609 100644 --- a/runtime/atomic.h +++ b/runtime/atomic.h @@ -22,8 +22,9 @@ #include #include +#include + #include "arch/instruction_set.h" -#include "base/logging.h" #include "base/macros.h" namespace art { diff --git a/runtime/barrier.cc b/runtime/barrier.cc index 9bcda35a9d..8d3bc0e095 100644 --- a/runtime/barrier.cc +++ b/runtime/barrier.cc @@ -16,7 +16,7 @@ #include "barrier.h" -#include "base/logging.h" +#include "base/logging.h" // Required for gAborting. #include "base/mutex.h" #include "base/time_utils.h" #include "thread.h" diff --git a/runtime/base/allocator.cc b/runtime/base/allocator.cc index bb006389fa..2da88c3830 100644 --- a/runtime/base/allocator.cc +++ b/runtime/base/allocator.cc @@ -19,8 +19,9 @@ #include #include +#include + #include "atomic.h" -#include "base/logging.h" namespace art { diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc index 2e35f8ac4f..cc413c5ab9 100644 --- a/runtime/base/arena_allocator.cc +++ b/runtime/base/arena_allocator.cc @@ -23,7 +23,8 @@ #include #include -#include "logging.h" +#include + #include "mem_map.h" #include "mutex.h" #include "systrace.h" diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h index a327cb0a8b..9e03658aef 100644 --- a/runtime/base/arena_allocator.h +++ b/runtime/base/arena_allocator.h @@ -20,11 +20,11 @@ #include #include -#include "base/bit_utils.h" -#include "base/dchecked_vector.h" -#include "base/memory_tool.h" +#include "bit_utils.h" +#include "dchecked_vector.h" #include "debug_stack.h" #include "macros.h" +#include "memory_tool.h" #include "mutex.h" namespace art { diff --git a/runtime/base/arena_object.h b/runtime/base/arena_object.h index ed00babd62..06884c23d4 100644 --- a/runtime/base/arena_object.h +++ b/runtime/base/arena_object.h @@ -17,8 +17,10 @@ #ifndef ART_RUNTIME_BASE_ARENA_OBJECT_H_ #define ART_RUNTIME_BASE_ARENA_OBJECT_H_ -#include "base/arena_allocator.h" -#include "base/logging.h" +#include + +#include "arena_allocator.h" +#include "macros.h" #include "scoped_arena_allocator.h" namespace art { diff --git a/runtime/base/array_ref.h b/runtime/base/array_ref.h index 630a036f3d..ef86512cf7 100644 --- a/runtime/base/array_ref.h +++ b/runtime/base/array_ref.h @@ -20,7 +20,7 @@ #include #include -#include "base/logging.h" +#include namespace art { diff --git a/runtime/base/bit_field.h b/runtime/base/bit_field.h index a80ca28d2e..86007d6a35 100644 --- a/runtime/base/bit_field.h +++ b/runtime/base/bit_field.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_BASE_BIT_FIELD_H_ #define ART_RUNTIME_BASE_BIT_FIELD_H_ +#include + #include "globals.h" -#include "logging.h" namespace art { diff --git a/runtime/base/bit_utils.h b/runtime/base/bit_utils.h index 5d836545e9..34cddbff6a 100644 --- a/runtime/base/bit_utils.h +++ b/runtime/base/bit_utils.h @@ -20,7 +20,8 @@ #include #include -#include "base/logging.h" +#include + #include "base/stl_util_identity.h" namespace art { diff --git a/runtime/base/bit_utils_iterator.h b/runtime/base/bit_utils_iterator.h index 8514de6b75..2d3d0508cc 100644 --- a/runtime/base/bit_utils_iterator.h +++ b/runtime/base/bit_utils_iterator.h @@ -21,9 +21,10 @@ #include #include +#include + #include "base/bit_utils.h" #include "base/iteration_range.h" -#include "base/logging.h" #include "base/stl_util.h" namespace art { diff --git a/runtime/base/bit_vector-inl.h b/runtime/base/bit_vector-inl.h index 0e67f77e19..e67d4e25eb 100644 --- a/runtime/base/bit_vector-inl.h +++ b/runtime/base/bit_vector-inl.h @@ -17,9 +17,11 @@ #ifndef ART_RUNTIME_BASE_BIT_VECTOR_INL_H_ #define ART_RUNTIME_BASE_BIT_VECTOR_INL_H_ -#include "base/bit_utils.h" #include "bit_vector.h" -#include "logging.h" + +#include + +#include "base/bit_utils.h" namespace art { diff --git a/runtime/base/bounded_fifo.h b/runtime/base/bounded_fifo.h index 7bcd382022..1520770fe6 100644 --- a/runtime/base/bounded_fifo.h +++ b/runtime/base/bounded_fifo.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_BASE_BOUNDED_FIFO_H_ #define ART_RUNTIME_BASE_BOUNDED_FIFO_H_ +#include + #include "base/bit_utils.h" -#include "base/logging.h" namespace art { diff --git a/runtime/base/casts.h b/runtime/base/casts.h index 92c493ace7..ac1a10c24f 100644 --- a/runtime/base/casts.h +++ b/runtime/base/casts.h @@ -24,8 +24,7 @@ #include #include -#include "base/logging.h" -#include "base/macros.h" +#include namespace art { diff --git a/runtime/base/dchecked_vector.h b/runtime/base/dchecked_vector.h index 77f0ea2b7c..7236ac301a 100644 --- a/runtime/base/dchecked_vector.h +++ b/runtime/base/dchecked_vector.h @@ -21,7 +21,7 @@ #include #include -#include "base/logging.h" +#include namespace art { diff --git a/runtime/base/debug_stack.h b/runtime/base/debug_stack.h index 886065db30..1331e10a02 100644 --- a/runtime/base/debug_stack.h +++ b/runtime/base/debug_stack.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_BASE_DEBUG_STACK_H_ #define ART_RUNTIME_BASE_DEBUG_STACK_H_ -#include "base/logging.h" -#include "base/macros.h" +#include +#include + #include "globals.h" namespace art { diff --git a/runtime/base/file_magic.cc b/runtime/base/file_magic.cc index dffb9b43a1..ac2e184163 100644 --- a/runtime/base/file_magic.cc +++ b/runtime/base/file_magic.cc @@ -20,9 +20,9 @@ #include #include -#include "android-base/stringprintf.h" +#include +#include -#include "base/logging.h" #include "base/unix_file/fd_file.h" #include "dex_file.h" diff --git a/runtime/base/file_utils.h b/runtime/base/file_utils.h index 007f3b443d..e4555ad3cb 100644 --- a/runtime/base/file_utils.h +++ b/runtime/base/file_utils.h @@ -21,13 +21,14 @@ #include +#include + #include "arch/instruction_set.h" -#include "base/logging.h" namespace art { bool ReadFileToString(const std::string& file_name, std::string* result); -bool PrintFileToLog(const std::string& file_name, LogSeverity level); +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/hash_set.h b/runtime/base/hash_set.h index c743342a98..47e6d93346 100644 --- a/runtime/base/hash_set.h +++ b/runtime/base/hash_set.h @@ -25,8 +25,10 @@ #include #include +#include + #include "bit_utils.h" -#include "logging.h" +#include "macros.h" namespace art { diff --git a/runtime/base/histogram-inl.h b/runtime/base/histogram-inl.h index be2092040d..3ce0140c84 100644 --- a/runtime/base/histogram-inl.h +++ b/runtime/base/histogram-inl.h @@ -24,6 +24,8 @@ #include "histogram.h" +#include + #include "base/bit_utils.h" #include "base/time_utils.h" #include "utils.h" diff --git a/runtime/base/histogram.h b/runtime/base/histogram.h index e0c921e408..7544a9c918 100644 --- a/runtime/base/histogram.h +++ b/runtime/base/histogram.h @@ -19,7 +19,7 @@ #include #include -#include "base/logging.h" +#include namespace art { diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index c0cf4872de..9a037b50bf 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -24,8 +24,10 @@ #include #include +#include + #include "atomic.h" -#include "base/logging.h" +#include "base/logging.h" // For gAborting. #include "base/macros.h" #include "globals.h" diff --git a/runtime/base/scoped_arena_allocator.h b/runtime/base/scoped_arena_allocator.h index 8f50fd443b..35e337f0d6 100644 --- a/runtime/base/scoped_arena_allocator.h +++ b/runtime/base/scoped_arena_allocator.h @@ -17,10 +17,11 @@ #ifndef ART_RUNTIME_BASE_SCOPED_ARENA_ALLOCATOR_H_ #define ART_RUNTIME_BASE_SCOPED_ARENA_ALLOCATOR_H_ +#include + #include "arena_allocator.h" #include "debug_stack.h" #include "globals.h" -#include "logging.h" #include "macros.h" namespace art { diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc index b8df6897e4..514b97bfb1 100644 --- a/runtime/base/scoped_flock.cc +++ b/runtime/base/scoped_flock.cc @@ -19,9 +19,9 @@ #include #include -#include "android-base/stringprintf.h" +#include +#include -#include "base/logging.h" #include "base/unix_file/fd_file.h" namespace art { diff --git a/runtime/base/scoped_flock.h b/runtime/base/scoped_flock.h index 1b933c07f3..db6c819c6c 100644 --- a/runtime/base/scoped_flock.h +++ b/runtime/base/scoped_flock.h @@ -20,9 +20,8 @@ #include #include -#include "android-base/unique_fd.h" +#include -#include "base/logging.h" #include "base/macros.h" #include "base/unix_file/fd_file.h" #include "os.h" diff --git a/runtime/base/stl_util.h b/runtime/base/stl_util.h index b27297241d..02f37652cf 100644 --- a/runtime/base/stl_util.h +++ b/runtime/base/stl_util.h @@ -21,7 +21,7 @@ #include #include -#include "base/logging.h" +#include namespace art { diff --git a/runtime/base/stringpiece.cc b/runtime/base/stringpiece.cc index 2570bad85d..672431cf9d 100644 --- a/runtime/base/stringpiece.cc +++ b/runtime/base/stringpiece.cc @@ -19,7 +19,7 @@ #include #include -#include "logging.h" +#include namespace art { diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc index b8d6931a83..23ec3e1aea 100644 --- a/runtime/base/timing_logger.cc +++ b/runtime/base/timing_logger.cc @@ -18,8 +18,9 @@ #include "timing_logger.h" +#include + #include "base/histogram-inl.h" -#include "base/logging.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/time_utils.h" diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc index 792c58172e..37f239da23 100644 --- a/runtime/base/unix_file/fd_file.cc +++ b/runtime/base/unix_file/fd_file.cc @@ -23,7 +23,7 @@ #include -#include "base/logging.h" +#include // Includes needed for FdFile::Copy(). #ifdef __linux__ diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index c3dd702446..0b9bf225d4 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -21,11 +21,12 @@ #include -#include "android-base/stringprintf.h" +#include +#include #include "art_field-inl.h" #include "art_method-inl.h" -#include "base/logging.h" +#include "base/macros.h" #include "base/to_str.h" #include "class_linker-inl.h" #include "class_linker.h" diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index cd52bb6551..9b55ca50b2 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -18,11 +18,11 @@ #include -#include "android-base/stringprintf.h" +#include +#include #include "art_field-inl.h" #include "art_method-inl.h" -#include "base/logging.h" #include "class_linker-inl.h" #include "dex_file-inl.h" #include "dex_instruction-inl.h" diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h index 18809683cc..4025a1a0f9 100644 --- a/runtime/dex_file-inl.h +++ b/runtime/dex_file-inl.h @@ -19,7 +19,6 @@ #include "base/bit_utils.h" #include "base/casts.h" -#include "base/logging.h" #include "base/stringpiece.h" #include "cdex/compact_dex_file.h" #include "dex_file.h" diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index af79207834..2d02415ba4 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -29,7 +29,6 @@ #include "android-base/stringprintf.h" #include "base/enums.h" -#include "base/logging.h" #include "base/stl_util.h" #include "dex_file-inl.h" #include "leb128.h" diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 944a30849f..561b3678b4 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -21,8 +21,10 @@ #include #include +#include + #include "base/iteration_range.h" -#include "base/logging.h" +#include "base/macros.h" #include "base/value_object.h" #include "dex_file_types.h" #include "dex_instruction_iterator.h" diff --git a/runtime/dex_file_layout.h b/runtime/dex_file_layout.h index 4c960c3ff5..9fac5f8458 100644 --- a/runtime/dex_file_layout.h +++ b/runtime/dex_file_layout.h @@ -21,7 +21,7 @@ #include #include -#include "base/logging.h" +#include namespace art { diff --git a/runtime/dex_file_tracking_registrar.cc b/runtime/dex_file_tracking_registrar.cc index 874d8ea905..4de43760d4 100644 --- a/runtime/dex_file_tracking_registrar.cc +++ b/runtime/dex_file_tracking_registrar.cc @@ -19,6 +19,8 @@ #include #include +#include + // For dex tracking through poisoning. Note: Requires forcing sanitization. This is the reason for // the ifdefs and early include. #ifdef ART_DEX_FILE_ACCESS_TRACKING @@ -28,7 +30,6 @@ #endif #include "base/memory_tool.h" -#include "base/logging.h" #include "dex_file-inl.h" namespace art { diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h index 4041820616..3ced6920e2 100644 --- a/runtime/dex_instruction.h +++ b/runtime/dex_instruction.h @@ -17,7 +17,8 @@ #ifndef ART_RUNTIME_DEX_INSTRUCTION_H_ #define ART_RUNTIME_DEX_INSTRUCTION_H_ -#include "base/logging.h" +#include + #include "base/macros.h" #include "globals.h" diff --git a/runtime/dex_instruction_iterator.h b/runtime/dex_instruction_iterator.h index be583a2533..eabe009a0b 100644 --- a/runtime/dex_instruction_iterator.h +++ b/runtime/dex_instruction_iterator.h @@ -19,8 +19,10 @@ #include +#include + #include "dex_instruction.h" -#include "base/logging.h" +#include "base/macros.h" namespace art { diff --git a/runtime/dex_to_dex_decompiler.cc b/runtime/dex_to_dex_decompiler.cc index a5ebaded5f..a4e4fb50c5 100644 --- a/runtime/dex_to_dex_decompiler.cc +++ b/runtime/dex_to_dex_decompiler.cc @@ -16,7 +16,9 @@ #include "dex_to_dex_decompiler.h" -#include "base/logging.h" +#include + +#include "base/macros.h" #include "base/mutex.h" #include "bytecode_utils.h" #include "dex_file-inl.h" diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index afe4eeb059..d057ff3b1a 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -25,7 +25,6 @@ #include "android-base/strings.h" #include "arch/instruction_set.h" -#include "base/logging.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "elf_file_impl.h" diff --git a/runtime/elf_utils.h b/runtime/elf_utils.h index 418d937b12..0cac8e8d02 100644 --- a/runtime/elf_utils.h +++ b/runtime/elf_utils.h @@ -19,11 +19,11 @@ #include +#include + // Explicitly include our own elf.h to avoid Linux and other dependencies. #include "./elf.h" -#include "base/logging.h" - namespace art { // Architecture dependent flags for the ELF header. diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc index 7ec360a93c..780e221129 100644 --- a/runtime/entrypoints/jni/jni_entrypoints.cc +++ b/runtime/entrypoints/jni/jni_entrypoints.cc @@ -14,8 +14,9 @@ * limitations under the License. */ +#include + #include "art_method-inl.h" -#include "base/logging.h" #include "entrypoints/entrypoint_utils.h" #include "java_vm_ext.h" #include "mirror/object-inl.h" diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h index 8acaa90053..8c90800463 100644 --- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h +++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h @@ -17,7 +17,7 @@ #ifndef ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_DEFAULT_INIT_ENTRYPOINTS_H_ #define ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_DEFAULT_INIT_ENTRYPOINTS_H_ -#include "base/logging.h" +#include "base/logging.h" // FOR VLOG_IS_ON. #include "entrypoints/jni/jni_entrypoints.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "quick_alloc_entrypoints.h" diff --git a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc index 5f40711753..c782c9c949 100644 --- a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "base/logging.h" +#include "base/logging.h" // For VLOG_IS_ON. #include "base/mutex.h" #include "base/systrace.h" #include "callee_save_frame.h" diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc index 29a62c86ee..b13b6fbcae 100644 --- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc @@ -14,9 +14,10 @@ * limitations under the License. */ +#include + #include "art_method-inl.h" #include "base/casts.h" -#include "base/logging.h" #include "entrypoints/entrypoint_utils-inl.h" #include "indirect_reference_table.h" #include "mirror/object-inl.h" diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h index 3d0e8172b6..e5b5694413 100644 --- a/runtime/gc/accounting/atomic_stack.h +++ b/runtime/gc/accounting/atomic_stack.h @@ -23,8 +23,9 @@ #include #include +#include + #include "atomic.h" -#include "base/logging.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 cd3923abbe..ca6b4794de 100644 --- a/runtime/gc/accounting/bitmap-inl.h +++ b/runtime/gc/accounting/bitmap-inl.h @@ -21,9 +21,10 @@ #include +#include + #include "atomic.h" #include "base/bit_utils.h" -#include "base/logging.h" namespace art { namespace gc { diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h index 6ff53597e4..5f2f2dda42 100644 --- a/runtime/gc/accounting/card_table-inl.h +++ b/runtime/gc/accounting/card_table-inl.h @@ -17,10 +17,12 @@ #ifndef ART_RUNTIME_GC_ACCOUNTING_CARD_TABLE_INL_H_ #define ART_RUNTIME_GC_ACCOUNTING_CARD_TABLE_INL_H_ +#include "card_table.h" + +#include + #include "atomic.h" #include "base/bit_utils.h" -#include "base/logging.h" -#include "card_table.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 01b5896650..934e57a61b 100644 --- a/runtime/gc/accounting/card_table.cc +++ b/runtime/gc/accounting/card_table.cc @@ -18,7 +18,6 @@ #include -#include "base/logging.h" #include "base/systrace.h" #include "card_table-inl.h" #include "gc/heap.h" diff --git a/runtime/gc/accounting/heap_bitmap.h b/runtime/gc/accounting/heap_bitmap.h index 4237e7ee3f..c997f8dbfc 100644 --- a/runtime/gc/accounting/heap_bitmap.h +++ b/runtime/gc/accounting/heap_bitmap.h @@ -17,8 +17,11 @@ #ifndef ART_RUNTIME_GC_ACCOUNTING_HEAP_BITMAP_H_ #define ART_RUNTIME_GC_ACCOUNTING_HEAP_BITMAP_H_ +#include + #include "base/allocator.h" -#include "base/logging.h" +#include "base/macros.h" +#include "base/mutex.h" #include "space_bitmap.h" namespace art { diff --git a/runtime/gc/accounting/space_bitmap-inl.h b/runtime/gc/accounting/space_bitmap-inl.h index b37dd965fc..ba833693f6 100644 --- a/runtime/gc/accounting/space_bitmap-inl.h +++ b/runtime/gc/accounting/space_bitmap-inl.h @@ -21,9 +21,10 @@ #include +#include + #include "atomic.h" #include "base/bit_utils.h" -#include "base/logging.h" namespace art { namespace gc { diff --git a/runtime/gc/allocator/dlmalloc.cc b/runtime/gc/allocator/dlmalloc.cc index ef916f8745..65062208d6 100644 --- a/runtime/gc/allocator/dlmalloc.cc +++ b/runtime/gc/allocator/dlmalloc.cc @@ -16,8 +16,9 @@ #include "dlmalloc.h" +#include + #include "base/bit_utils.h" -#include "base/logging.h" // ART specific morecore implementation defined in space.cc. static void* art_heap_morecore(void* m, intptr_t increment); diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h index 2c90773b8f..6e5cf0ede8 100644 --- a/runtime/gc/allocator/rosalloc.h +++ b/runtime/gc/allocator/rosalloc.h @@ -26,9 +26,10 @@ #include #include +#include + #include "base/allocator.h" #include "base/bit_utils.h" -#include "base/logging.h" #include "base/mutex.h" #include "globals.h" #include "thread.h" diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc index c5a341fc80..fa34270d95 100644 --- a/runtime/gc/collector/garbage_collector.cc +++ b/runtime/gc/collector/garbage_collector.cc @@ -22,7 +22,7 @@ #include "base/dumpable.h" #include "base/histogram-inl.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG_IS_ON. #include "base/mutex-inl.h" #include "base/systrace.h" #include "base/time_utils.h" diff --git a/runtime/gc/collector/immune_spaces.cc b/runtime/gc/collector/immune_spaces.cc index 1024050409..3b5961899f 100644 --- a/runtime/gc/collector/immune_spaces.cc +++ b/runtime/gc/collector/immune_spaces.cc @@ -19,6 +19,7 @@ #include #include +#include "base/logging.h" // For VLOG. #include "gc/space/space-inl.h" #include "mirror/object.h" #include "oat_file.h" diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc index aef98dee58..34cc129ce8 100644 --- a/runtime/gc/collector/mark_compact.cc +++ b/runtime/gc/collector/mark_compact.cc @@ -16,7 +16,9 @@ #include "mark_compact.h" -#include "base/logging.h" +#include + +#include "base/macros.h" #include "base/mutex-inl.h" #include "base/timing_logger.h" #include "gc/accounting/heap_bitmap-inl.h" diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index c6caf4b08e..fdfe949265 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -25,7 +25,7 @@ #include "base/bounded_fifo.h" #include "base/enums.h" #include "base/file_utils.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "base/mutex-inl.h" #include "base/systrace.h" diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index 9fb37b6138..3150781a5a 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -22,7 +22,7 @@ #include #include -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "base/mutex-inl.h" #include "base/timing_logger.h" diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc index 871208037a..d88fcdcc95 100644 --- a/runtime/gc/gc_cause.cc +++ b/runtime/gc/gc_cause.cc @@ -15,7 +15,10 @@ */ #include "gc_cause.h" -#include "base/logging.h" + +#include + +#include "base/macros.h" #include "globals.h" #include diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index ac0d82e12a..10bae7114e 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -22,10 +22,13 @@ #include #include +#include + #include "allocator_type.h" #include "arch/instruction_set.h" #include "atomic.h" -#include "base/logging.h" +#include "base/logging.h" // For DECLARE_RUNTIME_DEBUG_FLAG. +#include "base/macros.h" #include "base/mutex.h" #include "base/time_utils.h" #include "gc/collector/gc_type.h" diff --git a/runtime/gc/space/image_space_fs.h b/runtime/gc/space/image_space_fs.h index a0ecb95ac7..6ce81e9209 100644 --- a/runtime/gc/space/image_space_fs.h +++ b/runtime/gc/space/image_space_fs.h @@ -23,7 +23,7 @@ #include "android-base/stringprintf.h" #include "base/file_utils.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "base/unix_file/fd_file.h" #include "globals.h" diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc index 45f4f82448..d2efb102e9 100644 --- a/runtime/gc/space/large_object_space.cc +++ b/runtime/gc/space/large_object_space.cc @@ -20,7 +20,9 @@ #include -#include "base/logging.h" +#include + +#include "base/macros.h" #include "base/memory_tool.h" #include "base/mutex-inl.h" #include "base/stl_util.h" diff --git a/runtime/gc/space/space.cc b/runtime/gc/space/space.cc index 74ce273abf..2c6afa7eb8 100644 --- a/runtime/gc/space/space.cc +++ b/runtime/gc/space/space.cc @@ -16,7 +16,9 @@ #include "space.h" -#include "base/logging.h" +#include + +#include "base/macros.h" #include "gc/accounting/heap_bitmap.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/heap.h" diff --git a/runtime/handle.h b/runtime/handle.h index ccff575495..18e503d1de 100644 --- a/runtime/handle.h +++ b/runtime/handle.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_HANDLE_H_ #define ART_RUNTIME_HANDLE_H_ +#include + #include "base/casts.h" -#include "base/logging.h" #include "base/macros.h" #include "base/mutex.h" #include "base/value_object.h" diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h index f248a118e9..28a230291d 100644 --- a/runtime/handle_scope.h +++ b/runtime/handle_scope.h @@ -19,8 +19,9 @@ #include +#include + #include "base/enums.h" -#include "base/logging.h" #include "base/macros.h" #include "base/mutex.h" #include "handle.h" diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index 6a1a8c7271..f4fc85b8e5 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -36,11 +36,13 @@ #include -#include "android-base/stringprintf.h" +#include +#include #include "art_field-inl.h" #include "art_method-inl.h" -#include "base/logging.h" +#include "base/macros.h" +#include "base/mutex.h" #include "base/time_utils.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" diff --git a/runtime/indenter.h b/runtime/indenter.h index 69b973252d..6361dd2092 100644 --- a/runtime/indenter.h +++ b/runtime/indenter.h @@ -20,7 +20,8 @@ #include #include -#include "base/logging.h" +#include + #include "base/macros.h" namespace art { diff --git a/runtime/index_bss_mapping.h b/runtime/index_bss_mapping.h index d9f4e663a7..dcbc05c195 100644 --- a/runtime/index_bss_mapping.h +++ b/runtime/index_bss_mapping.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_INDEX_BSS_MAPPING_H_ #define ART_RUNTIME_INDEX_BSS_MAPPING_H_ +#include + #include "base/bit_utils.h" -#include "base/logging.h" namespace art { diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h index 6675099523..00184e2ed0 100644 --- a/runtime/indirect_reference_table.h +++ b/runtime/indirect_reference_table.h @@ -23,8 +23,10 @@ #include #include +#include + #include "base/bit_utils.h" -#include "base/logging.h" +#include "base/macros.h" #include "base/mutex.h" #include "gc_root.h" #include "obj_ptr.h" diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index f097bc71b9..9a0877f8ac 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -26,13 +26,14 @@ #include #include -#include "android-base/stringprintf.h" +#include +#include #include "art_field-inl.h" #include "art_method-inl.h" #include "base/enums.h" -#include "base/logging.h" #include "base/macros.h" +#include "base/mutex.h" #include "class_linker-inl.h" #include "common_dex_operations.h" #include "common_throws.h" diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 31e7986770..dece830f1a 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -26,12 +26,12 @@ #include #include -#include "android-base/stringprintf.h" +#include +#include #include "art_method-inl.h" #include "base/casts.h" #include "base/enums.h" -#include "base/logging.h" #include "base/macros.h" #include "class_linker.h" #include "common_throws.h" diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h index aeeda1e791..d712b10bc2 100644 --- a/runtime/jdwp/jdwp.h +++ b/runtime/jdwp/jdwp.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_JDWP_JDWP_H_ #include "atomic.h" +#include "base/logging.h" // For VLOG. #include "base/mutex.h" #include "jdwp/jdwp_bits.h" #include "jdwp/jdwp_constants.h" diff --git a/runtime/jdwp/jdwp_adb.cc b/runtime/jdwp/jdwp_adb.cc index ede4f9edb7..d68430f3ac 100644 --- a/runtime/jdwp/jdwp_adb.cc +++ b/runtime/jdwp/jdwp_adb.cc @@ -22,7 +22,7 @@ #include "android-base/stringprintf.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "jdwp/jdwp_priv.h" #include "thread-current-inl.h" diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index 41cb64276c..9409b7661f 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -25,7 +25,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "debugger.h" #include "jdwp/jdwp_constants.h" #include "jdwp/jdwp_expand_buf.h" diff --git a/runtime/jdwp/jdwp_expand_buf.cc b/runtime/jdwp/jdwp_expand_buf.cc index f0b8c918dc..4b4ca0e4a3 100644 --- a/runtime/jdwp/jdwp_expand_buf.cc +++ b/runtime/jdwp/jdwp_expand_buf.cc @@ -23,7 +23,8 @@ #include #include -#include "base/logging.h" +#include + #include "jdwp/jdwp.h" #include "jdwp/jdwp_bits.h" diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc index 618332b7ef..89eef88b88 100644 --- a/runtime/jdwp/jdwp_handler.cc +++ b/runtime/jdwp/jdwp_handler.cc @@ -24,7 +24,7 @@ #include "atomic.h" #include "base/hex_dump.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "debugger.h" #include "jdwp/jdwp_constants.h" diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc index e6c60685cc..e275554721 100644 --- a/runtime/jdwp/jdwp_main.cc +++ b/runtime/jdwp/jdwp_main.cc @@ -23,7 +23,7 @@ #include "android-base/stringprintf.h" #include "atomic.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/time_utils.h" #include "debugger.h" #include "jdwp/jdwp_priv.h" diff --git a/runtime/jdwp/jdwp_socket.cc b/runtime/jdwp/jdwp_socket.cc index 97662f0727..673a942517 100644 --- a/runtime/jdwp/jdwp_socket.cc +++ b/runtime/jdwp/jdwp_socket.cc @@ -28,7 +28,7 @@ #include "android-base/stringprintf.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "jdwp/jdwp_priv.h" namespace art { diff --git a/runtime/jit/debugger_interface.cc b/runtime/jit/debugger_interface.cc index 135d9b1f51..4d1c85a1c2 100644 --- a/runtime/jit/debugger_interface.cc +++ b/runtime/jit/debugger_interface.cc @@ -16,7 +16,8 @@ #include "debugger_interface.h" -#include "base/logging.h" +#include + #include "base/mutex.h" #include "thread-current-inl.h" #include "thread.h" diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 0d95bc6e64..a6aa9167eb 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -20,7 +20,7 @@ #include "art_method-inl.h" #include "base/enums.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/memory_tool.h" #include "debugger.h" #include "entrypoints/runtime_asm_entrypoints.h" diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index bb0048de0b..7754777eb6 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -35,6 +35,7 @@ #include "base/arena_allocator.h" #include "base/dumpable.h" #include "base/file_utils.h" +#include "base/logging.h" // For VLOG. #include "base/mutex.h" #include "base/scoped_flock.h" #include "base/stl_util.h" diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 1e55158a34..48fc5f730b 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -28,7 +28,7 @@ #include "atomic.h" #include "base/allocator.h" #include "base/enums.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/mutex.h" #include "base/stl_util.h" #include "class_linker-inl.h" diff --git a/runtime/leb128.h b/runtime/leb128.h index 31459af3a0..2bfed7f539 100644 --- a/runtime/leb128.h +++ b/runtime/leb128.h @@ -19,8 +19,10 @@ #include +#include + #include "base/bit_utils.h" -#include "base/logging.h" +#include "base/macros.h" #include "globals.h" namespace art { diff --git a/runtime/lock_word.h b/runtime/lock_word.h index b9aa0b793b..fac1a7597d 100644 --- a/runtime/lock_word.h +++ b/runtime/lock_word.h @@ -20,8 +20,9 @@ #include #include +#include + #include "base/bit_utils.h" -#include "base/logging.h" #include "read_barrier.h" namespace art { diff --git a/runtime/managed_stack.h b/runtime/managed_stack.h index 07078ecb13..d1c230fd8f 100644 --- a/runtime/managed_stack.h +++ b/runtime/managed_stack.h @@ -21,7 +21,8 @@ #include #include -#include "base/logging.h" +#include + #include "base/macros.h" #include "base/mutex.h" #include "base/bit_utils.h" diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index f5d12d5f7b..8abf8a6003 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -35,6 +35,7 @@ #include "base/allocator.h" #include "base/bit_utils.h" #include "base/file_utils.h" +#include "base/logging.h" // For VLOG_IS_ON. #include "base/memory_tool.h" #include "globals.h" #include "utils.h" diff --git a/runtime/memory_region.cc b/runtime/memory_region.cc index 13cc5c99bc..862ff73639 100644 --- a/runtime/memory_region.cc +++ b/runtime/memory_region.cc @@ -19,9 +19,6 @@ #include #include -#include "base/logging.h" -#include "globals.h" - namespace art { void MemoryRegion::CopyFrom(size_t offset, const MemoryRegion& from) const { diff --git a/runtime/memory_region.h b/runtime/memory_region.h index 7cf5d49d70..23e0aecbda 100644 --- a/runtime/memory_region.h +++ b/runtime/memory_region.h @@ -20,10 +20,11 @@ #include #include +#include + #include "arch/instruction_set.h" #include "base/bit_utils.h" #include "base/casts.h" -#include "base/logging.h" #include "base/macros.h" #include "base/value_object.h" #include "globals.h" diff --git a/runtime/method_info.h b/runtime/method_info.h index 5a72125be4..6485af992d 100644 --- a/runtime/method_info.h +++ b/runtime/method_info.h @@ -17,7 +17,9 @@ #ifndef ART_RUNTIME_METHOD_INFO_H_ #define ART_RUNTIME_METHOD_INFO_H_ -#include "base/logging.h" +#include + +#include "base/macros.h" #include "leb128.h" #include "memory_region.h" diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index 22812454d1..636c84c759 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -19,11 +19,11 @@ #include "array.h" -#include "android-base/stringprintf.h" +#include +#include #include "base/bit_utils.h" #include "base/casts.h" -#include "base/logging.h" #include "class.h" #include "gc/heap-inl.h" #include "obj_ptr-inl.h" diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index 8b11c1290d..8d4d44b6f9 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -19,11 +19,12 @@ #include "dex_cache.h" +#include + #include "art_field.h" #include "art_method.h" #include "base/casts.h" #include "base/enums.h" -#include "base/logging.h" #include "class_linker.h" #include "dex_file.h" #include "gc/heap-inl.h" diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc index 2f63dff3be..eb4db00ccd 100644 --- a/runtime/mirror/dex_cache.cc +++ b/runtime/mirror/dex_cache.cc @@ -17,7 +17,6 @@ #include "dex_cache-inl.h" #include "art_method-inl.h" -#include "base/logging.h" #include "class_linker.h" #include "gc/accounting/card_table-inl.h" #include "gc/heap.h" diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index f75786b521..509db0292e 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -19,6 +19,7 @@ #include "array.h" #include "base/bit_utils.h" +#include "base/mutex.h" #include "dex_file_types.h" #include "object.h" #include "object_array.h" diff --git a/runtime/monitor_pool.cc b/runtime/monitor_pool.cc index d00f979379..cf5934b6a0 100644 --- a/runtime/monitor_pool.cc +++ b/runtime/monitor_pool.cc @@ -16,7 +16,7 @@ #include "monitor_pool.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/mutex-inl.h" #include "monitor.h" #include "thread-current-inl.h" diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index a7bee39a81..bdbb80bd58 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -18,11 +18,13 @@ #include -#include "android-base/stringprintf.h" +#include +#include #include "arch/instruction_set.h" #include "art_method-inl.h" -#include "base/logging.h" +#include "base/macros.h" +#include "base/mutex.h" #include "debugger.h" #include "java_vm_ext.h" #include "jit/jit.h" diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc index c79f51b51e..8f8fd71727 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc @@ -16,8 +16,9 @@ #include "org_apache_harmony_dalvik_ddmc_DdmServer.h" +#include + #include "base/array_ref.h" -#include "base/logging.h" #include "debugger.h" #include "jni_internal.h" #include "native_util.h" diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc index f5057b013a..7b733824c5 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc @@ -16,8 +16,9 @@ #include "org_apache_harmony_dalvik_ddmc_DdmVmInternal.h" +#include + #include "base/file_utils.h" -#include "base/logging.h" #include "base/mutex.h" #include "debugger.h" #include "gc/heap.h" diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc index cd8315cdf9..10d10912f1 100644 --- a/runtime/native_bridge_art_interface.cc +++ b/runtime/native_bridge_art_interface.cc @@ -22,7 +22,7 @@ #include "art_method-inl.h" #include "base/enums.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "dex_file-inl.h" #include "jni_internal.h" diff --git a/runtime/non_debuggable_classes.cc b/runtime/non_debuggable_classes.cc index 7db199cd06..8484e2cde7 100644 --- a/runtime/non_debuggable_classes.cc +++ b/runtime/non_debuggable_classes.cc @@ -16,7 +16,6 @@ #include "non_debuggable_classes.h" -#include "base/logging.h" #include "jni_internal.h" #include "mirror/class-inl.h" #include "nativehelper/scoped_local_ref.h" diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index cd18ce102e..8707e736bd 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -24,7 +24,7 @@ #include "android-base/strings.h" #include "base/file_utils.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/stl_util.h" #include "class_linker.h" #include "compiler_filter.h" diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index b86f479eed..91a138a259 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -26,7 +26,7 @@ #include "art_field-inl.h" #include "base/bit_vector-inl.h" #include "base/file_utils.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/stl_util.h" #include "base/systrace.h" #include "class_linker.h" diff --git a/runtime/os_linux.cc b/runtime/os_linux.cc index a463f700d8..1b3e0000da 100644 --- a/runtime/os_linux.cc +++ b/runtime/os_linux.cc @@ -23,7 +23,8 @@ #include #include -#include "base/logging.h" +#include + #include "base/unix_file/fd_file.h" namespace art { diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 85af560ce3..a3c00364a1 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -18,8 +18,10 @@ #include +#include + #include "base/file_utils.h" -#include "base/logging.h" +#include "base/macros.h" #include "base/stringpiece.h" #include "debugger.h" #include "gc/heap.h" diff --git a/runtime/plugin.cc b/runtime/plugin.cc index 6aa078771b..7d86f1d5dc 100644 --- a/runtime/plugin.cc +++ b/runtime/plugin.cc @@ -20,8 +20,6 @@ #include "android-base/stringprintf.h" -#include "base/logging.h" - namespace art { using android::base::StringPrintf; diff --git a/runtime/plugin.h b/runtime/plugin.h index f077aaf3fb..909c710a96 100644 --- a/runtime/plugin.h +++ b/runtime/plugin.h @@ -18,7 +18,8 @@ #define ART_RUNTIME_PLUGIN_H_ #include -#include "base/logging.h" + +#include namespace art { diff --git a/runtime/primitive.h b/runtime/primitive.h index a429914d5c..5b163d8cbe 100644 --- a/runtime/primitive.h +++ b/runtime/primitive.h @@ -19,7 +19,8 @@ #include -#include "base/logging.h" +#include + #include "base/macros.h" namespace art { diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h index 12b63c933d..1103dab52c 100644 --- a/runtime/quick_exception_handler.h +++ b/runtime/quick_exception_handler.h @@ -17,7 +17,8 @@ #ifndef ART_RUNTIME_QUICK_EXCEPTION_HANDLER_H_ #define ART_RUNTIME_QUICK_EXCEPTION_HANDLER_H_ -#include "base/logging.h" +#include + #include "base/macros.h" #include "base/mutex.h" #include "deoptimization_kind.h" diff --git a/runtime/read_barrier.h b/runtime/read_barrier.h index d4b9f4311f..ce2cb0e568 100644 --- a/runtime/read_barrier.h +++ b/runtime/read_barrier.h @@ -17,7 +17,9 @@ #ifndef ART_RUNTIME_READ_BARRIER_H_ #define ART_RUNTIME_READ_BARRIER_H_ -#include "base/logging.h" +#include + +#include "base/logging.h" // For DECLARE_RUNTIME_DEBUG_FLAG. #include "base/macros.h" #include "base/mutex.h" #include "gc_root.h" diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc index eb69d91dad..b49bec6921 100644 --- a/runtime/runtime_common.cc +++ b/runtime/runtime_common.cc @@ -23,10 +23,11 @@ #include #include -#include "android-base/stringprintf.h" +#include +#include #include "base/file_utils.h" -#include "base/logging.h" +#include "base/logging.h" // For gAborting. #include "base/macros.h" #include "base/mutex.h" #include "native_stack_dump.h" diff --git a/runtime/safe_map.h b/runtime/safe_map.h index f29869172e..33e45bdbd8 100644 --- a/runtime/safe_map.h +++ b/runtime/safe_map.h @@ -21,8 +21,9 @@ #include #include +#include + #include "base/allocator.h" -#include "base/logging.h" namespace art { diff --git a/runtime/scoped_thread_state_change-inl.h b/runtime/scoped_thread_state_change-inl.h index aa96871145..a9702a70bc 100644 --- a/runtime/scoped_thread_state_change-inl.h +++ b/runtime/scoped_thread_state_change-inl.h @@ -19,6 +19,8 @@ #include "scoped_thread_state_change.h" +#include + #include "base/casts.h" #include "jni_env_ext-inl.h" #include "obj_ptr-inl.h" diff --git a/runtime/scoped_thread_state_change.cc b/runtime/scoped_thread_state_change.cc index 94354fc586..6a86cc6411 100644 --- a/runtime/scoped_thread_state_change.cc +++ b/runtime/scoped_thread_state_change.cc @@ -19,7 +19,6 @@ #include #include "base/casts.h" -#include "base/logging.h" #include "java_vm_ext.h" #include "obj_ptr-inl.h" #include "runtime-inl.h" diff --git a/runtime/signal_set.h b/runtime/signal_set.h index 6f888525cb..53613236fa 100644 --- a/runtime/signal_set.h +++ b/runtime/signal_set.h @@ -19,7 +19,7 @@ #include -#include "base/logging.h" +#include namespace art { diff --git a/runtime/stride_iterator.h b/runtime/stride_iterator.h index 0560c33eee..511c2c66f2 100644 --- a/runtime/stride_iterator.h +++ b/runtime/stride_iterator.h @@ -19,7 +19,7 @@ #include -#include "base/logging.h" +#include namespace art { diff --git a/runtime/string_reference.h b/runtime/string_reference.h index d0ab4e40d0..24a425371b 100644 --- a/runtime/string_reference.h +++ b/runtime/string_reference.h @@ -19,7 +19,8 @@ #include -#include "base/logging.h" +#include + #include "dex_file-inl.h" #include "dex_file_reference.h" #include "dex_file_types.h" diff --git a/runtime/thread_linux.cc b/runtime/thread_linux.cc index b922d94617..9673eee795 100644 --- a/runtime/thread_linux.cc +++ b/runtime/thread_linux.cc @@ -14,9 +14,11 @@ * limitations under the License. */ +#include "thread.h" + #include -#include "thread.h" +#include "base/logging.h" // For VLOG. #include "utils.h" namespace art { diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc index cffaffc045..386cdf006a 100644 --- a/runtime/thread_pool.cc +++ b/runtime/thread_pool.cc @@ -22,11 +22,11 @@ #include -#include "android-base/stringprintf.h" +#include +#include #include "base/bit_utils.h" #include "base/casts.h" -#include "base/logging.h" #include "base/stl_util.h" #include "base/time_utils.h" #include "runtime.h" diff --git a/runtime/transaction.cc b/runtime/transaction.cc index e923aff439..c9766bc9ca 100644 --- a/runtime/transaction.cc +++ b/runtime/transaction.cc @@ -16,7 +16,8 @@ #include "transaction.h" -#include "base/logging.h" +#include + #include "base/stl_util.h" #include "gc/accounting/card_table-inl.h" #include "gc_root-inl.h" diff --git a/runtime/type_reference.h b/runtime/type_reference.h index f7daa2bd58..10a67b1798 100644 --- a/runtime/type_reference.h +++ b/runtime/type_reference.h @@ -19,7 +19,8 @@ #include -#include "base/logging.h" +#include + #include "dex_file_types.h" #include "string_reference.h" diff --git a/runtime/utf.cc b/runtime/utf.cc index 7e06482635..93fcb32136 100644 --- a/runtime/utf.cc +++ b/runtime/utf.cc @@ -16,7 +16,8 @@ #include "utf.h" -#include "base/logging.h" +#include + #include "mirror/array.h" #include "mirror/object-inl.h" #include "utf-inl.h" diff --git a/runtime/utils.h b/runtime/utils.h index ede32dc57a..789498ce09 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -23,9 +23,10 @@ #include #include +#include + #include "arch/instruction_set.h" #include "base/casts.h" -#include "base/logging.h" #include "base/stringpiece.h" #include "globals.h" #include "primitive.h" diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h index 9d4e9fb96c..855b856187 100644 --- a/runtime/utils/dex_cache_arrays_layout-inl.h +++ b/runtime/utils/dex_cache_arrays_layout-inl.h @@ -19,8 +19,9 @@ #include "dex_cache_arrays_layout.h" +#include + #include "base/bit_utils.h" -#include "base/logging.h" #include "gc_root.h" #include "globals.h" #include "mirror/dex_cache.h" diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index fb9d24f9bc..fe768a1fd5 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -20,8 +20,9 @@ #include +#include + #include "base/bit_utils.h" -#include "base/logging.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "dex_file.h" diff --git a/runtime/verifier/method_verifier-inl.h b/runtime/verifier/method_verifier-inl.h index a7fa9f34d1..445a6ff7de 100644 --- a/runtime/verifier/method_verifier-inl.h +++ b/runtime/verifier/method_verifier-inl.h @@ -19,7 +19,8 @@ #include "method_verifier.h" -#include "base/logging.h" +#include + #include "handle_scope-inl.h" #include "mirror/class_loader.h" #include "mirror/dex_cache.h" diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index f1af2529bd..f10cd37a2c 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -23,7 +23,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" #include "base/enums.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/mutex-inl.h" #include "base/stl_util.h" #include "base/systrace.h" diff --git a/runtime/verifier/register_line-inl.h b/runtime/verifier/register_line-inl.h index a9c9428581..39d73f54d8 100644 --- a/runtime/verifier/register_line-inl.h +++ b/runtime/verifier/register_line-inl.h @@ -19,6 +19,7 @@ #include "register_line.h" +#include "base/logging.h" // For VLOG. #include "method_verifier.h" #include "reg_type_cache-inl.h" @@ -192,6 +193,27 @@ inline RegisterLine::RegisterLine(size_t num_regs, MethodVerifier* verifier) SetResultTypeToUnknown(verifier); } +inline void RegisterLine::ClearRegToLockDepth(size_t reg, size_t depth) { + CHECK_LT(depth, 32u); + DCHECK(IsSetLockDepth(reg, depth)); + auto it = reg_to_lock_depths_.find(reg); + DCHECK(it != reg_to_lock_depths_.end()); + uint32_t depths = it->second ^ (1 << depth); + if (depths != 0) { + it->second = depths; + } else { + reg_to_lock_depths_.erase(it); + } + // Need to unlock every register at the same lock depth. These are aliased locks. + uint32_t mask = 1 << depth; + for (auto& pair : reg_to_lock_depths_) { + if ((pair.second & mask) != 0) { + VLOG(verifier) << "Also unlocking " << pair.first; + pair.second ^= mask; + } + } +} + inline void RegisterLineArenaDelete::operator()(RegisterLine* ptr) const { if (ptr != nullptr) { ptr->~RegisterLine(); diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h index 71eb4d6ac7..82f63b281a 100644 --- a/runtime/verifier/register_line.h +++ b/runtime/verifier/register_line.h @@ -20,6 +20,8 @@ #include #include +#include + #include "base/scoped_arena_containers.h" #include "safe_map.h" @@ -401,26 +403,7 @@ class RegisterLine { return true; } - void ClearRegToLockDepth(size_t reg, size_t depth) { - CHECK_LT(depth, 32u); - DCHECK(IsSetLockDepth(reg, depth)); - auto it = reg_to_lock_depths_.find(reg); - DCHECK(it != reg_to_lock_depths_.end()); - uint32_t depths = it->second ^ (1 << depth); - if (depths != 0) { - it->second = depths; - } else { - reg_to_lock_depths_.erase(it); - } - // Need to unlock every register at the same lock depth. These are aliased locks. - uint32_t mask = 1 << depth; - for (auto& pair : reg_to_lock_depths_) { - if ((pair.second & mask) != 0) { - VLOG(verifier) << "Also unlocking " << pair.first; - pair.second ^= mask; - } - } - } + void ClearRegToLockDepth(size_t reg, size_t depth); void ClearAllRegToLockDepths(size_t reg) { reg_to_lock_depths_.erase(reg); diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 9722db9641..5a653fe3a1 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -20,9 +20,9 @@ #include -#include "android-base/stringprintf.h" +#include +#include -#include "base/logging.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "jni_internal.h" #include "mirror/class.h" diff --git a/runtime/zip_archive.h b/runtime/zip_archive.h index 821cc5ceb1..75f8757f6c 100644 --- a/runtime/zip_archive.h +++ b/runtime/zip_archive.h @@ -21,7 +21,8 @@ #include #include -#include "base/logging.h" +#include + #include "base/unix_file/random_access_file.h" #include "globals.h" #include "mem_map.h" diff --git a/simulator/code_simulator_arm64.cc b/simulator/code_simulator_arm64.cc index 939d2e287f..a64bd0bc0b 100644 --- a/simulator/code_simulator_arm64.cc +++ b/simulator/code_simulator_arm64.cc @@ -16,7 +16,7 @@ #include "code_simulator_arm64.h" -#include "base/logging.h" +#include using namespace vixl::aarch64; // NOLINT(build/namespaces) diff --git a/simulator/code_simulator_container.cc b/simulator/code_simulator_container.cc index a5f05dc8fc..9f52b320f2 100644 --- a/simulator/code_simulator_container.cc +++ b/simulator/code_simulator_container.cc @@ -18,6 +18,7 @@ #include "code_simulator_container.h" +#include "base/logging.h" // For VLOG. #include "code_simulator.h" #include "globals.h" diff --git a/simulator/code_simulator_container.h b/simulator/code_simulator_container.h index 31a915e4f1..a21971508a 100644 --- a/simulator/code_simulator_container.h +++ b/simulator/code_simulator_container.h @@ -17,8 +17,9 @@ #ifndef ART_SIMULATOR_CODE_SIMULATOR_CONTAINER_H_ #define ART_SIMULATOR_CODE_SIMULATOR_CONTAINER_H_ +#include + #include "arch/instruction_set.h" -#include "base/logging.h" namespace art { diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc index bc5a0a64e8..eb14d48a8d 100644 --- a/test/004-JniTest/jni_test.cc +++ b/test/004-JniTest/jni_test.cc @@ -20,8 +20,9 @@ #include #include +#include + #include "art_method-inl.h" -#include "base/logging.h" #include "jni.h" namespace art { diff --git a/test/044-proxy/native_proxy.cc b/test/044-proxy/native_proxy.cc index f168719bf5..f3178f9c2a 100644 --- a/test/044-proxy/native_proxy.cc +++ b/test/044-proxy/native_proxy.cc @@ -16,7 +16,7 @@ #include "jni.h" -#include "base/logging.h" +#include namespace art { diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc index 66270fd5c7..ef758e86e1 100644 --- a/test/137-cfi/cfi.cc +++ b/test/137-cfi/cfi.cc @@ -25,11 +25,11 @@ #include "jni.h" +#include +#include #include -#include "android-base/stringprintf.h" #include "base/file_utils.h" -#include "base/logging.h" #include "base/macros.h" #include "gc/heap.h" #include "gc/space/image_space.h" diff --git a/test/642-fp-callees/fp_callees.cc b/test/642-fp-callees/fp_callees.cc index 600f9690eb..17bb55b70d 100644 --- a/test/642-fp-callees/fp_callees.cc +++ b/test/642-fp-callees/fp_callees.cc @@ -14,8 +14,9 @@ * limitations under the License. */ +#include + #include "base/casts.h" -#include "base/logging.h" #include "jni.h" namespace art { diff --git a/test/900-hello-plugin/load_unload.cc b/test/900-hello-plugin/load_unload.cc index 19312b4d71..cab0abf58b 100644 --- a/test/900-hello-plugin/load_unload.cc +++ b/test/900-hello-plugin/load_unload.cc @@ -17,9 +17,10 @@ #include #include +#include +#include + #include "art_method-inl.h" -#include "base/logging.h" -#include "base/macros.h" #include "java_vm_ext.h" #include "runtime.h" diff --git a/test/936-search-onload/search_onload.cc b/test/936-search-onload/search_onload.cc index 90d87e0e7b..23cea83be6 100644 --- a/test/936-search-onload/search_onload.cc +++ b/test/936-search-onload/search_onload.cc @@ -18,8 +18,9 @@ #include -#include "android-base/stringprintf.h" -#include "base/logging.h" +#include +#include + #include "base/macros.h" #include "jni.h" #include "jvmti.h" diff --git a/test/983-source-transform-verify/source_transform.cc b/test/983-source-transform-verify/source_transform.cc index ca3f88b347..3010d7a1a4 100644 --- a/test/983-source-transform-verify/source_transform.cc +++ b/test/983-source-transform-verify/source_transform.cc @@ -25,7 +25,6 @@ #include "jni.h" #include "jvmti.h" -#include "base/logging.h" #include "base/macros.h" #include "bytecode_utils.h" #include "dex_file.h" diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index 34580800cc..1eed80ec91 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -16,9 +16,11 @@ #include "jni.h" +#include +#include + #include "art_method-inl.h" #include "base/enums.h" -#include "base/logging.h" #include "dex_file-inl.h" #include "instrumentation.h" #include "jit/jit.h" diff --git a/test/common/stack_inspect.cc b/test/common/stack_inspect.cc index 80a278012d..046b1fbcdd 100644 --- a/test/common/stack_inspect.cc +++ b/test/common/stack_inspect.cc @@ -16,7 +16,9 @@ #include "jni.h" -#include "base/logging.h" +#include + +#include "base/mutex.h" #include "dex_file-inl.h" #include "jni_internal.h" #include "mirror/class-inl.h" diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index d85f33a05d..9a7352e479 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -17,7 +17,8 @@ #include #include -#include "base/logging.h" +#include + #include "base/macros.h" #include "jni_binder.h" -- GitLab From dcc528d2c7d5ac2cc075d4c965fdf702421d0f43 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 7 Dec 2017 13:37:10 -0800 Subject: [PATCH 138/226] ART: Move runtime-debug flags to own files To reduce the need for base/logging.h and separate out concerns. Test: m Change-Id: Ib373357325c6e622f608ada341594c3bea2fce2e --- runtime/Android.bp | 1 + runtime/art_method.h | 2 +- runtime/base/logging.cc | 49 -------------- runtime/base/logging.h | 37 ----------- runtime/base/logging_test.cc | 1 + runtime/base/runtime_debug.cc | 74 +++++++++++++++++++++ runtime/base/runtime_debug.h | 61 +++++++++++++++++ runtime/common_runtime_test.cc | 1 + runtime/gc/heap.h | 2 +- runtime/jit/jit.cc | 1 + runtime/native/dalvik_system_ZygoteHooks.cc | 1 + runtime/native_stack_dump.cc | 1 + runtime/read_barrier.h | 2 +- runtime/thread-inl.h | 1 + test/004-JniTest/jni_test.cc | 1 + 15 files changed, 146 insertions(+), 89 deletions(-) create mode 100644 runtime/base/runtime_debug.cc create mode 100644 runtime/base/runtime_debug.h diff --git a/runtime/Android.bp b/runtime/Android.bp index a136ccb9d0..6477347a6e 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -39,6 +39,7 @@ cc_defaults { "base/hex_dump.cc", "base/logging.cc", "base/mutex.cc", + "base/runtime_debug.cc", "base/safe_copy.cc", "base/scoped_arena_allocator.cc", "base/scoped_flock.cc", diff --git a/runtime/art_method.h b/runtime/art_method.h index 4718150400..2c23c99dae 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -25,8 +25,8 @@ #include "base/casts.h" #include "base/enums.h" #include "base/iteration_range.h" -#include "base/logging.h" // For RUNTIME_DEBUG_FLAG. #include "base/macros.h" +#include "base/runtime_debug.h" #include "dex_file.h" #include "dex_instruction_iterator.h" #include "gc_root.h" diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc index 4776357fdf..26106eef2f 100644 --- a/runtime/base/logging.cc +++ b/runtime/base/logging.cc @@ -34,55 +34,6 @@ namespace art { -// We test here that the runtime-debug-checks are actually a no-op constexpr false in release -// builds, as we can't check that in gtests (which are always debug). - -#ifdef NDEBUG -namespace { -DECLARE_RUNTIME_DEBUG_FLAG(kTestForConstexpr); -static_assert(!kTestForConstexpr, "Issue with DECLARE_RUNTIME_DEBUG_FLAG in NDEBUG."); -} -#endif - -// Implementation of runtime debug flags. This should be compile-time optimized away in release -// builds. -namespace { -bool gSlowEnabled = false; // Default for slow flags is "off." - -// Use a function with a static to ensure our vector storage doesn't have initialization order -// issues. -std::vector& GetFlagPtrs() { - static std::vector g_flag_ptrs; - return g_flag_ptrs; -} - -bool RegisterRuntimeDebugFlagImpl(bool* flag_ptr) { - GetFlagPtrs().push_back(flag_ptr); - return gSlowEnabled; -} - -void SetRuntimeDebugFlagsEnabledImpl(bool enabled) { - gSlowEnabled = enabled; - for (bool* flag_ptr : GetFlagPtrs()) { - *flag_ptr = enabled; - } -} - -} // namespace - -bool RegisterRuntimeDebugFlag(bool* flag_ptr) { - if (kIsDebugBuild) { - return RegisterRuntimeDebugFlagImpl(flag_ptr); - } - return false; -} - -void SetRuntimeDebugFlagsEnabled(bool enabled) { - if (kIsDebugBuild) { - SetRuntimeDebugFlagsEnabledImpl(enabled); - } -} - LogVerbosity gLogVerbosity; std::atomic gAborting(0); diff --git a/runtime/base/logging.h b/runtime/base/logging.h index 5703b3c746..981fbbfba4 100644 --- a/runtime/base/logging.h +++ b/runtime/base/logging.h @@ -63,43 +63,6 @@ struct LogVerbosity { // Global log verbosity setting, initialized by InitLogging. extern LogVerbosity gLogVerbosity; -// Runtime debug flags are flags that have a runtime component, that is, their value can be changed. -// This is meant to implement fast vs slow debug builds, in that certain debug flags can be turned -// on and off. To that effect, expose two macros to help implement and globally drive these flags: -// -// In the header, declare a (class) flag like this: -// -// class C { -// DECLARE_RUNTIME_DEBUG_FLAG(kFlag); -// }; -// -// This will declare a flag kFlag that is a constexpr false in release builds, and a static field -// in debug builds. Usage is than uniform as C::kFlag. -// -// In the cc file, define the flag like this: -// -// DEFINE_RUNTIME_DEBUG_FLAG(C, kFlag); -// -// This will define the static storage, as necessary, and register the flag with the runtime -// infrastructure to toggle the value. - -#ifdef NDEBUG -#define DECLARE_RUNTIME_DEBUG_FLAG(x) \ - static constexpr bool x = false; -// Note: the static_assert in the following only works for public flags. Fix this when we cross -// the line at some point. -#define DEFINE_RUNTIME_DEBUG_FLAG(C, x) \ - static_assert(!C::x, "Unexpected enabled flag in release build"); -#else -#define DECLARE_RUNTIME_DEBUG_FLAG(x) \ - static bool x; -#define DEFINE_RUNTIME_DEBUG_FLAG(C, x) \ - bool C::x = RegisterRuntimeDebugFlag(&C::x); -#endif // NDEBUG - -bool RegisterRuntimeDebugFlag(bool* runtime_debug_flag); -void SetRuntimeDebugFlagsEnabled(bool enabled); - // 0 if not abort, non-zero if an abort is in progress. Used on fatal exit to prevents recursive // aborts. Global declaration allows us to disable some error checking to ensure fatal shutdown // makes forward progress. diff --git a/runtime/base/logging_test.cc b/runtime/base/logging_test.cc index d380b9eccc..404e080b03 100644 --- a/runtime/base/logging_test.cc +++ b/runtime/base/logging_test.cc @@ -22,6 +22,7 @@ #include "base/bit_utils.h" #include "base/macros.h" #include "common_runtime_test.h" +#include "runtime_debug.h" namespace art { diff --git a/runtime/base/runtime_debug.cc b/runtime/base/runtime_debug.cc new file mode 100644 index 0000000000..4f8a8ec9c6 --- /dev/null +++ b/runtime/base/runtime_debug.cc @@ -0,0 +1,74 @@ +/* + * 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 "runtime_debug.h" + +#include + +#include "globals.h" + +namespace art { + +// We test here that the runtime-debug-checks are actually a no-op constexpr false in release +// builds, as we can't check that in gtests (which are always debug). + +#ifdef NDEBUG +namespace { +DECLARE_RUNTIME_DEBUG_FLAG(kTestForConstexpr); +static_assert(!kTestForConstexpr, "Issue with DECLARE_RUNTIME_DEBUG_FLAG in NDEBUG."); +} +#endif + +// Implementation of runtime debug flags. This should be compile-time optimized away in release +// builds. +namespace { +bool gSlowEnabled = false; // Default for slow flags is "off." + +// Use a function with a static to ensure our vector storage doesn't have initialization order +// issues. +std::vector& GetFlagPtrs() { + static std::vector g_flag_ptrs; + return g_flag_ptrs; +} + +bool RegisterRuntimeDebugFlagImpl(bool* flag_ptr) { + GetFlagPtrs().push_back(flag_ptr); + return gSlowEnabled; +} + +void SetRuntimeDebugFlagsEnabledImpl(bool enabled) { + gSlowEnabled = enabled; + for (bool* flag_ptr : GetFlagPtrs()) { + *flag_ptr = enabled; + } +} + +} // namespace + +bool RegisterRuntimeDebugFlag(bool* flag_ptr) { + if (kIsDebugBuild) { + return RegisterRuntimeDebugFlagImpl(flag_ptr); + } + return false; +} + +void SetRuntimeDebugFlagsEnabled(bool enabled) { + if (kIsDebugBuild) { + SetRuntimeDebugFlagsEnabledImpl(enabled); + } +} + +} // namespace art diff --git a/runtime/base/runtime_debug.h b/runtime/base/runtime_debug.h new file mode 100644 index 0000000000..89a0361fa7 --- /dev/null +++ b/runtime/base/runtime_debug.h @@ -0,0 +1,61 @@ +/* + * 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_BASE_RUNTIME_DEBUG_H_ +#define ART_RUNTIME_BASE_RUNTIME_DEBUG_H_ + +namespace art { + +// Runtime debug flags are flags that have a runtime component, that is, their value can be changed. +// This is meant to implement fast vs slow debug builds, in that certain debug flags can be turned +// on and off. To that effect, expose two macros to help implement and globally drive these flags: +// +// In the header, declare a (class) flag like this: +// +// class C { +// DECLARE_RUNTIME_DEBUG_FLAG(kFlag); +// }; +// +// This will declare a flag kFlag that is a constexpr false in release builds, and a static field +// in debug builds. Usage is than uniform as C::kFlag. +// +// In the cc file, define the flag like this: +// +// DEFINE_RUNTIME_DEBUG_FLAG(C, kFlag); +// +// This will define the static storage, as necessary, and register the flag with the runtime +// infrastructure to toggle the value. + +#ifdef NDEBUG +#define DECLARE_RUNTIME_DEBUG_FLAG(x) \ + static constexpr bool x = false; +// Note: the static_assert in the following only works for public flags. Fix this when we cross +// the line at some point. +#define DEFINE_RUNTIME_DEBUG_FLAG(C, x) \ + static_assert(!C::x, "Unexpected enabled flag in release build"); +#else +#define DECLARE_RUNTIME_DEBUG_FLAG(x) \ + static bool x; +#define DEFINE_RUNTIME_DEBUG_FLAG(C, x) \ + bool C::x = RegisterRuntimeDebugFlag(&C::x); +#endif // NDEBUG + +bool RegisterRuntimeDebugFlag(bool* runtime_debug_flag); +void SetRuntimeDebugFlagsEnabled(bool enabled); + +} // namespace art + +#endif // ART_RUNTIME_BASE_RUNTIME_DEBUG_H_ diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index ef1647caf3..6db4d92708 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/runtime_debug.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 10bae7114e..bb21b7cf00 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -27,9 +27,9 @@ #include "allocator_type.h" #include "arch/instruction_set.h" #include "atomic.h" -#include "base/logging.h" // For DECLARE_RUNTIME_DEBUG_FLAG. #include "base/macros.h" #include "base/mutex.h" +#include "base/runtime_debug.h" #include "base/time_utils.h" #include "gc/collector/gc_type.h" #include "gc/collector/iteration.h" diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index a6aa9167eb..783d05df34 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -22,6 +22,7 @@ #include "base/enums.h" #include "base/logging.h" // For VLOG. #include "base/memory_tool.h" +#include "base/runtime_debug.h" #include "debugger.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "interpreter/interpreter.h" diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index bdbb80bd58..fd80aaeaf7 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -25,6 +25,7 @@ #include "art_method-inl.h" #include "base/macros.h" #include "base/mutex.h" +#include "base/runtime_debug.h" #include "debugger.h" #include "java_vm_ext.h" #include "jit/jit.h" diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc index f166714b79..841506bce3 100644 --- a/runtime/native_stack_dump.cc +++ b/runtime/native_stack_dump.cc @@ -41,6 +41,7 @@ #include "arch/instruction_set.h" #include "base/file_utils.h" +#include "base/logging.h" // For gAborting. #include "base/memory_tool.h" #include "base/mutex.h" #include "base/unix_file/fd_file.h" diff --git a/runtime/read_barrier.h b/runtime/read_barrier.h index ce2cb0e568..e8df2ad4ce 100644 --- a/runtime/read_barrier.h +++ b/runtime/read_barrier.h @@ -19,9 +19,9 @@ #include -#include "base/logging.h" // For DECLARE_RUNTIME_DEBUG_FLAG. #include "base/macros.h" #include "base/mutex.h" +#include "base/runtime_debug.h" #include "gc_root.h" #include "jni.h" #include "mirror/object_reference.h" diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index b5a962691b..9d08531b69 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -20,6 +20,7 @@ #include "thread.h" #include "base/casts.h" +#include "base/logging.h" // For gAborting. #include "base/mutex-inl.h" #include "base/time_utils.h" #include "jni_env_ext.h" diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc index eb14d48a8d..4561895509 100644 --- a/test/004-JniTest/jni_test.cc +++ b/test/004-JniTest/jni_test.cc @@ -23,6 +23,7 @@ #include #include "art_method-inl.h" +#include "base/runtime_debug.h" #include "jni.h" namespace art { -- GitLab From 39b378ca3b4b6dc6da1651b84ee4289cd9bff0f8 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 7 Dec 2017 15:44:13 -0800 Subject: [PATCH 139/226] ART: Factor out gAborting Cut dependencies on base/logging.h by moving gAborting to its own header. Leave the static storage in logging.cc. Test: m Change-Id: Ib2ca880e15f9cb50cb9aab803784826bb46efb5e --- dexoptanalyzer/dexoptanalyzer.cc | 1 + runtime/barrier.cc | 4 +++- runtime/base/aborting.h | 31 +++++++++++++++++++++++++++++ runtime/base/file_utils.cc | 2 +- runtime/base/logging.cc | 3 ++- runtime/base/logging.h | 5 ----- runtime/base/mutex.h | 2 +- runtime/gc/heap-inl.h | 1 + runtime/native_stack_dump.cc | 2 +- runtime/runtime.cc | 1 + runtime/runtime_common.cc | 5 +++-- runtime/signal_catcher.cc | 1 + runtime/thread-inl.h | 2 +- runtime/thread_list.cc | 1 + runtime/verifier/method_verifier.cc | 1 + runtime/verifier/reg_type_cache.cc | 1 + 16 files changed, 50 insertions(+), 13 deletions(-) create mode 100644 runtime/base/aborting.h diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc index 39c9b9993b..cde68da801 100644 --- a/dexoptanalyzer/dexoptanalyzer.cc +++ b/dexoptanalyzer/dexoptanalyzer.cc @@ -16,6 +16,7 @@ #include +#include "base/logging.h" // For InitLogging. #include "android-base/stringprintf.h" #include "android-base/strings.h" #include "base/file_utils.h" diff --git a/runtime/barrier.cc b/runtime/barrier.cc index 8d3bc0e095..4329a5a245 100644 --- a/runtime/barrier.cc +++ b/runtime/barrier.cc @@ -16,7 +16,9 @@ #include "barrier.h" -#include "base/logging.h" // Required for gAborting. +#include + +#include "base/aborting.h" #include "base/mutex.h" #include "base/time_utils.h" #include "thread.h" diff --git a/runtime/base/aborting.h b/runtime/base/aborting.h new file mode 100644 index 0000000000..8906c96ea7 --- /dev/null +++ b/runtime/base/aborting.h @@ -0,0 +1,31 @@ +/* + * 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_BASE_ABORTING_H_ +#define ART_RUNTIME_BASE_ABORTING_H_ + +#include + +namespace art { + +// 0 if not abort, non-zero if an abort is in progress. Used on fatal exit to prevents recursive +// aborts. Global declaration allows us to disable some error checking to ensure fatal shutdown +// makes forward progress. +extern std::atomic gAborting; + +} // namespace art + +#endif // ART_RUNTIME_BASE_ABORTING_H_ diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc index 323a06519d..db498600d9 100644 --- a/runtime/base/file_utils.cc +++ b/runtime/base/file_utils.cc @@ -89,7 +89,7 @@ bool ReadFileToString(const std::string& file_name, std::string* result) { } } -bool PrintFileToLog(const std::string& file_name, LogSeverity level) { +bool PrintFileToLog(const std::string& file_name, android::base::LogSeverity level) { File file(file_name, O_RDONLY, false); if (!file.IsOpened()) { return false; diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc index 26106eef2f..90eb74c75c 100644 --- a/runtime/base/logging.cc +++ b/runtime/base/logging.cc @@ -20,7 +20,8 @@ #include #include -#include "base/mutex.h" +#include "aborting.h" +#include "mutex.h" #include "thread-current-inl.h" #include "utils.h" diff --git a/runtime/base/logging.h b/runtime/base/logging.h index 981fbbfba4..c562bdf59f 100644 --- a/runtime/base/logging.h +++ b/runtime/base/logging.h @@ -63,11 +63,6 @@ struct LogVerbosity { // Global log verbosity setting, initialized by InitLogging. extern LogVerbosity gLogVerbosity; -// 0 if not abort, non-zero if an abort is in progress. Used on fatal exit to prevents recursive -// aborts. Global declaration allows us to disable some error checking to ensure fatal shutdown -// makes forward progress. -extern std::atomic gAborting; - // Configure logging based on ANDROID_LOG_TAGS environment variable. // We need to parse a string that looks like // diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 9a037b50bf..7077298ca9 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -27,7 +27,7 @@ #include #include "atomic.h" -#include "base/logging.h" // For gAborting. +#include "base/aborting.h" #include "base/macros.h" #include "globals.h" diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 2047646413..141efd2f06 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -20,6 +20,7 @@ #include "heap.h" #include "allocation_listener.h" +#include "base/logging.h" // For VLOG. #include "base/time_utils.h" #include "gc/accounting/atomic_stack.h" #include "gc/accounting/card_table-inl.h" diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc index 841506bce3..ec9455289f 100644 --- a/runtime/native_stack_dump.cc +++ b/runtime/native_stack_dump.cc @@ -40,8 +40,8 @@ #include "android-base/stringprintf.h" #include "arch/instruction_set.h" +#include "base/aborting.h" #include "base/file_utils.h" -#include "base/logging.h" // For gAborting. #include "base/memory_tool.h" #include "base/mutex.h" #include "base/unix_file/fd_file.h" diff --git a/runtime/runtime.cc b/runtime/runtime.cc index d15de38b0a..7239ad3213 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -57,6 +57,7 @@ #include "asm_support.h" #include "asm_support_check.h" #include "atomic.h" +#include "base/aborting.h" #include "base/arena_allocator.h" #include "base/dumpable.h" #include "base/enums.h" diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc index b49bec6921..59af9187f9 100644 --- a/runtime/runtime_common.cc +++ b/runtime/runtime_common.cc @@ -26,8 +26,9 @@ #include #include +#include "base/aborting.h" #include "base/file_utils.h" -#include "base/logging.h" // For gAborting. +#include "base/logging.h" // For LogHelper, GetCmdLine. #include "base/macros.h" #include "base/mutex.h" #include "native_stack_dump.h" @@ -431,7 +432,7 @@ void HandleUnexpectedSignalCommon(int signal_number, logger(LOG_STREAM(FATAL_WITHOUT_ABORT)); } if (kIsDebugBuild && signal_number == SIGSEGV) { - PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT); + PrintFileToLog("/proc/self/maps", android::base::LogSeverity::FATAL_WITHOUT_ABORT); } Runtime* runtime = Runtime::Current(); diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc index bf5d718113..d9c4da9b96 100644 --- a/runtime/signal_catcher.cc +++ b/runtime/signal_catcher.cc @@ -35,6 +35,7 @@ #include "arch/instruction_set.h" #include "base/file_utils.h" +#include "base/logging.h" // For GetCmdLine. #include "base/time_utils.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index 9d08531b69..62b0789a43 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -19,8 +19,8 @@ #include "thread.h" +#include "base/aborting.h" #include "base/casts.h" -#include "base/logging.h" // For gAborting. #include "base/mutex-inl.h" #include "base/time_utils.h" #include "jni_env_ext.h" diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 9f553147c4..8754819e09 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -28,6 +28,7 @@ #include "nativehelper/scoped_local_ref.h" #include "nativehelper/scoped_utf_chars.h" +#include "base/aborting.h" #include "base/histogram-inl.h" #include "base/mutex-inl.h" #include "base/systrace.h" diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index f10cd37a2c..82820fda9a 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -22,6 +22,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" +#include "base/aborting.h" #include "base/enums.h" #include "base/logging.h" // For VLOG. #include "base/mutex-inl.h" diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index a3f08c8580..c68fa0f0d6 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -18,6 +18,7 @@ #include +#include "base/aborting.h" #include "base/arena_bit_vector.h" #include "base/bit_vector-inl.h" #include "base/casts.h" -- GitLab From 170331f0e44a0e07fcfe0b5932517e0500f5cd1f Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 7 Dec 2017 18:41:03 -0800 Subject: [PATCH 140/226] ART: Remove base/logging from heap-inl.h Hide the single uncommon VLOG and remove the include. Fix up transitive includes. Test: m Change-Id: I917df597cb62c57040c1fb0e0079df4d95e5a658 --- compiler/dex/dex_to_dex_compiler.cc | 1 + compiler/driver/compiler_driver.cc | 1 + compiler/jit/jit_compiler.cc | 1 + compiler/linker/arm/relative_patcher_thumb2.cc | 2 ++ dex2oat/linker/oat_writer.cc | 1 + dexoptanalyzer/dexoptanalyzer.cc | 1 + openjdkjvmti/OpenjdkJvmTi.cc | 1 + patchoat/patchoat.cc | 1 + runtime/cha.cc | 1 + runtime/common_runtime_test.h | 5 +++++ runtime/fault_handler.cc | 1 + runtime/gc/accounting/mod_union_table.cc | 1 + runtime/gc/allocation_record.cc | 1 + runtime/gc/allocator/rosalloc.cc | 1 + runtime/gc/heap-inl.h | 4 +--- runtime/gc/heap.cc | 6 ++++++ runtime/gc/heap.h | 3 +++ runtime/gc/space/dlmalloc_space.cc | 1 + runtime/gc/space/malloc_space.cc | 1 + runtime/gc/space/rosalloc_space.cc | 1 + runtime/gc/verification.cc | 2 +- runtime/jit/jit_code_cache.cc | 1 + runtime/jit/profile_saver.cc | 1 + runtime/mirror/class.cc | 1 + runtime/monitor.cc | 1 + runtime/oat_file.cc | 5 +++-- runtime/quick_exception_handler.cc | 1 + 27 files changed, 41 insertions(+), 6 deletions(-) diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index eddc8ed132..476903522e 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -21,6 +21,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" +#include "base/logging.h" // For VLOG #include "base/macros.h" #include "base/mutex.h" #include "bytecode_utils.h" diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 0ca3c8f613..267f094e25 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -32,6 +32,7 @@ #include "base/array_ref.h" #include "base/bit_vector.h" #include "base/enums.h" +#include "base/logging.h" // For VLOG #include "base/stl_util.h" #include "base/systrace.h" #include "base/time_utils.h" diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index f33c5e1b97..74603c668f 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -21,6 +21,7 @@ #include "arch/instruction_set.h" #include "arch/instruction_set_features.h" #include "art_method-inl.h" +#include "base/logging.h" // For VLOG #include "base/stringpiece.h" #include "base/time_utils.h" #include "base/timing_logger.h" diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc index 48747fc379..78755176e4 100644 --- a/compiler/linker/arm/relative_patcher_thumb2.cc +++ b/compiler/linker/arm/relative_patcher_thumb2.cc @@ -16,6 +16,8 @@ #include "linker/arm/relative_patcher_thumb2.h" +#include + #include "arch/arm/asm_support_arm.h" #include "art_method.h" #include "base/bit_utils.h" diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 293078acee..60bc5d46d6 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -26,6 +26,7 @@ #include "base/bit_vector-inl.h" #include "base/enums.h" #include "base/file_magic.h" +#include "base/logging.h" // For VLOG #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc index cde68da801..eead2dcf83 100644 --- a/dexoptanalyzer/dexoptanalyzer.cc +++ b/dexoptanalyzer/dexoptanalyzer.cc @@ -20,6 +20,7 @@ #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_file.h" diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc index ac68fcf307..aae805569f 100644 --- a/openjdkjvmti/OpenjdkJvmTi.cc +++ b/openjdkjvmti/OpenjdkJvmTi.cc @@ -40,6 +40,7 @@ #include "jvmti.h" #include "art_jvmti.h" +#include "base/logging.h" // For gLogVerbosity. #include "base/mutex.h" #include "events-inl.h" #include "jni_env_ext-inl.h" diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index ae82d72c0f..eb648cba18 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -30,6 +30,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" #include "base/dumpable.h" +#include "base/logging.h" // For InitLogging. #include "base/memory_tool.h" #include "base/scoped_flock.h" #include "base/stringpiece.h" diff --git a/runtime/cha.cc b/runtime/cha.cc index 6c011e8e39..a53d7e5b25 100644 --- a/runtime/cha.cc +++ b/runtime/cha.cc @@ -17,6 +17,7 @@ #include "cha.h" #include "art_method-inl.h" +#include "base/logging.h" // For VLOG #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "linear_alloc.h" diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 5be8d5b55c..0f931e3dff 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -22,6 +22,8 @@ #include +#include + #include "arch/instruction_set.h" #include "base/mutex.h" #include "globals.h" @@ -32,6 +34,9 @@ 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()); diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc index 6a4e5b5f01..f66836f147 100644 --- a/runtime/fault_handler.cc +++ b/runtime/fault_handler.cc @@ -21,6 +21,7 @@ #include #include "art_method-inl.h" +#include "base/logging.h" // For VLOG #include "base/safe_copy.h" #include "base/stl_util.h" #include "dex_file_types.h" diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc index 1b3d0dadae..0dd05cd6f0 100644 --- a/runtime/gc/accounting/mod_union_table.cc +++ b/runtime/gc/accounting/mod_union_table.cc @@ -18,6 +18,7 @@ #include +#include "base/logging.h" // For VLOG #include "base/stl_util.h" #include "bitmap-inl.h" #include "card_table-inl.h" diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc index 2257b81e09..2ee4239e8a 100644 --- a/runtime/gc/allocation_record.cc +++ b/runtime/gc/allocation_record.cc @@ -18,6 +18,7 @@ #include "art_method-inl.h" #include "base/enums.h" +#include "base/logging.h" // For VLOG #include "base/stl_util.h" #include "obj_ptr-inl.h" #include "object_callbacks.h" diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc index b742ac4a7c..928abe873e 100644 --- a/runtime/gc/allocator/rosalloc.cc +++ b/runtime/gc/allocator/rosalloc.cc @@ -23,6 +23,7 @@ #include "android-base/stringprintf.h" +#include "base/logging.h" // For VLOG #include "base/memory_tool.h" #include "base/mutex-inl.h" #include "gc/space/memory_tool_settings.h" diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 141efd2f06..52dd104ac8 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -20,7 +20,6 @@ #include "heap.h" #include "allocation_listener.h" -#include "base/logging.h" // For VLOG. #include "base/time_utils.h" #include "gc/accounting/atomic_stack.h" #include "gc/accounting/card_table-inl.h" @@ -402,8 +401,7 @@ inline bool Heap::IsOutOfMemoryOnAllocation(AllocatorType allocator_type, return true; } // TODO: Grow for allocation is racy, fix it. - VLOG(heap) << "Growing heap from " << PrettySize(max_allowed_footprint_) << " to " - << PrettySize(new_footprint) << " for a " << PrettySize(alloc_size) << " allocation"; + VlogHeapGrowth(max_allowed_footprint_, new_footprint, alloc_size); max_allowed_footprint_ = new_footprint; } } diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index f29ae92e2d..91f1ff6ace 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -30,6 +30,7 @@ #include "base/dumpable.h" #include "base/file_utils.h" #include "base/histogram-inl.h" +#include "base/logging.h" // For VLOG. #include "base/memory_tool.h" #include "base/stl_util.h" #include "base/systrace.h" @@ -4156,5 +4157,10 @@ const Verification* Heap::GetVerification() const { return verification_.get(); } +void Heap::VlogHeapGrowth(size_t max_allowed_footprint, size_t new_footprint, size_t alloc_size) { + VLOG(heap) << "Growing heap from " << PrettySize(max_allowed_footprint) << " to " + << PrettySize(new_footprint) << " for a " << PrettySize(alloc_size) << " allocation"; +} + } // namespace gc } // namespace art diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index bb21b7cf00..0d11658e01 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -1099,6 +1099,9 @@ class Heap { void TraceHeapSize(size_t heap_size); + // Remove a vlog code from heap-inl.h which is transitively included in half the world. + static void VlogHeapGrowth(size_t max_allowed_footprint, size_t new_footprint, size_t alloc_size); + // All-known continuous spaces, where objects lie within fixed bounds. std::vector continuous_spaces_ GUARDED_BY(Locks::mutator_lock_); diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index 576a35c52d..a3eef90e3a 100644 --- a/runtime/gc/space/dlmalloc_space.cc +++ b/runtime/gc/space/dlmalloc_space.cc @@ -16,6 +16,7 @@ #include "dlmalloc_space-inl.h" +#include "base/logging.h" // For VLOG. #include "base/time_utils.h" #include "gc/accounting/card_table.h" #include "gc/accounting/space_bitmap-inl.h" diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc index dcb783782f..17274b508d 100644 --- a/runtime/gc/space/malloc_space.cc +++ b/runtime/gc/space/malloc_space.cc @@ -18,6 +18,7 @@ #include "android-base/stringprintf.h" +#include "base/logging.h" // For VLOG #include "gc/accounting/card_table-inl.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/heap.h" diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc index 5d1f191e4d..3a685cb82d 100644 --- a/runtime/gc/space/rosalloc_space.cc +++ b/runtime/gc/space/rosalloc_space.cc @@ -17,6 +17,7 @@ #include "rosalloc_space-inl.h" +#include "base/logging.h" // For VLOG. #include "base/time_utils.h" #include "gc/accounting/card_table.h" #include "gc/accounting/space_bitmap-inl.h" diff --git a/runtime/gc/verification.cc b/runtime/gc/verification.cc index 3cd04a61e9..d99b37762f 100644 --- a/runtime/gc/verification.cc +++ b/runtime/gc/verification.cc @@ -86,7 +86,7 @@ void Verification::LogHeapCorruption(ObjPtr holder, mirror::Object* ref, bool fatal) const { // Lowest priority logging first: - PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT); + PrintFileToLog("/proc/self/maps", android::base::LogSeverity::FATAL_WITHOUT_ABORT); MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true); // Buffer the output in the string stream since it is more important than the stack traces // and we want it to have log priority. The stack traces are printed from Runtime::Abort diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index a5c167eee8..6f03a688e6 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -21,6 +21,7 @@ #include "arch/context.h" #include "art_method-inl.h" #include "base/enums.h" +#include "base/logging.h" // For VLOG. #include "base/stl_util.h" #include "base/systrace.h" #include "base/time_utils.h" diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index acbc6e63a4..ee11cfddb0 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -25,6 +25,7 @@ #include "art_method-inl.h" #include "base/enums.h" +#include "base/logging.h" // For VLOG. #include "base/scoped_arena_containers.h" #include "base/stl_util.h" #include "base/systrace.h" diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 892c03912a..a0a2f46433 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -20,6 +20,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" +#include "base/logging.h" // For VLOG. #include "class-inl.h" #include "class_ext.h" #include "class_linker-inl.h" diff --git a/runtime/monitor.cc b/runtime/monitor.cc index d5520d9aca..12529058e2 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -21,6 +21,7 @@ #include "android-base/stringprintf.h" #include "art_method-inl.h" +#include "base/logging.h" // For VLOG. #include "base/mutex.h" #include "base/stl_util.h" #include "base/systrace.h" diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index c82df7119f..32f8df7010 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -39,6 +39,7 @@ #include "base/bit_vector.h" #include "base/enums.h" #include "base/file_utils.h" +#include "base/logging.h" // For VLOG_IS_ON. #include "base/stl_util.h" #include "base/systrace.h" #include "base/unix_file/fd_file.h" @@ -314,7 +315,7 @@ bool OatFileBase::ComputeFields(uint8_t* requested_base, if (requested_base != nullptr && begin_ != requested_base) { // Host can fail this check. Do not dump there to avoid polluting the output. if (kIsTargetBuild && (kIsDebugBuild || VLOG_IS_ON(oat))) { - PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); + PrintFileToLog("/proc/self/maps", android::base::LogSeverity::WARNING); } *error_msg = StringPrintf("Failed to find oatdata symbol at expected address: " "oatdata=%p != expected=%p. See process maps in the log.", @@ -1068,7 +1069,7 @@ void DlOpenOatFile::PreSetup(const std::string& elf_filename) { dl_iterate_context context0 = { Begin(), &dlopen_mmaps_, 0, 0}; if (dl_iterate_phdr(dl_iterate_context::callback, &context0) == 0) { // OK, give up and print an error. - PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); + PrintFileToLog("/proc/self/maps", android::base::LogSeverity::WARNING); LOG(ERROR) << "File " << elf_filename << " loaded with dlopen but cannot find its mmaps."; } } diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index f94923e065..a7771abc26 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -19,6 +19,7 @@ #include "arch/context.h" #include "art_method-inl.h" #include "base/enums.h" +#include "base/logging.h" // For VLOG_IS_ON. #include "dex_file_types.h" #include "dex_instruction.h" #include "entrypoints/entrypoint_utils.h" -- GitLab From dfebbac8e30172169dbdaaf7e6a0be51433d57f1 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 6 Dec 2017 14:17:22 -0800 Subject: [PATCH 141/226] ART: Add ScopedTrace constructor with lambda In an effort to allow more complex and expensive atrace tagging, add a ScopedTrace constructor that uses a passed-in lambda to compute the string lazily. Add a macro to simplify usage. Test: m Change-Id: I3b4576d786177042922fef0d05161e4cc11144d4 --- runtime/base/systrace.h | 13 +++++++++++++ runtime/verifier/method_verifier.cc | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/runtime/base/systrace.h b/runtime/base/systrace.h index 06db48a576..c6b6ff1d43 100644 --- a/runtime/base/systrace.h +++ b/runtime/base/systrace.h @@ -23,6 +23,8 @@ #include +#include "android-base/stringprintf.h" + namespace art { class ScopedTrace { @@ -30,6 +32,12 @@ class ScopedTrace { explicit ScopedTrace(const char* name) { ATRACE_BEGIN(name); } + template + explicit ScopedTrace(Fn fn) { + if (ATRACE_ENABLED()) { + ATRACE_BEGIN(fn().c_str()); + } + } explicit ScopedTrace(const std::string& name) : ScopedTrace(name.c_str()) {} @@ -38,6 +46,11 @@ class ScopedTrace { } }; +#define SCOPED_TRACE(fmtstr, ...) \ + ::art::ScopedTrace trace ## __LINE__([&]() { \ + return ::android::base::StringPrintf((fmtstr), __VA_ARGS__); \ + }) + } // namespace art #endif // ART_RUNTIME_BASE_SYSTRACE_H_ diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index be58a57c24..8bd3098998 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -284,7 +284,7 @@ FailureKind MethodVerifier::VerifyClass(Thread* self, bool allow_soft_failures, HardFailLogMode log_level, std::string* error) { - ScopedTrace trace(__FUNCTION__); + SCOPED_TRACE("VerifyClass %s", PrettyDescriptor(dex_file->GetClassDescriptor(class_def)).c_str()); // A class must not be abstract and final. if ((class_def.access_flags_ & (kAccAbstract | kAccFinal)) == (kAccAbstract | kAccFinal)) { -- GitLab From 219cb9021fa74af7773066ffb8fc77ac85f3d0de Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 7 Dec 2017 16:20:39 +0000 Subject: [PATCH 142/226] Reduce memory allocations in dexlayout's CodeFixups. Using heaptrack for the services.odex compilation of aosp_taimen-userdebug: - before: calls to allocation functions: 5032880 - after: calls to allocation functions: 4645378 Test: Rely on TreeHugger Bug: 70331349 Change-Id: I477d5a84e0bf22994ce3de9fe56525de0b498801 --- dexlayout/dex_ir.cc | 24 ++++++++++++------------ dexlayout/dex_ir.h | 34 +++++++++++++++++----------------- dexlayout/dex_visualize.cc | 12 ++++-------- dexlayout/dexlayout.cc | 6 +++--- 4 files changed, 36 insertions(+), 40 deletions(-) diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc index 2af579c73c..90df2d7bb9 100644 --- a/dexlayout/dex_ir.cc +++ b/dexlayout/dex_ir.cc @@ -674,20 +674,20 @@ CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, // Add "fixup" references to types, strings, methods, and fields. // This is temporary, as we will probably want more detailed parsing of the // instructions here. - std::unique_ptr> type_ids(new std::vector()); - std::unique_ptr> string_ids(new std::vector()); - std::unique_ptr> method_ids(new std::vector()); - std::unique_ptr> field_ids(new std::vector()); + std::vector type_ids; + std::vector string_ids; + std::vector method_ids; + std::vector field_ids; if (GetIdsFromByteCode(*this, code_item, - type_ids.get(), - string_ids.get(), - method_ids.get(), - field_ids.get())) { - CodeFixups* fixups = new CodeFixups(type_ids.release(), - string_ids.release(), - method_ids.release(), - field_ids.release()); + /*out*/ &type_ids, + /*out*/ &string_ids, + /*out*/ &method_ids, + /*out*/ &field_ids)) { + CodeFixups* fixups = new CodeFixups(std::move(type_ids), + std::move(string_ids), + std::move(method_ids), + std::move(field_ids)); code_item->SetCodeFixups(fixups); } diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h index 8421774104..b25e1645dd 100644 --- a/dexlayout/dex_ir.h +++ b/dexlayout/dex_ir.h @@ -1013,25 +1013,25 @@ using TryItemVector = std::vector>; class CodeFixups { public: - CodeFixups(std::vector* type_ids, - std::vector* string_ids, - std::vector* method_ids, - std::vector* field_ids) - : type_ids_(type_ids), - string_ids_(string_ids), - method_ids_(method_ids), - field_ids_(field_ids) { } - - std::vector* TypeIds() const { return type_ids_.get(); } - std::vector* StringIds() const { return string_ids_.get(); } - std::vector* MethodIds() const { return method_ids_.get(); } - std::vector* FieldIds() const { return field_ids_.get(); } + CodeFixups(std::vector type_ids, + std::vector string_ids, + std::vector method_ids, + std::vector field_ids) + : type_ids_(std::move(type_ids)), + string_ids_(std::move(string_ids)), + method_ids_(std::move(method_ids)), + field_ids_(std::move(field_ids)) { } + + const std::vector& TypeIds() const { return type_ids_; } + const std::vector& StringIds() const { return string_ids_; } + const std::vector& MethodIds() const { return method_ids_; } + const std::vector& FieldIds() const { return field_ids_; } private: - std::unique_ptr> type_ids_; - std::unique_ptr> string_ids_; - std::unique_ptr> method_ids_; - std::unique_ptr> field_ids_; + std::vector type_ids_; + std::vector string_ids_; + std::vector method_ids_; + std::vector field_ids_; DISALLOW_COPY_AND_ASSIGN(CodeFixups); }; diff --git a/dexlayout/dex_visualize.cc b/dexlayout/dex_visualize.cc index 4b46341ada..e4ed69b8d2 100644 --- a/dexlayout/dex_visualize.cc +++ b/dexlayout/dex_visualize.cc @@ -188,20 +188,16 @@ class Dumper { DumpAddressRange(code_item, class_index); const dex_ir::CodeFixups* fixups = code_item->GetCodeFixups(); if (fixups != nullptr) { - std::vector* type_ids = fixups->TypeIds(); - for (dex_ir::TypeId* type_id : *type_ids) { + for (dex_ir::TypeId* type_id : fixups->TypeIds()) { DumpTypeId(type_id, class_index); } - std::vector* string_ids = fixups->StringIds(); - for (dex_ir::StringId* string_id : *string_ids) { + for (dex_ir::StringId* string_id : fixups->StringIds()) { DumpStringId(string_id, class_index); } - std::vector* method_ids = fixups->MethodIds(); - for (dex_ir::MethodId* method_id : *method_ids) { + for (dex_ir::MethodId* method_id : fixups->MethodIds()) { DumpMethodId(method_id, class_index); } - std::vector* field_ids = fixups->FieldIds(); - for (dex_ir::FieldId* field_id : *field_ids) { + for (dex_ir::FieldId* field_id : fixups->FieldIds()) { DumpFieldId(field_id, class_index); } } diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index d904a52f0c..1016fca15b 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -1656,11 +1656,11 @@ void DexLayout::LayoutStringData(const DexFile* dex_file) { continue; } // Add const-strings. - for (dex_ir::StringId* id : *fixups->StringIds()) { + for (dex_ir::StringId* id : fixups->StringIds()) { from_hot_method[id->GetIndex()] = true; } // Add field classes, names, and types. - for (dex_ir::FieldId* id : *fixups->FieldIds()) { + for (dex_ir::FieldId* id : fixups->FieldIds()) { // TODO: Only visit field ids from static getters and setters. from_hot_method[id->Class()->GetStringId()->GetIndex()] = true; from_hot_method[id->Name()->GetIndex()] = true; @@ -1668,7 +1668,7 @@ void DexLayout::LayoutStringData(const DexFile* dex_file) { } // For clinits, add referenced method classes, names, and protos. if (is_clinit) { - for (dex_ir::MethodId* id : *fixups->MethodIds()) { + for (dex_ir::MethodId* id : fixups->MethodIds()) { from_hot_method[id->Class()->GetStringId()->GetIndex()] = true; from_hot_method[id->Name()->GetIndex()] = true; is_shorty[id->Proto()->Shorty()->GetIndex()] = true; -- GitLab From 93592f53e35f01b54fbc84a7502b2e055ff6fe45 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Fri, 8 Dec 2017 10:53:27 +0000 Subject: [PATCH 143/226] Fix VDEX header when no quickening info When there is no quickening info to be written, the OatWriter logic wrongly assumes that padding was written to align the file size to 32 bits. The file is therefore shorter than the size calculated from header data. This patch fixes that. Additionally, it enforces that OatWriter::WriteQuickeningInfo is always called, which was not the case with some of our tests. This requirement was already mentioned in OatWriter comments but not adhered to. Test: m test-art-host-gtest Bug: 70267762 Change-Id: If25600761677efba173d7cac02931e71c6260f9d --- dex2oat/linker/image_test.h | 17 +++++++++-------- dex2oat/linker/oat_writer.cc | 16 +++++++++++----- dex2oat/linker/oat_writer_test.cc | 11 +++++++++++ runtime/vdex_file.h | 18 +++++++++++++----- 4 files changed, 44 insertions(+), 18 deletions(-) diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index cedbccf7cc..85145d3d64 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -293,14 +293,7 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, bool image_space_ok = writer->PrepareImageAddressSpace(); ASSERT_TRUE(image_space_ok); - for (size_t i = 0, size = vdex_files.size(); i != size; ++i) { - std::unique_ptr vdex_out = - std::make_unique( - std::make_unique(vdex_files[i].GetFile())); - oat_writers[i]->WriteVerifierDeps(vdex_out.get(), nullptr); - oat_writers[i]->WriteChecksumsAndVdexHeader(vdex_out.get()); - } - + 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()); @@ -308,6 +301,14 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, ElfWriter* const elf_writer = elf_writers[i].get(); std::vector cur_dex_files(1u, class_path[i]); oat_writer->Initialize(driver, writer.get(), cur_dex_files); + + std::unique_ptr vdex_out = + std::make_unique( + std::make_unique(vdex_files[i].GetFile())); + oat_writer->WriteVerifierDeps(vdex_out.get(), nullptr); + oat_writer->WriteQuickeningInfo(vdex_out.get()); + 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; diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 293078acee..b5c5d98c11 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -2742,10 +2742,6 @@ bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) { size_t initial_offset = vdex_size_; size_t start_offset = RoundUp(initial_offset, 4u); - vdex_size_ = start_offset; - vdex_quickening_info_offset_ = vdex_size_; - size_quickening_info_alignment_ = start_offset - initial_offset; - off_t actual_offset = vdex_out->Seek(start_offset, kSeekSet); if (actual_offset != static_cast(start_offset)) { PLOG(ERROR) << "Failed to seek to quickening info section. Actual: " << actual_offset @@ -2789,7 +2785,16 @@ bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) { size_quickening_info_ = 0; } - vdex_size_ += size_quickening_info_; + if (size_quickening_info_ == 0) { + // Nothing was written. Leave `vdex_size_` untouched and unaligned. + vdex_quickening_info_offset_ = initial_offset; + size_quickening_info_alignment_ = 0; + } else { + vdex_size_ = start_offset + size_quickening_info_; + vdex_quickening_info_offset_ = start_offset; + size_quickening_info_alignment_ = start_offset - initial_offset; + } + return true; } @@ -3864,6 +3869,7 @@ bool OatWriter::WriteChecksumsAndVdexHeader(OutputStream* vdex_out) { 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_verifier_deps_offset_ - vdex_dex_files_offset_; size_t verifier_deps_section_size = vdex_quickening_info_offset_ - vdex_verifier_deps_offset_; diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index fec05cc393..e9958b129a 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -45,6 +45,7 @@ #include "oat_writer.h" #include "scoped_thread_state_change-inl.h" #include "utils/test_dex_file_builder.h" +#include "vdex_file.h" namespace art { namespace linker { @@ -225,6 +226,9 @@ class OatTest : public CommonCompilerTest { if (!oat_writer.WriteVerifierDeps(vdex_out.get(), nullptr)) { return false; } + if (!oat_writer.WriteQuickeningInfo(vdex_out.get())) { + return false; + } if (!oat_writer.WriteChecksumsAndVdexHeader(vdex_out.get())) { return false; } @@ -652,6 +656,13 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) { &opened_dex_file2->GetHeader(), 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(); + ASSERT_EQ(vdex_header.GetQuickeningInfoSize(), 0u); + + 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()); } TEST_F(OatTest, DexFileInputCheckOutput) { diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index 3e0882693a..2d9fcab59c 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -68,6 +68,18 @@ class VdexFile { 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() + + GetVerifierDepsSize() + + GetQuickeningInfoSize(); + } + + size_t GetSizeOfChecksumsSection() const { + return sizeof(VdexChecksum) * GetNumberOfDexFiles(); + } + static constexpr uint8_t kVdexInvalidMagic[] = { 'w', 'd', 'e', 'x' }; private: @@ -172,17 +184,13 @@ class VdexFile { } const uint8_t* DexBegin() const { - return Begin() + sizeof(Header) + GetSizeOfChecksumsSection(); + return Begin() + sizeof(Header) + GetHeader().GetSizeOfChecksumsSection(); } const uint8_t* DexEnd() const { return DexBegin() + GetHeader().GetDexSize(); } - size_t GetSizeOfChecksumsSection() const { - return sizeof(VdexChecksum) * GetHeader().GetNumberOfDexFiles(); - } - uint32_t GetDexFileIndex(const DexFile& dex_file) const; std::unique_ptr mmap_; -- GitLab From 1344914b3029a02c6f991e6e541c48ad39828d06 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 7 Dec 2017 22:26:24 +0000 Subject: [PATCH 144/226] Don't check the offset of a NPE for field accesses. The compiler might remove or reorder field accesses. bug: 70281892 Test: 671-npe-field-opts Change-Id: I5430a7a938aef3636911694d48b19bb166be9d06 --- runtime/common_throws.cc | 16 +++-- test/671-npe-field-opts/expected.txt | 0 test/671-npe-field-opts/info.txt | 3 + test/671-npe-field-opts/src/Main.java | 86 +++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 test/671-npe-field-opts/expected.txt create mode 100644 test/671-npe-field-opts/info.txt create mode 100644 test/671-npe-field-opts/src/Main.java diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index cd52bb6551..de71edb349 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -441,7 +441,7 @@ static bool IsValidReadBarrierImplicitCheck(uintptr_t addr) { return addr == monitor_offset; } -static bool IsValidImplicitCheck(uintptr_t addr, ArtMethod* method, const Instruction& instr) +static bool IsValidImplicitCheck(uintptr_t addr, const Instruction& instr) REQUIRES_SHARED(Locks::mutator_lock_) { if (!CanDoImplicitNullCheckOn(addr)) { return false; @@ -483,9 +483,10 @@ static bool IsValidImplicitCheck(uintptr_t addr, ArtMethod* method, const Instru case Instruction::IPUT_BYTE: case Instruction::IPUT_CHAR: case Instruction::IPUT_SHORT: { - ArtField* field = - Runtime::Current()->GetClassLinker()->ResolveField(instr.VRegC_22c(), method, false); - return (addr == 0) || (addr == field->GetOffset().Uint32Value()); + // We might be doing an implicit null check with an offset that doesn't correspond + // to the instruction, for example with two field accesses and the first one being + // eliminated or re-ordered. + return true; } case Instruction::IGET_OBJECT_QUICK: @@ -506,7 +507,10 @@ static bool IsValidImplicitCheck(uintptr_t addr, ArtMethod* method, const Instru case Instruction::IPUT_SHORT_QUICK: case Instruction::IPUT_WIDE_QUICK: case Instruction::IPUT_OBJECT_QUICK: { - return (addr == 0u) || (addr == instr.VRegC_22c()); + // We might be doing an implicit null check with an offset that doesn't correspond + // to the instruction, for example with two field accesses and the first one being + // eliminated or re-ordered. + return true; } case Instruction::AGET_OBJECT: @@ -550,7 +554,7 @@ void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) { const DexFile::CodeItem* code = method->GetCodeItem(); CHECK_LT(throw_dex_pc, code->insns_size_in_code_units_); const Instruction* instr = Instruction::At(&code->insns_[throw_dex_pc]); - if (check_address && !IsValidImplicitCheck(addr, method, *instr)) { + if (check_address && !IsValidImplicitCheck(addr, *instr)) { const DexFile* dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile(); LOG(FATAL) << "Invalid address for an implicit NullPointerException check: " << "0x" << std::hex << addr << std::dec diff --git a/test/671-npe-field-opts/expected.txt b/test/671-npe-field-opts/expected.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/671-npe-field-opts/info.txt b/test/671-npe-field-opts/info.txt new file mode 100644 index 0000000000..f1e584633f --- /dev/null +++ b/test/671-npe-field-opts/info.txt @@ -0,0 +1,3 @@ +Regression test for the compiler, which used to +re-order or remove field access in a way that would confuse the runtime +when validating a NPE. diff --git a/test/671-npe-field-opts/src/Main.java b/test/671-npe-field-opts/src/Main.java new file mode 100644 index 0000000000..a5e81ce672 --- /dev/null +++ b/test/671-npe-field-opts/src/Main.java @@ -0,0 +1,86 @@ +/* + * 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 { + static Main obj; + // Make 'doCheck' volatile to prevent optimizations + // in $noinline$bar like LICM that could hoist the null check + // out of the loop. + static volatile boolean doCheck = true; + + float floatField; + int intField; + + public static void main(String[] args) { + try { + $noinline$bar(); + throw new Error("Expected NPE"); + } catch (NullPointerException e) { + check(e, 29, 52, "$noinline$bar"); + } + + try { + $noinline$foo(); + throw new Error("Expected NPE"); + } catch (NullPointerException e) { + check(e, 36, 44, "$noinline$foo"); + } + } + + public static float $noinline$foo() { + int v1 = obj.intField; + float v2 = obj.floatField; + return v2; + } + + public static float $noinline$bar() { + float a = 0; + while (doCheck) { + float f = obj.floatField; + int i = obj.intField; + a = (float)i + f; + } + return a; + } + + static void check(NullPointerException npe, int mainLine, int methodLine, String methodName) { + StackTraceElement[] trace = npe.getStackTrace(); + checkElement(trace[0], "Main", methodName, "Main.java", methodLine); + checkElement(trace[1], "Main", "main", "Main.java", mainLine); + } + + static void checkElement(StackTraceElement element, + String declaringClass, String methodName, + String fileName, int lineNumber) { + assertEquals(declaringClass, element.getClassName()); + assertEquals(methodName, element.getMethodName()); + assertEquals(fileName, element.getFileName()); + assertEquals(lineNumber, element.getLineNumber()); + } + + static void assertEquals(Object expected, Object actual) { + if (!expected.equals(actual)) { + String msg = "Expected \"" + expected + "\" but got \"" + actual + "\""; + throw new AssertionError(msg); + } + } + + static void assertEquals(int expected, int actual) { + if (expected != actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } +} -- GitLab From 28e012a4af2d710e5e5f824709ffd6432e4f549f Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 7 Dec 2017 11:22:59 +0000 Subject: [PATCH 145/226] Determine HLoadClass/String load kind early. This helps save memory by avoiding the allocation of HEnvironment and related objects for AOT references to boot image strings and classes (kBootImage* load kinds) and also for JIT references (kJitTableAddress). Compiling aosp_taimen-userdebug boot image, the most memory hungry method BatteryStats.dumpLocked() needs - before: Used 55105384 bytes of arena memory... ... UseListNode 10009704 Environment 423248 EnvVRegs 20676560 ... - after: Used 50559176 bytes of arena memory... ... UseListNode 8568936 Environment 365680 EnvVRegs 17628704 ... Test: m test-art-host-gtest Test: testrunner.py --host --optimizing --jit Bug: 34053922 Change-Id: I68e73a438e6ac8e8908e6fccf53bbeea8a64a077 --- compiler/driver/compiler_driver-inl.h | 12 +- compiler/driver/compiler_driver.cc | 11 +- compiler/driver/compiler_driver.h | 7 +- compiler/optimizing/inliner.cc | 6 +- compiler/optimizing/instruction_builder.cc | 35 +++-- compiler/optimizing/instruction_builder.h | 11 +- compiler/optimizing/nodes.cc | 30 ---- compiler/optimizing/nodes.h | 29 ++++ compiler/optimizing/optimization.cc | 3 +- compiler/optimizing/sharpening.cc | 38 ++--- compiler/optimizing/sharpening.h | 19 ++- dex2oat/linker/oat_writer.cc | 21 +-- runtime/art_method.cc | 6 +- runtime/art_method.h | 2 +- runtime/class_linker-inl.h | 5 +- runtime/class_linker.cc | 38 ++--- runtime/class_linker.h | 28 ++-- runtime/class_linker_test.cc | 12 +- runtime/dex_file_annotations.cc | 17 ++- runtime/entrypoints/entrypoint_utils-inl.h | 32 +++-- runtime/entrypoints/entrypoint_utils.h | 13 +- .../quick/quick_dexcache_entrypoints.cc | 40 +++--- runtime/interpreter/mterp/mterp.cc | 24 ++-- runtime/transaction_test.cc | 5 +- runtime/verifier/method_verifier.cc | 22 +-- runtime/verifier/method_verifier.h | 3 +- test/552-checker-sharpening/src/Main.java | 130 ++---------------- 27 files changed, 263 insertions(+), 336 deletions(-) diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index b04392918d..7e118d5d85 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -32,13 +32,13 @@ namespace art { -inline mirror::Class* CompilerDriver::ResolveClass( +inline ObjPtr CompilerDriver::ResolveClass( const ScopedObjectAccess& soa, Handle dex_cache, Handle class_loader, dex::TypeIndex cls_index, const DexCompilationUnit* mUnit) { DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile()); DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); - mirror::Class* cls = mUnit->GetClassLinker()->ResolveType( + ObjPtr cls = mUnit->GetClassLinker()->ResolveType( *mUnit->GetDexFile(), cls_index, dex_cache, class_loader); DCHECK_EQ(cls == nullptr, soa.Self()->IsExceptionPending()); if (UNLIKELY(cls == nullptr)) { @@ -48,7 +48,7 @@ inline mirror::Class* CompilerDriver::ResolveClass( return cls; } -inline mirror::Class* CompilerDriver::ResolveCompilingMethodsClass( +inline ObjPtr CompilerDriver::ResolveCompilingMethodsClass( const ScopedObjectAccess& soa, Handle dex_cache, Handle class_loader, const DexCompilationUnit* mUnit) { DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile()); @@ -89,8 +89,10 @@ inline ArtField* CompilerDriver::ResolveField( } inline std::pair CompilerDriver::IsFastInstanceField( - mirror::DexCache* dex_cache, mirror::Class* referrer_class, - ArtField* resolved_field, uint16_t field_idx) { + ObjPtr dex_cache, + ObjPtr referrer_class, + ArtField* resolved_field, + uint16_t field_idx) { DCHECK(!resolved_field->IsStatic()); ObjPtr fields_class = resolved_field->GetDeclaringClass(); bool fast_get = referrer_class != nullptr && diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 0ca3c8f613..f49d119900 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -717,7 +717,8 @@ static void ResolveConstStrings(Handle dex_cache, dex::StringIndex string_index((inst->Opcode() == Instruction::CONST_STRING) ? inst->VRegB_21c() : inst->VRegB_31c()); - mirror::String* string = class_linker->ResolveString(dex_file, string_index, dex_cache); + ObjPtr string = + class_linker->ResolveString(dex_file, string_index, dex_cache); CHECK(string != nullptr) << "Could not allocate a string when forcing determinism"; break; } @@ -1371,7 +1372,7 @@ ArtField* CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const ScopedObjectAccess& soa) { // Try to resolve the field and compiling method's class. ArtField* resolved_field; - mirror::Class* referrer_class; + ObjPtr referrer_class; Handle dex_cache(mUnit->GetDexCache()); { Handle class_loader_handle = mUnit->GetClassLoader(); @@ -1542,7 +1543,7 @@ class ParallelCompilationManager { // A fast version of SkipClass above if the class pointer is available // that avoids the expensive FindInClassPath search. -static bool SkipClass(jobject class_loader, const DexFile& dex_file, mirror::Class* klass) +static bool SkipClass(jobject class_loader, const DexFile& dex_file, ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(klass != nullptr); const DexFile& original_dex_file = *klass->GetDexCache()->GetDexFile(); @@ -1636,8 +1637,8 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { Handle dex_cache(hs.NewHandle(class_linker->FindDexCache( soa.Self(), dex_file))); // Resolve the class. - mirror::Class* klass = class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache, - class_loader); + ObjPtr klass = + class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache, class_loader); bool resolve_fields_and_methods; if (klass == nullptr) { // Class couldn't be resolved, for example, super-class is in a different dex file. Don't diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index d2141e8bc7..ce7ec4520d 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -219,12 +219,12 @@ class CompilerDriver { REQUIRES_SHARED(Locks::mutator_lock_); // Resolve compiling method's class. Returns null on failure. - mirror::Class* ResolveCompilingMethodsClass( + ObjPtr ResolveCompilingMethodsClass( const ScopedObjectAccess& soa, Handle dex_cache, Handle class_loader, const DexCompilationUnit* mUnit) REQUIRES_SHARED(Locks::mutator_lock_); - mirror::Class* ResolveClass( + ObjPtr ResolveClass( const ScopedObjectAccess& soa, Handle dex_cache, Handle class_loader, dex::TypeIndex type_index, const DexCompilationUnit* mUnit) @@ -247,7 +247,8 @@ class CompilerDriver { // Can we fast-path an IGET/IPUT access to an instance field? If yes, compute the field offset. std::pair IsFastInstanceField( - mirror::DexCache* dex_cache, mirror::Class* referrer_class, + ObjPtr dex_cache, + ObjPtr referrer_class, ArtField* resolved_field, uint16_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 560372e22e..a175c21760 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -876,9 +876,9 @@ HInstruction* HInliner::AddTypeGuard(HInstruction* receiver, load_class, codegen_, compiler_driver_, caller_compilation_unit_); DCHECK(kind != HLoadClass::LoadKind::kInvalid) << "We should always be able to reference a class for inline caches"; - // Insert before setting the kind, as setting the kind affects the inputs. - bb_cursor->InsertInstructionAfter(load_class, receiver_class); + // Load kind must be set before inserting the instruction into the graph. load_class->SetLoadKind(kind); + bb_cursor->InsertInstructionAfter(load_class, receiver_class); // In AOT mode, we will most likely load the class from BSS, which will involve a call // to the runtime. In this case, the load instruction will need an environment so copy // it from the invoke instruction. @@ -1932,7 +1932,7 @@ void HInliner::RunOptimizations(HGraph* callee_graph, // optimization that could lead to a HDeoptimize. The following optimizations do not. HDeadCodeElimination dce(callee_graph, inline_stats_, "dead_code_elimination$inliner"); HConstantFolding fold(callee_graph, "constant_folding$inliner"); - HSharpening sharpening(callee_graph, codegen_, dex_compilation_unit, compiler_driver_, handles_); + HSharpening sharpening(callee_graph, codegen_, compiler_driver_); InstructionSimplifier simplify(callee_graph, codegen_, compiler_driver_, inline_stats_); IntrinsicsRecognizer intrinsics(callee_graph, inline_stats_); diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 782546c9d8..4485f064c6 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1128,7 +1128,7 @@ void HInstructionBuilder::BuildConstructorFenceForAllocation(HInstruction* alloc MethodCompilationStat::kConstructorFenceGeneratedNew); } -static bool IsSubClass(mirror::Class* to_test, mirror::Class* super_class) +static bool IsSubClass(ObjPtr to_test, ObjPtr super_class) REQUIRES_SHARED(Locks::mutator_lock_) { return to_test != nullptr && !to_test->IsInterface() && to_test->IsSubClass(super_class); } @@ -1424,7 +1424,7 @@ bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instructio return true; } -static mirror::Class* GetClassFrom(CompilerDriver* driver, +static ObjPtr GetClassFrom(CompilerDriver* driver, const DexCompilationUnit& compilation_unit) { ScopedObjectAccess soa(Thread::Current()); Handle class_loader = compilation_unit.GetClassLoader(); @@ -1433,11 +1433,11 @@ static mirror::Class* GetClassFrom(CompilerDriver* driver, return driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, &compilation_unit); } -mirror::Class* HInstructionBuilder::GetOutermostCompilingClass() const { +ObjPtr HInstructionBuilder::GetOutermostCompilingClass() const { return GetClassFrom(compiler_driver_, *outer_compilation_unit_); } -mirror::Class* HInstructionBuilder::GetCompilingClass() const { +ObjPtr HInstructionBuilder::GetCompilingClass() const { return GetClassFrom(compiler_driver_, *dex_compilation_unit_); } @@ -1799,6 +1799,17 @@ static TypeCheckKind ComputeTypeCheckKind(Handle cls) } } +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); + HSharpening::ProcessLoadString(load_string, + code_generator_, + compiler_driver_, + *dex_compilation_unit_, + handles_); + AppendInstruction(load_string); +} + HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc) { ScopedObjectAccess soa(Thread::Current()); const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); @@ -1811,7 +1822,7 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint3 if (klass->IsPublic()) { needs_access_check = false; } else { - mirror::Class* compiling_class = GetCompilingClass(); + ObjPtr compiling_class = GetCompilingClass(); if (compiling_class != nullptr && compiling_class->CanAccess(klass.Get())) { needs_access_check = false; } @@ -1856,9 +1867,9 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, // We actually cannot reference this class, we're forced to bail. return nullptr; } - // Append the instruction first, as setting the load kind affects the inputs. - AppendInstruction(load_class); + // Load kind must be set before inserting the instruction into the graph. load_class->SetLoadKind(load_kind); + AppendInstruction(load_class); return load_class; } @@ -2837,20 +2848,14 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::CONST_STRING: { dex::StringIndex string_index(instruction.VRegB_21c()); - AppendInstruction(new (allocator_) HLoadString(graph_->GetCurrentMethod(), - string_index, - *dex_file_, - dex_pc)); + BuildLoadString(string_index, dex_pc); UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); break; } case Instruction::CONST_STRING_JUMBO: { dex::StringIndex string_index(instruction.VRegB_31c()); - AppendInstruction(new (allocator_) HLoadString(graph_->GetCurrentMethod(), - string_index, - *dex_file_, - dex_pc)); + BuildLoadString(string_index, dex_pc); UpdateLocal(instruction.VRegA_31c(), current_block_->GetLastInstruction()); break; } diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index 2446ddb86a..0500d40cd3 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -240,9 +240,10 @@ class HInstructionBuilder : public ValueObject { // Builds an instruction sequence for a switch statement. void BuildSwitch(const Instruction& instruction, uint32_t dex_pc); - // Builds a `HLoadClass` loading the given `type_index`. If `outer` is true, - // this method will use the outer class's dex file to lookup the type at - // `type_index`. + // Builds a `HLoadString` loading the given `string_index`. + void BuildLoadString(dex::StringIndex string_index, uint32_t dex_pc); + + // Builds a `HLoadClass` loading the given `type_index`. HLoadClass* BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc); HLoadClass* BuildLoadClass(dex::TypeIndex type_index, @@ -253,10 +254,10 @@ class HInstructionBuilder : public ValueObject { REQUIRES_SHARED(Locks::mutator_lock_); // Returns the outer-most compiling method's class. - mirror::Class* GetOutermostCompilingClass() const; + ObjPtr GetOutermostCompilingClass() const; // Returns the class whose method is being compiled. - mirror::Class* GetCompilingClass() const; + ObjPtr GetCompilingClass() const; // Returns whether `type_index` points to the outer-most compiling method's class. bool IsOutermostCompilingClass(dex::TypeIndex type_index) const; diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index d117bfb67d..5f33ed6303 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -2831,21 +2831,6 @@ bool HLoadClass::InstructionDataEquals(const HInstruction* other) const { } } -void HLoadClass::SetLoadKind(LoadKind load_kind) { - SetPackedField(load_kind); - - if (load_kind != LoadKind::kRuntimeCall && - load_kind != LoadKind::kReferrersClass) { - RemoveAsUserOfInput(0u); - SetRawInputAt(0u, nullptr); - } - - if (!NeedsEnvironment()) { - RemoveEnvironment(); - SetSideEffects(SideEffects::None()); - } -} - std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs) { switch (rhs) { case HLoadClass::LoadKind::kReferrersClass: @@ -2888,21 +2873,6 @@ bool HLoadString::InstructionDataEquals(const HInstruction* other) const { } } -void HLoadString::SetLoadKind(LoadKind load_kind) { - // Once sharpened, the load kind should not be changed again. - DCHECK_EQ(GetLoadKind(), LoadKind::kRuntimeCall); - SetPackedField(load_kind); - - if (load_kind != LoadKind::kRuntimeCall) { - RemoveAsUserOfInput(0u); - SetRawInputAt(0u, nullptr); - } - if (!NeedsEnvironment()) { - RemoveEnvironment(); - SetSideEffects(SideEffects::None()); - } -} - std::ostream& operator<<(std::ostream& os, HLoadString::LoadKind rhs) { switch (rhs) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 7fbd7f48a2..42a9d95b9a 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -6060,6 +6060,20 @@ class HLoadClass FINAL : public HInstruction { }; std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs); +// Note: defined outside class to see operator<<(., HLoadClass::LoadKind). +inline void HLoadClass::SetLoadKind(LoadKind load_kind) { + // The load kind should be determined before inserting the instruction to the graph. + DCHECK(GetBlock() == nullptr); + DCHECK(GetEnvironment() == nullptr); + SetPackedField(load_kind); + if (load_kind != LoadKind::kRuntimeCall && load_kind != LoadKind::kReferrersClass) { + special_input_ = HUserRecord(nullptr); + } + if (!NeedsEnvironment()) { + SetSideEffects(SideEffects::None()); + } +} + // Note: defined outside class to see operator<<(., HLoadClass::LoadKind). inline void HLoadClass::AddSpecialInput(HInstruction* special_input) { // The special input is used for PC-relative loads on some architectures, @@ -6207,6 +6221,21 @@ class HLoadString FINAL : public HInstruction { }; std::ostream& operator<<(std::ostream& os, HLoadString::LoadKind rhs); +// Note: defined outside class to see operator<<(., HLoadString::LoadKind). +inline void HLoadString::SetLoadKind(LoadKind load_kind) { + // The load kind should be determined before inserting the instruction to the graph. + DCHECK(GetBlock() == nullptr); + DCHECK(GetEnvironment() == nullptr); + DCHECK_EQ(GetLoadKind(), LoadKind::kRuntimeCall); + SetPackedField(load_kind); + if (load_kind != LoadKind::kRuntimeCall) { + special_input_ = HUserRecord(nullptr); + } + if (!NeedsEnvironment()) { + SetSideEffects(SideEffects::None()); + } +} + // Note: defined outside class to see operator<<(., HLoadString::LoadKind). inline void HLoadString::AddSpecialInput(HInstruction* special_input) { // The special input is used for PC-relative loads on some architectures, diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc index 7edb642c5b..7149d93d07 100644 --- a/compiler/optimizing/optimization.cc +++ b/compiler/optimizing/optimization.cc @@ -258,8 +258,7 @@ ArenaVector ConstructOptimizations( break; } case OptimizationPass::kSharpening: - opt = new (allocator) HSharpening( - graph, codegen, dex_compilation_unit, driver, handles, name); + opt = new (allocator) HSharpening(graph, codegen, driver, name); break; case OptimizationPass::kSelectGenerator: opt = new (allocator) HSelectGenerator(graph, handles, stats, name); diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index e46c9a7081..64092d307d 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -45,8 +45,6 @@ void HSharpening::Run() { SharpenInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect(), codegen_, compiler_driver_); - } else if (instruction->IsLoadString()) { - ProcessLoadString(instruction->AsLoadString()); } // TODO: Move the sharpening of invoke-virtual/-interface/-super from HGraphBuilder // here. Rewrite it to avoid the CompilerDriver's reliance on verifier data @@ -147,10 +145,11 @@ void HSharpening::SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, invoke->SetDispatchInfo(dispatch_info); } -HLoadClass::LoadKind HSharpening::ComputeLoadClassKind(HLoadClass* load_class, - CodeGenerator* codegen, - CompilerDriver* compiler_driver, - const DexCompilationUnit& dex_compilation_unit) { +HLoadClass::LoadKind HSharpening::ComputeLoadClassKind( + HLoadClass* load_class, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + const DexCompilationUnit& dex_compilation_unit) { Handle klass = load_class->GetClass(); DCHECK(load_class->GetLoadKind() == HLoadClass::LoadKind::kRuntimeCall || load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass) @@ -237,7 +236,12 @@ HLoadClass::LoadKind HSharpening::ComputeLoadClassKind(HLoadClass* load_class, return load_kind; } -void HSharpening::ProcessLoadString(HLoadString* load_string) { +void HSharpening::ProcessLoadString( + HLoadString* load_string, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + const DexCompilationUnit& dex_compilation_unit, + VariableSizedHandleScope* handles) { DCHECK_EQ(load_string->GetLoadKind(), HLoadString::LoadKind::kRuntimeCall); const DexFile& dex_file = load_string->GetDexFile(); @@ -249,26 +253,26 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { ClassLinker* class_linker = runtime->GetClassLinker(); ScopedObjectAccess soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); - Handle dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile()) - ? compilation_unit_.GetDexCache() + Handle dex_cache = IsSameDexFile(dex_file, *dex_compilation_unit.GetDexFile()) + ? dex_compilation_unit.GetDexCache() : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)); - mirror::String* string = nullptr; + ObjPtr string = nullptr; - if (codegen_->GetCompilerOptions().IsBootImage()) { + if (codegen->GetCompilerOptions().IsBootImage()) { // Compiling boot image. Resolve the string and allocate it if needed, to ensure // the string will be added to the boot image. DCHECK(!runtime->UseJitCompilation()); string = class_linker->ResolveString(dex_file, string_index, dex_cache); CHECK(string != nullptr); - if (compiler_driver_->GetSupportBootImageFixup()) { - DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file)); + if (compiler_driver->GetSupportBootImageFixup()) { + DCHECK(ContainsElement(compiler_driver->GetDexFilesForOatFile(), &dex_file)); desired_load_kind = HLoadString::LoadKind::kBootImageLinkTimePcRelative; } else { // compiler_driver_test. Do not sharpen. desired_load_kind = HLoadString::LoadKind::kRuntimeCall; } } else if (runtime->UseJitCompilation()) { - DCHECK(!codegen_->GetCompilerOptions().GetCompilePic()); + DCHECK(!codegen->GetCompilerOptions().GetCompilePic()); string = class_linker->LookupString(dex_file, string_index, dex_cache.Get()); if (string != nullptr) { if (runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { @@ -283,7 +287,7 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { // AOT app compilation. Try to lookup the string without allocating if not found. string = class_linker->LookupString(dex_file, string_index, dex_cache.Get()); if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { - if (codegen_->GetCompilerOptions().GetCompilePic()) { + if (codegen->GetCompilerOptions().GetCompilePic()) { desired_load_kind = HLoadString::LoadKind::kBootImageInternTable; } else { desired_load_kind = HLoadString::LoadKind::kBootImageAddress; @@ -293,12 +297,12 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { } } if (string != nullptr) { - load_string->SetString(handles_->NewHandle(string)); + load_string->SetString(handles->NewHandle(string)); } } DCHECK_NE(desired_load_kind, static_cast(-1)); - HLoadString::LoadKind load_kind = codegen_->GetSupportedLoadStringKind(desired_load_kind); + HLoadString::LoadKind load_kind = codegen->GetSupportedLoadStringKind(desired_load_kind); load_string->SetLoadKind(load_kind); } diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h index bb1954eeeb..6df7d6d91e 100644 --- a/compiler/optimizing/sharpening.h +++ b/compiler/optimizing/sharpening.h @@ -34,26 +34,29 @@ class HSharpening : public HOptimization { public: HSharpening(HGraph* graph, CodeGenerator* codegen, - const DexCompilationUnit& compilation_unit, CompilerDriver* compiler_driver, - VariableSizedHandleScope* handles, const char* name = kSharpeningPassName) : HOptimization(graph, name), codegen_(codegen), - compilation_unit_(compilation_unit), - compiler_driver_(compiler_driver), - handles_(handles) { } + compiler_driver_(compiler_driver) { } void Run() OVERRIDE; 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 the builder and the inliner. static HLoadClass::LoadKind ComputeLoadClassKind(HLoadClass* load_class, CodeGenerator* codegen, CompilerDriver* compiler_driver, const DexCompilationUnit& dex_compilation_unit) - REQUIRES_SHARED(Locks::mutator_lock_); + REQUIRES_SHARED(Locks::mutator_lock_); // Used by Sharpening and InstructionSimplifier. static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, @@ -61,12 +64,8 @@ class HSharpening : public HOptimization { CompilerDriver* compiler_driver); private: - void ProcessLoadString(HLoadString* load_string); - CodeGenerator* codegen_; - const DexCompilationUnit& compilation_unit_; CompilerDriver* compiler_driver_; - VariableSizedHandleScope* handles_; }; } // namespace art diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 293078acee..ca7f1b37ab 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -1951,20 +1951,22 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { : class_linker_->FindDexCache(Thread::Current(), *target_dex_file); } - mirror::Class* GetTargetType(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr GetTargetType(const LinkerPatch& patch) + REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(writer_->HasImage()); ObjPtr dex_cache = GetDexCache(patch.TargetTypeDexFile()); ObjPtr type = ClassLinker::LookupResolvedType(patch.TargetTypeIndex(), dex_cache, class_loader_); CHECK(type != nullptr); - return type.Ptr(); + return type; } - mirror::String* GetTargetString(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr GetTargetString(const LinkerPatch& patch) + REQUIRES_SHARED(Locks::mutator_lock_) { ClassLinker* linker = Runtime::Current()->GetClassLinker(); - mirror::String* string = linker->LookupString(*patch.TargetStringDexFile(), - patch.TargetStringIndex(), - GetDexCache(patch.TargetStringDexFile())); + ObjPtr string = linker->LookupString(*patch.TargetStringDexFile(), + patch.TargetStringIndex(), + GetDexCache(patch.TargetStringDexFile())); DCHECK(string != nullptr); DCHECK(writer_->HasBootImage() || Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(string)); @@ -1980,13 +1982,14 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { return static_cast(reinterpret_cast(method) - oat_data_begin); } - uint32_t GetTargetObjectOffset(mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t GetTargetObjectOffset(ObjPtr object) + REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(writer_->HasBootImage()); - object = writer_->image_writer_->GetImageAddress(object); + object = writer_->image_writer_->GetImageAddress(object.Ptr()); size_t oat_index = writer_->image_writer_->GetOatIndexForDexFile(dex_file_); uintptr_t oat_data_begin = writer_->image_writer_->GetOatDataBegin(oat_index); // TODO: Clean up offset types. The target offset must be treated as signed. - return static_cast(reinterpret_cast(object) - oat_data_begin); + return static_cast(reinterpret_cast(object.Ptr()) - oat_data_begin); } void PatchObjectAddress(std::vector* code, uint32_t offset, mirror::Object* object) diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 43a51391b9..ebfa4feefa 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -134,7 +134,7 @@ uint16_t ArtMethod::FindObsoleteDexClassDefIndex() { return dex_file->GetIndexForClassDef(*class_def); } -mirror::String* ArtMethod::GetNameAsString(Thread* self) { +ObjPtr ArtMethod::GetNameAsString(Thread* self) { CHECK(!IsProxyMethod()); StackHandleScope<1> hs(self); Handle dex_cache(hs.NewHandle(GetDexCache())); @@ -550,8 +550,8 @@ bool ArtMethod::EqualParameters(Handle> param } auto* cl = Runtime::Current()->GetClassLinker(); for (size_t i = 0; i < count; ++i) { - auto type_idx = proto_params->GetTypeItem(i).type_idx_; - auto* type = cl->ResolveType(type_idx, this); + dex::TypeIndex type_idx = proto_params->GetTypeItem(i).type_idx_; + ObjPtr type = cl->ResolveType(type_idx, this); if (type == nullptr) { Thread::Current()->AssertPendingException(); return false; diff --git a/runtime/art_method.h b/runtime/art_method.h index 0a592e0528..60e436cc7e 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -575,7 +575,7 @@ class ArtMethod FINAL { ALWAYS_INLINE const char* GetName() REQUIRES_SHARED(Locks::mutator_lock_); - mirror::String* GetNameAsString(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr GetNameAsString(Thread* self) 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 d6f003027b..648b455e50 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -73,7 +73,8 @@ inline ObjPtr ClassLinker::LookupResolvedType( return type; } -inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) { +inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, + ArtMethod* referrer) { Thread::PoisonObjectPointersIfDebug(); if (kIsDebugBuild) { Thread::Current()->AssertNoPendingException(); @@ -87,7 +88,7 @@ inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMetho const DexFile& dex_file = *dex_cache->GetDexFile(); resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader); } - return resolved_type.Ptr(); + return resolved_type; } template diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index e5bb7862cb..80d4bb15b8 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -7727,14 +7727,14 @@ void ClassLinker::CreateReferenceInstanceOffsets(Handle klass) { klass->SetReferenceInstanceOffsets(reference_offsets); } -mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, - dex::StringIndex string_idx, - Handle dex_cache) { +ObjPtr ClassLinker::ResolveString(const DexFile& dex_file, + 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.Ptr(); + return resolved; } uint32_t utf16_length; const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length); @@ -7742,16 +7742,16 @@ mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, if (string != nullptr) { dex_cache->SetResolvedString(string_idx, string); } - return string.Ptr(); + return string; } -mirror::String* ClassLinker::LookupString(const DexFile& dex_file, - dex::StringIndex string_idx, - ObjPtr dex_cache) { +ObjPtr ClassLinker::LookupString(const DexFile& dex_file, + dex::StringIndex string_idx, + ObjPtr dex_cache) { DCHECK(dex_cache != nullptr); ObjPtr resolved = dex_cache->GetResolvedString(string_idx); if (resolved != nullptr) { - return resolved.Ptr(); + return resolved; } uint32_t utf16_length; const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length); @@ -7760,7 +7760,7 @@ mirror::String* ClassLinker::LookupString(const DexFile& dex_file, if (string != nullptr) { dex_cache->SetResolvedString(string_idx, string); } - return string.Ptr(); + return string; } ObjPtr ClassLinker::LookupResolvedType(const DexFile& dex_file, @@ -7794,19 +7794,19 @@ ObjPtr ClassLinker::LookupResolvedType(const DexFile& dex_file, return type; } -mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, - dex::TypeIndex type_idx, - ObjPtr referrer) { +ObjPtr ClassLinker::ResolveType(const DexFile& dex_file, + 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 ResolveType(dex_file, type_idx, dex_cache, class_loader); } -mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, - dex::TypeIndex type_idx, - Handle dex_cache, - Handle class_loader) { +ObjPtr ClassLinker::ResolveType(const DexFile& dex_file, + dex::TypeIndex type_idx, + Handle dex_cache, + Handle class_loader) { DCHECK(dex_cache != nullptr); Thread::PoisonObjectPointersIfDebug(); ObjPtr resolved = dex_cache->GetResolvedType(type_idx); @@ -7835,7 +7835,7 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, } DCHECK((resolved == nullptr) || resolved->IsResolved()) << resolved->PrettyDescriptor() << " " << resolved->GetStatus(); - return resolved.Ptr(); + return resolved; } template @@ -8410,7 +8410,7 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod( DexFileParameterIterator it(*dex_file, target_method->GetPrototype()); while (it.HasNext()) { const dex::TypeIndex type_idx = it.GetTypeIdx(); - mirror::Class* klass = ResolveType(*dex_file, type_idx, dex_cache, class_loader); + ObjPtr klass = ResolveType(*dex_file, type_idx, dex_cache, class_loader); if (nullptr == klass) { DCHECK(self->IsExceptionPending()); return nullptr; diff --git a/runtime/class_linker.h b/runtime/class_linker.h index a4c4f3d9ab..16255f4542 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -245,31 +245,31 @@ class ClassLinker { // Resolve a String with the given index from the DexFile, storing the // result in the DexCache. - mirror::String* ResolveString(const DexFile& dex_file, - dex::StringIndex string_idx, - Handle dex_cache) + ObjPtr ResolveString(const DexFile& dex_file, + dex::StringIndex string_idx, + Handle dex_cache) REQUIRES_SHARED(Locks::mutator_lock_); // Find a String with the given index from the DexFile, storing the // result in the DexCache if found. Return null if not found. - mirror::String* LookupString(const DexFile& dex_file, - dex::StringIndex string_idx, - ObjPtr dex_cache) + ObjPtr LookupString(const DexFile& dex_file, + dex::StringIndex string_idx, + ObjPtr dex_cache) REQUIRES_SHARED(Locks::mutator_lock_); // Resolve a Type with the given index from the DexFile, storing the // result in the DexCache. The referrer is used to identify the // target DexCache and ClassLoader to use for resolution. - mirror::Class* ResolveType(const DexFile& dex_file, - dex::TypeIndex type_idx, - ObjPtr referrer) + ObjPtr ResolveType(const DexFile& dex_file, + 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, storing the // result in the DexCache. The referrer is used to identify the // target DexCache and ClassLoader to use for resolution. - mirror::Class* ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) + ObjPtr ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); @@ -289,10 +289,10 @@ class ClassLinker { // result in DexCache. The ClassLoader is used to search for the // type, since it may be referenced from but not contained within // the given DexFile. - mirror::Class* ResolveType(const DexFile& dex_file, - dex::TypeIndex type_idx, - Handle dex_cache, - Handle class_loader) + ObjPtr ResolveType(const DexFile& dex_file, + dex::TypeIndex type_idx, + Handle dex_cache, + Handle class_loader) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 892a850997..94125507ef 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -1304,10 +1304,18 @@ TEST_F(ClassLinkerTest, ResolveVerifyAndClinit) { const DexFile::TypeId* type_id = dex_file->FindTypeId("LStaticsFromCode;"); ASSERT_TRUE(type_id != nullptr); dex::TypeIndex type_idx = dex_file->GetIndexForTypeId(*type_id); - mirror::Class* uninit = ResolveVerifyAndClinit(type_idx, clinit, soa.Self(), true, false); + ObjPtr uninit = ResolveVerifyAndClinit(type_idx, + clinit, + soa.Self(), + /* can_run_clinit */ true, + /* verify_access */ false); EXPECT_TRUE(uninit != nullptr); EXPECT_FALSE(uninit->IsInitialized()); - mirror::Class* init = ResolveVerifyAndClinit(type_idx, getS0, soa.Self(), true, false); + ObjPtr init = ResolveVerifyAndClinit(type_idx, + getS0, + soa.Self(), + /* can_run_clinit */ true, + /* verify_access */ false); EXPECT_TRUE(init != nullptr); EXPECT_TRUE(init->IsInitialized()); } diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index b44bd51643..27b9202d25 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -783,9 +783,8 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet( uint32_t type_index = DecodeUnsignedLeb128(&annotation); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Thread* self = Thread::Current(); - mirror::Class* resolved_class; StackHandleScope<2> hs(self); - resolved_class = class_linker->ResolveType( + ObjPtr resolved_class = class_linker->ResolveType( klass.GetDexFile(), dex::TypeIndex(type_index), hs.NewHandle(klass.GetDexCache()), @@ -1594,17 +1593,17 @@ void RuntimeEncodedStaticFieldValueIterator::ReadValueToField(ArtField* field) c case kDouble: field->SetDouble(field->GetDeclaringClass(), jval_.d); break; case kNull: field->SetObject(field->GetDeclaringClass(), nullptr); break; case kString: { - mirror::String* resolved = linker_->ResolveString(dex_file_, - dex::StringIndex(jval_.i), - *dex_cache_); + ObjPtr resolved = linker_->ResolveString(dex_file_, + dex::StringIndex(jval_.i), + *dex_cache_); field->SetObject(field->GetDeclaringClass(), resolved); break; } case kType: { - mirror::Class* resolved = linker_->ResolveType(dex_file_, - dex::TypeIndex(jval_.i), - *dex_cache_, - *class_loader_); + ObjPtr resolved = linker_->ResolveType(dex_file_, + dex::TypeIndex(jval_.i), + *dex_cache_, + *class_loader_); field->SetObject(field->GetDeclaringClass(), resolved); break; } diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 8253739427..475c1e7e90 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -245,7 +245,7 @@ inline mirror::Class* CheckArrayAlloc(dex::TypeIndex type_idx, *slow_path = true; return nullptr; // Failure } - mirror::Class* klass = method->GetDexCache()->GetResolvedType(type_idx); + ObjPtr klass = method->GetDexCache()->GetResolvedType(type_idx); if (UNLIKELY(klass == nullptr)) { // Not in dex cache so try to resolve ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); klass = class_linker->ResolveType(type_idx, method); @@ -264,7 +264,7 @@ inline mirror::Class* CheckArrayAlloc(dex::TypeIndex type_idx, return nullptr; // Failure } } - return klass; + return klass.Ptr(); } // Given the context of a calling Method, use its DexCache to resolve a type to an array Class. If @@ -500,7 +500,8 @@ inline ArtMethod* FindMethodFromCode(uint32_t method_idx, Handle h_referring_class(hs2.NewHandle(referrer->GetDeclaringClass())); const dex::TypeIndex method_type_idx = referrer->GetDexFile()->GetMethodId(method_idx).class_idx_; - mirror::Class* method_reference_class = class_linker->ResolveType(method_type_idx, referrer); + ObjPtr method_reference_class = + class_linker->ResolveType(method_type_idx, referrer); if (UNLIKELY(method_reference_class == nullptr)) { // Bad type idx. CHECK(self->IsExceptionPending()); @@ -711,13 +712,13 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx, } } -inline mirror::Class* ResolveVerifyAndClinit(dex::TypeIndex type_idx, - ArtMethod* referrer, - Thread* self, - bool can_run_clinit, - bool verify_access) { +inline ObjPtr ResolveVerifyAndClinit(dex::TypeIndex type_idx, + ArtMethod* referrer, + Thread* self, + bool can_run_clinit, + bool verify_access) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - mirror::Class* klass = class_linker->ResolveType(type_idx, referrer); + ObjPtr klass = class_linker->ResolveType(type_idx, referrer); if (UNLIKELY(klass == nullptr)) { CHECK(self->IsExceptionPending()); return nullptr; // Failure - Indicate to caller to deliver exception @@ -748,9 +749,9 @@ inline mirror::Class* ResolveVerifyAndClinit(dex::TypeIndex type_idx, return h_class.Get(); } -static inline mirror::String* ResolveString(ClassLinker* class_linker, - dex::StringIndex string_idx, - ArtMethod* referrer) +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); @@ -760,10 +761,11 @@ static inline mirror::String* ResolveString(ClassLinker* class_linker, const DexFile& dex_file = *dex_cache->GetDexFile(); string = class_linker->ResolveString(dex_file, string_idx, dex_cache); } - return string.Ptr(); + return string; } -inline mirror::String* ResolveStringFromCode(ArtMethod* referrer, dex::StringIndex string_idx) { +inline ObjPtr ResolveStringFromCode(ArtMethod* referrer, + dex::StringIndex string_idx) { Thread::PoisonObjectPointersIfDebug(); ObjPtr string = referrer->GetDexCache()->GetResolvedString(string_idx); if (UNLIKELY(string == nullptr)) { @@ -773,7 +775,7 @@ inline mirror::String* ResolveStringFromCode(ArtMethod* referrer, dex::StringInd ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); string = class_linker->ResolveString(dex_file, string_idx, dex_cache); } - return string.Ptr(); + return string; } inline void UnlockJniSynchronizedMethod(jobject locked, Thread* self) { diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index cda70ea265..830ef84250 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -143,15 +143,16 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_); -inline mirror::Class* ResolveVerifyAndClinit(dex::TypeIndex type_idx, - ArtMethod* referrer, - Thread* self, - bool can_run_clinit, - bool verify_access) +inline ObjPtr ResolveVerifyAndClinit(dex::TypeIndex type_idx, + ArtMethod* referrer, + Thread* self, + bool can_run_clinit, + bool verify_access) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); -inline mirror::String* ResolveStringFromCode(ArtMethod* referrer, dex::StringIndex string_idx) +inline ObjPtr ResolveStringFromCode(ArtMethod* referrer, + dex::StringIndex string_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 238ada94ff..98378382c5 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -138,15 +138,15 @@ extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx, auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod( self, CalleeSaveType::kSaveEverythingForClinit); ArtMethod* caller = caller_and_outer.caller; - mirror::Class* result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), - caller, - self, - /* can_run_clinit */ true, - /* verify_access */ false); + ObjPtr result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), + caller, + self, + /* can_run_clinit */ true, + /* verify_access */ false); if (LIKELY(result != nullptr) && CanReferenceBss(caller_and_outer.outer_method, caller)) { StoreTypeInBss(caller_and_outer.outer_method, dex::TypeIndex(type_idx), result); } - return result; + return result.Ptr(); } extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, Thread* self) @@ -156,15 +156,15 @@ extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, Thread* s auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod( self, CalleeSaveType::kSaveEverythingForClinit); ArtMethod* caller = caller_and_outer.caller; - mirror::Class* result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), - caller, - self, - /* can_run_clinit */ false, - /* verify_access */ false); + ObjPtr result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), + caller, + self, + /* can_run_clinit */ false, + /* verify_access */ false); if (LIKELY(result != nullptr) && CanReferenceBss(caller_and_outer.outer_method, caller)) { StoreTypeInBss(caller_and_outer.outer_method, dex::TypeIndex(type_idx), result); } - return result; + return result.Ptr(); } extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx, Thread* self) @@ -174,13 +174,13 @@ extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, CalleeSaveType::kSaveEverything); ArtMethod* caller = caller_and_outer.caller; - mirror::Class* result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), - caller, - self, - /* can_run_clinit */ false, - /* verify_access */ true); + ObjPtr result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), + caller, + self, + /* can_run_clinit */ false, + /* verify_access */ true); // Do not StoreTypeInBss(); access check entrypoint is never used together with .bss. - return result; + return result.Ptr(); } extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* self) @@ -189,11 +189,11 @@ extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, CalleeSaveType::kSaveEverything); ArtMethod* caller = caller_and_outer.caller; - mirror::String* result = ResolveStringFromCode(caller, dex::StringIndex(string_idx)); + ObjPtr result = ResolveStringFromCode(caller, dex::StringIndex(string_idx)); if (LIKELY(result != nullptr) && CanReferenceBss(caller_and_outer.outer_method, caller)) { StoreStringInBss(caller_and_outer.outer_method, dex::StringIndex(string_idx), result); } - return result; + return result.Ptr(); } } // namespace art diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 987298bd8a..9c7645af9e 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -376,15 +376,15 @@ extern "C" size_t MterpConstClass(uint32_t index, ShadowFrame* shadow_frame, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::Class* c = ResolveVerifyAndClinit(dex::TypeIndex(index), - shadow_frame->GetMethod(), - self, - false, - false); + ObjPtr c = ResolveVerifyAndClinit(dex::TypeIndex(index), + shadow_frame->GetMethod(), + self, + /* can_run_clinit */ false, + /* verify_access */ false); if (UNLIKELY(c == nullptr)) { return true; } - shadow_frame->SetVRegReference(tgt_vreg, c); + shadow_frame->SetVRegReference(tgt_vreg, c.Ptr()); return false; } @@ -463,17 +463,17 @@ extern "C" size_t MterpNewInstance(ShadowFrame* shadow_frame, Thread* self, uint REQUIRES_SHARED(Locks::mutator_lock_) { const Instruction* inst = Instruction::At(shadow_frame->GetDexPCPtr()); mirror::Object* obj = nullptr; - mirror::Class* c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()), - shadow_frame->GetMethod(), - self, - false, - false); + ObjPtr c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()), + shadow_frame->GetMethod(), + self, + /* can_run_clinit */ false, + /* verify_access */ false); if (LIKELY(c != nullptr)) { if (UNLIKELY(c->IsStringClass())) { gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); obj = mirror::String::AllocEmptyString(self, allocator_type); } else { - obj = AllocObjectFromCode(c, + obj = AllocObjectFromCode(c.Ptr(), self, Runtime::Current()->GetHeap()->GetCurrentAllocator()); } diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc index e52dd08540..f922dd317b 100644 --- a/runtime/transaction_test.cc +++ b/runtime/transaction_test.cc @@ -502,10 +502,11 @@ TEST_F(TransactionTest, ResolveString) { ASSERT_TRUE(h_klass->IsInitialized()); // Make sure the string got resolved by the transaction. { - mirror::String* s = class_linker_->LookupString(*dex_file, string_idx, h_dex_cache.Get()); + ObjPtr s = + class_linker_->LookupString(*dex_file, string_idx, h_dex_cache.Get()); ASSERT_TRUE(s != nullptr); EXPECT_STREQ(s->ToModifiedUtf8().c_str(), kResolvedString); - EXPECT_EQ(s, h_dex_cache->GetResolvedString(string_idx)); + EXPECT_EQ(s.Ptr(), 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 fefb4f6526..b6b152fd76 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -1074,9 +1074,9 @@ bool MethodVerifier::ScanTryCatchBlocks() { // Ensure exception types are resolved so that they don't need resolution to be delivered, // unresolved exception types will be ignored by exception delivery if (iterator.GetHandlerTypeIndex().IsValid()) { - mirror::Class* exception_type = linker->ResolveType(*dex_file_, - iterator.GetHandlerTypeIndex(), - dex_cache_, class_loader_); + ObjPtr exception_type = linker->ResolveType(*dex_file_, + iterator.GetHandlerTypeIndex(), + dex_cache_, class_loader_); if (exception_type == nullptr) { DCHECK(self_->IsExceptionPending()); self_->ClearException(); @@ -3639,8 +3639,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { has_catch_all_handler = true; } else { // It is also a catch-all if it is java.lang.Throwable. - mirror::Class* klass = linker->ResolveType(*dex_file_, handler_type_idx, dex_cache_, - class_loader_); + ObjPtr klass = + linker->ResolveType(*dex_file_, handler_type_idx, dex_cache_, class_loader_); if (klass != nullptr) { if (klass == mirror::Throwable::GetJavaLangThrowable()) { has_catch_all_handler = true; @@ -3758,16 +3758,16 @@ void MethodVerifier::UninstantiableError(const char* descriptor) { << "non-instantiable klass " << descriptor; } -inline bool MethodVerifier::IsInstantiableOrPrimitive(mirror::Class* klass) { +inline bool MethodVerifier::IsInstantiableOrPrimitive(ObjPtr klass) { return klass->IsInstantiable() || klass->IsPrimitive(); } template const RegType& MethodVerifier::ResolveClass(dex::TypeIndex class_idx) { - mirror::Class* klass = can_load_classes_ + ObjPtr klass = can_load_classes_ ? Runtime::Current()->GetClassLinker()->ResolveType( *dex_file_, class_idx, dex_cache_, class_loader_) - : ClassLinker::LookupResolvedType(class_idx, dex_cache_.Get(), class_loader_.Get()).Ptr(); + : ClassLinker::LookupResolvedType(class_idx, dex_cache_.Get(), class_loader_.Get()); if (can_load_classes_ && klass == nullptr) { DCHECK(self_->IsExceptionPending()); self_->ClearException(); @@ -3780,10 +3780,10 @@ const RegType& MethodVerifier::ResolveClass(dex::TypeIndex class_idx) { UninstantiableError(descriptor); precise = false; } - result = reg_types_.FindClass(klass, precise); + result = reg_types_.FindClass(klass.Ptr(), precise); if (result == nullptr) { const char* descriptor = dex_file_->StringByTypeIdx(class_idx); - result = reg_types_.InsertClass(descriptor, klass, precise); + result = reg_types_.InsertClass(descriptor, klass.Ptr(), precise); } } else { const char* descriptor = dex_file_->StringByTypeIdx(class_idx); @@ -3798,7 +3798,7 @@ const RegType& MethodVerifier::ResolveClass(dex::TypeIndex class_idx) { } // Record result of class resolution attempt. - VerifierDeps::MaybeRecordClassResolution(*dex_file_, class_idx, klass); + VerifierDeps::MaybeRecordClassResolution(*dex_file_, class_idx, klass.Ptr()); // 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 diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index c885914633..f26f3e2655 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -257,7 +257,8 @@ class MethodVerifier { REQUIRES_SHARED(Locks::mutator_lock_); void UninstantiableError(const char* descriptor); - static bool IsInstantiableOrPrimitive(mirror::Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); + static bool IsInstantiableOrPrimitive(ObjPtr klass) + REQUIRES_SHARED(Locks::mutator_lock_); // Is the method being verified a constructor? See the comment on the field. bool IsConstructor() const { diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java index 55873eabf0..3173afdfcd 100644 --- a/test/552-checker-sharpening/src/Main.java +++ b/test/552-checker-sharpening/src/Main.java @@ -44,24 +44,11 @@ public class Main { /// CHECK-START: int Main.testSimple(int) sharpening (before) /// CHECK: InvokeStaticOrDirect method_load_kind:RuntimeCall - /// CHECK-START-ARM: int Main.testSimple(int) sharpening (after) + /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: int Main.testSimple(int) sharpening (after) /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - /// CHECK-START-ARM64: int Main.testSimple(int) sharpening (after) - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - - /// CHECK-START-MIPS: int Main.testSimple(int) sharpening (after) - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - - /// CHECK-START-MIPS64: int Main.testSimple(int) sharpening (after) - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - - /// CHECK-START-X86: int Main.testSimple(int) sharpening (after) + /// CHECK-START-X86: int Main.testSimple(int) pc_relative_fixups_x86 (before) /// CHECK-NOT: X86ComputeBaseMethodAddress - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - - /// CHECK-START-X86_64: int Main.testSimple(int) sharpening (after) - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry /// CHECK-START-X86: int Main.testSimple(int) pc_relative_fixups_x86 (after) /// CHECK: X86ComputeBaseMethodAddress @@ -74,31 +61,14 @@ public class Main { /// CHECK-START: int Main.testDiamond(boolean, int) sharpening (before) /// CHECK: InvokeStaticOrDirect method_load_kind:RuntimeCall + /// CHECK: InvokeStaticOrDirect method_load_kind:RuntimeCall - /// CHECK-START-ARM: int Main.testDiamond(boolean, int) sharpening (after) - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - - /// CHECK-START-ARM64: int Main.testDiamond(boolean, int) sharpening (after) - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - - /// CHECK-START-MIPS: int Main.testDiamond(boolean, int) sharpening (after) - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - - /// CHECK-START-MIPS64: int Main.testDiamond(boolean, int) sharpening (after) + /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: int Main.testDiamond(boolean, int) sharpening (after) /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - /// CHECK-START-X86: int Main.testDiamond(boolean, int) sharpening (after) + /// CHECK-START-X86: int Main.testDiamond(boolean, int) pc_relative_fixups_x86 (before) /// CHECK-NOT: X86ComputeBaseMethodAddress - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - - /// CHECK-START-X86_64: int Main.testDiamond(boolean, int) sharpening (after) - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry /// CHECK-START-X86: int Main.testDiamond(boolean, int) pc_relative_fixups_x86 (after) /// CHECK: X86ComputeBaseMethodAddress @@ -169,30 +139,7 @@ public class Main { return x; } - /// CHECK-START: java.lang.String Main.$noinline$getBootImageString() sharpening (before) - /// CHECK: LoadString load_kind:RuntimeCall - - /// CHECK-START-X86: java.lang.String Main.$noinline$getBootImageString() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadString load_kind:{{BootImageAddress|BootImageInternTable}} - - /// CHECK-START-X86_64: java.lang.String Main.$noinline$getBootImageString() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadString load_kind:{{BootImageAddress|BootImageInternTable}} - - /// CHECK-START-ARM: java.lang.String Main.$noinline$getBootImageString() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadString load_kind:{{BootImageAddress|BootImageInternTable}} - - /// CHECK-START-ARM64: java.lang.String Main.$noinline$getBootImageString() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadString load_kind:{{BootImageAddress|BootImageInternTable}} - - /// CHECK-START-MIPS: java.lang.String Main.$noinline$getBootImageString() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadString load_kind:{{BootImageAddress|BootImageInternTable}} - - /// CHECK-START-MIPS64: java.lang.String Main.$noinline$getBootImageString() sharpening (after) + /// 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}} @@ -203,31 +150,16 @@ public class Main { return ""; } - /// CHECK-START: java.lang.String Main.$noinline$getNonBootImageString() sharpening (before) - /// CHECK: LoadString load_kind:RuntimeCall - - /// CHECK-START-X86: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after) + /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.String Main.$noinline$getNonBootImageString() builder (after) /// CHECK: LoadString load_kind:BssEntry + /// CHECK-START-X86: java.lang.String Main.$noinline$getNonBootImageString() pc_relative_fixups_x86 (before) + /// CHECK-NOT: X86ComputeBaseMethodAddress + /// CHECK-START-X86: java.lang.String Main.$noinline$getNonBootImageString() pc_relative_fixups_x86 (after) /// CHECK-DAG: X86ComputeBaseMethodAddress /// CHECK-DAG: LoadString load_kind:BssEntry - /// CHECK-START-X86_64: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after) - /// CHECK: LoadString load_kind:BssEntry - - /// CHECK-START-ARM: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after) - /// CHECK: LoadString load_kind:BssEntry - - /// CHECK-START-ARM64: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after) - /// CHECK: LoadString load_kind:BssEntry - - /// CHECK-START-MIPS: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after) - /// CHECK: LoadString load_kind:BssEntry - - /// CHECK-START-MIPS64: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after) - /// CHECK: LoadString load_kind:BssEntry - public static String $noinline$getNonBootImageString() { // Prevent inlining to avoid the string comparison being optimized away. if (doThrow) { throw new Error(); } @@ -235,27 +167,7 @@ public class Main { return "non-boot-image-string"; } - /// CHECK-START-X86: java.lang.Class Main.$noinline$getStringClass() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadClass load_kind:{{BootImageAddress|BootImageClassTable}} class_name:java.lang.String - - /// CHECK-START-X86_64: java.lang.Class Main.$noinline$getStringClass() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadClass load_kind:{{BootImageAddress|BootImageClassTable}} class_name:java.lang.String - - /// CHECK-START-ARM: java.lang.Class Main.$noinline$getStringClass() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadClass load_kind:{{BootImageAddress|BootImageClassTable}} class_name:java.lang.String - - /// CHECK-START-ARM64: java.lang.Class Main.$noinline$getStringClass() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadClass load_kind:{{BootImageAddress|BootImageClassTable}} class_name:java.lang.String - - /// CHECK-START-MIPS: java.lang.Class Main.$noinline$getStringClass() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadClass load_kind:{{BootImageAddress|BootImageClassTable}} class_name:java.lang.String - - /// CHECK-START-MIPS64: java.lang.Class Main.$noinline$getStringClass() sharpening (after) + /// 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 @@ -266,28 +178,16 @@ public class Main { return String.class; } - /// CHECK-START-X86: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) + /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.Class Main.$noinline$getOtherClass() builder (after) /// CHECK: LoadClass load_kind:BssEntry class_name:Other + /// CHECK-START-X86: java.lang.Class Main.$noinline$getOtherClass() pc_relative_fixups_x86 (before) + /// CHECK-NOT: X86ComputeBaseMethodAddress + /// CHECK-START-X86: java.lang.Class Main.$noinline$getOtherClass() pc_relative_fixups_x86 (after) /// CHECK-DAG: X86ComputeBaseMethodAddress /// CHECK-DAG: LoadClass load_kind:BssEntry class_name:Other - /// CHECK-START-X86_64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) - /// CHECK: LoadClass load_kind:BssEntry class_name:Other - - /// CHECK-START-ARM: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) - /// CHECK: LoadClass load_kind:BssEntry class_name:Other - - /// CHECK-START-ARM64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) - /// CHECK: LoadClass load_kind:BssEntry class_name:Other - - /// CHECK-START-MIPS: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) - /// CHECK: LoadClass load_kind:BssEntry class_name:Other - - /// CHECK-START-MIPS64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) - /// CHECK: LoadClass load_kind:BssEntry class_name:Other - public static Class $noinline$getOtherClass() { // Prevent inlining to avoid the string comparison being optimized away. if (doThrow) { throw new Error(); } -- GitLab From 208f67072283be64da231b51f9c195aff403dceb Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 8 Dec 2017 12:00:50 +0000 Subject: [PATCH 146/226] Change ArtField::ProxyFindSystemClass() to lookup the class. As the function is called from ArtField::LookupType(), we should avoid calls that appear to allow type resolution rather than plain lookup. The lookup should always succeed. Also rename ArtField::LookupType() to LookupResolvedType() to align with naming used in ClassLinker and ArtMethod. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Change-Id: I0a87347b5341575e47e0fdba6d58ade2543387c8 --- compiler/optimizing/reference_type_propagation.cc | 2 +- oatdump/oatdump.cc | 2 +- runtime/art_field-inl.h | 2 +- runtime/art_field.cc | 5 ++++- runtime/art_field.h | 2 +- runtime/class_linker.cc | 4 ++-- runtime/mirror/object.cc | 4 ++-- runtime/verifier/method_verifier.cc | 4 ++-- 8 files changed, 14 insertions(+), 11 deletions(-) diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 7246129e25..d84f14acc0 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -612,7 +612,7 @@ void ReferenceTypePropagation::RTPVisitor::UpdateFieldAccessTypeInfo(HInstructio // The field is unknown only during tests. if (info.GetField() != nullptr) { - klass = info.GetField()->LookupType(); + klass = info.GetField()->LookupResolvedType(); } SetClassAsTypeInfo(instr, klass, /* is_exact */ false); diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 2c15087612..1b4485b233 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -2269,7 +2269,7 @@ class ImageDumper { os << StringPrintf("null %s\n", PrettyDescriptor(field->GetTypeDescriptor()).c_str()); } else { // Grab the field type without causing resolution. - ObjPtr field_type = field->LookupType(); + ObjPtr field_type = field->LookupResolvedType(); if (field_type != nullptr) { PrettyObjectValue(os, field_type, value); } else { diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h index 4a328e8d60..ae81ee8470 100644 --- a/runtime/art_field-inl.h +++ b/runtime/art_field-inl.h @@ -299,7 +299,7 @@ inline bool ArtField::IsPrimitiveType() REQUIRES_SHARED(Locks::mutator_lock_) { return GetTypeAsPrimitiveType() != Primitive::kPrimNot; } -inline ObjPtr ArtField::LookupType() { +inline ObjPtr ArtField::LookupResolvedType() { ScopedAssertNoThreadSuspension ants(__FUNCTION__); const uint32_t field_index = GetDexFieldIndex(); ObjPtr declaring_class = GetDeclaringClass(); diff --git a/runtime/art_field.cc b/runtime/art_field.cc index bc728f4476..54746a3685 100644 --- a/runtime/art_field.cc +++ b/runtime/art_field.cc @@ -45,7 +45,10 @@ void ArtField::SetOffset(MemberOffset num_bytes) { ObjPtr ArtField::ProxyFindSystemClass(const char* descriptor) { DCHECK(GetDeclaringClass()->IsProxyClass()); - return Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), descriptor); + ObjPtr klass = Runtime::Current()->GetClassLinker()->LookupClass( + Thread::Current(), descriptor, /* class_loader */ nullptr); + DCHECK(klass != nullptr); + return klass; } ObjPtr ArtField::ResolveGetStringName(Thread* self, diff --git a/runtime/art_field.h b/runtime/art_field.h index 866bf0bc70..a6f050810f 100644 --- a/runtime/art_field.h +++ b/runtime/art_field.h @@ -205,7 +205,7 @@ class ArtField FINAL { bool IsPrimitiveType() REQUIRES_SHARED(Locks::mutator_lock_); - ObjPtr LookupType() REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr LookupResolvedType() REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr ResolveType() REQUIRES_SHARED(Locks::mutator_lock_); size_t FieldSize() REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index e5bb7862cb..71b6378c81 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -8659,10 +8659,10 @@ jobject ClassLinker::CreateWellKnownClassLoader(Thread* self, DCHECK_EQ(h_dex_element_class.Get(), element_file_field->GetDeclaringClass()); ArtField* cookie_field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie); - DCHECK_EQ(cookie_field->GetDeclaringClass(), element_file_field->LookupType()); + DCHECK_EQ(cookie_field->GetDeclaringClass(), element_file_field->LookupResolvedType()); ArtField* file_name_field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_fileName); - DCHECK_EQ(file_name_field->GetDeclaringClass(), element_file_field->LookupType()); + DCHECK_EQ(file_name_field->GetDeclaringClass(), element_file_field->LookupResolvedType()); // Fill the elements array. int32_t index = 0; diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index 87cc620309..97fb793530 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -240,7 +240,7 @@ void Object::CheckFieldAssignmentImpl(MemberOffset field_offset, ObjPtr CHECK_NE(field.GetTypeAsPrimitiveType(), Primitive::kPrimNot); // TODO: resolve the field type for moving GC. ObjPtr field_type = - kMovingCollector ? field.LookupType() : field.ResolveType(); + kMovingCollector ? field.LookupResolvedType() : field.ResolveType(); if (field_type != nullptr) { CHECK(field_type->IsAssignableFrom(new_value->GetClass())); } @@ -258,7 +258,7 @@ void Object::CheckFieldAssignmentImpl(MemberOffset field_offset, ObjPtr CHECK_NE(field.GetTypeAsPrimitiveType(), Primitive::kPrimNot); // TODO: resolve the field type for moving GC. ObjPtr field_type = - kMovingCollector ? field.LookupType() : field.ResolveType(); + kMovingCollector ? field.LookupResolvedType() : field.ResolveType(); if (field_type != nullptr) { CHECK(field_type->IsAssignableFrom(new_value->GetClass())); } diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index fefb4f6526..25982cffda 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -5046,7 +5046,7 @@ void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType& } ObjPtr field_type_class = - can_load_classes_ ? field->ResolveType() : field->LookupType(); + can_load_classes_ ? field->ResolveType() : field->LookupResolvedType(); if (field_type_class != nullptr) { field_type = &FromClass(field->GetTypeDescriptor(), field_type_class.Ptr(), @@ -5195,7 +5195,7 @@ void MethodVerifier::VerifyQuickFieldAccess(const Instruction* inst, const RegTy const RegType* field_type; { ObjPtr field_type_class = - can_load_classes_ ? field->ResolveType() : field->LookupType(); + can_load_classes_ ? field->ResolveType() : field->LookupResolvedType(); if (field_type_class != nullptr) { field_type = &FromClass(field->GetTypeDescriptor(), -- GitLab From 4a10c759d3dfe786e13bc1598ce683b38cf86340 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Thu, 7 Dec 2017 09:33:36 +0000 Subject: [PATCH 147/226] jfuzz: Add try..catch..finally blocks Adds try..catch..finally blocks to generated code. Also adds invocations of a nop() function. Bug: 69128828 Test: Manually generating, dx'ing, and verifying outputs Change-Id: I5e54d63e694acf8106c6deb966a584b9253001b6 --- tools/jfuzz/README.md | 2 + tools/jfuzz/jfuzz.cc | 168 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 141 insertions(+), 29 deletions(-) diff --git a/tools/jfuzz/README.md b/tools/jfuzz/README.md index 10d175b83d..4edfe1b35b 100644 --- a/tools/jfuzz/README.md +++ b/tools/jfuzz/README.md @@ -28,6 +28,8 @@ where (higher values yield deeper nested conditionals) -n : defines a fuzzing nest for for/while/do-while loops (higher values yield deeper nested loops) + -t : defines a fuzzing nest for try-catch-finally blocks + (higher values yield deeper nested try-catch-finally blocks) -v : prints version number and exits -h : prints help and exits diff --git a/tools/jfuzz/jfuzz.cc b/tools/jfuzz/jfuzz.cc index 7990c6cf8e..825648b1ed 100644 --- a/tools/jfuzz/jfuzz.cc +++ b/tools/jfuzz/jfuzz.cc @@ -30,9 +30,6 @@ namespace { /* * Operators. */ - -#define EMIT(x) fputs((x)[random0(sizeof(x)/sizeof(const char*))], out_); - static constexpr const char* kIncDecOps[] = { "++", "--" }; static constexpr const char* kIntUnaryOps[] = { "+", "-", "~" }; static constexpr const char* kFpUnaryOps[] = { "+", "-" }; @@ -50,12 +47,22 @@ static constexpr const char* kFpAssignOps[] = { "=", "+=", "-=", "*=", "/=" }; static constexpr const char* kBoolRelOps[] = { "==", "!=" }; static constexpr const char* kRelOps[] = { "==", "!=", ">", ">=", "<", "<=" }; +/* + * Exceptions. + */ +static const char* kExceptionTypes[] = { + "IllegalStateException", + "NullPointerException", + "IllegalArgumentException", + "ArrayIndexOutOfBoundsException" +}; + /* * Version of JFuzz. Increase this each time changes are made to the program * to preserve the property that a given version of JFuzz yields the same * fuzzed program for a deterministic random seed. */ -const char* VERSION = "1.4"; +const char* VERSION = "1.5"; /* * Maximum number of array dimensions, together with corresponding maximum size @@ -64,6 +71,14 @@ const char* VERSION = "1.4"; static const uint32_t kMaxDim = 10; static const uint32_t kMaxDimSize[kMaxDim + 1] = { 0, 1000, 32, 10, 6, 4, 3, 3, 2, 2, 2 }; +/* + * Utility function to return the number of elements in an array. + */ +template +constexpr uint32_t countof(T const (&)[N]) { + return N; +} + /** * A class that generates a random program that compiles correctly. The program * is generated using rules that generate various programming constructs. Each rule @@ -78,7 +93,8 @@ class JFuzz { uint32_t expr_depth, uint32_t stmt_length, uint32_t if_nest, - uint32_t loop_nest) + uint32_t loop_nest, + uint32_t try_nest) : out_(out), fuzz_random_engine_(seed), fuzz_seed_(seed), @@ -86,6 +102,7 @@ class JFuzz { fuzz_stmt_length_(stmt_length), fuzz_if_nest_(if_nest), fuzz_loop_nest_(loop_nest), + fuzz_try_nest_(try_nest), return_type_(randomType()), array_type_(randomType()), array_dim_(random1(kMaxDim)), @@ -97,6 +114,7 @@ class JFuzz { loop_nest_(0), switch_nest_(0), do_nest_(0), + try_nest_(0), boolean_local_(0), int_local_(0), long_local_(0), @@ -168,6 +186,12 @@ class JFuzz { } } + // Emits a random strong selected from an array of operator strings. + template + inline void emitOneOf(const char* const (&ops)[N]) { + fputs(ops[random0(N)], out_); + } + // // Expressions. // @@ -177,9 +201,9 @@ class JFuzz { if (tp == kBoolean) { fputc('!', out_); } else if (isInteger(tp)) { - EMIT(kIntUnaryOps); + emitOneOf(kIntUnaryOps); } else { // isFP(tp) - EMIT(kFpUnaryOps); + emitOneOf(kFpUnaryOps); } } @@ -188,38 +212,38 @@ class JFuzz { if (tp == kBoolean) { // Not applicable, just leave "as is". } else { // isInteger(tp) || isFP(tp) - EMIT(kIncDecOps); + emitOneOf(kIncDecOps); } } // Emit a binary operator (same type in-out). void emitBinaryOp(Type tp) { if (tp == kBoolean) { - EMIT(kBoolBinOps); + emitOneOf(kBoolBinOps); } else if (isInteger(tp)) { - EMIT(kIntBinOps); + emitOneOf(kIntBinOps); } else { // isFP(tp) - EMIT(kFpBinOps); + emitOneOf(kFpBinOps); } } // Emit an assignment operator (same type in-out). void emitAssignmentOp(Type tp) { if (tp == kBoolean) { - EMIT(kBoolAssignOps); + emitOneOf(kBoolAssignOps); } else if (isInteger(tp)) { - EMIT(kIntAssignOps); + emitOneOf(kIntAssignOps); } else { // isFP(tp) - EMIT(kFpAssignOps); + emitOneOf(kFpAssignOps); } } // Emit a relational operator (one type in, boolean out). void emitRelationalOp(Type tp) { if (tp == kBoolean) { - EMIT(kBoolRelOps); + emitOneOf(kBoolRelOps); } else { // isInteger(tp) || isFP(tp) - EMIT(kRelOps); + emitOneOf(kRelOps); } } @@ -808,7 +832,7 @@ class JFuzz { fputs("{\n", out_); indentation_ += 2; emitIndentation(); - fprintf(out_, "int i%u = %d;", loop_nest_, isWhile ? -1 : 0); + fprintf(out_, "int i%u = %d;\n", loop_nest_, isWhile ? -1 : 0); emitIndentation(); if (isWhile) { fprintf(out_, "while (++i%u < ", loop_nest_); @@ -871,6 +895,73 @@ class JFuzz { return mayFollowTrue || mayFollowFalse; } + bool emitTry() { + fputs("try {\n", out_); + indentation_ += 2; + bool mayFollow = emitStatementList(); + indentation_ -= 2; + emitIndentation(); + fputc('}', out_); + return mayFollow; + } + + bool emitCatch() { + uint32_t count = random1(countof(kExceptionTypes)); + bool mayFollow = false; + for (uint32_t i = 0; i < count; ++i) { + fprintf(out_, " catch (%s ex%u_%u) {\n", kExceptionTypes[i], try_nest_, i); + indentation_ += 2; + mayFollow |= emitStatementList(); + indentation_ -= 2; + emitIndentation(); + fputc('}', out_); + } + return mayFollow; + } + + bool emitFinally() { + fputs(" finally {\n", out_); + indentation_ += 2; + bool mayFollow = emitStatementList(); + indentation_ -= 2; + emitIndentation(); + fputc('}', out_); + return mayFollow; + } + + // Emit a try-catch-finally block. + bool emitTryCatchFinally() { + // Apply a hard limit on the number of catch blocks. This is for + // javac which fails if blocks within try-catch-finally are too + // large (much less than you'd expect). + if (try_nest_ > fuzz_try_nest_) { + return emitAssignment(); // fall back + } + + ++try_nest_; // Entering try-catch-finally + + bool mayFollow = emitTry(); + switch (random0(3)) { + case 0: // try..catch + mayFollow |= emitCatch(); + break; + case 1: // try..finally + mayFollow &= emitFinally(); + break; + case 2: // try..catch..finally + // When determining whether code may follow, we observe that a + // finally block always follows after try and catch + // block. Code may only follow if the finally block permits + // and either the try or catch block allows code to follow. + mayFollow = (mayFollow | emitCatch()) & emitFinally(); + break; + } + fputc('\n', out_); + + --try_nest_; // Leaving try-catch-finally + return mayFollow; + } + // Emit a switch statement. bool emitSwitch() { // Continuing if nest becomes less likely as the depth grows. @@ -915,6 +1006,11 @@ class JFuzz { return mayFollow; } + bool emitNopCall() { + fputs("nop();\n", out_); + return true; + } + // Emit an assignment statement. bool emitAssignment() { Type tp = randomType(); @@ -930,16 +1026,18 @@ class JFuzz { // Emit a single statement. Returns true if statements may follow. bool emitStatement() { switch (random1(16)) { // favor assignments - case 1: return emitReturn(false); break; - case 2: return emitContinue(); break; - case 3: return emitBreak(); break; - case 4: return emitScope(); break; - case 5: return emitArrayInit(); break; - case 6: return emitForLoop(); break; - case 7: return emitDoLoop(); break; - case 8: return emitIfStmt(); break; - case 9: return emitSwitch(); break; - default: return emitAssignment(); break; + case 1: return emitReturn(false); break; + case 2: return emitContinue(); break; + case 3: return emitBreak(); break; + case 4: return emitScope(); break; + case 5: return emitArrayInit(); break; + case 6: return emitForLoop(); break; + case 7: return emitDoLoop(); break; + case 8: return emitIfStmt(); break; + case 9: return emitSwitch(); break; + case 10: return emitTryCatchFinally(); break; + case 11: return emitNopCall(); break; + default: return emitAssignment(); break; } } @@ -1109,6 +1207,11 @@ class JFuzz { fputs(" }\n", out_); } + // Emit a static void method. + void emitStaticNopMethod() { + fputs(" public static void nop() {}\n", out_); + } + // Emit program header. Emit command line options in the comments. void emitHeader() { fputs("\n/**\n * AOSP JFuzz Tester.\n", out_); @@ -1133,6 +1236,7 @@ class JFuzz { emitArrayDecl(); emitTestConstructor(); emitTestMethod(); + emitStaticNopMethod(); emitMainMethod(); indentation_ -= 2; fputs("}\n\n", out_); @@ -1167,6 +1271,7 @@ class JFuzz { const uint32_t fuzz_stmt_length_; const uint32_t fuzz_if_nest_; const uint32_t fuzz_loop_nest_; + const uint32_t fuzz_try_nest_; // Return and array setup. const Type return_type_; @@ -1182,6 +1287,7 @@ class JFuzz { uint32_t loop_nest_; uint32_t switch_nest_; uint32_t do_nest_; + uint32_t try_nest_; uint32_t boolean_local_; uint32_t int_local_; uint32_t long_local_; @@ -1203,6 +1309,7 @@ int32_t main(int32_t argc, char** argv) { uint32_t stmt_length = 8; uint32_t if_nest = 2; uint32_t loop_nest = 3; + uint32_t try_nest = 2; // Parse options. while (1) { @@ -1226,6 +1333,9 @@ int32_t main(int32_t argc, char** argv) { case 'n': loop_nest = strtoul(optarg, nullptr, 0); break; + case 't': + try_nest = strtoul(optarg, nullptr, 0); + break; case 'v': fprintf(stderr, "jfuzz version %s\n", VERSION); return 0; @@ -1234,7 +1344,7 @@ int32_t main(int32_t argc, char** argv) { fprintf(stderr, "usage: %s [-s seed] " "[-d expr-depth] [-l stmt-length] " - "[-i if-nest] [-n loop-nest] [-v] [-h]\n", + "[-i if-nest] [-n loop-nest] [-t try-nest] [-v] [-h]\n", argv[0]); return 1; } @@ -1244,7 +1354,7 @@ int32_t main(int32_t argc, char** argv) { srand(seed); // Generate fuzzed program. - JFuzz fuzz(stdout, seed, expr_depth, stmt_length, if_nest, loop_nest); + JFuzz fuzz(stdout, seed, expr_depth, stmt_length, if_nest, loop_nest, try_nest); fuzz.emitProgram(); return 0; } -- GitLab From e11dd50ac2b5ccbf3b02213b7361f55b1f1a90da Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 8 Dec 2017 14:09:45 +0000 Subject: [PATCH 148/226] Do not pass DexFile to ClassLinker::ResolveField*(). The DexFile can be easily retrieved from the DexCache, so reduce the number of arguments that need to be passed. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Change-Id: I0579db64c63afea789c7c9ad8db81e37c9248e97 --- compiler/driver/compiler_driver-inl.h | 21 +++++---------- compiler/driver/compiler_driver.cc | 30 +++++++++++----------- compiler/driver/compiler_driver.h | 22 ++++++---------- compiler/optimizing/instruction_builder.cc | 3 +-- runtime/class_linker-inl.h | 3 +-- runtime/class_linker.cc | 17 ++++++------ runtime/class_linker.h | 23 ++++++++--------- runtime/dex_file_annotations.cc | 9 +++---- runtime/dex_file_annotations.h | 17 ++++++------ runtime/entrypoints/entrypoint_utils-inl.h | 3 +-- runtime/verifier/method_verifier.cc | 4 +-- 11 files changed, 64 insertions(+), 88 deletions(-) diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index 7e118d5d85..42fc4aa5ba 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -58,13 +58,13 @@ inline ObjPtr CompilerDriver::ResolveCompilingMethodsClass( return ResolveClass(soa, dex_cache, class_loader, referrer_method_id.class_idx_, mUnit); } -inline ArtField* CompilerDriver::ResolveFieldWithDexFile( - const ScopedObjectAccess& soa, Handle dex_cache, - Handle class_loader, const DexFile* dex_file, - uint32_t field_idx, bool is_static) { - DCHECK_EQ(dex_cache->GetDexFile(), dex_file); +inline ArtField* CompilerDriver::ResolveField(const ScopedObjectAccess& soa, + Handle dex_cache, + Handle class_loader, + uint32_t field_idx, + bool is_static) { ArtField* resolved_field = Runtime::Current()->GetClassLinker()->ResolveField( - *dex_file, field_idx, dex_cache, class_loader, is_static); + field_idx, dex_cache, class_loader, is_static); DCHECK_EQ(resolved_field == nullptr, soa.Self()->IsExceptionPending()); if (UNLIKELY(resolved_field == nullptr)) { // Clean up any exception left by type resolution. @@ -79,15 +79,6 @@ inline ArtField* CompilerDriver::ResolveFieldWithDexFile( return resolved_field; } -inline ArtField* CompilerDriver::ResolveField( - const ScopedObjectAccess& soa, Handle dex_cache, - Handle class_loader, const DexCompilationUnit* mUnit, - uint32_t field_idx, bool is_static) { - DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); - return ResolveFieldWithDexFile(soa, dex_cache, class_loader, mUnit->GetDexFile(), field_idx, - is_static); -} - inline std::pair CompilerDriver::IsFastInstanceField( ObjPtr dex_cache, ObjPtr referrer_class, diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index f49d119900..2d43476d5a 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1368,17 +1368,18 @@ void CompilerDriver::ProcessedStaticField(bool resolved, bool local) { } ArtField* CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, - const DexCompilationUnit* mUnit, bool is_put, + const DexCompilationUnit* mUnit, + bool is_put, const ScopedObjectAccess& soa) { // Try to resolve the field and compiling method's class. ArtField* resolved_field; ObjPtr referrer_class; Handle dex_cache(mUnit->GetDexCache()); { - Handle class_loader_handle = mUnit->GetClassLoader(); - resolved_field = ResolveField(soa, dex_cache, class_loader_handle, mUnit, field_idx, false); + Handle class_loader = mUnit->GetClassLoader(); + resolved_field = ResolveField(soa, dex_cache, class_loader, field_idx, /* is_static */ false); referrer_class = resolved_field != nullptr - ? ResolveCompilingMethodsClass(soa, dex_cache, class_loader_handle, mUnit) : nullptr; + ? ResolveCompilingMethodsClass(soa, dex_cache, class_loader, mUnit) : nullptr; } bool can_link = false; if (resolved_field != nullptr && referrer_class != nullptr) { @@ -1664,8 +1665,8 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { ClassDataItemIterator it(dex_file, class_data); while (it.HasNextStaticField()) { if (resolve_fields_and_methods) { - ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), - dex_cache, class_loader, true); + ArtField* field = class_linker->ResolveField( + it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ true); if (field == nullptr) { CheckAndClearResolveException(soa.Self()); } @@ -1679,8 +1680,8 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { requires_constructor_barrier = true; } if (resolve_fields_and_methods) { - ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), - dex_cache, class_loader, false); + ArtField* field = class_linker->ResolveField( + it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ false); if (field == nullptr) { CheckAndClearResolveException(soa.Self()); } @@ -2329,22 +2330,21 @@ class InitializeClassVisitor : public CompilationVisitor { DCHECK(!klass->IsInitialized()); StackHandleScope<1> hs(Thread::Current()); - Handle h_dex_cache = hs.NewHandle(klass->GetDexCache()); + Handle dex_cache = hs.NewHandle(klass->GetDexCache()); const DexFile* dex_file = manager_->GetDexFile(); const DexFile::ClassDef* class_def = klass->GetClassDef(); ClassLinker* class_linker = manager_->GetClassLinker(); // Check encoded final field values for strings and intern. - annotations::RuntimeEncodedStaticFieldValueIterator value_it(*dex_file, - &h_dex_cache, - &class_loader, + annotations::RuntimeEncodedStaticFieldValueIterator value_it(dex_cache, + class_loader, manager_->GetClassLinker(), *class_def); for ( ; value_it.HasNext(); value_it.Next()) { if (value_it.GetValueType() == annotations::RuntimeEncodedStaticFieldValueIterator::kString) { // Resolve the string. This will intern the string. art::ObjPtr resolved = class_linker->ResolveString( - *dex_file, dex::StringIndex(value_it.GetJavaValue().i), h_dex_cache); + *dex_file, dex::StringIndex(value_it.GetJavaValue().i), dex_cache); CHECK(resolved != nullptr); } } @@ -2357,11 +2357,11 @@ class InitializeClassVisitor : public CompilationVisitor { for (const DexInstructionPcPair& inst : code_item->Instructions()) { if (inst->Opcode() == Instruction::CONST_STRING) { ObjPtr s = class_linker->ResolveString( - *dex_file, dex::StringIndex(inst->VRegB_21c()), h_dex_cache); + *dex_file, dex::StringIndex(inst->VRegB_21c()), dex_cache); CHECK(s != nullptr); } else if (inst->Opcode() == Instruction::CONST_STRING_JUMBO) { ObjPtr s = class_linker->ResolveString( - *dex_file, dex::StringIndex(inst->VRegB_31c()), h_dex_cache); + *dex_file, dex::StringIndex(inst->VRegB_31c()), dex_cache); CHECK(s != nullptr); } } diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index ce7ec4520d..ab788e326f 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -232,17 +232,11 @@ class CompilerDriver { // Resolve a field. Returns null on failure, including incompatible class change. // NOTE: Unlike ClassLinker's ResolveField(), this method enforces is_static. - ArtField* ResolveField( - const ScopedObjectAccess& soa, Handle dex_cache, - Handle class_loader, const DexCompilationUnit* mUnit, - uint32_t field_idx, bool is_static) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Resolve a field with a given dex file. - ArtField* ResolveFieldWithDexFile( - const ScopedObjectAccess& soa, Handle dex_cache, - Handle class_loader, const DexFile* dex_file, - uint32_t field_idx, bool is_static) + ArtField* ResolveField(const ScopedObjectAccess& soa, + Handle dex_cache, + Handle class_loader, + uint32_t field_idx, + bool is_static) REQUIRES_SHARED(Locks::mutator_lock_); // Can we fast-path an IGET/IPUT access to an instance field? If yes, compute the field offset. @@ -271,9 +265,9 @@ class CompilerDriver { REQUIRES(!Locks::mutator_lock_); ArtField* ComputeInstanceFieldInfo(uint32_t field_idx, - const DexCompilationUnit* mUnit, - bool is_put, - const ScopedObjectAccess& soa) + const DexCompilationUnit* mUnit, + bool is_put, + const ScopedObjectAccess& soa) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 4485f064c6..bce4de32d5 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1484,8 +1484,7 @@ ArtField* HInstructionBuilder::ResolveField(uint16_t field_idx, bool is_static, Handle class_loader = dex_compilation_unit_->GetClassLoader(); Handle compiling_class(hs.NewHandle(GetCompilingClass())); - ArtField* resolved_field = class_linker->ResolveField(*dex_compilation_unit_->GetDexFile(), - field_idx, + ArtField* resolved_field = class_linker->ResolveField(field_idx, dex_compilation_unit_->GetDexCache(), class_loader, is_static); diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index 648b455e50..2d8d4b41a0 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -299,8 +299,7 @@ inline ArtField* ClassLinker::ResolveField(uint32_t field_idx, StackHandleScope<2> hs(Thread::Current()); Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); Handle class_loader(hs.NewHandle(declaring_class->GetClassLoader())); - const DexFile& dex_file = *dex_cache->GetDexFile(); - resolved_field = ResolveField(dex_file, field_idx, dex_cache, class_loader, is_static); + resolved_field = ResolveField(field_idx, dex_cache, class_loader, is_static); // Note: We cannot check here to see whether we added the field to the cache. The type // might be an erroneous class, which results in it being hidden from us. } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 81aa371fcb..095cb30a04 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -4818,7 +4818,6 @@ bool ClassLinker::InitializeClass(Thread* self, Handle klass, if (num_static_fields > 0) { const DexFile::ClassDef* dex_class_def = klass->GetClassDef(); CHECK(dex_class_def != nullptr); - const DexFile& dex_file = klass->GetDexFile(); StackHandleScope<3> hs(self); Handle class_loader(hs.NewHandle(klass->GetClassLoader())); Handle dex_cache(hs.NewHandle(klass->GetDexCache())); @@ -4836,11 +4835,11 @@ bool ClassLinker::InitializeClass(Thread* self, Handle klass, } } - annotations::RuntimeEncodedStaticFieldValueIterator value_it(dex_file, - &dex_cache, - &class_loader, + annotations::RuntimeEncodedStaticFieldValueIterator value_it(dex_cache, + class_loader, 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()) { @@ -4848,7 +4847,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle klass, CHECK(can_init_statics); for ( ; value_it.HasNext(); value_it.Next(), field_it.Next()) { ArtField* field = ResolveField( - dex_file, field_it.GetMemberIndex(), dex_cache, class_loader, true); + field_it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ true); if (Runtime::Current()->IsActiveTransaction()) { value_it.ReadValueToField(field); } else { @@ -8114,8 +8113,7 @@ ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx, return resolved_field; } -ArtField* ClassLinker::ResolveField(const DexFile& dex_file, - uint32_t field_idx, +ArtField* ClassLinker::ResolveField(uint32_t field_idx, Handle dex_cache, Handle class_loader, bool is_static) { @@ -8125,6 +8123,7 @@ ArtField* ClassLinker::ResolveField(const DexFile& dex_file, if (resolved != nullptr) { return resolved; } + 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(dex_file, field_id.class_idx_, dex_cache, class_loader); @@ -8156,8 +8155,7 @@ ArtField* ClassLinker::ResolveField(const DexFile& dex_file, return resolved; } -ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, - uint32_t field_idx, +ArtField* ClassLinker::ResolveFieldJLS(uint32_t field_idx, Handle dex_cache, Handle class_loader) { DCHECK(dex_cache != nullptr); @@ -8166,6 +8164,7 @@ ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, if (resolved != nullptr) { return resolved; } + const DexFile& dex_file = *dex_cache->GetDexFile(); const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); Thread* self = Thread::Current(); ObjPtr klass(ResolveType(dex_file, field_id.class_idx_, dex_cache, class_loader)); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 16255f4542..6a8c7c1d05 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -345,23 +345,22 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - // Resolve a field with a given ID from the DexFile, storing the - // result in DexCache. The ClassLinker and ClassLoader are used as - // in ResolveType. What is unique is the is_static argument which is - // used to determine if we are resolving a static or non-static - // field. - ArtField* ResolveField(const DexFile& dex_file, uint32_t field_idx, + // Resolve a field 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 is_static argument which is used + // to determine if we are resolving a static or non-static field. + ArtField* ResolveField(uint32_t field_idx, Handle dex_cache, - Handle class_loader, bool is_static) + Handle class_loader, + bool is_static) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - // Resolve a field with a given ID from the DexFile, storing the - // result in DexCache. The ClassLinker and ClassLoader are used as - // in ResolveType. No is_static argument is provided so that Java + // Resolve a field 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. No is_static argument is provided so that Java // field resolution semantics are followed. - ArtField* ResolveFieldJLS(const DexFile& dex_file, - uint32_t field_idx, + ArtField* ResolveFieldJLS(uint32_t field_idx, Handle dex_cache, Handle class_loader) REQUIRES_SHARED(Locks::mutator_lock_) diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index 27b9202d25..437347403b 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -540,7 +540,6 @@ bool ProcessAnnotationValue(const ClassData& klass, } else { StackHandleScope<2> hs(self); ArtField* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS( - klass.GetDexFile(), index, hs.NewHandle(klass.GetDexCache()), hs.NewHandle(klass.GetClassLoader())); @@ -569,7 +568,6 @@ bool ProcessAnnotationValue(const ClassData& klass, } else { StackHandleScope<3> hs(self); ArtField* enum_field = Runtime::Current()->GetClassLinker()->ResolveField( - klass.GetDexFile(), index, hs.NewHandle(klass.GetDexCache()), hs.NewHandle(klass.GetClassLoader()), @@ -1580,7 +1578,6 @@ int32_t GetLineNumFromPC(const DexFile* dex_file, ArtMethod* method, uint32_t re template void RuntimeEncodedStaticFieldValueIterator::ReadValueToField(ArtField* field) const { DCHECK(dex_cache_ != nullptr); - DCHECK(class_loader_ != nullptr); switch (type_) { case kBoolean: field->SetBoolean(field->GetDeclaringClass(), jval_.z); break; @@ -1595,15 +1592,15 @@ void RuntimeEncodedStaticFieldValueIterator::ReadValueToField(ArtField* field) c case kString: { ObjPtr resolved = linker_->ResolveString(dex_file_, dex::StringIndex(jval_.i), - *dex_cache_); + dex_cache_); field->SetObject(field->GetDeclaringClass(), resolved); break; } case kType: { ObjPtr resolved = linker_->ResolveType(dex_file_, dex::TypeIndex(jval_.i), - *dex_cache_, - *class_loader_); + dex_cache_, + class_loader_); field->SetObject(field->GetDeclaringClass(), resolved); break; } diff --git a/runtime/dex_file_annotations.h b/runtime/dex_file_annotations.h index a934a4f99c..9ff0929176 100644 --- a/runtime/dex_file_annotations.h +++ b/runtime/dex_file_annotations.h @@ -19,18 +19,18 @@ #include "dex_file.h" +#include "handle.h" +#include "mirror/dex_cache.h" #include "mirror/object_array.h" namespace art { namespace mirror { class ClassLoader; -class DexCache; } // namespace mirror class ArtField; class ArtMethod; class ClassLinker; -template class MutableHandle; namespace annotations { @@ -116,13 +116,12 @@ int32_t GetLineNumFromPC(const DexFile* dex_file, ArtMethod* method, uint32_t re class RuntimeEncodedStaticFieldValueIterator : public EncodedStaticFieldValueIterator { public: // A constructor meant to be called from runtime code. - RuntimeEncodedStaticFieldValueIterator(const DexFile& dex_file, - Handle* dex_cache, - Handle* class_loader, + RuntimeEncodedStaticFieldValueIterator(Handle dex_cache, + Handle class_loader, ClassLinker* linker, const DexFile::ClassDef& class_def) REQUIRES_SHARED(Locks::mutator_lock_) - : EncodedStaticFieldValueIterator(dex_file, class_def), + : EncodedStaticFieldValueIterator(*dex_cache->GetDexFile(), class_def), dex_cache_(dex_cache), class_loader_(class_loader), linker_(linker) { @@ -132,9 +131,9 @@ class RuntimeEncodedStaticFieldValueIterator : public EncodedStaticFieldValueIte void ReadValueToField(ArtField* field) const REQUIRES_SHARED(Locks::mutator_lock_); private: - Handle* const dex_cache_; // Dex cache to resolve literal objects. - Handle* const class_loader_; // ClassLoader to resolve types. - ClassLinker* linker_; // Linker to resolve literal objects. + const Handle dex_cache_; // Dex cache to resolve literal objects. + const Handle class_loader_; // ClassLoader to resolve types. + ClassLinker* const linker_; // Linker to resolve literal objects. DISALLOW_IMPLICIT_CONSTRUCTORS(RuntimeEncodedStaticFieldValueIterator); }; diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 475c1e7e90..df17b30580 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -349,8 +349,7 @@ inline ArtField* FindFieldFromCode(uint32_t field_idx, Handle h_dex_cache(hs.NewHandle(method->GetDexCache())); Handle h_class_loader(hs.NewHandle(method->GetClassLoader())); - resolved_field = class_linker->ResolveFieldJLS(*method->GetDexFile(), - field_idx, + resolved_field = class_linker->ResolveFieldJLS(field_idx, h_dex_cache, h_class_loader); } else { diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 3ca34a70f3..8bd00b802c 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -4883,7 +4883,7 @@ ArtField* MethodVerifier::GetStaticField(int field_idx) { return nullptr; // Can't resolve Class so no more to do here, will do checking at runtime. } ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ArtField* field = class_linker->ResolveFieldJLS(*dex_file_, field_idx, dex_cache_, class_loader_); + ArtField* field = class_linker->ResolveFieldJLS(field_idx, dex_cache_, class_loader_); // Record result of the field resolution attempt. VerifierDeps::MaybeRecordFieldResolution(*dex_file_, field_idx, field); @@ -4924,7 +4924,7 @@ ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_id return nullptr; // Can't resolve Class so no more to do here } ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ArtField* field = class_linker->ResolveFieldJLS(*dex_file_, field_idx, dex_cache_, class_loader_); + ArtField* field = class_linker->ResolveFieldJLS(field_idx, dex_cache_, class_loader_); // Record result of the field resolution attempt. VerifierDeps::MaybeRecordFieldResolution(*dex_file_, field_idx, field); -- GitLab From af94020190a2153834e30fc962e28c3b63d7ffc2 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 8 Dec 2017 15:01:18 +0000 Subject: [PATCH 149/226] Do not pass DexFile to ClassLinker::ResolveMethodType(). The DexFile can be easily retrieved from the DexCache, so reduce the number of arguments that need to be passed. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Change-Id: I00634b89013b7348460aa73561fa14be7c8cdb7e --- runtime/class_linker.cc | 26 +++++++++++------------ runtime/class_linker.h | 23 ++++++++++---------- runtime/class_linker_test.cc | 18 +++------------- runtime/interpreter/interpreter_common.cc | 10 +++------ runtime/interpreter/interpreter_common.h | 12 +++++------ runtime/mirror/dex_cache_test.cc | 2 -- 6 files changed, 37 insertions(+), 54 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 095cb30a04..012e2a4548 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -8184,11 +8184,11 @@ ArtField* ClassLinker::ResolveFieldJLS(uint32_t field_idx, return resolved; } -mirror::MethodType* ClassLinker::ResolveMethodType(Thread* self, - const DexFile& dex_file, - uint32_t proto_idx, - Handle dex_cache, - Handle class_loader) { +ObjPtr ClassLinker::ResolveMethodType( + Thread* self, + uint32_t proto_idx, + Handle dex_cache, + Handle class_loader) { DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); DCHECK(dex_cache != nullptr); @@ -8200,6 +8200,7 @@ mirror::MethodType* ClassLinker::ResolveMethodType(Thread* self, StackHandleScope<4> hs(self); // First resolve the return type. + const DexFile& dex_file = *dex_cache->GetDexFile(); const DexFile::ProtoId& proto_id = dex_file.GetProtoId(proto_idx); Handle return_type(hs.NewHandle( ResolveType(dex_file, proto_id.return_type_idx_, dex_cache, class_loader))); @@ -8246,14 +8247,13 @@ mirror::MethodType* ClassLinker::ResolveMethodType(Thread* self, return type.Get(); } -mirror::MethodType* ClassLinker::ResolveMethodType(Thread* self, - uint32_t proto_idx, - ArtMethod* referrer) { +ObjPtr ClassLinker::ResolveMethodType(Thread* self, + uint32_t proto_idx, + ArtMethod* referrer) { StackHandleScope<2> hs(self); - const DexFile* dex_file = referrer->GetDexFile(); Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); Handle class_loader(hs.NewHandle(referrer->GetClassLoader())); - return ResolveMethodType(self, *dex_file, proto_idx, dex_cache, class_loader); + return ResolveMethodType(self, proto_idx, dex_cache, class_loader); } mirror::MethodHandle* ClassLinker::ResolveMethodHandleForField( @@ -8548,9 +8548,9 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod( return mirror::MethodHandleImpl::Create(self, target, kind, method_type); } -mirror::MethodHandle* ClassLinker::ResolveMethodHandle(Thread* self, - uint32_t method_handle_idx, - ArtMethod* referrer) +ObjPtr ClassLinker::ResolveMethodHandle(Thread* self, + uint32_t method_handle_idx, + ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile* const dex_file = referrer->GetDexFile(); const DexFile::MethodHandleItem& method_handle = dex_file->GetMethodHandle(method_handle_idx); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 6a8c7c1d05..a5cde5b592 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -366,25 +366,26 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - // Resolve a method type with a given ID from the DexFile, storing - // the result in the DexCache. - mirror::MethodType* ResolveMethodType(Thread* self, - const DexFile& dex_file, - uint32_t proto_idx, - Handle dex_cache, - Handle class_loader) + // 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, + Handle dex_cache, + Handle class_loader) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - mirror::MethodType* ResolveMethodType(Thread* self, uint32_t proto_idx, ArtMethod* referrer) + ObjPtr ResolveMethodType(Thread* self, + uint32_t proto_idx, + ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_); // Resolve a method handle with a given ID from the DexFile. The // result is not cached in the DexCache as the instance will only be // used once in most circumstances. - mirror::MethodHandle* ResolveMethodHandle(Thread* self, - uint32_t method_handle_idx, - ArtMethod* referrer) + ObjPtr ResolveMethodHandle(Thread* self, + uint32_t method_handle_idx, + ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_); // Returns true on success, false if there's an exception pending. diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 94125507ef..b625c40fc3 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -1549,11 +1549,7 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) { // Its RType = Ljava/lang/String; // Its PTypes = { Ljava/lang/String; } Handle method1_type = hs.NewHandle( - class_linker_->ResolveMethodType(soa.Self(), - dex_file, - method1_id.proto_idx_, - dex_cache, - class_loader)); + class_linker_->ResolveMethodType(soa.Self(), method1_id.proto_idx_, dex_cache, class_loader)); // Assert that the method type was resolved successfully. ASSERT_TRUE(method1_type != nullptr); @@ -1567,11 +1563,7 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) { // Resolve the method type again and assert that we get back the same value. Handle method1_type2 = hs.NewHandle( - class_linker_->ResolveMethodType(soa.Self(), - dex_file, - method1_id.proto_idx_, - dex_cache, - class_loader)); + class_linker_->ResolveMethodType(soa.Self(), method1_id.proto_idx_, dex_cache, class_loader)); ASSERT_EQ(method1_type.Get(), method1_type2.Get()); // Resolve the MethodType associated with a different method signature @@ -1584,11 +1576,7 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) { ASSERT_FALSE(method2->IsDirect()); const DexFile::MethodId& method2_id = dex_file.GetMethodId(method2->GetDexMethodIndex()); Handle method2_type = hs.NewHandle( - class_linker_->ResolveMethodType(soa.Self(), - dex_file, - method2_id.proto_idx_, - dex_cache, - class_loader)); + class_linker_->ResolveMethodType(soa.Self(), method2_id.proto_idx_, dex_cache, class_loader)); ASSERT_TRUE(method1_type.Get() != method2_type.Get()); } diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index d2d017e118..10cbf8daf1 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -1073,12 +1073,8 @@ static ObjPtr InvokeBootstrapMethod(Thread* self, // 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, - *dex_file, - method_type_idx, - dex_cache, - class_loader))); + Handle method_type(hs.NewHandle( + class_linker->ResolveMethodType(self, method_type_idx, dex_cache, class_loader))); if (method_type.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -1113,7 +1109,7 @@ static ObjPtr InvokeBootstrapMethod(Thread* self, case EncodedArrayValueIterator::ValueType::kMethodType: { uint32_t idx = static_cast(jvalue.i); ObjPtr ref = - class_linker->ResolveMethodType(self, *dex_file, idx, dex_cache, class_loader); + class_linker->ResolveMethodType(self, idx, dex_cache, class_loader); if (ref.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index f097bc71b9..ebd67ab4ec 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -206,17 +206,17 @@ static inline bool DoInvoke(Thread* self, } } -static inline mirror::MethodHandle* ResolveMethodHandle(Thread* self, - uint32_t method_handle_index, - ArtMethod* referrer) +static inline ObjPtr ResolveMethodHandle(Thread* self, + uint32_t method_handle_index, + ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); return class_linker->ResolveMethodHandle(self, method_handle_index, referrer); } -static inline mirror::MethodType* ResolveMethodType(Thread* self, - uint32_t method_type_index, - ArtMethod* referrer) +static inline ObjPtr ResolveMethodType(Thread* self, + uint32_t method_type_index, + ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); return class_linker->ResolveMethodType(self, method_type_index, referrer); diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc index 8198636b3d..d2bff2c19a 100644 --- a/runtime/mirror/dex_cache_test.cc +++ b/runtime/mirror/dex_cache_test.cc @@ -150,13 +150,11 @@ TEST_F(DexCacheMethodHandlesTest, TestResolvedMethodTypes) { const DexFile::MethodId& method2_id = dex_file.GetMethodId(method2->GetDexMethodIndex()); Handle method1_type = hs.NewHandle( class_linker_->ResolveMethodType(soa.Self(), - dex_file, method1_id.proto_idx_, dex_cache, class_loader)); Handle method2_type = hs.NewHandle( class_linker_->ResolveMethodType(soa.Self(), - dex_file, method2_id.proto_idx_, dex_cache, class_loader)); -- GitLab From dcd56c9116c2dc45b40b38fbb2a55c7e33a85a45 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 20 Nov 2017 20:30:24 -0800 Subject: [PATCH 150/226] Add --cdex-fast and --cdex-none variant to testrunner Added --cdex-fast and --cdex-none variant options. These are passed to ART through the compact-dex-level option. Bug: 63756964 Test: test/testrunner/testrunner.py --host --cdex-fast -j40 Test: Commented out header writing in compact_dex_writer.cc and confirmed tests fail. Change-Id: I408555b48286d2c4c5eca72e2cabe956aa1e9835 --- test/etc/run-test-jar | 4 ++++ test/knownfailures.json | 8 +++++++- test/run-test | 6 ++++++ test/testrunner/testrunner.py | 21 ++++++++++++++++----- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 31f43fc536..4844d1e758 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -184,6 +184,10 @@ while true; do elif [ "x$1" = "x--prebuild" ]; then PREBUILD="y" shift + elif [ "x$1" = "x--compact-dex-level" ]; then + shift + COMPILE_FLAGS="${COMPILE_FLAGS} --compact-dex-level=$1" + shift elif [ "x$1" = "x--jvmti-redefine-stress" ]; then # APP_IMAGE doesn't really work with jvmti redefine stress USE_JVMTI="y" diff --git a/test/knownfailures.json b/test/knownfailures.json index 5cd788816a..ae1830af5c 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -383,7 +383,7 @@ "tests": ["629-vdex-speed", "634-vdex-duplicate"], "description": ["Profile driven dexlayout does not work with vdex or dex verifier."], - "variant": "speed-profile" + "variant": "speed-profile | cdex-fast" }, { "test_patterns": ["616-cha.*"], @@ -649,5 +649,11 @@ "tests": "661-oat-writer-layout", "variant": "interp-ac | interpreter | jit | no-dex2oat | no-prebuild | no-image | trace", "description": ["Test is designed to only check --compiler-filter=speed"] + }, + { + "tests": ["628-vdex", "975-iface-private"], + "variant": "cdex-fast", + "description": ["CompactDex doesn't yet work with input-vdex or 975-iface private"] } + ] diff --git a/test/run-test b/test/run-test index fdb2ee47a7..75fe15c919 100755 --- a/test/run-test +++ b/test/run-test @@ -225,6 +225,11 @@ while true; do run_args="${run_args} --prebuild" prebuild_mode="yes" shift; + elif [ "x$1" = "x--compact-dex-level" ]; then + option="$1" + shift + run_args="${run_args} $option $1" + shift; elif [ "x$1" = "x--strip-dex" ]; then run_args="${run_args} --strip-dex" shift; @@ -660,6 +665,7 @@ if [ "$usage" = "yes" ]; then echo " -Xcompiler-option Pass an option to the compiler." echo " --build-option Pass an option to the build script." echo " --runtime-option Pass an option to the runtime." + echo " --compact-dex-level Specify a compact dex level to the compiler." echo " --debug Wait for the default debugger to attach." echo " --debug-agent " echo " Wait for the given debugger agent to attach. Currently" diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 554b8a5429..93998579f3 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -141,6 +141,7 @@ def gather_test_info(): VARIANT_TYPE_DICT['debuggable'] = {'ndebuggable', 'debuggable'} VARIANT_TYPE_DICT['gc'] = {'gcstress', 'gcverify', 'cms'} VARIANT_TYPE_DICT['prebuild'] = {'no-prebuild', 'no-dex2oat', 'prebuild'} + VARIANT_TYPE_DICT['cdex_level'] = {'cdex-none', 'cdex-fast'} VARIANT_TYPE_DICT['relocate'] = {'relocate-npatchoat', 'relocate', 'no-relocate'} VARIANT_TYPE_DICT['jni'] = {'jni', 'forcecopy', 'checkjni'} VARIANT_TYPE_DICT['address_sizes'] = {'64', '32'} @@ -183,6 +184,9 @@ def setup_test_env(): 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-none') + # By default only run without jvmti if not _user_input_variants['jvmti']: _user_input_variants['jvmti'].add('no-jvmti') @@ -339,10 +343,11 @@ def run_tests(tests): _user_input_variants['relocate'], _user_input_variants['trace'], _user_input_variants['gc'], _user_input_variants['jni'], _user_input_variants['image'], _user_input_variants['pictest'], - _user_input_variants['debuggable'], _user_input_variants['jvmti']) + _user_input_variants['debuggable'], _user_input_variants['jvmti'], + _user_input_variants['cdex_level']) for test, target, run, prebuild, compiler, relocate, trace, gc, \ - jni, image, pictest, debuggable, jvmti in config: + jni, image, pictest, debuggable, jvmti, cdex_level in config: for address_size in _user_input_variants['address_sizes_target'][target]: if stop_testrunner: # When ART_TEST_KEEP_GOING is set to false, then as soon as a test @@ -356,6 +361,7 @@ def run_tests(tests): test_name += target + '-run-test-' test_name += run + '-' test_name += prebuild + '-' + test_name += cdex_level + '-' test_name += compiler + '-' test_name += relocate + '-' test_name += trace + '-' @@ -369,7 +375,7 @@ def run_tests(tests): test_name += address_size variant_set = {target, run, prebuild, compiler, relocate, trace, gc, jni, - image, pictest, debuggable, jvmti, address_size} + image, pictest, debuggable, jvmti, cdex_level, address_size} options_test = options_all @@ -386,6 +392,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 compiler == 'optimizing': options_test += ' --optimizing' elif compiler == 'regalloc_gc': @@ -806,6 +815,7 @@ def parse_test_name(test_name): regex += '(' + '|'.join(VARIANT_TYPE_DICT['pictest']) + ')-' regex += '(' + '|'.join(VARIANT_TYPE_DICT['debuggable']) + ')-' regex += '(' + '|'.join(VARIANT_TYPE_DICT['jvmti']) + ')-' + regex += '(' + '|'.join(VARIANT_TYPE_DICT['cdex_level']) + ')-' regex += '(' + '|'.join(RUN_TEST_SET) + ')' regex += '(' + '|'.join(VARIANT_TYPE_DICT['address_sizes']) + ')$' match = re.match(regex, test_name) @@ -822,8 +832,9 @@ def parse_test_name(test_name): _user_input_variants['pictest'].add(match.group(10)) _user_input_variants['debuggable'].add(match.group(11)) _user_input_variants['jvmti'].add(match.group(12)) - _user_input_variants['address_sizes'].add(match.group(14)) - return {match.group(13)} + _user_input_variants['cdex_level'].add(match.group(13)) + _user_input_variants['address_sizes'].add(match.group(15)) + return {match.group(14)} raise ValueError(test_name + " is not a valid test") -- GitLab From c654816053ae07fb1f129d705e94b76e59f37454 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Fri, 8 Dec 2017 12:15:22 -0800 Subject: [PATCH 151/226] ART: Change SCOPED_TRACE implementation Move to a LOG-like usage pattern. This improves usability in the presence of lock annotations. Demonstrate in the JIT compiler, where a lambda would require a REQUIRES_SHARED annotation. Test: m Test: manual Change-Id: I9da2bfb29ed11660dbeb6f114a3d6c7ffef3a26d --- compiler/Android.bp | 7 +----- compiler/jit/jit_compiler.cc | 3 +++ runtime/base/systrace.h | 36 +++++++++++++++++++++++++---- runtime/verifier/method_verifier.cc | 2 +- 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/compiler/Android.bp b/compiler/Android.bp index 37a18cb9e9..fc19b54131 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -181,15 +181,10 @@ art_cc_defaults { ], }, }, - target: { - android: { - // For atrace. - shared_libs: ["libcutils"], - }, - }, generated_sources: ["art_compiler_operator_srcs"], shared_libs: [ "libbase", + "libcutils", // for atrace. "liblzma", ], include_dirs: ["art/disassembler"], diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index 74603c668f..88e3e5b230 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -23,6 +23,7 @@ #include "art_method-inl.h" #include "base/logging.h" // For VLOG #include "base/stringpiece.h" +#include "base/systrace.h" #include "base/time_utils.h" #include "base/timing_logger.h" #include "base/unix_file/fd_file.h" @@ -163,6 +164,8 @@ JitCompiler::~JitCompiler() { } bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method, bool osr) { + SCOPED_TRACE << "JIT compiling " << method->PrettyMethod(); + DCHECK(!method->IsProxyMethod()); DCHECK(method->GetDeclaringClass()->IsResolved()); diff --git a/runtime/base/systrace.h b/runtime/base/systrace.h index c6b6ff1d43..08ab93d232 100644 --- a/runtime/base/systrace.h +++ b/runtime/base/systrace.h @@ -21,6 +21,7 @@ #include #include +#include #include #include "android-base/stringprintf.h" @@ -46,10 +47,37 @@ class ScopedTrace { } }; -#define SCOPED_TRACE(fmtstr, ...) \ - ::art::ScopedTrace trace ## __LINE__([&]() { \ - return ::android::base::StringPrintf((fmtstr), __VA_ARGS__); \ - }) +// Helper for the SCOPED_TRACE macro. Do not use directly. +class ScopedTraceNoStart { + public: + ScopedTraceNoStart() { + } + + ~ScopedTraceNoStart() { + ATRACE_END(); + } + + // Message helper for the macro. Do not use directly. + class ScopedTraceMessageHelper { + public: + ScopedTraceMessageHelper() { + } + ~ScopedTraceMessageHelper() { + ATRACE_BEGIN(buffer_.str().c_str()); + } + + std::ostream& stream() { + return buffer_; + } + + private: + std::ostringstream buffer_; + }; +}; + +#define SCOPED_TRACE \ + ::art::ScopedTraceNoStart trace ## __LINE__; \ + (ATRACE_ENABLED()) && ::art::ScopedTraceNoStart::ScopedTraceMessageHelper().stream() } // namespace art diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 271e2133cc..154292d460 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -283,7 +283,7 @@ FailureKind MethodVerifier::VerifyClass(Thread* self, bool allow_soft_failures, HardFailLogMode log_level, std::string* error) { - SCOPED_TRACE("VerifyClass %s", PrettyDescriptor(dex_file->GetClassDescriptor(class_def)).c_str()); + 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)) { -- GitLab From 9b827ab7e63cf8b24987e75186434348d0dbf4e8 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 7 Dec 2017 19:32:48 -0800 Subject: [PATCH 152/226] ART: Clean up ATRACE use Remove old ATRACE_BEGIN & _END pairs, where possible. Remove ATRACE_CALL and replace it with ScopedTrace. Remove utils/Trace.h include. Test: m Change-Id: I3a5123202f4e373074bfe0f7359ee6c60a70352a --- compiler/driver/compiler_driver.cc | 10 +++++----- runtime/base/systrace.h | 1 - runtime/gc/heap.cc | 3 +-- runtime/thread_list.cc | 7 +++---- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 3bba9ef4ae..129c5d8e23 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1612,7 +1612,7 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { : manager_(manager) {} void Visit(size_t class_def_index) OVERRIDE REQUIRES(!Locks::mutator_lock_) { - ATRACE_CALL(); + ScopedTrace trace(__FUNCTION__); Thread* const self = Thread::Current(); jobject jclass_loader = manager_->GetClassLoader(); const DexFile& dex_file = *manager_->GetDexFile(); @@ -1957,7 +1957,7 @@ class VerifyClassVisitor : public CompilationVisitor { : manager_(manager), log_level_(log_level) {} virtual void Visit(size_t class_def_index) REQUIRES(!Locks::mutator_lock_) OVERRIDE { - ATRACE_CALL(); + ScopedTrace trace(__FUNCTION__); ScopedObjectAccess soa(Thread::Current()); const DexFile& dex_file = *manager_->GetDexFile(); const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); @@ -2086,7 +2086,7 @@ class SetVerifiedClassVisitor : public CompilationVisitor { explicit SetVerifiedClassVisitor(const ParallelCompilationManager* manager) : manager_(manager) {} virtual void Visit(size_t class_def_index) REQUIRES(!Locks::mutator_lock_) OVERRIDE { - ATRACE_CALL(); + ScopedTrace trace(__FUNCTION__); ScopedObjectAccess soa(Thread::Current()); const DexFile& dex_file = *manager_->GetDexFile(); const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); @@ -2150,7 +2150,7 @@ class InitializeClassVisitor : public CompilationVisitor { explicit InitializeClassVisitor(const ParallelCompilationManager* manager) : manager_(manager) {} void Visit(size_t class_def_index) OVERRIDE { - ATRACE_CALL(); + ScopedTrace trace(__FUNCTION__); jobject jclass_loader = manager_->GetClassLoader(); const DexFile& dex_file = *manager_->GetDexFile(); const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); @@ -2666,7 +2666,7 @@ class CompileClassVisitor : public CompilationVisitor { explicit CompileClassVisitor(const ParallelCompilationManager* manager) : manager_(manager) {} virtual void Visit(size_t class_def_index) REQUIRES(!Locks::mutator_lock_) OVERRIDE { - ATRACE_CALL(); + ScopedTrace trace(__FUNCTION__); const DexFile& dex_file = *manager_->GetDexFile(); const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); ClassLinker* class_linker = manager_->GetClassLinker(); diff --git a/runtime/base/systrace.h b/runtime/base/systrace.h index 08ab93d232..dc2206e420 100644 --- a/runtime/base/systrace.h +++ b/runtime/base/systrace.h @@ -19,7 +19,6 @@ #define ATRACE_TAG ATRACE_TAG_DALVIK #include -#include #include #include diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 91f1ff6ace..f7be4c8d5a 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -800,12 +800,11 @@ void Heap::IncrementDisableThreadFlip(Thread* self) { bool has_waited = false; uint64_t wait_start = NanoTime(); if (thread_flip_running_) { - ATRACE_BEGIN("IncrementDisableThreadFlip"); + ScopedTrace trace("IncrementDisableThreadFlip"); while (thread_flip_running_) { has_waited = true; thread_flip_cond_->Wait(self); } - ATRACE_END(); } ++disable_thread_flip_count_; if (has_waited) { diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 8754819e09..e43b9f4b96 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -365,11 +365,11 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function, Closure* callback // Run the checkpoint on the suspended threads. for (const auto& thread : suspended_count_modified_threads) { if (!thread->IsSuspended()) { - if (ATRACE_ENABLED()) { + ScopedTrace trace([&]() { std::ostringstream oss; thread->ShortDump(oss); - ATRACE_BEGIN((std::string("Waiting for suspension of thread ") + oss.str()).c_str()); - } + return std::string("Waiting for suspension of thread ") + oss.str(); + }); // Busy wait until the thread is suspended. const uint64_t start_time = NanoTime(); do { @@ -378,7 +378,6 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function, Closure* callback const uint64_t total_delay = NanoTime() - start_time; // Shouldn't need to wait for longer than 1000 microseconds. constexpr uint64_t kLongWaitThreshold = MsToNs(1); - ATRACE_END(); if (UNLIKELY(total_delay > kLongWaitThreshold)) { LOG(WARNING) << "Long wait of " << PrettyDuration(total_delay) << " for " << *thread << " suspension!"; -- GitLab From 5c6ff7d04131d048fce99eba32ed636b9a04669c Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Sun, 10 Dec 2017 15:10:54 +0000 Subject: [PATCH 153/226] Blacklist timing out test. bug: 70459916 Change-Id: Ieb5e41b21c1fcaac9dcf7b01ccca314ce783c6c8 --- tools/libjdwp_oj_art_failures.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/libjdwp_oj_art_failures.txt b/tools/libjdwp_oj_art_failures.txt index e1cc831303..145022e7eb 100644 --- a/tools/libjdwp_oj_art_failures.txt +++ b/tools/libjdwp_oj_art_failures.txt @@ -79,5 +79,11 @@ result: EXEC_FAILED, bug: 69591477, name: "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.ExitTest#testExit001" +}, +{ + description: "Test times out on fugu-debug", + result: EXEC_FAILED, + bug: 70459916, + name: "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest#testVMDebug" } ] -- GitLab From e82b60fabed960b36221665f090717695d7a9820 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Sun, 10 Dec 2017 21:50:10 +0000 Subject: [PATCH 154/226] Update help message dump-timing -> dump-timings. Test: m Change-Id: I14fd14db7cad267cef42abab058de83bf0fcb980 --- dex2oat/dex2oat.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 27bec1d3e1..a70e551ec0 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -345,7 +345,7 @@ NO_RETURN static void Usage(const char* fmt, ...) { CompilerOptions::kDefaultInlineMaxCodeUnits); UsageError(" Default: %d", CompilerOptions::kDefaultInlineMaxCodeUnits); UsageError(""); - UsageError(" --dump-timing: display a breakdown of where time was spent"); + UsageError(" --dump-timings: display a breakdown of where time was spent"); UsageError(""); UsageError(" -g"); UsageError(" --generate-debug-info: Generate debug information for native debugging,"); -- GitLab From 9c32ada693e810f5afad8a448a69fa8ea3f3934e Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Mon, 11 Dec 2017 07:24:43 +0000 Subject: [PATCH 155/226] jfuzz: Fix whitespace bogon Test: mm Change-Id: I34763a786be01ec571ae121f49fe6e2bab3f8dcb --- tools/jfuzz/jfuzz.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/jfuzz/jfuzz.cc b/tools/jfuzz/jfuzz.cc index 825648b1ed..a6034c8c3a 100644 --- a/tools/jfuzz/jfuzz.cc +++ b/tools/jfuzz/jfuzz.cc @@ -30,6 +30,7 @@ namespace { /* * Operators. */ + static constexpr const char* kIncDecOps[] = { "++", "--" }; static constexpr const char* kIntUnaryOps[] = { "+", "-", "~" }; static constexpr const char* kFpUnaryOps[] = { "+", "-" }; -- GitLab From bfe2c6cc48cc56305c42c1d09bf03cf80f556a49 Mon Sep 17 00:00:00 2001 From: Goran Jakovljevic Date: Mon, 11 Dec 2017 12:28:19 +0100 Subject: [PATCH 156/226] MIPS: Add missing include Following error has been generated due to missing include: art/runtime/arch/mips/entrypoints_init_mips.cc:288:27: error: use of undeclared identifier 'systrace_lock_logging' This fixes aosp_mips-eng build. Test: successful aosp_mips-eng build Change-Id: I19920cbb7dd3d928352be95a06d5138d3d505bd0 --- runtime/arch/mips/entrypoints_init_mips.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index dca3382664..209f36705a 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -18,6 +18,7 @@ #include "arch/mips/asm_support_mips.h" #include "atomic.h" +#include "base/logging.h" #include "entrypoints/entrypoint_utils.h" #include "entrypoints/jni/jni_entrypoints.h" #include "entrypoints/math_entrypoints.h" -- GitLab From a64b52deb0c792b8a0d47546edb8a2f8a7816c33 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 8 Dec 2017 16:27:49 +0000 Subject: [PATCH 157/226] Do not pass DexFile to ClassLinker::Lookup/ResolveString(). The DexFile can be easily retrieved from the DexCache, so reduce the number of arguments that need to be passed. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Change-Id: I8c4cec43b31b27de7e4e94374fdd69c6d6ca6c13 --- compiler/driver/compiler_driver.cc | 13 +++++-------- compiler/optimizing/sharpening.cc | 6 +++--- dex2oat/linker/image_writer.cc | 2 +- dex2oat/linker/oat_writer.cc | 5 ++--- runtime/art_field-inl.h | 5 ++--- runtime/art_field.cc | 5 +---- runtime/art_field.h | 1 - runtime/art_method.cc | 3 +-- runtime/class_linker.cc | 8 ++++---- runtime/class_linker.h | 14 ++++++-------- runtime/dex_file_annotations.cc | 5 ++--- runtime/entrypoints/entrypoint_utils-inl.h | 6 ++---- runtime/interpreter/interpreter_common.cc | 4 ++-- runtime/interpreter/interpreter_common.h | 4 +--- runtime/transaction_test.cc | 6 +++--- 15 files changed, 35 insertions(+), 52 deletions(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 2d43476d5a..d51bded7eb 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -701,7 +701,6 @@ void CompilerDriver::Resolve(jobject class_loader, // stable order. static void ResolveConstStrings(Handle dex_cache, - const DexFile& dex_file, const DexFile::CodeItem* code_item) REQUIRES_SHARED(Locks::mutator_lock_) { if (code_item == nullptr) { @@ -717,8 +716,7 @@ static void ResolveConstStrings(Handle dex_cache, dex::StringIndex string_index((inst->Opcode() == Instruction::CONST_STRING) ? inst->VRegB_21c() : inst->VRegB_31c()); - ObjPtr string = - class_linker->ResolveString(dex_file, string_index, dex_cache); + ObjPtr string = class_linker->ResolveString(string_index, dex_cache); CHECK(string != nullptr) << "Could not allocate a string when forcing determinism"; break; } @@ -773,7 +771,7 @@ static void ResolveConstStrings(CompilerDriver* driver, continue; } previous_method_idx = method_idx; - ResolveConstStrings(dex_cache, *dex_file, it.GetMethodCodeItem()); + ResolveConstStrings(dex_cache, it.GetMethodCodeItem()); it.Next(); } DCHECK(!it.HasNext()); @@ -2331,7 +2329,6 @@ class InitializeClassVisitor : public CompilationVisitor { StackHandleScope<1> hs(Thread::Current()); Handle dex_cache = hs.NewHandle(klass->GetDexCache()); - const DexFile* dex_file = manager_->GetDexFile(); const DexFile::ClassDef* class_def = klass->GetClassDef(); ClassLinker* class_linker = manager_->GetClassLinker(); @@ -2344,7 +2341,7 @@ class InitializeClassVisitor : public CompilationVisitor { if (value_it.GetValueType() == annotations::RuntimeEncodedStaticFieldValueIterator::kString) { // Resolve the string. This will intern the string. art::ObjPtr resolved = class_linker->ResolveString( - *dex_file, dex::StringIndex(value_it.GetJavaValue().i), dex_cache); + dex::StringIndex(value_it.GetJavaValue().i), dex_cache); CHECK(resolved != nullptr); } } @@ -2357,11 +2354,11 @@ class InitializeClassVisitor : public CompilationVisitor { for (const DexInstructionPcPair& inst : code_item->Instructions()) { if (inst->Opcode() == Instruction::CONST_STRING) { ObjPtr s = class_linker->ResolveString( - *dex_file, dex::StringIndex(inst->VRegB_21c()), dex_cache); + dex::StringIndex(inst->VRegB_21c()), dex_cache); CHECK(s != nullptr); } else if (inst->Opcode() == Instruction::CONST_STRING_JUMBO) { ObjPtr s = class_linker->ResolveString( - *dex_file, dex::StringIndex(inst->VRegB_31c()), dex_cache); + dex::StringIndex(inst->VRegB_31c()), dex_cache); CHECK(s != nullptr); } } diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index 64092d307d..1e49411c72 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -262,7 +262,7 @@ void HSharpening::ProcessLoadString( // Compiling boot image. Resolve the string and allocate it if needed, to ensure // the string will be added to the boot image. DCHECK(!runtime->UseJitCompilation()); - string = class_linker->ResolveString(dex_file, string_index, dex_cache); + string = class_linker->ResolveString(string_index, dex_cache); CHECK(string != nullptr); if (compiler_driver->GetSupportBootImageFixup()) { DCHECK(ContainsElement(compiler_driver->GetDexFilesForOatFile(), &dex_file)); @@ -273,7 +273,7 @@ void HSharpening::ProcessLoadString( } } else if (runtime->UseJitCompilation()) { DCHECK(!codegen->GetCompilerOptions().GetCompilePic()); - string = class_linker->LookupString(dex_file, string_index, dex_cache.Get()); + string = class_linker->LookupString(string_index, dex_cache.Get()); if (string != nullptr) { if (runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { desired_load_kind = HLoadString::LoadKind::kBootImageAddress; @@ -285,7 +285,7 @@ void HSharpening::ProcessLoadString( } } else { // AOT app compilation. Try to lookup the string without allocating if not found. - string = class_linker->LookupString(dex_file, string_index, dex_cache.Get()); + 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; diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 5ade583b88..28521e1222 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -1148,7 +1148,7 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr dex_cache, uint32_t stored_index = pair.index; ObjPtr string = pair.object.Read(); if (string == nullptr || i < stored_index) { - string = class_linker->LookupString(dex_file, string_idx, dex_cache); + string = class_linker->LookupString(string_idx, dex_cache); DCHECK(string == nullptr || dex_cache->GetResolvedString(string_idx) == string); } } diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 4be225d881..57d02c532c 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -1964,9 +1964,8 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { ObjPtr GetTargetString(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) { ClassLinker* linker = Runtime::Current()->GetClassLinker(); - ObjPtr string = linker->LookupString(*patch.TargetStringDexFile(), - patch.TargetStringIndex(), - GetDexCache(patch.TargetStringDexFile())); + ObjPtr string = + linker->LookupString(patch.TargetStringIndex(), GetDexCache(patch.TargetStringDexFile())); DCHECK(string != nullptr); DCHECK(writer_->HasBootImage() || Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(string)); diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h index ae81ee8470..02ae0e32ea 100644 --- a/runtime/art_field-inl.h +++ b/runtime/art_field-inl.h @@ -352,11 +352,10 @@ inline ObjPtr ArtField::GetStringName(Thread* self, bool resolve auto dex_field_index = GetDexFieldIndex(); CHECK_NE(dex_field_index, dex::kDexNoIndex); ObjPtr dex_cache = GetDexCache(); - const auto* dex_file = dex_cache->GetDexFile(); - const auto& field_id = dex_file->GetFieldId(dex_field_index); + 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, *dex_file, field_id.name_idx_, dex_cache); + name = ResolveGetStringName(self, field_id.name_idx_, dex_cache); } return name; } diff --git a/runtime/art_field.cc b/runtime/art_field.cc index 54746a3685..dbba2b0918 100644 --- a/runtime/art_field.cc +++ b/runtime/art_field.cc @@ -52,13 +52,10 @@ ObjPtr ArtField::ProxyFindSystemClass(const char* descriptor) { } ObjPtr ArtField::ResolveGetStringName(Thread* self, - const DexFile& dex_file, dex::StringIndex string_idx, ObjPtr dex_cache) { StackHandleScope<1> hs(self); - return Runtime::Current()->GetClassLinker()->ResolveString(dex_file, - string_idx, - hs.NewHandle(dex_cache)); + return Runtime::Current()->GetClassLinker()->ResolveString(string_idx, hs.NewHandle(dex_cache)); } std::string ArtField::PrettyField(ArtField* f, bool with_type) { diff --git a/runtime/art_field.h b/runtime/art_field.h index a6f050810f..8d2f9ff71b 100644 --- a/runtime/art_field.h +++ b/runtime/art_field.h @@ -234,7 +234,6 @@ class ArtField FINAL { ObjPtr ProxyFindSystemClass(const char* descriptor) REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr ResolveGetStringName(Thread* self, - const DexFile& dex_file, dex::StringIndex string_idx, ObjPtr dex_cache) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/art_method.cc b/runtime/art_method.cc index ebfa4feefa..9005120eb0 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -141,8 +141,7 @@ ObjPtr ArtMethod::GetNameAsString(Thread* self) { 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(*dex_file, method_id.name_idx_, - dex_cache); + return Runtime::Current()->GetClassLinker()->ResolveString(method_id.name_idx_, dex_cache); } void ArtMethod::ThrowInvocationTimeError() { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 012e2a4548..093ec6540c 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -7726,8 +7726,7 @@ void ClassLinker::CreateReferenceInstanceOffsets(Handle klass) { klass->SetReferenceInstanceOffsets(reference_offsets); } -ObjPtr ClassLinker::ResolveString(const DexFile& dex_file, - dex::StringIndex string_idx, +ObjPtr ClassLinker::ResolveString(dex::StringIndex string_idx, Handle dex_cache) { DCHECK(dex_cache != nullptr); Thread::PoisonObjectPointersIfDebug(); @@ -7735,6 +7734,7 @@ ObjPtr ClassLinker::ResolveString(const DexFile& dex_file, 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); ObjPtr string = intern_table_->InternStrong(utf16_length, utf8_data); @@ -7744,14 +7744,14 @@ ObjPtr ClassLinker::ResolveString(const DexFile& dex_file, return string; } -ObjPtr ClassLinker::LookupString(const DexFile& dex_file, - dex::StringIndex string_idx, +ObjPtr ClassLinker::LookupString(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); ObjPtr string = diff --git a/runtime/class_linker.h b/runtime/class_linker.h index a5cde5b592..6a5768ec27 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -243,17 +243,15 @@ class ClassLinker { REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - // Resolve a String with the given index from the DexFile, storing the - // result in the DexCache. - ObjPtr ResolveString(const DexFile& dex_file, - dex::StringIndex string_idx, + // 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, Handle dex_cache) REQUIRES_SHARED(Locks::mutator_lock_); - // Find a String with the given index from the DexFile, storing the - // result in the DexCache if found. Return null if not found. - ObjPtr LookupString(const DexFile& dex_file, - dex::StringIndex string_idx, + // Find a String with the given index from the DexFile associated with the given DexCache, + // storing the result in the DexCache if found. Return null if not found. + ObjPtr LookupString(dex::StringIndex string_idx, ObjPtr dex_cache) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index 437347403b..eaf31f308f 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -458,7 +458,7 @@ bool ProcessAnnotationValue(const ClassData& klass, } else { StackHandleScope<1> hs(self); element_object = Runtime::Current()->GetClassLinker()->ResolveString( - klass.GetDexFile(), dex::StringIndex(index), hs.NewHandle(klass.GetDexCache())); + dex::StringIndex(index), hs.NewHandle(klass.GetDexCache())); set_object = true; if (element_object == nullptr) { return false; @@ -1590,8 +1590,7 @@ void RuntimeEncodedStaticFieldValueIterator::ReadValueToField(ArtField* field) c case kDouble: field->SetDouble(field->GetDeclaringClass(), jval_.d); break; case kNull: field->SetObject(field->GetDeclaringClass(), nullptr); break; case kString: { - ObjPtr resolved = linker_->ResolveString(dex_file_, - dex::StringIndex(jval_.i), + ObjPtr resolved = linker_->ResolveString(dex::StringIndex(jval_.i), dex_cache_); field->SetObject(field->GetDeclaringClass(), resolved); break; diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index df17b30580..fa3c027db8 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -757,8 +757,7 @@ static inline ObjPtr ResolveString(ClassLinker* class_linker, if (UNLIKELY(string == nullptr)) { StackHandleScope<1> hs(Thread::Current()); Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); - const DexFile& dex_file = *dex_cache->GetDexFile(); - string = class_linker->ResolveString(dex_file, string_idx, dex_cache); + string = class_linker->ResolveString(string_idx, dex_cache); } return string; } @@ -770,9 +769,8 @@ inline ObjPtr ResolveStringFromCode(ArtMethod* referrer, if (UNLIKELY(string == nullptr)) { StackHandleScope<1> hs(Thread::Current()); Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); - const DexFile& dex_file = *dex_cache->GetDexFile(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - string = class_linker->ResolveString(dex_file, string_idx, dex_cache); + string = class_linker->ResolveString(string_idx, dex_cache); } return string; } diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 10cbf8daf1..4d7a576c06 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -1062,7 +1062,7 @@ static ObjPtr InvokeBootstrapMethod(Thread* self, // The second parameter is the name to lookup. { dex::StringIndex name_idx(static_cast(it.GetJavaValue().i)); - ObjPtr name = class_linker->ResolveString(*dex_file, name_idx, dex_cache); + ObjPtr name = class_linker->ResolveString(name_idx, dex_cache); if (name.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -1132,7 +1132,7 @@ static ObjPtr InvokeBootstrapMethod(Thread* self, } case EncodedArrayValueIterator::ValueType::kString: { dex::StringIndex idx(static_cast(jvalue.i)); - ObjPtr ref = class_linker->ResolveString(*dex_file, idx, dex_cache); + ObjPtr ref = class_linker->ResolveString(idx, dex_cache); if (ref.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index ebd67ab4ec..5937801a0c 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -348,9 +348,7 @@ static inline ObjPtr ResolveString(Thread* self, if (UNLIKELY(string_ptr == nullptr)) { StackHandleScope<1> hs(self); Handle dex_cache(hs.NewHandle(method->GetDexCache())); - string_ptr = Runtime::Current()->GetClassLinker()->ResolveString(*dex_cache->GetDexFile(), - string_idx, - dex_cache); + string_ptr = Runtime::Current()->GetClassLinker()->ResolveString(string_idx, dex_cache); } return string_ptr; } diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc index f922dd317b..304017eadb 100644 --- a/runtime/transaction_test.cc +++ b/runtime/transaction_test.cc @@ -493,7 +493,7 @@ TEST_F(TransactionTest, ResolveString) { dex::StringIndex string_idx = dex_file->GetIndexForStringId(*string_id); ASSERT_TRUE(string_idx.IsValid()); // String should only get resolved by the initializer. - EXPECT_TRUE(class_linker_->LookupString(*dex_file, string_idx, h_dex_cache.Get()) == nullptr); + EXPECT_TRUE(class_linker_->LookupString(string_idx, h_dex_cache.Get()) == nullptr); EXPECT_TRUE(h_dex_cache->GetResolvedString(string_idx) == nullptr); // Do the transaction, then roll back. Runtime::Current()->EnterTransactionMode(); @@ -503,14 +503,14 @@ TEST_F(TransactionTest, ResolveString) { // Make sure the string got resolved by the transaction. { ObjPtr s = - class_linker_->LookupString(*dex_file, string_idx, h_dex_cache.Get()); + 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)); } Runtime::Current()->RollbackAndExitTransactionMode(); // Check that the string did not stay resolved. - EXPECT_TRUE(class_linker_->LookupString(*dex_file, string_idx, h_dex_cache.Get()) == nullptr); + EXPECT_TRUE(class_linker_->LookupString(string_idx, h_dex_cache.Get()) == nullptr); EXPECT_TRUE(h_dex_cache->GetResolvedString(string_idx) == nullptr); ASSERT_FALSE(h_klass->IsInitialized()); ASSERT_FALSE(soa.Self()->IsExceptionPending()); -- GitLab From 890111968fbd3f5ae528d97e42984c12a3dd27bd Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 11 Dec 2017 13:45:05 +0000 Subject: [PATCH 158/226] Do not pass DexFile to ClassLinker::Lookup/ResolveMethod(). The DexFile can be easily retrieved from the DexCache, so reduce the number of arguments that need to be passed. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Change-Id: I2e47280e7cb8b84595130c4abfb5ece18d7f5c75 --- compiler/dex/dex_to_dex_compiler.cc | 1 - compiler/driver/compiler_driver-inl.h | 2 +- compiler/driver/compiler_driver.cc | 5 ++++- compiler/optimizing/instruction_builder.cc | 1 - compiler/optimizing/reference_type_propagation.cc | 2 +- compiler/verifier_deps_test.cc | 3 +-- dex2oat/linker/oat_writer.cc | 3 +-- runtime/class_linker-inl.h | 4 +--- runtime/class_linker.cc | 13 +++++-------- runtime/class_linker.h | 15 ++++++--------- runtime/dex_file_annotations.cc | 2 -- runtime/verifier/method_verifier.cc | 2 +- 12 files changed, 21 insertions(+), 32 deletions(-) diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index 476903522e..ead909af9a 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -296,7 +296,6 @@ void DexCompiler::CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc, ClassLinker* class_linker = unit_.GetClassLinker(); ArtMethod* resolved_method = class_linker->ResolveMethod( - GetDexFile(), method_idx, unit_.GetDexCache(), unit_.GetClassLoader(), diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index 42fc4aa5ba..34c8f22c03 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -105,7 +105,7 @@ inline ArtMethod* CompilerDriver::ResolveMethod( DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); ArtMethod* resolved_method = mUnit->GetClassLinker()->ResolveMethod( - *dex_cache->GetDexFile(), method_idx, dex_cache, class_loader, nullptr, invoke_type); + method_idx, dex_cache, class_loader, /* referrer */ nullptr, invoke_type); if (UNLIKELY(resolved_method == nullptr)) { DCHECK(soa.Self()->IsExceptionPending()); // Clean up any exception left by type resolution. diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index fcc6b8be9e..8f726895de 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1690,7 +1690,10 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { if (resolve_fields_and_methods) { while (it.HasNextMethod()) { ArtMethod* method = class_linker->ResolveMethod( - dex_file, it.GetMemberIndex(), dex_cache, class_loader, nullptr, + it.GetMemberIndex(), + dex_cache, + class_loader, + /* referrer */ nullptr, it.GetMethodInvokeType(class_def)); if (method == nullptr) { CheckAndClearResolveException(soa.Self()); diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index bce4de32d5..6fdb616ce2 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -796,7 +796,6 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in ArtMethod* resolved_method = class_linker->ResolveMethod( - *dex_compilation_unit_->GetDexFile(), method_idx, dex_compilation_unit_->GetDexCache(), class_loader, diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index d84f14acc0..549572ce40 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -544,7 +544,7 @@ void ReferenceTypePropagation::RTPVisitor::SetClassAsTypeInfo(HInstruction* inst // the method is from the String class, the null loader is good enough. Handle loader(hs.NewHandle(nullptr)); ArtMethod* method = cl->ResolveMethod( - dex_file, invoke->GetDexMethodIndex(), dex_cache, loader, nullptr, kDirect); + invoke->GetDexMethodIndex(), dex_cache, loader, /* referrer */ nullptr, kDirect); DCHECK(method != nullptr); mirror::Class* declaring_class = method->GetDeclaringClass(); DCHECK(declaring_class != nullptr); diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 4709fd0e9e..ee1d7c69bc 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -158,11 +158,10 @@ class VerifierDepsTest : public CommonCompilerTest { while (it.HasNextDirectMethod()) { ArtMethod* resolved_method = class_linker_->ResolveMethod( - *primary_dex_file_, it.GetMemberIndex(), dex_cache_handle, class_loader_handle, - nullptr, + /* referrer */ nullptr, it.GetMethodInvokeType(*class_def)); CHECK(resolved_method != nullptr); if (method_name == resolved_method->GetName()) { diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index b163eeb7b7..c271a6cdf4 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -1592,11 +1592,10 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { ScopedObjectAccessUnchecked soa(self); StackHandleScope<1> hs(self); method = class_linker_->ResolveMethod( - *dex_file_, it.GetMemberIndex(), hs.NewHandle(dex_cache), ScopedNullHandle(), - nullptr, + /* referrer */ nullptr, invoke_type); if (method == nullptr) { LOG(FATAL_WITHOUT_ABORT) << "Unexpected failure to resolve a method: " diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index 2d8d4b41a0..c086dd2676 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -237,9 +237,7 @@ inline ArtMethod* ClassLinker::ResolveMethod(Thread* self, StackHandleScope<2> hs(self); Handle h_dex_cache(hs.NewHandle(referrer->GetDexCache())); Handle h_class_loader(hs.NewHandle(declaring_class->GetClassLoader())); - const DexFile* dex_file = h_dex_cache->GetDexFile(); - resolved_method = ResolveMethod(*dex_file, - method_idx, + resolved_method = ResolveMethod(method_idx, h_dex_cache, h_class_loader, referrer, diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 093ec6540c..892c28d54f 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -7938,8 +7938,7 @@ std::string DescribeLoaders(ObjPtr loader, const char* clas } template -ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, - uint32_t method_idx, +ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, Handle dex_cache, Handle class_loader, ArtMethod* referrer, @@ -7957,6 +7956,7 @@ ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); return resolved; } + const DexFile& dex_file = *dex_cache->GetDexFile(); const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); ObjPtr klass = nullptr; if (valid_dex_cache_method) { @@ -8048,8 +8048,7 @@ ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, } } -ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(const DexFile& dex_file, - uint32_t method_idx, +ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx, Handle dex_cache, Handle class_loader) { ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_); @@ -8060,6 +8059,7 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(const DexFile& dex_file, return resolved; } // Fail, get the declaring class. + const DexFile& dex_file = *dex_cache->GetDexFile(); const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); ObjPtr klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); @@ -8431,8 +8431,7 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod( // the invocation type to determine if the method is private. We // then resolve again specifying the intended invocation type to // force the appropriate checks. - target_method = ResolveMethodWithoutInvokeType(*dex_file, - method_handle.field_or_method_idx_, + target_method = ResolveMethodWithoutInvokeType(method_handle.field_or_method_idx_, hs.NewHandle(referrer->GetDexCache()), hs.NewHandle(referrer->GetClassLoader())); if (UNLIKELY(target_method == nullptr)) { @@ -9033,14 +9032,12 @@ mirror::IfTable* ClassLinker::AllocIfTable(Thread* self, size_t ifcount) { // Instantiate ResolveMethod. template ArtMethod* ClassLinker::ResolveMethod( - const DexFile& dex_file, uint32_t method_idx, Handle dex_cache, Handle class_loader, ArtMethod* referrer, InvokeType type); template ArtMethod* ClassLinker::ResolveMethod( - const DexFile& dex_file, uint32_t method_idx, Handle dex_cache, Handle class_loader, diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 6a5768ec27..55a4d2db15 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -307,14 +307,12 @@ class ClassLinker { ObjPtr class_loader) REQUIRES_SHARED(Locks::mutator_lock_); - // Resolve a method with a given ID from the DexFile, 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 determine if this method is a direct, static, or - // virtual method. + // 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 + // determine if this method is a direct, static, or virtual method. template - ArtMethod* ResolveMethod(const DexFile& dex_file, - uint32_t method_idx, + ArtMethod* ResolveMethod(uint32_t method_idx, Handle dex_cache, Handle class_loader, ArtMethod* referrer, @@ -330,8 +328,7 @@ class ClassLinker { ArtMethod* ResolveMethod(Thread* self, uint32_t method_idx, ArtMethod* referrer, InvokeType type) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - ArtMethod* ResolveMethodWithoutInvokeType(const DexFile& dex_file, - uint32_t method_idx, + ArtMethod* ResolveMethodWithoutInvokeType(uint32_t method_idx, Handle dex_cache, Handle class_loader) REQUIRES_SHARED(Locks::mutator_lock_) diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index eaf31f308f..43260f733b 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -501,7 +501,6 @@ bool ProcessAnnotationValue(const ClassData& klass, ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); StackHandleScope<2> hs(self); ArtMethod* method = class_linker->ResolveMethodWithoutInvokeType( - klass.GetDexFile(), index, hs.NewHandle(klass.GetDexCache()), hs.NewHandle(klass.GetClassLoader())); @@ -1398,7 +1397,6 @@ mirror::Class* GetEnclosingClass(Handle klass) { } StackHandleScope<2> hs(Thread::Current()); ArtMethod* method = Runtime::Current()->GetClassLinker()->ResolveMethodWithoutInvokeType( - data.GetDexFile(), annotation_value.value_.GetI(), hs.NewHandle(data.GetDexCache()), hs.NewHandle(data.GetClassLoader())); diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 24f83b881d..bf02450744 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -230,7 +230,7 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethods(Thread* self, previous_method_idx = method_idx; InvokeType type = it->GetMethodInvokeType(class_def); ArtMethod* method = linker->ResolveMethod( - *dex_file, method_idx, dex_cache, class_loader, nullptr, type); + method_idx, dex_cache, class_loader, /* referrer */ nullptr, type); if (method == nullptr) { DCHECK(self->IsExceptionPending()); // We couldn't resolve the method, but continue regardless. -- GitLab From 14e895ed419fcb67c16be1387d248a4e973cf940 Mon Sep 17 00:00:00 2001 From: Alex Klyubin Date: Wed, 22 Nov 2017 12:29:03 -0800 Subject: [PATCH 159/226] Add patchoat test The test checks that relocating an image using dex2oat and patchoat yields the same ART file, except for OAT checksum and patch offset delta. Test: make test-art-host-gtest-patchoat_test Bug: 66697305 Change-Id: I80f8b996bc8fc88ef798dceac9fd9ecd629045a3 --- build/Android.gtest.mk | 10 ++ patchoat/Android.bp | 13 ++ patchoat/patchoat_test.cc | 347 ++++++++++++++++++++++++++++++++++++++ runtime/image.h | 4 + 4 files changed, 374 insertions(+) create mode 100644 patchoat/patchoat_test.cc diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 3c8eade773..230b2665e6 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -124,6 +124,7 @@ ART_GTEST_image_space_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex ART_GTEST_oat_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 ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods ART_GTEST_profile_assistant_test_DEX_DEPS := ProfileTestMultiDex @@ -251,6 +252,11 @@ ART_GTEST_oatdump_test_TARGET_DEPS := \ 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_patchoat_test_HOST_DEPS := \ + $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) +ART_GTEST_patchoat_test_TARGET_DEPS := \ + $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) + # Profile assistant tests requires profman utility. ART_GTEST_profile_assistant_test_HOST_DEPS := profmand-host ART_GTEST_profile_assistant_test_TARGET_DEPS := profmand-target @@ -270,6 +276,7 @@ ART_TEST_MODULES := \ art_dexoptanalyzer_tests \ art_imgdiag_tests \ art_oatdump_tests \ + art_patchoat_tests \ art_profman_tests \ art_runtime_tests \ art_runtime_compiler_tests \ @@ -683,6 +690,9 @@ ART_GTEST_dex2oat_image_test_DEX_DEPS := ART_GTEST_dex2oat_image_test_HOST_DEPS := ART_GTEST_dex2oat_image_test_TARGET_DEPS := ART_GTEST_object_test_DEX_DEPS := +ART_GTEST_patchoat_test_DEX_DEPS := +ART_GTEST_patchoat_test_HOST_DEPS := +ART_GTEST_patchoat_test_TARGET_DEPS := ART_GTEST_proxy_test_DEX_DEPS := ART_GTEST_reflection_test_DEX_DEPS := ART_GTEST_stub_test_DEX_DEPS := diff --git a/patchoat/Android.bp b/patchoat/Android.bp index d3bc2a754b..0902823644 100644 --- a/patchoat/Android.bp +++ b/patchoat/Android.bp @@ -47,3 +47,16 @@ art_cc_binary { "libartd", ], } + +art_cc_test { + name: "art_patchoat_tests", + defaults: [ + "art_gtest_defaults", + ], + srcs: [ + "patchoat_test.cc", + ], + shared_libs: [ + "libartd", + ], +} diff --git a/patchoat/patchoat_test.cc b/patchoat/patchoat_test.cc new file mode 100644 index 0000000000..2e76c8c518 --- /dev/null +++ b/patchoat/patchoat_test.cc @@ -0,0 +1,347 @@ +/* + * 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. + */ + +#include +#include + +#include "android-base/stringprintf.h" +#include "android-base/strings.h" + +#include "dexopt_test.h" +#include "runtime.h" + +#include + +namespace art { + +using android::base::StringPrintf; + +class PatchoatTest : public DexoptTest { + public: + static void AddRuntimeArg(std::vector& args, const std::string& arg) { + args.push_back("--runtime-arg"); + args.push_back(arg); + } + + bool CompileBootImage(const std::vector& extra_args, + const std::string& image_file_name_prefix, + uint32_t base_addr, + std::string* error_msg) { + Runtime* const runtime = Runtime::Current(); + std::vector argv; + argv.push_back(runtime->GetCompilerExecutable()); + AddRuntimeArg(argv, "-Xms64m"); + AddRuntimeArg(argv, "-Xmx64m"); + std::vector dex_files = GetLibCoreDexFileNames(); + for (const std::string& dex_file : dex_files) { + argv.push_back("--dex-file=" + dex_file); + argv.push_back("--dex-location=" + dex_file); + } + if (runtime->IsJavaDebuggable()) { + argv.push_back("--debuggable"); + } + runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv); + + AddRuntimeArg(argv, "-Xverify:softfail"); + + if (!kIsTargetBuild) { + argv.push_back("--host"); + } + + argv.push_back("--image=" + image_file_name_prefix + ".art"); + argv.push_back("--oat-file=" + image_file_name_prefix + ".oat"); + argv.push_back("--oat-location=" + image_file_name_prefix + ".oat"); + argv.push_back(StringPrintf("--base=0x%" PRIx32, base_addr)); + argv.push_back("--compile-pic"); + argv.push_back("--multi-image"); + argv.push_back("--no-generate-debug-info"); + + std::vector compiler_options = runtime->GetCompilerOptions(); + argv.insert(argv.end(), compiler_options.begin(), compiler_options.end()); + + // We must set --android-root. + const char* android_root = getenv("ANDROID_ROOT"); + CHECK(android_root != nullptr); + argv.push_back("--android-root=" + std::string(android_root)); + argv.insert(argv.end(), extra_args.begin(), extra_args.end()); + + return RunDex2OatOrPatchoat(argv, error_msg); + } + + bool RelocateBootImage(const std::string& input_image_location, + const std::string& output_image_filename, + off_t base_offset_delta, + std::string* error_msg) { + Runtime* const runtime = Runtime::Current(); + std::vector argv; + argv.push_back(runtime->GetPatchoatExecutable()); + argv.push_back("--input-image-location=" + input_image_location); + argv.push_back("--output-image-file=" + output_image_filename); + argv.push_back(StringPrintf("--base-offset-delta=0x%jx", (intmax_t) base_offset_delta)); + argv.push_back(StringPrintf("--instruction-set=%s", GetInstructionSetString(kRuntimeISA))); + + return RunDex2OatOrPatchoat(argv, error_msg); + } + + bool RunDex2OatOrPatchoat(const std::vector& args, std::string* error_msg) { + int link[2]; + + if (pipe(link) == -1) { + return false; + } + + pid_t pid = fork(); + if (pid == -1) { + return false; + } + + if (pid == 0) { + // We need dex2oat to actually log things. + setenv("ANDROID_LOG_TAGS", "*:f", 1); + dup2(link[1], STDERR_FILENO); + close(link[0]); + close(link[1]); + std::vector c_args; + for (const std::string& str : args) { + c_args.push_back(str.c_str()); + } + c_args.push_back(nullptr); + execv(c_args[0], const_cast(c_args.data())); + exit(1); + UNREACHABLE(); + } else { + close(link[1]); + char buffer[128]; + memset(buffer, 0, 128); + ssize_t bytes_read = 0; + + while (TEMP_FAILURE_RETRY(bytes_read = read(link[0], buffer, 128)) > 0) { + *error_msg += std::string(buffer, bytes_read); + } + close(link[0]); + int status = -1; + if (waitpid(pid, &status, 0) != -1) { + return (status == 0); + } + return false; + } + } + + bool CompileBootImageToDir( + const std::string& output_dir, + const std::vector& dex2oat_extra_args, + uint32_t base_addr, + std::string* error_msg) { + return CompileBootImage(dex2oat_extra_args, output_dir + "/boot", base_addr, error_msg); + } + + bool CopyImageChecksumAndSetPatchDelta( + const std::string& src_image_filename, + const std::string& dest_image_filename, + off_t dest_patch_delta, + std::string* error_msg) { + std::unique_ptr src_file(OS::OpenFileForReading(src_image_filename.c_str())); + if (src_file.get() == nullptr) { + *error_msg = StringPrintf("Failed to open source image file %s", src_image_filename.c_str()); + return false; + } + ImageHeader src_header; + if (!src_file->ReadFully(&src_header, sizeof(src_header))) { + *error_msg = StringPrintf("Failed to read source image file %s", src_image_filename.c_str()); + return false; + } + + std::unique_ptr dest_file(OS::OpenFileReadWrite(dest_image_filename.c_str())); + if (dest_file.get() == nullptr) { + *error_msg = + StringPrintf("Failed to open destination image file %s", dest_image_filename.c_str()); + return false; + } + ImageHeader dest_header; + if (!dest_file->ReadFully(&dest_header, sizeof(dest_header))) { + *error_msg = + StringPrintf("Failed to read destination image file %s", dest_image_filename.c_str()); + return false; + } + dest_header.SetOatChecksum(src_header.GetOatChecksum()); + dest_header.SetPatchDelta(dest_patch_delta); + if (!dest_file->ResetOffset()) { + *error_msg = + StringPrintf( + "Failed to seek to start of destination image file %s", dest_image_filename.c_str()); + return false; + } + if (!dest_file->WriteFully(&dest_header, sizeof(dest_header))) { + *error_msg = + StringPrintf("Failed to write to destination image file %s", dest_image_filename.c_str()); + dest_file->Erase(); + return false; + } + if (dest_file->FlushCloseOrErase() != 0) { + *error_msg = + StringPrintf( + "Failed to flush/close destination image file %s", dest_image_filename.c_str()); + return false; + } + + return true; + } + + bool ReadFully( + const std::string& filename, std::vector* contents, std::string* error_msg) { + std::unique_ptr file(OS::OpenFileForReading(filename.c_str())); + if (file.get() == nullptr) { + *error_msg = "Failed to open"; + return false; + } + int64_t size = file->GetLength(); + if (size < 0) { + *error_msg = "Failed to get size"; + return false; + } + contents->resize(size); + if (!file->ReadFully(&(*contents)[0], size)) { + *error_msg = "Failed to read"; + contents->clear(); + return false; + } + return true; + } + + bool BinaryDiff( + const std::string& filename1, const std::string& filename2, std::string* error_msg) { + std::string read_error_msg; + std::vector image1; + if (!ReadFully(filename1, &image1, &read_error_msg)) { + *error_msg = StringPrintf("Failed to read %s: %s", filename1.c_str(), read_error_msg.c_str()); + return true; + } + std::vector image2; + if (!ReadFully(filename2, &image2, &read_error_msg)) { + *error_msg = StringPrintf("Failed to read %s: %s", filename2.c_str(), read_error_msg.c_str()); + return true; + } + if (image1.size() != image2.size()) { + *error_msg = + StringPrintf( + "%s and %s are of different size: %zu vs %zu", + filename1.c_str(), + filename2.c_str(), + image1.size(), + image2.size()); + return true; + } + size_t size = image1.size(); + for (size_t i = 0; i < size; i++) { + if (image1[i] != image2[i]) { + *error_msg = + StringPrintf("%s and %s differ at offset %zu", filename1.c_str(), filename2.c_str(), i); + return true; + } + } + + return false; + } +}; + +TEST_F(PatchoatTest, PatchoatRelocationSameAsDex2oatRelocation) { + // This test check that relocating a boot image using patchoat produces the same result as + // producing the boot image for that relocated base address using dex2oat. To be precise, these + // two files will have two small differences: the OAT checksum and base address. However, this + // test takes this into account. + + // 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; + // Force single-threaded compilation to avoid non-determinism which may be caused by + // multi-threaded compilation. We want the boot image created by this dex2oat run and the run + // below to differ only in its base address. + std::vector dex2oat_extra_args; + dex2oat_extra_args.push_back("-j1"); + std::string error_msg; + if (!CompileBootImageToDir(dex2oat_orig_dir, dex2oat_extra_args, orig_base_addr, &error_msg)) { + FAIL() << "CompileBootImage1 failed: " << error_msg; + } + + // Compile a "relocated" boot image into a random directory using dex2oat. This image is relocated + // in the sense that it uses a different base address. + ScratchFile dex2oat_reloc_scratch; + dex2oat_reloc_scratch.Unlink(); + std::string dex2oat_reloc_dir = dex2oat_reloc_scratch.GetFilename(); + ASSERT_EQ(0, mkdir(dex2oat_reloc_dir.c_str(), 0700)); + const uint32_t reloc_base_addr = 0x70000000; + // if (!CompileBootImageToDir(dex2oat_dir, reloc_base_addr, &error_msg)) { + if (!CompileBootImageToDir(dex2oat_reloc_dir, dex2oat_extra_args, reloc_base_addr, &error_msg)) { + FAIL() << "CompileBootImage2 failed: " << error_msg; + } + const off_t base_addr_delta = reloc_base_addr - orig_base_addr; + + // Relocate the original boot image using patchoat. The image is relocated by the same amount + // as the second/relocated image produced by dex2oat. + ScratchFile patchoat_scratch; + patchoat_scratch.Unlink(); + std::string patchoat_dir = patchoat_scratch.GetFilename(); + ASSERT_EQ(0, mkdir(patchoat_dir.c_str(), 0700)); + 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())); + if (!RelocateBootImage( + dex2oat_orig_dir + "/boot.art", + patchoat_dir + "/boot.art", + base_addr_delta, + &error_msg)) { + FAIL() << "RelocateBootImage failed: " << error_msg; + } + + // dex2oat_reloc_image_filename is the boot image relocated using dex2oat + // patchoat_reloc_image_filename is the boot image relocated using patchoat + std::string dex2oat_reloc_image_filename = dex2oat_reloc_dir + "/boot.art"; + std::string patchoat_reloc_image_filename = dex2oat_orig_dir + "/boot.art"; + std::replace( + patchoat_reloc_image_filename.begin() + 1, patchoat_reloc_image_filename.end(), '/', '@'); + patchoat_reloc_image_filename = + patchoat_dir + + (android::base::StartsWith(patchoat_reloc_image_filename, "/") ? "" : "/") + + patchoat_reloc_image_filename; + + // Patch up the dex2oat-relocated image so that it looks as though it was relocated by patchoat. + // patchoat preserves the OAT checksum header field and sets patch delta header field. + if (!CopyImageChecksumAndSetPatchDelta( + dex2oat_orig_dir + "/boot.art", + dex2oat_reloc_dir + "/boot.art", + base_addr_delta, + &error_msg)) { + FAIL() << "Unable to copy image checksum: " << error_msg; + } + + // Assert that the patchoat-relocated image is identical to the dex2oat-relocated image + if (BinaryDiff(dex2oat_reloc_image_filename, patchoat_reloc_image_filename, &error_msg)) { + FAIL() << "patchoat- and dex2oat-relocated images differ: " << error_msg; + } + + ClearDirectory(dex2oat_orig_dir.c_str(), /*recursive*/ true); + ClearDirectory(dex2oat_reloc_dir.c_str(), /*recursive*/ true); + ClearDirectory(patchoat_dir.c_str(), /*recursive*/ true); + rmdir(dex2oat_orig_dir.c_str()); + rmdir(dex2oat_reloc_dir.c_str()); + rmdir(patchoat_dir.c_str()); +} + +} // namespace art diff --git a/runtime/image.h b/runtime/image.h index 3844186a9b..159a308fb3 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -179,6 +179,10 @@ class PACKED(4) ImageHeader { return patch_delta_; } + void SetPatchDelta(off_t patch_delta) { + patch_delta_ = patch_delta; + } + static std::string GetOatLocationFromImageLocation(const std::string& image) { return GetLocationFromImageLocation(image, "oat"); } -- GitLab From a7f6b8151ee8b8c16a11e148fea1c02ca33dc211 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 11 Dec 2017 13:34:29 -0800 Subject: [PATCH 160/226] Add ART_DEFAULT_COMPACT_DEX_LEVEL If specified, this option changes the default compact dex level used by dex2oat. Defaults to none currently. Bug: 63756964 Test: ART_DEFAULT_COMPACT_DEX_LEVEL=fast mm test-art-host-gtest and verify it fails for now. Test: test/testrunner/testrunner.py --host -j64 and verify it passes Change-Id: Ib252f432b3545297725656a5cca40d1bb57b0ced --- build/Android.common_build.mk | 5 +++++ build/art.go | 3 +++ dex2oat/dex2oat.cc | 2 +- runtime/base/macros.h | 4 ++++ runtime/cdex/compact_dex_level.h | 16 ++++++++++++++++ 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index f5a95fa0cf..08962526dd 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -49,6 +49,11 @@ endif # Enable the read barrier by default. ART_USE_READ_BARRIER ?= true +# Default compact dex level to none. +ifeq ($(ART_DEFAULT_COMPACT_DEX_LEVEL),) +ART_DEFAULT_COMPACT_DEX_LEVEL := none +endif + ART_CPP_EXTENSION := .cc ifndef LIBART_IMG_HOST_BASE_ADDRESS diff --git a/build/art.go b/build/art.go index 5704b43834..3f598da00a 100644 --- a/build/art.go +++ b/build/art.go @@ -66,6 +66,9 @@ func globalFlags(ctx android.BaseContext) ([]string, []string) { "-DART_READ_BARRIER_TYPE_IS_"+barrierType+"=1") } + cdexLevel := envDefault(ctx, "ART_DEFAULT_COMPACT_DEX_LEVEL", "none") + cflags = append(cflags, "-DART_DEFAULT_COMPACT_DEX_LEVEL="+cdexLevel) + // We need larger stack overflow guards for ASAN, as the compiled code will have // larger frame sizes. For simplicity, just use global not-target-specific cflags. // Note: We increase this for both debug and non-debug, as the overflow gap will diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 27bec1d3e1..82331ba31b 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -2814,7 +2814,7 @@ class Dex2Oat FINAL { // Dex files we are compiling, does not include the class path dex files. std::vector dex_files_; std::string no_inline_from_string_; - CompactDexLevel compact_dex_level_ = CompactDexLevel::kCompactDexLevelNone; + CompactDexLevel compact_dex_level_ = kDefaultCompactDexLevel; std::vector> elf_writers_; std::vector> oat_writers_; diff --git a/runtime/base/macros.h b/runtime/base/macros.h index 6cd7d60253..512e5ce651 100644 --- a/runtime/base/macros.h +++ b/runtime/base/macros.h @@ -59,6 +59,10 @@ template ART_FRIEND_TEST(test_set_name, individual_test) #define QUOTE(x) #x #define STRINGIFY(x) QUOTE(x) +// Append tokens after evaluating. +#define APPEND_TOKENS_AFTER_EVAL_2(a, b) a ## b +#define APPEND_TOKENS_AFTER_EVAL(a, b) APPEND_TOKENS_AFTER_EVAL_2(a, b) + #ifndef NDEBUG #define ALWAYS_INLINE #else diff --git a/runtime/cdex/compact_dex_level.h b/runtime/cdex/compact_dex_level.h index b824462bf0..5aec00195d 100644 --- a/runtime/cdex/compact_dex_level.h +++ b/runtime/cdex/compact_dex_level.h @@ -17,6 +17,9 @@ #ifndef ART_RUNTIME_CDEX_COMPACT_DEX_LEVEL_H_ #define ART_RUNTIME_CDEX_COMPACT_DEX_LEVEL_H_ +#include + +#include "base/macros.h" #include "dex_file.h" namespace art { @@ -29,6 +32,19 @@ enum class CompactDexLevel { kCompactDexLevelFast, }; +#ifndef ART_DEFAULT_COMPACT_DEX_LEVEL +#error ART_DEFAULT_COMPACT_DEX_LEVEL not specified. +#else +#define ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_fast CompactDexLevel::kCompactDexLevelFast +#define ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_none CompactDexLevel::kCompactDexLevelNone + +#define ART_DEFAULT_COMPACT_DEX_LEVEL_DEFAULT APPEND_TOKENS_AFTER_EVAL( \ + ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_, \ + ART_DEFAULT_COMPACT_DEX_LEVEL) + +static constexpr CompactDexLevel kDefaultCompactDexLevel = ART_DEFAULT_COMPACT_DEX_LEVEL_DEFAULT; +#endif + } // namespace art #endif // ART_RUNTIME_CDEX_COMPACT_DEX_LEVEL_H_ -- GitLab From 31f4c9f86522061d682fd0e2c6003043cec496dc Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 8 Dec 2017 15:46:11 -0800 Subject: [PATCH 161/226] Add CodeItemDebugInfoAccessor Use it in places where DecodeDebugPositionInfo is called. Motivation: Abstract away calls to GetDebugInfoOffset. Bug: 63756964 Test: test-art-host Test: art/tools/run-jdwp-tests.sh '--mode=host' --debug Change-Id: I3ab2eff56c472cc717f49d17fd17eb0b8fde4062 --- compiler/debug/elf_debug_info_writer.h | 34 ++++++++------- compiler/debug/elf_debug_line_writer.h | 6 +-- compiler/optimizing/instruction_builder.cc | 12 +++--- dex2oat/linker/oat_writer.cc | 3 +- dexdump/dexdump.cc | 12 ++++-- dexlist/dexlist.cc | 2 +- openjdkjvmti/ti_method.cc | 44 +++++++++++-------- runtime/code_item_accessors-inl.h | 28 ++++++++++++- runtime/code_item_accessors.h | 24 +++++++++++ runtime/debugger.cc | 49 ++++++++++++---------- runtime/dex_file-inl.h | 21 +++++----- runtime/dex_file.h | 7 ++-- runtime/dex_file_annotations.cc | 8 ++-- runtime/dex_file_test.cc | 9 +++- 14 files changed, 167 insertions(+), 92 deletions(-) diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index d5999941d7..81a0a69bfa 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -22,6 +22,7 @@ #include #include "art_field-inl.h" +#include "code_item_accessors-inl.h" #include "debug/dwarf/debug_abbrev_writer.h" #include "debug/dwarf/debug_info_entry_writer.h" #include "debug/elf_compilation_unit.h" @@ -48,10 +49,10 @@ static void LocalInfoCallback(void* ctx, const DexFile::LocalInfo& entry) { static std::vector GetParamNames(const MethodDebugInfo* mi) { std::vector names; - if (mi->code_item != nullptr) { + CodeItemDebugInfoAccessor accessor(mi->dex_file, mi->code_item); + if (accessor.HasCodeItem()) { DCHECK(mi->dex_file != nullptr); - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*mi->dex_file, mi->code_item); - const uint8_t* stream = mi->dex_file->GetDebugInfoStream(debug_info_offset); + const uint8_t* stream = mi->dex_file->GetDebugInfoStream(accessor.DebugInfoOffset()); if (stream != nullptr) { DecodeUnsignedLeb128(&stream); // line. uint32_t parameters_size = DecodeUnsignedLeb128(&stream); @@ -162,7 +163,7 @@ class ElfCompilationUnitWriter { for (auto mi : compilation_unit.methods) { DCHECK(mi->dex_file != nullptr); const DexFile* dex = mi->dex_file; - const DexFile::CodeItem* dex_code = mi->code_item; + CodeItemDebugInfoAccessor accessor(dex, mi->code_item); const DexFile::MethodId& dex_method = dex->GetMethodId(mi->dex_method_index); const DexFile::ProtoId& dex_proto = dex->GetMethodPrototype(dex_method); const DexFile::TypeList* dex_params = dex->GetProtoParameters(dex_proto); @@ -204,13 +205,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::vector dex_reg_maps; - if (dex_code != nullptr && mi->code_info != nullptr) { + 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); dex_reg_maps.push_back(code_info.GetDexRegisterMapOf( - stack_map, encoding, dex_code->registers_size_)); + stack_map, encoding, accessor.RegistersSize())); } } @@ -224,9 +225,9 @@ class ElfCompilationUnitWriter { WriteName("this"); info_.WriteFlagPresent(DW_AT_artificial); WriteLazyType(dex_class_desc); - if (dex_code != nullptr) { + if (accessor.HasCodeItem()) { // Write the stack location of the parameter. - const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg; + const uint32_t vreg = accessor.RegistersSize() - accessor.InsSize() + arg_reg; const bool is64bitValue = false; WriteRegLocation(mi, dex_reg_maps, vreg, is64bitValue, compilation_unit.code_address); } @@ -244,30 +245,31 @@ class ElfCompilationUnitWriter { const char* type_desc = dex->StringByTypeIdx(dex_params->GetTypeItem(i).type_idx_); WriteLazyType(type_desc); const bool is64bitValue = type_desc[0] == 'D' || type_desc[0] == 'J'; - if (dex_code != nullptr) { + if (accessor.HasCodeItem()) { // Write the stack location of the parameter. - const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg; + const uint32_t vreg = accessor.RegistersSize() - accessor.InsSize() + arg_reg; WriteRegLocation(mi, dex_reg_maps, vreg, is64bitValue, compilation_unit.code_address); } arg_reg += is64bitValue ? 2 : 1; info_.EndTag(); } - if (dex_code != nullptr) { - DCHECK_EQ(arg_reg, dex_code->ins_size_); + if (accessor.HasCodeItem()) { + DCHECK_EQ(arg_reg, accessor.InsSize()); } } // Write local variables. LocalInfos local_infos; - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex, dex_code); - if (dex->DecodeDebugLocalInfo(dex_code, - debug_info_offset, + if (dex->DecodeDebugLocalInfo(accessor.RegistersSize(), + accessor.InsSize(), + accessor.InsnsSizeInCodeUnits(), + accessor.DebugInfoOffset(), is_static, mi->dex_method_index, LocalInfoCallback, &local_infos)) { for (const DexFile::LocalInfo& var : local_infos) { - if (var.reg_ < dex_code->registers_size_ - dex_code->ins_size_) { + if (var.reg_ < accessor.RegistersSize() - accessor.InsSize()) { info_.StartTag(DW_TAG_variable); WriteName(var.name_); WriteLazyType(var.descriptor_); diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index 943e03a765..c7224fc94a 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -159,9 +159,9 @@ class ElfDebugLineWriter { PositionInfos dex2line_map; DCHECK(mi->dex_file != nullptr); const DexFile* dex = mi->dex_file; - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex, mi->code_item); - if (!dex->DecodeDebugPositionInfo( - mi->code_item, debug_info_offset, PositionInfoCallback, &dex2line_map)) { + CodeItemDebugInfoAccessor accessor(dex, mi->code_item); + const uint32_t debug_info_offset = accessor.DebugInfoOffset(); + if (!dex->DecodeDebugPositionInfo(debug_info_offset, PositionInfoCallback, &dex2line_map)) { continue; } diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 782546c9d8..fc7b28c00f 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -442,17 +442,15 @@ ArenaBitVector* HInstructionBuilder::FindNativeDebugInfoLocations() { return false; } }; - const uint32_t num_instructions = code_item_->insns_size_in_code_units_; + CodeItemDebugInfoAccessor accessor(dex_file_, code_item_); ArenaBitVector* locations = ArenaBitVector::Create(local_allocator_, - num_instructions, + accessor.InsnsSizeInCodeUnits(), /* expandable */ false, kArenaAllocGraphBuilder); locations->ClearAllBits(); - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex_file_, code_item_); - dex_file_->DecodeDebugPositionInfo(code_item_, debug_info_offset, Callback::Position, locations); + dex_file_->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), Callback::Position, locations); // Instruction-specific tweaks. - IterationRange instructions = code_item_->Instructions(); - for (const DexInstructionPcPair& inst : instructions) { + for (const DexInstructionPcPair& inst : accessor) { switch (inst->Opcode()) { case Instruction::MOVE_EXCEPTION: { // Stop in native debugger after the exception has been moved. @@ -461,7 +459,7 @@ ArenaBitVector* HInstructionBuilder::FindNativeDebugInfoLocations() { locations->ClearBit(inst.DexPc()); DexInstructionIterator next = std::next(DexInstructionIterator(inst)); DCHECK(next.DexPc() != inst.DexPc()); - if (next != instructions.end()) { + if (next != accessor.end()) { locations->SetBit(next.DexPc()); } break; diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index b5c5d98c11..260888e818 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -2694,7 +2694,8 @@ class OatWriter::WriteQuickeningIndicesMethodVisitor { CompiledMethod* compiled_method = driver.GetCompiledMethod(MethodReference(dex_file, method_idx)); const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem(); - uint32_t existing_debug_info_offset = OatFile::GetDebugInfoOffset(*dex_file, code_item); + CodeItemDebugInfoAccessor accessor(dex_file, code_item); + const uint32_t existing_debug_info_offset = accessor.DebugInfoOffset(); // If the existing offset is already out of bounds (and not magic marker 0xFFFFFFFF) // we will pretend the method has been quickened. bool existing_offset_out_of_bounds = diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index a7af193f0a..527a5b993c 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -1203,10 +1203,16 @@ static void dumpCode(const DexFile* pDexFile, u4 idx, u4 flags, bool is_static = (flags & kAccStatic) != 0; fprintf(gOutFile, " positions : \n"); uint32_t debug_info_offset = pDexFile->GetDebugInfoOffset(pCode); - pDexFile->DecodeDebugPositionInfo(pCode, debug_info_offset, dumpPositionsCb, nullptr); + pDexFile->DecodeDebugPositionInfo(debug_info_offset, dumpPositionsCb, nullptr); fprintf(gOutFile, " locals : \n"); - pDexFile->DecodeDebugLocalInfo( - pCode, debug_info_offset, is_static, idx, dumpLocalsCb, nullptr); + pDexFile->DecodeDebugLocalInfo(pCode->registers_size_, + pCode->ins_size_, + pCode->insns_size_in_code_units_, + debug_info_offset, + is_static, + idx, + dumpLocalsCb, + nullptr); } /* diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc index 3bd903de5b..4bf0475494 100644 --- a/dexlist/dexlist.cc +++ b/dexlist/dexlist.cc @@ -121,7 +121,7 @@ static void dumpMethod(const DexFile* pDexFile, // Find the first line. int firstLine = -1; uint32_t debug_info_offset = pDexFile->GetDebugInfoOffset(pCode); - pDexFile->DecodeDebugPositionInfo(pCode, debug_info_offset, positionsCb, &firstLine); + pDexFile->DecodeDebugPositionInfo(debug_info_offset, positionsCb, &firstLine); // Method signature. const Signature signature = pDexFile->GetMethodSignature(pMethodId); diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index 448ce41d0f..4444853427 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -37,6 +37,7 @@ #include "art_method-inl.h" #include "base/enums.h" #include "base/mutex-inl.h" +#include "code_item_accessors-inl.h" #include "dex_file_annotations.h" #include "dex_file_types.h" #include "events-inl.h" @@ -190,12 +191,17 @@ jvmtiError MethodUtil::GetLocalVariableTable(jvmtiEnv* env, } art::ScopedObjectAccess soa(art::Thread::Current()); - const art::DexFile* dex_file = art_method->GetDexFile(); - const art::DexFile::CodeItem* code_item = art_method->GetCodeItem(); - // TODO code_item == nullptr means that the method is abstract (or native, but we check that + + const art::DexFile* const dex_file = art_method->GetDexFile(); + if (dex_file == nullptr) { + return ERR(ABSENT_INFORMATION); + } + + // TODO HasCodeItem == false means that the method is abstract (or native, but we check that // earlier). We should check what is returned by the RI in this situation since it's not clear // what the appropriate return value is from the spec. - if (dex_file == nullptr || code_item == nullptr) { + art::CodeItemDebugInfoAccessor accessor(art_method); + if (!accessor.HasCodeItem()) { return ERR(ABSENT_INFORMATION); } @@ -260,9 +266,10 @@ jvmtiError MethodUtil::GetLocalVariableTable(jvmtiEnv* env, }; LocalVariableContext context(env); - uint32_t debug_info_offset = art::OatFile::GetDebugInfoOffset(*dex_file, code_item); - if (!dex_file->DecodeDebugLocalInfo(code_item, - debug_info_offset, + if (!dex_file->DecodeDebugLocalInfo(accessor.RegistersSize(), + accessor.InsSize(), + accessor.InsnsSizeInCodeUnits(), + accessor.DebugInfoOffset(), art_method->IsStatic(), art_method->GetDexMethodIndex(), LocalVariableContext::Callback, @@ -462,7 +469,7 @@ jvmtiError MethodUtil::GetLineNumberTable(jvmtiEnv* env, art::ArtMethod* art_method = art::jni::DecodeArtMethod(method); DCHECK(!art_method->IsRuntimeMethod()); - const art::DexFile::CodeItem* code_item; + art::CodeItemDebugInfoAccessor accessor; const art::DexFile* dex_file; { art::ScopedObjectAccess soa(art::Thread::Current()); @@ -477,15 +484,14 @@ jvmtiError MethodUtil::GetLineNumberTable(jvmtiEnv* env, return ERR(NULL_POINTER); } - code_item = art_method->GetCodeItem(); + accessor = art::CodeItemDebugInfoAccessor(art_method); dex_file = art_method->GetDexFile(); - DCHECK(code_item != nullptr) << art_method->PrettyMethod() << " " << dex_file->GetLocation(); + DCHECK(accessor.HasCodeItem()) << art_method->PrettyMethod() << " " << dex_file->GetLocation(); } LineNumberContext context; - uint32_t debug_info_offset = art::OatFile::GetDebugInfoOffset(*dex_file, code_item); bool success = dex_file->DecodeDebugPositionInfo( - code_item, debug_info_offset, CollectLineNumbers, &context); + accessor.DebugInfoOffset(), CollectLineNumbers, &context); if (!success) { return ERR(ABSENT_INFORMATION); } @@ -613,8 +619,11 @@ class CommonLocalVariableClosure : public art::Closure { /*out*/art::Primitive::Type* type) REQUIRES(art::Locks::mutator_lock_) { const art::DexFile* dex_file = method->GetDexFile(); - const art::DexFile::CodeItem* code_item = method->GetCodeItem(); - if (dex_file == nullptr || code_item == nullptr) { + if (dex_file == nullptr) { + return ERR(OPAQUE_FRAME); + } + art::CodeItemDebugInfoAccessor accessor(method); + if (!accessor.HasCodeItem()) { return ERR(OPAQUE_FRAME); } @@ -653,9 +662,10 @@ class CommonLocalVariableClosure : public art::Closure { }; GetLocalVariableInfoContext context(slot_, dex_pc, descriptor, type); - uint32_t debug_info_offset = art::OatFile::GetDebugInfoOffset(*dex_file, code_item); - if (!dex_file->DecodeDebugLocalInfo(code_item, - debug_info_offset, + if (!dex_file->DecodeDebugLocalInfo(accessor.RegistersSize(), + accessor.InsSize(), + accessor.InsnsSizeInCodeUnits(), + accessor.DebugInfoOffset(), method->IsStatic(), method->GetDexMethodIndex(), GetLocalVariableInfoContext::Callback, diff --git a/runtime/code_item_accessors-inl.h b/runtime/code_item_accessors-inl.h index d04849d09a..4f4d8cc86e 100644 --- a/runtime/code_item_accessors-inl.h +++ b/runtime/code_item_accessors-inl.h @@ -22,6 +22,7 @@ #include "art_method-inl.h" #include "cdex/compact_dex_file.h" #include "dex_file-inl.h" +#include "oat_file.h" #include "standard_dex_file.h" namespace art { @@ -37,7 +38,7 @@ inline void CodeItemInstructionAccessor::Init(const StandardDexFile::CodeItem& c } inline void CodeItemInstructionAccessor::Init(const DexFile* dex_file, - const DexFile::CodeItem* code_item) { + const DexFile::CodeItem* code_item) { DCHECK(dex_file != nullptr); DCHECK(code_item != nullptr); if (dex_file->IsCompactDexFile()) { @@ -150,6 +151,31 @@ inline const DexFile::TryItem* CodeItemDataAccessor::FindTryItem(uint32_t try_de return index != -1 ? &try_items.begin()[index] : nullptr; } +inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(ArtMethod* method) + : CodeItemDebugInfoAccessor(method->GetDexFile(), method->GetCodeItem()) {} + +inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(const DexFile* dex_file, + const DexFile::CodeItem* code_item) { + if (code_item == nullptr) { + return; + } + debug_info_offset_ = OatFile::GetDebugInfoOffset(*dex_file, code_item); + if (dex_file->IsCompactDexFile()) { + Init(down_cast(*code_item)); + } else { + DCHECK(dex_file->IsStandardDexFile()); + Init(down_cast(*code_item)); + } +} + +inline void CodeItemDebugInfoAccessor::Init(const CompactDexFile::CodeItem& code_item) { + CodeItemDataAccessor::Init(code_item); +} + +inline void CodeItemDebugInfoAccessor::Init(const StandardDexFile::CodeItem& code_item) { + CodeItemDataAccessor::Init(code_item); +} + } // namespace art #endif // ART_RUNTIME_CODE_ITEM_ACCESSORS_INL_H_ diff --git a/runtime/code_item_accessors.h b/runtime/code_item_accessors.h index aa1305acad..a089a276de 100644 --- a/runtime/code_item_accessors.h +++ b/runtime/code_item_accessors.h @@ -132,6 +132,30 @@ class CodeItemDataAccessor : public CodeItemInstructionAccessor { uint16_t tries_size_; }; +// Abstract accesses to code item data including debug info offset. More heavy weight than the other +// helpers. +class CodeItemDebugInfoAccessor : public CodeItemDataAccessor { + public: + CodeItemDebugInfoAccessor() = default; + + // Handles null code items, but not null dex files. + ALWAYS_INLINE CodeItemDebugInfoAccessor(const DexFile* dex_file, + const DexFile::CodeItem* code_item); + + ALWAYS_INLINE explicit CodeItemDebugInfoAccessor(ArtMethod* method); + + uint32_t DebugInfoOffset() const { + return debug_info_offset_; + } + + protected: + ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item); + ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item); + + private: + uint32_t debug_info_offset_ = 0u; +}; + } // namespace art #endif // ART_RUNTIME_CODE_ITEM_ACCESSORS_H_ diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 13029fb958..541bd1da81 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -1661,16 +1661,16 @@ void Dbg::OutputLineTable(JDWP::RefTypeId, JDWP::MethodId method_id, JDWP::Expan } }; ArtMethod* m = FromMethodId(method_id); - const DexFile::CodeItem* code_item = m->GetCodeItem(); + CodeItemDebugInfoAccessor accessor(m); uint64_t start, end; - if (code_item == nullptr) { + if (!accessor.HasCodeItem()) { DCHECK(m->IsNative() || m->IsProxyMethod()); start = -1; end = -1; } else { start = 0; // Return the index of the last instruction - end = code_item->insns_size_in_code_units_ - 1; + end = accessor.InsnsSizeInCodeUnits() - 1; } expandBufAdd8BE(pReply, start); @@ -1684,10 +1684,10 @@ void Dbg::OutputLineTable(JDWP::RefTypeId, JDWP::MethodId method_id, JDWP::Expan context.numItems = 0; context.pReply = pReply; - if (code_item != nullptr) { - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*(m->GetDexFile()), code_item); - m->GetDexFile()->DecodeDebugPositionInfo( - code_item, debug_info_offset, DebugCallbackContext::Callback, &context); + if (accessor.HasCodeItem()) { + m->GetDexFile()->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), + DebugCallbackContext::Callback, + &context); } JDWP::Set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems); @@ -1727,6 +1727,7 @@ void Dbg::OutputVariableTable(JDWP::RefTypeId, JDWP::MethodId method_id, bool wi } }; ArtMethod* m = FromMethodId(method_id); + CodeItemDebugInfoAccessor accessor(m); // arg_count considers doubles and longs to take 2 units. // variable_count considers everything to take 1 unit. @@ -1742,12 +1743,15 @@ void Dbg::OutputVariableTable(JDWP::RefTypeId, JDWP::MethodId method_id, bool wi context.variable_count = 0; context.with_generic = with_generic; - const DexFile::CodeItem* code_item = m->GetCodeItem(); - if (code_item != nullptr) { - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*(m->GetDexFile()), code_item); - m->GetDexFile()->DecodeDebugLocalInfo( - code_item, debug_info_offset, m->IsStatic(), m->GetDexMethodIndex(), - DebugCallbackContext::Callback, &context); + if (accessor.HasCodeItem()) { + m->GetDexFile()->DecodeDebugLocalInfo(accessor.RegistersSize(), + accessor.InsSize(), + accessor.InsnsSizeInCodeUnits(), + accessor.DebugInfoOffset(), + m->IsStatic(), + m->GetDexMethodIndex(), + DebugCallbackContext::Callback, + &context); } JDWP::Set4BE(expandBufGetBuffer(pReply) + variable_count_offset, context.variable_count); @@ -3836,9 +3840,9 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize // Find the dex_pc values that correspond to the current line, for line-based single-stepping. struct DebugCallbackContext { DebugCallbackContext(SingleStepControl* single_step_control_cb, - int32_t line_number_cb, const DexFile::CodeItem* code_item) + int32_t line_number_cb, uint32_t num_insns_in_code_units) : single_step_control_(single_step_control_cb), line_number_(line_number_cb), - code_item_(code_item), last_pc_valid(false), last_pc(0) { + num_insns_in_code_units_(num_insns_in_code_units), last_pc_valid(false), last_pc(0) { } static bool Callback(void* raw_context, const DexFile::PositionInfo& entry) { @@ -3864,8 +3868,7 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize ~DebugCallbackContext() { // If the line number was the last in the position table... if (last_pc_valid) { - size_t end = code_item_->insns_size_in_code_units_; - for (uint32_t dex_pc = last_pc; dex_pc < end; ++dex_pc) { + for (uint32_t dex_pc = last_pc; dex_pc < num_insns_in_code_units_; ++dex_pc) { single_step_control_->AddDexPc(dex_pc); } } @@ -3873,7 +3876,7 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize SingleStepControl* const single_step_control_; const int32_t line_number_; - const DexFile::CodeItem* const code_item_; + const uint32_t num_insns_in_code_units_; bool last_pc_valid; uint32_t last_pc; }; @@ -3892,11 +3895,11 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize // Note: if the thread is not running Java code (pure native thread), there is no "current" // method on the stack (and no line number either). if (m != nullptr && !m->IsNative()) { - const DexFile::CodeItem* const code_item = m->GetCodeItem(); - DebugCallbackContext context(single_step_control, line_number, code_item); - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*(m->GetDexFile()), code_item); - m->GetDexFile()->DecodeDebugPositionInfo( - code_item, debug_info_offset, DebugCallbackContext::Callback, &context); + CodeItemDebugInfoAccessor accessor(m); + DebugCallbackContext context(single_step_control, line_number, accessor.InsnsSizeInCodeUnits()); + m->GetDexFile()->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), + DebugCallbackContext::Callback, + &context); } // Activate single-step in the thread. diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h index 18809683cc..1c76a32f61 100644 --- a/runtime/dex_file-inl.h +++ b/runtime/dex_file-inl.h @@ -385,13 +385,16 @@ bool DexFile::DecodeDebugLocalInfo(const uint8_t* stream, } template -bool DexFile::DecodeDebugLocalInfo(const CodeItem* code_item, +bool DexFile::DecodeDebugLocalInfo(uint32_t registers_size, + uint32_t ins_size, + uint32_t insns_size_in_code_units, uint32_t debug_info_offset, bool is_static, uint32_t method_idx, NewLocalCallback new_local_callback, void* context) const { - if (code_item == nullptr) { + const uint8_t* const stream = GetDebugInfoStream(debug_info_offset); + if (stream == nullptr) { return false; } std::vector arg_descriptors; @@ -399,15 +402,15 @@ bool DexFile::DecodeDebugLocalInfo(const CodeItem* code_item, for (; it.HasNext(); it.Next()) { arg_descriptors.push_back(it.GetDescriptor()); } - return DecodeDebugLocalInfo(GetDebugInfoStream(debug_info_offset), + return DecodeDebugLocalInfo(stream, GetLocation(), GetMethodDeclaringClassDescriptor(GetMethodId(method_idx)), arg_descriptors, this->PrettyMethod(method_idx), is_static, - code_item->registers_size_, - code_item->ins_size_, - code_item->insns_size_in_code_units_, + registers_size, + ins_size, + insns_size_in_code_units, [this](uint32_t idx) { return StringDataByIdx(dex::StringIndex(idx)); }, @@ -488,13 +491,9 @@ bool DexFile::DecodeDebugPositionInfo(const uint8_t* stream, } template -bool DexFile::DecodeDebugPositionInfo(const CodeItem* code_item, - uint32_t debug_info_offset, +bool DexFile::DecodeDebugPositionInfo(uint32_t debug_info_offset, DexDebugNewPosition position_functor, void* context) const { - if (code_item == nullptr) { - return false; - } return DecodeDebugPositionInfo(GetDebugInfoStream(debug_info_offset), [this](uint32_t idx) { return StringDataByIdx(dex::StringIndex(idx)); diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 944a30849f..91ec630f0c 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -954,7 +954,9 @@ class DexFile { NewLocalCallback new_local, void* context); template - bool DecodeDebugLocalInfo(const CodeItem* code_item, + bool DecodeDebugLocalInfo(uint32_t registers_size, + uint32_t ins_size, + uint32_t insns_size_in_code_units, uint32_t debug_info_offset, bool is_static, uint32_t method_idx, @@ -968,8 +970,7 @@ class DexFile { DexDebugNewPosition position_functor, void* context); template - bool DecodeDebugPositionInfo(const CodeItem* code_item, - uint32_t debug_info_offset, + bool DecodeDebugPositionInfo(uint32_t debug_info_offset, DexDebugNewPosition position_functor, void* context) const; diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index b44bd51643..837291b023 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -1567,14 +1567,12 @@ int32_t GetLineNumFromPC(const DexFile* dex_file, ArtMethod* method, uint32_t re return -2; } - const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset()); - DCHECK(code_item != nullptr) << method->PrettyMethod() << " " << dex_file->GetLocation(); + CodeItemDebugInfoAccessor accessor(method); + DCHECK(accessor.HasCodeItem()) << method->PrettyMethod() << " " << dex_file->GetLocation(); // A method with no line number info should return -1 DexFile::LineNumFromPcContext context(rel_pc, -1); - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex_file, code_item); - dex_file->DecodeDebugPositionInfo( - code_item, debug_info_offset, DexFile::LineNumForPcCb, &context); + dex_file->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), DexFile::LineNumForPcCb, &context); return context.line_num_; } diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index 14c36b4538..69055041e5 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -731,7 +731,14 @@ TEST_F(DexFileTest, OpenDexDebugInfoLocalNullType) { const DexFile::ClassDef& class_def = raw->GetClassDef(0); const DexFile::CodeItem* code_item = raw->GetCodeItem(raw->FindCodeItemOffset(class_def, 1)); uint32_t debug_info_offset = raw->GetDebugInfoOffset(code_item); - ASSERT_TRUE(raw->DecodeDebugLocalInfo(code_item, debug_info_offset, true, 1, Callback, nullptr)); + ASSERT_TRUE(raw->DecodeDebugLocalInfo(code_item->registers_size_, + code_item->ins_size_, + code_item->insns_size_in_code_units_, + debug_info_offset, + true, + 1, + Callback, + nullptr)); } } // namespace art -- GitLab From 5dd08acd0b568bb05e2e75fc02d8a6d3d7aa6f8e Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Tue, 12 Dec 2017 08:54:59 +0000 Subject: [PATCH 162/226] Revert "Add patchoat test" Bug: 66697305 Fails on device This reverts commit 14e895ed419fcb67c16be1387d248a4e973cf940. Change-Id: Id817d738be32f44c5dfc97d2646b0090824dc404 --- build/Android.gtest.mk | 10 -- patchoat/Android.bp | 13 -- patchoat/patchoat_test.cc | 347 -------------------------------------- runtime/image.h | 4 - 4 files changed, 374 deletions(-) delete mode 100644 patchoat/patchoat_test.cc diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 230b2665e6..3c8eade773 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -124,7 +124,6 @@ ART_GTEST_image_space_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex ART_GTEST_oat_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 ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods ART_GTEST_profile_assistant_test_DEX_DEPS := ProfileTestMultiDex @@ -252,11 +251,6 @@ ART_GTEST_oatdump_test_TARGET_DEPS := \ 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_patchoat_test_HOST_DEPS := \ - $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) -ART_GTEST_patchoat_test_TARGET_DEPS := \ - $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) - # Profile assistant tests requires profman utility. ART_GTEST_profile_assistant_test_HOST_DEPS := profmand-host ART_GTEST_profile_assistant_test_TARGET_DEPS := profmand-target @@ -276,7 +270,6 @@ ART_TEST_MODULES := \ art_dexoptanalyzer_tests \ art_imgdiag_tests \ art_oatdump_tests \ - art_patchoat_tests \ art_profman_tests \ art_runtime_tests \ art_runtime_compiler_tests \ @@ -690,9 +683,6 @@ ART_GTEST_dex2oat_image_test_DEX_DEPS := ART_GTEST_dex2oat_image_test_HOST_DEPS := ART_GTEST_dex2oat_image_test_TARGET_DEPS := ART_GTEST_object_test_DEX_DEPS := -ART_GTEST_patchoat_test_DEX_DEPS := -ART_GTEST_patchoat_test_HOST_DEPS := -ART_GTEST_patchoat_test_TARGET_DEPS := ART_GTEST_proxy_test_DEX_DEPS := ART_GTEST_reflection_test_DEX_DEPS := ART_GTEST_stub_test_DEX_DEPS := diff --git a/patchoat/Android.bp b/patchoat/Android.bp index 0902823644..d3bc2a754b 100644 --- a/patchoat/Android.bp +++ b/patchoat/Android.bp @@ -47,16 +47,3 @@ art_cc_binary { "libartd", ], } - -art_cc_test { - name: "art_patchoat_tests", - defaults: [ - "art_gtest_defaults", - ], - srcs: [ - "patchoat_test.cc", - ], - shared_libs: [ - "libartd", - ], -} diff --git a/patchoat/patchoat_test.cc b/patchoat/patchoat_test.cc deleted file mode 100644 index 2e76c8c518..0000000000 --- a/patchoat/patchoat_test.cc +++ /dev/null @@ -1,347 +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. - */ - -#include -#include - -#include "android-base/stringprintf.h" -#include "android-base/strings.h" - -#include "dexopt_test.h" -#include "runtime.h" - -#include - -namespace art { - -using android::base::StringPrintf; - -class PatchoatTest : public DexoptTest { - public: - static void AddRuntimeArg(std::vector& args, const std::string& arg) { - args.push_back("--runtime-arg"); - args.push_back(arg); - } - - bool CompileBootImage(const std::vector& extra_args, - const std::string& image_file_name_prefix, - uint32_t base_addr, - std::string* error_msg) { - Runtime* const runtime = Runtime::Current(); - std::vector argv; - argv.push_back(runtime->GetCompilerExecutable()); - AddRuntimeArg(argv, "-Xms64m"); - AddRuntimeArg(argv, "-Xmx64m"); - std::vector dex_files = GetLibCoreDexFileNames(); - for (const std::string& dex_file : dex_files) { - argv.push_back("--dex-file=" + dex_file); - argv.push_back("--dex-location=" + dex_file); - } - if (runtime->IsJavaDebuggable()) { - argv.push_back("--debuggable"); - } - runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv); - - AddRuntimeArg(argv, "-Xverify:softfail"); - - if (!kIsTargetBuild) { - argv.push_back("--host"); - } - - argv.push_back("--image=" + image_file_name_prefix + ".art"); - argv.push_back("--oat-file=" + image_file_name_prefix + ".oat"); - argv.push_back("--oat-location=" + image_file_name_prefix + ".oat"); - argv.push_back(StringPrintf("--base=0x%" PRIx32, base_addr)); - argv.push_back("--compile-pic"); - argv.push_back("--multi-image"); - argv.push_back("--no-generate-debug-info"); - - std::vector compiler_options = runtime->GetCompilerOptions(); - argv.insert(argv.end(), compiler_options.begin(), compiler_options.end()); - - // We must set --android-root. - const char* android_root = getenv("ANDROID_ROOT"); - CHECK(android_root != nullptr); - argv.push_back("--android-root=" + std::string(android_root)); - argv.insert(argv.end(), extra_args.begin(), extra_args.end()); - - return RunDex2OatOrPatchoat(argv, error_msg); - } - - bool RelocateBootImage(const std::string& input_image_location, - const std::string& output_image_filename, - off_t base_offset_delta, - std::string* error_msg) { - Runtime* const runtime = Runtime::Current(); - std::vector argv; - argv.push_back(runtime->GetPatchoatExecutable()); - argv.push_back("--input-image-location=" + input_image_location); - argv.push_back("--output-image-file=" + output_image_filename); - argv.push_back(StringPrintf("--base-offset-delta=0x%jx", (intmax_t) base_offset_delta)); - argv.push_back(StringPrintf("--instruction-set=%s", GetInstructionSetString(kRuntimeISA))); - - return RunDex2OatOrPatchoat(argv, error_msg); - } - - bool RunDex2OatOrPatchoat(const std::vector& args, std::string* error_msg) { - int link[2]; - - if (pipe(link) == -1) { - return false; - } - - pid_t pid = fork(); - if (pid == -1) { - return false; - } - - if (pid == 0) { - // We need dex2oat to actually log things. - setenv("ANDROID_LOG_TAGS", "*:f", 1); - dup2(link[1], STDERR_FILENO); - close(link[0]); - close(link[1]); - std::vector c_args; - for (const std::string& str : args) { - c_args.push_back(str.c_str()); - } - c_args.push_back(nullptr); - execv(c_args[0], const_cast(c_args.data())); - exit(1); - UNREACHABLE(); - } else { - close(link[1]); - char buffer[128]; - memset(buffer, 0, 128); - ssize_t bytes_read = 0; - - while (TEMP_FAILURE_RETRY(bytes_read = read(link[0], buffer, 128)) > 0) { - *error_msg += std::string(buffer, bytes_read); - } - close(link[0]); - int status = -1; - if (waitpid(pid, &status, 0) != -1) { - return (status == 0); - } - return false; - } - } - - bool CompileBootImageToDir( - const std::string& output_dir, - const std::vector& dex2oat_extra_args, - uint32_t base_addr, - std::string* error_msg) { - return CompileBootImage(dex2oat_extra_args, output_dir + "/boot", base_addr, error_msg); - } - - bool CopyImageChecksumAndSetPatchDelta( - const std::string& src_image_filename, - const std::string& dest_image_filename, - off_t dest_patch_delta, - std::string* error_msg) { - std::unique_ptr src_file(OS::OpenFileForReading(src_image_filename.c_str())); - if (src_file.get() == nullptr) { - *error_msg = StringPrintf("Failed to open source image file %s", src_image_filename.c_str()); - return false; - } - ImageHeader src_header; - if (!src_file->ReadFully(&src_header, sizeof(src_header))) { - *error_msg = StringPrintf("Failed to read source image file %s", src_image_filename.c_str()); - return false; - } - - std::unique_ptr dest_file(OS::OpenFileReadWrite(dest_image_filename.c_str())); - if (dest_file.get() == nullptr) { - *error_msg = - StringPrintf("Failed to open destination image file %s", dest_image_filename.c_str()); - return false; - } - ImageHeader dest_header; - if (!dest_file->ReadFully(&dest_header, sizeof(dest_header))) { - *error_msg = - StringPrintf("Failed to read destination image file %s", dest_image_filename.c_str()); - return false; - } - dest_header.SetOatChecksum(src_header.GetOatChecksum()); - dest_header.SetPatchDelta(dest_patch_delta); - if (!dest_file->ResetOffset()) { - *error_msg = - StringPrintf( - "Failed to seek to start of destination image file %s", dest_image_filename.c_str()); - return false; - } - if (!dest_file->WriteFully(&dest_header, sizeof(dest_header))) { - *error_msg = - StringPrintf("Failed to write to destination image file %s", dest_image_filename.c_str()); - dest_file->Erase(); - return false; - } - if (dest_file->FlushCloseOrErase() != 0) { - *error_msg = - StringPrintf( - "Failed to flush/close destination image file %s", dest_image_filename.c_str()); - return false; - } - - return true; - } - - bool ReadFully( - const std::string& filename, std::vector* contents, std::string* error_msg) { - std::unique_ptr file(OS::OpenFileForReading(filename.c_str())); - if (file.get() == nullptr) { - *error_msg = "Failed to open"; - return false; - } - int64_t size = file->GetLength(); - if (size < 0) { - *error_msg = "Failed to get size"; - return false; - } - contents->resize(size); - if (!file->ReadFully(&(*contents)[0], size)) { - *error_msg = "Failed to read"; - contents->clear(); - return false; - } - return true; - } - - bool BinaryDiff( - const std::string& filename1, const std::string& filename2, std::string* error_msg) { - std::string read_error_msg; - std::vector image1; - if (!ReadFully(filename1, &image1, &read_error_msg)) { - *error_msg = StringPrintf("Failed to read %s: %s", filename1.c_str(), read_error_msg.c_str()); - return true; - } - std::vector image2; - if (!ReadFully(filename2, &image2, &read_error_msg)) { - *error_msg = StringPrintf("Failed to read %s: %s", filename2.c_str(), read_error_msg.c_str()); - return true; - } - if (image1.size() != image2.size()) { - *error_msg = - StringPrintf( - "%s and %s are of different size: %zu vs %zu", - filename1.c_str(), - filename2.c_str(), - image1.size(), - image2.size()); - return true; - } - size_t size = image1.size(); - for (size_t i = 0; i < size; i++) { - if (image1[i] != image2[i]) { - *error_msg = - StringPrintf("%s and %s differ at offset %zu", filename1.c_str(), filename2.c_str(), i); - return true; - } - } - - return false; - } -}; - -TEST_F(PatchoatTest, PatchoatRelocationSameAsDex2oatRelocation) { - // This test check that relocating a boot image using patchoat produces the same result as - // producing the boot image for that relocated base address using dex2oat. To be precise, these - // two files will have two small differences: the OAT checksum and base address. However, this - // test takes this into account. - - // 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; - // Force single-threaded compilation to avoid non-determinism which may be caused by - // multi-threaded compilation. We want the boot image created by this dex2oat run and the run - // below to differ only in its base address. - std::vector dex2oat_extra_args; - dex2oat_extra_args.push_back("-j1"); - std::string error_msg; - if (!CompileBootImageToDir(dex2oat_orig_dir, dex2oat_extra_args, orig_base_addr, &error_msg)) { - FAIL() << "CompileBootImage1 failed: " << error_msg; - } - - // Compile a "relocated" boot image into a random directory using dex2oat. This image is relocated - // in the sense that it uses a different base address. - ScratchFile dex2oat_reloc_scratch; - dex2oat_reloc_scratch.Unlink(); - std::string dex2oat_reloc_dir = dex2oat_reloc_scratch.GetFilename(); - ASSERT_EQ(0, mkdir(dex2oat_reloc_dir.c_str(), 0700)); - const uint32_t reloc_base_addr = 0x70000000; - // if (!CompileBootImageToDir(dex2oat_dir, reloc_base_addr, &error_msg)) { - if (!CompileBootImageToDir(dex2oat_reloc_dir, dex2oat_extra_args, reloc_base_addr, &error_msg)) { - FAIL() << "CompileBootImage2 failed: " << error_msg; - } - const off_t base_addr_delta = reloc_base_addr - orig_base_addr; - - // Relocate the original boot image using patchoat. The image is relocated by the same amount - // as the second/relocated image produced by dex2oat. - ScratchFile patchoat_scratch; - patchoat_scratch.Unlink(); - std::string patchoat_dir = patchoat_scratch.GetFilename(); - ASSERT_EQ(0, mkdir(patchoat_dir.c_str(), 0700)); - 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())); - if (!RelocateBootImage( - dex2oat_orig_dir + "/boot.art", - patchoat_dir + "/boot.art", - base_addr_delta, - &error_msg)) { - FAIL() << "RelocateBootImage failed: " << error_msg; - } - - // dex2oat_reloc_image_filename is the boot image relocated using dex2oat - // patchoat_reloc_image_filename is the boot image relocated using patchoat - std::string dex2oat_reloc_image_filename = dex2oat_reloc_dir + "/boot.art"; - std::string patchoat_reloc_image_filename = dex2oat_orig_dir + "/boot.art"; - std::replace( - patchoat_reloc_image_filename.begin() + 1, patchoat_reloc_image_filename.end(), '/', '@'); - patchoat_reloc_image_filename = - patchoat_dir - + (android::base::StartsWith(patchoat_reloc_image_filename, "/") ? "" : "/") - + patchoat_reloc_image_filename; - - // Patch up the dex2oat-relocated image so that it looks as though it was relocated by patchoat. - // patchoat preserves the OAT checksum header field and sets patch delta header field. - if (!CopyImageChecksumAndSetPatchDelta( - dex2oat_orig_dir + "/boot.art", - dex2oat_reloc_dir + "/boot.art", - base_addr_delta, - &error_msg)) { - FAIL() << "Unable to copy image checksum: " << error_msg; - } - - // Assert that the patchoat-relocated image is identical to the dex2oat-relocated image - if (BinaryDiff(dex2oat_reloc_image_filename, patchoat_reloc_image_filename, &error_msg)) { - FAIL() << "patchoat- and dex2oat-relocated images differ: " << error_msg; - } - - ClearDirectory(dex2oat_orig_dir.c_str(), /*recursive*/ true); - ClearDirectory(dex2oat_reloc_dir.c_str(), /*recursive*/ true); - ClearDirectory(patchoat_dir.c_str(), /*recursive*/ true); - rmdir(dex2oat_orig_dir.c_str()); - rmdir(dex2oat_reloc_dir.c_str()); - rmdir(patchoat_dir.c_str()); -} - -} // namespace art diff --git a/runtime/image.h b/runtime/image.h index 159a308fb3..3844186a9b 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -179,10 +179,6 @@ class PACKED(4) ImageHeader { return patch_delta_; } - void SetPatchDelta(off_t patch_delta) { - patch_delta_ = patch_delta; - } - static std::string GetOatLocationFromImageLocation(const std::string& image) { return GetLocationFromImageLocation(image, "oat"); } -- GitLab From e155f4b858be42a825e8270f65abb0fdcf6cbc9f Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Wed, 6 Dec 2017 15:18:38 +0000 Subject: [PATCH 163/226] Decouple virtual memory allocation in ElfBuilder. Multi-boot-image requires knowing the loaded size ahead of time, and calculating that size ended up being fairly hacky. Decouple allocation of virtual memory and allocation of file space, so that we have more flexibility when we do those. Test: The generated boot image is bit-for-bit identical as before. Change-Id: I012b55a71e7625f2310570f9b91447c9db73578b --- compiler/debug/elf_debug_frame_writer.h | 7 +- compiler/debug/elf_debug_info_writer.h | 4 +- compiler/debug/elf_debug_line_writer.h | 4 +- compiler/debug/elf_gnu_debugdata_writer.h | 4 +- compiler/linker/elf_builder.h | 224 ++++++++++------------ dex2oat/linker/elf_writer_quick.cc | 4 +- oatdump/oatdump.cc | 13 +- 7 files changed, 112 insertions(+), 148 deletions(-) diff --git a/compiler/debug/elf_debug_frame_writer.h b/compiler/debug/elf_debug_frame_writer.h index d0c98a7b79..27b70c8caa 100644 --- a/compiler/debug/elf_debug_frame_writer.h +++ b/compiler/debug/elf_debug_frame_writer.h @@ -207,13 +207,12 @@ void WriteCFISection(linker::ElfBuilder* builder, } // Write .eh_frame/.debug_frame section. - auto* cfi_section = (format == dwarf::DW_DEBUG_FRAME_FORMAT - ? builder->GetDebugFrame() - : builder->GetEhFrame()); + const bool is_debug_frame = format == dwarf::DW_DEBUG_FRAME_FORMAT; + auto* cfi_section = (is_debug_frame ? builder->GetDebugFrame() : builder->GetEhFrame()); { cfi_section->Start(); const bool is64bit = Is64BitInstructionSet(builder->GetIsa()); - const Elf_Addr cfi_address = cfi_section->GetAddress(); + const Elf_Addr cfi_address = (is_debug_frame ? 0 : cfi_section->GetAddress()); const Elf_Addr cie_address = cfi_address; Elf_Addr buffer_address = cfi_address; std::vector buffer; // Small temporary buffer. diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index d5999941d7..2c5a2f436e 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -296,7 +296,7 @@ class ElfCompilationUnitWriter { CHECK_EQ(info_.Depth(), 0); std::vector buffer; buffer.reserve(info_.data()->size() + KB); - const size_t offset = owner_->builder_->GetDebugInfo()->GetSize(); + const size_t offset = owner_->builder_->GetDebugInfo()->GetPosition(); // All compilation units share single table which is at the start of .debug_abbrev. const size_t debug_abbrev_offset = 0; WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_); @@ -461,7 +461,7 @@ class ElfCompilationUnitWriter { CHECK_EQ(info_.Depth(), 0); std::vector buffer; buffer.reserve(info_.data()->size() + KB); - const size_t offset = owner_->builder_->GetDebugInfo()->GetSize(); + const size_t offset = owner_->builder_->GetDebugInfo()->GetPosition(); // All compilation units share single table which is at the start of .debug_abbrev. const size_t debug_abbrev_offset = 0; WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_); diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index 943e03a765..f8e1bf7a72 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -60,7 +60,7 @@ class ElfDebugLineWriter { ? builder_->GetText()->GetAddress() : 0; - compilation_unit.debug_line_offset = builder_->GetDebugLine()->GetSize(); + compilation_unit.debug_line_offset = builder_->GetDebugLine()->GetPosition(); std::vector files; std::unordered_map files_map; @@ -268,7 +268,7 @@ class ElfDebugLineWriter { } std::vector buffer; buffer.reserve(opcodes.data()->size() + KB); - size_t offset = builder_->GetDebugLine()->GetSize(); + size_t offset = builder_->GetDebugLine()->GetPosition(); WriteDebugLineTable(directories, files, opcodes, offset, &buffer, &debug_line_patches_); builder_->GetDebugLine()->WriteFully(buffer.data(), buffer.size()); return buffer.size(); diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h index 1cdf6b0ad1..9b8ec35ed1 100644 --- a/compiler/debug/elf_gnu_debugdata_writer.h +++ b/compiler/debug/elf_gnu_debugdata_writer.h @@ -91,8 +91,8 @@ static std::vector MakeMiniDebugInfoInternal( builder->Start(); // Mirror .rodata and .text as NOBITS sections. // It is needed to detected relocations after compression. - builder->GetRoData()->WriteNoBitsSection(rodata_section_size); - builder->GetText()->WriteNoBitsSection(text_section_size); + builder->GetRoData()->AllocateVirtualMemory(rodata_section_size); + builder->GetText()->AllocateVirtualMemory(text_section_size); WriteDebugSymbols(builder.get(), method_infos, false /* with_signature */); WriteCFISection(builder.get(), method_infos, diff --git a/compiler/linker/elf_builder.h b/compiler/linker/elf_builder.h index b30b55e9b4..3710878ea1 100644 --- a/compiler/linker/elf_builder.h +++ b/compiler/linker/elf_builder.h @@ -108,8 +108,6 @@ class ElfBuilder FINAL { section_index_(0), name_(name), link_(link), - started_(false), - finished_(false), phdr_flags_(PF_R), phdr_type_(0) { DCHECK_GE(align, 1u); @@ -120,90 +118,62 @@ class ElfBuilder FINAL { header_.sh_entsize = entsize; } - // Start writing of this section. - void Start() { - CHECK(!started_); - CHECK(!finished_); - started_ = true; - auto& sections = owner_->sections_; - // Check that the previous section is complete. - CHECK(sections.empty() || sections.back()->finished_); - // The first ELF section index is 1. Index 0 is reserved for NULL. - section_index_ = sections.size() + 1; - // Page-align if we switch between allocated and non-allocated sections, - // or if we change the type of allocation (e.g. executable vs non-executable). - if (!sections.empty()) { - if (header_.sh_flags != sections.back()->header_.sh_flags) { - header_.sh_addralign = kPageSize; - } - } - // Align file position. - if (header_.sh_type != SHT_NOBITS) { - header_.sh_offset = owner_->AlignFileOffset(header_.sh_addralign); - } else { - header_.sh_offset = 0; - } - // Align virtual memory address. - if ((header_.sh_flags & SHF_ALLOC) != 0) { - header_.sh_addr = owner_->AlignVirtualAddress(header_.sh_addralign); - } else { - header_.sh_addr = 0; - } - // Push this section on the list of written sections. - sections.push_back(this); + // Allocate chunk of virtual memory for this section from the owning ElfBuilder. + // This must be done at the start for all SHF_ALLOC sections (i.e. mmaped by linker). + // It is fine to allocate section but never call Start/End() (e.g. the .bss section). + void AllocateVirtualMemory(Elf_Word size) { + AllocateVirtualMemory(owner_->virtual_address_, size); } - // Finish writing of this section. - void End() { - CHECK(started_); - CHECK(!finished_); - finished_ = true; - if (header_.sh_type == SHT_NOBITS) { - CHECK_GT(header_.sh_size, 0u); - } else { - // Use the current file position to determine section size. - off_t file_offset = owner_->stream_.Seek(0, kSeekCurrent); - CHECK_GE(file_offset, (off_t)header_.sh_offset); - header_.sh_size = file_offset - header_.sh_offset; - } - if ((header_.sh_flags & SHF_ALLOC) != 0) { - owner_->virtual_address_ += header_.sh_size; - } + void AllocateVirtualMemory(Elf_Addr addr, Elf_Word size) { + CHECK_NE(header_.sh_flags & SHF_ALLOC, 0u); + Elf_Word align = AddSection(); + CHECK_EQ(header_.sh_addr, 0u); + header_.sh_addr = RoundUp(addr, align); + CHECK(header_.sh_size == 0u || header_.sh_size == size); + header_.sh_size = size; + CHECK_LE(owner_->virtual_address_, header_.sh_addr); + owner_->virtual_address_ = header_.sh_addr + header_.sh_size; } - // Get the location of this section in virtual memory. - Elf_Addr GetAddress() const { - CHECK(started_); - return header_.sh_addr; + // Start writing file data of this section. + void Start() { + CHECK(owner_->current_section_ == nullptr); + Elf_Word align = AddSection(); + CHECK_EQ(header_.sh_offset, 0u); + header_.sh_offset = owner_->AlignFileOffset(align); + owner_->current_section_ = this; } - // Returns the size of the content of this section. - Elf_Word GetSize() const { - if (finished_) { - return header_.sh_size; - } else { - CHECK(started_); - CHECK_NE(header_.sh_type, (Elf_Word)SHT_NOBITS); - return owner_->stream_.Seek(0, kSeekCurrent) - header_.sh_offset; - } + // Finish writing file data of this section. + void End() { + CHECK(owner_->current_section_ == this); + Elf_Word position = GetPosition(); + CHECK(header_.sh_size == 0u || header_.sh_size == position); + header_.sh_size = position; + owner_->current_section_ = nullptr; + } + + // Get the number of bytes written so far. + // Only valid while writing the section. + Elf_Word GetPosition() const { + CHECK(owner_->current_section_ == this); + off_t file_offset = owner_->stream_.Seek(0, kSeekCurrent); + DCHECK_GE(file_offset, (off_t)header_.sh_offset); + return file_offset - header_.sh_offset; } - // Write this section as "NOBITS" section. (used for the .bss section) - // This means that the ELF file does not contain the initial data for this section - // and it will be zero-initialized when the ELF file is loaded in the running program. - void WriteNoBitsSection(Elf_Word size) { + // Get the location of this section in virtual memory. + Elf_Addr GetAddress() const { DCHECK_NE(header_.sh_flags & SHF_ALLOC, 0u); - header_.sh_type = SHT_NOBITS; - Start(); - header_.sh_size = size; - End(); + DCHECK_NE(header_.sh_addr, 0u); + return header_.sh_addr; } // This function always succeeds to simplify code. // Use builder's Good() to check the actual status. bool WriteFully(const void* buffer, size_t byte_count) OVERRIDE { - CHECK(started_); - CHECK(!finished_); + CHECK(owner_->current_section_ == this); return owner_->stream_.WriteFully(buffer, byte_count); } @@ -221,19 +191,32 @@ class ElfBuilder FINAL { } Elf_Word GetSectionIndex() const { - DCHECK(started_); DCHECK_NE(section_index_, 0u); return section_index_; } private: + // Add this section to the list of generated ELF sections (if not there already). + // It also ensures the alignment is sufficient to generate valid program headers, + // since that depends on the previous section. It returns the required alignment. + Elf_Word AddSection() { + if (section_index_ == 0) { + std::vector& sections = owner_->sections_; + Elf_Word last = sections.empty() ? PF_R : sections.back()->phdr_flags_; + if (owner_->write_program_headers_ && phdr_flags_ != last) { + header_.sh_addralign = kPageSize; // Page-align if R/W/X flags changed. + } + sections.push_back(this); + section_index_ = sections.size(); // First ELF section has index 1. + } + return header_.sh_addralign; + } + ElfBuilder* owner_; Elf_Shdr header_; Elf_Word section_index_; const std::string name_; const Section* const link_; - bool started_; - bool finished_; Elf_Word phdr_flags_; Elf_Word phdr_type_; @@ -370,7 +353,7 @@ class ElfBuilder FINAL { Elf_Word section_index; if (section != nullptr) { DCHECK_LE(section->GetAddress(), addr); - DCHECK_LE(addr, section->GetAddress() + section->GetSize()); + DCHECK_LE(addr, section->GetAddress() + section->header_.sh_size); section_index = section->GetSectionIndex(); } else { section_index = static_cast(SHN_ABS); @@ -479,6 +462,10 @@ class ElfBuilder FINAL { digest_start_(-1) { } + Elf_Word GetSize() { + return 16 + kBuildIdLen; + } + void Write() { // The size fields are 32-bit on both 32-bit and 64-bit systems, confirmed // with the 64-bit linker and libbfd code. The size of name and desc must @@ -490,6 +477,7 @@ class ElfBuilder FINAL { digest_start_ = this->Seek(0, kSeekCurrent); static_assert(kBuildIdLen % 4 == 0, "expecting a mutliple of 4 for build ID length"); this->WriteFully(std::string(kBuildIdLen, '\0').c_str(), kBuildIdLen); // desc. + DCHECK_EQ(this->GetPosition(), GetSize()); } off_t GetDigestStart() { @@ -530,6 +518,7 @@ class ElfBuilder FINAL { abiflags_(this, ".MIPS.abiflags", SHT_MIPS_ABIFLAGS, SHF_ALLOC, nullptr, 0, kPageSize, 0, isa, features), build_id_(this, ".note.gnu.build-id", SHT_NOTE, SHF_ALLOC, nullptr, 0, 4, 0), + current_section_(nullptr), started_(false), write_program_headers_(false), loaded_size_(0u), @@ -545,6 +534,7 @@ class ElfBuilder FINAL { ~ElfBuilder() {} InstructionSet GetIsa() { return isa_; } + BuildIdSection* GetBuildId() { return &build_id_; } Section* GetRoData() { return &rodata_; } Section* GetText() { return &text_; } Section* GetBss() { return &bss_; } @@ -622,6 +612,9 @@ class ElfBuilder FINAL { if (section->link_ != nullptr) { section->header_.sh_link = section->link_->GetSectionIndex(); } + if (section->header_.sh_offset == 0) { + section->header_.sh_type = SHT_NOBITS; + } } shstrtab_.End(); @@ -680,65 +673,57 @@ class ElfBuilder FINAL { soname = soname.substr(directory_separator_pos + 1); } - // Calculate addresses of .text, .bss and .dynstr. - DCHECK_EQ(rodata_.header_.sh_addralign, static_cast(kPageSize)); - DCHECK_EQ(text_.header_.sh_addralign, static_cast(kPageSize)); - DCHECK_EQ(bss_.header_.sh_addralign, static_cast(kPageSize)); - DCHECK_EQ(dynstr_.header_.sh_addralign, static_cast(kPageSize)); - Elf_Word rodata_address = rodata_.GetAddress(); - Elf_Word text_address = RoundUp(rodata_address + rodata_size, kPageSize); - Elf_Word bss_address = RoundUp(text_address + text_size, kPageSize); - Elf_Word abiflags_address = RoundUp(bss_address + bss_size, kPageSize); - Elf_Word abiflags_size = 0; + // Allocate all pre-dynamic sections. + rodata_.AllocateVirtualMemory(rodata_size); + text_.AllocateVirtualMemory(text_size); + if (bss_size != 0) { + bss_.AllocateVirtualMemory(bss_size); + } if (isa_ == InstructionSet::kMips || isa_ == InstructionSet::kMips64) { - abiflags_size = abiflags_.GetSize(); + abiflags_.AllocateVirtualMemory(abiflags_.GetSize()); } - Elf_Word dynstr_address = RoundUp(abiflags_address + abiflags_size, kPageSize); // Cache .dynstr, .dynsym and .hash data. dynstr_.Add(""); // dynstr should start with empty string. - Elf_Word rodata_index = rodata_.GetSectionIndex(); Elf_Word oatdata = dynstr_.Add("oatdata"); - dynsym_.Add(oatdata, rodata_index, rodata_address, rodata_size, STB_GLOBAL, STT_OBJECT); + dynsym_.Add(oatdata, &rodata_, rodata_.GetAddress(), rodata_size, STB_GLOBAL, STT_OBJECT); if (text_size != 0u) { - Elf_Word text_index = rodata_index + 1u; Elf_Word oatexec = dynstr_.Add("oatexec"); - dynsym_.Add(oatexec, text_index, text_address, text_size, STB_GLOBAL, STT_OBJECT); + dynsym_.Add(oatexec, &text_, text_.GetAddress(), text_size, STB_GLOBAL, STT_OBJECT); Elf_Word oatlastword = dynstr_.Add("oatlastword"); - Elf_Word oatlastword_address = text_address + text_size - 4; - dynsym_.Add(oatlastword, text_index, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT); + Elf_Word oatlastword_address = text_.GetAddress() + text_size - 4; + dynsym_.Add(oatlastword, &text_, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT); } else if (rodata_size != 0) { // rodata_ can be size 0 for dwarf_test. Elf_Word oatlastword = dynstr_.Add("oatlastword"); - Elf_Word oatlastword_address = rodata_address + rodata_size - 4; - dynsym_.Add(oatlastword, rodata_index, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT); + Elf_Word oatlastword_address = rodata_.GetAddress() + rodata_size - 4; + dynsym_.Add(oatlastword, &rodata_, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT); } DCHECK_LE(bss_roots_offset, bss_size); if (bss_size != 0u) { - Elf_Word bss_index = rodata_index + 1u + (text_size != 0 ? 1u : 0u); Elf_Word oatbss = dynstr_.Add("oatbss"); - dynsym_.Add(oatbss, bss_index, bss_address, bss_roots_offset, STB_GLOBAL, STT_OBJECT); + dynsym_.Add(oatbss, &bss_, bss_.GetAddress(), bss_roots_offset, STB_GLOBAL, STT_OBJECT); DCHECK_LE(bss_methods_offset, bss_roots_offset); DCHECK_LE(bss_roots_offset, bss_size); // Add a symbol marking the start of the methods part of the .bss, if not empty. if (bss_methods_offset != bss_roots_offset) { - Elf_Word bss_methods_address = bss_address + bss_methods_offset; + Elf_Word bss_methods_address = bss_.GetAddress() + bss_methods_offset; Elf_Word bss_methods_size = bss_roots_offset - bss_methods_offset; Elf_Word oatbssroots = dynstr_.Add("oatbssmethods"); dynsym_.Add( - oatbssroots, bss_index, bss_methods_address, bss_methods_size, STB_GLOBAL, STT_OBJECT); + oatbssroots, &bss_, bss_methods_address, bss_methods_size, STB_GLOBAL, STT_OBJECT); } // Add a symbol marking the start of the GC roots part of the .bss, if not empty. if (bss_roots_offset != bss_size) { - Elf_Word bss_roots_address = bss_address + bss_roots_offset; + Elf_Word bss_roots_address = bss_.GetAddress() + bss_roots_offset; Elf_Word bss_roots_size = bss_size - bss_roots_offset; Elf_Word oatbssroots = dynstr_.Add("oatbssroots"); dynsym_.Add( - oatbssroots, bss_index, bss_roots_address, bss_roots_size, STB_GLOBAL, STT_OBJECT); + oatbssroots, &bss_, bss_roots_address, bss_roots_size, STB_GLOBAL, STT_OBJECT); } Elf_Word oatbsslastword = dynstr_.Add("oatbsslastword"); - Elf_Word bsslastword_address = bss_address + bss_size - 4; - dynsym_.Add(oatbsslastword, bss_index, bsslastword_address, 4, STB_GLOBAL, STT_OBJECT); + Elf_Word bsslastword_address = bss_.GetAddress() + bss_size - 4; + dynsym_.Add(oatbsslastword, &bss_, bsslastword_address, 4, STB_GLOBAL, STT_OBJECT); } Elf_Word soname_offset = dynstr_.Add(soname); @@ -759,28 +744,24 @@ class ElfBuilder FINAL { hash.push_back(0); // Last symbol terminates the chain. hash_.Add(hash.data(), hash.size() * sizeof(hash[0])); - // Calculate addresses of .dynsym, .hash and .dynamic. - DCHECK_EQ(dynstr_.header_.sh_flags, dynsym_.header_.sh_flags); - DCHECK_EQ(dynsym_.header_.sh_flags, hash_.header_.sh_flags); - Elf_Word dynsym_address = - RoundUp(dynstr_address + dynstr_.GetCacheSize(), dynsym_.header_.sh_addralign); - Elf_Word hash_address = - RoundUp(dynsym_address + dynsym_.GetCacheSize(), hash_.header_.sh_addralign); - DCHECK_EQ(dynamic_.header_.sh_addralign, static_cast(kPageSize)); - Elf_Word dynamic_address = RoundUp(hash_address + dynsym_.GetCacheSize(), kPageSize); + // Allocate all remaining sections. + dynstr_.AllocateVirtualMemory(dynstr_.GetCacheSize()); + dynsym_.AllocateVirtualMemory(dynsym_.GetCacheSize()); + hash_.AllocateVirtualMemory(hash_.GetCacheSize()); Elf_Dyn dyns[] = { - { DT_HASH, { hash_address } }, - { DT_STRTAB, { dynstr_address } }, - { DT_SYMTAB, { dynsym_address } }, + { DT_HASH, { hash_.GetAddress() } }, + { DT_STRTAB, { dynstr_.GetAddress() } }, + { DT_SYMTAB, { dynsym_.GetAddress() } }, { DT_SYMENT, { sizeof(Elf_Sym) } }, { DT_STRSZ, { dynstr_.GetCacheSize() } }, { DT_SONAME, { soname_offset } }, { DT_NULL, { 0 } }, }; dynamic_.Add(&dyns, sizeof(dyns)); + dynamic_.AllocateVirtualMemory(dynamic_.GetCacheSize()); - loaded_size_ = RoundUp(dynamic_address + dynamic_.GetCacheSize(), kPageSize); + loaded_size_ = RoundUp(virtual_address_, kPageSize); } void WriteDynamicSection() { @@ -788,8 +769,6 @@ class ElfBuilder FINAL { dynsym_.WriteCachedSection(); hash_.WriteCachedSection(); dynamic_.WriteCachedSection(); - - CHECK_EQ(loaded_size_, RoundUp(dynamic_.GetAddress() + dynamic_.GetSize(), kPageSize)); } Elf_Word GetLoadedSize() { @@ -828,10 +807,6 @@ class ElfBuilder FINAL { return stream_.Seek(RoundUp(stream_.Seek(0, kSeekCurrent), alignment), kSeekSet); } - Elf_Addr AlignVirtualAddress(size_t alignment) { - return virtual_address_ = RoundUp(virtual_address_, alignment); - } - private: static Elf_Ehdr MakeElfHeader(InstructionSet isa, const InstructionSetFeatures* features) { Elf_Ehdr elf_header = Elf_Ehdr(); @@ -902,7 +877,6 @@ class ElfBuilder FINAL { elf_header.e_ehsize = sizeof(Elf_Ehdr); elf_header.e_phentsize = sizeof(Elf_Phdr); elf_header.e_shentsize = sizeof(Elf_Shdr); - elf_header.e_phoff = sizeof(Elf_Ehdr); return elf_header; } @@ -933,6 +907,7 @@ class ElfBuilder FINAL { for (auto* section : sections_) { const Elf_Shdr& shdr = section->header_; if ((shdr.sh_flags & SHF_ALLOC) != 0 && shdr.sh_size != 0) { + DCHECK(shdr.sh_addr != 0u) << "Allocate virtual memory for the section"; // PT_LOAD tells the linker to mmap part of the file. // The linker can only mmap page-aligned sections. // Single PT_LOAD may contain several ELF sections. @@ -1010,6 +985,7 @@ class ElfBuilder FINAL { // List of used section in the order in which they were written. std::vector sections_; + Section* current_section_; // The section which is currently being written. bool started_; bool write_program_headers_; diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc index 5fc33ddf8b..758e21de93 100644 --- a/dex2oat/linker/elf_writer_quick.cc +++ b/dex2oat/linker/elf_writer_quick.cc @@ -173,6 +173,7 @@ template void ElfWriterQuick::Start() { builder_->Start(); if (compiler_options_->GetGenerateBuildId()) { + builder_->GetBuildId()->AllocateVirtualMemory(builder_->GetBuildId()->GetSize()); builder_->WriteBuildIdSection(); } } @@ -225,9 +226,6 @@ void ElfWriterQuick::EndText(OutputStream* text) { template void ElfWriterQuick::WriteDynamicSection() { - if (bss_size_ != 0u) { - builder_->GetBss()->WriteNoBitsSection(bss_size_); - } if (builder_->GetIsa() == InstructionSet::kMips || builder_->GetIsa() == InstructionSet::kMips64) { builder_->WriteMIPSabiflagsSection(); diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 1b4485b233..1a1d8cc76e 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -146,13 +146,10 @@ class OatSymbolizer FINAL { auto* rodata = builder_->GetRoData(); auto* text = builder_->GetText(); - auto* bss = builder_->GetBss(); const uint8_t* rodata_begin = oat_file_->Begin(); const size_t rodata_size = oat_file_->GetOatHeader().GetExecutableOffset(); - if (no_bits_) { - rodata->WriteNoBitsSection(rodata_size); - } else { + if (!no_bits_) { rodata->Start(); rodata->WriteFully(rodata_begin, rodata_size); rodata->End(); @@ -160,18 +157,12 @@ class OatSymbolizer FINAL { const uint8_t* text_begin = oat_file_->Begin() + rodata_size; const size_t text_size = oat_file_->End() - text_begin; - if (no_bits_) { - text->WriteNoBitsSection(text_size); - } else { + if (!no_bits_) { text->Start(); text->WriteFully(text_begin, text_size); text->End(); } - if (oat_file_->BssSize() != 0) { - bss->WriteNoBitsSection(oat_file_->BssSize()); - } - if (isa == InstructionSet::kMips || isa == InstructionSet::kMips64) { builder_->WriteMIPSabiflagsSection(); } -- GitLab From 666ee3d7c6039c80e75287e311895bd6a9b01e9f Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 11 Dec 2017 18:37:36 +0000 Subject: [PATCH 164/226] Do not pass DexFile to ClassLinker::Lookup/ResolveType(). The DexFile can be easily retrieved from the DexCache, so reduce the number of arguments that need to be passed. Also refactor the code to avoid doing the DexCache lookup twice and avoid unnecessary read barriers in the initial DexCache lookup (also for Lookup/ResolveField()). Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Change-Id: Idea9aa42b6a5bade947e93e330b1abdb9d11b2da --- compiler/driver/compiler_driver-inl.h | 16 ++- compiler/driver/compiler_driver.cc | 15 +- compiler/driver/compiler_driver.h | 24 ++-- compiler/optimizing/inliner.cc | 4 +- compiler/optimizing/instruction_builder.cc | 5 +- .../optimizing/reference_type_propagation.cc | 4 +- dex2oat/linker/image_writer.cc | 8 +- dex2oat/linker/oat_writer.cc | 2 +- runtime/art_field-inl.h | 26 +--- runtime/art_method-inl.h | 25 ++-- runtime/class_linker-inl.h | 116 +++++++++++---- runtime/class_linker.cc | 132 ++++++++---------- runtime/class_linker.h | 72 ++++++---- runtime/class_linker_test.cc | 20 ++- runtime/dex_file_annotations.cc | 8 +- runtime/entrypoints/entrypoint_utils-inl.h | 2 +- .../quick/quick_trampoline_entrypoints.cc | 14 +- runtime/interpreter/interpreter_common.cc | 3 +- runtime/mirror/class-inl.h | 2 - runtime/mirror/class.cc | 6 +- runtime/native/dalvik_system_VMRuntime.cc | 8 +- runtime/native/java_lang_Class.cc | 4 +- runtime/verifier/method_verifier.cc | 17 ++- 23 files changed, 279 insertions(+), 254 deletions(-) diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index 34c8f22c03..294072d7e7 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -33,13 +33,15 @@ namespace art { inline ObjPtr CompilerDriver::ResolveClass( - const ScopedObjectAccess& soa, Handle dex_cache, - Handle class_loader, dex::TypeIndex cls_index, + const ScopedObjectAccess& soa, + Handle dex_cache, + Handle class_loader, + dex::TypeIndex cls_index, const DexCompilationUnit* mUnit) { DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile()); DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); - ObjPtr cls = mUnit->GetClassLinker()->ResolveType( - *mUnit->GetDexFile(), cls_index, dex_cache, class_loader); + ObjPtr cls = + mUnit->GetClassLinker()->ResolveType(cls_index, dex_cache, class_loader); DCHECK_EQ(cls == nullptr, soa.Self()->IsExceptionPending()); if (UNLIKELY(cls == nullptr)) { // Clean up any exception left by type resolution. @@ -49,8 +51,10 @@ inline ObjPtr CompilerDriver::ResolveClass( } inline ObjPtr CompilerDriver::ResolveCompilingMethodsClass( - const ScopedObjectAccess& soa, Handle dex_cache, - Handle class_loader, const DexCompilationUnit* mUnit) { + const ScopedObjectAccess& soa, + Handle dex_cache, + Handle class_loader, + const DexCompilationUnit* mUnit) { DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile()); DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); const DexFile::MethodId& referrer_method_id = diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 8f726895de..0631c0f12c 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1048,22 +1048,21 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) { for (const auto& exception_type : unresolved_exception_types) { dex::TypeIndex exception_type_idx = exception_type.first; const DexFile* dex_file = exception_type.second; - StackHandleScope<2> hs2(self); + StackHandleScope<1> hs2(self); Handle dex_cache(hs2.NewHandle(class_linker->RegisterDexFile(*dex_file, nullptr))); - Handle klass(hs2.NewHandle( + ObjPtr klass = (dex_cache != nullptr) - ? class_linker->ResolveType(*dex_file, - exception_type_idx, + ? class_linker->ResolveType(exception_type_idx, dex_cache, ScopedNullHandle()) - : nullptr)); + : nullptr; if (klass == nullptr) { const DexFile::TypeId& type_id = dex_file->GetTypeId(exception_type_idx); const char* descriptor = dex_file->GetTypeDescriptor(type_id); LOG(FATAL) << "Failed to resolve class " << descriptor; } - DCHECK(java_lang_Throwable->IsAssignableFrom(klass.Get())); + DCHECK(java_lang_Throwable->IsAssignableFrom(klass)); } // Resolving exceptions may load classes that reference more exceptions, iterate until no // more are found @@ -1638,7 +1637,7 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { soa.Self(), dex_file))); // Resolve the class. ObjPtr klass = - class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache, class_loader); + class_linker->ResolveType(class_def.class_idx_, dex_cache, class_loader); bool resolve_fields_and_methods; if (klass == nullptr) { // Class couldn't be resolved, for example, super-class is in a different dex file. Don't @@ -1729,7 +1728,7 @@ class ResolveTypeVisitor : public CompilationVisitor { dex_file, class_loader.Get()))); ObjPtr klass = (dex_cache != nullptr) - ? class_linker->ResolveType(dex_file, dex::TypeIndex(type_idx), dex_cache, class_loader) + ? class_linker->ResolveType(dex::TypeIndex(type_idx), dex_cache, class_loader) : nullptr; if (klass == nullptr) { diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index ab788e326f..e001726c95 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -219,15 +219,17 @@ class CompilerDriver { REQUIRES_SHARED(Locks::mutator_lock_); // Resolve compiling method's class. Returns null on failure. - ObjPtr ResolveCompilingMethodsClass( - const ScopedObjectAccess& soa, Handle dex_cache, - Handle class_loader, const DexCompilationUnit* mUnit) + ObjPtr ResolveCompilingMethodsClass(const ScopedObjectAccess& soa, + Handle dex_cache, + Handle class_loader, + const DexCompilationUnit* mUnit) REQUIRES_SHARED(Locks::mutator_lock_); - ObjPtr ResolveClass( - const ScopedObjectAccess& soa, Handle dex_cache, - Handle class_loader, dex::TypeIndex type_index, - const DexCompilationUnit* mUnit) + ObjPtr ResolveClass(const ScopedObjectAccess& soa, + Handle dex_cache, + Handle class_loader, + dex::TypeIndex type_index, + const DexCompilationUnit* mUnit) REQUIRES_SHARED(Locks::mutator_lock_); // Resolve a field. Returns null on failure, including incompatible class change. @@ -240,10 +242,10 @@ class CompilerDriver { REQUIRES_SHARED(Locks::mutator_lock_); // Can we fast-path an IGET/IPUT access to an instance field? If yes, compute the field offset. - std::pair IsFastInstanceField( - ObjPtr dex_cache, - ObjPtr referrer_class, - ArtField* resolved_field, uint16_t field_idx) + std::pair IsFastInstanceField(ObjPtr dex_cache, + ObjPtr referrer_class, + ArtField* resolved_field, + uint16_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_); // Resolve a method. Returns null on failure, including incompatible class change. diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index a175c21760..8750910fe1 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -294,7 +294,7 @@ static dex::TypeIndex FindClassIndexIn(mirror::Class* cls, // as there may be different class loaders. So only return the index if it's // the right class already resolved with the class loader. if (index.IsValid()) { - ObjPtr resolved = ClassLinker::LookupResolvedType( + ObjPtr resolved = compilation_unit.GetClassLinker()->LookupResolvedType( index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get()); if (resolved != cls) { index = dex::TypeIndex::Invalid(); @@ -682,7 +682,7 @@ HInliner::InlineCacheType HInliner::ExtractClassesFromOfflineProfile( << "is invalid in location" << dex_cache->GetDexFile()->GetLocation(); return kInlineCacheNoData; } - ObjPtr clazz = ClassLinker::LookupResolvedType( + ObjPtr clazz = caller_compilation_unit_.GetClassLinker()->LookupResolvedType( class_ref.type_index, dex_cache, caller_compilation_unit_.GetClassLoader().Get()); diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 6fdb616ce2..710c2efd5f 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -830,7 +830,6 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in return nullptr; } ObjPtr referenced_class = class_linker->LookupResolvedType( - *dex_compilation_unit_->GetDexFile(), dex_compilation_unit_->GetDexFile()->GetMethodId(method_idx).class_idx_, dex_compilation_unit_->GetDexCache().Get(), class_loader.Get()); @@ -1424,7 +1423,7 @@ bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instructio } static ObjPtr GetClassFrom(CompilerDriver* driver, - const DexCompilationUnit& compilation_unit) { + const DexCompilationUnit& compilation_unit) { ScopedObjectAccess soa(Thread::Current()); Handle class_loader = compilation_unit.GetClassLoader(); Handle dex_cache = compilation_unit.GetDexCache(); @@ -2933,7 +2932,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, ObjPtr HInstructionBuilder::LookupResolvedType( dex::TypeIndex type_index, const DexCompilationUnit& compilation_unit) const { - return ClassLinker::LookupResolvedType( + return compilation_unit.GetClassLinker()->LookupResolvedType( type_index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get()); } diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 549572ce40..8bb124e066 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -576,8 +576,8 @@ void ReferenceTypePropagation::RTPVisitor::UpdateReferenceTypeInfo(HInstruction* ScopedObjectAccess soa(Thread::Current()); ObjPtr dex_cache = FindDexCacheWithHint(soa.Self(), dex_file, hint_dex_cache_); - ObjPtr klass = - ClassLinker::LookupResolvedType(type_idx, dex_cache, class_loader_.Get()); + ObjPtr klass = Runtime::Current()->GetClassLinker()->LookupResolvedType( + type_idx, dex_cache, class_loader_.Get()); SetClassAsTypeInfo(instr, klass, is_exact); } diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index f6ceb27b21..738bbf8e9d 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -1050,8 +1050,7 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr dex_cache, const DexFile::MethodId& method_id = dex_file.GetMethodId(i); if (method_id.class_idx_ != last_class_idx) { last_class_idx = method_id.class_idx_; - last_class = class_linker->LookupResolvedType( - dex_file, last_class_idx, dex_cache, class_loader); + last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader); if (last_class != nullptr && !KeepClass(last_class)) { last_class = nullptr; } @@ -1096,8 +1095,7 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr dex_cache, const DexFile::FieldId& field_id = dex_file.GetFieldId(i); if (field_id.class_idx_ != last_class_idx) { last_class_idx = field_id.class_idx_; - last_class = class_linker->LookupResolvedType( - dex_file, last_class_idx, dex_cache, class_loader); + last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader); if (last_class != nullptr && !KeepClass(last_class)) { last_class = nullptr; } @@ -1130,7 +1128,7 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr dex_cache, uint32_t stored_index = pair.index; ObjPtr klass = pair.object.Read(); if (klass == nullptr || i < stored_index) { - klass = class_linker->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader); + klass = class_linker->LookupResolvedType(type_idx, dex_cache, class_loader); if (klass != nullptr) { DCHECK_EQ(dex_cache->GetResolvedType(type_idx), klass); stored_index = i; // For correct clearing below if not keeping the `klass`. diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index c271a6cdf4..786acce8ee 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -1956,7 +1956,7 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { DCHECK(writer_->HasImage()); ObjPtr dex_cache = GetDexCache(patch.TargetTypeDexFile()); ObjPtr type = - ClassLinker::LookupResolvedType(patch.TargetTypeIndex(), dex_cache, class_loader_); + class_linker_->LookupResolvedType(patch.TargetTypeIndex(), dex_cache, class_loader_); CHECK(type != nullptr); return type; } diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h index 941f9e908c..2b18577ed0 100644 --- a/runtime/art_field-inl.h +++ b/runtime/art_field-inl.h @@ -307,16 +307,10 @@ inline ObjPtr ArtField::LookupResolvedType() { if (UNLIKELY(declaring_class->IsProxyClass())) { return ProxyFindSystemClass(GetTypeDescriptor()); } - ObjPtr dex_cache = declaring_class->GetDexCache(); - const DexFile* const dex_file = dex_cache->GetDexFile(); - dex::TypeIndex type_idx = dex_file->GetFieldId(field_index).type_idx_; - ObjPtr type = dex_cache->GetResolvedType(type_idx); - if (UNLIKELY(type == nullptr)) { - type = Runtime::Current()->GetClassLinker()->LookupResolvedType( - *dex_file, type_idx, dex_cache, declaring_class->GetClassLoader()); - DCHECK(!Thread::Current()->IsExceptionPending()); - } - return type.Ptr(); + ObjPtr type = Runtime::Current()->GetClassLinker()->LookupResolvedType( + declaring_class->GetDexFile().GetFieldId(field_index).type_idx_, declaring_class); + DCHECK(!Thread::Current()->IsExceptionPending()); + return type; } inline ObjPtr ArtField::ResolveType() { @@ -325,15 +319,9 @@ inline ObjPtr ArtField::ResolveType() { if (UNLIKELY(declaring_class->IsProxyClass())) { return ProxyFindSystemClass(GetTypeDescriptor()); } - auto* dex_cache = declaring_class->GetDexCache(); - const DexFile* const dex_file = dex_cache->GetDexFile(); - dex::TypeIndex type_idx = dex_file->GetFieldId(field_index).type_idx_; - ObjPtr type = dex_cache->GetResolvedType(type_idx); - if (UNLIKELY(type == nullptr)) { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - type = class_linker->ResolveType(*dex_file, type_idx, declaring_class); - DCHECK_EQ(type == nullptr, Thread::Current()->IsExceptionPending()); - } + ObjPtr type = Runtime::Current()->GetClassLinker()->ResolveType( + declaring_class->GetDexFile().GetFieldId(field_index).type_idx_, declaring_class); + DCHECK_EQ(type == nullptr, Thread::Current()->IsExceptionPending()); return type; } diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 327081f67a..869394c388 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -106,23 +106,16 @@ inline uint32_t ArtMethod::GetDexMethodIndex() { inline ObjPtr ArtMethod::LookupResolvedClassFromTypeIndex(dex::TypeIndex type_idx) { ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ObjPtr dex_cache = GetDexCache(); - ObjPtr type = dex_cache->GetResolvedType(type_idx); - if (UNLIKELY(type == nullptr)) { - type = Runtime::Current()->GetClassLinker()->LookupResolvedType( - *dex_cache->GetDexFile(), type_idx, dex_cache, GetClassLoader()); - } - return type.Ptr(); + ObjPtr type = + Runtime::Current()->GetClassLinker()->LookupResolvedType(type_idx, this); + DCHECK(!Thread::Current()->IsExceptionPending()); + return type; } inline ObjPtr ArtMethod::ResolveClassFromTypeIndex(dex::TypeIndex type_idx) { - ObjPtr dex_cache = GetDexCache(); - ObjPtr type = dex_cache->GetResolvedType(type_idx); - if (UNLIKELY(type == nullptr)) { - type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this); - CHECK(type != nullptr || Thread::Current()->IsExceptionPending()); - } - return type.Ptr(); + ObjPtr type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this); + DCHECK_EQ(type == nullptr, Thread::Current()->IsExceptionPending()); + return type; } inline bool ArtMethod::CheckIncompatibleClassChange(InvokeType type) { @@ -305,9 +298,7 @@ inline const DexFile::ClassDef& ArtMethod::GetClassDef() { inline const char* ArtMethod::GetReturnTypeDescriptor() { DCHECK(!IsProxyMethod()); const DexFile* dex_file = GetDexFile(); - const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex()); - const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id); - return dex_file->GetTypeDescriptor(dex_file->GetTypeId(proto_id.return_type_idx_)); + return dex_file->GetTypeDescriptor(dex_file->GetTypeId(GetReturnTypeIndex())); } inline Primitive::Type ArtMethod::GetReturnTypePrimitive() { diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index c086dd2676..4b317f886f 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -61,16 +61,27 @@ inline mirror::Class* ClassLinker::FindArrayClass(Thread* self, return array_class.Ptr(); } -inline ObjPtr ClassLinker::LookupResolvedType( - dex::TypeIndex type_idx, - ObjPtr dex_cache, - ObjPtr class_loader) { - ObjPtr type = dex_cache->GetResolvedType(type_idx); - if (type == nullptr) { - type = Runtime::Current()->GetClassLinker()->LookupResolvedType( - *dex_cache->GetDexFile(), type_idx, dex_cache, class_loader); +inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, + ObjPtr referrer) { + if (kObjPtrPoisoning) { + StackHandleScope<1> hs(Thread::Current()); + HandleWrapperObjPtr referrer_wrapper = hs.NewHandleWrapper(&referrer); + Thread::Current()->PoisonObjectPointers(); } - return type; + if (kIsDebugBuild) { + Thread::Current()->AssertNoPendingException(); + } + // 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); + } + return resolved_type; } inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, @@ -79,18 +90,67 @@ inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, if (kIsDebugBuild) { Thread::Current()->AssertNoPendingException(); } - ObjPtr resolved_type = referrer->GetDexCache()->GetResolvedType(type_idx); + // 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 declaring_class = referrer->GetDeclaringClass(); + ObjPtr referring_class = referrer->GetDeclaringClass(); Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); - Handle class_loader(hs.NewHandle(declaring_class->GetClassLoader())); - const DexFile& dex_file = *dex_cache->GetDexFile(); - resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader); + Handle class_loader(hs.NewHandle(referring_class->GetClassLoader())); + resolved_type = DoResolveType(type_idx, dex_cache, class_loader); } return resolved_type; } +inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, + Handle dex_cache, + Handle class_loader) { + DCHECK(dex_cache != nullptr); + Thread::PoisonObjectPointersIfDebug(); + ObjPtr resolved = dex_cache->GetResolvedType(type_idx); + if (resolved == nullptr) { + resolved = DoResolveType(type_idx, dex_cache, class_loader); + } + return resolved; +} + +inline ObjPtr ClassLinker::LookupResolvedType(dex::TypeIndex type_idx, + ObjPtr 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->GetDexCache(), referrer->GetClassLoader()); + } + return type; +} + +inline ObjPtr ClassLinker::LookupResolvedType(dex::TypeIndex type_idx, + ArtMethod* 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->GetDexCache(), referrer->GetClassLoader()); + } + return type; +} + +inline ObjPtr ClassLinker::LookupResolvedType( + dex::TypeIndex type_idx, + ObjPtr dex_cache, + ObjPtr class_loader) { + ObjPtr type = dex_cache->GetResolvedType(type_idx); + if (type == nullptr) { + type = DoLookupResolvedType(type_idx, dex_cache, class_loader); + } + return type; +} + template inline bool ClassLinker::CheckInvokeClassMismatch(ObjPtr dex_cache, InvokeType type, @@ -148,10 +208,9 @@ inline bool ClassLinker::CheckInvokeClassMismatch(ObjPtr dex_c dex_cache, type, [this, dex_cache, method_idx, class_loader]() REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = *dex_cache->GetDexFile(); - const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); + const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(method_idx); ObjPtr klass = - LookupResolvedType(dex_file, method_id.class_idx_, dex_cache, class_loader); + LookupResolvedType(method_id.class_idx_, dex_cache, class_loader); DCHECK(klass != nullptr); return klass; }); @@ -187,6 +246,8 @@ inline ArtMethod* ClassLinker::GetResolvedMethod(uint32_t method_idx, ArtMethod* // lookup in the context of the original method from where it steals the code. // However, we delay the GetInterfaceMethodIfProxy() until needed. DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor()); + // We do not need the read barrier for getting the DexCache for the initial resolved method + // lookup as both from-space and to-space copies point to the same native resolved methods array. ArtMethod* resolved_method = referrer->GetDexCache()->GetResolvedMethod( method_idx, image_pointer_size_); if (resolved_method == nullptr) { @@ -228,6 +289,8 @@ inline ArtMethod* ClassLinker::ResolveMethod(Thread* self, // However, we delay the GetInterfaceMethodIfProxy() until needed. DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor()); Thread::PoisonObjectPointersIfDebug(); + // We do not need the read barrier for getting the DexCache for the initial resolved method + // lookup as both from-space and to-space copies point to the same native resolved methods array. ArtMethod* resolved_method = referrer->GetDexCache()->GetResolvedMethod( method_idx, image_pointer_size_); DCHECK(resolved_method == nullptr || !resolved_method->IsRuntimeMethod()); @@ -278,10 +341,13 @@ inline ArtMethod* ClassLinker::ResolveMethod(Thread* self, inline ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx, ArtMethod* referrer, bool is_static) { - ObjPtr dex_cache = referrer->GetDexCache(); - ArtField* field = dex_cache->GetResolvedField(field_idx, image_pointer_size_); + // We do not need the read barrier for getting the DexCache for the initial resolved field + // lookup as both from-space and to-space copies point to the same native resolved fields array. + ArtField* field = referrer->GetDexCache()->GetResolvedField( + field_idx, image_pointer_size_); if (field == nullptr) { - field = LookupResolvedField(field_idx, dex_cache, referrer->GetClassLoader(), is_static); + ObjPtr class_loader = referrer->GetDeclaringClass()->GetClassLoader(); + field = LookupResolvedField(field_idx, referrer->GetDexCache(), class_loader, is_static); } return field; } @@ -290,13 +356,15 @@ inline ArtField* ClassLinker::ResolveField(uint32_t field_idx, ArtMethod* referrer, bool is_static) { Thread::PoisonObjectPointersIfDebug(); - ObjPtr declaring_class = referrer->GetDeclaringClass(); - ArtField* resolved_field = - referrer->GetDexCache()->GetResolvedField(field_idx, image_pointer_size_); + // We do not need the read barrier for getting the DexCache for the initial resolved field + // lookup as both from-space and to-space copies point to the same native resolved fields array. + ArtField* resolved_field = referrer->GetDexCache()->GetResolvedField( + field_idx, image_pointer_size_); if (UNLIKELY(resolved_field == nullptr)) { StackHandleScope<2> hs(Thread::Current()); + ObjPtr referring_class = referrer->GetDeclaringClass(); Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); - Handle class_loader(hs.NewHandle(declaring_class->GetClassLoader())); + Handle class_loader(hs.NewHandle(referring_class->GetClassLoader())); resolved_field = ResolveField(field_idx, dex_cache, class_loader, is_static); // Note: We cannot check here to see whether we added the field to the cache. The type // might be an erroneous class, which results in it being hidden from us. diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 892c28d54f..55fa6328f5 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -5466,7 +5466,7 @@ bool ClassLinker::LoadSuperAndInterfaces(Handle klass, const DexF return false; } - ObjPtr super_class = ResolveType(dex_file, super_class_idx, klass.Get()); + ObjPtr super_class = ResolveType(super_class_idx, klass.Get()); if (super_class == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return false; @@ -5485,7 +5485,7 @@ bool ClassLinker::LoadSuperAndInterfaces(Handle klass, const DexF if (interfaces != nullptr) { for (size_t i = 0; i < interfaces->Size(); i++) { dex::TypeIndex idx = interfaces->GetTypeItem(i).type_idx_; - ObjPtr interface = ResolveType(dex_file, idx, klass.Get()); + ObjPtr interface = ResolveType(idx, klass.Get()); if (interface == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return false; @@ -7762,74 +7762,56 @@ ObjPtr ClassLinker::LookupString(dex::StringIndex string_idx, return string; } -ObjPtr ClassLinker::LookupResolvedType(const DexFile& dex_file, - dex::TypeIndex type_idx, - ObjPtr dex_cache, - ObjPtr class_loader) { - ObjPtr type = dex_cache->GetResolvedType(type_idx); - if (type == nullptr) { - const char* descriptor = dex_file.StringByTypeIdx(type_idx); - DCHECK_NE(*descriptor, '\0') << "descriptor is empty string"; - if (descriptor[1] == '\0') { - // only the descriptors of primitive types should be 1 character long, also avoid class lookup - // for primitive classes that aren't backed by dex files. - type = FindPrimitiveClass(descriptor[0]); +ObjPtr ClassLinker::DoLookupResolvedType(dex::TypeIndex type_idx, + ObjPtr dex_cache, + ObjPtr class_loader) { + const DexFile& dex_file = *dex_cache->GetDexFile(); + const char* descriptor = dex_file.StringByTypeIdx(type_idx); + DCHECK_NE(*descriptor, '\0') << "descriptor is empty string"; + ObjPtr type = nullptr; + if (descriptor[1] == '\0') { + // only the descriptors of primitive types should be 1 character long, also avoid class lookup + // for primitive classes that aren't backed by dex files. + type = FindPrimitiveClass(descriptor[0]); + } else { + Thread* const self = Thread::Current(); + 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()); + } + if (type != nullptr) { + if (type->IsResolved()) { + dex_cache->SetResolvedType(type_idx, type); } else { - Thread* const self = Thread::Current(); - 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()); - } - if (type != nullptr) { - if (type->IsResolved()) { - dex_cache->SetResolvedType(type_idx, type); - } else { - type = nullptr; - } + type = nullptr; } } - DCHECK(type == nullptr || type->IsResolved()); return type; } -ObjPtr ClassLinker::ResolveType(const DexFile& dex_file, - 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 ResolveType(dex_file, type_idx, dex_cache, class_loader); -} - -ObjPtr ClassLinker::ResolveType(const DexFile& dex_file, - dex::TypeIndex type_idx, - Handle dex_cache, - Handle class_loader) { - DCHECK(dex_cache != nullptr); - Thread::PoisonObjectPointersIfDebug(); - ObjPtr resolved = dex_cache->GetResolvedType(type_idx); - if (resolved == nullptr) { - Thread* self = Thread::Current(); - const char* descriptor = dex_file.StringByTypeIdx(type_idx); - resolved = FindClass(self, descriptor, class_loader); - if (resolved != nullptr) { - // TODO: we used to throw here if resolved's class loader was not the - // boot class loader. This was to permit different classes with the - // same name to be loaded simultaneously by different loaders - dex_cache->SetResolvedType(type_idx, resolved); - } else { - CHECK(self->IsExceptionPending()) - << "Expected pending exception for failed resolution of: " << descriptor; - // Convert a ClassNotFoundException to a NoClassDefFoundError. - StackHandleScope<1> hs(self); - Handle cause(hs.NewHandle(self->GetException())); - if (cause->InstanceOf(GetClassRoot(kJavaLangClassNotFoundException))) { - DCHECK(resolved == nullptr); // No Handle needed to preserve resolved. - self->ClearException(); - ThrowNoClassDefFoundError("Failed resolution of: %s", descriptor); - self->GetException()->SetCause(cause.Get()); - } +ObjPtr ClassLinker::DoResolveType(dex::TypeIndex type_idx, + Handle dex_cache, + Handle class_loader) { + Thread* self = Thread::Current(); + const char* descriptor = dex_cache->GetDexFile()->StringByTypeIdx(type_idx); + ObjPtr resolved = FindClass(self, descriptor, class_loader); + if (resolved != nullptr) { + // TODO: we used to throw here if resolved's class loader was not the + // boot class loader. This was to permit different classes with the + // same name to be loaded simultaneously by different loaders + dex_cache->SetResolvedType(type_idx, resolved); + } else { + CHECK(self->IsExceptionPending()) + << "Expected pending exception for failed resolution of: " << descriptor; + // Convert a ClassNotFoundException to a NoClassDefFoundError. + StackHandleScope<1> hs(self); + Handle cause(hs.NewHandle(self->GetException())); + if (cause->InstanceOf(GetClassRoot(kJavaLangClassNotFoundException))) { + DCHECK(resolved == nullptr); // No Handle needed to preserve resolved. + self->ClearException(); + ThrowNoClassDefFoundError("Failed resolution of: %s", descriptor); + self->GetException()->SetCause(cause.Get()); } } DCHECK((resolved == nullptr) || resolved->IsResolved()) @@ -7962,7 +7944,7 @@ ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, if (valid_dex_cache_method) { // We have a valid method from the DexCache but we need to perform ICCE and IAE checks. DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); - klass = LookupResolvedType(dex_file, method_id.class_idx_, dex_cache.Get(), class_loader.Get()); + klass = LookupResolvedType(method_id.class_idx_, dex_cache.Get(), class_loader.Get()); if (UNLIKELY(klass == nullptr)) { const char* descriptor = dex_file.StringByTypeIdx(method_id.class_idx_); LOG(FATAL) << "Check failed: klass != nullptr Bug: 64759619 Method: " @@ -7973,7 +7955,7 @@ ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, } } else { // The method was not in the DexCache, resolve the declaring class. - klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); + klass = ResolveType(method_id.class_idx_, dex_cache, class_loader); if (klass == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; @@ -8059,10 +8041,8 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx, return resolved; } // Fail, get the declaring class. - const DexFile& dex_file = *dex_cache->GetDexFile(); - const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); - ObjPtr klass = - ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); + const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(method_idx); + ObjPtr klass = ResolveType(method_id.class_idx_, dex_cache, class_loader); if (klass == nullptr) { Thread::Current()->AssertPendingException(); return nullptr; @@ -8084,7 +8064,7 @@ ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx, const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); ObjPtr klass = dex_cache->GetResolvedType(field_id.class_idx_); if (klass == nullptr) { - klass = LookupResolvedType(dex_file, field_id.class_idx_, dex_cache, class_loader); + klass = LookupResolvedType(field_id.class_idx_, dex_cache, class_loader); } if (klass == nullptr) { // The class has not been resolved yet, so the field is also unresolved. @@ -8126,7 +8106,7 @@ 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(dex_file, field_id.class_idx_, dex_cache, class_loader); + ObjPtr klass = ResolveType(field_id.class_idx_, dex_cache, class_loader); if (klass == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; @@ -8167,7 +8147,7 @@ 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(dex_file, field_id.class_idx_, dex_cache, class_loader)); + ObjPtr klass = ResolveType(field_id.class_idx_, dex_cache, class_loader); if (klass == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; @@ -8203,7 +8183,7 @@ ObjPtr ClassLinker::ResolveMethodType( const DexFile& dex_file = *dex_cache->GetDexFile(); const DexFile::ProtoId& proto_id = dex_file.GetProtoId(proto_idx); Handle return_type(hs.NewHandle( - ResolveType(dex_file, proto_id.return_type_idx_, dex_cache, class_loader))); + ResolveType(proto_id.return_type_idx_, dex_cache, class_loader))); if (return_type == nullptr) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -8229,7 +8209,7 @@ ObjPtr ClassLinker::ResolveMethodType( MutableHandle param_class = hs.NewHandle(nullptr); for (; it.HasNext(); it.Next()) { const dex::TypeIndex type_idx = it.GetTypeIdx(); - param_class.Assign(ResolveType(dex_file, type_idx, dex_cache, class_loader)); + param_class.Assign(ResolveType(type_idx, dex_cache, class_loader)); if (param_class == nullptr) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -8514,7 +8494,7 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod( DexFileParameterIterator it(*dex_file, target_method->GetPrototype()); while (it.HasNext()) { const dex::TypeIndex type_idx = it.GetTypeIdx(); - ObjPtr klass = ResolveType(*dex_file, type_idx, dex_cache, class_loader); + ObjPtr klass = ResolveType(type_idx, dex_cache, class_loader); if (nullptr == klass) { DCHECK(self->IsExceptionPending()); return nullptr; diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 55a4d2db15..10562f0890 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -255,45 +255,50 @@ class ClassLinker { ObjPtr dex_cache) REQUIRES_SHARED(Locks::mutator_lock_); - // Resolve a Type with the given index from the DexFile, storing the - // result in the DexCache. The referrer is used to identify the - // target DexCache and ClassLoader to use for resolution. - ObjPtr ResolveType(const DexFile& dex_file, - dex::TypeIndex type_idx, - ObjPtr referrer) + // 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, ObjPtr referrer) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - // Resolve a Type with the given index from the DexFile, storing the - // result in the DexCache. The referrer is used to identify the - // target DexCache and ClassLoader to use for resolution. + // 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, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - // Look up a resolved type with the given ID from the DexFile. The ClassLoader is used to search - // for the type, since it may be referenced from but not contained within the given DexFile. - ObjPtr LookupResolvedType(const DexFile& dex_file, - dex::TypeIndex type_idx, - ObjPtr dex_cache, - ObjPtr class_loader) - REQUIRES_SHARED(Locks::mutator_lock_); - static ObjPtr LookupResolvedType(dex::TypeIndex type_idx, - ObjPtr dex_cache, - ObjPtr class_loader) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Resolve a type with the given ID from the DexFile, storing the - // result in DexCache. The ClassLoader is used to search for the - // type, since it may be referenced from but not contained within - // the given DexFile. - ObjPtr ResolveType(const DexFile& dex_file, - dex::TypeIndex type_idx, + // Resolve a type with the given index from the DexFile associated with the given DexCache + // and ClassLoader, storing the result in DexCache. The ClassLoader is used to search for + // the type, since it may be referenced from but not contained within the DexFile. + ObjPtr ResolveType(dex::TypeIndex type_idx, Handle dex_cache, Handle class_loader) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); + // 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, + 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, ArtMethod* referrer) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Look up a resolved type with the given index from the DexFile associated with the given + // DexCache and ClassLoader. The ClassLoader is used to search for the type, since it may + // be referenced from but not contained within the DexFile. + ObjPtr LookupResolvedType(dex::TypeIndex type_idx, + ObjPtr dex_cache, + ObjPtr class_loader) + REQUIRES_SHARED(Locks::mutator_lock_); + // Determine whether a dex cache result should be trusted, or an IncompatibleClassChangeError // check and IllegalAccessError check should be performed even after a hit. enum class ResolveMode { // private. @@ -876,6 +881,19 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) 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 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, + Handle dex_cache, + Handle class_loader) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); + // 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, diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index b625c40fc3..246f89e5cc 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -954,15 +954,14 @@ TEST_F(ClassLinkerTest, LookupResolvedType) { ObjPtr klass = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader); dex::TypeIndex type_idx = klass->GetClassDef()->class_idx_; ObjPtr dex_cache = klass->GetDexCache(); - const DexFile& dex_file = klass->GetDexFile(); EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()), + class_linker_->LookupResolvedType(type_idx, dex_cache, class_loader.Get()), klass); // Zero out the resolved type and make sure LookupResolvedType still finds it. dex_cache->ClearResolvedType(type_idx); EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr); EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()), + class_linker_->LookupResolvedType(type_idx, dex_cache, class_loader.Get()), klass); } @@ -983,7 +982,7 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeArray) { dex::TypeIndex array_idx = dex_file.GetIndexForTypeId(*array_id); // Check that the array class wasn't resolved yet. EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(array_idx, dex_cache.Get(), class_loader.Get()), ObjPtr(nullptr)); // Resolve the array class we want to test. ObjPtr array_klass @@ -991,13 +990,13 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeArray) { ASSERT_OBJ_PTR_NE(array_klass, ObjPtr(nullptr)); // Test that LookupResolvedType() finds the array class. EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(array_idx, dex_cache.Get(), class_loader.Get()), array_klass); // Zero out the resolved type and make sure LookupResolvedType() still finds it. dex_cache->ClearResolvedType(array_idx); EXPECT_TRUE(dex_cache->GetResolvedType(array_idx) == nullptr); EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(array_idx, dex_cache.Get(), class_loader.Get()), array_klass); } @@ -1012,15 +1011,14 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeErroneousInit) { ASSERT_OBJ_PTR_NE(klass.Get(), ObjPtr(nullptr)); dex::TypeIndex type_idx = klass->GetClassDef()->class_idx_; Handle dex_cache = hs.NewHandle(klass->GetDexCache()); - const DexFile& dex_file = klass->GetDexFile(); EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(type_idx, dex_cache.Get(), class_loader.Get()), klass.Get()); // Zero out the resolved type and make sure LookupResolvedType still finds it. dex_cache->ClearResolvedType(type_idx); EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr); EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(type_idx, dex_cache.Get(), class_loader.Get()), klass.Get()); // Force initialization to turn the class erroneous. bool initialized = class_linker_->EnsureInitialized(soa.Self(), @@ -1032,13 +1030,13 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeErroneousInit) { soa.Self()->ClearException(); // Check that the LookupResolvedType() can still find the resolved type. EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(type_idx, dex_cache.Get(), class_loader.Get()), klass.Get()); // Zero out the resolved type and make sure LookupResolvedType() still finds it. dex_cache->ClearResolvedType(type_idx); EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr); EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(type_idx, dex_cache.Get(), class_loader.Get()), klass.Get()); } diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index 43260f733b..bc7b985b41 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -343,8 +343,7 @@ mirror::Object* ProcessEncodedAnnotation(const ClassData& klass, const uint8_t** StackHandleScope<4> hs(self); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle annotation_class(hs.NewHandle( - class_linker->ResolveType(klass.GetDexFile(), - dex::TypeIndex(type_index), + class_linker->ResolveType(dex::TypeIndex(type_index), hs.NewHandle(klass.GetDexCache()), hs.NewHandle(klass.GetClassLoader())))); if (annotation_class == nullptr) { @@ -474,7 +473,6 @@ bool ProcessAnnotationValue(const ClassData& klass, dex::TypeIndex type_index(index); StackHandleScope<2> hs(self); element_object = Runtime::Current()->GetClassLinker()->ResolveType( - klass.GetDexFile(), type_index, hs.NewHandle(klass.GetDexCache()), hs.NewHandle(klass.GetClassLoader())); @@ -782,7 +780,6 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet( Thread* self = Thread::Current(); StackHandleScope<2> hs(self); ObjPtr resolved_class = class_linker->ResolveType( - klass.GetDexFile(), dex::TypeIndex(type_index), hs.NewHandle(klass.GetDexCache()), hs.NewHandle(klass.GetClassLoader())); @@ -1594,8 +1591,7 @@ void RuntimeEncodedStaticFieldValueIterator::ReadValueToField(ArtField* field) c break; } case kType: { - ObjPtr resolved = linker_->ResolveType(dex_file_, - dex::TypeIndex(jval_.i), + ObjPtr resolved = linker_->ResolveType(dex::TypeIndex(jval_.i), dex_cache_, class_loader_); field->SetObject(field->GetDeclaringClass(), resolved); diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index fa3c027db8..9e5085067c 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -682,7 +682,7 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx, } else if (type == kSuper) { // TODO This lookup is rather slow. dex::TypeIndex method_type_idx = dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_; - ObjPtr method_reference_class = ClassLinker::LookupResolvedType( + ObjPtr method_reference_class = linker->LookupResolvedType( method_type_idx, dex_cache, referrer->GetClassLoader()); if (method_reference_class == nullptr) { // Need to do full type resolution... diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 0a76cddf5e..ca5b79921c 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -1250,17 +1250,8 @@ extern "C" const void* artQuickResolutionTrampoline( } else { DCHECK_EQ(invoke_type, kSuper); CHECK(caller != nullptr) << invoke_type; - StackHandleScope<2> hs(self); - Handle dex_cache( - hs.NewHandle(caller->GetDeclaringClass()->GetDexCache())); - Handle class_loader( - hs.NewHandle(caller->GetDeclaringClass()->GetClassLoader())); - // TODO Maybe put this into a mirror::Class function. ObjPtr ref_class = linker->LookupResolvedType( - *dex_cache->GetDexFile(), - dex_cache->GetDexFile()->GetMethodId(called_method.index).class_idx_, - dex_cache.Get(), - class_loader.Get()); + caller->GetDexFile()->GetMethodId(called_method.index).class_idx_, caller); if (ref_class->IsInterface()) { called = ref_class->FindVirtualMethodForInterfaceSuper(called, kRuntimePointerSize); } else { @@ -2580,9 +2571,8 @@ extern "C" uintptr_t artInvokePolymorphic( const Instruction& inst = code->InstructionAt(dex_pc); DCHECK(inst.Opcode() == Instruction::INVOKE_POLYMORPHIC || inst.Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE); - const DexFile* dex_file = caller_method->GetDexFile(); const uint32_t proto_idx = inst.VRegH(); - const char* shorty = dex_file->GetShorty(proto_idx); + 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. RememberForGcArgumentVisitor gc_visitor(sp, kMethodIsStatic, shorty, shorty_length, &soa); diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 4d7a576c06..122d1a816b 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -1143,8 +1143,7 @@ static ObjPtr InvokeBootstrapMethod(Thread* self, } case EncodedArrayValueIterator::ValueType::kType: { dex::TypeIndex idx(static_cast(jvalue.i)); - ObjPtr ref = - class_linker->ResolveType(*dex_file, idx, dex_cache, class_loader); + ObjPtr ref = class_linker->ResolveType(idx, dex_cache, class_loader); if (ref.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index eb54f7fb1f..b4f5d81067 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -440,7 +440,6 @@ inline bool Class::ResolvedFieldAccessTest(ObjPtr access_to, // cache. Use LookupResolveType here to search the class table if it is not in the dex cache. // should be no thread suspension due to the class being resolved. ObjPtr dex_access_to = Runtime::Current()->GetClassLinker()->LookupResolvedType( - *dex_cache->GetDexFile(), class_idx, dex_cache, access_to->GetClassLoader()); @@ -477,7 +476,6 @@ inline bool Class::ResolvedMethodAccessTest(ObjPtr access_to, // The referenced class has already been resolved with the method, but may not be in the dex // cache. ObjPtr dex_access_to = Runtime::Current()->GetClassLinker()->LookupResolvedType( - *dex_cache->GetDexFile(), class_idx, dex_cache, access_to->GetClassLoader()); diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index a0a2f46433..e0a341da67 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -1035,7 +1035,7 @@ ObjPtr Class::GetDirectInterface(Thread* self, ObjPtr klass, uint3 return interfaces->Get(idx); } else { dex::TypeIndex type_idx = klass->GetDirectInterfaceTypeIdx(idx); - ObjPtr interface = ClassLinker::LookupResolvedType( + ObjPtr interface = Runtime::Current()->GetClassLinker()->LookupResolvedType( type_idx, klass->GetDexCache(), klass->GetClassLoader()); return interface; } @@ -1047,9 +1047,7 @@ ObjPtr Class::ResolveDirectInterface(Thread* self, Handle klass, u DCHECK(!klass->IsArrayClass()); DCHECK(!klass->IsProxyClass()); dex::TypeIndex type_idx = klass->GetDirectInterfaceTypeIdx(idx); - interface = Runtime::Current()->GetClassLinker()->ResolveType(klass->GetDexFile(), - type_idx, - klass.Get()); + interface = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, klass.Get()); CHECK(interface != nullptr || self->IsExceptionPending()); } return interface; diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 2d1f886896..1b5c535c87 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -375,8 +375,8 @@ static void PreloadDexCachesResolveField(ObjPtr dex_cache, } const DexFile* dex_file = dex_cache->GetDexFile(); const DexFile::FieldId& field_id = dex_file->GetFieldId(field_idx); - ObjPtr klass = - ClassLinker::LookupResolvedType(field_id.class_idx_, dex_cache, nullptr); + ObjPtr klass = Runtime::Current()->GetClassLinker()->LookupResolvedType( + field_id.class_idx_, dex_cache, /* class_loader */ nullptr); if (klass == nullptr) { return; } @@ -401,8 +401,8 @@ static void PreloadDexCachesResolveMethod(ObjPtr dex_cache, ui } const DexFile* dex_file = dex_cache->GetDexFile(); const DexFile::MethodId& method_id = dex_file->GetMethodId(method_idx); - ObjPtr klass = - ClassLinker::LookupResolvedType(method_id.class_idx_, dex_cache, nullptr); + ObjPtr klass = Runtime::Current()->GetClassLinker()->LookupResolvedType( + method_id.class_idx_, dex_cache, /* class_loader */ nullptr); if (klass == nullptr) { return; } diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 9359ffc7fd..da5cee1ddc 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -146,11 +146,11 @@ 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(ClassLinker::LookupResolvedType( - type_idx, klass->GetDexCache(), klass->GetClassLoader())); + interface.Assign(linker->LookupResolvedType(type_idx, klass.Get())); ifaces->SetWithoutChecks(i, interface.Get()); } diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index bf02450744..4ff49edb90 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -1077,9 +1077,8 @@ bool MethodVerifier::ScanTryCatchBlocks() { // Ensure exception types are resolved so that they don't need resolution to be delivered, // unresolved exception types will be ignored by exception delivery if (iterator.GetHandlerTypeIndex().IsValid()) { - ObjPtr exception_type = linker->ResolveType(*dex_file_, - iterator.GetHandlerTypeIndex(), - dex_cache_, class_loader_); + ObjPtr exception_type = + linker->ResolveType(iterator.GetHandlerTypeIndex(), dex_cache_, class_loader_); if (exception_type == nullptr) { DCHECK(self_->IsExceptionPending()); self_->ClearException(); @@ -2434,8 +2433,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { const RegType& res_type = ResolveClass(type_idx); if (res_type.IsConflict()) { // If this is a primitive type, fail HARD. - ObjPtr klass = - ClassLinker::LookupResolvedType(type_idx, dex_cache_.Get(), class_loader_.Get()); + ObjPtr klass = Runtime::Current()->GetClassLinker()->LookupResolvedType( + type_idx, dex_cache_.Get(), class_loader_.Get()); if (klass != nullptr && klass->IsPrimitive()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "using primitive type " << dex_file_->StringByTypeIdx(type_idx) << " in instanceof in " @@ -3643,7 +3642,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } else { // It is also a catch-all if it is java.lang.Throwable. ObjPtr klass = - linker->ResolveType(*dex_file_, handler_type_idx, dex_cache_, class_loader_); + linker->ResolveType(handler_type_idx, dex_cache_, class_loader_); if (klass != nullptr) { if (klass == mirror::Throwable::GetJavaLangThrowable()) { has_catch_all_handler = true; @@ -3767,10 +3766,10 @@ inline bool MethodVerifier::IsInstantiableOrPrimitive(ObjPtr klas template const RegType& MethodVerifier::ResolveClass(dex::TypeIndex class_idx) { + ClassLinker* linker = Runtime::Current()->GetClassLinker(); ObjPtr klass = can_load_classes_ - ? Runtime::Current()->GetClassLinker()->ResolveType( - *dex_file_, class_idx, dex_cache_, class_loader_) - : ClassLinker::LookupResolvedType(class_idx, dex_cache_.Get(), class_loader_.Get()); + ? linker->ResolveType(class_idx, dex_cache_, class_loader_) + : linker->LookupResolvedType(class_idx, dex_cache_.Get(), class_loader_.Get()); if (can_load_classes_ && klass == nullptr) { DCHECK(self_->IsExceptionPending()); self_->ClearException(); -- GitLab From 8a4d1e5f1e4a7b27b894ce9292764f23bf8656bc Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 11 Dec 2017 13:30:38 +0000 Subject: [PATCH 165/226] ART: Give 667-jit-jni-stub more time for compiling. Test: Rely on TreeHugger Bug: 70426338 Change-Id: I35df51b4ed6f7bb2121d63ed65861fdbad73b004 --- test/667-jit-jni-stub/src/Main.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/667-jit-jni-stub/src/Main.java b/test/667-jit-jni-stub/src/Main.java index b867970eab..794308d6e1 100644 --- a/test/667-jit-jni-stub/src/Main.java +++ b/test/667-jit-jni-stub/src/Main.java @@ -135,13 +135,16 @@ public class Main { int count = 0; while (!hasJitCompiledEntrypoint(Main.class, "callThrough")) { // If `call` is true, also exercise the `callThrough()` method to increase hotness. - int limit = call ? 1 << Math.min(count, 12) : 0; + // Ramp-up the number of calls we do up to 1 << 12. + final int rampUpCutOff = 12; + int limit = call ? 1 << Math.min(count, rampUpCutOff) : 0; for (int i = 0; i < limit; ++i) { callThrough(Main.class, "doNothing"); } try { - // Sleep to give a chance for the JIT to compile `hasJit` stub. - Thread.sleep(100); + // Sleep to give a chance for the JIT to compile `callThrough` stub. + // After the ramp-up phase, give the JIT even more time to compile. + Thread.sleep(count >= rampUpCutOff ? 200 : 100); } catch (Exception e) { // Ignore } -- GitLab From 1de1e11ac90db9fad8916ac43d43714ccb8d978f Mon Sep 17 00:00:00 2001 From: Artem Serov Date: Thu, 20 Jul 2017 16:33:59 +0100 Subject: [PATCH 166/226] ART: Try to statically evaluate some conditions. If a condition 'cond' is evaluated in an HIf instruction then in the successors of the this HIF_BLOCK we statically know the value of the condition (TRUE in TRUE_SUCC, FALSE in FALSE_SUCC). Using that we could 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) { ... } ... int a = cond ? 5 : 105; ... } The patch is a prerequisite step for "Loop peeling to eliminate invariant exits" however it brings some value on its own with a tiny code size reduction in boot-framework.oat (-8Kb). Test: 458-checker-instruct-simplification Test: test-art-target, test-art-host. Change-Id: Ifbe45097dc2b5f098176fa1a1d023ea90b76d396 --- compiler/optimizing/instruction_simplifier.cc | 21 +++ compiler/optimizing/nodes.cc | 14 +- compiler/optimizing/nodes.h | 16 +- .../src/Main.java | 138 ++++++++++++++++++ 4 files changed, 182 insertions(+), 7 deletions(-) diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 4c18e16c48..4c98d8323d 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -994,6 +994,27 @@ void InstructionSimplifierVisitor::VisitIf(HIf* instruction) { instruction->GetBlock()->SwapSuccessors(); RecordSimplification(); } + HInstruction* input = 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 (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 (!input->IsConstant()) { + HBasicBlock* true_succ = instruction->IfTrueSuccessor(); + HBasicBlock* false_succ = instruction->IfFalseSuccessor(); + + DCHECK_EQ(true_succ->GetPredecessors().size(), 1u); + input->ReplaceUsesDominatedBy( + true_succ->GetFirstInstruction(), GetGraph()->GetIntConstant(1), /* strictly */ false); + RecordSimplification(); + + DCHECK_EQ(false_succ->GetPredecessors().size(), 1u); + input->ReplaceUsesDominatedBy( + false_succ->GetFirstInstruction(), GetGraph()->GetIntConstant(0), /* strictly */ false); + RecordSimplification(); + } } void InstructionSimplifierVisitor::VisitArrayLength(HArrayLength* instruction) { diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index fa580d9bed..dda0243d3d 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1110,10 +1110,10 @@ bool HInstructionList::FoundBefore(const HInstruction* instruction1, return true; } -bool HInstruction::StrictlyDominates(HInstruction* other_instruction) const { +bool HInstruction::Dominates(HInstruction* other_instruction, bool strictly) const { if (other_instruction == this) { // An instruction does not strictly dominate itself. - return false; + return !strictly; } HBasicBlock* block = GetBlock(); HBasicBlock* other_block = other_instruction->GetBlock(); @@ -1147,6 +1147,10 @@ bool HInstruction::StrictlyDominates(HInstruction* other_instruction) const { } } +bool HInstruction::StrictlyDominates(HInstruction* other_instruction) const { + return Dominates(other_instruction, /* strictly */ true); +} + void HInstruction::RemoveEnvironment() { RemoveEnvironmentUses(this); environment_ = nullptr; @@ -1169,14 +1173,16 @@ void HInstruction::ReplaceWith(HInstruction* other) { DCHECK(env_uses_.empty()); } -void HInstruction::ReplaceUsesDominatedBy(HInstruction* dominator, HInstruction* replacement) { +void HInstruction::ReplaceUsesDominatedBy(HInstruction* dominator, + HInstruction* replacement, + bool strictly) { const HUseList& uses = 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 (dominator->StrictlyDominates(user)) { + if (dominator->Dominates(user, strictly)) { user->ReplaceInput(replacement, index); } } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 66d5bfea32..07ab325a0e 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -2098,9 +2098,13 @@ class HInstruction : public ArenaObject { return IsRemovable() && !HasUses(); } - // Does this instruction strictly dominate `other_instruction`? - // Returns false if this instruction and `other_instruction` are the same. + // Does this instruction dominate (strictly or in regular sense depending on 'strictly') + // `other_instruction`? + // Returns '!strictly' if this instruction and `other_instruction` are the same. // Aborts if this instruction and `other_instruction` are both phis. + bool Dominates(HInstruction* other_instruction, bool strictly) const; + + // Return 'Dominates(other_instruction, /*strictly*/ true)'. bool StrictlyDominates(HInstruction* other_instruction) const; int GetId() const { return id_; } @@ -2161,7 +2165,13 @@ class HInstruction : public ArenaObject { void SetLocations(LocationSummary* locations) { locations_ = locations; } void ReplaceWith(HInstruction* instruction); - void ReplaceUsesDominatedBy(HInstruction* dominator, HInstruction* replacement); + + // Replace all uses of the instruction which are dominated by 'dominator' with 'replacement'. + // 'strictly' determines whether strict or regular domination relation should be checked. + void ReplaceUsesDominatedBy(HInstruction* dominator, + HInstruction* replacement, + bool strictly = true); + void ReplaceInput(HInstruction* replacement, size_t index); // This is almost the same as doing `ReplaceWith()`. But in this helper, the diff --git a/test/458-checker-instruct-simplification/src/Main.java b/test/458-checker-instruct-simplification/src/Main.java index 7797f31867..ccaba61722 100644 --- a/test/458-checker-instruct-simplification/src/Main.java +++ b/test/458-checker-instruct-simplification/src/Main.java @@ -2602,6 +2602,124 @@ public class Main { return (byte)((int)(((long)(b & 0xff)) & 255L)); } + /// CHECK-START: void Main.$noinline$testIfCondStaticEvaluation(int[], boolean) instruction_simplifier (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> Equal [<>,<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Equal [<>,<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] + /// CHECK-DAG: <> Equal [<>,<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.$noinline$testIfCondStaticEvaluation(int[], boolean) instruction_simplifier (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Equal [<>,<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] + /// CHECK-DAG: <> Equal [<>,<>] + /// CHECK-DAG: If [<>] + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.$noinline$testIfCondStaticEvaluation(int[], boolean) dead_code_elimination$after_inlining (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: If [<>] + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] + // + /// CHECK-NOT: IntConstant 0 + /// CHECK-NOT: If [<>] + /// CHECK-NOT: ArraySet + private static void $noinline$testIfCondStaticEvaluation(int[] a, boolean f) { + if (f) { + if (f) { + a[0] = 1; + } + } else { + if (f) { + a[0] = 0; + } + } + } + + /// CHECK-START: void Main.$noinline$testManualUnrollWithInvarExits(int[], boolean) instruction_simplifier (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> Equal [<>,<>] loop:none + /// CHECK-DAG: If [<>] loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] 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: <> NotEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.$noinline$testManualUnrollWithInvarExits(int[], boolean) instruction_simplifier (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: If [<>] loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] 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: <> NotEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.$noinline$testManualUnrollWithInvarExits(int[], boolean) dead_code_elimination$after_inlining (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: If [<>] loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] 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: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + private static void $noinline$testManualUnrollWithInvarExits(int[] a, boolean f) { + if (f) { + return; + } + a[0] = 1; + for (int i = 1; i < a.length; i++) { + if (f) { + return; + } + a[i] = 1; + } + } + + public static final int LENGTH = 1024; + + private static final void initArray(int[] a) { + for (int i = 0; i < a.length; i++) { + a[i] = 0; + } + } + public static void main(String[] args) { int arg = 123456; float floatArg = 123456.125f; @@ -2845,6 +2963,26 @@ public class Main { assertIntEquals(1, $noinline$bug68142795Boolean(true)); assertIntEquals(0x7f, $noinline$bug68142795Elaborate((byte) 0x7f)); assertIntEquals((byte) 0x80, $noinline$bug68142795Elaborate((byte) 0x80)); + + int[] array = new int[LENGTH]; + + array[0] = 0; + $noinline$testIfCondStaticEvaluation(array, true); + assertIntEquals(array[0], 1); + array[0] = 0; + $noinline$testIfCondStaticEvaluation(array, false); + assertIntEquals(array[0], 0); + + initArray(array); + $noinline$testManualUnrollWithInvarExits(array, false); + for (int i = 0; i < array.length; i++) { + assertIntEquals(array[i], 1); + } + initArray(array); + $noinline$testManualUnrollWithInvarExits(array, true); + for (int i = 0; i < array.length; i++) { + assertIntEquals(array[i], 0); + } } private static boolean $inline$true() { return true; } -- GitLab From 3cd9bcf0a834bc63f794f10e17796be7289daad8 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Tue, 12 Dec 2017 11:30:43 -0800 Subject: [PATCH 167/226] Add new VMDebug jdwp test to skips for prebuilt-libjdwp The functionality is not implemented in libjdwp. Test: ./art/tools/run-prebuilt-libjdwp-tests.sh --mode=host Bug: 69169846 Change-Id: If670fb2f4eadd0da786e2f3d37621718f47edc91 --- tools/libjdwp_art_failures.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/libjdwp_art_failures.txt b/tools/libjdwp_art_failures.txt index bf1c9370b0..0495e6dc1b 100644 --- a/tools/libjdwp_art_failures.txt +++ b/tools/libjdwp_art_failures.txt @@ -67,7 +67,8 @@ { description: "Tests for VMDebug functionality not implemented in the upstream libjdwp", result: EXEC_FAILED, - name: "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest#testVMDebug" + names: [ "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest#testVMDebug", + "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest#testVMDebug002" ] }, /* TODO Categorize these failures more. */ { -- GitLab From 8d8a005ae3ae355dee88b3cb1351b93a070d4209 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 12 Dec 2017 12:03:04 -0800 Subject: [PATCH 168/226] ART: Reinstate secondary-image-patching exit Do not attempt imageless running with a stale secondary image. Instead prune the cache and exit, relying on init restarting the process. The combined image checksum is not written down into the boot image itself, only into apps. This should really be fixed. Follow-up to commit a463b6a920a2a0bf14f9cca20a561b412b9349d1. Bug: 30832951 Bug: 69428309 Bug: 70213235 Bug: 70498154 Test: m test-art-host Change-Id: I27ab3384ce4ffd93efb00a2e216cb839fe4357d5 --- runtime/gc/space/image_space.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 74813b4dd1..bcfc68c4a6 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1587,7 +1587,9 @@ std::unique_ptr ImageSpace::CreateBootImage(const char* image_locati if (!Runtime::Current()->IsImageDex2OatEnabled()) { local_error_msg = "Patching disabled."; } else if (secondary_image) { - local_error_msg = "Cannot patch a secondary image."; + // We really want a working image. Prune and restart. + PruneDalvikCache(image_isa); + _exit(1); } else if (ImageCreationAllowed(is_global_cache, image_isa, &local_error_msg)) { bool patch_success = RelocateImage(image_location, cache_filename.c_str(), image_isa, &local_error_msg); -- GitLab From 6f2a6341b6dd8910aebacefea653f11006b46abe Mon Sep 17 00:00:00 2001 From: Alex Light Date: Tue, 12 Dec 2017 09:55:05 -0800 Subject: [PATCH 169/226] Clean up JVMTI DDMS extension function. We change it to not return failure (and print a warning) if the chunk handler returns an empty chunk. This is a surprisingly common result in real-world code and was causing significant log-spam. Bug: 70559172 Test: ./test.py --host -j50 Change-Id: I1ba0f43cb2e834b09f51db75ec9100d97e916b62 --- openjdkjvmti/ti_ddms.cc | 24 ++++++----- openjdkjvmti/ti_extension.cc | 2 +- runtime/debugger.cc | 7 +--- test/1940-ddms-ext/expected.txt | 8 ++++ test/1940-ddms-ext/src-art/art/Test1940.java | 43 ++++++++++++++------ 5 files changed, 56 insertions(+), 28 deletions(-) diff --git a/openjdkjvmti/ti_ddms.cc b/openjdkjvmti/ti_ddms.cc index 500a453f78..0b4906d798 100644 --- a/openjdkjvmti/ti_ddms.cc +++ b/openjdkjvmti/ti_ddms.cc @@ -49,14 +49,16 @@ jvmtiError DDMSUtil::HandleChunk(jvmtiEnv* env, /*out*/jint* type_out, /*out*/jint* data_length_out, /*out*/jbyte** data_out) { - constexpr uint32_t kDdmHeaderSize = sizeof(uint32_t) * 2; - if (env == nullptr || data_in == nullptr || data_out == nullptr || data_length_out == nullptr) { + if (env == nullptr || type_out == nullptr || data_out == nullptr || data_length_out == nullptr) { return ERR(NULL_POINTER); - } else if (length_in < static_cast(kDdmHeaderSize)) { - // need to get type and length at least. + } else if (data_in == nullptr && length_in != 0) { + // Data-in shouldn't be null if we have data. return ERR(ILLEGAL_ARGUMENT); } + *data_length_out = 0; + *data_out = nullptr; + art::Thread* self = art::Thread::Current(); art::ScopedThreadStateChange(self, art::ThreadState::kNative); @@ -71,13 +73,15 @@ jvmtiError DDMSUtil::HandleChunk(jvmtiEnv* env, return ERR(INTERNAL); } else { jvmtiError error = OK; - JvmtiUniquePtr ret = AllocJvmtiUniquePtr(env, out_data.size(), &error); - if (error != OK) { - return error; + if (!out_data.empty()) { + JvmtiUniquePtr ret = AllocJvmtiUniquePtr(env, out_data.size(), &error); + if (error != OK) { + return error; + } + memcpy(ret.get(), out_data.data(), out_data.size()); + *data_out = ret.release(); + *data_length_out = static_cast(out_data.size()); } - memcpy(ret.get(), out_data.data(), out_data.size()); - *data_out = ret.release(); - *data_length_out = static_cast(out_data.size()); return OK; } } diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc index afd0723d0f..79a8cd6304 100644 --- a/openjdkjvmti/ti_extension.cc +++ b/openjdkjvmti/ti_extension.cc @@ -216,7 +216,7 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, { { "type_in", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, { "length_in", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, - { "data_in", JVMTI_KIND_IN_BUF, JVMTI_TYPE_JBYTE, false }, + { "data_in", JVMTI_KIND_IN_BUF, JVMTI_TYPE_JBYTE, true }, { "type_out", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false }, { "data_len_out", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false }, { "data_out", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_JBYTE, false } diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 13029fb958..b8f15af5dc 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -4376,15 +4376,12 @@ bool Dbg::DdmHandleChunk(JNIEnv* env, replyData.get(), offset, length); - if (length == 0 || replyData.get() == nullptr) { - return false; - } - out_data->resize(length); env->GetByteArrayRegion(replyData.get(), offset, length, reinterpret_cast(out_data->data())); + return true; } @@ -4418,7 +4415,7 @@ bool Dbg::DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pRep std::vector out_data; uint32_t out_type = 0; request->Skip(request_length); - if (!DdmHandleChunk(env, type, data, &out_type, &out_data)) { + if (!DdmHandleChunk(env, type, data, &out_type, &out_data) || out_data.empty()) { return false; } const uint32_t kDdmHeaderSize = 8; diff --git a/test/1940-ddms-ext/expected.txt b/test/1940-ddms-ext/expected.txt index 62d3b7bd4c..f12593f4db 100644 --- a/test/1940-ddms-ext/expected.txt +++ b/test/1940-ddms-ext/expected.txt @@ -3,8 +3,16 @@ MyDdmHandler: Chunk received: Chunk(Type: 0xDEADBEEF, Len: 8, data: [1, 2, 3, 4, MyDdmHandler: Putting value 0x800025 MyDdmHandler: Chunk returned: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, -128, 0, 37]) JVMTI returned chunk: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, -128, 0, 37]) +Sending empty data array +MyDdmHandler: Chunk received: Chunk(Type: 0xDEADBEEF, Len: 0, data: []) +MyDdmHandler: Putting value 0x1 +MyDdmHandler: Chunk returned: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, 0, 0, 1]) +JVMTI returned chunk: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, 0, 0, 1]) Sending chunk: Chunk(Type: 0xDEADBEEF, Len: 8, data: [9, 10, 11, 12, 13, 14, 15, 16]) Chunk published: Chunk(Type: 0xDEADBEEF, Len: 8, data: [9, 10, 11, 12, 13, 14, 15, 16]) +Sending data [1] to chunk handler -1412567295 +MyDdmHandler: Chunk received: Chunk(Type: 0xABCDEF01, Len: 1, data: [1]) +JVMTI returned chunk: Chunk(Type: 0xFADE7357, Len: 0, data: []) 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 9f79eaebba..55c40f4317 100644 --- a/test/1940-ddms-ext/src-art/art/Test1940.java +++ b/test/1940-ddms-ext/src-art/art/Test1940.java @@ -30,6 +30,7 @@ public class Test1940 { public static final int DDMS_HEADER_LENGTH = 8; public static final int MY_DDMS_TYPE = 0xDEADBEEF; public static final int MY_DDMS_RESPONSE_TYPE = 0xFADE7357; + public static final int MY_EMPTY_DDMS_TYPE = 0xABCDEF01; public static final boolean PRINT_ALL_CHUNKS = false; @@ -58,19 +59,24 @@ public class Test1940 { public void connected() {} public void disconnected() {} public Chunk handleChunk(Chunk req) { - // For this test we will simply calculate the checksum - checkEq(req.type, MY_DDMS_TYPE); System.out.println("MyDdmHandler: Chunk received: " + printChunk(req)); - ByteBuffer b = ByteBuffer.wrap(new byte[8]); - Adler32 a = new Adler32(); - a.update(req.data, req.offset, req.length); - b.order(ByteOrder.BIG_ENDIAN); - long val = a.getValue(); - b.putLong(val); - System.out.printf("MyDdmHandler: Putting value 0x%X\n", val); - Chunk ret = new Chunk(MY_DDMS_RESPONSE_TYPE, b.array(), 0, 8); - System.out.println("MyDdmHandler: Chunk returned: " + printChunk(ret)); - return ret; + if (req.type == MY_DDMS_TYPE) { + // For this test we will simply calculate the checksum + ByteBuffer b = ByteBuffer.wrap(new byte[8]); + Adler32 a = new Adler32(); + a.update(req.data, req.offset, req.length); + b.order(ByteOrder.BIG_ENDIAN); + long val = a.getValue(); + b.putLong(val); + System.out.printf("MyDdmHandler: Putting value 0x%X\n", val); + Chunk ret = new Chunk(MY_DDMS_RESPONSE_TYPE, b.array(), 0, 8); + System.out.println("MyDdmHandler: Chunk returned: " + printChunk(ret)); + return ret; + } else if (req.type == MY_EMPTY_DDMS_TYPE) { + return new Chunk(MY_DDMS_RESPONSE_TYPE, new byte[0], 0, 0); + } else { + throw new TestError("Unknown ddm request type: " + req.type); + } } } @@ -113,18 +119,31 @@ public class Test1940 { Test1940.class.getDeclaredMethod("HandlePublish", Integer.TYPE, new byte[0].getClass())); // Test sending chunk directly. DdmServer.registerHandler(MY_DDMS_TYPE, SINGLE_HANDLER); + DdmServer.registerHandler(MY_EMPTY_DDMS_TYPE, SINGLE_HANDLER); DdmServer.registrationComplete(); byte[] data = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; System.out.println("Sending data " + Arrays.toString(data)); Chunk res = processChunk(data); System.out.println("JVMTI returned chunk: " + printChunk(res)); + // Test sending an empty chunk. + System.out.println("Sending empty data array"); + res = processChunk(new byte[0]); + System.out.println("JVMTI returned chunk: " + printChunk(res)); + // Test sending chunk through DdmServer#sendChunk Chunk c = new Chunk( MY_DDMS_TYPE, new byte[] { 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }, 0, 8); System.out.println("Sending chunk: " + printChunk(c)); DdmServer.sendChunk(c); + // Test getting back an empty chunk. + data = new byte[] { 0x1 }; + System.out.println( + "Sending data " + Arrays.toString(data) + " to chunk handler " + MY_EMPTY_DDMS_TYPE); + res = processChunk(new Chunk(MY_EMPTY_DDMS_TYPE, data, 0, 1)); + System.out.println("JVMTI returned chunk: " + printChunk(res)); + // Test thread chunks are sent. final boolean[] types_seen = new boolean[] { false, false, false }; CURRENT_HANDLER = (type, cdata) -> { -- GitLab From e5463a88039a14c1d1d501d2932d069e6d4224e2 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Tue, 12 Dec 2017 13:33:28 -0800 Subject: [PATCH 170/226] Ensure that DDM processing doesn't leave unhandled exceptions Previously if a DDM chunk handler returns an invalid chunk the thread that processed the chunk could end up with an unhandled exception. This could be troublesome if it happened on a debugger thread, which might never actually handle it. Test: ./test.py --host -j50 Bug: 70570850 Change-Id: Ic67db951d721cf7532b08c0cc3f35bed9396d52d --- runtime/debugger.cc | 8 ++++++++ test/1940-ddms-ext/check | 21 ++++++++++++++++++++ test/1940-ddms-ext/expected.txt | 3 +++ test/1940-ddms-ext/src-art/art/Test1940.java | 15 ++++++++++++++ 4 files changed, 47 insertions(+) create mode 100755 test/1940-ddms-ext/check diff --git a/runtime/debugger.cc b/runtime/debugger.cc index b8f15af5dc..a6efdf5750 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -4382,6 +4382,14 @@ bool Dbg::DdmHandleChunk(JNIEnv* env, length, reinterpret_cast(out_data->data())); + if (env->ExceptionCheck()) { + LOG(INFO) << StringPrintf("Exception thrown when reading response data from dispatcher 0x%08x", + type); + env->ExceptionDescribe(); + env->ExceptionClear(); + return false; + } + return true; } diff --git a/test/1940-ddms-ext/check b/test/1940-ddms-ext/check new file mode 100755 index 0000000000..d2c03841fc --- /dev/null +++ b/test/1940-ddms-ext/check @@ -0,0 +1,21 @@ +#!/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. +sed -e '/\t.*$/d' "$2" | sed -e '/java.lang.ArrayIndexOutOfBoundsException:.*$/d' > "$2.tmp" + +./default-check "$1" "$2.tmp" diff --git a/test/1940-ddms-ext/expected.txt b/test/1940-ddms-ext/expected.txt index f12593f4db..1a457a01a5 100644 --- a/test/1940-ddms-ext/expected.txt +++ b/test/1940-ddms-ext/expected.txt @@ -13,6 +13,9 @@ Chunk published: Chunk(Type: 0xDEADBEEF, Len: 8, data: [9, 10, 11, 12, 13, 14, 1 Sending data [1] to chunk handler -1412567295 MyDdmHandler: Chunk received: Chunk(Type: 0xABCDEF01, Len: 1, data: [1]) 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 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 55c40f4317..226fe350bd 100644 --- a/test/1940-ddms-ext/src-art/art/Test1940.java +++ b/test/1940-ddms-ext/src-art/art/Test1940.java @@ -31,6 +31,7 @@ public class Test1940 { public static final int MY_DDMS_TYPE = 0xDEADBEEF; public static final int MY_DDMS_RESPONSE_TYPE = 0xFADE7357; public static final int MY_EMPTY_DDMS_TYPE = 0xABCDEF01; + public static final int MY_INVALID_DDMS_TYPE = 0x12345678; public static final boolean PRINT_ALL_CHUNKS = false; @@ -74,6 +75,9 @@ public class Test1940 { return ret; } else if (req.type == MY_EMPTY_DDMS_TYPE) { return new Chunk(MY_DDMS_RESPONSE_TYPE, new byte[0], 0, 0); + } else if (req.type == MY_INVALID_DDMS_TYPE) { + // This is a very invalid chunk. + return new Chunk(MY_DDMS_RESPONSE_TYPE, new byte[] { 0 }, /*offset*/ 12, /*length*/ 55); } else { throw new TestError("Unknown ddm request type: " + req.type); } @@ -120,6 +124,7 @@ public class Test1940 { // Test sending chunk directly. DdmServer.registerHandler(MY_DDMS_TYPE, SINGLE_HANDLER); DdmServer.registerHandler(MY_EMPTY_DDMS_TYPE, SINGLE_HANDLER); + DdmServer.registerHandler(MY_INVALID_DDMS_TYPE, SINGLE_HANDLER); DdmServer.registrationComplete(); byte[] data = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; System.out.println("Sending data " + Arrays.toString(data)); @@ -144,6 +149,16 @@ public class Test1940 { res = processChunk(new Chunk(MY_EMPTY_DDMS_TYPE, data, 0, 1)); System.out.println("JVMTI returned chunk: " + printChunk(res)); + // Test getting back an invalid chunk. + System.out.println( + "Sending data " + Arrays.toString(data) + " to chunk handler " + MY_INVALID_DDMS_TYPE); + try { + res = processChunk(new Chunk(MY_INVALID_DDMS_TYPE, data, 0, 1)); + System.out.println("JVMTI returned chunk: " + printChunk(res)); + } catch (RuntimeException e) { + System.out.println("Got error: " + e.getMessage()); + } + // Test thread chunks are sent. final boolean[] types_seen = new boolean[] { false, false, false }; CURRENT_HANDLER = (type, cdata) -> { -- GitLab From 8187e0baaa634151a070dfbf6f933e1f529d7ef6 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 12 Dec 2017 12:03:04 -0800 Subject: [PATCH 171/226] ART: Reinstate secondary-image-patching exit Do not attempt imageless running with a stale secondary image. Instead prune the cache and exit, relying on init restarting the process. The combined image checksum is not written down into the boot image itself, only into apps. This should really be fixed. Follow-up to commit a463b6a920a2a0bf14f9cca20a561b412b9349d1. (cherry picked from commit 8d8a005ae3ae355dee88b3cb1351b93a070d4209) Bug: 30832951 Bug: 69428309 Bug: 70213235 Bug: 70498154 Test: m test-art-host Merged-In: I27ab3384ce4ffd93efb00a2e216cb839fe4357d5 Change-Id: I27ab3384ce4ffd93efb00a2e216cb839fe4357d5 --- runtime/gc/space/image_space.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index fe0d35f455..3aab4c6bba 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1574,7 +1574,9 @@ std::unique_ptr ImageSpace::CreateBootImage(const char* image_locati if (!Runtime::Current()->IsImageDex2OatEnabled()) { local_error_msg = "Patching disabled."; } else if (secondary_image) { - local_error_msg = "Cannot patch a secondary image."; + // We really want a working image. Prune and restart. + PruneDalvikCache(image_isa); + _exit(1); } else if (ImageCreationAllowed(is_global_cache, image_isa, &local_error_msg)) { bool patch_success = RelocateImage(image_location, cache_filename.c_str(), image_isa, &local_error_msg); -- GitLab From 33d0b4a0b1bf19fc2493b5d6ebfcb2bd1935c0bd Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 13 Dec 2017 09:01:46 +0000 Subject: [PATCH 172/226] Blacklist failing test. Change-Id: I322af2e66371a3858c1fb61a580491a5ce6cbdf3 --- tools/libjdwp_oj_art_failures.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/libjdwp_oj_art_failures.txt b/tools/libjdwp_oj_art_failures.txt index 145022e7eb..2049a42fb3 100644 --- a/tools/libjdwp_oj_art_failures.txt +++ b/tools/libjdwp_oj_art_failures.txt @@ -80,6 +80,11 @@ bug: 69591477, name: "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.ExitTest#testExit001" }, +{ + description: "Test for VMDebug functionality not implemented in the upstream libjdwp", + result: EXEC_FAILED, + name: "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest#testVMDebug002" +}, { description: "Test times out on fugu-debug", result: EXEC_FAILED, -- GitLab From 5791327fecd58ab256ebceb44d97e62d16ab8e54 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 13 Dec 2017 15:19:40 +0000 Subject: [PATCH 173/226] Fix typo in test name. Change-Id: I1e2d37069623798a6fba495da9bc17ce11f96725 --- tools/libjdwp_art_failures.txt | 2 +- tools/libjdwp_oj_art_failures.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/libjdwp_art_failures.txt b/tools/libjdwp_art_failures.txt index 0495e6dc1b..dd42171a70 100644 --- a/tools/libjdwp_art_failures.txt +++ b/tools/libjdwp_art_failures.txt @@ -68,7 +68,7 @@ description: "Tests for VMDebug functionality not implemented in the upstream libjdwp", result: EXEC_FAILED, names: [ "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest#testVMDebug", - "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest#testVMDebug002" ] + "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest002#testVMDebug" ] }, /* TODO Categorize these failures more. */ { diff --git a/tools/libjdwp_oj_art_failures.txt b/tools/libjdwp_oj_art_failures.txt index 2049a42fb3..556f2ba674 100644 --- a/tools/libjdwp_oj_art_failures.txt +++ b/tools/libjdwp_oj_art_failures.txt @@ -83,7 +83,7 @@ { description: "Test for VMDebug functionality not implemented in the upstream libjdwp", result: EXEC_FAILED, - name: "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest#testVMDebug002" + name: "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest002#testVMDebug" }, { description: "Test times out on fugu-debug", -- GitLab From 89ff8b23f7c4189ba82407d95c3100c2f397cf19 Mon Sep 17 00:00:00 2001 From: Artem Serov Date: Mon, 20 Nov 2017 11:51:05 +0000 Subject: [PATCH 174/226] ARM64: Workaround for the callee saved FP registers and SIMD. Treat as scheduling barriers those vector instructions whose live ranges exceed the vectorized loop boundaries. This is a workaround for the lack of notion of SIMD register in the compiler; around a call we have to save/restore all live SIMD&FP registers (only lower 64 bits of SIMD&FP registers are callee saved) so don't reorder such vector instructions. Test: 706-checker-scheduler, test-art-host, test-art-target Bug: 69667779 Change-Id: I31e57518339d41545a0c519f7299afe381a8286c --- compiler/optimizing/nodes_vector.h | 10 ++++ compiler/optimizing/scheduler.h | 5 ++ compiler/optimizing/scheduler_arm64.h | 14 ++++++ test/706-checker-scheduler/src/Main.java | 64 ++++++++++++++++++++++++ 4 files changed, 93 insertions(+) diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h index 096349fd73..87dff8403b 100644 --- a/compiler/optimizing/nodes_vector.h +++ b/compiler/optimizing/nodes_vector.h @@ -109,6 +109,16 @@ class HVecOperation : public HVariableInputSizeInstruction { // Assumes vector nodes cannot be moved by default. Each concrete implementation // that can be moved should override this method and return true. + // + // Note: similar approach is used for instruction scheduling (if it is turned on for the target): + // by default HScheduler::IsSchedulable returns false for a particular HVecOperation. + // HScheduler${ARCH}::IsSchedulable can be overridden to return true for an instruction (see + // scheduler_arm64.h for example) if it is safe to schedule it; in this case one *must* also + // look at/update HScheduler${ARCH}::IsSchedulingBarrier for this instruction. + // + // Note: For newly introduced vector instructions HScheduler${ARCH}::IsSchedulingBarrier must be + // altered to return true if the instruction might reside outside the SIMD loop body since SIMD + // registers are not kept alive across vector loop boundaries (yet). bool CanBeMoved() const OVERRIDE { return false; } // Tests if all data of a vector node (vector length and packed type) is equal. diff --git a/compiler/optimizing/scheduler.h b/compiler/optimizing/scheduler.h index bb7c353bc2..dfa077f7de 100644 --- a/compiler/optimizing/scheduler.h +++ b/compiler/optimizing/scheduler.h @@ -462,6 +462,11 @@ class HScheduler { // containing basic block from being scheduled. // This method is used to restrict scheduling to instructions that we know are // safe to handle. + // + // For newly introduced instructions by default HScheduler::IsSchedulable returns false. + // HScheduler${ARCH}::IsSchedulable can be overridden to return true for an instruction (see + // scheduler_arm64.h for example) if it is safe to schedule it; in this case one *must* also + // look at/update HScheduler${ARCH}::IsSchedulingBarrier for this instruction. virtual bool IsSchedulable(const HInstruction* instruction) const; bool IsSchedulable(const HBasicBlock* block) const; diff --git a/compiler/optimizing/scheduler_arm64.h b/compiler/optimizing/scheduler_arm64.h index 32f161f26a..f71cb5b784 100644 --- a/compiler/optimizing/scheduler_arm64.h +++ b/compiler/optimizing/scheduler_arm64.h @@ -151,6 +151,20 @@ class HSchedulerARM64 : public HScheduler { #undef CASE_INSTRUCTION_KIND } + // Treat as scheduling barriers those vector instructions whose live ranges exceed the vectorized + // loop boundaries. This is a workaround for the lack of notion of SIMD register in the compiler; + // around a call we have to save/restore all live SIMD&FP registers (only lower 64 bits of + // SIMD&FP registers are callee saved) so don't reorder such vector instructions. + // + // TODO: remove this when a proper support of SIMD registers is introduced to the compiler. + bool IsSchedulingBarrier(const HInstruction* instr) const OVERRIDE { + return HScheduler::IsSchedulingBarrier(instr) || + instr->IsVecReduce() || + instr->IsVecExtractScalar() || + instr->IsVecSetScalars() || + instr->IsVecReplicateScalar(); + } + private: SchedulingLatencyVisitorARM64 arm64_latency_visitor_; DISALLOW_COPY_AND_ASSIGN(HSchedulerARM64); diff --git a/test/706-checker-scheduler/src/Main.java b/test/706-checker-scheduler/src/Main.java index d21596d4bc..25e4fad714 100644 --- a/test/706-checker-scheduler/src/Main.java +++ b/test/706-checker-scheduler/src/Main.java @@ -523,7 +523,71 @@ public class Main { return res; } + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + private static final int ARRAY_SIZE = 32; + + // Check that VecReplicateScalar is not reordered. + /// CHECK-START-ARM64: void Main.testVecReplicateScalar() scheduler (before) + /// CHECK: Phi loop:<> outer_loop:none + /// CHECK: NewArray loop:<> outer_loop:none + /// CHECK: VecReplicateScalar loop:<> outer_loop:none + + /// CHECK-START-ARM64: void Main.testVecReplicateScalar() scheduler (after) + /// CHECK: Phi loop:<> outer_loop:none + /// CHECK: NewArray loop:<> outer_loop:none + /// CHECK: VecReplicateScalar loop:<> outer_loop:none + private static void testVecReplicateScalar() { + for (int j = 0; j <= 8; j++) { + int[] a = new int[ARRAY_SIZE]; + for (int i = 0; i < a.length; i++) { + a[i] += 1; + } + for (int i = 0; i < a.length; i++) { + expectEquals(1, a[i]); + } + } + } + + // Check that VecSetScalars, VecReduce, VecExtractScalar are not reordered. + /// CHECK-START-ARM64: void Main.testVecSetScalars() scheduler (before) + /// CHECK: Phi loop:<> outer_loop:none + /// CHECK: NewArray loop:<> outer_loop:none + /// CHECK: VecSetScalars loop:<> outer_loop:none + // + /// CHECK: VecReduce loop:<> outer_loop:none + /// CHECK: VecExtractScalar loop:<> outer_loop:none + /// CHECK: InvokeStaticOrDirect loop:<> outer_loop:none + /// CHECK: InvokeStaticOrDirect loop:<> outer_loop:none + + /// CHECK-START-ARM64: void Main.testVecSetScalars() scheduler (after) + /// CHECK: Phi loop:<> outer_loop:none + /// CHECK: NewArray loop:<> outer_loop:none + /// CHECK: VecSetScalars loop:<> outer_loop:none + // + /// CHECK: VecReduce loop:<> outer_loop:none + /// CHECK: VecExtractScalar loop:<> outer_loop:none + /// CHECK: InvokeStaticOrDirect loop:<> outer_loop:none + /// CHECK: InvokeStaticOrDirect loop:<> outer_loop:none + private static void testVecSetScalars() { + for (int j = 0; j <= 8; j++) { + int[] a = new int[ARRAY_SIZE]; + int s = 5; + for (int i = 0; i < ARRAY_SIZE; i++) { + s+=a[i]; + } + expectEquals(a[0], 0); + expectEquals(s, 5); + } + } + public static void main(String[] args) { + testVecSetScalars(); + testVecReplicateScalar(); if ((arrayAccess() + intDiv(10)) != -35) { System.out.println("FAIL"); } -- GitLab From e619f6c51d0ff99a00c63d3d092e0132522e8fc8 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 12 Dec 2017 16:00:01 +0000 Subject: [PATCH 175/226] X86: Clean up interface type check for heap poisoning. Test: ART_HEAP_POISONING=true m test-art-host-gtest Test: ART_HEAP_POISONING=true testrunner.py --host --optimizing Bug: 32577579 Change-Id: Icb2a838fc3a2ddcc2a50b72236afe2cd65f02e01 --- compiler/optimizing/code_generator_x86.cc | 83 ++++++++++---------- compiler/optimizing/code_generator_x86_64.cc | 81 ++++++++++--------- 2 files changed, 86 insertions(+), 78 deletions(-) diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 2e8170ecc4..952b00fce9 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -317,6 +317,13 @@ class TypeCheckSlowPathX86 : public SlowPathCode { CodeGeneratorX86* x86_codegen = down_cast(codegen); __ Bind(GetEntryLabel()); + if (kPoisonHeapReferences && + instruction_->IsCheckCast() && + instruction_->AsCheckCast()->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) { + // First, unpoison the `cls` reference that was poisoned for direct memory comparison. + __ UnpoisonHeapReference(locations->InAt(1).AsRegister()); + } + if (!is_fatal_) { SaveLiveRegisters(codegen, locations); } @@ -6369,7 +6376,7 @@ static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) { // interface pointer, one for loading the current interface. // 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 && !kPoisonHeapReferences) { + if (type_check_kind == TypeCheckKind::kInterfaceCheck) { return 2; } return 1 + NumberOfInstanceOfTemps(type_check_kind); @@ -6638,7 +6645,7 @@ static bool IsTypeCheckSlowPathFatal(TypeCheckKind type_check_kind, bool throws_ case TypeCheckKind::kArrayObjectCheck: return !throws_into_catch && !kEmitCompilerReadBarrier; case TypeCheckKind::kInterfaceCheck: - return !throws_into_catch && !kEmitCompilerReadBarrier && !kPoisonHeapReferences; + return !throws_into_catch && !kEmitCompilerReadBarrier; case TypeCheckKind::kArrayCheck: case TypeCheckKind::kUnresolvedCheck: return false; @@ -6849,44 +6856,40 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { break; case TypeCheckKind::kInterfaceCheck: { - // Fast path for the interface check. Since we compare with a memory location in the inner - // loop we would need to have cls poisoned. However unpoisoning cls would reset the - // conditional flags and cause the conditional jump to be incorrect. Therefore we just jump - // to the slow path if we are running under poisoning. - if (!kPoisonHeapReferences) { - // Try to avoid read barriers to improve the fast path. We can not get false positives by - // doing this. - // /* HeapReference */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - obj_loc, - class_offset, - kWithoutReadBarrier); - - // /* HeapReference */ temp = temp->iftable_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - temp_loc, - iftable_offset, - kWithoutReadBarrier); - // Iftable is never null. - __ movl(maybe_temp2_loc.AsRegister(), Address(temp, array_length_offset)); - // Loop through the iftable and check if any class matches. - NearLabel start_loop; - __ Bind(&start_loop); - // Need to subtract first to handle the empty array case. - __ subl(maybe_temp2_loc.AsRegister(), Immediate(2)); - __ j(kNegative, type_check_slow_path->GetEntryLabel()); - // Go to next interface if the classes do not match. - __ cmpl(cls.AsRegister(), - CodeGeneratorX86::ArrayAddress(temp, - maybe_temp2_loc, - TIMES_4, - object_array_data_offset)); - __ j(kNotEqual, &start_loop); - } else { - __ jmp(type_check_slow_path->GetEntryLabel()); - } + // 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_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kWithoutReadBarrier); + + // /* HeapReference */ temp = temp->iftable_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + temp_loc, + iftable_offset, + kWithoutReadBarrier); + // Iftable is never null. + __ movl(maybe_temp2_loc.AsRegister(), Address(temp, array_length_offset)); + // Maybe poison the `cls` for direct comparison with memory. + __ MaybePoisonHeapReference(cls.AsRegister()); + // Loop through the iftable and check if any class matches. + NearLabel start_loop; + __ Bind(&start_loop); + // Need to subtract first to handle the empty array case. + __ subl(maybe_temp2_loc.AsRegister(), Immediate(2)); + __ j(kNegative, type_check_slow_path->GetEntryLabel()); + // Go to next interface if the classes do not match. + __ cmpl(cls.AsRegister(), + CodeGeneratorX86::ArrayAddress(temp, + maybe_temp2_loc, + TIMES_4, + object_array_data_offset)); + __ j(kNotEqual, &start_loop); + // If `cls` was poisoned above, unpoison it. + __ MaybeUnpoisonHeapReference(cls.AsRegister()); break; } } diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index e25688c9a3..106531cd27 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -337,6 +337,13 @@ class TypeCheckSlowPathX86_64 : public SlowPathCode { CodeGeneratorX86_64* x86_64_codegen = down_cast(codegen); __ Bind(GetEntryLabel()); + if (kPoisonHeapReferences && + instruction_->IsCheckCast() && + instruction_->AsCheckCast()->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) { + // First, unpoison the `cls` reference that was poisoned for direct memory comparison. + __ UnpoisonHeapReference(locations->InAt(1).AsRegister()); + } + if (!is_fatal_) { SaveLiveRegisters(codegen, locations); } @@ -5729,7 +5736,7 @@ void InstructionCodeGeneratorX86_64::VisitThrow(HThrow* instruction) { } static bool CheckCastTypeCheckNeedsATemporary(TypeCheckKind type_check_kind) { - if (type_check_kind == TypeCheckKind::kInterfaceCheck && !kPoisonHeapReferences) { + if (type_check_kind == TypeCheckKind::kInterfaceCheck) { // We need a temporary for holding the iftable length. return true; } @@ -6019,7 +6026,7 @@ static bool IsTypeCheckSlowPathFatal(TypeCheckKind type_check_kind, bool throws_ case TypeCheckKind::kArrayObjectCheck: return !throws_into_catch && !kEmitCompilerReadBarrier; case TypeCheckKind::kInterfaceCheck: - return !throws_into_catch && !kEmitCompilerReadBarrier && !kPoisonHeapReferences; + return !throws_into_catch && !kEmitCompilerReadBarrier; case TypeCheckKind::kArrayCheck: case TypeCheckKind::kUnresolvedCheck: return false; @@ -6233,42 +6240,40 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { } case TypeCheckKind::kInterfaceCheck: - // Fast path for the interface check. We always go slow path for heap poisoning since - // unpoisoning cls would require an extra temp. - if (!kPoisonHeapReferences) { - // Try to avoid read barriers to improve the fast path. We can not get false positives by - // doing this. - // /* HeapReference */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - obj_loc, - class_offset, - kWithoutReadBarrier); - - // /* HeapReference */ temp = temp->iftable_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - temp_loc, - iftable_offset, - kWithoutReadBarrier); - // Iftable is never null. - __ movl(maybe_temp2_loc.AsRegister(), Address(temp, array_length_offset)); - // Loop through the iftable and check if any class matches. - NearLabel start_loop; - __ Bind(&start_loop); - // Need to subtract first to handle the empty array case. - __ subl(maybe_temp2_loc.AsRegister(), Immediate(2)); - __ j(kNegative, type_check_slow_path->GetEntryLabel()); - // Go to next interface if the classes do not match. - __ cmpl(cls.AsRegister(), - CodeGeneratorX86_64::ArrayAddress(temp, - maybe_temp2_loc, - TIMES_4, - object_array_data_offset)); - __ j(kNotEqual, &start_loop); // Return if same class. - } else { - __ jmp(type_check_slow_path->GetEntryLabel()); - } + // 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_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kWithoutReadBarrier); + + // /* HeapReference */ temp = temp->iftable_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + temp_loc, + iftable_offset, + kWithoutReadBarrier); + // Iftable is never null. + __ movl(maybe_temp2_loc.AsRegister(), Address(temp, array_length_offset)); + // Maybe poison the `cls` for direct comparison with memory. + __ MaybePoisonHeapReference(cls.AsRegister()); + // Loop through the iftable and check if any class matches. + NearLabel start_loop; + __ Bind(&start_loop); + // Need to subtract first to handle the empty array case. + __ subl(maybe_temp2_loc.AsRegister(), Immediate(2)); + __ j(kNegative, type_check_slow_path->GetEntryLabel()); + // Go to next interface if the classes do not match. + __ cmpl(cls.AsRegister(), + CodeGeneratorX86_64::ArrayAddress(temp, + maybe_temp2_loc, + TIMES_4, + object_array_data_offset)); + __ j(kNotEqual, &start_loop); // Return if same class. + // If `cls` was poisoned above, unpoison it. + __ MaybeUnpoisonHeapReference(cls.AsRegister()); break; } -- GitLab From e4f220d489a84873ae7eb0492e3d009bc274bfab Mon Sep 17 00:00:00 2001 From: Alex Light Date: Wed, 13 Dec 2017 10:13:50 -0800 Subject: [PATCH 176/226] Rename libjdwp expectations files. It was very confusing and difficult to remember which files went with which test configuration. Hopefully with this rename things will be clearer. Test: ./art/tools/run-jdwp-tests.sh --mode=host Test: ./art/tools/run-libjdwp-tests.sh --mode=host Test: ./art/tools/run-prebuilt-libjdwp-tests.sh --mode=host Change-Id: I5736187bab4b79e47699f3177c328ff2ddff5cba --- ...j_art_failures.txt => external_oj_libjdwp_art_failures.txt} | 3 +++ ...jdwp_art_failures.txt => prebuilt_libjdwp_art_failures.txt} | 3 +++ tools/run-libjdwp-tests.sh | 2 +- tools/run-prebuilt-libjdwp-tests.sh | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) rename tools/{libjdwp_oj_art_failures.txt => external_oj_libjdwp_art_failures.txt} (97%) rename tools/{libjdwp_art_failures.txt => prebuilt_libjdwp_art_failures.txt} (97%) diff --git a/tools/libjdwp_oj_art_failures.txt b/tools/external_oj_libjdwp_art_failures.txt similarity index 97% rename from tools/libjdwp_oj_art_failures.txt rename to tools/external_oj_libjdwp_art_failures.txt index 556f2ba674..23dbba8027 100644 --- a/tools/libjdwp_oj_art_failures.txt +++ b/tools/external_oj_libjdwp_art_failures.txt @@ -1,6 +1,9 @@ /* * This file contains expectations for ART's buildbot. The purpose of this file is * to temporarily list failing tests and not break the bots. + * + * This file contains the expectations for the 'libjdwp-aot' and 'libjdwp-jit' + * test groups on the chromium buildbot. */ [ { diff --git a/tools/libjdwp_art_failures.txt b/tools/prebuilt_libjdwp_art_failures.txt similarity index 97% rename from tools/libjdwp_art_failures.txt rename to tools/prebuilt_libjdwp_art_failures.txt index dd42171a70..7694a4c7e4 100644 --- a/tools/libjdwp_art_failures.txt +++ b/tools/prebuilt_libjdwp_art_failures.txt @@ -1,6 +1,9 @@ /* * This file contains expectations for ART's buildbot. The purpose of this file is * to temporarily list failing tests and not break the bots. + * + * This file contains the expectations for the 'prebuilt-libjdwp-aot' and + * 'prebuilt-libjdwp-jit' test groups on the chromium buildbot. */ [ { diff --git a/tools/run-libjdwp-tests.sh b/tools/run-libjdwp-tests.sh index 47e7c4595d..e116facd98 100755 --- a/tools/run-libjdwp-tests.sh +++ b/tools/run-libjdwp-tests.sh @@ -79,7 +79,7 @@ else args+=(-Xplugin:libopenjdkjvmti.so) fi -expect_path=$PWD/art/tools/libjdwp_oj_art_failures.txt +expect_path=$PWD/art/tools/external_oj_libjdwp_art_failures.txt function verbose_run() { echo "$@" env "$@" diff --git a/tools/run-prebuilt-libjdwp-tests.sh b/tools/run-prebuilt-libjdwp-tests.sh index 46c2a153a7..e7f028ae63 100755 --- a/tools/run-prebuilt-libjdwp-tests.sh +++ b/tools/run-prebuilt-libjdwp-tests.sh @@ -96,7 +96,7 @@ if [[ ! -f $plugin ]]; then fi props_path=$PWD/art/tools/libjdwp-compat.props -expect_path=$PWD/art/tools/libjdwp_art_failures.txt +expect_path=$PWD/art/tools/prebuilt_libjdwp_art_failures.txt function verbose_run() { echo "$@" -- GitLab From d47dea28cf430815ed8a33081490ad5ad87e1cd6 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Wed, 13 Dec 2017 10:19:38 -0800 Subject: [PATCH 177/226] Remove some Tests that are passing from external_oj_libjdwp_art_failures. The DDM test is passing and the VMDebug002 test is only failing sporadically on fugu due to timeouts. Test: ./art/tools/run-libjdwp-tests.sh --mode=host Change-Id: I78041b1bd2beebbb8e6424a63e0e40751bde20ed --- tools/external_oj_libjdwp_art_failures.txt | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/tools/external_oj_libjdwp_art_failures.txt b/tools/external_oj_libjdwp_art_failures.txt index 23dbba8027..1178af4852 100644 --- a/tools/external_oj_libjdwp_art_failures.txt +++ b/tools/external_oj_libjdwp_art_failures.txt @@ -71,27 +71,17 @@ bug: 69121056, name: "org.apache.harmony.jpda.tests.jdwp.ObjectReference.IsCollectedTest#testIsCollected001" }, -{ - description: "Test for ddms extensions that are not yet implemented", - result: EXEC_FAILED, - bug: 69169846, - name: "org.apache.harmony.jpda.tests.jdwp.DDM.DDMTest#testChunk001" -}, { description: "Test crashes", result: EXEC_FAILED, bug: 69591477, name: "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.ExitTest#testExit001" }, -{ - description: "Test for VMDebug functionality not implemented in the upstream libjdwp", - result: EXEC_FAILED, - name: "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest002#testVMDebug" -}, { description: "Test times out on fugu-debug", result: EXEC_FAILED, bug: 70459916, - name: "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest#testVMDebug" + names: [ "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest#testVMDebug", + "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest002#testVMDebug" ] } ] -- GitLab From ad7d26fa0afee37ff73bd82679925ae0d2cffad3 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Tue, 12 Dec 2017 15:58:21 -0800 Subject: [PATCH 178/226] Added choice of dexer to fuzzer scripts. Rationale: We know you have a choice when it comes to dexers. Thank you for flying...... Bug: 70576364 Test: dexfuzz and jfuzz scripts Change-Id: Id3d18cd76adb9b3099dbf039314ebeb855507b72 --- tools/jfuzz/README.md | 14 ++--- tools/jfuzz/run_dex_fuzz_test.py | 30 +++++----- tools/jfuzz/run_jfuzz_test.py | 97 ++++++++++++++++---------------- 3 files changed, 74 insertions(+), 67 deletions(-) diff --git a/tools/jfuzz/README.md b/tools/jfuzz/README.md index 4edfe1b35b..bee2396fea 100644 --- a/tools/jfuzz/README.md +++ b/tools/jfuzz/README.md @@ -50,7 +50,7 @@ How to start JFuzz testing [--report_script=SCRIPT] [--jfuzz_arg=ARG] [--true_divergence] - [--use_dx] + [--dexer=DEXER] where @@ -66,7 +66,7 @@ where --report_script : path to script called for each divergence --jfuzz_arg : argument for jfuzz --true_divergence : don't bisect timeout divergences - --use_dx : use dx (rather than jack) + --dexer=DEXER : use either dx, d8, or jack to obtain dex files How to start JFuzz nightly testing ================================== @@ -87,14 +87,14 @@ How to start J/DexFuzz testing (multi-layered) [--num_tests=NUM_TESTS] [--num_inputs=NUM_INPUTS] [--device=DEVICE] - [--use_dx] + [--dexer=DEXER] 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) - --use_dx : use dx (rather than jack) + --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 Background ========== diff --git a/tools/jfuzz/run_dex_fuzz_test.py b/tools/jfuzz/run_dex_fuzz_test.py index ca0aec01cc..fdff9c03fa 100755 --- a/tools/jfuzz/run_dex_fuzz_test.py +++ b/tools/jfuzz/run_dex_fuzz_test.py @@ -41,14 +41,14 @@ from common.common import RunCommand class DexFuzzTester(object): """Tester that feeds JFuzz programs into DexFuzz testing.""" - def __init__(self, num_tests, num_inputs, device, use_dx): + def __init__(self, num_tests, num_inputs, device, dexer): """Constructor for the tester. Args: num_tests: int, number of tests to run num_inputs: int, number of JFuzz programs to generate device: string, target device serial number (or None) - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer """ self._num_tests = num_tests self._num_inputs = num_inputs @@ -58,7 +58,7 @@ class DexFuzzTester(object): self._dexfuzz_dir = None self._inputs_dir = None self._dexfuzz_env = None - self._use_dx = use_dx + self._dexer = dexer def __enter__(self): """On entry, enters new temp directory after saving current directory. @@ -109,13 +109,14 @@ class DexFuzzTester(object): Raises: FatalError: error when compilation fails """ - if self._use_dx: + if self._dexer == 'dx' or self._dexer == 'd8': if RunCommand(['javac', 'Test.java'], out=None, err='jerr.txt', timeout=30) != RetCode.SUCCESS: print('Unexpected error while running javac') raise FatalError('Unexpected error while running javac') cfiles = glob('*.class') - if RunCommand(['dx', '--dex', '--output=classes.dex'] + cfiles, + dx = 'dx' if self._dexer == 'dx' else 'd8-compat-dx' + if RunCommand([dx, '--dex', '--output=classes.dex'] + cfiles, out=None, err='dxerr.txt', timeout=30) != RetCode.SUCCESS: print('Unexpected error while running dx') raise FatalError('Unexpected error while running dx') @@ -124,7 +125,8 @@ class DexFuzzTester(object): os.unlink(cfile) os.unlink('jerr.txt') os.unlink('dxerr.txt') - else: + + 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: @@ -132,6 +134,8 @@ class DexFuzzTester(object): raise FatalError('Unexpected error while running Jack') # Cleanup on success (nothing to see). os.unlink('jackerr.txt') + else: + raise FatalError('Unknown dexer: ' + self._dexer) def GenerateJFuzzPrograms(self): """Generates JFuzz programs. @@ -175,16 +179,16 @@ class DexFuzzTester(object): def main(): # Handle arguments. parser = argparse.ArgumentParser() - parser.add_argument('--num_tests', default=1000, - type=int, help='number of tests to run') - parser.add_argument('--num_inputs', default=10, - type=int, help='number of JFuzz program to generate') - parser.add_argument('--use_dx', default=False, action='store_true', - help='use dx (rather than jack)') + parser.add_argument('--num_tests', default=1000, type=int, + help='number of tests to run (default: 1000)') + parser.add_argument('--num_inputs', default=10, type=int, + help='number of JFuzz program to generate (default: 10)') + parser.add_argument('--dexer', default='dx', type=str, + help='defines dexer as dx, d8, or jack (default: dx)') parser.add_argument('--device', help='target device serial number') args = parser.parse_args() # Run the DexFuzz tester. - with DexFuzzTester(args.num_tests, args.num_inputs, args.device, args.use_dx) as fuzzer: + with DexFuzzTester(args.num_tests, args.num_inputs, args.device, args.dexer) as fuzzer: fuzzer.Run() if __name__ == '__main__': diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py index dac1c79817..b88994013e 100755 --- a/tools/jfuzz/run_jfuzz_test.py +++ b/tools/jfuzz/run_jfuzz_test.py @@ -43,11 +43,11 @@ from common.common import DeviceTestEnv BISECTABLE_RET_CODES = (RetCode.SUCCESS, RetCode.ERROR, RetCode.TIMEOUT) -def GetExecutionModeRunner(use_dx, device, mode): +def GetExecutionModeRunner(dexer, device, mode): """Returns a runner for the given execution mode. Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer device: string, target device serial number (or None) mode: string, execution mode Returns: @@ -58,13 +58,13 @@ def GetExecutionModeRunner(use_dx, device, mode): if mode == 'ri': return TestRunnerRIOnHost() if mode == 'hint': - return TestRunnerArtIntOnHost(use_dx) + return TestRunnerArtIntOnHost(dexer) if mode == 'hopt': - return TestRunnerArtOptOnHost(use_dx) + return TestRunnerArtOptOnHost(dexer) if mode == 'tint': - return TestRunnerArtIntOnTarget(use_dx, device) + return TestRunnerArtIntOnTarget(dexer, device) if mode == 'topt': - return TestRunnerArtOptOnTarget(use_dx, device) + return TestRunnerArtOptOnTarget(dexer, device) raise FatalError('Unknown execution mode') @@ -117,27 +117,30 @@ class TestRunner(object): class TestRunnerWithHostCompilation(TestRunner): """Abstract test runner that supports compilation on host.""" - def __init__(self, use_dx): + def __init__(self, dexer): """Constructor for the runner with host compilation. Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer """ self._jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', 'Test.java'] - self._use_dx = use_dx + self._dexer = dexer def CompileOnHost(self): - if self._use_dx: + if self._dexer == 'dx' or self._dexer == 'd8': if RunCommand(['javac', 'Test.java'], out=None, err=None, timeout=30) == RetCode.SUCCESS: - retc = RunCommand(['dx', '--dex', '--output=classes.dex'] + glob('*.class'), + dx = 'dx' if self._dexer == 'dx' else 'd8-compat-dx' + retc = RunCommand([dx, '--dex', '--output=classes.dex'] + glob('*.class'), out=None, err='dxerr.txt', timeout=30) else: retc = RetCode.NOTCOMPILED - else: + 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 @@ -167,14 +170,14 @@ class TestRunnerRIOnHost(TestRunner): class TestRunnerArtOnHost(TestRunnerWithHostCompilation): """Abstract test runner of Art on host.""" - def __init__(self, use_dx, extra_args=None): + def __init__(self, dexer, extra_args=None): """Constructor for the Art on host tester. Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer extra_args: list of strings, extra arguments for dalvikvm """ - super().__init__(use_dx) + super().__init__(dexer) self._art_cmd = ['/bin/bash', 'art', '-cp', 'classes.dex'] if extra_args is not None: self._art_cmd += extra_args @@ -191,13 +194,13 @@ class TestRunnerArtOnHost(TestRunnerWithHostCompilation): class TestRunnerArtIntOnHost(TestRunnerArtOnHost): """Concrete test runner of interpreter mode Art on host.""" - def __init__(self, use_dx): + def __init__(self, dexer): """Constructor for the Art on host tester (interpreter). Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer """ - super().__init__(use_dx, ['-Xint']) + super().__init__(dexer, ['-Xint']) @property def description(self): @@ -214,13 +217,13 @@ class TestRunnerArtIntOnHost(TestRunnerArtOnHost): class TestRunnerArtOptOnHost(TestRunnerArtOnHost): """Concrete test runner of optimizing compiler mode Art on host.""" - def __init__(self, use_dx): + def __init__(self, dexer): """Constructor for the Art on host tester (optimizing). Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer """ - super().__init__(use_dx, None) + super().__init__(dexer, None) @property def description(self): @@ -239,15 +242,15 @@ class TestRunnerArtOptOnHost(TestRunnerArtOnHost): class TestRunnerArtOnTarget(TestRunnerWithHostCompilation): """Abstract test runner of Art on target.""" - def __init__(self, use_dx, device, extra_args=None): + def __init__(self, dexer, device, extra_args=None): """Constructor for the Art on target tester. Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer device: string, target device serial number (or None) extra_args: list of strings, extra arguments for dalvikvm """ - super().__init__(use_dx) + super().__init__(dexer) self._test_env = DeviceTestEnv('jfuzz_', specific_device=device) self._dalvik_cmd = ['dalvikvm'] if extra_args is not None: @@ -281,14 +284,14 @@ class TestRunnerArtOnTarget(TestRunnerWithHostCompilation): class TestRunnerArtIntOnTarget(TestRunnerArtOnTarget): """Concrete test runner of interpreter mode Art on target.""" - def __init__(self, use_dx, device): + def __init__(self, dexer, device): """Constructor for the Art on target tester (interpreter). Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer device: string, target device serial number (or None) """ - super().__init__(use_dx, device, ['-Xint']) + super().__init__(dexer, device, ['-Xint']) @property def description(self): @@ -305,14 +308,14 @@ class TestRunnerArtIntOnTarget(TestRunnerArtOnTarget): class TestRunnerArtOptOnTarget(TestRunnerArtOnTarget): """Concrete test runner of optimizing compiler mode Art on target.""" - def __init__(self, use_dx, device): + def __init__(self, dexer, device): """Constructor for the Art on target tester (optimizing). Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer device: string, target device serial number (or None) """ - super().__init__(use_dx, device, None) + super().__init__(dexer, device, None) @property def description(self): @@ -342,7 +345,7 @@ class JFuzzTester(object): """Tester that runs JFuzz many times and report divergences.""" def __init__(self, num_tests, device, mode1, mode2, jfuzz_args, - report_script, true_divergence_only, use_dx): + report_script, true_divergence_only, dexer): """Constructor for the tester. Args: @@ -353,16 +356,16 @@ class JFuzzTester(object): jfuzz_args: list of strings, additional arguments for jfuzz report_script: string, path to script called for each divergence true_divergence_only: boolean, if True don't bisect timeout divergences - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer """ self._num_tests = num_tests self._device = device - self._runner1 = GetExecutionModeRunner(use_dx, device, mode1) - self._runner2 = GetExecutionModeRunner(use_dx, device, mode2) + self._runner1 = GetExecutionModeRunner(dexer, device, mode1) + self._runner2 = GetExecutionModeRunner(dexer, device, mode2) self._jfuzz_args = jfuzz_args self._report_script = report_script self._true_divergence_only = true_divergence_only - self._use_dx = use_dx + self._dexer = dexer self._save_dir = None self._results_dir = None self._jfuzz_dir = None @@ -405,7 +408,7 @@ class JFuzzTester(object): print('Directory :', self._results_dir) print('Exec-mode1:', self._runner1.description) print('Exec-mode2:', self._runner2.description) - print('Compiler :', 'dx' if self._use_dx else 'jack') + print('Dexer :', self._dexer) print() self.ShowStats() for self._test in range(1, self._num_tests + 1): @@ -525,8 +528,7 @@ class JFuzzTester(object): for arg in jfuzz_cmd_str.strip().split(' -')][1:] wrapped_args = ['--jfuzz_arg={0}'.format(opt) for opt in jfuzz_args] repro_cmd_str = (os.path.basename(__file__) + - ' --num_tests=1 ' + - ('--use_dx ' if self._use_dx else '') + + ' --num_tests=1 --dexer=' + self._dexer + ' '.join(wrapped_args)) comment = 'jfuzz {0}\nReproduce test:\n{1}\nReproduce divergence:\n{2}\n'.format( jfuzz_ver, jfuzz_cmd_str, repro_cmd_str) @@ -592,21 +594,22 @@ class JFuzzTester(object): def main(): # Handle arguments. parser = argparse.ArgumentParser() - parser.add_argument('--num_tests', default=10000, - type=int, help='number of tests to run') + parser.add_argument('--num_tests', default=10000, type=int, + help='number of tests to run') parser.add_argument('--device', help='target device serial number') parser.add_argument('--mode1', default='ri', help='execution mode 1 (default: ri)') parser.add_argument('--mode2', default='hopt', help='execution mode 2 (default: hopt)') - parser.add_argument('--report_script', help='script called for each' - ' divergence') + parser.add_argument('--report_script', + help='script called for each divergence') parser.add_argument('--jfuzz_arg', default=[], dest='jfuzz_args', - action='append', help='argument for jfuzz') + action='append', + help='argument for jfuzz') parser.add_argument('--true_divergence', default=False, action='store_true', - help='don\'t bisect timeout divergences') - parser.add_argument('--use_dx', default=False, action='store_true', - help='use dx (rather than jack)') + help='do not bisect timeout divergences') + parser.add_argument('--dexer', default='dx', type=str, + help='defines dexer as dx, d8, or jack (default: dx)') args = parser.parse_args() if args.mode1 == args.mode2: raise FatalError('Identical execution modes given') @@ -614,7 +617,7 @@ def main(): with JFuzzTester(args.num_tests, args.device, args.mode1, args.mode2, args.jfuzz_args, args.report_script, - args.true_divergence, args.use_dx) as fuzzer: + args.true_divergence, args.dexer) as fuzzer: fuzzer.Run() if __name__ == '__main__': -- GitLab From cfe50bb5a66251feeb4a16d25eb2f95d7a0f99ce Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Tue, 12 Dec 2017 14:54:12 -0800 Subject: [PATCH 179/226] Implemented missing move/exchange paths for x86 Rationale: Recent SIMD register spilling bug fix exposed missing cases in the x86 and x86_64 code generator for moving and exchanging SIMD spill slots. Test: run-test --host -Xcompiler-option --instruction-set-features=sse4.1 623-checker-loop-regressions (32/64) Bug: 70559970 Change-Id: Iae66d6874b93af5b2db80db70bce4b0f4a9b9f3f --- compiler/optimizing/code_generator_x86.cc | 67 ++++++++------ compiler/optimizing/code_generator_x86.h | 6 +- compiler/optimizing/code_generator_x86_64.cc | 92 +++++++++++++------- compiler/optimizing/code_generator_x86_64.h | 5 +- 4 files changed, 109 insertions(+), 61 deletions(-) diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 2e8170ecc4..42ee9db167 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -5732,24 +5732,18 @@ X86Assembler* ParallelMoveResolverX86::GetAssembler() const { return codegen_->GetAssembler(); } -void ParallelMoveResolverX86::MoveMemoryToMemory32(int dst, int src) { +void ParallelMoveResolverX86::MoveMemoryToMemory(int dst, int src, int number_of_words) { ScratchRegisterScope ensure_scratch( this, kNoRegister, EAX, codegen_->GetNumberOfCoreRegisters()); Register temp_reg = static_cast(ensure_scratch.GetRegister()); int stack_offset = ensure_scratch.IsSpilled() ? kX86WordSize : 0; - __ movl(temp_reg, Address(ESP, src + stack_offset)); - __ movl(Address(ESP, dst + stack_offset), temp_reg); -} -void ParallelMoveResolverX86::MoveMemoryToMemory64(int dst, int src) { - ScratchRegisterScope ensure_scratch( - this, kNoRegister, EAX, codegen_->GetNumberOfCoreRegisters()); - Register temp_reg = static_cast(ensure_scratch.GetRegister()); - int stack_offset = ensure_scratch.IsSpilled() ? kX86WordSize : 0; - __ movl(temp_reg, Address(ESP, src + stack_offset)); - __ movl(Address(ESP, dst + stack_offset), temp_reg); - __ movl(temp_reg, Address(ESP, src + stack_offset + kX86WordSize)); - __ movl(Address(ESP, dst + stack_offset + kX86WordSize), temp_reg); + // Now that temp register is available (possibly spilled), move blocks of memory. + for (int i = 0; i < number_of_words; i++) { + __ movl(temp_reg, Address(ESP, src + stack_offset)); + __ movl(Address(ESP, dst + stack_offset), temp_reg); + stack_offset += kX86WordSize; + } } void ParallelMoveResolverX86::EmitMove(size_t index) { @@ -5800,7 +5794,7 @@ void ParallelMoveResolverX86::EmitMove(size_t index) { __ movss(destination.AsFpuRegister(), Address(ESP, source.GetStackIndex())); } else { DCHECK(destination.IsStackSlot()); - MoveMemoryToMemory32(destination.GetStackIndex(), source.GetStackIndex()); + MoveMemoryToMemory(destination.GetStackIndex(), source.GetStackIndex(), 1); } } else if (source.IsDoubleStackSlot()) { if (destination.IsRegisterPair()) { @@ -5811,11 +5805,15 @@ void ParallelMoveResolverX86::EmitMove(size_t index) { __ movsd(destination.AsFpuRegister(), Address(ESP, source.GetStackIndex())); } else { DCHECK(destination.IsDoubleStackSlot()) << destination; - MoveMemoryToMemory64(destination.GetStackIndex(), source.GetStackIndex()); + MoveMemoryToMemory(destination.GetStackIndex(), source.GetStackIndex(), 2); } } else if (source.IsSIMDStackSlot()) { - DCHECK(destination.IsFpuRegister()); - __ movups(destination.AsFpuRegister(), Address(ESP, source.GetStackIndex())); + if (destination.IsFpuRegister()) { + __ movups(destination.AsFpuRegister(), Address(ESP, source.GetStackIndex())); + } else { + DCHECK(destination.IsSIMDStackSlot()); + MoveMemoryToMemory(destination.GetStackIndex(), source.GetStackIndex(), 4); + } } else if (source.IsConstant()) { HConstant* constant = source.GetConstant(); if (constant->IsIntConstant() || constant->IsNullConstant()) { @@ -5915,7 +5913,16 @@ void ParallelMoveResolverX86::Exchange32(XmmRegister reg, int mem) { __ movd(reg, temp_reg); } -void ParallelMoveResolverX86::Exchange(int mem1, int mem2) { +void ParallelMoveResolverX86::Exchange128(XmmRegister reg, int mem) { + size_t extra_slot = 4 * kX86WordSize; + __ subl(ESP, Immediate(extra_slot)); + __ movups(Address(ESP, 0), XmmRegister(reg)); + ExchangeMemory(0, mem + extra_slot, 4); + __ movups(XmmRegister(reg), Address(ESP, 0)); + __ addl(ESP, Immediate(extra_slot)); +} + +void ParallelMoveResolverX86::ExchangeMemory(int mem1, int mem2, int number_of_words) { ScratchRegisterScope ensure_scratch1( this, kNoRegister, EAX, codegen_->GetNumberOfCoreRegisters()); @@ -5925,10 +5932,15 @@ void ParallelMoveResolverX86::Exchange(int mem1, int mem2) { int stack_offset = ensure_scratch1.IsSpilled() ? kX86WordSize : 0; stack_offset += ensure_scratch2.IsSpilled() ? kX86WordSize : 0; - __ movl(static_cast(ensure_scratch1.GetRegister()), Address(ESP, mem1 + stack_offset)); - __ movl(static_cast(ensure_scratch2.GetRegister()), Address(ESP, mem2 + stack_offset)); - __ movl(Address(ESP, mem2 + stack_offset), static_cast(ensure_scratch1.GetRegister())); - __ movl(Address(ESP, mem1 + stack_offset), static_cast(ensure_scratch2.GetRegister())); + + // Now that temp registers are available (possibly spilled), exchange blocks of memory. + for (int i = 0; i < number_of_words; i++) { + __ movl(static_cast(ensure_scratch1.GetRegister()), Address(ESP, mem1 + stack_offset)); + __ movl(static_cast(ensure_scratch2.GetRegister()), Address(ESP, mem2 + stack_offset)); + __ movl(Address(ESP, mem2 + stack_offset), static_cast(ensure_scratch1.GetRegister())); + __ movl(Address(ESP, mem1 + stack_offset), static_cast(ensure_scratch2.GetRegister())); + stack_offset += kX86WordSize; + } } void ParallelMoveResolverX86::EmitSwap(size_t index) { @@ -5947,7 +5959,7 @@ void ParallelMoveResolverX86::EmitSwap(size_t index) { } else if (source.IsStackSlot() && destination.IsRegister()) { Exchange(destination.AsRegister(), source.GetStackIndex()); } else if (source.IsStackSlot() && destination.IsStackSlot()) { - Exchange(destination.GetStackIndex(), source.GetStackIndex()); + ExchangeMemory(destination.GetStackIndex(), source.GetStackIndex(), 1); } else if (source.IsFpuRegister() && destination.IsFpuRegister()) { // Use XOR Swap algorithm to avoid a temporary. DCHECK_NE(source.reg(), destination.reg()); @@ -5983,8 +5995,13 @@ void ParallelMoveResolverX86::EmitSwap(size_t index) { // Move the high double to the low double. __ psrldq(reg, Immediate(8)); } else if (destination.IsDoubleStackSlot() && source.IsDoubleStackSlot()) { - Exchange(destination.GetStackIndex(), source.GetStackIndex()); - Exchange(destination.GetHighStackIndex(kX86WordSize), source.GetHighStackIndex(kX86WordSize)); + ExchangeMemory(destination.GetStackIndex(), source.GetStackIndex(), 2); + } else if (source.IsSIMDStackSlot() && destination.IsSIMDStackSlot()) { + ExchangeMemory(destination.GetStackIndex(), source.GetStackIndex(), 4); + } else if (source.IsFpuRegister() && destination.IsSIMDStackSlot()) { + Exchange128(source.AsFpuRegister(), destination.GetStackIndex()); + } else if (destination.IsFpuRegister() && source.IsSIMDStackSlot()) { + Exchange128(destination.AsFpuRegister(), source.GetStackIndex()); } else { LOG(FATAL) << "Unimplemented: source: " << source << ", destination: " << destination; } diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 176e4dfda0..40b7e3c54f 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -139,10 +139,10 @@ class ParallelMoveResolverX86 : public ParallelMoveResolverWithSwap { private: void Exchange(Register reg, int mem); - void Exchange(int mem1, int mem2); void Exchange32(XmmRegister reg, int mem); - void MoveMemoryToMemory32(int dst, int src); - void MoveMemoryToMemory64(int dst, int src); + void Exchange128(XmmRegister reg, int mem); + void ExchangeMemory(int mem1, int mem2, int number_of_words); + void MoveMemoryToMemory(int dst, int src, int number_of_words); CodeGeneratorX86* const codegen_; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index e25688c9a3..02fbf234c1 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -5220,9 +5220,17 @@ void ParallelMoveResolverX86_64::EmitMove(size_t index) { __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP)); } } else if (source.IsSIMDStackSlot()) { - DCHECK(destination.IsFpuRegister()); - __ movups(destination.AsFpuRegister(), - Address(CpuRegister(RSP), source.GetStackIndex())); + if (destination.IsFpuRegister()) { + __ movups(destination.AsFpuRegister(), + Address(CpuRegister(RSP), source.GetStackIndex())); + } else { + DCHECK(destination.IsSIMDStackSlot()); + size_t high = kX86_64WordSize; + __ movq(CpuRegister(TMP), Address(CpuRegister(RSP), source.GetStackIndex())); + __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP)); + __ movq(CpuRegister(TMP), Address(CpuRegister(RSP), source.GetStackIndex() + high)); + __ movq(Address(CpuRegister(RSP), destination.GetStackIndex() + high), CpuRegister(TMP)); + } } else if (source.IsConstant()) { HConstant* constant = source.GetConstant(); if (constant->IsIntConstant() || constant->IsNullConstant()) { @@ -5290,19 +5298,6 @@ void ParallelMoveResolverX86_64::Exchange32(CpuRegister reg, int mem) { __ movl(reg, CpuRegister(TMP)); } -void ParallelMoveResolverX86_64::Exchange32(int mem1, int mem2) { - ScratchRegisterScope ensure_scratch( - this, TMP, RAX, codegen_->GetNumberOfCoreRegisters()); - - int stack_offset = ensure_scratch.IsSpilled() ? kX86_64WordSize : 0; - __ movl(CpuRegister(TMP), Address(CpuRegister(RSP), mem1 + stack_offset)); - __ movl(CpuRegister(ensure_scratch.GetRegister()), - Address(CpuRegister(RSP), mem2 + stack_offset)); - __ movl(Address(CpuRegister(RSP), mem2 + stack_offset), CpuRegister(TMP)); - __ movl(Address(CpuRegister(RSP), mem1 + stack_offset), - CpuRegister(ensure_scratch.GetRegister())); -} - void ParallelMoveResolverX86_64::Exchange64(CpuRegister reg1, CpuRegister reg2) { __ movq(CpuRegister(TMP), reg1); __ movq(reg1, reg2); @@ -5315,19 +5310,6 @@ void ParallelMoveResolverX86_64::Exchange64(CpuRegister reg, int mem) { __ movq(reg, CpuRegister(TMP)); } -void ParallelMoveResolverX86_64::Exchange64(int mem1, int mem2) { - ScratchRegisterScope ensure_scratch( - this, TMP, RAX, codegen_->GetNumberOfCoreRegisters()); - - int stack_offset = ensure_scratch.IsSpilled() ? kX86_64WordSize : 0; - __ movq(CpuRegister(TMP), Address(CpuRegister(RSP), mem1 + stack_offset)); - __ movq(CpuRegister(ensure_scratch.GetRegister()), - Address(CpuRegister(RSP), mem2 + stack_offset)); - __ movq(Address(CpuRegister(RSP), mem2 + stack_offset), CpuRegister(TMP)); - __ movq(Address(CpuRegister(RSP), mem1 + stack_offset), - CpuRegister(ensure_scratch.GetRegister())); -} - void ParallelMoveResolverX86_64::Exchange32(XmmRegister reg, int mem) { __ movl(CpuRegister(TMP), Address(CpuRegister(RSP), mem)); __ movss(Address(CpuRegister(RSP), mem), reg); @@ -5340,6 +5322,48 @@ void ParallelMoveResolverX86_64::Exchange64(XmmRegister reg, int mem) { __ movd(reg, CpuRegister(TMP)); } +void ParallelMoveResolverX86_64::Exchange128(XmmRegister reg, int mem) { + size_t extra_slot = 2 * kX86_64WordSize; + __ subq(CpuRegister(RSP), Immediate(extra_slot)); + __ movups(Address(CpuRegister(RSP), 0), XmmRegister(reg)); + ExchangeMemory64(0, mem + extra_slot, 2); + __ movups(XmmRegister(reg), Address(CpuRegister(RSP), 0)); + __ addq(CpuRegister(RSP), Immediate(extra_slot)); +} + +void ParallelMoveResolverX86_64::ExchangeMemory32(int mem1, int mem2) { + ScratchRegisterScope ensure_scratch( + this, TMP, RAX, codegen_->GetNumberOfCoreRegisters()); + + int stack_offset = ensure_scratch.IsSpilled() ? kX86_64WordSize : 0; + __ movl(CpuRegister(TMP), Address(CpuRegister(RSP), mem1 + stack_offset)); + __ movl(CpuRegister(ensure_scratch.GetRegister()), + Address(CpuRegister(RSP), mem2 + stack_offset)); + __ movl(Address(CpuRegister(RSP), mem2 + stack_offset), CpuRegister(TMP)); + __ movl(Address(CpuRegister(RSP), mem1 + stack_offset), + CpuRegister(ensure_scratch.GetRegister())); +} + +void ParallelMoveResolverX86_64::ExchangeMemory64(int mem1, int mem2, int num_of_qwords) { + ScratchRegisterScope ensure_scratch( + this, TMP, RAX, codegen_->GetNumberOfCoreRegisters()); + + int stack_offset = ensure_scratch.IsSpilled() ? kX86_64WordSize : 0; + + // Now that temp registers are available (possibly spilled), exchange blocks of memory. + for (int i = 0; i < num_of_qwords; i++) { + __ movq(CpuRegister(TMP), + Address(CpuRegister(RSP), mem1 + stack_offset)); + __ movq(CpuRegister(ensure_scratch.GetRegister()), + Address(CpuRegister(RSP), mem2 + stack_offset)); + __ movq(Address(CpuRegister(RSP), mem2 + stack_offset), + CpuRegister(TMP)); + __ movq(Address(CpuRegister(RSP), mem1 + stack_offset), + CpuRegister(ensure_scratch.GetRegister())); + stack_offset += kX86_64WordSize; + } +} + void ParallelMoveResolverX86_64::EmitSwap(size_t index) { MoveOperands* move = moves_[index]; Location source = move->GetSource(); @@ -5352,13 +5376,13 @@ void ParallelMoveResolverX86_64::EmitSwap(size_t index) { } else if (source.IsStackSlot() && destination.IsRegister()) { Exchange32(destination.AsRegister(), source.GetStackIndex()); } else if (source.IsStackSlot() && destination.IsStackSlot()) { - Exchange32(destination.GetStackIndex(), source.GetStackIndex()); + ExchangeMemory32(destination.GetStackIndex(), source.GetStackIndex()); } else if (source.IsRegister() && destination.IsDoubleStackSlot()) { Exchange64(source.AsRegister(), destination.GetStackIndex()); } else if (source.IsDoubleStackSlot() && destination.IsRegister()) { Exchange64(destination.AsRegister(), source.GetStackIndex()); } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) { - Exchange64(destination.GetStackIndex(), source.GetStackIndex()); + ExchangeMemory64(destination.GetStackIndex(), source.GetStackIndex(), 1); } else if (source.IsFpuRegister() && destination.IsFpuRegister()) { __ movd(CpuRegister(TMP), source.AsFpuRegister()); __ movaps(source.AsFpuRegister(), destination.AsFpuRegister()); @@ -5371,6 +5395,12 @@ void ParallelMoveResolverX86_64::EmitSwap(size_t index) { Exchange64(source.AsFpuRegister(), destination.GetStackIndex()); } else if (source.IsDoubleStackSlot() && destination.IsFpuRegister()) { Exchange64(destination.AsFpuRegister(), source.GetStackIndex()); + } else if (source.IsSIMDStackSlot() && destination.IsSIMDStackSlot()) { + ExchangeMemory64(destination.GetStackIndex(), source.GetStackIndex(), 2); + } else if (source.IsFpuRegister() && destination.IsSIMDStackSlot()) { + Exchange128(source.AsFpuRegister(), destination.GetStackIndex()); + } else if (destination.IsFpuRegister() && source.IsSIMDStackSlot()) { + Exchange128(destination.AsFpuRegister(), source.GetStackIndex()); } else { LOG(FATAL) << "Unimplemented swap between " << source << " and " << destination; } diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 00c5c27470..e86123ef01 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -139,11 +139,12 @@ class ParallelMoveResolverX86_64 : public ParallelMoveResolverWithSwap { private: void Exchange32(CpuRegister reg, int mem); void Exchange32(XmmRegister reg, int mem); - void Exchange32(int mem1, int mem2); void Exchange64(CpuRegister reg1, CpuRegister reg2); void Exchange64(CpuRegister reg, int mem); void Exchange64(XmmRegister reg, int mem); - void Exchange64(int mem1, int mem2); + void Exchange128(XmmRegister reg, int mem); + void ExchangeMemory32(int mem1, int mem2); + void ExchangeMemory64(int mem1, int mem2, int num_of_qwords); CodeGeneratorX86_64* const codegen_; -- GitLab From 09faaea17b75269805b4857ed3c9cd04c7273959 Mon Sep 17 00:00:00 2001 From: Artem Serov Date: Thu, 7 Dec 2017 14:36:01 +0000 Subject: [PATCH 180/226] ART: Fix single-preheader transformation. Original implementation of "Make sure the loop has only one pre-header" had an assumption that the header had no phi functions since loops with multiple preheaders now only may exist during graph building before ssa construction; all of the optimizations preserve the single-preheader invariant. This code is used by DCE; DCE was called multiple times but after graph building preheader transformation was never executed. However if someone introduces a optimization which might not keep the invariant (e.g. loop peeling) the data flow adjustments must be performed. Test: loop_optimization_test.cc Test: test-art-target, test-art-host Change-Id: I88bb0aad2dd5241addef7fe9cda474a6868bf532 --- compiler/optimizing/loop_optimization_test.cc | 83 +++++++++++- compiler/optimizing/nodes.cc | 124 ++++++++++++++++-- compiler/optimizing/nodes.h | 11 ++ 3 files changed, 205 insertions(+), 13 deletions(-) diff --git a/compiler/optimizing/loop_optimization_test.cc b/compiler/optimizing/loop_optimization_test.cc index 4e1857df5b..db8368986c 100644 --- a/compiler/optimizing/loop_optimization_test.cc +++ b/compiler/optimizing/loop_optimization_test.cc @@ -194,7 +194,9 @@ TEST_F(LoopOptimizationTest, LoopNestWithSequence) { // Check that SimplifyLoop() doesn't invalidate data flow when ordering loop headers' // predecessors. -TEST_F(LoopOptimizationTest, SimplifyLoop) { +// +// This is a test for nodes.cc functionality - HGraph::SimplifyLoop. +TEST_F(LoopOptimizationTest, SimplifyLoopReoderPredecessors) { // Can't use AddLoop as we want special order for blocks predecessors. HBasicBlock* header = new (GetAllocator()) HBasicBlock(graph_); HBasicBlock* body = new (GetAllocator()) HBasicBlock(graph_); @@ -232,4 +234,83 @@ TEST_F(LoopOptimizationTest, SimplifyLoop) { ASSERT_TRUE(input->GetBlock()->Dominates(header->GetPredecessors()[i])); } } + +// Test that SimplifyLoop() processes the multiple-preheaders loops correctly. +// +// This is a test for nodes.cc functionality - HGraph::SimplifyLoop. +TEST_F(LoopOptimizationTest, SimplifyLoopSinglePreheader) { + HBasicBlock* header = AddLoop(entry_block_, return_block_); + + header->InsertInstructionBefore( + new (GetAllocator()) HSuspendCheck(), header->GetLastInstruction()); + + // Insert an if construct before the loop so it will have two preheaders. + HBasicBlock* if_block = new (GetAllocator()) HBasicBlock(graph_); + HBasicBlock* preheader0 = new (GetAllocator()) HBasicBlock(graph_); + HBasicBlock* preheader1 = new (GetAllocator()) HBasicBlock(graph_); + + graph_->AddBlock(if_block); + graph_->AddBlock(preheader0); + graph_->AddBlock(preheader1); + + // Fix successors/predecessors. + entry_block_->ReplaceSuccessor(header, if_block); + if_block->AddSuccessor(preheader0); + if_block->AddSuccessor(preheader1); + preheader0->AddSuccessor(header); + preheader1->AddSuccessor(header); + + if_block->AddInstruction(new (GetAllocator()) HIf(parameter_)); + preheader0->AddInstruction(new (GetAllocator()) HGoto()); + preheader1->AddInstruction(new (GetAllocator()) HGoto()); + + HBasicBlock* body = header->GetSuccessors()[0]; + DCHECK(body != return_block_); + + // Add some data flow. + HIntConstant* const_0 = graph_->GetIntConstant(0); + HIntConstant* const_1 = graph_->GetIntConstant(1); + HIntConstant* const_2 = graph_->GetIntConstant(2); + + HAdd* preheader0_add = new (GetAllocator()) HAdd(DataType::Type::kInt32, parameter_, const_0); + preheader0->AddInstruction(preheader0_add); + HAdd* preheader1_add = new (GetAllocator()) HAdd(DataType::Type::kInt32, parameter_, const_1); + preheader1->AddInstruction(preheader1_add); + + HPhi* header_phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32); + header->AddPhi(header_phi); + + HAdd* body_add = new (GetAllocator()) HAdd(DataType::Type::kInt32, parameter_, const_2); + body->AddInstruction(body_add); + + DCHECK(header->GetPredecessors()[0] == body); + DCHECK(header->GetPredecessors()[1] == preheader0); + DCHECK(header->GetPredecessors()[2] == preheader1); + + header_phi->AddInput(body_add); + header_phi->AddInput(preheader0_add); + header_phi->AddInput(preheader1_add); + + graph_->ClearLoopInformation(); + graph_->ClearDominanceInformation(); + graph_->BuildDominatorTree(); + + EXPECT_EQ(header->GetPredecessors().size(), 2u); + EXPECT_EQ(header->GetPredecessors()[1], body); + + HBasicBlock* new_preheader = header->GetLoopInformation()->GetPreHeader(); + EXPECT_EQ(preheader0->GetSingleSuccessor(), new_preheader); + EXPECT_EQ(preheader1->GetSingleSuccessor(), new_preheader); + + EXPECT_EQ(new_preheader->GetPhis().CountSize(), 1u); + HPhi* new_preheader_phi = new_preheader->GetFirstPhi()->AsPhi(); + EXPECT_EQ(new_preheader_phi->InputCount(), 2u); + EXPECT_EQ(new_preheader_phi->InputAt(0), preheader0_add); + EXPECT_EQ(new_preheader_phi->InputAt(1), preheader1_add); + + EXPECT_EQ(header_phi->InputCount(), 2u); + EXPECT_EQ(header_phi->InputAt(0), new_preheader_phi); + EXPECT_EQ(header_phi->InputAt(1), body_add); +} + } // namespace art diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index d39c2aded5..099e6cc6ac 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -397,6 +397,117 @@ void HGraph::OrderLoopHeaderPredecessors(HBasicBlock* header) { } } +// Transform control flow of the loop to a single preheader format (don't touch the data flow). +// New_preheader can be already among the header predecessors - this situation will be correctly +// processed. +static void FixControlForNewSinglePreheader(HBasicBlock* header, HBasicBlock* new_preheader) { + HLoopInformation* loop_info = header->GetLoopInformation(); + for (size_t pred = 0; pred < header->GetPredecessors().size(); ++pred) { + HBasicBlock* predecessor = header->GetPredecessors()[pred]; + if (!loop_info->IsBackEdge(*predecessor) && predecessor != new_preheader) { + predecessor->ReplaceSuccessor(header, new_preheader); + pred--; + } + } +} + +// == Before == == After == +// _________ _________ _________ _________ +// | B0 | | B1 | (old preheaders) | B0 | | B1 | +// |=========| |=========| |=========| |=========| +// | i0 = .. | | i1 = .. | | i0 = .. | | i1 = .. | +// |_________| |_________| |_________| |_________| +// \ / \ / +// \ / ___v____________v___ +// \ / (new preheader) | B20 <- B0, B1 | +// | | |====================| +// | | | i20 = phi(i0, i1) | +// | | |____________________| +// | | | +// /\ | | /\ /\ | /\ +// / v_______v_________v_______v \ / v___________v_____________v \ +// | | B10 <- B0, B1, B2, B3 | | | | B10 <- B20, B2, B3 | | +// | |===========================| | (header) | |===========================| | +// | | i10 = phi(i0, i1, i2, i3) | | | | i10 = phi(i20, i2, i3) | | +// | |___________________________| | | |___________________________| | +// | / \ | | / \ | +// | ... ... | | ... ... | +// | _________ _________ | | _________ _________ | +// | | B2 | | B3 | | | | B2 | | B3 | | +// | |=========| |=========| | (back edges) | |=========| |=========| | +// | | i2 = .. | | i3 = .. | | | | i2 = .. | | i3 = .. | | +// | |_________| |_________| | | |_________| |_________| | +// \ / \ / \ / \ / +// \___/ \___/ \___/ \___/ +// +void HGraph::TransformLoopToSinglePreheaderFormat(HBasicBlock* header) { + HLoopInformation* loop_info = header->GetLoopInformation(); + + HBasicBlock* preheader = new (allocator_) HBasicBlock(this, header->GetDexPc()); + AddBlock(preheader); + preheader->AddInstruction(new (allocator_) HGoto(header->GetDexPc())); + + // If the old header has no Phis then we only need to fix the control flow. + if (header->GetPhis().IsEmpty()) { + FixControlForNewSinglePreheader(header, preheader); + preheader->AddSuccessor(header); + return; + } + + // Find the first non-back edge block in the header's predecessors list. + size_t first_nonbackedge_pred_pos = 0; + bool found = false; + for (size_t pred = 0; pred < header->GetPredecessors().size(); ++pred) { + HBasicBlock* predecessor = header->GetPredecessors()[pred]; + if (!loop_info->IsBackEdge(*predecessor)) { + first_nonbackedge_pred_pos = pred; + found = true; + break; + } + } + + DCHECK(found); + + // Fix the data-flow. + for (HInstructionIterator it(header->GetPhis()); !it.Done(); it.Advance()) { + HPhi* header_phi = it.Current()->AsPhi(); + + HPhi* preheader_phi = new (GetAllocator()) HPhi(GetAllocator(), + header_phi->GetRegNumber(), + 0, + header_phi->GetType()); + if (header_phi->GetType() == DataType::Type::kReference) { + preheader_phi->SetReferenceTypeInfo(header_phi->GetReferenceTypeInfo()); + } + preheader->AddPhi(preheader_phi); + + HInstruction* orig_input = header_phi->InputAt(first_nonbackedge_pred_pos); + header_phi->ReplaceInput(preheader_phi, first_nonbackedge_pred_pos); + preheader_phi->AddInput(orig_input); + + for (size_t input_pos = first_nonbackedge_pred_pos + 1; + input_pos < header_phi->InputCount(); + input_pos++) { + HInstruction* input = header_phi->InputAt(input_pos); + HBasicBlock* pred_block = header->GetPredecessors()[input_pos]; + + if (loop_info->Contains(*pred_block)) { + DCHECK(loop_info->IsBackEdge(*pred_block)); + } else { + preheader_phi->AddInput(input); + header_phi->RemoveInputAt(input_pos); + input_pos--; + } + } + } + + // Fix the control-flow. + HBasicBlock* first_pred = header->GetPredecessors()[first_nonbackedge_pred_pos]; + preheader->InsertBetween(first_pred, header); + + FixControlForNewSinglePreheader(header, preheader); +} + void HGraph::SimplifyLoop(HBasicBlock* header) { HLoopInformation* info = header->GetLoopInformation(); @@ -406,18 +517,7 @@ void HGraph::SimplifyLoop(HBasicBlock* header) { // this graph. size_t number_of_incomings = header->GetPredecessors().size() - info->NumberOfBackEdges(); if (number_of_incomings != 1 || (GetEntryBlock()->GetSingleSuccessor() == header)) { - HBasicBlock* pre_header = new (allocator_) HBasicBlock(this, header->GetDexPc()); - AddBlock(pre_header); - pre_header->AddInstruction(new (allocator_) HGoto(header->GetDexPc())); - - for (size_t pred = 0; pred < header->GetPredecessors().size(); ++pred) { - HBasicBlock* predecessor = header->GetPredecessors()[pred]; - if (!info->IsBackEdge(*predecessor)) { - predecessor->ReplaceSuccessor(header, pre_header); - pred--; - } - } - pre_header->AddSuccessor(header); + TransformLoopToSinglePreheaderFormat(header); } OrderLoopHeaderPredecessors(header); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index affd54ed72..f83abd708a 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -423,6 +423,17 @@ class HGraph : public ArenaObject { void SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor); void OrderLoopHeaderPredecessors(HBasicBlock* header); + + // Transform a loop into a format with a single preheader. + // + // Each phi in the header should be split: original one in the header should only hold + // inputs reachable from the back edges and a single input from the preheader. The newly created + // phi in the preheader should collate the inputs from the original multiple incoming blocks. + // + // Loops in the graph typically have a single preheader, so this method is used to "repair" loops + // that no longer have this property. + void TransformLoopToSinglePreheaderFormat(HBasicBlock* header); + void SimplifyLoop(HBasicBlock* header); int32_t GetNextInstructionId() { -- GitLab From 203010a86542b16991ede122085b07eea6c55bec Mon Sep 17 00:00:00 2001 From: Alex Klyubin Date: Wed, 13 Dec 2017 12:53:28 -0800 Subject: [PATCH 181/226] Revert "Revert "Add patchoat test"" This reverts commit 5dd08acd0b568bb05e2e75fc02d8a6d3d7aa6f8e. This attempt removes any remaining non-determinism, by passing in --force-determinism to dex2oat in the test. As a result, the test now passes both host-side and device-side. Test: make test-art-host-gtest-patchoat_test Test: make test-art-target-gtest-patchoat_test Bug: 66697305 Change-Id: I8192ced68cec9f270b004488eb264f429bf7ab60 --- build/Android.gtest.mk | 10 ++ patchoat/Android.bp | 13 ++ patchoat/patchoat_test.cc | 346 ++++++++++++++++++++++++++++++++++++++ runtime/image.h | 4 + 4 files changed, 373 insertions(+) create mode 100644 patchoat/patchoat_test.cc diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 3c8eade773..230b2665e6 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -124,6 +124,7 @@ ART_GTEST_image_space_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex ART_GTEST_oat_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 ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods ART_GTEST_profile_assistant_test_DEX_DEPS := ProfileTestMultiDex @@ -251,6 +252,11 @@ ART_GTEST_oatdump_test_TARGET_DEPS := \ 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_patchoat_test_HOST_DEPS := \ + $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) +ART_GTEST_patchoat_test_TARGET_DEPS := \ + $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) + # Profile assistant tests requires profman utility. ART_GTEST_profile_assistant_test_HOST_DEPS := profmand-host ART_GTEST_profile_assistant_test_TARGET_DEPS := profmand-target @@ -270,6 +276,7 @@ ART_TEST_MODULES := \ art_dexoptanalyzer_tests \ art_imgdiag_tests \ art_oatdump_tests \ + art_patchoat_tests \ art_profman_tests \ art_runtime_tests \ art_runtime_compiler_tests \ @@ -683,6 +690,9 @@ ART_GTEST_dex2oat_image_test_DEX_DEPS := ART_GTEST_dex2oat_image_test_HOST_DEPS := ART_GTEST_dex2oat_image_test_TARGET_DEPS := ART_GTEST_object_test_DEX_DEPS := +ART_GTEST_patchoat_test_DEX_DEPS := +ART_GTEST_patchoat_test_HOST_DEPS := +ART_GTEST_patchoat_test_TARGET_DEPS := ART_GTEST_proxy_test_DEX_DEPS := ART_GTEST_reflection_test_DEX_DEPS := ART_GTEST_stub_test_DEX_DEPS := diff --git a/patchoat/Android.bp b/patchoat/Android.bp index d3bc2a754b..0902823644 100644 --- a/patchoat/Android.bp +++ b/patchoat/Android.bp @@ -47,3 +47,16 @@ art_cc_binary { "libartd", ], } + +art_cc_test { + name: "art_patchoat_tests", + defaults: [ + "art_gtest_defaults", + ], + srcs: [ + "patchoat_test.cc", + ], + shared_libs: [ + "libartd", + ], +} diff --git a/patchoat/patchoat_test.cc b/patchoat/patchoat_test.cc new file mode 100644 index 0000000000..e06b2dbf38 --- /dev/null +++ b/patchoat/patchoat_test.cc @@ -0,0 +1,346 @@ +/* + * 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. + */ + +#include +#include + +#include "android-base/stringprintf.h" +#include "android-base/strings.h" + +#include "dexopt_test.h" +#include "runtime.h" + +#include + +namespace art { + +using android::base::StringPrintf; + +class PatchoatTest : public DexoptTest { + public: + static void AddRuntimeArg(std::vector& args, const std::string& arg) { + args.push_back("--runtime-arg"); + args.push_back(arg); + } + + bool CompileBootImage(const std::vector& extra_args, + const std::string& image_file_name_prefix, + uint32_t base_addr, + std::string* error_msg) { + Runtime* const runtime = Runtime::Current(); + std::vector argv; + argv.push_back(runtime->GetCompilerExecutable()); + AddRuntimeArg(argv, "-Xms64m"); + AddRuntimeArg(argv, "-Xmx64m"); + std::vector dex_files = GetLibCoreDexFileNames(); + for (const std::string& dex_file : dex_files) { + argv.push_back("--dex-file=" + dex_file); + argv.push_back("--dex-location=" + dex_file); + } + if (runtime->IsJavaDebuggable()) { + argv.push_back("--debuggable"); + } + runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv); + + AddRuntimeArg(argv, "-Xverify:softfail"); + + if (!kIsTargetBuild) { + argv.push_back("--host"); + } + + argv.push_back("--image=" + image_file_name_prefix + ".art"); + argv.push_back("--oat-file=" + image_file_name_prefix + ".oat"); + argv.push_back("--oat-location=" + image_file_name_prefix + ".oat"); + argv.push_back(StringPrintf("--base=0x%" PRIx32, base_addr)); + argv.push_back("--compile-pic"); + argv.push_back("--multi-image"); + argv.push_back("--no-generate-debug-info"); + + std::vector compiler_options = runtime->GetCompilerOptions(); + argv.insert(argv.end(), compiler_options.begin(), compiler_options.end()); + + // We must set --android-root. + const char* android_root = getenv("ANDROID_ROOT"); + CHECK(android_root != nullptr); + argv.push_back("--android-root=" + std::string(android_root)); + argv.insert(argv.end(), extra_args.begin(), extra_args.end()); + + return RunDex2OatOrPatchoat(argv, error_msg); + } + + bool RelocateBootImage(const std::string& input_image_location, + const std::string& output_image_filename, + off_t base_offset_delta, + std::string* error_msg) { + Runtime* const runtime = Runtime::Current(); + std::vector argv; + argv.push_back(runtime->GetPatchoatExecutable()); + argv.push_back("--input-image-location=" + input_image_location); + argv.push_back("--output-image-file=" + output_image_filename); + argv.push_back(StringPrintf("--base-offset-delta=0x%jx", (intmax_t) base_offset_delta)); + argv.push_back(StringPrintf("--instruction-set=%s", GetInstructionSetString(kRuntimeISA))); + + return RunDex2OatOrPatchoat(argv, error_msg); + } + + bool RunDex2OatOrPatchoat(const std::vector& args, std::string* error_msg) { + int link[2]; + + if (pipe(link) == -1) { + return false; + } + + pid_t pid = fork(); + if (pid == -1) { + return false; + } + + if (pid == 0) { + // We need dex2oat to actually log things. + setenv("ANDROID_LOG_TAGS", "*:f", 1); + dup2(link[1], STDERR_FILENO); + close(link[0]); + close(link[1]); + std::vector c_args; + for (const std::string& str : args) { + c_args.push_back(str.c_str()); + } + c_args.push_back(nullptr); + execv(c_args[0], const_cast(c_args.data())); + exit(1); + UNREACHABLE(); + } else { + close(link[1]); + char buffer[128]; + memset(buffer, 0, 128); + ssize_t bytes_read = 0; + + while (TEMP_FAILURE_RETRY(bytes_read = read(link[0], buffer, 128)) > 0) { + *error_msg += std::string(buffer, bytes_read); + } + close(link[0]); + int status = -1; + if (waitpid(pid, &status, 0) != -1) { + return (status == 0); + } + return false; + } + } + + bool CompileBootImageToDir( + const std::string& output_dir, + const std::vector& dex2oat_extra_args, + uint32_t base_addr, + std::string* error_msg) { + return CompileBootImage(dex2oat_extra_args, output_dir + "/boot", base_addr, error_msg); + } + + bool CopyImageChecksumAndSetPatchDelta( + const std::string& src_image_filename, + const std::string& dest_image_filename, + off_t dest_patch_delta, + std::string* error_msg) { + std::unique_ptr src_file(OS::OpenFileForReading(src_image_filename.c_str())); + if (src_file.get() == nullptr) { + *error_msg = StringPrintf("Failed to open source image file %s", src_image_filename.c_str()); + return false; + } + ImageHeader src_header; + if (!src_file->ReadFully(&src_header, sizeof(src_header))) { + *error_msg = StringPrintf("Failed to read source image file %s", src_image_filename.c_str()); + return false; + } + + std::unique_ptr dest_file(OS::OpenFileReadWrite(dest_image_filename.c_str())); + if (dest_file.get() == nullptr) { + *error_msg = + StringPrintf("Failed to open destination image file %s", dest_image_filename.c_str()); + return false; + } + ImageHeader dest_header; + if (!dest_file->ReadFully(&dest_header, sizeof(dest_header))) { + *error_msg = + StringPrintf("Failed to read destination image file %s", dest_image_filename.c_str()); + return false; + } + dest_header.SetOatChecksum(src_header.GetOatChecksum()); + dest_header.SetPatchDelta(dest_patch_delta); + if (!dest_file->ResetOffset()) { + *error_msg = + StringPrintf( + "Failed to seek to start of destination image file %s", dest_image_filename.c_str()); + return false; + } + if (!dest_file->WriteFully(&dest_header, sizeof(dest_header))) { + *error_msg = + StringPrintf("Failed to write to destination image file %s", dest_image_filename.c_str()); + dest_file->Erase(); + return false; + } + if (dest_file->FlushCloseOrErase() != 0) { + *error_msg = + StringPrintf( + "Failed to flush/close destination image file %s", dest_image_filename.c_str()); + return false; + } + + return true; + } + + bool ReadFully( + const std::string& filename, std::vector* contents, std::string* error_msg) { + std::unique_ptr file(OS::OpenFileForReading(filename.c_str())); + if (file.get() == nullptr) { + *error_msg = "Failed to open"; + return false; + } + int64_t size = file->GetLength(); + if (size < 0) { + *error_msg = "Failed to get size"; + return false; + } + contents->resize(size); + if (!file->ReadFully(&(*contents)[0], size)) { + *error_msg = "Failed to read"; + contents->clear(); + return false; + } + return true; + } + + bool BinaryDiff( + const std::string& filename1, const std::string& filename2, std::string* error_msg) { + std::string read_error_msg; + std::vector image1; + if (!ReadFully(filename1, &image1, &read_error_msg)) { + *error_msg = StringPrintf("Failed to read %s: %s", filename1.c_str(), read_error_msg.c_str()); + return true; + } + std::vector image2; + if (!ReadFully(filename2, &image2, &read_error_msg)) { + *error_msg = StringPrintf("Failed to read %s: %s", filename2.c_str(), read_error_msg.c_str()); + return true; + } + if (image1.size() != image2.size()) { + *error_msg = + StringPrintf( + "%s and %s are of different size: %zu vs %zu", + filename1.c_str(), + filename2.c_str(), + image1.size(), + image2.size()); + return true; + } + size_t size = image1.size(); + for (size_t i = 0; i < size; i++) { + if (image1[i] != image2[i]) { + *error_msg = + StringPrintf("%s and %s differ at offset %zu", filename1.c_str(), filename2.c_str(), i); + return true; + } + } + + return false; + } +}; + +TEST_F(PatchoatTest, PatchoatRelocationSameAsDex2oatRelocation) { + // This test check that relocating a boot image using patchoat produces the same result as + // producing the boot image for that relocated base address using dex2oat. To be precise, these + // two files will have two small differences: the OAT checksum and base address. However, this + // test takes this into account. + + // 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; + // Force deterministic output. We want the boot images created by this dex2oat run and the run + // below to differ only in their base address. + std::vector dex2oat_extra_args; + dex2oat_extra_args.push_back("--force-determinism"); + dex2oat_extra_args.push_back("-j1"); // Might not be needed. Causes a 3-5x slowdown. + std::string error_msg; + if (!CompileBootImageToDir(dex2oat_orig_dir, dex2oat_extra_args, orig_base_addr, &error_msg)) { + FAIL() << "CompileBootImage1 failed: " << error_msg; + } + + // Compile a "relocated" boot image into a random directory using dex2oat. This image is relocated + // in the sense that it uses a different base address. + ScratchFile dex2oat_reloc_scratch; + dex2oat_reloc_scratch.Unlink(); + std::string dex2oat_reloc_dir = dex2oat_reloc_scratch.GetFilename(); + ASSERT_EQ(0, mkdir(dex2oat_reloc_dir.c_str(), 0700)); + const uint32_t reloc_base_addr = 0x70000000; + if (!CompileBootImageToDir(dex2oat_reloc_dir, dex2oat_extra_args, reloc_base_addr, &error_msg)) { + FAIL() << "CompileBootImage2 failed: " << error_msg; + } + const off_t base_addr_delta = reloc_base_addr - orig_base_addr; + + // Relocate the original boot image using patchoat. The image is relocated by the same amount + // as the second/relocated image produced by dex2oat. + ScratchFile patchoat_scratch; + patchoat_scratch.Unlink(); + std::string patchoat_dir = patchoat_scratch.GetFilename(); + ASSERT_EQ(0, mkdir(patchoat_dir.c_str(), 0700)); + 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())); + if (!RelocateBootImage( + dex2oat_orig_dir + "/boot.art", + patchoat_dir + "/boot.art", + base_addr_delta, + &error_msg)) { + FAIL() << "RelocateBootImage failed: " << error_msg; + } + + // dex2oat_reloc_image_filename is the boot image relocated using dex2oat + // patchoat_reloc_image_filename is the boot image relocated using patchoat + std::string dex2oat_reloc_image_filename = dex2oat_reloc_dir + "/boot.art"; + std::string patchoat_reloc_image_filename = dex2oat_orig_dir + "/boot.art"; + std::replace( + patchoat_reloc_image_filename.begin() + 1, patchoat_reloc_image_filename.end(), '/', '@'); + patchoat_reloc_image_filename = + patchoat_dir + + (android::base::StartsWith(patchoat_reloc_image_filename, "/") ? "" : "/") + + patchoat_reloc_image_filename; + + // Patch up the dex2oat-relocated image so that it looks as though it was relocated by patchoat. + // patchoat preserves the OAT checksum header field and sets patch delta header field. + if (!CopyImageChecksumAndSetPatchDelta( + dex2oat_orig_dir + "/boot.art", + dex2oat_reloc_dir + "/boot.art", + base_addr_delta, + &error_msg)) { + FAIL() << "Unable to copy image checksum: " << error_msg; + } + + // Assert that the patchoat-relocated image is identical to the dex2oat-relocated image + if (BinaryDiff(dex2oat_reloc_image_filename, patchoat_reloc_image_filename, &error_msg)) { + FAIL() << "patchoat- and dex2oat-relocated images differ: " << error_msg; + } + + ClearDirectory(dex2oat_orig_dir.c_str(), /*recursive*/ true); + ClearDirectory(dex2oat_reloc_dir.c_str(), /*recursive*/ true); + ClearDirectory(patchoat_dir.c_str(), /*recursive*/ true); + rmdir(dex2oat_orig_dir.c_str()); + rmdir(dex2oat_reloc_dir.c_str()); + rmdir(patchoat_dir.c_str()); +} + +} // namespace art diff --git a/runtime/image.h b/runtime/image.h index 3844186a9b..159a308fb3 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -179,6 +179,10 @@ class PACKED(4) ImageHeader { return patch_delta_; } + void SetPatchDelta(off_t patch_delta) { + patch_delta_ = patch_delta; + } + static std::string GetOatLocationFromImageLocation(const std::string& image) { return GetLocationFromImageLocation(image, "oat"); } -- GitLab From b0042b83f9695bb7e2f07c451166a97a6c14383f Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 13 Dec 2017 13:50:36 -0800 Subject: [PATCH 182/226] Specify INFO log severity for -XX:DumpGCPerformanceOnShutdown The art script only has severity set to warning, this was causing the GC timings to not get output. Bug: 70630582 Test: art -Xmx512m -XX:DumpGCPerformanceOnShutdown .. some benchmark Change-Id: I03e4e09836f0bd73fbbff3640b4db8617cc63c95 --- runtime/runtime.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 7239ad3213..1cdeb7c77c 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -297,6 +297,7 @@ Runtime::~Runtime() { } if (dump_gc_performance_on_shutdown_) { + ScopedLogSeverity sls(LogSeverity::INFO); // This can't be called from the Heap destructor below because it // could call RosAlloc::InspectAll() which needs the thread_list // to be still alive. -- GitLab From ae7e83817e546848ef6b2949dd9065b153e14316 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 20 Nov 2017 15:10:28 +0000 Subject: [PATCH 183/226] Don't embed the dex code in the oat file if dex is uncompressed. Take uncompressed dex code as a signal that the app wants to opt into b/63920015. bug: 63920015 Test: dex2oat_test, 071-dexfile-clean-map Change-Id: I878e7bb80fc895a2d9aafe81aa7666b86af1f808 --- build/Android.gtest.mk | 19 ++++++++- compiler/driver/compiler_driver.cc | 6 +++ compiler/driver/compiler_driver.h | 6 +-- dex2oat/dex2oat.cc | 12 +++--- dex2oat/dex2oat_test.cc | 15 +++++++ dex2oat/linker/image_test.h | 12 +++--- dex2oat/linker/oat_writer.cc | 68 ++++++++++++++++++++++++++---- dex2oat/linker/oat_writer.h | 6 ++- dex2oat/linker/oat_writer_test.cc | 8 ++-- runtime/dex_file.h | 4 ++ runtime/dex_file_loader.cc | 2 +- runtime/oat_file.cc | 50 +++++++++++++--------- runtime/oat_file.h | 9 ++++ runtime/zip_archive.cc | 7 ++- runtime/zip_archive.h | 3 +- 15 files changed, 176 insertions(+), 51 deletions(-) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 3c8eade773..9af6cf46b4 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -72,6 +72,11 @@ $(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval $(call build-art-test-dex,art-gte ART_TEST_HOST_GTEST_MainStripped_DEX := $(basename $(ART_TEST_HOST_GTEST_Main_DEX))Stripped$(suffix $(ART_TEST_HOST_GTEST_Main_DEX)) ART_TEST_TARGET_GTEST_MainStripped_DEX := $(basename $(ART_TEST_TARGET_GTEST_Main_DEX))Stripped$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX)) +# Create rules for MainUncompressed, a copy of Main with the classes.dex uncompressed +# for the dex2oat tests. +ART_TEST_HOST_GTEST_MainUncompressed_DEX := $(basename $(ART_TEST_HOST_GTEST_Main_DEX))Uncompressed$(suffix $(ART_TEST_HOST_GTEST_Main_DEX)) +ART_TEST_TARGET_GTEST_MainUncompressed_DEX := $(basename $(ART_TEST_TARGET_GTEST_Main_DEX))Uncompressed$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX)) + $(ART_TEST_HOST_GTEST_MainStripped_DEX): $(ART_TEST_HOST_GTEST_Main_DEX) cp $< $@ $(call dexpreopt-remove-classes.dex,$@) @@ -80,6 +85,16 @@ $(ART_TEST_TARGET_GTEST_MainStripped_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX) cp $< $@ $(call dexpreopt-remove-classes.dex,$@) +$(ART_TEST_HOST_GTEST_MainUncompressed_DEX): $(ART_TEST_HOST_GTEST_Main_DEX) $(ZIPALIGN) + cp $< $@ + $(call uncompress-dexs, $@) + $(call align-package, $@) + +$(ART_TEST_TARGET_GTEST_MainUncompressed_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX) $(ZIPALIGN) + cp $< $@ + $(call uncompress-dexs, $@) + $(call align-package, $@) + ART_TEST_GTEST_VerifierDeps_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDeps/*.smali)) ART_TEST_GTEST_VerifierDepsMulti_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDepsMulti/*.smali)) ART_TEST_HOST_GTEST_VerifierDeps_DEX := $(dir $(ART_TEST_HOST_GTEST_Main_DEX))$(subst Main,VerifierDeps,$(basename $(notdir $(ART_TEST_HOST_GTEST_Main_DEX))))$(suffix $(ART_TEST_HOST_GTEST_Main_DEX)) @@ -110,7 +125,7 @@ ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods Prof ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested MultiDex ART_GTEST_dexlayout_test_DEX_DEPS := ManyMethods -ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ManyMethods Statics VerifierDeps +ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ManyMethods Statics VerifierDeps MainUncompressed 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_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods @@ -696,6 +711,8 @@ $(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 := ART_TEST_TARGET_GTEST_MainStripped_DEX := +ART_TEST_HOST_GTEST_MainUncompressed_DEX := +ART_TEST_TARGET_GTEST_MainUncompressed_DEX := ART_TEST_GTEST_VerifierDeps_SRC := ART_TEST_HOST_GTEST_VerifierDeps_DEX := ART_TEST_TARGET_GTEST_VerifierDeps_DEX := diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 0ca3c8f613..d1cb175430 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -401,6 +401,12 @@ static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel( Thread* self, const CompilerDriver& driver, Handle class_loader, const DexFile& dex_file, const DexFile::ClassDef& class_def) REQUIRES_SHARED(Locks::mutator_lock_) { + // When the dex file is uncompressed in the APK, we do not generate a copy in the .vdex + // file. As a result, dex2oat will map the dex file read-only, and we only need to check + // that to know if we can do quickening. + if (dex_file.GetContainer() != nullptr && dex_file.GetContainer()->IsReadOnly()) { + return optimizer::DexToDexCompilationLevel::kDontDexToDexCompile; + } auto* const runtime = Runtime::Current(); DCHECK(driver.GetCompilerOptions().IsQuickeningCompilationEnabled()); const char* descriptor = dex_file.GetClassDescriptor(class_def); diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index d2141e8bc7..eab15b9f49 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -102,13 +102,13 @@ class CompilerDriver { ~CompilerDriver(); - // Set dex files that will be stored in the oat file after being compiled. + // Set dex files associated with the oat file being compiled. void SetDexFilesForOatFile(const std::vector& dex_files); // Set dex files classpath. void SetClasspathDexFiles(const std::vector& dex_files); - // Get dex file that will be stored in the oat file after being compiled. + // Get dex files associated with the the oat file being compiled. ArrayRef GetDexFilesForOatFile() const { return ArrayRef(dex_files_for_oat_file_); } @@ -528,7 +528,7 @@ class CompilerDriver { bool support_boot_image_fixup_; - // List of dex files that will be stored in the oat file. + // List of dex files associates with the oat file. std::vector dex_files_for_oat_file_; CompiledMethodStorage compiled_method_storage_; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 27bec1d3e1..3f4904bb77 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1550,7 +1550,7 @@ class Dex2Oat FINAL { for (size_t i = 0, size = oat_writers_.size(); i != size; ++i) { rodata_.push_back(elf_writers_[i]->StartRoData()); // Unzip or copy dex files straight to the oat file. - std::unique_ptr opened_dex_files_map; + 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 @@ -1570,14 +1570,16 @@ class Dex2Oat FINAL { return dex2oat::ReturnCode::kOther; } dex_files_per_oat_file_.push_back(MakeNonOwningPointerVector(opened_dex_files)); - if (opened_dex_files_map != nullptr) { - opened_dex_files_maps_.push_back(std::move(opened_dex_files_map)); + if (opened_dex_files_map.empty()) { + DCHECK(opened_dex_files.empty()); + } else { + for (std::unique_ptr& map : opened_dex_files_map) { + opened_dex_files_maps_.push_back(std::move(map)); + } for (std::unique_ptr& dex_file : opened_dex_files) { dex_file_oat_index_map_.emplace(dex_file.get(), i); opened_dex_files_.push_back(std::move(dex_file)); } - } else { - DCHECK(opened_dex_files.empty()); } } } diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index ad287b0745..41f2c70267 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -1513,4 +1513,19 @@ TEST_F(Dex2oatDedupeCode, DedupeTest) { EXPECT_LT(dedupe_size, no_dedupe_size); } +TEST_F(Dex2oatTest, UncompressedTest) { + std::unique_ptr dex(OpenTestDexFile("MainUncompressed")); + std::string out_dir = GetScratchDir(); + const std::string base_oat_name = out_dir + "/base.oat"; + GenerateOdexForTest(dex->GetLocation(), + base_oat_name, + CompilerFilter::Filter::kQuicken, + { }, + true, // expect_success + false, // use_fd + [](const OatFile& o) { + CHECK(!o.ContainsDexCode()); + }); +} + } // namespace art diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index cedbccf7cc..208297b8f6 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -253,7 +253,7 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, } std::vector rodata; - std::vector> opened_dex_files_map; + std::vector> opened_dex_files_maps; std::vector> opened_dex_files; // Now that we have finalized key_value_store_, start writing the oat file. for (size_t i = 0, size = oat_writers.size(); i != size; ++i) { @@ -266,7 +266,7 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, dex_file->GetLocation().c_str(), dex_file->GetLocationChecksum()); - std::unique_ptr cur_opened_dex_files_map; + std::vector> cur_opened_dex_files_maps; std::vector> cur_opened_dex_files; bool dex_files_ok = oat_writers[i]->WriteAndOpenDexFiles( vdex_files[i].GetFile(), @@ -276,12 +276,14 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, &key_value_store, /* verify */ false, // Dex files may be dex-to-dex-ed, don't verify. /* update_input_vdex */ false, - &cur_opened_dex_files_map, + &cur_opened_dex_files_maps, &cur_opened_dex_files); ASSERT_TRUE(dex_files_ok); - if (cur_opened_dex_files_map != nullptr) { - opened_dex_files_map.push_back(std::move(cur_opened_dex_files_map)); + if (!cur_opened_dex_files_maps.empty()) { + for (std::unique_ptr& cur_map : cur_opened_dex_files_maps) { + opened_dex_files_maps.push_back(std::move(cur_map)); + } for (std::unique_ptr& cur_dex_file : cur_opened_dex_files) { // dex_file_oat_index_map_.emplace(dex_file.get(), i); opened_dex_files.push_back(std::move(cur_dex_file)); diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 293078acee..9e61c42ff5 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -363,6 +363,7 @@ OatWriter::OatWriter(bool compiling_boot_image, compiler_driver_(nullptr), image_writer_(nullptr), compiling_boot_image_(compiling_boot_image), + only_contains_uncompressed_zip_entries_(false), dex_files_(nullptr), vdex_size_(0u), vdex_dex_files_offset_(0u), @@ -636,7 +637,7 @@ bool OatWriter::WriteAndOpenDexFiles( SafeMap* key_value_store, bool verify, bool update_input_vdex, - /*out*/ std::unique_ptr* opened_dex_files_map, + /*out*/ std::vector>* opened_dex_files_map, /*out*/ std::vector>* opened_dex_files) { CHECK(write_state_ == WriteState::kAddingDexFileSources); @@ -645,7 +646,7 @@ bool OatWriter::WriteAndOpenDexFiles( return false; } - std::unique_ptr dex_files_map; + std::vector> dex_files_map; std::vector> dex_files; // Initialize VDEX and OAT headers. @@ -3287,14 +3288,28 @@ bool OatWriter::WriteDexFiles(OutputStream* out, File* file, bool update_input_v vdex_dex_files_offset_ = vdex_size_; - // Write dex files. + only_contains_uncompressed_zip_entries_ = true; for (OatDexFile& oat_dex_file : oat_dex_files_) { - if (!WriteDexFile(out, file, &oat_dex_file, update_input_vdex)) { - return false; + if (!oat_dex_file.source_.IsZipEntry()) { + only_contains_uncompressed_zip_entries_ = false; + break; + } + ZipEntry* entry = oat_dex_file.source_.GetZipEntry(); + if (!entry->IsUncompressed() || !entry->IsAlignedToDexHeader()) { + only_contains_uncompressed_zip_entries_ = false; + break; + } + } + + if (!only_contains_uncompressed_zip_entries_) { + // Write dex files. + for (OatDexFile& oat_dex_file : oat_dex_files_) { + if (!WriteDexFile(out, file, &oat_dex_file, update_input_vdex)) { + return false; + } } } - CloseSources(); return true; } @@ -3616,7 +3631,7 @@ bool OatWriter::WriteDexFile(OutputStream* out, bool OatWriter::OpenDexFiles( File* file, bool verify, - /*out*/ std::unique_ptr* opened_dex_files_map, + /*out*/ std::vector>* opened_dex_files_map, /*out*/ std::vector>* opened_dex_files) { TimingLogger::ScopedTiming split("OpenDexFiles", timings_); @@ -3625,6 +3640,43 @@ bool OatWriter::OpenDexFiles( return true; } + if (only_contains_uncompressed_zip_entries_) { + std::vector> dex_files; + std::vector> maps; + for (OatDexFile& oat_dex_file : oat_dex_files_) { + std::string error_msg; + MemMap* map = oat_dex_file.source_.GetZipEntry()->MapDirectlyFromFile( + oat_dex_file.dex_file_location_data_, &error_msg); + if (map == nullptr) { + LOG(ERROR) << error_msg; + return false; + } + maps.emplace_back(map); + // Now, open the dex file. + dex_files.emplace_back(DexFileLoader::Open(map->Begin(), + map->Size(), + oat_dex_file.GetLocation(), + oat_dex_file.dex_file_location_checksum_, + /* oat_dex_file */ nullptr, + verify, + verify, + &error_msg)); + if (dex_files.back() == nullptr) { + LOG(ERROR) << "Failed to open dex file from oat file. File: " << oat_dex_file.GetLocation() + << " Error: " << error_msg; + return false; + } + oat_dex_file.class_offsets_.resize(dex_files.back()->GetHeader().class_defs_size_); + } + *opened_dex_files_map = std::move(maps); + *opened_dex_files = std::move(dex_files); + CloseSources(); + return true; + } + // We could have closed the sources at the point of writing the dex files, but to + // make it consistent with the case we're not writing the dex files, we close them now. + CloseSources(); + size_t map_offset = oat_dex_files_[0].dex_file_offset_; size_t length = vdex_size_ - map_offset; @@ -3683,7 +3735,7 @@ bool OatWriter::OpenDexFiles( oat_dex_file.class_offsets_.resize(dex_files.back()->GetHeader().class_defs_size_); } - *opened_dex_files_map = std::move(dex_files_map); + opened_dex_files_map->push_back(std::move(dex_files_map)); *opened_dex_files = std::move(dex_files); return true; } diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index 4055878b55..3e9f4636fb 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -173,7 +173,7 @@ class OatWriter { SafeMap* key_value_store, bool verify, bool update_input_vdex, - /*out*/ std::unique_ptr* opened_dex_files_map, + /*out*/ std::vector>* opened_dex_files_map, /*out*/ std::vector>* opened_dex_files); bool WriteQuickeningInfo(OutputStream* vdex_out); bool WriteVerifierDeps(OutputStream* vdex_out, verifier::VerifierDeps* verifier_deps); @@ -300,7 +300,7 @@ class OatWriter { bool update_input_vdex); bool OpenDexFiles(File* file, bool verify, - /*out*/ std::unique_ptr* opened_dex_files_map, + /*out*/ std::vector>* opened_dex_files_map, /*out*/ std::vector>* opened_dex_files); size_t InitOatHeader(InstructionSet instruction_set, @@ -367,6 +367,8 @@ class OatWriter { const CompilerDriver* compiler_driver_; ImageWriter* image_writer_; const bool compiling_boot_image_; + // Whether the dex files being compiled are all uncompressed in the APK. + bool only_contains_uncompressed_zip_entries_; // note OatFile does not take ownership of the DexFiles const std::vector* dex_files_; diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index fec05cc393..f4dfcd6a0d 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -186,7 +186,7 @@ class OatTest : public CommonCompilerTest { oat_file); elf_writer->Start(); OutputStream* oat_rodata = elf_writer->StartRoData(); - std::unique_ptr opened_dex_files_map; + std::vector> opened_dex_files_maps; std::vector> opened_dex_files; if (!oat_writer.WriteAndOpenDexFiles(vdex_file, oat_rodata, @@ -195,7 +195,7 @@ class OatTest : public CommonCompilerTest { &key_value_store, verify, /* update_input_vdex */ false, - &opened_dex_files_map, + &opened_dex_files_maps, &opened_dex_files)) { return false; } @@ -251,7 +251,9 @@ class OatTest : public CommonCompilerTest { return false; } - opened_dex_files_maps_.emplace_back(std::move(opened_dex_files_map)); + for (std::unique_ptr& map : opened_dex_files_maps) { + opened_dex_files_maps_.emplace_back(std::move(map)); + } for (std::unique_ptr& dex_file : opened_dex_files) { opened_dex_files_.emplace_back(dex_file.release()); } diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 944a30849f..73b8c30d96 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -1032,6 +1032,10 @@ class DexFile { ALWAYS_INLINE const StandardDexFile* AsStandardDexFile() const; ALWAYS_INLINE const CompactDexFile* AsCompactDexFile() const; + DexFileContainer* GetContainer() const { + return container_.get(); + } + protected: DexFile(const uint8_t* base, size_t size, diff --git a/runtime/dex_file_loader.cc b/runtime/dex_file_loader.cc index bc9276985b..6d4bbf13d8 100644 --- a/runtime/dex_file_loader.cc +++ b/runtime/dex_file_loader.cc @@ -379,7 +379,7 @@ std::unique_ptr DexFileLoader::OpenOneDexFileFromZip( std::unique_ptr map; if (zip_entry->IsUncompressed()) { - if (!zip_entry->IsAlignedTo(alignof(DexFile::Header))) { + if (!zip_entry->IsAlignedToDexHeader()) { // Do not mmap unaligned ZIP entries because // doing so would fail dex verification which requires 4 byte alignment. LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; " diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index c82df7119f..1d05399de2 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -594,14 +594,6 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { dex_file_location.c_str()); return false; } - if (UNLIKELY(dex_file_offset == 0U)) { - *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with zero dex " - "file offset", - GetLocation().c_str(), - i, - dex_file_location.c_str()); - return false; - } if (UNLIKELY(dex_file_offset > DexSize())) { *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file " "offset %u > %zu", @@ -612,20 +604,36 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { DexSize()); return false; } - if (UNLIKELY(DexSize() - dex_file_offset < sizeof(DexFile::Header))) { - *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file " - "offset %u of %zu but the size of dex file header is %zu", - GetLocation().c_str(), - i, - dex_file_location.c_str(), - dex_file_offset, - DexSize(), - sizeof(DexFile::Header)); - return false; + const uint8_t* dex_file_pointer = nullptr; + if (UNLIKELY(dex_file_offset == 0U)) { + if (uncompressed_dex_files_ == nullptr) { + uncompressed_dex_files_.reset(new std::vector>()); + // No dex files, load it from location. + if (!DexFileLoader::Open(dex_file_location.c_str(), + dex_file_location, + /* verify */ false, + /* verify_checksum */ false, + error_msg, + uncompressed_dex_files_.get())) { + return false; + } + } + dex_file_pointer = uncompressed_dex_files_.get()->at(i)->Begin(); + } else { + if (UNLIKELY(DexSize() - dex_file_offset < sizeof(DexFile::Header))) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file " + "offset %u of %zu but the size of dex file header is %zu", + GetLocation().c_str(), + i, + dex_file_location.c_str(), + dex_file_offset, + DexSize(), + sizeof(DexFile::Header)); + return false; + } + dex_file_pointer = DexBegin() + dex_file_offset; } - const uint8_t* dex_file_pointer = DexBegin() + dex_file_offset; - const bool valid_magic = DexFileLoader::IsMagicValid(dex_file_pointer); if (UNLIKELY(!valid_magic)) { *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with invalid " @@ -646,7 +654,7 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { return false; } const DexFile::Header* header = reinterpret_cast(dex_file_pointer); - if (DexSize() - dex_file_offset < header->file_size_) { + if (dex_file_offset != 0 && (DexSize() - dex_file_offset < header->file_size_)) { *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file " "offset %u and size %u truncated at %zu", GetLocation().c_str(), diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 36a4d7b8fc..61daa9579e 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -324,6 +324,11 @@ class OatFile { return vdex_.get(); } + // Whether the OatFile embeds the Dex code. + bool ContainsDexCode() const { + return uncompressed_dex_files_ == nullptr; + } + protected: OatFile(const std::string& filename, bool executable); @@ -389,6 +394,10 @@ class OatFile { // elements. std::list<> and std::deque<> satisfy this requirement, std::vector<> doesn't. mutable std::list string_cache_ GUARDED_BY(secondary_lookup_lock_); + // Cache of dex files mapped directly from a location, in case the OatFile does + // not embed the dex code. + std::unique_ptr>> uncompressed_dex_files_; + friend class gc::collector::DummyOatFile; // For modifying begin_ and end_. friend class OatClass; friend class art::OatDexFile; diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc index f3d4d77214..279c0e4e83 100644 --- a/runtime/zip_archive.cc +++ b/runtime/zip_archive.cc @@ -27,6 +27,7 @@ #include "android-base/stringprintf.h" #include "ziparchive/zip_archive.h" +#include "dex_file.h" #include "base/bit_utils.h" #include "base/unix_file/fd_file.h" @@ -49,11 +50,15 @@ bool ZipEntry::IsUncompressed() { return zip_entry_->method == kCompressStored; } -bool ZipEntry::IsAlignedTo(size_t alignment) { +bool ZipEntry::IsAlignedTo(size_t alignment) const { DCHECK(IsPowerOfTwo(alignment)) << alignment; 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/runtime/zip_archive.h index 821cc5ceb1..ca7bbe965b 100644 --- a/runtime/zip_archive.h +++ b/runtime/zip_archive.h @@ -58,7 +58,8 @@ class ZipEntry { uint32_t GetCrc32(); bool IsUncompressed(); - bool IsAlignedTo(size_t alignment); + bool IsAlignedTo(size_t alignment) const; + bool IsAlignedToDexHeader() const; private: ZipEntry(ZipArchiveHandle handle, -- GitLab From e4033fa9647774382d303f3b20e7139998565035 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Wed, 13 Dec 2017 10:32:59 -0800 Subject: [PATCH 184/226] Loosen check:jni around GetStatic[...]Field -Xcheck:jni was requiring that the jclass being passed to GetStatic[...]Field was the exact same class as the jfieldID's declaring class. This was stricter than is really needed and causes some issues when dealing with fields where the declaring class cannot be easily determined. Bug: 70532839 Test: ./test.py --host -j50 Change-Id: I0987de09af956ed9a8dde37c50606604fdd94b87 --- runtime/check_jni.cc | 4 +- runtime/jni_internal_test.cc | 145 +++++++++++++++++-------- test/004-JniTest/expected.txt | 1 + test/004-JniTest/jni_test.cc | 8 ++ test/004-JniTest/src/Main.java | 15 +++ test/AllFields/AllFields.java | 2 +- test/AllFields/AllFieldsSub.java | 17 +++ test/AllFields/AllFieldsUnrelated.java | 17 +++ 8 files changed, 158 insertions(+), 51 deletions(-) create mode 100644 test/AllFields/AllFieldsSub.java create mode 100644 test/AllFields/AllFieldsUnrelated.java diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index 0b9bf225d4..90f478f5f4 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -373,7 +373,7 @@ class ScopedCheck { if (f == nullptr) { return false; } - if (c != f->GetDeclaringClass()) { + if (!f->GetDeclaringClass()->IsAssignableFrom(c)) { AbortF("static jfieldID %p not valid for class %s", fid, mirror::Class::PrettyClass(c).c_str()); return false; @@ -710,7 +710,7 @@ class ScopedCheck { return false; } ObjPtr c = o->AsClass(); - if (c != field->GetDeclaringClass()) { + if (!field->GetDeclaringClass()->IsAssignableFrom(c)) { AbortF("attempt to access static field %s with an incompatible class argument of %s: %p", field->PrettyField().c_str(), mirror::Class::PrettyDescriptor(c).c_str(), fid); return false; diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index 1ecfe7cb76..efeff0a378 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -1783,66 +1783,115 @@ TEST_F(JniInternalTest, GetObjectArrayElement_SetObjectArrayElement) { EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); \ } while (false) +#define TEST_PRIMITIVE_FIELD_FOR_CLASS(cname) \ + do { \ + Thread::Current()->TransitionFromSuspendedToRunnable(); \ + LoadDex("AllFields"); \ + bool started = runtime_->Start(); \ + ASSERT_TRUE(started); \ + jclass c = env_->FindClass(cname); \ + ASSERT_NE(c, nullptr); \ + jobject o = env_->AllocObject(c); \ + ASSERT_NE(o, nullptr); \ + \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Boolean, "sZ", "Z", JNI_TRUE, JNI_FALSE); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Byte, "sB", "B", 1, 2); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Char, "sC", "C", 'a', 'b'); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_DOUBLE_EQ, Double, "sD", "D", 1.0, 2.0); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_FLOAT_EQ, Float, "sF", "F", 1.0, 2.0); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Int, "sI", "I", 1, 2); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Long, "sJ", "J", 1, 2); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Short, "sS", "S", 1, 2); \ + \ + EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Boolean, "iZ", "Z", JNI_TRUE, JNI_FALSE); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Byte, "iB", "B", 1, 2); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Char, "iC", "C", 'a', 'b'); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_DOUBLE_EQ, o, Double, "iD", "D", 1.0, 2.0); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_FLOAT_EQ, o, Float, "iF", "F", 1.0, 2.0); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Int, "iI", "I", 1, 2); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Long, "iJ", "J", 1, 2); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Short, "iS", "S", 1, 2); \ + } while (false) TEST_F(JniInternalTest, GetPrimitiveField_SetPrimitiveField) { + TEST_PRIMITIVE_FIELD_FOR_CLASS("AllFields"); +} + +TEST_F(JniInternalTest, GetPrimitiveField_SetPrimitiveField_Subclass) { + TEST_PRIMITIVE_FIELD_FOR_CLASS("AllFieldsSub"); +} + +#define EXPECT_UNRELATED_FIELD_FAILURE(type, field_name, sig, value1) \ + do { \ + jfieldID fid = env_->GetStaticFieldID(c, field_name, sig); \ + EXPECT_NE(fid, nullptr); \ + CheckJniAbortCatcher jni_abort_catcher; \ + env_->Get ## type ## Field(uc, fid); \ + jni_abort_catcher.Check("not valid for an object of class"); \ + env_->Set ## type ## Field(uc, fid, value1); \ + jni_abort_catcher.Check("not valid for an object of class"); \ + } while (false) + +TEST_F(JniInternalTest, GetField_SetField_unrelated) { Thread::Current()->TransitionFromSuspendedToRunnable(); LoadDex("AllFields"); bool started = runtime_->Start(); ASSERT_TRUE(started); - jclass c = env_->FindClass("AllFields"); ASSERT_NE(c, nullptr); - jobject o = env_->AllocObject(c); - ASSERT_NE(o, nullptr); - - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Boolean, "sZ", "Z", JNI_TRUE, JNI_FALSE); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Byte, "sB", "B", 1, 2); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Char, "sC", "C", 'a', 'b'); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_DOUBLE_EQ, Double, "sD", "D", 1.0, 2.0); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_FLOAT_EQ, Float, "sF", "F", 1.0, 2.0); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Int, "sI", "I", 1, 2); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Long, "sJ", "J", 1, 2); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Short, "sS", "S", 1, 2); - - EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Boolean, "iZ", "Z", JNI_TRUE, JNI_FALSE); - EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Byte, "iB", "B", 1, 2); - EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Char, "iC", "C", 'a', 'b'); - EXPECT_PRIMITIVE_FIELD(EXPECT_DOUBLE_EQ, o, Double, "iD", "D", 1.0, 2.0); - EXPECT_PRIMITIVE_FIELD(EXPECT_FLOAT_EQ, o, Float, "iF", "F", 1.0, 2.0); - EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Int, "iI", "I", 1, 2); - EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Long, "iJ", "J", 1, 2); - EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Short, "iS", "S", 1, 2); + jclass uc = env_->FindClass("AllFieldsUnrelated"); + ASSERT_NE(uc, nullptr); + bool old_check_jni = vm_->SetCheckJniEnabled(true); + EXPECT_UNRELATED_FIELD_FAILURE(Boolean, "sZ", "Z", JNI_TRUE); + EXPECT_UNRELATED_FIELD_FAILURE(Byte, "sB", "B", 1); + EXPECT_UNRELATED_FIELD_FAILURE(Char, "sC", "C", 'a'); + EXPECT_UNRELATED_FIELD_FAILURE(Double, "sD", "D", 1.0); + EXPECT_UNRELATED_FIELD_FAILURE(Float, "sF", "F", 1.0); + EXPECT_UNRELATED_FIELD_FAILURE(Int, "sI", "I", 1); + EXPECT_UNRELATED_FIELD_FAILURE(Long, "sJ", "J", 1); + EXPECT_UNRELATED_FIELD_FAILURE(Short, "sS", "S", 1); + EXPECT_UNRELATED_FIELD_FAILURE(Object, "sObject", "Ljava/lang/Object;", c); + EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); } -TEST_F(JniInternalTest, GetObjectField_SetObjectField) { - Thread::Current()->TransitionFromSuspendedToRunnable(); - LoadDex("AllFields"); - runtime_->Start(); +#define TEST_OBJECT_FIELD_FOR_CLASS(cname) \ + do { \ + Thread::Current()->TransitionFromSuspendedToRunnable(); \ + LoadDex("AllFields"); \ + runtime_->Start(); \ + \ + jclass c = env_->FindClass(cname); \ + ASSERT_NE(c, nullptr); \ + jobject o = env_->AllocObject(c); \ + ASSERT_NE(o, nullptr); \ + \ + jstring s1 = env_->NewStringUTF("hello"); \ + ASSERT_NE(s1, nullptr); \ + jstring s2 = env_->NewStringUTF("world"); \ + ASSERT_NE(s2, nullptr); \ + \ + jfieldID s_fid = env_->GetStaticFieldID(c, "sObject", "Ljava/lang/Object;"); \ + ASSERT_NE(s_fid, nullptr); \ + jfieldID i_fid = env_->GetFieldID(c, "iObject", "Ljava/lang/Object;"); \ + ASSERT_NE(i_fid, nullptr); \ + \ + env_->SetStaticObjectField(c, s_fid, s1); \ + ASSERT_TRUE(env_->IsSameObject(s1, env_->GetStaticObjectField(c, s_fid))); \ + env_->SetStaticObjectField(c, s_fid, s2); \ + ASSERT_TRUE(env_->IsSameObject(s2, env_->GetStaticObjectField(c, s_fid))); \ + \ + env_->SetObjectField(o, i_fid, s1); \ + ASSERT_TRUE(env_->IsSameObject(s1, env_->GetObjectField(o, i_fid))); \ + env_->SetObjectField(o, i_fid, s2); \ + ASSERT_TRUE(env_->IsSameObject(s2, env_->GetObjectField(o, i_fid))); \ + } while (false) - jclass c = env_->FindClass("AllFields"); - ASSERT_NE(c, nullptr); - jobject o = env_->AllocObject(c); - ASSERT_NE(o, nullptr); +TEST_F(JniInternalTest, GetObjectField_SetObjectField) { + TEST_OBJECT_FIELD_FOR_CLASS("AllFields"); +} - jstring s1 = env_->NewStringUTF("hello"); - ASSERT_NE(s1, nullptr); - jstring s2 = env_->NewStringUTF("world"); - ASSERT_NE(s2, nullptr); - - jfieldID s_fid = env_->GetStaticFieldID(c, "sObject", "Ljava/lang/Object;"); - ASSERT_NE(s_fid, nullptr); - jfieldID i_fid = env_->GetFieldID(c, "iObject", "Ljava/lang/Object;"); - ASSERT_NE(i_fid, nullptr); - - env_->SetStaticObjectField(c, s_fid, s1); - ASSERT_TRUE(env_->IsSameObject(s1, env_->GetStaticObjectField(c, s_fid))); - env_->SetStaticObjectField(c, s_fid, s2); - ASSERT_TRUE(env_->IsSameObject(s2, env_->GetStaticObjectField(c, s_fid))); - - env_->SetObjectField(o, i_fid, s1); - ASSERT_TRUE(env_->IsSameObject(s1, env_->GetObjectField(o, i_fid))); - env_->SetObjectField(o, i_fid, s2); - ASSERT_TRUE(env_->IsSameObject(s2, env_->GetObjectField(o, i_fid))); +TEST_F(JniInternalTest, GetObjectField_SetObjectField_subclass) { + TEST_OBJECT_FIELD_FOR_CLASS("AllFieldsSub"); } TEST_F(JniInternalTest, NewLocalRef_nullptr) { diff --git a/test/004-JniTest/expected.txt b/test/004-JniTest/expected.txt index 1d05160883..b09b9a22eb 100644 --- a/test/004-JniTest/expected.txt +++ b/test/004-JniTest/expected.txt @@ -1,4 +1,5 @@ JNI_OnLoad called +ABC.XYZ = 12, GetStaticIntField(DEF.class, 'XYZ') = 12 Super. Super. Subclass. diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc index 4561895509..33a8f5bba2 100644 --- a/test/004-JniTest/jni_test.cc +++ b/test/004-JniTest/jni_test.cc @@ -90,6 +90,14 @@ static void testFindClassOnAttachedNativeThread(JNIEnv* env) { CHECK(!env->ExceptionCheck()); } +extern "C" JNIEXPORT jint JNICALL Java_Main_getFieldSubclass(JNIEnv* env, + jclass, + jobject f_obj, + jclass sub) { + jfieldID f = env->FromReflectedField(f_obj); + return env->GetStaticIntField(sub, f); +} + // http://b/10994325 extern "C" JNIEXPORT void JNICALL Java_Main_testFindClassOnAttachedNativeThread(JNIEnv*, jclass) { PthreadHelper(&testFindClassOnAttachedNativeThread); diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java index 871107c56b..f94dcf6c70 100644 --- a/test/004-JniTest/src/Main.java +++ b/test/004-JniTest/src/Main.java @@ -18,6 +18,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Field; import java.lang.reflect.Proxy; import java.util.regex.Pattern; @@ -32,6 +33,7 @@ public class Main { throw new RuntimeException("Slow-debug flags unexpectedly off."); } + testFieldSubclass(); testFindClassOnAttachedNativeThread(); testFindFieldOnAttachedNativeThread(); testReflectFieldGetFromAttachedNativeThreadNative(); @@ -65,6 +67,19 @@ public class Main { testDoubleLoad(args[0]); } + static class ABC { public static int XYZ = 12; } + static class DEF extends ABC {} + public static void testFieldSubclass() { + try { + System.out.println("ABC.XYZ = " + ABC.XYZ + ", GetStaticIntField(DEF.class, 'XYZ') = " + + getFieldSubclass(ABC.class.getDeclaredField("XYZ"), DEF.class)); + } catch (Exception e) { + throw new RuntimeException("Failed to test get static field on a subclass", e); + } + } + + public static native int getFieldSubclass(Field f, Class sub); + private static native boolean registerNativesJniTest(); private static native void testCallDefaultMethods(); diff --git a/test/AllFields/AllFields.java b/test/AllFields/AllFields.java index d5eac8fa2e..24f8ba1a0b 100644 --- a/test/AllFields/AllFields.java +++ b/test/AllFields/AllFields.java @@ -14,7 +14,7 @@ * limitations under the License. */ -class AllFields { +public class AllFields { static boolean sZ; static byte sB; static char sC; diff --git a/test/AllFields/AllFieldsSub.java b/test/AllFields/AllFieldsSub.java new file mode 100644 index 0000000000..d5f933f88d --- /dev/null +++ b/test/AllFields/AllFieldsSub.java @@ -0,0 +1,17 @@ +/* + * 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 AllFieldsSub extends AllFields { } diff --git a/test/AllFields/AllFieldsUnrelated.java b/test/AllFields/AllFieldsUnrelated.java new file mode 100644 index 0000000000..4db66b1886 --- /dev/null +++ b/test/AllFields/AllFieldsUnrelated.java @@ -0,0 +1,17 @@ +/* + * 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 AllFieldsUnrelated { } -- GitLab From f4886df5e72fa21eddfc4cc7860f4154929b3380 Mon Sep 17 00:00:00 2001 From: David Srbecky Date: Mon, 11 Dec 2017 16:06:29 +0000 Subject: [PATCH 185/226] Add mini-debug-info generation mode for JIT. This excludes everything that is not needed for backtraces and compresses the resulting ELF file (wrapped in another ELF file). This approximately halves the size of the debug data for JIT. The vast majority of the data is the overhead of ELF header. We could amortize this by storing more methods per ELF file. It also adds NOBITS .text section to all debug ELF files, as that seems necessary for gdb to find the symbols. On the other hand, it removes .rodata from debug ELF files. Test: Manually tested that gdb can use this data to unwind. Test: m test-art-host-gtest Test: testrunner.py --optimizing --host Test: testrunner.py -t 137-cfi Change-Id: Ic0a2dfa953cb79973a7b2ae99d32018599e61171 --- compiler/debug/elf_debug_writer.cc | 40 +++++++++++++++------- compiler/debug/elf_debug_writer.h | 7 ++-- compiler/debug/elf_gnu_debugdata_writer.h | 10 +++--- compiler/debug/elf_symtab_writer.h | 5 +-- compiler/linker/elf_builder.h | 4 +-- compiler/optimizing/optimizing_compiler.cc | 18 ++++++---- dex2oat/linker/elf_writer_quick.cc | 2 +- runtime/jit/jit.cc | 4 +++ test/137-cfi/run | 7 +++- 9 files changed, 63 insertions(+), 34 deletions(-) diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc index 33c46d7e1f..a6267292bf 100644 --- a/compiler/debug/elf_debug_writer.cc +++ b/compiler/debug/elf_debug_writer.cc @@ -108,29 +108,32 @@ void WriteDebugInfo(linker::ElfBuilder* builder, std::vector MakeMiniDebugInfo( InstructionSet isa, const InstructionSetFeatures* features, - size_t rodata_size, + uint64_t text_address, size_t text_size, const ArrayRef& method_infos) { if (Is64BitInstructionSet(isa)) { return MakeMiniDebugInfoInternal(isa, features, - rodata_size, + text_address, text_size, method_infos); } else { return MakeMiniDebugInfoInternal(isa, features, - rodata_size, + text_address, text_size, method_infos); } } template -static std::vector WriteDebugElfFileForMethodsInternal( +static std::vector MakeElfFileForJITInternal( InstructionSet isa, const InstructionSetFeatures* features, - const ArrayRef& method_infos) { + bool mini_debug_info, + const MethodDebugInfo& mi) { + CHECK_EQ(mi.is_code_address_text_relative, false); + ArrayRef method_infos(&mi, 1); std::vector buffer; buffer.reserve(KB); linker::VectorOutputStream out("Debug ELF file", &buffer); @@ -138,23 +141,34 @@ static std::vector WriteDebugElfFileForMethodsInternal( new linker::ElfBuilder(isa, features, &out)); // No program headers since the ELF file is not linked and has no allocated sections. builder->Start(false /* write_program_headers */); - WriteDebugInfo(builder.get(), - method_infos, - dwarf::DW_DEBUG_FRAME_FORMAT, - false /* write_oat_patches */); + if (mini_debug_info) { + std::vector mdi = MakeMiniDebugInfo(isa, + features, + mi.code_address, + mi.code_size, + method_infos); + builder->WriteSection(".gnu_debugdata", &mdi); + } else { + builder->GetText()->AllocateVirtualMemory(mi.code_address, mi.code_size); + WriteDebugInfo(builder.get(), + method_infos, + dwarf::DW_DEBUG_FRAME_FORMAT, + false /* write_oat_patches */); + } builder->End(); CHECK(builder->Good()); return buffer; } -std::vector WriteDebugElfFileForMethods( +std::vector MakeElfFileForJIT( InstructionSet isa, const InstructionSetFeatures* features, - const ArrayRef& method_infos) { + bool mini_debug_info, + const MethodDebugInfo& method_info) { if (Is64BitInstructionSet(isa)) { - return WriteDebugElfFileForMethodsInternal(isa, features, method_infos); + return MakeElfFileForJITInternal(isa, features, mini_debug_info, method_info); } else { - return WriteDebugElfFileForMethodsInternal(isa, features, method_infos); + return MakeElfFileForJITInternal(isa, features, mini_debug_info, method_info); } } diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h index d24ca9b203..a47bf076b9 100644 --- a/compiler/debug/elf_debug_writer.h +++ b/compiler/debug/elf_debug_writer.h @@ -43,14 +43,15 @@ void WriteDebugInfo( std::vector MakeMiniDebugInfo( InstructionSet isa, const InstructionSetFeatures* features, - size_t rodata_section_size, + uint64_t text_section_address, size_t text_section_size, const ArrayRef& method_infos); -std::vector WriteDebugElfFileForMethods( +std::vector MakeElfFileForJIT( InstructionSet isa, const InstructionSetFeatures* features, - const ArrayRef& method_infos); + bool mini_debug_info, + const MethodDebugInfo& method_info); std::vector WriteDebugElfFileForClasses( InstructionSet isa, diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h index 9b8ec35ed1..78b8e2780c 100644 --- a/compiler/debug/elf_gnu_debugdata_writer.h +++ b/compiler/debug/elf_gnu_debugdata_writer.h @@ -80,7 +80,7 @@ template static std::vector MakeMiniDebugInfoInternal( InstructionSet isa, const InstructionSetFeatures* features, - size_t rodata_section_size, + typename ElfTypes::Addr text_section_address, size_t text_section_size, const ArrayRef& method_infos) { std::vector buffer; @@ -88,11 +88,9 @@ static std::vector MakeMiniDebugInfoInternal( linker::VectorOutputStream out("Mini-debug-info ELF file", &buffer); std::unique_ptr> builder( new linker::ElfBuilder(isa, features, &out)); - builder->Start(); - // Mirror .rodata and .text as NOBITS sections. - // It is needed to detected relocations after compression. - builder->GetRoData()->AllocateVirtualMemory(rodata_section_size); - builder->GetText()->AllocateVirtualMemory(text_section_size); + builder->Start(false /* write_program_headers */); + // Mirror .text as NOBITS section since the added symbols will reference it. + builder->GetText()->AllocateVirtualMemory(text_section_address, text_section_size); WriteDebugSymbols(builder.get(), method_infos, false /* with_signature */); WriteCFISection(builder.get(), method_infos, diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h index 0907e102a0..57e010f232 100644 --- a/compiler/debug/elf_symtab_writer.h +++ b/compiler/debug/elf_symtab_writer.h @@ -79,8 +79,9 @@ static void WriteDebugSymbols(linker::ElfBuilder* builder, last_name_offset = name_offset; } - const auto* text = info.is_code_address_text_relative ? builder->GetText() : nullptr; - uint64_t address = info.code_address + (text != nullptr ? text->GetAddress() : 0); + const auto* text = builder->GetText(); + uint64_t address = info.code_address; + address += info.is_code_address_text_relative ? text->GetAddress() : 0; // Add in code delta, e.g., thumb bit 0 for Thumb2 code. address += CompiledMethod::CodeDelta(info.isa); symtab->Add(name_offset, text, address, info.code_size, STB_GLOBAL, STT_FUNC); diff --git a/compiler/linker/elf_builder.h b/compiler/linker/elf_builder.h index 3710878ea1..aa3cd98595 100644 --- a/compiler/linker/elf_builder.h +++ b/compiler/linker/elf_builder.h @@ -203,13 +203,13 @@ class ElfBuilder FINAL { if (section_index_ == 0) { std::vector& sections = owner_->sections_; Elf_Word last = sections.empty() ? PF_R : sections.back()->phdr_flags_; - if (owner_->write_program_headers_ && phdr_flags_ != last) { + if (phdr_flags_ != last) { header_.sh_addralign = kPageSize; // Page-align if R/W/X flags changed. } sections.push_back(this); section_index_ = sections.size(); // First ELF section has index 1. } - return header_.sh_addralign; + return owner_->write_program_headers_ ? header_.sh_addralign : 1; } ElfBuilder* owner_; diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 73c72fc57a..24b1a123ee 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -1224,7 +1224,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, } const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); - if (compiler_options.GetGenerateDebugInfo()) { + if (compiler_options.GenerateAnyDebugInfo()) { const auto* method_header = reinterpret_cast(code); const uintptr_t code_address = reinterpret_cast(method_header->GetCode()); debug::MethodDebugInfo info = {}; @@ -1244,10 +1244,13 @@ bool OptimizingCompiler::JitCompile(Thread* self, info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); info.code_info = nullptr; info.cfi = jni_compiled_method.GetCfi(); - std::vector elf_file = debug::WriteDebugElfFileForMethods( + // If both flags are passed, generate full debug info. + const bool mini_debug_info = !compiler_options.GetGenerateDebugInfo(); + std::vector elf_file = debug::MakeElfFileForJIT( GetCompilerDriver()->GetInstructionSet(), GetCompilerDriver()->GetInstructionSetFeatures(), - ArrayRef(&info, 1)); + mini_debug_info, + info); CreateJITCodeEntryForAddress(code_address, std::move(elf_file)); } @@ -1352,7 +1355,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, } const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); - if (compiler_options.GetGenerateDebugInfo()) { + if (compiler_options.GenerateAnyDebugInfo()) { const auto* method_header = reinterpret_cast(code); const uintptr_t code_address = reinterpret_cast(method_header->GetCode()); debug::MethodDebugInfo info = {}; @@ -1372,10 +1375,13 @@ bool OptimizingCompiler::JitCompile(Thread* self, 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()); - std::vector elf_file = debug::WriteDebugElfFileForMethods( + // If both flags are passed, generate full debug info. + const bool mini_debug_info = !compiler_options.GetGenerateDebugInfo(); + std::vector elf_file = debug::MakeElfFileForJIT( GetCompilerDriver()->GetInstructionSet(), GetCompilerDriver()->GetInstructionSetFeatures(), - ArrayRef(&info, 1)); + mini_debug_info, + info); CreateJITCodeEntryForAddress(code_address, std::move(elf_file)); } diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc index 758e21de93..aa64b7d59e 100644 --- a/dex2oat/linker/elf_writer_quick.cc +++ b/dex2oat/linker/elf_writer_quick.cc @@ -67,7 +67,7 @@ class DebugInfoTask : public Task { void Run(Thread*) { result_ = debug::MakeMiniDebugInfo(isa_, instruction_set_features_, - rodata_section_size_, + kPageSize + rodata_section_size_, // .text address. text_section_size_, method_infos_); } diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 783d05df34..278bc57412 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -648,6 +648,10 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ // We do not want to compile such methods. return; } + if (hot_method_threshold_ == 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_); diff --git a/test/137-cfi/run b/test/137-cfi/run index ebc729bc74..adea71a07f 100755 --- a/test/137-cfi/run +++ b/test/137-cfi/run @@ -16,10 +16,15 @@ # Test with full DWARF debugging information. # Check full signatures of methods. +# The option jitthreshold:0 ensures that if we run the test in JIT mode, +# there will be JITed frames on the callstack (it synchronously JITs on first use). ${RUN} "$@" -Xcompiler-option --generate-debug-info \ + --runtime-option -Xjitthreshold:0 \ --args --full-signatures --args --test-local --args --test-remote # Test with minimal compressed debugging information. # Check only method names (parameters are omitted to save space). # Check only remote unwinding since decompression is disabled in local unwinds (b/27391690). -${RUN} "$@" -Xcompiler-option --generate-mini-debug-info --args --test-remote +${RUN} "$@" -Xcompiler-option --generate-mini-debug-info \ + --runtime-option -Xjitthreshold:0 \ + --args --test-remote -- GitLab From 04366f382239f4bcf1f9c67bb1ff6975607cd8e4 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Thu, 14 Dec 2017 15:15:19 +0000 Subject: [PATCH 186/226] Revert "ART: Try to statically evaluate some conditions." CL has an unwanted 10-15% compile-time impact. This reverts commit 1de1e11ac90db9fad8916ac43d43714ccb8d978f. Change-Id: I76b45aa95bbd24dd025d2ee6cf37d77fe17b8497 --- compiler/optimizing/instruction_simplifier.cc | 21 --- compiler/optimizing/nodes.cc | 14 +- compiler/optimizing/nodes.h | 16 +- .../src/Main.java | 138 ------------------ 4 files changed, 7 insertions(+), 182 deletions(-) diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 4c98d8323d..4c18e16c48 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -994,27 +994,6 @@ void InstructionSimplifierVisitor::VisitIf(HIf* instruction) { instruction->GetBlock()->SwapSuccessors(); RecordSimplification(); } - HInstruction* input = 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 (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 (!input->IsConstant()) { - HBasicBlock* true_succ = instruction->IfTrueSuccessor(); - HBasicBlock* false_succ = instruction->IfFalseSuccessor(); - - DCHECK_EQ(true_succ->GetPredecessors().size(), 1u); - input->ReplaceUsesDominatedBy( - true_succ->GetFirstInstruction(), GetGraph()->GetIntConstant(1), /* strictly */ false); - RecordSimplification(); - - DCHECK_EQ(false_succ->GetPredecessors().size(), 1u); - input->ReplaceUsesDominatedBy( - false_succ->GetFirstInstruction(), GetGraph()->GetIntConstant(0), /* strictly */ false); - RecordSimplification(); - } } void InstructionSimplifierVisitor::VisitArrayLength(HArrayLength* instruction) { diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index dda0243d3d..fa580d9bed 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1110,10 +1110,10 @@ bool HInstructionList::FoundBefore(const HInstruction* instruction1, return true; } -bool HInstruction::Dominates(HInstruction* other_instruction, bool strictly) const { +bool HInstruction::StrictlyDominates(HInstruction* other_instruction) const { if (other_instruction == this) { // An instruction does not strictly dominate itself. - return !strictly; + return false; } HBasicBlock* block = GetBlock(); HBasicBlock* other_block = other_instruction->GetBlock(); @@ -1147,10 +1147,6 @@ bool HInstruction::Dominates(HInstruction* other_instruction, bool strictly) con } } -bool HInstruction::StrictlyDominates(HInstruction* other_instruction) const { - return Dominates(other_instruction, /* strictly */ true); -} - void HInstruction::RemoveEnvironment() { RemoveEnvironmentUses(this); environment_ = nullptr; @@ -1173,16 +1169,14 @@ void HInstruction::ReplaceWith(HInstruction* other) { DCHECK(env_uses_.empty()); } -void HInstruction::ReplaceUsesDominatedBy(HInstruction* dominator, - HInstruction* replacement, - bool strictly) { +void HInstruction::ReplaceUsesDominatedBy(HInstruction* dominator, HInstruction* replacement) { const HUseList& uses = 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 (dominator->Dominates(user, strictly)) { + if (dominator->StrictlyDominates(user)) { user->ReplaceInput(replacement, index); } } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 07ab325a0e..66d5bfea32 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -2098,13 +2098,9 @@ class HInstruction : public ArenaObject { return IsRemovable() && !HasUses(); } - // Does this instruction dominate (strictly or in regular sense depending on 'strictly') - // `other_instruction`? - // Returns '!strictly' if this instruction and `other_instruction` are the same. + // Does this instruction strictly dominate `other_instruction`? + // Returns false if this instruction and `other_instruction` are the same. // Aborts if this instruction and `other_instruction` are both phis. - bool Dominates(HInstruction* other_instruction, bool strictly) const; - - // Return 'Dominates(other_instruction, /*strictly*/ true)'. bool StrictlyDominates(HInstruction* other_instruction) const; int GetId() const { return id_; } @@ -2165,13 +2161,7 @@ class HInstruction : public ArenaObject { void SetLocations(LocationSummary* locations) { locations_ = locations; } void ReplaceWith(HInstruction* instruction); - - // Replace all uses of the instruction which are dominated by 'dominator' with 'replacement'. - // 'strictly' determines whether strict or regular domination relation should be checked. - void ReplaceUsesDominatedBy(HInstruction* dominator, - HInstruction* replacement, - bool strictly = true); - + void ReplaceUsesDominatedBy(HInstruction* dominator, HInstruction* replacement); void ReplaceInput(HInstruction* replacement, size_t index); // This is almost the same as doing `ReplaceWith()`. But in this helper, the diff --git a/test/458-checker-instruct-simplification/src/Main.java b/test/458-checker-instruct-simplification/src/Main.java index ccaba61722..7797f31867 100644 --- a/test/458-checker-instruct-simplification/src/Main.java +++ b/test/458-checker-instruct-simplification/src/Main.java @@ -2602,124 +2602,6 @@ public class Main { return (byte)((int)(((long)(b & 0xff)) & 255L)); } - /// CHECK-START: void Main.$noinline$testIfCondStaticEvaluation(int[], boolean) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> Equal [<>,<>] - /// CHECK-DAG: If [<>] - /// CHECK-DAG: <> Equal [<>,<>] - /// CHECK-DAG: If [<>] - /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] - /// CHECK-DAG: <> Equal [<>,<>] - /// CHECK-DAG: If [<>] - /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] - // - /// CHECK-NOT: If - /// CHECK-NOT: ArraySet - - /// CHECK-START: void Main.$noinline$testIfCondStaticEvaluation(int[], boolean) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: If [<>] - /// CHECK-DAG: <> Equal [<>,<>] - /// CHECK-DAG: If [<>] - /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] - /// CHECK-DAG: <> Equal [<>,<>] - /// CHECK-DAG: If [<>] - /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] - // - /// CHECK-NOT: If - /// CHECK-NOT: ArraySet - - /// CHECK-START: void Main.$noinline$testIfCondStaticEvaluation(int[], boolean) dead_code_elimination$after_inlining (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: If [<>] - /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] - // - /// CHECK-NOT: IntConstant 0 - /// CHECK-NOT: If [<>] - /// CHECK-NOT: ArraySet - private static void $noinline$testIfCondStaticEvaluation(int[] a, boolean f) { - if (f) { - if (f) { - a[0] = 1; - } - } else { - if (f) { - a[0] = 0; - } - } - } - - /// CHECK-START: void Main.$noinline$testManualUnrollWithInvarExits(int[], boolean) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue loop:none - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 1 loop:none - /// CHECK-DAG: <> Equal [<>,<>] loop:none - /// CHECK-DAG: If [<>] loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] 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: <> NotEqual [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: If [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-NOT: If - /// CHECK-NOT: ArraySet - - /// CHECK-START: void Main.$noinline$testManualUnrollWithInvarExits(int[], boolean) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue loop:none - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> IntConstant 1 loop:none - /// CHECK-DAG: If [<>] loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] 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: <> NotEqual [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: If [<>] loop:<> outer_loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-NOT: If - /// CHECK-NOT: ArraySet - - /// CHECK-START: void Main.$noinline$testManualUnrollWithInvarExits(int[], boolean) dead_code_elimination$after_inlining (after) - /// CHECK-DAG: <> ParameterValue loop:none - /// CHECK-DAG: <> IntConstant 1 loop:none - /// CHECK-DAG: If [<>] loop:none - /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] 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: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-NOT: If - /// CHECK-NOT: ArraySet - private static void $noinline$testManualUnrollWithInvarExits(int[] a, boolean f) { - if (f) { - return; - } - a[0] = 1; - for (int i = 1; i < a.length; i++) { - if (f) { - return; - } - a[i] = 1; - } - } - - public static final int LENGTH = 1024; - - private static final void initArray(int[] a) { - for (int i = 0; i < a.length; i++) { - a[i] = 0; - } - } - public static void main(String[] args) { int arg = 123456; float floatArg = 123456.125f; @@ -2963,26 +2845,6 @@ public class Main { assertIntEquals(1, $noinline$bug68142795Boolean(true)); assertIntEquals(0x7f, $noinline$bug68142795Elaborate((byte) 0x7f)); assertIntEquals((byte) 0x80, $noinline$bug68142795Elaborate((byte) 0x80)); - - int[] array = new int[LENGTH]; - - array[0] = 0; - $noinline$testIfCondStaticEvaluation(array, true); - assertIntEquals(array[0], 1); - array[0] = 0; - $noinline$testIfCondStaticEvaluation(array, false); - assertIntEquals(array[0], 0); - - initArray(array); - $noinline$testManualUnrollWithInvarExits(array, false); - for (int i = 0; i < array.length; i++) { - assertIntEquals(array[i], 1); - } - initArray(array); - $noinline$testManualUnrollWithInvarExits(array, true); - for (int i = 0; i < array.length; i++) { - assertIntEquals(array[i], 0); - } } private static boolean $inline$true() { return true; } -- GitLab From 8758454d380a2b0de1f4a99e9623cfac5460ccdf Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 12 Dec 2017 17:47:52 +0000 Subject: [PATCH 187/226] Clean up InstanceOf/CheckCast. Avoid read barriers for boot image class InstanceOf. Boot image classes are non-moveable, so comparing them against from-space and to-space reference yields the same result. Change the notion of a "fatal" type check slow path to mean that the runtime call shall not return by normal path, i.e. "fatal" now includes certainly throwing in a try-block. This avoids unnecessary code to restore registers and jump back. For boot image classes the CheckCast comparisons do not need read barriers (for the same reason as for InstanceOf), so we shall not have any false negatives and can treat the check's slow paths as final in the same cases as in non-CC configs. Boot image size for aosp_taimen-userdebug in AOSP master: - before: arm boot*.oat: 37075460 arm64 boot*.oat: 43431768 - after: arm boot*.oat: 36894292 (-177KiB, -0.5%) arm64 boot*.oat: 43201256 (-225KiB, -0.5%) Also remove some obsolete helpers from CodeGenerator. Test: Additional test in 603-checker-instanceof. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Test: Pixel 2 XL boots. Test: testrunner.py --target --optimizing Bug: 12687968 Change-Id: Ib1381084e46a10e70320dcc618f0502ad725f0b8 --- compiler/optimizing/code_generator.cc | 9 --- compiler/optimizing/code_generator.h | 51 ++++++++++++-- compiler/optimizing/code_generator_arm64.cc | 66 +++++++------------ .../optimizing/code_generator_arm_vixl.cc | 66 +++++++------------ compiler/optimizing/code_generator_x86.cc | 63 +++++++----------- compiler/optimizing/code_generator_x86_64.cc | 62 +++++++---------- compiler/optimizing/nodes.h | 20 ++++-- test/603-checker-instanceof/src/Main.java | 40 ++++++++++- 8 files changed, 189 insertions(+), 188 deletions(-) diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 45eec6d744..dee74e96dc 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -295,15 +295,6 @@ void CodeGenerator::EmitJitRootPatches(uint8_t* code ATTRIBUTE_UNUSED, DCHECK_EQ(code_generation_data_->GetNumberOfJitClassRoots(), 0u); } -size_t CodeGenerator::GetCacheOffset(uint32_t index) { - return sizeof(GcRoot) * index; -} - -size_t CodeGenerator::GetCachePointerOffset(uint32_t index) { - PointerSize pointer_size = InstructionSetPointerSize(GetInstructionSet()); - return static_cast(pointer_size) * index; -} - uint32_t CodeGenerator::GetArrayLengthOffset(HArrayLength* array_length) { return array_length->IsStringLength() ? mirror::String::CountOffset().Uint32Value() diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 08e4462356..3c5a37f958 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -388,13 +388,6 @@ class CodeGenerator : public DeletableArenaObject { bool IsBlockedCoreRegister(size_t i) { return blocked_core_registers_[i]; } bool IsBlockedFloatingPointRegister(size_t i) { return blocked_fpu_registers_[i]; } - // Helper that returns the pointer offset of an index in an object array. - // Note: this method assumes we always have the same pointer size, regardless - // of the architecture. - static size_t GetCacheOffset(uint32_t index); - // Pointer variant for ArtMethod and ArtField arrays. - size_t GetCachePointerOffset(uint32_t index); - // Helper that returns the offset of the array's length field. // Note: Besides the normal arrays, we also use the HArrayLength for // accessing the String's `count` field in String intrinsics. @@ -412,6 +405,50 @@ class CodeGenerator : public DeletableArenaObject { Location to2, DataType::Type type2); + static bool InstanceOfNeedsReadBarrier(HInstanceOf* instance_of) { + // Used only for kExactCheck, kAbstractClassCheck, kClassHierarchyCheck and kArrayObjectCheck. + DCHECK(instance_of->GetTypeCheckKind() == TypeCheckKind::kExactCheck || + instance_of->GetTypeCheckKind() == TypeCheckKind::kAbstractClassCheck || + instance_of->GetTypeCheckKind() == TypeCheckKind::kClassHierarchyCheck || + instance_of->GetTypeCheckKind() == TypeCheckKind::kArrayObjectCheck) + << instance_of->GetTypeCheckKind(); + // If the target class is in the boot image, it's non-moveable and it doesn't matter + // if we compare it with a from-space or to-space reference, the result is the same. + // It's OK to traverse a class hierarchy jumping between from-space and to-space. + return kEmitCompilerReadBarrier && !instance_of->GetTargetClass()->IsInBootImage(); + } + + static ReadBarrierOption ReadBarrierOptionForInstanceOf(HInstanceOf* instance_of) { + return InstanceOfNeedsReadBarrier(instance_of) ? kWithReadBarrier : kWithoutReadBarrier; + } + + static bool IsTypeCheckSlowPathFatal(HCheckCast* check_cast) { + switch (check_cast->GetTypeCheckKind()) { + case TypeCheckKind::kExactCheck: + case TypeCheckKind::kAbstractClassCheck: + case TypeCheckKind::kClassHierarchyCheck: + case TypeCheckKind::kArrayObjectCheck: + case TypeCheckKind::kInterfaceCheck: { + bool needs_read_barrier = + kEmitCompilerReadBarrier && !check_cast->GetTargetClass()->IsInBootImage(); + // We do not emit read barriers for HCheckCast, so we can get false negatives + // and the slow path shall re-check and simply return if the cast is actually OK. + return !needs_read_barrier; + } + case TypeCheckKind::kArrayCheck: + case TypeCheckKind::kUnresolvedCheck: + return false; + } + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); + } + + static LocationSummary::CallKind GetCheckCastCallKind(HCheckCast* check_cast) { + return (IsTypeCheckSlowPathFatal(check_cast) && !check_cast->CanThrowIntoCatchBlock()) + ? LocationSummary::kNoCall // In fact, call on a fatal (non-returning) slow path. + : LocationSummary::kCallOnSlowPath; + } + static bool StoreNeedsWriteBarrier(DataType::Type type, HInstruction* value) { // Check that null value is not represented as an integer constant. DCHECK(type != DataType::Type::kReference || !value->IsIntConstant()); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 5054a299d3..f9dcb5d6ef 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -478,7 +478,7 @@ class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 { __ Bind(GetEntryLabel()); - if (!is_fatal_) { + if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) { SaveLiveRegisters(codegen, locations); } @@ -3822,11 +3822,12 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kExactCheck: case TypeCheckKind::kAbstractClassCheck: case TypeCheckKind::kClassHierarchyCheck: - case TypeCheckKind::kArrayObjectCheck: - call_kind = - kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; - baker_read_barrier_slow_path = kUseBakerReadBarrier; + case TypeCheckKind::kArrayObjectCheck: { + bool needs_read_barrier = CodeGenerator::InstanceOfNeedsReadBarrier(instruction); + call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; + baker_read_barrier_slow_path = kUseBakerReadBarrier && needs_read_barrier; break; + } case TypeCheckKind::kArrayCheck: case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: @@ -3875,13 +3876,15 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { switch (type_check_kind) { case TypeCheckKind::kExactCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); __ Cmp(out, cls); __ Cset(out, eq); if (zero.IsLinked()) { @@ -3891,13 +3894,15 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kAbstractClassCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // If the class is abstract, we eagerly fetch the super class of the // object to avoid doing a comparison we know will fail. vixl::aarch64::Label loop, success; @@ -3907,7 +3912,7 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { out_loc, super_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // If `out` is null, we use it for the result, and jump to `done`. __ Cbz(out, &done); __ Cmp(out, cls); @@ -3920,13 +3925,15 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kClassHierarchyCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // Walk over the class hierarchy to find a match. vixl::aarch64::Label loop, success; __ Bind(&loop); @@ -3937,7 +3944,7 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { out_loc, super_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); __ Cbnz(out, &loop); // If `out` is null, we use it for the result, and jump to `done`. __ B(&done); @@ -3950,13 +3957,15 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kArrayObjectCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // Do an exact check. vixl::aarch64::Label exact_check; __ Cmp(out, cls); @@ -3967,7 +3976,7 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { out_loc, component_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // If `out` is null, we use it for the result, and jump to `done`. __ Cbz(out, &done); __ Ldrh(out, HeapOperand(out, primitive_offset)); @@ -4048,26 +4057,8 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { } void LocationsBuilderARM64::VisitCheckCast(HCheckCast* instruction) { - LocationSummary::CallKind call_kind = LocationSummary::kNoCall; - bool throws_into_catch = instruction->CanThrowIntoCatchBlock(); - TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); - switch (type_check_kind) { - case TypeCheckKind::kExactCheck: - case TypeCheckKind::kAbstractClassCheck: - case TypeCheckKind::kClassHierarchyCheck: - case TypeCheckKind::kArrayObjectCheck: - call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ? - LocationSummary::kCallOnSlowPath : - LocationSummary::kNoCall; // In fact, call on a fatal (non-returning) slow path. - break; - case TypeCheckKind::kArrayCheck: - case TypeCheckKind::kUnresolvedCheck: - case TypeCheckKind::kInterfaceCheck: - call_kind = LocationSummary::kCallOnSlowPath; - break; - } - + LocationSummary::CallKind call_kind = CodeGenerator::GetCheckCastCallKind(instruction); LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); @@ -4098,18 +4089,7 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { const uint32_t object_array_data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value(); - bool is_type_check_slow_path_fatal = false; - // Always false for read barriers since we may need to go to the entrypoint for non-fatal cases - // from false negatives. The false negatives may come from avoiding read barriers below. Avoiding - // read barriers is done for performance and code size reasons. - if (!kEmitCompilerReadBarrier) { - is_type_check_slow_path_fatal = - (type_check_kind == TypeCheckKind::kExactCheck || - type_check_kind == TypeCheckKind::kAbstractClassCheck || - type_check_kind == TypeCheckKind::kClassHierarchyCheck || - type_check_kind == TypeCheckKind::kArrayObjectCheck) && - !instruction->CanThrowIntoCatchBlock(); - } + bool is_type_check_slow_path_fatal = CodeGenerator::IsTypeCheckSlowPathFatal(instruction); SlowPathCodeARM64* type_check_slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARM64( instruction, is_type_check_slow_path_fatal); diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 3f8f0c44f3..017598d484 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -619,7 +619,7 @@ class TypeCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL { CodeGeneratorARMVIXL* arm_codegen = down_cast(codegen); __ Bind(GetEntryLabel()); - if (!is_fatal_) { + if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) { SaveLiveRegisters(codegen, locations); } @@ -7377,11 +7377,12 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kExactCheck: case TypeCheckKind::kAbstractClassCheck: case TypeCheckKind::kClassHierarchyCheck: - case TypeCheckKind::kArrayObjectCheck: - call_kind = - kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; - baker_read_barrier_slow_path = kUseBakerReadBarrier; + case TypeCheckKind::kArrayObjectCheck: { + bool needs_read_barrier = CodeGenerator::InstanceOfNeedsReadBarrier(instruction); + call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; + baker_read_barrier_slow_path = kUseBakerReadBarrier && needs_read_barrier; break; + } case TypeCheckKind::kArrayCheck: case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: @@ -7434,13 +7435,15 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) switch (type_check_kind) { case TypeCheckKind::kExactCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // Classes must be equal for the instanceof to succeed. __ Cmp(out, cls); // We speculatively set the result to false without changing the condition @@ -7467,13 +7470,15 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } case TypeCheckKind::kAbstractClassCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // If the class is abstract, we eagerly fetch the super class of the // object to avoid doing a comparison we know will fail. vixl32::Label loop; @@ -7483,7 +7488,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) out_loc, super_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // If `out` is null, we use it for the result, and jump to the final label. __ CompareAndBranchIfZero(out, final_label, /* far_target */ false); __ Cmp(out, cls); @@ -7493,13 +7498,15 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } case TypeCheckKind::kClassHierarchyCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // Walk over the class hierarchy to find a match. vixl32::Label loop, success; __ Bind(&loop); @@ -7510,7 +7517,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) out_loc, super_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // This is essentially a null check, but it sets the condition flags to the // proper value for the code that follows the loop, i.e. not `eq`. __ Cmp(out, 1); @@ -7547,13 +7554,15 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } case TypeCheckKind::kArrayObjectCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // Do an exact check. vixl32::Label exact_check; __ Cmp(out, cls); @@ -7564,7 +7573,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) out_loc, component_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // If `out` is null, we use it for the result, and jump to the final label. __ CompareAndBranchIfZero(out, final_label, /* far_target */ false); GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset); @@ -7654,26 +7663,8 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) { - LocationSummary::CallKind call_kind = LocationSummary::kNoCall; - bool throws_into_catch = instruction->CanThrowIntoCatchBlock(); - TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); - switch (type_check_kind) { - case TypeCheckKind::kExactCheck: - case TypeCheckKind::kAbstractClassCheck: - case TypeCheckKind::kClassHierarchyCheck: - case TypeCheckKind::kArrayObjectCheck: - call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ? - LocationSummary::kCallOnSlowPath : - LocationSummary::kNoCall; // In fact, call on a fatal (non-returning) slow path. - break; - case TypeCheckKind::kArrayCheck: - case TypeCheckKind::kUnresolvedCheck: - case TypeCheckKind::kInterfaceCheck: - call_kind = LocationSummary::kCallOnSlowPath; - break; - } - + LocationSummary::CallKind call_kind = CodeGenerator::GetCheckCastCallKind(instruction); LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); @@ -7702,18 +7693,7 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { const uint32_t object_array_data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value(); - // Always false for read barriers since we may need to go to the entrypoint for non-fatal cases - // from false negatives. The false negatives may come from avoiding read barriers below. Avoiding - // read barriers is done for performance and code size reasons. - bool is_type_check_slow_path_fatal = false; - if (!kEmitCompilerReadBarrier) { - is_type_check_slow_path_fatal = - (type_check_kind == TypeCheckKind::kExactCheck || - type_check_kind == TypeCheckKind::kAbstractClassCheck || - type_check_kind == TypeCheckKind::kClassHierarchyCheck || - type_check_kind == TypeCheckKind::kArrayObjectCheck) && - !instruction->CanThrowIntoCatchBlock(); - } + bool is_type_check_slow_path_fatal = CodeGenerator::IsTypeCheckSlowPathFatal(instruction); SlowPathCodeARMVIXL* type_check_slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL( instruction, is_type_check_slow_path_fatal); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 952b00fce9..9a7495b0ef 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -324,7 +324,7 @@ class TypeCheckSlowPathX86 : public SlowPathCode { __ UnpoisonHeapReference(locations->InAt(1).AsRegister()); } - if (!is_fatal_) { + if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) { SaveLiveRegisters(codegen, locations); } @@ -6390,11 +6390,12 @@ void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kExactCheck: case TypeCheckKind::kAbstractClassCheck: case TypeCheckKind::kClassHierarchyCheck: - case TypeCheckKind::kArrayObjectCheck: - call_kind = - kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; - baker_read_barrier_slow_path = kUseBakerReadBarrier; + case TypeCheckKind::kArrayObjectCheck: { + bool needs_read_barrier = CodeGenerator::InstanceOfNeedsReadBarrier(instruction); + call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; + baker_read_barrier_slow_path = kUseBakerReadBarrier && needs_read_barrier; break; + } case TypeCheckKind::kArrayCheck: case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: @@ -6442,12 +6443,14 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { switch (type_check_kind) { case TypeCheckKind::kExactCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, - kCompilerReadBarrierOption); + read_barrier_option); if (cls.IsRegister()) { __ cmpl(out, cls.AsRegister()); } else { @@ -6463,12 +6466,14 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kAbstractClassCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, - kCompilerReadBarrierOption); + read_barrier_option); // If the class is abstract, we eagerly fetch the super class of the // object to avoid doing a comparison we know will fail. NearLabel loop; @@ -6478,7 +6483,7 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { out_loc, super_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); __ testl(out, out); // If `out` is null, we use it for the result, and jump to `done`. __ j(kEqual, &done); @@ -6497,12 +6502,14 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kClassHierarchyCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, - kCompilerReadBarrierOption); + read_barrier_option); // Walk over the class hierarchy to find a match. NearLabel loop, success; __ Bind(&loop); @@ -6518,7 +6525,7 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { out_loc, super_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); __ testl(out, out); __ j(kNotEqual, &loop); // If `out` is null, we use it for the result, and jump to `done`. @@ -6532,12 +6539,14 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kArrayObjectCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, - kCompilerReadBarrierOption); + read_barrier_option); // Do an exact check. NearLabel exact_check; if (cls.IsRegister()) { @@ -6553,7 +6562,7 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { out_loc, component_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); __ testl(out, out); // If `out` is null, we use it for the result, and jump to `done`. __ j(kEqual, &done); @@ -6637,30 +6646,9 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { } } -static bool IsTypeCheckSlowPathFatal(TypeCheckKind type_check_kind, bool throws_into_catch) { - switch (type_check_kind) { - case TypeCheckKind::kExactCheck: - case TypeCheckKind::kAbstractClassCheck: - case TypeCheckKind::kClassHierarchyCheck: - case TypeCheckKind::kArrayObjectCheck: - return !throws_into_catch && !kEmitCompilerReadBarrier; - case TypeCheckKind::kInterfaceCheck: - return !throws_into_catch && !kEmitCompilerReadBarrier; - case TypeCheckKind::kArrayCheck: - case TypeCheckKind::kUnresolvedCheck: - return false; - } - LOG(FATAL) << "Unreachable"; - UNREACHABLE(); -} - void LocationsBuilderX86::VisitCheckCast(HCheckCast* instruction) { - bool throws_into_catch = instruction->CanThrowIntoCatchBlock(); TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); - LocationSummary::CallKind call_kind = - IsTypeCheckSlowPathFatal(type_check_kind, throws_into_catch) - ? LocationSummary::kNoCall - : LocationSummary::kCallOnSlowPath; + LocationSummary::CallKind call_kind = CodeGenerator::GetCheckCastCallKind(instruction); LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); @@ -6698,12 +6686,7 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { const uint32_t object_array_data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value(); - // Always false for read barriers since we may need to go to the entrypoint for non-fatal cases - // from false negatives. The false negatives may come from avoiding read barriers below. Avoiding - // read barriers is done for performance and code size reasons. - bool is_type_check_slow_path_fatal = - IsTypeCheckSlowPathFatal(type_check_kind, instruction->CanThrowIntoCatchBlock()); - + bool is_type_check_slow_path_fatal = CodeGenerator::IsTypeCheckSlowPathFatal(instruction); SlowPathCode* type_check_slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathX86( instruction, is_type_check_slow_path_fatal); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 106531cd27..70ce522417 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -344,7 +344,7 @@ class TypeCheckSlowPathX86_64 : public SlowPathCode { __ UnpoisonHeapReference(locations->InAt(1).AsRegister()); } - if (!is_fatal_) { + if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) { SaveLiveRegisters(codegen, locations); } @@ -5763,11 +5763,12 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kExactCheck: case TypeCheckKind::kAbstractClassCheck: case TypeCheckKind::kClassHierarchyCheck: - case TypeCheckKind::kArrayObjectCheck: - call_kind = - kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; - baker_read_barrier_slow_path = kUseBakerReadBarrier; + case TypeCheckKind::kArrayObjectCheck: { + bool needs_read_barrier = CodeGenerator::InstanceOfNeedsReadBarrier(instruction); + call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; + baker_read_barrier_slow_path = kUseBakerReadBarrier && needs_read_barrier; break; + } case TypeCheckKind::kArrayCheck: case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: @@ -5818,12 +5819,14 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { switch (type_check_kind) { case TypeCheckKind::kExactCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, - kCompilerReadBarrierOption); + read_barrier_option); if (cls.IsRegister()) { __ cmpl(out, cls.AsRegister()); } else { @@ -5844,12 +5847,14 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kAbstractClassCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, - kCompilerReadBarrierOption); + read_barrier_option); // If the class is abstract, we eagerly fetch the super class of the // object to avoid doing a comparison we know will fail. NearLabel loop, success; @@ -5859,7 +5864,7 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { out_loc, super_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); __ testl(out, out); // If `out` is null, we use it for the result, and jump to `done`. __ j(kEqual, &done); @@ -5878,12 +5883,14 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kClassHierarchyCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, - kCompilerReadBarrierOption); + read_barrier_option); // Walk over the class hierarchy to find a match. NearLabel loop, success; __ Bind(&loop); @@ -5899,7 +5906,7 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { out_loc, super_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); __ testl(out, out); __ j(kNotEqual, &loop); // If `out` is null, we use it for the result, and jump to `done`. @@ -5913,12 +5920,14 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kArrayObjectCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, - kCompilerReadBarrierOption); + read_barrier_option); // Do an exact check. NearLabel exact_check; if (cls.IsRegister()) { @@ -5934,7 +5943,7 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { out_loc, component_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); __ testl(out, out); // If `out` is null, we use it for the result, and jump to `done`. __ j(kEqual, &done); @@ -6018,30 +6027,9 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } } -static bool IsTypeCheckSlowPathFatal(TypeCheckKind type_check_kind, bool throws_into_catch) { - switch (type_check_kind) { - case TypeCheckKind::kExactCheck: - case TypeCheckKind::kAbstractClassCheck: - case TypeCheckKind::kClassHierarchyCheck: - case TypeCheckKind::kArrayObjectCheck: - return !throws_into_catch && !kEmitCompilerReadBarrier; - case TypeCheckKind::kInterfaceCheck: - return !throws_into_catch && !kEmitCompilerReadBarrier; - case TypeCheckKind::kArrayCheck: - case TypeCheckKind::kUnresolvedCheck: - return false; - } - LOG(FATAL) << "Unreachable"; - UNREACHABLE(); -} - void LocationsBuilderX86_64::VisitCheckCast(HCheckCast* instruction) { - bool throws_into_catch = instruction->CanThrowIntoCatchBlock(); TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); - bool is_fatal_slow_path = IsTypeCheckSlowPathFatal(type_check_kind, throws_into_catch); - LocationSummary::CallKind call_kind = is_fatal_slow_path - ? LocationSummary::kNoCall - : LocationSummary::kCallOnSlowPath; + LocationSummary::CallKind call_kind = CodeGenerator::GetCheckCastCallKind(instruction); LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); @@ -6082,11 +6070,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { const uint32_t object_array_data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value(); - // Always false for read barriers since we may need to go to the entrypoint for non-fatal cases - // from false negatives. The false negatives may come from avoiding read barriers below. Avoiding - // read barriers is done for performance and code size reasons. - bool is_type_check_slow_path_fatal = - IsTypeCheckSlowPathFatal(type_check_kind, instruction->CanThrowIntoCatchBlock()); + bool is_type_check_slow_path_fatal = CodeGenerator::IsTypeCheckSlowPathFatal(instruction); SlowPathCode* type_check_slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathX86_64( instruction, is_type_check_slow_path_fatal); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index affd54ed72..6527cd3b4c 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -6600,7 +6600,7 @@ std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs); class HInstanceOf FINAL : public HExpression<2> { public: HInstanceOf(HInstruction* object, - HLoadClass* constant, + HLoadClass* target_class, TypeCheckKind check_kind, uint32_t dex_pc) : HExpression(DataType::Type::kBool, @@ -6609,7 +6609,13 @@ class HInstanceOf FINAL : public HExpression<2> { SetPackedField(check_kind); SetPackedFlag(true); SetRawInputAt(0, object); - SetRawInputAt(1, constant); + 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; } @@ -6703,14 +6709,20 @@ class HBoundType FINAL : public HExpression<1> { class HCheckCast FINAL : public HTemplateInstruction<2> { public: HCheckCast(HInstruction* object, - HLoadClass* constant, + HLoadClass* target_class, TypeCheckKind check_kind, uint32_t dex_pc) : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) { SetPackedField(check_kind); SetPackedFlag(true); SetRawInputAt(0, object); - SetRawInputAt(1, constant); + 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; } diff --git a/test/603-checker-instanceof/src/Main.java b/test/603-checker-instanceof/src/Main.java index ddf4b92fba..1487969c03 100644 --- a/test/603-checker-instanceof/src/Main.java +++ b/test/603-checker-instanceof/src/Main.java @@ -22,12 +22,17 @@ class ChildClass extends SuperClass { public class Main { - /// CHECK-START: void Main.main(java.lang.String[]) builder (after) + public static void main(String[] args) { + test1(); + test2(); + } + + /// CHECK-START: void Main.test1() builder (after) /// CHECK: BoundType klass:SuperClass can_be_null:false exact:false - /// CHECK-START: void Main.main(java.lang.String[]) builder (after) + /// CHECK-START: void Main.test1() builder (after) /// CHECK-NOT: BoundType klass:SuperClass can_be_null:false exact:true - public static void main(String[] args) { + public static void test1() { Object obj = new ChildClass(); // We need a fixed point iteration to hit the bogus type update @@ -45,4 +50,33 @@ public class Main { } } } + + /// CHECK-START-X86: boolean Main.$noinline$instanceOfString(java.lang.Object) disassembly (after) + /// CHECK: InstanceOf check_kind:exact_check + /// CHECK-NOT: {{.*fs:.*}} + + /// CHECK-START-X86_64: boolean Main.$noinline$instanceOfString(java.lang.Object) disassembly (after) + /// CHECK: InstanceOf check_kind:exact_check + /// CHECK-NOT: {{.*gs:.*}} + + /// CHECK-START-{ARM,ARM64}: boolean Main.$noinline$instanceOfString(java.lang.Object) disassembly (after) + /// CHECK: InstanceOf check_kind:exact_check + // For ARM and ARM64, the marking register (r8 and x20, respectively) can be used in + // non-CC configs for any other purpose, so we'd need a config-specific checker test. + // TODO: Add the checks when we support config-specific tests. + public static boolean $noinline$instanceOfString(Object o) { + // String is a final class, so `instanceof String` should use exact check. + // String is in the boot image, so we should avoid read barriers. The presence + // of the read barrier can be checked in the architecture-specific disassembly. + return o instanceof String; + } + + public static void test2() { + if ($noinline$instanceOfString(new Object())) { + throw new Error(); + } + if (!$noinline$instanceOfString(new String())) { + throw new Error(); + } + } } -- GitLab From f3ebccef2fd375e8b06fc0f93ba8395a577a37bf Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 11 Dec 2017 20:40:23 -0800 Subject: [PATCH 188/226] ART: Add Monitor::FetchState Add a function returning descriptive state for a monitor. The function will return the thread state, and possible the object the thread is blocked/waiting/sleeping on, as well as the lock owner's tid. Rewrite DescribeWait to use this. In preparation for new uses. Bug: 70538431 Test: m Change-Id: I79d0104ad04dec633522d4127542184a0cbdf95c --- runtime/monitor.cc | 143 ++++++++++++++++++++++++++++++--------------- runtime/monitor.h | 5 ++ 2 files changed, 102 insertions(+), 46 deletions(-) diff --git a/runtime/monitor.cc b/runtime/monitor.cc index 12529058e2..ba18f8ddd4 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -1291,60 +1291,111 @@ uint32_t Monitor::GetLockOwnerThreadId(mirror::Object* obj) { void Monitor::DescribeWait(std::ostream& os, const Thread* thread) { // Determine the wait message and object we're waiting or blocked upon. - mirror::Object* pretty_object = nullptr; + mirror::Object* pretty_object; + uint32_t lock_owner; + ThreadState state = FetchState(thread, &pretty_object, &lock_owner); + const char* wait_message = nullptr; - uint32_t lock_owner = ThreadList::kInvalidThreadId; - ThreadState state = thread->GetState(); - if (state == kWaiting || state == kTimedWaiting || state == kSleeping) { - wait_message = (state == kSleeping) ? " - sleeping on " : " - waiting on "; - Thread* self = Thread::Current(); - MutexLock mu(self, *thread->GetWaitMutex()); - Monitor* monitor = thread->GetWaitMonitor(); - if (monitor != nullptr) { - pretty_object = monitor->GetObject(); - } - } else if (state == kBlocked || state == kWaitingForLockInflation) { - wait_message = (state == kBlocked) ? " - waiting to lock " - : " - waiting for lock inflation of "; - pretty_object = thread->GetMonitorEnterObject(); - if (pretty_object != nullptr) { - if (kUseReadBarrier && Thread::Current()->GetIsGcMarking()) { - // We may call Thread::Dump() in the middle of the CC thread flip and this thread's stack - // may have not been flipped yet and "pretty_object" may be a from-space (stale) ref, in - // which case the GetLockOwnerThreadId() call below will crash. So explicitly mark/forward - // it here. - pretty_object = ReadBarrier::Mark(pretty_object); - } - lock_owner = pretty_object->GetLockOwnerThreadId(); - } + switch (state) { + case kWaiting: + case kTimedWaiting: + wait_message = " - waiting on "; + break; + + case kSleeping: + wait_message = " - sleeping on "; + break; + + case kBlocked: + wait_message = " - waiting to lock "; + break; + + case kWaitingForLockInflation: + wait_message = " - waiting for lock inflation of "; + break; + + default: + break; + } + + if (wait_message == nullptr) { + return; } - if (wait_message != nullptr) { - if (pretty_object == nullptr) { - os << wait_message << "an unknown object"; + if (pretty_object == nullptr) { + os << wait_message << "an unknown object"; + } else { + if ((pretty_object->GetLockWord(true).GetState() == LockWord::kThinLocked) && + Locks::mutator_lock_->IsExclusiveHeld(Thread::Current())) { + // Getting the identity hashcode here would result in lock inflation and suspension of the + // current thread, which isn't safe if this is the only runnable thread. + os << wait_message << StringPrintf("<@addr=0x%" PRIxPTR "> (a %s)", + reinterpret_cast(pretty_object), + pretty_object->PrettyTypeOf().c_str()); } else { - if ((pretty_object->GetLockWord(true).GetState() == LockWord::kThinLocked) && - Locks::mutator_lock_->IsExclusiveHeld(Thread::Current())) { - // Getting the identity hashcode here would result in lock inflation and suspension of the - // current thread, which isn't safe if this is the only runnable thread. - os << wait_message << StringPrintf("<@addr=0x%" PRIxPTR "> (a %s)", - reinterpret_cast(pretty_object), - pretty_object->PrettyTypeOf().c_str()); - } else { - // - waiting on <0x6008c468> (a java.lang.Class) - // Call PrettyTypeOf before IdentityHashCode since IdentityHashCode can cause thread - // suspension and move pretty_object. - const std::string pretty_type(pretty_object->PrettyTypeOf()); - os << wait_message << StringPrintf("<0x%08x> (a %s)", pretty_object->IdentityHashCode(), - pretty_type.c_str()); + // - waiting on <0x6008c468> (a java.lang.Class) + // Call PrettyTypeOf before IdentityHashCode since IdentityHashCode can cause thread + // suspension and move pretty_object. + const std::string pretty_type(pretty_object->PrettyTypeOf()); + os << wait_message << StringPrintf("<0x%08x> (a %s)", pretty_object->IdentityHashCode(), + pretty_type.c_str()); + } + } + // - waiting to lock <0x613f83d8> (a java.lang.Object) held by thread 5 + if (lock_owner != ThreadList::kInvalidThreadId) { + os << " held by thread " << lock_owner; + } + os << "\n"; +} + +ThreadState Monitor::FetchState(const Thread* thread, + /* out */ mirror::Object** monitor_object, + /* out */ uint32_t* lock_owner_tid) { + DCHECK(monitor_object != nullptr); + DCHECK(lock_owner_tid != nullptr); + + *monitor_object = nullptr; + *lock_owner_tid = ThreadList::kInvalidThreadId; + + ThreadState state = thread->GetState(); + + switch (state) { + case kWaiting: + case kTimedWaiting: + case kSleeping: + { + Thread* self = Thread::Current(); + MutexLock mu(self, *thread->GetWaitMutex()); + Monitor* monitor = thread->GetWaitMonitor(); + if (monitor != nullptr) { + *monitor_object = monitor->GetObject(); } } - // - waiting to lock <0x613f83d8> (a java.lang.Object) held by thread 5 - if (lock_owner != ThreadList::kInvalidThreadId) { - os << " held by thread " << lock_owner; + break; + + case kBlocked: + case kWaitingForLockInflation: + { + mirror::Object* lock_object = thread->GetMonitorEnterObject(); + if (lock_object != nullptr) { + if (kUseReadBarrier && Thread::Current()->GetIsGcMarking()) { + // We may call Thread::Dump() in the middle of the CC thread flip and this thread's stack + // may have not been flipped yet and "pretty_object" may be a from-space (stale) ref, in + // which case the GetLockOwnerThreadId() call below will crash. So explicitly mark/forward + // it here. + lock_object = ReadBarrier::Mark(lock_object); + } + *monitor_object = lock_object; + *lock_owner_tid = lock_object->GetLockOwnerThreadId(); + } } - os << "\n"; + break; + + default: + break; } + + return state; } mirror::Object* Monitor::GetContendedMonitor(Thread* thread) { diff --git a/runtime/monitor.h b/runtime/monitor.h index b4c0e6f471..30f47d849f 100644 --- a/runtime/monitor.h +++ b/runtime/monitor.h @@ -97,6 +97,11 @@ class Monitor { static void DescribeWait(std::ostream& os, const Thread* thread) REQUIRES(!Locks::thread_suspend_count_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + static ThreadState FetchState(const Thread* thread, + /* out */ mirror::Object** monitor_object, + /* out */ uint32_t* lock_owner_tid) + REQUIRES(!Locks::thread_suspend_count_lock_) + REQUIRES_SHARED(Locks::mutator_lock_); // Used to implement JDWP's ThreadReference.CurrentContendedMonitor. static mirror::Object* GetContendedMonitor(Thread* thread) -- GitLab From 06fa9f09ab9099c58c30696d1fc8cac4f0a725b8 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Thu, 14 Dec 2017 11:15:59 -0800 Subject: [PATCH 189/226] Added option to control debugging info on/off. Rationale: Mads Ager recommended having debug info on/off as separate interesting cases for fuzz testing D8. Bug: 70576364 Test: fuzz testing Change-Id: Ie4def53b9ec6b855826864e20c89ef30a0d2a9b8 --- tools/jfuzz/README.md | 4 ++ tools/jfuzz/run_dex_fuzz_test.py | 16 ++++-- tools/jfuzz/run_jfuzz_test.py | 85 +++++++++++++++++++++----------- 3 files changed, 73 insertions(+), 32 deletions(-) diff --git a/tools/jfuzz/README.md b/tools/jfuzz/README.md index bee2396fea..1991c97529 100644 --- a/tools/jfuzz/README.md +++ b/tools/jfuzz/README.md @@ -51,6 +51,7 @@ How to start JFuzz testing [--jfuzz_arg=ARG] [--true_divergence] [--dexer=DEXER] + [--debug_info] where @@ -67,6 +68,7 @@ where --jfuzz_arg : argument for jfuzz --true_divergence : don't bisect timeout divergences --dexer=DEXER : use either dx, d8, or jack to obtain dex files + --debug_info : include debugging info How to start JFuzz nightly testing ================================== @@ -88,6 +90,7 @@ How to start J/DexFuzz testing (multi-layered) [--num_inputs=NUM_INPUTS] [--device=DEVICE] [--dexer=DEXER] + [--debug_info] where @@ -95,6 +98,7 @@ where --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 + --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 fdff9c03fa..a25ef3fca5 100755 --- a/tools/jfuzz/run_dex_fuzz_test.py +++ b/tools/jfuzz/run_dex_fuzz_test.py @@ -41,7 +41,7 @@ from common.common import RunCommand class DexFuzzTester(object): """Tester that feeds JFuzz programs into DexFuzz testing.""" - def __init__(self, num_tests, num_inputs, device, dexer): + def __init__(self, num_tests, num_inputs, device, dexer, debug_info): """Constructor for the tester. Args: @@ -49,6 +49,7 @@ class DexFuzzTester(object): num_inputs: int, number of JFuzz programs to generate device: string, target device serial number (or None) dexer: string, defines dexer + debug_info: boolean, if True include debugging info """ self._num_tests = num_tests self._num_inputs = num_inputs @@ -59,6 +60,7 @@ class DexFuzzTester(object): self._inputs_dir = None self._dexfuzz_env = None self._dexer = dexer + self._debug_info = debug_info def __enter__(self): """On entry, enters new temp directory after saving current directory. @@ -110,7 +112,8 @@ class DexFuzzTester(object): FatalError: error when compilation fails """ if self._dexer == 'dx' or self._dexer == 'd8': - if RunCommand(['javac', 'Test.java'], + dbg = '-g' if self._debug_info else '-g:none' + if RunCommand(['javac', dbg, 'Test.java'], out=None, err='jerr.txt', timeout=30) != RetCode.SUCCESS: print('Unexpected error while running javac') raise FatalError('Unexpected error while running javac') @@ -183,12 +186,17 @@ def main(): help='number of tests to run (default: 1000)') parser.add_argument('--num_inputs', default=10, type=int, 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)') - parser.add_argument('--device', help='target device serial number') + parser.add_argument('--debug_info', default=False, action='store_true', + help='include debugging info') args = parser.parse_args() # Run the DexFuzz tester. - with DexFuzzTester(args.num_tests, args.num_inputs, args.device, args.dexer) as fuzzer: + with DexFuzzTester(args.num_tests, + args.num_inputs, + args.device, + args.dexer, args.debug_info) as fuzzer: fuzzer.Run() if __name__ == '__main__': diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py index b88994013e..2b56767c86 100755 --- a/tools/jfuzz/run_jfuzz_test.py +++ b/tools/jfuzz/run_jfuzz_test.py @@ -43,11 +43,12 @@ from common.common import DeviceTestEnv BISECTABLE_RET_CODES = (RetCode.SUCCESS, RetCode.ERROR, RetCode.TIMEOUT) -def GetExecutionModeRunner(dexer, device, mode): +def GetExecutionModeRunner(dexer, debug_info, device, mode): """Returns a runner for the given execution mode. Args: dexer: string, defines dexer + debug_info: boolean, if True include debugging info device: string, target device serial number (or None) mode: string, execution mode Returns: @@ -56,15 +57,15 @@ def GetExecutionModeRunner(dexer, device, mode): FatalError: error for unknown execution mode """ if mode == 'ri': - return TestRunnerRIOnHost() + return TestRunnerRIOnHost(debug_info) if mode == 'hint': - return TestRunnerArtIntOnHost(dexer) + return TestRunnerArtIntOnHost(dexer, debug_info) if mode == 'hopt': - return TestRunnerArtOptOnHost(dexer) + return TestRunnerArtOptOnHost(dexer, debug_info) if mode == 'tint': - return TestRunnerArtIntOnTarget(dexer, device) + return TestRunnerArtIntOnTarget(dexer, debug_info, device) if mode == 'topt': - return TestRunnerArtOptOnTarget(dexer, device) + return TestRunnerArtOptOnTarget(dexer, debug_info, device) raise FatalError('Unknown execution mode') @@ -117,19 +118,22 @@ class TestRunner(object): class TestRunnerWithHostCompilation(TestRunner): """Abstract test runner that supports compilation on host.""" - def __init__(self, dexer): + def __init__(self, dexer, debug_info): """Constructor for the runner with host compilation. Args: dexer: string, defines dexer + debug_info: boolean, if True include debugging info """ + self._dexer = dexer + self._debug_info = debug_info self._jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', 'Test.java'] - self._dexer = dexer def CompileOnHost(self): if self._dexer == 'dx' or self._dexer == 'd8': - if RunCommand(['javac', 'Test.java'], + dbg = '-g' if self._debug_info else '-g:none' + if RunCommand(['javac', dbg, 'Test.java'], out=None, err=None, timeout=30) == RetCode.SUCCESS: dx = 'dx' if self._dexer == 'dx' else 'd8-compat-dx' retc = RunCommand([dx, '--dex', '--output=classes.dex'] + glob('*.class'), @@ -147,6 +151,14 @@ class TestRunnerWithHostCompilation(TestRunner): class TestRunnerRIOnHost(TestRunner): """Concrete test runner of the reference implementation on host.""" + def __init__(self, debug_info): + """Constructor for the runner with host compilation. + + Args: + debug_info: boolean, if True include debugging info + """ + self._debug_info = debug_info + @property def description(self): return 'RI on host' @@ -156,7 +168,8 @@ class TestRunnerRIOnHost(TestRunner): return 'RI' def CompileAndRunTest(self): - if RunCommand(['javac', 'Test.java'], + dbg = '-g' if self._debug_info else '-g:none' + if RunCommand(['javac', dbg, 'Test.java'], out=None, err=None, timeout=30) == RetCode.SUCCESS: retc = RunCommand(['java', 'Test'], self.output_file, err=None) else: @@ -170,14 +183,15 @@ class TestRunnerRIOnHost(TestRunner): class TestRunnerArtOnHost(TestRunnerWithHostCompilation): """Abstract test runner of Art on host.""" - def __init__(self, dexer, extra_args=None): + def __init__(self, dexer, debug_info, extra_args=None): """Constructor for the Art on host tester. Args: dexer: string, defines dexer + debug_info: boolean, if True include debugging info extra_args: list of strings, extra arguments for dalvikvm """ - super().__init__(dexer) + super().__init__(dexer, debug_info) self._art_cmd = ['/bin/bash', 'art', '-cp', 'classes.dex'] if extra_args is not None: self._art_cmd += extra_args @@ -194,13 +208,14 @@ class TestRunnerArtOnHost(TestRunnerWithHostCompilation): class TestRunnerArtIntOnHost(TestRunnerArtOnHost): """Concrete test runner of interpreter mode Art on host.""" - def __init__(self, dexer): + def __init__(self, dexer, debug_info): """Constructor for the Art on host tester (interpreter). Args: dexer: string, defines dexer + debug_info: boolean, if True include debugging info """ - super().__init__(dexer, ['-Xint']) + super().__init__(dexer, debug_info, ['-Xint']) @property def description(self): @@ -217,13 +232,14 @@ class TestRunnerArtIntOnHost(TestRunnerArtOnHost): class TestRunnerArtOptOnHost(TestRunnerArtOnHost): """Concrete test runner of optimizing compiler mode Art on host.""" - def __init__(self, dexer): + def __init__(self, dexer, debug_info): """Constructor for the Art on host tester (optimizing). Args: dexer: string, defines dexer + debug_info: boolean, if True include debugging info """ - super().__init__(dexer, None) + super().__init__(dexer, debug_info, None) @property def description(self): @@ -242,15 +258,16 @@ class TestRunnerArtOptOnHost(TestRunnerArtOnHost): class TestRunnerArtOnTarget(TestRunnerWithHostCompilation): """Abstract test runner of Art on target.""" - def __init__(self, dexer, device, extra_args=None): + def __init__(self, dexer, debug_info, device, extra_args=None): """Constructor for the Art on target tester. Args: dexer: string, defines dexer + debug_info: boolean, if True include debugging info device: string, target device serial number (or None) extra_args: list of strings, extra arguments for dalvikvm """ - super().__init__(dexer) + super().__init__(dexer, debug_info) self._test_env = DeviceTestEnv('jfuzz_', specific_device=device) self._dalvik_cmd = ['dalvikvm'] if extra_args is not None: @@ -284,14 +301,15 @@ class TestRunnerArtOnTarget(TestRunnerWithHostCompilation): class TestRunnerArtIntOnTarget(TestRunnerArtOnTarget): """Concrete test runner of interpreter mode Art on target.""" - def __init__(self, dexer, device): + def __init__(self, dexer, debug_info, device): """Constructor for the Art on target tester (interpreter). Args: dexer: string, defines dexer + debug_info: boolean, if True include debugging info device: string, target device serial number (or None) """ - super().__init__(dexer, device, ['-Xint']) + super().__init__(dexer, debug_info, device, ['-Xint']) @property def description(self): @@ -308,14 +326,15 @@ class TestRunnerArtIntOnTarget(TestRunnerArtOnTarget): class TestRunnerArtOptOnTarget(TestRunnerArtOnTarget): """Concrete test runner of optimizing compiler mode Art on target.""" - def __init__(self, dexer, device): + def __init__(self, dexer, debug_info, device): """Constructor for the Art on target tester (optimizing). Args: dexer: string, defines dexer + debug_info: boolean, if True include debugging info device: string, target device serial number (or None) """ - super().__init__(dexer, device, None) + super().__init__(dexer, debug_info, device, None) @property def description(self): @@ -345,7 +364,7 @@ class JFuzzTester(object): """Tester that runs JFuzz many times and report divergences.""" def __init__(self, num_tests, device, mode1, mode2, jfuzz_args, - report_script, true_divergence_only, dexer): + report_script, true_divergence_only, dexer, debug_info): """Constructor for the tester. Args: @@ -357,15 +376,17 @@ class JFuzzTester(object): report_script: string, path to script called for each divergence true_divergence_only: boolean, if True don't bisect timeout divergences dexer: string, defines dexer + debug_info: boolean, if True include debugging info """ self._num_tests = num_tests self._device = device - self._runner1 = GetExecutionModeRunner(dexer, device, mode1) - self._runner2 = GetExecutionModeRunner(dexer, device, mode2) + self._runner1 = GetExecutionModeRunner(dexer, debug_info, device, mode1) + self._runner2 = GetExecutionModeRunner(dexer, debug_info, device, mode2) self._jfuzz_args = jfuzz_args self._report_script = report_script self._true_divergence_only = true_divergence_only self._dexer = dexer + self._debug_info = debug_info self._save_dir = None self._results_dir = None self._jfuzz_dir = None @@ -409,6 +430,7 @@ class JFuzzTester(object): print('Exec-mode1:', self._runner1.description) print('Exec-mode2:', self._runner2.description) print('Dexer :', self._dexer) + print('Debug-info:', self._debug_info) print() self.ShowStats() for self._test in range(1, self._num_tests + 1): @@ -529,6 +551,7 @@ class JFuzzTester(object): wrapped_args = ['--jfuzz_arg={0}'.format(opt) for opt in jfuzz_args] repro_cmd_str = (os.path.basename(__file__) + ' --num_tests=1 --dexer=' + self._dexer + + (' --debug_info' if self._debug_info else '') + ' '.join(wrapped_args)) comment = 'jfuzz {0}\nReproduce test:\n{1}\nReproduce divergence:\n{2}\n'.format( jfuzz_ver, jfuzz_cmd_str, repro_cmd_str) @@ -610,14 +633,20 @@ def main(): help='do not bisect timeout divergences') parser.add_argument('--dexer', default='dx', type=str, help='defines dexer as dx, d8, or jack (default: dx)') + parser.add_argument('--debug_info', default=False, action='store_true', + help='include debugging info') args = parser.parse_args() if args.mode1 == args.mode2: raise FatalError('Identical execution modes given') # Run the JFuzz tester. with JFuzzTester(args.num_tests, - args.device, args.mode1, args.mode2, - args.jfuzz_args, args.report_script, - args.true_divergence, args.dexer) as fuzzer: + args.device, + args.mode1, args.mode2, + args.jfuzz_args, + args.report_script, + args.true_divergence, + args.dexer, + args.debug_info) as fuzzer: fuzzer.Run() if __name__ == '__main__': -- GitLab From b6f309e4e9b7c0531f8d09998b48d82b45714173 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 11 Dec 2017 20:45:41 -0800 Subject: [PATCH 190/226] ART: Factor out StackTraceElement creation Move to a helper function to aid future reuse. Bug: 70538431 Test: m test-art-host Change-Id: Ia7c26d707abbae3a9d5cc5db99007e34a8ab6b62 --- runtime/thread.cc | 105 ++++++++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 49 deletions(-) diff --git a/runtime/thread.cc b/runtime/thread.cc index cb350edb5f..21b8ea51e5 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -2603,6 +2603,61 @@ bool Thread::IsExceptionThrownByCurrentMethod(ObjPtr exceptio return count_visitor.GetDepth() == static_cast(exception->GetStackDepth()); } +static ObjPtr CreateStackTraceElement( + const ScopedObjectAccessAlreadyRunnable& soa, + ArtMethod* method, + uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) { + int32_t line_number; + StackHandleScope<3> hs(soa.Self()); + auto class_name_object(hs.NewHandle(nullptr)); + auto source_name_object(hs.NewHandle(nullptr)); + if (method->IsProxyMethod()) { + line_number = -1; + class_name_object.Assign(method->GetDeclaringClass()->GetName()); + // source_name_object intentionally left null for proxy methods + } else { + line_number = method->GetLineNumFromDexPC(dex_pc); + // Allocate element, potentially triggering GC + // TODO: reuse class_name_object via Class::name_? + const char* descriptor = method->GetDeclaringClassDescriptor(); + CHECK(descriptor != nullptr); + std::string class_name(PrettyDescriptor(descriptor)); + class_name_object.Assign( + mirror::String::AllocFromModifiedUtf8(soa.Self(), class_name.c_str())); + if (class_name_object == nullptr) { + soa.Self()->AssertPendingOOMException(); + return nullptr; + } + const char* source_file = method->GetDeclaringClassSourceFile(); + if (line_number == -1) { + // Make the line_number field of StackTraceElement hold the dex pc. + // source_name_object is intentionally left null if we failed to map the dex pc to + // a line number (most probably because there is no debug info). See b/30183883. + line_number = dex_pc; + } else { + if (source_file != nullptr) { + source_name_object.Assign(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file)); + if (source_name_object == nullptr) { + soa.Self()->AssertPendingOOMException(); + return nullptr; + } + } + } + } + const char* method_name = method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetName(); + CHECK(method_name != nullptr); + Handle method_name_object( + hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), method_name))); + if (method_name_object == nullptr) { + return nullptr; + } + return mirror::StackTraceElement::Alloc(soa.Self(), + class_name_object, + method_name_object, + source_name_object, + line_number); +} + jobjectArray Thread::InternalStackTraceToStackTraceElementArray( const ScopedObjectAccessAlreadyRunnable& soa, jobject internal, @@ -2649,55 +2704,7 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray( ArtMethod* method = method_trace->GetElementPtrSize(i, kRuntimePointerSize); uint32_t dex_pc = method_trace->GetElementPtrSize( i + method_trace->GetLength() / 2, kRuntimePointerSize); - int32_t line_number; - StackHandleScope<3> hs(soa.Self()); - auto class_name_object(hs.NewHandle(nullptr)); - auto source_name_object(hs.NewHandle(nullptr)); - if (method->IsProxyMethod()) { - line_number = -1; - class_name_object.Assign(method->GetDeclaringClass()->GetName()); - // source_name_object intentionally left null for proxy methods - } else { - line_number = method->GetLineNumFromDexPC(dex_pc); - // Allocate element, potentially triggering GC - // TODO: reuse class_name_object via Class::name_? - const char* descriptor = method->GetDeclaringClassDescriptor(); - CHECK(descriptor != nullptr); - std::string class_name(PrettyDescriptor(descriptor)); - class_name_object.Assign( - mirror::String::AllocFromModifiedUtf8(soa.Self(), class_name.c_str())); - if (class_name_object == nullptr) { - soa.Self()->AssertPendingOOMException(); - return nullptr; - } - const char* source_file = method->GetDeclaringClassSourceFile(); - if (line_number == -1) { - // Make the line_number field of StackTraceElement hold the dex pc. - // source_name_object is intentionally left null if we failed to map the dex pc to - // a line number (most probably because there is no debug info). See b/30183883. - line_number = dex_pc; - } else { - if (source_file != nullptr) { - source_name_object.Assign(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file)); - if (source_name_object == nullptr) { - soa.Self()->AssertPendingOOMException(); - return nullptr; - } - } - } - } - const char* method_name = method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetName(); - CHECK(method_name != nullptr); - Handle method_name_object( - hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), method_name))); - if (method_name_object == nullptr) { - return nullptr; - } - ObjPtr obj = mirror::StackTraceElement::Alloc(soa.Self(), - class_name_object, - method_name_object, - source_name_object, - line_number); + ObjPtr obj = CreateStackTraceElement(soa, method, dex_pc); if (obj == nullptr) { return nullptr; } -- GitLab From 217eb067308cf5aa43065377b66acbbee0f5b7c3 Mon Sep 17 00:00:00 2001 From: Mingyao Yang Date: Mon, 11 Dec 2017 15:20:07 -0800 Subject: [PATCH 191/226] Fix the side effects of clinit check HClinitCheck obviously does reads so it's side effects should include all reads and writes, just like HInvoke. GVN now explicitly allows clinit check to be reused, which would otherwise be disallowed based on the dependency introduced by the new side effects. Also make licm's logic cleaner and treat clinit check as a special case also, otherwise licm can't hoist clinit check due to the dependency introduced by the new side effects also. Test: run-test on host. Change-Id: I16886cfe557803d84d84ce68fbb185ebfc0b84dc --- compiler/optimizing/gvn.cc | 7 +++++-- compiler/optimizing/licm.cc | 27 ++++++++++++++++++++++----- compiler/optimizing/nodes.h | 2 +- test/445-checker-licm/src/Main.java | 28 ++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc index 813772e9af..71c394ec1f 100644 --- a/compiler/optimizing/gvn.cc +++ b/compiler/optimizing/gvn.cc @@ -301,8 +301,11 @@ class ValueSet : public ArenaObject { // Pure instructions are put into odd buckets to speed up deletion. Note that in the // case of irreducible loops, we don't put pure instructions in odd buckets, as we // need to delete them when entering the loop. - if (instruction->GetSideEffects().HasDependencies() || - instruction->GetBlock()->GetGraph()->HasIrreducibleLoops()) { + // ClinitCheck is treated as a pure instruction since it's only executed + // once. + bool pure = !instruction->GetSideEffects().HasDependencies() || + instruction->IsClinitCheck(); + if (!pure || instruction->GetBlock()->GetGraph()->HasIrreducibleLoops()) { return (hash_code << 1) | 0; } else { return (hash_code << 1) | 1; diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc index 7af1a20f98..d3a0376e9c 100644 --- a/compiler/optimizing/licm.cc +++ b/compiler/optimizing/licm.cc @@ -129,10 +129,25 @@ void LICM::Run() { !inst_it.Done(); inst_it.Advance()) { HInstruction* instruction = inst_it.Current(); - if (instruction->CanBeMoved() - && (!instruction->CanThrow() || !found_first_non_hoisted_visible_instruction_in_loop) - && !instruction->GetSideEffects().MayDependOn(loop_effects) - && InputsAreDefinedBeforeLoop(instruction)) { + bool can_move = false; + if (instruction->CanBeMoved() && InputsAreDefinedBeforeLoop(instruction)) { + if (instruction->CanThrow()) { + if (!found_first_non_hoisted_visible_instruction_in_loop) { + DCHECK(instruction->GetBlock()->IsLoopHeader()); + if (instruction->IsClinitCheck()) { + // clinit is only done once, and since all visible instructions + // in the loop header so far have been hoisted out, we can hoist + // the clinit check out also. + can_move = true; + } else if (!instruction->GetSideEffects().MayDependOn(loop_effects)) { + can_move = true; + } + } + } else if (!instruction->GetSideEffects().MayDependOn(loop_effects)) { + can_move = true; + } + } + if (can_move) { // We need to update the environment if the instruction has a loop header // phi in it. if (instruction->NeedsEnvironment()) { @@ -142,7 +157,9 @@ void LICM::Run() { } instruction->MoveBefore(pre_header->GetLastInstruction()); MaybeRecordStat(stats_, MethodCompilationStat::kLoopInvariantMoved); - } else if (instruction->CanThrow() || instruction->DoesAnyWrite()) { + } + + if (!can_move && (instruction->CanThrow() || instruction->DoesAnyWrite())) { // If `instruction` can do something visible (throw or write), // we cannot move further instructions that can throw. found_first_non_hoisted_visible_instruction_in_loop = true; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 42a9d95b9a..d33f2f1d65 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -6259,7 +6259,7 @@ class HClinitCheck FINAL : public HExpression<1> { HClinitCheck(HLoadClass* constant, uint32_t dex_pc) : HExpression( DataType::Type::kReference, - SideEffects::AllChanges(), // Assume write/read on all fields/arrays. + SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays. dex_pc) { SetRawInputAt(0, constant); } diff --git a/test/445-checker-licm/src/Main.java b/test/445-checker-licm/src/Main.java index 00ce3a9f8e..9635e70278 100644 --- a/test/445-checker-licm/src/Main.java +++ b/test/445-checker-licm/src/Main.java @@ -15,6 +15,11 @@ */ public class Main { + static class Dummy { + static int getValue() { + return 1; + } + } /// CHECK-START: int Main.div() licm (before) /// CHECK-DAG: Div loop:{{B\d+}} @@ -107,6 +112,28 @@ public class Main { return result; } + /// CHECK-START: int Main.clinitCheck() licm (before) + /// CHECK-DAG: <> LoadClass loop:<> + /// CHECK-DAG: ClinitCheck [<>] loop:<> + + /// CHECK-START: int Main.clinitCheck() licm (after) + /// CHECK-NOT: LoadClass loop:{{B\d+}} + /// CHECK-NOT: ClinitCheck loop:{{B\d+}} + + /// CHECK-START: int Main.clinitCheck() licm (after) + /// CHECK-DAG: <> LoadClass loop:none + /// CHECK-DAG: ClinitCheck [<>] loop:none + + public static int clinitCheck() { + int i = 0; + int sum = 0; + do { + sum += Dummy.getValue(); + i++; + } while (i < 10); + return sum; + } + /// CHECK-START: int Main.divAndIntrinsic(int[]) licm (before) /// CHECK-DAG: Div loop:{{B\d+}} @@ -213,6 +240,7 @@ public class Main { assertEquals(18900, innerMul()); assertEquals(105, divByA(2, 0)); assertEquals(12, arrayLength(new int[] { 4, 8 })); + assertEquals(10, clinitCheck()); assertEquals(21, divAndIntrinsic(new int[] { 4, -2, 8, -3 })); assertEquals(45, invariantBoundIntrinsic(-10)); assertEquals(30, invariantBodyIntrinsic(2, 3)); -- GitLab From 1fb8d5ee6c7f03269cf08b18a0af5b4e25924a27 Mon Sep 17 00:00:00 2001 From: Alex Klyubin Date: Thu, 14 Dec 2017 21:32:44 +0000 Subject: [PATCH 192/226] Revert "Revert "Revert "Add patchoat test""" This reverts commit 203010a86542b16991ede122085b07eea6c55bec. Reason for revert: This test is breaking on a build server -- no idea why as no output can be seen. Change-Id: Ibb93d84e673c45e13ba81b1f045eb7c8d31494fd --- build/Android.gtest.mk | 10 -- patchoat/Android.bp | 13 -- patchoat/patchoat_test.cc | 346 -------------------------------------- runtime/image.h | 4 - 4 files changed, 373 deletions(-) delete mode 100644 patchoat/patchoat_test.cc diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 230b2665e6..3c8eade773 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -124,7 +124,6 @@ ART_GTEST_image_space_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex ART_GTEST_oat_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 ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods ART_GTEST_profile_assistant_test_DEX_DEPS := ProfileTestMultiDex @@ -252,11 +251,6 @@ ART_GTEST_oatdump_test_TARGET_DEPS := \ 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_patchoat_test_HOST_DEPS := \ - $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) -ART_GTEST_patchoat_test_TARGET_DEPS := \ - $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) - # Profile assistant tests requires profman utility. ART_GTEST_profile_assistant_test_HOST_DEPS := profmand-host ART_GTEST_profile_assistant_test_TARGET_DEPS := profmand-target @@ -276,7 +270,6 @@ ART_TEST_MODULES := \ art_dexoptanalyzer_tests \ art_imgdiag_tests \ art_oatdump_tests \ - art_patchoat_tests \ art_profman_tests \ art_runtime_tests \ art_runtime_compiler_tests \ @@ -690,9 +683,6 @@ ART_GTEST_dex2oat_image_test_DEX_DEPS := ART_GTEST_dex2oat_image_test_HOST_DEPS := ART_GTEST_dex2oat_image_test_TARGET_DEPS := ART_GTEST_object_test_DEX_DEPS := -ART_GTEST_patchoat_test_DEX_DEPS := -ART_GTEST_patchoat_test_HOST_DEPS := -ART_GTEST_patchoat_test_TARGET_DEPS := ART_GTEST_proxy_test_DEX_DEPS := ART_GTEST_reflection_test_DEX_DEPS := ART_GTEST_stub_test_DEX_DEPS := diff --git a/patchoat/Android.bp b/patchoat/Android.bp index 0902823644..d3bc2a754b 100644 --- a/patchoat/Android.bp +++ b/patchoat/Android.bp @@ -47,16 +47,3 @@ art_cc_binary { "libartd", ], } - -art_cc_test { - name: "art_patchoat_tests", - defaults: [ - "art_gtest_defaults", - ], - srcs: [ - "patchoat_test.cc", - ], - shared_libs: [ - "libartd", - ], -} diff --git a/patchoat/patchoat_test.cc b/patchoat/patchoat_test.cc deleted file mode 100644 index e06b2dbf38..0000000000 --- a/patchoat/patchoat_test.cc +++ /dev/null @@ -1,346 +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. - */ - -#include -#include - -#include "android-base/stringprintf.h" -#include "android-base/strings.h" - -#include "dexopt_test.h" -#include "runtime.h" - -#include - -namespace art { - -using android::base::StringPrintf; - -class PatchoatTest : public DexoptTest { - public: - static void AddRuntimeArg(std::vector& args, const std::string& arg) { - args.push_back("--runtime-arg"); - args.push_back(arg); - } - - bool CompileBootImage(const std::vector& extra_args, - const std::string& image_file_name_prefix, - uint32_t base_addr, - std::string* error_msg) { - Runtime* const runtime = Runtime::Current(); - std::vector argv; - argv.push_back(runtime->GetCompilerExecutable()); - AddRuntimeArg(argv, "-Xms64m"); - AddRuntimeArg(argv, "-Xmx64m"); - std::vector dex_files = GetLibCoreDexFileNames(); - for (const std::string& dex_file : dex_files) { - argv.push_back("--dex-file=" + dex_file); - argv.push_back("--dex-location=" + dex_file); - } - if (runtime->IsJavaDebuggable()) { - argv.push_back("--debuggable"); - } - runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv); - - AddRuntimeArg(argv, "-Xverify:softfail"); - - if (!kIsTargetBuild) { - argv.push_back("--host"); - } - - argv.push_back("--image=" + image_file_name_prefix + ".art"); - argv.push_back("--oat-file=" + image_file_name_prefix + ".oat"); - argv.push_back("--oat-location=" + image_file_name_prefix + ".oat"); - argv.push_back(StringPrintf("--base=0x%" PRIx32, base_addr)); - argv.push_back("--compile-pic"); - argv.push_back("--multi-image"); - argv.push_back("--no-generate-debug-info"); - - std::vector compiler_options = runtime->GetCompilerOptions(); - argv.insert(argv.end(), compiler_options.begin(), compiler_options.end()); - - // We must set --android-root. - const char* android_root = getenv("ANDROID_ROOT"); - CHECK(android_root != nullptr); - argv.push_back("--android-root=" + std::string(android_root)); - argv.insert(argv.end(), extra_args.begin(), extra_args.end()); - - return RunDex2OatOrPatchoat(argv, error_msg); - } - - bool RelocateBootImage(const std::string& input_image_location, - const std::string& output_image_filename, - off_t base_offset_delta, - std::string* error_msg) { - Runtime* const runtime = Runtime::Current(); - std::vector argv; - argv.push_back(runtime->GetPatchoatExecutable()); - argv.push_back("--input-image-location=" + input_image_location); - argv.push_back("--output-image-file=" + output_image_filename); - argv.push_back(StringPrintf("--base-offset-delta=0x%jx", (intmax_t) base_offset_delta)); - argv.push_back(StringPrintf("--instruction-set=%s", GetInstructionSetString(kRuntimeISA))); - - return RunDex2OatOrPatchoat(argv, error_msg); - } - - bool RunDex2OatOrPatchoat(const std::vector& args, std::string* error_msg) { - int link[2]; - - if (pipe(link) == -1) { - return false; - } - - pid_t pid = fork(); - if (pid == -1) { - return false; - } - - if (pid == 0) { - // We need dex2oat to actually log things. - setenv("ANDROID_LOG_TAGS", "*:f", 1); - dup2(link[1], STDERR_FILENO); - close(link[0]); - close(link[1]); - std::vector c_args; - for (const std::string& str : args) { - c_args.push_back(str.c_str()); - } - c_args.push_back(nullptr); - execv(c_args[0], const_cast(c_args.data())); - exit(1); - UNREACHABLE(); - } else { - close(link[1]); - char buffer[128]; - memset(buffer, 0, 128); - ssize_t bytes_read = 0; - - while (TEMP_FAILURE_RETRY(bytes_read = read(link[0], buffer, 128)) > 0) { - *error_msg += std::string(buffer, bytes_read); - } - close(link[0]); - int status = -1; - if (waitpid(pid, &status, 0) != -1) { - return (status == 0); - } - return false; - } - } - - bool CompileBootImageToDir( - const std::string& output_dir, - const std::vector& dex2oat_extra_args, - uint32_t base_addr, - std::string* error_msg) { - return CompileBootImage(dex2oat_extra_args, output_dir + "/boot", base_addr, error_msg); - } - - bool CopyImageChecksumAndSetPatchDelta( - const std::string& src_image_filename, - const std::string& dest_image_filename, - off_t dest_patch_delta, - std::string* error_msg) { - std::unique_ptr src_file(OS::OpenFileForReading(src_image_filename.c_str())); - if (src_file.get() == nullptr) { - *error_msg = StringPrintf("Failed to open source image file %s", src_image_filename.c_str()); - return false; - } - ImageHeader src_header; - if (!src_file->ReadFully(&src_header, sizeof(src_header))) { - *error_msg = StringPrintf("Failed to read source image file %s", src_image_filename.c_str()); - return false; - } - - std::unique_ptr dest_file(OS::OpenFileReadWrite(dest_image_filename.c_str())); - if (dest_file.get() == nullptr) { - *error_msg = - StringPrintf("Failed to open destination image file %s", dest_image_filename.c_str()); - return false; - } - ImageHeader dest_header; - if (!dest_file->ReadFully(&dest_header, sizeof(dest_header))) { - *error_msg = - StringPrintf("Failed to read destination image file %s", dest_image_filename.c_str()); - return false; - } - dest_header.SetOatChecksum(src_header.GetOatChecksum()); - dest_header.SetPatchDelta(dest_patch_delta); - if (!dest_file->ResetOffset()) { - *error_msg = - StringPrintf( - "Failed to seek to start of destination image file %s", dest_image_filename.c_str()); - return false; - } - if (!dest_file->WriteFully(&dest_header, sizeof(dest_header))) { - *error_msg = - StringPrintf("Failed to write to destination image file %s", dest_image_filename.c_str()); - dest_file->Erase(); - return false; - } - if (dest_file->FlushCloseOrErase() != 0) { - *error_msg = - StringPrintf( - "Failed to flush/close destination image file %s", dest_image_filename.c_str()); - return false; - } - - return true; - } - - bool ReadFully( - const std::string& filename, std::vector* contents, std::string* error_msg) { - std::unique_ptr file(OS::OpenFileForReading(filename.c_str())); - if (file.get() == nullptr) { - *error_msg = "Failed to open"; - return false; - } - int64_t size = file->GetLength(); - if (size < 0) { - *error_msg = "Failed to get size"; - return false; - } - contents->resize(size); - if (!file->ReadFully(&(*contents)[0], size)) { - *error_msg = "Failed to read"; - contents->clear(); - return false; - } - return true; - } - - bool BinaryDiff( - const std::string& filename1, const std::string& filename2, std::string* error_msg) { - std::string read_error_msg; - std::vector image1; - if (!ReadFully(filename1, &image1, &read_error_msg)) { - *error_msg = StringPrintf("Failed to read %s: %s", filename1.c_str(), read_error_msg.c_str()); - return true; - } - std::vector image2; - if (!ReadFully(filename2, &image2, &read_error_msg)) { - *error_msg = StringPrintf("Failed to read %s: %s", filename2.c_str(), read_error_msg.c_str()); - return true; - } - if (image1.size() != image2.size()) { - *error_msg = - StringPrintf( - "%s and %s are of different size: %zu vs %zu", - filename1.c_str(), - filename2.c_str(), - image1.size(), - image2.size()); - return true; - } - size_t size = image1.size(); - for (size_t i = 0; i < size; i++) { - if (image1[i] != image2[i]) { - *error_msg = - StringPrintf("%s and %s differ at offset %zu", filename1.c_str(), filename2.c_str(), i); - return true; - } - } - - return false; - } -}; - -TEST_F(PatchoatTest, PatchoatRelocationSameAsDex2oatRelocation) { - // This test check that relocating a boot image using patchoat produces the same result as - // producing the boot image for that relocated base address using dex2oat. To be precise, these - // two files will have two small differences: the OAT checksum and base address. However, this - // test takes this into account. - - // 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; - // Force deterministic output. We want the boot images created by this dex2oat run and the run - // below to differ only in their base address. - std::vector dex2oat_extra_args; - dex2oat_extra_args.push_back("--force-determinism"); - dex2oat_extra_args.push_back("-j1"); // Might not be needed. Causes a 3-5x slowdown. - std::string error_msg; - if (!CompileBootImageToDir(dex2oat_orig_dir, dex2oat_extra_args, orig_base_addr, &error_msg)) { - FAIL() << "CompileBootImage1 failed: " << error_msg; - } - - // Compile a "relocated" boot image into a random directory using dex2oat. This image is relocated - // in the sense that it uses a different base address. - ScratchFile dex2oat_reloc_scratch; - dex2oat_reloc_scratch.Unlink(); - std::string dex2oat_reloc_dir = dex2oat_reloc_scratch.GetFilename(); - ASSERT_EQ(0, mkdir(dex2oat_reloc_dir.c_str(), 0700)); - const uint32_t reloc_base_addr = 0x70000000; - if (!CompileBootImageToDir(dex2oat_reloc_dir, dex2oat_extra_args, reloc_base_addr, &error_msg)) { - FAIL() << "CompileBootImage2 failed: " << error_msg; - } - const off_t base_addr_delta = reloc_base_addr - orig_base_addr; - - // Relocate the original boot image using patchoat. The image is relocated by the same amount - // as the second/relocated image produced by dex2oat. - ScratchFile patchoat_scratch; - patchoat_scratch.Unlink(); - std::string patchoat_dir = patchoat_scratch.GetFilename(); - ASSERT_EQ(0, mkdir(patchoat_dir.c_str(), 0700)); - 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())); - if (!RelocateBootImage( - dex2oat_orig_dir + "/boot.art", - patchoat_dir + "/boot.art", - base_addr_delta, - &error_msg)) { - FAIL() << "RelocateBootImage failed: " << error_msg; - } - - // dex2oat_reloc_image_filename is the boot image relocated using dex2oat - // patchoat_reloc_image_filename is the boot image relocated using patchoat - std::string dex2oat_reloc_image_filename = dex2oat_reloc_dir + "/boot.art"; - std::string patchoat_reloc_image_filename = dex2oat_orig_dir + "/boot.art"; - std::replace( - patchoat_reloc_image_filename.begin() + 1, patchoat_reloc_image_filename.end(), '/', '@'); - patchoat_reloc_image_filename = - patchoat_dir - + (android::base::StartsWith(patchoat_reloc_image_filename, "/") ? "" : "/") - + patchoat_reloc_image_filename; - - // Patch up the dex2oat-relocated image so that it looks as though it was relocated by patchoat. - // patchoat preserves the OAT checksum header field and sets patch delta header field. - if (!CopyImageChecksumAndSetPatchDelta( - dex2oat_orig_dir + "/boot.art", - dex2oat_reloc_dir + "/boot.art", - base_addr_delta, - &error_msg)) { - FAIL() << "Unable to copy image checksum: " << error_msg; - } - - // Assert that the patchoat-relocated image is identical to the dex2oat-relocated image - if (BinaryDiff(dex2oat_reloc_image_filename, patchoat_reloc_image_filename, &error_msg)) { - FAIL() << "patchoat- and dex2oat-relocated images differ: " << error_msg; - } - - ClearDirectory(dex2oat_orig_dir.c_str(), /*recursive*/ true); - ClearDirectory(dex2oat_reloc_dir.c_str(), /*recursive*/ true); - ClearDirectory(patchoat_dir.c_str(), /*recursive*/ true); - rmdir(dex2oat_orig_dir.c_str()); - rmdir(dex2oat_reloc_dir.c_str()); - rmdir(patchoat_dir.c_str()); -} - -} // namespace art diff --git a/runtime/image.h b/runtime/image.h index 159a308fb3..3844186a9b 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -179,10 +179,6 @@ class PACKED(4) ImageHeader { return patch_delta_; } - void SetPatchDelta(off_t patch_delta) { - patch_delta_ = patch_delta; - } - static std::string GetOatLocationFromImageLocation(const std::string& image) { return GetLocationFromImageLocation(image, "oat"); } -- GitLab From 4531aae45b32e33481352613d6017916147f1b6d Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Thu, 2 Nov 2017 14:01:14 -0700 Subject: [PATCH 193/226] Re-enable dumping unattached threads. Bug: 31666428 Test: Ran unit tests, kill -3 on bluetooth process. Change-Id: I8db4923278a9302ab83ff53e61549b63bac67ec8 --- runtime/thread_list.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index e43b9f4b96..8095ef57c7 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -69,8 +69,7 @@ static constexpr useconds_t kThreadSuspendMaxSleepUs = 5000; // Whether we should try to dump the native stack of unattached threads. See commit ed8b723 for // some history. -// Turned off again. b/29248079 -static constexpr bool kDumpUnattachedThreadNativeStackForSigQuit = false; +static constexpr bool kDumpUnattachedThreadNativeStackForSigQuit = true; ThreadList::ThreadList(uint64_t thread_suspend_timeout_ns) : suspend_all_count_(0), -- GitLab From 9310495f3bb601058a3b6dedc298ad9f5275510e Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 13 Dec 2017 17:13:15 -0800 Subject: [PATCH 194/226] ART: Factor out stack dumping with monitor objects Factor out generic stack walking with monitor object visitation into MonitorObjectsStackVisitor. Rewrite StackDumpVisitor in terms of this, removing Monitor::DescribeWait on the way. Bug: 70538431 Test: m test-art-host Test: manual (SIGQUIT dumping) Change-Id: I6f359f9a5f665f308328ad466bc7a437f52aea24 --- runtime/monitor.cc | 59 --------- runtime/monitor.h | 3 - runtime/monitor_objects_stack_visitor.h | 159 ++++++++++++++++++++++++ runtime/thread.cc | 147 +++++++++++++--------- 4 files changed, 246 insertions(+), 122 deletions(-) create mode 100644 runtime/monitor_objects_stack_visitor.h diff --git a/runtime/monitor.cc b/runtime/monitor.cc index ba18f8ddd4..cfef9c7d96 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -1289,65 +1289,6 @@ uint32_t Monitor::GetLockOwnerThreadId(mirror::Object* obj) { } } -void Monitor::DescribeWait(std::ostream& os, const Thread* thread) { - // Determine the wait message and object we're waiting or blocked upon. - mirror::Object* pretty_object; - uint32_t lock_owner; - ThreadState state = FetchState(thread, &pretty_object, &lock_owner); - - const char* wait_message = nullptr; - switch (state) { - case kWaiting: - case kTimedWaiting: - wait_message = " - waiting on "; - break; - - case kSleeping: - wait_message = " - sleeping on "; - break; - - case kBlocked: - wait_message = " - waiting to lock "; - break; - - case kWaitingForLockInflation: - wait_message = " - waiting for lock inflation of "; - break; - - default: - break; - } - - if (wait_message == nullptr) { - return; - } - - if (pretty_object == nullptr) { - os << wait_message << "an unknown object"; - } else { - if ((pretty_object->GetLockWord(true).GetState() == LockWord::kThinLocked) && - Locks::mutator_lock_->IsExclusiveHeld(Thread::Current())) { - // Getting the identity hashcode here would result in lock inflation and suspension of the - // current thread, which isn't safe if this is the only runnable thread. - os << wait_message << StringPrintf("<@addr=0x%" PRIxPTR "> (a %s)", - reinterpret_cast(pretty_object), - pretty_object->PrettyTypeOf().c_str()); - } else { - // - waiting on <0x6008c468> (a java.lang.Class) - // Call PrettyTypeOf before IdentityHashCode since IdentityHashCode can cause thread - // suspension and move pretty_object. - const std::string pretty_type(pretty_object->PrettyTypeOf()); - os << wait_message << StringPrintf("<0x%08x> (a %s)", pretty_object->IdentityHashCode(), - pretty_type.c_str()); - } - } - // - waiting to lock <0x613f83d8> (a java.lang.Object) held by thread 5 - if (lock_owner != ThreadList::kInvalidThreadId) { - os << " held by thread " << lock_owner; - } - os << "\n"; -} - ThreadState Monitor::FetchState(const Thread* thread, /* out */ mirror::Object** monitor_object, /* out */ uint32_t* lock_owner_tid) { diff --git a/runtime/monitor.h b/runtime/monitor.h index 30f47d849f..f150a8c091 100644 --- a/runtime/monitor.h +++ b/runtime/monitor.h @@ -94,9 +94,6 @@ class Monitor { bool interruptShouldThrow, ThreadState why) REQUIRES_SHARED(Locks::mutator_lock_) NO_THREAD_SAFETY_ANALYSIS; - static void DescribeWait(std::ostream& os, const Thread* thread) - REQUIRES(!Locks::thread_suspend_count_lock_) - REQUIRES_SHARED(Locks::mutator_lock_); static ThreadState FetchState(const Thread* thread, /* out */ mirror::Object** monitor_object, /* out */ uint32_t* lock_owner_tid) diff --git a/runtime/monitor_objects_stack_visitor.h b/runtime/monitor_objects_stack_visitor.h new file mode 100644 index 0000000000..5c962c3b26 --- /dev/null +++ b/runtime/monitor_objects_stack_visitor.h @@ -0,0 +1,159 @@ +/* + * 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_MONITOR_OBJECTS_STACK_VISITOR_H_ +#define ART_RUNTIME_MONITOR_OBJECTS_STACK_VISITOR_H_ + +#include + +#include "art_method.h" +#include "base/mutex.h" +#include "monitor.h" +#include "stack.h" +#include "thread.h" +#include "thread_state.h" + +namespace art { + +namespace mirror { +class Object; +} + +class Context; + +class MonitorObjectsStackVisitor : public StackVisitor { + public: + MonitorObjectsStackVisitor(Thread* thread_in, + Context* context, + bool check_suspended = true, + bool dump_locks_in = true) + REQUIRES_SHARED(Locks::mutator_lock_) + : StackVisitor(thread_in, + context, + StackVisitor::StackWalkKind::kIncludeInlinedFrames, + check_suspended), + frame_count(0u), + dump_locks(dump_locks_in) {} + + enum class VisitMethodResult { + kContinueMethod, + kSkipMethod, + kEndStackWalk, + }; + + bool VisitFrame() FINAL REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* m = GetMethod(); + if (m->IsRuntimeMethod()) { + return true; + } + + VisitMethodResult vmrEntry = StartMethod(m, frame_count); + switch (vmrEntry) { + case VisitMethodResult::kContinueMethod: + break; + case VisitMethodResult::kSkipMethod: + return true; + case VisitMethodResult::kEndStackWalk: + return false; + } + + if (frame_count == 0) { + // Top frame, check for blocked state. + + mirror::Object* monitor_object; + uint32_t lock_owner_tid; + ThreadState state = Monitor::FetchState(GetThread(), + &monitor_object, + &lock_owner_tid); + switch (state) { + case kWaiting: + case kTimedWaiting: + VisitWaitingObject(monitor_object, state); + break; + case kSleeping: + VisitSleepingObject(monitor_object); + break; + + case kBlocked: + case kWaitingForLockInflation: + VisitBlockedOnObject(monitor_object, state, lock_owner_tid); + break; + + default: + break; + } + } + + if (dump_locks) { + // Visit locks, but do not abort on errors. This could trigger a nested abort. + // Skip visiting locks if dump_locks is false as it would cause a bad_mutexes_held in + // RegTypeCache::RegTypeCache due to thread_list_lock. + Monitor::VisitLocks(this, VisitLockedObject, this, false); + } + + ++frame_count; + + VisitMethodResult vmrExit = EndMethod(m); + switch (vmrExit) { + case VisitMethodResult::kContinueMethod: + case VisitMethodResult::kSkipMethod: + return true; + + case VisitMethodResult::kEndStackWalk: + return false; + } + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); + } + + protected: + virtual VisitMethodResult StartMethod(ArtMethod* m, size_t frame_nr) + REQUIRES_SHARED(Locks::mutator_lock_) = 0; + virtual VisitMethodResult EndMethod(ArtMethod* m) + REQUIRES_SHARED(Locks::mutator_lock_) = 0; + + virtual void VisitWaitingObject(mirror::Object* obj, ThreadState state) + REQUIRES_SHARED(Locks::mutator_lock_) = 0; + virtual void VisitSleepingObject(mirror::Object* obj) + REQUIRES_SHARED(Locks::mutator_lock_) = 0; + virtual void VisitBlockedOnObject(mirror::Object* obj, ThreadState state, uint32_t owner_tid) + REQUIRES_SHARED(Locks::mutator_lock_) = 0; + virtual void VisitLockedObject(mirror::Object* obj) + REQUIRES_SHARED(Locks::mutator_lock_) = 0; + + size_t frame_count; + + private: + static void VisitLockedObject(mirror::Object* o, void* context) + REQUIRES_SHARED(Locks::mutator_lock_) { + MonitorObjectsStackVisitor* self = reinterpret_cast(context); + if (o != nullptr) { + if (kUseReadBarrier && Thread::Current()->GetIsGcMarking()) { + // We may call Thread::Dump() in the middle of the CC thread flip and this thread's stack + // may have not been flipped yet and "o" may be a from-space (stale) ref, in which case the + // IdentityHashCode call below will crash. So explicitly mark/forward it here. + o = ReadBarrier::Mark(o); + } + } + self->VisitLockedObject(o); + } + + const bool dump_locks; +}; + +} // namespace art + +#endif // ART_RUNTIME_MONITOR_OBJECTS_STACK_VISITOR_H_ diff --git a/runtime/thread.cc b/runtime/thread.cc index 21b8ea51e5..681210567b 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -70,6 +70,7 @@ #include "mirror/object_array-inl.h" #include "mirror/stack_trace_element.h" #include "monitor.h" +#include "monitor_objects_stack_visitor.h" #include "native_stack_dump.h" #include "nativehelper/scoped_local_ref.h" #include "nativehelper/scoped_utf_chars.h" @@ -1756,25 +1757,22 @@ void Thread::DumpState(std::ostream& os) const { Thread::DumpState(os, this, GetTid()); } -struct StackDumpVisitor : public StackVisitor { +struct StackDumpVisitor : public MonitorObjectsStackVisitor { StackDumpVisitor(std::ostream& os_in, Thread* thread_in, Context* context, - bool can_allocate_in, + bool can_allocate, bool check_suspended = true, - bool dump_locks_in = true) + bool dump_locks = true) REQUIRES_SHARED(Locks::mutator_lock_) - : StackVisitor(thread_in, - context, - StackVisitor::StackWalkKind::kIncludeInlinedFrames, - check_suspended), + : MonitorObjectsStackVisitor(thread_in, + context, + check_suspended, + can_allocate && dump_locks), os(os_in), - can_allocate(can_allocate_in), last_method(nullptr), last_line_number(0), - repetition_count(0), - frame_count(0), - dump_locks(dump_locks_in) {} + repetition_count(0) {} virtual ~StackDumpVisitor() { if (frame_count == 0) { @@ -1782,13 +1780,12 @@ struct StackDumpVisitor : public StackVisitor { } } - bool VisitFrame() REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* m = GetMethod(); - if (m->IsRuntimeMethod()) { - return true; - } + static constexpr size_t kMaxRepetition = 3u; + + VisitMethodResult StartMethod(ArtMethod* m, size_t frame_nr ATTRIBUTE_UNUSED) + OVERRIDE + REQUIRES_SHARED(Locks::mutator_lock_) { m = m->GetInterfaceMethodIfProxy(kRuntimePointerSize); - const int kMaxRepetition = 3; ObjPtr c = m->GetDeclaringClass(); ObjPtr dex_cache = c->GetDexCache(); int line_number = -1; @@ -1806,67 +1803,97 @@ struct StackDumpVisitor : public StackVisitor { last_line_number = line_number; last_method = m; } - if (repetition_count < kMaxRepetition) { - os << " at " << m->PrettyMethod(false); - if (m->IsNative()) { - os << "(Native method)"; - } else { - const char* source_file(m->GetDeclaringClassSourceFile()); - os << "(" << (source_file != nullptr ? source_file : "unavailable") - << ":" << line_number << ")"; - } - os << "\n"; - if (frame_count == 0) { - Monitor::DescribeWait(os, GetThread()); - } - if (can_allocate && dump_locks) { - // Visit locks, but do not abort on errors. This would trigger a nested abort. - // Skip visiting locks if dump_locks is false as it would cause a bad_mutexes_held in - // RegTypeCache::RegTypeCache due to thread_list_lock. - Monitor::VisitLocks(this, DumpLockedObject, &os, false); - } + + if (repetition_count >= kMaxRepetition) { + // Skip visiting=printing anything. + return VisitMethodResult::kSkipMethod; } - ++frame_count; - return true; + os << " at " << m->PrettyMethod(false); + if (m->IsNative()) { + os << "(Native method)"; + } else { + const char* source_file(m->GetDeclaringClassSourceFile()); + os << "(" << (source_file != nullptr ? source_file : "unavailable") + << ":" << line_number << ")"; + } + os << "\n"; + // Go and visit locks. + return VisitMethodResult::kContinueMethod; } - static void DumpLockedObject(mirror::Object* o, void* context) + VisitMethodResult EndMethod(ArtMethod* m ATTRIBUTE_UNUSED) OVERRIDE { + return VisitMethodResult::kContinueMethod; + } + + void VisitWaitingObject(mirror::Object* obj, ThreadState state ATTRIBUTE_UNUSED) + OVERRIDE + REQUIRES_SHARED(Locks::mutator_lock_) { + PrintObject(obj, " - waiting on ", ThreadList::kInvalidThreadId); + } + void VisitSleepingObject(mirror::Object* obj) + OVERRIDE + REQUIRES_SHARED(Locks::mutator_lock_) { + PrintObject(obj, " - sleeping on ", ThreadList::kInvalidThreadId); + } + void VisitBlockedOnObject(mirror::Object* obj, + ThreadState state, + uint32_t owner_tid) + OVERRIDE + REQUIRES_SHARED(Locks::mutator_lock_) { + const char* msg; + switch (state) { + case kBlocked: + msg = " - waiting to lock "; + break; + + case kWaitingForLockInflation: + msg = " - waiting for lock inflation of "; + break; + + default: + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); + } + PrintObject(obj, msg, owner_tid); + } + void VisitLockedObject(mirror::Object* obj) + OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { - std::ostream& os = *reinterpret_cast(context); - os << " - locked "; - if (o == nullptr) { - os << "an unknown object"; + PrintObject(obj, " - locked ", ThreadList::kInvalidThreadId); + } + + void PrintObject(mirror::Object* obj, + const char* msg, + uint32_t owner_tid) REQUIRES_SHARED(Locks::mutator_lock_) { + if (obj == nullptr) { + os << msg << "an unknown object"; } else { - if (kUseReadBarrier && Thread::Current()->GetIsGcMarking()) { - // We may call Thread::Dump() in the middle of the CC thread flip and this thread's stack - // may have not been flipped yet and "o" may be a from-space (stale) ref, in which case the - // IdentityHashCode call below will crash. So explicitly mark/forward it here. - o = ReadBarrier::Mark(o); - } - if ((o->GetLockWord(false).GetState() == LockWord::kThinLocked) && + if ((obj->GetLockWord(true).GetState() == LockWord::kThinLocked) && Locks::mutator_lock_->IsExclusiveHeld(Thread::Current())) { // Getting the identity hashcode here would result in lock inflation and suspension of the // current thread, which isn't safe if this is the only runnable thread. - os << StringPrintf("<@addr=0x%" PRIxPTR "> (a %s)", reinterpret_cast(o), - o->PrettyTypeOf().c_str()); + os << msg << StringPrintf("<@addr=0x%" PRIxPTR "> (a %s)", + reinterpret_cast(obj), + obj->PrettyTypeOf().c_str()); } else { - // IdentityHashCode can cause thread suspension, which would invalidate o if it moved. So - // we get the pretty type beofre we call IdentityHashCode. - const std::string pretty_type(o->PrettyTypeOf()); - os << StringPrintf("<0x%08x> (a %s)", o->IdentityHashCode(), pretty_type.c_str()); + // - waiting on <0x6008c468> (a java.lang.Class) + // Call PrettyTypeOf before IdentityHashCode since IdentityHashCode can cause thread + // suspension and move pretty_object. + const std::string pretty_type(obj->PrettyTypeOf()); + os << msg << StringPrintf("<0x%08x> (a %s)", obj->IdentityHashCode(), pretty_type.c_str()); } } + if (owner_tid != ThreadList::kInvalidThreadId) { + os << " held by thread " << owner_tid; + } os << "\n"; } std::ostream& os; - const bool can_allocate; ArtMethod* last_method; int last_line_number; - int repetition_count; - int frame_count; - const bool dump_locks; + size_t repetition_count; }; static bool ShouldShowNativeStack(const Thread* thread) -- GitLab From 2af7a3e6572254b733fa3d3699ddcf27ebc93866 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 14 Dec 2017 18:36:05 -0800 Subject: [PATCH 195/226] Log object info for CAS failure Log extra info to see if the whole object is corrupted or just the lock word. Bug: 70146596 Test: test-art-host Change-Id: I165bed0b85053282878aadb3d961683a217f4019 --- runtime/gc/collector/concurrent_copying.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index 7beff960cc..70685bcbf7 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -760,7 +760,8 @@ class ConcurrentCopying::ImmuneSpaceScanObjVisitor { // Done scanning the object, go back to white. bool success = obj->AtomicSetReadBarrierState(ReadBarrier::GrayState(), ReadBarrier::WhiteState()); - CHECK(success); + CHECK(success) + << Runtime::Current()->GetHeap()->GetVerification()->DumpObjectInfo(obj, "failed CAS"); } } else { collector_->ScanImmuneObject(obj); -- GitLab From 8a34abcb8a74ecda146af761b390de753092da36 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 13 Dec 2017 15:05:10 -0800 Subject: [PATCH 196/226] Move art-heap-poisoning and art-gtest-ss-gc to test cdex Get some extra automated testing coverage. Bug: 63756964 Test: test/testrunner/run_build_test_target.py art-heap-poisoning Test: test/testrunner/run_build_test_target.py art-gtest-ss-gc-tlab Change-Id: I32dac955135d433947109eb4300622c590bf9e1d --- test/testrunner/target_config.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index 6d21442045..297ce08bee 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -229,10 +229,13 @@ target_config = { }, 'art-heap-poisoning' : { 'run-test' : ['--interpreter', - '--optimizing'], + '--optimizing', + '--cdex-fast'], 'env' : { 'ART_USE_READ_BARRIER' : 'false', - 'ART_HEAP_POISONING' : 'true' + 'ART_HEAP_POISONING' : 'true', + # Get some extra automated testing coverage for compact dex. + 'ART_DEFAULT_COMPACT_DEX_LEVEL' : 'fast' } }, 'art-preopt' : { @@ -276,7 +279,9 @@ target_config = { 'make' : 'test-art-host-gtest', 'env': { 'ART_DEFAULT_GC_TYPE' : 'SS', - 'ART_USE_READ_BARRIER' : 'false' + 'ART_USE_READ_BARRIER' : 'false', + # Get some extra automated testing coverage for compact dex. + 'ART_DEFAULT_COMPACT_DEX_LEVEL' : 'fast' } }, 'art-gtest-gss-gc': { -- GitLab From 9ff1251fed35356bf74dc875ae9b0cea9e50a3b0 Mon Sep 17 00:00:00 2001 From: Kazuhiro Inaba Date: Mon, 4 Dec 2017 16:26:38 +0900 Subject: [PATCH 197/226] ART: Null thread_name check for loggings in rare occasions. Passing null char* to ostream::operator<< is undefined and can cause a crash (for instance when libc++ is used) instead of printing the log. Bug: None (crbug.com/531282) Test: None Change-Id: Ieb1f0fc50723b06e72b66b1da7b6abe58d5b9a02 --- runtime/thread.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/runtime/thread.cc b/runtime/thread.cc index 681210567b..b86e56b531 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -774,14 +774,16 @@ template Thread* Thread::Attach(const char* thread_name, bool as_daemon, PeerAction peer_action) { Runtime* runtime = Runtime::Current(); if (runtime == nullptr) { - LOG(ERROR) << "Thread attaching to non-existent runtime: " << thread_name; + LOG(ERROR) << "Thread attaching to non-existent runtime: " << + ((thread_name != nullptr) ? thread_name : "(Unnamed)"); return nullptr; } Thread* self; { MutexLock mu(nullptr, *Locks::runtime_shutdown_lock_); if (runtime->IsShuttingDownLocked()) { - LOG(WARNING) << "Thread attaching while runtime is shutting down: " << thread_name; + LOG(WARNING) << "Thread attaching while runtime is shutting down: " << + ((thread_name != nullptr) ? thread_name : "(Unnamed)"); return nullptr; } else { Runtime::Current()->StartThreadBirth(); -- GitLab From 2c4b084bf93ddfea6b60d05ff82c44dab9de9f28 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 13 Dec 2017 11:49:51 -0800 Subject: [PATCH 198/226] Fix cdex bugs to enable ART_DEFAULT_COMPACT_DEX_LEVEL=fast tests passing Compute dex checksum for compact dex generation. Handle input vdex by not aborting in oat_writer, instead just avoid generating compact dex for the input vdex case. Re-enabled some compact dex tests. Bug: 63756964 Test: ART_DEFAULT_COMPACT_DEX_LEVEL=fast test-art-host Change-Id: Ic9b4e4e59e6cd22b66ee2fc0d32c9b4a15f13497 --- dex2oat/dex2oat.cc | 11 ++++++++--- dex2oat/dex2oat_test.cc | 2 ++ dex2oat/linker/oat_writer.cc | 6 ++++-- dexlayout/dex_writer.cc | 6 ++++++ dexlayout/dexlayout.cc | 6 +----- dexlayout/dexlayout.h | 9 +++++++++ runtime/common_runtime_test.h | 6 ++++++ runtime/dex_file.cc | 8 ++++++-- runtime/dex_file.h | 1 + test/knownfailures.json | 6 +++--- 10 files changed, 46 insertions(+), 15 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 7cb04f2aa8..1574cd77f4 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -58,6 +58,7 @@ #include "compiler_callbacks.h" #include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" +#include "dexlayout.h" #include "dex/quick_compiler_callbacks.h" #include "dex/verification_results.h" #include "dex2oat_options.h" @@ -2233,12 +2234,16 @@ class Dex2Oat FINAL { return DoProfileGuidedOptimizations(); } - bool DoEagerUnquickeningOfVdex() const { - // DexLayout can invalidate the vdex metadata, so we need to unquicken - // the vdex file eagerly, before passing it to dexlayout. + bool MayInvalidateVdexMetadata() const { + // DexLayout can invalidate the vdex metadata if changing the class def order is enabled, so + // we need to unquicken the vdex file eagerly, before passing it to dexlayout. return DoDexLayoutOptimizations(); } + bool DoEagerUnquickeningOfVdex() const { + return MayInvalidateVdexMetadata(); + } + bool LoadProfile() { DCHECK(UseProfile()); // TODO(calin): We should be using the runtime arena pool (instead of the diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 8805aa1d14..7e90e51c73 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -875,6 +875,8 @@ TEST_F(Dex2oatLayoutTest, TestLayoutAppImage) { } TEST_F(Dex2oatLayoutTest, TestVdexLayout) { + // Disabled until figure out running compact dex + DexLayout causes quickening errors. + TEST_DISABLED_FOR_COMPACT_DEX(); RunTestVDex(); } diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index d4fc59c6c2..d261679ac1 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -3322,8 +3322,9 @@ bool OatWriter::WriteDexFile(OutputStream* out, if (!SeekToDexFile(out, file, oat_dex_file)) { return false; } - if (profile_compilation_info_ != nullptr || - compact_dex_level_ != CompactDexLevel::kCompactDexLevelNone) { + // update_input_vdex disables compact dex and layout. + if (!update_input_vdex && (profile_compilation_info_ != nullptr || + compact_dex_level_ != CompactDexLevel::kCompactDexLevelNone)) { CHECK(!update_input_vdex) << "We should never update the input vdex when doing dexlayout"; if (!LayoutAndWriteDexFile(out, oat_dex_file)) { return false; @@ -3441,6 +3442,7 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil Options options; options.output_to_memmap_ = true; options.compact_dex_level_ = compact_dex_level_; + options.update_checksum_ = true; DexLayout dex_layout(options, profile_compilation_info_, nullptr); dex_layout.ProcessDexFile(location.c_str(), dex_file.get(), 0); std::unique_ptr mem_map(dex_layout.GetAndReleaseMemMap()); diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc index 1fac2359b0..4fd4157391 100644 --- a/dexlayout/dex_writer.cc +++ b/dexlayout/dex_writer.cc @@ -893,6 +893,12 @@ void DexWriter::WriteMemMap() { header_->SetFileSize(offset); } WriteHeader(); + + if (dex_layout_->GetOptions().update_checksum_) { + header_->SetChecksum(DexFile::CalculateChecksum(mem_map_->Begin(), offset)); + // Rewrite the header with the calculated checksum. + WriteHeader(); + } } void DexWriter::Output(dex_ir::Header* header, diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index 33155b686b..f87778a65b 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -53,10 +53,6 @@ namespace art { using android::base::StringPrintf; -// Setting this to false disables class def layout entirely, which is stronger than strictly -// necessary to ensure the partial order w.r.t. class derivation. TODO: Re-enable (b/68317550). -static constexpr bool kChangeClassDefOrder = false; - /* * Flags for use with createAccessFlagStr(). */ @@ -1594,7 +1590,7 @@ void DexLayout::LayoutClassDefsAndClassData(const DexFile* dex_file) { } CHECK_EQ(class_data_index, class_datas.size()); - if (kChangeClassDefOrder) { + if (DexLayout::kChangeClassDefOrder) { // This currently produces dex files that violate the spec since the super class class_def is // supposed to occur before any subclasses. dex_ir::CollectionVector::Vector& class_defs = diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h index 8a277b7afe..958251e8ac 100644 --- a/dexlayout/dexlayout.h +++ b/dexlayout/dexlayout.h @@ -63,6 +63,7 @@ class Options { bool verbose_ = false; bool verify_output_ = kIsDebugBuild; bool visualize_pattern_ = false; + bool update_checksum_ = false; CompactDexLevel compact_dex_level_ = CompactDexLevel::kCompactDexLevelNone; OutputFormat output_format_ = kOutputPlain; const char* output_dex_directory_ = nullptr; @@ -79,6 +80,10 @@ class DexLayoutHotnessInfo { class DexLayout { public: + // Setting this to false disables class def layout entirely, which is stronger than strictly + // necessary to ensure the partial order w.r.t. class derivation. TODO: Re-enable (b/68317550). + static constexpr bool kChangeClassDefOrder = false; + DexLayout(Options& options, ProfileCompilationInfo* info, FILE* out_file, @@ -102,6 +107,10 @@ class DexLayout { return layout_hotness_info_; } + const Options& GetOptions() const { + return options_; + } + private: void DumpAnnotationSetItem(dex_ir::AnnotationSetItem* set_item); void DumpBytecodes(uint32_t idx, const dex_ir::CodeItem* code, uint32_t code_offset); diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 0f931e3dff..625884d98d 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -26,6 +26,7 @@ #include "arch/instruction_set.h" #include "base/mutex.h" +#include "cdex/compact_dex_level.h" #include "globals.h" // TODO: Add inl file and avoid including inl. #include "obj_ptr-inl.h" @@ -305,6 +306,11 @@ class CheckJniAbortCatcher { return; \ } +#define TEST_DISABLED_FOR_COMPACT_DEX() \ + if (kDefaultCompactDexLevel != CompactDexLevel::kCompactDexLevelNone) { \ + printf("WARNING: TEST DISABLED FOR COMPACT DEX\n"); \ + return; \ + } } // namespace art #endif // ART_RUNTIME_COMMON_RUNTIME_TEST_H_ diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 2d02415ba4..4aed4026d2 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -46,9 +46,13 @@ static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wro static_assert(std::is_trivially_copyable::value, "TypeIndex not trivial"); uint32_t DexFile::CalculateChecksum() const { + return CalculateChecksum(Begin(), Size()); +} + +uint32_t DexFile::CalculateChecksum(const uint8_t* begin, size_t size) { const uint32_t non_sum = OFFSETOF_MEMBER(DexFile::Header, signature_); - const uint8_t* non_sum_ptr = Begin() + non_sum; - return adler32(adler32(0L, Z_NULL, 0), non_sum_ptr, Size() - non_sum); + const uint8_t* non_sum_ptr = begin + non_sum; + return adler32(adler32(0L, Z_NULL, 0), non_sum_ptr, size - non_sum); } int DexFile::GetPermissions() const { diff --git a/runtime/dex_file.h b/runtime/dex_file.h index de3af8a289..76110f2e5d 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -1017,6 +1017,7 @@ class DexFile { // Recalculates the checksum of the dex file. Does not use the current value in the header. uint32_t CalculateChecksum() const; + static uint32_t CalculateChecksum(const uint8_t* begin, size_t size); // Returns a human-readable form of the method at an index. std::string PrettyMethod(uint32_t method_idx, bool with_signature = true) const; diff --git a/test/knownfailures.json b/test/knownfailures.json index ae1830af5c..dc051d9515 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -383,7 +383,7 @@ "tests": ["629-vdex-speed", "634-vdex-duplicate"], "description": ["Profile driven dexlayout does not work with vdex or dex verifier."], - "variant": "speed-profile | cdex-fast" + "variant": "speed-profile" }, { "test_patterns": ["616-cha.*"], @@ -651,9 +651,9 @@ "description": ["Test is designed to only check --compiler-filter=speed"] }, { - "tests": ["628-vdex", "975-iface-private"], + "tests": ["975-iface-private"], "variant": "cdex-fast", - "description": ["CompactDex doesn't yet work with input-vdex or 975-iface private"] + "description": ["CompactDex doesn't yet work with 975-iface private"] } ] -- GitLab From e7de5ec3e4cd1d607b647d98ea64df105479b867 Mon Sep 17 00:00:00 2001 From: Goran Jakovljevic Date: Thu, 14 Dec 2017 10:25:20 +0100 Subject: [PATCH 199/226] MIPS: Support swaps between 128-bit locations Add support for swaps between two SIMDStackSlots, two VectorRegisters (extended FpuRegister) and between a SIMDStackSlot and a VectorRegister. This fixes test 623-checker-loop-regressions for MIPS64R6 and MIPS32R6. Test: ./testrunner.py --optimizing --target in QEMU (MIPS64R6) Test: ./testrunner.py --optimizing --target in QEMU (MIPS32R6) Change-Id: I36aa209f79790fb6c08b9a171f810769a6b40afc --- compiler/optimizing/code_generator_mips.cc | 47 ++++++++++++++---- compiler/optimizing/code_generator_mips.h | 1 + compiler/optimizing/code_generator_mips64.cc | 51 ++++++++++++++++---- compiler/optimizing/code_generator_mips64.h | 1 + runtime/arch/mips/registers_mips.h | 3 +- runtime/arch/mips64/registers_mips64.h | 3 +- 6 files changed, 84 insertions(+), 22 deletions(-) diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 1f6b214f11..9f4c2349e7 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -1095,17 +1095,23 @@ void ParallelMoveResolverMIPS::EmitSwap(size_t index) { __ Move(r2, r1); __ Move(r1, TMP); } else if (loc1.IsFpuRegister() && loc2.IsFpuRegister()) { - FRegister f1 = loc1.AsFpuRegister(); - FRegister f2 = loc2.AsFpuRegister(); - if (type == DataType::Type::kFloat32) { - __ MovS(FTMP, f2); - __ MovS(f2, f1); - __ MovS(f1, FTMP); + if (codegen_->GetGraph()->HasSIMD()) { + __ MoveV(static_cast(FTMP), VectorRegisterFrom(loc1)); + __ MoveV(VectorRegisterFrom(loc1), VectorRegisterFrom(loc2)); + __ MoveV(VectorRegisterFrom(loc2), static_cast(FTMP)); } else { - DCHECK_EQ(type, DataType::Type::kFloat64); - __ MovD(FTMP, f2); - __ MovD(f2, f1); - __ MovD(f1, FTMP); + FRegister f1 = loc1.AsFpuRegister(); + FRegister f2 = loc2.AsFpuRegister(); + if (type == DataType::Type::kFloat32) { + __ MovS(FTMP, f2); + __ MovS(f2, f1); + __ MovS(f1, FTMP); + } else { + DCHECK_EQ(type, DataType::Type::kFloat64); + __ MovD(FTMP, f2); + __ MovD(f2, f1); + __ MovD(f1, FTMP); + } } } else if ((loc1.IsRegister() && loc2.IsFpuRegister()) || (loc1.IsFpuRegister() && loc2.IsRegister())) { @@ -1152,6 +1158,8 @@ void ParallelMoveResolverMIPS::EmitSwap(size_t index) { Exchange(loc1.GetStackIndex(), loc2.GetStackIndex(), /* double_slot */ false); } else if (loc1.IsDoubleStackSlot() && loc2.IsDoubleStackSlot()) { Exchange(loc1.GetStackIndex(), loc2.GetStackIndex(), /* double_slot */ true); + } else if (loc1.IsSIMDStackSlot() && loc2.IsSIMDStackSlot()) { + ExchangeQuadSlots(loc1.GetStackIndex(), loc2.GetStackIndex()); } else if ((loc1.IsRegister() && loc2.IsStackSlot()) || (loc1.IsStackSlot() && loc2.IsRegister())) { Register reg = loc1.IsRegister() ? loc1.AsRegister() : loc2.AsRegister(); @@ -1174,6 +1182,13 @@ void ParallelMoveResolverMIPS::EmitSwap(size_t index) { __ Move(TMP, reg_h); __ LoadFromOffset(kLoadWord, reg_h, SP, offset_h); __ StoreToOffset(kStoreWord, TMP, SP, offset_h); + } else if ((loc1.IsFpuRegister() && loc2.IsSIMDStackSlot()) || + (loc1.IsSIMDStackSlot() && loc2.IsFpuRegister())) { + Location fp_loc = loc1.IsFpuRegister() ? loc1 : loc2; + intptr_t offset = loc1.IsFpuRegister() ? loc2.GetStackIndex() : loc1.GetStackIndex(); + __ MoveV(static_cast(FTMP), VectorRegisterFrom(fp_loc)); + __ LoadQFromOffset(fp_loc.AsFpuRegister(), SP, offset); + __ StoreQToOffset(FTMP, SP, offset); } else if (loc1.IsFpuRegister() || loc2.IsFpuRegister()) { FRegister reg = loc1.IsFpuRegister() ? loc1.AsFpuRegister() : loc2.AsFpuRegister(); @@ -1225,6 +1240,13 @@ void ParallelMoveResolverMIPS::Exchange(int index1, int index2, bool double_slot } } +void ParallelMoveResolverMIPS::ExchangeQuadSlots(int index1, int index2) { + __ LoadQFromOffset(FTMP, SP, index1); + __ LoadQFromOffset(FTMP2, SP, index2); + __ StoreQToOffset(FTMP, SP, index2); + __ StoreQToOffset(FTMP2, SP, index1); +} + void CodeGeneratorMIPS::ComputeSpillMask() { core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_; fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_; @@ -1790,6 +1812,11 @@ void CodeGeneratorMIPS::SetupBlockedRegisters() const { blocked_core_registers_[TMP] = true; blocked_fpu_registers_[FTMP] = true; + if (GetInstructionSetFeatures().HasMsa()) { + // To be used just for MSA instructions. + blocked_fpu_registers_[FTMP2] = true; + } + // Reserve suspend and thread registers. blocked_core_registers_[S0] = true; blocked_core_registers_[TR] = true; diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 7845e312cb..cf8e7a373a 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -145,6 +145,7 @@ class ParallelMoveResolverMIPS : public ParallelMoveResolverWithSwap { void RestoreScratch(int reg) OVERRIDE; void Exchange(int index1, int index2, bool double_slot); + void ExchangeQuadSlots(int index1, int index2); MipsAssembler* GetAssembler() const; diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 22989c8283..eb64f1be23 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1061,6 +1061,13 @@ void ParallelMoveResolverMIPS64::Exchange(int index1, int index2, bool double_sl __ StoreToOffset(store_type, TMP, SP, index1 + stack_offset); } +void ParallelMoveResolverMIPS64::ExchangeQuadSlots(int index1, int index2) { + __ LoadFpuFromOffset(kLoadQuadword, FTMP, SP, index1); + __ LoadFpuFromOffset(kLoadQuadword, FTMP2, SP, index2); + __ StoreFpuToOffset(kStoreQuadword, FTMP, SP, index2); + __ StoreFpuToOffset(kStoreQuadword, FTMP2, SP, index1); +} + static dwarf::Reg DWARFReg(GpuRegister reg) { return dwarf::Reg::Mips64Core(static_cast(reg)); } @@ -1370,6 +1377,8 @@ void CodeGeneratorMIPS64::SwapLocations(Location loc1, Location loc2, DataType:: bool is_slot1 = loc1.IsStackSlot() || loc1.IsDoubleStackSlot(); bool is_slot2 = loc2.IsStackSlot() || loc2.IsDoubleStackSlot(); + bool is_simd1 = loc1.IsSIMDStackSlot(); + bool is_simd2 = loc2.IsSIMDStackSlot(); bool is_fp_reg1 = loc1.IsFpuRegister(); bool is_fp_reg2 = loc2.IsFpuRegister(); @@ -1382,17 +1391,23 @@ void CodeGeneratorMIPS64::SwapLocations(Location loc1, Location loc2, DataType:: __ Move(r1, TMP); } else if (is_fp_reg2 && is_fp_reg1) { // Swap 2 FPRs - FpuRegister r1 = loc1.AsFpuRegister(); - FpuRegister r2 = loc2.AsFpuRegister(); - if (type == DataType::Type::kFloat32) { - __ MovS(FTMP, r1); - __ MovS(r1, r2); - __ MovS(r2, FTMP); + if (GetGraph()->HasSIMD()) { + __ MoveV(static_cast(FTMP), VectorRegisterFrom(loc1)); + __ MoveV(VectorRegisterFrom(loc1), VectorRegisterFrom(loc2)); + __ MoveV(VectorRegisterFrom(loc2), static_cast(FTMP)); } else { - DCHECK_EQ(type, DataType::Type::kFloat64); - __ MovD(FTMP, r1); - __ MovD(r1, r2); - __ MovD(r2, FTMP); + FpuRegister r1 = loc1.AsFpuRegister(); + FpuRegister r2 = loc2.AsFpuRegister(); + if (type == DataType::Type::kFloat32) { + __ MovS(FTMP, r1); + __ MovS(r1, r2); + __ MovS(r2, FTMP); + } else { + DCHECK_EQ(type, DataType::Type::kFloat64); + __ MovD(FTMP, r1); + __ MovD(r1, r2); + __ MovD(r2, FTMP); + } } } else if (is_slot1 != is_slot2) { // Swap GPR/FPR and stack slot @@ -1421,6 +1436,17 @@ void CodeGeneratorMIPS64::SwapLocations(Location loc1, Location loc2, DataType:: move_resolver_.Exchange(loc1.GetStackIndex(), loc2.GetStackIndex(), loc1.IsDoubleStackSlot()); + } else if (is_simd1 && is_simd2) { + move_resolver_.ExchangeQuadSlots(loc1.GetStackIndex(), loc2.GetStackIndex()); + } else if ((is_fp_reg1 && is_simd2) || (is_fp_reg2 && is_simd1)) { + Location fp_reg_loc = is_fp_reg1 ? loc1 : loc2; + Location mem_loc = is_fp_reg1 ? loc2 : loc1; + __ LoadFpuFromOffset(kLoadQuadword, FTMP, SP, mem_loc.GetStackIndex()); + __ StoreFpuToOffset(kStoreQuadword, + fp_reg_loc.AsFpuRegister(), + SP, + mem_loc.GetStackIndex()); + __ MoveV(VectorRegisterFrom(fp_reg_loc), static_cast(FTMP)); } else { LOG(FATAL) << "Unimplemented swap between locations " << loc1 << " and " << loc2; } @@ -1653,6 +1679,11 @@ void CodeGeneratorMIPS64::SetupBlockedRegisters() const { blocked_core_registers_[TMP2] = true; blocked_fpu_registers_[FTMP] = true; + if (GetInstructionSetFeatures().HasMsa()) { + // To be used just for MSA instructions. + blocked_fpu_registers_[FTMP2] = true; + } + // Reserve suspend and thread registers. blocked_core_registers_[S0] = true; blocked_core_registers_[TR] = true; diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 2a95b3775d..d479410f07 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -142,6 +142,7 @@ class ParallelMoveResolverMIPS64 : public ParallelMoveResolverWithSwap { void RestoreScratch(int reg) OVERRIDE; void Exchange(int index1, int index2, bool double_slot); + void ExchangeQuadSlots(int index1, int index2); Mips64Assembler* GetAssembler() const; diff --git a/runtime/arch/mips/registers_mips.h b/runtime/arch/mips/registers_mips.h index f500b5881a..c7f9a3e74e 100644 --- a/runtime/arch/mips/registers_mips.h +++ b/runtime/arch/mips/registers_mips.h @@ -101,7 +101,8 @@ enum FRegister { F29 = 29, F30 = 30, F31 = 31, - FTMP = F6, // scratch register + FTMP = F6, // scratch register + FTMP2 = F7, // scratch register (in addition to FTMP, reserved for MSA instructions) kNumberOfFRegisters = 32, kNoFRegister = -1, }; diff --git a/runtime/arch/mips64/registers_mips64.h b/runtime/arch/mips64/registers_mips64.h index bca260a6a4..d3a24b6202 100644 --- a/runtime/arch/mips64/registers_mips64.h +++ b/runtime/arch/mips64/registers_mips64.h @@ -102,7 +102,8 @@ enum FpuRegister { F29 = 29, F30 = 30, F31 = 31, - FTMP = F8, // scratch register + FTMP = F8, // scratch register + FTMP2 = F9, // scratch register (in addition to FTMP, reserved for MSA instructions) kNumberOfFpuRegisters = 32, kNoFpuRegister = -1, }; -- GitLab From bb68b222ac429c419b3b591e239fc43c9c3f5b83 Mon Sep 17 00:00:00 2001 From: Tobias Thierer Date: Mon, 11 Dec 2017 22:29:17 +0000 Subject: [PATCH 200/226] Tweak art tests to decouple better from toolchain version. This CL changes some nested classes to be prebuilt rather than compiled each time the test is run so they don't depend on javac behavior, which changed between OpenJDK 8 and OpenJDK 9. This way, the tests pass when run on either OpenJDK 8 or 9. The class files were generated using OpenJDK 8 javac and converted to jasmin (.j) using ClassFileAnalyzer 0.7.0 [1] and Jasper 1.00 [2], respectively; license headers and comments were added to the generated .j files. [1] http://classfileanalyzer.javaseiten.de/ [2] http://www.angelfire.com/tx4/cus/jasper/ 031-class-attributes: Unlike OpenJDK 8 javac, OpenJDK 9 javac does not consider an anonymous class declared inside a static method to be static. This affects ClassAttr$1.j, which is now prebuilt. 085-old-style-inner-class: Main$1 and Main$2 relied on compilation with javac -source 1.4 -target 1.4, which OpenJDK 9 javac doesn't support. They are now prebuilt. The two classes are identical in all relevant aspects; a future CL could simplify the test by dropping one of them. Bug: 69449021 Test: The two tests touched by this CL pass in: EXPERIMENTAL_USE_OPENJDK9=1.8 \ art/test/testrunner/run_build_test_target.py -j40 art-interpreter Change-Id: I72ba88eb6325a641876b7143008dfb209a16047e --- .../jasmin/ClassAttrs$1.j | 49 +++++++++++++++++++ test/031-class-attributes/src/ClassAttrs.java | 11 +++-- test/085-old-style-inner-class/build | 33 ------------- .../085-old-style-inner-class/jasmin/Main$1.j | 39 +++++++++++++++ .../085-old-style-inner-class/jasmin/Main$2.j | 39 +++++++++++++++ test/085-old-style-inner-class/src/Main.java | 10 ++-- 6 files changed, 142 insertions(+), 39 deletions(-) create mode 100644 test/031-class-attributes/jasmin/ClassAttrs$1.j delete mode 100644 test/085-old-style-inner-class/build create mode 100644 test/085-old-style-inner-class/jasmin/Main$1.j create mode 100644 test/085-old-style-inner-class/jasmin/Main$2.j diff --git a/test/031-class-attributes/jasmin/ClassAttrs$1.j b/test/031-class-attributes/jasmin/ClassAttrs$1.j new file mode 100644 index 0000000000..ea767efdba --- /dev/null +++ b/test/031-class-attributes/jasmin/ClassAttrs$1.j @@ -0,0 +1,49 @@ +; 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. + +; (new OtherClass() { int i = 5; }).getClass() + +; ClassAttrs$1.j + +; Generated by ClassFileAnalyzer (Can) +; Analyzer and Disassembler for Java class files +; (Jasmin syntax 2, http://jasmin.sourceforge.net) +; +; ClassFileAnalyzer, version 0.7.0 + +.bytecode 52.0 +.source ClassAttrs.java +.class final ClassAttrs$1 +.super OtherClass +.enclosing method ClassAttrs/main()V +; OpenJDK javac versions <= 8 consider anonymous classes declared side +; static methods to be static (as is this one), whereas OpenJDK 9 javac +; does not. See http://b/62290080 +.inner class static inner ClassAttrs$1 ; + +.field i I + +.method ()V + .limit stack 2 + .limit locals 1 + .line 112 + 0: aload_0 + 1: invokespecial OtherClass/()V + 4: aload_0 + 5: iconst_5 + 6: putfield ClassAttrs$1/i I + 9: return +.end method + + diff --git a/test/031-class-attributes/src/ClassAttrs.java b/test/031-class-attributes/src/ClassAttrs.java index 8489a2c222..f55a34c5f2 100644 --- a/test/031-class-attributes/src/ClassAttrs.java +++ b/test/031-class-attributes/src/ClassAttrs.java @@ -107,9 +107,14 @@ public class ClassAttrs { inner.showMe(); ClassAttrs attrs = new ClassAttrs(); - - /* anonymous, not local, not member */ - printClassAttrs((new OtherClass() { int i = 5; }).getClass()); + try { + /* anonymous, not local, not member */ + printClassAttrs(Class.forName("ClassAttrs$1")); // ClassAttrs$1.j + } catch (ClassNotFoundException e) { + System.out.println("FAILED: " + e); + e.printStackTrace(System.out); + throw new AssertionError(e); + } /* member, not anonymous, not local */ printClassAttrs(MemberClass.class); diff --git a/test/085-old-style-inner-class/build b/test/085-old-style-inner-class/build deleted file mode 100644 index 21dc66269d..0000000000 --- a/test/085-old-style-inner-class/build +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2010 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Stop if something fails. -set -e - -# We compile for a 1.4 target to suppress the use of EnclosingMethod -# attributes. -mkdir classes -${JAVAC} -source 1.4 -target 1.4 -d classes `find src -name '*.java'` - -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - ${JACK} --import classes.jill.jar --output-dex . -else - # Suppress stderr to keep the inner class warnings out of the expected output. - ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes 2>/dev/null -fi - -zip $TEST_NAME.jar classes.dex diff --git a/test/085-old-style-inner-class/jasmin/Main$1.j b/test/085-old-style-inner-class/jasmin/Main$1.j new file mode 100644 index 0000000000..fde1ddea53 --- /dev/null +++ b/test/085-old-style-inner-class/jasmin/Main$1.j @@ -0,0 +1,39 @@ +; 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. + +.source Main.java +.class final Main$1 +.super java/lang/Object +.implements java/lang/Runnable + +; new Runnable() { +; public void run() { } +; }; + +.method ()V + .limit stack 1 + .limit locals 1 + .line 23 + aload_0 + invokespecial java/lang/Object/()V + return +.end method + +.method public run()V + .limit stack 0 + .limit locals 1 + .line 24 + return +.end method + diff --git a/test/085-old-style-inner-class/jasmin/Main$2.j b/test/085-old-style-inner-class/jasmin/Main$2.j new file mode 100644 index 0000000000..dedbe8682e --- /dev/null +++ b/test/085-old-style-inner-class/jasmin/Main$2.j @@ -0,0 +1,39 @@ +; 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. + +.source Main.java +.class final Main$2 +.super java/lang/Object +.implements java/lang/Runnable + +; new Runnable() { +; public void run() { } +; }; + +.method ()V + .limit stack 1 + .limit locals 1 + .line 28 + aload_0 + invokespecial java/lang/Object/()V + return +.end method + +.method public run()V + .limit stack 0 + .limit locals 1 + .line 29 + return +.end method + diff --git a/test/085-old-style-inner-class/src/Main.java b/test/085-old-style-inner-class/src/Main.java index c9a5b72dbd..831364cae1 100644 --- a/test/085-old-style-inner-class/src/Main.java +++ b/test/085-old-style-inner-class/src/Main.java @@ -20,15 +20,19 @@ import java.lang.reflect.Method; * Test reflection on old-style inner classes. */ public class Main { + /* + // Main$1.j private static Runnable theRunnable = new Runnable() { public void run() { } }; + // Main$1.2 private static Runnable create() { return new Runnable() { public void run() { } }; } + */ private static String nameOf(Class clazz) { return (clazz == null) ? "(null)" : clazz.getName(); @@ -48,8 +52,8 @@ public class Main { nameOf(clazz.getEnclosingMethod())); } - public static void main(String args[]) { - infoFor(theRunnable.getClass()); - infoFor(create().getClass()); + public static void main(String args[]) throws ClassNotFoundException { + infoFor(Class.forName("Main$1")); + infoFor(Class.forName("Main$2")); } } -- GitLab From ae915a0182db98769ed4851fab440e79e012babd Mon Sep 17 00:00:00 2001 From: Hans Boehm Date: Tue, 12 Dec 2017 11:05:32 -0800 Subject: [PATCH 201/226] Improve scoped spinlock implementations Both ScopedAllMutexesLock and ScopedExpectedMutexesOnWeakRefAccessLock really implement simple spinlocks. But they did it in an unorthodox way, with a CAS on unlock. I see no reason for that. Use the standard (and faster) idiom instead. The NanoSleep(100) waiting logic was probably suboptimal and definitely misleading. I timed NanoSleep(100) on Linux4.4 host, and it takes about 60 usecs, i.e. 60,000 nsecs. By comparison a no-op sched_yield takes about 1 usec. This replaces it with waiting logic that should be generically usable. This is no doubt overkill, but the hope is that we can eventually reuse this where it matters more. Test: Built and booted AOSP. Change-Id: I6e47508ecb8d5e5d0b4f08c8e8f073ad7b1d192e --- runtime/base/mutex.cc | 45 +++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 3adebe7e00..7324dff974 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -102,18 +102,40 @@ static bool ComputeRelativeTimeSpec(timespec* result_ts, const timespec& lhs, co } #endif +// Wait for an amount of time that roughly increases in the argument i. +// Spin for small arguments and yield/sleep for longer ones. +static void BackOff(uint32_t i) { + static constexpr uint32_t kSpinMax = 10; + static constexpr uint32_t kYieldMax = 20; + if (i <= kSpinMax) { + // TODO: Esp. in very latency-sensitive cases, consider replacing this with an explicit + // test-and-test-and-set loop in the caller. Possibly skip entirely on a uniprocessor. + volatile uint32_t x = 0; + const uint32_t spin_count = 10 * i; + for (uint32_t spin = 0; spin < spin_count; ++spin) { + ++x; // Volatile; hence should not be optimized away. + } + // TODO: Consider adding x86 PAUSE and/or ARM YIELD here. + } else if (i <= kYieldMax) { + sched_yield(); + } else { + NanoSleep(1000ull * (i - kYieldMax)); + } +} + class ScopedAllMutexesLock FINAL { public: explicit ScopedAllMutexesLock(const BaseMutex* mutex) : mutex_(mutex) { - while (!gAllMutexData->all_mutexes_guard.CompareExchangeWeakAcquire(0, mutex)) { - NanoSleep(100); + for (uint32_t i = 0; + !gAllMutexData->all_mutexes_guard.CompareExchangeWeakAcquire(0, mutex); + ++i) { + BackOff(i); } } ~ScopedAllMutexesLock() { - while (!gAllMutexData->all_mutexes_guard.CompareExchangeWeakRelease(mutex_, 0)) { - NanoSleep(100); - } + DCHECK_EQ(gAllMutexData->all_mutexes_guard.LoadRelaxed(), mutex_); + gAllMutexData->all_mutexes_guard.StoreRelease(0); } private: @@ -123,17 +145,16 @@ class ScopedAllMutexesLock FINAL { class Locks::ScopedExpectedMutexesOnWeakRefAccessLock FINAL { public: explicit ScopedExpectedMutexesOnWeakRefAccessLock(const BaseMutex* mutex) : mutex_(mutex) { - while (!Locks::expected_mutexes_on_weak_ref_access_guard_.CompareExchangeWeakAcquire(0, - mutex)) { - NanoSleep(100); + for (uint32_t i = 0; + !Locks::expected_mutexes_on_weak_ref_access_guard_.CompareExchangeWeakAcquire(0, mutex); + ++i) { + BackOff(i); } } ~ScopedExpectedMutexesOnWeakRefAccessLock() { - while (!Locks::expected_mutexes_on_weak_ref_access_guard_.CompareExchangeWeakRelease(mutex_, - 0)) { - NanoSleep(100); - } + DCHECK_EQ(Locks::expected_mutexes_on_weak_ref_access_guard_.LoadRelaxed(), mutex_); + Locks::expected_mutexes_on_weak_ref_access_guard_.StoreRelease(0); } private: -- GitLab From 437b54c730ab306208af5aa035b85c294d090024 Mon Sep 17 00:00:00 2001 From: Tobias Thierer Date: Fri, 15 Dec 2017 18:38:22 +0000 Subject: [PATCH 202/226] Work around a art test script bug mixing up bootclasspaths. art/test/testrunner/run_build_test_target.py -j40 art-interpreter compiles tests against the default toolchain (eg. OpenJDK) core libraries but runs them against libcore. This is a bug because the default toolchain API may differ from libcore's; specifically, before this CL, 070-nio-buffer breaks when the default toolchain is OpenJDK 9. This CL works around that bug specifically by avoiding use of OpenJDK9 covariant overrides that aren't in libcore. For example, IntBuffer#clear->IntBuffer does not exist (but Buffer#clear->Buffer does). After this CL, 070-nio-buffer passes when run under OpenJDK 9. This CL can be reverted when the test runner scripts are fixed in a future CL. Bug: 70521453 Test: export EXPERIMENTAL_USE_OPENJDK9=1.8 && \ art/test/testrunner/run_build_test_target.py -j40 art-interpreter Test: export EXPERIMENTAL_USE_OPENJDK9=false && \ art/test/testrunner/run_build_test_target.py -j40 art-interpreter Change-Id: I463716906f48d1ecf6b9892f2d793dd75f2619eb --- test/070-nio-buffer/src/Main.java | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/test/070-nio-buffer/src/Main.java b/test/070-nio-buffer/src/Main.java index a3eeb3fda6..86eb553594 100644 --- a/test/070-nio-buffer/src/Main.java +++ b/test/070-nio-buffer/src/Main.java @@ -14,6 +14,7 @@ * limitations under the License. */ +import java.nio.Buffer; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -50,9 +51,9 @@ public class Main { 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031 }; - shortBuf.position(0); + ((Buffer) shortBuf).position(0); shortBuf.put(myShorts, 0, 32); // should work - shortBuf.position(0); + ((Buffer) shortBuf).position(0); shortBuf.put(myShorts, 16, 16); // should work shortBuf.put(myShorts, 16, 16); // advance to end @@ -64,7 +65,7 @@ public class Main { } try { - shortBuf.position(0); + ((Buffer) shortBuf).position(0); shortBuf.put(myShorts, 0, 33); // should fail System.out.println("ERROR: out-of-bounds put succeeded\n"); } catch (IndexOutOfBoundsException ioobe) { @@ -72,7 +73,7 @@ public class Main { } try { - shortBuf.position(16); + ((Buffer) shortBuf).position(16); shortBuf.put(myShorts, 0, 17); // should fail System.out.println("ERROR: out-of-bounds put succeeded\n"); } catch (BufferOverflowException boe) { @@ -93,13 +94,13 @@ public class Main { int data[] = new int[25]; //FloatBuffer int1 = direct.asFloatBuffer(); //float data[] = new float[25]; - int1.clear (); - int1.put (data); - int1.position (0); + ((Buffer) int1).clear(); + int1.put(data); + ((Buffer) int1).position(0); - int1.clear (); + ((Buffer) int1).clear(); int1.put (data); - int1.position (0); + ((Buffer) int1).position(0); } /* @@ -119,7 +120,7 @@ public class Main { } static void storeValues(ByteBuffer directBuf) { - directBuf.position(0); + ((Buffer) directBuf).position(0); ShortBuffer shortBuf = directBuf.asShortBuffer(); CharBuffer charBuf = directBuf.asCharBuffer(); IntBuffer intBuf = directBuf.asIntBuffer(); @@ -157,7 +158,7 @@ public class Main { throw new RuntimeException("double get/store failed"); } - directBuf.position(0); + ((Buffer) directBuf).position(0); char[] outBuf = new char[directBuf.limit() * 2]; for (int i = 0; i < directBuf.limit(); i++) { byte b = directBuf.get(); -- GitLab From bd82d17b4d19f5dfab0916585fba30a4a69163d8 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Fri, 15 Dec 2017 11:31:57 -0800 Subject: [PATCH 203/226] Fixed typos in fuzzer scripts/readme. Test: fuzzer Change-Id: I937d8dac66a19b903d15702de51e9adfc1a8e160 --- tools/jfuzz/README.md | 2 +- tools/jfuzz/jfuzz.cc | 2 +- tools/jfuzz/run_jfuzz_test.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/jfuzz/README.md b/tools/jfuzz/README.md index 1991c97529..eb0e71f53d 100644 --- a/tools/jfuzz/README.md +++ b/tools/jfuzz/README.md @@ -98,7 +98,7 @@ where --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 - --debug_info : include debugging info + --debug_info : include debugging info Background ========== diff --git a/tools/jfuzz/jfuzz.cc b/tools/jfuzz/jfuzz.cc index a6034c8c3a..60c62752ef 100644 --- a/tools/jfuzz/jfuzz.cc +++ b/tools/jfuzz/jfuzz.cc @@ -1210,7 +1210,7 @@ class JFuzz { // Emit a static void method. void emitStaticNopMethod() { - fputs(" public static void nop() {}\n", out_); + fputs(" public static void nop() {}\n\n", out_); } // Emit program header. Emit command line options in the comments. diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py index 2b56767c86..34180d993f 100755 --- a/tools/jfuzz/run_jfuzz_test.py +++ b/tools/jfuzz/run_jfuzz_test.py @@ -551,7 +551,7 @@ class JFuzzTester(object): wrapped_args = ['--jfuzz_arg={0}'.format(opt) for opt in jfuzz_args] repro_cmd_str = (os.path.basename(__file__) + ' --num_tests=1 --dexer=' + self._dexer + - (' --debug_info' if self._debug_info else '') + + (' --debug_info ' if self._debug_info else ' ') + ' '.join(wrapped_args)) comment = 'jfuzz {0}\nReproduce test:\n{1}\nReproduce divergence:\n{2}\n'.format( jfuzz_ver, jfuzz_cmd_str, repro_cmd_str) -- GitLab From d1d5c95f5ae38ca194c251914da82371e0192b60 Mon Sep 17 00:00:00 2001 From: Alex Klyubin Date: Fri, 15 Dec 2017 12:57:33 -0800 Subject: [PATCH 204/226] Revert^4 "Add patchoat test" This reverts commit 1fb8d5ee6c7f03269cf08b18a0af5b4e25924a27. Skip the patchoat test on the build server where the build is not using read barriers. Read barriers (or CMS/MS garbage collector) are required for the --force-determinism feature used by the test. Test: ./art/test/testrunner/run_build_test_target.py art-gtest-debug-gc Test: make test-art-host-gtest-patchoat_test Test: make test-art-target-gtest-patchoat_test Bug: 66697305 Change-Id: I99da0111ca0e34d32473dfe721b7a2f49289e850 --- build/Android.gtest.mk | 10 ++ patchoat/Android.bp | 13 ++ patchoat/patchoat_test.cc | 352 ++++++++++++++++++++++++++++++++++++++ runtime/image.h | 4 + 4 files changed, 379 insertions(+) create mode 100644 patchoat/patchoat_test.cc diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 9af6cf46b4..0023cb9e91 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -139,6 +139,7 @@ ART_GTEST_image_space_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex ART_GTEST_oat_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 ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods ART_GTEST_profile_assistant_test_DEX_DEPS := ProfileTestMultiDex @@ -266,6 +267,11 @@ ART_GTEST_oatdump_test_TARGET_DEPS := \ 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_patchoat_test_HOST_DEPS := \ + $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) +ART_GTEST_patchoat_test_TARGET_DEPS := \ + $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) + # Profile assistant tests requires profman utility. ART_GTEST_profile_assistant_test_HOST_DEPS := profmand-host ART_GTEST_profile_assistant_test_TARGET_DEPS := profmand-target @@ -285,6 +291,7 @@ ART_TEST_MODULES := \ art_dexoptanalyzer_tests \ art_imgdiag_tests \ art_oatdump_tests \ + art_patchoat_tests \ art_profman_tests \ art_runtime_tests \ art_runtime_compiler_tests \ @@ -698,6 +705,9 @@ ART_GTEST_dex2oat_image_test_DEX_DEPS := ART_GTEST_dex2oat_image_test_HOST_DEPS := ART_GTEST_dex2oat_image_test_TARGET_DEPS := ART_GTEST_object_test_DEX_DEPS := +ART_GTEST_patchoat_test_DEX_DEPS := +ART_GTEST_patchoat_test_HOST_DEPS := +ART_GTEST_patchoat_test_TARGET_DEPS := ART_GTEST_proxy_test_DEX_DEPS := ART_GTEST_reflection_test_DEX_DEPS := ART_GTEST_stub_test_DEX_DEPS := diff --git a/patchoat/Android.bp b/patchoat/Android.bp index d3bc2a754b..0902823644 100644 --- a/patchoat/Android.bp +++ b/patchoat/Android.bp @@ -47,3 +47,16 @@ art_cc_binary { "libartd", ], } + +art_cc_test { + name: "art_patchoat_tests", + defaults: [ + "art_gtest_defaults", + ], + srcs: [ + "patchoat_test.cc", + ], + shared_libs: [ + "libartd", + ], +} diff --git a/patchoat/patchoat_test.cc b/patchoat/patchoat_test.cc new file mode 100644 index 0000000000..dc4c78b32f --- /dev/null +++ b/patchoat/patchoat_test.cc @@ -0,0 +1,352 @@ +/* + * 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. + */ + +#include +#include + +#include "android-base/stringprintf.h" +#include "android-base/strings.h" + +#include "dexopt_test.h" +#include "runtime.h" + +#include + +namespace art { + +using android::base::StringPrintf; + +class PatchoatTest : public DexoptTest { + public: + static void AddRuntimeArg(std::vector& args, const std::string& arg) { + args.push_back("--runtime-arg"); + args.push_back(arg); + } + + bool CompileBootImage(const std::vector& extra_args, + const std::string& image_file_name_prefix, + uint32_t base_addr, + std::string* error_msg) { + Runtime* const runtime = Runtime::Current(); + std::vector argv; + argv.push_back(runtime->GetCompilerExecutable()); + AddRuntimeArg(argv, "-Xms64m"); + AddRuntimeArg(argv, "-Xmx64m"); + std::vector dex_files = GetLibCoreDexFileNames(); + for (const std::string& dex_file : dex_files) { + argv.push_back("--dex-file=" + dex_file); + argv.push_back("--dex-location=" + dex_file); + } + if (runtime->IsJavaDebuggable()) { + argv.push_back("--debuggable"); + } + runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv); + + AddRuntimeArg(argv, "-Xverify:softfail"); + + if (!kIsTargetBuild) { + argv.push_back("--host"); + } + + argv.push_back("--image=" + image_file_name_prefix + ".art"); + argv.push_back("--oat-file=" + image_file_name_prefix + ".oat"); + argv.push_back("--oat-location=" + image_file_name_prefix + ".oat"); + argv.push_back(StringPrintf("--base=0x%" PRIx32, base_addr)); + argv.push_back("--compile-pic"); + argv.push_back("--multi-image"); + argv.push_back("--no-generate-debug-info"); + + std::vector compiler_options = runtime->GetCompilerOptions(); + argv.insert(argv.end(), compiler_options.begin(), compiler_options.end()); + + // We must set --android-root. + const char* android_root = getenv("ANDROID_ROOT"); + CHECK(android_root != nullptr); + argv.push_back("--android-root=" + std::string(android_root)); + argv.insert(argv.end(), extra_args.begin(), extra_args.end()); + + return RunDex2OatOrPatchoat(argv, error_msg); + } + + bool RelocateBootImage(const std::string& input_image_location, + const std::string& output_image_filename, + off_t base_offset_delta, + std::string* error_msg) { + Runtime* const runtime = Runtime::Current(); + std::vector argv; + argv.push_back(runtime->GetPatchoatExecutable()); + argv.push_back("--input-image-location=" + input_image_location); + argv.push_back("--output-image-file=" + output_image_filename); + argv.push_back(StringPrintf("--base-offset-delta=0x%jx", (intmax_t) base_offset_delta)); + argv.push_back(StringPrintf("--instruction-set=%s", GetInstructionSetString(kRuntimeISA))); + + return RunDex2OatOrPatchoat(argv, error_msg); + } + + bool RunDex2OatOrPatchoat(const std::vector& args, std::string* error_msg) { + int link[2]; + + if (pipe(link) == -1) { + return false; + } + + pid_t pid = fork(); + if (pid == -1) { + return false; + } + + if (pid == 0) { + // We need dex2oat to actually log things. + setenv("ANDROID_LOG_TAGS", "*:e", 1); + dup2(link[1], STDERR_FILENO); + close(link[0]); + close(link[1]); + std::vector c_args; + for (const std::string& str : args) { + c_args.push_back(str.c_str()); + } + c_args.push_back(nullptr); + execv(c_args[0], const_cast(c_args.data())); + exit(1); + UNREACHABLE(); + } else { + close(link[1]); + char buffer[128]; + memset(buffer, 0, 128); + ssize_t bytes_read = 0; + + while (TEMP_FAILURE_RETRY(bytes_read = read(link[0], buffer, 128)) > 0) { + *error_msg += std::string(buffer, bytes_read); + } + close(link[0]); + int status = -1; + if (waitpid(pid, &status, 0) != -1) { + return (status == 0); + } + return false; + } + } + + bool CompileBootImageToDir( + const std::string& output_dir, + const std::vector& dex2oat_extra_args, + uint32_t base_addr, + std::string* error_msg) { + return CompileBootImage(dex2oat_extra_args, output_dir + "/boot", base_addr, error_msg); + } + + bool CopyImageChecksumAndSetPatchDelta( + const std::string& src_image_filename, + const std::string& dest_image_filename, + off_t dest_patch_delta, + std::string* error_msg) { + std::unique_ptr src_file(OS::OpenFileForReading(src_image_filename.c_str())); + if (src_file.get() == nullptr) { + *error_msg = StringPrintf("Failed to open source image file %s", src_image_filename.c_str()); + return false; + } + ImageHeader src_header; + if (!src_file->ReadFully(&src_header, sizeof(src_header))) { + *error_msg = StringPrintf("Failed to read source image file %s", src_image_filename.c_str()); + return false; + } + + std::unique_ptr dest_file(OS::OpenFileReadWrite(dest_image_filename.c_str())); + if (dest_file.get() == nullptr) { + *error_msg = + StringPrintf("Failed to open destination image file %s", dest_image_filename.c_str()); + return false; + } + ImageHeader dest_header; + if (!dest_file->ReadFully(&dest_header, sizeof(dest_header))) { + *error_msg = + StringPrintf("Failed to read destination image file %s", dest_image_filename.c_str()); + return false; + } + dest_header.SetOatChecksum(src_header.GetOatChecksum()); + dest_header.SetPatchDelta(dest_patch_delta); + if (!dest_file->ResetOffset()) { + *error_msg = + StringPrintf( + "Failed to seek to start of destination image file %s", dest_image_filename.c_str()); + return false; + } + if (!dest_file->WriteFully(&dest_header, sizeof(dest_header))) { + *error_msg = + StringPrintf("Failed to write to destination image file %s", dest_image_filename.c_str()); + dest_file->Erase(); + return false; + } + if (dest_file->FlushCloseOrErase() != 0) { + *error_msg = + StringPrintf( + "Failed to flush/close destination image file %s", dest_image_filename.c_str()); + return false; + } + + return true; + } + + bool ReadFully( + const std::string& filename, std::vector* contents, std::string* error_msg) { + std::unique_ptr file(OS::OpenFileForReading(filename.c_str())); + if (file.get() == nullptr) { + *error_msg = "Failed to open"; + return false; + } + int64_t size = file->GetLength(); + if (size < 0) { + *error_msg = "Failed to get size"; + return false; + } + contents->resize(size); + if (!file->ReadFully(&(*contents)[0], size)) { + *error_msg = "Failed to read"; + contents->clear(); + return false; + } + return true; + } + + bool BinaryDiff( + const std::string& filename1, const std::string& filename2, std::string* error_msg) { + std::string read_error_msg; + std::vector image1; + if (!ReadFully(filename1, &image1, &read_error_msg)) { + *error_msg = StringPrintf("Failed to read %s: %s", filename1.c_str(), read_error_msg.c_str()); + return true; + } + std::vector image2; + if (!ReadFully(filename2, &image2, &read_error_msg)) { + *error_msg = StringPrintf("Failed to read %s: %s", filename2.c_str(), read_error_msg.c_str()); + return true; + } + if (image1.size() != image2.size()) { + *error_msg = + StringPrintf( + "%s and %s are of different size: %zu vs %zu", + filename1.c_str(), + filename2.c_str(), + image1.size(), + image2.size()); + return true; + } + size_t size = image1.size(); + for (size_t i = 0; i < size; i++) { + if (image1[i] != image2[i]) { + *error_msg = + StringPrintf("%s and %s differ at offset %zu", filename1.c_str(), filename2.c_str(), i); + return true; + } + } + + return false; + } +}; + +TEST_F(PatchoatTest, PatchoatRelocationSameAsDex2oatRelocation) { +#if defined(ART_USE_READ_BARRIER) + // This test checks that relocating a boot image using patchoat produces the same result as + // producing the boot image for that relocated base address using dex2oat. To be precise, these + // two files will have two small differences: the OAT checksum and base address. However, this + // test takes this into account. + + // 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; + // Force deterministic output. We want the boot images created by this dex2oat run and the run + // below to differ only in their base address. + std::vector dex2oat_extra_args; + dex2oat_extra_args.push_back("--force-determinism"); + dex2oat_extra_args.push_back("-j1"); // Might not be needed. Causes a 3-5x slowdown. + std::string error_msg; + if (!CompileBootImageToDir(dex2oat_orig_dir, dex2oat_extra_args, orig_base_addr, &error_msg)) { + FAIL() << "CompileBootImage1 failed: " << error_msg; + } + + // Compile a "relocated" boot image into a random directory using dex2oat. This image is relocated + // in the sense that it uses a different base address. + ScratchFile dex2oat_reloc_scratch; + dex2oat_reloc_scratch.Unlink(); + std::string dex2oat_reloc_dir = dex2oat_reloc_scratch.GetFilename(); + ASSERT_EQ(0, mkdir(dex2oat_reloc_dir.c_str(), 0700)); + const uint32_t reloc_base_addr = 0x70000000; + if (!CompileBootImageToDir(dex2oat_reloc_dir, dex2oat_extra_args, reloc_base_addr, &error_msg)) { + FAIL() << "CompileBootImage2 failed: " << error_msg; + } + const off_t base_addr_delta = reloc_base_addr - orig_base_addr; + + // Relocate the original boot image using patchoat. The image is relocated by the same amount + // as the second/relocated image produced by dex2oat. + ScratchFile patchoat_scratch; + patchoat_scratch.Unlink(); + std::string patchoat_dir = patchoat_scratch.GetFilename(); + ASSERT_EQ(0, mkdir(patchoat_dir.c_str(), 0700)); + 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())); + if (!RelocateBootImage( + dex2oat_orig_dir + "/boot.art", + patchoat_dir + "/boot.art", + base_addr_delta, + &error_msg)) { + FAIL() << "RelocateBootImage failed: " << error_msg; + } + + // dex2oat_reloc_image_filename is the boot image relocated using dex2oat + // patchoat_reloc_image_filename is the boot image relocated using patchoat + std::string dex2oat_reloc_image_filename = dex2oat_reloc_dir + "/boot.art"; + std::string patchoat_reloc_image_filename = dex2oat_orig_dir + "/boot.art"; + std::replace( + patchoat_reloc_image_filename.begin() + 1, patchoat_reloc_image_filename.end(), '/', '@'); + patchoat_reloc_image_filename = + patchoat_dir + + (android::base::StartsWith(patchoat_reloc_image_filename, "/") ? "" : "/") + + patchoat_reloc_image_filename; + + // Patch up the dex2oat-relocated image so that it looks as though it was relocated by patchoat. + // patchoat preserves the OAT checksum header field and sets patch delta header field. + if (!CopyImageChecksumAndSetPatchDelta( + dex2oat_orig_dir + "/boot.art", + dex2oat_reloc_dir + "/boot.art", + base_addr_delta, + &error_msg)) { + FAIL() << "Unable to copy image checksum: " << error_msg; + } + + // Assert that the patchoat-relocated image is identical to the dex2oat-relocated image + if (BinaryDiff(dex2oat_reloc_image_filename, patchoat_reloc_image_filename, &error_msg)) { + FAIL() << "patchoat- and dex2oat-relocated images differ: " << error_msg; + } + + ClearDirectory(dex2oat_orig_dir.c_str(), /*recursive*/ true); + ClearDirectory(dex2oat_reloc_dir.c_str(), /*recursive*/ true); + ClearDirectory(patchoat_dir.c_str(), /*recursive*/ true); + rmdir(dex2oat_orig_dir.c_str()); + rmdir(dex2oat_reloc_dir.c_str()); + rmdir(patchoat_dir.c_str()); +#else + LOG(INFO) << "Skipping PatchoatRelocationSameAsDex2oatRelocation"; + // Force-print to std::cout so it's also outside the logcat. + std::cout << "Skipping PatchoatRelocationSameAsDex2oatRelocation" << std::endl; +#endif +} + +} // namespace art diff --git a/runtime/image.h b/runtime/image.h index 3844186a9b..159a308fb3 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -179,6 +179,10 @@ class PACKED(4) ImageHeader { return patch_delta_; } + void SetPatchDelta(off_t patch_delta) { + patch_delta_ = patch_delta; + } + static std::string GetOatLocationFromImageLocation(const std::string& image) { return GetLocationFromImageLocation(image, "oat"); } -- GitLab From 7ff53f880f8b17c576924b3b1c4cba7381335444 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 15 Dec 2017 23:05:02 +0000 Subject: [PATCH 205/226] Revert "Move art-heap-poisoning and art-gtest-ss-gc to test cdex" This reverts commit 8a34abcb8a74ecda146af761b390de753092da36. Reason for revert: Gtests broken, working on it. Bug: 63756964 Change-Id: Ie3eab9df68035090be875c04cf1a69ea94e1314d --- test/testrunner/target_config.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index 297ce08bee..6d21442045 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -229,13 +229,10 @@ target_config = { }, 'art-heap-poisoning' : { 'run-test' : ['--interpreter', - '--optimizing', - '--cdex-fast'], + '--optimizing'], 'env' : { 'ART_USE_READ_BARRIER' : 'false', - 'ART_HEAP_POISONING' : 'true', - # Get some extra automated testing coverage for compact dex. - 'ART_DEFAULT_COMPACT_DEX_LEVEL' : 'fast' + 'ART_HEAP_POISONING' : 'true' } }, 'art-preopt' : { @@ -279,9 +276,7 @@ target_config = { 'make' : 'test-art-host-gtest', 'env': { 'ART_DEFAULT_GC_TYPE' : 'SS', - 'ART_USE_READ_BARRIER' : 'false', - # Get some extra automated testing coverage for compact dex. - 'ART_DEFAULT_COMPACT_DEX_LEVEL' : 'fast' + 'ART_USE_READ_BARRIER' : 'false' } }, 'art-gtest-gss-gc': { -- GitLab From 2f5bfb1d3cb92a71898f0a5109ad71a5461783e6 Mon Sep 17 00:00:00 2001 From: Victor Chang Date: Mon, 18 Dec 2017 19:04:53 +0100 Subject: [PATCH 206/226] Undefine TO_ENTRY_POINT macro after consumption Test: m Change-Id: I3f68ad67bae98d454d4b7145d5895f53e31a3730 --- runtime/well_known_classes.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 5a653fe3a1..5fef7dfd83 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -269,7 +269,7 @@ uint32_t WellKnownClasses::StringInitToEntryPoint(ArtMethod* string_init) { return kQuick ## entry_point_name; \ } STRING_INIT_LIST(TO_ENTRY_POINT) - #undef TO_STRING_FACTORY + #undef TO_ENTRY_POINT LOG(FATAL) << "Could not find StringFactory method for String."; return 0; } -- GitLab From 8f514ee35ec3dda9b38b794a8052e0312e4b1c04 Mon Sep 17 00:00:00 2001 From: Alan Leung Date: Fri, 8 Dec 2017 14:08:25 -0800 Subject: [PATCH 207/226] Update java.lang.String class size. In standalone desugar, one of the implemented interface CharSequence has two lambdas: lambda$chars$0$CharSequence lambda$codePoints$1$CharSequence which are virtual functions. In D8 Desugar, both of them are now direct methods. Bug: 69477285 Test: USE_D8_DESUGAR=true m && USE_D8_DESUGAR=false m Change-Id: I94aaa42e86bd104fc86fa14d1eb45b2e906da5a9 --- build/art.go | 4 ++++ runtime/mirror/string-inl.h | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/build/art.go b/build/art.go index 3f598da00a..58df11ca5e 100644 --- a/build/art.go +++ b/build/art.go @@ -103,6 +103,10 @@ func globalFlags(ctx android.BaseContext) ([]string, []string) { asflags = append(asflags, "-DART_MIPS32_CHECK_ALIGNMENT") } + if envTrue(ctx, "USE_D8_DESUGAR") { + cflags = append(cflags, "-DUSE_D8_DESUGAR=1") + } + return cflags, asflags } diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index 84587c871c..24c75ec0d8 100644 --- a/runtime/mirror/string-inl.h +++ b/runtime/mirror/string-inl.h @@ -35,7 +35,16 @@ namespace art { namespace mirror { inline uint32_t String::ClassSize(PointerSize pointer_size) { +#ifdef USE_D8_DESUGAR + // Two lambdas in CharSequence: + // lambda$chars$0$CharSequence + // lambda$codePoints$1$CharSequence + // which were virtual functions in standalone desugar, becomes + // direct functions with D8 desugaring. + uint32_t vtable_entries = Object::kVTableLength + 54; +#else uint32_t vtable_entries = Object::kVTableLength + 56; +#endif return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 1, 2, pointer_size); } -- GitLab From 403207107da7f11525c4d305184c56b35ec1c17a Mon Sep 17 00:00:00 2001 From: Alex Light Date: Thu, 14 Dec 2017 11:52:04 -0800 Subject: [PATCH 208/226] Add support for selecting alternate JDWP implementations Change JDWP options parsing to take place later and add a -XjdwpProvider:_ option that can be used by the runtime to select an appropriate JDWP provider. The argument is a string. If 'none' is given JDWP will be totally disabled. If 'internal' is given the current internal JDWP implementation is used. If 'default' is given the 'internal' JDWP implementation will currently be used. Other values will be added in the future. Also adds a runtime callback that will be invoked when the runtime wants to start or stop the debugger (namely at the post-zygote fork and just before exit) and check if a debugger is availible. Also add '-XjdwpOptions:_' in preparation for the eventual removal of the existing -Xrunjdwp=_ and -Xagentlib:jdwp=_ as top-level options. All of these options now store their arguments as a std::string to be interpreted by the JDWP implementation as it sees fit. Also change the jdwpOptions to default to transport=dt_android_adb if there is not one specified and it is available. This will make changing the default transport based on the JDWP provider easier. These new options are needed to allow us to support both the old, internal, JDWP implementation as its replacement is tested and verified. This lets us switch between them with little difficulty. We will probably remove one or both of these options once we have confidence that the new jdwp implementation has stuck. Test: ./test.py --host -j50 Test: ./test/run-test --host --debug 001-HelloWorld Test: Manual, flash walleye, debug app Bug: 62821960 Change-Id: Ie31db6b6f7d76a03d4ab8e178fcf298ed0eec203 --- cmdline/cmdline_parser_test.cc | 60 +++++------ cmdline/cmdline_types.h | 127 +++--------------------- runtime/Android.bp | 2 + runtime/debugger.cc | 16 +++ runtime/debugger.h | 18 ++-- runtime/jdwp/jdwp.h | 7 +- runtime/jdwp/jdwp_main.cc | 113 +++++++++++++++++++++ runtime/jdwp/jdwp_options_test.cc | 79 +++++++++++++++ runtime/jdwp_provider.h | 35 +++++++ runtime/native/dalvik_system_VMDebug.cc | 5 +- runtime/parsed_options.cc | 7 +- runtime/runtime.cc | 29 +++++- runtime/runtime.h | 15 +++ runtime/runtime_callbacks.cc | 29 ++++++ runtime/runtime_callbacks.h | 26 +++++ runtime/runtime_options.def | 3 +- 16 files changed, 406 insertions(+), 165 deletions(-) create mode 100644 runtime/jdwp/jdwp_options_test.cc create mode 100644 runtime/jdwp_provider.h diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index c438c54cea..802200013d 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -20,6 +20,7 @@ #include "gtest/gtest.h" +#include "jdwp_provider.h" #include "experimental_flags.h" #include "parsed_options.h" #include "runtime.h" @@ -364,48 +365,35 @@ TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) { } // TEST_F /* - * {"-Xrunjdwp:_", "-agentlib:jdwp=_"} + * { "-XjdwpProvider:_" } */ -TEST_F(CmdlineParserTest, TestJdwpOptions) { - /* - * Test success - */ +TEST_F(CmdlineParserTest, TestJdwpProviderEmpty) { { - /* - * "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n" - */ - JDWP::JdwpOptions opt = JDWP::JdwpOptions(); - opt.transport = JDWP::JdwpTransportType::kJdwpTransportSocket; - opt.port = 8000; - opt.server = true; + EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kInternal, "", M::JdwpProvider); + } +} // TEST_F - const char *opt_args = "-Xrunjdwp:transport=dt_socket,address=8000,server=y"; +TEST_F(CmdlineParserTest, TestJdwpProviderDefault) { + const char* opt_args = "-XjdwpProvider:default"; + EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kInternal, opt_args, M::JdwpProvider); +} // TEST_F - EXPECT_SINGLE_PARSE_VALUE(opt, opt_args, M::JdwpOptions); - } +TEST_F(CmdlineParserTest, TestJdwpProviderInternal) { + const char* opt_args = "-XjdwpProvider:internal"; + EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kInternal, opt_args, M::JdwpProvider); +} // TEST_F - { - /* - * "Example: -agentlib:jdwp=transport=dt_socket,address=localhost:6500,server=n\n"); - */ - JDWP::JdwpOptions opt = JDWP::JdwpOptions(); - opt.transport = JDWP::JdwpTransportType::kJdwpTransportSocket; - opt.host = "localhost"; - opt.port = 6500; - opt.server = false; - - const char *opt_args = "-agentlib:jdwp=transport=dt_socket,address=localhost:6500,server=n"; - - EXPECT_SINGLE_PARSE_VALUE(opt, opt_args, M::JdwpOptions); - } +TEST_F(CmdlineParserTest, TestJdwpProviderNone) { + const char* opt_args = "-XjdwpProvider:none"; + EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kNone, opt_args, M::JdwpProvider); +} // TEST_F - /* - * Test failures - */ - EXPECT_SINGLE_PARSE_FAIL("-Xrunjdwp:help", CmdlineResult::kUsage); // usage for help only - EXPECT_SINGLE_PARSE_FAIL("-Xrunjdwp:blabla", CmdlineResult::kFailure); // invalid subarg - EXPECT_SINGLE_PARSE_FAIL("-agentlib:jdwp=help", CmdlineResult::kUsage); // usage for help only - EXPECT_SINGLE_PARSE_FAIL("-agentlib:jdwp=blabla", CmdlineResult::kFailure); // invalid subarg +TEST_F(CmdlineParserTest, TestJdwpProviderHelp) { + EXPECT_SINGLE_PARSE_FAIL("-XjdwpProvider:help", CmdlineResult::kUsage); +} // TEST_F + +TEST_F(CmdlineParserTest, TestJdwpProviderFail) { + EXPECT_SINGLE_PARSE_FAIL("-XjdwpProvider:blablabla", CmdlineResult::kFailure); } // TEST_F /* diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index f12ef971af..5c887f8a73 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -34,6 +34,7 @@ #include "gc/collector_type.h" #include "gc/space/large_object_space.h" #include "jdwp/jdwp.h" +#include "jdwp_provider.h" #include "jit/profile_saver_options.h" #include "plugin.h" #include "read_barrier_config.h" @@ -64,123 +65,27 @@ struct CmdlineType : CmdlineTypeParser { }; template <> -struct CmdlineType : CmdlineTypeParser { +struct CmdlineType : CmdlineTypeParser { /* - * Handle one of the JDWP name/value pairs. - * - * JDWP options are: - * help: if specified, show help message and bail - * transport: may be dt_socket or dt_shmem - * address: for dt_socket, "host:port", or just "port" when listening - * server: if "y", wait for debugger to attach; if "n", attach to debugger - * timeout: how long to wait for debugger to connect / listen - * - * Useful with server=n (these aren't supported yet): - * onthrow=: connect to debugger when exception thrown - * onuncaught=y|n: connect to debugger when uncaught exception thrown - * launch=: launch the debugger itself - * - * The "transport" option is required, as is "address" if server=n. + * Handle a single JDWP provider name. Must be either 'internal', 'default', or the file name of + * an agent. A plugin will make use of this and the jdwpOptions to set up jdwp when appropriate. */ - Result Parse(const std::string& options) { - VLOG(jdwp) << "ParseJdwpOptions: " << options; - - if (options == "help") { + Result Parse(const std::string& option) { + if (option == "help") { return Result::Usage( - "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n" - "Example: -Xrunjdwp:transport=dt_socket,address=localhost:6500,server=n\n"); - } - - const std::string s; - - std::vector pairs; - Split(options, ',', &pairs); - - JDWP::JdwpOptions jdwp_options; - - for (const std::string& jdwp_option : pairs) { - std::string::size_type equals_pos = jdwp_option.find('='); - if (equals_pos == std::string::npos) { - return Result::Failure(s + - "Can't parse JDWP option '" + jdwp_option + "' in '" + options + "'"); - } - - Result parse_attempt = ParseJdwpOption(jdwp_option.substr(0, equals_pos), - jdwp_option.substr(equals_pos + 1), - &jdwp_options); - if (parse_attempt.IsError()) { - // We fail to parse this JDWP option. - return parse_attempt; - } - } - - if (jdwp_options.transport == JDWP::kJdwpTransportUnknown) { - return Result::Failure(s + "Must specify JDWP transport: " + options); - } - if (!jdwp_options.server && (jdwp_options.host.empty() || jdwp_options.port == 0)) { - return Result::Failure(s + "Must specify JDWP host and port when server=n: " + options); - } - - return Result::Success(std::move(jdwp_options)); - } - - Result ParseJdwpOption(const std::string& name, const std::string& value, - JDWP::JdwpOptions* jdwp_options) { - if (name == "transport") { - if (value == "dt_socket") { - jdwp_options->transport = JDWP::kJdwpTransportSocket; - } else if (value == "dt_android_adb") { - jdwp_options->transport = JDWP::kJdwpTransportAndroidAdb; - } else { - return Result::Failure("JDWP transport not supported: " + value); - } - } else if (name == "server") { - if (value == "n") { - jdwp_options->server = false; - } else if (value == "y") { - jdwp_options->server = true; - } else { - return Result::Failure("JDWP option 'server' must be 'y' or 'n'"); - } - } else if (name == "suspend") { - if (value == "n") { - jdwp_options->suspend = false; - } else if (value == "y") { - jdwp_options->suspend = true; - } else { - return Result::Failure("JDWP option 'suspend' must be 'y' or 'n'"); - } - } else if (name == "address") { - /* this is either or : */ - std::string port_string; - jdwp_options->host.clear(); - std::string::size_type colon = value.find(':'); - if (colon != std::string::npos) { - jdwp_options->host = value.substr(0, colon); - port_string = value.substr(colon + 1); - } else { - port_string = value; - } - if (port_string.empty()) { - return Result::Failure("JDWP address missing port: " + value); - } - char* end; - uint64_t port = strtoul(port_string.c_str(), &end, 10); - if (*end != '\0' || port > 0xffff) { - return Result::Failure("JDWP address has junk in port field: " + value); - } - jdwp_options->port = port; - } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") { - /* valid but unsupported */ - LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'"; + "Example: -XjdwpProvider:none to disable JDWP\n" + "Example: -XjdwpProvider:internal for internal jdwp implementation\n" + "Example: -XjdwpProvider:default for the default jdwp implementation" + " (currently internal)\n"); + } else if (option == "internal" || option == "default") { + return Result::Success(JdwpProvider::kInternal); + } else if (option == "none") { + return Result::Success(JdwpProvider::kNone); } else { - LOG(INFO) << "Ignoring unrecognized JDWP option '" << name << "'='" << value << "'"; + return Result::Failure(std::string("not a valid jdwp provider: ") + option); } - - return Result::SuccessNoValue(); } - - static const char* Name() { return "JdwpOptions"; } + static const char* Name() { return "JdwpProvider"; } }; template diff --git a/runtime/Android.bp b/runtime/Android.bp index 6477347a6e..1e5fe16e19 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -470,6 +470,7 @@ gensrcs { "instrumentation.h", "indirect_reference_table.h", "invoke_type.h", + "jdwp_provider.h", "jdwp/jdwp.h", "jdwp/jdwp_constants.h", "lock_word.h", @@ -602,6 +603,7 @@ art_cc_test { "intern_table_test.cc", "interpreter/safe_math_test.cc", "interpreter/unstarted_runtime_test.cc", + "jdwp/jdwp_options_test.cc", "java_vm_ext_test.cc", "jit/profile_compilation_info_test.cc", "leb128_test.cc", diff --git a/runtime/debugger.cc b/runtime/debugger.cc index b5ae09f701..c85c2336b0 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -328,6 +328,7 @@ bool Dbg::gDisposed = false; ObjectRegistry* Dbg::gRegistry = nullptr; DebuggerActiveMethodInspectionCallback Dbg::gDebugActiveCallback; DebuggerDdmCallback Dbg::gDebugDdmCallback; +InternalDebuggerControlCallback Dbg::gDebuggerControlCallback; // Deoptimization support. std::vector Dbg::deoptimization_requests_; @@ -364,6 +365,20 @@ bool DebuggerActiveMethodInspectionCallback::IsMethodSafeToJit(ArtMethod* m) { return !Dbg::MethodHasAnyBreakpoints(m); } +void InternalDebuggerControlCallback::StartDebugger() { + // Release the mutator lock. + ScopedThreadStateChange stsc(art::Thread::Current(), kNative); + Dbg::StartJdwp(); +} + +void InternalDebuggerControlCallback::StopDebugger() { + Dbg::StopJdwp(); +} + +bool InternalDebuggerControlCallback::IsDebuggerConfigured() { + return Dbg::IsJdwpConfigured(); +} + // Breakpoints. static std::vector gBreakpoints GUARDED_BY(Locks::breakpoint_lock_); @@ -736,6 +751,7 @@ void Dbg::ConfigureJdwp(const JDWP::JdwpOptions& jdwp_options) { CHECK_NE(jdwp_options.transport, JDWP::kJdwpTransportUnknown); gJdwpOptions = jdwp_options; gJdwpConfigured = true; + Runtime::Current()->GetRuntimeCallbacks()->AddDebuggerControlCallback(&gDebuggerControlCallback); } bool Dbg::IsJdwpConfigured() { diff --git a/runtime/debugger.h b/runtime/debugger.h index d5bad8dc67..74018137a0 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -53,18 +53,22 @@ class ScopedObjectAccessUnchecked; class StackVisitor; 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_); +}; + struct DebuggerDdmCallback : public DdmCallback { void DdmPublishChunk(uint32_t type, const ArrayRef& data) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); }; -struct DebuggerActiveMethodInspectionCallback : public MethodInspectionCallback { - bool IsMethodBeingInspected(ArtMethod* m ATTRIBUTE_UNUSED) - OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); - bool IsMethodSafeToJit(ArtMethod* m) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); +struct InternalDebuggerControlCallback : public DebuggerControlCallback { + void StartDebugger() OVERRIDE; + void StopDebugger() OVERRIDE; + bool IsDebuggerConfigured() OVERRIDE; }; - /* * Invoke-during-breakpoint support. */ @@ -251,7 +255,8 @@ class Dbg { } // Configures JDWP with parsed command-line options. - static void ConfigureJdwp(const JDWP::JdwpOptions& jdwp_options); + static void ConfigureJdwp(const JDWP::JdwpOptions& jdwp_options) + REQUIRES_SHARED(Locks::mutator_lock_); // Returns true if we had -Xrunjdwp or -agentlib:jdwp= on the command line. static bool IsJdwpConfigured(); @@ -789,6 +794,7 @@ class Dbg { static DebuggerActiveMethodInspectionCallback gDebugActiveCallback; static DebuggerDdmCallback gDebugDdmCallback; + static InternalDebuggerControlCallback gDebuggerControlCallback; // Indicates whether we should drop the JDWP connection because the runtime stops or the // debugger called VirtualMachine.Dispose. diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h index d712b10bc2..b491c3ee5c 100644 --- a/runtime/jdwp/jdwp.h +++ b/runtime/jdwp/jdwp.h @@ -98,14 +98,15 @@ bool operator!=(const JdwpLocation& lhs, const JdwpLocation& rhs); * How we talk to the debugger. */ enum JdwpTransportType { - kJdwpTransportUnknown = 0, + kJdwpTransportNone = 0, + kJdwpTransportUnknown, // Unknown tranpsort kJdwpTransportSocket, // transport=dt_socket kJdwpTransportAndroidAdb, // transport=dt_android_adb }; std::ostream& operator<<(std::ostream& os, const JdwpTransportType& rhs); struct JdwpOptions { - JdwpTransportType transport = kJdwpTransportUnknown; + JdwpTransportType transport = kJdwpTransportNone; bool server = false; bool suspend = false; std::string host = ""; @@ -114,6 +115,8 @@ struct JdwpOptions { bool operator==(const JdwpOptions& lhs, const JdwpOptions& rhs); +bool ParseJdwpOptions(const std::string& options, JdwpOptions* jdwp_options); + struct JdwpEvent; class JdwpNetStateBase; struct ModBasket; diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc index e275554721..63f5dc8b69 100644 --- a/runtime/jdwp/jdwp_main.cc +++ b/runtime/jdwp/jdwp_main.cc @@ -37,6 +37,119 @@ using android::base::StringPrintf; static void* StartJdwpThread(void* arg); + +static bool ParseJdwpOption(const std::string& name, + const std::string& value, + JdwpOptions* jdwp_options) { + if (name == "transport") { + if (value == "dt_socket") { + jdwp_options->transport = JDWP::kJdwpTransportSocket; + } else if (value == "dt_android_adb") { + jdwp_options->transport = JDWP::kJdwpTransportAndroidAdb; + } else { + jdwp_options->transport = JDWP::kJdwpTransportUnknown; + LOG(ERROR) << "JDWP transport not supported: " << value; + return false; + } + } else if (name == "server") { + if (value == "n") { + jdwp_options->server = false; + } else if (value == "y") { + jdwp_options->server = true; + } else { + LOG(ERROR) << "JDWP option 'server' must be 'y' or 'n'"; + return false; + } + } else if (name == "suspend") { + if (value == "n") { + jdwp_options->suspend = false; + } else if (value == "y") { + jdwp_options->suspend = true; + } else { + LOG(ERROR) << "JDWP option 'suspend' must be 'y' or 'n'"; + return false; + } + } else if (name == "address") { + /* this is either or : */ + std::string port_string; + jdwp_options->host.clear(); + std::string::size_type colon = value.find(':'); + if (colon != std::string::npos) { + jdwp_options->host = value.substr(0, colon); + port_string = value.substr(colon + 1); + } else { + port_string = value; + } + if (port_string.empty()) { + LOG(ERROR) << "JDWP address missing port: " << value; + return false; + } + char* end; + uint64_t port = strtoul(port_string.c_str(), &end, 10); + if (*end != '\0' || port > 0xffff) { + LOG(ERROR) << "JDWP address has junk in port field: " << value; + return false; + } + jdwp_options->port = port; + } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") { + /* valid but unsupported */ + LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'"; + } else { + LOG(INFO) << "Ignoring unrecognized JDWP option '" << name << "'='" << value << "'"; + } + + return true; +} + +bool ParseJdwpOptions(const std::string& options, JdwpOptions* jdwp_options) { + VLOG(jdwp) << "ParseJdwpOptions: " << options; + + if (options == "help") { + LOG(ERROR) << "Example: -XjdwpOptions:transport=dt_socket,address=8000,server=y\n" + << "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n" + << "Example: -Xrunjdwp:transport=dt_socket,address=localhost:6500,server=n\n"; + return false; + } + + const std::string s; + + std::vector pairs; + Split(options, ',', &pairs); + + for (const std::string& jdwp_option : pairs) { + std::string::size_type equals_pos = jdwp_option.find('='); + if (equals_pos == std::string::npos) { + LOG(ERROR) << s << "Can't parse JDWP option '" << jdwp_option << "' in '" << options << "'"; + return false; + } + + bool parse_attempt = ParseJdwpOption(jdwp_option.substr(0, equals_pos), + jdwp_option.substr(equals_pos + 1), + jdwp_options); + if (!parse_attempt) { + // We fail to parse this JDWP option. + return parse_attempt; + } + } + + if (jdwp_options->transport == JDWP::kJdwpTransportUnknown) { + LOG(ERROR) << s << "Must specify JDWP transport: " << options; + return false; + } +#if ART_TARGET_ANDROID + if (jdwp_options->transport == JDWP::kJdwpTransportNone) { + jdwp_options->transport = JDWP::kJdwpTransportAndroidAdb; + LOG(WARNING) << "no JDWP transport specified. Defaulting to dt_android_adb"; + } +#endif + if (!jdwp_options->server && (jdwp_options->host.empty() || jdwp_options->port == 0)) { + LOG(ERROR) << s << "Must specify JDWP host and port when server=n: " << options; + return false; + } + + return true; +} + /* * JdwpNetStateBase class implementation */ diff --git a/runtime/jdwp/jdwp_options_test.cc b/runtime/jdwp/jdwp_options_test.cc new file mode 100644 index 0000000000..10c52e8cf8 --- /dev/null +++ b/runtime/jdwp/jdwp_options_test.cc @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jdwp.h" + +#include "gtest/gtest.h" + +namespace art { +namespace JDWP { + +TEST(JdwpOptionsTest, Options) { + { + /* + * "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n" + */ + JDWP::JdwpOptions opt = JDWP::JdwpOptions(); + const char *opt_args = "transport=dt_socket,address=8000,server=y"; + + EXPECT_TRUE(ParseJdwpOptions(opt_args, &opt)); + EXPECT_EQ(opt.transport, JdwpTransportType::kJdwpTransportSocket); + EXPECT_EQ(opt.port, 8000u); + EXPECT_EQ(opt.server, true); + EXPECT_EQ(opt.suspend, false); + } + + { + /* + * Example: transport=dt_socket,address=localhost:6500,server=n + */ + JDWP::JdwpOptions opt = JDWP::JdwpOptions(); + const char *opt_args = "transport=dt_socket,address=localhost:6500,server=y"; + + EXPECT_TRUE(ParseJdwpOptions(opt_args, &opt)); + EXPECT_EQ(opt.transport, JdwpTransportType::kJdwpTransportSocket); + EXPECT_EQ(opt.port, 6500u); + EXPECT_EQ(opt.host, "localhost"); + EXPECT_EQ(opt.server, true); + EXPECT_EQ(opt.suspend, false); + } + + { + /* + * Example: transport=dt_android_adb,server=n,suspend=y; + */ + JDWP::JdwpOptions opt = JDWP::JdwpOptions(); + const char *opt_args = "transport=dt_android_adb,server=y"; + + EXPECT_TRUE(ParseJdwpOptions(opt_args, &opt)); + EXPECT_EQ(opt.transport, JdwpTransportType::kJdwpTransportAndroidAdb); + EXPECT_EQ(opt.port, 0xFFFF); + EXPECT_EQ(opt.host, ""); + EXPECT_EQ(opt.server, true); + EXPECT_EQ(opt.suspend, false); + } + + /* + * Test failures + */ + JDWP::JdwpOptions opt = JDWP::JdwpOptions(); + EXPECT_FALSE(ParseJdwpOptions("help", &opt)); + EXPECT_FALSE(ParseJdwpOptions("blabla", &opt)); + EXPECT_FALSE(ParseJdwpOptions("transport=dt_android_adb,server=n", &opt)); +} + +} // namespace JDWP +} // namespace art diff --git a/runtime/jdwp_provider.h b/runtime/jdwp_provider.h new file mode 100644 index 0000000000..849ba211fe --- /dev/null +++ b/runtime/jdwp_provider.h @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#ifndef ART_RUNTIME_JDWP_PROVIDER_H_ +#define ART_RUNTIME_JDWP_PROVIDER_H_ + +#include + +#include "base/macros.h" +#include "base/logging.h" + +namespace art { + +enum class JdwpProvider { + kNone, + kInternal, +}; + +std::ostream& operator<<(std::ostream& os, const JdwpProvider& rhs); + +} // namespace art +#endif // ART_RUNTIME_JDWP_PROVIDER_H_ diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 88a78ab4be..787646dd21 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -157,8 +157,9 @@ static jboolean VMDebug_isDebuggerConnected(JNIEnv*, jclass) { return Dbg::IsDebuggerActive(); } -static jboolean VMDebug_isDebuggingEnabled(JNIEnv*, jclass) { - return Dbg::IsJdwpConfigured(); +static jboolean VMDebug_isDebuggingEnabled(JNIEnv* env, jclass) { + ScopedObjectAccess soa(env); + return Runtime::Current()->GetRuntimeCallbacks()->IsDebuggerConfigured(); } static jlong VMDebug_lastDebuggerActivity(JNIEnv*, jclass) { diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index a3c00364a1..47309edfd7 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -92,8 +92,11 @@ std::unique_ptr ParsedOptions::MakeParser(bool ignore_unrecognize .IntoKey(M::CheckJni) .Define("-Xjniopts:forcecopy") .IntoKey(M::JniOptsForceCopy) - .Define({"-Xrunjdwp:_", "-agentlib:jdwp=_"}) - .WithType() + .Define("-XjdwpProvider:_") + .WithType() + .IntoKey(M::JdwpProvider) + .Define({"-Xrunjdwp:_", "-agentlib:jdwp=_", "-XjdwpOptions:_"}) + .WithType() .IntoKey(M::JdwpOptions) // TODO Re-enable -agentlib: once I have a good way to transform the values. // .Define("-agentlib:_") diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 1cdeb7c77c..9c527e7fcd 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -355,7 +355,7 @@ Runtime::~Runtime() { } // Make sure our internal threads are dead before we start tearing down things they're using. - Dbg::StopJdwp(); + GetRuntimeCallbacks()->StopDebugger(); delete signal_catcher_; // Make sure all other non-daemon threads have terminated, and all daemon threads are suspended. @@ -871,8 +871,10 @@ void Runtime::InitNonZygoteOrPostFork( StartSignalCatcher(); // Start the JDWP thread. If the command-line debugger flags specified "suspend=y", - // this will pause the runtime, so we probably want this to come last. - Dbg::StartJdwp(); + // this will pause the runtime (in the internal debugger implementation), so we probably want + // this to come last. + ScopedObjectAccess soa(Thread::Current()); + GetRuntimeCallbacks()->StartDebugger(); } void Runtime::StartSignalCatcher() { @@ -1221,8 +1223,24 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { dump_gc_performance_on_shutdown_ = runtime_options.Exists(Opt::DumpGCPerformanceOnShutdown); - if (runtime_options.Exists(Opt::JdwpOptions)) { - Dbg::ConfigureJdwp(runtime_options.GetOrDefault(Opt::JdwpOptions)); + jdwp_options_ = runtime_options.GetOrDefault(Opt::JdwpOptions); + jdwp_provider_ = runtime_options.GetOrDefault(Opt::JdwpProvider); + switch (jdwp_provider_) { + case JdwpProvider::kNone: { + LOG(WARNING) << "Disabling all JDWP support."; + break; + } + case JdwpProvider::kInternal: { + if (runtime_options.Exists(Opt::JdwpOptions)) { + JDWP::JdwpOptions ops; + if (!JDWP::ParseJdwpOptions(runtime_options.GetOrDefault(Opt::JdwpOptions), &ops)) { + LOG(ERROR) << "failed to parse jdwp options!"; + return false; + } + Dbg::ConfigureJdwp(ops); + } + break; + } } callbacks_->AddThreadLifecycleCallback(Dbg::GetThreadLifecycleCallback()); callbacks_->AddClassLoadCallback(Dbg::GetClassLoadCallback()); @@ -1509,6 +1527,7 @@ static bool EnsureJvmtiPlugin(Runtime* runtime, } // Is the process debuggable? Otherwise, do not attempt to load the plugin. + // TODO Support a crimped jvmti for non-debuggable runtimes. if (!runtime->IsJavaDebuggable()) { *error_msg = "Process is not debuggable."; return false; diff --git a/runtime/runtime.h b/runtime/runtime.h index 476b71f169..89caac4f3c 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -35,6 +35,7 @@ #include "experimental_flags.h" #include "gc_root.h" #include "instrumentation.h" +#include "jdwp_provider.h" #include "obj_ptr.h" #include "offsets.h" #include "process_state.h" @@ -696,6 +697,14 @@ class Runtime { return madvise_random_access_; } + const std::string& GetJdwpOptions() { + return jdwp_options_; + } + + JdwpProvider GetJdwpProvider() const { + return jdwp_provider_; + } + private: static void InitPlatformSignalHandlers(); @@ -953,6 +962,12 @@ class Runtime { // Whether zygote code is in a section that should not start threads. bool zygote_no_threads_; + // The string containing requested jdwp options + std::string jdwp_options_; + + // The jdwp provider we were configured with. + JdwpProvider jdwp_provider_; + // Saved environment. class EnvSnapshot { public: diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc index 40d7889565..cd3c0b7c88 100644 --- a/runtime/runtime_callbacks.cc +++ b/runtime/runtime_callbacks.cc @@ -49,6 +49,35 @@ void RuntimeCallbacks::DdmPublishChunk(uint32_t type, const ArrayRefIsDebuggerConfigured()) { + return true; + } + } + return false; +} + +void RuntimeCallbacks::StartDebugger() { + for (DebuggerControlCallback* cb : debugger_control_callbacks_) { + cb->StartDebugger(); + } +} + +void RuntimeCallbacks::StopDebugger() { + for (DebuggerControlCallback* cb : debugger_control_callbacks_) { + cb->StopDebugger(); + } +} + void RuntimeCallbacks::AddMethodInspectionCallback(MethodInspectionCallback* cb) { method_inspection_callbacks_.push_back(cb); } diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h index baf941a8e1..f405c9fe34 100644 --- a/runtime/runtime_callbacks.h +++ b/runtime/runtime_callbacks.h @@ -62,6 +62,19 @@ class DdmCallback { REQUIRES_SHARED(Locks::mutator_lock_) = 0; }; +class DebuggerControlCallback { + public: + virtual ~DebuggerControlCallback() {} + + // Begin running the debugger. + virtual void StartDebugger() = 0; + // The debugger should begin shutting down since the runtime is ending. This is just advisory + virtual void StopDebugger() = 0; + + // This allows the debugger to tell the runtime if it is configured. + virtual bool IsDebuggerConfigured() = 0; +}; + class RuntimeSigQuitCallback { public: virtual ~RuntimeSigQuitCallback() {} @@ -197,6 +210,17 @@ class RuntimeCallbacks { void AddDdmCallback(DdmCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_); void RemoveDdmCallback(DdmCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_); + void StartDebugger() REQUIRES_SHARED(Locks::mutator_lock_); + // NO_THREAD_SAFETY_ANALYSIS since this is only called when we are in the middle of shutting down + // and the mutator_lock_ is no longer acquirable. + void StopDebugger() NO_THREAD_SAFETY_ANALYSIS; + bool IsDebuggerConfigured() REQUIRES_SHARED(Locks::mutator_lock_); + + void AddDebuggerControlCallback(DebuggerControlCallback* cb) + REQUIRES_SHARED(Locks::mutator_lock_); + void RemoveDebuggerControlCallback(DebuggerControlCallback* cb) + REQUIRES_SHARED(Locks::mutator_lock_); + private: std::vector thread_callbacks_ GUARDED_BY(Locks::mutator_lock_); @@ -214,6 +238,8 @@ class RuntimeCallbacks { GUARDED_BY(Locks::mutator_lock_); std::vector ddm_callbacks_ GUARDED_BY(Locks::mutator_lock_); + std::vector debugger_control_callbacks_ + GUARDED_BY(Locks::mutator_lock_); }; } // namespace art diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 2e03562505..4bc824570a 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -43,7 +43,8 @@ RUNTIME_OPTIONS_KEY (std::string, ClassPath) RUNTIME_OPTIONS_KEY (std::string, Image) RUNTIME_OPTIONS_KEY (Unit, CheckJni) RUNTIME_OPTIONS_KEY (Unit, JniOptsForceCopy) -RUNTIME_OPTIONS_KEY (JDWP::JdwpOptions, JdwpOptions) +RUNTIME_OPTIONS_KEY (std::string, JdwpOptions, "") +RUNTIME_OPTIONS_KEY (JdwpProvider, JdwpProvider, JdwpProvider::kInternal) 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 532246e54787d3016882dfcc9e6d210c48a8c6d9 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 15 Dec 2017 16:44:21 -0800 Subject: [PATCH 209/226] Revert "Revert "Move art-heap-poisoning and art-gtest-ss-gc to test cdex"" Temporarily disabled failing test case. This reverts commit 7ff53f880f8b17c576924b3b1c4cba7381335444. Bug: 63756964 Test: test-art-host-gtest Change-Id: Iebdb405266c051b59b27eb4ad66371bfe07b1e0a --- dex2oat/dex2oat_test.cc | 2 ++ test/testrunner/target_config.py | 11 ++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 74adc758d9..796f1d89d8 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -955,6 +955,8 @@ class Dex2oatUnquickenTest : public Dex2oatTest { }; TEST_F(Dex2oatUnquickenTest, UnquickenMultiDex) { + // Disabled until figure out running compact dex + DexLayout causes quickening errors. + TEST_DISABLED_FOR_COMPACT_DEX(); RunUnquickenMultiDex(); } diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index 6d21442045..297ce08bee 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -229,10 +229,13 @@ target_config = { }, 'art-heap-poisoning' : { 'run-test' : ['--interpreter', - '--optimizing'], + '--optimizing', + '--cdex-fast'], 'env' : { 'ART_USE_READ_BARRIER' : 'false', - 'ART_HEAP_POISONING' : 'true' + 'ART_HEAP_POISONING' : 'true', + # Get some extra automated testing coverage for compact dex. + 'ART_DEFAULT_COMPACT_DEX_LEVEL' : 'fast' } }, 'art-preopt' : { @@ -276,7 +279,9 @@ target_config = { 'make' : 'test-art-host-gtest', 'env': { 'ART_DEFAULT_GC_TYPE' : 'SS', - 'ART_USE_READ_BARRIER' : 'false' + 'ART_USE_READ_BARRIER' : 'false', + # Get some extra automated testing coverage for compact dex. + 'ART_DEFAULT_COMPACT_DEX_LEVEL' : 'fast' } }, 'art-gtest-gss-gc': { -- GitLab From e166e67666bf4b23e4ed0a98f5e2bb3cae9cee7d Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 19 Dec 2017 18:59:29 +0000 Subject: [PATCH 210/226] Revert "Don't embed the dex code in the oat file if dex is uncompressed." This reverts commit ae7e83817e546848ef6b2949dd9065b153e14316. Reason for revert: Broken wrt/ preopted apps and stripping Bug: 63920015 Bug: 70777774 Change-Id: I39580684d46fa57bd780d2d8bedd65a47d58cf5e Test: m --- build/Android.gtest.mk | 19 +-------- compiler/driver/compiler_driver.cc | 6 --- compiler/driver/compiler_driver.h | 6 +-- dex2oat/dex2oat.cc | 12 +++--- dex2oat/dex2oat_test.cc | 15 ------- dex2oat/linker/image_test.h | 12 +++--- dex2oat/linker/oat_writer.cc | 68 ++++-------------------------- dex2oat/linker/oat_writer.h | 6 +-- dex2oat/linker/oat_writer_test.cc | 8 ++-- runtime/dex_file.h | 4 -- runtime/dex_file_loader.cc | 2 +- runtime/oat_file.cc | 50 +++++++++------------- runtime/oat_file.h | 9 ---- runtime/zip_archive.cc | 7 +-- runtime/zip_archive.h | 3 +- 15 files changed, 51 insertions(+), 176 deletions(-) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 9af6cf46b4..3c8eade773 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -72,11 +72,6 @@ $(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval $(call build-art-test-dex,art-gte ART_TEST_HOST_GTEST_MainStripped_DEX := $(basename $(ART_TEST_HOST_GTEST_Main_DEX))Stripped$(suffix $(ART_TEST_HOST_GTEST_Main_DEX)) ART_TEST_TARGET_GTEST_MainStripped_DEX := $(basename $(ART_TEST_TARGET_GTEST_Main_DEX))Stripped$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX)) -# Create rules for MainUncompressed, a copy of Main with the classes.dex uncompressed -# for the dex2oat tests. -ART_TEST_HOST_GTEST_MainUncompressed_DEX := $(basename $(ART_TEST_HOST_GTEST_Main_DEX))Uncompressed$(suffix $(ART_TEST_HOST_GTEST_Main_DEX)) -ART_TEST_TARGET_GTEST_MainUncompressed_DEX := $(basename $(ART_TEST_TARGET_GTEST_Main_DEX))Uncompressed$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX)) - $(ART_TEST_HOST_GTEST_MainStripped_DEX): $(ART_TEST_HOST_GTEST_Main_DEX) cp $< $@ $(call dexpreopt-remove-classes.dex,$@) @@ -85,16 +80,6 @@ $(ART_TEST_TARGET_GTEST_MainStripped_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX) cp $< $@ $(call dexpreopt-remove-classes.dex,$@) -$(ART_TEST_HOST_GTEST_MainUncompressed_DEX): $(ART_TEST_HOST_GTEST_Main_DEX) $(ZIPALIGN) - cp $< $@ - $(call uncompress-dexs, $@) - $(call align-package, $@) - -$(ART_TEST_TARGET_GTEST_MainUncompressed_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX) $(ZIPALIGN) - cp $< $@ - $(call uncompress-dexs, $@) - $(call align-package, $@) - ART_TEST_GTEST_VerifierDeps_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDeps/*.smali)) ART_TEST_GTEST_VerifierDepsMulti_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDepsMulti/*.smali)) ART_TEST_HOST_GTEST_VerifierDeps_DEX := $(dir $(ART_TEST_HOST_GTEST_Main_DEX))$(subst Main,VerifierDeps,$(basename $(notdir $(ART_TEST_HOST_GTEST_Main_DEX))))$(suffix $(ART_TEST_HOST_GTEST_Main_DEX)) @@ -125,7 +110,7 @@ ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods Prof ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested 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 +ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ManyMethods Statics VerifierDeps 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_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods @@ -711,8 +696,6 @@ $(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 := ART_TEST_TARGET_GTEST_MainStripped_DEX := -ART_TEST_HOST_GTEST_MainUncompressed_DEX := -ART_TEST_TARGET_GTEST_MainUncompressed_DEX := ART_TEST_GTEST_VerifierDeps_SRC := ART_TEST_HOST_GTEST_VerifierDeps_DEX := ART_TEST_TARGET_GTEST_VerifierDeps_DEX := diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index d1cb175430..0ca3c8f613 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -401,12 +401,6 @@ static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel( Thread* self, const CompilerDriver& driver, Handle class_loader, const DexFile& dex_file, const DexFile::ClassDef& class_def) REQUIRES_SHARED(Locks::mutator_lock_) { - // When the dex file is uncompressed in the APK, we do not generate a copy in the .vdex - // file. As a result, dex2oat will map the dex file read-only, and we only need to check - // that to know if we can do quickening. - if (dex_file.GetContainer() != nullptr && dex_file.GetContainer()->IsReadOnly()) { - return optimizer::DexToDexCompilationLevel::kDontDexToDexCompile; - } auto* const runtime = Runtime::Current(); DCHECK(driver.GetCompilerOptions().IsQuickeningCompilationEnabled()); const char* descriptor = dex_file.GetClassDescriptor(class_def); diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index eab15b9f49..d2141e8bc7 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -102,13 +102,13 @@ class CompilerDriver { ~CompilerDriver(); - // Set dex files associated with the oat file being compiled. + // Set dex files that will be stored in the oat file after being compiled. void SetDexFilesForOatFile(const std::vector& dex_files); // Set dex files classpath. void SetClasspathDexFiles(const std::vector& dex_files); - // Get dex files associated with the the oat file being compiled. + // Get dex file that will be stored in the oat file after being compiled. ArrayRef GetDexFilesForOatFile() const { return ArrayRef(dex_files_for_oat_file_); } @@ -528,7 +528,7 @@ class CompilerDriver { bool support_boot_image_fixup_; - // List of dex files associates with the oat file. + // List of dex files that will be stored in the oat file. std::vector dex_files_for_oat_file_; CompiledMethodStorage compiled_method_storage_; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 3f4904bb77..27bec1d3e1 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1550,7 +1550,7 @@ class Dex2Oat FINAL { for (size_t i = 0, size = oat_writers_.size(); i != size; ++i) { rodata_.push_back(elf_writers_[i]->StartRoData()); // Unzip or copy dex files straight to the oat file. - std::vector> opened_dex_files_map; + std::unique_ptr 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 @@ -1570,16 +1570,14 @@ class Dex2Oat FINAL { return dex2oat::ReturnCode::kOther; } dex_files_per_oat_file_.push_back(MakeNonOwningPointerVector(opened_dex_files)); - if (opened_dex_files_map.empty()) { - DCHECK(opened_dex_files.empty()); - } else { - for (std::unique_ptr& map : opened_dex_files_map) { - opened_dex_files_maps_.push_back(std::move(map)); - } + if (opened_dex_files_map != nullptr) { + opened_dex_files_maps_.push_back(std::move(opened_dex_files_map)); for (std::unique_ptr& dex_file : opened_dex_files) { dex_file_oat_index_map_.emplace(dex_file.get(), i); opened_dex_files_.push_back(std::move(dex_file)); } + } else { + DCHECK(opened_dex_files.empty()); } } } diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 41f2c70267..ad287b0745 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -1513,19 +1513,4 @@ TEST_F(Dex2oatDedupeCode, DedupeTest) { EXPECT_LT(dedupe_size, no_dedupe_size); } -TEST_F(Dex2oatTest, UncompressedTest) { - std::unique_ptr dex(OpenTestDexFile("MainUncompressed")); - std::string out_dir = GetScratchDir(); - const std::string base_oat_name = out_dir + "/base.oat"; - GenerateOdexForTest(dex->GetLocation(), - base_oat_name, - CompilerFilter::Filter::kQuicken, - { }, - true, // expect_success - false, // use_fd - [](const OatFile& o) { - CHECK(!o.ContainsDexCode()); - }); -} - } // namespace art diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index 208297b8f6..cedbccf7cc 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -253,7 +253,7 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, } std::vector rodata; - std::vector> opened_dex_files_maps; + std::vector> opened_dex_files_map; std::vector> opened_dex_files; // Now that we have finalized key_value_store_, start writing the oat file. for (size_t i = 0, size = oat_writers.size(); i != size; ++i) { @@ -266,7 +266,7 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, dex_file->GetLocation().c_str(), dex_file->GetLocationChecksum()); - std::vector> cur_opened_dex_files_maps; + std::unique_ptr cur_opened_dex_files_map; std::vector> cur_opened_dex_files; bool dex_files_ok = oat_writers[i]->WriteAndOpenDexFiles( vdex_files[i].GetFile(), @@ -276,14 +276,12 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, &key_value_store, /* verify */ false, // Dex files may be dex-to-dex-ed, don't verify. /* update_input_vdex */ false, - &cur_opened_dex_files_maps, + &cur_opened_dex_files_map, &cur_opened_dex_files); ASSERT_TRUE(dex_files_ok); - if (!cur_opened_dex_files_maps.empty()) { - for (std::unique_ptr& cur_map : cur_opened_dex_files_maps) { - opened_dex_files_maps.push_back(std::move(cur_map)); - } + if (cur_opened_dex_files_map != nullptr) { + opened_dex_files_map.push_back(std::move(cur_opened_dex_files_map)); for (std::unique_ptr& cur_dex_file : cur_opened_dex_files) { // dex_file_oat_index_map_.emplace(dex_file.get(), i); opened_dex_files.push_back(std::move(cur_dex_file)); diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 9e61c42ff5..293078acee 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -363,7 +363,6 @@ OatWriter::OatWriter(bool compiling_boot_image, compiler_driver_(nullptr), image_writer_(nullptr), compiling_boot_image_(compiling_boot_image), - only_contains_uncompressed_zip_entries_(false), dex_files_(nullptr), vdex_size_(0u), vdex_dex_files_offset_(0u), @@ -637,7 +636,7 @@ bool OatWriter::WriteAndOpenDexFiles( SafeMap* key_value_store, bool verify, bool update_input_vdex, - /*out*/ std::vector>* opened_dex_files_map, + /*out*/ std::unique_ptr* opened_dex_files_map, /*out*/ std::vector>* opened_dex_files) { CHECK(write_state_ == WriteState::kAddingDexFileSources); @@ -646,7 +645,7 @@ bool OatWriter::WriteAndOpenDexFiles( return false; } - std::vector> dex_files_map; + std::unique_ptr dex_files_map; std::vector> dex_files; // Initialize VDEX and OAT headers. @@ -3288,28 +3287,14 @@ bool OatWriter::WriteDexFiles(OutputStream* out, File* file, bool update_input_v vdex_dex_files_offset_ = vdex_size_; - only_contains_uncompressed_zip_entries_ = true; + // Write dex files. for (OatDexFile& oat_dex_file : oat_dex_files_) { - if (!oat_dex_file.source_.IsZipEntry()) { - only_contains_uncompressed_zip_entries_ = false; - break; - } - ZipEntry* entry = oat_dex_file.source_.GetZipEntry(); - if (!entry->IsUncompressed() || !entry->IsAlignedToDexHeader()) { - only_contains_uncompressed_zip_entries_ = false; - break; - } - } - - if (!only_contains_uncompressed_zip_entries_) { - // Write dex files. - for (OatDexFile& oat_dex_file : oat_dex_files_) { - if (!WriteDexFile(out, file, &oat_dex_file, update_input_vdex)) { - return false; - } + if (!WriteDexFile(out, file, &oat_dex_file, update_input_vdex)) { + return false; } } + CloseSources(); return true; } @@ -3631,7 +3616,7 @@ bool OatWriter::WriteDexFile(OutputStream* out, bool OatWriter::OpenDexFiles( File* file, bool verify, - /*out*/ std::vector>* opened_dex_files_map, + /*out*/ std::unique_ptr* opened_dex_files_map, /*out*/ std::vector>* opened_dex_files) { TimingLogger::ScopedTiming split("OpenDexFiles", timings_); @@ -3640,43 +3625,6 @@ bool OatWriter::OpenDexFiles( return true; } - if (only_contains_uncompressed_zip_entries_) { - std::vector> dex_files; - std::vector> maps; - for (OatDexFile& oat_dex_file : oat_dex_files_) { - std::string error_msg; - MemMap* map = oat_dex_file.source_.GetZipEntry()->MapDirectlyFromFile( - oat_dex_file.dex_file_location_data_, &error_msg); - if (map == nullptr) { - LOG(ERROR) << error_msg; - return false; - } - maps.emplace_back(map); - // Now, open the dex file. - dex_files.emplace_back(DexFileLoader::Open(map->Begin(), - map->Size(), - oat_dex_file.GetLocation(), - oat_dex_file.dex_file_location_checksum_, - /* oat_dex_file */ nullptr, - verify, - verify, - &error_msg)); - if (dex_files.back() == nullptr) { - LOG(ERROR) << "Failed to open dex file from oat file. File: " << oat_dex_file.GetLocation() - << " Error: " << error_msg; - return false; - } - oat_dex_file.class_offsets_.resize(dex_files.back()->GetHeader().class_defs_size_); - } - *opened_dex_files_map = std::move(maps); - *opened_dex_files = std::move(dex_files); - CloseSources(); - return true; - } - // We could have closed the sources at the point of writing the dex files, but to - // make it consistent with the case we're not writing the dex files, we close them now. - CloseSources(); - size_t map_offset = oat_dex_files_[0].dex_file_offset_; size_t length = vdex_size_ - map_offset; @@ -3735,7 +3683,7 @@ bool OatWriter::OpenDexFiles( oat_dex_file.class_offsets_.resize(dex_files.back()->GetHeader().class_defs_size_); } - opened_dex_files_map->push_back(std::move(dex_files_map)); + *opened_dex_files_map = std::move(dex_files_map); *opened_dex_files = std::move(dex_files); return true; } diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index 3e9f4636fb..4055878b55 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -173,7 +173,7 @@ class OatWriter { SafeMap* key_value_store, bool verify, bool update_input_vdex, - /*out*/ std::vector>* opened_dex_files_map, + /*out*/ std::unique_ptr* opened_dex_files_map, /*out*/ std::vector>* opened_dex_files); bool WriteQuickeningInfo(OutputStream* vdex_out); bool WriteVerifierDeps(OutputStream* vdex_out, verifier::VerifierDeps* verifier_deps); @@ -300,7 +300,7 @@ class OatWriter { bool update_input_vdex); bool OpenDexFiles(File* file, bool verify, - /*out*/ std::vector>* opened_dex_files_map, + /*out*/ std::unique_ptr* opened_dex_files_map, /*out*/ std::vector>* opened_dex_files); size_t InitOatHeader(InstructionSet instruction_set, @@ -367,8 +367,6 @@ class OatWriter { const CompilerDriver* compiler_driver_; ImageWriter* image_writer_; const bool compiling_boot_image_; - // Whether the dex files being compiled are all uncompressed in the APK. - bool only_contains_uncompressed_zip_entries_; // note OatFile does not take ownership of the DexFiles const std::vector* dex_files_; diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index f4dfcd6a0d..fec05cc393 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -186,7 +186,7 @@ class OatTest : public CommonCompilerTest { oat_file); elf_writer->Start(); OutputStream* oat_rodata = elf_writer->StartRoData(); - std::vector> opened_dex_files_maps; + std::unique_ptr opened_dex_files_map; std::vector> opened_dex_files; if (!oat_writer.WriteAndOpenDexFiles(vdex_file, oat_rodata, @@ -195,7 +195,7 @@ class OatTest : public CommonCompilerTest { &key_value_store, verify, /* update_input_vdex */ false, - &opened_dex_files_maps, + &opened_dex_files_map, &opened_dex_files)) { return false; } @@ -251,9 +251,7 @@ class OatTest : public CommonCompilerTest { return false; } - for (std::unique_ptr& map : opened_dex_files_maps) { - opened_dex_files_maps_.emplace_back(std::move(map)); - } + opened_dex_files_maps_.emplace_back(std::move(opened_dex_files_map)); for (std::unique_ptr& dex_file : opened_dex_files) { opened_dex_files_.emplace_back(dex_file.release()); } diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 73b8c30d96..944a30849f 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -1032,10 +1032,6 @@ class DexFile { ALWAYS_INLINE const StandardDexFile* AsStandardDexFile() const; ALWAYS_INLINE const CompactDexFile* AsCompactDexFile() const; - DexFileContainer* GetContainer() const { - return container_.get(); - } - protected: DexFile(const uint8_t* base, size_t size, diff --git a/runtime/dex_file_loader.cc b/runtime/dex_file_loader.cc index 6d4bbf13d8..bc9276985b 100644 --- a/runtime/dex_file_loader.cc +++ b/runtime/dex_file_loader.cc @@ -379,7 +379,7 @@ std::unique_ptr DexFileLoader::OpenOneDexFileFromZip( std::unique_ptr map; if (zip_entry->IsUncompressed()) { - if (!zip_entry->IsAlignedToDexHeader()) { + if (!zip_entry->IsAlignedTo(alignof(DexFile::Header))) { // Do not mmap unaligned ZIP entries because // doing so would fail dex verification which requires 4 byte alignment. LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; " diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 1d05399de2..c82df7119f 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -594,6 +594,14 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { dex_file_location.c_str()); return false; } + if (UNLIKELY(dex_file_offset == 0U)) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with zero dex " + "file offset", + GetLocation().c_str(), + i, + dex_file_location.c_str()); + return false; + } if (UNLIKELY(dex_file_offset > DexSize())) { *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file " "offset %u > %zu", @@ -604,36 +612,20 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { DexSize()); return false; } - const uint8_t* dex_file_pointer = nullptr; - if (UNLIKELY(dex_file_offset == 0U)) { - if (uncompressed_dex_files_ == nullptr) { - uncompressed_dex_files_.reset(new std::vector>()); - // No dex files, load it from location. - if (!DexFileLoader::Open(dex_file_location.c_str(), - dex_file_location, - /* verify */ false, - /* verify_checksum */ false, - error_msg, - uncompressed_dex_files_.get())) { - return false; - } - } - dex_file_pointer = uncompressed_dex_files_.get()->at(i)->Begin(); - } else { - if (UNLIKELY(DexSize() - dex_file_offset < sizeof(DexFile::Header))) { - *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file " - "offset %u of %zu but the size of dex file header is %zu", - GetLocation().c_str(), - i, - dex_file_location.c_str(), - dex_file_offset, - DexSize(), - sizeof(DexFile::Header)); - return false; - } - dex_file_pointer = DexBegin() + dex_file_offset; + if (UNLIKELY(DexSize() - dex_file_offset < sizeof(DexFile::Header))) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file " + "offset %u of %zu but the size of dex file header is %zu", + GetLocation().c_str(), + i, + dex_file_location.c_str(), + dex_file_offset, + DexSize(), + sizeof(DexFile::Header)); + return false; } + const uint8_t* dex_file_pointer = DexBegin() + dex_file_offset; + const bool valid_magic = DexFileLoader::IsMagicValid(dex_file_pointer); if (UNLIKELY(!valid_magic)) { *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with invalid " @@ -654,7 +646,7 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { return false; } const DexFile::Header* header = reinterpret_cast(dex_file_pointer); - if (dex_file_offset != 0 && (DexSize() - dex_file_offset < header->file_size_)) { + if (DexSize() - dex_file_offset < header->file_size_) { *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file " "offset %u and size %u truncated at %zu", GetLocation().c_str(), diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 61daa9579e..36a4d7b8fc 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -324,11 +324,6 @@ class OatFile { return vdex_.get(); } - // Whether the OatFile embeds the Dex code. - bool ContainsDexCode() const { - return uncompressed_dex_files_ == nullptr; - } - protected: OatFile(const std::string& filename, bool executable); @@ -394,10 +389,6 @@ class OatFile { // elements. std::list<> and std::deque<> satisfy this requirement, std::vector<> doesn't. mutable std::list string_cache_ GUARDED_BY(secondary_lookup_lock_); - // Cache of dex files mapped directly from a location, in case the OatFile does - // not embed the dex code. - std::unique_ptr>> uncompressed_dex_files_; - friend class gc::collector::DummyOatFile; // For modifying begin_ and end_. friend class OatClass; friend class art::OatDexFile; diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc index 279c0e4e83..f3d4d77214 100644 --- a/runtime/zip_archive.cc +++ b/runtime/zip_archive.cc @@ -27,7 +27,6 @@ #include "android-base/stringprintf.h" #include "ziparchive/zip_archive.h" -#include "dex_file.h" #include "base/bit_utils.h" #include "base/unix_file/fd_file.h" @@ -50,15 +49,11 @@ bool ZipEntry::IsUncompressed() { return zip_entry_->method == kCompressStored; } -bool ZipEntry::IsAlignedTo(size_t alignment) const { +bool ZipEntry::IsAlignedTo(size_t alignment) { DCHECK(IsPowerOfTwo(alignment)) << alignment; 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/runtime/zip_archive.h index ca7bbe965b..821cc5ceb1 100644 --- a/runtime/zip_archive.h +++ b/runtime/zip_archive.h @@ -58,8 +58,7 @@ class ZipEntry { uint32_t GetCrc32(); bool IsUncompressed(); - bool IsAlignedTo(size_t alignment) const; - bool IsAlignedToDexHeader() const; + bool IsAlignedTo(size_t alignment); private: ZipEntry(ZipArchiveHandle handle, -- GitLab From 641a3afee3e6879b71ae9fe47b92680580901a49 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 15 Dec 2017 11:42:58 -0800 Subject: [PATCH 211/226] Add code_item_accessors-no_art-inl and use it in dexlist, dexdump Added new helper to prevent inclusion of ART code. Removed accesses to dex code. Bug: 63756964 Bug: 70852830 Test: test-art-host-gtest -j40 Test: mm test-art-host-dexdump -j40 Change-Id: Ie0220df464a5cc2b81c0ee3e0483cdf2de003092 --- dexdump/dexdump.cc | 40 +++--- dexlist/dexlist.cc | 7 +- runtime/code_item_accessors-inl.h | 118 +--------------- runtime/code_item_accessors-no_art-inl.h | 169 +++++++++++++++++++++++ runtime/code_item_accessors.h | 18 +++ 5 files changed, 210 insertions(+), 142 deletions(-) create mode 100644 runtime/code_item_accessors-no_art-inl.h diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 527a5b993c..f64c89a5b0 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -44,6 +44,7 @@ #include "android-base/stringprintf.h" +#include "code_item_accessors-no_art-inl.h" #include "dex_file-inl.h" #include "dex_file_loader.h" #include "dex_file_types.h" @@ -949,14 +950,14 @@ static void dumpInstruction(const DexFile* pDexFile, fprintf(gOutFile, "%06x:", codeOffset + 0x10 + insnIdx * 2); // Dump (part of) raw bytes. - const u2* insns = pCode->insns_; + CodeItemInstructionAccessor accessor(pDexFile, pCode); for (u4 i = 0; i < 8; i++) { if (i < insnWidth) { if (i == 7) { fprintf(gOutFile, " ... "); } else { // Print 16-bit value in little-endian order. - const u1* bytePtr = (const u1*) &insns[insnIdx + i]; + const u1* bytePtr = (const u1*) &accessor.Insns()[insnIdx + i]; fprintf(gOutFile, " %02x%02x", bytePtr[0], bytePtr[1]); } } else { @@ -966,7 +967,7 @@ static void dumpInstruction(const DexFile* pDexFile, // Dump pseudo-instruction or opcode. if (pDecInsn->Opcode() == Instruction::NOP) { - const u2 instr = get2LE((const u1*) &insns[insnIdx]); + const u2 instr = get2LE((const u1*) &accessor.Insns()[insnIdx]); if (instr == Instruction::kPackedSwitchSignature) { fprintf(gOutFile, "|%04x: packed-switch-data (%d units)", insnIdx, insnWidth); } else if (instr == Instruction::kSparseSwitchSignature) { @@ -1167,16 +1168,15 @@ static void dumpBytecodes(const DexFile* pDexFile, u4 idx, codeOffset, codeOffset, dot.get(), name, signature.ToString().c_str()); // Iterate over all instructions. - const u2* insns = pCode->insns_; - for (u4 insnIdx = 0; insnIdx < pCode->insns_size_in_code_units_;) { - const Instruction* instruction = Instruction::At(&insns[insnIdx]); + CodeItemDataAccessor accessor(pDexFile, pCode); + for (const DexInstructionPcPair& pair : accessor) { + const Instruction* instruction = &pair.Inst(); const u4 insnWidth = instruction->SizeInCodeUnits(); if (insnWidth == 0) { - fprintf(stderr, "GLITCH: zero-width instruction at idx=0x%04x\n", insnIdx); + fprintf(stderr, "GLITCH: zero-width instruction at idx=0x%04x\n", pair.DexPc()); break; } - dumpInstruction(pDexFile, pCode, codeOffset, insnIdx, insnWidth, instruction); - insnIdx += insnWidth; + dumpInstruction(pDexFile, pCode, codeOffset, pair.DexPc(), insnWidth, instruction); } // for } @@ -1185,11 +1185,13 @@ static void dumpBytecodes(const DexFile* pDexFile, u4 idx, */ static void dumpCode(const DexFile* pDexFile, u4 idx, u4 flags, const DexFile::CodeItem* pCode, u4 codeOffset) { - fprintf(gOutFile, " registers : %d\n", pCode->registers_size_); - fprintf(gOutFile, " ins : %d\n", pCode->ins_size_); - fprintf(gOutFile, " outs : %d\n", pCode->outs_size_); + CodeItemDebugInfoAccessor accessor(pDexFile, pCode, pDexFile->GetDebugInfoOffset(pCode)); + + fprintf(gOutFile, " registers : %d\n", accessor.RegistersSize()); + fprintf(gOutFile, " ins : %d\n", accessor.InsSize()); + fprintf(gOutFile, " outs : %d\n", accessor.OutsSize()); fprintf(gOutFile, " insns size : %d 16-bit code units\n", - pCode->insns_size_in_code_units_); + accessor.InsnsSizeInCodeUnits()); // Bytecode disassembly, if requested. if (gOptions.disassemble) { @@ -1202,17 +1204,9 @@ static void dumpCode(const DexFile* pDexFile, u4 idx, u4 flags, // Positions and locals table in the debug info. bool is_static = (flags & kAccStatic) != 0; fprintf(gOutFile, " positions : \n"); - uint32_t debug_info_offset = pDexFile->GetDebugInfoOffset(pCode); - pDexFile->DecodeDebugPositionInfo(debug_info_offset, dumpPositionsCb, nullptr); + pDexFile->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), dumpPositionsCb, nullptr); fprintf(gOutFile, " locals : \n"); - pDexFile->DecodeDebugLocalInfo(pCode->registers_size_, - pCode->ins_size_, - pCode->insns_size_in_code_units_, - debug_info_offset, - is_static, - idx, - dumpLocalsCb, - nullptr); + accessor.DecodeDebugLocalInfo(is_static, idx, dumpLocalsCb, nullptr); } /* diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc index 4c13ed6410..2c910d4018 100644 --- a/dexlist/dexlist.cc +++ b/dexlist/dexlist.cc @@ -27,6 +27,7 @@ #include #include "base/logging.h" // For InitLogging. +#include "code_item_accessors-no_art-inl.h" #include "dex_file-inl.h" #include "dex_file_loader.h" #include "mem_map.h" @@ -99,6 +100,7 @@ static void dumpMethod(const DexFile* pDexFile, if (pCode == nullptr || codeOffset == 0) { return; } + CodeItemDebugInfoAccessor accessor(pDexFile, pCode, pDexFile->GetDebugInfoOffset(pCode)); // Method information. const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(idx); @@ -121,8 +123,7 @@ static void dumpMethod(const DexFile* pDexFile, // Find the first line. int firstLine = -1; - uint32_t debug_info_offset = pDexFile->GetDebugInfoOffset(pCode); - pDexFile->DecodeDebugPositionInfo(debug_info_offset, positionsCb, &firstLine); + pDexFile->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), positionsCb, &firstLine); // Method signature. const Signature signature = pDexFile->GetMethodSignature(pMethodId); @@ -130,7 +131,7 @@ static void dumpMethod(const DexFile* pDexFile, // Dump actual method information. fprintf(gOutFile, "0x%08x %d %s %s %s %s %d\n", - insnsOff, pCode->insns_size_in_code_units_ * 2, + insnsOff, accessor.InsnsSizeInCodeUnits() * 2, className.get(), methodName, typeDesc, fileName, firstLine); free(typeDesc); diff --git a/runtime/code_item_accessors-inl.h b/runtime/code_item_accessors-inl.h index 4f4d8cc86e..1ba3c55f77 100644 --- a/runtime/code_item_accessors-inl.h +++ b/runtime/code_item_accessors-inl.h @@ -17,7 +17,7 @@ #ifndef ART_RUNTIME_CODE_ITEM_ACCESSORS_INL_H_ #define ART_RUNTIME_CODE_ITEM_ACCESSORS_INL_H_ -#include "code_item_accessors.h" +#include "code_item_accessors-no_art-inl.h" #include "art_method-inl.h" #include "cdex/compact_dex_file.h" @@ -27,45 +27,9 @@ namespace art { -inline void CodeItemInstructionAccessor::Init(const CompactDexFile::CodeItem& code_item) { - insns_size_in_code_units_ = code_item.insns_size_in_code_units_; - insns_ = code_item.insns_; -} - -inline void CodeItemInstructionAccessor::Init(const StandardDexFile::CodeItem& code_item) { - insns_size_in_code_units_ = code_item.insns_size_in_code_units_; - insns_ = code_item.insns_; -} - -inline void CodeItemInstructionAccessor::Init(const DexFile* dex_file, - const DexFile::CodeItem* code_item) { - DCHECK(dex_file != nullptr); - DCHECK(code_item != nullptr); - if (dex_file->IsCompactDexFile()) { - Init(down_cast(*code_item)); - } else { - DCHECK(dex_file->IsStandardDexFile()); - Init(down_cast(*code_item)); - } -} - -inline CodeItemInstructionAccessor::CodeItemInstructionAccessor( - const DexFile* dex_file, - const DexFile::CodeItem* code_item) { - Init(dex_file, code_item); -} - inline CodeItemInstructionAccessor::CodeItemInstructionAccessor(ArtMethod* method) : CodeItemInstructionAccessor(method->GetDexFile(), method->GetCodeItem()) {} -inline DexInstructionIterator CodeItemInstructionAccessor::begin() const { - return DexInstructionIterator(insns_, 0u); -} - -inline DexInstructionIterator CodeItemInstructionAccessor::end() const { - return DexInstructionIterator(insns_, insns_size_in_code_units_); -} - inline CodeItemInstructionAccessor CodeItemInstructionAccessor::CreateNullable( ArtMethod* method) { DCHECK(method != nullptr); @@ -79,78 +43,14 @@ inline CodeItemInstructionAccessor CodeItemInstructionAccessor::CreateNullable( return ret; } -inline void CodeItemDataAccessor::Init(const CompactDexFile::CodeItem& code_item) { - CodeItemInstructionAccessor::Init(code_item); - registers_size_ = code_item.registers_size_; - ins_size_ = code_item.ins_size_; - outs_size_ = code_item.outs_size_; - tries_size_ = code_item.tries_size_; -} - -inline void CodeItemDataAccessor::Init(const StandardDexFile::CodeItem& code_item) { - CodeItemInstructionAccessor::Init(code_item); - registers_size_ = code_item.registers_size_; - ins_size_ = code_item.ins_size_; - outs_size_ = code_item.outs_size_; - tries_size_ = code_item.tries_size_; -} - -inline void CodeItemDataAccessor::Init(const DexFile* dex_file, - const DexFile::CodeItem* code_item) { - DCHECK(dex_file != nullptr); - DCHECK(code_item != nullptr); - if (dex_file->IsCompactDexFile()) { - CodeItemDataAccessor::Init(down_cast(*code_item)); - } else { - DCHECK(dex_file->IsStandardDexFile()); - CodeItemDataAccessor::Init(down_cast(*code_item)); - } -} - -inline CodeItemDataAccessor::CodeItemDataAccessor(const DexFile* dex_file, - const DexFile::CodeItem* code_item) { - Init(dex_file, code_item); -} - inline CodeItemDataAccessor::CodeItemDataAccessor(ArtMethod* method) : CodeItemDataAccessor(method->GetDexFile(), method->GetCodeItem()) {} -inline CodeItemDataAccessor CodeItemDataAccessor::CreateNullable( - const DexFile* dex_file, - const DexFile::CodeItem* code_item) { - CodeItemDataAccessor ret; - if (code_item != nullptr) { - ret.Init(dex_file, code_item); - } else { - DCHECK(!ret.HasCodeItem()) << "Should be null initialized"; - } - return ret; -} - inline CodeItemDataAccessor CodeItemDataAccessor::CreateNullable(ArtMethod* method) { DCHECK(method != nullptr); return CreateNullable(method->GetDexFile(), method->GetCodeItem()); } -inline IterationRange CodeItemDataAccessor::TryItems() const { - const DexFile::TryItem* try_items = DexFile::GetTryItems(end(), 0u); - return { - try_items, - try_items + TriesSize() }; -} - -inline const uint8_t* CodeItemDataAccessor::GetCatchHandlerData(size_t offset) const { - return DexFile::GetCatchHandlerData(end(), TriesSize(), offset); -} - -inline const DexFile::TryItem* CodeItemDataAccessor::FindTryItem(uint32_t try_dex_pc) const { - IterationRange try_items(TryItems()); - int32_t index = DexFile::FindTryItem(try_items.begin(), - try_items.end() - try_items.begin(), - try_dex_pc); - return index != -1 ? &try_items.begin()[index] : nullptr; -} - inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(ArtMethod* method) : CodeItemDebugInfoAccessor(method->GetDexFile(), method->GetCodeItem()) {} @@ -159,21 +59,7 @@ inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(const DexFile* dex_f if (code_item == nullptr) { return; } - debug_info_offset_ = OatFile::GetDebugInfoOffset(*dex_file, code_item); - if (dex_file->IsCompactDexFile()) { - Init(down_cast(*code_item)); - } else { - DCHECK(dex_file->IsStandardDexFile()); - Init(down_cast(*code_item)); - } -} - -inline void CodeItemDebugInfoAccessor::Init(const CompactDexFile::CodeItem& code_item) { - CodeItemDataAccessor::Init(code_item); -} - -inline void CodeItemDebugInfoAccessor::Init(const StandardDexFile::CodeItem& code_item) { - CodeItemDataAccessor::Init(code_item); + Init(dex_file, code_item, OatFile::GetDebugInfoOffset(*dex_file, code_item)); } } // namespace art diff --git a/runtime/code_item_accessors-no_art-inl.h b/runtime/code_item_accessors-no_art-inl.h new file mode 100644 index 0000000000..96321b54f5 --- /dev/null +++ b/runtime/code_item_accessors-no_art-inl.h @@ -0,0 +1,169 @@ +/* + * 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_CODE_ITEM_ACCESSORS_NO_ART_INL_H_ +#define ART_RUNTIME_CODE_ITEM_ACCESSORS_NO_ART_INL_H_ + +#include "code_item_accessors.h" + +#include "cdex/compact_dex_file.h" +#include "dex_file-inl.h" +#include "standard_dex_file.h" + +// The no ART version is used by binaries that don't include the whole runtime. + +namespace art { + +inline void CodeItemInstructionAccessor::Init(const CompactDexFile::CodeItem& code_item) { + insns_size_in_code_units_ = code_item.insns_size_in_code_units_; + insns_ = code_item.insns_; +} + +inline void CodeItemInstructionAccessor::Init(const StandardDexFile::CodeItem& code_item) { + insns_size_in_code_units_ = code_item.insns_size_in_code_units_; + insns_ = code_item.insns_; +} + +inline void CodeItemInstructionAccessor::Init(const DexFile* dex_file, + const DexFile::CodeItem* code_item) { + DCHECK(dex_file != nullptr); + DCHECK(code_item != nullptr); + if (dex_file->IsCompactDexFile()) { + Init(down_cast(*code_item)); + } else { + DCHECK(dex_file->IsStandardDexFile()); + Init(down_cast(*code_item)); + } +} + +inline CodeItemInstructionAccessor::CodeItemInstructionAccessor( + const DexFile* dex_file, + const DexFile::CodeItem* code_item) { + Init(dex_file, code_item); +} + +inline DexInstructionIterator CodeItemInstructionAccessor::begin() const { + return DexInstructionIterator(insns_, 0u); +} + +inline DexInstructionIterator CodeItemInstructionAccessor::end() const { + return DexInstructionIterator(insns_, insns_size_in_code_units_); +} + +inline void CodeItemDataAccessor::Init(const CompactDexFile::CodeItem& code_item) { + CodeItemInstructionAccessor::Init(code_item); + registers_size_ = code_item.registers_size_; + ins_size_ = code_item.ins_size_; + outs_size_ = code_item.outs_size_; + tries_size_ = code_item.tries_size_; +} + +inline void CodeItemDataAccessor::Init(const StandardDexFile::CodeItem& code_item) { + CodeItemInstructionAccessor::Init(code_item); + registers_size_ = code_item.registers_size_; + ins_size_ = code_item.ins_size_; + outs_size_ = code_item.outs_size_; + tries_size_ = code_item.tries_size_; +} + +inline void CodeItemDataAccessor::Init(const DexFile* dex_file, + const DexFile::CodeItem* code_item) { + DCHECK(dex_file != nullptr); + DCHECK(code_item != nullptr); + if (dex_file->IsCompactDexFile()) { + CodeItemDataAccessor::Init(down_cast(*code_item)); + } else { + DCHECK(dex_file->IsStandardDexFile()); + CodeItemDataAccessor::Init(down_cast(*code_item)); + } +} + +inline CodeItemDataAccessor::CodeItemDataAccessor(const DexFile* dex_file, + const DexFile::CodeItem* code_item) { + Init(dex_file, code_item); +} + +inline CodeItemDataAccessor CodeItemDataAccessor::CreateNullable( + const DexFile* dex_file, + const DexFile::CodeItem* code_item) { + CodeItemDataAccessor ret; + if (code_item != nullptr) { + ret.Init(dex_file, code_item); + } else { + DCHECK(!ret.HasCodeItem()) << "Should be null initialized"; + } + return ret; +} + +inline IterationRange CodeItemDataAccessor::TryItems() const { + const DexFile::TryItem* try_items = DexFile::GetTryItems(end(), 0u); + return { + try_items, + try_items + TriesSize() }; +} + +inline const uint8_t* CodeItemDataAccessor::GetCatchHandlerData(size_t offset) const { + return DexFile::GetCatchHandlerData(end(), TriesSize(), offset); +} + +inline const DexFile::TryItem* CodeItemDataAccessor::FindTryItem(uint32_t try_dex_pc) const { + IterationRange try_items(TryItems()); + int32_t index = DexFile::FindTryItem(try_items.begin(), + try_items.end() - try_items.begin(), + try_dex_pc); + return index != -1 ? &try_items.begin()[index] : nullptr; +} + +inline void CodeItemDebugInfoAccessor::Init(const DexFile* dex_file, + const DexFile::CodeItem* code_item, + uint32_t debug_info_offset) { + dex_file_ = dex_file; + debug_info_offset_ = debug_info_offset; + if (dex_file->IsCompactDexFile()) { + Init(down_cast(*code_item)); + } else { + DCHECK(dex_file->IsStandardDexFile()); + Init(down_cast(*code_item)); + } +} + +inline void CodeItemDebugInfoAccessor::Init(const CompactDexFile::CodeItem& code_item) { + CodeItemDataAccessor::Init(code_item); +} + +inline void CodeItemDebugInfoAccessor::Init(const StandardDexFile::CodeItem& code_item) { + CodeItemDataAccessor::Init(code_item); +} + +template +inline bool CodeItemDebugInfoAccessor::DecodeDebugLocalInfo(bool is_static, + uint32_t method_idx, + NewLocalCallback new_local, + void* context) const { + return dex_file_->DecodeDebugLocalInfo(RegistersSize(), + InsSize(), + InsnsSizeInCodeUnits(), + DebugInfoOffset(), + is_static, + method_idx, + new_local, + context); +} + + +} // namespace art + +#endif // ART_RUNTIME_CODE_ITEM_ACCESSORS_NO_ART_INL_H_ diff --git a/runtime/code_item_accessors.h b/runtime/code_item_accessors.h index a089a276de..9f40114438 100644 --- a/runtime/code_item_accessors.h +++ b/runtime/code_item_accessors.h @@ -142,17 +142,35 @@ class CodeItemDebugInfoAccessor : public CodeItemDataAccessor { ALWAYS_INLINE CodeItemDebugInfoAccessor(const DexFile* dex_file, const DexFile::CodeItem* code_item); + // Initialize with an existing offset. + ALWAYS_INLINE CodeItemDebugInfoAccessor(const DexFile* dex_file, + const DexFile::CodeItem* code_item, + uint32_t debug_info_offset) { + Init(dex_file, code_item, debug_info_offset); + } + + ALWAYS_INLINE void Init(const DexFile* dex_file, + const DexFile::CodeItem* code_item, + uint32_t debug_info_offset); + ALWAYS_INLINE explicit CodeItemDebugInfoAccessor(ArtMethod* method); uint32_t DebugInfoOffset() const { return debug_info_offset_; } + template + bool DecodeDebugLocalInfo(bool is_static, + uint32_t method_idx, + NewLocalCallback new_local, + void* context) const; + protected: ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item); ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item); private: + const DexFile* dex_file_ = nullptr; uint32_t debug_info_offset_ = 0u; }; -- GitLab From a46e50b87508a87264b04bf526f86fa93e2d8c30 Mon Sep 17 00:00:00 2001 From: Alex Klyubin Date: Tue, 19 Dec 2017 12:16:11 -0800 Subject: [PATCH 212/226] Test all .art files in patchoat test This augments the patchoat test to check all .art files produced by dex2oat and patchoat, rather than just the main one -- boot.art. For context, the test currently produces two .art files: boot.art and boot-core-libart-hostdex.art. Test: ./art/test/testrunner/run_build_test_target.py art-gtest-debug-gc Test: make test-art-host-gtest-patchoat_test Test: make test-art-target-gtest-patchoat_test Bug: 66697305 Change-Id: I8a90e70811ac8b98a7c2de5bfe6a388ca884e259 --- patchoat/patchoat_test.cc | 103 ++++++++++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 22 deletions(-) diff --git a/patchoat/patchoat_test.cc b/patchoat/patchoat_test.cc index dc4c78b32f..86e851c72b 100644 --- a/patchoat/patchoat_test.cc +++ b/patchoat/patchoat_test.cc @@ -14,6 +14,9 @@ * limitations under the License. */ +#include +#include + #include #include @@ -31,6 +34,44 @@ using android::base::StringPrintf; class PatchoatTest : public DexoptTest { public: + static bool ListDirFilesEndingWith( + const std::string& dir, + const std::string& suffix, + std::vector* filenames, + std::string* error_msg) { + DIR* d = opendir(dir.c_str()); + if (d == nullptr) { + *error_msg = "Failed to open directory"; + return false; + } + dirent* e; + struct stat s; + size_t suffix_len = suffix.size(); + while ((e = readdir(d)) != nullptr) { + if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0)) { + continue; + } + size_t name_len = strlen(e->d_name); + if ((name_len < suffix_len) || (strcmp(&e->d_name[name_len - suffix_len], suffix.c_str()))) { + continue; + } + std::string basename(e->d_name); + std::string filename = dir + "/" + basename; + int stat_result = lstat(filename.c_str(), &s); + if (stat_result != 0) { + *error_msg = + StringPrintf("Failed to stat %s: stat returned %d", filename.c_str(), stat_result); + return false; + } + if (S_ISDIR(s.st_mode)) { + continue; + } + filenames->push_back(basename); + } + closedir(d); + return true; + } + static void AddRuntimeArg(std::vector& args, const std::string& arg) { args.push_back("--runtime-arg"); args.push_back(arg); @@ -310,30 +351,48 @@ TEST_F(PatchoatTest, PatchoatRelocationSameAsDex2oatRelocation) { FAIL() << "RelocateBootImage failed: " << error_msg; } - // dex2oat_reloc_image_filename is the boot image relocated using dex2oat - // patchoat_reloc_image_filename is the boot image relocated using patchoat - std::string dex2oat_reloc_image_filename = dex2oat_reloc_dir + "/boot.art"; - std::string patchoat_reloc_image_filename = dex2oat_orig_dir + "/boot.art"; - std::replace( - patchoat_reloc_image_filename.begin() + 1, patchoat_reloc_image_filename.end(), '/', '@'); - patchoat_reloc_image_filename = - patchoat_dir - + (android::base::StartsWith(patchoat_reloc_image_filename, "/") ? "" : "/") - + patchoat_reloc_image_filename; - - // Patch up the dex2oat-relocated image so that it looks as though it was relocated by patchoat. - // patchoat preserves the OAT checksum header field and sets patch delta header field. - if (!CopyImageChecksumAndSetPatchDelta( - dex2oat_orig_dir + "/boot.art", - dex2oat_reloc_dir + "/boot.art", - base_addr_delta, - &error_msg)) { - FAIL() << "Unable to copy image checksum: " << error_msg; + // Assert that patchoat created the same set of .art files as dex2oat + std::vector dex2oat_image_basenames; + std::vector patchoat_image_basenames; + if (!ListDirFilesEndingWith(dex2oat_reloc_dir, ".art", &dex2oat_image_basenames, &error_msg)) { + FAIL() << "Failed to list *.art files in " << dex2oat_reloc_dir << ": " << error_msg; + } + if (!ListDirFilesEndingWith(patchoat_dir, ".art", &patchoat_image_basenames, &error_msg)) { + FAIL() << "Failed to list *.art files in " << patchoat_dir << ": " << error_msg; + } + std::sort(dex2oat_image_basenames.begin(), dex2oat_image_basenames.end()); + std::sort(patchoat_image_basenames.begin(), patchoat_image_basenames.end()); + // .art file names output by patchoat look like tmp@art-data--@boot*.art. To + // compare these with .art file names output by dex2oat we retain only the part of the file name + // after the last @. + std::vector patchoat_image_shortened_basenames(patchoat_image_basenames.size()); + for (size_t i = 0; i < patchoat_image_basenames.size(); i++) { + patchoat_image_shortened_basenames[i] = + patchoat_image_basenames[i].substr(patchoat_image_basenames[i].find_last_of("@") + 1); + } + ASSERT_EQ(dex2oat_image_basenames, patchoat_image_shortened_basenames); + + // Patch up the dex2oat-relocated image files so that it looks as though they were relocated by + // patchoat. patchoat preserves the OAT checksum header field and sets patch delta header field. + for (const std::string& image_basename : dex2oat_image_basenames) { + if (!CopyImageChecksumAndSetPatchDelta( + dex2oat_orig_dir + "/" + image_basename, + dex2oat_reloc_dir + "/" + image_basename, + base_addr_delta, + &error_msg)) { + FAIL() << "Unable to patch up " << image_basename << ": " << error_msg; + } } - // Assert that the patchoat-relocated image is identical to the dex2oat-relocated image - if (BinaryDiff(dex2oat_reloc_image_filename, patchoat_reloc_image_filename, &error_msg)) { - FAIL() << "patchoat- and dex2oat-relocated images differ: " << error_msg; + // Assert that the patchoat-relocated images are identical to the dex2oat-relocated images + for (size_t i = 0; i < dex2oat_image_basenames.size(); i++) { + const std::string& dex2oat_image_basename = dex2oat_image_basenames[i]; + const std::string& dex2oat_image_filename = dex2oat_reloc_dir + "/" + dex2oat_image_basename; + const std::string& patchoat_image_filename = patchoat_dir + "/" + patchoat_image_basenames[i]; + if (BinaryDiff(dex2oat_image_filename, patchoat_image_filename, &error_msg)) { + FAIL() << "patchoat- and dex2oat-relocated variants of " << dex2oat_image_basename + << " differ: " << error_msg; + } } ClearDirectory(dex2oat_orig_dir.c_str(), /*recursive*/ true); -- GitLab From 3979571aa1dfc907569fb7e27ab225ca89f6f86e Mon Sep 17 00:00:00 2001 From: Alex Light Date: Thu, 14 Dec 2017 11:58:21 -0800 Subject: [PATCH 213/226] Add fd-forwarding transport lib dt_fd_forward is a jdwpTransport that takes as an address a local socket file-descriptor and uses it to allow some other piece of code or program to control the actual attach and communication process. tools/dt_fds_forward.py is a python program that can be used as a controller for this comms system. This is useful for testing. It implements the same behavior (excepting DDMS) that the adbconnection plugin will but is capable of being easily used on normal host machines. Unlike the plugin, dt_fds_forward.py works by allowing a socket file-descriptor to be inherited by the forked Java Language Runtime. The overall goal of this transport is to use it to allow us to safely multiplex the out-bound data with DDMS data. This is needed to let us match current DDMS behavior which transmits packets in the blind on the same channel JDWP traffic is sent on. Test: ./tools/dt_fds_forward.py \ ./test/run-test --host --dev 001-HelloWorld Test: jdb -attach localhost:12345 Test: nc localhost 12345, handshake, disconnect Test: jdb -attach localhost:12345, detach, attach Test: ./tools/dt_fds_forward.py \ --debug-lib $JAVA_HOME/jre/lib/amd64/libjdwp.so \ -- \ ./test/run-test --host --jvm --dev 001-HelloWorld Test: jdb -attach localhost:12345 Bug: 62821960 Bug: 69169846 Change-Id: I654db6c6991c006933e1e1f0a4e41c13f795f9a8 --- Android.bp | 2 + dt_fd_forward/Android.bp | 63 ++ ...ODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION | 0 dt_fd_forward/NOTICE | 30 + dt_fd_forward/README.md | 32 + dt_fd_forward/dt_fd_forward.cc | 761 ++++++++++++++++++ dt_fd_forward/dt_fd_forward.h | 154 ++++ dt_fd_forward/export/Android.bp | 22 + dt_fd_forward/export/MODULE_LICENSE_APACHE2 | 0 dt_fd_forward/export/fd_transport.h | 69 ++ tools/dt_fds_forward.py | 196 +++++ 11 files changed, 1329 insertions(+) create mode 100644 dt_fd_forward/Android.bp create mode 100644 dt_fd_forward/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION create mode 100644 dt_fd_forward/NOTICE create mode 100644 dt_fd_forward/README.md create mode 100644 dt_fd_forward/dt_fd_forward.cc create mode 100644 dt_fd_forward/dt_fd_forward.h create mode 100644 dt_fd_forward/export/Android.bp create mode 100644 dt_fd_forward/export/MODULE_LICENSE_APACHE2 create mode 100644 dt_fd_forward/export/fd_transport.h create mode 100755 tools/dt_fds_forward.py diff --git a/Android.bp b/Android.bp index 295ae4c556..2400115663 100644 --- a/Android.bp +++ b/Android.bp @@ -31,6 +31,8 @@ subdirs = [ "dexlist", "dexoptanalyzer", "disassembler", + "dt_fd_forward", + "dt_fd_forward/export", "imgdiag", "oatdump", "openjdkjvm", diff --git a/dt_fd_forward/Android.bp b/dt_fd_forward/Android.bp new file mode 100644 index 0000000000..84faa08112 --- /dev/null +++ b/dt_fd_forward/Android.bp @@ -0,0 +1,63 @@ +// +// 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. +// + +// Build variants {target,host} x {debug,ndebug} x {32,64} + +cc_defaults { + name: "dt_fd_forward-defaults", + host_supported: true, + srcs: ["dt_fd_forward.cc"], + defaults: ["art_defaults"], + + // Note that this tool needs to be built for both 32-bit and 64-bit since it needs to be same + // ISA as what it is attached to. + compile_multilib: "both", + + shared_libs: [ + "libbase", + ], + target: { + android: { + }, + host: { + }, + }, + header_libs: [ + "javavm_headers", + "dt_fd_forward_export", + ], + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, +} + +art_cc_library { + name: "libdt_fd_forward", + defaults: ["dt_fd_forward-defaults"], +} + +art_cc_library { + name: "libdt_fd_forwardd", + defaults: [ + "art_debug_defaults", + "dt_fd_forward-defaults", + ], +} diff --git a/dt_fd_forward/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION b/dt_fd_forward/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION new file mode 100644 index 0000000000..e69de29bb2 diff --git a/dt_fd_forward/NOTICE b/dt_fd_forward/NOTICE new file mode 100644 index 0000000000..4fd88fa841 --- /dev/null +++ b/dt_fd_forward/NOTICE @@ -0,0 +1,30 @@ +Copyright (C) 2017 The Android Open Source Project +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + +This file implements interfaces from the file jdwpTransport.h. This +implementation is licensed under the same terms as the file +jdwpTransport.h. The copyright and license information for the file +jdwpTransport.h follows. + +Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + +This code is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 only, as +published by the Free Software Foundation. Oracle designates this +particular file as subject to the "Classpath" exception as provided +by Oracle in the LICENSE file that accompanied this code. + +This code is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +version 2 for more details (a copy is included in the LICENSE file that +accompanied this code). + +You should have received a copy of the GNU General Public License version +2 along with this work; if not, write to the Free Software Foundation, +Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + +Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +or visit www.oracle.com if you need additional information or have any +questions. diff --git a/dt_fd_forward/README.md b/dt_fd_forward/README.md new file mode 100644 index 0000000000..c7ac840928 --- /dev/null +++ b/dt_fd_forward/README.md @@ -0,0 +1,32 @@ +# dt_fd_forward + +dt_fd_forward is a jdwpTransport library. It implements the [Java Debug Wire +Protocol Transport Interface +(jdwpTransport)](https://docs.oracle.com/javase/7/docs/technotes/guides/jpda/jdwpTransport.html). +It allows one to handle and proxy JDWP traffic by supplying the implementation +for Attach. This transport requires an address. The address is a single integer +value that is the file-descriptor of an open AF\_UNIX socket. + +When this transport begins listening or attaching it will send the +null-terminated string "dt_fd_forward:START-LISTEN\0" over the given socket. + +When this transport stops listening for connections it will send the +null-terminated string "dt_fd_forward:END-LISTEN\0" over the socket. + +When this transport has successfully received fds from the proxy it sends the +message "dt_fd_forward:ATTACHED\0" over the socket. + +When this transport has closed its copies of the fds it will send the proxy the +message "dt_fd_forward:CLOSING\0" over the socket. + +When this transport accepts or attaches to a connection it will read from the +socket a 1 byte message and 3 file-descriptors. The file descriptors are, in +order, an fd that will be read from to get incoming JDWP packets (read\_fd\_), +an fd that outgoing JDWP packets will be written to (write\_fd\_), and an +_eventfd_ (write\_lock\_fd\_). The eventfd should not have any flags set. Prior +to writing any data to write\_fd\_ the transport will _read_ from the +write\_lock\_fd\_ and after finishing the write it will _write_ to it. This +allows one to safely multiplex data on the write\_fd\_. + +This transport implements no optional capabilities, though this may change in +the future. diff --git a/dt_fd_forward/dt_fd_forward.cc b/dt_fd_forward/dt_fd_forward.cc new file mode 100644 index 0000000000..cf3088dc60 --- /dev/null +++ b/dt_fd_forward/dt_fd_forward.cc @@ -0,0 +1,761 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jdwpTransport.h. This + * implementation is licensed under the same terms as the file + * jdwpTransport.h. The copyright and license information for the file + * jdwpTransport.h follows. + * + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "dt_fd_forward.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace dt_fd_forward { + +// Helper that puts line-number in error message. +#define DT_IO_ERROR(f) \ + SetLastError(::android::base::StringPrintf("%s:%d - %s: %s", \ + __FILE__, __LINE__, f, strerror(errno))) + +extern const jdwpTransportNativeInterface_ gTransportInterface; + +template static T HostToNetwork(T in); +template static T NetworkToHost(T in); + +template<> int8_t HostToNetwork(int8_t in) { return in; } +template<> int8_t NetworkToHost(int8_t in) { return in; } +template<> int16_t HostToNetwork(int16_t in) { return htons(in); } +template<> int16_t NetworkToHost(int16_t in) { return ntohs(in); } +template<> int32_t HostToNetwork(int32_t in) { return htonl(in); } +template<> int32_t NetworkToHost(int32_t in) { return ntohl(in); } + +FdForwardTransport::FdForwardTransport(jdwpTransportCallback* cb) + : mem_(*cb), + read_fd_(-1), + write_fd_(-1), + wakeup_fd_(eventfd(0, EFD_NONBLOCK)), + listen_fd_(-1), + close_notify_fd_(-1), + state_(TransportState::kClosed), + current_seq_num_(0) {} + +FdForwardTransport::~FdForwardTransport() { } + +bool FdForwardTransport::ChangeState(TransportState old_state, TransportState new_state) { + if (old_state == state_) { + state_ = new_state; + state_cv_.notify_all(); + return true; + } else { + return false; + } +} + +jdwpTransportError FdForwardTransport::PerformAttach(int listen_fd) { + jdwpTransportError err = SetupListen(listen_fd); + if (err != OK) { + return OK; + } + err = Accept(); + StopListening(); + return err; +} + +static void SendListenMessage(const android::base::unique_fd& fd) { + TEMP_FAILURE_RETRY(send(fd, kListenStartMessage, sizeof(kListenStartMessage), MSG_EOR)); +} + +jdwpTransportError FdForwardTransport::SetupListen(int listen_fd) { + std::lock_guard lk(state_mutex_); + if (!ChangeState(TransportState::kClosed, TransportState::kListenSetup)) { + return ERR(ILLEGAL_STATE); + } else { + listen_fd_.reset(dup(listen_fd)); + SendListenMessage(listen_fd_); + CHECK(ChangeState(TransportState::kListenSetup, TransportState::kListening)); + return OK; + } +} + +static void SendListenEndMessage(const android::base::unique_fd& fd) { + TEMP_FAILURE_RETRY(send(fd, kListenEndMessage, sizeof(kListenEndMessage), MSG_EOR)); +} + +jdwpTransportError FdForwardTransport::StopListening() { + std::lock_guard lk(state_mutex_); + if (listen_fd_ != -1) { + SendListenEndMessage(listen_fd_); + } + // Don't close the listen_fd_ since we might need it for later calls to listen. + if (ChangeState(TransportState::kListening, TransportState::kClosed) || + state_ == TransportState::kOpen) { + listen_fd_.reset(); + } + return OK; +} + +// Last error message. +thread_local std::string global_last_error_; + +void FdForwardTransport::SetLastError(const std::string& desc) { + LOG(ERROR) << desc; + global_last_error_ = desc; +} + +IOResult FdForwardTransport::ReadFullyWithoutChecks(void* data, size_t ndata) { + uint8_t* bdata = reinterpret_cast(data); + size_t nbytes = 0; + while (nbytes < ndata) { + int res = TEMP_FAILURE_RETRY(read(read_fd_, bdata + nbytes, ndata - nbytes)); + if (res < 0) { + DT_IO_ERROR("Failed read()"); + return IOResult::kError; + } else if (res == 0) { + return IOResult::kEOF; + } else { + nbytes += res; + } + } + return IOResult::kOk; +} + +IOResult FdForwardTransport::ReadUpToMax(void* data, size_t ndata, /*out*/size_t* read_amount) { + CHECK_GE(read_fd_.get(), 0); + int avail; + int res = ioctl(read_fd_, FIONREAD, &avail); + if (res < 0) { + DT_IO_ERROR("Failed ioctl(read_fd_, FIONREAD, &avail)"); + return IOResult::kError; + } + size_t to_read = std::min(static_cast(avail), ndata); + *read_amount = to_read; + if (*read_amount == 0) { + // Check if the read would cause an EOF. + struct pollfd pollfd = { read_fd_, POLLRDHUP, 0 }; + res = poll(&pollfd, /*nfds*/1, /*timeout*/0); + if (res < 0 || (pollfd.revents & POLLERR) == POLLERR) { + DT_IO_ERROR("Failed poll on read fd."); + return IOResult::kError; + } + return ((pollfd.revents & (POLLRDHUP | POLLHUP)) == 0) ? IOResult::kOk : IOResult::kEOF; + } + + return ReadFullyWithoutChecks(data, to_read); +} + +IOResult FdForwardTransport::ReadFully(void* data, size_t ndata) { + uint64_t seq_num = current_seq_num_; + size_t nbytes = 0; + while (nbytes < ndata) { + size_t read_len; + struct pollfd pollfds[2]; + { + std::lock_guard lk(state_mutex_); + // Operations in this block must not cause an unbounded pause. + if (state_ != TransportState::kOpen || seq_num != current_seq_num_) { + // Async-close occurred! + return IOResult::kInterrupt; + } else { + CHECK_GE(read_fd_.get(), 0); + } + IOResult res = ReadUpToMax(reinterpret_cast(data) + nbytes, + ndata - nbytes, + /*out*/&read_len); + if (res != IOResult::kOk) { + return res; + } else { + nbytes += read_len; + } + + pollfds[0] = { read_fd_, POLLRDHUP | POLLIN, 0 }; + pollfds[1] = { wakeup_fd_, POLLIN, 0 }; + } + if (read_len == 0) { + // No more data. Sleep without locks until more is available. We don't actually check for any + // errors since possible ones are (1) the read_fd_ is closed or wakeup happens which are both + // fine since the wakeup_fd_ or the poll failing will wake us up. + int poll_res = poll(pollfds, 2, -1); + if (poll_res < 0) { + DT_IO_ERROR("Failed to poll!"); + } + // Clear the wakeup_fd regardless. + uint64_t val; + int unused = read(wakeup_fd_, &val, sizeof(val)); + DCHECK(unused == sizeof(val) || errno == EAGAIN); + if (poll_res < 0) { + return IOResult::kError; + } + } + } + return IOResult::kOk; +} + +// A helper that allows us to lock the eventfd 'fd'. +class ScopedEventFdLock { + public: + explicit ScopedEventFdLock(const android::base::unique_fd& fd) : fd_(fd), data_(0) { + TEMP_FAILURE_RETRY(read(fd_, &data_, sizeof(data_))); + } + + ~ScopedEventFdLock() { + TEMP_FAILURE_RETRY(write(fd_, &data_, sizeof(data_))); + } + + private: + const android::base::unique_fd& fd_; + uint64_t data_; +}; + +IOResult FdForwardTransport::WriteFullyWithoutChecks(const void* data, size_t ndata) { + ScopedEventFdLock sefdl(write_lock_fd_); + const uint8_t* bdata = static_cast(data); + size_t nbytes = 0; + while (nbytes < ndata) { + int res = TEMP_FAILURE_RETRY(write(write_fd_, bdata + nbytes, ndata - nbytes)); + if (res < 0) { + DT_IO_ERROR("Failed write()"); + return IOResult::kError; + } else if (res == 0) { + return IOResult::kEOF; + } else { + nbytes += res; + } + } + return IOResult::kOk; +} + +IOResult FdForwardTransport::WriteFully(const void* data, size_t ndata) { + std::lock_guard lk(state_mutex_); + if (state_ != TransportState::kOpen) { + return IOResult::kInterrupt; + } + return WriteFullyWithoutChecks(data, ndata); +} + +static void SendAcceptMessage(int fd) { + TEMP_FAILURE_RETRY(send(fd, kAcceptMessage, sizeof(kAcceptMessage), MSG_EOR)); +} + +IOResult FdForwardTransport::ReceiveFdsFromSocket() { + union { + cmsghdr cm; + uint8_t buffer[CMSG_SPACE(sizeof(FdSet))]; + } msg_union; + // We don't actually care about the data. Only FDs. We need to have an iovec anyway to tell if we + // got the values or not though. + char dummy = '\0'; + iovec iov; + iov.iov_base = &dummy; + iov.iov_len = sizeof(dummy); + + msghdr msg; + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = msg_union.buffer; + msg.msg_controllen = sizeof(msg_union.buffer); + + cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = msg.msg_controllen; + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + memset(reinterpret_cast(CMSG_DATA(cmsg)), -1, FdSet::kDataLength); + + int res = TEMP_FAILURE_RETRY(recvmsg(listen_fd_, &msg, 0)); + if (res <= 0) { + DT_IO_ERROR("Failed to receive fds!"); + return IOResult::kError; + } + FdSet out_fds = FdSet::ReadData(CMSG_DATA(cmsg)); + if (out_fds.read_fd_ < 0 || out_fds.write_fd_ < 0 || out_fds.write_lock_fd_ < 0) { + DT_IO_ERROR("Received fds were invalid!"); + if (out_fds.read_fd_ >= 0) { + close(out_fds.read_fd_); + } + if (out_fds.write_fd_ >= 0) { + close(out_fds.write_fd_); + } + if (out_fds.write_lock_fd_ >= 0) { + close(out_fds.write_lock_fd_); + } + return IOResult::kError; + } + + read_fd_.reset(out_fds.read_fd_); + write_fd_.reset(out_fds.write_fd_); + write_lock_fd_.reset(out_fds.write_lock_fd_); + + // We got the fds. Send ack. + close_notify_fd_.reset(dup(listen_fd_)); + SendAcceptMessage(close_notify_fd_); + + return IOResult::kOk; +} + +// Accept the connection. Note that we match the behavior of other transports which is to just close +// the connection and try again if we get a bad handshake. +jdwpTransportError FdForwardTransport::Accept() { + // TODO Work with timeouts. + while (true) { + std::unique_lock lk(state_mutex_); + while (!ChangeState(TransportState::kListening, TransportState::kOpening)) { + if (state_ == TransportState::kClosed || + state_ == TransportState::kOpen) { + return ERR(ILLEGAL_STATE); + } + state_cv_.wait(lk); + } + + DCHECK_NE(listen_fd_.get(), -1); + if (ReceiveFdsFromSocket() != IOResult::kOk) { + CHECK(ChangeState(TransportState::kOpening, TransportState::kListening)); + return ERR(IO_ERROR); + } + + current_seq_num_++; + + // Moved to the opening state. + char handshake_recv[sizeof(kJdwpHandshake)]; + memset(handshake_recv, 0, sizeof(handshake_recv)); + IOResult res = ReadFullyWithoutChecks(handshake_recv, sizeof(handshake_recv)); + if (res != IOResult::kOk || + strncmp(handshake_recv, kJdwpHandshake, sizeof(kJdwpHandshake)) != 0) { + DT_IO_ERROR("Failed to read handshake"); + CHECK(ChangeState(TransportState::kOpening, TransportState::kListening)); + CloseFdsLocked(); + // Retry. + continue; + } + res = WriteFullyWithoutChecks(kJdwpHandshake, sizeof(kJdwpHandshake)); + if (res != IOResult::kOk) { + DT_IO_ERROR("Failed to write handshake"); + CHECK(ChangeState(TransportState::kOpening, TransportState::kListening)); + CloseFdsLocked(); + // Retry. + continue; + } + break; + } + CHECK(ChangeState(TransportState::kOpening, TransportState::kOpen)); + return OK; +} + +void SendClosingMessage(int fd) { + if (fd >= 0) { + TEMP_FAILURE_RETRY(send(fd, kCloseMessage, sizeof(kCloseMessage), MSG_EOR)); + } +} + +// Actually close the fds associated with this transport. +void FdForwardTransport::CloseFdsLocked() { + // We have a different set of fd's now. Increase the seq number. + current_seq_num_++; + + // All access to these is locked under the state_mutex_ so we are safe to close these. + { + ScopedEventFdLock sefdl(write_lock_fd_); + if (close_notify_fd_ >= 0) { + SendClosingMessage(close_notify_fd_); + } + close_notify_fd_.reset(); + read_fd_.reset(); + write_fd_.reset(); + close_notify_fd_.reset(); + } + write_lock_fd_.reset(); + + // Send a wakeup in case we have any in-progress reads/writes. + uint64_t data = 1; + TEMP_FAILURE_RETRY(write(wakeup_fd_, &data, sizeof(data))); +} + +jdwpTransportError FdForwardTransport::Close() { + std::lock_guard lk(state_mutex_); + jdwpTransportError res = + ChangeState(TransportState::kOpen, TransportState::kClosed) ? OK : ERR(ILLEGAL_STATE); + // Send a wakeup after changing the state even if nothing actually happened. + uint64_t data = 1; + TEMP_FAILURE_RETRY(write(wakeup_fd_, &data, sizeof(data))); + if (res == OK) { + CloseFdsLocked(); + } + return res; +} + +// A helper class to read and parse the JDWP packet. +class PacketReader { + public: + PacketReader(FdForwardTransport* transport, jdwpPacket* pkt) + : transport_(transport), + pkt_(pkt), + is_eof_(false), + is_err_(false) {} + bool ReadFully() { + // Zero out. + memset(pkt_, 0, sizeof(jdwpPacket)); + int32_t len = ReadInt32(); // read len + if (is_err_) { + return false; + } else if (is_eof_) { + return true; + } else if (len < 11) { + transport_->DT_IO_ERROR("Packet with len < 11 received!"); + return false; + } + pkt_->type.cmd.len = len; + pkt_->type.cmd.id = ReadInt32(); + pkt_->type.cmd.flags = ReadByte(); + if (is_err_) { + return false; + } else if (is_eof_) { + return true; + } else if ((pkt_->type.reply.flags & JDWPTRANSPORT_FLAGS_REPLY) == JDWPTRANSPORT_FLAGS_REPLY) { + ReadReplyPacket(); + } else { + ReadCmdPacket(); + } + return !is_err_; + } + + private: + void ReadReplyPacket() { + pkt_->type.reply.errorCode = ReadInt16(); + pkt_->type.reply.data = ReadRemaining(); + } + + void ReadCmdPacket() { + pkt_->type.cmd.cmdSet = ReadByte(); + pkt_->type.cmd.cmd = ReadByte(); + pkt_->type.cmd.data = ReadRemaining(); + } + + template + T HandleResult(IOResult res, T val, T fail) { + switch (res) { + case IOResult::kError: + is_err_ = true; + return fail; + case IOResult::kOk: + return val; + case IOResult::kEOF: + is_eof_ = true; + pkt_->type.cmd.len = 0; + return fail; + case IOResult::kInterrupt: + transport_->DT_IO_ERROR("Failed to read, concurrent close!"); + is_err_ = true; + return fail; + } + } + + jbyte* ReadRemaining() { + if (is_eof_ || is_err_) { + return nullptr; + } + jbyte* out = nullptr; + jint rem = pkt_->type.cmd.len - 11; + CHECK_GE(rem, 0); + if (rem == 0) { + return nullptr; + } else { + out = reinterpret_cast(transport_->Alloc(rem)); + IOResult res = transport_->ReadFully(out, rem); + jbyte* ret = HandleResult(res, out, static_cast(nullptr)); + if (ret != out) { + transport_->Free(out); + } + return ret; + } + } + + jbyte ReadByte() { + if (is_eof_ || is_err_) { + return -1; + } + jbyte out; + IOResult res = transport_->ReadFully(&out, sizeof(out)); + return HandleResult(res, NetworkToHost(out), static_cast(-1)); + } + + jshort ReadInt16() { + if (is_eof_ || is_err_) { + return -1; + } + jshort out; + IOResult res = transport_->ReadFully(&out, sizeof(out)); + return HandleResult(res, NetworkToHost(out), static_cast(-1)); + } + + jint ReadInt32() { + if (is_eof_ || is_err_) { + return -1; + } + jint out; + IOResult res = transport_->ReadFully(&out, sizeof(out)); + return HandleResult(res, NetworkToHost(out), -1); + } + + FdForwardTransport* transport_; + jdwpPacket* pkt_; + bool is_eof_; + bool is_err_; +}; + +jdwpTransportError FdForwardTransport::ReadPacket(jdwpPacket* pkt) { + if (pkt == nullptr) { + return ERR(ILLEGAL_ARGUMENT); + } + PacketReader reader(this, pkt); + if (reader.ReadFully()) { + return OK; + } else { + return ERR(IO_ERROR); + } +} + +// A class that writes a packet to the transport. +class PacketWriter { + public: + PacketWriter(FdForwardTransport* transport, const jdwpPacket* pkt) + : transport_(transport), pkt_(pkt), data_() {} + + bool WriteFully() { + PushInt32(pkt_->type.cmd.len); + PushInt32(pkt_->type.cmd.id); + PushByte(pkt_->type.cmd.flags); + if ((pkt_->type.reply.flags & JDWPTRANSPORT_FLAGS_REPLY) == JDWPTRANSPORT_FLAGS_REPLY) { + PushInt16(pkt_->type.reply.errorCode); + PushData(pkt_->type.reply.data, pkt_->type.reply.len - 11); + } else { + PushByte(pkt_->type.cmd.cmdSet); + PushByte(pkt_->type.cmd.cmd); + PushData(pkt_->type.cmd.data, pkt_->type.cmd.len - 11); + } + IOResult res = transport_->WriteFully(data_.data(), data_.size()); + return res == IOResult::kOk; + } + + private: + void PushInt32(int32_t data) { + data = HostToNetwork(data); + PushData(&data, sizeof(data)); + } + void PushInt16(int16_t data) { + data = HostToNetwork(data); + PushData(&data, sizeof(data)); + } + void PushByte(jbyte data) { + data_.push_back(HostToNetwork(data)); + } + + void PushData(void* d, size_t size) { + uint8_t* bytes = reinterpret_cast(d); + data_.insert(data_.end(), bytes, bytes + size); + } + + FdForwardTransport* transport_; + const jdwpPacket* pkt_; + std::vector data_; +}; + +jdwpTransportError FdForwardTransport::WritePacket(const jdwpPacket* pkt) { + if (pkt == nullptr) { + return ERR(ILLEGAL_ARGUMENT); + } + PacketWriter writer(this, pkt); + if (writer.WriteFully()) { + return OK; + } else { + return ERR(IO_ERROR); + } +} + +jboolean FdForwardTransport::IsOpen() { + return state_ == TransportState::kOpen; +} + +void* FdForwardTransport::Alloc(size_t s) { + return mem_.alloc(s); +} + +void FdForwardTransport::Free(void* data) { + mem_.free(data); +} + +jdwpTransportError FdForwardTransport::GetLastError(/*out*/char** err) { + std::string data = global_last_error_; + *err = reinterpret_cast(Alloc(data.size() + 1)); + strcpy(*err, data.c_str()); + return OK; +} + +static FdForwardTransport* AsFdForward(jdwpTransportEnv* env) { + return reinterpret_cast(env); +} + +static jdwpTransportError ParseAddress(const std::string& addr, + /*out*/int* listen_sock) { + if (!android::base::ParseInt(addr.c_str(), listen_sock) || *listen_sock < 0) { + LOG(ERROR) << "address format is not " << addr; + return ERR(ILLEGAL_ARGUMENT); + } + return OK; +} + +class JdwpTransportFunctions { + public: + static jdwpTransportError GetCapabilities(jdwpTransportEnv* env ATTRIBUTE_UNUSED, + /*out*/ JDWPTransportCapabilities* capabilities_ptr) { + // We don't support any of the optional capabilities (can_timeout_attach, can_timeout_accept, + // can_timeout_handshake) so just return a zeroed capabilities ptr. + // TODO We should maybe support these timeout options. + memset(capabilities_ptr, 0, sizeof(JDWPTransportCapabilities)); + return OK; + } + + // Address is + static jdwpTransportError Attach(jdwpTransportEnv* env, + const char* address, + jlong attach_timeout ATTRIBUTE_UNUSED, + jlong handshake_timeout ATTRIBUTE_UNUSED) { + if (address == nullptr || *address == '\0') { + return ERR(ILLEGAL_ARGUMENT); + } + int listen_fd; + jdwpTransportError err = ParseAddress(address, &listen_fd); + if (err != OK) { + return err; + } + return AsFdForward(env)->PerformAttach(listen_fd); + } + + static jdwpTransportError StartListening(jdwpTransportEnv* env, + const char* address, + /*out*/ char** actual_address) { + if (address == nullptr || *address == '\0') { + return ERR(ILLEGAL_ARGUMENT); + } + int listen_fd; + jdwpTransportError err = ParseAddress(address, &listen_fd); + if (err != OK) { + return err; + } + err = AsFdForward(env)->SetupListen(listen_fd); + if (err != OK) { + return err; + } + if (actual_address != nullptr) { + *actual_address = reinterpret_cast(AsFdForward(env)->Alloc(strlen(address) + 1)); + memcpy(*actual_address, address, strlen(address) + 1); + } + return OK; + } + + static jdwpTransportError StopListening(jdwpTransportEnv* env) { + return AsFdForward(env)->StopListening(); + } + + static jdwpTransportError Accept(jdwpTransportEnv* env, + jlong accept_timeout ATTRIBUTE_UNUSED, + jlong handshake_timeout ATTRIBUTE_UNUSED) { + return AsFdForward(env)->Accept(); + } + + static jboolean IsOpen(jdwpTransportEnv* env) { + return AsFdForward(env)->IsOpen(); + } + + static jdwpTransportError Close(jdwpTransportEnv* env) { + return AsFdForward(env)->Close(); + } + + static jdwpTransportError ReadPacket(jdwpTransportEnv* env, jdwpPacket *pkt) { + return AsFdForward(env)->ReadPacket(pkt); + } + + static jdwpTransportError WritePacket(jdwpTransportEnv* env, const jdwpPacket* pkt) { + return AsFdForward(env)->WritePacket(pkt); + } + + static jdwpTransportError GetLastError(jdwpTransportEnv* env, char** error) { + return AsFdForward(env)->GetLastError(error); + } +}; + +// The actual struct holding all the entrypoints into the jdwpTransport interface. +const jdwpTransportNativeInterface_ gTransportInterface = { + nullptr, // reserved1 + JdwpTransportFunctions::GetCapabilities, + JdwpTransportFunctions::Attach, + JdwpTransportFunctions::StartListening, + JdwpTransportFunctions::StopListening, + JdwpTransportFunctions::Accept, + JdwpTransportFunctions::IsOpen, + JdwpTransportFunctions::Close, + JdwpTransportFunctions::ReadPacket, + JdwpTransportFunctions::WritePacket, + JdwpTransportFunctions::GetLastError, +}; + +extern "C" +JNIEXPORT jint JNICALL jdwpTransport_OnLoad(JavaVM* vm ATTRIBUTE_UNUSED, + jdwpTransportCallback* cb, + jint version, + jdwpTransportEnv** /*out*/env) { + if (version != JDWPTRANSPORT_VERSION_1_0) { + LOG(ERROR) << "unknown version " << version; + return JNI_EVERSION; + } + void* data = cb->alloc(sizeof(FdForwardTransport)); + if (data == nullptr) { + LOG(ERROR) << "Failed to allocate data for transport!"; + return JNI_ENOMEM; + } + FdForwardTransport* transport = + new (data) FdForwardTransport(cb); + transport->functions = &gTransportInterface; + *env = transport; + return JNI_OK; +} + +} // namespace dt_fd_forward diff --git a/dt_fd_forward/dt_fd_forward.h b/dt_fd_forward/dt_fd_forward.h new file mode 100644 index 0000000000..9303c59acd --- /dev/null +++ b/dt_fd_forward/dt_fd_forward.h @@ -0,0 +1,154 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jdwpTransport.h. This implementation + * is licensed under the same terms as the file jdwpTransport.h. The + * copyright and license information for the file jdwpTranport.h follows. + * + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_DT_FD_FORWARD_DT_FD_FORWARD_H_ +#define ART_DT_FD_FORWARD_DT_FD_FORWARD_H_ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "fd_transport.h" + +namespace dt_fd_forward { + +static constexpr uint8_t kReplyFlag = 0x80; +// Macro and constexpr to make error values less annoying to write. +#define ERR(e) JDWPTRANSPORT_ERROR_ ## e +static constexpr jdwpTransportError OK = ERR(NONE); + +static constexpr const char kJdwpHandshake[14] = { + 'J', 'D', 'W', 'P', '-', 'H', 'a', 'n', 'd', 's', 'h', 'a', 'k', 'e' +}; // "JDWP-Handshake" + +enum class TransportState { + kClosed, // Main state. + kListenSetup, // Transient, wait for the state to change before proceeding. + kListening, // Main state. + kOpening, // Transient, wait for the state to change before proceeding. + kOpen, // Main state. +}; + +enum class IOResult { + kOk, kInterrupt, kError, kEOF, +}; + +class PacketReader; +class PacketWriter; + +// TODO It would be good to get the thread-safety analysis checks working but first we would need to +// use something other than std::mutex which does not have the annotations required. +class FdForwardTransport : public jdwpTransportEnv { + public: + explicit FdForwardTransport(jdwpTransportCallback* cb); + ~FdForwardTransport(); + + jdwpTransportError PerformAttach(int listen_fd); + jdwpTransportError SetupListen(int listen_fd); + jdwpTransportError StopListening(); + + jboolean IsOpen(); + + jdwpTransportError WritePacket(const jdwpPacket* pkt); + jdwpTransportError ReadPacket(jdwpPacket* pkt); + jdwpTransportError Close(); + jdwpTransportError Accept(); + jdwpTransportError GetLastError(/*out*/char** description); + + void* Alloc(size_t data); + void Free(void* data); + + private: + void SetLastError(const std::string& desc); + + bool ChangeState(TransportState old_state, TransportState new_state); // REQUIRES(state_mutex_); + + IOResult ReceiveFdsFromSocket(); + + IOResult WriteFully(const void* data, size_t ndata); // REQUIRES(!state_mutex_); + IOResult WriteFullyWithoutChecks(const void* data, size_t ndata); // REQUIRES(state_mutex_); + IOResult ReadFully(void* data, size_t ndata); // REQUIRES(!state_mutex_); + IOResult ReadUpToMax(void* data, size_t ndata, /*out*/size_t* amount_read); + // REQUIRES(state_mutex_); + IOResult ReadFullyWithoutChecks(void* data, size_t ndata); // REQUIRES(state_mutex_); + + void CloseFdsLocked(); // REQUIRES(state_mutex_) + + // The allocation/deallocation functions. + jdwpTransportCallback mem_; + + // Input from the server; + android::base::unique_fd read_fd_; // GUARDED_BY(state_mutex_); + // Output to the server; + android::base::unique_fd write_fd_; // GUARDED_BY(state_mutex_); + + // an eventfd passed with the write_fd to the transport that we will 'read' from to get a lock on + // the write_fd_. The other side must not hold it for unbounded time. + android::base::unique_fd write_lock_fd_; // GUARDED_BY(state_mutex_); + + // Eventfd we will use to wake-up paused reads for close(). + android::base::unique_fd wakeup_fd_; + + // Socket we will get the read/write fd's from. + android::base::unique_fd listen_fd_; + + // Fd we will write close notification to. This is a dup of listen_fd_. + android::base::unique_fd close_notify_fd_; + + TransportState state_; // GUARDED_BY(state_mutex_); + + std::mutex state_mutex_; + std::condition_variable state_cv_; + + // A counter that we use to make sure we don't do half a read on one and half on another fd. + std::atomic current_seq_num_; + + friend class PacketReader; // For ReadFullyWithInterrupt + friend class PacketWriter; // For WriteFullyWithInterrupt +}; + +} // namespace dt_fd_forward + +#endif // ART_DT_FD_FORWARD_DT_FD_FORWARD_H_ diff --git a/dt_fd_forward/export/Android.bp b/dt_fd_forward/export/Android.bp new file mode 100644 index 0000000000..c3a63212b9 --- /dev/null +++ b/dt_fd_forward/export/Android.bp @@ -0,0 +1,22 @@ +// +// 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. +// + +cc_library_headers { + name: "dt_fd_forward_export", + export_include_dirs: [ "." ], + host_supported: true, + device_supported: true, +} diff --git a/dt_fd_forward/export/MODULE_LICENSE_APACHE2 b/dt_fd_forward/export/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/dt_fd_forward/export/fd_transport.h b/dt_fd_forward/export/fd_transport.h new file mode 100644 index 0000000000..245f0c2275 --- /dev/null +++ b/dt_fd_forward/export/fd_transport.h @@ -0,0 +1,69 @@ +// 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_DT_FD_FORWARD_EXPORT_FD_TRANSPORT_H_ +#define ART_DT_FD_FORWARD_EXPORT_FD_TRANSPORT_H_ + +#include + +namespace dt_fd_forward { + +// The file-descriptors sent over a socket to the dt_fd_forward transport. +struct FdSet { + // A fd that can be read from which provides the JDWP data. + int read_fd_; + + // A fd that can be written to in order to provide JDWP responses and events. + int write_fd_; + + // A eventfd that can be locked to ensure that writes to write_fd_ are atomic. This must be held + // when writing to write_fd_. This allows the proxy to insert packets into the response stream + // without having to parse it. + int write_lock_fd_; + + static constexpr size_t kDataLength = sizeof(int) * 3; + void WriteData(void* buf) { + int* ibuf = reinterpret_cast(buf); + ibuf[0] = read_fd_; + ibuf[1] = write_fd_; + ibuf[2] = write_lock_fd_; + } + + static FdSet ReadData(void* buf) { + int* ibuf = reinterpret_cast(buf); + return FdSet { ibuf[0], ibuf[1], ibuf[2] }; + } +}; + +// This message is sent over the fd associated with the transport when we are listening for fds. +static constexpr char kListenStartMessage[] = "dt_fd_forward:START-LISTEN"; + +// This message is sent over the fd associated with the transport when we stop listening for fds. +static constexpr char kListenEndMessage[] = "dt_fd_forward:END-LISTEN"; + +// This message is sent over the fd associated with the transport when we have accepted a +// connection. This is sent before any handshaking has occurred. It is simply an acknowledgment +// that the FdSet has been received. This will be paired with a single CLOSING message when these +// fds are closed. +static constexpr char kAcceptMessage[] = "dt_fd_forward:ACCEPTED"; + +// This message is sent over the fd associated with the transport when we are closing the fds. This +// can be used by the proxy to send additional data on a dup'd fd. The write_lock_fd_ will be held +// until the other two fds are closed and then it will be released and closed. +static constexpr char kCloseMessage[] = "dt_fd_forward:CLOSING"; + +} // namespace dt_fd_forward + +#endif // ART_DT_FD_FORWARD_EXPORT_FD_TRANSPORT_H_ diff --git a/tools/dt_fds_forward.py b/tools/dt_fds_forward.py new file mode 100755 index 0000000000..516b7fef96 --- /dev/null +++ b/tools/dt_fds_forward.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +# +# 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. + +""" +A python program that simulates the plugin side of the dt_fd_forward transport for testing. + +This program will invoke a given java language runtime program and send down debugging arguments +that cause it to use the dt_fd_forward transport. This will then create a normal server-port that +debuggers can attach to. +""" + +import argparse +import array +from multiprocessing import Process +import contextlib +import ctypes +import os +import select +import socket +import subprocess +import sys +import time + +LISTEN_START_MESSAGE = b"dt_fd_forward:START-LISTEN\x00" +LISTEN_END_MESSAGE = b"dt_fd_forward:END-LISTEN\x00" +ACCEPTED_MESSAGE = b"dt_fd_forward:ACCEPTED\x00" +CLOSE_MESSAGE = b"dt_fd_forward:CLOSING\x00" + +libc = ctypes.cdll.LoadLibrary("libc.so.6") +def eventfd(init_val, flags): + """ + Creates an eventfd. See 'man 2 eventfd' for more information. + """ + return libc.eventfd(init_val, flags) + +@contextlib.contextmanager +def make_eventfd(init): + """ + Creates an eventfd with given initial value that is closed after the manager finishes. + """ + fd = eventfd(init, 0) + yield fd + os.close(fd) + +@contextlib.contextmanager +def make_sockets(): + """ + Make a (remote,local) socket pair. The remote socket is inheritable by forked processes. They are + both linked together. + """ + (rfd, lfd) = socket.socketpair(socket.AF_UNIX, socket.SOCK_SEQPACKET) + yield (rfd, lfd) + rfd.close() + lfd.close() + +def send_fds(sock, remote_read, remote_write, remote_event): + """ + Send the three fds over the given socket. + """ + sock.sendmsg([b"!"], # We don't actually care what we send here. + [(socket.SOL_SOCKET, # Send over socket. + socket.SCM_RIGHTS, # Payload is file-descriptor array + array.array('i', [remote_read, remote_write, remote_event]))]) + + +def HandleSockets(host, port, local_sock, finish_event): + """ + Handle the IO between the network and the runtime. + + This is similar to what we will do with the plugin that controls the jdwp connection. + + The main difference is it will keep around the connection and event-fd in order to let it send + ddms packets directly. + """ + listening = False + with socket.socket() as sock: + sock.bind((host, port)) + sock.listen() + while True: + sources = [local_sock, finish_event, sock] + print("Starting select on " + str(sources)) + (rf, _, _) = select.select(sources, [], []) + if local_sock in rf: + buf = local_sock.recv(1024) + print("Local_sock has data: " + str(buf)) + if buf == LISTEN_START_MESSAGE: + print("listening on " + str(sock)) + listening = True + elif buf == LISTEN_END_MESSAGE: + print("End listening") + listening = False + elif buf == ACCEPTED_MESSAGE: + print("Fds were accepted.") + elif buf == CLOSE_MESSAGE: + # TODO Dup the fds and send a fake DDMS message like the actual plugin would. + print("Fds were closed") + else: + print("Unknown data received from socket " + str(buf)) + return + elif sock in rf: + (conn, addr) = sock.accept() + with conn: + print("connection accepted from " + str(addr)) + if listening: + with make_eventfd(1) as efd: + print("sending fds ({}, {}, {}) to target.".format(conn.fileno(), conn.fileno(), efd)) + send_fds(local_sock, conn.fileno(), conn.fileno(), efd) + else: + print("Closing fds since we cannot accept them.") + if finish_event in rf: + print("woke up from finish_event") + return + +def StartChildProcess(cmd_pre, cmd_post, jdwp_lib, jdwp_ops, remote_sock, can_be_runtest): + """ + Open the child java-language runtime process. + """ + full_cmd = list(cmd_pre) + os.set_inheritable(remote_sock.fileno(), True) + jdwp_arg = jdwp_lib + "=" + \ + jdwp_ops + "transport=dt_fd_forward,address=" + str(remote_sock.fileno()) + if can_be_runtest and cmd_pre[0].endswith("run-test"): + print("Assuming run-test. Pass --no-run-test if this isn't true") + full_cmd += ["--with-agent", jdwp_arg] + else: + full_cmd.append("-agentpath:" + jdwp_arg) + full_cmd += cmd_post + print("Running " + str(full_cmd)) + # Start the actual process with the fd being passed down. + proc = subprocess.Popen(full_cmd, close_fds=False) + # Get rid of the extra socket. + remote_sock.close() + proc.wait() + +def main(): + parser = argparse.ArgumentParser(description=""" + Runs a socket that forwards to dt_fds. + + Pass '--' to start passing in the program we will pass the debug + options down to. + """) + parser.add_argument("--host", type=str, default="localhost", + help="Host we will listen for traffic on. Defaults to 'localhost'.") + parser.add_argument("--debug-lib", type=str, default="libjdwp.so", + help="jdwp library we pass to -agentpath:. Default is 'libjdwp.so'") + parser.add_argument("--debug-options", type=str, default="server=y,suspend=y,", + help="non-address options we pass to jdwp agent, default is " + + "'server=y,suspend=y,'") + parser.add_argument("--port", type=int, default=12345, + help="port we will expose the traffic on. Defaults to 12345.") + parser.add_argument("--no-run-test", default=False, action="store_true", + help="don't pass in arguments for run-test even if it looks like that is " + + "the program") + parser.add_argument("--pre-end", type=int, default=1, + help="number of 'rest' arguments to put before passing in the debug options") + end_idx = 0 if '--' not in sys.argv else sys.argv.index('--') + if end_idx == 0 and ('--help' in sys.argv or '-h' in sys.argv): + parser.print_help() + return + args = parser.parse_args(sys.argv[:end_idx][1:]) + rest = sys.argv[1 + end_idx:] + + with make_eventfd(0) as wakeup_event: + with make_sockets() as (remote_sock, local_sock): + invoker = Process(target=StartChildProcess, + args=(rest[:args.pre_end], + rest[args.pre_end:], + args.debug_lib, + args.debug_options, + remote_sock, + not args.no_run_test)) + socket_handler = Process(target=HandleSockets, + args=(args.host, args.port, local_sock, wakeup_event)) + socket_handler.start() + invoker.start() + invoker.join() + # Write any 64 bit value to the wakeup_event to make sure that the socket handler will wake + # up and exit. + os.write(wakeup_event, b'\x00\x00\x00\x00\x00\x00\x01\x00') + socket_handler.join() + +if __name__ == '__main__': + main() -- GitLab From fbf9670f31d09c47078d43fd85ee2bda23273d26 Mon Sep 17 00:00:00 2001 From: Alex Light Date: Thu, 14 Dec 2017 13:27:13 -0800 Subject: [PATCH 214/226] Add adbconnection plugin This plugin will take care of creating and managing a debugger connection through the adb daemon. This involves sending DDMS messages from the runtime, loading the JDWP agent when required, and setting up the connection. We need this since DDMS packets can be sent even if there has not been a full handshake done with the JDWP agent. Add an 'adbconnection' value to '-XjdwpProvider:...' to allow one to request that the adbconnection plugin be used to provide JDWP functionality. Bug: 62821960 Test: Manual, Flash walleye, debug apps Change-Id: Id9bed589b7c5e3830e6cdfbfee460b091459a27b --- Android.bp | 1 + Android.mk | 2 + adbconnection/Android.bp | 77 ++++ adbconnection/adbconnection.cc | 639 +++++++++++++++++++++++++++++++++ adbconnection/adbconnection.h | 155 ++++++++ cmdline/cmdline_parser_test.cc | 5 + cmdline/cmdline_types.h | 3 + runtime/jdwp_provider.h | 1 + runtime/runtime.cc | 5 + 9 files changed, 888 insertions(+) create mode 100644 adbconnection/Android.bp create mode 100644 adbconnection/adbconnection.cc create mode 100644 adbconnection/adbconnection.h diff --git a/Android.bp b/Android.bp index 2400115663..197860694b 100644 --- a/Android.bp +++ b/Android.bp @@ -20,6 +20,7 @@ art_static_dependencies = [ ] subdirs = [ + "adbconnection", "benchmark", "build", "cmdline", diff --git a/Android.mk b/Android.mk index 174cde36ef..cb0b709107 100644 --- a/Android.mk +++ b/Android.mk @@ -370,6 +370,7 @@ LOCAL_REQUIRED_MODULES := \ libopenjdkjvmti \ patchoat \ profman \ + libadbconnection \ # For nosy apps, we provide a fake library that avoids namespace issues and gives some warnings. LOCAL_REQUIRED_MODULES += libart_fake @@ -395,6 +396,7 @@ LOCAL_REQUIRED_MODULES += \ libopenjdkjvmtid \ patchoatd \ profmand \ + libadbconnectiond \ endif endif diff --git a/adbconnection/Android.bp b/adbconnection/Android.bp new file mode 100644 index 0000000000..b2f82dd97b --- /dev/null +++ b/adbconnection/Android.bp @@ -0,0 +1,77 @@ +// +// 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. +// + +// Build variants {target,host} x {debug,ndebug} x {32,64} + +cc_defaults { + name: "adbconnection-defaults", + host_supported: true, + srcs: ["adbconnection.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", + ], + }, + host: { + }, + }, + header_libs: [ + "libnativehelper_header_only", + "dt_fd_forward_export", + ], + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, + symlink_preferred_arch: true, + required: [ + "libjdwp", + "libdt_fd_forward", + ], +} + +art_cc_library { + name: "libadbconnection", + defaults: ["adbconnection-defaults"], + shared_libs: [ + "libart", + ], +} + +art_cc_library { + name: "libadbconnectiond", + defaults: [ + "art_debug_defaults", + "adbconnection-defaults", + ], + shared_libs: [ + "libartd", + ], +} diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc new file mode 100644 index 0000000000..2a9982a6e4 --- /dev/null +++ b/adbconnection/adbconnection.cc @@ -0,0 +1,639 @@ +/* + * 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. + */ + +#include + +#include "adbconnection.h" + +#include "android-base/endian.h" +#include "android-base/stringprintf.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "java_vm_ext.h" +#include "jni_env_ext.h" +#include "mirror/throwable.h" +#include "nativehelper/ScopedLocalRef.h" +#include "runtime-inl.h" +#include "runtime_callbacks.h" +#include "scoped_thread_state_change-inl.h" +#include "well_known_classes.h" + +#include "jdwp/jdwp_priv.h" + +#include "fd_transport.h" + +#include "poll.h" + +#ifdef ART_TARGET_ANDROID +#include "cutils/sockets.h" +#endif + +#include +#include +#include +#include + +namespace adbconnection { + +using dt_fd_forward::kListenStartMessage; +using dt_fd_forward::kListenEndMessage; +using dt_fd_forward::kAcceptMessage; +using dt_fd_forward::kCloseMessage; + +using android::base::StringPrintf; + +static constexpr int kEventfdLocked = 0; +static constexpr int kEventfdUnlocked = 1; +static constexpr int kControlSockSendTimeout = 10; + +static AdbConnectionState* gState; + +static bool IsDebuggingPossible() { + // TODO We need to do this on IsJdwpAllowed not IsDebuggable in order to support userdebug + // workloads. For now we will only allow it when we are debuggable so that testing is easier. + return art::Runtime::Current()->IsJavaDebuggable() && art::Dbg::IsJdwpAllowed(); +} + +// Begin running the debugger. +void AdbConnectionDebuggerController::StartDebugger() { + if (IsDebuggingPossible()) { + connection_->StartDebuggerThreads(); + } else { + LOG(ERROR) << "Not starting debugger since process cannot load the jdwp agent."; + } +} + +// The debugger should begin shutting down since the runtime is ending. We don't actually do +// anything here. The real shutdown has already happened as far as the agent is concerned. +void AdbConnectionDebuggerController::StopDebugger() { } + +bool AdbConnectionDebuggerController::IsDebuggerConfigured() { + return IsDebuggingPossible() && !art::Runtime::Current()->GetJdwpOptions().empty(); +} + +void AdbConnectionDdmCallback::DdmPublishChunk(uint32_t type, + const art::ArrayRef& data) { + connection_->PublishDdmData(type, data); +} + +class ScopedEventFdLock { + public: + explicit ScopedEventFdLock(int fd) : fd_(fd), data_(0) { + TEMP_FAILURE_RETRY(read(fd_, &data_, sizeof(data_))); + } + + ~ScopedEventFdLock() { + TEMP_FAILURE_RETRY(write(fd_, &data_, sizeof(data_))); + } + + private: + int fd_; + uint64_t data_; +}; + +AdbConnectionState::AdbConnectionState(const std::string& agent_name) + : agent_name_(agent_name), + controller_(this), + ddm_callback_(this), + sleep_event_fd_(-1), + control_sock_(-1), + local_agent_control_sock_(-1), + remote_agent_control_sock_(-1), + adb_connection_socket_(-1), + adb_write_event_fd_(-1), + shutting_down_(false), + agent_loaded_(false), + agent_listening_(false), + next_ddm_id_(1) { + // Setup the addr. + control_addr_.controlAddrUn.sun_family = AF_UNIX; + control_addr_len_ = sizeof(control_addr_.controlAddrUn.sun_family) + sizeof(kJdwpControlName) - 1; + memcpy(control_addr_.controlAddrUn.sun_path, kJdwpControlName, sizeof(kJdwpControlName) - 1); + + // Add the startup callback. + art::ScopedObjectAccess soa(art::Thread::Current()); + art::Runtime::Current()->GetRuntimeCallbacks()->AddDebuggerControlCallback(&controller_); +} + +static jobject CreateAdbConnectionThread(art::Thread* thr) { + JNIEnv* env = thr->GetJniEnv(); + // Move to native state to talk with the jnienv api. + art::ScopedThreadStateChange stsc(thr, art::kNative); + ScopedLocalRef thr_name(env, env->NewStringUTF(kAdbConnectionThreadName)); + ScopedLocalRef thr_group( + env, + env->GetStaticObjectField(art::WellKnownClasses::java_lang_ThreadGroup, + art::WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup)); + return env->NewObject(art::WellKnownClasses::java_lang_Thread, + art::WellKnownClasses::java_lang_Thread_init, + thr_group.get(), + thr_name.get(), + /*Priority*/ 0, + /*Daemon*/ true); +} + +struct CallbackData { + AdbConnectionState* this_; + jobject thr_; +}; + +static void* CallbackFunction(void* vdata) { + std::unique_ptr data(reinterpret_cast(vdata)); + art::Thread* self = art::Thread::Attach(kAdbConnectionThreadName, + true, + data->thr_); + CHECK(self != nullptr) << "threads_being_born_ should have ensured thread could be attached."; + // The name in Attach() is only for logging. Set the thread name. This is important so + // that the thread is no longer seen as starting up. + { + art::ScopedObjectAccess soa(self); + self->SetThreadName(kAdbConnectionThreadName); + } + + // Release the peer. + JNIEnv* env = self->GetJniEnv(); + env->DeleteGlobalRef(data->thr_); + data->thr_ = nullptr; + { + // The StartThreadBirth was called in the parent thread. We let the runtime know we are up + // before going into the provided code. + art::MutexLock mu(self, *art::Locks::runtime_shutdown_lock_); + art::Runtime::Current()->EndThreadBirth(); + } + data->this_->RunPollLoop(self); + int detach_result = art::Runtime::Current()->GetJavaVM()->DetachCurrentThread(); + CHECK_EQ(detach_result, 0); + + return nullptr; +} + +void AdbConnectionState::StartDebuggerThreads() { + // First do all the final setup we need. + CHECK_EQ(adb_write_event_fd_.get(), -1); + CHECK_EQ(sleep_event_fd_.get(), -1); + CHECK_EQ(local_agent_control_sock_.get(), -1); + CHECK_EQ(remote_agent_control_sock_.get(), -1); + + sleep_event_fd_.reset(eventfd(kEventfdLocked, EFD_CLOEXEC)); + CHECK_NE(sleep_event_fd_.get(), -1) << "Unable to create wakeup eventfd."; + adb_write_event_fd_.reset(eventfd(kEventfdUnlocked, EFD_CLOEXEC)); + CHECK_NE(adb_write_event_fd_.get(), -1) << "Unable to create write-lock eventfd."; + + { + art::ScopedObjectAccess soa(art::Thread::Current()); + art::Runtime::Current()->GetRuntimeCallbacks()->AddDdmCallback(&ddm_callback_); + } + // Setup the socketpair we use to talk to the agent. + bool has_sockets; + do { + has_sockets = android::base::Socketpair(AF_UNIX, + SOCK_SEQPACKET | SOCK_CLOEXEC, + 0, + &local_agent_control_sock_, + &remote_agent_control_sock_); + } while (!has_sockets && errno == EINTR); + if (!has_sockets) { + PLOG(FATAL) << "Unable to create socketpair for agent control!"; + } + + // Next start the threads. + art::Thread* self = art::Thread::Current(); + art::ScopedObjectAccess soa(self); + { + art::Runtime* runtime = art::Runtime::Current(); + art::MutexLock mu(self, *art::Locks::runtime_shutdown_lock_); + if (runtime->IsShuttingDownLocked()) { + // The runtime is shutting down so we cannot create new threads. This shouldn't really happen. + LOG(ERROR) << "The runtime is shutting down when we are trying to start up the debugger!"; + return; + } + runtime->StartThreadBirth(); + } + ScopedLocalRef thr(soa.Env(), CreateAdbConnectionThread(soa.Self())); + pthread_t pthread; + std::unique_ptr data(new CallbackData { this, soa.Env()->NewGlobalRef(thr.get()) }); + int pthread_create_result = pthread_create(&pthread, + nullptr, + &CallbackFunction, + data.get()); + if (pthread_create_result != 0) { + // If the create succeeded the other thread will call EndThreadBirth. + art::Runtime* runtime = art::Runtime::Current(); + soa.Env()->DeleteGlobalRef(data->thr_); + LOG(ERROR) << "Failed to create thread for adb-jdwp connection manager!"; + art::MutexLock mu(art::Thread::Current(), *art::Locks::runtime_shutdown_lock_); + runtime->EndThreadBirth(); + return; + } + data.release(); +} + +static bool FlagsSet(int16_t data, int16_t flags) { + return (data & flags) == flags; +} + +void AdbConnectionState::CloseFds() { + // Lock the write_event_fd so that concurrent PublishDdms will see that the connection is closed. + ScopedEventFdLock lk(adb_write_event_fd_); + // shutdown(adb_connection_socket_, SHUT_RDWR); + adb_connection_socket_.reset(); +} + +uint32_t AdbConnectionState::NextDdmId() { + // Just have a normal counter but always set the sign bit. + return (next_ddm_id_++) | 0x80000000; +} + +void AdbConnectionState::PublishDdmData(uint32_t type, const art::ArrayRef& data) { + // Get the write_event early to fail fast. + ScopedEventFdLock lk(adb_write_event_fd_); + if (adb_connection_socket_ == -1) { + LOG(WARNING) << "Not sending ddms data of type " + << StringPrintf("%c%c%c%c", + static_cast(type >> 24), + static_cast(type >> 16), + static_cast(type >> 8), + static_cast(type)) << " due to no connection!"; + // Adb is not connected. + return; + } + + // the adb_write_event_fd_ will ensure that the adb_connection_socket_ will not go away until + // after we have sent our data. + static constexpr uint32_t kDdmPacketHeaderSize = + kJDWPHeaderLen // jdwp command packet size + + sizeof(uint32_t) // Type + + sizeof(uint32_t); // length + std::array pkt; + uint8_t* pkt_data = pkt.data(); + + // Write the length first. + *reinterpret_cast(pkt_data) = htonl(kDdmPacketHeaderSize + data.size()); + pkt_data += sizeof(uint32_t); + + // Write the id next; + *reinterpret_cast(pkt_data) = htonl(NextDdmId()); + pkt_data += sizeof(uint32_t); + + // next the flags. (0 for cmd packet because DDMS). + *(pkt_data++) = 0; + // Now the cmd-set + *(pkt_data++) = kJDWPDdmCmdSet; + // Now the command + *(pkt_data++) = kJDWPDdmCmd; + + // now the type. + *reinterpret_cast(pkt_data) = htonl(type); + pkt_data += sizeof(uint32_t); + + // Now the data.size() + *reinterpret_cast(pkt_data) = htonl(data.size()); + pkt_data += sizeof(uint32_t); + + static uint32_t constexpr kIovSize = 2; + struct iovec iovs[kIovSize] = { + { pkt.data(), pkt.size() }, + { const_cast(data.data()), data.size() }, + }; + // now pkt_header has the header. + // use writev to send the actual data. + ssize_t res = TEMP_FAILURE_RETRY(writev(adb_connection_socket_, iovs, kIovSize)); + if (static_cast(res) != (kDdmPacketHeaderSize + data.size())) { + PLOG(ERROR) << StringPrintf("Failed to send DDMS packet %c%c%c%c to debugger (%zd of %zu)", + static_cast(type >> 24), + static_cast(type >> 16), + static_cast(type >> 8), + static_cast(type), + res, data.size() + kDdmPacketHeaderSize); + } else { + VLOG(jdwp) << StringPrintf("sent DDMS packet %c%c%c%c to debugger %zu", + static_cast(type >> 24), + static_cast(type >> 16), + static_cast(type >> 8), + static_cast(type), + data.size() + kDdmPacketHeaderSize); + } +} + +void AdbConnectionState::SendAgentFds() { + // TODO + DCHECK(!sent_agent_fds_); + char dummy = '!'; + union { + cmsghdr cm; + char buffer[CMSG_SPACE(dt_fd_forward::FdSet::kDataLength)]; + } cm_un; + iovec iov; + iov.iov_base = &dummy; + iov.iov_len = 1; + + msghdr msg; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = cm_un.buffer; + msg.msg_controllen = sizeof(cm_un.buffer); + + cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(dt_fd_forward::FdSet::kDataLength); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + + // Duplicate the fds before sending them. + android::base::unique_fd read_fd(dup(adb_connection_socket_)); + CHECK_NE(read_fd.get(), -1) << "Failed to dup read_fd_: " << strerror(errno); + android::base::unique_fd write_fd(dup(adb_connection_socket_)); + CHECK_NE(write_fd.get(), -1) << "Failed to dup write_fd: " << strerror(errno); + android::base::unique_fd write_lock_fd(dup(adb_write_event_fd_)); + CHECK_NE(write_lock_fd.get(), -1) << "Failed to dup write_lock_fd: " << strerror(errno); + + dt_fd_forward::FdSet { + read_fd.get(), write_fd.get(), write_lock_fd.get() + }.WriteData(CMSG_DATA(cmsg)); + + int res = TEMP_FAILURE_RETRY(sendmsg(local_agent_control_sock_, &msg, MSG_EOR)); + if (res < 0) { + PLOG(ERROR) << "Failed to send agent adb connection fds."; + } else { + sent_agent_fds_ = true; + VLOG(jdwp) << "Fds have been sent to jdwp agent!"; + } +} + +android::base::unique_fd AdbConnectionState::ReadFdFromAdb() { + // We don't actually care about the data that is sent. We do need to receive something though. + char dummy = '!'; + union { + cmsghdr cm; + char buffer[CMSG_SPACE(sizeof(int))]; + } cm_un; + + iovec iov; + iov.iov_base = &dummy; + iov.iov_len = 1; + + msghdr msg; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = cm_un.buffer; + msg.msg_controllen = sizeof(cm_un.buffer); + + cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = msg.msg_controllen; + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + (reinterpret_cast(CMSG_DATA(cmsg)))[0] = -1; + + int rc = TEMP_FAILURE_RETRY(recvmsg(control_sock_, &msg, 0)); + + if (rc <= 0) { + PLOG(WARNING) << "Receiving file descriptor from ADB failed (socket " << control_sock_ << ")"; + return android::base::unique_fd(-1); + } else { + VLOG(jdwp) << "Fds have been received from ADB!"; + } + + return android::base::unique_fd((reinterpret_cast(CMSG_DATA(cmsg)))[0]); +} + +bool AdbConnectionState::SetupAdbConnection() { + int sleep_ms = 500; + const int sleep_max_ms = 2*1000; + char buff[sizeof(pid_t) + 1]; + + android::base::unique_fd sock(socket(AF_UNIX, SOCK_SEQPACKET, 0)); + if (sock < 0) { + PLOG(ERROR) << "Could not create ADB control socket"; + return false; + } + struct timeval timeout; + timeout.tv_sec = kControlSockSendTimeout; + timeout.tv_usec = 0; + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + snprintf(buff, sizeof(buff), "%04x", getpid()); + buff[sizeof(pid_t)] = 0; + + while (!shutting_down_) { + // If adbd isn't running, because USB debugging was disabled or + // perhaps the system is restarting it for "adb root", the + // connect() will fail. We loop here forever waiting for it + // to come back. + // + // Waking up and polling every couple of seconds is generally a + // bad thing to do, but we only do this if the application is + // debuggable *and* adbd isn't running. Still, for the sake + // of battery life, we should consider timing out and giving + // up after a few minutes in case somebody ships an app with + // the debuggable flag set. + int ret = connect(sock, &control_addr_.controlAddrPlain, control_addr_len_); + if (ret == 0) { + bool trusted = sock >= 0; +#ifdef ART_TARGET_ANDROID + // Needed for socket_peer_is_trusted. + trusted = trusted && socket_peer_is_trusted(sock); +#endif + if (!trusted) { + LOG(ERROR) << "adb socket is not trusted. Aborting connection."; + if (sock >= 0 && shutdown(sock, SHUT_RDWR)) { + PLOG(ERROR) << "trouble shutting down socket"; + } + return false; + } + /* now try to send our pid to the ADB daemon */ + ret = TEMP_FAILURE_RETRY(send(sock, buff, sizeof(pid_t), 0)); + if (ret == sizeof(pid_t)) { + LOG(INFO) << "PID " << getpid() << " send to adb"; + control_sock_ = std::move(sock); + return true; + } else { + PLOG(ERROR) << "Weird, can't send JDWP process pid to ADB. Aborting connection."; + return false; + } + } else { + PLOG(ERROR) << "Can't connect to ADB control socket. Will retry."; + + usleep(sleep_ms * 1000); + + sleep_ms += (sleep_ms >> 1); + if (sleep_ms > sleep_max_ms) { + sleep_ms = sleep_max_ms; + } + } + } + return false; +} + +void AdbConnectionState::RunPollLoop(art::Thread* self) { + CHECK_EQ(self->GetState(), art::kNative); + art::Locks::mutator_lock_->AssertNotHeld(self); + self->SetState(art::kWaitingInMainDebuggerLoop); + // shutting_down_ set by StopDebuggerThreads + while (!shutting_down_) { + // First get the control_sock_ from adb if we don't have one. We only need to do this once. + if (control_sock_ == -1 && !SetupAdbConnection()) { + LOG(ERROR) << "Failed to setup adb connection."; + return; + } + while (!shutting_down_ && control_sock_ != -1) { + struct pollfd pollfds[4] = { + { sleep_event_fd_, POLLIN, 0 }, + // -1 as an fd causes it to be ignored by poll + { (agent_loaded_ ? local_agent_control_sock_ : -1), POLLIN, 0 }, + // Check for the control_sock_ actually going away. Only do this if we don't have an active + // connection. + { (adb_connection_socket_ == -1 ? control_sock_ : -1), POLLIN | POLLRDHUP, 0 }, + // if we have not loaded the agent either the adb_connection_socket_ is -1 meaning we don't + // have a real connection yet or the socket through adb needs to be listened to for incoming + // data that the agent can handle. + { ((!agent_has_socket_ && !sent_agent_fds_) ? adb_connection_socket_ : -1), POLLIN, 0 } + }; + int res = TEMP_FAILURE_RETRY(poll(pollfds, 4, -1)); + if (res < 0) { + PLOG(ERROR) << "Failed to poll!"; + return; + } + // We don't actually care about doing this we just use it to wake us up. + // const struct pollfd& sleep_event_poll = pollfds[0]; + const struct pollfd& agent_control_sock_poll = pollfds[1]; + const struct pollfd& control_sock_poll = pollfds[2]; + const struct pollfd& adb_socket_poll = pollfds[3]; + if (FlagsSet(agent_control_sock_poll.revents, POLLIN)) { + DCHECK(agent_loaded_); + char buf[257]; + res = TEMP_FAILURE_RETRY(recv(local_agent_control_sock_, buf, sizeof(buf) - 1, 0)); + if (res < 0) { + PLOG(ERROR) << "Failed to read message from agent control socket! Retrying"; + continue; + } else { + buf[res + 1] = '\0'; + VLOG(jdwp) << "Local agent control sock has data: " << static_cast(buf); + } + if (memcmp(kListenStartMessage, buf, sizeof(kListenStartMessage)) == 0) { + agent_listening_ = true; + if (adb_connection_socket_ != -1) { + SendAgentFds(); + } + } else if (memcmp(kListenEndMessage, buf, sizeof(kListenEndMessage)) == 0) { + agent_listening_ = false; + } else if (memcmp(kCloseMessage, buf, sizeof(kCloseMessage)) == 0) { + CloseFds(); + agent_has_socket_ = false; + } else if (memcmp(kAcceptMessage, buf, sizeof(kAcceptMessage)) == 0) { + agent_has_socket_ = true; + sent_agent_fds_ = false; + } else { + LOG(ERROR) << "Unknown message received from debugger! '" << std::string(buf) << "'"; + } + } else if (FlagsSet(control_sock_poll.revents, POLLIN)) { + bool maybe_send_fds = false; + { + // Hold onto this lock so that concurrent ddm publishes don't try to use an illegal fd. + ScopedEventFdLock sefdl(adb_write_event_fd_); + android::base::unique_fd new_fd(ReadFdFromAdb()); + if (new_fd == -1) { + // Something went wrong. We need to retry getting the control socket. + PLOG(ERROR) << "Something went wrong getting fds from adb. Retry!"; + control_sock_.reset(); + break; + } else if (adb_connection_socket_ != -1) { + // We already have a connection. + VLOG(jdwp) << "Ignoring second debugger. Accept then drop!"; + if (new_fd >= 0) { + new_fd.reset(); + } + } else { + VLOG(jdwp) << "Adb connection established with fd " << new_fd; + adb_connection_socket_ = std::move(new_fd); + maybe_send_fds = true; + } + } + if (maybe_send_fds && agent_loaded_ && agent_listening_) { + VLOG(jdwp) << "Sending fds as soon as we received them."; + SendAgentFds(); + } + } else if (FlagsSet(control_sock_poll.revents, POLLRDHUP)) { + // The other end of the adb connection just dropped it. + // Reset the connection since we don't have an active socket through the adb server. + DCHECK(!agent_has_socket_) << "We shouldn't be doing anything if there is already a " + << "connection active"; + control_sock_.reset(); + break; + } else if (FlagsSet(adb_socket_poll.revents, POLLIN)) { + DCHECK(!agent_has_socket_); + if (!agent_loaded_) { + DCHECK(!agent_listening_); + // Load the agent now! + self->AssertNoPendingException(); + art::Runtime::Current()->AttachAgent(MakeAgentArg()); + if (self->IsExceptionPending()) { + LOG(ERROR) << "Failed to load agent " << agent_name_; + art::ScopedObjectAccess soa(self); + self->GetException()->Dump(); + self->ClearException(); + return; + } + agent_loaded_ = true; + } else if (agent_listening_ && !sent_agent_fds_) { + VLOG(jdwp) << "Sending agent fds again on data."; + SendAgentFds(); + } + } else { + VLOG(jdwp) << "Woke up poll without anything to do!"; + } + } + } +} + +std::string AdbConnectionState::MakeAgentArg() { + // TODO Get this from something user settable? + const std::string& opts = art::Runtime::Current()->GetJdwpOptions(); + return agent_name_ + "=" + opts + (opts.empty() ? "" : ",") + + "transport=dt_fd_forward,address=" + std::to_string(remote_agent_control_sock_); +} + +void AdbConnectionState::StopDebuggerThreads() { + // The regular agent system will take care of unloading the agent (if needed). + shutting_down_ = true; + // Wakeup the poll loop. + uint64_t data = 1; + TEMP_FAILURE_RETRY(write(sleep_event_fd_, &data, sizeof(data))); +} + +// The plugin initialization function. +extern "C" bool ArtPlugin_Initialize() REQUIRES_SHARED(art::Locks::mutator_lock_) { + DCHECK(art::Runtime::Current()->GetJdwpProvider() == art::JdwpProvider::kAdbConnection); + // TODO Provide some way for apps to set this maybe? + gState = new AdbConnectionState(kDefaultJdwpAgentName); + CHECK(gState != nullptr); + return true; +} + +extern "C" bool ArtPlugin_Deinitialize() { + CHECK(gState != nullptr); + // Just do this a second time? + // TODO I don't think this should be needed. + gState->StopDebuggerThreads(); + delete gState; + return true; +} + +} // namespace adbconnection diff --git a/adbconnection/adbconnection.h b/adbconnection/adbconnection.h new file mode 100644 index 0000000000..28a5a05af3 --- /dev/null +++ b/adbconnection/adbconnection.h @@ -0,0 +1,155 @@ +/* + * 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_ADBCONNECTION_ADBCONNECTION_H_ +#define ART_ADBCONNECTION_ADBCONNECTION_H_ + +#include +#include +#include + +#include "android-base/unique_fd.h" + +#include "base/mutex.h" +#include "runtime_callbacks.h" + +#include +#include +#include + +namespace adbconnection { + +static constexpr char kJdwpControlName[] = "\0jdwp-control"; +static constexpr char kAdbConnectionThreadName[] = "ADB-JDWP Connection Control Thread"; + +// The default jdwp agent name. +static constexpr char kDefaultJdwpAgentName[] = "libjdwp.so"; + +class AdbConnectionState; + +struct AdbConnectionDebuggerController : public art::DebuggerControlCallback { + explicit AdbConnectionDebuggerController(AdbConnectionState* connection) + : connection_(connection) {} + + // Begin running the debugger. + void StartDebugger() OVERRIDE; + + // The debugger should begin shutting down since the runtime is ending. + void StopDebugger() OVERRIDE; + + bool IsDebuggerConfigured() OVERRIDE; + + private: + AdbConnectionState* connection_; +}; + +struct AdbConnectionDdmCallback : public art::DdmCallback { + explicit AdbConnectionDdmCallback(AdbConnectionState* connection) : connection_(connection) {} + + void DdmPublishChunk(uint32_t type, + const art::ArrayRef& data) + REQUIRES_SHARED(art::Locks::mutator_lock_); + + private: + AdbConnectionState* connection_; +}; + +class AdbConnectionState { + public: + explicit AdbConnectionState(const std::string& agent_name); + + // Called on the listening thread to start dealing with new input. thr is used to attach the new + // thread to the runtime. + void RunPollLoop(art::Thread* self); + + // Sends ddms data over the socket, if there is one. This data is sent even if we haven't finished + // hand-shaking yet. + void PublishDdmData(uint32_t type, const art::ArrayRef& data); + + // Stops debugger threads during shutdown. + void StopDebuggerThreads(); + + private: + uint32_t NextDdmId(); + + void StartDebuggerThreads(); + + // Tell adbd about the new runtime. + bool SetupAdbConnection(); + + std::string MakeAgentArg(); + + android::base::unique_fd ReadFdFromAdb(); + + void SendAgentFds(); + + void CloseFds(); + + std::string agent_name_; + + AdbConnectionDebuggerController controller_; + AdbConnectionDdmCallback ddm_callback_; + + // Eventfd used to allow the StopDebuggerThreads function to wake up sleeping threads + android::base::unique_fd sleep_event_fd_; + + // Socket that we use to talk to adbd. + android::base::unique_fd control_sock_; + + // Socket that we use to talk to the agent (if it's loaded). + android::base::unique_fd local_agent_control_sock_; + + // The fd of the socket the agent uses to talk to us. We need to keep it around in order to clean + // it up when the runtime goes away. + android::base::unique_fd remote_agent_control_sock_; + + // The fd that is forwarded through adb to the client. This is guarded by the + // adb_write_event_fd_. + android::base::unique_fd adb_connection_socket_; + + // The fd we send to the agent to let us synchronize access to the shared adb_connection_socket_. + // This is also used as a general lock for the adb_connection_socket_ on any threads other than + // the poll thread. + android::base::unique_fd adb_write_event_fd_; + + std::atomic shutting_down_; + + // True if we have loaded the agent library. + std::atomic agent_loaded_; + + // True if the dt_fd_forward transport is listening for a new communication channel. + std::atomic agent_listening_; + + // True if the dt_fd_forward transport has the socket. If so we don't do anything to the agent or + // the adb connection socket until connection goes away. + std::atomic agent_has_socket_; + + std::atomic sent_agent_fds_; + + std::atomic next_ddm_id_; + + socklen_t control_addr_len_; + union { + sockaddr_un controlAddrUn; + sockaddr controlAddrPlain; + } control_addr_; + + friend struct AdbConnectionDebuggerController; +}; + +} // namespace adbconnection + +#endif // ART_ADBCONNECTION_ADBCONNECTION_H_ diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index 802200013d..5d672061df 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -388,6 +388,11 @@ TEST_F(CmdlineParserTest, TestJdwpProviderNone) { EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kNone, opt_args, M::JdwpProvider); } // TEST_F +TEST_F(CmdlineParserTest, TestJdwpProviderAdbconnection) { + const char* opt_args = "-XjdwpProvider:adbconnection"; + EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kAdbConnection, opt_args, M::JdwpProvider); +} // TEST_F + TEST_F(CmdlineParserTest, TestJdwpProviderHelp) { EXPECT_SINGLE_PARSE_FAIL("-XjdwpProvider:help", CmdlineResult::kUsage); } // TEST_F diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index 5c887f8a73..529fe2bcdb 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -75,10 +75,13 @@ struct CmdlineType : CmdlineTypeParser { return Result::Usage( "Example: -XjdwpProvider:none to disable JDWP\n" "Example: -XjdwpProvider:internal for internal jdwp implementation\n" + "Example: -XjdwpProvider:adbconnection for adb connection mediated jdwp implementation\n" "Example: -XjdwpProvider:default for the default jdwp implementation" " (currently internal)\n"); } else if (option == "internal" || option == "default") { return Result::Success(JdwpProvider::kInternal); + } else if (option == "adbconnection") { + return Result::Success(JdwpProvider::kAdbConnection); } else if (option == "none") { return Result::Success(JdwpProvider::kNone); } else { diff --git a/runtime/jdwp_provider.h b/runtime/jdwp_provider.h index 849ba211fe..b62e10b4f8 100644 --- a/runtime/jdwp_provider.h +++ b/runtime/jdwp_provider.h @@ -27,6 +27,7 @@ namespace art { enum class JdwpProvider { kNone, kInternal, + kAdbConnection, }; std::ostream& operator<<(std::ostream& os, const JdwpProvider& rhs); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 9c527e7fcd..a172392c51 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1241,6 +1241,11 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { } break; } + case JdwpProvider::kAdbConnection: { + constexpr const char* plugin_name = kIsDebugBuild ? "libadbconnectiond.so" + : "libadbconnection.so"; + plugins_.push_back(Plugin::Create(plugin_name)); + } } callbacks_->AddThreadLifecycleCallback(Dbg::GetThreadLifecycleCallback()); callbacks_->AddClassLoadCallback(Dbg::GetClassLoadCallback()); -- GitLab From 7314ad133572b52335d7232361e86bd6b8cc5f50 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 19 Dec 2017 18:59:29 +0000 Subject: [PATCH 215/226] Revert "Don't embed the dex code in the oat file if dex is uncompressed." This reverts commit ae7e83817e546848ef6b2949dd9065b153e14316. Reason for revert: Broken wrt/ preopted apps and stripping Bug: 63920015 Bug: 70777774 Change-Id: I39580684d46fa57bd780d2d8bedd65a47d58cf5e Test: m (cherry picked from commit e166e67666bf4b23e4ed0a98f5e2bb3cae9cee7d) --- build/Android.gtest.mk | 19 +-------- compiler/driver/compiler_driver.cc | 6 --- compiler/driver/compiler_driver.h | 6 +-- dex2oat/dex2oat.cc | 12 +++--- dex2oat/dex2oat_test.cc | 15 ------- dex2oat/linker/image_test.h | 12 +++--- dex2oat/linker/oat_writer.cc | 68 ++++-------------------------- dex2oat/linker/oat_writer.h | 6 +-- dex2oat/linker/oat_writer_test.cc | 8 ++-- runtime/dex_file.h | 4 -- runtime/dex_file_loader.cc | 2 +- runtime/oat_file.cc | 50 +++++++++------------- runtime/oat_file.h | 9 ---- runtime/zip_archive.cc | 7 +-- runtime/zip_archive.h | 3 +- 15 files changed, 51 insertions(+), 176 deletions(-) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 0023cb9e91..230b2665e6 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -72,11 +72,6 @@ $(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval $(call build-art-test-dex,art-gte ART_TEST_HOST_GTEST_MainStripped_DEX := $(basename $(ART_TEST_HOST_GTEST_Main_DEX))Stripped$(suffix $(ART_TEST_HOST_GTEST_Main_DEX)) ART_TEST_TARGET_GTEST_MainStripped_DEX := $(basename $(ART_TEST_TARGET_GTEST_Main_DEX))Stripped$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX)) -# Create rules for MainUncompressed, a copy of Main with the classes.dex uncompressed -# for the dex2oat tests. -ART_TEST_HOST_GTEST_MainUncompressed_DEX := $(basename $(ART_TEST_HOST_GTEST_Main_DEX))Uncompressed$(suffix $(ART_TEST_HOST_GTEST_Main_DEX)) -ART_TEST_TARGET_GTEST_MainUncompressed_DEX := $(basename $(ART_TEST_TARGET_GTEST_Main_DEX))Uncompressed$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX)) - $(ART_TEST_HOST_GTEST_MainStripped_DEX): $(ART_TEST_HOST_GTEST_Main_DEX) cp $< $@ $(call dexpreopt-remove-classes.dex,$@) @@ -85,16 +80,6 @@ $(ART_TEST_TARGET_GTEST_MainStripped_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX) cp $< $@ $(call dexpreopt-remove-classes.dex,$@) -$(ART_TEST_HOST_GTEST_MainUncompressed_DEX): $(ART_TEST_HOST_GTEST_Main_DEX) $(ZIPALIGN) - cp $< $@ - $(call uncompress-dexs, $@) - $(call align-package, $@) - -$(ART_TEST_TARGET_GTEST_MainUncompressed_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX) $(ZIPALIGN) - cp $< $@ - $(call uncompress-dexs, $@) - $(call align-package, $@) - ART_TEST_GTEST_VerifierDeps_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDeps/*.smali)) ART_TEST_GTEST_VerifierDepsMulti_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDepsMulti/*.smali)) ART_TEST_HOST_GTEST_VerifierDeps_DEX := $(dir $(ART_TEST_HOST_GTEST_Main_DEX))$(subst Main,VerifierDeps,$(basename $(notdir $(ART_TEST_HOST_GTEST_Main_DEX))))$(suffix $(ART_TEST_HOST_GTEST_Main_DEX)) @@ -125,7 +110,7 @@ ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods Prof ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested 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 +ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ManyMethods Statics VerifierDeps 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_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods @@ -721,8 +706,6 @@ $(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 := ART_TEST_TARGET_GTEST_MainStripped_DEX := -ART_TEST_HOST_GTEST_MainUncompressed_DEX := -ART_TEST_TARGET_GTEST_MainUncompressed_DEX := ART_TEST_GTEST_VerifierDeps_SRC := ART_TEST_HOST_GTEST_VerifierDeps_DEX := ART_TEST_TARGET_GTEST_VerifierDeps_DEX := diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 1c65df8bb5..0631c0f12c 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -402,12 +402,6 @@ static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel( Thread* self, const CompilerDriver& driver, Handle class_loader, const DexFile& dex_file, const DexFile::ClassDef& class_def) REQUIRES_SHARED(Locks::mutator_lock_) { - // When the dex file is uncompressed in the APK, we do not generate a copy in the .vdex - // file. As a result, dex2oat will map the dex file read-only, and we only need to check - // that to know if we can do quickening. - if (dex_file.GetContainer() != nullptr && dex_file.GetContainer()->IsReadOnly()) { - return optimizer::DexToDexCompilationLevel::kDontDexToDexCompile; - } auto* const runtime = Runtime::Current(); DCHECK(driver.GetCompilerOptions().IsQuickeningCompilationEnabled()); const char* descriptor = dex_file.GetClassDescriptor(class_def); diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 07b65438b0..e001726c95 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -102,13 +102,13 @@ class CompilerDriver { ~CompilerDriver(); - // Set dex files associated with the oat file being compiled. + // Set dex files that will be stored in the oat file after being compiled. void SetDexFilesForOatFile(const std::vector& dex_files); // Set dex files classpath. void SetClasspathDexFiles(const std::vector& dex_files); - // Get dex files associated with the the oat file being compiled. + // Get dex file that will be stored in the oat file after being compiled. ArrayRef GetDexFilesForOatFile() const { return ArrayRef(dex_files_for_oat_file_); } @@ -525,7 +525,7 @@ class CompilerDriver { bool support_boot_image_fixup_; - // List of dex files associates with the oat file. + // List of dex files that will be stored in the oat file. std::vector dex_files_for_oat_file_; CompiledMethodStorage compiled_method_storage_; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 4248b72b3a..1574cd77f4 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1551,7 +1551,7 @@ class Dex2Oat FINAL { for (size_t i = 0, size = oat_writers_.size(); i != size; ++i) { rodata_.push_back(elf_writers_[i]->StartRoData()); // Unzip or copy dex files straight to the oat file. - std::vector> opened_dex_files_map; + std::unique_ptr 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 @@ -1571,16 +1571,14 @@ class Dex2Oat FINAL { return dex2oat::ReturnCode::kOther; } dex_files_per_oat_file_.push_back(MakeNonOwningPointerVector(opened_dex_files)); - if (opened_dex_files_map.empty()) { - DCHECK(opened_dex_files.empty()); - } else { - for (std::unique_ptr& map : opened_dex_files_map) { - opened_dex_files_maps_.push_back(std::move(map)); - } + if (opened_dex_files_map != nullptr) { + opened_dex_files_maps_.push_back(std::move(opened_dex_files_map)); for (std::unique_ptr& dex_file : opened_dex_files) { dex_file_oat_index_map_.emplace(dex_file.get(), i); opened_dex_files_.push_back(std::move(dex_file)); } + } else { + DCHECK(opened_dex_files.empty()); } } } diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 74adc758d9..7e90e51c73 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -1515,19 +1515,4 @@ TEST_F(Dex2oatDedupeCode, DedupeTest) { EXPECT_LT(dedupe_size, no_dedupe_size); } -TEST_F(Dex2oatTest, UncompressedTest) { - std::unique_ptr dex(OpenTestDexFile("MainUncompressed")); - std::string out_dir = GetScratchDir(); - const std::string base_oat_name = out_dir + "/base.oat"; - GenerateOdexForTest(dex->GetLocation(), - base_oat_name, - CompilerFilter::Filter::kQuicken, - { }, - true, // expect_success - false, // use_fd - [](const OatFile& o) { - CHECK(!o.ContainsDexCode()); - }); -} - } // namespace art diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index 2c24066782..85145d3d64 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -253,7 +253,7 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, } std::vector rodata; - std::vector> opened_dex_files_maps; + std::vector> opened_dex_files_map; std::vector> opened_dex_files; // Now that we have finalized key_value_store_, start writing the oat file. for (size_t i = 0, size = oat_writers.size(); i != size; ++i) { @@ -266,7 +266,7 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, dex_file->GetLocation().c_str(), dex_file->GetLocationChecksum()); - std::vector> cur_opened_dex_files_maps; + std::unique_ptr cur_opened_dex_files_map; std::vector> cur_opened_dex_files; bool dex_files_ok = oat_writers[i]->WriteAndOpenDexFiles( vdex_files[i].GetFile(), @@ -276,14 +276,12 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, &key_value_store, /* verify */ false, // Dex files may be dex-to-dex-ed, don't verify. /* update_input_vdex */ false, - &cur_opened_dex_files_maps, + &cur_opened_dex_files_map, &cur_opened_dex_files); ASSERT_TRUE(dex_files_ok); - if (!cur_opened_dex_files_maps.empty()) { - for (std::unique_ptr& cur_map : cur_opened_dex_files_maps) { - opened_dex_files_maps.push_back(std::move(cur_map)); - } + if (cur_opened_dex_files_map != nullptr) { + opened_dex_files_map.push_back(std::move(cur_opened_dex_files_map)); for (std::unique_ptr& cur_dex_file : cur_opened_dex_files) { // dex_file_oat_index_map_.emplace(dex_file.get(), i); opened_dex_files.push_back(std::move(cur_dex_file)); diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index e594a321db..d261679ac1 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -364,7 +364,6 @@ OatWriter::OatWriter(bool compiling_boot_image, compiler_driver_(nullptr), image_writer_(nullptr), compiling_boot_image_(compiling_boot_image), - only_contains_uncompressed_zip_entries_(false), dex_files_(nullptr), vdex_size_(0u), vdex_dex_files_offset_(0u), @@ -638,7 +637,7 @@ bool OatWriter::WriteAndOpenDexFiles( SafeMap* key_value_store, bool verify, bool update_input_vdex, - /*out*/ std::vector>* opened_dex_files_map, + /*out*/ std::unique_ptr* opened_dex_files_map, /*out*/ std::vector>* opened_dex_files) { CHECK(write_state_ == WriteState::kAddingDexFileSources); @@ -647,7 +646,7 @@ bool OatWriter::WriteAndOpenDexFiles( return false; } - std::vector> dex_files_map; + std::unique_ptr dex_files_map; std::vector> dex_files; // Initialize VDEX and OAT headers. @@ -3296,28 +3295,14 @@ bool OatWriter::WriteDexFiles(OutputStream* out, File* file, bool update_input_v vdex_dex_files_offset_ = vdex_size_; - only_contains_uncompressed_zip_entries_ = true; + // Write dex files. for (OatDexFile& oat_dex_file : oat_dex_files_) { - if (!oat_dex_file.source_.IsZipEntry()) { - only_contains_uncompressed_zip_entries_ = false; - break; - } - ZipEntry* entry = oat_dex_file.source_.GetZipEntry(); - if (!entry->IsUncompressed() || !entry->IsAlignedToDexHeader()) { - only_contains_uncompressed_zip_entries_ = false; - break; - } - } - - if (!only_contains_uncompressed_zip_entries_) { - // Write dex files. - for (OatDexFile& oat_dex_file : oat_dex_files_) { - if (!WriteDexFile(out, file, &oat_dex_file, update_input_vdex)) { - return false; - } + if (!WriteDexFile(out, file, &oat_dex_file, update_input_vdex)) { + return false; } } + CloseSources(); return true; } @@ -3641,7 +3626,7 @@ bool OatWriter::WriteDexFile(OutputStream* out, bool OatWriter::OpenDexFiles( File* file, bool verify, - /*out*/ std::vector>* opened_dex_files_map, + /*out*/ std::unique_ptr* opened_dex_files_map, /*out*/ std::vector>* opened_dex_files) { TimingLogger::ScopedTiming split("OpenDexFiles", timings_); @@ -3650,43 +3635,6 @@ bool OatWriter::OpenDexFiles( return true; } - if (only_contains_uncompressed_zip_entries_) { - std::vector> dex_files; - std::vector> maps; - for (OatDexFile& oat_dex_file : oat_dex_files_) { - std::string error_msg; - MemMap* map = oat_dex_file.source_.GetZipEntry()->MapDirectlyFromFile( - oat_dex_file.dex_file_location_data_, &error_msg); - if (map == nullptr) { - LOG(ERROR) << error_msg; - return false; - } - maps.emplace_back(map); - // Now, open the dex file. - dex_files.emplace_back(DexFileLoader::Open(map->Begin(), - map->Size(), - oat_dex_file.GetLocation(), - oat_dex_file.dex_file_location_checksum_, - /* oat_dex_file */ nullptr, - verify, - verify, - &error_msg)); - if (dex_files.back() == nullptr) { - LOG(ERROR) << "Failed to open dex file from oat file. File: " << oat_dex_file.GetLocation() - << " Error: " << error_msg; - return false; - } - oat_dex_file.class_offsets_.resize(dex_files.back()->GetHeader().class_defs_size_); - } - *opened_dex_files_map = std::move(maps); - *opened_dex_files = std::move(dex_files); - CloseSources(); - return true; - } - // We could have closed the sources at the point of writing the dex files, but to - // make it consistent with the case we're not writing the dex files, we close them now. - CloseSources(); - size_t map_offset = oat_dex_files_[0].dex_file_offset_; size_t length = vdex_size_ - map_offset; @@ -3745,7 +3693,7 @@ bool OatWriter::OpenDexFiles( oat_dex_file.class_offsets_.resize(dex_files.back()->GetHeader().class_defs_size_); } - opened_dex_files_map->push_back(std::move(dex_files_map)); + *opened_dex_files_map = std::move(dex_files_map); *opened_dex_files = std::move(dex_files); return true; } diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index 3e9f4636fb..4055878b55 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -173,7 +173,7 @@ class OatWriter { SafeMap* key_value_store, bool verify, bool update_input_vdex, - /*out*/ std::vector>* opened_dex_files_map, + /*out*/ std::unique_ptr* opened_dex_files_map, /*out*/ std::vector>* opened_dex_files); bool WriteQuickeningInfo(OutputStream* vdex_out); bool WriteVerifierDeps(OutputStream* vdex_out, verifier::VerifierDeps* verifier_deps); @@ -300,7 +300,7 @@ class OatWriter { bool update_input_vdex); bool OpenDexFiles(File* file, bool verify, - /*out*/ std::vector>* opened_dex_files_map, + /*out*/ std::unique_ptr* opened_dex_files_map, /*out*/ std::vector>* opened_dex_files); size_t InitOatHeader(InstructionSet instruction_set, @@ -367,8 +367,6 @@ class OatWriter { const CompilerDriver* compiler_driver_; ImageWriter* image_writer_; const bool compiling_boot_image_; - // Whether the dex files being compiled are all uncompressed in the APK. - bool only_contains_uncompressed_zip_entries_; // note OatFile does not take ownership of the DexFiles const std::vector* dex_files_; diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 138306e23c..e9958b129a 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -187,7 +187,7 @@ class OatTest : public CommonCompilerTest { oat_file); elf_writer->Start(); OutputStream* oat_rodata = elf_writer->StartRoData(); - std::vector> opened_dex_files_maps; + std::unique_ptr opened_dex_files_map; std::vector> opened_dex_files; if (!oat_writer.WriteAndOpenDexFiles(vdex_file, oat_rodata, @@ -196,7 +196,7 @@ class OatTest : public CommonCompilerTest { &key_value_store, verify, /* update_input_vdex */ false, - &opened_dex_files_maps, + &opened_dex_files_map, &opened_dex_files)) { return false; } @@ -255,9 +255,7 @@ class OatTest : public CommonCompilerTest { return false; } - for (std::unique_ptr& map : opened_dex_files_maps) { - opened_dex_files_maps_.emplace_back(std::move(map)); - } + opened_dex_files_maps_.emplace_back(std::move(opened_dex_files_map)); for (std::unique_ptr& dex_file : opened_dex_files) { opened_dex_files_.emplace_back(dex_file.release()); } diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 6b8ab282d8..76110f2e5d 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -1036,10 +1036,6 @@ class DexFile { ALWAYS_INLINE const StandardDexFile* AsStandardDexFile() const; ALWAYS_INLINE const CompactDexFile* AsCompactDexFile() const; - DexFileContainer* GetContainer() const { - return container_.get(); - } - protected: DexFile(const uint8_t* base, size_t size, diff --git a/runtime/dex_file_loader.cc b/runtime/dex_file_loader.cc index 6d4bbf13d8..bc9276985b 100644 --- a/runtime/dex_file_loader.cc +++ b/runtime/dex_file_loader.cc @@ -379,7 +379,7 @@ std::unique_ptr DexFileLoader::OpenOneDexFileFromZip( std::unique_ptr map; if (zip_entry->IsUncompressed()) { - if (!zip_entry->IsAlignedToDexHeader()) { + if (!zip_entry->IsAlignedTo(alignof(DexFile::Header))) { // Do not mmap unaligned ZIP entries because // doing so would fail dex verification which requires 4 byte alignment. LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; " diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index c20aa117f6..32f8df7010 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -595,6 +595,14 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { dex_file_location.c_str()); return false; } + if (UNLIKELY(dex_file_offset == 0U)) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with zero dex " + "file offset", + GetLocation().c_str(), + i, + dex_file_location.c_str()); + return false; + } if (UNLIKELY(dex_file_offset > DexSize())) { *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file " "offset %u > %zu", @@ -605,36 +613,20 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { DexSize()); return false; } - const uint8_t* dex_file_pointer = nullptr; - if (UNLIKELY(dex_file_offset == 0U)) { - if (uncompressed_dex_files_ == nullptr) { - uncompressed_dex_files_.reset(new std::vector>()); - // No dex files, load it from location. - if (!DexFileLoader::Open(dex_file_location.c_str(), - dex_file_location, - /* verify */ false, - /* verify_checksum */ false, - error_msg, - uncompressed_dex_files_.get())) { - return false; - } - } - dex_file_pointer = uncompressed_dex_files_.get()->at(i)->Begin(); - } else { - if (UNLIKELY(DexSize() - dex_file_offset < sizeof(DexFile::Header))) { - *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file " - "offset %u of %zu but the size of dex file header is %zu", - GetLocation().c_str(), - i, - dex_file_location.c_str(), - dex_file_offset, - DexSize(), - sizeof(DexFile::Header)); - return false; - } - dex_file_pointer = DexBegin() + dex_file_offset; + if (UNLIKELY(DexSize() - dex_file_offset < sizeof(DexFile::Header))) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file " + "offset %u of %zu but the size of dex file header is %zu", + GetLocation().c_str(), + i, + dex_file_location.c_str(), + dex_file_offset, + DexSize(), + sizeof(DexFile::Header)); + return false; } + const uint8_t* dex_file_pointer = DexBegin() + dex_file_offset; + const bool valid_magic = DexFileLoader::IsMagicValid(dex_file_pointer); if (UNLIKELY(!valid_magic)) { *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with invalid " @@ -655,7 +647,7 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { return false; } const DexFile::Header* header = reinterpret_cast(dex_file_pointer); - if (dex_file_offset != 0 && (DexSize() - dex_file_offset < header->file_size_)) { + if (DexSize() - dex_file_offset < header->file_size_) { *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file " "offset %u and size %u truncated at %zu", GetLocation().c_str(), diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 61daa9579e..36a4d7b8fc 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -324,11 +324,6 @@ class OatFile { return vdex_.get(); } - // Whether the OatFile embeds the Dex code. - bool ContainsDexCode() const { - return uncompressed_dex_files_ == nullptr; - } - protected: OatFile(const std::string& filename, bool executable); @@ -394,10 +389,6 @@ class OatFile { // elements. std::list<> and std::deque<> satisfy this requirement, std::vector<> doesn't. mutable std::list string_cache_ GUARDED_BY(secondary_lookup_lock_); - // Cache of dex files mapped directly from a location, in case the OatFile does - // not embed the dex code. - std::unique_ptr>> uncompressed_dex_files_; - friend class gc::collector::DummyOatFile; // For modifying begin_ and end_. friend class OatClass; friend class art::OatDexFile; diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc index 279c0e4e83..f3d4d77214 100644 --- a/runtime/zip_archive.cc +++ b/runtime/zip_archive.cc @@ -27,7 +27,6 @@ #include "android-base/stringprintf.h" #include "ziparchive/zip_archive.h" -#include "dex_file.h" #include "base/bit_utils.h" #include "base/unix_file/fd_file.h" @@ -50,15 +49,11 @@ bool ZipEntry::IsUncompressed() { return zip_entry_->method == kCompressStored; } -bool ZipEntry::IsAlignedTo(size_t alignment) const { +bool ZipEntry::IsAlignedTo(size_t alignment) { DCHECK(IsPowerOfTwo(alignment)) << alignment; 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/runtime/zip_archive.h index 70518e1360..75f8757f6c 100644 --- a/runtime/zip_archive.h +++ b/runtime/zip_archive.h @@ -59,8 +59,7 @@ class ZipEntry { uint32_t GetCrc32(); bool IsUncompressed(); - bool IsAlignedTo(size_t alignment) const; - bool IsAlignedToDexHeader() const; + bool IsAlignedTo(size_t alignment); private: ZipEntry(ZipArchiveHandle handle, -- GitLab From 85baf7a5a681c73825e0e0909d3757906b4e772b Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 19 Dec 2017 21:57:43 -0800 Subject: [PATCH 216/226] ART: Add barrier to dex2oat watchdog startup Ensure that the watchdog is running before progressing. Bug: 63052624 Test: m test-art-host Change-Id: I4ada6be7e46b5ee10f9f53805fa10efb15d6de1e --- dex2oat/dex2oat.cc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 1574cd77f4..bef9d77b9a 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -41,6 +41,7 @@ #include "arch/instruction_set_features.h" #include "arch/mips/instruction_set_features_mips.h" #include "art_method-inl.h" +#include "barrier.h" #include "base/callee_save_type.h" #include "base/dumpable.h" #include "base/file_utils.h" @@ -480,7 +481,8 @@ class WatchDog { public: explicit WatchDog(int64_t timeout_in_milliseconds) - : timeout_in_milliseconds_(timeout_in_milliseconds), + : wait_barrier_(2), + timeout_in_milliseconds_(timeout_in_milliseconds), shutting_down_(false) { const char* reason = "dex2oat watch dog thread startup"; CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_init, (&mutex_, nullptr), reason); @@ -525,9 +527,14 @@ class WatchDog { static constexpr int64_t kDefaultWatchdogTimeoutInMS = kWatchdogVerifyMultiplier * kWatchDogTimeoutSeconds * 1000; + void WaitForCallBackStart(Thread* self) { + wait_barrier_.Wait(self); + } + private: static void* CallBack(void* arg) { WatchDog* self = reinterpret_cast(arg); + self->wait_barrier_.Pass(nullptr); ::art::SetThreadName("dex2oat watch dog"); self->Wait(); return nullptr; @@ -585,6 +592,8 @@ class WatchDog { pthread_attr_t attr_; pthread_t pthread_; + Barrier wait_barrier_; + const int64_t timeout_in_milliseconds_; bool shutting_down_; }; @@ -929,6 +938,8 @@ class Dex2Oat FINAL { ? parser_options->watch_dog_timeout_in_ms : WatchDog::kDefaultWatchdogTimeoutInMS; watchdog_.reset(new WatchDog(timeout)); + watchdog_->WaitForCallBackStart(nullptr); // The runtime hasn't been started, yet. So + // nullptr for current thread. } // Fill some values into the key-value store for the oat header. -- GitLab From c7547c3a52785f4a69675a88ec2a90696f26c75d Mon Sep 17 00:00:00 2001 From: Alex Light Date: Wed, 20 Dec 2017 08:05:06 -0800 Subject: [PATCH 217/226] Disable libraries using eventfd on darwin hosts. The eventfd API is not supported on darwin/MacOS. Since these libraries are not meant for host use we will simply not build them for those platforms. The libraries disabled are libdt_fd_forward and libadbconnection. Both of these libraries are used for debugging support on device only so it should not be any real issue. Test: None Change-Id: Ia1b1efc50a0bd9427640edec322db5ebd37d92a2 --- adbconnection/Android.bp | 3 +++ dt_fd_forward/Android.bp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/adbconnection/Android.bp b/adbconnection/Android.bp index b2f82dd97b..441b706556 100644 --- a/adbconnection/Android.bp +++ b/adbconnection/Android.bp @@ -37,6 +37,9 @@ cc_defaults { }, host: { }, + darwin: { + enabled: false, + }, }, header_libs: [ "libnativehelper_header_only", diff --git a/dt_fd_forward/Android.bp b/dt_fd_forward/Android.bp index 84faa08112..1ba2323a15 100644 --- a/dt_fd_forward/Android.bp +++ b/dt_fd_forward/Android.bp @@ -34,6 +34,9 @@ cc_defaults { }, host: { }, + darwin: { + enabled: false, + }, }, header_libs: [ "javavm_headers", -- GitLab From 8d840901650a4ac3c647cc0372e01d9a6eb433a9 Mon Sep 17 00:00:00 2001 From: Tomasz Wasilczyk Date: Wed, 20 Dec 2017 08:19:53 -0800 Subject: [PATCH 218/226] Use LogHelper::LogLineLowStack instead of LogMessage::LogLine. The LogLine method is being altered to include LOG_TAG. Instead of updating the usage here with the new signature, a better match for ART is selected: LogLineLowStack. Bug: 35361699 Test: it builds Change-Id: Ie6c01d3a25b097af693aa56c2e611f5261976fef --- dex2oat/dex2oat.cc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 4248b72b3a..5a2b46914a 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -538,11 +538,8 @@ class WatchDog { // it's rather easy to hang in unwinding. // LogLine also avoids ART logging lock issues, as it's really only a wrapper around // logcat logging or stderr output. - android::base::LogMessage::LogLine(__FILE__, - __LINE__, - android::base::LogId::DEFAULT, - LogSeverity::FATAL, - message.c_str()); + LogHelper::LogLineLowStack(__FILE__, __LINE__, LogSeverity::FATAL, message.c_str()); + // If we're on the host, try to dump all threads to get a sense of what's going on. This is // restricted to the host as the dump may itself go bad. // TODO: Use a double watchdog timeout, so we can enable this on-device. -- GitLab From b91f9c154a816620f01206e119f036d9d28ce683 Mon Sep 17 00:00:00 2001 From: Hans Boehm Date: Tue, 19 Dec 2017 15:01:28 -0800 Subject: [PATCH 219/226] Make GetState() handle overflowed state and 0 path to root Fix GetState() so that it deals correctly with an overflowed state in which the path to root is entirely zero. We don't try to salvage the DCHECK for now. Fix the stated invariants to be more consistent with the code. Bug: 69564627 Test: AOSP builds & runs. Host tests pass. Change-Id: Idd975f03d4292e4fc52ad7714bbb2b1b98e17f96 --- runtime/base/bit_string.h | 11 ++++++----- runtime/subtype_check_info.h | 22 ++++++++++++---------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/runtime/base/bit_string.h b/runtime/base/bit_string.h index a2164f335d..bfbe8eaf71 100644 --- a/runtime/base/bit_string.h +++ b/runtime/base/bit_string.h @@ -114,7 +114,7 @@ inline std::ostream& operator<<(std::ostream& os, const BitStringChar& bc) { /** * BitString * - * MSB LSB + * lsb (least significant bit) msb * +------------+------------+------------+-----+------------+ * | | | | | | * | Char0 | Char1 | Char2 | ... | CharN | @@ -131,8 +131,9 @@ inline std::ostream& operator<<(std::ostream& os, const BitStringChar& bc) { * "ABCDE...K" := [A,B,C,D,E, ... K] + [0]*(N-idx(K)) s.t. N >= K. * // Padded with trailing 0s to fit (N+1) bitstring chars. * MaxBitstringLen := N+1 - * StrLen(Bitstring) := MaxBitStringLen - | forall char in CharI..CharN : char == 0 AND Char(I-1) != 0 | - * // Maximum length - the # of consecutive trailing zeroes. + * StrLen(Bitstring) := I s.t. (I == 0 OR Char(I-1) != 0) + * AND forall char in CharI..CharN : char == 0 + * // = Maximum length - the # of consecutive trailing zeroes. * Bitstring[N] := CharN * Bitstring[I..N) := [CharI, CharI+1, ... CharN-1] * @@ -278,8 +279,8 @@ struct BitString { private: friend std::ostream& operator<<(std::ostream& os, const BitString& bit_string); - // Data is stored with the "highest" position in the least-significant-bit. - // As positions approach 0, the bits are stored with increasing significance. + // Data is stored with the first character in the least-significant-bit. + // Unused bits are zero. StorageType storage_; }; diff --git a/runtime/subtype_check_info.h b/runtime/subtype_check_info.h index cd579c3a5c..8320fb35a9 100644 --- a/runtime/subtype_check_info.h +++ b/runtime/subtype_check_info.h @@ -90,10 +90,13 @@ namespace art { * * Uninitialized <=> StrLen(PathToRoot) == 0 * Next == 0 + * OF == False * Initialized <=> StrLen(PathToRoot) < Depth - * Next == 0 + * Next == 1 + * OF == False * Assigned <=> StrLen(PathToRoot) == Depth - * Next > 1 + * Next >= 1 + * OF == False * Overflowed <=> OF == True * * Tree Invariants: @@ -219,7 +222,7 @@ struct SubtypeCheckInfo { // Next must be non-0 to disambiguate it from Uninitialized. child.MaybeInitNext(); - // Always clear the inherited Parent's next Value on the child. + // Always clear the inherited Parent's next Value, i.e. the child's last path entry. OverwriteNextValueFromParent(/*inout*/&child, BitStringChar{}); // The state is now Initialized | Overflowed. @@ -235,7 +238,6 @@ struct SubtypeCheckInfo { // Assign attempt. if (HasNext() && !bitstring_and_of_.overflow_) { - // Do not bother assigning if parent had overflowed. BitStringChar next = GetNext(); if (next != next.MaximumValue()) { // The parent's "next" value is now the child's latest path element. @@ -260,17 +262,16 @@ struct SubtypeCheckInfo { // Get the current state (Uninitialized, Initialized, Assigned, or Overflowed). // See the "SubtypeCheckInfo" documentation above which explains how a state is determined. State GetState() const { - if (GetBitString().IsEmpty()) { - // Empty bitstring (all 0s) -> uninitialized. - DCHECK(!bitstring_and_of_.overflow_); - return kUninitialized; - } - if (bitstring_and_of_.overflow_) { // Overflowed if and only if the OF bit was set. return kOverflowed; } + if (GetBitString().IsEmpty()) { + // Empty bitstring (all 0s) -> uninitialized. + return kUninitialized; + } + // Either Assigned or Initialized. BitString path_to_root = GetPathToRoot(); @@ -387,6 +388,7 @@ struct SubtypeCheckInfo { SetBitStringUnchecked(bs); } + // If there is a next field, set it to 1. void MaybeInitNext() { if (HasNext()) { // Clearing out the "Next" value like this -- GitLab From db3519338e2de3562e7e8147e6c0714dd64f910e Mon Sep 17 00:00:00 2001 From: Hans Boehm Date: Tue, 19 Dec 2017 15:01:28 -0800 Subject: [PATCH 220/226] Make GetState() handle overflowed state and 0 path to root Fix GetState() so that it deals correctly with an overflowed state in which the path to root is entirely zero. We don't try to salvage the DCHECK for now. Fix the stated invariants to be more consistent with the code. Bug: 69564627 Test: AOSP builds & runs. Host tests pass. Change-Id: Idd975f03d4292e4fc52ad7714bbb2b1b98e17f96 (cherry picked from commit b91f9c154a816620f01206e119f036d9d28ce683) --- runtime/base/bit_string.h | 11 ++++++----- runtime/subtype_check_info.h | 22 ++++++++++++---------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/runtime/base/bit_string.h b/runtime/base/bit_string.h index a2164f335d..bfbe8eaf71 100644 --- a/runtime/base/bit_string.h +++ b/runtime/base/bit_string.h @@ -114,7 +114,7 @@ inline std::ostream& operator<<(std::ostream& os, const BitStringChar& bc) { /** * BitString * - * MSB LSB + * lsb (least significant bit) msb * +------------+------------+------------+-----+------------+ * | | | | | | * | Char0 | Char1 | Char2 | ... | CharN | @@ -131,8 +131,9 @@ inline std::ostream& operator<<(std::ostream& os, const BitStringChar& bc) { * "ABCDE...K" := [A,B,C,D,E, ... K] + [0]*(N-idx(K)) s.t. N >= K. * // Padded with trailing 0s to fit (N+1) bitstring chars. * MaxBitstringLen := N+1 - * StrLen(Bitstring) := MaxBitStringLen - | forall char in CharI..CharN : char == 0 AND Char(I-1) != 0 | - * // Maximum length - the # of consecutive trailing zeroes. + * StrLen(Bitstring) := I s.t. (I == 0 OR Char(I-1) != 0) + * AND forall char in CharI..CharN : char == 0 + * // = Maximum length - the # of consecutive trailing zeroes. * Bitstring[N] := CharN * Bitstring[I..N) := [CharI, CharI+1, ... CharN-1] * @@ -278,8 +279,8 @@ struct BitString { private: friend std::ostream& operator<<(std::ostream& os, const BitString& bit_string); - // Data is stored with the "highest" position in the least-significant-bit. - // As positions approach 0, the bits are stored with increasing significance. + // Data is stored with the first character in the least-significant-bit. + // Unused bits are zero. StorageType storage_; }; diff --git a/runtime/subtype_check_info.h b/runtime/subtype_check_info.h index cd579c3a5c..8320fb35a9 100644 --- a/runtime/subtype_check_info.h +++ b/runtime/subtype_check_info.h @@ -90,10 +90,13 @@ namespace art { * * Uninitialized <=> StrLen(PathToRoot) == 0 * Next == 0 + * OF == False * Initialized <=> StrLen(PathToRoot) < Depth - * Next == 0 + * Next == 1 + * OF == False * Assigned <=> StrLen(PathToRoot) == Depth - * Next > 1 + * Next >= 1 + * OF == False * Overflowed <=> OF == True * * Tree Invariants: @@ -219,7 +222,7 @@ struct SubtypeCheckInfo { // Next must be non-0 to disambiguate it from Uninitialized. child.MaybeInitNext(); - // Always clear the inherited Parent's next Value on the child. + // Always clear the inherited Parent's next Value, i.e. the child's last path entry. OverwriteNextValueFromParent(/*inout*/&child, BitStringChar{}); // The state is now Initialized | Overflowed. @@ -235,7 +238,6 @@ struct SubtypeCheckInfo { // Assign attempt. if (HasNext() && !bitstring_and_of_.overflow_) { - // Do not bother assigning if parent had overflowed. BitStringChar next = GetNext(); if (next != next.MaximumValue()) { // The parent's "next" value is now the child's latest path element. @@ -260,17 +262,16 @@ struct SubtypeCheckInfo { // Get the current state (Uninitialized, Initialized, Assigned, or Overflowed). // See the "SubtypeCheckInfo" documentation above which explains how a state is determined. State GetState() const { - if (GetBitString().IsEmpty()) { - // Empty bitstring (all 0s) -> uninitialized. - DCHECK(!bitstring_and_of_.overflow_); - return kUninitialized; - } - if (bitstring_and_of_.overflow_) { // Overflowed if and only if the OF bit was set. return kOverflowed; } + if (GetBitString().IsEmpty()) { + // Empty bitstring (all 0s) -> uninitialized. + return kUninitialized; + } + // Either Assigned or Initialized. BitString path_to_root = GetPathToRoot(); @@ -387,6 +388,7 @@ struct SubtypeCheckInfo { SetBitStringUnchecked(bs); } + // If there is a next field, set it to 1. void MaybeInitNext() { if (HasNext()) { // Clearing out the "Next" value like this -- GitLab From 5e399b8715f3cb153ddb619a7c47515583799db3 Mon Sep 17 00:00:00 2001 From: Artem Serov Date: Thu, 21 Dec 2017 14:28:35 +0000 Subject: [PATCH 221/226] ART: Rename cloner_test. Rename cloner_test to superblock_cloner_test to be consistent with the test naming conventioni as a new SuperblockCloner file is arriving. Test: superblock_cloner_test.cc. Change-Id: I066a20b4599de6c59b83676bb11295135a512791 --- compiler/Android.bp | 2 +- .../{cloner_test.cc => superblock_cloner_test.cc} | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename compiler/optimizing/{cloner_test.cc => superblock_cloner_test.cc} (98%) diff --git a/compiler/Android.bp b/compiler/Android.bp index fc19b54131..164f9c1e8f 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -318,7 +318,7 @@ art_cc_test { "linker/linker_patch_test.cc", "linker/output_stream_test.cc", "optimizing/bounds_check_elimination_test.cc", - "optimizing/cloner_test.cc", + "optimizing/superblock_cloner_test.cc", "optimizing/data_type_test.cc", "optimizing/dominator_test.cc", "optimizing/find_loops_test.cc", diff --git a/compiler/optimizing/cloner_test.cc b/compiler/optimizing/superblock_cloner_test.cc similarity index 98% rename from compiler/optimizing/cloner_test.cc rename to compiler/optimizing/superblock_cloner_test.cc index d34dd81767..fd77eb81fc 100644 --- a/compiler/optimizing/cloner_test.cc +++ b/compiler/optimizing/superblock_cloner_test.cc @@ -24,9 +24,9 @@ namespace art { // 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 ClonerTest : public OptimizingUnitTest { +class SuperblockClonerTest : public OptimizingUnitTest { public: - ClonerTest() + SuperblockClonerTest() : graph_(CreateGraph()), entry_block_(nullptr), exit_block_(nullptr), parameter_(nullptr) {} void CreateBasicLoopControlFlow(/* out */ HBasicBlock** header_p, @@ -154,7 +154,7 @@ class ClonerTest : public OptimizingUnitTest { HInstruction* parameter_; }; -TEST_F(ClonerTest, IndividualInstrCloner) { +TEST_F(SuperblockClonerTest, IndividualInstrCloner) { HBasicBlock* header = nullptr; HBasicBlock* loop_body = nullptr; -- GitLab From 808c7a57bb913b13c22884f57cdacd59bf1fdb3f Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 15 Dec 2017 11:19:33 -0800 Subject: [PATCH 222/226] Make CodeItem fields private Make code item fields private and use accessors. Added a hand full of friend classes to reduce the size of the change. Changed default to be nullable and removed CreateNullable. CreateNullable was a bad API since it defaulted to the unsafe, may add a CreateNonNullable if it's important for performance. Motivation: Have a different layout for code items in cdex. Bug: 63756964 Test: test-art-host-gtest Test: test/testrunner/testrunner.py --host Test: art/tools/run-jdwp-tests.sh '--mode=host' '--variant=X32' --debug Change-Id: I42bc7435e20358682075cb6de52713b595f95bf9 --- compiler/compiler.cc | 10 +-- compiler/debug/elf_debug_info_writer.h | 12 ++-- compiler/debug/elf_debug_loc_writer.h | 5 +- compiler/dex/inline_method_analyser.cc | 11 +-- compiler/driver/compiler_driver.cc | 8 +-- compiler/exception_test.cc | 6 +- compiler/optimizing/block_builder.cc | 71 ++++++++++++------- compiler/optimizing/block_builder.h | 21 ++---- compiler/optimizing/builder.cc | 54 +++++++++----- compiler/optimizing/builder.h | 20 ++---- compiler/optimizing/inliner.cc | 16 +++-- compiler/optimizing/instruction_builder.cc | 58 ++++++++++++--- compiler/optimizing/instruction_builder.h | 30 ++------ compiler/optimizing/optimization.cc | 6 +- compiler/optimizing/optimizing_compiler.cc | 8 ++- compiler/optimizing/optimizing_unit_test.h | 4 +- dexdump/dexdump.cc | 2 +- dexdump/dexdump_cfg.cc | 39 +++++----- dexlayout/dex_ir.cc | 30 ++++---- oatdump/oatdump.cc | 3 +- openjdkjvmti/ti_breakpoint.cc | 2 +- openjdkjvmti/ti_method.cc | 28 ++++---- openjdkjvmti/ti_stack.cc | 3 +- runtime/art_method-inl.h | 12 +--- runtime/art_method.cc | 5 +- runtime/art_method.h | 11 ++- runtime/check_reference_map_visitor.h | 6 +- runtime/class_linker.cc | 9 ++- runtime/code_item_accessors-inl.h | 20 +----- runtime/code_item_accessors-no_art-inl.h | 52 +++++++------- runtime/code_item_accessors.h | 15 +--- runtime/common_dex_operations.h | 7 +- runtime/common_throws.cc | 34 ++++----- runtime/debugger.cc | 36 +++++----- runtime/dex_file.h | 9 +++ runtime/dex_file_test.cc | 12 +--- runtime/dex_file_tracking_registrar.cc | 13 ++-- .../quick/quick_fillarray_entrypoints.cc | 2 +- .../quick/quick_trampoline_entrypoints.cc | 21 +++--- runtime/instrumentation.cc | 9 ++- runtime/interpreter/interpreter.cc | 58 +++++++-------- runtime/interpreter/interpreter.h | 10 ++- runtime/interpreter/interpreter_common.cc | 12 ++-- .../interpreter/interpreter_switch_impl.cc | 30 ++++---- runtime/interpreter/interpreter_switch_impl.h | 3 +- runtime/interpreter/shadow_frame.cc | 6 +- runtime/interpreter/unstarted_runtime.cc | 4 +- runtime/interpreter/unstarted_runtime.h | 3 +- runtime/jit/jit.cc | 3 +- runtime/method_handles.cc | 33 +++++---- runtime/monitor.cc | 13 ++-- runtime/oat_file.cc | 8 +-- runtime/oat_file.h | 6 +- runtime/quick_exception_handler.cc | 9 ++- runtime/stack.cc | 28 ++++---- runtime/thread.cc | 2 +- runtime/verifier/method_verifier.cc | 12 ++-- test/466-get-live-vreg/get_live_vreg_jni.cc | 3 +- 58 files changed, 486 insertions(+), 477 deletions(-) diff --git a/compiler/compiler.cc b/compiler/compiler.cc index bb614ae7b2..47f44ff3bc 100644 --- a/compiler/compiler.cc +++ b/compiler/compiler.cc @@ -19,6 +19,7 @@ #include #include "base/macros.h" +#include "code_item_accessors-inl.h" #include "driver/compiler_driver.h" #include "optimizing/optimizing_compiler.h" #include "utils.h" @@ -46,15 +47,16 @@ bool Compiler::IsPathologicalCase(const DexFile::CodeItem& code_item, * Dalvik uses 16-bit uints for instruction and register counts. We'll limit to a quarter * of that, which also guarantees we cannot overflow our 16-bit internal Quick SSA name space. */ - if (code_item.insns_size_in_code_units_ >= UINT16_MAX / 4) { + CodeItemDataAccessor accessor(&dex_file, &code_item); + if (accessor.InsnsSizeInCodeUnits() >= UINT16_MAX / 4) { LOG(INFO) << "Method exceeds compiler instruction limit: " - << code_item.insns_size_in_code_units_ + << accessor.InsnsSizeInCodeUnits() << " in " << dex_file.PrettyMethod(method_idx); return true; } - if (code_item.registers_size_ >= UINT16_MAX / 4) { + if (accessor.RegistersSize() >= UINT16_MAX / 4) { LOG(INFO) << "Method exceeds compiler virtual register limit: " - << code_item.registers_size_ << " in " << dex_file.PrettyMethod(method_idx); + << accessor.RegistersSize() << " in " << dex_file.PrettyMethod(method_idx); return true; } return false; diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index 107ed488cd..0e11e32987 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -260,14 +260,10 @@ class ElfCompilationUnitWriter { // Write local variables. LocalInfos local_infos; - if (dex->DecodeDebugLocalInfo(accessor.RegistersSize(), - accessor.InsSize(), - accessor.InsnsSizeInCodeUnits(), - accessor.DebugInfoOffset(), - is_static, - mi->dex_method_index, - LocalInfoCallback, - &local_infos)) { + if (accessor.DecodeDebugLocalInfo(is_static, + mi->dex_method_index, + LocalInfoCallback, + &local_infos)) { for (const DexFile::LocalInfo& var : local_infos) { if (var.reg_ < accessor.RegistersSize() - accessor.InsSize()) { info_.StartTag(DW_TAG_variable); diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h index 1d609af4e6..34c2919a21 100644 --- a/compiler/debug/elf_debug_loc_writer.h +++ b/compiler/debug/elf_debug_loc_writer.h @@ -149,11 +149,12 @@ static std::vector GetVariableLocations( DCHECK_LT(stack_map_index, dex_register_maps.size()); 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, method_info->code_item->registers_size_, code_info, encoding); + vreg, accessor.RegistersSize(), code_info, encoding); if (is64bitValue) { reg_hi = dex_register_map.GetDexRegisterLocation( - vreg + 1, method_info->code_item->registers_size_, code_info, encoding); + vreg + 1, accessor.RegistersSize(), code_info, encoding); } // Add location entry for this address range. diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc index b409eb2dbb..80677b934b 100644 --- a/compiler/dex/inline_method_analyser.cc +++ b/compiler/dex/inline_method_analyser.cc @@ -141,8 +141,11 @@ bool Matcher::DoMatch(const CodeItemDataAccessor* code_item, MatchFn* const* pat ArtMethod* GetTargetConstructor(ArtMethod* method, const Instruction* invoke_direct) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK_EQ(invoke_direct->Opcode(), Instruction::INVOKE_DIRECT); - DCHECK_EQ(invoke_direct->VRegC_35c(), - method->GetCodeItem()->registers_size_ - method->GetCodeItem()->ins_size_); + if (kIsDebugBuild) { + CodeItemDataAccessor accessor(method); + DCHECK_EQ(invoke_direct->VRegC_35c(), + accessor.RegistersSize() - accessor.InsSize()); + } uint32_t method_index = invoke_direct->VRegB_35c(); ArtMethod* target_method = Runtime::Current()->GetClassLinker()->LookupResolvedMethod( method_index, method->GetDexCache(), method->GetClassLoader()); @@ -323,7 +326,7 @@ bool DoAnalyseConstructor(const CodeItemDataAccessor* code_item, if (target_method->GetDeclaringClass()->IsObjectClass()) { DCHECK_EQ(CodeItemDataAccessor(target_method).begin()->Opcode(), Instruction::RETURN_VOID); } else { - CodeItemDataAccessor target_code_item = CodeItemDataAccessor::CreateNullable(target_method); + CodeItemDataAccessor target_code_item(target_method); if (!target_code_item.HasCodeItem()) { return false; // Native constructor? } @@ -427,7 +430,7 @@ static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) == InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT), "iget/iput_short variant"); bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method, InlineMethod* result) { - CodeItemDataAccessor code_item = CodeItemDataAccessor::CreateNullable(method); + CodeItemDataAccessor code_item(method); if (!code_item.HasCodeItem()) { // Native or abstract. return false; diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 0631c0f12c..68f963e3ab 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -949,14 +949,14 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { ArtMethod* method, std::set>* exceptions_to_resolve) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile::CodeItem* code_item = method->GetCodeItem(); - if (code_item == nullptr) { + if (method->GetCodeItem() == nullptr) { return; // native or abstract method } - if (code_item->tries_size_ == 0) { + CodeItemDataAccessor accessor(method); + if (accessor.TriesSize() == 0) { return; // nothing to process } - const uint8_t* encoded_catch_handler_list = DexFile::GetCatchHandlerData(*code_item, 0); + const uint8_t* encoded_catch_handler_list = accessor.GetCatchHandlerData(); size_t num_encoded_catch_handlers = DecodeUnsignedLeb128(&encoded_catch_handler_list); for (size_t i = 0; i < num_encoded_catch_handlers; i++) { int32_t encoded_catch_handler_size = DecodeSignedLeb128(&encoded_catch_handler_list); diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc index 897b50bdac..4dbef0d799 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 "class_linker.h" +#include "code_item_accessors-inl.h" #include "common_runtime_test.h" #include "dex_file-inl.h" #include "dex_file.h" @@ -129,11 +130,12 @@ class ExceptionTest : public CommonRuntimeTest { TEST_F(ExceptionTest, FindCatchHandler) { ScopedObjectAccess soa(Thread::Current()); const DexFile::CodeItem* code_item = dex_->GetCodeItem(method_f_->GetCodeItemOffset()); + CodeItemDataAccessor accessor(dex_, code_item); ASSERT_TRUE(code_item != nullptr); - ASSERT_EQ(2u, code_item->tries_size_); - ASSERT_NE(0u, code_item->insns_size_in_code_units_); + ASSERT_EQ(2u, accessor.TriesSize()); + ASSERT_NE(0u, accessor.InsnsSizeInCodeUnits()); const DexFile::TryItem *t0, *t1; t0 = dex_->GetTryItems(*code_item, 0); diff --git a/compiler/optimizing/block_builder.cc b/compiler/optimizing/block_builder.cc index 58f591bd1e..c505efafe2 100644 --- a/compiler/optimizing/block_builder.cc +++ b/compiler/optimizing/block_builder.cc @@ -18,10 +18,31 @@ #include "base/logging.h" // FOR VLOG. #include "bytecode_utils.h" +#include "code_item_accessors-inl.h" #include "quicken_info.h" namespace art { +HBasicBlockBuilder::HBasicBlockBuilder(HGraph* graph, + const DexFile* const dex_file, + const CodeItemDebugInfoAccessor& accessor, + ScopedArenaAllocator* local_allocator) + : allocator_(graph->GetAllocator()), + graph_(graph), + dex_file_(dex_file), + code_item_accessor_(accessor), + local_allocator_(local_allocator), + branch_targets_(code_item_accessor_.HasCodeItem() + ? code_item_accessor_.InsnsSizeInCodeUnits() + : /* fake dex_pc=0 for intrinsic graph */ 1u, + nullptr, + local_allocator->Adapter(kArenaAllocGraphBuilder)), + throwing_blocks_(kDefaultNumberOfThrowingBlocks, + local_allocator->Adapter(kArenaAllocGraphBuilder)), + number_of_branches_(0u), + quicken_index_for_dex_pc_(std::less(), + local_allocator->Adapter(kArenaAllocGraphBuilder)) {} + HBasicBlock* HBasicBlockBuilder::MaybeCreateBlockAt(uint32_t dex_pc) { return MaybeCreateBlockAt(dex_pc, dex_pc); } @@ -41,20 +62,19 @@ bool HBasicBlockBuilder::CreateBranchTargets() { // Create the first block for the dex instructions, single successor of the entry block. MaybeCreateBlockAt(0u); - if (code_item_->tries_size_ != 0) { + if (code_item_accessor_.TriesSize() != 0) { // Create branch targets at the start/end of the TryItem range. These are // places where the program might fall through into/out of the a block and // where TryBoundary instructions will be inserted later. Other edges which // enter/exit the try blocks are a result of branches/switches. - for (size_t idx = 0; idx < code_item_->tries_size_; ++idx) { - const DexFile::TryItem* try_item = DexFile::GetTryItems(*code_item_, idx); - uint32_t dex_pc_start = try_item->start_addr_; - uint32_t dex_pc_end = dex_pc_start + try_item->insn_count_; + 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_; MaybeCreateBlockAt(dex_pc_start); - if (dex_pc_end < code_item_->insns_size_in_code_units_) { + if (dex_pc_end < code_item_accessor_.InsnsSizeInCodeUnits()) { // TODO: Do not create block if the last instruction cannot fall through. MaybeCreateBlockAt(dex_pc_end); - } else if (dex_pc_end == code_item_->insns_size_in_code_units_) { + } else if (dex_pc_end == code_item_accessor_.InsnsSizeInCodeUnits()) { // The TryItem spans until the very end of the CodeItem and therefore // cannot have any code afterwards. } else { @@ -65,7 +85,7 @@ bool HBasicBlockBuilder::CreateBranchTargets() { } // Create branch targets for exception handlers. - const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*code_item_, 0); + 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); @@ -78,8 +98,7 @@ bool HBasicBlockBuilder::CreateBranchTargets() { // Iterate over all instructions and find branching instructions. Create blocks for // the locations these instructions branch to. - IterationRange instructions = code_item_->Instructions(); - for (const DexInstructionPcPair& pair : instructions) { + for (const DexInstructionPcPair& pair : code_item_accessor_) { const uint32_t dex_pc = pair.DexPc(); const Instruction& instruction = pair.Inst(); @@ -109,7 +128,7 @@ bool HBasicBlockBuilder::CreateBranchTargets() { if (instruction.CanFlowThrough()) { DexInstructionIterator next(std::next(DexInstructionIterator(pair))); - if (next == instructions.end()) { + if (next == code_item_accessor_.end()) { // In the normal case we should never hit this but someone can artificially forge a dex // file to fall-through out the method code. In this case we bail out compilation. VLOG(compiler) << "Not compiled: Fall-through beyond the CodeItem"; @@ -130,7 +149,7 @@ void HBasicBlockBuilder::ConnectBasicBlocks() { bool is_throwing_block = false; // Calculate the qucikening index here instead of CreateBranchTargets since it's easier to // calculate in dex_pc order. - for (const DexInstructionPcPair& pair : code_item_->Instructions()) { + for (const DexInstructionPcPair& pair : code_item_accessor_) { const uint32_t dex_pc = pair.DexPc(); const Instruction& instruction = pair.Inst(); @@ -213,10 +232,12 @@ static const DexFile::TryItem* GetTryItem( // successors matches the order in which runtime exception delivery searches // for a handler. static void LinkToCatchBlocks(HTryBoundary* try_boundary, - const DexFile::CodeItem& code_item, + const CodeItemDataAccessor& accessor, const DexFile::TryItem* try_item, const ScopedArenaSafeMap& catch_blocks) { - for (CatchHandlerIterator it(code_item, *try_item); it.HasNext(); it.Next()) { + for (CatchHandlerIterator it(accessor.GetCatchHandlerData(try_item->handler_off_)); + it.HasNext(); + it.Next()) { try_boundary->AddExceptionHandler(catch_blocks.Get(it.GetHandlerAddress())); } } @@ -232,7 +253,7 @@ bool HBasicBlockBuilder::MightHaveLiveNormalPredecessors(HBasicBlock* catch_bloc } } - const Instruction& first = code_item_->InstructionAt(catch_block->GetDexPc()); + const Instruction& first = code_item_accessor_.InstructionAt(catch_block->GetDexPc()); if (first.Opcode() == Instruction::MOVE_EXCEPTION) { // Verifier guarantees that if a catch block begins with MOVE_EXCEPTION then // it has no live normal predecessors. @@ -250,7 +271,7 @@ bool HBasicBlockBuilder::MightHaveLiveNormalPredecessors(HBasicBlock* catch_bloc } void HBasicBlockBuilder::InsertTryBoundaryBlocks() { - if (code_item_->tries_size_ == 0) { + if (code_item_accessor_.TriesSize() == 0) { return; } @@ -272,12 +293,10 @@ void HBasicBlockBuilder::InsertTryBoundaryBlocks() { // loop for synchronized blocks. if (ContainsElement(throwing_blocks_, block)) { // Try to find a TryItem covering the block. - const int32_t try_item_idx = DexFile::FindTryItem(DexFile::GetTryItems(*code_item_, 0u), - code_item_->tries_size_, - block->GetDexPc()); - if (try_item_idx != -1) { + const DexFile::TryItem* try_item = code_item_accessor_.FindTryItem(block->GetDexPc()); + if (try_item != nullptr) { // Block throwing and in a TryItem. Store the try block information. - try_block_info.Put(block->GetBlockId(), DexFile::GetTryItems(*code_item_, try_item_idx)); + try_block_info.Put(block->GetBlockId(), try_item); } } } @@ -288,7 +307,7 @@ void HBasicBlockBuilder::InsertTryBoundaryBlocks() { // Iterate over catch blocks, create artifical landing pads if necessary to // simplify the CFG, and set metadata. - const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*code_item_, 0); + 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); @@ -336,7 +355,7 @@ void HBasicBlockBuilder::InsertTryBoundaryBlocks() { HTryBoundary* try_entry = new (allocator_) HTryBoundary( HTryBoundary::BoundaryKind::kEntry, try_block->GetDexPc()); try_block->CreateImmediateDominator()->AddInstruction(try_entry); - LinkToCatchBlocks(try_entry, *code_item_, try_item, catch_blocks); + LinkToCatchBlocks(try_entry, code_item_accessor_, try_item, catch_blocks); break; } } @@ -364,13 +383,13 @@ void HBasicBlockBuilder::InsertTryBoundaryBlocks() { HTryBoundary* try_exit = new (allocator_) HTryBoundary(HTryBoundary::BoundaryKind::kExit, successor->GetDexPc()); graph_->SplitEdge(try_block, successor)->AddInstruction(try_exit); - LinkToCatchBlocks(try_exit, *code_item_, try_item, catch_blocks); + LinkToCatchBlocks(try_exit, code_item_accessor_, try_item, catch_blocks); } } } bool HBasicBlockBuilder::Build() { - DCHECK(code_item_ != nullptr); + DCHECK(code_item_accessor_.HasCodeItem()); DCHECK(graph_->GetBlocks().empty()); graph_->SetEntryBlock(new (allocator_) HBasicBlock(graph_, kNoDexPc)); @@ -388,7 +407,7 @@ bool HBasicBlockBuilder::Build() { } void HBasicBlockBuilder::BuildIntrinsic() { - DCHECK(code_item_ == nullptr); + DCHECK(!code_item_accessor_.HasCodeItem()); DCHECK(graph_->GetBlocks().empty()); // Create blocks. diff --git a/compiler/optimizing/block_builder.h b/compiler/optimizing/block_builder.h index 7d0f56db34..e68b95c46f 100644 --- a/compiler/optimizing/block_builder.h +++ b/compiler/optimizing/block_builder.h @@ -19,6 +19,7 @@ #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" +#include "code_item_accessors.h" #include "dex_file.h" #include "nodes.h" @@ -28,22 +29,8 @@ class HBasicBlockBuilder : public ValueObject { public: HBasicBlockBuilder(HGraph* graph, const DexFile* const dex_file, - const DexFile::CodeItem* code_item, - ScopedArenaAllocator* local_allocator) - : allocator_(graph->GetAllocator()), - graph_(graph), - dex_file_(dex_file), - code_item_(code_item), - local_allocator_(local_allocator), - branch_targets_(code_item != nullptr ? code_item->insns_size_in_code_units_ - : /* fake dex_pc=0 for intrinsic graph */ 1u, - nullptr, - local_allocator->Adapter(kArenaAllocGraphBuilder)), - throwing_blocks_(kDefaultNumberOfThrowingBlocks, - local_allocator->Adapter(kArenaAllocGraphBuilder)), - number_of_branches_(0u), - quicken_index_for_dex_pc_(std::less(), - local_allocator->Adapter(kArenaAllocGraphBuilder)) {} + const CodeItemDebugInfoAccessor& accessor, + ScopedArenaAllocator* local_allocator); // Creates basic blocks in `graph_` at branch target dex_pc positions of the // `code_item_`. Blocks are connected but left unpopulated with instructions. @@ -83,7 +70,7 @@ class HBasicBlockBuilder : public ValueObject { HGraph* const graph_; const DexFile* const dex_file_; - const DexFile::CodeItem* const code_item_; // null for intrinsic graph. + CodeItemDataAccessor code_item_accessor_; // null code item for intrinsic graph. ScopedArenaAllocator* const local_allocator_; ScopedArenaVector branch_targets_; diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index d73ef1f3a1..af537dd653 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -37,7 +37,7 @@ namespace art { HGraphBuilder::HGraphBuilder(HGraph* graph, - const DexFile::CodeItem* code_item, + const CodeItemDebugInfoAccessor& accessor, const DexCompilationUnit* dex_compilation_unit, const DexCompilationUnit* outer_compilation_unit, CompilerDriver* driver, @@ -47,7 +47,7 @@ HGraphBuilder::HGraphBuilder(HGraph* graph, VariableSizedHandleScope* handles) : graph_(graph), dex_file_(&graph->GetDexFile()), - code_item_(code_item), + code_item_accessor_(accessor), dex_compilation_unit_(dex_compilation_unit), outer_compilation_unit_(outer_compilation_unit), compiler_driver_(driver), @@ -57,6 +57,23 @@ HGraphBuilder::HGraphBuilder(HGraph* graph, handles_(handles), return_type_(DataType::FromShorty(dex_compilation_unit_->GetShorty()[0])) {} +HGraphBuilder::HGraphBuilder(HGraph* graph, + const DexCompilationUnit* dex_compilation_unit, + const CodeItemDebugInfoAccessor& accessor, + VariableSizedHandleScope* handles, + DataType::Type return_type) + : graph_(graph), + dex_file_(&graph->GetDexFile()), + code_item_accessor_(accessor), + dex_compilation_unit_(dex_compilation_unit), + outer_compilation_unit_(nullptr), + compiler_driver_(nullptr), + code_generator_(nullptr), + compilation_stats_(nullptr), + interpreter_metadata_(nullptr), + handles_(handles), + return_type_(return_type) {} + bool HGraphBuilder::SkipCompilation(size_t number_of_branches) { if (compiler_driver_ == nullptr) { // Note that the compiler driver is null when unit testing. @@ -69,20 +86,20 @@ bool HGraphBuilder::SkipCompilation(size_t number_of_branches) { return false; } - if (compiler_options.IsHugeMethod(code_item_->insns_size_in_code_units_)) { + const uint32_t code_units = code_item_accessor_.InsnsSizeInCodeUnits(); + if (compiler_options.IsHugeMethod(code_units)) { VLOG(compiler) << "Skip compilation of huge method " << dex_file_->PrettyMethod(dex_compilation_unit_->GetDexMethodIndex()) - << ": " << code_item_->insns_size_in_code_units_ << " code units"; + << ": " << code_units << " code units"; MaybeRecordStat(compilation_stats_, MethodCompilationStat::kNotCompiledHugeMethod); return true; } // If it's large and contains no branches, it's likely to be machine generated initialization. - if (compiler_options.IsLargeMethod(code_item_->insns_size_in_code_units_) - && (number_of_branches == 0)) { + if (compiler_options.IsLargeMethod(code_units) && (number_of_branches == 0)) { VLOG(compiler) << "Skip compilation of large method with no branch " << dex_file_->PrettyMethod(dex_compilation_unit_->GetDexMethodIndex()) - << ": " << code_item_->insns_size_in_code_units_ << " code units"; + << ": " << code_units << " code units"; MaybeRecordStat(compilation_stats_, MethodCompilationStat::kNotCompiledLargeMethodNoBranches); return true; } @@ -91,17 +108,17 @@ bool HGraphBuilder::SkipCompilation(size_t number_of_branches) { } GraphAnalysisResult HGraphBuilder::BuildGraph() { - DCHECK(code_item_ != nullptr); + DCHECK(code_item_accessor_.HasCodeItem()); DCHECK(graph_->GetBlocks().empty()); - graph_->SetNumberOfVRegs(code_item_->registers_size_); - graph_->SetNumberOfInVRegs(code_item_->ins_size_); - graph_->SetMaximumNumberOfOutVRegs(code_item_->outs_size_); - graph_->SetHasTryCatch(code_item_->tries_size_ != 0); + graph_->SetNumberOfVRegs(code_item_accessor_.RegistersSize()); + graph_->SetNumberOfInVRegs(code_item_accessor_.InsSize()); + graph_->SetMaximumNumberOfOutVRegs(code_item_accessor_.OutsSize()); + graph_->SetHasTryCatch(code_item_accessor_.TriesSize() != 0); // Use ScopedArenaAllocator for all local allocations. ScopedArenaAllocator local_allocator(graph_->GetArenaStack()); - HBasicBlockBuilder block_builder(graph_, dex_file_, code_item_, &local_allocator); + HBasicBlockBuilder block_builder(graph_, dex_file_, code_item_accessor_, &local_allocator); SsaBuilder ssa_builder(graph_, dex_compilation_unit_->GetClassLoader(), dex_compilation_unit_->GetDexCache(), @@ -111,7 +128,7 @@ GraphAnalysisResult HGraphBuilder::BuildGraph() { &block_builder, &ssa_builder, dex_file_, - code_item_, + code_item_accessor_, return_type_, dex_compilation_unit_, outer_compilation_unit_, @@ -150,7 +167,7 @@ GraphAnalysisResult HGraphBuilder::BuildGraph() { } void HGraphBuilder::BuildIntrinsicGraph(ArtMethod* method) { - DCHECK(code_item_ == nullptr); + DCHECK(!code_item_accessor_.HasCodeItem()); DCHECK(graph_->GetBlocks().empty()); // Determine the number of arguments and associated vregs. @@ -170,7 +187,10 @@ void HGraphBuilder::BuildIntrinsicGraph(ArtMethod* method) { // Use ScopedArenaAllocator for all local allocations. ScopedArenaAllocator local_allocator(graph_->GetArenaStack()); - HBasicBlockBuilder block_builder(graph_, dex_file_, /* code_item */ nullptr, &local_allocator); + HBasicBlockBuilder block_builder(graph_, + dex_file_, + CodeItemDebugInfoAccessor(), + &local_allocator); SsaBuilder ssa_builder(graph_, dex_compilation_unit_->GetClassLoader(), dex_compilation_unit_->GetDexCache(), @@ -180,7 +200,7 @@ void HGraphBuilder::BuildIntrinsicGraph(ArtMethod* method) { &block_builder, &ssa_builder, dex_file_, - /* code_item */ nullptr, + CodeItemDebugInfoAccessor(), return_type_, dex_compilation_unit_, outer_compilation_unit_, diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 0bb3a051f7..c40e0b4e6a 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -18,6 +18,7 @@ #define ART_COMPILER_OPTIMIZING_BUILDER_H_ #include "base/arena_object.h" +#include "code_item_accessors.h" #include "dex_file-inl.h" #include "dex_file.h" #include "driver/compiler_driver.h" @@ -33,7 +34,7 @@ class OptimizingCompilerStats; class HGraphBuilder : public ValueObject { public: HGraphBuilder(HGraph* graph, - const DexFile::CodeItem* code_item, + const CodeItemDebugInfoAccessor& accessor, const DexCompilationUnit* dex_compilation_unit, const DexCompilationUnit* outer_compilation_unit, CompilerDriver* driver, @@ -45,20 +46,9 @@ class HGraphBuilder : public ValueObject { // Only for unit testing. HGraphBuilder(HGraph* graph, const DexCompilationUnit* dex_compilation_unit, - const DexFile::CodeItem& code_item, + const CodeItemDebugInfoAccessor& accessor, VariableSizedHandleScope* handles, - DataType::Type return_type = DataType::Type::kInt32) - : graph_(graph), - dex_file_(&graph->GetDexFile()), - code_item_(&code_item), - dex_compilation_unit_(dex_compilation_unit), - outer_compilation_unit_(nullptr), - compiler_driver_(nullptr), - code_generator_(nullptr), - compilation_stats_(nullptr), - interpreter_metadata_(nullptr), - handles_(handles), - return_type_(return_type) {} + DataType::Type return_type = DataType::Type::kInt32); GraphAnalysisResult BuildGraph(); void BuildIntrinsicGraph(ArtMethod* method); @@ -70,7 +60,7 @@ class HGraphBuilder : public ValueObject { HGraph* const graph_; const DexFile* const dex_file_; - const DexFile::CodeItem* const code_item_; // null for intrinsic graph. + const CodeItemDebugInfoAccessor code_item_accessor_; // null for intrinsic graph. // The compilation unit of the current method being compiled. Note that // it can be an inlined method. diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 8750910fe1..7a66d807cf 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -1381,26 +1381,26 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, bool same_dex_file = IsSameDexFile(*outer_compilation_unit_.GetDexFile(), *method->GetDexFile()); - const DexFile::CodeItem* code_item = method->GetCodeItem(); + CodeItemDataAccessor accessor(method); - if (code_item == nullptr) { + if (!accessor.HasCodeItem()) { LOG_FAIL_NO_STAT() << "Method " << method->PrettyMethod() << " is not inlined because it is native"; return false; } size_t inline_max_code_units = compiler_driver_->GetCompilerOptions().GetInlineMaxCodeUnits(); - if (code_item->insns_size_in_code_units_ > inline_max_code_units) { + if (accessor.InsnsSizeInCodeUnits() > inline_max_code_units) { LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedCodeItem) << "Method " << method->PrettyMethod() << " is not inlined because its code item is too big: " - << code_item->insns_size_in_code_units_ + << accessor.InsnsSizeInCodeUnits() << " > " << inline_max_code_units; return false; } - if (code_item->tries_size_ != 0) { + if (accessor.TriesSize() != 0) { LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedTryCatch) << "Method " << method->PrettyMethod() << " is not inlined because of try block"; return false; @@ -1660,6 +1660,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, const DexFile::CodeItem* code_item = resolved_method->GetCodeItem(); const DexFile& callee_dex_file = *resolved_method->GetDexFile(); uint32_t method_index = resolved_method->GetDexMethodIndex(); + CodeItemDebugInfoAccessor code_item_accessor(&callee_dex_file, code_item); ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker(); Handle dex_cache = NewHandleIfDifferent(resolved_method->GetDexCache(), caller_compilation_unit_.GetDexCache(), @@ -1714,7 +1715,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, } } HGraphBuilder builder(callee_graph, - code_item, + code_item_accessor, &dex_compilation_unit, &outer_compilation_unit_, compiler_driver_, @@ -1967,6 +1968,7 @@ void HInliner::RunOptimizations(HGraph* callee_graph, return; } + CodeItemDataAccessor accessor(&callee_graph->GetDexFile(), code_item); HInliner inliner(callee_graph, outermost_graph_, codegen_, @@ -1975,7 +1977,7 @@ void HInliner::RunOptimizations(HGraph* callee_graph, compiler_driver_, handles_, inline_stats_, - total_number_of_dex_registers_ + code_item->registers_size_, + total_number_of_dex_registers_ + accessor.RegistersSize(), total_number_of_instructions_ + number_of_instructions, this, depth_ + 1); diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index e36d91fb05..3f5923fa3f 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -39,6 +39,44 @@ namespace art { +HInstructionBuilder::HInstructionBuilder(HGraph* graph, + HBasicBlockBuilder* block_builder, + SsaBuilder* ssa_builder, + const DexFile* dex_file, + const CodeItemDebugInfoAccessor& accessor, + DataType::Type return_type, + const DexCompilationUnit* dex_compilation_unit, + const DexCompilationUnit* outer_compilation_unit, + CompilerDriver* compiler_driver, + CodeGenerator* code_generator, + const uint8_t* interpreter_metadata, + OptimizingCompilerStats* compiler_stats, + VariableSizedHandleScope* handles, + ScopedArenaAllocator* local_allocator) + : allocator_(graph->GetAllocator()), + graph_(graph), + handles_(handles), + dex_file_(dex_file), + code_item_accessor_(accessor), + return_type_(return_type), + block_builder_(block_builder), + ssa_builder_(ssa_builder), + compiler_driver_(compiler_driver), + code_generator_(code_generator), + dex_compilation_unit_(dex_compilation_unit), + outer_compilation_unit_(outer_compilation_unit), + quicken_info_(interpreter_metadata), + compilation_stats_(compiler_stats), + local_allocator_(local_allocator), + locals_for_(local_allocator->Adapter(kArenaAllocGraphBuilder)), + current_block_(nullptr), + current_locals_(nullptr), + latest_result_(nullptr), + current_this_parameter_(nullptr), + loop_headers_(local_allocator->Adapter(kArenaAllocGraphBuilder)) { + loop_headers_.reserve(kDefaultNumberOfLoops); +} + HBasicBlock* HInstructionBuilder::FindBlockStartingAt(uint32_t dex_pc) const { return block_builder_->GetBlockAt(dex_pc); } @@ -273,7 +311,7 @@ static bool IsBlockPopulated(HBasicBlock* block) { } bool HInstructionBuilder::Build() { - DCHECK(code_item_ != nullptr); + DCHECK(code_item_accessor_.HasCodeItem()); locals_for_.resize( graph_->GetBlocks().size(), ScopedArenaVector(local_allocator_->Adapter(kArenaAllocGraphBuilder))); @@ -323,7 +361,7 @@ bool HInstructionBuilder::Build() { quicken_index = block_builder_->GetQuickenIndex(block_dex_pc); } - for (const DexInstructionPcPair& pair : code_item_->Instructions(block_dex_pc)) { + for (const DexInstructionPcPair& pair : code_item_accessor_.InstructionsFrom(block_dex_pc)) { if (current_block_ == nullptr) { // The previous instruction ended this block. break; @@ -367,7 +405,7 @@ bool HInstructionBuilder::Build() { } void HInstructionBuilder::BuildIntrinsic(ArtMethod* method) { - DCHECK(code_item_ == nullptr); + DCHECK(!code_item_accessor_.HasCodeItem()); DCHECK(method->IsIntrinsic()); locals_for_.resize( @@ -442,15 +480,16 @@ ArenaBitVector* HInstructionBuilder::FindNativeDebugInfoLocations() { return false; } }; - CodeItemDebugInfoAccessor accessor(dex_file_, code_item_); ArenaBitVector* locations = ArenaBitVector::Create(local_allocator_, - accessor.InsnsSizeInCodeUnits(), + code_item_accessor_.InsnsSizeInCodeUnits(), /* expandable */ false, kArenaAllocGraphBuilder); locations->ClearAllBits(); - dex_file_->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), Callback::Position, locations); + dex_file_->DecodeDebugPositionInfo(code_item_accessor_.DebugInfoOffset(), + Callback::Position, + locations); // Instruction-specific tweaks. - for (const DexInstructionPcPair& inst : accessor) { + for (const DexInstructionPcPair& inst : code_item_accessor_) { switch (inst->Opcode()) { case Instruction::MOVE_EXCEPTION: { // Stop in native debugger after the exception has been moved. @@ -459,7 +498,7 @@ ArenaBitVector* HInstructionBuilder::FindNativeDebugInfoLocations() { locations->ClearBit(inst.DexPc()); DexInstructionIterator next = std::next(DexInstructionIterator(inst)); DCHECK(next.DexPc() != inst.DexPc()); - if (next != accessor.end()) { + if (next != code_item_accessor_.end()) { locations->SetBit(next.DexPc()); } break; @@ -1706,7 +1745,8 @@ void HInstructionBuilder::BuildFillArrayData(const Instruction& instruction, uin int32_t payload_offset = instruction.VRegB_31t() + dex_pc; const Instruction::ArrayDataPayload* payload = - reinterpret_cast(code_item_->insns_ + payload_offset); + reinterpret_cast( + code_item_accessor_.Insns() + payload_offset); const uint8_t* data = payload->data; uint32_t element_count = payload->element_count; diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index 0500d40cd3..b4e30516ab 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -19,6 +19,7 @@ #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" +#include "code_item_accessors.h" #include "data_type.h" #include "dex_file.h" #include "dex_file_types.h" @@ -50,7 +51,7 @@ class HInstructionBuilder : public ValueObject { HBasicBlockBuilder* block_builder, SsaBuilder* ssa_builder, const DexFile* dex_file, - const DexFile::CodeItem* code_item, + const CodeItemDebugInfoAccessor& accessor, DataType::Type return_type, const DexCompilationUnit* dex_compilation_unit, const DexCompilationUnit* outer_compilation_unit, @@ -59,30 +60,7 @@ class HInstructionBuilder : public ValueObject { const uint8_t* interpreter_metadata, OptimizingCompilerStats* compiler_stats, VariableSizedHandleScope* handles, - ScopedArenaAllocator* local_allocator) - : allocator_(graph->GetAllocator()), - graph_(graph), - handles_(handles), - dex_file_(dex_file), - code_item_(code_item), - return_type_(return_type), - block_builder_(block_builder), - ssa_builder_(ssa_builder), - compiler_driver_(compiler_driver), - code_generator_(code_generator), - dex_compilation_unit_(dex_compilation_unit), - outer_compilation_unit_(outer_compilation_unit), - quicken_info_(interpreter_metadata), - compilation_stats_(compiler_stats), - local_allocator_(local_allocator), - locals_for_(local_allocator->Adapter(kArenaAllocGraphBuilder)), - current_block_(nullptr), - current_locals_(nullptr), - latest_result_(nullptr), - current_this_parameter_(nullptr), - loop_headers_(local_allocator->Adapter(kArenaAllocGraphBuilder)) { - loop_headers_.reserve(kDefaultNumberOfLoops); - } + ScopedArenaAllocator* local_allocator); bool Build(); void BuildIntrinsic(ArtMethod* method); @@ -329,7 +307,7 @@ class HInstructionBuilder : public ValueObject { // The dex file where the method being compiled is, and the bytecode data. const DexFile* const dex_file_; - const DexFile::CodeItem* const code_item_; // null for intrinsic graph. + const CodeItemDebugInfoAccessor code_item_accessor_; // null for intrinsic graph. // The return type of the method being compiled. const DataType::Type return_type_; diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc index 7149d93d07..d8ac696d1e 100644 --- a/compiler/optimizing/optimization.cc +++ b/compiler/optimizing/optimization.cc @@ -35,6 +35,7 @@ #include "bounds_check_elimination.h" #include "cha_guard_optimization.h" +#include "code_item_accessors-inl.h" #include "code_sinking.h" #include "constant_folding.h" #include "constructor_fence_redundancy_elimination.h" @@ -241,7 +242,8 @@ ArenaVector ConstructOptimizations( opt = new (allocator) HDeadCodeElimination(graph, stats, name); break; case OptimizationPass::kInliner: { - size_t number_of_dex_registers = dex_compilation_unit.GetCodeItem()->registers_size_; + CodeItemDataAccessor accessor(dex_compilation_unit.GetDexFile(), + dex_compilation_unit.GetCodeItem()); opt = new (allocator) HInliner(graph, // outer_graph graph, // outermost_graph codegen, @@ -250,7 +252,7 @@ ArenaVector ConstructOptimizations( driver, handles, stats, - number_of_dex_registers, + accessor.RegistersSize(), /* total_number_of_instructions */ 0, /* parent */ nullptr, /* depth */ 0, diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 24b1a123ee..9d04dd8343 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -766,11 +766,13 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, static constexpr size_t kSpaceFilterOptimizingThreshold = 128; const CompilerOptions& compiler_options = compiler_driver->GetCompilerOptions(); if ((compiler_options.GetCompilerFilter() == CompilerFilter::kSpace) - && (code_item->insns_size_in_code_units_ > kSpaceFilterOptimizingThreshold)) { + && (CodeItemInstructionAccessor(&dex_file, code_item).InsnsSizeInCodeUnits() > + kSpaceFilterOptimizingThreshold)) { MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kNotCompiledSpaceFilter); return nullptr; } + CodeItemDebugInfoAccessor code_item_accessor(&dex_file, code_item); HGraph* graph = new (allocator) HGraph( allocator, arena_stack, @@ -814,7 +816,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, VLOG(compiler) << "Building " << pass_observer.GetMethodName(); PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer); HGraphBuilder builder(graph, - code_item, + code_item_accessor, &dex_compilation_unit, &dex_compilation_unit, compiler_driver, @@ -932,7 +934,7 @@ CodeGenerator* OptimizingCompiler::TryCompileIntrinsic( VLOG(compiler) << "Building intrinsic graph " << pass_observer.GetMethodName(); PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer); HGraphBuilder builder(graph, - /* code_item */ nullptr, + CodeItemDebugInfoAccessor(), // Null code item. &dex_compilation_unit, &dex_compilation_unit, compiler_driver, diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h index 158c252f45..7d05262b10 100644 --- a/compiler/optimizing/optimizing_unit_test.h +++ b/compiler/optimizing/optimizing_unit_test.h @@ -19,6 +19,7 @@ #include "base/scoped_arena_allocator.h" #include "builder.h" +#include "code_item_accessors-inl.h" #include "common_compiler_test.h" #include "dex_file.h" #include "dex_instruction.h" @@ -145,7 +146,8 @@ class OptimizingUnitTest : public CommonCompilerTest { /* access_flags */ 0u, /* verified_method */ nullptr, handles_->NewHandle(nullptr)); - HGraphBuilder builder(graph, dex_compilation_unit, *code_item, handles_.get(), return_type); + CodeItemDebugInfoAccessor accessor(&graph->GetDexFile(), code_item); + HGraphBuilder builder(graph, dex_compilation_unit, accessor, handles_.get(), return_type); bool graph_built = (builder.BuildGraph() == kAnalysisSuccess); return graph_built ? graph : nullptr; } diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index f64c89a5b0..730d4b97a0 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -735,7 +735,7 @@ static void dumpInterface(const DexFile* pDexFile, const DexFile::TypeItem& pTyp * Dumps the catches table associated with the code. */ static void dumpCatches(const DexFile* pDexFile, const DexFile::CodeItem* pCode) { - const u4 triesSize = pCode->tries_size_; + const u4 triesSize = CodeItemDataAccessor(pDexFile, pCode).TriesSize(); // No catch table. if (triesSize == 0) { diff --git a/dexdump/dexdump_cfg.cc b/dexdump/dexdump_cfg.cc index 23ecf93447..dd57a11758 100644 --- a/dexdump/dexdump_cfg.cc +++ b/dexdump/dexdump_cfg.cc @@ -25,6 +25,7 @@ #include #include +#include "code_item_accessors-no_art-inl.h" #include "dex_file-inl.h" #include "dex_instruction-inl.h" @@ -37,17 +38,17 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, os << "digraph {\n"; os << " # /* " << dex_file->PrettyMethod(dex_method_idx, true) << " */\n"; + CodeItemInstructionAccessor accessor(dex_file, code_item); + std::set dex_pc_is_branch_target; { // Go and populate. - const Instruction* inst = Instruction::At(code_item->insns_); - for (uint32_t dex_pc = 0; - dex_pc < code_item->insns_size_in_code_units_; - dex_pc += inst->SizeInCodeUnits(), inst = inst->Next()) { + for (const DexInstructionPcPair& pair : accessor) { + const Instruction* inst = &pair.Inst(); if (inst->IsBranch()) { - dex_pc_is_branch_target.insert(dex_pc + inst->GetTargetOffset()); + dex_pc_is_branch_target.insert(pair.DexPc() + inst->GetTargetOffset()); } else if (inst->IsSwitch()) { - const uint16_t* insns = code_item->insns_ + dex_pc; + const uint16_t* insns = reinterpret_cast(inst); int32_t switch_offset = insns[1] | (static_cast(insns[2]) << 16); const uint16_t* switch_insns = insns + switch_offset; uint32_t switch_count = switch_insns[1]; @@ -63,7 +64,7 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, int32_t offset = static_cast(switch_insns[targets_offset + targ * 2]) | static_cast(switch_insns[targets_offset + targ * 2 + 1] << 16); - dex_pc_is_branch_target.insert(dex_pc + offset); + dex_pc_is_branch_target.insert(pair.DexPc() + offset); } } } @@ -74,12 +75,10 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, std::map dex_pc_to_incl_id; // This has entries for all dex pcs. { - const Instruction* inst = Instruction::At(code_item->insns_); bool first_in_block = true; bool force_new_block = false; - for (uint32_t dex_pc = 0; - dex_pc < code_item->insns_size_in_code_units_; - dex_pc += inst->SizeInCodeUnits(), inst = inst->Next()) { + for (const DexInstructionPcPair& pair : accessor) { + const uint32_t dex_pc = pair.DexPc(); if (dex_pc == 0 || (dex_pc_is_branch_target.find(dex_pc) != dex_pc_is_branch_target.end()) || force_new_block) { @@ -108,7 +107,7 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, // Dump the instruction. Need to escape '"', '<', '>', '{' and '}'. os << "<" << "p" << dex_pc << ">"; os << " 0x" << std::hex << dex_pc << std::dec << ": "; - std::string inst_str = inst->DumpString(dex_file); + std::string inst_str = pair.Inst().DumpString(dex_file); size_t cur_start = 0; // It's OK to start at zero, instruction dumps don't start with chars // we need to escape. while (cur_start != std::string::npos) { @@ -137,7 +136,7 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, // Force a new block for some fall-throughs and some instructions that terminate the "local" // control flow. - force_new_block = inst->IsSwitch() || inst->IsBasicBlockEnd(); + force_new_block = pair.Inst().IsSwitch() || pair.Inst().IsBasicBlockEnd(); } // Close last node. if (dex_pc_to_node_id.size() > 0) { @@ -162,10 +161,9 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, uint32_t last_node_id = std::numeric_limits::max(); uint32_t old_dex_pc = 0; uint32_t block_start_dex_pc = std::numeric_limits::max(); - const Instruction* inst = Instruction::At(code_item->insns_); - for (uint32_t dex_pc = 0; - dex_pc < code_item->insns_size_in_code_units_; - old_dex_pc = dex_pc, dex_pc += inst->SizeInCodeUnits(), inst = inst->Next()) { + for (const DexInstructionPcPair& pair : accessor) { + const Instruction* inst = &pair.Inst(); + const uint32_t dex_pc = pair.DexPc(); { auto it = dex_pc_to_node_id.find(dex_pc); if (it != dex_pc_to_node_id.end()) { @@ -222,7 +220,7 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, } } else if (inst->IsSwitch()) { // TODO: Iterate through all switch targets. - const uint16_t* insns = code_item->insns_ + dex_pc; + const uint16_t* insns = reinterpret_cast(inst); /* make sure the start of the switch is in range */ int32_t switch_offset = insns[1] | (static_cast(insns[2]) << 16); /* offset to switch table is a relative branch-style offset */ @@ -272,6 +270,7 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, // No fall-through. last_node_id = std::numeric_limits::max(); } + old_dex_pc = pair.DexPc(); } // Finish up the last block, if it had common exceptions. if (!exception_targets.empty()) { @@ -293,7 +292,7 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, // TODO // Exception edges. If this is not the first instruction in the block for (uint32_t dex_pc : blocks_with_detailed_exceptions) { - const Instruction* inst = Instruction::At(&code_item->insns_[dex_pc]); + const Instruction* inst = &accessor.InstructionAt(dex_pc); uint32_t this_node_id = dex_pc_to_incl_id.find(dex_pc)->second; while (true) { CatchHandlerIterator catch_it(*code_item, dex_pc); @@ -322,7 +321,7 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, // Loop update. Have a break-out if the next instruction is a branch target and thus in // another block. dex_pc += inst->SizeInCodeUnits(); - if (dex_pc >= code_item->insns_size_in_code_units_) { + if (dex_pc >= accessor.InsnsSizeInCodeUnits()) { break; } if (dex_pc_to_node_id.find(dex_pc) != dex_pc_to_node_id.end()) { diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc index 90df2d7bb9..a163bd96c0 100644 --- a/dexlayout/dex_ir.cc +++ b/dexlayout/dex_ir.cc @@ -21,6 +21,8 @@ */ #include "dex_ir.h" + +#include "code_item_accessors-inl.h" #include "dex_instruction-inl.h" #include "dex_ir_builder.h" @@ -564,13 +566,14 @@ ParameterAnnotation* Collections::GenerateParameterAnnotation( CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, const DexFile::CodeItem& disk_code_item, uint32_t offset) { - uint16_t registers_size = disk_code_item.registers_size_; - uint16_t ins_size = disk_code_item.ins_size_; - uint16_t outs_size = disk_code_item.outs_size_; - uint32_t tries_size = disk_code_item.tries_size_; + CodeItemDebugInfoAccessor accessor(&dex_file, &disk_code_item); + const uint16_t registers_size = accessor.RegistersSize(); + const uint16_t ins_size = accessor.InsSize(); + const uint16_t outs_size = accessor.OutsSize(); + const uint32_t tries_size = accessor.TriesSize(); // TODO: Calculate the size of the debug info. - uint32_t debug_info_offset = dex_file.GetDebugInfoOffset(&disk_code_item); + const uint32_t debug_info_offset = accessor.DebugInfoOffset(); const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(debug_info_offset); DebugInfoItem* debug_info = nullptr; if (debug_info_stream != nullptr) { @@ -584,20 +587,19 @@ CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, } } - uint32_t insns_size = disk_code_item.insns_size_in_code_units_; + uint32_t insns_size = accessor.InsnsSizeInCodeUnits(); uint16_t* insns = new uint16_t[insns_size]; - memcpy(insns, disk_code_item.insns_, insns_size * sizeof(uint16_t)); + memcpy(insns, accessor.Insns(), insns_size * sizeof(uint16_t)); TryItemVector* tries = nullptr; CatchHandlerVector* handler_list = nullptr; if (tries_size > 0) { tries = new TryItemVector(); handler_list = new CatchHandlerVector(); - for (uint32_t i = 0; i < tries_size; ++i) { - const DexFile::TryItem* disk_try_item = dex_file.GetTryItems(disk_code_item, i); - uint32_t start_addr = disk_try_item->start_addr_; - uint16_t insn_count = disk_try_item->insn_count_; - uint16_t handler_off = disk_try_item->handler_off_; + for (const DexFile::TryItem& disk_try_item : accessor.TryItems()) { + uint32_t start_addr = disk_try_item.start_addr_; + uint16_t insn_count = disk_try_item.insn_count_; + uint16_t handler_off = disk_try_item.handler_off_; const CatchHandler* handlers = nullptr; for (std::unique_ptr& existing_handlers : *handler_list) { if (handler_off == existing_handlers->GetListOffset()) { @@ -608,7 +610,7 @@ CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, if (handlers == nullptr) { bool catch_all = false; TypeAddrPairVector* addr_pairs = new TypeAddrPairVector(); - for (CatchHandlerIterator it(disk_code_item, *disk_try_item); it.HasNext(); it.Next()) { + for (CatchHandlerIterator it(disk_code_item, disk_try_item); it.HasNext(); it.Next()) { const dex::TypeIndex type_index = it.GetHandlerTypeIndex(); const TypeId* type_id = GetTypeIdOrNullPtr(type_index.index_); catch_all |= type_id == nullptr; @@ -622,7 +624,7 @@ CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, tries->push_back(std::unique_ptr(try_item)); } // Manually walk catch handlers list and add any missing handlers unreferenced by try items. - const uint8_t* handlers_base = DexFile::GetCatchHandlerData(disk_code_item, 0); + const uint8_t* handlers_base = accessor.GetCatchHandlerData(); const uint8_t* handlers_data = handlers_base; uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_data); while (handlers_size > handler_list->size()) { diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 1a1d8cc76e..b2e19a89d6 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1261,8 +1261,7 @@ class OatDumper { bool* addr_found) { bool success = true; - CodeItemDataAccessor code_item_accessor(CodeItemDataAccessor::CreateNullable(&dex_file, - code_item)); + CodeItemDataAccessor code_item_accessor(&dex_file, code_item); // TODO: Support regex std::string method_name = dex_file.GetMethodName(dex_file.GetMethodId(dex_method_idx)); diff --git a/openjdkjvmti/ti_breakpoint.cc b/openjdkjvmti/ti_breakpoint.cc index 8e5b56e9bf..7634fa312f 100644 --- a/openjdkjvmti/ti_breakpoint.cc +++ b/openjdkjvmti/ti_breakpoint.cc @@ -98,7 +98,7 @@ jvmtiError BreakpointUtil::SetBreakpoint(jvmtiEnv* jenv, jmethodID method, jloca art::ScopedObjectAccess soa(art::Thread::Current()); art::ArtMethod* art_method = art::jni::DecodeArtMethod(method)->GetCanonicalMethod(); if (location < 0 || static_cast(location) >= - art_method->GetCodeItem()->insns_size_in_code_units_) { + art_method->DexInstructions().InsnsSizeInCodeUnits()) { return ERR(INVALID_LOCATION); } DeoptManager::Get()->AddMethodBreakpoint(art_method); diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index 4444853427..947ba7911f 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -123,19 +123,19 @@ jvmtiError MethodUtil::GetBytecodes(jvmtiEnv* env, } art::ScopedObjectAccess soa(art::Thread::Current()); - const art::DexFile::CodeItem* code_item = art_method->GetCodeItem(); - if (code_item == nullptr) { + art::CodeItemInstructionAccessor accessor(art_method); + if (!accessor.HasCodeItem()) { *size_ptr = 0; *bytecode_ptr = nullptr; return OK; } // 2 bytes per instruction for dex code. - *size_ptr = code_item->insns_size_in_code_units_ * 2; + *size_ptr = accessor.InsnsSizeInCodeUnits() * 2; jvmtiError err = env->Allocate(*size_ptr, bytecode_ptr); if (err != OK) { return err; } - memcpy(*bytecode_ptr, code_item->insns_, *size_ptr); + memcpy(*bytecode_ptr, accessor.Insns(), *size_ptr); return OK; } @@ -168,7 +168,7 @@ jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED, } DCHECK_NE(art_method->GetCodeItemOffset(), 0u); - *size_ptr = art_method->GetCodeItem()->ins_size_; + *size_ptr = art::CodeItemDataAccessor(art_method).InsSize(); return ERR(NONE); } @@ -266,14 +266,10 @@ jvmtiError MethodUtil::GetLocalVariableTable(jvmtiEnv* env, }; LocalVariableContext context(env); - if (!dex_file->DecodeDebugLocalInfo(accessor.RegistersSize(), - accessor.InsSize(), - accessor.InsnsSizeInCodeUnits(), - accessor.DebugInfoOffset(), - art_method->IsStatic(), - art_method->GetDexMethodIndex(), - LocalVariableContext::Callback, - &context)) { + if (!accessor.DecodeDebugLocalInfo(art_method->IsStatic(), + art_method->GetDexMethodIndex(), + LocalVariableContext::Callback, + &context)) { // Something went wrong with decoding the debug information. It might as well not be there. return ERR(ABSENT_INFORMATION); } else { @@ -305,7 +301,7 @@ jvmtiError MethodUtil::GetMaxLocals(jvmtiEnv* env ATTRIBUTE_UNUSED, } DCHECK_NE(art_method->GetCodeItemOffset(), 0u); - *max_ptr = art_method->GetCodeItem()->registers_size_; + *max_ptr = art::CodeItemDataAccessor(art_method).RegistersSize(); return ERR(NONE); } @@ -420,7 +416,7 @@ jvmtiError MethodUtil::GetMethodLocation(jvmtiEnv* env ATTRIBUTE_UNUSED, DCHECK_NE(art_method->GetCodeItemOffset(), 0u); *start_location_ptr = 0; - *end_location_ptr = art_method->GetCodeItem()->insns_size_in_code_units_ - 1; + *end_location_ptr = art_method->DexInstructions().InsnsSizeInCodeUnits() - 1; return ERR(NONE); } @@ -571,7 +567,7 @@ class CommonLocalVariableClosure : public art::Closure { // TODO It might be useful to fake up support for get at least on proxy frames. result_ = ERR(OPAQUE_FRAME); return; - } else if (method->GetCodeItem()->registers_size_ <= slot_) { + } else if (art::CodeItemDataAccessor(method).RegistersSize() <= slot_) { result_ = ERR(INVALID_SLOT); return; } diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc index b43eaa0286..17b4243377 100644 --- a/openjdkjvmti/ti_stack.cc +++ b/openjdkjvmti/ti_stack.cc @@ -44,6 +44,7 @@ #include "base/bit_utils.h" #include "base/enums.h" #include "base/mutex.h" +#include "code_item_accessors-inl.h" #include "dex_file.h" #include "dex_file_annotations.h" #include "dex_file_types.h" @@ -1044,7 +1045,7 @@ jvmtiError StackUtil::NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth) if (shadow_frame == nullptr) { needs_instrument = true; const size_t frame_id = visitor.GetFrameId(); - const uint16_t num_regs = method->GetCodeItem()->registers_size_; + const uint16_t num_regs = art::CodeItemDataAccessor(method).RegistersSize(); shadow_frame = target->FindOrCreateDebuggerShadowFrame(frame_id, num_regs, method, diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 869394c388..c6c4f4ae41 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -459,16 +459,8 @@ inline void ArtMethod::UpdateEntrypoints(const Visitor& visitor, PointerSize poi } } -inline IterationRange ArtMethod::DexInstructions() { - CodeItemInstructionAccessor accessor(this); - return { accessor.begin(), - accessor.end() }; -} - -inline IterationRange ArtMethod::NullableDexInstructions() { - CodeItemInstructionAccessor accessor(CodeItemInstructionAccessor::CreateNullable(this)); - return { accessor.begin(), - accessor.end() }; +inline CodeItemInstructionAccessor ArtMethod::DexInstructions() { + return CodeItemInstructionAccessor(this); } } // namespace art diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 9005120eb0..7ddaa7edc3 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -297,9 +297,8 @@ uint32_t ArtMethod::FindCatchBlock(Handle exception_type, } } if (found_dex_pc != dex::kDexNoIndex) { - const Instruction* first_catch_instr = - Instruction::At(&code_item->insns_[found_dex_pc]); - *has_no_move_exception = (first_catch_instr->Opcode() != Instruction::MOVE_EXCEPTION); + const Instruction& first_catch_instr = DexInstructions().InstructionAt(found_dex_pc); + *has_no_move_exception = (first_catch_instr.Opcode() != Instruction::MOVE_EXCEPTION); } // Put the exception back. if (exception != nullptr) { diff --git a/runtime/art_method.h b/runtime/art_method.h index f433223ebf..ab90a10f5a 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -27,6 +27,7 @@ #include "base/iteration_range.h" #include "base/macros.h" #include "base/runtime_debug.h" +#include "code_item_accessors.h" #include "dex_file.h" #include "dex_instruction_iterator.h" #include "gc_root.h" @@ -716,13 +717,9 @@ class ArtMethod FINAL { "ptr_sized_fields_.entry_point_from_quick_compiled_code_"); } - // Returns the dex instructions of the code item for the art method. Must not be called on null - // code items. - ALWAYS_INLINE IterationRange DexInstructions() - REQUIRES_SHARED(Locks::mutator_lock_); - - // Handles a null code item by returning iterators that have a null address. - ALWAYS_INLINE IterationRange NullableDexInstructions() + // Returns the dex instructions of the code item for the art method. Returns an empty array for + // the null code item case. + ALWAYS_INLINE CodeItemInstructionAccessor DexInstructions() REQUIRES_SHARED(Locks::mutator_lock_); protected: diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h index d9e89159f5..c4a613a942 100644 --- a/runtime/check_reference_map_visitor.h +++ b/runtime/check_reference_map_visitor.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_CHECK_REFERENCE_MAP_VISITOR_H_ #include "art_method-inl.h" +#include "code_item_accessors-inl.h" #include "dex_file_types.h" #include "oat_quick_method_header.h" #include "scoped_thread_state_change-inl.h" @@ -66,14 +67,15 @@ class CheckReferenceMapVisitor : public StackVisitor { CodeInfo code_info = GetCurrentOatQuickMethodHeader()->GetOptimizedCodeInfo(); CodeInfoEncoding encoding = code_info.ExtractEncoding(); StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); - uint16_t number_of_dex_registers = m->GetCodeItem()->registers_size_; + CodeItemDataAccessor accessor(m); + 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); for (int i = 0; i < number_of_references; ++i) { int reg = registers[i]; - CHECK(reg < m->GetCodeItem()->registers_size_); + CHECK_LT(reg, accessor.RegistersSize()); DexRegisterLocation location = dex_register_map.GetDexRegisterLocation( reg, number_of_dex_registers, code_info, encoding); switch (location.GetKind()) { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 55fa6328f5..45b761893f 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -4317,15 +4317,14 @@ void ClassLinker::ResolveClassExceptionHandlerTypes(Handle klass) void ClassLinker::ResolveMethodExceptionHandlerTypes(ArtMethod* method) { // similar to DexVerifier::ScanTryCatchBlocks and dex2oat's ResolveExceptionsForMethod. - const DexFile::CodeItem* code_item = - method->GetDexFile()->GetCodeItem(method->GetCodeItemOffset()); - if (code_item == nullptr) { + CodeItemDataAccessor accessor(method); + if (!accessor.HasCodeItem()) { return; // native or abstract method } - if (code_item->tries_size_ == 0) { + if (accessor.TriesSize() == 0) { return; // nothing to process } - const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*code_item, 0); + const uint8_t* handlers_ptr = accessor.GetCatchHandlerData(0); uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); for (uint32_t idx = 0; idx < handlers_size; idx++) { CatchHandlerIterator iterator(handlers_ptr); diff --git a/runtime/code_item_accessors-inl.h b/runtime/code_item_accessors-inl.h index 1ba3c55f77..f63ea6990f 100644 --- a/runtime/code_item_accessors-inl.h +++ b/runtime/code_item_accessors-inl.h @@ -30,27 +30,9 @@ namespace art { inline CodeItemInstructionAccessor::CodeItemInstructionAccessor(ArtMethod* method) : CodeItemInstructionAccessor(method->GetDexFile(), method->GetCodeItem()) {} -inline CodeItemInstructionAccessor CodeItemInstructionAccessor::CreateNullable( - ArtMethod* method) { - DCHECK(method != nullptr); - CodeItemInstructionAccessor ret; - const DexFile::CodeItem* code_item = method->GetCodeItem(); - if (code_item != nullptr) { - ret.Init(method->GetDexFile(), code_item); - } else { - DCHECK(!ret.HasCodeItem()) << "Should be null initialized"; - } - return ret; -} - inline CodeItemDataAccessor::CodeItemDataAccessor(ArtMethod* method) : CodeItemDataAccessor(method->GetDexFile(), method->GetCodeItem()) {} -inline CodeItemDataAccessor CodeItemDataAccessor::CreateNullable(ArtMethod* method) { - DCHECK(method != nullptr); - return CreateNullable(method->GetDexFile(), method->GetCodeItem()); -} - inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(ArtMethod* method) : CodeItemDebugInfoAccessor(method->GetDexFile(), method->GetCodeItem()) {} @@ -59,7 +41,7 @@ inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(const DexFile* dex_f if (code_item == nullptr) { return; } - Init(dex_file, code_item, OatFile::GetDebugInfoOffset(*dex_file, code_item)); + Init(dex_file, code_item, OatFile::GetDebugInfoOffset(*dex_file, code_item->debug_info_off_)); } } // namespace art diff --git a/runtime/code_item_accessors-no_art-inl.h b/runtime/code_item_accessors-no_art-inl.h index 96321b54f5..ebdd78bd74 100644 --- a/runtime/code_item_accessors-no_art-inl.h +++ b/runtime/code_item_accessors-no_art-inl.h @@ -24,7 +24,6 @@ #include "standard_dex_file.h" // The no ART version is used by binaries that don't include the whole runtime. - namespace art { inline void CodeItemInstructionAccessor::Init(const CompactDexFile::CodeItem& code_item) { @@ -39,13 +38,14 @@ inline void CodeItemInstructionAccessor::Init(const StandardDexFile::CodeItem& c inline void CodeItemInstructionAccessor::Init(const DexFile* dex_file, const DexFile::CodeItem* code_item) { - DCHECK(dex_file != nullptr); - DCHECK(code_item != nullptr); - if (dex_file->IsCompactDexFile()) { - Init(down_cast(*code_item)); - } else { - DCHECK(dex_file->IsStandardDexFile()); - Init(down_cast(*code_item)); + if (code_item != nullptr) { + DCHECK(dex_file != nullptr); + if (dex_file->IsCompactDexFile()) { + Init(down_cast(*code_item)); + } else { + DCHECK(dex_file->IsStandardDexFile()); + Init(down_cast(*code_item)); + } } } @@ -63,6 +63,14 @@ inline DexInstructionIterator CodeItemInstructionAccessor::end() const { return DexInstructionIterator(insns_, insns_size_in_code_units_); } +inline IterationRange CodeItemInstructionAccessor::InstructionsFrom( + uint32_t start_dex_pc) const { + DCHECK_LT(start_dex_pc, InsnsSizeInCodeUnits()); + return { + DexInstructionIterator(insns_, start_dex_pc), + DexInstructionIterator(insns_, insns_size_in_code_units_) }; +} + inline void CodeItemDataAccessor::Init(const CompactDexFile::CodeItem& code_item) { CodeItemInstructionAccessor::Init(code_item); registers_size_ = code_item.registers_size_; @@ -81,13 +89,14 @@ inline void CodeItemDataAccessor::Init(const StandardDexFile::CodeItem& code_ite inline void CodeItemDataAccessor::Init(const DexFile* dex_file, const DexFile::CodeItem* code_item) { - DCHECK(dex_file != nullptr); - DCHECK(code_item != nullptr); - if (dex_file->IsCompactDexFile()) { - CodeItemDataAccessor::Init(down_cast(*code_item)); - } else { - DCHECK(dex_file->IsStandardDexFile()); - CodeItemDataAccessor::Init(down_cast(*code_item)); + if (code_item != nullptr) { + DCHECK(dex_file != nullptr); + if (dex_file->IsCompactDexFile()) { + CodeItemDataAccessor::Init(down_cast(*code_item)); + } else { + DCHECK(dex_file->IsStandardDexFile()); + CodeItemDataAccessor::Init(down_cast(*code_item)); + } } } @@ -96,18 +105,6 @@ inline CodeItemDataAccessor::CodeItemDataAccessor(const DexFile* dex_file, Init(dex_file, code_item); } -inline CodeItemDataAccessor CodeItemDataAccessor::CreateNullable( - const DexFile* dex_file, - const DexFile::CodeItem* code_item) { - CodeItemDataAccessor ret; - if (code_item != nullptr) { - ret.Init(dex_file, code_item); - } else { - DCHECK(!ret.HasCodeItem()) << "Should be null initialized"; - } - return ret; -} - inline IterationRange CodeItemDataAccessor::TryItems() const { const DexFile::TryItem* try_items = DexFile::GetTryItems(end(), 0u); return { @@ -163,7 +160,6 @@ inline bool CodeItemDebugInfoAccessor::DecodeDebugLocalInfo(bool is_static, context); } - } // namespace art #endif // ART_RUNTIME_CODE_ITEM_ACCESSORS_NO_ART_INL_H_ diff --git a/runtime/code_item_accessors.h b/runtime/code_item_accessors.h index 9f40114438..4cbe7a1ba0 100644 --- a/runtime/code_item_accessors.h +++ b/runtime/code_item_accessors.h @@ -42,6 +42,8 @@ class CodeItemInstructionAccessor { ALWAYS_INLINE DexInstructionIterator end() const; + IterationRange InstructionsFrom(uint32_t start_dex_pc) const; + uint32_t InsnsSizeInCodeUnits() const { return insns_size_in_code_units_; } @@ -52,6 +54,7 @@ class CodeItemInstructionAccessor { // Return the instruction for a dex pc. const Instruction& InstructionAt(uint32_t dex_pc) const { + DCHECK_LT(dex_pc, InsnsSizeInCodeUnits()); return *Instruction::At(insns_ + dex_pc); } @@ -60,10 +63,6 @@ class CodeItemInstructionAccessor { return Insns() != nullptr; } - // CreateNullable allows ArtMethods that have a null code item. - ALWAYS_INLINE static CodeItemInstructionAccessor CreateNullable(ArtMethod* method) - REQUIRES_SHARED(Locks::mutator_lock_); - protected: CodeItemInstructionAccessor() = default; @@ -109,14 +108,6 @@ class CodeItemDataAccessor : public CodeItemInstructionAccessor { const DexFile::TryItem* FindTryItem(uint32_t try_dex_pc) const; - // CreateNullable allows ArtMethods that have a null code item. - ALWAYS_INLINE static CodeItemDataAccessor CreateNullable(ArtMethod* method) - REQUIRES_SHARED(Locks::mutator_lock_); - - ALWAYS_INLINE static CodeItemDataAccessor CreateNullable( - const DexFile* dex_file, - const DexFile::CodeItem* code_item); - protected: CodeItemDataAccessor() = default; diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h index 267735fe95..89887022e0 100644 --- a/runtime/common_dex_operations.h +++ b/runtime/common_dex_operations.h @@ -23,6 +23,7 @@ #include "base/macros.h" #include "base/mutex.h" #include "class_linker.h" +#include "code_item_accessors.h" #include "handle_scope-inl.h" #include "instrumentation.h" #include "interpreter/shadow_frame.h" @@ -52,7 +53,7 @@ namespace interpreter { } // namespace interpreter inline void PerformCall(Thread* self, - const DexFile::CodeItem* code_item, + const CodeItemDataAccessor& accessor, ArtMethod* caller_method, const size_t first_dest_reg, ShadowFrame* callee_frame, @@ -61,13 +62,13 @@ inline void PerformCall(Thread* self, REQUIRES_SHARED(Locks::mutator_lock_) { if (LIKELY(Runtime::Current()->IsStarted())) { if (use_interpreter_entrypoint) { - interpreter::ArtInterpreterToInterpreterBridge(self, code_item, callee_frame, result); + interpreter::ArtInterpreterToInterpreterBridge(self, accessor, callee_frame, result); } else { interpreter::ArtInterpreterToCompiledCodeBridge( self, caller_method, callee_frame, first_dest_reg, result); } } else { - interpreter::UnstartedRuntime::Invoke(self, code_item, callee_frame, result, first_dest_reg); + interpreter::UnstartedRuntime::Invoke(self, accessor, callee_frame, result, first_dest_reg); } } diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index 575d18e26a..707885c88d 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -551,43 +551,43 @@ static bool IsValidImplicitCheck(uintptr_t addr, const Instruction& instr) void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) { uint32_t throw_dex_pc; ArtMethod* method = Thread::Current()->GetCurrentMethod(&throw_dex_pc); - const DexFile::CodeItem* code = method->GetCodeItem(); - CHECK_LT(throw_dex_pc, code->insns_size_in_code_units_); - const Instruction* instr = Instruction::At(&code->insns_[throw_dex_pc]); - if (check_address && !IsValidImplicitCheck(addr, *instr)) { + CodeItemInstructionAccessor accessor(method); + CHECK_LT(throw_dex_pc, accessor.InsnsSizeInCodeUnits()); + const Instruction& instr = accessor.InstructionAt(throw_dex_pc); + if (check_address && !IsValidImplicitCheck(addr, instr)) { const DexFile* dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile(); LOG(FATAL) << "Invalid address for an implicit NullPointerException check: " << "0x" << std::hex << addr << std::dec << ", at " - << instr->DumpString(dex_file) + << instr.DumpString(dex_file) << " in " << method->PrettyMethod(); } - switch (instr->Opcode()) { + switch (instr.Opcode()) { case Instruction::INVOKE_DIRECT: - ThrowNullPointerExceptionForMethodAccess(instr->VRegB_35c(), kDirect); + ThrowNullPointerExceptionForMethodAccess(instr.VRegB_35c(), kDirect); break; case Instruction::INVOKE_DIRECT_RANGE: - ThrowNullPointerExceptionForMethodAccess(instr->VRegB_3rc(), kDirect); + ThrowNullPointerExceptionForMethodAccess(instr.VRegB_3rc(), kDirect); break; case Instruction::INVOKE_VIRTUAL: - ThrowNullPointerExceptionForMethodAccess(instr->VRegB_35c(), kVirtual); + ThrowNullPointerExceptionForMethodAccess(instr.VRegB_35c(), kVirtual); break; case Instruction::INVOKE_VIRTUAL_RANGE: - ThrowNullPointerExceptionForMethodAccess(instr->VRegB_3rc(), kVirtual); + ThrowNullPointerExceptionForMethodAccess(instr.VRegB_3rc(), kVirtual); break; case Instruction::INVOKE_INTERFACE: - ThrowNullPointerExceptionForMethodAccess(instr->VRegB_35c(), kInterface); + ThrowNullPointerExceptionForMethodAccess(instr.VRegB_35c(), kInterface); break; case Instruction::INVOKE_INTERFACE_RANGE: - ThrowNullPointerExceptionForMethodAccess(instr->VRegB_3rc(), kInterface); + ThrowNullPointerExceptionForMethodAccess(instr.VRegB_3rc(), kInterface); break; case Instruction::INVOKE_POLYMORPHIC: - ThrowNullPointerExceptionForMethodAccess(instr->VRegB_45cc(), kVirtual); + ThrowNullPointerExceptionForMethodAccess(instr.VRegB_45cc(), kVirtual); break; case Instruction::INVOKE_POLYMORPHIC_RANGE: - ThrowNullPointerExceptionForMethodAccess(instr->VRegB_4rcc(), kVirtual); + ThrowNullPointerExceptionForMethodAccess(instr.VRegB_4rcc(), kVirtual); break; case Instruction::INVOKE_VIRTUAL_QUICK: case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { @@ -612,7 +612,7 @@ void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) { case Instruction::IGET_CHAR: case Instruction::IGET_SHORT: { ArtField* field = - Runtime::Current()->GetClassLinker()->ResolveField(instr->VRegC_22c(), method, false); + Runtime::Current()->GetClassLinker()->ResolveField(instr.VRegC_22c(), method, false); ThrowNullPointerExceptionForFieldAccess(field, true /* read */); break; } @@ -644,7 +644,7 @@ void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) { case Instruction::IPUT_CHAR: case Instruction::IPUT_SHORT: { ArtField* field = - Runtime::Current()->GetClassLinker()->ResolveField(instr->VRegC_22c(), method, false); + Runtime::Current()->GetClassLinker()->ResolveField(instr.VRegC_22c(), method, false); ThrowNullPointerExceptionForFieldAccess(field, false /* write */); break; } @@ -707,7 +707,7 @@ void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) { const DexFile* dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile(); LOG(FATAL) << "NullPointerException at an unexpected instruction: " - << instr->DumpString(dex_file) + << instr.DumpString(dex_file) << " in " << method->PrettyMethod(); break; diff --git a/runtime/debugger.cc b/runtime/debugger.cc index c85c2336b0..2be00f5f5b 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -278,11 +278,8 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati } private: - static bool IsReturn(ArtMethod* method, uint32_t dex_pc) - REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile::CodeItem* code_item = method->GetCodeItem(); - const Instruction* instruction = Instruction::At(&code_item->insns_[dex_pc]); - return instruction->IsReturn(); + static bool IsReturn(ArtMethod* method, uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) { + return method->DexInstructions().InstructionAt(dex_pc).IsReturn(); } static bool IsListeningToDexPcMoved() REQUIRES_SHARED(Locks::mutator_lock_) { @@ -1535,15 +1532,15 @@ static uint32_t MangleAccessFlags(uint32_t accessFlags) { */ static uint16_t MangleSlot(uint16_t slot, ArtMethod* m) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile::CodeItem* code_item = m->GetCodeItem(); - if (code_item == nullptr) { + CodeItemDataAccessor accessor(m); + if (!accessor.HasCodeItem()) { // We should not get here for a method without code (native, proxy or abstract). Log it and // return the slot as is since all registers are arguments. LOG(WARNING) << "Trying to mangle slot for method without code " << m->PrettyMethod(); return slot; } - uint16_t ins_size = code_item->ins_size_; - uint16_t locals_size = code_item->registers_size_ - ins_size; + uint16_t ins_size = accessor.InsSize(); + uint16_t locals_size = accessor.RegistersSize() - ins_size; if (slot >= locals_size) { return slot - locals_size; } else { @@ -1566,8 +1563,8 @@ static size_t GetMethodNumArgRegistersIncludingThis(ArtMethod* method) */ static uint16_t DemangleSlot(uint16_t slot, ArtMethod* m, JDWP::JdwpError* error) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile::CodeItem* code_item = m->GetCodeItem(); - if (code_item == nullptr) { + CodeItemDataAccessor accessor(m); + if (!accessor.HasCodeItem()) { // We should not get here for a method without code (native, proxy or abstract). Log it and // return the slot as is since all registers are arguments. LOG(WARNING) << "Trying to demangle slot for method without code " @@ -1578,9 +1575,9 @@ static uint16_t DemangleSlot(uint16_t slot, ArtMethod* m, JDWP::JdwpError* error return slot; } } else { - if (slot < code_item->registers_size_) { - uint16_t ins_size = code_item->ins_size_; - uint16_t locals_size = code_item->registers_size_ - ins_size; + if (slot < accessor.RegistersSize()) { + uint16_t ins_size = accessor.InsSize(); + uint16_t locals_size = accessor.RegistersSize() - ins_size; *error = JDWP::ERR_NONE; return (slot < ins_size) ? slot + locals_size : slot - ins_size; } @@ -1793,9 +1790,9 @@ JDWP::JdwpError Dbg::GetBytecodes(JDWP::RefTypeId, JDWP::MethodId method_id, if (m == nullptr) { return JDWP::ERR_INVALID_METHODID; } - const DexFile::CodeItem* code_item = m->GetCodeItem(); - size_t byte_count = code_item->insns_size_in_code_units_ * 2; - const uint8_t* begin = reinterpret_cast(code_item->insns_); + CodeItemDataAccessor accessor(m); + size_t byte_count = accessor.InsnsSizeInCodeUnits() * 2; + const uint8_t* begin = reinterpret_cast(accessor.Insns()); const uint8_t* end = begin + byte_count; for (const uint8_t* p = begin; p != end; ++p) { bytecodes->push_back(*p); @@ -2978,9 +2975,8 @@ void Dbg::PostLocationEvent(ArtMethod* m, int dex_pc, mirror::Object* this_objec Handle pending_exception(hs.NewHandle(self->GetException())); self->ClearException(); if (kIsDebugBuild && pending_exception != nullptr) { - const DexFile::CodeItem* code_item = location.method->GetCodeItem(); - const Instruction* instr = Instruction::At(&code_item->insns_[location.dex_pc]); - CHECK_EQ(Instruction::MOVE_EXCEPTION, instr->Opcode()); + const Instruction& instr = location.method->DexInstructions().InstructionAt(location.dex_pc); + CHECK_EQ(Instruction::MOVE_EXCEPTION, instr.Opcode()); } gJdwpState->PostLocationEvent(&location, this_object, event_flags, return_value); diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 76110f2e5d..f3a88f766c 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -320,6 +320,7 @@ class DexFile { debug_info_off_ = new_offset; } + private: uint16_t registers_size_; // the number of registers used by this code // (locals + parameters) uint16_t ins_size_; // the number of words of incoming arguments to the method @@ -340,6 +341,14 @@ class DexFile { uint16_t insns_[1]; // actual array of bytecode. private: + ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor); + friend class CatchHandlerIterator; + friend class CodeItemDataAccessor; + friend class CodeItemDebugInfoAccessor; + friend class CodeItemInstructionAccessor; + friend class DexFile; // TODO: Remove this one when it's cleaned up. + friend class DexFileVerifier; + friend class VdexFile; // TODO: Remove this one when it's cleaned up. DISALLOW_COPY_AND_ASSIGN(CodeItem); }; diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index 69055041e5..87eec571f1 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -22,6 +22,7 @@ #include "base/stl_util.h" #include "base/unix_file/fd_file.h" +#include "code_item_accessors-inl.h" #include "common_runtime_test.h" #include "dex_file-inl.h" #include "dex_file_loader.h" @@ -730,15 +731,8 @@ TEST_F(DexFileTest, OpenDexDebugInfoLocalNullType) { kRawDexDebugInfoLocalNullType, tmp.GetFilename().c_str(), 0xf25f2b38U, true); const DexFile::ClassDef& class_def = raw->GetClassDef(0); const DexFile::CodeItem* code_item = raw->GetCodeItem(raw->FindCodeItemOffset(class_def, 1)); - uint32_t debug_info_offset = raw->GetDebugInfoOffset(code_item); - ASSERT_TRUE(raw->DecodeDebugLocalInfo(code_item->registers_size_, - code_item->ins_size_, - code_item->insns_size_in_code_units_, - debug_info_offset, - true, - 1, - Callback, - nullptr)); + CodeItemDebugInfoAccessor accessor(raw.get(), code_item); + ASSERT_TRUE(accessor.DecodeDebugLocalInfo(true, 1, Callback, nullptr)); } } // namespace art diff --git a/runtime/dex_file_tracking_registrar.cc b/runtime/dex_file_tracking_registrar.cc index 4de43760d4..c157ddc790 100644 --- a/runtime/dex_file_tracking_registrar.cc +++ b/runtime/dex_file_tracking_registrar.cc @@ -30,6 +30,7 @@ #endif #include "base/memory_tool.h" +#include "code_item_accessors-inl.h" #include "dex_file-inl.h" namespace art { @@ -184,9 +185,12 @@ void DexFileTrackingRegistrar::SetAllCodeItemStartRegistration(bool should_poiso if (code_item != nullptr) { const void* code_item_begin = reinterpret_cast(code_item); size_t code_item_start = reinterpret_cast(code_item); - size_t code_item_start_end = reinterpret_cast(&code_item->insns_[1]); + CodeItemInstructionAccessor accessor(dex_file_, code_item); + size_t code_item_start_end = reinterpret_cast(accessor.Insns()); size_t code_item_start_size = code_item_start_end - code_item_start; - range_values_.push_back(std::make_tuple(code_item_begin, code_item_start_size, should_poison)); + range_values_.push_back(std::make_tuple(code_item_begin, + code_item_start_size, + should_poison)); } cdit.Next(); } @@ -204,9 +208,10 @@ void DexFileTrackingRegistrar::SetAllInsnsRegistration(bool should_poison) { while (cdit.HasNextMethod()) { const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); if (code_item != nullptr) { - const void* insns_begin = reinterpret_cast(&code_item->insns_); + CodeItemInstructionAccessor accessor(dex_file_, code_item); + const void* insns_begin = reinterpret_cast(accessor.Insns()); // Member insns_size_in_code_units_ is in 2-byte units - size_t insns_size = code_item->insns_size_in_code_units_ * 2; + size_t insns_size = accessor.InsnsSizeInCodeUnits() * 2; range_values_.push_back(std::make_tuple(insns_begin, insns_size, should_poison)); } cdit.Next(); diff --git a/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc b/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc index d4bc1c76b1..d22f180c7a 100644 --- a/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc @@ -28,7 +28,7 @@ extern "C" int artHandleFillArrayDataFromCode(uint32_t payload_offset, mirror::A ArtMethod* method, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); - const uint16_t* const insns = method->GetCodeItem()->insns_; + const uint16_t* const insns = method->DexInstructions().Insns(); const Instruction::ArrayDataPayload* payload = reinterpret_cast(insns + payload_offset); bool success = FillArrayData(array, payload); diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index ca5b79921c..a4d14ecf7e 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -784,8 +784,8 @@ extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method, Thread* self, DCHECK(!method->IsNative()) << method->PrettyMethod(); uint32_t shorty_len = 0; ArtMethod* non_proxy_method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize); - const DexFile::CodeItem* code_item = non_proxy_method->GetCodeItem(); - DCHECK(code_item != nullptr) << method->PrettyMethod(); + DCHECK(non_proxy_method->GetCodeItem() != nullptr) << method->PrettyMethod(); + CodeItemDataAccessor accessor(non_proxy_method); const char* shorty = non_proxy_method->GetShorty(&shorty_len); JValue result; @@ -795,12 +795,12 @@ extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method, Thread* self, } else { const char* old_cause = self->StartAssertNoThreadSuspension( "Building interpreter shadow frame"); - uint16_t num_regs = code_item->registers_size_; + uint16_t num_regs = accessor.RegistersSize(); // No last shadow coming from quick. ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr = CREATE_SHADOW_FRAME(num_regs, /* link */ nullptr, method, /* dex pc */ 0); ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get(); - size_t first_arg_reg = code_item->registers_size_ - code_item->ins_size_; + size_t first_arg_reg = accessor.RegistersSize() - accessor.InsSize(); BuildQuickShadowFrameVisitor shadow_frame_builder(sp, method->IsStatic(), shorty, shorty_len, shadow_frame, first_arg_reg); shadow_frame_builder.VisitArguments(); @@ -823,7 +823,7 @@ extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method, Thread* self, } } - result = interpreter::EnterInterpreterFromEntryPoint(self, code_item, shadow_frame); + result = interpreter::EnterInterpreterFromEntryPoint(self, accessor, shadow_frame); } // Pop transition. @@ -1121,10 +1121,9 @@ extern "C" const void* artQuickResolutionTrampoline( // code. if (!found_stack_map || kIsDebugBuild) { uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp); - const DexFile::CodeItem* code; - code = caller->GetCodeItem(); - CHECK_LT(dex_pc, code->insns_size_in_code_units_); - const Instruction& instr = code->InstructionAt(dex_pc); + CodeItemInstructionAccessor accessor(caller); + CHECK_LT(dex_pc, accessor.InsnsSizeInCodeUnits()); + const Instruction& instr = accessor.InstructionAt(dex_pc); Instruction::Code instr_code = instr.Opcode(); bool is_range; switch (instr_code) { @@ -2450,9 +2449,7 @@ extern "C" TwoWordReturn artInvokeInterfaceTrampoline(ArtMethod* interface_metho // Fetch the dex_method_idx of the target interface method from the caller. uint32_t dex_method_idx; uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp); - const DexFile::CodeItem* code_item = caller_method->GetCodeItem(); - DCHECK_LT(dex_pc, code_item->insns_size_in_code_units_); - const Instruction& instr = code_item->InstructionAt(dex_pc); + const Instruction& instr = caller_method->DexInstructions().InstructionAt(dex_pc); Instruction::Code instr_code = instr.Opcode(); DCHECK(instr_code == Instruction::INVOKE_INTERFACE || instr_code == Instruction::INVOKE_INTERFACE_RANGE) diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 49f202182d..a4ee21d4aa 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -1246,18 +1246,17 @@ struct RuntimeMethodShortyVisitor : public StackVisitor { shorty = m->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty()[0]; return false; } - const DexFile::CodeItem* code_item = m->GetCodeItem(); - const Instruction* instr = Instruction::At(&code_item->insns_[GetDexPc()]); - if (instr->IsInvoke()) { + const Instruction& instr = m->DexInstructions().InstructionAt(GetDexPc()); + if (instr.IsInvoke()) { const DexFile* dex_file = m->GetDexFile(); - if (interpreter::IsStringInit(dex_file, instr->VRegB())) { + if (interpreter::IsStringInit(dex_file, instr.VRegB())) { // Invoking string init constructor is turned into invoking // StringFactory.newStringFromChars() which returns a string. shorty = 'L'; return false; } // A regular invoke, use callee's shorty. - uint32_t method_idx = instr->VRegB(); + uint32_t method_idx = instr.VRegB(); shorty = dex_file->GetMethodShorty(method_idx)[0]; } // Stop stack walking since we've seen a Java frame. diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 01b7d4e457..9b81819da2 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -240,7 +240,7 @@ static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind; static inline JValue Execute( Thread* self, - const DexFile::CodeItem* code_item, + const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, bool stay_in_interpreter = false) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -254,11 +254,13 @@ static inline JValue Execute( ArtMethod *method = shadow_frame.GetMethod(); if (UNLIKELY(instrumentation->HasMethodEntryListeners())) { - instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - method, 0); + instrumentation->MethodEnterEvent(self, + shadow_frame.GetThisObject(accessor.InsSize()), + method, + 0); if (UNLIKELY(self->IsExceptionPending())) { instrumentation->MethodUnwindEvent(self, - shadow_frame.GetThisObject(code_item->ins_size_), + shadow_frame.GetThisObject(accessor.InsSize()), method, 0); return JValue(); @@ -277,7 +279,7 @@ static inline JValue Execute( // Calculate the offset of the first input reg. The input registers are in the high regs. // It's ok to access the code item here since JIT code will have been touched by the // interpreter and compiler already. - uint16_t arg_offset = code_item->registers_size_ - code_item->ins_size_; + uint16_t arg_offset = accessor.RegistersSize() - accessor.InsSize(); ArtInterpreterToCompiledCodeBridge(self, nullptr, &shadow_frame, arg_offset, &result); // Push the shadow frame back as the caller will expect it. self->PushShadowFrame(&shadow_frame); @@ -302,27 +304,27 @@ static inline JValue Execute( if (kInterpreterImplKind == kMterpImplKind) { if (transaction_active) { // No Mterp variant - just use the switch interpreter. - return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, false); } else if (UNLIKELY(!Runtime::Current()->IsStarted())) { - return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, false); } else { while (true) { // Mterp does not support all instrumentation/debugging. if (MterpShouldSwitchInterpreters() != 0) { - return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, false); } bool returned = ExecuteMterpImpl(self, - code_item->insns_, + accessor.Insns(), &shadow_frame, &result_register); if (returned) { return result_register; } else { // Mterp didn't like that instruction. Single-step it with the reference interpreter. - result_register = ExecuteSwitchImpl(self, code_item, shadow_frame, + result_register = ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, true); if (shadow_frame.GetDexPC() == dex::kDexNoIndex) { // Single-stepped a return or an exception not handled locally. Return to caller. @@ -334,10 +336,10 @@ static inline JValue Execute( } else { DCHECK_EQ(kInterpreterImplKind, kSwitchImplKind); if (transaction_active) { - return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, false); } else { - return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, false); } } @@ -346,19 +348,19 @@ static inline JValue Execute( if (kInterpreterImplKind == kMterpImplKind) { // No access check variants for Mterp. Just use the switch version. if (transaction_active) { - return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, false); } else { - return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, false); } } else { DCHECK_EQ(kInterpreterImplKind, kSwitchImplKind); if (transaction_active) { - return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, false); } else { - return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, false); } } @@ -387,12 +389,12 @@ void EnterInterpreterFromInvoke(Thread* self, } const char* old_cause = self->StartAssertNoThreadSuspension("EnterInterpreterFromInvoke"); - const DexFile::CodeItem* code_item = method->GetCodeItem(); + CodeItemDataAccessor accessor(method); uint16_t num_regs; uint16_t num_ins; - if (code_item != nullptr) { - num_regs = code_item->registers_size_; - num_ins = code_item->ins_size_; + if (accessor.HasCodeItem()) { + num_regs = accessor.RegistersSize(); + num_ins = accessor.InsSize(); } else if (!method->IsInvokable()) { self->EndAssertNoThreadSuspension(old_cause); method->ThrowInvocationTimeError(); @@ -454,7 +456,7 @@ void EnterInterpreterFromInvoke(Thread* self, } } if (LIKELY(!method->IsNative())) { - JValue r = Execute(self, code_item, *shadow_frame, JValue(), stay_in_interpreter); + JValue r = Execute(self, accessor, *shadow_frame, JValue(), stay_in_interpreter); if (result != nullptr) { *result = r; } @@ -497,7 +499,7 @@ void EnterInterpreterFromDeoptimize(Thread* self, DCHECK(!shadow_frame->GetMethod()->MustCountLocks()); self->SetTopOfShadowStack(shadow_frame); - const DexFile::CodeItem* code_item = shadow_frame->GetMethod()->GetCodeItem(); + CodeItemDataAccessor accessor(shadow_frame->GetMethod()); const uint32_t dex_pc = shadow_frame->GetDexPC(); uint32_t new_dex_pc = dex_pc; if (UNLIKELY(self->IsExceptionPending())) { @@ -510,7 +512,7 @@ void EnterInterpreterFromDeoptimize(Thread* self, self, *shadow_frame, instrumentation) ? shadow_frame->GetDexPC() : dex::kDexNoIndex; } else if (!from_code) { // Deoptimization is not called from code directly. - const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]); + const Instruction* instr = &accessor.InstructionAt(dex_pc); if (deopt_method_type == DeoptimizationMethodType::kKeepDexPc) { DCHECK(first); // Need to re-execute the dex instruction. @@ -566,7 +568,7 @@ void EnterInterpreterFromDeoptimize(Thread* self, } if (new_dex_pc != dex::kDexNoIndex) { shadow_frame->SetDexPC(new_dex_pc); - value = Execute(self, code_item, *shadow_frame, value); + value = Execute(self, accessor, *shadow_frame, value); } ShadowFrame* old_frame = shadow_frame; shadow_frame = shadow_frame->GetLink(); @@ -580,7 +582,7 @@ void EnterInterpreterFromDeoptimize(Thread* self, ret_val->SetJ(value.GetJ()); } -JValue EnterInterpreterFromEntryPoint(Thread* self, const DexFile::CodeItem* code_item, +JValue EnterInterpreterFromEntryPoint(Thread* self, const CodeItemDataAccessor& accessor, ShadowFrame* shadow_frame) { DCHECK_EQ(self, Thread::Current()); bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks(); @@ -593,11 +595,11 @@ JValue EnterInterpreterFromEntryPoint(Thread* self, const DexFile::CodeItem* cod if (jit != nullptr) { jit->NotifyCompiledCodeToInterpreterTransition(self, shadow_frame->GetMethod()); } - return Execute(self, code_item, *shadow_frame, JValue()); + return Execute(self, accessor, *shadow_frame, JValue()); } void ArtInterpreterToInterpreterBridge(Thread* self, - const DexFile::CodeItem* code_item, + const CodeItemDataAccessor& accessor, ShadowFrame* shadow_frame, JValue* result) { bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks(); @@ -626,7 +628,7 @@ void ArtInterpreterToInterpreterBridge(Thread* self, } if (LIKELY(!shadow_frame->GetMethod()->IsNative())) { - result->SetJ(Execute(self, code_item, *shadow_frame, JValue()).GetJ()); + result->SetJ(Execute(self, accessor, *shadow_frame, JValue()).GetJ()); } else { // We don't expect to be asked to interpret native code (which is entered via a JNI compiler // generated stub) except during testing and image writing. diff --git a/runtime/interpreter/interpreter.h b/runtime/interpreter/interpreter.h index df8568edcd..f9d7774d5b 100644 --- a/runtime/interpreter/interpreter.h +++ b/runtime/interpreter/interpreter.h @@ -27,6 +27,7 @@ class Object; } // namespace mirror class ArtMethod; +class CodeItemDataAccessor; union JValue; class ShadowFrame; class Thread; @@ -52,12 +53,15 @@ extern void EnterInterpreterFromDeoptimize(Thread* self, DeoptimizationMethodType method_type) REQUIRES_SHARED(Locks::mutator_lock_); -extern JValue EnterInterpreterFromEntryPoint(Thread* self, const DexFile::CodeItem* code_item, +extern JValue EnterInterpreterFromEntryPoint(Thread* self, + const CodeItemDataAccessor& accessor, ShadowFrame* shadow_frame) REQUIRES_SHARED(Locks::mutator_lock_); -void ArtInterpreterToInterpreterBridge(Thread* self, const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result) +void ArtInterpreterToInterpreterBridge(Thread* self, + const CodeItemDataAccessor& accessor, + ShadowFrame* shadow_frame, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); // One-time sanity check. diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 122d1a816b..deed16b974 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -1320,7 +1320,7 @@ static inline bool DoCallCommon(ArtMethod* called_method, } // Compute method information. - const DexFile::CodeItem* code_item = called_method->GetCodeItem(); + CodeItemDataAccessor accessor(called_method); // Number of registers for the callee's call frame. uint16_t num_regs; // Test whether to use the interpreter or compiler entrypoint, and save that result to pass to @@ -1334,7 +1334,7 @@ static inline bool DoCallCommon(ArtMethod* called_method, ClassLinker::ShouldUseInterpreterEntrypoint( called_method, called_method->GetEntryPointFromQuickCompiledCode()); - if (LIKELY(code_item != nullptr)) { + if (LIKELY(accessor.HasCodeItem())) { // When transitioning to compiled code, space only needs to be reserved for the input registers. // The rest of the frame gets discarded. This also prevents accessing the called method's code // item, saving memory by keeping code items of compiled code untouched. @@ -1342,8 +1342,8 @@ static inline bool DoCallCommon(ArtMethod* called_method, DCHECK(!Runtime::Current()->IsAotCompiler()) << "Compiler should use interpreter entrypoint"; num_regs = number_of_inputs; } else { - num_regs = code_item->registers_size_; - DCHECK_EQ(string_init ? number_of_inputs - 1 : number_of_inputs, code_item->ins_size_); + num_regs = accessor.RegistersSize(); + DCHECK_EQ(string_init ? number_of_inputs - 1 : number_of_inputs, accessor.InsSize()); } } else { DCHECK(called_method->IsNative() || called_method->IsProxyMethod()); @@ -1367,7 +1367,7 @@ static inline bool DoCallCommon(ArtMethod* called_method, DCHECK_GT(num_regs, 0u); // As the method is an instance method, there should be at least 1. // The new StringFactory call is static and has one fewer argument. - if (code_item == nullptr) { + if (!accessor.HasCodeItem()) { DCHECK(called_method->IsNative() || called_method->IsProxyMethod()); num_regs--; } // else ... don't need to change num_regs since it comes up from the string_init's code item @@ -1499,7 +1499,7 @@ static inline bool DoCallCommon(ArtMethod* called_method, } PerformCall(self, - code_item, + accessor, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 094f08664e..81c1e1deeb 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -67,7 +67,7 @@ namespace interpreter { { \ if (UNLIKELY(instrumentation->HasDexPcListeners()) && \ UNLIKELY(!DoDexPcMoveEvent(self, \ - code_item, \ + accessor, \ shadow_frame, \ dex_pc, \ instrumentation, \ @@ -125,7 +125,7 @@ namespace interpreter { // jvmti-agents while handling breakpoint or single step events. We had to move this into its own // function because it was making ExecuteSwitchImpl have too large a stack. NO_INLINE static bool DoDexPcMoveEvent(Thread* self, - const DexFile::CodeItem* code_item, + const CodeItemDataAccessor& accessor, const ShadowFrame& shadow_frame, uint32_t dex_pc, const instrumentation::Instrumentation* instrumentation, @@ -139,7 +139,7 @@ NO_INLINE static bool DoDexPcMoveEvent(Thread* self, hs.NewHandleWrapper(LIKELY(save_ref == nullptr) ? &null_obj : save_ref->GetGCRoot())); self->ClearException(); instrumentation->DexPcMovedEvent(self, - shadow_frame.GetThisObject(code_item->ins_size_), + shadow_frame.GetThisObject(accessor.InsSize()), shadow_frame.GetMethod(), dex_pc); if (UNLIKELY(self->IsExceptionPending())) { @@ -188,7 +188,7 @@ NO_INLINE static bool SendMethodExitEvents(Thread* self, } template -JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, +JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, bool interpret_one_instruction) { constexpr bool do_assignability_check = do_access_check; @@ -200,10 +200,10 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, uint32_t dex_pc = shadow_frame.GetDexPC(); const auto* const instrumentation = Runtime::Current()->GetInstrumentation(); - const uint16_t* const insns = code_item->insns_; + ArtMethod* method = shadow_frame.GetMethod(); + const uint16_t* const insns = accessor.Insns(); const Instruction* inst = Instruction::At(insns + dex_pc); uint16_t inst_data; - ArtMethod* method = shadow_frame.GetMethod(); jit::Jit* jit = Runtime::Current()->GetJit(); do { @@ -303,7 +303,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, !SendMethodExitEvents(self, instrumentation, shadow_frame, - shadow_frame.GetThisObject(code_item->ins_size_), + shadow_frame.GetThisObject(accessor.InsSize()), shadow_frame.GetMethod(), inst->GetDexPc(insns), result))) { @@ -325,7 +325,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, !SendMethodExitEvents(self, instrumentation, shadow_frame, - shadow_frame.GetThisObject(code_item->ins_size_), + shadow_frame.GetThisObject(accessor.InsSize()), shadow_frame.GetMethod(), inst->GetDexPc(insns), result))) { @@ -348,7 +348,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, !SendMethodExitEvents(self, instrumentation, shadow_frame, - shadow_frame.GetThisObject(code_item->ins_size_), + shadow_frame.GetThisObject(accessor.InsSize()), shadow_frame.GetMethod(), inst->GetDexPc(insns), result))) { @@ -370,7 +370,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, !SendMethodExitEvents(self, instrumentation, shadow_frame, - shadow_frame.GetThisObject(code_item->ins_size_), + shadow_frame.GetThisObject(accessor.InsSize()), shadow_frame.GetMethod(), inst->GetDexPc(insns), result))) { @@ -412,7 +412,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, !SendMethodExitEvents(self, instrumentation, shadow_frame, - shadow_frame.GetThisObject(code_item->ins_size_), + shadow_frame.GetThisObject(accessor.InsSize()), shadow_frame.GetMethod(), inst->GetDexPc(insns), result))) { @@ -2484,19 +2484,19 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, // Explicit definitions of ExecuteSwitchImpl. template HOT_ATTR -JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, +JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, bool interpret_one_instruction); template HOT_ATTR -JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, +JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, bool interpret_one_instruction); template -JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, +JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, bool interpret_one_instruction); template -JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, +JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, bool interpret_one_instruction); diff --git a/runtime/interpreter/interpreter_switch_impl.h b/runtime/interpreter/interpreter_switch_impl.h index 267df2e219..aa0f854bb7 100644 --- a/runtime/interpreter/interpreter_switch_impl.h +++ b/runtime/interpreter/interpreter_switch_impl.h @@ -25,6 +25,7 @@ namespace art { +class CodeItemDataAccessor; class ShadowFrame; class Thread; @@ -32,7 +33,7 @@ namespace interpreter { template JValue ExecuteSwitchImpl(Thread* self, - const DexFile::CodeItem* code_item, + const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, bool interpret_one_instruction) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/interpreter/shadow_frame.cc b/runtime/interpreter/shadow_frame.cc index ab154cf767..fe7e3e0a9b 100644 --- a/runtime/interpreter/shadow_frame.cc +++ b/runtime/interpreter/shadow_frame.cc @@ -27,9 +27,9 @@ mirror::Object* ShadowFrame::GetThisObject() const { } else if (m->IsNative()) { return GetVRegReference(0); } else { - const DexFile::CodeItem* code_item = m->GetCodeItem(); - CHECK(code_item != nullptr) << ArtMethod::PrettyMethod(m); - uint16_t reg = code_item->registers_size_ - code_item->ins_size_; + CHECK(m->GetCodeItem() != nullptr) << ArtMethod::PrettyMethod(m); + CodeItemDataAccessor accessor(m); + uint16_t reg = accessor.RegistersSize() - accessor.InsSize(); return GetVRegReference(reg); } } diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index dece830f1a..d1436fa9cf 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -1910,7 +1910,7 @@ void UnstartedRuntime::Initialize() { tables_initialized_ = true; } -void UnstartedRuntime::Invoke(Thread* self, const DexFile::CodeItem* code_item, +void UnstartedRuntime::Invoke(Thread* self, const CodeItemDataAccessor& accessor, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { // In a runtime that's not started we intercept certain methods to avoid complicated dependency // problems in core libraries. @@ -1930,7 +1930,7 @@ void UnstartedRuntime::Invoke(Thread* self, const DexFile::CodeItem* code_item, self->PopShadowFrame(); } else { // Not special, continue with regular interpreter execution. - ArtInterpreterToInterpreterBridge(self, code_item, shadow_frame, result); + ArtInterpreterToInterpreterBridge(self, accessor, shadow_frame, result); } } diff --git a/runtime/interpreter/unstarted_runtime.h b/runtime/interpreter/unstarted_runtime.h index bc9ead8360..2e86dea1a9 100644 --- a/runtime/interpreter/unstarted_runtime.h +++ b/runtime/interpreter/unstarted_runtime.h @@ -25,6 +25,7 @@ namespace art { class ArtMethod; +class CodeItemDataAccessor; class Thread; class ShadowFrame; @@ -48,7 +49,7 @@ class UnstartedRuntime { static void Initialize(); static void Invoke(Thread* self, - const DexFile::CodeItem* code_item, + const CodeItemDataAccessor& accessor, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 278bc57412..12bf79d7ca 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -467,7 +467,8 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, // Fetch some data before looking up for an OSR method. We don't want thread // suspension once we hold an OSR method, as the JIT code cache could delete the OSR // method while we are being suspended. - const size_t number_of_vregs = method->GetCodeItem()->registers_size_; + CodeItemDataAccessor accessor(method); + const size_t number_of_vregs = accessor.RegistersSize(); const char* shorty = method->GetShorty(); std::string method_name(VLOG_IS_ON(jit) ? method->PrettyMethod() : ""); void** memory = nullptr; diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 8eb31c1ef8..88f30a8900 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -408,7 +408,7 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { // Compute method information. - const DexFile::CodeItem* code_item = called_method->GetCodeItem(); + CodeItemDataAccessor accessor(called_method); // Number of registers for the callee's call frame. Note that for non-exact // invokes, we always derive this information from the callee method. We @@ -419,10 +419,10 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, uint16_t num_regs; size_t num_input_regs; size_t first_dest_reg; - if (LIKELY(code_item != nullptr)) { - num_regs = code_item->registers_size_; - first_dest_reg = num_regs - code_item->ins_size_; - num_input_regs = code_item->ins_size_; + if (LIKELY(accessor.HasCodeItem())) { + num_regs = accessor.RegistersSize(); + first_dest_reg = num_regs - accessor.InsSize(); + num_input_regs = accessor.InsSize(); // Parameter registers go at the end of the shadow frame. DCHECK_NE(first_dest_reg, (size_t)-1); } else { @@ -496,7 +496,7 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint( called_method, called_method->GetEntryPointFromQuickCompiledCode()); PerformCall(self, - code_item, + accessor, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, @@ -550,10 +550,9 @@ static inline bool MethodHandleInvokeTransform(ArtMethod* called_method, // - One for the only method argument (an EmulatedStackFrame). static constexpr size_t kNumRegsForTransform = 2; - const DexFile::CodeItem* code_item = called_method->GetCodeItem(); - DCHECK(code_item != nullptr); - DCHECK_EQ(kNumRegsForTransform, code_item->registers_size_); - DCHECK_EQ(kNumRegsForTransform, code_item->ins_size_); + CodeItemDataAccessor accessor(called_method); + DCHECK_EQ(kNumRegsForTransform, accessor.RegistersSize()); + DCHECK_EQ(kNumRegsForTransform, accessor.InsSize()); ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr = CREATE_SHADOW_FRAME(kNumRegsForTransform, &shadow_frame, called_method, /* dex pc */ 0); @@ -589,7 +588,7 @@ static inline bool MethodHandleInvokeTransform(ArtMethod* called_method, bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint( called_method, called_method->GetEntryPointFromQuickCompiledCode()); PerformCall(self, - code_item, + accessor, shadow_frame.GetMethod(), 0 /* first destination register */, new_shadow_frame, @@ -1035,14 +1034,14 @@ static inline bool MethodHandleInvokeExactInternal( } // Compute method information. - const DexFile::CodeItem* code_item = called_method->GetCodeItem(); + CodeItemDataAccessor accessor(called_method); uint16_t num_regs; size_t num_input_regs; size_t first_dest_reg; - if (LIKELY(code_item != nullptr)) { - num_regs = code_item->registers_size_; - first_dest_reg = num_regs - code_item->ins_size_; - num_input_regs = code_item->ins_size_; + if (LIKELY(accessor.HasCodeItem())) { + num_regs = accessor.RegistersSize(); + first_dest_reg = num_regs - accessor.InsSize(); + num_input_regs = accessor.InsSize(); // Parameter registers go at the end of the shadow frame. DCHECK_NE(first_dest_reg, (size_t)-1); } else { @@ -1066,7 +1065,7 @@ static inline bool MethodHandleInvokeExactInternal( bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint( called_method, called_method->GetEntryPointFromQuickCompiledCode()); PerformCall(self, - code_item, + accessor, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, diff --git a/runtime/monitor.cc b/runtime/monitor.cc index cfef9c7d96..542692fe46 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -1377,9 +1377,9 @@ void Monitor::VisitLocks(StackVisitor* stack_visitor, void (*callback)(mirror::O } // Is there any reason to believe there's any synchronization in this method? - const DexFile::CodeItem* code_item = m->GetCodeItem(); - CHECK(code_item != nullptr) << m->PrettyMethod(); - if (code_item->tries_size_ == 0) { + CHECK(m->GetCodeItem() != nullptr) << m->PrettyMethod(); + CodeItemDataAccessor accessor(m); + if (accessor.TriesSize() == 0) { return; // No "tries" implies no synchronization, so no held locks to report. } @@ -1399,11 +1399,10 @@ void Monitor::VisitLocks(StackVisitor* stack_visitor, void (*callback)(mirror::O for (verifier::MethodVerifier::DexLockInfo& dex_lock_info : monitor_enter_dex_pcs) { // As a debug check, check that dex PC corresponds to a monitor-enter. if (kIsDebugBuild) { - const Instruction* monitor_enter_instruction = - Instruction::At(&code_item->insns_[dex_lock_info.dex_pc]); - CHECK_EQ(monitor_enter_instruction->Opcode(), Instruction::MONITOR_ENTER) + const Instruction& monitor_enter_instruction = accessor.InstructionAt(dex_lock_info.dex_pc); + CHECK_EQ(monitor_enter_instruction.Opcode(), Instruction::MONITOR_ENTER) << "expected monitor-enter @" << dex_lock_info.dex_pc << "; was " - << reinterpret_cast(monitor_enter_instruction); + << reinterpret_cast(&monitor_enter_instruction); } // Iterate through the set of dex registers, as the compiler may not have held all of them diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 32f8df7010..f437db281d 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -1500,14 +1500,10 @@ ArrayRef> OatFile::GetBssGcRoots() const { } } -uint32_t OatFile::GetDebugInfoOffset(const DexFile& dex_file, - const DexFile::CodeItem* code_item) { - if (code_item == nullptr) { - return 0; - } - const uint32_t debug_info_off = code_item->debug_info_off_; +uint32_t OatFile::GetDebugInfoOffset(const DexFile& dex_file, uint32_t debug_info_off) { // Note that although the specification says that 0 should be used if there // is no debug information, some applications incorrectly use 0xFFFFFFFF. + // The following check also handles debug_info_off == 0. if (debug_info_off < dex_file.Size() || debug_info_off == 0xFFFFFFFF) { return debug_info_off; } diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 36a4d7b8fc..1fb17a46f8 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -114,9 +114,9 @@ class OatFile { const char* abs_dex_location, std::string* error_msg); - // Return the debug info offset of the code item `item` located in `dex_file`. - static uint32_t GetDebugInfoOffset(const DexFile& dex_file, - const DexFile::CodeItem* item); + // Return the actual debug info offset for an offset that might be actually pointing to + // dequickening info. The returned debug info offset is the one originally in the the dex file. + static uint32_t GetDebugInfoOffset(const DexFile& dex_file, uint32_t debug_info_off); virtual ~OatFile(); diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index a7771abc26..06b94c30d2 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -222,7 +222,8 @@ void QuickExceptionHandler::SetCatchEnvironmentForOptimizedHandler(StackVisitor* self_->DumpStack(LOG_STREAM(INFO) << "Setting catch phis: "); } - const size_t number_of_vregs = handler_method_->GetCodeItem()->registers_size_; + CodeItemDataAccessor accessor(handler_method_); + const size_t number_of_vregs = accessor.RegistersSize(); CodeInfo code_info = handler_method_header_->GetOptimizedCodeInfo(); CodeInfoEncoding encoding = code_info.ExtractEncoding(); @@ -359,7 +360,8 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { const size_t frame_id = GetFrameId(); ShadowFrame* new_frame = GetThread()->FindDebuggerShadowFrame(frame_id); const bool* updated_vregs; - const size_t num_regs = method->GetCodeItem()->registers_size_; + CodeItemDataAccessor accessor(method); + const size_t num_regs = accessor.RegistersSize(); if (new_frame == nullptr) { new_frame = ShadowFrame::CreateDeoptimizedFrame(num_regs, nullptr, method, GetDexPc()); updated_vregs = nullptr; @@ -406,7 +408,8 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { uintptr_t native_pc_offset = method_header->NativeQuickPcOffset(GetCurrentQuickFramePc()); CodeInfoEncoding encoding = code_info.ExtractEncoding(); StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); - const size_t number_of_vregs = m->GetCodeItem()->registers_size_; + CodeItemDataAccessor accessor(m); + 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); DexRegisterMap vreg_map = IsInInlinedFrame() diff --git a/runtime/stack.cc b/runtime/stack.cc index 5ad1f7c9c5..bbea48b441 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -154,13 +154,13 @@ mirror::Object* StackVisitor::GetThisObject() const { return cur_shadow_frame_->GetVRegReference(0); } } else { - const DexFile::CodeItem* code_item = m->GetCodeItem(); - if (code_item == nullptr) { + CodeItemDataAccessor accessor(m); + if (!accessor.HasCodeItem()) { UNIMPLEMENTED(ERROR) << "Failed to determine this object of abstract or proxy method: " << ArtMethod::PrettyMethod(m); return nullptr; } else { - uint16_t reg = code_item->registers_size_ - code_item->ins_size_; + uint16_t reg = accessor.RegistersSize() - accessor.InsSize(); uint32_t value = 0; bool success = GetVReg(m, reg, kReferenceVReg, &value); // We currently always guarantee the `this` object is live throughout the method. @@ -223,11 +223,11 @@ bool StackVisitor::GetVReg(ArtMethod* m, uint16_t vreg, VRegKind kind, uint32_t* bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKind kind, uint32_t* val) const { DCHECK_EQ(m, GetMethod()); - const DexFile::CodeItem* code_item = m->GetCodeItem(); - DCHECK(code_item != nullptr) << m->PrettyMethod(); // Can't be null or how would we compile - // its instructions? - uint16_t number_of_dex_registers = code_item->registers_size_; - DCHECK_LT(vreg, code_item->registers_size_); + // Can't be null or how would we compile its instructions? + DCHECK(m->GetCodeItem() != nullptr) << m->PrettyMethod(); + CodeItemDataAccessor accessor(m); + 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(); @@ -395,8 +395,8 @@ bool StackVisitor::SetVReg(ArtMethod* m, uint16_t vreg, uint32_t new_value, VRegKind kind) { - const DexFile::CodeItem* code_item = m->GetCodeItem(); - if (code_item == nullptr) { + CodeItemDataAccessor accessor(m); + if (!accessor.HasCodeItem()) { return false; } ShadowFrame* shadow_frame = GetCurrentShadowFrame(); @@ -404,7 +404,7 @@ bool StackVisitor::SetVReg(ArtMethod* m, // This is a compiled frame: we must prepare and update a shadow frame that will // be executed by the interpreter after deoptimization of the stack. const size_t frame_id = GetFrameId(); - const uint16_t num_regs = code_item->registers_size_; + const uint16_t num_regs = accessor.RegistersSize(); shadow_frame = thread_->FindOrCreateDebuggerShadowFrame(frame_id, num_regs, m, GetDexPc()); CHECK(shadow_frame != nullptr); // Remember the vreg has been set for debugging and must not be overwritten by the @@ -432,15 +432,15 @@ bool StackVisitor::SetVRegPair(ArtMethod* m, LOG(FATAL) << "Expected long or double: kind_lo=" << kind_lo << ", kind_hi=" << kind_hi; UNREACHABLE(); } - const DexFile::CodeItem* code_item = m->GetCodeItem(); - if (code_item == nullptr) { + CodeItemDataAccessor accessor(m); + if (!accessor.HasCodeItem()) { return false; } ShadowFrame* shadow_frame = GetCurrentShadowFrame(); if (shadow_frame == nullptr) { // This is a compiled frame: we must prepare for deoptimization (see SetVRegFromDebugger). const size_t frame_id = GetFrameId(); - const uint16_t num_regs = code_item->registers_size_; + const uint16_t num_regs = accessor.RegistersSize(); shadow_frame = thread_->FindOrCreateDebuggerShadowFrame(frame_id, num_regs, m, GetDexPc()); CHECK(shadow_frame != nullptr); // Remember the vreg pair has been set for debugging and must not be overwritten by the diff --git a/runtime/thread.cc b/runtime/thread.cc index b86e56b531..6babd75d48 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -3425,7 +3425,7 @@ class ReferenceMapVisitor : public StackVisitor { const CodeInfoEncoding& _encoding, const StackMap& map, RootVisitor& _visitor) - : number_of_dex_registers(method->GetCodeItem()->registers_size_), + : number_of_dex_registers(CodeItemDataAccessor(method).RegistersSize()), code_info(_code_info), encoding(_encoding), dex_register_map(code_info.GetDexRegisterMapOf(map, diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 4ff49edb90..be6915f04b 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -350,13 +350,13 @@ FailureKind MethodVerifier::VerifyClass(Thread* self, } } -static bool IsLargeMethod(const DexFile::CodeItem* const code_item) { - if (code_item == nullptr) { +static bool IsLargeMethod(const CodeItemDataAccessor& accessor) { + if (!accessor.HasCodeItem()) { return false; } - uint16_t registers_size = code_item->registers_size_; - uint32_t insns_size = code_item->insns_size_in_code_units_; + uint16_t registers_size = accessor.RegistersSize(); + uint32_t insns_size = accessor.InsnsSizeInCodeUnits(); return registers_size * insns_size > 4*1024*1024; } @@ -494,7 +494,7 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, if (duration_ns > MsToNs(100)) { LOG(WARNING) << "Verification of " << dex_file->PrettyMethod(method_idx) << " took " << PrettyDuration(duration_ns) - << (IsLargeMethod(code_item) ? " (large method)" : ""); + << (IsLargeMethod(verifier.CodeItem()) ? " (large method)" : ""); } } result.types = verifier.encountered_failure_types_; @@ -567,7 +567,7 @@ MethodVerifier::MethodVerifier(Thread* self, dex_cache_(dex_cache), class_loader_(class_loader), class_def_(class_def), - code_item_accessor_(CodeItemDataAccessor::CreateNullable(dex_file, code_item)), + code_item_accessor_(dex_file, code_item), declaring_class_(nullptr), interesting_dex_pc_(-1), monitor_enter_dex_pcs_(nullptr), diff --git a/test/466-get-live-vreg/get_live_vreg_jni.cc b/test/466-get-live-vreg/get_live_vreg_jni.cc index 6cea673b41..24792f1eed 100644 --- a/test/466-get-live-vreg/get_live_vreg_jni.cc +++ b/test/466-get-live-vreg/get_live_vreg_jni.cc @@ -16,6 +16,7 @@ #include "arch/context.h" #include "art_method-inl.h" +#include "code_item_accessors-inl.h" #include "jni.h" #include "oat_quick_method_header.h" #include "scoped_thread_state_change-inl.h" @@ -41,7 +42,7 @@ class TestVisitor : public StackVisitor { CHECK(GetVReg(m, 0, kIntVReg, &value)); CHECK_EQ(value, 42u); } else if (m_name.compare("$opt$noinline$testIntervalHole") == 0) { - uint32_t number_of_dex_registers = m->GetCodeItem()->registers_size_; + uint32_t number_of_dex_registers = CodeItemDataAccessor(m).RegistersSize(); uint32_t dex_register_of_first_parameter = number_of_dex_registers - 2; found_method_ = true; uint32_t value = 0; -- GitLab From 55256cb60e11d4fac71affb4b9760a2931a3598d Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 21 Dec 2017 17:07:11 -0800 Subject: [PATCH 223/226] Extensions to check JNI. Ensure critical lock isn't held when returning from a down-call. Log a warning if the critical lock is held for a significant period of time. Refactor JNIEnvExt to be a class rather than a struct. Test: mma test-art-host Change-Id: I4d149cb04d3a7308a22b92b196e51e2f1ae17ede --- dex2oat/dex2oat.cc | 6 +- openjdkjvm/OpenjdkJvm.cc | 2 +- openjdkjvmti/ti_class.cc | 2 +- openjdkjvmti/ti_stack.cc | 2 +- runtime/base/mutex.cc | 2 +- runtime/base/time_utils.h | 9 ++ runtime/check_jni.cc | 69 +++++++--- runtime/class_linker.cc | 4 +- .../quick/quick_jni_entrypoints.cc | 16 +-- runtime/gc/heap.cc | 2 +- runtime/gc/reference_processor.cc | 2 +- runtime/indirect_reference_table.cc | 2 +- runtime/java_vm_ext.cc | 2 +- runtime/jni_env_ext-inl.h | 8 +- runtime/jni_env_ext.cc | 85 ++++++------ runtime/jni_env_ext.h | 128 +++++++++++++----- runtime/jni_internal.cc | 24 ++-- runtime/jni_internal_test.cc | 8 +- runtime/native/dalvik_system_VMRuntime.cc | 2 +- runtime/native/java_lang_Thread.cc | 2 +- ...pache_harmony_dalvik_ddmc_DdmVmInternal.cc | 8 +- runtime/reflection.cc | 8 +- runtime/runtime.cc | 2 +- runtime/scoped_thread_state_change-inl.h | 4 +- runtime/scoped_thread_state_change.h | 2 +- runtime/thread-inl.h | 2 +- runtime/thread.cc | 26 ++-- runtime/thread.h | 2 +- .../daemon_jni_shutdown.cc | 2 +- 29 files changed, 268 insertions(+), 165 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index b3d956137a..f5d09dea45 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1695,10 +1695,10 @@ class Dex2Oat FINAL { CHECK(class_loader != nullptr); ScopedObjectAccess soa(Thread::Current()); // Unload class loader to free RAM. - jweak weak_class_loader = soa.Env()->vm->AddWeakGlobalRef( + jweak weak_class_loader = soa.Env()->GetVm()->AddWeakGlobalRef( soa.Self(), soa.Decode(class_loader)); - soa.Env()->vm->DeleteGlobalRef(soa.Self(), class_loader); + soa.Env()->GetVm()->DeleteGlobalRef(soa.Self(), class_loader); runtime_->GetHeap()->CollectGarbage(/*clear_soft_references*/ true); ObjPtr decoded_weak = soa.Decode(weak_class_loader); if (decoded_weak != nullptr) { @@ -2898,7 +2898,7 @@ class ScopedGlobalRef { ~ScopedGlobalRef() { if (obj_ != nullptr) { ScopedObjectAccess soa(Thread::Current()); - soa.Env()->vm->DeleteGlobalRef(soa.Self(), obj_); + soa.Env()->GetVm()->DeleteGlobalRef(soa.Self(), obj_); } } diff --git a/openjdkjvm/OpenjdkJvm.cc b/openjdkjvm/OpenjdkJvm.cc index 1b8233aae8..ff839f5126 100644 --- a/openjdkjvm/OpenjdkJvm.cc +++ b/openjdkjvm/OpenjdkJvm.cc @@ -387,7 +387,7 @@ JNIEXPORT void JVM_Interrupt(JNIEnv* env, jobject jthread) { JNIEXPORT jboolean JVM_IsInterrupted(JNIEnv* env, jobject jthread, jboolean clearInterrupted) { if (clearInterrupted) { - return static_cast(env)->self->Interrupted() ? JNI_TRUE : JNI_FALSE; + return static_cast(env)->GetSelf()->Interrupted() ? JNI_TRUE : JNI_FALSE; } else { art::ScopedFastNativeObjectAccess soa(env); art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_); diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc index e69c78bab1..60ab0a50e6 100644 --- a/openjdkjvmti/ti_class.cc +++ b/openjdkjvmti/ti_class.cc @@ -490,7 +490,7 @@ struct ClassCallback : public art::ClassLoadCallback { // Fix up the local table with a root visitor. RootUpdater local_update(local->input_, local->output_); - t->GetJniEnv()->locals.VisitRoots( + t->GetJniEnv()->VisitJniLocalRoots( &local_update, art::RootInfo(art::kRootJNILocal, t->GetThreadId())); } diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc index b43eaa0286..8ee150ee3e 100644 --- a/openjdkjvmti/ti_stack.cc +++ b/openjdkjvmti/ti_stack.cc @@ -891,7 +891,7 @@ struct MonitorInfoClosure : public art::Closure { visitor.WalkStack(/* include_transitions */ false); // Find any other monitors, including ones acquired in native code. art::RootInfo root_info(art::kRootVMInternal); - target->GetJniEnv()->monitors.VisitRoots(&visitor, root_info); + target->GetJniEnv()->VisitMonitorRoots(&visitor, root_info); err_ = handle_results_(soa_, visitor); } diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 7324dff974..e88ed68ef1 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -959,7 +959,7 @@ void ConditionVariable::WaitHoldingLocks(Thread* self) { } if (self != nullptr) { JNIEnvExt* const env = self->GetJniEnv(); - if (UNLIKELY(env != nullptr && env->runtime_deleted)) { + if (UNLIKELY(env != nullptr && env->IsRuntimeDeleted())) { CHECK(self->IsDaemon()); // If the runtime has been deleted, then we cannot proceed. Just sleep forever. This may // occur for user daemon threads that get a spurious wakeup. This occurs for test 132 with diff --git a/runtime/base/time_utils.h b/runtime/base/time_utils.h index 919937f5ba..7648a109fa 100644 --- a/runtime/base/time_utils.h +++ b/runtime/base/time_utils.h @@ -76,6 +76,15 @@ static constexpr inline uint64_t MsToNs(uint64_t ms) { return ms * 1000 * 1000; } +// Converts the given number of milliseconds to microseconds +static constexpr inline uint64_t MsToUs(uint64_t ms) { + return ms * 1000; +} + +static constexpr inline uint64_t UsToNs(uint64_t us) { + return us * 1000; +} + #if defined(__APPLE__) #ifndef CLOCK_REALTIME // No clocks to specify on OS/X < 10.12, fake value to pass to routines that require a clock. diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index 90f478f5f4..ce4cee827a 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -28,6 +28,7 @@ #include "art_method-inl.h" #include "base/macros.h" #include "base/to_str.h" +#include "base/time_utils.h" #include "class_linker-inl.h" #include "class_linker.h" #include "dex_file-inl.h" @@ -55,24 +56,37 @@ using android::base::StringPrintf; * =========================================================================== */ +// Warn if a JNI critical is held for longer than 16ms. +static constexpr uint64_t kCriticalWarnTimeUs = MsToUs(16); +static_assert(kCriticalWarnTimeUs > 0, "No JNI critical warn time set"); + // Flags passed into ScopedCheck. -#define kFlag_Default 0x0000 +static constexpr uint16_t kFlag_Default = 0x0000; -#define kFlag_CritBad 0x0000 // Calling while in critical is not allowed. -#define kFlag_CritOkay 0x0001 // Calling while in critical is allowed. -#define kFlag_CritGet 0x0002 // This is a critical "get". -#define kFlag_CritRelease 0x0003 // This is a critical "release". -#define kFlag_CritMask 0x0003 // Bit mask to get "crit" value. +// Calling while in critical is not allowed. +static constexpr uint16_t kFlag_CritBad = 0x0000; +// Calling while in critical is allowed. +static constexpr uint16_t kFlag_CritOkay = 0x0001; +// This is a critical "get". +static constexpr uint16_t kFlag_CritGet = 0x0002; +// This is a critical "release". +static constexpr uint16_t kFlag_CritRelease = 0x0003; +// Bit mask to get "crit" value. +static constexpr uint16_t kFlag_CritMask = 0x0003; -#define kFlag_ExcepBad 0x0000 // Raised exceptions are not allowed. -#define kFlag_ExcepOkay 0x0004 // Raised exceptions are allowed. +// Raised exceptions are allowed. +static constexpr uint16_t kFlag_ExcepOkay = 0x0004; -#define kFlag_Release 0x0010 // Are we in a non-critical release function? -#define kFlag_NullableUtf 0x0020 // Are our UTF parameters nullable? +// Are we in a non-critical release function? +static constexpr uint16_t kFlag_Release = 0x0010; +// Are our UTF parameters nullable? +static constexpr uint16_t kFlag_NullableUtf = 0x0020; -#define kFlag_Invocation 0x8000 // Part of the invocation interface (JavaVM*). +// Part of the invocation interface (JavaVM*). +static constexpr uint16_t kFlag_Invocation = 0x0100; -#define kFlag_ForceTrace 0x80000000 // Add this to a JNI function's flags if you want to trace every call. +// Add this to a JNI function's flags if you want to trace every call. +static constexpr uint16_t kFlag_ForceTrace = 0x8000; class VarArgs; /* @@ -249,8 +263,8 @@ class VarArgs { class ScopedCheck { public: - ScopedCheck(int flags, const char* functionName, bool has_method = true) - : function_name_(functionName), flags_(flags), indent_(0), has_method_(has_method) { + ScopedCheck(uint16_t flags, const char* functionName, bool has_method = true) + : function_name_(functionName), indent_(0), flags_(flags), has_method_(has_method) { } ~ScopedCheck() {} @@ -1197,7 +1211,7 @@ class ScopedCheck { // this particular instance of JNIEnv. if (env != threadEnv) { // Get the thread owning the JNIEnv that's being used. - Thread* envThread = reinterpret_cast(env)->self; + Thread* envThread = reinterpret_cast(env)->self_; AbortF("thread %s using JNIEnv* from thread %s", ToStr(*self).c_str(), ToStr(*envThread).c_str()); return false; @@ -1209,7 +1223,7 @@ class ScopedCheck { case kFlag_CritOkay: // okay to call this method break; case kFlag_CritBad: // not okay to call - if (threadEnv->critical) { + if (threadEnv->critical_ > 0) { AbortF("thread %s using JNI after critical get", ToStr(*self).c_str()); return false; @@ -1217,15 +1231,25 @@ class ScopedCheck { break; case kFlag_CritGet: // this is a "get" call // Don't check here; we allow nested gets. - threadEnv->critical++; + if (threadEnv->critical_ == 0) { + threadEnv->critical_start_us_ = self->GetCpuMicroTime(); + } + threadEnv->critical_++; break; case kFlag_CritRelease: // this is a "release" call - threadEnv->critical--; - if (threadEnv->critical < 0) { + if (threadEnv->critical_ == 0) { AbortF("thread %s called too many critical releases", ToStr(*self).c_str()); return false; + } else if (threadEnv->critical_ == 1) { + // Leaving the critical region, possibly warn about long critical regions. + uint64_t critical_duration_us = self->GetCpuMicroTime() - threadEnv->critical_start_us_; + if (critical_duration_us > kCriticalWarnTimeUs) { + LOG(WARNING) << "JNI critical lock held for " + << PrettyDuration(UsToNs(critical_duration_us)) << " on " << *self; + } } + threadEnv->critical_--; break; default: LOG(FATAL) << "Bad flags (internal error): " << flags_; @@ -1357,9 +1381,10 @@ class ScopedCheck { // The name of the JNI function being checked. const char* const function_name_; - const int flags_; int indent_; + const uint16_t flags_; + const bool has_method_; DISALLOW_COPY_AND_ASSIGN(ScopedCheck); @@ -2592,11 +2617,11 @@ class CheckJNI { private: static JavaVMExt* GetJavaVMExt(JNIEnv* env) { - return reinterpret_cast(env)->vm; + return reinterpret_cast(env)->GetVm(); } static const JNINativeInterface* baseEnv(JNIEnv* env) { - return reinterpret_cast(env)->unchecked_functions; + return reinterpret_cast(env)->unchecked_functions_; } static jobject NewRef(const char* function_name, JNIEnv* env, jobject obj, IndirectRefKind kind) { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 55fa6328f5..ae285c7737 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3391,7 +3391,7 @@ void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, // 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); - JavaVMExt* const vm = self->GetJniEnv()->vm; + JavaVMExt* const vm = self->GetJniEnv()->GetVm(); for (auto it = dex_caches_.begin(); it != dex_caches_.end(); ) { DexCacheData data = *it; if (self->IsJWeakCleared(data.weak_root)) { @@ -5269,7 +5269,7 @@ void ClassLinker::RegisterClassLoader(ObjPtr class_loader) CHECK(class_loader->GetClassTable() == nullptr); Thread* const self = Thread::Current(); ClassLoaderData data; - data.weak_root = self->GetJniEnv()->vm->AddWeakGlobalRef(self, class_loader); + data.weak_root = self->GetJniEnv()->GetVm()->AddWeakGlobalRef(self, class_loader); // Create and set the class table. data.class_table = new ClassTable; class_loader->SetClassTable(data.class_table); diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc index b13b6fbcae..3c41a8c3b5 100644 --- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc @@ -51,8 +51,8 @@ extern void ReadBarrierJni(mirror::CompressedReference* handle_o extern uint32_t JniMethodFastStart(Thread* self) { JNIEnvExt* env = self->GetJniEnv(); DCHECK(env != nullptr); - uint32_t saved_local_ref_cookie = bit_cast(env->local_ref_cookie); - env->local_ref_cookie = env->locals.GetSegmentState(); + uint32_t saved_local_ref_cookie = bit_cast(env->GetLocalRefCookie()); + env->SetLocalRefCookie(env->GetLocalsSegmentState()); if (kIsDebugBuild) { ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); @@ -66,8 +66,8 @@ extern uint32_t JniMethodFastStart(Thread* self) { extern uint32_t JniMethodStart(Thread* self) { JNIEnvExt* env = self->GetJniEnv(); DCHECK(env != nullptr); - uint32_t saved_local_ref_cookie = bit_cast(env->local_ref_cookie); - env->local_ref_cookie = env->locals.GetSegmentState(); + uint32_t saved_local_ref_cookie = bit_cast(env->GetLocalRefCookie()); + env->SetLocalRefCookie(env->GetLocalsSegmentState()); ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); // TODO: Introduce special entrypoint for synchronized @FastNative methods? // Or ban synchronized @FastNative outright to avoid the extra check here? @@ -115,11 +115,11 @@ ALWAYS_INLINE static inline void GoToRunnableFast(Thread* self) { static void PopLocalReferences(uint32_t saved_local_ref_cookie, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { JNIEnvExt* env = self->GetJniEnv(); - if (UNLIKELY(env->check_jni)) { + if (UNLIKELY(env->IsCheckJniEnabled())) { env->CheckNoHeldMonitors(); } - env->locals.SetSegmentState(env->local_ref_cookie); - env->local_ref_cookie = bit_cast(saved_local_ref_cookie); + env->SetLocalSegmentState(env->GetLocalRefCookie()); + env->SetLocalRefCookie(bit_cast(saved_local_ref_cookie)); self->PopHandleScope(); } @@ -156,7 +156,7 @@ static mirror::Object* JniMethodEndWithReferenceHandleResult(jobject result, } PopLocalReferences(saved_local_ref_cookie, self); // Process result. - if (UNLIKELY(self->GetJniEnv()->check_jni)) { + if (UNLIKELY(self->GetJniEnv()->IsCheckJniEnabled())) { // CheckReferenceResult can resolve types. StackHandleScope<1> hs(self); HandleWrapperObjPtr h_obj(hs.NewHandleWrapper(&o)); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index f7be4c8d5a..3ba12ca493 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -1299,7 +1299,7 @@ class TrimIndirectReferenceTableClosure : public Closure { explicit TrimIndirectReferenceTableClosure(Barrier* barrier) : barrier_(barrier) { } virtual void Run(Thread* thread) OVERRIDE NO_THREAD_SAFETY_ANALYSIS { - thread->GetJniEnv()->locals.Trim(); + thread->GetJniEnv()->TrimLocals(); // If thread is a running mutator, then act on behalf of the trim thread. // See the code in ThreadList::RunCheckpoint. barrier_->Pass(Thread::Current()); diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc index d58d09c794..c59642fe4e 100644 --- a/runtime/gc/reference_processor.cc +++ b/runtime/gc/reference_processor.cc @@ -273,7 +273,7 @@ void ReferenceProcessor::EnqueueClearedReferences(Thread* self) { jobject cleared_references; { ReaderMutexLock mu(self, *Locks::mutator_lock_); - cleared_references = self->GetJniEnv()->vm->AddGlobalRef( + cleared_references = self->GetJniEnv()->GetVm()->AddGlobalRef( self, cleared_references_.GetList()); } if (kAsyncReferenceQueueAdd) { diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc index 2c8ec47492..51878312d9 100644 --- a/runtime/indirect_reference_table.cc +++ b/runtime/indirect_reference_table.cc @@ -357,7 +357,7 @@ bool IndirectReferenceTable::Remove(IRTSegmentState previous_state, IndirectRef if (self->HandleScopeContains(reinterpret_cast(iref))) { auto* env = self->GetJniEnv(); DCHECK(env != nullptr); - if (env->check_jni) { + if (env->IsCheckJniEnabled()) { ScopedObjectAccess soa(self); LOG(WARNING) << "Attempt to remove non-JNI local reference, dumping thread"; if (kDumpStackOnNonLocalReference) { diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc index f8b82ed313..104fb66ed8 100644 --- a/runtime/java_vm_ext.cc +++ b/runtime/java_vm_ext.cc @@ -337,7 +337,7 @@ class Libraries { } else { VLOG(jni) << "[JNI_OnUnload found for \"" << library->GetPath() << "\"]: Calling..."; JNI_OnUnloadFn jni_on_unload = reinterpret_cast(sym); - jni_on_unload(self->GetJniEnv()->vm, nullptr); + jni_on_unload(self->GetJniEnv()->GetVm(), nullptr); } delete library; } diff --git a/runtime/jni_env_ext-inl.h b/runtime/jni_env_ext-inl.h index d66df081c6..14f708b18d 100644 --- a/runtime/jni_env_ext-inl.h +++ b/runtime/jni_env_ext-inl.h @@ -26,7 +26,7 @@ namespace art { template inline T JNIEnvExt::AddLocalReference(ObjPtr obj) { std::string error_msg; - IndirectRef ref = locals.Add(local_ref_cookie, obj, &error_msg); + IndirectRef ref = locals_.Add(local_ref_cookie_, obj, &error_msg); if (UNLIKELY(ref == nullptr)) { // This is really unexpected if we allow resizing local IRTs... LOG(FATAL) << error_msg; @@ -35,10 +35,10 @@ inline T JNIEnvExt::AddLocalReference(ObjPtr obj) { // TODO: fix this to understand PushLocalFrame, so we can turn it on. if (false) { - if (check_jni) { - size_t entry_count = locals.Capacity(); + if (check_jni_) { + size_t entry_count = locals_.Capacity(); if (entry_count > 16) { - locals.Dump(LOG_STREAM(WARNING) << "Warning: more than 16 JNI local references: " + locals_.Dump(LOG_STREAM(WARNING) << "Warning: more than 16 JNI local references: " << entry_count << " (most recent was a " << mirror::Object::PrettyTypeOf(obj) << ")\n"); // TODO: LOG(FATAL) in a later release? diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc index 8352657f28..efe43ee0e9 100644 --- a/runtime/jni_env_ext.cc +++ b/runtime/jni_env_ext.cc @@ -21,6 +21,7 @@ #include "android-base/stringprintf.h" +#include "base/to_str.h" #include "check_jni.h" #include "indirect_reference_table.h" #include "java_vm_ext.h" @@ -28,6 +29,7 @@ #include "lock_word.h" #include "mirror/object-inl.h" #include "nth_caller_visitor.h" +#include "scoped_thread_state_change.h" #include "thread-current-inl.h" #include "thread_list.h" @@ -40,14 +42,11 @@ static constexpr size_t kMonitorsMax = 4096; // Arbitrary sanity check. const JNINativeInterface* JNIEnvExt::table_override_ = nullptr; -// Checking "locals" requires the mutator lock, but at creation time we're really only interested -// in validity, which isn't changing. To avoid grabbing the mutator lock, factored out and tagged -// with NO_THREAD_SAFETY_ANALYSIS. -static bool CheckLocalsValid(JNIEnvExt* in) NO_THREAD_SAFETY_ANALYSIS { +bool JNIEnvExt::CheckLocalsValid(JNIEnvExt* in) NO_THREAD_SAFETY_ANALYSIS { if (in == nullptr) { return false; } - return in->locals.IsValid(); + return in->locals_.IsValid(); } jint JNIEnvExt::GetEnvHandler(JavaVMExt* vm, /*out*/void** env, jint version) { @@ -73,23 +72,23 @@ JNIEnvExt* JNIEnvExt::Create(Thread* self_in, JavaVMExt* vm_in, std::string* err } JNIEnvExt::JNIEnvExt(Thread* self_in, JavaVMExt* vm_in, std::string* error_msg) - : self(self_in), - vm(vm_in), - local_ref_cookie(kIRTFirstSegment), - locals(kLocalsInitial, kLocal, IndirectReferenceTable::ResizableCapacity::kYes, error_msg), - check_jni(false), - runtime_deleted(false), - critical(0), - monitors("monitors", kMonitorsInitial, kMonitorsMax) { + : self_(self_in), + vm_(vm_in), + local_ref_cookie_(kIRTFirstSegment), + locals_(kLocalsInitial, kLocal, IndirectReferenceTable::ResizableCapacity::kYes, error_msg), + monitors_("monitors", kMonitorsInitial, kMonitorsMax), + critical_(0), + check_jni_(false), + runtime_deleted_(false) { MutexLock mu(Thread::Current(), *Locks::jni_function_table_lock_); - check_jni = vm->IsCheckJniEnabled(); - functions = GetFunctionTable(check_jni); - unchecked_functions = GetJniNativeInterface(); + check_jni_ = vm_in->IsCheckJniEnabled(); + functions = GetFunctionTable(check_jni_); + unchecked_functions_ = GetJniNativeInterface(); } void JNIEnvExt::SetFunctionsToRuntimeShutdownFunctions() { functions = GetRuntimeShutdownNativeInterface(); - runtime_deleted = true; + runtime_deleted_ = true; } JNIEnvExt::~JNIEnvExt() { @@ -100,7 +99,7 @@ jobject JNIEnvExt::NewLocalRef(mirror::Object* obj) { return nullptr; } std::string error_msg; - jobject ref = reinterpret_cast(locals.Add(local_ref_cookie, obj, &error_msg)); + jobject ref = reinterpret_cast(locals_.Add(local_ref_cookie_, obj, &error_msg)); if (UNLIKELY(ref == nullptr)) { // This is really unexpected if we allow resizing local IRTs... LOG(FATAL) << error_msg; @@ -111,12 +110,12 @@ jobject JNIEnvExt::NewLocalRef(mirror::Object* obj) { void JNIEnvExt::DeleteLocalRef(jobject obj) { if (obj != nullptr) { - locals.Remove(local_ref_cookie, reinterpret_cast(obj)); + locals_.Remove(local_ref_cookie_, reinterpret_cast(obj)); } } void JNIEnvExt::SetCheckJniEnabled(bool enabled) { - check_jni = enabled; + check_jni_ = enabled; MutexLock mu(Thread::Current(), *Locks::jni_function_table_lock_); functions = GetFunctionTable(enabled); // Check whether this is a no-op because of override. @@ -126,20 +125,20 @@ void JNIEnvExt::SetCheckJniEnabled(bool enabled) { } void JNIEnvExt::DumpReferenceTables(std::ostream& os) { - locals.Dump(os); - monitors.Dump(os); + locals_.Dump(os); + monitors_.Dump(os); } void JNIEnvExt::PushFrame(int capacity) { - DCHECK_GE(locals.FreeCapacity(), static_cast(capacity)); - stacked_local_ref_cookies.push_back(local_ref_cookie); - local_ref_cookie = locals.GetSegmentState(); + DCHECK_GE(locals_.FreeCapacity(), static_cast(capacity)); + stacked_local_ref_cookies_.push_back(local_ref_cookie_); + local_ref_cookie_ = locals_.GetSegmentState(); } void JNIEnvExt::PopFrame() { - locals.SetSegmentState(local_ref_cookie); - local_ref_cookie = stacked_local_ref_cookies.back(); - stacked_local_ref_cookies.pop_back(); + locals_.SetSegmentState(local_ref_cookie_); + local_ref_cookie_ = stacked_local_ref_cookies_.back(); + stacked_local_ref_cookies_.pop_back(); } // Note: the offset code is brittle, as we can't use OFFSETOF_MEMBER or offsetof easily. Thus, there @@ -188,7 +187,7 @@ static uintptr_t GetJavaCallFrame(Thread* self) REQUIRES_SHARED(Locks::mutator_l } void JNIEnvExt::RecordMonitorEnter(jobject obj) { - locked_objects_.push_back(std::make_pair(GetJavaCallFrame(self), obj)); + locked_objects_.push_back(std::make_pair(GetJavaCallFrame(self_), obj)); } static std::string ComputeMonitorDescription(Thread* self, @@ -230,7 +229,7 @@ static void RemoveMonitors(Thread* self, } void JNIEnvExt::CheckMonitorRelease(jobject obj) { - uintptr_t current_frame = GetJavaCallFrame(self); + uintptr_t current_frame = GetJavaCallFrame(self_); std::pair exact_pair = std::make_pair(current_frame, obj); auto it = std::find(locked_objects_.begin(), locked_objects_.end(), exact_pair); bool will_abort = false; @@ -238,11 +237,11 @@ void JNIEnvExt::CheckMonitorRelease(jobject obj) { locked_objects_.erase(it); } else { // Check whether this monitor was locked in another JNI "session." - ObjPtr mirror_obj = self->DecodeJObject(obj); + ObjPtr mirror_obj = self_->DecodeJObject(obj); for (std::pair& pair : locked_objects_) { - if (self->DecodeJObject(pair.second) == mirror_obj) { - std::string monitor_descr = ComputeMonitorDescription(self, pair.second); - vm->JniAbortF("", + if (self_->DecodeJObject(pair.second) == mirror_obj) { + std::string monitor_descr = ComputeMonitorDescription(self_, pair.second); + vm_->JniAbortF("", "Unlocking monitor that wasn't locked here: %s", monitor_descr.c_str()); will_abort = true; @@ -255,26 +254,26 @@ void JNIEnvExt::CheckMonitorRelease(jobject obj) { // the monitors table, otherwise we may visit local objects in GC during abort (which won't be // valid anymore). if (will_abort) { - RemoveMonitors(self, current_frame, &monitors, &locked_objects_); + RemoveMonitors(self_, current_frame, &monitors_, &locked_objects_); } } void JNIEnvExt::CheckNoHeldMonitors() { - uintptr_t current_frame = GetJavaCallFrame(self); // The locked_objects_ are grouped by their stack frame component, as this enforces structured // locking, and the groups form a stack. So the current frame entries are at the end. Check // whether the vector is empty, and when there are elements, whether the last element belongs // to this call - this signals that there are unlocked monitors. if (!locked_objects_.empty()) { + uintptr_t current_frame = GetJavaCallFrame(self_); std::pair& pair = locked_objects_[locked_objects_.size() - 1]; if (pair.first == current_frame) { - std::string monitor_descr = ComputeMonitorDescription(self, pair.second); - vm->JniAbortF("", + std::string monitor_descr = ComputeMonitorDescription(self_, pair.second); + vm_->JniAbortF("", "Still holding a locked object on JNI end: %s", monitor_descr.c_str()); // When we abort, also make sure that any locks from the current "session" are removed from // the monitors table, otherwise we may visit local objects in GC during abort. - RemoveMonitors(self, current_frame, &monitors, &locked_objects_); + RemoveMonitors(self_, current_frame, &monitors_, &locked_objects_); } else if (kIsDebugBuild) { // Make sure there are really no other entries and our checking worked as expected. for (std::pair& check_pair : locked_objects_) { @@ -282,12 +281,18 @@ void JNIEnvExt::CheckNoHeldMonitors() { } } } + // Ensure critical locks aren't held when returning to Java. + if (critical_ > 0) { + vm_->JniAbortF("", + "Critical lock held when returning to Java on thread %s", + ToStr(*self_).c_str()); + } } static void ThreadResetFunctionTable(Thread* thread, void* arg ATTRIBUTE_UNUSED) REQUIRES(Locks::jni_function_table_lock_) { JNIEnvExt* env = thread->GetJniEnv(); - bool check_jni = env->check_jni; + bool check_jni = env->IsCheckJniEnabled(); env->functions = JNIEnvExt::GetFunctionTable(check_jni); } diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h index 2f6c5dc92a..0e8fd03057 100644 --- a/runtime/jni_env_ext.h +++ b/runtime/jni_env_ext.h @@ -37,10 +37,15 @@ class Object; // low enough that it forces sanity checks. static constexpr size_t kLocalsInitial = 512; -struct JNIEnvExt : public JNIEnv { +class JNIEnvExt : public JNIEnv { + public: // Creates a new JNIEnvExt. Returns null on error, in which case error_msg // will contain a description of the error. static JNIEnvExt* Create(Thread* self, JavaVMExt* vm, std::string* error_msg); + static Offset SegmentStateOffset(size_t pointer_size); + static Offset LocalRefCookieOffset(size_t pointer_size); + static Offset SelfOffset(size_t pointer_size); + static jint GetEnvHandler(JavaVMExt* vm, /*out*/void** out, jint version); ~JNIEnvExt(); @@ -58,43 +63,44 @@ struct JNIEnvExt : public JNIEnv { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::alloc_tracker_lock_); - static Offset SegmentStateOffset(size_t pointer_size); - static Offset LocalRefCookieOffset(size_t pointer_size); - static Offset SelfOffset(size_t pointer_size); - - static jint GetEnvHandler(JavaVMExt* vm, /*out*/void** out, jint version); + void UpdateLocal(IndirectRef iref, ObjPtr obj) REQUIRES_SHARED(Locks::mutator_lock_) { + locals_.Update(iref, obj); + } jobject NewLocalRef(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_); void DeleteLocalRef(jobject obj) REQUIRES_SHARED(Locks::mutator_lock_); - Thread* const self; - JavaVMExt* const vm; - - // Cookie used when using the local indirect reference table. - IRTSegmentState local_ref_cookie; + void TrimLocals() REQUIRES_SHARED(Locks::mutator_lock_) { + locals_.Trim(); + } + void AssertLocalsEmpty() REQUIRES_SHARED(Locks::mutator_lock_) { + locals_.AssertEmpty(); + } + size_t GetLocalsCapacity() REQUIRES_SHARED(Locks::mutator_lock_) { + return locals_.Capacity(); + } - // JNI local references. - IndirectReferenceTable locals GUARDED_BY(Locks::mutator_lock_); + IRTSegmentState GetLocalRefCookie() const { return local_ref_cookie_; } + void SetLocalRefCookie(IRTSegmentState new_cookie) { local_ref_cookie_ = new_cookie; } - // Stack of cookies corresponding to PushLocalFrame/PopLocalFrame calls. - // TODO: to avoid leaks (and bugs), we need to clear this vector on entry (or return) - // to a native method. - std::vector stacked_local_ref_cookies; + IRTSegmentState GetLocalsSegmentState() const REQUIRES_SHARED(Locks::mutator_lock_) { + return locals_.GetSegmentState(); + } + void SetLocalSegmentState(IRTSegmentState new_state) REQUIRES_SHARED(Locks::mutator_lock_) { + locals_.SetSegmentState(new_state); + } - // Frequently-accessed fields cached from JavaVM. - bool check_jni; + void VisitJniLocalRoots(RootVisitor* visitor, const RootInfo& root_info) + REQUIRES_SHARED(Locks::mutator_lock_) { + locals_.VisitRoots(visitor, root_info); + } - // If we are a JNI env for a daemon thread with a deleted runtime. - bool runtime_deleted; + Thread* GetSelf() const { return self_; } + JavaVMExt* GetVm() const { return vm_; } - // How many nested "critical" JNI calls are we in? - int critical; + bool IsRuntimeDeleted() const { return runtime_deleted_; } + bool IsCheckJniEnabled() const { return check_jni_; } - // Entered JNI monitors, for bulk exit on thread detach. - ReferenceTable monitors; - - // Used by -Xcheck:jni. - const JNINativeInterface* unchecked_functions; // Functions to keep track of monitor lock and unlock operations. Used to ensure proper locking // rules in CheckJNI mode. @@ -108,6 +114,11 @@ struct JNIEnvExt : public JNIEnv { // Check that no monitors are held that have been acquired in this JNI "segment." void CheckNoHeldMonitors() REQUIRES_SHARED(Locks::mutator_lock_); + void VisitMonitorRoots(RootVisitor* visitor, const RootInfo& root_info) + REQUIRES_SHARED(Locks::mutator_lock_) { + monitors_.VisitRoots(visitor, root_info); + } + // Set the functions to the runtime shutdown functions. void SetFunctionsToRuntimeShutdownFunctions(); @@ -124,6 +135,11 @@ struct JNIEnvExt : public JNIEnv { REQUIRES(Locks::jni_function_table_lock_); private: + // Checking "locals" requires the mutator lock, but at creation time we're + // really only interested in validity, which isn't changing. To avoid grabbing + // the mutator lock, factored out and tagged with NO_THREAD_SAFETY_ANALYSIS. + static bool CheckLocalsValid(JNIEnvExt* in) NO_THREAD_SAFETY_ANALYSIS; + // Override of function tables. This applies to both default as well as instrumented (CheckJNI) // function tables. static const JNINativeInterface* table_override_ GUARDED_BY(Locks::jni_function_table_lock_); @@ -133,29 +149,73 @@ struct JNIEnvExt : public JNIEnv { JNIEnvExt(Thread* self, JavaVMExt* vm, std::string* error_msg) REQUIRES(!Locks::jni_function_table_lock_); + // Link to Thread::Current(). + Thread* const self_; + + // The invocation interface JavaVM. + JavaVMExt* const vm_; + + // Cookie used when using the local indirect reference table. + IRTSegmentState local_ref_cookie_; + + // JNI local references. + IndirectReferenceTable locals_ GUARDED_BY(Locks::mutator_lock_); + + // Stack of cookies corresponding to PushLocalFrame/PopLocalFrame calls. + // TODO: to avoid leaks (and bugs), we need to clear this vector on entry (or return) + // to a native method. + std::vector stacked_local_ref_cookies_; + + // Entered JNI monitors, for bulk exit on thread detach. + ReferenceTable monitors_; + + // Used by -Xcheck:jni. + const JNINativeInterface* unchecked_functions_; + // All locked objects, with the (Java caller) stack frame that locked them. Used in CheckJNI // to ensure that only monitors locked in this native frame are being unlocked, and that at // the end all are unlocked. std::vector> locked_objects_; + + // Start time of "critical" JNI calls to ensure that their use doesn't + // excessively block the VM with CheckJNI. + uint64_t critical_start_us_; + + // How many nested "critical" JNI calls are we in? Used by CheckJNI to ensure that criticals are + uint32_t critical_; + + // Frequently-accessed fields cached from JavaVM. + bool check_jni_; + + // If we are a JNI env for a daemon thread with a deleted runtime. + bool runtime_deleted_; + + friend class CheckJNI; + friend class JNI; + friend class ScopedCheck; + friend class ScopedJniEnvLocalRefState; + friend class Thread; + ART_FRIEND_TEST(JniInternalTest, JNIEnvExtOffsets); }; // Used to save and restore the JNIEnvExt state when not going through code created by the JNI // compiler. class ScopedJniEnvLocalRefState { public: - explicit ScopedJniEnvLocalRefState(JNIEnvExt* env) : env_(env) { - saved_local_ref_cookie_ = env->local_ref_cookie; - env->local_ref_cookie = env->locals.GetSegmentState(); + explicit ScopedJniEnvLocalRefState(JNIEnvExt* env) : + env_(env), + saved_local_ref_cookie_(env->local_ref_cookie_) { + env->local_ref_cookie_ = env->locals_.GetSegmentState(); } ~ScopedJniEnvLocalRefState() { - env_->locals.SetSegmentState(env_->local_ref_cookie); - env_->local_ref_cookie = saved_local_ref_cookie_; + env_->locals_.SetSegmentState(env_->local_ref_cookie_); + env_->local_ref_cookie_ = saved_local_ref_cookie_; } private: JNIEnvExt* const env_; - IRTSegmentState saved_local_ref_cookie_; + const IRTSegmentState saved_local_ref_cookie_; DISALLOW_COPY_AND_ASSIGN(ScopedJniEnvLocalRefState); }; diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 48fc5f730b..53ae628a44 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -383,7 +383,7 @@ int ThrowNewException(JNIEnv* env, jclass exception_class, const char* msg, jobj } static JavaVMExt* JavaVmExtFromEnv(JNIEnv* env) { - return reinterpret_cast(env)->vm; + return reinterpret_cast(env)->GetVm(); } #define CHECK_NON_NULL_ARGUMENT(value) \ @@ -545,7 +545,7 @@ class JNI { } static jboolean ExceptionCheck(JNIEnv* env) { - return static_cast(env)->self->IsExceptionPending() ? JNI_TRUE : JNI_FALSE; + return static_cast(env)->self_->IsExceptionPending() ? JNI_TRUE : JNI_FALSE; } static void ExceptionClear(JNIEnv* env) { @@ -623,8 +623,8 @@ class JNI { } static void DeleteGlobalRef(JNIEnv* env, jobject obj) { - JavaVMExt* vm = down_cast(env)->vm; - Thread* self = down_cast(env)->self; + JavaVMExt* vm = down_cast(env)->GetVm(); + Thread* self = down_cast(env)->self_; vm->DeleteGlobalRef(self, obj); } @@ -635,8 +635,8 @@ class JNI { } static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj) { - JavaVMExt* vm = down_cast(env)->vm; - Thread* self = down_cast(env)->self; + JavaVMExt* vm = down_cast(env)->GetVm(); + Thread* self = down_cast(env)->self_; vm->DeleteWeakGlobalRef(self, obj); } @@ -659,7 +659,7 @@ class JNI { // it. b/22119403 ScopedObjectAccess soa(env); auto* ext_env = down_cast(env); - if (!ext_env->locals.Remove(ext_env->local_ref_cookie, obj)) { + if (!ext_env->locals_.Remove(ext_env->local_ref_cookie_, obj)) { // Attempting to delete a local reference that is not in the // topmost local reference frame is a no-op. DeleteLocalRef returns // void and doesn't throw any exceptions, but we should probably @@ -2310,7 +2310,7 @@ class JNI { // first, either as a direct or a virtual method. Then move to // the parent. ArtMethod* m = nullptr; - bool warn_on_going_to_parent = down_cast(env)->vm->IsCheckJniEnabled(); + bool warn_on_going_to_parent = down_cast(env)->GetVm()->IsCheckJniEnabled(); for (ObjPtr current_class = c.Get(); current_class != nullptr; current_class = current_class->GetSuperClass()) { @@ -2399,7 +2399,7 @@ class JNI { ObjPtr o = soa.Decode(java_object); o = o->MonitorEnter(soa.Self()); if (soa.Self()->HoldsLock(o)) { - soa.Env()->monitors.Add(o); + soa.Env()->monitors_.Add(o); } if (soa.Self()->IsExceptionPending()) { return JNI_ERR; @@ -2414,7 +2414,7 @@ class JNI { bool remove_mon = soa.Self()->HoldsLock(o); o->MonitorExit(soa.Self()); if (remove_mon) { - soa.Env()->monitors.Remove(o); + soa.Env()->monitors_.Remove(o); } if (soa.Self()->IsExceptionPending()) { return JNI_ERR; @@ -2458,7 +2458,7 @@ class JNI { jobject result = env->NewObject(WellKnownClasses::java_nio_DirectByteBuffer, WellKnownClasses::java_nio_DirectByteBuffer_init, address_arg, capacity_arg); - return static_cast(env)->self->IsExceptionPending() ? nullptr : result; + return static_cast(env)->self_->IsExceptionPending() ? nullptr : result; } static void* GetDirectBufferAddress(JNIEnv* env, jobject java_buffer) { @@ -2504,7 +2504,7 @@ class JNI { } std::string error_msg; - if (!soa.Env()->locals.EnsureFreeCapacity(static_cast(desired_capacity), &error_msg)) { + if (!soa.Env()->locals_.EnsureFreeCapacity(static_cast(desired_capacity), &error_msg)) { std::string caller_error = android::base::StringPrintf("%s: %s", caller, error_msg.c_str()); soa.Self()->ThrowOutOfMemoryError(caller_error.c_str()); return JNI_ERR; diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index efeff0a378..ad24c94ae9 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -867,7 +867,7 @@ TEST_F(JniInternalTest, GetStaticMethodID) { static size_t GetLocalsCapacity(JNIEnv* env) { ScopedObjectAccess soa(Thread::Current()); - return reinterpret_cast(env)->locals.Capacity(); + return reinterpret_cast(env)->GetLocalsCapacity(); } TEST_F(JniInternalTest, FromReflectedField_ToReflectedField) { @@ -2400,15 +2400,15 @@ TEST_F(JniInternalTest, IndirectReferenceTableOffsets) { // Test the offset computation of JNIEnvExt offsets. b/26071368. TEST_F(JniInternalTest, JNIEnvExtOffsets) { - EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, local_ref_cookie), + EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, local_ref_cookie_), JNIEnvExt::LocalRefCookieOffset(sizeof(void*)).Uint32Value()); - EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, self), JNIEnvExt::SelfOffset(sizeof(void*)).Uint32Value()); + EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, self_), JNIEnvExt::SelfOffset(sizeof(void*)).Uint32Value()); // segment_state_ is private in the IndirectReferenceTable. So this test isn't as good as we'd // hope it to be. uint32_t segment_state_now = - OFFSETOF_MEMBER(JNIEnvExt, locals) + + OFFSETOF_MEMBER(JNIEnvExt, locals_) + IndirectReferenceTable::SegmentStateOffset(sizeof(void*)).Uint32Value(); uint32_t segment_state_computed = JNIEnvExt::SegmentStateOffset(sizeof(void*)).Uint32Value(); EXPECT_EQ(segment_state_now, segment_state_computed); diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 1b5c535c87..6446e1b4f5 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -223,7 +223,7 @@ static jboolean VMRuntime_is64Bit(JNIEnv*, jobject) { } static jboolean VMRuntime_isCheckJniEnabled(JNIEnv* env, jobject) { - return down_cast(env)->vm->IsCheckJniEnabled() ? JNI_TRUE : JNI_FALSE; + return down_cast(env)->GetVm()->IsCheckJniEnabled() ? JNI_TRUE : JNI_FALSE; } static void VMRuntime_setTargetSdkVersionNative(JNIEnv*, jobject, jint target_sdk_version) { diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc index a717264bcb..9a52f7002b 100644 --- a/runtime/native/java_lang_Thread.cc +++ b/runtime/native/java_lang_Thread.cc @@ -37,7 +37,7 @@ static jobject Thread_currentThread(JNIEnv* env, jclass) { } static jboolean Thread_interrupted(JNIEnv* env, jclass) { - return static_cast(env)->self->Interrupted() ? JNI_TRUE : JNI_FALSE; + return static_cast(env)->GetSelf()->Interrupted() ? JNI_TRUE : JNI_FALSE; } static jboolean Thread_isInterrupted(JNIEnv* env, jobject java_thread) { diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc index 7b733824c5..836637f4f0 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc @@ -32,6 +32,10 @@ namespace art { +static Thread* GetSelf(JNIEnv* env) { + return static_cast(env)->GetSelf(); +} + static void DdmVmInternal_enableRecentAllocations(JNIEnv*, jclass, jboolean enable) { Dbg::SetAllocTrackingEnabled(enable); } @@ -51,7 +55,7 @@ static jboolean DdmVmInternal_getRecentAllocationStatus(JNIEnv*, jclass) { */ static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint thin_lock_id) { jobjectArray trace = nullptr; - Thread* const self = Thread::Current(); + Thread* const self = GetSelf(env); if (static_cast(thin_lock_id) == self->GetThreadId()) { // No need to suspend ourself to build stacktrace. ScopedObjectAccess soa(env); @@ -136,7 +140,7 @@ static void ThreadStatsGetterCallback(Thread* t, void* context) { static jbyteArray DdmVmInternal_getThreadStats(JNIEnv* env, jclass) { std::vector bytes; - Thread* self = static_cast(env)->self; + Thread* self = GetSelf(env); { MutexLock mu(self, *Locks::thread_list_lock_); ThreadList* thread_list = Runtime::Current()->GetThreadList(); diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 9683cedd4d..cacbe87f71 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -447,7 +447,7 @@ static void InvokeWithArgArray(const ScopedObjectAccessAlreadyRunnable& soa, const char* shorty) REQUIRES_SHARED(Locks::mutator_lock_) { uint32_t* args = arg_array->GetArray(); - if (UNLIKELY(soa.Env()->check_jni)) { + if (UNLIKELY(soa.Env()->IsCheckJniEnabled())) { CheckMethodArguments(soa.Vm(), method->GetInterfaceMethodIfProxy(kRuntimePointerSize), args); } method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty); @@ -927,14 +927,14 @@ void UpdateReference(Thread* self, jobject obj, ObjPtr result) { IndirectRef ref = reinterpret_cast(obj); IndirectRefKind kind = IndirectReferenceTable::GetIndirectRefKind(ref); if (kind == kLocal) { - self->GetJniEnv()->locals.Update(obj, result); + self->GetJniEnv()->UpdateLocal(obj, result); } else if (kind == kHandleScopeOrInvalid) { LOG(FATAL) << "Unsupported UpdateReference for kind kHandleScopeOrInvalid"; } else if (kind == kGlobal) { - self->GetJniEnv()->vm->UpdateGlobal(self, ref, result); + self->GetJniEnv()->GetVm()->UpdateGlobal(self, ref, result); } else { DCHECK_EQ(kind, kWeakGlobal); - self->GetJniEnv()->vm->UpdateWeakGlobal(self, ref, result); + self->GetJniEnv()->GetVm()->UpdateWeakGlobal(self, ref, result); } } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index a172392c51..54aa9e57e6 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -807,7 +807,7 @@ bool Runtime::Start() { { ScopedObjectAccess soa(self); - self->GetJniEnv()->locals.AssertEmpty(); + self->GetJniEnv()->AssertLocalsEmpty(); } VLOG(startup) << "Runtime::Start exiting"; diff --git a/runtime/scoped_thread_state_change-inl.h b/runtime/scoped_thread_state_change-inl.h index a9702a70bc..f95593209b 100644 --- a/runtime/scoped_thread_state_change-inl.h +++ b/runtime/scoped_thread_state_change-inl.h @@ -97,12 +97,12 @@ inline bool ScopedObjectAccessAlreadyRunnable::IsRunnable() const { } inline ScopedObjectAccessAlreadyRunnable::ScopedObjectAccessAlreadyRunnable(JNIEnv* env) - : self_(ThreadForEnv(env)), env_(down_cast(env)), vm_(env_->vm) {} + : self_(ThreadForEnv(env)), env_(down_cast(env)), vm_(env_->GetVm()) {} inline ScopedObjectAccessAlreadyRunnable::ScopedObjectAccessAlreadyRunnable(Thread* self) : self_(self), env_(down_cast(self->GetJniEnv())), - vm_(env_ != nullptr ? env_->vm : nullptr) {} + vm_(env_ != nullptr ? env_->GetVm() : nullptr) {} inline ScopedObjectAccessUnchecked::ScopedObjectAccessUnchecked(JNIEnv* env) : ScopedObjectAccessAlreadyRunnable(env), tsc_(Self(), kRunnable) { diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h index 02b6124118..0c42c5ae8d 100644 --- a/runtime/scoped_thread_state_change.h +++ b/runtime/scoped_thread_state_change.h @@ -27,7 +27,7 @@ namespace art { class JavaVMExt; -struct JNIEnvExt; +class JNIEnvExt; template class ObjPtr; class Thread; diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index 62b0789a43..7392dcf6a5 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -34,7 +34,7 @@ namespace art { // Quickly access the current thread from a JNIEnv. static inline Thread* ThreadForEnv(JNIEnv* env) { JNIEnvExt* full_env(down_cast(env)); - return full_env->self; + return full_env->GetSelf(); } inline void Thread::AllowThreadSuspension() { diff --git a/runtime/thread.cc b/runtime/thread.cc index b86e56b531..5777d141fa 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -621,7 +621,7 @@ void Thread::InstallImplicitProtection() { void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) { CHECK(java_peer != nullptr); - Thread* self = static_cast(env)->self; + Thread* self = static_cast(env)->GetSelf(); if (VLOG_IS_ON(threads)) { ScopedObjectAccess soa(env); @@ -754,8 +754,8 @@ bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm, JNIEnvExt* jni_en tls32_.thin_lock_thread_id = thread_list->AllocThreadId(this); if (jni_env_ext != nullptr) { - DCHECK_EQ(jni_env_ext->vm, java_vm); - DCHECK_EQ(jni_env_ext->self, this); + DCHECK_EQ(jni_env_ext->GetVm(), java_vm); + DCHECK_EQ(jni_env_ext->GetSelf(), this); tlsPtr_.jni_env = jni_env_ext; } else { std::string error_msg; @@ -853,7 +853,7 @@ Thread* Thread::Attach(const char* thread_name, if (thread_name != nullptr) { self->tlsPtr_.name->assign(thread_name); ::art::SetThreadName(thread_name); - } else if (self->GetJniEnv()->check_jni) { + } else if (self->GetJniEnv()->IsCheckJniEnabled()) { LOG(WARNING) << *Thread::Current() << " attached without supplying a name"; } } @@ -2170,7 +2170,7 @@ void Thread::Destroy() { ScopedObjectAccess soa(self); MonitorExitVisitor visitor(self); // On thread detach, all monitors entered with JNI MonitorEnter are automatically exited. - tlsPtr_.jni_env->monitors.VisitRoots(&visitor, RootInfo(kRootVMInternal)); + tlsPtr_.jni_env->monitors_.VisitRoots(&visitor, RootInfo(kRootVMInternal)); } // Release locally held global references which releasing may require the mutator lock. if (tlsPtr_.jpeer != nullptr) { @@ -2339,7 +2339,7 @@ ObjPtr Thread::DecodeJObject(jobject obj) const { bool expect_null = false; // The "kinds" below are sorted by the frequency we expect to encounter them. if (kind == kLocal) { - IndirectReferenceTable& locals = tlsPtr_.jni_env->locals; + IndirectReferenceTable& locals = tlsPtr_.jni_env->locals_; // Local references do not need a read barrier. result = locals.Get(ref); } else if (kind == kHandleScopeOrInvalid) { @@ -2350,15 +2350,15 @@ ObjPtr Thread::DecodeJObject(jobject obj) const { result = reinterpret_cast*>(obj)->AsMirrorPtr(); VerifyObject(result); } else { - tlsPtr_.jni_env->vm->JniAbortF(nullptr, "use of invalid jobject %p", obj); + tlsPtr_.jni_env->vm_->JniAbortF(nullptr, "use of invalid jobject %p", obj); expect_null = true; result = nullptr; } } else if (kind == kGlobal) { - result = tlsPtr_.jni_env->vm->DecodeGlobal(ref); + result = tlsPtr_.jni_env->vm_->DecodeGlobal(ref); } else { DCHECK_EQ(kind, kWeakGlobal); - result = tlsPtr_.jni_env->vm->DecodeWeakGlobal(const_cast(this), ref); + result = tlsPtr_.jni_env->vm_->DecodeWeakGlobal(const_cast(this), ref); if (Runtime::Current()->IsClearedJniWeakGlobal(result)) { // This is a special case where it's okay to return null. expect_null = true; @@ -2367,7 +2367,7 @@ ObjPtr Thread::DecodeJObject(jobject obj) const { } if (UNLIKELY(!expect_null && result == nullptr)) { - tlsPtr_.jni_env->vm->JniAbortF(nullptr, "use of deleted %s %p", + tlsPtr_.jni_env->vm_->JniAbortF(nullptr, "use of deleted %s %p", ToStr(kind).c_str(), obj); } return result; @@ -2378,7 +2378,7 @@ bool Thread::IsJWeakCleared(jweak obj) const { IndirectRef ref = reinterpret_cast(obj); IndirectRefKind kind = IndirectReferenceTable::GetIndirectRefKind(ref); CHECK_EQ(kind, kWeakGlobal); - return tlsPtr_.jni_env->vm->IsWeakGlobalCleared(const_cast(this), ref); + return tlsPtr_.jni_env->vm_->IsWeakGlobalCleared(const_cast(this), ref); } // Implements java.lang.Thread.interrupted. @@ -3516,8 +3516,8 @@ void Thread::VisitRoots(RootVisitor* visitor) { RootInfo(kRootNativeStack, thread_id)); } visitor->VisitRootIfNonNull(&tlsPtr_.monitor_enter_object, RootInfo(kRootNativeStack, thread_id)); - tlsPtr_.jni_env->locals.VisitRoots(visitor, RootInfo(kRootJNILocal, thread_id)); - tlsPtr_.jni_env->monitors.VisitRoots(visitor, RootInfo(kRootJNIMonitor, thread_id)); + tlsPtr_.jni_env->VisitJniLocalRoots(visitor, RootInfo(kRootJNILocal, thread_id)); + tlsPtr_.jni_env->VisitMonitorRoots(visitor, RootInfo(kRootJNIMonitor, thread_id)); HandleScopeVisitRoots(visitor, thread_id); if (tlsPtr_.debug_invoke_req != nullptr) { tlsPtr_.debug_invoke_req->VisitRoots(visitor, RootInfo(kRootDebugger, thread_id)); diff --git a/runtime/thread.h b/runtime/thread.h index 0803975d26..0f1d7a4a8a 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -86,7 +86,7 @@ class DeoptimizationContextRecord; class DexFile; class FrameIdToShadowFrame; class JavaVMExt; -struct JNIEnvExt; +class JNIEnvExt; class Monitor; class RootVisitor; class ScopedObjectAccessAlreadyRunnable; diff --git a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc index 7d40f5773d..f01b82553d 100644 --- a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc +++ b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc @@ -58,7 +58,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_destroyJavaVMAndExit(JNIEnv* env, jc Thread* const self = Thread::Current(); self->SetTopOfStack(nullptr); self->SetTopOfShadowStack(nullptr); - JavaVM* vm = down_cast(env)->vm; + JavaVM* vm = down_cast(env)->GetVm(); vm->DetachCurrentThread(); // Open ourself again to make sure the native library does not get unloaded from // underneath us due to DestroyJavaVM. b/28406866 -- GitLab From 0f54b0d5322b9227f15e23bdd4109979aa9555eb Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 27 Dec 2017 08:37:02 -0800 Subject: [PATCH 224/226] Revert "ART: Add barrier to dex2oat watchdog startup" This reverts commit 85baf7a5a681c73825e0e0909d3757906b4e772b. The change was ineffective. Bug: 63052624 Test: m test-art-host Change-Id: Id6805e0315feebd2f1afbf41d301074e5518e938 --- dex2oat/dex2oat.cc | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index f5d09dea45..11c903145f 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -41,7 +41,6 @@ #include "arch/instruction_set_features.h" #include "arch/mips/instruction_set_features_mips.h" #include "art_method-inl.h" -#include "barrier.h" #include "base/callee_save_type.h" #include "base/dumpable.h" #include "base/file_utils.h" @@ -481,8 +480,7 @@ class WatchDog { public: explicit WatchDog(int64_t timeout_in_milliseconds) - : wait_barrier_(2), - timeout_in_milliseconds_(timeout_in_milliseconds), + : timeout_in_milliseconds_(timeout_in_milliseconds), shutting_down_(false) { const char* reason = "dex2oat watch dog thread startup"; CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_init, (&mutex_, nullptr), reason); @@ -527,14 +525,9 @@ class WatchDog { static constexpr int64_t kDefaultWatchdogTimeoutInMS = kWatchdogVerifyMultiplier * kWatchDogTimeoutSeconds * 1000; - void WaitForCallBackStart(Thread* self) { - wait_barrier_.Wait(self); - } - private: static void* CallBack(void* arg) { WatchDog* self = reinterpret_cast(arg); - self->wait_barrier_.Pass(nullptr); ::art::SetThreadName("dex2oat watch dog"); self->Wait(); return nullptr; @@ -589,8 +582,6 @@ class WatchDog { pthread_attr_t attr_; pthread_t pthread_; - Barrier wait_barrier_; - const int64_t timeout_in_milliseconds_; bool shutting_down_; }; @@ -935,8 +926,6 @@ class Dex2Oat FINAL { ? parser_options->watch_dog_timeout_in_ms : WatchDog::kDefaultWatchdogTimeoutInMS; watchdog_.reset(new WatchDog(timeout)); - watchdog_->WaitForCallBackStart(nullptr); // The runtime hasn't been started, yet. So - // nullptr for current thread. } // Fill some values into the key-value store for the oat header. -- GitLab From 21f9cb88932fc321427f41256b154dc626233a6f Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 27 Dec 2017 08:43:08 -0800 Subject: [PATCH 225/226] ART: Disable watchdog test for non-CC Disable the watchdog trigger test. It is not clear why CMS does not reliably trigger the watchdog. Bug: 63052624 Test: m test-art-host Change-Id: I4f6de0af4f08317f75069dcaf83702f2d8b5c1c8 --- dex2oat/dex2oat_test.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index e9610fd4f6..c8a70c0161 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -995,6 +995,7 @@ TEST_F(Dex2oatWatchdogTest, TestWatchdogOK) { TEST_F(Dex2oatWatchdogTest, TestWatchdogTrigger) { TEST_DISABLED_FOR_MEMORY_TOOL_VALGRIND(); // b/63052624 + TEST_DISABLED_WITHOUT_BAKER_READ_BARRIERS(); // b/63052624 // Check with ten milliseconds. RunTest(false, { "--watchdog-timeout=10" }); } -- GitLab From 94c589db78da2b66bc681c6480819ca4bd4d3326 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 27 Dec 2017 12:43:01 -0800 Subject: [PATCH 226/226] ART: Mark Dbg GCs as debugger Change Heap::CollectGarbage to accept explicit GcCause, but implicitly default to kGcCauseExplicit. Change Dbg functions that run an explicit GC to set the cause to kGcCauseDebugger. Test: m test-art-host Change-Id: I53d4073fca01c1de78d14a58dff33004c7971981 --- runtime/debugger.cc | 7 ++++--- runtime/gc/heap.cc | 4 ++-- runtime/gc/heap.h | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 2be00f5f5b..f504d86f76 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -40,6 +40,7 @@ #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/card_table-inl.h" #include "gc/allocation_record.h" +#include "gc/gc_cause.h" #include "gc/scoped_gc_critical_section.h" #include "gc/space/bump_pointer_space-walk-inl.h" #include "gc/space/large_object_space.h" @@ -947,7 +948,7 @@ JDWP::JdwpError Dbg::GetContendedMonitor(JDWP::ObjectId thread_id, JDWP::JdwpError Dbg::GetInstanceCounts(const std::vector& class_ids, std::vector* counts) { gc::Heap* heap = Runtime::Current()->GetHeap(); - heap->CollectGarbage(false); + heap->CollectGarbage(false, gc::GcCause::kGcCauseDebugger); VariableSizedHandleScope hs(Thread::Current()); std::vector> classes; counts->clear(); @@ -968,7 +969,7 @@ JDWP::JdwpError Dbg::GetInstances(JDWP::RefTypeId class_id, int32_t max_count, std::vector* instances) { gc::Heap* heap = Runtime::Current()->GetHeap(); // We only want reachable instances, so do a GC. - heap->CollectGarbage(false); + heap->CollectGarbage(false, gc::GcCause::kGcCauseDebugger); JDWP::JdwpError error; ObjPtr c = DecodeClass(class_id, &error); if (c == nullptr) { @@ -990,7 +991,7 @@ JDWP::JdwpError Dbg::GetInstances(JDWP::RefTypeId class_id, int32_t max_count, JDWP::JdwpError Dbg::GetReferringObjects(JDWP::ObjectId object_id, int32_t max_count, std::vector* referring_objects) { gc::Heap* heap = Runtime::Current()->GetHeap(); - heap->CollectGarbage(false); + heap->CollectGarbage(false, gc::GcCause::kGcCauseDebugger); JDWP::JdwpError error; ObjPtr o = gRegistry->Get(object_id, &error); if (o == nullptr) { diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 3ba12ca493..dbaddaf8d4 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -1883,10 +1883,10 @@ void Heap::GetReferringObjects(VariableSizedHandleScope& scope, VisitObjects(referring_objects_finder); } -void Heap::CollectGarbage(bool clear_soft_references) { +void Heap::CollectGarbage(bool clear_soft_references, GcCause cause) { // Even if we waited for a GC we still need to do another GC since weaks allocated during the // last GC will not have necessarily been cleared. - CollectGarbageInternal(gc_plan_.back(), kGcCauseExplicit, clear_soft_references); + CollectGarbageInternal(gc_plan_.back(), cause, clear_soft_references); } bool Heap::SupportHomogeneousSpaceCompactAndCollectorTransitions() const { diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 0d11658e01..7dcf82f415 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -333,7 +333,7 @@ class Heap { REQUIRES_SHARED(Locks::mutator_lock_); // Initiates an explicit garbage collection. - void CollectGarbage(bool clear_soft_references) + void CollectGarbage(bool clear_soft_references, GcCause cause = kGcCauseExplicit) REQUIRES(!*gc_complete_lock_, !*pending_task_lock_); // Does a concurrent GC, should only be called by the GC daemon thread -- GitLab