Merge "Ensure MemoryRegion::Load & MemoryRegion::Store are word-aligned."
diff --git a/Android.mk b/Android.mk
index 216e865..9360355 100644
--- a/Android.mk
+++ b/Android.mk
@@ -42,27 +42,7 @@
.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 c5669c0..1a4c30c 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -188,6 +188,7 @@
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 257406a..1d0aad5 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -180,7 +180,6 @@
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 9cffbc8..d7b210d 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -78,7 +78,6 @@
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 0000000..cc4ef8f
--- /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 0000000..6de45f5
--- /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 0000000..f34acee
--- /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 0000000..4b7d8d9
--- /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 0000000..0456e85
--- /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 0000000..d4e83f8
--- /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 0000000..fa666df
--- /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 0000000..d8e29f0
--- /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 d59ad6c..afd39e8 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -85,9 +85,6 @@
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 e9ca042..be432c5 100644
--- a/compiler/optimizing/boolean_simplifier.cc
+++ b/compiler/optimizing/boolean_simplifier.cc
@@ -59,7 +59,8 @@
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 @@
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 0f79d18..1f95041 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -2087,16 +2087,32 @@
}
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 @@
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 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 @@
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));
+ // 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>());
+
+ codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pIdivmod), rem, rem->GetDexPc(), nullptr);
+ }
break;
}
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 4b990f1..2c17a67 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -49,7 +49,8 @@
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 36cf856..628a844 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -191,8 +191,10 @@
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 0f6978d..33176f0 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -903,8 +903,6 @@
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 5f78933..72d303c 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1030,8 +1030,6 @@
}
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 9cc77c6..10f6e1d 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 6b7e4ae..384737f 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -1196,8 +1196,6 @@
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 05ea906..736cea8 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -1014,8 +1014,6 @@
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 5ce73ba..b2f9c65 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -583,8 +583,13 @@
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 22ec2a5..b97a667 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -28,6 +28,7 @@
kAttemptCompilation = 0,
kCompiledBaseline,
kCompiledOptimized,
+ kCompiledQuick,
kInlinedInvoke,
kNotCompiledUnsupportedIsa,
kNotCompiledPathological,
@@ -65,16 +66,22 @@
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 @@
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 cecc210..cf38bd3 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -213,7 +213,7 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
}
// 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 @@
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 fcc6112..717be75 100644
--- a/compiler/optimizing/register_allocator.h
+++ b/compiler/optimizing/register_allocator.h
@@ -144,6 +144,13 @@
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 56ccd71..0f3973e 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -318,6 +318,8 @@
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 b57029d..bc78dc2 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -180,6 +180,15 @@
// 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 @@
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 16f0e70..0c2250e 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -1094,7 +1094,7 @@
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 @@
.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 8cb95f1..697bf00 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1504,11 +1504,11 @@
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 4104509..d400010 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -220,7 +220,6 @@
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 @@
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 a29487f..5fbc2ee 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -140,10 +140,11 @@
// 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 dfb42b8..d36b690 100644
--- a/runtime/leb128.h
+++ b/runtime/leb128.h
@@ -124,6 +124,18 @@
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 @@
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 @@
}
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 @@
}
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 b2798d3..0422fcd 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 @@
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 0000000..da39d9d
--- /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 0000000..882a29c
--- /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 0000000..a3101fe
--- /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 0000000..aac07fd
--- /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 0000000..e69de29
--- /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 0000000..0a46584
--- /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 0000000..f36304d
--- /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 0000000..1dd27c9
--- /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 2c555d5..90c01f5 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -19,7 +19,7 @@
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 @@
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 @@
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 \