diff options
43 files changed, 1752 insertions, 176 deletions
diff --git a/Android.mk b/Android.mk index 216e86585f..93603556bc 100644 --- a/Android.mk +++ b/Android.mk @@ -42,27 +42,7 @@ clean-oat: clean-oat-host clean-oat-target .PHONY: clean-oat-host clean-oat-host: - rm -f $(HOST_CORE_IMG_OUTS) - rm -f $(HOST_CORE_OAT_OUTS) - rm -f $(HOST_OUT_JAVA_LIBRARIES)/$(ART_HOST_ARCH)/*.odex -ifneq ($(HOST_PREFER_32_BIT),true) - rm -f $(HOST_OUT_JAVA_LIBRARIES)/$(2ND_ART_HOST_ARCH)/*.odex -endif - rm -f $(TARGET_CORE_IMG_OUTS) - rm -f $(TARGET_CORE_OAT_OUTS) - rm -rf $(DEXPREOPT_PRODUCT_DIR_FULL_PATH) - rm -f $(TARGET_OUT_UNSTRIPPED)/system/framework/*.odex - rm -f $(TARGET_OUT_UNSTRIPPED)/system/framework/*/*.oat - rm -f $(TARGET_OUT_UNSTRIPPED)/system/framework/*/*.art - rm -f $(TARGET_OUT)/framework/*/*.oat - rm -f $(TARGET_OUT)/framework/*/*.art - rm -f $(TARGET_OUT_APPS)/*.odex - rm -f $(TARGET_OUT_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/javalib.odex - rm -f $(TARGET_OUT_INTERMEDIATES)/APPS/*_intermediates/*.odex -ifdef TARGET_2ND_ARCH - rm -f $(2ND_TARGET_OUT_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/javalib.odex - rm -f $(2ND_TARGET_OUT_INTERMEDIATES)/APPS/*_intermediates/*.odex -endif + find $(OUT_DIR) -name "*.oat" -o -name "*.odex" -o -name "*.art" | xargs rm -f ifneq ($(TMPDIR),) rm -rf $(TMPDIR)/$(USER)/test-*/dalvik-cache/* rm -rf $(TMPDIR)/android-data/dalvik-cache/* diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index c5669c05e2..1a4c30c20d 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -188,6 +188,7 @@ COMPILER_GTEST_COMMON_SRC_FILES := \ compiler/dex/local_value_numbering_test.cc \ compiler/dex/mir_graph_test.cc \ compiler/dex/mir_optimization_test.cc \ + compiler/dwarf/dwarf_test.cc \ compiler/driver/compiler_driver_test.cc \ compiler/elf_writer_test.cc \ compiler/image_test.cc \ diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index 257406a622..1d0aad5425 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -180,7 +180,6 @@ void CommonCompilerTest::SetUpRuntimeOptions(RuntimeOptions* options) { callbacks_.reset(new QuickCompilerCallbacks(verification_results_.get(), method_inliner_map_.get(), CompilerCallbacks::CallbackMode::kCompileApp)); - options->push_back(std::make_pair("compilercallbacks", callbacks_.get())); } void CommonCompilerTest::TearDown() { diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index 9cffbc86f3..d7b210d571 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -78,7 +78,6 @@ class CommonCompilerTest : public CommonRuntimeTest { std::unique_ptr<CompilerOptions> compiler_options_; std::unique_ptr<VerificationResults> verification_results_; std::unique_ptr<DexFileToMethodInlinerMap> method_inliner_map_; - std::unique_ptr<CompilerCallbacks> callbacks_; std::unique_ptr<CompilerDriver> compiler_driver_; std::unique_ptr<CumulativeLogger> timer_; std::unique_ptr<const InstructionSetFeatures> instruction_set_features_; diff --git a/compiler/dwarf/debug_frame_opcode_writer.h b/compiler/dwarf/debug_frame_opcode_writer.h new file mode 100644 index 0000000000..cc4ef8fde1 --- /dev/null +++ b/compiler/dwarf/debug_frame_opcode_writer.h @@ -0,0 +1,282 @@ +/* + * 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. + */ + +#ifndef ART_COMPILER_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_ +#define ART_COMPILER_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_ + +#include "dwarf.h" +#include "register.h" +#include "writer.h" + +namespace art { +namespace dwarf { + +// Writer for .debug_frame opcodes (DWARF-3). +// See the DWARF specification for the precise meaning of the opcodes. +// The writer is very light-weight, however it will do the following for you: +// * Choose the most compact encoding of a given opcode. +// * Keep track of current state and convert absolute values to deltas. +// * Divide by header-defined factors as appropriate. +template<typename Allocator = std::allocator<uint8_t> > +class DebugFrameOpCodeWriter : private Writer<Allocator> { + public: + // To save space, DWARF divides most offsets by header-defined factors. + // They are used in integer divisions, so we make them constants. + // We usually subtract from stack base pointer, so making the factor + // negative makes the encoded values positive and thus easier to encode. + static constexpr int kDataAlignmentFactor = -4; + static constexpr int kCodeAlignmentFactor = 1; + + // Explicitely advance the program counter to given location. + void AdvancePC(int absolute_pc) { + DCHECK_GE(absolute_pc, current_pc_); + int delta = FactorCodeOffset(absolute_pc - current_pc_); + if (delta != 0) { + if (delta <= 0x3F) { + this->PushUint8(DW_CFA_advance_loc | delta); + } else if (delta <= UINT8_MAX) { + this->PushUint8(DW_CFA_advance_loc1); + this->PushUint8(delta); + } else if (delta <= UINT16_MAX) { + this->PushUint8(DW_CFA_advance_loc2); + this->PushUint16(delta); + } else { + this->PushUint8(DW_CFA_advance_loc4); + this->PushUint32(delta); + } + } + current_pc_ = absolute_pc; + } + + // Override this method to automatically advance the PC before each opcode. + virtual void ImplicitlyAdvancePC() { } + + // Common alias in assemblers - spill relative to current stack pointer. + void RelOffset(Reg reg, int offset) { + Offset(reg, offset - current_cfa_offset_); + } + + // Common alias in assemblers - increase stack frame size. + void AdjustCFAOffset(int delta) { + DefCFAOffset(current_cfa_offset_ + delta); + } + + // Custom alias - spill many registers based on bitmask. + void RelOffsetForMany(Reg reg_base, int offset, uint32_t reg_mask, + int reg_size) { + DCHECK(reg_size == 4 || reg_size == 8); + for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) { + if ((reg_mask & 1) != 0u) { + RelOffset(Reg(reg_base.num() + i), offset); + offset += reg_size; + } + } + } + + // Custom alias - unspill many registers based on bitmask. + void RestoreMany(Reg reg_base, uint32_t reg_mask) { + for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) { + if ((reg_mask & 1) != 0u) { + Restore(Reg(reg_base.num() + i)); + } + } + } + + void Nop() { + this->PushUint8(DW_CFA_nop); + } + + void Offset(Reg reg, int offset) { + ImplicitlyAdvancePC(); + int factored_offset = FactorDataOffset(offset); // May change sign. + if (factored_offset >= 0) { + if (0 <= reg.num() && reg.num() <= 0x3F) { + this->PushUint8(DW_CFA_offset | reg.num()); + this->PushUleb128(factored_offset); + } else { + this->PushUint8(DW_CFA_offset_extended); + this->PushUleb128(reg.num()); + this->PushUleb128(factored_offset); + } + } else { + uses_dwarf3_features_ = true; + this->PushUint8(DW_CFA_offset_extended_sf); + this->PushUleb128(reg.num()); + this->PushSleb128(factored_offset); + } + } + + void Restore(Reg reg) { + ImplicitlyAdvancePC(); + if (0 <= reg.num() && reg.num() <= 0x3F) { + this->PushUint8(DW_CFA_restore | reg.num()); + } else { + this->PushUint8(DW_CFA_restore_extended); + this->PushUleb128(reg.num()); + } + } + + void Undefined(Reg reg) { + ImplicitlyAdvancePC(); + this->PushUint8(DW_CFA_undefined); + this->PushUleb128(reg.num()); + } + + void SameValue(Reg reg) { + ImplicitlyAdvancePC(); + this->PushUint8(DW_CFA_same_value); + this->PushUleb128(reg.num()); + } + + // The previous value of "reg" is stored in register "new_reg". + void Register(Reg reg, Reg new_reg) { + ImplicitlyAdvancePC(); + this->PushUint8(DW_CFA_register); + this->PushUleb128(reg.num()); + this->PushUleb128(new_reg.num()); + } + + void RememberState() { + // Note that we do not need to advance the PC. + this->PushUint8(DW_CFA_remember_state); + } + + void RestoreState() { + ImplicitlyAdvancePC(); + this->PushUint8(DW_CFA_restore_state); + } + + void DefCFA(Reg reg, int offset) { + ImplicitlyAdvancePC(); + if (offset >= 0) { + this->PushUint8(DW_CFA_def_cfa); + this->PushUleb128(reg.num()); + this->PushUleb128(offset); // Non-factored. + } else { + uses_dwarf3_features_ = true; + this->PushUint8(DW_CFA_def_cfa_sf); + this->PushUleb128(reg.num()); + this->PushSleb128(FactorDataOffset(offset)); + } + current_cfa_offset_ = offset; + } + + void DefCFARegister(Reg reg) { + ImplicitlyAdvancePC(); + this->PushUint8(DW_CFA_def_cfa_register); + this->PushUleb128(reg.num()); + } + + void DefCFAOffset(int offset) { + if (current_cfa_offset_ != offset) { + ImplicitlyAdvancePC(); + if (offset >= 0) { + this->PushUint8(DW_CFA_def_cfa_offset); + this->PushUleb128(offset); // Non-factored. + } else { + uses_dwarf3_features_ = true; + this->PushUint8(DW_CFA_def_cfa_offset_sf); + this->PushSleb128(FactorDataOffset(offset)); + } + current_cfa_offset_ = offset; + } + } + + void ValOffset(Reg reg, int offset) { + ImplicitlyAdvancePC(); + uses_dwarf3_features_ = true; + int factored_offset = FactorDataOffset(offset); // May change sign. + if (factored_offset >= 0) { + this->PushUint8(DW_CFA_val_offset); + this->PushUleb128(reg.num()); + this->PushUleb128(factored_offset); + } else { + this->PushUint8(DW_CFA_val_offset_sf); + this->PushUleb128(reg.num()); + this->PushSleb128(factored_offset); + } + } + + void DefCFAExpression(void* expr, int expr_size) { + ImplicitlyAdvancePC(); + uses_dwarf3_features_ = true; + this->PushUint8(DW_CFA_def_cfa_expression); + this->PushUleb128(expr_size); + this->PushData(expr, expr_size); + } + + void Expression(Reg reg, void* expr, int expr_size) { + ImplicitlyAdvancePC(); + uses_dwarf3_features_ = true; + this->PushUint8(DW_CFA_expression); + this->PushUleb128(reg.num()); + this->PushUleb128(expr_size); + this->PushData(expr, expr_size); + } + + void ValExpression(Reg reg, void* expr, int expr_size) { + ImplicitlyAdvancePC(); + uses_dwarf3_features_ = true; + this->PushUint8(DW_CFA_val_expression); + this->PushUleb128(reg.num()); + this->PushUleb128(expr_size); + this->PushData(expr, expr_size); + } + + int GetCurrentCFAOffset() const { + return current_cfa_offset_; + } + + void SetCurrentCFAOffset(int offset) { + current_cfa_offset_ = offset; + } + + using Writer<Allocator>::data; + + DebugFrameOpCodeWriter(const Allocator& alloc = Allocator()) + : Writer<Allocator>(&opcodes_), + opcodes_(alloc), + current_cfa_offset_(0), + current_pc_(0), + uses_dwarf3_features_(false) { + } + + virtual ~DebugFrameOpCodeWriter() { } + + protected: + int FactorDataOffset(int offset) const { + DCHECK_EQ(offset % kDataAlignmentFactor, 0); + return offset / kDataAlignmentFactor; + } + + int FactorCodeOffset(int offset) const { + DCHECK_EQ(offset % kCodeAlignmentFactor, 0); + return offset / kCodeAlignmentFactor; + } + + std::vector<uint8_t, Allocator> opcodes_; + int current_cfa_offset_; + int current_pc_; + bool uses_dwarf3_features_; + + private: + DISALLOW_COPY_AND_ASSIGN(DebugFrameOpCodeWriter); +}; + +} // namespace dwarf +} // namespace art + +#endif // ART_COMPILER_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_ diff --git a/compiler/dwarf/debug_frame_writer.h b/compiler/dwarf/debug_frame_writer.h new file mode 100644 index 0000000000..6de45f5526 --- /dev/null +++ b/compiler/dwarf/debug_frame_writer.h @@ -0,0 +1,96 @@ +/* + * 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. + */ + +#ifndef ART_COMPILER_DWARF_DEBUG_FRAME_WRITER_H_ +#define ART_COMPILER_DWARF_DEBUG_FRAME_WRITER_H_ + +#include "debug_frame_opcode_writer.h" +#include "dwarf.h" +#include "writer.h" + +namespace art { +namespace dwarf { + +// Writer for the .eh_frame section (which extends .debug_frame specification). +template<typename Allocator = std::allocator<uint8_t>> +class DebugFrameWriter FINAL : private Writer<Allocator> { + public: + void WriteCIE(Reg return_address_register, + const uint8_t* initial_opcodes, + int initial_opcodes_size) { + DCHECK(cie_header_start_ == ~0u); + cie_header_start_ = this->data()->size(); + this->PushUint32(0); // Length placeholder. + this->PushUint32(0); // CIE id. + this->PushUint8(1); // Version. + this->PushString("zR"); + this->PushUleb128(DebugFrameOpCodeWriter<Allocator>::kCodeAlignmentFactor); + this->PushSleb128(DebugFrameOpCodeWriter<Allocator>::kDataAlignmentFactor); + this->PushUleb128(return_address_register.num()); // ubyte in DWARF2. + this->PushUleb128(1); // z: Augmentation data size. + if (use_64bit_address_) { + this->PushUint8(0x04); // R: ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata8). + } else { + this->PushUint8(0x03); // R: ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata4). + } + this->PushData(initial_opcodes, initial_opcodes_size); + this->Pad(use_64bit_address_ ? 8 : 4); + this->UpdateUint32(cie_header_start_, this->data()->size() - cie_header_start_ - 4); + } + + void WriteCIE(Reg return_address_register, + const DebugFrameOpCodeWriter<Allocator>& opcodes) { + WriteCIE(return_address_register, opcodes.data()->data(), opcodes.data()->size()); + } + + void WriteFDE(uint64_t initial_address, + uint64_t address_range, + const uint8_t* unwind_opcodes, + int unwind_opcodes_size) { + DCHECK(cie_header_start_ != ~0u); + size_t fde_header_start = this->data()->size(); + this->PushUint32(0); // Length placeholder. + this->PushUint32(this->data()->size() - cie_header_start_); // 'CIE_pointer' + if (use_64bit_address_) { + this->PushUint64(initial_address); + this->PushUint64(address_range); + } else { + this->PushUint32(initial_address); + this->PushUint32(address_range); + } + this->PushUleb128(0); // Augmentation data size. + this->PushData(unwind_opcodes, unwind_opcodes_size); + this->Pad(use_64bit_address_ ? 8 : 4); + this->UpdateUint32(fde_header_start, this->data()->size() - fde_header_start - 4); + } + + DebugFrameWriter(std::vector<uint8_t, Allocator>* buffer, bool use_64bit_address) + : Writer<Allocator>(buffer), + use_64bit_address_(use_64bit_address), + cie_header_start_(~0u) { + } + + private: + bool use_64bit_address_; + size_t cie_header_start_; + + DISALLOW_COPY_AND_ASSIGN(DebugFrameWriter); +}; + +} // namespace dwarf +} // namespace art + +#endif // ART_COMPILER_DWARF_DEBUG_FRAME_WRITER_H_ diff --git a/compiler/dwarf/debug_line_opcode_writer.h b/compiler/dwarf/debug_line_opcode_writer.h new file mode 100644 index 0000000000..f34acee647 --- /dev/null +++ b/compiler/dwarf/debug_line_opcode_writer.h @@ -0,0 +1,243 @@ +/* + * 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. + */ + +#ifndef ART_COMPILER_DWARF_DEBUG_LINE_OPCODE_WRITER_H_ +#define ART_COMPILER_DWARF_DEBUG_LINE_OPCODE_WRITER_H_ + +#include "dwarf.h" +#include "writer.h" + +namespace art { +namespace dwarf { + +// Writer for the .debug_line opcodes (DWARF-3). +// The writer is very light-weight, however it will do the following for you: +// * Choose the most compact encoding of a given opcode. +// * Keep track of current state and convert absolute values to deltas. +// * Divide by header-defined factors as appropriate. +template<typename Allocator = std::allocator<uint8_t>> +class DebugLineOpCodeWriter FINAL : private Writer<Allocator> { + public: + static constexpr int kOpcodeBase = 13; + static constexpr bool kDefaultIsStmt = true; + static constexpr int kLineBase = -5; + static constexpr int kLineRange = 14; + + void AddRow() { + this->PushUint8(DW_LNS_copy); + } + + void AdvancePC(uint64_t absolute_address) { + DCHECK_NE(current_address_, 0u); // Use SetAddress for the first advance. + DCHECK_GE(absolute_address, current_address_); + if (absolute_address != current_address_) { + uint64_t delta = FactorCodeOffset(absolute_address - current_address_); + if (delta <= INT32_MAX) { + this->PushUint8(DW_LNS_advance_pc); + this->PushUleb128(static_cast<int>(delta)); + current_address_ = absolute_address; + } else { + SetAddress(absolute_address); + } + } + } + + void AdvanceLine(int absolute_line) { + int delta = absolute_line - current_line_; + if (delta != 0) { + this->PushUint8(DW_LNS_advance_line); + this->PushSleb128(delta); + current_line_ = absolute_line; + } + } + + void SetFile(int file) { + if (current_file_ != file) { + this->PushUint8(DW_LNS_set_file); + this->PushUleb128(file); + current_file_ = file; + } + } + + void SetColumn(int column) { + this->PushUint8(DW_LNS_set_column); + this->PushUleb128(column); + } + + void NegateStmt() { + this->PushUint8(DW_LNS_negate_stmt); + } + + void SetBasicBlock() { + this->PushUint8(DW_LNS_set_basic_block); + } + + void SetPrologueEnd() { + uses_dwarf3_features_ = true; + this->PushUint8(DW_LNS_set_prologue_end); + } + + void SetEpilogueBegin() { + uses_dwarf3_features_ = true; + this->PushUint8(DW_LNS_set_epilogue_begin); + } + + void SetISA(int isa) { + uses_dwarf3_features_ = true; + this->PushUint8(DW_LNS_set_isa); + this->PushUleb128(isa); + } + + void EndSequence() { + this->PushUint8(0); + this->PushUleb128(1); + this->PushUint8(DW_LNE_end_sequence); + current_address_ = 0; + current_file_ = 1; + current_line_ = 1; + } + + // Uncoditionally set address using the long encoding. + // This gives the linker opportunity to relocate the address. + void SetAddress(uint64_t absolute_address) { + DCHECK_GE(absolute_address, current_address_); + FactorCodeOffset(absolute_address); // Check if it is factorable. + this->PushUint8(0); + if (use_64bit_address_) { + this->PushUleb128(1 + 8); + this->PushUint8(DW_LNE_set_address); + this->PushUint64(absolute_address); + } else { + this->PushUleb128(1 + 4); + this->PushUint8(DW_LNE_set_address); + this->PushUint32(absolute_address); + } + current_address_ = absolute_address; + } + + void DefineFile(const char* filename, + int directory_index, + int modification_time, + int file_size) { + int size = 1 + + strlen(filename) + 1 + + UnsignedLeb128Size(directory_index) + + UnsignedLeb128Size(modification_time) + + UnsignedLeb128Size(file_size); + this->PushUint8(0); + this->PushUleb128(size); + size_t start = data()->size(); + this->PushUint8(DW_LNE_define_file); + this->PushString(filename); + this->PushUleb128(directory_index); + this->PushUleb128(modification_time); + this->PushUleb128(file_size); + DCHECK_EQ(start + size, data()->size()); + } + + // Compact address and line opcode. + void AddRow(uint64_t absolute_address, int absolute_line) { + DCHECK_GE(absolute_address, current_address_); + + // If the address is definitely too far, use the long encoding. + uint64_t delta_address = FactorCodeOffset(absolute_address - current_address_); + if (delta_address > UINT8_MAX) { + AdvancePC(absolute_address); + delta_address = 0; + } + + // If the line is definitely too far, use the long encoding. + int delta_line = absolute_line - current_line_; + if (!(kLineBase <= delta_line && delta_line < kLineBase + kLineRange)) { + AdvanceLine(absolute_line); + delta_line = 0; + } + + // Both address and line should be reasonable now. Use the short encoding. + int opcode = kOpcodeBase + (delta_line - kLineBase) + + (static_cast<int>(delta_address) * kLineRange); + if (opcode > UINT8_MAX) { + // If the address is still too far, try to increment it by const amount. + int const_advance = (0xFF - kOpcodeBase) / kLineRange; + opcode -= (kLineRange * const_advance); + if (opcode <= UINT8_MAX) { + this->PushUint8(DW_LNS_const_add_pc); + } else { + // Give up and use long encoding for address. + AdvancePC(absolute_address); + // Still use the opcode to do line advance and copy. + opcode = kOpcodeBase + (delta_line - kLineBase); + } + } + DCHECK(kOpcodeBase <= opcode && opcode <= 0xFF); + this->PushUint8(opcode); // Special opcode. + current_line_ = absolute_line; + current_address_ = absolute_address; + } + + int GetCodeFactorBits() const { + return code_factor_bits_; + } + + uint64_t CurrentAddress() const { + return current_address_; + } + + int CurrentFile() const { + return current_file_; + } + + int CurrentLine() const { + return current_line_; + } + + using Writer<Allocator>::data; + + DebugLineOpCodeWriter(bool use64bitAddress, + int codeFactorBits, + const Allocator& alloc = Allocator()) + : Writer<Allocator>(&opcodes_), + opcodes_(alloc), + uses_dwarf3_features_(false), + use_64bit_address_(use64bitAddress), + code_factor_bits_(codeFactorBits), + current_address_(0), + current_file_(1), + current_line_(1) { + } + + private: + uint64_t FactorCodeOffset(uint64_t offset) const { + DCHECK_GE(code_factor_bits_, 0); + DCHECK_EQ((offset >> code_factor_bits_) << code_factor_bits_, offset); + return offset >> code_factor_bits_; + } + + std::vector<uint8_t, Allocator> opcodes_; + bool uses_dwarf3_features_; + bool use_64bit_address_; + int code_factor_bits_; + uint64_t current_address_; + int current_file_; + int current_line_; + + DISALLOW_COPY_AND_ASSIGN(DebugLineOpCodeWriter); +}; + +} // namespace dwarf +} // namespace art + +#endif // ART_COMPILER_DWARF_DEBUG_LINE_OPCODE_WRITER_H_ diff --git a/compiler/dwarf/debug_line_writer.h b/compiler/dwarf/debug_line_writer.h new file mode 100644 index 0000000000..4b7d8d9d92 --- /dev/null +++ b/compiler/dwarf/debug_line_writer.h @@ -0,0 +1,87 @@ +/* + * 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. + */ + +#ifndef ART_COMPILER_DWARF_DEBUG_LINE_WRITER_H_ +#define ART_COMPILER_DWARF_DEBUG_LINE_WRITER_H_ + +#include "debug_line_opcode_writer.h" +#include "dwarf.h" +#include "writer.h" +#include <string> + +namespace art { +namespace dwarf { + +// Writer for the .debug_line section (DWARF-3). +template<typename Allocator = std::allocator<uint8_t>> +class DebugLineWriter FINAL : private Writer<Allocator> { + public: + struct FileEntry { + std::string file_name; + int directory_index; + int modification_time; + int file_size; + }; + + void WriteTable(const std::vector<std::string>& include_directories, + const std::vector<FileEntry>& files, + const DebugLineOpCodeWriter<Allocator>& opcodes) { + size_t header_start = this->data()->size(); + this->PushUint32(0); // Section-length placeholder. + // Claim DWARF-2 version even though we use some DWARF-3 features. + // DWARF-2 consumers will ignore the unknown opcodes. + // This is what clang currently does. + this->PushUint16(2); // .debug_line version. + size_t header_length_pos = this->data()->size(); + this->PushUint32(0); // Header-length placeholder. + this->PushUint8(1 << opcodes.GetCodeFactorBits()); + this->PushUint8(DebugLineOpCodeWriter<Allocator>::kDefaultIsStmt ? 1 : 0); + this->PushInt8(DebugLineOpCodeWriter<Allocator>::kLineBase); + this->PushUint8(DebugLineOpCodeWriter<Allocator>::kLineRange); + this->PushUint8(DebugLineOpCodeWriter<Allocator>::kOpcodeBase); + static const int opcode_lengths[DebugLineOpCodeWriter<Allocator>::kOpcodeBase] = { + 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 }; + for (int i = 1; i < DebugLineOpCodeWriter<Allocator>::kOpcodeBase; i++) { + this->PushUint8(opcode_lengths[i]); + } + for (const std::string& directory : include_directories) { + this->PushData(directory.data(), directory.size() + 1); + } + this->PushUint8(0); // Terminate include_directories list. + for (const FileEntry& file : files) { + this->PushData(file.file_name.data(), file.file_name.size() + 1); + this->PushUleb128(file.directory_index); + this->PushUleb128(file.modification_time); + this->PushUleb128(file.file_size); + } + this->PushUint8(0); // Terminate file list. + this->UpdateUint32(header_length_pos, this->data()->size() - header_length_pos - 4); + this->PushData(opcodes.data()->data(), opcodes.data()->size()); + this->UpdateUint32(header_start, this->data()->size() - header_start - 4); + } + + explicit DebugLineWriter(std::vector<uint8_t, Allocator>* buffer) + : Writer<Allocator>(buffer) { + } + + private: + DISALLOW_COPY_AND_ASSIGN(DebugLineWriter); +}; + +} // namespace dwarf +} // namespace art + +#endif // ART_COMPILER_DWARF_DEBUG_LINE_WRITER_H_ diff --git a/compiler/dwarf/dwarf_test.cc b/compiler/dwarf/dwarf_test.cc new file mode 100644 index 0000000000..0456e851de --- /dev/null +++ b/compiler/dwarf/dwarf_test.cc @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dwarf_test.h" + +#include "dwarf/debug_frame_opcode_writer.h" +#include "dwarf/debug_frame_writer.h" +#include "dwarf/debug_line_opcode_writer.h" +#include "dwarf/debug_line_writer.h" +#include "gtest/gtest.h" + +namespace art { +namespace dwarf { + +// Run the tests only on host since we need objdump. +#ifndef HAVE_ANDROID_OS + +TEST_F(DwarfTest, DebugFrame) { + const bool is64bit = false; + + // Pick offset value which would catch Uleb vs Sleb errors. + const int offset = 40000; + ASSERT_EQ(UnsignedLeb128Size(offset / 4), 2u); + ASSERT_EQ(SignedLeb128Size(offset / 4), 3u); + DW_CHECK("Data alignment factor: -4"); + const Reg reg(6); + + // Test the opcodes in the order mentioned in the spec. + // There are usually several encoding variations of each opcode. + DebugFrameOpCodeWriter<> opcodes; + DW_CHECK("FDE"); + int pc = 0; + for (int i : {0, 1, 0x3F, 0x40, 0xFF, 0x100, 0xFFFF, 0x10000}) { + pc += i; + opcodes.AdvancePC(pc); + } + DW_CHECK_NEXT("DW_CFA_advance_loc: 1 to 01000001"); + DW_CHECK_NEXT("DW_CFA_advance_loc: 63 to 01000040"); + DW_CHECK_NEXT("DW_CFA_advance_loc1: 64 to 01000080"); + DW_CHECK_NEXT("DW_CFA_advance_loc1: 255 to 0100017f"); + DW_CHECK_NEXT("DW_CFA_advance_loc2: 256 to 0100027f"); + DW_CHECK_NEXT("DW_CFA_advance_loc2: 65535 to 0101027e"); + DW_CHECK_NEXT("DW_CFA_advance_loc4: 65536 to 0102027e"); + opcodes.DefCFA(reg, offset); + DW_CHECK_NEXT("DW_CFA_def_cfa: r6 (esi) ofs 40000"); + opcodes.DefCFA(reg, -offset); + DW_CHECK_NEXT("DW_CFA_def_cfa_sf: r6 (esi) ofs -40000"); + opcodes.DefCFARegister(reg); + DW_CHECK_NEXT("DW_CFA_def_cfa_register: r6 (esi)"); + opcodes.DefCFAOffset(offset); + DW_CHECK_NEXT("DW_CFA_def_cfa_offset: 40000"); + opcodes.DefCFAOffset(-offset); + DW_CHECK_NEXT("DW_CFA_def_cfa_offset_sf: -40000"); + uint8_t expr[] = { 0 }; + opcodes.DefCFAExpression(expr, arraysize(expr)); + DW_CHECK_NEXT("DW_CFA_def_cfa_expression"); + opcodes.Undefined(reg); + DW_CHECK_NEXT("DW_CFA_undefined: r6 (esi)"); + opcodes.SameValue(reg); + DW_CHECK_NEXT("DW_CFA_same_value: r6 (esi)"); + opcodes.Offset(Reg(0x3F), -offset); + DW_CHECK_NEXT("DW_CFA_offset: r63 at cfa-40000"); + opcodes.Offset(Reg(0x40), -offset); + DW_CHECK_NEXT("DW_CFA_offset_extended: r64 at cfa-40000"); + opcodes.Offset(Reg(0x40), offset); + DW_CHECK_NEXT("DW_CFA_offset_extended_sf: r64 at cfa+40000"); + opcodes.ValOffset(reg, -offset); + DW_CHECK_NEXT("DW_CFA_val_offset: r6 (esi) at cfa-40000"); + opcodes.ValOffset(reg, offset); + DW_CHECK_NEXT("DW_CFA_val_offset_sf: r6 (esi) at cfa+40000"); + opcodes.Register(reg, Reg(1)); + DW_CHECK_NEXT("DW_CFA_register: r6 (esi) in r1 (ecx)"); + opcodes.Expression(reg, expr, arraysize(expr)); + DW_CHECK_NEXT("DW_CFA_expression: r6 (esi)"); + opcodes.ValExpression(reg, expr, arraysize(expr)); + DW_CHECK_NEXT("DW_CFA_val_expression: r6 (esi)"); + // Bad register likely means that it does not exist on x86, + // but we want to test high register numbers anyway. + opcodes.Restore(Reg(0x3F)); + DW_CHECK_NEXT("DW_CFA_restore: bad register: r63"); + opcodes.Restore(Reg(0x40)); + DW_CHECK_NEXT("DW_CFA_restore_extended: bad register: r64"); + opcodes.Restore(reg); + DW_CHECK_NEXT("DW_CFA_restore: r6 (esi)"); + opcodes.RememberState(); + DW_CHECK_NEXT("DW_CFA_remember_state"); + opcodes.RestoreState(); + DW_CHECK_NEXT("DW_CFA_restore_state"); + opcodes.Nop(); + DW_CHECK_NEXT("DW_CFA_nop"); + + // Also test helpers. + opcodes.DefCFA(Reg(4), 100); // ESP + DW_CHECK_NEXT("DW_CFA_def_cfa: r4 (esp) ofs 100"); + opcodes.AdjustCFAOffset(8); + DW_CHECK_NEXT("DW_CFA_def_cfa_offset: 108"); + opcodes.RelOffset(Reg(0), 0); // push R0 + DW_CHECK_NEXT("DW_CFA_offset: r0 (eax) at cfa-108"); + opcodes.RelOffset(Reg(1), 4); // push R1 + DW_CHECK_NEXT("DW_CFA_offset: r1 (ecx) at cfa-104"); + opcodes.RelOffsetForMany(Reg(2), 8, 1 | (1 << 3), 4); // push R2 and R5 + DW_CHECK_NEXT("DW_CFA_offset: r2 (edx) at cfa-100"); + DW_CHECK_NEXT("DW_CFA_offset: r5 (ebp) at cfa-96"); + opcodes.RestoreMany(Reg(2), 1 | (1 << 3)); // pop R2 and R5 + DW_CHECK_NEXT("DW_CFA_restore: r2 (edx)"); + DW_CHECK_NEXT("DW_CFA_restore: r5 (ebp)"); + + DebugFrameWriter<> eh_frame(&eh_frame_data_, is64bit); + DebugFrameOpCodeWriter<> initial_opcodes; + eh_frame.WriteCIE(Reg(is64bit ? 16 : 8), // Return address register. + initial_opcodes); // Initial opcodes. + eh_frame.WriteFDE(0x01000000, 0x01000000, + opcodes.data()->data(), opcodes.data()->size()); + CheckObjdumpOutput(is64bit, "-W"); +} + +TEST_F(DwarfTest, DebugFrame64) { + const bool is64bit = true; + DebugFrameWriter<> eh_frame(&eh_frame_data_, is64bit); + DebugFrameOpCodeWriter<> no_opcodes; + eh_frame.WriteCIE(Reg(16), no_opcodes); + eh_frame.WriteFDE(0x0100000000000000, 0x0200000000000000, + no_opcodes.data()->data(), no_opcodes.data()->size()); + DW_CHECK("FDE cie=00000000 pc=0100000000000000..0300000000000000"); + CheckObjdumpOutput(is64bit, "-W"); +} + +TEST_F(DwarfTest, DebugLine) { + const bool is64bit = false; + const int code_factor_bits = 1; + DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits); + + std::vector<std::string> include_directories; + include_directories.push_back("/path/to/source"); + DW_CHECK("1\t/path/to/source"); + + std::vector<DebugLineWriter<>::FileEntry> files { + { "file0.c", 0, 1000, 2000 }, + { "file1.c", 1, 1000, 2000 }, + { "file2.c", 1, 1000, 2000 }, + }; + DW_CHECK("1\t0\t1000\t2000\tfile0.c"); + DW_CHECK_NEXT("2\t1\t1000\t2000\tfile1.c"); + DW_CHECK_NEXT("3\t1\t1000\t2000\tfile2.c"); + + DW_CHECK("Line Number Statements"); + opcodes.SetAddress(0x01000000); + DW_CHECK_NEXT("Extended opcode 2: set Address to 0x1000000"); + opcodes.AddRow(); + DW_CHECK_NEXT("Copy"); + opcodes.AdvancePC(0x01000100); + DW_CHECK_NEXT("Advance PC by 256 to 0x1000100"); + opcodes.SetFile(2); + DW_CHECK_NEXT("Set File Name to entry 2 in the File Name Table"); + opcodes.AdvanceLine(3); + DW_CHECK_NEXT("Advance Line by 2 to 3"); + opcodes.SetColumn(4); + DW_CHECK_NEXT("Set column to 4"); + opcodes.NegateStmt(); + DW_CHECK_NEXT("Set is_stmt to 0"); + opcodes.SetBasicBlock(); + DW_CHECK_NEXT("Set basic block"); + opcodes.SetPrologueEnd(); + DW_CHECK_NEXT("Set prologue_end to true"); + opcodes.SetEpilogueBegin(); + DW_CHECK_NEXT("Set epilogue_begin to true"); + opcodes.SetISA(5); + DW_CHECK_NEXT("Set ISA to 5"); + opcodes.EndSequence(); + DW_CHECK_NEXT("Extended opcode 1: End of Sequence"); + // TODO: objdump says this opcode has invalid length. I am not convinced. + // opcodes.DefineFile("file.c", 0, 1000, 2000); + // DW_CHECK_NEXT("Extended opcode 3: define new File Table entry"); + // DW_CHECK_NEXT("1\t0\t1000\t2000\tfile.c"); + + DebugLineWriter<> debug_line(&debug_line_data_); + debug_line.WriteTable(include_directories, files, opcodes); + CheckObjdumpOutput(is64bit, "-W"); +} + +// DWARF has special one byte codes which advance PC and line at the same time. +TEST_F(DwarfTest, DebugLineSpecialOpcodes) { + const bool is64bit = false; + const int code_factor_bits = 1; + uint32_t pc = 0x01000000; + int line = 1; + DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits); + opcodes.SetAddress(pc); + size_t num_rows = 0; + DW_CHECK("Line Number Statements:"); + DW_CHECK("Special opcode"); + DW_CHECK("Advance PC by constant"); + DW_CHECK("Decoded dump of debug contents of section .debug_line:"); + for (int addr_delta = 0; addr_delta < 80; addr_delta += 2) { + for (int line_delta = 16; line_delta >= -16; --line_delta) { + pc += addr_delta; + line += line_delta; + opcodes.AddRow(pc, line); + num_rows++; + ASSERT_EQ(opcodes.CurrentAddress(), pc); + ASSERT_EQ(opcodes.CurrentLine(), line); + char expected[1024]; + sprintf(expected, "%i 0x%x", line, pc); + DW_CHECK_NEXT(expected); + } + } + EXPECT_LT(opcodes.data()->size(), num_rows * 3); + + std::vector<std::string> directories; + std::vector<DebugLineWriter<>::FileEntry> files; + DebugLineWriter<> debug_line(&debug_line_data_); + debug_line.WriteTable(directories, files, opcodes); + CheckObjdumpOutput(is64bit, "-W -WL"); +} + +#endif // HAVE_ANDROID_OS + +} // namespace dwarf +} // namespace art diff --git a/compiler/dwarf/dwarf_test.h b/compiler/dwarf/dwarf_test.h new file mode 100644 index 0000000000..d4e83f8f74 --- /dev/null +++ b/compiler/dwarf/dwarf_test.h @@ -0,0 +1,186 @@ +/* + * 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. + */ + +#ifndef ART_COMPILER_DWARF_DWARF_TEST_H_ +#define ART_COMPILER_DWARF_DWARF_TEST_H_ + +#include <cstring> +#include <memory> +#include <stdio.h> +#include <string> + +#include "utils.h" +#include "base/unix_file/fd_file.h" +#include "common_runtime_test.h" +#include "elf_builder.h" +#include "gtest/gtest.h" +#include "os.h" + +namespace art { +namespace dwarf { + +#define DW_CHECK(substring) Check(substring, false, __FILE__, __LINE__) +#define DW_CHECK_NEXT(substring) Check(substring, true, __FILE__, __LINE__) + +class DwarfTest : public CommonRuntimeTest { + public: + static constexpr bool kPrintObjdumpOutput = false; // debugging. + + struct ExpectedLine { + std::string substring; + bool next; + const char* at_file; + int at_line; + }; + + // Check that the objdump output contains given output. + // If next is true, it must be the next line. Otherwise lines are skipped. + void Check(const char* substr, bool next, const char* at_file, int at_line) { + expected_lines_.push_back(ExpectedLine {substr, next, at_file, at_line}); + } + + // Pretty-print the generated DWARF data using objdump. + template<typename Elf_Word, typename Elf_Sword, typename Elf_Addr, typename Elf_Dyn, + typename Elf_Sym, typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr> + std::vector<std::string> Objdump(bool is64bit, const char* args) { + // Write simple elf file with just the DWARF sections. + class NoCode : public CodeOutput { + virtual void SetCodeOffset(size_t) { } + virtual bool Write(OutputStream*) { return true; } + } code; + ScratchFile file; + InstructionSet isa = is64bit ? kX86_64 : kX86; + ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn, + Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr> builder( + &code, file.GetFile(), isa, 0, 0, 0, 0, 0, 0, false, false); + typedef ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> Section; + if (!debug_info_data_.empty()) { + Section debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0); + debug_info.SetBuffer(debug_info_data_); + builder.RegisterRawSection(debug_info); + } + if (!debug_abbrev_data_.empty()) { + Section debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0); + debug_abbrev.SetBuffer(debug_abbrev_data_); + builder.RegisterRawSection(debug_abbrev); + } + if (!debug_str_data_.empty()) { + Section debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0); + debug_str.SetBuffer(debug_str_data_); + builder.RegisterRawSection(debug_str); + } + if (!debug_line_data_.empty()) { + Section debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0); + debug_line.SetBuffer(debug_line_data_); + builder.RegisterRawSection(debug_line); + } + if (!eh_frame_data_.empty()) { + Section eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0); + eh_frame.SetBuffer(eh_frame_data_); + builder.RegisterRawSection(eh_frame); + } + builder.Init(); + builder.Write(); + + // Read the elf file back using objdump. + std::vector<std::string> lines; + auto cmd = std::string("objdump ") + args + " " + file.GetFilename() + " 2>&1"; + FILE* output = popen(cmd.data(), "r"); + char buffer[1024]; + const char* line; + while ((line = fgets(buffer, sizeof(buffer), output)) != nullptr) { + if (kPrintObjdumpOutput) { + printf("%s", line); + } + if (line[0] != '\0' && line[0] != '\n') { + EXPECT_TRUE(strstr(line, "objdump: Error:") == nullptr) << line; + EXPECT_TRUE(strstr(line, "objdump: Warning:") == nullptr) << line; + std::string str(line); + if (str.back() == '\n') { + str.pop_back(); + } + lines.push_back(str); + } + } + pclose(output); + return lines; + } + + std::vector<std::string> Objdump(bool is64bit, const char* args) { + if (is64bit) { + return Objdump<Elf64_Word, Elf64_Sword, Elf64_Addr, Elf64_Dyn, + Elf64_Sym, Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(is64bit, args); + } else { + return Objdump<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn, + Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(is64bit, args); + } + } + + // Compare objdump output to the recorded checks. + void CheckObjdumpOutput(bool is64bit, const char* args) { + std::vector<std::string> actual_lines = Objdump(is64bit, args); + auto actual_line = actual_lines.begin(); + for (const ExpectedLine& expected_line : expected_lines_) { + const std::string& substring = expected_line.substring; + if (actual_line == actual_lines.end()) { + ADD_FAILURE_AT(expected_line.at_file, expected_line.at_line) << + "Expected '" << substring << "'.\n" << + "Seen end of output."; + } else if (expected_line.next) { + if (actual_line->find(substring) == std::string::npos) { + ADD_FAILURE_AT(expected_line.at_file, expected_line.at_line) << + "Expected '" << substring << "'.\n" << + "Seen '" << actual_line->data() << "'."; + } else { + // printf("Found '%s' in '%s'.\n", substring.data(), actual_line->data()); + } + actual_line++; + } else { + bool found = false; + for (auto it = actual_line; it < actual_lines.end(); it++) { + if (it->find(substring) != std::string::npos) { + actual_line = it; + found = true; + break; + } + } + if (!found) { + ADD_FAILURE_AT(expected_line.at_file, expected_line.at_line) << + "Expected '" << substring << "'.\n" << + "Not found anywhere in the rest of the output."; + } else { + // printf("Found '%s' in '%s'.\n", substring.data(), actual_line->data()); + actual_line++; + } + } + } + } + + // Buffers which are going to assembled into ELF file and passed to objdump. + std::vector<uint8_t> eh_frame_data_; + std::vector<uint8_t> debug_info_data_; + std::vector<uint8_t> debug_abbrev_data_; + std::vector<uint8_t> debug_str_data_; + std::vector<uint8_t> debug_line_data_; + + // The expected output of objdump. + std::vector<ExpectedLine> expected_lines_; +}; + +} // namespace dwarf +} // namespace art + +#endif // ART_COMPILER_DWARF_DWARF_TEST_H_ diff --git a/compiler/dwarf/register.h b/compiler/dwarf/register.h new file mode 100644 index 0000000000..fa666dffa9 --- /dev/null +++ b/compiler/dwarf/register.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#ifndef ART_COMPILER_DWARF_REGISTER_H_ +#define ART_COMPILER_DWARF_REGISTER_H_ + +namespace art { +namespace dwarf { + +// Represents DWARF register. +class Reg { + public: + explicit Reg(int reg_num) : num_(reg_num) { } + int num() const { return num_; } + + // TODO: Arm S0–S31 register mapping is obsolescent. + // We should use VFP-v3/Neon D0-D31 mapping instead. + // However, D0 is aliased to pair of S0 and S1, so using that + // mapping we can not easily say S0 is spilled and S1 is not. + // There are ways around this in DWARF but they are complex. + // It would be much simpler to always spill whole D registers. + // Arm64 mapping is correct since we already do this there. + + static Reg ArmCore(int num) { return Reg(num); } + static Reg ArmFp(int num) { return Reg(64 + num); } // S0–S31. + static Reg Arm64Core(int num) { return Reg(num); } + static Reg Arm64Fp(int num) { return Reg(64 + num); } // V0-V31. + static Reg MipsCore(int num) { return Reg(num); } + static Reg Mips64Core(int num) { return Reg(num); } + static Reg X86Core(int num) { return Reg(num); } + static Reg X86Fp(int num) { return Reg(21 + num); } + static Reg X86_64Core(int num) { + static const int map[8] = {0, 2, 1, 3, 7, 6, 4, 5}; + return Reg(num < 8 ? map[num] : num); + } + static Reg X86_64Fp(int num) { return Reg(17 + num); } + + private: + int num_; +}; + +} // namespace dwarf +} // namespace art + +#endif // ART_COMPILER_DWARF_REGISTER_H_ diff --git a/compiler/dwarf/writer.h b/compiler/dwarf/writer.h new file mode 100644 index 0000000000..d8e29f0986 --- /dev/null +++ b/compiler/dwarf/writer.h @@ -0,0 +1,159 @@ +/* + * 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. + */ + +#ifndef ART_COMPILER_DWARF_WRITER_H_ +#define ART_COMPILER_DWARF_WRITER_H_ + +#include <vector> +#include "leb128.h" +#include "base/logging.h" +#include "utils.h" + +namespace art { +namespace dwarf { + +// The base class for all DWARF writers. +template<typename Allocator = std::allocator<uint8_t>> +class Writer { + public: + void PushUint8(int value) { + DCHECK_GE(value, 0); + DCHECK_LE(value, UINT8_MAX); + data_->push_back(value & 0xff); + } + + void PushUint16(int value) { + DCHECK_GE(value, 0); + DCHECK_LE(value, UINT16_MAX); + data_->push_back((value >> 0) & 0xff); + data_->push_back((value >> 8) & 0xff); + } + + void PushUint32(uint32_t value) { + data_->push_back((value >> 0) & 0xff); + data_->push_back((value >> 8) & 0xff); + data_->push_back((value >> 16) & 0xff); + data_->push_back((value >> 24) & 0xff); + } + + void PushUint32(int value) { + DCHECK_GE(value, 0); + PushUint32(static_cast<uint32_t>(value)); + } + + void PushUint32(uint64_t value) { + DCHECK_LE(value, UINT32_MAX); + PushUint32(static_cast<uint32_t>(value)); + } + + void PushUint64(uint64_t value) { + data_->push_back((value >> 0) & 0xff); + data_->push_back((value >> 8) & 0xff); + data_->push_back((value >> 16) & 0xff); + data_->push_back((value >> 24) & 0xff); + data_->push_back((value >> 32) & 0xff); + data_->push_back((value >> 40) & 0xff); + data_->push_back((value >> 48) & 0xff); + data_->push_back((value >> 56) & 0xff); + } + + void PushInt8(int value) { + DCHECK_GE(value, INT8_MIN); + DCHECK_LE(value, INT8_MAX); + PushUint8(static_cast<uint8_t>(value)); + } + + void PushInt16(int value) { + DCHECK_GE(value, INT16_MIN); + DCHECK_LE(value, INT16_MAX); + PushUint16(static_cast<uint16_t>(value)); + } + + void PushInt32(int value) { + PushUint32(static_cast<uint32_t>(value)); + } + + void PushInt64(int64_t value) { + PushUint64(static_cast<uint64_t>(value)); + } + + // Variable-length encoders. + + void PushUleb128(uint32_t value) { + EncodeUnsignedLeb128(data_, value); + } + + void PushUleb128(int value) { + DCHECK_GE(value, 0); + EncodeUnsignedLeb128(data_, value); + } + + void PushSleb128(int value) { + EncodeSignedLeb128(data_, value); + } + + // Miscellaneous functions. + + void PushString(const char* value) { + data_->insert(data_->end(), value, value + strlen(value) + 1); + } + + void PushData(const void* ptr, size_t size) { + const char* p = reinterpret_cast<const char*>(ptr); + data_->insert(data_->end(), p, p + size); + } + + void UpdateUint32(size_t offset, uint32_t value) { + DCHECK_LT(offset + 3, data_->size()); + (*data_)[offset + 0] = (value >> 0) & 0xFF; + (*data_)[offset + 1] = (value >> 8) & 0xFF; + (*data_)[offset + 2] = (value >> 16) & 0xFF; + (*data_)[offset + 3] = (value >> 24) & 0xFF; + } + + void UpdateUint64(size_t offset, uint64_t value) { + DCHECK_LT(offset + 7, data_->size()); + (*data_)[offset + 0] = (value >> 0) & 0xFF; + (*data_)[offset + 1] = (value >> 8) & 0xFF; + (*data_)[offset + 2] = (value >> 16) & 0xFF; + (*data_)[offset + 3] = (value >> 24) & 0xFF; + (*data_)[offset + 4] = (value >> 32) & 0xFF; + (*data_)[offset + 5] = (value >> 40) & 0xFF; + (*data_)[offset + 6] = (value >> 48) & 0xFF; + (*data_)[offset + 7] = (value >> 56) & 0xFF; + } + + void Pad(int alignment) { + DCHECK_NE(alignment, 0); + data_->resize(RoundUp(data_->size(), alignment), 0); + } + + const std::vector<uint8_t, Allocator>* data() const { + return data_; + } + + explicit Writer(std::vector<uint8_t, Allocator>* buffer) : data_(buffer) { } + + private: + std::vector<uint8_t, Allocator>* data_; + + DISALLOW_COPY_AND_ASSIGN(Writer); +}; + +} // namespace dwarf +} // namespace art + +#endif // ART_COMPILER_DWARF_WRITER_H_ diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index d59ad6c32f..afd39e8874 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -85,9 +85,6 @@ TEST_F(OatTest, WriteRead) { compiler_options_.reset(new CompilerOptions); verification_results_.reset(new VerificationResults(compiler_options_.get())); method_inliner_map_.reset(new DexFileToMethodInlinerMap); - callbacks_.reset(new QuickCompilerCallbacks(verification_results_.get(), - method_inliner_map_.get(), - CompilerCallbacks::CallbackMode::kCompileApp)); timer_.reset(new CumulativeLogger("Compilation times")); compiler_driver_.reset(new CompilerDriver(compiler_options_.get(), verification_results_.get(), diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc index e9ca042f1d..be432c5a20 100644 --- a/compiler/optimizing/boolean_simplifier.cc +++ b/compiler/optimizing/boolean_simplifier.cc @@ -59,7 +59,8 @@ static HInstruction* GetOppositeCondition(HInstruction* cond) { return new (allocator) HGreaterThan(lhs, rhs); } else if (cond->IsGreaterThan()) { return new (allocator) HLessThanOrEqual(lhs, rhs); - } else if (cond->IsGreaterThanOrEqual()) { + } else { + DCHECK(cond->IsGreaterThanOrEqual()); return new (allocator) HLessThan(lhs, rhs); } } else if (cond->IsIntConstant()) { @@ -70,10 +71,11 @@ static HInstruction* GetOppositeCondition(HInstruction* cond) { DCHECK(int_const->IsOne()); return graph->GetIntConstant(0); } + } else { + // General case when 'cond' is another instruction of type boolean. + // Negate with 'cond == 0'. + return new (allocator) HEqual(cond, graph->GetIntConstant(0)); } - - LOG(FATAL) << "Instruction " << cond->DebugName() << " used as a condition"; - UNREACHABLE(); } void HBooleanSimplifier::Run() { diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 0f79d189be..1f95041a92 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -2087,16 +2087,32 @@ void InstructionCodeGeneratorARM::VisitMul(HMul* mul) { } void LocationsBuilderARM::VisitDiv(HDiv* div) { - LocationSummary::CallKind call_kind = div->GetResultType() == Primitive::kPrimLong - ? LocationSummary::kCall - : LocationSummary::kNoCall; + LocationSummary::CallKind call_kind = LocationSummary::kNoCall; + if (div->GetResultType() == Primitive::kPrimLong) { + // pLdiv runtime call. + call_kind = LocationSummary::kCall; + } else if (div->GetResultType() == Primitive::kPrimInt && + !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + // pIdivmod runtime call. + call_kind = LocationSummary::kCall; + } + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, call_kind); switch (div->GetResultType()) { case Primitive::kPrimInt: { - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + } else { + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but + // we only need the former. + locations->SetOut(Location::RegisterLocation(R0)); + } break; } case Primitive::kPrimLong: { @@ -2129,9 +2145,18 @@ void InstructionCodeGeneratorARM::VisitDiv(HDiv* div) { switch (div->GetResultType()) { case Primitive::kPrimInt: { - __ sdiv(out.AsRegister<Register>(), - first.AsRegister<Register>(), - second.AsRegister<Register>()); + if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + __ sdiv(out.AsRegister<Register>(), + first.AsRegister<Register>(), + second.AsRegister<Register>()); + } else { + InvokeRuntimeCallingConvention calling_convention; + DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegister<Register>()); + DCHECK_EQ(calling_convention.GetRegisterAt(1), second.AsRegister<Register>()); + DCHECK_EQ(R0, out.AsRegister<Register>()); + + codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pIdivmod), div, div->GetDexPc(), nullptr); + } break; } @@ -2169,17 +2194,32 @@ void InstructionCodeGeneratorARM::VisitDiv(HDiv* div) { void LocationsBuilderARM::VisitRem(HRem* rem) { Primitive::Type type = rem->GetResultType(); - LocationSummary::CallKind call_kind = type == Primitive::kPrimInt - ? LocationSummary::kNoCall - : LocationSummary::kCall; + + // Most remainders are implemented in the runtime. + LocationSummary::CallKind call_kind = LocationSummary::kCall; + if (rem->GetResultType() == Primitive::kPrimInt && + codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + // Have hardware divide instruction for int, do it with three instructions. + call_kind = LocationSummary::kNoCall; + } + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind); switch (type) { case Primitive::kPrimInt: { - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); - locations->AddTemp(Location::RequiresRegister()); + if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + locations->AddTemp(Location::RequiresRegister()); + } else { + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but + // we only need the latter. + locations->SetOut(Location::RegisterLocation(R1)); + } break; } case Primitive::kPrimLong: { @@ -2224,16 +2264,25 @@ void InstructionCodeGeneratorARM::VisitRem(HRem* rem) { Primitive::Type type = rem->GetResultType(); switch (type) { case Primitive::kPrimInt: { - Register reg1 = first.AsRegister<Register>(); - Register reg2 = second.AsRegister<Register>(); - Register temp = locations->GetTemp(0).AsRegister<Register>(); + if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + Register reg1 = first.AsRegister<Register>(); + Register reg2 = second.AsRegister<Register>(); + Register temp = locations->GetTemp(0).AsRegister<Register>(); + + // temp = reg1 / reg2 (integer division) + // temp = temp * reg2 + // dest = reg1 - temp + __ sdiv(temp, reg1, reg2); + __ mul(temp, temp, reg2); + __ sub(out.AsRegister<Register>(), reg1, ShifterOperand(temp)); + } else { + InvokeRuntimeCallingConvention calling_convention; + DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegister<Register>()); + DCHECK_EQ(calling_convention.GetRegisterAt(1), second.AsRegister<Register>()); + DCHECK_EQ(R1, out.AsRegister<Register>()); - // temp = reg1 / reg2 (integer division) - // temp = temp * reg2 - // dest = reg1 - temp - __ sdiv(temp, reg1, reg2); - __ mul(temp, temp, reg2); - __ sub(out.AsRegister<Register>(), reg1, ShifterOperand(temp)); + codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pIdivmod), rem, rem->GetDexPc(), nullptr); + } break; } diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 4b990f1ddd..2c17a67867 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -49,7 +49,8 @@ void HInliner::Run() { for (HInstruction* instruction = block->GetFirstInstruction(); instruction != nullptr;) { HInstruction* next = instruction->GetNext(); HInvokeStaticOrDirect* call = instruction->AsInvokeStaticOrDirect(); - if (call != nullptr) { + // As long as the call is not intrinsified, it is worth trying to inline. + if (call != nullptr && call->GetIntrinsic() == Intrinsics::kNone) { // We use the original invoke type to ensure the resolution of the called method // works properly. if (!TryInline(call, call->GetDexMethodIndex(), call->GetOriginalInvokeType())) { diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 36cf8568e5..628a844cc7 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -191,8 +191,10 @@ static Intrinsics GetIntrinsic(InlineMethod method) { case kIntrinsicCompareTo: return Intrinsics::kStringCompareTo; case kIntrinsicIsEmptyOrLength: - return ((method.d.data & kIntrinsicFlagIsEmpty) == 0) ? - Intrinsics::kStringLength : Intrinsics::kStringIsEmpty; + // The inliner can handle these two cases - and this is the preferred approach + // since after inlining the call is no longer visible (as opposed to waiting + // until codegen to handle intrinsic). + return Intrinsics::kNone; case kIntrinsicIndexOf: return ((method.d.data & kIntrinsicFlagBase0) == 0) ? Intrinsics::kStringIndexOfAfter : Intrinsics::kStringIndexOf; diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 0f6978dc67..33176f009c 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -903,8 +903,6 @@ UNIMPLEMENTED_INTRINSIC(MathRoundDouble) // Could be done by changing rounding UNIMPLEMENTED_INTRINSIC(MathRoundFloat) // Could be done by changing rounding mode, maybe? UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) // High register pressure. UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) -UNIMPLEMENTED_INTRINSIC(StringIsEmpty) // Might not want to do these two anyways, inlining should -UNIMPLEMENTED_INTRINSIC(StringLength) // be good enough here. UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 5f78933bd3..72d303c870 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1030,8 +1030,6 @@ void IntrinsicCodeGeneratorARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED } UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) -UNIMPLEMENTED_INTRINSIC(StringIsEmpty) // Might not want to do these two anyways, inlining should -UNIMPLEMENTED_INTRINSIC(StringLength) // be good enough here. UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h index 9cc77c6251..10f6e1d6c7 100644 --- a/compiler/optimizing/intrinsics_list.h +++ b/compiler/optimizing/intrinsics_list.h @@ -60,10 +60,8 @@ V(MemoryPokeShortNative, kStatic) \ V(StringCharAt, kDirect) \ V(StringCompareTo, kDirect) \ - V(StringIsEmpty, kDirect) \ V(StringIndexOf, kDirect) \ V(StringIndexOfAfter, kDirect) \ - V(StringLength, kDirect) \ V(UnsafeCASInt, kDirect) \ V(UnsafeCASLong, kDirect) \ V(UnsafeCASObject, kDirect) \ diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 6b7e4aeb5d..384737f55a 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -1196,8 +1196,6 @@ UNIMPLEMENTED_INTRINSIC(MathCeil) UNIMPLEMENTED_INTRINSIC(MathRint) UNIMPLEMENTED_INTRINSIC(MathRoundDouble) UNIMPLEMENTED_INTRINSIC(MathRoundFloat) -UNIMPLEMENTED_INTRINSIC(StringIsEmpty) // Might not want to do these two anyways, inlining should -UNIMPLEMENTED_INTRINSIC(StringLength) // be good enough here. UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 05ea906cad..736cea88cb 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -1014,8 +1014,6 @@ UNIMPLEMENTED_INTRINSIC(MathCeil) UNIMPLEMENTED_INTRINSIC(MathRint) UNIMPLEMENTED_INTRINSIC(MathRoundDouble) UNIMPLEMENTED_INTRINSIC(MathRoundFloat) -UNIMPLEMENTED_INTRINSIC(StringIsEmpty) // Might not want to do these two anyways, inlining should -UNIMPLEMENTED_INTRINSIC(StringLength) // be good enough here. UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 5ce73baef2..b2f9c65153 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -583,8 +583,13 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, if (method != nullptr) { return method; } - return delegate_->Compile(code_item, access_flags, invoke_type, class_def_idx, method_idx, - class_loader, dex_file); + method = delegate_->Compile(code_item, access_flags, invoke_type, class_def_idx, method_idx, + class_loader, dex_file); + + if (method != nullptr) { + compilation_stats_.RecordStat(MethodCompilationStat::kCompiledQuick); + } + return method; } Compiler* CreateOptimizingCompiler(CompilerDriver* driver) { diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 22ec2a5167..b97a66719d 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -28,6 +28,7 @@ enum MethodCompilationStat { kAttemptCompilation = 0, kCompiledBaseline, kCompiledOptimized, + kCompiledQuick, kInlinedInvoke, kNotCompiledUnsupportedIsa, kNotCompiledPathological, @@ -65,16 +66,22 @@ class OptimizingCompilerStats { compile_stats_[kCompiledBaseline] * 100 / compile_stats_[kAttemptCompilation]; size_t optimized_percent = compile_stats_[kCompiledOptimized] * 100 / compile_stats_[kAttemptCompilation]; + size_t quick_percent = + compile_stats_[kCompiledQuick] * 100 / compile_stats_[kAttemptCompilation]; std::ostringstream oss; - oss << "Attempted compilation of " << compile_stats_[kAttemptCompilation] << " methods: " - << unoptimized_percent << "% (" << compile_stats_[kCompiledBaseline] << ") unoptimized, " - << optimized_percent << "% (" << compile_stats_[kCompiledOptimized] << ") optimized."; + oss << "Attempted compilation of " << compile_stats_[kAttemptCompilation] << " methods: "; + + oss << unoptimized_percent << "% (" << compile_stats_[kCompiledBaseline] << ") unoptimized, "; + oss << optimized_percent << "% (" << compile_stats_[kCompiledOptimized] << ") optimized, "; + oss << quick_percent << "% (" << compile_stats_[kCompiledQuick] << ") quick."; + + LOG(INFO) << oss.str(); + for (int i = 0; i < kLastStat; i++) { if (compile_stats_[i] != 0) { - oss << "\n" << PrintMethodCompilationStat(i) << ": " << compile_stats_[i]; + VLOG(compiler) << PrintMethodCompilationStat(i) << ": " << compile_stats_[i]; } } - LOG(INFO) << oss.str(); } } @@ -84,6 +91,7 @@ class OptimizingCompilerStats { case kAttemptCompilation : return "kAttemptCompilation"; case kCompiledBaseline : return "kCompiledBaseline"; case kCompiledOptimized : return "kCompiledOptimized"; + case kCompiledQuick : return "kCompiledQuick"; case kInlinedInvoke : return "kInlinedInvoke"; case kNotCompiledUnsupportedIsa : return "kNotCompiledUnsupportedIsa"; case kNotCompiledPathological : return "kNotCompiledPathological"; diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index cecc210cbf..cf38bd3f8c 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -213,7 +213,7 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { LiveInterval* interval = LiveInterval::MakeTempInterval(allocator_, Primitive::kPrimInt); temp_intervals_.Add(interval); - interval->AddRange(position, position + 1); + interval->AddTempUse(instruction, i); unhandled_core_intervals_.Add(interval); break; } @@ -222,7 +222,7 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { LiveInterval* interval = LiveInterval::MakeTempInterval(allocator_, Primitive::kPrimDouble); temp_intervals_.Add(interval); - interval->AddRange(position, position + 1); + interval->AddTempUse(instruction, i); if (codegen_->NeedsTwoRegisters(Primitive::kPrimDouble)) { interval->AddHighInterval(true); LiveInterval* high = interval->GetHighInterval(); @@ -851,6 +851,23 @@ bool RegisterAllocator::TrySplitNonPairOrUnalignedPairIntervalAt(size_t position return false; } +bool RegisterAllocator::PotentiallyRemoveOtherHalf(LiveInterval* interval, + GrowableArray<LiveInterval*>* intervals, + size_t index) { + if (interval->IsLowInterval()) { + DCHECK_EQ(intervals->Get(index), interval->GetHighInterval()); + intervals->DeleteAt(index); + return true; + } else if (interval->IsHighInterval()) { + DCHECK_GT(index, 0u); + DCHECK_EQ(intervals->Get(index - 1), interval->GetLowInterval()); + intervals->DeleteAt(index - 1); + return true; + } else { + return false; + } +} + // Find the register that is used the last, and spill the interval // that holds it. If the first use of `current` is after that register // we spill `current` instead. @@ -974,33 +991,17 @@ bool RegisterAllocator::AllocateBlockedReg(LiveInterval* current) { if (active->GetRegister() == reg) { DCHECK(!active->IsFixed()); LiveInterval* split = Split(active, current->GetStart()); - active_.DeleteAt(i); if (split != active) { handled_.Add(active); } + active_.DeleteAt(i); + PotentiallyRemoveOtherHalf(active, &active_, i); AddSorted(unhandled_, split); - - if (active->IsLowInterval() || active->IsHighInterval()) { - LiveInterval* other_half = active->IsLowInterval() - ? active->GetHighInterval() - : active->GetLowInterval(); - // We also need to remove the other half from the list of actives. - bool found = false; - for (size_t j = 0; j < active_.Size(); ++j) { - if (active_.Get(j) == other_half) { - found = true; - active_.DeleteAt(j); - handled_.Add(other_half); - break; - } - } - DCHECK(found); - } break; } } - for (size_t i = 0, e = inactive_.Size(); i < e; ++i) { + for (size_t i = 0; i < inactive_.Size(); ++i) { LiveInterval* inactive = inactive_.Get(i); if (inactive->GetRegister() == reg) { if (!current->IsSplit() && !inactive->IsFixed()) { @@ -1024,29 +1025,14 @@ bool RegisterAllocator::AllocateBlockedReg(LiveInterval* current) { // If it's inactive, it must start before the current interval. DCHECK_NE(split, inactive); inactive_.DeleteAt(i); + if (PotentiallyRemoveOtherHalf(inactive, &inactive_, i) && inactive->IsHighInterval()) { + // We have removed an entry prior to `inactive`. So we need to decrement. + --i; + } + // Decrement because we have removed `inactive` from the list. --i; - --e; handled_.Add(inactive); AddSorted(unhandled_, split); - - if (inactive->IsLowInterval() || inactive->IsHighInterval()) { - LiveInterval* other_half = inactive->IsLowInterval() - ? inactive->GetHighInterval() - : inactive->GetLowInterval(); - - // We also need to remove the other half from the list of inactives. - bool found = false; - for (size_t j = 0; j < inactive_.Size(); ++j) { - if (inactive_.Get(j) == other_half) { - found = true; - inactive_.DeleteAt(j); - --e; - handled_.Add(other_half); - break; - } - } - DCHECK(found); - } } } } @@ -1695,8 +1681,6 @@ void RegisterAllocator::Resolve() { } // Assign temp locations. - HInstruction* current = nullptr; - size_t temp_index = 0; for (size_t i = 0; i < temp_intervals_.Size(); ++i) { LiveInterval* temp = temp_intervals_.Get(i); if (temp->IsHighInterval()) { @@ -1704,25 +1688,20 @@ void RegisterAllocator::Resolve() { continue; } HInstruction* at = liveness_.GetTempUser(temp); - if (at != current) { - temp_index = 0; - current = at; - } + size_t temp_index = liveness_.GetTempIndex(temp); LocationSummary* locations = at->GetLocations(); switch (temp->GetType()) { case Primitive::kPrimInt: - locations->SetTempAt( - temp_index++, Location::RegisterLocation(temp->GetRegister())); + locations->SetTempAt(temp_index, Location::RegisterLocation(temp->GetRegister())); break; case Primitive::kPrimDouble: if (codegen_->NeedsTwoRegisters(Primitive::kPrimDouble)) { Location location = Location::FpuRegisterPairLocation( temp->GetRegister(), temp->GetHighInterval()->GetRegister()); - locations->SetTempAt(temp_index++, location); + locations->SetTempAt(temp_index, location); } else { - locations->SetTempAt( - temp_index++, Location::FpuRegisterLocation(temp->GetRegister())); + locations->SetTempAt(temp_index, Location::FpuRegisterLocation(temp->GetRegister())); } break; diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h index fcc61128a6..717be75533 100644 --- a/compiler/optimizing/register_allocator.h +++ b/compiler/optimizing/register_allocator.h @@ -144,6 +144,13 @@ class RegisterAllocator { size_t first_register_use, size_t* next_use); + // If `interval` has another half, remove it from the list of `intervals`. + // `index` holds the index at which `interval` is in `intervals`. + // Returns whether there is another half. + bool PotentiallyRemoveOtherHalf(LiveInterval* interval, + GrowableArray<LiveInterval*>* intervals, + size_t index); + ArenaAllocator* const allocator_; CodeGenerator* const codegen_; const SsaLivenessAnalysis& liveness_; diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index 56ccd717cf..0f3973e5fb 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -318,6 +318,8 @@ static int RegisterOrLowRegister(Location location) { int LiveInterval::FindFirstRegisterHint(size_t* free_until) const { DCHECK(!IsHighInterval()); + if (IsTemp()) return kNoRegister; + if (GetParent() == this && defined_by_ != nullptr) { // This is the first interval for the instruction. Try to find // a register based on its definition. diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index b57029d1a7..bc78dc2e76 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -180,6 +180,15 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { // This interval is the result of a split. bool IsSplit() const { return parent_ != this; } + void AddTempUse(HInstruction* instruction, size_t temp_index) { + DCHECK(IsTemp()); + DCHECK(first_use_ == nullptr) << "A temporary can only have one user"; + size_t position = instruction->GetLifetimePosition(); + first_use_ = new (allocator_) UsePosition( + instruction, temp_index, /* is_environment */ false, position, first_use_); + AddRange(position, position + 1); + } + void AddUse(HInstruction* instruction, size_t input_index, bool is_environment) { // Set the use within the instruction. size_t position = instruction->GetLifetimePosition() + 1; @@ -856,7 +865,15 @@ class SsaLivenessAnalysis : public ValueObject { HInstruction* GetTempUser(LiveInterval* temp) const { // A temporary shares the same lifetime start as the instruction that requires it. DCHECK(temp->IsTemp()); - return GetInstructionFromPosition(temp->GetStart() / 2); + HInstruction* user = GetInstructionFromPosition(temp->GetStart() / 2); + DCHECK_EQ(user, temp->GetFirstUse()->GetUser()); + return user; + } + + size_t GetTempIndex(LiveInterval* temp) const { + // We use the input index to store the index of the temporary in the user's temporary list. + DCHECK(temp->IsTemp()); + return temp->GetFirstUse()->GetInputIndex(); } size_t GetMaxLifetimePosition() const { diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 16f0e70999..0c2250eab8 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1094,7 +1094,7 @@ ENTRY art_quick_resolution_trampoline lw $a0, ARG_SLOT_SIZE($sp) # load resolved method to $a0 RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME move $t9, $v0 # code pointer must be in $t9 to generate the global pointer - jalr $zero, $v0 # tail call to method + jalr $zero, $t9 # tail call to method nop 1: RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME @@ -1203,29 +1203,28 @@ art_quick_instrumentation_exit: .cpload $t9 move $ra, $zero # link register is to here, so clobber with 0 for later checks + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME addiu $sp, $sp, -16 # allocate temp storage on the stack .cfi_adjust_cfa_offset 16 - sw $v0, 12($sp) - .cfi_rel_offset 2, 32 - sw $v1, 8($sp) - .cfi_rel_offset 3, 36 - s.d $f0, 0($sp) - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME + sw $v0, ARG_SLOT_SIZE+12($sp) + .cfi_rel_offset 2, ARG_SLOT_SIZE+12 + sw $v1, ARG_SLOT_SIZE+8($sp) + .cfi_rel_offset 3, ARG_SLOT_SIZE+8 + s.d $f0, ARG_SLOT_SIZE($sp) s.d $f0, 16($sp) # pass fpr result move $a2, $v0 # pass gpr result move $a3, $v1 - addiu $a1, $sp, ARG_SLOT_SIZE # pass $sp (remove arg slots) + addiu $a1, $sp, ARG_SLOT_SIZE+16 # pass $sp (remove arg slots and temp storage) jal artInstrumentationMethodExitFromCode # (Thread*, SP, gpr_res, fpr_res) move $a0, rSELF # pass Thread::Current - move $t0, $v0 # set aside returned link register + move $t9, $v0 # set aside returned link register move $ra, $v1 # set link register for deoptimization - addiu $sp, $sp, ARG_SLOT_SIZE+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE # args slot + refs_only callee save frame - lw $v0, 12($sp) # restore return values - lw $v1, 8($sp) - l.d $f0, 0($sp) - jalr $zero, $t0 # return - addiu $sp, $sp, 16 # remove temp storage from stack - .cfi_adjust_cfa_offset -16 + lw $v0, ARG_SLOT_SIZE+12($sp) # restore return values + lw $v1, ARG_SLOT_SIZE+8($sp) + l.d $f0, ARG_SLOT_SIZE($sp) + jalr $zero, $t9 # return + addiu $sp, $sp, ARG_SLOT_SIZE+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE+16 # restore stack + .cfi_adjust_cfa_offset -(ARG_SLOT_SIZE+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE+16) END art_quick_instrumentation_exit /* diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 8cb95f1ab6..697bf003ea 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1504,11 +1504,11 @@ art_quick_instrumentation_exit: move $a1, $t0 # pass $sp jal artInstrumentationMethodExitFromCode # (Thread*, SP, gpr_res, fpr_res) move $a0, rSELF # pass Thread::Current - move $t0, $v0 # set aside returned link register + move $t9, $v0 # set aside returned link register move $ra, $v1 # set link register for deoptimization ld $v0, 0($sp) # restore return values l.d $f0, 8($sp) - jalr $zero, $t0 # return + jalr $zero, $t9 # return daddiu $sp, $sp, 16+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE # 16 bytes of saved values + ref_only callee save frame .cfi_adjust_cfa_offset -(16+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE) END art_quick_instrumentation_exit diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 4104509608..d400010e4b 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -220,7 +220,6 @@ void CommonRuntimeTest::SetUp() { std::string min_heap_string(StringPrintf("-Xms%zdm", gc::Heap::kDefaultInitialSize / MB)); std::string max_heap_string(StringPrintf("-Xmx%zdm", gc::Heap::kDefaultMaximumSize / MB)); - callbacks_.reset(new NoopCompilerCallbacks()); RuntimeOptions options; std::string boot_class_path_string = "-Xbootclasspath:" + GetLibCoreDexFileName(); @@ -228,9 +227,16 @@ void CommonRuntimeTest::SetUp() { options.push_back(std::make_pair("-Xcheck:jni", nullptr)); options.push_back(std::make_pair(min_heap_string, nullptr)); options.push_back(std::make_pair(max_heap_string, nullptr)); - options.push_back(std::make_pair("compilercallbacks", callbacks_.get())); + + callbacks_.reset(new NoopCompilerCallbacks()); + SetUpRuntimeOptions(&options); + // Install compiler-callbacks if SetupRuntimeOptions hasn't deleted them. + if (callbacks_.get() != nullptr) { + options.push_back(std::make_pair("compilercallbacks", callbacks_.get())); + } + PreRuntimeCreate(); if (!Runtime::Create(options, false)) { LOG(FATAL) << "Failed to create runtime"; diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index a29487fff4..5fbc2ee680 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -140,10 +140,11 @@ class CommonRuntimeTest : public testing::Test { // Get the first dex file from a PathClassLoader. Will abort if it is null. const DexFile* GetFirstDexFile(jobject jclass_loader); + std::unique_ptr<CompilerCallbacks> callbacks_; + private: static std::string GetCoreFileLocation(const char* suffix); - std::unique_ptr<CompilerCallbacks> callbacks_; std::vector<std::unique_ptr<const DexFile>> loaded_dex_files_; }; diff --git a/runtime/leb128.h b/runtime/leb128.h index dfb42b8e05..d36b690aa1 100644 --- a/runtime/leb128.h +++ b/runtime/leb128.h @@ -124,6 +124,18 @@ static inline uint8_t* EncodeUnsignedLeb128(uint8_t* dest, uint32_t value) { return dest; } +template<typename Allocator> +static inline void EncodeUnsignedLeb128(std::vector<uint8_t, Allocator>* dest, uint32_t value) { + uint8_t out = value & 0x7f; + value >>= 7; + while (value != 0) { + dest->push_back(out | 0x80); + out = value & 0x7f; + value >>= 7; + } + dest->push_back(out); +} + static inline uint8_t* EncodeSignedLeb128(uint8_t* dest, int32_t value) { uint32_t extra_bits = static_cast<uint32_t>(value ^ (value >> 31)) >> 6; uint8_t out = value & 0x7f; @@ -137,6 +149,19 @@ static inline uint8_t* EncodeSignedLeb128(uint8_t* dest, int32_t value) { return dest; } +template<typename Allocator> +static inline void EncodeSignedLeb128(std::vector<uint8_t, Allocator>* dest, int32_t value) { + uint32_t extra_bits = static_cast<uint32_t>(value ^ (value >> 31)) >> 6; + uint8_t out = value & 0x7f; + while (extra_bits != 0u) { + dest->push_back(out | 0x80); + value >>= 7; + out = value & 0x7f; + extra_bits >>= 7; + } + dest->push_back(out); +} + // An encoder that pushed uint32_t data onto the given std::vector. class Leb128Encoder { public: @@ -149,14 +174,7 @@ class Leb128Encoder { } void PushBackUnsigned(uint32_t value) { - uint8_t out = value & 0x7f; - value >>= 7; - while (value != 0) { - data_->push_back(out | 0x80); - out = value & 0x7f; - value >>= 7; - } - data_->push_back(out); + EncodeUnsignedLeb128(data_, value); } template<typename It> @@ -167,15 +185,7 @@ class Leb128Encoder { } void PushBackSigned(int32_t value) { - uint32_t extra_bits = static_cast<uint32_t>(value ^ (value >> 31)) >> 6; - uint8_t out = value & 0x7f; - while (extra_bits != 0u) { - data_->push_back(out | 0x80); - value >>= 7; - out = value & 0x7f; - extra_bits >>= 7; - } - data_->push_back(out); + EncodeSignedLeb128(data_, value); } template<typename It> diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index b2798d3e8b..0422fcda1a 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -27,6 +27,7 @@ #include "class_linker.h" #include "common_runtime_test.h" +#include "compiler_callbacks.h" #include "mem_map.h" #include "os.h" #include "thread-inl.h" @@ -77,11 +78,7 @@ class OatFileAssistantTest : public CommonRuntimeTest { nullptr)); // Make sure compilercallbacks are not set so that relocation will be // enabled. - for (std::pair<std::string, const void*>& pair : *options) { - if (pair.first == "compilercallbacks") { - pair.second = nullptr; - } - } + callbacks_.reset(); } virtual void PreRuntimeCreate() { diff --git a/test/467-regalloc-pair/expected.txt b/test/467-regalloc-pair/expected.txt new file mode 100644 index 0000000000..da39d9da2b --- /dev/null +++ b/test/467-regalloc-pair/expected.txt @@ -0,0 +1 @@ +In interface diff --git a/test/467-regalloc-pair/info.txt b/test/467-regalloc-pair/info.txt new file mode 100644 index 0000000000..882a29c029 --- /dev/null +++ b/test/467-regalloc-pair/info.txt @@ -0,0 +1,2 @@ +Regression test for optimizing's register allocator +that used to trip when compiling TestCase.testCase on x86. diff --git a/test/467-regalloc-pair/smali/TestCase.smali b/test/467-regalloc-pair/smali/TestCase.smali new file mode 100644 index 0000000000..a3101feb12 --- /dev/null +++ b/test/467-regalloc-pair/smali/TestCase.smali @@ -0,0 +1,59 @@ +# 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. + +.class public LTestCase; + +.super Ljava/lang/Object; + +.method public static testCase([BLMain;)V + .registers 12 + const/4 v2, 0 + array-length v0, v10 + div-int/lit8 v0, v0, 7 + invoke-static {v2, v0}, Ljava/lang/Math;->max(II)I + move-result v7 + move v6, v2 + move v3, v2 + :label5 + if-ge v6, v7, :label1 + const-wide/16 v0, 0 + move-wide v4, v0 + move v1, v2 + move v0, v3 + :label4 + const/4 v3, 6 + if-ge v1, v3, :label2 + const/16 v3, 8 + shl-long/2addr v4, v3 + add-int/lit8 v3, v0, 1 + aget-byte v0, v10, v0 + if-gez v0, :label3 + add-int/lit16 v0, v0, 256 + :label3 + int-to-long v8, v0 + or-long/2addr v4, v8 + add-int/lit8 v0, v1, 1 + move v1, v0 + move v0, v3 + goto :label4 + :label2 + add-int/lit8 v3, v0, 1 + aget-byte v0, v10, v0 + invoke-interface {v11, v4, v5, v0}, LItf;->invokeInterface(JI)V + add-int/lit8 v0, v6, 1 + move v6, v0 + goto :label5 + :label1 + return-void +.end method diff --git a/test/467-regalloc-pair/src/Main.java b/test/467-regalloc-pair/src/Main.java new file mode 100644 index 0000000000..aac07fdc22 --- /dev/null +++ b/test/467-regalloc-pair/src/Main.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; + +interface Itf { + public void invokeInterface(long l, int i); +} + +public class Main implements Itf { + + // Workaround for b/18051191. + class InnerClass {} + + public static void main(String[] args) throws Exception { + Class<?> c = Class.forName("TestCase"); + Method m = c.getMethod("testCase", byte[].class, Main.class); + m.invoke(null, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }, new Main()); + } + + public void invokeInterface(long l, int i) { + System.out.println("In interface"); + } +} diff --git a/test/468-bool-simplifier-regression/expected.txt b/test/468-bool-simplifier-regression/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/468-bool-simplifier-regression/expected.txt diff --git a/test/468-bool-simplifier-regression/info.txt b/test/468-bool-simplifier-regression/info.txt new file mode 100644 index 0000000000..0a465846b1 --- /dev/null +++ b/test/468-bool-simplifier-regression/info.txt @@ -0,0 +1,2 @@ +Regression test for optimizing's boolean simplifier +that used to trip when a boolean value was the input of an If. diff --git a/test/468-bool-simplifier-regression/smali/TestCase.smali b/test/468-bool-simplifier-regression/smali/TestCase.smali new file mode 100644 index 0000000000..f36304d333 --- /dev/null +++ b/test/468-bool-simplifier-regression/smali/TestCase.smali @@ -0,0 +1,32 @@ +# 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. + +.class public LTestCase; + +.super Ljava/lang/Object; + +.field public static value:Z + +.method public static testCase()Z + .registers 2 + sget-boolean v0, LTestCase;->value:Z + const/4 v1, 1 + if-eq v0, v1, :label1 + const/4 v1, 1 + goto :label2 + :label1 + const/4 v1, 0 + :label2 + return v1 +.end method diff --git a/test/468-bool-simplifier-regression/src/Main.java b/test/468-bool-simplifier-regression/src/Main.java new file mode 100644 index 0000000000..1dd27c9287 --- /dev/null +++ b/test/468-bool-simplifier-regression/src/Main.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.*; + +public class Main { + public static boolean runTest(boolean input) throws Exception { + Class<?> c = Class.forName("TestCase"); + Method m = c.getMethod("testCase"); + Field f = c.getField("value"); + f.set(null, (Boolean) input); + return (Boolean) m.invoke(null); + } + + public static void main(String[] args) throws Exception { + if (runTest(true) != false) { + throw new Error("Expected false, got true"); + } + + if (runTest(false) != true) { + throw new Error("Expected true, got false"); + } + } +} diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index 2c555d51f8..90c01f544c 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -19,7 +19,7 @@ if [ ! -d libcore ]; then exit 1 fi -if [ $ANDROID_SERIAL == 03a79ae90ae5889b ] || [ $ANDROID_SERIAL == HT4CTJT03670 ] || [ $ANDROID_SERIAL == HT49CJT00070 ]; then +if [[ $ANDROID_SERIAL == 03a79ae90ae5889b ]] || [[ $ANDROID_SERIAL == HT4CTJT03670 ]] || [[ $ANDROID_SERIAL == HT49CJT00070 ]]; then echo "Not run because of localhost failures. Investigating." exit 0 fi @@ -40,13 +40,25 @@ image="-Ximage:/data/art-test/core.art" args=$@ debuggee_args="-Xcompiler-option --compiler-backend=Optimizing -Xcompiler-option --debuggable" device_dir="--device-dir=/data/local/tmp" +# We use the art script on target to ensure the runner and the debuggee share the same +# image. +vm_command="--vm-command=$art" +image_compiler_option="" while true; do if [[ "$1" == "--mode=host" ]]; then - art="art" + # Specify bash explicitly since the art script cannot, since it has to run on the device + # with mksh. + art="bash out/host/linux-x86/bin/art" # We force generation of a new image to avoid build-time and run-time classpath differences. image="-Ximage:/system/non/existent" + # We do not need a device directory on host. device_dir="" + # Vogar knows which VM to use on host. + vm_command="" + # We only compile the image on the host. Note that not providing this option + # puts us below the adb command limit for vogar. + image_compiler_option="--vm-arg -Ximage-compiler-option --vm-arg --debuggable" shift elif [[ $1 == -Ximage:* ]]; then image="$1" @@ -59,16 +71,16 @@ while true; do done # Run the tests using vogar. -vogar --vm-command=$art \ +vogar $vm_command \ --vm-arg $image \ --verbose \ $args \ $device_dir \ - --vm-arg -Ximage-compiler-option \ - --vm-arg --debuggable \ + $image_compiler_option \ --timeout 600 \ --vm-arg -Djpda.settings.verbose=true \ --vm-arg -Djpda.settings.syncPort=34016 \ + --vm-arg -Djpda.settings.transportAddress=127.0.0.1:55107 \ --vm-arg -Djpda.settings.debuggeeJavaPath="$art $image $debuggee_args" \ --classpath $test_jar \ --classpath $junit_jar \ |