Merge "ahat: annotate char[] objects with their string values."
diff --git a/Android.mk b/Android.mk
index 4f73127..2e05d33 100644
--- a/Android.mk
+++ b/Android.mk
@@ -547,3 +547,5 @@
art_dont_bother :=
art_test_bother :=
TEST_ART_TARGET_SYNC_DEPS :=
+
+include $(art_path)/runtime/openjdkjvm/Android.mk
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index e3f0c24..704d69a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -239,13 +239,13 @@
runtime/proxy_test.cc \
runtime/reflection_test.cc \
compiler/compiled_method_test.cc \
+ compiler/debug/dwarf/dwarf_test.cc \
compiler/dex/gvn_dead_code_elimination_test.cc \
compiler/dex/global_value_numbering_test.cc \
compiler/dex/local_value_numbering_test.cc \
compiler/dex/mir_graph_test.cc \
compiler/dex/mir_optimization_test.cc \
compiler/dex/type_inference_test.cc \
- compiler/dwarf/dwarf_test.cc \
compiler/driver/compiled_method_storage_test.cc \
compiler/driver/compiler_driver_test.cc \
compiler/elf_writer_test.cc \
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 87eff82..b164942 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -20,6 +20,7 @@
LIBART_COMPILER_SRC_FILES := \
compiled_method.cc \
+ debug/elf_debug_writer.cc \
dex/global_value_numbering.cc \
dex/gvn_dead_code_elimination.cc \
dex/local_value_numbering.cc \
@@ -105,7 +106,6 @@
utils/swap_space.cc \
compiler.cc \
elf_writer.cc \
- elf_writer_debug.cc \
elf_writer_quick.cc \
image_writer.cc \
oat_writer.cc \
diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h
index 508b04a..230cb9a 100644
--- a/compiler/cfi_test.h
+++ b/compiler/cfi_test.h
@@ -22,9 +22,9 @@
#include <sstream>
#include "arch/instruction_set.h"
-#include "dwarf/dwarf_constants.h"
-#include "dwarf/dwarf_test.h"
-#include "dwarf/headers.h"
+#include "debug/dwarf/dwarf_constants.h"
+#include "debug/dwarf/dwarf_test.h"
+#include "debug/dwarf/headers.h"
#include "disassembler/disassembler.h"
#include "gtest/gtest.h"
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index 7a93613..5887620 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -283,11 +283,13 @@
static_assert(sizeof(element_offset_) == sizeof(cmp1_), "needed by relational operators");
};
union {
- uint32_t cmp2_; // Used for relational operators.
+ // Note: To avoid uninitialized padding on 64-bit systems, we use `size_t` for `cmp2_`.
+ // This allows a hashing function to treat an array of linker patches as raw memory.
+ size_t cmp2_; // Used for relational operators.
// Literal offset of the insn loading PC (same as literal_offset if it's the same insn,
// may be different if the PC-relative addressing needs multiple insns).
uint32_t pc_insn_offset_;
- static_assert(sizeof(pc_insn_offset_) == sizeof(cmp2_), "needed by relational operators");
+ static_assert(sizeof(pc_insn_offset_) <= sizeof(cmp2_), "needed by relational operators");
};
friend bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs);
diff --git a/compiler/compiler.h b/compiler/compiler.h
index 3a9ce1b..97c60de 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -64,7 +64,8 @@
virtual bool JitCompile(Thread* self ATTRIBUTE_UNUSED,
jit::JitCodeCache* code_cache ATTRIBUTE_UNUSED,
- ArtMethod* method ATTRIBUTE_UNUSED)
+ ArtMethod* method ATTRIBUTE_UNUSED,
+ bool osr ATTRIBUTE_UNUSED)
SHARED_REQUIRES(Locks::mutator_lock_) {
return false;
}
diff --git a/compiler/dwarf/debug_abbrev_writer.h b/compiler/debug/dwarf/debug_abbrev_writer.h
similarity index 92%
rename from compiler/dwarf/debug_abbrev_writer.h
rename to compiler/debug/dwarf/debug_abbrev_writer.h
index 71367e8..0fc843c 100644
--- a/compiler/dwarf/debug_abbrev_writer.h
+++ b/compiler/debug/dwarf/debug_abbrev_writer.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_DWARF_DEBUG_ABBREV_WRITER_H_
-#define ART_COMPILER_DWARF_DEBUG_ABBREV_WRITER_H_
+#ifndef ART_COMPILER_DEBUG_DWARF_DEBUG_ABBREV_WRITER_H_
+#define ART_COMPILER_DEBUG_DWARF_DEBUG_ABBREV_WRITER_H_
#include <cstdint>
#include <type_traits>
@@ -23,8 +23,8 @@
#include "base/casts.h"
#include "base/stl_util.h"
-#include "dwarf/dwarf_constants.h"
-#include "dwarf/writer.h"
+#include "debug/dwarf/dwarf_constants.h"
+#include "debug/dwarf/writer.h"
#include "leb128.h"
namespace art {
@@ -95,4 +95,4 @@
} // namespace dwarf
} // namespace art
-#endif // ART_COMPILER_DWARF_DEBUG_ABBREV_WRITER_H_
+#endif // ART_COMPILER_DEBUG_DWARF_DEBUG_ABBREV_WRITER_H_
diff --git a/compiler/dwarf/debug_frame_opcode_writer.h b/compiler/debug/dwarf/debug_frame_opcode_writer.h
similarity index 96%
rename from compiler/dwarf/debug_frame_opcode_writer.h
rename to compiler/debug/dwarf/debug_frame_opcode_writer.h
index f74f37c..7c75c9b 100644
--- a/compiler/dwarf/debug_frame_opcode_writer.h
+++ b/compiler/debug/dwarf/debug_frame_opcode_writer.h
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
-#define ART_COMPILER_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
+#ifndef ART_COMPILER_DEBUG_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
+#define ART_COMPILER_DEBUG_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
#include "base/bit_utils.h"
-#include "dwarf/dwarf_constants.h"
-#include "dwarf/register.h"
-#include "dwarf/writer.h"
+#include "debug/dwarf/dwarf_constants.h"
+#include "debug/dwarf/register.h"
+#include "debug/dwarf/writer.h"
namespace art {
namespace dwarf {
@@ -338,4 +338,4 @@
} // namespace dwarf
} // namespace art
-#endif // ART_COMPILER_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
+#endif // ART_COMPILER_DEBUG_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_
diff --git a/compiler/dwarf/debug_info_entry_writer.h b/compiler/debug/dwarf/debug_info_entry_writer.h
similarity index 95%
rename from compiler/dwarf/debug_info_entry_writer.h
rename to compiler/debug/dwarf/debug_info_entry_writer.h
index 1e29859..85f021e 100644
--- a/compiler/dwarf/debug_info_entry_writer.h
+++ b/compiler/debug/dwarf/debug_info_entry_writer.h
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_DWARF_DEBUG_INFO_ENTRY_WRITER_H_
-#define ART_COMPILER_DWARF_DEBUG_INFO_ENTRY_WRITER_H_
+#ifndef ART_COMPILER_DEBUG_DWARF_DEBUG_INFO_ENTRY_WRITER_H_
+#define ART_COMPILER_DEBUG_DWARF_DEBUG_INFO_ENTRY_WRITER_H_
#include <cstdint>
#include <unordered_map>
#include "base/casts.h"
-#include "dwarf/debug_abbrev_writer.h"
-#include "dwarf/dwarf_constants.h"
-#include "dwarf/expression.h"
-#include "dwarf/writer.h"
+#include "debug/dwarf/debug_abbrev_writer.h"
+#include "debug/dwarf/dwarf_constants.h"
+#include "debug/dwarf/expression.h"
+#include "debug/dwarf/writer.h"
#include "leb128.h"
namespace art {
@@ -225,4 +225,4 @@
} // namespace dwarf
} // namespace art
-#endif // ART_COMPILER_DWARF_DEBUG_INFO_ENTRY_WRITER_H_
+#endif // ART_COMPILER_DEBUG_DWARF_DEBUG_INFO_ENTRY_WRITER_H_
diff --git a/compiler/dwarf/debug_line_opcode_writer.h b/compiler/debug/dwarf/debug_line_opcode_writer.h
similarity index 96%
rename from compiler/dwarf/debug_line_opcode_writer.h
rename to compiler/debug/dwarf/debug_line_opcode_writer.h
index 201f0b4..58502a3 100644
--- a/compiler/dwarf/debug_line_opcode_writer.h
+++ b/compiler/debug/dwarf/debug_line_opcode_writer.h
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
-#define ART_COMPILER_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
+#ifndef ART_COMPILER_DEBUG_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
+#define ART_COMPILER_DEBUG_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
#include <cstdint>
-#include "dwarf/dwarf_constants.h"
-#include "dwarf/writer.h"
+#include "debug/dwarf/dwarf_constants.h"
+#include "debug/dwarf/writer.h"
namespace art {
namespace dwarf {
@@ -252,4 +252,4 @@
} // namespace dwarf
} // namespace art
-#endif // ART_COMPILER_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
+#endif // ART_COMPILER_DEBUG_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
diff --git a/compiler/dwarf/dwarf_constants.h b/compiler/debug/dwarf/dwarf_constants.h
similarity index 98%
rename from compiler/dwarf/dwarf_constants.h
rename to compiler/debug/dwarf/dwarf_constants.h
index 0d7951b..96f805e 100644
--- a/compiler/dwarf/dwarf_constants.h
+++ b/compiler/debug/dwarf/dwarf_constants.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_DWARF_DWARF_CONSTANTS_H_
-#define ART_COMPILER_DWARF_DWARF_CONSTANTS_H_
+#ifndef ART_COMPILER_DEBUG_DWARF_DWARF_CONSTANTS_H_
+#define ART_COMPILER_DEBUG_DWARF_DWARF_CONSTANTS_H_
namespace art {
namespace dwarf {
@@ -691,4 +691,4 @@
} // namespace dwarf
} // namespace art
-#endif // ART_COMPILER_DWARF_DWARF_CONSTANTS_H_
+#endif // ART_COMPILER_DEBUG_DWARF_DWARF_CONSTANTS_H_
diff --git a/compiler/dwarf/dwarf_test.cc b/compiler/debug/dwarf/dwarf_test.cc
similarity index 98%
rename from compiler/dwarf/dwarf_test.cc
rename to compiler/debug/dwarf/dwarf_test.cc
index 3237311..e455d0d 100644
--- a/compiler/dwarf/dwarf_test.cc
+++ b/compiler/debug/dwarf/dwarf_test.cc
@@ -16,11 +16,11 @@
#include "dwarf_test.h"
-#include "dwarf/dwarf_constants.h"
-#include "dwarf/debug_frame_opcode_writer.h"
-#include "dwarf/debug_info_entry_writer.h"
-#include "dwarf/debug_line_opcode_writer.h"
-#include "dwarf/headers.h"
+#include "debug/dwarf/debug_frame_opcode_writer.h"
+#include "debug/dwarf/debug_info_entry_writer.h"
+#include "debug/dwarf/debug_line_opcode_writer.h"
+#include "debug/dwarf/dwarf_constants.h"
+#include "debug/dwarf/headers.h"
#include "gtest/gtest.h"
namespace art {
diff --git a/compiler/dwarf/dwarf_test.h b/compiler/debug/dwarf/dwarf_test.h
similarity index 97%
rename from compiler/dwarf/dwarf_test.h
rename to compiler/debug/dwarf/dwarf_test.h
index c3a3ca9..41bfe79 100644
--- a/compiler/dwarf/dwarf_test.h
+++ b/compiler/debug/dwarf/dwarf_test.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_DWARF_DWARF_TEST_H_
-#define ART_COMPILER_DWARF_DWARF_TEST_H_
+#ifndef ART_COMPILER_DEBUG_DWARF_DWARF_TEST_H_
+#define ART_COMPILER_DEBUG_DWARF_DWARF_TEST_H_
#include <cstring>
#include <dirent.h>
@@ -169,4 +169,4 @@
} // namespace dwarf
} // namespace art
-#endif // ART_COMPILER_DWARF_DWARF_TEST_H_
+#endif // ART_COMPILER_DEBUG_DWARF_DWARF_TEST_H_
diff --git a/compiler/dwarf/expression.h b/compiler/debug/dwarf/expression.h
similarity index 93%
rename from compiler/dwarf/expression.h
rename to compiler/debug/dwarf/expression.h
index 1503d03..fafc046 100644
--- a/compiler/dwarf/expression.h
+++ b/compiler/debug/dwarf/expression.h
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_DWARF_EXPRESSION_H_
-#define ART_COMPILER_DWARF_EXPRESSION_H_
+#ifndef ART_COMPILER_DEBUG_DWARF_EXPRESSION_H_
+#define ART_COMPILER_DEBUG_DWARF_EXPRESSION_H_
#include <cstddef>
#include <cstdint>
-#include "dwarf/dwarf_constants.h"
-#include "dwarf/writer.h"
+#include "debug/dwarf/dwarf_constants.h"
+#include "debug/dwarf/writer.h"
namespace art {
namespace dwarf {
@@ -118,4 +118,4 @@
} // namespace dwarf
} // namespace art
-#endif // ART_COMPILER_DWARF_EXPRESSION_H_
+#endif // ART_COMPILER_DEBUG_DWARF_EXPRESSION_H_
diff --git a/compiler/dwarf/headers.h b/compiler/debug/dwarf/headers.h
similarity index 95%
rename from compiler/dwarf/headers.h
rename to compiler/debug/dwarf/headers.h
index 137c566..146d9fd 100644
--- a/compiler/dwarf/headers.h
+++ b/compiler/debug/dwarf/headers.h
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_DWARF_HEADERS_H_
-#define ART_COMPILER_DWARF_HEADERS_H_
+#ifndef ART_COMPILER_DEBUG_DWARF_HEADERS_H_
+#define ART_COMPILER_DEBUG_DWARF_HEADERS_H_
#include <cstdint>
-#include "dwarf/debug_frame_opcode_writer.h"
-#include "dwarf/debug_info_entry_writer.h"
-#include "dwarf/debug_line_opcode_writer.h"
-#include "dwarf/dwarf_constants.h"
-#include "dwarf/register.h"
-#include "dwarf/writer.h"
+#include "debug/dwarf/debug_frame_opcode_writer.h"
+#include "debug/dwarf/debug_info_entry_writer.h"
+#include "debug/dwarf/debug_line_opcode_writer.h"
+#include "debug/dwarf/dwarf_constants.h"
+#include "debug/dwarf/register.h"
+#include "debug/dwarf/writer.h"
#include "utils/array_ref.h"
namespace art {
@@ -204,4 +204,4 @@
} // namespace dwarf
} // namespace art
-#endif // ART_COMPILER_DWARF_HEADERS_H_
+#endif // ART_COMPILER_DEBUG_DWARF_HEADERS_H_
diff --git a/compiler/dwarf/register.h b/compiler/debug/dwarf/register.h
similarity index 93%
rename from compiler/dwarf/register.h
rename to compiler/debug/dwarf/register.h
index aa3070a..24bacac 100644
--- a/compiler/dwarf/register.h
+++ b/compiler/debug/dwarf/register.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_DWARF_REGISTER_H_
-#define ART_COMPILER_DWARF_REGISTER_H_
+#ifndef ART_COMPILER_DEBUG_DWARF_REGISTER_H_
+#define ART_COMPILER_DEBUG_DWARF_REGISTER_H_
namespace art {
namespace dwarf {
@@ -59,4 +59,4 @@
} // namespace dwarf
} // namespace art
-#endif // ART_COMPILER_DWARF_REGISTER_H_
+#endif // ART_COMPILER_DEBUG_DWARF_REGISTER_H_
diff --git a/compiler/dwarf/writer.h b/compiler/debug/dwarf/writer.h
similarity index 97%
rename from compiler/dwarf/writer.h
rename to compiler/debug/dwarf/writer.h
index 74acf07..95912ad 100644
--- a/compiler/dwarf/writer.h
+++ b/compiler/debug/dwarf/writer.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_DWARF_WRITER_H_
-#define ART_COMPILER_DWARF_WRITER_H_
+#ifndef ART_COMPILER_DEBUG_DWARF_WRITER_H_
+#define ART_COMPILER_DEBUG_DWARF_WRITER_H_
#include <type_traits>
#include <vector>
@@ -179,4 +179,4 @@
} // namespace dwarf
} // namespace art
-#endif // ART_COMPILER_DWARF_WRITER_H_
+#endif // ART_COMPILER_DEBUG_DWARF_WRITER_H_
diff --git a/compiler/debug/elf_compilation_unit.h b/compiler/debug/elf_compilation_unit.h
new file mode 100644
index 0000000..f725f45
--- /dev/null
+++ b/compiler/debug/elf_compilation_unit.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DEBUG_ELF_COMPILATION_UNIT_H_
+#define ART_COMPILER_DEBUG_ELF_COMPILATION_UNIT_H_
+
+#include <vector>
+
+#include "debug/method_debug_info.h"
+
+namespace art {
+namespace debug {
+
+struct ElfCompilationUnit {
+ std::vector<const MethodDebugInfo*> methods;
+ size_t debug_line_offset = 0;
+ uintptr_t low_pc = std::numeric_limits<uintptr_t>::max();
+ uintptr_t high_pc = 0;
+};
+
+} // namespace debug
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_ELF_COMPILATION_UNIT_H_
+
diff --git a/compiler/debug/elf_debug_frame_writer.h b/compiler/debug/elf_debug_frame_writer.h
new file mode 100644
index 0000000..f6d9b16
--- /dev/null
+++ b/compiler/debug/elf_debug_frame_writer.h
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DEBUG_ELF_DEBUG_FRAME_WRITER_H_
+#define ART_COMPILER_DEBUG_ELF_DEBUG_FRAME_WRITER_H_
+
+#include <vector>
+
+#include "arch/instruction_set.h"
+#include "debug/dwarf/debug_frame_opcode_writer.h"
+#include "debug/dwarf/dwarf_constants.h"
+#include "debug/dwarf/headers.h"
+#include "debug/method_debug_info.h"
+#include "elf_builder.h"
+
+namespace art {
+namespace debug {
+
+static void WriteCIE(InstructionSet isa,
+ dwarf::CFIFormat format,
+ std::vector<uint8_t>* buffer) {
+ using Reg = dwarf::Reg;
+ // Scratch registers should be marked as undefined. This tells the
+ // debugger that its value in the previous frame is not recoverable.
+ bool is64bit = Is64BitInstructionSet(isa);
+ switch (isa) {
+ case kArm:
+ case kThumb2: {
+ dwarf::DebugFrameOpCodeWriter<> opcodes;
+ opcodes.DefCFA(Reg::ArmCore(13), 0); // R13(SP).
+ // core registers.
+ for (int reg = 0; reg < 13; reg++) {
+ if (reg < 4 || reg == 12) {
+ opcodes.Undefined(Reg::ArmCore(reg));
+ } else {
+ opcodes.SameValue(Reg::ArmCore(reg));
+ }
+ }
+ // fp registers.
+ for (int reg = 0; reg < 32; reg++) {
+ if (reg < 16) {
+ opcodes.Undefined(Reg::ArmFp(reg));
+ } else {
+ opcodes.SameValue(Reg::ArmFp(reg));
+ }
+ }
+ auto return_reg = Reg::ArmCore(14); // R14(LR).
+ WriteCIE(is64bit, return_reg, opcodes, format, buffer);
+ return;
+ }
+ case kArm64: {
+ dwarf::DebugFrameOpCodeWriter<> opcodes;
+ opcodes.DefCFA(Reg::Arm64Core(31), 0); // R31(SP).
+ // core registers.
+ for (int reg = 0; reg < 30; reg++) {
+ if (reg < 8 || reg == 16 || reg == 17) {
+ opcodes.Undefined(Reg::Arm64Core(reg));
+ } else {
+ opcodes.SameValue(Reg::Arm64Core(reg));
+ }
+ }
+ // fp registers.
+ for (int reg = 0; reg < 32; reg++) {
+ if (reg < 8 || reg >= 16) {
+ opcodes.Undefined(Reg::Arm64Fp(reg));
+ } else {
+ opcodes.SameValue(Reg::Arm64Fp(reg));
+ }
+ }
+ auto return_reg = Reg::Arm64Core(30); // R30(LR).
+ WriteCIE(is64bit, return_reg, opcodes, format, buffer);
+ return;
+ }
+ case kMips:
+ case kMips64: {
+ dwarf::DebugFrameOpCodeWriter<> opcodes;
+ opcodes.DefCFA(Reg::MipsCore(29), 0); // R29(SP).
+ // core registers.
+ for (int reg = 1; reg < 26; reg++) {
+ if (reg < 16 || reg == 24 || reg == 25) { // AT, V*, A*, T*.
+ opcodes.Undefined(Reg::MipsCore(reg));
+ } else {
+ opcodes.SameValue(Reg::MipsCore(reg));
+ }
+ }
+ // fp registers.
+ for (int reg = 0; reg < 32; reg++) {
+ if (reg < 24) {
+ opcodes.Undefined(Reg::Mips64Fp(reg));
+ } else {
+ opcodes.SameValue(Reg::Mips64Fp(reg));
+ }
+ }
+ auto return_reg = Reg::MipsCore(31); // R31(RA).
+ WriteCIE(is64bit, return_reg, opcodes, format, buffer);
+ return;
+ }
+ case kX86: {
+ // FIXME: Add fp registers once libunwind adds support for them. Bug: 20491296
+ constexpr bool generate_opcodes_for_x86_fp = false;
+ dwarf::DebugFrameOpCodeWriter<> opcodes;
+ opcodes.DefCFA(Reg::X86Core(4), 4); // R4(ESP).
+ opcodes.Offset(Reg::X86Core(8), -4); // R8(EIP).
+ // core registers.
+ for (int reg = 0; reg < 8; reg++) {
+ if (reg <= 3) {
+ opcodes.Undefined(Reg::X86Core(reg));
+ } else if (reg == 4) {
+ // Stack pointer.
+ } else {
+ opcodes.SameValue(Reg::X86Core(reg));
+ }
+ }
+ // fp registers.
+ if (generate_opcodes_for_x86_fp) {
+ for (int reg = 0; reg < 8; reg++) {
+ opcodes.Undefined(Reg::X86Fp(reg));
+ }
+ }
+ auto return_reg = Reg::X86Core(8); // R8(EIP).
+ WriteCIE(is64bit, return_reg, opcodes, format, buffer);
+ return;
+ }
+ case kX86_64: {
+ dwarf::DebugFrameOpCodeWriter<> opcodes;
+ opcodes.DefCFA(Reg::X86_64Core(4), 8); // R4(RSP).
+ opcodes.Offset(Reg::X86_64Core(16), -8); // R16(RIP).
+ // core registers.
+ for (int reg = 0; reg < 16; reg++) {
+ if (reg == 4) {
+ // Stack pointer.
+ } else if (reg < 12 && reg != 3 && reg != 5) { // except EBX and EBP.
+ opcodes.Undefined(Reg::X86_64Core(reg));
+ } else {
+ opcodes.SameValue(Reg::X86_64Core(reg));
+ }
+ }
+ // fp registers.
+ for (int reg = 0; reg < 16; reg++) {
+ if (reg < 12) {
+ opcodes.Undefined(Reg::X86_64Fp(reg));
+ } else {
+ opcodes.SameValue(Reg::X86_64Fp(reg));
+ }
+ }
+ auto return_reg = Reg::X86_64Core(16); // R16(RIP).
+ WriteCIE(is64bit, return_reg, opcodes, format, buffer);
+ return;
+ }
+ case kNone:
+ break;
+ }
+ LOG(FATAL) << "Cannot write CIE frame for ISA " << isa;
+ UNREACHABLE();
+}
+
+template<typename ElfTypes>
+void WriteCFISection(ElfBuilder<ElfTypes>* builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos,
+ dwarf::CFIFormat format,
+ bool write_oat_patches) {
+ CHECK(format == dwarf::DW_DEBUG_FRAME_FORMAT || format == dwarf::DW_EH_FRAME_FORMAT);
+ typedef typename ElfTypes::Addr Elf_Addr;
+
+ if (method_infos.empty()) {
+ return;
+ }
+
+ std::vector<uint32_t> binary_search_table;
+ std::vector<uintptr_t> patch_locations;
+ if (format == dwarf::DW_EH_FRAME_FORMAT) {
+ binary_search_table.reserve(2 * method_infos.size());
+ } else {
+ patch_locations.reserve(method_infos.size());
+ }
+
+ // The methods can be written in any order.
+ // Let's therefore sort them in the lexicographical order of the opcodes.
+ // This has no effect on its own. However, if the final .debug_frame section is
+ // compressed it reduces the size since similar opcodes sequences are grouped.
+ std::vector<const MethodDebugInfo*> sorted_method_infos;
+ sorted_method_infos.reserve(method_infos.size());
+ for (size_t i = 0; i < method_infos.size(); i++) {
+ sorted_method_infos.push_back(&method_infos[i]);
+ }
+ std::sort(
+ sorted_method_infos.begin(),
+ sorted_method_infos.end(),
+ [](const MethodDebugInfo* lhs, const MethodDebugInfo* rhs) {
+ ArrayRef<const uint8_t> l = lhs->compiled_method->GetCFIInfo();
+ ArrayRef<const uint8_t> r = rhs->compiled_method->GetCFIInfo();
+ return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
+ });
+
+ // Write .eh_frame/.debug_frame section.
+ auto* cfi_section = (format == dwarf::DW_DEBUG_FRAME_FORMAT
+ ? builder->GetDebugFrame()
+ : builder->GetEhFrame());
+ {
+ cfi_section->Start();
+ const bool is64bit = Is64BitInstructionSet(builder->GetIsa());
+ const Elf_Addr text_address = builder->GetText()->Exists()
+ ? builder->GetText()->GetAddress()
+ : 0;
+ const Elf_Addr cfi_address = cfi_section->GetAddress();
+ const Elf_Addr cie_address = cfi_address;
+ Elf_Addr buffer_address = cfi_address;
+ std::vector<uint8_t> buffer; // Small temporary buffer.
+ WriteCIE(builder->GetIsa(), format, &buffer);
+ cfi_section->WriteFully(buffer.data(), buffer.size());
+ buffer_address += buffer.size();
+ buffer.clear();
+ for (const MethodDebugInfo* mi : sorted_method_infos) {
+ if (!mi->deduped) { // Only one FDE per unique address.
+ ArrayRef<const uint8_t> opcodes = mi->compiled_method->GetCFIInfo();
+ if (!opcodes.empty()) {
+ const Elf_Addr code_address = text_address + mi->low_pc;
+ if (format == dwarf::DW_EH_FRAME_FORMAT) {
+ binary_search_table.push_back(
+ dchecked_integral_cast<uint32_t>(code_address));
+ binary_search_table.push_back(
+ dchecked_integral_cast<uint32_t>(buffer_address));
+ }
+ WriteFDE(is64bit, cfi_address, cie_address,
+ code_address, mi->high_pc - mi->low_pc,
+ opcodes, format, buffer_address, &buffer,
+ &patch_locations);
+ cfi_section->WriteFully(buffer.data(), buffer.size());
+ buffer_address += buffer.size();
+ buffer.clear();
+ }
+ }
+ }
+ cfi_section->End();
+ }
+
+ if (format == dwarf::DW_EH_FRAME_FORMAT) {
+ auto* header_section = builder->GetEhFrameHdr();
+ header_section->Start();
+ uint32_t header_address = dchecked_integral_cast<int32_t>(header_section->GetAddress());
+ // Write .eh_frame_hdr section.
+ std::vector<uint8_t> buffer;
+ dwarf::Writer<> header(&buffer);
+ header.PushUint8(1); // Version.
+ // Encoding of .eh_frame pointer - libunwind does not honor datarel here,
+ // so we have to use pcrel which means relative to the pointer's location.
+ header.PushUint8(dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4);
+ // Encoding of binary search table size.
+ header.PushUint8(dwarf::DW_EH_PE_udata4);
+ // Encoding of binary search table addresses - libunwind supports only this
+ // specific combination, which means relative to the start of .eh_frame_hdr.
+ header.PushUint8(dwarf::DW_EH_PE_datarel | dwarf::DW_EH_PE_sdata4);
+ // .eh_frame pointer
+ header.PushInt32(cfi_section->GetAddress() - (header_address + 4u));
+ // Binary search table size (number of entries).
+ header.PushUint32(dchecked_integral_cast<uint32_t>(binary_search_table.size()/2));
+ header_section->WriteFully(buffer.data(), buffer.size());
+ // Binary search table.
+ for (size_t i = 0; i < binary_search_table.size(); i++) {
+ // Make addresses section-relative since we know the header address now.
+ binary_search_table[i] -= header_address;
+ }
+ header_section->WriteFully(binary_search_table.data(), binary_search_table.size());
+ header_section->End();
+ } else {
+ if (write_oat_patches) {
+ builder->WritePatches(".debug_frame.oat_patches",
+ ArrayRef<const uintptr_t>(patch_locations));
+ }
+ }
+}
+
+} // namespace debug
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_ELF_DEBUG_FRAME_WRITER_H_
+
diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h
new file mode 100644
index 0000000..eed032f
--- /dev/null
+++ b/compiler/debug/elf_debug_info_writer.h
@@ -0,0 +1,649 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DEBUG_ELF_DEBUG_INFO_WRITER_H_
+#define ART_COMPILER_DEBUG_ELF_DEBUG_INFO_WRITER_H_
+
+#include <map>
+#include <unordered_set>
+#include <vector>
+
+#include "debug/dwarf/debug_abbrev_writer.h"
+#include "debug/dwarf/debug_info_entry_writer.h"
+#include "debug/elf_compilation_unit.h"
+#include "debug/elf_debug_loc_writer.h"
+#include "debug/method_debug_info.h"
+#include "dex_file-inl.h"
+#include "dex_file.h"
+#include "elf_builder.h"
+#include "linear_alloc.h"
+#include "mirror/array.h"
+#include "mirror/class-inl.h"
+#include "mirror/class.h"
+
+namespace art {
+namespace debug {
+
+typedef std::vector<DexFile::LocalInfo> LocalInfos;
+
+static void LocalInfoCallback(void* ctx, const DexFile::LocalInfo& entry) {
+ static_cast<LocalInfos*>(ctx)->push_back(entry);
+}
+
+static std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) {
+ std::vector<const char*> names;
+ if (mi->code_item != nullptr) {
+ const uint8_t* stream = mi->dex_file->GetDebugInfoStream(mi->code_item);
+ if (stream != nullptr) {
+ DecodeUnsignedLeb128(&stream); // line.
+ uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
+ for (uint32_t i = 0; i < parameters_size; ++i) {
+ uint32_t id = DecodeUnsignedLeb128P1(&stream);
+ names.push_back(mi->dex_file->StringDataByIdx(id));
+ }
+ }
+ }
+ return names;
+}
+
+// Helper class to write .debug_info and its supporting sections.
+template<typename ElfTypes>
+class ElfDebugInfoWriter {
+ using Elf_Addr = typename ElfTypes::Addr;
+
+ public:
+ explicit ElfDebugInfoWriter(ElfBuilder<ElfTypes>* builder)
+ : builder_(builder),
+ debug_abbrev_(&debug_abbrev_buffer_) {
+ }
+
+ void Start() {
+ builder_->GetDebugInfo()->Start();
+ }
+
+ void End(bool write_oat_patches) {
+ builder_->GetDebugInfo()->End();
+ if (write_oat_patches) {
+ builder_->WritePatches(".debug_info.oat_patches",
+ ArrayRef<const uintptr_t>(debug_info_patches_));
+ }
+ builder_->WriteSection(".debug_abbrev", &debug_abbrev_buffer_);
+ if (!debug_loc_.empty()) {
+ builder_->WriteSection(".debug_loc", &debug_loc_);
+ }
+ if (!debug_ranges_.empty()) {
+ builder_->WriteSection(".debug_ranges", &debug_ranges_);
+ }
+ }
+
+ private:
+ ElfBuilder<ElfTypes>* builder_;
+ std::vector<uintptr_t> debug_info_patches_;
+ std::vector<uint8_t> debug_abbrev_buffer_;
+ dwarf::DebugAbbrevWriter<> debug_abbrev_;
+ std::vector<uint8_t> debug_loc_;
+ std::vector<uint8_t> debug_ranges_;
+
+ std::unordered_set<const char*> defined_dex_classes_; // For CHECKs only.
+
+ template<typename ElfTypes2>
+ friend class ElfCompilationUnitWriter;
+};
+
+// Helper class to write one compilation unit.
+// It holds helper methods and temporary state.
+template<typename ElfTypes>
+class ElfCompilationUnitWriter {
+ using Elf_Addr = typename ElfTypes::Addr;
+
+ public:
+ explicit ElfCompilationUnitWriter(ElfDebugInfoWriter<ElfTypes>* owner)
+ : owner_(owner),
+ info_(Is64BitInstructionSet(owner_->builder_->GetIsa()), &owner->debug_abbrev_) {
+ }
+
+ void Write(const ElfCompilationUnit& compilation_unit) {
+ CHECK(!compilation_unit.methods.empty());
+ const Elf_Addr text_address = owner_->builder_->GetText()->Exists()
+ ? owner_->builder_->GetText()->GetAddress()
+ : 0;
+ const uintptr_t cu_size = compilation_unit.high_pc - compilation_unit.low_pc;
+ using namespace dwarf; // NOLINT. For easy access to DWARF constants.
+
+ info_.StartTag(DW_TAG_compile_unit);
+ info_.WriteString(DW_AT_producer, "Android dex2oat");
+ info_.WriteData1(DW_AT_language, DW_LANG_Java);
+ info_.WriteString(DW_AT_comp_dir, "$JAVA_SRC_ROOT");
+ info_.WriteAddr(DW_AT_low_pc, text_address + compilation_unit.low_pc);
+ info_.WriteUdata(DW_AT_high_pc, dchecked_integral_cast<uint32_t>(cu_size));
+ info_.WriteSecOffset(DW_AT_stmt_list, compilation_unit.debug_line_offset);
+
+ const char* last_dex_class_desc = nullptr;
+ for (auto mi : compilation_unit.methods) {
+ const DexFile* dex = mi->dex_file;
+ const DexFile::CodeItem* dex_code = mi->code_item;
+ const DexFile::MethodId& dex_method = dex->GetMethodId(mi->dex_method_index);
+ const DexFile::ProtoId& dex_proto = dex->GetMethodPrototype(dex_method);
+ const DexFile::TypeList* dex_params = dex->GetProtoParameters(dex_proto);
+ const char* dex_class_desc = dex->GetMethodDeclaringClassDescriptor(dex_method);
+ const bool is_static = (mi->access_flags & kAccStatic) != 0;
+
+ // Enclose the method in correct class definition.
+ if (last_dex_class_desc != dex_class_desc) {
+ if (last_dex_class_desc != nullptr) {
+ EndClassTag();
+ }
+ // Write reference tag for the class we are about to declare.
+ size_t reference_tag_offset = info_.StartTag(DW_TAG_reference_type);
+ type_cache_.emplace(std::string(dex_class_desc), reference_tag_offset);
+ size_t type_attrib_offset = info_.size();
+ info_.WriteRef4(DW_AT_type, 0);
+ info_.EndTag();
+ // Declare the class that owns this method.
+ size_t class_offset = StartClassTag(dex_class_desc);
+ info_.UpdateUint32(type_attrib_offset, class_offset);
+ info_.WriteFlagPresent(DW_AT_declaration);
+ // Check that each class is defined only once.
+ bool unique = owner_->defined_dex_classes_.insert(dex_class_desc).second;
+ CHECK(unique) << "Redefinition of " << dex_class_desc;
+ last_dex_class_desc = dex_class_desc;
+ }
+
+ int start_depth = info_.Depth();
+ info_.StartTag(DW_TAG_subprogram);
+ WriteName(dex->GetMethodName(dex_method));
+ info_.WriteAddr(DW_AT_low_pc, text_address + mi->low_pc);
+ info_.WriteUdata(DW_AT_high_pc, dchecked_integral_cast<uint32_t>(mi->high_pc-mi->low_pc));
+ std::vector<uint8_t> expr_buffer;
+ Expression expr(&expr_buffer);
+ expr.WriteOpCallFrameCfa();
+ info_.WriteExprLoc(DW_AT_frame_base, expr);
+ WriteLazyType(dex->GetReturnTypeDescriptor(dex_proto));
+
+ // Write parameters. DecodeDebugLocalInfo returns them as well, but it does not
+ // guarantee order or uniqueness so it is safer to iterate over them manually.
+ // DecodeDebugLocalInfo might not also be available if there is no debug info.
+ std::vector<const char*> param_names = GetParamNames(mi);
+ uint32_t arg_reg = 0;
+ if (!is_static) {
+ info_.StartTag(DW_TAG_formal_parameter);
+ WriteName("this");
+ info_.WriteFlagPresent(DW_AT_artificial);
+ WriteLazyType(dex_class_desc);
+ if (dex_code != nullptr) {
+ // Write the stack location of the parameter.
+ const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg;
+ const bool is64bitValue = false;
+ WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc);
+ }
+ arg_reg++;
+ info_.EndTag();
+ }
+ if (dex_params != nullptr) {
+ for (uint32_t i = 0; i < dex_params->Size(); ++i) {
+ info_.StartTag(DW_TAG_formal_parameter);
+ // Parameter names may not be always available.
+ if (i < param_names.size()) {
+ WriteName(param_names[i]);
+ }
+ // Write the type.
+ const char* type_desc = dex->StringByTypeIdx(dex_params->GetTypeItem(i).type_idx_);
+ WriteLazyType(type_desc);
+ const bool is64bitValue = type_desc[0] == 'D' || type_desc[0] == 'J';
+ if (dex_code != nullptr) {
+ // Write the stack location of the parameter.
+ const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg;
+ WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc);
+ }
+ arg_reg += is64bitValue ? 2 : 1;
+ info_.EndTag();
+ }
+ if (dex_code != nullptr) {
+ DCHECK_EQ(arg_reg, dex_code->ins_size_);
+ }
+ }
+
+ // Write local variables.
+ LocalInfos local_infos;
+ if (dex->DecodeDebugLocalInfo(dex_code,
+ is_static,
+ mi->dex_method_index,
+ LocalInfoCallback,
+ &local_infos)) {
+ for (const DexFile::LocalInfo& var : local_infos) {
+ if (var.reg_ < dex_code->registers_size_ - dex_code->ins_size_) {
+ info_.StartTag(DW_TAG_variable);
+ WriteName(var.name_);
+ WriteLazyType(var.descriptor_);
+ bool is64bitValue = var.descriptor_[0] == 'D' || var.descriptor_[0] == 'J';
+ WriteRegLocation(mi, var.reg_, is64bitValue, compilation_unit.low_pc,
+ var.start_address_, var.end_address_);
+ info_.EndTag();
+ }
+ }
+ }
+
+ info_.EndTag();
+ CHECK_EQ(info_.Depth(), start_depth); // Balanced start/end.
+ }
+ if (last_dex_class_desc != nullptr) {
+ EndClassTag();
+ }
+ FinishLazyTypes();
+ CloseNamespacesAboveDepth(0);
+ info_.EndTag(); // DW_TAG_compile_unit
+ CHECK_EQ(info_.Depth(), 0);
+ std::vector<uint8_t> buffer;
+ buffer.reserve(info_.data()->size() + KB);
+ const size_t offset = owner_->builder_->GetDebugInfo()->GetSize();
+ // All compilation units share single table which is at the start of .debug_abbrev.
+ const size_t debug_abbrev_offset = 0;
+ WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_);
+ owner_->builder_->GetDebugInfo()->WriteFully(buffer.data(), buffer.size());
+ }
+
+ void Write(const ArrayRef<mirror::Class*>& types) SHARED_REQUIRES(Locks::mutator_lock_) {
+ using namespace dwarf; // NOLINT. For easy access to DWARF constants.
+
+ info_.StartTag(DW_TAG_compile_unit);
+ info_.WriteString(DW_AT_producer, "Android dex2oat");
+ info_.WriteData1(DW_AT_language, DW_LANG_Java);
+
+ // Base class references to be patched at the end.
+ std::map<size_t, mirror::Class*> base_class_references;
+
+ // Already written declarations or definitions.
+ std::map<mirror::Class*, size_t> class_declarations;
+
+ std::vector<uint8_t> expr_buffer;
+ for (mirror::Class* type : types) {
+ if (type->IsPrimitive()) {
+ // For primitive types the definition and the declaration is the same.
+ if (type->GetPrimitiveType() != Primitive::kPrimVoid) {
+ WriteTypeDeclaration(type->GetDescriptor(nullptr));
+ }
+ } else if (type->IsArrayClass()) {
+ mirror::Class* element_type = type->GetComponentType();
+ uint32_t component_size = type->GetComponentSize();
+ uint32_t data_offset = mirror::Array::DataOffset(component_size).Uint32Value();
+ uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
+
+ CloseNamespacesAboveDepth(0); // Declare in root namespace.
+ info_.StartTag(DW_TAG_array_type);
+ std::string descriptor_string;
+ WriteLazyType(element_type->GetDescriptor(&descriptor_string));
+ WriteLinkageName(type);
+ info_.WriteUdata(DW_AT_data_member_location, data_offset);
+ info_.StartTag(DW_TAG_subrange_type);
+ Expression count_expr(&expr_buffer);
+ count_expr.WriteOpPushObjectAddress();
+ count_expr.WriteOpPlusUconst(length_offset);
+ count_expr.WriteOpDerefSize(4); // Array length is always 32-bit wide.
+ info_.WriteExprLoc(DW_AT_count, count_expr);
+ info_.EndTag(); // DW_TAG_subrange_type.
+ info_.EndTag(); // DW_TAG_array_type.
+ } else if (type->IsInterface()) {
+ // Skip. Variables cannot have an interface as a dynamic type.
+ // We do not expose the interface information to the debugger in any way.
+ } else {
+ std::string descriptor_string;
+ const char* desc = type->GetDescriptor(&descriptor_string);
+ size_t class_offset = StartClassTag(desc);
+ class_declarations.emplace(type, class_offset);
+
+ if (!type->IsVariableSize()) {
+ info_.WriteUdata(DW_AT_byte_size, type->GetObjectSize());
+ }
+
+ WriteLinkageName(type);
+
+ if (type->IsObjectClass()) {
+ // Generate artificial member which is used to get the dynamic type of variable.
+ // The run-time value of this field will correspond to linkage name of some type.
+ // We need to do it only once in j.l.Object since all other types inherit it.
+ info_.StartTag(DW_TAG_member);
+ WriteName(".dynamic_type");
+ WriteLazyType(sizeof(uintptr_t) == 8 ? "J" : "I");
+ info_.WriteFlagPresent(DW_AT_artificial);
+ // Create DWARF expression to get the value of the methods_ field.
+ Expression expr(&expr_buffer);
+ // The address of the object has been implicitly pushed on the stack.
+ // Dereference the klass_ field of Object (32-bit; possibly poisoned).
+ DCHECK_EQ(type->ClassOffset().Uint32Value(), 0u);
+ DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Class>), 4u);
+ expr.WriteOpDerefSize(4);
+ if (kPoisonHeapReferences) {
+ expr.WriteOpNeg();
+ // DWARF stack is pointer sized. Ensure that the high bits are clear.
+ expr.WriteOpConstu(0xFFFFFFFF);
+ expr.WriteOpAnd();
+ }
+ // Add offset to the methods_ field.
+ expr.WriteOpPlusUconst(mirror::Class::MethodsOffset().Uint32Value());
+ // Top of stack holds the location of the field now.
+ info_.WriteExprLoc(DW_AT_data_member_location, expr);
+ info_.EndTag(); // DW_TAG_member.
+ }
+
+ // Base class.
+ mirror::Class* base_class = type->GetSuperClass();
+ if (base_class != nullptr) {
+ info_.StartTag(DW_TAG_inheritance);
+ base_class_references.emplace(info_.size(), base_class);
+ info_.WriteRef4(DW_AT_type, 0);
+ info_.WriteUdata(DW_AT_data_member_location, 0);
+ info_.WriteSdata(DW_AT_accessibility, DW_ACCESS_public);
+ info_.EndTag(); // DW_TAG_inheritance.
+ }
+
+ // Member variables.
+ for (uint32_t i = 0, count = type->NumInstanceFields(); i < count; ++i) {
+ ArtField* field = type->GetInstanceField(i);
+ info_.StartTag(DW_TAG_member);
+ WriteName(field->GetName());
+ WriteLazyType(field->GetTypeDescriptor());
+ info_.WriteUdata(DW_AT_data_member_location, field->GetOffset().Uint32Value());
+ uint32_t access_flags = field->GetAccessFlags();
+ if (access_flags & kAccPublic) {
+ info_.WriteSdata(DW_AT_accessibility, DW_ACCESS_public);
+ } else if (access_flags & kAccProtected) {
+ info_.WriteSdata(DW_AT_accessibility, DW_ACCESS_protected);
+ } else if (access_flags & kAccPrivate) {
+ info_.WriteSdata(DW_AT_accessibility, DW_ACCESS_private);
+ }
+ info_.EndTag(); // DW_TAG_member.
+ }
+
+ if (type->IsStringClass()) {
+ // Emit debug info about an artifical class member for java.lang.String which represents
+ // the first element of the data stored in a string instance. Consumers of the debug
+ // info will be able to read the content of java.lang.String based on the count (real
+ // field) and based on the location of this data member.
+ info_.StartTag(DW_TAG_member);
+ WriteName("value");
+ // We don't support fields with C like array types so we just say its type is java char.
+ WriteLazyType("C"); // char.
+ info_.WriteUdata(DW_AT_data_member_location,
+ mirror::String::ValueOffset().Uint32Value());
+ info_.WriteSdata(DW_AT_accessibility, DW_ACCESS_private);
+ info_.EndTag(); // DW_TAG_member.
+ }
+
+ EndClassTag();
+ }
+ }
+
+ // Write base class declarations.
+ for (const auto& base_class_reference : base_class_references) {
+ size_t reference_offset = base_class_reference.first;
+ mirror::Class* base_class = base_class_reference.second;
+ const auto& it = class_declarations.find(base_class);
+ if (it != class_declarations.end()) {
+ info_.UpdateUint32(reference_offset, it->second);
+ } else {
+ // Declare base class. We can not use the standard WriteLazyType
+ // since we want to avoid the DW_TAG_reference_tag wrapping.
+ std::string tmp_storage;
+ const char* base_class_desc = base_class->GetDescriptor(&tmp_storage);
+ size_t base_class_declaration_offset = StartClassTag(base_class_desc);
+ info_.WriteFlagPresent(DW_AT_declaration);
+ WriteLinkageName(base_class);
+ EndClassTag();
+ class_declarations.emplace(base_class, base_class_declaration_offset);
+ info_.UpdateUint32(reference_offset, base_class_declaration_offset);
+ }
+ }
+
+ FinishLazyTypes();
+ CloseNamespacesAboveDepth(0);
+ info_.EndTag(); // DW_TAG_compile_unit.
+ CHECK_EQ(info_.Depth(), 0);
+ std::vector<uint8_t> buffer;
+ buffer.reserve(info_.data()->size() + KB);
+ const size_t offset = owner_->builder_->GetDebugInfo()->GetSize();
+ // All compilation units share single table which is at the start of .debug_abbrev.
+ const size_t debug_abbrev_offset = 0;
+ WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_);
+ owner_->builder_->GetDebugInfo()->WriteFully(buffer.data(), buffer.size());
+ }
+
+ // Write table into .debug_loc which describes location of dex register.
+ // The dex register might be valid only at some points and it might
+ // move between machine registers and stack.
+ void WriteRegLocation(const MethodDebugInfo* method_info,
+ uint16_t vreg,
+ bool is64bitValue,
+ uint32_t compilation_unit_low_pc,
+ uint32_t dex_pc_low = 0,
+ uint32_t dex_pc_high = 0xFFFFFFFF) {
+ WriteDebugLocEntry(method_info,
+ vreg,
+ is64bitValue,
+ compilation_unit_low_pc,
+ dex_pc_low,
+ dex_pc_high,
+ owner_->builder_->GetIsa(),
+ &info_,
+ &owner_->debug_loc_,
+ &owner_->debug_ranges_);
+ }
+
+ // Linkage name uniquely identifies type.
+ // It is used to determine the dynamic type of objects.
+ // We use the methods_ field of class since it is unique and it is not moved by the GC.
+ void WriteLinkageName(mirror::Class* type) SHARED_REQUIRES(Locks::mutator_lock_) {
+ auto* methods_ptr = type->GetMethodsPtr();
+ if (methods_ptr == nullptr) {
+ // Some types might have no methods. Allocate empty array instead.
+ LinearAlloc* allocator = Runtime::Current()->GetLinearAlloc();
+ void* storage = allocator->Alloc(Thread::Current(), sizeof(LengthPrefixedArray<ArtMethod>));
+ methods_ptr = new (storage) LengthPrefixedArray<ArtMethod>(0);
+ type->SetMethodsPtr(methods_ptr, 0, 0);
+ DCHECK(type->GetMethodsPtr() != nullptr);
+ }
+ char name[32];
+ snprintf(name, sizeof(name), "0x%" PRIXPTR, reinterpret_cast<uintptr_t>(methods_ptr));
+ info_.WriteString(dwarf::DW_AT_linkage_name, name);
+ }
+
+ // Some types are difficult to define as we go since they need
+ // to be enclosed in the right set of namespaces. Therefore we
+ // just define all types lazily at the end of compilation unit.
+ void WriteLazyType(const char* type_descriptor) {
+ if (type_descriptor != nullptr && type_descriptor[0] != 'V') {
+ lazy_types_.emplace(std::string(type_descriptor), info_.size());
+ info_.WriteRef4(dwarf::DW_AT_type, 0);
+ }
+ }
+
+ void FinishLazyTypes() {
+ for (const auto& lazy_type : lazy_types_) {
+ info_.UpdateUint32(lazy_type.second, WriteTypeDeclaration(lazy_type.first));
+ }
+ lazy_types_.clear();
+ }
+
+ private:
+ void WriteName(const char* name) {
+ if (name != nullptr) {
+ info_.WriteString(dwarf::DW_AT_name, name);
+ }
+ }
+
+ // Convert dex type descriptor to DWARF.
+ // Returns offset in the compilation unit.
+ size_t WriteTypeDeclaration(const std::string& desc) {
+ using namespace dwarf; // NOLINT. For easy access to DWARF constants.
+
+ DCHECK(!desc.empty());
+ const auto& it = type_cache_.find(desc);
+ if (it != type_cache_.end()) {
+ return it->second;
+ }
+
+ size_t offset;
+ if (desc[0] == 'L') {
+ // Class type. For example: Lpackage/name;
+ size_t class_offset = StartClassTag(desc.c_str());
+ info_.WriteFlagPresent(DW_AT_declaration);
+ EndClassTag();
+ // Reference to the class type.
+ offset = info_.StartTag(DW_TAG_reference_type);
+ info_.WriteRef(DW_AT_type, class_offset);
+ info_.EndTag();
+ } else if (desc[0] == '[') {
+ // Array type.
+ size_t element_type = WriteTypeDeclaration(desc.substr(1));
+ CloseNamespacesAboveDepth(0); // Declare in root namespace.
+ size_t array_type = info_.StartTag(DW_TAG_array_type);
+ info_.WriteFlagPresent(DW_AT_declaration);
+ info_.WriteRef(DW_AT_type, element_type);
+ info_.EndTag();
+ offset = info_.StartTag(DW_TAG_reference_type);
+ info_.WriteRef4(DW_AT_type, array_type);
+ info_.EndTag();
+ } else {
+ // Primitive types.
+ DCHECK_EQ(desc.size(), 1u);
+
+ const char* name;
+ uint32_t encoding;
+ uint32_t byte_size;
+ switch (desc[0]) {
+ case 'B':
+ name = "byte";
+ encoding = DW_ATE_signed;
+ byte_size = 1;
+ break;
+ case 'C':
+ name = "char";
+ encoding = DW_ATE_UTF;
+ byte_size = 2;
+ break;
+ case 'D':
+ name = "double";
+ encoding = DW_ATE_float;
+ byte_size = 8;
+ break;
+ case 'F':
+ name = "float";
+ encoding = DW_ATE_float;
+ byte_size = 4;
+ break;
+ case 'I':
+ name = "int";
+ encoding = DW_ATE_signed;
+ byte_size = 4;
+ break;
+ case 'J':
+ name = "long";
+ encoding = DW_ATE_signed;
+ byte_size = 8;
+ break;
+ case 'S':
+ name = "short";
+ encoding = DW_ATE_signed;
+ byte_size = 2;
+ break;
+ case 'Z':
+ name = "boolean";
+ encoding = DW_ATE_boolean;
+ byte_size = 1;
+ break;
+ case 'V':
+ LOG(FATAL) << "Void type should not be encoded";
+ UNREACHABLE();
+ default:
+ LOG(FATAL) << "Unknown dex type descriptor: \"" << desc << "\"";
+ UNREACHABLE();
+ }
+ CloseNamespacesAboveDepth(0); // Declare in root namespace.
+ offset = info_.StartTag(DW_TAG_base_type);
+ WriteName(name);
+ info_.WriteData1(DW_AT_encoding, encoding);
+ info_.WriteData1(DW_AT_byte_size, byte_size);
+ info_.EndTag();
+ }
+
+ type_cache_.emplace(desc, offset);
+ return offset;
+ }
+
+ // Start DW_TAG_class_type tag nested in DW_TAG_namespace tags.
+ // Returns offset of the class tag in the compilation unit.
+ size_t StartClassTag(const char* desc) {
+ std::string name = SetNamespaceForClass(desc);
+ size_t offset = info_.StartTag(dwarf::DW_TAG_class_type);
+ WriteName(name.c_str());
+ return offset;
+ }
+
+ void EndClassTag() {
+ info_.EndTag();
+ }
+
+ // Set the current namespace nesting to one required by the given class.
+ // Returns the class name with namespaces, 'L', and ';' stripped.
+ std::string SetNamespaceForClass(const char* desc) {
+ DCHECK(desc != nullptr && desc[0] == 'L');
+ desc++; // Skip the initial 'L'.
+ size_t depth = 0;
+ for (const char* end; (end = strchr(desc, '/')) != nullptr; desc = end + 1, ++depth) {
+ // Check whether the name at this depth is already what we need.
+ if (depth < current_namespace_.size()) {
+ const std::string& name = current_namespace_[depth];
+ if (name.compare(0, name.size(), desc, end - desc) == 0) {
+ continue;
+ }
+ }
+ // Otherwise we need to open a new namespace tag at this depth.
+ CloseNamespacesAboveDepth(depth);
+ info_.StartTag(dwarf::DW_TAG_namespace);
+ std::string name(desc, end - desc);
+ WriteName(name.c_str());
+ current_namespace_.push_back(std::move(name));
+ }
+ CloseNamespacesAboveDepth(depth);
+ return std::string(desc, strchr(desc, ';') - desc);
+ }
+
+ // Close namespace tags to reach the given nesting depth.
+ void CloseNamespacesAboveDepth(size_t depth) {
+ DCHECK_LE(depth, current_namespace_.size());
+ while (current_namespace_.size() > depth) {
+ info_.EndTag();
+ current_namespace_.pop_back();
+ }
+ }
+
+ // For access to the ELF sections.
+ ElfDebugInfoWriter<ElfTypes>* owner_;
+ // Temporary buffer to create and store the entries.
+ dwarf::DebugInfoEntryWriter<> info_;
+ // Cache of already translated type descriptors.
+ std::map<std::string, size_t> type_cache_; // type_desc -> definition_offset.
+ // 32-bit references which need to be resolved to a type later.
+ // Given type may be used multiple times. Therefore we need a multimap.
+ std::multimap<std::string, size_t> lazy_types_; // type_desc -> patch_offset.
+ // The current set of open namespace tags which are active and not closed yet.
+ std::vector<std::string> current_namespace_;
+};
+
+} // namespace debug
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_ELF_DEBUG_INFO_WRITER_H_
+
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h
new file mode 100644
index 0000000..d3859ca
--- /dev/null
+++ b/compiler/debug/elf_debug_line_writer.h
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DEBUG_ELF_DEBUG_LINE_WRITER_H_
+#define ART_COMPILER_DEBUG_ELF_DEBUG_LINE_WRITER_H_
+
+#include <vector>
+
+#include "compiled_method.h"
+#include "debug/dwarf/debug_line_opcode_writer.h"
+#include "debug/dwarf/headers.h"
+#include "debug/elf_compilation_unit.h"
+#include "dex_file-inl.h"
+#include "elf_builder.h"
+#include "stack_map.h"
+
+namespace art {
+namespace debug {
+
+typedef std::vector<DexFile::PositionInfo> PositionInfos;
+
+static bool PositionInfoCallback(void* ctx, const DexFile::PositionInfo& entry) {
+ static_cast<PositionInfos*>(ctx)->push_back(entry);
+ return false;
+}
+
+template<typename ElfTypes>
+class ElfDebugLineWriter {
+ using Elf_Addr = typename ElfTypes::Addr;
+
+ public:
+ explicit ElfDebugLineWriter(ElfBuilder<ElfTypes>* builder) : builder_(builder) {
+ }
+
+ void Start() {
+ builder_->GetDebugLine()->Start();
+ }
+
+ // Write line table for given set of methods.
+ // Returns the number of bytes written.
+ size_t WriteCompilationUnit(ElfCompilationUnit& compilation_unit) {
+ const bool is64bit = Is64BitInstructionSet(builder_->GetIsa());
+ const Elf_Addr text_address = builder_->GetText()->Exists()
+ ? builder_->GetText()->GetAddress()
+ : 0;
+
+ compilation_unit.debug_line_offset = builder_->GetDebugLine()->GetSize();
+
+ std::vector<dwarf::FileEntry> files;
+ std::unordered_map<std::string, size_t> files_map;
+ std::vector<std::string> directories;
+ std::unordered_map<std::string, size_t> directories_map;
+ int code_factor_bits_ = 0;
+ int dwarf_isa = -1;
+ switch (builder_->GetIsa()) {
+ case kArm: // arm actually means thumb2.
+ case kThumb2:
+ code_factor_bits_ = 1; // 16-bit instuctions
+ dwarf_isa = 1; // DW_ISA_ARM_thumb.
+ break;
+ case kArm64:
+ case kMips:
+ case kMips64:
+ code_factor_bits_ = 2; // 32-bit instructions
+ break;
+ case kNone:
+ case kX86:
+ case kX86_64:
+ break;
+ }
+ dwarf::DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits_);
+ for (const MethodDebugInfo* mi : compilation_unit.methods) {
+ // Ignore function if we have already generated line table for the same address.
+ // It would confuse the debugger and the DWARF specification forbids it.
+ if (mi->deduped) {
+ continue;
+ }
+
+ uint32_t prologue_end = std::numeric_limits<uint32_t>::max();
+ ArrayRef<const SrcMapElem> pc2dex_map;
+ std::vector<SrcMapElem> pc2dex_map_from_stack_maps;
+ if (mi->IsFromOptimizingCompiler()) {
+ // Use stack maps to create mapping table from pc to dex.
+ const CodeInfo code_info(mi->compiled_method->GetVmapTable().data());
+ const StackMapEncoding encoding = code_info.ExtractEncoding();
+ for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) {
+ StackMap stack_map = code_info.GetStackMapAt(s, encoding);
+ DCHECK(stack_map.IsValid());
+ const uint32_t pc = stack_map.GetNativePcOffset(encoding);
+ const int32_t dex = stack_map.GetDexPc(encoding);
+ pc2dex_map_from_stack_maps.push_back({pc, dex});
+ if (stack_map.HasDexRegisterMap(encoding)) {
+ // Guess that the first map with local variables is the end of prologue.
+ prologue_end = std::min(prologue_end, pc);
+ }
+ }
+ std::sort(pc2dex_map_from_stack_maps.begin(),
+ pc2dex_map_from_stack_maps.end());
+ pc2dex_map = ArrayRef<const SrcMapElem>(pc2dex_map_from_stack_maps);
+ } else {
+ // Use the mapping table provided by the quick compiler.
+ pc2dex_map = mi->compiled_method->GetSrcMappingTable();
+ prologue_end = 0;
+ }
+
+ if (pc2dex_map.empty()) {
+ continue;
+ }
+
+ Elf_Addr method_address = text_address + mi->low_pc;
+
+ PositionInfos dex2line_map;
+ const DexFile* dex = mi->dex_file;
+ if (!dex->DecodeDebugPositionInfo(mi->code_item, PositionInfoCallback, &dex2line_map)) {
+ continue;
+ }
+
+ if (dex2line_map.empty()) {
+ continue;
+ }
+
+ opcodes.SetAddress(method_address);
+ if (dwarf_isa != -1) {
+ opcodes.SetISA(dwarf_isa);
+ }
+
+ // Get and deduplicate directory and filename.
+ int file_index = 0; // 0 - primary source file of the compilation.
+ auto& dex_class_def = dex->GetClassDef(mi->class_def_index);
+ const char* source_file = dex->GetSourceFile(dex_class_def);
+ if (source_file != nullptr) {
+ std::string file_name(source_file);
+ size_t file_name_slash = file_name.find_last_of('/');
+ std::string class_name(dex->GetClassDescriptor(dex_class_def));
+ size_t class_name_slash = class_name.find_last_of('/');
+ std::string full_path(file_name);
+
+ // Guess directory from package name.
+ int directory_index = 0; // 0 - current directory of the compilation.
+ if (file_name_slash == std::string::npos && // Just filename.
+ class_name.front() == 'L' && // Type descriptor for a class.
+ class_name_slash != std::string::npos) { // Has package name.
+ std::string package_name = class_name.substr(1, class_name_slash - 1);
+ auto it = directories_map.find(package_name);
+ if (it == directories_map.end()) {
+ directory_index = 1 + directories.size();
+ directories_map.emplace(package_name, directory_index);
+ directories.push_back(package_name);
+ } else {
+ directory_index = it->second;
+ }
+ full_path = package_name + "/" + file_name;
+ }
+
+ // Add file entry.
+ auto it2 = files_map.find(full_path);
+ if (it2 == files_map.end()) {
+ file_index = 1 + files.size();
+ files_map.emplace(full_path, file_index);
+ files.push_back(dwarf::FileEntry {
+ file_name,
+ directory_index,
+ 0, // Modification time - NA.
+ 0, // File size - NA.
+ });
+ } else {
+ file_index = it2->second;
+ }
+ }
+ opcodes.SetFile(file_index);
+
+ // Generate mapping opcodes from PC to Java lines.
+ if (file_index != 0) {
+ bool first = true;
+ for (SrcMapElem pc2dex : pc2dex_map) {
+ uint32_t pc = pc2dex.from_;
+ int dex_pc = pc2dex.to_;
+ // Find mapping with address with is greater than our dex pc; then go back one step.
+ auto dex2line = std::upper_bound(
+ dex2line_map.begin(),
+ dex2line_map.end(),
+ dex_pc,
+ [](uint32_t address, const DexFile::PositionInfo& entry) {
+ return address < entry.address_;
+ });
+ // Look for first valid mapping after the prologue.
+ if (dex2line != dex2line_map.begin() && pc >= prologue_end) {
+ int line = (--dex2line)->line_;
+ if (first) {
+ first = false;
+ if (pc > 0) {
+ // Assume that any preceding code is prologue.
+ int first_line = dex2line_map.front().line_;
+ // Prologue is not a sensible place for a breakpoint.
+ opcodes.NegateStmt();
+ opcodes.AddRow(method_address, first_line);
+ opcodes.NegateStmt();
+ opcodes.SetPrologueEnd();
+ }
+ opcodes.AddRow(method_address + pc, line);
+ } else if (line != opcodes.CurrentLine()) {
+ opcodes.AddRow(method_address + pc, line);
+ }
+ }
+ }
+ } else {
+ // line 0 - instruction cannot be attributed to any source line.
+ opcodes.AddRow(method_address, 0);
+ }
+
+ opcodes.AdvancePC(text_address + mi->high_pc);
+ opcodes.EndSequence();
+ }
+ std::vector<uint8_t> buffer;
+ buffer.reserve(opcodes.data()->size() + KB);
+ size_t offset = builder_->GetDebugLine()->GetSize();
+ WriteDebugLineTable(directories, files, opcodes, offset, &buffer, &debug_line_patches_);
+ builder_->GetDebugLine()->WriteFully(buffer.data(), buffer.size());
+ return buffer.size();
+ }
+
+ void End(bool write_oat_patches) {
+ builder_->GetDebugLine()->End();
+ if (write_oat_patches) {
+ builder_->WritePatches(".debug_line.oat_patches",
+ ArrayRef<const uintptr_t>(debug_line_patches_));
+ }
+ }
+
+ private:
+ ElfBuilder<ElfTypes>* builder_;
+ std::vector<uintptr_t> debug_line_patches_;
+};
+
+} // namespace debug
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_ELF_DEBUG_LINE_WRITER_H_
+
diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h
new file mode 100644
index 0000000..a19b36f
--- /dev/null
+++ b/compiler/debug/elf_debug_loc_writer.h
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DEBUG_ELF_DEBUG_LOC_WRITER_H_
+#define ART_COMPILER_DEBUG_ELF_DEBUG_LOC_WRITER_H_
+
+#include <map>
+
+#include "arch/instruction_set.h"
+#include "compiled_method.h"
+#include "debug/dwarf/debug_info_entry_writer.h"
+#include "debug/dwarf/register.h"
+#include "debug/method_debug_info.h"
+#include "stack_map.h"
+
+namespace art {
+namespace debug {
+using Reg = dwarf::Reg;
+
+static Reg GetDwarfCoreReg(InstructionSet isa, int machine_reg) {
+ switch (isa) {
+ case kArm:
+ case kThumb2:
+ return Reg::ArmCore(machine_reg);
+ case kArm64:
+ return Reg::Arm64Core(machine_reg);
+ case kX86:
+ return Reg::X86Core(machine_reg);
+ case kX86_64:
+ return Reg::X86_64Core(machine_reg);
+ case kMips:
+ return Reg::MipsCore(machine_reg);
+ case kMips64:
+ return Reg::Mips64Core(machine_reg);
+ case kNone:
+ LOG(FATAL) << "No instruction set";
+ }
+ UNREACHABLE();
+}
+
+static Reg GetDwarfFpReg(InstructionSet isa, int machine_reg) {
+ switch (isa) {
+ case kArm:
+ case kThumb2:
+ return Reg::ArmFp(machine_reg);
+ case kArm64:
+ return Reg::Arm64Fp(machine_reg);
+ case kX86:
+ return Reg::X86Fp(machine_reg);
+ case kX86_64:
+ return Reg::X86_64Fp(machine_reg);
+ case kMips:
+ return Reg::MipsFp(machine_reg);
+ case kMips64:
+ return Reg::Mips64Fp(machine_reg);
+ case kNone:
+ LOG(FATAL) << "No instruction set";
+ }
+ UNREACHABLE();
+}
+
+struct VariableLocation {
+ uint32_t low_pc;
+ uint32_t high_pc;
+ DexRegisterLocation reg_lo; // May be None if the location is unknown.
+ DexRegisterLocation reg_hi; // Most significant bits of 64-bit value.
+};
+
+// Get the location of given dex register (e.g. stack or machine register).
+// Note that the location might be different based on the current pc.
+// The result will cover all ranges where the variable is in scope.
+// PCs corresponding to stackmap with dex register map are accurate,
+// all other PCs are best-effort only.
+std::vector<VariableLocation> GetVariableLocations(const MethodDebugInfo* method_info,
+ uint16_t vreg,
+ bool is64bitValue,
+ uint32_t dex_pc_low,
+ uint32_t dex_pc_high) {
+ std::vector<VariableLocation> variable_locations;
+
+ // Get stack maps sorted by pc (they might not be sorted internally).
+ const CodeInfo code_info(method_info->compiled_method->GetVmapTable().data());
+ const StackMapEncoding encoding = code_info.ExtractEncoding();
+ std::map<uint32_t, StackMap> stack_maps;
+ for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) {
+ StackMap stack_map = code_info.GetStackMapAt(s, encoding);
+ DCHECK(stack_map.IsValid());
+ const uint32_t low_pc = method_info->low_pc + stack_map.GetNativePcOffset(encoding);
+ DCHECK_LE(low_pc, method_info->high_pc);
+ stack_maps.emplace(low_pc, stack_map);
+ }
+
+ // Create entries for the requested register based on stack map data.
+ for (auto it = stack_maps.begin(); it != stack_maps.end(); it++) {
+ const StackMap& stack_map = it->second;
+ const uint32_t low_pc = it->first;
+ auto next_it = it;
+ next_it++;
+ const uint32_t high_pc = next_it != stack_maps.end() ? next_it->first
+ : method_info->high_pc;
+ DCHECK_LE(low_pc, high_pc);
+ if (low_pc == high_pc) {
+ continue; // Ignore if the address range is empty.
+ }
+
+ // Check that the stack map is in the requested range.
+ uint32_t dex_pc = stack_map.GetDexPc(encoding);
+ if (!(dex_pc_low <= dex_pc && dex_pc < dex_pc_high)) {
+ continue;
+ }
+
+ // Find the location of the dex register.
+ DexRegisterLocation reg_lo = DexRegisterLocation::None();
+ DexRegisterLocation reg_hi = DexRegisterLocation::None();
+ if (stack_map.HasDexRegisterMap(encoding)) {
+ DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(
+ stack_map, encoding, method_info->code_item->registers_size_);
+ reg_lo = dex_register_map.GetDexRegisterLocation(
+ vreg, method_info->code_item->registers_size_, code_info, encoding);
+ if (is64bitValue) {
+ reg_hi = dex_register_map.GetDexRegisterLocation(
+ vreg + 1, method_info->code_item->registers_size_, code_info, encoding);
+ }
+ }
+
+ // Add location entry for this address range.
+ if (!variable_locations.empty() &&
+ variable_locations.back().reg_lo == reg_lo &&
+ variable_locations.back().reg_hi == reg_hi &&
+ variable_locations.back().high_pc == low_pc) {
+ // Merge with the previous entry (extend its range).
+ variable_locations.back().high_pc = high_pc;
+ } else if (!variable_locations.empty() && reg_lo == DexRegisterLocation::None()) {
+ // Unknown location - use the last known location as best-effort guess.
+ variable_locations.back().high_pc = high_pc;
+ } else {
+ variable_locations.push_back({low_pc, high_pc, reg_lo, reg_hi});
+ }
+ }
+
+ return variable_locations;
+}
+
+// Write table into .debug_loc which describes location of dex register.
+// The dex register might be valid only at some points and it might
+// move between machine registers and stack.
+static void WriteDebugLocEntry(const MethodDebugInfo* method_info,
+ uint16_t vreg,
+ bool is64bitValue,
+ uint32_t compilation_unit_low_pc,
+ uint32_t dex_pc_low,
+ uint32_t dex_pc_high,
+ InstructionSet isa,
+ dwarf::DebugInfoEntryWriter<>* debug_info,
+ std::vector<uint8_t>* debug_loc_buffer,
+ std::vector<uint8_t>* debug_ranges_buffer) {
+ using Kind = DexRegisterLocation::Kind;
+ if (!method_info->IsFromOptimizingCompiler()) {
+ return;
+ }
+
+ dwarf::Writer<> debug_loc(debug_loc_buffer);
+ dwarf::Writer<> debug_ranges(debug_ranges_buffer);
+ debug_info->WriteSecOffset(dwarf::DW_AT_location, debug_loc.size());
+ debug_info->WriteSecOffset(dwarf::DW_AT_start_scope, debug_ranges.size());
+
+ std::vector<VariableLocation> variable_locations = GetVariableLocations(
+ method_info,
+ vreg,
+ is64bitValue,
+ dex_pc_low,
+ dex_pc_high);
+
+ // Write .debug_loc entries.
+ const bool is64bit = Is64BitInstructionSet(isa);
+ std::vector<uint8_t> expr_buffer;
+ for (const VariableLocation& variable_location : variable_locations) {
+ // Translate dex register location to DWARF expression.
+ // Note that 64-bit value might be split to two distinct locations.
+ // (for example, two 32-bit machine registers, or even stack and register)
+ dwarf::Expression expr(&expr_buffer);
+ DexRegisterLocation reg_lo = variable_location.reg_lo;
+ DexRegisterLocation reg_hi = variable_location.reg_hi;
+ for (int piece = 0; piece < (is64bitValue ? 2 : 1); piece++) {
+ DexRegisterLocation reg_loc = (piece == 0 ? reg_lo : reg_hi);
+ const Kind kind = reg_loc.GetKind();
+ const int32_t value = reg_loc.GetValue();
+ if (kind == Kind::kInStack) {
+ const size_t frame_size = method_info->compiled_method->GetFrameSizeInBytes();
+ // The stack offset is relative to SP. Make it relative to CFA.
+ expr.WriteOpFbreg(value - frame_size);
+ if (piece == 0 && reg_hi.GetKind() == Kind::kInStack &&
+ reg_hi.GetValue() == value + 4) {
+ break; // the high word is correctly implied by the low word.
+ }
+ } else if (kind == Kind::kInRegister) {
+ expr.WriteOpReg(GetDwarfCoreReg(isa, value).num());
+ if (piece == 0 && reg_hi.GetKind() == Kind::kInRegisterHigh &&
+ reg_hi.GetValue() == value) {
+ break; // the high word is correctly implied by the low word.
+ }
+ } else if (kind == Kind::kInFpuRegister) {
+ if ((isa == kArm || isa == kThumb2) &&
+ piece == 0 && reg_hi.GetKind() == Kind::kInFpuRegister &&
+ reg_hi.GetValue() == value + 1 && value % 2 == 0) {
+ // Translate S register pair to D register (e.g. S4+S5 to D2).
+ expr.WriteOpReg(Reg::ArmDp(value / 2).num());
+ break;
+ }
+ expr.WriteOpReg(GetDwarfFpReg(isa, value).num());
+ if (piece == 0 && reg_hi.GetKind() == Kind::kInFpuRegisterHigh &&
+ reg_hi.GetValue() == reg_lo.GetValue()) {
+ break; // the high word is correctly implied by the low word.
+ }
+ } else if (kind == Kind::kConstant) {
+ expr.WriteOpConsts(value);
+ expr.WriteOpStackValue();
+ } else if (kind == Kind::kNone) {
+ break;
+ } else {
+ // kInStackLargeOffset and kConstantLargeValue are hidden by GetKind().
+ // kInRegisterHigh and kInFpuRegisterHigh should be handled by
+ // the special cases above and they should not occur alone.
+ LOG(ERROR) << "Unexpected register location kind: "
+ << DexRegisterLocation::PrettyDescriptor(kind);
+ break;
+ }
+ if (is64bitValue) {
+ // Write the marker which is needed by split 64-bit values.
+ // This code is skipped by the special cases.
+ expr.WriteOpPiece(4);
+ }
+ }
+
+ if (expr.size() > 0) {
+ if (is64bit) {
+ debug_loc.PushUint64(variable_location.low_pc - compilation_unit_low_pc);
+ debug_loc.PushUint64(variable_location.high_pc - compilation_unit_low_pc);
+ } else {
+ debug_loc.PushUint32(variable_location.low_pc - compilation_unit_low_pc);
+ debug_loc.PushUint32(variable_location.high_pc - compilation_unit_low_pc);
+ }
+ // Write the expression.
+ debug_loc.PushUint16(expr.size());
+ debug_loc.PushData(expr.data());
+ } else {
+ // Do not generate .debug_loc if the location is not known.
+ }
+ }
+ // Write end-of-list entry.
+ if (is64bit) {
+ debug_loc.PushUint64(0);
+ debug_loc.PushUint64(0);
+ } else {
+ debug_loc.PushUint32(0);
+ debug_loc.PushUint32(0);
+ }
+
+ // Write .debug_ranges entries.
+ // This includes ranges where the variable is in scope but the location is not known.
+ for (size_t i = 0; i < variable_locations.size(); i++) {
+ uint32_t low_pc = variable_locations[i].low_pc;
+ uint32_t high_pc = variable_locations[i].high_pc;
+ while (i + 1 < variable_locations.size() && variable_locations[i+1].low_pc == high_pc) {
+ // Merge address range with the next entry.
+ high_pc = variable_locations[++i].high_pc;
+ }
+ if (is64bit) {
+ debug_ranges.PushUint64(low_pc - compilation_unit_low_pc);
+ debug_ranges.PushUint64(high_pc - compilation_unit_low_pc);
+ } else {
+ debug_ranges.PushUint32(low_pc - compilation_unit_low_pc);
+ debug_ranges.PushUint32(high_pc - compilation_unit_low_pc);
+ }
+ }
+ // Write end-of-list entry.
+ if (is64bit) {
+ debug_ranges.PushUint64(0);
+ debug_ranges.PushUint64(0);
+ } else {
+ debug_ranges.PushUint32(0);
+ debug_ranges.PushUint32(0);
+ }
+}
+
+} // namespace debug
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_ELF_DEBUG_LOC_WRITER_H_
+
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
new file mode 100644
index 0000000..01bd679
--- /dev/null
+++ b/compiler/debug/elf_debug_writer.cc
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "elf_debug_writer.h"
+
+#include <vector>
+
+#include "debug/dwarf/dwarf_constants.h"
+#include "debug/elf_compilation_unit.h"
+#include "debug/elf_debug_frame_writer.h"
+#include "debug/elf_debug_info_writer.h"
+#include "debug/elf_debug_line_writer.h"
+#include "debug/elf_debug_loc_writer.h"
+#include "debug/elf_gnu_debugdata_writer.h"
+#include "debug/elf_symtab_writer.h"
+#include "debug/method_debug_info.h"
+#include "elf_builder.h"
+#include "linker/vector_output_stream.h"
+#include "utils/array_ref.h"
+
+namespace art {
+namespace debug {
+
+template <typename ElfTypes>
+void WriteDebugInfo(ElfBuilder<ElfTypes>* builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos,
+ dwarf::CFIFormat cfi_format,
+ bool write_oat_patches) {
+ // Add methods to .symtab.
+ WriteDebugSymbols(builder, method_infos, true /* with_signature */);
+ // Generate CFI (stack unwinding information).
+ WriteCFISection(builder, method_infos, cfi_format, write_oat_patches);
+ // Write DWARF .debug_* sections.
+ WriteDebugSections(builder, method_infos, write_oat_patches);
+}
+
+template<typename ElfTypes>
+static void WriteDebugSections(ElfBuilder<ElfTypes>* builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos,
+ bool write_oat_patches) {
+ // Group the methods into compilation units based on source file.
+ std::vector<ElfCompilationUnit> compilation_units;
+ const char* last_source_file = nullptr;
+ for (const MethodDebugInfo& mi : method_infos) {
+ auto& dex_class_def = mi.dex_file->GetClassDef(mi.class_def_index);
+ const char* source_file = mi.dex_file->GetSourceFile(dex_class_def);
+ if (compilation_units.empty() || source_file != last_source_file) {
+ compilation_units.push_back(ElfCompilationUnit());
+ }
+ ElfCompilationUnit& cu = compilation_units.back();
+ cu.methods.push_back(&mi);
+ cu.low_pc = std::min(cu.low_pc, mi.low_pc);
+ cu.high_pc = std::max(cu.high_pc, mi.high_pc);
+ last_source_file = source_file;
+ }
+
+ // Write .debug_line section.
+ if (!compilation_units.empty()) {
+ ElfDebugLineWriter<ElfTypes> line_writer(builder);
+ line_writer.Start();
+ for (auto& compilation_unit : compilation_units) {
+ line_writer.WriteCompilationUnit(compilation_unit);
+ }
+ line_writer.End(write_oat_patches);
+ }
+
+ // Write .debug_info section.
+ if (!compilation_units.empty()) {
+ ElfDebugInfoWriter<ElfTypes> info_writer(builder);
+ info_writer.Start();
+ for (const auto& compilation_unit : compilation_units) {
+ ElfCompilationUnitWriter<ElfTypes> cu_writer(&info_writer);
+ cu_writer.Write(compilation_unit);
+ }
+ info_writer.End(write_oat_patches);
+ }
+}
+
+std::vector<uint8_t> MakeMiniDebugInfo(
+ InstructionSet isa,
+ size_t rodata_size,
+ size_t text_size,
+ const ArrayRef<const MethodDebugInfo>& method_infos) {
+ if (Is64BitInstructionSet(isa)) {
+ return MakeMiniDebugInfoInternal<ElfTypes64>(isa, rodata_size, text_size, method_infos);
+ } else {
+ return MakeMiniDebugInfoInternal<ElfTypes32>(isa, rodata_size, text_size, method_infos);
+ }
+}
+
+template <typename ElfTypes>
+static ArrayRef<const uint8_t> WriteDebugElfFileForMethodInternal(
+ const MethodDebugInfo& method_info) {
+ const InstructionSet isa = method_info.compiled_method->GetInstructionSet();
+ std::vector<uint8_t> buffer;
+ buffer.reserve(KB);
+ VectorOutputStream out("Debug ELF file", &buffer);
+ std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
+ // No program headers since the ELF file is not linked and has no allocated sections.
+ builder->Start(false /* write_program_headers */);
+ WriteDebugInfo(builder.get(),
+ ArrayRef<const MethodDebugInfo>(&method_info, 1),
+ dwarf::DW_DEBUG_FRAME_FORMAT,
+ false /* write_oat_patches */);
+ builder->End();
+ CHECK(builder->Good());
+ // Make a copy of the buffer. We want to shrink it anyway.
+ uint8_t* result = new uint8_t[buffer.size()];
+ CHECK(result != nullptr);
+ memcpy(result, buffer.data(), buffer.size());
+ return ArrayRef<const uint8_t>(result, buffer.size());
+}
+
+ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const MethodDebugInfo& method_info) {
+ const InstructionSet isa = method_info.compiled_method->GetInstructionSet();
+ if (Is64BitInstructionSet(isa)) {
+ return WriteDebugElfFileForMethodInternal<ElfTypes64>(method_info);
+ } else {
+ return WriteDebugElfFileForMethodInternal<ElfTypes32>(method_info);
+ }
+}
+
+template <typename ElfTypes>
+static ArrayRef<const uint8_t> WriteDebugElfFileForClassesInternal(
+ const InstructionSet isa, const ArrayRef<mirror::Class*>& types)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ std::vector<uint8_t> buffer;
+ buffer.reserve(KB);
+ VectorOutputStream out("Debug ELF file", &buffer);
+ std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
+ // No program headers since the ELF file is not linked and has no allocated sections.
+ builder->Start(false /* write_program_headers */);
+ ElfDebugInfoWriter<ElfTypes> info_writer(builder.get());
+ info_writer.Start();
+ ElfCompilationUnitWriter<ElfTypes> cu_writer(&info_writer);
+ cu_writer.Write(types);
+ info_writer.End(false /* write_oat_patches */);
+
+ builder->End();
+ CHECK(builder->Good());
+ // Make a copy of the buffer. We want to shrink it anyway.
+ uint8_t* result = new uint8_t[buffer.size()];
+ CHECK(result != nullptr);
+ memcpy(result, buffer.data(), buffer.size());
+ return ArrayRef<const uint8_t>(result, buffer.size());
+}
+
+ArrayRef<const uint8_t> WriteDebugElfFileForClasses(const InstructionSet isa,
+ const ArrayRef<mirror::Class*>& types) {
+ if (Is64BitInstructionSet(isa)) {
+ return WriteDebugElfFileForClassesInternal<ElfTypes64>(isa, types);
+ } else {
+ return WriteDebugElfFileForClassesInternal<ElfTypes32>(isa, types);
+ }
+}
+
+// Explicit instantiations
+template void WriteDebugInfo<ElfTypes32>(
+ ElfBuilder<ElfTypes32>* builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos,
+ dwarf::CFIFormat cfi_format,
+ bool write_oat_patches);
+template void WriteDebugInfo<ElfTypes64>(
+ ElfBuilder<ElfTypes64>* builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos,
+ dwarf::CFIFormat cfi_format,
+ bool write_oat_patches);
+
+} // namespace debug
+} // namespace art
diff --git a/compiler/elf_writer_debug.h b/compiler/debug/elf_debug_writer.h
similarity index 60%
rename from compiler/elf_writer_debug.h
rename to compiler/debug/elf_debug_writer.h
index e19da08..103b501 100644
--- a/compiler/elf_writer_debug.h
+++ b/compiler/debug/elf_debug_writer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_ELF_WRITER_DEBUG_H_
-#define ART_COMPILER_ELF_WRITER_DEBUG_H_
+#ifndef ART_COMPILER_DEBUG_ELF_DEBUG_WRITER_H_
+#define ART_COMPILER_DEBUG_ELF_DEBUG_WRITER_H_
#include "base/macros.h"
#include "base/mutex.h"
-#include "dwarf/dwarf_constants.h"
+#include "debug/dwarf/dwarf_constants.h"
#include "elf_builder.h"
#include "utils/array_ref.h"
@@ -27,25 +27,27 @@
namespace mirror {
class Class;
}
-namespace dwarf {
+namespace debug {
struct MethodDebugInfo;
template <typename ElfTypes>
void WriteDebugInfo(ElfBuilder<ElfTypes>* builder,
const ArrayRef<const MethodDebugInfo>& method_infos,
- CFIFormat cfi_format);
+ dwarf::CFIFormat cfi_format,
+ bool write_oat_patches);
-template <typename ElfTypes>
-void WriteMiniDebugInfo(ElfBuilder<ElfTypes>* builder,
- const ArrayRef<const MethodDebugInfo>& method_infos);
+std::vector<uint8_t> MakeMiniDebugInfo(InstructionSet isa,
+ size_t rodata_section_size,
+ size_t text_section_size,
+ const ArrayRef<const MethodDebugInfo>& method_infos);
-ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const dwarf::MethodDebugInfo& method_info);
+ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const MethodDebugInfo& method_info);
ArrayRef<const uint8_t> WriteDebugElfFileForClasses(const InstructionSet isa,
const ArrayRef<mirror::Class*>& types)
SHARED_REQUIRES(Locks::mutator_lock_);
-} // namespace dwarf
+} // namespace debug
} // namespace art
-#endif // ART_COMPILER_ELF_WRITER_DEBUG_H_
+#endif // ART_COMPILER_DEBUG_ELF_DEBUG_WRITER_H_
diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h
new file mode 100644
index 0000000..5c7d1c7
--- /dev/null
+++ b/compiler/debug/elf_gnu_debugdata_writer.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DEBUG_ELF_GNU_DEBUGDATA_WRITER_H_
+#define ART_COMPILER_DEBUG_ELF_GNU_DEBUGDATA_WRITER_H_
+
+#include <vector>
+
+#include "arch/instruction_set.h"
+#include "elf_builder.h"
+#include "linker/vector_output_stream.h"
+
+// liblzma.
+#include "7zCrc.h"
+#include "XzCrc64.h"
+#include "XzEnc.h"
+
+namespace art {
+namespace debug {
+
+static void XzCompress(const std::vector<uint8_t>* src, std::vector<uint8_t>* dst) {
+ // Configure the compression library.
+ CrcGenerateTable();
+ Crc64GenerateTable();
+ CLzma2EncProps lzma2Props;
+ Lzma2EncProps_Init(&lzma2Props);
+ lzma2Props.lzmaProps.level = 1; // Fast compression.
+ Lzma2EncProps_Normalize(&lzma2Props);
+ CXzProps props;
+ XzProps_Init(&props);
+ props.lzma2Props = &lzma2Props;
+ // Implement the required interface for communication (written in C so no virtual methods).
+ struct XzCallbacks : public ISeqInStream, public ISeqOutStream, public ICompressProgress {
+ static SRes ReadImpl(void* p, void* buf, size_t* size) {
+ auto* ctx = static_cast<XzCallbacks*>(reinterpret_cast<ISeqInStream*>(p));
+ *size = std::min(*size, ctx->src_->size() - ctx->src_pos_);
+ memcpy(buf, ctx->src_->data() + ctx->src_pos_, *size);
+ ctx->src_pos_ += *size;
+ return SZ_OK;
+ }
+ static size_t WriteImpl(void* p, const void* buf, size_t size) {
+ auto* ctx = static_cast<XzCallbacks*>(reinterpret_cast<ISeqOutStream*>(p));
+ const uint8_t* buffer = reinterpret_cast<const uint8_t*>(buf);
+ ctx->dst_->insert(ctx->dst_->end(), buffer, buffer + size);
+ return size;
+ }
+ static SRes ProgressImpl(void* , UInt64, UInt64) {
+ return SZ_OK;
+ }
+ size_t src_pos_;
+ const std::vector<uint8_t>* src_;
+ std::vector<uint8_t>* dst_;
+ };
+ XzCallbacks callbacks;
+ callbacks.Read = XzCallbacks::ReadImpl;
+ callbacks.Write = XzCallbacks::WriteImpl;
+ callbacks.Progress = XzCallbacks::ProgressImpl;
+ callbacks.src_pos_ = 0;
+ callbacks.src_ = src;
+ callbacks.dst_ = dst;
+ // Compress.
+ SRes res = Xz_Encode(&callbacks, &callbacks, &props, &callbacks);
+ CHECK_EQ(res, SZ_OK);
+}
+
+template <typename ElfTypes>
+static std::vector<uint8_t> MakeMiniDebugInfoInternal(
+ InstructionSet isa,
+ size_t rodata_section_size,
+ size_t text_section_size,
+ const ArrayRef<const MethodDebugInfo>& method_infos) {
+ std::vector<uint8_t> buffer;
+ buffer.reserve(KB);
+ VectorOutputStream out("Mini-debug-info ELF file", &buffer);
+ std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
+ builder->Start();
+ // Mirror .rodata and .text as NOBITS sections.
+ // It is needed to detected relocations after compression.
+ builder->GetRoData()->WriteNoBitsSection(rodata_section_size);
+ builder->GetText()->WriteNoBitsSection(text_section_size);
+ WriteDebugSymbols(builder.get(), method_infos, false /* with_signature */);
+ WriteCFISection(builder.get(),
+ method_infos,
+ dwarf::DW_DEBUG_FRAME_FORMAT,
+ false /* write_oat_paches */);
+ builder->End();
+ CHECK(builder->Good());
+ std::vector<uint8_t> compressed_buffer;
+ compressed_buffer.reserve(buffer.size() / 4);
+ XzCompress(&buffer, &compressed_buffer);
+ return compressed_buffer;
+}
+
+} // namespace debug
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_ELF_GNU_DEBUGDATA_WRITER_H_
+
diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h
new file mode 100644
index 0000000..41508f4
--- /dev/null
+++ b/compiler/debug/elf_symtab_writer.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DEBUG_ELF_SYMTAB_WRITER_H_
+#define ART_COMPILER_DEBUG_ELF_SYMTAB_WRITER_H_
+
+#include <unordered_set>
+
+#include "debug/method_debug_info.h"
+#include "elf_builder.h"
+#include "utils.h"
+
+namespace art {
+namespace debug {
+
+// The ARM specification defines three special mapping symbols
+// $a, $t and $d which mark ARM, Thumb and data ranges respectively.
+// These symbols can be used by tools, for example, to pretty
+// print instructions correctly. Objdump will use them if they
+// exist, but it will still work well without them.
+// However, these extra symbols take space, so let's just generate
+// one symbol which marks the whole .text section as code.
+constexpr bool kGenerateSingleArmMappingSymbol = true;
+
+template <typename ElfTypes>
+static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos,
+ bool with_signature) {
+ bool generated_mapping_symbol = false;
+ auto* strtab = builder->GetStrTab();
+ auto* symtab = builder->GetSymTab();
+
+ if (method_infos.empty()) {
+ return;
+ }
+
+ // Find all addresses (low_pc) which contain deduped methods.
+ // The first instance of method is not marked deduped_, but the rest is.
+ std::unordered_set<uint32_t> deduped_addresses;
+ for (const MethodDebugInfo& info : method_infos) {
+ if (info.deduped) {
+ deduped_addresses.insert(info.low_pc);
+ }
+ }
+
+ strtab->Start();
+ strtab->Write(""); // strtab should start with empty string.
+ std::string last_name;
+ size_t last_name_offset = 0;
+ for (const MethodDebugInfo& info : method_infos) {
+ if (info.deduped) {
+ continue; // Add symbol only for the first instance.
+ }
+ std::string name = PrettyMethod(info.dex_method_index, *info.dex_file, with_signature);
+ if (deduped_addresses.find(info.low_pc) != deduped_addresses.end()) {
+ name += " [DEDUPED]";
+ }
+ // If we write method names without signature, we might see the same name multiple times.
+ size_t name_offset = (name == last_name ? last_name_offset : strtab->Write(name));
+
+ const auto* text = builder->GetText()->Exists() ? builder->GetText() : nullptr;
+ const bool is_relative = (text != nullptr);
+ uint32_t low_pc = info.low_pc;
+ // Add in code delta, e.g., thumb bit 0 for Thumb2 code.
+ low_pc += info.compiled_method->CodeDelta();
+ symtab->Add(name_offset,
+ text,
+ low_pc,
+ is_relative,
+ info.high_pc - info.low_pc,
+ STB_GLOBAL,
+ STT_FUNC);
+
+ // Conforming to aaelf, add $t mapping symbol to indicate start of a sequence of thumb2
+ // instructions, so that disassembler tools can correctly disassemble.
+ // Note that even if we generate just a single mapping symbol, ARM's Streamline
+ // requires it to match function symbol. Just address 0 does not work.
+ if (info.compiled_method->GetInstructionSet() == kThumb2) {
+ if (!generated_mapping_symbol || !kGenerateSingleArmMappingSymbol) {
+ symtab->Add(strtab->Write("$t"), text, info.low_pc & ~1,
+ is_relative, 0, STB_LOCAL, STT_NOTYPE);
+ generated_mapping_symbol = true;
+ }
+ }
+
+ last_name = std::move(name);
+ last_name_offset = name_offset;
+ }
+ strtab->End();
+
+ // Symbols are buffered and written after names (because they are smaller).
+ // We could also do two passes in this function to avoid the buffering.
+ symtab->Start();
+ symtab->Write();
+ symtab->End();
+}
+
+} // namespace debug
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_ELF_SYMTAB_WRITER_H_
+
diff --git a/compiler/debug/method_debug_info.h b/compiler/debug/method_debug_info.h
new file mode 100644
index 0000000..6b3dd8c
--- /dev/null
+++ b/compiler/debug/method_debug_info.h
@@ -0,0 +1,48 @@
+/*
+ * 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_DEBUG_METHOD_DEBUG_INFO_H_
+#define ART_COMPILER_DEBUG_METHOD_DEBUG_INFO_H_
+
+#include "compiled_method.h"
+#include "dex_file.h"
+
+namespace art {
+namespace debug {
+
+struct MethodDebugInfo {
+ const DexFile* dex_file;
+ size_t class_def_index;
+ uint32_t dex_method_index;
+ uint32_t access_flags;
+ const DexFile::CodeItem* code_item;
+ bool deduped;
+ uintptr_t low_pc;
+ uintptr_t high_pc;
+ CompiledMethod* compiled_method;
+
+ bool IsFromOptimizingCompiler() const {
+ return compiled_method->GetQuickCode().size() > 0 &&
+ compiled_method->GetVmapTable().size() > 0 &&
+ compiled_method->GetGcMap().size() == 0 &&
+ code_item != nullptr;
+ }
+};
+
+} // namespace debug
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_METHOD_DEBUG_INFO_H_
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index 22b178c..209f101 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -875,6 +875,7 @@
move_result = mir_graph->FindMoveResult(bb, invoke);
result = GenInlineIPut(mir_graph, bb, invoke, move_result, method);
break;
+ case kInlineOpConstructor:
case kInlineStringInit:
return false;
default:
diff --git a/compiler/dex/quick/lazy_debug_frame_opcode_writer.h b/compiler/dex/quick/lazy_debug_frame_opcode_writer.h
index c425fc8..85050f4 100644
--- a/compiler/dex/quick/lazy_debug_frame_opcode_writer.h
+++ b/compiler/dex/quick/lazy_debug_frame_opcode_writer.h
@@ -19,7 +19,7 @@
#include "base/arena_allocator.h"
#include "base/arena_containers.h"
-#include "dwarf/debug_frame_opcode_writer.h"
+#include "debug/dwarf/debug_frame_opcode_writer.h"
namespace art {
struct LIR;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 6bc2a13..f078bf6 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -2306,9 +2306,9 @@
mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, soa.Self());
// Mark methods as pre-verified. If we don't do this, the interpreter will run with
// access checks.
- klass->SetPreverifiedFlagOnAllMethods(
+ klass->SetSkipAccessChecksFlagOnAllMethods(
GetInstructionSetPointerSize(manager_->GetCompiler()->GetInstructionSet()));
- klass->SetPreverified();
+ klass->SetVerificationAttempted();
}
// Record the final class status if necessary.
ClassReference ref(manager_->GetDexFile(), class_def_index);
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 4c03e5d..4785885 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -249,9 +249,9 @@
ProfileCompilationInfo info;
for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
- std::cout << std::string(dex_file->GetLocation());
- profile_info_.AddData(dex_file->GetLocation(), dex_file->GetLocationChecksum(), 1);
- profile_info_.AddData(dex_file->GetLocation(), dex_file->GetLocationChecksum(), 2);
+ std::string key = ProfileCompilationInfo::GetProfileDexFileKey(dex_file->GetLocation());
+ profile_info_.AddData(key, dex_file->GetLocationChecksum(), 1);
+ profile_info_.AddData(key, dex_file->GetLocationChecksum(), 2);
}
return &profile_info_;
}
diff --git a/compiler/dwarf/method_debug_info.h b/compiler/dwarf/method_debug_info.h
deleted file mode 100644
index e8ba914..0000000
--- a/compiler/dwarf/method_debug_info.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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_METHOD_DEBUG_INFO_H_
-#define ART_COMPILER_DWARF_METHOD_DEBUG_INFO_H_
-
-#include "dex_file.h"
-
-namespace art {
-class CompiledMethod;
-namespace dwarf {
-
-struct MethodDebugInfo {
- const DexFile* dex_file_;
- size_t class_def_index_;
- uint32_t dex_method_index_;
- uint32_t access_flags_;
- const DexFile::CodeItem* code_item_;
- bool deduped_;
- uintptr_t low_pc_;
- uintptr_t high_pc_;
- CompiledMethod* compiled_method_;
-};
-
-} // namespace dwarf
-} // namespace art
-
-#endif // ART_COMPILER_DWARF_METHOD_DEBUG_INFO_H_
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index 3d24d19..b673eeb 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -110,18 +110,27 @@
CHECK(sections.empty() || sections.back()->finished_);
// The first ELF section index is 1. Index 0 is reserved for NULL.
section_index_ = sections.size() + 1;
- // Push this section on the list of written sections.
- sections.push_back(this);
+ // Page-align if we switch between allocated and non-allocated sections,
+ // or if we change the type of allocation (e.g. executable vs non-executable).
+ if (!sections.empty()) {
+ if (header_.sh_flags != sections.back()->header_.sh_flags) {
+ header_.sh_addralign = kPageSize;
+ }
+ }
// Align file position.
if (header_.sh_type != SHT_NOBITS) {
- header_.sh_offset = RoundUp(owner_->stream_.Seek(0, kSeekCurrent), header_.sh_addralign);
- owner_->stream_.Seek(header_.sh_offset, kSeekSet);
+ header_.sh_offset = owner_->AlignFileOffset(header_.sh_addralign);
+ } else {
+ header_.sh_offset = 0;
}
// Align virtual memory address.
if ((header_.sh_flags & SHF_ALLOC) != 0) {
- header_.sh_addr = RoundUp(owner_->virtual_address_, header_.sh_addralign);
- owner_->virtual_address_ = header_.sh_addr;
+ header_.sh_addr = owner_->AlignVirtualAddress(header_.sh_addralign);
+ } else {
+ header_.sh_addr = 0;
}
+ // Push this section on the list of written sections.
+ sections.push_back(this);
}
// Finish writing of this section.
@@ -170,8 +179,8 @@
// and it will be zero-initialized when the ELF file is loaded in the running program.
void WriteNoBitsSection(Elf_Word size) {
DCHECK_NE(header_.sh_flags & SHF_ALLOC, 0u);
- Start();
header_.sh_type = SHT_NOBITS;
+ Start();
header_.sh_size = size;
End();
}
@@ -293,12 +302,13 @@
dynamic_(this, ".dynamic", SHT_DYNAMIC, SHF_ALLOC, &dynstr_, 0, kPageSize, sizeof(Elf_Dyn)),
eh_frame_(this, ".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0),
eh_frame_hdr_(this, ".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0),
- strtab_(this, ".strtab", 0, kPageSize),
+ strtab_(this, ".strtab", 0, 1),
symtab_(this, ".symtab", SHT_SYMTAB, 0, &strtab_),
debug_frame_(this, ".debug_frame", SHT_PROGBITS, 0, nullptr, 0, sizeof(Elf_Addr), 0),
debug_info_(this, ".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0),
debug_line_(this, ".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0),
shstrtab_(this, ".shstrtab", 0, 1),
+ started_(false),
virtual_address_(0) {
text_.phdr_flags_ = PF_R | PF_X;
bss_.phdr_flags_ = PF_R | PF_W;
@@ -351,22 +361,25 @@
other_sections_.push_back(std::move(s));
}
- // Set where the next section will be allocated in the virtual address space.
- void SetVirtualAddress(Elf_Addr address) {
- DCHECK_GE(address, virtual_address_);
- virtual_address_ = address;
- }
-
- void Start() {
- // Reserve space for ELF header and program headers.
- // We do not know the number of headers until later, so
- // it is easiest to just reserve a fixed amount of space.
- int size = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * kMaxProgramHeaders;
+ // Reserve space for ELF header and program headers.
+ // We do not know the number of headers until later, so
+ // it is easiest to just reserve a fixed amount of space.
+ // Program headers are required for loading by the linker.
+ // It is possible to omit them for ELF files used for debugging.
+ void Start(bool write_program_headers = true) {
+ int size = sizeof(Elf_Ehdr);
+ if (write_program_headers) {
+ size += sizeof(Elf_Phdr) * kMaxProgramHeaders;
+ }
stream_.Seek(size, kSeekSet);
+ started_ = true;
virtual_address_ += size;
+ write_program_headers_ = write_program_headers;
}
void End() {
+ DCHECK(started_);
+
// Write section names and finish the section headers.
shstrtab_.Start();
shstrtab_.Write("");
@@ -386,8 +399,7 @@
shdrs.push_back(section->header_);
}
Elf_Off section_headers_offset;
- section_headers_offset = RoundUp(stream_.Seek(0, kSeekCurrent), sizeof(Elf_Off));
- stream_.Seek(section_headers_offset, kSeekSet);
+ section_headers_offset = AlignFileOffset(sizeof(Elf_Off));
stream_.WriteFully(shdrs.data(), shdrs.size() * sizeof(shdrs[0]));
// Flush everything else before writing the program headers. This should prevent
@@ -395,14 +407,21 @@
// and partially written data if we suddenly lose power, for example.
stream_.Flush();
- // Write the initial file headers.
- std::vector<Elf_Phdr> phdrs = MakeProgramHeaders();
+ // The main ELF header.
Elf_Ehdr elf_header = MakeElfHeader(isa_);
- elf_header.e_phoff = sizeof(Elf_Ehdr);
elf_header.e_shoff = section_headers_offset;
- elf_header.e_phnum = phdrs.size();
elf_header.e_shnum = shdrs.size();
elf_header.e_shstrndx = shstrtab_.GetSectionIndex();
+
+ // Program headers (i.e. mmap instructions).
+ std::vector<Elf_Phdr> phdrs;
+ if (write_program_headers_) {
+ phdrs = MakeProgramHeaders();
+ CHECK_LE(phdrs.size(), kMaxProgramHeaders);
+ elf_header.e_phoff = sizeof(Elf_Ehdr);
+ elf_header.e_phnum = phdrs.size();
+ }
+
stream_.Seek(0, kSeekSet);
stream_.WriteFully(&elf_header, sizeof(elf_header));
stream_.WriteFully(phdrs.data(), phdrs.size() * sizeof(phdrs[0]));
@@ -492,6 +511,14 @@
return &stream_;
}
+ off_t AlignFileOffset(size_t alignment) {
+ return stream_.Seek(RoundUp(stream_.Seek(0, kSeekCurrent), alignment), kSeekSet);
+ }
+
+ Elf_Addr AlignVirtualAddress(size_t alignment) {
+ return virtual_address_ = RoundUp(virtual_address_, alignment);
+ }
+
private:
static Elf_Ehdr MakeElfHeader(InstructionSet isa) {
Elf_Ehdr elf_header = Elf_Ehdr();
@@ -666,9 +693,13 @@
// List of used section in the order in which they were written.
std::vector<Section*> sections_;
+ bool started_;
+
// Used for allocation of virtual address space.
Elf_Addr virtual_address_;
+ size_t write_program_headers_;
+
DISALLOW_COPY_AND_ASSIGN(ElfBuilder);
};
diff --git a/compiler/elf_writer.h b/compiler/elf_writer.h
index c5a0fd5..d50a08c 100644
--- a/compiler/elf_writer.h
+++ b/compiler/elf_writer.h
@@ -32,9 +32,9 @@
class ElfFile;
class OutputStream;
-namespace dwarf {
+namespace debug {
struct MethodDebugInfo;
-} // namespace dwarf
+} // namespace debug
class ElfWriter {
public:
@@ -52,13 +52,16 @@
virtual ~ElfWriter() {}
virtual void Start() = 0;
+ virtual void PrepareDebugInfo(size_t rodata_section_size,
+ size_t text_section_size,
+ const ArrayRef<const debug::MethodDebugInfo>& method_infos) = 0;
virtual OutputStream* StartRoData() = 0;
virtual void EndRoData(OutputStream* rodata) = 0;
virtual OutputStream* StartText() = 0;
virtual void EndText(OutputStream* text) = 0;
virtual void SetBssSize(size_t bss_size) = 0;
virtual void WriteDynamicSection() = 0;
- virtual void WriteDebugInfo(const ArrayRef<const dwarf::MethodDebugInfo>& method_infos) = 0;
+ virtual void WriteDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) = 0;
virtual void WritePatchLocations(const ArrayRef<const uintptr_t>& patch_locations) = 0;
virtual bool End() = 0;
diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc
deleted file mode 100644
index d1f5007..0000000
--- a/compiler/elf_writer_debug.cc
+++ /dev/null
@@ -1,1646 +0,0 @@
-/*
- * 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 "elf_writer_debug.h"
-
-#include <algorithm>
-#include <unordered_set>
-#include <vector>
-#include <cstdio>
-
-#include "base/casts.h"
-#include "base/stl_util.h"
-#include "linear_alloc.h"
-#include "compiled_method.h"
-#include "dex_file-inl.h"
-#include "driver/compiler_driver.h"
-#include "dwarf/expression.h"
-#include "dwarf/headers.h"
-#include "dwarf/method_debug_info.h"
-#include "dwarf/register.h"
-#include "elf_builder.h"
-#include "linker/vector_output_stream.h"
-#include "mirror/array.h"
-#include "mirror/class-inl.h"
-#include "mirror/class.h"
-#include "oat_writer.h"
-#include "stack_map.h"
-#include "utils.h"
-
-// liblzma.
-#include "XzEnc.h"
-#include "7zCrc.h"
-#include "XzCrc64.h"
-
-namespace art {
-namespace dwarf {
-
-// The ARM specification defines three special mapping symbols
-// $a, $t and $d which mark ARM, Thumb and data ranges respectively.
-// These symbols can be used by tools, for example, to pretty
-// print instructions correctly. Objdump will use them if they
-// exist, but it will still work well without them.
-// However, these extra symbols take space, so let's just generate
-// one symbol which marks the whole .text section as code.
-constexpr bool kGenerateSingleArmMappingSymbol = true;
-
-static Reg GetDwarfCoreReg(InstructionSet isa, int machine_reg) {
- switch (isa) {
- case kArm:
- case kThumb2:
- return Reg::ArmCore(machine_reg);
- case kArm64:
- return Reg::Arm64Core(machine_reg);
- case kX86:
- return Reg::X86Core(machine_reg);
- case kX86_64:
- return Reg::X86_64Core(machine_reg);
- case kMips:
- return Reg::MipsCore(machine_reg);
- case kMips64:
- return Reg::Mips64Core(machine_reg);
- default:
- LOG(FATAL) << "Unknown instruction set: " << isa;
- UNREACHABLE();
- }
-}
-
-static Reg GetDwarfFpReg(InstructionSet isa, int machine_reg) {
- switch (isa) {
- case kArm:
- case kThumb2:
- return Reg::ArmFp(machine_reg);
- case kArm64:
- return Reg::Arm64Fp(machine_reg);
- case kX86:
- return Reg::X86Fp(machine_reg);
- case kX86_64:
- return Reg::X86_64Fp(machine_reg);
- case kMips:
- return Reg::MipsFp(machine_reg);
- case kMips64:
- return Reg::Mips64Fp(machine_reg);
- default:
- LOG(FATAL) << "Unknown instruction set: " << isa;
- UNREACHABLE();
- }
-}
-
-static void WriteCIE(InstructionSet isa,
- CFIFormat format,
- std::vector<uint8_t>* buffer) {
- // Scratch registers should be marked as undefined. This tells the
- // debugger that its value in the previous frame is not recoverable.
- bool is64bit = Is64BitInstructionSet(isa);
- switch (isa) {
- case kArm:
- case kThumb2: {
- DebugFrameOpCodeWriter<> opcodes;
- opcodes.DefCFA(Reg::ArmCore(13), 0); // R13(SP).
- // core registers.
- for (int reg = 0; reg < 13; reg++) {
- if (reg < 4 || reg == 12) {
- opcodes.Undefined(Reg::ArmCore(reg));
- } else {
- opcodes.SameValue(Reg::ArmCore(reg));
- }
- }
- // fp registers.
- for (int reg = 0; reg < 32; reg++) {
- if (reg < 16) {
- opcodes.Undefined(Reg::ArmFp(reg));
- } else {
- opcodes.SameValue(Reg::ArmFp(reg));
- }
- }
- auto return_reg = Reg::ArmCore(14); // R14(LR).
- WriteCIE(is64bit, return_reg, opcodes, format, buffer);
- return;
- }
- case kArm64: {
- DebugFrameOpCodeWriter<> opcodes;
- opcodes.DefCFA(Reg::Arm64Core(31), 0); // R31(SP).
- // core registers.
- for (int reg = 0; reg < 30; reg++) {
- if (reg < 8 || reg == 16 || reg == 17) {
- opcodes.Undefined(Reg::Arm64Core(reg));
- } else {
- opcodes.SameValue(Reg::Arm64Core(reg));
- }
- }
- // fp registers.
- for (int reg = 0; reg < 32; reg++) {
- if (reg < 8 || reg >= 16) {
- opcodes.Undefined(Reg::Arm64Fp(reg));
- } else {
- opcodes.SameValue(Reg::Arm64Fp(reg));
- }
- }
- auto return_reg = Reg::Arm64Core(30); // R30(LR).
- WriteCIE(is64bit, return_reg, opcodes, format, buffer);
- return;
- }
- case kMips:
- case kMips64: {
- DebugFrameOpCodeWriter<> opcodes;
- opcodes.DefCFA(Reg::MipsCore(29), 0); // R29(SP).
- // core registers.
- for (int reg = 1; reg < 26; reg++) {
- if (reg < 16 || reg == 24 || reg == 25) { // AT, V*, A*, T*.
- opcodes.Undefined(Reg::MipsCore(reg));
- } else {
- opcodes.SameValue(Reg::MipsCore(reg));
- }
- }
- // fp registers.
- for (int reg = 0; reg < 32; reg++) {
- if (reg < 24) {
- opcodes.Undefined(Reg::Mips64Fp(reg));
- } else {
- opcodes.SameValue(Reg::Mips64Fp(reg));
- }
- }
- auto return_reg = Reg::MipsCore(31); // R31(RA).
- WriteCIE(is64bit, return_reg, opcodes, format, buffer);
- return;
- }
- case kX86: {
- // FIXME: Add fp registers once libunwind adds support for them. Bug: 20491296
- constexpr bool generate_opcodes_for_x86_fp = false;
- DebugFrameOpCodeWriter<> opcodes;
- opcodes.DefCFA(Reg::X86Core(4), 4); // R4(ESP).
- opcodes.Offset(Reg::X86Core(8), -4); // R8(EIP).
- // core registers.
- for (int reg = 0; reg < 8; reg++) {
- if (reg <= 3) {
- opcodes.Undefined(Reg::X86Core(reg));
- } else if (reg == 4) {
- // Stack pointer.
- } else {
- opcodes.SameValue(Reg::X86Core(reg));
- }
- }
- // fp registers.
- if (generate_opcodes_for_x86_fp) {
- for (int reg = 0; reg < 8; reg++) {
- opcodes.Undefined(Reg::X86Fp(reg));
- }
- }
- auto return_reg = Reg::X86Core(8); // R8(EIP).
- WriteCIE(is64bit, return_reg, opcodes, format, buffer);
- return;
- }
- case kX86_64: {
- DebugFrameOpCodeWriter<> opcodes;
- opcodes.DefCFA(Reg::X86_64Core(4), 8); // R4(RSP).
- opcodes.Offset(Reg::X86_64Core(16), -8); // R16(RIP).
- // core registers.
- for (int reg = 0; reg < 16; reg++) {
- if (reg == 4) {
- // Stack pointer.
- } else if (reg < 12 && reg != 3 && reg != 5) { // except EBX and EBP.
- opcodes.Undefined(Reg::X86_64Core(reg));
- } else {
- opcodes.SameValue(Reg::X86_64Core(reg));
- }
- }
- // fp registers.
- for (int reg = 0; reg < 16; reg++) {
- if (reg < 12) {
- opcodes.Undefined(Reg::X86_64Fp(reg));
- } else {
- opcodes.SameValue(Reg::X86_64Fp(reg));
- }
- }
- auto return_reg = Reg::X86_64Core(16); // R16(RIP).
- WriteCIE(is64bit, return_reg, opcodes, format, buffer);
- return;
- }
- case kNone:
- break;
- }
- LOG(FATAL) << "Cannot write CIE frame for ISA " << isa;
- UNREACHABLE();
-}
-
-template<typename ElfTypes>
-void WriteCFISection(ElfBuilder<ElfTypes>* builder,
- const ArrayRef<const MethodDebugInfo>& method_infos,
- CFIFormat format,
- bool write_oat_patches) {
- CHECK(format == DW_DEBUG_FRAME_FORMAT || format == DW_EH_FRAME_FORMAT);
- typedef typename ElfTypes::Addr Elf_Addr;
-
- if (method_infos.empty()) {
- return;
- }
-
- std::vector<uint32_t> binary_search_table;
- std::vector<uintptr_t> patch_locations;
- if (format == DW_EH_FRAME_FORMAT) {
- binary_search_table.reserve(2 * method_infos.size());
- } else {
- patch_locations.reserve(method_infos.size());
- }
-
- // The methods can be written any order.
- // Let's therefore sort them in the lexicographical order of the opcodes.
- // This has no effect on its own. However, if the final .debug_frame section is
- // compressed it reduces the size since similar opcodes sequences are grouped.
- std::vector<const MethodDebugInfo*> sorted_method_infos;
- sorted_method_infos.reserve(method_infos.size());
- for (size_t i = 0; i < method_infos.size(); i++) {
- sorted_method_infos.push_back(&method_infos[i]);
- }
- std::sort(
- sorted_method_infos.begin(),
- sorted_method_infos.end(),
- [](const MethodDebugInfo* lhs, const MethodDebugInfo* rhs) {
- ArrayRef<const uint8_t> l = lhs->compiled_method_->GetCFIInfo();
- ArrayRef<const uint8_t> r = rhs->compiled_method_->GetCFIInfo();
- return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
- });
-
- // Write .eh_frame/.debug_frame section.
- auto* cfi_section = (format == DW_DEBUG_FRAME_FORMAT
- ? builder->GetDebugFrame()
- : builder->GetEhFrame());
- {
- cfi_section->Start();
- const bool is64bit = Is64BitInstructionSet(builder->GetIsa());
- const Elf_Addr text_address = builder->GetText()->Exists()
- ? builder->GetText()->GetAddress()
- : 0;
- const Elf_Addr cfi_address = cfi_section->GetAddress();
- const Elf_Addr cie_address = cfi_address;
- Elf_Addr buffer_address = cfi_address;
- std::vector<uint8_t> buffer; // Small temporary buffer.
- WriteCIE(builder->GetIsa(), format, &buffer);
- cfi_section->WriteFully(buffer.data(), buffer.size());
- buffer_address += buffer.size();
- buffer.clear();
- for (const MethodDebugInfo* mi : sorted_method_infos) {
- if (!mi->deduped_) { // Only one FDE per unique address.
- ArrayRef<const uint8_t> opcodes = mi->compiled_method_->GetCFIInfo();
- if (!opcodes.empty()) {
- const Elf_Addr code_address = text_address + mi->low_pc_;
- if (format == DW_EH_FRAME_FORMAT) {
- binary_search_table.push_back(
- dchecked_integral_cast<uint32_t>(code_address));
- binary_search_table.push_back(
- dchecked_integral_cast<uint32_t>(buffer_address));
- }
- WriteFDE(is64bit, cfi_address, cie_address,
- code_address, mi->high_pc_ - mi->low_pc_,
- opcodes, format, buffer_address, &buffer,
- &patch_locations);
- cfi_section->WriteFully(buffer.data(), buffer.size());
- buffer_address += buffer.size();
- buffer.clear();
- }
- }
- }
- cfi_section->End();
- }
-
- if (format == DW_EH_FRAME_FORMAT) {
- auto* header_section = builder->GetEhFrameHdr();
- header_section->Start();
- uint32_t header_address = dchecked_integral_cast<int32_t>(header_section->GetAddress());
- // Write .eh_frame_hdr section.
- std::vector<uint8_t> buffer;
- Writer<> header(&buffer);
- header.PushUint8(1); // Version.
- // Encoding of .eh_frame pointer - libunwind does not honor datarel here,
- // so we have to use pcrel which means relative to the pointer's location.
- header.PushUint8(DW_EH_PE_pcrel | DW_EH_PE_sdata4);
- // Encoding of binary search table size.
- header.PushUint8(DW_EH_PE_udata4);
- // Encoding of binary search table addresses - libunwind supports only this
- // specific combination, which means relative to the start of .eh_frame_hdr.
- header.PushUint8(DW_EH_PE_datarel | DW_EH_PE_sdata4);
- // .eh_frame pointer
- header.PushInt32(cfi_section->GetAddress() - (header_address + 4u));
- // Binary search table size (number of entries).
- header.PushUint32(dchecked_integral_cast<uint32_t>(binary_search_table.size()/2));
- header_section->WriteFully(buffer.data(), buffer.size());
- // Binary search table.
- for (size_t i = 0; i < binary_search_table.size(); i++) {
- // Make addresses section-relative since we know the header address now.
- binary_search_table[i] -= header_address;
- }
- header_section->WriteFully(binary_search_table.data(), binary_search_table.size());
- header_section->End();
- } else {
- if (write_oat_patches) {
- builder->WritePatches(".debug_frame.oat_patches",
- ArrayRef<const uintptr_t>(patch_locations));
- }
- }
-}
-
-namespace {
- struct CompilationUnit {
- std::vector<const MethodDebugInfo*> methods_;
- size_t debug_line_offset_ = 0;
- uintptr_t low_pc_ = std::numeric_limits<uintptr_t>::max();
- uintptr_t high_pc_ = 0;
- };
-
- typedef std::vector<DexFile::LocalInfo> LocalInfos;
-
- void LocalInfoCallback(void* ctx, const DexFile::LocalInfo& entry) {
- static_cast<LocalInfos*>(ctx)->push_back(entry);
- }
-
- typedef std::vector<DexFile::PositionInfo> PositionInfos;
-
- bool PositionInfoCallback(void* ctx, const DexFile::PositionInfo& entry) {
- static_cast<PositionInfos*>(ctx)->push_back(entry);
- return false;
- }
-
- std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) {
- std::vector<const char*> names;
- if (mi->code_item_ != nullptr) {
- const uint8_t* stream = mi->dex_file_->GetDebugInfoStream(mi->code_item_);
- if (stream != nullptr) {
- DecodeUnsignedLeb128(&stream); // line.
- uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
- for (uint32_t i = 0; i < parameters_size; ++i) {
- uint32_t id = DecodeUnsignedLeb128P1(&stream);
- names.push_back(mi->dex_file_->StringDataByIdx(id));
- }
- }
- }
- return names;
- }
-
- struct VariableLocation {
- uint32_t low_pc;
- uint32_t high_pc;
- DexRegisterLocation reg_lo; // May be None if the location is unknown.
- DexRegisterLocation reg_hi; // Most significant bits of 64-bit value.
- };
-
- // Get the location of given dex register (e.g. stack or machine register).
- // Note that the location might be different based on the current pc.
- // The result will cover all ranges where the variable is in scope.
- std::vector<VariableLocation> GetVariableLocations(const MethodDebugInfo* method_info,
- uint16_t vreg,
- bool is64bitValue,
- uint32_t dex_pc_low,
- uint32_t dex_pc_high) {
- std::vector<VariableLocation> variable_locations;
-
- // Get stack maps sorted by pc (they might not be sorted internally).
- const CodeInfo code_info(method_info->compiled_method_->GetVmapTable().data());
- const StackMapEncoding encoding = code_info.ExtractEncoding();
- std::map<uint32_t, StackMap> stack_maps;
- for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) {
- StackMap stack_map = code_info.GetStackMapAt(s, encoding);
- DCHECK(stack_map.IsValid());
- const uint32_t low_pc = method_info->low_pc_ + stack_map.GetNativePcOffset(encoding);
- DCHECK_LE(low_pc, method_info->high_pc_);
- stack_maps.emplace(low_pc, stack_map);
- }
-
- // Create entries for the requested register based on stack map data.
- for (auto it = stack_maps.begin(); it != stack_maps.end(); it++) {
- const StackMap& stack_map = it->second;
- const uint32_t low_pc = it->first;
- auto next_it = it;
- next_it++;
- const uint32_t high_pc = next_it != stack_maps.end() ? next_it->first
- : method_info->high_pc_;
- DCHECK_LE(low_pc, high_pc);
- if (low_pc == high_pc) {
- continue; // Ignore if the address range is empty.
- }
-
- // Check that the stack map is in the requested range.
- uint32_t dex_pc = stack_map.GetDexPc(encoding);
- if (!(dex_pc_low <= dex_pc && dex_pc < dex_pc_high)) {
- continue;
- }
-
- // Find the location of the dex register.
- DexRegisterLocation reg_lo = DexRegisterLocation::None();
- DexRegisterLocation reg_hi = DexRegisterLocation::None();
- if (stack_map.HasDexRegisterMap(encoding)) {
- DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(
- stack_map, encoding, method_info->code_item_->registers_size_);
- reg_lo = dex_register_map.GetDexRegisterLocation(
- vreg, method_info->code_item_->registers_size_, code_info, encoding);
- if (is64bitValue) {
- reg_hi = dex_register_map.GetDexRegisterLocation(
- vreg + 1, method_info->code_item_->registers_size_, code_info, encoding);
- }
- }
-
- // Add location entry for this address range.
- if (!variable_locations.empty() &&
- variable_locations.back().reg_lo == reg_lo &&
- variable_locations.back().reg_hi == reg_hi &&
- variable_locations.back().high_pc == low_pc) {
- // Merge with the previous entry (extend its range).
- variable_locations.back().high_pc = high_pc;
- } else {
- variable_locations.push_back({low_pc, high_pc, reg_lo, reg_hi});
- }
- }
-
- return variable_locations;
- }
-
- bool IsFromOptimizingCompiler(const MethodDebugInfo* method_info) {
- return method_info->compiled_method_->GetQuickCode().size() > 0 &&
- method_info->compiled_method_->GetVmapTable().size() > 0 &&
- method_info->compiled_method_->GetGcMap().size() == 0 &&
- method_info->code_item_ != nullptr;
- }
-} // namespace
-
-// Helper class to write .debug_info and its supporting sections.
-template<typename ElfTypes>
-class DebugInfoWriter {
- typedef typename ElfTypes::Addr Elf_Addr;
-
- // Helper class to write one compilation unit.
- // It holds helper methods and temporary state.
- class CompilationUnitWriter {
- public:
- explicit CompilationUnitWriter(DebugInfoWriter* owner)
- : owner_(owner),
- info_(Is64BitInstructionSet(owner_->builder_->GetIsa()), &owner->debug_abbrev_) {
- }
-
- void Write(const CompilationUnit& compilation_unit) {
- CHECK(!compilation_unit.methods_.empty());
- const Elf_Addr text_address = owner_->builder_->GetText()->Exists()
- ? owner_->builder_->GetText()->GetAddress()
- : 0;
- const uintptr_t cu_size = compilation_unit.high_pc_ - compilation_unit.low_pc_;
-
- info_.StartTag(DW_TAG_compile_unit);
- info_.WriteString(DW_AT_producer, "Android dex2oat");
- info_.WriteData1(DW_AT_language, DW_LANG_Java);
- info_.WriteString(DW_AT_comp_dir, "$JAVA_SRC_ROOT");
- info_.WriteAddr(DW_AT_low_pc, text_address + compilation_unit.low_pc_);
- info_.WriteUdata(DW_AT_high_pc, dchecked_integral_cast<uint32_t>(cu_size));
- info_.WriteSecOffset(DW_AT_stmt_list, compilation_unit.debug_line_offset_);
-
- const char* last_dex_class_desc = nullptr;
- for (auto mi : compilation_unit.methods_) {
- const DexFile* dex = mi->dex_file_;
- const DexFile::CodeItem* dex_code = mi->code_item_;
- const DexFile::MethodId& dex_method = dex->GetMethodId(mi->dex_method_index_);
- const DexFile::ProtoId& dex_proto = dex->GetMethodPrototype(dex_method);
- const DexFile::TypeList* dex_params = dex->GetProtoParameters(dex_proto);
- const char* dex_class_desc = dex->GetMethodDeclaringClassDescriptor(dex_method);
- const bool is_static = (mi->access_flags_ & kAccStatic) != 0;
-
- // Enclose the method in correct class definition.
- if (last_dex_class_desc != dex_class_desc) {
- if (last_dex_class_desc != nullptr) {
- EndClassTag();
- }
- // Write reference tag for the class we are about to declare.
- size_t reference_tag_offset = info_.StartTag(DW_TAG_reference_type);
- type_cache_.emplace(std::string(dex_class_desc), reference_tag_offset);
- size_t type_attrib_offset = info_.size();
- info_.WriteRef4(DW_AT_type, 0);
- info_.EndTag();
- // Declare the class that owns this method.
- size_t class_offset = StartClassTag(dex_class_desc);
- info_.UpdateUint32(type_attrib_offset, class_offset);
- info_.WriteFlagPresent(DW_AT_declaration);
- // Check that each class is defined only once.
- bool unique = owner_->defined_dex_classes_.insert(dex_class_desc).second;
- CHECK(unique) << "Redefinition of " << dex_class_desc;
- last_dex_class_desc = dex_class_desc;
- }
-
- int start_depth = info_.Depth();
- info_.StartTag(DW_TAG_subprogram);
- WriteName(dex->GetMethodName(dex_method));
- info_.WriteAddr(DW_AT_low_pc, text_address + mi->low_pc_);
- info_.WriteUdata(DW_AT_high_pc, dchecked_integral_cast<uint32_t>(mi->high_pc_-mi->low_pc_));
- std::vector<uint8_t> expr_buffer;
- Expression expr(&expr_buffer);
- expr.WriteOpCallFrameCfa();
- info_.WriteExprLoc(DW_AT_frame_base, expr);
- WriteLazyType(dex->GetReturnTypeDescriptor(dex_proto));
-
- // Write parameters. DecodeDebugLocalInfo returns them as well, but it does not
- // guarantee order or uniqueness so it is safer to iterate over them manually.
- // DecodeDebugLocalInfo might not also be available if there is no debug info.
- std::vector<const char*> param_names = GetParamNames(mi);
- uint32_t arg_reg = 0;
- if (!is_static) {
- info_.StartTag(DW_TAG_formal_parameter);
- WriteName("this");
- info_.WriteFlagPresent(DW_AT_artificial);
- WriteLazyType(dex_class_desc);
- if (dex_code != nullptr) {
- // Write the stack location of the parameter.
- const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg;
- const bool is64bitValue = false;
- WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc_);
- }
- arg_reg++;
- info_.EndTag();
- }
- if (dex_params != nullptr) {
- for (uint32_t i = 0; i < dex_params->Size(); ++i) {
- info_.StartTag(DW_TAG_formal_parameter);
- // Parameter names may not be always available.
- if (i < param_names.size()) {
- WriteName(param_names[i]);
- }
- // Write the type.
- const char* type_desc = dex->StringByTypeIdx(dex_params->GetTypeItem(i).type_idx_);
- WriteLazyType(type_desc);
- const bool is64bitValue = type_desc[0] == 'D' || type_desc[0] == 'J';
- if (dex_code != nullptr) {
- // Write the stack location of the parameter.
- const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg;
- WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc_);
- }
- arg_reg += is64bitValue ? 2 : 1;
- info_.EndTag();
- }
- if (dex_code != nullptr) {
- DCHECK_EQ(arg_reg, dex_code->ins_size_);
- }
- }
-
- // Write local variables.
- LocalInfos local_infos;
- if (dex->DecodeDebugLocalInfo(dex_code,
- is_static,
- mi->dex_method_index_,
- LocalInfoCallback,
- &local_infos)) {
- for (const DexFile::LocalInfo& var : local_infos) {
- if (var.reg_ < dex_code->registers_size_ - dex_code->ins_size_) {
- info_.StartTag(DW_TAG_variable);
- WriteName(var.name_);
- WriteLazyType(var.descriptor_);
- bool is64bitValue = var.descriptor_[0] == 'D' || var.descriptor_[0] == 'J';
- WriteRegLocation(mi, var.reg_, is64bitValue, compilation_unit.low_pc_,
- var.start_address_, var.end_address_);
- info_.EndTag();
- }
- }
- }
-
- info_.EndTag();
- CHECK_EQ(info_.Depth(), start_depth); // Balanced start/end.
- }
- if (last_dex_class_desc != nullptr) {
- EndClassTag();
- }
- FinishLazyTypes();
- CloseNamespacesAboveDepth(0);
- info_.EndTag(); // DW_TAG_compile_unit
- CHECK_EQ(info_.Depth(), 0);
- std::vector<uint8_t> buffer;
- buffer.reserve(info_.data()->size() + KB);
- const size_t offset = owner_->builder_->GetDebugInfo()->GetSize();
- // All compilation units share single table which is at the start of .debug_abbrev.
- const size_t debug_abbrev_offset = 0;
- WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_);
- owner_->builder_->GetDebugInfo()->WriteFully(buffer.data(), buffer.size());
- }
-
- void Write(const ArrayRef<mirror::Class*>& types) SHARED_REQUIRES(Locks::mutator_lock_) {
- info_.StartTag(DW_TAG_compile_unit);
- info_.WriteString(DW_AT_producer, "Android dex2oat");
- info_.WriteData1(DW_AT_language, DW_LANG_Java);
-
- // Base class references to be patched at the end.
- std::map<size_t, mirror::Class*> base_class_references;
-
- // Already written declarations or definitions.
- std::map<mirror::Class*, size_t> class_declarations;
-
- std::vector<uint8_t> expr_buffer;
- for (mirror::Class* type : types) {
- if (type->IsPrimitive()) {
- // For primitive types the definition and the declaration is the same.
- if (type->GetPrimitiveType() != Primitive::kPrimVoid) {
- WriteTypeDeclaration(type->GetDescriptor(nullptr));
- }
- } else if (type->IsArrayClass()) {
- mirror::Class* element_type = type->GetComponentType();
- uint32_t component_size = type->GetComponentSize();
- uint32_t data_offset = mirror::Array::DataOffset(component_size).Uint32Value();
- uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value();
-
- CloseNamespacesAboveDepth(0); // Declare in root namespace.
- info_.StartTag(DW_TAG_array_type);
- std::string descriptor_string;
- WriteLazyType(element_type->GetDescriptor(&descriptor_string));
- WriteLinkageName(type);
- info_.WriteUdata(DW_AT_data_member_location, data_offset);
- info_.StartTag(DW_TAG_subrange_type);
- Expression count_expr(&expr_buffer);
- count_expr.WriteOpPushObjectAddress();
- count_expr.WriteOpPlusUconst(length_offset);
- count_expr.WriteOpDerefSize(4); // Array length is always 32-bit wide.
- info_.WriteExprLoc(DW_AT_count, count_expr);
- info_.EndTag(); // DW_TAG_subrange_type.
- info_.EndTag(); // DW_TAG_array_type.
- } else if (type->IsInterface()) {
- // Skip. Variables cannot have an interface as a dynamic type.
- // We do not expose the interface information to the debugger in any way.
- } else {
- std::string descriptor_string;
- const char* desc = type->GetDescriptor(&descriptor_string);
- size_t class_offset = StartClassTag(desc);
- class_declarations.emplace(type, class_offset);
-
- if (!type->IsVariableSize()) {
- info_.WriteUdata(DW_AT_byte_size, type->GetObjectSize());
- }
-
- WriteLinkageName(type);
-
- if (type->IsObjectClass()) {
- // Generate artificial member which is used to get the dynamic type of variable.
- // The run-time value of this field will correspond to linkage name of some type.
- // We need to do it only once in j.l.Object since all other types inherit it.
- info_.StartTag(DW_TAG_member);
- WriteName(".dynamic_type");
- WriteLazyType(sizeof(uintptr_t) == 8 ? "J" : "I");
- info_.WriteFlagPresent(DW_AT_artificial);
- // Create DWARF expression to get the value of the methods_ field.
- Expression expr(&expr_buffer);
- // The address of the object has been implicitly pushed on the stack.
- // Dereference the klass_ field of Object (32-bit; possibly poisoned).
- DCHECK_EQ(type->ClassOffset().Uint32Value(), 0u);
- DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Class>), 4u);
- expr.WriteOpDerefSize(4);
- if (kPoisonHeapReferences) {
- expr.WriteOpNeg();
- // DWARF stack is pointer sized. Ensure that the high bits are clear.
- expr.WriteOpConstu(0xFFFFFFFF);
- expr.WriteOpAnd();
- }
- // Add offset to the methods_ field.
- expr.WriteOpPlusUconst(mirror::Class::MethodsOffset().Uint32Value());
- // Top of stack holds the location of the field now.
- info_.WriteExprLoc(DW_AT_data_member_location, expr);
- info_.EndTag(); // DW_TAG_member.
- }
-
- // Base class.
- mirror::Class* base_class = type->GetSuperClass();
- if (base_class != nullptr) {
- info_.StartTag(DW_TAG_inheritance);
- base_class_references.emplace(info_.size(), base_class);
- info_.WriteRef4(DW_AT_type, 0);
- info_.WriteUdata(DW_AT_data_member_location, 0);
- info_.WriteSdata(DW_AT_accessibility, DW_ACCESS_public);
- info_.EndTag(); // DW_TAG_inheritance.
- }
-
- // Member variables.
- for (uint32_t i = 0, count = type->NumInstanceFields(); i < count; ++i) {
- ArtField* field = type->GetInstanceField(i);
- info_.StartTag(DW_TAG_member);
- WriteName(field->GetName());
- WriteLazyType(field->GetTypeDescriptor());
- info_.WriteUdata(DW_AT_data_member_location, field->GetOffset().Uint32Value());
- uint32_t access_flags = field->GetAccessFlags();
- if (access_flags & kAccPublic) {
- info_.WriteSdata(DW_AT_accessibility, DW_ACCESS_public);
- } else if (access_flags & kAccProtected) {
- info_.WriteSdata(DW_AT_accessibility, DW_ACCESS_protected);
- } else if (access_flags & kAccPrivate) {
- info_.WriteSdata(DW_AT_accessibility, DW_ACCESS_private);
- }
- info_.EndTag(); // DW_TAG_member.
- }
-
- if (type->IsStringClass()) {
- // Emit debug info about an artifical class member for java.lang.String which represents
- // the first element of the data stored in a string instance. Consumers of the debug
- // info will be able to read the content of java.lang.String based on the count (real
- // field) and based on the location of this data member.
- info_.StartTag(DW_TAG_member);
- WriteName("value");
- // We don't support fields with C like array types so we just say its type is java char.
- WriteLazyType("C"); // char.
- info_.WriteUdata(DW_AT_data_member_location,
- mirror::String::ValueOffset().Uint32Value());
- info_.WriteSdata(DW_AT_accessibility, DW_ACCESS_private);
- info_.EndTag(); // DW_TAG_member.
- }
-
- EndClassTag();
- }
- }
-
- // Write base class declarations.
- for (const auto& base_class_reference : base_class_references) {
- size_t reference_offset = base_class_reference.first;
- mirror::Class* base_class = base_class_reference.second;
- const auto& it = class_declarations.find(base_class);
- if (it != class_declarations.end()) {
- info_.UpdateUint32(reference_offset, it->second);
- } else {
- // Declare base class. We can not use the standard WriteLazyType
- // since we want to avoid the DW_TAG_reference_tag wrapping.
- std::string tmp_storage;
- const char* base_class_desc = base_class->GetDescriptor(&tmp_storage);
- size_t base_class_declaration_offset = StartClassTag(base_class_desc);
- info_.WriteFlagPresent(DW_AT_declaration);
- WriteLinkageName(base_class);
- EndClassTag();
- class_declarations.emplace(base_class, base_class_declaration_offset);
- info_.UpdateUint32(reference_offset, base_class_declaration_offset);
- }
- }
-
- FinishLazyTypes();
- CloseNamespacesAboveDepth(0);
- info_.EndTag(); // DW_TAG_compile_unit.
- CHECK_EQ(info_.Depth(), 0);
- std::vector<uint8_t> buffer;
- buffer.reserve(info_.data()->size() + KB);
- const size_t offset = owner_->builder_->GetDebugInfo()->GetSize();
- // All compilation units share single table which is at the start of .debug_abbrev.
- const size_t debug_abbrev_offset = 0;
- WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_);
- owner_->builder_->GetDebugInfo()->WriteFully(buffer.data(), buffer.size());
- }
-
- // Linkage name uniquely identifies type.
- // It is used to determine the dynamic type of objects.
- // We use the methods_ field of class since it is unique and it is not moved by the GC.
- void WriteLinkageName(mirror::Class* type) SHARED_REQUIRES(Locks::mutator_lock_) {
- auto* methods_ptr = type->GetMethodsPtr();
- if (methods_ptr == nullptr) {
- // Some types might have no methods. Allocate empty array instead.
- LinearAlloc* allocator = Runtime::Current()->GetLinearAlloc();
- void* storage = allocator->Alloc(Thread::Current(), sizeof(LengthPrefixedArray<ArtMethod>));
- methods_ptr = new (storage) LengthPrefixedArray<ArtMethod>(0);
- type->SetMethodsPtr(methods_ptr, 0, 0);
- DCHECK(type->GetMethodsPtr() != nullptr);
- }
- char name[32];
- snprintf(name, sizeof(name), "0x%" PRIXPTR, reinterpret_cast<uintptr_t>(methods_ptr));
- info_.WriteString(DW_AT_linkage_name, name);
- }
-
- // Write table into .debug_loc which describes location of dex register.
- // The dex register might be valid only at some points and it might
- // move between machine registers and stack.
- void WriteRegLocation(const MethodDebugInfo* method_info,
- uint16_t vreg,
- bool is64bitValue,
- uint32_t compilation_unit_low_pc,
- uint32_t dex_pc_low = 0,
- uint32_t dex_pc_high = 0xFFFFFFFF) {
- using Kind = DexRegisterLocation::Kind;
- if (!IsFromOptimizingCompiler(method_info)) {
- return;
- }
-
- Writer<> debug_loc(&owner_->debug_loc_);
- Writer<> debug_ranges(&owner_->debug_ranges_);
- info_.WriteSecOffset(DW_AT_location, debug_loc.size());
- info_.WriteSecOffset(DW_AT_start_scope, debug_ranges.size());
-
- std::vector<VariableLocation> variable_locations = GetVariableLocations(
- method_info,
- vreg,
- is64bitValue,
- dex_pc_low,
- dex_pc_high);
-
- // Write .debug_loc entries.
- const InstructionSet isa = owner_->builder_->GetIsa();
- const bool is64bit = Is64BitInstructionSet(isa);
- std::vector<uint8_t> expr_buffer;
- for (const VariableLocation& variable_location : variable_locations) {
- // Translate dex register location to DWARF expression.
- // Note that 64-bit value might be split to two distinct locations.
- // (for example, two 32-bit machine registers, or even stack and register)
- Expression expr(&expr_buffer);
- DexRegisterLocation reg_lo = variable_location.reg_lo;
- DexRegisterLocation reg_hi = variable_location.reg_hi;
- for (int piece = 0; piece < (is64bitValue ? 2 : 1); piece++) {
- DexRegisterLocation reg_loc = (piece == 0 ? reg_lo : reg_hi);
- const Kind kind = reg_loc.GetKind();
- const int32_t value = reg_loc.GetValue();
- if (kind == Kind::kInStack) {
- const size_t frame_size = method_info->compiled_method_->GetFrameSizeInBytes();
- // The stack offset is relative to SP. Make it relative to CFA.
- expr.WriteOpFbreg(value - frame_size);
- if (piece == 0 && reg_hi.GetKind() == Kind::kInStack &&
- reg_hi.GetValue() == value + 4) {
- break; // the high word is correctly implied by the low word.
- }
- } else if (kind == Kind::kInRegister) {
- expr.WriteOpReg(GetDwarfCoreReg(isa, value).num());
- if (piece == 0 && reg_hi.GetKind() == Kind::kInRegisterHigh &&
- reg_hi.GetValue() == value) {
- break; // the high word is correctly implied by the low word.
- }
- } else if (kind == Kind::kInFpuRegister) {
- if ((isa == kArm || isa == kThumb2) &&
- piece == 0 && reg_hi.GetKind() == Kind::kInFpuRegister &&
- reg_hi.GetValue() == value + 1 && value % 2 == 0) {
- // Translate S register pair to D register (e.g. S4+S5 to D2).
- expr.WriteOpReg(Reg::ArmDp(value / 2).num());
- break;
- }
- expr.WriteOpReg(GetDwarfFpReg(isa, value).num());
- if (piece == 0 && reg_hi.GetKind() == Kind::kInFpuRegisterHigh &&
- reg_hi.GetValue() == reg_lo.GetValue()) {
- break; // the high word is correctly implied by the low word.
- }
- } else if (kind == Kind::kConstant) {
- expr.WriteOpConsts(value);
- expr.WriteOpStackValue();
- } else if (kind == Kind::kNone) {
- break;
- } else {
- // kInStackLargeOffset and kConstantLargeValue are hidden by GetKind().
- // kInRegisterHigh and kInFpuRegisterHigh should be handled by
- // the special cases above and they should not occur alone.
- LOG(ERROR) << "Unexpected register location kind: "
- << DexRegisterLocation::PrettyDescriptor(kind);
- break;
- }
- if (is64bitValue) {
- // Write the marker which is needed by split 64-bit values.
- // This code is skipped by the special cases.
- expr.WriteOpPiece(4);
- }
- }
-
- if (expr.size() > 0) {
- if (is64bit) {
- debug_loc.PushUint64(variable_location.low_pc - compilation_unit_low_pc);
- debug_loc.PushUint64(variable_location.high_pc - compilation_unit_low_pc);
- } else {
- debug_loc.PushUint32(variable_location.low_pc - compilation_unit_low_pc);
- debug_loc.PushUint32(variable_location.high_pc - compilation_unit_low_pc);
- }
- // Write the expression.
- debug_loc.PushUint16(expr.size());
- debug_loc.PushData(expr.data());
- } else {
- // Do not generate .debug_loc if the location is not known.
- }
- }
- // Write end-of-list entry.
- if (is64bit) {
- debug_loc.PushUint64(0);
- debug_loc.PushUint64(0);
- } else {
- debug_loc.PushUint32(0);
- debug_loc.PushUint32(0);
- }
-
- // Write .debug_ranges entries.
- // This includes ranges where the variable is in scope but the location is not known.
- for (size_t i = 0; i < variable_locations.size(); i++) {
- uint32_t low_pc = variable_locations[i].low_pc;
- uint32_t high_pc = variable_locations[i].high_pc;
- while (i + 1 < variable_locations.size() && variable_locations[i+1].low_pc == high_pc) {
- // Merge address range with the next entry.
- high_pc = variable_locations[++i].high_pc;
- }
- if (is64bit) {
- debug_ranges.PushUint64(low_pc - compilation_unit_low_pc);
- debug_ranges.PushUint64(high_pc - compilation_unit_low_pc);
- } else {
- debug_ranges.PushUint32(low_pc - compilation_unit_low_pc);
- debug_ranges.PushUint32(high_pc - compilation_unit_low_pc);
- }
- }
- // Write end-of-list entry.
- if (is64bit) {
- debug_ranges.PushUint64(0);
- debug_ranges.PushUint64(0);
- } else {
- debug_ranges.PushUint32(0);
- debug_ranges.PushUint32(0);
- }
- }
-
- // Some types are difficult to define as we go since they need
- // to be enclosed in the right set of namespaces. Therefore we
- // just define all types lazily at the end of compilation unit.
- void WriteLazyType(const char* type_descriptor) {
- if (type_descriptor != nullptr && type_descriptor[0] != 'V') {
- lazy_types_.emplace(std::string(type_descriptor), info_.size());
- info_.WriteRef4(DW_AT_type, 0);
- }
- }
-
- void FinishLazyTypes() {
- for (const auto& lazy_type : lazy_types_) {
- info_.UpdateUint32(lazy_type.second, WriteTypeDeclaration(lazy_type.first));
- }
- lazy_types_.clear();
- }
-
- private:
- void WriteName(const char* name) {
- if (name != nullptr) {
- info_.WriteString(DW_AT_name, name);
- }
- }
-
- // Convert dex type descriptor to DWARF.
- // Returns offset in the compilation unit.
- size_t WriteTypeDeclaration(const std::string& desc) {
- DCHECK(!desc.empty());
- const auto& it = type_cache_.find(desc);
- if (it != type_cache_.end()) {
- return it->second;
- }
-
- size_t offset;
- if (desc[0] == 'L') {
- // Class type. For example: Lpackage/name;
- size_t class_offset = StartClassTag(desc.c_str());
- info_.WriteFlagPresent(DW_AT_declaration);
- EndClassTag();
- // Reference to the class type.
- offset = info_.StartTag(DW_TAG_reference_type);
- info_.WriteRef(DW_AT_type, class_offset);
- info_.EndTag();
- } else if (desc[0] == '[') {
- // Array type.
- size_t element_type = WriteTypeDeclaration(desc.substr(1));
- CloseNamespacesAboveDepth(0); // Declare in root namespace.
- size_t array_type = info_.StartTag(DW_TAG_array_type);
- info_.WriteFlagPresent(DW_AT_declaration);
- info_.WriteRef(DW_AT_type, element_type);
- info_.EndTag();
- offset = info_.StartTag(DW_TAG_reference_type);
- info_.WriteRef4(DW_AT_type, array_type);
- info_.EndTag();
- } else {
- // Primitive types.
- DCHECK_EQ(desc.size(), 1u);
-
- const char* name;
- uint32_t encoding;
- uint32_t byte_size;
- switch (desc[0]) {
- case 'B':
- name = "byte";
- encoding = DW_ATE_signed;
- byte_size = 1;
- break;
- case 'C':
- name = "char";
- encoding = DW_ATE_UTF;
- byte_size = 2;
- break;
- case 'D':
- name = "double";
- encoding = DW_ATE_float;
- byte_size = 8;
- break;
- case 'F':
- name = "float";
- encoding = DW_ATE_float;
- byte_size = 4;
- break;
- case 'I':
- name = "int";
- encoding = DW_ATE_signed;
- byte_size = 4;
- break;
- case 'J':
- name = "long";
- encoding = DW_ATE_signed;
- byte_size = 8;
- break;
- case 'S':
- name = "short";
- encoding = DW_ATE_signed;
- byte_size = 2;
- break;
- case 'Z':
- name = "boolean";
- encoding = DW_ATE_boolean;
- byte_size = 1;
- break;
- case 'V':
- LOG(FATAL) << "Void type should not be encoded";
- UNREACHABLE();
- default:
- LOG(FATAL) << "Unknown dex type descriptor: \"" << desc << "\"";
- UNREACHABLE();
- }
- CloseNamespacesAboveDepth(0); // Declare in root namespace.
- offset = info_.StartTag(DW_TAG_base_type);
- WriteName(name);
- info_.WriteData1(DW_AT_encoding, encoding);
- info_.WriteData1(DW_AT_byte_size, byte_size);
- info_.EndTag();
- }
-
- type_cache_.emplace(desc, offset);
- return offset;
- }
-
- // Start DW_TAG_class_type tag nested in DW_TAG_namespace tags.
- // Returns offset of the class tag in the compilation unit.
- size_t StartClassTag(const char* desc) {
- std::string name = SetNamespaceForClass(desc);
- size_t offset = info_.StartTag(DW_TAG_class_type);
- WriteName(name.c_str());
- return offset;
- }
-
- void EndClassTag() {
- info_.EndTag();
- }
-
- // Set the current namespace nesting to one required by the given class.
- // Returns the class name with namespaces, 'L', and ';' stripped.
- std::string SetNamespaceForClass(const char* desc) {
- DCHECK(desc != nullptr && desc[0] == 'L');
- desc++; // Skip the initial 'L'.
- size_t depth = 0;
- for (const char* end; (end = strchr(desc, '/')) != nullptr; desc = end + 1, ++depth) {
- // Check whether the name at this depth is already what we need.
- if (depth < current_namespace_.size()) {
- const std::string& name = current_namespace_[depth];
- if (name.compare(0, name.size(), desc, end - desc) == 0) {
- continue;
- }
- }
- // Otherwise we need to open a new namespace tag at this depth.
- CloseNamespacesAboveDepth(depth);
- info_.StartTag(DW_TAG_namespace);
- std::string name(desc, end - desc);
- WriteName(name.c_str());
- current_namespace_.push_back(std::move(name));
- }
- CloseNamespacesAboveDepth(depth);
- return std::string(desc, strchr(desc, ';') - desc);
- }
-
- // Close namespace tags to reach the given nesting depth.
- void CloseNamespacesAboveDepth(size_t depth) {
- DCHECK_LE(depth, current_namespace_.size());
- while (current_namespace_.size() > depth) {
- info_.EndTag();
- current_namespace_.pop_back();
- }
- }
-
- // For access to the ELF sections.
- DebugInfoWriter<ElfTypes>* owner_;
- // Temporary buffer to create and store the entries.
- DebugInfoEntryWriter<> info_;
- // Cache of already translated type descriptors.
- std::map<std::string, size_t> type_cache_; // type_desc -> definition_offset.
- // 32-bit references which need to be resolved to a type later.
- // Given type may be used multiple times. Therefore we need a multimap.
- std::multimap<std::string, size_t> lazy_types_; // type_desc -> patch_offset.
- // The current set of open namespace tags which are active and not closed yet.
- std::vector<std::string> current_namespace_;
- };
-
- public:
- explicit DebugInfoWriter(ElfBuilder<ElfTypes>* builder)
- : builder_(builder),
- debug_abbrev_(&debug_abbrev_buffer_) {
- }
-
- void Start() {
- builder_->GetDebugInfo()->Start();
- }
-
- void WriteCompilationUnit(const CompilationUnit& compilation_unit) {
- CompilationUnitWriter writer(this);
- writer.Write(compilation_unit);
- }
-
- void WriteTypes(const ArrayRef<mirror::Class*>& types) SHARED_REQUIRES(Locks::mutator_lock_) {
- CompilationUnitWriter writer(this);
- writer.Write(types);
- }
-
- void End() {
- builder_->GetDebugInfo()->End();
- builder_->WritePatches(".debug_info.oat_patches",
- ArrayRef<const uintptr_t>(debug_info_patches_));
- builder_->WriteSection(".debug_abbrev", &debug_abbrev_buffer_);
- builder_->WriteSection(".debug_loc", &debug_loc_);
- builder_->WriteSection(".debug_ranges", &debug_ranges_);
- }
-
- private:
- ElfBuilder<ElfTypes>* builder_;
- std::vector<uintptr_t> debug_info_patches_;
- std::vector<uint8_t> debug_abbrev_buffer_;
- DebugAbbrevWriter<> debug_abbrev_;
- std::vector<uint8_t> debug_loc_;
- std::vector<uint8_t> debug_ranges_;
-
- std::unordered_set<const char*> defined_dex_classes_; // For CHECKs only.
-};
-
-template<typename ElfTypes>
-class DebugLineWriter {
- typedef typename ElfTypes::Addr Elf_Addr;
-
- public:
- explicit DebugLineWriter(ElfBuilder<ElfTypes>* builder) : builder_(builder) {
- }
-
- void Start() {
- builder_->GetDebugLine()->Start();
- }
-
- // Write line table for given set of methods.
- // Returns the number of bytes written.
- size_t WriteCompilationUnit(CompilationUnit& compilation_unit) {
- const bool is64bit = Is64BitInstructionSet(builder_->GetIsa());
- const Elf_Addr text_address = builder_->GetText()->Exists()
- ? builder_->GetText()->GetAddress()
- : 0;
-
- compilation_unit.debug_line_offset_ = builder_->GetDebugLine()->GetSize();
-
- std::vector<FileEntry> files;
- std::unordered_map<std::string, size_t> files_map;
- std::vector<std::string> directories;
- std::unordered_map<std::string, size_t> directories_map;
- int code_factor_bits_ = 0;
- int dwarf_isa = -1;
- switch (builder_->GetIsa()) {
- case kArm: // arm actually means thumb2.
- case kThumb2:
- code_factor_bits_ = 1; // 16-bit instuctions
- dwarf_isa = 1; // DW_ISA_ARM_thumb.
- break;
- case kArm64:
- case kMips:
- case kMips64:
- code_factor_bits_ = 2; // 32-bit instructions
- break;
- case kNone:
- case kX86:
- case kX86_64:
- break;
- }
- DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits_);
- for (const MethodDebugInfo* mi : compilation_unit.methods_) {
- // Ignore function if we have already generated line table for the same address.
- // It would confuse the debugger and the DWARF specification forbids it.
- if (mi->deduped_) {
- continue;
- }
-
- ArrayRef<const SrcMapElem> src_mapping_table;
- std::vector<SrcMapElem> src_mapping_table_from_stack_maps;
- if (IsFromOptimizingCompiler(mi)) {
- // Use stack maps to create mapping table from pc to dex.
- const CodeInfo code_info(mi->compiled_method_->GetVmapTable().data());
- const StackMapEncoding encoding = code_info.ExtractEncoding();
- for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) {
- StackMap stack_map = code_info.GetStackMapAt(s, encoding);
- DCHECK(stack_map.IsValid());
- // Emit only locations where we have local-variable information.
- // In particular, skip mappings inside the prologue.
- if (stack_map.HasDexRegisterMap(encoding)) {
- const uint32_t pc = stack_map.GetNativePcOffset(encoding);
- const int32_t dex = stack_map.GetDexPc(encoding);
- src_mapping_table_from_stack_maps.push_back({pc, dex});
- }
- }
- std::sort(src_mapping_table_from_stack_maps.begin(),
- src_mapping_table_from_stack_maps.end());
- src_mapping_table = ArrayRef<const SrcMapElem>(src_mapping_table_from_stack_maps);
- } else {
- // Use the mapping table provided by the quick compiler.
- src_mapping_table = mi->compiled_method_->GetSrcMappingTable();
- }
-
- if (src_mapping_table.empty()) {
- continue;
- }
-
- Elf_Addr method_address = text_address + mi->low_pc_;
-
- PositionInfos position_infos;
- const DexFile* dex = mi->dex_file_;
- if (!dex->DecodeDebugPositionInfo(mi->code_item_, PositionInfoCallback, &position_infos)) {
- continue;
- }
-
- if (position_infos.empty()) {
- continue;
- }
-
- opcodes.SetAddress(method_address);
- if (dwarf_isa != -1) {
- opcodes.SetISA(dwarf_isa);
- }
-
- // Get and deduplicate directory and filename.
- int file_index = 0; // 0 - primary source file of the compilation.
- auto& dex_class_def = dex->GetClassDef(mi->class_def_index_);
- const char* source_file = dex->GetSourceFile(dex_class_def);
- if (source_file != nullptr) {
- std::string file_name(source_file);
- size_t file_name_slash = file_name.find_last_of('/');
- std::string class_name(dex->GetClassDescriptor(dex_class_def));
- size_t class_name_slash = class_name.find_last_of('/');
- std::string full_path(file_name);
-
- // Guess directory from package name.
- int directory_index = 0; // 0 - current directory of the compilation.
- if (file_name_slash == std::string::npos && // Just filename.
- class_name.front() == 'L' && // Type descriptor for a class.
- class_name_slash != std::string::npos) { // Has package name.
- std::string package_name = class_name.substr(1, class_name_slash - 1);
- auto it = directories_map.find(package_name);
- if (it == directories_map.end()) {
- directory_index = 1 + directories.size();
- directories_map.emplace(package_name, directory_index);
- directories.push_back(package_name);
- } else {
- directory_index = it->second;
- }
- full_path = package_name + "/" + file_name;
- }
-
- // Add file entry.
- auto it2 = files_map.find(full_path);
- if (it2 == files_map.end()) {
- file_index = 1 + files.size();
- files_map.emplace(full_path, file_index);
- files.push_back(FileEntry {
- file_name,
- directory_index,
- 0, // Modification time - NA.
- 0, // File size - NA.
- });
- } else {
- file_index = it2->second;
- }
- }
- opcodes.SetFile(file_index);
-
- // Generate mapping opcodes from PC to Java lines.
- if (file_index != 0) {
- bool first = true;
- for (SrcMapElem pc2dex : src_mapping_table) {
- uint32_t pc = pc2dex.from_;
- int dex_pc = pc2dex.to_;
- // Find mapping with address with is greater than our dex pc; then go back one step.
- auto ub = std::upper_bound(position_infos.begin(), position_infos.end(), dex_pc,
- [](uint32_t address, const DexFile::PositionInfo& entry) {
- return address < entry.address_;
- });
- if (ub != position_infos.begin()) {
- int line = (--ub)->line_;
- if (first) {
- first = false;
- if (pc > 0) {
- // Assume that any preceding code is prologue.
- int first_line = position_infos.front().line_;
- // Prologue is not a sensible place for a breakpoint.
- opcodes.NegateStmt();
- opcodes.AddRow(method_address, first_line);
- opcodes.NegateStmt();
- opcodes.SetPrologueEnd();
- }
- opcodes.AddRow(method_address + pc, line);
- } else if (line != opcodes.CurrentLine()) {
- opcodes.AddRow(method_address + pc, line);
- }
- }
- }
- } else {
- // line 0 - instruction cannot be attributed to any source line.
- opcodes.AddRow(method_address, 0);
- }
-
- opcodes.AdvancePC(text_address + mi->high_pc_);
- opcodes.EndSequence();
- }
- std::vector<uint8_t> buffer;
- buffer.reserve(opcodes.data()->size() + KB);
- size_t offset = builder_->GetDebugLine()->GetSize();
- WriteDebugLineTable(directories, files, opcodes, offset, &buffer, &debug_line_patches);
- builder_->GetDebugLine()->WriteFully(buffer.data(), buffer.size());
- return buffer.size();
- }
-
- void End() {
- builder_->GetDebugLine()->End();
- builder_->WritePatches(".debug_line.oat_patches",
- ArrayRef<const uintptr_t>(debug_line_patches));
- }
-
- private:
- ElfBuilder<ElfTypes>* builder_;
- std::vector<uintptr_t> debug_line_patches;
-};
-
-template<typename ElfTypes>
-static void WriteDebugSections(ElfBuilder<ElfTypes>* builder,
- const ArrayRef<const MethodDebugInfo>& method_infos) {
- // Group the methods into compilation units based on source file.
- std::vector<CompilationUnit> compilation_units;
- const char* last_source_file = nullptr;
- for (const MethodDebugInfo& mi : method_infos) {
- auto& dex_class_def = mi.dex_file_->GetClassDef(mi.class_def_index_);
- const char* source_file = mi.dex_file_->GetSourceFile(dex_class_def);
- if (compilation_units.empty() || source_file != last_source_file) {
- compilation_units.push_back(CompilationUnit());
- }
- CompilationUnit& cu = compilation_units.back();
- cu.methods_.push_back(&mi);
- cu.low_pc_ = std::min(cu.low_pc_, mi.low_pc_);
- cu.high_pc_ = std::max(cu.high_pc_, mi.high_pc_);
- last_source_file = source_file;
- }
-
- // Write .debug_line section.
- if (!compilation_units.empty()) {
- DebugLineWriter<ElfTypes> line_writer(builder);
- line_writer.Start();
- for (auto& compilation_unit : compilation_units) {
- line_writer.WriteCompilationUnit(compilation_unit);
- }
- line_writer.End();
- }
-
- // Write .debug_info section.
- if (!compilation_units.empty()) {
- DebugInfoWriter<ElfTypes> info_writer(builder);
- info_writer.Start();
- for (const auto& compilation_unit : compilation_units) {
- info_writer.WriteCompilationUnit(compilation_unit);
- }
- info_writer.End();
- }
-}
-
-template <typename ElfTypes>
-static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder,
- const ArrayRef<const MethodDebugInfo>& method_infos,
- bool with_signature) {
- bool generated_mapping_symbol = false;
- auto* strtab = builder->GetStrTab();
- auto* symtab = builder->GetSymTab();
-
- if (method_infos.empty()) {
- return;
- }
-
- // Find all addresses (low_pc) which contain deduped methods.
- // The first instance of method is not marked deduped_, but the rest is.
- std::unordered_set<uint32_t> deduped_addresses;
- for (const MethodDebugInfo& info : method_infos) {
- if (info.deduped_) {
- deduped_addresses.insert(info.low_pc_);
- }
- }
-
- strtab->Start();
- strtab->Write(""); // strtab should start with empty string.
- std::string last_name;
- size_t last_name_offset = 0;
- for (const MethodDebugInfo& info : method_infos) {
- if (info.deduped_) {
- continue; // Add symbol only for the first instance.
- }
- std::string name = PrettyMethod(info.dex_method_index_, *info.dex_file_, with_signature);
- if (deduped_addresses.find(info.low_pc_) != deduped_addresses.end()) {
- name += " [DEDUPED]";
- }
- // If we write method names without signature, we might see the same name multiple times.
- size_t name_offset = (name == last_name ? last_name_offset : strtab->Write(name));
-
- const auto* text = builder->GetText()->Exists() ? builder->GetText() : nullptr;
- const bool is_relative = (text != nullptr);
- uint32_t low_pc = info.low_pc_;
- // Add in code delta, e.g., thumb bit 0 for Thumb2 code.
- low_pc += info.compiled_method_->CodeDelta();
- symtab->Add(name_offset,
- text,
- low_pc,
- is_relative,
- info.high_pc_ - info.low_pc_,
- STB_GLOBAL,
- STT_FUNC);
-
- // Conforming to aaelf, add $t mapping symbol to indicate start of a sequence of thumb2
- // instructions, so that disassembler tools can correctly disassemble.
- // Note that even if we generate just a single mapping symbol, ARM's Streamline
- // requires it to match function symbol. Just address 0 does not work.
- if (info.compiled_method_->GetInstructionSet() == kThumb2) {
- if (!generated_mapping_symbol || !kGenerateSingleArmMappingSymbol) {
- symtab->Add(strtab->Write("$t"), text, info.low_pc_ & ~1,
- is_relative, 0, STB_LOCAL, STT_NOTYPE);
- generated_mapping_symbol = true;
- }
- }
-
- last_name = std::move(name);
- last_name_offset = name_offset;
- }
- strtab->End();
-
- // Symbols are buffered and written after names (because they are smaller).
- // We could also do two passes in this function to avoid the buffering.
- symtab->Start();
- symtab->Write();
- symtab->End();
-}
-
-template <typename ElfTypes>
-void WriteDebugInfo(ElfBuilder<ElfTypes>* builder,
- const ArrayRef<const MethodDebugInfo>& method_infos,
- CFIFormat cfi_format) {
- // Add methods to .symtab.
- WriteDebugSymbols(builder, method_infos, true /* with_signature */);
- // Generate CFI (stack unwinding information).
- WriteCFISection(builder, method_infos, cfi_format, true /* write_oat_patches */);
- // Write DWARF .debug_* sections.
- WriteDebugSections(builder, method_infos);
-}
-
-static void XzCompress(const std::vector<uint8_t>* src, std::vector<uint8_t>* dst) {
- // Configure the compression library.
- CrcGenerateTable();
- Crc64GenerateTable();
- CLzma2EncProps lzma2Props;
- Lzma2EncProps_Init(&lzma2Props);
- lzma2Props.lzmaProps.level = 1; // Fast compression.
- Lzma2EncProps_Normalize(&lzma2Props);
- CXzProps props;
- XzProps_Init(&props);
- props.lzma2Props = &lzma2Props;
- // Implement the required interface for communication (written in C so no virtual methods).
- struct XzCallbacks : public ISeqInStream, public ISeqOutStream, public ICompressProgress {
- static SRes ReadImpl(void* p, void* buf, size_t* size) {
- auto* ctx = static_cast<XzCallbacks*>(reinterpret_cast<ISeqInStream*>(p));
- *size = std::min(*size, ctx->src_->size() - ctx->src_pos_);
- memcpy(buf, ctx->src_->data() + ctx->src_pos_, *size);
- ctx->src_pos_ += *size;
- return SZ_OK;
- }
- static size_t WriteImpl(void* p, const void* buf, size_t size) {
- auto* ctx = static_cast<XzCallbacks*>(reinterpret_cast<ISeqOutStream*>(p));
- const uint8_t* buffer = reinterpret_cast<const uint8_t*>(buf);
- ctx->dst_->insert(ctx->dst_->end(), buffer, buffer + size);
- return size;
- }
- static SRes ProgressImpl(void* , UInt64, UInt64) {
- return SZ_OK;
- }
- size_t src_pos_;
- const std::vector<uint8_t>* src_;
- std::vector<uint8_t>* dst_;
- };
- XzCallbacks callbacks;
- callbacks.Read = XzCallbacks::ReadImpl;
- callbacks.Write = XzCallbacks::WriteImpl;
- callbacks.Progress = XzCallbacks::ProgressImpl;
- callbacks.src_pos_ = 0;
- callbacks.src_ = src;
- callbacks.dst_ = dst;
- // Compress.
- SRes res = Xz_Encode(&callbacks, &callbacks, &props, &callbacks);
- CHECK_EQ(res, SZ_OK);
-}
-
-template <typename ElfTypes>
-void WriteMiniDebugInfo(ElfBuilder<ElfTypes>* parent_builder,
- const ArrayRef<const MethodDebugInfo>& method_infos) {
- const InstructionSet isa = parent_builder->GetIsa();
- std::vector<uint8_t> buffer;
- buffer.reserve(KB);
- VectorOutputStream out("Mini-debug-info ELF file", &buffer);
- std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
- builder->Start();
- // Write .rodata and .text as NOBITS sections.
- // This allows tools to detect virtual address relocation of the parent ELF file.
- builder->SetVirtualAddress(parent_builder->GetRoData()->GetAddress());
- builder->GetRoData()->WriteNoBitsSection(parent_builder->GetRoData()->GetSize());
- builder->SetVirtualAddress(parent_builder->GetText()->GetAddress());
- builder->GetText()->WriteNoBitsSection(parent_builder->GetText()->GetSize());
- WriteDebugSymbols(builder.get(), method_infos, false /* with_signature */);
- WriteCFISection(builder.get(), method_infos, DW_DEBUG_FRAME_FORMAT, false /* write_oat_paches */);
- builder->End();
- CHECK(builder->Good());
- std::vector<uint8_t> compressed_buffer;
- compressed_buffer.reserve(buffer.size() / 4);
- XzCompress(&buffer, &compressed_buffer);
- parent_builder->WriteSection(".gnu_debugdata", &compressed_buffer);
-}
-
-template <typename ElfTypes>
-static ArrayRef<const uint8_t> WriteDebugElfFileForMethodInternal(
- const dwarf::MethodDebugInfo& method_info) {
- const InstructionSet isa = method_info.compiled_method_->GetInstructionSet();
- std::vector<uint8_t> buffer;
- buffer.reserve(KB);
- VectorOutputStream out("Debug ELF file", &buffer);
- std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
- builder->Start();
- WriteDebugInfo(builder.get(),
- ArrayRef<const MethodDebugInfo>(&method_info, 1),
- DW_DEBUG_FRAME_FORMAT);
- builder->End();
- CHECK(builder->Good());
- // Make a copy of the buffer. We want to shrink it anyway.
- uint8_t* result = new uint8_t[buffer.size()];
- CHECK(result != nullptr);
- memcpy(result, buffer.data(), buffer.size());
- return ArrayRef<const uint8_t>(result, buffer.size());
-}
-
-ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const dwarf::MethodDebugInfo& method_info) {
- const InstructionSet isa = method_info.compiled_method_->GetInstructionSet();
- if (Is64BitInstructionSet(isa)) {
- return WriteDebugElfFileForMethodInternal<ElfTypes64>(method_info);
- } else {
- return WriteDebugElfFileForMethodInternal<ElfTypes32>(method_info);
- }
-}
-
-template <typename ElfTypes>
-static ArrayRef<const uint8_t> WriteDebugElfFileForClassesInternal(
- const InstructionSet isa, const ArrayRef<mirror::Class*>& types)
- SHARED_REQUIRES(Locks::mutator_lock_) {
- std::vector<uint8_t> buffer;
- buffer.reserve(KB);
- VectorOutputStream out("Debug ELF file", &buffer);
- std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
- builder->Start();
-
- DebugInfoWriter<ElfTypes> info_writer(builder.get());
- info_writer.Start();
- info_writer.WriteTypes(types);
- info_writer.End();
-
- builder->End();
- CHECK(builder->Good());
- // Make a copy of the buffer. We want to shrink it anyway.
- uint8_t* result = new uint8_t[buffer.size()];
- CHECK(result != nullptr);
- memcpy(result, buffer.data(), buffer.size());
- return ArrayRef<const uint8_t>(result, buffer.size());
-}
-
-ArrayRef<const uint8_t> WriteDebugElfFileForClasses(const InstructionSet isa,
- const ArrayRef<mirror::Class*>& types) {
- if (Is64BitInstructionSet(isa)) {
- return WriteDebugElfFileForClassesInternal<ElfTypes64>(isa, types);
- } else {
- return WriteDebugElfFileForClassesInternal<ElfTypes32>(isa, types);
- }
-}
-
-// Explicit instantiations
-template void WriteDebugInfo<ElfTypes32>(
- ElfBuilder<ElfTypes32>* builder,
- const ArrayRef<const MethodDebugInfo>& method_infos,
- CFIFormat cfi_format);
-template void WriteDebugInfo<ElfTypes64>(
- ElfBuilder<ElfTypes64>* builder,
- const ArrayRef<const MethodDebugInfo>& method_infos,
- CFIFormat cfi_format);
-template void WriteMiniDebugInfo<ElfTypes32>(
- ElfBuilder<ElfTypes32>* builder,
- const ArrayRef<const MethodDebugInfo>& method_infos);
-template void WriteMiniDebugInfo<ElfTypes64>(
- ElfBuilder<ElfTypes64>* builder,
- const ArrayRef<const MethodDebugInfo>& method_infos);
-
-} // namespace dwarf
-} // namespace art
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 6bf080a..1d71e57 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -23,16 +23,18 @@
#include "base/logging.h"
#include "base/stl_util.h"
#include "compiled_method.h"
+#include "debug/elf_debug_writer.h"
+#include "debug/method_debug_info.h"
#include "driver/compiler_options.h"
-#include "dwarf/method_debug_info.h"
#include "elf.h"
#include "elf_builder.h"
#include "elf_utils.h"
-#include "elf_writer_debug.h"
#include "globals.h"
#include "leb128.h"
#include "linker/buffered_output_stream.h"
#include "linker/file_output_stream.h"
+#include "thread-inl.h"
+#include "thread_pool.h"
#include "utils.h"
namespace art {
@@ -46,6 +48,37 @@
// Let's use .debug_frame because it is easier to strip or compress.
constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_DEBUG_FRAME_FORMAT;
+class DebugInfoTask : public Task {
+ public:
+ DebugInfoTask(InstructionSet isa,
+ size_t rodata_section_size,
+ size_t text_section_size,
+ const ArrayRef<const debug::MethodDebugInfo>& method_infos)
+ : isa_(isa),
+ rodata_section_size_(rodata_section_size),
+ text_section_size_(text_section_size),
+ method_infos_(method_infos) {
+ }
+
+ void Run(Thread*) {
+ result_ = debug::MakeMiniDebugInfo(isa_,
+ rodata_section_size_,
+ text_section_size_,
+ method_infos_);
+ }
+
+ std::vector<uint8_t>* GetResult() {
+ return &result_;
+ }
+
+ private:
+ InstructionSet isa_;
+ size_t rodata_section_size_;
+ size_t text_section_size_;
+ const ArrayRef<const debug::MethodDebugInfo>& method_infos_;
+ std::vector<uint8_t> result_;
+};
+
template <typename ElfTypes>
class ElfWriterQuick FINAL : public ElfWriter {
public:
@@ -55,13 +88,16 @@
~ElfWriterQuick();
void Start() OVERRIDE;
+ void PrepareDebugInfo(size_t rodata_section_size,
+ size_t text_section_size,
+ const ArrayRef<const debug::MethodDebugInfo>& method_infos) OVERRIDE;
OutputStream* StartRoData() OVERRIDE;
void EndRoData(OutputStream* rodata) OVERRIDE;
OutputStream* StartText() OVERRIDE;
void EndText(OutputStream* text) OVERRIDE;
void SetBssSize(size_t bss_size) OVERRIDE;
void WriteDynamicSection() OVERRIDE;
- void WriteDebugInfo(const ArrayRef<const dwarf::MethodDebugInfo>& method_infos) OVERRIDE;
+ void WriteDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) OVERRIDE;
void WritePatchLocations(const ArrayRef<const uintptr_t>& patch_locations) OVERRIDE;
bool End() OVERRIDE;
@@ -75,6 +111,8 @@
File* const elf_file_;
std::unique_ptr<BufferedOutputStream> output_stream_;
std::unique_ptr<ElfBuilder<ElfTypes>> builder_;
+ std::unique_ptr<DebugInfoTask> debug_info_task_;
+ std::unique_ptr<ThreadPool> debug_info_thread_pool_;
DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterQuick);
};
@@ -147,15 +185,40 @@
}
template <typename ElfTypes>
-void ElfWriterQuick<ElfTypes>::WriteDebugInfo(
- const ArrayRef<const dwarf::MethodDebugInfo>& method_infos) {
- if (compiler_options_->GetGenerateDebugInfo()) {
- // Generate all the debug information we can.
- dwarf::WriteDebugInfo(builder_.get(), method_infos, kCFIFormat);
+void ElfWriterQuick<ElfTypes>::PrepareDebugInfo(
+ size_t rodata_section_size,
+ size_t text_section_size,
+ const ArrayRef<const debug::MethodDebugInfo>& method_infos) {
+ if (!method_infos.empty() && compiler_options_->GetGenerateMiniDebugInfo()) {
+ // Prepare the mini-debug-info in background while we do other I/O.
+ Thread* self = Thread::Current();
+ debug_info_task_ = std::unique_ptr<DebugInfoTask>(
+ new DebugInfoTask(builder_->GetIsa(),
+ rodata_section_size,
+ text_section_size,
+ method_infos));
+ debug_info_thread_pool_ = std::unique_ptr<ThreadPool>(
+ new ThreadPool("Mini-debug-info writer", 1));
+ debug_info_thread_pool_->AddTask(self, debug_info_task_.get());
+ debug_info_thread_pool_->StartWorkers(self);
}
- if (compiler_options_->GetGenerateMiniDebugInfo()) {
- // Generate only some information and compress it.
- dwarf::WriteMiniDebugInfo(builder_.get(), method_infos);
+}
+
+template <typename ElfTypes>
+void ElfWriterQuick<ElfTypes>::WriteDebugInfo(
+ const ArrayRef<const debug::MethodDebugInfo>& method_infos) {
+ if (!method_infos.empty()) {
+ if (compiler_options_->GetGenerateDebugInfo()) {
+ // Generate all the debug information we can.
+ debug::WriteDebugInfo(builder_.get(), method_infos, kCFIFormat, true /* write_oat_patches */);
+ }
+ if (compiler_options_->GetGenerateMiniDebugInfo()) {
+ // Wait for the mini-debug-info generation to finish and write it to disk.
+ Thread* self = Thread::Current();
+ DCHECK(debug_info_thread_pool_ != nullptr);
+ debug_info_thread_pool_->Wait(self, true, false);
+ builder_->WriteSection(".gnu_debugdata", debug_info_task_->GetResult());
+ }
}
}
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index a5a7796..4920f9b 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -23,7 +23,7 @@
#include "base/unix_file/fd_file.h"
#include "class_linker-inl.h"
#include "common_compiler_test.h"
-#include "dwarf/method_debug_info.h"
+#include "debug/method_debug_info.h"
#include "elf_writer.h"
#include "elf_writer_quick.h"
#include "gc/space/image_space.h"
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 6774758..3fe7861 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -16,19 +16,19 @@
#include "jit_compiler.h"
-#include "art_method-inl.h"
#include "arch/instruction_set.h"
#include "arch/instruction_set_features.h"
+#include "art_method-inl.h"
#include "base/stringpiece.h"
#include "base/time_utils.h"
#include "base/timing_logger.h"
#include "base/unix_file/fd_file.h"
#include "compiler_callbacks.h"
+#include "debug/elf_debug_writer.h"
#include "dex/pass_manager.h"
#include "dex/quick_compiler_callbacks.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
-#include "elf_writer_debug.h"
#include "jit/debugger_interface.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
@@ -60,11 +60,12 @@
delete reinterpret_cast<JitCompiler*>(handle);
}
-extern "C" bool jit_compile_method(void* handle, ArtMethod* method, Thread* self)
+extern "C" bool jit_compile_method(
+ void* handle, ArtMethod* method, Thread* self, bool osr)
SHARED_REQUIRES(Locks::mutator_lock_) {
auto* jit_compiler = reinterpret_cast<JitCompiler*>(handle);
DCHECK(jit_compiler != nullptr);
- return jit_compiler->CompileMethod(self, method);
+ return jit_compiler->CompileMethod(self, method, osr);
}
extern "C" void jit_types_loaded(void* handle, mirror::Class** types, size_t count)
@@ -73,7 +74,7 @@
DCHECK(jit_compiler != nullptr);
if (jit_compiler->GetCompilerOptions()->GetGenerateDebugInfo()) {
const ArrayRef<mirror::Class*> types_array(types, count);
- ArrayRef<const uint8_t> elf_file = dwarf::WriteDebugElfFileForClasses(kRuntimeISA, types_array);
+ ArrayRef<const uint8_t> elf_file = debug::WriteDebugElfFileForClasses(kRuntimeISA, types_array);
CreateJITCodeEntry(std::unique_ptr<const uint8_t[]>(elf_file.data()), elf_file.size());
}
}
@@ -201,7 +202,8 @@
}
}
-bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method) {
+bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method, bool osr) {
+ DCHECK(!method->IsProxyMethod());
TimingLogger logger("JIT compiler timing logger", true, VLOG_IS_ON(jit));
const uint64_t start_time = NanoTime();
StackHandleScope<2> hs(self);
@@ -219,20 +221,17 @@
bool success = false;
{
TimingLogger::ScopedTiming t2("Compiling", &logger);
- // If we get a request to compile a proxy method, we pass the actual Java method
- // of that proxy method, as the compiler does not expect a proxy method.
- ArtMethod* method_to_compile = method->GetInterfaceMethodIfProxy(sizeof(void*));
JitCodeCache* const code_cache = runtime->GetJit()->GetCodeCache();
- success = compiler_driver_->GetCompiler()->JitCompile(self, code_cache, method_to_compile);
- if (success && perf_file_ != nullptr) {
- const void* ptr = method_to_compile->GetEntryPointFromQuickCompiledCode();
+ success = compiler_driver_->GetCompiler()->JitCompile(self, code_cache, method, osr);
+ if (success && (perf_file_ != nullptr)) {
+ const void* ptr = method->GetEntryPointFromQuickCompiledCode();
std::ostringstream stream;
stream << std::hex
<< reinterpret_cast<uintptr_t>(ptr)
<< " "
<< code_cache->GetMemorySizeOfCodePointer(ptr)
<< " "
- << PrettyMethod(method_to_compile)
+ << PrettyMethod(method)
<< std::endl;
std::string str = stream.str();
bool res = perf_file_->WriteFully(str.c_str(), str.size());
diff --git a/compiler/jit/jit_compiler.h b/compiler/jit/jit_compiler.h
index 037a18a..5294d0e 100644
--- a/compiler/jit/jit_compiler.h
+++ b/compiler/jit/jit_compiler.h
@@ -37,7 +37,7 @@
public:
static JitCompiler* Create();
virtual ~JitCompiler();
- bool CompileMethod(Thread* self, ArtMethod* method)
+ bool CompileMethod(Thread* self, ArtMethod* method, bool osr)
SHARED_REQUIRES(Locks::mutator_lock_);
CompilerCallbacks* GetCompilerCallbacks() const;
size_t GetTotalCompileTime() const {
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index cff2f47..894d29e 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -21,20 +21,20 @@
#include "common_compiler_test.h"
#include "compiled_method.h"
#include "compiler.h"
+#include "debug/method_debug_info.h"
#include "dex/pass_manager.h"
#include "dex/quick/dex_file_to_method_inliner_map.h"
#include "dex/quick_compiler_callbacks.h"
#include "dex/verification_results.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
-#include "dwarf/method_debug_info.h"
#include "elf_writer.h"
#include "elf_writer_quick.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "linker/vector_output_stream.h"
#include "mirror/class-inl.h"
-#include "mirror/object_array-inl.h"
#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
#include "oat_file-inl.h"
#include "oat_writer.h"
#include "scoped_thread_state_change.h"
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 90ac499..47dcfd5 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -29,11 +29,11 @@
#include "class_linker.h"
#include "compiled_class.h"
#include "compiled_method.h"
-#include "dex_file-inl.h"
+#include "debug/method_debug_info.h"
#include "dex/verification_results.h"
+#include "dex_file-inl.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
-#include "dwarf/method_debug_info.h"
#include "gc/space/image_space.h"
#include "gc/space/space.h"
#include "handle_scope-inl.h"
@@ -811,7 +811,7 @@
// Record debug information for this function if we are doing that.
const uint32_t quick_code_start = quick_code_offset -
writer_->oat_header_->GetExecutableOffset() - thumb_offset;
- writer_->method_info_.push_back(dwarf::MethodDebugInfo {
+ writer_->method_info_.push_back(debug::MethodDebugInfo {
dex_file_,
class_def_index_,
it.GetMemberIndex(),
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 14c6d50..5a55fc6 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -43,9 +43,9 @@
class TypeLookupTable;
class ZipEntry;
-namespace dwarf {
+namespace debug {
struct MethodDebugInfo;
-} // namespace dwarf
+} // namespace debug
// OatHeader variable length with count of D OatDexFiles
//
@@ -193,8 +193,8 @@
~OatWriter();
- ArrayRef<const dwarf::MethodDebugInfo> GetMethodDebugInfo() const {
- return ArrayRef<const dwarf::MethodDebugInfo>(method_info_);
+ ArrayRef<const debug::MethodDebugInfo> GetMethodDebugInfo() const {
+ return ArrayRef<const debug::MethodDebugInfo>(method_info_);
}
const CompilerDriver* GetCompilerDriver() {
@@ -289,7 +289,7 @@
// We need this because we keep plain pointers to the strings' c_str().
std::list<std::string> zipped_dex_file_locations_;
- dchecked_vector<dwarf::MethodDebugInfo> method_info_;
+ dchecked_vector<debug::MethodDebugInfo> method_info_;
const CompilerDriver* compiler_driver_;
ImageWriter* image_writer_;
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index eee6116..ba1b168 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -63,9 +63,10 @@
return true;
}
+ // Return true if instruction can be expressed as "left_instruction + right_constant".
static bool IsAddOrSubAConstant(HInstruction* instruction,
- HInstruction** left_instruction,
- int* right_constant) {
+ /* out */ HInstruction** left_instruction,
+ /* out */ int32_t* right_constant) {
if (instruction->IsAdd() || instruction->IsSub()) {
HBinaryOperation* bin_op = instruction->AsBinaryOperation();
HInstruction* left = bin_op->GetLeft();
@@ -82,9 +83,22 @@
return false;
}
+ // Expresses any instruction as a value bound.
+ static ValueBound AsValueBound(HInstruction* instruction) {
+ if (instruction->IsIntConstant()) {
+ return ValueBound(nullptr, instruction->AsIntConstant()->GetValue());
+ }
+ HInstruction *left;
+ int32_t right;
+ if (IsAddOrSubAConstant(instruction, &left, &right)) {
+ return ValueBound(left, right);
+ }
+ return ValueBound(instruction, 0);
+ }
+
// Try to detect useful value bound format from an instruction, e.g.
// a constant or array length related value.
- static ValueBound DetectValueBoundFromValue(HInstruction* instruction, bool* found) {
+ static ValueBound DetectValueBoundFromValue(HInstruction* instruction, /* out */ bool* found) {
DCHECK(instruction != nullptr);
if (instruction->IsIntConstant()) {
*found = true;
@@ -227,7 +241,7 @@
// Add a constant to a ValueBound.
// `overflow` or `underflow` will return whether the resulting bound may
// overflow or underflow an int.
- ValueBound Add(int32_t c, bool* overflow, bool* underflow) const {
+ ValueBound Add(int32_t c, /* out */ bool* overflow, /* out */ bool* underflow) const {
*overflow = *underflow = false;
if (c == 0) {
return *this;
@@ -488,10 +502,10 @@
// the deoptimization technique.
static constexpr size_t kThresholdForAddingDeoptimize = 2;
- // Very large constant index is considered as an anomaly. This is a threshold
- // beyond which we don't bother to apply the deoptimization technique since
- // it's likely some AIOOBE will be thrown.
- static constexpr int32_t kMaxConstantForAddingDeoptimize =
+ // Very large lengths are considered an anomaly. This is a threshold beyond which we don't
+ // bother to apply the deoptimization technique since it's likely, or sometimes certain,
+ // an AIOOBE will be thrown.
+ static constexpr uint32_t kMaxLengthForAddingDeoptimize =
std::numeric_limits<int32_t>::max() - 1024 * 1024;
// Added blocks for loop body entry test.
@@ -508,7 +522,7 @@
std::less<int>(),
graph->GetArena()->Adapter(kArenaAllocBoundsCheckElimination)),
graph->GetArena()->Adapter(kArenaAllocBoundsCheckElimination)),
- first_constant_index_bounds_check_map_(
+ first_index_bounds_check_map_(
std::less<int>(),
graph->GetArena()->Adapter(kArenaAllocBoundsCheckElimination)),
early_exit_loop_(
@@ -518,23 +532,16 @@
std::less<uint32_t>(),
graph->GetArena()->Adapter(kArenaAllocBoundsCheckElimination)),
finite_loop_(graph->GetArena()->Adapter(kArenaAllocBoundsCheckElimination)),
- need_to_revisit_block_(false),
- has_deoptimization_on_constant_subscripts_(false),
+ has_dom_based_dynamic_bce_(false),
initial_block_size_(graph->GetBlocks().size()),
side_effects_(side_effects),
induction_range_(induction_analysis) {}
void VisitBasicBlock(HBasicBlock* block) OVERRIDE {
DCHECK(!IsAddedBlock(block));
- first_constant_index_bounds_check_map_.clear();
+ first_index_bounds_check_map_.clear();
HGraphVisitor::VisitBasicBlock(block);
- if (need_to_revisit_block_) {
- AddComparesWithDeoptimization(block);
- need_to_revisit_block_ = false;
- first_constant_index_bounds_check_map_.clear();
- GetValueRangeMap(block)->clear();
- HGraphVisitor::VisitBasicBlock(block);
- }
+ AddComparesWithDeoptimization(block);
}
void Finish() {
@@ -555,8 +562,7 @@
// Added blocks don't keep value ranges.
return nullptr;
}
- uint32_t block_id = basic_block->GetBlockId();
- return &maps_[block_id];
+ return &maps_[basic_block->GetBlockId()];
}
// Traverse up the dominator tree to look for value range info.
@@ -576,6 +582,11 @@
return nullptr;
}
+ // Helper method to assign a new range to an instruction in given basic block.
+ void AssignRange(HBasicBlock* basic_block, HInstruction* instruction, ValueRange* range) {
+ GetValueRangeMap(basic_block)->Overwrite(instruction->GetId(), range);
+ }
+
// Narrow the value range of `instruction` at the end of `basic_block` with `range`,
// and push the narrowed value range to `successor`.
void ApplyRangeFromComparison(HInstruction* instruction, HBasicBlock* basic_block,
@@ -583,7 +594,7 @@
ValueRange* existing_range = LookupValueRange(instruction, basic_block);
if (existing_range == nullptr) {
if (range != nullptr) {
- GetValueRangeMap(successor)->Overwrite(instruction->GetId(), range);
+ AssignRange(successor, instruction, range);
}
return;
}
@@ -595,8 +606,7 @@
return;
}
}
- ValueRange* narrowed_range = existing_range->Narrow(range);
- GetValueRangeMap(successor)->Overwrite(instruction->GetId(), narrowed_range);
+ AssignRange(successor, instruction, existing_range->Narrow(range));
}
// Special case that we may simultaneously narrow two MonotonicValueRange's to
@@ -778,37 +788,37 @@
array_length->IsPhi());
bool try_dynamic_bce = true;
+ // Analyze index range.
if (!index->IsIntConstant()) {
- // Non-constant subscript.
+ // Non-constant index.
ValueBound lower = ValueBound(nullptr, 0); // constant 0
ValueBound upper = ValueBound(array_length, -1); // array_length - 1
ValueRange array_range(GetGraph()->GetArena(), lower, upper);
- // Try range obtained by dominator-based analysis.
+ // Try index range obtained by dominator-based analysis.
ValueRange* index_range = LookupValueRange(index, block);
if (index_range != nullptr && index_range->FitsIn(&array_range)) {
ReplaceInstruction(bounds_check, index);
return;
}
- // Try range obtained by induction variable analysis.
+ // Try index range obtained by induction variable analysis.
// Disables dynamic bce if OOB is certain.
if (InductionRangeFitsIn(&array_range, bounds_check, index, &try_dynamic_bce)) {
ReplaceInstruction(bounds_check, index);
return;
}
} else {
- // Constant subscript.
+ // Constant index.
int32_t constant = index->AsIntConstant()->GetValue();
if (constant < 0) {
// Will always throw exception.
return;
- }
- if (array_length->IsIntConstant()) {
+ } else if (array_length->IsIntConstant()) {
if (constant < array_length->AsIntConstant()->GetValue()) {
ReplaceInstruction(bounds_check, index);
}
return;
}
-
+ // Analyze array length range.
DCHECK(array_length->IsArrayLength());
ValueRange* existing_range = LookupValueRange(array_length, block);
if (existing_range != nullptr) {
@@ -823,37 +833,35 @@
// bounds check.
}
}
-
- if (first_constant_index_bounds_check_map_.find(array_length->GetId()) ==
- first_constant_index_bounds_check_map_.end()) {
- // Remember the first bounds check against array_length of a constant index.
- // That bounds check instruction has an associated HEnvironment where we
- // may add an HDeoptimize to eliminate bounds checks of constant indices
- // against array_length.
- first_constant_index_bounds_check_map_.Put(array_length->GetId(), bounds_check);
- } else {
- // We've seen it at least twice. It's beneficial to introduce a compare with
- // deoptimization fallback to eliminate the bounds checks.
- need_to_revisit_block_ = true;
- }
-
// Once we have an array access like 'array[5] = 1', we record array.length >= 6.
// We currently don't do it for non-constant index since a valid array[i] can't prove
// a valid array[i-1] yet due to the lower bound side.
if (constant == std::numeric_limits<int32_t>::max()) {
// Max() as an index will definitely throw AIOOBE.
return;
+ } else {
+ ValueBound lower = ValueBound(nullptr, constant + 1);
+ ValueBound upper = ValueBound::Max();
+ ValueRange* range = new (GetGraph()->GetArena())
+ ValueRange(GetGraph()->GetArena(), lower, upper);
+ AssignRange(block, array_length, range);
}
- ValueBound lower = ValueBound(nullptr, constant + 1);
- ValueBound upper = ValueBound::Max();
- ValueRange* range = new (GetGraph()->GetArena())
- ValueRange(GetGraph()->GetArena(), lower, upper);
- GetValueRangeMap(block)->Overwrite(array_length->GetId(), range);
}
// If static analysis fails, and OOB is not certain, try dynamic elimination.
if (try_dynamic_bce) {
- TryDynamicBCE(bounds_check);
+ // Try loop-based dynamic elimination.
+ if (TryDynamicBCE(bounds_check)) {
+ return;
+ }
+ // Prepare dominator-based dynamic elimination.
+ if (first_index_bounds_check_map_.find(array_length->GetId()) ==
+ first_index_bounds_check_map_.end()) {
+ // Remember the first bounds check against each array_length. That bounds check
+ // instruction has an associated HEnvironment where we may add an HDeoptimize
+ // to eliminate subsequent bounds checks against the same array_length.
+ first_index_bounds_check_map_.Put(array_length->GetId(), bounds_check);
+ }
}
}
@@ -914,7 +922,7 @@
increment,
bound);
}
- GetValueRangeMap(phi->GetBlock())->Overwrite(phi->GetId(), range);
+ AssignRange(phi->GetBlock(), phi, range);
}
}
}
@@ -942,7 +950,7 @@
}
ValueRange* range = left_range->Add(right->AsIntConstant()->GetValue());
if (range != nullptr) {
- GetValueRangeMap(add->GetBlock())->Overwrite(add->GetId(), range);
+ AssignRange(add->GetBlock(), add, range);
}
}
}
@@ -957,7 +965,7 @@
}
ValueRange* range = left_range->Add(-right->AsIntConstant()->GetValue());
if (range != nullptr) {
- GetValueRangeMap(sub->GetBlock())->Overwrite(sub->GetId(), range);
+ AssignRange(sub->GetBlock(), sub, range);
return;
}
}
@@ -997,7 +1005,7 @@
GetGraph()->GetArena(),
ValueBound(nullptr, right_const - upper.GetConstant()),
ValueBound(array_length, right_const - lower.GetConstant()));
- GetValueRangeMap(sub->GetBlock())->Overwrite(sub->GetId(), range);
+ AssignRange(sub->GetBlock(), sub, range);
}
}
}
@@ -1045,7 +1053,7 @@
GetGraph()->GetArena(),
ValueBound(nullptr, std::numeric_limits<int32_t>::min()),
ValueBound(left, 0));
- GetValueRangeMap(instruction->GetBlock())->Overwrite(instruction->GetId(), range);
+ AssignRange(instruction->GetBlock(), instruction, range);
}
}
@@ -1071,7 +1079,7 @@
GetGraph()->GetArena(),
ValueBound(nullptr, 0),
ValueBound(nullptr, constant));
- GetValueRangeMap(instruction->GetBlock())->Overwrite(instruction->GetId(), range);
+ AssignRange(instruction->GetBlock(), instruction, range);
}
}
}
@@ -1095,30 +1103,11 @@
if (existing_range != nullptr) {
range = existing_range->Narrow(range);
}
- GetValueRangeMap(new_array->GetBlock())->Overwrite(left->GetId(), range);
+ AssignRange(new_array->GetBlock(), left, range);
}
}
}
- void VisitDeoptimize(HDeoptimize* deoptimize) OVERRIDE {
- if (!deoptimize->InputAt(0)->IsLessThanOrEqual()) {
- return;
- }
- // If this instruction was added by AddCompareWithDeoptimization(), narrow
- // the range accordingly in subsequent basic blocks.
- HLessThanOrEqual* less_than_or_equal = deoptimize->InputAt(0)->AsLessThanOrEqual();
- HInstruction* instruction = less_than_or_equal->InputAt(0);
- if (instruction->IsArrayLength()) {
- HInstruction* constant = less_than_or_equal->InputAt(1);
- DCHECK(constant->IsIntConstant());
- DCHECK(constant->AsIntConstant()->GetValue() <= kMaxConstantForAddingDeoptimize);
- ValueBound lower = ValueBound(nullptr, constant->AsIntConstant()->GetValue() + 1);
- ValueRange* range = new (GetGraph()->GetArena())
- ValueRange(GetGraph()->GetArena(), lower, ValueBound::Max());
- GetValueRangeMap(deoptimize->GetBlock())->Overwrite(instruction->GetId(), range);
- }
- }
-
/**
* After null/bounds checks are eliminated, some invariant array references
* may be exposed underneath which can be hoisted out of the loop to the
@@ -1130,13 +1119,12 @@
* a[i][j] = 0; --a[i]--+
* }
*
- * Note: this optimization is no longer applied after deoptimization on array references
- * with constant subscripts has occurred (see AddCompareWithDeoptimization()), since in
- * those cases it would be unsafe to hoist array references across their deoptimization
- * instruction inside a loop.
+ * Note: this optimization is no longer applied after dominator-based dynamic deoptimization
+ * has occurred (see AddCompareWithDeoptimization()), since in those cases it would be
+ * unsafe to hoist array references across their deoptimization instruction inside a loop.
*/
void VisitArrayGet(HArrayGet* array_get) OVERRIDE {
- if (!has_deoptimization_on_constant_subscripts_ && array_get->IsInLoop()) {
+ if (!has_dom_based_dynamic_bce_ && array_get->IsInLoop()) {
HLoopInformation* loop = array_get->GetBlock()->GetLoopInformation();
if (loop->IsDefinedOutOfTheLoop(array_get->InputAt(0)) &&
loop->IsDefinedOutOfTheLoop(array_get->InputAt(1))) {
@@ -1148,69 +1136,105 @@
}
}
- void AddCompareWithDeoptimization(HInstruction* array_length,
- HIntConstant* const_instr,
- HBasicBlock* block) {
- DCHECK(array_length->IsArrayLength());
- ValueRange* range = LookupValueRange(array_length, block);
- ValueBound lower_bound = range->GetLower();
- DCHECK(lower_bound.IsConstant());
- DCHECK(const_instr->GetValue() <= kMaxConstantForAddingDeoptimize);
- // Note that the lower bound of the array length may have been refined
- // through other instructions (such as `HNewArray(length - 4)`).
- DCHECK_LE(const_instr->GetValue() + 1, lower_bound.GetConstant());
-
- // If array_length is less than lower_const, deoptimize.
- HBoundsCheck* bounds_check = first_constant_index_bounds_check_map_.Get(
- array_length->GetId())->AsBoundsCheck();
- HCondition* cond = new (GetGraph()->GetArena()) HLessThanOrEqual(array_length, const_instr);
- HDeoptimize* deoptimize = new (GetGraph()->GetArena())
- HDeoptimize(cond, bounds_check->GetDexPc());
- block->InsertInstructionBefore(cond, bounds_check);
- block->InsertInstructionBefore(deoptimize, bounds_check);
- deoptimize->CopyEnvironmentFrom(bounds_check->GetEnvironment());
- // Flag that this kind of deoptimization on array references with constant
- // subscripts has occurred to prevent further hoisting of these references.
- has_deoptimization_on_constant_subscripts_ = true;
+ // Perform dominator-based dynamic elimination on suitable set of bounds checks.
+ void AddCompareWithDeoptimization(HBasicBlock* block,
+ HInstruction* array_length,
+ HInstruction* base,
+ int32_t min_c, int32_t max_c) {
+ HBoundsCheck* bounds_check =
+ first_index_bounds_check_map_.Get(array_length->GetId())->AsBoundsCheck();
+ // Construct deoptimization on single or double bounds on range [base-min_c,base+max_c],
+ // for example either for a[0]..a[3] just 3 or for a[base-1]..a[base+3] both base-1
+ // and base+3, since we made the assumption any in between value may occur too.
+ static_assert(kMaxLengthForAddingDeoptimize < std::numeric_limits<int32_t>::max(),
+ "Incorrect max length may be subject to arithmetic wrap-around");
+ HInstruction* upper = GetGraph()->GetIntConstant(max_c);
+ if (base == nullptr) {
+ DCHECK_GE(min_c, 0);
+ } else {
+ HInstruction* lower = new (GetGraph()->GetArena())
+ HAdd(Primitive::kPrimInt, base, GetGraph()->GetIntConstant(min_c));
+ upper = new (GetGraph()->GetArena()) HAdd(Primitive::kPrimInt, base, upper);
+ block->InsertInstructionBefore(lower, bounds_check);
+ block->InsertInstructionBefore(upper, bounds_check);
+ InsertDeoptInBlock(bounds_check, new (GetGraph()->GetArena()) HAbove(lower, upper));
+ }
+ InsertDeoptInBlock(bounds_check, new (GetGraph()->GetArena()) HAboveOrEqual(upper, array_length));
+ // Flag that this kind of deoptimization has occurred.
+ has_dom_based_dynamic_bce_ = true;
}
+ // Attempt dominator-based dynamic elimination on remaining candidates.
void AddComparesWithDeoptimization(HBasicBlock* block) {
- for (ArenaSafeMap<int, HBoundsCheck*>::iterator it =
- first_constant_index_bounds_check_map_.begin();
- it != first_constant_index_bounds_check_map_.end();
- ++it) {
- HBoundsCheck* bounds_check = it->second;
+ for (const auto& entry : first_index_bounds_check_map_) {
+ HBoundsCheck* bounds_check = entry.second;
+ HInstruction* index = bounds_check->InputAt(0);
HInstruction* array_length = bounds_check->InputAt(1);
if (!array_length->IsArrayLength()) {
- // Prior deoptimizations may have changed the array length to a phi.
- // TODO(mingyao): propagate the range to the phi?
- DCHECK(array_length->IsPhi()) << array_length->DebugName();
- continue;
+ continue; // disregard phis and constants
}
- HIntConstant* lower_bound_const_instr = nullptr;
- int32_t lower_bound_const = std::numeric_limits<int32_t>::min();
- size_t counter = 0;
- // Count the constant indexing for which bounds checks haven't
- // been removed yet.
- for (HUseIterator<HInstruction*> it2(array_length->GetUses());
- !it2.Done();
- it2.Advance()) {
+ // Collect all bounds checks are still there and that are related as "a[base + constant]"
+ // for a base instruction (possibly absent) and various constants. Note that no attempt
+ // is made to partition the set into matching subsets (viz. a[0], a[1] and a[base+1] and
+ // a[base+2] are considered as one set).
+ // TODO: would such a partitioning be worthwhile?
+ ValueBound value = ValueBound::AsValueBound(index);
+ HInstruction* base = value.GetInstruction();
+ int32_t min_c = base == nullptr ? 0 : value.GetConstant();
+ int32_t max_c = value.GetConstant();
+ ArenaVector<HBoundsCheck*> candidates(
+ GetGraph()->GetArena()->Adapter(kArenaAllocBoundsCheckElimination));
+ ArenaVector<HBoundsCheck*> standby(
+ GetGraph()->GetArena()->Adapter(kArenaAllocBoundsCheckElimination));
+ for (HUseIterator<HInstruction*> it2(array_length->GetUses()); !it2.Done(); it2.Advance()) {
+ // Another bounds check in same or dominated block?
HInstruction* user = it2.Current()->GetUser();
- if (user->GetBlock() == block &&
- user->IsBoundsCheck() &&
- user->AsBoundsCheck()->InputAt(0)->IsIntConstant()) {
- DCHECK_EQ(array_length, user->AsBoundsCheck()->InputAt(1));
- HIntConstant* const_instr = user->AsBoundsCheck()->InputAt(0)->AsIntConstant();
- if (const_instr->GetValue() > lower_bound_const) {
- lower_bound_const = const_instr->GetValue();
- lower_bound_const_instr = const_instr;
+ HBasicBlock* other_block = user->GetBlock();
+ if (user->IsBoundsCheck() && block->Dominates(other_block)) {
+ HBoundsCheck* other_bounds_check = user->AsBoundsCheck();
+ HInstruction* other_index = other_bounds_check->InputAt(0);
+ HInstruction* other_array_length = other_bounds_check->InputAt(1);
+ ValueBound other_value = ValueBound::AsValueBound(other_index);
+ if (array_length == other_array_length && base == other_value.GetInstruction()) {
+ int32_t other_c = other_value.GetConstant();
+ // Since a subsequent dominated block could be under a conditional, only accept
+ // the other bounds check if it is in same block or both blocks dominate the exit.
+ // TODO: we could improve this by testing proper post-dominance, or even if this
+ // constant is seen along *all* conditional paths that follow.
+ HBasicBlock* exit = GetGraph()->GetExitBlock();
+ if (block == user->GetBlock() ||
+ (block->Dominates(exit) && other_block->Dominates(exit))) {
+ min_c = std::min(min_c, other_c);
+ max_c = std::max(max_c, other_c);
+ candidates.push_back(other_bounds_check);
+ } else {
+ // Add this candidate later only if it falls into the range.
+ standby.push_back(other_bounds_check);
+ }
}
- counter++;
}
}
- if (counter >= kThresholdForAddingDeoptimize &&
- lower_bound_const_instr->GetValue() <= kMaxConstantForAddingDeoptimize) {
- AddCompareWithDeoptimization(array_length, lower_bound_const_instr, block);
+ // Add standby candidates that fall in selected range.
+ for (HBoundsCheck* other_bounds_check : standby) {
+ HInstruction* other_index = other_bounds_check->InputAt(0);
+ int32_t other_c = ValueBound::AsValueBound(other_index).GetConstant();
+ if (min_c <= other_c && other_c <= max_c) {
+ candidates.push_back(other_bounds_check);
+ }
+ }
+ // Perform dominator-based deoptimization if it seems profitable. Note that we reject cases
+ // where the distance min_c:max_c range gets close to the maximum possible array length,
+ // since those cases are likely to always deopt (such situations do not necessarily go
+ // OOB, though, since the programmer could rely on wrap-around from max to min).
+ size_t threshold = kThresholdForAddingDeoptimize + (base == nullptr ? 0 : 1); // extra test?
+ uint32_t distance = static_cast<uint32_t>(max_c) - static_cast<uint32_t>(min_c);
+ if (candidates.size() >= threshold &&
+ (base != nullptr || min_c >= 0) && // reject certain OOB
+ distance <= kMaxLengthForAddingDeoptimize) { // reject likely/certain deopt
+ AddCompareWithDeoptimization(block, array_length, base, min_c, max_c);
+ for (HInstruction* other_bounds_check : candidates) {
+ ReplaceInstruction(other_bounds_check, other_bounds_check->InputAt(0));
+ }
}
}
}
@@ -1227,27 +1251,28 @@
InductionVarRange::Value v1;
InductionVarRange::Value v2;
bool needs_finite_test = false;
- induction_range_.GetInductionRange(context, index, &v1, &v2, &needs_finite_test);
- do {
- if (v1.is_known && (v1.a_constant == 0 || v1.a_constant == 1) &&
- v2.is_known && (v2.a_constant == 0 || v2.a_constant == 1)) {
- DCHECK(v1.a_constant == 1 || v1.instruction == nullptr);
- DCHECK(v2.a_constant == 1 || v2.instruction == nullptr);
- ValueRange index_range(GetGraph()->GetArena(),
- ValueBound(v1.instruction, v1.b_constant),
- ValueBound(v2.instruction, v2.b_constant));
- // If analysis reveals a certain OOB, disable dynamic BCE.
- if (index_range.GetLower().LessThan(array_range->GetLower()) ||
- index_range.GetUpper().GreaterThan(array_range->GetUpper())) {
- *try_dynamic_bce = false;
- return false;
+ if (induction_range_.GetInductionRange(context, index, &v1, &v2, &needs_finite_test)) {
+ do {
+ if (v1.is_known && (v1.a_constant == 0 || v1.a_constant == 1) &&
+ v2.is_known && (v2.a_constant == 0 || v2.a_constant == 1)) {
+ DCHECK(v1.a_constant == 1 || v1.instruction == nullptr);
+ DCHECK(v2.a_constant == 1 || v2.instruction == nullptr);
+ ValueRange index_range(GetGraph()->GetArena(),
+ ValueBound(v1.instruction, v1.b_constant),
+ ValueBound(v2.instruction, v2.b_constant));
+ // If analysis reveals a certain OOB, disable dynamic BCE.
+ if (index_range.GetLower().LessThan(array_range->GetLower()) ||
+ index_range.GetUpper().GreaterThan(array_range->GetUpper())) {
+ *try_dynamic_bce = false;
+ return false;
+ }
+ // Use analysis for static bce only if loop is finite.
+ if (!needs_finite_test && index_range.FitsIn(array_range)) {
+ return true;
+ }
}
- // Use analysis for static bce only if loop is finite.
- if (!needs_finite_test && index_range.FitsIn(array_range)) {
- return true;
- }
- }
- } while (induction_range_.RefineOuter(&v1, &v2));
+ } while (induction_range_.RefineOuter(&v1, &v2));
+ }
return false;
}
@@ -1258,7 +1283,7 @@
* deoptimization). If no deoptimization occurs, the loop is executed with all corresponding
* bounds checks and related null checks removed.
*/
- void TryDynamicBCE(HBoundsCheck* instruction) {
+ bool TryDynamicBCE(HBoundsCheck* instruction) {
HLoopInformation* loop = instruction->GetBlock()->GetLoopInformation();
HInstruction* index = instruction->InputAt(0);
HInstruction* length = instruction->InputAt(1);
@@ -1284,11 +1309,13 @@
HBasicBlock* block = GetPreHeader(loop, instruction);
induction_range_.GenerateRangeCode(instruction, index, GetGraph(), block, &lower, &upper);
if (lower != nullptr) {
- InsertDeopt(loop, block, new (GetGraph()->GetArena()) HAbove(lower, upper));
+ InsertDeoptInLoop(loop, block, new (GetGraph()->GetArena()) HAbove(lower, upper));
}
- InsertDeopt(loop, block, new (GetGraph()->GetArena()) HAboveOrEqual(upper, length));
+ InsertDeoptInLoop(loop, block, new (GetGraph()->GetArena()) HAboveOrEqual(upper, length));
ReplaceInstruction(instruction, index);
+ return true;
}
+ return false;
}
/**
@@ -1381,7 +1408,7 @@
HBasicBlock* block = GetPreHeader(loop, check);
HInstruction* cond =
new (GetGraph()->GetArena()) HEqual(array, GetGraph()->GetNullConstant());
- InsertDeopt(loop, block, cond);
+ InsertDeoptInLoop(loop, block, cond);
ReplaceInstruction(check, array);
return true;
}
@@ -1447,8 +1474,8 @@
return loop->GetPreHeader();
}
- /** Inserts a deoptimization test. */
- void InsertDeopt(HLoopInformation* loop, HBasicBlock* block, HInstruction* condition) {
+ /** Inserts a deoptimization test in a loop preheader. */
+ void InsertDeoptInLoop(HLoopInformation* loop, HBasicBlock* block, HInstruction* condition) {
HInstruction* suspend = loop->GetSuspendCheck();
block->InsertInstructionBefore(condition, block->GetLastInstruction());
HDeoptimize* deoptimize =
@@ -1460,6 +1487,16 @@
}
}
+ /** Inserts a deoptimization test right before a bounds check. */
+ void InsertDeoptInBlock(HBoundsCheck* bounds_check, HInstruction* condition) {
+ HBasicBlock* block = bounds_check->GetBlock();
+ block->InsertInstructionBefore(condition, bounds_check);
+ HDeoptimize* deoptimize =
+ new (GetGraph()->GetArena()) HDeoptimize(condition, bounds_check->GetDexPc());
+ block->InsertInstructionBefore(deoptimize, bounds_check);
+ deoptimize->CopyEnvironmentFrom(bounds_check->GetEnvironment());
+ }
+
/** Hoists instruction out of the loop to preheader or deoptimization block. */
void HoistToPreHeaderOrDeoptBlock(HLoopInformation* loop, HInstruction* instruction) {
HBasicBlock* block = GetPreHeader(loop, instruction);
@@ -1627,9 +1664,9 @@
// A set of maps, one per basic block, from instruction to range.
ArenaVector<ArenaSafeMap<int, ValueRange*>> maps_;
- // Map an HArrayLength instruction's id to the first HBoundsCheck instruction in
- // a block that checks a constant index against that HArrayLength.
- ArenaSafeMap<int, HBoundsCheck*> first_constant_index_bounds_check_map_;
+ // Map an HArrayLength instruction's id to the first HBoundsCheck instruction
+ // in a block that checks an index against that HArrayLength.
+ ArenaSafeMap<int, HBoundsCheck*> first_index_bounds_check_map_;
// Early-exit loop bookkeeping.
ArenaSafeMap<uint32_t, bool> early_exit_loop_;
@@ -1640,15 +1677,8 @@
// Finite loop bookkeeping.
ArenaSet<uint32_t> finite_loop_;
- // For the block, there is at least one HArrayLength instruction for which there
- // is more than one bounds check instruction with constant indexing. And it's
- // beneficial to add a compare instruction that has deoptimization fallback and
- // eliminate those bounds checks.
- bool need_to_revisit_block_;
-
- // Flag that denotes whether deoptimization has occurred on array references
- // with constant subscripts (see AddCompareWithDeoptimization()).
- bool has_deoptimization_on_constant_subscripts_;
+ // Flag that denotes whether dominator-based dynamic elimination has occurred.
+ bool has_dom_based_dynamic_bce_;
// Initial number of blocks.
uint32_t initial_block_size_;
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index c7430e7..05e1356 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -32,114 +32,12 @@
#include "nodes.h"
#include "primitive.h"
#include "scoped_thread_state_change.h"
+#include "ssa_builder.h"
#include "thread.h"
#include "utils/dex_cache_arrays_layout-inl.h"
namespace art {
-/**
- * Helper class to add HTemporary instructions. This class is used when
- * converting a DEX instruction to multiple HInstruction, and where those
- * instructions do not die at the following instruction, but instead spans
- * multiple instructions.
- */
-class Temporaries : public ValueObject {
- public:
- explicit Temporaries(HGraph* graph) : graph_(graph), index_(0) {}
-
- void Add(HInstruction* instruction) {
- HInstruction* temp = new (graph_->GetArena()) HTemporary(index_, instruction->GetDexPc());
- instruction->GetBlock()->AddInstruction(temp);
-
- DCHECK(temp->GetPrevious() == instruction);
-
- size_t offset;
- if (instruction->GetType() == Primitive::kPrimLong
- || instruction->GetType() == Primitive::kPrimDouble) {
- offset = 2;
- } else {
- offset = 1;
- }
- index_ += offset;
-
- graph_->UpdateTemporariesVRegSlots(index_);
- }
-
- private:
- HGraph* const graph_;
-
- // Current index in the temporary stack, updated by `Add`.
- size_t index_;
-};
-
-class SwitchTable : public ValueObject {
- public:
- SwitchTable(const Instruction& instruction, uint32_t dex_pc, bool sparse)
- : instruction_(instruction), dex_pc_(dex_pc), sparse_(sparse) {
- int32_t table_offset = instruction.VRegB_31t();
- const uint16_t* table = reinterpret_cast<const uint16_t*>(&instruction) + table_offset;
- if (sparse) {
- CHECK_EQ(table[0], static_cast<uint16_t>(Instruction::kSparseSwitchSignature));
- } else {
- CHECK_EQ(table[0], static_cast<uint16_t>(Instruction::kPackedSwitchSignature));
- }
- num_entries_ = table[1];
- values_ = reinterpret_cast<const int32_t*>(&table[2]);
- }
-
- uint16_t GetNumEntries() const {
- return num_entries_;
- }
-
- void CheckIndex(size_t index) const {
- if (sparse_) {
- // In a sparse table, we have num_entries_ keys and num_entries_ values, in that order.
- DCHECK_LT(index, 2 * static_cast<size_t>(num_entries_));
- } else {
- // In a packed table, we have the starting key and num_entries_ values.
- DCHECK_LT(index, 1 + static_cast<size_t>(num_entries_));
- }
- }
-
- int32_t GetEntryAt(size_t index) const {
- CheckIndex(index);
- return values_[index];
- }
-
- uint32_t GetDexPcForIndex(size_t index) const {
- CheckIndex(index);
- return dex_pc_ +
- (reinterpret_cast<const int16_t*>(values_ + index) -
- reinterpret_cast<const int16_t*>(&instruction_));
- }
-
- // Index of the first value in the table.
- size_t GetFirstValueIndex() const {
- if (sparse_) {
- // In a sparse table, we have num_entries_ keys and num_entries_ values, in that order.
- return num_entries_;
- } else {
- // In a packed table, we have the starting key and num_entries_ values.
- return 1;
- }
- }
-
- private:
- const Instruction& instruction_;
- const uint32_t dex_pc_;
-
- // Whether this is a sparse-switch table (or a packed-switch one).
- const bool sparse_;
-
- // This can't be const as it needs to be computed off of the given instruction, and complicated
- // expressions in the initializer list seemed very ugly.
- uint16_t num_entries_;
-
- const int32_t* values_;
-
- DISALLOW_COPY_AND_ASSIGN(SwitchTable);
-};
-
void HGraphBuilder::InitializeLocals(uint16_t count) {
graph_->SetNumberOfVRegs(count);
locals_.resize(count);
@@ -351,7 +249,7 @@
// loop for synchronized blocks.
if (block->HasThrowingInstructions()) {
// Try to find a TryItem covering the block.
- DCHECK_NE(block->GetDexPc(), kNoDexPc) << "Block must have a dec_pc to find its TryItem.";
+ DCHECK_NE(block->GetDexPc(), kNoDexPc) << "Block must have a dex_pc to find its TryItem.";
const int32_t try_item_idx = DexFile::FindTryItem(code_item, block->GetDexPc());
if (try_item_idx != -1) {
// Block throwing and in a TryItem. Store the try block information.
@@ -425,7 +323,8 @@
}
}
-bool HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) {
+GraphAnalysisResult HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item,
+ StackHandleScopeCollection* handles) {
DCHECK(graph_->GetBlocks().empty());
const uint16_t* code_ptr = code_item.insns_;
@@ -452,12 +351,12 @@
// start a new block, and create these blocks.
if (!ComputeBranchTargets(code_ptr, code_end, &number_of_branches)) {
MaybeRecordStat(MethodCompilationStat::kNotCompiledBranchOutsideMethodCode);
- return false;
+ return kAnalysisInvalidBytecode;
}
// Note that the compiler driver is null when unit testing.
if ((compiler_driver_ != nullptr) && SkipCompilation(code_item, number_of_branches)) {
- return false;
+ return kAnalysisInvalidBytecode;
}
// Find locations where we want to generate extra stackmaps for native debugging.
@@ -488,7 +387,7 @@
}
}
if (!AnalyzeDexInstruction(instruction, dex_pc)) {
- return false;
+ return kAnalysisInvalidBytecode;
}
dex_pc += instruction.SizeInCodeUnits();
code_ptr += instruction.SizeInCodeUnits();
@@ -507,7 +406,13 @@
// non-exceptional edges to have been created.
InsertTryBoundaryBlocks(code_item);
- return true;
+ GraphAnalysisResult result = graph_->BuildDominatorTree();
+ if (result != kAnalysisSuccess) {
+ return result;
+ }
+
+ graph_->InitializeInexactObjectRTI(handles);
+ return SsaBuilder(graph_, handles).BuildSsa();
}
void HGraphBuilder::MaybeUpdateCurrentBlock(size_t dex_pc) {
@@ -1234,12 +1139,10 @@
size_t start_index = 0;
size_t argument_index = 0;
if (invoke->GetOriginalInvokeType() != InvokeType::kStatic) { // Instance call.
- Temporaries temps(graph_);
HInstruction* arg = LoadLocal(
is_range ? register_index : args[0], Primitive::kPrimNot, invoke->GetDexPc());
HNullCheck* null_check = new (arena_) HNullCheck(arg, invoke->GetDexPc());
current_block_->AddInstruction(null_check);
- temps.Add(null_check);
invoke->SetArgumentAt(0, null_check);
start_index = 1;
argument_index = 1;
@@ -1337,9 +1240,6 @@
? GetFieldAccessType(*dex_file_, field_index)
: resolved_field->GetTypeAsPrimitiveType();
if (is_put) {
- Temporaries temps(graph_);
- // We need one temporary for the null check.
- temps.Add(null_check);
HInstruction* value = LoadLocal(source_or_dest_reg, field_type, dex_pc);
HInstruction* field_set = nullptr;
if (resolved_field == nullptr) {
@@ -1524,8 +1424,6 @@
uint16_t class_def_index = klass->GetDexClassDefIndex();
if (is_put) {
// We need to keep the class alive before loading the value.
- Temporaries temps(graph_);
- temps.Add(cls);
HInstruction* value = LoadLocal(source_or_dest_reg, field_type, dex_pc);
DCHECK_EQ(value->GetType(), field_type);
current_block_->AddInstruction(new (arena_) HStaticFieldSet(cls,
@@ -1578,9 +1476,7 @@
|| (type == Primitive::kPrimInt && second->AsIntConstant()->GetValue() == 0)
|| (type == Primitive::kPrimLong && second->AsLongConstant()->GetValue() == 0)) {
second = new (arena_) HDivZeroCheck(second, dex_pc);
- Temporaries temps(graph_);
current_block_->AddInstruction(second);
- temps.Add(current_block_->GetLastInstruction());
}
if (isDiv) {
@@ -1599,21 +1495,15 @@
uint8_t array_reg = instruction.VRegB_23x();
uint8_t index_reg = instruction.VRegC_23x();
- // We need one temporary for the null check, one for the index, and one for the length.
- Temporaries temps(graph_);
-
HInstruction* object = LoadLocal(array_reg, Primitive::kPrimNot, dex_pc);
object = new (arena_) HNullCheck(object, dex_pc);
current_block_->AddInstruction(object);
- temps.Add(object);
HInstruction* length = new (arena_) HArrayLength(object, dex_pc);
current_block_->AddInstruction(length);
- temps.Add(length);
HInstruction* index = LoadLocal(index_reg, Primitive::kPrimInt, dex_pc);
index = new (arena_) HBoundsCheck(index, length, dex_pc);
current_block_->AddInstruction(index);
- temps.Add(index);
if (is_put) {
HInstruction* value = LoadLocal(source_or_dest_reg, anticipated_type, dex_pc);
// TODO: Insert a type check node if the type is Object.
@@ -1654,8 +1544,6 @@
bool is_reference_array = (primitive == 'L') || (primitive == '[');
Primitive::Type type = is_reference_array ? Primitive::kPrimNot : Primitive::kPrimInt;
- Temporaries temps(graph_);
- temps.Add(object);
for (size_t i = 0; i < number_of_vreg_arguments; ++i) {
HInstruction* value = LoadLocal(is_range ? register_index + i : args[i], type, dex_pc);
HInstruction* index = graph_->GetIntConstant(i, dex_pc);
@@ -1680,11 +1568,9 @@
}
void HGraphBuilder::BuildFillArrayData(const Instruction& instruction, uint32_t dex_pc) {
- Temporaries temps(graph_);
HInstruction* array = LoadLocal(instruction.VRegA_31t(), Primitive::kPrimNot, dex_pc);
HNullCheck* null_check = new (arena_) HNullCheck(array, dex_pc);
current_block_->AddInstruction(null_check);
- temps.Add(null_check);
HInstruction* length = new (arena_) HArrayLength(null_check, dex_pc);
current_block_->AddInstruction(length);
@@ -1801,10 +1687,6 @@
compiler_driver_->CanAssumeTypeIsPresentInDexCache(dex_file, type_index));
current_block_->AddInstruction(cls);
- // The class needs a temporary before being used by the type check.
- Temporaries temps(graph_);
- temps.Add(cls);
-
TypeCheckKind check_kind = ComputeTypeCheckKind(resolved_class);
if (instruction.Opcode() == Instruction::INSTANCE_OF) {
current_block_->AddInstruction(new (arena_) HInstanceOf(object, cls, check_kind, dex_pc));
@@ -2883,8 +2765,6 @@
case Instruction::ARRAY_LENGTH: {
HInstruction* object = LoadLocal(instruction.VRegB_12x(), Primitive::kPrimNot, dex_pc);
- // No need for a temporary for the null check, it is the only input of the following
- // instruction.
object = new (arena_) HNullCheck(object, dex_pc);
current_block_->AddInstruction(object);
current_block_->AddInstruction(new (arena_) HArrayLength(object, dex_pc));
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 1d604e7..e3dd0e8 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -30,7 +30,6 @@
namespace art {
class Instruction;
-class SwitchTable;
class HGraphBuilder : public ValueObject {
public:
@@ -81,7 +80,8 @@
null_dex_cache_(),
dex_cache_(null_dex_cache_) {}
- bool BuildGraph(const DexFile::CodeItem& code);
+ GraphAnalysisResult BuildGraph(const DexFile::CodeItem& code,
+ StackHandleScopeCollection* handles);
static constexpr const char* kBuilderPassName = "builder";
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index a3bbfdb..c2c8ccf 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -287,19 +287,6 @@
}
}
-Location CodeGenerator::GetTemporaryLocation(HTemporary* temp) const {
- uint16_t number_of_locals = GetGraph()->GetNumberOfLocalVRegs();
- // The type of the previous instruction tells us if we need a single or double stack slot.
- Primitive::Type type = temp->GetType();
- int32_t temp_size = (type == Primitive::kPrimLong) || (type == Primitive::kPrimDouble) ? 2 : 1;
- // Use the temporary region (right below the dex registers).
- int32_t slot = GetFrameSize() - FrameEntrySpillSize()
- - kVRegSize // filler
- - (number_of_locals * kVRegSize)
- - ((temp_size + temp->GetIndex()) * kVRegSize);
- return temp_size == 2 ? Location::DoubleStackSlot(slot) : Location::StackSlot(slot);
-}
-
int32_t CodeGenerator::GetStackSlot(HLocal* local) const {
uint16_t reg_number = local->GetRegNumber();
uint16_t number_of_locals = GetGraph()->GetNumberOfLocalVRegs();
@@ -629,8 +616,76 @@
return stack_map_stream_.PrepareForFillIn();
}
-void CodeGenerator::BuildStackMaps(MemoryRegion region) {
+static void CheckCovers(uint32_t dex_pc,
+ const HGraph& graph,
+ const CodeInfo& code_info,
+ const ArenaVector<HSuspendCheck*>& loop_headers,
+ ArenaVector<size_t>* covered) {
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ for (size_t i = 0; i < loop_headers.size(); ++i) {
+ if (loop_headers[i]->GetDexPc() == dex_pc) {
+ if (graph.IsCompilingOsr()) {
+ DCHECK(code_info.GetOsrStackMapForDexPc(dex_pc, encoding).IsValid());
+ }
+ ++(*covered)[i];
+ }
+ }
+}
+
+// Debug helper to ensure loop entries in compiled code are matched by
+// dex branch instructions.
+static void CheckLoopEntriesCanBeUsedForOsr(const HGraph& graph,
+ const CodeInfo& code_info,
+ const DexFile::CodeItem& code_item) {
+ if (graph.HasTryCatch()) {
+ // One can write loops through try/catch, which we do not support for OSR anyway.
+ return;
+ }
+ ArenaVector<HSuspendCheck*> loop_headers(graph.GetArena()->Adapter(kArenaAllocMisc));
+ for (HReversePostOrderIterator it(graph); !it.Done(); it.Advance()) {
+ if (it.Current()->IsLoopHeader()) {
+ HSuspendCheck* suspend_check = it.Current()->GetLoopInformation()->GetSuspendCheck();
+ if (!suspend_check->GetEnvironment()->IsFromInlinedInvoke()) {
+ loop_headers.push_back(suspend_check);
+ }
+ }
+ }
+ ArenaVector<size_t> covered(loop_headers.size(), 0, graph.GetArena()->Adapter(kArenaAllocMisc));
+ const uint16_t* code_ptr = code_item.insns_;
+ const uint16_t* code_end = code_item.insns_ + code_item.insns_size_in_code_units_;
+
+ size_t dex_pc = 0;
+ while (code_ptr < code_end) {
+ const Instruction& instruction = *Instruction::At(code_ptr);
+ if (instruction.IsBranch()) {
+ uint32_t target = dex_pc + instruction.GetTargetOffset();
+ CheckCovers(target, graph, code_info, loop_headers, &covered);
+ } else if (instruction.IsSwitch()) {
+ SwitchTable table(instruction, dex_pc, instruction.Opcode() == Instruction::SPARSE_SWITCH);
+ uint16_t num_entries = table.GetNumEntries();
+ size_t offset = table.GetFirstValueIndex();
+
+ // Use a larger loop counter type to avoid overflow issues.
+ for (size_t i = 0; i < num_entries; ++i) {
+ // The target of the case.
+ uint32_t target = dex_pc + table.GetEntryAt(i + offset);
+ CheckCovers(target, graph, code_info, loop_headers, &covered);
+ }
+ }
+ dex_pc += instruction.SizeInCodeUnits();
+ code_ptr += instruction.SizeInCodeUnits();
+ }
+
+ for (size_t i = 0; i < covered.size(); ++i) {
+ DCHECK_NE(covered[i], 0u) << "Loop in compiled code has no dex branch equivalent";
+ }
+}
+
+void CodeGenerator::BuildStackMaps(MemoryRegion region, const DexFile::CodeItem& code_item) {
stack_map_stream_.FillIn(region);
+ if (kIsDebugBuild) {
+ CheckLoopEntriesCanBeUsedForOsr(*graph_, CodeInfo(region), code_item);
+ }
}
void CodeGenerator::RecordPcInfo(HInstruction* instruction,
@@ -705,6 +760,46 @@
EmitEnvironment(instruction->GetEnvironment(), slow_path);
stack_map_stream_.EndStackMapEntry();
+
+ HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
+ if (instruction->IsSuspendCheck() &&
+ (info != nullptr) &&
+ graph_->IsCompilingOsr() &&
+ (inlining_depth == 0)) {
+ DCHECK_EQ(info->GetSuspendCheck(), instruction);
+ // We duplicate the stack map as a marker that this stack map can be an OSR entry.
+ // Duplicating it avoids having the runtime recognize and skip an OSR stack map.
+ DCHECK(info->IsIrreducible());
+ stack_map_stream_.BeginStackMapEntry(
+ dex_pc, native_pc, register_mask, locations->GetStackMask(), outer_environment_size, 0);
+ EmitEnvironment(instruction->GetEnvironment(), slow_path);
+ stack_map_stream_.EndStackMapEntry();
+ if (kIsDebugBuild) {
+ HEnvironment* environment = instruction->GetEnvironment();
+ for (size_t i = 0, environment_size = environment->Size(); i < environment_size; ++i) {
+ HInstruction* in_environment = environment->GetInstructionAt(i);
+ if (in_environment != nullptr) {
+ DCHECK(in_environment->IsPhi() || in_environment->IsConstant());
+ Location location = environment->GetLocationAt(i);
+ DCHECK(location.IsStackSlot() ||
+ location.IsDoubleStackSlot() ||
+ location.IsConstant() ||
+ location.IsInvalid());
+ if (location.IsStackSlot() || location.IsDoubleStackSlot()) {
+ DCHECK_LT(location.GetStackIndex(), static_cast<int32_t>(GetFrameSize()));
+ }
+ }
+ }
+ }
+ } else if (kIsDebugBuild) {
+ // Ensure stack maps are unique, by checking that the native pc in the stack map
+ // last emitted is different than the native pc of the stack map just emitted.
+ size_t number_of_stack_maps = stack_map_stream_.GetNumberOfStackMaps();
+ if (number_of_stack_maps > 1) {
+ DCHECK_NE(stack_map_stream_.GetStackMap(number_of_stack_maps - 1).native_pc_offset,
+ stack_map_stream_.GetStackMap(number_of_stack_maps - 2).native_pc_offset);
+ }
+ }
}
bool CodeGenerator::HasStackMapAtCurrentPc() {
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 4f8f146..49c193e 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -187,7 +187,6 @@
virtual void GenerateFrameEntry() = 0;
virtual void GenerateFrameExit() = 0;
virtual void Bind(HBasicBlock* block) = 0;
- virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) = 0;
virtual void MoveConstant(Location destination, int32_t value) = 0;
virtual void MoveLocation(Location dst, Location src, Primitive::Type dst_type) = 0;
virtual void AddLocationAsTemp(Location location, LocationSummary* locations) = 0;
@@ -203,7 +202,6 @@
size_t number_of_out_slots,
const ArenaVector<HBasicBlock*>& block_order);
int32_t GetStackSlot(HLocal* local) const;
- Location GetTemporaryLocation(HTemporary* temp) const;
uint32_t GetFrameSize() const { return frame_size_; }
void SetFrameSize(uint32_t size) { frame_size_ = size; }
@@ -288,7 +286,7 @@
slow_paths_.push_back(slow_path);
}
- void BuildStackMaps(MemoryRegion region);
+ void BuildStackMaps(MemoryRegion region, const DexFile::CodeItem& code_item);
size_t ComputeStackMapsSize();
bool IsLeafMethod() const {
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index c2d9edd..87f52c6 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1195,90 +1195,6 @@
}
}
-void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstruction* move_for) {
- LocationSummary* locations = instruction->GetLocations();
- if (instruction->IsCurrentMethod()) {
- Move32(location, Location::StackSlot(kCurrentMethodStackOffset));
- } else if (locations != nullptr && locations->Out().Equals(location)) {
- return;
- } else if (locations != nullptr && locations->Out().IsConstant()) {
- HConstant* const_to_move = locations->Out().GetConstant();
- if (const_to_move->IsIntConstant() || const_to_move->IsNullConstant()) {
- int32_t value = GetInt32ValueOf(const_to_move);
- if (location.IsRegister()) {
- __ LoadImmediate(location.AsRegister<Register>(), value);
- } else {
- DCHECK(location.IsStackSlot());
- __ LoadImmediate(IP, value);
- __ StoreToOffset(kStoreWord, IP, SP, location.GetStackIndex());
- }
- } else {
- DCHECK(const_to_move->IsLongConstant()) << const_to_move->DebugName();
- int64_t value = const_to_move->AsLongConstant()->GetValue();
- if (location.IsRegisterPair()) {
- __ LoadImmediate(location.AsRegisterPairLow<Register>(), Low32Bits(value));
- __ LoadImmediate(location.AsRegisterPairHigh<Register>(), High32Bits(value));
- } else {
- DCHECK(location.IsDoubleStackSlot());
- __ LoadImmediate(IP, Low32Bits(value));
- __ StoreToOffset(kStoreWord, IP, SP, location.GetStackIndex());
- __ LoadImmediate(IP, High32Bits(value));
- __ StoreToOffset(kStoreWord, IP, SP, location.GetHighStackIndex(kArmWordSize));
- }
- }
- } else if (instruction->IsLoadLocal()) {
- uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal());
- switch (instruction->GetType()) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimInt:
- case Primitive::kPrimNot:
- case Primitive::kPrimFloat:
- Move32(location, Location::StackSlot(stack_slot));
- break;
-
- case Primitive::kPrimLong:
- case Primitive::kPrimDouble:
- Move64(location, Location::DoubleStackSlot(stack_slot));
- break;
-
- default:
- LOG(FATAL) << "Unexpected type " << instruction->GetType();
- }
- } else if (instruction->IsTemporary()) {
- Location temp_location = GetTemporaryLocation(instruction->AsTemporary());
- if (temp_location.IsStackSlot()) {
- Move32(location, temp_location);
- } else {
- DCHECK(temp_location.IsDoubleStackSlot());
- Move64(location, temp_location);
- }
- } else {
- DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary());
- switch (instruction->GetType()) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimNot:
- case Primitive::kPrimInt:
- case Primitive::kPrimFloat:
- Move32(location, locations->Out());
- break;
-
- case Primitive::kPrimLong:
- case Primitive::kPrimDouble:
- Move64(location, locations->Out());
- break;
-
- default:
- LOG(FATAL) << "Unexpected type " << instruction->GetType();
- }
- }
-}
-
void CodeGeneratorARM::MoveConstant(Location location, int32_t value) {
DCHECK(location.IsRegister());
__ LoadImmediate(location.AsRegister<Register>(), value);
@@ -2163,6 +2079,8 @@
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to byte is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimShort:
@@ -2181,6 +2099,8 @@
case Primitive::kPrimShort:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to short is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2265,6 +2185,8 @@
case Primitive::kPrimChar:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to char is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2364,6 +2286,10 @@
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to byte is a result of code transformations.
+ __ sbfx(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>(), 0, 8);
+ break;
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimShort:
@@ -2381,6 +2307,10 @@
case Primitive::kPrimShort:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to short is a result of code transformations.
+ __ sbfx(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>(), 0, 16);
+ break;
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2482,6 +2412,10 @@
case Primitive::kPrimChar:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to char is a result of code transformations.
+ __ ubfx(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>(), 0, 16);
+ break;
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -3750,6 +3684,7 @@
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
switch (compare->InputAt(0)->GetType()) {
+ case Primitive::kPrimInt:
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
@@ -3779,6 +3714,13 @@
Primitive::Type type = compare->InputAt(0)->GetType();
Condition less_cond;
switch (type) {
+ case Primitive::kPrimInt: {
+ __ LoadImmediate(out, 0);
+ __ cmp(left.AsRegister<Register>(),
+ ShifterOperand(right.AsRegister<Register>())); // Signed compare.
+ less_cond = LT;
+ break;
+ }
case Primitive::kPrimLong: {
__ cmp(left.AsRegisterPairHigh<Register>(),
ShifterOperand(right.AsRegisterPairHigh<Register>())); // Signed compare.
@@ -3808,6 +3750,7 @@
LOG(FATAL) << "Unexpected compare type " << type;
UNREACHABLE();
}
+
__ b(&done, EQ);
__ b(&less, less_cond);
@@ -4924,14 +4867,6 @@
}
}
-void LocationsBuilderARM::VisitTemporary(HTemporary* temp) {
- temp->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorARM::VisitTemporary(HTemporary* temp ATTRIBUTE_UNUSED) {
- // Nothing to do, this is driven by the code generator.
-}
-
void LocationsBuilderARM::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
LOG(FATAL) << "Unreachable";
}
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 558c9cf..cfd7a3b 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -307,7 +307,6 @@
void GenerateFrameEntry() OVERRIDE;
void GenerateFrameExit() OVERRIDE;
void Bind(HBasicBlock* block) OVERRIDE;
- void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
void MoveConstant(Location destination, int32_t value) OVERRIDE;
void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE;
void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 4179fab..435ae5e 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1066,54 +1066,6 @@
__ Bind(GetLabelOf(block));
}
-void CodeGeneratorARM64::Move(HInstruction* instruction,
- Location location,
- HInstruction* move_for) {
- LocationSummary* locations = instruction->GetLocations();
- Primitive::Type type = instruction->GetType();
- DCHECK_NE(type, Primitive::kPrimVoid);
-
- if (instruction->IsCurrentMethod()) {
- MoveLocation(location,
- Location::DoubleStackSlot(kCurrentMethodStackOffset),
- Primitive::kPrimVoid);
- } else if (locations != nullptr && locations->Out().Equals(location)) {
- return;
- } else if (instruction->IsIntConstant()
- || instruction->IsLongConstant()
- || instruction->IsNullConstant()) {
- int64_t value = GetInt64ValueOf(instruction->AsConstant());
- if (location.IsRegister()) {
- Register dst = RegisterFrom(location, type);
- DCHECK(((instruction->IsIntConstant() || instruction->IsNullConstant()) && dst.Is32Bits()) ||
- (instruction->IsLongConstant() && dst.Is64Bits()));
- __ Mov(dst, value);
- } else {
- DCHECK(location.IsStackSlot() || location.IsDoubleStackSlot());
- UseScratchRegisterScope temps(GetVIXLAssembler());
- Register temp = (instruction->IsIntConstant() || instruction->IsNullConstant())
- ? temps.AcquireW()
- : temps.AcquireX();
- __ Mov(temp, value);
- __ Str(temp, StackOperandFrom(location));
- }
- } else if (instruction->IsTemporary()) {
- Location temp_location = GetTemporaryLocation(instruction->AsTemporary());
- MoveLocation(location, temp_location, type);
- } else if (instruction->IsLoadLocal()) {
- uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal());
- if (Primitive::Is64BitType(type)) {
- MoveLocation(location, Location::DoubleStackSlot(stack_slot), type);
- } else {
- MoveLocation(location, Location::StackSlot(stack_slot), type);
- }
-
- } else {
- DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary());
- MoveLocation(location, locations->Out(), type);
- }
-}
-
void CodeGeneratorARM64::MoveConstant(Location location, int32_t value) {
DCHECK(location.IsRegister());
__ Mov(RegisterFrom(location, Primitive::kPrimInt), value);
@@ -2408,6 +2360,7 @@
new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
Primitive::Type in_type = compare->InputAt(0)->GetType();
switch (in_type) {
+ case Primitive::kPrimInt:
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, ARM64EncodableConstantOrRegister(compare->InputAt(1), compare));
@@ -2436,14 +2389,14 @@
// 1 if: left > right
// -1 if: left < right
switch (in_type) {
+ case Primitive::kPrimInt:
case Primitive::kPrimLong: {
Register result = OutputRegister(compare);
Register left = InputRegisterAt(compare, 0);
Operand right = InputOperandAt(compare, 1);
-
__ Cmp(left, right);
- __ Cset(result, ne);
- __ Cneg(result, result, lt);
+ __ Cset(result, ne); // result == +1 if NE or 0 otherwise
+ __ Cneg(result, result, lt); // result == -1 if LT or unchanged otherwise
break;
}
case Primitive::kPrimFloat:
@@ -2975,30 +2928,128 @@
/* false_target */ nullptr);
}
+enum SelectVariant {
+ kCsel,
+ kCselFalseConst,
+ kCselTrueConst,
+ kFcsel,
+};
+
+static inline bool IsConditionOnFloatingPointValues(HInstruction* condition) {
+ return condition->IsCondition() &&
+ Primitive::IsFloatingPointType(condition->InputAt(0)->GetType());
+}
+
+static inline bool IsRecognizedCselConstant(HInstruction* constant) {
+ if (constant->IsConstant()) {
+ int64_t value = Int64FromConstant(constant->AsConstant());
+ if ((value == -1) || (value == 0) || (value == 1)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static inline SelectVariant GetSelectVariant(HSelect* select) {
+ if (Primitive::IsFloatingPointType(select->GetType())) {
+ return kFcsel;
+ } else if (IsRecognizedCselConstant(select->GetFalseValue())) {
+ return kCselFalseConst;
+ } else if (IsRecognizedCselConstant(select->GetTrueValue())) {
+ return kCselTrueConst;
+ } else {
+ return kCsel;
+ }
+}
+
+static inline bool HasSwappedInputs(SelectVariant variant) {
+ return variant == kCselTrueConst;
+}
+
+static inline Condition GetConditionForSelect(HCondition* condition, SelectVariant variant) {
+ IfCondition cond = HasSwappedInputs(variant) ? condition->GetOppositeCondition()
+ : condition->GetCondition();
+ return IsConditionOnFloatingPointValues(condition) ? ARM64FPCondition(cond, condition->IsGtBias())
+ : ARM64Condition(cond);
+}
+
void LocationsBuilderARM64::VisitSelect(HSelect* select) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
- if (Primitive::IsFloatingPointType(select->GetType())) {
- locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::RequiresFpuRegister());
- } else {
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ switch (GetSelectVariant(select)) {
+ case kCsel:
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
+ break;
+ case kCselFalseConst:
+ locations->SetInAt(0, Location::ConstantLocation(select->InputAt(0)->AsConstant()));
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
+ break;
+ case kCselTrueConst:
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::ConstantLocation(select->InputAt(1)->AsConstant()));
+ locations->SetOut(Location::RequiresRegister());
+ break;
+ case kFcsel:
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
}
if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
locations->SetInAt(2, Location::RequiresRegister());
}
- locations->SetOut(Location::SameAsFirstInput());
}
void InstructionCodeGeneratorARM64::VisitSelect(HSelect* select) {
- LocationSummary* locations = select->GetLocations();
- vixl::Label false_target;
- GenerateTestAndBranch(select,
- /* condition_input_index */ 2,
- /* true_target */ nullptr,
- &false_target);
- codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
- __ Bind(&false_target);
+ HInstruction* cond = select->GetCondition();
+ SelectVariant variant = GetSelectVariant(select);
+ Condition csel_cond;
+
+ if (IsBooleanValueOrMaterializedCondition(cond)) {
+ if (cond->IsCondition() && cond->GetNext() == select) {
+ // Condition codes set from previous instruction.
+ csel_cond = GetConditionForSelect(cond->AsCondition(), variant);
+ } else {
+ __ Cmp(InputRegisterAt(select, 2), 0);
+ csel_cond = HasSwappedInputs(variant) ? eq : ne;
+ }
+ } else if (IsConditionOnFloatingPointValues(cond)) {
+ Location rhs = cond->GetLocations()->InAt(1);
+ if (rhs.IsConstant()) {
+ DCHECK(IsFloatingPointZeroConstant(rhs.GetConstant()));
+ __ Fcmp(InputFPRegisterAt(cond, 0), 0.0);
+ } else {
+ __ Fcmp(InputFPRegisterAt(cond, 0), InputFPRegisterAt(cond, 1));
+ }
+ csel_cond = GetConditionForSelect(cond->AsCondition(), variant);
+ } else {
+ __ Cmp(InputRegisterAt(cond, 0), InputOperandAt(cond, 1));
+ csel_cond = GetConditionForSelect(cond->AsCondition(), variant);
+ }
+
+ switch (variant) {
+ case kCsel:
+ case kCselFalseConst:
+ __ Csel(OutputRegister(select),
+ InputRegisterAt(select, 1),
+ InputOperandAt(select, 0),
+ csel_cond);
+ break;
+ case kCselTrueConst:
+ __ Csel(OutputRegister(select),
+ InputRegisterAt(select, 0),
+ InputOperandAt(select, 1),
+ csel_cond);
+ break;
+ case kFcsel:
+ __ Fcsel(OutputFPRegister(select),
+ InputFPRegisterAt(select, 1),
+ InputFPRegisterAt(select, 0),
+ csel_cond);
+ break;
+ }
}
void LocationsBuilderARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
@@ -4444,14 +4495,6 @@
GenerateSuspendCheck(instruction, nullptr);
}
-void LocationsBuilderARM64::VisitTemporary(HTemporary* temp) {
- temp->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorARM64::VisitTemporary(HTemporary* temp ATTRIBUTE_UNUSED) {
- // Nothing to do, this is driven by the code generator.
-}
-
void LocationsBuilderARM64::VisitThrow(HThrow* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
@@ -4879,20 +4922,18 @@
static_assert(
sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
- temp2 = temps.AcquireW();
// /* HeapReference<Object> */ ref =
// *(obj + offset + index * sizeof(HeapReference<Object>))
- MemOperand source = HeapOperand(obj);
+ const size_t shift_amount = Primitive::ComponentSizeShift(type);
if (index.IsConstant()) {
- uint32_t computed_offset =
- offset + (Int64ConstantFrom(index) << Primitive::ComponentSizeShift(type));
- source = HeapOperand(obj, computed_offset);
+ uint32_t computed_offset = offset + (Int64ConstantFrom(index) << shift_amount);
+ Load(type, ref_reg, HeapOperand(obj, computed_offset));
} else {
+ temp2 = temps.AcquireW();
__ Add(temp2, obj, offset);
- source = HeapOperand(temp2, XRegisterFrom(index), LSL, Primitive::ComponentSizeShift(type));
+ Load(type, ref_reg, HeapOperand(temp2, XRegisterFrom(index), LSL, shift_amount));
+ temps.Release(temp2);
}
- Load(type, ref_reg, source);
- temps.Release(temp2);
} else {
// /* HeapReference<Object> */ ref = *(obj + offset)
MemOperand field = HeapOperand(obj, offset);
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index a9d1bbd..360488e 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -350,8 +350,6 @@
return CommonGetLabelOf<vixl::Label>(block_labels_, block);
}
- void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
-
size_t GetWordSize() const OVERRIDE {
return kArm64WordSize;
}
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 961fe62..3eda863 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -976,46 +976,6 @@
__ LoadConst32(dst, value);
}
-void CodeGeneratorMIPS::Move(HInstruction* instruction,
- Location location,
- HInstruction* move_for) {
- LocationSummary* locations = instruction->GetLocations();
- Primitive::Type type = instruction->GetType();
- DCHECK_NE(type, Primitive::kPrimVoid);
-
- if (instruction->IsCurrentMethod()) {
- Move32(location, Location::StackSlot(kCurrentMethodStackOffset));
- } else if (locations != nullptr && locations->Out().Equals(location)) {
- return;
- } else if (instruction->IsIntConstant()
- || instruction->IsLongConstant()
- || instruction->IsNullConstant()) {
- MoveConstant(location, instruction->AsConstant());
- } else if (instruction->IsTemporary()) {
- Location temp_location = GetTemporaryLocation(instruction->AsTemporary());
- if (temp_location.IsStackSlot()) {
- Move32(location, temp_location);
- } else {
- DCHECK(temp_location.IsDoubleStackSlot());
- Move64(location, temp_location);
- }
- } else if (instruction->IsLoadLocal()) {
- uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal());
- if (Primitive::Is64BitType(type)) {
- Move64(location, Location::DoubleStackSlot(stack_slot));
- } else {
- Move32(location, Location::StackSlot(stack_slot));
- }
- } else {
- DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary());
- if (Primitive::Is64BitType(type)) {
- Move64(location, locations->Out());
- } else {
- Move32(location, locations->Out());
- }
- }
-}
-
void CodeGeneratorMIPS::AddLocationAsTemp(Location location, LocationSummary* locations) {
if (location.IsRegister()) {
locations->AddTemp(location);
@@ -2123,6 +2083,7 @@
new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
switch (in_type) {
+ case Primitive::kPrimInt:
case Primitive::kPrimLong:
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
@@ -2153,6 +2114,14 @@
// 1 if: left > right
// -1 if: left < right
switch (in_type) {
+ case Primitive::kPrimInt: {
+ Register lhs = locations->InAt(0).AsRegister<Register>();
+ Register rhs = locations->InAt(1).AsRegister<Register>();
+ __ Slt(TMP, lhs, rhs);
+ __ Slt(res, rhs, lhs);
+ __ Subu(res, res, TMP);
+ break;
+ }
case Primitive::kPrimLong: {
MipsLabel done;
Register lhs_high = locations->InAt(0).AsRegisterPairHigh<Register>();
@@ -4786,14 +4755,6 @@
GenerateSuspendCheck(instruction, nullptr);
}
-void LocationsBuilderMIPS::VisitTemporary(HTemporary* temp) {
- temp->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorMIPS::VisitTemporary(HTemporary* temp ATTRIBUTE_UNUSED) {
- // Nothing to do, this is driven by the code generator.
-}
-
void LocationsBuilderMIPS::VisitThrow(HThrow* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index 2cde0ed..12964b0 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -268,7 +268,6 @@
void Bind(HBasicBlock* block) OVERRIDE;
- void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
void Move32(Location destination, Location source);
void Move64(Location destination, Location source);
void MoveConstant(Location location, HConstant* c);
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 3e1563c..119084e 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -869,65 +869,6 @@
}
}
-void CodeGeneratorMIPS64::Move(HInstruction* instruction,
- Location location,
- HInstruction* move_for) {
- LocationSummary* locations = instruction->GetLocations();
- Primitive::Type type = instruction->GetType();
- DCHECK_NE(type, Primitive::kPrimVoid);
-
- if (instruction->IsCurrentMethod()) {
- MoveLocation(location, Location::DoubleStackSlot(kCurrentMethodStackOffset), type);
- } else if (locations != nullptr && locations->Out().Equals(location)) {
- return;
- } else if (instruction->IsIntConstant()
- || instruction->IsLongConstant()
- || instruction->IsNullConstant()) {
- if (location.IsRegister()) {
- // Move to GPR from constant
- GpuRegister dst = location.AsRegister<GpuRegister>();
- if (instruction->IsNullConstant() || instruction->IsIntConstant()) {
- __ LoadConst32(dst, GetInt32ValueOf(instruction->AsConstant()));
- } else {
- __ LoadConst64(dst, instruction->AsLongConstant()->GetValue());
- }
- } else {
- DCHECK(location.IsStackSlot() || location.IsDoubleStackSlot());
- // Move to stack from constant
- GpuRegister gpr = ZERO;
- if (location.IsStackSlot()) {
- int32_t value = GetInt32ValueOf(instruction->AsConstant());
- if (value != 0) {
- gpr = TMP;
- __ LoadConst32(gpr, value);
- }
- __ StoreToOffset(kStoreWord, gpr, SP, location.GetStackIndex());
- } else {
- DCHECK(location.IsDoubleStackSlot());
- int64_t value = instruction->AsLongConstant()->GetValue();
- if (value != 0) {
- gpr = TMP;
- __ LoadConst64(gpr, value);
- }
- __ StoreToOffset(kStoreDoubleword, gpr, SP, location.GetStackIndex());
- }
- }
- } else if (instruction->IsTemporary()) {
- Location temp_location = GetTemporaryLocation(instruction->AsTemporary());
- MoveLocation(location, temp_location, type);
- } else if (instruction->IsLoadLocal()) {
- uint32_t stack_slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal());
- if (Primitive::Is64BitType(type)) {
- MoveLocation(location, Location::DoubleStackSlot(stack_slot), type);
- } else {
- MoveLocation(location, Location::StackSlot(stack_slot), type);
- }
- } else {
- DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary());
- MoveLocation(location, locations->Out(), type);
- }
-}
-
void CodeGeneratorMIPS64::MoveConstant(Location location, int32_t value) {
DCHECK(location.IsRegister());
__ LoadConst32(location.AsRegister<GpuRegister>(), value);
@@ -1763,6 +1704,7 @@
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare);
switch (in_type) {
+ case Primitive::kPrimInt:
case Primitive::kPrimLong:
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(compare->InputAt(1)));
@@ -1791,16 +1733,25 @@
// 1 if: left > right
// -1 if: left < right
switch (in_type) {
+ case Primitive::kPrimInt:
case Primitive::kPrimLong: {
GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>();
Location rhs_location = locations->InAt(1);
bool use_imm = rhs_location.IsConstant();
GpuRegister rhs = ZERO;
if (use_imm) {
- int64_t value = CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant()->AsConstant());
- if (value != 0) {
- rhs = AT;
- __ LoadConst64(rhs, value);
+ if (in_type == Primitive::kPrimInt) {
+ int32_t value = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant()->AsConstant());
+ if (value != 0) {
+ rhs = AT;
+ __ LoadConst32(rhs, value);
+ }
+ } else {
+ int64_t value = CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant()->AsConstant());
+ if (value != 0) {
+ rhs = AT;
+ __ LoadConst64(rhs, value);
+ }
}
} else {
rhs = rhs_location.AsRegister<GpuRegister>();
@@ -3936,14 +3887,6 @@
GenerateSuspendCheck(instruction, nullptr);
}
-void LocationsBuilderMIPS64::VisitTemporary(HTemporary* temp) {
- temp->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorMIPS64::VisitTemporary(HTemporary* temp ATTRIBUTE_UNUSED) {
- // Nothing to do, this is driven by the code generator.
-}
-
void LocationsBuilderMIPS64::VisitThrow(HThrow* instruction) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
@@ -4000,18 +3943,26 @@
__ Andi(dst, src, 0xFFFF);
break;
case Primitive::kPrimByte:
- // long is never converted into types narrower than int directly,
- // so SEB and SEH can be used without ever causing unpredictable results
- // on 64-bit inputs
- DCHECK(input_type != Primitive::kPrimLong);
- __ Seb(dst, src);
+ if (input_type == Primitive::kPrimLong) {
+ // Type conversion from long to types narrower than int is a result of code
+ // transformations. To avoid unpredictable results for SEB and SEH, we first
+ // need to sign-extend the low 32-bit value into bits 32 through 63.
+ __ Sll(dst, src, 0);
+ __ Seb(dst, dst);
+ } else {
+ __ Seb(dst, src);
+ }
break;
case Primitive::kPrimShort:
- // long is never converted into types narrower than int directly,
- // so SEB and SEH can be used without ever causing unpredictable results
- // on 64-bit inputs
- DCHECK(input_type != Primitive::kPrimLong);
- __ Seh(dst, src);
+ if (input_type == Primitive::kPrimLong) {
+ // Type conversion from long to types narrower than int is a result of code
+ // transformations. To avoid unpredictable results for SEB and SEH, we first
+ // need to sign-extend the low 32-bit value into bits 32 through 63.
+ __ Sll(dst, src, 0);
+ __ Seh(dst, dst);
+ } else {
+ __ Seh(dst, src);
+ }
break;
case Primitive::kPrimInt:
case Primitive::kPrimLong:
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index c836f83..1161253 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -268,8 +268,6 @@
void Bind(HBasicBlock* block) OVERRIDE;
- void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
-
size_t GetWordSize() const OVERRIDE { return kMips64DoublewordSize; }
size_t GetFloatingPointSpillSlotSize() const OVERRIDE { return kMips64DoublewordSize; }
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index da054ba..3c880c2 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1127,91 +1127,6 @@
}
}
-void CodeGeneratorX86::Move(HInstruction* instruction, Location location, HInstruction* move_for) {
- LocationSummary* locations = instruction->GetLocations();
- if (instruction->IsCurrentMethod()) {
- Move32(location, Location::StackSlot(kCurrentMethodStackOffset));
- } else if (locations != nullptr && locations->Out().Equals(location)) {
- return;
- } else if (locations != nullptr && locations->Out().IsConstant()) {
- HConstant* const_to_move = locations->Out().GetConstant();
- if (const_to_move->IsIntConstant() || const_to_move->IsNullConstant()) {
- Immediate imm(GetInt32ValueOf(const_to_move));
- if (location.IsRegister()) {
- __ movl(location.AsRegister<Register>(), imm);
- } else if (location.IsStackSlot()) {
- __ movl(Address(ESP, location.GetStackIndex()), imm);
- } else {
- DCHECK(location.IsConstant());
- DCHECK_EQ(location.GetConstant(), const_to_move);
- }
- } else if (const_to_move->IsLongConstant()) {
- int64_t value = const_to_move->AsLongConstant()->GetValue();
- if (location.IsRegisterPair()) {
- __ movl(location.AsRegisterPairLow<Register>(), Immediate(Low32Bits(value)));
- __ movl(location.AsRegisterPairHigh<Register>(), Immediate(High32Bits(value)));
- } else if (location.IsDoubleStackSlot()) {
- __ movl(Address(ESP, location.GetStackIndex()), Immediate(Low32Bits(value)));
- __ movl(Address(ESP, location.GetHighStackIndex(kX86WordSize)),
- Immediate(High32Bits(value)));
- } else {
- DCHECK(location.IsConstant());
- DCHECK_EQ(location.GetConstant(), instruction);
- }
- }
- } else if (instruction->IsTemporary()) {
- Location temp_location = GetTemporaryLocation(instruction->AsTemporary());
- if (temp_location.IsStackSlot()) {
- Move32(location, temp_location);
- } else {
- DCHECK(temp_location.IsDoubleStackSlot());
- Move64(location, temp_location);
- }
- } else if (instruction->IsLoadLocal()) {
- int slot = GetStackSlot(instruction->AsLoadLocal()->GetLocal());
- switch (instruction->GetType()) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimInt:
- case Primitive::kPrimNot:
- case Primitive::kPrimFloat:
- Move32(location, Location::StackSlot(slot));
- break;
-
- case Primitive::kPrimLong:
- case Primitive::kPrimDouble:
- Move64(location, Location::DoubleStackSlot(slot));
- break;
-
- default:
- LOG(FATAL) << "Unimplemented local type " << instruction->GetType();
- }
- } else {
- DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary());
- switch (instruction->GetType()) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimInt:
- case Primitive::kPrimNot:
- case Primitive::kPrimFloat:
- Move32(location, locations->Out());
- break;
-
- case Primitive::kPrimLong:
- case Primitive::kPrimDouble:
- Move64(location, locations->Out());
- break;
-
- default:
- LOG(FATAL) << "Unexpected type " << instruction->GetType();
- }
- }
-}
-
void CodeGeneratorX86::MoveConstant(Location location, int32_t value) {
DCHECK(location.IsRegister());
__ movl(location.AsRegister<Register>(), Immediate(value));
@@ -1350,11 +1265,7 @@
int32_t val_high = High32Bits(value);
int32_t val_low = Low32Bits(value);
- if (val_high == 0) {
- __ testl(left_high, left_high);
- } else {
- __ cmpl(left_high, Immediate(val_high));
- }
+ codegen_->Compare32BitValue(left_high, val_high);
if (if_cond == kCondNE) {
__ j(X86Condition(true_high_cond), true_label);
} else if (if_cond == kCondEQ) {
@@ -1364,12 +1275,8 @@
__ j(X86Condition(false_high_cond), false_label);
}
// Must be equal high, so compare the lows.
- if (val_low == 0) {
- __ testl(left_low, left_low);
- } else {
- __ cmpl(left_low, Immediate(val_low));
- }
- } else {
+ codegen_->Compare32BitValue(left_low, val_low);
+ } else if (right.IsRegisterPair()) {
Register right_high = right.AsRegisterPairHigh<Register>();
Register right_low = right.AsRegisterPairLow<Register>();
@@ -1384,11 +1291,58 @@
}
// Must be equal high, so compare the lows.
__ cmpl(left_low, right_low);
+ } else {
+ DCHECK(right.IsDoubleStackSlot());
+ __ cmpl(left_high, Address(ESP, right.GetHighStackIndex(kX86WordSize)));
+ if (if_cond == kCondNE) {
+ __ j(X86Condition(true_high_cond), true_label);
+ } else if (if_cond == kCondEQ) {
+ __ j(X86Condition(false_high_cond), false_label);
+ } else {
+ __ j(X86Condition(true_high_cond), true_label);
+ __ j(X86Condition(false_high_cond), false_label);
+ }
+ // Must be equal high, so compare the lows.
+ __ cmpl(left_low, Address(ESP, right.GetStackIndex()));
}
// The last comparison might be unsigned.
__ j(final_condition, true_label);
}
+void InstructionCodeGeneratorX86::GenerateFPCompare(Location lhs,
+ Location rhs,
+ HInstruction* insn,
+ bool is_double) {
+ HX86LoadFromConstantTable* const_area = insn->InputAt(1)->AsX86LoadFromConstantTable();
+ if (is_double) {
+ if (rhs.IsFpuRegister()) {
+ __ ucomisd(lhs.AsFpuRegister<XmmRegister>(), rhs.AsFpuRegister<XmmRegister>());
+ } else if (const_area != nullptr) {
+ DCHECK(const_area->IsEmittedAtUseSite());
+ __ ucomisd(lhs.AsFpuRegister<XmmRegister>(),
+ codegen_->LiteralDoubleAddress(
+ const_area->GetConstant()->AsDoubleConstant()->GetValue(),
+ const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+ } else {
+ DCHECK(rhs.IsDoubleStackSlot());
+ __ ucomisd(lhs.AsFpuRegister<XmmRegister>(), Address(ESP, rhs.GetStackIndex()));
+ }
+ } else {
+ if (rhs.IsFpuRegister()) {
+ __ ucomiss(lhs.AsFpuRegister<XmmRegister>(), rhs.AsFpuRegister<XmmRegister>());
+ } else if (const_area != nullptr) {
+ DCHECK(const_area->IsEmittedAtUseSite());
+ __ ucomiss(lhs.AsFpuRegister<XmmRegister>(),
+ codegen_->LiteralFloatAddress(
+ const_area->GetConstant()->AsFloatConstant()->GetValue(),
+ const_area->GetLocations()->InAt(0).AsRegister<Register>()));
+ } else {
+ DCHECK(rhs.IsStackSlot());
+ __ ucomiss(lhs.AsFpuRegister<XmmRegister>(), Address(ESP, rhs.GetStackIndex()));
+ }
+ }
+}
+
template<class LabelType>
void InstructionCodeGeneratorX86::GenerateCompareTestAndBranch(HCondition* condition,
LabelType* true_target_in,
@@ -1409,11 +1363,11 @@
GenerateLongComparesAndJumps(condition, true_target, false_target);
break;
case Primitive::kPrimFloat:
- __ ucomiss(left.AsFpuRegister<XmmRegister>(), right.AsFpuRegister<XmmRegister>());
+ GenerateFPCompare(left, right, condition, false);
GenerateFPJumps(condition, true_target, false_target);
break;
case Primitive::kPrimDouble:
- __ ucomisd(left.AsFpuRegister<XmmRegister>(), right.AsFpuRegister<XmmRegister>());
+ GenerateFPCompare(left, right, condition, true);
GenerateFPJumps(condition, true_target, false_target);
break;
default:
@@ -1513,11 +1467,7 @@
__ cmpl(lhs.AsRegister<Register>(), rhs.AsRegister<Register>());
} else if (rhs.IsConstant()) {
int32_t constant = CodeGenerator::GetInt32ValueOf(rhs.GetConstant());
- if (constant == 0) {
- __ testl(lhs.AsRegister<Register>(), lhs.AsRegister<Register>());
- } else {
- __ cmpl(lhs.AsRegister<Register>(), Immediate(constant));
- }
+ codegen_->Compare32BitValue(lhs.AsRegister<Register>(), constant);
} else {
__ cmpl(lhs.AsRegister<Register>(), Address(ESP, rhs.GetStackIndex()));
}
@@ -1656,7 +1606,7 @@
switch (cond->InputAt(0)->GetType()) {
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
+ locations->SetInAt(1, Location::Any());
if (!cond->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister());
}
@@ -1665,7 +1615,13 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::RequiresFpuRegister());
+ if (cond->InputAt(1)->IsX86LoadFromConstantTable()) {
+ DCHECK(cond->InputAt(1)->IsEmittedAtUseSite());
+ } else if (cond->InputAt(1)->IsConstant()) {
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ } else {
+ locations->SetInAt(1, Location::Any());
+ }
if (!cond->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister());
}
@@ -1704,11 +1660,7 @@
__ cmpl(lhs.AsRegister<Register>(), rhs.AsRegister<Register>());
} else if (rhs.IsConstant()) {
int32_t constant = CodeGenerator::GetInt32ValueOf(rhs.GetConstant());
- if (constant == 0) {
- __ testl(lhs.AsRegister<Register>(), lhs.AsRegister<Register>());
- } else {
- __ cmpl(lhs.AsRegister<Register>(), Immediate(constant));
- }
+ codegen_->Compare32BitValue(lhs.AsRegister<Register>(), constant);
} else {
__ cmpl(lhs.AsRegister<Register>(), Address(ESP, rhs.GetStackIndex()));
}
@@ -1719,11 +1671,11 @@
GenerateLongComparesAndJumps(cond, &true_label, &false_label);
break;
case Primitive::kPrimFloat:
- __ ucomiss(lhs.AsFpuRegister<XmmRegister>(), rhs.AsFpuRegister<XmmRegister>());
+ GenerateFPCompare(lhs, rhs, cond, false);
GenerateFPJumps(cond, &true_label, &false_label);
break;
case Primitive::kPrimDouble:
- __ ucomisd(lhs.AsFpuRegister<XmmRegister>(), rhs.AsFpuRegister<XmmRegister>());
+ GenerateFPCompare(lhs, rhs, cond, true);
GenerateFPJumps(cond, &true_label, &false_label);
break;
}
@@ -2159,6 +2111,32 @@
}
}
+void LocationsBuilderX86::VisitX86FPNeg(HX86FPNeg* neg) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall);
+ DCHECK(Primitive::IsFloatingPointType(neg->GetType()));
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+ locations->AddTemp(Location::RequiresFpuRegister());
+}
+
+void InstructionCodeGeneratorX86::VisitX86FPNeg(HX86FPNeg* neg) {
+ LocationSummary* locations = neg->GetLocations();
+ Location out = locations->Out();
+ DCHECK(locations->InAt(0).Equals(out));
+
+ Register constant_area = locations->InAt(1).AsRegister<Register>();
+ XmmRegister mask = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+ if (neg->GetType() == Primitive::kPrimFloat) {
+ __ movss(mask, codegen_->LiteralInt32Address(INT32_C(0x80000000), constant_area));
+ __ xorps(out.AsFpuRegister<XmmRegister>(), mask);
+ } else {
+ __ movsd(mask, codegen_->LiteralInt64Address(INT64_C(0x8000000000000000), constant_area));
+ __ xorpd(out.AsFpuRegister<XmmRegister>(), mask);
+ }
+}
+
void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) {
Primitive::Type result_type = conversion->GetResultType();
Primitive::Type input_type = conversion->GetInputType();
@@ -2180,6 +2158,18 @@
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
+ case Primitive::kPrimLong: {
+ // Type conversion from long to byte is a result of code transformations.
+ HInstruction* input = conversion->InputAt(0);
+ Location input_location = input->IsConstant()
+ ? Location::ConstantLocation(input->AsConstant())
+ : Location::RegisterPairLocation(EAX, EDX);
+ locations->SetInAt(0, input_location);
+ // Make the output overlap to please the register allocator. This greatly simplifies
+ // the validation of the linear scan implementation
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+ break;
+ }
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimShort:
@@ -2200,6 +2190,8 @@
case Primitive::kPrimShort:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to short is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2277,6 +2269,8 @@
case Primitive::kPrimChar:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to char is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2371,6 +2365,16 @@
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to byte is a result of code transformations.
+ if (in.IsRegisterPair()) {
+ __ movsxb(out.AsRegister<Register>(), in.AsRegisterPairLow<ByteRegister>());
+ } else {
+ DCHECK(in.GetConstant()->IsLongConstant());
+ int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
+ __ movl(out.AsRegister<Register>(), Immediate(static_cast<int8_t>(value)));
+ }
+ break;
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimShort:
@@ -2394,6 +2398,18 @@
case Primitive::kPrimShort:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to short is a result of code transformations.
+ if (in.IsRegisterPair()) {
+ __ movsxw(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>());
+ } else if (in.IsDoubleStackSlot()) {
+ __ movsxw(out.AsRegister<Register>(), Address(ESP, in.GetStackIndex()));
+ } else {
+ DCHECK(in.GetConstant()->IsLongConstant());
+ int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
+ __ movl(out.AsRegister<Register>(), Immediate(static_cast<int16_t>(value)));
+ }
+ break;
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2530,6 +2546,18 @@
case Primitive::kPrimChar:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to short is a result of code transformations.
+ if (in.IsRegisterPair()) {
+ __ movzxw(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>());
+ } else if (in.IsDoubleStackSlot()) {
+ __ movzxw(out.AsRegister<Register>(), Address(ESP, in.GetStackIndex()));
+ } else {
+ DCHECK(in.GetConstant()->IsLongConstant());
+ int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
+ __ movl(out.AsRegister<Register>(), Immediate(static_cast<uint16_t>(value)));
+ }
+ break;
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -4077,6 +4105,7 @@
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
switch (compare->InputAt(0)->GetType()) {
+ case Primitive::kPrimInt:
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::Any());
@@ -4086,7 +4115,13 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::RequiresFpuRegister());
+ if (compare->InputAt(1)->IsX86LoadFromConstantTable()) {
+ DCHECK(compare->InputAt(1)->IsEmittedAtUseSite());
+ } else if (compare->InputAt(1)->IsConstant()) {
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ } else {
+ locations->SetInAt(1, Location::Any());
+ }
locations->SetOut(Location::RequiresRegister());
break;
}
@@ -4102,7 +4137,21 @@
Location right = locations->InAt(1);
NearLabel less, greater, done;
+ Condition less_cond = kLess;
+
switch (compare->InputAt(0)->GetType()) {
+ case Primitive::kPrimInt: {
+ Register left_reg = left.AsRegister<Register>();
+ if (right.IsConstant()) {
+ int32_t value = right.GetConstant()->AsIntConstant()->GetValue();
+ codegen_->Compare32BitValue(left_reg, value);
+ } else if (right.IsStackSlot()) {
+ __ cmpl(left_reg, Address(ESP, right.GetStackIndex()));
+ } else {
+ __ cmpl(left_reg, right.AsRegister<Register>());
+ }
+ break;
+ }
case Primitive::kPrimLong: {
Register left_low = left.AsRegisterPairLow<Register>();
Register left_high = left.AsRegisterPairHigh<Register>();
@@ -4124,11 +4173,7 @@
__ cmpl(left_high, Address(ESP, right.GetHighStackIndex(kX86WordSize)));
} else {
DCHECK(right_is_const) << right;
- if (val_high == 0) {
- __ testl(left_high, left_high);
- } else {
- __ cmpl(left_high, Immediate(val_high));
- }
+ codegen_->Compare32BitValue(left_high, val_high);
}
__ j(kLess, &less); // Signed compare.
__ j(kGreater, &greater); // Signed compare.
@@ -4138,30 +4183,30 @@
__ cmpl(left_low, Address(ESP, right.GetStackIndex()));
} else {
DCHECK(right_is_const) << right;
- if (val_low == 0) {
- __ testl(left_low, left_low);
- } else {
- __ cmpl(left_low, Immediate(val_low));
- }
+ codegen_->Compare32BitValue(left_low, val_low);
}
+ less_cond = kBelow; // for CF (unsigned).
break;
}
case Primitive::kPrimFloat: {
- __ ucomiss(left.AsFpuRegister<XmmRegister>(), right.AsFpuRegister<XmmRegister>());
+ GenerateFPCompare(left, right, compare, false);
__ j(kUnordered, compare->IsGtBias() ? &greater : &less);
+ less_cond = kBelow; // for CF (floats).
break;
}
case Primitive::kPrimDouble: {
- __ ucomisd(left.AsFpuRegister<XmmRegister>(), right.AsFpuRegister<XmmRegister>());
+ GenerateFPCompare(left, right, compare, true);
__ j(kUnordered, compare->IsGtBias() ? &greater : &less);
+ less_cond = kBelow; // for CF (floats).
break;
}
default:
LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType();
}
+
__ movl(out, Immediate(0));
__ j(kEqual, &done);
- __ j(kBelow, &less); // kBelow is for CF (unsigned & floats).
+ __ j(less_cond, &less);
__ Bind(&greater);
__ movl(out, Immediate(1));
@@ -5446,14 +5491,6 @@
}
}
-void LocationsBuilderX86::VisitTemporary(HTemporary* temp) {
- temp->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorX86::VisitTemporary(HTemporary* temp ATTRIBUTE_UNUSED) {
- // Nothing to do, this is driven by the code generator.
-}
-
void LocationsBuilderX86::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
LOG(FATAL) << "Unreachable";
}
@@ -7121,6 +7158,22 @@
return Address(reg, kDummy32BitOffset, fixup);
}
+void CodeGeneratorX86::Load32BitValue(Register dest, int32_t value) {
+ if (value == 0) {
+ __ xorl(dest, dest);
+ } else {
+ __ movl(dest, Immediate(value));
+ }
+}
+
+void CodeGeneratorX86::Compare32BitValue(Register dest, int32_t value) {
+ if (value == 0) {
+ __ testl(dest, dest);
+ } else {
+ __ cmpl(dest, Immediate(value));
+ }
+}
+
Address CodeGeneratorX86::LiteralCaseTable(HX86PackedSwitch* switch_instr,
Register reg,
Register value) {
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 0aef478..2fb6d60 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -296,6 +296,8 @@
HBasicBlock* switch_block,
HBasicBlock* default_block);
+ void GenerateFPCompare(Location lhs, Location rhs, HInstruction* insn, bool is_double);
+
X86Assembler* const assembler_;
CodeGeneratorX86* const codegen_;
@@ -315,7 +317,6 @@
void GenerateFrameEntry() OVERRIDE;
void GenerateFrameExit() OVERRIDE;
void Bind(HBasicBlock* block) OVERRIDE;
- void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
void MoveConstant(Location destination, int32_t value) OVERRIDE;
void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE;
void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE;
@@ -450,6 +451,12 @@
Address LiteralInt32Address(int32_t v, Register reg);
Address LiteralInt64Address(int64_t v, Register reg);
+ // Load a 32-bit value into a register in the most efficient manner.
+ void Load32BitValue(Register dest, int32_t value);
+
+ // Compare a register with a 32-bit value in the most efficient manner.
+ void Compare32BitValue(Register dest, int32_t value);
+
Address LiteralCaseTable(HX86PackedSwitch* switch_instr, Register reg, Register value);
void Finalize(CodeAllocator* allocator) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 6795488..a53a6be 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1126,30 +1126,43 @@
return;
}
if (destination.IsRegister()) {
+ CpuRegister dest = destination.AsRegister<CpuRegister>();
if (source.IsRegister()) {
- __ movq(destination.AsRegister<CpuRegister>(), source.AsRegister<CpuRegister>());
+ __ movq(dest, source.AsRegister<CpuRegister>());
} else if (source.IsFpuRegister()) {
- __ movd(destination.AsRegister<CpuRegister>(), source.AsFpuRegister<XmmRegister>());
+ __ movd(dest, source.AsFpuRegister<XmmRegister>());
} else if (source.IsStackSlot()) {
- __ movl(destination.AsRegister<CpuRegister>(),
- Address(CpuRegister(RSP), source.GetStackIndex()));
+ __ movl(dest, Address(CpuRegister(RSP), source.GetStackIndex()));
+ } else if (source.IsConstant()) {
+ HConstant* constant = source.GetConstant();
+ if (constant->IsLongConstant()) {
+ Load64BitValue(dest, constant->AsLongConstant()->GetValue());
+ } else {
+ Load32BitValue(dest, GetInt32ValueOf(constant));
+ }
} else {
DCHECK(source.IsDoubleStackSlot());
- __ movq(destination.AsRegister<CpuRegister>(),
- Address(CpuRegister(RSP), source.GetStackIndex()));
+ __ movq(dest, Address(CpuRegister(RSP), source.GetStackIndex()));
}
} else if (destination.IsFpuRegister()) {
+ XmmRegister dest = destination.AsFpuRegister<XmmRegister>();
if (source.IsRegister()) {
- __ movd(destination.AsFpuRegister<XmmRegister>(), source.AsRegister<CpuRegister>());
+ __ movd(dest, source.AsRegister<CpuRegister>());
} else if (source.IsFpuRegister()) {
- __ movaps(destination.AsFpuRegister<XmmRegister>(), source.AsFpuRegister<XmmRegister>());
+ __ movaps(dest, source.AsFpuRegister<XmmRegister>());
+ } else if (source.IsConstant()) {
+ HConstant* constant = source.GetConstant();
+ int64_t value = CodeGenerator::GetInt64ValueOf(constant);
+ if (constant->IsFloatConstant()) {
+ Load32BitValue(dest, static_cast<int32_t>(value));
+ } else {
+ Load64BitValue(dest, value);
+ }
} else if (source.IsStackSlot()) {
- __ movss(destination.AsFpuRegister<XmmRegister>(),
- Address(CpuRegister(RSP), source.GetStackIndex()));
+ __ movss(dest, Address(CpuRegister(RSP), source.GetStackIndex()));
} else {
DCHECK(source.IsDoubleStackSlot());
- __ movsd(destination.AsFpuRegister<XmmRegister>(),
- Address(CpuRegister(RSP), source.GetStackIndex()));
+ __ movsd(dest, Address(CpuRegister(RSP), source.GetStackIndex()));
}
} else if (destination.IsStackSlot()) {
if (source.IsRegister()) {
@@ -1193,82 +1206,6 @@
}
}
-void CodeGeneratorX86_64::Move(HInstruction* instruction,
- Location location,
- HInstruction* move_for) {
- LocationSummary* locations = instruction->GetLocations();
- if (instruction->IsCurrentMethod()) {
- Move(location, Location::DoubleStackSlot(kCurrentMethodStackOffset));
- } else if (locations != nullptr && locations->Out().Equals(location)) {
- return;
- } else if (locations != nullptr && locations->Out().IsConstant()) {
- HConstant* const_to_move = locations->Out().GetConstant();
- if (const_to_move->IsIntConstant() || const_to_move->IsNullConstant()) {
- Immediate imm(GetInt32ValueOf(const_to_move));
- if (location.IsRegister()) {
- __ movl(location.AsRegister<CpuRegister>(), imm);
- } else if (location.IsStackSlot()) {
- __ movl(Address(CpuRegister(RSP), location.GetStackIndex()), imm);
- } else {
- DCHECK(location.IsConstant());
- DCHECK_EQ(location.GetConstant(), const_to_move);
- }
- } else if (const_to_move->IsLongConstant()) {
- int64_t value = const_to_move->AsLongConstant()->GetValue();
- if (location.IsRegister()) {
- Load64BitValue(location.AsRegister<CpuRegister>(), value);
- } else if (location.IsDoubleStackSlot()) {
- Store64BitValueToStack(location, value);
- } else {
- DCHECK(location.IsConstant());
- DCHECK_EQ(location.GetConstant(), const_to_move);
- }
- }
- } else if (instruction->IsLoadLocal()) {
- switch (instruction->GetType()) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimInt:
- case Primitive::kPrimNot:
- case Primitive::kPrimFloat:
- Move(location, Location::StackSlot(GetStackSlot(instruction->AsLoadLocal()->GetLocal())));
- break;
-
- case Primitive::kPrimLong:
- case Primitive::kPrimDouble:
- Move(location,
- Location::DoubleStackSlot(GetStackSlot(instruction->AsLoadLocal()->GetLocal())));
- break;
-
- default:
- LOG(FATAL) << "Unexpected local type " << instruction->GetType();
- }
- } else if (instruction->IsTemporary()) {
- Location temp_location = GetTemporaryLocation(instruction->AsTemporary());
- Move(location, temp_location);
- } else {
- DCHECK((instruction->GetNext() == move_for) || instruction->GetNext()->IsTemporary());
- switch (instruction->GetType()) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimInt:
- case Primitive::kPrimNot:
- case Primitive::kPrimLong:
- case Primitive::kPrimFloat:
- case Primitive::kPrimDouble:
- Move(location, locations->Out());
- break;
-
- default:
- LOG(FATAL) << "Unexpected type " << instruction->GetType();
- }
- }
-}
-
void CodeGeneratorX86_64::MoveConstant(Location location, int32_t value) {
DCHECK(location.IsRegister());
Load64BitValue(location.AsRegister<CpuRegister>(), static_cast<int64_t>(value));
@@ -1345,42 +1282,44 @@
__ j(X86_64FPCondition(cond->GetCondition()), true_label);
}
-template<class LabelType>
-void InstructionCodeGeneratorX86_64::GenerateCompareTestAndBranch(HCondition* condition,
- LabelType* true_target_in,
- LabelType* false_target_in) {
- // Generated branching requires both targets to be explicit. If either of the
- // targets is nullptr (fallthrough) use and bind `fallthrough_target` instead.
- LabelType fallthrough_target;
- LabelType* true_target = true_target_in == nullptr ? &fallthrough_target : true_target_in;
- LabelType* false_target = false_target_in == nullptr ? &fallthrough_target : false_target_in;
-
+void InstructionCodeGeneratorX86_64::GenerateCompareTest(HCondition* condition) {
LocationSummary* locations = condition->GetLocations();
+
Location left = locations->InAt(0);
Location right = locations->InAt(1);
-
Primitive::Type type = condition->InputAt(0)->GetType();
switch (type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ CpuRegister left_reg = left.AsRegister<CpuRegister>();
+ if (right.IsConstant()) {
+ int32_t value = CodeGenerator::GetInt32ValueOf(right.GetConstant());
+ if (value == 0) {
+ __ testl(left_reg, left_reg);
+ } else {
+ __ cmpl(left_reg, Immediate(value));
+ }
+ } else if (right.IsStackSlot()) {
+ __ cmpl(left_reg, Address(CpuRegister(RSP), right.GetStackIndex()));
+ } else {
+ __ cmpl(left_reg, right.AsRegister<CpuRegister>());
+ }
+ break;
+ }
case Primitive::kPrimLong: {
CpuRegister left_reg = left.AsRegister<CpuRegister>();
if (right.IsConstant()) {
int64_t value = right.GetConstant()->AsLongConstant()->GetValue();
- if (IsInt<32>(value)) {
- if (value == 0) {
- __ testq(left_reg, left_reg);
- } else {
- __ cmpq(left_reg, Immediate(static_cast<int32_t>(value)));
- }
- } else {
- // Value won't fit in a 32-bit integer.
- __ cmpq(left_reg, codegen_->LiteralInt64Address(value));
- }
+ codegen_->Compare64BitValue(left_reg, value);
} else if (right.IsDoubleStackSlot()) {
__ cmpq(left_reg, Address(CpuRegister(RSP), right.GetStackIndex()));
} else {
__ cmpq(left_reg, right.AsRegister<CpuRegister>());
}
- __ j(X86_64IntegerCondition(condition->GetCondition()), true_target);
break;
}
case Primitive::kPrimFloat: {
@@ -1395,7 +1334,6 @@
__ ucomiss(left.AsFpuRegister<XmmRegister>(),
Address(CpuRegister(RSP), right.GetStackIndex()));
}
- GenerateFPJumps(condition, true_target, false_target);
break;
}
case Primitive::kPrimDouble: {
@@ -1410,6 +1348,38 @@
__ ucomisd(left.AsFpuRegister<XmmRegister>(),
Address(CpuRegister(RSP), right.GetStackIndex()));
}
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected condition type " << type;
+ }
+}
+
+template<class LabelType>
+void InstructionCodeGeneratorX86_64::GenerateCompareTestAndBranch(HCondition* condition,
+ LabelType* true_target_in,
+ LabelType* false_target_in) {
+ // Generated branching requires both targets to be explicit. If either of the
+ // targets is nullptr (fallthrough) use and bind `fallthrough_target` instead.
+ LabelType fallthrough_target;
+ LabelType* true_target = true_target_in == nullptr ? &fallthrough_target : true_target_in;
+ LabelType* false_target = false_target_in == nullptr ? &fallthrough_target : false_target_in;
+
+ // Generate the comparison to set the CC.
+ GenerateCompareTest(condition);
+
+ // Now generate the correct jump(s).
+ Primitive::Type type = condition->InputAt(0)->GetType();
+ switch (type) {
+ case Primitive::kPrimLong: {
+ __ j(X86_64IntegerCondition(condition->GetCondition()), true_target);
+ break;
+ }
+ case Primitive::kPrimFloat: {
+ GenerateFPJumps(condition, true_target, false_target);
+ break;
+ }
+ case Primitive::kPrimDouble: {
GenerateFPJumps(condition, true_target, false_target);
break;
}
@@ -1508,11 +1478,7 @@
__ cmpl(lhs.AsRegister<CpuRegister>(), rhs.AsRegister<CpuRegister>());
} else if (rhs.IsConstant()) {
int32_t constant = CodeGenerator::GetInt32ValueOf(rhs.GetConstant());
- if (constant == 0) {
- __ testl(lhs.AsRegister<CpuRegister>(), lhs.AsRegister<CpuRegister>());
- } else {
- __ cmpl(lhs.AsRegister<CpuRegister>(), Immediate(constant));
- }
+ codegen_->Compare32BitValue(lhs.AsRegister<CpuRegister>(), constant);
} else {
__ cmpl(lhs.AsRegister<CpuRegister>(),
Address(CpuRegister(RSP), rhs.GetStackIndex()));
@@ -1564,14 +1530,39 @@
/* false_target */ nullptr);
}
+static bool SelectCanUseCMOV(HSelect* select) {
+ // There are no conditional move instructions for XMMs.
+ if (Primitive::IsFloatingPointType(select->GetType())) {
+ return false;
+ }
+
+ // A FP condition doesn't generate the single CC that we need.
+ HInstruction* condition = select->GetCondition();
+ if (condition->IsCondition() &&
+ Primitive::IsFloatingPointType(condition->InputAt(0)->GetType())) {
+ return false;
+ }
+
+ // We can generate a CMOV for this Select.
+ return true;
+}
+
void LocationsBuilderX86_64::VisitSelect(HSelect* select) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
if (Primitive::IsFloatingPointType(select->GetType())) {
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::Any());
} else {
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ if (SelectCanUseCMOV(select)) {
+ if (select->InputAt(1)->IsConstant()) {
+ locations->SetInAt(1, Location::RequiresRegister());
+ } else {
+ locations->SetInAt(1, Location::Any());
+ }
+ } else {
+ locations->SetInAt(1, Location::Any());
+ }
}
if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
locations->SetInAt(2, Location::RequiresRegister());
@@ -1581,13 +1572,59 @@
void InstructionCodeGeneratorX86_64::VisitSelect(HSelect* select) {
LocationSummary* locations = select->GetLocations();
- NearLabel false_target;
- GenerateTestAndBranch<NearLabel>(select,
- /* condition_input_index */ 2,
- /* true_target */ nullptr,
- &false_target);
- codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
- __ Bind(&false_target);
+ if (SelectCanUseCMOV(select)) {
+ // If both the condition and the source types are integer, we can generate
+ // a CMOV to implement Select.
+ CpuRegister value_false = locations->InAt(0).AsRegister<CpuRegister>();
+ Location value_true_loc = locations->InAt(1);
+ DCHECK(locations->InAt(0).Equals(locations->Out()));
+
+ HInstruction* select_condition = select->GetCondition();
+ Condition cond = kNotEqual;
+
+ // Figure out how to test the 'condition'.
+ if (select_condition->IsCondition()) {
+ HCondition* condition = select_condition->AsCondition();
+ if (!condition->IsEmittedAtUseSite()) {
+ // This was a previously materialized condition.
+ // Can we use the existing condition code?
+ if (AreEflagsSetFrom(condition, select)) {
+ // Materialization was the previous instruction. Condition codes are right.
+ cond = X86_64IntegerCondition(condition->GetCondition());
+ } else {
+ // No, we have to recreate the condition code.
+ CpuRegister cond_reg = locations->InAt(2).AsRegister<CpuRegister>();
+ __ testl(cond_reg, cond_reg);
+ }
+ } else {
+ GenerateCompareTest(condition);
+ cond = X86_64IntegerCondition(condition->GetCondition());
+ }
+ } else {
+ // Must be a boolean condition, which needs to be compared to 0.
+ CpuRegister cond_reg = locations->InAt(2).AsRegister<CpuRegister>();
+ __ testl(cond_reg, cond_reg);
+ }
+
+ // If the condition is true, overwrite the output, which already contains false.
+ // Generate the correct sized CMOV.
+ bool is_64_bit = Primitive::Is64BitType(select->GetType());
+ if (value_true_loc.IsRegister()) {
+ __ cmov(cond, value_false, value_true_loc.AsRegister<CpuRegister>(), is_64_bit);
+ } else {
+ __ cmov(cond,
+ value_false,
+ Address(CpuRegister(RSP), value_true_loc.GetStackIndex()), is_64_bit);
+ }
+ } else {
+ NearLabel false_target;
+ GenerateTestAndBranch<NearLabel>(select,
+ /* condition_input_index */ 2,
+ /* true_target */ nullptr,
+ &false_target);
+ codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
+ __ Bind(&false_target);
+ }
}
void LocationsBuilderX86_64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
@@ -1691,11 +1728,7 @@
__ cmpl(lhs.AsRegister<CpuRegister>(), rhs.AsRegister<CpuRegister>());
} else if (rhs.IsConstant()) {
int32_t constant = CodeGenerator::GetInt32ValueOf(rhs.GetConstant());
- if (constant == 0) {
- __ testl(lhs.AsRegister<CpuRegister>(), lhs.AsRegister<CpuRegister>());
- } else {
- __ cmpl(lhs.AsRegister<CpuRegister>(), Immediate(constant));
- }
+ codegen_->Compare32BitValue(lhs.AsRegister<CpuRegister>(), constant);
} else {
__ cmpl(lhs.AsRegister<CpuRegister>(), Address(CpuRegister(RSP), rhs.GetStackIndex()));
}
@@ -1709,16 +1742,7 @@
__ cmpq(lhs.AsRegister<CpuRegister>(), rhs.AsRegister<CpuRegister>());
} else if (rhs.IsConstant()) {
int64_t value = rhs.GetConstant()->AsLongConstant()->GetValue();
- if (IsInt<32>(value)) {
- if (value == 0) {
- __ testq(lhs.AsRegister<CpuRegister>(), lhs.AsRegister<CpuRegister>());
- } else {
- __ cmpq(lhs.AsRegister<CpuRegister>(), Immediate(static_cast<int32_t>(value)));
- }
- } else {
- // Value won't fit in an int.
- __ cmpq(lhs.AsRegister<CpuRegister>(), codegen_->LiteralInt64Address(value));
- }
+ codegen_->Compare64BitValue(lhs.AsRegister<CpuRegister>(), value);
} else {
__ cmpq(lhs.AsRegister<CpuRegister>(), Address(CpuRegister(RSP), rhs.GetStackIndex()));
}
@@ -1850,6 +1874,7 @@
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
switch (compare->InputAt(0)->GetType()) {
+ case Primitive::kPrimInt:
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::Any());
@@ -1876,21 +1901,26 @@
NearLabel less, greater, done;
Primitive::Type type = compare->InputAt(0)->GetType();
+ Condition less_cond = kLess;
+
switch (type) {
+ case Primitive::kPrimInt: {
+ CpuRegister left_reg = left.AsRegister<CpuRegister>();
+ if (right.IsConstant()) {
+ int32_t value = right.GetConstant()->AsIntConstant()->GetValue();
+ codegen_->Compare32BitValue(left_reg, value);
+ } else if (right.IsStackSlot()) {
+ __ cmpl(left_reg, Address(CpuRegister(RSP), right.GetStackIndex()));
+ } else {
+ __ cmpl(left_reg, right.AsRegister<CpuRegister>());
+ }
+ break;
+ }
case Primitive::kPrimLong: {
CpuRegister left_reg = left.AsRegister<CpuRegister>();
if (right.IsConstant()) {
int64_t value = right.GetConstant()->AsLongConstant()->GetValue();
- if (IsInt<32>(value)) {
- if (value == 0) {
- __ testq(left_reg, left_reg);
- } else {
- __ cmpq(left_reg, Immediate(static_cast<int32_t>(value)));
- }
- } else {
- // Value won't fit in an int.
- __ cmpq(left_reg, codegen_->LiteralInt64Address(value));
- }
+ codegen_->Compare64BitValue(left_reg, value);
} else if (right.IsDoubleStackSlot()) {
__ cmpq(left_reg, Address(CpuRegister(RSP), right.GetStackIndex()));
} else {
@@ -1909,6 +1939,7 @@
__ ucomiss(left_reg, right.AsFpuRegister<XmmRegister>());
}
__ j(kUnordered, compare->IsGtBias() ? &greater : &less);
+ less_cond = kBelow; // ucomis{s,d} sets CF
break;
}
case Primitive::kPrimDouble: {
@@ -1922,14 +1953,16 @@
__ ucomisd(left_reg, right.AsFpuRegister<XmmRegister>());
}
__ j(kUnordered, compare->IsGtBias() ? &greater : &less);
+ less_cond = kBelow; // ucomis{s,d} sets CF
break;
}
default:
LOG(FATAL) << "Unexpected compare type " << type;
}
+
__ movl(out, Immediate(0));
__ j(kEqual, &done);
- __ j(type == Primitive::kPrimLong ? kLess : kBelow, &less); // ucomis{s,d} sets CF (kBelow)
+ __ j(less_cond, &less);
__ Bind(&greater);
__ movl(out, Immediate(1));
@@ -2339,6 +2372,8 @@
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to byte is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimShort:
@@ -2357,6 +2392,8 @@
case Primitive::kPrimShort:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to short is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2434,6 +2471,8 @@
case Primitive::kPrimChar:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to char is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2528,6 +2567,8 @@
switch (result_type) {
case Primitive::kPrimByte:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to byte is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimShort:
@@ -2536,13 +2577,12 @@
// Processing a Dex `int-to-byte' instruction.
if (in.IsRegister()) {
__ movsxb(out.AsRegister<CpuRegister>(), in.AsRegister<CpuRegister>());
- } else if (in.IsStackSlot()) {
+ } else if (in.IsStackSlot() || in.IsDoubleStackSlot()) {
__ movsxb(out.AsRegister<CpuRegister>(),
Address(CpuRegister(RSP), in.GetStackIndex()));
} else {
- DCHECK(in.GetConstant()->IsIntConstant());
__ movl(out.AsRegister<CpuRegister>(),
- Immediate(static_cast<int8_t>(in.GetConstant()->AsIntConstant()->GetValue())));
+ Immediate(static_cast<int8_t>(Int64FromConstant(in.GetConstant()))));
}
break;
@@ -2554,6 +2594,8 @@
case Primitive::kPrimShort:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to short is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2562,13 +2604,12 @@
// Processing a Dex `int-to-short' instruction.
if (in.IsRegister()) {
__ movsxw(out.AsRegister<CpuRegister>(), in.AsRegister<CpuRegister>());
- } else if (in.IsStackSlot()) {
+ } else if (in.IsStackSlot() || in.IsDoubleStackSlot()) {
__ movsxw(out.AsRegister<CpuRegister>(),
Address(CpuRegister(RSP), in.GetStackIndex()));
} else {
- DCHECK(in.GetConstant()->IsIntConstant());
__ movl(out.AsRegister<CpuRegister>(),
- Immediate(static_cast<int16_t>(in.GetConstant()->AsIntConstant()->GetValue())));
+ Immediate(static_cast<int16_t>(Int64FromConstant(in.GetConstant()))));
}
break;
@@ -2711,6 +2752,8 @@
case Primitive::kPrimChar:
switch (input_type) {
+ case Primitive::kPrimLong:
+ // Type conversion from long to char is a result of code transformations.
case Primitive::kPrimBoolean:
// Boolean input is a result of code transformations.
case Primitive::kPrimByte:
@@ -2719,14 +2762,12 @@
// Processing a Dex `int-to-char' instruction.
if (in.IsRegister()) {
__ movzxw(out.AsRegister<CpuRegister>(), in.AsRegister<CpuRegister>());
- } else if (in.IsStackSlot()) {
+ } else if (in.IsStackSlot() || in.IsDoubleStackSlot()) {
__ movzxw(out.AsRegister<CpuRegister>(),
Address(CpuRegister(RSP), in.GetStackIndex()));
} else {
- DCHECK(in.GetConstant()->IsIntConstant());
__ movl(out.AsRegister<CpuRegister>(),
- Immediate(static_cast<uint16_t>(
- in.GetConstant()->AsIntConstant()->GetValue())));
+ Immediate(static_cast<uint16_t>(Int64FromConstant(in.GetConstant()))));
}
break;
@@ -2750,11 +2791,7 @@
} else if (in.IsConstant()) {
int32_t v = in.GetConstant()->AsIntConstant()->GetValue();
XmmRegister dest = out.AsFpuRegister<XmmRegister>();
- if (v == 0) {
- __ xorps(dest, dest);
- } else {
- __ movss(dest, codegen_->LiteralFloatAddress(static_cast<float>(v)));
- }
+ codegen_->Load32BitValue(dest, static_cast<float>(v));
} else {
__ cvtsi2ss(out.AsFpuRegister<XmmRegister>(),
Address(CpuRegister(RSP), in.GetStackIndex()), false);
@@ -2768,11 +2805,7 @@
} else if (in.IsConstant()) {
int64_t v = in.GetConstant()->AsLongConstant()->GetValue();
XmmRegister dest = out.AsFpuRegister<XmmRegister>();
- if (v == 0) {
- __ xorps(dest, dest);
- } else {
- __ movss(dest, codegen_->LiteralFloatAddress(static_cast<float>(v)));
- }
+ codegen_->Load64BitValue(dest, static_cast<double>(v));
} else {
__ cvtsi2ss(out.AsFpuRegister<XmmRegister>(),
Address(CpuRegister(RSP), in.GetStackIndex()), true);
@@ -2786,11 +2819,7 @@
} else if (in.IsConstant()) {
double v = in.GetConstant()->AsDoubleConstant()->GetValue();
XmmRegister dest = out.AsFpuRegister<XmmRegister>();
- if (bit_cast<int64_t, double>(v) == 0) {
- __ xorps(dest, dest);
- } else {
- __ movss(dest, codegen_->LiteralFloatAddress(static_cast<float>(v)));
- }
+ codegen_->Load32BitValue(dest, static_cast<float>(v));
} else {
__ cvtsd2ss(out.AsFpuRegister<XmmRegister>(),
Address(CpuRegister(RSP), in.GetStackIndex()));
@@ -2817,11 +2846,7 @@
} else if (in.IsConstant()) {
int32_t v = in.GetConstant()->AsIntConstant()->GetValue();
XmmRegister dest = out.AsFpuRegister<XmmRegister>();
- if (v == 0) {
- __ xorpd(dest, dest);
- } else {
- __ movsd(dest, codegen_->LiteralDoubleAddress(static_cast<double>(v)));
- }
+ codegen_->Load64BitValue(dest, static_cast<double>(v));
} else {
__ cvtsi2sd(out.AsFpuRegister<XmmRegister>(),
Address(CpuRegister(RSP), in.GetStackIndex()), false);
@@ -2835,11 +2860,7 @@
} else if (in.IsConstant()) {
int64_t v = in.GetConstant()->AsLongConstant()->GetValue();
XmmRegister dest = out.AsFpuRegister<XmmRegister>();
- if (v == 0) {
- __ xorpd(dest, dest);
- } else {
- __ movsd(dest, codegen_->LiteralDoubleAddress(static_cast<double>(v)));
- }
+ codegen_->Load64BitValue(dest, static_cast<double>(v));
} else {
__ cvtsi2sd(out.AsFpuRegister<XmmRegister>(),
Address(CpuRegister(RSP), in.GetStackIndex()), true);
@@ -2853,11 +2874,7 @@
} else if (in.IsConstant()) {
float v = in.GetConstant()->AsFloatConstant()->GetValue();
XmmRegister dest = out.AsFpuRegister<XmmRegister>();
- if (bit_cast<int32_t, float>(v) == 0) {
- __ xorpd(dest, dest);
- } else {
- __ movsd(dest, codegen_->LiteralDoubleAddress(static_cast<double>(v)));
- }
+ codegen_->Load64BitValue(dest, static_cast<double>(v));
} else {
__ cvtss2sd(out.AsFpuRegister<XmmRegister>(),
Address(CpuRegister(RSP), in.GetStackIndex()));
@@ -5066,14 +5083,6 @@
}
}
-void LocationsBuilderX86_64::VisitTemporary(HTemporary* temp) {
- temp->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorX86_64::VisitTemporary(HTemporary* temp ATTRIBUTE_UNUSED) {
- // Nothing to do, this is driven by the code generator.
-}
-
void LocationsBuilderX86_64::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
LOG(FATAL) << "Unimplemented";
}
@@ -5196,18 +5205,12 @@
}
} else if (constant->IsFloatConstant()) {
float fp_value = constant->AsFloatConstant()->GetValue();
- int32_t value = bit_cast<int32_t, float>(fp_value);
if (destination.IsFpuRegister()) {
XmmRegister dest = destination.AsFpuRegister<XmmRegister>();
- if (value == 0) {
- // easy FP 0.0.
- __ xorps(dest, dest);
- } else {
- __ movss(dest, codegen_->LiteralFloatAddress(fp_value));
- }
+ codegen_->Load32BitValue(dest, fp_value);
} else {
DCHECK(destination.IsStackSlot()) << destination;
- Immediate imm(value);
+ Immediate imm(bit_cast<int32_t, float>(fp_value));
__ movl(Address(CpuRegister(RSP), destination.GetStackIndex()), imm);
}
} else {
@@ -5216,11 +5219,7 @@
int64_t value = bit_cast<int64_t, double>(fp_value);
if (destination.IsFpuRegister()) {
XmmRegister dest = destination.AsFpuRegister<XmmRegister>();
- if (value == 0) {
- __ xorpd(dest, dest);
- } else {
- __ movsd(dest, codegen_->LiteralDoubleAddress(fp_value));
- }
+ codegen_->Load64BitValue(dest, fp_value);
} else {
DCHECK(destination.IsDoubleStackSlot()) << destination;
codegen_->Store64BitValueToStack(destination, value);
@@ -5774,19 +5773,20 @@
is_type_check_slow_path_fatal);
codegen_->AddSlowPath(type_check_slow_path);
- NearLabel done;
- // Avoid null check if we know obj is not null.
- if (instruction->MustDoNullCheck()) {
- __ testl(obj, obj);
- __ j(kEqual, &done);
- }
-
- // /* HeapReference<Class> */ temp = obj->klass_
- GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
-
switch (type_check_kind) {
case TypeCheckKind::kExactCheck:
case TypeCheckKind::kArrayCheck: {
+ NearLabel done;
+ // Avoid null check if we know obj is not null.
+ if (instruction->MustDoNullCheck()) {
+ __ testl(obj, obj);
+ __ j(kEqual, &done);
+ }
+
+ // /* HeapReference<Class> */ temp = obj->klass_
+ GenerateReferenceLoadTwoRegisters(
+ instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
+
if (cls.IsRegister()) {
__ cmpl(temp, cls.AsRegister<CpuRegister>());
} else {
@@ -5796,10 +5796,22 @@
// Jump to slow path for throwing the exception or doing a
// more involved array check.
__ j(kNotEqual, type_check_slow_path->GetEntryLabel());
+ __ Bind(&done);
break;
}
case TypeCheckKind::kAbstractClassCheck: {
+ NearLabel done;
+ // Avoid null check if we know obj is not null.
+ if (instruction->MustDoNullCheck()) {
+ __ testl(obj, obj);
+ __ j(kEqual, &done);
+ }
+
+ // /* HeapReference<Class> */ temp = obj->klass_
+ GenerateReferenceLoadTwoRegisters(
+ instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
+
// If the class is abstract, we eagerly fetch the super class of the
// object to avoid doing a comparison we know will fail.
NearLabel loop, compare_classes;
@@ -5830,10 +5842,22 @@
__ cmpl(temp, Address(CpuRegister(RSP), cls.GetStackIndex()));
}
__ j(kNotEqual, &loop);
+ __ Bind(&done);
break;
}
case TypeCheckKind::kClassHierarchyCheck: {
+ NearLabel done;
+ // Avoid null check if we know obj is not null.
+ if (instruction->MustDoNullCheck()) {
+ __ testl(obj, obj);
+ __ j(kEqual, &done);
+ }
+
+ // /* HeapReference<Class> */ temp = obj->klass_
+ GenerateReferenceLoadTwoRegisters(
+ instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
+
// Walk over the class hierarchy to find a match.
NearLabel loop;
__ Bind(&loop);
@@ -5861,10 +5885,26 @@
GenerateReferenceLoadTwoRegisters(
instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
__ jmp(type_check_slow_path->GetEntryLabel());
+ __ Bind(&done);
break;
}
case TypeCheckKind::kArrayObjectCheck: {
+ // We cannot use a NearLabel here, as its range might be too
+ // short in some cases when read barriers are enabled. This has
+ // been observed for instance when the code emitted for this
+ // case uses high x86-64 registers (R8-R15).
+ Label done;
+ // Avoid null check if we know obj is not null.
+ if (instruction->MustDoNullCheck()) {
+ __ testl(obj, obj);
+ __ j(kEqual, &done);
+ }
+
+ // /* HeapReference<Class> */ temp = obj->klass_
+ GenerateReferenceLoadTwoRegisters(
+ instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
+
// Do an exact check.
NearLabel check_non_primitive_component_type;
if (cls.IsRegister()) {
@@ -5903,11 +5943,23 @@
GenerateReferenceLoadTwoRegisters(
instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
__ jmp(type_check_slow_path->GetEntryLabel());
+ __ Bind(&done);
break;
}
case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck:
+ NearLabel done;
+ // Avoid null check if we know obj is not null.
+ if (instruction->MustDoNullCheck()) {
+ __ testl(obj, obj);
+ __ j(kEqual, &done);
+ }
+
+ // /* HeapReference<Class> */ temp = obj->klass_
+ GenerateReferenceLoadTwoRegisters(
+ instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
+
// We always go into the type check slow path for the unresolved
// and interface check cases.
//
@@ -5926,9 +5978,9 @@
// call to the runtime not using a type checking slow path).
// This should also be beneficial for the other cases above.
__ jmp(type_check_slow_path->GetEntryLabel());
+ __ Bind(&done);
break;
}
- __ Bind(&done);
__ Bind(type_check_slow_path->GetExitLabel());
}
@@ -6467,6 +6519,51 @@
}
}
+void CodeGeneratorX86_64::Load32BitValue(XmmRegister dest, int32_t value) {
+ if (value == 0) {
+ __ xorps(dest, dest);
+ } else {
+ __ movss(dest, LiteralInt32Address(value));
+ }
+}
+
+void CodeGeneratorX86_64::Load64BitValue(XmmRegister dest, int64_t value) {
+ if (value == 0) {
+ __ xorpd(dest, dest);
+ } else {
+ __ movsd(dest, LiteralInt64Address(value));
+ }
+}
+
+void CodeGeneratorX86_64::Load32BitValue(XmmRegister dest, float value) {
+ Load32BitValue(dest, bit_cast<int32_t, float>(value));
+}
+
+void CodeGeneratorX86_64::Load64BitValue(XmmRegister dest, double value) {
+ Load64BitValue(dest, bit_cast<int64_t, double>(value));
+}
+
+void CodeGeneratorX86_64::Compare32BitValue(CpuRegister dest, int32_t value) {
+ if (value == 0) {
+ __ testl(dest, dest);
+ } else {
+ __ cmpl(dest, Immediate(value));
+ }
+}
+
+void CodeGeneratorX86_64::Compare64BitValue(CpuRegister dest, int64_t value) {
+ if (IsInt<32>(value)) {
+ if (value == 0) {
+ __ testq(dest, dest);
+ } else {
+ __ cmpq(dest, Immediate(static_cast<int32_t>(value)));
+ }
+ } else {
+ // Value won't fit in an int.
+ __ cmpq(dest, LiteralInt64Address(value));
+ }
+}
+
void CodeGeneratorX86_64::Store64BitValueToStack(Location dest, int64_t value) {
DCHECK(dest.IsDoubleStackSlot());
if (IsInt<32>(value)) {
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 318087e..97f6f84 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -264,6 +264,7 @@
void GenerateExplicitNullCheck(HNullCheck* instruction);
void PushOntoFPStack(Location source, uint32_t temp_offset,
uint32_t stack_adjustment, bool is_float);
+ void GenerateCompareTest(HCondition* condition);
template<class LabelType>
void GenerateTestAndBranch(HInstruction* instruction,
size_t condition_input_index,
@@ -298,7 +299,6 @@
void GenerateFrameEntry() OVERRIDE;
void GenerateFrameExit() OVERRIDE;
void Bind(HBasicBlock* block) OVERRIDE;
- void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
void MoveConstant(Location destination, int32_t value) OVERRIDE;
void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE;
void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE;
@@ -478,9 +478,17 @@
Address LiteralInt32Address(int32_t v);
Address LiteralInt64Address(int64_t v);
- // Load a 32/64 bit value into a register in the most efficient manner.
+ // Load a 32/64-bit value into a register in the most efficient manner.
void Load32BitValue(CpuRegister dest, int32_t value);
void Load64BitValue(CpuRegister dest, int64_t value);
+ void Load32BitValue(XmmRegister dest, int32_t value);
+ void Load64BitValue(XmmRegister dest, int64_t value);
+ void Load32BitValue(XmmRegister dest, float value);
+ void Load64BitValue(XmmRegister dest, double value);
+
+ // Compare a register with a 32/64-bit value in the most efficient manner.
+ void Compare32BitValue(CpuRegister dest, int32_t value);
+ void Compare64BitValue(CpuRegister dest, int64_t value);
Address LiteralCaseTable(HPackedSwitch* switch_instr);
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index 322a577..6be79fa 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -206,10 +206,13 @@
std::function<void(HGraph*)> hook_before_codegen,
bool has_result,
Expected expected) {
- ASSERT_TRUE(graph->IsInSsaForm());
-
- SSAChecker graph_checker(graph);
+ GraphChecker graph_checker(graph);
graph_checker.Run();
+ if (!graph_checker.IsValid()) {
+ for (auto error : graph_checker.GetErrors()) {
+ std::cout << error << std::endl;
+ }
+ }
ASSERT_TRUE(graph_checker.IsValid());
SsaLivenessAnalysis liveness(graph, codegen);
@@ -292,14 +295,9 @@
for (InstructionSet target_isa : GetTargetISAs()) {
ArenaPool pool;
ArenaAllocator arena(&pool);
- HGraph* graph = CreateGraph(&arena);
- HGraphBuilder builder(graph);
- const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- bool graph_built = builder.BuildGraph(*item);
- ASSERT_TRUE(graph_built);
+ HGraph* graph = CreateCFG(&arena, data);
// Remove suspend checks, they cannot be executed in this context.
RemoveSuspendChecks(graph);
- TransformToSsa(graph);
RunCode(target_isa, graph, [](HGraph*) {}, has_result, expected);
}
}
@@ -310,14 +308,9 @@
for (InstructionSet target_isa : GetTargetISAs()) {
ArenaPool pool;
ArenaAllocator arena(&pool);
- HGraph* graph = CreateGraph(&arena);
- HGraphBuilder builder(graph, Primitive::kPrimLong);
- const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- bool graph_built = builder.BuildGraph(*item);
- ASSERT_TRUE(graph_built);
+ HGraph* graph = CreateCFG(&arena, data, Primitive::kPrimLong);
// Remove suspend checks, they cannot be executed in this context.
RemoveSuspendChecks(graph);
- TransformToSsa(graph);
RunCode(target_isa, graph, [](HGraph*) {}, has_result, expected);
}
}
@@ -640,6 +633,7 @@
ArenaAllocator allocator(&pool);
HGraph* graph = CreateGraph(&allocator);
+
HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
graph->AddBlock(entry);
graph->SetEntryBlock(entry);
@@ -672,7 +666,7 @@
else_block->AddInstruction(new (&allocator) HReturn(constant1));
ASSERT_FALSE(equal->IsEmittedAtUseSite());
- TransformToSsa(graph);
+ graph->BuildDominatorTree();
PrepareForRegisterAllocation(graph).Run();
ASSERT_TRUE(equal->IsEmittedAtUseSite());
@@ -723,7 +717,7 @@
HReturn ret(&cmp_lt);
code_block->AddInstruction(&ret);
- TransformToSsa(graph);
+ graph->BuildDominatorTree();
auto hook_before_codegen = [](HGraph* graph_in) {
HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors()[0];
HParallelMove* move = new (graph_in->GetArena()) HParallelMove(graph_in->GetArena());
@@ -777,9 +771,9 @@
HIntConstant* cst_rhs = graph->GetIntConstant(rhs[i]);
HLessThan cmp_lt(cst_lhs, cst_rhs);
if_block->AddInstruction(&cmp_lt);
- // We insert a temporary to separate the HIf from the HLessThan and force
- // the materialization of the condition.
- HTemporary force_materialization(0);
+ // We insert a dummy instruction to separate the HIf from the HLessThan
+ // and force the materialization of the condition.
+ HMemoryBarrier force_materialization(MemBarrierKind::kAnyAny, 0);
if_block->AddInstruction(&force_materialization);
HIf if_lt(&cmp_lt);
if_block->AddInstruction(&if_lt);
@@ -791,7 +785,7 @@
HReturn ret_ge(cst_ge);
if_false_block->AddInstruction(&ret_ge);
- TransformToSsa(graph);
+ graph->BuildDominatorTree();
auto hook_before_codegen = [](HGraph* graph_in) {
HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors()[0];
HParallelMove* move = new (graph_in->GetArena()) HParallelMove(graph_in->GetArena());
@@ -907,7 +901,7 @@
block->AddInstruction(comparison);
block->AddInstruction(new (&allocator) HReturn(comparison));
- TransformToSsa(graph);
+ graph->BuildDominatorTree();
RunCode(target_isa, graph, [](HGraph*) {}, true, expected_result);
}
diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc
index a8f65bf..9c69f8c 100644
--- a/compiler/optimizing/constant_folding_test.cc
+++ b/compiler/optimizing/constant_folding_test.cc
@@ -56,7 +56,6 @@
const std::string& expected_after_dce,
std::function<void(HGraph*)> check_after_cf) {
ASSERT_NE(graph_, nullptr);
- TransformToSsa(graph_);
StringPrettyPrinter printer_before(graph_);
printer_before.VisitInsertionOrder();
@@ -67,9 +66,9 @@
X86InstructionSetFeatures::FromCppDefines());
x86::CodeGeneratorX86 codegenX86(graph_, *features_x86.get(), CompilerOptions());
HConstantFolding(graph_).Run();
- SSAChecker ssa_checker_cf(graph_);
- ssa_checker_cf.Run();
- ASSERT_TRUE(ssa_checker_cf.IsValid());
+ GraphChecker graph_checker_cf(graph_);
+ graph_checker_cf.Run();
+ ASSERT_TRUE(graph_checker_cf.IsValid());
StringPrettyPrinter printer_after_cf(graph_);
printer_after_cf.VisitInsertionOrder();
@@ -79,9 +78,9 @@
check_after_cf(graph_);
HDeadCodeElimination(graph_).Run();
- SSAChecker ssa_checker_dce(graph_);
- ssa_checker_dce.Run();
- ASSERT_TRUE(ssa_checker_dce.IsValid());
+ GraphChecker graph_checker_dce(graph_);
+ graph_checker_dce.Run();
+ ASSERT_TRUE(graph_checker_dce.IsValid());
StringPrettyPrinter printer_after_dce(graph_);
printer_after_dce.VisitInsertionOrder();
@@ -775,76 +774,87 @@
HInstruction* zero = graph_->GetIntConstant(0);
HInstruction* last;
block->AddInstruction(last = new (&allocator_) HAbove(zero, parameter));
- block->AddInstruction(new (&allocator_) HDeoptimize(last, 0));
+ block->AddInstruction(new (&allocator_) HSelect(last, parameter, parameter, 0));
block->AddInstruction(last = new (&allocator_) HAbove(parameter, zero));
- block->AddInstruction(new (&allocator_) HDeoptimize(last, 0));
+ block->AddInstruction(new (&allocator_) HSelect(last, parameter, parameter, 0));
block->AddInstruction(last = new (&allocator_) HAboveOrEqual(zero, parameter));
- block->AddInstruction(new (&allocator_) HDeoptimize(last, 0));
+ block->AddInstruction(new (&allocator_) HSelect(last, parameter, parameter, 0));
block->AddInstruction(last = new (&allocator_) HAboveOrEqual(parameter, zero));
- block->AddInstruction(new (&allocator_) HDeoptimize(last, 0));
+ block->AddInstruction(new (&allocator_) HSelect(last, parameter, parameter, 0));
block->AddInstruction(last = new (&allocator_) HBelow(zero, parameter));
- block->AddInstruction(new (&allocator_) HDeoptimize(last, 0));
+ block->AddInstruction(new (&allocator_) HSelect(last, parameter, parameter, 0));
block->AddInstruction(last = new (&allocator_) HBelow(parameter, zero));
- block->AddInstruction(new (&allocator_) HDeoptimize(last, 0));
+ block->AddInstruction(new (&allocator_) HSelect(last, parameter, parameter, 0));
block->AddInstruction(last = new (&allocator_) HBelowOrEqual(zero, parameter));
- block->AddInstruction(new (&allocator_) HDeoptimize(last, 0));
+ block->AddInstruction(new (&allocator_) HSelect(last, parameter, parameter, 0));
block->AddInstruction(last = new (&allocator_) HBelowOrEqual(parameter, zero));
- block->AddInstruction(new (&allocator_) HDeoptimize(last, 0));
+ block->AddInstruction(new (&allocator_) HSelect(last, parameter, parameter, 0));
entry_block->AddInstruction(new (&allocator_) HGoto());
block->AddInstruction(new (&allocator_) HReturn(zero));
exit_block->AddInstruction(new (&allocator_) HExit());
+ graph_->BuildDominatorTree();
+
const std::string expected_before =
"BasicBlock 0, succ: 1\n"
- " 0: ParameterValue [16, 14, 12, 10, 8, 6, 4, 2]\n"
+ " 0: ParameterValue [17, 17, 16, 15, 15, 14, 13, 13, 12, 11, 11, 10, 9, 9, "
+ "8, 7, 7, 6, 5, 5, 4, 3, 3, 2]\n"
" 1: IntConstant [19, 16, 14, 12, 10, 8, 6, 4, 2]\n"
" 18: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
" 2: Above(1, 0) [3]\n"
- " 3: Deoptimize(2)\n"
+ " 3: Select(0, 0, 2)\n"
" 4: Above(0, 1) [5]\n"
- " 5: Deoptimize(4)\n"
+ " 5: Select(0, 0, 4)\n"
" 6: AboveOrEqual(1, 0) [7]\n"
- " 7: Deoptimize(6)\n"
+ " 7: Select(0, 0, 6)\n"
" 8: AboveOrEqual(0, 1) [9]\n"
- " 9: Deoptimize(8)\n"
+ " 9: Select(0, 0, 8)\n"
" 10: Below(1, 0) [11]\n"
- " 11: Deoptimize(10)\n"
+ " 11: Select(0, 0, 10)\n"
" 12: Below(0, 1) [13]\n"
- " 13: Deoptimize(12)\n"
+ " 13: Select(0, 0, 12)\n"
" 14: BelowOrEqual(1, 0) [15]\n"
- " 15: Deoptimize(14)\n"
+ " 15: Select(0, 0, 14)\n"
" 16: BelowOrEqual(0, 1) [17]\n"
- " 17: Deoptimize(16)\n"
+ " 17: Select(0, 0, 16)\n"
" 19: Return(1)\n"
"BasicBlock 2, pred: 1\n"
" 20: Exit\n";
const std::string expected_after_cf =
"BasicBlock 0, succ: 1\n"
- " 0: ParameterValue [16, 10, 6, 4]\n"
+ " 0: ParameterValue [17, 17, 16, 15, 15, 13, 13, 11, 11, 10, 9, 9, 7, 7, 6, 5, 5, 4, 3, 3]\n"
" 1: IntConstant [13, 3, 19, 16, 10, 6, 4]\n"
" 21: IntConstant [15, 9]\n"
" 18: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
- " 3: Deoptimize(1)\n"
+ " 3: Select(0, 0, 1)\n"
" 4: Above(0, 1) [5]\n"
- " 5: Deoptimize(4)\n"
+ " 5: Select(0, 0, 4)\n"
" 6: AboveOrEqual(1, 0) [7]\n"
- " 7: Deoptimize(6)\n"
- " 9: Deoptimize(21)\n"
+ " 7: Select(0, 0, 6)\n"
+ " 9: Select(0, 0, 21)\n"
" 10: Below(1, 0) [11]\n"
- " 11: Deoptimize(10)\n"
- " 13: Deoptimize(1)\n"
- " 15: Deoptimize(21)\n"
+ " 11: Select(0, 0, 10)\n"
+ " 13: Select(0, 0, 1)\n"
+ " 15: Select(0, 0, 21)\n"
" 16: BelowOrEqual(0, 1) [17]\n"
- " 17: Deoptimize(16)\n"
+ " 17: Select(0, 0, 16)\n"
" 19: Return(1)\n"
"BasicBlock 2, pred: 1\n"
" 20: Exit\n";
- const std::string expected_after_dce = expected_after_cf;
+ const std::string expected_after_dce =
+ "BasicBlock 0, succ: 1\n"
+ " 0: ParameterValue\n"
+ " 1: IntConstant [19]\n"
+ " 18: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 19: Return(1)\n"
+ "BasicBlock 2, pred: 1\n"
+ " 20: Exit\n";
auto check_after_cf = [](HGraph* graph) {
CHECK(graph != nullptr);
diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc
index f0f98ef..930795b 100644
--- a/compiler/optimizing/dead_code_elimination_test.cc
+++ b/compiler/optimizing/dead_code_elimination_test.cc
@@ -36,8 +36,6 @@
HGraph* graph = CreateCFG(&allocator, data);
ASSERT_NE(graph, nullptr);
- TransformToSsa(graph);
-
StringPrettyPrinter printer_before(graph);
printer_before.VisitInsertionOrder();
std::string actual_before = printer_before.str();
@@ -47,9 +45,9 @@
X86InstructionSetFeatures::FromCppDefines());
x86::CodeGeneratorX86 codegenX86(graph, *features_x86.get(), CompilerOptions());
HDeadCodeElimination(graph).Run();
- SSAChecker ssa_checker(graph);
- ssa_checker.Run();
- ASSERT_TRUE(ssa_checker.IsValid());
+ GraphChecker graph_checker(graph);
+ graph_checker.Run();
+ ASSERT_TRUE(graph_checker.IsValid());
StringPrettyPrinter printer_after(graph);
printer_after.VisitInsertionOrder();
diff --git a/compiler/optimizing/dominator_test.cc b/compiler/optimizing/dominator_test.cc
index feb8b20..50c677a 100644
--- a/compiler/optimizing/dominator_test.cc
+++ b/compiler/optimizing/dominator_test.cc
@@ -24,15 +24,12 @@
namespace art {
+class OptimizerTest : public CommonCompilerTest {};
+
static void TestCode(const uint16_t* data, const uint32_t* blocks, size_t blocks_length) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
- HGraph* graph = CreateGraph(&allocator);
- HGraphBuilder builder(graph);
- const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- bool graph_built = builder.BuildGraph(*item);
- ASSERT_TRUE(graph_built);
- graph->BuildDominatorTree();
+ HGraph* graph = CreateCFG(&allocator, data);
ASSERT_EQ(graph->GetBlocks().size(), blocks_length);
for (size_t i = 0, e = blocks_length; i < e; ++i) {
if (blocks[i] == kInvalidBlockId) {
@@ -50,7 +47,7 @@
}
}
-TEST(OptimizerTest, ReturnVoid) {
+TEST_F(OptimizerTest, ReturnVoid) {
const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
Instruction::RETURN_VOID); // Block number 1
@@ -63,7 +60,7 @@
TestCode(data, dominators, sizeof(dominators) / sizeof(int));
}
-TEST(OptimizerTest, CFG1) {
+TEST_F(OptimizerTest, CFG1) {
const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
Instruction::GOTO | 0x100, // Block number 1
Instruction::RETURN_VOID); // Block number 2
@@ -78,7 +75,7 @@
TestCode(data, dominators, sizeof(dominators) / sizeof(int));
}
-TEST(OptimizerTest, CFG2) {
+TEST_F(OptimizerTest, CFG2) {
const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
Instruction::GOTO | 0x100, // Block number 1
Instruction::GOTO | 0x100, // Block number 2
@@ -95,7 +92,7 @@
TestCode(data, dominators, sizeof(dominators) / sizeof(int));
}
-TEST(OptimizerTest, CFG3) {
+TEST_F(OptimizerTest, CFG3) {
const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM(
Instruction::GOTO | 0x200, // Block number 1
Instruction::RETURN_VOID, // Block number 2
@@ -126,7 +123,7 @@
TestCode(data3, dominators, sizeof(dominators) / sizeof(int));
}
-TEST(OptimizerTest, CFG4) {
+TEST_F(OptimizerTest, CFG4) {
const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM(
Instruction::NOP,
Instruction::GOTO | 0xFF00);
@@ -146,7 +143,7 @@
TestCode(data2, dominators, sizeof(dominators) / sizeof(int));
}
-TEST(OptimizerTest, CFG5) {
+TEST_F(OptimizerTest, CFG5) {
const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
Instruction::RETURN_VOID, // Block number 1
Instruction::GOTO | 0x100, // Dead block
@@ -163,7 +160,7 @@
TestCode(data, dominators, sizeof(dominators) / sizeof(int));
}
-TEST(OptimizerTest, CFG6) {
+TEST_F(OptimizerTest, CFG6) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::IF_EQ, 3,
@@ -182,7 +179,7 @@
TestCode(data, dominators, sizeof(dominators) / sizeof(int));
}
-TEST(OptimizerTest, CFG7) {
+TEST_F(OptimizerTest, CFG7) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::IF_EQ, 3, // Block number 1
@@ -202,7 +199,7 @@
TestCode(data, dominators, sizeof(dominators) / sizeof(int));
}
-TEST(OptimizerTest, CFG8) {
+TEST_F(OptimizerTest, CFG8) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::IF_EQ, 3, // Block number 1
@@ -223,7 +220,7 @@
TestCode(data, dominators, sizeof(dominators) / sizeof(int));
}
-TEST(OptimizerTest, CFG9) {
+TEST_F(OptimizerTest, CFG9) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::IF_EQ, 3, // Block number 1
@@ -244,7 +241,7 @@
TestCode(data, dominators, sizeof(dominators) / sizeof(int));
}
-TEST(OptimizerTest, CFG10) {
+TEST_F(OptimizerTest, CFG10) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::IF_EQ, 6, // Block number 1
diff --git a/compiler/optimizing/find_loops_test.cc b/compiler/optimizing/find_loops_test.cc
index 4770fa2..04789d9 100644
--- a/compiler/optimizing/find_loops_test.cc
+++ b/compiler/optimizing/find_loops_test.cc
@@ -27,16 +27,9 @@
namespace art {
-static HGraph* TestCode(const uint16_t* data, ArenaAllocator* allocator) {
- HGraph* graph = CreateGraph(allocator);
- HGraphBuilder builder(graph);
- const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- builder.BuildGraph(*item);
- graph->BuildDominatorTree();
- return graph;
-}
+class FindLoopsTest : public CommonCompilerTest {};
-TEST(FindLoopsTest, CFG1) {
+TEST_F(FindLoopsTest, CFG1) {
// Constant is not used.
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
@@ -44,26 +37,26 @@
ArenaPool arena;
ArenaAllocator allocator(&arena);
- HGraph* graph = TestCode(data, &allocator);
+ HGraph* graph = CreateCFG(&allocator, data);
for (HBasicBlock* block : graph->GetBlocks()) {
ASSERT_EQ(block->GetLoopInformation(), nullptr);
}
}
-TEST(FindLoopsTest, CFG2) {
+TEST_F(FindLoopsTest, CFG2) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::RETURN);
ArenaPool arena;
ArenaAllocator allocator(&arena);
- HGraph* graph = TestCode(data, &allocator);
+ HGraph* graph = CreateCFG(&allocator, data);
for (HBasicBlock* block : graph->GetBlocks()) {
ASSERT_EQ(block->GetLoopInformation(), nullptr);
}
}
-TEST(FindLoopsTest, CFG3) {
+TEST_F(FindLoopsTest, CFG3) {
const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 3 << 12 | 0,
Instruction::CONST_4 | 4 << 12 | 1 << 8,
@@ -73,13 +66,13 @@
ArenaPool arena;
ArenaAllocator allocator(&arena);
- HGraph* graph = TestCode(data, &allocator);
+ HGraph* graph = CreateCFG(&allocator, data);
for (HBasicBlock* block : graph->GetBlocks()) {
ASSERT_EQ(block->GetLoopInformation(), nullptr);
}
}
-TEST(FindLoopsTest, CFG4) {
+TEST_F(FindLoopsTest, CFG4) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::IF_EQ, 4,
@@ -90,13 +83,13 @@
ArenaPool arena;
ArenaAllocator allocator(&arena);
- HGraph* graph = TestCode(data, &allocator);
+ HGraph* graph = CreateCFG(&allocator, data);
for (HBasicBlock* block : graph->GetBlocks()) {
ASSERT_EQ(block->GetLoopInformation(), nullptr);
}
}
-TEST(FindLoopsTest, CFG5) {
+TEST_F(FindLoopsTest, CFG5) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::IF_EQ, 3,
@@ -105,7 +98,7 @@
ArenaPool arena;
ArenaAllocator allocator(&arena);
- HGraph* graph = TestCode(data, &allocator);
+ HGraph* graph = CreateCFG(&allocator, data);
for (HBasicBlock* block : graph->GetBlocks()) {
ASSERT_EQ(block->GetLoopInformation(), nullptr);
}
@@ -137,7 +130,7 @@
}
}
-TEST(FindLoopsTest, Loop1) {
+TEST_F(FindLoopsTest, Loop1) {
// Simple loop with one preheader and one back edge.
// var a = 0;
// while (a == a) {
@@ -151,7 +144,7 @@
ArenaPool arena;
ArenaAllocator allocator(&arena);
- HGraph* graph = TestCode(data, &allocator);
+ HGraph* graph = CreateCFG(&allocator, data);
TestBlock(graph, 0, false, kInvalidBlockId); // entry block
TestBlock(graph, 1, false, kInvalidBlockId); // pre header
@@ -162,7 +155,7 @@
TestBlock(graph, 5, false, kInvalidBlockId); // exit block
}
-TEST(FindLoopsTest, Loop2) {
+TEST_F(FindLoopsTest, Loop2) {
// Make sure we support a preheader of a loop not being the first predecessor
// in the predecessor list of the header.
// var a = 0;
@@ -179,7 +172,7 @@
ArenaPool arena;
ArenaAllocator allocator(&arena);
- HGraph* graph = TestCode(data, &allocator);
+ HGraph* graph = CreateCFG(&allocator, data);
TestBlock(graph, 0, false, kInvalidBlockId); // entry block
TestBlock(graph, 1, false, kInvalidBlockId); // goto block
@@ -191,7 +184,7 @@
TestBlock(graph, 6, false, kInvalidBlockId); // exit block
}
-TEST(FindLoopsTest, Loop3) {
+TEST_F(FindLoopsTest, Loop3) {
// Make sure we create a preheader of a loop when a header originally has two
// incoming blocks and one back edge.
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
@@ -204,7 +197,7 @@
ArenaPool arena;
ArenaAllocator allocator(&arena);
- HGraph* graph = TestCode(data, &allocator);
+ HGraph* graph = CreateCFG(&allocator, data);
TestBlock(graph, 0, false, kInvalidBlockId); // entry block
TestBlock(graph, 1, false, kInvalidBlockId); // goto block
@@ -218,7 +211,7 @@
TestBlock(graph, 8, false, kInvalidBlockId); // synthesized pre header
}
-TEST(FindLoopsTest, Loop4) {
+TEST_F(FindLoopsTest, Loop4) {
// Test loop with originally two back edges.
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
@@ -230,7 +223,7 @@
ArenaPool arena;
ArenaAllocator allocator(&arena);
- HGraph* graph = TestCode(data, &allocator);
+ HGraph* graph = CreateCFG(&allocator, data);
TestBlock(graph, 0, false, kInvalidBlockId); // entry block
TestBlock(graph, 1, false, kInvalidBlockId); // pre header
@@ -244,7 +237,7 @@
}
-TEST(FindLoopsTest, Loop5) {
+TEST_F(FindLoopsTest, Loop5) {
// Test loop with two exit edges.
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
@@ -256,7 +249,7 @@
ArenaPool arena;
ArenaAllocator allocator(&arena);
- HGraph* graph = TestCode(data, &allocator);
+ HGraph* graph = CreateCFG(&allocator, data);
TestBlock(graph, 0, false, kInvalidBlockId); // entry block
TestBlock(graph, 1, false, kInvalidBlockId); // pre header
@@ -270,7 +263,7 @@
TestBlock(graph, 8, false, kInvalidBlockId); // synthesized block at the loop exit
}
-TEST(FindLoopsTest, InnerLoop) {
+TEST_F(FindLoopsTest, InnerLoop) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::IF_EQ, 6,
@@ -281,7 +274,7 @@
ArenaPool arena;
ArenaAllocator allocator(&arena);
- HGraph* graph = TestCode(data, &allocator);
+ HGraph* graph = CreateCFG(&allocator, data);
TestBlock(graph, 0, false, kInvalidBlockId); // entry block
TestBlock(graph, 1, false, kInvalidBlockId); // pre header of outer loop
@@ -301,7 +294,7 @@
*graph->GetBlocks()[3]->GetLoopInformation()));
}
-TEST(FindLoopsTest, TwoLoops) {
+TEST_F(FindLoopsTest, TwoLoops) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::IF_EQ, 3,
@@ -312,7 +305,7 @@
ArenaPool arena;
ArenaAllocator allocator(&arena);
- HGraph* graph = TestCode(data, &allocator);
+ HGraph* graph = CreateCFG(&allocator, data);
TestBlock(graph, 0, false, kInvalidBlockId); // entry block
TestBlock(graph, 1, false, kInvalidBlockId); // pre header of first loop
@@ -331,7 +324,7 @@
*graph->GetBlocks()[4]->GetLoopInformation()));
}
-TEST(FindLoopsTest, NonNaturalLoop) {
+TEST_F(FindLoopsTest, NonNaturalLoop) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::IF_EQ, 3,
@@ -342,14 +335,14 @@
ArenaPool arena;
ArenaAllocator allocator(&arena);
- HGraph* graph = TestCode(data, &allocator);
+ HGraph* graph = CreateCFG(&allocator, data);
ASSERT_TRUE(graph->GetBlocks()[3]->IsLoopHeader());
HLoopInformation* info = graph->GetBlocks()[3]->GetLoopInformation();
ASSERT_EQ(1u, info->NumberOfBackEdges());
ASSERT_FALSE(info->GetHeader()->Dominates(info->GetBackEdges()[0]));
}
-TEST(FindLoopsTest, DoWhileLoop) {
+TEST_F(FindLoopsTest, DoWhileLoop) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::GOTO | 0x0100,
@@ -358,7 +351,7 @@
ArenaPool arena;
ArenaAllocator allocator(&arena);
- HGraph* graph = TestCode(data, &allocator);
+ HGraph* graph = CreateCFG(&allocator, data);
TestBlock(graph, 0, false, kInvalidBlockId); // entry block
TestBlock(graph, 1, false, kInvalidBlockId); // pre header of first loop
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 962e77d..e6e9177 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -149,6 +149,103 @@
}
current->Accept(this);
}
+
+ // Ensure that catch blocks are not normal successors, and normal blocks are
+ // never exceptional successors.
+ for (HBasicBlock* successor : block->GetNormalSuccessors()) {
+ if (successor->IsCatchBlock()) {
+ AddError(StringPrintf("Catch block %d is a normal successor of block %d.",
+ successor->GetBlockId(),
+ block->GetBlockId()));
+ }
+ }
+ for (HBasicBlock* successor : block->GetExceptionalSuccessors()) {
+ if (!successor->IsCatchBlock()) {
+ AddError(StringPrintf("Normal block %d is an exceptional successor of block %d.",
+ successor->GetBlockId(),
+ block->GetBlockId()));
+ }
+ }
+
+ // Ensure dominated blocks have `block` as the dominator.
+ for (HBasicBlock* dominated : block->GetDominatedBlocks()) {
+ if (dominated->GetDominator() != block) {
+ AddError(StringPrintf("Block %d should be the dominator of %d.",
+ block->GetBlockId(),
+ dominated->GetBlockId()));
+ }
+ }
+
+ // Ensure there is no critical edge (i.e., an edge connecting a
+ // block with multiple successors to a block with multiple
+ // predecessors). Exceptional edges are synthesized and hence
+ // not accounted for.
+ if (block->GetSuccessors().size() > 1) {
+ for (HBasicBlock* successor : block->GetNormalSuccessors()) {
+ if (successor->IsExitBlock() &&
+ block->IsSingleTryBoundary() &&
+ block->GetPredecessors().size() == 1u &&
+ block->GetSinglePredecessor()->GetLastInstruction()->IsThrow()) {
+ // Allowed critical edge Throw->TryBoundary->Exit.
+ } else if (successor->GetPredecessors().size() > 1) {
+ AddError(StringPrintf("Critical edge between blocks %d and %d.",
+ block->GetBlockId(),
+ successor->GetBlockId()));
+ }
+ }
+ }
+
+ // Ensure try membership information is consistent.
+ if (block->IsCatchBlock()) {
+ if (block->IsTryBlock()) {
+ const HTryBoundary& try_entry = block->GetTryCatchInformation()->GetTryEntry();
+ AddError(StringPrintf("Catch blocks should not be try blocks but catch block %d "
+ "has try entry %s:%d.",
+ block->GetBlockId(),
+ try_entry.DebugName(),
+ try_entry.GetId()));
+ }
+
+ if (block->IsLoopHeader()) {
+ AddError(StringPrintf("Catch blocks should not be loop headers but catch block %d is.",
+ block->GetBlockId()));
+ }
+ } else {
+ for (HBasicBlock* predecessor : block->GetPredecessors()) {
+ const HTryBoundary* incoming_try_entry = predecessor->ComputeTryEntryOfSuccessors();
+ if (block->IsTryBlock()) {
+ const HTryBoundary& stored_try_entry = block->GetTryCatchInformation()->GetTryEntry();
+ if (incoming_try_entry == nullptr) {
+ AddError(StringPrintf("Block %d has try entry %s:%d but no try entry follows "
+ "from predecessor %d.",
+ block->GetBlockId(),
+ stored_try_entry.DebugName(),
+ stored_try_entry.GetId(),
+ predecessor->GetBlockId()));
+ } else if (!incoming_try_entry->HasSameExceptionHandlersAs(stored_try_entry)) {
+ AddError(StringPrintf("Block %d has try entry %s:%d which is not consistent "
+ "with %s:%d that follows from predecessor %d.",
+ block->GetBlockId(),
+ stored_try_entry.DebugName(),
+ stored_try_entry.GetId(),
+ incoming_try_entry->DebugName(),
+ incoming_try_entry->GetId(),
+ predecessor->GetBlockId()));
+ }
+ } else if (incoming_try_entry != nullptr) {
+ AddError(StringPrintf("Block %d is not a try block but try entry %s:%d follows "
+ "from predecessor %d.",
+ block->GetBlockId(),
+ incoming_try_entry->DebugName(),
+ incoming_try_entry->GetId(),
+ predecessor->GetBlockId()));
+ }
+ }
+ }
+
+ if (block->IsLoopHeader()) {
+ HandleLoop(block);
+ }
}
void GraphChecker::VisitBoundsCheck(HBoundsCheck* check) {
@@ -168,7 +265,7 @@
// Ensure that all exception handlers are catch blocks.
// Note that a normal-flow successor may be a catch block before CFG
- // simplification. We only test normal-flow successors in SsaChecker.
+ // simplification. We only test normal-flow successors in GraphChecker.
for (HBasicBlock* handler : handlers) {
if (!handler->IsCatchBlock()) {
AddError(StringPrintf("Block %d with %s:%d has exceptional successor %d which "
@@ -303,6 +400,88 @@
input->GetId()));
}
}
+
+ // Ensure an instruction dominates all its uses.
+ for (HUseIterator<HInstruction*> use_it(instruction->GetUses());
+ !use_it.Done(); use_it.Advance()) {
+ HInstruction* use = use_it.Current()->GetUser();
+ if (!use->IsPhi() && !instruction->StrictlyDominates(use)) {
+ AddError(StringPrintf("Instruction %s:%d in block %d does not dominate "
+ "use %s:%d in block %d.",
+ instruction->DebugName(),
+ instruction->GetId(),
+ current_block_->GetBlockId(),
+ use->DebugName(),
+ use->GetId(),
+ use->GetBlock()->GetBlockId()));
+ }
+ }
+
+ if (instruction->NeedsEnvironment() && !instruction->HasEnvironment()) {
+ AddError(StringPrintf("Instruction %s:%d in block %d requires an environment "
+ "but does not have one.",
+ instruction->DebugName(),
+ instruction->GetId(),
+ current_block_->GetBlockId()));
+ }
+
+ // Ensure an instruction having an environment is dominated by the
+ // instructions contained in the environment.
+ for (HEnvironment* environment = instruction->GetEnvironment();
+ environment != nullptr;
+ environment = environment->GetParent()) {
+ for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+ HInstruction* env_instruction = environment->GetInstructionAt(i);
+ if (env_instruction != nullptr
+ && !env_instruction->StrictlyDominates(instruction)) {
+ AddError(StringPrintf("Instruction %d in environment of instruction %d "
+ "from block %d does not dominate instruction %d.",
+ env_instruction->GetId(),
+ instruction->GetId(),
+ current_block_->GetBlockId(),
+ instruction->GetId()));
+ }
+ }
+ }
+
+ // Ensure that reference type instructions have reference type info.
+ if (instruction->GetType() == Primitive::kPrimNot) {
+ ScopedObjectAccess soa(Thread::Current());
+ if (!instruction->GetReferenceTypeInfo().IsValid()) {
+ AddError(StringPrintf("Reference type instruction %s:%d does not have "
+ "valid reference type information.",
+ instruction->DebugName(),
+ instruction->GetId()));
+ }
+ }
+
+ if (instruction->CanThrowIntoCatchBlock()) {
+ // Find the top-level environment. This corresponds to the environment of
+ // the catch block since we do not inline methods with try/catch.
+ HEnvironment* environment = instruction->GetEnvironment();
+ while (environment->GetParent() != nullptr) {
+ environment = environment->GetParent();
+ }
+
+ // Find all catch blocks and test that `instruction` has an environment
+ // value for each one.
+ const HTryBoundary& entry = instruction->GetBlock()->GetTryCatchInformation()->GetTryEntry();
+ for (HBasicBlock* catch_block : entry.GetExceptionHandlers()) {
+ for (HInstructionIterator phi_it(catch_block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
+ HPhi* catch_phi = phi_it.Current()->AsPhi();
+ if (environment->GetInstructionAt(catch_phi->GetRegNumber()) == nullptr) {
+ AddError(StringPrintf("Instruction %s:%d throws into catch block %d "
+ "with catch phi %d for vreg %d but its "
+ "corresponding environment slot is empty.",
+ instruction->DebugName(),
+ instruction->GetId(),
+ catch_block->GetBlockId(),
+ catch_phi->GetId(),
+ catch_phi->GetRegNumber()));
+ }
+ }
+ }
+ }
}
void GraphChecker::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
@@ -371,108 +550,7 @@
}
}
-void SSAChecker::VisitBasicBlock(HBasicBlock* block) {
- super_type::VisitBasicBlock(block);
-
- // Ensure that catch blocks are not normal successors, and normal blocks are
- // never exceptional successors.
- for (HBasicBlock* successor : block->GetNormalSuccessors()) {
- if (successor->IsCatchBlock()) {
- AddError(StringPrintf("Catch block %d is a normal successor of block %d.",
- successor->GetBlockId(),
- block->GetBlockId()));
- }
- }
- for (HBasicBlock* successor : block->GetExceptionalSuccessors()) {
- if (!successor->IsCatchBlock()) {
- AddError(StringPrintf("Normal block %d is an exceptional successor of block %d.",
- successor->GetBlockId(),
- block->GetBlockId()));
- }
- }
-
- // Ensure dominated blocks have `block` as the dominator.
- for (HBasicBlock* dominated : block->GetDominatedBlocks()) {
- if (dominated->GetDominator() != block) {
- AddError(StringPrintf("Block %d should be the dominator of %d.",
- block->GetBlockId(),
- dominated->GetBlockId()));
- }
- }
-
- // Ensure there is no critical edge (i.e., an edge connecting a
- // block with multiple successors to a block with multiple
- // predecessors). Exceptional edges are synthesized and hence
- // not accounted for.
- if (block->GetSuccessors().size() > 1) {
- for (HBasicBlock* successor : block->GetNormalSuccessors()) {
- if (successor->IsExitBlock() &&
- block->IsSingleTryBoundary() &&
- block->GetPredecessors().size() == 1u &&
- block->GetSinglePredecessor()->GetLastInstruction()->IsThrow()) {
- // Allowed critical edge Throw->TryBoundary->Exit.
- } else if (successor->GetPredecessors().size() > 1) {
- AddError(StringPrintf("Critical edge between blocks %d and %d.",
- block->GetBlockId(),
- successor->GetBlockId()));
- }
- }
- }
-
- // Ensure try membership information is consistent.
- if (block->IsCatchBlock()) {
- if (block->IsTryBlock()) {
- const HTryBoundary& try_entry = block->GetTryCatchInformation()->GetTryEntry();
- AddError(StringPrintf("Catch blocks should not be try blocks but catch block %d "
- "has try entry %s:%d.",
- block->GetBlockId(),
- try_entry.DebugName(),
- try_entry.GetId()));
- }
-
- if (block->IsLoopHeader()) {
- AddError(StringPrintf("Catch blocks should not be loop headers but catch block %d is.",
- block->GetBlockId()));
- }
- } else {
- for (HBasicBlock* predecessor : block->GetPredecessors()) {
- const HTryBoundary* incoming_try_entry = predecessor->ComputeTryEntryOfSuccessors();
- if (block->IsTryBlock()) {
- const HTryBoundary& stored_try_entry = block->GetTryCatchInformation()->GetTryEntry();
- if (incoming_try_entry == nullptr) {
- AddError(StringPrintf("Block %d has try entry %s:%d but no try entry follows "
- "from predecessor %d.",
- block->GetBlockId(),
- stored_try_entry.DebugName(),
- stored_try_entry.GetId(),
- predecessor->GetBlockId()));
- } else if (!incoming_try_entry->HasSameExceptionHandlersAs(stored_try_entry)) {
- AddError(StringPrintf("Block %d has try entry %s:%d which is not consistent "
- "with %s:%d that follows from predecessor %d.",
- block->GetBlockId(),
- stored_try_entry.DebugName(),
- stored_try_entry.GetId(),
- incoming_try_entry->DebugName(),
- incoming_try_entry->GetId(),
- predecessor->GetBlockId()));
- }
- } else if (incoming_try_entry != nullptr) {
- AddError(StringPrintf("Block %d is not a try block but try entry %s:%d follows "
- "from predecessor %d.",
- block->GetBlockId(),
- incoming_try_entry->DebugName(),
- incoming_try_entry->GetId(),
- predecessor->GetBlockId()));
- }
- }
- }
-
- if (block->IsLoopHeader()) {
- CheckLoop(block);
- }
-}
-
-void SSAChecker::CheckLoop(HBasicBlock* loop_header) {
+void GraphChecker::HandleLoop(HBasicBlock* loop_header) {
int id = loop_header->GetBlockId();
HLoopInformation* loop_information = loop_header->GetLoopInformation();
@@ -582,92 +660,6 @@
}
}
-void SSAChecker::VisitInstruction(HInstruction* instruction) {
- super_type::VisitInstruction(instruction);
-
- // Ensure an instruction dominates all its uses.
- for (HUseIterator<HInstruction*> use_it(instruction->GetUses());
- !use_it.Done(); use_it.Advance()) {
- HInstruction* use = use_it.Current()->GetUser();
- if (!use->IsPhi() && !instruction->StrictlyDominates(use)) {
- AddError(StringPrintf("Instruction %s:%d in block %d does not dominate "
- "use %s:%d in block %d.",
- instruction->DebugName(),
- instruction->GetId(),
- current_block_->GetBlockId(),
- use->DebugName(),
- use->GetId(),
- use->GetBlock()->GetBlockId()));
- }
- }
-
- if (instruction->NeedsEnvironment() && !instruction->HasEnvironment()) {
- AddError(StringPrintf("Instruction %s:%d in block %d requires an environment "
- "but does not have one.",
- instruction->DebugName(),
- instruction->GetId(),
- current_block_->GetBlockId()));
- }
-
- // Ensure an instruction having an environment is dominated by the
- // instructions contained in the environment.
- for (HEnvironment* environment = instruction->GetEnvironment();
- environment != nullptr;
- environment = environment->GetParent()) {
- for (size_t i = 0, e = environment->Size(); i < e; ++i) {
- HInstruction* env_instruction = environment->GetInstructionAt(i);
- if (env_instruction != nullptr
- && !env_instruction->StrictlyDominates(instruction)) {
- AddError(StringPrintf("Instruction %d in environment of instruction %d "
- "from block %d does not dominate instruction %d.",
- env_instruction->GetId(),
- instruction->GetId(),
- current_block_->GetBlockId(),
- instruction->GetId()));
- }
- }
- }
-
- // Ensure that reference type instructions have reference type info.
- if (instruction->GetType() == Primitive::kPrimNot) {
- ScopedObjectAccess soa(Thread::Current());
- if (!instruction->GetReferenceTypeInfo().IsValid()) {
- AddError(StringPrintf("Reference type instruction %s:%d does not have "
- "valid reference type information.",
- instruction->DebugName(),
- instruction->GetId()));
- }
- }
-
- if (instruction->CanThrowIntoCatchBlock()) {
- // Find the top-level environment. This corresponds to the environment of
- // the catch block since we do not inline methods with try/catch.
- HEnvironment* environment = instruction->GetEnvironment();
- while (environment->GetParent() != nullptr) {
- environment = environment->GetParent();
- }
-
- // Find all catch blocks and test that `instruction` has an environment
- // value for each one.
- const HTryBoundary& entry = instruction->GetBlock()->GetTryCatchInformation()->GetTryEntry();
- for (HBasicBlock* catch_block : entry.GetExceptionHandlers()) {
- for (HInstructionIterator phi_it(catch_block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
- HPhi* catch_phi = phi_it.Current()->AsPhi();
- if (environment->GetInstructionAt(catch_phi->GetRegNumber()) == nullptr) {
- AddError(StringPrintf("Instruction %s:%d throws into catch block %d "
- "with catch phi %d for vreg %d but its "
- "corresponding environment slot is empty.",
- instruction->DebugName(),
- instruction->GetId(),
- catch_block->GetBlockId(),
- catch_phi->GetId(),
- catch_phi->GetRegNumber()));
- }
- }
- }
- }
-}
-
static Primitive::Type PrimitiveKind(Primitive::Type type) {
switch (type) {
case Primitive::kPrimBoolean:
@@ -710,7 +702,7 @@
}
}
-void SSAChecker::VisitPhi(HPhi* phi) {
+void GraphChecker::VisitPhi(HPhi* phi) {
VisitInstruction(phi);
// Ensure the first input of a phi is not itself.
@@ -846,7 +838,7 @@
}
}
-void SSAChecker::HandleBooleanInput(HInstruction* instruction, size_t input_index) {
+void GraphChecker::HandleBooleanInput(HInstruction* instruction, size_t input_index) {
HInstruction* input = instruction->InputAt(input_index);
if (input->IsIntConstant()) {
int32_t value = input->AsIntConstant()->GetValue();
@@ -876,7 +868,7 @@
}
}
-void SSAChecker::VisitPackedSwitch(HPackedSwitch* instruction) {
+void GraphChecker::VisitPackedSwitch(HPackedSwitch* instruction) {
VisitInstruction(instruction);
// Check that the number of block successors matches the switch count plus
// one for the default block.
@@ -892,22 +884,22 @@
}
}
-void SSAChecker::VisitIf(HIf* instruction) {
+void GraphChecker::VisitIf(HIf* instruction) {
VisitInstruction(instruction);
HandleBooleanInput(instruction, 0);
}
-void SSAChecker::VisitSelect(HSelect* instruction) {
+void GraphChecker::VisitSelect(HSelect* instruction) {
VisitInstruction(instruction);
HandleBooleanInput(instruction, 2);
}
-void SSAChecker::VisitBooleanNot(HBooleanNot* instruction) {
+void GraphChecker::VisitBooleanNot(HBooleanNot* instruction) {
VisitInstruction(instruction);
HandleBooleanInput(instruction, 0);
}
-void SSAChecker::VisitCondition(HCondition* op) {
+void GraphChecker::VisitCondition(HCondition* op) {
VisitInstruction(op);
if (op->GetType() != Primitive::kPrimBoolean) {
AddError(StringPrintf(
@@ -937,7 +929,7 @@
}
}
-void SSAChecker::VisitBinaryOperation(HBinaryOperation* op) {
+void GraphChecker::VisitBinaryOperation(HBinaryOperation* op) {
VisitInstruction(op);
if (op->IsUShr() || op->IsShr() || op->IsShl() || op->IsRor()) {
if (PrimitiveKind(op->InputAt(1)->GetType()) != Primitive::kPrimInt) {
@@ -979,7 +971,7 @@
}
}
-void SSAChecker::VisitConstant(HConstant* instruction) {
+void GraphChecker::VisitConstant(HConstant* instruction) {
HBasicBlock* block = instruction->GetBlock();
if (!block->IsEntryBlock()) {
AddError(StringPrintf(
@@ -990,7 +982,7 @@
}
}
-void SSAChecker::VisitBoundType(HBoundType* instruction) {
+void GraphChecker::VisitBoundType(HBoundType* instruction) {
VisitInstruction(instruction);
ScopedObjectAccess soa(Thread::Current());
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index 8724cde..52252cd 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -32,34 +32,38 @@
dump_prefix_(dump_prefix),
seen_ids_(graph->GetArena(), graph->GetCurrentInstructionId(), false) {}
- // Check the whole graph (in insertion order).
- virtual void Run() { VisitInsertionOrder(); }
+ // Check the whole graph (in reverse post-order).
+ void Run() {
+ // VisitReversePostOrder is used instead of VisitInsertionOrder,
+ // as the latter might visit dead blocks removed by the dominator
+ // computation.
+ VisitReversePostOrder();
+ }
- // Check `block`.
void VisitBasicBlock(HBasicBlock* block) OVERRIDE;
- // Check `instruction`.
void VisitInstruction(HInstruction* instruction) OVERRIDE;
+ void VisitPhi(HPhi* phi) OVERRIDE;
- // Perform control-flow graph checks on instruction.
- void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE;
-
- // Check that the HasBoundsChecks() flag is set for bounds checks.
+ void VisitBinaryOperation(HBinaryOperation* op) OVERRIDE;
+ void VisitBooleanNot(HBooleanNot* instruction) OVERRIDE;
+ void VisitBoundType(HBoundType* instruction) OVERRIDE;
void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE;
-
- // Check successors of blocks ending in TryBoundary.
- void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE;
-
- // Check that LoadException is the first instruction in a catch block.
- void VisitLoadException(HLoadException* load) OVERRIDE;
-
- // Check that HCheckCast and HInstanceOf have HLoadClass as second input.
void VisitCheckCast(HCheckCast* check) OVERRIDE;
+ void VisitCondition(HCondition* op) OVERRIDE;
+ void VisitConstant(HConstant* instruction) OVERRIDE;
+ void VisitIf(HIf* instruction) OVERRIDE;
void VisitInstanceOf(HInstanceOf* check) OVERRIDE;
-
- // Check that the Return and ReturnVoid jump to the exit block.
+ void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE;
+ void VisitLoadException(HLoadException* load) OVERRIDE;
+ void VisitPackedSwitch(HPackedSwitch* instruction) OVERRIDE;
void VisitReturn(HReturn* ret) OVERRIDE;
void VisitReturnVoid(HReturnVoid* ret) OVERRIDE;
+ void VisitSelect(HSelect* instruction) OVERRIDE;
+ void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE;
+
+ void HandleLoop(HBasicBlock* loop_header);
+ void HandleBooleanInput(HInstruction* instruction, size_t input_index);
// Was the last visit of the graph valid?
bool IsValid() const {
@@ -97,46 +101,6 @@
DISALLOW_COPY_AND_ASSIGN(GraphChecker);
};
-
-// An SSA graph visitor performing various checks.
-class SSAChecker : public GraphChecker {
- public:
- typedef GraphChecker super_type;
-
- explicit SSAChecker(HGraph* graph)
- : GraphChecker(graph, "art::SSAChecker: ") {}
-
- // Check the whole graph (in reverse post-order).
- void Run() OVERRIDE {
- // VisitReversePostOrder is used instead of VisitInsertionOrder,
- // as the latter might visit dead blocks removed by the dominator
- // computation.
- VisitReversePostOrder();
- }
-
- // Perform SSA form checks on `block`.
- void VisitBasicBlock(HBasicBlock* block) OVERRIDE;
- // Loop-related checks from block `loop_header`.
- void CheckLoop(HBasicBlock* loop_header);
-
- // Perform SSA form checks on instructions.
- void VisitInstruction(HInstruction* instruction) OVERRIDE;
- void VisitPhi(HPhi* phi) OVERRIDE;
- void VisitBinaryOperation(HBinaryOperation* op) OVERRIDE;
- void VisitCondition(HCondition* op) OVERRIDE;
- void VisitIf(HIf* instruction) OVERRIDE;
- void VisitPackedSwitch(HPackedSwitch* instruction) OVERRIDE;
- void VisitSelect(HSelect* instruction) OVERRIDE;
- void VisitBooleanNot(HBooleanNot* instruction) OVERRIDE;
- void VisitConstant(HConstant* instruction) OVERRIDE;
- void VisitBoundType(HBoundType* instruction) OVERRIDE;
-
- void HandleBooleanInput(HInstruction* instruction, size_t input_index);
-
- private:
- DISALLOW_COPY_AND_ASSIGN(SSAChecker);
-};
-
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_GRAPH_CHECKER_H_
diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc
index d10df4c..2b82319 100644
--- a/compiler/optimizing/graph_checker_test.cc
+++ b/compiler/optimizing/graph_checker_test.cc
@@ -38,6 +38,7 @@
graph->AddBlock(exit_block);
graph->SetExitBlock(exit_block);
entry_block->AddSuccessor(exit_block);
+ graph->BuildDominatorTree();
return graph;
}
@@ -52,28 +53,16 @@
ASSERT_TRUE(graph_checker.IsValid());
}
-static void TestCodeSSA(const uint16_t* data) {
- ArenaPool pool;
- ArenaAllocator allocator(&pool);
- HGraph* graph = CreateCFG(&allocator, data);
- ASSERT_NE(graph, nullptr);
+class GraphCheckerTest : public CommonCompilerTest {};
- TransformToSsa(graph);
-
- SSAChecker ssa_checker(graph);
- ssa_checker.Run();
- ASSERT_TRUE(ssa_checker.IsValid());
-}
-
-
-TEST(GraphChecker, ReturnVoid) {
+TEST_F(GraphCheckerTest, ReturnVoid) {
const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
Instruction::RETURN_VOID);
TestCode(data);
}
-TEST(GraphChecker, CFG1) {
+TEST_F(GraphCheckerTest, CFG1) {
const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
Instruction::GOTO | 0x100,
Instruction::RETURN_VOID);
@@ -81,7 +70,7 @@
TestCode(data);
}
-TEST(GraphChecker, CFG2) {
+TEST_F(GraphCheckerTest, CFG2) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::IF_EQ, 3,
@@ -91,7 +80,7 @@
TestCode(data);
}
-TEST(GraphChecker, CFG3) {
+TEST_F(GraphCheckerTest, CFG3) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::IF_EQ, 3,
@@ -103,7 +92,7 @@
// Test case with an invalid graph containing inconsistent
// predecessor/successor arcs in CFG.
-TEST(GraphChecker, InconsistentPredecessorsAndSuccessors) {
+TEST_F(GraphCheckerTest, InconsistentPredecessorsAndSuccessors) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
@@ -121,7 +110,7 @@
// Test case with an invalid graph containing a non-branch last
// instruction in a block.
-TEST(GraphChecker, BlockEndingWithNonBranchInstruction) {
+TEST_F(GraphCheckerTest, BlockEndingWithNonBranchInstruction) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
@@ -141,9 +130,7 @@
ASSERT_FALSE(graph_checker.IsValid());
}
-class SSACheckerTest : public CommonCompilerTest {};
-
-TEST_F(SSACheckerTest, SSAPhi) {
+TEST_F(GraphCheckerTest, SSAPhi) {
// This code creates one Phi function during the conversion to SSA form.
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
@@ -151,7 +138,7 @@
Instruction::CONST_4 | 4 << 12 | 0,
Instruction::RETURN | 0 << 8);
- TestCodeSSA(data);
+ TestCode(data);
}
} // namespace art
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 9d796c1..4cf0eb1 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -22,6 +22,7 @@
#include <sstream>
#include "bounds_check_elimination.h"
+#include "builder.h"
#include "code_generator.h"
#include "dead_code_elimination.h"
#include "disassembler.h"
@@ -31,7 +32,6 @@
#include "optimization.h"
#include "reference_type_propagation.h"
#include "register_allocator.h"
-#include "ssa_builder.h"
#include "ssa_liveness_analysis.h"
#include "utils/assembler.h"
@@ -368,11 +368,13 @@
}
void VisitCheckCast(HCheckCast* check_cast) OVERRIDE {
+ StartAttributeStream("check_kind") << check_cast->GetTypeCheckKind();
StartAttributeStream("must_do_null_check") << std::boolalpha
<< check_cast->MustDoNullCheck() << std::noboolalpha;
}
void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE {
+ StartAttributeStream("check_kind") << instance_of->GetTypeCheckKind();
StartAttributeStream("must_do_null_check") << std::boolalpha
<< instance_of->MustDoNullCheck() << std::noboolalpha;
}
@@ -508,7 +510,7 @@
|| IsPass(HDeadCodeElimination::kInitialDeadCodeEliminationPassName)
|| IsPass(BoundsCheckElimination::kBoundsCheckEliminationPassName)
|| IsPass(RegisterAllocator::kRegisterAllocatorPassName)
- || IsPass(SsaBuilder::kSsaBuilderPassName)) {
+ || IsPass(HGraphBuilder::kBuilderPassName)) {
HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
if (info == nullptr) {
StartAttributeStream("loop") << "none";
@@ -525,7 +527,7 @@
}
}
- if ((IsPass(SsaBuilder::kSsaBuilderPassName)
+ if ((IsPass(HGraphBuilder::kBuilderPassName)
|| IsPass(HInliner::kInlinerPassName))
&& (instruction->GetType() == Primitive::kPrimNot)) {
ReferenceTypeInfo info = instruction->IsLoadClass()
@@ -545,7 +547,7 @@
// doesn't run or doesn't inline anything, the NullConstant remains untyped.
// So we should check NullConstants for validity only after reference type propagation.
DCHECK(graph_in_bad_state_ ||
- (!is_after_pass_ && IsPass(SsaBuilder::kSsaBuilderPassName)))
+ (!is_after_pass_ && IsPass(HGraphBuilder::kBuilderPassName)))
<< instruction->DebugName() << instruction->GetId() << " has invalid rti "
<< (is_after_pass_ ? "after" : "before") << " pass " << pass_name_;
}
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
index 1f4eaf3..56dc088 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -100,7 +100,7 @@
ASSERT_EQ(different_offset->GetBlock(), block);
ASSERT_EQ(use_after_kill->GetBlock(), block);
- TransformToSsa(graph);
+ graph->BuildDominatorTree();
SideEffectsAnalysis side_effects(graph);
side_effects.Run();
GVNOptimization(graph, side_effects).Run();
@@ -182,7 +182,7 @@
0));
join->AddInstruction(new (&allocator) HExit());
- TransformToSsa(graph);
+ graph->BuildDominatorTree();
SideEffectsAnalysis side_effects(graph);
side_effects.Run();
GVNOptimization(graph, side_effects).Run();
@@ -288,7 +288,7 @@
ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body);
ASSERT_EQ(field_get_in_exit->GetBlock(), exit);
- TransformToSsa(graph);
+ graph->BuildDominatorTree();
{
SideEffectsAnalysis side_effects(graph);
side_effects.Run();
@@ -364,7 +364,7 @@
inner_loop_exit->AddInstruction(new (&allocator) HGoto());
outer_loop_exit->AddInstruction(new (&allocator) HExit());
- TransformToSsa(graph);
+ graph->BuildDominatorTree();
ASSERT_TRUE(inner_loop_header->GetLoopInformation()->IsIn(
*outer_loop_header->GetLoopInformation()));
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 29a1845..89e4690 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -86,39 +86,28 @@
constant1_ = graph_->GetIntConstant(1);
constant100_ = graph_->GetIntConstant(100);
float_constant0_ = graph_->GetFloatConstant(0.0f);
- induc_ = new (&allocator_) HLocal(n);
- entry_->AddInstruction(induc_);
- entry_->AddInstruction(new (&allocator_) HStoreLocal(induc_, constant0_));
- tmp_ = new (&allocator_) HLocal(n + 1);
- entry_->AddInstruction(tmp_);
- entry_->AddInstruction(new (&allocator_) HStoreLocal(tmp_, constant100_));
- dum_ = new (&allocator_) HLocal(n + 2);
- entry_->AddInstruction(dum_);
return_->AddInstruction(new (&allocator_) HReturnVoid());
exit_->AddInstruction(new (&allocator_) HExit());
// Provide loop instructions.
for (int d = 0; d < n; d++) {
- basic_[d] = new (&allocator_) HLocal(d);
- entry_->AddInstruction(basic_[d]);
- loop_preheader_[d]->AddInstruction(new (&allocator_) HStoreLocal(basic_[d], constant0_));
+ basic_[d] = new (&allocator_) HPhi(&allocator_, d, 0, Primitive::kPrimInt);
loop_preheader_[d]->AddInstruction(new (&allocator_) HGoto());
- HInstruction* load = new (&allocator_) HLoadLocal(basic_[d], Primitive::kPrimInt);
- loop_header_[d]->AddInstruction(load);
- HInstruction* compare = new (&allocator_) HLessThan(load, constant100_);
+ loop_header_[d]->AddPhi(basic_[d]);
+ HInstruction* compare = new (&allocator_) HLessThan(basic_[d], constant100_);
loop_header_[d]->AddInstruction(compare);
loop_header_[d]->AddInstruction(new (&allocator_) HIf(compare));
- load = new (&allocator_) HLoadLocal(basic_[d], Primitive::kPrimInt);
- loop_body_[d]->AddInstruction(load);
- increment_[d] = new (&allocator_) HAdd(Primitive::kPrimInt, load, constant1_);
+ increment_[d] = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[d], constant1_);
loop_body_[d]->AddInstruction(increment_[d]);
- loop_body_[d]->AddInstruction(new (&allocator_) HStoreLocal(basic_[d], increment_[d]));
loop_body_[d]->AddInstruction(new (&allocator_) HGoto());
+
+ basic_[d]->AddInput(constant0_);
+ basic_[d]->AddInput(increment_[d]);
}
}
// Builds if-statement at depth d.
- void BuildIf(int d, HBasicBlock** ifT, HBasicBlock **ifF) {
+ HPhi* BuildIf(int d, HBasicBlock** ifT, HBasicBlock **ifF) {
HBasicBlock* cond = new (&allocator_) HBasicBlock(graph_);
HBasicBlock* ifTrue = new (&allocator_) HBasicBlock(graph_);
HBasicBlock* ifFalse = new (&allocator_) HBasicBlock(graph_);
@@ -134,6 +123,10 @@
cond->AddInstruction(new (&allocator_) HIf(parameter_));
*ifT = ifTrue;
*ifF = ifFalse;
+
+ HPhi* select_phi = new (&allocator_) HPhi(&allocator_, -1, 0, Primitive::kPrimInt);
+ loop_body_[d]->AddPhi(select_phi);
+ return select_phi;
}
// Inserts instruction right before increment at depth d.
@@ -142,25 +135,20 @@
return instruction;
}
- // Inserts local load at depth d.
- HInstruction* InsertLocalLoad(HLocal* local, int d) {
- return InsertInstruction(new (&allocator_) HLoadLocal(local, Primitive::kPrimInt), d);
+ // Inserts a phi to loop header at depth d and returns it.
+ HPhi* InsertLoopPhi(int vreg, int d) {
+ HPhi* phi = new (&allocator_) HPhi(&allocator_, vreg, 0, Primitive::kPrimInt);
+ loop_header_[d]->AddPhi(phi);
+ return phi;
}
- // Inserts local store at depth d.
- HInstruction* InsertLocalStore(HLocal* local, HInstruction* rhs, int d) {
- return InsertInstruction(new (&allocator_) HStoreLocal(local, rhs), d);
- }
-
- // Inserts an array store with given local as subscript at depth d to
+ // Inserts an array store with given `subscript` at depth d to
// enable tests to inspect the computed induction at that point easily.
- HInstruction* InsertArrayStore(HLocal* subscript, int d) {
- HInstruction* load = InsertInstruction(
- new (&allocator_) HLoadLocal(subscript, Primitive::kPrimInt), d);
+ HInstruction* InsertArrayStore(HInstruction* subscript, int d) {
// ArraySet is given a float value in order to avoid SsaBuilder typing
// it from the array's non-existent reference type info.
return InsertInstruction(new (&allocator_) HArraySet(
- parameter_, load, float_constant0_, Primitive::kPrimFloat, 0), d);
+ parameter_, subscript, float_constant0_, Primitive::kPrimFloat, 0), d);
}
// Returns induction information of instruction in loop at depth d.
@@ -171,7 +159,7 @@
// Performs InductionVarAnalysis (after proper set up).
void PerformInductionVarAnalysis() {
- TransformToSsa(graph_);
+ graph_->BuildDominatorTree();
iva_ = new (&allocator_) HInductionVarAnalysis(graph_);
iva_->Run();
}
@@ -191,16 +179,13 @@
HInstruction* constant1_;
HInstruction* constant100_;
HInstruction* float_constant0_;
- HLocal* induc_; // "vreg_n", the "k"
- HLocal* tmp_; // "vreg_n+1"
- HLocal* dum_; // "vreg_n+2"
// Loop specifics.
HBasicBlock* loop_preheader_[10];
HBasicBlock* loop_header_[10];
HBasicBlock* loop_body_[10];
HInstruction* increment_[10];
- HLocal* basic_[10]; // "vreg_d", the "i_d"
+ HPhi* basic_[10]; // "vreg_d", the "i_d"
};
//
@@ -216,7 +201,7 @@
// ..
// }
BuildLoopNest(10);
- TransformToSsa(graph_);
+ graph_->BuildDominatorTree();
ASSERT_EQ(entry_->GetLoopInformation(), nullptr);
for (int d = 0; d < 1; d++) {
ASSERT_EQ(loop_preheader_[d]->GetLoopInformation(),
@@ -258,20 +243,15 @@
// }
BuildLoopNest(1);
HInstruction *add = InsertInstruction(
- new (&allocator_) HAdd(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
- InsertLocalStore(induc_, add, 0);
+ new (&allocator_) HAdd(Primitive::kPrimInt, constant100_, basic_[0]), 0);
HInstruction *sub = InsertInstruction(
- new (&allocator_) HSub(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
- InsertLocalStore(induc_, sub, 0);
+ new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
HInstruction *mul = InsertInstruction(
- new (&allocator_) HMul(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
- InsertLocalStore(induc_, mul, 0);
+ new (&allocator_) HMul(Primitive::kPrimInt, constant100_, basic_[0]), 0);
HInstruction *shl = InsertInstruction(
- new (&allocator_) HShl(Primitive::kPrimInt, InsertLocalLoad(basic_[0], 0), constant1_), 0);
- InsertLocalStore(induc_, shl, 0);
+ new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0);
HInstruction *neg = InsertInstruction(
- new (&allocator_) HNeg(Primitive::kPrimInt, InsertLocalLoad(basic_[0], 0)), 0);
- InsertLocalStore(induc_, neg, 0);
+ new (&allocator_) HNeg(Primitive::kPrimInt, basic_[0]), 0);
PerformInductionVarAnalysis();
EXPECT_STREQ("((1) * i + (100))", GetInductionInfo(add, 0).c_str());
@@ -291,14 +271,16 @@
// a[k] = 0;
// }
BuildLoopNest(1);
+ HPhi* k = InsertLoopPhi(0, 0);
+ k->AddInput(constant0_);
+
HInstruction *add = InsertInstruction(
- new (&allocator_) HAdd(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
- InsertLocalStore(induc_, add, 0);
- HInstruction* store1 = InsertArrayStore(induc_, 0);
+ new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0);
+ HInstruction* store1 = InsertArrayStore(add, 0);
HInstruction *sub = InsertInstruction(
- new (&allocator_) HSub(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0);
- InsertLocalStore(induc_, sub, 0);
- HInstruction* store2 = InsertArrayStore(induc_, 0);
+ new (&allocator_) HSub(Primitive::kPrimInt, add, constant1_), 0);
+ HInstruction* store2 = InsertArrayStore(sub, 0);
+ k->AddInput(sub);
PerformInductionVarAnalysis();
EXPECT_STREQ("(((100) - (1)) * i + (100))",
@@ -316,23 +298,24 @@
// a[k] = 0;
// }
BuildLoopNest(1);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
+
HBasicBlock* ifTrue;
HBasicBlock* ifFalse;
- BuildIf(0, &ifTrue, &ifFalse);
+ HPhi* k_body = BuildIf(0, &ifTrue, &ifFalse);
+
// True-branch.
- HInstruction* load1 = new (&allocator_) HLoadLocal(induc_, Primitive::kPrimInt);
- ifTrue->AddInstruction(load1);
- HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, load1, constant1_);
+ HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_);
ifTrue->AddInstruction(inc1);
- ifTrue->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc1));
+ k_body->AddInput(inc1);
// False-branch.
- HInstruction* load2 = new (&allocator_) HLoadLocal(induc_, Primitive::kPrimInt);
- ifFalse->AddInstruction(load2);
- HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, load2, constant1_);
+ HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_);
ifFalse->AddInstruction(inc2);
- ifFalse->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc2));
+ k_body->AddInput(inc2);
// Merge over a phi.
- HInstruction* store = InsertArrayStore(induc_, 0);
+ HInstruction* store = InsertArrayStore(k_body, 0);
+ k_header->AddInput(k_body);
PerformInductionVarAnalysis();
EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(store->InputAt(1), 0).c_str());
@@ -348,21 +331,18 @@
BuildLoopNest(1);
HBasicBlock* ifTrue;
HBasicBlock* ifFalse;
- BuildIf(0, &ifTrue, &ifFalse);
+ HPhi* k = BuildIf(0, &ifTrue, &ifFalse);
+
// True-branch.
- HInstruction* load1 = new (&allocator_) HLoadLocal(basic_[0], Primitive::kPrimInt);
- ifTrue->AddInstruction(load1);
- HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, load1, constant1_);
+ HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], constant1_);
ifTrue->AddInstruction(inc1);
- ifTrue->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc1));
+ k->AddInput(inc1);
// False-branch.
- HInstruction* load2 = new (&allocator_) HLoadLocal(basic_[0], Primitive::kPrimInt);
- ifFalse->AddInstruction(load2);
- HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, load2, constant1_);
+ HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], constant1_);
ifFalse->AddInstruction(inc2);
- ifFalse->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc2));
+ k->AddInput(inc2);
// Merge over a phi.
- HInstruction* store = InsertArrayStore(induc_, 0);
+ HInstruction* store = InsertArrayStore(k, 0);
PerformInductionVarAnalysis();
EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(store->InputAt(1), 0).c_str());
@@ -376,10 +356,13 @@
// k = 100 - i;
// }
BuildLoopNest(1);
- HInstruction* store = InsertArrayStore(induc_, 0);
+ HPhi* k = InsertLoopPhi(0, 0);
+ k->AddInput(constant0_);
+
+ HInstruction* store = InsertArrayStore(k, 0);
HInstruction *sub = InsertInstruction(
- new (&allocator_) HSub(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
- InsertLocalStore(induc_, sub, 0);
+ new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
+ k->AddInput(sub);
PerformInductionVarAnalysis();
EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)))",
@@ -396,11 +379,16 @@
// t = 100 - i;
// }
BuildLoopNest(1);
- HInstruction* store = InsertArrayStore(induc_, 0);
- InsertLocalStore(induc_, InsertLocalLoad(tmp_, 0), 0);
+ HPhi* k = InsertLoopPhi(0, 0);
+ k->AddInput(constant0_);
+ HPhi* t = InsertLoopPhi(1, 0);
+ t->AddInput(constant100_);
+
+ HInstruction* store = InsertArrayStore(k, 0);
+ k->AddInput(t);
HInstruction *sub = InsertInstruction(
- new (&allocator_) HSub(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0);
- InsertLocalStore(tmp_, sub, 0);
+ new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0], 0), 0);
+ t->AddInput(sub);
PerformInductionVarAnalysis();
EXPECT_STREQ("wrap((0), wrap((100), (( - (1)) * i + (100))))",
@@ -419,26 +407,21 @@
// k = i << 1;
// }
BuildLoopNest(1);
+ HPhi* k = InsertLoopPhi(0, 0);
+ k->AddInput(constant0_);
+
HInstruction *add = InsertInstruction(
- new (&allocator_) HAdd(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
- InsertLocalStore(tmp_, add, 0);
+ new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0);
HInstruction *sub = InsertInstruction(
- new (&allocator_) HSub(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
- InsertLocalStore(tmp_, sub, 0);
+ new (&allocator_) HSub(Primitive::kPrimInt, k, constant100_), 0);
HInstruction *mul = InsertInstruction(
- new (&allocator_) HMul(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
- InsertLocalStore(tmp_, mul, 0);
+ new (&allocator_) HMul(Primitive::kPrimInt, k, constant100_), 0);
HInstruction *shl = InsertInstruction(
- new (&allocator_) HShl(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0);
- InsertLocalStore(tmp_, shl, 0);
+ new (&allocator_) HShl(Primitive::kPrimInt, k, constant1_), 0);
HInstruction *neg = InsertInstruction(
- new (&allocator_) HNeg(Primitive::kPrimInt, InsertLocalLoad(induc_, 0)), 0);
- InsertLocalStore(tmp_, neg, 0);
- InsertLocalStore(
- induc_,
- InsertInstruction(
- new (&allocator_)
- HShl(Primitive::kPrimInt, InsertLocalLoad(basic_[0], 0), constant1_), 0), 0);
+ new (&allocator_) HNeg(Primitive::kPrimInt, k), 0);
+ k->AddInput(
+ InsertInstruction(new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0));
PerformInductionVarAnalysis();
EXPECT_STREQ("wrap((100), ((2) * i + (100)))", GetInductionInfo(add, 0).c_str());
@@ -461,11 +444,15 @@
// k = d;
// }
BuildLoopNest(1);
- HInstruction* store1 = InsertArrayStore(induc_, 0);
- HInstruction* store2 = InsertArrayStore(tmp_, 0);
- InsertLocalStore(dum_, InsertLocalLoad(tmp_, 0), 0);
- InsertLocalStore(tmp_, InsertLocalLoad(induc_, 0), 0);
- InsertLocalStore(induc_, InsertLocalLoad(dum_, 0), 0);
+ HPhi* k = InsertLoopPhi(0, 0);
+ k->AddInput(constant0_);
+ HPhi* t = InsertLoopPhi(1, 0);
+ t->AddInput(constant100_);
+
+ HInstruction* store1 = InsertArrayStore(k, 0);
+ HInstruction* store2 = InsertArrayStore(t, 0);
+ k->AddInput(t);
+ t->AddInput(k);
PerformInductionVarAnalysis();
EXPECT_STREQ("periodic((0), (100))", GetInductionInfo(store1->InputAt(1), 0).c_str());
@@ -480,10 +467,13 @@
// k = 1 - k;
// }
BuildLoopNest(1);
- HInstruction* store = InsertArrayStore(induc_, 0);
+ HPhi* k = InsertLoopPhi(0, 0);
+ k->AddInput(constant0_);
+
+ HInstruction* store = InsertArrayStore(k, 0);
HInstruction *sub = InsertInstruction(
- new (&allocator_) HSub(Primitive::kPrimInt, constant1_, InsertLocalLoad(induc_, 0)), 0);
- InsertLocalStore(induc_, sub, 0);
+ new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k), 0);
+ k->AddInput(sub);
PerformInductionVarAnalysis();
EXPECT_STREQ("periodic((0), (1))", GetInductionInfo(store->InputAt(1), 0).c_str());
@@ -502,26 +492,24 @@
// t = - k;
// }
BuildLoopNest(1);
- InsertLocalStore(
- induc_,
- InsertInstruction(new (&allocator_)
- HSub(Primitive::kPrimInt, constant1_, InsertLocalLoad(induc_, 0)), 0), 0);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
+
+ HInstruction* k_body = InsertInstruction(
+ new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k_header), 0);
+ k_header->AddInput(k_body);
+
// Derived expressions.
HInstruction *add = InsertInstruction(
- new (&allocator_) HAdd(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
- InsertLocalStore(tmp_, add, 0);
+ new (&allocator_) HAdd(Primitive::kPrimInt, k_body, constant100_), 0);
HInstruction *sub = InsertInstruction(
- new (&allocator_) HSub(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
- InsertLocalStore(tmp_, sub, 0);
+ new (&allocator_) HSub(Primitive::kPrimInt, k_body, constant100_), 0);
HInstruction *mul = InsertInstruction(
- new (&allocator_) HMul(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0);
- InsertLocalStore(tmp_, mul, 0);
+ new (&allocator_) HMul(Primitive::kPrimInt, k_body, constant100_), 0);
HInstruction *shl = InsertInstruction(
- new (&allocator_) HShl(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0);
- InsertLocalStore(tmp_, shl, 0);
+ new (&allocator_) HShl(Primitive::kPrimInt, k_body, constant1_), 0);
HInstruction *neg = InsertInstruction(
- new (&allocator_) HNeg(Primitive::kPrimInt, InsertLocalLoad(induc_, 0)), 0);
- InsertLocalStore(tmp_, neg, 0);
+ new (&allocator_) HNeg(Primitive::kPrimInt, k_body), 0);
PerformInductionVarAnalysis();
EXPECT_STREQ("periodic(((1) + (100)), (100))", GetInductionInfo(add, 0).c_str());
@@ -543,10 +531,20 @@
// ..
// }
BuildLoopNest(10);
+
+ HPhi* k[10];
+ for (int d = 0; d < 10; d++) {
+ k[d] = InsertLoopPhi(0, d);
+ }
+
HInstruction *inc = InsertInstruction(
- new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, InsertLocalLoad(induc_, 9)), 9);
- InsertLocalStore(induc_, inc, 9);
- HInstruction* store = InsertArrayStore(induc_, 9);
+ new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, k[9]), 9);
+ HInstruction* store = InsertArrayStore(inc, 9);
+
+ for (int d = 0; d < 10; d++) {
+ k[d]->AddInput((d != 0) ? k[d - 1] : constant0_);
+ k[d]->AddInput((d != 9) ? k[d + 1] : inc);
+ }
PerformInductionVarAnalysis();
// Avoid exact phi number, since that depends on the SSA building phase.
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index ae15fcf..9566c29 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -93,7 +93,7 @@
DCHECK(induction_analysis != nullptr);
}
-void InductionVarRange::GetInductionRange(HInstruction* context,
+bool InductionVarRange::GetInductionRange(HInstruction* context,
HInstruction* instruction,
/*out*/Value* min_val,
/*out*/Value* max_val,
@@ -111,12 +111,9 @@
*min_val = GetVal(info, trip, in_body, /* is_min */ true);
*max_val = SimplifyMax(GetVal(info, trip, in_body, /* is_min */ false));
*needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip);
- } else {
- // No loop to analyze.
- *min_val = Value();
- *max_val = Value();
- *needs_finite_test = false;
+ return true;
}
+ return false; // Nothing known
}
bool InductionVarRange::RefineOuter(/*in-out*/Value* min_val, /*in-out*/Value* max_val) const {
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
index 974b8fb..3cb7b4b 100644
--- a/compiler/optimizing/induction_var_range.h
+++ b/compiler/optimizing/induction_var_range.h
@@ -60,13 +60,13 @@
* Given a context denoted by the first instruction, returns a possibly conservative
* lower and upper bound on the instruction's value in the output parameters min_val
* and max_val, respectively. The need_finite_test flag denotes if an additional finite-test
- * is needed to protect the range evaluation inside its loop.
+ * is needed to protect the range evaluation inside its loop. Returns false on failure.
*/
- void GetInductionRange(HInstruction* context,
+ bool GetInductionRange(HInstruction* context,
HInstruction* instruction,
- /*out*/Value* min_val,
- /*out*/Value* max_val,
- /*out*/bool* needs_finite_test);
+ /*out*/ Value* min_val,
+ /*out*/ Value* max_val,
+ /*out*/ bool* needs_finite_test);
/** Refines the values with induction of next outer loop. Returns true on change. */
bool RefineOuter(/*in-out*/Value* min_val, /*in-out*/Value* max_val) const;
@@ -79,8 +79,8 @@
*/
bool CanGenerateCode(HInstruction* context,
HInstruction* instruction,
- /*out*/bool* needs_finite_test,
- /*out*/bool* needs_taken_test);
+ /*out*/ bool* needs_finite_test,
+ /*out*/ bool* needs_taken_test);
/**
* Generates the actual code in the HIR for the lower and upper bound expressions on the
@@ -101,8 +101,8 @@
HInstruction* instruction,
HGraph* graph,
HBasicBlock* block,
- /*out*/HInstruction** lower,
- /*out*/HInstruction** upper);
+ /*out*/ HInstruction** lower,
+ /*out*/ HInstruction** upper);
/**
* Generates explicit taken-test for the loop in the given context. Code is generated in
@@ -113,7 +113,7 @@
void GenerateTakenTest(HInstruction* context,
HGraph* graph,
HBasicBlock* block,
- /*out*/HInstruction** taken_test);
+ /*out*/ HInstruction** taken_test);
private:
bool NeedsTripCount(HInductionVarAnalysis::InductionInfo* info) const;
@@ -168,17 +168,17 @@
HInstruction* instruction,
HGraph* graph,
HBasicBlock* block,
- /*out*/HInstruction** lower,
- /*out*/HInstruction** upper,
- /*out*/HInstruction** taken_test,
- /*out*/bool* needs_finite_test,
- /*out*/bool* needs_taken_test) const;
+ /*out*/ HInstruction** lower,
+ /*out*/ HInstruction** upper,
+ /*out*/ HInstruction** taken_test,
+ /*out*/ bool* needs_finite_test,
+ /*out*/ bool* needs_taken_test) const;
bool GenerateCode(HInductionVarAnalysis::InductionInfo* info,
HInductionVarAnalysis::InductionInfo* trip,
HGraph* graph,
HBasicBlock* block,
- /*out*/HInstruction** result,
+ /*out*/ HInstruction** result,
bool in_body,
bool is_min) const;
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
index eda9c01..55a654e 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -86,25 +86,20 @@
loop_body->AddSuccessor(loop_header);
return_block->AddSuccessor(exit_block_);
// Instructions.
- HLocal* induc = new (&allocator_) HLocal(0);
- entry_block_->AddInstruction(induc);
- loop_preheader_->AddInstruction(
- new (&allocator_) HStoreLocal(induc, graph_->GetIntConstant(lower))); // i = l
loop_preheader_->AddInstruction(new (&allocator_) HGoto());
- HInstruction* load = new (&allocator_) HLoadLocal(induc, Primitive::kPrimInt);
- loop_header->AddInstruction(load);
+ HPhi* phi = new (&allocator_) HPhi(&allocator_, 0, 0, Primitive::kPrimInt);
+ loop_header->AddPhi(phi);
+ phi->AddInput(graph_->GetIntConstant(lower)); // i = l
if (stride > 0) {
- condition_ = new (&allocator_) HLessThan(load, upper); // i < u
+ condition_ = new (&allocator_) HLessThan(phi, upper); // i < u
} else {
- condition_ = new (&allocator_) HGreaterThan(load, upper); // i > u
+ condition_ = new (&allocator_) HGreaterThan(phi, upper); // i > u
}
loop_header->AddInstruction(condition_);
loop_header->AddInstruction(new (&allocator_) HIf(condition_));
- load = new (&allocator_) HLoadLocal(induc, Primitive::kPrimInt);
- loop_body->AddInstruction(load);
- increment_ = new (&allocator_) HAdd(Primitive::kPrimInt, load, graph_->GetIntConstant(stride));
- loop_body->AddInstruction(increment_);
- loop_body->AddInstruction(new (&allocator_) HStoreLocal(induc, increment_)); // i += s
+ increment_ = new (&allocator_) HAdd(Primitive::kPrimInt, phi, graph_->GetIntConstant(stride));
+ loop_body->AddInstruction(increment_); // i += s
+ phi->AddInput(increment_);
loop_body->AddInstruction(new (&allocator_) HGoto());
return_block->AddInstruction(new (&allocator_) HReturnVoid());
exit_block_->AddInstruction(new (&allocator_) HExit());
@@ -112,7 +107,7 @@
/** Constructs SSA and performs induction variable analysis. */
void PerformInductionVarAnalysis() {
- TransformToSsa(graph_);
+ graph_->BuildDominatorTree();
iva_->Run();
}
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 9b91b53..a5acab8 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -258,8 +258,9 @@
}
if (actual_method != nullptr) {
- return TryInline(invoke_instruction, actual_method);
+ return TryInlineAndReplace(invoke_instruction, actual_method, /* do_rtp */ true);
}
+
DCHECK(!invoke_instruction->IsInvokeStaticOrDirect());
// Check if we can use an inline cache.
@@ -344,7 +345,7 @@
HInstruction* cursor = invoke_instruction->GetPrevious();
HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
- if (!TryInline(invoke_instruction, resolved_method, /* do_rtp */ false)) {
+ if (!TryInlineAndReplace(invoke_instruction, resolved_method, /* do_rtp */ false)) {
return false;
}
@@ -378,7 +379,7 @@
// Run type propagation to get the guard typed, and eventually propagate the
// type of the receiver.
- ReferenceTypePropagation rtp_fixup(graph_, handles_);
+ ReferenceTypePropagation rtp_fixup(graph_, handles_, /* is_first_run */ false);
rtp_fixup.Run();
MaybeRecordStat(kInlinedMonomorphicCall);
@@ -420,6 +421,9 @@
actual_method = new_method;
} else if (actual_method != new_method) {
// Different methods, bailout.
+ VLOG(compiler) << "Call to " << PrettyMethod(resolved_method)
+ << " from inline cache is not inlined because it resolves"
+ << " to different methods";
return false;
}
}
@@ -428,7 +432,7 @@
HInstruction* cursor = invoke_instruction->GetPrevious();
HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
- if (!TryInline(invoke_instruction, actual_method, /* do_rtp */ false)) {
+ if (!TryInlineAndReplace(invoke_instruction, actual_method, /* do_rtp */ false)) {
return false;
}
@@ -474,7 +478,7 @@
deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
// Run type propagation to get the guard typed.
- ReferenceTypePropagation rtp_fixup(graph_, handles_);
+ ReferenceTypePropagation rtp_fixup(graph_, handles_, /* is_first_run */ false);
rtp_fixup.Run();
MaybeRecordStat(kInlinedPolymorphicCall);
@@ -482,14 +486,29 @@
return true;
}
-bool HInliner::TryInline(HInvoke* invoke_instruction, ArtMethod* method, bool do_rtp) {
+bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, ArtMethod* method, bool do_rtp) {
+ HInstruction* return_replacement = nullptr;
+ if (!TryBuildAndInline(invoke_instruction, method, &return_replacement)) {
+ return false;
+ }
+ if (return_replacement != nullptr) {
+ invoke_instruction->ReplaceWith(return_replacement);
+ }
+ invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction);
+ FixUpReturnReferenceType(invoke_instruction, method, return_replacement, do_rtp);
+ return true;
+}
+
+bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction,
+ ArtMethod* method,
+ HInstruction** return_replacement) {
const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
// Check whether we're allowed to inline. The outermost compilation unit is the relevant
// dex file here (though the transitivity of an inline chain would allow checking the calller).
if (!compiler_driver_->MayInline(method->GetDexFile(),
outer_compilation_unit_.GetDexFile())) {
- if (TryPatternSubstitution(invoke_instruction, method, do_rtp)) {
+ if (TryPatternSubstitution(invoke_instruction, method, return_replacement)) {
VLOG(compiler) << "Successfully replaced pattern of invoke " << PrettyMethod(method);
MaybeRecordStat(kReplacedInvokeWithSimplePattern);
return true;
@@ -556,7 +575,7 @@
return false;
}
- if (!TryBuildAndInline(method, invoke_instruction, same_dex_file, do_rtp)) {
+ if (!TryBuildAndInlineHelper(invoke_instruction, method, same_dex_file, return_replacement)) {
return false;
}
@@ -583,27 +602,27 @@
// Try to recognize known simple patterns and replace invoke call with appropriate instructions.
bool HInliner::TryPatternSubstitution(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
- bool do_rtp) {
+ HInstruction** return_replacement) {
InlineMethod inline_method;
if (!InlineMethodAnalyser::AnalyseMethodCode(resolved_method, &inline_method)) {
return false;
}
- HInstruction* return_replacement = nullptr;
switch (inline_method.opcode) {
case kInlineOpNop:
DCHECK_EQ(invoke_instruction->GetType(), Primitive::kPrimVoid);
+ *return_replacement = nullptr;
break;
case kInlineOpReturnArg:
- return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction,
- inline_method.d.return_data.arg);
+ *return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction,
+ inline_method.d.return_data.arg);
break;
case kInlineOpNonWideConst:
if (resolved_method->GetShorty()[0] == 'L') {
DCHECK_EQ(inline_method.d.data, 0u);
- return_replacement = graph_->GetNullConstant();
+ *return_replacement = graph_->GetNullConstant();
} else {
- return_replacement = graph_->GetIntConstant(static_cast<int32_t>(inline_method.d.data));
+ *return_replacement = graph_->GetIntConstant(static_cast<int32_t>(inline_method.d.data));
}
break;
case kInlineOpIGet: {
@@ -612,12 +631,13 @@
// TODO: Needs null check.
return false;
}
+ Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, data.object_arg);
- HInstanceFieldGet* iget = CreateInstanceFieldGet(resolved_method, data.field_idx, obj);
+ HInstanceFieldGet* iget = CreateInstanceFieldGet(dex_cache, data.field_idx, obj);
DCHECK_EQ(iget->GetFieldOffset().Uint32Value(), data.field_offset);
DCHECK_EQ(iget->IsVolatile() ? 1u : 0u, data.is_volatile);
invoke_instruction->GetBlock()->InsertInstructionBefore(iget, invoke_instruction);
- return_replacement = iget;
+ *return_replacement = iget;
break;
}
case kInlineOpIPut: {
@@ -626,37 +646,84 @@
// TODO: Needs null check.
return false;
}
+ Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, data.object_arg);
HInstruction* value = GetInvokeInputForArgVRegIndex(invoke_instruction, data.src_arg);
- HInstanceFieldSet* iput = CreateInstanceFieldSet(resolved_method, data.field_idx, obj, value);
+ HInstanceFieldSet* iput = CreateInstanceFieldSet(dex_cache, data.field_idx, obj, value);
DCHECK_EQ(iput->GetFieldOffset().Uint32Value(), data.field_offset);
DCHECK_EQ(iput->IsVolatile() ? 1u : 0u, data.is_volatile);
invoke_instruction->GetBlock()->InsertInstructionBefore(iput, invoke_instruction);
if (data.return_arg_plus1 != 0u) {
size_t return_arg = data.return_arg_plus1 - 1u;
- return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction, return_arg);
+ *return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction, return_arg);
}
break;
}
+ case kInlineOpConstructor: {
+ const InlineConstructorData& data = inline_method.d.constructor_data;
+ // Get the indexes to arrays for easier processing.
+ uint16_t iput_field_indexes[] = {
+ data.iput0_field_index, data.iput1_field_index, data.iput2_field_index
+ };
+ uint16_t iput_args[] = { data.iput0_arg, data.iput1_arg, data.iput2_arg };
+ static_assert(arraysize(iput_args) == arraysize(iput_field_indexes), "Size mismatch");
+ // Count valid field indexes.
+ size_t number_of_iputs = 0u;
+ while (number_of_iputs != arraysize(iput_field_indexes) &&
+ iput_field_indexes[number_of_iputs] != DexFile::kDexNoIndex16) {
+ // Check that there are no duplicate valid field indexes.
+ DCHECK_EQ(0, std::count(iput_field_indexes + number_of_iputs + 1,
+ iput_field_indexes + arraysize(iput_field_indexes),
+ iput_field_indexes[number_of_iputs]));
+ ++number_of_iputs;
+ }
+ // Check that there are no valid field indexes in the rest of the array.
+ DCHECK_EQ(0, std::count_if(iput_field_indexes + number_of_iputs,
+ iput_field_indexes + arraysize(iput_field_indexes),
+ [](uint16_t index) { return index != DexFile::kDexNoIndex16; }));
+
+ // Create HInstanceFieldSet for each IPUT that stores non-zero data.
+ Handle<mirror::DexCache> dex_cache;
+ HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, /* this */ 0u);
+ bool needs_constructor_barrier = false;
+ for (size_t i = 0; i != number_of_iputs; ++i) {
+ HInstruction* value = GetInvokeInputForArgVRegIndex(invoke_instruction, iput_args[i]);
+ if (!value->IsConstant() ||
+ (!value->AsConstant()->IsZero() && !value->IsNullConstant())) {
+ if (dex_cache.GetReference() == nullptr) {
+ dex_cache = handles_->NewHandle(resolved_method->GetDexCache());
+ }
+ uint16_t field_index = iput_field_indexes[i];
+ HInstanceFieldSet* iput = CreateInstanceFieldSet(dex_cache, field_index, obj, value);
+ invoke_instruction->GetBlock()->InsertInstructionBefore(iput, invoke_instruction);
+
+ // Check whether the field is final. If it is, we need to add a barrier.
+ size_t pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
+ ArtField* resolved_field = dex_cache->GetResolvedField(field_index, pointer_size);
+ DCHECK(resolved_field != nullptr);
+ if (resolved_field->IsFinal()) {
+ needs_constructor_barrier = true;
+ }
+ }
+ }
+ if (needs_constructor_barrier) {
+ HMemoryBarrier* barrier = new (graph_->GetArena()) HMemoryBarrier(kStoreStore, kNoDexPc);
+ invoke_instruction->GetBlock()->InsertInstructionBefore(barrier, invoke_instruction);
+ }
+ *return_replacement = nullptr;
+ break;
+ }
default:
LOG(FATAL) << "UNREACHABLE";
UNREACHABLE();
}
-
- if (return_replacement != nullptr) {
- invoke_instruction->ReplaceWith(return_replacement);
- }
- invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction);
-
- FixUpReturnReferenceType(resolved_method, invoke_instruction, return_replacement, do_rtp);
return true;
}
-HInstanceFieldGet* HInliner::CreateInstanceFieldGet(ArtMethod* resolved_method,
+HInstanceFieldGet* HInliner::CreateInstanceFieldGet(Handle<mirror::DexCache> dex_cache,
uint32_t field_index,
HInstruction* obj)
SHARED_REQUIRES(Locks::mutator_lock_) {
- Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
size_t pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
ArtField* resolved_field = dex_cache->GetResolvedField(field_index, pointer_size);
DCHECK(resolved_field != nullptr);
@@ -667,24 +734,23 @@
resolved_field->IsVolatile(),
field_index,
resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
- *resolved_method->GetDexFile(),
+ *dex_cache->GetDexFile(),
dex_cache,
// Read barrier generates a runtime call in slow path and we need a valid
// dex pc for the associated stack map. 0 is bogus but valid. Bug: 26854537.
/* dex_pc */ 0);
if (iget->GetType() == Primitive::kPrimNot) {
- ReferenceTypePropagation rtp(graph_, handles_);
+ ReferenceTypePropagation rtp(graph_, handles_, /* is_first_run */ false);
rtp.Visit(iget);
}
return iget;
}
-HInstanceFieldSet* HInliner::CreateInstanceFieldSet(ArtMethod* resolved_method,
+HInstanceFieldSet* HInliner::CreateInstanceFieldSet(Handle<mirror::DexCache> dex_cache,
uint32_t field_index,
HInstruction* obj,
HInstruction* value)
SHARED_REQUIRES(Locks::mutator_lock_) {
- Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
size_t pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
ArtField* resolved_field = dex_cache->GetResolvedField(field_index, pointer_size);
DCHECK(resolved_field != nullptr);
@@ -696,17 +762,18 @@
resolved_field->IsVolatile(),
field_index,
resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
- *resolved_method->GetDexFile(),
+ *dex_cache->GetDexFile(),
dex_cache,
// Read barrier generates a runtime call in slow path and we need a valid
// dex pc for the associated stack map. 0 is bogus but valid. Bug: 26854537.
/* dex_pc */ 0);
return iput;
}
-bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
- HInvoke* invoke_instruction,
- bool same_dex_file,
- bool do_rtp) {
+
+bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
+ bool same_dex_file,
+ HInstruction** return_replacement) {
ScopedObjectAccess soa(Thread::Current());
const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
const DexFile& callee_dex_file = *resolved_method->GetDexFile();
@@ -758,6 +825,7 @@
compiler_driver_->GetInstructionSet(),
invoke_type,
graph_->IsDebuggable(),
+ /* osr */ false,
graph_->GetCurrentInstructionId());
callee_graph->SetArtMethod(resolved_method);
@@ -771,7 +839,7 @@
resolved_method->GetQuickenedInfo(),
dex_cache);
- if (!builder.BuildGraph(*code_item)) {
+ if (builder.BuildGraph(*code_item, handles_) != kAnalysisSuccess) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
<< " could not be built, so cannot be inlined";
return false;
@@ -784,12 +852,6 @@
return false;
}
- if (callee_graph->TryBuildingSsa(handles_) != kAnalysisSuccess) {
- VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
- << " could not be transformed to SSA";
- return false;
- }
-
size_t parameter_index = 0;
for (HInstructionIterator instructions(callee_graph->GetEntryBlock()->GetInstructions());
!instructions.Done();
@@ -934,12 +996,18 @@
if (current->IsNewInstance() &&
(current->AsNewInstance()->GetEntrypoint() == kQuickAllocObjectWithAccessCheck)) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
+ << " could not be inlined because it is using an entrypoint"
+ << " with access checks";
// Allocation entrypoint does not handle inlined frames.
return false;
}
if (current->IsNewArray() &&
(current->AsNewArray()->GetEntrypoint() == kQuickAllocArrayWithAccessCheck)) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
+ << " could not be inlined because it is using an entrypoint"
+ << " with access checks";
// Allocation entrypoint does not handle inlined frames.
return false;
}
@@ -949,22 +1017,21 @@
current->IsUnresolvedStaticFieldSet() ||
current->IsUnresolvedInstanceFieldSet()) {
// Entrypoint for unresolved fields does not handle inlined frames.
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
+ << " could not be inlined because it is using an unresolved"
+ << " entrypoint";
return false;
}
}
}
number_of_inlined_instructions_ += number_of_instructions;
- HInstruction* return_replacement = callee_graph->InlineInto(graph_, invoke_instruction);
- if (return_replacement != nullptr) {
- DCHECK_EQ(graph_, return_replacement->GetBlock()->GetGraph());
- }
- FixUpReturnReferenceType(resolved_method, invoke_instruction, return_replacement, do_rtp);
+ *return_replacement = callee_graph->InlineInto(graph_, invoke_instruction);
return true;
}
-void HInliner::FixUpReturnReferenceType(ArtMethod* resolved_method,
- HInvoke* invoke_instruction,
+void HInliner::FixUpReturnReferenceType(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
HInstruction* return_replacement,
bool do_rtp) {
// Check the integrity of reference types and run another type propagation if needed.
@@ -990,13 +1057,13 @@
if (invoke_rti.IsStrictSupertypeOf(return_rti)
|| (return_rti.IsExact() && !invoke_rti.IsExact())
|| !return_replacement->CanBeNull()) {
- ReferenceTypePropagation(graph_, handles_).Run();
+ ReferenceTypePropagation(graph_, handles_, /* is_first_run */ false).Run();
}
}
} else if (return_replacement->IsInstanceOf()) {
if (do_rtp) {
// Inlining InstanceOf into an If may put a tighter bound on reference types.
- ReferenceTypePropagation(graph_, handles_).Run();
+ ReferenceTypePropagation(graph_, handles_, /* is_first_run */ false).Run();
}
}
}
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 0127d55..9dd9bf5 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -61,20 +61,33 @@
bool TryInline(HInvoke* invoke_instruction);
// Try to inline `resolved_method` in place of `invoke_instruction`. `do_rtp` is whether
- // reference type propagation can run after the inlining.
- bool TryInline(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp = true)
+ // reference type propagation can run after the inlining. If the inlining is successful, this
+ // method will replace and remove the `invoke_instruction`.
+ bool TryInlineAndReplace(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp)
SHARED_REQUIRES(Locks::mutator_lock_);
+ bool TryBuildAndInline(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
+ HInstruction** return_replacement)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ bool TryBuildAndInlineHelper(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
+ bool same_dex_file,
+ HInstruction** return_replacement);
+
// Try to recognize known simple patterns and replace invoke call with appropriate instructions.
- bool TryPatternSubstitution(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp)
+ bool TryPatternSubstitution(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
+ HInstruction** return_replacement)
SHARED_REQUIRES(Locks::mutator_lock_);
// Create a new HInstanceFieldGet.
- HInstanceFieldGet* CreateInstanceFieldGet(ArtMethod* resolved_method,
+ HInstanceFieldGet* CreateInstanceFieldGet(Handle<mirror::DexCache> dex_cache,
uint32_t field_index,
HInstruction* obj);
// Create a new HInstanceFieldSet.
- HInstanceFieldSet* CreateInstanceFieldSet(ArtMethod* resolved_method,
+ HInstanceFieldSet* CreateInstanceFieldSet(Handle<mirror::DexCache> dex_cache,
uint32_t field_index,
HInstruction* obj,
HInstruction* value);
@@ -94,18 +107,13 @@
const InlineCache& ic)
SHARED_REQUIRES(Locks::mutator_lock_);
- bool TryBuildAndInline(ArtMethod* resolved_method,
- HInvoke* invoke_instruction,
- bool same_dex_file,
- bool do_rtp = true);
-
HInstanceFieldGet* BuildGetReceiverClass(ClassLinker* class_linker,
HInstruction* receiver,
uint32_t dex_pc) const
SHARED_REQUIRES(Locks::mutator_lock_);
- void FixUpReturnReferenceType(ArtMethod* resolved_method,
- HInvoke* invoke_instruction,
+ void FixUpReturnReferenceType(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
HInstruction* return_replacement,
bool do_rtp)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index c1e3863..a48d06f 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -91,6 +91,7 @@
void SimplifyRotate(HInvoke* invoke, bool is_left);
void SimplifySystemArrayCopy(HInvoke* invoke);
void SimplifyStringEquals(HInvoke* invoke);
+ void SimplifyCompare(HInvoke* invoke, bool has_zero_op);
OptimizingCompilerStats* stats_;
bool simplification_occurred_ = false;
@@ -176,8 +177,8 @@
// We can apply De Morgan's laws if both inputs are Not's and are only used
// by `op`.
- if (left->IsNot() &&
- right->IsNot() &&
+ if (((left->IsNot() && right->IsNot()) ||
+ (left->IsBooleanNot() && right->IsBooleanNot())) &&
left->HasOnlyOneNonEnvironmentUse() &&
right->HasOnlyOneNonEnvironmentUse()) {
// Replace code looking like
@@ -187,8 +188,8 @@
// with
// OR or, a, b (respectively AND)
// NOT dest, or
- HInstruction* src_left = left->AsNot()->GetInput();
- HInstruction* src_right = right->AsNot()->GetInput();
+ HInstruction* src_left = left->InputAt(0);
+ HInstruction* src_right = right->InputAt(0);
uint32_t dex_pc = op->GetDexPc();
// Remove the negations on the inputs.
@@ -204,7 +205,12 @@
} else {
hbin = new (GetGraph()->GetArena()) HAnd(type, src_left, src_right, dex_pc);
}
- HNot* hnot = new (GetGraph()->GetArena()) HNot(type, hbin, dex_pc);
+ HInstruction* hnot;
+ if (left->IsBooleanNot()) {
+ hnot = new (GetGraph()->GetArena()) HBooleanNot(hbin, dex_pc);
+ } else {
+ hnot = new (GetGraph()->GetArena()) HNot(type, hbin, dex_pc);
+ }
op->GetBlock()->InsertInstructionBefore(hbin, op);
op->GetBlock()->ReplaceAndRemoveInstructionWith(op, hnot);
@@ -751,11 +757,97 @@
}
}
+static bool IsTypeConversionImplicit(Primitive::Type input_type, Primitive::Type result_type) {
+ // Besides conversion to the same type, widening integral conversions are implicit,
+ // excluding conversions to long and the byte->char conversion where we need to
+ // clear the high 16 bits of the 32-bit sign-extended representation of byte.
+ return result_type == input_type ||
+ (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimByte) ||
+ (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimShort) ||
+ (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimChar) ||
+ (result_type == Primitive::kPrimShort && input_type == Primitive::kPrimByte);
+}
+
+static bool IsTypeConversionLossless(Primitive::Type input_type, Primitive::Type result_type) {
+ // The conversion to a larger type is loss-less with the exception of two cases,
+ // - conversion to char, the only unsigned type, where we may lose some bits, and
+ // - conversion from float to long, the only FP to integral conversion with smaller FP type.
+ // For integral to FP conversions this holds because the FP mantissa is large enough.
+ DCHECK_NE(input_type, result_type);
+ return Primitive::ComponentSize(result_type) > Primitive::ComponentSize(input_type) &&
+ result_type != Primitive::kPrimChar &&
+ !(result_type == Primitive::kPrimLong && input_type == Primitive::kPrimFloat);
+}
+
void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruction) {
- if (instruction->GetResultType() == instruction->GetInputType()) {
- // Remove the instruction if it's converting to the same type.
- instruction->ReplaceWith(instruction->GetInput());
+ HInstruction* input = instruction->GetInput();
+ Primitive::Type input_type = input->GetType();
+ Primitive::Type result_type = instruction->GetResultType();
+ if (IsTypeConversionImplicit(input_type, result_type)) {
+ // Remove the implicit conversion; this includes conversion to the same type.
+ instruction->ReplaceWith(input);
instruction->GetBlock()->RemoveInstruction(instruction);
+ RecordSimplification();
+ return;
+ }
+
+ if (input->IsTypeConversion()) {
+ HTypeConversion* input_conversion = input->AsTypeConversion();
+ HInstruction* original_input = input_conversion->GetInput();
+ Primitive::Type original_type = original_input->GetType();
+
+ // When the first conversion is lossless, a direct conversion from the original type
+ // to the final type yields the same result, even for a lossy second conversion, for
+ // example float->double->int or int->double->float.
+ bool is_first_conversion_lossless = IsTypeConversionLossless(original_type, input_type);
+
+ // For integral conversions, see if the first conversion loses only bits that the second
+ // doesn't need, i.e. the final type is no wider than the intermediate. If so, direct
+ // conversion yields the same result, for example long->int->short or int->char->short.
+ bool integral_conversions_with_non_widening_second =
+ Primitive::IsIntegralType(input_type) &&
+ Primitive::IsIntegralType(original_type) &&
+ Primitive::IsIntegralType(result_type) &&
+ Primitive::ComponentSize(result_type) <= Primitive::ComponentSize(input_type);
+
+ if (is_first_conversion_lossless || integral_conversions_with_non_widening_second) {
+ // If the merged conversion is implicit, do the simplification unconditionally.
+ if (IsTypeConversionImplicit(original_type, result_type)) {
+ instruction->ReplaceWith(original_input);
+ instruction->GetBlock()->RemoveInstruction(instruction);
+ if (!input_conversion->HasUses()) {
+ // Don't wait for DCE.
+ input_conversion->GetBlock()->RemoveInstruction(input_conversion);
+ }
+ RecordSimplification();
+ return;
+ }
+ // Otherwise simplify only if the first conversion has no other use.
+ if (input_conversion->HasOnlyOneNonEnvironmentUse()) {
+ input_conversion->ReplaceWith(original_input);
+ input_conversion->GetBlock()->RemoveInstruction(input_conversion);
+ RecordSimplification();
+ return;
+ }
+ }
+ } else if (input->IsAnd() &&
+ Primitive::IsIntegralType(result_type) &&
+ input->HasOnlyOneNonEnvironmentUse()) {
+ DCHECK(Primitive::IsIntegralType(input_type));
+ HAnd* input_and = input->AsAnd();
+ HConstant* constant = input_and->GetConstantRight();
+ if (constant != nullptr) {
+ int64_t value = Int64FromConstant(constant);
+ DCHECK_NE(value, -1); // "& -1" would have been optimized away in VisitAnd().
+ size_t trailing_ones = CTZ(~static_cast<uint64_t>(value));
+ if (trailing_ones >= kBitsPerByte * Primitive::ComponentSize(result_type)) {
+ // The `HAnd` is useless, for example in `(byte) (x & 0xff)`, get rid of it.
+ input_and->ReplaceWith(input_and->GetLeastConstantLeft());
+ input_and->GetBlock()->RemoveInstruction(input_and);
+ RecordSimplification();
+ return;
+ }
+ }
}
}
@@ -1308,8 +1400,8 @@
HInstruction* left = instruction->GetLeft();
HInstruction* right = instruction->GetRight();
- if (left->IsNot() &&
- right->IsNot() &&
+ if (((left->IsNot() && right->IsNot()) ||
+ (left->IsBooleanNot() && right->IsBooleanNot())) &&
left->HasOnlyOneNonEnvironmentUse() &&
right->HasOnlyOneNonEnvironmentUse()) {
// Replace code looking like
@@ -1318,8 +1410,8 @@
// XOR dst, nota, notb
// with
// XOR dst, a, b
- instruction->ReplaceInput(left->AsNot()->GetInput(), 0);
- instruction->ReplaceInput(right->AsNot()->GetInput(), 1);
+ instruction->ReplaceInput(left->InputAt(0), 0);
+ instruction->ReplaceInput(right->InputAt(0), 1);
left->GetBlock()->RemoveInstruction(left);
right->GetBlock()->RemoveInstruction(right);
RecordSimplification();
@@ -1441,6 +1533,24 @@
}
}
+void InstructionSimplifierVisitor::SimplifyCompare(HInvoke* invoke, bool is_signum) {
+ DCHECK(invoke->IsInvokeStaticOrDirect());
+ uint32_t dex_pc = invoke->GetDexPc();
+ HInstruction* left = invoke->InputAt(0);
+ HInstruction* right;
+ Primitive::Type type = left->GetType();
+ if (!is_signum) {
+ right = invoke->InputAt(1);
+ } else if (type == Primitive::kPrimLong) {
+ right = GetGraph()->GetLongConstant(0);
+ } else {
+ right = GetGraph()->GetIntConstant(0);
+ }
+ HCompare* compare = new (GetGraph()->GetArena())
+ HCompare(type, left, right, ComparisonBias::kNoBias, dex_pc);
+ invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, compare);
+}
+
void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) {
if (instruction->GetIntrinsic() == Intrinsics::kStringEquals) {
SimplifyStringEquals(instruction);
@@ -1452,6 +1562,12 @@
} else if (instruction->GetIntrinsic() == Intrinsics::kIntegerRotateLeft ||
instruction->GetIntrinsic() == Intrinsics::kLongRotateLeft) {
SimplifyRotate(instruction, true);
+ } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerCompare ||
+ instruction->GetIntrinsic() == Intrinsics::kLongCompare) {
+ SimplifyCompare(instruction, /* is_signum */ false);
+ } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerSignum ||
+ instruction->GetIntrinsic() == Intrinsics::kLongSignum) {
+ SimplifyCompare(instruction, /* is_signum */ true);
}
}
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index a6be324..db39bc8 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -481,6 +481,7 @@
case kInlineOpNonWideConst:
case kInlineOpIGet:
case kInlineOpIPut:
+ case kInlineOpConstructor:
return Intrinsics::kNone;
// String init cases, not intrinsics.
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index e8912b3..00a158b 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -1580,6 +1580,251 @@
__ Bind(slow_path->GetExitLabel());
}
+static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ // If the graph is debuggable, all callee-saved floating-point registers are blocked by
+ // the code generator. Furthermore, the register allocator creates fixed live intervals
+ // for all caller-saved registers because we are doing a function call. As a result, if
+ // the input and output locations are unallocated, the register allocator runs out of
+ // registers and fails; however, a debuggable graph is not the common case.
+ if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
+ return;
+ }
+
+ DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
+ DCHECK_EQ(invoke->InputAt(0)->GetType(), Primitive::kPrimDouble);
+ DCHECK_EQ(invoke->GetType(), Primitive::kPrimDouble);
+
+ LocationSummary* const locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kCall,
+ kIntrinsified);
+ const InvokeRuntimeCallingConvention calling_convention;
+
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ // Native code uses the soft float ABI.
+ locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+ locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+}
+
+static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ // If the graph is debuggable, all callee-saved floating-point registers are blocked by
+ // the code generator. Furthermore, the register allocator creates fixed live intervals
+ // for all caller-saved registers because we are doing a function call. As a result, if
+ // the input and output locations are unallocated, the register allocator runs out of
+ // registers and fails; however, a debuggable graph is not the common case.
+ if (invoke->GetBlock()->GetGraph()->IsDebuggable()) {
+ return;
+ }
+
+ DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
+ DCHECK_EQ(invoke->InputAt(0)->GetType(), Primitive::kPrimDouble);
+ DCHECK_EQ(invoke->InputAt(1)->GetType(), Primitive::kPrimDouble);
+ DCHECK_EQ(invoke->GetType(), Primitive::kPrimDouble);
+
+ LocationSummary* const locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kCall,
+ kIntrinsified);
+ const InvokeRuntimeCallingConvention calling_convention;
+
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ // Native code uses the soft float ABI.
+ locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+ locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+ locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+ locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(3)));
+}
+
+static void GenFPToFPCall(HInvoke* invoke,
+ ArmAssembler* assembler,
+ CodeGeneratorARM* codegen,
+ QuickEntrypointEnum entry) {
+ LocationSummary* const locations = invoke->GetLocations();
+ const InvokeRuntimeCallingConvention calling_convention;
+
+ DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
+ DCHECK(locations->WillCall() && locations->Intrinsified());
+ DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(0)));
+ DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(1)));
+
+ __ LoadFromOffset(kLoadWord, LR, TR, GetThreadOffset<kArmWordSize>(entry).Int32Value());
+ // Native code uses the soft float ABI.
+ __ vmovrrd(calling_convention.GetRegisterAt(0),
+ calling_convention.GetRegisterAt(1),
+ FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
+ __ blx(LR);
+ codegen->RecordPcInfo(invoke, invoke->GetDexPc());
+ __ vmovdrr(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
+ calling_convention.GetRegisterAt(0),
+ calling_convention.GetRegisterAt(1));
+}
+
+static void GenFPFPToFPCall(HInvoke* invoke,
+ ArmAssembler* assembler,
+ CodeGeneratorARM* codegen,
+ QuickEntrypointEnum entry) {
+ LocationSummary* const locations = invoke->GetLocations();
+ const InvokeRuntimeCallingConvention calling_convention;
+
+ DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
+ DCHECK(locations->WillCall() && locations->Intrinsified());
+ DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(0)));
+ DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(1)));
+ DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(2)));
+ DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(calling_convention.GetRegisterAt(3)));
+
+ __ LoadFromOffset(kLoadWord, LR, TR, GetThreadOffset<kArmWordSize>(entry).Int32Value());
+ // Native code uses the soft float ABI.
+ __ vmovrrd(calling_convention.GetRegisterAt(0),
+ calling_convention.GetRegisterAt(1),
+ FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
+ __ vmovrrd(calling_convention.GetRegisterAt(2),
+ calling_convention.GetRegisterAt(3),
+ FromLowSToD(locations->InAt(1).AsFpuRegisterPairLow<SRegister>()));
+ __ blx(LR);
+ codegen->RecordPcInfo(invoke, invoke->GetDexPc());
+ __ vmovdrr(FromLowSToD(locations->Out().AsFpuRegisterPairLow<SRegister>()),
+ calling_convention.GetRegisterAt(0),
+ calling_convention.GetRegisterAt(1));
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathCos(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathCos(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCos);
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathSin(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathSin(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSin);
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathAcos(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathAcos(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAcos);
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathAsin(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathAsin(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAsin);
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathAtan(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathAtan(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan);
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathCbrt(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathCbrt(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCbrt);
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathCosh(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathCosh(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickCosh);
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathExp(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathExp(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExp);
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathExpm1(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathExpm1(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickExpm1);
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathLog(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathLog(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog);
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathLog10(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathLog10(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickLog10);
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathSinh(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathSinh(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickSinh);
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathTan(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathTan(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTan);
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathTanh(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathTanh(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetAssembler(), codegen_, kQuickTanh);
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathAtan2(HInvoke* invoke) {
+ CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathAtan2(HInvoke* invoke) {
+ GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickAtan2);
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathHypot(HInvoke* invoke) {
+ CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathHypot(HInvoke* invoke) {
+ GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickHypot);
+}
+
+void IntrinsicLocationsBuilderARM::VisitMathNextAfter(HInvoke* invoke) {
+ CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitMathNextAfter(HInvoke* invoke) {
+ GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickNextAfter);
+}
+
// Unimplemented intrinsics.
#define UNIMPLEMENTED_INTRINSIC(Name) \
@@ -1610,44 +1855,27 @@
UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
-UNIMPLEMENTED_INTRINSIC(MathCos)
-UNIMPLEMENTED_INTRINSIC(MathSin)
-UNIMPLEMENTED_INTRINSIC(MathAcos)
-UNIMPLEMENTED_INTRINSIC(MathAsin)
-UNIMPLEMENTED_INTRINSIC(MathAtan)
-UNIMPLEMENTED_INTRINSIC(MathAtan2)
-UNIMPLEMENTED_INTRINSIC(MathCbrt)
-UNIMPLEMENTED_INTRINSIC(MathCosh)
-UNIMPLEMENTED_INTRINSIC(MathExp)
-UNIMPLEMENTED_INTRINSIC(MathExpm1)
-UNIMPLEMENTED_INTRINSIC(MathHypot)
-UNIMPLEMENTED_INTRINSIC(MathLog)
-UNIMPLEMENTED_INTRINSIC(MathLog10)
-UNIMPLEMENTED_INTRINSIC(MathNextAfter)
-UNIMPLEMENTED_INTRINSIC(MathSinh)
-UNIMPLEMENTED_INTRINSIC(MathTan)
-UNIMPLEMENTED_INTRINSIC(MathTanh)
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
-UNIMPLEMENTED_INTRINSIC(IntegerCompare)
-UNIMPLEMENTED_INTRINSIC(LongCompare)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
+
+// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
+UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
+UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
+UNIMPLEMENTED_INTRINSIC(LongRotateRight)
+UNIMPLEMENTED_INTRINSIC(IntegerCompare)
+UNIMPLEMENTED_INTRINSIC(LongCompare)
UNIMPLEMENTED_INTRINSIC(IntegerSignum)
UNIMPLEMENTED_INTRINSIC(LongSignum)
-// Rotate operations are handled as HRor instructions.
-UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
-UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
-UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
-UNIMPLEMENTED_INTRINSIC(LongRotateRight)
-
#undef UNIMPLEMENTED_INTRINSIC
#undef __
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 5dce83a..4140d94 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -284,36 +284,6 @@
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
-static void GenCompare(LocationSummary* locations, bool is_long, vixl::MacroAssembler* masm) {
- Location op1 = locations->InAt(0);
- Location op2 = locations->InAt(1);
- Location out = locations->Out();
-
- Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1);
- Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2);
- Register out_reg = WRegisterFrom(out);
-
- __ Cmp(op1_reg, op2_reg);
- __ Cset(out_reg, gt); // out == +1 if GT or 0 otherwise
- __ Cinv(out_reg, out_reg, lt); // out == -1 if LT or unchanged otherwise
-}
-
-void IntrinsicLocationsBuilderARM64::VisitIntegerCompare(HInvoke* invoke) {
- CreateIntIntToIntLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM64::VisitIntegerCompare(HInvoke* invoke) {
- GenCompare(invoke->GetLocations(), /* is_long */ false, GetVIXLAssembler());
-}
-
-void IntrinsicLocationsBuilderARM64::VisitLongCompare(HInvoke* invoke) {
- CreateIntIntToIntLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM64::VisitLongCompare(HInvoke* invoke) {
- GenCompare(invoke->GetLocations(), /* is_long */ true, GetVIXLAssembler());
-}
-
static void GenNumberOfLeadingZeros(LocationSummary* locations,
Primitive::Type type,
vixl::MacroAssembler* masm) {
@@ -1456,34 +1426,6 @@
__ Bind(slow_path->GetExitLabel());
}
-static void GenSignum(LocationSummary* locations, bool is_long, vixl::MacroAssembler* masm) {
- Location op1 = locations->InAt(0);
- Location out = locations->Out();
-
- Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1);
- Register out_reg = WRegisterFrom(out);
-
- __ Cmp(op1_reg, 0);
- __ Cset(out_reg, gt); // out == +1 if GT or 0 otherwise
- __ Cinv(out_reg, out_reg, lt); // out == -1 if LT or unchanged otherwise
-}
-
-void IntrinsicLocationsBuilderARM64::VisitIntegerSignum(HInvoke* invoke) {
- CreateIntToIntLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM64::VisitIntegerSignum(HInvoke* invoke) {
- GenSignum(invoke->GetLocations(), /* is_long */ false, GetVIXLAssembler());
-}
-
-void IntrinsicLocationsBuilderARM64::VisitLongSignum(HInvoke* invoke) {
- CreateIntToIntLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorARM64::VisitLongSignum(HInvoke* invoke) {
- GenSignum(invoke->GetLocations(), /* is_long */ true, GetVIXLAssembler());
-}
-
static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
@@ -1684,11 +1626,15 @@
UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
-// Rotate operations are handled as HRor instructions.
+// Handled as HIR instructions.
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
-UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
+UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
UNIMPLEMENTED_INTRINSIC(LongRotateRight)
+UNIMPLEMENTED_INTRINSIC(IntegerCompare)
+UNIMPLEMENTED_INTRINSIC(LongCompare)
+UNIMPLEMENTED_INTRINSIC(IntegerSignum)
+UNIMPLEMENTED_INTRINSIC(LongSignum)
#undef UNIMPLEMENTED_INTRINSIC
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 0d9cf09..2294713 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1019,12 +1019,14 @@
UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
-UNIMPLEMENTED_INTRINSIC(IntegerCompare)
-UNIMPLEMENTED_INTRINSIC(LongCompare)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
+
+// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(IntegerCompare)
+UNIMPLEMENTED_INTRINSIC(LongCompare)
UNIMPLEMENTED_INTRINSIC(IntegerSignum)
UNIMPLEMENTED_INTRINSIC(LongSignum)
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index f681d1f..ac28503 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1767,12 +1767,14 @@
UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
-UNIMPLEMENTED_INTRINSIC(IntegerCompare)
-UNIMPLEMENTED_INTRINSIC(LongCompare)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
+
+// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(IntegerCompare)
+UNIMPLEMENTED_INTRINSIC(LongCompare)
UNIMPLEMENTED_INTRINSIC(IntegerSignum)
UNIMPLEMENTED_INTRINSIC(LongSignum)
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index acc40bc..22cefe8 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -37,10 +37,12 @@
static constexpr int kDoubleNaNHigh = 0x7FF80000;
static constexpr int kDoubleNaNLow = 0x00000000;
-static constexpr int kFloatNaN = 0x7FC00000;
+static constexpr int64_t kDoubleNaN = INT64_C(0x7FF8000000000000);
+static constexpr int32_t kFloatNaN = INT32_C(0x7FC00000);
IntrinsicLocationsBuilderX86::IntrinsicLocationsBuilderX86(CodeGeneratorX86* codegen)
- : arena_(codegen->GetGraph()->GetArena()), codegen_(codegen) {
+ : arena_(codegen->GetGraph()->GetArena()),
+ codegen_(codegen) {
}
@@ -256,15 +258,38 @@
LocationSummary::kNoCall,
kIntrinsified);
locations->SetInAt(0, Location::RequiresFpuRegister());
- // TODO: Allow x86 to work with memory. This requires assembler support, see below.
- // locations->SetInAt(0, Location::Any()); // X86 can work on memory directly.
locations->SetOut(Location::SameAsFirstInput());
+ HInvokeStaticOrDirect* static_or_direct = invoke->AsInvokeStaticOrDirect();
+ DCHECK(static_or_direct != nullptr);
+ if (static_or_direct->HasSpecialInput() &&
+ invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) {
+ // We need addressibility for the constant area.
+ locations->SetInAt(1, Location::RequiresRegister());
+ // We need a temporary to hold the constant.
+ locations->AddTemp(Location::RequiresFpuRegister());
+ }
}
-static void MathAbsFP(LocationSummary* locations, bool is64bit, X86Assembler* assembler) {
+static void MathAbsFP(LocationSummary* locations,
+ bool is64bit,
+ X86Assembler* assembler,
+ CodeGeneratorX86* codegen) {
Location output = locations->Out();
- if (output.IsFpuRegister()) {
+ DCHECK(output.IsFpuRegister());
+ if (locations->GetInputCount() == 2 && locations->InAt(1).IsValid()) {
+ DCHECK(locations->InAt(1).IsRegister());
+ // We also have a constant area pointer.
+ Register constant_area = locations->InAt(1).AsRegister<Register>();
+ XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+ if (is64bit) {
+ __ movsd(temp, codegen->LiteralInt64Address(INT64_C(0x7FFFFFFFFFFFFFFF), constant_area));
+ __ andpd(output.AsFpuRegister<XmmRegister>(), temp);
+ } else {
+ __ movss(temp, codegen->LiteralInt32Address(INT32_C(0x7FFFFFFF), constant_area));
+ __ andps(output.AsFpuRegister<XmmRegister>(), temp);
+ }
+ } else {
// Create the right constant on an aligned stack.
if (is64bit) {
__ subl(ESP, Immediate(8));
@@ -277,19 +302,6 @@
__ andps(output.AsFpuRegister<XmmRegister>(), Address(ESP, 0));
}
__ addl(ESP, Immediate(16));
- } else {
- // TODO: update when assember support is available.
- UNIMPLEMENTED(FATAL) << "Needs assembler support.";
-// Once assembler support is available, in-memory operations look like this:
-// if (is64bit) {
-// DCHECK(output.IsDoubleStackSlot());
-// __ andl(Address(Register(RSP), output.GetHighStackIndex(kX86WordSize)),
-// Immediate(0x7FFFFFFF));
-// } else {
-// DCHECK(output.IsStackSlot());
-// // Can use and with a literal directly.
-// __ andl(Address(Register(RSP), output.GetStackIndex()), Immediate(0x7FFFFFFF));
-// }
}
}
@@ -298,7 +310,7 @@
}
void IntrinsicCodeGeneratorX86::VisitMathAbsDouble(HInvoke* invoke) {
- MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
+ MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler(), codegen_);
}
void IntrinsicLocationsBuilderX86::VisitMathAbsFloat(HInvoke* invoke) {
@@ -306,7 +318,7 @@
}
void IntrinsicCodeGeneratorX86::VisitMathAbsFloat(HInvoke* invoke) {
- MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
+ MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler(), codegen_);
}
static void CreateAbsIntLocation(ArenaAllocator* arena, HInvoke* invoke) {
@@ -388,8 +400,11 @@
GenAbsLong(invoke->GetLocations(), GetAssembler());
}
-static void GenMinMaxFP(LocationSummary* locations, bool is_min, bool is_double,
- X86Assembler* assembler) {
+static void GenMinMaxFP(LocationSummary* locations,
+ bool is_min,
+ bool is_double,
+ X86Assembler* assembler,
+ CodeGeneratorX86* codegen) {
Location op1_loc = locations->InAt(0);
Location op2_loc = locations->InAt(1);
Location out_loc = locations->Out();
@@ -450,15 +465,26 @@
// NaN handling.
__ Bind(&nan);
- if (is_double) {
- __ pushl(Immediate(kDoubleNaNHigh));
- __ pushl(Immediate(kDoubleNaNLow));
- __ movsd(out, Address(ESP, 0));
- __ addl(ESP, Immediate(8));
+ // Do we have a constant area pointer?
+ if (locations->GetInputCount() == 3 && locations->InAt(2).IsValid()) {
+ DCHECK(locations->InAt(2).IsRegister());
+ Register constant_area = locations->InAt(2).AsRegister<Register>();
+ if (is_double) {
+ __ movsd(out, codegen->LiteralInt64Address(kDoubleNaN, constant_area));
+ } else {
+ __ movss(out, codegen->LiteralInt32Address(kFloatNaN, constant_area));
+ }
} else {
- __ pushl(Immediate(kFloatNaN));
- __ movss(out, Address(ESP, 0));
- __ addl(ESP, Immediate(4));
+ if (is_double) {
+ __ pushl(Immediate(kDoubleNaNHigh));
+ __ pushl(Immediate(kDoubleNaNLow));
+ __ movsd(out, Address(ESP, 0));
+ __ addl(ESP, Immediate(8));
+ } else {
+ __ pushl(Immediate(kFloatNaN));
+ __ movss(out, Address(ESP, 0));
+ __ addl(ESP, Immediate(4));
+ }
}
__ jmp(&done);
@@ -483,6 +509,12 @@
// The following is sub-optimal, but all we can do for now. It would be fine to also accept
// the second input to be the output (we can simply swap inputs).
locations->SetOut(Location::SameAsFirstInput());
+ HInvokeStaticOrDirect* static_or_direct = invoke->AsInvokeStaticOrDirect();
+ DCHECK(static_or_direct != nullptr);
+ if (static_or_direct->HasSpecialInput() &&
+ invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) {
+ locations->SetInAt(2, Location::RequiresRegister());
+ }
}
void IntrinsicLocationsBuilderX86::VisitMathMinDoubleDouble(HInvoke* invoke) {
@@ -490,7 +522,11 @@
}
void IntrinsicCodeGeneratorX86::VisitMathMinDoubleDouble(HInvoke* invoke) {
- GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetAssembler());
+ GenMinMaxFP(invoke->GetLocations(),
+ /* is_min */ true,
+ /* is_double */ true,
+ GetAssembler(),
+ codegen_);
}
void IntrinsicLocationsBuilderX86::VisitMathMinFloatFloat(HInvoke* invoke) {
@@ -498,7 +534,11 @@
}
void IntrinsicCodeGeneratorX86::VisitMathMinFloatFloat(HInvoke* invoke) {
- GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetAssembler());
+ GenMinMaxFP(invoke->GetLocations(),
+ /* is_min */ true,
+ /* is_double */ false,
+ GetAssembler(),
+ codegen_);
}
void IntrinsicLocationsBuilderX86::VisitMathMaxDoubleDouble(HInvoke* invoke) {
@@ -506,7 +546,11 @@
}
void IntrinsicCodeGeneratorX86::VisitMathMaxDoubleDouble(HInvoke* invoke) {
- GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetAssembler());
+ GenMinMaxFP(invoke->GetLocations(),
+ /* is_min */ false,
+ /* is_double */ true,
+ GetAssembler(),
+ codegen_);
}
void IntrinsicLocationsBuilderX86::VisitMathMaxFloatFloat(HInvoke* invoke) {
@@ -514,7 +558,11 @@
}
void IntrinsicCodeGeneratorX86::VisitMathMaxFloatFloat(HInvoke* invoke) {
- GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetAssembler());
+ GenMinMaxFP(invoke->GetLocations(),
+ /* is_min */ false,
+ /* is_double */ false,
+ GetAssembler(),
+ codegen_);
}
static void GenMinMax(LocationSummary* locations, bool is_min, bool is_long,
@@ -2245,7 +2293,7 @@
}
void IntrinsicCodeGeneratorX86::VisitIntegerReverse(HInvoke* invoke) {
- X86Assembler* assembler = down_cast<X86Assembler*>(codegen_->GetAssembler());
+ X86Assembler* assembler = GetAssembler();
LocationSummary* locations = invoke->GetLocations();
Register reg = locations->InAt(0).AsRegister<Register>();
@@ -2276,7 +2324,7 @@
}
void IntrinsicCodeGeneratorX86::VisitLongReverse(HInvoke* invoke) {
- X86Assembler* assembler = down_cast<X86Assembler*>(codegen_->GetAssembler());
+ X86Assembler* assembler = GetAssembler();
LocationSummary* locations = invoke->GetLocations();
Register reg_low = locations->InAt(0).AsRegisterPairLow<Register>();
@@ -2320,7 +2368,9 @@
locations->SetOut(Location::RequiresRegister());
}
-static void GenBitCount(X86Assembler* assembler, HInvoke* invoke, bool is_long) {
+static void GenBitCount(X86Assembler* assembler,
+ CodeGeneratorX86* codegen,
+ HInvoke* invoke, bool is_long) {
LocationSummary* locations = invoke->GetLocations();
Location src = locations->InAt(0);
Register out = locations->Out().AsRegister<Register>();
@@ -2331,11 +2381,7 @@
value = is_long
? POPCOUNT(static_cast<uint64_t>(value))
: POPCOUNT(static_cast<uint32_t>(value));
- if (value == 0) {
- __ xorl(out, out);
- } else {
- __ movl(out, Immediate(value));
- }
+ codegen->Load32BitValue(out, value);
return;
}
@@ -2367,7 +2413,7 @@
}
void IntrinsicCodeGeneratorX86::VisitIntegerBitCount(HInvoke* invoke) {
- GenBitCount(GetAssembler(), invoke, /* is_long */ false);
+ GenBitCount(GetAssembler(), codegen_, invoke, /* is_long */ false);
}
void IntrinsicLocationsBuilderX86::VisitLongBitCount(HInvoke* invoke) {
@@ -2375,7 +2421,7 @@
}
void IntrinsicCodeGeneratorX86::VisitLongBitCount(HInvoke* invoke) {
- GenBitCount(GetAssembler(), invoke, /* is_long */ true);
+ GenBitCount(GetAssembler(), codegen_, invoke, /* is_long */ true);
}
static void CreateLeadingZeroLocations(ArenaAllocator* arena, HInvoke* invoke, bool is_long) {
@@ -2390,7 +2436,9 @@
locations->SetOut(Location::RequiresRegister());
}
-static void GenLeadingZeros(X86Assembler* assembler, HInvoke* invoke, bool is_long) {
+static void GenLeadingZeros(X86Assembler* assembler,
+ CodeGeneratorX86* codegen,
+ HInvoke* invoke, bool is_long) {
LocationSummary* locations = invoke->GetLocations();
Location src = locations->InAt(0);
Register out = locations->Out().AsRegister<Register>();
@@ -2403,11 +2451,7 @@
} else {
value = is_long ? CLZ(static_cast<uint64_t>(value)) : CLZ(static_cast<uint32_t>(value));
}
- if (value == 0) {
- __ xorl(out, out);
- } else {
- __ movl(out, Immediate(value));
- }
+ codegen->Load32BitValue(out, value);
return;
}
@@ -2474,8 +2518,7 @@
}
void IntrinsicCodeGeneratorX86::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
- X86Assembler* assembler = down_cast<X86Assembler*>(codegen_->GetAssembler());
- GenLeadingZeros(assembler, invoke, /* is_long */ false);
+ GenLeadingZeros(GetAssembler(), codegen_, invoke, /* is_long */ false);
}
void IntrinsicLocationsBuilderX86::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
@@ -2483,8 +2526,7 @@
}
void IntrinsicCodeGeneratorX86::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
- X86Assembler* assembler = down_cast<X86Assembler*>(codegen_->GetAssembler());
- GenLeadingZeros(assembler, invoke, /* is_long */ true);
+ GenLeadingZeros(GetAssembler(), codegen_, invoke, /* is_long */ true);
}
static void CreateTrailingZeroLocations(ArenaAllocator* arena, HInvoke* invoke, bool is_long) {
@@ -2499,7 +2541,9 @@
locations->SetOut(Location::RequiresRegister());
}
-static void GenTrailingZeros(X86Assembler* assembler, HInvoke* invoke, bool is_long) {
+static void GenTrailingZeros(X86Assembler* assembler,
+ CodeGeneratorX86* codegen,
+ HInvoke* invoke, bool is_long) {
LocationSummary* locations = invoke->GetLocations();
Location src = locations->InAt(0);
Register out = locations->Out().AsRegister<Register>();
@@ -2512,11 +2556,7 @@
} else {
value = is_long ? CTZ(static_cast<uint64_t>(value)) : CTZ(static_cast<uint32_t>(value));
}
- if (value == 0) {
- __ xorl(out, out);
- } else {
- __ movl(out, Immediate(value));
- }
+ codegen->Load32BitValue(out, value);
return;
}
@@ -2570,8 +2610,7 @@
}
void IntrinsicCodeGeneratorX86::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
- X86Assembler* assembler = down_cast<X86Assembler*>(codegen_->GetAssembler());
- GenTrailingZeros(assembler, invoke, /* is_long */ false);
+ GenTrailingZeros(GetAssembler(), codegen_, invoke, /* is_long */ false);
}
void IntrinsicLocationsBuilderX86::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
@@ -2579,8 +2618,7 @@
}
void IntrinsicCodeGeneratorX86::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
- X86Assembler* assembler = down_cast<X86Assembler*>(codegen_->GetAssembler());
- GenTrailingZeros(assembler, invoke, /* is_long */ true);
+ GenTrailingZeros(GetAssembler(), codegen_, invoke, /* is_long */ true);
}
// Unimplemented intrinsics.
@@ -2600,20 +2638,20 @@
UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
-UNIMPLEMENTED_INTRINSIC(IntegerCompare)
-UNIMPLEMENTED_INTRINSIC(LongCompare)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
-UNIMPLEMENTED_INTRINSIC(IntegerSignum)
-UNIMPLEMENTED_INTRINSIC(LongSignum)
-// Rotate operations are handled as HRor instructions.
+// Handled as HIR instructions.
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
+UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
UNIMPLEMENTED_INTRINSIC(LongRotateRight)
-UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
+UNIMPLEMENTED_INTRINSIC(IntegerCompare)
+UNIMPLEMENTED_INTRINSIC(LongCompare)
+UNIMPLEMENTED_INTRINSIC(IntegerSignum)
+UNIMPLEMENTED_INTRINSIC(LongSignum)
#undef UNIMPLEMENTED_INTRINSIC
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 51fa514..c9a4344 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2431,58 +2431,6 @@
GenBitCount(GetAssembler(), codegen_, invoke, /* is_long */ true);
}
-static void CreateCompareLocations(ArenaAllocator* arena, HInvoke* invoke) {
- LocationSummary* locations = new (arena) LocationSummary(invoke,
- LocationSummary::kNoCall,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister());
-}
-
-static void GenCompare(X86_64Assembler* assembler, HInvoke* invoke, bool is_long) {
- LocationSummary* locations = invoke->GetLocations();
- CpuRegister src1 = locations->InAt(0).AsRegister<CpuRegister>();
- CpuRegister src2 = locations->InAt(1).AsRegister<CpuRegister>();
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
-
- NearLabel is_lt, done;
-
- __ xorl(out, out);
-
- if (is_long) {
- __ cmpq(src1, src2);
- } else {
- __ cmpl(src1, src2);
- }
- __ j(kEqual, &done);
- __ j(kLess, &is_lt);
-
- __ movl(out, Immediate(1));
- __ jmp(&done);
-
- __ Bind(&is_lt);
- __ movl(out, Immediate(-1));
-
- __ Bind(&done);
-}
-
-void IntrinsicLocationsBuilderX86_64::VisitIntegerCompare(HInvoke* invoke) {
- CreateCompareLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorX86_64::VisitIntegerCompare(HInvoke* invoke) {
- GenCompare(GetAssembler(), invoke, /* is_long */ false);
-}
-
-void IntrinsicLocationsBuilderX86_64::VisitLongCompare(HInvoke* invoke) {
- CreateCompareLocations(arena_, invoke);
-}
-
-void IntrinsicCodeGeneratorX86_64::VisitLongCompare(HInvoke* invoke) {
- GenCompare(GetAssembler(), invoke, /* is_long */ true);
-}
-
static void CreateOneBitLocations(ArenaAllocator* arena, HInvoke* invoke, bool is_high) {
LocationSummary* locations = new (arena) LocationSummary(invoke,
LocationSummary::kNoCall,
@@ -2757,74 +2705,6 @@
GenTrailingZeros(GetAssembler(), codegen_, invoke, /* is_long */ true);
}
-static void CreateSignLocations(ArenaAllocator* arena, HInvoke* invoke) {
- LocationSummary* locations = new (arena) LocationSummary(invoke,
- LocationSummary::kNoCall,
- kIntrinsified);
- locations->SetInAt(0, Location::Any());
- locations->SetOut(Location::RequiresRegister());
- locations->AddTemp(Location::RequiresRegister()); // Need a writeable register.
-}
-
-static void GenSign(X86_64Assembler* assembler,
- CodeGeneratorX86_64* codegen,
- HInvoke* invoke, bool is_long) {
- LocationSummary* locations = invoke->GetLocations();
- Location src = locations->InAt(0);
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
-
- if (invoke->InputAt(0)->IsConstant()) {
- // Evaluate this at compile time.
- int64_t value = Int64FromConstant(invoke->InputAt(0)->AsConstant());
- codegen->Load32BitValue(out, value == 0 ? 0 : (value > 0 ? 1 : -1));
- return;
- }
-
- // Copy input into temporary.
- CpuRegister tmp = locations->GetTemp(0).AsRegister<CpuRegister>();
- if (src.IsRegister()) {
- if (is_long) {
- __ movq(tmp, src.AsRegister<CpuRegister>());
- } else {
- __ movl(tmp, src.AsRegister<CpuRegister>());
- }
- } else if (is_long) {
- DCHECK(src.IsDoubleStackSlot());
- __ movq(tmp, Address(CpuRegister(RSP), src.GetStackIndex()));
- } else {
- DCHECK(src.IsStackSlot());
- __ movl(tmp, Address(CpuRegister(RSP), src.GetStackIndex()));
- }
-
- // Do the bit twiddling: basically tmp >> 63/31 | -tmp >>> 63/31 for long/int.
- if (is_long) {
- __ movq(out, tmp);
- __ sarq(out, Immediate(63));
- __ negq(tmp);
- __ shrq(tmp, Immediate(63));
- __ orq(out, tmp);
- } else {
- __ movl(out, tmp);
- __ sarl(out, Immediate(31));
- __ negl(tmp);
- __ shrl(tmp, Immediate(31));
- __ orl(out, tmp);
- }
-}
-
-void IntrinsicLocationsBuilderX86_64::VisitIntegerSignum(HInvoke* invoke) {
- CreateSignLocations(arena_, invoke);
-}
-void IntrinsicCodeGeneratorX86_64::VisitIntegerSignum(HInvoke* invoke) {
- GenSign(GetAssembler(), codegen_, invoke, /* is_long */ false);
-}
-void IntrinsicLocationsBuilderX86_64::VisitLongSignum(HInvoke* invoke) {
- CreateSignLocations(arena_, invoke);
-}
-void IntrinsicCodeGeneratorX86_64::VisitLongSignum(HInvoke* invoke) {
- GenSign(GetAssembler(), codegen_, invoke, /* is_long */ true);
-}
-
// Unimplemented intrinsics.
#define UNIMPLEMENTED_INTRINSIC(Name) \
@@ -2840,11 +2720,15 @@
UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
-// Rotate operations are handled as HRor instructions.
+// Handled as HIR instructions.
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
-UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
+UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
UNIMPLEMENTED_INTRINSIC(LongRotateRight)
+UNIMPLEMENTED_INTRINSIC(IntegerCompare)
+UNIMPLEMENTED_INTRINSIC(LongCompare)
+UNIMPLEMENTED_INTRINSIC(IntegerSignum)
+UNIMPLEMENTED_INTRINSIC(LongSignum)
#undef UNIMPLEMENTED_INTRINSIC
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
index 2b63ec8..9fb32f4 100644
--- a/compiler/optimizing/licm_test.cc
+++ b/compiler/optimizing/licm_test.cc
@@ -76,7 +76,7 @@
// Performs LICM optimizations (after proper set up).
void PerformLICM() {
- TransformToSsa(graph_);
+ graph_->BuildDominatorTree();
SideEffectsAnalysis side_effects(graph_);
side_effects.Run();
LICM(graph_, side_effects).Run();
diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc
index ed275b1..13e14c5 100644
--- a/compiler/optimizing/linearize_test.cc
+++ b/compiler/optimizing/linearize_test.cc
@@ -39,14 +39,7 @@
static void TestCode(const uint16_t* data, const uint32_t (&expected_order)[number_of_blocks]) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
- HGraph* graph = CreateGraph(&allocator);
- HGraphBuilder builder(graph);
- const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- bool graph_built = builder.BuildGraph(*item);
- ASSERT_TRUE(graph_built);
-
- TransformToSsa(graph);
-
+ HGraph* graph = CreateCFG(&allocator, data);
std::unique_ptr<const X86InstructionSetFeatures> features_x86(
X86InstructionSetFeatures::FromCppDefines());
x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions());
diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc
index 991f8f7..3202493 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -32,14 +32,10 @@
class LiveRangesTest : public CommonCompilerTest {};
static HGraph* BuildGraph(const uint16_t* data, ArenaAllocator* allocator) {
- HGraph* graph = CreateGraph(allocator);
- HGraphBuilder builder(graph);
- const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- builder.BuildGraph(*item);
+ HGraph* graph = CreateCFG(allocator, data);
// Suspend checks implementation may change in the future, and this test relies
// on how instructions are ordered.
RemoveSuspendChecks(graph);
- TransformToSsa(graph);
// `Inline` conditions into ifs.
PrepareForRegisterAllocation(graph).Run();
return graph;
@@ -303,13 +299,12 @@
* 12: equal
* 14: if +++++
* | \ +
- * | 18: suspend
- * | 20: add
- * | 22: goto
+ * | 18: add
+ * | 20: goto
* |
- * 26: return
+ * 24: return
* |
- * 30: exit
+ * 28: exit
*
* We want to make sure the phi at 10 has a lifetime hole after the add at 20.
*/
@@ -345,18 +340,18 @@
interval = phi->GetLiveInterval();
range = interval->GetFirstRange();
ASSERT_EQ(10u, range->GetStart());
- ASSERT_EQ(21u, range->GetEnd());
+ ASSERT_EQ(19u, range->GetEnd());
range = range->GetNext();
ASSERT_TRUE(range != nullptr);
- ASSERT_EQ(24u, range->GetStart());
- ASSERT_EQ(26u, range->GetEnd());
+ ASSERT_EQ(22u, range->GetStart());
+ ASSERT_EQ(24u, range->GetEnd());
// Test for the add instruction.
HAdd* add = liveness.GetInstructionFromSsaIndex(2)->AsAdd();
interval = add->GetLiveInterval();
range = interval->GetFirstRange();
- ASSERT_EQ(20u, range->GetStart());
- ASSERT_EQ(24u, range->GetEnd());
+ ASSERT_EQ(18u, range->GetStart());
+ ASSERT_EQ(22u, range->GetEnd());
ASSERT_TRUE(range->GetNext() == nullptr);
}
diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc
index 7736eed..92a987c 100644
--- a/compiler/optimizing/liveness_test.cc
+++ b/compiler/optimizing/liveness_test.cc
@@ -46,12 +46,7 @@
static void TestCode(const uint16_t* data, const char* expected) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
- HGraph* graph = CreateGraph(&allocator);
- HGraphBuilder builder(graph);
- const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- bool graph_built = builder.BuildGraph(*item);
- ASSERT_TRUE(graph_built);
- TransformToSsa(graph);
+ HGraph* graph = CreateCFG(&allocator, data);
// `Inline` conditions into ifs.
PrepareForRegisterAllocation(graph).Run();
std::unique_ptr<const X86InstructionSetFeatures> features_x86(
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 9a97f54..8eaac0b 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -61,7 +61,7 @@
(use->IsStaticFieldSet() && (reference_ == use->InputAt(1))) ||
(use->IsUnresolvedStaticFieldSet() && (reference_ == use->InputAt(0))) ||
(use->IsArraySet() && (reference_ == use->InputAt(2)))) {
- // reference_ is merged to a phi/HSelect, passed to a callee, or stored to heap.
+ // reference_ is merged to HPhi/HSelect, passed to a callee, or stored to heap.
// reference_ isn't the only name that can refer to its value anymore.
is_singleton_ = false;
is_singleton_and_not_returned_ = false;
@@ -458,6 +458,10 @@
CreateReferenceInfoForReferenceType(instruction);
}
+ void VisitSelect(HSelect* instruction) OVERRIDE {
+ CreateReferenceInfoForReferenceType(instruction);
+ }
+
void VisitDeoptimize(HDeoptimize* instruction ATTRIBUTE_UNUSED) OVERRIDE {
may_deoptimize_ = true;
}
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index c057eca..27015c0 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -27,6 +27,15 @@
namespace art {
+void HGraph::InitializeInexactObjectRTI(StackHandleScopeCollection* handles) {
+ ScopedObjectAccess soa(Thread::Current());
+ // Create the inexact Object reference type and store it in the HGraph.
+ ClassLinker* linker = Runtime::Current()->GetClassLinker();
+ inexact_object_rti_ = ReferenceTypeInfo::Create(
+ handles->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangObject)),
+ /* is_exact */ false);
+}
+
void HGraph::AddBlock(HBasicBlock* block) {
block->SetBlockId(blocks_.size());
blocks_.push_back(block);
@@ -236,29 +245,6 @@
}
}
-GraphAnalysisResult HGraph::TryBuildingSsa(StackHandleScopeCollection* handles) {
- GraphAnalysisResult result = BuildDominatorTree();
- if (result != kAnalysisSuccess) {
- return result;
- }
-
- // Create the inexact Object reference type and store it in the HGraph.
- ScopedObjectAccess soa(Thread::Current());
- ClassLinker* linker = Runtime::Current()->GetClassLinker();
- inexact_object_rti_ = ReferenceTypeInfo::Create(
- handles->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangObject)),
- /* is_exact */ false);
-
- // Tranforms graph to SSA form.
- result = SsaBuilder(this, handles).BuildSsa();
- if (result != kAnalysisSuccess) {
- return result;
- }
-
- in_ssa_form_ = true;
- return kAnalysisSuccess;
-}
-
HBasicBlock* HGraph::SplitEdge(HBasicBlock* block, HBasicBlock* successor) {
HBasicBlock* new_block = new (arena_) HBasicBlock(this, successor->GetDexPc());
AddBlock(new_block);
@@ -647,6 +633,10 @@
header_->GetGraph()->SetHasIrreducibleLoops(true);
PopulateIrreducibleRecursive(back_edge);
} else {
+ if (header_->GetGraph()->IsCompilingOsr()) {
+ irreducible_ = true;
+ header_->GetGraph()->SetHasIrreducibleLoops(true);
+ }
PopulateRecursive(back_edge);
}
}
@@ -858,7 +848,6 @@
// At the end of the loop pre-header, the corresponding value for instruction
// is the first input of the phi.
HInstruction* initial = instruction->AsPhi()->InputAt(0);
- DCHECK(initial->GetBlock()->Dominates(loop_header));
SetRawEnvAt(i, initial);
initial->AddEnvUseAt(this, i);
} else {
@@ -1589,7 +1578,7 @@
loop_info->Remove(this);
if (loop_info->IsBackEdge(*this)) {
// If this was the last back edge of the loop, we deliberately leave the
- // loop in an inconsistent state and will fail SSAChecker unless the
+ // loop in an inconsistent state and will fail GraphChecker unless the
// entire loop is removed during the pass.
loop_info->RemoveBackEdge(this);
}
@@ -1628,7 +1617,7 @@
} else if (num_pred_successors == 0u) {
// The predecessor has no remaining successors and therefore must be dead.
// We deliberately leave it without a control-flow instruction so that the
- // SSAChecker fails unless it is not removed during the pass too.
+ // GraphChecker fails unless it is not removed during the pass too.
predecessor->RemoveInstruction(last_instruction);
} else {
// There are multiple successors left. The removed block might be a successor
@@ -2041,13 +2030,6 @@
}
}
- if (return_value != nullptr) {
- invoke->ReplaceWith(return_value);
- }
-
- // Finally remove the invoke from the caller.
- invoke->GetBlock()->RemoveInstruction(invoke);
-
return return_value;
}
@@ -2384,4 +2366,26 @@
return os;
}
+std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs) {
+ switch (rhs) {
+ case TypeCheckKind::kUnresolvedCheck:
+ return os << "unresolved_check";
+ case TypeCheckKind::kExactCheck:
+ return os << "exact_check";
+ case TypeCheckKind::kClassHierarchyCheck:
+ return os << "class_hierarchy_check";
+ case TypeCheckKind::kAbstractClassCheck:
+ return os << "abstract_class_check";
+ case TypeCheckKind::kInterfaceCheck:
+ return os << "interface_check";
+ case TypeCheckKind::kArrayObjectCheck:
+ return os << "array_object_check";
+ case TypeCheckKind::kArrayCheck:
+ return os << "array_check";
+ default:
+ LOG(FATAL) << "Unknown TypeCheckKind: " << static_cast<int>(rhs);
+ UNREACHABLE();
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index b808347..854854f 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -98,6 +98,7 @@
};
enum GraphAnalysisResult {
+ kAnalysisInvalidBytecode,
kAnalysisFailThrowCatchLoop,
kAnalysisFailAmbiguousArrayOp,
kAnalysisSuccess,
@@ -274,6 +275,7 @@
InstructionSet instruction_set,
InvokeType invoke_type = kInvalidInvokeType,
bool debuggable = false,
+ bool osr = false,
int start_instruction_id = 0)
: arena_(arena),
blocks_(arena->Adapter(kArenaAllocBlockList)),
@@ -302,14 +304,19 @@
cached_long_constants_(std::less<int64_t>(), arena->Adapter(kArenaAllocConstantsMap)),
cached_double_constants_(std::less<int64_t>(), arena->Adapter(kArenaAllocConstantsMap)),
cached_current_method_(nullptr),
- inexact_object_rti_(ReferenceTypeInfo::CreateInvalid()) {
+ inexact_object_rti_(ReferenceTypeInfo::CreateInvalid()),
+ osr_(osr) {
blocks_.reserve(kDefaultNumberOfBlocks);
}
+ // Acquires and stores RTI of inexact Object to be used when creating HNullConstant.
+ void InitializeInexactObjectRTI(StackHandleScopeCollection* handles);
+
ArenaAllocator* GetArena() const { return arena_; }
const ArenaVector<HBasicBlock*>& GetBlocks() const { return blocks_; }
bool IsInSsaForm() const { return in_ssa_form_; }
+ void SetInSsaForm() { in_ssa_form_ = true; }
HBasicBlock* GetEntryBlock() const { return entry_block_; }
HBasicBlock* GetExitBlock() const { return exit_block_; }
@@ -320,11 +327,6 @@
void AddBlock(HBasicBlock* block);
- // Try building the SSA form of this graph, with dominance computation and
- // loop recognition. Returns a code specifying that it was successful or the
- // reason for failure.
- GraphAnalysisResult TryBuildingSsa(StackHandleScopeCollection* handles);
-
void ComputeDominanceInformation();
void ClearDominanceInformation();
void ClearLoopInformation();
@@ -343,8 +345,9 @@
void ComputeTryBlockInformation();
// Inline this graph in `outer_graph`, replacing the given `invoke` instruction.
- // Returns the instruction used to replace the invoke expression or null if the
- // invoke is for a void method.
+ // Returns the instruction to replace the invoke expression or null if the
+ // invoke is for a void method. Note that the caller is responsible for replacing
+ // and removing the invoke instruction.
HInstruction* InlineInto(HGraph* outer_graph, HInvoke* invoke);
// Need to add a couple of blocks to test if the loop body is entered and
@@ -478,6 +481,8 @@
return instruction_set_;
}
+ bool IsCompilingOsr() const { return osr_; }
+
bool HasTryCatch() const { return has_try_catch_; }
void SetHasTryCatch(bool value) { has_try_catch_ = value; }
@@ -606,6 +611,11 @@
// collection pointer to passes which may create NullConstant.
ReferenceTypeInfo inexact_object_rti_;
+ // Whether we are compiling this graph for on stack replacement: this will
+ // make all loops seen as irreducible and emit special stack maps to mark
+ // compiled code entries which the interpreter can directly jump to.
+ const bool osr_;
+
friend class SsaBuilder; // For caching constants.
friend class SsaLivenessAnalysis; // For the linear order.
ART_FRIEND_TEST(GraphTest, IfSuccessorSimpleJoinBlock1);
@@ -1226,7 +1236,6 @@
M(StoreLocal, Instruction) \
M(Sub, BinaryOperation) \
M(SuspendCheck, Instruction) \
- M(Temporary, Instruction) \
M(Throw, Instruction) \
M(TryBoundary, Instruction) \
M(TypeConversion, Instruction) \
@@ -1259,6 +1268,7 @@
#define FOR_EACH_CONCRETE_INSTRUCTION_X86(M) \
M(X86ComputeBaseMethodAddress, Instruction) \
M(X86LoadFromConstantTable, Instruction) \
+ M(X86FPNeg, Instruction) \
M(X86PackedSwitch, Instruction)
#endif
@@ -3662,6 +3672,7 @@
// method pointer; otherwise there may be one platform-specific special input,
// such as PC-relative addressing base.
uint32_t GetSpecialInputIndex() const { return GetNumberOfArguments(); }
+ bool HasSpecialInput() const { return GetNumberOfArguments() != InputCount(); }
InvokeType GetOptimizedInvokeType() const { return optimized_invoke_type_; }
void SetOptimizedInvokeType(InvokeType invoke_type) {
@@ -4931,33 +4942,6 @@
DISALLOW_COPY_AND_ASSIGN(HBoundsCheck);
};
-/**
- * Some DEX instructions are folded into multiple HInstructions that need
- * to stay live until the last HInstruction. This class
- * is used as a marker for the baseline compiler to ensure its preceding
- * HInstruction stays live. `index` represents the stack location index of the
- * instruction (the actual offset is computed as index * vreg_size).
- */
-class HTemporary : public HTemplateInstruction<0> {
- public:
- explicit HTemporary(size_t index, uint32_t dex_pc = kNoDexPc)
- : HTemplateInstruction(SideEffects::None(), dex_pc), index_(index) {}
-
- size_t GetIndex() const { return index_; }
-
- Primitive::Type GetType() const OVERRIDE {
- // The previous instruction is the one that will be stored in the temporary location.
- DCHECK(GetPrevious() != nullptr);
- return GetPrevious()->GetType();
- }
-
- DECLARE_INSTRUCTION(Temporary);
-
- private:
- const size_t index_;
- DISALLOW_COPY_AND_ASSIGN(HTemporary);
-};
-
class HSuspendCheck : public HTemplateInstruction<0> {
public:
explicit HSuspendCheck(uint32_t dex_pc)
@@ -5441,6 +5425,8 @@
kArrayCheck // No optimization yet when checking against a generic array.
};
+std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs);
+
class HInstanceOf : public HExpression<2> {
public:
HInstanceOf(HInstruction* object,
@@ -6013,9 +5999,14 @@
};
inline int64_t Int64FromConstant(HConstant* constant) {
- DCHECK(constant->IsIntConstant() || constant->IsLongConstant());
- return constant->IsIntConstant() ? constant->AsIntConstant()->GetValue()
- : constant->AsLongConstant()->GetValue();
+ if (constant->IsIntConstant()) {
+ return constant->AsIntConstant()->GetValue();
+ } else if (constant->IsLongConstant()) {
+ return constant->AsLongConstant()->GetValue();
+ } else {
+ DCHECK(constant->IsNullConstant());
+ return 0;
+ }
}
inline bool IsSameDexFile(const DexFile& lhs, const DexFile& rhs) {
@@ -6040,6 +6031,74 @@
FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
#undef INSTRUCTION_TYPE_CHECK
+class SwitchTable : public ValueObject {
+ public:
+ SwitchTable(const Instruction& instruction, uint32_t dex_pc, bool sparse)
+ : instruction_(instruction), dex_pc_(dex_pc), sparse_(sparse) {
+ int32_t table_offset = instruction.VRegB_31t();
+ const uint16_t* table = reinterpret_cast<const uint16_t*>(&instruction) + table_offset;
+ if (sparse) {
+ CHECK_EQ(table[0], static_cast<uint16_t>(Instruction::kSparseSwitchSignature));
+ } else {
+ CHECK_EQ(table[0], static_cast<uint16_t>(Instruction::kPackedSwitchSignature));
+ }
+ num_entries_ = table[1];
+ values_ = reinterpret_cast<const int32_t*>(&table[2]);
+ }
+
+ uint16_t GetNumEntries() const {
+ return num_entries_;
+ }
+
+ void CheckIndex(size_t index) const {
+ if (sparse_) {
+ // In a sparse table, we have num_entries_ keys and num_entries_ values, in that order.
+ DCHECK_LT(index, 2 * static_cast<size_t>(num_entries_));
+ } else {
+ // In a packed table, we have the starting key and num_entries_ values.
+ DCHECK_LT(index, 1 + static_cast<size_t>(num_entries_));
+ }
+ }
+
+ int32_t GetEntryAt(size_t index) const {
+ CheckIndex(index);
+ return values_[index];
+ }
+
+ uint32_t GetDexPcForIndex(size_t index) const {
+ CheckIndex(index);
+ return dex_pc_ +
+ (reinterpret_cast<const int16_t*>(values_ + index) -
+ reinterpret_cast<const int16_t*>(&instruction_));
+ }
+
+ // Index of the first value in the table.
+ size_t GetFirstValueIndex() const {
+ if (sparse_) {
+ // In a sparse table, we have num_entries_ keys and num_entries_ values, in that order.
+ return num_entries_;
+ } else {
+ // In a packed table, we have the starting key and num_entries_ values.
+ return 1;
+ }
+ }
+
+ private:
+ const Instruction& instruction_;
+ const uint32_t dex_pc_;
+
+ // Whether this is a sparse-switch table (or a packed-switch one).
+ const bool sparse_;
+
+ // This can't be const as it needs to be computed off of the given instruction, and complicated
+ // expressions in the initializer list seemed very ugly.
+ uint16_t num_entries_;
+
+ const int32_t* values_;
+
+ DISALLOW_COPY_AND_ASSIGN(SwitchTable);
+};
+
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_NODES_H_
diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h
index b1bf939..0b3a84d 100644
--- a/compiler/optimizing/nodes_x86.h
+++ b/compiler/optimizing/nodes_x86.h
@@ -56,6 +56,25 @@
DISALLOW_COPY_AND_ASSIGN(HX86LoadFromConstantTable);
};
+// Version of HNeg with access to the constant table for FP types.
+class HX86FPNeg : public HExpression<2> {
+ public:
+ HX86FPNeg(Primitive::Type result_type,
+ HInstruction* input,
+ HX86ComputeBaseMethodAddress* method_base,
+ uint32_t dex_pc)
+ : HExpression(result_type, SideEffects::None(), dex_pc) {
+ DCHECK(Primitive::IsFloatingPointType(result_type));
+ SetRawInputAt(0, input);
+ SetRawInputAt(1, method_base);
+ }
+
+ DECLARE_INSTRUCTION(X86FPNeg);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HX86FPNeg);
+};
+
// X86 version of HPackedSwitch that holds a pointer to the base method address.
class HX86PackedSwitch : public HTemplateInstruction<2> {
public:
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index bdc664b..12b748b 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -45,15 +45,14 @@
#include "compiler.h"
#include "constant_folding.h"
#include "dead_code_elimination.h"
+#include "debug/elf_debug_writer.h"
+#include "debug/method_debug_info.h"
#include "dex/quick/dex_file_to_method_inliner_map.h"
-#include "dex/verified_method.h"
#include "dex/verification_results.h"
-#include "driver/compiler_driver.h"
+#include "dex/verified_method.h"
#include "driver/compiler_driver-inl.h"
#include "driver/compiler_options.h"
#include "driver/dex_compilation_unit.h"
-#include "dwarf/method_debug_info.h"
-#include "elf_writer_debug.h"
#include "elf_writer_quick.h"
#include "graph_checker.h"
#include "graph_visualizer.h"
@@ -64,20 +63,20 @@
#include "intrinsics.h"
#include "jit/debugger_interface.h"
#include "jit/jit_code_cache.h"
-#include "licm.h"
#include "jni/quick/jni_compiler.h"
+#include "licm.h"
#include "load_store_elimination.h"
#include "nodes.h"
+#include "oat_quick_method_header.h"
#include "prepare_for_register_allocation.h"
#include "reference_type_propagation.h"
#include "register_allocator.h"
-#include "oat_quick_method_header.h"
#include "select_generator.h"
#include "sharpening.h"
#include "side_effects_analysis.h"
#include "ssa_builder.h"
-#include "ssa_phi_elimination.h"
#include "ssa_liveness_analysis.h"
+#include "ssa_phi_elimination.h"
#include "utils/assembler.h"
#include "verifier/method_verifier.h"
@@ -187,18 +186,10 @@
// Validate the HGraph if running in debug mode.
if (kIsDebugBuild) {
if (!graph_in_bad_state_) {
- if (graph_->IsInSsaForm()) {
- SSAChecker checker(graph_);
- checker.Run();
- if (!checker.IsValid()) {
- LOG(FATAL) << "Error after " << pass_name << ": " << Dumpable<SSAChecker>(checker);
- }
- } else {
- GraphChecker checker(graph_);
- checker.Run();
- if (!checker.IsValid()) {
- LOG(FATAL) << "Error after " << pass_name << ": " << Dumpable<GraphChecker>(checker);
- }
+ GraphChecker checker(graph_);
+ checker.Run();
+ if (!checker.IsValid()) {
+ LOG(FATAL) << "Error after " << pass_name << ": " << Dumpable<GraphChecker>(checker);
}
}
}
@@ -300,7 +291,7 @@
}
}
- bool JitCompile(Thread* self, jit::JitCodeCache* code_cache, ArtMethod* method)
+ bool JitCompile(Thread* self, jit::JitCodeCache* code_cache, ArtMethod* method, bool osr)
OVERRIDE
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -309,7 +300,8 @@
CompiledMethod* Emit(ArenaAllocator* arena,
CodeVectorAllocator* code_allocator,
CodeGenerator* codegen,
- CompilerDriver* driver) const;
+ CompilerDriver* driver,
+ const DexFile::CodeItem* item) const;
// Try compiling a method and return the code generator used for
// compiling it.
@@ -327,7 +319,8 @@
uint32_t method_idx,
jobject class_loader,
const DexFile& dex_file,
- Handle<mirror::DexCache> dex_cache) const;
+ Handle<mirror::DexCache> dex_cache,
+ bool osr) const;
std::unique_ptr<OptimizingCompilerStats> compilation_stats_;
@@ -580,11 +573,12 @@
CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* arena,
CodeVectorAllocator* code_allocator,
CodeGenerator* codegen,
- CompilerDriver* compiler_driver) const {
+ CompilerDriver* compiler_driver,
+ const DexFile::CodeItem* code_item) const {
ArenaVector<LinkerPatch> linker_patches = EmitAndSortLinkerPatches(codegen);
ArenaVector<uint8_t> stack_map(arena->Adapter(kArenaAllocStackMaps));
stack_map.resize(codegen->ComputeStackMapsSize());
- codegen->BuildStackMaps(MemoryRegion(stack_map.data(), stack_map.size()));
+ codegen->BuildStackMaps(MemoryRegion(stack_map.data(), stack_map.size()), *code_item);
CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod(
compiler_driver,
@@ -615,7 +609,8 @@
uint32_t method_idx,
jobject class_loader,
const DexFile& dex_file,
- Handle<mirror::DexCache> dex_cache) const {
+ Handle<mirror::DexCache> dex_cache,
+ bool osr) const {
MaybeRecordStat(MethodCompilationStat::kAttemptCompilation);
CompilerDriver* compiler_driver = GetCompilerDriver();
InstructionSet instruction_set = compiler_driver->GetInstructionSet();
@@ -662,9 +657,31 @@
&& compiler_driver->RequiresConstructorBarrier(Thread::Current(),
dex_compilation_unit.GetDexFile(),
dex_compilation_unit.GetClassDefIndex());
+
HGraph* graph = new (arena) HGraph(
- arena, dex_file, method_idx, requires_barrier, compiler_driver->GetInstructionSet(),
- kInvalidInvokeType, compiler_driver->GetCompilerOptions().GetDebuggable());
+ arena,
+ dex_file,
+ method_idx,
+ requires_barrier,
+ compiler_driver->GetInstructionSet(),
+ kInvalidInvokeType,
+ compiler_driver->GetCompilerOptions().GetDebuggable(),
+ osr);
+
+ const uint8_t* interpreter_metadata = nullptr;
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> loader(hs.NewHandle(
+ soa.Decode<mirror::ClassLoader*>(class_loader)));
+ ArtMethod* art_method = compiler_driver->ResolveMethod(
+ soa, dex_cache, loader, &dex_compilation_unit, method_idx, invoke_type);
+ // We may not get a method, for example if its class is erroneous.
+ if (art_method != nullptr) {
+ graph->SetArtMethod(art_method);
+ interpreter_metadata = art_method->GetQuickenedInfo();
+ }
+ }
std::unique_ptr<CodeGenerator> codegen(
CodeGenerator::Create(graph,
@@ -683,73 +700,54 @@
visualizer_output_.get(),
compiler_driver);
- const uint8_t* interpreter_metadata = nullptr;
- {
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::ClassLoader> loader(hs.NewHandle(
- soa.Decode<mirror::ClassLoader*>(class_loader)));
- ArtMethod* art_method = compiler_driver->ResolveMethod(
- soa, dex_cache, loader, &dex_compilation_unit, method_idx, invoke_type);
- // We may not get a method, for example if its class is erroneous.
- if (art_method != nullptr) {
- graph->SetArtMethod(art_method);
- interpreter_metadata = art_method->GetQuickenedInfo();
- }
- }
- HGraphBuilder builder(graph,
- &dex_compilation_unit,
- &dex_compilation_unit,
- &dex_file,
- compiler_driver,
- compilation_stats_.get(),
- interpreter_metadata,
- dex_cache);
-
VLOG(compiler) << "Building " << pass_observer.GetMethodName();
{
- PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer);
- if (!builder.BuildGraph(*code_item)) {
- pass_observer.SetGraphInBadState();
- return nullptr;
- }
- }
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScopeCollection handles(soa.Self());
+ // Do not hold `mutator_lock_` between optimizations.
+ ScopedThreadSuspension sts(soa.Self(), kNative);
- VLOG(compiler) << "Optimizing " << pass_observer.GetMethodName();
-
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScopeCollection handles(soa.Self());
- ScopedThreadSuspension sts(soa.Self(), kNative);
-
- {
- PassScope scope(SsaBuilder::kSsaBuilderPassName, &pass_observer);
- GraphAnalysisResult result = graph->TryBuildingSsa(&handles);
- if (result != kAnalysisSuccess) {
- switch (result) {
- case kAnalysisFailThrowCatchLoop:
- MaybeRecordStat(MethodCompilationStat::kNotCompiledThrowCatchLoop);
- break;
- case kAnalysisFailAmbiguousArrayOp:
- MaybeRecordStat(MethodCompilationStat::kNotCompiledAmbiguousArrayOp);
- break;
- case kAnalysisSuccess:
- UNREACHABLE();
+ {
+ PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer);
+ HGraphBuilder builder(graph,
+ &dex_compilation_unit,
+ &dex_compilation_unit,
+ &dex_file,
+ compiler_driver,
+ compilation_stats_.get(),
+ interpreter_metadata,
+ dex_cache);
+ GraphAnalysisResult result = builder.BuildGraph(*code_item, &handles);
+ if (result != kAnalysisSuccess) {
+ switch (result) {
+ case kAnalysisInvalidBytecode:
+ break;
+ case kAnalysisFailThrowCatchLoop:
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledThrowCatchLoop);
+ break;
+ case kAnalysisFailAmbiguousArrayOp:
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledAmbiguousArrayOp);
+ break;
+ case kAnalysisSuccess:
+ UNREACHABLE();
+ }
+ pass_observer.SetGraphInBadState();
+ return nullptr;
}
- pass_observer.SetGraphInBadState();
- return nullptr;
}
- }
- RunOptimizations(graph,
- codegen.get(),
- compiler_driver,
- compilation_stats_.get(),
- dex_compilation_unit,
- &pass_observer,
- &handles);
- codegen->Compile(code_allocator);
- pass_observer.DumpDisassembly();
+ RunOptimizations(graph,
+ codegen.get(),
+ compiler_driver,
+ compilation_stats_.get(),
+ dex_compilation_unit,
+ &pass_observer,
+ &handles);
+
+ codegen->Compile(code_allocator);
+ pass_observer.DumpDisassembly();
+ }
if (kArenaAllocatorCountAllocations) {
if (arena->BytesAllocated() > 4 * MB) {
@@ -797,10 +795,11 @@
method_idx,
jclass_loader,
dex_file,
- dex_cache));
+ dex_cache,
+ /* osr */ false));
if (codegen.get() != nullptr) {
MaybeRecordStat(MethodCompilationStat::kCompiled);
- method = Emit(&arena, &code_allocator, codegen.get(), compiler_driver);
+ method = Emit(&arena, &code_allocator, codegen.get(), compiler_driver, code_item);
}
} else {
if (compiler_driver->GetCompilerOptions().VerifyAtRuntime()) {
@@ -843,7 +842,8 @@
bool OptimizingCompiler::JitCompile(Thread* self,
jit::JitCodeCache* code_cache,
- ArtMethod* method) {
+ ArtMethod* method,
+ bool osr) {
StackHandleScope<2> hs(self);
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
method->GetDeclaringClass()->GetClassLoader()));
@@ -873,7 +873,8 @@
method_idx,
jclass_loader,
*dex_file,
- dex_cache));
+ dex_cache,
+ osr));
if (codegen.get() == nullptr) {
return false;
}
@@ -885,7 +886,7 @@
return false;
}
MaybeRecordStat(MethodCompilationStat::kCompiled);
- codegen->BuildStackMaps(MemoryRegion(stack_map_data, stack_map_size));
+ codegen->BuildStackMaps(MemoryRegion(stack_map_data, stack_map_size), *code_item);
const void* code = code_cache->CommitCode(
self,
method,
@@ -896,7 +897,8 @@
codegen->GetCoreSpillMask(),
codegen->GetFpuSpillMask(),
code_allocator.GetMemory().data(),
- code_allocator.GetSize());
+ code_allocator.GetSize(),
+ osr);
if (code == nullptr) {
code_cache->ClearData(self, stack_map_data);
@@ -919,7 +921,7 @@
ArrayRef<const uint8_t>(), // native_gc_map.
ArrayRef<const uint8_t>(*codegen->GetAssembler()->cfi().data()),
ArrayRef<const LinkerPatch>());
- dwarf::MethodDebugInfo method_debug_info {
+ debug::MethodDebugInfo method_debug_info {
dex_file,
class_def_idx,
method_idx,
@@ -930,7 +932,7 @@
code_address + code_allocator.GetSize(),
&compiled_method
};
- ArrayRef<const uint8_t> elf_file = dwarf::WriteDebugElfFileForMethod(method_debug_info);
+ ArrayRef<const uint8_t> elf_file = debug::WriteDebugElfFileForMethod(method_debug_info);
CreateJITCodeEntryForAddress(code_address,
std::unique_ptr<const uint8_t[]>(elf_file.data()),
elf_file.size());
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index 5a91043..0c7648e 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -64,10 +64,12 @@
void RemoveSuspendChecks(HGraph* graph) {
for (HBasicBlock* block : graph->GetBlocks()) {
- for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
- HInstruction* current = it.Current();
- if (current->IsSuspendCheck()) {
- current->GetBlock()->RemoveInstruction(current);
+ if (block != nullptr) {
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* current = it.Current();
+ if (current->IsSuspendCheck()) {
+ current->GetBlock()->RemoveInstruction(current);
+ }
}
}
}
@@ -83,12 +85,17 @@
inline HGraph* CreateCFG(ArenaAllocator* allocator,
const uint16_t* data,
Primitive::Type return_type = Primitive::kPrimInt) {
- HGraph* graph = CreateGraph(allocator);
- HGraphBuilder builder(graph, return_type);
const DexFile::CodeItem* item =
reinterpret_cast<const DexFile::CodeItem*>(data);
- bool graph_built = builder.BuildGraph(*item);
- return graph_built ? graph : nullptr;
+ HGraph* graph = CreateGraph(allocator);
+
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScopeCollection handles(soa.Self());
+ HGraphBuilder builder(graph, return_type);
+ bool graph_built = (builder.BuildGraph(*item, &handles) == kAnalysisSuccess);
+ return graph_built ? graph : nullptr;
+ }
}
// Naive string diff data type.
@@ -114,12 +121,6 @@
return instruction->GetBlock() == nullptr;
}
-inline void TransformToSsa(HGraph* graph) {
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScopeCollection handles(soa.Self());
- EXPECT_EQ(graph->TryBuildingSsa(&handles), kAnalysisSuccess);
-}
-
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_
diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc
index a2180bc..a6f1461 100644
--- a/compiler/optimizing/pc_relative_fixups_x86.cc
+++ b/compiler/optimizing/pc_relative_fixups_x86.cc
@@ -53,6 +53,10 @@
BinaryFP(div);
}
+ void VisitCompare(HCompare* compare) OVERRIDE {
+ BinaryFP(compare);
+ }
+
void VisitReturn(HReturn* ret) OVERRIDE {
HConstant* value = ret->InputAt(0)->AsConstant();
if ((value != nullptr && Primitive::IsFloatingPointType(value->GetType()))) {
@@ -74,11 +78,50 @@
void BinaryFP(HBinaryOperation* bin) {
HConstant* rhs = bin->InputAt(1)->AsConstant();
- if (rhs != nullptr && Primitive::IsFloatingPointType(bin->GetResultType())) {
+ if (rhs != nullptr && Primitive::IsFloatingPointType(rhs->GetType())) {
ReplaceInput(bin, rhs, 1, false);
}
}
+ void VisitEqual(HEqual* cond) OVERRIDE {
+ BinaryFP(cond);
+ }
+
+ void VisitNotEqual(HNotEqual* cond) OVERRIDE {
+ BinaryFP(cond);
+ }
+
+ void VisitLessThan(HLessThan* cond) OVERRIDE {
+ BinaryFP(cond);
+ }
+
+ void VisitLessThanOrEqual(HLessThanOrEqual* cond) OVERRIDE {
+ BinaryFP(cond);
+ }
+
+ void VisitGreaterThan(HGreaterThan* cond) OVERRIDE {
+ BinaryFP(cond);
+ }
+
+ void VisitGreaterThanOrEqual(HGreaterThanOrEqual* cond) OVERRIDE {
+ BinaryFP(cond);
+ }
+
+ void VisitNeg(HNeg* neg) OVERRIDE {
+ if (Primitive::IsFloatingPointType(neg->GetType())) {
+ // We need to replace the HNeg with a HX86FPNeg in order to address the constant area.
+ InitializePCRelativeBasePointer();
+ HGraph* graph = GetGraph();
+ HBasicBlock* block = neg->GetBlock();
+ HX86FPNeg* x86_fp_neg = new (graph->GetArena()) HX86FPNeg(
+ neg->GetType(),
+ neg->InputAt(0),
+ base_,
+ neg->GetDexPc());
+ block->ReplaceAndRemoveInstructionWith(neg, x86_fp_neg);
+ }
+ }
+
void VisitPackedSwitch(HPackedSwitch* switch_insn) OVERRIDE {
if (switch_insn->GetNumEntries() <=
InstructionCodeGeneratorX86::kPackedSwitchJumpTableThreshold) {
@@ -127,12 +170,23 @@
// If this is an invoke-static/-direct with PC-relative dex cache array
// addressing, we need the PC-relative address base.
HInvokeStaticOrDirect* invoke_static_or_direct = invoke->AsInvokeStaticOrDirect();
+ // We can't add a pointer to the constant area if we already have a current
+ // method pointer. This may arise when sharpening doesn't remove the current
+ // method pointer from the invoke.
+ if (invoke_static_or_direct != nullptr &&
+ invoke_static_or_direct->HasCurrentMethodInput()) {
+ DCHECK(!invoke_static_or_direct->HasPcRelativeDexCache());
+ return;
+ }
+
+ bool base_added = false;
if (invoke_static_or_direct != nullptr && invoke_static_or_direct->HasPcRelativeDexCache()) {
InitializePCRelativeBasePointer();
// Add the extra parameter base_.
- DCHECK(!invoke_static_or_direct->HasCurrentMethodInput());
invoke_static_or_direct->AddSpecialInput(base_);
+ base_added = true;
}
+
// Ensure that we can load FP arguments from the constant area.
for (size_t i = 0, e = invoke->InputCount(); i < e; i++) {
HConstant* input = invoke->InputAt(i)->AsConstant();
@@ -140,6 +194,25 @@
ReplaceInput(invoke, input, i, true);
}
}
+
+ // These intrinsics need the constant area.
+ switch (invoke->GetIntrinsic()) {
+ case Intrinsics::kMathAbsDouble:
+ case Intrinsics::kMathAbsFloat:
+ case Intrinsics::kMathMaxDoubleDouble:
+ case Intrinsics::kMathMaxFloatFloat:
+ case Intrinsics::kMathMinDoubleDouble:
+ case Intrinsics::kMathMinFloatFloat:
+ if (!base_added) {
+ DCHECK(invoke_static_or_direct != nullptr);
+ DCHECK(!invoke_static_or_direct->HasCurrentMethodInput());
+ InitializePCRelativeBasePointer();
+ invoke_static_or_direct->AddSpecialInput(base_);
+ }
+ break;
+ default:
+ break;
+ }
}
// The generated HX86ComputeBaseMethodAddress in the entry block needed as an
diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc
index c56100d..2de0c1b 100644
--- a/compiler/optimizing/pretty_printer_test.cc
+++ b/compiler/optimizing/pretty_printer_test.cc
@@ -30,17 +30,15 @@
static void TestCode(const uint16_t* data, const char* expected) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
- HGraph* graph = CreateGraph(&allocator);
- HGraphBuilder builder(graph);
- const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- bool graph_built = builder.BuildGraph(*item);
- ASSERT_TRUE(graph_built);
+ HGraph* graph = CreateCFG(&allocator, data);
StringPrettyPrinter printer(graph);
printer.VisitInsertionOrder();
ASSERT_STREQ(expected, printer.str().c_str());
}
-TEST(PrettyPrinterTest, ReturnVoid) {
+class PrettyPrinterTest : public CommonCompilerTest {};
+
+TEST_F(PrettyPrinterTest, ReturnVoid) {
const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
Instruction::RETURN_VOID);
@@ -56,7 +54,7 @@
TestCode(data, expected);
}
-TEST(PrettyPrinterTest, CFG1) {
+TEST_F(PrettyPrinterTest, CFG1) {
const char* expected =
"BasicBlock 0, succ: 1\n"
" 3: SuspendCheck\n"
@@ -76,7 +74,7 @@
TestCode(data, expected);
}
-TEST(PrettyPrinterTest, CFG2) {
+TEST_F(PrettyPrinterTest, CFG2) {
const char* expected =
"BasicBlock 0, succ: 1\n"
" 4: SuspendCheck\n"
@@ -98,7 +96,7 @@
TestCode(data, expected);
}
-TEST(PrettyPrinterTest, CFG3) {
+TEST_F(PrettyPrinterTest, CFG3) {
const char* expected =
"BasicBlock 0, succ: 1\n"
" 4: SuspendCheck\n"
@@ -134,16 +132,16 @@
TestCode(data3, expected);
}
-TEST(PrettyPrinterTest, CFG4) {
+TEST_F(PrettyPrinterTest, CFG4) {
const char* expected =
- "BasicBlock 0, succ: 1\n"
+ "BasicBlock 0, succ: 3\n"
" 3: SuspendCheck\n"
- " 4: Goto 1\n"
- "BasicBlock 1, pred: 0, 1, succ: 1\n"
+ " 4: Goto 3\n"
+ "BasicBlock 1, pred: 3, 1, succ: 1\n"
" 0: SuspendCheck\n"
" 1: Goto 1\n"
- "BasicBlock 2\n"
- " 2: Exit\n";
+ "BasicBlock 3, pred: 0, succ: 1\n"
+ " 5: Goto 1\n";
const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM(
Instruction::NOP,
@@ -157,15 +155,13 @@
TestCode(data2, expected);
}
-TEST(PrettyPrinterTest, CFG5) {
+TEST_F(PrettyPrinterTest, CFG5) {
const char* expected =
"BasicBlock 0, succ: 1\n"
" 3: SuspendCheck\n"
" 4: Goto 1\n"
- "BasicBlock 1, pred: 0, 2, succ: 3\n"
+ "BasicBlock 1, pred: 0, succ: 3\n"
" 0: ReturnVoid\n"
- "BasicBlock 2, succ: 1\n"
- " 1: Goto 1\n"
"BasicBlock 3, pred: 1\n"
" 2: Exit\n";
@@ -177,25 +173,23 @@
TestCode(data, expected);
}
-TEST(PrettyPrinterTest, CFG6) {
+TEST_F(PrettyPrinterTest, CFG6) {
const char* expected =
"BasicBlock 0, succ: 1\n"
- " 0: Local [4, 3, 2]\n"
- " 1: IntConstant [2]\n"
+ " 1: IntConstant [5, 5]\n"
" 10: SuspendCheck\n"
" 11: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 3, 2\n"
- " 2: StoreLocal(0, 1)\n"
- " 3: LoadLocal(0) [5]\n"
- " 4: LoadLocal(0) [5]\n"
- " 5: Equal(3, 4) [6]\n"
+ "BasicBlock 1, pred: 0, succ: 5, 2\n"
+ " 5: Equal(1, 1) [6]\n"
" 6: If(5)\n"
"BasicBlock 2, pred: 1, succ: 3\n"
" 7: Goto 3\n"
- "BasicBlock 3, pred: 1, 2, succ: 4\n"
+ "BasicBlock 3, pred: 5, 2, succ: 4\n"
" 8: ReturnVoid\n"
"BasicBlock 4, pred: 3\n"
- " 9: Exit\n";
+ " 9: Exit\n"
+ "BasicBlock 5, pred: 1, succ: 3\n"
+ " 12: Goto 3\n";
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
@@ -206,26 +200,24 @@
TestCode(data, expected);
}
-TEST(PrettyPrinterTest, CFG7) {
+TEST_F(PrettyPrinterTest, CFG7) {
const char* expected =
"BasicBlock 0, succ: 1\n"
- " 0: Local [4, 3, 2]\n"
- " 1: IntConstant [2]\n"
+ " 1: IntConstant [5, 5]\n"
" 11: SuspendCheck\n"
" 12: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 3, 2\n"
- " 2: StoreLocal(0, 1)\n"
- " 3: LoadLocal(0) [5]\n"
- " 4: LoadLocal(0) [5]\n"
- " 5: Equal(3, 4) [6]\n"
+ "BasicBlock 1, pred: 0, succ: 5, 6\n"
+ " 5: Equal(1, 1) [6]\n"
" 6: If(5)\n"
- "BasicBlock 2, pred: 1, 3, succ: 3\n"
+ "BasicBlock 2, pred: 6, 3, succ: 3\n"
" 7: Goto 3\n"
- "BasicBlock 3, pred: 1, 2, succ: 2\n"
+ "BasicBlock 3, pred: 5, 2, succ: 2\n"
" 8: SuspendCheck\n"
" 9: Goto 2\n"
- "BasicBlock 4\n"
- " 10: Exit\n";
+ "BasicBlock 5, pred: 1, succ: 3\n"
+ " 13: Goto 3\n"
+ "BasicBlock 6, pred: 1, succ: 2\n"
+ " 14: Goto 2\n";
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
@@ -236,15 +228,13 @@
TestCode(data, expected);
}
-TEST(PrettyPrinterTest, IntConstant) {
+TEST_F(PrettyPrinterTest, IntConstant) {
const char* expected =
"BasicBlock 0, succ: 1\n"
- " 0: Local [2]\n"
- " 1: IntConstant [2]\n"
+ " 1: IntConstant\n"
" 5: SuspendCheck\n"
" 6: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
- " 2: StoreLocal(0, 1)\n"
" 3: ReturnVoid\n"
"BasicBlock 2, pred: 1\n"
" 4: Exit\n";
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 1224a48..deaa415 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -55,10 +55,12 @@
public:
RTPVisitor(HGraph* graph,
HandleCache* handle_cache,
- ArenaVector<HInstruction*>* worklist)
+ ArenaVector<HInstruction*>* worklist,
+ bool is_first_run)
: HGraphDelegateVisitor(graph),
handle_cache_(handle_cache),
- worklist_(worklist) {}
+ worklist_(worklist),
+ is_first_run_(is_first_run) {}
void VisitNewInstance(HNewInstance* new_instance) OVERRIDE;
void VisitLoadClass(HLoadClass* load_class) OVERRIDE;
@@ -86,14 +88,17 @@
private:
HandleCache* handle_cache_;
ArenaVector<HInstruction*>* worklist_;
+ const bool is_first_run_;
};
ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph,
StackHandleScopeCollection* handles,
+ bool is_first_run,
const char* name)
: HOptimization(graph, name),
handle_cache_(handles),
- worklist_(graph->GetArena()->Adapter(kArenaAllocReferenceTypePropagation)) {
+ worklist_(graph->GetArena()->Adapter(kArenaAllocReferenceTypePropagation)),
+ is_first_run_(is_first_run) {
}
void ReferenceTypePropagation::ValidateTypes() {
@@ -125,7 +130,7 @@
}
void ReferenceTypePropagation::Visit(HInstruction* instruction) {
- RTPVisitor visitor(graph_, &handle_cache_, &worklist_);
+ RTPVisitor visitor(graph_, &handle_cache_, &worklist_, is_first_run_);
instruction->Accept(&visitor);
}
@@ -144,7 +149,7 @@
}
void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
- RTPVisitor visitor(graph_, &handle_cache_, &worklist_);
+ RTPVisitor visitor(graph_, &handle_cache_, &worklist_, is_first_run_);
// Handle Phis first as there might be instructions in the same block who depend on them.
for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
VisitPhi(it.Current()->AsPhi());
@@ -620,6 +625,7 @@
DCHECK_EQ(bound_type->InputAt(0), check_cast->InputAt(0));
if (class_rti.IsValid()) {
+ DCHECK(is_first_run_);
// This is the first run of RTP and class is resolved.
bound_type->SetUpperBound(class_rti, /* CheckCast succeeds for nulls. */ true);
} else {
@@ -636,6 +642,12 @@
}
if (phi->GetBlock()->IsLoopHeader()) {
+ if (!is_first_run_ && graph_->IsCompilingOsr()) {
+ // Don't update the type of a loop phi when compiling OSR: we may have done
+ // speculative optimizations dominating that phi, that do not hold at the
+ // point the interpreter jumps to that loop header.
+ return;
+ }
ScopedObjectAccess soa(Thread::Current());
// Set the initial type for the phi. Use the non back edge input for reaching
// a fixed point faster.
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index a7f10a6..028a6fc 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -33,6 +33,7 @@
public:
ReferenceTypePropagation(HGraph* graph,
StackHandleScopeCollection* handles,
+ bool is_first_run,
const char* name = kReferenceTypePropagationPassName);
// Visit a single instruction.
@@ -93,6 +94,8 @@
ArenaVector<HInstruction*> worklist_;
+ // Whether this reference type propagation is the first run we are doing.
+ const bool is_first_run_;
static constexpr size_t kDefaultWorklistSize = 8;
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index 5cd30ad..b8d76b9 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -994,10 +994,6 @@
return false;
}
- // We use the first use to compare with other intervals. If this interval
- // is used after any active intervals, we will spill this interval.
- size_t first_use = current->FirstUseAfter(current->GetStart());
-
// First set all registers as not being used.
size_t* next_use = registers_array_;
for (size_t i = 0; i < number_of_registers_; ++i) {
@@ -1011,7 +1007,7 @@
if (active->IsFixed()) {
next_use[active->GetRegister()] = current->GetStart();
} else {
- size_t use = active->FirstUseAfter(current->GetStart());
+ size_t use = active->FirstRegisterUseAfter(current->GetStart());
if (use != kNoLifetime) {
next_use[active->GetRegister()] = use;
}
@@ -1052,16 +1048,16 @@
DCHECK(current->IsHighInterval());
reg = current->GetRegister();
// When allocating the low part, we made sure the high register was available.
- DCHECK_LT(first_use, next_use[reg]);
+ DCHECK_LT(first_register_use, next_use[reg]);
} else if (current->IsLowInterval()) {
- reg = FindAvailableRegisterPair(next_use, first_use);
+ reg = FindAvailableRegisterPair(next_use, first_register_use);
// We should spill if both registers are not available.
- should_spill = (first_use >= next_use[reg])
- || (first_use >= next_use[GetHighForLowRegister(reg)]);
+ should_spill = (first_register_use >= next_use[reg])
+ || (first_register_use >= next_use[GetHighForLowRegister(reg)]);
} else {
DCHECK(!current->IsHighInterval());
reg = FindAvailableRegister(next_use, current);
- should_spill = (first_use >= next_use[reg]);
+ should_spill = (first_register_use >= next_use[reg]);
}
DCHECK_NE(reg, kNoRegister);
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index 572faa8..a9de7c3 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -38,11 +38,7 @@
static bool Check(const uint16_t* data) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
- HGraph* graph = CreateGraph(&allocator);
- HGraphBuilder builder(graph);
- const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- builder.BuildGraph(*item);
- TransformToSsa(graph);
+ HGraph* graph = CreateCFG(&allocator, data);
std::unique_ptr<const X86InstructionSetFeatures> features_x86(
X86InstructionSetFeatures::FromCppDefines());
x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions());
@@ -254,15 +250,6 @@
ASSERT_TRUE(Check(data));
}
-static HGraph* BuildSSAGraph(const uint16_t* data, ArenaAllocator* allocator) {
- HGraph* graph = CreateGraph(allocator);
- HGraphBuilder builder(graph);
- const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- builder.BuildGraph(*item);
- TransformToSsa(graph);
- return graph;
-}
-
TEST_F(RegisterAllocatorTest, Loop3) {
/*
* Test the following snippet:
@@ -302,7 +289,7 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
- HGraph* graph = BuildSSAGraph(data, &allocator);
+ HGraph* graph = CreateCFG(&allocator, data);
std::unique_ptr<const X86InstructionSetFeatures> features_x86(
X86InstructionSetFeatures::FromCppDefines());
x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions());
@@ -336,7 +323,7 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
- HGraph* graph = BuildSSAGraph(data, &allocator);
+ HGraph* graph = CreateCFG(&allocator, data);
std::unique_ptr<const X86InstructionSetFeatures> features_x86(
X86InstructionSetFeatures::FromCppDefines());
x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions());
@@ -390,7 +377,7 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
- HGraph* graph = BuildSSAGraph(data, &allocator);
+ HGraph* graph = CreateCFG(&allocator, data);
SsaDeadPhiElimination(graph).Run();
std::unique_ptr<const X86InstructionSetFeatures> features_x86(
X86InstructionSetFeatures::FromCppDefines());
@@ -414,7 +401,7 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
- HGraph* graph = BuildSSAGraph(data, &allocator);
+ HGraph* graph = CreateCFG(&allocator, data);
SsaDeadPhiElimination(graph).Run();
std::unique_ptr<const X86InstructionSetFeatures> features_x86(
X86InstructionSetFeatures::FromCppDefines());
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 165d09d..43f2499 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -430,8 +430,6 @@
}
for (HNewInstance* new_instance : uninitialized_strings_) {
- DCHECK(new_instance->IsStringAlloc());
-
// Replace NewInstance of String with NullConstant if not used prior to
// calling StringFactory. In case of deoptimization, the interpreter is
// expected to skip null check on the `this` argument of the StringFactory call.
@@ -440,10 +438,26 @@
new_instance->GetBlock()->RemoveInstruction(new_instance);
// Remove LoadClass if not needed any more.
- HLoadClass* load_class = new_instance->InputAt(0)->AsLoadClass();
+ HInstruction* input = new_instance->InputAt(0);
+ HLoadClass* load_class = nullptr;
+
+ // If the class was not present in the dex cache at the point of building
+ // the graph, the builder inserted a HClinitCheck in between. Since the String
+ // class is always initialized at the point of running Java code, we can remove
+ // that check.
+ if (input->IsClinitCheck()) {
+ load_class = input->InputAt(0)->AsLoadClass();
+ input->ReplaceWith(load_class);
+ input->GetBlock()->RemoveInstruction(input);
+ } else {
+ load_class = input->AsLoadClass();
+ DCHECK(new_instance->IsStringAlloc());
+ DCHECK(!load_class->NeedsAccessCheck()) << "String class is always accessible";
+ }
DCHECK(load_class != nullptr);
- DCHECK(!load_class->NeedsAccessCheck()) << "String class is always accessible";
if (!load_class->HasUses()) {
+ // Even if the HLoadClass needs access check, we can remove it, as we know the
+ // String class does not need it.
load_class->GetBlock()->RemoveInstruction(load_class);
}
}
@@ -451,6 +465,8 @@
}
GraphAnalysisResult SsaBuilder::BuildSsa() {
+ DCHECK(!GetGraph()->IsInSsaForm());
+
// 1) Visit in reverse post order. We need to have all predecessors of a block
// visited (with the exception of loops) in order to create the right environment
// for that block. For loops, we create phis whose inputs will be set in 2).
@@ -483,7 +499,7 @@
// 6) Compute type of reference type instructions. The pass assumes that
// NullConstant has been fixed up.
- ReferenceTypePropagation(GetGraph(), handles_).Run();
+ ReferenceTypePropagation(GetGraph(), handles_, /* is_first_run */ true).Run();
// 7) Step 1) duplicated ArrayGet instructions with ambiguous type (int/float
// or long/double) and marked ArraySets with ambiguous input type. Now that RTP
@@ -533,6 +549,7 @@
}
}
+ GetGraph()->SetInSsaForm();
return kAnalysisSuccess;
}
@@ -899,11 +916,6 @@
}
}
-void SsaBuilder::VisitTemporary(HTemporary* temp) {
- // Temporaries are only used by the baseline register allocator.
- temp->GetBlock()->RemoveInstruction(temp);
-}
-
void SsaBuilder::VisitArrayGet(HArrayGet* aget) {
Primitive::Type type = aget->GetType();
DCHECK(!Primitive::IsFloatingPointType(type));
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index ccef8ea..2dae9c2 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -75,13 +75,10 @@
void VisitLoadLocal(HLoadLocal* load) OVERRIDE;
void VisitStoreLocal(HStoreLocal* store) OVERRIDE;
void VisitInstruction(HInstruction* instruction) OVERRIDE;
- void VisitTemporary(HTemporary* instruction) OVERRIDE;
void VisitArrayGet(HArrayGet* aget) OVERRIDE;
void VisitArraySet(HArraySet* aset) OVERRIDE;
void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE;
- static constexpr const char* kSsaBuilderPassName = "ssa_builder";
-
private:
void SetLoopHeaderPhiInputs();
void FixEnvironmentPhis();
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index 1dd3508..83e9dac 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -226,7 +226,7 @@
// The only instructions which may not be recorded in the environments
// are constants created by the SSA builder as typed equivalents of
// untyped constants from the bytecode, or phis with only such constants
- // as inputs (verified by SSAChecker). Their raw binary value must
+ // as inputs (verified by GraphChecker). Their raw binary value must
// therefore be the same and we only need to keep alive one.
} else {
size_t phi_input_index = successor->GetPredecessorIndexOf(block);
diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc
index d2885a8..a688092 100644
--- a/compiler/optimizing/ssa_test.cc
+++ b/compiler/optimizing/ssa_test.cc
@@ -79,13 +79,7 @@
static void TestCode(const uint16_t* data, const char* expected) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
- HGraph* graph = CreateGraph(&allocator);
- HGraphBuilder builder(graph);
- const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- bool graph_built = builder.BuildGraph(*item);
- ASSERT_TRUE(graph_built);
-
- TransformToSsa(graph);
+ HGraph* graph = CreateCFG(&allocator, data);
// Suspend checks implementation may change in the future, and this test relies
// on how instructions are ordered.
RemoveSuspendChecks(graph);
diff --git a/compiler/optimizing/suspend_check_test.cc b/compiler/optimizing/suspend_check_test.cc
index b6c704c..15cd4e8 100644
--- a/compiler/optimizing/suspend_check_test.cc
+++ b/compiler/optimizing/suspend_check_test.cc
@@ -18,6 +18,7 @@
#include "dex_instruction.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
+#include "pretty_printer.h"
#include "gtest/gtest.h"
@@ -30,20 +31,17 @@
static void TestCode(const uint16_t* data) {
ArenaPool pool;
ArenaAllocator allocator(&pool);
- HGraph* graph = CreateGraph(&allocator);
- HGraphBuilder builder(graph);
- const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
- bool graph_built = builder.BuildGraph(*item);
- ASSERT_TRUE(graph_built);
-
- HBasicBlock* first_block = graph->GetEntryBlock()->GetSuccessors()[0];
- HInstruction* first_instruction = first_block->GetFirstInstruction();
- // Account for some tests having a store local as first instruction.
- ASSERT_TRUE(first_instruction->IsSuspendCheck()
- || first_instruction->GetNext()->IsSuspendCheck());
+ HGraph* graph = CreateCFG(&allocator, data);
+ HBasicBlock* first_block = graph->GetEntryBlock()->GetSingleSuccessor();
+ HBasicBlock* loop_header = first_block->GetSingleSuccessor();
+ ASSERT_TRUE(loop_header->IsLoopHeader());
+ ASSERT_EQ(loop_header->GetLoopInformation()->GetPreHeader(), first_block);
+ ASSERT_TRUE(loop_header->GetFirstInstruction()->IsSuspendCheck());
}
-TEST(CodegenTest, CFG1) {
+class SuspendCheckTest : public CommonCompilerTest {};
+
+TEST_F(SuspendCheckTest, CFG1) {
const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
Instruction::NOP,
Instruction::GOTO | 0xFF00);
@@ -51,14 +49,14 @@
TestCode(data);
}
-TEST(CodegenTest, CFG2) {
+TEST_F(SuspendCheckTest, CFG2) {
const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
Instruction::GOTO_32, 0, 0);
TestCode(data);
}
-TEST(CodegenTest, CFG3) {
+TEST_F(SuspendCheckTest, CFG3) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::IF_EQ, 0xFFFF,
@@ -67,7 +65,7 @@
TestCode(data);
}
-TEST(CodegenTest, CFG4) {
+TEST_F(SuspendCheckTest, CFG4) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::IF_NE, 0xFFFF,
@@ -76,7 +74,7 @@
TestCode(data);
}
-TEST(CodegenTest, CFG5) {
+TEST_F(SuspendCheckTest, CFG5) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::IF_EQZ, 0xFFFF,
@@ -85,7 +83,7 @@
TestCode(data);
}
-TEST(CodegenTest, CFG6) {
+TEST_F(SuspendCheckTest, CFG6) {
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
Instruction::IF_NEZ, 0xFFFF,
diff --git a/compiler/utils/arm/managed_register_arm.h b/compiler/utils/arm/managed_register_arm.h
index 5fde9e8..5b84058 100644
--- a/compiler/utils/arm/managed_register_arm.h
+++ b/compiler/utils/arm/managed_register_arm.h
@@ -19,7 +19,7 @@
#include "base/logging.h"
#include "constants_arm.h"
-#include "dwarf/register.h"
+#include "debug/dwarf/register.h"
#include "utils/managed_register.h"
namespace art {
diff --git a/compiler/utils/arm64/managed_register_arm64.h b/compiler/utils/arm64/managed_register_arm64.h
index dbcd8c5..46be1c5 100644
--- a/compiler/utils/arm64/managed_register_arm64.h
+++ b/compiler/utils/arm64/managed_register_arm64.h
@@ -19,7 +19,7 @@
#include "base/logging.h"
#include "constants_arm64.h"
-#include "dwarf/register.h"
+#include "debug/dwarf/register.h"
#include "utils/managed_register.h"
namespace art {
diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h
index 1dbc142..414ea7e 100644
--- a/compiler/utils/assembler.h
+++ b/compiler/utils/assembler.h
@@ -21,9 +21,10 @@
#include "arch/instruction_set.h"
#include "arch/instruction_set_features.h"
+#include "arm/constants_arm.h"
#include "base/logging.h"
#include "base/macros.h"
-#include "arm/constants_arm.h"
+#include "debug/dwarf/debug_frame_opcode_writer.h"
#include "label.h"
#include "managed_register.h"
#include "memory_region.h"
@@ -31,7 +32,6 @@
#include "offsets.h"
#include "x86/constants_x86.h"
#include "x86_64/constants_x86_64.h"
-#include "dwarf/debug_frame_opcode_writer.h"
namespace art {
diff --git a/compiler/utils/mips/managed_register_mips.h b/compiler/utils/mips/managed_register_mips.h
index 40d39e3..5e7ed11 100644
--- a/compiler/utils/mips/managed_register_mips.h
+++ b/compiler/utils/mips/managed_register_mips.h
@@ -18,7 +18,7 @@
#define ART_COMPILER_UTILS_MIPS_MANAGED_REGISTER_MIPS_H_
#include "constants_mips.h"
-#include "dwarf/register.h"
+#include "debug/dwarf/register.h"
#include "utils/managed_register.h"
namespace art {
diff --git a/compiler/utils/mips64/managed_register_mips64.h b/compiler/utils/mips64/managed_register_mips64.h
index 4c4705b..1d36128 100644
--- a/compiler/utils/mips64/managed_register_mips64.h
+++ b/compiler/utils/mips64/managed_register_mips64.h
@@ -18,7 +18,7 @@
#define ART_COMPILER_UTILS_MIPS64_MANAGED_REGISTER_MIPS64_H_
#include "constants_mips64.h"
-#include "dwarf/register.h"
+#include "debug/dwarf/register.h"
#include "utils/managed_register.h"
namespace art {
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index 7138a46..3efef70 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -326,6 +326,14 @@
}
+void X86Assembler::cmovl(Condition condition, Register dst, const Address& src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x0F);
+ EmitUint8(0x40 + condition);
+ EmitOperand(dst, src);
+}
+
+
void X86Assembler::setb(Condition condition, Register dst) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x0F);
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index 759a41e..00ff7bd 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -363,6 +363,7 @@
void leal(Register dst, const Address& src);
void cmovl(Condition condition, Register dst, Register src);
+ void cmovl(Condition condition, Register dst, const Address& src);
void setb(Condition condition, Register dst);
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index 0fd0982..d0d5147 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -332,6 +332,21 @@
}
+TEST_F(AssemblerX86Test, CmovlAddress) {
+ GetAssembler()->cmovl(x86::kEqual, x86::Register(x86::EAX), x86::Address(
+ x86::Register(x86::EDI), x86::Register(x86::EBX), x86::TIMES_4, 12));
+ GetAssembler()->cmovl(x86::kNotEqual, x86::Register(x86::EDI), x86::Address(
+ x86::Register(x86::ESI), x86::Register(x86::EBX), x86::TIMES_4, 12));
+ GetAssembler()->cmovl(x86::kEqual, x86::Register(x86::EDI), x86::Address(
+ x86::Register(x86::EDI), x86::Register(x86::EAX), x86::TIMES_4, 12));
+ const char* expected =
+ "cmovzl 0xc(%EDI,%EBX,4), %eax\n"
+ "cmovnzl 0xc(%ESI,%EBX,4), %edi\n"
+ "cmovzl 0xc(%EDI,%EAX,4), %edi\n";
+
+ DriverStr(expected, "cmovl_address");
+}
+
/////////////////
// Near labels //
/////////////////
diff --git a/compiler/utils/x86/managed_register_x86.h b/compiler/utils/x86/managed_register_x86.h
index 4e8c41e..fc20d7e 100644
--- a/compiler/utils/x86/managed_register_x86.h
+++ b/compiler/utils/x86/managed_register_x86.h
@@ -18,7 +18,7 @@
#define ART_COMPILER_UTILS_X86_MANAGED_REGISTER_X86_H_
#include "constants_x86.h"
-#include "dwarf/register.h"
+#include "debug/dwarf/register.h"
#include "utils/managed_register.h"
namespace art {
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 10f5a00..d86ad1b 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -223,6 +223,19 @@
}
+void X86_64Assembler::cmov(Condition c, CpuRegister dst, const Address& src, bool is64bit) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ if (is64bit) {
+ EmitRex64(dst, src);
+ } else {
+ EmitOptionalRex32(dst, src);
+ }
+ EmitUint8(0x0F);
+ EmitUint8(0x40 + c);
+ EmitOperand(dst.LowBits(), src);
+}
+
+
void X86_64Assembler::movzxb(CpuRegister dst, CpuRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitOptionalByteRegNormalizingRex32(dst, src);
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 6f0847e..f00cb12 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -366,6 +366,7 @@
void cmov(Condition c, CpuRegister dst, CpuRegister src); // This is the 64b version.
void cmov(Condition c, CpuRegister dst, CpuRegister src, bool is64bit);
+ void cmov(Condition c, CpuRegister dst, const Address& src, bool is64bit);
void movzxb(CpuRegister dst, CpuRegister src);
void movzxb(CpuRegister dst, const Address& src);
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index 8a87fca..4f65709 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -1371,6 +1371,37 @@
DriverStr(expected, "popcntq_address");
}
+TEST_F(AssemblerX86_64Test, CmovlAddress) {
+ GetAssembler()->cmov(x86_64::kEqual, x86_64::CpuRegister(x86_64::R10), x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12), false);
+ GetAssembler()->cmov(x86_64::kNotEqual, x86_64::CpuRegister(x86_64::RDI), x86_64::Address(
+ x86_64::CpuRegister(x86_64::R10), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12), false);
+ GetAssembler()->cmov(x86_64::kEqual, x86_64::CpuRegister(x86_64::RDI), x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12), false);
+ const char* expected =
+ "cmovzl 0xc(%RDI,%RBX,4), %R10d\n"
+ "cmovnzl 0xc(%R10,%RBX,4), %edi\n"
+ "cmovzl 0xc(%RDI,%R9,4), %edi\n";
+
+ DriverStr(expected, "cmovl_address");
+}
+
+TEST_F(AssemblerX86_64Test, CmovqAddress) {
+ GetAssembler()->cmov(x86_64::kEqual, x86_64::CpuRegister(x86_64::R10), x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12), true);
+ GetAssembler()->cmov(x86_64::kNotEqual, x86_64::CpuRegister(x86_64::RDI), x86_64::Address(
+ x86_64::CpuRegister(x86_64::R10), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12), true);
+ GetAssembler()->cmov(x86_64::kEqual, x86_64::CpuRegister(x86_64::RDI), x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12), true);
+ const char* expected =
+ "cmovzq 0xc(%RDI,%RBX,4), %R10\n"
+ "cmovnzq 0xc(%R10,%RBX,4), %rdi\n"
+ "cmovzq 0xc(%RDI,%R9,4), %rdi\n";
+
+ DriverStr(expected, "cmovq_address");
+}
+
+
/////////////////
// Near labels //
/////////////////
diff --git a/compiler/utils/x86_64/managed_register_x86_64.h b/compiler/utils/x86_64/managed_register_x86_64.h
index 47bbb44..c4228c1 100644
--- a/compiler/utils/x86_64/managed_register_x86_64.h
+++ b/compiler/utils/x86_64/managed_register_x86_64.h
@@ -18,7 +18,7 @@
#define ART_COMPILER_UTILS_X86_64_MANAGED_REGISTER_X86_64_H_
#include "constants_x86_64.h"
-#include "dwarf/register.h"
+#include "debug/dwarf/register.h"
#include "utils/managed_register.h"
namespace art {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index b3e3ba6..541fb5a 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -35,9 +35,9 @@
#define ATRACE_TAG ATRACE_TAG_DALVIK
#include <cutils/trace.h>
-#include "art_method-inl.h"
#include "arch/instruction_set_features.h"
#include "arch/mips/instruction_set_features_mips.h"
+#include "art_method-inl.h"
#include "base/dumpable.h"
#include "base/macros.h"
#include "base/stl_util.h"
@@ -48,14 +48,14 @@
#include "class_linker.h"
#include "compiler.h"
#include "compiler_callbacks.h"
-#include "dex_file-inl.h"
+#include "debug/method_debug_info.h"
#include "dex/pass_manager.h"
-#include "dex/verification_results.h"
-#include "dex/quick_compiler_callbacks.h"
#include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "dex/quick_compiler_callbacks.h"
+#include "dex/verification_results.h"
+#include "dex_file-inl.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
-#include "dwarf/method_debug_info.h"
#include "elf_file.h"
#include "elf_writer.h"
#include "elf_writer_quick.h"
@@ -126,6 +126,11 @@
continue;
}
+ // The image format is dropped.
+ if (StartsWith(original_argv[i], "--image-format=")) {
+ continue;
+ }
+
// This should leave any dex-file and oat-file options, describing what we compiled.
// However, we prefer to drop this when we saw --zip-fd.
@@ -1682,6 +1687,12 @@
std::vector<const DexFile*>& dex_files = dex_files_per_oat_file_[i];
oat_writer->PrepareLayout(driver_.get(), image_writer_.get(), dex_files);
+ // We need to mirror the layout of the ELF file in the compressed debug-info.
+ // Therefore we need to propagate the sizes of at least those sections.
+ size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset();
+ size_t text_size = oat_writer->GetSize() - rodata_size;
+ elf_writer->PrepareDebugInfo(rodata_size, text_size, oat_writer->GetMethodDebugInfo());
+
OutputStream*& rodata = rodata_[i];
DCHECK(rodata != nullptr);
if (!oat_writer->WriteRodata(rodata)) {
diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc
index 5e2cf6b..0e709eb 100644
--- a/disassembler/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -1152,8 +1152,10 @@
args << Rd << ", #" << imm16;
break;
}
- case 0x16: {
+ case 0x16: case 0x14: case 0x1C: {
// BFI Rd, Rn, #lsb, #width - 111 10 0 11 011 0 nnnn 0 iii dddd ii 0 iiiii
+ // SBFX Rd, Rn, #lsb, #width - 111 10 0 11 010 0 nnnn 0 iii dddd ii 0 iiiii
+ // UBFX Rd, Rn, #lsb, #width - 111 10 0 11 110 0 nnnn 0 iii dddd ii 0 iiiii
ArmRegister Rd(instr, 8);
ArmRegister Rn(instr, 16);
uint32_t msb = instr & 0x1F;
@@ -1161,12 +1163,21 @@
uint32_t imm3 = (instr >> 12) & 0x7;
uint32_t lsb = (imm3 << 2) | imm2;
uint32_t width = msb - lsb + 1;
- if (Rn.r != 0xF) {
- opcode << "bfi";
- args << Rd << ", " << Rn << ", #" << lsb << ", #" << width;
+ if (op3 == 0x16) {
+ if (Rn.r != 0xF) {
+ opcode << "bfi";
+ args << Rd << ", " << Rn << ", #" << lsb << ", #" << width;
+ } else {
+ opcode << "bfc";
+ args << Rd << ", #" << lsb << ", #" << width;
+ }
} else {
- opcode << "bfc";
- args << Rd << ", #" << lsb << ", #" << width;
+ opcode << ((op3 & 0x8) != 0u ? "ubfx" : "sbfx");
+ args << Rd << ", " << Rn << ", #" << lsb << ", #" << width;
+ if (Rd.r == 13 || Rd.r == 15 || Rn.r == 13 || Rn.r == 15 ||
+ (instr & 0x04000020) != 0u) {
+ args << " (UNPREDICTABLE)";
+ }
}
break;
}
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 1668dc5..1d80bda 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -150,30 +150,17 @@
}
}
-bool PatchOat::Patch(File* input_oat, const std::string& image_location, off_t delta,
- File* output_oat, File* output_image, InstructionSet isa,
- TimingLogger* timings,
- bool output_oat_opened_from_fd ATTRIBUTE_UNUSED,
- bool new_oat_out) {
+bool PatchOat::Patch(const std::string& image_location,
+ off_t delta,
+ const std::string& output_directory,
+ InstructionSet isa,
+ TimingLogger* timings) {
CHECK(Runtime::Current() == nullptr);
- CHECK(output_image != nullptr);
- CHECK_GE(output_image->Fd(), 0);
- CHECK(input_oat != nullptr);
- CHECK(output_oat != nullptr);
- CHECK_GE(input_oat->Fd(), 0);
- CHECK_GE(output_oat->Fd(), 0);
CHECK(!image_location.empty()) << "image file must have a filename.";
TimingLogger::ScopedTiming t("Runtime Setup", timings);
- if (isa == kNone) {
- Elf32_Ehdr elf_hdr;
- if (sizeof(elf_hdr) != input_oat->Read(reinterpret_cast<char*>(&elf_hdr), sizeof(elf_hdr), 0)) {
- LOG(ERROR) << "unable to read elf header";
- return false;
- }
- isa = GetInstructionSetFromELF(elf_hdr.e_machine, elf_hdr.e_flags);
- }
+ CHECK_NE(isa, kNone);
const char* isa_name = GetInstructionSetString(isa);
// Set up the runtime
@@ -193,8 +180,6 @@
Thread::Current()->TransitionFromRunnableToSuspended(kNative);
ScopedObjectAccess soa(Thread::Current());
- std::string output_directory =
- output_image->GetPath().substr(0, output_image->GetPath().find_last_of("/"));
t.NewTiming("Image and oat Patching setup");
std::vector<gc::space::ImageSpace*> spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces();
std::map<gc::space::ImageSpace*, std::unique_ptr<File>> space_to_file_map;
@@ -325,6 +310,7 @@
std::string output_image_filename = output_directory +
(StartsWith(converted_image_filename, "/") ? "" : "/") +
converted_image_filename;
+ bool new_oat_out;
std::unique_ptr<File>
output_image_file(CreateOrOpen(output_image_filename.c_str(), &new_oat_out));
if (output_image_file.get() == nullptr) {
@@ -932,21 +918,9 @@
UsageError(" --output-image-file=<file.art>: Specifies the exact file to write the patched");
UsageError(" image file to.");
UsageError("");
- UsageError(" --output-image-fd=<file-descriptor>: Specifies the file-descriptor to write the");
- UsageError(" the patched image file to.");
- UsageError("");
- UsageError(" --orig-base-offset=<original-base-offset>: Specify the base offset the input file");
- UsageError(" was compiled with. This is needed if one is specifying a --base-offset");
- UsageError("");
- UsageError(" --base-offset=<new-base-offset>: Specify the base offset we will repatch the");
- UsageError(" given files to use. This requires that --orig-base-offset is also given.");
- UsageError("");
UsageError(" --base-offset-delta=<delta>: Specify the amount to change the old base-offset by.");
UsageError(" This value may be negative.");
UsageError("");
- UsageError(" --patched-image-file=<file.art>: Relocate the oat file to be the same as the");
- UsageError(" given image file.");
- UsageError("");
UsageError(" --patched-image-location=<file.art>: Relocate the oat file to be the same as the");
UsageError(" image at the given location. If used one must also specify the");
UsageError(" --instruction-set flag. It will search for this image in the same way that");
@@ -992,6 +966,244 @@
return true;
}
+static int patchoat_image(TimingLogger& timings,
+ InstructionSet isa,
+ const std::string& input_image_location,
+ const std::string& output_image_filename,
+ off_t base_delta,
+ bool base_delta_set,
+ bool debug) {
+ CHECK(!input_image_location.empty());
+ if (output_image_filename.empty()) {
+ Usage("Image patching requires --output-image-file");
+ }
+
+ if (!base_delta_set) {
+ Usage("Must supply a desired new offset or delta.");
+ }
+
+ if (!IsAligned<kPageSize>(base_delta)) {
+ Usage("Base offset/delta must be aligned to a pagesize (0x%08x) boundary.", kPageSize);
+ }
+
+ if (debug) {
+ LOG(INFO) << "moving offset by " << base_delta
+ << " (0x" << std::hex << base_delta << ") bytes or "
+ << std::dec << (base_delta/kPageSize) << " pages.";
+ }
+
+ TimingLogger::ScopedTiming pt("patch image and oat", &timings);
+
+ std::string output_directory =
+ output_image_filename.substr(0, output_image_filename.find_last_of("/"));
+ bool ret = PatchOat::Patch(input_image_location, base_delta, output_directory, isa, &timings);
+
+ if (kIsDebugBuild) {
+ LOG(INFO) << "Exiting with return ... " << ret;
+ }
+ return ret ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+static int patchoat_oat(TimingLogger& timings,
+ InstructionSet isa,
+ const std::string& patched_image_location,
+ off_t base_delta,
+ bool base_delta_set,
+ int input_oat_fd,
+ const std::string& input_oat_location,
+ std::string input_oat_filename,
+ bool have_input_oat,
+ int output_oat_fd,
+ std::string output_oat_filename,
+ bool have_output_oat,
+ bool lock_output,
+ bool debug) {
+ {
+ // Only 1 of these may be set.
+ uint32_t cnt = 0;
+ cnt += (base_delta_set) ? 1 : 0;
+ cnt += (!patched_image_location.empty()) ? 1 : 0;
+ if (cnt > 1) {
+ Usage("Only one of --base-offset-delta or --patched-image-location may be used.");
+ } else if (cnt == 0) {
+ Usage("Must specify --base-offset-delta or --patched-image-location.");
+ }
+ }
+
+ if (!have_input_oat || !have_output_oat) {
+ Usage("Both input and output oat must be supplied to patch an app odex.");
+ }
+
+ if (!input_oat_location.empty()) {
+ if (!LocationToFilename(input_oat_location, isa, &input_oat_filename)) {
+ Usage("Unable to find filename for input oat location %s", input_oat_location.c_str());
+ }
+ if (debug) {
+ LOG(INFO) << "Using input-oat-file " << input_oat_filename;
+ }
+ }
+
+ bool match_delta = false;
+ if (!patched_image_location.empty()) {
+ std::string system_filename;
+ bool has_system = false;
+ std::string cache_filename;
+ bool has_cache = false;
+ bool has_android_data_unused = false;
+ bool is_global_cache = false;
+ if (!gc::space::ImageSpace::FindImageFilename(patched_image_location.c_str(), isa,
+ &system_filename, &has_system, &cache_filename,
+ &has_android_data_unused, &has_cache,
+ &is_global_cache)) {
+ Usage("Unable to determine image file for location %s", patched_image_location.c_str());
+ }
+ std::string patched_image_filename;
+ if (has_cache) {
+ patched_image_filename = cache_filename;
+ } else if (has_system) {
+ LOG(WARNING) << "Only image file found was in /system for image location "
+ << patched_image_location;
+ patched_image_filename = system_filename;
+ } else {
+ Usage("Unable to determine image file for location %s", patched_image_location.c_str());
+ }
+ if (debug) {
+ LOG(INFO) << "Using patched-image-file " << patched_image_filename;
+ }
+
+ base_delta_set = true;
+ match_delta = true;
+ std::string error_msg;
+ if (!ReadBaseDelta(patched_image_filename.c_str(), &base_delta, &error_msg)) {
+ Usage(error_msg.c_str(), patched_image_filename.c_str());
+ }
+ }
+
+ if (!IsAligned<kPageSize>(base_delta)) {
+ Usage("Base offset/delta must be alligned to a pagesize (0x%08x) boundary.", kPageSize);
+ }
+
+ // Do we need to cleanup output files if we fail?
+ bool new_oat_out = false;
+
+ std::unique_ptr<File> input_oat;
+ std::unique_ptr<File> output_oat;
+
+ if (input_oat_fd != -1) {
+ if (input_oat_filename.empty()) {
+ input_oat_filename = "input-oat-file";
+ }
+ input_oat.reset(new File(input_oat_fd, input_oat_filename, false));
+ if (input_oat_fd == output_oat_fd) {
+ input_oat.get()->DisableAutoClose();
+ }
+ if (input_oat == nullptr) {
+ // Unlikely, but ensure exhaustive logging in non-0 exit code case
+ LOG(ERROR) << "Failed to open input oat file by its FD" << input_oat_fd;
+ }
+ } else {
+ CHECK(!input_oat_filename.empty());
+ input_oat.reset(OS::OpenFileForReading(input_oat_filename.c_str()));
+ if (input_oat == nullptr) {
+ int err = errno;
+ LOG(ERROR) << "Failed to open input oat file " << input_oat_filename
+ << ": " << strerror(err) << "(" << err << ")";
+ }
+ }
+
+ if (output_oat_fd != -1) {
+ if (output_oat_filename.empty()) {
+ output_oat_filename = "output-oat-file";
+ }
+ output_oat.reset(new File(output_oat_fd, output_oat_filename, true));
+ if (output_oat == nullptr) {
+ // Unlikely, but ensure exhaustive logging in non-0 exit code case
+ LOG(ERROR) << "Failed to open output oat file by its FD" << output_oat_fd;
+ }
+ } else {
+ CHECK(!output_oat_filename.empty());
+ output_oat.reset(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out));
+ if (output_oat == nullptr) {
+ int err = errno;
+ LOG(ERROR) << "Failed to open output oat file " << output_oat_filename
+ << ": " << strerror(err) << "(" << err << ")";
+ }
+ }
+
+ // TODO: get rid of this.
+ auto cleanup = [&output_oat_filename, &new_oat_out](bool success) {
+ if (!success) {
+ if (new_oat_out) {
+ CHECK(!output_oat_filename.empty());
+ TEMP_FAILURE_RETRY(unlink(output_oat_filename.c_str()));
+ }
+ }
+
+ if (kIsDebugBuild) {
+ LOG(INFO) << "Cleaning up.. success? " << success;
+ }
+ };
+
+ if (input_oat.get() == nullptr || output_oat.get() == nullptr) {
+ LOG(ERROR) << "Failed to open input/output oat files";
+ cleanup(false);
+ return EXIT_FAILURE;
+ }
+
+ if (match_delta) {
+ std::string error_msg;
+ // Figure out what the current delta is so we can match it to the desired delta.
+ std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat.get(), PROT_READ, MAP_PRIVATE,
+ &error_msg));
+ off_t current_delta = 0;
+ if (elf.get() == nullptr) {
+ LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg;
+ cleanup(false);
+ return EXIT_FAILURE;
+ } else if (!ReadOatPatchDelta(elf.get(), ¤t_delta, &error_msg)) {
+ LOG(ERROR) << "Unable to get current delta: " << error_msg;
+ cleanup(false);
+ return EXIT_FAILURE;
+ }
+ // Before this line base_delta is the desired final delta. We need it to be the actual amount to
+ // change everything by. We subtract the current delta from it to make it this.
+ base_delta -= current_delta;
+ if (!IsAligned<kPageSize>(base_delta)) {
+ LOG(ERROR) << "Given image file was relocated by an illegal delta";
+ cleanup(false);
+ return false;
+ }
+ }
+
+ if (debug) {
+ LOG(INFO) << "moving offset by " << base_delta
+ << " (0x" << std::hex << base_delta << ") bytes or "
+ << std::dec << (base_delta/kPageSize) << " pages.";
+ }
+
+ ScopedFlock output_oat_lock;
+ if (lock_output) {
+ std::string error_msg;
+ if (!output_oat_lock.Init(output_oat.get(), &error_msg)) {
+ LOG(ERROR) << "Unable to lock output oat " << output_oat->GetPath() << ": " << error_msg;
+ cleanup(false);
+ return EXIT_FAILURE;
+ }
+ }
+
+ TimingLogger::ScopedTiming pt("patch oat", &timings);
+ bool ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings,
+ output_oat_fd >= 0, // was it opened from FD?
+ new_oat_out);
+ ret = FinishFile(output_oat.get(), ret);
+
+ if (kIsDebugBuild) {
+ LOG(INFO) << "Exiting with return ... " << ret;
+ }
+ cleanup(ret);
+ return ret ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
static int patchoat(int argc, char **argv) {
InitLogging(argv);
MemMap::Init();
@@ -1024,15 +1236,8 @@
int output_oat_fd = -1;
bool have_output_oat = false;
std::string output_image_filename;
- int output_image_fd = -1;
- bool have_output_image = false;
- uintptr_t base_offset = 0;
- bool base_offset_set = false;
- uintptr_t orig_base_offset = 0;
- bool orig_base_offset_set = false;
off_t base_delta = 0;
bool base_delta_set = false;
- bool match_delta = false;
std::string patched_image_filename;
std::string patched_image_location;
bool dump_timings = kIsDebugBuild;
@@ -1096,36 +1301,7 @@
Usage("--output-oat-fd pass a negative value %d", output_oat_fd);
}
} else if (option.starts_with("--output-image-file=")) {
- if (have_output_image) {
- Usage("Only one of --output-image-file, and --output-image-fd may be used.");
- }
- have_output_image = true;
output_image_filename = option.substr(strlen("--output-image-file=")).data();
- } else if (option.starts_with("--output-image-fd=")) {
- if (have_output_image) {
- Usage("Only one of --output-image-file, and --output-image-fd may be used.");
- }
- have_output_image = true;
- const char* image_fd_str = option.substr(strlen("--output-image-fd=")).data();
- if (!ParseInt(image_fd_str, &output_image_fd)) {
- Usage("Failed to parse --output-image-fd argument '%s' as an integer", image_fd_str);
- }
- if (output_image_fd < 0) {
- Usage("--output-image-fd pass a negative value %d", output_image_fd);
- }
- } else if (option.starts_with("--orig-base-offset=")) {
- const char* orig_base_offset_str = option.substr(strlen("--orig-base-offset=")).data();
- orig_base_offset_set = true;
- if (!ParseUint(orig_base_offset_str, &orig_base_offset)) {
- Usage("Failed to parse --orig-base-offset argument '%s' as an uintptr_t",
- orig_base_offset_str);
- }
- } else if (option.starts_with("--base-offset=")) {
- const char* base_offset_str = option.substr(strlen("--base-offset=")).data();
- base_offset_set = true;
- if (!ParseUint(base_offset_str, &base_offset)) {
- Usage("Failed to parse --base-offset argument '%s' as an uintptr_t", base_offset_str);
- }
} else if (option.starts_with("--base-offset-delta=")) {
const char* base_delta_str = option.substr(strlen("--base-offset-delta=")).data();
base_delta_set = true;
@@ -1134,8 +1310,6 @@
}
} else if (option.starts_with("--patched-image-location=")) {
patched_image_location = option.substr(strlen("--patched-image-location=")).data();
- } else if (option.starts_with("--patched-image-file=")) {
- patched_image_filename = option.substr(strlen("--patched-image-file=")).data();
} else if (option == "--lock-output") {
lock_output = true;
} else if (option == "--no-lock-output") {
@@ -1149,284 +1323,43 @@
}
}
- {
- // Only 1 of these may be set.
- uint32_t cnt = 0;
- cnt += (base_delta_set) ? 1 : 0;
- cnt += (base_offset_set && orig_base_offset_set) ? 1 : 0;
- cnt += (!patched_image_filename.empty()) ? 1 : 0;
- cnt += (!patched_image_location.empty()) ? 1 : 0;
- if (cnt > 1) {
- Usage("Only one of --base-offset/--orig-base-offset, --base-offset-delta, "
- "--patched-image-filename or --patched-image-location may be used.");
- } else if (cnt == 0) {
- Usage("Must specify --base-offset-delta, --base-offset and --orig-base-offset, "
- "--patched-image-location or --patched-image-file");
- }
+ // The instruction set is mandatory. This simplifies things...
+ if (!isa_set) {
+ Usage("Instruction set must be set.");
}
- if (have_input_oat != have_output_oat) {
- Usage("Either both input and output oat must be supplied or niether must be.");
- }
-
- if ((!input_image_location.empty()) != have_output_image) {
- Usage("Either both input and output image must be supplied or niether must be.");
- }
-
- // We know we have both the input and output so rename for clarity.
- bool have_image_files = have_output_image;
- bool have_oat_files = have_output_oat;
-
- if (!have_oat_files) {
- if (have_image_files) {
- Usage("Cannot patch an image file without an oat file");
- } else {
- Usage("Must be patching either an oat file or an image file with an oat file.");
- }
- }
-
- if (!have_oat_files && !isa_set) {
- Usage("Must include ISA if patching an image file without an oat file.");
- }
-
- if (!input_oat_location.empty()) {
- if (!isa_set) {
- Usage("specifying a location requires specifying an instruction set");
- }
- if (!LocationToFilename(input_oat_location, isa, &input_oat_filename)) {
- Usage("Unable to find filename for input oat location %s", input_oat_location.c_str());
- }
- if (debug) {
- LOG(INFO) << "Using input-oat-file " << input_oat_filename;
- }
- }
- if (!patched_image_location.empty()) {
- if (!isa_set) {
- Usage("specifying a location requires specifying an instruction set");
- }
- std::string system_filename;
- bool has_system = false;
- std::string cache_filename;
- bool has_cache = false;
- bool has_android_data_unused = false;
- bool is_global_cache = false;
- if (!gc::space::ImageSpace::FindImageFilename(patched_image_location.c_str(), isa,
- &system_filename, &has_system, &cache_filename,
- &has_android_data_unused, &has_cache,
- &is_global_cache)) {
- Usage("Unable to determine image file for location %s", patched_image_location.c_str());
- }
- if (has_cache) {
- patched_image_filename = cache_filename;
- } else if (has_system) {
- LOG(WARNING) << "Only image file found was in /system for image location "
- << patched_image_location;
- patched_image_filename = system_filename;
- } else {
- Usage("Unable to determine image file for location %s", patched_image_location.c_str());
- }
- if (debug) {
- LOG(INFO) << "Using patched-image-file " << patched_image_filename;
- }
- }
-
- if (!base_delta_set) {
- if (orig_base_offset_set && base_offset_set) {
- base_delta_set = true;
- base_delta = base_offset - orig_base_offset;
- } else if (!patched_image_filename.empty()) {
- if (have_image_files) {
- Usage("--patched-image-location should not be used when patching other images");
- }
- base_delta_set = true;
- match_delta = true;
- std::string error_msg;
- if (!ReadBaseDelta(patched_image_filename.c_str(), &base_delta, &error_msg)) {
- Usage(error_msg.c_str(), patched_image_filename.c_str());
- }
- } else {
- if (base_offset_set) {
- Usage("Unable to determine original base offset.");
- } else {
- Usage("Must supply a desired new offset or delta.");
- }
- }
- }
-
- if (!IsAligned<kPageSize>(base_delta)) {
- Usage("Base offset/delta must be alligned to a pagesize (0x%08x) boundary.", kPageSize);
- }
-
- // Do we need to cleanup output files if we fail?
- bool new_image_out = false;
- bool new_oat_out = false;
-
- std::unique_ptr<File> input_oat;
- std::unique_ptr<File> output_oat;
- std::unique_ptr<File> output_image;
-
- if (have_image_files) {
- CHECK(!input_image_location.empty());
-
- if (output_image_fd != -1) {
- if (output_image_filename.empty()) {
- output_image_filename = "output-image-file";
- }
- output_image.reset(new File(output_image_fd, output_image_filename, true));
- } else {
- CHECK(!output_image_filename.empty());
- output_image.reset(CreateOrOpen(output_image_filename.c_str(), &new_image_out));
- }
+ int ret;
+ if (!input_image_location.empty()) {
+ ret = patchoat_image(timings,
+ isa,
+ input_image_location,
+ output_image_filename,
+ base_delta,
+ base_delta_set,
+ debug);
} else {
- CHECK(output_image_filename.empty() && output_image_fd == -1 && input_image_location.empty());
+ ret = patchoat_oat(timings,
+ isa,
+ patched_image_location,
+ base_delta,
+ base_delta_set,
+ input_oat_fd,
+ input_oat_location,
+ input_oat_filename,
+ have_input_oat,
+ output_oat_fd,
+ output_oat_filename,
+ have_output_oat,
+ lock_output,
+ debug);
}
- if (have_oat_files) {
- if (input_oat_fd != -1) {
- if (input_oat_filename.empty()) {
- input_oat_filename = "input-oat-file";
- }
- input_oat.reset(new File(input_oat_fd, input_oat_filename, false));
- if (input_oat_fd == output_oat_fd) {
- input_oat.get()->DisableAutoClose();
- }
- if (input_oat == nullptr) {
- // Unlikely, but ensure exhaustive logging in non-0 exit code case
- LOG(ERROR) << "Failed to open input oat file by its FD" << input_oat_fd;
- }
- } else {
- CHECK(!input_oat_filename.empty());
- input_oat.reset(OS::OpenFileForReading(input_oat_filename.c_str()));
- if (input_oat == nullptr) {
- int err = errno;
- LOG(ERROR) << "Failed to open input oat file " << input_oat_filename
- << ": " << strerror(err) << "(" << err << ")";
- }
- }
-
- if (output_oat_fd != -1) {
- if (output_oat_filename.empty()) {
- output_oat_filename = "output-oat-file";
- }
- output_oat.reset(new File(output_oat_fd, output_oat_filename, true));
- if (output_oat == nullptr) {
- // Unlikely, but ensure exhaustive logging in non-0 exit code case
- LOG(ERROR) << "Failed to open output oat file by its FD" << output_oat_fd;
- }
- } else {
- CHECK(!output_oat_filename.empty());
- output_oat.reset(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out));
- if (output_oat == nullptr) {
- int err = errno;
- LOG(ERROR) << "Failed to open output oat file " << output_oat_filename
- << ": " << strerror(err) << "(" << err << ")";
- }
- }
+ timings.EndTiming();
+ if (dump_timings) {
+ LOG(INFO) << Dumpable<TimingLogger>(timings);
}
- // TODO: get rid of this.
- auto cleanup = [&output_image_filename, &output_oat_filename,
- &new_oat_out, &new_image_out, &timings, &dump_timings](bool success) {
- timings.EndTiming();
- if (!success) {
- if (new_oat_out) {
- CHECK(!output_oat_filename.empty());
- TEMP_FAILURE_RETRY(unlink(output_oat_filename.c_str()));
- }
- if (new_image_out) {
- CHECK(!output_image_filename.empty());
- TEMP_FAILURE_RETRY(unlink(output_image_filename.c_str()));
- }
- }
- if (dump_timings) {
- LOG(INFO) << Dumpable<TimingLogger>(timings);
- }
-
- if (kIsDebugBuild) {
- LOG(INFO) << "Cleaning up.. success? " << success;
- }
- };
-
- if (have_oat_files && (input_oat.get() == nullptr || output_oat.get() == nullptr)) {
- LOG(ERROR) << "Failed to open input/output oat files";
- cleanup(false);
- return EXIT_FAILURE;
- } else if (have_image_files && output_image.get() == nullptr) {
- LOG(ERROR) << "Failed to open output image file";
- cleanup(false);
- return EXIT_FAILURE;
- }
-
- if (match_delta) {
- CHECK(!have_image_files); // We will not do this with images.
- std::string error_msg;
- // Figure out what the current delta is so we can match it to the desired delta.
- std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat.get(), PROT_READ, MAP_PRIVATE,
- &error_msg));
- off_t current_delta = 0;
- if (elf.get() == nullptr) {
- LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg;
- cleanup(false);
- return EXIT_FAILURE;
- } else if (!ReadOatPatchDelta(elf.get(), ¤t_delta, &error_msg)) {
- LOG(ERROR) << "Unable to get current delta: " << error_msg;
- cleanup(false);
- return EXIT_FAILURE;
- }
- // Before this line base_delta is the desired final delta. We need it to be the actual amount to
- // change everything by. We subtract the current delta from it to make it this.
- base_delta -= current_delta;
- if (!IsAligned<kPageSize>(base_delta)) {
- LOG(ERROR) << "Given image file was relocated by an illegal delta";
- cleanup(false);
- return false;
- }
- }
-
- if (debug) {
- LOG(INFO) << "moving offset by " << base_delta
- << " (0x" << std::hex << base_delta << ") bytes or "
- << std::dec << (base_delta/kPageSize) << " pages.";
- }
-
- // TODO: is it going to be promatic to unlink a file that was flock-ed?
- ScopedFlock output_oat_lock;
- if (lock_output) {
- std::string error_msg;
- if (have_oat_files && !output_oat_lock.Init(output_oat.get(), &error_msg)) {
- LOG(ERROR) << "Unable to lock output oat " << output_image->GetPath() << ": " << error_msg;
- cleanup(false);
- return EXIT_FAILURE;
- }
- }
-
- bool ret;
- if (have_image_files && have_oat_files) {
- TimingLogger::ScopedTiming pt("patch image and oat", &timings);
- ret = PatchOat::Patch(input_oat.get(), input_image_location, base_delta,
- output_oat.get(), output_image.get(), isa, &timings,
- output_oat_fd >= 0, // was it opened from FD?
- new_oat_out);
- // The order here doesn't matter. If the first one is successfully saved and the second one
- // erased, ImageSpace will still detect a problem and not use the files.
- ret = FinishFile(output_image.get(), ret);
- ret = FinishFile(output_oat.get(), ret);
- } else if (have_oat_files) {
- TimingLogger::ScopedTiming pt("patch oat", &timings);
- ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings,
- output_oat_fd >= 0, // was it opened from FD?
- new_oat_out);
- ret = FinishFile(output_oat.get(), ret);
- } else {
- CHECK(false);
- ret = true;
- }
-
- if (kIsDebugBuild) {
- LOG(INFO) << "Exiting with return ... " << ret;
- }
- cleanup(ret);
- return (ret) ? EXIT_SUCCESS : EXIT_FAILURE;
+ return ret;
}
} // namespace art
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index ceddc34..a6a8fee 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -53,11 +53,11 @@
TimingLogger* timings);
// Patch both the image and the oat file
- static bool Patch(File* oat_in, const std::string& art_location,
- off_t delta, File* oat_out, File* art_out, InstructionSet isa,
- TimingLogger* timings,
- bool output_oat_opened_from_fd, // Was this using --oatput-oat-fd ?
- bool new_oat_out); // Output oat was a new file created by us?
+ static bool Patch(const std::string& art_location,
+ off_t delta,
+ const std::string& output_directory,
+ InstructionSet isa,
+ TimingLogger* timings);
~PatchOat() {}
PatchOat(PatchOat&&) = default;
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 288f95e..e9f7add 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -275,6 +275,8 @@
arch/arm64/fault_handler_arm64.cc
LIBART_SRC_FILES_x86 := \
+ interpreter/mterp/mterp.cc \
+ interpreter/mterp/out/mterp_x86.S \
arch/x86/context_x86.cc \
arch/x86/entrypoints_init_x86.cc \
arch/x86/jni_entrypoints_x86.S \
@@ -286,20 +288,6 @@
LIBART_TARGET_SRC_FILES_x86 := \
$(LIBART_SRC_FILES_x86)
-# Darwin uses non-standard x86 assembly syntax. Don't build x86 Darwin host mterp there.
-ifeq ($(HOST_OS),darwin)
- LIBART_SRC_FILES_x86 += \
- interpreter/mterp/mterp_stub.cc
-else
- LIBART_SRC_FILES_x86 += \
- interpreter/mterp/mterp.cc \
- interpreter/mterp/out/mterp_x86.S
-endif
-# But do support x86 mterp for target build regardless of host platform.
-LIBART_TARGET_SRC_FILES_x86 += \
- interpreter/mterp/mterp.cc \
- interpreter/mterp/out/mterp_x86.S
-
# Note that the fault_handler_x86.cc is not a mistake. This file is
# shared between the x86 and x86_64 architectures.
LIBART_SRC_FILES_x86_64 := \
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index 7141181..e358ff8 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -130,6 +130,25 @@
qpoints->pL2f = art_quick_l2f;
}
+ // More math.
+ qpoints->pCos = cos;
+ qpoints->pSin = sin;
+ qpoints->pAcos = acos;
+ qpoints->pAsin = asin;
+ qpoints->pAtan = atan;
+ qpoints->pAtan2 = atan2;
+ qpoints->pCbrt = cbrt;
+ qpoints->pCosh = cosh;
+ qpoints->pExp = exp;
+ qpoints->pExpm1 = expm1;
+ qpoints->pHypot = hypot;
+ qpoints->pLog = log;
+ qpoints->pLog10 = log10;
+ qpoints->pNextAfter = nextafter;
+ qpoints->pSinh = sinh;
+ qpoints->pTan = tan;
+ qpoints->pTanh = tanh;
+
// Intrinsics
qpoints->pIndexOf = art_quick_indexof;
qpoints->pStringCompareTo = art_quick_string_compareto;
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 631b784..c4e314b 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -429,6 +429,56 @@
END art_quick_invoke_stub_internal
/*
+ * On stack replacement stub.
+ * On entry:
+ * r0 = stack to copy
+ * r1 = size of stack
+ * r2 = pc to call
+ * r3 = JValue* result
+ * [sp] = shorty
+ * [sp + 4] = thread
+ */
+ENTRY art_quick_osr_stub
+ SPILL_ALL_CALLEE_SAVE_GPRS @ Spill regs (9)
+ mov r11, sp @ Save the stack pointer
+ mov r10, r1 @ Save size of stack
+ ldr r9, [r11, #40] @ Move managed thread pointer into r9
+ mov r8, r2 @ Save the pc to call
+ sub r7, sp, #12 @ Reserve space for stack pointer,
+ @ JValue* result, and ArtMethod* slot.
+ and r7, #0xFFFFFFF0 @ Align stack pointer
+ mov sp, r7 @ Update stack pointer
+ str r11, [sp, #4] @ Save old stack pointer
+ str r3, [sp, #8] @ Save JValue* result
+ mov ip, #0
+ str ip, [sp] @ Store null for ArtMethod* at bottom of frame
+ sub sp, sp, r1 @ Reserve space for callee stack
+ mov r2, r1
+ mov r1, r0
+ mov r0, sp
+ bl memcpy @ memcpy (dest r0, src r1, bytes r2)
+ bl .Losr_entry @ Call the method
+ ldr r10, [sp, #8] @ Restore JValue* result
+ ldr sp, [sp, #4] @ Restore saved stack pointer
+ ldr r4, [sp, #36] @ load shorty
+ ldrb r4, [r4, #0] @ load return type
+ cmp r4, #68 @ Test if result type char == 'D'.
+ beq .Losr_fp_result
+ cmp r4, #70 @ Test if result type char == 'F'.
+ beq .Losr_fp_result
+ strd r0, [r10] @ Store r0/r1 into result pointer
+ b .Losr_exit
+.Losr_fp_result:
+ vstr d0, [r10] @ Store s0-s1/d0 into result pointer
+.Losr_exit:
+ pop {r4, r5, r6, r7, r8, r9, r10, r11, pc}
+.Losr_entry:
+ sub r10, r10, #4
+ str lr, [sp, r10] @ Store link register per the compiler ABI
+ bx r8
+END art_quick_osr_stub
+
+ /*
* On entry r0 is uint32_t* gprs_ and r1 is uint32_t* fprs_
*/
ARM_ENTRY art_quick_do_long_jump
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 9ccabad..e848008 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -915,6 +915,105 @@
+/* extern"C" void art_quick_osr_stub(void** stack, x0
+ * size_t stack_size_in_bytes, x1
+ * const uin8_t* native_pc, x2
+ * JValue *result, x3
+ * char *shorty, x4
+ * Thread *self) x5
+ */
+ENTRY art_quick_osr_stub
+SAVE_SIZE=15*8 // x3, x4, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, SP, LR, FP saved.
+ mov x9, sp // Save stack pointer.
+ .cfi_register sp,x9
+
+ sub x10, sp, # SAVE_SIZE
+ and x10, x10, # ~0xf // Enforce 16 byte stack alignment.
+ mov sp, x10 // Set new SP.
+
+ str x28, [sp, #112]
+ stp x26, x27, [sp, #96]
+ stp x24, x25, [sp, #80]
+ stp x22, x23, [sp, #64]
+ stp x20, x21, [sp, #48]
+ stp x9, x19, [sp, #32] // Save old stack pointer and x19.
+ stp x3, x4, [sp, #16] // Save result and shorty addresses.
+ stp xFP, xLR, [sp] // Store LR & FP.
+ mov xSELF, x5 // Move thread pointer into SELF register.
+
+ sub sp, sp, #16
+ str xzr, [sp] // Store null for ArtMethod* slot
+ // Branch to stub.
+ bl .Losr_entry
+ add sp, sp, #16
+
+ // Restore return value address and shorty address.
+ ldp x3,x4, [sp, #16]
+ ldr x28, [sp, #112]
+ ldp x26, x27, [sp, #96]
+ ldp x24, x25, [sp, #80]
+ ldp x22, x23, [sp, #64]
+ ldp x20, x21, [sp, #48]
+
+ // Store result (w0/x0/s0/d0) appropriately, depending on resultType.
+ ldrb w10, [x4]
+
+ // Check the return type and store the correct register into the jvalue in memory.
+
+ // Don't set anything for a void type.
+ cmp w10, #'V'
+ beq .Losr_exit
+
+ // Is it a double?
+ cmp w10, #'D'
+ bne .Lno_double
+ str d0, [x3]
+ b .Losr_exit
+
+.Lno_double: // Is it a float?
+ cmp w10, #'F'
+ bne .Lno_float
+ str s0, [x3]
+ b .Losr_exit
+
+.Lno_float: // Just store x0. Doesn't matter if it is 64 or 32 bits.
+ str x0, [x3]
+
+.Losr_exit: // Finish up.
+ ldp x2, x19, [sp, #32] // Restore stack pointer and x19.
+ ldp xFP, xLR, [sp] // Restore old frame pointer and link register.
+ mov sp, x2
+ ret
+
+.Losr_entry:
+ // Update stack pointer for the callee
+ sub sp, sp, x1
+
+ // Update link register slot expected by the callee.
+ sub w1, w1, #8
+ str lr, [sp, x1]
+
+ // Copy arguments into stack frame.
+ // Use simple copy routine for now.
+ // 4 bytes per slot.
+ // X0 - source address
+ // W1 - args length
+ // SP - destination address.
+ // W10 - temporary
+.Losr_loop_entry:
+ cmp w1, #0
+ beq .Losr_loop_exit
+ sub w1, w1, #4
+ ldr w10, [x0, x1]
+ str w10, [sp, x1]
+ b .Losr_loop_entry
+
+.Losr_loop_exit:
+ // Branch to the OSR entry point.
+ br x2
+
+END art_quick_osr_stub
+
/*
* On entry x0 is uintptr_t* gprs_ and x1 is uint64_t* fprs_
*/
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index da30331..fbee5d7 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1712,5 +1712,65 @@
ret
END_FUNCTION art_quick_read_barrier_for_root_slow
+ /*
+ * On stack replacement stub.
+ * On entry:
+ * [sp] = return address
+ * [sp + 4] = stack to copy
+ * [sp + 8] = size of stack
+ * [sp + 12] = pc to call
+ * [sp + 16] = JValue* result
+ * [sp + 20] = shorty
+ * [sp + 24] = thread
+ */
+DEFINE_FUNCTION art_quick_osr_stub
+ // Save native callee saves.
+ PUSH ebp
+ PUSH ebx
+ PUSH esi
+ PUSH edi
+ mov 4+16(%esp), %esi // ESI = argument array
+ mov 8+16(%esp), %ecx // ECX = size of args
+ mov 12+16(%esp), %ebx // EBX = pc to call
+ mov %esp, %ebp // Save stack pointer
+ andl LITERAL(0xFFFFFFF0), %esp // Align stack
+ PUSH ebp // Save old stack pointer
+ subl LITERAL(12), %esp // Align stack
+ movl LITERAL(0), (%esp) // Store null for ArtMethod* slot
+ call .Losr_entry
+
+ // Restore stack pointer.
+ addl LITERAL(12), %esp
+ POP ebp
+ mov %ebp, %esp
+
+ // Restore callee saves.
+ POP edi
+ POP esi
+ POP ebx
+ POP ebp
+ mov 16(%esp), %ecx // Get JValue result
+ mov %eax, (%ecx) // Store the result assuming it is a long, int or Object*
+ mov %edx, 4(%ecx) // Store the other half of the result
+ mov 20(%esp), %edx // Get the shorty
+ cmpb LITERAL(68), (%edx) // Test if result type char == 'D'
+ je .Losr_return_double_quick
+ cmpb LITERAL(70), (%edx) // Test if result type char == 'F'
+ je .Losr_return_float_quick
+ ret
+.Losr_return_double_quick:
+ movsd %xmm0, (%ecx) // Store the floating point result
+ ret
+.Losr_return_float_quick:
+ movss %xmm0, (%ecx) // Store the floating point result
+ ret
+.Losr_entry:
+ subl LITERAL(4), %ecx // Given stack size contains pushed frame pointer, substract it.
+ subl %ecx, %esp
+ mov %esp, %edi // EDI = beginning of stack
+ rep movsb // while (ecx--) { *edi++ = *esi++ }
+ jmp *%ebx
+END_FUNCTION art_quick_osr_stub
+
// TODO: implement these!
UNIMPLEMENTED art_quick_memcmp16
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 883da96..69caec8 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1744,3 +1744,64 @@
RESTORE_FP_CALLEE_SAVE_FRAME
ret
END_FUNCTION art_quick_read_barrier_for_root_slow
+
+ /*
+ * On stack replacement stub.
+ * On entry:
+ * [sp] = return address
+ * rdi = stack to copy
+ * rsi = size of stack
+ * rdx = pc to call
+ * rcx = JValue* result
+ * r8 = shorty
+ * r9 = thread
+ *
+ * Note that the native C ABI already aligned the stack to 16-byte.
+ */
+DEFINE_FUNCTION art_quick_osr_stub
+ // Save the non-volatiles.
+ PUSH rbp // Save rbp.
+ PUSH rcx // Save rcx/result*.
+ PUSH r8 // Save r8/shorty*.
+
+ // Save callee saves.
+ PUSH rbx
+ PUSH r12
+ PUSH r13
+ PUSH r14
+ PUSH r15
+
+ pushq LITERAL(0) // Push null for ArtMethod*.
+ movl %esi, %ecx // rcx := size of stack
+ movq %rdi, %rsi // rsi := stack to copy
+ call .Losr_entry
+
+ // Restore stack and callee-saves.
+ addq LITERAL(8), %rsp
+ POP r15
+ POP r14
+ POP r13
+ POP r12
+ POP rbx
+ POP r8
+ POP rcx
+ POP rbp
+ cmpb LITERAL(68), (%r8) // Test if result type char == 'D'.
+ je .Losr_return_double_quick
+ cmpb LITERAL(70), (%r8) // Test if result type char == 'F'.
+ je .Losr_return_float_quick
+ movq %rax, (%rcx) // Store the result assuming its a long, int or Object*
+ ret
+.Losr_return_double_quick:
+ movsd %xmm0, (%rcx) // Store the double floating point result.
+ ret
+.Losr_return_float_quick:
+ movss %xmm0, (%rcx) // Store the floating point result.
+ ret
+.Losr_entry:
+ subl LITERAL(8), %ecx // Given stack size contains pushed frame pointer, substract it.
+ subq %rcx, %rsp
+ movq %rsp, %rdi // rdi := beginning of stack
+ rep movsb // while (rcx--) { *rdi++ = *rsi++ }
+ jmp *%rdx
+END_FUNCTION art_quick_osr_stub
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 28540c8..ebe89bb 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -449,23 +449,26 @@
void ArtMethod::VisitRoots(RootVisitorType& visitor, size_t pointer_size) {
ArtMethod* interface_method = nullptr;
mirror::Class* klass = declaring_class_.Read();
- if (UNLIKELY(klass != nullptr && klass->IsProxyClass())) {
- // For normal methods, dex cache shortcuts will be visited through the declaring class.
- // However, for proxies we need to keep the interface method alive, so we visit its roots.
- interface_method = mirror::DexCache::GetElementPtrSize(
- GetDexCacheResolvedMethods(pointer_size),
- GetDexMethodIndex(),
- pointer_size);
- DCHECK(interface_method != nullptr);
- DCHECK_EQ(interface_method,
- Runtime::Current()->GetClassLinker()->FindMethodForProxy(klass, this));
- interface_method->VisitRoots(visitor, pointer_size);
- }
-
- visitor.VisitRootIfNonNull(declaring_class_.AddressWithoutBarrier());
- ProfilingInfo* profiling_info = GetProfilingInfo(pointer_size);
- if (hotness_count_ != 0 && !IsNative() && profiling_info != nullptr) {
- profiling_info->VisitRoots(visitor);
+ if (LIKELY(klass != nullptr)) {
+ if (UNLIKELY(klass->IsProxyClass())) {
+ // For normal methods, dex cache shortcuts will be visited through the declaring class.
+ // However, for proxies we need to keep the interface method alive, so we visit its roots.
+ interface_method = mirror::DexCache::GetElementPtrSize(
+ GetDexCacheResolvedMethods(pointer_size),
+ GetDexMethodIndex(),
+ pointer_size);
+ DCHECK(interface_method != nullptr);
+ DCHECK_EQ(interface_method,
+ Runtime::Current()->GetClassLinker()->FindMethodForProxy(klass, this));
+ interface_method->VisitRoots(visitor, pointer_size);
+ }
+ visitor.VisitRoot(declaring_class_.AddressWithoutBarrier());
+ if (!IsNative()) {
+ ProfilingInfo* profiling_info = GetProfilingInfo(pointer_size);
+ if (profiling_info != nullptr) {
+ profiling_info->VisitRoots(visitor);
+ }
+ }
}
}
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 6f36016..cd38e16 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -292,22 +292,7 @@
// Unusual case where we were running generated code and an
// exception was thrown to force the activations to be removed from the
// stack. Continue execution in the interpreter.
- self->ClearException();
- ShadowFrame* shadow_frame =
- self->PopStackedShadowFrame(StackedShadowFrameType::kDeoptimizationShadowFrame);
- mirror::Throwable* pending_exception = nullptr;
- bool from_code = false;
- self->PopDeoptimizationContext(result, &pending_exception, &from_code);
- CHECK(!from_code);
- self->SetTopOfStack(nullptr);
- self->SetTopOfShadowStack(shadow_frame);
-
- // Restore the exception that was pending before deoptimization then interpret the
- // deoptimized frames.
- if (pending_exception != nullptr) {
- self->SetException(pending_exception);
- }
- interpreter::EnterInterpreterFromDeoptimize(self, shadow_frame, from_code, result);
+ self->DeoptimizeWithDeoptimizationException(result);
}
if (kLogInvocationStartAndReturn) {
LOG(INFO) << StringPrintf("Returned '%s' quick code=%p", PrettyMethod(this).c_str(),
diff --git a/runtime/art_method.h b/runtime/art_method.h
index ce23c2a..078a978 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -174,13 +174,13 @@
bool IsProxyMethod() SHARED_REQUIRES(Locks::mutator_lock_);
- bool IsPreverified() {
- return (GetAccessFlags() & kAccPreverified) != 0;
+ bool SkipAccessChecks() {
+ return (GetAccessFlags() & kAccSkipAccessChecks) != 0;
}
- void SetPreverified() {
- DCHECK(!IsPreverified());
- SetAccessFlags(GetAccessFlags() | kAccPreverified);
+ void SetSkipAccessChecks() {
+ DCHECK(!SkipAccessChecks());
+ SetAccessFlags(GetAccessFlags() | kAccSkipAccessChecks);
}
// Returns true if this method could be overridden by a default method.
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 31610a3..eb3b7f3 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -150,11 +150,11 @@
ADD_TEST_EQ(THREAD_ROSALLOC_RUNS_OFFSET,
art::Thread::RosAllocRunsOffset<__SIZEOF_POINTER__>().Int32Value())
// Offset of field Thread::tlsPtr_.thread_local_alloc_stack_top.
-#define THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET (THREAD_ROSALLOC_RUNS_OFFSET + 34 * __SIZEOF_POINTER__)
+#define THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET (THREAD_ROSALLOC_RUNS_OFFSET + 16 * __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET,
art::Thread::ThreadLocalAllocStackTopOffset<__SIZEOF_POINTER__>().Int32Value())
// Offset of field Thread::tlsPtr_.thread_local_alloc_stack_end.
-#define THREAD_LOCAL_ALLOC_STACK_END_OFFSET (THREAD_ROSALLOC_RUNS_OFFSET + 35 * __SIZEOF_POINTER__)
+#define THREAD_LOCAL_ALLOC_STACK_END_OFFSET (THREAD_ROSALLOC_RUNS_OFFSET + 17 * __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_LOCAL_ALLOC_STACK_END_OFFSET,
art::Thread::ThreadLocalAllocStackEndOffset<__SIZEOF_POINTER__>().Int32Value())
@@ -331,21 +331,23 @@
ADD_TEST_EQ(ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE,
static_cast<int32_t>(art::gc::allocator::RosAlloc::kMaxThreadLocalBracketSize))
-#define ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT 4
+#define ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT 3
ADD_TEST_EQ(ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT,
- static_cast<int32_t>(art::gc::allocator::RosAlloc::kBracketQuantumSizeShift))
+ static_cast<int32_t>(art::gc::allocator::RosAlloc::kThreadLocalBracketQuantumSizeShift))
-#define ROSALLOC_BRACKET_QUANTUM_SIZE_MASK 15
+#define ROSALLOC_BRACKET_QUANTUM_SIZE_MASK 7
ADD_TEST_EQ(ROSALLOC_BRACKET_QUANTUM_SIZE_MASK,
- static_cast<int32_t>(art::gc::allocator::RosAlloc::kBracketQuantumSize - 1))
+ static_cast<int32_t>(art::gc::allocator::RosAlloc::kThreadLocalBracketQuantumSize - 1))
-#define ROSALLOC_BRACKET_QUANTUM_SIZE_MASK_TOGGLED32 0xfffffff0
+#define ROSALLOC_BRACKET_QUANTUM_SIZE_MASK_TOGGLED32 0xfffffff8
ADD_TEST_EQ(static_cast<uint32_t>(ROSALLOC_BRACKET_QUANTUM_SIZE_MASK_TOGGLED32),
- ~static_cast<uint32_t>(art::gc::allocator::RosAlloc::kBracketQuantumSize - 1))
+ ~static_cast<uint32_t>(
+ art::gc::allocator::RosAlloc::kThreadLocalBracketQuantumSize - 1))
-#define ROSALLOC_BRACKET_QUANTUM_SIZE_MASK_TOGGLED64 0xfffffffffffffff0
+#define ROSALLOC_BRACKET_QUANTUM_SIZE_MASK_TOGGLED64 0xfffffffffffffff8
ADD_TEST_EQ(static_cast<uint64_t>(ROSALLOC_BRACKET_QUANTUM_SIZE_MASK_TOGGLED64),
- ~static_cast<uint64_t>(art::gc::allocator::RosAlloc::kBracketQuantumSize - 1))
+ ~static_cast<uint64_t>(
+ art::gc::allocator::RosAlloc::kThreadLocalBracketQuantumSize - 1))
#define ROSALLOC_RUN_FREE_LIST_OFFSET 8
ADD_TEST_EQ(ROSALLOC_RUN_FREE_LIST_OFFSET,
diff --git a/runtime/base/scoped_arena_allocator.cc b/runtime/base/scoped_arena_allocator.cc
index 90c6ee3..7d04fa0 100644
--- a/runtime/base/scoped_arena_allocator.cc
+++ b/runtime/base/scoped_arena_allocator.cc
@@ -99,7 +99,7 @@
if (UNLIKELY(static_cast<size_t>(top_end_ - ptr) < rounded_bytes)) {
ptr = AllocateFromNextArena(rounded_bytes);
CHECK(ptr != nullptr) << "Failed to allocate memory";
- MEMORY_TOOL_MAKE_NOACCESS(ptr, top_end_);
+ MEMORY_TOOL_MAKE_NOACCESS(ptr, top_end_ - ptr);
}
CurrentStats()->RecordAlloc(bytes, kind);
top_ptr_ = ptr + rounded_bytes;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index c80f91a..5278d1b 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -326,6 +326,24 @@
std::fill_n(find_array_class_cache_, kFindArrayCacheSize, GcRoot<mirror::Class>(nullptr));
}
+void ClassLinker::CheckSystemClass(Thread* self, Handle<mirror::Class> c1, const char* descriptor) {
+ mirror::Class* c2 = FindSystemClass(self, descriptor);
+ if (c2 == nullptr) {
+ LOG(FATAL) << "Could not find class " << descriptor;
+ UNREACHABLE();
+ }
+ if (c1.Get() != c2) {
+ std::ostringstream os1, os2;
+ c1->DumpClass(os1, mirror::Class::kDumpClassFullDetail);
+ c2->DumpClass(os2, mirror::Class::kDumpClassFullDetail);
+ LOG(FATAL) << "InitWithoutImage: Class mismatch for " << descriptor
+ << ". This is most likely the result of a broken build. Make sure that "
+ << "libcore and art projects match.\n\n"
+ << os1.str() << "\n\n" << os2.str();
+ UNREACHABLE();
+ }
+}
+
bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> boot_class_path,
std::string* error_msg) {
VLOG(startup) << "ClassLinker::Init";
@@ -517,18 +535,12 @@
// Object, String and DexCache need to be rerun through FindSystemClass to finish init
mirror::Class::SetStatus(java_lang_Object, mirror::Class::kStatusNotReady, self);
- CHECK_EQ(java_lang_Object.Get(), FindSystemClass(self, "Ljava/lang/Object;"));
+ CheckSystemClass(self, java_lang_Object, "Ljava/lang/Object;");
CHECK_EQ(java_lang_Object->GetObjectSize(), mirror::Object::InstanceSize());
mirror::Class::SetStatus(java_lang_String, mirror::Class::kStatusNotReady, self);
- mirror::Class* String_class = FindSystemClass(self, "Ljava/lang/String;");
- if (java_lang_String.Get() != String_class) {
- std::ostringstream os1, os2;
- java_lang_String->DumpClass(os1, mirror::Class::kDumpClassFullDetail);
- String_class->DumpClass(os2, mirror::Class::kDumpClassFullDetail);
- LOG(FATAL) << os1.str() << "\n\n" << os2.str();
- }
+ CheckSystemClass(self, java_lang_String, "Ljava/lang/String;");
mirror::Class::SetStatus(java_lang_DexCache, mirror::Class::kStatusNotReady, self);
- CHECK_EQ(java_lang_DexCache.Get(), FindSystemClass(self, "Ljava/lang/DexCache;"));
+ CheckSystemClass(self, java_lang_DexCache, "Ljava/lang/DexCache;");
CHECK_EQ(java_lang_DexCache->GetObjectSize(), mirror::DexCache::InstanceSize());
// Setup the primitive array type classes - can't be done until Object has a vtable.
@@ -538,14 +550,13 @@
SetClassRoot(kByteArrayClass, FindSystemClass(self, "[B"));
mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass));
- CHECK_EQ(char_array_class.Get(), FindSystemClass(self, "[C"));
+ CheckSystemClass(self, char_array_class, "[C");
SetClassRoot(kShortArrayClass, FindSystemClass(self, "[S"));
mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass));
- CHECK_EQ(int_array_class.Get(), FindSystemClass(self, "[I"));
-
- CHECK_EQ(long_array_class.Get(), FindSystemClass(self, "[J"));
+ CheckSystemClass(self, int_array_class, "[I");
+ CheckSystemClass(self, long_array_class, "[J");
SetClassRoot(kFloatArrayClass, FindSystemClass(self, "[F"));
mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass));
@@ -553,9 +564,12 @@
SetClassRoot(kDoubleArrayClass, FindSystemClass(self, "[D"));
mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass));
- CHECK_EQ(class_array_class.Get(), FindSystemClass(self, "[Ljava/lang/Class;"));
+ // Run Class through FindSystemClass. This initializes the dex_cache_ fields and register it
+ // in class_table_.
+ CheckSystemClass(self, java_lang_Class, "Ljava/lang/Class;");
- CHECK_EQ(object_array_class.Get(), FindSystemClass(self, "[Ljava/lang/Object;"));
+ CheckSystemClass(self, class_array_class, "[Ljava/lang/Class;");
+ CheckSystemClass(self, object_array_class, "[Ljava/lang/Object;");
// Setup the single, global copy of "iftable".
auto java_lang_Cloneable = hs.NewHandle(FindSystemClass(self, "Ljava/lang/Cloneable;"));
@@ -577,14 +591,11 @@
mirror::Class::GetDirectInterface(self, object_array_class, 0));
CHECK_EQ(java_io_Serializable.Get(),
mirror::Class::GetDirectInterface(self, object_array_class, 1));
- // Run Class, ArtField, and ArtMethod through FindSystemClass. This initializes their
- // dex_cache_ fields and register them in class_table_.
- CHECK_EQ(java_lang_Class.Get(), FindSystemClass(self, "Ljava/lang/Class;"));
CHECK_EQ(object_array_string.Get(),
FindSystemClass(self, GetClassRootDescriptor(kJavaLangStringArrayClass)));
- // End of special init trickery, subsequent classes may be loaded via FindSystemClass.
+ // End of special init trickery, all subsequent classes may be loaded via FindSystemClass.
// Create java.lang.reflect.Proxy root.
SetClassRoot(kJavaLangReflectProxy, FindSystemClass(self, "Ljava/lang/reflect/Proxy;"));
@@ -624,7 +635,7 @@
// java.lang.ref classes need to be specially flagged, but otherwise are normal classes
// finish initializing Reference class
mirror::Class::SetStatus(java_lang_ref_Reference, mirror::Class::kStatusNotReady, self);
- CHECK_EQ(java_lang_ref_Reference.Get(), FindSystemClass(self, "Ljava/lang/ref/Reference;"));
+ CheckSystemClass(self, java_lang_ref_Reference, "Ljava/lang/ref/Reference;");
CHECK_EQ(java_lang_ref_Reference->GetObjectSize(), mirror::Reference::InstanceSize());
CHECK_EQ(java_lang_ref_Reference->GetClassSize(),
mirror::Reference::ClassSize(image_pointer_size_));
@@ -1669,6 +1680,22 @@
forward_dex_cache_arrays);
class_table->Visit(visitor);
}
+ // forward_dex_cache_arrays is true iff we copied all of the dex cache arrays into the .bss.
+ // In this case, madvise away the dex cache arrays section of the image to reduce RAM usage and
+ // mark as PROT_NONE to catch any invalid accesses.
+ if (forward_dex_cache_arrays) {
+ const ImageSection& dex_cache_section = header.GetImageSection(
+ ImageHeader::kSectionDexCacheArrays);
+ uint8_t* section_begin = AlignUp(space->Begin() + dex_cache_section.Offset(), kPageSize);
+ uint8_t* section_end = AlignDown(space->Begin() + dex_cache_section.End(), kPageSize);
+ if (section_begin < section_end) {
+ madvise(section_begin, section_end - section_begin, MADV_DONTNEED);
+ mprotect(section_begin, section_end - section_begin, PROT_NONE);
+ VLOG(image) << "Released and protected dex cache array image section from "
+ << reinterpret_cast<const void*>(section_begin) << "-"
+ << reinterpret_cast<const void*>(section_end);
+ }
+ }
}
VLOG(class_linker) << "Adding image space took " << PrettyDuration(NanoTime() - start_time);
return true;
@@ -2593,19 +2620,37 @@
return oat_class.GetOatMethod(oat_method_idx).GetQuickCode();
}
-// Returns true if the method must run with interpreter, false otherwise.
-static bool NeedsInterpreter(ArtMethod* method, const void* quick_code)
- SHARED_REQUIRES(Locks::mutator_lock_) {
+bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* quick_code) {
+ if (UNLIKELY(method->IsNative() || method->IsProxyMethod())) {
+ return false;
+ }
+
if (quick_code == nullptr) {
- // No code: need interpreter.
- // May return true for native code, in the case of generic JNI
- // DCHECK(!method->IsNative());
return true;
}
- // If interpreter mode is enabled, every method (except native and proxy) must
- // be run with interpreter.
- return Runtime::Current()->GetInstrumentation()->InterpretOnly() &&
- !method->IsNative() && !method->IsProxyMethod();
+
+ Runtime* runtime = Runtime::Current();
+ instrumentation::Instrumentation* instr = runtime->GetInstrumentation();
+ if (instr->InterpretOnly()) {
+ return true;
+ }
+
+ if (runtime->GetClassLinker()->IsQuickToInterpreterBridge(quick_code)) {
+ // Doing this check avoids doing compiled/interpreter transitions.
+ return true;
+ }
+
+ if (Dbg::IsForcedInterpreterNeededForCalling(Thread::Current(), method)) {
+ // Force the use of interpreter when it is required by the debugger.
+ return true;
+ }
+
+ if (runtime->UseJit() && runtime->GetJit()->JitAtFirstUse()) {
+ // The force JIT uses the interpreter entry point to execute the JIT.
+ return true;
+ }
+
+ return false;
}
void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) {
@@ -2650,15 +2695,12 @@
OatFile::OatMethod oat_method = oat_class.GetOatMethod(method_index);
quick_code = oat_method.GetQuickCode();
}
- const bool enter_interpreter = NeedsInterpreter(method, quick_code);
- if (enter_interpreter) {
+ // Check whether the method is native, in which case it's generic JNI.
+ if (quick_code == nullptr && method->IsNative()) {
+ quick_code = GetQuickGenericJniStub();
+ } else if (ShouldUseInterpreterEntrypoint(method, quick_code)) {
// Use interpreter entry point.
- // Check whether the method is native, in which case it's generic JNI.
- if (quick_code == nullptr && method->IsNative()) {
- quick_code = GetQuickGenericJniStub();
- } else {
- quick_code = GetQuickToInterpreterBridge();
- }
+ quick_code = GetQuickToInterpreterBridge();
}
runtime->GetInstrumentation()->UpdateMethodsCode(method, quick_code);
}
@@ -2689,7 +2731,8 @@
}
// Install entry point from interpreter.
- bool enter_interpreter = NeedsInterpreter(method, method->GetEntryPointFromQuickCompiledCode());
+ const void* quick_code = method->GetEntryPointFromQuickCompiledCode();
+ bool enter_interpreter = ShouldUseInterpreterEntrypoint(method, quick_code);
if (!method->IsInvokable()) {
EnsureThrowsInvocationError(method);
@@ -2701,20 +2744,18 @@
// It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
// after initializing class (see ClassLinker::InitializeClass method).
method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());
+ } else if (quick_code == nullptr && method->IsNative()) {
+ method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub());
} else if (enter_interpreter) {
- if (!method->IsNative()) {
- // Set entry point from compiled code if there's no code or in interpreter only mode.
- method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
- } else {
- method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub());
- }
+ // Set entry point from compiled code if there's no code or in interpreter only mode.
+ method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
}
if (method->IsNative()) {
// Unregistering restores the dlsym lookup stub.
method->UnregisterNative();
- if (enter_interpreter) {
+ if (enter_interpreter || quick_code == nullptr) {
// We have a native method here without code. Then it should have either the generic JNI
// trampoline as entrypoint (non-static), or the resolution trampoline (static).
// TODO: this doesn't handle all the cases where trampolines may be installed.
@@ -3625,7 +3666,7 @@
// Don't attempt to re-verify if already sufficiently verified.
if (klass->IsVerified()) {
- EnsurePreverifiedMethods(klass);
+ EnsureSkipAccessChecksMethods(klass);
return;
}
if (klass->IsCompileTimeVerified() && Runtime::Current()->IsAotCompiler()) {
@@ -3648,22 +3689,10 @@
mirror::Class::SetStatus(klass, mirror::Class::kStatusVerifyingAtRuntime, self);
}
- // Skip verification if we are forcing a soft fail.
- // This has to be before the normal verification enabled check,
- // since technically verification is disabled in this mode.
- if (UNLIKELY(Runtime::Current()->IsVerificationSoftFail())) {
- // Force verification to be a 'soft failure'.
- mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, self);
- // As this is a fake verified status, make sure the methods are _not_ marked preverified
- // later.
- klass->SetPreverified();
- return;
- }
-
// Skip verification if disabled.
if (!Runtime::Current()->IsVerificationEnabled()) {
mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, self);
- EnsurePreverifiedMethods(klass);
+ EnsureSkipAccessChecksMethods(klass);
return;
}
@@ -3766,9 +3795,9 @@
mirror::Class::SetStatus(klass, mirror::Class::kStatusRetryVerificationAtRuntime, self);
} else {
mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, self);
- // As this is a fake verified status, make sure the methods are _not_ marked preverified
- // later.
- klass->SetPreverified();
+ // As this is a fake verified status, make sure the methods are _not_ marked
+ // kAccSkipAccessChecks later.
+ klass->SetVerificationAttempted();
}
}
} else {
@@ -3781,19 +3810,26 @@
}
if (preverified || verifier_failure == verifier::MethodVerifier::kNoFailure) {
// Class is verified so we don't need to do any access check on its methods.
- // Let the interpreter know it by setting the kAccPreverified flag onto each
+ // Let the interpreter know it by setting the kAccSkipAccessChecks flag onto each
// method.
// Note: we're going here during compilation and at runtime. When we set the
- // kAccPreverified flag when compiling image classes, the flag is recorded
+ // kAccSkipAccessChecks flag when compiling image classes, the flag is recorded
// in the image and is set when loading the image.
- EnsurePreverifiedMethods(klass);
+
+ if (UNLIKELY(Runtime::Current()->IsVerificationSoftFail())) {
+ // Never skip access checks if the verification soft fail is forced.
+ // Mark the class as having a verification attempt to avoid re-running the verifier.
+ klass->SetVerificationAttempted();
+ } else {
+ EnsureSkipAccessChecksMethods(klass);
+ }
}
}
-void ClassLinker::EnsurePreverifiedMethods(Handle<mirror::Class> klass) {
- if (!klass->IsPreverified()) {
- klass->SetPreverifiedFlagOnAllMethods(image_pointer_size_);
- klass->SetPreverified();
+void ClassLinker::EnsureSkipAccessChecksMethods(Handle<mirror::Class> klass) {
+ if (!klass->WasVerificationAttempted()) {
+ klass->SetSkipAccessChecksFlagOnAllMethods(image_pointer_size_);
+ klass->SetVerificationAttempted();
}
}
@@ -3824,7 +3860,7 @@
}
// We may be running with a preopted oat file but without image. In this case,
- // we don't skip verification of preverified classes to ensure we initialize
+ // we don't skip verification of skip_access_checks classes to ensure we initialize
// dex caches with all types resolved during verification.
// We need to trust image classes, as these might be coming out of a pre-opted, quickened boot
// image (that we just failed loading), and the verifier can't be run on quickened opcodes when
@@ -3932,8 +3968,9 @@
}
DCHECK(klass->GetClass() != nullptr);
klass->SetObjectSize(sizeof(mirror::Proxy));
- // Set the class access flags incl. preverified, so we do not try to set the flag on the methods.
- klass->SetAccessFlags(kAccClassIsProxy | kAccPublic | kAccFinal | kAccPreverified);
+ // Set the class access flags incl. VerificationAttempted, so we do not try to set the flag on
+ // the methods.
+ klass->SetAccessFlags(kAccClassIsProxy | kAccPublic | kAccFinal | kAccVerificationAttempted);
klass->SetClassLoader(soa.Decode<mirror::ClassLoader*>(loader));
DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot);
klass->SetName(soa.Decode<mirror::String*>(name));
@@ -4104,9 +4141,10 @@
void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod* out) {
// Create constructor for Proxy that must initialize the method.
- CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 19u);
+ CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 18u);
ArtMethod* proxy_constructor = GetClassRoot(kJavaLangReflectProxy)->GetDirectMethodUnchecked(
2, image_pointer_size_);
+ DCHECK_EQ(std::string(proxy_constructor->GetName()), "<init>");
// Ensure constructor is in dex cache so that we can use the dex cache to look up the overridden
// constructor method.
GetClassRoot(kJavaLangReflectProxy)->GetDexCache()->SetResolvedMethod(
@@ -4741,7 +4779,7 @@
bool can_init_parents) {
DCHECK(c.Get() != nullptr);
if (c->IsInitialized()) {
- EnsurePreverifiedMethods(c);
+ EnsureSkipAccessChecksMethods(c);
return true;
}
const bool success = InitializeClass(self, c, can_init_fields, can_init_parents);
@@ -6436,11 +6474,11 @@
for (ArtMethod* def_method : default_methods) {
ArtMethod& new_method = *out;
new_method.CopyFrom(def_method, image_pointer_size_);
- // Clear the preverified flag if it is present. Since this class hasn't been verified yet it
- // shouldn't have methods that are preverified.
+ // Clear the kAccSkipAccessChecks flag if it is present. Since this class hasn't been verified
+ // yet it shouldn't have methods that are skipping access checks.
// TODO This is rather arbitrary. We should maybe support classes where only some of its
- // methods are preverified.
- new_method.SetAccessFlags((new_method.GetAccessFlags() | kAccDefault) & ~kAccPreverified);
+ // methods are skip_access_checks.
+ new_method.SetAccessFlags((new_method.GetAccessFlags() | kAccDefault) & ~kAccSkipAccessChecks);
move_table.emplace(def_method, &new_method);
++out;
}
@@ -6448,11 +6486,11 @@
ArtMethod& new_method = *out;
new_method.CopyFrom(conf_method, image_pointer_size_);
// This is a type of default method (there are default method impls, just a conflict) so mark
- // this as a default, non-abstract method, since thats what it is. Also clear the preverified
- // bit since this class hasn't been verified yet it shouldn't have methods that are
- // preverified.
+ // this as a default, non-abstract method, since thats what it is. Also clear the
+ // kAccSkipAccessChecks bit since this class hasn't been verified yet it shouldn't have
+ // methods that are skipping access checks.
constexpr uint32_t kSetFlags = kAccDefault | kAccDefaultConflict;
- constexpr uint32_t kMaskFlags = ~(kAccAbstract | kAccPreverified);
+ constexpr uint32_t kMaskFlags = ~(kAccAbstract | kAccSkipAccessChecks);
new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags);
DCHECK(new_method.IsDefaultConflicting());
// The actual method might or might not be marked abstract since we just copied it from a
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 9217c32..a9448f7 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -592,6 +592,9 @@
REQUIRES(!Locks::classlinker_classes_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
+ static bool ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* quick_code)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
struct DexCacheData {
// Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may
// not work properly.
@@ -964,9 +967,10 @@
void CreateProxyMethod(Handle<mirror::Class> klass, ArtMethod* prototype, ArtMethod* out)
SHARED_REQUIRES(Locks::mutator_lock_);
- // Ensures that methods have the kAccPreverified bit set. We use the kAccPreverfied bit on the
- // class access flags to determine whether this has been done before.
- void EnsurePreverifiedMethods(Handle<mirror::Class> c)
+ // Ensures that methods have the kAccSkipAccessChecks bit set. We use the
+ // kAccVerificationAttempted bit on the class access flags to determine whether this has been done
+ // before.
+ void EnsureSkipAccessChecksMethods(Handle<mirror::Class> c)
SHARED_REQUIRES(Locks::mutator_lock_);
mirror::Class* LookupClassFromBootImage(const char* descriptor)
@@ -1024,6 +1028,11 @@
REQUIRES(!dex_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
+ // Check that c1 == FindSystemClass(self, descriptor). Abort with class dumps otherwise.
+ void CheckSystemClass(Thread* self, Handle<mirror::Class> c1, const char* descriptor)
+ REQUIRES(!dex_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
std::vector<const DexFile*> boot_class_path_;
std::vector<std::unique_ptr<const DexFile>> boot_dex_files_;
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 40dfda9..3a0f3e5 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -210,11 +210,10 @@
klass->GetDescriptor(&temp2)));
if (klass->IsInterface()) {
EXPECT_TRUE(klass->IsAbstract());
- if (klass->NumDirectMethods() == 1) {
- EXPECT_TRUE(klass->GetDirectMethod(0, sizeof(void*))->IsClassInitializer());
- EXPECT_TRUE(klass->GetDirectMethod(0, sizeof(void*))->IsDirect());
- } else {
- EXPECT_EQ(0U, klass->NumDirectMethods());
+ // Check that all direct methods are static (either <clinit> or a regular static method).
+ for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) {
+ EXPECT_TRUE(m.IsStatic());
+ EXPECT_TRUE(m.IsDirect());
}
} else {
if (!klass->IsSynthetic()) {
@@ -1126,14 +1125,14 @@
static void CheckMethod(ArtMethod* method, bool verified)
SHARED_REQUIRES(Locks::mutator_lock_) {
if (!method->IsNative() && !method->IsAbstract()) {
- EXPECT_EQ((method->GetAccessFlags() & kAccPreverified) != 0U, verified)
+ EXPECT_EQ((method->GetAccessFlags() & kAccSkipAccessChecks) != 0U, verified)
<< PrettyMethod(method, true);
}
}
-static void CheckPreverified(mirror::Class* c, bool preverified)
+static void CheckVerificationAttempted(mirror::Class* c, bool preverified)
SHARED_REQUIRES(Locks::mutator_lock_) {
- EXPECT_EQ((c->GetAccessFlags() & kAccPreverified) != 0U, preverified)
+ EXPECT_EQ((c->GetAccessFlags() & kAccVerificationAttempted) != 0U, preverified)
<< "Class " << PrettyClass(c) << " not as expected";
for (auto& m : c->GetMethods(sizeof(void*))) {
CheckMethod(&m, preverified);
@@ -1147,7 +1146,7 @@
ASSERT_TRUE(JavaLangObject != nullptr);
EXPECT_TRUE(JavaLangObject->IsInitialized()) << "Not testing already initialized class from the "
"core";
- CheckPreverified(JavaLangObject, true);
+ CheckVerificationAttempted(JavaLangObject, true);
}
TEST_F(ClassLinkerTest, Preverified_UninitializedBoot) {
@@ -1160,10 +1159,10 @@
EXPECT_FALSE(security_manager->IsInitialized()) << "Not testing uninitialized class from the "
"core";
- CheckPreverified(security_manager.Get(), false);
+ CheckVerificationAttempted(security_manager.Get(), false);
class_linker_->EnsureInitialized(soa.Self(), security_manager, true, true);
- CheckPreverified(security_manager.Get(), true);
+ CheckVerificationAttempted(security_manager.Get(), true);
}
TEST_F(ClassLinkerTest, Preverified_App) {
@@ -1175,10 +1174,10 @@
Handle<mirror::Class> statics(
hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStatics;", class_loader)));
- CheckPreverified(statics.Get(), false);
+ CheckVerificationAttempted(statics.Get(), false);
class_linker_->EnsureInitialized(soa.Self(), statics, true, true);
- CheckPreverified(statics.Get(), true);
+ CheckVerificationAttempted(statics.Get(), true);
}
TEST_F(ClassLinkerTest, IsBootStrapClassLoaded) {
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 7a852e2..ddf2749 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -478,7 +478,7 @@
// Check field access flags.
std::string error_msg;
- if (!CheckFieldAccessFlags(access_flags, class_access_flags, &error_msg)) {
+ if (!CheckFieldAccessFlags(idx, access_flags, class_access_flags, &error_msg)) {
ErrorStringPrintf("%s", error_msg.c_str());
return false;
}
@@ -2312,12 +2312,88 @@
return count <= 1;
}
-bool DexFileVerifier::CheckFieldAccessFlags(uint32_t field_access_flags,
+// Helper functions to retrieve names from the dex file. We do not want to rely on DexFile
+// functionality, as we're still verifying the dex file. begin and header correspond to the
+// underscored variants in the DexFileVerifier.
+
+static std::string GetStringOrError(const uint8_t* const begin,
+ const DexFile::Header* const header,
+ uint32_t string_idx) {
+ if (header->string_ids_size_ < string_idx) {
+ return "(error)";
+ }
+
+ const DexFile::StringId* string_id =
+ reinterpret_cast<const DexFile::StringId*>(begin + header->string_ids_off_) + string_idx;
+
+ // Assume that the data is OK at this point. String data has been checked at this point.
+
+ const uint8_t* ptr = begin + string_id->string_data_off_;
+ DecodeUnsignedLeb128(&ptr);
+ return reinterpret_cast<const char*>(ptr);
+}
+
+static std::string GetClassOrError(const uint8_t* const begin,
+ const DexFile::Header* const header,
+ uint32_t class_idx) {
+ if (header->type_ids_size_ < class_idx) {
+ return "(error)";
+ }
+
+ const DexFile::TypeId* type_id =
+ reinterpret_cast<const DexFile::TypeId*>(begin + header->type_ids_off_) + class_idx;
+
+ // Assume that the data is OK at this point. Type id offsets have been checked at this point.
+
+ return GetStringOrError(begin, header, type_id->descriptor_idx_);
+}
+
+static std::string GetFieldDescriptionOrError(const uint8_t* const begin,
+ const DexFile::Header* const header,
+ uint32_t idx) {
+ if (header->field_ids_size_ < idx) {
+ return "(error)";
+ }
+
+ const DexFile::FieldId* field_id =
+ reinterpret_cast<const DexFile::FieldId*>(begin + header->field_ids_off_) + idx;
+
+ // Assume that the data is OK at this point. Field id offsets have been checked at this point.
+
+ std::string class_name = GetClassOrError(begin, header, field_id->class_idx_);
+ std::string field_name = GetStringOrError(begin, header, field_id->name_idx_);
+
+ return class_name + "." + field_name;
+}
+
+static std::string GetMethodDescriptionOrError(const uint8_t* const begin,
+ const DexFile::Header* const header,
+ uint32_t idx) {
+ if (header->method_ids_size_ < idx) {
+ return "(error)";
+ }
+
+ const DexFile::MethodId* method_id =
+ reinterpret_cast<const DexFile::MethodId*>(begin + header->method_ids_off_) + idx;
+
+ // Assume that the data is OK at this point. Method id offsets have been checked at this point.
+
+ std::string class_name = GetClassOrError(begin, header, method_id->class_idx_);
+ std::string method_name = GetStringOrError(begin, header, method_id->name_idx_);
+
+ return class_name + "." + method_name;
+}
+
+bool DexFileVerifier::CheckFieldAccessFlags(uint32_t idx,
+ uint32_t field_access_flags,
uint32_t class_access_flags,
std::string* error_msg) {
// Generally sort out >16-bit flags.
if ((field_access_flags & ~kAccJavaFlagsMask) != 0) {
- *error_msg = StringPrintf("Bad class_data_item field access_flags %x", field_access_flags);
+ *error_msg = StringPrintf("Bad field access_flags for %s: %x(%s)",
+ GetFieldDescriptionOrError(begin_, header_, idx).c_str(),
+ field_access_flags,
+ PrettyJavaAccessFlags(field_access_flags).c_str());
return false;
}
@@ -2334,8 +2410,10 @@
// Fields may have only one of public/protected/final.
if (!CheckAtMostOneOfPublicProtectedPrivate(field_access_flags)) {
- *error_msg = StringPrintf("Field may have only one of public/protected/private, %x",
- field_access_flags);
+ *error_msg = StringPrintf("Field may have only one of public/protected/private, %s: %x(%s)",
+ GetFieldDescriptionOrError(begin_, header_, idx).c_str(),
+ field_access_flags,
+ PrettyJavaAccessFlags(field_access_flags).c_str());
return false;
}
@@ -2344,14 +2422,19 @@
// Interface fields must be public final static.
constexpr uint32_t kPublicFinalStatic = kAccPublic | kAccFinal | kAccStatic;
if ((field_access_flags & kPublicFinalStatic) != kPublicFinalStatic) {
- *error_msg = StringPrintf("Interface field is not public final static: %x",
- field_access_flags);
+ *error_msg = StringPrintf("Interface field is not public final static, %s: %x(%s)",
+ GetFieldDescriptionOrError(begin_, header_, idx).c_str(),
+ field_access_flags,
+ PrettyJavaAccessFlags(field_access_flags).c_str());
return false;
}
// Interface fields may be synthetic, but may not have other flags.
constexpr uint32_t kDisallowed = ~(kPublicFinalStatic | kAccSynthetic);
if ((field_access_flags & kFieldAccessFlags & kDisallowed) != 0) {
- *error_msg = StringPrintf("Interface field has disallowed flag: %x", field_access_flags);
+ *error_msg = StringPrintf("Interface field has disallowed flag, %s: %x(%s)",
+ GetFieldDescriptionOrError(begin_, header_, idx).c_str(),
+ field_access_flags,
+ PrettyJavaAccessFlags(field_access_flags).c_str());
return false;
}
return true;
@@ -2360,7 +2443,8 @@
// Volatile fields may not be final.
constexpr uint32_t kVolatileFinal = kAccVolatile | kAccFinal;
if ((field_access_flags & kVolatileFinal) == kVolatileFinal) {
- *error_msg = "Fields may not be volatile and final";
+ *error_msg = StringPrintf("Fields may not be volatile and final: %s",
+ GetFieldDescriptionOrError(begin_, header_, idx).c_str());
return false;
}
@@ -2410,7 +2494,9 @@
constexpr uint32_t kAllMethodFlags =
kAccJavaFlagsMask | kAccConstructor | kAccDeclaredSynchronized;
if ((method_access_flags & ~kAllMethodFlags) != 0) {
- *error_msg = StringPrintf("Bad class_data_item method access_flags %x", method_access_flags);
+ *error_msg = StringPrintf("Bad method access_flags for %s: %x",
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str(),
+ method_access_flags);
return false;
}
@@ -2430,7 +2516,8 @@
// Methods may have only one of public/protected/final.
if (!CheckAtMostOneOfPublicProtectedPrivate(method_access_flags)) {
- *error_msg = StringPrintf("Method may have only one of public/protected/private, %x",
+ *error_msg = StringPrintf("Method may have only one of public/protected/private, %s: %x",
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str(),
method_access_flags);
return false;
}
@@ -2456,8 +2543,10 @@
// Only methods named "<clinit>" or "<init>" may be marked constructor. Note: we cannot enforce
// the reverse for backwards compatibility reasons.
if (((method_access_flags & kAccConstructor) != 0) && !is_constructor) {
- *error_msg = StringPrintf("Method %" PRIu32 " is marked constructor, but doesn't match name",
- method_index);
+ *error_msg =
+ StringPrintf("Method %" PRIu32 "(%s) is marked constructor, but doesn't match name",
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
return false;
}
// Check that the static constructor (= static initializer) is named "<clinit>" and that the
@@ -2465,8 +2554,9 @@
if (is_constructor) {
bool is_static = (method_access_flags & kAccStatic) != 0;
if (is_static ^ is_clinit_by_name) {
- *error_msg = StringPrintf("Constructor %" PRIu32 " is not flagged correctly wrt/ static.",
- method_index);
+ *error_msg = StringPrintf("Constructor %" PRIu32 "(%s) is not flagged correctly wrt/ static.",
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
return false;
}
}
@@ -2474,8 +2564,9 @@
// and other methods in the virtual methods list.
bool is_direct = (method_access_flags & (kAccStatic | kAccPrivate)) != 0 || is_constructor;
if (is_direct != expect_direct) {
- *error_msg = StringPrintf("Direct/virtual method %" PRIu32 " not in expected list %d",
+ *error_msg = StringPrintf("Direct/virtual method %" PRIu32 "(%s) not in expected list %d",
method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str(),
expect_direct);
return false;
}
@@ -2488,14 +2579,17 @@
if (!has_code) {
// Only native or abstract methods may not have code.
if ((method_access_flags & (kAccNative | kAccAbstract)) == 0) {
- *error_msg = StringPrintf("Method %" PRIu32 " has no code, but is not marked native or "
+ *error_msg = StringPrintf("Method %" PRIu32 "(%s) has no code, but is not marked native or "
"abstract",
- method_index);
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
return false;
}
// Constructors must always have code.
if (is_constructor) {
- *error_msg = StringPrintf("Constructor %u must not be abstract or native", method_index);
+ *error_msg = StringPrintf("Constructor %u(%s) must not be abstract or native",
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
return false;
}
if ((method_access_flags & kAccAbstract) != 0) {
@@ -2503,14 +2597,15 @@
constexpr uint32_t kForbidden =
kAccPrivate | kAccStatic | kAccFinal | kAccNative | kAccStrict | kAccSynchronized;
if ((method_access_flags & kForbidden) != 0) {
- *error_msg = StringPrintf("Abstract method %" PRIu32 " has disallowed access flags %x",
- method_index,
- method_access_flags);
+ *error_msg = StringPrintf("Abstract method %" PRIu32 "(%s) has disallowed access flags %x",
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str(),
+ method_access_flags);
return false;
}
// Abstract methods should be in an abstract class or interface.
if ((class_access_flags & (kAccInterface | kAccAbstract)) == 0) {
- LOG(WARNING) << "Method " << PrettyMethod(method_index, *dex_file_)
+ LOG(WARNING) << "Method " << GetMethodDescriptionOrError(begin_, header_, method_index)
<< " is abstract, but the declaring class is neither abstract nor an "
<< "interface in dex file "
<< dex_file_->GetLocation();
@@ -2520,8 +2615,9 @@
if ((class_access_flags & kAccInterface) != 0) {
// Interface methods must be public and abstract.
if ((method_access_flags & (kAccPublic | kAccAbstract)) != (kAccPublic | kAccAbstract)) {
- *error_msg = StringPrintf("Interface method %" PRIu32 " is not public and abstract",
- method_index);
+ *error_msg = StringPrintf("Interface method %" PRIu32 "(%s) is not public and abstract",
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
return false;
}
// At this point, we know the method is public and abstract. This means that all the checks
@@ -2533,8 +2629,9 @@
// When there's code, the method must not be native or abstract.
if ((method_access_flags & (kAccNative | kAccAbstract)) != 0) {
- *error_msg = StringPrintf("Method %" PRIu32 " has code, but is marked native or abstract",
- method_index);
+ *error_msg = StringPrintf("Method %" PRIu32 "(%s) has code, but is marked native or abstract",
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
return false;
}
@@ -2543,8 +2640,9 @@
static constexpr uint32_t kInitAllowed =
kAccPrivate | kAccProtected | kAccPublic | kAccStrict | kAccVarargs | kAccSynthetic;
if ((method_access_flags & ~kInitAllowed) != 0) {
- *error_msg = StringPrintf("Constructor %" PRIu32 " flagged inappropriately %x",
+ *error_msg = StringPrintf("Constructor %" PRIu32 "(%s) flagged inappropriately %x",
method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str(),
method_access_flags);
return false;
}
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index 6c63749..ddfeea2 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -157,9 +157,10 @@
// Check validity of the given access flags, interpreted for a field in the context of a class
// with the given second access flags.
- static bool CheckFieldAccessFlags(uint32_t field_access_flags,
- uint32_t class_access_flags,
- std::string* error_msg);
+ bool CheckFieldAccessFlags(uint32_t idx,
+ uint32_t field_access_flags,
+ uint32_t class_access_flags,
+ std::string* error_msg);
// Check validity of the given method and access flags, in the context of a class with the given
// second access flags.
bool CheckMethodAccessFlags(uint32_t method_index,
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index b67af53..558a6ed 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -527,7 +527,7 @@
ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
OrMaskToMethodFlags(dex_file, "<init>", kAccStatic);
},
- "Constructor 1 is not flagged correctly wrt/ static");
+ "Constructor 1(LMethodFlags;.<init>) is not flagged correctly wrt/ static");
static constexpr uint32_t kInitNotAllowed[] = {
kAccFinal,
kAccSynchronized,
@@ -544,7 +544,7 @@
ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
OrMaskToMethodFlags(dex_file, "<init>", kInitNotAllowed[i]);
},
- "Constructor 1 flagged inappropriately");
+ "Constructor 1(LMethodFlags;.<init>) flagged inappropriately");
}
}
@@ -742,7 +742,7 @@
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
},
- "Interface method 1 is not public and abstract");
+ "Interface method 1(LInterfaceMethodFlags;.foo) is not public and abstract");
VerifyModification(
kMethodFlagsInterface,
"method_flags_interface_non_abstract",
@@ -751,7 +751,7 @@
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccAbstract);
},
- "Method 1 has no code, but is not marked native or abstract");
+ "Method 1(LInterfaceMethodFlags;.foo) has no code, but is not marked native or abstract");
VerifyModification(
kMethodFlagsInterface,
@@ -761,7 +761,7 @@
OrMaskToMethodFlags(dex_file, "foo", kAccStatic);
},
- "Direct/virtual method 1 not in expected list 0");
+ "Direct/virtual method 1(LInterfaceMethodFlags;.foo) not in expected list 0");
VerifyModification(
kMethodFlagsInterface,
"method_flags_interface_private",
@@ -771,7 +771,7 @@
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
OrMaskToMethodFlags(dex_file, "foo", kAccPrivate);
},
- "Direct/virtual method 1 not in expected list 0");
+ "Direct/virtual method 1(LInterfaceMethodFlags;.foo) not in expected list 0");
VerifyModification(
kMethodFlagsInterface,
@@ -781,7 +781,7 @@
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
},
- "Interface method 1 is not public and abstract");
+ "Interface method 1(LInterfaceMethodFlags;.foo) is not public and abstract");
VerifyModification(
kMethodFlagsInterface,
"method_flags_interface_protected",
@@ -791,7 +791,7 @@
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
OrMaskToMethodFlags(dex_file, "foo", kAccProtected);
},
- "Interface method 1 is not public and abstract");
+ "Interface method 1(LInterfaceMethodFlags;.foo) is not public and abstract");
constexpr uint32_t kAllMethodFlags =
kAccPublic |
@@ -831,7 +831,7 @@
}
OrMaskToMethodFlags(dex_file, "foo", mask);
},
- "Abstract method 1 has disallowed access flags");
+ "Abstract method 1(LInterfaceMethodFlags;.foo) has disallowed access flags");
}
}
diff --git a/runtime/dex_instruction_utils.h b/runtime/dex_instruction_utils.h
index 1ae2b1b..2849cd8 100644
--- a/runtime/dex_instruction_utils.h
+++ b/runtime/dex_instruction_utils.h
@@ -49,6 +49,16 @@
// NOTE: The following functions disregard quickened instructions.
+// By "direct" const we mean to exclude const-string and const-class
+// which load data from somewhere else, i.e. indirectly.
+constexpr bool IsInstructionDirectConst(Instruction::Code opcode) {
+ return Instruction::CONST_4 <= opcode && opcode <= Instruction::CONST_WIDE_HIGH16;
+}
+
+constexpr bool IsInstructionConstWide(Instruction::Code opcode) {
+ return Instruction::CONST_WIDE_16 <= opcode && opcode <= Instruction::CONST_WIDE_HIGH16;
+}
+
constexpr bool IsInstructionReturn(Instruction::Code opcode) {
return Instruction::RETURN_VOID <= opcode && opcode <= Instruction::RETURN_OBJECT;
}
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 0663b7e..7e7d904 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -299,8 +299,10 @@
}
template<FindFieldType type, bool access_check>
-inline ArtField* FindFieldFromCode(uint32_t field_idx, ArtMethod* referrer,
- Thread* self, size_t expected_size) {
+inline ArtField* FindFieldFromCode(uint32_t field_idx,
+ ArtMethod* referrer,
+ Thread* self,
+ size_t expected_size) REQUIRES(!Roles::uninterruptible_) {
bool is_primitive;
bool is_set;
bool is_static;
@@ -316,7 +318,31 @@
default: is_primitive = true; is_set = true; is_static = true; break;
}
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- ArtField* resolved_field = class_linker->ResolveField(field_idx, referrer, is_static);
+
+ ArtField* resolved_field;
+ if (access_check) {
+ // Slow path: According to JLS 13.4.8, a linkage error may occur if a compile-time
+ // qualifying type of a field and the resolved run-time qualifying type of a field differed
+ // in their static-ness.
+ //
+ // In particular, don't assume the dex instruction already correctly knows if the
+ // real field is static or not. The resolution must not be aware of this.
+ ArtMethod* method = referrer->GetInterfaceMethodIfProxy(sizeof(void*));
+
+ StackHandleScope<2> hs(self);
+ Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(method->GetDexCache()));
+ Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(method->GetClassLoader()));
+
+ resolved_field = class_linker->ResolveFieldJLS(*method->GetDexFile(),
+ field_idx,
+ h_dex_cache,
+ h_class_loader);
+ } else {
+ // Fast path: Verifier already would've called ResolveFieldJLS and we wouldn't
+ // be executing here if there was a static/non-static mismatch.
+ resolved_field = class_linker->ResolveField(field_idx, referrer, is_static);
+ }
+
if (UNLIKELY(resolved_field == nullptr)) {
DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
return nullptr; // Failure.
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index b5a55bf..3dfad76 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -273,15 +273,15 @@
if (outer_method != nullptr) {
const OatQuickMethodHeader* current_code = outer_method->GetOatQuickMethodHeader(caller_pc);
if (current_code->IsOptimized()) {
- uintptr_t native_pc_offset = current_code->NativeQuickPcOffset(caller_pc);
- CodeInfo code_info = current_code->GetOptimizedCodeInfo();
- StackMapEncoding encoding = code_info.ExtractEncoding();
- StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
- DCHECK(stack_map.IsValid());
- if (stack_map.HasInlineInfo(encoding)) {
- InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
- caller = GetResolvedMethod(outer_method, inline_info, inline_info.GetDepth() - 1);
- }
+ uintptr_t native_pc_offset = current_code->NativeQuickPcOffset(caller_pc);
+ CodeInfo code_info = current_code->GetOptimizedCodeInfo();
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
+ DCHECK(stack_map.IsValid());
+ if (stack_map.HasInlineInfo(encoding)) {
+ InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
+ caller = GetResolvedMethod(outer_method, inline_info, inline_info.GetDepth() - 1);
+ }
}
}
if (kIsDebugBuild && do_caller_check) {
diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc
index 1850254..a245f18 100644
--- a/runtime/entrypoints/quick/quick_field_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc
@@ -27,7 +27,36 @@
namespace art {
-extern "C" int8_t artGetByteStaticFromCode(uint32_t field_idx, ArtMethod* referrer,
+inline constexpr bool FindFieldTypeIsRead(FindFieldType type) {
+ return type == InstanceObjectRead ||
+ type == InstancePrimitiveRead ||
+ type == StaticObjectRead ||
+ type == StaticPrimitiveRead;
+}
+
+// Helper function to do a null check after trying to resolve the field. Not for statics since obj
+// does not exist there. There is a suspend check, object is a double pointer to update the value
+// in the caller in case it moves.
+template<FindFieldType type, bool kAccessCheck>
+ALWAYS_INLINE static inline ArtField* FindInstanceField(uint32_t field_idx,
+ ArtMethod* referrer,
+ Thread* self,
+ size_t size,
+ mirror::Object** obj)
+ REQUIRES(!Roles::uninterruptible_)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ StackHandleScope<1> hs(self);
+ HandleWrapper<mirror::Object> h(hs.NewHandleWrapper(obj));
+ ArtField* field = FindFieldFromCode<type, kAccessCheck>(field_idx, referrer, self, size);
+ if (LIKELY(field != nullptr) && UNLIKELY(h.Get() == nullptr)) {
+ ThrowNullPointerExceptionForFieldAccess(field, /*is_read*/FindFieldTypeIsRead(type));
+ return nullptr;
+ }
+ return field;
+}
+
+extern "C" int8_t artGetByteStaticFromCode(uint32_t field_idx,
+ ArtMethod* referrer,
Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
@@ -42,7 +71,8 @@
return 0; // Will throw exception by checking with Thread::Current.
}
-extern "C" uint8_t artGetBooleanStaticFromCode(uint32_t field_idx, ArtMethod* referrer,
+extern "C" uint8_t artGetBooleanStaticFromCode(uint32_t field_idx,
+ ArtMethod* referrer,
Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
@@ -57,7 +87,8 @@
return 0; // Will throw exception by checking with Thread::Current.
}
-extern "C" int16_t artGetShortStaticFromCode(uint32_t field_idx, ArtMethod* referrer,
+extern "C" int16_t artGetShortStaticFromCode(uint32_t field_idx,
+ ArtMethod* referrer,
Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
@@ -125,12 +156,16 @@
Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
- ArtField* field = FindFieldFast(field_idx, referrer, StaticObjectRead,
+ ArtField* field = FindFieldFast(field_idx,
+ referrer,
+ StaticObjectRead,
sizeof(mirror::HeapReference<mirror::Object>));
if (LIKELY(field != nullptr)) {
return field->GetObj(field->GetDeclaringClass());
}
- field = FindFieldFromCode<StaticObjectRead, true>(field_idx, referrer, self,
+ field = FindFieldFromCode<StaticObjectRead, true>(field_idx,
+ referrer,
+ self,
sizeof(mirror::HeapReference<mirror::Object>));
if (LIKELY(field != nullptr)) {
return field->GetObj(field->GetDeclaringClass());
@@ -138,149 +173,159 @@
return nullptr; // Will throw exception by checking with Thread::Current.
}
-extern "C" int8_t artGetByteInstanceFromCode(uint32_t field_idx, mirror::Object* obj,
- ArtMethod* referrer, Thread* self)
+extern "C" int8_t artGetByteInstanceFromCode(uint32_t field_idx,
+ mirror::Object* obj,
+ ArtMethod* referrer,
+ Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(int8_t));
if (LIKELY(field != nullptr && obj != nullptr)) {
return field->GetByte(obj);
}
- field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
- sizeof(int8_t));
+ field = FindInstanceField<InstancePrimitiveRead, true>(field_idx,
+ referrer,
+ self,
+ sizeof(int8_t),
+ &obj);
if (LIKELY(field != nullptr)) {
- if (UNLIKELY(obj == nullptr)) {
- ThrowNullPointerExceptionForFieldAccess(field, true);
- } else {
- return field->GetByte(obj);
- }
+ return field->GetByte(obj);
}
return 0; // Will throw exception by checking with Thread::Current.
}
-extern "C" uint8_t artGetBooleanInstanceFromCode(uint32_t field_idx, mirror::Object* obj,
- ArtMethod* referrer, Thread* self)
+extern "C" uint8_t artGetBooleanInstanceFromCode(uint32_t field_idx,
+ mirror::Object* obj,
+ ArtMethod* referrer,
+ Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(int8_t));
if (LIKELY(field != nullptr && obj != nullptr)) {
return field->GetBoolean(obj);
}
- field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
- sizeof(int8_t));
+ field = FindInstanceField<InstancePrimitiveRead, true>(field_idx,
+ referrer,
+ self,
+ sizeof(int8_t),
+ &obj);
if (LIKELY(field != nullptr)) {
- if (UNLIKELY(obj == nullptr)) {
- ThrowNullPointerExceptionForFieldAccess(field, true);
- } else {
- return field->GetBoolean(obj);
- }
+ return field->GetBoolean(obj);
}
return 0; // Will throw exception by checking with Thread::Current.
}
-extern "C" int16_t artGetShortInstanceFromCode(uint32_t field_idx, mirror::Object* obj,
- ArtMethod* referrer, Thread* self)
+extern "C" int16_t artGetShortInstanceFromCode(uint32_t field_idx,
+ mirror::Object* obj,
+ ArtMethod* referrer,
+ Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(int16_t));
if (LIKELY(field != nullptr && obj != nullptr)) {
return field->GetShort(obj);
}
- field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
- sizeof(int16_t));
+ field = FindInstanceField<InstancePrimitiveRead, true>(field_idx,
+ referrer,
+ self,
+ sizeof(int16_t),
+ &obj);
if (LIKELY(field != nullptr)) {
- if (UNLIKELY(obj == nullptr)) {
- ThrowNullPointerExceptionForFieldAccess(field, true);
- } else {
- return field->GetShort(obj);
- }
+ return field->GetShort(obj);
}
return 0; // Will throw exception by checking with Thread::Current.
}
-extern "C" uint16_t artGetCharInstanceFromCode(uint32_t field_idx, mirror::Object* obj,
- ArtMethod* referrer, Thread* self)
+extern "C" uint16_t artGetCharInstanceFromCode(uint32_t field_idx,
+ mirror::Object* obj,
+ ArtMethod* referrer,
+ Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(int16_t));
if (LIKELY(field != nullptr && obj != nullptr)) {
return field->GetChar(obj);
}
- field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
- sizeof(int16_t));
+ field = FindInstanceField<InstancePrimitiveRead, true>(field_idx,
+ referrer,
+ self,
+ sizeof(int16_t),
+ &obj);
if (LIKELY(field != nullptr)) {
- if (UNLIKELY(obj == nullptr)) {
- ThrowNullPointerExceptionForFieldAccess(field, true);
- } else {
- return field->GetChar(obj);
- }
+ return field->GetChar(obj);
}
return 0; // Will throw exception by checking with Thread::Current.
}
-extern "C" uint32_t artGet32InstanceFromCode(uint32_t field_idx, mirror::Object* obj,
- ArtMethod* referrer, Thread* self)
+extern "C" uint32_t artGet32InstanceFromCode(uint32_t field_idx,
+ mirror::Object* obj,
+ ArtMethod* referrer,
+ Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(int32_t));
if (LIKELY(field != nullptr && obj != nullptr)) {
return field->Get32(obj);
}
- field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
- sizeof(int32_t));
+ field = FindInstanceField<InstancePrimitiveRead, true>(field_idx,
+ referrer,
+ self,
+ sizeof(int32_t),
+ &obj);
if (LIKELY(field != nullptr)) {
- if (UNLIKELY(obj == nullptr)) {
- ThrowNullPointerExceptionForFieldAccess(field, true);
- } else {
- return field->Get32(obj);
- }
+ return field->Get32(obj);
}
return 0; // Will throw exception by checking with Thread::Current.
}
-extern "C" uint64_t artGet64InstanceFromCode(uint32_t field_idx, mirror::Object* obj,
- ArtMethod* referrer, Thread* self)
+extern "C" uint64_t artGet64InstanceFromCode(uint32_t field_idx,
+ mirror::Object* obj,
+ ArtMethod* referrer,
+ Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead, sizeof(int64_t));
if (LIKELY(field != nullptr && obj != nullptr)) {
return field->Get64(obj);
}
- field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
- sizeof(int64_t));
+ field = FindInstanceField<InstancePrimitiveRead, true>(field_idx,
+ referrer,
+ self,
+ sizeof(int64_t),
+ &obj);
if (LIKELY(field != nullptr)) {
- if (UNLIKELY(obj == nullptr)) {
- ThrowNullPointerExceptionForFieldAccess(field, true);
- } else {
- return field->Get64(obj);
- }
+ return field->Get64(obj);
}
return 0; // Will throw exception by checking with Thread::Current.
}
-extern "C" mirror::Object* artGetObjInstanceFromCode(uint32_t field_idx, mirror::Object* obj,
+extern "C" mirror::Object* artGetObjInstanceFromCode(uint32_t field_idx,
+ mirror::Object* obj,
ArtMethod* referrer,
Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
- ArtField* field = FindFieldFast(field_idx, referrer, InstanceObjectRead,
+ ArtField* field = FindFieldFast(field_idx,
+ referrer,
+ InstanceObjectRead,
sizeof(mirror::HeapReference<mirror::Object>));
if (LIKELY(field != nullptr && obj != nullptr)) {
return field->GetObj(obj);
}
- field = FindFieldFromCode<InstanceObjectRead, true>(
- field_idx, referrer, self, sizeof(mirror::HeapReference<mirror::Object>));
+ field = FindInstanceField<InstanceObjectRead, true>(field_idx,
+ referrer,
+ self,
+ sizeof(mirror::HeapReference<mirror::Object>),
+ &obj);
if (LIKELY(field != nullptr)) {
- if (UNLIKELY(obj == nullptr)) {
- ThrowNullPointerExceptionForFieldAccess(field, true);
- } else {
- return field->GetObj(obj);
- }
+ return field->GetObj(obj);
}
return nullptr; // Will throw exception by checking with Thread::Current.
}
-extern "C" int artSet8StaticFromCode(uint32_t field_idx, uint32_t new_value,
- ArtMethod* referrer, Thread* self)
+extern "C" int artSet8StaticFromCode(uint32_t field_idx,
+ uint32_t new_value,
+ ArtMethod* referrer,
+ Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(int8_t));
@@ -310,8 +355,10 @@
return -1; // failure
}
-extern "C" int artSet16StaticFromCode(uint32_t field_idx, uint16_t new_value,
- ArtMethod* referrer, Thread* self)
+extern "C" int artSet16StaticFromCode(uint32_t field_idx,
+ uint16_t new_value,
+ ArtMethod* referrer,
+ Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(int16_t));
@@ -341,8 +388,10 @@
return -1; // failure
}
-extern "C" int artSet32StaticFromCode(uint32_t field_idx, uint32_t new_value,
- ArtMethod* referrer, Thread* self)
+extern "C" int artSet32StaticFromCode(uint32_t field_idx,
+ uint32_t new_value,
+ ArtMethod* referrer,
+ Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(int32_t));
@@ -360,8 +409,10 @@
return -1; // failure
}
-extern "C" int artSet64StaticFromCode(uint32_t field_idx, ArtMethod* referrer,
- uint64_t new_value, Thread* self)
+extern "C" int artSet64StaticFromCode(uint32_t field_idx,
+ ArtMethod* referrer,
+ uint64_t new_value,
+ Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(int64_t));
@@ -379,11 +430,15 @@
return -1; // failure
}
-extern "C" int artSetObjStaticFromCode(uint32_t field_idx, mirror::Object* new_value,
- ArtMethod* referrer, Thread* self)
+extern "C" int artSetObjStaticFromCode(uint32_t field_idx,
+ mirror::Object* new_value,
+ ArtMethod* referrer,
+ Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
- ArtField* field = FindFieldFast(field_idx, referrer, StaticObjectWrite,
+ ArtField* field = FindFieldFast(field_idx,
+ referrer,
+ StaticObjectWrite,
sizeof(mirror::HeapReference<mirror::Object>));
if (LIKELY(field != nullptr)) {
if (LIKELY(!field->IsPrimitiveType())) {
@@ -392,8 +447,15 @@
return 0; // success
}
}
- field = FindFieldFromCode<StaticObjectWrite, true>(field_idx, referrer, self,
- sizeof(mirror::HeapReference<mirror::Object>));
+ {
+ StackHandleScope<1> hs(self);
+ HandleWrapper<mirror::Object> h_obj(hs.NewHandleWrapper(&new_value));
+ field = FindFieldFromCode<StaticObjectWrite, true>(
+ field_idx,
+ referrer,
+ self,
+ sizeof(mirror::HeapReference<mirror::Object>));
+ }
if (LIKELY(field != nullptr)) {
// Compiled code can't use transactional mode.
field->SetObj<false>(field->GetDeclaringClass(), new_value);
@@ -402,8 +464,11 @@
return -1; // failure
}
-extern "C" int artSet8InstanceFromCode(uint32_t field_idx, mirror::Object* obj, uint8_t new_value,
- ArtMethod* referrer, Thread* self)
+extern "C" int artSet8InstanceFromCode(uint32_t field_idx,
+ mirror::Object* obj,
+ uint8_t new_value,
+ ArtMethod* referrer,
+ Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(int8_t));
@@ -418,31 +483,29 @@
}
return 0; // success
}
- {
- StackHandleScope<1> hs(self);
- HandleWrapper<mirror::Object> h_obj(hs.NewHandleWrapper(&obj));
- field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, self,
- sizeof(int8_t));
- }
+ field = FindInstanceField<InstancePrimitiveWrite, true>(field_idx,
+ referrer,
+ self,
+ sizeof(int8_t),
+ &obj);
if (LIKELY(field != nullptr)) {
- if (UNLIKELY(obj == nullptr)) {
- ThrowNullPointerExceptionForFieldAccess(field, false);
+ Primitive::Type type = field->GetTypeAsPrimitiveType();
+ // Compiled code can't use transactional mode.
+ if (type == Primitive::kPrimBoolean) {
+ field->SetBoolean<false>(obj, new_value);
} else {
- Primitive::Type type = field->GetTypeAsPrimitiveType();
- // Compiled code can't use transactional mode.
- if (type == Primitive::kPrimBoolean) {
- field->SetBoolean<false>(obj, new_value);
- } else {
- field->SetByte<false>(obj, new_value);
- }
- return 0; // success
+ field->SetByte<false>(obj, new_value);
}
+ return 0; // success
}
return -1; // failure
}
-extern "C" int artSet16InstanceFromCode(uint32_t field_idx, mirror::Object* obj, uint16_t new_value,
- ArtMethod* referrer, Thread* self)
+extern "C" int artSet16InstanceFromCode(uint32_t field_idx,
+ mirror::Object* obj,
+ uint16_t new_value,
+ ArtMethod* referrer,
+ Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(int16_t));
@@ -457,32 +520,30 @@
}
return 0; // success
}
- {
- StackHandleScope<1> hs(self);
- HandleWrapper<mirror::Object> h_obj(hs.NewHandleWrapper(&obj));
- field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, self,
- sizeof(int16_t));
- }
+ field = FindInstanceField<InstancePrimitiveWrite, true>(field_idx,
+ referrer,
+ self,
+ sizeof(int16_t),
+ &obj);
if (LIKELY(field != nullptr)) {
- if (UNLIKELY(obj == nullptr)) {
- ThrowNullPointerExceptionForFieldAccess(field, false);
+ Primitive::Type type = field->GetTypeAsPrimitiveType();
+ // Compiled code can't use transactional mode.
+ if (type == Primitive::kPrimChar) {
+ field->SetChar<false>(obj, new_value);
} else {
- Primitive::Type type = field->GetTypeAsPrimitiveType();
- // Compiled code can't use transactional mode.
- if (type == Primitive::kPrimChar) {
- field->SetChar<false>(obj, new_value);
- } else {
- DCHECK_EQ(Primitive::kPrimShort, type);
- field->SetShort<false>(obj, new_value);
- }
- return 0; // success
+ DCHECK_EQ(Primitive::kPrimShort, type);
+ field->SetShort<false>(obj, new_value);
}
+ return 0; // success
}
return -1; // failure
}
-extern "C" int artSet32InstanceFromCode(uint32_t field_idx, mirror::Object* obj, uint32_t new_value,
- ArtMethod* referrer, Thread* self)
+extern "C" int artSet32InstanceFromCode(uint32_t field_idx,
+ mirror::Object* obj,
+ uint32_t new_value,
+ ArtMethod* referrer,
+ Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(int32_t));
@@ -491,26 +552,24 @@
field->Set32<false>(obj, new_value);
return 0; // success
}
- {
- StackHandleScope<1> hs(self);
- HandleWrapper<mirror::Object> h_obj(hs.NewHandleWrapper(&obj));
- field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, self,
- sizeof(int32_t));
- }
+ field = FindInstanceField<InstancePrimitiveWrite, true>(field_idx,
+ referrer,
+ self,
+ sizeof(int32_t),
+ &obj);
if (LIKELY(field != nullptr)) {
- if (UNLIKELY(obj == nullptr)) {
- ThrowNullPointerExceptionForFieldAccess(field, false);
- } else {
- // Compiled code can't use transactional mode.
- field->Set32<false>(obj, new_value);
- return 0; // success
- }
+ // Compiled code can't use transactional mode.
+ field->Set32<false>(obj, new_value);
+ return 0; // success
}
return -1; // failure
}
-extern "C" int artSet64InstanceFromCode(uint32_t field_idx, mirror::Object* obj, uint64_t new_value,
- ArtMethod* referrer, Thread* self)
+extern "C" int artSet64InstanceFromCode(uint32_t field_idx,
+ mirror::Object* obj,
+ uint64_t new_value,
+ ArtMethod* referrer,
+ Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(int64_t));
@@ -519,34 +578,45 @@
field->Set64<false>(obj, new_value);
return 0; // success
}
- field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, self,
- sizeof(int64_t));
+ field = FindInstanceField<InstancePrimitiveWrite, true>(field_idx,
+ referrer,
+ self,
+ sizeof(int64_t),
+ &obj);
if (LIKELY(field != nullptr)) {
- if (UNLIKELY(obj == nullptr)) {
- ThrowNullPointerExceptionForFieldAccess(field, false);
- } else {
- // Compiled code can't use transactional mode.
- field->Set64<false>(obj, new_value);
- return 0; // success
- }
+ // Compiled code can't use transactional mode.
+ field->Set64<false>(obj, new_value);
+ return 0;
}
return -1; // failure
}
-extern "C" int artSetObjInstanceFromCode(uint32_t field_idx, mirror::Object* obj,
+extern "C" int artSetObjInstanceFromCode(uint32_t field_idx,
+ mirror::Object* obj,
mirror::Object* new_value,
- ArtMethod* referrer, Thread* self)
+ ArtMethod* referrer,
+ Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
- ArtField* field = FindFieldFast(field_idx, referrer, InstanceObjectWrite,
+ ArtField* field = FindFieldFast(field_idx,
+ referrer,
+ InstanceObjectWrite,
sizeof(mirror::HeapReference<mirror::Object>));
if (LIKELY(field != nullptr && obj != nullptr)) {
// Compiled code can't use transactional mode.
field->SetObj<false>(obj, new_value);
return 0; // success
}
- field = FindFieldFromCode<InstanceObjectWrite, true>(field_idx, referrer, self,
- sizeof(mirror::HeapReference<mirror::Object>));
+ {
+ StackHandleScope<2> hs(self);
+ HandleWrapper<mirror::Object> h_obj(hs.NewHandleWrapper(&obj));
+ HandleWrapper<mirror::Object> h_new_value(hs.NewHandleWrapper(&new_value));
+ field = FindFieldFromCode<InstanceObjectWrite, true>(
+ field_idx,
+ referrer,
+ self,
+ sizeof(mirror::HeapReference<mirror::Object>));
+ }
if (LIKELY(field != nullptr)) {
if (UNLIKELY(obj == nullptr)) {
ThrowNullPointerExceptionForFieldAccess(field, false);
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index f87d48d..e72809b 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -127,7 +127,7 @@
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_default_ibase, mterp_alt_ibase, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_alt_ibase, rosalloc_runs, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, rosalloc_runs, thread_local_alloc_stack_top,
- sizeof(void*) * kNumRosAllocThreadLocalSizeBrackets);
+ sizeof(void*) * kNumRosAllocThreadLocalSizeBracketsInThread);
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_alloc_stack_top, thread_local_alloc_stack_end,
sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_alloc_stack_end, held_mutexes, sizeof(void*));
diff --git a/runtime/gc/allocator/rosalloc-inl.h b/runtime/gc/allocator/rosalloc-inl.h
index 2510514..d1c81e3 100644
--- a/runtime/gc/allocator/rosalloc-inl.h
+++ b/runtime/gc/allocator/rosalloc-inl.h
@@ -62,11 +62,6 @@
}
size_t bracket_size;
size_t idx = SizeToIndexAndBracketSize(size, &bracket_size);
- DCHECK_EQ(idx, SizeToIndex(size));
- DCHECK_EQ(bracket_size, IndexToBracketSize(idx));
- DCHECK_EQ(bracket_size, bracketSizes[idx]);
- DCHECK_LE(size, bracket_size);
- DCHECK(size > 512 || bracket_size - size < 16);
DCHECK_LT(idx, kNumThreadLocalSizeBrackets);
Run* thread_local_run = reinterpret_cast<Run*>(self->GetRosAllocRun(idx));
if (kIsDebugBuild) {
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index 7d00094..8b125dd 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -638,11 +638,6 @@
DCHECK_LE(size, kLargeSizeThreshold);
size_t bracket_size;
size_t idx = SizeToIndexAndBracketSize(size, &bracket_size);
- DCHECK_EQ(idx, SizeToIndex(size));
- DCHECK_EQ(bracket_size, IndexToBracketSize(idx));
- DCHECK_EQ(bracket_size, bracketSizes[idx]);
- DCHECK_LE(size, bracket_size);
- DCHECK(size > 512 || bracket_size - size < 16);
Locks::mutator_lock_->AssertExclusiveHeld(self);
void* slot_addr = AllocFromCurrentRunUnlocked(self, idx);
if (LIKELY(slot_addr != nullptr)) {
@@ -662,14 +657,7 @@
DCHECK_LE(size, kLargeSizeThreshold);
size_t bracket_size;
size_t idx = SizeToIndexAndBracketSize(size, &bracket_size);
- DCHECK_EQ(idx, SizeToIndex(size));
- DCHECK_EQ(bracket_size, IndexToBracketSize(idx));
- DCHECK_EQ(bracket_size, bracketSizes[idx]);
- DCHECK_LE(size, bracket_size);
- DCHECK(size > 512 || bracket_size - size < 16);
-
void* slot_addr;
-
if (LIKELY(idx < kNumThreadLocalSizeBrackets)) {
// Use a thread-local run.
Run* thread_local_run = reinterpret_cast<Run*>(self->GetRosAllocRun(idx));
@@ -881,17 +869,6 @@
return stream.str();
}
-inline size_t RosAlloc::Run::SlotIndex(Slot* slot) {
- const uint8_t idx = size_bracket_idx_;
- const size_t bracket_size = bracketSizes[idx];
- const size_t offset_from_slot_base = reinterpret_cast<uint8_t*>(slot)
- - reinterpret_cast<uint8_t*>(FirstSlot());
- DCHECK_EQ(offset_from_slot_base % bracket_size, static_cast<size_t>(0));
- size_t slot_idx = offset_from_slot_base / bracket_size;
- DCHECK_LT(slot_idx, numOfSlots[idx]);
- return slot_idx;
-}
-
void RosAlloc::Run::FreeSlot(void* ptr) {
DCHECK(!IsThreadLocal());
const uint8_t idx = size_bracket_idx_;
@@ -1647,9 +1624,14 @@
void RosAlloc::Initialize() {
// bracketSizes.
+ static_assert(kNumRegularSizeBrackets == kNumOfSizeBrackets - 2,
+ "There should be two non-regular brackets");
for (size_t i = 0; i < kNumOfSizeBrackets; i++) {
- if (i < kNumOfSizeBrackets - 2) {
- bracketSizes[i] = 16 * (i + 1);
+ if (i < kNumThreadLocalSizeBrackets) {
+ bracketSizes[i] = kThreadLocalBracketQuantumSize * (i + 1);
+ } else if (i < kNumRegularSizeBrackets) {
+ bracketSizes[i] = kBracketQuantumSize * (i - kNumThreadLocalSizeBrackets + 1) +
+ (kThreadLocalBracketQuantumSize * kNumThreadLocalSizeBrackets);
} else if (i == kNumOfSizeBrackets - 2) {
bracketSizes[i] = 1 * KB;
} else {
@@ -1662,16 +1644,13 @@
}
// numOfPages.
for (size_t i = 0; i < kNumOfSizeBrackets; i++) {
- if (i < 4) {
+ if (i < kNumThreadLocalSizeBrackets) {
numOfPages[i] = 1;
- } else if (i < 8) {
- numOfPages[i] = 1;
- } else if (i < 16) {
+ } else if (i < (kNumThreadLocalSizeBrackets + kNumRegularSizeBrackets) / 2) {
numOfPages[i] = 4;
- } else if (i < 32) {
+ } else if (i < kNumRegularSizeBrackets) {
numOfPages[i] = 8;
- } else if (i == 32) {
- DCHECK_EQ(i, kNumOfSizeBrackets - 2);
+ } else if (i == kNumOfSizeBrackets - 2) {
numOfPages[i] = 16;
} else {
DCHECK_EQ(i, kNumOfSizeBrackets - 1);
@@ -1701,8 +1680,8 @@
size_t tmp_header_size = (tmp_unaligned_header_size % bracket_size == 0) ?
tmp_unaligned_header_size :
tmp_unaligned_header_size + (bracket_size - tmp_unaligned_header_size % bracket_size);
- DCHECK_EQ(tmp_header_size % bracket_size, static_cast<size_t>(0));
- DCHECK_EQ(tmp_header_size % 8, static_cast<size_t>(0));
+ DCHECK_EQ(tmp_header_size % bracket_size, 0U);
+ DCHECK_EQ(tmp_header_size % sizeof(uint64_t), 0U);
if (tmp_slots_size + tmp_header_size <= run_size) {
// Found the right number of slots, that is, there was enough
// space for the header (including the bit maps.)
@@ -1711,8 +1690,8 @@
break;
}
}
- DCHECK_GT(num_of_slots, 0U);
- DCHECK_GT(header_size, 0U);
+ DCHECK_GT(num_of_slots, 0U) << i;
+ DCHECK_GT(header_size, 0U) << i;
// Add the padding for the alignment remainder.
header_size += run_size % bracket_size;
DCHECK_EQ(header_size + num_of_slots * bracket_size, run_size);
@@ -1723,7 +1702,7 @@
<< ", headerSizes[" << i << "]=" << headerSizes[i];
}
}
- // Fill the alloc bitmap so nobody can successfully allocate from it.
+ // Set up the dedicated full run so that nobody can successfully allocate from it.
if (kIsDebugBuild) {
dedicated_full_run_->magic_num_ = kMagicNum;
}
@@ -1735,6 +1714,9 @@
// The smallest bracket size must be at least as large as the sizeof(Slot).
DCHECK_LE(sizeof(Slot), bracketSizes[0]) << "sizeof(Slot) <= the smallest bracket size";
+ // Check the invariants between the max bracket sizes and the number of brackets.
+ DCHECK_EQ(kMaxThreadLocalBracketSize, bracketSizes[kNumThreadLocalSizeBrackets - 1]);
+ DCHECK_EQ(kMaxRegularBracketSize, bracketSizes[kNumRegularSizeBrackets - 1]);
}
void RosAlloc::BytesAllocatedCallback(void* start ATTRIBUTE_UNUSED, void* end ATTRIBUTE_UNUSED,
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index 3ce3d63..a472a8b 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -366,7 +366,7 @@
static size_t fixed_header_size() {
return sizeof(Run);
}
- Slot* FirstSlot() {
+ Slot* FirstSlot() const {
const uint8_t idx = size_bracket_idx_;
return reinterpret_cast<Slot*>(reinterpret_cast<uintptr_t>(this) + headerSizes[idx]);
}
@@ -473,7 +473,16 @@
DCHECK_LT(slot_idx, numOfSlots[idx]);
return reinterpret_cast<Slot*>(ptr);
}
- size_t SlotIndex(Slot* slot);
+ size_t SlotIndex(Slot* slot) const {
+ const uint8_t idx = size_bracket_idx_;
+ const size_t bracket_size = bracketSizes[idx];
+ const size_t offset_from_slot_base = reinterpret_cast<uint8_t*>(slot)
+ - reinterpret_cast<uint8_t*>(FirstSlot());
+ DCHECK_EQ(offset_from_slot_base % bracket_size, 0U);
+ size_t slot_idx = offset_from_slot_base / bracket_size;
+ DCHECK_LT(slot_idx, numOfSlots[idx]);
+ return slot_idx;
+ }
// TODO: DISALLOW_COPY_AND_ASSIGN(Run);
};
@@ -482,10 +491,8 @@
static constexpr uint8_t kMagicNum = 42;
// The magic number for free pages.
static constexpr uint8_t kMagicNumFree = 43;
- // The number of size brackets. Sync this with the length of Thread::rosalloc_runs_.
- static constexpr size_t kNumOfSizeBrackets = kNumRosAllocThreadLocalSizeBrackets;
- // The number of smaller size brackets that are the quantum size apart.
- static constexpr size_t kNumOfQuantumSizeBrackets = 32;
+ // The number of size brackets.
+ static constexpr size_t kNumOfSizeBrackets = 42;
// The sizes (the slot sizes, in bytes) of the size brackets.
static size_t bracketSizes[kNumOfSizeBrackets];
// The numbers of pages that are used for runs for each size bracket.
@@ -506,16 +513,23 @@
}
// Returns the index of the size bracket from the bracket size.
static size_t BracketSizeToIndex(size_t size) {
- DCHECK(16 <= size && ((size < 1 * KB && size % 16 == 0) || size == 1 * KB || size == 2 * KB));
+ DCHECK(8 <= size &&
+ ((size <= kMaxThreadLocalBracketSize && size % kThreadLocalBracketQuantumSize == 0) ||
+ (size <= kMaxRegularBracketSize && size % kBracketQuantumSize == 0) ||
+ size == 1 * KB || size == 2 * KB));
size_t idx;
if (UNLIKELY(size == 1 * KB)) {
idx = kNumOfSizeBrackets - 2;
} else if (UNLIKELY(size == 2 * KB)) {
idx = kNumOfSizeBrackets - 1;
+ } else if (LIKELY(size <= kMaxThreadLocalBracketSize)) {
+ DCHECK_EQ(size % kThreadLocalBracketQuantumSize, 0U);
+ idx = size / kThreadLocalBracketQuantumSize - 1;
} else {
- DCHECK(size < 1 * KB);
- DCHECK_EQ(size % 16, static_cast<size_t>(0));
- idx = size / 16 - 1;
+ DCHECK(size <= kMaxRegularBracketSize);
+ DCHECK_EQ((size - kMaxThreadLocalBracketSize) % kBracketQuantumSize, 0U);
+ idx = ((size - kMaxThreadLocalBracketSize) / kBracketQuantumSize - 1)
+ + kNumThreadLocalSizeBrackets;
}
DCHECK(bracketSizes[idx] == size);
return idx;
@@ -530,51 +544,64 @@
// Rounds up the size up the nearest bracket size.
static size_t RoundToBracketSize(size_t size) {
DCHECK(size <= kLargeSizeThreshold);
- if (LIKELY(size <= 512)) {
- return RoundUp(size, 16);
- } else if (512 < size && size <= 1 * KB) {
+ if (LIKELY(size <= kMaxThreadLocalBracketSize)) {
+ return RoundUp(size, kThreadLocalBracketQuantumSize);
+ } else if (size <= kMaxRegularBracketSize) {
+ return RoundUp(size, kBracketQuantumSize);
+ } else if (UNLIKELY(size <= 1 * KB)) {
return 1 * KB;
} else {
- DCHECK(1 * KB < size && size <= 2 * KB);
+ DCHECK_LE(size, 2 * KB);
return 2 * KB;
}
}
// Returns the size bracket index from the byte size with rounding.
static size_t SizeToIndex(size_t size) {
DCHECK(size <= kLargeSizeThreshold);
- if (LIKELY(size <= 512)) {
- return RoundUp(size, 16) / 16 - 1;
- } else if (512 < size && size <= 1 * KB) {
+ if (LIKELY(size <= kMaxThreadLocalBracketSize)) {
+ return RoundUp(size, kThreadLocalBracketQuantumSize) / kThreadLocalBracketQuantumSize - 1;
+ } else if (size <= kMaxRegularBracketSize) {
+ return (RoundUp(size, kBracketQuantumSize) - kMaxThreadLocalBracketSize) / kBracketQuantumSize
+ - 1 + kNumThreadLocalSizeBrackets;
+ } else if (size <= 1 * KB) {
return kNumOfSizeBrackets - 2;
} else {
- DCHECK(1 * KB < size && size <= 2 * KB);
+ DCHECK_LE(size, 2 * KB);
return kNumOfSizeBrackets - 1;
}
}
// A combination of SizeToIndex() and RoundToBracketSize().
static size_t SizeToIndexAndBracketSize(size_t size, size_t* bracket_size_out) {
DCHECK(size <= kLargeSizeThreshold);
- if (LIKELY(size <= 512)) {
- size_t bracket_size = RoundUp(size, 16);
- *bracket_size_out = bracket_size;
- size_t idx = bracket_size / 16 - 1;
- DCHECK_EQ(bracket_size, IndexToBracketSize(idx));
- return idx;
- } else if (512 < size && size <= 1 * KB) {
- size_t bracket_size = 1024;
- *bracket_size_out = bracket_size;
- size_t idx = kNumOfSizeBrackets - 2;
- DCHECK_EQ(bracket_size, IndexToBracketSize(idx));
- return idx;
+ size_t idx;
+ size_t bracket_size;
+ if (LIKELY(size <= kMaxThreadLocalBracketSize)) {
+ bracket_size = RoundUp(size, kThreadLocalBracketQuantumSize);
+ idx = bracket_size / kThreadLocalBracketQuantumSize - 1;
+ } else if (size <= kMaxRegularBracketSize) {
+ bracket_size = RoundUp(size, kBracketQuantumSize);
+ idx = ((bracket_size - kMaxThreadLocalBracketSize) / kBracketQuantumSize - 1)
+ + kNumThreadLocalSizeBrackets;
+ } else if (size <= 1 * KB) {
+ bracket_size = 1 * KB;
+ idx = kNumOfSizeBrackets - 2;
} else {
- DCHECK(1 * KB < size && size <= 2 * KB);
- size_t bracket_size = 2048;
- *bracket_size_out = bracket_size;
- size_t idx = kNumOfSizeBrackets - 1;
- DCHECK_EQ(bracket_size, IndexToBracketSize(idx));
- return idx;
+ DCHECK(size <= 2 * KB);
+ bracket_size = 2 * KB;
+ idx = kNumOfSizeBrackets - 1;
}
+ DCHECK_EQ(idx, SizeToIndex(size)) << idx;
+ DCHECK_EQ(bracket_size, IndexToBracketSize(idx)) << idx;
+ DCHECK_EQ(bracket_size, bracketSizes[idx]) << idx;
+ DCHECK_LE(size, bracket_size) << idx;
+ DCHECK(size > kMaxRegularBracketSize ||
+ (size <= kMaxThreadLocalBracketSize &&
+ bracket_size - size < kThreadLocalBracketQuantumSize) ||
+ (size <= kMaxRegularBracketSize && bracket_size - size < kBracketQuantumSize)) << idx;
+ *bracket_size_out = bracket_size;
+ return idx;
}
+
// Returns the page map index from an address. Requires that the
// address is page size aligned.
size_t ToPageMapIndex(const void* addr) const {
@@ -630,18 +657,37 @@
// The default value for page_release_size_threshold_.
static constexpr size_t kDefaultPageReleaseSizeThreshold = 4 * MB;
- // We use thread-local runs for the size Brackets whose indexes
+ // We use thread-local runs for the size brackets whose indexes
// are less than this index. We use shared (current) runs for the rest.
- static const size_t kNumThreadLocalSizeBrackets = 8;
+ // Sync this with the length of Thread::rosalloc_runs_.
+ static const size_t kNumThreadLocalSizeBrackets = 16;
+ static_assert(kNumThreadLocalSizeBrackets == kNumRosAllocThreadLocalSizeBracketsInThread,
+ "Mismatch between kNumThreadLocalSizeBrackets and "
+ "kNumRosAllocThreadLocalSizeBracketsInThread");
// The size of the largest bracket we use thread-local runs for.
// This should be equal to bracketSizes[kNumThreadLocalSizeBrackets - 1].
static const size_t kMaxThreadLocalBracketSize = 128;
- // The bracket size increment for the brackets of size <= 512 bytes.
+ // We use regular (8 or 16-bytes increment) runs for the size brackets whose indexes are less than
+ // this index.
+ static const size_t kNumRegularSizeBrackets = 40;
+
+ // The size of the largest regular (8 or 16-byte increment) bracket. Non-regular brackets are the
+ // 1 KB and the 2 KB brackets. This should be equal to bracketSizes[kNumRegularSizeBrackets - 1].
+ static const size_t kMaxRegularBracketSize = 512;
+
+ // The bracket size increment for the thread-local brackets (<= kMaxThreadLocalBracketSize bytes).
+ static constexpr size_t kThreadLocalBracketQuantumSize = 8;
+
+ // Equal to Log2(kThreadLocalBracketQuantumSize).
+ static constexpr size_t kThreadLocalBracketQuantumSizeShift = 3;
+
+ // The bracket size increment for the non-thread-local, regular brackets (of size <=
+ // kMaxRegularBracketSize bytes and > kMaxThreadLocalBracketSize bytes).
static constexpr size_t kBracketQuantumSize = 16;
- // Equal to Log2(kQuantumBracketSizeIncrement).
+ // Equal to Log2(kBracketQuantumSize).
static constexpr size_t kBracketQuantumSizeShift = 4;
private:
diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h
index c8e913c..ae41226 100644
--- a/runtime/gc/collector_type.h
+++ b/runtime/gc/collector_type.h
@@ -42,6 +42,8 @@
kCollectorTypeCC,
// Instrumentation critical section fake collector.
kCollectorTypeInstrumentation,
+ // Fake collector for adding or removing application image spaces.
+ kCollectorTypeAddRemoveAppImageSpace,
// A homogeneous space compaction collector used in background transition
// when both foreground and background collector are CMS.
kCollectorTypeHomogeneousSpaceCompact,
diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc
index 84243df..679432b 100644
--- a/runtime/gc/gc_cause.cc
+++ b/runtime/gc/gc_cause.cc
@@ -34,6 +34,7 @@
case kGcCauseHomogeneousSpaceCompact: return "HomogeneousSpaceCompact";
case kGcCauseTrim: return "HeapTrim";
case kGcCauseInstrumentation: return "Instrumentation";
+ case kGcCauseAddRemoveAppImageSpace: return "AddRemoveAppImageSpace";
default:
LOG(FATAL) << "Unreachable";
UNREACHABLE();
diff --git a/runtime/gc/gc_cause.h b/runtime/gc/gc_cause.h
index 34c7766..c6b505c 100644
--- a/runtime/gc/gc_cause.h
+++ b/runtime/gc/gc_cause.h
@@ -41,6 +41,8 @@
kGcCauseTrim,
// Not a real GC cause, used to implement exclusion between GC and instrumentation.
kGcCauseInstrumentation,
+ // Not a real GC cause, used to add or remove app image spaces.
+ kGcCauseAddRemoveAppImageSpace,
// GC triggered for background transition when both foreground and background collector are CMS.
kGcCauseHomogeneousSpaceCompact,
};
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 136b793..3c9312f 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -271,7 +271,7 @@
// The loaded spaces. Secondary images may fail to load, in which case we need to remove
// already added spaces.
std::vector<space::Space*> added_image_spaces;
-
+ uint8_t* const original_requested_alloc_space_begin = requested_alloc_space_begin;
for (size_t index = 0; index < image_file_names.size(); ++index) {
std::string& image_name = image_file_names[index];
ATRACE_BEGIN("ImageSpace::Create");
@@ -320,7 +320,7 @@
delete loaded_space;
}
boot_image_spaces_.clear();
- requested_alloc_space_begin = nullptr;
+ requested_alloc_space_begin = original_requested_alloc_space_begin;
break;
}
}
@@ -2622,6 +2622,10 @@
}
if (collector != mark_compact_collector_ && collector != concurrent_copying_collector_) {
temp_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
+ if (kIsDebugBuild) {
+ // Try to read each page of the memory map in case mprotect didn't work properly b/19894268.
+ temp_space_->GetMemMap()->TryReadable();
+ }
CHECK(temp_space_->IsEmpty());
}
gc_type = collector::kGcTypeFull; // TODO: Not hard code this in.
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index d185e63..0c06c38 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -305,12 +305,6 @@
std::string output_image_filename_arg("--output-image-file=");
output_image_filename_arg += dest_filename;
- std::string input_oat_location_arg("--input-oat-location=");
- input_oat_location_arg += ImageHeader::GetOatLocationFromImageLocation(image_location);
-
- std::string output_oat_filename_arg("--output-oat-file=");
- output_oat_filename_arg += ImageHeader::GetOatLocationFromImageLocation(dest_filename);
-
std::string instruction_set_arg("--instruction-set=");
instruction_set_arg += GetInstructionSetString(isa);
@@ -324,9 +318,6 @@
argv.push_back(input_image_location_arg);
argv.push_back(output_image_filename_arg);
- argv.push_back(input_oat_location_arg);
- argv.push_back(output_oat_filename_arg);
-
argv.push_back(instruction_set_arg);
argv.push_back(base_offset_arg);
@@ -925,7 +916,7 @@
if (fixup_heap_objects_) {
method->UpdateObjectsForImageRelocation(ForwardObjectAdapter(this));
}
- method->UpdateEntrypoints(ForwardCodeAdapter(this));
+ method->UpdateEntrypoints<kWithoutReadBarrier>(ForwardCodeAdapter(this));
}
private:
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 3eff7fc..4fd3c78 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -27,6 +27,7 @@
#include "unstarted_runtime.h"
#include "mterp/mterp.h"
#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
namespace art {
namespace interpreter {
@@ -293,9 +294,10 @@
method, 0);
}
- if (UNLIKELY(Runtime::Current()->GetJit() != nullptr &&
- Runtime::Current()->GetJit()->JitAtFirstUse() &&
- method->HasAnyCompiledCode())) {
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (UNLIKELY(jit != nullptr &&
+ jit->JitAtFirstUse() &&
+ jit->GetCodeCache()->ContainsMethod(method))) {
JValue result;
// Pop the shadow frame before calling into compiled code.
@@ -311,7 +313,7 @@
shadow_frame.GetMethod()->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self);
bool transaction_active = Runtime::Current()->IsActiveTransaction();
- if (LIKELY(shadow_frame.GetMethod()->IsPreverified())) {
+ if (LIKELY(shadow_frame.GetMethod()->SkipAccessChecks())) {
// Enter the "without access check" interpreter.
if (kInterpreterImplKind == kMterpImplKind) {
if (transaction_active) {
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 6c9cc70..cbaa817 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -20,6 +20,7 @@
#include "debugger.h"
#include "entrypoints/runtime_asm_entrypoints.h"
+#include "jit/jit.h"
#include "mirror/array-inl.h"
#include "stack.h"
#include "unstarted_runtime.h"
@@ -501,23 +502,6 @@
uint32_t (&arg)[kVarArgMax],
uint32_t vregC) ALWAYS_INLINE;
-SHARED_REQUIRES(Locks::mutator_lock_)
-static inline bool NeedsInterpreter(Thread* self, ShadowFrame* new_shadow_frame) ALWAYS_INLINE;
-
-static inline bool NeedsInterpreter(Thread* self, ShadowFrame* new_shadow_frame) {
- ArtMethod* target = new_shadow_frame->GetMethod();
- if (UNLIKELY(target->IsNative() || target->IsProxyMethod())) {
- return false;
- }
- Runtime* runtime = Runtime::Current();
- ClassLinker* class_linker = runtime->GetClassLinker();
- return runtime->GetInstrumentation()->IsForcedInterpretOnly() ||
- // Doing this check avoids doing compiled/interpreter transitions.
- class_linker->IsQuickToInterpreterBridge(target->GetEntryPointFromQuickCompiledCode()) ||
- // Force the use of interpreter when it is required by the debugger.
- Dbg::IsForcedInterpreterNeededForCalling(self, target);
-}
-
void ArtInterpreterToCompiledCodeBridge(Thread* self,
const DexFile::CodeItem* code_item,
ShadowFrame* shadow_frame,
@@ -637,17 +621,25 @@
self, new_shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction);
self->EndAssertNoThreadSuspension(old_cause);
+ // ArtMethod here is needed to check type information of the call site against the callee.
+ // Type information is retrieved from a DexFile/DexCache for that respective declared method.
+ //
+ // As a special case for proxy methods, which are not dex-backed,
+ // we have to retrieve type information from the proxy's method
+ // interface method instead (which is dex backed since proxies are never interfaces).
+ ArtMethod* method = new_shadow_frame->GetMethod()->GetInterfaceMethodIfProxy(sizeof(void*));
+
// We need to do runtime check on reference assignment. We need to load the shorty
// to get the exact type of each reference argument.
- const DexFile::TypeList* params = new_shadow_frame->GetMethod()->GetParameterTypeList();
+ const DexFile::TypeList* params = method->GetParameterTypeList();
uint32_t shorty_len = 0;
- const char* shorty = new_shadow_frame->GetMethod()->GetShorty(&shorty_len);
+ const char* shorty = method->GetShorty(&shorty_len);
// Handle receiver apart since it's not part of the shorty.
size_t dest_reg = first_dest_reg;
size_t arg_offset = 0;
- if (!new_shadow_frame->GetMethod()->IsStatic()) {
+ if (!method->IsStatic()) {
size_t receiver_reg = is_range ? vregC : arg[0];
new_shadow_frame->SetVRegReference(dest_reg, shadow_frame.GetVRegReference(receiver_reg));
++dest_reg;
@@ -667,7 +659,7 @@
if (do_assignability_check && o != nullptr) {
size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
Class* arg_type =
- new_shadow_frame->GetMethod()->GetClassFromTypeIndex(
+ method->GetClassFromTypeIndex(
params->GetTypeItem(shorty_pos).type_idx_, true /* resolve */, pointer_size);
if (arg_type == nullptr) {
CHECK(self->IsExceptionPending());
@@ -728,7 +720,10 @@
// Do the call now.
if (LIKELY(Runtime::Current()->IsStarted())) {
- if (NeedsInterpreter(self, new_shadow_frame)) {
+ ArtMethod* target = new_shadow_frame->GetMethod();
+ if (ClassLinker::ShouldUseInterpreterEntrypoint(
+ target,
+ target->GetEntryPointFromQuickCompiledCode())) {
ArtInterpreterToInterpreterBridge(self, code_item, new_shadow_frame, result);
} else {
ArtInterpreterToCompiledCodeBridge(self, code_item, new_shadow_frame, result);
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index 940d344..ca8598e 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -21,6 +21,7 @@
#include "base/stl_util.h" // MakeUnique
#include "experimental_flags.h"
#include "interpreter_common.h"
+#include "jit/jit.h"
#include "safe_math.h"
#include <memory> // std::unique_ptr
@@ -63,10 +64,15 @@
currentHandlersTable = handlersTable[ \
Runtime::Current()->GetInstrumentation()->GetInterpreterHandlerTable()]
-#define BRANCH_INSTRUMENTATION(offset) \
- do { \
+#define BRANCH_INSTRUMENTATION(offset) \
+ do { \
+ ArtMethod* method = shadow_frame.GetMethod(); \
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); \
- instrumentation->Branch(self, shadow_frame.GetMethod(), dex_pc, offset); \
+ instrumentation->Branch(self, method, dex_pc, offset); \
+ JValue result; \
+ if (jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, &result)) { \
+ return result; \
+ } \
} while (false)
#define UNREACHABLE_CODE_CHECK() \
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index f606978..25dbab2 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -17,6 +17,7 @@
#include "base/stl_util.h" // MakeUnique
#include "experimental_flags.h"
#include "interpreter_common.h"
+#include "jit/jit.h"
#include "safe_math.h"
#include <memory> // std::unique_ptr
@@ -69,9 +70,14 @@
} \
} while (false)
-#define BRANCH_INSTRUMENTATION(offset) \
- do { \
- instrumentation->Branch(self, shadow_frame.GetMethod(), dex_pc, offset); \
+#define BRANCH_INSTRUMENTATION(offset) \
+ do { \
+ ArtMethod* method = shadow_frame.GetMethod(); \
+ instrumentation->Branch(self, method, dex_pc, offset); \
+ JValue result; \
+ if (jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, &result)) { \
+ return result; \
+ } \
} while (false)
static bool IsExperimentalInstructionEnabled(const Instruction *inst) {
diff --git a/runtime/interpreter/mterp/arm/binopWide.S b/runtime/interpreter/mterp/arm/binopWide.S
index 57d43c6..1d511ec 100644
--- a/runtime/interpreter/mterp/arm/binopWide.S
+++ b/runtime/interpreter/mterp/arm/binopWide.S
@@ -16,10 +16,10 @@
*/
/* binop vAA, vBB, vCC */
FETCH r0, 1 @ r0<- CCBB
- mov r9, rINST, lsr #8 @ r9<- AA
+ mov rINST, rINST, lsr #8 @ rINST<- AA
and r2, r0, #255 @ r2<- BB
mov r3, r0, lsr #8 @ r3<- CC
- add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA]
add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
@@ -28,8 +28,8 @@
orrs ip, r2, r3 @ second arg (r2-r3) is zero?
beq common_errDivideByZero
.endif
+ CLEAR_SHADOW_PAIR rINST, lr, ip @ Zero out the shadow regs
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
-
$preinstr @ optional op; may set condition codes
$instr @ result<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
diff --git a/runtime/interpreter/mterp/arm/binopWide2addr.S b/runtime/interpreter/mterp/arm/binopWide2addr.S
index 4e855f2..81db48b 100644
--- a/runtime/interpreter/mterp/arm/binopWide2addr.S
+++ b/runtime/interpreter/mterp/arm/binopWide2addr.S
@@ -15,17 +15,17 @@
*/
/* binop/2addr vA, vB */
mov r1, rINST, lsr #12 @ r1<- B
- ubfx r9, rINST, #8, #4 @ r9<- A
+ ubfx rINST, rINST, #8, #4 @ rINST<- A
add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
- add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[A]
ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
.if $chkzero
orrs ip, r2, r3 @ second arg (r2-r3) is zero?
beq common_errDivideByZero
.endif
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
-
$preinstr @ optional op; may set condition codes
$instr @ result<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
diff --git a/runtime/interpreter/mterp/arm/fbinopWide.S b/runtime/interpreter/mterp/arm/fbinopWide.S
index 1bed817..ca13bfb 100644
--- a/runtime/interpreter/mterp/arm/fbinopWide.S
+++ b/runtime/interpreter/mterp/arm/fbinopWide.S
@@ -14,9 +14,9 @@
VREG_INDEX_TO_ADDR r2, r2 @ r2<- &vBB
fldd d1, [r3] @ d1<- vCC
fldd d0, [r2] @ d0<- vBB
-
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
$instr @ s2<- op
+ CLEAR_SHADOW_PAIR r9, ip, lr @ Zero shadow regs
GET_INST_OPCODE ip @ extract opcode from rINST
VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vAA
fstd d2, [r9] @ vAA<- d2
diff --git a/runtime/interpreter/mterp/arm/fbinopWide2addr.S b/runtime/interpreter/mterp/arm/fbinopWide2addr.S
index 9f56986..4e7401d 100644
--- a/runtime/interpreter/mterp/arm/fbinopWide2addr.S
+++ b/runtime/interpreter/mterp/arm/fbinopWide2addr.S
@@ -12,10 +12,10 @@
VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB
and r9, r9, #15 @ r9<- A
fldd d1, [r3] @ d1<- vB
+ CLEAR_SHADOW_PAIR r9, ip, r0 @ Zero out shadow regs
VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
fldd d0, [r9] @ d0<- vA
-
$instr @ d2<- op
GET_INST_OPCODE ip @ extract opcode from rINST
fstd d2, [r9] @ vAA<- d2
diff --git a/runtime/interpreter/mterp/arm/funopWider.S b/runtime/interpreter/mterp/arm/funopWider.S
index 087a1f2..450ba3a 100644
--- a/runtime/interpreter/mterp/arm/funopWider.S
+++ b/runtime/interpreter/mterp/arm/funopWider.S
@@ -12,6 +12,7 @@
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
and r9, r9, #15 @ r9<- A
$instr @ d0<- op
+ CLEAR_SHADOW_PAIR r9, ip, lr @ Zero shadow regs
GET_INST_OPCODE ip @ extract opcode from rINST
VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA
fstd d0, [r9] @ vA<- d0
diff --git a/runtime/interpreter/mterp/arm/header.S b/runtime/interpreter/mterp/arm/header.S
index 14319d9..b2370bf 100644
--- a/runtime/interpreter/mterp/arm/header.S
+++ b/runtime/interpreter/mterp/arm/header.S
@@ -263,6 +263,19 @@
str \reg, [rFP, \vreg, lsl #2]
str \reg, [rREFS, \vreg, lsl #2]
.endm
+.macro SET_VREG_SHADOW reg, vreg
+ str \reg, [rREFS, \vreg, lsl #2]
+.endm
+
+/*
+ * Clear the corresponding shadow regs for a vreg pair
+ */
+.macro CLEAR_SHADOW_PAIR vreg, tmp1, tmp2
+ mov \tmp1, #0
+ add \tmp2, \vreg, #1
+ SET_VREG_SHADOW \tmp1, \vreg
+ SET_VREG_SHADOW \tmp1, \tmp2
+.endm
/*
* Convert a virtual register index into an address.
diff --git a/runtime/interpreter/mterp/arm/op_aget_wide.S b/runtime/interpreter/mterp/arm/op_aget_wide.S
index caaec71..e1430b4 100644
--- a/runtime/interpreter/mterp/arm/op_aget_wide.S
+++ b/runtime/interpreter/mterp/arm/op_aget_wide.S
@@ -10,6 +10,7 @@
mov r3, r0, lsr #8 @ r3<- CC
GET_VREG r0, r2 @ r0<- vBB (array object)
GET_VREG r1, r3 @ r1<- vCC (requested index)
+ CLEAR_SHADOW_PAIR r9, r2, r3 @ Zero out the shadow regs
cmp r0, #0 @ null array object?
beq common_errNullObject @ yes, bail
ldr r3, [r0, #MIRROR_ARRAY_LENGTH_OFFSET] @ r3<- arrayObj->length
diff --git a/runtime/interpreter/mterp/arm/op_const_wide.S b/runtime/interpreter/mterp/arm/op_const_wide.S
index 2cdc426..12394b6 100644
--- a/runtime/interpreter/mterp/arm/op_const_wide.S
+++ b/runtime/interpreter/mterp/arm/op_const_wide.S
@@ -6,6 +6,7 @@
FETCH r3, 4 @ r3<- HHHH (high)
mov r9, rINST, lsr #8 @ r9<- AA
orr r1, r2, r3, lsl #16 @ r1<- HHHHhhhh (high word)
+ CLEAR_SHADOW_PAIR r9, r2, r3 @ Zero out the shadow regs
FETCH_ADVANCE_INST 5 @ advance rPC, load rINST
add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
GET_INST_OPCODE ip @ extract opcode from rINST
diff --git a/runtime/interpreter/mterp/arm/op_const_wide_16.S b/runtime/interpreter/mterp/arm/op_const_wide_16.S
index 56bfc17..3811d86 100644
--- a/runtime/interpreter/mterp/arm/op_const_wide_16.S
+++ b/runtime/interpreter/mterp/arm/op_const_wide_16.S
@@ -3,6 +3,7 @@
mov r3, rINST, lsr #8 @ r3<- AA
mov r1, r0, asr #31 @ r1<- ssssssss
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
+ CLEAR_SHADOW_PAIR r3, r2, lr @ Zero out the shadow regs
add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r3, {r0-r1} @ vAA<- r0/r1
diff --git a/runtime/interpreter/mterp/arm/op_const_wide_32.S b/runtime/interpreter/mterp/arm/op_const_wide_32.S
index 36d4628..0b6f1cc 100644
--- a/runtime/interpreter/mterp/arm/op_const_wide_32.S
+++ b/runtime/interpreter/mterp/arm/op_const_wide_32.S
@@ -4,6 +4,7 @@
FETCH_S r2, 2 @ r2<- ssssBBBB (high)
FETCH_ADVANCE_INST 3 @ advance rPC, load rINST
orr r0, r0, r2, lsl #16 @ r0<- BBBBbbbb
+ CLEAR_SHADOW_PAIR r3, r2, lr @ Zero out the shadow regs
add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
mov r1, r0, asr #31 @ r1<- ssssssss
GET_INST_OPCODE ip @ extract opcode from rINST
diff --git a/runtime/interpreter/mterp/arm/op_const_wide_high16.S b/runtime/interpreter/mterp/arm/op_const_wide_high16.S
index bee592d..b9796eb 100644
--- a/runtime/interpreter/mterp/arm/op_const_wide_high16.S
+++ b/runtime/interpreter/mterp/arm/op_const_wide_high16.S
@@ -4,6 +4,7 @@
mov r0, #0 @ r0<- 00000000
mov r1, r1, lsl #16 @ r1<- BBBB0000
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
+ CLEAR_SHADOW_PAIR r3, r0, r2 @ Zero shadow regs
add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r3, {r0-r1} @ vAA<- r0/r1
diff --git a/runtime/interpreter/mterp/arm/op_iget_wide.S b/runtime/interpreter/mterp/arm/op_iget_wide.S
index f8d2f41..859ffac 100644
--- a/runtime/interpreter/mterp/arm/op_iget_wide.S
+++ b/runtime/interpreter/mterp/arm/op_iget_wide.S
@@ -15,8 +15,9 @@
PREFETCH_INST 2
cmp r3, #0
bne MterpException @ bail out
- add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
- stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ CLEAR_SHADOW_PAIR r2, ip, lr @ Zero out the shadow regs
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
ADVANCE 2
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
diff --git a/runtime/interpreter/mterp/arm/op_iget_wide_quick.S b/runtime/interpreter/mterp/arm/op_iget_wide_quick.S
index 4d6976e..07f854a 100644
--- a/runtime/interpreter/mterp/arm/op_iget_wide_quick.S
+++ b/runtime/interpreter/mterp/arm/op_iget_wide_quick.S
@@ -8,6 +8,7 @@
ldrd r0, [r3, ip] @ r0<- obj.field (64 bits, aligned)
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ CLEAR_SHADOW_PAIR r2, ip, lr @ Zero out the shadow regs
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r3, {r0-r1} @ fp[A]<- r0/r1
GOTO_OPCODE ip @ jump to next instruction
diff --git a/runtime/interpreter/mterp/arm/op_move_result_wide.S b/runtime/interpreter/mterp/arm/op_move_result_wide.S
index c64103c..1845ccf 100644
--- a/runtime/interpreter/mterp/arm/op_move_result_wide.S
+++ b/runtime/interpreter/mterp/arm/op_move_result_wide.S
@@ -1,8 +1,9 @@
/* move-result-wide vAA */
- mov r2, rINST, lsr #8 @ r2<- AA
+ mov rINST, rINST, lsr #8 @ rINST<- AA
ldr r3, [rFP, #OFF_FP_RESULT_REGISTER]
- add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ add r2, rFP, rINST, lsl #2 @ r2<- &fp[AA]
ldmia r3, {r0-r1} @ r0/r1<- retval.j
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero out the shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
stmia r2, {r0-r1} @ fp[AA]<- r0/r1
GET_INST_OPCODE ip @ extract opcode from rINST
diff --git a/runtime/interpreter/mterp/arm/op_move_wide.S b/runtime/interpreter/mterp/arm/op_move_wide.S
index 1345b95..f5d156d 100644
--- a/runtime/interpreter/mterp/arm/op_move_wide.S
+++ b/runtime/interpreter/mterp/arm/op_move_wide.S
@@ -1,10 +1,11 @@
/* move-wide vA, vB */
/* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
mov r3, rINST, lsr #12 @ r3<- B
- ubfx r2, rINST, #8, #4 @ r2<- A
+ ubfx rINST, rINST, #8, #4 @ rINST<- A
add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
- add r2, rFP, r2, lsl #2 @ r2<- &fp[A]
+ add r2, rFP, rINST, lsl #2 @ r2<- &fp[A]
ldmia r3, {r0-r1} @ r0/r1<- fp[B]
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero out the shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r2, {r0-r1} @ fp[A]<- r0/r1
diff --git a/runtime/interpreter/mterp/arm/op_move_wide_16.S b/runtime/interpreter/mterp/arm/op_move_wide_16.S
index 133a4c3..8a55c4b 100644
--- a/runtime/interpreter/mterp/arm/op_move_wide_16.S
+++ b/runtime/interpreter/mterp/arm/op_move_wide_16.S
@@ -3,9 +3,10 @@
FETCH r3, 2 @ r3<- BBBB
FETCH r2, 1 @ r2<- AAAA
add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB]
- add r2, rFP, r2, lsl #2 @ r2<- &fp[AAAA]
+ add lr, rFP, r2, lsl #2 @ r2<- &fp[AAAA]
ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB]
FETCH_ADVANCE_INST 3 @ advance rPC, load rINST
- stmia r2, {r0-r1} @ fp[AAAA]<- r0/r1
+ CLEAR_SHADOW_PAIR r2, r3, ip @ Zero out the shadow regs
+ stmia lr, {r0-r1} @ fp[AAAA]<- r0/r1
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
diff --git a/runtime/interpreter/mterp/arm/op_move_wide_from16.S b/runtime/interpreter/mterp/arm/op_move_wide_from16.S
index f2ae785..b65259d 100644
--- a/runtime/interpreter/mterp/arm/op_move_wide_from16.S
+++ b/runtime/interpreter/mterp/arm/op_move_wide_from16.S
@@ -1,10 +1,11 @@
/* move-wide/from16 vAA, vBBBB */
/* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
FETCH r3, 1 @ r3<- BBBB
- mov r2, rINST, lsr #8 @ r2<- AA
+ mov rINST, rINST, lsr #8 @ rINST<- AA
add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB]
- add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ add r2, rFP, rINST, lsl #2 @ r2<- &fp[AA]
ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB]
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero out the shadow regs
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r2, {r0-r1} @ fp[AA]<- r0/r1
diff --git a/runtime/interpreter/mterp/arm/op_sget_wide.S b/runtime/interpreter/mterp/arm/op_sget_wide.S
index 97db05f..3a50908 100644
--- a/runtime/interpreter/mterp/arm/op_sget_wide.S
+++ b/runtime/interpreter/mterp/arm/op_sget_wide.S
@@ -12,10 +12,11 @@
bl artGet64StaticFromCode
ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET]
mov r9, rINST, lsr #8 @ r9<- AA
- add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add lr, rFP, r9, lsl #2 @ r9<- &fp[AA]
cmp r3, #0 @ Fail to resolve?
bne MterpException @ bail out
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
- stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ CLEAR_SHADOW_PAIR r9, r2, ip @ Zero out the shadow regs
+ stmia lr, {r0-r1} @ vAA/vAA+1<- r0/r1
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
diff --git a/runtime/interpreter/mterp/arm/unopWide.S b/runtime/interpreter/mterp/arm/unopWide.S
index 7b8739c..a074234 100644
--- a/runtime/interpreter/mterp/arm/unopWide.S
+++ b/runtime/interpreter/mterp/arm/unopWide.S
@@ -8,10 +8,11 @@
*/
/* unop vA, vB */
mov r3, rINST, lsr #12 @ r3<- B
- ubfx r9, rINST, #8, #4 @ r9<- A
+ ubfx rINST, rINST, #8, #4 @ rINST<- A
add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
- add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[A]
ldmia r3, {r0-r1} @ r0/r1<- vAA
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
$preinstr @ optional op; may set condition codes
$instr @ r0/r1<- op, r2-r3 changed
diff --git a/runtime/interpreter/mterp/arm/unopWider.S b/runtime/interpreter/mterp/arm/unopWider.S
index 657a395..23b6b9d 100644
--- a/runtime/interpreter/mterp/arm/unopWider.S
+++ b/runtime/interpreter/mterp/arm/unopWider.S
@@ -8,10 +8,11 @@
*/
/* unop vA, vB */
mov r3, rINST, lsr #12 @ r3<- B
- ubfx r9, rINST, #8, #4 @ r9<- A
+ ubfx rINST, rINST, #8, #4 @ rINST<- A
GET_VREG r0, r3 @ r0<- vB
- add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[A]
$preinstr @ optional op; may set condition codes
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
$instr @ r0<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
diff --git a/runtime/interpreter/mterp/config_x86 b/runtime/interpreter/mterp/config_x86
index 5fab379..f1501e1 100644
--- a/runtime/interpreter/mterp/config_x86
+++ b/runtime/interpreter/mterp/config_x86
@@ -19,6 +19,10 @@
handler-style computed-goto
handler-size 128
+function-type-format FUNCTION_TYPE(%s)
+function-size-format SIZE(%s,%s)
+global-name-format SYMBOL(%s)
+
# source for alternate entry stub
asm-alt-stub x86/alt_stub.S
diff --git a/runtime/interpreter/mterp/gen_mterp.py b/runtime/interpreter/mterp/gen_mterp.py
index f56d8bd..5839b5f 100755
--- a/runtime/interpreter/mterp/gen_mterp.py
+++ b/runtime/interpreter/mterp/gen_mterp.py
@@ -41,6 +41,9 @@
alt_label_prefix = ".L_ALT" # use ".L" to hide labels from gdb
style = None # interpreter style
generate_alt_table = False
+function_type_format = ".type %s, %%function"
+function_size_format = ".size %s, .-%s"
+global_name_format = "%s"
# Exception class.
class DataParseError(SyntaxError):
@@ -147,7 +150,24 @@
raise DataParseError("import requires one argument")
default_alt_stub = tokens[1]
generate_alt_table = True
-
+#
+# Change the default function type format
+#
+def setFunctionTypeFormat(tokens):
+ global function_type_format
+ function_type_format = tokens[1]
+#
+# Change the default function size format
+#
+def setFunctionSizeFormat(tokens):
+ global function_size_format
+ function_size_format = tokens[1]
+#
+# Change the global name format
+#
+def setGlobalNameFormat(tokens):
+ global global_name_format
+ global_name_format = tokens[1]
#
# Parse arch config file --
# Start of opcode list.
@@ -259,12 +279,12 @@
sister_list = []
assert len(opcodes) == kNumPackedOpcodes
need_dummy_start = False
- start_label = "artMterpAsmInstructionStart"
- end_label = "artMterpAsmInstructionEnd"
+ start_label = global_name_format % "artMterpAsmInstructionStart"
+ end_label = global_name_format % "artMterpAsmInstructionEnd"
# point MterpAsmInstructionStart at the first handler or stub
asm_fp.write("\n .global %s\n" % start_label)
- asm_fp.write(" .type %s, %%function\n" % start_label)
+ asm_fp.write(" " + (function_type_format % start_label) + "\n");
asm_fp.write("%s = " % start_label + label_prefix + "_op_nop\n")
asm_fp.write(" .text\n\n")
@@ -290,21 +310,23 @@
asm_fp.write(label_prefix + "_op_nop: /* dummy */\n");
emitAlign()
- asm_fp.write(" .size %s, .-%s\n" % (start_label, start_label))
+ asm_fp.write(" " + (function_size_format % (start_label, start_label)) + "\n")
asm_fp.write(" .global %s\n" % end_label)
asm_fp.write("%s:\n" % end_label)
if style == "computed-goto":
+ start_sister_label = global_name_format % "artMterpAsmSisterStart"
+ end_sister_label = global_name_format % "artMterpAsmSisterEnd"
emitSectionComment("Sister implementations", asm_fp)
- asm_fp.write(" .global artMterpAsmSisterStart\n")
- asm_fp.write(" .type artMterpAsmSisterStart, %function\n")
+ asm_fp.write(" .global %s\n" % start_sister_label)
+ asm_fp.write(" " + (function_type_format % start_sister_label) + "\n");
asm_fp.write(" .text\n")
asm_fp.write(" .balign 4\n")
- asm_fp.write("artMterpAsmSisterStart:\n")
+ asm_fp.write("%s:\n" % start_sister_label)
asm_fp.writelines(sister_list)
- asm_fp.write("\n .size artMterpAsmSisterStart, .-artMterpAsmSisterStart\n")
- asm_fp.write(" .global artMterpAsmSisterEnd\n")
- asm_fp.write("artMterpAsmSisterEnd:\n\n")
+ asm_fp.write("\n " + (function_size_format % (start_sister_label, start_sister_label)) + "\n")
+ asm_fp.write(" .global %s\n" % end_sister_label)
+ asm_fp.write("%s:\n\n" % end_sister_label)
#
# Load an alternate entry stub
@@ -324,12 +346,12 @@
#
def loadAndEmitAltOpcodes():
assert len(opcodes) == kNumPackedOpcodes
- start_label = "artMterpAsmAltInstructionStart"
- end_label = "artMterpAsmAltInstructionEnd"
+ start_label = global_name_format % "artMterpAsmAltInstructionStart"
+ end_label = global_name_format % "artMterpAsmAltInstructionEnd"
# point MterpAsmInstructionStart at the first handler or stub
asm_fp.write("\n .global %s\n" % start_label)
- asm_fp.write(" .type %s, %%function\n" % start_label)
+ asm_fp.write(" " + (function_type_format % start_label) + "\n");
asm_fp.write(" .text\n\n")
asm_fp.write("%s = " % start_label + label_prefix + "_ALT_op_nop\n")
@@ -342,7 +364,7 @@
loadAndEmitAltStub(source, i)
emitAlign()
- asm_fp.write(" .size %s, .-%s\n" % (start_label, start_label))
+ asm_fp.write(" " + (function_size_format % (start_label, start_label)) + "\n")
asm_fp.write(" .global %s\n" % end_label)
asm_fp.write("%s:\n" % end_label)
@@ -579,6 +601,12 @@
splitops = True
elif tokens[0] == "fallback-stub":
setFallbackStub(tokens)
+ elif tokens[0] == "function-type-format":
+ setFunctionTypeFormat(tokens)
+ elif tokens[0] == "function-size-format":
+ setFunctionSizeFormat(tokens)
+ elif tokens[0] == "global-name-format":
+ setGlobalNameFormat(tokens)
else:
raise DataParseError, "unrecognized command '%s'" % tokens[0]
if style == None:
diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S
index 78c784b..ee19559 100644
--- a/runtime/interpreter/mterp/out/mterp_arm.S
+++ b/runtime/interpreter/mterp/out/mterp_arm.S
@@ -270,6 +270,19 @@
str \reg, [rFP, \vreg, lsl #2]
str \reg, [rREFS, \vreg, lsl #2]
.endm
+.macro SET_VREG_SHADOW reg, vreg
+ str \reg, [rREFS, \vreg, lsl #2]
+.endm
+
+/*
+ * Clear the corresponding shadow regs for a vreg pair
+ */
+.macro CLEAR_SHADOW_PAIR vreg, tmp1, tmp2
+ mov \tmp1, #0
+ add \tmp2, \vreg, #1
+ SET_VREG_SHADOW \tmp1, \vreg
+ SET_VREG_SHADOW \tmp1, \tmp2
+.endm
/*
* Convert a virtual register index into an address.
@@ -426,10 +439,11 @@
/* move-wide vA, vB */
/* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
mov r3, rINST, lsr #12 @ r3<- B
- ubfx r2, rINST, #8, #4 @ r2<- A
+ ubfx rINST, rINST, #8, #4 @ rINST<- A
add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
- add r2, rFP, r2, lsl #2 @ r2<- &fp[A]
+ add r2, rFP, rINST, lsl #2 @ r2<- &fp[A]
ldmia r3, {r0-r1} @ r0/r1<- fp[B]
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero out the shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r2, {r0-r1} @ fp[A]<- r0/r1
@@ -442,10 +456,11 @@
/* move-wide/from16 vAA, vBBBB */
/* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
FETCH r3, 1 @ r3<- BBBB
- mov r2, rINST, lsr #8 @ r2<- AA
+ mov rINST, rINST, lsr #8 @ rINST<- AA
add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB]
- add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ add r2, rFP, rINST, lsl #2 @ r2<- &fp[AA]
ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB]
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero out the shadow regs
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r2, {r0-r1} @ fp[AA]<- r0/r1
@@ -460,10 +475,11 @@
FETCH r3, 2 @ r3<- BBBB
FETCH r2, 1 @ r2<- AAAA
add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB]
- add r2, rFP, r2, lsl #2 @ r2<- &fp[AAAA]
+ add lr, rFP, r2, lsl #2 @ r2<- &fp[AAAA]
ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB]
FETCH_ADVANCE_INST 3 @ advance rPC, load rINST
- stmia r2, {r0-r1} @ fp[AAAA]<- r0/r1
+ CLEAR_SHADOW_PAIR r2, r3, ip @ Zero out the shadow regs
+ stmia lr, {r0-r1} @ fp[AAAA]<- r0/r1
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
@@ -550,10 +566,11 @@
.L_op_move_result_wide: /* 0x0b */
/* File: arm/op_move_result_wide.S */
/* move-result-wide vAA */
- mov r2, rINST, lsr #8 @ r2<- AA
+ mov rINST, rINST, lsr #8 @ rINST<- AA
ldr r3, [rFP, #OFF_FP_RESULT_REGISTER]
- add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ add r2, rFP, rINST, lsl #2 @ r2<- &fp[AA]
ldmia r3, {r0-r1} @ r0/r1<- retval.j
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero out the shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
stmia r2, {r0-r1} @ fp[AA]<- r0/r1
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -731,6 +748,7 @@
mov r3, rINST, lsr #8 @ r3<- AA
mov r1, r0, asr #31 @ r1<- ssssssss
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
+ CLEAR_SHADOW_PAIR r3, r2, lr @ Zero out the shadow regs
add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r3, {r0-r1} @ vAA<- r0/r1
@@ -746,6 +764,7 @@
FETCH_S r2, 2 @ r2<- ssssBBBB (high)
FETCH_ADVANCE_INST 3 @ advance rPC, load rINST
orr r0, r0, r2, lsl #16 @ r0<- BBBBbbbb
+ CLEAR_SHADOW_PAIR r3, r2, lr @ Zero out the shadow regs
add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
mov r1, r0, asr #31 @ r1<- ssssssss
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -764,6 +783,7 @@
FETCH r3, 4 @ r3<- HHHH (high)
mov r9, rINST, lsr #8 @ r9<- AA
orr r1, r2, r3, lsl #16 @ r1<- HHHHhhhh (high word)
+ CLEAR_SHADOW_PAIR r9, r2, r3 @ Zero out the shadow regs
FETCH_ADVANCE_INST 5 @ advance rPC, load rINST
add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -780,6 +800,7 @@
mov r0, #0 @ r0<- 00000000
mov r1, r1, lsl #16 @ r1<- BBBB0000
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
+ CLEAR_SHADOW_PAIR r3, r0, r2 @ Zero shadow regs
add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r3, {r0-r1} @ vAA<- r0/r1
@@ -2068,6 +2089,7 @@
mov r3, r0, lsr #8 @ r3<- CC
GET_VREG r0, r2 @ r0<- vBB (array object)
GET_VREG r1, r3 @ r1<- vCC (requested index)
+ CLEAR_SHADOW_PAIR r9, r2, r3 @ Zero out the shadow regs
cmp r0, #0 @ null array object?
beq common_errNullObject @ yes, bail
ldr r3, [r0, #MIRROR_ARRAY_LENGTH_OFFSET] @ r3<- arrayObj->length
@@ -2519,8 +2541,9 @@
PREFETCH_INST 2
cmp r3, #0
bne MterpException @ bail out
- add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
- stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ CLEAR_SHADOW_PAIR r2, ip, lr @ Zero out the shadow regs
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
ADVANCE 2
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
@@ -2909,11 +2932,12 @@
bl artGet64StaticFromCode
ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET]
mov r9, rINST, lsr #8 @ r9<- AA
- add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add lr, rFP, r9, lsl #2 @ r9<- &fp[AA]
cmp r3, #0 @ Fail to resolve?
bne MterpException @ bail out
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
- stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ CLEAR_SHADOW_PAIR r9, r2, ip @ Zero out the shadow regs
+ stmia lr, {r0-r1} @ vAA/vAA+1<- r0/r1
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
@@ -3622,10 +3646,11 @@
*/
/* unop vA, vB */
mov r3, rINST, lsr #12 @ r3<- B
- ubfx r9, rINST, #8, #4 @ r9<- A
+ ubfx rINST, rINST, #8, #4 @ rINST<- A
add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
- add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[A]
ldmia r3, {r0-r1} @ r0/r1<- vAA
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
rsbs r0, r0, #0 @ optional op; may set condition codes
rsc r1, r1, #0 @ r0/r1<- op, r2-r3 changed
@@ -3649,10 +3674,11 @@
*/
/* unop vA, vB */
mov r3, rINST, lsr #12 @ r3<- B
- ubfx r9, rINST, #8, #4 @ r9<- A
+ ubfx rINST, rINST, #8, #4 @ rINST<- A
add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
- add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[A]
ldmia r3, {r0-r1} @ r0/r1<- vAA
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
mvn r0, r0 @ optional op; may set condition codes
mvn r1, r1 @ r0/r1<- op, r2-r3 changed
@@ -3702,10 +3728,11 @@
*/
/* unop vA, vB */
mov r3, rINST, lsr #12 @ r3<- B
- ubfx r9, rINST, #8, #4 @ r9<- A
+ ubfx rINST, rINST, #8, #4 @ rINST<- A
add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
- add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[A]
ldmia r3, {r0-r1} @ r0/r1<- vAA
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
@ optional op; may set condition codes
add r1, r1, #0x80000000 @ r0/r1<- op, r2-r3 changed
@@ -3729,10 +3756,11 @@
*/
/* unop vA, vB */
mov r3, rINST, lsr #12 @ r3<- B
- ubfx r9, rINST, #8, #4 @ r9<- A
+ ubfx rINST, rINST, #8, #4 @ rINST<- A
GET_VREG r0, r3 @ r0<- vB
- add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[A]
@ optional op; may set condition codes
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
mov r1, r0, asr #31 @ r0<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -3785,6 +3813,7 @@
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
and r9, r9, #15 @ r9<- A
fsitod d0, s0 @ d0<- op
+ CLEAR_SHADOW_PAIR r9, ip, lr @ Zero shadow regs
GET_INST_OPCODE ip @ extract opcode from rINST
VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA
fstd d0, [r9] @ vA<- d0
@@ -3912,10 +3941,11 @@
*/
/* unop vA, vB */
mov r3, rINST, lsr #12 @ r3<- B
- ubfx r9, rINST, #8, #4 @ r9<- A
+ ubfx rINST, rINST, #8, #4 @ rINST<- A
GET_VREG r0, r3 @ r0<- vB
- add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[A]
@ optional op; may set condition codes
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
bl f2l_doconv @ r0<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -3944,6 +3974,7 @@
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
and r9, r9, #15 @ r9<- A
fcvtds d0, s0 @ d0<- op
+ CLEAR_SHADOW_PAIR r9, ip, lr @ Zero shadow regs
GET_INST_OPCODE ip @ extract opcode from rINST
VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA
fstd d0, [r9] @ vA<- d0
@@ -3990,10 +4021,11 @@
*/
/* unop vA, vB */
mov r3, rINST, lsr #12 @ r3<- B
- ubfx r9, rINST, #8, #4 @ r9<- A
+ ubfx rINST, rINST, #8, #4 @ rINST<- A
add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
- add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[A]
ldmia r3, {r0-r1} @ r0/r1<- vAA
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
@ optional op; may set condition codes
bl d2l_doconv @ r0/r1<- op, r2-r3 changed
@@ -4570,10 +4602,10 @@
*/
/* binop vAA, vBB, vCC */
FETCH r0, 1 @ r0<- CCBB
- mov r9, rINST, lsr #8 @ r9<- AA
+ mov rINST, rINST, lsr #8 @ rINST<- AA
and r2, r0, #255 @ r2<- BB
mov r3, r0, lsr #8 @ r3<- CC
- add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA]
add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
@@ -4582,8 +4614,8 @@
orrs ip, r2, r3 @ second arg (r2-r3) is zero?
beq common_errDivideByZero
.endif
+ CLEAR_SHADOW_PAIR rINST, lr, ip @ Zero out the shadow regs
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
-
adds r0, r0, r2 @ optional op; may set condition codes
adc r1, r1, r3 @ result<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -4614,10 +4646,10 @@
*/
/* binop vAA, vBB, vCC */
FETCH r0, 1 @ r0<- CCBB
- mov r9, rINST, lsr #8 @ r9<- AA
+ mov rINST, rINST, lsr #8 @ rINST<- AA
and r2, r0, #255 @ r2<- BB
mov r3, r0, lsr #8 @ r3<- CC
- add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA]
add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
@@ -4626,8 +4658,8 @@
orrs ip, r2, r3 @ second arg (r2-r3) is zero?
beq common_errDivideByZero
.endif
+ CLEAR_SHADOW_PAIR rINST, lr, ip @ Zero out the shadow regs
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
-
subs r0, r0, r2 @ optional op; may set condition codes
sbc r1, r1, r3 @ result<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -4699,10 +4731,10 @@
*/
/* binop vAA, vBB, vCC */
FETCH r0, 1 @ r0<- CCBB
- mov r9, rINST, lsr #8 @ r9<- AA
+ mov rINST, rINST, lsr #8 @ rINST<- AA
and r2, r0, #255 @ r2<- BB
mov r3, r0, lsr #8 @ r3<- CC
- add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA]
add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
@@ -4711,8 +4743,8 @@
orrs ip, r2, r3 @ second arg (r2-r3) is zero?
beq common_errDivideByZero
.endif
+ CLEAR_SHADOW_PAIR rINST, lr, ip @ Zero out the shadow regs
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
-
@ optional op; may set condition codes
bl __aeabi_ldivmod @ result<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -4744,10 +4776,10 @@
*/
/* binop vAA, vBB, vCC */
FETCH r0, 1 @ r0<- CCBB
- mov r9, rINST, lsr #8 @ r9<- AA
+ mov rINST, rINST, lsr #8 @ rINST<- AA
and r2, r0, #255 @ r2<- BB
mov r3, r0, lsr #8 @ r3<- CC
- add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA]
add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
@@ -4756,8 +4788,8 @@
orrs ip, r2, r3 @ second arg (r2-r3) is zero?
beq common_errDivideByZero
.endif
+ CLEAR_SHADOW_PAIR rINST, lr, ip @ Zero out the shadow regs
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
-
@ optional op; may set condition codes
bl __aeabi_ldivmod @ result<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -4788,10 +4820,10 @@
*/
/* binop vAA, vBB, vCC */
FETCH r0, 1 @ r0<- CCBB
- mov r9, rINST, lsr #8 @ r9<- AA
+ mov rINST, rINST, lsr #8 @ rINST<- AA
and r2, r0, #255 @ r2<- BB
mov r3, r0, lsr #8 @ r3<- CC
- add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA]
add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
@@ -4800,8 +4832,8 @@
orrs ip, r2, r3 @ second arg (r2-r3) is zero?
beq common_errDivideByZero
.endif
+ CLEAR_SHADOW_PAIR rINST, lr, ip @ Zero out the shadow regs
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
-
and r0, r0, r2 @ optional op; may set condition codes
and r1, r1, r3 @ result<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -4832,10 +4864,10 @@
*/
/* binop vAA, vBB, vCC */
FETCH r0, 1 @ r0<- CCBB
- mov r9, rINST, lsr #8 @ r9<- AA
+ mov rINST, rINST, lsr #8 @ rINST<- AA
and r2, r0, #255 @ r2<- BB
mov r3, r0, lsr #8 @ r3<- CC
- add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA]
add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
@@ -4844,8 +4876,8 @@
orrs ip, r2, r3 @ second arg (r2-r3) is zero?
beq common_errDivideByZero
.endif
+ CLEAR_SHADOW_PAIR rINST, lr, ip @ Zero out the shadow regs
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
-
orr r0, r0, r2 @ optional op; may set condition codes
orr r1, r1, r3 @ result<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -4876,10 +4908,10 @@
*/
/* binop vAA, vBB, vCC */
FETCH r0, 1 @ r0<- CCBB
- mov r9, rINST, lsr #8 @ r9<- AA
+ mov rINST, rINST, lsr #8 @ rINST<- AA
and r2, r0, #255 @ r2<- BB
mov r3, r0, lsr #8 @ r3<- CC
- add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA]
add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
@@ -4888,8 +4920,8 @@
orrs ip, r2, r3 @ second arg (r2-r3) is zero?
beq common_errDivideByZero
.endif
+ CLEAR_SHADOW_PAIR rINST, lr, ip @ Zero out the shadow regs
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
-
eor r0, r0, r2 @ optional op; may set condition codes
eor r1, r1, r3 @ result<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -5177,9 +5209,9 @@
VREG_INDEX_TO_ADDR r2, r2 @ r2<- &vBB
fldd d1, [r3] @ d1<- vCC
fldd d0, [r2] @ d0<- vBB
-
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
faddd d2, d0, d1 @ s2<- op
+ CLEAR_SHADOW_PAIR r9, ip, lr @ Zero shadow regs
GET_INST_OPCODE ip @ extract opcode from rINST
VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vAA
fstd d2, [r9] @ vAA<- d2
@@ -5207,9 +5239,9 @@
VREG_INDEX_TO_ADDR r2, r2 @ r2<- &vBB
fldd d1, [r3] @ d1<- vCC
fldd d0, [r2] @ d0<- vBB
-
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
fsubd d2, d0, d1 @ s2<- op
+ CLEAR_SHADOW_PAIR r9, ip, lr @ Zero shadow regs
GET_INST_OPCODE ip @ extract opcode from rINST
VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vAA
fstd d2, [r9] @ vAA<- d2
@@ -5237,9 +5269,9 @@
VREG_INDEX_TO_ADDR r2, r2 @ r2<- &vBB
fldd d1, [r3] @ d1<- vCC
fldd d0, [r2] @ d0<- vBB
-
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
fmuld d2, d0, d1 @ s2<- op
+ CLEAR_SHADOW_PAIR r9, ip, lr @ Zero shadow regs
GET_INST_OPCODE ip @ extract opcode from rINST
VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vAA
fstd d2, [r9] @ vAA<- d2
@@ -5267,9 +5299,9 @@
VREG_INDEX_TO_ADDR r2, r2 @ r2<- &vBB
fldd d1, [r3] @ d1<- vCC
fldd d0, [r2] @ d0<- vBB
-
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
fdivd d2, d0, d1 @ s2<- op
+ CLEAR_SHADOW_PAIR r9, ip, lr @ Zero shadow regs
GET_INST_OPCODE ip @ extract opcode from rINST
VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vAA
fstd d2, [r9] @ vAA<- d2
@@ -5299,10 +5331,10 @@
*/
/* binop vAA, vBB, vCC */
FETCH r0, 1 @ r0<- CCBB
- mov r9, rINST, lsr #8 @ r9<- AA
+ mov rINST, rINST, lsr #8 @ rINST<- AA
and r2, r0, #255 @ r2<- BB
mov r3, r0, lsr #8 @ r3<- CC
- add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA]
add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
@@ -5311,8 +5343,8 @@
orrs ip, r2, r3 @ second arg (r2-r3) is zero?
beq common_errDivideByZero
.endif
+ CLEAR_SHADOW_PAIR rINST, lr, ip @ Zero out the shadow regs
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
-
@ optional op; may set condition codes
bl fmod @ result<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -5754,17 +5786,17 @@
*/
/* binop/2addr vA, vB */
mov r1, rINST, lsr #12 @ r1<- B
- ubfx r9, rINST, #8, #4 @ r9<- A
+ ubfx rINST, rINST, #8, #4 @ rINST<- A
add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
- add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[A]
ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
.if 0
orrs ip, r2, r3 @ second arg (r2-r3) is zero?
beq common_errDivideByZero
.endif
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
-
adds r0, r0, r2 @ optional op; may set condition codes
adc r1, r1, r3 @ result<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -5794,17 +5826,17 @@
*/
/* binop/2addr vA, vB */
mov r1, rINST, lsr #12 @ r1<- B
- ubfx r9, rINST, #8, #4 @ r9<- A
+ ubfx rINST, rINST, #8, #4 @ rINST<- A
add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
- add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[A]
ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
.if 0
orrs ip, r2, r3 @ second arg (r2-r3) is zero?
beq common_errDivideByZero
.endif
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
-
subs r0, r0, r2 @ optional op; may set condition codes
sbc r1, r1, r3 @ result<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -5863,17 +5895,17 @@
*/
/* binop/2addr vA, vB */
mov r1, rINST, lsr #12 @ r1<- B
- ubfx r9, rINST, #8, #4 @ r9<- A
+ ubfx rINST, rINST, #8, #4 @ rINST<- A
add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
- add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[A]
ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
.if 1
orrs ip, r2, r3 @ second arg (r2-r3) is zero?
beq common_errDivideByZero
.endif
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
-
@ optional op; may set condition codes
bl __aeabi_ldivmod @ result<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -5904,17 +5936,17 @@
*/
/* binop/2addr vA, vB */
mov r1, rINST, lsr #12 @ r1<- B
- ubfx r9, rINST, #8, #4 @ r9<- A
+ ubfx rINST, rINST, #8, #4 @ rINST<- A
add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
- add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[A]
ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
.if 1
orrs ip, r2, r3 @ second arg (r2-r3) is zero?
beq common_errDivideByZero
.endif
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
-
@ optional op; may set condition codes
bl __aeabi_ldivmod @ result<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -5944,17 +5976,17 @@
*/
/* binop/2addr vA, vB */
mov r1, rINST, lsr #12 @ r1<- B
- ubfx r9, rINST, #8, #4 @ r9<- A
+ ubfx rINST, rINST, #8, #4 @ rINST<- A
add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
- add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[A]
ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
.if 0
orrs ip, r2, r3 @ second arg (r2-r3) is zero?
beq common_errDivideByZero
.endif
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
-
and r0, r0, r2 @ optional op; may set condition codes
and r1, r1, r3 @ result<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -5984,17 +6016,17 @@
*/
/* binop/2addr vA, vB */
mov r1, rINST, lsr #12 @ r1<- B
- ubfx r9, rINST, #8, #4 @ r9<- A
+ ubfx rINST, rINST, #8, #4 @ rINST<- A
add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
- add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[A]
ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
.if 0
orrs ip, r2, r3 @ second arg (r2-r3) is zero?
beq common_errDivideByZero
.endif
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
-
orr r0, r0, r2 @ optional op; may set condition codes
orr r1, r1, r3 @ result<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -6024,17 +6056,17 @@
*/
/* binop/2addr vA, vB */
mov r1, rINST, lsr #12 @ r1<- B
- ubfx r9, rINST, #8, #4 @ r9<- A
+ ubfx rINST, rINST, #8, #4 @ rINST<- A
add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
- add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[A]
ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
.if 0
orrs ip, r2, r3 @ second arg (r2-r3) is zero?
beq common_errDivideByZero
.endif
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
-
eor r0, r0, r2 @ optional op; may set condition codes
eor r1, r1, r3 @ result<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -6294,10 +6326,10 @@
VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB
and r9, r9, #15 @ r9<- A
fldd d1, [r3] @ d1<- vB
+ CLEAR_SHADOW_PAIR r9, ip, r0 @ Zero out shadow regs
VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
fldd d0, [r9] @ d0<- vA
-
faddd d2, d0, d1 @ d2<- op
GET_INST_OPCODE ip @ extract opcode from rINST
fstd d2, [r9] @ vAA<- d2
@@ -6323,10 +6355,10 @@
VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB
and r9, r9, #15 @ r9<- A
fldd d1, [r3] @ d1<- vB
+ CLEAR_SHADOW_PAIR r9, ip, r0 @ Zero out shadow regs
VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
fldd d0, [r9] @ d0<- vA
-
fsubd d2, d0, d1 @ d2<- op
GET_INST_OPCODE ip @ extract opcode from rINST
fstd d2, [r9] @ vAA<- d2
@@ -6352,10 +6384,10 @@
VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB
and r9, r9, #15 @ r9<- A
fldd d1, [r3] @ d1<- vB
+ CLEAR_SHADOW_PAIR r9, ip, r0 @ Zero out shadow regs
VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
fldd d0, [r9] @ d0<- vA
-
fmuld d2, d0, d1 @ d2<- op
GET_INST_OPCODE ip @ extract opcode from rINST
fstd d2, [r9] @ vAA<- d2
@@ -6381,10 +6413,10 @@
VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB
and r9, r9, #15 @ r9<- A
fldd d1, [r3] @ d1<- vB
+ CLEAR_SHADOW_PAIR r9, ip, r0 @ Zero out shadow regs
VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
fldd d0, [r9] @ d0<- vA
-
fdivd d2, d0, d1 @ d2<- op
GET_INST_OPCODE ip @ extract opcode from rINST
fstd d2, [r9] @ vAA<- d2
@@ -6413,17 +6445,17 @@
*/
/* binop/2addr vA, vB */
mov r1, rINST, lsr #12 @ r1<- B
- ubfx r9, rINST, #8, #4 @ r9<- A
+ ubfx rINST, rINST, #8, #4 @ rINST<- A
add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
- add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ add r9, rFP, rINST, lsl #2 @ r9<- &fp[A]
ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
.if 0
orrs ip, r2, r3 @ second arg (r2-r3) is zero?
beq common_errDivideByZero
.endif
+ CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
-
@ optional op; may set condition codes
bl fmod @ result<- op, r0-r3 changed
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -7155,6 +7187,7 @@
ldrd r0, [r3, ip] @ r0<- obj.field (64 bits, aligned)
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ CLEAR_SHADOW_PAIR r2, ip, lr @ Zero out the shadow regs
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r3, {r0-r1} @ fp[A]<- r0/r1
GOTO_OPCODE ip @ jump to next instruction
diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S
index e2918dc..96229ce 100644
--- a/runtime/interpreter/mterp/out/mterp_x86.S
+++ b/runtime/interpreter/mterp/out/mterp_x86.S
@@ -96,6 +96,22 @@
*/
#include "asm_support.h"
+/*
+ * Handle mac compiler specific
+ */
+#if defined(__APPLE__)
+ #define MACRO_LITERAL(value) $(value)
+ #define FUNCTION_TYPE(name)
+ #define SIZE(start,end)
+ // Mac OS' symbols have an _ prefix.
+ #define SYMBOL(name) _ ## name
+#else
+ #define MACRO_LITERAL(value) $value
+ #define FUNCTION_TYPE(name) .type name, @function
+ #define SIZE(start,end) .size start, .-end
+ #define SYMBOL(name) name
+#endif
+
/* Frame size must be 16-byte aligned.
* Remember about 4 bytes for return address
*/
@@ -199,7 +215,7 @@
*/
.macro REFRESH_INST _opnum
movb rINSTbl, rINSTbh
- movb $\_opnum, rINSTbl
+ movb MACRO_LITERAL(\_opnum), rINSTbl
.endm
/*
@@ -215,7 +231,7 @@
.macro GOTO_NEXT
movzx rINSTbl,%eax
movzbl rINSTbh,rINST
- shll $7, %eax
+ shll MACRO_LITERAL(7), %eax
addl rIBASE, %eax
jmp *%eax
.endm
@@ -255,7 +271,7 @@
.macro SET_VREG _reg _vreg
movl \_reg, (rFP,\_vreg,4)
- movl $0, (rREFS,\_vreg,4)
+ movl MACRO_LITERAL(0), (rREFS,\_vreg,4)
.endm
/* Write wide value from xmm. xmm is clobbered. */
@@ -276,16 +292,16 @@
.macro SET_VREG_HIGH _reg _vreg
movl \_reg, 4(rFP,\_vreg,4)
- movl $0, 4(rREFS,\_vreg,4)
+ movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4)
.endm
.macro CLEAR_REF _vreg
- movl $0, (rREFS,\_vreg,4)
+ movl MACRO_LITERAL(0), (rREFS,\_vreg,4)
.endm
.macro CLEAR_WIDE_REF _vreg
- movl $0, (rREFS,\_vreg,4)
- movl $0, 4(rREFS,\_vreg,4)
+ movl MACRO_LITERAL(0), (rREFS,\_vreg,4)
+ movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4)
.endm
/* File: x86/entry.S */
@@ -309,8 +325,8 @@
*/
.text
- .global ExecuteMterpImpl
- .type ExecuteMterpImpl, %function
+ .global SYMBOL(ExecuteMterpImpl)
+ FUNCTION_TYPE(ExecuteMterpImpl)
/*
* On entry:
@@ -321,7 +337,7 @@
*
*/
-ExecuteMterpImpl:
+SYMBOL(ExecuteMterpImpl):
.cfi_startproc
/* Allocate frame */
subl $FRAME_SIZE, %esp
@@ -362,9 +378,9 @@
/* NOTE: no fallthrough */
- .global artMterpAsmInstructionStart
- .type artMterpAsmInstructionStart, %function
-artMterpAsmInstructionStart = .L_op_nop
+ .global SYMBOL(artMterpAsmInstructionStart)
+ FUNCTION_TYPE(SYMBOL(artMterpAsmInstructionStart))
+SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
.text
/* ------------------------------ */
@@ -382,11 +398,11 @@
movzbl rINSTbl, %eax # eax <- BA
andb $0xf, %al # eax <- A
shrl $4, rINST # rINST <- B
- GET_VREG rINST rINST
+ GET_VREG rINST, rINST
.if 0
- SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B]
+ SET_VREG_OBJECT rINST, %eax # fp[A] <- fp[B]
.else
- SET_VREG rINST %eax # fp[A] <- fp[B]
+ SET_VREG rINST, %eax # fp[A] <- fp[B]
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -398,11 +414,11 @@
/* op vAA, vBBBB */
movzx rINSTbl, %eax # eax <- AA
movw 2(rPC), rINSTw # rINSTw <- BBBB
- GET_VREG rINST rINST # rINST <- fp[BBBB]
+ GET_VREG rINST, rINST # rINST <- fp[BBBB]
.if 0
- SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B]
+ SET_VREG_OBJECT rINST, %eax # fp[A] <- fp[B]
.else
- SET_VREG rINST %eax # fp[A] <- fp[B]
+ SET_VREG rINST, %eax # fp[A] <- fp[B]
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -414,11 +430,11 @@
/* op vAAAA, vBBBB */
movzwl 4(rPC), %ecx # ecx <- BBBB
movzwl 2(rPC), %eax # eax <- AAAA
- GET_VREG rINST %ecx
+ GET_VREG rINST, %ecx
.if 0
- SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B]
+ SET_VREG_OBJECT rINST, %eax # fp[A] <- fp[B]
.else
- SET_VREG rINST %eax # fp[A] <- fp[B]
+ SET_VREG rINST, %eax # fp[A] <- fp[B]
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
@@ -431,8 +447,8 @@
movzbl rINSTbl, %ecx # ecx <- BA
sarl $4, rINST # rINST <- B
andb $0xf, %cl # ecx <- A
- GET_WIDE_FP_VREG %xmm0 rINST # xmm0 <- v[B]
- SET_WIDE_FP_VREG %xmm0 %ecx # v[A] <- xmm0
+ GET_WIDE_FP_VREG %xmm0, rINST # xmm0 <- v[B]
+ SET_WIDE_FP_VREG %xmm0, %ecx # v[A] <- xmm0
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
/* ------------------------------ */
@@ -443,8 +459,8 @@
/* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
movzwl 2(rPC), %ecx # ecx <- BBBB
movzbl rINSTbl, %eax # eax <- AAAA
- GET_WIDE_FP_VREG %xmm0 %ecx # xmm0 <- v[B]
- SET_WIDE_FP_VREG %xmm0 %eax # v[A] <- xmm0
+ GET_WIDE_FP_VREG %xmm0, %ecx # xmm0 <- v[B]
+ SET_WIDE_FP_VREG %xmm0, %eax # v[A] <- xmm0
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -455,8 +471,8 @@
/* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
movzwl 4(rPC), %ecx # ecx<- BBBB
movzwl 2(rPC), %eax # eax<- AAAA
- GET_WIDE_FP_VREG %xmm0 %ecx # xmm0 <- v[B]
- SET_WIDE_FP_VREG %xmm0 %eax # v[A] <- xmm0
+ GET_WIDE_FP_VREG %xmm0, %ecx # xmm0 <- v[B]
+ SET_WIDE_FP_VREG %xmm0, %eax # v[A] <- xmm0
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
/* ------------------------------ */
@@ -469,11 +485,11 @@
movzbl rINSTbl, %eax # eax <- BA
andb $0xf, %al # eax <- A
shrl $4, rINST # rINST <- B
- GET_VREG rINST rINST
+ GET_VREG rINST, rINST
.if 1
- SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B]
+ SET_VREG_OBJECT rINST, %eax # fp[A] <- fp[B]
.else
- SET_VREG rINST %eax # fp[A] <- fp[B]
+ SET_VREG rINST, %eax # fp[A] <- fp[B]
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -487,11 +503,11 @@
/* op vAA, vBBBB */
movzx rINSTbl, %eax # eax <- AA
movw 2(rPC), rINSTw # rINSTw <- BBBB
- GET_VREG rINST rINST # rINST <- fp[BBBB]
+ GET_VREG rINST, rINST # rINST <- fp[BBBB]
.if 1
- SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B]
+ SET_VREG_OBJECT rINST, %eax # fp[A] <- fp[B]
.else
- SET_VREG rINST %eax # fp[A] <- fp[B]
+ SET_VREG rINST, %eax # fp[A] <- fp[B]
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -505,11 +521,11 @@
/* op vAAAA, vBBBB */
movzwl 4(rPC), %ecx # ecx <- BBBB
movzwl 2(rPC), %eax # eax <- AAAA
- GET_VREG rINST %ecx
+ GET_VREG rINST, %ecx
.if 1
- SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B]
+ SET_VREG_OBJECT rINST, %eax # fp[A] <- fp[B]
.else
- SET_VREG rINST %eax # fp[A] <- fp[B]
+ SET_VREG rINST, %eax # fp[A] <- fp[B]
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
@@ -523,9 +539,9 @@
movl OFF_FP_RESULT_REGISTER(rFP), %eax # get pointer to result JType.
movl (%eax), %eax # r0 <- result.i.
.if 0
- SET_VREG_OBJECT %eax rINST # fp[A] <- fp[B]
+ SET_VREG_OBJECT %eax, rINST # fp[A] <- fp[B]
.else
- SET_VREG %eax rINST # fp[A] <- fp[B]
+ SET_VREG %eax, rINST # fp[A] <- fp[B]
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -537,8 +553,8 @@
movl OFF_FP_RESULT_REGISTER(rFP), %eax # get pointer to result JType.
movl 4(%eax), %ecx # Get high
movl (%eax), %eax # Get low
- SET_VREG %eax rINST # v[AA+0] <- eax
- SET_VREG_HIGH %ecx rINST # v[AA+1] <- ecx
+ SET_VREG %eax, rINST # v[AA+0] <- eax
+ SET_VREG_HIGH %ecx, rINST # v[AA+1] <- ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
/* ------------------------------ */
@@ -551,9 +567,9 @@
movl OFF_FP_RESULT_REGISTER(rFP), %eax # get pointer to result JType.
movl (%eax), %eax # r0 <- result.i.
.if 1
- SET_VREG_OBJECT %eax rINST # fp[A] <- fp[B]
+ SET_VREG_OBJECT %eax, rINST # fp[A] <- fp[B]
.else
- SET_VREG %eax rINST # fp[A] <- fp[B]
+ SET_VREG %eax, rINST # fp[A] <- fp[B]
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -565,7 +581,7 @@
/* move-exception vAA */
movl rSELF, %ecx
movl THREAD_EXCEPTION_OFFSET(%ecx), %eax
- SET_VREG_OBJECT %eax rINST # fp[AA] <- exception object
+ SET_VREG_OBJECT %eax, rINST # fp[AA] <- exception object
movl $0, THREAD_EXCEPTION_OFFSET(%ecx)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -574,12 +590,12 @@
.L_op_return_void: /* 0x0e */
/* File: x86/op_return_void.S */
.extern MterpThreadFenceForConstructor
- call MterpThreadFenceForConstructor
+ call SYMBOL(MterpThreadFenceForConstructor)
movl rSELF, %eax
testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax)
jz 1f
movl %eax, OUT_ARG0(%esp)
- call MterpSuspendCheck
+ call SYMBOL(MterpSuspendCheck)
1:
xorl %eax, %eax
xorl %ecx, %ecx
@@ -596,14 +612,14 @@
*/
/* op vAA */
.extern MterpThreadFenceForConstructor
- call MterpThreadFenceForConstructor
+ call SYMBOL(MterpThreadFenceForConstructor)
movl rSELF, %eax
testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax)
jz 1f
movl %eax, OUT_ARG0(%esp)
- call MterpSuspendCheck
+ call SYMBOL(MterpSuspendCheck)
1:
- GET_VREG %eax rINST # eax <- vAA
+ GET_VREG %eax, rINST # eax <- vAA
xorl %ecx, %ecx
jmp MterpReturn
@@ -616,15 +632,15 @@
*/
/* return-wide vAA */
.extern MterpThreadFenceForConstructor
- call MterpThreadFenceForConstructor
+ call SYMBOL(MterpThreadFenceForConstructor)
movl rSELF, %eax
testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax)
jz 1f
movl %eax, OUT_ARG0(%esp)
- call MterpSuspendCheck
+ call SYMBOL(MterpSuspendCheck)
1:
- GET_VREG %eax rINST # eax <- v[AA+0]
- GET_VREG_HIGH %ecx rINST # ecx <- v[AA+1]
+ GET_VREG %eax, rINST # eax <- v[AA+0]
+ GET_VREG_HIGH %ecx, rINST # ecx <- v[AA+1]
jmp MterpReturn
/* ------------------------------ */
@@ -639,14 +655,14 @@
*/
/* op vAA */
.extern MterpThreadFenceForConstructor
- call MterpThreadFenceForConstructor
+ call SYMBOL(MterpThreadFenceForConstructor)
movl rSELF, %eax
testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax)
jz 1f
movl %eax, OUT_ARG0(%esp)
- call MterpSuspendCheck
+ call SYMBOL(MterpSuspendCheck)
1:
- GET_VREG %eax rINST # eax <- vAA
+ GET_VREG %eax, rINST # eax <- vAA
xorl %ecx, %ecx
jmp MterpReturn
@@ -660,7 +676,7 @@
movl $0xf, rINST
andl %eax, rINST # rINST <- A
sarl $4, %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
/* ------------------------------ */
@@ -669,7 +685,7 @@
/* File: x86/op_const_16.S */
/* const/16 vAA, #+BBBB */
movswl 2(rPC), %ecx # ecx <- ssssBBBB
- SET_VREG %ecx rINST # vAA <- ssssBBBB
+ SET_VREG %ecx, rINST # vAA <- ssssBBBB
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -678,7 +694,7 @@
/* File: x86/op_const.S */
/* const vAA, #+BBBBbbbb */
movl 2(rPC), %eax # grab all 32 bits at once
- SET_VREG %eax rINST # vAA<- eax
+ SET_VREG %eax, rINST # vAA<- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
/* ------------------------------ */
@@ -688,7 +704,7 @@
/* const/high16 vAA, #+BBBB0000 */
movzwl 2(rPC), %eax # eax <- 0000BBBB
sall $16, %eax # eax <- BBBB0000
- SET_VREG %eax rINST # vAA <- eax
+ SET_VREG %eax, rINST # vAA <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -699,8 +715,8 @@
movswl 2(rPC), %eax # eax <- ssssBBBB
movl rIBASE, %ecx # preserve rIBASE (cltd trashes it)
cltd # rIBASE:eax <- ssssssssssssBBBB
- SET_VREG_HIGH rIBASE rINST # store msw
- SET_VREG %eax rINST # store lsw
+ SET_VREG_HIGH rIBASE, rINST # store msw
+ SET_VREG %eax, rINST # store lsw
movl %ecx, rIBASE # restore rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -712,8 +728,8 @@
movl 2(rPC), %eax # eax <- BBBBbbbb
movl rIBASE, %ecx # preserve rIBASE (cltd trashes it)
cltd # rIBASE:eax <- ssssssssssssBBBB
- SET_VREG_HIGH rIBASE rINST # store msw
- SET_VREG %eax rINST # store lsw
+ SET_VREG_HIGH rIBASE, rINST # store msw
+ SET_VREG %eax, rINST # store lsw
movl %ecx, rIBASE # restore rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
@@ -725,8 +741,8 @@
movl 2(rPC), %eax # eax <- lsw
movzbl rINSTbl, %ecx # ecx <- AA
movl 6(rPC), rINST # rINST <- msw
- SET_VREG %eax %ecx
- SET_VREG_HIGH rINST %ecx
+ SET_VREG %eax, %ecx
+ SET_VREG_HIGH rINST, %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 5
/* ------------------------------ */
@@ -736,9 +752,9 @@
/* const-wide/high16 vAA, #+BBBB000000000000 */
movzwl 2(rPC), %eax # eax <- 0000BBBB
sall $16, %eax # eax <- BBBB0000
- SET_VREG_HIGH %eax rINST # v[AA+1] <- eax
+ SET_VREG_HIGH %eax, rINST # v[AA+1] <- eax
xorl %eax, %eax
- SET_VREG %eax rINST # v[AA+0] <- eax
+ SET_VREG %eax, rINST # v[AA+0] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -754,7 +770,7 @@
movl %eax, OUT_ARG2(%esp)
movl rSELF, %eax
movl %eax, OUT_ARG3(%esp)
- call MterpConstString # (index, tgt_reg, shadow_frame, self)
+ call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self)
REFRESH_IBASE
testl %eax, %eax
jnz MterpPossibleException
@@ -773,7 +789,7 @@
movl %eax, OUT_ARG2(%esp)
movl rSELF, %eax
movl %eax, OUT_ARG3(%esp)
- call MterpConstString # (index, tgt_reg, shadow_frame, self)
+ call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self)
REFRESH_IBASE
testl %eax, %eax
jnz MterpPossibleException
@@ -792,7 +808,7 @@
movl %eax, OUT_ARG2(%esp)
movl rSELF, %eax
movl %eax, OUT_ARG3(%esp)
- call MterpConstClass # (index, tgt_reg, shadow_frame, self)
+ call SYMBOL(MterpConstClass) # (index, tgt_reg, shadow_frame, self)
REFRESH_IBASE
testl %eax, %eax
jnz MterpPossibleException
@@ -807,11 +823,11 @@
*/
/* monitor-enter vAA */
EXPORT_PC
- GET_VREG %ecx rINST
+ GET_VREG %ecx, rINST
movl %ecx, OUT_ARG0(%esp)
movl rSELF, %eax
movl %eax, OUT_ARG1(%esp)
- call artLockObjectFromCode # (object, self)
+ call SYMBOL(artLockObjectFromCode) # (object, self)
REFRESH_IBASE
testl %eax, %eax
jnz MterpException
@@ -830,11 +846,11 @@
*/
/* monitor-exit vAA */
EXPORT_PC
- GET_VREG %ecx rINST
+ GET_VREG %ecx, rINST
movl %ecx, OUT_ARG0(%esp)
movl rSELF, %eax
movl %eax, OUT_ARG1(%esp)
- call artUnlockObjectFromCode # (object, self)
+ call SYMBOL(artUnlockObjectFromCode) # (object, self)
REFRESH_IBASE
testl %eax, %eax
jnz MterpException
@@ -857,7 +873,7 @@
movl %eax, OUT_ARG2(%esp)
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp)
- call MterpCheckCast # (index, &obj, method, self)
+ call SYMBOL(MterpCheckCast) # (index, &obj, method, self)
REFRESH_IBASE
testl %eax, %eax
jnz MterpPossibleException
@@ -885,13 +901,13 @@
movl %eax, OUT_ARG2(%esp)
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp)
- call MterpInstanceOf # (index, &obj, method, self)
+ call SYMBOL(MterpInstanceOf) # (index, &obj, method, self)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
andb $0xf, rINSTbl # rINSTbl <- A
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -903,12 +919,12 @@
*/
mov rINST, %eax # eax <- BA
sarl $4, rINST # rINST <- B
- GET_VREG %ecx rINST # ecx <- vB (object ref)
+ GET_VREG %ecx, rINST # ecx <- vB (object ref)
testl %ecx, %ecx # is null?
je common_errNullObject
andb $0xf, %al # eax <- A
movl MIRROR_ARRAY_LENGTH_OFFSET(%ecx), rINST
- SET_VREG rINST %eax
+ SET_VREG rINST, %eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
/* ------------------------------ */
@@ -926,7 +942,7 @@
movl %ecx, OUT_ARG1(%esp)
REFRESH_INST 34
movl rINST, OUT_ARG2(%esp)
- call MterpNewInstance
+ call SYMBOL(MterpNewInstance)
REFRESH_IBASE
testl %eax, %eax # 0 means an exception is thrown
jz MterpPossibleException
@@ -952,7 +968,7 @@
movl rINST, OUT_ARG2(%esp)
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp)
- call MterpNewArray
+ call SYMBOL(MterpNewArray)
REFRESH_IBASE
testl %eax, %eax # 0 means an exception is thrown
jz MterpPossibleException
@@ -976,7 +992,7 @@
movl rPC, OUT_ARG1(%esp)
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp)
- call MterpFilledNewArray
+ call SYMBOL(MterpFilledNewArray)
REFRESH_IBASE
testl %eax, %eax # 0 means an exception is thrown
jz MterpPossibleException
@@ -1001,7 +1017,7 @@
movl rPC, OUT_ARG1(%esp)
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp)
- call MterpFilledNewArrayRange
+ call SYMBOL(MterpFilledNewArrayRange)
REFRESH_IBASE
testl %eax, %eax # 0 means an exception is thrown
jz MterpPossibleException
@@ -1016,10 +1032,10 @@
EXPORT_PC
movl 2(rPC), %ecx # ecx <- BBBBbbbb
leal (rPC,%ecx,2), %ecx # ecx <- PC + BBBBbbbb*2
- GET_VREG %eax rINST # eax <- vAA (array object)
+ GET_VREG %eax, rINST # eax <- vAA (array object)
movl %eax, OUT_ARG0(%esp)
movl %ecx, OUT_ARG1(%esp)
- call MterpFillArrayData # (obj, payload)
+ call SYMBOL(MterpFillArrayData) # (obj, payload)
REFRESH_IBASE
testl %eax, %eax # 0 means an exception is thrown
jz MterpPossibleException
@@ -1034,7 +1050,7 @@
*/
/* throw vAA */
EXPORT_PC
- GET_VREG %eax rINST # eax<- vAA (exception object)
+ GET_VREG %eax, rINST # eax<- vAA (exception object)
testl %eax, %eax
jz common_errNullObject
movl rSELF,%ecx
@@ -1133,11 +1149,11 @@
*/
/* op vAA, +BBBB */
movl 2(rPC), %ecx # ecx <- BBBBbbbb
- GET_VREG %eax rINST # eax <- vAA
+ GET_VREG %eax, rINST # eax <- vAA
leal (rPC,%ecx,2), %ecx # ecx <- PC + BBBBbbbb*2
movl %eax, OUT_ARG1(%esp) # ARG1 <- vAA
movl %ecx, OUT_ARG0(%esp) # ARG0 <- switchData
- call MterpDoPackedSwitch
+ call SYMBOL(MterpDoPackedSwitch)
addl %eax, %eax
leal (rPC, %eax), rPC
FETCH_INST
@@ -1167,11 +1183,11 @@
*/
/* op vAA, +BBBB */
movl 2(rPC), %ecx # ecx <- BBBBbbbb
- GET_VREG %eax rINST # eax <- vAA
+ GET_VREG %eax, rINST # eax <- vAA
leal (rPC,%ecx,2), %ecx # ecx <- PC + BBBBbbbb*2
movl %eax, OUT_ARG1(%esp) # ARG1 <- vAA
movl %ecx, OUT_ARG0(%esp) # ARG0 <- switchData
- call MterpDoSparseSwitch
+ call SYMBOL(MterpDoSparseSwitch)
addl %eax, %eax
leal (rPC, %eax), rPC
FETCH_INST
@@ -1223,7 +1239,7 @@
.Lop_cmpl_float_less:
decl %eax
.Lop_cmpl_float_finish:
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -1264,7 +1280,7 @@
.Lop_cmpg_float_less:
decl %eax
.Lop_cmpg_float_finish:
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -1305,7 +1321,7 @@
.Lop_cmpl_double_less:
decl %eax
.Lop_cmpl_double_finish:
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -1346,7 +1362,7 @@
.Lop_cmpg_double_less:
decl %eax
.Lop_cmpg_double_finish:
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -1361,17 +1377,17 @@
/* cmp-long vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG_HIGH %eax %eax # eax <- v[BB+1], BB is clobbered
+ GET_VREG_HIGH %eax, %eax # eax <- v[BB+1], BB is clobbered
cmpl VREG_HIGH_ADDRESS(%ecx), %eax
jl .Lop_cmp_long_smaller
jg .Lop_cmp_long_bigger
movzbl 2(rPC), %eax # eax <- BB, restore BB
- GET_VREG %eax %eax # eax <- v[BB]
+ GET_VREG %eax, %eax # eax <- v[BB]
sub VREG_ADDRESS(%ecx), %eax
ja .Lop_cmp_long_bigger
jb .Lop_cmp_long_smaller
.Lop_cmp_long_finish:
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
.Lop_cmp_long_bigger:
@@ -1397,7 +1413,7 @@
/* if-cmp vA, vB, +CCCC */
movzx rINSTbl, %ecx # ecx <- A+
andb $0xf, %cl # ecx <- A
- GET_VREG %eax %ecx # eax <- vA
+ GET_VREG %eax, %ecx # eax <- vA
sarl $4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
movl $2, %eax # assume not taken
@@ -1432,7 +1448,7 @@
/* if-cmp vA, vB, +CCCC */
movzx rINSTbl, %ecx # ecx <- A+
andb $0xf, %cl # ecx <- A
- GET_VREG %eax %ecx # eax <- vA
+ GET_VREG %eax, %ecx # eax <- vA
sarl $4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
movl $2, %eax # assume not taken
@@ -1467,7 +1483,7 @@
/* if-cmp vA, vB, +CCCC */
movzx rINSTbl, %ecx # ecx <- A+
andb $0xf, %cl # ecx <- A
- GET_VREG %eax %ecx # eax <- vA
+ GET_VREG %eax, %ecx # eax <- vA
sarl $4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
movl $2, %eax # assume not taken
@@ -1502,7 +1518,7 @@
/* if-cmp vA, vB, +CCCC */
movzx rINSTbl, %ecx # ecx <- A+
andb $0xf, %cl # ecx <- A
- GET_VREG %eax %ecx # eax <- vA
+ GET_VREG %eax, %ecx # eax <- vA
sarl $4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
movl $2, %eax # assume not taken
@@ -1537,7 +1553,7 @@
/* if-cmp vA, vB, +CCCC */
movzx rINSTbl, %ecx # ecx <- A+
andb $0xf, %cl # ecx <- A
- GET_VREG %eax %ecx # eax <- vA
+ GET_VREG %eax, %ecx # eax <- vA
sarl $4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
movl $2, %eax # assume not taken
@@ -1572,7 +1588,7 @@
/* if-cmp vA, vB, +CCCC */
movzx rINSTbl, %ecx # ecx <- A+
andb $0xf, %cl # ecx <- A
- GET_VREG %eax %ecx # eax <- vA
+ GET_VREG %eax, %ecx # eax <- vA
sarl $4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
movl $2, %eax # assume not taken
@@ -1857,14 +1873,14 @@
/* op vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB (array object)
- GET_VREG %ecx %ecx # ecx <- vCC (requested index)
+ GET_VREG %eax, %eax # eax <- vBB (array object)
+ GET_VREG %ecx, %ecx # ecx <- vCC (requested index)
testl %eax, %eax # null array object?
je common_errNullObject # bail if so
cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx
jae common_errArrayIndex # index >= length, bail.
movl MIRROR_INT_ARRAY_DATA_OFFSET(%eax,%ecx,4), %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1877,15 +1893,15 @@
/* aget-wide vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB (array object)
- GET_VREG %ecx %ecx # ecx <- vCC (requested index)
+ GET_VREG %eax, %eax # eax <- vBB (array object)
+ GET_VREG %ecx, %ecx # ecx <- vCC (requested index)
testl %eax, %eax # null array object?
je common_errNullObject # bail if so
cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx
jae common_errArrayIndex # index >= length, bail.
leal MIRROR_WIDE_ARRAY_DATA_OFFSET(%eax,%ecx,8), %eax
movq (%eax), %xmm0 # xmm0 <- vBB[vCC]
- SET_WIDE_FP_VREG %xmm0 rINST # vAA <- xmm0
+ SET_WIDE_FP_VREG %xmm0, rINST # vAA <- xmm0
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1900,17 +1916,17 @@
/* op vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB (array object)
- GET_VREG %ecx %ecx # ecs <- vCC (requested index)
+ GET_VREG %eax, %eax # eax <- vBB (array object)
+ GET_VREG %ecx, %ecx # ecs <- vCC (requested index)
EXPORT_PC
movl %eax, OUT_ARG0(%esp)
movl %ecx, OUT_ARG1(%esp)
- call artAGetObjectFromMterp # (array, index)
+ call SYMBOL(artAGetObjectFromMterp) # (array, index)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
- SET_VREG_OBJECT %eax rINST
+ SET_VREG_OBJECT %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1927,14 +1943,14 @@
/* op vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB (array object)
- GET_VREG %ecx %ecx # ecx <- vCC (requested index)
+ GET_VREG %eax, %eax # eax <- vBB (array object)
+ GET_VREG %ecx, %ecx # ecx <- vCC (requested index)
testl %eax, %eax # null array object?
je common_errNullObject # bail if so
cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx
jae common_errArrayIndex # index >= length, bail.
movzbl MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(%eax,%ecx,1), %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -1952,14 +1968,14 @@
/* op vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB (array object)
- GET_VREG %ecx %ecx # ecx <- vCC (requested index)
+ GET_VREG %eax, %eax # eax <- vBB (array object)
+ GET_VREG %ecx, %ecx # ecx <- vCC (requested index)
testl %eax, %eax # null array object?
je common_errNullObject # bail if so
cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx
jae common_errArrayIndex # index >= length, bail.
movsbl MIRROR_BYTE_ARRAY_DATA_OFFSET(%eax,%ecx,1), %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -1977,14 +1993,14 @@
/* op vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB (array object)
- GET_VREG %ecx %ecx # ecx <- vCC (requested index)
+ GET_VREG %eax, %eax # eax <- vBB (array object)
+ GET_VREG %ecx, %ecx # ecx <- vCC (requested index)
testl %eax, %eax # null array object?
je common_errNullObject # bail if so
cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx
jae common_errArrayIndex # index >= length, bail.
movzwl MIRROR_CHAR_ARRAY_DATA_OFFSET(%eax,%ecx,2), %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2002,14 +2018,14 @@
/* op vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB (array object)
- GET_VREG %ecx %ecx # ecx <- vCC (requested index)
+ GET_VREG %eax, %eax # eax <- vBB (array object)
+ GET_VREG %ecx, %ecx # ecx <- vCC (requested index)
testl %eax, %eax # null array object?
je common_errNullObject # bail if so
cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx
jae common_errArrayIndex # index >= length, bail.
movswl MIRROR_SHORT_ARRAY_DATA_OFFSET(%eax,%ecx,2), %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2026,14 +2042,14 @@
/* op vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB (array object)
- GET_VREG %ecx %ecx # ecx <- vCC (requested index)
+ GET_VREG %eax, %eax # eax <- vBB (array object)
+ GET_VREG %ecx, %ecx # ecx <- vCC (requested index)
testl %eax, %eax # null array object?
je common_errNullObject # bail if so
cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx
jae common_errArrayIndex # index >= length, bail.
leal MIRROR_INT_ARRAY_DATA_OFFSET(%eax,%ecx,4), %eax
- GET_VREG rINST rINST
+ GET_VREG rINST, rINST
movl rINST, (%eax)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2048,14 +2064,14 @@
/* aput-wide vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB (array object)
- GET_VREG %ecx %ecx # ecx <- vCC (requested index)
+ GET_VREG %eax, %eax # eax <- vBB (array object)
+ GET_VREG %ecx, %ecx # ecx <- vCC (requested index)
testl %eax, %eax # null array object?
je common_errNullObject # bail if so
cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx
jae common_errArrayIndex # index >= length, bail.
leal MIRROR_WIDE_ARRAY_DATA_OFFSET(%eax,%ecx,8), %eax
- GET_WIDE_FP_VREG %xmm0 rINST # xmm0 <- vAA
+ GET_WIDE_FP_VREG %xmm0, rINST # xmm0 <- vAA
movq %xmm0, (%eax) # vBB[vCC] <- xmm0
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2073,7 +2089,7 @@
movl rPC, OUT_ARG1(%esp)
REFRESH_INST 77
movl rINST, OUT_ARG2(%esp)
- call MterpAputObject # (array, index)
+ call SYMBOL(MterpAputObject) # (array, index)
REFRESH_IBASE
testl %eax, %eax
jz MterpPossibleException
@@ -2093,14 +2109,14 @@
/* op vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB (array object)
- GET_VREG %ecx %ecx # ecx <- vCC (requested index)
+ GET_VREG %eax, %eax # eax <- vBB (array object)
+ GET_VREG %ecx, %ecx # ecx <- vCC (requested index)
testl %eax, %eax # null array object?
je common_errNullObject # bail if so
cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx
jae common_errArrayIndex # index >= length, bail.
leal MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(%eax,%ecx,1), %eax
- GET_VREG rINST rINST
+ GET_VREG rINST, rINST
movb rINSTbl, (%eax)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2119,14 +2135,14 @@
/* op vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB (array object)
- GET_VREG %ecx %ecx # ecx <- vCC (requested index)
+ GET_VREG %eax, %eax # eax <- vBB (array object)
+ GET_VREG %ecx, %ecx # ecx <- vCC (requested index)
testl %eax, %eax # null array object?
je common_errNullObject # bail if so
cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx
jae common_errArrayIndex # index >= length, bail.
leal MIRROR_BYTE_ARRAY_DATA_OFFSET(%eax,%ecx,1), %eax
- GET_VREG rINST rINST
+ GET_VREG rINST, rINST
movb rINSTbl, (%eax)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2145,14 +2161,14 @@
/* op vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB (array object)
- GET_VREG %ecx %ecx # ecx <- vCC (requested index)
+ GET_VREG %eax, %eax # eax <- vBB (array object)
+ GET_VREG %ecx, %ecx # ecx <- vCC (requested index)
testl %eax, %eax # null array object?
je common_errNullObject # bail if so
cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx
jae common_errArrayIndex # index >= length, bail.
leal MIRROR_CHAR_ARRAY_DATA_OFFSET(%eax,%ecx,2), %eax
- GET_VREG rINST rINST
+ GET_VREG rINST, rINST
movw rINSTw, (%eax)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2171,14 +2187,14 @@
/* op vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB (array object)
- GET_VREG %ecx %ecx # ecx <- vCC (requested index)
+ GET_VREG %eax, %eax # eax <- vBB (array object)
+ GET_VREG %ecx, %ecx # ecx <- vCC (requested index)
testl %eax, %eax # null array object?
je common_errNullObject # bail if so
cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx
jae common_errArrayIndex # index >= length, bail.
leal MIRROR_SHORT_ARRAY_DATA_OFFSET(%eax,%ecx,2), %eax
- GET_VREG rINST rINST
+ GET_VREG rINST, rINST
movw rINSTw, (%eax)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2203,16 +2219,16 @@
movl %eax, OUT_ARG2(%esp) # referrer
mov rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call artGet32InstanceFromCode
+ call SYMBOL(artGet32InstanceFromCode)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 0
- SET_VREG_OBJECT %eax rINST # fp[A] <-value
+ SET_VREG_OBJECT %eax, rINST # fp[A] <-value
.else
- SET_VREG %eax rINST # fp[A] <-value
+ SET_VREG %eax, rINST # fp[A] <-value
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2236,13 +2252,13 @@
movl %eax, OUT_ARG2(%esp) # referrer
mov rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call artGet64InstanceFromCode
+ call SYMBOL(artGet64InstanceFromCode)
mov rSELF, %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
- SET_VREG %eax rINST
- SET_VREG_HIGH %edx rINST
+ SET_VREG %eax, rINST
+ SET_VREG_HIGH %edx, rINST
REFRESH_IBASE_FROM_SELF %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2267,16 +2283,16 @@
movl %eax, OUT_ARG2(%esp) # referrer
mov rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call artGetObjInstanceFromCode
+ call SYMBOL(artGetObjInstanceFromCode)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 1
- SET_VREG_OBJECT %eax rINST # fp[A] <-value
+ SET_VREG_OBJECT %eax, rINST # fp[A] <-value
.else
- SET_VREG %eax rINST # fp[A] <-value
+ SET_VREG %eax, rINST # fp[A] <-value
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2302,16 +2318,16 @@
movl %eax, OUT_ARG2(%esp) # referrer
mov rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call artGetBooleanInstanceFromCode
+ call SYMBOL(artGetBooleanInstanceFromCode)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 0
- SET_VREG_OBJECT %eax rINST # fp[A] <-value
+ SET_VREG_OBJECT %eax, rINST # fp[A] <-value
.else
- SET_VREG %eax rINST # fp[A] <-value
+ SET_VREG %eax, rINST # fp[A] <-value
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2337,16 +2353,16 @@
movl %eax, OUT_ARG2(%esp) # referrer
mov rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call artGetByteInstanceFromCode
+ call SYMBOL(artGetByteInstanceFromCode)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 0
- SET_VREG_OBJECT %eax rINST # fp[A] <-value
+ SET_VREG_OBJECT %eax, rINST # fp[A] <-value
.else
- SET_VREG %eax rINST # fp[A] <-value
+ SET_VREG %eax, rINST # fp[A] <-value
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2372,16 +2388,16 @@
movl %eax, OUT_ARG2(%esp) # referrer
mov rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call artGetCharInstanceFromCode
+ call SYMBOL(artGetCharInstanceFromCode)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 0
- SET_VREG_OBJECT %eax rINST # fp[A] <-value
+ SET_VREG_OBJECT %eax, rINST # fp[A] <-value
.else
- SET_VREG %eax rINST # fp[A] <-value
+ SET_VREG %eax, rINST # fp[A] <-value
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2407,16 +2423,16 @@
movl %eax, OUT_ARG2(%esp) # referrer
mov rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call artGetShortInstanceFromCode
+ call SYMBOL(artGetShortInstanceFromCode)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 0
- SET_VREG_OBJECT %eax rINST # fp[A] <-value
+ SET_VREG_OBJECT %eax, rINST # fp[A] <-value
.else
- SET_VREG %eax rINST # fp[A] <-value
+ SET_VREG %eax, rINST # fp[A] <-value
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2444,7 +2460,7 @@
movl %eax, OUT_ARG2(%esp) # fp[A]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
- call artSet32InstanceFromMterp
+ call SYMBOL(artSet32InstanceFromMterp)
testl %eax, %eax
jnz MterpPossibleException
REFRESH_IBASE
@@ -2468,7 +2484,7 @@
movl %eax, OUT_ARG2(%esp) # &fp[A]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
- call artSet64InstanceFromMterp
+ call SYMBOL(artSet64InstanceFromMterp)
testl %eax, %eax
jnz MterpPossibleException
REFRESH_IBASE
@@ -2486,7 +2502,7 @@
movl rINST, OUT_ARG2(%esp)
movl rSELF, %eax
movl %eax, OUT_ARG3(%esp)
- call MterpIputObject
+ call SYMBOL(MterpIputObject)
testl %eax, %eax
jz MterpException
REFRESH_IBASE
@@ -2516,7 +2532,7 @@
movl %eax, OUT_ARG2(%esp) # fp[A]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
- call artSet8InstanceFromMterp
+ call SYMBOL(artSet8InstanceFromMterp)
testl %eax, %eax
jnz MterpPossibleException
REFRESH_IBASE
@@ -2547,7 +2563,7 @@
movl %eax, OUT_ARG2(%esp) # fp[A]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
- call artSet8InstanceFromMterp
+ call SYMBOL(artSet8InstanceFromMterp)
testl %eax, %eax
jnz MterpPossibleException
REFRESH_IBASE
@@ -2578,7 +2594,7 @@
movl %eax, OUT_ARG2(%esp) # fp[A]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
- call artSet16InstanceFromMterp
+ call SYMBOL(artSet16InstanceFromMterp)
testl %eax, %eax
jnz MterpPossibleException
REFRESH_IBASE
@@ -2609,7 +2625,7 @@
movl %eax, OUT_ARG2(%esp) # fp[A]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
- call artSet16InstanceFromMterp
+ call SYMBOL(artSet16InstanceFromMterp)
testl %eax, %eax
jnz MterpPossibleException
REFRESH_IBASE
@@ -2634,15 +2650,15 @@
movl %eax, OUT_ARG1(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp) # self
- call artGet32StaticFromCode
+ call SYMBOL(artGet32StaticFromCode)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
.if 0
- SET_VREG_OBJECT %eax rINST # fp[A] <- value
+ SET_VREG_OBJECT %eax, rINST # fp[A] <- value
.else
- SET_VREG %eax rINST # fp[A] <- value
+ SET_VREG %eax, rINST # fp[A] <- value
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2663,12 +2679,12 @@
movl %eax, OUT_ARG1(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp) # self
- call artGet64StaticFromCode
+ call SYMBOL(artGet64StaticFromCode)
movl rSELF, %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
- SET_VREG %eax rINST # fp[A]<- low part
- SET_VREG_HIGH %edx rINST # fp[A+1]<- high part
+ SET_VREG %eax, rINST # fp[A]<- low part
+ SET_VREG_HIGH %edx, rINST # fp[A+1]<- high part
REFRESH_IBASE_FROM_SELF %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2691,15 +2707,15 @@
movl %eax, OUT_ARG1(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp) # self
- call artGetObjStaticFromCode
+ call SYMBOL(artGetObjStaticFromCode)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
.if 1
- SET_VREG_OBJECT %eax rINST # fp[A] <- value
+ SET_VREG_OBJECT %eax, rINST # fp[A] <- value
.else
- SET_VREG %eax rINST # fp[A] <- value
+ SET_VREG %eax, rINST # fp[A] <- value
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2723,15 +2739,15 @@
movl %eax, OUT_ARG1(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp) # self
- call artGetBooleanStaticFromCode
+ call SYMBOL(artGetBooleanStaticFromCode)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
.if 0
- SET_VREG_OBJECT %eax rINST # fp[A] <- value
+ SET_VREG_OBJECT %eax, rINST # fp[A] <- value
.else
- SET_VREG %eax rINST # fp[A] <- value
+ SET_VREG %eax, rINST # fp[A] <- value
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2755,15 +2771,15 @@
movl %eax, OUT_ARG1(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp) # self
- call artGetByteStaticFromCode
+ call SYMBOL(artGetByteStaticFromCode)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
.if 0
- SET_VREG_OBJECT %eax rINST # fp[A] <- value
+ SET_VREG_OBJECT %eax, rINST # fp[A] <- value
.else
- SET_VREG %eax rINST # fp[A] <- value
+ SET_VREG %eax, rINST # fp[A] <- value
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2787,15 +2803,15 @@
movl %eax, OUT_ARG1(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp) # self
- call artGetCharStaticFromCode
+ call SYMBOL(artGetCharStaticFromCode)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
.if 0
- SET_VREG_OBJECT %eax rINST # fp[A] <- value
+ SET_VREG_OBJECT %eax, rINST # fp[A] <- value
.else
- SET_VREG %eax rINST # fp[A] <- value
+ SET_VREG %eax, rINST # fp[A] <- value
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2819,15 +2835,15 @@
movl %eax, OUT_ARG1(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp) # self
- call artGetShortStaticFromCode
+ call SYMBOL(artGetShortStaticFromCode)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
.if 0
- SET_VREG_OBJECT %eax rINST # fp[A] <- value
+ SET_VREG_OBJECT %eax, rINST # fp[A] <- value
.else
- SET_VREG %eax rINST # fp[A] <- value
+ SET_VREG %eax, rINST # fp[A] <- value
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2846,13 +2862,13 @@
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref BBBB
- GET_VREG rINST rINST
+ GET_VREG rINST, rINST
movl rINST, OUT_ARG1(%esp) # fp[AA]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG2(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call artSet32StaticFromCode
+ call SYMBOL(artSet32StaticFromCode)
testl %eax, %eax
jnz MterpException
REFRESH_IBASE
@@ -2877,7 +2893,7 @@
movl %eax, OUT_ARG2(%esp) # &fp[AA]
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call artSet64IndirectStaticFromMterp
+ call SYMBOL(artSet64IndirectStaticFromMterp)
testl %eax, %eax
jnz MterpException
REFRESH_IBASE
@@ -2895,7 +2911,7 @@
movl rINST, OUT_ARG2(%esp)
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp)
- call MterpSputObject
+ call SYMBOL(MterpSputObject)
testl %eax, %eax
jz MterpException
REFRESH_IBASE
@@ -2916,13 +2932,13 @@
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref BBBB
- GET_VREG rINST rINST
+ GET_VREG rINST, rINST
movl rINST, OUT_ARG1(%esp) # fp[AA]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG2(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call artSet8StaticFromCode
+ call SYMBOL(artSet8StaticFromCode)
testl %eax, %eax
jnz MterpException
REFRESH_IBASE
@@ -2944,13 +2960,13 @@
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref BBBB
- GET_VREG rINST rINST
+ GET_VREG rINST, rINST
movl rINST, OUT_ARG1(%esp) # fp[AA]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG2(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call artSet8StaticFromCode
+ call SYMBOL(artSet8StaticFromCode)
testl %eax, %eax
jnz MterpException
REFRESH_IBASE
@@ -2972,13 +2988,13 @@
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref BBBB
- GET_VREG rINST rINST
+ GET_VREG rINST, rINST
movl rINST, OUT_ARG1(%esp) # fp[AA]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG2(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call artSet16StaticFromCode
+ call SYMBOL(artSet16StaticFromCode)
testl %eax, %eax
jnz MterpException
REFRESH_IBASE
@@ -3000,13 +3016,13 @@
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref BBBB
- GET_VREG rINST rINST
+ GET_VREG rINST, rINST
movl rINST, OUT_ARG1(%esp) # fp[AA]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG2(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call artSet16StaticFromCode
+ call SYMBOL(artSet16StaticFromCode)
testl %eax, %eax
jnz MterpException
REFRESH_IBASE
@@ -3032,7 +3048,7 @@
movl rPC, OUT_ARG2(%esp)
REFRESH_INST 110
movl rINST, OUT_ARG3(%esp)
- call MterpInvokeVirtual
+ call SYMBOL(MterpInvokeVirtual)
testl %eax, %eax
jz MterpException
REFRESH_IBASE
@@ -3065,7 +3081,7 @@
movl rPC, OUT_ARG2(%esp)
REFRESH_INST 111
movl rINST, OUT_ARG3(%esp)
- call MterpInvokeSuper
+ call SYMBOL(MterpInvokeSuper)
testl %eax, %eax
jz MterpException
REFRESH_IBASE
@@ -3098,7 +3114,7 @@
movl rPC, OUT_ARG2(%esp)
REFRESH_INST 112
movl rINST, OUT_ARG3(%esp)
- call MterpInvokeDirect
+ call SYMBOL(MterpInvokeDirect)
testl %eax, %eax
jz MterpException
REFRESH_IBASE
@@ -3124,7 +3140,7 @@
movl rPC, OUT_ARG2(%esp)
REFRESH_INST 113
movl rINST, OUT_ARG3(%esp)
- call MterpInvokeStatic
+ call SYMBOL(MterpInvokeStatic)
testl %eax, %eax
jz MterpException
REFRESH_IBASE
@@ -3151,7 +3167,7 @@
movl rPC, OUT_ARG2(%esp)
REFRESH_INST 114
movl rINST, OUT_ARG3(%esp)
- call MterpInvokeInterface
+ call SYMBOL(MterpInvokeInterface)
testl %eax, %eax
jz MterpException
REFRESH_IBASE
@@ -3173,7 +3189,7 @@
testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax)
jz 1f
movl %eax, OUT_ARG0(%esp)
- call MterpSuspendCheck
+ call SYMBOL(MterpSuspendCheck)
1:
xorl %eax, %eax
xorl %ecx, %ecx
@@ -3198,7 +3214,7 @@
movl rPC, OUT_ARG2(%esp)
REFRESH_INST 116
movl rINST, OUT_ARG3(%esp)
- call MterpInvokeVirtualRange
+ call SYMBOL(MterpInvokeVirtualRange)
testl %eax, %eax
jz MterpException
REFRESH_IBASE
@@ -3224,7 +3240,7 @@
movl rPC, OUT_ARG2(%esp)
REFRESH_INST 117
movl rINST, OUT_ARG3(%esp)
- call MterpInvokeSuperRange
+ call SYMBOL(MterpInvokeSuperRange)
testl %eax, %eax
jz MterpException
REFRESH_IBASE
@@ -3250,7 +3266,7 @@
movl rPC, OUT_ARG2(%esp)
REFRESH_INST 118
movl rINST, OUT_ARG3(%esp)
- call MterpInvokeDirectRange
+ call SYMBOL(MterpInvokeDirectRange)
testl %eax, %eax
jz MterpException
REFRESH_IBASE
@@ -3276,7 +3292,7 @@
movl rPC, OUT_ARG2(%esp)
REFRESH_INST 119
movl rINST, OUT_ARG3(%esp)
- call MterpInvokeStaticRange
+ call SYMBOL(MterpInvokeStaticRange)
testl %eax, %eax
jz MterpException
REFRESH_IBASE
@@ -3302,7 +3318,7 @@
movl rPC, OUT_ARG2(%esp)
REFRESH_INST 120
movl rINST, OUT_ARG3(%esp)
- call MterpInvokeInterfaceRange
+ call SYMBOL(MterpInvokeInterfaceRange)
testl %eax, %eax
jz MterpException
REFRESH_IBASE
@@ -3343,10 +3359,10 @@
/* unop vA, vB */
movzbl rINSTbl,%ecx # ecx <- A+
sarl $4,rINST # rINST <- B
- GET_VREG %eax rINST # eax <- vB
+ GET_VREG %eax, rINST # eax <- vB
andb $0xf,%cl # ecx <- A
negl %eax
- SET_VREG %eax %ecx
+ SET_VREG %eax, %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -3362,10 +3378,10 @@
/* unop vA, vB */
movzbl rINSTbl,%ecx # ecx <- A+
sarl $4,rINST # rINST <- B
- GET_VREG %eax rINST # eax <- vB
+ GET_VREG %eax, rINST # eax <- vB
andb $0xf,%cl # ecx <- A
notl %eax
- SET_VREG %eax %ecx
+ SET_VREG %eax, %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -3377,13 +3393,13 @@
movzbl rINSTbl, %ecx # ecx <- BA
sarl $4, %ecx # ecx <- B
andb $0xf, rINSTbl # rINST <- A
- GET_VREG %eax %ecx # eax <- v[B+0]
- GET_VREG_HIGH %ecx %ecx # ecx <- v[B+1]
+ GET_VREG %eax, %ecx # eax <- v[B+0]
+ GET_VREG_HIGH %ecx, %ecx # ecx <- v[B+1]
negl %eax
adcl $0, %ecx
negl %ecx
- SET_VREG %eax rINST # v[A+0] <- eax
- SET_VREG_HIGH %ecx rINST # v[A+1] <- ecx
+ SET_VREG %eax, rINST # v[A+0] <- eax
+ SET_VREG_HIGH %ecx, rINST # v[A+1] <- ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -3395,12 +3411,12 @@
movzbl rINSTbl, %ecx # ecx <- BA
sarl $4, %ecx # ecx <- B
andb $0xf, rINSTbl # rINST <- A
- GET_VREG %eax %ecx # eax <- v[B+0]
- GET_VREG_HIGH %ecx %ecx # ecx <- v[B+1]
+ GET_VREG %eax, %ecx # eax <- v[B+0]
+ GET_VREG_HIGH %ecx, %ecx # ecx <- v[B+1]
notl %eax
notl %ecx
- SET_VREG %eax rINST # v[A+0] <- eax
- SET_VREG_HIGH %ecx rINST # v[A+1] <- ecx
+ SET_VREG %eax, rINST # v[A+0] <- eax
+ SET_VREG_HIGH %ecx, rINST # v[A+1] <- ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
/* ------------------------------ */
@@ -3456,12 +3472,12 @@
/* int to long vA, vB */
movzbl rINSTbl, %eax # eax <- +A
sarl $4, %eax # eax <- B
- GET_VREG %eax %eax # eax <- vB
+ GET_VREG %eax, %eax # eax <- vB
andb $0xf, rINSTbl # rINST <- A
movl rIBASE, %ecx # cltd trashes rIBASE/edx
cltd # rINST:eax<- sssssssBBBBBBBB
- SET_VREG_HIGH rIBASE rINST # v[A+1] <- rIBASE
- SET_VREG %eax rINST # v[A+0] <- %eax
+ SET_VREG_HIGH rIBASE, rINST # v[A+1] <- rIBASE
+ SET_VREG %eax, rINST # v[A+0] <- %eax
movl %ecx, rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -3523,11 +3539,11 @@
movzbl rINSTbl, %eax # eax <- BA
andb $0xf, %al # eax <- A
shrl $4, rINST # rINST <- B
- GET_VREG rINST rINST
+ GET_VREG rINST, rINST
.if 0
- SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B]
+ SET_VREG_OBJECT rINST, %eax # fp[A] <- fp[B]
.else
- SET_VREG rINST %eax # fp[A] <- fp[B]
+ SET_VREG rINST, %eax # fp[A] <- fp[B]
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -3904,10 +3920,10 @@
/* unop vA, vB */
movzbl rINSTbl,%ecx # ecx <- A+
sarl $4,rINST # rINST <- B
- GET_VREG %eax rINST # eax <- vB
+ GET_VREG %eax, rINST # eax <- vB
andb $0xf,%cl # ecx <- A
movsbl %al, %eax
- SET_VREG %eax %ecx
+ SET_VREG %eax, %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -3923,10 +3939,10 @@
/* unop vA, vB */
movzbl rINSTbl,%ecx # ecx <- A+
sarl $4,rINST # rINST <- B
- GET_VREG %eax rINST # eax <- vB
+ GET_VREG %eax, rINST # eax <- vB
andb $0xf,%cl # ecx <- A
movzwl %ax,%eax
- SET_VREG %eax %ecx
+ SET_VREG %eax, %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -3942,10 +3958,10 @@
/* unop vA, vB */
movzbl rINSTbl,%ecx # ecx <- A+
sarl $4,rINST # rINST <- B
- GET_VREG %eax rINST # eax <- vB
+ GET_VREG %eax, rINST # eax <- vB
andb $0xf,%cl # ecx <- A
movswl %ax, %eax
- SET_VREG %eax %ecx
+ SET_VREG %eax, %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -3966,9 +3982,9 @@
/* binop vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB
+ GET_VREG %eax, %eax # eax <- vBB
addl (rFP,%ecx,4), %eax # ex: addl (rFP,%ecx,4),%eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -3989,9 +4005,9 @@
/* binop vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB
+ GET_VREG %eax, %eax # eax <- vBB
subl (rFP,%ecx,4), %eax # ex: addl (rFP,%ecx,4),%eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -4005,11 +4021,11 @@
/* mul vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB
+ GET_VREG %eax, %eax # eax <- vBB
mov rIBASE, LOCAL0(%esp)
imull (rFP,%ecx,4), %eax # trashes rIBASE/edx
mov LOCAL0(%esp), rIBASE
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -4024,8 +4040,8 @@
/* div/rem vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB
- GET_VREG %ecx %ecx # ecx <- vCC
+ GET_VREG %eax, %eax # eax <- vBB
+ GET_VREG %ecx, %ecx # ecx <- vCC
mov rIBASE, LOCAL0(%esp)
testl %ecx, %ecx
je common_errDivideByZero
@@ -4061,7 +4077,7 @@
xorl %edx, %edx # Clear %edx before divide
div %cx
.Lop_div_int_finish:
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
mov LOCAL0(%esp), rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -4078,8 +4094,8 @@
/* div/rem vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB
- GET_VREG %ecx %ecx # ecx <- vCC
+ GET_VREG %eax, %eax # eax <- vBB
+ GET_VREG %ecx, %ecx # ecx <- vCC
mov rIBASE, LOCAL0(%esp)
testl %ecx, %ecx
je common_errDivideByZero
@@ -4115,7 +4131,7 @@
xorl %edx, %edx # Clear %edx before divide
div %cx
.Lop_rem_int_finish:
- SET_VREG rIBASE rINST
+ SET_VREG rIBASE, rINST
mov LOCAL0(%esp), rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -4137,9 +4153,9 @@
/* binop vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB
+ GET_VREG %eax, %eax # eax <- vBB
andl (rFP,%ecx,4), %eax # ex: addl (rFP,%ecx,4),%eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -4160,9 +4176,9 @@
/* binop vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB
+ GET_VREG %eax, %eax # eax <- vBB
orl (rFP,%ecx,4), %eax # ex: addl (rFP,%ecx,4),%eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -4183,9 +4199,9 @@
/* binop vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB
+ GET_VREG %eax, %eax # eax <- vBB
xorl (rFP,%ecx,4), %eax # ex: addl (rFP,%ecx,4),%eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -4201,10 +4217,10 @@
/* binop vAA, vBB, vCC */
movzbl 2(rPC),%eax # eax <- BB
movzbl 3(rPC),%ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB
- GET_VREG %ecx %ecx # eax <- vBB
+ GET_VREG %eax, %eax # eax <- vBB
+ GET_VREG %ecx, %ecx # eax <- vBB
sall %cl, %eax # ex: addl %ecx,%eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -4220,10 +4236,10 @@
/* binop vAA, vBB, vCC */
movzbl 2(rPC),%eax # eax <- BB
movzbl 3(rPC),%ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB
- GET_VREG %ecx %ecx # eax <- vBB
+ GET_VREG %eax, %eax # eax <- vBB
+ GET_VREG %ecx, %ecx # eax <- vBB
sarl %cl, %eax # ex: addl %ecx,%eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -4239,10 +4255,10 @@
/* binop vAA, vBB, vCC */
movzbl 2(rPC),%eax # eax <- BB
movzbl 3(rPC),%ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB
- GET_VREG %ecx %ecx # eax <- vBB
+ GET_VREG %eax, %eax # eax <- vBB
+ GET_VREG %ecx, %ecx # eax <- vBB
shrl %cl, %eax # ex: addl %ecx,%eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -4255,16 +4271,16 @@
* Generic 64-bit binary operation.
*/
/* binop vAA, vBB, vCC */
- movzbl 2(rPC),%eax # eax <- BB
- movzbl 3(rPC),%ecx # ecx <- CC
- movl rIBASE,LOCAL0(%esp) # save rIBASE
- GET_VREG rIBASE %eax # rIBASE <- v[BB+0]
- GET_VREG_HIGH %eax %eax # eax <- v[BB+1]
+ movzbl 2(rPC), %eax # eax <- BB
+ movzbl 3(rPC), %ecx # ecx <- CC
+ movl rIBASE, LOCAL0(%esp) # save rIBASE
+ GET_VREG rIBASE, %eax # rIBASE <- v[BB+0]
+ GET_VREG_HIGH %eax, %eax # eax <- v[BB+1]
addl (rFP,%ecx,4), rIBASE # ex: addl (rFP,%ecx,4),rIBASE
adcl 4(rFP,%ecx,4), %eax # ex: adcl 4(rFP,%ecx,4),%eax
- SET_VREG rIBASE rINST # v[AA+0] <- rIBASE
- movl LOCAL0(%esp),rIBASE # restore rIBASE
- SET_VREG_HIGH %eax rINST # v[AA+1] <- eax
+ SET_VREG rIBASE, rINST # v[AA+0] <- rIBASE
+ movl LOCAL0(%esp), rIBASE # restore rIBASE
+ SET_VREG_HIGH %eax, rINST # v[AA+1] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -4277,16 +4293,16 @@
* Generic 64-bit binary operation.
*/
/* binop vAA, vBB, vCC */
- movzbl 2(rPC),%eax # eax <- BB
- movzbl 3(rPC),%ecx # ecx <- CC
- movl rIBASE,LOCAL0(%esp) # save rIBASE
- GET_VREG rIBASE %eax # rIBASE <- v[BB+0]
- GET_VREG_HIGH %eax %eax # eax <- v[BB+1]
+ movzbl 2(rPC), %eax # eax <- BB
+ movzbl 3(rPC), %ecx # ecx <- CC
+ movl rIBASE, LOCAL0(%esp) # save rIBASE
+ GET_VREG rIBASE, %eax # rIBASE <- v[BB+0]
+ GET_VREG_HIGH %eax, %eax # eax <- v[BB+1]
subl (rFP,%ecx,4), rIBASE # ex: addl (rFP,%ecx,4),rIBASE
sbbl 4(rFP,%ecx,4), %eax # ex: adcl 4(rFP,%ecx,4),%eax
- SET_VREG rIBASE rINST # v[AA+0] <- rIBASE
- movl LOCAL0(%esp),rIBASE # restore rIBASE
- SET_VREG_HIGH %eax rINST # v[AA+1] <- eax
+ SET_VREG rIBASE, rINST # v[AA+0] <- rIBASE
+ movl LOCAL0(%esp), rIBASE # restore rIBASE
+ SET_VREG_HIGH %eax, rINST # v[AA+1] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -4323,9 +4339,9 @@
mov LOCAL0(%esp), rPC # restore Interpreter PC
mov LOCAL1(%esp), rFP # restore FP
leal (%ecx,rIBASE), rIBASE # full result now in rIBASE:%eax
- SET_VREG_HIGH rIBASE rINST # v[B+1] <- rIBASE
+ SET_VREG_HIGH rIBASE, rINST # v[B+1] <- rIBASE
mov LOCAL2(%esp), rIBASE # restore IBASE
- SET_VREG %eax rINST # v[B] <- eax
+ SET_VREG %eax, rINST # v[B] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -4340,18 +4356,18 @@
mov rIBASE, LOCAL0(%esp) # save rIBASE/%edx
mov rINST, LOCAL1(%esp) # save rINST/%ebx
movzbl 3(rPC), %eax # eax <- CC
- GET_VREG %ecx %eax
- GET_VREG_HIGH %ebx %eax
+ GET_VREG %ecx, %eax
+ GET_VREG_HIGH %ebx, %eax
movl %ecx, %edx
orl %ebx, %ecx
jz common_errDivideByZero
movzbl 2(rPC), %eax # eax <- BB
- GET_VREG_HIGH %ecx %eax
- GET_VREG %eax %eax
- call art_quick_ldiv
+ GET_VREG_HIGH %ecx, %eax
+ GET_VREG %eax, %eax
+ call SYMBOL(art_quick_ldiv)
mov LOCAL1(%esp), rINST # restore rINST/%ebx
- SET_VREG_HIGH rIBASE rINST
- SET_VREG %eax rINST
+ SET_VREG_HIGH rIBASE, rINST
+ SET_VREG %eax, rINST
mov LOCAL0(%esp), rIBASE # restore rIBASE/%edx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -4368,18 +4384,18 @@
mov rIBASE, LOCAL0(%esp) # save rIBASE/%edx
mov rINST, LOCAL1(%esp) # save rINST/%ebx
movzbl 3(rPC), %eax # eax <- CC
- GET_VREG %ecx %eax
- GET_VREG_HIGH %ebx %eax
+ GET_VREG %ecx, %eax
+ GET_VREG_HIGH %ebx, %eax
movl %ecx, %edx
orl %ebx, %ecx
jz common_errDivideByZero
movzbl 2(rPC), %eax # eax <- BB
- GET_VREG_HIGH %ecx %eax
- GET_VREG %eax %eax
- call art_quick_lmod
+ GET_VREG_HIGH %ecx, %eax
+ GET_VREG %eax, %eax
+ call SYMBOL(art_quick_lmod)
mov LOCAL1(%esp), rINST # restore rINST/%ebx
- SET_VREG_HIGH rIBASE rINST
- SET_VREG %eax rINST
+ SET_VREG_HIGH rIBASE, rINST
+ SET_VREG %eax, rINST
mov LOCAL0(%esp), rIBASE # restore rIBASE/%edx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -4393,16 +4409,16 @@
* Generic 64-bit binary operation.
*/
/* binop vAA, vBB, vCC */
- movzbl 2(rPC),%eax # eax <- BB
- movzbl 3(rPC),%ecx # ecx <- CC
- movl rIBASE,LOCAL0(%esp) # save rIBASE
- GET_VREG rIBASE %eax # rIBASE <- v[BB+0]
- GET_VREG_HIGH %eax %eax # eax <- v[BB+1]
+ movzbl 2(rPC), %eax # eax <- BB
+ movzbl 3(rPC), %ecx # ecx <- CC
+ movl rIBASE, LOCAL0(%esp) # save rIBASE
+ GET_VREG rIBASE, %eax # rIBASE <- v[BB+0]
+ GET_VREG_HIGH %eax, %eax # eax <- v[BB+1]
andl (rFP,%ecx,4), rIBASE # ex: addl (rFP,%ecx,4),rIBASE
andl 4(rFP,%ecx,4), %eax # ex: adcl 4(rFP,%ecx,4),%eax
- SET_VREG rIBASE rINST # v[AA+0] <- rIBASE
- movl LOCAL0(%esp),rIBASE # restore rIBASE
- SET_VREG_HIGH %eax rINST # v[AA+1] <- eax
+ SET_VREG rIBASE, rINST # v[AA+0] <- rIBASE
+ movl LOCAL0(%esp), rIBASE # restore rIBASE
+ SET_VREG_HIGH %eax, rINST # v[AA+1] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -4415,16 +4431,16 @@
* Generic 64-bit binary operation.
*/
/* binop vAA, vBB, vCC */
- movzbl 2(rPC),%eax # eax <- BB
- movzbl 3(rPC),%ecx # ecx <- CC
- movl rIBASE,LOCAL0(%esp) # save rIBASE
- GET_VREG rIBASE %eax # rIBASE <- v[BB+0]
- GET_VREG_HIGH %eax %eax # eax <- v[BB+1]
+ movzbl 2(rPC), %eax # eax <- BB
+ movzbl 3(rPC), %ecx # ecx <- CC
+ movl rIBASE, LOCAL0(%esp) # save rIBASE
+ GET_VREG rIBASE, %eax # rIBASE <- v[BB+0]
+ GET_VREG_HIGH %eax, %eax # eax <- v[BB+1]
orl (rFP,%ecx,4), rIBASE # ex: addl (rFP,%ecx,4),rIBASE
orl 4(rFP,%ecx,4), %eax # ex: adcl 4(rFP,%ecx,4),%eax
- SET_VREG rIBASE rINST # v[AA+0] <- rIBASE
- movl LOCAL0(%esp),rIBASE # restore rIBASE
- SET_VREG_HIGH %eax rINST # v[AA+1] <- eax
+ SET_VREG rIBASE, rINST # v[AA+0] <- rIBASE
+ movl LOCAL0(%esp), rIBASE # restore rIBASE
+ SET_VREG_HIGH %eax, rINST # v[AA+1] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -4437,16 +4453,16 @@
* Generic 64-bit binary operation.
*/
/* binop vAA, vBB, vCC */
- movzbl 2(rPC),%eax # eax <- BB
- movzbl 3(rPC),%ecx # ecx <- CC
- movl rIBASE,LOCAL0(%esp) # save rIBASE
- GET_VREG rIBASE %eax # rIBASE <- v[BB+0]
- GET_VREG_HIGH %eax %eax # eax <- v[BB+1]
+ movzbl 2(rPC), %eax # eax <- BB
+ movzbl 3(rPC), %ecx # ecx <- CC
+ movl rIBASE, LOCAL0(%esp) # save rIBASE
+ GET_VREG rIBASE, %eax # rIBASE <- v[BB+0]
+ GET_VREG_HIGH %eax, %eax # eax <- v[BB+1]
xorl (rFP,%ecx,4), rIBASE # ex: addl (rFP,%ecx,4),rIBASE
xorl 4(rFP,%ecx,4), %eax # ex: adcl 4(rFP,%ecx,4),%eax
- SET_VREG rIBASE rINST # v[AA+0] <- rIBASE
- movl LOCAL0(%esp),rIBASE # restore rIBASE
- SET_VREG_HIGH %eax rINST # v[AA+1] <- eax
+ SET_VREG rIBASE, rINST # v[AA+0] <- rIBASE
+ movl LOCAL0(%esp), rIBASE # restore rIBASE
+ SET_VREG_HIGH %eax, rINST # v[AA+1] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -4469,9 +4485,9 @@
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
movl rIBASE, LOCAL0(%esp)
- GET_VREG_HIGH rIBASE %eax # ecx <- v[BB+1]
- GET_VREG %ecx %ecx # ecx <- vCC
- GET_VREG %eax %eax # eax <- v[BB+0]
+ GET_VREG_HIGH rIBASE, %eax # ecx <- v[BB+1]
+ GET_VREG %ecx, %ecx # ecx <- vCC
+ GET_VREG %eax, %eax # eax <- v[BB+0]
shldl %eax,rIBASE
sall %cl, %eax
testb $32, %cl
@@ -4479,9 +4495,9 @@
movl %eax, rIBASE
xorl %eax, %eax
2:
- SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE
+ SET_VREG_HIGH rIBASE, rINST # v[AA+1] <- rIBASE
movl LOCAL0(%esp), rIBASE
- SET_VREG %eax rINST # v[AA+0] <- %eax
+ SET_VREG %eax, rINST # v[AA+0] <- %eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -4503,9 +4519,9 @@
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
movl rIBASE, LOCAL0(%esp)
- GET_VREG_HIGH rIBASE %eax # rIBASE<- v[BB+1]
- GET_VREG %ecx %ecx # ecx <- vCC
- GET_VREG %eax %eax # eax <- v[BB+0]
+ GET_VREG_HIGH rIBASE, %eax # rIBASE<- v[BB+1]
+ GET_VREG %ecx, %ecx # ecx <- vCC
+ GET_VREG %eax, %eax # eax <- v[BB+0]
shrdl rIBASE, %eax
sarl %cl, rIBASE
testb $32, %cl
@@ -4513,9 +4529,9 @@
movl rIBASE, %eax
sarl $31, rIBASE
2:
- SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE
+ SET_VREG_HIGH rIBASE, rINST # v[AA+1] <- rIBASE
movl LOCAL0(%esp), rIBASE
- SET_VREG %eax rINST # v[AA+0] <- eax
+ SET_VREG %eax, rINST # v[AA+0] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -4537,9 +4553,9 @@
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
movl rIBASE, LOCAL0(%esp)
- GET_VREG_HIGH rIBASE %eax # rIBASE <- v[BB+1]
- GET_VREG %ecx %ecx # ecx <- vCC
- GET_VREG %eax %eax # eax <- v[BB+0]
+ GET_VREG_HIGH rIBASE, %eax # rIBASE <- v[BB+1]
+ GET_VREG %ecx, %ecx # ecx <- vCC
+ GET_VREG %eax, %eax # eax <- v[BB+0]
shrdl rIBASE, %eax
shrl %cl, rIBASE
testb $32, %cl
@@ -4547,9 +4563,9 @@
movl rIBASE, %eax
xorl rIBASE, rIBASE
2:
- SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE
+ SET_VREG_HIGH rIBASE, rINST # v[AA+1] <- rIBASE
movl LOCAL0(%esp), rIBASE
- SET_VREG %eax rINST # v[BB+0] <- eax
+ SET_VREG %eax, rINST # v[BB+0] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -4728,7 +4744,7 @@
/* binop/2addr vA, vB */
movzx rINSTbl, %ecx # ecx <- A+
sarl $4, rINST # rINST <- B
- GET_VREG %eax rINST # eax <- vB
+ GET_VREG %eax, rINST # eax <- vB
andb $0xf, %cl # ecx <- A
addl %eax, (rFP,%ecx,4) # for ex: addl %eax,(rFP,%ecx,4)
CLEAR_REF %ecx
@@ -4753,7 +4769,7 @@
/* binop/2addr vA, vB */
movzx rINSTbl, %ecx # ecx <- A+
sarl $4, rINST # rINST <- B
- GET_VREG %eax rINST # eax <- vB
+ GET_VREG %eax, rINST # eax <- vB
andb $0xf, %cl # ecx <- A
subl %eax, (rFP,%ecx,4) # for ex: addl %eax,(rFP,%ecx,4)
CLEAR_REF %ecx
@@ -4767,12 +4783,12 @@
/* mul vA, vB */
movzx rINSTbl, %ecx # ecx <- A+
sarl $4, rINST # rINST <- B
- GET_VREG %eax rINST # eax <- vB
+ GET_VREG %eax, rINST # eax <- vB
andb $0xf, %cl # ecx <- A
mov rIBASE, LOCAL0(%esp)
imull (rFP,%ecx,4), %eax # trashes rIBASE/edx
mov LOCAL0(%esp), rIBASE
- SET_VREG %eax %ecx
+ SET_VREG %eax, %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
/* ------------------------------ */
@@ -4788,9 +4804,9 @@
movzx rINSTbl, %ecx # eax <- BA
mov rIBASE, LOCAL0(%esp)
sarl $4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # eax <- vBB
+ GET_VREG %ecx, %ecx # eax <- vBB
andb $0xf, rINSTbl # rINST <- A
- GET_VREG %eax rINST # eax <- vBB
+ GET_VREG %eax, rINST # eax <- vBB
testl %ecx, %ecx
je common_errDivideByZero
cmpl $-1, %ecx
@@ -4798,14 +4814,14 @@
cmpl $0x80000000, %eax
jne .Lop_div_int_2addr_continue_div2addr
movl $0x80000000, %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
mov LOCAL0(%esp), rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
.Lop_div_int_2addr_continue_div2addr:
cltd
idivl %ecx
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
mov LOCAL0(%esp), rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -4823,9 +4839,9 @@
movzx rINSTbl, %ecx # eax <- BA
mov rIBASE, LOCAL0(%esp)
sarl $4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # eax <- vBB
+ GET_VREG %ecx, %ecx # eax <- vBB
andb $0xf, rINSTbl # rINST <- A
- GET_VREG %eax rINST # eax <- vBB
+ GET_VREG %eax, rINST # eax <- vBB
testl %ecx, %ecx
je common_errDivideByZero
cmpl $-1, %ecx
@@ -4833,14 +4849,14 @@
cmpl $0x80000000, %eax
jne .Lop_rem_int_2addr_continue_div2addr
movl $0, rIBASE
- SET_VREG rIBASE rINST
+ SET_VREG rIBASE, rINST
mov LOCAL0(%esp), rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
.Lop_rem_int_2addr_continue_div2addr:
cltd
idivl %ecx
- SET_VREG rIBASE rINST
+ SET_VREG rIBASE, rINST
mov LOCAL0(%esp), rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -4863,7 +4879,7 @@
/* binop/2addr vA, vB */
movzx rINSTbl, %ecx # ecx <- A+
sarl $4, rINST # rINST <- B
- GET_VREG %eax rINST # eax <- vB
+ GET_VREG %eax, rINST # eax <- vB
andb $0xf, %cl # ecx <- A
andl %eax, (rFP,%ecx,4) # for ex: addl %eax,(rFP,%ecx,4)
CLEAR_REF %ecx
@@ -4888,7 +4904,7 @@
/* binop/2addr vA, vB */
movzx rINSTbl, %ecx # ecx <- A+
sarl $4, rINST # rINST <- B
- GET_VREG %eax rINST # eax <- vB
+ GET_VREG %eax, rINST # eax <- vB
andb $0xf, %cl # ecx <- A
orl %eax, (rFP,%ecx,4) # for ex: addl %eax,(rFP,%ecx,4)
CLEAR_REF %ecx
@@ -4913,7 +4929,7 @@
/* binop/2addr vA, vB */
movzx rINSTbl, %ecx # ecx <- A+
sarl $4, rINST # rINST <- B
- GET_VREG %eax rINST # eax <- vB
+ GET_VREG %eax, rINST # eax <- vB
andb $0xf, %cl # ecx <- A
xorl %eax, (rFP,%ecx,4) # for ex: addl %eax,(rFP,%ecx,4)
CLEAR_REF %ecx
@@ -4931,11 +4947,11 @@
/* shift/2addr vA, vB */
movzx rINSTbl, %ecx # eax <- BA
sarl $4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # eax <- vBB
+ GET_VREG %ecx, %ecx # eax <- vBB
andb $0xf, rINSTbl # rINST <- A
- GET_VREG %eax rINST # eax <- vAA
+ GET_VREG %eax, rINST # eax <- vAA
sall %cl, %eax # ex: sarl %cl, %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -4950,11 +4966,11 @@
/* shift/2addr vA, vB */
movzx rINSTbl, %ecx # eax <- BA
sarl $4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # eax <- vBB
+ GET_VREG %ecx, %ecx # eax <- vBB
andb $0xf, rINSTbl # rINST <- A
- GET_VREG %eax rINST # eax <- vAA
+ GET_VREG %eax, rINST # eax <- vAA
sarl %cl, %eax # ex: sarl %cl, %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -4969,11 +4985,11 @@
/* shift/2addr vA, vB */
movzx rINSTbl, %ecx # eax <- BA
sarl $4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # eax <- vBB
+ GET_VREG %ecx, %ecx # eax <- vBB
andb $0xf, rINSTbl # rINST <- A
- GET_VREG %eax rINST # eax <- vAA
+ GET_VREG %eax, rINST # eax <- vAA
shrl %cl, %eax # ex: sarl %cl, %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -4986,11 +5002,11 @@
* Generic 64-bit binary operation.
*/
/* binop/2addr vA, vB */
- movzbl rINSTbl,%ecx # ecx<- BA
- sarl $4,%ecx # ecx<- B
- GET_VREG %eax %ecx # eax<- v[B+0]
- GET_VREG_HIGH %ecx %ecx # eax<- v[B+1]
- andb $0xF,rINSTbl # rINST<- A
+ movzbl rINSTbl, %ecx # ecx<- BA
+ sarl $4, %ecx # ecx<- B
+ GET_VREG %eax, %ecx # eax<- v[B+0]
+ GET_VREG_HIGH %ecx, %ecx # eax<- v[B+1]
+ andb $0xF, rINSTbl # rINST<- A
addl %eax, (rFP,rINST,4) # ex: addl %eax,(rFP,rINST,4)
adcl %ecx, 4(rFP,rINST,4) # ex: adcl %ecx,4(rFP,rINST,4)
CLEAR_WIDE_REF rINST
@@ -5006,11 +5022,11 @@
* Generic 64-bit binary operation.
*/
/* binop/2addr vA, vB */
- movzbl rINSTbl,%ecx # ecx<- BA
- sarl $4,%ecx # ecx<- B
- GET_VREG %eax %ecx # eax<- v[B+0]
- GET_VREG_HIGH %ecx %ecx # eax<- v[B+1]
- andb $0xF,rINSTbl # rINST<- A
+ movzbl rINSTbl, %ecx # ecx<- BA
+ sarl $4, %ecx # ecx<- B
+ GET_VREG %eax, %ecx # eax<- v[B+0]
+ GET_VREG_HIGH %ecx, %ecx # eax<- v[B+1]
+ andb $0xF, rINSTbl # rINST<- A
subl %eax, (rFP,rINST,4) # ex: addl %eax,(rFP,rINST,4)
sbbl %ecx, 4(rFP,rINST,4) # ex: adcl %ecx,4(rFP,rINST,4)
CLEAR_WIDE_REF rINST
@@ -5072,17 +5088,17 @@
andb $0xf, rINSTbl # rINST <- A
mov rINST, LOCAL1(%esp) # save rINST/%ebx
movl %ebx, %ecx
- GET_VREG %edx %eax
- GET_VREG_HIGH %ebx %eax
+ GET_VREG %edx, %eax
+ GET_VREG_HIGH %ebx, %eax
movl %edx, %eax
orl %ebx, %eax
jz common_errDivideByZero
- GET_VREG %eax %ecx
- GET_VREG_HIGH %ecx %ecx
- call art_quick_ldiv
+ GET_VREG %eax, %ecx
+ GET_VREG_HIGH %ecx, %ecx
+ call SYMBOL(art_quick_ldiv)
mov LOCAL1(%esp), rINST # restore rINST/%ebx
- SET_VREG_HIGH rIBASE rINST
- SET_VREG %eax rINST
+ SET_VREG_HIGH rIBASE, rINST
+ SET_VREG %eax, rINST
mov LOCAL0(%esp), rIBASE # restore rIBASE/%edx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -5102,17 +5118,17 @@
andb $0xf, rINSTbl # rINST <- A
mov rINST, LOCAL1(%esp) # save rINST/%ebx
movl %ebx, %ecx
- GET_VREG %edx %eax
- GET_VREG_HIGH %ebx %eax
+ GET_VREG %edx, %eax
+ GET_VREG_HIGH %ebx, %eax
movl %edx, %eax
orl %ebx, %eax
jz common_errDivideByZero
- GET_VREG %eax %ecx
- GET_VREG_HIGH %ecx %ecx
- call art_quick_lmod
+ GET_VREG %eax, %ecx
+ GET_VREG_HIGH %ecx, %ecx
+ call SYMBOL(art_quick_lmod)
mov LOCAL1(%esp), rINST # restore rINST/%ebx
- SET_VREG_HIGH rIBASE rINST
- SET_VREG %eax rINST
+ SET_VREG_HIGH rIBASE, rINST
+ SET_VREG %eax, rINST
mov LOCAL0(%esp), rIBASE # restore rIBASE/%edx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -5126,11 +5142,11 @@
* Generic 64-bit binary operation.
*/
/* binop/2addr vA, vB */
- movzbl rINSTbl,%ecx # ecx<- BA
- sarl $4,%ecx # ecx<- B
- GET_VREG %eax %ecx # eax<- v[B+0]
- GET_VREG_HIGH %ecx %ecx # eax<- v[B+1]
- andb $0xF,rINSTbl # rINST<- A
+ movzbl rINSTbl, %ecx # ecx<- BA
+ sarl $4, %ecx # ecx<- B
+ GET_VREG %eax, %ecx # eax<- v[B+0]
+ GET_VREG_HIGH %ecx, %ecx # eax<- v[B+1]
+ andb $0xF, rINSTbl # rINST<- A
andl %eax, (rFP,rINST,4) # ex: addl %eax,(rFP,rINST,4)
andl %ecx, 4(rFP,rINST,4) # ex: adcl %ecx,4(rFP,rINST,4)
CLEAR_WIDE_REF rINST
@@ -5146,11 +5162,11 @@
* Generic 64-bit binary operation.
*/
/* binop/2addr vA, vB */
- movzbl rINSTbl,%ecx # ecx<- BA
- sarl $4,%ecx # ecx<- B
- GET_VREG %eax %ecx # eax<- v[B+0]
- GET_VREG_HIGH %ecx %ecx # eax<- v[B+1]
- andb $0xF,rINSTbl # rINST<- A
+ movzbl rINSTbl, %ecx # ecx<- BA
+ sarl $4, %ecx # ecx<- B
+ GET_VREG %eax, %ecx # eax<- v[B+0]
+ GET_VREG_HIGH %ecx, %ecx # eax<- v[B+1]
+ andb $0xF, rINSTbl # rINST<- A
orl %eax, (rFP,rINST,4) # ex: addl %eax,(rFP,rINST,4)
orl %ecx, 4(rFP,rINST,4) # ex: adcl %ecx,4(rFP,rINST,4)
CLEAR_WIDE_REF rINST
@@ -5166,11 +5182,11 @@
* Generic 64-bit binary operation.
*/
/* binop/2addr vA, vB */
- movzbl rINSTbl,%ecx # ecx<- BA
- sarl $4,%ecx # ecx<- B
- GET_VREG %eax %ecx # eax<- v[B+0]
- GET_VREG_HIGH %ecx %ecx # eax<- v[B+1]
- andb $0xF,rINSTbl # rINST<- A
+ movzbl rINSTbl, %ecx # ecx<- BA
+ sarl $4, %ecx # ecx<- B
+ GET_VREG %eax, %ecx # eax<- v[B+0]
+ GET_VREG_HIGH %ecx, %ecx # eax<- v[B+1]
+ andb $0xF, rINSTbl # rINST<- A
xorl %eax, (rFP,rINST,4) # ex: addl %eax,(rFP,rINST,4)
xorl %ecx, 4(rFP,rINST,4) # ex: adcl %ecx,4(rFP,rINST,4)
CLEAR_WIDE_REF rINST
@@ -5191,11 +5207,11 @@
/* rINSTw gets AA */
movzbl rINSTbl, %ecx # ecx <- BA
andb $0xf, rINSTbl # rINST <- A
- GET_VREG %eax rINST # eax <- v[AA+0]
+ GET_VREG %eax, rINST # eax <- v[AA+0]
sarl $4, %ecx # ecx <- B
movl rIBASE, LOCAL0(%esp)
- GET_VREG_HIGH rIBASE rINST # rIBASE <- v[AA+1]
- GET_VREG %ecx %ecx # ecx <- vBB
+ GET_VREG_HIGH rIBASE, rINST # rIBASE <- v[AA+1]
+ GET_VREG %ecx, %ecx # ecx <- vBB
shldl %eax, rIBASE
sall %cl, %eax
testb $32, %cl
@@ -5203,9 +5219,9 @@
movl %eax, rIBASE
xorl %eax, %eax
2:
- SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE
+ SET_VREG_HIGH rIBASE, rINST # v[AA+1] <- rIBASE
movl LOCAL0(%esp), rIBASE
- SET_VREG %eax rINST # v[AA+0] <- eax
+ SET_VREG %eax, rINST # v[AA+0] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
/* ------------------------------ */
@@ -5222,11 +5238,11 @@
/* rINSTw gets AA */
movzbl rINSTbl, %ecx # ecx <- BA
andb $0xf, rINSTbl # rINST <- A
- GET_VREG %eax rINST # eax <- v[AA+0]
+ GET_VREG %eax, rINST # eax <- v[AA+0]
sarl $4, %ecx # ecx <- B
movl rIBASE, LOCAL0(%esp)
- GET_VREG_HIGH rIBASE rINST # rIBASE <- v[AA+1]
- GET_VREG %ecx %ecx # ecx <- vBB
+ GET_VREG_HIGH rIBASE, rINST # rIBASE <- v[AA+1]
+ GET_VREG %ecx, %ecx # ecx <- vBB
shrdl rIBASE, %eax
sarl %cl, rIBASE
testb $32, %cl
@@ -5234,9 +5250,9 @@
movl rIBASE, %eax
sarl $31, rIBASE
2:
- SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE
+ SET_VREG_HIGH rIBASE, rINST # v[AA+1] <- rIBASE
movl LOCAL0(%esp), rIBASE
- SET_VREG %eax rINST # v[AA+0] <- eax
+ SET_VREG %eax, rINST # v[AA+0] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
/* ------------------------------ */
@@ -5253,11 +5269,11 @@
/* rINSTw gets AA */
movzbl rINSTbl, %ecx # ecx <- BA
andb $0xf, rINSTbl # rINST <- A
- GET_VREG %eax rINST # eax <- v[AA+0]
+ GET_VREG %eax, rINST # eax <- v[AA+0]
sarl $4, %ecx # ecx <- B
movl rIBASE, LOCAL0(%esp)
- GET_VREG_HIGH rIBASE rINST # rIBASE <- v[AA+1]
- GET_VREG %ecx %ecx # ecx <- vBB
+ GET_VREG_HIGH rIBASE, rINST # rIBASE <- v[AA+1]
+ GET_VREG %ecx, %ecx # ecx <- vBB
shrdl rIBASE, %eax
shrl %cl, rIBASE
testb $32, %cl
@@ -5265,9 +5281,9 @@
movl rIBASE, %eax
xorl rIBASE, rIBASE
2:
- SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE
+ SET_VREG_HIGH rIBASE, rINST # v[AA+1] <- rIBASE
movl LOCAL0(%esp), rIBASE
- SET_VREG %eax rINST # v[AA+0] <- eax
+ SET_VREG %eax, rINST # v[AA+0] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
/* ------------------------------ */
@@ -5455,11 +5471,11 @@
/* binop/lit16 vA, vB, #+CCCC */
movzbl rINSTbl, %eax # eax <- 000000BA
sarl $4, %eax # eax <- B
- GET_VREG %eax %eax # eax <- vB
+ GET_VREG %eax, %eax # eax <- vB
movswl 2(rPC), %ecx # ecx <- ssssCCCC
andb $0xf, rINSTbl # rINST <- A
addl %ecx, %eax # for example: addl %ecx, %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -5481,11 +5497,11 @@
/* binop/lit16 vA, vB, #+CCCC */
movzbl rINSTbl, %eax # eax <- 000000BA
sarl $4, %eax # eax <- B
- GET_VREG %eax %eax # eax <- vB
+ GET_VREG %eax, %eax # eax <- vB
movswl 2(rPC), %ecx # ecx <- ssssCCCC
andb $0xf, rINSTbl # rINST <- A
subl %eax, %ecx # for example: addl %ecx, %eax
- SET_VREG %ecx rINST
+ SET_VREG %ecx, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -5497,13 +5513,13 @@
/* Need A in rINST, ssssCCCC in ecx, vB in eax */
movzbl rINSTbl, %eax # eax <- 000000BA
sarl $4, %eax # eax <- B
- GET_VREG %eax %eax # eax <- vB
+ GET_VREG %eax, %eax # eax <- vB
movswl 2(rPC), %ecx # ecx <- ssssCCCC
andb $0xf, rINSTbl # rINST <- A
mov rIBASE, LOCAL0(%esp)
imull %ecx, %eax # trashes rIBASE/edx
mov LOCAL0(%esp), rIBASE
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -5519,7 +5535,7 @@
/* Need A in rINST, ssssCCCC in ecx, vB in eax */
movzbl rINSTbl, %eax # eax <- 000000BA
sarl $4, %eax # eax <- B
- GET_VREG %eax %eax # eax <- vB
+ GET_VREG %eax, %eax # eax <- vB
movswl 2(rPC), %ecx # ecx <- ssssCCCC
andb $0xf, rINSTbl # rINST <- A
testl %ecx, %ecx
@@ -5529,14 +5545,14 @@
cmpl $0x80000000, %eax
jne .Lop_div_int_lit16_continue_div
movl $0x80000000, %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
.Lop_div_int_lit16_continue_div:
mov rIBASE, LOCAL0(%esp)
cltd
idivl %ecx
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
mov LOCAL0(%esp), rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -5554,7 +5570,7 @@
/* Need A in rINST, ssssCCCC in ecx, vB in eax */
movzbl rINSTbl, %eax # eax <- 000000BA
sarl $4, %eax # eax <- B
- GET_VREG %eax %eax # eax <- vB
+ GET_VREG %eax, %eax # eax <- vB
movswl 2(rPC), %ecx # ecx <- ssssCCCC
andb $0xf, rINSTbl # rINST <- A
testl %ecx, %ecx
@@ -5564,14 +5580,14 @@
cmpl $0x80000000, %eax
jne .Lop_rem_int_lit16_continue_div
movl $0, %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
.Lop_rem_int_lit16_continue_div:
mov rIBASE, LOCAL0(%esp)
cltd
idivl %ecx
- SET_VREG rIBASE rINST
+ SET_VREG rIBASE, rINST
mov LOCAL0(%esp), rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -5593,11 +5609,11 @@
/* binop/lit16 vA, vB, #+CCCC */
movzbl rINSTbl, %eax # eax <- 000000BA
sarl $4, %eax # eax <- B
- GET_VREG %eax %eax # eax <- vB
+ GET_VREG %eax, %eax # eax <- vB
movswl 2(rPC), %ecx # ecx <- ssssCCCC
andb $0xf, rINSTbl # rINST <- A
andl %ecx, %eax # for example: addl %ecx, %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -5618,11 +5634,11 @@
/* binop/lit16 vA, vB, #+CCCC */
movzbl rINSTbl, %eax # eax <- 000000BA
sarl $4, %eax # eax <- B
- GET_VREG %eax %eax # eax <- vB
+ GET_VREG %eax, %eax # eax <- vB
movswl 2(rPC), %ecx # ecx <- ssssCCCC
andb $0xf, rINSTbl # rINST <- A
orl %ecx, %eax # for example: addl %ecx, %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -5643,11 +5659,11 @@
/* binop/lit16 vA, vB, #+CCCC */
movzbl rINSTbl, %eax # eax <- 000000BA
sarl $4, %eax # eax <- B
- GET_VREG %eax %eax # eax <- vB
+ GET_VREG %eax, %eax # eax <- vB
movswl 2(rPC), %ecx # ecx <- ssssCCCC
andb $0xf, rINSTbl # rINST <- A
xorl %ecx, %eax # for example: addl %ecx, %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -5669,9 +5685,9 @@
/* binop/lit8 vAA, vBB, #+CC */
movzbl 2(rPC), %eax # eax <- BB
movsbl 3(rPC), %ecx # ecx <- ssssssCC
- GET_VREG %eax %eax # eax <- rBB
+ GET_VREG %eax, %eax # eax <- rBB
addl %ecx, %eax # ex: addl %ecx,%eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -5693,9 +5709,9 @@
/* binop/lit8 vAA, vBB, #+CC */
movzbl 2(rPC), %eax # eax <- BB
movsbl 3(rPC), %ecx # ecx <- ssssssCC
- GET_VREG %eax %eax # eax <- rBB
+ GET_VREG %eax, %eax # eax <- rBB
subl %eax, %ecx # ex: addl %ecx,%eax
- SET_VREG %ecx rINST
+ SET_VREG %ecx, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -5706,11 +5722,11 @@
/* mul/lit8 vAA, vBB, #+CC */
movzbl 2(rPC), %eax # eax <- BB
movsbl 3(rPC), %ecx # ecx <- ssssssCC
- GET_VREG %eax %eax # eax <- rBB
+ GET_VREG %eax, %eax # eax <- rBB
mov rIBASE, LOCAL0(%esp)
imull %ecx, %eax # trashes rIBASE/edx
mov LOCAL0(%esp), rIBASE
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -5725,7 +5741,7 @@
/* div/rem/lit8 vAA, vBB, #+CC */
movzbl 2(rPC), %eax # eax <- BB
movsbl 3(rPC), %ecx # ecx <- ssssssCC
- GET_VREG %eax %eax # eax <- rBB
+ GET_VREG %eax, %eax # eax <- rBB
testl %ecx, %ecx
je common_errDivideByZero
cmpl $0x80000000, %eax
@@ -5733,14 +5749,14 @@
cmpl $-1, %ecx
jne .Lop_div_int_lit8_continue_div
movl $0x80000000, %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
.Lop_div_int_lit8_continue_div:
mov rIBASE, LOCAL0(%esp)
cltd
idivl %ecx
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
mov LOCAL0(%esp), rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -5757,7 +5773,7 @@
/* div/rem/lit8 vAA, vBB, #+CC */
movzbl 2(rPC), %eax # eax <- BB
movsbl 3(rPC), %ecx # ecx <- ssssssCC
- GET_VREG %eax %eax # eax <- rBB
+ GET_VREG %eax, %eax # eax <- rBB
testl %ecx, %ecx
je common_errDivideByZero
cmpl $0x80000000, %eax
@@ -5765,14 +5781,14 @@
cmpl $-1, %ecx
jne .Lop_rem_int_lit8_continue_div
movl $0, %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
.Lop_rem_int_lit8_continue_div:
mov rIBASE, LOCAL0(%esp)
cltd
idivl %ecx
- SET_VREG rIBASE rINST
+ SET_VREG rIBASE, rINST
mov LOCAL0(%esp), rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -5795,9 +5811,9 @@
/* binop/lit8 vAA, vBB, #+CC */
movzbl 2(rPC), %eax # eax <- BB
movsbl 3(rPC), %ecx # ecx <- ssssssCC
- GET_VREG %eax %eax # eax <- rBB
+ GET_VREG %eax, %eax # eax <- rBB
andl %ecx, %eax # ex: addl %ecx,%eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -5819,9 +5835,9 @@
/* binop/lit8 vAA, vBB, #+CC */
movzbl 2(rPC), %eax # eax <- BB
movsbl 3(rPC), %ecx # ecx <- ssssssCC
- GET_VREG %eax %eax # eax <- rBB
+ GET_VREG %eax, %eax # eax <- rBB
orl %ecx, %eax # ex: addl %ecx,%eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -5843,9 +5859,9 @@
/* binop/lit8 vAA, vBB, #+CC */
movzbl 2(rPC), %eax # eax <- BB
movsbl 3(rPC), %ecx # ecx <- ssssssCC
- GET_VREG %eax %eax # eax <- rBB
+ GET_VREG %eax, %eax # eax <- rBB
xorl %ecx, %eax # ex: addl %ecx,%eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -5867,9 +5883,9 @@
/* binop/lit8 vAA, vBB, #+CC */
movzbl 2(rPC), %eax # eax <- BB
movsbl 3(rPC), %ecx # ecx <- ssssssCC
- GET_VREG %eax %eax # eax <- rBB
+ GET_VREG %eax, %eax # eax <- rBB
sall %cl, %eax # ex: addl %ecx,%eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -5891,9 +5907,9 @@
/* binop/lit8 vAA, vBB, #+CC */
movzbl 2(rPC), %eax # eax <- BB
movsbl 3(rPC), %ecx # ecx <- ssssssCC
- GET_VREG %eax %eax # eax <- rBB
+ GET_VREG %eax, %eax # eax <- rBB
sarl %cl, %eax # ex: addl %ecx,%eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -5915,9 +5931,9 @@
/* binop/lit8 vAA, vBB, #+CC */
movzbl 2(rPC), %eax # eax <- BB
movsbl 3(rPC), %ecx # ecx <- ssssssCC
- GET_VREG %eax %eax # eax <- rBB
+ GET_VREG %eax, %eax # eax <- rBB
shrl %cl, %eax # ex: addl %ecx,%eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -5929,13 +5945,13 @@
/* op vA, vB, offset@CCCC */
movzbl rINSTbl, %ecx # ecx <- BA
sarl $4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # vB (object we're operating on)
+ GET_VREG %ecx, %ecx # vB (object we're operating on)
movzwl 2(rPC), %eax # eax <- field byte offset
testl %ecx, %ecx # is object null?
je common_errNullObject
movl (%ecx,%eax,1), %eax
andb $0xf,rINSTbl # rINST <- A
- SET_VREG %eax rINST # fp[A] <- value
+ SET_VREG %eax, rINST # fp[A] <- value
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -5945,13 +5961,13 @@
/* iget-wide-quick vA, vB, offset@CCCC */
movzbl rINSTbl, %ecx # ecx <- BA
sarl $4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # vB (object we're operating on)
+ GET_VREG %ecx, %ecx # vB (object we're operating on)
movzwl 2(rPC), %eax # eax <- field byte offset
testl %ecx, %ecx # is object null?
je common_errNullObject
movq (%ecx,%eax,1), %xmm0
andb $0xf, rINSTbl # rINST <- A
- SET_WIDE_FP_VREG %xmm0 rINST
+ SET_WIDE_FP_VREG %xmm0, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -5962,18 +5978,18 @@
/* op vA, vB, offset@CCCC */
movzbl rINSTbl, %ecx # ecx <- BA
sarl $4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # vB (object we're operating on)
+ GET_VREG %ecx, %ecx # vB (object we're operating on)
movzwl 2(rPC), %eax # eax <- field byte offset
movl %ecx, OUT_ARG0(%esp)
movl %eax, OUT_ARG1(%esp)
EXPORT_PC
- call artIGetObjectFromMterp # (obj, offset)
+ call SYMBOL(artIGetObjectFromMterp) # (obj, offset)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $0xf,rINSTbl # rINST <- A
- SET_VREG_OBJECT %eax rINST # fp[A] <- value
+ SET_VREG_OBJECT %eax, rINST # fp[A] <- value
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -5984,11 +6000,11 @@
/* op vA, vB, offset@CCCC */
movzbl rINSTbl, %ecx # ecx <- BA
sarl $4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # vB (object we're operating on)
+ GET_VREG %ecx, %ecx # vB (object we're operating on)
testl %ecx, %ecx # is object null?
je common_errNullObject
andb $0xf, rINSTbl # rINST <- A
- GET_VREG rINST rINST # rINST <- v[A]
+ GET_VREG rINST, rINST # rINST <- v[A]
movzwl 2(rPC), %eax # eax <- field byte offset
movl rINST, (%ecx,%eax,1)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -6000,13 +6016,13 @@
/* iput-wide-quick vA, vB, offset@CCCC */
movzbl rINSTbl, %ecx # ecx<- BA
sarl $4, %ecx # ecx<- B
- GET_VREG %ecx %ecx # vB (object we're operating on)
+ GET_VREG %ecx, %ecx # vB (object we're operating on)
testl %ecx, %ecx # is object null?
je common_errNullObject
movzwl 2(rPC), %eax # eax<- field byte offset
leal (%ecx,%eax,1), %ecx # ecx<- Address of 64-bit target
andb $0xf, rINSTbl # rINST<- A
- GET_WIDE_FP_VREG %xmm0 rINST # xmm0<- fp[A]/fp[A+1]
+ GET_WIDE_FP_VREG %xmm0, rINST # xmm0<- fp[A]/fp[A+1]
movq %xmm0, (%ecx) # obj.field<- r0/r1
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -6020,7 +6036,7 @@
movl rPC, OUT_ARG1(%esp)
REFRESH_INST 232
movl rINST, OUT_ARG2(%esp)
- call MterpIputObjectQuick
+ call SYMBOL(MterpIputObjectQuick)
testl %eax, %eax
jz MterpException
REFRESH_IBASE
@@ -6045,7 +6061,7 @@
movl rPC, OUT_ARG2(%esp)
REFRESH_INST 233
movl rINST, OUT_ARG3(%esp)
- call MterpInvokeVirtualQuick
+ call SYMBOL(MterpInvokeVirtualQuick)
testl %eax, %eax
jz MterpException
REFRESH_IBASE
@@ -6071,7 +6087,7 @@
movl rPC, OUT_ARG2(%esp)
REFRESH_INST 234
movl rINST, OUT_ARG3(%esp)
- call MterpInvokeVirtualQuickRange
+ call SYMBOL(MterpInvokeVirtualQuickRange)
testl %eax, %eax
jz MterpException
REFRESH_IBASE
@@ -6087,11 +6103,11 @@
/* op vA, vB, offset@CCCC */
movzbl rINSTbl, %ecx # ecx <- BA
sarl $4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # vB (object we're operating on)
+ GET_VREG %ecx, %ecx # vB (object we're operating on)
testl %ecx, %ecx # is object null?
je common_errNullObject
andb $0xf, rINSTbl # rINST <- A
- GET_VREG rINST rINST # rINST <- v[A]
+ GET_VREG rINST, rINST # rINST <- v[A]
movzwl 2(rPC), %eax # eax <- field byte offset
movb rINSTbl, (%ecx,%eax,1)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -6106,11 +6122,11 @@
/* op vA, vB, offset@CCCC */
movzbl rINSTbl, %ecx # ecx <- BA
sarl $4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # vB (object we're operating on)
+ GET_VREG %ecx, %ecx # vB (object we're operating on)
testl %ecx, %ecx # is object null?
je common_errNullObject
andb $0xf, rINSTbl # rINST <- A
- GET_VREG rINST rINST # rINST <- v[A]
+ GET_VREG rINST, rINST # rINST <- v[A]
movzwl 2(rPC), %eax # eax <- field byte offset
movb rINSTbl, (%ecx,%eax,1)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -6125,11 +6141,11 @@
/* op vA, vB, offset@CCCC */
movzbl rINSTbl, %ecx # ecx <- BA
sarl $4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # vB (object we're operating on)
+ GET_VREG %ecx, %ecx # vB (object we're operating on)
testl %ecx, %ecx # is object null?
je common_errNullObject
andb $0xf, rINSTbl # rINST <- A
- GET_VREG rINST rINST # rINST <- v[A]
+ GET_VREG rINST, rINST # rINST <- v[A]
movzwl 2(rPC), %eax # eax <- field byte offset
movw rINSTw, (%ecx,%eax,1)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -6144,11 +6160,11 @@
/* op vA, vB, offset@CCCC */
movzbl rINSTbl, %ecx # ecx <- BA
sarl $4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # vB (object we're operating on)
+ GET_VREG %ecx, %ecx # vB (object we're operating on)
testl %ecx, %ecx # is object null?
je common_errNullObject
andb $0xf, rINSTbl # rINST <- A
- GET_VREG rINST rINST # rINST <- v[A]
+ GET_VREG rINST, rINST # rINST <- v[A]
movzwl 2(rPC), %eax # eax <- field byte offset
movw rINSTw, (%ecx,%eax,1)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -6163,13 +6179,13 @@
/* op vA, vB, offset@CCCC */
movzbl rINSTbl, %ecx # ecx <- BA
sarl $4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # vB (object we're operating on)
+ GET_VREG %ecx, %ecx # vB (object we're operating on)
movzwl 2(rPC), %eax # eax <- field byte offset
testl %ecx, %ecx # is object null?
je common_errNullObject
movsbl (%ecx,%eax,1), %eax
andb $0xf,rINSTbl # rINST <- A
- SET_VREG %eax rINST # fp[A] <- value
+ SET_VREG %eax, rINST # fp[A] <- value
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -6182,13 +6198,13 @@
/* op vA, vB, offset@CCCC */
movzbl rINSTbl, %ecx # ecx <- BA
sarl $4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # vB (object we're operating on)
+ GET_VREG %ecx, %ecx # vB (object we're operating on)
movzwl 2(rPC), %eax # eax <- field byte offset
testl %ecx, %ecx # is object null?
je common_errNullObject
movsbl (%ecx,%eax,1), %eax
andb $0xf,rINSTbl # rINST <- A
- SET_VREG %eax rINST # fp[A] <- value
+ SET_VREG %eax, rINST # fp[A] <- value
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -6201,13 +6217,13 @@
/* op vA, vB, offset@CCCC */
movzbl rINSTbl, %ecx # ecx <- BA
sarl $4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # vB (object we're operating on)
+ GET_VREG %ecx, %ecx # vB (object we're operating on)
movzwl 2(rPC), %eax # eax <- field byte offset
testl %ecx, %ecx # is object null?
je common_errNullObject
movzwl (%ecx,%eax,1), %eax
andb $0xf,rINSTbl # rINST <- A
- SET_VREG %eax rINST # fp[A] <- value
+ SET_VREG %eax, rINST # fp[A] <- value
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -6220,13 +6236,13 @@
/* op vA, vB, offset@CCCC */
movzbl rINSTbl, %ecx # ecx <- BA
sarl $4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # vB (object we're operating on)
+ GET_VREG %ecx, %ecx # vB (object we're operating on)
movzwl 2(rPC), %eax # eax <- field byte offset
testl %ecx, %ecx # is object null?
je common_errNullObject
movswl (%ecx,%eax,1), %eax
andb $0xf,rINSTbl # rINST <- A
- SET_VREG %eax rINST # fp[A] <- value
+ SET_VREG %eax, rINST # fp[A] <- value
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -6350,31 +6366,31 @@
.balign 128
- .size artMterpAsmInstructionStart, .-artMterpAsmInstructionStart
- .global artMterpAsmInstructionEnd
-artMterpAsmInstructionEnd:
+ SIZE(SYMBOL(artMterpAsmInstructionStart),SYMBOL(artMterpAsmInstructionStart))
+ .global SYMBOL(artMterpAsmInstructionEnd)
+SYMBOL(artMterpAsmInstructionEnd):
/*
* ===========================================================================
* Sister implementations
* ===========================================================================
*/
- .global artMterpAsmSisterStart
- .type artMterpAsmSisterStart, %function
+ .global SYMBOL(artMterpAsmSisterStart)
+ FUNCTION_TYPE(SYMBOL(artMterpAsmSisterStart))
.text
.balign 4
-artMterpAsmSisterStart:
+SYMBOL(artMterpAsmSisterStart):
- .size artMterpAsmSisterStart, .-artMterpAsmSisterStart
- .global artMterpAsmSisterEnd
-artMterpAsmSisterEnd:
+ SIZE(SYMBOL(artMterpAsmSisterStart),SYMBOL(artMterpAsmSisterStart))
+ .global SYMBOL(artMterpAsmSisterEnd)
+SYMBOL(artMterpAsmSisterEnd):
- .global artMterpAsmAltInstructionStart
- .type artMterpAsmAltInstructionStart, %function
+ .global SYMBOL(artMterpAsmAltInstructionStart)
+ FUNCTION_TYPE(SYMBOL(artMterpAsmAltInstructionStart))
.text
-artMterpAsmAltInstructionStart = .L_ALT_op_nop
+SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop
/* ------------------------------ */
.balign 128
.L_ALT_op_nop: /* 0x00 */
@@ -6396,7 +6412,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(0*128)
@@ -6421,7 +6437,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(1*128)
@@ -6446,7 +6462,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(2*128)
@@ -6471,7 +6487,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(3*128)
@@ -6496,7 +6512,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(4*128)
@@ -6521,7 +6537,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(5*128)
@@ -6546,7 +6562,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(6*128)
@@ -6571,7 +6587,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(7*128)
@@ -6596,7 +6612,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(8*128)
@@ -6621,7 +6637,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(9*128)
@@ -6646,7 +6662,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(10*128)
@@ -6671,7 +6687,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(11*128)
@@ -6696,7 +6712,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(12*128)
@@ -6721,7 +6737,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(13*128)
@@ -6746,7 +6762,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(14*128)
@@ -6771,7 +6787,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(15*128)
@@ -6796,7 +6812,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(16*128)
@@ -6821,7 +6837,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(17*128)
@@ -6846,7 +6862,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(18*128)
@@ -6871,7 +6887,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(19*128)
@@ -6896,7 +6912,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(20*128)
@@ -6921,7 +6937,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(21*128)
@@ -6946,7 +6962,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(22*128)
@@ -6971,7 +6987,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(23*128)
@@ -6996,7 +7012,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(24*128)
@@ -7021,7 +7037,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(25*128)
@@ -7046,7 +7062,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(26*128)
@@ -7071,7 +7087,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(27*128)
@@ -7096,7 +7112,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(28*128)
@@ -7121,7 +7137,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(29*128)
@@ -7146,7 +7162,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(30*128)
@@ -7171,7 +7187,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(31*128)
@@ -7196,7 +7212,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(32*128)
@@ -7221,7 +7237,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(33*128)
@@ -7246,7 +7262,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(34*128)
@@ -7271,7 +7287,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(35*128)
@@ -7296,7 +7312,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(36*128)
@@ -7321,7 +7337,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(37*128)
@@ -7346,7 +7362,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(38*128)
@@ -7371,7 +7387,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(39*128)
@@ -7396,7 +7412,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(40*128)
@@ -7421,7 +7437,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(41*128)
@@ -7446,7 +7462,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(42*128)
@@ -7471,7 +7487,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(43*128)
@@ -7496,7 +7512,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(44*128)
@@ -7521,7 +7537,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(45*128)
@@ -7546,7 +7562,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(46*128)
@@ -7571,7 +7587,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(47*128)
@@ -7596,7 +7612,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(48*128)
@@ -7621,7 +7637,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(49*128)
@@ -7646,7 +7662,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(50*128)
@@ -7671,7 +7687,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(51*128)
@@ -7696,7 +7712,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(52*128)
@@ -7721,7 +7737,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(53*128)
@@ -7746,7 +7762,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(54*128)
@@ -7771,7 +7787,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(55*128)
@@ -7796,7 +7812,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(56*128)
@@ -7821,7 +7837,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(57*128)
@@ -7846,7 +7862,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(58*128)
@@ -7871,7 +7887,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(59*128)
@@ -7896,7 +7912,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(60*128)
@@ -7921,7 +7937,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(61*128)
@@ -7946,7 +7962,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(62*128)
@@ -7971,7 +7987,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(63*128)
@@ -7996,7 +8012,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(64*128)
@@ -8021,7 +8037,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(65*128)
@@ -8046,7 +8062,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(66*128)
@@ -8071,7 +8087,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(67*128)
@@ -8096,7 +8112,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(68*128)
@@ -8121,7 +8137,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(69*128)
@@ -8146,7 +8162,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(70*128)
@@ -8171,7 +8187,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(71*128)
@@ -8196,7 +8212,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(72*128)
@@ -8221,7 +8237,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(73*128)
@@ -8246,7 +8262,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(74*128)
@@ -8271,7 +8287,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(75*128)
@@ -8296,7 +8312,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(76*128)
@@ -8321,7 +8337,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(77*128)
@@ -8346,7 +8362,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(78*128)
@@ -8371,7 +8387,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(79*128)
@@ -8396,7 +8412,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(80*128)
@@ -8421,7 +8437,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(81*128)
@@ -8446,7 +8462,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(82*128)
@@ -8471,7 +8487,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(83*128)
@@ -8496,7 +8512,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(84*128)
@@ -8521,7 +8537,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(85*128)
@@ -8546,7 +8562,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(86*128)
@@ -8571,7 +8587,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(87*128)
@@ -8596,7 +8612,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(88*128)
@@ -8621,7 +8637,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(89*128)
@@ -8646,7 +8662,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(90*128)
@@ -8671,7 +8687,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(91*128)
@@ -8696,7 +8712,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(92*128)
@@ -8721,7 +8737,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(93*128)
@@ -8746,7 +8762,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(94*128)
@@ -8771,7 +8787,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(95*128)
@@ -8796,7 +8812,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(96*128)
@@ -8821,7 +8837,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(97*128)
@@ -8846,7 +8862,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(98*128)
@@ -8871,7 +8887,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(99*128)
@@ -8896,7 +8912,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(100*128)
@@ -8921,7 +8937,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(101*128)
@@ -8946,7 +8962,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(102*128)
@@ -8971,7 +8987,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(103*128)
@@ -8996,7 +9012,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(104*128)
@@ -9021,7 +9037,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(105*128)
@@ -9046,7 +9062,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(106*128)
@@ -9071,7 +9087,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(107*128)
@@ -9096,7 +9112,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(108*128)
@@ -9121,7 +9137,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(109*128)
@@ -9146,7 +9162,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(110*128)
@@ -9171,7 +9187,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(111*128)
@@ -9196,7 +9212,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(112*128)
@@ -9221,7 +9237,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(113*128)
@@ -9246,7 +9262,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(114*128)
@@ -9271,7 +9287,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(115*128)
@@ -9296,7 +9312,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(116*128)
@@ -9321,7 +9337,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(117*128)
@@ -9346,7 +9362,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(118*128)
@@ -9371,7 +9387,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(119*128)
@@ -9396,7 +9412,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(120*128)
@@ -9421,7 +9437,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(121*128)
@@ -9446,7 +9462,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(122*128)
@@ -9471,7 +9487,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(123*128)
@@ -9496,7 +9512,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(124*128)
@@ -9521,7 +9537,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(125*128)
@@ -9546,7 +9562,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(126*128)
@@ -9571,7 +9587,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(127*128)
@@ -9596,7 +9612,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(128*128)
@@ -9621,7 +9637,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(129*128)
@@ -9646,7 +9662,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(130*128)
@@ -9671,7 +9687,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(131*128)
@@ -9696,7 +9712,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(132*128)
@@ -9721,7 +9737,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(133*128)
@@ -9746,7 +9762,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(134*128)
@@ -9771,7 +9787,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(135*128)
@@ -9796,7 +9812,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(136*128)
@@ -9821,7 +9837,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(137*128)
@@ -9846,7 +9862,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(138*128)
@@ -9871,7 +9887,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(139*128)
@@ -9896,7 +9912,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(140*128)
@@ -9921,7 +9937,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(141*128)
@@ -9946,7 +9962,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(142*128)
@@ -9971,7 +9987,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(143*128)
@@ -9996,7 +10012,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(144*128)
@@ -10021,7 +10037,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(145*128)
@@ -10046,7 +10062,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(146*128)
@@ -10071,7 +10087,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(147*128)
@@ -10096,7 +10112,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(148*128)
@@ -10121,7 +10137,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(149*128)
@@ -10146,7 +10162,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(150*128)
@@ -10171,7 +10187,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(151*128)
@@ -10196,7 +10212,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(152*128)
@@ -10221,7 +10237,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(153*128)
@@ -10246,7 +10262,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(154*128)
@@ -10271,7 +10287,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(155*128)
@@ -10296,7 +10312,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(156*128)
@@ -10321,7 +10337,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(157*128)
@@ -10346,7 +10362,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(158*128)
@@ -10371,7 +10387,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(159*128)
@@ -10396,7 +10412,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(160*128)
@@ -10421,7 +10437,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(161*128)
@@ -10446,7 +10462,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(162*128)
@@ -10471,7 +10487,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(163*128)
@@ -10496,7 +10512,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(164*128)
@@ -10521,7 +10537,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(165*128)
@@ -10546,7 +10562,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(166*128)
@@ -10571,7 +10587,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(167*128)
@@ -10596,7 +10612,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(168*128)
@@ -10621,7 +10637,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(169*128)
@@ -10646,7 +10662,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(170*128)
@@ -10671,7 +10687,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(171*128)
@@ -10696,7 +10712,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(172*128)
@@ -10721,7 +10737,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(173*128)
@@ -10746,7 +10762,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(174*128)
@@ -10771,7 +10787,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(175*128)
@@ -10796,7 +10812,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(176*128)
@@ -10821,7 +10837,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(177*128)
@@ -10846,7 +10862,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(178*128)
@@ -10871,7 +10887,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(179*128)
@@ -10896,7 +10912,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(180*128)
@@ -10921,7 +10937,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(181*128)
@@ -10946,7 +10962,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(182*128)
@@ -10971,7 +10987,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(183*128)
@@ -10996,7 +11012,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(184*128)
@@ -11021,7 +11037,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(185*128)
@@ -11046,7 +11062,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(186*128)
@@ -11071,7 +11087,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(187*128)
@@ -11096,7 +11112,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(188*128)
@@ -11121,7 +11137,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(189*128)
@@ -11146,7 +11162,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(190*128)
@@ -11171,7 +11187,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(191*128)
@@ -11196,7 +11212,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(192*128)
@@ -11221,7 +11237,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(193*128)
@@ -11246,7 +11262,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(194*128)
@@ -11271,7 +11287,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(195*128)
@@ -11296,7 +11312,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(196*128)
@@ -11321,7 +11337,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(197*128)
@@ -11346,7 +11362,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(198*128)
@@ -11371,7 +11387,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(199*128)
@@ -11396,7 +11412,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(200*128)
@@ -11421,7 +11437,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(201*128)
@@ -11446,7 +11462,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(202*128)
@@ -11471,7 +11487,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(203*128)
@@ -11496,7 +11512,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(204*128)
@@ -11521,7 +11537,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(205*128)
@@ -11546,7 +11562,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(206*128)
@@ -11571,7 +11587,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(207*128)
@@ -11596,7 +11612,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(208*128)
@@ -11621,7 +11637,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(209*128)
@@ -11646,7 +11662,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(210*128)
@@ -11671,7 +11687,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(211*128)
@@ -11696,7 +11712,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(212*128)
@@ -11721,7 +11737,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(213*128)
@@ -11746,7 +11762,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(214*128)
@@ -11771,7 +11787,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(215*128)
@@ -11796,7 +11812,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(216*128)
@@ -11821,7 +11837,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(217*128)
@@ -11846,7 +11862,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(218*128)
@@ -11871,7 +11887,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(219*128)
@@ -11896,7 +11912,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(220*128)
@@ -11921,7 +11937,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(221*128)
@@ -11946,7 +11962,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(222*128)
@@ -11971,7 +11987,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(223*128)
@@ -11996,7 +12012,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(224*128)
@@ -12021,7 +12037,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(225*128)
@@ -12046,7 +12062,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(226*128)
@@ -12071,7 +12087,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(227*128)
@@ -12096,7 +12112,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(228*128)
@@ -12121,7 +12137,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(229*128)
@@ -12146,7 +12162,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(230*128)
@@ -12171,7 +12187,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(231*128)
@@ -12196,7 +12212,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(232*128)
@@ -12221,7 +12237,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(233*128)
@@ -12246,7 +12262,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(234*128)
@@ -12271,7 +12287,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(235*128)
@@ -12296,7 +12312,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(236*128)
@@ -12321,7 +12337,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(237*128)
@@ -12346,7 +12362,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(238*128)
@@ -12371,7 +12387,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(239*128)
@@ -12396,7 +12412,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(240*128)
@@ -12421,7 +12437,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(241*128)
@@ -12446,7 +12462,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(242*128)
@@ -12471,7 +12487,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(243*128)
@@ -12496,7 +12512,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(244*128)
@@ -12521,7 +12537,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(245*128)
@@ -12546,7 +12562,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(246*128)
@@ -12571,7 +12587,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(247*128)
@@ -12596,7 +12612,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(248*128)
@@ -12621,7 +12637,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(249*128)
@@ -12646,7 +12662,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(250*128)
@@ -12671,7 +12687,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(251*128)
@@ -12696,7 +12712,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(252*128)
@@ -12721,7 +12737,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(253*128)
@@ -12746,7 +12762,7 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(254*128)
@@ -12771,14 +12787,14 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(255*128)
.balign 128
- .size artMterpAsmAltInstructionStart, .-artMterpAsmAltInstructionStart
- .global artMterpAsmAltInstructionEnd
-artMterpAsmAltInstructionEnd:
+ SIZE(SYMBOL(artMterpAsmAltInstructionStart),SYMBOL(artMterpAsmAltInstructionStart))
+ .global SYMBOL(artMterpAsmAltInstructionEnd)
+SYMBOL(artMterpAsmAltInstructionEnd):
/* File: x86/footer.S */
/*
* ===========================================================================
@@ -12802,7 +12818,7 @@
movl %eax, OUT_ARG0(%esp)
lea OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG1(%esp)
- call MterpLogDivideByZeroException
+ call SYMBOL(MterpLogDivideByZeroException)
#endif
jmp MterpCommonFallback
@@ -12813,7 +12829,7 @@
movl %eax, OUT_ARG0(%esp)
lea OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG1(%esp)
- call MterpLogArrayIndexException
+ call SYMBOL(MterpLogArrayIndexException)
#endif
jmp MterpCommonFallback
@@ -12824,7 +12840,7 @@
movl %eax, OUT_ARG0(%esp)
lea OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG1(%esp)
- call MterpLogNegativeArraySizeException
+ call SYMBOL(MterpLogNegativeArraySizeException)
#endif
jmp MterpCommonFallback
@@ -12835,7 +12851,7 @@
movl %eax, OUT_ARG0(%esp)
lea OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG1(%esp)
- call MterpLogNoSuchMethodException
+ call SYMBOL(MterpLogNoSuchMethodException)
#endif
jmp MterpCommonFallback
@@ -12846,7 +12862,7 @@
movl %eax, OUT_ARG0(%esp)
lea OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG1(%esp)
- call MterpLogNullObjectException
+ call SYMBOL(MterpLogNullObjectException)
#endif
jmp MterpCommonFallback
@@ -12857,7 +12873,7 @@
movl %eax, OUT_ARG0(%esp)
lea OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG0(%esp)
- call MterpLogExceptionThrownException
+ call SYMBOL(MterpLogExceptionThrownException)
#endif
jmp MterpCommonFallback
@@ -12870,7 +12886,7 @@
movl %ecx, OUT_ARG0(%esp)
movl THREAD_FLAGS_OFFSET(%eax), %eax
movl %eax, OUT_ARG2(%esp)
- call MterpLogSuspendFallback
+ call SYMBOL(MterpLogSuspendFallback)
#endif
jmp MterpCommonFallback
@@ -12895,7 +12911,7 @@
movl %eax, OUT_ARG0(%esp)
lea OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG1(%esp)
- call MterpHandleException
+ call SYMBOL(MterpHandleException)
testl %eax, %eax
jz MterpExceptionReturn
REFRESH_IBASE
@@ -12919,7 +12935,7 @@
testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax)
jz 1f
movl %eax, OUT_ARG0(%esp)
- call MterpSuspendCheck
+ call SYMBOL(MterpSuspendCheck)
REFRESH_IBASE
1:
GOTO_NEXT
@@ -12934,7 +12950,7 @@
movl %eax, OUT_ARG0(%esp)
lea OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG1(%esp)
- call MterpLogFallback
+ call SYMBOL(MterpLogFallback)
#endif
MterpCommonFallback:
xor %eax, %eax
@@ -12965,5 +12981,5 @@
ret
.cfi_endproc
- .size ExecuteMterpImpl, .-ExecuteMterpImpl
+ SIZE(ExecuteMterpImpl,ExecuteMterpImpl)
diff --git a/runtime/interpreter/mterp/x86/alt_stub.S b/runtime/interpreter/mterp/x86/alt_stub.S
index 6462fc5..5a91167 100644
--- a/runtime/interpreter/mterp/x86/alt_stub.S
+++ b/runtime/interpreter/mterp/x86/alt_stub.S
@@ -15,6 +15,6 @@
movl %ecx, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %eax
movl %eax, OUT_ARG1(%esp)
- call MterpCheckBefore # (self, shadow_frame)
+ call SYMBOL(MterpCheckBefore) # (self, shadow_frame)
REFRESH_IBASE
jmp .L_op_nop+(${opnum}*${handler_size_bytes})
diff --git a/runtime/interpreter/mterp/x86/bincmp.S b/runtime/interpreter/mterp/x86/bincmp.S
index a9a8c3a..27cf6ea 100644
--- a/runtime/interpreter/mterp/x86/bincmp.S
+++ b/runtime/interpreter/mterp/x86/bincmp.S
@@ -8,7 +8,7 @@
/* if-cmp vA, vB, +CCCC */
movzx rINSTbl, %ecx # ecx <- A+
andb $$0xf, %cl # ecx <- A
- GET_VREG %eax %ecx # eax <- vA
+ GET_VREG %eax, %ecx # eax <- vA
sarl $$4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
movl $$2, %eax # assume not taken
diff --git a/runtime/interpreter/mterp/x86/bindiv.S b/runtime/interpreter/mterp/x86/bindiv.S
index 742f758..bb5b319 100644
--- a/runtime/interpreter/mterp/x86/bindiv.S
+++ b/runtime/interpreter/mterp/x86/bindiv.S
@@ -6,8 +6,8 @@
/* div/rem vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB
- GET_VREG %ecx %ecx # ecx <- vCC
+ GET_VREG %eax, %eax # eax <- vBB
+ GET_VREG %ecx, %ecx # ecx <- vCC
mov rIBASE, LOCAL0(%esp)
testl %ecx, %ecx
je common_errDivideByZero
@@ -43,6 +43,6 @@
xorl %edx, %edx # Clear %edx before divide
div %cx
.L${opcode}_finish:
- SET_VREG $result rINST
+ SET_VREG $result, rINST
mov LOCAL0(%esp), rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/bindiv2addr.S b/runtime/interpreter/mterp/x86/bindiv2addr.S
index ee7c523..e620996 100644
--- a/runtime/interpreter/mterp/x86/bindiv2addr.S
+++ b/runtime/interpreter/mterp/x86/bindiv2addr.S
@@ -7,9 +7,9 @@
movzx rINSTbl, %ecx # eax <- BA
mov rIBASE, LOCAL0(%esp)
sarl $$4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # eax <- vBB
+ GET_VREG %ecx, %ecx # eax <- vBB
andb $$0xf, rINSTbl # rINST <- A
- GET_VREG %eax rINST # eax <- vBB
+ GET_VREG %eax, rINST # eax <- vBB
testl %ecx, %ecx
je common_errDivideByZero
cmpl $$-1, %ecx
@@ -17,13 +17,13 @@
cmpl $$0x80000000, %eax
jne .L${opcode}_continue_div2addr
movl $special, $result
- SET_VREG $result rINST
+ SET_VREG $result, rINST
mov LOCAL0(%esp), rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
.L${opcode}_continue_div2addr:
cltd
idivl %ecx
- SET_VREG $result rINST
+ SET_VREG $result, rINST
mov LOCAL0(%esp), rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/bindivLit16.S b/runtime/interpreter/mterp/x86/bindivLit16.S
index a2c4334..be094ae 100644
--- a/runtime/interpreter/mterp/x86/bindivLit16.S
+++ b/runtime/interpreter/mterp/x86/bindivLit16.S
@@ -7,7 +7,7 @@
/* Need A in rINST, ssssCCCC in ecx, vB in eax */
movzbl rINSTbl, %eax # eax <- 000000BA
sarl $$4, %eax # eax <- B
- GET_VREG %eax %eax # eax <- vB
+ GET_VREG %eax, %eax # eax <- vB
movswl 2(rPC), %ecx # ecx <- ssssCCCC
andb $$0xf, rINSTbl # rINST <- A
testl %ecx, %ecx
@@ -17,13 +17,13 @@
cmpl $$0x80000000, %eax
jne .L${opcode}_continue_div
movl $special, %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
.L${opcode}_continue_div:
mov rIBASE, LOCAL0(%esp)
cltd
idivl %ecx
- SET_VREG $result rINST
+ SET_VREG $result, rINST
mov LOCAL0(%esp), rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/bindivLit8.S b/runtime/interpreter/mterp/x86/bindivLit8.S
index 61bee06..fddb545 100644
--- a/runtime/interpreter/mterp/x86/bindivLit8.S
+++ b/runtime/interpreter/mterp/x86/bindivLit8.S
@@ -6,7 +6,7 @@
/* div/rem/lit8 vAA, vBB, #+CC */
movzbl 2(rPC), %eax # eax <- BB
movsbl 3(rPC), %ecx # ecx <- ssssssCC
- GET_VREG %eax %eax # eax <- rBB
+ GET_VREG %eax, %eax # eax <- rBB
testl %ecx, %ecx
je common_errDivideByZero
cmpl $$0x80000000, %eax
@@ -14,13 +14,13 @@
cmpl $$-1, %ecx
jne .L${opcode}_continue_div
movl $special, %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
.L${opcode}_continue_div:
mov rIBASE, LOCAL0(%esp)
cltd
idivl %ecx
- SET_VREG $result rINST
+ SET_VREG $result, rINST
mov LOCAL0(%esp), rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/binop.S b/runtime/interpreter/mterp/x86/binop.S
index 5383f25..d895235 100644
--- a/runtime/interpreter/mterp/x86/binop.S
+++ b/runtime/interpreter/mterp/x86/binop.S
@@ -11,7 +11,7 @@
/* binop vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB
+ GET_VREG %eax, %eax # eax <- vBB
$instr # ex: addl (rFP,%ecx,4),%eax
- SET_VREG $result rINST
+ SET_VREG $result, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/binop1.S b/runtime/interpreter/mterp/x86/binop1.S
index cd51d0c..5049bb3 100644
--- a/runtime/interpreter/mterp/x86/binop1.S
+++ b/runtime/interpreter/mterp/x86/binop1.S
@@ -6,8 +6,8 @@
/* binop vAA, vBB, vCC */
movzbl 2(rPC),%eax # eax <- BB
movzbl 3(rPC),%ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB
- GET_VREG %ecx %ecx # eax <- vBB
+ GET_VREG %eax, %eax # eax <- vBB
+ GET_VREG %ecx, %ecx # eax <- vBB
$instr # ex: addl %ecx,%eax
- SET_VREG $result rINST
+ SET_VREG $result, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/binop2addr.S b/runtime/interpreter/mterp/x86/binop2addr.S
index abee4db..f126234 100644
--- a/runtime/interpreter/mterp/x86/binop2addr.S
+++ b/runtime/interpreter/mterp/x86/binop2addr.S
@@ -12,7 +12,7 @@
/* binop/2addr vA, vB */
movzx rINSTbl, %ecx # ecx <- A+
sarl $$4, rINST # rINST <- B
- GET_VREG %eax rINST # eax <- vB
+ GET_VREG %eax, rINST # eax <- vB
andb $$0xf, %cl # ecx <- A
$instr # for ex: addl %eax,(rFP,%ecx,4)
CLEAR_REF %ecx
diff --git a/runtime/interpreter/mterp/x86/binopLit16.S b/runtime/interpreter/mterp/x86/binopLit16.S
index 6c7fe61..2fd59de 100644
--- a/runtime/interpreter/mterp/x86/binopLit16.S
+++ b/runtime/interpreter/mterp/x86/binopLit16.S
@@ -11,9 +11,9 @@
/* binop/lit16 vA, vB, #+CCCC */
movzbl rINSTbl, %eax # eax <- 000000BA
sarl $$4, %eax # eax <- B
- GET_VREG %eax %eax # eax <- vB
+ GET_VREG %eax, %eax # eax <- vB
movswl 2(rPC), %ecx # ecx <- ssssCCCC
andb $$0xf, rINSTbl # rINST <- A
$instr # for example: addl %ecx, %eax
- SET_VREG $result rINST
+ SET_VREG $result, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/binopLit8.S b/runtime/interpreter/mterp/x86/binopLit8.S
index 924685d..67cead2 100644
--- a/runtime/interpreter/mterp/x86/binopLit8.S
+++ b/runtime/interpreter/mterp/x86/binopLit8.S
@@ -12,7 +12,7 @@
/* binop/lit8 vAA, vBB, #+CC */
movzbl 2(rPC), %eax # eax <- BB
movsbl 3(rPC), %ecx # ecx <- ssssssCC
- GET_VREG %eax %eax # eax <- rBB
+ GET_VREG %eax, %eax # eax <- rBB
$instr # ex: addl %ecx,%eax
- SET_VREG $result rINST
+ SET_VREG $result, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/binopWide.S b/runtime/interpreter/mterp/x86/binopWide.S
index 9f7106e..da1293d 100644
--- a/runtime/interpreter/mterp/x86/binopWide.S
+++ b/runtime/interpreter/mterp/x86/binopWide.S
@@ -2,14 +2,14 @@
* Generic 64-bit binary operation.
*/
/* binop vAA, vBB, vCC */
- movzbl 2(rPC),%eax # eax <- BB
- movzbl 3(rPC),%ecx # ecx <- CC
- movl rIBASE,LOCAL0(%esp) # save rIBASE
- GET_VREG rIBASE %eax # rIBASE <- v[BB+0]
- GET_VREG_HIGH %eax %eax # eax <- v[BB+1]
+ movzbl 2(rPC), %eax # eax <- BB
+ movzbl 3(rPC), %ecx # ecx <- CC
+ movl rIBASE, LOCAL0(%esp) # save rIBASE
+ GET_VREG rIBASE, %eax # rIBASE <- v[BB+0]
+ GET_VREG_HIGH %eax, %eax # eax <- v[BB+1]
$instr1 # ex: addl (rFP,%ecx,4),rIBASE
$instr2 # ex: adcl 4(rFP,%ecx,4),%eax
- SET_VREG rIBASE rINST # v[AA+0] <- rIBASE
- movl LOCAL0(%esp),rIBASE # restore rIBASE
- SET_VREG_HIGH %eax rINST # v[AA+1] <- eax
+ SET_VREG rIBASE, rINST # v[AA+0] <- rIBASE
+ movl LOCAL0(%esp), rIBASE # restore rIBASE
+ SET_VREG_HIGH %eax, rINST # v[AA+1] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/binopWide2addr.S b/runtime/interpreter/mterp/x86/binopWide2addr.S
index 7560af4..da816f4 100644
--- a/runtime/interpreter/mterp/x86/binopWide2addr.S
+++ b/runtime/interpreter/mterp/x86/binopWide2addr.S
@@ -2,11 +2,11 @@
* Generic 64-bit binary operation.
*/
/* binop/2addr vA, vB */
- movzbl rINSTbl,%ecx # ecx<- BA
- sarl $$4,%ecx # ecx<- B
- GET_VREG %eax %ecx # eax<- v[B+0]
- GET_VREG_HIGH %ecx %ecx # eax<- v[B+1]
- andb $$0xF,rINSTbl # rINST<- A
+ movzbl rINSTbl, %ecx # ecx<- BA
+ sarl $$4, %ecx # ecx<- B
+ GET_VREG %eax, %ecx # eax<- v[B+0]
+ GET_VREG_HIGH %ecx, %ecx # eax<- v[B+1]
+ andb $$0xF, rINSTbl # rINST<- A
$instr1 # ex: addl %eax,(rFP,rINST,4)
$instr2 # ex: adcl %ecx,4(rFP,rINST,4)
CLEAR_WIDE_REF rINST
diff --git a/runtime/interpreter/mterp/x86/entry.S b/runtime/interpreter/mterp/x86/entry.S
index a24ef70..b83f7e1 100644
--- a/runtime/interpreter/mterp/x86/entry.S
+++ b/runtime/interpreter/mterp/x86/entry.S
@@ -18,8 +18,8 @@
*/
.text
- .global ExecuteMterpImpl
- .type ExecuteMterpImpl, %function
+ .global SYMBOL(ExecuteMterpImpl)
+ FUNCTION_TYPE(ExecuteMterpImpl)
/*
* On entry:
@@ -30,7 +30,7 @@
*
*/
-ExecuteMterpImpl:
+SYMBOL(ExecuteMterpImpl):
.cfi_startproc
/* Allocate frame */
subl $$FRAME_SIZE, %esp
diff --git a/runtime/interpreter/mterp/x86/footer.S b/runtime/interpreter/mterp/x86/footer.S
index a2a36c4..385e784 100644
--- a/runtime/interpreter/mterp/x86/footer.S
+++ b/runtime/interpreter/mterp/x86/footer.S
@@ -20,7 +20,7 @@
movl %eax, OUT_ARG0(%esp)
lea OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG1(%esp)
- call MterpLogDivideByZeroException
+ call SYMBOL(MterpLogDivideByZeroException)
#endif
jmp MterpCommonFallback
@@ -31,7 +31,7 @@
movl %eax, OUT_ARG0(%esp)
lea OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG1(%esp)
- call MterpLogArrayIndexException
+ call SYMBOL(MterpLogArrayIndexException)
#endif
jmp MterpCommonFallback
@@ -42,7 +42,7 @@
movl %eax, OUT_ARG0(%esp)
lea OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG1(%esp)
- call MterpLogNegativeArraySizeException
+ call SYMBOL(MterpLogNegativeArraySizeException)
#endif
jmp MterpCommonFallback
@@ -53,7 +53,7 @@
movl %eax, OUT_ARG0(%esp)
lea OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG1(%esp)
- call MterpLogNoSuchMethodException
+ call SYMBOL(MterpLogNoSuchMethodException)
#endif
jmp MterpCommonFallback
@@ -64,7 +64,7 @@
movl %eax, OUT_ARG0(%esp)
lea OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG1(%esp)
- call MterpLogNullObjectException
+ call SYMBOL(MterpLogNullObjectException)
#endif
jmp MterpCommonFallback
@@ -75,7 +75,7 @@
movl %eax, OUT_ARG0(%esp)
lea OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG0(%esp)
- call MterpLogExceptionThrownException
+ call SYMBOL(MterpLogExceptionThrownException)
#endif
jmp MterpCommonFallback
@@ -88,7 +88,7 @@
movl %ecx, OUT_ARG0(%esp)
movl THREAD_FLAGS_OFFSET(%eax), %eax
movl %eax, OUT_ARG2(%esp)
- call MterpLogSuspendFallback
+ call SYMBOL(MterpLogSuspendFallback)
#endif
jmp MterpCommonFallback
@@ -113,7 +113,7 @@
movl %eax, OUT_ARG0(%esp)
lea OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG1(%esp)
- call MterpHandleException
+ call SYMBOL(MterpHandleException)
testl %eax, %eax
jz MterpExceptionReturn
REFRESH_IBASE
@@ -137,7 +137,7 @@
testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax)
jz 1f
movl %eax, OUT_ARG0(%esp)
- call MterpSuspendCheck
+ call SYMBOL(MterpSuspendCheck)
REFRESH_IBASE
1:
GOTO_NEXT
@@ -152,7 +152,7 @@
movl %eax, OUT_ARG0(%esp)
lea OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG1(%esp)
- call MterpLogFallback
+ call SYMBOL(MterpLogFallback)
#endif
MterpCommonFallback:
xor %eax, %eax
@@ -183,4 +183,4 @@
ret
.cfi_endproc
- .size ExecuteMterpImpl, .-ExecuteMterpImpl
+ SIZE(ExecuteMterpImpl,ExecuteMterpImpl)
diff --git a/runtime/interpreter/mterp/x86/fpcmp.S b/runtime/interpreter/mterp/x86/fpcmp.S
index 2b98667..5f9eef9 100644
--- a/runtime/interpreter/mterp/x86/fpcmp.S
+++ b/runtime/interpreter/mterp/x86/fpcmp.S
@@ -31,5 +31,5 @@
.L${opcode}_less:
decl %eax
.L${opcode}_finish:
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S
index 2481785..0977b90 100644
--- a/runtime/interpreter/mterp/x86/header.S
+++ b/runtime/interpreter/mterp/x86/header.S
@@ -89,6 +89,22 @@
*/
#include "asm_support.h"
+/*
+ * Handle mac compiler specific
+ */
+#if defined(__APPLE__)
+ #define MACRO_LITERAL(value) $$(value)
+ #define FUNCTION_TYPE(name)
+ #define SIZE(start,end)
+ // Mac OS' symbols have an _ prefix.
+ #define SYMBOL(name) _ ## name
+#else
+ #define MACRO_LITERAL(value) $$value
+ #define FUNCTION_TYPE(name) .type name, @function
+ #define SIZE(start,end) .size start, .-end
+ #define SYMBOL(name) name
+#endif
+
/* Frame size must be 16-byte aligned.
* Remember about 4 bytes for return address
*/
@@ -192,7 +208,7 @@
*/
.macro REFRESH_INST _opnum
movb rINSTbl, rINSTbh
- movb $$\_opnum, rINSTbl
+ movb MACRO_LITERAL(\_opnum), rINSTbl
.endm
/*
@@ -208,7 +224,7 @@
.macro GOTO_NEXT
movzx rINSTbl,%eax
movzbl rINSTbh,rINST
- shll $$${handler_size_bits}, %eax
+ shll MACRO_LITERAL(${handler_size_bits}), %eax
addl rIBASE, %eax
jmp *%eax
.endm
@@ -248,7 +264,7 @@
.macro SET_VREG _reg _vreg
movl \_reg, (rFP,\_vreg,4)
- movl $$0, (rREFS,\_vreg,4)
+ movl MACRO_LITERAL(0), (rREFS,\_vreg,4)
.endm
/* Write wide value from xmm. xmm is clobbered. */
@@ -269,14 +285,14 @@
.macro SET_VREG_HIGH _reg _vreg
movl \_reg, 4(rFP,\_vreg,4)
- movl $$0, 4(rREFS,\_vreg,4)
+ movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4)
.endm
.macro CLEAR_REF _vreg
- movl $$0, (rREFS,\_vreg,4)
+ movl MACRO_LITERAL(0), (rREFS,\_vreg,4)
.endm
.macro CLEAR_WIDE_REF _vreg
- movl $$0, (rREFS,\_vreg,4)
- movl $$0, 4(rREFS,\_vreg,4)
+ movl MACRO_LITERAL(0), (rREFS,\_vreg,4)
+ movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4)
.endm
diff --git a/runtime/interpreter/mterp/x86/invoke.S b/runtime/interpreter/mterp/x86/invoke.S
index 80f7822..054fbfd 100644
--- a/runtime/interpreter/mterp/x86/invoke.S
+++ b/runtime/interpreter/mterp/x86/invoke.S
@@ -13,7 +13,7 @@
movl rPC, OUT_ARG2(%esp)
REFRESH_INST ${opnum}
movl rINST, OUT_ARG3(%esp)
- call $helper
+ call SYMBOL($helper)
testl %eax, %eax
jz MterpException
REFRESH_IBASE
diff --git a/runtime/interpreter/mterp/x86/op_aget.S b/runtime/interpreter/mterp/x86/op_aget.S
index 52b5236..338386f 100644
--- a/runtime/interpreter/mterp/x86/op_aget.S
+++ b/runtime/interpreter/mterp/x86/op_aget.S
@@ -8,12 +8,12 @@
/* op vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB (array object)
- GET_VREG %ecx %ecx # ecx <- vCC (requested index)
+ GET_VREG %eax, %eax # eax <- vBB (array object)
+ GET_VREG %ecx, %ecx # ecx <- vCC (requested index)
testl %eax, %eax # null array object?
je common_errNullObject # bail if so
cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx
jae common_errArrayIndex # index >= length, bail.
$load $data_offset(%eax,%ecx,$shift), %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_aget_object.S b/runtime/interpreter/mterp/x86/op_aget_object.S
index 61f3e91..cbfb50c 100644
--- a/runtime/interpreter/mterp/x86/op_aget_object.S
+++ b/runtime/interpreter/mterp/x86/op_aget_object.S
@@ -6,15 +6,15 @@
/* op vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB (array object)
- GET_VREG %ecx %ecx # ecs <- vCC (requested index)
+ GET_VREG %eax, %eax # eax <- vBB (array object)
+ GET_VREG %ecx, %ecx # ecs <- vCC (requested index)
EXPORT_PC
movl %eax, OUT_ARG0(%esp)
movl %ecx, OUT_ARG1(%esp)
- call artAGetObjectFromMterp # (array, index)
+ call SYMBOL(artAGetObjectFromMterp) # (array, index)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
- SET_VREG_OBJECT %eax rINST
+ SET_VREG_OBJECT %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_aget_wide.S b/runtime/interpreter/mterp/x86/op_aget_wide.S
index 663adc6..92c612a 100644
--- a/runtime/interpreter/mterp/x86/op_aget_wide.S
+++ b/runtime/interpreter/mterp/x86/op_aget_wide.S
@@ -4,13 +4,13 @@
/* aget-wide vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB (array object)
- GET_VREG %ecx %ecx # ecx <- vCC (requested index)
+ GET_VREG %eax, %eax # eax <- vBB (array object)
+ GET_VREG %ecx, %ecx # ecx <- vCC (requested index)
testl %eax, %eax # null array object?
je common_errNullObject # bail if so
cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx
jae common_errArrayIndex # index >= length, bail.
leal MIRROR_WIDE_ARRAY_DATA_OFFSET(%eax,%ecx,8), %eax
movq (%eax), %xmm0 # xmm0 <- vBB[vCC]
- SET_WIDE_FP_VREG %xmm0 rINST # vAA <- xmm0
+ SET_WIDE_FP_VREG %xmm0, rINST # vAA <- xmm0
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_aput.S b/runtime/interpreter/mterp/x86/op_aput.S
index 2ea465d..9d8c52d 100644
--- a/runtime/interpreter/mterp/x86/op_aput.S
+++ b/runtime/interpreter/mterp/x86/op_aput.S
@@ -8,13 +8,13 @@
/* op vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB (array object)
- GET_VREG %ecx %ecx # ecx <- vCC (requested index)
+ GET_VREG %eax, %eax # eax <- vBB (array object)
+ GET_VREG %ecx, %ecx # ecx <- vCC (requested index)
testl %eax, %eax # null array object?
je common_errNullObject # bail if so
cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx
jae common_errArrayIndex # index >= length, bail.
leal $data_offset(%eax,%ecx,$shift), %eax
- GET_VREG rINST rINST
+ GET_VREG rINST, rINST
$store $reg, (%eax)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_aput_object.S b/runtime/interpreter/mterp/x86/op_aput_object.S
index 2af5acb..9cfc221 100644
--- a/runtime/interpreter/mterp/x86/op_aput_object.S
+++ b/runtime/interpreter/mterp/x86/op_aput_object.S
@@ -8,7 +8,7 @@
movl rPC, OUT_ARG1(%esp)
REFRESH_INST ${opnum}
movl rINST, OUT_ARG2(%esp)
- call MterpAputObject # (array, index)
+ call SYMBOL(MterpAputObject) # (array, index)
REFRESH_IBASE
testl %eax, %eax
jz MterpPossibleException
diff --git a/runtime/interpreter/mterp/x86/op_aput_wide.S b/runtime/interpreter/mterp/x86/op_aput_wide.S
index 7a33371..43ef64a 100644
--- a/runtime/interpreter/mterp/x86/op_aput_wide.S
+++ b/runtime/interpreter/mterp/x86/op_aput_wide.S
@@ -5,13 +5,13 @@
/* aput-wide vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB (array object)
- GET_VREG %ecx %ecx # ecx <- vCC (requested index)
+ GET_VREG %eax, %eax # eax <- vBB (array object)
+ GET_VREG %ecx, %ecx # ecx <- vCC (requested index)
testl %eax, %eax # null array object?
je common_errNullObject # bail if so
cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx
jae common_errArrayIndex # index >= length, bail.
leal MIRROR_WIDE_ARRAY_DATA_OFFSET(%eax,%ecx,8), %eax
- GET_WIDE_FP_VREG %xmm0 rINST # xmm0 <- vAA
+ GET_WIDE_FP_VREG %xmm0, rINST # xmm0 <- vAA
movq %xmm0, (%eax) # vBB[vCC] <- xmm0
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_array_length.S b/runtime/interpreter/mterp/x86/op_array_length.S
index 3e42a7c..60ed80b 100644
--- a/runtime/interpreter/mterp/x86/op_array_length.S
+++ b/runtime/interpreter/mterp/x86/op_array_length.S
@@ -3,10 +3,10 @@
*/
mov rINST, %eax # eax <- BA
sarl $$4, rINST # rINST <- B
- GET_VREG %ecx rINST # ecx <- vB (object ref)
+ GET_VREG %ecx, rINST # ecx <- vB (object ref)
testl %ecx, %ecx # is null?
je common_errNullObject
andb $$0xf, %al # eax <- A
movl MIRROR_ARRAY_LENGTH_OFFSET(%ecx), rINST
- SET_VREG rINST %eax
+ SET_VREG rINST, %eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/op_check_cast.S b/runtime/interpreter/mterp/x86/op_check_cast.S
index 018432a..ae2ff9e 100644
--- a/runtime/interpreter/mterp/x86/op_check_cast.S
+++ b/runtime/interpreter/mterp/x86/op_check_cast.S
@@ -11,7 +11,7 @@
movl %eax, OUT_ARG2(%esp)
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp)
- call MterpCheckCast # (index, &obj, method, self)
+ call SYMBOL(MterpCheckCast) # (index, &obj, method, self)
REFRESH_IBASE
testl %eax, %eax
jnz MterpPossibleException
diff --git a/runtime/interpreter/mterp/x86/op_cmp_long.S b/runtime/interpreter/mterp/x86/op_cmp_long.S
index bd86738..1f729b0 100644
--- a/runtime/interpreter/mterp/x86/op_cmp_long.S
+++ b/runtime/interpreter/mterp/x86/op_cmp_long.S
@@ -5,17 +5,17 @@
/* cmp-long vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG_HIGH %eax %eax # eax <- v[BB+1], BB is clobbered
+ GET_VREG_HIGH %eax, %eax # eax <- v[BB+1], BB is clobbered
cmpl VREG_HIGH_ADDRESS(%ecx), %eax
jl .L${opcode}_smaller
jg .L${opcode}_bigger
movzbl 2(rPC), %eax # eax <- BB, restore BB
- GET_VREG %eax %eax # eax <- v[BB]
+ GET_VREG %eax, %eax # eax <- v[BB]
sub VREG_ADDRESS(%ecx), %eax
ja .L${opcode}_bigger
jb .L${opcode}_smaller
.L${opcode}_finish:
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
.L${opcode}_bigger:
diff --git a/runtime/interpreter/mterp/x86/op_const.S b/runtime/interpreter/mterp/x86/op_const.S
index dc69530..544d63b 100644
--- a/runtime/interpreter/mterp/x86/op_const.S
+++ b/runtime/interpreter/mterp/x86/op_const.S
@@ -1,4 +1,4 @@
/* const vAA, #+BBBBbbbb */
movl 2(rPC), %eax # grab all 32 bits at once
- SET_VREG %eax rINST # vAA<- eax
+ SET_VREG %eax, rINST # vAA<- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
diff --git a/runtime/interpreter/mterp/x86/op_const_16.S b/runtime/interpreter/mterp/x86/op_const_16.S
index f5707cf..97cd5fa 100644
--- a/runtime/interpreter/mterp/x86/op_const_16.S
+++ b/runtime/interpreter/mterp/x86/op_const_16.S
@@ -1,4 +1,4 @@
/* const/16 vAA, #+BBBB */
movswl 2(rPC), %ecx # ecx <- ssssBBBB
- SET_VREG %ecx rINST # vAA <- ssssBBBB
+ SET_VREG %ecx, rINST # vAA <- ssssBBBB
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_const_4.S b/runtime/interpreter/mterp/x86/op_const_4.S
index c336411..a60ba96 100644
--- a/runtime/interpreter/mterp/x86/op_const_4.S
+++ b/runtime/interpreter/mterp/x86/op_const_4.S
@@ -3,5 +3,5 @@
movl $$0xf, rINST
andl %eax, rINST # rINST <- A
sarl $$4, %eax
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/op_const_class.S b/runtime/interpreter/mterp/x86/op_const_class.S
index eceb8bc..343e110 100644
--- a/runtime/interpreter/mterp/x86/op_const_class.S
+++ b/runtime/interpreter/mterp/x86/op_const_class.S
@@ -7,7 +7,7 @@
movl %eax, OUT_ARG2(%esp)
movl rSELF, %eax
movl %eax, OUT_ARG3(%esp)
- call MterpConstClass # (index, tgt_reg, shadow_frame, self)
+ call SYMBOL(MterpConstClass) # (index, tgt_reg, shadow_frame, self)
REFRESH_IBASE
testl %eax, %eax
jnz MterpPossibleException
diff --git a/runtime/interpreter/mterp/x86/op_const_high16.S b/runtime/interpreter/mterp/x86/op_const_high16.S
index da78d1b..576967a 100644
--- a/runtime/interpreter/mterp/x86/op_const_high16.S
+++ b/runtime/interpreter/mterp/x86/op_const_high16.S
@@ -1,5 +1,5 @@
/* const/high16 vAA, #+BBBB0000 */
movzwl 2(rPC), %eax # eax <- 0000BBBB
sall $$16, %eax # eax <- BBBB0000
- SET_VREG %eax rINST # vAA <- eax
+ SET_VREG %eax, rINST # vAA <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_const_string.S b/runtime/interpreter/mterp/x86/op_const_string.S
index 9acd6fe..bbac69c 100644
--- a/runtime/interpreter/mterp/x86/op_const_string.S
+++ b/runtime/interpreter/mterp/x86/op_const_string.S
@@ -7,7 +7,7 @@
movl %eax, OUT_ARG2(%esp)
movl rSELF, %eax
movl %eax, OUT_ARG3(%esp)
- call MterpConstString # (index, tgt_reg, shadow_frame, self)
+ call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self)
REFRESH_IBASE
testl %eax, %eax
jnz MterpPossibleException
diff --git a/runtime/interpreter/mterp/x86/op_const_string_jumbo.S b/runtime/interpreter/mterp/x86/op_const_string_jumbo.S
index 5c728b2..4236807 100644
--- a/runtime/interpreter/mterp/x86/op_const_string_jumbo.S
+++ b/runtime/interpreter/mterp/x86/op_const_string_jumbo.S
@@ -7,7 +7,7 @@
movl %eax, OUT_ARG2(%esp)
movl rSELF, %eax
movl %eax, OUT_ARG3(%esp)
- call MterpConstString # (index, tgt_reg, shadow_frame, self)
+ call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self)
REFRESH_IBASE
testl %eax, %eax
jnz MterpPossibleException
diff --git a/runtime/interpreter/mterp/x86/op_const_wide.S b/runtime/interpreter/mterp/x86/op_const_wide.S
index 745490e..3750728 100644
--- a/runtime/interpreter/mterp/x86/op_const_wide.S
+++ b/runtime/interpreter/mterp/x86/op_const_wide.S
@@ -2,6 +2,6 @@
movl 2(rPC), %eax # eax <- lsw
movzbl rINSTbl, %ecx # ecx <- AA
movl 6(rPC), rINST # rINST <- msw
- SET_VREG %eax %ecx
- SET_VREG_HIGH rINST %ecx
+ SET_VREG %eax, %ecx
+ SET_VREG_HIGH rINST, %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 5
diff --git a/runtime/interpreter/mterp/x86/op_const_wide_16.S b/runtime/interpreter/mterp/x86/op_const_wide_16.S
index 8029cfe..1331c32 100644
--- a/runtime/interpreter/mterp/x86/op_const_wide_16.S
+++ b/runtime/interpreter/mterp/x86/op_const_wide_16.S
@@ -2,7 +2,7 @@
movswl 2(rPC), %eax # eax <- ssssBBBB
movl rIBASE, %ecx # preserve rIBASE (cltd trashes it)
cltd # rIBASE:eax <- ssssssssssssBBBB
- SET_VREG_HIGH rIBASE rINST # store msw
- SET_VREG %eax rINST # store lsw
+ SET_VREG_HIGH rIBASE, rINST # store msw
+ SET_VREG %eax, rINST # store lsw
movl %ecx, rIBASE # restore rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_const_wide_32.S b/runtime/interpreter/mterp/x86/op_const_wide_32.S
index 3e23d3a..ed7d62b 100644
--- a/runtime/interpreter/mterp/x86/op_const_wide_32.S
+++ b/runtime/interpreter/mterp/x86/op_const_wide_32.S
@@ -2,7 +2,7 @@
movl 2(rPC), %eax # eax <- BBBBbbbb
movl rIBASE, %ecx # preserve rIBASE (cltd trashes it)
cltd # rIBASE:eax <- ssssssssssssBBBB
- SET_VREG_HIGH rIBASE rINST # store msw
- SET_VREG %eax rINST # store lsw
+ SET_VREG_HIGH rIBASE, rINST # store msw
+ SET_VREG %eax, rINST # store lsw
movl %ecx, rIBASE # restore rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
diff --git a/runtime/interpreter/mterp/x86/op_const_wide_high16.S b/runtime/interpreter/mterp/x86/op_const_wide_high16.S
index d2a1119..11b9310 100644
--- a/runtime/interpreter/mterp/x86/op_const_wide_high16.S
+++ b/runtime/interpreter/mterp/x86/op_const_wide_high16.S
@@ -1,7 +1,7 @@
/* const-wide/high16 vAA, #+BBBB000000000000 */
movzwl 2(rPC), %eax # eax <- 0000BBBB
sall $$16, %eax # eax <- BBBB0000
- SET_VREG_HIGH %eax rINST # v[AA+1] <- eax
+ SET_VREG_HIGH %eax, rINST # v[AA+1] <- eax
xorl %eax, %eax
- SET_VREG %eax rINST # v[AA+0] <- eax
+ SET_VREG %eax, rINST # v[AA+0] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_div_long.S b/runtime/interpreter/mterp/x86/op_div_long.S
index 5772826..e56a035 100644
--- a/runtime/interpreter/mterp/x86/op_div_long.S
+++ b/runtime/interpreter/mterp/x86/op_div_long.S
@@ -7,17 +7,17 @@
mov rIBASE, LOCAL0(%esp) # save rIBASE/%edx
mov rINST, LOCAL1(%esp) # save rINST/%ebx
movzbl 3(rPC), %eax # eax <- CC
- GET_VREG %ecx %eax
- GET_VREG_HIGH %ebx %eax
+ GET_VREG %ecx, %eax
+ GET_VREG_HIGH %ebx, %eax
movl %ecx, %edx
orl %ebx, %ecx
jz common_errDivideByZero
movzbl 2(rPC), %eax # eax <- BB
- GET_VREG_HIGH %ecx %eax
- GET_VREG %eax %eax
- call $routine
+ GET_VREG_HIGH %ecx, %eax
+ GET_VREG %eax, %eax
+ call SYMBOL($routine)
mov LOCAL1(%esp), rINST # restore rINST/%ebx
- SET_VREG_HIGH rIBASE rINST
- SET_VREG %eax rINST
+ SET_VREG_HIGH rIBASE, rINST
+ SET_VREG %eax, rINST
mov LOCAL0(%esp), rIBASE # restore rIBASE/%edx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_div_long_2addr.S b/runtime/interpreter/mterp/x86/op_div_long_2addr.S
index 2696042..159cc44 100644
--- a/runtime/interpreter/mterp/x86/op_div_long_2addr.S
+++ b/runtime/interpreter/mterp/x86/op_div_long_2addr.S
@@ -10,16 +10,16 @@
andb $$0xf, rINSTbl # rINST <- A
mov rINST, LOCAL1(%esp) # save rINST/%ebx
movl %ebx, %ecx
- GET_VREG %edx %eax
- GET_VREG_HIGH %ebx %eax
+ GET_VREG %edx, %eax
+ GET_VREG_HIGH %ebx, %eax
movl %edx, %eax
orl %ebx, %eax
jz common_errDivideByZero
- GET_VREG %eax %ecx
- GET_VREG_HIGH %ecx %ecx
- call $routine
+ GET_VREG %eax, %ecx
+ GET_VREG_HIGH %ecx, %ecx
+ call SYMBOL($routine)
mov LOCAL1(%esp), rINST # restore rINST/%ebx
- SET_VREG_HIGH rIBASE rINST
- SET_VREG %eax rINST
+ SET_VREG_HIGH rIBASE, rINST
+ SET_VREG %eax, rINST
mov LOCAL0(%esp), rIBASE # restore rIBASE/%edx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/op_fill_array_data.S b/runtime/interpreter/mterp/x86/op_fill_array_data.S
index 0cb05f6..004aed9 100644
--- a/runtime/interpreter/mterp/x86/op_fill_array_data.S
+++ b/runtime/interpreter/mterp/x86/op_fill_array_data.S
@@ -2,10 +2,10 @@
EXPORT_PC
movl 2(rPC), %ecx # ecx <- BBBBbbbb
leal (rPC,%ecx,2), %ecx # ecx <- PC + BBBBbbbb*2
- GET_VREG %eax rINST # eax <- vAA (array object)
+ GET_VREG %eax, rINST # eax <- vAA (array object)
movl %eax, OUT_ARG0(%esp)
movl %ecx, OUT_ARG1(%esp)
- call MterpFillArrayData # (obj, payload)
+ call SYMBOL(MterpFillArrayData) # (obj, payload)
REFRESH_IBASE
testl %eax, %eax # 0 means an exception is thrown
jz MterpPossibleException
diff --git a/runtime/interpreter/mterp/x86/op_filled_new_array.S b/runtime/interpreter/mterp/x86/op_filled_new_array.S
index c08b09f..a2bac29 100644
--- a/runtime/interpreter/mterp/x86/op_filled_new_array.S
+++ b/runtime/interpreter/mterp/x86/op_filled_new_array.S
@@ -13,7 +13,7 @@
movl rPC, OUT_ARG1(%esp)
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp)
- call $helper
+ call SYMBOL($helper)
REFRESH_IBASE
testl %eax, %eax # 0 means an exception is thrown
jz MterpPossibleException
diff --git a/runtime/interpreter/mterp/x86/op_iget.S b/runtime/interpreter/mterp/x86/op_iget.S
index 868ffd0..9932610 100644
--- a/runtime/interpreter/mterp/x86/op_iget.S
+++ b/runtime/interpreter/mterp/x86/op_iget.S
@@ -15,15 +15,15 @@
movl %eax, OUT_ARG2(%esp) # referrer
mov rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call $helper
+ call SYMBOL($helper)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $$0xf, rINSTbl # rINST <- A
.if $is_object
- SET_VREG_OBJECT %eax rINST # fp[A] <-value
+ SET_VREG_OBJECT %eax, rINST # fp[A] <-value
.else
- SET_VREG %eax rINST # fp[A] <-value
+ SET_VREG %eax, rINST # fp[A] <-value
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_iget_object_quick.S b/runtime/interpreter/mterp/x86/op_iget_object_quick.S
index b09772f..fe16694 100644
--- a/runtime/interpreter/mterp/x86/op_iget_object_quick.S
+++ b/runtime/interpreter/mterp/x86/op_iget_object_quick.S
@@ -2,16 +2,16 @@
/* op vA, vB, offset@CCCC */
movzbl rINSTbl, %ecx # ecx <- BA
sarl $$4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # vB (object we're operating on)
+ GET_VREG %ecx, %ecx # vB (object we're operating on)
movzwl 2(rPC), %eax # eax <- field byte offset
movl %ecx, OUT_ARG0(%esp)
movl %eax, OUT_ARG1(%esp)
EXPORT_PC
- call artIGetObjectFromMterp # (obj, offset)
+ call SYMBOL(artIGetObjectFromMterp) # (obj, offset)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $$0xf,rINSTbl # rINST <- A
- SET_VREG_OBJECT %eax rINST # fp[A] <- value
+ SET_VREG_OBJECT %eax, rINST # fp[A] <- value
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_iget_quick.S b/runtime/interpreter/mterp/x86/op_iget_quick.S
index 372071c..1b7440f 100644
--- a/runtime/interpreter/mterp/x86/op_iget_quick.S
+++ b/runtime/interpreter/mterp/x86/op_iget_quick.S
@@ -3,11 +3,11 @@
/* op vA, vB, offset@CCCC */
movzbl rINSTbl, %ecx # ecx <- BA
sarl $$4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # vB (object we're operating on)
+ GET_VREG %ecx, %ecx # vB (object we're operating on)
movzwl 2(rPC), %eax # eax <- field byte offset
testl %ecx, %ecx # is object null?
je common_errNullObject
${load} (%ecx,%eax,1), %eax
andb $$0xf,rINSTbl # rINST <- A
- SET_VREG %eax rINST # fp[A] <- value
+ SET_VREG %eax, rINST # fp[A] <- value
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_iget_wide.S b/runtime/interpreter/mterp/x86/op_iget_wide.S
index 58e5a65..92126b4 100644
--- a/runtime/interpreter/mterp/x86/op_iget_wide.S
+++ b/runtime/interpreter/mterp/x86/op_iget_wide.S
@@ -14,12 +14,12 @@
movl %eax, OUT_ARG2(%esp) # referrer
mov rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call artGet64InstanceFromCode
+ call SYMBOL(artGet64InstanceFromCode)
mov rSELF, %ecx
cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $$0xf, rINSTbl # rINST <- A
- SET_VREG %eax rINST
- SET_VREG_HIGH %edx rINST
+ SET_VREG %eax, rINST
+ SET_VREG_HIGH %edx, rINST
REFRESH_IBASE_FROM_SELF %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_iget_wide_quick.S b/runtime/interpreter/mterp/x86/op_iget_wide_quick.S
index 8be336b..7ce74cc 100644
--- a/runtime/interpreter/mterp/x86/op_iget_wide_quick.S
+++ b/runtime/interpreter/mterp/x86/op_iget_wide_quick.S
@@ -1,11 +1,11 @@
/* iget-wide-quick vA, vB, offset@CCCC */
movzbl rINSTbl, %ecx # ecx <- BA
sarl $$4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # vB (object we're operating on)
+ GET_VREG %ecx, %ecx # vB (object we're operating on)
movzwl 2(rPC), %eax # eax <- field byte offset
testl %ecx, %ecx # is object null?
je common_errNullObject
movq (%ecx,%eax,1), %xmm0
andb $$0xf, rINSTbl # rINST <- A
- SET_WIDE_FP_VREG %xmm0 rINST
+ SET_WIDE_FP_VREG %xmm0, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_instance_of.S b/runtime/interpreter/mterp/x86/op_instance_of.S
index c9bfba5..fd5bf44 100644
--- a/runtime/interpreter/mterp/x86/op_instance_of.S
+++ b/runtime/interpreter/mterp/x86/op_instance_of.S
@@ -16,11 +16,11 @@
movl %eax, OUT_ARG2(%esp)
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp)
- call MterpInstanceOf # (index, &obj, method, self)
+ call SYMBOL(MterpInstanceOf) # (index, &obj, method, self)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
andb $$0xf, rINSTbl # rINSTbl <- A
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_int_to_long.S b/runtime/interpreter/mterp/x86/op_int_to_long.S
index 736ea69..6f9ea26 100644
--- a/runtime/interpreter/mterp/x86/op_int_to_long.S
+++ b/runtime/interpreter/mterp/x86/op_int_to_long.S
@@ -1,12 +1,12 @@
/* int to long vA, vB */
movzbl rINSTbl, %eax # eax <- +A
sarl $$4, %eax # eax <- B
- GET_VREG %eax %eax # eax <- vB
+ GET_VREG %eax, %eax # eax <- vB
andb $$0xf, rINSTbl # rINST <- A
movl rIBASE, %ecx # cltd trashes rIBASE/edx
cltd # rINST:eax<- sssssssBBBBBBBB
- SET_VREG_HIGH rIBASE rINST # v[A+1] <- rIBASE
- SET_VREG %eax rINST # v[A+0] <- %eax
+ SET_VREG_HIGH rIBASE, rINST # v[A+1] <- rIBASE
+ SET_VREG %eax, rINST # v[A+0] <- %eax
movl %ecx, rIBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/op_iput.S b/runtime/interpreter/mterp/x86/op_iput.S
index f8a6549..13cfe5c 100644
--- a/runtime/interpreter/mterp/x86/op_iput.S
+++ b/runtime/interpreter/mterp/x86/op_iput.S
@@ -18,7 +18,7 @@
movl %eax, OUT_ARG2(%esp) # fp[A]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
- call $handler
+ call SYMBOL($handler)
testl %eax, %eax
jnz MterpPossibleException
REFRESH_IBASE
diff --git a/runtime/interpreter/mterp/x86/op_iput_object.S b/runtime/interpreter/mterp/x86/op_iput_object.S
index 20d57aa..f63075c 100644
--- a/runtime/interpreter/mterp/x86/op_iput_object.S
+++ b/runtime/interpreter/mterp/x86/op_iput_object.S
@@ -6,7 +6,7 @@
movl rINST, OUT_ARG2(%esp)
movl rSELF, %eax
movl %eax, OUT_ARG3(%esp)
- call MterpIputObject
+ call SYMBOL(MterpIputObject)
testl %eax, %eax
jz MterpException
REFRESH_IBASE
diff --git a/runtime/interpreter/mterp/x86/op_iput_object_quick.S b/runtime/interpreter/mterp/x86/op_iput_object_quick.S
index 4c7f4bd..d54b1b7 100644
--- a/runtime/interpreter/mterp/x86/op_iput_object_quick.S
+++ b/runtime/interpreter/mterp/x86/op_iput_object_quick.S
@@ -4,7 +4,7 @@
movl rPC, OUT_ARG1(%esp)
REFRESH_INST ${opnum}
movl rINST, OUT_ARG2(%esp)
- call MterpIputObjectQuick
+ call SYMBOL(MterpIputObjectQuick)
testl %eax, %eax
jz MterpException
REFRESH_IBASE
diff --git a/runtime/interpreter/mterp/x86/op_iput_quick.S b/runtime/interpreter/mterp/x86/op_iput_quick.S
index e2f7caf..b67cee0 100644
--- a/runtime/interpreter/mterp/x86/op_iput_quick.S
+++ b/runtime/interpreter/mterp/x86/op_iput_quick.S
@@ -3,11 +3,11 @@
/* op vA, vB, offset@CCCC */
movzbl rINSTbl, %ecx # ecx <- BA
sarl $$4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # vB (object we're operating on)
+ GET_VREG %ecx, %ecx # vB (object we're operating on)
testl %ecx, %ecx # is object null?
je common_errNullObject
andb $$0xf, rINSTbl # rINST <- A
- GET_VREG rINST rINST # rINST <- v[A]
+ GET_VREG rINST, rINST # rINST <- v[A]
movzwl 2(rPC), %eax # eax <- field byte offset
${store} ${reg}, (%ecx,%eax,1)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_iput_wide.S b/runtime/interpreter/mterp/x86/op_iput_wide.S
index 92cb770..573e14d 100644
--- a/runtime/interpreter/mterp/x86/op_iput_wide.S
+++ b/runtime/interpreter/mterp/x86/op_iput_wide.S
@@ -12,7 +12,7 @@
movl %eax, OUT_ARG2(%esp) # &fp[A]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
- call artSet64InstanceFromMterp
+ call SYMBOL(artSet64InstanceFromMterp)
testl %eax, %eax
jnz MterpPossibleException
REFRESH_IBASE
diff --git a/runtime/interpreter/mterp/x86/op_iput_wide_quick.S b/runtime/interpreter/mterp/x86/op_iput_wide_quick.S
index 72285c5..17de6f8 100644
--- a/runtime/interpreter/mterp/x86/op_iput_wide_quick.S
+++ b/runtime/interpreter/mterp/x86/op_iput_wide_quick.S
@@ -1,12 +1,12 @@
/* iput-wide-quick vA, vB, offset@CCCC */
movzbl rINSTbl, %ecx # ecx<- BA
sarl $$4, %ecx # ecx<- B
- GET_VREG %ecx %ecx # vB (object we're operating on)
+ GET_VREG %ecx, %ecx # vB (object we're operating on)
testl %ecx, %ecx # is object null?
je common_errNullObject
movzwl 2(rPC), %eax # eax<- field byte offset
leal (%ecx,%eax,1), %ecx # ecx<- Address of 64-bit target
andb $$0xf, rINSTbl # rINST<- A
- GET_WIDE_FP_VREG %xmm0 rINST # xmm0<- fp[A]/fp[A+1]
+ GET_WIDE_FP_VREG %xmm0, rINST # xmm0<- fp[A]/fp[A+1]
movq %xmm0, (%ecx) # obj.field<- r0/r1
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_monitor_enter.S b/runtime/interpreter/mterp/x86/op_monitor_enter.S
index 8236fb3..9e885bd 100644
--- a/runtime/interpreter/mterp/x86/op_monitor_enter.S
+++ b/runtime/interpreter/mterp/x86/op_monitor_enter.S
@@ -3,11 +3,11 @@
*/
/* monitor-enter vAA */
EXPORT_PC
- GET_VREG %ecx rINST
+ GET_VREG %ecx, rINST
movl %ecx, OUT_ARG0(%esp)
movl rSELF, %eax
movl %eax, OUT_ARG1(%esp)
- call artLockObjectFromCode # (object, self)
+ call SYMBOL(artLockObjectFromCode) # (object, self)
REFRESH_IBASE
testl %eax, %eax
jnz MterpException
diff --git a/runtime/interpreter/mterp/x86/op_monitor_exit.S b/runtime/interpreter/mterp/x86/op_monitor_exit.S
index 56d4eb3..0904800 100644
--- a/runtime/interpreter/mterp/x86/op_monitor_exit.S
+++ b/runtime/interpreter/mterp/x86/op_monitor_exit.S
@@ -7,11 +7,11 @@
*/
/* monitor-exit vAA */
EXPORT_PC
- GET_VREG %ecx rINST
+ GET_VREG %ecx, rINST
movl %ecx, OUT_ARG0(%esp)
movl rSELF, %eax
movl %eax, OUT_ARG1(%esp)
- call artUnlockObjectFromCode # (object, self)
+ call SYMBOL(artUnlockObjectFromCode) # (object, self)
REFRESH_IBASE
testl %eax, %eax
jnz MterpException
diff --git a/runtime/interpreter/mterp/x86/op_move.S b/runtime/interpreter/mterp/x86/op_move.S
index 0a531be..ea173b9 100644
--- a/runtime/interpreter/mterp/x86/op_move.S
+++ b/runtime/interpreter/mterp/x86/op_move.S
@@ -4,10 +4,10 @@
movzbl rINSTbl, %eax # eax <- BA
andb $$0xf, %al # eax <- A
shrl $$4, rINST # rINST <- B
- GET_VREG rINST rINST
+ GET_VREG rINST, rINST
.if $is_object
- SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B]
+ SET_VREG_OBJECT rINST, %eax # fp[A] <- fp[B]
.else
- SET_VREG rINST %eax # fp[A] <- fp[B]
+ SET_VREG rINST, %eax # fp[A] <- fp[B]
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/op_move_16.S b/runtime/interpreter/mterp/x86/op_move_16.S
index 0773f41..454deb5 100644
--- a/runtime/interpreter/mterp/x86/op_move_16.S
+++ b/runtime/interpreter/mterp/x86/op_move_16.S
@@ -3,10 +3,10 @@
/* op vAAAA, vBBBB */
movzwl 4(rPC), %ecx # ecx <- BBBB
movzwl 2(rPC), %eax # eax <- AAAA
- GET_VREG rINST %ecx
+ GET_VREG rINST, %ecx
.if $is_object
- SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B]
+ SET_VREG_OBJECT rINST, %eax # fp[A] <- fp[B]
.else
- SET_VREG rINST %eax # fp[A] <- fp[B]
+ SET_VREG rINST, %eax # fp[A] <- fp[B]
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
diff --git a/runtime/interpreter/mterp/x86/op_move_exception.S b/runtime/interpreter/mterp/x86/op_move_exception.S
index e37cdfa..d8dc74f 100644
--- a/runtime/interpreter/mterp/x86/op_move_exception.S
+++ b/runtime/interpreter/mterp/x86/op_move_exception.S
@@ -1,6 +1,6 @@
/* move-exception vAA */
movl rSELF, %ecx
movl THREAD_EXCEPTION_OFFSET(%ecx), %eax
- SET_VREG_OBJECT %eax rINST # fp[AA] <- exception object
+ SET_VREG_OBJECT %eax, rINST # fp[AA] <- exception object
movl $$0, THREAD_EXCEPTION_OFFSET(%ecx)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/op_move_from16.S b/runtime/interpreter/mterp/x86/op_move_from16.S
index 623a4d3..e869855 100644
--- a/runtime/interpreter/mterp/x86/op_move_from16.S
+++ b/runtime/interpreter/mterp/x86/op_move_from16.S
@@ -3,10 +3,10 @@
/* op vAA, vBBBB */
movzx rINSTbl, %eax # eax <- AA
movw 2(rPC), rINSTw # rINSTw <- BBBB
- GET_VREG rINST rINST # rINST <- fp[BBBB]
+ GET_VREG rINST, rINST # rINST <- fp[BBBB]
.if $is_object
- SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B]
+ SET_VREG_OBJECT rINST, %eax # fp[A] <- fp[B]
.else
- SET_VREG rINST %eax # fp[A] <- fp[B]
+ SET_VREG rINST, %eax # fp[A] <- fp[B]
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_move_result.S b/runtime/interpreter/mterp/x86/op_move_result.S
index 414f2cb..f6f2129 100644
--- a/runtime/interpreter/mterp/x86/op_move_result.S
+++ b/runtime/interpreter/mterp/x86/op_move_result.S
@@ -4,8 +4,8 @@
movl OFF_FP_RESULT_REGISTER(rFP), %eax # get pointer to result JType.
movl (%eax), %eax # r0 <- result.i.
.if $is_object
- SET_VREG_OBJECT %eax rINST # fp[A] <- fp[B]
+ SET_VREG_OBJECT %eax, rINST # fp[A] <- fp[B]
.else
- SET_VREG %eax rINST # fp[A] <- fp[B]
+ SET_VREG %eax, rINST # fp[A] <- fp[B]
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/op_move_result_wide.S b/runtime/interpreter/mterp/x86/op_move_result_wide.S
index 0c1683b..7818cce 100644
--- a/runtime/interpreter/mterp/x86/op_move_result_wide.S
+++ b/runtime/interpreter/mterp/x86/op_move_result_wide.S
@@ -2,6 +2,6 @@
movl OFF_FP_RESULT_REGISTER(rFP), %eax # get pointer to result JType.
movl 4(%eax), %ecx # Get high
movl (%eax), %eax # Get low
- SET_VREG %eax rINST # v[AA+0] <- eax
- SET_VREG_HIGH %ecx rINST # v[AA+1] <- ecx
+ SET_VREG %eax, rINST # v[AA+0] <- eax
+ SET_VREG_HIGH %ecx, rINST # v[AA+1] <- ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/op_move_wide.S b/runtime/interpreter/mterp/x86/op_move_wide.S
index 9c0e985..79ce7b7 100644
--- a/runtime/interpreter/mterp/x86/op_move_wide.S
+++ b/runtime/interpreter/mterp/x86/op_move_wide.S
@@ -3,6 +3,6 @@
movzbl rINSTbl, %ecx # ecx <- BA
sarl $$4, rINST # rINST <- B
andb $$0xf, %cl # ecx <- A
- GET_WIDE_FP_VREG %xmm0 rINST # xmm0 <- v[B]
- SET_WIDE_FP_VREG %xmm0 %ecx # v[A] <- xmm0
+ GET_WIDE_FP_VREG %xmm0, rINST # xmm0 <- v[B]
+ SET_WIDE_FP_VREG %xmm0, %ecx # v[A] <- xmm0
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/op_move_wide_16.S b/runtime/interpreter/mterp/x86/op_move_wide_16.S
index 7522c27..a6b8596 100644
--- a/runtime/interpreter/mterp/x86/op_move_wide_16.S
+++ b/runtime/interpreter/mterp/x86/op_move_wide_16.S
@@ -2,6 +2,6 @@
/* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
movzwl 4(rPC), %ecx # ecx<- BBBB
movzwl 2(rPC), %eax # eax<- AAAA
- GET_WIDE_FP_VREG %xmm0 %ecx # xmm0 <- v[B]
- SET_WIDE_FP_VREG %xmm0 %eax # v[A] <- xmm0
+ GET_WIDE_FP_VREG %xmm0, %ecx # xmm0 <- v[B]
+ SET_WIDE_FP_VREG %xmm0, %eax # v[A] <- xmm0
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
diff --git a/runtime/interpreter/mterp/x86/op_move_wide_from16.S b/runtime/interpreter/mterp/x86/op_move_wide_from16.S
index 5ad2cb4..ec344de 100644
--- a/runtime/interpreter/mterp/x86/op_move_wide_from16.S
+++ b/runtime/interpreter/mterp/x86/op_move_wide_from16.S
@@ -2,6 +2,6 @@
/* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
movzwl 2(rPC), %ecx # ecx <- BBBB
movzbl rINSTbl, %eax # eax <- AAAA
- GET_WIDE_FP_VREG %xmm0 %ecx # xmm0 <- v[B]
- SET_WIDE_FP_VREG %xmm0 %eax # v[A] <- xmm0
+ GET_WIDE_FP_VREG %xmm0, %ecx # xmm0 <- v[B]
+ SET_WIDE_FP_VREG %xmm0, %eax # v[A] <- xmm0
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_mul_int.S b/runtime/interpreter/mterp/x86/op_mul_int.S
index a367ab7..77f4659 100644
--- a/runtime/interpreter/mterp/x86/op_mul_int.S
+++ b/runtime/interpreter/mterp/x86/op_mul_int.S
@@ -4,9 +4,9 @@
/* mul vAA, vBB, vCC */
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
- GET_VREG %eax %eax # eax <- vBB
+ GET_VREG %eax, %eax # eax <- vBB
mov rIBASE, LOCAL0(%esp)
imull (rFP,%ecx,4), %eax # trashes rIBASE/edx
mov LOCAL0(%esp), rIBASE
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_mul_int_2addr.S b/runtime/interpreter/mterp/x86/op_mul_int_2addr.S
index 6005075..f92a28e 100644
--- a/runtime/interpreter/mterp/x86/op_mul_int_2addr.S
+++ b/runtime/interpreter/mterp/x86/op_mul_int_2addr.S
@@ -1,10 +1,10 @@
/* mul vA, vB */
movzx rINSTbl, %ecx # ecx <- A+
sarl $$4, rINST # rINST <- B
- GET_VREG %eax rINST # eax <- vB
+ GET_VREG %eax, rINST # eax <- vB
andb $$0xf, %cl # ecx <- A
mov rIBASE, LOCAL0(%esp)
imull (rFP,%ecx,4), %eax # trashes rIBASE/edx
mov LOCAL0(%esp), rIBASE
- SET_VREG %eax %ecx
+ SET_VREG %eax, %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/op_mul_int_lit16.S b/runtime/interpreter/mterp/x86/op_mul_int_lit16.S
index 1c0fde3..31ab613 100644
--- a/runtime/interpreter/mterp/x86/op_mul_int_lit16.S
+++ b/runtime/interpreter/mterp/x86/op_mul_int_lit16.S
@@ -2,11 +2,11 @@
/* Need A in rINST, ssssCCCC in ecx, vB in eax */
movzbl rINSTbl, %eax # eax <- 000000BA
sarl $$4, %eax # eax <- B
- GET_VREG %eax %eax # eax <- vB
+ GET_VREG %eax, %eax # eax <- vB
movswl 2(rPC), %ecx # ecx <- ssssCCCC
andb $$0xf, rINSTbl # rINST <- A
mov rIBASE, LOCAL0(%esp)
imull %ecx, %eax # trashes rIBASE/edx
mov LOCAL0(%esp), rIBASE
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_mul_int_lit8.S b/runtime/interpreter/mterp/x86/op_mul_int_lit8.S
index 4d7a22d..6637aa7 100644
--- a/runtime/interpreter/mterp/x86/op_mul_int_lit8.S
+++ b/runtime/interpreter/mterp/x86/op_mul_int_lit8.S
@@ -1,9 +1,9 @@
/* mul/lit8 vAA, vBB, #+CC */
movzbl 2(rPC), %eax # eax <- BB
movsbl 3(rPC), %ecx # ecx <- ssssssCC
- GET_VREG %eax %eax # eax <- rBB
+ GET_VREG %eax, %eax # eax <- rBB
mov rIBASE, LOCAL0(%esp)
imull %ecx, %eax # trashes rIBASE/edx
mov LOCAL0(%esp), rIBASE
- SET_VREG %eax rINST
+ SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_mul_long.S b/runtime/interpreter/mterp/x86/op_mul_long.S
index 3746e41..f35ca13 100644
--- a/runtime/interpreter/mterp/x86/op_mul_long.S
+++ b/runtime/interpreter/mterp/x86/op_mul_long.S
@@ -27,7 +27,7 @@
mov LOCAL0(%esp), rPC # restore Interpreter PC
mov LOCAL1(%esp), rFP # restore FP
leal (%ecx,rIBASE), rIBASE # full result now in rIBASE:%eax
- SET_VREG_HIGH rIBASE rINST # v[B+1] <- rIBASE
+ SET_VREG_HIGH rIBASE, rINST # v[B+1] <- rIBASE
mov LOCAL2(%esp), rIBASE # restore IBASE
- SET_VREG %eax rINST # v[B] <- eax
+ SET_VREG %eax, rINST # v[B] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_neg_long.S b/runtime/interpreter/mterp/x86/op_neg_long.S
index 7cc17f0..30da247 100644
--- a/runtime/interpreter/mterp/x86/op_neg_long.S
+++ b/runtime/interpreter/mterp/x86/op_neg_long.S
@@ -2,12 +2,12 @@
movzbl rINSTbl, %ecx # ecx <- BA
sarl $$4, %ecx # ecx <- B
andb $$0xf, rINSTbl # rINST <- A
- GET_VREG %eax %ecx # eax <- v[B+0]
- GET_VREG_HIGH %ecx %ecx # ecx <- v[B+1]
+ GET_VREG %eax, %ecx # eax <- v[B+0]
+ GET_VREG_HIGH %ecx, %ecx # ecx <- v[B+1]
negl %eax
adcl $$0, %ecx
negl %ecx
- SET_VREG %eax rINST # v[A+0] <- eax
- SET_VREG_HIGH %ecx rINST # v[A+1] <- ecx
+ SET_VREG %eax, rINST # v[A+0] <- eax
+ SET_VREG_HIGH %ecx, rINST # v[A+1] <- ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/op_new_array.S b/runtime/interpreter/mterp/x86/op_new_array.S
index 6852183..2490477 100644
--- a/runtime/interpreter/mterp/x86/op_new_array.S
+++ b/runtime/interpreter/mterp/x86/op_new_array.S
@@ -14,7 +14,7 @@
movl rINST, OUT_ARG2(%esp)
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp)
- call MterpNewArray
+ call SYMBOL(MterpNewArray)
REFRESH_IBASE
testl %eax, %eax # 0 means an exception is thrown
jz MterpPossibleException
diff --git a/runtime/interpreter/mterp/x86/op_new_instance.S b/runtime/interpreter/mterp/x86/op_new_instance.S
index a3632e8..712a5eb 100644
--- a/runtime/interpreter/mterp/x86/op_new_instance.S
+++ b/runtime/interpreter/mterp/x86/op_new_instance.S
@@ -9,7 +9,7 @@
movl %ecx, OUT_ARG1(%esp)
REFRESH_INST ${opnum}
movl rINST, OUT_ARG2(%esp)
- call MterpNewInstance
+ call SYMBOL(MterpNewInstance)
REFRESH_IBASE
testl %eax, %eax # 0 means an exception is thrown
jz MterpPossibleException
diff --git a/runtime/interpreter/mterp/x86/op_not_long.S b/runtime/interpreter/mterp/x86/op_not_long.S
index 55666a1..8f706e1 100644
--- a/runtime/interpreter/mterp/x86/op_not_long.S
+++ b/runtime/interpreter/mterp/x86/op_not_long.S
@@ -2,10 +2,10 @@
movzbl rINSTbl, %ecx # ecx <- BA
sarl $$4, %ecx # ecx <- B
andb $$0xf, rINSTbl # rINST <- A
- GET_VREG %eax %ecx # eax <- v[B+0]
- GET_VREG_HIGH %ecx %ecx # ecx <- v[B+1]
+ GET_VREG %eax, %ecx # eax <- v[B+0]
+ GET_VREG_HIGH %ecx, %ecx # ecx <- v[B+1]
notl %eax
notl %ecx
- SET_VREG %eax rINST # v[A+0] <- eax
- SET_VREG_HIGH %ecx rINST # v[A+1] <- ecx
+ SET_VREG %eax, rINST # v[A+0] <- eax
+ SET_VREG_HIGH %ecx, rINST # v[A+1] <- ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/op_packed_switch.S b/runtime/interpreter/mterp/x86/op_packed_switch.S
index 4e39a48..230b58e 100644
--- a/runtime/interpreter/mterp/x86/op_packed_switch.S
+++ b/runtime/interpreter/mterp/x86/op_packed_switch.S
@@ -10,11 +10,11 @@
*/
/* op vAA, +BBBB */
movl 2(rPC), %ecx # ecx <- BBBBbbbb
- GET_VREG %eax rINST # eax <- vAA
+ GET_VREG %eax, rINST # eax <- vAA
leal (rPC,%ecx,2), %ecx # ecx <- PC + BBBBbbbb*2
movl %eax, OUT_ARG1(%esp) # ARG1 <- vAA
movl %ecx, OUT_ARG0(%esp) # ARG0 <- switchData
- call $func
+ call SYMBOL($func)
addl %eax, %eax
leal (rPC, %eax), rPC
FETCH_INST
diff --git a/runtime/interpreter/mterp/x86/op_return.S b/runtime/interpreter/mterp/x86/op_return.S
index 183b3bf..8e3cfad 100644
--- a/runtime/interpreter/mterp/x86/op_return.S
+++ b/runtime/interpreter/mterp/x86/op_return.S
@@ -5,13 +5,13 @@
*/
/* op vAA */
.extern MterpThreadFenceForConstructor
- call MterpThreadFenceForConstructor
+ call SYMBOL(MterpThreadFenceForConstructor)
movl rSELF, %eax
testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax)
jz 1f
movl %eax, OUT_ARG0(%esp)
- call MterpSuspendCheck
+ call SYMBOL(MterpSuspendCheck)
1:
- GET_VREG %eax rINST # eax <- vAA
+ GET_VREG %eax, rINST # eax <- vAA
xorl %ecx, %ecx
jmp MterpReturn
diff --git a/runtime/interpreter/mterp/x86/op_return_void.S b/runtime/interpreter/mterp/x86/op_return_void.S
index f3e24c7..a14a4f6 100644
--- a/runtime/interpreter/mterp/x86/op_return_void.S
+++ b/runtime/interpreter/mterp/x86/op_return_void.S
@@ -1,10 +1,10 @@
.extern MterpThreadFenceForConstructor
- call MterpThreadFenceForConstructor
+ call SYMBOL(MterpThreadFenceForConstructor)
movl rSELF, %eax
testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax)
jz 1f
movl %eax, OUT_ARG0(%esp)
- call MterpSuspendCheck
+ call SYMBOL(MterpSuspendCheck)
1:
xorl %eax, %eax
xorl %ecx, %ecx
diff --git a/runtime/interpreter/mterp/x86/op_return_void_no_barrier.S b/runtime/interpreter/mterp/x86/op_return_void_no_barrier.S
index add4e20..1d0e933 100644
--- a/runtime/interpreter/mterp/x86/op_return_void_no_barrier.S
+++ b/runtime/interpreter/mterp/x86/op_return_void_no_barrier.S
@@ -2,7 +2,7 @@
testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax)
jz 1f
movl %eax, OUT_ARG0(%esp)
- call MterpSuspendCheck
+ call SYMBOL(MterpSuspendCheck)
1:
xorl %eax, %eax
xorl %ecx, %ecx
diff --git a/runtime/interpreter/mterp/x86/op_return_wide.S b/runtime/interpreter/mterp/x86/op_return_wide.S
index 34a3380..7d1850a 100644
--- a/runtime/interpreter/mterp/x86/op_return_wide.S
+++ b/runtime/interpreter/mterp/x86/op_return_wide.S
@@ -3,13 +3,13 @@
*/
/* return-wide vAA */
.extern MterpThreadFenceForConstructor
- call MterpThreadFenceForConstructor
+ call SYMBOL(MterpThreadFenceForConstructor)
movl rSELF, %eax
testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax)
jz 1f
movl %eax, OUT_ARG0(%esp)
- call MterpSuspendCheck
+ call SYMBOL(MterpSuspendCheck)
1:
- GET_VREG %eax rINST # eax <- v[AA+0]
- GET_VREG_HIGH %ecx rINST # ecx <- v[AA+1]
+ GET_VREG %eax, rINST # eax <- v[AA+0]
+ GET_VREG_HIGH %ecx, rINST # ecx <- v[AA+1]
jmp MterpReturn
diff --git a/runtime/interpreter/mterp/x86/op_sget.S b/runtime/interpreter/mterp/x86/op_sget.S
index ed5aedf..ec96458 100644
--- a/runtime/interpreter/mterp/x86/op_sget.S
+++ b/runtime/interpreter/mterp/x86/op_sget.S
@@ -13,14 +13,14 @@
movl %eax, OUT_ARG1(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp) # self
- call $helper
+ call SYMBOL($helper)
movl rSELF, %ecx
REFRESH_IBASE_FROM_SELF %ecx
cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
.if $is_object
- SET_VREG_OBJECT %eax rINST # fp[A] <- value
+ SET_VREG_OBJECT %eax, rINST # fp[A] <- value
.else
- SET_VREG %eax rINST # fp[A] <- value
+ SET_VREG %eax, rINST # fp[A] <- value
.endif
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_sget_wide.S b/runtime/interpreter/mterp/x86/op_sget_wide.S
index 76b993b..833f266 100644
--- a/runtime/interpreter/mterp/x86/op_sget_wide.S
+++ b/runtime/interpreter/mterp/x86/op_sget_wide.S
@@ -11,11 +11,11 @@
movl %eax, OUT_ARG1(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG2(%esp) # self
- call artGet64StaticFromCode
+ call SYMBOL(artGet64StaticFromCode)
movl rSELF, %ecx
cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
- SET_VREG %eax rINST # fp[A]<- low part
- SET_VREG_HIGH %edx rINST # fp[A+1]<- high part
+ SET_VREG %eax, rINST # fp[A]<- low part
+ SET_VREG_HIGH %edx, rINST # fp[A+1]<- high part
REFRESH_IBASE_FROM_SELF %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_shl_long.S b/runtime/interpreter/mterp/x86/op_shl_long.S
index 56d13e3..aa58a93 100644
--- a/runtime/interpreter/mterp/x86/op_shl_long.S
+++ b/runtime/interpreter/mterp/x86/op_shl_long.S
@@ -13,9 +13,9 @@
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
movl rIBASE, LOCAL0(%esp)
- GET_VREG_HIGH rIBASE %eax # ecx <- v[BB+1]
- GET_VREG %ecx %ecx # ecx <- vCC
- GET_VREG %eax %eax # eax <- v[BB+0]
+ GET_VREG_HIGH rIBASE, %eax # ecx <- v[BB+1]
+ GET_VREG %ecx, %ecx # ecx <- vCC
+ GET_VREG %eax, %eax # eax <- v[BB+0]
shldl %eax,rIBASE
sall %cl, %eax
testb $$32, %cl
@@ -23,7 +23,7 @@
movl %eax, rIBASE
xorl %eax, %eax
2:
- SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE
+ SET_VREG_HIGH rIBASE, rINST # v[AA+1] <- rIBASE
movl LOCAL0(%esp), rIBASE
- SET_VREG %eax rINST # v[AA+0] <- %eax
+ SET_VREG %eax, rINST # v[AA+0] <- %eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_shl_long_2addr.S b/runtime/interpreter/mterp/x86/op_shl_long_2addr.S
index 5da873f..6bbf49c 100644
--- a/runtime/interpreter/mterp/x86/op_shl_long_2addr.S
+++ b/runtime/interpreter/mterp/x86/op_shl_long_2addr.S
@@ -8,11 +8,11 @@
/* rINSTw gets AA */
movzbl rINSTbl, %ecx # ecx <- BA
andb $$0xf, rINSTbl # rINST <- A
- GET_VREG %eax rINST # eax <- v[AA+0]
+ GET_VREG %eax, rINST # eax <- v[AA+0]
sarl $$4, %ecx # ecx <- B
movl rIBASE, LOCAL0(%esp)
- GET_VREG_HIGH rIBASE rINST # rIBASE <- v[AA+1]
- GET_VREG %ecx %ecx # ecx <- vBB
+ GET_VREG_HIGH rIBASE, rINST # rIBASE <- v[AA+1]
+ GET_VREG %ecx, %ecx # ecx <- vBB
shldl %eax, rIBASE
sall %cl, %eax
testb $$32, %cl
@@ -20,7 +20,7 @@
movl %eax, rIBASE
xorl %eax, %eax
2:
- SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE
+ SET_VREG_HIGH rIBASE, rINST # v[AA+1] <- rIBASE
movl LOCAL0(%esp), rIBASE
- SET_VREG %eax rINST # v[AA+0] <- eax
+ SET_VREG %eax, rINST # v[AA+0] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/op_shr_long.S b/runtime/interpreter/mterp/x86/op_shr_long.S
index 4490a9a..68aa0ee 100644
--- a/runtime/interpreter/mterp/x86/op_shr_long.S
+++ b/runtime/interpreter/mterp/x86/op_shr_long.S
@@ -13,9 +13,9 @@
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
movl rIBASE, LOCAL0(%esp)
- GET_VREG_HIGH rIBASE %eax # rIBASE<- v[BB+1]
- GET_VREG %ecx %ecx # ecx <- vCC
- GET_VREG %eax %eax # eax <- v[BB+0]
+ GET_VREG_HIGH rIBASE, %eax # rIBASE<- v[BB+1]
+ GET_VREG %ecx, %ecx # ecx <- vCC
+ GET_VREG %eax, %eax # eax <- v[BB+0]
shrdl rIBASE, %eax
sarl %cl, rIBASE
testb $$32, %cl
@@ -23,7 +23,7 @@
movl rIBASE, %eax
sarl $$31, rIBASE
2:
- SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE
+ SET_VREG_HIGH rIBASE, rINST # v[AA+1] <- rIBASE
movl LOCAL0(%esp), rIBASE
- SET_VREG %eax rINST # v[AA+0] <- eax
+ SET_VREG %eax, rINST # v[AA+0] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_shr_long_2addr.S b/runtime/interpreter/mterp/x86/op_shr_long_2addr.S
index 57494f9..148bd1b 100644
--- a/runtime/interpreter/mterp/x86/op_shr_long_2addr.S
+++ b/runtime/interpreter/mterp/x86/op_shr_long_2addr.S
@@ -8,11 +8,11 @@
/* rINSTw gets AA */
movzbl rINSTbl, %ecx # ecx <- BA
andb $$0xf, rINSTbl # rINST <- A
- GET_VREG %eax rINST # eax <- v[AA+0]
+ GET_VREG %eax, rINST # eax <- v[AA+0]
sarl $$4, %ecx # ecx <- B
movl rIBASE, LOCAL0(%esp)
- GET_VREG_HIGH rIBASE rINST # rIBASE <- v[AA+1]
- GET_VREG %ecx %ecx # ecx <- vBB
+ GET_VREG_HIGH rIBASE, rINST # rIBASE <- v[AA+1]
+ GET_VREG %ecx, %ecx # ecx <- vBB
shrdl rIBASE, %eax
sarl %cl, rIBASE
testb $$32, %cl
@@ -20,7 +20,7 @@
movl rIBASE, %eax
sarl $$31, rIBASE
2:
- SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE
+ SET_VREG_HIGH rIBASE, rINST # v[AA+1] <- rIBASE
movl LOCAL0(%esp), rIBASE
- SET_VREG %eax rINST # v[AA+0] <- eax
+ SET_VREG %eax, rINST # v[AA+0] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/op_sput.S b/runtime/interpreter/mterp/x86/op_sput.S
index 04a8f23..a199281 100644
--- a/runtime/interpreter/mterp/x86/op_sput.S
+++ b/runtime/interpreter/mterp/x86/op_sput.S
@@ -9,13 +9,13 @@
EXPORT_PC
movzwl 2(rPC), %eax
movl %eax, OUT_ARG0(%esp) # field ref BBBB
- GET_VREG rINST rINST
+ GET_VREG rINST, rINST
movl rINST, OUT_ARG1(%esp) # fp[AA]
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG2(%esp) # referrer
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call $helper
+ call SYMBOL($helper)
testl %eax, %eax
jnz MterpException
REFRESH_IBASE
diff --git a/runtime/interpreter/mterp/x86/op_sput_object.S b/runtime/interpreter/mterp/x86/op_sput_object.S
index 0480e00..e3e57fc 100644
--- a/runtime/interpreter/mterp/x86/op_sput_object.S
+++ b/runtime/interpreter/mterp/x86/op_sput_object.S
@@ -6,7 +6,7 @@
movl rINST, OUT_ARG2(%esp)
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp)
- call MterpSputObject
+ call SYMBOL(MterpSputObject)
testl %eax, %eax
jz MterpException
REFRESH_IBASE
diff --git a/runtime/interpreter/mterp/x86/op_sput_wide.S b/runtime/interpreter/mterp/x86/op_sput_wide.S
index d58d5af..7544838 100644
--- a/runtime/interpreter/mterp/x86/op_sput_wide.S
+++ b/runtime/interpreter/mterp/x86/op_sput_wide.S
@@ -13,7 +13,7 @@
movl %eax, OUT_ARG2(%esp) # &fp[AA]
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
- call artSet64IndirectStaticFromMterp
+ call SYMBOL(artSet64IndirectStaticFromMterp)
testl %eax, %eax
jnz MterpException
REFRESH_IBASE
diff --git a/runtime/interpreter/mterp/x86/op_throw.S b/runtime/interpreter/mterp/x86/op_throw.S
index 15b20b5..a6e6b1e 100644
--- a/runtime/interpreter/mterp/x86/op_throw.S
+++ b/runtime/interpreter/mterp/x86/op_throw.S
@@ -3,7 +3,7 @@
*/
/* throw vAA */
EXPORT_PC
- GET_VREG %eax rINST # eax<- vAA (exception object)
+ GET_VREG %eax, rINST # eax<- vAA (exception object)
testl %eax, %eax
jz common_errNullObject
movl rSELF,%ecx
diff --git a/runtime/interpreter/mterp/x86/op_ushr_long.S b/runtime/interpreter/mterp/x86/op_ushr_long.S
index 287946e..9527c9c 100644
--- a/runtime/interpreter/mterp/x86/op_ushr_long.S
+++ b/runtime/interpreter/mterp/x86/op_ushr_long.S
@@ -13,9 +13,9 @@
movzbl 2(rPC), %eax # eax <- BB
movzbl 3(rPC), %ecx # ecx <- CC
movl rIBASE, LOCAL0(%esp)
- GET_VREG_HIGH rIBASE %eax # rIBASE <- v[BB+1]
- GET_VREG %ecx %ecx # ecx <- vCC
- GET_VREG %eax %eax # eax <- v[BB+0]
+ GET_VREG_HIGH rIBASE, %eax # rIBASE <- v[BB+1]
+ GET_VREG %ecx, %ecx # ecx <- vCC
+ GET_VREG %eax, %eax # eax <- v[BB+0]
shrdl rIBASE, %eax
shrl %cl, rIBASE
testb $$32, %cl
@@ -23,7 +23,7 @@
movl rIBASE, %eax
xorl rIBASE, rIBASE
2:
- SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE
+ SET_VREG_HIGH rIBASE, rINST # v[AA+1] <- rIBASE
movl LOCAL0(%esp), rIBASE
- SET_VREG %eax rINST # v[BB+0] <- eax
+ SET_VREG %eax, rINST # v[BB+0] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_ushr_long_2addr.S b/runtime/interpreter/mterp/x86/op_ushr_long_2addr.S
index 39c2724..72fcc36 100644
--- a/runtime/interpreter/mterp/x86/op_ushr_long_2addr.S
+++ b/runtime/interpreter/mterp/x86/op_ushr_long_2addr.S
@@ -8,11 +8,11 @@
/* rINSTw gets AA */
movzbl rINSTbl, %ecx # ecx <- BA
andb $$0xf, rINSTbl # rINST <- A
- GET_VREG %eax rINST # eax <- v[AA+0]
+ GET_VREG %eax, rINST # eax <- v[AA+0]
sarl $$4, %ecx # ecx <- B
movl rIBASE, LOCAL0(%esp)
- GET_VREG_HIGH rIBASE rINST # rIBASE <- v[AA+1]
- GET_VREG %ecx %ecx # ecx <- vBB
+ GET_VREG_HIGH rIBASE, rINST # rIBASE <- v[AA+1]
+ GET_VREG %ecx, %ecx # ecx <- vBB
shrdl rIBASE, %eax
shrl %cl, rIBASE
testb $$32, %cl
@@ -20,7 +20,7 @@
movl rIBASE, %eax
xorl rIBASE, rIBASE
2:
- SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE
+ SET_VREG_HIGH rIBASE, rINST # v[AA+1] <- rIBASE
movl LOCAL0(%esp), rIBASE
- SET_VREG %eax rINST # v[AA+0] <- eax
+ SET_VREG %eax, rINST # v[AA+0] <- eax
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/shop2addr.S b/runtime/interpreter/mterp/x86/shop2addr.S
index 94d3545..96c9954 100644
--- a/runtime/interpreter/mterp/x86/shop2addr.S
+++ b/runtime/interpreter/mterp/x86/shop2addr.S
@@ -5,9 +5,9 @@
/* shift/2addr vA, vB */
movzx rINSTbl, %ecx # eax <- BA
sarl $$4, %ecx # ecx <- B
- GET_VREG %ecx %ecx # eax <- vBB
+ GET_VREG %ecx, %ecx # eax <- vBB
andb $$0xf, rINSTbl # rINST <- A
- GET_VREG %eax rINST # eax <- vAA
+ GET_VREG %eax, rINST # eax <- vAA
$instr # ex: sarl %cl, %eax
- SET_VREG $result rINST
+ SET_VREG $result, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/unop.S b/runtime/interpreter/mterp/x86/unop.S
index 00d3e15..db09fc0 100644
--- a/runtime/interpreter/mterp/x86/unop.S
+++ b/runtime/interpreter/mterp/x86/unop.S
@@ -6,8 +6,8 @@
/* unop vA, vB */
movzbl rINSTbl,%ecx # ecx <- A+
sarl $$4,rINST # rINST <- B
- GET_VREG %eax rINST # eax <- vB
+ GET_VREG %eax, rINST # eax <- vB
andb $$0xf,%cl # ecx <- A
$instr
- SET_VREG %eax %ecx
+ SET_VREG %eax, %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index fa5c41d..188deb0 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -25,15 +25,19 @@
#include "jit_code_cache.h"
#include "jit_instrumentation.h"
#include "oat_file_manager.h"
+#include "oat_quick_method_header.h"
#include "offline_profiling_info.h"
#include "profile_saver.h"
#include "runtime.h"
#include "runtime_options.h"
+#include "stack_map.h"
#include "utils.h"
namespace art {
namespace jit {
+static constexpr bool kEnableOnStackReplacement = true;
+
JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& options) {
auto* jit_options = new JitOptions;
jit_options->use_jit_ = options.GetOrDefault(RuntimeArgumentMap::UseJIT);
@@ -43,6 +47,8 @@
options.GetOrDefault(RuntimeArgumentMap::JITCodeCacheMaxCapacity);
jit_options->compile_threshold_ =
options.GetOrDefault(RuntimeArgumentMap::JITCompileThreshold);
+ // TODO(ngeoffray): Make this a proper option.
+ jit_options->osr_threshold_ = jit_options->compile_threshold_ * 2;
jit_options->warmup_threshold_ =
options.GetOrDefault(RuntimeArgumentMap::JITWarmupThreshold);
jit_options->dump_info_on_shutdown_ =
@@ -121,7 +127,7 @@
*error_msg = "JIT couldn't find jit_unload entry point";
return false;
}
- jit_compile_method_ = reinterpret_cast<bool (*)(void*, ArtMethod*, Thread*)>(
+ jit_compile_method_ = reinterpret_cast<bool (*)(void*, ArtMethod*, Thread*, bool)>(
dlsym(jit_library_handle_, "jit_compile_method"));
if (jit_compile_method_ == nullptr) {
dlclose(jit_library_handle_);
@@ -156,8 +162,9 @@
return true;
}
-bool Jit::CompileMethod(ArtMethod* method, Thread* self) {
+bool Jit::CompileMethod(ArtMethod* method, Thread* self, bool osr) {
DCHECK(!method->IsRuntimeMethod());
+
// Don't compile the method if it has breakpoints.
if (Dbg::IsDebuggerActive() && Dbg::MethodHasAnyBreakpoints(method)) {
VLOG(jit) << "JIT not compiling " << PrettyMethod(method) << " due to breakpoint";
@@ -171,11 +178,15 @@
return false;
}
- if (!code_cache_->NotifyCompilationOf(method, self)) {
+ // If we get a request to compile a proxy method, we pass the actual Java method
+ // of that proxy method, as the compiler does not expect a proxy method.
+ ArtMethod* method_to_compile = method->GetInterfaceMethodIfProxy(sizeof(void*));
+ if (!code_cache_->NotifyCompilationOf(method_to_compile, self, osr)) {
+ VLOG(jit) << "JIT not compiling " << PrettyMethod(method) << " due to code cache";
return false;
}
- bool success = jit_compile_method_(jit_compiler_handle_, method, self);
- code_cache_->DoneCompiling(method, self);
+ bool success = jit_compile_method_(jit_compiler_handle_, method_to_compile, self, osr);
+ code_cache_->DoneCompiling(method_to_compile, self);
return success;
}
@@ -224,9 +235,11 @@
}
}
-void Jit::CreateInstrumentationCache(size_t compile_threshold, size_t warmup_threshold) {
+void Jit::CreateInstrumentationCache(size_t compile_threshold,
+ size_t warmup_threshold,
+ size_t osr_threshold) {
instrumentation_cache_.reset(
- new jit::JitInstrumentationCache(compile_threshold, warmup_threshold));
+ new jit::JitInstrumentationCache(compile_threshold, warmup_threshold, osr_threshold));
}
void Jit::NewTypeLoadedIfUsingJit(mirror::Class* type) {
@@ -255,5 +268,149 @@
}
}
+extern "C" void art_quick_osr_stub(void** stack,
+ uint32_t stack_size_in_bytes,
+ const uint8_t* native_pc,
+ JValue* result,
+ const char* shorty,
+ Thread* self);
+
+bool Jit::MaybeDoOnStackReplacement(Thread* thread,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ int32_t dex_pc_offset,
+ JValue* result) {
+ if (!kEnableOnStackReplacement) {
+ return false;
+ }
+
+ Jit* jit = Runtime::Current()->GetJit();
+ if (jit == nullptr) {
+ return false;
+ }
+
+ if (kRuntimeISA == kMips || kRuntimeISA == kMips64) {
+ VLOG(jit) << "OSR not supported on this platform: " << kRuntimeISA;
+ return false;
+ }
+
+ // Get the actual Java method if this method is from a proxy class. The compiler
+ // and the JIT code cache do not expect methods from proxy classes.
+ method = method->GetInterfaceMethodIfProxy(sizeof(void*));
+
+ // Cheap check if the method has been compiled already. That's an indicator that we should
+ // osr into it.
+ if (!jit->GetCodeCache()->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
+ return false;
+ }
+
+ // Fetch some data before looking up for an OSR method. We don't want thread
+ // suspension once we hold an OSR method, as the JIT code cache could delete the OSR
+ // method while we are being suspended.
+ const size_t number_of_vregs = method->GetCodeItem()->registers_size_;
+ const char* shorty = method->GetShorty();
+ std::string method_name(VLOG_IS_ON(jit) ? PrettyMethod(method) : "");
+ void** memory = nullptr;
+ size_t frame_size = 0;
+ ShadowFrame* shadow_frame = nullptr;
+ const uint8_t* native_pc = nullptr;
+
+ {
+ ScopedAssertNoThreadSuspension sts(thread, "Holding OSR method");
+ const OatQuickMethodHeader* osr_method = jit->GetCodeCache()->LookupOsrMethodHeader(method);
+ if (osr_method == nullptr) {
+ // No osr method yet, just return to the interpreter.
+ return false;
+ }
+
+ CodeInfo code_info = osr_method->GetOptimizedCodeInfo();
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+
+ // Find stack map starting at the target dex_pc.
+ StackMap stack_map = code_info.GetOsrStackMapForDexPc(dex_pc + dex_pc_offset, encoding);
+ if (!stack_map.IsValid()) {
+ // There is no OSR stack map for this dex pc offset. Just return to the interpreter in the
+ // hope that the next branch has one.
+ return false;
+ }
+
+ // We found a stack map, now fill the frame with dex register values from the interpreter's
+ // shadow frame.
+ DexRegisterMap vreg_map =
+ code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_vregs);
+
+ frame_size = osr_method->GetFrameSizeInBytes();
+
+ // Allocate memory to put shadow frame values. The osr stub will copy that memory to
+ // stack.
+ // Note that we could pass the shadow frame to the stub, and let it copy the values there,
+ // but that is engineering complexity not worth the effort for something like OSR.
+ memory = reinterpret_cast<void**>(malloc(frame_size));
+ CHECK(memory != nullptr);
+ memset(memory, 0, frame_size);
+
+ // Art ABI: ArtMethod is at the bottom of the stack.
+ memory[0] = method;
+
+ shadow_frame = thread->PopShadowFrame();
+ if (!vreg_map.IsValid()) {
+ // If we don't have a dex register map, then there are no live dex registers at
+ // this dex pc.
+ } else {
+ for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) {
+ DexRegisterLocation::Kind location =
+ vreg_map.GetLocationKind(vreg, number_of_vregs, code_info, encoding);
+ if (location == DexRegisterLocation::Kind::kNone) {
+ // Dex register is dead or uninitialized.
+ continue;
+ }
+
+ if (location == DexRegisterLocation::Kind::kConstant) {
+ // We skip constants because the compiled code knows how to handle them.
+ continue;
+ }
+
+ DCHECK(location == DexRegisterLocation::Kind::kInStack)
+ << DexRegisterLocation::PrettyDescriptor(location);
+
+ int32_t vreg_value = shadow_frame->GetVReg(vreg);
+ int32_t slot_offset = vreg_map.GetStackOffsetInBytes(vreg,
+ number_of_vregs,
+ code_info,
+ encoding);
+ DCHECK_LT(slot_offset, static_cast<int32_t>(frame_size));
+ DCHECK_GT(slot_offset, 0);
+ (reinterpret_cast<int32_t*>(memory))[slot_offset / sizeof(int32_t)] = vreg_value;
+ }
+ }
+
+ native_pc = stack_map.GetNativePcOffset(encoding) + osr_method->GetEntryPoint();
+ VLOG(jit) << "Jumping to "
+ << method_name
+ << "@"
+ << std::hex << reinterpret_cast<uintptr_t>(native_pc);
+ }
+
+ {
+ ManagedStack fragment;
+ thread->PushManagedStackFragment(&fragment);
+ (*art_quick_osr_stub)(memory,
+ frame_size,
+ native_pc,
+ result,
+ shorty,
+ thread);
+
+ if (UNLIKELY(thread->GetException() == Thread::GetDeoptimizationException())) {
+ thread->DeoptimizeWithDeoptimizationException(result);
+ }
+ thread->PopManagedStackFragment(fragment);
+ }
+ free(memory);
+ thread->PushShadowFrame(shadow_frame);
+ VLOG(jit) << "Done running OSR code for " << method_name;
+ return true;
+}
+
} // namespace jit
} // namespace art
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index a80f51f..042da92 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -49,9 +49,11 @@
virtual ~Jit();
static Jit* Create(JitOptions* options, std::string* error_msg);
- bool CompileMethod(ArtMethod* method, Thread* self)
+ bool CompileMethod(ArtMethod* method, Thread* self, bool osr)
SHARED_REQUIRES(Locks::mutator_lock_);
- void CreateInstrumentationCache(size_t compile_threshold, size_t warmup_threshold);
+ void CreateInstrumentationCache(size_t compile_threshold,
+ size_t warmup_threshold,
+ size_t osr_threshold);
void CreateThreadPool();
CompilerCallbacks* GetCompilerCallbacks() {
return compiler_callbacks_;
@@ -88,6 +90,17 @@
bool JitAtFirstUse();
+ // If an OSR compiled version is available for `method`,
+ // and `dex_pc + dex_pc_offset` is an entry point of that compiled
+ // version, this method will jump to the compiled code, let it run,
+ // and return true afterwards. Return false otherwise.
+ static bool MaybeDoOnStackReplacement(Thread* thread,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ int32_t dex_pc_offset,
+ JValue* result)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
private:
Jit();
bool LoadCompiler(std::string* error_msg);
@@ -97,7 +110,7 @@
void* jit_compiler_handle_;
void* (*jit_load_)(CompilerCallbacks**, bool*);
void (*jit_unload_)(void*);
- bool (*jit_compile_method_)(void*, ArtMethod*, Thread*);
+ bool (*jit_compile_method_)(void*, ArtMethod*, Thread*, bool);
void (*jit_types_loaded_)(void*, mirror::Class**, size_t count);
// Performance monitoring.
@@ -123,6 +136,9 @@
size_t GetWarmupThreshold() const {
return warmup_threshold_;
}
+ size_t GetOsrThreshold() const {
+ return osr_threshold_;
+ }
size_t GetCodeCacheInitialCapacity() const {
return code_cache_initial_capacity_;
}
@@ -155,6 +171,7 @@
size_t code_cache_max_capacity_;
size_t compile_threshold_;
size_t warmup_threshold_;
+ size_t osr_threshold_;
bool dump_info_on_shutdown_;
bool save_profiling_info_;
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index f325949..9111ddf 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -184,7 +184,8 @@
size_t core_spill_mask,
size_t fp_spill_mask,
const uint8_t* code,
- size_t code_size) {
+ size_t code_size,
+ bool osr) {
uint8_t* result = CommitCodeInternal(self,
method,
mapping_table,
@@ -194,7 +195,8 @@
core_spill_mask,
fp_spill_mask,
code,
- code_size);
+ code_size,
+ osr);
if (result == nullptr) {
// Retry.
GarbageCollectCache(self);
@@ -207,7 +209,8 @@
core_spill_mask,
fp_spill_mask,
code,
- code_size);
+ code_size,
+ osr);
}
return result;
}
@@ -287,7 +290,8 @@
size_t core_spill_mask,
size_t fp_spill_mask,
const uint8_t* code,
- size_t code_size) {
+ size_t code_size,
+ bool osr) {
size_t alignment = GetInstructionSetAlignment(kRuntimeISA);
// Ensure the header ends up at expected instruction alignment.
size_t header_size = RoundUp(sizeof(OatQuickMethodHeader), alignment);
@@ -329,8 +333,12 @@
{
MutexLock mu(self, lock_);
method_code_map_.Put(code_ptr, method);
- Runtime::Current()->GetInstrumentation()->UpdateMethodsCode(
- method, method_header->GetEntryPoint());
+ if (osr) {
+ osr_code_map_.Put(method, code_ptr);
+ } else {
+ Runtime::Current()->GetInstrumentation()->UpdateMethodsCode(
+ method, method_header->GetEntryPoint());
+ }
if (collection_in_progress_) {
// We need to update the live bitmap if there is a GC to ensure it sees this new
// code.
@@ -338,7 +346,7 @@
}
last_update_time_ns_.StoreRelease(NanoTime());
VLOG(jit)
- << "JIT added "
+ << "JIT added (osr = " << std::boolalpha << osr << std::noboolalpha << ") "
<< PrettyMethod(method) << "@" << method
<< " ccache_size=" << PrettySize(CodeCacheSizeLocked()) << ": "
<< " dcache_size=" << PrettySize(DataCacheSizeLocked()) << ": "
@@ -569,6 +577,10 @@
info->GetMethod()->SetProfilingInfo(nullptr);
}
}
+
+ // Empty osr method map, as osr compiled code will be deleted (except the ones
+ // on thread stacks).
+ osr_code_map_.clear();
}
// Run a checkpoint on all threads to mark the JIT compiled code they are running.
@@ -662,6 +674,15 @@
return method_header;
}
+OatQuickMethodHeader* JitCodeCache::LookupOsrMethodHeader(ArtMethod* method) {
+ MutexLock mu(Thread::Current(), lock_);
+ auto it = osr_code_map_.find(method);
+ if (it == osr_code_map_.end()) {
+ return nullptr;
+ }
+ return OatQuickMethodHeader::FromCodePointer(it->second);
+}
+
ProfilingInfo* JitCodeCache::AddProfilingInfo(Thread* self,
ArtMethod* method,
const std::vector<uint32_t>& entries,
@@ -733,12 +754,15 @@
return last_update_time_ns_.LoadAcquire();
}
-bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self) {
- if (ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
+bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr) {
+ if (!osr && ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
return false;
}
MutexLock mu(self, lock_);
+ if (osr && (osr_code_map_.find(method) != osr_code_map_.end())) {
+ return false;
+ }
ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*));
if (info == nullptr || info->IsMethodBeingCompiled()) {
return false;
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 69fc553..048f8d0 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -71,7 +71,7 @@
// Number of compilations done throughout the lifetime of the JIT.
size_t NumberOfCompilations() REQUIRES(!lock_);
- bool NotifyCompilationOf(ArtMethod* method, Thread* self)
+ bool NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr)
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!lock_);
@@ -89,7 +89,8 @@
size_t core_spill_mask,
size_t fp_spill_mask,
const uint8_t* code,
- size_t code_size)
+ size_t code_size,
+ bool osr)
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!lock_);
@@ -131,6 +132,10 @@
REQUIRES(!lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
+ OatQuickMethodHeader* LookupOsrMethodHeader(ArtMethod* method)
+ REQUIRES(!lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
// Remove all methods in our cache that were allocated by 'alloc'.
void RemoveMethodsIn(Thread* self, const LinearAlloc& alloc)
REQUIRES(!lock_)
@@ -187,7 +192,8 @@
size_t core_spill_mask,
size_t fp_spill_mask,
const uint8_t* code,
- size_t code_size)
+ size_t code_size,
+ bool osr)
REQUIRES(!lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -237,8 +243,10 @@
void* data_mspace_ GUARDED_BY(lock_);
// Bitmap for collecting code and data.
std::unique_ptr<CodeCacheBitmap> live_bitmap_;
- // This map holds compiled code associated to the ArtMethod.
+ // Holds compiled code associated to the ArtMethod.
SafeMap<const void*, ArtMethod*> method_code_map_ GUARDED_BY(lock_);
+ // Holds osr compiled code associated to the ArtMethod.
+ SafeMap<ArtMethod*, const void*> osr_code_map_ GUARDED_BY(lock_);
// ProfilingInfo objects we have allocated.
std::vector<ProfilingInfo*> profiling_infos_ GUARDED_BY(lock_);
diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc
index d597b36..a4e40ad 100644
--- a/runtime/jit/jit_instrumentation.cc
+++ b/runtime/jit/jit_instrumentation.cc
@@ -29,7 +29,8 @@
public:
enum TaskKind {
kAllocateProfile,
- kCompile
+ kCompile,
+ kCompileOsr
};
JitCompileTask(ArtMethod* method, TaskKind kind) : method_(method), kind_(kind) {
@@ -48,9 +49,14 @@
ScopedObjectAccess soa(self);
if (kind_ == kCompile) {
VLOG(jit) << "JitCompileTask compiling method " << PrettyMethod(method_);
- if (!Runtime::Current()->GetJit()->CompileMethod(method_, self)) {
+ if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ false)) {
VLOG(jit) << "Failed to compile method " << PrettyMethod(method_);
}
+ } else if (kind_ == kCompileOsr) {
+ VLOG(jit) << "JitCompileTask compiling method osr " << PrettyMethod(method_);
+ if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ true)) {
+ VLOG(jit) << "Failed to compile method osr " << PrettyMethod(method_);
+ }
} else {
DCHECK(kind_ == kAllocateProfile);
if (ProfilingInfo::Create(self, method_, /* retry_allocation */ true)) {
@@ -72,9 +78,11 @@
};
JitInstrumentationCache::JitInstrumentationCache(size_t hot_method_threshold,
- size_t warm_method_threshold)
+ size_t warm_method_threshold,
+ size_t osr_method_threshold)
: hot_method_threshold_(hot_method_threshold),
warm_method_threshold_(warm_method_threshold),
+ osr_method_threshold_(osr_method_threshold),
listener_(this) {
}
@@ -151,6 +159,11 @@
DCHECK(thread_pool_ != nullptr);
thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
}
+
+ if (sample_count == osr_method_threshold_) {
+ DCHECK(thread_pool_ != nullptr);
+ thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr));
+ }
}
JitInstrumentationListener::JitInstrumentationListener(JitInstrumentationCache* cache)
diff --git a/runtime/jit/jit_instrumentation.h b/runtime/jit/jit_instrumentation.h
index 06559ad..d1c5c44 100644
--- a/runtime/jit/jit_instrumentation.h
+++ b/runtime/jit/jit_instrumentation.h
@@ -96,7 +96,9 @@
// Keeps track of which methods are hot.
class JitInstrumentationCache {
public:
- JitInstrumentationCache(size_t hot_method_threshold, size_t warm_method_threshold);
+ JitInstrumentationCache(size_t hot_method_threshold,
+ size_t warm_method_threshold,
+ size_t osr_method_threshold);
void AddSamples(Thread* self, ArtMethod* method, size_t samples)
SHARED_REQUIRES(Locks::mutator_lock_);
void CreateThreadPool();
@@ -112,6 +114,7 @@
private:
size_t hot_method_threshold_;
size_t warm_method_threshold_;
+ size_t osr_method_threshold_;
JitInstrumentationListener listener_;
std::unique_ptr<ThreadPool> thread_pool_;
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc
index b4b872f..0aff1f7 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -33,6 +33,21 @@
namespace art {
+// Transform the actual dex location into relative paths.
+// Note: this is OK because we don't store profiles of different apps into the same file.
+// Apps with split apks don't cause trouble because each split has a different name and will not
+// collide with other entries.
+std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_location) {
+ DCHECK(!dex_location.empty());
+ size_t last_sep_index = dex_location.find_last_of('/');
+ if (last_sep_index == std::string::npos) {
+ return dex_location;
+ } else {
+ DCHECK(last_sep_index < dex_location.size());
+ return dex_location.substr(last_sep_index + 1);
+ }
+}
+
bool ProfileCompilationInfo::SaveProfilingInfo(const std::string& filename,
const std::vector<ArtMethod*>& methods) {
if (methods.empty()) {
@@ -58,7 +73,7 @@
ScopedObjectAccess soa(Thread::Current());
for (auto it = methods.begin(); it != methods.end(); it++) {
const DexFile* dex_file = (*it)->GetDexFile();
- if (!info.AddData(dex_file->GetLocation(),
+ if (!info.AddData(GetProfileDexFileKey(dex_file->GetLocation()),
dex_file->GetLocationChecksum(),
(*it)->GetDexMethodIndex())) {
return false;
@@ -107,8 +122,8 @@
* dex_location1,dex_location_checksum1,method_id11,method_id12...
* dex_location2,dex_location_checksum2,method_id21,method_id22...
* e.g.
- * /system/priv-app/app/app.apk,131232145,11,23,454,54
- * /system/priv-app/app/app.apk:classes5.dex,218490184,39,13,49,1
+ * app.apk,131232145,11,23,454,54
+ * app.apk:classes5.dex,218490184,39,13,49,1
**/
bool ProfileCompilationInfo::Save(uint32_t fd) {
DCHECK_GE(fd, 0u);
@@ -270,7 +285,7 @@
}
bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const {
- auto info_it = info_.find(method_ref.dex_file->GetLocation());
+ auto info_it = info_.find(GetProfileDexFileKey(method_ref.dex_file->GetLocation()));
if (info_it != info_.end()) {
if (method_ref.dex_file->GetLocationChecksum() != info_it->second.checksum) {
return false;
diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h
index ffd1433..c388c4a 100644
--- a/runtime/jit/offline_profiling_info.h
+++ b/runtime/jit/offline_profiling_info.h
@@ -66,6 +66,8 @@
// For testing purposes.
bool Equals(ProfileCompilationInfo& other);
+ // Exposed for testing purpose.
+ static std::string GetProfileDexFileKey(const std::string& dex_location);
private:
bool AddData(const std::string& dex_location, uint32_t checksum, uint16_t method_idx);
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 18c52e4..c908b39 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -877,4 +877,22 @@
return os;
}
+void MemMap::TryReadable() {
+ if (base_begin_ == nullptr && base_size_ == 0) {
+ return;
+ }
+ CHECK_NE(prot_ & PROT_READ, 0);
+ volatile uint8_t* begin = reinterpret_cast<volatile uint8_t*>(base_begin_);
+ volatile uint8_t* end = begin + base_size_;
+ DCHECK(IsAligned<kPageSize>(begin));
+ DCHECK(IsAligned<kPageSize>(end));
+ // Read the first byte of each page. Use volatile to prevent the compiler from optimizing away the
+ // reads.
+ for (volatile uint8_t* ptr = begin; ptr < end; ptr += kPageSize) {
+ // This read could fault if protection wasn't set correctly.
+ uint8_t value = *ptr;
+ UNUSED(value);
+ }
+}
+
} // namespace art
diff --git a/runtime/mem_map.h b/runtime/mem_map.h
index ebd550a..3eaf576 100644
--- a/runtime/mem_map.h
+++ b/runtime/mem_map.h
@@ -184,6 +184,11 @@
static void Init() REQUIRES(!Locks::mem_maps_lock_);
static void Shutdown() REQUIRES(!Locks::mem_maps_lock_);
+ // If the map is PROT_READ, try to read each page of the map to check it is in fact readable (not
+ // faulting). This is used to diagnose a bug b/19894268 where mprotect doesn't seem to be working
+ // intermittently.
+ void TryReadable();
+
private:
MemMap(const std::string& name,
uint8_t* begin,
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index b97d994..cdc6204 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -800,11 +800,11 @@
return nullptr;
}
-void Class::SetPreverifiedFlagOnAllMethods(size_t pointer_size) {
+void Class::SetSkipAccessChecksFlagOnAllMethods(size_t pointer_size) {
DCHECK(IsVerified());
for (auto& m : GetMethods(pointer_size)) {
if (!m.IsNative() && m.IsInvokable()) {
- m.SetPreverified();
+ m.SetSkipAccessChecks();
}
}
}
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 8fa4975..388a231 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -287,14 +287,18 @@
return (GetAccessFlags() & kAccSynthetic) != 0;
}
- // Returns true if the class can avoid access checks.
- bool IsPreverified() SHARED_REQUIRES(Locks::mutator_lock_) {
- return (GetAccessFlags() & kAccPreverified) != 0;
+ // Return whether the class had run the verifier at least once.
+ // This does not necessarily mean that access checks are avoidable,
+ // since the class methods might still need to be run with access checks.
+ bool WasVerificationAttempted() SHARED_REQUIRES(Locks::mutator_lock_) {
+ return (GetAccessFlags() & kAccSkipAccessChecks) != 0;
}
- void SetPreverified() SHARED_REQUIRES(Locks::mutator_lock_) {
+ // Mark the class as having gone through a verification attempt.
+ // Mutually exclusive from whether or not each method is allowed to skip access checks.
+ void SetVerificationAttempted() SHARED_REQUIRES(Locks::mutator_lock_) {
uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_));
- SetAccessFlags(flags | kAccPreverified);
+ SetAccessFlags(flags | kAccVerificationAttempted);
}
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
@@ -562,7 +566,7 @@
// The size of java.lang.Class.class.
static uint32_t ClassClassSize(size_t pointer_size) {
// The number of vtable entries in java.lang.Class.
- uint32_t vtable_entries = Object::kVTableLength + 69;
+ uint32_t vtable_entries = Object::kVTableLength + 72;
return ComputeClassSize(true, vtable_entries, 0, 0, 4, 1, 0, pointer_size);
}
@@ -1136,8 +1140,8 @@
void VisitNativeRoots(Visitor& visitor, size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
- // When class is verified, set the kAccPreverified flag on each method.
- void SetPreverifiedFlagOnAllMethods(size_t pointer_size)
+ // When class is verified, set the kAccSkipAccessChecks flag on each method.
+ void SetSkipAccessChecksFlagOnAllMethods(size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
// Get the descriptor of the class. In a few cases a std::string is required, rather than
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index 9946eab..ed4c5fc 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -42,14 +42,16 @@
static constexpr uint32_t kAccJavaFlagsMask = 0xffff; // bits set from Java sources (low 16)
-static constexpr uint32_t kAccConstructor = 0x00010000; // method (dex only) <(cl)init>
-static constexpr uint32_t kAccDeclaredSynchronized = 0x00020000; // method (dex only)
-static constexpr uint32_t kAccClassIsProxy = 0x00040000; // class (dex only)
-static constexpr uint32_t kAccPreverified = 0x00080000; // class (runtime),
- // method (dex only)
-static constexpr uint32_t kAccFastNative = 0x00080000; // method (dex only)
-static constexpr uint32_t kAccMiranda = 0x00200000; // method (dex only)
-static constexpr uint32_t kAccDefault = 0x00400000; // method (runtime)
+static constexpr uint32_t kAccConstructor = 0x00010000; // method (dex only) <(cl)init>
+static constexpr uint32_t kAccDeclaredSynchronized = 0x00020000; // method (dex only)
+static constexpr uint32_t kAccClassIsProxy = 0x00040000; // class (dex only)
+// Used by a method to denote that its execution does not need to go through slow path interpreter.
+static constexpr uint32_t kAccSkipAccessChecks = 0x00080000; // method (dex only)
+// Used by a class to denote that the verifier has attempted to check it at least once.
+static constexpr uint32_t kAccVerificationAttempted = 0x00080000; // class (runtime)
+static constexpr uint32_t kAccFastNative = 0x00080000; // method (dex only)
+static constexpr uint32_t kAccMiranda = 0x00200000; // method (dex only)
+static constexpr uint32_t kAccDefault = 0x00400000; // method (runtime)
// This is set by the class linker during LinkInterfaceMethods. Prior to that point we do not know
// if any particular method needs to be a default conflict. Used to figure out at runtime if
// invoking this method will throw an exception.
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index a7881ac..a092b9f 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -66,6 +66,7 @@
DEBUG_ENABLE_JNI_LOGGING = 1 << 4,
DEBUG_GENERATE_DEBUG_INFO = 1 << 5,
DEBUG_ALWAYS_JIT = 1 << 6,
+ DEBUG_NATIVE_DEBUGGABLE = 1 << 7,
};
Runtime* const runtime = Runtime::Current();
@@ -117,6 +118,11 @@
debug_flags &= ~DEBUG_ALWAYS_JIT;
}
+ if ((debug_flags & DEBUG_NATIVE_DEBUGGABLE) != 0) {
+ runtime->AddCompilerOption("--native-debuggable");
+ debug_flags &= ~DEBUG_NATIVE_DEBUGGABLE;
+ }
+
if (debug_flags != 0) {
LOG(ERROR) << StringPrintf("Unknown bits set in debug_flags: %#x", debug_flags);
}
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 0ddd4a2..b5d859b 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -388,7 +388,7 @@
auto h_args = hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Class>*>(args));
Handle<mirror::Class> h_klass = hs.NewHandle(DecodeClass(soa, javaThis));
ArtMethod* result = nullptr;
- for (auto& m : h_klass->GetVirtualMethods(sizeof(void*))) {
+ for (auto& m : h_klass->GetDeclaredVirtualMethods(sizeof(void*))) {
auto* np_method = m.GetInterfaceMethodIfProxy(sizeof(void*));
// May cause thread suspension.
mirror::String* np_name = np_method->GetNameAsString(soa.Self());
@@ -469,14 +469,21 @@
return soa.AddLocalReference<jobjectArray>(ret.Get());
}
-static jobject Class_getDeclaredAnnotation(JNIEnv* env, jobject javaThis, jclass annotationType) {
+static jobject Class_getDeclaredAnnotation(JNIEnv* env, jobject javaThis, jclass annotationClass) {
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<2> hs(soa.Self());
Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
+
+ // Handle public contract to throw NPE if the "annotationClass" argument was null.
+ if (UNLIKELY(annotationClass == nullptr)) {
+ ThrowNullPointerException("annotationClass");
+ return nullptr;
+ }
+
if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
return nullptr;
}
- Handle<mirror::Class> annotation_class(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
+ Handle<mirror::Class> annotation_class(hs.NewHandle(soa.Decode<mirror::Class*>(annotationClass)));
return soa.AddLocalReference<jobject>(
klass->GetDexFile().GetAnnotationForClass(klass, annotation_class));
}
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index de90f0a..18cf81a 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -24,6 +24,7 @@
#include "base/stl_util.h"
#include "class_linker.h"
#include "dex_file-inl.h"
+#include "gc/scoped_gc_critical_section.h"
#include "gc/space/image_space.h"
#include "handle_scope-inl.h"
#include "mirror/class_loader.h"
@@ -379,27 +380,36 @@
// spaces array.
{
ScopedThreadSuspension sts(self, kSuspended);
+ gc::ScopedGCCriticalSection gcs(self,
+ gc::kGcCauseAddRemoveAppImageSpace,
+ gc::kCollectorTypeAddRemoveAppImageSpace);
ScopedSuspendAll ssa("Add image space");
runtime->GetHeap()->AddSpace(image_space.get());
}
added_image_space = true;
- if (!runtime->GetClassLinker()->AddImageSpace(image_space.get(),
- h_loader,
- dex_elements,
- dex_location,
- /*out*/&dex_files,
- /*out*/&temp_error_msg)) {
+ if (runtime->GetClassLinker()->AddImageSpace(image_space.get(),
+ h_loader,
+ dex_elements,
+ dex_location,
+ /*out*/&dex_files,
+ /*out*/&temp_error_msg)) {
+ // Successfully added image space to heap, release the map so that it does not get
+ // freed.
+ image_space.release();
+ } else {
LOG(INFO) << "Failed to add image file " << temp_error_msg;
dex_files.clear();
{
ScopedThreadSuspension sts(self, kSuspended);
+ gc::ScopedGCCriticalSection gcs(self,
+ gc::kGcCauseAddRemoveAppImageSpace,
+ gc::kCollectorTypeAddRemoveAppImageSpace);
ScopedSuspendAll ssa("Remove image space");
runtime->GetHeap()->RemoveSpace(image_space.get());
}
added_image_space = false;
// Non-fatal, don't update error_msg.
}
- image_space.release();
}
}
}
diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h
index 5643739..2b7eca2 100644
--- a/runtime/oat_quick_method_header.h
+++ b/runtime/oat_quick_method_header.h
@@ -108,7 +108,7 @@
}
template <bool kCheckFrameSize = true>
- uint32_t GetFrameSizeInBytes() {
+ uint32_t GetFrameSizeInBytes() const {
uint32_t result = frame_info_.FrameSizeInBytes();
if (kCheckFrameSize) {
DCHECK_LE(static_cast<size_t>(kStackAlignment), result);
diff --git a/runtime/openjdkjvm/Android.mk b/runtime/openjdkjvm/Android.mk
new file mode 100644
index 0000000..9b7404e
--- /dev/null
+++ b/runtime/openjdkjvm/Android.mk
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := openjdkjvm-phony
+include $(BUILD_PHONY_PACKAGE)
diff --git a/runtime/openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION b/runtime/openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/runtime/openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION
diff --git a/runtime/openjdkjvm/OpenjdkJvm.cc b/runtime/openjdkjvm/OpenjdkJvm.cc
index ab0d934..725067a 100644
--- a/runtime/openjdkjvm/OpenjdkJvm.cc
+++ b/runtime/openjdkjvm/OpenjdkJvm.cc
@@ -66,17 +66,13 @@
#undef LOG_TAG
#define LOG_TAG "artopenjdk"
-using art::DEBUG;
using art::WARNING;
-using art::VERBOSE;
using art::INFO;
using art::ERROR;
using art::FATAL;
/* posix open() with extensions; used by e.g. ZipFile */
JNIEXPORT jint JVM_Open(const char* fname, jint flags, jint mode) {
- LOG(DEBUG) << "JVM_Open fname='" << fname << "', flags=" << flags << ", mode=" << mode;
-
/*
* The call is expected to handle JVM_O_DELETE, which causes the file
* to be removed after it is opened. Also, some code seems to
@@ -86,7 +82,6 @@
int fd = TEMP_FAILURE_RETRY(open(fname, flags & ~JVM_O_DELETE, mode));
if (fd < 0) {
int err = errno;
- LOG(DEBUG) << "open(" << fname << ") failed: " << strerror(errno);
if (err == EEXIST) {
return JVM_EEXIST;
} else {
@@ -95,39 +90,32 @@
}
if (flags & JVM_O_DELETE) {
- LOG(DEBUG) << "Deleting '" << fname << "' after open\n";
if (unlink(fname) != 0) {
LOG(WARNING) << "Post-open deletion of '" << fname << "' failed: " << strerror(errno);
}
- /* ignore */
}
- LOG(VERBOSE) << "open(" << fname << ") --> " << fd;
return fd;
}
/* posix close() */
JNIEXPORT jint JVM_Close(jint fd) {
- LOG(DEBUG) << "JVM_Close fd=" << fd;
// don't want TEMP_FAILURE_RETRY here -- file is closed even if EINTR
return close(fd);
}
/* posix read() */
JNIEXPORT jint JVM_Read(jint fd, char* buf, jint nbytes) {
- LOG(DEBUG) << "JVM_Read fd=" << fd << ", buf='" << buf << "', nbytes=" << nbytes;
return TEMP_FAILURE_RETRY(read(fd, buf, nbytes));
}
/* posix write(); is used to write messages to stderr */
JNIEXPORT jint JVM_Write(jint fd, char* buf, jint nbytes) {
- LOG(DEBUG) << "JVM_Write fd=" << fd << ", buf='" << buf << "', nbytes=" << nbytes;
return TEMP_FAILURE_RETRY(write(fd, buf, nbytes));
}
/* posix lseek() */
JNIEXPORT jlong JVM_Lseek(jint fd, jlong offset, jint whence) {
- LOG(DEBUG) << "JVM_Lseek fd=" << fd << ", offset=" << offset << ", whence=" << whence;
return TEMP_FAILURE_RETRY(lseek(fd, offset, whence));
}
@@ -136,42 +124,41 @@
* mutexes. They're used by ZipFile.
*/
JNIEXPORT void* JVM_RawMonitorCreate(void) {
- LOG(DEBUG) << "JVM_RawMonitorCreate";
- pthread_mutex_t* newMutex =
+ pthread_mutex_t* mutex =
reinterpret_cast<pthread_mutex_t*>(malloc(sizeof(pthread_mutex_t)));
- pthread_mutex_init(newMutex, NULL);
- return newMutex;
+ CHECK(mutex != nullptr);
+ CHECK_PTHREAD_CALL(pthread_mutex_init, (mutex, nullptr), "JVM_RawMonitorCreate");
+ return mutex;
}
JNIEXPORT void JVM_RawMonitorDestroy(void* mon) {
- LOG(DEBUG) << "JVM_RawMonitorDestroy mon=" << mon;
- pthread_mutex_destroy(reinterpret_cast<pthread_mutex_t*>(mon));
+ CHECK_PTHREAD_CALL(pthread_mutex_destroy,
+ (reinterpret_cast<pthread_mutex_t*>(mon)),
+ "JVM_RawMonitorDestroy");
+ free(mon);
}
JNIEXPORT jint JVM_RawMonitorEnter(void* mon) {
- LOG(DEBUG) << "JVM_RawMonitorEnter mon=" << mon;
return pthread_mutex_lock(reinterpret_cast<pthread_mutex_t*>(mon));
}
JNIEXPORT void JVM_RawMonitorExit(void* mon) {
- LOG(DEBUG) << "JVM_RawMonitorExit mon=" << mon;
- pthread_mutex_unlock(reinterpret_cast<pthread_mutex_t*>(mon));
+ CHECK_PTHREAD_CALL(pthread_mutex_unlock,
+ (reinterpret_cast<pthread_mutex_t*>(mon)),
+ "JVM_RawMonitorExit");
}
JNIEXPORT char* JVM_NativePath(char* path) {
- LOG(DEBUG) << "JVM_NativePath path='" << path << "'";
return path;
}
JNIEXPORT jint JVM_GetLastErrorString(char* buf, int len) {
#if defined(__GLIBC__) || defined(__BIONIC__)
- int err = errno; // grab before JVM_TRACE can trash it
- LOG(DEBUG) << "JVM_GetLastErrorString buf=" << buf << ", len=" << len;
-
if (len == 0) {
return 0;
}
+ const int err = errno;
char* result = strerror_r(err, buf, len);
if (result != buf) {
strncpy(buf, result, len);
@@ -203,27 +190,22 @@
/* posix fsync() */
JNIEXPORT jint JVM_Sync(jint fd) {
- LOG(DEBUG) << "JVM_Sync fd=" << fd;
return TEMP_FAILURE_RETRY(fsync(fd));
}
JNIEXPORT void* JVM_FindLibraryEntry(void* handle, const char* name) {
- LOG(DEBUG) << "JVM_FindLibraryEntry handle=" << handle << " name=" << name;
return dlsym(handle, name);
}
-JNIEXPORT jlong JVM_CurrentTimeMillis(JNIEnv* env, jclass clazz ATTRIBUTE_UNUSED) {
- LOG(DEBUG) << "JVM_CurrentTimeMillis env=" << env;
+JNIEXPORT jlong JVM_CurrentTimeMillis(JNIEnv* env ATTRIBUTE_UNUSED,
+ jclass clazz ATTRIBUTE_UNUSED) {
struct timeval tv;
-
gettimeofday(&tv, (struct timezone *) NULL);
jlong when = tv.tv_sec * 1000LL + tv.tv_usec / 1000;
return when;
}
JNIEXPORT jint JVM_Socket(jint domain, jint type, jint protocol) {
- LOG(DEBUG) << "JVM_Socket domain=" << domain << ", type=" << type << ", protocol=" << protocol;
-
return TEMP_FAILURE_RETRY(socket(domain, type, protocol));
}
@@ -247,21 +229,15 @@
JNIEXPORT jint JVM_SetSockOpt(jint fd, int level, int optname,
const char* optval, int optlen) {
- LOG(DEBUG) << "JVM_SetSockOpt fd=" << fd << ", level=" << level << ", optname=" << optname
- << ", optval=" << optval << ", optlen=" << optlen;
return TEMP_FAILURE_RETRY(setsockopt(fd, level, optname, optval, optlen));
}
JNIEXPORT jint JVM_SocketShutdown(jint fd, jint howto) {
- LOG(DEBUG) << "JVM_SocketShutdown fd=" << fd << ", howto=" << howto;
return TEMP_FAILURE_RETRY(shutdown(fd, howto));
}
JNIEXPORT jint JVM_GetSockOpt(jint fd, int level, int optname, char* optval,
int* optlen) {
- LOG(DEBUG) << "JVM_GetSockOpt fd=" << fd << ", level=" << level << ", optname=" << optname
- << ", optval=" << optval << ", optlen=" << optlen;
-
socklen_t len = *optlen;
int cc = TEMP_FAILURE_RETRY(getsockopt(fd, level, optname, optval, &len));
*optlen = len;
@@ -269,8 +245,6 @@
}
JNIEXPORT jint JVM_GetSockName(jint fd, struct sockaddr* addr, int* addrlen) {
- LOG(DEBUG) << "JVM_GetSockName fd=" << fd << ", addr=" << addr << ", addrlen=" << addrlen;
-
socklen_t len = *addrlen;
int cc = TEMP_FAILURE_RETRY(getsockname(fd, addr, &len));
*addrlen = len;
@@ -278,10 +252,7 @@
}
JNIEXPORT jint JVM_SocketAvailable(jint fd, jint* result) {
- LOG(DEBUG) << "JVM_SocketAvailable fd=" << fd << ", result=" << result;
-
if (TEMP_FAILURE_RETRY(ioctl(fd, FIONREAD, result)) < 0) {
- LOG(DEBUG) << "ioctl(" << fd << ", FIONREAD) failed: " << strerror(errno);
return JNI_FALSE;
}
@@ -289,39 +260,27 @@
}
JNIEXPORT jint JVM_Send(jint fd, char* buf, jint nBytes, jint flags) {
- LOG(DEBUG) << "JVM_Send fd=" << fd << ", buf=" << buf << ", nBytes="
- << nBytes << ", flags=" << flags;
-
return TEMP_FAILURE_RETRY(send(fd, buf, nBytes, flags));
}
JNIEXPORT jint JVM_SocketClose(jint fd) {
- LOG(DEBUG) << "JVM_SocketClose fd=" << fd;
-
- // don't want TEMP_FAILURE_RETRY here -- file is closed even if EINTR
+ // Don't want TEMP_FAILURE_RETRY here -- file is closed even if EINTR.
return close(fd);
}
JNIEXPORT jint JVM_Listen(jint fd, jint count) {
- LOG(DEBUG) << "JVM_Listen fd=" << fd << ", count=" << count;
-
return TEMP_FAILURE_RETRY(listen(fd, count));
}
JNIEXPORT jint JVM_Connect(jint fd, struct sockaddr* addr, jint addrlen) {
- LOG(DEBUG) << "JVM_Connect fd=" << fd << ", addr=" << addr << ", addrlen=" << addrlen;
-
return TEMP_FAILURE_RETRY(connect(fd, addr, addrlen));
}
JNIEXPORT int JVM_GetHostName(char* name, int namelen) {
- LOG(DEBUG) << "JVM_GetHostName name=" << name << ", namelen=" << namelen;
-
return TEMP_FAILURE_RETRY(gethostname(name, namelen));
}
JNIEXPORT jstring JVM_InternString(JNIEnv* env, jstring jstr) {
- LOG(DEBUG) << "JVM_InternString env=" << env << ", jstr=" << jstr;
art::ScopedFastNativeObjectAccess soa(env);
art::mirror::String* s = soa.Decode<art::mirror::String*>(jstr);
art::mirror::String* result = s->Intern();
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index aa64ee3..f9d916a 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -146,6 +146,10 @@
.Define({"-XX:EnableHSpaceCompactForOOM", "-XX:DisableHSpaceCompactForOOM"})
.WithValues({true, false})
.IntoKey(M::EnableHSpaceCompactForOOM)
+ .Define("-XX:DumpNativeStackOnSigQuit:_")
+ .WithType<bool>()
+ .WithValueMap({{"false", false}, {"true", true}})
+ .IntoKey(M::DumpNativeStackOnSigQuit)
.Define("-Xusejit:_")
.WithType<bool>()
.WithValueMap({{"false", false}, {"true", true}})
@@ -277,6 +281,8 @@
.WithType<ExperimentalFlags>()
.AppendValues()
.IntoKey(M::Experimental)
+ .Define("-Xforce-nb-testing")
+ .IntoKey(M::ForceNativeBridge)
.Ignore({
"-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
"-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
@@ -665,6 +671,7 @@
UsageMessage(stream, " -XX:BackgroundGC=none\n");
UsageMessage(stream, " -XX:LargeObjectSpace={disabled,map,freelist}\n");
UsageMessage(stream, " -XX:LargeObjectThreshold=N\n");
+ UsageMessage(stream, " -XX:DumpNativeStackOnSigQuit=booleanvalue\n");
UsageMessage(stream, " -Xmethod-trace\n");
UsageMessage(stream, " -Xmethod-trace-file:filename");
UsageMessage(stream, " -Xmethod-trace-file-size:integervalue\n");
diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc
index 6b84c8f..9b10f2e 100644
--- a/runtime/quick/inline_method_analyser.cc
+++ b/runtime/quick/inline_method_analyser.cc
@@ -22,6 +22,7 @@
#include "dex_file-inl.h"
#include "dex_instruction.h"
#include "dex_instruction-inl.h"
+#include "dex_instruction_utils.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache-inl.h"
#include "verifier/method_verifier-inl.h"
@@ -33,6 +34,366 @@
namespace art {
+namespace { // anonymous namespace
+
+// Helper class for matching a pattern.
+class Matcher {
+ public:
+ // Match function type.
+ typedef bool MatchFn(Matcher* matcher);
+
+ template <size_t size>
+ static bool Match(const DexFile::CodeItem* code_item, MatchFn* const (&pattern)[size]);
+
+ // Match and advance.
+
+ static bool Mark(Matcher* matcher);
+
+ template <bool (Matcher::*Fn)()>
+ static bool Required(Matcher* matcher);
+
+ template <bool (Matcher::*Fn)()>
+ static bool Repeated(Matcher* matcher); // On match, returns to the mark.
+
+ // Match an individual instruction.
+
+ template <Instruction::Code opcode> bool Opcode();
+ bool Const0();
+ bool IPutOnThis();
+
+ private:
+ explicit Matcher(const DexFile::CodeItem* code_item)
+ : code_item_(code_item),
+ instruction_(Instruction::At(code_item->insns_)),
+ pos_(0u),
+ mark_(0u) { }
+
+ static bool DoMatch(const DexFile::CodeItem* code_item, MatchFn* const* pattern, size_t size);
+
+ const DexFile::CodeItem* const code_item_;
+ const Instruction* instruction_;
+ size_t pos_;
+ size_t mark_;
+};
+
+template <size_t size>
+bool Matcher::Match(const DexFile::CodeItem* code_item, MatchFn* const (&pattern)[size]) {
+ return DoMatch(code_item, pattern, size);
+}
+
+bool Matcher::Mark(Matcher* matcher) {
+ matcher->pos_ += 1u; // Advance to the next match function before marking.
+ matcher->mark_ = matcher->pos_;
+ return true;
+}
+
+template <bool (Matcher::*Fn)()>
+bool Matcher::Required(Matcher* matcher) {
+ if (!(matcher->*Fn)()) {
+ return false;
+ }
+ matcher->pos_ += 1u;
+ matcher->instruction_ = matcher->instruction_->Next();
+ return true;
+}
+
+template <bool (Matcher::*Fn)()>
+bool Matcher::Repeated(Matcher* matcher) {
+ if (!(matcher->*Fn)()) {
+ // Didn't match optional instruction, try the next match function.
+ matcher->pos_ += 1u;
+ return true;
+ }
+ matcher->pos_ = matcher->mark_;
+ matcher->instruction_ = matcher->instruction_->Next();
+ return true;
+}
+
+template <Instruction::Code opcode>
+bool Matcher::Opcode() {
+ return instruction_->Opcode() == opcode;
+}
+
+// Match const 0.
+bool Matcher::Const0() {
+ return IsInstructionDirectConst(instruction_->Opcode()) &&
+ (instruction_->Opcode() == Instruction::CONST_WIDE ? instruction_->VRegB_51l() == 0
+ : instruction_->VRegB() == 0);
+}
+
+bool Matcher::IPutOnThis() {
+ DCHECK_NE(code_item_->ins_size_, 0u);
+ return IsInstructionIPut(instruction_->Opcode()) &&
+ instruction_->VRegB_22c() == code_item_->registers_size_ - code_item_->ins_size_;
+}
+
+bool Matcher::DoMatch(const DexFile::CodeItem* code_item, MatchFn* const* pattern, size_t size) {
+ Matcher matcher(code_item);
+ while (matcher.pos_ != size) {
+ if (!pattern[matcher.pos_](&matcher)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// Used for a single invoke in a constructor. In that situation, the method verifier makes
+// sure we invoke a constructor either in the same class or superclass with at least "this".
+ArtMethod* GetTargetConstructor(ArtMethod* method, const Instruction* invoke_direct)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK_EQ(invoke_direct->Opcode(), Instruction::INVOKE_DIRECT);
+ DCHECK_EQ(invoke_direct->VRegC_35c(),
+ method->GetCodeItem()->registers_size_ - method->GetCodeItem()->ins_size_);
+ uint32_t method_index = invoke_direct->VRegB_35c();
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ ArtMethod* target_method =
+ method->GetDexCache()->GetResolvedMethod(method_index, pointer_size);
+ if (kIsDebugBuild && target_method != nullptr) {
+ CHECK(!target_method->IsStatic());
+ CHECK(target_method->IsConstructor());
+ CHECK(target_method->GetDeclaringClass() == method->GetDeclaringClass() ||
+ target_method->GetDeclaringClass() == method->GetDeclaringClass()->GetSuperClass());
+ }
+ return target_method;
+}
+
+// Return the forwarded arguments and check that all remaining arguments are zero.
+// If the check fails, return static_cast<size_t>(-1).
+size_t CountForwardedConstructorArguments(const DexFile::CodeItem* code_item,
+ const Instruction* invoke_direct,
+ uint16_t zero_vreg_mask) {
+ DCHECK_EQ(invoke_direct->Opcode(), Instruction::INVOKE_DIRECT);
+ size_t number_of_args = invoke_direct->VRegA_35c();
+ DCHECK_NE(number_of_args, 0u);
+ uint32_t args[Instruction::kMaxVarArgRegs];
+ invoke_direct->GetVarArgs(args);
+ uint16_t this_vreg = args[0];
+ DCHECK_EQ(this_vreg, code_item->registers_size_ - code_item->ins_size_); // Checked by verifier.
+ size_t forwarded = 1u;
+ while (forwarded < number_of_args &&
+ args[forwarded] == this_vreg + forwarded &&
+ (zero_vreg_mask & (1u << args[forwarded])) == 0) {
+ ++forwarded;
+ }
+ for (size_t i = forwarded; i != number_of_args; ++i) {
+ if ((zero_vreg_mask & (1u << args[i])) == 0) {
+ return static_cast<size_t>(-1);
+ }
+ }
+ return forwarded;
+}
+
+uint16_t GetZeroVRegMask(const Instruction* const0) {
+ DCHECK(IsInstructionDirectConst(const0->Opcode()));
+ DCHECK((const0->Opcode() == Instruction::CONST_WIDE) ? const0->VRegB_51l() == 0u
+ : const0->VRegB() == 0);
+ uint16_t base_mask = IsInstructionConstWide(const0->Opcode()) ? 3u : 1u;
+ return base_mask << const0->VRegA();
+}
+
+// We limit the number of IPUTs storing parameters. There can be any number
+// of IPUTs that store the value 0 as they are useless in a constructor as
+// the object always starts zero-initialized. We also eliminate all but the
+// last store to any field as they are not observable; not even if the field
+// is volatile as no reference to the object can escape from a constructor
+// with this pattern.
+static constexpr size_t kMaxConstructorIPuts = 3u;
+
+struct ConstructorIPutData {
+ ConstructorIPutData() : field_index(DexFile::kDexNoIndex16), arg(0u) { }
+
+ uint16_t field_index;
+ uint16_t arg;
+};
+
+bool RecordConstructorIPut(ArtMethod* method,
+ const Instruction* new_iput,
+ uint16_t this_vreg,
+ uint16_t zero_vreg_mask,
+ /*inout*/ ConstructorIPutData (&iputs)[kMaxConstructorIPuts])
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(IsInstructionIPut(new_iput->Opcode()));
+ uint32_t field_index = new_iput->VRegC_22c();
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ mirror::DexCache* dex_cache = method->GetDexCache();
+ ArtField* field = dex_cache->GetResolvedField(field_index, pointer_size);
+ if (UNLIKELY(field == nullptr)) {
+ return false;
+ }
+ // Remove previous IPUT to the same field, if any. Different field indexes may refer
+ // to the same field, so we need to compare resolved fields from the dex cache.
+ for (size_t old_pos = 0; old_pos != arraysize(iputs); ++old_pos) {
+ if (iputs[old_pos].field_index == DexFile::kDexNoIndex16) {
+ break;
+ }
+ ArtField* f = dex_cache->GetResolvedField(iputs[old_pos].field_index, pointer_size);
+ DCHECK(f != nullptr);
+ if (f == field) {
+ auto back_it = std::copy(iputs + old_pos + 1, iputs + arraysize(iputs), iputs + old_pos);
+ *back_it = ConstructorIPutData();
+ break;
+ }
+ }
+ // If the stored value isn't zero, record the IPUT.
+ if ((zero_vreg_mask & (1u << new_iput->VRegA_22c())) == 0u) {
+ size_t new_pos = 0;
+ while (new_pos != arraysize(iputs) && iputs[new_pos].field_index != DexFile::kDexNoIndex16) {
+ ++new_pos;
+ }
+ if (new_pos == arraysize(iputs)) {
+ return false; // Exceeded capacity of the output array.
+ }
+ iputs[new_pos].field_index = field_index;
+ iputs[new_pos].arg = new_iput->VRegA_22c() - this_vreg;
+ }
+ return true;
+}
+
+bool DoAnalyseConstructor(const DexFile::CodeItem* code_item,
+ ArtMethod* method,
+ /*inout*/ ConstructorIPutData (&iputs)[kMaxConstructorIPuts])
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ // On entry we should not have any IPUTs yet.
+ DCHECK_EQ(0, std::count_if(
+ iputs,
+ iputs + arraysize(iputs),
+ [](const ConstructorIPutData& iput_data) {
+ return iput_data.field_index != DexFile::kDexNoIndex16;
+ }));
+
+ // Limit the maximum number of code units we're willing to match.
+ static constexpr size_t kMaxCodeUnits = 16u;
+
+ // Limit the number of registers that the constructor may use to 16.
+ // Given that IPUTs must use low 16 registers and we do not match MOVEs,
+ // this is a reasonable limitation.
+ static constexpr size_t kMaxVRegs = 16u;
+
+ // We try to match a constructor that calls another constructor (either in
+ // superclass or in the same class) with the same parameters, or with some
+ // parameters truncated (allowed only for calls to superclass constructor)
+ // or with extra parameters with value 0 (with any type, including null).
+ // This call can be followed by optional IPUTs on "this" storing either one
+ // of the parameters or 0 and the code must then finish with RETURN_VOID.
+ // The called constructor must be either java.lang.Object.<init>() or it
+ // must also match the same pattern.
+ static Matcher::MatchFn* const kConstructorPattern[] = {
+ &Matcher::Mark,
+ &Matcher::Repeated<&Matcher::Const0>,
+ &Matcher::Required<&Matcher::Opcode<Instruction::INVOKE_DIRECT>>,
+ &Matcher::Mark,
+ &Matcher::Repeated<&Matcher::Const0>,
+ &Matcher::Repeated<&Matcher::IPutOnThis>,
+ &Matcher::Required<&Matcher::Opcode<Instruction::RETURN_VOID>>,
+ };
+
+ DCHECK(method != nullptr);
+ DCHECK(!method->IsStatic());
+ DCHECK(method->IsConstructor());
+ DCHECK(code_item != nullptr);
+ if (!method->GetDeclaringClass()->IsVerified() ||
+ code_item->insns_size_in_code_units_ > kMaxCodeUnits ||
+ code_item->registers_size_ > kMaxVRegs ||
+ !Matcher::Match(code_item, kConstructorPattern)) {
+ return false;
+ }
+
+ // Verify the invoke, prevent a few odd cases and collect IPUTs.
+ uint16_t this_vreg = code_item->registers_size_ - code_item->ins_size_;
+ uint16_t zero_vreg_mask = 0u;
+ for (const Instruction* instruction = Instruction::At(code_item->insns_);
+ instruction->Opcode() != Instruction::RETURN_VOID;
+ instruction = instruction->Next()) {
+ if (instruction->Opcode() == Instruction::INVOKE_DIRECT) {
+ ArtMethod* target_method = GetTargetConstructor(method, instruction);
+ if (target_method == nullptr) {
+ return false;
+ }
+ // We allow forwarding constructors only if they pass more arguments
+ // to prevent infinite recursion.
+ if (target_method->GetDeclaringClass() == method->GetDeclaringClass() &&
+ instruction->VRegA_35c() <= code_item->ins_size_) {
+ return false;
+ }
+ size_t forwarded = CountForwardedConstructorArguments(code_item, instruction, zero_vreg_mask);
+ if (forwarded == static_cast<size_t>(-1)) {
+ return false;
+ }
+ if (target_method->GetDeclaringClass()->IsObjectClass()) {
+ DCHECK_EQ(Instruction::At(target_method->GetCodeItem()->insns_)->Opcode(),
+ Instruction::RETURN_VOID);
+ } else {
+ const DexFile::CodeItem* target_code_item = target_method->GetCodeItem();
+ if (target_code_item == nullptr) {
+ return false; // Native constructor?
+ }
+ if (!DoAnalyseConstructor(target_code_item, target_method, iputs)) {
+ return false;
+ }
+ // Prune IPUTs with zero input.
+ auto kept_end = std::remove_if(
+ iputs,
+ iputs + arraysize(iputs),
+ [forwarded](const ConstructorIPutData& iput_data) {
+ return iput_data.arg >= forwarded;
+ });
+ std::fill(kept_end, iputs + arraysize(iputs), ConstructorIPutData());
+ // If we have any IPUTs from the call, check that the target method is in the same
+ // dex file (compare DexCache references), otherwise field_indexes would be bogus.
+ if (iputs[0].field_index != DexFile::kDexNoIndex16 &&
+ target_method->GetDexCache() != method->GetDexCache()) {
+ return false;
+ }
+ }
+ } else if (IsInstructionDirectConst(instruction->Opcode())) {
+ zero_vreg_mask |= GetZeroVRegMask(instruction);
+ if ((zero_vreg_mask & (1u << this_vreg)) != 0u) {
+ return false; // Overwriting `this` is unsupported.
+ }
+ } else {
+ DCHECK(IsInstructionIPut(instruction->Opcode()));
+ DCHECK_EQ(instruction->VRegB_22c(), this_vreg);
+ if (!RecordConstructorIPut(method, instruction, this_vreg, zero_vreg_mask, iputs)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+} // anonymous namespace
+
+bool AnalyseConstructor(const DexFile::CodeItem* code_item,
+ ArtMethod* method,
+ InlineMethod* result)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ ConstructorIPutData iputs[kMaxConstructorIPuts];
+ if (!DoAnalyseConstructor(code_item, method, iputs)) {
+ return false;
+ }
+ static_assert(kMaxConstructorIPuts == 3, "Unexpected limit"); // Code below depends on this.
+ DCHECK(iputs[0].field_index != DexFile::kDexNoIndex16 ||
+ iputs[1].field_index == DexFile::kDexNoIndex16);
+ DCHECK(iputs[1].field_index != DexFile::kDexNoIndex16 ||
+ iputs[2].field_index == DexFile::kDexNoIndex16);
+
+#define STORE_IPUT(n) \
+ do { \
+ result->d.constructor_data.iput##n##_field_index = iputs[n].field_index; \
+ result->d.constructor_data.iput##n##_arg = iputs[n].arg; \
+ } while (false)
+
+ STORE_IPUT(0);
+ STORE_IPUT(1);
+ STORE_IPUT(2);
+#undef STORE_IPUT
+
+ result->opcode = kInlineOpConstructor;
+ result->flags = kInlineSpecial;
+ result->d.constructor_data.reserved = 0u;
+ return true;
+}
+
static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET), "iget type");
static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_WIDE), "iget_wide type");
static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_OBJECT),
@@ -123,7 +484,19 @@
case Instruction::CONST_16:
case Instruction::CONST_HIGH16:
// TODO: Support wide constants (RETURN_WIDE).
- return AnalyseConstMethod(code_item, result);
+ if (AnalyseConstMethod(code_item, result)) {
+ return true;
+ }
+ FALLTHROUGH_INTENDED;
+ case Instruction::CONST_WIDE:
+ case Instruction::CONST_WIDE_16:
+ case Instruction::CONST_WIDE_32:
+ case Instruction::CONST_WIDE_HIGH16:
+ case Instruction::INVOKE_DIRECT:
+ if (method != nullptr && !method->IsStatic() && method->IsConstructor()) {
+ return AnalyseConstructor(code_item, method, result);
+ }
+ return false;
case Instruction::IGET:
case Instruction::IGET_OBJECT:
case Instruction::IGET_BOOLEAN:
diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h
index 046d225..0b09a70 100644
--- a/runtime/quick/inline_method_analyser.h
+++ b/runtime/quick/inline_method_analyser.h
@@ -107,6 +107,7 @@
kInlineOpNonWideConst,
kInlineOpIGet,
kInlineOpIPut,
+ kInlineOpConstructor,
kInlineStringInit,
};
std::ostream& operator<<(std::ostream& os, const InlineMethodOpcode& rhs);
@@ -168,6 +169,19 @@
static_assert(sizeof(InlineReturnArgData) == sizeof(uint64_t),
"Invalid size of InlineReturnArgData");
+struct InlineConstructorData {
+ // There can be up to 3 IPUTs, unused fields are marked with kNoDexIndex16.
+ uint16_t iput0_field_index;
+ uint16_t iput1_field_index;
+ uint16_t iput2_field_index;
+ uint16_t iput0_arg : 4;
+ uint16_t iput1_arg : 4;
+ uint16_t iput2_arg : 4;
+ uint16_t reserved : 4;
+};
+static_assert(sizeof(InlineConstructorData) == sizeof(uint64_t),
+ "Invalid size of InlineConstructorData");
+
struct InlineMethod {
InlineMethodOpcode opcode;
InlineMethodFlags flags;
@@ -175,6 +189,7 @@
uint64_t data;
InlineIGetIPutData ifield_data;
InlineReturnArgData return_data;
+ InlineConstructorData constructor_data;
} d;
};
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 0c06ca6..2aeb792 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -335,6 +335,7 @@
os << "Runtime aborting...\n";
if (Runtime::Current() == nullptr) {
os << "(Runtime does not yet exist!)\n";
+ DumpNativeStack(os, GetTid(), nullptr, " native: ", nullptr);
return;
}
Thread* self = Thread::Current();
@@ -602,9 +603,12 @@
if (is_native_bridge_loaded_) {
PreInitializeNativeBridge(".");
}
+ NativeBridgeAction action = force_native_bridge_
+ ? NativeBridgeAction::kInitialize
+ : NativeBridgeAction::kUnload;
InitNonZygoteOrPostFork(self->GetJniEnv(),
/* is_system_server */ false,
- NativeBridgeAction::kInitialize,
+ action,
GetInstructionSetString(kRuntimeISA));
}
@@ -914,6 +918,7 @@
is_explicit_gc_disabled_ = runtime_options.Exists(Opt::DisableExplicitGC);
dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::Dex2Oat);
image_dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::ImageDex2Oat);
+ dump_native_stack_on_sig_quit_ = runtime_options.GetOrDefault(Opt::DumpNativeStackOnSigQuit);
vfprintf_ = runtime_options.GetOrDefault(Opt::HookVfprintf);
exit_ = runtime_options.GetOrDefault(Opt::HookExit);
@@ -939,6 +944,7 @@
allow_dex_file_fallback_ = !runtime_options.Exists(Opt::NoDexFileFallback);
no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain);
+ force_native_bridge_ = runtime_options.Exists(Opt::ForceNativeBridge);
Split(runtime_options.GetOrDefault(Opt::CpuAbiList), ',', &cpu_abilist_);
@@ -1883,7 +1889,8 @@
if (jit_.get() != nullptr) {
compiler_callbacks_ = jit_->GetCompilerCallbacks();
jit_->CreateInstrumentationCache(jit_options_->GetCompileThreshold(),
- jit_options_->GetWarmupThreshold());
+ jit_options_->GetWarmupThreshold(),
+ jit_options_->GetOsrThreshold());
jit_->CreateThreadPool();
// Notify native debugger about the classes already loaded before the creation of the jit.
@@ -1914,7 +1921,8 @@
}
bool Runtime::IsVerificationEnabled() const {
- return verify_ == verifier::VerifyMode::kEnable;
+ return verify_ == verifier::VerifyMode::kEnable ||
+ verify_ == verifier::VerifyMode::kSoftFail;
}
bool Runtime::IsVerificationSoftFail() const {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index c8c2ee5..1956bae 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -603,6 +603,10 @@
safe_mode_ = mode;
}
+ bool GetDumpNativeStackOnSigQuit() const {
+ return dump_native_stack_on_sig_quit_;
+ }
+
private:
static void InitPlatformSignalHandlers();
@@ -774,6 +778,9 @@
// building a statically link version of dex2oat.
bool no_sig_chain_;
+ // Force the use of native bridge even if the app ISA matches the runtime ISA.
+ bool force_native_bridge_;
+
// Whether or not a native bridge has been loaded.
//
// The native bridge allows running native code compiled for a foreign ISA. The way it works is,
@@ -810,6 +817,9 @@
// Whether the application should run in safe mode, that is, interpreter only.
bool safe_mode_;
+ // Whether threads should dump their native stack on SIGQUIT.
+ bool dump_native_stack_on_sig_quit_;
+
DISALLOW_COPY_AND_ASSIGN(Runtime);
};
std::ostream& operator<<(std::ostream& os, const Runtime::CalleeSaveType& rhs);
diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc
index 122dcb1..8237b06 100644
--- a/runtime/runtime_linux.cc
+++ b/runtime/runtime_linux.cc
@@ -114,6 +114,9 @@
switch (signal_code) {
case SEGV_MAPERR: return "SEGV_MAPERR";
case SEGV_ACCERR: return "SEGV_ACCERR";
+#if defined(SEGV_BNDERR)
+ case SEGV_BNDERR: return "SEGV_BNDERR";
+#endif
}
break;
case SIGTRAP:
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 308f3ba..838d1a9 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -67,6 +67,7 @@
RUNTIME_OPTIONS_KEY (bool, UseTLAB, (kUseTlab || kUseReadBarrier))
RUNTIME_OPTIONS_KEY (bool, EnableHSpaceCompactForOOM, true)
RUNTIME_OPTIONS_KEY (bool, UseJIT, false)
+RUNTIME_OPTIONS_KEY (bool, DumpNativeStackOnSigQuit, true)
RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold, jit::Jit::kDefaultCompileThreshold)
RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold, jit::Jit::kDefaultWarmupThreshold)
RUNTIME_OPTIONS_KEY (MemoryKiB, JITCodeCacheInitialCapacity, jit::JitCodeCache::kInitialCapacity)
@@ -92,6 +93,7 @@
RUNTIME_OPTIONS_KEY (Unit, DisableExplicitGC)
RUNTIME_OPTIONS_KEY (Unit, NoSigChain)
+RUNTIME_OPTIONS_KEY (Unit, ForceNativeBridge)
RUNTIME_OPTIONS_KEY (LogVerbosity, Verbose)
RUNTIME_OPTIONS_KEY (unsigned int, LockProfThreshold)
RUNTIME_OPTIONS_KEY (std::string, StackTraceFile)
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 84185ce..97eb805 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -1195,6 +1195,35 @@
return StackMap();
}
+ StackMap GetOsrStackMapForDexPc(uint32_t dex_pc, const StackMapEncoding& encoding) const {
+ size_t e = GetNumberOfStackMaps();
+ if (e == 0) {
+ // There cannot be OSR stack map if there is no stack map.
+ return StackMap();
+ }
+ // Walk over all stack maps. If two consecutive stack maps are identical, then we
+ // have found a stack map suitable for OSR.
+ for (size_t i = 0; i < e - 1; ++i) {
+ StackMap stack_map = GetStackMapAt(i, encoding);
+ if (stack_map.GetDexPc(encoding) == dex_pc) {
+ StackMap other = GetStackMapAt(i + 1, encoding);
+ if (other.GetDexPc(encoding) == dex_pc &&
+ other.GetNativePcOffset(encoding) == stack_map.GetNativePcOffset(encoding)) {
+ DCHECK_EQ(other.GetDexRegisterMapOffset(encoding),
+ stack_map.GetDexRegisterMapOffset(encoding));
+ DCHECK(!stack_map.HasInlineInfo(encoding));
+ if (i < e - 2) {
+ // Make sure there are not three identical stack maps following each other.
+ DCHECK_NE(stack_map.GetNativePcOffset(encoding),
+ GetStackMapAt(i + 2, encoding).GetNativePcOffset(encoding));
+ }
+ return stack_map;
+ }
+ }
+ }
+ return StackMap();
+ }
+
StackMap GetStackMapForNativePcOffset(uint32_t native_pc_offset,
const StackMapEncoding& encoding) const {
// TODO: Safepoint stack maps are sorted by native_pc_offset but catch stack
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 2abcd67..2ee1605 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -936,9 +936,9 @@
<< "]";
}
-void Thread::Dump(std::ostream& os, BacktraceMap* backtrace_map) const {
+void Thread::Dump(std::ostream& os, bool dump_native_stack, BacktraceMap* backtrace_map) const {
DumpState(os);
- DumpStack(os, backtrace_map);
+ DumpStack(os, dump_native_stack, backtrace_map);
}
mirror::String* Thread::GetThreadName(const ScopedObjectAccessAlreadyRunnable& soa) const {
@@ -1497,7 +1497,9 @@
}
}
-void Thread::DumpStack(std::ostream& os, BacktraceMap* backtrace_map) const {
+void Thread::DumpStack(std::ostream& os,
+ bool dump_native_stack,
+ BacktraceMap* backtrace_map) const {
// TODO: we call this code when dying but may not have suspended the thread ourself. The
// IsSuspended check is therefore racy with the use for dumping (normally we inhibit
// the race with the thread_suspend_count_lock_).
@@ -1510,7 +1512,7 @@
}
if (safe_to_dump) {
// If we're currently in native code, dump that stack before dumping the managed stack.
- if (dump_for_abort || ShouldShowNativeStack(this)) {
+ if (dump_native_stack && (dump_for_abort || ShouldShowNativeStack(this))) {
DumpKernelStack(os, GetTid(), " kernel: ", false);
ArtMethod* method = GetCurrentMethod(nullptr, !dump_for_abort);
DumpNativeStack(os, GetTid(), backtrace_map, " native: ", method);
@@ -1599,7 +1601,7 @@
tls32_.state_and_flags.as_struct.state = kNative;
memset(&tlsPtr_.held_mutexes[0], 0, sizeof(tlsPtr_.held_mutexes));
std::fill(tlsPtr_.rosalloc_runs,
- tlsPtr_.rosalloc_runs + kNumRosAllocThreadLocalSizeBrackets,
+ tlsPtr_.rosalloc_runs + kNumRosAllocThreadLocalSizeBracketsInThread,
gc::allocator::RosAlloc::GetDedicatedFullRun());
for (uint32_t i = 0; i < kMaxCheckpoints; ++i) {
tlsPtr_.checkpoint_functions[i] = nullptr;
@@ -3012,4 +3014,25 @@
return count;
}
+
+void Thread::DeoptimizeWithDeoptimizationException(JValue* result) {
+ DCHECK_EQ(GetException(), Thread::GetDeoptimizationException());
+ ClearException();
+ ShadowFrame* shadow_frame =
+ PopStackedShadowFrame(StackedShadowFrameType::kDeoptimizationShadowFrame);
+ mirror::Throwable* pending_exception = nullptr;
+ bool from_code = false;
+ PopDeoptimizationContext(result, &pending_exception, &from_code);
+ CHECK(!from_code) << "Deoptimizing from code should be done with single frame deoptimization";
+ SetTopOfStack(nullptr);
+ SetTopOfShadowStack(shadow_frame);
+
+ // Restore the exception that was pending before deoptimization then interpret the
+ // deoptimized frames.
+ if (pending_exception != nullptr) {
+ SetException(pending_exception);
+ }
+ interpreter::EnterInterpreterFromDeoptimize(this, shadow_frame, from_code, result);
+}
+
} // namespace art
diff --git a/runtime/thread.h b/runtime/thread.h
index d7887ca..2726e91 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -113,7 +113,8 @@
kSingleFrameDeoptimizationShadowFrame
};
-static constexpr size_t kNumRosAllocThreadLocalSizeBrackets = 34;
+// This should match RosAlloc::kNumThreadLocalSizeBrackets.
+static constexpr size_t kNumRosAllocThreadLocalSizeBracketsInThread = 16;
// Thread's stack layout for implicit stack overflow checks:
//
@@ -186,7 +187,9 @@
void ShortDump(std::ostream& os) const;
// Dumps the detailed thread state and the thread stack (used for SIGQUIT).
- void Dump(std::ostream& os, BacktraceMap* backtrace_map = nullptr) const
+ void Dump(std::ostream& os,
+ bool dump_native_stack = true,
+ BacktraceMap* backtrace_map = nullptr) const
REQUIRES(!Locks::thread_suspend_count_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -552,6 +555,9 @@
OFFSETOF_MEMBER(tls_32bit_sized_values, is_gc_marking));
}
+ // Deoptimize the Java stack.
+ void DeoptimizeWithDeoptimizationException(JValue* result) SHARED_REQUIRES(Locks::mutator_lock_);
+
private:
template<size_t pointer_size>
static ThreadOffset<pointer_size> ThreadOffsetFromTlsPtr(size_t tls_ptr_offset) {
@@ -1107,7 +1113,9 @@
void VerifyStackImpl() SHARED_REQUIRES(Locks::mutator_lock_);
void DumpState(std::ostream& os) const SHARED_REQUIRES(Locks::mutator_lock_);
- void DumpStack(std::ostream& os, BacktraceMap* backtrace_map = nullptr) const
+ void DumpStack(std::ostream& os,
+ bool dump_native_stack = true,
+ BacktraceMap* backtrace_map = nullptr) const
REQUIRES(!Locks::thread_suspend_count_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -1421,7 +1429,7 @@
void* mterp_alt_ibase;
// There are RosAlloc::kNumThreadLocalSizeBrackets thread-local size brackets per thread.
- void* rosalloc_runs[kNumRosAllocThreadLocalSizeBrackets];
+ void* rosalloc_runs[kNumRosAllocThreadLocalSizeBracketsInThread];
// Thread-local allocation stack data/routines.
StackReference<mirror::Object>* thread_local_alloc_stack_top;
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index c8714a6..49d54fd 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -140,7 +140,7 @@
suspend_all_historam_.PrintConfidenceIntervals(os, 0.99, data); // Dump time to suspend.
}
}
- Dump(os);
+ Dump(os, Runtime::Current()->GetDumpNativeStackOnSigQuit());
DumpUnattachedThreads(os);
}
@@ -189,8 +189,11 @@
// A closure used by Thread::Dump.
class DumpCheckpoint FINAL : public Closure {
public:
- explicit DumpCheckpoint(std::ostream* os)
- : os_(os), barrier_(0), backtrace_map_(BacktraceMap::Create(getpid())) {}
+ DumpCheckpoint(std::ostream* os, bool dump_native_stack)
+ : os_(os),
+ barrier_(0),
+ backtrace_map_(dump_native_stack ? BacktraceMap::Create(getpid()) : nullptr),
+ dump_native_stack_(dump_native_stack) {}
void Run(Thread* thread) OVERRIDE {
// Note thread and self may not be equal if thread was already suspended at the point of the
@@ -199,7 +202,7 @@
std::ostringstream local_os;
{
ScopedObjectAccess soa(self);
- thread->Dump(local_os, backtrace_map_.get());
+ thread->Dump(local_os, dump_native_stack_, backtrace_map_.get());
}
local_os << "\n";
{
@@ -228,14 +231,16 @@
Barrier barrier_;
// A backtrace map, so that all threads use a shared info and don't reacquire/parse separately.
std::unique_ptr<BacktraceMap> backtrace_map_;
+ // Whether we should dump the native stack.
+ const bool dump_native_stack_;
};
-void ThreadList::Dump(std::ostream& os) {
+void ThreadList::Dump(std::ostream& os, bool dump_native_stack) {
{
MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
os << "DALVIK THREADS (" << list_.size() << "):\n";
}
- DumpCheckpoint checkpoint(&os);
+ DumpCheckpoint checkpoint(&os, dump_native_stack);
size_t threads_running_checkpoint = RunCheckpoint(&checkpoint);
if (threads_running_checkpoint != 0) {
checkpoint.WaitForThreadsToRunThroughCheckpoint(threads_running_checkpoint);
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 2e73f6a..363cab8 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -49,7 +49,7 @@
void DumpForSigQuit(std::ostream& os)
REQUIRES(!Locks::thread_list_lock_, !Locks::mutator_lock_);
// For thread suspend timeout dumps.
- void Dump(std::ostream& os)
+ void Dump(std::ostream& os, bool dump_native_stack = true)
REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_);
pid_t GetLockOwner(); // For SignalCatcher.
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 56154c6..1d31408 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -58,6 +58,10 @@
// On VLOG(verifier), should we dump the whole state when we run into a hard failure?
static constexpr bool kDumpRegLinesOnHardFailureIfVLOG = true;
+// We print a warning blurb about "dx --no-optimize" when we find monitor-locking issues. Make
+// sure we only print this once.
+static bool gPrintedDxMonitorText = false;
+
PcToRegisterLineTable::PcToRegisterLineTable(ScopedArenaAllocator& arena)
: register_lines_(arena.Adapter(kArenaAllocVerifier)) {}
@@ -166,23 +170,38 @@
return kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod();
}
+static MethodVerifier::FailureKind FailureKindMax(MethodVerifier::FailureKind fk1,
+ MethodVerifier::FailureKind fk2) {
+ static_assert(MethodVerifier::FailureKind::kNoFailure <
+ MethodVerifier::FailureKind::kSoftFailure
+ && MethodVerifier::FailureKind::kSoftFailure <
+ MethodVerifier::FailureKind::kHardFailure,
+ "Unexpected FailureKind order");
+ return std::max(fk1, fk2);
+}
+
+void MethodVerifier::FailureData::Merge(const MethodVerifier::FailureData& fd) {
+ kind = FailureKindMax(kind, fd.kind);
+ types |= fd.types;
+}
+
template <bool kDirect>
-void MethodVerifier::VerifyMethods(Thread* self,
- ClassLinker* linker,
- const DexFile* dex_file,
- const DexFile::ClassDef* class_def,
- ClassDataItemIterator* it,
- Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader,
- CompilerCallbacks* callbacks,
- bool allow_soft_failures,
- bool log_hard_failures,
- bool need_precise_constants,
- bool* hard_fail,
- size_t* error_count,
- std::string* error_string) {
+MethodVerifier::FailureData MethodVerifier::VerifyMethods(Thread* self,
+ ClassLinker* linker,
+ const DexFile* dex_file,
+ const DexFile::ClassDef* class_def,
+ ClassDataItemIterator* it,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader,
+ CompilerCallbacks* callbacks,
+ bool allow_soft_failures,
+ bool log_hard_failures,
+ bool need_precise_constants,
+ std::string* error_string) {
DCHECK(it != nullptr);
+ MethodVerifier::FailureData failure_data;
+
int64_t previous_method_idx = -1;
while (HasNextMethod<kDirect>(it)) {
self->AllowThreadSuspension();
@@ -206,7 +225,7 @@
}
StackHandleScope<1> hs(self);
std::string hard_failure_msg;
- MethodVerifier::FailureKind result = VerifyMethod(self,
+ MethodVerifier::FailureData result = VerifyMethod(self,
method_idx,
dex_file,
dex_cache,
@@ -220,24 +239,24 @@
log_hard_failures,
need_precise_constants,
&hard_failure_msg);
- if (result != kNoFailure) {
- if (result == kHardFailure) {
- if (*error_count > 0) {
- *error_string += "\n";
- }
- if (!*hard_fail) {
- *error_string += "Verifier rejected class ";
- *error_string += PrettyDescriptor(dex_file->GetClassDescriptor(*class_def));
- *error_string += ":";
- }
- *error_string += " ";
- *error_string += hard_failure_msg;
- *hard_fail = true;
+ if (result.kind == kHardFailure) {
+ if (failure_data.kind == kHardFailure) {
+ // If we logged an error before, we need a newline.
+ *error_string += "\n";
+ } else {
+ // If we didn't log a hard failure before, print the header of the message.
+ *error_string += "Verifier rejected class ";
+ *error_string += PrettyDescriptor(dex_file->GetClassDescriptor(*class_def));
+ *error_string += ":";
}
- *error_count = *error_count + 1;
+ *error_string += " ";
+ *error_string += hard_failure_msg;
}
+ failure_data.Merge(result);
it->Next();
}
+
+ return failure_data;
}
MethodVerifier::FailureKind MethodVerifier::VerifyClass(Thread* self,
@@ -268,44 +287,53 @@
while (it.HasNextStaticField() || it.HasNextInstanceField()) {
it.Next();
}
- size_t error_count = 0;
- bool hard_fail = false;
ClassLinker* linker = Runtime::Current()->GetClassLinker();
// Direct methods.
- VerifyMethods<true>(self,
- linker,
- dex_file,
- class_def,
- &it,
- dex_cache,
- class_loader,
- callbacks,
- allow_soft_failures,
- log_hard_failures,
- false /* need precise constants */,
- &hard_fail,
- &error_count,
- error);
+ MethodVerifier::FailureData data1 = VerifyMethods<true>(self,
+ linker,
+ dex_file,
+ class_def,
+ &it,
+ dex_cache,
+ class_loader,
+ callbacks,
+ allow_soft_failures,
+ log_hard_failures,
+ false /* need precise constants */,
+ error);
// Virtual methods.
- VerifyMethods<false>(self,
- linker,
- dex_file,
- class_def,
- &it,
- dex_cache,
- class_loader,
- callbacks,
- allow_soft_failures,
- log_hard_failures,
- false /* need precise constants */,
- &hard_fail,
- &error_count,
- error);
+ MethodVerifier::FailureData data2 = VerifyMethods<false>(self,
+ linker,
+ dex_file,
+ class_def,
+ &it,
+ dex_cache,
+ class_loader,
+ callbacks,
+ allow_soft_failures,
+ log_hard_failures,
+ false /* need precise constants */,
+ error);
- if (error_count == 0) {
+ data1.Merge(data2);
+
+ if (data1.kind == kNoFailure) {
return kNoFailure;
} else {
- return hard_fail ? kHardFailure : kSoftFailure;
+ if ((data1.types & VERIFY_ERROR_LOCKING) != 0) {
+ // Print a warning about expected slow-down. Use a string temporary to print one contiguous
+ // warning.
+ std::string tmp =
+ StringPrintf("Class %s failed lock verification and will run slower.",
+ PrettyDescriptor(dex_file->GetClassDescriptor(*class_def)).c_str());
+ if (!gPrintedDxMonitorText) {
+ tmp = tmp + "\nCommon causes for lock verification issues are non-optimized dex code\n"
+ "and incorrect proguard optimizations.";
+ gPrintedDxMonitorText = true;
+ }
+ LOG(WARNING) << tmp;
+ }
+ return data1.kind;
}
}
@@ -320,7 +348,7 @@
return registers_size * insns_size > 4*1024*1024;
}
-MethodVerifier::FailureKind MethodVerifier::VerifyMethod(Thread* self,
+MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self,
uint32_t method_idx,
const DexFile* dex_file,
Handle<mirror::DexCache> dex_cache,
@@ -334,7 +362,7 @@
bool log_hard_failures,
bool need_precise_constants,
std::string* hard_failure_msg) {
- MethodVerifier::FailureKind result = kNoFailure;
+ MethodVerifier::FailureData result;
uint64_t start_ns = kTimeVerifyMethod ? NanoTime() : 0;
MethodVerifier verifier(self, dex_file, dex_cache, class_loader, class_def, code_item,
@@ -355,7 +383,7 @@
verifier.DumpFailures(VLOG_STREAM(verifier) << "Soft verification failures in "
<< PrettyMethod(method_idx, *dex_file) << "\n");
}
- result = kSoftFailure;
+ result.kind = kSoftFailure;
}
} else {
// Bad method data.
@@ -364,7 +392,7 @@
if (UNLIKELY(verifier.have_pending_experimental_failure_)) {
// Failed due to being forced into interpreter. This is ok because
// we just want to skip verification.
- result = kSoftFailure;
+ result.kind = kSoftFailure;
} else {
CHECK(verifier.have_pending_hard_failure_);
if (VLOG_IS_ON(verifier) || log_hard_failures) {
@@ -376,7 +404,7 @@
*hard_failure_msg =
verifier.failure_messages_[verifier.failure_messages_.size() - 1]->str();
}
- result = kHardFailure;
+ result.kind = kHardFailure;
if (callbacks != nullptr) {
// Let the interested party know that we failed the class.
@@ -397,6 +425,7 @@
<< (IsLargeMethod(code_item) ? " (large method)" : "");
}
}
+ result.types = verifier.encountered_failure_types_;
return result;
}
@@ -2382,6 +2411,8 @@
if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(res_type)) {
if (res_type.IsUninitializedTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "thrown exception not initialized";
+ } else if (!res_type.IsReferenceTypes()) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "thrown value of non-reference type " << res_type;
} else {
Fail(res_type.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS : VERIFY_ERROR_BAD_CLASS_SOFT)
<< "thrown class " << res_type << " not instanceof Throwable";
@@ -4495,6 +4526,19 @@
if (UNLIKELY(have_pending_hard_failure_)) {
return;
}
+ if (should_adjust) {
+ if (field == nullptr) {
+ Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Might be accessing a superclass instance field prior "
+ << "to the superclass being initialized in "
+ << PrettyMethod(dex_method_idx_, *dex_file_);
+ } else if (field->GetDeclaringClass() != GetDeclaringClass().GetClass()) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "cannot access superclass instance field "
+ << PrettyField(field) << " of a not fully initialized "
+ << "object within the context of "
+ << PrettyMethod(dex_method_idx_, *dex_file_);
+ return;
+ }
+ }
}
const RegType* field_type = nullptr;
if (field != nullptr) {
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 613d5af..c7d1e6b 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -25,6 +25,7 @@
#include "base/macros.h"
#include "base/scoped_arena_containers.h"
#include "base/stl_util.h"
+#include "base/value_object.h"
#include "dex_file.h"
#include "handle.h"
#include "instruction_flags.h"
@@ -344,23 +345,31 @@
// Adds the given string to the end of the last failure message.
void AppendToLastFailMessage(std::string);
+ // Verification result for method(s). Includes a (maximum) failure kind, and (the union of)
+ // all failure types.
+ struct FailureData : ValueObject {
+ FailureKind kind = kNoFailure;
+ uint32_t types = 0U;
+
+ // Merge src into this. Uses the most severe failure kind, and the union of types.
+ void Merge(const FailureData& src);
+ };
+
// Verify all direct or virtual methods of a class. The method assumes that the iterator is
// positioned correctly, and the iterator will be updated.
template <bool kDirect>
- static void VerifyMethods(Thread* self,
- ClassLinker* linker,
- const DexFile* dex_file,
- const DexFile::ClassDef* class_def,
- ClassDataItemIterator* it,
- Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader,
- CompilerCallbacks* callbacks,
- bool allow_soft_failures,
- bool log_hard_failures,
- bool need_precise_constants,
- bool* hard_fail,
- size_t* error_count,
- std::string* error_string)
+ static FailureData VerifyMethods(Thread* self,
+ ClassLinker* linker,
+ const DexFile* dex_file,
+ const DexFile::ClassDef* class_def,
+ ClassDataItemIterator* it,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader,
+ CompilerCallbacks* callbacks,
+ bool allow_soft_failures,
+ bool log_hard_failures,
+ bool need_precise_constants,
+ std::string* error_string)
SHARED_REQUIRES(Locks::mutator_lock_);
/*
@@ -374,7 +383,7 @@
* (3) Iterate through the method, checking type safety and looking
* for code flow problems.
*/
- static FailureKind VerifyMethod(Thread* self, uint32_t method_idx,
+ static FailureData VerifyMethod(Thread* self, uint32_t method_idx,
const DexFile* dex_file,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader,
diff --git a/runtime/verifier/register_line-inl.h b/runtime/verifier/register_line-inl.h
index 08f85b3..330c06a 100644
--- a/runtime/verifier/register_line-inl.h
+++ b/runtime/verifier/register_line-inl.h
@@ -178,9 +178,9 @@
if (MonitorStackDepth() != 0) {
verifier->Fail(VERIFY_ERROR_LOCKING);
if (kDumpLockFailures) {
- LOG(WARNING) << "expected empty monitor stack in "
- << PrettyMethod(verifier->GetMethodReference().dex_method_index,
- *verifier->GetMethodReference().dex_file);
+ VLOG(verifier) << "expected empty monitor stack in "
+ << PrettyMethod(verifier->GetMethodReference().dex_method_index,
+ *verifier->GetMethodReference().dex_file);
}
}
}
diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc
index 37343b5..b7cde99 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -348,9 +348,9 @@
} else if (monitors_.size() >= 32) {
verifier->Fail(VERIFY_ERROR_LOCKING);
if (kDumpLockFailures) {
- LOG(WARNING) << "monitor-enter stack overflow while verifying "
- << PrettyMethod(verifier->GetMethodReference().dex_method_index,
- *verifier->GetMethodReference().dex_file);
+ VLOG(verifier) << "monitor-enter stack overflow while verifying "
+ << PrettyMethod(verifier->GetMethodReference().dex_method_index,
+ *verifier->GetMethodReference().dex_file);
}
} else {
if (SetRegToLockDepth(reg_idx, monitors_.size())) {
@@ -364,9 +364,9 @@
} else {
verifier->Fail(VERIFY_ERROR_LOCKING);
if (kDumpLockFailures) {
- LOG(WARNING) << "unexpected monitor-enter on register v" << reg_idx << " in "
- << PrettyMethod(verifier->GetMethodReference().dex_method_index,
- *verifier->GetMethodReference().dex_file);
+ VLOG(verifier) << "unexpected monitor-enter on register v" << reg_idx << " in "
+ << PrettyMethod(verifier->GetMethodReference().dex_method_index,
+ *verifier->GetMethodReference().dex_file);
}
}
}
@@ -379,9 +379,9 @@
} else if (monitors_.empty()) {
verifier->Fail(VERIFY_ERROR_LOCKING);
if (kDumpLockFailures) {
- LOG(WARNING) << "monitor-exit stack underflow while verifying "
- << PrettyMethod(verifier->GetMethodReference().dex_method_index,
- *verifier->GetMethodReference().dex_file);
+ VLOG(verifier) << "monitor-exit stack underflow while verifying "
+ << PrettyMethod(verifier->GetMethodReference().dex_method_index,
+ *verifier->GetMethodReference().dex_file);
}
} else {
monitors_.pop_back();
@@ -400,9 +400,9 @@
if (!success) {
verifier->Fail(VERIFY_ERROR_LOCKING);
if (kDumpLockFailures) {
- LOG(WARNING) << "monitor-exit not unlocking the top of the monitor stack while verifying "
- << PrettyMethod(verifier->GetMethodReference().dex_method_index,
- *verifier->GetMethodReference().dex_file);
+ VLOG(verifier) << "monitor-exit not unlocking the top of the monitor stack while verifying "
+ << PrettyMethod(verifier->GetMethodReference().dex_method_index,
+ *verifier->GetMethodReference().dex_file);
}
} else {
// Record the register was unlocked. This clears all aliases, thus it will also clear the
@@ -453,10 +453,10 @@
if (monitors_.size() != incoming_line->monitors_.size()) {
verifier->Fail(VERIFY_ERROR_LOCKING);
if (kDumpLockFailures) {
- LOG(WARNING) << "mismatched stack depths (depth=" << MonitorStackDepth()
- << ", incoming depth=" << incoming_line->MonitorStackDepth() << ") in "
- << PrettyMethod(verifier->GetMethodReference().dex_method_index,
- *verifier->GetMethodReference().dex_file);
+ VLOG(verifier) << "mismatched stack depths (depth=" << MonitorStackDepth()
+ << ", incoming depth=" << incoming_line->MonitorStackDepth() << ") in "
+ << PrettyMethod(verifier->GetMethodReference().dex_method_index,
+ *verifier->GetMethodReference().dex_file);
}
} else if (reg_to_lock_depths_ != incoming_line->reg_to_lock_depths_) {
for (uint32_t idx = 0; idx < num_regs_; idx++) {
@@ -488,10 +488,10 @@
reg_to_lock_depths_)) {
verifier->Fail(VERIFY_ERROR_LOCKING);
if (kDumpLockFailures) {
- LOG(WARNING) << "mismatched stack depths for register v" << idx
- << ": " << depths << " != " << incoming_depths << " in "
- << PrettyMethod(verifier->GetMethodReference().dex_method_index,
- *verifier->GetMethodReference().dex_file);
+ VLOG(verifier) << "mismatched stack depths for register v" << idx
+ << ": " << depths << " != " << incoming_depths << " in "
+ << PrettyMethod(verifier->GetMethodReference().dex_method_index,
+ *verifier->GetMethodReference().dex_file);
}
break;
}
@@ -530,11 +530,11 @@
// No aliases for both current and incoming, we'll lose information.
verifier->Fail(VERIFY_ERROR_LOCKING);
if (kDumpLockFailures) {
- LOG(WARNING) << "mismatched lock levels for register v" << idx << ": "
- << std::hex << locked_levels << std::dec << " != "
- << std::hex << incoming_locked_levels << std::dec << " in "
- << PrettyMethod(verifier->GetMethodReference().dex_method_index,
- *verifier->GetMethodReference().dex_file);
+ VLOG(verifier) << "mismatched lock levels for register v" << idx << ": "
+ << std::hex << locked_levels << std::dec << " != "
+ << std::hex << incoming_locked_levels << std::dec << " in "
+ << PrettyMethod(verifier->GetMethodReference().dex_method_index,
+ *verifier->GetMethodReference().dex_file);
}
break;
}
diff --git a/test/003-omnibus-opcodes/build b/test/003-omnibus-opcodes/build
index faa2983..56e8784 100644
--- a/test/003-omnibus-opcodes/build
+++ b/test/003-omnibus-opcodes/build
@@ -23,8 +23,8 @@
${JAVAC} -d classes `find src2 -name '*.java'`
if [ ${USE_JACK} = "true" ]; then
- ${JILL} classes --output classes.jack
- ${JACK} --import classes.jack --output-dex .
+ jar cf classes.jill.jar -C classes .
+ ${JACK} --import classes.jill.jar --output-dex .
else
${DX} -JXmx256m --debug --dex --output=classes.dex classes
fi
diff --git a/test/005-annotations/build b/test/005-annotations/build
index 057b351..93bee50 100644
--- a/test/005-annotations/build
+++ b/test/005-annotations/build
@@ -29,8 +29,8 @@
rm 'classes/android/test/anno/ClassWithInnerAnnotationClass$MissingInnerAnnotationClass.class'
if [ ${USE_JACK} = "true" ]; then
- ${JILL} classes --output classes.jack
- ${JACK} --import classes.jack --output-dex .
+ jar cf classes.jill.jar -C classes .
+ ${JACK} --import classes.jill.jar --output-dex .
else
${DX} -JXmx256m --debug --dex --output=classes.dex classes
fi
diff --git a/test/022-interface/build b/test/022-interface/build
index 3f8915c..5cfc7f2 100644
--- a/test/022-interface/build
+++ b/test/022-interface/build
@@ -20,8 +20,8 @@
# Use classes that are compiled with ecj that exposes an invokeinterface
# issue when interfaces override methods in Object
if [ ${USE_JACK} = "true" ]; then
- ${JILL} classes --output classes.jack
- ${JACK} --import classes.jack --output-dex .
+ jar cf classes.jill.jar -C classes .
+ ${JACK} --import classes.jill.jar --output-dex .
else
${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes
fi
diff --git a/test/048-reflect-v8/expected.txt b/test/048-reflect-v8/expected.txt
index 2d0b4cc..54aede9 100644
--- a/test/048-reflect-v8/expected.txt
+++ b/test/048-reflect-v8/expected.txt
@@ -1,4 +1,104 @@
-Main$DefaultInterface is default = yes
-Main$RegularInterface is default = no
-Main$ImplementsWithDefault is default = yes
-Main$ImplementsWithRegular is default = no
+==============================
+Are These Methods Default:
+==============================
+IsDefaultTest$DefaultInterface is default = yes
+IsDefaultTest$RegularInterface is default = no
+IsDefaultTest$ImplementsWithDefault is default = yes
+IsDefaultTest$ImplementsWithRegular is default = no
+==============================
+Are These Methods found by getDeclaredMethod:
+==============================
+No error thrown for class interface DefaultDeclared$DefaultInterface
+No error thrown for class interface DefaultDeclared$RegularInterface
+NoSuchMethodException thrown for class class DefaultDeclared$ImplementsWithDefault
+No error thrown for class class DefaultDeclared$ImplementsWithDeclared
+No error thrown for class class DefaultDeclared$ImplementsWithRegular
+NoSuchMethodException thrown for class class DefaultDeclared$UnimplementedWithRegular
+==============================
+Class annotations by type:
+==============================
+Annotations by type, defined by class SingleUser with annotation Calendar: @Calendar(dayOfMonth=unspecified_month, dayOfWeek=single, hour=23)
+Annotations by type, defined by class SingleUser with annotation Calendars: <empty>
+Annotations by type, defined by class User with annotation Calendar: @Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)
+Annotations by type, defined by class User with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Annotations by type, defined by class User2 with annotation Calendar: @Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)
+Annotations by type, defined by class User2 with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)])
+Annotations by type, defined by class UserComplex with annotation Calendar: @Calendar(dayOfMonth=afirst, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)
+Annotations by type, defined by class UserComplex with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)])
+Annotations by type, defined by class UserSub with annotation Calendar: @Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)
+Annotations by type, defined by class UserSub with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Annotations by type, defined by class UserSub2 with annotation Calendar: @Calendar(dayOfMonth=sub2, dayOfWeek=unspecified_week, hour=6)
+Annotations by type, defined by class UserSub2 with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+-----------------------------
+-----------------------------
+==============================
+Class declared annotation:
+==============================
+Declared annotations by class class SingleUser, annotation interface Calendar: @Calendar(dayOfMonth=unspecified_month, dayOfWeek=single, hour=23)
+Declared annotations by class class SingleUser, annotation interface Calendars: <null>
+Declared annotations by class class User, annotation interface Calendar: <null>
+Declared annotations by class class User, annotation interface Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Declared annotations by class class UserComplex, annotation interface Calendar: @Calendar(dayOfMonth=afirst, dayOfWeek=unspecified_week, hour=6)
+Declared annotations by class class UserComplex, annotation interface Calendars: @Calendars(value=[@Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)])
+Declared annotations by class class UserSub, annotation interface Calendar: <null>
+Declared annotations by class class UserSub, annotation interface Calendars: <null>
+Declared annotations by class class UserSub2, annotation interface Calendar: @Calendar(dayOfMonth=sub2, dayOfWeek=unspecified_week, hour=6)
+Declared annotations by class class UserSub2, annotation interface Calendars: <null>
+-----------------------------
+-----------------------------
+==============================
+Declared class annotations by type:
+==============================
+Declared annnotations by type, defined by class SingleUser with annotation Calendar: @Calendar(dayOfMonth=unspecified_month, dayOfWeek=single, hour=23)
+Declared annnotations by type, defined by class SingleUser with annotation Calendars: <empty>
+Declared annnotations by type, defined by class User with annotation Calendar: @Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)
+Declared annnotations by type, defined by class User with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Declared annnotations by type, defined by class User2 with annotation Calendar: @Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)
+Declared annnotations by type, defined by class User2 with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)])
+Declared annnotations by type, defined by class UserComplex with annotation Calendar: @Calendar(dayOfMonth=afirst, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)
+Declared annnotations by type, defined by class UserComplex with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)])
+Declared annnotations by type, defined by class UserSub with annotation Calendar: <empty>
+Declared annnotations by type, defined by class UserSub with annotation Calendars: <empty>
+Declared annnotations by type, defined by class UserSub2 with annotation Calendar: @Calendar(dayOfMonth=sub2, dayOfWeek=unspecified_week, hour=6)
+Declared annnotations by type, defined by class UserSub2 with annotation Calendars: <empty>
+-----------------------------
+-----------------------------
+==============================
+Method annotations by type:
+==============================
+Annotations by type, defined by method singleUser with annotation Calendar: @Calendar(dayOfMonth=unspecified_month, dayOfWeek=single, hour=23)
+Annotations by type, defined by method singleUser with annotation Calendars: <empty>
+Annotations by type, defined by method user with annotation Calendar: @Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)
+Annotations by type, defined by method user with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Annotations by type, defined by method user2 with annotation Calendar: @Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)
+Annotations by type, defined by method user2 with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)])
+Annotations by type, defined by method userComplex with annotation Calendar: @Calendar(dayOfMonth=afirst, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)
+Annotations by type, defined by method userComplex with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)])
+-----------------------------
+-----------------------------
+==============================
+Declared method annotations:
+==============================
+Annotations declared by method singleUser with annotation Calendar: @Calendar(dayOfMonth=unspecified_month, dayOfWeek=single, hour=23)
+Annotations declared by method singleUser with annotation Calendars: <null>
+Annotations declared by method user with annotation Calendar: <null>
+Annotations declared by method user with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Annotations declared by method user2 with annotation Calendar: <null>
+Annotations declared by method user2 with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)])
+Annotations declared by method userComplex with annotation Calendar: @Calendar(dayOfMonth=afirst, dayOfWeek=unspecified_week, hour=6)
+Annotations declared by method userComplex with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)])
+-----------------------------
+-----------------------------
+==============================
+Declared method annotations by type:
+==============================
+Annotations by type, defined by method singleUser with annotation Calendar: @Calendar(dayOfMonth=unspecified_month, dayOfWeek=single, hour=23)
+Annotations by type, defined by method singleUser with annotation Calendars: <empty>
+Annotations by type, defined by method user with annotation Calendar: @Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)
+Annotations by type, defined by method user with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Annotations by type, defined by method user2 with annotation Calendar: @Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)
+Annotations by type, defined by method user2 with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)])
+Annotations by type, defined by method userComplex with annotation Calendar: @Calendar(dayOfMonth=afirst, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)
+Annotations by type, defined by method userComplex with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)])
+-----------------------------
+-----------------------------
diff --git a/test/048-reflect-v8/src/AnnotationTest.java b/test/048-reflect-v8/src/AnnotationTest.java
new file mode 100644
index 0000000..75e6845
--- /dev/null
+++ b/test/048-reflect-v8/src/AnnotationTest.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+public class AnnotationTest extends AnnotationTestHelpers {
+ public static void testAnnotationsByType() {
+ System.out.println("==============================");
+ System.out.println("Class annotations by type:");
+ System.out.println("==============================");
+
+ // Print associated annotations:
+ // * A is directly present or repeatably present on an element E;
+ // * No annotation of A is directly/repeatably present on an element
+ // AND E is a class AND A's type is inheritable, AND A is associated with its superclass.
+ // (Looks through subtypes recursively only if there's 0 result at each level,
+ // and the annotation is @Inheritable).
+ printAnnotationsByType(Calendar.class, SingleUser.class);
+ printAnnotationsByType(Calendars.class, SingleUser.class);
+
+ printAnnotationsByType(Calendar.class, User.class);
+ printAnnotationsByType(Calendars.class, User.class);
+
+ printAnnotationsByType(Calendar.class, User2.class); // Enforce ordering 'z,x,y'
+ printAnnotationsByType(Calendars.class, User2.class);
+
+ // NOTE:
+ // Order of outer-most annotations Calendars[C,C],S vs C,Calendars[C,C] is unspecified.
+ // In particular it's the order of #getDeclaredAnnotations which is completely unmentioned.
+ // The only requirement for #getAnnotationsByType is to have same ordering as
+ // #getDeclaredAnnotations.
+ // (Calendars[] itself has to maintain value() order).
+ printAnnotationsByType(Calendar.class, UserComplex.class); // Cs(C,C),C collapses into C,C,C.
+ printAnnotationsByType(Calendars.class, UserComplex.class);
+
+ printAnnotationsByType(Calendar.class, UserSub.class);
+ printAnnotationsByType(Calendars.class, UserSub.class);
+
+ printAnnotationsByType(Calendar.class, UserSub2.class);
+ // The directly present "Calendar" annotation masks all the repeatably present
+ // "Calendar" annotations coming from User.
+ printAnnotationsByType(Calendars.class, UserSub2.class);
+ // Edge case: UserSub2 doesn't directly have a Calendars annotation,
+ // so it doesn't mask the "User" Calendars annotation.
+
+ System.out.println("-----------------------------");
+ System.out.println("-----------------------------");
+
+ }
+
+ public static void testDeclaredAnnotation() {
+ System.out.println("==============================");
+ System.out.println("Class declared annotation:");
+ System.out.println("==============================");
+
+ // Print directly present annotations:
+ //
+ // The element E has an annotation_item for it (accessible through an
+ // annotations_directory_item) corresponding to an annotation A,
+ // and A's type_idx must match that on the encoded_annotation (from the annotation_item).
+ // (Does not look through the subtypes recursively)
+ printDeclaredAnnotation(SingleUser.class, Calendar.class);
+ printDeclaredAnnotation(SingleUser.class, Calendars.class);
+
+ printDeclaredAnnotation(User.class, Calendar.class);
+ printDeclaredAnnotation(User.class, Calendars.class);
+
+ printDeclaredAnnotation(UserComplex.class, Calendar.class);
+ printDeclaredAnnotation(UserComplex.class, Calendars.class);
+
+ printDeclaredAnnotation(UserSub.class, Calendar.class);
+ printDeclaredAnnotation(UserSub.class, Calendars.class);
+
+ printDeclaredAnnotation(UserSub2.class, Calendar.class);
+ printDeclaredAnnotation(UserSub2.class, Calendars.class);
+
+ System.out.println("-----------------------------");
+ System.out.println("-----------------------------");
+ }
+
+ public static void testDeclaredAnnotationsByType() {
+ System.out.println("==============================");
+ System.out.println("Declared class annotations by type:");
+ System.out.println("==============================");
+
+ // A is directly present or repeatably present on an element E;
+ // -- (does not do any recursion for classes regardless of @Inherited)
+ printDeclaredAnnotationsByType(Calendar.class, SingleUser.class);
+ printDeclaredAnnotationsByType(Calendars.class, SingleUser.class);
+
+ printDeclaredAnnotationsByType(Calendar.class, User.class);
+ printDeclaredAnnotationsByType(Calendars.class, User.class);
+
+ printDeclaredAnnotationsByType(Calendar.class, User2.class); // Enforce ordering 'z,x,y'
+ printDeclaredAnnotationsByType(Calendars.class, User2.class);
+
+ printDeclaredAnnotationsByType(Calendar.class, UserComplex.class);
+ printDeclaredAnnotationsByType(Calendars.class, UserComplex.class);
+
+ printDeclaredAnnotationsByType(Calendar.class, UserSub.class);
+ printDeclaredAnnotationsByType(Calendars.class, UserSub.class);
+
+ printDeclaredAnnotationsByType(Calendar.class, UserSub2.class);
+ // The directly present "Calendar" annotation masks all the repeatably present "Calendar"
+ // annotations coming from User.
+ printDeclaredAnnotationsByType(Calendars.class, UserSub2.class);
+ // Edge case: UserSub2 doesn't directly have a Calendars annotation,
+ // so it doesn't mask the "User" Calendars annotation.
+
+ System.out.println("-----------------------------");
+ System.out.println("-----------------------------");
+ }
+
+ // Print the annotation "annotationClass" that is associated with an element denoted by
+ // "annotationUseClass."
+ private static <A extends Annotation> void printAnnotationsByType(Class<A> annotationClass,
+ Class<?> annotationUseClass) {
+ A[] annotationsByType = annotationUseClass.getAnnotationsByType(annotationClass);
+
+ String msg = "Annotations by type, defined by class "
+ + annotationUseClass.getName() + " with annotation " + annotationClass.getName() + ": "
+ + asString(annotationsByType);
+
+
+ System.out.println(msg);
+ }
+
+ private static <A extends Annotation> void printDeclaredAnnotation(Class<?> annotationUseClass,
+ Class<A> annotationDefClass) {
+ A anno = annotationUseClass.getDeclaredAnnotation(annotationDefClass);
+
+ String msg = asString(anno);
+
+ System.out.println("Declared annotations by class " + annotationUseClass
+ + ", annotation " + annotationDefClass + ": " + msg);
+ }
+
+ // Print the annotation "annotationClass" that is directly/indirectly present with an element
+ // denoted by "annotationUseClass."
+ private static <A extends Annotation> void printDeclaredAnnotationsByType(
+ Class<A> annotationClass, Class<?> annotationUseClass) {
+ A[] annotationsByType = annotationUseClass.getDeclaredAnnotationsByType(annotationClass);
+
+ String msg = "Declared annnotations by type, defined by class " + annotationUseClass.getName()
+ + " with annotation " + annotationClass.getName() + ": "
+ + asString(annotationsByType);
+
+ System.out.println(msg);
+ }
+
+ public static void testMethodAnnotationsByType() {
+ System.out.println("==============================");
+ System.out.println("Method annotations by type:");
+ System.out.println("==============================");
+
+ // Print associated annotations:
+ // * A is directly present or repeatably present on an element E;
+ // * No annotation of A is directly/repeatably present on an element AND E is a class
+ // AND A's type is inheritable, AND A is associated with its superclass.
+ // (Looks through subtypes recursively only if there's 0 result at each level,
+ // and the annotation is @Inheritable).
+ printMethodAnnotationsByType(Calendar.class, "singleUser", AnnotationTestFixture.class);
+ printMethodAnnotationsByType(Calendars.class, "singleUser", AnnotationTestFixture.class);
+
+ printMethodAnnotationsByType(Calendar.class, "user", AnnotationTestFixture.class);
+ printMethodAnnotationsByType(Calendars.class, "user", AnnotationTestFixture.class);
+
+ printMethodAnnotationsByType(Calendar.class, "user2", AnnotationTestFixture.class);
+ printMethodAnnotationsByType(Calendars.class, "user2", AnnotationTestFixture.class);
+
+ printMethodAnnotationsByType(Calendar.class, "userComplex", AnnotationTestFixture.class);
+ printMethodAnnotationsByType(Calendars.class, "userComplex", AnnotationTestFixture.class);
+
+ System.out.println("-----------------------------");
+ System.out.println("-----------------------------");
+ }
+
+ // Print the annotation "annotationClass" that is associated with an element denoted by
+ // "annotationUseClass" method methodName.
+ private static <A extends Annotation> void printMethodAnnotationsByType(Class<A> annotationClass,
+ String methodName, Class<?> annotationUseClass) {
+ Method m = null;
+ try {
+ m = annotationUseClass.getDeclaredMethod(methodName);
+ } catch (Throwable t) {
+ throw new AssertionError(t);
+ }
+ A[] annotationsByType = m.getAnnotationsByType(annotationClass);
+
+ String msg = "Annotations by type, defined by method " + m.getName() + " with annotation " +
+ annotationClass.getName() + ": " +
+ asString(annotationsByType);
+
+ System.out.println(msg);
+ }
+
+ public static void testMethodDeclaredAnnotations() {
+ System.out.println("==============================");
+ System.out.println("Declared method annotations:");
+ System.out.println("==============================");
+
+ printMethodDeclaredAnnotation(Calendar.class, "singleUser", AnnotationTestFixture.class);
+ printMethodDeclaredAnnotation(Calendars.class, "singleUser", AnnotationTestFixture.class);
+
+ printMethodDeclaredAnnotation(Calendar.class, "user", AnnotationTestFixture.class);
+ printMethodDeclaredAnnotation(Calendars.class, "user", AnnotationTestFixture.class);
+
+ printMethodDeclaredAnnotation(Calendar.class, "user2", AnnotationTestFixture.class);
+ printMethodDeclaredAnnotation(Calendars.class, "user2", AnnotationTestFixture.class);
+
+ printMethodDeclaredAnnotation(Calendar.class, "userComplex", AnnotationTestFixture.class);
+ printMethodDeclaredAnnotation(Calendars.class, "userComplex", AnnotationTestFixture.class);
+
+ System.out.println("-----------------------------");
+ System.out.println("-----------------------------");
+ }
+
+ // Print the annotation "annotationClass" that is associated with an element denoted by
+ // methodName in annotationUseClass.
+ private static <A extends Annotation> void printMethodDeclaredAnnotation(Class<A> annotationClass,
+ String methodName, Class<?> annotationUseClass) {
+ Method m = null;
+ try {
+ m = annotationUseClass.getDeclaredMethod(methodName);
+ } catch (Throwable t) {
+ throw new AssertionError(t);
+ }
+ Annotation annotationsByType = m.getDeclaredAnnotation(annotationClass);
+
+ String msg = "Annotations declared by method " + m.getName() + " with annotation "
+ + annotationClass.getName() + ": "
+ + asString(annotationsByType);
+
+ System.out.println(msg);
+ }
+
+ public static void testMethodDeclaredAnnotationsByType() {
+ System.out.println("==============================");
+ System.out.println("Declared method annotations by type:");
+ System.out.println("==============================");
+
+ printMethodDeclaredAnnotationByType(Calendar.class, "singleUser", AnnotationTestFixture.class);
+ printMethodDeclaredAnnotationByType(Calendars.class, "singleUser", AnnotationTestFixture.class);
+
+ printMethodDeclaredAnnotationByType(Calendar.class, "user", AnnotationTestFixture.class);
+ printMethodDeclaredAnnotationByType(Calendars.class, "user", AnnotationTestFixture.class);
+
+ printMethodDeclaredAnnotationByType(Calendar.class, "user2", AnnotationTestFixture.class);
+ printMethodDeclaredAnnotationByType(Calendars.class, "user2", AnnotationTestFixture.class);
+
+ printMethodDeclaredAnnotationByType(Calendar.class, "userComplex", AnnotationTestFixture.class);
+ printMethodDeclaredAnnotationByType(Calendars.class, "userComplex",
+ AnnotationTestFixture.class);
+
+ System.out.println("-----------------------------");
+ System.out.println("-----------------------------");
+ }
+
+ // Print the annotation "annotationClass" that is associated with an element denoted by
+ // methodName in annotationUseClass.
+ private static <A extends Annotation> void printMethodDeclaredAnnotationByType(
+ Class<A> annotationClass, String methodName, Class<?> annotationUseClass) {
+ Method m = null;
+ try {
+ m = annotationUseClass.getDeclaredMethod(methodName);
+ } catch (Throwable t) {
+ throw new AssertionError(t);
+ }
+ A[] annotationsByType = m.getDeclaredAnnotationsByType(annotationClass);
+
+ String msg = "Annotations by type, defined by method " + m.getName() + " with annotation "
+ + annotationClass.getName() + ": "
+ + asString(annotationsByType);
+
+ System.out.println(msg);
+ }
+}
diff --git a/test/048-reflect-v8/src/AnnotationTestFixture.java b/test/048-reflect-v8/src/AnnotationTestFixture.java
new file mode 100644
index 0000000..248dfac
--- /dev/null
+++ b/test/048-reflect-v8/src/AnnotationTestFixture.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class AnnotationTestFixture {
+
+ @Calendar(dayOfWeek="single", hour=23)
+ public static void singleUser() {
+
+ }
+ @Calendars ({
+ @Calendar(dayOfMonth="last"),
+ @Calendar(dayOfWeek="Fri", hour=23)
+ })
+ public static void user() {
+
+ }
+
+ @Calendars ({
+ @Calendar(dayOfMonth="z"),
+ @Calendar(dayOfMonth="x"),
+ @Calendar(dayOfMonth="y")
+ })
+ public static void user2() {
+
+ }
+
+ @Calendar(dayOfMonth="afirst")
+ @Calendars ({
+ @Calendar(dayOfMonth="zsecond"),
+ @Calendar(dayOfMonth="athird", hour=23)
+ })
+ public static void userComplex() {
+
+ }
+}
diff --git a/test/048-reflect-v8/src/AnnotationTestHelpers.java b/test/048-reflect-v8/src/AnnotationTestHelpers.java
new file mode 100644
index 0000000..6b5bea2
--- /dev/null
+++ b/test/048-reflect-v8/src/AnnotationTestHelpers.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.annotation.Annotation;
+
+public class AnnotationTestHelpers {
+ // Provide custom print function that print a deterministic output.
+ // Note that Annotation#toString has unspecified order: it prints out the
+ // fields, which is why we can't rely on it.
+
+ public static String asString(Annotation anno) {
+ if (anno instanceof Calendar) {
+ return asString((Calendar)anno);
+ } else if (anno instanceof Calendars) {
+ return asString((Calendars)anno);
+ } else {
+ if (anno == null) {
+ return "<null>";
+ }
+ // Fall-back, usually would only go here in a test failure.
+ return anno.toString();
+ }
+ }
+
+ public static String asString(Annotation[] annos) {
+ String msg = "";
+
+ if (annos == null) {
+ msg += "<null>";
+ } else if (annos.length == 0) {
+ msg += "<empty>";
+ } else {
+ for (int i = 0; i < annos.length; ++i) {
+ msg += asString(annos[i]);
+
+ if (i != annos.length - 1) {
+ msg += ", ";
+ }
+ }
+ }
+
+ return msg;
+ }
+
+ public static String asString(Calendar calendar) {
+ if (calendar == null) {
+ return "<null>";
+ }
+
+ return "@Calendar(dayOfMonth=" + calendar.dayOfMonth() + ", dayOfWeek=" +
+ calendar.dayOfWeek() + ", hour=" + calendar.hour() + ")";
+ }
+
+ public static String asString(Calendars calendars) {
+ if (calendars == null) {
+ return "<null>";
+ }
+
+ String s = "@Calendars(value=[";
+
+ Calendar[] allValues = calendars.value();
+ for (int i = 0; i < allValues.length; ++i) {
+ s += asString(allValues[i]);
+ if (i != allValues.length - 1) {
+ s += ", ";
+ }
+ }
+
+ s += "])";
+
+ return s;
+ }
+}
diff --git a/test/048-reflect-v8/src/Calendar.java b/test/048-reflect-v8/src/Calendar.java
new file mode 100644
index 0000000..4a16573
--- /dev/null
+++ b/test/048-reflect-v8/src/Calendar.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+// This is a plain old non-1.8 annotation. At runtime we can see that it has a
+// "Repeatable" annotation if we query with getDeclaredAnnotation(Repeatable.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Repeatable(Calendars.class)
+@Inherited // note: container must also be @Inherited by JLS.
+public @interface Calendar {
+ String dayOfMonth() default "unspecified_month";
+ String dayOfWeek() default "unspecified_week";
+ int hour() default 6;
+}
+
diff --git a/test/048-reflect-v8/src/Calendars.java b/test/048-reflect-v8/src/Calendars.java
new file mode 100644
index 0000000..caeda52
--- /dev/null
+++ b/test/048-reflect-v8/src/Calendars.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+// Plain old annotation, there's nothing 1.8 specific about it.
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited // note: elements must also be @Inherited by JLS.
+public @interface Calendars {
+ Calendar[] value();
+}
diff --git a/test/048-reflect-v8/src/DefaultDeclared.java b/test/048-reflect-v8/src/DefaultDeclared.java
new file mode 100644
index 0000000..16e8a24
--- /dev/null
+++ b/test/048-reflect-v8/src/DefaultDeclared.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+public class DefaultDeclared {
+ interface DefaultInterface {
+ default void sayHi() {
+ System.out.println("hi default");
+ }
+ }
+
+ interface RegularInterface {
+ void sayHi();
+ }
+
+ class ImplementsWithDefault implements DefaultInterface {}
+
+ class ImplementsWithDeclared implements DefaultInterface {
+ public void sayHi() {
+ System.out.println("hello specific from default");
+ }
+ }
+
+ abstract class UnimplementedWithRegular implements RegularInterface { }
+
+ class ImplementsWithRegular implements RegularInterface {
+ public void sayHi() {
+ System.out.println("hello specific");
+ }
+ }
+
+ private static void printGetMethod(Class<?> klass) {
+ Method m;
+ try {
+ m = klass.getDeclaredMethod("sayHi");
+ System.out.println("No error thrown for class " + klass.toString());
+ } catch (NoSuchMethodException e) {
+ System.out.println("NoSuchMethodException thrown for class " + klass.toString());
+ } catch (Throwable t) {
+ System.out.println("Unknown error thrown for class " + klass.toString());
+ t.printStackTrace();
+ }
+ }
+
+ public static void test() {
+ System.out.println("==============================");
+ System.out.println("Are These Methods found by getDeclaredMethod:");
+ System.out.println("==============================");
+
+ printGetMethod(DefaultInterface.class);
+ printGetMethod(RegularInterface.class);
+ printGetMethod(ImplementsWithDefault.class);
+ printGetMethod(ImplementsWithDeclared.class);
+ printGetMethod(ImplementsWithRegular.class);
+ printGetMethod(UnimplementedWithRegular.class);
+ }
+}
diff --git a/test/048-reflect-v8/src/IFaceA.java b/test/048-reflect-v8/src/IFaceA.java
new file mode 100644
index 0000000..9b1f610
--- /dev/null
+++ b/test/048-reflect-v8/src/IFaceA.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Stored as a complex annotation Calendars(Calendar,Calendar)
+// in the binary.
+@Calendars ({
+ @Calendar(dayOfMonth="if_a_first"),
+ @Calendar(dayOfMonth="if_b_last")
+})
+public interface IFaceA {
+}
diff --git a/test/048-reflect-v8/src/IFaceSimple.java b/test/048-reflect-v8/src/IFaceSimple.java
new file mode 100644
index 0000000..93cf610
--- /dev/null
+++ b/test/048-reflect-v8/src/IFaceSimple.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Simple annotation, no container.
+@Calendar(dayOfMonth="if_simple_first")
+public interface IFaceSimple {
+
+}
diff --git a/test/048-reflect-v8/src/IsDefaultTest.java b/test/048-reflect-v8/src/IsDefaultTest.java
new file mode 100644
index 0000000..177dcf1
--- /dev/null
+++ b/test/048-reflect-v8/src/IsDefaultTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+public class IsDefaultTest {
+ interface DefaultInterface {
+ default void sayHi() {
+ System.out.println("hi default");
+ }
+ }
+
+ interface RegularInterface {
+ void sayHi();
+ }
+
+ class ImplementsWithDefault implements DefaultInterface {}
+ class ImplementsWithRegular implements RegularInterface {
+ public void sayHi() {
+ System.out.println("hello specific");
+ }
+ }
+
+ private static void printIsDefault(Class<?> klass) {
+ Method m;
+ try {
+ m = klass.getMethod("sayHi");
+ } catch (Throwable t) {
+ System.out.println(t);
+ return;
+ }
+
+ boolean isDefault = m.isDefault();
+ System.out.println(klass.getName() + " is default = " + (isDefault ? "yes" : "no"));
+ }
+
+ public static void test() {
+ System.out.println("==============================");
+ System.out.println("Are These Methods Default:");
+ System.out.println("==============================");
+
+ printIsDefault(DefaultInterface.class);
+ printIsDefault(RegularInterface.class);
+ printIsDefault(ImplementsWithDefault.class);
+ printIsDefault(ImplementsWithRegular.class);
+ }
+}
diff --git a/test/048-reflect-v8/src/Main.java b/test/048-reflect-v8/src/Main.java
index 7fa2a92..b270e68 100644
--- a/test/048-reflect-v8/src/Main.java
+++ b/test/048-reflect-v8/src/Main.java
@@ -14,43 +14,15 @@
* limitations under the License.
*/
-import java.lang.reflect.Method;
-
public class Main {
- interface DefaultInterface {
- default void sayHi() {
- System.out.println("hi default");
- }
- }
-
- interface RegularInterface {
- void sayHi();
- }
-
- class ImplementsWithDefault implements DefaultInterface {}
- class ImplementsWithRegular implements RegularInterface {
- public void sayHi() {
- System.out.println("hello specific");
- }
- }
-
- private static void printIsDefault(Class<?> klass) {
- Method m;
- try {
- m = klass.getMethod("sayHi");
- } catch (Throwable t) {
- System.out.println(t);
- return;
- }
-
- boolean isDefault = m.isDefault();
- System.out.println(klass.getName() + " is default = " + (isDefault ? "yes" : "no"));
- }
-
public static void main(String[] args) {
- printIsDefault(DefaultInterface.class);
- printIsDefault(RegularInterface.class);
- printIsDefault(ImplementsWithDefault.class);
- printIsDefault(ImplementsWithRegular.class);
+ IsDefaultTest.test();
+ DefaultDeclared.test();
+ AnnotationTest.testAnnotationsByType();
+ AnnotationTest.testDeclaredAnnotation();
+ AnnotationTest.testDeclaredAnnotationsByType();
+ AnnotationTest.testMethodAnnotationsByType();
+ AnnotationTest.testMethodDeclaredAnnotations();
+ AnnotationTest.testMethodDeclaredAnnotationsByType();
}
}
diff --git a/test/048-reflect-v8/src/SingleUser.java b/test/048-reflect-v8/src/SingleUser.java
new file mode 100644
index 0000000..0f9c430
--- /dev/null
+++ b/test/048-reflect-v8/src/SingleUser.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Stored as a single "Calendar" annotation in the binary.
+@Calendar(dayOfWeek="single", hour=23)
+public class SingleUser {
+
+}
diff --git a/test/048-reflect-v8/src/User.java b/test/048-reflect-v8/src/User.java
new file mode 100644
index 0000000..003ceeb
--- /dev/null
+++ b/test/048-reflect-v8/src/User.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Stored as a complex annotation Calendars(Calendar,Calendar)
+// in the binary.
+//
+/* FIXME: Use this code instead, when Jack supports repeatable annotations properly.
+ *
+ * @Calendar(dayOfMonth="last")
+ * @Calendar(dayOfWeek="Fri", hour=23)
+ */
+@Calendars ({
+ @Calendar(dayOfMonth="last"),
+ @Calendar(dayOfWeek="Fri", hour=23)
+})
+public class User {
+
+}
diff --git a/test/048-reflect-v8/src/User2.java b/test/048-reflect-v8/src/User2.java
new file mode 100644
index 0000000..1a6049f
--- /dev/null
+++ b/test/048-reflect-v8/src/User2.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Stored as a complex annotation Calendars(Calendar,Calendar,Calendar)
+// in the binary.
+// (Check for order, should be z,x,y)
+@Calendars ({
+ @Calendar(dayOfMonth="z"),
+ @Calendar(dayOfMonth="x"),
+ @Calendar(dayOfMonth="y")
+})
+public class User2 {
+
+}
diff --git a/test/048-reflect-v8/src/UserComplex.java b/test/048-reflect-v8/src/UserComplex.java
new file mode 100644
index 0000000..e262349
--- /dev/null
+++ b/test/048-reflect-v8/src/UserComplex.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Stored as a complex annotation Calendars(Calendar,Calendar)
+// followed by a Calendar in the binary.
+// In other words { Calendars([C,C]), C }
+//
+// Note that trying to do {C,Calendars,C} or similar
+// is illegal by the JLS.
+@Calendar(dayOfMonth="afirst")
+@Calendars ({
+ @Calendar(dayOfMonth="zsecond"),
+ @Calendar(dayOfMonth="athird", hour=23)
+})
+// @Calendar(dayOfMonth="zlast") // Leave for future ordering test
+public class UserComplex {
+
+}
diff --git a/test/048-reflect-v8/src/UserSub.java b/test/048-reflect-v8/src/UserSub.java
new file mode 100644
index 0000000..d60aa6a
--- /dev/null
+++ b/test/048-reflect-v8/src/UserSub.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class UserSub
+ extends User
+ implements IFaceA, IFaceSimple {
+
+}
diff --git a/test/048-reflect-v8/src/UserSub2.java b/test/048-reflect-v8/src/UserSub2.java
new file mode 100644
index 0000000..13e2eb0
--- /dev/null
+++ b/test/048-reflect-v8/src/UserSub2.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This calendar subsumes anything else we would've normally gotten from the subclass.
+@Calendar(dayOfMonth="sub2")
+public class UserSub2
+ extends User
+ implements IFaceA, IFaceSimple {
+
+}
diff --git a/test/085-old-style-inner-class/build b/test/085-old-style-inner-class/build
index 6f50a76..21dc662 100644
--- a/test/085-old-style-inner-class/build
+++ b/test/085-old-style-inner-class/build
@@ -23,8 +23,8 @@
${JAVAC} -source 1.4 -target 1.4 -d classes `find src -name '*.java'`
if [ ${USE_JACK} = "true" ]; then
- ${JILL} classes --output classes.jack
- ${JACK} --import classes.jack --output-dex .
+ jar cf classes.jill.jar -C classes .
+ ${JACK} --import classes.jill.jar --output-dex .
else
# Suppress stderr to keep the inner class warnings out of the expected output.
${DX} --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes 2>/dev/null
diff --git a/test/091-override-package-private-method/build b/test/091-override-package-private-method/build
index 5a340dc..073a4ba 100755
--- a/test/091-override-package-private-method/build
+++ b/test/091-override-package-private-method/build
@@ -24,14 +24,12 @@
mv classes/OverridePackagePrivateMethodSuper.class classes-ex
if [ ${USE_JACK} = "true" ]; then
- # Create .jack files from classes generated with javac.
- ${JILL} classes --output classes.jack
- ${JILL} classes-ex --output classes-ex.jack
+ jar cf classes.jill.jar -C classes .
+ jar cf classes-ex.jill.jar -C classes-ex .
- # Create DEX files from .jack files.
- ${JACK} --import classes.jack --output-dex .
+ ${JACK} --import classes.jill.jar --output-dex .
zip $TEST_NAME.jar classes.dex
- ${JACK} --import classes-ex.jack --output-dex .
+ ${JACK} --import classes-ex.jill.jar --output-dex .
zip ${TEST_NAME}-ex.jar classes.dex
else
if [ ${NEED_DEX} = "true" ]; then
diff --git a/test/097-duplicate-method/build b/test/097-duplicate-method/build
index a855873..4525549 100644
--- a/test/097-duplicate-method/build
+++ b/test/097-duplicate-method/build
@@ -23,10 +23,10 @@
${JACK} --output-jack src.jack src
${JASMIN} -d classes src/*.j
- ${JILL} classes --output jasmin.jack
+ jar cf jasmin.jill.jar -C classes .
# We set jack.import.type.policy=keep-first to consider class definitions from jasmin first.
- ${JACK} --import jasmin.jack --import src.jack -D jack.import.type.policy=keep-first --output-dex .
+ ${JACK} --import jasmin.jill.jar --import src.jack -D jack.import.type.policy=keep-first --output-dex .
else
${JAVAC} -d classes src/*.java
${JASMIN} -d classes src/*.j
diff --git a/test/111-unresolvable-exception/build b/test/111-unresolvable-exception/build
index e772fb8..58ac26d 100644
--- a/test/111-unresolvable-exception/build
+++ b/test/111-unresolvable-exception/build
@@ -22,8 +22,8 @@
rm classes/TestException.class
if [ ${USE_JACK} = "true" ]; then
- ${JILL} classes --output classes.jack
- ${JACK} --import classes.jack --output-dex .
+ jar cf classes.jill.jar -C classes .
+ ${JACK} --import classes.jill.jar --output-dex .
else
${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes
fi
diff --git a/test/113-multidex/build b/test/113-multidex/build
index 8ef5c0e..4557ccd 100644
--- a/test/113-multidex/build
+++ b/test/113-multidex/build
@@ -28,14 +28,12 @@
rm classes2/Second.class classes2/FillerA.class classes2/FillerB.class classes2/Inf*.class
if [ ${USE_JACK} = "true" ]; then
- # Create .jack files from classes generated with javac.
- ${JILL} classes --output classes.jack
- ${JILL} classes2 --output classes2.jack
+ jar cf classes.jill.jar -C classes .
+ jar cf classes2.jill.jar -C classes2 .
- # Create DEX files from .jack files.
- ${JACK} --import classes.jack --output-dex .
+ ${JACK} --import classes.jill.jar --output-dex .
mv classes.dex classes-1.dex
- ${JACK} --import classes2.jack --output-dex .
+ ${JACK} --import classes2.jill.jar --output-dex .
mv classes.dex classes2.dex
mv classes-1.dex classes.dex
else
diff --git a/test/115-native-bridge/expected.txt b/test/115-native-bridge/expected.txt
index b003307..852ec2e 100644
--- a/test/115-native-bridge/expected.txt
+++ b/test/115-native-bridge/expected.txt
@@ -1,4 +1,3 @@
-Code cache exists: './code_cache'.
Native bridge initialized.
Checking for getEnvValues.
Ready for native bridge tests.
diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc
index b70ca4f..aca356b 100644
--- a/test/115-native-bridge/nativebridge.cc
+++ b/test/115-native-bridge/nativebridge.cc
@@ -269,16 +269,12 @@
struct stat st;
if (app_code_cache_dir != nullptr) {
if (stat(app_code_cache_dir, &st) == 0) {
- if (S_ISDIR(st.st_mode)) {
- printf("Code cache exists: '%s'.\n", app_code_cache_dir);
- } else {
+ if (!S_ISDIR(st.st_mode)) {
printf("Code cache is not a directory.\n");
}
} else {
perror("Error when stat-ing the code_cache:");
}
- } else {
- printf("app_code_cache_dir is null.\n");
}
if (art_cbs != nullptr) {
diff --git a/test/115-native-bridge/run b/test/115-native-bridge/run
index ea2045b..aeb5721 100644
--- a/test/115-native-bridge/run
+++ b/test/115-native-bridge/run
@@ -28,4 +28,4 @@
LEFT=$(echo ${ARGS} | sed -r 's/-Djava.library.path.*//')
RIGHT=$(echo ${ARGS} | sed -r 's/.*Djava.library.path[^ ]* //')
MODARGS="${LEFT} -Djava.library.path=`pwd` ${RIGHT}"
-exec ${RUN} --runtime-option -XX:NativeBridge=libnativebridgetest.so ${MODARGS} NativeBridgeMain
+exec ${RUN} --runtime-option -Xforce-nb-testing --runtime-option -XX:NativeBridge=libnativebridgetest.so ${MODARGS} NativeBridgeMain
diff --git a/test/121-modifiers/build b/test/121-modifiers/build
index 85b69e9..771dd51 100644
--- a/test/121-modifiers/build
+++ b/test/121-modifiers/build
@@ -31,9 +31,9 @@
# mv Main.class A.class A\$B.class A\$C.class classes/
if [ ${USE_JACK} = "true" ]; then
- ${JILL} classes --output classes.jack
+ jar cf classes.jill.jar -C classes .
# Workaround b/19561685: disable sanity checks to produce a DEX file with invalid modifiers.
- ${JACK} --sanity-checks off --import classes.jack --output-dex .
+ ${JACK} --sanity-checks off --import classes.jill.jar --output-dex .
else
${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes
fi
diff --git a/test/124-missing-classes/build b/test/124-missing-classes/build
index b92ecf9..0a340a2 100644
--- a/test/124-missing-classes/build
+++ b/test/124-missing-classes/build
@@ -27,8 +27,8 @@
rm 'classes/Main$MissingInnerClass.class'
if [ ${USE_JACK} = "true" ]; then
- ${JILL} classes --output classes.jack
- ${JACK} --import classes.jack --output-dex .
+ jar cf classes.jill.jar -C classes .
+ ${JACK} --import classes.jill.jar --output-dex .
else
${DX} -JXmx256m --debug --dex --output=classes.dex classes
fi
diff --git a/test/126-miranda-multidex/build b/test/126-miranda-multidex/build
index b7f2118..00b9ba0 100644
--- a/test/126-miranda-multidex/build
+++ b/test/126-miranda-multidex/build
@@ -28,14 +28,12 @@
rm classes2/Main.class classes2/MirandaAbstract.class classes2/MirandaClass*.class classes2/MirandaInterface2*.class
if [ ${USE_JACK} = "true" ]; then
- # Create .jack files from classes generated with javac.
- ${JILL} classes --output classes.jack
- ${JILL} classes2 --output classes2.jack
+ jar cf classes.jill.jar -C classes .
+ jar cf classes2.jill.jar -C classes2 .
- # Create DEX files from .jack files.
- ${JACK} --import classes.jack --output-dex .
+ ${JACK} --import classes.jill.jar --output-dex .
mv classes.dex classes-1.dex
- ${JACK} --import classes2.jack --output-dex .
+ ${JACK} --import classes2.jill.jar --output-dex .
mv classes.dex classes2.dex
mv classes-1.dex classes.dex
else
diff --git a/test/127-checker-secondarydex/build b/test/127-checker-secondarydex/build
index 0d9f4d6..7ce46ac 100755
--- a/test/127-checker-secondarydex/build
+++ b/test/127-checker-secondarydex/build
@@ -24,14 +24,12 @@
mv classes/Super.class classes-ex
if [ ${USE_JACK} = "true" ]; then
- # Create .jack files from classes generated with javac.
- ${JILL} classes --output classes.jack
- ${JILL} classes-ex --output classes-ex.jack
+ jar cf classes.jill.jar -C classes .
+ jar cf classes-ex.jill.jar -C classes-ex .
- # Create DEX files from .jack files.
- ${JACK} --import classes.jack --output-dex .
+ ${JACK} --import classes.jill.jar --output-dex .
zip $TEST_NAME.jar classes.dex
- ${JACK} --import classes-ex.jack --output-dex .
+ ${JACK} --import classes-ex.jill.jar --output-dex .
zip ${TEST_NAME}-ex.jar classes.dex
else
if [ ${NEED_DEX} = "true" ]; then
diff --git a/test/127-checker-secondarydex/src/Test.java b/test/127-checker-secondarydex/src/Test.java
index 266ed19..438e854 100644
--- a/test/127-checker-secondarydex/src/Test.java
+++ b/test/127-checker-secondarydex/src/Test.java
@@ -23,7 +23,7 @@
System.out.println("Test");
}
- /// CHECK-START: java.lang.Integer Test.toInteger() ssa_builder (after)
+ /// CHECK-START: java.lang.Integer Test.toInteger() builder (after)
/// CHECK: LoadClass needs_access_check:false klass:java.lang.Integer
public Integer toInteger() {
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index 87656bc..45251b8 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -53,6 +53,7 @@
extern "C" JNIEXPORT jboolean JNICALL Java_Main_sleep(JNIEnv*, jobject, jint, jboolean, jdouble) {
// Keep pausing.
+ printf("Going to sleep\n");
for (;;) {
pause();
}
diff --git a/test/137-cfi/expected.txt b/test/137-cfi/expected.txt
index 8db7853..6a5618e 100644
--- a/test/137-cfi/expected.txt
+++ b/test/137-cfi/expected.txt
@@ -1,2 +1 @@
JNI_OnLoad called
-JNI_OnLoad called
diff --git a/test/137-cfi/run b/test/137-cfi/run
index 6f4bcfe..8ec98c1 100755
--- a/test/137-cfi/run
+++ b/test/137-cfi/run
@@ -20,4 +20,5 @@
# Test with minimal compressed debugging information.
# Check only method names (parameters are omitted to save space).
-${RUN} "$@" -Xcompiler-option --generate-mini-debug-info
+# Temporarily disable due to bug 27172087 (leak/race in libunwind).
+# ${RUN} "$@" -Xcompiler-option --generate-mini-debug-info
diff --git a/test/137-cfi/src/Main.java b/test/137-cfi/src/Main.java
index 7755338..d60a4eb 100644
--- a/test/137-cfi/src/Main.java
+++ b/test/137-cfi/src/Main.java
@@ -16,10 +16,7 @@
import java.io.BufferedReader;
import java.io.FileReader;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
+import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Comparator;
@@ -98,9 +95,12 @@
throw new RuntimeException("Couldn't parse process");
}
- // Wait a bit, so the forked process has time to run until its sleep phase.
+ // Wait until the forked process had time to run until its sleep phase.
try {
- Thread.sleep(5000);
+ InputStreamReader stdout = new InputStreamReader(p.getInputStream(), "UTF-8");
+ BufferedReader lineReader = new BufferedReader(stdout);
+ while (!lineReader.readLine().contains("Going to sleep")) {
+ }
} catch (Exception e) {
throw new RuntimeException(e);
}
diff --git a/test/444-checker-nce/src/Main.java b/test/444-checker-nce/src/Main.java
index 865355c..c96b18c 100644
--- a/test/444-checker-nce/src/Main.java
+++ b/test/444-checker-nce/src/Main.java
@@ -27,7 +27,7 @@
return m.g();
}
- /// CHECK-START: Main Main.thisTest() ssa_builder (after)
+ /// CHECK-START: Main Main.thisTest() builder (after)
/// CHECK: NullCheck
/// CHECK: InvokeStaticOrDirect
@@ -38,7 +38,7 @@
return g();
}
- /// CHECK-START: Main Main.newInstanceRemoveTest() ssa_builder (after)
+ /// CHECK-START: Main Main.newInstanceRemoveTest() builder (after)
/// CHECK: NewInstance
/// CHECK: NullCheck
/// CHECK: InvokeStaticOrDirect
@@ -52,7 +52,7 @@
return m.g();
}
- /// CHECK-START: Main Main.newArrayRemoveTest() ssa_builder (after)
+ /// CHECK-START: Main Main.newArrayRemoveTest() builder (after)
/// CHECK: NewArray
/// CHECK: NullCheck
/// CHECK: ArrayGet
@@ -178,7 +178,7 @@
return n.g();
}
- /// CHECK-START: Main Main.scopeRemoveTest(int, Main) ssa_builder (after)
+ /// CHECK-START: Main Main.scopeRemoveTest(int, Main) builder (after)
/// CHECK: NullCheck
/// CHECK-START: Main Main.scopeRemoveTest(int, Main) instruction_simplifier (after)
diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
index 8f9a32a..31bb94c 100644
--- a/test/449-checker-bce/src/Main.java
+++ b/test/449-checker-bce/src/Main.java
@@ -122,8 +122,9 @@
/// CHECK: ArraySet
static void constantIndexing1(int[] array) {
- array[5] = 1;
- array[4] = 1;
+ // Decreasing order: bc for 5 but not for 4.
+ array[5] = 11;
+ array[4] = 11;
}
@@ -136,17 +137,18 @@
/// CHECK: ArraySet
/// CHECK: BoundsCheck
/// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
/// CHECK-START: void Main.$opt$noinline$constantIndexing2(int[]) BCE (after)
- /// CHECK: LessThanOrEqual
- /// CHECK: Deoptimize
- /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ /// CHECK: BoundsCheck
/// CHECK: ArraySet
- /// CHECK-NOT: BoundsCheck
+ /// CHECK: BoundsCheck
/// CHECK: ArraySet
- /// CHECK-NOT: BoundsCheck
+ /// CHECK: BoundsCheck
/// CHECK: ArraySet
- /// CHECK-NOT: BoundsCheck
+ /// CHECK: BoundsCheck
/// CHECK: ArraySet
/// CHECK: BoundsCheck
/// CHECK: ArraySet
@@ -156,12 +158,39 @@
array[2] = 1;
array[3] = 1;
array[4] = 1;
- array[-1] = 1;
+ array[-1] = 1; // prevents the whole opt on [-1:4]
if (array[1] == 1) {
throw new Error("");
}
}
+ /// CHECK-START: void Main.constantIndexing2b(int[]) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+
+ /// CHECK-START: void Main.constantIndexing2b(int[]) BCE (after)
+ /// CHECK: Deoptimize
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+
+ static void constantIndexing2b(int[] array) {
+ array[0] = 7;
+ array[1] = 7;
+ array[2] = 7;
+ array[3] = 7;
+ }
/// CHECK-START: int[] Main.constantIndexing3(int[], int[], boolean) BCE (before)
/// CHECK: BoundsCheck
@@ -182,11 +211,9 @@
/// CHECK: ArraySet
/// CHECK-START: int[] Main.constantIndexing3(int[], int[], boolean) BCE (after)
- /// CHECK: LessThanOrEqual
/// CHECK: Deoptimize
/// CHECK-NOT: BoundsCheck
/// CHECK: ArrayGet
- /// CHECK: LessThanOrEqual
/// CHECK: Deoptimize
/// CHECK-NOT: BoundsCheck
/// CHECK: ArraySet
@@ -220,14 +247,14 @@
/// CHECK: ArraySet
/// CHECK-START: void Main.constantIndexing4(int[]) BCE (after)
- /// CHECK-NOT: LessThanOrEqual
+ /// CHECK-NOT: Deoptimize
/// CHECK: BoundsCheck
/// CHECK: ArraySet
// There is only one array access. It's not beneficial
// to create a compare with deoptimization instruction.
static void constantIndexing4(int[] array) {
- array[0] = 1;
+ array[0] = -1;
}
@@ -260,10 +287,221 @@
/// CHECK-START: void Main.constantIndexing6(int[]) BCE (after)
/// CHECK: Deoptimize
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
static void constantIndexing6(int[] array) {
- array[3] = 1;
- array[4] = 1;
+ array[3] = 111;
+ array[4] = 111;
+ }
+
+ /// CHECK-START: void Main.constantIndexing7(int[], int) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+
+ /// CHECK-START: void Main.constantIndexing7(int[], int) BCE (after)
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+
+ static void constantIndexing7(int[] array, int base) {
+ // With constant offsets to symbolic base.
+ array[base] = 10;
+ array[base + 1] = 20;
+ array[base + 2] = 30;
+ array[base + 3] = 40;
+ }
+
+ /// CHECK-START: void Main.constantIndexing8(int[], int) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+
+ /// CHECK-START: void Main.constantIndexing8(int[], int) BCE (after)
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+
+ static void constantIndexing8(int[] array, int base) {
+ // With constant offsets "both ways" to symbolic base.
+ array[base - 1] = 100;
+ array[base] = 200;
+ array[base + 1] = 300;
+ array[base + 2] = 400;
+ }
+
+ /// CHECK-START: void Main.constantIndexing9(int[], int) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+
+ /// CHECK-START: void Main.constantIndexing9(int[], int) BCE (after)
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+
+ static void constantIndexing9(int[] array, int base) {
+ // Final range is base..base+3 so conditional
+ // references may be included in the end.
+ array[base] = 0;
+ if (base != 12345)
+ array[base + 2] = 2;
+ array[base + 3] = 3;
+ if (base != 67890)
+ array[base + 1] = 1;
+ }
+
+ static void runAllConstantIndices() {
+ int[] a1 = { 0 };
+ int[] a6 = { 0, 0, 0, 0, 0, 0 };
+
+ boolean caught = false;
+ try {
+ constantIndexing1(a1);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ caught = true;
+ }
+ if (!caught) {
+ System.out.println("constant indices 1 failed!");
+ }
+
+ constantIndexing1(a6);
+ if (a6[4] != 11 || a6[5] != 11) {
+ System.out.println("constant indices 1 failed!");
+ }
+
+ caught = false;
+ try {
+ $opt$noinline$constantIndexing2(a6);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ caught = true;
+ }
+ if (!caught || a6[0] != 0 || a6[1] != 1 || a6[2] != 1 ||
+ a6[3] != 1 || a6[4] != 1 || a6[5] != 11) {
+ System.out.println("constant indices 2 failed!");
+ }
+
+ caught = false;
+ try {
+ constantIndexing2b(a1);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ caught = true;
+ }
+ if (!caught || a1[0] != 7) {
+ System.out.println("constant indices 2b failed!");
+ }
+
+ constantIndexing2b(a6);
+ if (a6[0] != 7 || a6[1] != 7 || a6[2] != 7 ||
+ a6[3] != 7 || a6[4] != 1 || a6[5] != 11) {
+ System.out.println("constant indices 2b failed!");
+ }
+
+ int[] b4 = new int[4];
+ constantIndexing3(a6, b4, true);
+ if (b4[0] != 7 || b4[1] != 7 || b4[2] != 7 || b4[3] != 7) {
+ System.out.println("constant indices 3 failed!");
+ }
+
+ constantIndexing4(a1);
+ if (a1[0] != -1) {
+ System.out.println("constant indices 4 failed!");
+ }
+
+ caught = false;
+ try {
+ constantIndexing5(a6);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ caught = true;
+ }
+ if (!caught) {
+ System.out.println("constant indices 5 failed!");
+ }
+
+ constantIndexing6(a6);
+ if (a6[0] != 7 || a6[1] != 7 || a6[2] != 7 ||
+ a6[3] != 111 || a6[4] != 111 || a6[5] != 11) {
+ System.out.println("constant indices 6 failed!");
+ }
+
+ constantIndexing7(a6, 1);
+ if (a6[0] != 7 || a6[1] != 10 || a6[2] != 20 ||
+ a6[3] != 30 || a6[4] != 40 || a6[5] != 11) {
+ System.out.println("constant indices 7 failed!");
+ }
+
+ caught = false;
+ try {
+ constantIndexing7(a6, 5);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ caught = true;
+ }
+ if (!caught || a6[0] != 7 || a6[1] != 10 || a6[2] != 20 ||
+ a6[3] != 30 || a6[4] != 40 || a6[5] != 10) {
+ System.out.println("constant indices 7 failed!");
+ }
+
+ constantIndexing8(a6, 1);
+ if (a6[0] != 100 || a6[1] != 200 || a6[2] != 300 ||
+ a6[3] != 400 || a6[4] != 40 || a6[5] != 10) {
+ System.out.println("constant indices 8 failed!");
+ }
+
+ caught = false;
+ try {
+ constantIndexing8(a6, 0);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ caught = true;
+ }
+ if (!caught || a6[0] != 100) {
+ System.out.println("constant indices 8 failed!");
+ }
+
+ constantIndexing9(a6, 0);
+ if (a6[0] != 0 || a6[1] != 1 || a6[2] != 2 ||
+ a6[3] != 3 || a6[4] != 40 || a6[5] != 10) {
+ System.out.println("constant indices 9 failed!");
+ }
}
// A helper into which the actual throwing function should be inlined.
@@ -1102,6 +1340,9 @@
static void testUnknownBounds() {
boolean caught = false;
+
+ runAllConstantIndices();
+
Main main = new Main();
main.foo1(new int[10], 0, 10, false);
if (main.sum != 10) {
diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java
index d48b30e..027a9d9 100644
--- a/test/450-checker-types/src/Main.java
+++ b/test/450-checker-types/src/Main.java
@@ -205,7 +205,7 @@
public static boolean $inline$InstanceofSubclassB(Object o) { return o instanceof SubclassB; }
public static boolean $inline$InstanceofSubclassC(Object o) { return o instanceof SubclassC; }
- /// CHECK-START: void Main.testInstanceOf_NotInlined(java.lang.Object) ssa_builder (after)
+ /// CHECK-START: void Main.testInstanceOf_NotInlined(java.lang.Object) builder (after)
/// CHECK-DAG: <<Cst0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
/// CHECK-DAG: <<IOf1:z\d+>> InstanceOf
@@ -229,7 +229,7 @@
}
}
- /// CHECK-START: void Main.testNotInstanceOf_NotInlined(java.lang.Object) ssa_builder (after)
+ /// CHECK-START: void Main.testNotInstanceOf_NotInlined(java.lang.Object) builder (after)
/// CHECK-DAG: <<Cst0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
/// CHECK-DAG: <<IOf1:z\d+>> InstanceOf
@@ -487,7 +487,7 @@
((SubclassA)a[0]).$noinline$g();
}
- /// CHECK-START: int Main.testLoadExceptionInCatchNonExact(int, int) ssa_builder (after)
+ /// CHECK-START: int Main.testLoadExceptionInCatchNonExact(int, int) builder (after)
/// CHECK: LoadException klass:java.lang.ArithmeticException can_be_null:false exact:false
public int testLoadExceptionInCatchNonExact(int x, int y) {
try {
@@ -497,7 +497,7 @@
}
}
- /// CHECK-START: int Main.testLoadExceptionInCatchExact(int) ssa_builder (after)
+ /// CHECK-START: int Main.testLoadExceptionInCatchExact(int) builder (after)
/// CHECK: LoadException klass:FinalException can_be_null:false exact:true
public int testLoadExceptionInCatchExact(int x) {
try {
@@ -511,7 +511,7 @@
}
}
- /// CHECK-START: int Main.testLoadExceptionInCatchAll(int, int) ssa_builder (after)
+ /// CHECK-START: int Main.testLoadExceptionInCatchAll(int, int) builder (after)
/// CHECK: LoadException klass:java.lang.Throwable can_be_null:false exact:false
public int testLoadExceptionInCatchAll(int x, int y) {
try {
@@ -532,7 +532,7 @@
return genericFinal.get();
}
- /// CHECK-START: SubclassC Main.inlineGenerics() ssa_builder (after)
+ /// CHECK-START: SubclassC Main.inlineGenerics() builder (after)
/// CHECK: <<Invoke:l\d+>> InvokeStaticOrDirect klass:SubclassC exact:false
/// CHECK-NEXT: Return [<<Invoke>>]
@@ -544,7 +544,7 @@
return c;
}
- /// CHECK-START: Final Main.inlineGenericsFinal() ssa_builder (after)
+ /// CHECK-START: Final Main.inlineGenericsFinal() builder (after)
/// CHECK: <<Invoke:l\d+>> InvokeStaticOrDirect klass:Final exact:true
/// CHECK-NEXT: Return [<<Invoke>>]
@@ -586,7 +586,7 @@
return new SubclassA();
}
- /// CHECK-START: void Main.updateNodesInTheSameBlockAsPhi(boolean) ssa_builder (after)
+ /// CHECK-START: void Main.updateNodesInTheSameBlockAsPhi(boolean) builder (after)
/// CHECK: <<Phi:l\d+>> Phi klass:Super
/// CHECK: NullCheck [<<Phi>>] klass:Super
@@ -620,7 +620,7 @@
}
- /// CHECK-START: void Main.argumentCheck(Super, double, SubclassA, Final) ssa_builder (after)
+ /// CHECK-START: void Main.argumentCheck(Super, double, SubclassA, Final) builder (after)
/// CHECK: ParameterValue klass:Main can_be_null:false exact:false
/// CHECK: ParameterValue klass:Super can_be_null:true exact:false
/// CHECK: ParameterValue
@@ -636,7 +636,7 @@
private int mainField = 0;
- /// CHECK-START: SuperInterface Main.getWiderType(boolean, Interface, OtherInterface) ssa_builder (after)
+ /// CHECK-START: SuperInterface Main.getWiderType(boolean, Interface, OtherInterface) builder (after)
/// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object
/// CHECK: Return [<<Phi>>]
private SuperInterface getWiderType(boolean cond, Interface a, OtherInterface b) {
@@ -692,7 +692,7 @@
getSuper();
}
- /// CHECK-START: void Main.testLoopPhiWithNullFirstInput(boolean) ssa_builder (after)
+ /// CHECK-START: void Main.testLoopPhiWithNullFirstInput(boolean) builder (after)
/// CHECK-DAG: <<Null:l\d+>> NullConstant
/// CHECK-DAG: <<Main:l\d+>> NewInstance klass:Main exact:true
/// CHECK-DAG: <<LoopPhi:l\d+>> Phi [<<Null>>,<<LoopPhi>>,<<Main>>] klass:Main exact:true
@@ -705,7 +705,7 @@
}
}
- /// CHECK-START: java.lang.Object[] Main.testInstructionsWithUntypedParent() ssa_builder (after)
+ /// CHECK-START: java.lang.Object[] Main.testInstructionsWithUntypedParent() builder (after)
/// CHECK-DAG: <<Null:l\d+>> NullConstant
/// CHECK-DAG: <<LoopPhi:l\d+>> Phi [<<Null>>,<<Phi:l\d+>>] klass:java.lang.Object[] exact:true
/// CHECK-DAG: <<Array:l\d+>> NewArray klass:java.lang.Object[] exact:true
diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java
index 3c8abeb..8d6bb65 100644
--- a/test/458-checker-instruction-simplification/src/Main.java
+++ b/test/458-checker-instruction-simplification/src/Main.java
@@ -46,6 +46,12 @@
}
}
+ public static void assertStringEquals(String expected, String result) {
+ if (expected == null ? result != null : !expected.equals(result)) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
/**
* Tiny programs exercising optimizations of arithmetic identities.
*/
@@ -1401,10 +1407,10 @@
// Test that conditions on float/double are not flipped.
- /// CHECK-START: int Main.floatConditionNotEqualOne(float) ssa_builder (after)
+ /// CHECK-START: int Main.floatConditionNotEqualOne(float) builder (after)
/// CHECK: LessThanOrEqual
- /// CHECK-START: int Main.floatConditionNotEqualOne(float) register (before)
+ /// CHECK-START: int Main.floatConditionNotEqualOne(float) instruction_simplifier_before_codegen (after)
/// CHECK-DAG: <<Arg:f\d+>> ParameterValue
/// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
/// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
@@ -1417,10 +1423,10 @@
return ((f > 42.0f) == true) ? 13 : 54;
}
- /// CHECK-START: int Main.doubleConditionEqualZero(double) ssa_builder (after)
+ /// CHECK-START: int Main.doubleConditionEqualZero(double) builder (after)
/// CHECK: LessThanOrEqual
- /// CHECK-START: int Main.doubleConditionEqualZero(double) register (before)
+ /// CHECK-START: int Main.doubleConditionEqualZero(double) instruction_simplifier_before_codegen (after)
/// CHECK-DAG: <<Arg:d\d+>> ParameterValue
/// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
/// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
@@ -1433,6 +1439,337 @@
return ((d > 42.0) != false) ? 13 : 54;
}
+ /// CHECK-START: int Main.intToDoubleToInt(int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>]
+ /// CHECK-DAG: Return [<<Int>>]
+
+ /// CHECK-START: int Main.intToDoubleToInt(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
+
+ /// CHECK-START: int Main.intToDoubleToInt(int) instruction_simplifier (after)
+ /// CHECK-NOT: TypeConversion
+
+ public static int intToDoubleToInt(int value) {
+ // Lossless conversion followed by a conversion back.
+ return (int) (double) value;
+ }
+
+ /// CHECK-START: java.lang.String Main.intToDoubleToIntPrint(int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: {{i\d+}} TypeConversion [<<Double>>]
+
+ /// CHECK-START: java.lang.String Main.intToDoubleToIntPrint(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: {{d\d+}} TypeConversion [<<Arg>>]
+
+ /// CHECK-START: java.lang.String Main.intToDoubleToIntPrint(int) instruction_simplifier (after)
+ /// CHECK-DAG: TypeConversion
+ /// CHECK-NOT: TypeConversion
+
+ public static String intToDoubleToIntPrint(int value) {
+ // Lossless conversion followed by a conversion back
+ // with another use of the intermediate result.
+ double d = (double) value;
+ int i = (int) d;
+ return "d=" + d + ", i=" + i;
+ }
+
+ /// CHECK-START: int Main.byteToDoubleToInt(byte) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:b\d+>> ParameterValue
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>]
+ /// CHECK-DAG: Return [<<Int>>]
+
+ /// CHECK-START: int Main.byteToDoubleToInt(byte) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:b\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
+
+ /// CHECK-START: int Main.byteToDoubleToInt(byte) instruction_simplifier (after)
+ /// CHECK-NOT: TypeConversion
+
+ public static int byteToDoubleToInt(byte value) {
+ // Lossless conversion followed by another conversion, use implicit conversion.
+ return (int) (double) value;
+ }
+
+ /// CHECK-START: int Main.floatToDoubleToInt(float) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:f\d+>> ParameterValue
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>]
+ /// CHECK-DAG: Return [<<Int>>]
+
+ /// CHECK-START: int Main.floatToDoubleToInt(float) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:f\d+>> ParameterValue
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: Return [<<Int>>]
+
+ /// CHECK-START: int Main.floatToDoubleToInt(float) instruction_simplifier (after)
+ /// CHECK-DAG: TypeConversion
+ /// CHECK-NOT: TypeConversion
+
+ public static int floatToDoubleToInt(float value) {
+ // Lossless conversion followed by another conversion.
+ return (int) (double) value;
+ }
+
+ /// CHECK-START: java.lang.String Main.floatToDoubleToIntPrint(float) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:f\d+>> ParameterValue
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: {{i\d+}} TypeConversion [<<Double>>]
+
+ /// CHECK-START: java.lang.String Main.floatToDoubleToIntPrint(float) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:f\d+>> ParameterValue
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: {{i\d+}} TypeConversion [<<Double>>]
+
+ public static String floatToDoubleToIntPrint(float value) {
+ // Lossless conversion followed by another conversion with
+ // an extra use of the intermediate result.
+ double d = (double) value;
+ int i = (int) d;
+ return "d=" + d + ", i=" + i;
+ }
+
+ /// CHECK-START: short Main.byteToDoubleToShort(byte) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:b\d+>> ParameterValue
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>]
+ /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>]
+ /// CHECK-DAG: Return [<<Short>>]
+
+ /// CHECK-START: short Main.byteToDoubleToShort(byte) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:b\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
+
+ /// CHECK-START: short Main.byteToDoubleToShort(byte) instruction_simplifier (after)
+ /// CHECK-NOT: TypeConversion
+
+ public static short byteToDoubleToShort(byte value) {
+ // Originally, this is byte->double->int->short. The first conversion is lossless,
+ // so we merge this with the second one to byte->int which we omit as it's an implicit
+ // conversion. Then we eliminate the resulting byte->short as an implicit conversion.
+ return (short) (double) value;
+ }
+
+ /// CHECK-START: short Main.charToDoubleToShort(char) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:c\d+>> ParameterValue
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>]
+ /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>]
+ /// CHECK-DAG: Return [<<Short>>]
+
+ /// CHECK-START: short Main.charToDoubleToShort(char) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:c\d+>> ParameterValue
+ /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: Return [<<Short>>]
+
+ /// CHECK-START: short Main.charToDoubleToShort(char) instruction_simplifier (after)
+ /// CHECK-DAG: TypeConversion
+ /// CHECK-NOT: TypeConversion
+
+ public static short charToDoubleToShort(char value) {
+ // Originally, this is char->double->int->short. The first conversion is lossless,
+ // so we merge this with the second one to char->int which we omit as it's an implicit
+ // conversion. Then we are left with the resulting char->short conversion.
+ return (short) (double) value;
+ }
+
+ /// CHECK-START: short Main.floatToIntToShort(float) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:f\d+>> ParameterValue
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>]
+ /// CHECK-DAG: Return [<<Short>>]
+
+ /// CHECK-START: short Main.floatToIntToShort(float) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:f\d+>> ParameterValue
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>]
+ /// CHECK-DAG: Return [<<Short>>]
+
+ public static short floatToIntToShort(float value) {
+ // Lossy FP to integral conversion followed by another conversion: no simplification.
+ return (short) value;
+ }
+
+ /// CHECK-START: int Main.intToFloatToInt(int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Float:f\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Float>>]
+ /// CHECK-DAG: Return [<<Int>>]
+
+ /// CHECK-START: int Main.intToFloatToInt(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Float:f\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Float>>]
+ /// CHECK-DAG: Return [<<Int>>]
+
+ public static int intToFloatToInt(int value) {
+ // Lossy integral to FP conversion followed another conversion: no simplification.
+ return (int) (float) value;
+ }
+
+ /// CHECK-START: double Main.longToIntToDouble(long) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Int>>]
+ /// CHECK-DAG: Return [<<Double>>]
+
+ /// CHECK-START: double Main.longToIntToDouble(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Int>>]
+ /// CHECK-DAG: Return [<<Double>>]
+
+ public static double longToIntToDouble(long value) {
+ // Lossy long-to-int conversion followed an integral to FP conversion: no simplification.
+ return (double) (int) value;
+ }
+
+ /// CHECK-START: long Main.longToIntToLong(long) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Long:j\d+>> TypeConversion [<<Int>>]
+ /// CHECK-DAG: Return [<<Long>>]
+
+ /// CHECK-START: long Main.longToIntToLong(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Long:j\d+>> TypeConversion [<<Int>>]
+ /// CHECK-DAG: Return [<<Long>>]
+
+ public static long longToIntToLong(long value) {
+ // Lossy long-to-int conversion followed an int-to-long conversion: no simplification.
+ return (long) (int) value;
+ }
+
+ /// CHECK-START: short Main.shortToCharToShort(short) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:s\d+>> ParameterValue
+ /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Char>>]
+ /// CHECK-DAG: Return [<<Short>>]
+
+ /// CHECK-START: short Main.shortToCharToShort(short) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:s\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
+
+ public static short shortToCharToShort(short value) {
+ // Integral conversion followed by non-widening integral conversion to original type.
+ return (short) (char) value;
+ }
+
+ /// CHECK-START: int Main.shortToLongToInt(short) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:s\d+>> ParameterValue
+ /// CHECK-DAG: <<Long:j\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Long>>]
+ /// CHECK-DAG: Return [<<Int>>]
+
+ /// CHECK-START: int Main.shortToLongToInt(short) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:s\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
+
+ public static int shortToLongToInt(short value) {
+ // Integral conversion followed by non-widening integral conversion, use implicit conversion.
+ return (int) (long) value;
+ }
+
+ /// CHECK-START: byte Main.shortToCharToByte(short) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:s\d+>> ParameterValue
+ /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: <<Byte:b\d+>> TypeConversion [<<Char>>]
+ /// CHECK-DAG: Return [<<Byte>>]
+
+ /// CHECK-START: byte Main.shortToCharToByte(short) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:s\d+>> ParameterValue
+ /// CHECK-DAG: <<Byte:b\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: Return [<<Byte>>]
+
+ public static byte shortToCharToByte(short value) {
+ // Integral conversion followed by non-widening integral conversion losing bits
+ // from the original type. Simplify to use only one conversion.
+ return (byte) (char) value;
+ }
+
+ /// CHECK-START: java.lang.String Main.shortToCharToBytePrint(short) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:s\d+>> ParameterValue
+ /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: {{b\d+}} TypeConversion [<<Char>>]
+
+ /// CHECK-START: java.lang.String Main.shortToCharToBytePrint(short) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:s\d+>> ParameterValue
+ /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: {{b\d+}} TypeConversion [<<Char>>]
+
+ public static String shortToCharToBytePrint(short value) {
+ // Integral conversion followed by non-widening integral conversion losing bits
+ // from the original type with an extra use of the intermediate result.
+ char c = (char) value;
+ byte b = (byte) c;
+ return "c=" + ((int) c) + ", b=" + ((int) b); // implicit conversions.
+ }
+
+ /// CHECK-START: byte Main.longAnd0xffToByte(long) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Mask:j\d+>> LongConstant 255
+ /// CHECK-DAG: <<And:j\d+>> And [<<Mask>>,<<Arg>>]
+ /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<And>>]
+ /// CHECK-DAG: <<Byte:b\d+>> TypeConversion [<<Int>>]
+ /// CHECK-DAG: Return [<<Byte>>]
+
+ /// CHECK-START: byte Main.longAnd0xffToByte(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Byte:b\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: Return [<<Byte>>]
+
+ /// CHECK-START: byte Main.longAnd0xffToByte(long) instruction_simplifier (after)
+ /// CHECK-NOT: And
+
+ public static byte longAnd0xffToByte(long value) {
+ return (byte) (value & 0xff);
+ }
+
+ /// CHECK-START: char Main.intAnd0x1ffffToChar(int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Mask:i\d+>> IntConstant 131071
+ /// CHECK-DAG: <<And:i\d+>> And [<<Mask>>,<<Arg>>]
+ /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<And>>]
+ /// CHECK-DAG: Return [<<Char>>]
+
+ /// CHECK-START: char Main.intAnd0x1ffffToChar(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: Return [<<Char>>]
+
+ /// CHECK-START: char Main.intAnd0x1ffffToChar(int) instruction_simplifier (after)
+ /// CHECK-NOT: And
+
+ public static char intAnd0x1ffffToChar(int value) {
+ // Keeping all significant bits and one more.
+ return (char) (value & 0x1ffff);
+ }
+
+ /// CHECK-START: short Main.intAnd0x17fffToShort(int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Mask:i\d+>> IntConstant 98303
+ /// CHECK-DAG: <<And:i\d+>> And [<<Mask>>,<<Arg>>]
+ /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<And>>]
+ /// CHECK-DAG: Return [<<Short>>]
+
+ /// CHECK-START: short Main.intAnd0x17fffToShort(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Mask:i\d+>> IntConstant 98303
+ /// CHECK-DAG: <<And:i\d+>> And [<<Mask>>,<<Arg>>]
+ /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<And>>]
+ /// CHECK-DAG: Return [<<Short>>]
+
+ public static short intAnd0x17fffToShort(int value) {
+ // No simplification: clearing a significant bit.
+ return (short) (value & 0x17fff);
+ }
+
public static void main(String[] args) {
int arg = 123456;
@@ -1518,6 +1855,57 @@
assertIntEquals(floatConditionNotEqualOne(43.0f), 13);
assertIntEquals(doubleConditionEqualZero(6.0), 54);
assertIntEquals(doubleConditionEqualZero(43.0), 13);
+
+ assertIntEquals(intToDoubleToInt(1234567), 1234567);
+ assertIntEquals(intToDoubleToInt(Integer.MIN_VALUE), Integer.MIN_VALUE);
+ assertIntEquals(intToDoubleToInt(Integer.MAX_VALUE), Integer.MAX_VALUE);
+ assertStringEquals(intToDoubleToIntPrint(7654321), "d=7654321.0, i=7654321");
+ assertIntEquals(byteToDoubleToInt((byte) 12), 12);
+ assertIntEquals(byteToDoubleToInt(Byte.MIN_VALUE), Byte.MIN_VALUE);
+ assertIntEquals(byteToDoubleToInt(Byte.MAX_VALUE), Byte.MAX_VALUE);
+ assertIntEquals(floatToDoubleToInt(11.3f), 11);
+ assertStringEquals(floatToDoubleToIntPrint(12.25f), "d=12.25, i=12");
+ assertIntEquals(byteToDoubleToShort((byte) 123), 123);
+ assertIntEquals(byteToDoubleToShort(Byte.MIN_VALUE), Byte.MIN_VALUE);
+ assertIntEquals(byteToDoubleToShort(Byte.MAX_VALUE), Byte.MAX_VALUE);
+ assertIntEquals(charToDoubleToShort((char) 1234), 1234);
+ assertIntEquals(charToDoubleToShort(Character.MIN_VALUE), Character.MIN_VALUE);
+ assertIntEquals(charToDoubleToShort(Character.MAX_VALUE), /* sign-extended */ -1);
+ assertIntEquals(floatToIntToShort(12345.75f), 12345);
+ assertIntEquals(floatToIntToShort((float)(Short.MIN_VALUE - 1)), Short.MAX_VALUE);
+ assertIntEquals(floatToIntToShort((float)(Short.MAX_VALUE + 1)), Short.MIN_VALUE);
+ assertIntEquals(intToFloatToInt(-54321), -54321);
+ assertDoubleEquals(longToIntToDouble(0x1234567812345678L), (double) 0x12345678);
+ assertDoubleEquals(longToIntToDouble(Long.MIN_VALUE), 0.0);
+ assertDoubleEquals(longToIntToDouble(Long.MAX_VALUE), -1.0);
+ assertLongEquals(longToIntToLong(0x1234567812345678L), 0x0000000012345678L);
+ assertLongEquals(longToIntToLong(0x1234567887654321L), 0xffffffff87654321L);
+ assertLongEquals(longToIntToLong(Long.MIN_VALUE), 0L);
+ assertLongEquals(longToIntToLong(Long.MAX_VALUE), -1L);
+ assertIntEquals(shortToCharToShort((short) -5678), (short) -5678);
+ assertIntEquals(shortToCharToShort(Short.MIN_VALUE), Short.MIN_VALUE);
+ assertIntEquals(shortToCharToShort(Short.MAX_VALUE), Short.MAX_VALUE);
+ assertIntEquals(shortToLongToInt((short) 5678), 5678);
+ assertIntEquals(shortToLongToInt(Short.MIN_VALUE), Short.MIN_VALUE);
+ assertIntEquals(shortToLongToInt(Short.MAX_VALUE), Short.MAX_VALUE);
+ assertIntEquals(shortToCharToByte((short) 0x1234), 0x34);
+ assertIntEquals(shortToCharToByte((short) 0x12f0), -0x10);
+ assertIntEquals(shortToCharToByte(Short.MIN_VALUE), 0);
+ assertIntEquals(shortToCharToByte(Short.MAX_VALUE), -1);
+ assertStringEquals(shortToCharToBytePrint((short) 1025), "c=1025, b=1");
+ assertStringEquals(shortToCharToBytePrint((short) 1023), "c=1023, b=-1");
+ assertStringEquals(shortToCharToBytePrint((short) -1), "c=65535, b=-1");
+
+ assertIntEquals(longAnd0xffToByte(0x1234432112344321L), 0x21);
+ assertIntEquals(longAnd0xffToByte(Long.MIN_VALUE), 0);
+ assertIntEquals(longAnd0xffToByte(Long.MAX_VALUE), -1);
+ assertIntEquals(intAnd0x1ffffToChar(0x43211234), 0x1234);
+ assertIntEquals(intAnd0x1ffffToChar(Integer.MIN_VALUE), 0);
+ assertIntEquals(intAnd0x1ffffToChar(Integer.MAX_VALUE), Character.MAX_VALUE);
+ assertIntEquals(intAnd0x17fffToShort(0x87654321), 0x4321);
+ assertIntEquals(intAnd0x17fffToShort(0x88888888), 0x0888);
+ assertIntEquals(intAnd0x17fffToShort(Integer.MIN_VALUE), 0);
+ assertIntEquals(intAnd0x17fffToShort(Integer.MAX_VALUE), Short.MAX_VALUE);
}
public static boolean booleanField;
diff --git a/test/464-checker-inline-sharpen-calls/src/Main.java b/test/464-checker-inline-sharpen-calls/src/Main.java
index 2222e0f..3f25635 100644
--- a/test/464-checker-inline-sharpen-calls/src/Main.java
+++ b/test/464-checker-inline-sharpen-calls/src/Main.java
@@ -39,7 +39,7 @@
m.invokeVirtual();
}
- /// CHECK-START: int Main.inlineSharpenHelperInvoke() ssa_builder (after)
+ /// CHECK-START: int Main.inlineSharpenHelperInvoke() builder (after)
/// CHECK-DAG: <<Invoke:i\d+>> InvokeVirtual {{.*\.getFoo.*}}
/// CHECK-DAG: Return [<<Invoke>>]
diff --git a/test/477-checker-bound-type/src/Main.java b/test/477-checker-bound-type/src/Main.java
index 0f65e44..2504ab2 100644
--- a/test/477-checker-bound-type/src/Main.java
+++ b/test/477-checker-bound-type/src/Main.java
@@ -17,7 +17,7 @@
public class Main {
- /// CHECK-START: java.lang.Object Main.boundTypeForIf(java.lang.Object) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.boundTypeForIf(java.lang.Object) builder (after)
/// CHECK: BoundType
public static Object boundTypeForIf(Object a) {
if (a != null) {
@@ -27,7 +27,7 @@
}
}
- /// CHECK-START: java.lang.Object Main.boundTypeForInstanceOf(java.lang.Object) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.boundTypeForInstanceOf(java.lang.Object) builder (after)
/// CHECK: BoundType
public static Object boundTypeForInstanceOf(Object a) {
if (a instanceof Main) {
@@ -37,7 +37,7 @@
}
}
- /// CHECK-START: java.lang.Object Main.noBoundTypeForIf(java.lang.Object) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.noBoundTypeForIf(java.lang.Object) builder (after)
/// CHECK-NOT: BoundType
public static Object noBoundTypeForIf(Object a) {
if (a == null) {
@@ -47,7 +47,7 @@
}
}
- /// CHECK-START: java.lang.Object Main.noBoundTypeForInstanceOf(java.lang.Object) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.noBoundTypeForInstanceOf(java.lang.Object) builder (after)
/// CHECK-NOT: BoundType
public static Object noBoundTypeForInstanceOf(Object a) {
if (a instanceof Main) {
diff --git a/test/492-checker-inline-invoke-interface/src/Main.java b/test/492-checker-inline-invoke-interface/src/Main.java
index 3106ce4..a919690 100644
--- a/test/492-checker-inline-invoke-interface/src/Main.java
+++ b/test/492-checker-inline-invoke-interface/src/Main.java
@@ -31,7 +31,7 @@
int a = ForceStatic.field;
}
- /// CHECK-START: void Main.main(java.lang.String[]) ssa_builder (after)
+ /// CHECK-START: void Main.main(java.lang.String[]) builder (after)
/// CHECK: InvokeStaticOrDirect {{.*Main.<init>.*}}
/// CHECK: InvokeInterface
diff --git a/test/510-checker-try-catch/smali/Builder.smali b/test/510-checker-try-catch/smali/Builder.smali
index 1fde5ed..8ec840d 100644
--- a/test/510-checker-try-catch/smali/Builder.smali
+++ b/test/510-checker-try-catch/smali/Builder.smali
@@ -41,28 +41,35 @@
## CHECK: predecessors "<<BEnterTry2>>"
## CHECK: successors "<<BExitTry2:B\d+>>"
## CHECK: DivZeroCheck
+## CHECK: <<Div:i\d+>> Div
-## CHECK: name "<<BReturn:B\d+>>"
-## CHECK: predecessors "<<BExitTry2>>" "<<BCatch1:B\d+>>" "<<BCatch2:B\d+>>" "<<BCatch3:B\d+>>"
+## CHECK: name "<<BAfterTry2:B\d+>>"
+## CHECK: predecessors "<<BExitTry2>>"
+## CHECK: successors "<<BReturn:B\d+>>"
+## CHECK: Goto
+
+## CHECK: name "<<BReturn>>"
+## CHECK: predecessors "<<BAfterTry2>>" "<<BCatch1:B\d+>>" "<<BCatch2:B\d+>>" "<<BCatch3:B\d+>>"
+## CHECK: Phi [<<Div>>,<<Minus1>>,<<Minus2>>,<<Minus3>>]
## CHECK: Return
## CHECK: name "<<BCatch1>>"
## CHECK: predecessors "<<BEnterTry1>>" "<<BExitTry1>>"
## CHECK: successors "<<BReturn>>"
## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus1>>]
+## CHECK: Goto
## CHECK: name "<<BCatch2>>"
## CHECK: predecessors "<<BEnterTry2>>" "<<BExitTry2>>"
## CHECK: successors "<<BReturn>>"
## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus2>>]
+## CHECK: Goto
## CHECK: name "<<BCatch3>>"
## CHECK: predecessors "<<BEnterTry1>>" "<<BEnterTry2>>" "<<BExitTry1>>" "<<BExitTry2>>"
## CHECK: successors "<<BReturn>>"
## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus3>>]
+## CHECK: Goto
## CHECK: name "<<BEnterTry1>>"
## CHECK: predecessors "B0"
@@ -84,7 +91,7 @@
## CHECK: name "<<BExitTry2>>"
## CHECK: predecessors "<<BTry2>>"
-## CHECK: successors "<<BReturn>>"
+## CHECK: successors "<<BAfterTry2>>"
## CHECK: xhandlers "<<BCatch2>>" "<<BCatch3>>"
## CHECK: TryBoundary kind:exit
@@ -105,6 +112,8 @@
.catch Ljava/lang/OutOfMemoryError; {:try_start_2 .. :try_end_2} :catch_mem
.catchall {:try_start_2 .. :try_end_2} :catch_other
+ nop
+
:return
return p0
@@ -131,7 +140,7 @@
## CHECK: name "<<BIf>>"
## CHECK: predecessors "B0"
-## CHECK: successors "<<BEnterTry2:B\d+>>" "<<BThen:B\d+>>"
+## CHECK: successors "<<BSplit1:B\d+>>" "<<BThen:B\d+>>"
## CHECK: If
## CHECK: name "<<BThen>>"
@@ -145,19 +154,19 @@
## CHECK: Div
## CHECK: name "<<BTry2:B\d+>>"
-## CHECK: predecessors "<<BEnterTry2>>"
+## CHECK: predecessors "<<BEnterTry2:B\d+>>"
## CHECK: successors "<<BExitTry2:B\d+>>"
## CHECK: Div
## CHECK: name "<<BReturn:B\d+>>"
-## CHECK: predecessors "<<BExitTry2>>" "<<BCatch:B\d+>>"
+## CHECK: predecessors "<<BSplit3:B\d+>>" "<<BCatch:B\d+>>"
## CHECK: Return
## CHECK: name "<<BCatch>>"
## CHECK: predecessors "<<BEnterTry1>>" "<<BEnterTry2>>" "<<BExitTry1>>" "<<BExitTry2>>"
## CHECK: successors "<<BReturn>>"
## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus1>>]
+## CHECK: Goto
## CHECK: name "<<BEnterTry1>>"
## CHECK: predecessors "<<BThen>>"
@@ -166,23 +175,38 @@
## CHECK: TryBoundary kind:entry
## CHECK: name "<<BEnterTry2>>"
-## CHECK: predecessors "<<BIf>>" "<<BExitTry1>>"
+## CHECK: predecessors "<<BSplit1>>" "<<BSplit2:B\d+>>"
## CHECK: successors "<<BTry2>>"
## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:entry
## CHECK: name "<<BExitTry1>>"
## CHECK: predecessors "<<BTry1>>"
-## CHECK: successors "<<BEnterTry2>>"
+## CHECK: successors "<<BSplit2>>"
## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:exit
## CHECK: name "<<BExitTry2>>"
## CHECK: predecessors "<<BTry2>>"
-## CHECK: successors "<<BReturn>>"
+## CHECK: successors "<<BSplit3>>"
## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:exit
+## CHECK: name "<<BSplit1>>"
+## CHECK: predecessors "<<BIf>>"
+## CHECK: successors "<<BEnterTry2>>"
+## CHECK: Goto
+
+## CHECK: name "<<BSplit2>>"
+## CHECK: predecessors "<<BExitTry1>>"
+## CHECK: successors "<<BEnterTry2>>"
+## CHECK: Goto
+
+## CHECK: name "<<BSplit3>>"
+## CHECK: predecessors "<<BExitTry2>>"
+## CHECK: successors "<<BReturn>>"
+## CHECK: Goto
+
.method public static testMultipleEntries(IIII)I
.registers 4
@@ -220,23 +244,24 @@
## CHECK: name "<<BTry:B\d+>>"
## CHECK: predecessors "<<BEnterTry>>"
## CHECK: successors "<<BExitTry1:B\d+>>" "<<BExitTry2:B\d+>>"
-## CHECK: Div
+## CHECK: <<Div:i\d+>> Div
## CHECK: If
## CHECK: name "<<BReturn:B\d+>>"
-## CHECK: predecessors "<<BExitTry2>>" "<<BThen:B\d+>>" "<<BCatch:B\d+>>"
+## CHECK: predecessors "<<BSplit:B\d+>>" "<<BThen:B\d+>>" "<<BCatch:B\d+>>"
+## CHECK: Phi [<<Div>>,<<Minus1>>,<<Minus2>>]
## CHECK: Return
## CHECK: name "<<BThen>>"
## CHECK: predecessors "<<BExitTry1>>"
## CHECK: successors "<<BReturn>>"
-## CHECK: StoreLocal [v0,<<Minus1>>]
+## CHECK: Goto
## CHECK: name "<<BCatch>>"
## CHECK: predecessors "<<BEnterTry>>" "<<BExitTry1>>" "<<BExitTry2>>"
## CHECK: successors "<<BReturn>>"
## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus2>>]
+## CHECK: Goto
## CHECK: name "<<BEnterTry>>"
## CHECK: predecessors "B0"
@@ -252,10 +277,15 @@
## CHECK: name "<<BExitTry2>>"
## CHECK: predecessors "<<BTry>>"
-## CHECK: successors "<<BReturn>>"
+## CHECK: successors "<<BSplit>>"
## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:exit
+## CHECK: name "<<BSplit>>"
+## CHECK: predecessors "<<BExitTry2>>"
+## CHECK: successors "<<BReturn>>"
+## CHECK: Goto
+
.method public static testMultipleExits(II)I
.registers 2
@@ -295,23 +325,25 @@
## CHECK: name "<<BTry2:B\d+>>"
## CHECK: predecessors "<<BEnter2:B\d+>>"
## CHECK: successors "<<BExit2:B\d+>>"
-## CHECK: Div
+## CHECK: <<Div:i\d+>> Div
+## CHECK: Goto
## CHECK: name "<<BReturn:B\d+>>"
-## CHECK: predecessors "<<BExit2>>" "<<BCatch1:B\d+>>" "<<BCatch2:B\d+>>"
+## CHECK: predecessors "<<BSplit:B\d+>>" "<<BCatch1:B\d+>>" "<<BCatch2:B\d+>>"
+## CHECK: Phi [<<Div>>,<<Minus1>>,<<Minus2>>]
## CHECK: Return
## CHECK: name "<<BCatch1>>"
## CHECK: predecessors "<<BEnter1>>" "<<BExit1>>"
## CHECK: successors "<<BReturn>>"
## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus1>>]
+## CHECK: Goto
## CHECK: name "<<BCatch2>>"
## CHECK: predecessors "<<BEnter2>>" "<<BExit2>>"
## CHECK: successors "<<BReturn>>"
## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus2>>]
+## CHECK: Goto
## CHECK: name "<<BEnter1>>"
## CHECK: predecessors "B0"
@@ -333,10 +365,15 @@
## CHECK: name "<<BExit2>>"
## CHECK: predecessors "<<BTry2>>"
-## CHECK: successors "<<BReturn>>"
+## CHECK: successors "<<BSplit>>"
## CHECK: xhandlers "<<BCatch2>>"
## CHECK: TryBoundary kind:exit
+## CHECK: name "<<BSplit>>"
+## CHECK: predecessors "<<BExit2>>"
+## CHECK: successors "<<BReturn>>"
+## CHECK: Goto
+
.method public static testSharedBoundary(III)I
.registers 3
@@ -378,28 +415,31 @@
## CHECK: name "<<BTry1:B\d+>>"
## CHECK: predecessors "<<BEnter1:B\d+>>"
## CHECK: successors "<<BExit1:B\d+>>"
-## CHECK: Div
+## CHECK: <<Div:i\d+>> Div
+## CHECK: Goto
## CHECK: name "<<BTry2:B\d+>>"
## CHECK: predecessors "<<BEnter2>>"
## CHECK: successors "<<BExit2:B\d+>>"
## CHECK: Div
+## CHECK: Goto
## CHECK: name "<<BReturn:B\d+>>"
-## CHECK: predecessors "<<BExit1>>" "<<BCatch1:B\d+>>" "<<BCatch2:B\d+>>"
+## CHECK: predecessors "<<BSplit:B\d+>>" "<<BCatch1:B\d+>>" "<<BCatch2:B\d+>>"
+## CHECK: Phi [<<Div>>,<<Minus1>>,<<Minus2>>]
## CHECK: Return
## CHECK: name "<<BCatch1>>"
## CHECK: predecessors "<<BEnter1>>" "<<BExit1>>"
## CHECK: successors "<<BReturn>>"
## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus1>>]
+## CHECK: Goto
## CHECK: name "<<BCatch2>>"
## CHECK: predecessors "<<BEnter2>>" "<<BExit2>>"
## CHECK: successors "<<BReturn>>"
## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus2>>]
+## CHECK: Goto
## CHECK: name "<<BEnter1>>"
## CHECK: predecessors "<<BExit2>>"
@@ -415,7 +455,7 @@
## CHECK: name "<<BExit1>>"
## CHECK: predecessors "<<BTry1>>"
-## CHECK: successors "<<BReturn>>"
+## CHECK: successors "<<BSplit>>"
## CHECK: xhandlers "<<BCatch1>>"
## CHECK: TryBoundary kind:exit
@@ -425,6 +465,11 @@
## CHECK: xhandlers "<<BCatch2>>"
## CHECK: TryBoundary kind:exit
+## CHECK: name "<<BSplit>>"
+## CHECK: predecessors "<<BExit1>>"
+## CHECK: successors "<<BReturn>>"
+## CHECK: Goto
+
.method public static testSharedBoundary_Reverse(III)I
.registers 3
@@ -472,26 +517,30 @@
## CHECK: predecessors "<<BEnter2:B\d+>>"
## CHECK: successors "<<BExit2:B\d+>>"
## CHECK: Div
+## CHECK: Goto
## CHECK: name "<<BTry3:B\d+>>"
## CHECK: predecessors "<<BEnter3:B\d+>>"
## CHECK: successors "<<BExit3:B\d+>>"
-## CHECK: Div
+## CHECK: <<Div:i\d+>> Div
+## CHECK: Goto
## CHECK: name "<<BReturn:B\d+>>"
-## CHECK: predecessors "<<BExit3>>" "<<BCatchArith:B\d+>>" "<<BCatchAll:B\d+>>"
+## CHECK: predecessors "<<BSplit:B\d+>>" "<<BCatchArith:B\d+>>" "<<BCatchAll:B\d+>>"
+## CHECK: Phi [<<Div>>,<<Minus1>>,<<Minus2>>]
+## CHECK: Return
## CHECK: name "<<BCatchArith>>"
## CHECK: predecessors "<<BEnter2>>" "<<BExit2>>"
## CHECK: successors "<<BReturn>>"
## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus1>>]
+## CHECK: Goto
## CHECK: name "<<BCatchAll>>"
## CHECK: predecessors "<<BEnter1>>" "<<BEnter2>>" "<<BEnter3>>" "<<BExit1>>" "<<BExit2>>" "<<BExit3>>"
## CHECK: successors "<<BReturn>>"
## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus2>>]
+## CHECK: Goto
## CHECK: name "<<BEnter1>>"
## CHECK: predecessors "B0"
@@ -525,10 +574,15 @@
## CHECK: name "<<BExit3>>"
## CHECK: predecessors "<<BTry3>>"
-## CHECK: successors "<<BReturn>>"
+## CHECK: successors "<<BSplit>>"
## CHECK: xhandlers "<<BCatchAll>>"
## CHECK: TryBoundary kind:exit
+## CHECK: name "<<BSplit>>"
+## CHECK: predecessors "<<BExit3>>"
+## CHECK: successors "<<BReturn>>"
+## CHECK: Goto
+
.method public static testNestedTry(IIII)I
.registers 4
@@ -567,14 +621,18 @@
## CHECK: predecessors "<<BEnterTry1:B\d+>>"
## CHECK: successors "<<BExitTry1:B\d+>>"
## CHECK: Div
+## CHECK: Goto
## CHECK: name "<<BTry2:B\d+>>"
## CHECK: predecessors "<<BEnterTry2:B\d+>>"
## CHECK: successors "<<BExitTry2:B\d+>>"
-## CHECK: Div
+## CHECK: <<Div:i\d+>> Div
+## CHECK: Goto
## CHECK: name "<<BReturn:B\d+>>"
-## CHECK: predecessors "<<BExitTry2>>" "<<BCatch:B\d+>>"
+## CHECK: predecessors "<<BSplit:B\d+>>" "<<BCatch:B\d+>>"
+## CHECK: Phi [<<Div>>,<<Minus1>>]
+## CHECK: Return
## CHECK: name "<<BOutside:B\d+>>"
## CHECK: predecessors "<<BExitTry1>>"
@@ -585,7 +643,7 @@
## CHECK: predecessors "<<BEnterTry1>>" "<<BEnterTry2>>" "<<BExitTry1>>" "<<BExitTry2>>"
## CHECK: successors "<<BReturn>>"
## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus1>>]
+## CHECK: Goto
## CHECK: name "<<BEnterTry1>>"
## CHECK: predecessors "B0"
@@ -607,10 +665,15 @@
## CHECK: name "<<BExitTry2>>"
## CHECK: predecessors "<<BTry2>>"
-## CHECK: successors "<<BReturn>>"
+## CHECK: successors "<<BSplit>>"
## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:exit
+## CHECK: name "<<BSplit>>"
+## CHECK: predecessors "<<BExitTry2>>"
+## CHECK: successors "<<BReturn>>"
+## CHECK: Goto
+
.method public static testIncontinuousTry(IIII)I
.registers 4
@@ -642,12 +705,12 @@
## CHECK: name "<<BPSwitch0>>"
## CHECK: predecessors "B0"
-## CHECK: successors "<<BEnterTry2:B\d+>>" "<<BPSwitch1:B\d+>>"
+## CHECK: successors "<<BSplit1:B\d+>>" "<<BPSwitch1:B\d+>>"
## CHECK: If
## CHECK: name "<<BPSwitch1>>"
## CHECK: predecessors "<<BPSwitch0>>"
-## CHECK: successors "<<BOutside:B\d+>>" "<<BEnterTry1:B\d+>>"
+## CHECK: successors "<<BSplit2:B\d+>>" "<<BEnterTry1:B\d+>>"
## CHECK: If
## CHECK: name "<<BTry1:B\d+>>"
@@ -656,44 +719,68 @@
## CHECK: Div
## CHECK: name "<<BTry2:B\d+>>"
-## CHECK: predecessors "<<BEnterTry2>>"
+## CHECK: predecessors "<<BEnterTry2:B\d+>>"
## CHECK: successors "<<BExitTry2:B\d+>>"
## CHECK: Div
-## CHECK: name "<<BOutside>>"
-## CHECK: predecessors "<<BPSwitch1>>" "<<BExitTry2>>"
-## CHECK: successors "<<BCatchReturn:B\d+>>"
+## CHECK: name "<<BOutside:B\d+>>"
+## CHECK: predecessors "<<BSplit2>>" "<<BSplit4:B\d+>>"
+## CHECK: successors "<<BReturn:B\d+>>"
## CHECK: Div
-## CHECK: name "<<BCatchReturn>>"
-## CHECK: predecessors "<<BOutside>>" "<<BEnterTry1>>" "<<BEnterTry2>>" "<<BExitTry1>>" "<<BExitTry2>>"
+## CHECK: name "<<BCatch:B\d+>>"
+## CHECK: predecessors "<<BEnterTry1>>" "<<BEnterTry2>>" "<<BExitTry1>>" "<<BExitTry2>>"
## CHECK: flags "catch_block"
-## CHECK: Return
+## CHECK: Goto
## CHECK: name "<<BEnterTry1>>"
## CHECK: predecessors "<<BPSwitch1>>"
## CHECK: successors "<<BTry1>>"
-## CHECK: xhandlers "<<BCatchReturn>>"
+## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:entry
## CHECK: name "<<BEnterTry2>>"
-## CHECK: predecessors "<<BPSwitch0>>"
+## CHECK: predecessors "<<BSplit1>>" "<<BSplit3:B\d+>>"
## CHECK: successors "<<BTry2>>"
-## CHECK: xhandlers "<<BCatchReturn>>"
+## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:entry
## CHECK: name "<<BExitTry1>>"
## CHECK: predecessors "<<BTry1>>"
-## CHECK: successors "<<BEnterTry2>>"
-## CHECK: xhandlers "<<BCatchReturn>>"
+## CHECK: successors "<<BSplit3>>"
+## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:exit
## CHECK: name "<<BExitTry2>>"
## CHECK: predecessors "<<BTry2>>"
-## CHECK: successors "<<BOutside>>"
-## CHECK: xhandlers "<<BCatchReturn>>"
+## CHECK: successors "<<BSplit4>>"
+## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:exit
+## CHECK: name "<<BReturn>>"
+## CHECK: predecessors "<<BCatch>>" "<<BOutside>>"
+## CHECK: Return
+
+## CHECK: name "<<BSplit1>>"
+## CHECK: predecessors "<<BPSwitch0>>"
+## CHECK: successors "<<BEnterTry2>>"
+## CHECK: Goto
+
+## CHECK: name "<<BSplit2>>"
+## CHECK: predecessors "<<BPSwitch1>>"
+## CHECK: successors "<<BOutside>>"
+## CHECK: Goto
+
+## CHECK: name "<<BSplit3>>"
+## CHECK: predecessors "<<BExitTry1>>"
+## CHECK: successors "<<BEnterTry2>>"
+## CHECK: Goto
+
+## CHECK: name "<<BSplit4>>"
+## CHECK: predecessors "<<BExitTry2>>"
+## CHECK: successors "<<BOutside>>"
+## CHECK: Goto
+
.method public static testSwitchTryEnter(IIII)I
.registers 4
@@ -728,58 +815,78 @@
## CHECK: name "<<BPSwitch0:B\d+>>"
## CHECK: predecessors "<<BEnterTry1>>"
-## CHECK: successors "<<BTry2:B\d+>>" "<<BExitTry1:B\d+>>"
+## CHECK: successors "<<BSplit1:B\d+>>" "<<BExitTry1:B\d+>>"
## CHECK: If
## CHECK: name "<<BPSwitch1:B\d+>>"
## CHECK: predecessors "<<BExitTry1>>"
-## CHECK: successors "<<BOutside:B\d+>>" "<<BEnterTry2:B\d+>>"
+## CHECK: successors "<<BSplit2:B\d+>>" "<<BEnterTry2:B\d+>>"
## CHECK: If
## CHECK: name "<<BTry1:B\d+>>"
## CHECK: predecessors "<<BEnterTry2>>"
-## CHECK: successors "<<BTry2>>"
+## CHECK: successors "<<BTry2:B\d+>>"
## CHECK: Div
## CHECK: name "<<BTry2>>"
-## CHECK: predecessors "<<BPSwitch0>>"
+## CHECK: predecessors "<<BSplit1>>" "<<BTry1>>"
## CHECK: successors "<<BExitTry2:B\d+>>"
## CHECK: Div
-## CHECK: name "<<BOutside>>"
-## CHECK: predecessors "<<BPSwitch1>>" "<<BExitTry2>>"
-## CHECK: successors "<<BCatchReturn:B\d+>>"
+## CHECK: name "<<BOutside:B\d+>>"
+## CHECK: predecessors "<<BSplit2>>" "<<BSplit3:B\d+>>"
+## CHECK: successors "<<BReturn:B\d+>>"
## CHECK: Div
-## CHECK: name "<<BCatchReturn>>"
-## CHECK: predecessors "<<BOutside>>" "<<BEnterTry1>>" "<<BEnterTry2>>" "<<BExitTry1>>" "<<BExitTry2>>"
+## CHECK: name "<<BCatch:B\d+>>"
+## CHECK: predecessors "<<BEnterTry1>>" "<<BEnterTry2>>" "<<BExitTry1>>" "<<BExitTry2>>"
+## CHECK: successors "<<BReturn>>"
## CHECK: flags "catch_block"
-## CHECK: Return
+## CHECK: Goto
## CHECK: name "<<BEnterTry1>>"
## CHECK: predecessors "B0"
## CHECK: successors "<<BPSwitch0>>"
-## CHECK: xhandlers "<<BCatchReturn>>"
+## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:entry
## CHECK: name "<<BEnterTry2>>"
## CHECK: predecessors "<<BPSwitch1>>"
## CHECK: successors "<<BTry1>>"
-## CHECK: xhandlers "<<BCatchReturn>>"
+## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:entry
## CHECK: name "<<BExitTry1>>"
## CHECK: predecessors "<<BPSwitch0>>"
## CHECK: successors "<<BPSwitch1>>"
-## CHECK: xhandlers "<<BCatchReturn>>"
+## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:exit
## CHECK: name "<<BExitTry2>>"
## CHECK: predecessors "<<BTry2>>"
-## CHECK: successors "<<BOutside>>"
-## CHECK: xhandlers "<<BCatchReturn>>"
+## CHECK: successors "<<BSplit3>>"
+## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:exit
+## CHECK: name "<<BReturn>>"
+## CHECK: predecessors "<<BCatch>>" "<<BOutside>>"
+## CHECK: Return
+
+## CHECK: name "<<BSplit1>>"
+## CHECK: predecessors "<<BPSwitch0>>"
+## CHECK: successors "<<BTry2>>"
+## CHECK: Goto
+
+## CHECK: name "<<BSplit2>>"
+## CHECK: predecessors "<<BPSwitch1>>"
+## CHECK: successors "<<BOutside>>"
+## CHECK: Goto
+
+## CHECK: name "<<BSplit3>>"
+## CHECK: predecessors "<<BExitTry2>>"
+## CHECK: successors "<<BOutside>>"
+## CHECK: Goto
+
.method public static testSwitchTryExit(IIII)I
.registers 4
@@ -825,7 +932,7 @@
## CHECK: predecessors "<<BEnterTry>>" "<<BExitTry>>"
## CHECK: successors "<<BExit:B\d+>>"
## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus1>>]
+## CHECK: Return [<<Minus1>>]
## CHECK: name "<<BExit>>"
## CHECK: predecessors "<<BExitTry>>" "<<BCatch>>"
@@ -861,18 +968,21 @@
## CHECK-START: int Builder.testCatchLoop(int, int, int) builder (after)
## CHECK: name "B0"
-## CHECK: successors "<<BCatch:B\d+>>"
+## CHECK: successors "<<BSplit2:B\d+>>"
-## CHECK: name "<<BCatch>>"
-## CHECK: predecessors "B0" "<<BEnterTry:B\d+>>" "<<BExitTry:B\d+>>"
-## CHECK: successors "<<BEnterTry>>"
+## CHECK: name "<<BCatch:B\d+>>"
+## CHECK: predecessors "<<BEnterTry:B\d+>>" "<<BExitTry:B\d+>>"
+## CHECK: successors "<<BSplit1:B\d+>>"
## CHECK: flags "catch_block"
## CHECK: name "<<BReturn:B\d+>>"
## CHECK: predecessors "<<BExitTry>>"
## CHECK: successors "<<BExit:B\d+>>"
+## CHECK: Return
## CHECK: name "<<BExit>>"
+## CHECK: predecessors "<<BReturn>>"
+## CHECK: Exit
## CHECK: name "<<BTry:B\d+>>"
## CHECK: predecessors "<<BEnterTry>>"
@@ -880,7 +990,7 @@
## CHECK: Div
## CHECK: name "<<BEnterTry>>"
-## CHECK: predecessors "<<BCatch>>"
+## CHECK: predecessors "<<BSplit1>>"
## CHECK: successors "<<BTry>>"
## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:entry
@@ -891,6 +1001,16 @@
## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:exit
+## CHECK: name "<<BSplit1>>"
+## CHECK: predecessors "<<BSplit2>>" "<<BCatch>>"
+## CHECK: successors "<<BEnterTry>>"
+## CHECK: Goto
+
+## CHECK: name "<<BSplit2>>"
+## CHECK: predecessors "B0"
+## CHECK: successors "<<BSplit1>>"
+## CHECK: Goto
+
.method public static testCatchLoop(III)I
.registers 4
@@ -918,14 +1038,16 @@
## CHECK: Div
## CHECK: name "<<BCatch:B\d+>>"
-## CHECK: predecessors "<<BExitTry1>>" "<<BEnterTry1>>" "<<BEnterTry2:B\d+>>" "<<BExitTry1>>" "<<BExitTry2:B\d+>>"
-## CHECK: successors "<<BEnterTry2>>"
+## CHECK: predecessors "<<BEnterTry1>>" "<<BEnterTry2:B\d+>>" "<<BExitTry1>>" "<<BExitTry2:B\d+>>"
+## CHECK: successors "<<BSplit1:B\d+>>"
## CHECK: flags "catch_block"
## CHECK: name "<<BReturn:B\d+>>"
## CHECK: predecessors "<<BExitTry2>>"
+## CHECK: successors "<<BExit:B\d+>>"
-## CHECK: name "{{B\d+}}"
+## CHECK: name "<<BExit>>"
+## CHECK: predecessors "<<BReturn>>"
## CHECK: Exit
## CHECK: name "<<BTry2:B\d+>>"
@@ -940,14 +1062,14 @@
## CHECK: TryBoundary kind:entry
## CHECK: name "<<BEnterTry2>>"
-## CHECK: predecessors "<<BCatch>>"
+## CHECK: predecessors "<<BSplit1>>"
## CHECK: successors "<<BTry2>>"
## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:entry
## CHECK: name "<<BExitTry1>>"
## CHECK: predecessors "<<BTry1>>"
-## CHECK: successors "<<BCatch>>"
+## CHECK: successors "<<BSplit2:B\d+>>"
## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:exit
@@ -957,6 +1079,16 @@
## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:exit
+## CHECK: name "<<BSplit1>>"
+## CHECK: predecessors "<<BSplit2>>" "<<BCatch>>"
+## CHECK: successors "<<BEnterTry2>>"
+## CHECK: Goto
+
+## CHECK: name "<<BSplit2>>"
+## CHECK: predecessors "<<BExitTry1>>"
+## CHECK: successors "<<BSplit1>>"
+## CHECK: Goto
+
.method public static testHandlerEdge1(III)I
.registers 4
@@ -977,16 +1109,16 @@
## CHECK-START: int Builder.testHandlerEdge2(int, int, int) builder (after)
## CHECK: name "B0"
-## CHECK: successors "<<BCatch1:B\d+>>"
+## CHECK: successors "<<BSplit4:B\d+>>"
-## CHECK: name "<<BCatch1>>"
-## CHECK: predecessors "B0" "<<BEnterTry2:B\d+>>" "<<BExitTry2:B\d+>>"
-## CHECK: successors "<<BEnterTry1:B\d+>>"
+## CHECK: name "<<BCatch1:B\d+>>"
+## CHECK: predecessors "<<BEnterTry2:B\d+>>" "<<BExitTry2:B\d+>>"
+## CHECK: successors "<<BSplit1:B\d+>>"
## CHECK: flags "catch_block"
## CHECK: name "<<BCatch2:B\d+>>"
-## CHECK: predecessors "<<BExitTry1:B\d+>>" "<<BEnterTry1>>" "<<BExitTry1>>"
-## CHECK: successors "<<BEnterTry2>>"
+## CHECK: predecessors "<<BEnterTry1:B\d+>>" "<<BExitTry1:B\d+>>"
+## CHECK: successors "<<BSplit2:B\d+>>"
## CHECK: flags "catch_block"
## CHECK: name "<<BReturn:B\d+>>"
@@ -995,6 +1127,7 @@
## CHECK: Return
## CHECK: name "<<BExit>>"
+## CHECK: Exit
## CHECK: name "<<BTry1:B\d+>>"
## CHECK: predecessors "<<BEnterTry1>>"
@@ -1007,20 +1140,20 @@
## CHECK: Div
## CHECK: name "<<BEnterTry1>>"
-## CHECK: predecessors "<<BCatch1>>"
+## CHECK: predecessors "<<BSplit1>>"
## CHECK: successors "<<BTry1>>"
## CHECK: xhandlers "<<BCatch2>>"
## CHECK: TryBoundary kind:entry
## CHECK: name "<<BEnterTry2>>"
-## CHECK: predecessors "<<BCatch2>>"
+## CHECK: predecessors "<<BSplit2>>"
## CHECK: successors "<<BTry2>>"
## CHECK: xhandlers "<<BCatch1>>"
## CHECK: TryBoundary kind:entry
## CHECK: name "<<BExitTry1>>"
## CHECK: predecessors "<<BTry1>>"
-## CHECK: successors "<<BCatch2>>"
+## CHECK: successors "<<BSplit3:B\d+>>"
## CHECK: xhandlers "<<BCatch2>>"
## CHECK: TryBoundary kind:exit
@@ -1030,6 +1163,26 @@
## CHECK: xhandlers "<<BCatch1>>"
## CHECK: TryBoundary kind:exit
+## CHECK: name "<<BSplit1>>"
+## CHECK: predecessors "<<BSplit4>>" "<<BCatch1>>"
+## CHECK: successors "<<BEnterTry1>>"
+## CHECK: Goto
+
+## CHECK: name "<<BSplit2>>"
+## CHECK: predecessors "<<BCatch2>>" "<<BSplit3>>"
+## CHECK: successors "<<BEnterTry2>>"
+## CHECK: Goto
+
+## CHECK: name "<<BSplit3>>"
+## CHECK: predecessors "<<BExitTry1>>"
+## CHECK: successors "<<BSplit2>>"
+## CHECK: Goto
+
+## CHECK: name "<<BSplit4>>"
+## CHECK: predecessors "B0"
+## CHECK: successors "<<BSplit1>>"
+## CHECK: Goto
+
.method public static testHandlerEdge2(III)I
.registers 4
@@ -1053,10 +1206,10 @@
## CHECK-START: int Builder.testTryInLoop(int, int) builder (after)
## CHECK: name "B0"
-## CHECK: successors "<<BEnterTry:B\d+>>"
+## CHECK: successors "<<BSplit1:B\d+>>"
## CHECK: name "<<BTry:B\d+>>"
-## CHECK: predecessors "<<BEnterTry>>"
+## CHECK: predecessors "<<BEnterTry:B\d+>>"
## CHECK: successors "<<BExitTry:B\d+>>"
## CHECK: Div
@@ -1065,22 +1218,28 @@
## CHECK: successors "<<BEnterTry>>"
## CHECK: flags "catch_block"
-## CHECK: name "<<BExit:B\d+>>"
-## CHECK-NOT: predecessors "{{B\d+}}"
-## CHECK: end_block
-
## CHECK: name "<<BEnterTry>>"
-## CHECK: predecessors "B0"
+## CHECK: predecessors "<<BSplit1>>"
## CHECK: successors "<<BTry>>"
## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:entry
## CHECK: name "<<BExitTry>>"
## CHECK: predecessors "<<BTry>>"
-## CHECK: successors "<<BEnterTry>>"
+## CHECK: successors "<<BSplit2:B\d+>>"
## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:exit
+## CHECK: name "<<BSplit1>>"
+## CHECK: predecessors "B0"
+## CHECK: successors "<<BEnterTry>>"
+## CHECK: Goto
+
+## CHECK: name "<<BSplit2>>"
+## CHECK: predecessors "<<BExitTry>>"
+## CHECK: successors "<<BEnterTry>>"
+## CHECK: Goto
+
.method public static testTryInLoop(II)I
.registers 3
@@ -1098,9 +1257,10 @@
# INVOKE it follows, even if there is a try boundary between them.
## CHECK-START: int Builder.testMoveResult_Invoke(int, int, int) builder (after)
-
-## CHECK: <<Res:i\d+>> InvokeStaticOrDirect
-## CHECK-NEXT: StoreLocal [v0,<<Res>>]
+## CHECK-DAG: <<M1:i\d+>> IntConstant -1
+## CHECK-DAG: <<Res:i\d+>> InvokeStaticOrDirect
+## CHECK-DAG: <<Phi:i\d+>> Phi [<<Res>>,<<M1>>]
+## CHECK-DAG: Return [<<Phi>>]
.method public static testMoveResult_Invoke(III)I
.registers 3
@@ -1124,16 +1284,16 @@
# FILLED_NEW_ARRAY it follows, even if there is a try boundary between them.
## CHECK-START: int[] Builder.testMoveResult_FilledNewArray(int, int, int) builder (after)
-
-## CHECK: <<Res:l\d+>> NewArray
-## CHECK-NEXT: Temporary
-## CHECK-NEXT: <<Local1:i\d+>> LoadLocal [v0]
-## CHECK-NEXT: ArraySet [<<Res>>,{{i\d+}},<<Local1>>]
-## CHECK-NEXT: <<Local2:i\d+>> LoadLocal [v1]
-## CHECK-NEXT: ArraySet [<<Res>>,{{i\d+}},<<Local2>>]
-## CHECK-NEXT: <<Local3:i\d+>> LoadLocal [v2]
-## CHECK-NEXT: ArraySet [<<Res>>,{{i\d+}},<<Local3>>]
-## CHECK-NEXT: StoreLocal [v0,<<Res>>]
+## CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+## CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+## CHECK-DAG: <<Arg3:i\d+>> ParameterValue
+## CHECK-DAG: <<Null:l\d+>> NullConstant
+## CHECK-DAG: <<Res:l\d+>> NewArray
+## CHECK-DAG: ArraySet [<<Res>>,{{i\d+}},<<Arg1>>]
+## CHECK-DAG: ArraySet [<<Res>>,{{i\d+}},<<Arg2>>]
+## CHECK-DAG: ArraySet [<<Res>>,{{i\d+}},<<Arg3>>]
+## CHECK-DAG: <<Phi:l\d+>> Phi [<<Res>>,<<Null>>]
+## CHECK-DAG: Return [<<Phi>>]
.method public static testMoveResult_FilledNewArray(III)[I
.registers 3
diff --git a/test/510-checker-try-catch/smali/SsaBuilder.smali b/test/510-checker-try-catch/smali/SsaBuilder.smali
index a6a5bfe..1fd5fb2 100644
--- a/test/510-checker-try-catch/smali/SsaBuilder.smali
+++ b/test/510-checker-try-catch/smali/SsaBuilder.smali
@@ -19,7 +19,7 @@
# Tests that catch blocks with both normal and exceptional predecessors are
# split in two.
-## CHECK-START: int SsaBuilder.testSimplifyCatchBlock(int, int, int) ssa_builder (after)
+## CHECK-START: int SsaBuilder.testSimplifyCatchBlock(int, int, int) builder (after)
## CHECK: name "B1"
## CHECK-NEXT: from_bci
@@ -62,7 +62,7 @@
# Should be rejected because :catch_all is a loop header.
-## CHECK-START: int SsaBuilder.testCatchLoopHeader(int, int, int) ssa_builder (after, bad_state)
+## CHECK-START: int SsaBuilder.testCatchLoopHeader(int, int, int) builder (after, bad_state)
.method public static testCatchLoopHeader(III)I
.registers 4
@@ -84,7 +84,7 @@
# Tests creation of catch Phis.
-## CHECK-START: int SsaBuilder.testPhiCreation(int, int, int) ssa_builder (after)
+## CHECK-START: int SsaBuilder.testPhiCreation(int, int, int) builder (after)
## CHECK-DAG: <<P0:i\d+>> ParameterValue
## CHECK-DAG: <<P1:i\d+>> ParameterValue
## CHECK-DAG: <<P2:i\d+>> ParameterValue
@@ -127,7 +127,7 @@
# Tests that phi elimination does not remove catch phis where the value does
# not dominate the phi.
-## CHECK-START: int SsaBuilder.testPhiElimination_Domination(int, int) ssa_builder (after)
+## CHECK-START: int SsaBuilder.testPhiElimination_Domination(int, int) builder (after)
## CHECK-DAG: <<P0:i\d+>> ParameterValue
## CHECK-DAG: <<P1:i\d+>> ParameterValue
## CHECK-DAG: <<Cst5:i\d+>> IntConstant 5
@@ -168,7 +168,7 @@
# Tests that phi elimination loops until no more phis can be removed.
-## CHECK-START: int SsaBuilder.testPhiElimination_Dependencies(int, int, int) ssa_builder (after)
+## CHECK-START: int SsaBuilder.testPhiElimination_Dependencies(int, int, int) builder (after)
## CHECK-NOT: Phi
.method public static testPhiElimination_Dependencies(III)I
@@ -200,10 +200,7 @@
# Tests that dead catch blocks are removed.
-## CHECK-START: int SsaBuilder.testDeadCatchBlock(int, int, int) ssa_builder (before)
-## CHECK: Mul
-
-## CHECK-START: int SsaBuilder.testDeadCatchBlock(int, int, int) ssa_builder (after)
+## CHECK-START: int SsaBuilder.testDeadCatchBlock(int, int, int) builder (after)
## CHECK-DAG: <<P0:i\d+>> ParameterValue
## CHECK-DAG: <<P1:i\d+>> ParameterValue
## CHECK-DAG: <<P2:i\d+>> ParameterValue
@@ -211,7 +208,7 @@
## CHECK-DAG: <<Add2:i\d+>> Add [<<Add1>>,<<P2>>]
## CHECK-DAG: Return [<<Add2>>]
-## CHECK-START: int SsaBuilder.testDeadCatchBlock(int, int, int) ssa_builder (after)
+## CHECK-START: int SsaBuilder.testDeadCatchBlock(int, int, int) builder (after)
## CHECK-NOT: flags "catch_block"
## CHECK-NOT: Mul
diff --git a/test/517-checker-builder-fallthrough/smali/TestCase.smali b/test/517-checker-builder-fallthrough/smali/TestCase.smali
index bc9502b..946f169 100644
--- a/test/517-checker-builder-fallthrough/smali/TestCase.smali
+++ b/test/517-checker-builder-fallthrough/smali/TestCase.smali
@@ -25,8 +25,8 @@
## CHECK: name "B1"
## CHECK: successors "B5" "B2"
-## CHECK: StoreLocal [v0,<<Const0>>]
-## CHECK: If
+## CHECK: <<Cond:z\d+>> Equal [<<Const0>>,<<Const0>>]
+## CHECK: If [<<Cond>>]
## CHECK: name "B2"
## CHECK: successors "B4"
diff --git a/test/523-checker-can-throw-regression/smali/Test.smali b/test/523-checker-can-throw-regression/smali/Test.smali
index 87192ea..4b737a9 100644
--- a/test/523-checker-can-throw-regression/smali/Test.smali
+++ b/test/523-checker-can-throw-regression/smali/Test.smali
@@ -46,8 +46,10 @@
div-int/2addr p0, p1
:else
div-int/2addr p0, p2
- return p0
:try_end_2
- .catchall {:try_start_2 .. :try_end_2} :catchall
+ .catchall {:try_start_2 .. :try_end_2} :catchall2
+
+ :catchall2
+ return p0
.end method
diff --git a/test/529-checker-unresolved/build b/test/529-checker-unresolved/build
index 8c3c4f8..d85035b 100644
--- a/test/529-checker-unresolved/build
+++ b/test/529-checker-unresolved/build
@@ -29,14 +29,12 @@
mv classes/UnresolvedSuperClass.class classes-ex
if [ ${USE_JACK} = "true" ]; then
- # Create .jack files from classes generated with javac.
- ${JILL} classes --output classes.jack
- ${JILL} classes-ex --output classes-ex.jack
+ jar cf classes.jill.jar -C classes .
+ jar cf classes-ex.jill.jar -C classes-ex .
- # Create DEX files from .jack files.
- ${JACK} --import classes.jack --output-dex .
+ ${JACK} --import classes.jill.jar --output-dex .
zip $TEST_NAME.jar classes.dex
- ${JACK} --import classes-ex.jack --output-dex .
+ ${JACK} --import classes-ex.jill.jar --output-dex .
zip ${TEST_NAME}-ex.jar classes.dex
else
if [ ${NEED_DEX} = "true" ]; then
diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java
index d647683..4d6ea06 100644
--- a/test/530-checker-lse/src/Main.java
+++ b/test/530-checker-lse/src/Main.java
@@ -664,28 +664,19 @@
System.out.println("testFinalizableByForcingGc() failed to force gc.");
}
- /// CHECK-START: int Main.testHSelect(boolean) load_store_elimination (before)
+ /// CHECK-START: int Main.$noinline$testHSelect(boolean) load_store_elimination (before)
/// CHECK: InstanceFieldSet
/// CHECK: Select
- /// CHECK-START: int Main.testHSelect(boolean) load_store_elimination (after)
+ /// CHECK-START: int Main.$noinline$testHSelect(boolean) load_store_elimination (after)
/// CHECK: InstanceFieldSet
/// CHECK: Select
// Test that HSelect creates alias.
- public static int testHSelect(boolean b) {
- // Disable inlining.
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
-
+ public static int $noinline$testHSelect(boolean b) {
+ if (sFlag) {
+ throw new Error();
+ }
TestClass obj = new TestClass();
TestClass obj2 = null;
obj.i = 0xdead;
@@ -754,6 +745,8 @@
assertIntEquals(test23(false), 5);
assertFloatEquals(test24(), 8.0f);
testFinalizableByForcingGc();
- assertIntEquals(testHSelect(true), 0xdead);
+ assertIntEquals($noinline$testHSelect(true), 0xdead);
}
+
+ static boolean sFlag;
}
diff --git a/test/537-checker-debuggable/smali/TestCase.smali b/test/537-checker-debuggable/smali/TestCase.smali
index 8e6c7ef..5714d3a 100644
--- a/test/537-checker-debuggable/smali/TestCase.smali
+++ b/test/537-checker-debuggable/smali/TestCase.smali
@@ -20,10 +20,10 @@
# be eliminated in normal mode but kept live in debuggable mode. Test that
# Checker runs the correct test for each compilation mode.
-## CHECK-START: int TestCase.deadPhi(int, int, int) ssa_builder (after)
+## CHECK-START: int TestCase.deadPhi(int, int, int) builder (after)
## CHECK-NOT: Phi
-## CHECK-START-DEBUGGABLE: int TestCase.deadPhi(int, int, int) ssa_builder (after)
+## CHECK-START-DEBUGGABLE: int TestCase.deadPhi(int, int, int) builder (after)
## CHECK: Phi
.method public static deadPhi(III)I
diff --git a/test/540-checker-rtp-bug/src/Main.java b/test/540-checker-rtp-bug/src/Main.java
index 9a9f0b6..17b11db 100644
--- a/test/540-checker-rtp-bug/src/Main.java
+++ b/test/540-checker-rtp-bug/src/Main.java
@@ -21,7 +21,7 @@
}
public class Main {
- /// CHECK-START: Final Main.testKeepCheckCast(java.lang.Object, boolean) ssa_builder (after)
+ /// CHECK-START: Final Main.testKeepCheckCast(java.lang.Object, boolean) builder (after)
/// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object
/// CHECK: <<Class:l\d+>> LoadClass
/// CHECK: CheckCast [<<Phi>>,<<Class>>]
@@ -43,7 +43,7 @@
return (Final) x;
}
- /// CHECK-START: void Main.testKeepInstanceOf(java.lang.Object, boolean) ssa_builder (after)
+ /// CHECK-START: void Main.testKeepInstanceOf(java.lang.Object, boolean) builder (after)
/// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object
/// CHECK: <<Class:l\d+>> LoadClass
/// CHECK: InstanceOf [<<Phi>>,<<Class>>]
@@ -65,7 +65,7 @@
}
}
- /// CHECK-START: java.lang.String Main.testNoInline(java.lang.Object, boolean) ssa_builder (after)
+ /// CHECK-START: java.lang.String Main.testNoInline(java.lang.Object, boolean) builder (after)
/// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object
/// CHECK: <<NC:l\d+>> NullCheck [<<Phi>>]
/// CHECK: <<Ret:l\d+>> InvokeVirtual [<<NC>>] method_name:java.lang.Object.toString
diff --git a/test/549-checker-types-merge/src/Main.java b/test/549-checker-types-merge/src/Main.java
index 917073b..51af3cf 100644
--- a/test/549-checker-types-merge/src/Main.java
+++ b/test/549-checker-types-merge/src/Main.java
@@ -38,14 +38,14 @@
public class Main {
- /// CHECK-START: java.lang.Object Main.testMergeNullContant(boolean) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeNullContant(boolean) builder (after)
/// CHECK: <<Phi:l\d+>> Phi klass:Main
/// CHECK: Return [<<Phi>>]
private Object testMergeNullContant(boolean cond) {
return cond ? null : new Main();
}
- /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassExtendsA, ClassExtendsB) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassExtendsA, ClassExtendsB) builder (after)
/// CHECK: <<Phi:l\d+>> Phi klass:ClassSuper
/// CHECK: Return [<<Phi>>]
private Object testMergeClasses(boolean cond, ClassExtendsA a, ClassExtendsB b) {
@@ -53,7 +53,7 @@
return cond ? a : b;
}
- /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassExtendsA, ClassSuper) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassExtendsA, ClassSuper) builder (after)
/// CHECK: <<Phi:l\d+>> Phi klass:ClassSuper
/// CHECK: Return [<<Phi>>]
private Object testMergeClasses(boolean cond, ClassExtendsA a, ClassSuper b) {
@@ -61,7 +61,7 @@
return cond ? a : b;
}
- /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassSuper, ClassSuper) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassSuper, ClassSuper) builder (after)
/// CHECK: <<Phi:l\d+>> Phi klass:ClassSuper
/// CHECK: Return [<<Phi>>]
private Object testMergeClasses(boolean cond, ClassSuper a, ClassSuper b) {
@@ -69,7 +69,7 @@
return cond ? a : b;
}
- /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassOtherSuper, ClassSuper) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeClasses(boolean, ClassOtherSuper, ClassSuper) builder (after)
/// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object
/// CHECK: Return [<<Phi>>]
private Object testMergeClasses(boolean cond, ClassOtherSuper a, ClassSuper b) {
@@ -77,7 +77,7 @@
return cond ? a : b;
}
- /// CHECK-START: java.lang.Object Main.testMergeClassWithInterface(boolean, ClassImplementsInterfaceA, InterfaceSuper) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeClassWithInterface(boolean, ClassImplementsInterfaceA, InterfaceSuper) builder (after)
/// CHECK: <<Phi:l\d+>> Phi klass:InterfaceSuper
/// CHECK: Return [<<Phi>>]
private Object testMergeClassWithInterface(boolean cond, ClassImplementsInterfaceA a, InterfaceSuper b) {
@@ -85,7 +85,7 @@
return cond ? a : b;
}
- /// CHECK-START: java.lang.Object Main.testMergeClassWithInterface(boolean, ClassSuper, InterfaceSuper) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeClassWithInterface(boolean, ClassSuper, InterfaceSuper) builder (after)
/// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object
/// CHECK: Return [<<Phi>>]
private Object testMergeClassWithInterface(boolean cond, ClassSuper a, InterfaceSuper b) {
@@ -93,7 +93,7 @@
return cond ? a : b;
}
- /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceExtendsA, InterfaceSuper) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceExtendsA, InterfaceSuper) builder (after)
/// CHECK: <<Phi:l\d+>> Phi klass:InterfaceSuper
/// CHECK: Return [<<Phi>>]
private Object testMergeInterfaces(boolean cond, InterfaceExtendsA a, InterfaceSuper b) {
@@ -101,7 +101,7 @@
return cond ? a : b;
}
- /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceSuper, InterfaceSuper) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceSuper, InterfaceSuper) builder (after)
/// CHECK: <<Phi:l\d+>> Phi klass:InterfaceSuper
/// CHECK: Return [<<Phi>>]
private Object testMergeInterfaces(boolean cond, InterfaceSuper a, InterfaceSuper b) {
@@ -109,7 +109,7 @@
return cond ? a : b;
}
- /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceExtendsA, InterfaceExtendsB) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceExtendsA, InterfaceExtendsB) builder (after)
/// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object
/// CHECK: Return [<<Phi>>]
private Object testMergeInterfaces(boolean cond, InterfaceExtendsA a, InterfaceExtendsB b) {
@@ -117,7 +117,7 @@
return cond ? a : b;
}
- /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceSuper, InterfaceOtherSuper) ssa_builder (after)
+ /// CHECK-START: java.lang.Object Main.testMergeInterfaces(boolean, InterfaceSuper, InterfaceOtherSuper) builder (after)
/// CHECK: <<Phi:l\d+>> Phi klass:java.lang.Object
/// CHECK: Return [<<Phi>>]
private Object testMergeInterfaces(boolean cond, InterfaceSuper a, InterfaceOtherSuper b) {
diff --git a/test/550-checker-regression-wide-store/smali/TestCase.smali b/test/550-checker-regression-wide-store/smali/TestCase.smali
index 7974d56..9133c82 100644
--- a/test/550-checker-regression-wide-store/smali/TestCase.smali
+++ b/test/550-checker-regression-wide-store/smali/TestCase.smali
@@ -25,7 +25,7 @@
# Test storing into the high vreg of a wide pair. This scenario has runtime
# behaviour implications so we run it from Main.main.
-## CHECK-START: int TestCase.invalidateLow(long) ssa_builder (after)
+## CHECK-START: int TestCase.invalidateLow(long) builder (after)
## CHECK-DAG: <<Cst0:i\d+>> IntConstant 0
## CHECK-DAG: <<Arg:j\d+>> ParameterValue
## CHECK-DAG: <<Cast:i\d+>> TypeConversion [<<Arg>>]
@@ -53,7 +53,7 @@
# Test that storing a wide invalidates the value in the high vreg. This
# cannot be detected from runtime so we only test the environment with Checker.
-## CHECK-START: void TestCase.invalidateHigh1(long) ssa_builder (after)
+## CHECK-START: void TestCase.invalidateHigh1(long) builder (after)
## CHECK-DAG: <<Arg:j\d+>> ParameterValue
## CHECK-DAG: InvokeStaticOrDirect method_name:java.lang.System.nanoTime env:[[<<Arg>>,_,<<Arg>>,_]]
@@ -67,7 +67,7 @@
.end method
-## CHECK-START: void TestCase.invalidateHigh2(long) ssa_builder (after)
+## CHECK-START: void TestCase.invalidateHigh2(long) builder (after)
## CHECK-DAG: <<Arg:j\d+>> ParameterValue
## CHECK-DAG: InvokeStaticOrDirect method_name:java.lang.System.nanoTime env:[[<<Arg>>,_,_,<<Arg>>,_]]
diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java
index decdd1f..8d73d69 100644
--- a/test/551-checker-shifter-operand/src/Main.java
+++ b/test/551-checker-shifter-operand/src/Main.java
@@ -241,13 +241,14 @@
/// CHECK-START-ARM64: void Main.$opt$validateExtendByteInt1(int, byte) instruction_simplifier_arm64 (after)
/// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
+ /// CHECK-NOT: Arm64DataProcWithShifterOp
/// CHECK-START-ARM64: void Main.$opt$validateExtendByteInt1(int, byte) instruction_simplifier_arm64 (after)
/// CHECK-NOT: TypeConversion
public static void $opt$validateExtendByteInt1(int a, byte b) {
assertIntEquals(a + $noinline$byteToChar (b), a + (char)b);
+ // Conversions byte->short and short->int are implicit; nothing to merge.
assertIntEquals(a + $noinline$byteToShort(b), a + (short)b);
}
@@ -266,17 +267,24 @@
/// CHECK: Arm64DataProcWithShifterOp
/// CHECK: Arm64DataProcWithShifterOp
/// CHECK: Arm64DataProcWithShifterOp
+ /// CHECK: Arm64DataProcWithShifterOp
+ /// CHECK: Arm64DataProcWithShifterOp
+ /// CHECK-NOT: Arm64DataProcWithShifterOp
/// CHECK-START-ARM64: void Main.$opt$validateExtendByteLong(long, byte) instruction_simplifier_arm64 (after)
/// CHECK: TypeConversion
- /// CHECK: TypeConversion
/// CHECK-NOT: TypeConversion
public static void $opt$validateExtendByteLong(long a, byte b) {
- // The first two tests have a type conversion.
+ // In each of the following tests, there will be a merge on the LHS.
+
+ // The first test has an explicit byte->char conversion on RHS,
+ // followed by a conversion that is merged with the Add.
assertLongEquals(a + $noinline$byteToChar (b), a + (char)b);
+ // Since conversions byte->short and byte->int are implicit, the RHS
+ // for the two tests below is the same and one is eliminated by GVN.
+ // The other is then merged to a shifter operand instruction.
assertLongEquals(a + $noinline$byteToShort(b), a + (short)b);
- // This test does not because the conversion to `int` is optimized away.
assertLongEquals(a + $noinline$byteToInt (b), a + (int)b);
}
diff --git a/test/552-checker-primitive-typeprop/smali/ArrayGet.smali b/test/552-checker-primitive-typeprop/smali/ArrayGet.smali
index 042fa0c..de32290 100644
--- a/test/552-checker-primitive-typeprop/smali/ArrayGet.smali
+++ b/test/552-checker-primitive-typeprop/smali/ArrayGet.smali
@@ -19,10 +19,10 @@
# Test phi with fixed-type ArrayGet as an input and a matching second input.
# The phi should be typed accordingly.
-## CHECK-START: void ArrayGet.matchingFixedType(float[], float) ssa_builder (after)
+## CHECK-START: void ArrayGet.matchingFixedType(float[], float) builder (after)
## CHECK-NOT: Phi
-## CHECK-START-DEBUGGABLE: void ArrayGet.matchingFixedType(float[], float) ssa_builder (after)
+## CHECK-START-DEBUGGABLE: void ArrayGet.matchingFixedType(float[], float) builder (after)
## CHECK-DAG: <<Arg1:f\d+>> ParameterValue
## CHECK-DAG: <<Aget:f\d+>> ArrayGet
## CHECK-DAG: {{f\d+}} Phi [<<Aget>>,<<Arg1>>] reg:0
@@ -49,10 +49,10 @@
# Test phi with fixed-type ArrayGet as an input and a conflicting second input.
# The phi should be eliminated due to the conflict.
-## CHECK-START: void ArrayGet.conflictingFixedType(float[], int) ssa_builder (after)
+## CHECK-START: void ArrayGet.conflictingFixedType(float[], int) builder (after)
## CHECK-NOT: Phi
-## CHECK-START-DEBUGGABLE: void ArrayGet.conflictingFixedType(float[], int) ssa_builder (after)
+## CHECK-START-DEBUGGABLE: void ArrayGet.conflictingFixedType(float[], int) builder (after)
## CHECK-NOT: Phi
.method public static conflictingFixedType([FI)V
.registers 8
@@ -76,13 +76,13 @@
# Same test as the one above, only this time tests that type of ArrayGet is not
# changed.
-## CHECK-START: void ArrayGet.conflictingFixedType2(int[], float) ssa_builder (after)
+## CHECK-START: void ArrayGet.conflictingFixedType2(int[], float) builder (after)
## CHECK-NOT: Phi
-## CHECK-START-DEBUGGABLE: void ArrayGet.conflictingFixedType2(int[], float) ssa_builder (after)
+## CHECK-START-DEBUGGABLE: void ArrayGet.conflictingFixedType2(int[], float) builder (after)
## CHECK-NOT: Phi
-## CHECK-START-DEBUGGABLE: void ArrayGet.conflictingFixedType2(int[], float) ssa_builder (after)
+## CHECK-START-DEBUGGABLE: void ArrayGet.conflictingFixedType2(int[], float) builder (after)
## CHECK: {{i\d+}} ArrayGet
.method public static conflictingFixedType2([IF)V
.registers 8
@@ -107,10 +107,10 @@
# Test phi with free-type ArrayGet as an input and a matching second input.
# The phi should be typed accordingly.
-## CHECK-START: void ArrayGet.matchingFreeType(float[], float) ssa_builder (after)
+## CHECK-START: void ArrayGet.matchingFreeType(float[], float) builder (after)
## CHECK-NOT: Phi
-## CHECK-START-DEBUGGABLE: void ArrayGet.matchingFreeType(float[], float) ssa_builder (after)
+## CHECK-START-DEBUGGABLE: void ArrayGet.matchingFreeType(float[], float) builder (after)
## CHECK-DAG: <<Arg1:f\d+>> ParameterValue
## CHECK-DAG: <<Aget:f\d+>> ArrayGet
## CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Aget>>]
@@ -139,10 +139,10 @@
# The phi will be kept and typed according to the second input despite the
# conflict.
-## CHECK-START: void ArrayGet.conflictingFreeType(int[], float) ssa_builder (after)
+## CHECK-START: void ArrayGet.conflictingFreeType(int[], float) builder (after)
## CHECK-NOT: Phi
-## CHECK-START-DEBUGGABLE: void ArrayGet.conflictingFreeType(int[], float) ssa_builder (after)
+## CHECK-START-DEBUGGABLE: void ArrayGet.conflictingFreeType(int[], float) builder (after)
## CHECK-NOT: Phi
.method public static conflictingFreeType([IF)V
@@ -169,7 +169,7 @@
# case uses ArrayGet indirectly through two phis. It also creates an unused
# conflicting phi which should not be preserved.
-## CHECK-START: void ArrayGet.conflictingPhiUses(int[], float, boolean, boolean, boolean) ssa_builder (after)
+## CHECK-START: void ArrayGet.conflictingPhiUses(int[], float, boolean, boolean, boolean) builder (after)
## CHECK: InvokeStaticOrDirect env:[[{{i\d+}},{{i\d+}},_,{{i\d+}},{{.*}}
.method public static conflictingPhiUses([IFZZZ)V
@@ -209,10 +209,10 @@
# another. The situation needs to be resolved so that only one instruction
# remains.
-## CHECK-START: void ArrayGet.typedVsUntypedPhiUse(float[], float, boolean, boolean) ssa_builder (after)
+## CHECK-START: void ArrayGet.typedVsUntypedPhiUse(float[], float, boolean, boolean) builder (after)
## CHECK: {{f\d+}} ArrayGet
-## CHECK-START: void ArrayGet.typedVsUntypedPhiUse(float[], float, boolean, boolean) ssa_builder (after)
+## CHECK-START: void ArrayGet.typedVsUntypedPhiUse(float[], float, boolean, boolean) builder (after)
## CHECK-NOT: {{i\d+}} ArrayGet
.method public static typedVsUntypedPhiUse([FFZZ)V
diff --git a/test/552-checker-primitive-typeprop/smali/ArraySet.smali b/test/552-checker-primitive-typeprop/smali/ArraySet.smali
index 57d8606..087460a 100644
--- a/test/552-checker-primitive-typeprop/smali/ArraySet.smali
+++ b/test/552-checker-primitive-typeprop/smali/ArraySet.smali
@@ -19,7 +19,7 @@
# Note that the input is a Phi to make sure primitive type propagation is re-run
# on the replaced inputs.
-## CHECK-START: void ArraySet.ambiguousSet(int[], float[], boolean) ssa_builder (after)
+## CHECK-START: void ArraySet.ambiguousSet(int[], float[], boolean) builder (after)
## CHECK-DAG: <<IntArray:l\d+>> ParameterValue klass:int[]
## CHECK-DAG: <<IntA:i\d+>> IntConstant 0
## CHECK-DAG: <<IntB:i\d+>> IntConstant 1073741824
diff --git a/test/552-checker-primitive-typeprop/smali/SsaBuilder.smali b/test/552-checker-primitive-typeprop/smali/SsaBuilder.smali
index 395feaa..0d067ed 100644
--- a/test/552-checker-primitive-typeprop/smali/SsaBuilder.smali
+++ b/test/552-checker-primitive-typeprop/smali/SsaBuilder.smali
@@ -22,7 +22,7 @@
# otherwise running the code with an array short enough to throw will crash at
# runtime because v0 is undefined.
-## CHECK-START: int SsaBuilder.environmentPhi(boolean, int[]) ssa_builder (after)
+## CHECK-START: int SsaBuilder.environmentPhi(boolean, int[]) builder (after)
## CHECK-DAG: <<Cst0:f\d+>> FloatConstant 0
## CHECK-DAG: <<Cst2:f\d+>> FloatConstant 2
## CHECK-DAG: <<Phi:f\d+>> Phi [<<Cst0>>,<<Cst2>>]
diff --git a/test/552-checker-primitive-typeprop/smali/TypePropagation.smali b/test/552-checker-primitive-typeprop/smali/TypePropagation.smali
index 58682a1..d34e43e 100644
--- a/test/552-checker-primitive-typeprop/smali/TypePropagation.smali
+++ b/test/552-checker-primitive-typeprop/smali/TypePropagation.smali
@@ -15,7 +15,7 @@
.class public LTypePropagation;
.super Ljava/lang/Object;
-## CHECK-START-DEBUGGABLE: void TypePropagation.mergeDeadPhi(boolean, boolean, int, float, float) ssa_builder (after)
+## CHECK-START-DEBUGGABLE: void TypePropagation.mergeDeadPhi(boolean, boolean, int, float, float) builder (after)
## CHECK-NOT: Phi
.method public static mergeDeadPhi(ZZIFF)V
.registers 8
@@ -34,7 +34,7 @@
return-void
.end method
-## CHECK-START-DEBUGGABLE: void TypePropagation.mergeSameType(boolean, int, int) ssa_builder (after)
+## CHECK-START-DEBUGGABLE: void TypePropagation.mergeSameType(boolean, int, int) builder (after)
## CHECK: {{i\d+}} Phi
## CHECK-NOT: Phi
.method public static mergeSameType(ZII)V
@@ -47,7 +47,7 @@
return-void
.end method
-## CHECK-START-DEBUGGABLE: void TypePropagation.mergeVoidInput(boolean, boolean, int, int) ssa_builder (after)
+## CHECK-START-DEBUGGABLE: void TypePropagation.mergeVoidInput(boolean, boolean, int, int) builder (after)
## CHECK: {{i\d+}} Phi
## CHECK: {{i\d+}} Phi
## CHECK-NOT: Phi
@@ -64,7 +64,7 @@
return-void
.end method
-## CHECK-START-DEBUGGABLE: void TypePropagation.mergeDifferentSize(boolean, int, long) ssa_builder (after)
+## CHECK-START-DEBUGGABLE: void TypePropagation.mergeDifferentSize(boolean, int, long) builder (after)
## CHECK-NOT: Phi
.method public static mergeDifferentSize(ZIJ)V
.registers 8
@@ -76,7 +76,7 @@
return-void
.end method
-## CHECK-START-DEBUGGABLE: void TypePropagation.mergeRefFloat(boolean, float, java.lang.Object) ssa_builder (after)
+## CHECK-START-DEBUGGABLE: void TypePropagation.mergeRefFloat(boolean, float, java.lang.Object) builder (after)
## CHECK-NOT: Phi
.method public static mergeRefFloat(ZFLjava/lang/Object;)V
.registers 8
@@ -88,7 +88,7 @@
return-void
.end method
-## CHECK-START-DEBUGGABLE: void TypePropagation.mergeIntFloat_Success(boolean, float) ssa_builder (after)
+## CHECK-START-DEBUGGABLE: void TypePropagation.mergeIntFloat_Success(boolean, float) builder (after)
## CHECK: {{f\d+}} Phi
## CHECK-NOT: Phi
.method public static mergeIntFloat_Success(ZF)V
@@ -101,7 +101,7 @@
return-void
.end method
-## CHECK-START-DEBUGGABLE: void TypePropagation.mergeIntFloat_Fail(boolean, int, float) ssa_builder (after)
+## CHECK-START-DEBUGGABLE: void TypePropagation.mergeIntFloat_Fail(boolean, int, float) builder (after)
## CHECK-NOT: Phi
.method public static mergeIntFloat_Fail(ZIF)V
.registers 8
@@ -113,7 +113,7 @@
return-void
.end method
-## CHECK-START-DEBUGGABLE: void TypePropagation.updateAllUsersOnConflict(boolean, boolean, int, float, int) ssa_builder (after)
+## CHECK-START-DEBUGGABLE: void TypePropagation.updateAllUsersOnConflict(boolean, boolean, int, float, int) builder (after)
## CHECK-NOT: Phi
.method public static updateAllUsersOnConflict(ZZIFI)V
.registers 8
diff --git a/test/554-checker-rtp-checkcast/src/Main.java b/test/554-checker-rtp-checkcast/src/Main.java
index 607f71a..5bf766f 100644
--- a/test/554-checker-rtp-checkcast/src/Main.java
+++ b/test/554-checker-rtp-checkcast/src/Main.java
@@ -19,7 +19,7 @@
public static Object returnIntArray() { return new int[10]; }
- /// CHECK-START: void Main.boundTypeForMergingPhi() ssa_builder (after)
+ /// CHECK-START: void Main.boundTypeForMergingPhi() builder (after)
/// CHECK-DAG: ArraySet [<<NC:l\d+>>,{{i\d+}},{{i\d+}}]
/// CHECK-DAG: <<NC>> NullCheck [<<Phi:l\d+>>]
/// CHECK-DAG: <<Phi>> Phi klass:int[]
@@ -32,7 +32,7 @@
array[0] = 14;
}
- /// CHECK-START: void Main.boundTypeForLoopPhi() ssa_builder (after)
+ /// CHECK-START: void Main.boundTypeForLoopPhi() builder (after)
/// CHECK-DAG: ArraySet [<<NC:l\d+>>,{{i\d+}},{{i\d+}}]
/// CHECK-DAG: <<NC>> NullCheck [<<Phi:l\d+>>]
/// CHECK-DAG: <<Phi>> Phi klass:int[]
@@ -50,7 +50,7 @@
array[0] = 14;
}
- /// CHECK-START: void Main.boundTypeForCatchPhi() ssa_builder (after)
+ /// CHECK-START: void Main.boundTypeForCatchPhi() builder (after)
/// CHECK-DAG: ArraySet [<<NC:l\d+>>,{{i\d+}},{{i\d+}}]
/// CHECK-DAG: <<NC>> NullCheck [<<Phi:l\d+>>]
/// CHECK-DAG: <<Phi>> Phi is_catch_phi:true klass:int[]
diff --git a/test/555-checker-regression-x86const/build b/test/555-checker-regression-x86const/build
new file mode 100644
index 0000000..09dcc36
--- /dev/null
+++ b/test/555-checker-regression-x86const/build
@@ -0,0 +1,46 @@
+#!/bin/bash
+#
+# 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.
+
+# Stop if something fails.
+set -e
+
+# We can't use src-ex testing infrastructure because src and src-ex are compiled
+# with javac independetely and can't share code (without reflection).
+
+mkdir classes
+${JAVAC} -d classes `find src -name '*.java'`
+
+mkdir classes-ex
+mv classes/UnresolvedClass.class classes-ex
+
+if [ ${USE_JACK} = "true" ]; then
+ # Create .jack files from classes generated with javac.
+ ${JILL} classes --output classes.jack
+ ${JILL} classes-ex --output classes-ex.jack
+
+ # Create DEX files from .jack files.
+ ${JACK} --import classes.jack --output-dex .
+ zip $TEST_NAME.jar classes.dex
+ ${JACK} --import classes-ex.jack --output-dex .
+ zip ${TEST_NAME}-ex.jar classes.dex
+else
+ if [ ${NEED_DEX} = "true" ]; then
+ ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
+ zip $TEST_NAME.jar classes.dex
+ ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex
+ zip ${TEST_NAME}-ex.jar classes.dex
+ fi
+fi
diff --git a/test/555-checker-regression-x86const/expected.txt b/test/555-checker-regression-x86const/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/555-checker-regression-x86const/expected.txt
diff --git a/test/555-checker-regression-x86const/info.txt b/test/555-checker-regression-x86const/info.txt
new file mode 100644
index 0000000..c4037fa
--- /dev/null
+++ b/test/555-checker-regression-x86const/info.txt
@@ -0,0 +1,2 @@
+Check that X86 FP constant-area handling handles intrinsics with CurrentMethod
+on the call.
diff --git a/test/555-checker-regression-x86const/run b/test/555-checker-regression-x86const/run
new file mode 100644
index 0000000..63fdb8c
--- /dev/null
+++ b/test/555-checker-regression-x86const/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+# Use secondary switch to add secondary dex file to class path.
+exec ${RUN} "${@}" --secondary
diff --git a/test/555-checker-regression-x86const/src/Main.java b/test/555-checker-regression-x86const/src/Main.java
new file mode 100644
index 0000000..914cfde
--- /dev/null
+++ b/test/555-checker-regression-x86const/src/Main.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main extends UnresolvedClass {
+
+ /// CHECK-START: float Main.callAbs(float) register (before)
+ /// CHECK: <<CurrentMethod:[ij]\d+>> CurrentMethod
+ /// CHECK: <<ParamValue:f\d+>> ParameterValue
+ /// CHECK: InvokeStaticOrDirect [<<ParamValue>>,<<CurrentMethod>>] method_name:java.lang.Math.abs
+ static public float callAbs(float f) {
+ // An intrinsic invoke in a method that has unresolved references will still
+ // have a CurrentMethod as an argument. The X86 pc_relative_fixups_x86 pass
+ // must be able to handle Math.abs invokes that have a CurrentMethod, as both
+ // the CurrentMethod and the HX86LoadFromConstantTable (for the bitmask)
+ // expect to be in the 'SpecialInputIndex' input index.
+ return Math.abs(f);
+ }
+
+ static public void main(String[] args) {
+ expectEquals(callAbs(-6.5f), 6.5f);
+ }
+
+ public static void expectEquals(float expected, float result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/555-checker-regression-x86const/src/Unresolved.java b/test/555-checker-regression-x86const/src/Unresolved.java
new file mode 100644
index 0000000..e98bdbf
--- /dev/null
+++ b/test/555-checker-regression-x86const/src/Unresolved.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class UnresolvedClass {
+}
diff --git a/test/557-checker-ref-equivalent/smali/TestCase.smali b/test/557-checker-ref-equivalent/smali/TestCase.smali
index 2472957..1347554 100644
--- a/test/557-checker-ref-equivalent/smali/TestCase.smali
+++ b/test/557-checker-ref-equivalent/smali/TestCase.smali
@@ -16,7 +16,7 @@
.super Ljava/lang/Object;
-## CHECK-START: void TestCase.testIntRefEquivalent() ssa_builder (after)
+## CHECK-START: void TestCase.testIntRefEquivalent() builder (after)
## CHECK-NOT: Phi
.method public static testIntRefEquivalent()V
.registers 4
diff --git a/test/557-checker-ref-equivalent/src/Main.java b/test/557-checker-ref-equivalent/src/Main.java
index a970af5..9323757 100644
--- a/test/557-checker-ref-equivalent/src/Main.java
+++ b/test/557-checker-ref-equivalent/src/Main.java
@@ -16,7 +16,7 @@
public class Main {
- /// CHECK-START: void Main.testRedundantPhiCycle(boolean) ssa_builder (after)
+ /// CHECK-START: void Main.testRedundantPhiCycle(boolean) builder (after)
/// CHECK-NOT: Phi
private void testRedundantPhiCycle(boolean cond) {
Object o = null;
@@ -28,7 +28,7 @@
}
}
- /// CHECK-START: void Main.testLoopPhisWithNullAndCrossUses(boolean) ssa_builder (after)
+ /// CHECK-START: void Main.testLoopPhisWithNullAndCrossUses(boolean) builder (after)
/// CHECK-NOT: Phi
private void testLoopPhisWithNullAndCrossUses(boolean cond) {
Main a = null;
diff --git a/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali b/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali
index 971ad84..7ce60a3 100644
--- a/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali
+++ b/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali
@@ -323,7 +323,7 @@
# - / \-
# irreducible_loop_back_edge loop_within_back_edge
#
-## CHECK-START: void IrreducibleLoop.analyze1(int) ssa_builder (after)
+## CHECK-START: void IrreducibleLoop.analyze1(int) builder (after)
## CHECK-DAG: Goto loop:<<OuterLoop:B\d+>> outer_loop:none irreducible:true
## CHECK-DAG: Goto outer_loop:<<OuterLoop>> irreducible:false
.method public static analyze1(I)V
@@ -371,7 +371,7 @@
# exit \- /
# irreducible_loop_body
#
-## CHECK-START: void IrreducibleLoop.analyze2(int) ssa_builder (after)
+## CHECK-START: void IrreducibleLoop.analyze2(int) builder (after)
## CHECK-DAG: Goto outer_loop:none irreducible:false
## CHECK-DAG: Goto outer_loop:none irreducible:true
.method public static analyze2(I)V
@@ -418,7 +418,7 @@
# |
# exit
#
-## CHECK-START: void IrreducibleLoop.analyze3(int) ssa_builder (after)
+## CHECK-START: void IrreducibleLoop.analyze3(int) builder (after)
## CHECK-DAG: Goto loop:<<OuterLoop:B\d+>> outer_loop:none irreducible:true
## CHECK-DAG: Goto outer_loop:<<OuterLoop>> irreducible:true
.method public static analyze3(I)V
@@ -467,7 +467,7 @@
# |
# exit
#
-## CHECK-START: void IrreducibleLoop.analyze4(int) ssa_builder (after)
+## CHECK-START: void IrreducibleLoop.analyze4(int) builder (after)
## CHECK-DAG: Goto loop:<<OuterLoop:B\d+>> outer_loop:none irreducible:true
## CHECK-DAG: Goto outer_loop:<<OuterLoop>> irreducible:true
.method public static analyze4(I)V
@@ -519,7 +519,7 @@
# |
# exit
#
-## CHECK-START: void IrreducibleLoop.analyze5(int) ssa_builder (after)
+## CHECK-START: void IrreducibleLoop.analyze5(int) builder (after)
## CHECK-DAG: Goto loop:<<OuterLoop:B\d+>> outer_loop:none irreducible:true
## CHECK-DAG: Goto outer_loop:<<OuterLoop>> irreducible:true
.method public static analyze5(I)V
diff --git a/test/559-checker-rtp-ifnotnull/src/Main.java b/test/559-checker-rtp-ifnotnull/src/Main.java
index 8f40129..2dc5666 100644
--- a/test/559-checker-rtp-ifnotnull/src/Main.java
+++ b/test/559-checker-rtp-ifnotnull/src/Main.java
@@ -17,7 +17,7 @@
public class Main {
- /// CHECK-START: void Main.boundTypeForIfNotNull() ssa_builder (after)
+ /// CHECK-START: void Main.boundTypeForIfNotNull() builder (after)
/// CHECK-DAG: <<Method:(i|j)\d+>> CurrentMethod
/// CHECK-DAG: <<Null:l\d+>> NullConstant
/// CHECK-DAG: <<Cst5:i\d+>> IntConstant 5
diff --git a/test/562-bce-preheader/src/Main.java b/test/562-bce-preheader/src/Main.java
index 8de0533..8b527b4 100644
--- a/test/562-bce-preheader/src/Main.java
+++ b/test/562-bce-preheader/src/Main.java
@@ -70,6 +70,26 @@
return acc;
}
+ /**
+ * An artificial example with an inconsistent phi structure during
+ * dynamic bce that is corrected afterwards. Note that only the last
+ * assignment is really live, but the other statements set up an
+ * interesting phi structure.
+ */
+ private static int doit(int[] z) {
+ int a = 0;
+ for (int i = 0; i < 10; ++i) {
+ for (int j = i; j < 10; ++j) {
+ a = z[i];
+ for (int k = 0; k < 10; ++k) {
+ a += z[k];
+ a = z[i];
+ }
+ }
+ }
+ return a;
+ }
+
public static void main(String args[]) {
int[][] x = new int[2][2];
int y;
@@ -96,6 +116,9 @@
expectEquals(26, foo(a, b, 2));
expectEquals(38, foo(a, b, 3));
+ int[] z = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ expectEquals(10, doit(z));
+
System.out.println("passed");
}
diff --git a/test/565-checker-condition-liveness/src/Main.java b/test/565-checker-condition-liveness/src/Main.java
index a811e5b..dc4cb76 100644
--- a/test/565-checker-condition-liveness/src/Main.java
+++ b/test/565-checker-condition-liveness/src/Main.java
@@ -16,6 +16,24 @@
public class Main {
+ /// CHECK-START-X86: int Main.p(float) liveness (after)
+ /// CHECK: <<Arg:f\d+>> ParameterValue uses:[<<UseInput:\d+>>]
+ /// CHECK-DAG: <<Five:f\d+>> FloatConstant 5 uses:[<<UseInput>>]
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<MinusOne:i\d+>> IntConstant -1 uses:[<<UseInput>>]
+ /// CHECK: <<Base:i\d+>> X86ComputeBaseMethodAddress uses:[<<UseInput>>]
+ /// CHECK-NEXT: <<Load:f\d+>> X86LoadFromConstantTable [<<Base>>,<<Five>>]
+ /// CHECK-NEXT: <<Cond:z\d+>> LessThanOrEqual [<<Arg>>,<<Load>>]
+ /// CHECK-NEXT: Select [<<Zero>>,<<MinusOne>>,<<Cond>>] liveness:<<LivSel:\d+>>
+ /// CHECK-EVAL: <<UseInput>> == <<LivSel>> + 1
+
+ public static int p(float arg) {
+ if (arg > 5.0f) {
+ return 0;
+ }
+ return -1;
+ }
+
/// CHECK-START: void Main.main(java.lang.String[]) liveness (after)
/// CHECK: <<X:i\d+>> ArrayLength uses:[<<UseInput:\d+>>]
/// CHECK: <<Y:i\d+>> StaticFieldGet uses:[<<UseInput>>]
diff --git a/test/565-checker-doublenegbitwise/src/Main.java b/test/565-checker-doublenegbitwise/src/Main.java
index d681ad7..41af97b 100644
--- a/test/565-checker-doublenegbitwise/src/Main.java
+++ b/test/565-checker-doublenegbitwise/src/Main.java
@@ -37,7 +37,7 @@
// Note: before the instruction_simplifier pass, Xor's are used instead of
// Not's (the simplification happens during the same pass).
- /// CHECK-START-ARM64: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (before)
/// CHECK: <<P1:i\d+>> ParameterValue
/// CHECK: <<P2:i\d+>> ParameterValue
/// CHECK: <<CstM1:i\d+>> IntConstant -1
@@ -46,16 +46,18 @@
/// CHECK: <<And:i\d+>> And [<<Not1>>,<<Not2>>]
/// CHECK: Return [<<And>>]
- /// CHECK-START-ARM64: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
/// CHECK: <<P1:i\d+>> ParameterValue
/// CHECK: <<P2:i\d+>> ParameterValue
/// CHECK: <<Or:i\d+>> Or [<<P1>>,<<P2>>]
/// CHECK: <<Not:i\d+>> Not [<<Or>>]
/// CHECK: Return [<<Not>>]
- /// CHECK-START-ARM64: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
/// CHECK: Not
/// CHECK-NOT: Not
+
+ /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
/// CHECK-NOT: And
public static int $opt$noinline$andToOr(int a, int b) {
@@ -64,12 +66,49 @@
}
/**
+ * Test transformation of Not/Not/And into Or/Not for boolean negations.
+ * Note that the graph before this instruction simplification pass does not
+ * contain `HBooleanNot` instructions. This is because this transformation
+ * follows the optimization of `HSelect` to `HBooleanNot` occurring in the
+ * same pass.
+ */
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier_after_bce (before)
+ /// CHECK: <<P1:z\d+>> ParameterValue
+ /// CHECK: <<P2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>]
+ /// CHECK: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>]
+ /// CHECK: <<And:i\d+>> And [<<Select2>>,<<Select1>>]
+ /// CHECK: Return [<<And>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier_after_bce (after)
+ /// CHECK: <<Cond1:z\d+>> ParameterValue
+ /// CHECK: <<Cond2:z\d+>> ParameterValue
+ /// CHECK: <<Or:i\d+>> Or [<<Cond2>>,<<Cond1>>]
+ /// CHECK: <<BooleanNot:z\d+>> BooleanNot [<<Or>>]
+ /// CHECK: Return [<<BooleanNot>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier_after_bce (after)
+ /// CHECK: BooleanNot
+ /// CHECK-NOT: BooleanNot
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier_after_bce (after)
+ /// CHECK-NOT: And
+
+ public static boolean $opt$noinline$booleanAndToOr(boolean a, boolean b) {
+ if (doThrow) throw new Error();
+ return !a & !b;
+ }
+
+ /**
* Test transformation of Not/Not/Or into And/Not.
*/
// See note above.
// The second Xor has its arguments reversed for no obvious reason.
- /// CHECK-START-ARM64: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (before)
/// CHECK: <<P1:j\d+>> ParameterValue
/// CHECK: <<P2:j\d+>> ParameterValue
/// CHECK: <<CstM1:j\d+>> LongConstant -1
@@ -78,16 +117,18 @@
/// CHECK: <<Or:j\d+>> Or [<<Not1>>,<<Not2>>]
/// CHECK: Return [<<Or>>]
- /// CHECK-START-ARM64: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
/// CHECK: <<P1:j\d+>> ParameterValue
/// CHECK: <<P2:j\d+>> ParameterValue
/// CHECK: <<And:j\d+>> And [<<P1>>,<<P2>>]
/// CHECK: <<Not:j\d+>> Not [<<And>>]
/// CHECK: Return [<<Not>>]
- /// CHECK-START-ARM64: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
/// CHECK: Not
/// CHECK-NOT: Not
+
+ /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
/// CHECK-NOT: Or
public static long $opt$noinline$orToAnd(long a, long b) {
@@ -96,13 +137,50 @@
}
/**
+ * Test transformation of Not/Not/Or into Or/And for boolean negations.
+ * Note that the graph before this instruction simplification pass does not
+ * contain `HBooleanNot` instructions. This is because this transformation
+ * follows the optimization of `HSelect` to `HBooleanNot` occurring in the
+ * same pass.
+ */
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier_after_bce (before)
+ /// CHECK: <<P1:z\d+>> ParameterValue
+ /// CHECK: <<P2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>]
+ /// CHECK: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>]
+ /// CHECK: <<Or:i\d+>> Or [<<Select2>>,<<Select1>>]
+ /// CHECK: Return [<<Or>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier_after_bce (after)
+ /// CHECK: <<Cond1:z\d+>> ParameterValue
+ /// CHECK: <<Cond2:z\d+>> ParameterValue
+ /// CHECK: <<And:i\d+>> And [<<Cond2>>,<<Cond1>>]
+ /// CHECK: <<BooleanNot:z\d+>> BooleanNot [<<And>>]
+ /// CHECK: Return [<<BooleanNot>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier_after_bce (after)
+ /// CHECK: BooleanNot
+ /// CHECK-NOT: BooleanNot
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier_after_bce (after)
+ /// CHECK-NOT: Or
+
+ public static boolean $opt$noinline$booleanOrToAnd(boolean a, boolean b) {
+ if (doThrow) throw new Error();
+ return !a | !b;
+ }
+
+ /**
* Test that the transformation copes with inputs being separated from the
* bitwise operations.
* This is a regression test. The initial logic was inserting the new bitwise
* operation incorrectly.
*/
- /// CHECK-START-ARM64: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (before)
/// CHECK: <<P1:i\d+>> ParameterValue
/// CHECK: <<P2:i\d+>> ParameterValue
/// CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
@@ -114,7 +192,7 @@
/// CHECK: <<Or:i\d+>> Or [<<Not1>>,<<Not2>>]
/// CHECK: Return [<<Or>>]
- /// CHECK-START-ARM64: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
/// CHECK: <<P1:i\d+>> ParameterValue
/// CHECK: <<P2:i\d+>> ParameterValue
/// CHECK: <<Cst1:i\d+>> IntConstant 1
@@ -124,9 +202,11 @@
/// CHECK: <<Not:i\d+>> Not [<<And>>]
/// CHECK: Return [<<Not>>]
- /// CHECK-START-ARM64: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
/// CHECK: Not
/// CHECK-NOT: Not
+
+ /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
/// CHECK-NOT: Or
public static int $opt$noinline$regressInputsAway(int a, int b) {
@@ -143,7 +223,7 @@
*/
// See first note above.
- /// CHECK-START-ARM64: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (before)
/// CHECK: <<P1:i\d+>> ParameterValue
/// CHECK: <<P2:i\d+>> ParameterValue
/// CHECK: <<CstM1:i\d+>> IntConstant -1
@@ -152,13 +232,13 @@
/// CHECK: <<Xor:i\d+>> Xor [<<Not1>>,<<Not2>>]
/// CHECK: Return [<<Xor>>]
- /// CHECK-START-ARM64: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after)
/// CHECK: <<P1:i\d+>> ParameterValue
/// CHECK: <<P2:i\d+>> ParameterValue
/// CHECK: <<Xor:i\d+>> Xor [<<P1>>,<<P2>>]
/// CHECK: Return [<<Xor>>]
- /// CHECK-START-ARM64: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after)
/// CHECK-NOT: Not
public static int $opt$noinline$notXorToXor(int a, int b) {
@@ -167,10 +247,42 @@
}
/**
+ * Test transformation of Not/Not/Xor into Xor for boolean negations.
+ * Note that the graph before this instruction simplification pass does not
+ * contain `HBooleanNot` instructions. This is because this transformation
+ * follows the optimization of `HSelect` to `HBooleanNot` occurring in the
+ * same pass.
+ */
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier_after_bce (before)
+ /// CHECK: <<P1:z\d+>> ParameterValue
+ /// CHECK: <<P2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>]
+ /// CHECK: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>]
+ /// CHECK: <<Xor:i\d+>> Xor [<<Select2>>,<<Select1>>]
+ /// CHECK: Return [<<Xor>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier_after_bce (after)
+ /// CHECK: <<Cond1:z\d+>> ParameterValue
+ /// CHECK: <<Cond2:z\d+>> ParameterValue
+ /// CHECK: <<Xor:i\d+>> Xor [<<Cond2>>,<<Cond1>>]
+ /// CHECK: Return [<<Xor>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier_after_bce (after)
+ /// CHECK-NOT: BooleanNot
+
+ public static boolean $opt$noinline$booleanNotXorToXor(boolean a, boolean b) {
+ if (doThrow) throw new Error();
+ return !a ^ !b;
+ }
+
+ /**
* Check that no transformation is done when one Not has multiple uses.
*/
- /// CHECK-START-ARM64: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (before)
/// CHECK: <<P1:i\d+>> ParameterValue
/// CHECK: <<P2:i\d+>> ParameterValue
/// CHECK: <<CstM1:i\d+>> IntConstant -1
@@ -182,7 +294,7 @@
/// CHECK: <<Add:i\d+>> Add [<<And2>>,<<And1>>]
/// CHECK: Return [<<Add>>]
- /// CHECK-START-ARM64: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after)
/// CHECK: <<P1:i\d+>> ParameterValue
/// CHECK: <<P2:i\d+>> ParameterValue
/// CHECK: <<One:i\d+>> IntConstant 1
@@ -193,7 +305,7 @@
/// CHECK: <<Add:i\d+>> Add [<<And2>>,<<And1>>]
/// CHECK: Return [<<Add>>]
- /// CHECK-START-ARM64: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after)
/// CHECK-NOT: Or
public static int $opt$noinline$notMultipleUses(int a, int b) {
diff --git a/test/566-checker-codegen-select/src/Main.java b/test/566-checker-codegen-select/src/Main.java
index edb31e6..3a1b3fc 100644
--- a/test/566-checker-codegen-select/src/Main.java
+++ b/test/566-checker-codegen-select/src/Main.java
@@ -45,6 +45,13 @@
/// CHECK: LessThanOrEqual
/// CHECK-NEXT: Select
+ // Check that we generate CMOV for long on x86_64.
+ /// CHECK-START-X86_64: long Main.$noinline$longSelect_Constant(long) disassembly (after)
+ /// CHECK: LessThanOrEqual
+ /// CHECK-NEXT: Select
+ /// CHECK: cmpq
+ /// CHECK: cmovle/ngq
+
public long $noinline$longSelect_Constant(long param) {
if (doThrow) { throw new Error(); }
long val_true = longB;
@@ -52,12 +59,34 @@
return (param > 3L) ? val_true : val_false;
}
+ // Check that we generate CMOV for int on x86_64.
+ /// CHECK-START-X86_64: int Main.$noinline$intSelect_Constant(int) disassembly (after)
+ /// CHECK: LessThan
+ /// CHECK-NEXT: Select
+ /// CHECK: cmp
+ /// CHECK: cmovl/nge
+
+ public int $noinline$intSelect_Constant(int param) {
+ if (doThrow) { throw new Error(); }
+ int val_true = intB;
+ int val_false = intC;
+ return (param >= 3) ? val_true : val_false;
+ }
+
public static void main(String[] args) {
Main m = new Main();
assertLongEquals(5L, m.$noinline$longSelect(4L));
assertLongEquals(7L, m.$noinline$longSelect(2L));
assertLongEquals(5L, m.$noinline$longSelect_Constant(4L));
assertLongEquals(7L, m.$noinline$longSelect_Constant(2L));
+ assertIntEquals(5, m.$noinline$intSelect_Constant(4));
+ assertIntEquals(7, m.$noinline$intSelect_Constant(2));
+ }
+
+ public static void assertIntEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new Error(expected + " != " + actual);
+ }
}
public static void assertLongEquals(long expected, long actual) {
@@ -71,4 +100,6 @@
public long longA = 3L;
public long longB = 5L;
public long longC = 7L;
+ public int intB = 5;
+ public int intC = 7;
}
diff --git a/test/566-checker-signum/src/Main.java b/test/566-checker-signum/src/Main.java
index cc4a984..0ad0042 100644
--- a/test/566-checker-signum/src/Main.java
+++ b/test/566-checker-signum/src/Main.java
@@ -54,6 +54,13 @@
expectEquals(1, sign64(12345L));
expectEquals(1, sign64(Long.MAX_VALUE));
+ expectEquals(-1, sign64(0x800000007FFFFFFFL));
+ expectEquals(-1, sign64(0x80000000FFFFFFFFL));
+ expectEquals(1, sign64(0x000000007FFFFFFFL));
+ expectEquals(1, sign64(0x00000000FFFFFFFFL));
+ expectEquals(1, sign64(0x7FFFFFFF7FFFFFFFL));
+ expectEquals(1, sign64(0x7FFFFFFFFFFFFFFFL));
+
for (long i = -11L; i <= 11L; i++) {
int expected = 0;
if (i < 0) expected = -1;
@@ -61,6 +68,14 @@
expectEquals(expected, sign64(i));
}
+ for (long i = Long.MIN_VALUE; i <= Long.MIN_VALUE + 11L; i++) {
+ expectEquals(-1, sign64(i));
+ }
+
+ for (long i = Long.MAX_VALUE; i >= Long.MAX_VALUE - 11L; i--) {
+ expectEquals(1, sign64(i));
+ }
+
System.out.println("passed");
}
diff --git a/test/567-checker-compare/src/Main.java b/test/567-checker-compare/src/Main.java
index 52abb75..951d2c7 100644
--- a/test/567-checker-compare/src/Main.java
+++ b/test/567-checker-compare/src/Main.java
@@ -88,6 +88,10 @@
expectEquals(1, compare64(Long.MAX_VALUE, 1L));
expectEquals(1, compare64(Long.MAX_VALUE, Long.MAX_VALUE - 1L));
+ expectEquals(-1, compare64(0x111111117FFFFFFFL, 0x11111111FFFFFFFFL));
+ expectEquals(0, compare64(0x111111117FFFFFFFL, 0x111111117FFFFFFFL));
+ expectEquals(1, compare64(0x11111111FFFFFFFFL, 0x111111117FFFFFFFL));
+
for (long i = -11L; i <= 11L; i++) {
for (long j = -11L; j <= 11L; j++) {
int expected = 0;
@@ -97,6 +101,14 @@
}
}
+ for (long i = Long.MIN_VALUE; i <= Long.MIN_VALUE + 11L; i++) {
+ expectEquals(-1, compare64(i, 0));
+ }
+
+ for (long i = Long.MAX_VALUE; i >= Long.MAX_VALUE - 11L; i--) {
+ expectEquals(1, compare64(i, 0));
+ }
+
System.out.println("passed");
}
diff --git a/test/569-checker-pattern-replacement/src-multidex/Base.java b/test/569-checker-pattern-replacement/src-multidex/Base.java
new file mode 100644
index 0000000..f4d59af
--- /dev/null
+++ b/test/569-checker-pattern-replacement/src-multidex/Base.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Base {
+ Base() {
+ intField = 0; // Unnecessary IPUT.
+ doubleField = 0.0; // Unnecessary IPUT.
+ objectField = null; // Unnecessary IPUT.
+ }
+
+ Base(int intValue) {
+ intField = intValue;
+ }
+
+ Base(String stringValue) {
+ objectField = stringValue; // Unnecessary IPUT.
+ stringField = stringValue;
+ objectField = null; // Unnecessary IPUT.
+ }
+
+ Base(double doubleValue, Object objectValue) {
+ doubleField = doubleValue;
+ objectField = objectValue;
+ }
+
+ Base(int intValue, double doubleValue, Object objectValue) {
+ intField = intValue;
+ doubleField = doubleValue;
+ objectField = objectValue;
+ }
+
+ Base(int intValue, double doubleValue, Object objectValue, String stringValue) {
+ // Outside our limit of 3 IPUTs.
+ intField = intValue;
+ doubleField = doubleValue;
+ objectField = objectValue;
+ stringField = stringValue;
+ }
+
+ Base(double doubleValue) {
+ this(doubleValue, null);
+ }
+
+ Base(Object objectValue) {
+ // Unsupported forwarding of a value after a zero.
+ this(0.0, objectValue);
+ }
+
+ Base(int intValue, long dummy) {
+ this(intValue, 0.0, null);
+ }
+
+ public int intField;
+ public double doubleField;
+ public Object objectField;
+ public String stringField;
+}
diff --git a/test/569-checker-pattern-replacement/src-multidex/BaseWithFinalField.java b/test/569-checker-pattern-replacement/src-multidex/BaseWithFinalField.java
new file mode 100644
index 0000000..7a1d591
--- /dev/null
+++ b/test/569-checker-pattern-replacement/src-multidex/BaseWithFinalField.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class BaseWithFinalField {
+ BaseWithFinalField() {
+ intField = 0;
+ }
+
+ BaseWithFinalField(int intValue) {
+ intField = intValue;
+ }
+
+ public final int intField;
+}
diff --git a/test/569-checker-pattern-replacement/src-multidex/Derived.java b/test/569-checker-pattern-replacement/src-multidex/Derived.java
new file mode 100644
index 0000000..184563f
--- /dev/null
+++ b/test/569-checker-pattern-replacement/src-multidex/Derived.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public final class Derived extends Base {
+ public Derived() {
+ this(0);
+ }
+
+ public Derived(int intValue) {
+ super(intValue);
+ }
+
+ public Derived(String stringValue) {
+ super(stringValue);
+ stringField = null; // Clear field set by Base.<init>(String).
+ }
+
+ public Derived(double doubleValue) {
+ super(doubleValue, null);
+ }
+
+ public Derived(int intValue, double doubleValue, Object objectValue) {
+ super(intValue, doubleValue, objectValue);
+ objectField = null; // Clear field set by Base.<init>(int, double, Object).
+ intField = 0; // Clear field set by Base.<init>(int, double, Object).
+ }
+
+ Derived(int intValue, double doubleValue, Object objectValue, String stringValue) {
+ super(intValue, doubleValue, objectValue, stringValue);
+ // Clearing fields here doesn't help because the superclass constructor must
+ // satisfy the pattern constraints on its own and it doesn't (it has 4 IPUTs).
+ intField = 0;
+ doubleField = 0.0;
+ objectField = null;
+ stringField = null;
+ }
+
+ public Derived(float floatValue) {
+ super();
+ floatField = floatValue;
+ }
+
+ public Derived(int intValue, double doubleValue, Object objectValue, float floatValue) {
+ super(intValue, doubleValue, objectValue);
+ objectField = null; // Clear field set by Base.<init>(int, double, Object).
+ floatField = floatValue;
+ }
+
+ public float floatField;
+}
diff --git a/test/569-checker-pattern-replacement/src-multidex/DerivedInSecondDex.java b/test/569-checker-pattern-replacement/src-multidex/DerivedInSecondDex.java
new file mode 100644
index 0000000..50266e8
--- /dev/null
+++ b/test/569-checker-pattern-replacement/src-multidex/DerivedInSecondDex.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public final class DerivedInSecondDex extends BaseInMainDex {
+ DerivedInSecondDex() {
+ super();
+ }
+
+ DerivedInSecondDex(int intValue) {
+ // Not matched: Superclass in a different dex file has an IPUT.
+ super(intValue);
+ }
+
+ DerivedInSecondDex(long dummy) {
+ // Matched: Superclass in a different dex file has an IPUT that's pruned because we store 0.
+ super(0);
+ }
+}
diff --git a/test/569-checker-pattern-replacement/src-multidex/DerivedWithFinalField.java b/test/569-checker-pattern-replacement/src-multidex/DerivedWithFinalField.java
new file mode 100644
index 0000000..5b39b8a
--- /dev/null
+++ b/test/569-checker-pattern-replacement/src-multidex/DerivedWithFinalField.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public final class DerivedWithFinalField extends BaseWithFinalField {
+ DerivedWithFinalField() {
+ this(0);
+ }
+
+ DerivedWithFinalField(int intValue) {
+ super(intValue);
+ doubleField = 0.0;
+ }
+
+ DerivedWithFinalField(double doubleValue) {
+ super(0);
+ doubleField = doubleValue;
+ }
+
+ DerivedWithFinalField(int intValue, double doubleValue) {
+ super(intValue);
+ doubleField = doubleValue;
+ }
+
+ public final double doubleField;
+}
diff --git a/test/569-checker-pattern-replacement/src/BaseInMainDex.java b/test/569-checker-pattern-replacement/src/BaseInMainDex.java
new file mode 100644
index 0000000..b401540
--- /dev/null
+++ b/test/569-checker-pattern-replacement/src/BaseInMainDex.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class BaseInMainDex {
+ BaseInMainDex() {
+ }
+
+ BaseInMainDex(int intValue) {
+ intField = intValue;
+ }
+
+ public int intField;
+}
diff --git a/test/569-checker-pattern-replacement/src/Main.java b/test/569-checker-pattern-replacement/src/Main.java
index e2d451c..345e9fd 100644
--- a/test/569-checker-pattern-replacement/src/Main.java
+++ b/test/569-checker-pattern-replacement/src/Main.java
@@ -15,368 +15,1210 @@
*/
public class Main {
- /// CHECK-START: void Main.staticNop() inliner (before)
- /// CHECK: InvokeStaticOrDirect
+ /// CHECK-START: void Main.staticNop() inliner (before)
+ /// CHECK: InvokeStaticOrDirect
- /// CHECK-START: void Main.staticNop() inliner (after)
- /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-START: void Main.staticNop() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
- public static void staticNop() {
- Second.staticNop(11);
+ public static void staticNop() {
+ Second.staticNop(11);
+ }
+
+ /// CHECK-START: void Main.nop(Second) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: void Main.nop(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static void nop(Second s) {
+ s.nop();
+ }
+
+ /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (before)
+ /// CHECK-DAG: <<Value:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Ignored:i\d+>> IntConstant 77
+ /// CHECK-DAG: <<ClinitCk:l\d+>> ClinitCheck
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect [<<Ignored>>,<<Value>>{{(,[ij]\d+)?}},<<ClinitCk>>]
+ /// CHECK-DAG: Return [<<Invoke>>]
+
+ /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (after)
+ /// CHECK-DAG: <<Value:l\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Value>>]
+
+ /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static Object staticReturnArg2(String value) {
+ return Second.staticReturnArg2(77, value);
+ }
+
+ /// CHECK-START: long Main.returnArg1(Second, long) inliner (before)
+ /// CHECK-DAG: <<Second:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Value:j\d+>> ParameterValue
+ /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<Second>>]
+ /// CHECK-DAG: <<Invoke:j\d+>> InvokeVirtual [<<NullCk>>,<<Value>>]
+ /// CHECK-DAG: Return [<<Invoke>>]
+
+ /// CHECK-START: long Main.returnArg1(Second, long) inliner (after)
+ /// CHECK-DAG: <<Value:j\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Value>>]
+
+ /// CHECK-START: long Main.returnArg1(Second, long) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static long returnArg1(Second s, long value) {
+ return s.returnArg1(value);
+ }
+
+ /// CHECK-START: int Main.staticReturn9() inliner (before)
+ /// CHECK: {{i\d+}} InvokeStaticOrDirect
+
+ /// CHECK-START: int Main.staticReturn9() inliner (before)
+ /// CHECK-NOT: IntConstant 9
+
+ /// CHECK-START: int Main.staticReturn9() inliner (after)
+ /// CHECK-DAG: <<Const9:i\d+>> IntConstant 9
+ /// CHECK-DAG: Return [<<Const9>>]
+
+ /// CHECK-START: int Main.staticReturn9() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static int staticReturn9() {
+ return Second.staticReturn9();
+ }
+
+ /// CHECK-START: int Main.return7(Second) inliner (before)
+ /// CHECK: {{i\d+}} InvokeVirtual
+
+ /// CHECK-START: int Main.return7(Second) inliner (before)
+ /// CHECK-NOT: IntConstant 7
+
+ /// CHECK-START: int Main.return7(Second) inliner (after)
+ /// CHECK-DAG: <<Const7:i\d+>> IntConstant 7
+ /// CHECK-DAG: Return [<<Const7>>]
+
+ /// CHECK-START: int Main.return7(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static int return7(Second s) {
+ return s.return7(null);
+ }
+
+ /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (before)
+ /// CHECK: {{l\d+}} InvokeStaticOrDirect
+
+ /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (before)
+ /// CHECK-NOT: NullConstant
+
+ /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (after)
+ /// CHECK-DAG: <<Null:l\d+>> NullConstant
+ /// CHECK-DAG: Return [<<Null>>]
+
+ /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static String staticReturnNull() {
+ return Second.staticReturnNull();
+ }
+
+ /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (before)
+ /// CHECK: {{l\d+}} InvokeVirtual
+
+ /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (before)
+ /// CHECK-NOT: NullConstant
+
+ /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (after)
+ /// CHECK-DAG: <<Null:l\d+>> NullConstant
+ /// CHECK-DAG: Return [<<Null>>]
+
+ /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static Object returnNull(Second s) {
+ return s.returnNull();
+ }
+
+ /// CHECK-START: int Main.getInt(Second) inliner (before)
+ /// CHECK: {{i\d+}} InvokeVirtual
+
+ /// CHECK-START: int Main.getInt(Second) inliner (after)
+ /// CHECK: {{i\d+}} InstanceFieldGet
+
+ /// CHECK-START: int Main.getInt(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static int getInt(Second s) {
+ return s.getInstanceIntField();
+ }
+
+ /// CHECK-START: double Main.getDouble(Second) inliner (before)
+ /// CHECK: {{d\d+}} InvokeVirtual
+
+ /// CHECK-START: double Main.getDouble(Second) inliner (after)
+ /// CHECK: {{d\d+}} InstanceFieldGet
+
+ /// CHECK-START: double Main.getDouble(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static double getDouble(Second s) {
+ return s.getInstanceDoubleField(22);
+ }
+
+ /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (before)
+ /// CHECK: {{l\d+}} InvokeVirtual
+
+ /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (after)
+ /// CHECK: {{l\d+}} InstanceFieldGet
+
+ /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static Object getObject(Second s) {
+ return s.getInstanceObjectField(-1L);
+ }
+
+ /// CHECK-START: java.lang.String Main.getString(Second) inliner (before)
+ /// CHECK: {{l\d+}} InvokeVirtual
+
+ /// CHECK-START: java.lang.String Main.getString(Second) inliner (after)
+ /// CHECK: {{l\d+}} InstanceFieldGet
+
+ /// CHECK-START: java.lang.String Main.getString(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static String getString(Second s) {
+ return s.getInstanceStringField(null, "whatever", 1234L);
+ }
+
+ /// CHECK-START: int Main.staticGetInt(Second) inliner (before)
+ /// CHECK: {{i\d+}} InvokeStaticOrDirect
+
+ /// CHECK-START: int Main.staticGetInt(Second) inliner (after)
+ /// CHECK: {{i\d+}} InvokeStaticOrDirect
+
+ /// CHECK-START: int Main.staticGetInt(Second) inliner (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ public static int staticGetInt(Second s) {
+ return Second.staticGetInstanceIntField(s);
+ }
+
+ /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (before)
+ /// CHECK: {{d\d+}} InvokeVirtual
+
+ /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (after)
+ /// CHECK: {{d\d+}} InvokeVirtual
+
+ /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ public static double getDoubleFromParam(Second s) {
+ return s.getInstanceDoubleFieldFromParam(s);
+ }
+
+ /// CHECK-START: int Main.getStaticInt(Second) inliner (before)
+ /// CHECK: {{i\d+}} InvokeVirtual
+
+ /// CHECK-START: int Main.getStaticInt(Second) inliner (after)
+ /// CHECK: {{i\d+}} InvokeVirtual
+
+ /// CHECK-START: int Main.getStaticInt(Second) inliner (after)
+ /// CHECK-NOT: InstanceFieldGet
+ /// CHECK-NOT: StaticFieldGet
+
+ public static int getStaticInt(Second s) {
+ return s.getStaticIntField();
+ }
+
+ /// CHECK-START: long Main.setLong(Second, long) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: long Main.setLong(Second, long) inliner (after)
+ /// CHECK: InstanceFieldSet
+
+ /// CHECK-START: long Main.setLong(Second, long) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static long setLong(Second s, long value) {
+ s.setInstanceLongField(-1, value);
+ return s.instanceLongField;
+ }
+
+ /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (after)
+ /// CHECK-DAG: <<Second:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Value:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<Second>>]
+ /// CHECK-DAG: InstanceFieldSet [<<NullCk>>,<<Value>>]
+ /// CHECK-DAG: <<NullCk2:l\d+>> NullCheck [<<Second>>]
+ /// CHECK-DAG: <<IGet:j\d+>> InstanceFieldGet [<<NullCk2>>]
+ /// CHECK-DAG: <<Conv:j\d+>> TypeConversion [<<Arg2>>]
+ /// CHECK-DAG: <<Add:j\d+>> Add [<<IGet>>,<<Conv>>]
+ /// CHECK-DAG: Return [<<Add>>]
+
+ /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static long setLongReturnArg2(Second s, long value, int arg2) {
+ int result = s.setInstanceLongFieldReturnArg2(value, arg2);
+ return s.instanceLongField + result;
+ }
+
+ /// CHECK-START: long Main.staticSetLong(Second, long) inliner (before)
+ /// CHECK: InvokeStaticOrDirect
+
+ /// CHECK-START: long Main.staticSetLong(Second, long) inliner (after)
+ /// CHECK: InvokeStaticOrDirect
+
+ /// CHECK-START: long Main.staticSetLong(Second, long) inliner (after)
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static long staticSetLong(Second s, long value) {
+ Second.staticSetInstanceLongField(s, value);
+ return s.instanceLongField;
+ }
+
+ /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (after)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (after)
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static long setLongThroughParam(Second s, long value) {
+ s.setInstanceLongFieldThroughParam(s, value);
+ return s.instanceLongField;
+ }
+
+ /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (after)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (after)
+ /// CHECK-NOT: InstanceFieldSet
+ /// CHECK-NOT: StaticFieldSet
+
+ public static float setStaticFloat(Second s, float value) {
+ s.setStaticFloatField(value);
+ return s.staticFloatField;
+ }
+
+ /// CHECK-START: java.lang.Object Main.newObject() inliner (before)
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>{{(,[ij]\d+)?}}] method_name:java.lang.Object.<init>
+
+ /// CHECK-START: java.lang.Object Main.newObject() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static Object newObject() {
+ return new Object();
+ }
+
+ /// CHECK-START: double Main.constructBase() inliner (before)
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>{{(,[ij]\d+)?}}] method_name:Base.<init>
+
+ /// CHECK-START: double Main.constructBase() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructBase() {
+ Base b = new Base();
+ return b.intField + b.doubleField;
+ }
+
+ /// CHECK-START: double Main.constructBase(int) inliner (before)
+ /// CHECK-DAG: <<Value:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:Base.<init>
+
+ /// CHECK-START: double Main.constructBase(int) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+
+ /// CHECK-START: double Main.constructBase(int) inliner (after)
+ /// CHECK-DAG: <<Value:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Value>>]
+
+ /// CHECK-START: double Main.constructBase(int) inliner (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructBase(int intValue) {
+ Base b = new Base(intValue);
+ return b.intField + b.doubleField;
+ }
+
+ /// CHECK-START: double Main.constructBaseWith0() inliner (before)
+ /// CHECK-DAG: <<Value:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:Base.<init>
+
+ /// CHECK-START: double Main.constructBaseWith0() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructBaseWith0() {
+ Base b = new Base(0);
+ return b.intField + b.doubleField;
+ }
+
+ /// CHECK-START: java.lang.String Main.constructBase(java.lang.String) inliner (before)
+ /// CHECK-DAG: <<Value:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:Base.<init>
+
+ /// CHECK-START: java.lang.String Main.constructBase(java.lang.String) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+
+ /// CHECK-START: java.lang.String Main.constructBase(java.lang.String) inliner (after)
+ /// CHECK-DAG: <<Value:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Value>>]
+
+ /// CHECK-START: java.lang.String Main.constructBase(java.lang.String) inliner (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static String constructBase(String stringValue) {
+ Base b = new Base(stringValue);
+ return b.stringField;
+ }
+
+ /// CHECK-START: java.lang.String Main.constructBaseWithNullString() inliner (before)
+ /// CHECK-DAG: <<Null:l\d+>> NullConstant
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Null>>{{(,[ij]\d+)?}}] method_name:Base.<init>
+
+ /// CHECK-START: java.lang.String Main.constructBaseWithNullString() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+
+ /// CHECK-START: java.lang.String Main.constructBaseWithNullString() inliner (after)
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static String constructBaseWithNullString() {
+ String stringValue = null;
+ Base b = new Base(stringValue);
+ return b.stringField;
+ }
+
+ /// CHECK-START: double Main.constructBase(double, java.lang.Object) inliner (before)
+ /// CHECK-DAG: <<DValue:d\d+>> ParameterValue
+ /// CHECK-DAG: <<OValue:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<DValue>>,<<OValue>>{{(,[ij]\d+)?}}] method_name:Base.<init>
+
+ /// CHECK-START: double Main.constructBase(double, java.lang.Object) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+
+ /// CHECK-START: double Main.constructBase(double, java.lang.Object) inliner (after)
+ /// CHECK-DAG: <<DValue:d\d+>> ParameterValue
+ /// CHECK-DAG: <<OValue:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<DValue>>]
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<OValue>>]
+
+ /// CHECK-START: double Main.constructBase(double, java.lang.Object) inliner (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructBase(double doubleValue, Object objectValue) {
+ Base b = new Base(doubleValue, objectValue);
+ return (b.objectField != null) ? b.doubleField : -b.doubleField;
+ }
+
+ /// CHECK-START: double Main.constructBase(int, double, java.lang.Object) inliner (before)
+ /// CHECK-DAG: <<IValue:i\d+>> ParameterValue
+ /// CHECK-DAG: <<DValue:d\d+>> ParameterValue
+ /// CHECK-DAG: <<OValue:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<IValue>>,<<DValue>>,<<OValue>>{{(,[ij]\d+)?}}] method_name:Base.<init>
+
+ /// CHECK-START: double Main.constructBase(int, double, java.lang.Object) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+
+ /// CHECK-START: double Main.constructBase(int, double, java.lang.Object) inliner (after)
+ /// CHECK-DAG: <<IValue:i\d+>> ParameterValue
+ /// CHECK-DAG: <<DValue:d\d+>> ParameterValue
+ /// CHECK-DAG: <<OValue:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<IValue>>]
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<DValue>>]
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<OValue>>]
+
+ /// CHECK-START: double Main.constructBase(int, double, java.lang.Object) inliner (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructBase(int intValue, double doubleValue, Object objectValue) {
+ Base b = new Base(intValue, doubleValue, objectValue);
+ double tmp = b.intField + b.doubleField;
+ return (b.objectField != null) ? tmp : -tmp;
+ }
+
+ /// CHECK-START: double Main.constructBaseWith0DoubleNull(double) inliner (before)
+ /// CHECK-DAG: <<IValue:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<DValue:d\d+>> ParameterValue
+ /// CHECK-DAG: <<OValue:l\d+>> NullConstant
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<IValue>>,<<DValue>>,<<OValue>>{{(,[ij]\d+)?}}] method_name:Base.<init>
+
+ /// CHECK-START: double Main.constructBaseWith0DoubleNull(double) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+
+ /// CHECK-START: double Main.constructBaseWith0DoubleNull(double) inliner (after)
+ /// CHECK-DAG: <<DValue:d\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<DValue>>]
+
+ /// CHECK-START: double Main.constructBaseWith0DoubleNull(double) inliner (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructBaseWith0DoubleNull(double doubleValue) {
+ Base b = new Base(0, doubleValue, null);
+ double tmp = b.intField + b.doubleField;
+ return (b.objectField != null) ? tmp : -tmp;
+ }
+
+ /// CHECK-START: double Main.constructBase(int, double, java.lang.Object, java.lang.String) inliner (before)
+ /// CHECK-DAG: <<IValue:i\d+>> ParameterValue
+ /// CHECK-DAG: <<DValue:d\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<IValue>>,<<DValue>>,{{l\d+}},{{l\d+}}{{(,[ij]\d+)?}}] method_name:Base.<init>
+
+ /// CHECK-START: double Main.constructBase(int, double, java.lang.Object, java.lang.String) inliner (after)
+ /// CHECK-DAG: <<IValue:i\d+>> ParameterValue
+ /// CHECK-DAG: <<DValue:d\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<IValue>>,<<DValue>>,{{l\d+}},{{l\d+}}{{(,[ij]\d+)?}}] method_name:Base.<init>
+
+ /// CHECK-START: double Main.constructBase(int, double, java.lang.Object, java.lang.String) inliner (after)
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructBase(
+ int intValue, double doubleValue, Object objectValue, String stringValue) {
+ Base b = new Base(intValue, doubleValue, objectValue, stringValue);
+ double tmp = b.intField + b.doubleField;
+ tmp = (b.objectField != null) ? tmp : -tmp;
+ return (b.stringField != null) ? 2.0 * tmp : 0.5 * tmp;
+ }
+
+ /// CHECK-START: double Main.constructBase(double) inliner (before)
+ /// CHECK-DAG: <<Value:d\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:Base.<init>
+
+ /// CHECK-START: double Main.constructBase(double) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+
+ /// CHECK-START: double Main.constructBase(double) inliner (after)
+ /// CHECK-DAG: <<Value:d\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Value>>]
+
+ /// CHECK-START: double Main.constructBase(double) inliner (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructBase(double doubleValue) {
+ Base b = new Base(doubleValue);
+ return b.intField + b.doubleField;
+ }
+
+ /// CHECK-START: double Main.constructBaseWith0d() inliner (before)
+ /// CHECK-DAG: <<Value:d\d+>> DoubleConstant
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:Base.<init>
+
+ /// CHECK-START: double Main.constructBaseWith0d() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructBaseWith0d() {
+ Base b = new Base(0.0);
+ return b.intField + b.doubleField;
+ }
+
+ /// CHECK-START: double Main.constructBase(java.lang.Object) inliner (before)
+ /// CHECK-DAG: <<OValue:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<OValue>>{{(,[ij]\d+)?}}] method_name:Base.<init>
+
+ /// CHECK-START: double Main.constructBase(java.lang.Object) inliner (after)
+ /// CHECK-DAG: <<OValue:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<OValue>>{{(,[ij]\d+)?}}] method_name:Base.<init>
+
+ /// CHECK-START: double Main.constructBase(java.lang.Object) inliner (after)
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructBase(Object objectValue) {
+ Base b = new Base(objectValue);
+ double tmp = b.intField + b.doubleField;
+ return (b.objectField != null) ? tmp + 1.0 : tmp - 1.0;
+ }
+
+ /// CHECK-START: double Main.constructBase(int, long) inliner (before)
+ /// CHECK-DAG: <<IValue:i\d+>> ParameterValue
+ /// CHECK-DAG: <<JValue:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<IValue>>,<<JValue>>{{(,[ij]\d+)?}}] method_name:Base.<init>
+
+ /// CHECK-START: double Main.constructBase(int, long) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+
+ /// CHECK-START: double Main.constructBase(int, long) inliner (after)
+ /// CHECK-DAG: <<IValue:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<IValue>>]
+
+ /// CHECK-START: double Main.constructBase(int, long) inliner (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructBase(int intValue, long dummy) {
+ Base b = new Base(intValue, dummy);
+ return b.intField + b.doubleField;
+ }
+
+ /// CHECK-START: double Main.constructDerived() inliner (before)
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>{{(,[ij]\d+)?}}] method_name:Derived.<init>
+
+ /// CHECK-START: double Main.constructDerived() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructDerived() {
+ Derived d = new Derived();
+ return d.intField + d.doubleField;
+ }
+
+ /// CHECK-START: double Main.constructDerived(int) inliner (before)
+ /// CHECK-DAG: <<Value:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:Derived.<init>
+
+ /// CHECK-START: double Main.constructDerived(int) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+
+ /// CHECK-START: double Main.constructDerived(int) inliner (after)
+ /// CHECK-DAG: <<Value:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Value>>]
+
+ /// CHECK-START: double Main.constructDerived(int) inliner (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructDerived(int intValue) {
+ Derived d = new Derived(intValue);
+ return d.intField + d.doubleField;
+ }
+
+ /// CHECK-START: double Main.constructDerivedWith0() inliner (before)
+ /// CHECK-DAG: <<Value:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:Derived.<init>
+
+ /// CHECK-START: double Main.constructDerivedWith0() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructDerivedWith0() {
+ Derived d = new Derived(0);
+ return d.intField + d.doubleField;
+ }
+
+ /// CHECK-START: java.lang.String Main.constructDerived(java.lang.String) inliner (before)
+ /// CHECK-DAG: <<Value:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:Derived.<init>
+
+ /// CHECK-START: java.lang.String Main.constructDerived(java.lang.String) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+
+ /// CHECK-START: java.lang.String Main.constructDerived(java.lang.String) inliner (after)
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static String constructDerived(String stringValue) {
+ Derived d = new Derived(stringValue);
+ return d.stringField;
+ }
+
+ /// CHECK-START: double Main.constructDerived(double) inliner (before)
+ /// CHECK-DAG: <<Value:d\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:Derived.<init>
+
+ /// CHECK-START: double Main.constructDerived(double) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+
+ /// CHECK-START: double Main.constructDerived(double) inliner (after)
+ /// CHECK-DAG: <<Value:d\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Value>>]
+
+ /// CHECK-START: double Main.constructDerived(double) inliner (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructDerived(double doubleValue) {
+ Derived d = new Derived(doubleValue);
+ return d.intField + d.doubleField;
+ }
+
+ /// CHECK-START: double Main.constructDerivedWith0d() inliner (before)
+ /// CHECK-DAG: <<Value:d\d+>> DoubleConstant
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:Derived.<init>
+
+ /// CHECK-START: double Main.constructDerivedWith0d() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructDerivedWith0d() {
+ Derived d = new Derived(0.0);
+ return d.intField + d.doubleField;
+ }
+
+ /// CHECK-START: double Main.constructDerived(int, double, java.lang.Object) inliner (before)
+ /// CHECK-DAG: <<IValue:i\d+>> ParameterValue
+ /// CHECK-DAG: <<DValue:d\d+>> ParameterValue
+ /// CHECK-DAG: <<OValue:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<IValue>>,<<DValue>>,<<OValue>>{{(,[ij]\d+)?}}] method_name:Derived.<init>
+
+ /// CHECK-START: double Main.constructDerived(int, double, java.lang.Object) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+
+ /// CHECK-START: double Main.constructDerived(int, double, java.lang.Object) inliner (after)
+ /// CHECK-DAG: <<DValue:d\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<DValue>>]
+
+ /// CHECK-START: double Main.constructDerived(int, double, java.lang.Object) inliner (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructDerived(int intValue, double doubleValue, Object objectValue) {
+ Derived d = new Derived(intValue, doubleValue, objectValue);
+ double tmp = d.intField + d.doubleField;
+ return (d.objectField != null) ? tmp : -tmp;
+ }
+
+ /// CHECK-START: double Main.constructDerived(int, double, java.lang.Object, java.lang.String) inliner (before)
+ /// CHECK-DAG: <<IValue:i\d+>> ParameterValue
+ /// CHECK-DAG: <<DValue:d\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<IValue>>,<<DValue>>,{{l\d+}},{{l\d+}}{{(,[ij]\d+)?}}] method_name:Derived.<init>
+
+ /// CHECK-START: double Main.constructDerived(int, double, java.lang.Object, java.lang.String) inliner (after)
+ /// CHECK-DAG: <<IValue:i\d+>> ParameterValue
+ /// CHECK-DAG: <<DValue:d\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<IValue>>,<<DValue>>,{{l\d+}},{{l\d+}}{{(,[ij]\d+)?}}] method_name:Derived.<init>
+
+ /// CHECK-START: double Main.constructDerived(int, double, java.lang.Object, java.lang.String) inliner (after)
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructDerived(
+ int intValue, double doubleValue, Object objectValue, String stringValue) {
+ Derived d = new Derived(intValue, doubleValue, objectValue, stringValue);
+ double tmp = d.intField + d.doubleField;
+ tmp = (d.objectField != null) ? tmp : -tmp;
+ return (d.stringField != null) ? 2.0 * tmp : 0.5 * tmp;
+ }
+
+ /// CHECK-START: double Main.constructDerived(float) inliner (before)
+ /// CHECK-DAG: <<Value:f\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:Derived.<init>
+
+ /// CHECK-START: double Main.constructDerived(float) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+
+ /// CHECK-START: double Main.constructDerived(float) inliner (after)
+ /// CHECK-DAG: <<Value:f\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Value>>]
+
+ /// CHECK-START: double Main.constructDerived(float) inliner (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructDerived(float floatValue) {
+ Derived d = new Derived(floatValue);
+ return d.intField + d.doubleField + d.floatField;
+ }
+
+ /// CHECK-START: double Main.constructDerived(int, double, java.lang.Object, float) inliner (before)
+ /// CHECK-DAG: <<IValue:i\d+>> ParameterValue
+ /// CHECK-DAG: <<DValue:d\d+>> ParameterValue
+ /// CHECK-DAG: <<OValue:l\d+>> ParameterValue
+ /// CHECK-DAG: <<FValue:f\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<IValue>>,<<DValue>>,<<OValue>>,<<FValue>>{{(,[ij]\d+)?}}] method_name:Derived.<init>
+
+ /// CHECK-START: double Main.constructDerived(int, double, java.lang.Object, float) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+
+ /// CHECK-START: double Main.constructDerived(int, double, java.lang.Object, float) inliner (after)
+ /// CHECK-DAG: <<IValue:i\d+>> ParameterValue
+ /// CHECK-DAG: <<DValue:d\d+>> ParameterValue
+ /// CHECK-DAG: <<FValue:f\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<IValue>>]
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<DValue>>]
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<FValue>>]
+
+ /// CHECK-START: double Main.constructDerived(int, double, java.lang.Object, float) inliner (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructDerived(
+ int intValue, double doubleValue, Object objectValue, float floatValue) {
+ Derived d = new Derived(intValue, doubleValue, objectValue, floatValue);
+ double tmp = d.intField + d.doubleField + d.floatField;
+ return (d.objectField != null) ? tmp : -tmp;
+ }
+
+ /// CHECK-START: int Main.constructBaseWithFinalField() inliner (before)
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>{{(,[ij]\d+)?}}] method_name:BaseWithFinalField.<init>
+
+ /// CHECK-START: int Main.constructBaseWithFinalField() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static int constructBaseWithFinalField() {
+ BaseWithFinalField b = new BaseWithFinalField();
+ return b.intField;
+ }
+
+ /// CHECK-START: int Main.constructBaseWithFinalField(int) inliner (before)
+ /// CHECK-DAG: <<Value:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:BaseWithFinalField.<init>
+
+ /// CHECK-START: int Main.constructBaseWithFinalField(int) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ /// CHECK-START: int Main.constructBaseWithFinalField(int) inliner (after)
+ /// CHECK-DAG: <<Value:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Value>>]
+ /// CHECK-DAG: MemoryBarrier
+
+ /// CHECK-START: int Main.constructBaseWithFinalField(int) inliner (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static int constructBaseWithFinalField(int intValue) {
+ BaseWithFinalField b = new BaseWithFinalField(intValue);
+ return b.intField;
+ }
+
+ /// CHECK-START: int Main.constructBaseWithFinalFieldWith0() inliner (before)
+ /// CHECK-DAG: <<Value:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:BaseWithFinalField.<init>
+
+ /// CHECK-START: int Main.constructBaseWithFinalFieldWith0() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static int constructBaseWithFinalFieldWith0() {
+ BaseWithFinalField b = new BaseWithFinalField(0);
+ return b.intField;
+ }
+
+ /// CHECK-START: double Main.constructDerivedWithFinalField() inliner (before)
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>{{(,[ij]\d+)?}}] method_name:DerivedWithFinalField.<init>
+
+ /// CHECK-START: double Main.constructDerivedWithFinalField() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructDerivedWithFinalField() {
+ DerivedWithFinalField d = new DerivedWithFinalField();
+ return d.intField + d.doubleField;
+ }
+
+ /// CHECK-START: double Main.constructDerivedWithFinalField(int) inliner (before)
+ /// CHECK-DAG: <<Value:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:DerivedWithFinalField.<init>
+
+ /// CHECK-START: double Main.constructDerivedWithFinalField(int) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ /// CHECK-START: double Main.constructDerivedWithFinalField(int) inliner (after)
+ /// CHECK-DAG: <<Value:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Value>>]
+ /// CHECK-DAG: MemoryBarrier
+
+ /// CHECK-START: double Main.constructDerivedWithFinalField(int) inliner (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructDerivedWithFinalField(int intValue) {
+ DerivedWithFinalField d = new DerivedWithFinalField(intValue);
+ return d.intField + d.doubleField;
+ }
+
+ /// CHECK-START: double Main.constructDerivedWithFinalFieldWith0() inliner (before)
+ /// CHECK-DAG: <<Value:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:DerivedWithFinalField.<init>
+
+ /// CHECK-START: double Main.constructDerivedWithFinalFieldWith0() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructDerivedWithFinalFieldWith0() {
+ DerivedWithFinalField d = new DerivedWithFinalField(0);
+ return d.intField + d.doubleField;
+ }
+
+ /// CHECK-START: double Main.constructDerivedWithFinalField(double) inliner (before)
+ /// CHECK-DAG: <<Value:d\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:DerivedWithFinalField.<init>
+
+ /// CHECK-START: double Main.constructDerivedWithFinalField(double) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ /// CHECK-START: double Main.constructDerivedWithFinalField(double) inliner (after)
+ /// CHECK-DAG: <<Value:d\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Value>>]
+ /// CHECK-DAG: MemoryBarrier
+
+ /// CHECK-START: double Main.constructDerivedWithFinalField(double) inliner (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructDerivedWithFinalField(double doubleValue) {
+ DerivedWithFinalField d = new DerivedWithFinalField(doubleValue);
+ return d.intField + d.doubleField;
+ }
+
+ /// CHECK-START: double Main.constructDerivedWithFinalFieldWith0d() inliner (before)
+ /// CHECK-DAG: <<Value:d\d+>> DoubleConstant
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:DerivedWithFinalField.<init>
+
+ /// CHECK-START: double Main.constructDerivedWithFinalFieldWith0d() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructDerivedWithFinalFieldWith0d() {
+ DerivedWithFinalField d = new DerivedWithFinalField(0.0);
+ return d.intField + d.doubleField;
+ }
+
+ /// CHECK-START: double Main.constructDerivedWithFinalField(int, double) inliner (before)
+ /// CHECK-DAG: <<IValue:i\d+>> ParameterValue
+ /// CHECK-DAG: <<DValue:d\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<IValue>>,<<DValue>>{{(,[ij]\d+)?}}] method_name:DerivedWithFinalField.<init>
+
+ /// CHECK-START: double Main.constructDerivedWithFinalField(int, double) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ /// CHECK-START: double Main.constructDerivedWithFinalField(int, double) inliner (after)
+ /// CHECK-DAG: <<Value:d\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ /// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Value>>]
+ /// CHECK-DAG: MemoryBarrier
+
+ /// CHECK-START: double Main.constructDerivedWithFinalField(int, double) inliner (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ /// CHECK-START: double Main.constructDerivedWithFinalField(int, double) inliner (after)
+ /// CHECK-DAG: MemoryBarrier
+ /// CHECK-NOT: MemoryBarrier
+
+ public static double constructDerivedWithFinalField(int intValue, double doubleValue) {
+ DerivedWithFinalField d = new DerivedWithFinalField(intValue, doubleValue);
+ return d.intField + d.doubleField;
+ }
+
+ /// CHECK-START: double Main.constructDerivedWithFinalFieldWith0And0d() inliner (before)
+ /// CHECK-DAG: <<IValue:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<DValue:d\d+>> DoubleConstant
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<IValue>>,<<DValue>>{{(,[ij]\d+)?}}] method_name:DerivedWithFinalField.<init>
+
+ /// CHECK-START: double Main.constructDerivedWithFinalFieldWith0And0d() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static double constructDerivedWithFinalFieldWith0And0d() {
+ DerivedWithFinalField d = new DerivedWithFinalField(0, 0.0);
+ return d.intField + d.doubleField;
+ }
+
+ /// CHECK-START: int Main.constructDerivedInSecondDex() inliner (before)
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>{{(,[ij]\d+)?}}] method_name:DerivedInSecondDex.<init>
+
+ /// CHECK-START: int Main.constructDerivedInSecondDex() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static int constructDerivedInSecondDex() {
+ DerivedInSecondDex d = new DerivedInSecondDex();
+ return d.intField;
+ }
+
+ /// CHECK-START: int Main.constructDerivedInSecondDex(int) inliner (before)
+ /// CHECK-DAG: <<Value:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:DerivedInSecondDex.<init>
+
+ /// CHECK-START: int Main.constructDerivedInSecondDex(int) inliner (after)
+ /// CHECK-DAG: <<Value:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:DerivedInSecondDex.<init>
+
+ /// CHECK-START: int Main.constructDerivedInSecondDex(int) inliner (after)
+ /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static int constructDerivedInSecondDex(int intValue) {
+ DerivedInSecondDex d = new DerivedInSecondDex(intValue);
+ return d.intField;
+ }
+
+ /// CHECK-START: int Main.constructDerivedInSecondDexWith0() inliner (before)
+ /// CHECK-DAG: <<Value:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:DerivedInSecondDex.<init>
+
+ /// CHECK-START: int Main.constructDerivedInSecondDexWith0() inliner (after)
+ /// CHECK-DAG: <<Value:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:DerivedInSecondDex.<init>
+
+ /// CHECK-START: int Main.constructDerivedInSecondDexWith0() inliner (after)
+ /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static int constructDerivedInSecondDexWith0() {
+ DerivedInSecondDex d = new DerivedInSecondDex(0);
+ return d.intField;
+ }
+
+ /// CHECK-START: int Main.constructDerivedInSecondDex(long) inliner (before)
+ /// CHECK-DAG: <<Value:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>,<<Value>>{{(,[ij]\d+)?}}] method_name:DerivedInSecondDex.<init>
+
+ /// CHECK-START: int Main.constructDerivedInSecondDex(long) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: MemoryBarrier
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static int constructDerivedInSecondDex(long dummy) {
+ DerivedInSecondDex d = new DerivedInSecondDex(dummy);
+ return d.intField;
+ }
+
+ public static void main(String[] args) throws Exception {
+ Second s = new Second();
+
+ // Replaced NOP pattern.
+ staticNop();
+ nop(s);
+ // Replaced "return arg" pattern.
+ assertEquals("arbitrary string", staticReturnArg2("arbitrary string"));
+ assertEquals(4321L, returnArg1(s, 4321L));
+ // Replaced "return const" pattern.
+ assertEquals(9, staticReturn9());
+ assertEquals(7, return7(s));
+ assertEquals(null, staticReturnNull());
+ assertEquals(null, returnNull(s));
+ // Replaced IGET pattern.
+ assertEquals(42, getInt(s));
+ assertEquals(-42.0, getDouble(s));
+ assertEquals(null, getObject(s));
+ assertEquals("dummy", getString(s));
+ // Not replaced IGET pattern.
+ assertEquals(42, staticGetInt(s));
+ assertEquals(-42.0, getDoubleFromParam(s));
+ // SGET.
+ assertEquals(4242, getStaticInt(s));
+ // Replaced IPUT pattern.
+ assertEquals(111L, setLong(s, 111L));
+ assertEquals(345L, setLongReturnArg2(s, 222L, 123));
+ // Not replaced IPUT pattern.
+ assertEquals(222L, staticSetLong(s, 222L));
+ assertEquals(333L, setLongThroughParam(s, 333L));
+ // SPUT.
+ assertEquals(-11.5f, setStaticFloat(s, -11.5f));
+
+ if (newObject() == null) {
+ throw new AssertionError("new Object() cannot be null.");
}
- /// CHECK-START: void Main.nop(Second) inliner (before)
- /// CHECK: InvokeVirtual
+ assertEquals(0.0, constructBase());
+ assertEquals(42.0, constructBase(42));
+ assertEquals(0.0, constructBaseWith0());
+ assertEquals("something", constructBase("something"));
+ assertEquals(null, constructBaseWithNullString());
+ assertEquals(11.0, constructBase(11.0, new Object()));
+ assertEquals(-12.0, constructBase(12.0, null));
+ assertEquals(30.0, constructBase(17, 13.0, new Object()));
+ assertEquals(-34.0, constructBase(19, 15.0, null));
+ assertEquals(-22.5, constructBaseWith0DoubleNull(22.5));
+ assertEquals(-8.0, constructBase(2, 14.0, null, null));
+ assertEquals(-64.0, constructBase(4, 28.0, null, "dummy"));
+ assertEquals(13.0, constructBase(24, 2.0, new Object(), null));
+ assertEquals(30.0, constructBase(11, 4.0, new Object(), "dummy"));
+ assertEquals(43.0, constructBase(43.0));
+ assertEquals(0.0, constructBaseWith0d());
+ assertEquals(1.0, constructBase(new Object()));
+ assertEquals(-1.0, constructBase((Object) null));
+ assertEquals(123.0, constructBase(123, 65L));
- /// CHECK-START: void Main.nop(Second) inliner (after)
- /// CHECK-NOT: InvokeVirtual
+ assertEquals(0.0, constructDerived());
+ assertEquals(73.0, constructDerived(73));
+ assertEquals(0.0, constructDerivedWith0());
+ assertEquals(null, constructDerived("something else"));
+ assertEquals(18.0, constructDerived(18.0));
+ assertEquals(0.0, constructDerivedWith0d());
+ assertEquals(-7.0, constructDerived(5, 7.0, new Object()));
+ assertEquals(-4.0, constructDerived(9, 4.0, null));
+ assertEquals(0.0, constructDerived(1, 9.0, null, null));
+ assertEquals(0.0, constructDerived(2, 8.0, null, "dummy"));
+ assertEquals(0.0, constructDerived(3, 7.0, new Object(), null));
+ assertEquals(0.0, constructDerived(4, 6.0, new Object(), "dummy"));
+ assertEquals(17.0, constructDerived(17.0f));
+ assertEquals(-5.5, constructDerived(6, -7.0, new Object(), 6.5f));
- public static void nop(Second s) {
- s.nop();
+ assertEquals(0, constructBaseWithFinalField());
+ assertEquals(77, constructBaseWithFinalField(77));
+ assertEquals(0, constructBaseWithFinalFieldWith0());
+ assertEquals(0.0, constructDerivedWithFinalField());
+ assertEquals(-33.0, constructDerivedWithFinalField(-33));
+ assertEquals(0.0, constructDerivedWithFinalFieldWith0());
+ assertEquals(-44.0, constructDerivedWithFinalField(-44.0));
+ assertEquals(0.0, constructDerivedWithFinalFieldWith0d());
+ assertEquals(88, constructDerivedWithFinalField(22, 66.0));
+ assertEquals(0.0, constructDerivedWithFinalFieldWith0And0d());
+
+ assertEquals(0, constructDerivedInSecondDex());
+ assertEquals(123, constructDerivedInSecondDex(123));
+ assertEquals(0, constructDerivedInSecondDexWith0());
+ assertEquals(0, constructDerivedInSecondDex(7L));
+ }
+
+ private static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new AssertionError("Wrong result: " + expected + " != " + actual);
}
+ }
- /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (before)
- /// CHECK-DAG: <<Value:l\d+>> ParameterValue
- /// CHECK-DAG: <<Ignored:i\d+>> IntConstant 77
- /// CHECK-DAG: <<ClinitCk:l\d+>> ClinitCheck
- // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
- /// CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect [<<Ignored>>,<<Value>>{{(,[ij]\d+)?}},<<ClinitCk>>]
- /// CHECK-DAG: Return [<<Invoke>>]
-
- /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (after)
- /// CHECK-DAG: <<Value:l\d+>> ParameterValue
- /// CHECK-DAG: Return [<<Value>>]
-
- /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (after)
- /// CHECK-NOT: InvokeStaticOrDirect
-
- public static Object staticReturnArg2(String value) {
- return Second.staticReturnArg2(77, value);
+ private static void assertEquals(double expected, double actual) {
+ if (expected != actual) {
+ throw new AssertionError("Wrong result: " + expected + " != " + actual);
}
+ }
- /// CHECK-START: long Main.returnArg1(Second, long) inliner (before)
- /// CHECK-DAG: <<Second:l\d+>> ParameterValue
- /// CHECK-DAG: <<Value:j\d+>> ParameterValue
- /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<Second>>]
- /// CHECK-DAG: <<Invoke:j\d+>> InvokeVirtual [<<NullCk>>,<<Value>>]
- /// CHECK-DAG: Return [<<Invoke>>]
-
- /// CHECK-START: long Main.returnArg1(Second, long) inliner (after)
- /// CHECK-DAG: <<Value:j\d+>> ParameterValue
- /// CHECK-DAG: Return [<<Value>>]
-
- /// CHECK-START: long Main.returnArg1(Second, long) inliner (after)
- /// CHECK-NOT: InvokeVirtual
-
- public static long returnArg1(Second s, long value) {
- return s.returnArg1(value);
+ private static void assertEquals(Object expected, Object actual) {
+ if (expected != actual && (expected == null || !expected.equals(actual))) {
+ throw new AssertionError("Wrong result: " + expected + " != " + actual);
}
-
- /// CHECK-START: int Main.staticReturn9() inliner (before)
- /// CHECK: {{i\d+}} InvokeStaticOrDirect
-
- /// CHECK-START: int Main.staticReturn9() inliner (before)
- /// CHECK-NOT: IntConstant 9
-
- /// CHECK-START: int Main.staticReturn9() inliner (after)
- /// CHECK-DAG: <<Const9:i\d+>> IntConstant 9
- /// CHECK-DAG: Return [<<Const9>>]
-
- /// CHECK-START: int Main.staticReturn9() inliner (after)
- /// CHECK-NOT: InvokeStaticOrDirect
-
- public static int staticReturn9() {
- return Second.staticReturn9();
- }
-
- /// CHECK-START: int Main.return7(Second) inliner (before)
- /// CHECK: {{i\d+}} InvokeVirtual
-
- /// CHECK-START: int Main.return7(Second) inliner (before)
- /// CHECK-NOT: IntConstant 7
-
- /// CHECK-START: int Main.return7(Second) inliner (after)
- /// CHECK-DAG: <<Const7:i\d+>> IntConstant 7
- /// CHECK-DAG: Return [<<Const7>>]
-
- /// CHECK-START: int Main.return7(Second) inliner (after)
- /// CHECK-NOT: InvokeVirtual
-
- public static int return7(Second s) {
- return s.return7(null);
- }
-
- /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (before)
- /// CHECK: {{l\d+}} InvokeStaticOrDirect
-
- /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (before)
- /// CHECK-NOT: NullConstant
-
- /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (after)
- /// CHECK-DAG: <<Null:l\d+>> NullConstant
- /// CHECK-DAG: Return [<<Null>>]
-
- /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (after)
- /// CHECK-NOT: InvokeStaticOrDirect
-
- public static String staticReturnNull() {
- return Second.staticReturnNull();
- }
-
- /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (before)
- /// CHECK: {{l\d+}} InvokeVirtual
-
- /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (before)
- /// CHECK-NOT: NullConstant
-
- /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (after)
- /// CHECK-DAG: <<Null:l\d+>> NullConstant
- /// CHECK-DAG: Return [<<Null>>]
-
- /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (after)
- /// CHECK-NOT: InvokeVirtual
-
- public static Object returnNull(Second s) {
- return s.returnNull();
- }
-
- /// CHECK-START: int Main.getInt(Second) inliner (before)
- /// CHECK: {{i\d+}} InvokeVirtual
-
- /// CHECK-START: int Main.getInt(Second) inliner (after)
- /// CHECK: {{i\d+}} InstanceFieldGet
-
- /// CHECK-START: int Main.getInt(Second) inliner (after)
- /// CHECK-NOT: InvokeVirtual
-
- public static int getInt(Second s) {
- return s.getInstanceIntField();
- }
-
- /// CHECK-START: double Main.getDouble(Second) inliner (before)
- /// CHECK: {{d\d+}} InvokeVirtual
-
- /// CHECK-START: double Main.getDouble(Second) inliner (after)
- /// CHECK: {{d\d+}} InstanceFieldGet
-
- /// CHECK-START: double Main.getDouble(Second) inliner (after)
- /// CHECK-NOT: InvokeVirtual
-
- public static double getDouble(Second s) {
- return s.getInstanceDoubleField(22);
- }
-
- /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (before)
- /// CHECK: {{l\d+}} InvokeVirtual
-
- /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (after)
- /// CHECK: {{l\d+}} InstanceFieldGet
-
- /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (after)
- /// CHECK-NOT: InvokeVirtual
-
- public static Object getObject(Second s) {
- return s.getInstanceObjectField(-1L);
- }
-
- /// CHECK-START: java.lang.String Main.getString(Second) inliner (before)
- /// CHECK: {{l\d+}} InvokeVirtual
-
- /// CHECK-START: java.lang.String Main.getString(Second) inliner (after)
- /// CHECK: {{l\d+}} InstanceFieldGet
-
- /// CHECK-START: java.lang.String Main.getString(Second) inliner (after)
- /// CHECK-NOT: InvokeVirtual
-
- public static String getString(Second s) {
- return s.getInstanceStringField(null, "whatever", 1234L);
- }
-
- /// CHECK-START: int Main.staticGetInt(Second) inliner (before)
- /// CHECK: {{i\d+}} InvokeStaticOrDirect
-
- /// CHECK-START: int Main.staticGetInt(Second) inliner (after)
- /// CHECK: {{i\d+}} InvokeStaticOrDirect
-
- /// CHECK-START: int Main.staticGetInt(Second) inliner (after)
- /// CHECK-NOT: InstanceFieldGet
-
- public static int staticGetInt(Second s) {
- return Second.staticGetInstanceIntField(s);
- }
-
- /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (before)
- /// CHECK: {{d\d+}} InvokeVirtual
-
- /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (after)
- /// CHECK: {{d\d+}} InvokeVirtual
-
- /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (after)
- /// CHECK-NOT: InstanceFieldGet
-
- public static double getDoubleFromParam(Second s) {
- return s.getInstanceDoubleFieldFromParam(s);
- }
-
- /// CHECK-START: int Main.getStaticInt(Second) inliner (before)
- /// CHECK: {{i\d+}} InvokeVirtual
-
- /// CHECK-START: int Main.getStaticInt(Second) inliner (after)
- /// CHECK: {{i\d+}} InvokeVirtual
-
- /// CHECK-START: int Main.getStaticInt(Second) inliner (after)
- /// CHECK-NOT: InstanceFieldGet
- /// CHECK-NOT: StaticFieldGet
-
- public static int getStaticInt(Second s) {
- return s.getStaticIntField();
- }
-
- /// CHECK-START: long Main.setLong(Second, long) inliner (before)
- /// CHECK: InvokeVirtual
-
- /// CHECK-START: long Main.setLong(Second, long) inliner (after)
- /// CHECK: InstanceFieldSet
-
- /// CHECK-START: long Main.setLong(Second, long) inliner (after)
- /// CHECK-NOT: InvokeVirtual
-
- public static long setLong(Second s, long value) {
- s.setInstanceLongField(-1, value);
- return s.instanceLongField;
- }
-
- /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (before)
- /// CHECK: InvokeVirtual
-
- /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (after)
- /// CHECK-DAG: <<Second:l\d+>> ParameterValue
- /// CHECK-DAG: <<Value:j\d+>> ParameterValue
- /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
- /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<Second>>]
- /// CHECK-DAG: InstanceFieldSet [<<NullCk>>,<<Value>>]
- /// CHECK-DAG: <<NullCk2:l\d+>> NullCheck [<<Second>>]
- /// CHECK-DAG: <<IGet:j\d+>> InstanceFieldGet [<<NullCk2>>]
- /// CHECK-DAG: <<Conv:j\d+>> TypeConversion [<<Arg2>>]
- /// CHECK-DAG: <<Add:j\d+>> Add [<<IGet>>,<<Conv>>]
- /// CHECK-DAG: Return [<<Add>>]
-
- /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (after)
- /// CHECK-NOT: InvokeVirtual
-
- public static long setLongReturnArg2(Second s, long value, int arg2) {
- int result = s.setInstanceLongFieldReturnArg2(value, arg2);
- return s.instanceLongField + result;
- }
-
- /// CHECK-START: long Main.staticSetLong(Second, long) inliner (before)
- /// CHECK: InvokeStaticOrDirect
-
- /// CHECK-START: long Main.staticSetLong(Second, long) inliner (after)
- /// CHECK: InvokeStaticOrDirect
-
- /// CHECK-START: long Main.staticSetLong(Second, long) inliner (after)
- /// CHECK-NOT: InstanceFieldSet
-
- public static long staticSetLong(Second s, long value) {
- Second.staticSetInstanceLongField(s, value);
- return s.instanceLongField;
- }
-
- /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (before)
- /// CHECK: InvokeVirtual
-
- /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (after)
- /// CHECK: InvokeVirtual
-
- /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (after)
- /// CHECK-NOT: InstanceFieldSet
-
- public static long setLongThroughParam(Second s, long value) {
- s.setInstanceLongFieldThroughParam(s, value);
- return s.instanceLongField;
- }
-
- /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (before)
- /// CHECK: InvokeVirtual
-
- /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (after)
- /// CHECK: InvokeVirtual
-
- /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (after)
- /// CHECK-NOT: InstanceFieldSet
- /// CHECK-NOT: StaticFieldSet
-
- public static float setStaticFloat(Second s, float value) {
- s.setStaticFloatField(value);
- return s.staticFloatField;
- }
-
- /// CHECK-START: java.lang.Object Main.newObject() inliner (before)
- /// CHECK-DAG: <<Obj:l\d+>> NewInstance
- // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
- /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>{{(,[ij]\d+)?}}] method_name:java.lang.Object.<init>
-
- /// CHECK-START: java.lang.Object Main.newObject() inliner (after)
- /// CHECK-NOT: InvokeStaticOrDirect
-
- public static Object newObject() {
- return new Object();
- }
-
- public static void main(String[] args) throws Exception {
- Second s = new Second();
-
- // Replaced NOP pattern.
- staticNop();
- nop(s);
- // Replaced "return arg" pattern.
- assertEquals("arbitrary string", staticReturnArg2("arbitrary string"));
- assertEquals(4321L, returnArg1(s, 4321L));
- // Replaced "return const" pattern.
- assertEquals(9, staticReturn9());
- assertEquals(7, return7(s));
- assertEquals(null, staticReturnNull());
- assertEquals(null, returnNull(s));
- // Replaced IGET pattern.
- assertEquals(42, getInt(s));
- assertEquals(-42.0, getDouble(s));
- assertEquals(null, getObject(s));
- assertEquals("dummy", getString(s));
- // Not replaced IGET pattern.
- assertEquals(42, staticGetInt(s));
- assertEquals(-42.0, getDoubleFromParam(s));
- // SGET.
- assertEquals(4242, getStaticInt(s));
- // Replaced IPUT pattern.
- assertEquals(111L, setLong(s, 111L));
- assertEquals(345L, setLongReturnArg2(s, 222L, 123));
- // Not replaced IPUT pattern.
- assertEquals(222L, staticSetLong(s, 222L));
- assertEquals(333L, setLongThroughParam(s, 333L));
- // SPUT.
- assertEquals(-11.5f, setStaticFloat(s, -11.5f));
-
- if (newObject() == null) {
- throw new AssertionError("new Object() cannot be null.");
- }
- }
-
- private static void assertEquals(int expected, int actual) {
- if (expected != actual) {
- throw new AssertionError("Wrong result: " + expected + " != " + actual);
- }
- }
-
- private static void assertEquals(double expected, double actual) {
- if (expected != actual) {
- throw new AssertionError("Wrong result: " + expected + " != " + actual);
- }
- }
-
- private static void assertEquals(Object expected, Object actual) {
- if (expected != actual && (expected == null || !expected.equals(actual))) {
- throw new AssertionError("Wrong result: " + expected + " != " + actual);
- }
- }
+ }
}
diff --git a/test/570-checker-osr/expected.txt b/test/570-checker-osr/expected.txt
new file mode 100644
index 0000000..25fb220
--- /dev/null
+++ b/test/570-checker-osr/expected.txt
@@ -0,0 +1,5 @@
+JNI_OnLoad called
+100000
+200000
+300000
+400000
diff --git a/test/570-checker-osr/info.txt b/test/570-checker-osr/info.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/570-checker-osr/info.txt
diff --git a/test/570-checker-osr/osr.cc b/test/570-checker-osr/osr.cc
new file mode 100644
index 0000000..4c58b39
--- /dev/null
+++ b/test/570-checker-osr/osr.cc
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "art_method.h"
+#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
+#include "jit/profiling_info.h"
+#include "oat_quick_method_header.h"
+#include "scoped_thread_state_change.h"
+#include "stack_map.h"
+
+namespace art {
+
+class OsrVisitor : public StackVisitor {
+ public:
+ explicit OsrVisitor(Thread* thread)
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ in_osr_method_(false),
+ in_interpreter_(false) {}
+
+ bool VisitFrame() SHARED_REQUIRES(Locks::mutator_lock_) {
+ ArtMethod* m = GetMethod();
+ std::string m_name(m->GetName());
+
+ if ((m_name.compare("$noinline$returnInt") == 0) ||
+ (m_name.compare("$noinline$returnFloat") == 0) ||
+ (m_name.compare("$noinline$returnDouble") == 0) ||
+ (m_name.compare("$noinline$returnLong") == 0) ||
+ (m_name.compare("$noinline$deopt") == 0) ||
+ (m_name.compare("$noinline$inlineCache") == 0)) {
+ const OatQuickMethodHeader* header =
+ Runtime::Current()->GetJit()->GetCodeCache()->LookupOsrMethodHeader(m);
+ if (header != nullptr && header == GetCurrentOatQuickMethodHeader()) {
+ in_osr_method_ = true;
+ } else if (IsCurrentFrameInInterpreter()) {
+ in_interpreter_ = true;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ bool in_osr_method_;
+ bool in_interpreter_;
+};
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_ensureInOsrCode(JNIEnv*, jclass) {
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit == nullptr) {
+ // Just return true for non-jit configurations to stop the infinite loop.
+ return JNI_TRUE;
+ }
+ ScopedObjectAccess soa(Thread::Current());
+ OsrVisitor visitor(soa.Self());
+ visitor.WalkStack();
+ return visitor.in_osr_method_;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_ensureInInterpreter(JNIEnv*, jclass) {
+ if (!Runtime::Current()->UseJit()) {
+ // The return value is irrelevant if we're not using JIT.
+ return false;
+ }
+ ScopedObjectAccess soa(Thread::Current());
+ OsrVisitor visitor(soa.Self());
+ visitor.WalkStack();
+ return visitor.in_interpreter_;
+}
+
+class ProfilingInfoVisitor : public StackVisitor {
+ public:
+ explicit ProfilingInfoVisitor(Thread* thread)
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames) {}
+
+ bool VisitFrame() SHARED_REQUIRES(Locks::mutator_lock_) {
+ ArtMethod* m = GetMethod();
+ std::string m_name(m->GetName());
+
+ if (m_name.compare("$noinline$inlineCache") == 0) {
+ ProfilingInfo::Create(Thread::Current(), m, /* retry_allocation */ true);
+ return false;
+ }
+ return true;
+ }
+};
+
+extern "C" JNIEXPORT void JNICALL Java_Main_ensureHasProfilingInfo(JNIEnv*, jclass) {
+ if (!Runtime::Current()->UseJit()) {
+ return;
+ }
+ ScopedObjectAccess soa(Thread::Current());
+ ProfilingInfoVisitor visitor(soa.Self());
+ visitor.WalkStack();
+}
+
+class OsrCheckVisitor : public StackVisitor {
+ public:
+ explicit OsrCheckVisitor(Thread* thread)
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames) {}
+
+ bool VisitFrame() SHARED_REQUIRES(Locks::mutator_lock_) {
+ ArtMethod* m = GetMethod();
+ std::string m_name(m->GetName());
+
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (m_name.compare("$noinline$inlineCache") == 0 && jit != nullptr) {
+ while (jit->GetCodeCache()->LookupOsrMethodHeader(m) == nullptr) {
+ // Sleep to yield to the compiler thread.
+ sleep(0);
+ // Will either ensure it's compiled or do the compilation itself.
+ jit->CompileMethod(m, Thread::Current(), /* osr */ true);
+ }
+ return false;
+ }
+ return true;
+ }
+};
+
+extern "C" JNIEXPORT void JNICALL Java_Main_ensureHasOsrCode(JNIEnv*, jclass) {
+ if (!Runtime::Current()->UseJit()) {
+ return;
+ }
+ ScopedObjectAccess soa(Thread::Current());
+ OsrCheckVisitor visitor(soa.Self());
+ visitor.WalkStack();
+}
+
+} // namespace art
diff --git a/test/570-checker-osr/smali/Osr.smali b/test/570-checker-osr/smali/Osr.smali
new file mode 100644
index 0000000..869c7c3
--- /dev/null
+++ b/test/570-checker-osr/smali/Osr.smali
@@ -0,0 +1,35 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LOsr;
+
+.super Ljava/lang/Object;
+
+# Check that blocks only havig nops are not merged when they are loop headers.
+# This ensures we can do on-stack replacement for branches to those nop blocks.
+
+## CHECK-START: int Osr.simpleLoop(int, int) dead_code_elimination_final (after)
+## CHECK-DAG: SuspendCheck loop:<<OuterLoop:B\d+>> outer_loop:none
+## CHECK-DAG: SuspendCheck loop:{{B\d+}} outer_loop:<<OuterLoop>>
+.method public static simpleLoop(II)I
+ .registers 3
+ const/16 v0, 0
+ :nop_entry
+ nop
+ :loop_entry
+ add-int v0, v0, v0
+ if-eq v0, v1, :loop_entry
+ if-eq v0, v2, :nop_entry
+ return v0
+.end method
diff --git a/test/570-checker-osr/src/DeoptimizationController.java b/test/570-checker-osr/src/DeoptimizationController.java
new file mode 100644
index 0000000..907d133
--- /dev/null
+++ b/test/570-checker-osr/src/DeoptimizationController.java
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+// This file is a copy of 802-deoptimization/src/DeoptimizationController.java
+// because run-test requires standalone individual test.
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+/**
+ * Controls deoptimization using dalvik.system.VMDebug class.
+ */
+public class DeoptimizationController {
+ private static final String TEMP_FILE_NAME_PREFIX = "test";
+ private static final String TEMP_FILE_NAME_SUFFIX = ".trace";
+
+ private static File createTempFile() throws Exception {
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e) {
+ System.setProperty("java.io.tmpdir", "/data/local/tmp");
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e2) {
+ System.setProperty("java.io.tmpdir", "/sdcard");
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ }
+ }
+ }
+
+ public static void startDeoptimization() {
+ File tempFile = null;
+ try {
+ tempFile = createTempFile();
+ String tempFileName = tempFile.getPath();
+
+ VMDebug.startMethodTracing(tempFileName, 0, 0, false, 1000);
+ if (VMDebug.getMethodTracingMode() == 0) {
+ throw new IllegalStateException("Not tracing.");
+ }
+ } catch (Exception exc) {
+ exc.printStackTrace(System.err);
+ } finally {
+ if (tempFile != null) {
+ tempFile.delete();
+ }
+ }
+ }
+
+ public static void stopDeoptimization() {
+ try {
+ VMDebug.stopMethodTracing();
+ if (VMDebug.getMethodTracingMode() != 0) {
+ throw new IllegalStateException("Still tracing.");
+ }
+ } catch (Exception exc) {
+ exc.printStackTrace(System.err);
+ }
+ }
+
+ private static class VMDebug {
+ private static final Method startMethodTracingMethod;
+ private static final Method stopMethodTracingMethod;
+ private static final Method getMethodTracingModeMethod;
+
+ static {
+ try {
+ Class<?> c = Class.forName("dalvik.system.VMDebug");
+ startMethodTracingMethod = c.getDeclaredMethod("startMethodTracing", String.class,
+ Integer.TYPE, Integer.TYPE, Boolean.TYPE, Integer.TYPE);
+ stopMethodTracingMethod = c.getDeclaredMethod("stopMethodTracing");
+ getMethodTracingModeMethod = c.getDeclaredMethod("getMethodTracingMode");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void startMethodTracing(String filename, int bufferSize, int flags,
+ boolean samplingEnabled, int intervalUs) throws Exception {
+ startMethodTracingMethod.invoke(null, filename, bufferSize, flags, samplingEnabled,
+ intervalUs);
+ }
+ public static void stopMethodTracing() throws Exception {
+ stopMethodTracingMethod.invoke(null);
+ }
+ public static int getMethodTracingMode() throws Exception {
+ return (int) getMethodTracingModeMethod.invoke(null);
+ }
+ }
+}
diff --git a/test/570-checker-osr/src/Main.java b/test/570-checker-osr/src/Main.java
new file mode 100644
index 0000000..828908a
--- /dev/null
+++ b/test/570-checker-osr/src/Main.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ new SubMain();
+ System.loadLibrary(args[0]);
+ if ($noinline$returnInt() != 53) {
+ throw new Error("Unexpected return value");
+ }
+ if ($noinline$returnFloat() != 42.2f) {
+ throw new Error("Unexpected return value");
+ }
+ if ($noinline$returnDouble() != Double.longBitsToDouble(0xF000000000001111L)) {
+ throw new Error("Unexpected return value ");
+ }
+ if ($noinline$returnLong() != 0xFFFF000000001111L) {
+ throw new Error("Unexpected return value");
+ }
+
+ try {
+ $noinline$deopt();
+ } catch (Exception e) {}
+ DeoptimizationController.stopDeoptimization();
+
+ $noinline$inlineCache(new Main(), /* isSecondInvocation */ false);
+ if ($noinline$inlineCache(new SubMain(), /* isSecondInvocation */ true) != SubMain.class) {
+ throw new Error("Unexpected return value");
+ }
+ }
+
+ public static int $noinline$returnInt() {
+ if (doThrow) throw new Error("");
+ int i = 0;
+ for (; i < 100000; ++i) {
+ }
+ while (!ensureInOsrCode()) {}
+ System.out.println(i);
+ return 53;
+ }
+
+ public static float $noinline$returnFloat() {
+ if (doThrow) throw new Error("");
+ int i = 0;
+ for (; i < 200000; ++i) {
+ }
+ while (!ensureInOsrCode()) {}
+ System.out.println(i);
+ return 42.2f;
+ }
+
+ public static double $noinline$returnDouble() {
+ if (doThrow) throw new Error("");
+ int i = 0;
+ for (; i < 300000; ++i) {
+ }
+ while (!ensureInOsrCode()) {}
+ System.out.println(i);
+ return Double.longBitsToDouble(0xF000000000001111L);
+ }
+
+ public static long $noinline$returnLong() {
+ if (doThrow) throw new Error("");
+ int i = 0;
+ for (; i < 400000; ++i) {
+ }
+ while (!ensureInOsrCode()) {}
+ System.out.println(i);
+ return 0xFFFF000000001111L;
+ }
+
+ public static void $noinline$deopt() {
+ if (doThrow) throw new Error("");
+ int i = 0;
+ for (; i < 100000; ++i) {
+ }
+ while (!ensureInOsrCode()) {}
+ DeoptimizationController.startDeoptimization();
+ }
+
+ public static Class $noinline$inlineCache(Main m, boolean isSecondInvocation) {
+ // If we are running in non-JIT mode, or were unlucky enough to get this method
+ // already JITted, just return the expected value.
+ if (!ensureInInterpreter()) {
+ return SubMain.class;
+ }
+
+ ensureHasProfilingInfo();
+
+ // Ensure that we have OSR code to jump to.
+ if (isSecondInvocation) {
+ ensureHasOsrCode();
+ }
+
+ // This call will be optimized in the OSR compiled code
+ // to check and deoptimize if m is not of type 'Main'.
+ Main other = m.inlineCache();
+
+ // Jump to OSR compiled code. The second run
+ // of this method will have 'm' as a SubMain, and the compiled
+ // code we are jumping to will have wrongly optimize other as being a
+ // 'Main'.
+ if (isSecondInvocation) {
+ while (!ensureInOsrCode()) {}
+ }
+
+ // We used to wrongly optimize this call and assume 'other' was a 'Main'.
+ return other.returnClass();
+ }
+
+ public Main inlineCache() {
+ return new Main();
+ }
+
+ public Class returnClass() {
+ return Main.class;
+ }
+
+ public static int[] array = new int[4];
+
+ public static native boolean ensureInInterpreter();
+ public static native boolean ensureInOsrCode();
+ public static native void ensureHasProfilingInfo();
+ public static native void ensureHasOsrCode();
+
+ public static boolean doThrow = false;
+}
+
+class SubMain extends Main {
+ public Class returnClass() {
+ return SubMain.class;
+ }
+
+ public Main inlineCache() {
+ return new SubMain();
+ }
+}
diff --git a/test/570-checker-select/src/Main.java b/test/570-checker-select/src/Main.java
index 2f8094d..8a4cf60 100644
--- a/test/570-checker-select/src/Main.java
+++ b/test/570-checker-select/src/Main.java
@@ -19,6 +19,16 @@
/// CHECK-START: int Main.BoolCond_IntVarVar(boolean, int, int) register (after)
/// CHECK: Select [{{i\d+}},{{i\d+}},{{z\d+}}]
+ /// CHECK-START-ARM64: int Main.BoolCond_IntVarVar(boolean, int, int) disassembly (after)
+ /// CHECK: Select
+ /// CHECK-NEXT: cmp
+ /// CHECK-NEXT: csel ne
+
+ /// CHECK-START-X86_64: int Main.BoolCond_IntVarVar(boolean, int, int) disassembly (after)
+ /// CHECK: <<Cond:z\d+>> ParameterValue
+ /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>]
+ /// CHECK: cmovnz/ne
+
public static int BoolCond_IntVarVar(boolean cond, int x, int y) {
return cond ? x : y;
}
@@ -26,6 +36,16 @@
/// CHECK-START: int Main.BoolCond_IntVarCst(boolean, int) register (after)
/// CHECK: Select [{{i\d+}},{{i\d+}},{{z\d+}}]
+ /// CHECK-START-ARM64: int Main.BoolCond_IntVarCst(boolean, int) disassembly (after)
+ /// CHECK: Select
+ /// CHECK-NEXT: cmp
+ /// CHECK-NEXT: csinc ne
+
+ /// CHECK-START-X86_64: int Main.BoolCond_IntVarCst(boolean, int) disassembly (after)
+ /// CHECK: <<Cond:z\d+>> ParameterValue
+ /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>]
+ /// CHECK: cmovnz/ne
+
public static int BoolCond_IntVarCst(boolean cond, int x) {
return cond ? x : 1;
}
@@ -33,13 +53,79 @@
/// CHECK-START: int Main.BoolCond_IntCstVar(boolean, int) register (after)
/// CHECK: Select [{{i\d+}},{{i\d+}},{{z\d+}}]
+ /// CHECK-START-ARM64: int Main.BoolCond_IntCstVar(boolean, int) disassembly (after)
+ /// CHECK: Select
+ /// CHECK-NEXT: cmp
+ /// CHECK-NEXT: csinc eq
+
+ /// CHECK-START-X86_64: int Main.BoolCond_IntCstVar(boolean, int) disassembly (after)
+ /// CHECK: <<Cond:z\d+>> ParameterValue
+ /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>]
+ /// CHECK: cmovnz/ne
+
public static int BoolCond_IntCstVar(boolean cond, int y) {
return cond ? 1 : y;
}
+ /// CHECK-START: long Main.BoolCond_LongVarVar(boolean, long, long) register (after)
+ /// CHECK: Select [{{j\d+}},{{j\d+}},{{z\d+}}]
+
+ /// CHECK-START-ARM64: long Main.BoolCond_LongVarVar(boolean, long, long) disassembly (after)
+ /// CHECK: Select
+ /// CHECK-NEXT: cmp
+ /// CHECK-NEXT: csel ne
+
+ /// CHECK-START-X86_64: long Main.BoolCond_LongVarVar(boolean, long, long) disassembly (after)
+ /// CHECK: <<Cond:z\d+>> ParameterValue
+ /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>]
+ /// CHECK: cmovnz/neq
+
+ public static long BoolCond_LongVarVar(boolean cond, long x, long y) {
+ return cond ? x : y;
+ }
+
+ /// CHECK-START: long Main.BoolCond_LongVarCst(boolean, long) register (after)
+ /// CHECK: Select [{{j\d+}},{{j\d+}},{{z\d+}}]
+
+ /// CHECK-START-ARM64: long Main.BoolCond_LongVarCst(boolean, long) disassembly (after)
+ /// CHECK: Select
+ /// CHECK-NEXT: cmp
+ /// CHECK-NEXT: csinc ne
+
+ /// CHECK-START-X86_64: long Main.BoolCond_LongVarCst(boolean, long) disassembly (after)
+ /// CHECK: <<Cond:z\d+>> ParameterValue
+ /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>]
+ /// CHECK: cmovnz/neq
+
+ public static long BoolCond_LongVarCst(boolean cond, long x) {
+ return cond ? x : 1L;
+ }
+
+ /// CHECK-START: long Main.BoolCond_LongCstVar(boolean, long) register (after)
+ /// CHECK: Select [{{j\d+}},{{j\d+}},{{z\d+}}]
+
+ /// CHECK-START-ARM64: long Main.BoolCond_LongCstVar(boolean, long) disassembly (after)
+ /// CHECK: Select
+ /// CHECK-NEXT: cmp
+ /// CHECK-NEXT: csinc eq
+
+ /// CHECK-START-X86_64: long Main.BoolCond_LongCstVar(boolean, long) disassembly (after)
+ /// CHECK: <<Cond:z\d+>> ParameterValue
+ /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>]
+ /// CHECK: cmovnz/neq
+
+ public static long BoolCond_LongCstVar(boolean cond, long y) {
+ return cond ? 1L : y;
+ }
+
/// CHECK-START: float Main.BoolCond_FloatVarVar(boolean, float, float) register (after)
/// CHECK: Select [{{f\d+}},{{f\d+}},{{z\d+}}]
+ /// CHECK-START-ARM64: float Main.BoolCond_FloatVarVar(boolean, float, float) disassembly (after)
+ /// CHECK: Select
+ /// CHECK-NEXT: cmp
+ /// CHECK-NEXT: fcsel ne
+
public static float BoolCond_FloatVarVar(boolean cond, float x, float y) {
return cond ? x : y;
}
@@ -47,6 +133,11 @@
/// CHECK-START: float Main.BoolCond_FloatVarCst(boolean, float) register (after)
/// CHECK: Select [{{f\d+}},{{f\d+}},{{z\d+}}]
+ /// CHECK-START-ARM64: float Main.BoolCond_FloatVarCst(boolean, float) disassembly (after)
+ /// CHECK: Select
+ /// CHECK-NEXT: cmp
+ /// CHECK-NEXT: fcsel ne
+
public static float BoolCond_FloatVarCst(boolean cond, float x) {
return cond ? x : 1.0f;
}
@@ -54,6 +145,11 @@
/// CHECK-START: float Main.BoolCond_FloatCstVar(boolean, float) register (after)
/// CHECK: Select [{{f\d+}},{{f\d+}},{{z\d+}}]
+ /// CHECK-START-ARM64: float Main.BoolCond_FloatCstVar(boolean, float) disassembly (after)
+ /// CHECK: Select
+ /// CHECK-NEXT: cmp
+ /// CHECK-NEXT: fcsel ne
+
public static float BoolCond_FloatCstVar(boolean cond, float y) {
return cond ? 1.0f : y;
}
@@ -62,6 +158,16 @@
/// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}]
/// CHECK-NEXT: Select [{{i\d+}},{{i\d+}},<<Cond>>]
+ /// CHECK-START-ARM64: int Main.IntNonmatCond_IntVarVar(int, int, int, int) disassembly (after)
+ /// CHECK: Select
+ /// CHECK-NEXT: cmp
+ /// CHECK-NEXT: csel le
+
+ /// CHECK-START-X86_64: int Main.IntNonmatCond_IntVarVar(int, int, int, int) disassembly (after)
+ /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}]
+ /// CHECK-NEXT: Select [{{i\d+}},{{i\d+}},<<Cond>>]
+ /// CHECK: cmovle/ng
+
public static int IntNonmatCond_IntVarVar(int a, int b, int x, int y) {
return a > b ? x : y;
}
@@ -71,15 +177,119 @@
/// CHECK-NEXT: <<Sel:i\d+>> Select [{{i\d+}},{{i\d+}},{{z\d+}}]
/// CHECK-NEXT: Add [<<Cond>>,<<Sel>>]
+ /// CHECK-START-ARM64: int Main.IntMatCond_IntVarVar(int, int, int, int) disassembly (after)
+ /// CHECK: LessThanOrEqual
+ /// CHECK-NEXT: cmp
+ /// CHECK-NEXT: cset le
+ /// CHECK: Select
+ /// CHECK-NEXT: csel le
+
+ /// CHECK-START-X86_64: int Main.IntMatCond_IntVarVar(int, int, int, int) disassembly (after)
+ /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}]
+ /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>]
+ /// CHECK: cmovle/ng
+
public static int IntMatCond_IntVarVar(int a, int b, int x, int y) {
int result = (a > b ? x : y);
return result + (a > b ? 0 : 1);
}
+ /// CHECK-START: long Main.IntNonmatCond_LongVarVar(int, int, long, long) register (after)
+ /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}]
+ /// CHECK-NEXT: Select [{{j\d+}},{{j\d+}},<<Cond>>]
+
+ /// CHECK-START-ARM64: long Main.IntNonmatCond_LongVarVar(int, int, long, long) disassembly (after)
+ /// CHECK: Select
+ /// CHECK-NEXT: cmp
+ /// CHECK-NEXT: csel le
+
+ /// CHECK-START-X86_64: long Main.IntNonmatCond_LongVarVar(int, int, long, long) disassembly (after)
+ /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}]
+ /// CHECK-NEXT: Select [{{j\d+}},{{j\d+}},<<Cond>>]
+ /// CHECK: cmovle/ngq
+
+ public static long IntNonmatCond_LongVarVar(int a, int b, long x, long y) {
+ return a > b ? x : y;
+ }
+
+ /// CHECK-START: long Main.IntMatCond_LongVarVar(int, int, long, long) register (after)
+ /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}]
+ /// CHECK: <<Sel1:j\d+>> Select [{{j\d+}},{{j\d+}},<<Cond>>]
+ /// CHECK: <<Sel2:j\d+>> Select [{{j\d+}},{{j\d+}},<<Cond>>]
+ /// CHECK: Add [<<Sel2>>,<<Sel1>>]
+
+ /// CHECK-START-ARM64: long Main.IntMatCond_LongVarVar(int, int, long, long) disassembly (after)
+ /// CHECK: LessThanOrEqual
+ /// CHECK-NEXT: cmp
+ /// CHECK-NEXT: cset le
+ /// CHECK: Select
+ /// CHECK-NEXT: csel le
+
+ /// CHECK-START-X86_64: long Main.IntMatCond_LongVarVar(int, int, long, long) disassembly (after)
+ /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}]
+ /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>]
+ /// CHECK: cmovle/ngq
+ /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>]
+ /// CHECK: cmovnz/neq
+
+ public static long IntMatCond_LongVarVar(int a, int b, long x, long y) {
+ long result = (a > b ? x : y);
+ return result + (a > b ? 0L : 1L);
+ }
+
+ /// CHECK-START: long Main.LongNonmatCond_LongVarVar(long, long, long, long) register (after)
+ /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{j\d+}},{{j\d+}}]
+ /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>]
+
+ /// CHECK-START-ARM64: long Main.LongNonmatCond_LongVarVar(long, long, long, long) disassembly (after)
+ /// CHECK: Select
+ /// CHECK-NEXT: cmp
+ /// CHECK-NEXT: csel le
+
+ /// CHECK-START-X86_64: long Main.LongNonmatCond_LongVarVar(long, long, long, long) disassembly (after)
+ /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{j\d+}},{{j\d+}}]
+ /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>]
+ /// CHECK: cmovle/ngq
+
+ public static long LongNonmatCond_LongVarVar(long a, long b, long x, long y) {
+ return a > b ? x : y;
+ }
+
+ /// CHECK-START: long Main.LongMatCond_LongVarVar(long, long, long, long) register (after)
+ /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{j\d+}},{{j\d+}}]
+ /// CHECK: <<Sel1:j\d+>> Select [{{j\d+}},{{j\d+}},<<Cond>>]
+ /// CHECK: <<Sel2:j\d+>> Select [{{j\d+}},{{j\d+}},<<Cond>>]
+ /// CHECK: Add [<<Sel2>>,<<Sel1>>]
+
+ /// CHECK-START-ARM64: long Main.LongMatCond_LongVarVar(long, long, long, long) disassembly (after)
+ /// CHECK: LessThanOrEqual
+ /// CHECK-NEXT: cmp
+ /// CHECK-NEXT: cset le
+ /// CHECK: Select
+ /// CHECK-NEXT: csel le
+
+ /// CHECK-START-X86_64: long Main.LongMatCond_LongVarVar(long, long, long, long) disassembly (after)
+ /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{j\d+}},{{j\d+}}]
+ /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>]
+ /// CHECK: cmovle/ngq
+ /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>]
+ /// CHECK: cmovnz/neq
+
+ public static long LongMatCond_LongVarVar(long a, long b, long x, long y) {
+ long result = (a > b ? x : y);
+ return result + (a > b ? 0L : 1L);
+ }
+
/// CHECK-START: int Main.FloatLtNonmatCond_IntVarVar(float, float, int, int) register (after)
/// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{f\d+}},{{f\d+}}]
/// CHECK-NEXT: Select [{{i\d+}},{{i\d+}},<<Cond>>]
+ /// CHECK-START-ARM64: int Main.FloatLtNonmatCond_IntVarVar(float, float, int, int) disassembly (after)
+ /// CHECK: LessThanOrEqual
+ /// CHECK: Select
+ /// CHECK-NEXT: fcmp
+ /// CHECK-NEXT: csel le
+
public static int FloatLtNonmatCond_IntVarVar(float a, float b, int x, int y) {
return a > b ? x : y;
}
@@ -88,6 +298,12 @@
/// CHECK: <<Cond:z\d+>> GreaterThanOrEqual [{{f\d+}},{{f\d+}}]
/// CHECK-NEXT: Select [{{i\d+}},{{i\d+}},<<Cond>>]
+ /// CHECK-START-ARM64: int Main.FloatGtNonmatCond_IntVarVar(float, float, int, int) disassembly (after)
+ /// CHECK: GreaterThanOrEqual
+ /// CHECK: Select
+ /// CHECK-NEXT: fcmp
+ /// CHECK-NEXT: csel hs
+
public static int FloatGtNonmatCond_IntVarVar(float a, float b, int x, int y) {
return a < b ? x : y;
}
@@ -96,6 +312,12 @@
/// CHECK: <<Cond:z\d+>> GreaterThanOrEqual [{{f\d+}},{{f\d+}}]
/// CHECK-NEXT: Select [{{f\d+}},{{f\d+}},<<Cond>>]
+ /// CHECK-START-ARM64: float Main.FloatGtNonmatCond_FloatVarVar(float, float, float, float) disassembly (after)
+ /// CHECK: GreaterThanOrEqual
+ /// CHECK: Select
+ /// CHECK-NEXT: fcmp
+ /// CHECK-NEXT: fcsel hs
+
public static float FloatGtNonmatCond_FloatVarVar(float a, float b, float x, float y) {
return a < b ? x : y;
}
@@ -105,6 +327,13 @@
/// CHECK-NEXT: <<Sel:i\d+>> Select [{{i\d+}},{{i\d+}},<<Cond>>]
/// CHECK-NEXT: Add [<<Cond>>,<<Sel>>]
+ /// CHECK-START-ARM64: int Main.FloatLtMatCond_IntVarVar(float, float, int, int) disassembly (after)
+ /// CHECK: LessThanOrEqual
+ /// CHECK-NEXT: fcmp
+ /// CHECK-NEXT: cset le
+ /// CHECK: Select
+ /// CHECK-NEXT: csel le
+
public static int FloatLtMatCond_IntVarVar(float a, float b, int x, int y) {
int result = (a > b ? x : y);
return result + (a > b ? 0 : 1);
@@ -115,6 +344,13 @@
/// CHECK-NEXT: <<Sel:i\d+>> Select [{{i\d+}},{{i\d+}},<<Cond>>]
/// CHECK-NEXT: Add [<<Cond>>,<<Sel>>]
+ /// CHECK-START-ARM64: int Main.FloatGtMatCond_IntVarVar(float, float, int, int) disassembly (after)
+ /// CHECK: GreaterThanOrEqual
+ /// CHECK-NEXT: fcmp
+ /// CHECK-NEXT: cset hs
+ /// CHECK: Select
+ /// CHECK-NEXT: csel hs
+
public static int FloatGtMatCond_IntVarVar(float a, float b, int x, int y) {
int result = (a < b ? x : y);
return result + (a < b ? 0 : 1);
@@ -125,6 +361,13 @@
/// CHECK-NEXT: <<Sel:f\d+>> Select [{{f\d+}},{{f\d+}},<<Cond>>]
/// CHECK-NEXT: TypeConversion [<<Cond>>]
+ /// CHECK-START-ARM64: float Main.FloatGtMatCond_FloatVarVar(float, float, float, float) disassembly (after)
+ /// CHECK: GreaterThanOrEqual
+ /// CHECK-NEXT: fcmp
+ /// CHECK-NEXT: cset hs
+ /// CHECK: Select
+ /// CHECK-NEXT: fcsel hs
+
public static float FloatGtMatCond_FloatVarVar(float a, float b, float x, float y) {
float result = (a < b ? x : y);
return result + (a < b ? 0 : 1);
@@ -150,6 +393,13 @@
assertEqual(1, BoolCond_IntCstVar(true, 7));
assertEqual(7, BoolCond_IntCstVar(false, 7));
+ assertEqual(5L, BoolCond_LongVarVar(true, 5L, 7L));
+ assertEqual(7L, BoolCond_LongVarVar(false, 5L, 7L));
+ assertEqual(5L, BoolCond_LongVarCst(true, 5L));
+ assertEqual(1L, BoolCond_LongVarCst(false, 5L));
+ assertEqual(1L, BoolCond_LongCstVar(true, 7L));
+ assertEqual(7L, BoolCond_LongCstVar(false, 7L));
+
assertEqual(5, BoolCond_FloatVarVar(true, 5, 7));
assertEqual(7, BoolCond_FloatVarVar(false, 5, 7));
assertEqual(5, BoolCond_FloatVarCst(true, 5));
diff --git a/test/572-checker-array-get-regression/expected.txt b/test/572-checker-array-get-regression/expected.txt
new file mode 100644
index 0000000..f7d1ad4
--- /dev/null
+++ b/test/572-checker-array-get-regression/expected.txt
@@ -0,0 +1 @@
+524287
diff --git a/test/572-checker-array-get-regression/info.txt b/test/572-checker-array-get-regression/info.txt
new file mode 100644
index 0000000..d06feee
--- /dev/null
+++ b/test/572-checker-array-get-regression/info.txt
@@ -0,0 +1,3 @@
+Regression test for the ARM64 Baker's read barrier fast path compiler
+instrumentation of array loads with a large constant index, where we
+used to require too many scratch (temporary) registers.
diff --git a/test/572-checker-array-get-regression/src/Main.java b/test/572-checker-array-get-regression/src/Main.java
new file mode 100644
index 0000000..b55be70
--- /dev/null
+++ b/test/572-checker-array-get-regression/src/Main.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+ public static void main(String[] args) {
+ System.out.println(test().intValue());
+ }
+
+ /// CHECK-START: java.lang.Integer Main.test() builder (after)
+ /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+ /// CHECK-DAG: <<Const2P19:i\d+>> IntConstant 524288
+ /// CHECK-DAG: <<ConstM1:i\d+>> IntConstant -1
+ /// CHECK-DAG: <<Array:l\d+>> NewArray [<<Const2P19>>,<<Method>>]
+ /// CHECK-DAG: <<NullCheck1:l\d+>> NullCheck [<<Array>>]
+ /// CHECK-DAG: <<Length1:i\d+>> ArrayLength [<<NullCheck1>>]
+ /// CHECK-DAG: <<Index:i\d+>> Add [<<Length1>>,<<ConstM1>>]
+ /// CHECK-DAG: <<NullCheck2:l\d+>> NullCheck [<<Array>>]
+ /// CHECK-DAG: <<Length2:i\d+>> ArrayLength [<<NullCheck2>>]
+ /// CHECK-DAG: <<BoundsCheck:i\d+>> BoundsCheck [<<Index>>,<<Length2>>]
+ /// CHECK-DAG: <<LastElement:l\d+>> ArrayGet [<<NullCheck2>>,<<BoundsCheck>>]
+ /// CHECK-DAG: Return [<<LastElement>>]
+
+
+ /// CHECK-START: java.lang.Integer Main.test() register (before)
+ /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+ /// CHECK-DAG: <<Const2P19:i\d+>> IntConstant 524288
+ /// CHECK-DAG: <<Const2P19M1:i\d+>> IntConstant 524287
+ /// CHECK-DAG: <<Array:l\d+>> NewArray [<<Const2P19>>,<<Method>>]
+ /// CHECK-DAG: <<LastElement:l\d+>> ArrayGet [<<Array>>,<<Const2P19M1>>]
+ /// CHECK-DAG: Return [<<LastElement>>]
+
+ public static Integer test() {
+ Integer[] integers = new Integer[1 << 19];
+ initIntegerArray(integers);
+ // Array load with a large constant index (after constant folding
+ // and bounds check elimination).
+ Integer last_integer = integers[integers.length - 1];
+ return last_integer;
+ }
+
+ public static void initIntegerArray(Integer[] integers) {
+ for (int i = 0; i < integers.length; ++i) {
+ integers[i] = new Integer(i);
+ }
+ }
+
+}
diff --git a/test/573-checker-checkcast-regression/expected.txt b/test/573-checker-checkcast-regression/expected.txt
new file mode 100644
index 0000000..b8626c4
--- /dev/null
+++ b/test/573-checker-checkcast-regression/expected.txt
@@ -0,0 +1 @@
+4
diff --git a/test/573-checker-checkcast-regression/info.txt b/test/573-checker-checkcast-regression/info.txt
new file mode 100644
index 0000000..74a6d6e
--- /dev/null
+++ b/test/573-checker-checkcast-regression/info.txt
@@ -0,0 +1,4 @@
+Regression test for the x86-64 Baker's read barrier fast path compiler
+instrumentation of CheckCasts, where we used to use an
+art::x86_64::NearLabel, the range of which was sometimes too short
+with Baker's read barriers enabled.
diff --git a/test/573-checker-checkcast-regression/src/Main.java b/test/573-checker-checkcast-regression/src/Main.java
new file mode 100644
index 0000000..473a2b1
--- /dev/null
+++ b/test/573-checker-checkcast-regression/src/Main.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+ public static void main(String[] args) {
+ Object[] array = { new Integer(1), new Integer(2), new Integer(3) };
+ int result = test(array, 0, 2);
+ System.out.println(result);
+ }
+
+ // This test method uses two integers (`index1` and `index2`) to
+ // force the register allocator to use some high registers (R8-R15)
+ // on x86-64 in the code generated for the first CheckCast (which
+ // converts `new_array` to an `Object[]`), so as to produce code
+ // containing a conditional jump whose offset does not fit in a
+ // NearLabel when using Baker's read barrier fast path (because
+ // x86-64 instructions using these high registers have a larger
+ // encoding).
+ //
+ // The intent of this artifical constraint is to ensure the initial
+ // failure is properly tested by this regression test.
+
+ /// CHECK-START: int Main.test(java.lang.Object, int, int) register (after)
+ /// CHECK-DAG: CheckCast check_kind:array_object_check
+ /// CHECK-DAG: CheckCast check_kind:exact_check
+ /// CHECK-DAG: CheckCast check_kind:exact_check
+
+ static public int test(Object new_array, int index1, int index2) {
+ Object[] objectArray = (Object[]) new_array;
+ Integer integer1 = (Integer) objectArray[index1];
+ Integer integer2 = (Integer) objectArray[index2];
+ return integer1.intValue() + integer2.intValue();
+ }
+
+}
diff --git a/test/574-irreducible-and-constant-area/expected.txt b/test/574-irreducible-and-constant-area/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/574-irreducible-and-constant-area/expected.txt
diff --git a/test/574-irreducible-and-constant-area/info.txt b/test/574-irreducible-and-constant-area/info.txt
new file mode 100644
index 0000000..e957a5a
--- /dev/null
+++ b/test/574-irreducible-and-constant-area/info.txt
@@ -0,0 +1,3 @@
+Regression test for intrinsics on x86, which used to wrongly assume
+a HInvokeStaticOrDirect must have a special input (does not apply for irreducible
+loops).
diff --git a/test/574-irreducible-and-constant-area/run b/test/574-irreducible-and-constant-area/run
new file mode 100755
index 0000000..ffdbcc9
--- /dev/null
+++ b/test/574-irreducible-and-constant-area/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Don't do relocation, as this affects this test.
+exec ${RUN} "$@" --no-relocate
diff --git a/test/574-irreducible-and-constant-area/smali/IrreducibleLoop.smali b/test/574-irreducible-and-constant-area/smali/IrreducibleLoop.smali
new file mode 100644
index 0000000..d7d4346
--- /dev/null
+++ b/test/574-irreducible-and-constant-area/smali/IrreducibleLoop.smali
@@ -0,0 +1,35 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LIrreducibleLoop;
+
+.super Ljava/lang/Object;
+
+.method public static simpleLoop(I)I
+ .registers 5
+ const/16 v0, 42
+ const/16 v1, 42
+ const-wide/high16 v2, 0x4000000000000000L
+ if-eq p0, v0, :other_loop_entry
+ :loop_entry
+ invoke-static {v1, v1}, LMain;->$inline$foo(FF)V
+ invoke-static {v2, v3, v2, v3}, LMain;->$inline$foo(DD)V
+ if-ne p0, v0, :exit
+ add-int v0, v0, v0
+ :other_loop_entry
+ add-int v0, v0, v0
+ goto :loop_entry
+ :exit
+ return v0
+.end method
diff --git a/test/574-irreducible-and-constant-area/src/Main.java b/test/574-irreducible-and-constant-area/src/Main.java
new file mode 100644
index 0000000..3cdd924
--- /dev/null
+++ b/test/574-irreducible-and-constant-area/src/Main.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("IrreducibleLoop");
+ Method m = c.getMethod("simpleLoop", int.class);
+ Object[] arguments = { 42 };
+ m.invoke(null, arguments);
+ }
+
+ public static void $inline$foo(float a, float b) {
+ Math.abs(a);
+ Math.max(a, b);
+ Math.min(a, b);
+ }
+
+ public static void $inline$foo(double a, double b) {
+ Math.abs(a);
+ Math.max(a, b);
+ Math.min(a, b);
+ }
+}
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index 73ce307..8808a50 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -58,4 +58,6 @@
b/26594149 (6)
b/26594149 (7)
b/26594149 (8)
+b/27148248
+b/26965384
Done!
diff --git a/test/800-smali/smali/b_26965384.smali b/test/800-smali/smali/b_26965384.smali
new file mode 100644
index 0000000..47ed418
--- /dev/null
+++ b/test/800-smali/smali/b_26965384.smali
@@ -0,0 +1,20 @@
+.class public LB26965384;
+.super LB26965384Super;
+
+.method public constructor <init>()V
+ .locals 1
+ const v0, 0
+ iput v0, p0, LB26965384;->a:I
+ invoke-direct {p0}, LB26965384Super;-><init>()V
+ return-void
+.end method
+
+
+# Just by loading this class we should fail. It doesn't really matter what's in
+# this method.
+.method public static run()V
+ .registers 4
+ new-instance v0, LB26965384;
+ invoke-direct {v0}, LB26965384;-><init>()V
+ return-void
+.end method
diff --git a/test/800-smali/smali/b_26965384Super.smali b/test/800-smali/smali/b_26965384Super.smali
new file mode 100644
index 0000000..32faea7
--- /dev/null
+++ b/test/800-smali/smali/b_26965384Super.smali
@@ -0,0 +1,10 @@
+.class public LB26965384Super;
+.super Ljava/lang/Object;
+
+.field public a:I
+
+.method public constructor <init>()V
+ .locals 0
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
diff --git a/test/800-smali/smali/b_27148248.smali b/test/800-smali/smali/b_27148248.smali
new file mode 100644
index 0000000..4601cc6
--- /dev/null
+++ b/test/800-smali/smali/b_27148248.smali
@@ -0,0 +1,27 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LB27148248;
+
+# Regression for dex2oatd crash during compilation of method which
+# used to throw with argument of non-reference type.
+
+.super Ljava/lang/Object;
+
+.method public static run()V
+ .registers 1
+ const v0, 0xbad
+ throw v0
+.end method
+
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index b0eff5d..4e6de46 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -160,6 +160,10 @@
null));
testCases.add(new TestCase("b/26594149 (8)", "B26594149_8", "run", null, new VerifyError(),
null));
+ testCases.add(new TestCase("b/27148248", "B27148248", "run", null, new VerifyError(),
+ null));
+ testCases.add(new TestCase("b/26965384", "B26965384", "run", null, new VerifyError(),
+ null));
}
public void runTests() {
diff --git a/test/971-iface-super/util-src/generate_smali.py b/test/971-iface-super/util-src/generate_smali.py
index f01c904..3681411 100755
--- a/test/971-iface-super/util-src/generate_smali.py
+++ b/test/971-iface-super/util-src/generate_smali.py
@@ -39,7 +39,7 @@
import string
# The max depth the type tree can have.
-MAX_IFACE_DEPTH = 3
+MAX_IFACE_DEPTH = 2
class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin):
"""
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index faaf1f0..e547c72 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -40,7 +40,8 @@
466-get-live-vreg/get_live_vreg_jni.cc \
497-inlining-and-class-loader/clear_dex_cache.cc \
543-env-long-ref/env_long_ref.cc \
- 566-polymorphic-inlining/polymorphic_inline.cc
+ 566-polymorphic-inlining/polymorphic_inline.cc \
+ 570-checker-osr/osr.cc
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttestd.so
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index dfb540e..364be59 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -42,8 +42,7 @@
ifeq ($(ANDROID_COMPILE_WITH_JACK),true)
TEST_ART_RUN_TEST_DEPENDENCIES += \
- $(JACK) \
- $(JILL_JAR)
+ $(JACK)
TEST_ART_RUN_TEST_ORDERONLY_DEPENDENCIES += setup-jack-server
endif
@@ -72,8 +71,8 @@
DXMERGER=$(abspath $(HOST_OUT_EXECUTABLES)/dexmerger) \
JACK_VERSION=$(JACK_DEFAULT_VERSION) \
JACK=$(abspath $(JACK)) \
+ JACK_VERSION=$(JACK_DEFAULT_VERSION) \
JACK_CLASSPATH=$(TARGET_JACK_CLASSPATH) \
- JILL_JAR=$(abspath $(JILL_JAR)) \
$(LOCAL_PATH)/run-test $$(PRIVATE_RUN_TEST_OPTIONS) --output-path $$(abspath $$(dir $$@)) $(1)
$(hide) touch $$@
@@ -235,6 +234,18 @@
$(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(ALL_ADDRESS_SIZES))
endif
+# 569-checker-pattern-replacement tests behaviour present only on host.
+TEST_ART_BROKEN_TARGET_TESTS := \
+ 569-checker-pattern-replacement
+
+ifneq (,$(filter target,$(TARGET_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
+ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_TARGET_TESTS), $(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_TARGET_TESTS :=
+
# Tests that require python3.
TEST_ART_PYTHON3_DEPENDENCY_RUN_TESTS := \
960-default-smali \
@@ -302,18 +313,7 @@
# Temporarily disable some broken tests when forcing access checks in interpreter b/22414682
TEST_ART_BROKEN_INTERPRETER_ACCESS_CHECK_TESTS := \
- 004-JniTest \
- 005-annotations \
- 044-proxy \
- 073-mismatched-field \
- 088-monitor-verification \
- 135-MirandaDispatch \
- 137-cfi \
- 412-new-array \
- 471-uninitialized-locals \
- 506-verify-aput \
- 554-jit-profile-file \
- 800-smali
+ 137-cfi
ifneq (,$(filter interp-ac,$(COMPILER_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -412,13 +412,14 @@
# 137:
# This test unrolls and expects managed frames, but tracing means we run the interpreter.
-# 802:
+# 802 and 570-checker-osr:
# This test dynamically enables tracing to force a deoptimization. This makes the test meaningless
# when already tracing, and writes an error message that we do not want to check for.
TEST_ART_BROKEN_TRACING_RUN_TESTS := \
087-gc-after-link \
137-cfi \
141-class-unload \
+ 570-checker-osr \
802-deoptimization
ifneq (,$(filter trace stream,$(TRACE_TYPES)))
@@ -470,10 +471,7 @@
# Known broken tests for the mips32 optimizing compiler backend.
TEST_ART_BROKEN_OPTIMIZING_MIPS_RUN_TESTS := \
- 441-checker-inliner \
510-checker-try-catch \
- 536-checker-intrinsic-optimization \
- 557-checker-instruction-simplifier-ror \
ifeq (mips,$(TARGET_ARCH))
ifneq (,$(filter optimizing,$(COMPILER_TYPES)))
@@ -488,7 +486,6 @@
# Known broken tests for the mips64 optimizing compiler backend.
TEST_ART_BROKEN_OPTIMIZING_MIPS64_RUN_TESTS := \
- 557-checker-instruction-simplifier-ror \
ifeq (mips64,$(TARGET_ARCH))
ifneq (,$(filter optimizing,$(COMPILER_TYPES)))
@@ -964,8 +961,8 @@
DXMERGER=$(abspath $(HOST_OUT_EXECUTABLES)/dexmerger) \
JACK_VERSION=$(JACK_DEFAULT_VERSION) \
JACK=$(abspath $(JACK)) \
+ JACK_VERSION=$(JACK_DEFAULT_VERSION) \
JACK_CLASSPATH=$$(PRIVATE_JACK_CLASSPATH) \
- JILL_JAR=$(abspath $(JILL_JAR)) \
art/test/run-test $$(PRIVATE_RUN_TEST_OPTIONS) $(12) \
&& $$(call ART_TEST_PASSED,$$@) || $$(call ART_TEST_FAILED,$$@)
$$(hide) (echo $(MAKECMDGOALS) | grep -q $$@ && \
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 8245ccf..2db1e6c 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -406,6 +406,9 @@
TMP_DIR_OPTION="-Djava.io.tmpdir=/data/local/tmp"
fi
+# We set DumpNativeStackOnSigQuit to false to avoid stressing libunwind.
+# b/27185632
+# b/24664297
dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \
$GDB_ARGS \
$FLAGS \
@@ -420,6 +423,7 @@
$DEBUGGER_OPTS \
$DALVIKVM_BOOT_OPT \
$TMP_DIR_OPTION \
+ -XX:DumpNativeStackOnSigQuit:false \
-cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX $MAIN $ARGS"
# Remove whitespace.
diff --git a/test/run-test b/test/run-test
index faa597e..f1875d7 100755
--- a/test/run-test
+++ b/test/run-test
@@ -88,13 +88,7 @@
export JACK_CLASSPATH="${OUT_DIR:-$ANDROID_BUILD_TOP/out}/host/common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack:${OUT_DIR:-$ANDROID_BUILD_TOP/out}/host/common/obj/JAVA_LIBRARIES/core-oj-hostdex_intermediates/classes.jack"
fi
-# If JILL_JAR is not set, assume it is located in the prebuilts directory.
-if [ -z "$JILL_JAR" ]; then
- export JILL_JAR="$ANDROID_BUILD_TOP/prebuilts/sdk/tools/jill.jar"
-fi
-
export JACK="$JACK -g -cp $JACK_CLASSPATH"
-export JILL="java -jar $JILL_JAR"
info="info.txt"
build="build"
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 9e02ce2..2eb52bc 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -21,7 +21,7 @@
out_dir=${OUT_DIR-out}
java_libraries_dir=${out_dir}/target/common/obj/JAVA_LIBRARIES
-common_targets="vogar ${java_libraries_dir}/core-tests_intermediates/javalib.jar apache-harmony-jdwp-tests-hostdex ${java_libraries_dir}/jsr166-tests_intermediates/javalib.jar ${out_dir}/host/linux-x86/bin/jack"
+common_targets="vogar core-tests apache-harmony-jdwp-tests-hostdex jsr166-tests ${out_dir}/host/linux-x86/bin/jack"
mode="target"
j_arg="-j$(nproc)"
showcommands=
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 6d67f84..44206df 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -171,7 +171,7 @@
bug: 25437292
},
{
- description: "Failing tests after enso move.",
+ description: "Failing tests after OpenJDK move.",
result: EXEC_FAILED,
bug: 26326992,
names: ["libcore.icu.RelativeDateTimeFormatterTest#test_getRelativeDateTimeStringDST",
@@ -196,6 +196,12 @@
"org.apache.harmony.tests.java.text.DecimalFormatSymbolsTest#test_setInternationalCurrencySymbolLjava_lang_String",
"org.apache.harmony.tests.java.text.DecimalFormatTest#testSerializationHarmonyRICompatible",
"org.apache.harmony.tests.java.text.SimpleDateFormatTest#test_parseLjava_lang_StringLjava_text_ParsePosition",
+ "org.apache.harmony.tests.java.text.SimpleDateFormatTest#test_parse_W_w_dd_MMMM_yyyy_EEEE",
+ "org.apache.harmony.tests.java.text.SimpleDateFormatTest#test_parse_dayOfYearPatterns",
+ "org.apache.harmony.tests.java.text.SimpleDateFormatTest#test_parse_h_m_z",
+ "org.apache.harmony.tests.java.text.SimpleDateFormatTest#test_parse_h_z_2DigitOffsetFromGMT",
+ "org.apache.harmony.tests.java.text.SimpleDateFormatTest#test_parse_h_z_4DigitOffsetFromGMT",
+ "org.apache.harmony.tests.java.text.SimpleDateFormatTest#test_parse_h_z_4DigitOffsetNoGMT",
"org.apache.harmony.tests.java.util.jar.JarFileTest#test_getInputStreamLjava_util_jar_JarEntry_subtest0",
"libcore.java.util.CalendarTest#test_clear_45877",
"org.apache.harmony.crypto.tests.javax.crypto.spec.SecretKeySpecTest#testGetFormat",
diff --git a/tools/libcore_failures_concurrent_collector.txt b/tools/libcore_failures_concurrent_collector.txt
index 95d1292..d8ef9ba 100644
--- a/tools/libcore_failures_concurrent_collector.txt
+++ b/tools/libcore_failures_concurrent_collector.txt
@@ -27,7 +27,8 @@
description: "TimeoutException on host-{x86,x86-64}-concurrent-collector",
result: EXEC_FAILED,
modes: [host],
- names: ["libcore.java.util.zip.DeflaterOutputStreamTest#testSyncFlushDisabled",
+ names: ["libcore.java.util.zip.DeflaterOutputStreamTest#testSyncFlushEnabled",
+ "libcore.java.util.zip.DeflaterOutputStreamTest#testSyncFlushDisabled",
"libcore.java.util.zip.GZIPOutputStreamTest#testSyncFlushEnabled",
"libcore.java.util.zip.OldAndroidGZIPStreamTest#testGZIPStream",
"libcore.java.util.zip.OldAndroidZipStreamTest#testZipStream",
@@ -40,7 +41,8 @@
result: EXEC_FAILED,
modes: [device],
names: ["libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045",
- "libcore.java.text.SimpleDateFormatTest#testLocales"],
+ "libcore.java.text.SimpleDateFormatTest#testLocales",
+ "libcore.java.util.zip.ZipFileTest#testZipFileWithLotsOfEntries"],
bug: 26711853
}
]
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index f346239..45fb4b4d 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -20,13 +20,13 @@
fi
# Jar containing jsr166 tests.
-jsr166_test_jar=${OUT_DIR-out}/target/common/obj/JAVA_LIBRARIES/jsr166-tests_intermediates/javalib.jar
+jsr166_test_jack=${OUT_DIR-out}/target/common/obj/JAVA_LIBRARIES/jsr166-tests_intermediates/classes.jack
# Jar containing all the other tests.
-test_jar=${OUT_DIR-out}/target/common/obj/JAVA_LIBRARIES/core-tests_intermediates/javalib.jar
+test_jack=${OUT_DIR-out}/target/common/obj/JAVA_LIBRARIES/core-tests_intermediates/classes.jack
-if [ ! -f $test_jar ]; then
+if [ ! -f $test_jack ]; then
echo "Before running, you must build core-tests, jsr166-tests and vogar: \
make core-tests jsr166-tests vogar vogar.jar"
exit 1
@@ -108,7 +108,11 @@
# the default timeout.
vogar_args="$vogar_args --timeout 480"
+# Use Jack with "1.8" configuration.
+export JACK_VERSION=`basename prebuilts/sdk/tools/jacks/*ALPHA* | sed 's/^jack-//' | sed 's/.jar$//'`
+vogar_args="$vogar_args --toolchain jack --language JN"
+
# Run the tests using vogar.
echo "Running tests for the following test packages:"
echo ${working_packages[@]} | tr " " "\n"
-vogar $vogar_args --vm-arg -Xusejit:true $expectations --classpath $jsr166_test_jar --classpath $test_jar ${working_packages[@]}
+vogar $vogar_args --vm-arg -Xusejit:true $expectations --classpath $jsr166_test_jack --classpath $test_jack ${working_packages[@]}