am c1134980: Merge "ART: Sometimes even empty methods take forever to verify" into mnc-dev
* commit 'c1134980c652b69a10b0317ff306e12f4feeea3c':
ART: Sometimes even empty methods take forever to verify
diff --git a/build/Android.common_path.mk b/build/Android.common_path.mk
index e0c0b0c..2d6b6a3 100644
--- a/build/Android.common_path.mk
+++ b/build/Android.common_path.mk
@@ -80,7 +80,7 @@
TARGET_CORE_IMG_LOCATION := $(ART_TARGET_TEST_OUT)/core.art
# Jar files for core.art.
-TARGET_CORE_JARS := core-libart conscrypt okhttp core-junit bouncycastle
+TARGET_CORE_JARS := core-libart conscrypt okhttp bouncycastle
HOST_CORE_JARS := $(addsuffix -hostdex,$(TARGET_CORE_JARS))
HOST_CORE_DEX_LOCATIONS := $(foreach jar,$(HOST_CORE_JARS), $(HOST_OUT_JAVA_LIBRARIES)/$(jar).jar)
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 03165ed..e02fe4b 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -579,6 +579,8 @@
log_verbosity.class_linker = true;
} else if (verbose_options[j] == "compiler") {
log_verbosity.compiler = true;
+ } else if (verbose_options[j] == "deopt") {
+ log_verbosity.deopt = true;
} else if (verbose_options[j] == "gc") {
log_verbosity.gc = true;
} else if (verbose_options[j] == "heap") {
diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h
index f7501d2..5e345db 100644
--- a/compiler/cfi_test.h
+++ b/compiler/cfi_test.h
@@ -30,6 +30,8 @@
namespace art {
+constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_DEBUG_FRAME_FORMAT;
+
class CFITest : public dwarf::DwarfTest {
public:
void GenerateExpected(FILE* f, InstructionSet isa, const char* isa_str,
@@ -46,11 +48,11 @@
// Pretty-print CFI opcodes.
constexpr bool is64bit = false;
dwarf::DebugFrameOpCodeWriter<> initial_opcodes;
- dwarf::WriteEhFrameCIE(is64bit, dwarf::DW_EH_PE_absptr, dwarf::Reg(8),
- initial_opcodes, &eh_frame_data_);
- std::vector<uintptr_t> eh_frame_patches;
- dwarf::WriteEhFrameFDE(is64bit, 0, 0, actual_asm.size(), &actual_cfi,
- &eh_frame_data_, &eh_frame_patches);
+ dwarf::WriteDebugFrameCIE(is64bit, dwarf::DW_EH_PE_absptr, dwarf::Reg(8),
+ initial_opcodes, kCFIFormat, &debug_frame_data_);
+ std::vector<uintptr_t> debug_frame_patches;
+ dwarf::WriteDebugFrameFDE(is64bit, 0, 0, actual_asm.size(), &actual_cfi,
+ kCFIFormat, &debug_frame_data_, &debug_frame_patches);
ReformatCfi(Objdump(false, "-W"), &lines);
// Pretty-print assembly.
auto* opts = new DisassemblerOptions(false, actual_asm.data(), true);
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 47288b5..5dc93ce 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -74,8 +74,8 @@
static constexpr bool kTimeCompileMethod = !kIsDebugBuild;
-// Whether to produce 64-bit ELF files for 64-bit targets. Leave this off for now.
-static constexpr bool kProduce64BitELFFiles = false;
+// Whether to produce 64-bit ELF files for 64-bit targets.
+static constexpr bool kProduce64BitELFFiles = true;
// Whether classes-to-compile and methods-to-compile are only applied to the boot image, or, when
// given, too all compilations.
@@ -1241,7 +1241,7 @@
mirror::Class* referrer_class;
mirror::DexCache* dex_cache;
{
- StackHandleScope<3> hs(soa.Self());
+ StackHandleScope<2> hs(soa.Self());
Handle<mirror::DexCache> dex_cache_handle(
hs.NewHandle(mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile())));
Handle<mirror::ClassLoader> class_loader_handle(
diff --git a/compiler/dwarf/dwarf_constants.h b/compiler/dwarf/dwarf_constants.h
index 61a44cd..3b570e5 100644
--- a/compiler/dwarf/dwarf_constants.h
+++ b/compiler/dwarf/dwarf_constants.h
@@ -680,6 +680,14 @@
DW_EH_PE_aligned = 0x50,
};
+enum CFIFormat : uint8_t {
+ // This is the original format as defined by the specification.
+ // It is used for the .debug_frame section.
+ DW_DEBUG_FRAME_FORMAT,
+ // Slightly modified format used for the .eh_frame section.
+ DW_EH_FRAME_FORMAT
+};
+
} // namespace dwarf
} // namespace art
diff --git a/compiler/dwarf/dwarf_test.cc b/compiler/dwarf/dwarf_test.cc
index edba00a..4971f0e 100644
--- a/compiler/dwarf/dwarf_test.cc
+++ b/compiler/dwarf/dwarf_test.cc
@@ -26,6 +26,8 @@
namespace art {
namespace dwarf {
+constexpr CFIFormat kCFIFormat = DW_DEBUG_FRAME_FORMAT;
+
// Run the tests only on host since we need objdump.
#ifndef HAVE_ANDROID_OS
@@ -120,30 +122,30 @@
DW_CHECK_NEXT("DW_CFA_restore: r5 (ebp)");
DebugFrameOpCodeWriter<> initial_opcodes;
- WriteEhFrameCIE(is64bit, DW_EH_PE_absptr, Reg(is64bit ? 16 : 8),
- initial_opcodes, &eh_frame_data_);
- std::vector<uintptr_t> eh_frame_patches;
+ WriteDebugFrameCIE(is64bit, DW_EH_PE_absptr, Reg(is64bit ? 16 : 8),
+ initial_opcodes, kCFIFormat, &debug_frame_data_);
+ std::vector<uintptr_t> debug_frame_patches;
std::vector<uintptr_t> expected_patches { 28 }; // NOLINT
- WriteEhFrameFDE(is64bit, 0, 0x01000000, 0x01000000, opcodes.data(),
- &eh_frame_data_, &eh_frame_patches);
+ WriteDebugFrameFDE(is64bit, 0, 0x01000000, 0x01000000, opcodes.data(),
+ kCFIFormat, &debug_frame_data_, &debug_frame_patches);
- EXPECT_EQ(expected_patches, eh_frame_patches);
+ EXPECT_EQ(expected_patches, debug_frame_patches);
CheckObjdumpOutput(is64bit, "-W");
}
TEST_F(DwarfTest, DebugFrame64) {
constexpr bool is64bit = true;
DebugFrameOpCodeWriter<> initial_opcodes;
- WriteEhFrameCIE(is64bit, DW_EH_PE_absptr, Reg(16),
- initial_opcodes, &eh_frame_data_);
+ WriteDebugFrameCIE(is64bit, DW_EH_PE_absptr, Reg(16),
+ initial_opcodes, kCFIFormat, &debug_frame_data_);
DebugFrameOpCodeWriter<> opcodes;
- std::vector<uintptr_t> eh_frame_patches;
+ std::vector<uintptr_t> debug_frame_patches;
std::vector<uintptr_t> expected_patches { 32 }; // NOLINT
- WriteEhFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000,
- opcodes.data(), &eh_frame_data_, &eh_frame_patches);
+ WriteDebugFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000,
+ opcodes.data(), kCFIFormat, &debug_frame_data_, &debug_frame_patches);
DW_CHECK("FDE cie=00000000 pc=100000000000000..300000000000000");
- EXPECT_EQ(expected_patches, eh_frame_patches);
+ EXPECT_EQ(expected_patches, debug_frame_patches);
CheckObjdumpOutput(is64bit, "-W");
}
@@ -173,11 +175,11 @@
DW_CHECK_NEXT("DW_CFA_offset: r14 (r14)");
DW_CHECK_NEXT("DW_CFA_offset: r15 (r15)");
DebugFrameOpCodeWriter<> initial_opcodes;
- WriteEhFrameCIE(is64bit, DW_EH_PE_absptr, Reg(16),
- initial_opcodes, &eh_frame_data_);
- std::vector<uintptr_t> eh_frame_patches;
- WriteEhFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000,
- opcodes.data(), &eh_frame_data_, &eh_frame_patches);
+ WriteDebugFrameCIE(is64bit, DW_EH_PE_absptr, Reg(16),
+ initial_opcodes, kCFIFormat, &debug_frame_data_);
+ std::vector<uintptr_t> debug_frame_patches;
+ WriteDebugFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000,
+ opcodes.data(), kCFIFormat, &debug_frame_data_, &debug_frame_patches);
CheckObjdumpOutput(is64bit, "-W");
}
diff --git a/compiler/dwarf/dwarf_test.h b/compiler/dwarf/dwarf_test.h
index 230ebe3..3afb5ea 100644
--- a/compiler/dwarf/dwarf_test.h
+++ b/compiler/dwarf/dwarf_test.h
@@ -69,7 +69,7 @@
RawSection debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
RawSection debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
RawSection debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
- RawSection eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0);
+ RawSection debug_frame(".debug_frame", SHT_PROGBITS, 0, nullptr, 0, 8, 0);
if (!debug_info_data_.empty()) {
debug_info.SetBuffer(debug_info_data_);
builder.RegisterSection(&debug_info);
@@ -86,9 +86,9 @@
debug_line.SetBuffer(debug_line_data_);
builder.RegisterSection(&debug_line);
}
- if (!eh_frame_data_.empty()) {
- eh_frame.SetBuffer(eh_frame_data_);
- builder.RegisterSection(&eh_frame);
+ if (!debug_frame_data_.empty()) {
+ debug_frame.SetBuffer(debug_frame_data_);
+ builder.RegisterSection(&debug_frame);
}
ScratchFile file;
builder.Write(file.GetFile());
@@ -167,7 +167,7 @@
}
// Buffers which are going to assembled into ELF file and passed to objdump.
- std::vector<uint8_t> eh_frame_data_;
+ std::vector<uint8_t> debug_frame_data_;
std::vector<uint8_t> debug_info_data_;
std::vector<uint8_t> debug_abbrev_data_;
std::vector<uint8_t> debug_str_data_;
diff --git a/compiler/dwarf/headers.h b/compiler/dwarf/headers.h
index 9f64766..ad315ee 100644
--- a/compiler/dwarf/headers.h
+++ b/compiler/dwarf/headers.h
@@ -35,17 +35,18 @@
// and compilers are expected *not* to use it by default.
// In particular, it is not related to machine architecture.
-// Write common information entry (CIE) to .eh_frame section.
+// Write common information entry (CIE) to .debug_frame or .eh_frame section.
template<typename Allocator>
-void WriteEhFrameCIE(bool is64bit,
- ExceptionHeaderValueApplication address_type,
- Reg return_address_register,
- const DebugFrameOpCodeWriter<Allocator>& opcodes,
- std::vector<uint8_t>* eh_frame) {
- Writer<> writer(eh_frame);
+void WriteDebugFrameCIE(bool is64bit,
+ ExceptionHeaderValueApplication address_type,
+ Reg return_address_register,
+ const DebugFrameOpCodeWriter<Allocator>& opcodes,
+ CFIFormat format,
+ std::vector<uint8_t>* debug_frame) {
+ Writer<> writer(debug_frame);
size_t cie_header_start_ = writer.data()->size();
writer.PushUint32(0); // Length placeholder.
- writer.PushUint32(0); // CIE id.
+ writer.PushUint32((format == DW_EH_FRAME_FORMAT) ? 0 : 0xFFFFFFFF); // CIE id.
writer.PushUint8(1); // Version.
writer.PushString("zR");
writer.PushUleb128(DebugFrameOpCodeWriter<Allocator>::kCodeAlignmentFactor);
@@ -62,20 +63,26 @@
writer.UpdateUint32(cie_header_start_, writer.data()->size() - cie_header_start_ - 4);
}
-// Write frame description entry (FDE) to .eh_frame section.
+// Write frame description entry (FDE) to .debug_frame or .eh_frame section.
template<typename Allocator>
-void WriteEhFrameFDE(bool is64bit, size_t cie_offset,
- uint64_t initial_address, uint64_t address_range,
- const std::vector<uint8_t, Allocator>* opcodes,
- std::vector<uint8_t>* eh_frame,
- std::vector<uintptr_t>* eh_frame_patches) {
- Writer<> writer(eh_frame);
+void WriteDebugFrameFDE(bool is64bit, size_t cie_offset,
+ uint64_t initial_address, uint64_t address_range,
+ const std::vector<uint8_t, Allocator>* opcodes,
+ CFIFormat format,
+ std::vector<uint8_t>* debug_frame,
+ std::vector<uintptr_t>* debug_frame_patches) {
+ Writer<> writer(debug_frame);
size_t fde_header_start = writer.data()->size();
writer.PushUint32(0); // Length placeholder.
- uint32_t cie_pointer = writer.data()->size() - cie_offset;
- writer.PushUint32(cie_pointer);
+ if (format == DW_EH_FRAME_FORMAT) {
+ uint32_t cie_pointer = writer.data()->size() - cie_offset;
+ writer.PushUint32(cie_pointer);
+ } else {
+ uint32_t cie_pointer = cie_offset;
+ writer.PushUint32(cie_pointer);
+ }
// Relocate initial_address, but not address_range (it is size).
- eh_frame_patches->push_back(writer.data()->size());
+ debug_frame_patches->push_back(writer.data()->size());
if (is64bit) {
writer.PushUint64(initial_address);
writer.PushUint64(address_range);
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index 63d3a0d..972bd08 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -56,12 +56,12 @@
public:
Section(const std::string& name, Elf_Word type, Elf_Word flags,
const Section* link, Elf_Word info, Elf_Word align, Elf_Word entsize)
- : header_(), section_index_(0), name_(name), link_(link) {
- header_.sh_type = type;
- header_.sh_flags = flags;
- header_.sh_info = info;
- header_.sh_addralign = align;
- header_.sh_entsize = entsize;
+ : header_(new Elf_Shdr()), section_index_(0), name_(name), link_(link) {
+ header_->sh_type = type;
+ header_->sh_flags = flags;
+ header_->sh_info = info;
+ header_->sh_addralign = align;
+ header_->sh_entsize = entsize;
}
virtual ~Section() {}
@@ -79,11 +79,11 @@
}
const Elf_Shdr* GetHeader() const {
- return &header_;
+ return header_.get();
}
Elf_Shdr* GetHeader() {
- return &header_;
+ return header_.get();
}
Elf_Word GetSectionIndex() const {
@@ -100,7 +100,9 @@
}
private:
- Elf_Shdr header_;
+ // Elf_Shdr is somewhat large so allocate it on the heap.
+ // Otherwise we get in trouble with stack frame sizes.
+ std::unique_ptr<Elf_Shdr> header_;
Elf_Word section_index_;
const std::string name_;
const Section* const link_;
diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc
index 5e9cf76..f4df6c1 100644
--- a/compiler/elf_writer_debug.cc
+++ b/compiler/elf_writer_debug.cc
@@ -29,9 +29,10 @@
namespace art {
namespace dwarf {
-static void WriteEhFrameCIE(InstructionSet isa,
- ExceptionHeaderValueApplication addr_type,
- std::vector<uint8_t>* eh_frame) {
+static void WriteDebugFrameCIE(InstructionSet isa,
+ ExceptionHeaderValueApplication addr_type,
+ CFIFormat format,
+ std::vector<uint8_t>* eh_frame) {
// 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);
@@ -57,7 +58,8 @@
}
}
auto return_reg = Reg::ArmCore(14); // R14(LR).
- WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame);
+ WriteDebugFrameCIE(is64bit, addr_type, return_reg,
+ opcodes, format, eh_frame);
return;
}
case kArm64: {
@@ -80,7 +82,8 @@
}
}
auto return_reg = Reg::Arm64Core(30); // R30(LR).
- WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame);
+ WriteDebugFrameCIE(is64bit, addr_type, return_reg,
+ opcodes, format, eh_frame);
return;
}
case kMips:
@@ -96,7 +99,8 @@
}
}
auto return_reg = Reg::MipsCore(31); // R31(RA).
- WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame);
+ WriteDebugFrameCIE(is64bit, addr_type, return_reg,
+ opcodes, format, eh_frame);
return;
}
case kX86: {
@@ -122,7 +126,8 @@
}
}
auto return_reg = Reg::X86Core(8); // R8(EIP).
- WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame);
+ WriteDebugFrameCIE(is64bit, addr_type, return_reg,
+ opcodes, format, eh_frame);
return;
}
case kX86_64: {
@@ -148,7 +153,8 @@
}
}
auto return_reg = Reg::X86_64Core(16); // R16(RIP).
- WriteEhFrameCIE(is64bit, addr_type, return_reg, opcodes, eh_frame);
+ WriteDebugFrameCIE(is64bit, addr_type, return_reg,
+ opcodes, format, eh_frame);
return;
}
case kNone:
@@ -158,58 +164,61 @@
UNREACHABLE();
}
-void WriteEhFrame(const CompilerDriver* compiler,
- const OatWriter* oat_writer,
- ExceptionHeaderValueApplication address_type,
- std::vector<uint8_t>* eh_frame,
- std::vector<uintptr_t>* eh_frame_patches,
- std::vector<uint8_t>* eh_frame_hdr,
- std::vector<uintptr_t>* eh_frame_hdr_patches) {
+void WriteCFISection(const CompilerDriver* compiler,
+ const OatWriter* oat_writer,
+ ExceptionHeaderValueApplication address_type,
+ CFIFormat format,
+ std::vector<uint8_t>* debug_frame,
+ std::vector<uintptr_t>* debug_frame_patches,
+ std::vector<uint8_t>* eh_frame_hdr,
+ std::vector<uintptr_t>* eh_frame_hdr_patches) {
const auto& method_infos = oat_writer->GetMethodDebugInfo();
const InstructionSet isa = compiler->GetInstructionSet();
- // Write .eh_frame section.
+ // Write .eh_frame/.debug_frame section.
std::map<uint32_t, size_t> address_to_fde_offset_map;
- size_t cie_offset = eh_frame->size();
- WriteEhFrameCIE(isa, address_type, eh_frame);
+ size_t cie_offset = debug_frame->size();
+ WriteDebugFrameCIE(isa, address_type, format, debug_frame);
for (const OatWriter::DebugInfo& mi : method_infos) {
if (!mi.deduped_) { // Only one FDE per unique address.
const SwapVector<uint8_t>* opcodes = mi.compiled_method_->GetCFIInfo();
if (opcodes != nullptr) {
- address_to_fde_offset_map.emplace(mi.low_pc_, eh_frame->size());
- WriteEhFrameFDE(Is64BitInstructionSet(isa), cie_offset,
- mi.low_pc_, mi.high_pc_ - mi.low_pc_,
- opcodes, eh_frame, eh_frame_patches);
+ address_to_fde_offset_map.emplace(mi.low_pc_, debug_frame->size());
+ WriteDebugFrameFDE(Is64BitInstructionSet(isa), cie_offset,
+ mi.low_pc_, mi.high_pc_ - mi.low_pc_,
+ opcodes, format, debug_frame, debug_frame_patches);
}
}
}
- // Write .eh_frame_hdr section.
- Writer<> header(eh_frame_hdr);
- 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 - .eh_frame_hdr section is after .eh_frame section
- const int32_t relative_eh_frame_begin = -static_cast<int32_t>(eh_frame->size());
- header.PushInt32(relative_eh_frame_begin - 4U);
- // Binary search table size (number of entries).
- header.PushUint32(dchecked_integral_cast<uint32_t>(address_to_fde_offset_map.size()));
- // Binary search table.
- for (const auto& address_to_fde_offset : address_to_fde_offset_map) {
- u_int32_t code_address = address_to_fde_offset.first;
- int32_t fde_address = dchecked_integral_cast<int32_t>(address_to_fde_offset.second);
- eh_frame_hdr_patches->push_back(header.data()->size());
- header.PushUint32(code_address);
- // We know the exact layout (eh_frame is immediately before eh_frame_hdr)
- // and the data is relative to the start of the eh_frame_hdr,
- // so patching isn't necessary (in contrast to the code address above).
- header.PushInt32(relative_eh_frame_begin + fde_address);
+ if (format == DW_EH_FRAME_FORMAT) {
+ // Write .eh_frame_hdr section.
+ Writer<> header(eh_frame_hdr);
+ 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 - .eh_frame_hdr section is after .eh_frame section
+ const int32_t relative_eh_frame_begin = -static_cast<int32_t>(debug_frame->size());
+ header.PushInt32(relative_eh_frame_begin - 4U);
+ // Binary search table size (number of entries).
+ header.PushUint32(dchecked_integral_cast<uint32_t>(address_to_fde_offset_map.size()));
+ // Binary search table.
+ for (const auto& address_to_fde_offset : address_to_fde_offset_map) {
+ u_int32_t code_address = address_to_fde_offset.first;
+ int32_t fde_address = dchecked_integral_cast<int32_t>(address_to_fde_offset.second);
+ eh_frame_hdr_patches->push_back(header.data()->size());
+ header.PushUint32(code_address);
+ // We know the exact layout (eh_frame is immediately before eh_frame_hdr)
+ // and the data is relative to the start of the eh_frame_hdr,
+ // so patching isn't necessary (in contrast to the code address above).
+ header.PushInt32(relative_eh_frame_begin + fde_address);
+ }
}
}
@@ -234,6 +243,7 @@
std::vector<uintptr_t>* debug_line_patches) {
const std::vector<OatWriter::DebugInfo>& method_infos = oat_writer->GetMethodDebugInfo();
const InstructionSet isa = compiler->GetInstructionSet();
+ const bool is64bit = Is64BitInstructionSet(isa);
// Find all addresses (low_pc) which contain deduped methods.
// The first instance of method is not marked deduped_, but the rest is.
@@ -271,7 +281,7 @@
}
size_t debug_abbrev_offset = debug_abbrev->size();
- DebugInfoEntryWriter<> info(false /* 32 bit */, debug_abbrev);
+ DebugInfoEntryWriter<> info(is64bit, debug_abbrev);
info.StartTag(DW_TAG_compile_unit, DW_CHILDREN_yes);
info.WriteStrp(DW_AT_producer, "Android dex2oat", debug_str);
info.WriteData1(DW_AT_language, DW_LANG_Java);
@@ -316,7 +326,7 @@
case kX86_64:
break;
}
- DebugLineOpCodeWriter<> opcodes(false /* 32bit */, code_factor_bits_);
+ DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits_);
opcodes.SetAddress(cunit_low_pc);
if (dwarf_isa != -1) {
opcodes.SetISA(dwarf_isa);
diff --git a/compiler/elf_writer_debug.h b/compiler/elf_writer_debug.h
index 28d0e2c..69f7e0d 100644
--- a/compiler/elf_writer_debug.h
+++ b/compiler/elf_writer_debug.h
@@ -25,13 +25,14 @@
namespace art {
namespace dwarf {
-void WriteEhFrame(const CompilerDriver* compiler,
- const OatWriter* oat_writer,
- ExceptionHeaderValueApplication address_type,
- std::vector<uint8_t>* eh_frame,
- std::vector<uintptr_t>* eh_frame_patches,
- std::vector<uint8_t>* eh_frame_hdr,
- std::vector<uintptr_t>* eh_frame_hdr_patches);
+void WriteCFISection(const CompilerDriver* compiler,
+ const OatWriter* oat_writer,
+ ExceptionHeaderValueApplication address_type,
+ CFIFormat format,
+ std::vector<uint8_t>* debug_frame,
+ std::vector<uintptr_t>* debug_frame_patches,
+ std::vector<uint8_t>* eh_frame_hdr,
+ std::vector<uintptr_t>* eh_frame_hdr_patches);
void WriteDebugSections(const CompilerDriver* compiler,
const OatWriter* oat_writer,
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 79f9955..96dd7ca 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -37,6 +37,14 @@
namespace art {
+// .eh_frame and .debug_frame are almost identical.
+// Except for some minor formatting differences, the main difference
+// is that .eh_frame is allocated within the running program because
+// it is used by C++ exception handling (which we do not use so we
+// can choose either). C++ compilers generally tend to use .eh_frame
+// because if they need it sometimes, they might as well always use it.
+constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_EH_FRAME_FORMAT;
+
template <typename ElfTypes>
bool ElfWriterQuick<ElfTypes>::Create(File* elf_file,
OatWriter* oat_writer,
@@ -161,12 +169,17 @@
using RawSection = typename ElfBuilder<ElfTypes>::RawSection;
const auto* text = builder->GetText();
const bool is64bit = Is64BitInstructionSet(isa);
+ const int pointer_size = GetInstructionSetPointerSize(isa);
RawSection eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0,
is64bit ? Patch<Elf_Addr, uint64_t, kPointerRelativeAddress> :
Patch<Elf_Addr, uint32_t, kPointerRelativeAddress>,
text);
RawSection eh_frame_hdr(".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0,
Patch<Elf_Addr, uint32_t, kSectionRelativeAddress>, text);
+ RawSection debug_frame(".debug_frame", SHT_PROGBITS, 0, nullptr, 0, pointer_size, 0,
+ is64bit ? Patch<Elf_Addr, uint64_t, kAbsoluteAddress> :
+ Patch<Elf_Addr, uint32_t, kAbsoluteAddress>,
+ text);
RawSection debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0,
Patch<Elf_Addr, uint32_t, kAbsoluteAddress>, text);
RawSection debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
@@ -175,12 +188,25 @@
Patch<Elf_Addr, uint32_t, kAbsoluteAddress>, text);
if (!oat_writer->GetMethodDebugInfo().empty()) {
if (compiler_driver_->GetCompilerOptions().GetIncludeCFI()) {
- dwarf::WriteEhFrame(
- compiler_driver_, oat_writer, dwarf::DW_EH_PE_pcrel,
- eh_frame.GetBuffer(), eh_frame.GetPatchLocations(),
- eh_frame_hdr.GetBuffer(), eh_frame_hdr.GetPatchLocations());
- builder->RegisterSection(&eh_frame);
- builder->RegisterSection(&eh_frame_hdr);
+ if (kCFIFormat == dwarf::DW_EH_FRAME_FORMAT) {
+ dwarf::WriteCFISection(
+ compiler_driver_, oat_writer,
+ dwarf::DW_EH_PE_pcrel, kCFIFormat,
+ eh_frame.GetBuffer(), eh_frame.GetPatchLocations(),
+ eh_frame_hdr.GetBuffer(), eh_frame_hdr.GetPatchLocations());
+ builder->RegisterSection(&eh_frame);
+ builder->RegisterSection(&eh_frame_hdr);
+ } else {
+ DCHECK(kCFIFormat == dwarf::DW_DEBUG_FRAME_FORMAT);
+ dwarf::WriteCFISection(
+ compiler_driver_, oat_writer,
+ dwarf::DW_EH_PE_absptr, kCFIFormat,
+ debug_frame.GetBuffer(), debug_frame.GetPatchLocations(),
+ nullptr, nullptr);
+ builder->RegisterSection(&debug_frame);
+ *oat_writer->GetAbsolutePatchLocationsFor(".debug_frame") =
+ *debug_frame.GetPatchLocations();
+ }
}
if (compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
// Add methods to .symtab.
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index a5c6f23..c4eaabf 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -539,11 +539,6 @@
}
static bool RequiresConstructorBarrier(const DexCompilationUnit* cu, const CompilerDriver& driver) {
- // dex compilation unit is null only when unit testing.
- if (cu == nullptr) {
- return false;
- }
-
Thread* self = Thread::Current();
return cu->IsConstructor()
&& driver.RequiresConstructorBarrier(self, cu->GetDexFile(), cu->GetClassDefIndex());
@@ -551,9 +546,12 @@
void HGraphBuilder::BuildReturn(const Instruction& instruction, Primitive::Type type) {
if (type == Primitive::kPrimVoid) {
- // Note that we might insert redundant barriers when inlining `super` calls.
- // TODO: add a data flow analysis to get rid of duplicate barriers.
- if (RequiresConstructorBarrier(dex_compilation_unit_, *compiler_driver_)) {
+ if (graph_->ShouldGenerateConstructorBarrier()) {
+ // The compilation unit is null during testing.
+ if (dex_compilation_unit_ != nullptr) {
+ DCHECK(RequiresConstructorBarrier(dex_compilation_unit_, *compiler_driver_))
+ << "Inconsistent use of ShouldGenerateConstructorBarrier. Should not generate a barrier.";
+ }
current_block_->AddInstruction(new (arena_) HMemoryBarrier(kStoreStore));
}
current_block_->AddInstruction(new (arena_) HReturnVoid());
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 0e776b3..d71266d 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -645,23 +645,34 @@
}
}
+ uint32_t outer_dex_pc = dex_pc;
+ uint32_t outer_environment_size = 0;
+ uint32_t inlining_depth = 0;
+ if (instruction != nullptr) {
+ for (HEnvironment* environment = instruction->GetEnvironment();
+ environment != nullptr;
+ environment = environment->GetParent()) {
+ outer_dex_pc = environment->GetDexPc();
+ outer_environment_size = environment->Size();
+ if (environment != instruction->GetEnvironment()) {
+ inlining_depth++;
+ }
+ }
+ }
+
// Collect PC infos for the mapping table.
struct PcInfo pc_info;
- pc_info.dex_pc = dex_pc;
+ pc_info.dex_pc = outer_dex_pc;
pc_info.native_pc = GetAssembler()->CodeSize();
pc_infos_.Add(pc_info);
- uint32_t inlining_depth = 0;
-
if (instruction == nullptr) {
// For stack overflow checks.
- stack_map_stream_.BeginStackMapEntry(dex_pc, pc_info.native_pc, 0, 0, 0, inlining_depth);
+ stack_map_stream_.BeginStackMapEntry(pc_info.dex_pc, pc_info.native_pc, 0, 0, 0, 0);
stack_map_stream_.EndStackMapEntry();
return;
}
LocationSummary* locations = instruction->GetLocations();
- HEnvironment* environment = instruction->GetEnvironment();
- size_t environment_size = instruction->EnvironmentSize();
uint32_t register_mask = locations->GetRegisterMask();
if (locations->OnlyCallsOnSlowPath()) {
@@ -674,23 +685,34 @@
}
// The register mask must be a subset of callee-save registers.
DCHECK_EQ(register_mask & core_callee_save_mask_, register_mask);
- stack_map_stream_.BeginStackMapEntry(dex_pc,
+ stack_map_stream_.BeginStackMapEntry(pc_info.dex_pc,
pc_info.native_pc,
register_mask,
locations->GetStackMask(),
- environment_size,
+ outer_environment_size,
inlining_depth);
- if (environment != nullptr) {
- // TODO: Handle parent environment.
- DCHECK(environment->GetParent() == nullptr);
- DCHECK_EQ(environment->GetDexPc(), dex_pc);
+
+ EmitEnvironment(instruction->GetEnvironment(), slow_path);
+ stack_map_stream_.EndStackMapEntry();
+}
+
+void CodeGenerator::EmitEnvironment(HEnvironment* environment, SlowPathCode* slow_path) {
+ if (environment == nullptr) return;
+
+ if (environment->GetParent() != nullptr) {
+ // We emit the parent environment first.
+ EmitEnvironment(environment->GetParent(), slow_path);
+ stack_map_stream_.BeginInlineInfoEntry(environment->GetMethodIdx(),
+ environment->GetDexPc(),
+ environment->GetInvokeType(),
+ environment->Size());
}
// Walk over the environment, and record the location of dex registers.
- for (size_t i = 0; i < environment_size; ++i) {
+ for (size_t i = 0, environment_size = environment->Size(); i < environment_size; ++i) {
HInstruction* current = environment->GetInstructionAt(i);
if (current == nullptr) {
- stack_map_stream_.AddDexRegisterEntry(i, DexRegisterLocation::Kind::kNone, 0);
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kNone, 0);
continue;
}
@@ -701,41 +723,44 @@
if (current->IsLongConstant()) {
int64_t value = current->AsLongConstant()->GetValue();
stack_map_stream_.AddDexRegisterEntry(
- i, DexRegisterLocation::Kind::kConstant, Low32Bits(value));
+ DexRegisterLocation::Kind::kConstant, Low32Bits(value));
stack_map_stream_.AddDexRegisterEntry(
- ++i, DexRegisterLocation::Kind::kConstant, High32Bits(value));
+ DexRegisterLocation::Kind::kConstant, High32Bits(value));
+ ++i;
DCHECK_LT(i, environment_size);
} else if (current->IsDoubleConstant()) {
int64_t value = bit_cast<int64_t, double>(current->AsDoubleConstant()->GetValue());
stack_map_stream_.AddDexRegisterEntry(
- i, DexRegisterLocation::Kind::kConstant, Low32Bits(value));
+ DexRegisterLocation::Kind::kConstant, Low32Bits(value));
stack_map_stream_.AddDexRegisterEntry(
- ++i, DexRegisterLocation::Kind::kConstant, High32Bits(value));
+ DexRegisterLocation::Kind::kConstant, High32Bits(value));
+ ++i;
DCHECK_LT(i, environment_size);
} else if (current->IsIntConstant()) {
int32_t value = current->AsIntConstant()->GetValue();
- stack_map_stream_.AddDexRegisterEntry(i, DexRegisterLocation::Kind::kConstant, value);
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kConstant, value);
} else if (current->IsNullConstant()) {
- stack_map_stream_.AddDexRegisterEntry(i, DexRegisterLocation::Kind::kConstant, 0);
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kConstant, 0);
} else {
DCHECK(current->IsFloatConstant()) << current->DebugName();
int32_t value = bit_cast<int32_t, float>(current->AsFloatConstant()->GetValue());
- stack_map_stream_.AddDexRegisterEntry(i, DexRegisterLocation::Kind::kConstant, value);
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kConstant, value);
}
break;
}
case Location::kStackSlot: {
stack_map_stream_.AddDexRegisterEntry(
- i, DexRegisterLocation::Kind::kInStack, location.GetStackIndex());
+ DexRegisterLocation::Kind::kInStack, location.GetStackIndex());
break;
}
case Location::kDoubleStackSlot: {
stack_map_stream_.AddDexRegisterEntry(
- i, DexRegisterLocation::Kind::kInStack, location.GetStackIndex());
+ DexRegisterLocation::Kind::kInStack, location.GetStackIndex());
stack_map_stream_.AddDexRegisterEntry(
- ++i, DexRegisterLocation::Kind::kInStack, location.GetHighStackIndex(kVRegSize));
+ DexRegisterLocation::Kind::kInStack, location.GetHighStackIndex(kVRegSize));
+ ++i;
DCHECK_LT(i, environment_size);
break;
}
@@ -744,16 +769,18 @@
int id = location.reg();
if (slow_path != nullptr && slow_path->IsCoreRegisterSaved(id)) {
uint32_t offset = slow_path->GetStackOffsetOfCoreRegister(id);
- stack_map_stream_.AddDexRegisterEntry(i, DexRegisterLocation::Kind::kInStack, offset);
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack, offset);
if (current->GetType() == Primitive::kPrimLong) {
stack_map_stream_.AddDexRegisterEntry(
- ++i, DexRegisterLocation::Kind::kInStack, offset + kVRegSize);
+ DexRegisterLocation::Kind::kInStack, offset + kVRegSize);
+ ++i;
DCHECK_LT(i, environment_size);
}
} else {
- stack_map_stream_.AddDexRegisterEntry(i, DexRegisterLocation::Kind::kInRegister, id);
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInRegister, id);
if (current->GetType() == Primitive::kPrimLong) {
- stack_map_stream_.AddDexRegisterEntry(++i, DexRegisterLocation::Kind::kInRegister, id);
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInRegister, id);
+ ++i;
DCHECK_LT(i, environment_size);
}
}
@@ -764,17 +791,18 @@
int id = location.reg();
if (slow_path != nullptr && slow_path->IsFpuRegisterSaved(id)) {
uint32_t offset = slow_path->GetStackOffsetOfFpuRegister(id);
- stack_map_stream_.AddDexRegisterEntry(i, DexRegisterLocation::Kind::kInStack, offset);
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack, offset);
if (current->GetType() == Primitive::kPrimDouble) {
stack_map_stream_.AddDexRegisterEntry(
- ++i, DexRegisterLocation::Kind::kInStack, offset + kVRegSize);
+ DexRegisterLocation::Kind::kInStack, offset + kVRegSize);
+ ++i;
DCHECK_LT(i, environment_size);
}
} else {
- stack_map_stream_.AddDexRegisterEntry(i, DexRegisterLocation::Kind::kInFpuRegister, id);
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInFpuRegister, id);
if (current->GetType() == Primitive::kPrimDouble) {
- stack_map_stream_.AddDexRegisterEntry(
- ++i, DexRegisterLocation::Kind::kInFpuRegister, id);
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInFpuRegister, id);
+ ++i;
DCHECK_LT(i, environment_size);
}
}
@@ -786,16 +814,17 @@
int high = location.high();
if (slow_path != nullptr && slow_path->IsFpuRegisterSaved(low)) {
uint32_t offset = slow_path->GetStackOffsetOfFpuRegister(low);
- stack_map_stream_.AddDexRegisterEntry(i, DexRegisterLocation::Kind::kInStack, offset);
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack, offset);
} else {
- stack_map_stream_.AddDexRegisterEntry(i, DexRegisterLocation::Kind::kInFpuRegister, low);
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInFpuRegister, low);
}
if (slow_path != nullptr && slow_path->IsFpuRegisterSaved(high)) {
uint32_t offset = slow_path->GetStackOffsetOfFpuRegister(high);
- stack_map_stream_.AddDexRegisterEntry(++i, DexRegisterLocation::Kind::kInStack, offset);
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack, offset);
+ ++i;
} else {
- stack_map_stream_.AddDexRegisterEntry(
- ++i, DexRegisterLocation::Kind::kInFpuRegister, high);
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInFpuRegister, high);
+ ++i;
}
DCHECK_LT(i, environment_size);
break;
@@ -806,23 +835,23 @@
int high = location.high();
if (slow_path != nullptr && slow_path->IsCoreRegisterSaved(low)) {
uint32_t offset = slow_path->GetStackOffsetOfCoreRegister(low);
- stack_map_stream_.AddDexRegisterEntry(i, DexRegisterLocation::Kind::kInStack, offset);
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack, offset);
} else {
- stack_map_stream_.AddDexRegisterEntry(i, DexRegisterLocation::Kind::kInRegister, low);
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInRegister, low);
}
if (slow_path != nullptr && slow_path->IsCoreRegisterSaved(high)) {
uint32_t offset = slow_path->GetStackOffsetOfCoreRegister(high);
- stack_map_stream_.AddDexRegisterEntry(++i, DexRegisterLocation::Kind::kInStack, offset);
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack, offset);
} else {
- stack_map_stream_.AddDexRegisterEntry(
- ++i, DexRegisterLocation::Kind::kInRegister, high);
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInRegister, high);
}
+ ++i;
DCHECK_LT(i, environment_size);
break;
}
case Location::kInvalid: {
- stack_map_stream_.AddDexRegisterEntry(i, DexRegisterLocation::Kind::kNone, 0);
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kNone, 0);
break;
}
@@ -830,7 +859,10 @@
LOG(FATAL) << "Unexpected kind " << location.GetKind();
}
}
- stack_map_stream_.EndStackMapEntry();
+
+ if (environment->GetParent() != nullptr) {
+ stack_map_stream_.EndInlineInfoEntry();
+ }
}
bool CodeGenerator::CanMoveNullCheckToUser(HNullCheck* null_check) {
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index bdbd571..740beab 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -34,10 +34,15 @@
// Binary encoding of 2^31 for type double.
static int64_t constexpr k2Pow31EncodingForDouble = INT64_C(0x41E0000000000000);
+// Minimum value for a primitive integer.
+static int32_t constexpr kPrimIntMin = 0x80000000;
+// Minimum value for a primitive long.
+static int64_t constexpr kPrimLongMin = INT64_C(0x8000000000000000);
+
// Maximum value for a primitive integer.
static int32_t constexpr kPrimIntMax = 0x7fffffff;
// Maximum value for a primitive long.
-static int64_t constexpr kPrimLongMax = 0x7fffffffffffffff;
+static int64_t constexpr kPrimLongMax = INT64_C(0x7fffffffffffffff);
class Assembler;
class CodeGenerator;
@@ -77,8 +82,8 @@
virtual void EmitNativeCode(CodeGenerator* codegen) = 0;
- void SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations);
- void RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations);
+ virtual void SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations);
+ virtual void RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations);
void RecordPcInfo(CodeGenerator* codegen, HInstruction* instruction, uint32_t dex_pc);
bool IsCoreRegisterSaved(int reg) const {
@@ -97,11 +102,13 @@
return saved_fpu_stack_offsets_[reg];
}
- private:
+ protected:
static constexpr size_t kMaximumNumberOfExpectedRegisters = 32;
static constexpr uint32_t kRegisterNotSaved = -1;
uint32_t saved_core_stack_offsets_[kMaximumNumberOfExpectedRegisters];
uint32_t saved_fpu_stack_offsets_[kMaximumNumberOfExpectedRegisters];
+
+ private:
DISALLOW_COPY_AND_ASSIGN(SlowPathCode);
};
@@ -436,6 +443,7 @@
size_t GetStackOffsetOfSavedRegister(size_t index);
void CompileInternal(CodeAllocator* allocator, bool is_baseline);
void BlockIfInRegister(Location location, bool is_out = false) const;
+ void EmitEnvironment(HEnvironment* environment, SlowPathCode* slow_path);
HGraph* const graph_;
const CompilerOptions& compiler_options_;
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index f56e446..1c76630 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -17,6 +17,7 @@
#include "code_generator_arm.h"
#include "arch/arm/instruction_set_features_arm.h"
+#include "code_generator_utils.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "gc/accounting/card_table.h"
#include "intrinsics.h"
@@ -347,11 +348,11 @@
}
void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const {
- stream << ArmManagedRegister::FromCoreRegister(Register(reg));
+ stream << Register(reg);
}
void CodeGeneratorARM::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
- stream << ArmManagedRegister::FromSRegister(SRegister(reg));
+ stream << SRegister(reg);
}
size_t CodeGeneratorARM::SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
@@ -390,7 +391,7 @@
location_builder_(graph, this),
instruction_visitor_(graph, this),
move_resolver_(graph->GetArena(), this),
- assembler_(true),
+ assembler_(false /* can_relocate_branches */),
isa_features_(isa_features) {
// Save the PC register to mimic Quick.
AddAllocatedRegister(Location::RegisterLocation(PC));
@@ -2185,11 +2186,134 @@
}
}
+void InstructionCodeGeneratorARM::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv() || instruction->IsRem());
+ DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
+
+ LocationSummary* locations = instruction->GetLocations();
+ Location second = locations->InAt(1);
+ DCHECK(second.IsConstant());
+
+ Register out = locations->Out().AsRegister<Register>();
+ Register dividend = locations->InAt(0).AsRegister<Register>();
+ int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+ DCHECK(imm == 1 || imm == -1);
+
+ if (instruction->IsRem()) {
+ __ LoadImmediate(out, 0);
+ } else {
+ if (imm == 1) {
+ __ Mov(out, dividend);
+ } else {
+ __ rsb(out, dividend, ShifterOperand(0));
+ }
+ }
+}
+
+void InstructionCodeGeneratorARM::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv() || instruction->IsRem());
+ DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
+
+ LocationSummary* locations = instruction->GetLocations();
+ Location second = locations->InAt(1);
+ DCHECK(second.IsConstant());
+
+ Register out = locations->Out().AsRegister<Register>();
+ Register dividend = locations->InAt(0).AsRegister<Register>();
+ Register temp = locations->GetTemp(0).AsRegister<Register>();
+ int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+ int32_t abs_imm = std::abs(imm);
+ DCHECK(IsPowerOfTwo(abs_imm));
+ int ctz_imm = CTZ(abs_imm);
+
+ if (ctz_imm == 1) {
+ __ Lsr(temp, dividend, 32 - ctz_imm);
+ } else {
+ __ Asr(temp, dividend, 31);
+ __ Lsr(temp, temp, 32 - ctz_imm);
+ }
+ __ add(out, temp, ShifterOperand(dividend));
+
+ if (instruction->IsDiv()) {
+ __ Asr(out, out, ctz_imm);
+ if (imm < 0) {
+ __ rsb(out, out, ShifterOperand(0));
+ }
+ } else {
+ __ ubfx(out, out, 0, ctz_imm);
+ __ sub(out, out, ShifterOperand(temp));
+ }
+}
+
+void InstructionCodeGeneratorARM::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv() || instruction->IsRem());
+ DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
+
+ LocationSummary* locations = instruction->GetLocations();
+ Location second = locations->InAt(1);
+ DCHECK(second.IsConstant());
+
+ Register out = locations->Out().AsRegister<Register>();
+ Register dividend = locations->InAt(0).AsRegister<Register>();
+ Register temp1 = locations->GetTemp(0).AsRegister<Register>();
+ Register temp2 = locations->GetTemp(1).AsRegister<Register>();
+ int64_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+
+ int64_t magic;
+ int shift;
+ CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
+
+ __ LoadImmediate(temp1, magic);
+ __ smull(temp2, temp1, dividend, temp1);
+
+ if (imm > 0 && magic < 0) {
+ __ add(temp1, temp1, ShifterOperand(dividend));
+ } else if (imm < 0 && magic > 0) {
+ __ sub(temp1, temp1, ShifterOperand(dividend));
+ }
+
+ if (shift != 0) {
+ __ Asr(temp1, temp1, shift);
+ }
+
+ if (instruction->IsDiv()) {
+ __ sub(out, temp1, ShifterOperand(temp1, ASR, 31));
+ } else {
+ __ sub(temp1, temp1, ShifterOperand(temp1, ASR, 31));
+ // TODO: Strength reduction for mls.
+ __ LoadImmediate(temp2, imm);
+ __ mls(out, temp1, temp2, dividend);
+ }
+}
+
+void InstructionCodeGeneratorARM::GenerateDivRemConstantIntegral(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv() || instruction->IsRem());
+ DCHECK(instruction->GetResultType() == Primitive::kPrimInt);
+
+ LocationSummary* locations = instruction->GetLocations();
+ Location second = locations->InAt(1);
+ DCHECK(second.IsConstant());
+
+ int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+ if (imm == 0) {
+ // Do not generate anything. DivZeroCheck would prevent any code to be executed.
+ } else if (imm == 1 || imm == -1) {
+ DivRemOneOrMinusOne(instruction);
+ } else if (IsPowerOfTwo(std::abs(imm))) {
+ DivRemByPowerOfTwo(instruction);
+ } else {
+ DCHECK(imm <= -2 || imm >= 2);
+ GenerateDivRemWithAnyConstant(instruction);
+ }
+}
+
void LocationsBuilderARM::VisitDiv(HDiv* div) {
LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
if (div->GetResultType() == Primitive::kPrimLong) {
// pLdiv runtime call.
call_kind = LocationSummary::kCall;
+ } else if (div->GetResultType() == Primitive::kPrimInt && div->InputAt(1)->IsConstant()) {
+ // sdiv will be replaced by other instruction sequence.
} else if (div->GetResultType() == Primitive::kPrimInt &&
!codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
// pIdivmod runtime call.
@@ -2200,7 +2324,20 @@
switch (div->GetResultType()) {
case Primitive::kPrimInt: {
- if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
+ if (div->InputAt(1)->IsConstant()) {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(div->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ int32_t abs_imm = std::abs(div->InputAt(1)->AsIntConstant()->GetValue());
+ if (abs_imm <= 1) {
+ // No temp register required.
+ } else {
+ locations->AddTemp(Location::RequiresRegister());
+ if (!IsPowerOfTwo(abs_imm)) {
+ locations->AddTemp(Location::RequiresRegister());
+ }
+ }
+ } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
@@ -2244,7 +2381,9 @@
switch (div->GetResultType()) {
case Primitive::kPrimInt: {
- if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
+ if (second.IsConstant()) {
+ GenerateDivRemConstantIntegral(div);
+ } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
__ sdiv(out.AsRegister<Register>(),
first.AsRegister<Register>(),
second.AsRegister<Register>());
@@ -2296,8 +2435,11 @@
// Most remainders are implemented in the runtime.
LocationSummary::CallKind call_kind = LocationSummary::kCall;
- if (rem->GetResultType() == Primitive::kPrimInt &&
- codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
+ if (rem->GetResultType() == Primitive::kPrimInt && rem->InputAt(1)->IsConstant()) {
+ // sdiv will be replaced by other instruction sequence.
+ call_kind = LocationSummary::kNoCall;
+ } else if ((rem->GetResultType() == Primitive::kPrimInt)
+ && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
// Have hardware divide instruction for int, do it with three instructions.
call_kind = LocationSummary::kNoCall;
}
@@ -2306,7 +2448,20 @@
switch (type) {
case Primitive::kPrimInt: {
- if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
+ if (rem->InputAt(1)->IsConstant()) {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(rem->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ int32_t abs_imm = std::abs(rem->InputAt(1)->AsIntConstant()->GetValue());
+ if (abs_imm <= 1) {
+ // No temp register required.
+ } else {
+ locations->AddTemp(Location::RequiresRegister());
+ if (!IsPowerOfTwo(abs_imm)) {
+ locations->AddTemp(Location::RequiresRegister());
+ }
+ }
+ } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
@@ -2363,7 +2518,9 @@
Primitive::Type type = rem->GetResultType();
switch (type) {
case Primitive::kPrimInt: {
- if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
+ if (second.IsConstant()) {
+ GenerateDivRemConstantIntegral(rem);
+ } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
Register reg1 = first.AsRegister<Register>();
Register reg2 = second.AsRegister<Register>();
Register temp = locations->GetTemp(0).AsRegister<Register>();
@@ -2880,7 +3037,8 @@
}
void InstructionCodeGeneratorARM::HandleFieldSet(HInstruction* instruction,
- const FieldInfo& field_info) {
+ const FieldInfo& field_info,
+ bool value_can_be_null) {
DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
LocationSummary* locations = instruction->GetLocations();
@@ -2969,7 +3127,8 @@
if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
Register temp = locations->GetTemp(0).AsRegister<Register>();
Register card = locations->GetTemp(1).AsRegister<Register>();
- codegen_->MarkGCCard(temp, card, base, value.AsRegister<Register>());
+ codegen_->MarkGCCard(
+ temp, card, base, value.AsRegister<Register>(), value_can_be_null);
}
if (is_volatile) {
@@ -3096,7 +3255,7 @@
}
void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -3120,7 +3279,7 @@
}
void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) {
@@ -3390,7 +3549,7 @@
DCHECK_EQ(value_type, Primitive::kPrimNot);
Register temp = locations->GetTemp(0).AsRegister<Register>();
Register card = locations->GetTemp(1).AsRegister<Register>();
- codegen_->MarkGCCard(temp, card, obj, value);
+ codegen_->MarkGCCard(temp, card, obj, value, instruction->GetValueCanBeNull());
}
} else {
DCHECK_EQ(value_type, Primitive::kPrimNot);
@@ -3495,13 +3654,21 @@
__ b(slow_path->GetEntryLabel(), CS);
}
-void CodeGeneratorARM::MarkGCCard(Register temp, Register card, Register object, Register value) {
+void CodeGeneratorARM::MarkGCCard(Register temp,
+ Register card,
+ Register object,
+ Register value,
+ bool can_be_null) {
Label is_null;
- __ CompareAndBranchIfZero(value, &is_null);
+ if (can_be_null) {
+ __ CompareAndBranchIfZero(value, &is_null);
+ }
__ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmWordSize>().Int32Value());
__ Lsr(temp, object, gc::accounting::CardTable::kCardShift);
__ strb(card, Address(card, temp));
- __ Bind(&is_null);
+ if (can_be_null) {
+ __ Bind(&is_null);
+ }
}
void LocationsBuilderARM::VisitTemporary(HTemporary* temp) {
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 1a498e1..071bbee 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -181,7 +181,9 @@
HInstruction* instruction);
void GenerateWideAtomicLoad(Register addr, uint32_t offset,
Register out_lo, Register out_hi);
- void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+ void HandleFieldSet(HInstruction* instruction,
+ const FieldInfo& field_info,
+ bool value_can_be_null);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
void GenerateImplicitNullCheck(HNullCheck* instruction);
void GenerateExplicitNullCheck(HNullCheck* instruction);
@@ -189,6 +191,10 @@
Label* true_target,
Label* false_target,
Label* always_true_target);
+ void DivRemOneOrMinusOne(HBinaryOperation* instruction);
+ void DivRemByPowerOfTwo(HBinaryOperation* instruction);
+ void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
+ void GenerateDivRemConstantIntegral(HBinaryOperation* instruction);
ArmAssembler* const assembler_;
CodeGeneratorARM* const codegen_;
@@ -270,7 +276,7 @@
int32_t offset, HInstruction* instruction, uint32_t dex_pc, SlowPathCode* slow_path);
// Emit a write barrier.
- void MarkGCCard(Register temp, Register card, Register object, Register value);
+ void MarkGCCard(Register temp, Register card, Register object, Register value, bool can_be_null);
Label* GetLabelOf(HBasicBlock* block) const {
return CommonGetLabelOf<Label>(block_labels_.GetRawStorage(), block);
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 0222f93..b6d99ab 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -17,6 +17,7 @@
#include "code_generator_arm64.h"
#include "arch/arm64/instruction_set_features_arm64.h"
+#include "code_generator_utils.h"
#include "common_arm64.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
@@ -64,6 +65,7 @@
using helpers::WRegisterFrom;
using helpers::XRegisterFrom;
using helpers::ARM64EncodableConstantOrRegister;
+using helpers::ArtVixlRegCodeCoherentForRegSet;
static constexpr size_t kHeapRefSize = sizeof(mirror::HeapReference<mirror::Object>);
static constexpr int kCurrentMethodStackOffset = 0;
@@ -105,6 +107,88 @@
#define __ down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler()->
#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, x).Int32Value()
+// Calculate memory accessing operand for save/restore live registers.
+static void SaveRestoreLiveRegistersHelper(CodeGenerator* codegen,
+ RegisterSet* register_set,
+ int64_t spill_offset,
+ bool is_save) {
+ DCHECK(ArtVixlRegCodeCoherentForRegSet(register_set->GetCoreRegisters(),
+ codegen->GetNumberOfCoreRegisters(),
+ register_set->GetFloatingPointRegisters(),
+ codegen->GetNumberOfFloatingPointRegisters()));
+
+ CPURegList core_list = CPURegList(CPURegister::kRegister, kXRegSize,
+ register_set->GetCoreRegisters() & (~callee_saved_core_registers.list()));
+ CPURegList fp_list = CPURegList(CPURegister::kFPRegister, kDRegSize,
+ register_set->GetFloatingPointRegisters() & (~callee_saved_fp_registers.list()));
+
+ MacroAssembler* masm = down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler();
+ UseScratchRegisterScope temps(masm);
+
+ Register base = masm->StackPointer();
+ int64_t core_spill_size = core_list.TotalSizeInBytes();
+ int64_t fp_spill_size = fp_list.TotalSizeInBytes();
+ int64_t reg_size = kXRegSizeInBytes;
+ int64_t max_ls_pair_offset = spill_offset + core_spill_size + fp_spill_size - 2 * reg_size;
+ uint32_t ls_access_size = WhichPowerOf2(reg_size);
+ if (((core_list.Count() > 1) || (fp_list.Count() > 1)) &&
+ !masm->IsImmLSPair(max_ls_pair_offset, ls_access_size)) {
+ // If the offset does not fit in the instruction's immediate field, use an alternate register
+ // to compute the base address(float point registers spill base address).
+ Register new_base = temps.AcquireSameSizeAs(base);
+ __ Add(new_base, base, Operand(spill_offset + core_spill_size));
+ base = new_base;
+ spill_offset = -core_spill_size;
+ int64_t new_max_ls_pair_offset = fp_spill_size - 2 * reg_size;
+ DCHECK(masm->IsImmLSPair(spill_offset, ls_access_size));
+ DCHECK(masm->IsImmLSPair(new_max_ls_pair_offset, ls_access_size));
+ }
+
+ if (is_save) {
+ __ StoreCPURegList(core_list, MemOperand(base, spill_offset));
+ __ StoreCPURegList(fp_list, MemOperand(base, spill_offset + core_spill_size));
+ } else {
+ __ LoadCPURegList(core_list, MemOperand(base, spill_offset));
+ __ LoadCPURegList(fp_list, MemOperand(base, spill_offset + core_spill_size));
+ }
+}
+
+void SlowPathCodeARM64::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
+ RegisterSet* register_set = locations->GetLiveRegisters();
+ size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
+ for (size_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
+ if (!codegen->IsCoreCalleeSaveRegister(i) && register_set->ContainsCoreRegister(i)) {
+ // If the register holds an object, update the stack mask.
+ if (locations->RegisterContainsObject(i)) {
+ locations->SetStackBit(stack_offset / kVRegSize);
+ }
+ DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
+ DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
+ saved_core_stack_offsets_[i] = stack_offset;
+ stack_offset += kXRegSizeInBytes;
+ }
+ }
+
+ for (size_t i = 0, e = codegen->GetNumberOfFloatingPointRegisters(); i < e; ++i) {
+ if (!codegen->IsFloatingPointCalleeSaveRegister(i) &&
+ register_set->ContainsFloatingPointRegister(i)) {
+ DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
+ DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
+ saved_fpu_stack_offsets_[i] = stack_offset;
+ stack_offset += kDRegSizeInBytes;
+ }
+ }
+
+ SaveRestoreLiveRegistersHelper(codegen, register_set,
+ codegen->GetFirstRegisterSlotInSlowPath(), true /* is_save */);
+}
+
+void SlowPathCodeARM64::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
+ RegisterSet* register_set = locations->GetLiveRegisters();
+ SaveRestoreLiveRegistersHelper(codegen, register_set,
+ codegen->GetFirstRegisterSlotInSlowPath(), false /* is_save */);
+}
+
class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 {
public:
BoundsCheckSlowPathARM64(HBoundsCheck* instruction,
@@ -529,6 +613,19 @@
GetAssembler()->cfi().DefCFAOffset(GetFrameSize());
}
+vixl::CPURegList CodeGeneratorARM64::GetFramePreservedCoreRegisters() const {
+ DCHECK(ArtVixlRegCodeCoherentForRegSet(core_spill_mask_, GetNumberOfCoreRegisters(), 0, 0));
+ return vixl::CPURegList(vixl::CPURegister::kRegister, vixl::kXRegSize,
+ core_spill_mask_);
+}
+
+vixl::CPURegList CodeGeneratorARM64::GetFramePreservedFPRegisters() const {
+ DCHECK(ArtVixlRegCodeCoherentForRegSet(0, 0, fpu_spill_mask_,
+ GetNumberOfFloatingPointRegisters()));
+ return vixl::CPURegList(vixl::CPURegister::kFPRegister, vixl::kDRegSize,
+ fpu_spill_mask_);
+}
+
void CodeGeneratorARM64::Bind(HBasicBlock* block) {
__ Bind(GetLabelOf(block));
}
@@ -604,16 +701,20 @@
return Location::NoLocation();
}
-void CodeGeneratorARM64::MarkGCCard(Register object, Register value) {
+void CodeGeneratorARM64::MarkGCCard(Register object, Register value, bool value_can_be_null) {
UseScratchRegisterScope temps(GetVIXLAssembler());
Register card = temps.AcquireX();
Register temp = temps.AcquireW(); // Index within the CardTable - 32bit.
vixl::Label done;
- __ Cbz(value, &done);
+ if (value_can_be_null) {
+ __ Cbz(value, &done);
+ }
__ Ldr(card, MemOperand(tr, Thread::CardTableOffset<kArm64WordSize>().Int32Value()));
__ Lsr(temp, object, gc::accounting::CardTable::kCardShift);
__ Strb(card, MemOperand(card, temp.X()));
- __ Bind(&done);
+ if (value_can_be_null) {
+ __ Bind(&done);
+ }
}
void CodeGeneratorARM64::SetupBlockedRegisters(bool is_baseline) const {
@@ -691,11 +792,11 @@
}
void CodeGeneratorARM64::DumpCoreRegister(std::ostream& stream, int reg) const {
- stream << Arm64ManagedRegister::FromXRegister(XRegister(reg));
+ stream << XRegister(reg);
}
void CodeGeneratorARM64::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
- stream << Arm64ManagedRegister::FromDRegister(DRegister(reg));
+ stream << DRegister(reg);
}
void CodeGeneratorARM64::MoveConstant(CPURegister destination, HConstant* constant) {
@@ -979,14 +1080,12 @@
BlockPoolsScope block_pools(GetVIXLAssembler());
__ Ldr(lr, MemOperand(tr, entry_point_offset));
__ Blr(lr);
- if (instruction != nullptr) {
- RecordPcInfo(instruction, dex_pc, slow_path);
- DCHECK(instruction->IsSuspendCheck()
- || instruction->IsBoundsCheck()
- || instruction->IsNullCheck()
- || instruction->IsDivZeroCheck()
- || !IsLeafMethod());
- }
+ RecordPcInfo(instruction, dex_pc, slow_path);
+ DCHECK(instruction->IsSuspendCheck()
+ || instruction->IsBoundsCheck()
+ || instruction->IsNullCheck()
+ || instruction->IsDivZeroCheck()
+ || !IsLeafMethod());
}
void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path,
@@ -1170,7 +1269,8 @@
}
void InstructionCodeGeneratorARM64::HandleFieldSet(HInstruction* instruction,
- const FieldInfo& field_info) {
+ const FieldInfo& field_info,
+ bool value_can_be_null) {
DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
BlockPoolsScope block_pools(GetVIXLAssembler());
@@ -1196,7 +1296,7 @@
}
if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
- codegen_->MarkGCCard(obj, Register(value));
+ codegen_->MarkGCCard(obj, Register(value), value_can_be_null);
}
}
@@ -1422,7 +1522,7 @@
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
if (CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue())) {
- codegen_->MarkGCCard(obj, value.W());
+ codegen_->MarkGCCard(obj, value.W(), instruction->GetValueCanBeNull());
}
}
}
@@ -1605,6 +1705,152 @@
#undef DEFINE_CONDITION_VISITORS
#undef FOR_EACH_CONDITION_INSTRUCTION
+void InstructionCodeGeneratorARM64::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv() || instruction->IsRem());
+
+ LocationSummary* locations = instruction->GetLocations();
+ Location second = locations->InAt(1);
+ DCHECK(second.IsConstant());
+
+ Register out = OutputRegister(instruction);
+ Register dividend = InputRegisterAt(instruction, 0);
+ int64_t imm = Int64FromConstant(second.GetConstant());
+ DCHECK(imm == 1 || imm == -1);
+
+ if (instruction->IsRem()) {
+ __ Mov(out, 0);
+ } else {
+ if (imm == 1) {
+ __ Mov(out, dividend);
+ } else {
+ __ Neg(out, dividend);
+ }
+ }
+}
+
+void InstructionCodeGeneratorARM64::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv() || instruction->IsRem());
+
+ LocationSummary* locations = instruction->GetLocations();
+ Location second = locations->InAt(1);
+ DCHECK(second.IsConstant());
+
+ Register out = OutputRegister(instruction);
+ Register dividend = InputRegisterAt(instruction, 0);
+ int64_t imm = Int64FromConstant(second.GetConstant());
+ int64_t abs_imm = std::abs(imm);
+ DCHECK(IsPowerOfTwo(abs_imm));
+ int ctz_imm = CTZ(abs_imm);
+
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ Register temp = temps.AcquireSameSizeAs(out);
+
+ if (instruction->IsDiv()) {
+ __ Add(temp, dividend, abs_imm - 1);
+ __ Cmp(dividend, 0);
+ __ Csel(out, temp, dividend, lt);
+ if (imm > 0) {
+ __ Asr(out, out, ctz_imm);
+ } else {
+ __ Neg(out, Operand(out, ASR, ctz_imm));
+ }
+ } else {
+ int bits = instruction->GetResultType() == Primitive::kPrimInt ? 32 : 64;
+ __ Asr(temp, dividend, bits - 1);
+ __ Lsr(temp, temp, bits - ctz_imm);
+ __ Add(out, dividend, temp);
+ __ And(out, out, abs_imm - 1);
+ __ Sub(out, out, temp);
+ }
+}
+
+void InstructionCodeGeneratorARM64::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv() || instruction->IsRem());
+
+ LocationSummary* locations = instruction->GetLocations();
+ Location second = locations->InAt(1);
+ DCHECK(second.IsConstant());
+
+ Register out = OutputRegister(instruction);
+ Register dividend = InputRegisterAt(instruction, 0);
+ int64_t imm = Int64FromConstant(second.GetConstant());
+
+ Primitive::Type type = instruction->GetResultType();
+ DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
+
+ int64_t magic;
+ int shift;
+ CalculateMagicAndShiftForDivRem(imm, type == Primitive::kPrimLong /* is_long */, &magic, &shift);
+
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ Register temp = temps.AcquireSameSizeAs(out);
+
+ // temp = get_high(dividend * magic)
+ __ Mov(temp, magic);
+ if (type == Primitive::kPrimLong) {
+ __ Smulh(temp, dividend, temp);
+ } else {
+ __ Smull(temp.X(), dividend, temp);
+ __ Lsr(temp.X(), temp.X(), 32);
+ }
+
+ if (imm > 0 && magic < 0) {
+ __ Add(temp, temp, dividend);
+ } else if (imm < 0 && magic > 0) {
+ __ Sub(temp, temp, dividend);
+ }
+
+ if (shift != 0) {
+ __ Asr(temp, temp, shift);
+ }
+
+ if (instruction->IsDiv()) {
+ __ Sub(out, temp, Operand(temp, ASR, type == Primitive::kPrimLong ? 63 : 31));
+ } else {
+ __ Sub(temp, temp, Operand(temp, ASR, type == Primitive::kPrimLong ? 63 : 31));
+ // TODO: Strength reduction for msub.
+ Register temp_imm = temps.AcquireSameSizeAs(out);
+ __ Mov(temp_imm, imm);
+ __ Msub(out, temp, temp_imm, dividend);
+ }
+}
+
+void InstructionCodeGeneratorARM64::GenerateDivRemIntegral(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv() || instruction->IsRem());
+ Primitive::Type type = instruction->GetResultType();
+ DCHECK(type == Primitive::kPrimInt || Primitive::kPrimLong);
+
+ LocationSummary* locations = instruction->GetLocations();
+ Register out = OutputRegister(instruction);
+ Location second = locations->InAt(1);
+
+ if (second.IsConstant()) {
+ int64_t imm = Int64FromConstant(second.GetConstant());
+
+ if (imm == 0) {
+ // Do not generate anything. DivZeroCheck would prevent any code to be executed.
+ } else if (imm == 1 || imm == -1) {
+ DivRemOneOrMinusOne(instruction);
+ } else if (IsPowerOfTwo(std::abs(imm))) {
+ DivRemByPowerOfTwo(instruction);
+ } else {
+ DCHECK(imm <= -2 || imm >= 2);
+ GenerateDivRemWithAnyConstant(instruction);
+ }
+ } else {
+ Register dividend = InputRegisterAt(instruction, 0);
+ Register divisor = InputRegisterAt(instruction, 1);
+ if (instruction->IsDiv()) {
+ __ Sdiv(out, dividend, divisor);
+ } else {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ Register temp = temps.AcquireSameSizeAs(out);
+ __ Sdiv(temp, dividend, divisor);
+ __ Msub(out, temp, divisor, dividend);
+ }
+ }
+}
+
void LocationsBuilderARM64::VisitDiv(HDiv* div) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(div, LocationSummary::kNoCall);
@@ -1612,7 +1858,7 @@
case Primitive::kPrimInt:
case Primitive::kPrimLong:
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(div->InputAt(1)));
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
break;
@@ -1633,7 +1879,7 @@
switch (type) {
case Primitive::kPrimInt:
case Primitive::kPrimLong:
- __ Sdiv(OutputRegister(div), InputRegisterAt(div, 0), InputRegisterAt(div, 1));
+ GenerateDivRemIntegral(div);
break;
case Primitive::kPrimFloat:
@@ -1848,7 +2094,7 @@
}
void InstructionCodeGeneratorARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) {
@@ -2456,7 +2702,7 @@
case Primitive::kPrimInt:
case Primitive::kPrimLong:
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(rem->InputAt(1)));
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
break;
@@ -2481,14 +2727,7 @@
switch (type) {
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
- UseScratchRegisterScope temps(GetVIXLAssembler());
- Register dividend = InputRegisterAt(rem, 0);
- Register divisor = InputRegisterAt(rem, 1);
- Register output = OutputRegister(rem);
- Register temp = temps.AcquireSameSizeAs(output);
-
- __ Sdiv(temp, dividend, divisor);
- __ Msub(output, temp, divisor, dividend);
+ GenerateDivRemIntegral(rem);
break;
}
@@ -2598,7 +2837,7 @@
}
void InstructionCodeGeneratorARM64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
void LocationsBuilderARM64::VisitSuspendCheck(HSuspendCheck* instruction) {
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 8aeea54..b56ca10 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -70,6 +70,9 @@
vixl::Label* GetEntryLabel() { return &entry_label_; }
vixl::Label* GetExitLabel() { return &exit_label_; }
+ void SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) OVERRIDE;
+ void RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) OVERRIDE;
+
private:
vixl::Label entry_label_;
vixl::Label exit_label_;
@@ -154,7 +157,9 @@
void GenerateMemoryBarrier(MemBarrierKind kind);
void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
void HandleBinaryOp(HBinaryOperation* instr);
- void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+ void HandleFieldSet(HInstruction* instruction,
+ const FieldInfo& field_info,
+ bool value_can_be_null);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
void HandleShift(HBinaryOperation* instr);
void GenerateImplicitNullCheck(HNullCheck* instruction);
@@ -163,6 +168,11 @@
vixl::Label* true_target,
vixl::Label* false_target,
vixl::Label* always_true_target);
+ void DivRemOneOrMinusOne(HBinaryOperation* instruction);
+ void DivRemByPowerOfTwo(HBinaryOperation* instruction);
+ void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
+ void GenerateDivRemIntegral(HBinaryOperation* instruction);
+
Arm64Assembler* const assembler_;
CodeGeneratorARM64* const codegen_;
@@ -227,15 +237,8 @@
void GenerateFrameEntry() OVERRIDE;
void GenerateFrameExit() OVERRIDE;
- vixl::CPURegList GetFramePreservedCoreRegisters() const {
- return vixl::CPURegList(vixl::CPURegister::kRegister, vixl::kXRegSize,
- core_spill_mask_);
- }
-
- vixl::CPURegList GetFramePreservedFPRegisters() const {
- return vixl::CPURegList(vixl::CPURegister::kFPRegister, vixl::kDRegSize,
- fpu_spill_mask_);
- }
+ vixl::CPURegList GetFramePreservedCoreRegisters() const;
+ vixl::CPURegList GetFramePreservedFPRegisters() const;
void Bind(HBasicBlock* block) OVERRIDE;
@@ -266,7 +269,7 @@
vixl::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->vixl_masm_; }
// Emit a write barrier.
- void MarkGCCard(vixl::Register object, vixl::Register value);
+ void MarkGCCard(vixl::Register object, vixl::Register value, bool value_can_be_null);
// Register allocation.
@@ -277,10 +280,10 @@
Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
- size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id);
- size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id);
- size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id);
- size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id);
+ size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+ size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+ size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+ size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
// The number of registers that can be allocated. The register allocator may
// decide to reserve and not use a few of them.
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 2848a48..a6f01da 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -340,11 +340,11 @@
}
void CodeGeneratorX86::DumpCoreRegister(std::ostream& stream, int reg) const {
- stream << X86ManagedRegister::FromCpuRegister(Register(reg));
+ stream << Register(reg);
}
void CodeGeneratorX86::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
- stream << X86ManagedRegister::FromXmmRegister(XmmRegister(reg));
+ stream << XmmRegister(reg);
}
size_t CodeGeneratorX86::SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
@@ -3227,16 +3227,24 @@
DCHECK(!IsLeafMethod());
}
-void CodeGeneratorX86::MarkGCCard(Register temp, Register card, Register object, Register value) {
+void CodeGeneratorX86::MarkGCCard(Register temp,
+ Register card,
+ Register object,
+ Register value,
+ bool value_can_be_null) {
Label is_null;
- __ testl(value, value);
- __ j(kEqual, &is_null);
+ if (value_can_be_null) {
+ __ testl(value, value);
+ __ j(kEqual, &is_null);
+ }
__ fs()->movl(card, Address::Absolute(Thread::CardTableOffset<kX86WordSize>().Int32Value()));
__ movl(temp, object);
__ shrl(temp, Immediate(gc::accounting::CardTable::kCardShift));
__ movb(Address(temp, card, TIMES_1, 0),
X86ManagedRegister::FromCpuRegister(card).AsByteRegister());
- __ Bind(&is_null);
+ if (value_can_be_null) {
+ __ Bind(&is_null);
+ }
}
void LocationsBuilderX86::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) {
@@ -3381,7 +3389,8 @@
}
void InstructionCodeGeneratorX86::HandleFieldSet(HInstruction* instruction,
- const FieldInfo& field_info) {
+ const FieldInfo& field_info,
+ bool value_can_be_null) {
DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
LocationSummary* locations = instruction->GetLocations();
@@ -3454,7 +3463,7 @@
if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
Register temp = locations->GetTemp(0).AsRegister<Register>();
Register card = locations->GetTemp(1).AsRegister<Register>();
- codegen_->MarkGCCard(temp, card, base, value.AsRegister<Register>());
+ codegen_->MarkGCCard(temp, card, base, value.AsRegister<Register>(), value_can_be_null);
}
if (is_volatile) {
@@ -3475,7 +3484,7 @@
}
void InstructionCodeGeneratorX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
@@ -3483,7 +3492,7 @@
}
void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
void LocationsBuilderX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -3817,7 +3826,8 @@
if (needs_write_barrier) {
Register temp = locations->GetTemp(0).AsRegister<Register>();
Register card = locations->GetTemp(1).AsRegister<Register>();
- codegen_->MarkGCCard(temp, card, obj, value.AsRegister<Register>());
+ codegen_->MarkGCCard(
+ temp, card, obj, value.AsRegister<Register>(), instruction->GetValueCanBeNull());
}
} else {
DCHECK_EQ(value_type, Primitive::kPrimNot);
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 5a5a37b..28766d8 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -170,7 +170,9 @@
void GenerateShrLong(const Location& loc, int shift);
void GenerateUShrLong(const Location& loc, int shift);
void GenerateMemoryBarrier(MemBarrierKind kind);
- void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+ void HandleFieldSet(HInstruction* instruction,
+ const FieldInfo& field_info,
+ bool value_can_be_null);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
// Push value to FPU stack. `is_fp` specifies whether the value is floating point or not.
// `is_wide` specifies whether it is long/double or not.
@@ -260,7 +262,11 @@
void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Register temp);
// Emit a write barrier.
- void MarkGCCard(Register temp, Register card, Register object, Register value);
+ void MarkGCCard(Register temp,
+ Register card,
+ Register object,
+ Register value,
+ bool value_can_be_null);
void LoadCurrentMethod(Register reg);
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index e633970..f49c26d 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -396,11 +396,11 @@
}
void CodeGeneratorX86_64::DumpCoreRegister(std::ostream& stream, int reg) const {
- stream << X86_64ManagedRegister::FromCpuRegister(Register(reg));
+ stream << Register(reg);
}
void CodeGeneratorX86_64::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
- stream << X86_64ManagedRegister::FromXmmRegister(FloatRegister(reg));
+ stream << FloatRegister(reg);
}
size_t CodeGeneratorX86_64::SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
@@ -3249,7 +3249,8 @@
}
void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction,
- const FieldInfo& field_info) {
+ const FieldInfo& field_info,
+ bool value_can_be_null) {
DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
LocationSummary* locations = instruction->GetLocations();
@@ -3329,7 +3330,7 @@
if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
CpuRegister card = locations->GetTemp(1).AsRegister<CpuRegister>();
- codegen_->MarkGCCard(temp, card, base, value.AsRegister<CpuRegister>());
+ codegen_->MarkGCCard(temp, card, base, value.AsRegister<CpuRegister>(), value_can_be_null);
}
if (is_volatile) {
@@ -3342,7 +3343,7 @@
}
void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
void LocationsBuilderX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -3366,7 +3367,7 @@
}
void InstructionCodeGeneratorX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) {
@@ -3671,7 +3672,8 @@
DCHECK_EQ(value_type, Primitive::kPrimNot);
CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
CpuRegister card = locations->GetTemp(1).AsRegister<CpuRegister>();
- codegen_->MarkGCCard(temp, card, obj, value.AsRegister<CpuRegister>());
+ codegen_->MarkGCCard(
+ temp, card, obj, value.AsRegister<CpuRegister>(), instruction->GetValueCanBeNull());
}
} else {
DCHECK_EQ(value_type, Primitive::kPrimNot);
@@ -3816,16 +3818,21 @@
void CodeGeneratorX86_64::MarkGCCard(CpuRegister temp,
CpuRegister card,
CpuRegister object,
- CpuRegister value) {
+ CpuRegister value,
+ bool value_can_be_null) {
Label is_null;
- __ testl(value, value);
- __ j(kEqual, &is_null);
+ if (value_can_be_null) {
+ __ testl(value, value);
+ __ j(kEqual, &is_null);
+ }
__ gs()->movq(card, Address::Absolute(
Thread::CardTableOffset<kX86_64WordSize>().Int32Value(), true));
__ movq(temp, object);
__ shrq(temp, Immediate(gc::accounting::CardTable::kCardShift));
__ movb(Address(temp, card, TIMES_1, 0), card);
- __ Bind(&is_null);
+ if (value_can_be_null) {
+ __ Bind(&is_null);
+ }
}
void LocationsBuilderX86_64::VisitTemporary(HTemporary* temp) {
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 480ea6b..d7bd525 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -174,7 +174,9 @@
void GenerateDivRemIntegral(HBinaryOperation* instruction);
void HandleShift(HBinaryOperation* operation);
void GenerateMemoryBarrier(MemBarrierKind kind);
- void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+ void HandleFieldSet(HInstruction* instruction,
+ const FieldInfo& field_info,
+ bool value_can_be_null);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
void GenerateImplicitNullCheck(HNullCheck* instruction);
void GenerateExplicitNullCheck(HNullCheck* instruction);
@@ -248,7 +250,11 @@
}
// Emit a write barrier.
- void MarkGCCard(CpuRegister temp, CpuRegister card, CpuRegister object, CpuRegister value);
+ void MarkGCCard(CpuRegister temp,
+ CpuRegister card,
+ CpuRegister object,
+ CpuRegister value,
+ bool value_can_be_null);
// Helper method to move a value between two locations.
void Move(Location destination, Location source);
diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h
index 53f1f3c..246fff9 100644
--- a/compiler/optimizing/common_arm64.h
+++ b/compiler/optimizing/common_arm64.h
@@ -218,6 +218,28 @@
return Location::RequiresRegister();
}
+// Check if registers in art register set have the same register code in vixl. If the register
+// codes are same, we can initialize vixl register list simply by the register masks. Currently,
+// only SP/WSP and ZXR/WZR codes are different between art and vixl.
+// Note: This function is only used for debug checks.
+static inline bool ArtVixlRegCodeCoherentForRegSet(uint32_t art_core_registers,
+ size_t num_core,
+ uint32_t art_fpu_registers,
+ size_t num_fpu) {
+ // The register masks won't work if the number of register is larger than 32.
+ DCHECK_GE(sizeof(art_core_registers) * 8, num_core);
+ DCHECK_GE(sizeof(art_fpu_registers) * 8, num_fpu);
+ for (size_t art_reg_code = 0; art_reg_code < num_core; ++art_reg_code) {
+ if (RegisterSet::Contains(art_core_registers, art_reg_code)) {
+ if (art_reg_code != static_cast<size_t>(VIXLRegCodeFromART(art_reg_code))) {
+ return false;
+ }
+ }
+ }
+ // There is no register code translation for float registers.
+ return true;
+}
+
} // namespace helpers
} // namespace arm64
} // namespace art
diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc
index b7a92b5..20ce110 100644
--- a/compiler/optimizing/constant_folding.cc
+++ b/compiler/optimizing/constant_folding.cc
@@ -28,6 +28,7 @@
void VisitShift(HBinaryOperation* shift);
void VisitAnd(HAnd* instruction) OVERRIDE;
+ void VisitCompare(HCompare* instruction) OVERRIDE;
void VisitMul(HMul* instruction) OVERRIDE;
void VisitOr(HOr* instruction) OVERRIDE;
void VisitRem(HRem* instruction) OVERRIDE;
@@ -70,6 +71,14 @@
inst->ReplaceWith(constant);
inst->GetBlock()->RemoveInstruction(inst);
}
+ } else if (inst->IsTypeConversion()) {
+ // Constant folding: replace `TypeConversion(a)' with a constant at
+ // compile time if `a' is a constant.
+ HConstant* constant = inst->AsTypeConversion()->TryStaticEvaluation();
+ if (constant != nullptr) {
+ inst->ReplaceWith(constant);
+ inst->GetBlock()->RemoveInstruction(inst);
+ }
} else if (inst->IsDivZeroCheck()) {
// We can safely remove the check if the input is a non-null constant.
HDivZeroCheck* check = inst->AsDivZeroCheck();
@@ -108,6 +117,26 @@
}
}
+void InstructionWithAbsorbingInputSimplifier::VisitCompare(HCompare* instruction) {
+ HConstant* input_cst = instruction->GetConstantRight();
+ if (input_cst != nullptr) {
+ HInstruction* input_value = instruction->GetLeastConstantLeft();
+ if (Primitive::IsFloatingPointType(input_value->GetType()) &&
+ ((input_cst->IsFloatConstant() && input_cst->AsFloatConstant()->IsNaN()) ||
+ (input_cst->IsDoubleConstant() && input_cst->AsDoubleConstant()->IsNaN()))) {
+ // Replace code looking like
+ // CMP{G,L} dst, src, NaN
+ // with
+ // CONSTANT +1 (gt bias)
+ // or
+ // CONSTANT -1 (lt bias)
+ instruction->ReplaceWith(GetGraph()->GetConstant(Primitive::kPrimInt,
+ (instruction->IsGtBias() ? 1 : -1)));
+ instruction->GetBlock()->RemoveInstruction(instruction);
+ }
+ }
+}
+
void InstructionWithAbsorbingInputSimplifier::VisitMul(HMul* instruction) {
HConstant* input_cst = instruction->GetConstantRight();
Primitive::Type type = instruction->GetType();
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
index 6fbe75e..b31de98 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -17,6 +17,7 @@
#include "dead_code_elimination.h"
#include "base/bit_vector-inl.h"
+#include "ssa_phi_elimination.h"
namespace art {
@@ -132,6 +133,7 @@
void HDeadCodeElimination::Run() {
RemoveDeadBlocks();
+ SsaRedundantPhiElimination(graph_).Run();
RemoveDeadInstructions();
}
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index f5c630b..7da4f2d 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -24,8 +24,68 @@
#include "register_allocator.h"
#include "ssa_liveness_analysis.h"
+#include <cctype>
+#include <sstream>
+
namespace art {
+static bool HasWhitespace(const char* str) {
+ DCHECK(str != nullptr);
+ while (str[0] != 0) {
+ if (isspace(str[0])) {
+ return true;
+ }
+ str++;
+ }
+ return false;
+}
+
+class StringList {
+ public:
+ enum Format {
+ kArrayBrackets,
+ kSetBrackets,
+ };
+
+ // Create an empty list
+ explicit StringList(Format format = kArrayBrackets) : format_(format), is_empty_(true) {}
+
+ // Construct StringList from a linked list. List element class T
+ // must provide methods `GetNext` and `Dump`.
+ template<class T>
+ explicit StringList(T* first_entry, Format format = kArrayBrackets) : StringList(format) {
+ for (T* current = first_entry; current != nullptr; current = current->GetNext()) {
+ current->Dump(NewEntryStream());
+ }
+ }
+
+ std::ostream& NewEntryStream() {
+ if (is_empty_) {
+ is_empty_ = false;
+ } else {
+ sstream_ << ",";
+ }
+ return sstream_;
+ }
+
+ private:
+ Format format_;
+ bool is_empty_;
+ std::ostringstream sstream_;
+
+ friend std::ostream& operator<<(std::ostream& os, const StringList& list);
+};
+
+std::ostream& operator<<(std::ostream& os, const StringList& list) {
+ switch (list.format_) {
+ case StringList::kArrayBrackets: return os << "[" << list.sstream_.str() << "]";
+ case StringList::kSetBrackets: return os << "{" << list.sstream_.str() << "}";
+ default:
+ LOG(FATAL) << "Invalid StringList format";
+ UNREACHABLE();
+ }
+}
+
/**
* HGraph visitor to generate a file suitable for the c1visualizer tool and IRHydra.
*/
@@ -125,76 +185,99 @@
output_<< std::endl;
}
- void DumpLocation(Location location) {
+ void DumpLocation(std::ostream& stream, const Location& location) {
if (location.IsRegister()) {
- codegen_.DumpCoreRegister(output_, location.reg());
+ codegen_.DumpCoreRegister(stream, location.reg());
} else if (location.IsFpuRegister()) {
- codegen_.DumpFloatingPointRegister(output_, location.reg());
+ codegen_.DumpFloatingPointRegister(stream, location.reg());
} else if (location.IsConstant()) {
- output_ << "constant";
+ stream << "#";
HConstant* constant = location.GetConstant();
if (constant->IsIntConstant()) {
- output_ << " " << constant->AsIntConstant()->GetValue();
+ stream << constant->AsIntConstant()->GetValue();
} else if (constant->IsLongConstant()) {
- output_ << " " << constant->AsLongConstant()->GetValue();
+ stream << constant->AsLongConstant()->GetValue();
}
} else if (location.IsInvalid()) {
- output_ << "invalid";
+ stream << "invalid";
} else if (location.IsStackSlot()) {
- output_ << location.GetStackIndex() << "(sp)";
+ stream << location.GetStackIndex() << "(sp)";
} else if (location.IsFpuRegisterPair()) {
- codegen_.DumpFloatingPointRegister(output_, location.low());
- output_ << " and ";
- codegen_.DumpFloatingPointRegister(output_, location.high());
+ codegen_.DumpFloatingPointRegister(stream, location.low());
+ stream << "|";
+ codegen_.DumpFloatingPointRegister(stream, location.high());
} else if (location.IsRegisterPair()) {
- codegen_.DumpCoreRegister(output_, location.low());
- output_ << " and ";
- codegen_.DumpCoreRegister(output_, location.high());
+ codegen_.DumpCoreRegister(stream, location.low());
+ stream << "|";
+ codegen_.DumpCoreRegister(stream, location.high());
} else if (location.IsUnallocated()) {
- output_ << "<U>";
+ stream << "unallocated";
} else {
DCHECK(location.IsDoubleStackSlot());
- output_ << "2x" << location.GetStackIndex() << "(sp)";
+ stream << "2x" << location.GetStackIndex() << "(sp)";
}
}
+ std::ostream& StartAttributeStream(const char* name = nullptr) {
+ if (name == nullptr) {
+ output_ << " ";
+ } else {
+ DCHECK(!HasWhitespace(name)) << "Checker does not allow spaces in attributes";
+ output_ << " " << name << ":";
+ }
+ return output_;
+ }
+
void VisitParallelMove(HParallelMove* instruction) OVERRIDE {
- output_ << " (";
+ StartAttributeStream("liveness") << instruction->GetLifetimePosition();
+ StringList moves;
for (size_t i = 0, e = instruction->NumMoves(); i < e; ++i) {
MoveOperands* move = instruction->MoveOperandsAt(i);
- DumpLocation(move->GetSource());
- output_ << " -> ";
- DumpLocation(move->GetDestination());
- if (i + 1 != e) {
- output_ << ", ";
- }
+ std::ostream& str = moves.NewEntryStream();
+ DumpLocation(str, move->GetSource());
+ str << "->";
+ DumpLocation(str, move->GetDestination());
}
- output_ << ")";
- output_ << " (liveness: " << instruction->GetLifetimePosition() << ")";
+ StartAttributeStream("moves") << moves;
}
void VisitIntConstant(HIntConstant* instruction) OVERRIDE {
- output_ << " " << instruction->GetValue();
+ StartAttributeStream() << instruction->GetValue();
}
void VisitLongConstant(HLongConstant* instruction) OVERRIDE {
- output_ << " " << instruction->GetValue();
+ StartAttributeStream() << instruction->GetValue();
}
void VisitFloatConstant(HFloatConstant* instruction) OVERRIDE {
- output_ << " " << instruction->GetValue();
+ StartAttributeStream() << instruction->GetValue();
}
void VisitDoubleConstant(HDoubleConstant* instruction) OVERRIDE {
- output_ << " " << instruction->GetValue();
+ StartAttributeStream() << instruction->GetValue();
}
void VisitPhi(HPhi* phi) OVERRIDE {
- output_ << " " << phi->GetRegNumber();
+ StartAttributeStream("reg") << phi->GetRegNumber();
}
void VisitMemoryBarrier(HMemoryBarrier* barrier) OVERRIDE {
- output_ << " " << barrier->GetBarrierKind();
+ StartAttributeStream("kind") << barrier->GetBarrierKind();
+ }
+
+ void VisitLoadClass(HLoadClass* load_cass) OVERRIDE {
+ StartAttributeStream("gen_clinit_check") << std::boolalpha
+ << load_cass->MustGenerateClinitCheck() << std::noboolalpha;
+ }
+
+ void VisitCheckCast(HCheckCast* check_cast) OVERRIDE {
+ StartAttributeStream("must_do_null_check") << std::boolalpha
+ << check_cast->MustDoNullCheck() << std::noboolalpha;
+ }
+
+ void VisitInstanceOf(HInstanceOf* instance_of) OVERRIDE {
+ StartAttributeStream("must_do_null_check") << std::boolalpha
+ << instance_of->MustDoNullCheck() << std::noboolalpha;
}
bool IsPass(const char* name) {
@@ -203,65 +286,66 @@
void PrintInstruction(HInstruction* instruction) {
output_ << instruction->DebugName();
- instruction->Accept(this);
if (instruction->InputCount() > 0) {
- output_ << " [ ";
- for (HInputIterator inputs(instruction); !inputs.Done(); inputs.Advance()) {
- output_ << GetTypeId(inputs.Current()->GetType()) << inputs.Current()->GetId() << " ";
+ StringList inputs;
+ for (HInputIterator it(instruction); !it.Done(); it.Advance()) {
+ inputs.NewEntryStream() << GetTypeId(it.Current()->GetType()) << it.Current()->GetId();
}
- output_ << "]";
+ StartAttributeStream() << inputs;
}
+ instruction->Accept(this);
if (instruction->HasEnvironment()) {
- output_ << " (env:";
+ StringList envs;
for (HEnvironment* environment = instruction->GetEnvironment();
environment != nullptr;
environment = environment->GetParent()) {
- output_ << " [ ";
+ StringList vregs;
for (size_t i = 0, e = environment->Size(); i < e; ++i) {
HInstruction* insn = environment->GetInstructionAt(i);
if (insn != nullptr) {
- output_ << GetTypeId(insn->GetType()) << insn->GetId() << " ";
+ vregs.NewEntryStream() << GetTypeId(insn->GetType()) << insn->GetId();
} else {
- output_ << " _ ";
+ vregs.NewEntryStream() << "_";
}
}
- output_ << "]";
+ envs.NewEntryStream() << vregs;
}
- output_ << ")";
+ StartAttributeStream("env") << envs;
}
if (IsPass(SsaLivenessAnalysis::kLivenessPassName)
&& is_after_pass_
&& instruction->GetLifetimePosition() != kNoLifetime) {
- output_ << " (liveness: " << instruction->GetLifetimePosition();
+ StartAttributeStream("liveness") << instruction->GetLifetimePosition();
if (instruction->HasLiveInterval()) {
- output_ << " ";
- const LiveInterval& interval = *instruction->GetLiveInterval();
- interval.Dump(output_);
+ LiveInterval* interval = instruction->GetLiveInterval();
+ StartAttributeStream("ranges")
+ << StringList(interval->GetFirstRange(), StringList::kSetBrackets);
+ StartAttributeStream("uses") << StringList(interval->GetFirstUse());
+ StartAttributeStream("env_uses") << StringList(interval->GetFirstEnvironmentUse());
+ StartAttributeStream("is_fixed") << interval->IsFixed();
+ StartAttributeStream("is_split") << interval->IsSplit();
+ StartAttributeStream("is_low") << interval->IsLowInterval();
+ StartAttributeStream("is_high") << interval->IsHighInterval();
}
- output_ << ")";
} else if (IsPass(RegisterAllocator::kRegisterAllocatorPassName) && is_after_pass_) {
+ StartAttributeStream("liveness") << instruction->GetLifetimePosition();
LocationSummary* locations = instruction->GetLocations();
if (locations != nullptr) {
- output_ << " ( ";
+ StringList inputs;
for (size_t i = 0; i < instruction->InputCount(); ++i) {
- DumpLocation(locations->InAt(i));
- output_ << " ";
+ DumpLocation(inputs.NewEntryStream(), locations->InAt(i));
}
- output_ << ")";
- if (locations->Out().IsValid()) {
- output_ << " -> ";
- DumpLocation(locations->Out());
- }
+ std::ostream& attr = StartAttributeStream("locations");
+ attr << inputs << "->";
+ DumpLocation(attr, locations->Out());
}
- output_ << " (liveness: " << instruction->GetLifetimePosition() << ")";
} else if (IsPass(LICM::kLoopInvariantCodeMotionPassName)
|| IsPass(HDeadCodeElimination::kFinalDeadCodeEliminationPassName)) {
- output_ << " ( loop_header:";
HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
if (info == nullptr) {
- output_ << "null )";
+ StartAttributeStream("loop") << "none";
} else {
- output_ << "B" << info->GetHeader()->GetBlockId() << " )";
+ StartAttributeStream("loop") << "B" << info->GetHeader()->GetBlockId();
}
}
}
@@ -281,7 +365,7 @@
output_ << bci << " " << num_uses << " "
<< GetTypeId(instruction->GetType()) << instruction->GetId() << " ";
PrintInstruction(instruction);
- output_ << kEndInstructionMarker << std::endl;
+ output_ << " " << kEndInstructionMarker << std::endl;
}
}
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index afffc7a..47c6318 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -169,10 +169,31 @@
resolved_method->GetAccessFlags(),
nullptr);
+ bool requires_ctor_barrier = false;
+
+ if (dex_compilation_unit.IsConstructor()) {
+ // If it's a super invocation and we already generate a barrier there's no need
+ // to generate another one.
+ // We identify super calls by looking at the "this" pointer. If its value is the
+ // same as the local "this" pointer then we must have a super invocation.
+ bool is_super_invocation = invoke_instruction->InputAt(0)->IsParameterValue()
+ && invoke_instruction->InputAt(0)->AsParameterValue()->IsThis();
+ if (is_super_invocation && graph_->ShouldGenerateConstructorBarrier()) {
+ requires_ctor_barrier = false;
+ } else {
+ Thread* self = Thread::Current();
+ requires_ctor_barrier = compiler_driver_->RequiresConstructorBarrier(self,
+ dex_compilation_unit.GetDexFile(),
+ dex_compilation_unit.GetClassDefIndex());
+ }
+ }
+
HGraph* callee_graph = new (graph_->GetArena()) HGraph(
graph_->GetArena(),
caller_dex_file,
method_index,
+ requires_ctor_barrier,
+ invoke_instruction->GetOriginalInvokeType(),
graph_->IsDebuggable(),
graph_->GetCurrentInstructionId());
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 46fad17..fcb3471 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -45,6 +45,8 @@
void VisitEqual(HEqual* equal) OVERRIDE;
void VisitNotEqual(HNotEqual* equal) OVERRIDE;
void VisitBooleanNot(HBooleanNot* bool_not) OVERRIDE;
+ void VisitInstanceFieldSet(HInstanceFieldSet* equal) OVERRIDE;
+ void VisitStaticFieldSet(HStaticFieldSet* equal) OVERRIDE;
void VisitArraySet(HArraySet* equal) OVERRIDE;
void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE;
void VisitNullCheck(HNullCheck* instruction) OVERRIDE;
@@ -63,6 +65,7 @@
void VisitUShr(HUShr* instruction) OVERRIDE;
void VisitXor(HXor* instruction) OVERRIDE;
void VisitInstanceOf(HInstanceOf* instruction) OVERRIDE;
+ bool IsDominatedByInputNullCheck(HInstruction* instr);
OptimizingCompilerStats* stats_;
bool simplification_occurred_ = false;
@@ -78,6 +81,8 @@
}
void InstructionSimplifierVisitor::Run() {
+ // Iterate in reverse post order to open up more simplifications to users
+ // of instructions that got simplified.
for (HReversePostOrderIterator it(*GetGraph()); !it.Done();) {
// The simplification of an instruction to another instruction may yield
// possibilities for other simplifications. So although we perform a reverse
@@ -170,9 +175,20 @@
}
}
+bool InstructionSimplifierVisitor::IsDominatedByInputNullCheck(HInstruction* instr) {
+ HInstruction* input = instr->InputAt(0);
+ for (HUseIterator<HInstruction*> it(input->GetUses()); !it.Done(); it.Advance()) {
+ HInstruction* use = it.Current()->GetUser();
+ if (use->IsNullCheck() && use->StrictlyDominates(instr)) {
+ return true;
+ }
+ }
+ return false;
+}
+
void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) {
HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
- if (!check_cast->InputAt(0)->CanBeNull()) {
+ if (!check_cast->InputAt(0)->CanBeNull() || IsDominatedByInputNullCheck(check_cast)) {
check_cast->ClearMustDoNullCheck();
}
@@ -194,11 +210,25 @@
}
void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) {
- if (!instruction->InputAt(0)->CanBeNull()) {
+ if (!instruction->InputAt(0)->CanBeNull() || IsDominatedByInputNullCheck(instruction)) {
instruction->ClearMustDoNullCheck();
}
}
+void InstructionSimplifierVisitor::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
+ if ((instruction->GetValue()->GetType() == Primitive::kPrimNot)
+ && !instruction->GetValue()->CanBeNull()) {
+ instruction->ClearValueCanBeNull();
+ }
+}
+
+void InstructionSimplifierVisitor::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+ if ((instruction->GetValue()->GetType() == Primitive::kPrimNot)
+ && !instruction->GetValue()->CanBeNull()) {
+ instruction->ClearValueCanBeNull();
+ }
+}
+
void InstructionSimplifierVisitor::VisitSuspendCheck(HSuspendCheck* check) {
HBasicBlock* block = check->GetBlock();
// Currently always keep the suspend check at entry.
@@ -294,6 +324,10 @@
instruction->ClearNeedsTypeCheck();
}
}
+
+ if (!value->CanBeNull()) {
+ instruction->ClearValueCanBeNull();
+ }
}
void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruction) {
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index dccfe9a..e785bf9 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -657,7 +657,8 @@
if (type == Primitive::kPrimNot) {
Register temp = locations->GetTemp(0).AsRegister<Register>();
Register card = locations->GetTemp(1).AsRegister<Register>();
- codegen->MarkGCCard(temp, card, base, value);
+ bool value_can_be_null = true; // TODO: Worth finding out this information?
+ codegen->MarkGCCard(temp, card, base, value, value_can_be_null);
}
}
@@ -725,7 +726,8 @@
if (type == Primitive::kPrimNot) {
// Mark card for object assuming new value is stored. Worst case we will mark an unchanged
// object and scan the receiver at the next GC for nothing.
- codegen->MarkGCCard(tmp_ptr, tmp_lo, base, value_lo);
+ bool value_can_be_null = true; // TODO: Worth finding out this information?
+ codegen->MarkGCCard(tmp_ptr, tmp_lo, base, value_lo, value_can_be_null);
}
// Prevent reordering with prior memory operations.
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 2c4fab0..53497b6 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -797,7 +797,8 @@
}
if (type == Primitive::kPrimNot) {
- codegen->MarkGCCard(base, value);
+ bool value_can_be_null = true; // TODO: Worth finding out this information?
+ codegen->MarkGCCard(base, value, value_can_be_null);
}
}
@@ -856,7 +857,8 @@
// This needs to be before the temp registers, as MarkGCCard also uses VIXL temps.
if (type == Primitive::kPrimNot) {
// Mark card for object assuming new value is stored.
- codegen->MarkGCCard(base, value);
+ bool value_can_be_null = true; // TODO: Worth finding out this information?
+ codegen->MarkGCCard(base, value, value_can_be_null);
}
UseScratchRegisterScope temps(masm);
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 28b7a07..d2ca42d 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -1503,10 +1503,12 @@
}
if (type == Primitive::kPrimNot) {
+ bool value_can_be_null = true; // TODO: Worth finding out this information?
codegen->MarkGCCard(locations->GetTemp(0).AsRegister<Register>(),
locations->GetTemp(1).AsRegister<Register>(),
base,
- value_loc.AsRegister<Register>());
+ value_loc.AsRegister<Register>(),
+ value_can_be_null);
}
}
@@ -1602,10 +1604,12 @@
Register value = locations->InAt(4).AsRegister<Register>();
if (type == Primitive::kPrimNot) {
// Mark card for object assuming new value is stored.
+ bool value_can_be_null = true; // TODO: Worth finding out this information?
codegen->MarkGCCard(locations->GetTemp(0).AsRegister<Register>(),
locations->GetTemp(1).AsRegister<Register>(),
base,
- value);
+ value,
+ value_can_be_null);
}
__ LockCmpxchgl(Address(base, offset, TIMES_1, 0), value);
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 0efa714..2ccecfe 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -1374,10 +1374,12 @@
}
if (type == Primitive::kPrimNot) {
+ bool value_can_be_null = true; // TODO: Worth finding out this information?
codegen->MarkGCCard(locations->GetTemp(0).AsRegister<CpuRegister>(),
locations->GetTemp(1).AsRegister<CpuRegister>(),
base,
- value);
+ value,
+ value_can_be_null);
}
}
@@ -1459,10 +1461,12 @@
// Integer or object.
if (type == Primitive::kPrimNot) {
// Mark card for object assuming new value is stored.
+ bool value_can_be_null = true; // TODO: Worth finding out this information?
codegen->MarkGCCard(locations->GetTemp(0).AsRegister<CpuRegister>(),
locations->GetTemp(1).AsRegister<CpuRegister>(),
base,
- value);
+ value,
+ value_can_be_null);
}
__ LockCmpxchgl(Address(base, offset, TIMES_1, 0), value);
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 41adc72..47da9cc 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -16,6 +16,7 @@
#include "nodes.h"
+#include "code_generator.h"
#include "ssa_builder.h"
#include "base/bit_vector-inl.h"
#include "utils/growable_array.h"
@@ -794,6 +795,84 @@
}
}
+HConstant* HTypeConversion::TryStaticEvaluation() const {
+ HGraph* graph = GetBlock()->GetGraph();
+ if (GetInput()->IsIntConstant()) {
+ int32_t value = GetInput()->AsIntConstant()->GetValue();
+ switch (GetResultType()) {
+ case Primitive::kPrimLong:
+ return graph->GetLongConstant(static_cast<int64_t>(value));
+ case Primitive::kPrimFloat:
+ return graph->GetFloatConstant(static_cast<float>(value));
+ case Primitive::kPrimDouble:
+ return graph->GetDoubleConstant(static_cast<double>(value));
+ default:
+ return nullptr;
+ }
+ } else if (GetInput()->IsLongConstant()) {
+ int64_t value = GetInput()->AsLongConstant()->GetValue();
+ switch (GetResultType()) {
+ case Primitive::kPrimInt:
+ return graph->GetIntConstant(static_cast<int32_t>(value));
+ case Primitive::kPrimFloat:
+ return graph->GetFloatConstant(static_cast<float>(value));
+ case Primitive::kPrimDouble:
+ return graph->GetDoubleConstant(static_cast<double>(value));
+ default:
+ return nullptr;
+ }
+ } else if (GetInput()->IsFloatConstant()) {
+ float value = GetInput()->AsFloatConstant()->GetValue();
+ switch (GetResultType()) {
+ case Primitive::kPrimInt:
+ if (std::isnan(value))
+ return graph->GetIntConstant(0);
+ if (value >= kPrimIntMax)
+ return graph->GetIntConstant(kPrimIntMax);
+ if (value <= kPrimIntMin)
+ return graph->GetIntConstant(kPrimIntMin);
+ return graph->GetIntConstant(static_cast<int32_t>(value));
+ case Primitive::kPrimLong:
+ if (std::isnan(value))
+ return graph->GetLongConstant(0);
+ if (value >= kPrimLongMax)
+ return graph->GetLongConstant(kPrimLongMax);
+ if (value <= kPrimLongMin)
+ return graph->GetLongConstant(kPrimLongMin);
+ return graph->GetLongConstant(static_cast<int64_t>(value));
+ case Primitive::kPrimDouble:
+ return graph->GetDoubleConstant(static_cast<double>(value));
+ default:
+ return nullptr;
+ }
+ } else if (GetInput()->IsDoubleConstant()) {
+ double value = GetInput()->AsDoubleConstant()->GetValue();
+ switch (GetResultType()) {
+ case Primitive::kPrimInt:
+ if (std::isnan(value))
+ return graph->GetIntConstant(0);
+ if (value >= kPrimIntMax)
+ return graph->GetIntConstant(kPrimIntMax);
+ if (value <= kPrimLongMin)
+ return graph->GetIntConstant(kPrimIntMin);
+ return graph->GetIntConstant(static_cast<int32_t>(value));
+ case Primitive::kPrimLong:
+ if (std::isnan(value))
+ return graph->GetLongConstant(0);
+ if (value >= kPrimLongMax)
+ return graph->GetLongConstant(kPrimLongMax);
+ if (value <= kPrimLongMin)
+ return graph->GetLongConstant(kPrimLongMin);
+ return graph->GetLongConstant(static_cast<int64_t>(value));
+ case Primitive::kPrimFloat:
+ return graph->GetFloatConstant(static_cast<float>(value));
+ default:
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
HConstant* HUnaryOperation::TryStaticEvaluation() const {
if (GetInput()->IsIntConstant()) {
int32_t value = Evaluate(GetInput()->AsIntConstant()->GetValue());
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 77b587e..12ace41 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -60,6 +60,8 @@
static constexpr uint32_t kMaxIntShiftValue = 0x1f;
static constexpr uint64_t kMaxLongShiftValue = 0x3f;
+static constexpr InvokeType kInvalidInvokeType = static_cast<InvokeType>(-1);
+
enum IfCondition {
kCondEQ,
kCondNE,
@@ -120,6 +122,8 @@
HGraph(ArenaAllocator* arena,
const DexFile& dex_file,
uint32_t method_idx,
+ bool should_generate_constructor_barrier,
+ InvokeType invoke_type = kInvalidInvokeType,
bool debuggable = false,
int start_instruction_id = 0)
: arena_(arena),
@@ -137,6 +141,8 @@
current_instruction_id_(start_instruction_id),
dex_file_(dex_file),
method_idx_(method_idx),
+ invoke_type_(invoke_type),
+ should_generate_constructor_barrier_(should_generate_constructor_barrier),
cached_null_constant_(nullptr),
cached_int_constants_(std::less<int32_t>(), arena->Adapter()),
cached_float_constants_(std::less<int32_t>(), arena->Adapter()),
@@ -247,6 +253,10 @@
has_bounds_checks_ = value;
}
+ bool ShouldGenerateConstructorBarrier() const {
+ return should_generate_constructor_barrier_;
+ }
+
bool IsDebuggable() const { return debuggable_; }
// Returns a constant of the given type and value. If it does not exist
@@ -277,6 +287,10 @@
return method_idx_;
}
+ InvokeType GetInvokeType() const {
+ return invoke_type_;
+ }
+
private:
void VisitBlockForDominatorTree(HBasicBlock* block,
HBasicBlock* predecessor,
@@ -359,6 +373,11 @@
// The method index in the dex file.
const uint32_t method_idx_;
+ // If inlined, this encodes how the callee is being invoked.
+ const InvokeType invoke_type_;
+
+ const bool should_generate_constructor_barrier_;
+
// Cached constants.
HNullConstant* cached_null_constant_;
ArenaSafeMap<int32_t, HIntConstant*> cached_int_constants_;
@@ -1093,13 +1112,15 @@
size_t number_of_vregs,
const DexFile& dex_file,
uint32_t method_idx,
- uint32_t dex_pc)
+ uint32_t dex_pc,
+ InvokeType invoke_type)
: vregs_(arena, number_of_vregs),
locations_(arena, number_of_vregs),
parent_(nullptr),
dex_file_(dex_file),
method_idx_(method_idx),
- dex_pc_(dex_pc) {
+ dex_pc_(dex_pc),
+ invoke_type_(invoke_type) {
vregs_.SetSize(number_of_vregs);
for (size_t i = 0; i < number_of_vregs; i++) {
vregs_.Put(i, HUserRecord<HEnvironment*>());
@@ -1111,16 +1132,20 @@
}
}
+ HEnvironment(ArenaAllocator* arena, const HEnvironment& to_copy)
+ : HEnvironment(arena,
+ to_copy.Size(),
+ to_copy.GetDexFile(),
+ to_copy.GetMethodIdx(),
+ to_copy.GetDexPc(),
+ to_copy.GetInvokeType()) {}
+
void SetAndCopyParentChain(ArenaAllocator* allocator, HEnvironment* parent) {
- parent_ = new (allocator) HEnvironment(allocator,
- parent->Size(),
- parent->GetDexFile(),
- parent->GetMethodIdx(),
- parent->GetDexPc());
+ parent_ = new (allocator) HEnvironment(allocator, *parent);
+ parent_->CopyFrom(parent);
if (parent->GetParent() != nullptr) {
parent_->SetAndCopyParentChain(allocator, parent->GetParent());
}
- parent_->CopyFrom(parent);
}
void CopyFrom(const GrowableArray<HInstruction*>& locals);
@@ -1161,6 +1186,10 @@
return method_idx_;
}
+ InvokeType GetInvokeType() const {
+ return invoke_type_;
+ }
+
const DexFile& GetDexFile() const {
return dex_file_;
}
@@ -1180,6 +1209,7 @@
const DexFile& dex_file_;
const uint32_t method_idx_;
const uint32_t dex_pc_;
+ const InvokeType invoke_type_;
friend class HInstruction;
@@ -1393,12 +1423,7 @@
// copying, the uses lists are being updated.
void CopyEnvironmentFrom(HEnvironment* environment) {
ArenaAllocator* allocator = GetBlock()->GetGraph()->GetArena();
- environment_ = new (allocator) HEnvironment(
- allocator,
- environment->Size(),
- environment->GetDexFile(),
- environment->GetMethodIdx(),
- environment->GetDexPc());
+ environment_ = new (allocator) HEnvironment(allocator, *environment);
environment_->CopyFrom(environment);
if (environment->GetParent() != nullptr) {
environment_->SetAndCopyParentChain(allocator, environment->GetParent());
@@ -1408,16 +1433,11 @@
void CopyEnvironmentFromWithLoopPhiAdjustment(HEnvironment* environment,
HBasicBlock* block) {
ArenaAllocator* allocator = GetBlock()->GetGraph()->GetArena();
- environment_ = new (allocator) HEnvironment(
- allocator,
- environment->Size(),
- environment->GetDexFile(),
- environment->GetMethodIdx(),
- environment->GetDexPc());
+ environment_ = new (allocator) HEnvironment(allocator, *environment);
+ environment_->CopyFromWithLoopPhiAdjustment(environment, block);
if (environment->GetParent() != nullptr) {
environment_->SetAndCopyParentChain(allocator, environment->GetParent());
}
- environment_->CopyFromWithLoopPhiAdjustment(environment, block);
}
// Returns the number of entries in the environment. Typically, that is the
@@ -2197,15 +2217,16 @@
size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); }
bool IsMinusOne() const OVERRIDE {
- return bit_cast<uint32_t, float>(AsFloatConstant()->GetValue()) ==
- bit_cast<uint32_t, float>((-1.0f));
+ return bit_cast<uint32_t, float>(value_) == bit_cast<uint32_t, float>((-1.0f));
}
bool IsZero() const OVERRIDE {
- return AsFloatConstant()->GetValue() == 0.0f;
+ return value_ == 0.0f;
}
bool IsOne() const OVERRIDE {
- return bit_cast<uint32_t, float>(AsFloatConstant()->GetValue()) ==
- bit_cast<uint32_t, float>(1.0f);
+ return bit_cast<uint32_t, float>(value_) == bit_cast<uint32_t, float>(1.0f);
+ }
+ bool IsNaN() const {
+ return std::isnan(value_);
}
DECLARE_INSTRUCTION(FloatConstant);
@@ -2235,15 +2256,16 @@
size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); }
bool IsMinusOne() const OVERRIDE {
- return bit_cast<uint64_t, double>(AsDoubleConstant()->GetValue()) ==
- bit_cast<uint64_t, double>((-1.0));
+ return bit_cast<uint64_t, double>(value_) == bit_cast<uint64_t, double>((-1.0));
}
bool IsZero() const OVERRIDE {
- return AsDoubleConstant()->GetValue() == 0.0;
+ return value_ == 0.0;
}
bool IsOne() const OVERRIDE {
- return bit_cast<uint64_t, double>(AsDoubleConstant()->GetValue()) ==
- bit_cast<uint64_t, double>(1.0);
+ return bit_cast<uint64_t, double>(value_) == bit_cast<uint64_t, double>(1.0);
+ }
+ bool IsNaN() const {
+ return std::isnan(value_);
}
DECLARE_INSTRUCTION(DoubleConstant);
@@ -2366,6 +2388,8 @@
uint32_t GetDexMethodIndex() const { return dex_method_index_; }
+ InvokeType GetOriginalInvokeType() const { return original_invoke_type_; }
+
Intrinsics GetIntrinsic() const {
return intrinsic_;
}
@@ -2382,13 +2406,15 @@
uint32_t number_of_other_inputs,
Primitive::Type return_type,
uint32_t dex_pc,
- uint32_t dex_method_index)
+ uint32_t dex_method_index,
+ InvokeType original_invoke_type)
: HInstruction(SideEffects::All()),
number_of_arguments_(number_of_arguments),
inputs_(arena, number_of_arguments),
return_type_(return_type),
dex_pc_(dex_pc),
dex_method_index_(dex_method_index),
+ original_invoke_type_(original_invoke_type),
intrinsic_(Intrinsics::kNone) {
uint32_t number_of_inputs = number_of_arguments + number_of_other_inputs;
inputs_.SetSize(number_of_inputs);
@@ -2404,6 +2430,7 @@
const Primitive::Type return_type_;
const uint32_t dex_pc_;
const uint32_t dex_method_index_;
+ const InvokeType original_invoke_type_;
Intrinsics intrinsic_;
private:
@@ -2435,8 +2462,8 @@
clinit_check_requirement == ClinitCheckRequirement::kExplicit ? 1u : 0u,
return_type,
dex_pc,
- dex_method_index),
- original_invoke_type_(original_invoke_type),
+ dex_method_index,
+ original_invoke_type),
invoke_type_(invoke_type),
is_recursive_(is_recursive),
clinit_check_requirement_(clinit_check_requirement),
@@ -2449,7 +2476,6 @@
return false;
}
- InvokeType GetOriginalInvokeType() const { return original_invoke_type_; }
InvokeType GetInvokeType() const { return invoke_type_; }
bool IsRecursive() const { return is_recursive_; }
bool NeedsDexCache() const OVERRIDE { return !IsRecursive(); }
@@ -2507,7 +2533,6 @@
}
private:
- const InvokeType original_invoke_type_;
const InvokeType invoke_type_;
const bool is_recursive_;
ClinitCheckRequirement clinit_check_requirement_;
@@ -2526,7 +2551,7 @@
uint32_t dex_pc,
uint32_t dex_method_index,
uint32_t vtable_index)
- : HInvoke(arena, number_of_arguments, 0u, return_type, dex_pc, dex_method_index),
+ : HInvoke(arena, number_of_arguments, 0u, return_type, dex_pc, dex_method_index, kVirtual),
vtable_index_(vtable_index) {}
bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE {
@@ -2552,7 +2577,7 @@
uint32_t dex_pc,
uint32_t dex_method_index,
uint32_t imt_index)
- : HInvoke(arena, number_of_arguments, 0u, return_type, dex_pc, dex_method_index),
+ : HInvoke(arena, number_of_arguments, 0u, return_type, dex_pc, dex_method_index, kInterface),
imt_index_(imt_index) {}
bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE {
@@ -2900,6 +2925,8 @@
bool CanBeNull() const OVERRIDE { return !is_this_; }
+ bool IsThis() const { return is_this_; }
+
DECLARE_INSTRUCTION(ParameterValue);
private:
@@ -2980,6 +3007,10 @@
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; }
+ // Try to statically evaluate the conversion and return a HConstant
+ // containing the result. If the input cannot be converted, return nullptr.
+ HConstant* TryStaticEvaluation() const;
+
DECLARE_INSTRUCTION(TypeConversion);
private:
@@ -3157,7 +3188,8 @@
MemberOffset field_offset,
bool is_volatile)
: HTemplateInstruction(SideEffects::ChangesSomething()),
- field_info_(field_offset, field_type, is_volatile) {
+ field_info_(field_offset, field_type, is_volatile),
+ value_can_be_null_(true) {
SetRawInputAt(0, object);
SetRawInputAt(1, value);
}
@@ -3171,11 +3203,14 @@
Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
bool IsVolatile() const { return field_info_.IsVolatile(); }
HInstruction* GetValue() const { return InputAt(1); }
+ bool GetValueCanBeNull() const { return value_can_be_null_; }
+ void ClearValueCanBeNull() { value_can_be_null_ = false; }
DECLARE_INSTRUCTION(InstanceFieldSet);
private:
const FieldInfo field_info_;
+ bool value_can_be_null_;
DISALLOW_COPY_AND_ASSIGN(HInstanceFieldSet);
};
@@ -3224,7 +3259,8 @@
: HTemplateInstruction(SideEffects::ChangesSomething()),
dex_pc_(dex_pc),
expected_component_type_(expected_component_type),
- needs_type_check_(value->GetType() == Primitive::kPrimNot) {
+ needs_type_check_(value->GetType() == Primitive::kPrimNot),
+ value_can_be_null_(true) {
SetRawInputAt(0, array);
SetRawInputAt(1, index);
SetRawInputAt(2, value);
@@ -3246,6 +3282,11 @@
needs_type_check_ = false;
}
+ void ClearValueCanBeNull() {
+ value_can_be_null_ = false;
+ }
+
+ bool GetValueCanBeNull() const { return value_can_be_null_; }
bool NeedsTypeCheck() const { return needs_type_check_; }
uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
@@ -3271,6 +3312,7 @@
const uint32_t dex_pc_;
const Primitive::Type expected_component_type_;
bool needs_type_check_;
+ bool value_can_be_null_;
DISALLOW_COPY_AND_ASSIGN(HArraySet);
};
@@ -3417,8 +3459,8 @@
return generate_clinit_check_;
}
- void SetMustGenerateClinitCheck() {
- generate_clinit_check_ = true;
+ void SetMustGenerateClinitCheck(bool generate_clinit_check) {
+ generate_clinit_check_ = generate_clinit_check;
}
bool CanCallRuntime() const {
@@ -3571,7 +3613,8 @@
MemberOffset field_offset,
bool is_volatile)
: HTemplateInstruction(SideEffects::ChangesSomething()),
- field_info_(field_offset, field_type, is_volatile) {
+ field_info_(field_offset, field_type, is_volatile),
+ value_can_be_null_(true) {
SetRawInputAt(0, cls);
SetRawInputAt(1, value);
}
@@ -3582,11 +3625,14 @@
bool IsVolatile() const { return field_info_.IsVolatile(); }
HInstruction* GetValue() const { return InputAt(1); }
+ bool GetValueCanBeNull() const { return value_can_be_null_; }
+ void ClearValueCanBeNull() { value_can_be_null_ = false; }
DECLARE_INSTRUCTION(StaticFieldSet);
private:
const FieldInfo field_info_;
+ bool value_can_be_null_;
DISALLOW_COPY_AND_ASSIGN(HStaticFieldSet);
};
@@ -3889,7 +3935,9 @@
}
for (size_t i = 0, e = moves_.Size(); i < e; ++i) {
DCHECK(!destination.OverlapsWith(moves_.Get(i).GetDestination()))
- << "Overlapped destination for two moves in a parallel move.";
+ << "Overlapped destination for two moves in a parallel move: "
+ << moves_.Get(i).GetSource() << " ==> " << moves_.Get(i).GetDestination() << " and "
+ << source << " ==> " << destination;
}
}
moves_.Add(MoveOperands(source, destination, type, instruction));
diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc
index 2736453..782cde4 100644
--- a/compiler/optimizing/nodes_test.cc
+++ b/compiler/optimizing/nodes_test.cc
@@ -51,7 +51,7 @@
exit_block->AddInstruction(new (&allocator) HExit());
HEnvironment* environment = new (&allocator) HEnvironment(
- &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0);
+ &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic);
null_check->SetRawEnvironment(environment);
environment->SetRawEnvAt(0, parameter);
parameter->AddEnvUseAt(null_check->GetEnvironment(), 0);
@@ -132,7 +132,7 @@
ASSERT_TRUE(parameter1->GetUses().HasOnlyOneUse());
HEnvironment* environment = new (&allocator) HEnvironment(
- &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0);
+ &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic);
GrowableArray<HInstruction*> array(&allocator, 1);
array.Add(parameter1);
@@ -143,13 +143,13 @@
ASSERT_TRUE(parameter1->GetEnvUses().HasOnlyOneUse());
HEnvironment* parent1 = new (&allocator) HEnvironment(
- &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0);
+ &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic);
parent1->CopyFrom(array);
ASSERT_EQ(parameter1->GetEnvUses().SizeSlow(), 2u);
HEnvironment* parent2 = new (&allocator) HEnvironment(
- &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0);
+ &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic);
parent2->CopyFrom(array);
parent1->SetAndCopyParentChain(&allocator, parent2);
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 8bb5d8e..b2e8ecd 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -512,9 +512,14 @@
class_def_idx, method_idx, access_flags,
compiler_driver->GetVerifiedMethod(&dex_file, method_idx));
+ bool requires_barrier = dex_compilation_unit.IsConstructor()
+ && compiler_driver->RequiresConstructorBarrier(Thread::Current(),
+ dex_compilation_unit.GetDexFile(),
+ dex_compilation_unit.GetClassDefIndex());
ArenaAllocator arena(Runtime::Current()->GetArenaPool());
HGraph* graph = new (&arena) HGraph(
- &arena, dex_file, method_idx, compiler_driver->GetCompilerOptions().GetDebuggable());
+ &arena, dex_file, method_idx, requires_barrier, kInvalidInvokeType,
+ compiler_driver->GetCompilerOptions().GetDebuggable());
// For testing purposes, we put a special marker on method names that should be compiled
// with this compiler. This makes sure we're not regressing.
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index 4f8ec65..1fe9346 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -74,7 +74,7 @@
inline HGraph* CreateGraph(ArenaAllocator* allocator) {
return new (allocator) HGraph(
- allocator, *reinterpret_cast<DexFile*>(allocator->Alloc(sizeof(DexFile))), -1);
+ allocator, *reinterpret_cast<DexFile*>(allocator->Alloc(sizeof(DexFile))), -1, false);
}
// Create a control-flow graph from Dex instructions.
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 78d1185..538736b 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -53,7 +53,7 @@
if (check->GetPrevious() == cls) {
// Pass the initialization duty to the `HLoadClass` instruction,
// and remove the instruction from the graph.
- cls->SetMustGenerateClinitCheck();
+ cls->SetMustGenerateClinitCheck(true);
check->GetBlock()->RemoveInstruction(check);
}
}
@@ -82,8 +82,15 @@
void PrepareForRegisterAllocation::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
if (invoke->IsStaticWithExplicitClinitCheck()) {
size_t last_input_index = invoke->InputCount() - 1;
- HInstruction* last_input = invoke->InputAt(last_input_index);
- DCHECK(last_input->IsLoadClass()) << last_input->DebugName();
+ HLoadClass* last_input = invoke->InputAt(last_input_index)->AsLoadClass();
+ DCHECK(last_input != nullptr)
+ << "Last input is not HLoadClass. It is " << last_input->DebugName();
+
+ // The static call will initialize the class so there's no need for a clinit check if
+ // it's the first user.
+ if (last_input == invoke->GetPrevious()) {
+ last_input->SetMustGenerateClinitCheck(false);
+ }
// Remove a load class instruction as last input of a static
// invoke, which has been added (along with a clinit check,
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index f53f846..925099a 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -775,7 +775,7 @@
} else if (current->IsLowInterval()) {
reg = FindAvailableRegisterPair(free_until, current->GetStart());
} else {
- reg = FindAvailableRegister(free_until);
+ reg = FindAvailableRegister(free_until, current);
}
}
@@ -839,14 +839,52 @@
return reg;
}
-int RegisterAllocator::FindAvailableRegister(size_t* next_use) const {
+bool RegisterAllocator::IsCallerSaveRegister(int reg) const {
+ return processing_core_registers_
+ ? !codegen_->IsCoreCalleeSaveRegister(reg)
+ : !codegen_->IsFloatingPointCalleeSaveRegister(reg);
+}
+
+int RegisterAllocator::FindAvailableRegister(size_t* next_use, LiveInterval* current) const {
+ // We special case intervals that do not span a safepoint to try to find a caller-save
+ // register if one is available. We iterate from 0 to the number of registers,
+ // so if there are caller-save registers available at the end, we continue the iteration.
+ bool prefers_caller_save = !current->HasWillCallSafepoint();
int reg = kNoRegister;
- // Pick the register that is used the last.
for (size_t i = 0; i < number_of_registers_; ++i) {
- if (IsBlocked(i)) continue;
- if (reg == kNoRegister || next_use[i] > next_use[reg]) {
+ if (IsBlocked(i)) {
+ // Register cannot be used. Continue.
+ continue;
+ }
+
+ // Best case: we found a register fully available.
+ if (next_use[i] == kMaxLifetimePosition) {
+ if (prefers_caller_save && !IsCallerSaveRegister(i)) {
+ // We can get shorter encodings on some platforms by using
+ // small register numbers. So only update the candidate if the previous
+ // one was not available for the whole method.
+ if (reg == kNoRegister || next_use[reg] != kMaxLifetimePosition) {
+ reg = i;
+ }
+ // Continue the iteration in the hope of finding a caller save register.
+ continue;
+ } else {
+ reg = i;
+ // We know the register is good enough. Return it.
+ break;
+ }
+ }
+
+ // If we had no register before, take this one as a reference.
+ if (reg == kNoRegister) {
reg = i;
- if (next_use[i] == kMaxLifetimePosition) break;
+ continue;
+ }
+
+ // Pick the register that is used the last.
+ if (next_use[i] > next_use[reg]) {
+ reg = i;
+ continue;
}
}
return reg;
@@ -971,7 +1009,7 @@
|| (first_use >= next_use[GetHighForLowRegister(reg)]);
} else {
DCHECK(!current->IsHighInterval());
- reg = FindAvailableRegister(next_use);
+ reg = FindAvailableRegister(next_use, current);
should_spill = (first_use >= next_use[reg]);
}
diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h
index dc9c708..6d5bfc3 100644
--- a/compiler/optimizing/register_allocator.h
+++ b/compiler/optimizing/register_allocator.h
@@ -140,7 +140,8 @@
void DumpInterval(std::ostream& stream, LiveInterval* interval) const;
void DumpAllIntervals(std::ostream& stream) const;
int FindAvailableRegisterPair(size_t* next_use, size_t starting_at) const;
- int FindAvailableRegister(size_t* next_use) const;
+ int FindAvailableRegister(size_t* next_use, LiveInterval* current) const;
+ bool IsCallerSaveRegister(int reg) const;
// Try splitting an active non-pair or unaligned pair interval at the given `position`.
// Returns whether it was successful at finding such an interval.
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 59a2852..c51d248 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -504,7 +504,7 @@
// typed and the value in a dex register will not be used for both floating point and
// non-floating point operations. So the only reason an instruction would want a floating
// point equivalent is for an unused phi that will be removed by the dead phi elimination phase.
- DCHECK(user->IsPhi());
+ DCHECK(user->IsPhi()) << "is actually " << user->DebugName() << " (" << user->GetId() << ")";
return value;
}
}
@@ -547,7 +547,8 @@
current_locals_->Size(),
GetGraph()->GetDexFile(),
GetGraph()->GetMethodIdx(),
- instruction->GetDexPc());
+ instruction->GetDexPc(),
+ GetGraph()->GetInvokeType());
environment->CopyFrom(*current_locals_);
instruction->SetRawEnvironment(environment);
}
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 82c5454..4b19c5b 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -76,7 +76,7 @@
}
void Dump(std::ostream& stream) const {
- stream << "[" << start_ << ", " << end_ << ")";
+ stream << "[" << start_ << "," << end_ << ")";
}
LiveRange* Dup(ArenaAllocator* allocator) const {
@@ -542,6 +542,15 @@
return defined_by_;
}
+ bool HasWillCallSafepoint() const {
+ for (SafepointPosition* safepoint = first_safepoint_;
+ safepoint != nullptr;
+ safepoint = safepoint->GetNext()) {
+ if (safepoint->GetLocations()->WillCall()) return true;
+ }
+ return false;
+ }
+
SafepointPosition* FindSafepointJustBefore(size_t position) const {
for (SafepointPosition* safepoint = first_safepoint_, *previous = nullptr;
safepoint != nullptr;
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index 8344fc3..b446815 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#include "stack_map_stream.h"
namespace art {
@@ -52,6 +51,7 @@
dex_pc_max_ = std::max(dex_pc_max_, dex_pc);
native_pc_offset_max_ = std::max(native_pc_offset_max_, native_pc_offset);
register_mask_max_ = std::max(register_mask_max_, register_mask);
+ current_dex_register_ = 0;
}
void StackMapStream::EndStackMapEntry() {
@@ -60,11 +60,7 @@
current_entry_ = StackMapEntry();
}
-void StackMapStream::AddDexRegisterEntry(uint16_t dex_register,
- DexRegisterLocation::Kind kind,
- int32_t value) {
- DCHECK_LT(dex_register, current_entry_.num_dex_registers);
-
+void StackMapStream::AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value) {
if (kind != DexRegisterLocation::Kind::kNone) {
// Ensure we only use non-compressed location kind at this stage.
DCHECK(DexRegisterLocation::IsShortLocationKind(kind))
@@ -87,18 +83,49 @@
location_catalog_entries_indices_.Insert(std::make_pair(location, index));
}
- current_entry_.live_dex_registers_mask->SetBit(dex_register);
- current_entry_.dex_register_map_hash +=
- (1 << (dex_register % (sizeof(current_entry_.dex_register_map_hash) * kBitsPerByte)));
- current_entry_.dex_register_map_hash += static_cast<uint32_t>(value);
- current_entry_.dex_register_map_hash += static_cast<uint32_t>(kind);
+ if (in_inline_frame_) {
+ // TODO: Support sharing DexRegisterMap across InlineInfo.
+ DCHECK_LT(current_dex_register_, current_inline_info_.num_dex_registers);
+ current_inline_info_.live_dex_registers_mask->SetBit(current_dex_register_);
+ } else {
+ DCHECK_LT(current_dex_register_, current_entry_.num_dex_registers);
+ current_entry_.live_dex_registers_mask->SetBit(current_dex_register_);
+ current_entry_.dex_register_map_hash += (1 <<
+ (current_dex_register_ % (sizeof(current_entry_.dex_register_map_hash) * kBitsPerByte)));
+ current_entry_.dex_register_map_hash += static_cast<uint32_t>(value);
+ current_entry_.dex_register_map_hash += static_cast<uint32_t>(kind);
+ }
}
+ current_dex_register_++;
}
-void StackMapStream::AddInlineInfoEntry(uint32_t method_index) {
- InlineInfoEntry entry;
- entry.method_index = method_index;
- inline_infos_.Add(entry);
+void StackMapStream::BeginInlineInfoEntry(uint32_t method_index,
+ uint32_t dex_pc,
+ InvokeType invoke_type,
+ uint32_t num_dex_registers) {
+ DCHECK(!in_inline_frame_);
+ in_inline_frame_ = true;
+ current_inline_info_.method_index = method_index;
+ current_inline_info_.dex_pc = dex_pc;
+ current_inline_info_.invoke_type = invoke_type;
+ current_inline_info_.num_dex_registers = num_dex_registers;
+ current_inline_info_.dex_register_locations_start_index = dex_register_locations_.Size();
+ if (num_dex_registers != 0) {
+ current_inline_info_.live_dex_registers_mask =
+ new (allocator_) ArenaBitVector(allocator_, num_dex_registers, true);
+ } else {
+ current_inline_info_.live_dex_registers_mask = nullptr;
+ }
+ current_dex_register_ = 0;
+}
+
+void StackMapStream::EndInlineInfoEntry() {
+ DCHECK(in_inline_frame_);
+ DCHECK_EQ(current_dex_register_, current_inline_info_.num_dex_registers)
+ << "Inline information contains less registers than expected";
+ in_inline_frame_ = false;
+ inline_infos_.Add(current_inline_info_);
+ current_inline_info_ = InlineInfoEntry();
}
size_t StackMapStream::PrepareForFillIn() {
@@ -142,17 +169,18 @@
return size;
}
-size_t StackMapStream::ComputeDexRegisterMapSize(const StackMapEntry& entry) const {
+size_t StackMapStream::ComputeDexRegisterMapSize(uint32_t num_dex_registers,
+ const BitVector& live_dex_registers_mask) const {
// Size of the map in bytes.
size_t size = DexRegisterMap::kFixedSize;
// Add the live bit mask for the Dex register liveness.
- size += DexRegisterMap::GetLiveBitMaskSize(entry.num_dex_registers);
+ size += DexRegisterMap::GetLiveBitMaskSize(num_dex_registers);
// Compute the size of the set of live Dex register entries.
size_t number_of_live_dex_registers = 0;
for (size_t dex_register_number = 0;
- dex_register_number < entry.num_dex_registers;
+ dex_register_number < num_dex_registers;
++dex_register_number) {
- if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) {
+ if (live_dex_registers_mask.IsBitSet(dex_register_number)) {
++number_of_live_dex_registers;
}
}
@@ -167,11 +195,18 @@
size_t StackMapStream::ComputeDexRegisterMapsSize() const {
size_t size = 0;
+ size_t inline_info_index = 0;
for (size_t i = 0; i < stack_maps_.Size(); ++i) {
StackMapEntry entry = stack_maps_.Get(i);
if (entry.same_dex_register_map_as_ == kNoSameDexMapFound) {
+ size += ComputeDexRegisterMapSize(entry.num_dex_registers, *entry.live_dex_registers_mask);
+ } else {
// Entries with the same dex map will have the same offset.
- size += ComputeDexRegisterMapSize(entry);
+ }
+ for (size_t j = 0; j < entry.inlining_depth; ++j) {
+ InlineInfoEntry inline_entry = inline_infos_.Get(inline_info_index++);
+ size += ComputeDexRegisterMapSize(inline_entry.num_dex_registers,
+ *inline_entry.live_dex_registers_mask);
}
}
return size;
@@ -247,34 +282,19 @@
.GetDexRegisterMapOffset(code_info));
} else {
// New dex registers maps should be added to the stack map.
- MemoryRegion register_region =
- dex_register_locations_region.Subregion(
- next_dex_register_map_offset,
- ComputeDexRegisterMapSize(entry));
+ MemoryRegion register_region = dex_register_locations_region.Subregion(
+ next_dex_register_map_offset,
+ ComputeDexRegisterMapSize(entry.num_dex_registers, *entry.live_dex_registers_mask));
next_dex_register_map_offset += register_region.size();
DexRegisterMap dex_register_map(register_region);
stack_map.SetDexRegisterMapOffset(
code_info, register_region.start() - dex_register_locations_region.start());
- // Set the live bit mask.
- dex_register_map.SetLiveBitMask(entry.num_dex_registers, *entry.live_dex_registers_mask);
-
- // Set the dex register location mapping data.
- for (size_t dex_register_number = 0, index_in_dex_register_locations = 0;
- dex_register_number < entry.num_dex_registers;
- ++dex_register_number) {
- if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) {
- size_t location_catalog_entry_index =
- dex_register_locations_.Get(entry.dex_register_locations_start_index
- + index_in_dex_register_locations);
- dex_register_map.SetLocationCatalogEntryIndex(
- index_in_dex_register_locations,
- location_catalog_entry_index,
- entry.num_dex_registers,
- location_catalog_entries_.Size());
- ++index_in_dex_register_locations;
- }
- }
+ // Set the dex register location.
+ FillInDexRegisterMap(dex_register_map,
+ entry.num_dex_registers,
+ *entry.live_dex_registers_mask,
+ entry.dex_register_locations_start_index);
}
}
@@ -291,9 +311,30 @@
code_info, inline_region.start() - dex_register_locations_region.start());
inline_info.SetDepth(entry.inlining_depth);
- for (size_t j = 0; j < entry.inlining_depth; ++j) {
- InlineInfoEntry inline_entry = inline_infos_.Get(j + entry.inline_infos_start_index);
- inline_info.SetMethodReferenceIndexAtDepth(j, inline_entry.method_index);
+ for (size_t depth = 0; depth < entry.inlining_depth; ++depth) {
+ InlineInfoEntry inline_entry = inline_infos_.Get(depth + entry.inline_infos_start_index);
+ inline_info.SetMethodIndexAtDepth(depth, inline_entry.method_index);
+ inline_info.SetDexPcAtDepth(depth, inline_entry.dex_pc);
+ inline_info.SetInvokeTypeAtDepth(depth, inline_entry.invoke_type);
+ if (inline_entry.num_dex_registers == 0) {
+ // No dex map available.
+ inline_info.SetDexRegisterMapOffsetAtDepth(depth, StackMap::kNoDexRegisterMap);
+ DCHECK(inline_entry.live_dex_registers_mask == nullptr);
+ } else {
+ MemoryRegion register_region = dex_register_locations_region.Subregion(
+ next_dex_register_map_offset,
+ ComputeDexRegisterMapSize(inline_entry.num_dex_registers,
+ *inline_entry.live_dex_registers_mask));
+ next_dex_register_map_offset += register_region.size();
+ DexRegisterMap dex_register_map(register_region);
+ inline_info.SetDexRegisterMapOffsetAtDepth(
+ depth, register_region.start() - dex_register_locations_region.start());
+
+ FillInDexRegisterMap(dex_register_map,
+ inline_entry.num_dex_registers,
+ *inline_entry.live_dex_registers_mask,
+ inline_entry.dex_register_locations_start_index);
+ }
}
} else {
if (inline_info_size_ != 0) {
@@ -303,6 +344,28 @@
}
}
+void StackMapStream::FillInDexRegisterMap(DexRegisterMap dex_register_map,
+ uint32_t num_dex_registers,
+ const BitVector& live_dex_registers_mask,
+ uint32_t start_index_in_dex_register_locations) const {
+ dex_register_map.SetLiveBitMask(num_dex_registers, live_dex_registers_mask);
+ // Set the dex register location mapping data.
+ for (size_t dex_register_number = 0, index_in_dex_register_locations = 0;
+ dex_register_number < num_dex_registers;
+ ++dex_register_number) {
+ if (live_dex_registers_mask.IsBitSet(dex_register_number)) {
+ size_t location_catalog_entry_index = dex_register_locations_.Get(
+ start_index_in_dex_register_locations + index_in_dex_register_locations);
+ dex_register_map.SetLocationCatalogEntryIndex(
+ index_in_dex_register_locations,
+ location_catalog_entry_index,
+ num_dex_registers,
+ location_catalog_entries_.Size());
+ ++index_in_dex_register_locations;
+ }
+ }
+}
+
size_t StackMapStream::FindEntryWithTheSameDexMap() {
size_t current_entry_index = stack_maps_.Size();
auto entries_it = dex_map_hash_to_stack_map_indices_.find(current_entry_.dex_register_map_hash);
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 0c626be..0af983b 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -72,6 +72,7 @@
number_of_stack_maps_with_inline_info_(0),
dex_map_hash_to_stack_map_indices_(std::less<uint32_t>(), allocator->Adapter()),
current_entry_(),
+ current_inline_info_(),
stack_mask_size_(0),
inline_info_size_(0),
dex_register_maps_size_(0),
@@ -81,7 +82,9 @@
stack_maps_start_(0),
dex_register_maps_start_(0),
inline_infos_start_(0),
- needed_size_(0) {}
+ needed_size_(0),
+ current_dex_register_(0),
+ in_inline_frame_(false) {}
// See runtime/stack_map.h to know what these fields contain.
struct StackMapEntry {
@@ -99,7 +102,12 @@
};
struct InlineInfoEntry {
+ uint32_t dex_pc;
uint32_t method_index;
+ InvokeType invoke_type;
+ uint32_t num_dex_registers;
+ BitVector* live_dex_registers_mask;
+ size_t dex_register_locations_start_index;
};
void BeginStackMapEntry(uint32_t dex_pc,
@@ -110,11 +118,13 @@
uint8_t inlining_depth);
void EndStackMapEntry();
- void AddDexRegisterEntry(uint16_t dex_register,
- DexRegisterLocation::Kind kind,
- int32_t value);
+ void AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value);
- void AddInlineInfoEntry(uint32_t method_index);
+ void BeginInlineInfoEntry(uint32_t method_index,
+ uint32_t dex_pc,
+ InvokeType invoke_type,
+ uint32_t num_dex_registers);
+ void EndInlineInfoEntry();
// Prepares the stream to fill in a memory region. Must be called before FillIn.
// Returns the size (in bytes) needed to store this stream.
@@ -123,7 +133,8 @@
private:
size_t ComputeDexRegisterLocationCatalogSize() const;
- size_t ComputeDexRegisterMapSize(const StackMapEntry& entry) const;
+ size_t ComputeDexRegisterMapSize(uint32_t num_dex_registers,
+ const BitVector& live_dex_registers_mask) const;
size_t ComputeDexRegisterMapsSize() const;
size_t ComputeInlineInfoSize() const;
@@ -131,6 +142,10 @@
// or kNoSameDexMapFound if no such entry exists.
size_t FindEntryWithTheSameDexMap();
bool HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const;
+ void FillInDexRegisterMap(DexRegisterMap dex_register_map,
+ uint32_t num_dex_registers,
+ const BitVector& live_dex_registers_mask,
+ uint32_t start_index_in_dex_register_locations) const;
ArenaAllocator* allocator_;
GrowableArray<StackMapEntry> stack_maps_;
@@ -155,6 +170,7 @@
ArenaSafeMap<uint32_t, GrowableArray<uint32_t>> dex_map_hash_to_stack_map_indices_;
StackMapEntry current_entry_;
+ InlineInfoEntry current_inline_info_;
size_t stack_mask_size_;
size_t inline_info_size_;
size_t dex_register_maps_size_;
@@ -165,6 +181,8 @@
size_t dex_register_maps_start_;
size_t inline_infos_start_;
size_t needed_size_;
+ uint32_t current_dex_register_;
+ bool in_inline_frame_;
static constexpr uint32_t kNoSameDexMapFound = -1;
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index 3291a77..98e14ea 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -41,8 +41,8 @@
ArenaBitVector sp_mask(&arena, 0, false);
size_t number_of_dex_registers = 2;
stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
- stream.AddDexRegisterEntry(0, Kind::kInStack, 0); // Short location.
- stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Short location.
+ stream.AddDexRegisterEntry(Kind::kInStack, 0); // Short location.
+ stream.AddDexRegisterEntry(Kind::kConstant, -2); // Short location.
stream.EndStackMapEntry();
size_t size = stream.PrepareForFillIn();
@@ -124,19 +124,22 @@
sp_mask1.SetBit(2);
sp_mask1.SetBit(4);
size_t number_of_dex_registers = 2;
+ size_t number_of_dex_registers_in_inline_info = 0;
stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 2);
- stream.AddDexRegisterEntry(0, Kind::kInStack, 0); // Short location.
- stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location.
- stream.AddInlineInfoEntry(42);
- stream.AddInlineInfoEntry(82);
+ stream.AddDexRegisterEntry(Kind::kInStack, 0); // Short location.
+ stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location.
+ stream.BeginInlineInfoEntry(82, 3, kDirect, number_of_dex_registers_in_inline_info);
+ stream.EndInlineInfoEntry();
+ stream.BeginInlineInfoEntry(42, 2, kStatic, number_of_dex_registers_in_inline_info);
+ stream.EndInlineInfoEntry();
stream.EndStackMapEntry();
ArenaBitVector sp_mask2(&arena, 0, true);
sp_mask2.SetBit(3);
sp_mask1.SetBit(8);
stream.BeginStackMapEntry(1, 128, 0xFF, &sp_mask2, number_of_dex_registers, 0);
- stream.AddDexRegisterEntry(0, Kind::kInRegister, 18); // Short location.
- stream.AddDexRegisterEntry(1, Kind::kInFpuRegister, 3); // Short location.
+ stream.AddDexRegisterEntry(Kind::kInRegister, 18); // Short location.
+ stream.AddDexRegisterEntry(Kind::kInFpuRegister, 3); // Short location.
stream.EndStackMapEntry();
size_t size = stream.PrepareForFillIn();
@@ -211,8 +214,12 @@
ASSERT_TRUE(stack_map.HasInlineInfo(code_info));
InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map);
ASSERT_EQ(2u, inline_info.GetDepth());
- ASSERT_EQ(42u, inline_info.GetMethodReferenceIndexAtDepth(0));
- ASSERT_EQ(82u, inline_info.GetMethodReferenceIndexAtDepth(1));
+ ASSERT_EQ(82u, inline_info.GetMethodIndexAtDepth(0));
+ ASSERT_EQ(42u, inline_info.GetMethodIndexAtDepth(1));
+ ASSERT_EQ(3u, inline_info.GetDexPcAtDepth(0));
+ ASSERT_EQ(2u, inline_info.GetDexPcAtDepth(1));
+ ASSERT_EQ(kDirect, inline_info.GetInvokeTypeAtDepth(0));
+ ASSERT_EQ(kStatic, inline_info.GetInvokeTypeAtDepth(1));
}
// Second stack map.
@@ -277,8 +284,8 @@
ArenaBitVector sp_mask(&arena, 0, false);
uint32_t number_of_dex_registers = 2;
stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
- stream.AddDexRegisterEntry(0, Kind::kNone, 0); // No location.
- stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location.
+ stream.AddDexRegisterEntry(Kind::kNone, 0); // No location.
+ stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location.
stream.EndStackMapEntry();
size_t size = stream.PrepareForFillIn();
@@ -364,13 +371,13 @@
// as using a single value (in the whole CodeInfo object) would
// make this Dex register mapping data empty (see
// art::DexRegisterMap::SingleEntrySizeInBits).
- stream.AddDexRegisterEntry(i, Kind::kConstant, i % 2); // Short location.
+ stream.AddDexRegisterEntry(Kind::kConstant, i % 2); // Short location.
}
stream.EndStackMapEntry();
// Create the second stack map (and its Dex register map).
stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
for (uint32_t i = 0; i < number_of_dex_registers; ++i) {
- stream.AddDexRegisterEntry(i, Kind::kConstant, 0); // Short location.
+ stream.AddDexRegisterEntry(Kind::kConstant, 0); // Short location.
}
stream.EndStackMapEntry();
@@ -420,18 +427,18 @@
uint32_t number_of_dex_registers = 2;
// First stack map.
stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
- stream.AddDexRegisterEntry(0, Kind::kInRegister, 0); // Short location.
- stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location.
+ stream.AddDexRegisterEntry(Kind::kInRegister, 0); // Short location.
+ stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location.
stream.EndStackMapEntry();
// Second stack map, which should share the same dex register map.
stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
- stream.AddDexRegisterEntry(0, Kind::kInRegister, 0); // Short location.
- stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location.
+ stream.AddDexRegisterEntry(Kind::kInRegister, 0); // Short location.
+ stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location.
stream.EndStackMapEntry();
// Third stack map (doesn't share the dex register map).
stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
- stream.AddDexRegisterEntry(0, Kind::kInRegister, 2); // Short location.
- stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location.
+ stream.AddDexRegisterEntry(Kind::kInRegister, 2); // Short location.
+ stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location.
stream.EndStackMapEntry();
size_t size = stream.PrepareForFillIn();
@@ -500,4 +507,175 @@
ASSERT_FALSE(stack_map.HasInlineInfo(code_info));
}
+TEST(StackMapTest, InlineTest) {
+ ArenaPool pool;
+ ArenaAllocator arena(&pool);
+ StackMapStream stream(&arena);
+
+ ArenaBitVector sp_mask1(&arena, 0, true);
+ sp_mask1.SetBit(2);
+ sp_mask1.SetBit(4);
+
+ // First stack map.
+ stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask1, 2, 2);
+ stream.AddDexRegisterEntry(Kind::kInStack, 0);
+ stream.AddDexRegisterEntry(Kind::kConstant, 4);
+
+ stream.BeginInlineInfoEntry(42, 2, kStatic, 1);
+ stream.AddDexRegisterEntry(Kind::kInStack, 8);
+ stream.EndInlineInfoEntry();
+ stream.BeginInlineInfoEntry(82, 3, kStatic, 3);
+ stream.AddDexRegisterEntry(Kind::kInStack, 16);
+ stream.AddDexRegisterEntry(Kind::kConstant, 20);
+ stream.AddDexRegisterEntry(Kind::kInRegister, 15);
+ stream.EndInlineInfoEntry();
+
+ stream.EndStackMapEntry();
+
+ // Second stack map.
+ stream.BeginStackMapEntry(2, 22, 0x3, &sp_mask1, 2, 3);
+ stream.AddDexRegisterEntry(Kind::kInStack, 56);
+ stream.AddDexRegisterEntry(Kind::kConstant, 0);
+
+ stream.BeginInlineInfoEntry(42, 2, kDirect, 1);
+ stream.AddDexRegisterEntry(Kind::kInStack, 12);
+ stream.EndInlineInfoEntry();
+ stream.BeginInlineInfoEntry(82, 3, kStatic, 3);
+ stream.AddDexRegisterEntry(Kind::kInStack, 80);
+ stream.AddDexRegisterEntry(Kind::kConstant, 10);
+ stream.AddDexRegisterEntry(Kind::kInRegister, 5);
+ stream.EndInlineInfoEntry();
+ stream.BeginInlineInfoEntry(52, 5, kVirtual, 0);
+ stream.EndInlineInfoEntry();
+
+ stream.EndStackMapEntry();
+
+ // Third stack map.
+ stream.BeginStackMapEntry(4, 56, 0x3, &sp_mask1, 2, 0);
+ stream.AddDexRegisterEntry(Kind::kNone, 0);
+ stream.AddDexRegisterEntry(Kind::kConstant, 4);
+ stream.EndStackMapEntry();
+
+ // Fourth stack map.
+ stream.BeginStackMapEntry(6, 78, 0x3, &sp_mask1, 2, 3);
+ stream.AddDexRegisterEntry(Kind::kInStack, 56);
+ stream.AddDexRegisterEntry(Kind::kConstant, 0);
+
+ stream.BeginInlineInfoEntry(42, 2, kVirtual, 0);
+ stream.EndInlineInfoEntry();
+ stream.BeginInlineInfoEntry(52, 5, kInterface, 1);
+ stream.AddDexRegisterEntry(Kind::kInRegister, 2);
+ stream.EndInlineInfoEntry();
+ stream.BeginInlineInfoEntry(52, 10, kStatic, 2);
+ stream.AddDexRegisterEntry(Kind::kNone, 0);
+ stream.AddDexRegisterEntry(Kind::kInRegister, 3);
+ stream.EndInlineInfoEntry();
+
+ stream.EndStackMapEntry();
+
+ size_t size = stream.PrepareForFillIn();
+ void* memory = arena.Alloc(size, kArenaAllocMisc);
+ MemoryRegion region(memory, size);
+ stream.FillIn(region);
+
+ CodeInfo ci(region);
+
+ {
+ // Verify first stack map.
+ StackMap sm0 = ci.GetStackMapAt(0);
+
+ DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, 2);
+ ASSERT_EQ(0, dex_registers0.GetStackOffsetInBytes(0, 2, ci));
+ ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci));
+
+ InlineInfo if0 = ci.GetInlineInfoOf(sm0);
+ ASSERT_EQ(2u, if0.GetDepth());
+ ASSERT_EQ(2u, if0.GetDexPcAtDepth(0));
+ ASSERT_EQ(42u, if0.GetMethodIndexAtDepth(0));
+ ASSERT_EQ(kStatic, if0.GetInvokeTypeAtDepth(0));
+ ASSERT_EQ(3u, if0.GetDexPcAtDepth(1));
+ ASSERT_EQ(82u, if0.GetMethodIndexAtDepth(1));
+ ASSERT_EQ(kStatic, if0.GetInvokeTypeAtDepth(1));
+
+ DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if0, 1);
+ ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0, 1, ci));
+
+ DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if0, 3);
+ ASSERT_EQ(16, dex_registers2.GetStackOffsetInBytes(0, 3, ci));
+ ASSERT_EQ(20, dex_registers2.GetConstant(1, 3, ci));
+ ASSERT_EQ(15, dex_registers2.GetMachineRegister(2, 3, ci));
+ }
+
+ {
+ // Verify second stack map.
+ StackMap sm1 = ci.GetStackMapAt(1);
+
+ DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm1, 2);
+ ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci));
+ ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci));
+
+ InlineInfo if1 = ci.GetInlineInfoOf(sm1);
+ ASSERT_EQ(3u, if1.GetDepth());
+ ASSERT_EQ(2u, if1.GetDexPcAtDepth(0));
+ ASSERT_EQ(42u, if1.GetMethodIndexAtDepth(0));
+ ASSERT_EQ(kDirect, if1.GetInvokeTypeAtDepth(0));
+ ASSERT_EQ(3u, if1.GetDexPcAtDepth(1));
+ ASSERT_EQ(82u, if1.GetMethodIndexAtDepth(1));
+ ASSERT_EQ(kStatic, if1.GetInvokeTypeAtDepth(1));
+ ASSERT_EQ(5u, if1.GetDexPcAtDepth(2));
+ ASSERT_EQ(52u, if1.GetMethodIndexAtDepth(2));
+ ASSERT_EQ(kVirtual, if1.GetInvokeTypeAtDepth(2));
+
+ DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if1, 1);
+ ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0, 1, ci));
+
+ DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if1, 3);
+ ASSERT_EQ(80, dex_registers2.GetStackOffsetInBytes(0, 3, ci));
+ ASSERT_EQ(10, dex_registers2.GetConstant(1, 3, ci));
+ ASSERT_EQ(5, dex_registers2.GetMachineRegister(2, 3, ci));
+
+ ASSERT_FALSE(if1.HasDexRegisterMapAtDepth(2));
+ }
+
+ {
+ // Verify third stack map.
+ StackMap sm2 = ci.GetStackMapAt(2);
+
+ DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm2, 2);
+ ASSERT_FALSE(dex_registers0.IsDexRegisterLive(0));
+ ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci));
+ ASSERT_FALSE(sm2.HasInlineInfo(ci));
+ }
+
+ {
+ // Verify fourth stack map.
+ StackMap sm3 = ci.GetStackMapAt(3);
+
+ DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm3, 2);
+ ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci));
+ ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci));
+
+ InlineInfo if2 = ci.GetInlineInfoOf(sm3);
+ ASSERT_EQ(3u, if2.GetDepth());
+ ASSERT_EQ(2u, if2.GetDexPcAtDepth(0));
+ ASSERT_EQ(42u, if2.GetMethodIndexAtDepth(0));
+ ASSERT_EQ(kVirtual, if2.GetInvokeTypeAtDepth(0));
+ ASSERT_EQ(5u, if2.GetDexPcAtDepth(1));
+ ASSERT_EQ(52u, if2.GetMethodIndexAtDepth(1));
+ ASSERT_EQ(kInterface, if2.GetInvokeTypeAtDepth(1));
+ ASSERT_EQ(10u, if2.GetDexPcAtDepth(2));
+ ASSERT_EQ(52u, if2.GetMethodIndexAtDepth(2));
+ ASSERT_EQ(kStatic, if2.GetInvokeTypeAtDepth(2));
+
+ ASSERT_FALSE(if2.HasDexRegisterMapAtDepth(0));
+
+ DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(1, if2, 1);
+ ASSERT_EQ(2, dex_registers1.GetMachineRegister(0, 1, ci));
+
+ DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(2, if2, 2);
+ ASSERT_FALSE(dex_registers2.IsDexRegisterLive(0));
+ ASSERT_EQ(3, dex_registers2.GetMachineRegister(1, 2, ci));
+ }
+}
+
} // namespace art
diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc
index c410660..eca6f5a 100644
--- a/compiler/utils/arm/assembler_arm.cc
+++ b/compiler/utils/arm/assembler_arm.cc
@@ -860,8 +860,6 @@
// Set up call to Thread::Current()->pDeliverException.
__ LoadFromOffset(kLoadWord, R12, TR, QUICK_ENTRYPOINT_OFFSET(4, pDeliverException).Int32Value());
__ blx(R12);
- // Call never returns.
- __ bkpt(0);
#undef __
}
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index 313f365..dee8287 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -398,6 +398,8 @@
Condition cond = AL) = 0;
virtual void mls(Register rd, Register rn, Register rm, Register ra,
Condition cond = AL) = 0;
+ virtual void smull(Register rd_lo, Register rd_hi, Register rn, Register rm,
+ Condition cond = AL) = 0;
virtual void umull(Register rd_lo, Register rd_hi, Register rn, Register rm,
Condition cond = AL) = 0;
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index 9579691..6e165fc 100644
--- a/compiler/utils/arm/assembler_arm32.cc
+++ b/compiler/utils/arm/assembler_arm32.cc
@@ -200,6 +200,13 @@
}
+void Arm32Assembler::smull(Register rd_lo, Register rd_hi, Register rn,
+ Register rm, Condition cond) {
+ // Assembler registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs.
+ EmitMulOp(cond, B23 | B22, rd_lo, rd_hi, rn, rm);
+}
+
+
void Arm32Assembler::umull(Register rd_lo, Register rd_hi, Register rn,
Register rm, Condition cond) {
// Assembler registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs.
diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h
index b922d66..55ec7b4 100644
--- a/compiler/utils/arm/assembler_arm32.h
+++ b/compiler/utils/arm/assembler_arm32.h
@@ -90,6 +90,8 @@
Condition cond = AL) OVERRIDE;
void mls(Register rd, Register rn, Register rm, Register ra,
Condition cond = AL) OVERRIDE;
+ void smull(Register rd_lo, Register rd_hi, Register rn, Register rm,
+ Condition cond = AL) OVERRIDE;
void umull(Register rd_lo, Register rd_hi, Register rn, Register rm,
Condition cond = AL) OVERRIDE;
diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc
index 4a0ae0b..efd517b 100644
--- a/compiler/utils/arm/assembler_arm32_test.cc
+++ b/compiler/utils/arm/assembler_arm32_test.cc
@@ -293,12 +293,29 @@
f();
}
+ // NOTE: Only support simple test like "aaa=bbb"
+ bool EvalFilterString(std::string filter) {
+ if (filter.compare("") == 0) {
+ return false;
+ }
+
+ size_t equal_sign_index = filter.find('=');
+ if (equal_sign_index == std::string::npos) {
+ EXPECT_TRUE(false) << "Unsupported filter string.";
+ }
+
+ std::string lhs = filter.substr(0, equal_sign_index);
+ std::string rhs = filter.substr(equal_sign_index + 1, std::string::npos);
+ return lhs.compare(rhs) == 0;
+ }
+
void TemplateHelper(std::function<void(arm::Register)> f, int depth ATTRIBUTE_UNUSED,
- bool without_pc,
- std::string fmt, std::ostringstream& oss) {
+ bool without_pc, std::string fmt, std::string filter,
+ std::ostringstream& oss) {
std::vector<arm::Register*> registers = without_pc ? GetRegistersWithoutPC() : GetRegisters();
for (auto reg : registers) {
std::string after_reg = fmt;
+ std::string after_reg_filter = filter;
std::string reg_string = GetRegName<RegisterView::kUsePrimaryName>(*reg);
size_t reg_index;
@@ -308,14 +325,23 @@
after_reg.replace(reg_index, strlen(reg_token), reg_string);
}
+ while ((reg_index = after_reg_filter.find(reg_token)) != std::string::npos) {
+ after_reg_filter.replace(reg_index, strlen(reg_token), reg_string);
+ }
+ if (EvalFilterString(after_reg_filter)) {
+ continue;
+ }
+
ExecuteAndPrint([&] () { f(*reg); }, after_reg, oss);
}
}
void TemplateHelper(std::function<void(const arm::ShifterOperand&)> f, int depth ATTRIBUTE_UNUSED,
- bool without_pc ATTRIBUTE_UNUSED, std::string fmt, std::ostringstream& oss) {
+ bool without_pc ATTRIBUTE_UNUSED, std::string fmt, std::string filter,
+ std::ostringstream& oss) {
for (const arm::ShifterOperand& shift : GetShiftOperands()) {
std::string after_shift = fmt;
+ std::string after_shift_filter = filter;
std::string shift_string = GetShiftString(shift);
size_t shift_index;
@@ -323,30 +349,48 @@
after_shift.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string);
}
+ while ((shift_index = after_shift_filter.find(SHIFT_TOKEN)) != std::string::npos) {
+ after_shift_filter.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string);
+ }
+ if (EvalFilterString(after_shift_filter)) {
+ continue;
+ }
+
ExecuteAndPrint([&] () { f(shift); }, after_shift, oss);
}
}
void TemplateHelper(std::function<void(arm::Condition)> f, int depth ATTRIBUTE_UNUSED,
- bool without_pc ATTRIBUTE_UNUSED, std::string fmt, std::ostringstream& oss) {
+ bool without_pc ATTRIBUTE_UNUSED, std::string fmt, std::string filter,
+ std::ostringstream& oss) {
for (arm::Condition c : GetConditions()) {
std::string after_cond = fmt;
+ std::string after_cond_filter = filter;
size_t cond_index = after_cond.find(COND_TOKEN);
if (cond_index != std::string::npos) {
after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
}
+ cond_index = after_cond_filter.find(COND_TOKEN);
+ if (cond_index != std::string::npos) {
+ after_cond_filter.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
+ }
+ if (EvalFilterString(after_cond_filter)) {
+ continue;
+ }
+
ExecuteAndPrint([&] () { f(c); }, after_cond, oss);
}
}
template <typename... Args>
void TemplateHelper(std::function<void(arm::Register, Args...)> f, int depth, bool without_pc,
- std::string fmt, std::ostringstream& oss) {
+ std::string fmt, std::string filter, std::ostringstream& oss) {
std::vector<arm::Register*> registers = without_pc ? GetRegistersWithoutPC() : GetRegisters();
for (auto reg : registers) {
std::string after_reg = fmt;
+ std::string after_reg_filter = filter;
std::string reg_string = GetRegName<RegisterView::kUsePrimaryName>(*reg);
size_t reg_index;
@@ -356,17 +400,26 @@
after_reg.replace(reg_index, strlen(reg_token), reg_string);
}
+ while ((reg_index = after_reg_filter.find(reg_token)) != std::string::npos) {
+ after_reg_filter.replace(reg_index, strlen(reg_token), reg_string);
+ }
+ if (EvalFilterString(after_reg_filter)) {
+ continue;
+ }
+
auto lambda = [&] (Args... args) { f(*reg, args...); }; // NOLINT [readability/braces] [4]
TemplateHelper(std::function<void(Args...)>(lambda), depth + 1, without_pc,
- after_reg, oss);
+ after_reg, after_reg_filter, oss);
}
}
template <typename... Args>
void TemplateHelper(std::function<void(const arm::ShifterOperand&, Args...)> f, int depth,
- bool without_pc, std::string fmt, std::ostringstream& oss) {
+ bool without_pc, std::string fmt, std::string filter,
+ std::ostringstream& oss) {
for (const arm::ShifterOperand& shift : GetShiftOperands()) {
std::string after_shift = fmt;
+ std::string after_shift_filter = filter;
std::string shift_string = GetShiftString(shift);
size_t shift_index;
@@ -374,26 +427,42 @@
after_shift.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string);
}
+ while ((shift_index = after_shift_filter.find(SHIFT_TOKEN)) != std::string::npos) {
+ after_shift_filter.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string);
+ }
+ if (EvalFilterString(after_shift_filter)) {
+ continue;
+ }
+
auto lambda = [&] (Args... args) { f(shift, args...); }; // NOLINT [readability/braces] [4]
TemplateHelper(std::function<void(Args...)>(lambda), depth, without_pc,
- after_shift, oss);
+ after_shift, after_shift_filter, oss);
}
}
template <typename... Args>
void TemplateHelper(std::function<void(arm::Condition, Args...)> f, int depth, bool without_pc,
- std::string fmt, std::ostringstream& oss) {
+ std::string fmt, std::string filter, std::ostringstream& oss) {
for (arm::Condition c : GetConditions()) {
std::string after_cond = fmt;
+ std::string after_cond_filter = filter;
size_t cond_index = after_cond.find(COND_TOKEN);
if (cond_index != std::string::npos) {
after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
}
+ cond_index = after_cond_filter.find(COND_TOKEN);
+ if (cond_index != std::string::npos) {
+ after_cond_filter.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
+ }
+ if (EvalFilterString(after_cond_filter)) {
+ continue;
+ }
+
auto lambda = [&] (Args... args) { f(c, args...); }; // NOLINT [readability/braces] [4]
TemplateHelper(std::function<void(Args...)>(lambda), depth, without_pc,
- after_cond, oss);
+ after_cond, after_cond_filter, oss);
}
}
@@ -421,13 +490,13 @@
template <typename... Args>
void GenericTemplateHelper(std::function<void(Args...)> f, bool without_pc,
- std::string fmt, std::string test_name) {
+ std::string fmt, std::string test_name, std::string filter) {
first_ = false;
WarnOnCombinations(CountHelper<Args...>(without_pc));
std::ostringstream oss;
- TemplateHelper(f, 0, without_pc, fmt, oss);
+ TemplateHelper(f, 0, without_pc, fmt, filter, oss);
oss << "\n"; // Trailing newline.
@@ -436,26 +505,26 @@
template <typename... Args>
void T2Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt,
- std::string test_name) {
- GenericTemplateHelper(GetBoundFunction2(f), without_pc, fmt, test_name);
+ std::string test_name, std::string filter = "") {
+ GenericTemplateHelper(GetBoundFunction2(f), without_pc, fmt, test_name, filter);
}
template <typename... Args>
void T3Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt,
- std::string test_name) {
- GenericTemplateHelper(GetBoundFunction3(f), without_pc, fmt, test_name);
+ std::string test_name, std::string filter = "") {
+ GenericTemplateHelper(GetBoundFunction3(f), without_pc, fmt, test_name, filter);
}
template <typename... Args>
void T4Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt,
- std::string test_name) {
- GenericTemplateHelper(GetBoundFunction4(f), without_pc, fmt, test_name);
+ std::string test_name, std::string filter = "") {
+ GenericTemplateHelper(GetBoundFunction4(f), without_pc, fmt, test_name, filter);
}
template <typename... Args>
void T5Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt,
- std::string test_name) {
- GenericTemplateHelper(GetBoundFunction5(f), without_pc, fmt, test_name);
+ std::string test_name, std::string filter = "") {
+ GenericTemplateHelper(GetBoundFunction5(f), without_pc, fmt, test_name, filter);
}
private:
@@ -565,15 +634,18 @@
}
TEST_F(AssemblerArm32Test, Mla) {
- T5Helper(&arm::Arm32Assembler::mla, true, "mla{cond} {reg1}, {reg2}, {reg3}, {reg4}", "mul");
+ T5Helper(&arm::Arm32Assembler::mla, true, "mla{cond} {reg1}, {reg2}, {reg3}, {reg4}", "mla");
}
-/* TODO: Needs support to filter out register combinations, as rdhi must not be equal to rdlo.
TEST_F(AssemblerArm32Test, Umull) {
T5Helper(&arm::Arm32Assembler::umull, true, "umull{cond} {reg1}, {reg2}, {reg3}, {reg4}",
- "umull");
+ "umull", "{reg1}={reg2}"); // Skip the cases where reg1 == reg2.
}
-*/
+
+TEST_F(AssemblerArm32Test, Smull) {
+ T5Helper(&arm::Arm32Assembler::smull, true, "smull{cond} {reg1}, {reg2}, {reg3}, {reg4}",
+ "smull", "{reg1}={reg2}"); // Skip the cases where reg1 == reg2.
+}
TEST_F(AssemblerArm32Test, Sdiv) {
T4Helper(&arm::Arm32Assembler::sdiv, true, "sdiv{cond} {reg1}, {reg2}, {reg3}", "sdiv");
@@ -655,9 +727,10 @@
T4Helper(&arm::Arm32Assembler::rsc, true, "rsc{cond} {reg1}, {reg2}, {shift}", "rsc");
}
-/* TODO: Needs support to filter out register combinations, as reg1 must not be equal to reg3.
+/* TODO: Need better filter support.
TEST_F(AssemblerArm32Test, Strex) {
- RRRCWithoutPCHelper(&arm::Arm32Assembler::strex, "strex{cond} {reg1}, {reg2}, [{reg3}]", "strex");
+ T4Helper(&arm::Arm32Assembler::strex, "strex{cond} {reg1}, {reg2}, [{reg3}]", "strex",
+ "{reg1}={reg2}||{reg1}={reg3}"); // Skip the cases where reg1 == reg2 || reg1 == reg3.
}
*/
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 3b42f63..75f2b77 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -238,6 +238,24 @@
}
+void Thumb2Assembler::smull(Register rd_lo, Register rd_hi, Register rn,
+ Register rm, Condition cond) {
+ CheckCondition(cond);
+
+ uint32_t op1 = 0U /* 0b000; */;
+ uint32_t op2 = 0U /* 0b0000 */;
+ int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 | B23 |
+ op1 << 20 |
+ op2 << 4 |
+ static_cast<uint32_t>(rd_lo) << 12 |
+ static_cast<uint32_t>(rd_hi) << 8 |
+ static_cast<uint32_t>(rn) << 16 |
+ static_cast<uint32_t>(rm);
+
+ Emit32(encoding);
+}
+
+
void Thumb2Assembler::umull(Register rd_lo, Register rd_hi, Register rn,
Register rm, Condition cond) {
CheckCondition(cond);
@@ -740,13 +758,6 @@
return true;
}
- // Check for MOV with an ROR.
- if (opcode == MOV && so.IsRegister() && so.IsShift() && so.GetShift() == ROR) {
- if (so.GetImmediate() != 0) {
- return true;
- }
- }
-
bool rn_is_valid = true;
// Check for single operand instructions and ADD/SUB.
@@ -792,6 +803,19 @@
}
}
+ // Check for register shift operand.
+ if (so.IsRegister() && so.IsShift()) {
+ if (opcode != MOV) {
+ return true;
+ }
+ // Check for MOV with an ROR.
+ if (so.GetShift() == ROR) {
+ if (so.GetImmediate() != 0) {
+ return true;
+ }
+ }
+ }
+
// The instruction can be encoded in 16 bits.
return false;
}
@@ -1614,7 +1638,6 @@
// branch the size may change if it so happens that other branches change size that change
// the distance to the target and that distance puts this branch over the limit for 16 bits.
if (size == Branch::k16Bit) {
- DCHECK(!force_32bit_branches_);
Emit16(0); // Space for a 16 bit branch.
} else {
Emit32(0); // Space for a 32 bit branch.
@@ -1622,7 +1645,7 @@
} else {
// Branch is to an unbound label. Emit space for it.
uint16_t branch_id = AddBranch(branch_type, pc, cond); // Unresolved branch.
- if (force_32bit_branches_ || force_32bit_) {
+ if (!CanRelocateBranches() || force_32bit_) {
Emit16(static_cast<uint16_t>(label->position_)); // Emit current label link.
Emit16(0); // another 16 bits.
} else {
@@ -2258,7 +2281,7 @@
uint32_t branch_location = branch->GetLocation();
uint16_t next = buffer_.Load<uint16_t>(branch_location); // Get next in chain.
if (changed) {
- DCHECK(!force_32bit_branches_);
+ DCHECK(CanRelocateBranches());
MakeHoleForBranch(branch->GetLocation(), 2);
if (branch->IsCompareAndBranch()) {
// A cbz/cbnz instruction has changed size. There is no valid encoding for
@@ -2718,21 +2741,21 @@
void Thumb2Assembler::CompareAndBranchIfZero(Register r, Label* label) {
- if (force_32bit_branches_) {
+ if (CanRelocateBranches()) {
+ cbz(r, label);
+ } else {
cmp(r, ShifterOperand(0));
b(label, EQ);
- } else {
- cbz(r, label);
}
}
void Thumb2Assembler::CompareAndBranchIfNonZero(Register r, Label* label) {
- if (force_32bit_branches_) {
+ if (CanRelocateBranches()) {
+ cbnz(r, label);
+ } else {
cmp(r, ShifterOperand(0));
b(label, NE);
- } else {
- cbnz(r, label);
}
}
} // namespace arm
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index e33c240..90d489f 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -31,8 +31,8 @@
class Thumb2Assembler FINAL : public ArmAssembler {
public:
- explicit Thumb2Assembler(bool force_32bit_branches = false)
- : force_32bit_branches_(force_32bit_branches),
+ explicit Thumb2Assembler(bool can_relocate_branches = true)
+ : can_relocate_branches_(can_relocate_branches),
force_32bit_(false),
it_cond_index_(kNoItCondition),
next_condition_(AL) {
@@ -52,8 +52,8 @@
return force_32bit_;
}
- bool IsForced32BitBranches() const {
- return force_32bit_branches_;
+ bool CanRelocateBranches() const {
+ return can_relocate_branches_;
}
void FinalizeInstructions(const MemoryRegion& region) OVERRIDE {
@@ -112,6 +112,8 @@
Condition cond = AL) OVERRIDE;
void mls(Register rd, Register rn, Register rm, Register ra,
Condition cond = AL) OVERRIDE;
+ void smull(Register rd_lo, Register rd_hi, Register rn, Register rm,
+ Condition cond = AL) OVERRIDE;
void umull(Register rd_lo, Register rd_hi, Register rn, Register rm,
Condition cond = AL) OVERRIDE;
@@ -437,8 +439,12 @@
void EmitShift(Register rd, Register rm, Shift shift, uint8_t amount, bool setcc = false);
void EmitShift(Register rd, Register rn, Shift shift, Register rm, bool setcc = false);
- bool force_32bit_branches_; // Force the assembler to use 32 bit branch instructions.
- bool force_32bit_; // Force the assembler to use 32 bit thumb2 instructions.
+ // Whether the assembler can relocate branches. If false, unresolved branches will be
+ // emitted on 32bits.
+ bool can_relocate_branches_;
+
+ // Force the assembler to use 32 bit thumb2 instructions.
+ bool force_32bit_;
// IfThen conditions. Used to check that conditional instructions match the preceding IT.
Condition it_conditions_[4];
@@ -554,12 +560,21 @@
// size of the branch to change return true. Otherwise return false.
bool Resolve(uint32_t target) {
target_ = target;
- Size newsize = CalculateSize();
- if (size_ != newsize) {
- size_ = newsize;
- return true;
+ if (assembler_->CanRelocateBranches()) {
+ Size new_size = CalculateSize();
+ if (size_ != new_size) {
+ size_ = new_size;
+ return true;
+ }
+ return false;
+ } else {
+ if (kIsDebugBuild) {
+ Size new_size = CalculateSize();
+ // Check that the size has not increased.
+ DCHECK(!(new_size == k32Bit && size_ == k16Bit));
+ }
+ return false;
}
- return false;
}
// Move a cbz/cbnz branch. This is always forward.
@@ -575,6 +590,7 @@
// size of the branch instruction. It returns true if the branch
// has changed size.
bool Relocate(uint32_t oldlocation, int32_t delta) {
+ DCHECK(assembler_->CanRelocateBranches());
if (location_ > oldlocation) {
location_ += delta;
}
@@ -587,9 +603,9 @@
}
// Calculate the new size.
- Size newsize = CalculateSize();
- if (size_ != newsize) {
- size_ = newsize;
+ Size new_size = CalculateSize();
+ if (size_ != new_size) {
+ size_ = new_size;
return true;
}
return false;
@@ -631,15 +647,13 @@
private:
// Calculate the size of the branch instruction based on its type and offset.
Size CalculateSize() const {
- if (assembler_->IsForced32BitBranches()) {
- return k32Bit;
- }
if (target_ == kUnresolved) {
if (assembler_->IsForced32Bit() && (type_ == kUnconditional || type_ == kConditional)) {
return k32Bit;
}
- return k16Bit;
+ return assembler_->CanRelocateBranches() ? k16Bit : k32Bit;
}
+ // When the target is resolved, we know the best encoding for it.
int32_t delta = target_ - location_ - 4;
if (delta < 0) {
delta = -delta;
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 5f5561a..733441b 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -89,23 +89,24 @@
EXPECT_TRUE(CheckTools());
}
+#define __ GetAssembler()->
TEST_F(AssemblerThumb2Test, Sbfx) {
- GetAssembler()->sbfx(arm::R0, arm::R1, 0, 1);
- GetAssembler()->sbfx(arm::R0, arm::R1, 0, 8);
- GetAssembler()->sbfx(arm::R0, arm::R1, 0, 16);
- GetAssembler()->sbfx(arm::R0, arm::R1, 0, 32);
+ __ sbfx(arm::R0, arm::R1, 0, 1);
+ __ sbfx(arm::R0, arm::R1, 0, 8);
+ __ sbfx(arm::R0, arm::R1, 0, 16);
+ __ sbfx(arm::R0, arm::R1, 0, 32);
- GetAssembler()->sbfx(arm::R0, arm::R1, 8, 1);
- GetAssembler()->sbfx(arm::R0, arm::R1, 8, 8);
- GetAssembler()->sbfx(arm::R0, arm::R1, 8, 16);
- GetAssembler()->sbfx(arm::R0, arm::R1, 8, 24);
+ __ sbfx(arm::R0, arm::R1, 8, 1);
+ __ sbfx(arm::R0, arm::R1, 8, 8);
+ __ sbfx(arm::R0, arm::R1, 8, 16);
+ __ sbfx(arm::R0, arm::R1, 8, 24);
- GetAssembler()->sbfx(arm::R0, arm::R1, 16, 1);
- GetAssembler()->sbfx(arm::R0, arm::R1, 16, 8);
- GetAssembler()->sbfx(arm::R0, arm::R1, 16, 16);
+ __ sbfx(arm::R0, arm::R1, 16, 1);
+ __ sbfx(arm::R0, arm::R1, 16, 8);
+ __ sbfx(arm::R0, arm::R1, 16, 16);
- GetAssembler()->sbfx(arm::R0, arm::R1, 31, 1);
+ __ sbfx(arm::R0, arm::R1, 31, 1);
const char* expected =
"sbfx r0, r1, #0, #1\n"
@@ -127,21 +128,21 @@
}
TEST_F(AssemblerThumb2Test, Ubfx) {
- GetAssembler()->ubfx(arm::R0, arm::R1, 0, 1);
- GetAssembler()->ubfx(arm::R0, arm::R1, 0, 8);
- GetAssembler()->ubfx(arm::R0, arm::R1, 0, 16);
- GetAssembler()->ubfx(arm::R0, arm::R1, 0, 32);
+ __ ubfx(arm::R0, arm::R1, 0, 1);
+ __ ubfx(arm::R0, arm::R1, 0, 8);
+ __ ubfx(arm::R0, arm::R1, 0, 16);
+ __ ubfx(arm::R0, arm::R1, 0, 32);
- GetAssembler()->ubfx(arm::R0, arm::R1, 8, 1);
- GetAssembler()->ubfx(arm::R0, arm::R1, 8, 8);
- GetAssembler()->ubfx(arm::R0, arm::R1, 8, 16);
- GetAssembler()->ubfx(arm::R0, arm::R1, 8, 24);
+ __ ubfx(arm::R0, arm::R1, 8, 1);
+ __ ubfx(arm::R0, arm::R1, 8, 8);
+ __ ubfx(arm::R0, arm::R1, 8, 16);
+ __ ubfx(arm::R0, arm::R1, 8, 24);
- GetAssembler()->ubfx(arm::R0, arm::R1, 16, 1);
- GetAssembler()->ubfx(arm::R0, arm::R1, 16, 8);
- GetAssembler()->ubfx(arm::R0, arm::R1, 16, 16);
+ __ ubfx(arm::R0, arm::R1, 16, 1);
+ __ ubfx(arm::R0, arm::R1, 16, 8);
+ __ ubfx(arm::R0, arm::R1, 16, 16);
- GetAssembler()->ubfx(arm::R0, arm::R1, 31, 1);
+ __ ubfx(arm::R0, arm::R1, 31, 1);
const char* expected =
"ubfx r0, r1, #0, #1\n"
@@ -163,7 +164,7 @@
}
TEST_F(AssemblerThumb2Test, Vmstat) {
- GetAssembler()->vmstat();
+ __ vmstat();
const char* expected = "vmrs APSR_nzcv, FPSCR\n";
@@ -171,10 +172,10 @@
}
TEST_F(AssemblerThumb2Test, ldrexd) {
- GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R0);
- GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R1);
- GetAssembler()->ldrexd(arm::R0, arm::R1, arm::R2);
- GetAssembler()->ldrexd(arm::R5, arm::R3, arm::R7);
+ __ ldrexd(arm::R0, arm::R1, arm::R0);
+ __ ldrexd(arm::R0, arm::R1, arm::R1);
+ __ ldrexd(arm::R0, arm::R1, arm::R2);
+ __ ldrexd(arm::R5, arm::R3, arm::R7);
const char* expected =
"ldrexd r0, r1, [r0]\n"
@@ -185,10 +186,10 @@
}
TEST_F(AssemblerThumb2Test, strexd) {
- GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R0);
- GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R1);
- GetAssembler()->strexd(arm::R9, arm::R0, arm::R1, arm::R2);
- GetAssembler()->strexd(arm::R9, arm::R5, arm::R3, arm::R7);
+ __ strexd(arm::R9, arm::R0, arm::R1, arm::R0);
+ __ strexd(arm::R9, arm::R0, arm::R1, arm::R1);
+ __ strexd(arm::R9, arm::R0, arm::R1, arm::R2);
+ __ strexd(arm::R9, arm::R5, arm::R3, arm::R7);
const char* expected =
"strexd r9, r0, r1, [r0]\n"
@@ -199,9 +200,9 @@
}
TEST_F(AssemblerThumb2Test, LdrdStrd) {
- GetAssembler()->ldrd(arm::R0, arm::Address(arm::R2, 8));
- GetAssembler()->ldrd(arm::R0, arm::Address(arm::R12));
- GetAssembler()->strd(arm::R0, arm::Address(arm::R2, 8));
+ __ ldrd(arm::R0, arm::Address(arm::R2, 8));
+ __ ldrd(arm::R0, arm::Address(arm::R12));
+ __ strd(arm::R0, arm::Address(arm::R2, 8));
const char* expected =
"ldrd r0, r1, [r2, #8]\n"
@@ -211,7 +212,6 @@
}
TEST_F(AssemblerThumb2Test, eor) {
-#define __ GetAssembler()->
__ eor(arm::R1, arm::R1, arm::ShifterOperand(arm::R0));
__ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R1));
__ eor(arm::R1, arm::R8, arm::ShifterOperand(arm::R0));
@@ -230,23 +230,47 @@
TEST_F(AssemblerThumb2Test, sub) {
__ subs(arm::R1, arm::R0, arm::ShifterOperand(42));
__ sub(arm::R1, arm::R0, arm::ShifterOperand(42));
+ __ subs(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
+ __ sub(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
const char* expected =
"subs r1, r0, #42\n"
- "subw r1, r0, #42\n";
+ "subw r1, r0, #42\n"
+ "subs r1, r0, r2, asr #31\n"
+ "sub r1, r0, r2, asr #31\n";
DriverStr(expected, "sub");
}
TEST_F(AssemblerThumb2Test, add) {
__ adds(arm::R1, arm::R0, arm::ShifterOperand(42));
__ add(arm::R1, arm::R0, arm::ShifterOperand(42));
+ __ adds(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
+ __ add(arm::R1, arm::R0, arm::ShifterOperand(arm::R2, arm::ASR, 31));
const char* expected =
"adds r1, r0, #42\n"
- "addw r1, r0, #42\n";
+ "addw r1, r0, #42\n"
+ "adds r1, r0, r2, asr #31\n"
+ "add r1, r0, r2, asr #31\n";
DriverStr(expected, "add");
}
+TEST_F(AssemblerThumb2Test, umull) {
+ __ umull(arm::R0, arm::R1, arm::R2, arm::R3);
+
+ const char* expected =
+ "umull r0, r1, r2, r3\n";
+ DriverStr(expected, "umull");
+}
+
+TEST_F(AssemblerThumb2Test, smull) {
+ __ smull(arm::R0, arm::R1, arm::R2, arm::R3);
+
+ const char* expected =
+ "smull r0, r1, r2, r3\n";
+ DriverStr(expected, "smull");
+}
+
TEST_F(AssemblerThumb2Test, StoreWordToThumbOffset) {
arm::StoreOperandType type = arm::kStoreWord;
int32_t offset = 4092;
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 772fa9a..7738627 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -15,9 +15,11 @@
*/
#include <dirent.h>
+#include <errno.h>
#include <fstream>
-#include <sys/types.h>
#include <map>
+#include <string.h>
+#include <sys/types.h>
#include "gtest/gtest.h"
#include "utils/arm/assembler_thumb2.h"
@@ -105,12 +107,14 @@
// Assemble the .S
snprintf(cmd, sizeof(cmd), "%sas %s -o %s.o", toolsdir.c_str(), filename, filename);
- system(cmd);
+ int cmd_result = system(cmd);
+ ASSERT_EQ(cmd_result, 0) << strerror(errno);
// Remove the $d symbols to prevent the disassembler dumping the instructions
// as .word
snprintf(cmd, sizeof(cmd), "%sobjcopy -N '$d' %s.o %s.oo", toolsdir.c_str(), filename, filename);
- system(cmd);
+ int cmd_result2 = system(cmd);
+ ASSERT_EQ(cmd_result2, 0) << strerror(errno);
// Disassemble.
@@ -119,7 +123,8 @@
if (kPrintResults) {
// Print the results only, don't check. This is used to generate new output for inserting
// into the .inc file.
- system(cmd);
+ int cmd_result3 = system(cmd);
+ ASSERT_EQ(cmd_result3, 0) << strerror(errno);
} else {
// Check the results match the appropriate results in the .inc file.
FILE *fp = popen(cmd, "r");
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 7488578..3c145d7 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -313,8 +313,7 @@
/*
* All generated callsites for interface invokes and invocation slow paths will load arguments
* as usual - except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
- * the method_idx. This wrapper will save arg1-arg3, load the caller's Method*, align the
- * stack and call the appropriate C helper.
+ * the method_idx. This wrapper will save arg1-arg3, and call the appropriate C helper.
* NOTE: "this" is first visible argument of the target, and so can be found in arg1/r1.
*
* The helper will attempt to locate the target and return a 64-bit result in r0/r1 consisting
@@ -330,13 +329,10 @@
.extern \cxx_name
ENTRY \c_name
SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case allocation triggers GC
- ldr r2, [sp, #FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE] @ pass caller Method*
- mov r3, r9 @ pass Thread::Current
- mov r12, sp
- str r12, [sp, #-16]! @ expand the frame and pass SP
+ mov r2, r9 @ pass Thread::Current
+ mov r3, sp
.cfi_adjust_cfa_offset 16
bl \cxx_name @ (method_idx, this, caller, Thread*, SP)
- add sp, #16 @ strip the extra frame
.cfi_adjust_cfa_offset -16
mov r12, r1 @ save Method*->code_
RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index f8b0734..6b16a2e5 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -459,8 +459,7 @@
/*
* All generated callsites for interface invokes and invocation slow paths will load arguments
* as usual - except instead of loading arg0/x0 with the target Method*, arg0/x0 will contain
- * the method_idx. This wrapper will save arg1-arg3, load the caller's Method*, align the
- * stack and call the appropriate C helper.
+ * the method_idx. This wrapper will save arg1-arg3, and call the appropriate C helper.
* NOTE: "this" is first visible argument of the target, and so can be found in arg1/x1.
*
* The helper will attempt to locate the target and return a 128-bit result in x0/x1 consisting
@@ -483,10 +482,9 @@
// Helper signature is always
// (method_idx, *this_object, *caller_method, *self, sp)
- ldr w2, [sp, #FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE] // pass caller Method*
- mov x3, xSELF // pass Thread::Current
- mov x4, sp
- bl \cxx_name // (method_idx, this, caller, Thread*, SP)
+ mov x2, xSELF // pass Thread::Current
+ mov x3, sp
+ bl \cxx_name // (method_idx, this, Thread*, SP)
mov xIP0, x1 // save Method*->code_
RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
cbz x0, 1f // did we find the target? if not go to exception delivery
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index ee5c59f..92b180e 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -439,8 +439,7 @@
/*
* All generated callsites for interface invokes and invocation slow paths will load arguments
* as usual - except instead of loading arg0/$a0 with the target Method*, arg0/$a0 will contain
- * the method_idx. This wrapper will save arg1-arg3, load the caller's Method*, align the
- * stack and call the appropriate C helper.
+ * the method_idx. This wrapper will save arg1-arg3, and call the appropriate C helper.
* NOTE: "this" is first visable argument of the target, and so can be found in arg1/$a1.
*
* The helper will attempt to locate the target and return a 64-bit result in $v0/$v1 consisting
@@ -456,15 +455,13 @@
.extern \cxx_name
ENTRY \c_name
SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME # save callee saves in case allocation triggers GC
- lw $a2, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE+ARG_SLOT_SIZE($sp) # pass caller Method*
- addiu $t0, $sp, ARG_SLOT_SIZE # save $sp (remove arg slots)
- move $a3, rSELF # pass Thread::Current
- jal \cxx_name # (method_idx, this, caller, Thread*, $sp)
- sw $t0, 16($sp) # pass $sp
- move $a0, $v0 # save target Method*
+ move $a2, rSELF # pass Thread::Current
+ jal \cxx_name # (method_idx, this, Thread*, $sp)
+ addiu $a3, $sp, ARG_SLOT_SIZE # pass $sp (remove arg slots)
+ move $a0, $v0 # save target Method*
RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
beqz $v0, 1f
- move $t9, $v1 # save $v0->code_
+ move $t9, $v1 # save $v0->code_
jalr $zero, $t9
nop
1:
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index ff79b5d..b7320a6 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -529,10 +529,9 @@
.extern \cxx_name
ENTRY \c_name
SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME # save callee saves in case allocation triggers GC
- lwu $a2, FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE($sp) # pass caller Method*
- move $a3, rSELF # pass Thread::Current
- jal \cxx_name # (method_idx, this, caller, Thread*, $sp)
- move $a4, $sp # pass $sp
+ move $a2, rSELF # pass Thread::Current
+ jal \cxx_name # (method_idx, this, Thread*, $sp)
+ move $a3, $sp # pass $sp
move $a0, $v0 # save target Method*
move $t9, $v1 # save $v0->code_
RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 6ebeba3..d62c1bc 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -278,8 +278,7 @@
/*
* All generated callsites for interface invokes and invocation slow paths will load arguments
* as usual - except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
- * the method_idx. This wrapper will save arg1-arg3, load the caller's Method*, align the
- * stack and call the appropriate C helper.
+ * the method_idx. This wrapper will save arg1-arg3 and call the appropriate C helper.
* NOTE: "this" is first visible argument of the target, and so can be found in arg1/r1.
*
* The helper will attempt to locate the target and return a 64-bit result in r0/r1 consisting
@@ -297,19 +296,15 @@
movl %esp, %edx // remember SP
// Outgoing argument set up
- subl MACRO_LITERAL(12), %esp // alignment padding
- CFI_ADJUST_CFA_OFFSET(12)
PUSH edx // pass SP
pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
CFI_ADJUST_CFA_OFFSET(4)
- pushl 32+32(%edx) // pass caller Method*
- CFI_ADJUST_CFA_OFFSET(4)
PUSH ecx // pass arg2
PUSH eax // pass arg1
call VAR(cxx_name, 1) // cxx_name(arg1, arg2, arg3, Thread*, SP)
movl %edx, %edi // save code pointer in EDI
- addl MACRO_LITERAL(36), %esp // Pop arguments skip eax
- CFI_ADJUST_CFA_OFFSET(-36)
+ addl MACRO_LITERAL(20), %esp // Pop arguments skip eax
+ CFI_ADJUST_CFA_OFFSET(-20)
// Restore FPRs.
movsd 0(%esp), %xmm0
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index da4d92b..ddeb5b8 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -341,8 +341,7 @@
/*
* All generated callsites for interface invokes and invocation slow paths will load arguments
* as usual - except instead of loading arg0/rdi with the target Method*, arg0/rdi will contain
- * the method_idx. This wrapper will save arg1-arg3, load the caller's Method*, align the
- * stack and call the appropriate C helper.
+ * the method_idx. This wrapper will save arg1-arg3, and call the appropriate C helper.
* NOTE: "this" is first visible argument of the target, and so can be found in arg1/rsi.
*
* The helper will attempt to locate the target and return a 128-bit result in rax/rdx consisting
@@ -362,11 +361,10 @@
// Helper signature is always
// (method_idx, *this_object, *caller_method, *self, sp)
- movl FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE(%rsp), %edx // pass caller Method*
- movq %gs:THREAD_SELF_OFFSET, %rcx // pass Thread
- movq %rsp, %r8 // pass SP
+ movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread
+ movq %rsp, %rcx // pass SP
- call VAR(cxx_name, 1) // cxx_name(arg1, arg2, caller method*, Thread*, SP)
+ call VAR(cxx_name, 1) // cxx_name(arg1, arg2, Thread*, SP)
// save the code pointer
movq %rax, %rdi
movq %rdx, %rax
diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc
index 0ae7863..859de4b 100644
--- a/runtime/base/logging.cc
+++ b/runtime/base/logging.cc
@@ -289,17 +289,17 @@
CHECK_EQ(strlen(log_characters), INTERNAL_FATAL + 1U);
const char* program_name = ProgramInvocationShortName();
- write(STDERR_FILENO, program_name, strlen(program_name));
- write(STDERR_FILENO, " ", 1);
- write(STDERR_FILENO, &log_characters[log_severity], 1);
- write(STDERR_FILENO, " ", 1);
+ TEMP_FAILURE_RETRY(write(STDERR_FILENO, program_name, strlen(program_name)));
+ TEMP_FAILURE_RETRY(write(STDERR_FILENO, " ", 1));
+ TEMP_FAILURE_RETRY(write(STDERR_FILENO, &log_characters[log_severity], 1));
+ TEMP_FAILURE_RETRY(write(STDERR_FILENO, " ", 1));
// TODO: pid and tid.
- write(STDERR_FILENO, file, strlen(file));
+ TEMP_FAILURE_RETRY(write(STDERR_FILENO, file, strlen(file)));
// TODO: line.
UNUSED(line);
- write(STDERR_FILENO, "] ", 2);
- write(STDERR_FILENO, message, strlen(message));
- write(STDERR_FILENO, "\n", 1);
+ TEMP_FAILURE_RETRY(write(STDERR_FILENO, "] ", 2));
+ TEMP_FAILURE_RETRY(write(STDERR_FILENO, message, strlen(message)));
+ TEMP_FAILURE_RETRY(write(STDERR_FILENO, "\n", 1));
#endif
}
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index 8b34374..35b50d1 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -39,6 +39,7 @@
struct LogVerbosity {
bool class_linker; // Enabled with "-verbose:class".
bool compiler;
+ bool deopt;
bool gc;
bool heap;
bool jdwp;
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 0c5210d..b1d933d 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -1402,25 +1402,23 @@
template <typename ElfTypes>
bool ElfFileImpl<ElfTypes>::FixupDebugSections(typename std::make_signed<Elf_Off>::type base_address_delta) {
- const Elf_Shdr* debug_info = FindSectionByName(".debug_info");
- const Elf_Shdr* debug_abbrev = FindSectionByName(".debug_abbrev");
- const Elf_Shdr* debug_str = FindSectionByName(".debug_str");
- const Elf_Shdr* strtab_sec = FindSectionByName(".strtab");
- const Elf_Shdr* symtab_sec = FindSectionByName(".symtab");
-
- if (debug_info == nullptr || debug_abbrev == nullptr ||
- debug_str == nullptr || strtab_sec == nullptr || symtab_sec == nullptr) {
- // Release version of ART does not generate debug info.
- return true;
- }
if (base_address_delta == 0) {
return true;
}
- if (!ApplyOatPatchesTo(".debug_info", base_address_delta)) {
- return false;
+ if (FindSectionByName(".debug_frame") != nullptr) {
+ if (!ApplyOatPatchesTo(".debug_frame", base_address_delta)) {
+ return false;
+ }
}
- if (!ApplyOatPatchesTo(".debug_line", base_address_delta)) {
- return false;
+ if (FindSectionByName(".debug_info") != nullptr) {
+ if (!ApplyOatPatchesTo(".debug_info", base_address_delta)) {
+ return false;
+ }
+ }
+ if (FindSectionByName(".debug_line") != nullptr) {
+ if (!ApplyOatPatchesTo(".debug_line", base_address_delta)) {
+ return false;
+ }
}
return true;
}
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 9292cff..625e695 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -38,25 +38,34 @@
namespace art {
-inline mirror::ArtMethod* GetCalleeSaveMethodCaller(Thread* self, Runtime::CalleeSaveType type)
+inline mirror::ArtMethod* GetCalleeSaveMethodCaller(StackReference<mirror::ArtMethod>* sp,
+ Runtime::CalleeSaveType type,
+ bool do_caller_check = false)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- auto* refs_only_sp = self->GetManagedStack()->GetTopQuickFrame();
- DCHECK_EQ(refs_only_sp->AsMirrorPtr(), Runtime::Current()->GetCalleeSaveMethod(type));
+ DCHECK_EQ(sp->AsMirrorPtr(), Runtime::Current()->GetCalleeSaveMethod(type));
const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA, type);
auto* caller_sp = reinterpret_cast<StackReference<mirror::ArtMethod>*>(
- reinterpret_cast<uintptr_t>(refs_only_sp) + callee_frame_size);
+ reinterpret_cast<uintptr_t>(sp) + callee_frame_size);
auto* caller = caller_sp->AsMirrorPtr();
- if (kIsDebugBuild) {
- NthCallerVisitor visitor(self, 1, true);
+ if (kIsDebugBuild && do_caller_check) {
+ // Note that do_caller_check is optional, as this method can be called by
+ // stubs, and tests without a proper call stack.
+ NthCallerVisitor visitor(Thread::Current(), 1, true);
visitor.WalkStack();
- CHECK(caller == visitor.caller);
+ CHECK_EQ(caller, visitor.caller);
}
return caller;
}
+inline mirror::ArtMethod* GetCalleeSaveMethodCaller(Thread* self, Runtime::CalleeSaveType type)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return GetCalleeSaveMethodCaller(
+ self->GetManagedStack()->GetTopQuickFrame(), type, true /* do_caller_check */);
+}
+
template <const bool kAccessCheck>
ALWAYS_INLINE
inline mirror::Class* CheckObjectAlloc(uint32_t type_idx,
diff --git a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
index 6a8aaf2..1b1ef66 100644
--- a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "base/logging.h"
#include "callee_save_frame.h"
#include "dex_file-inl.h"
#include "interpreter/interpreter.h"
@@ -29,6 +30,12 @@
extern "C" NO_RETURN void artDeoptimize(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
+
+ if (VLOG_IS_ON(deopt)) {
+ LOG(INFO) << "Deopting:";
+ self->Dump(LOG(INFO));
+ }
+
self->SetException(Thread::GetDeoptimizationException());
self->QuickDeliverException();
}
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index 46629f5..9148878 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -25,8 +25,7 @@
namespace art {
-extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx,
- Thread* self)
+extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx, Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Called to ensure static storage base is initialized for direct static field reads and writes.
// A class may be accessing another class' fields when it doesn't have access, as access has been
@@ -36,8 +35,7 @@
return ResolveVerifyAndClinit(type_idx, caller, self, true, false);
}
-extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx,
- Thread* self)
+extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Called when method->dex_cache_resolved_types_[] misses.
ScopedQuickEntrypointChecks sqec(self);
@@ -45,8 +43,7 @@
return ResolveVerifyAndClinit(type_idx, caller, self, false, false);
}
-extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx,
- Thread* self)
+extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx, Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Called when caller isn't guaranteed to have access to a type and the dex cache may be
// unpopulated.
@@ -55,8 +52,7 @@
return ResolveVerifyAndClinit(type_idx, caller, self, false, true);
}
-extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx,
- Thread* self)
+extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
auto* caller = GetCalleeSaveMethodCaller(self, Runtime::kRefsOnly);
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 2e7e2df..345b0ad 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -294,8 +294,13 @@
static mirror::ArtMethod* GetCallingMethod(StackReference<mirror::ArtMethod>* sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(sp->AsMirrorPtr()->IsCalleeSaveMethod());
- uint8_t* previous_sp = reinterpret_cast<uint8_t*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_FrameSize;
- return reinterpret_cast<StackReference<mirror::ArtMethod>*>(previous_sp)->AsMirrorPtr();
+ return GetCalleeSaveMethodCaller(sp, Runtime::kRefsAndArgs);
+ }
+
+ static uint32_t GetCallingDexPc(StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK(sp->AsMirrorPtr()->IsCalleeSaveMethod());
+ return GetCallingMethod(sp)->ToDexPc(QuickArgumentVisitor::GetCallingPc(sp));
}
// For the given quick ref and args quick frame, return the caller's PC.
@@ -827,12 +832,13 @@
// Compute details about the called method (avoid GCs)
ClassLinker* linker = Runtime::Current()->GetClassLinker();
- mirror::ArtMethod* caller = QuickArgumentVisitor::GetCallingMethod(sp);
InvokeType invoke_type;
MethodReference called_method(nullptr, 0);
const bool called_method_known_on_entry = !called->IsRuntimeMethod();
+ mirror::ArtMethod* caller = nullptr;
if (!called_method_known_on_entry) {
- uint32_t dex_pc = caller->ToDexPc(QuickArgumentVisitor::GetCallingPc(sp));
+ caller = QuickArgumentVisitor::GetCallingMethod(sp);
+ uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
const DexFile::CodeItem* code;
called_method.dex_file = caller->GetDexFile();
code = caller->GetCodeItem();
@@ -1946,16 +1952,13 @@
// to hold the mutator lock (see SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) annotations).
template<InvokeType type, bool access_check>
-static TwoWordReturn artInvokeCommon(uint32_t method_idx, mirror::Object* this_object,
- mirror::ArtMethod* caller_method,
- Thread* self, StackReference<mirror::ArtMethod>* sp);
-
-template<InvokeType type, bool access_check>
-static TwoWordReturn artInvokeCommon(uint32_t method_idx, mirror::Object* this_object,
- mirror::ArtMethod* caller_method,
- Thread* self, StackReference<mirror::ArtMethod>* sp) {
+static TwoWordReturn artInvokeCommon(uint32_t method_idx,
+ mirror::Object* this_object,
+ Thread* self,
+ StackReference<mirror::ArtMethod>* sp) {
ScopedQuickEntrypointChecks sqec(self);
DCHECK_EQ(sp->AsMirrorPtr(), Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs));
+ mirror::ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp);
mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check,
type);
if (UNLIKELY(method == nullptr)) {
@@ -1994,7 +1997,6 @@
template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \
TwoWordReturn artInvokeCommon<type, access_check>(uint32_t method_idx, \
mirror::Object* this_object, \
- mirror::ArtMethod* caller_method, \
Thread* self, \
StackReference<mirror::ArtMethod>* sp) \
@@ -2012,58 +2014,58 @@
// See comments in runtime_support_asm.S
extern "C" TwoWordReturn artInvokeInterfaceTrampolineWithAccessCheck(
- uint32_t method_idx, mirror::Object* this_object,
- mirror::ArtMethod* caller_method, Thread* self,
+ uint32_t method_idx,
+ mirror::Object* this_object,
+ Thread* self,
StackReference<mirror::ArtMethod>* sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon<kInterface, true>(method_idx, this_object,
- caller_method, self, sp);
+ return artInvokeCommon<kInterface, true>(method_idx, this_object, self, sp);
}
extern "C" TwoWordReturn artInvokeDirectTrampolineWithAccessCheck(
- uint32_t method_idx, mirror::Object* this_object,
- mirror::ArtMethod* caller_method, Thread* self,
+ uint32_t method_idx,
+ mirror::Object* this_object,
+ Thread* self,
StackReference<mirror::ArtMethod>* sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon<kDirect, true>(method_idx, this_object, caller_method,
- self, sp);
+ return artInvokeCommon<kDirect, true>(method_idx, this_object, self, sp);
}
extern "C" TwoWordReturn artInvokeStaticTrampolineWithAccessCheck(
- uint32_t method_idx, mirror::Object* this_object,
- mirror::ArtMethod* caller_method, Thread* self,
+ uint32_t method_idx,
+ mirror::Object* this_object,
+ Thread* self,
StackReference<mirror::ArtMethod>* sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon<kStatic, true>(method_idx, this_object, caller_method,
- self, sp);
+ return artInvokeCommon<kStatic, true>(method_idx, this_object, self, sp);
}
extern "C" TwoWordReturn artInvokeSuperTrampolineWithAccessCheck(
- uint32_t method_idx, mirror::Object* this_object,
- mirror::ArtMethod* caller_method, Thread* self,
+ uint32_t method_idx,
+ mirror::Object* this_object,
+ Thread* self,
StackReference<mirror::ArtMethod>* sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon<kSuper, true>(method_idx, this_object, caller_method,
- self, sp);
+ return artInvokeCommon<kSuper, true>(method_idx, this_object, self, sp);
}
extern "C" TwoWordReturn artInvokeVirtualTrampolineWithAccessCheck(
- uint32_t method_idx, mirror::Object* this_object,
- mirror::ArtMethod* caller_method, Thread* self,
+ uint32_t method_idx,
+ mirror::Object* this_object,
+ Thread* self,
StackReference<mirror::ArtMethod>* sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon<kVirtual, true>(method_idx, this_object, caller_method,
- self, sp);
+ return artInvokeCommon<kVirtual, true>(method_idx, this_object, self, sp);
}
// Determine target of interface dispatch. This object is known non-null.
extern "C" TwoWordReturn artInvokeInterfaceTrampoline(mirror::ArtMethod* interface_method,
mirror::Object* this_object,
- mirror::ArtMethod* caller_method,
Thread* self,
StackReference<mirror::ArtMethod>* sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
+ mirror::ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp);
mirror::ArtMethod* method;
if (LIKELY(interface_method->GetDexMethodIndex() != DexFile::kDexNoIndex)) {
method = this_object->GetClass()->FindVirtualMethodForInterface(interface_method);
@@ -2075,12 +2077,7 @@
} else {
DCHECK(interface_method == Runtime::Current()->GetResolutionMethod());
- // Find the caller PC.
- constexpr size_t pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, Runtime::kRefsAndArgs);
- uintptr_t caller_pc = *reinterpret_cast<uintptr_t*>(reinterpret_cast<uint8_t*>(sp) + pc_offset);
-
- // Map the caller PC to a dex PC.
- uint32_t dex_pc = caller_method->ToDexPc(caller_pc);
+ uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
const DexFile::CodeItem* code = caller_method->GetCodeItem();
CHECK_LT(dex_pc, code->insns_size_in_code_units_);
const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
diff --git a/runtime/profiler.cc b/runtime/profiler.cc
index 5354fd8..3b0e6c1 100644
--- a/runtime/profiler.cc
+++ b/runtime/profiler.cc
@@ -302,7 +302,9 @@
} while (length > 0);
// Truncate the file to the new length.
- ftruncate(fd, full_length);
+ if (ftruncate(fd, full_length) == -1) {
+ LOG(ERROR) << "Failed to truncate profile file " << full_name;
+ }
// Now unlock the file, allowing another process in.
err = flock(fd, LOCK_UN);
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 8b504c1..922334e 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -62,7 +62,7 @@
RUNTIME_OPTIONS_KEY (Unit, DumpJITInfoOnShutdown)
RUNTIME_OPTIONS_KEY (Unit, IgnoreMaxFootprint)
RUNTIME_OPTIONS_KEY (Unit, LowMemoryMode)
-RUNTIME_OPTIONS_KEY (bool, UseTLAB, kUseTlab)
+RUNTIME_OPTIONS_KEY (bool, UseTLAB, (kUseTlab || kUseReadBarrier))
RUNTIME_OPTIONS_KEY (bool, EnableHSpaceCompactForOOM, true)
RUNTIME_OPTIONS_KEY (bool, UseJIT, false)
RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold, jit::Jit::kDefaultCompileThreshold)
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 800acaa..6795516 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -99,15 +99,45 @@
cur_quick_frame_pc_(0),
num_frames_(num_frames),
cur_depth_(0),
+ current_inlining_depth_(0),
context_(context) {
DCHECK(thread == Thread::Current() || thread->IsSuspended()) << *thread;
}
+InlineInfo StackVisitor::GetCurrentInlineInfo() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtMethod* outer_method = GetCurrentQuickFrame()->AsMirrorPtr();
+ uint32_t native_pc_offset = outer_method->NativeQuickPcOffset(cur_quick_frame_pc_);
+ CodeInfo code_info = outer_method->GetOptimizedCodeInfo();
+ StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
+ return code_info.GetInlineInfoOf(stack_map);
+}
+
+mirror::ArtMethod* StackVisitor::GetMethod() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (cur_shadow_frame_ != nullptr) {
+ return cur_shadow_frame_->GetMethod();
+ } else if (cur_quick_frame_ != nullptr) {
+ if (IsInInlinedFrame()) {
+ size_t depth_in_stack_map = current_inlining_depth_ - 1;
+ return GetCurrentQuickFrame()->AsMirrorPtr()->GetDexCacheResolvedMethod(
+ GetCurrentInlineInfo().GetMethodIndexAtDepth(depth_in_stack_map));
+ } else {
+ return cur_quick_frame_->AsMirrorPtr();
+ }
+ } else {
+ return nullptr;
+ }
+}
+
uint32_t StackVisitor::GetDexPc(bool abort_on_failure) const {
if (cur_shadow_frame_ != nullptr) {
return cur_shadow_frame_->GetDexPC();
} else if (cur_quick_frame_ != nullptr) {
- return GetMethod()->ToDexPc(cur_quick_frame_pc_, abort_on_failure);
+ if (IsInInlinedFrame()) {
+ size_t depth_in_stack_map = current_inlining_depth_ - 1;
+ return GetCurrentInlineInfo().GetDexPcAtDepth(depth_in_stack_map);
+ } else {
+ return GetMethod()->ToDexPc(cur_quick_frame_pc_, abort_on_failure);
+ }
} else {
return 0;
}
@@ -225,18 +255,27 @@
bool StackVisitor::GetVRegFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind,
uint32_t* val) const {
- const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*));
- DCHECK(code_pointer != nullptr);
- uint32_t native_pc_offset = m->NativeQuickPcOffset(cur_quick_frame_pc_);
- CodeInfo code_info = m->GetOptimizedCodeInfo();
- StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
+ DCHECK_EQ(m, GetMethod());
const DexFile::CodeItem* code_item = m->GetCodeItem();
DCHECK(code_item != nullptr) << PrettyMethod(m); // Can't be null or how would we compile
// its instructions?
- DCHECK_LT(vreg, code_item->registers_size_);
uint16_t number_of_dex_registers = code_item->registers_size_;
- DexRegisterMap dex_register_map =
- code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
+ DCHECK_LT(vreg, code_item->registers_size_);
+
+ mirror::ArtMethod* outer_method = GetCurrentQuickFrame()->AsMirrorPtr();
+ const void* code_pointer = outer_method->GetQuickOatCodePointer(sizeof(void*));
+ DCHECK(code_pointer != nullptr);
+ CodeInfo code_info = outer_method->GetOptimizedCodeInfo();
+
+ uint32_t native_pc_offset = outer_method->NativeQuickPcOffset(cur_quick_frame_pc_);
+ StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
+ size_t depth_in_stack_map = current_inlining_depth_ - 1;
+
+ DexRegisterMap dex_register_map = IsInInlinedFrame()
+ ? code_info.GetDexRegisterMapAtDepth(
+ depth_in_stack_map, code_info.GetInlineInfoOf(stack_map), number_of_dex_registers)
+ : code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
+
DexRegisterLocation::Kind location_kind =
dex_register_map.GetLocationKind(vreg, number_of_dex_registers, code_info);
switch (location_kind) {
@@ -704,6 +743,26 @@
mirror::ArtMethod* method = cur_quick_frame_->AsMirrorPtr();
while (method != nullptr) {
SanityCheckFrame();
+
+ if ((walk_kind_ == StackWalkKind::kIncludeInlinedFrames)
+ && method->IsOptimized(sizeof(void*))) {
+ CodeInfo code_info = method->GetOptimizedCodeInfo();
+ uint32_t native_pc_offset = method->NativeQuickPcOffset(cur_quick_frame_pc_);
+ StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
+ if (stack_map.HasInlineInfo(code_info)) {
+ InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map);
+ DCHECK_EQ(current_inlining_depth_, 0u);
+ for (current_inlining_depth_ = inline_info.GetDepth();
+ current_inlining_depth_ != 0;
+ --current_inlining_depth_) {
+ bool should_continue = VisitFrame();
+ if (UNLIKELY(!should_continue)) {
+ return;
+ }
+ }
+ }
+ }
+
bool should_continue = VisitFrame();
if (UNLIKELY(!should_continue)) {
return;
diff --git a/runtime/stack.h b/runtime/stack.h
index bf61016..5b43848 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -36,9 +36,10 @@
} // namespace mirror
class Context;
-class ShadowFrame;
class HandleScope;
+class InlineInfo;
class ScopedObjectAccess;
+class ShadowFrame;
class StackVisitor;
class Thread;
@@ -430,15 +431,7 @@
void WalkStack(bool include_transitions = false)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- mirror::ArtMethod* GetMethod() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (cur_shadow_frame_ != nullptr) {
- return cur_shadow_frame_->GetMethod();
- } else if (cur_quick_frame_ != nullptr) {
- return cur_quick_frame_->AsMirrorPtr();
- } else {
- return nullptr;
- }
- }
+ mirror::ArtMethod* GetMethod() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsShadowFrame() const {
return cur_shadow_frame_ != nullptr;
@@ -611,7 +604,7 @@
}
bool IsInInlinedFrame() const {
- return false;
+ return current_inlining_depth_ != 0;
}
uintptr_t GetCurrentQuickFramePc() const {
@@ -703,6 +696,8 @@
void SanityCheckFrame() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ InlineInfo GetCurrentInlineInfo() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
Thread* const thread_;
const StackWalkKind walk_kind_;
ShadowFrame* cur_shadow_frame_;
@@ -712,6 +707,9 @@
size_t num_frames_;
// Depth of the frame we're currently at.
size_t cur_depth_;
+ // Current inlining depth of the method we are currently at.
+ // 0 if there is no inlined frame.
+ size_t current_inlining_depth_;
protected:
Context* const context_;
diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc
index 11e7e44..6a0c07d 100644
--- a/runtime/stack_map.cc
+++ b/runtime/stack_map.cc
@@ -257,21 +257,48 @@
DumpStackMapHeader(os, i);
if (stack_map.HasDexRegisterMap(*this)) {
DexRegisterMap dex_register_map = GetDexRegisterMapOf(stack_map, number_of_dex_registers);
- // TODO: Display the bit mask of live Dex registers.
- for (size_t j = 0; j < number_of_dex_registers; ++j) {
- if (dex_register_map.IsDexRegisterLive(j)) {
- size_t location_catalog_entry_index = dex_register_map.GetLocationCatalogEntryIndex(
- j, number_of_dex_registers, number_of_location_catalog_entries);
- DexRegisterLocation location =
- dex_register_map.GetDexRegisterLocation(j, number_of_dex_registers, *this);
- DumpRegisterMapping(
- os, j, location, "v",
- "\t[entry " + std::to_string(static_cast<int>(location_catalog_entry_index)) + "]");
- }
- }
+ dex_register_map.Dump(os, *this, number_of_dex_registers);
}
}
- // TODO: Dump the stack map's inline information.
+ // TODO: Dump the stack map's inline information? We need to know more from the caller:
+ // we need to know the number of dex registers for each inlined method.
+}
+
+void DexRegisterMap::Dump(std::ostream& os,
+ const CodeInfo& code_info,
+ uint16_t number_of_dex_registers) const {
+ size_t number_of_location_catalog_entries =
+ code_info.GetNumberOfDexRegisterLocationCatalogEntries();
+ // TODO: Display the bit mask of live Dex registers.
+ for (size_t j = 0; j < number_of_dex_registers; ++j) {
+ if (IsDexRegisterLive(j)) {
+ size_t location_catalog_entry_index = GetLocationCatalogEntryIndex(
+ j, number_of_dex_registers, number_of_location_catalog_entries);
+ DexRegisterLocation location = GetDexRegisterLocation(j, number_of_dex_registers, code_info);
+ DumpRegisterMapping(
+ os, j, location, "v",
+ "\t[entry " + std::to_string(static_cast<int>(location_catalog_entry_index)) + "]");
+ }
+ }
+}
+
+void InlineInfo::Dump(std::ostream& os,
+ const CodeInfo& code_info,
+ uint16_t number_of_dex_registers[]) const {
+ os << "InlineInfo with depth " << static_cast<uint32_t>(GetDepth()) << "\n";
+
+ for (size_t i = 0; i < GetDepth(); ++i) {
+ os << " At depth " << i
+ << std::hex
+ << " (dex_pc=0x" << GetDexPcAtDepth(i)
+ << ", method_index=0x" << GetMethodIndexAtDepth(i)
+ << ")\n";
+ if (HasDexRegisterMapAtDepth(i)) {
+ DexRegisterMap dex_register_map =
+ code_info.GetDexRegisterMapAtDepth(i, *this, number_of_dex_registers[i]);
+ dex_register_map.Dump(os, code_info, number_of_dex_registers[i]);
+ }
+ }
}
} // namespace art
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index f68cafe..f07fb74 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -39,47 +39,6 @@
* their own fields.
*/
-/**
- * Inline information for a specific PC. The information is of the form:
- * [inlining_depth, [method_dex reference]+]
- */
-class InlineInfo {
- public:
- explicit InlineInfo(MemoryRegion region) : region_(region) {}
-
- uint8_t GetDepth() const {
- return region_.LoadUnaligned<uint8_t>(kDepthOffset);
- }
-
- void SetDepth(uint8_t depth) {
- region_.StoreUnaligned<uint8_t>(kDepthOffset, depth);
- }
-
- uint32_t GetMethodReferenceIndexAtDepth(uint8_t depth) const {
- return region_.LoadUnaligned<uint32_t>(kFixedSize + depth * SingleEntrySize());
- }
-
- void SetMethodReferenceIndexAtDepth(uint8_t depth, uint32_t index) {
- region_.StoreUnaligned<uint32_t>(kFixedSize + depth * SingleEntrySize(), index);
- }
-
- static size_t SingleEntrySize() {
- return sizeof(uint32_t);
- }
-
- private:
- // TODO: Instead of plain types such as "uint8_t", introduce
- // typedefs (and document the memory layout of InlineInfo).
- static constexpr int kDepthOffset = 0;
- static constexpr int kFixedSize = kDepthOffset + sizeof(uint8_t);
-
- MemoryRegion region_;
-
- friend class CodeInfo;
- friend class StackMap;
- friend class StackMapStream;
-};
-
// Dex register location container used by DexRegisterMap and StackMapStream.
class DexRegisterLocation {
public:
@@ -506,7 +465,8 @@
const CodeInfo& code_info) const {
DexRegisterLocation location =
GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info);
- DCHECK(location.GetKind() == DexRegisterLocation::Kind::kConstant);
+ DCHECK(location.GetKind() == DexRegisterLocation::Kind::kConstant)
+ << DexRegisterLocation::PrettyDescriptor(location.GetKind());
return location.GetValue();
}
@@ -641,6 +601,8 @@
return region_.size();
}
+ void Dump(std::ostream& o, const CodeInfo& code_info, uint16_t number_of_dex_registers) const;
+
private:
// Return the index in the Dex register map corresponding to the Dex
// register number `dex_register_number`.
@@ -675,9 +637,6 @@
* The information is of the form:
* [dex_pc, native_pc_offset, dex_register_map_offset, inlining_info_offset, register_mask,
* stack_mask].
- *
- * Note that register_mask is fixed size, but stack_mask is variable size, depending on the
- * stack size of a method.
*/
class StackMap {
public:
@@ -759,6 +718,90 @@
friend class StackMapStream;
};
+/**
+ * Inline information for a specific PC. The information is of the form:
+ * [inlining_depth, [dex_pc, method_index, dex_register_map_offset]+]
+ */
+class InlineInfo {
+ public:
+ explicit InlineInfo(MemoryRegion region) : region_(region) {}
+
+ uint8_t GetDepth() const {
+ return region_.LoadUnaligned<uint8_t>(kDepthOffset);
+ }
+
+ void SetDepth(uint8_t depth) {
+ region_.StoreUnaligned<uint8_t>(kDepthOffset, depth);
+ }
+
+ uint32_t GetMethodIndexAtDepth(uint8_t depth) const {
+ return region_.LoadUnaligned<uint32_t>(
+ kFixedSize + depth * SingleEntrySize() + kMethodIndexOffset);
+ }
+
+ void SetMethodIndexAtDepth(uint8_t depth, uint32_t index) {
+ region_.StoreUnaligned<uint32_t>(
+ kFixedSize + depth * SingleEntrySize() + kMethodIndexOffset, index);
+ }
+
+ uint32_t GetDexPcAtDepth(uint8_t depth) const {
+ return region_.LoadUnaligned<uint32_t>(
+ kFixedSize + depth * SingleEntrySize() + kDexPcOffset);
+ }
+
+ void SetDexPcAtDepth(uint8_t depth, uint32_t dex_pc) {
+ region_.StoreUnaligned<uint32_t>(
+ kFixedSize + depth * SingleEntrySize() + kDexPcOffset, dex_pc);
+ }
+
+ uint8_t GetInvokeTypeAtDepth(uint8_t depth) const {
+ return region_.LoadUnaligned<uint8_t>(
+ kFixedSize + depth * SingleEntrySize() + kInvokeTypeOffset);
+ }
+
+ void SetInvokeTypeAtDepth(uint8_t depth, uint8_t invoke_type) {
+ region_.StoreUnaligned<uint8_t>(
+ kFixedSize + depth * SingleEntrySize() + kInvokeTypeOffset, invoke_type);
+ }
+
+ uint32_t GetDexRegisterMapOffsetAtDepth(uint8_t depth) const {
+ return region_.LoadUnaligned<uint32_t>(
+ kFixedSize + depth * SingleEntrySize() + kDexRegisterMapOffset);
+ }
+
+ void SetDexRegisterMapOffsetAtDepth(uint8_t depth, uint32_t offset) {
+ region_.StoreUnaligned<uint32_t>(
+ kFixedSize + depth * SingleEntrySize() + kDexRegisterMapOffset, offset);
+ }
+
+ bool HasDexRegisterMapAtDepth(uint8_t depth) const {
+ return GetDexRegisterMapOffsetAtDepth(depth) != StackMap::kNoDexRegisterMap;
+ }
+
+ static size_t SingleEntrySize() {
+ return kFixedEntrySize;
+ }
+
+ void Dump(std::ostream& os, const CodeInfo& info, uint16_t* number_of_dex_registers) const;
+
+ private:
+ // TODO: Instead of plain types such as "uint8_t", introduce
+ // typedefs (and document the memory layout of InlineInfo).
+ static constexpr int kDepthOffset = 0;
+ static constexpr int kFixedSize = kDepthOffset + sizeof(uint8_t);
+
+ static constexpr int kMethodIndexOffset = 0;
+ static constexpr int kDexPcOffset = kMethodIndexOffset + sizeof(uint32_t);
+ static constexpr int kInvokeTypeOffset = kDexPcOffset + sizeof(uint32_t);
+ static constexpr int kDexRegisterMapOffset = kInvokeTypeOffset + sizeof(uint8_t);
+ static constexpr int kFixedEntrySize = kDexRegisterMapOffset + sizeof(uint32_t);
+
+ MemoryRegion region_;
+
+ friend class CodeInfo;
+ friend class StackMap;
+ friend class StackMapStream;
+};
/**
* Wrapper around all compiler information collected for a method.
@@ -960,6 +1003,17 @@
return DexRegisterMap(region_.Subregion(offset, size));
}
+ // Return the `DexRegisterMap` pointed by `inline_info` at depth `depth`.
+ DexRegisterMap GetDexRegisterMapAtDepth(uint8_t depth,
+ InlineInfo inline_info,
+ uint32_t number_of_dex_registers) const {
+ DCHECK(inline_info.HasDexRegisterMapAtDepth(depth));
+ uint32_t offset =
+ GetDexRegisterMapsOffset() + inline_info.GetDexRegisterMapOffsetAtDepth(depth);
+ size_t size = ComputeDexRegisterMapSizeOf(offset, number_of_dex_registers);
+ return DexRegisterMap(region_.Subregion(offset, size));
+ }
+
InlineInfo GetInlineInfoOf(StackMap stack_map) const {
DCHECK(stack_map.HasInlineInfo(*this));
uint32_t offset = stack_map.GetInlineDescriptorOffset(*this) + GetDexRegisterMapsOffset();
diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc
index 24e9600..db2fc9b 100644
--- a/test/115-native-bridge/nativebridge.cc
+++ b/test/115-native-bridge/nativebridge.cc
@@ -327,5 +327,7 @@
.loadLibrary = &native_bridge_loadLibrary,
.getTrampoline = &native_bridge_getTrampoline,
.isSupported = &native_bridge_isSupported,
- .getAppEnv = &native_bridge_getAppEnv
+ .getAppEnv = &native_bridge_getAppEnv,
+ .isCompatibleWith = nullptr,
+ .getSignalHandler = nullptr
};
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
new file mode 100644
index 0000000..b2d7e55
--- /dev/null
+++ b/test/137-cfi/cfi.cc
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ */
+
+#if __linux__
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#endif
+
+#include "jni.h"
+
+#include <backtrace/Backtrace.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "utils.h"
+
+namespace art {
+
+// For testing debuggerd. We do not have expected-death tests, so can't test this by default.
+// Code for this is copied from SignalTest.
+static constexpr bool kCauseSegfault = false;
+char* go_away_compiler_cfi = nullptr;
+
+static void CauseSegfault() {
+#if defined(__arm__) || defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
+ // On supported architectures we cause a real SEGV.
+ *go_away_compiler_cfi = 'a';
+#else
+ // On other architectures we simulate SEGV.
+ kill(getpid(), SIGSEGV);
+#endif
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_sleep(JNIEnv*, jobject, jint, jboolean, jdouble) {
+ // Keep pausing.
+ for (;;) {
+ pause();
+ }
+}
+
+// Helper to look for a sequence in the stack trace.
+#if __linux__
+static bool CheckStack(Backtrace* bt, const std::vector<std::string>& seq) {
+ size_t cur_search_index = 0; // The currently active index in seq.
+ CHECK_GT(seq.size(), 0U);
+
+ for (Backtrace::const_iterator it = bt->begin(); it != bt->end(); ++it) {
+ if (BacktraceMap::IsValid(it->map)) {
+ LOG(INFO) << "Got " << it->func_name << ", looking for " << seq[cur_search_index];
+ if (it->func_name == seq[cur_search_index]) {
+ cur_search_index++;
+ if (cur_search_index == seq.size()) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+#endif
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess(JNIEnv*, jobject, jint, jboolean) {
+#if __linux__
+ // TODO: What to do on Valgrind?
+
+ std::unique_ptr<Backtrace> bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid()));
+ if (!bt->Unwind(0, nullptr)) {
+ return JNI_FALSE;
+ } else if (bt->NumFrames() == 0) {
+ return JNI_FALSE;
+ }
+
+ // We cannot really parse an exact stack, as the optimizing compiler may inline some functions.
+ // This is also risky, as deduping might play a trick on us, so the test needs to make sure that
+ // only unique functions are being expected.
+ std::vector<std::string> seq = {
+ "Java_Main_unwindInProcess", // This function.
+ "boolean Main.unwindInProcess(int, boolean)", // The corresponding Java native method frame.
+ "void Main.main(java.lang.String[])" // The Java entry method.
+ };
+
+ bool result = CheckStack(bt.get(), seq);
+ if (!kCauseSegfault) {
+ return result ? JNI_TRUE : JNI_FALSE;
+ } else {
+ LOG(INFO) << "Result of check-stack: " << result;
+ }
+#endif
+
+ if (kCauseSegfault) {
+ CauseSegfault();
+ }
+
+ return JNI_FALSE;
+}
+
+#if __linux__
+static constexpr int kSleepTimeMicroseconds = 50000; // 0.05 seconds
+static constexpr int kMaxTotalSleepTimeMicroseconds = 1000000; // 1 second
+
+// Wait for a sigstop. This code is copied from libbacktrace.
+int wait_for_sigstop(pid_t tid, int* total_sleep_time_usec, bool* detach_failed ATTRIBUTE_UNUSED) {
+ for (;;) {
+ int status;
+ pid_t n = TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL | WNOHANG));
+ if (n == -1) {
+ PLOG(WARNING) << "waitpid failed: tid " << tid;
+ break;
+ } else if (n == tid) {
+ if (WIFSTOPPED(status)) {
+ return WSTOPSIG(status);
+ } else {
+ PLOG(ERROR) << "unexpected waitpid response: n=" << n << ", status=" << std::hex << status;
+ break;
+ }
+ }
+
+ if (*total_sleep_time_usec > kMaxTotalSleepTimeMicroseconds) {
+ PLOG(WARNING) << "timed out waiting for stop signal: tid=" << tid;
+ break;
+ }
+
+ usleep(kSleepTimeMicroseconds);
+ *total_sleep_time_usec += kSleepTimeMicroseconds;
+ }
+
+ return -1;
+}
+#endif
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(JNIEnv*, jobject, jint pid_int) {
+#if __linux__
+ // TODO: What to do on Valgrind?
+ pid_t pid = static_cast<pid_t>(pid_int);
+
+ // OK, this is painful. debuggerd uses ptrace to unwind other processes.
+
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0)) {
+ // Were not able to attach, bad.
+ PLOG(ERROR) << "Failed to attach.";
+ kill(pid, SIGCONT);
+ return JNI_FALSE;
+ }
+
+ kill(pid, SIGSTOP);
+
+ bool detach_failed = false;
+ int total_sleep_time_usec = 0;
+ int signal = wait_for_sigstop(pid, &total_sleep_time_usec, &detach_failed);
+ if (signal == -1) {
+ LOG(WARNING) << "wait_for_sigstop failed.";
+ }
+
+ std::unique_ptr<Backtrace> bt(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
+ bool result = true;
+ if (!bt->Unwind(0, nullptr)) {
+ result = false;
+ } else if (bt->NumFrames() == 0) {
+ result = false;
+ }
+
+ if (result) {
+ // See comment in unwindInProcess for non-exact stack matching.
+ std::vector<std::string> seq = {
+ // "Java_Main_sleep", // The sleep function being executed in the
+ // other runtime.
+ // Note: For some reason, the name isn't
+ // resolved, so don't look for it right now.
+ "boolean Main.sleep(int, boolean, double)", // The corresponding Java native method frame.
+ "void Main.main(java.lang.String[])" // The Java entry method.
+ };
+
+ result = CheckStack(bt.get(), seq);
+ }
+
+ if (ptrace(PTRACE_DETACH, pid, 0, 0) != 0) {
+ PLOG(ERROR) << "Detach failed";
+ }
+
+ // Continue the process so we can kill it on the Java side.
+ kill(pid, SIGCONT);
+
+ return result ? JNI_TRUE : JNI_FALSE;
+#else
+ return JNI_FALSE;
+#endif
+}
+
+} // namespace art
diff --git a/test/137-cfi/expected.txt b/test/137-cfi/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/137-cfi/expected.txt
diff --git a/test/137-cfi/info.txt b/test/137-cfi/info.txt
new file mode 100644
index 0000000..7d59605
--- /dev/null
+++ b/test/137-cfi/info.txt
@@ -0,0 +1 @@
+Test that unwinding with CFI info works.
diff --git a/test/137-cfi/src/Main.java b/test/137-cfi/src/Main.java
new file mode 100644
index 0000000..e184e66
--- /dev/null
+++ b/test/137-cfi/src/Main.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class Main {
+ // Whether to test local unwinding. Libunwind uses linker info to find executables. As we do
+ // not dlopen at the moment, this doesn't work, so keep it off for now.
+ public final static boolean TEST_LOCAL_UNWINDING = false;
+
+ // Unwinding another process, modelling debuggerd. This doesn't use the linker, so should work
+ // no matter whether we're using dlopen or not.
+ public final static boolean TEST_REMOTE_UNWINDING = true;
+
+ private boolean secondary;
+
+ public Main(boolean secondary) {
+ this.secondary = secondary;
+ }
+
+ public static void main(String[] args) throws Exception {
+ boolean secondary = false;
+ if (args.length > 0 && args[args.length - 1].equals("--secondary")) {
+ secondary = true;
+ }
+ new Main(secondary).run();
+ }
+
+ static {
+ System.loadLibrary("arttest");
+ }
+
+ private void run() {
+ if (secondary) {
+ if (!TEST_REMOTE_UNWINDING) {
+ throw new RuntimeException("Should not be running secondary!");
+ }
+ runSecondary();
+ } else {
+ runPrimary();
+ }
+ }
+
+ private void runSecondary() {
+ foo(true);
+ throw new RuntimeException("Didn't expect to get back...");
+ }
+
+ private void runPrimary() {
+ // First do the in-process unwinding.
+ if (TEST_LOCAL_UNWINDING && !foo(false)) {
+ System.out.println("Unwinding self failed.");
+ }
+
+ if (!TEST_REMOTE_UNWINDING) {
+ // Skip the remote step.
+ return;
+ }
+
+ // Fork the secondary.
+ String[] cmdline = getCmdLine();
+ String[] secCmdLine = new String[cmdline.length + 1];
+ System.arraycopy(cmdline, 0, secCmdLine, 0, cmdline.length);
+ secCmdLine[secCmdLine.length - 1] = "--secondary";
+ Process p = exec(secCmdLine);
+
+ try {
+ int pid = getPid(p);
+ if (pid <= 0) {
+ throw new RuntimeException("Couldn't parse process");
+ }
+
+ // Wait a bit, so the forked process has time to run until its sleep phase.
+ try {
+ Thread.sleep(5000);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ if (!unwindOtherProcess(pid)) {
+ System.out.println("Unwinding other process failed.");
+ }
+ } finally {
+ // Kill the forked process.
+ p.destroy();
+ }
+ }
+
+ private static Process exec(String[] args) {
+ try {
+ return Runtime.getRuntime().exec(args);
+ } catch (Exception exc) {
+ throw new RuntimeException(exc);
+ }
+ }
+
+ private static int getPid(Process p) {
+ // Could do reflection for the private pid field, but String parsing is easier.
+ String s = p.toString();
+ if (s.startsWith("Process[pid=")) {
+ return Integer.parseInt(s.substring("Process[pid=".length(), s.length() - 1));
+ } else {
+ return -1;
+ }
+ }
+
+ // Read /proc/self/cmdline to find the invocation command line (so we can fork another runtime).
+ private static String[] getCmdLine() {
+ try {
+ BufferedReader in = new BufferedReader(new FileReader("/proc/self/cmdline"));
+ String s = in.readLine();
+ in.close();
+ return s.split("\0");
+ } catch (Exception exc) {
+ throw new RuntimeException(exc);
+ }
+ }
+
+ public boolean foo(boolean b) {
+ return bar(b);
+ }
+
+ public boolean bar(boolean b) {
+ if (b) {
+ return sleep(2, b, 1.0);
+ } else {
+ return unwindInProcess(1, b);
+ }
+ }
+
+ // Native functions. Note: to avoid deduping, they must all have different signatures.
+
+ public native boolean sleep(int i, boolean b, double dummy);
+
+ public native boolean unwindInProcess(int i, boolean b);
+ public native boolean unwindOtherProcess(int pid);
+}
diff --git a/test/422-type-conversion/src/Main.java b/test/422-type-conversion/src/Main.java
index 7ce2868..447b9b8 100644
--- a/test/422-type-conversion/src/Main.java
+++ b/test/422-type-conversion/src/Main.java
@@ -625,65 +625,67 @@
assertCharEquals((char)0, $opt$IntToChar(-2147483648)); // -(2^31)
}
+ // A dummy value to defeat inlining of these routines.
+ static boolean doThrow = false;
// These methods produce int-to-long Dex instructions.
- static long $opt$ByteToLong(byte a) { return (long)a; }
- static long $opt$ShortToLong(short a) { return (long)a; }
- static long $opt$IntToLong(int a) { return (long)a; }
- static long $opt$CharToLong(int a) { return (long)a; }
+ static long $opt$ByteToLong(byte a) { if (doThrow) throw new Error(); return (long)a; }
+ static long $opt$ShortToLong(short a) { if (doThrow) throw new Error(); return (long)a; }
+ static long $opt$IntToLong(int a) { if (doThrow) throw new Error(); return (long)a; }
+ static long $opt$CharToLong(int a) { if (doThrow) throw new Error(); return (long)a; }
// These methods produce int-to-float Dex instructions.
- static float $opt$ByteToFloat(byte a) { return (float)a; }
- static float $opt$ShortToFloat(short a) { return (float)a; }
- static float $opt$IntToFloat(int a) { return (float)a; }
- static float $opt$CharToFloat(char a) { return (float)a; }
+ static float $opt$ByteToFloat(byte a) { if (doThrow) throw new Error(); return (float)a; }
+ static float $opt$ShortToFloat(short a) { if (doThrow) throw new Error(); return (float)a; }
+ static float $opt$IntToFloat(int a) { if (doThrow) throw new Error(); return (float)a; }
+ static float $opt$CharToFloat(char a) { if (doThrow) throw new Error(); return (float)a; }
// These methods produce int-to-double Dex instructions.
- static double $opt$ByteToDouble(byte a) { return (double)a; }
- static double $opt$ShortToDouble(short a) { return (double)a; }
- static double $opt$IntToDouble(int a) { return (double)a; }
- static double $opt$CharToDouble(int a) { return (double)a; }
+ static double $opt$ByteToDouble(byte a) { if (doThrow) throw new Error(); return (double)a; }
+ static double $opt$ShortToDouble(short a) { if (doThrow) throw new Error(); return (double)a; }
+ static double $opt$IntToDouble(int a) { if (doThrow) throw new Error(); return (double)a; }
+ static double $opt$CharToDouble(int a) { if (doThrow) throw new Error(); return (double)a; }
// These methods produce long-to-int Dex instructions.
- static int $opt$LongToInt(long a) { return (int)a; }
- static int $opt$LongLiteralToInt() { return (int)42L; }
+ static int $opt$LongToInt(long a) { if (doThrow) throw new Error(); return (int)a; }
+ static int $opt$LongLiteralToInt() { if (doThrow) throw new Error(); return (int)42L; }
// This method produces a long-to-float Dex instruction.
- static float $opt$LongToFloat(long a) { return (float)a; }
+ static float $opt$LongToFloat(long a) { if (doThrow) throw new Error(); return (float)a; }
// This method produces a long-to-double Dex instruction.
- static double $opt$LongToDouble(long a) { return (double)a; }
+ static double $opt$LongToDouble(long a) { if (doThrow) throw new Error(); return (double)a; }
// This method produces a float-to-int Dex instruction.
- static int $opt$FloatToInt(float a) { return (int)a; }
+ static int $opt$FloatToInt(float a) { if (doThrow) throw new Error(); return (int)a; }
// This method produces a float-to-long Dex instruction.
- static long $opt$FloatToLong(float a){ return (long)a; }
+ static long $opt$FloatToLong(float a){ if (doThrow) throw new Error(); return (long)a; }
// This method produces a float-to-double Dex instruction.
- static double $opt$FloatToDouble(float a) { return (double)a; }
+ static double $opt$FloatToDouble(float a) { if (doThrow) throw new Error(); return (double)a; }
// This method produces a double-to-int Dex instruction.
- static int $opt$DoubleToInt(double a){ return (int)a; }
+ static int $opt$DoubleToInt(double a){ if (doThrow) throw new Error(); return (int)a; }
// This method produces a double-to-long Dex instruction.
- static long $opt$DoubleToLong(double a){ return (long)a; }
+ static long $opt$DoubleToLong(double a){ if (doThrow) throw new Error(); return (long)a; }
// This method produces a double-to-float Dex instruction.
- static float $opt$DoubleToFloat(double a) { return (float)a; }
+ static float $opt$DoubleToFloat(double a) { if (doThrow) throw new Error(); return (float)a; }
// These methods produce int-to-byte Dex instructions.
- static byte $opt$ShortToByte(short a) { return (byte)a; }
- static byte $opt$IntToByte(int a) { return (byte)a; }
- static byte $opt$CharToByte(char a) { return (byte)a; }
+ static byte $opt$ShortToByte(short a) { if (doThrow) throw new Error(); return (byte)a; }
+ static byte $opt$IntToByte(int a) { if (doThrow) throw new Error(); return (byte)a; }
+ static byte $opt$CharToByte(char a) { if (doThrow) throw new Error(); return (byte)a; }
// These methods produce int-to-short Dex instructions.
- static short $opt$ByteToShort(byte a) { return (short)a; }
- static short $opt$IntToShort(int a) { return (short)a; }
- static short $opt$CharToShort(char a) { return (short)a; }
+ static short $opt$ByteToShort(byte a) { if (doThrow) throw new Error(); return (short)a; }
+ static short $opt$IntToShort(int a) { if (doThrow) throw new Error(); return (short)a; }
+ static short $opt$CharToShort(char a) { if (doThrow) throw new Error(); return (short)a; }
// These methods produce int-to-char Dex instructions.
- static char $opt$ByteToChar(byte a) { return (char)a; }
- static char $opt$ShortToChar(short a) { return (char)a; }
- static char $opt$IntToChar(int a) { return (char)a; }
+ static char $opt$ByteToChar(byte a) { if (doThrow) throw new Error(); return (char)a; }
+ static char $opt$ShortToChar(short a) { if (doThrow) throw new Error(); return (char)a; }
+ static char $opt$IntToChar(int a) { if (doThrow) throw new Error(); return (char)a; }
}
diff --git a/test/441-checker-inliner/src/Main.java b/test/441-checker-inliner/src/Main.java
index 631b140..8894d4e 100644
--- a/test/441-checker-inliner/src/Main.java
+++ b/test/441-checker-inliner/src/Main.java
@@ -17,9 +17,9 @@
public class Main {
// CHECK-START: void Main.InlineVoid() inliner (before)
- // CHECK-DAG: [[Const42:i\d+]] IntConstant 42
+ // CHECK-DAG: <<Const42:i\d+>> IntConstant 42
// CHECK-DAG: InvokeStaticOrDirect
- // CHECK-DAG: InvokeStaticOrDirect [ [[Const42]] ]
+ // CHECK-DAG: InvokeStaticOrDirect [<<Const42>>]
// CHECK-START: void Main.InlineVoid() inliner (after)
// CHECK-NOT: InvokeStaticOrDirect
@@ -30,94 +30,94 @@
}
// CHECK-START: int Main.InlineParameter(int) inliner (before)
- // CHECK-DAG: [[Param:i\d+]] ParameterValue
- // CHECK-DAG: [[Result:i\d+]] InvokeStaticOrDirect [ [[Param]] ]
- // CHECK-DAG: Return [ [[Result]] ]
+ // CHECK-DAG: <<Param:i\d+>> ParameterValue
+ // CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect [<<Param>>]
+ // CHECK-DAG: Return [<<Result>>]
// CHECK-START: int Main.InlineParameter(int) inliner (after)
- // CHECK-DAG: [[Param:i\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Param]] ]
+ // CHECK-DAG: <<Param:i\d+>> ParameterValue
+ // CHECK-DAG: Return [<<Param>>]
public static int InlineParameter(int a) {
return returnParameter(a);
}
// CHECK-START: long Main.InlineWideParameter(long) inliner (before)
- // CHECK-DAG: [[Param:j\d+]] ParameterValue
- // CHECK-DAG: [[Result:j\d+]] InvokeStaticOrDirect [ [[Param]] ]
- // CHECK-DAG: Return [ [[Result]] ]
+ // CHECK-DAG: <<Param:j\d+>> ParameterValue
+ // CHECK-DAG: <<Result:j\d+>> InvokeStaticOrDirect [<<Param>>]
+ // CHECK-DAG: Return [<<Result>>]
// CHECK-START: long Main.InlineWideParameter(long) inliner (after)
- // CHECK-DAG: [[Param:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Param]] ]
+ // CHECK-DAG: <<Param:j\d+>> ParameterValue
+ // CHECK-DAG: Return [<<Param>>]
public static long InlineWideParameter(long a) {
return returnWideParameter(a);
}
// CHECK-START: java.lang.Object Main.InlineReferenceParameter(java.lang.Object) inliner (before)
- // CHECK-DAG: [[Param:l\d+]] ParameterValue
- // CHECK-DAG: [[Result:l\d+]] InvokeStaticOrDirect [ [[Param]] ]
- // CHECK-DAG: Return [ [[Result]] ]
+ // CHECK-DAG: <<Param:l\d+>> ParameterValue
+ // CHECK-DAG: <<Result:l\d+>> InvokeStaticOrDirect [<<Param>>]
+ // CHECK-DAG: Return [<<Result>>]
// CHECK-START: java.lang.Object Main.InlineReferenceParameter(java.lang.Object) inliner (after)
- // CHECK-DAG: [[Param:l\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Param]] ]
+ // CHECK-DAG: <<Param:l\d+>> ParameterValue
+ // CHECK-DAG: Return [<<Param>>]
public static Object InlineReferenceParameter(Object o) {
return returnReferenceParameter(o);
}
// CHECK-START: int Main.InlineInt() inliner (before)
- // CHECK-DAG: [[Result:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Result]] ]
+ // CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Result>>]
// CHECK-START: int Main.InlineInt() inliner (after)
- // CHECK-DAG: [[Const4:i\d+]] IntConstant 4
- // CHECK-DAG: Return [ [[Const4]] ]
+ // CHECK-DAG: <<Const4:i\d+>> IntConstant 4
+ // CHECK-DAG: Return [<<Const4>>]
public static int InlineInt() {
return returnInt();
}
// CHECK-START: long Main.InlineWide() inliner (before)
- // CHECK-DAG: [[Result:j\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Result]] ]
+ // CHECK-DAG: <<Result:j\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Result>>]
// CHECK-START: long Main.InlineWide() inliner (after)
- // CHECK-DAG: [[Const8:j\d+]] LongConstant 8
- // CHECK-DAG: Return [ [[Const8]] ]
+ // CHECK-DAG: <<Const8:j\d+>> LongConstant 8
+ // CHECK-DAG: Return [<<Const8>>]
public static long InlineWide() {
return returnWide();
}
// CHECK-START: int Main.InlineAdd() inliner (before)
- // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
- // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
- // CHECK-DAG: [[Result:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Result]] ]
+ // CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ // CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ // CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Result>>]
// CHECK-START: int Main.InlineAdd() inliner (after)
- // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
- // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
- // CHECK-DAG: [[Add:i\d+]] Add [ [[Const3]] [[Const5]] ]
- // CHECK-DAG: Return [ [[Add]] ]
+ // CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ // CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ // CHECK-DAG: <<Add:i\d+>> Add [<<Const3>>,<<Const5>>]
+ // CHECK-DAG: Return [<<Add>>]
public static int InlineAdd() {
return returnAdd(3, 5);
}
// CHECK-START: int Main.InlineFieldAccess() inliner (before)
- // CHECK-DAG: [[After:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[After]] ]
+ // CHECK-DAG: <<After:i\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<After>>]
// CHECK-START: int Main.InlineFieldAccess() inliner (after)
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Before:i\d+]] StaticFieldGet
- // CHECK-DAG: [[After:i\d+]] Add [ [[Before]] [[Const1]] ]
- // CHECK-DAG: StaticFieldSet [ {{l\d+}} [[After]] ]
- // CHECK-DAG: Return [ [[After]] ]
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: <<Before:i\d+>> StaticFieldGet
+ // CHECK-DAG: <<After:i\d+>> Add [<<Before>>,<<Const1>>]
+ // CHECK-DAG: StaticFieldSet [{{l\d+}},<<After>>]
+ // CHECK-DAG: Return [<<After>>]
// CHECK-START: int Main.InlineFieldAccess() inliner (after)
// CHECK-NOT: InvokeStaticOrDirect
@@ -127,22 +127,22 @@
}
// CHECK-START: int Main.InlineWithControlFlow(boolean) inliner (before)
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
- // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
- // CHECK-DAG: [[Add:i\d+]] InvokeStaticOrDirect [ [[Const1]] [[Const3]] ]
- // CHECK-DAG: [[Sub:i\d+]] InvokeStaticOrDirect [ [[Const5]] [[Const3]] ]
- // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Add]] [[Sub]] ]
- // CHECK-DAG: Return [ [[Phi]] ]
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ // CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ // CHECK-DAG: <<Add:i\d+>> InvokeStaticOrDirect [<<Const1>>,<<Const3>>]
+ // CHECK-DAG: <<Sub:i\d+>> InvokeStaticOrDirect [<<Const5>>,<<Const3>>]
+ // CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Sub>>]
+ // CHECK-DAG: Return [<<Phi>>]
// CHECK-START: int Main.InlineWithControlFlow(boolean) inliner (after)
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
- // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
- // CHECK-DAG: [[Add:i\d+]] Add [ [[Const1]] [[Const3]] ]
- // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Const5]] [[Const3]] ]
- // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Add]] [[Sub]] ]
- // CHECK-DAG: Return [ [[Phi]] ]
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ // CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ // CHECK-DAG: <<Add:i\d+>> Add [<<Const1>>,<<Const3>>]
+ // CHECK-DAG: <<Sub:i\d+>> Sub [<<Const5>>,<<Const3>>]
+ // CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Sub>>]
+ // CHECK-DAG: Return [<<Phi>>]
public static int InlineWithControlFlow(boolean cond) {
int x, const1, const3, const5;
diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java
index 6b21fed..c258db9 100644
--- a/test/442-checker-constant-folding/src/Main.java
+++ b/test/442-checker-constant-folding/src/Main.java
@@ -16,6 +16,12 @@
public class Main {
+ public static void assertFalse(boolean condition) {
+ if (condition) {
+ throw new Error();
+ }
+ }
+
public static void assertIntEquals(int expected, int result) {
if (expected != result) {
throw new Error("Expected: " + expected + ", found: " + result);
@@ -28,19 +34,31 @@
}
}
+ public static void assertFloatEquals(float expected, float result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void assertDoubleEquals(double expected, double result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
/**
* Tiny three-register program exercising int constant folding
* on negation.
*/
// CHECK-START: int Main.IntNegation() constant_folding (before)
- // CHECK-DAG: [[Const42:i\d+]] IntConstant 42
- // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Const42]] ]
- // CHECK-DAG: Return [ [[Neg]] ]
+ // CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ // CHECK-DAG: <<Neg:i\d+>> Neg [<<Const42>>]
+ // CHECK-DAG: Return [<<Neg>>]
// CHECK-START: int Main.IntNegation() constant_folding (after)
- // CHECK-DAG: [[ConstN42:i\d+]] IntConstant -42
- // CHECK-DAG: Return [ [[ConstN42]] ]
+ // CHECK-DAG: <<ConstN42:i\d+>> IntConstant -42
+ // CHECK-DAG: Return [<<ConstN42>>]
public static int IntNegation() {
int x, y;
@@ -55,14 +73,14 @@
*/
// CHECK-START: int Main.IntAddition1() constant_folding (before)
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Const2:i\d+]] IntConstant 2
- // CHECK-DAG: [[Add:i\d+]] Add [ [[Const1]] [[Const2]] ]
- // CHECK-DAG: Return [ [[Add]] ]
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+ // CHECK-DAG: <<Add:i\d+>> Add [<<Const1>>,<<Const2>>]
+ // CHECK-DAG: Return [<<Add>>]
// CHECK-START: int Main.IntAddition1() constant_folding (after)
- // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
- // CHECK-DAG: Return [ [[Const3]] ]
+ // CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ // CHECK-DAG: Return [<<Const3>>]
public static int IntAddition1() {
int a, b, c;
@@ -78,18 +96,18 @@
*/
// CHECK-START: int Main.IntAddition2() constant_folding (before)
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Const2:i\d+]] IntConstant 2
- // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
- // CHECK-DAG: [[Const6:i\d+]] IntConstant 6
- // CHECK-DAG: [[Add1:i\d+]] Add [ [[Const1]] [[Const2]] ]
- // CHECK-DAG: [[Add2:i\d+]] Add [ [[Const5]] [[Const6]] ]
- // CHECK-DAG: [[Add3:i\d+]] Add [ [[Add1]] [[Add2]] ]
- // CHECK-DAG: Return [ [[Add3]] ]
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+ // CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ // CHECK-DAG: <<Const6:i\d+>> IntConstant 6
+ // CHECK-DAG: <<Add1:i\d+>> Add [<<Const1>>,<<Const2>>]
+ // CHECK-DAG: <<Add2:i\d+>> Add [<<Const5>>,<<Const6>>]
+ // CHECK-DAG: <<Add3:i\d+>> Add [<<Add1>>,<<Add2>>]
+ // CHECK-DAG: Return [<<Add3>>]
// CHECK-START: int Main.IntAddition2() constant_folding (after)
- // CHECK-DAG: [[Const14:i\d+]] IntConstant 14
- // CHECK-DAG: Return [ [[Const14]] ]
+ // CHECK-DAG: <<Const14:i\d+>> IntConstant 14
+ // CHECK-DAG: Return [<<Const14>>]
public static int IntAddition2() {
int a, b, c;
@@ -109,14 +127,14 @@
*/
// CHECK-START: int Main.IntSubtraction() constant_folding (before)
- // CHECK-DAG: [[Const6:i\d+]] IntConstant 6
- // CHECK-DAG: [[Const2:i\d+]] IntConstant 2
- // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Const6]] [[Const2]] ]
- // CHECK-DAG: Return [ [[Sub]] ]
+ // CHECK-DAG: <<Const6:i\d+>> IntConstant 6
+ // CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+ // CHECK-DAG: <<Sub:i\d+>> Sub [<<Const6>>,<<Const2>>]
+ // CHECK-DAG: Return [<<Sub>>]
// CHECK-START: int Main.IntSubtraction() constant_folding (after)
- // CHECK-DAG: [[Const4:i\d+]] IntConstant 4
- // CHECK-DAG: Return [ [[Const4]] ]
+ // CHECK-DAG: <<Const4:i\d+>> IntConstant 4
+ // CHECK-DAG: Return [<<Const4>>]
public static int IntSubtraction() {
int a, b, c;
@@ -132,14 +150,14 @@
*/
// CHECK-START: long Main.LongAddition() constant_folding (before)
- // CHECK-DAG: [[Const1:j\d+]] LongConstant 1
- // CHECK-DAG: [[Const2:j\d+]] LongConstant 2
- // CHECK-DAG: [[Add:j\d+]] Add [ [[Const1]] [[Const2]] ]
- // CHECK-DAG: Return [ [[Add]] ]
+ // CHECK-DAG: <<Const1:j\d+>> LongConstant 1
+ // CHECK-DAG: <<Const2:j\d+>> LongConstant 2
+ // CHECK-DAG: <<Add:j\d+>> Add [<<Const1>>,<<Const2>>]
+ // CHECK-DAG: Return [<<Add>>]
// CHECK-START: long Main.LongAddition() constant_folding (after)
- // CHECK-DAG: [[Const3:j\d+]] LongConstant 3
- // CHECK-DAG: Return [ [[Const3]] ]
+ // CHECK-DAG: <<Const3:j\d+>> LongConstant 3
+ // CHECK-DAG: Return [<<Const3>>]
public static long LongAddition() {
long a, b, c;
@@ -155,14 +173,14 @@
*/
// CHECK-START: long Main.LongSubtraction() constant_folding (before)
- // CHECK-DAG: [[Const6:j\d+]] LongConstant 6
- // CHECK-DAG: [[Const2:j\d+]] LongConstant 2
- // CHECK-DAG: [[Sub:j\d+]] Sub [ [[Const6]] [[Const2]] ]
- // CHECK-DAG: Return [ [[Sub]] ]
+ // CHECK-DAG: <<Const6:j\d+>> LongConstant 6
+ // CHECK-DAG: <<Const2:j\d+>> LongConstant 2
+ // CHECK-DAG: <<Sub:j\d+>> Sub [<<Const6>>,<<Const2>>]
+ // CHECK-DAG: Return [<<Sub>>]
// CHECK-START: long Main.LongSubtraction() constant_folding (after)
- // CHECK-DAG: [[Const4:j\d+]] LongConstant 4
- // CHECK-DAG: Return [ [[Const4]] ]
+ // CHECK-DAG: <<Const4:j\d+>> LongConstant 4
+ // CHECK-DAG: Return [<<Const4>>]
public static long LongSubtraction() {
long a, b, c;
@@ -177,14 +195,14 @@
*/
// CHECK-START: int Main.StaticCondition() constant_folding (before)
- // CHECK-DAG: [[Const7:i\d+]] IntConstant 7
- // CHECK-DAG: [[Const2:i\d+]] IntConstant 2
- // CHECK-DAG: [[Cond:z\d+]] GreaterThanOrEqual [ [[Const7]] [[Const2]] ]
- // CHECK-DAG: If [ [[Cond]] ]
+ // CHECK-DAG: <<Const7:i\d+>> IntConstant 7
+ // CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+ // CHECK-DAG: <<Cond:z\d+>> GreaterThanOrEqual [<<Const7>>,<<Const2>>]
+ // CHECK-DAG: If [<<Cond>>]
// CHECK-START: int Main.StaticCondition() constant_folding (after)
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: If [ [[Const1]] ]
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: If [<<Const1>>]
public static int StaticCondition() {
int a, b, c;
@@ -207,18 +225,18 @@
*/
// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (before)
- // CHECK-DAG: [[Const2:i\d+]] IntConstant 2
- // CHECK-DAG: [[Const5:i\d+]] IntConstant 5
- // CHECK-DAG: [[Add:i\d+]] Add [ [[Const5]] [[Const2]] ]
- // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Const5]] [[Const2]] ]
- // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Add]] [[Sub]] ]
- // CHECK-DAG: Return [ [[Phi]] ]
+ // CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+ // CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ // CHECK-DAG: <<Add:i\d+>> Add [<<Const5>>,<<Const2>>]
+ // CHECK-DAG: <<Sub:i\d+>> Sub [<<Const5>>,<<Const2>>]
+ // CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Sub>>]
+ // CHECK-DAG: Return [<<Phi>>]
// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (after)
- // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
- // CHECK-DAG: [[Const7:i\d+]] IntConstant 7
- // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Const7]] [[Const3]] ]
- // CHECK-DAG: Return [ [[Phi]] ]
+ // CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ // CHECK-DAG: <<Const7:i\d+>> IntConstant 7
+ // CHECK-DAG: <<Phi:i\d+>> Phi [<<Const7>>,<<Const3>>]
+ // CHECK-DAG: Return [<<Phi>>]
public static int JumpsAndConditionals(boolean cond) {
int a, b, c;
@@ -236,177 +254,393 @@
*/
// CHECK-START: int Main.And0(int) constant_folding (before)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[And:i\d+]] And [ [[Arg]] [[Const0]] ]
- // CHECK-DAG: Return [ [[And]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<And:i\d+>> And [<<Arg>>,<<Const0>>]
+ // CHECK-DAG: Return [<<And>>]
// CHECK-START: int Main.And0(int) constant_folding (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
// CHECK-NOT: And
- // CHECK-DAG: Return [ [[Const0]] ]
+ // CHECK-DAG: Return [<<Const0>>]
public static int And0(int arg) {
return arg & 0;
}
// CHECK-START: long Main.Mul0(long) constant_folding (before)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[Const0:j\d+]] LongConstant 0
- // CHECK-DAG: [[Mul:j\d+]] Mul [ [[Arg]] [[Const0]] ]
- // CHECK-DAG: Return [ [[Mul]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:j\d+>> LongConstant 0
+ // CHECK-DAG: <<Mul:j\d+>> Mul [<<Arg>>,<<Const0>>]
+ // CHECK-DAG: Return [<<Mul>>]
// CHECK-START: long Main.Mul0(long) constant_folding (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[Const0:j\d+]] LongConstant 0
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:j\d+>> LongConstant 0
// CHECK-NOT: Mul
- // CHECK-DAG: Return [ [[Const0]] ]
+ // CHECK-DAG: Return [<<Const0>>]
public static long Mul0(long arg) {
return arg * 0;
}
// CHECK-START: int Main.OrAllOnes(int) constant_folding (before)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[ConstF:i\d+]] IntConstant -1
- // CHECK-DAG: [[Or:i\d+]] Or [ [[Arg]] [[ConstF]] ]
- // CHECK-DAG: Return [ [[Or]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<ConstF:i\d+>> IntConstant -1
+ // CHECK-DAG: <<Or:i\d+>> Or [<<Arg>>,<<ConstF>>]
+ // CHECK-DAG: Return [<<Or>>]
// CHECK-START: int Main.OrAllOnes(int) constant_folding (after)
- // CHECK-DAG: [[ConstF:i\d+]] IntConstant -1
+ // CHECK-DAG: <<ConstF:i\d+>> IntConstant -1
// CHECK-NOT: Or
- // CHECK-DAG: Return [ [[ConstF]] ]
+ // CHECK-DAG: Return [<<ConstF>>]
public static int OrAllOnes(int arg) {
return arg | -1;
}
// CHECK-START: long Main.Rem0(long) constant_folding (before)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[Const0:j\d+]] LongConstant 0
- // CHECK-DAG: [[DivZeroCheck:j\d+]] DivZeroCheck [ [[Arg]] ]
- // CHECK-DAG: [[Rem:j\d+]] Rem [ [[Const0]] [[DivZeroCheck]] ]
- // CHECK-DAG: Return [ [[Rem]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:j\d+>> LongConstant 0
+ // CHECK-DAG: <<DivZeroCheck:j\d+>> DivZeroCheck [<<Arg>>]
+ // CHECK-DAG: <<Rem:j\d+>> Rem [<<Const0>>,<<DivZeroCheck>>]
+ // CHECK-DAG: Return [<<Rem>>]
// CHECK-START: long Main.Rem0(long) constant_folding (after)
- // CHECK-DAG: [[Const0:j\d+]] LongConstant 0
+ // CHECK-DAG: <<Const0:j\d+>> LongConstant 0
// CHECK-NOT: Rem
- // CHECK-DAG: Return [ [[Const0]] ]
+ // CHECK-DAG: Return [<<Const0>>]
public static long Rem0(long arg) {
return 0 % arg;
}
// CHECK-START: int Main.Rem1(int) constant_folding (before)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Rem:i\d+]] Rem [ [[Arg]] [[Const1]] ]
- // CHECK-DAG: Return [ [[Rem]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: <<Rem:i\d+>> Rem [<<Arg>>,<<Const1>>]
+ // CHECK-DAG: Return [<<Rem>>]
// CHECK-START: int Main.Rem1(int) constant_folding (after)
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
// CHECK-NOT: Rem
- // CHECK-DAG: Return [ [[Const0]] ]
+ // CHECK-DAG: Return [<<Const0>>]
public static int Rem1(int arg) {
return arg % 1;
}
// CHECK-START: long Main.RemN1(long) constant_folding (before)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[ConstN1:j\d+]] LongConstant -1
- // CHECK-DAG: [[DivZeroCheck:j\d+]] DivZeroCheck [ [[Arg]] ]
- // CHECK-DAG: [[Rem:j\d+]] Rem [ [[Arg]] [[DivZeroCheck]] ]
- // CHECK-DAG: Return [ [[Rem]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: <<ConstN1:j\d+>> LongConstant -1
+ // CHECK-DAG: <<DivZeroCheck:j\d+>> DivZeroCheck [<<ConstN1>>]
+ // CHECK-DAG: <<Rem:j\d+>> Rem [<<Arg>>,<<DivZeroCheck>>]
+ // CHECK-DAG: Return [<<Rem>>]
// CHECK-START: long Main.RemN1(long) constant_folding (after)
- // CHECK-DAG: [[Const0:j\d+]] LongConstant 0
+ // CHECK-DAG: <<Const0:j\d+>> LongConstant 0
// CHECK-NOT: Rem
- // CHECK-DAG: Return [ [[Const0]] ]
+ // CHECK-DAG: Return [<<Const0>>]
public static long RemN1(long arg) {
return arg % -1;
}
// CHECK-START: int Main.Shl0(int) constant_folding (before)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[Shl:i\d+]] Shl [ [[Const0]] [[Arg]] ]
- // CHECK-DAG: Return [ [[Shl]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<Shl:i\d+>> Shl [<<Const0>>,<<Arg>>]
+ // CHECK-DAG: Return [<<Shl>>]
// CHECK-START: int Main.Shl0(int) constant_folding (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
// CHECK-NOT: Shl
- // CHECK-DAG: Return [ [[Const0]] ]
+ // CHECK-DAG: Return [<<Const0>>]
public static int Shl0(int arg) {
return 0 << arg;
}
// CHECK-START: long Main.Shr0(int) constant_folding (before)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:j\d+]] LongConstant 0
- // CHECK-DAG: [[Shr:j\d+]] Shr [ [[Const0]] [[Arg]] ]
- // CHECK-DAG: Return [ [[Shr]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:j\d+>> LongConstant 0
+ // CHECK-DAG: <<Shr:j\d+>> Shr [<<Const0>>,<<Arg>>]
+ // CHECK-DAG: Return [<<Shr>>]
// CHECK-START: long Main.Shr0(int) constant_folding (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:j\d+]] LongConstant 0
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:j\d+>> LongConstant 0
// CHECK-NOT: Shr
- // CHECK-DAG: Return [ [[Const0]] ]
+ // CHECK-DAG: Return [<<Const0>>]
public static long Shr0(int arg) {
return (long)0 >> arg;
}
// CHECK-START: long Main.SubSameLong(long) constant_folding (before)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[Sub:j\d+]] Sub [ [[Arg]] [[Arg]] ]
- // CHECK-DAG: Return [ [[Sub]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: <<Sub:j\d+>> Sub [<<Arg>>,<<Arg>>]
+ // CHECK-DAG: Return [<<Sub>>]
// CHECK-START: long Main.SubSameLong(long) constant_folding (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[Const0:j\d+]] LongConstant 0
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:j\d+>> LongConstant 0
// CHECK-NOT: Sub
- // CHECK-DAG: Return [ [[Const0]] ]
+ // CHECK-DAG: Return [<<Const0>>]
public static long SubSameLong(long arg) {
return arg - arg;
}
// CHECK-START: int Main.UShr0(int) constant_folding (before)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[UShr:i\d+]] UShr [ [[Const0]] [[Arg]] ]
- // CHECK-DAG: Return [ [[UShr]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<UShr:i\d+>> UShr [<<Const0>>,<<Arg>>]
+ // CHECK-DAG: Return [<<UShr>>]
// CHECK-START: int Main.UShr0(int) constant_folding (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
// CHECK-NOT: UShr
- // CHECK-DAG: Return [ [[Const0]] ]
+ // CHECK-DAG: Return [<<Const0>>]
public static int UShr0(int arg) {
return 0 >>> arg;
}
// CHECK-START: int Main.XorSameInt(int) constant_folding (before)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Xor:i\d+]] Xor [ [[Arg]] [[Arg]] ]
- // CHECK-DAG: Return [ [[Xor]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Xor:i\d+>> Xor [<<Arg>>,<<Arg>>]
+ // CHECK-DAG: Return [<<Xor>>]
// CHECK-START: int Main.XorSameInt(int) constant_folding (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
// CHECK-NOT: Xor
- // CHECK-DAG: Return [ [[Const0]] ]
+ // CHECK-DAG: Return [<<Const0>>]
public static int XorSameInt(int arg) {
return arg ^ arg;
}
+ // CHECK-START: boolean Main.CmpFloatGreaterThanNaN(float) constant_folding (before)
+ // CHECK-DAG: <<Arg:f\d+>> ParameterValue
+ // CHECK-DAG: <<ConstNan:f\d+>> FloatConstant nan
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: IntConstant 1
+ // CHECK-DAG: <<Cmp:i\d+>> Compare [<<Arg>>,<<ConstNan>>]
+ // CHECK-DAG: <<Le:z\d+>> LessThanOrEqual [<<Cmp>>,<<Const0>>]
+ // CHECK-DAG: If [<<Le>>]
+
+ // CHECK-START: boolean Main.CmpFloatGreaterThanNaN(float) constant_folding (after)
+ // CHECK-DAG: ParameterValue
+ // CHECK-DAG: FloatConstant nan
+ // CHECK-DAG: IntConstant 0
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: If [<<Const1>>]
+
+ // CHECK-START: boolean Main.CmpFloatGreaterThanNaN(float) constant_folding (after)
+ // CHECK-NOT: Compare
+ // CHECK-NOT: LessThanOrEqual
+
+ public static boolean CmpFloatGreaterThanNaN(float arg) {
+ return arg > Float.NaN;
+ }
+
+ // CHECK-START: boolean Main.CmpDoubleLessThanNaN(double) constant_folding (before)
+ // CHECK-DAG: <<Arg:d\d+>> ParameterValue
+ // CHECK-DAG: <<ConstNan:d\d+>> DoubleConstant nan
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: IntConstant 1
+ // CHECK-DAG: <<Cmp:i\d+>> Compare [<<Arg>>,<<ConstNan>>]
+ // CHECK-DAG: <<Ge:z\d+>> GreaterThanOrEqual [<<Cmp>>,<<Const0>>]
+ // CHECK-DAG: If [<<Ge>>]
+
+ // CHECK-START: boolean Main.CmpDoubleLessThanNaN(double) constant_folding (after)
+ // CHECK-DAG: ParameterValue
+ // CHECK-DAG: DoubleConstant nan
+ // CHECK-DAG: IntConstant 0
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: If [<<Const1>>]
+
+ // CHECK-START: boolean Main.CmpDoubleLessThanNaN(double) constant_folding (after)
+ // CHECK-NOT: Compare
+ // CHECK-NOT: GreaterThanOrEqual
+
+ public static boolean CmpDoubleLessThanNaN(double arg) {
+ return arg < Double.NaN;
+ }
+
+ // CHECK-START: int Main.ReturnInt33() constant_folding (before)
+ // CHECK-DAG: <<Const33:j\d+>> LongConstant 33
+ // CHECK-DAG: <<Convert:i\d+>> TypeConversion [<<Const33>>]
+ // CHECK-DAG: Return [<<Convert>>]
+
+ // CHECK-START: int Main.ReturnInt33() constant_folding (after)
+ // CHECK-DAG: <<Const33:i\d+>> IntConstant 33
+ // CHECK-DAG: Return [<<Const33>>]
+
+ public static int ReturnInt33() {
+ long imm = 33L;
+ return (int) imm;
+ }
+
+ // CHECK-START: int Main.ReturnIntMax() constant_folding (before)
+ // CHECK-DAG: <<ConstMax:f\d+>> FloatConstant 1e+34
+ // CHECK-DAG: <<Convert:i\d+>> TypeConversion [<<ConstMax>>]
+ // CHECK-DAG: Return [<<Convert>>]
+
+ // CHECK-START: int Main.ReturnIntMax() constant_folding (after)
+ // CHECK-DAG: <<ConstMax:i\d+>> IntConstant 2147483647
+ // CHECK-DAG: Return [<<ConstMax>>]
+
+ public static int ReturnIntMax() {
+ float imm = 1.0e34f;
+ return (int) imm;
+ }
+
+ // CHECK-START: int Main.ReturnInt0() constant_folding (before)
+ // CHECK-DAG: <<ConstNaN:d\d+>> DoubleConstant nan
+ // CHECK-DAG: <<Convert:i\d+>> TypeConversion [<<ConstNaN>>]
+ // CHECK-DAG: Return [<<Convert>>]
+
+ // CHECK-START: int Main.ReturnInt0() constant_folding (after)
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: Return [<<Const0>>]
+
+ public static int ReturnInt0() {
+ double imm = Double.NaN;
+ return (int) imm;
+ }
+
+ // CHECK-START: long Main.ReturnLong33() constant_folding (before)
+ // CHECK-DAG: <<Const33:i\d+>> IntConstant 33
+ // CHECK-DAG: <<Convert:j\d+>> TypeConversion [<<Const33>>]
+ // CHECK-DAG: Return [<<Convert>>]
+
+ // CHECK-START: long Main.ReturnLong33() constant_folding (after)
+ // CHECK-DAG: <<Const33:j\d+>> LongConstant 33
+ // CHECK-DAG: Return [<<Const33>>]
+
+ public static long ReturnLong33() {
+ int imm = 33;
+ return (long) imm;
+ }
+
+ // CHECK-START: long Main.ReturnLong34() constant_folding (before)
+ // CHECK-DAG: <<Const34:f\d+>> FloatConstant 34
+ // CHECK-DAG: <<Convert:j\d+>> TypeConversion [<<Const34>>]
+ // CHECK-DAG: Return [<<Convert>>]
+
+ // CHECK-START: long Main.ReturnLong34() constant_folding (after)
+ // CHECK-DAG: <<Const34:j\d+>> LongConstant 34
+ // CHECK-DAG: Return [<<Const34>>]
+
+ public static long ReturnLong34() {
+ float imm = 34.0f;
+ return (long) imm;
+ }
+
+ // CHECK-START: long Main.ReturnLong0() constant_folding (before)
+ // CHECK-DAG: <<ConstNaN:d\d+>> DoubleConstant nan
+ // CHECK-DAG: <<Convert:j\d+>> TypeConversion [<<ConstNaN>>]
+ // CHECK-DAG: Return [<<Convert>>]
+
+ // CHECK-START: long Main.ReturnLong0() constant_folding (after)
+ // CHECK-DAG: <<Const0:j\d+>> LongConstant 0
+ // CHECK-DAG: Return [<<Const0>>]
+
+ public static long ReturnLong0() {
+ double imm = -Double.NaN;
+ return (long) imm;
+ }
+
+ // CHECK-START: float Main.ReturnFloat33() constant_folding (before)
+ // CHECK-DAG: <<Const33:i\d+>> IntConstant 33
+ // CHECK-DAG: <<Convert:f\d+>> TypeConversion [<<Const33>>]
+ // CHECK-DAG: Return [<<Convert>>]
+
+ // CHECK-START: float Main.ReturnFloat33() constant_folding (after)
+ // CHECK-DAG: <<Const33:f\d+>> FloatConstant 33
+ // CHECK-DAG: Return [<<Const33>>]
+
+ public static float ReturnFloat33() {
+ int imm = 33;
+ return (float) imm;
+ }
+
+ // CHECK-START: float Main.ReturnFloat34() constant_folding (before)
+ // CHECK-DAG: <<Const34:j\d+>> LongConstant 34
+ // CHECK-DAG: <<Convert:f\d+>> TypeConversion [<<Const34>>]
+ // CHECK-DAG: Return [<<Convert>>]
+
+ // CHECK-START: float Main.ReturnFloat34() constant_folding (after)
+ // CHECK-DAG: <<Const34:f\d+>> FloatConstant 34
+ // CHECK-DAG: Return [<<Const34>>]
+
+ public static float ReturnFloat34() {
+ long imm = 34L;
+ return (float) imm;
+ }
+
+ // CHECK-START: float Main.ReturnFloat99P25() constant_folding (before)
+ // CHECK-DAG: <<Const:d\d+>> DoubleConstant 99.25
+ // CHECK-DAG: <<Convert:f\d+>> TypeConversion [<<Const>>]
+ // CHECK-DAG: Return [<<Convert>>]
+
+ // CHECK-START: float Main.ReturnFloat99P25() constant_folding (after)
+ // CHECK-DAG: <<Const:f\d+>> FloatConstant 99.25
+ // CHECK-DAG: Return [<<Const>>]
+
+ public static float ReturnFloat99P25() {
+ double imm = 99.25;
+ return (float) imm;
+ }
+
+ // CHECK-START: double Main.ReturnDouble33() constant_folding (before)
+ // CHECK-DAG: <<Const33:i\d+>> IntConstant 33
+ // CHECK-DAG: <<Convert:d\d+>> TypeConversion [<<Const33>>]
+ // CHECK-DAG: Return [<<Convert>>]
+
+ // CHECK-START: double Main.ReturnDouble33() constant_folding (after)
+ // CHECK-DAG: <<Const33:d\d+>> DoubleConstant 33
+ // CHECK-DAG: Return [<<Const33>>]
+
+ public static double ReturnDouble33() {
+ int imm = 33;
+ return (double) imm;
+ }
+
+ // CHECK-START: double Main.ReturnDouble34() constant_folding (before)
+ // CHECK-DAG: <<Const34:j\d+>> LongConstant 34
+ // CHECK-DAG: <<Convert:d\d+>> TypeConversion [<<Const34>>]
+ // CHECK-DAG: Return [<<Convert>>]
+
+ // CHECK-START: double Main.ReturnDouble34() constant_folding (after)
+ // CHECK-DAG: <<Const34:d\d+>> DoubleConstant 34
+ // CHECK-DAG: Return [<<Const34>>]
+
+ public static double ReturnDouble34() {
+ long imm = 34L;
+ return (double) imm;
+ }
+
+ // CHECK-START: double Main.ReturnDouble99P25() constant_folding (before)
+ // CHECK-DAG: <<Const:f\d+>> FloatConstant 99.25
+ // CHECK-DAG: <<Convert:d\d+>> TypeConversion [<<Const>>]
+ // CHECK-DAG: Return [<<Convert>>]
+
+ // CHECK-START: double Main.ReturnDouble99P25() constant_folding (after)
+ // CHECK-DAG: <<Const:d\d+>> DoubleConstant 99.25
+ // CHECK-DAG: Return [<<Const>>]
+
+ public static double ReturnDouble99P25() {
+ float imm = 99.25f;
+ return (double) imm;
+ }
+
public static void main(String[] args) {
assertIntEquals(IntNegation(), -42);
assertIntEquals(IntAddition1(), 3);
@@ -417,17 +651,31 @@
assertIntEquals(StaticCondition(), 5);
assertIntEquals(JumpsAndConditionals(true), 7);
assertIntEquals(JumpsAndConditionals(false), 3);
- int random = 123456; // Chosen randomly.
- assertIntEquals(And0(random), 0);
- assertLongEquals(Mul0(random), 0);
- assertIntEquals(OrAllOnes(random), -1);
- assertLongEquals(Rem0(random), 0);
- assertIntEquals(Rem1(random), 0);
- assertLongEquals(RemN1(random), 0);
- assertIntEquals(Shl0(random), 0);
- assertLongEquals(Shr0(random), 0);
- assertLongEquals(SubSameLong(random), 0);
- assertIntEquals(UShr0(random), 0);
- assertIntEquals(XorSameInt(random), 0);
+ int arbitrary = 123456; // Value chosen arbitrarily.
+ assertIntEquals(And0(arbitrary), 0);
+ assertLongEquals(Mul0(arbitrary), 0);
+ assertIntEquals(OrAllOnes(arbitrary), -1);
+ assertLongEquals(Rem0(arbitrary), 0);
+ assertIntEquals(Rem1(arbitrary), 0);
+ assertLongEquals(RemN1(arbitrary), 0);
+ assertIntEquals(Shl0(arbitrary), 0);
+ assertLongEquals(Shr0(arbitrary), 0);
+ assertLongEquals(SubSameLong(arbitrary), 0);
+ assertIntEquals(UShr0(arbitrary), 0);
+ assertIntEquals(XorSameInt(arbitrary), 0);
+ assertFalse(CmpFloatGreaterThanNaN(arbitrary));
+ assertFalse(CmpDoubleLessThanNaN(arbitrary));
+ assertIntEquals(ReturnInt33(), 33);
+ assertIntEquals(ReturnIntMax(), 2147483647);
+ assertIntEquals(ReturnInt0(), 0);
+ assertLongEquals(ReturnLong33(), 33);
+ assertLongEquals(ReturnLong34(), 34);
+ assertLongEquals(ReturnLong0(), 0);
+ assertFloatEquals(ReturnFloat33(), 33);
+ assertFloatEquals(ReturnFloat34(), 34);
+ assertFloatEquals(ReturnFloat99P25(), 99.25f);
+ assertDoubleEquals(ReturnDouble33(), 33);
+ assertDoubleEquals(ReturnDouble34(), 34);
+ assertDoubleEquals(ReturnDouble99P25(), 99.25);
}
}
diff --git a/test/445-checker-licm/src/Main.java b/test/445-checker-licm/src/Main.java
index 91ac2ed..96918d3 100644
--- a/test/445-checker-licm/src/Main.java
+++ b/test/445-checker-licm/src/Main.java
@@ -17,13 +17,13 @@
public class Main {
// CHECK-START: int Main.div() licm (before)
- // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+ // CHECK-DAG: Div loop:{{B\d+}}
// CHECK-START: int Main.div() licm (after)
- // CHECK-NOT: Div ( loop_header:{{B\d+}} )
+ // CHECK-NOT: Div loop:{{B\d+}}
// CHECK-START: int Main.div() licm (after)
- // CHECK-DAG: Div ( loop_header:null )
+ // CHECK-DAG: Div loop:none
public static int div() {
int result = 0;
@@ -34,13 +34,13 @@
}
// CHECK-START: int Main.innerDiv() licm (before)
- // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+ // CHECK-DAG: Div loop:{{B\d+}}
// CHECK-START: int Main.innerDiv() licm (after)
- // CHECK-NOT: Div ( loop_header:{{B\d+}} )
+ // CHECK-NOT: Div loop:{{B\d+}}
// CHECK-START: int Main.innerDiv() licm (after)
- // CHECK-DAG: Div ( loop_header:null )
+ // CHECK-DAG: Div loop:none
public static int innerDiv() {
int result = 0;
@@ -53,10 +53,10 @@
}
// CHECK-START: int Main.innerDiv2() licm (before)
- // CHECK-DAG: Mul ( loop_header:{{B4}} )
+ // CHECK-DAG: Mul loop:B4
// CHECK-START: int Main.innerDiv2() licm (after)
- // CHECK-DAG: Mul ( loop_header:{{B2}} )
+ // CHECK-DAG: Mul loop:B2
public static int innerDiv2() {
int result = 0;
@@ -72,10 +72,10 @@
}
// CHECK-START: int Main.innerDiv3(int, int) licm (before)
- // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+ // CHECK-DAG: Div loop:{{B\d+}}
// CHECK-START: int Main.innerDiv3(int, int) licm (after)
- // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+ // CHECK-DAG: Div loop:{{B\d+}}
public static int innerDiv3(int a, int b) {
int result = 0;
@@ -88,16 +88,16 @@
}
// CHECK-START: int Main.arrayLength(int[]) licm (before)
- // CHECK-DAG: [[NullCheck:l\d+]] NullCheck ( loop_header:{{B\d+}} )
- // CHECK-DAG: ArrayLength [ [[NullCheck]] ] ( loop_header:{{B\d+}} )
+ // CHECK-DAG: <<NullCheck:l\d+>> NullCheck loop:{{B\d+}}
+ // CHECK-DAG: ArrayLength [<<NullCheck>>] loop:{{B\d+}}
// CHECK-START: int Main.arrayLength(int[]) licm (after)
- // CHECK-NOT: NullCheck ( loop_header:{{B\d+}} )
- // CHECK-NOT: ArrayLength ( loop_header:{{B\d+}} )
+ // CHECK-NOT: NullCheck loop:{{B\d+}}
+ // CHECK-NOT: ArrayLength loop:{{B\d+}}
// CHECK-START: int Main.arrayLength(int[]) licm (after)
- // CHECK-DAG: [[NullCheck:l\d+]] NullCheck ( loop_header:null )
- // CHECK-DAG: ArrayLength [ [[NullCheck]] ] ( loop_header:null )
+ // CHECK-DAG: <<NullCheck:l\d+>> NullCheck loop:none
+ // CHECK-DAG: ArrayLength [<<NullCheck>>] loop:none
public static int arrayLength(int[] array) {
int result = 0;
diff --git a/test/446-checker-inliner2/src/Main.java b/test/446-checker-inliner2/src/Main.java
index ecf071e..9ed66d6 100644
--- a/test/446-checker-inliner2/src/Main.java
+++ b/test/446-checker-inliner2/src/Main.java
@@ -17,15 +17,15 @@
public class Main {
// CHECK-START: int Main.inlineInstanceCall(Main) inliner (before)
- // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Invoke>>]
// CHECK-START: int Main.inlineInstanceCall(Main) inliner (after)
// CHECK-NOT: InvokeStaticOrDirect
// CHECK-START: int Main.inlineInstanceCall(Main) inliner (after)
- // CHECK-DAG: [[Field:i\d+]] InstanceFieldGet
- // CHECK-DAG: Return [ [[Field]] ]
+ // CHECK-DAG: <<Field:i\d+>> InstanceFieldGet
+ // CHECK-DAG: Return [<<Field>>]
public static int inlineInstanceCall(Main m) {
return m.foo();
@@ -38,15 +38,15 @@
int field = 42;
// CHECK-START: int Main.inlineNestedCall() inliner (before)
- // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Invoke>>]
// CHECK-START: int Main.inlineNestedCall() inliner (after)
// CHECK-NOT: InvokeStaticOrDirect
// CHECK-START: int Main.inlineNestedCall() inliner (after)
- // CHECK-DAG: [[Const38:i\d+]] IntConstant 38
- // CHECK-DAG: Return [ [[Const38]] ]
+ // CHECK-DAG: <<Const38:i\d+>> IntConstant 38
+ // CHECK-DAG: Return [<<Const38>>]
public static int inlineNestedCall() {
return nestedCall();
diff --git a/test/447-checker-inliner3/src/Main.java b/test/447-checker-inliner3/src/Main.java
index db4b236..9d022b9 100644
--- a/test/447-checker-inliner3/src/Main.java
+++ b/test/447-checker-inliner3/src/Main.java
@@ -17,8 +17,8 @@
public class Main {
// CHECK-START: int Main.inlineIfThenElse() inliner (before)
- // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Invoke>>]
// CHECK-START: int Main.inlineIfThenElse() inliner (after)
// CHECK-NOT: InvokeStaticOrDirect
diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java
index efb7b83..742210c 100644
--- a/test/458-checker-instruction-simplification/src/Main.java
+++ b/test/458-checker-instruction-simplification/src/Main.java
@@ -51,14 +51,14 @@
*/
// CHECK-START: long Main.Add0(long) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[Const0:j\d+]] LongConstant 0
- // CHECK-DAG: [[Add:j\d+]] Add [ [[Const0]] [[Arg]] ]
- // CHECK-DAG: Return [ [[Add]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:j\d+>> LongConstant 0
+ // CHECK-DAG: <<Add:j\d+>> Add [<<Const0>>,<<Arg>>]
+ // CHECK-DAG: Return [<<Add>>]
// CHECK-START: long Main.Add0(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: Return [<<Arg>>]
// CHECK-START: long Main.Add0(long) instruction_simplifier (after)
// CHECK-NOT: Add
@@ -68,14 +68,14 @@
}
// CHECK-START: int Main.AndAllOnes(int) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[ConstF:i\d+]] IntConstant -1
- // CHECK-DAG: [[And:i\d+]] And [ [[Arg]] [[ConstF]] ]
- // CHECK-DAG: Return [ [[And]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<ConstF:i\d+>> IntConstant -1
+ // CHECK-DAG: <<And:i\d+>> And [<<Arg>>,<<ConstF>>]
+ // CHECK-DAG: Return [<<And>>]
// CHECK-START: int Main.AndAllOnes(int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: Return [<<Arg>>]
// CHECK-START: int Main.AndAllOnes(int) instruction_simplifier (after)
// CHECK-NOT: And
@@ -85,14 +85,14 @@
}
// CHECK-START: long Main.Div1(long) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[Const1:j\d+]] LongConstant 1
- // CHECK-DAG: [[Div:j\d+]] Div [ [[Arg]] [[Const1]] ]
- // CHECK-DAG: Return [ [[Div]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: <<Const1:j\d+>> LongConstant 1
+ // CHECK-DAG: <<Div:j\d+>> Div [<<Arg>>,<<Const1>>]
+ // CHECK-DAG: Return [<<Div>>]
// CHECK-START: long Main.Div1(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: Return [<<Arg>>]
// CHECK-START: long Main.Div1(long) instruction_simplifier (after)
// CHECK-NOT: Div
@@ -102,15 +102,15 @@
}
// CHECK-START: int Main.DivN1(int) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[ConstN1:i\d+]] IntConstant -1
- // CHECK-DAG: [[Div:i\d+]] Div [ [[Arg]] [[ConstN1]] ]
- // CHECK-DAG: Return [ [[Div]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<ConstN1:i\d+>> IntConstant -1
+ // CHECK-DAG: <<Div:i\d+>> Div [<<Arg>>,<<ConstN1>>]
+ // CHECK-DAG: Return [<<Div>>]
// CHECK-START: int Main.DivN1(int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Arg]] ]
- // CHECK-DAG: Return [ [[Neg]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg>>]
+ // CHECK-DAG: Return [<<Neg>>]
// CHECK-START: int Main.DivN1(int) instruction_simplifier (after)
// CHECK-NOT: Div
@@ -120,14 +120,14 @@
}
// CHECK-START: long Main.Mul1(long) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[Const1:j\d+]] LongConstant 1
- // CHECK-DAG: [[Mul:j\d+]] Mul [ [[Arg]] [[Const1]] ]
- // CHECK-DAG: Return [ [[Mul]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: <<Const1:j\d+>> LongConstant 1
+ // CHECK-DAG: <<Mul:j\d+>> Mul [<<Arg>>,<<Const1>>]
+ // CHECK-DAG: Return [<<Mul>>]
// CHECK-START: long Main.Mul1(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: Return [<<Arg>>]
// CHECK-START: long Main.Mul1(long) instruction_simplifier (after)
// CHECK-NOT: Mul
@@ -137,15 +137,15 @@
}
// CHECK-START: int Main.MulN1(int) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[ConstN1:i\d+]] IntConstant -1
- // CHECK-DAG: [[Mul:i\d+]] Mul [ [[Arg]] [[ConstN1]] ]
- // CHECK-DAG: Return [ [[Mul]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<ConstN1:i\d+>> IntConstant -1
+ // CHECK-DAG: <<Mul:i\d+>> Mul [<<Arg>>,<<ConstN1>>]
+ // CHECK-DAG: Return [<<Mul>>]
// CHECK-START: int Main.MulN1(int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Arg]] ]
- // CHECK-DAG: Return [ [[Neg]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg>>]
+ // CHECK-DAG: Return [<<Neg>>]
// CHECK-START: int Main.MulN1(int) instruction_simplifier (after)
// CHECK-NOT: Mul
@@ -155,16 +155,16 @@
}
// CHECK-START: long Main.MulPowerOfTwo128(long) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[Const128:j\d+]] LongConstant 128
- // CHECK-DAG: [[Mul:j\d+]] Mul [ [[Arg]] [[Const128]] ]
- // CHECK-DAG: Return [ [[Mul]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: <<Const128:j\d+>> LongConstant 128
+ // CHECK-DAG: <<Mul:j\d+>> Mul [<<Arg>>,<<Const128>>]
+ // CHECK-DAG: Return [<<Mul>>]
// CHECK-START: long Main.MulPowerOfTwo128(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[Const7:i\d+]] IntConstant 7
- // CHECK-DAG: [[Shl:j\d+]] Shl [ [[Arg]] [[Const7]] ]
- // CHECK-DAG: Return [ [[Shl]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: <<Const7:i\d+>> IntConstant 7
+ // CHECK-DAG: <<Shl:j\d+>> Shl [<<Arg>>,<<Const7>>]
+ // CHECK-DAG: Return [<<Shl>>]
// CHECK-START: long Main.MulPowerOfTwo128(long) instruction_simplifier (after)
// CHECK-NOT: Mul
@@ -174,14 +174,14 @@
}
// CHECK-START: int Main.Or0(int) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[Or:i\d+]] Or [ [[Arg]] [[Const0]] ]
- // CHECK-DAG: Return [ [[Or]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<Or:i\d+>> Or [<<Arg>>,<<Const0>>]
+ // CHECK-DAG: Return [<<Or>>]
// CHECK-START: int Main.Or0(int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: Return [<<Arg>>]
// CHECK-START: int Main.Or0(int) instruction_simplifier (after)
// CHECK-NOT: Or
@@ -191,13 +191,13 @@
}
// CHECK-START: long Main.OrSame(long) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[Or:j\d+]] Or [ [[Arg]] [[Arg]] ]
- // CHECK-DAG: Return [ [[Or]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: <<Or:j\d+>> Or [<<Arg>>,<<Arg>>]
+ // CHECK-DAG: Return [<<Or>>]
// CHECK-START: long Main.OrSame(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: Return [<<Arg>>]
// CHECK-START: long Main.OrSame(long) instruction_simplifier (after)
// CHECK-NOT: Or
@@ -207,14 +207,14 @@
}
// CHECK-START: int Main.Shl0(int) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[Shl:i\d+]] Shl [ [[Arg]] [[Const0]] ]
- // CHECK-DAG: Return [ [[Shl]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<Shl:i\d+>> Shl [<<Arg>>,<<Const0>>]
+ // CHECK-DAG: Return [<<Shl>>]
// CHECK-START: int Main.Shl0(int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: Return [<<Arg>>]
// CHECK-START: int Main.Shl0(int) instruction_simplifier (after)
// CHECK-NOT: Shl
@@ -224,15 +224,15 @@
}
// CHECK-START: int Main.Shl1(int) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Shl:i\d+]] Shl [ [[Arg]] [[Const1]] ]
- // CHECK-DAG: Return [ [[Shl]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: <<Shl:i\d+>> Shl [<<Arg>>,<<Const1>>]
+ // CHECK-DAG: Return [<<Shl>>]
// CHECK-START: int Main.Shl1(int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Add:i\d+]] Add [ [[Arg]] [[Arg]] ]
- // CHECK-DAG: Return [ [[Add]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Add:i\d+>> Add [<<Arg>>,<<Arg>>]
+ // CHECK-DAG: Return [<<Add>>]
// CHECK-START: int Main.Shl1(int) instruction_simplifier (after)
// CHECK-NOT: Shl
@@ -242,14 +242,14 @@
}
// CHECK-START: long Main.Shr0(long) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[Shr:j\d+]] Shr [ [[Arg]] [[Const0]] ]
- // CHECK-DAG: Return [ [[Shr]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<Shr:j\d+>> Shr [<<Arg>>,<<Const0>>]
+ // CHECK-DAG: Return [<<Shr>>]
// CHECK-START: long Main.Shr0(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: Return [<<Arg>>]
// CHECK-START: long Main.Shr0(long) instruction_simplifier (after)
// CHECK-NOT: Shr
@@ -259,14 +259,14 @@
}
// CHECK-START: long Main.Sub0(long) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[Const0:j\d+]] LongConstant 0
- // CHECK-DAG: [[Sub:j\d+]] Sub [ [[Arg]] [[Const0]] ]
- // CHECK-DAG: Return [ [[Sub]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:j\d+>> LongConstant 0
+ // CHECK-DAG: <<Sub:j\d+>> Sub [<<Arg>>,<<Const0>>]
+ // CHECK-DAG: Return [<<Sub>>]
// CHECK-START: long Main.Sub0(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: Return [<<Arg>>]
// CHECK-START: long Main.Sub0(long) instruction_simplifier (after)
// CHECK-NOT: Sub
@@ -276,15 +276,15 @@
}
// CHECK-START: int Main.SubAliasNeg(int) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Const0]] [[Arg]] ]
- // CHECK-DAG: Return [ [[Sub]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<Sub:i\d+>> Sub [<<Const0>>,<<Arg>>]
+ // CHECK-DAG: Return [<<Sub>>]
// CHECK-START: int Main.SubAliasNeg(int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Arg]] ]
- // CHECK-DAG: Return [ [[Neg]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg>>]
+ // CHECK-DAG: Return [<<Neg>>]
// CHECK-START: int Main.SubAliasNeg(int) instruction_simplifier (after)
// CHECK-NOT: Sub
@@ -294,14 +294,14 @@
}
// CHECK-START: long Main.UShr0(long) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[UShr:j\d+]] UShr [ [[Arg]] [[Const0]] ]
- // CHECK-DAG: Return [ [[UShr]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<UShr:j\d+>> UShr [<<Arg>>,<<Const0>>]
+ // CHECK-DAG: Return [<<UShr>>]
// CHECK-START: long Main.UShr0(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: Return [<<Arg>>]
// CHECK-START: long Main.UShr0(long) instruction_simplifier (after)
// CHECK-NOT: UShr
@@ -311,14 +311,14 @@
}
// CHECK-START: int Main.Xor0(int) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[Xor:i\d+]] Xor [ [[Arg]] [[Const0]] ]
- // CHECK-DAG: Return [ [[Xor]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<Xor:i\d+>> Xor [<<Arg>>,<<Const0>>]
+ // CHECK-DAG: Return [<<Xor>>]
// CHECK-START: int Main.Xor0(int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: Return [<<Arg>>]
// CHECK-START: int Main.Xor0(int) instruction_simplifier (after)
// CHECK-NOT: Xor
@@ -328,15 +328,15 @@
}
// CHECK-START: int Main.XorAllOnes(int) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[ConstF:i\d+]] IntConstant -1
- // CHECK-DAG: [[Xor:i\d+]] Xor [ [[Arg]] [[ConstF]] ]
- // CHECK-DAG: Return [ [[Xor]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<ConstF:i\d+>> IntConstant -1
+ // CHECK-DAG: <<Xor:i\d+>> Xor [<<Arg>>,<<ConstF>>]
+ // CHECK-DAG: Return [<<Xor>>]
// CHECK-START: int Main.XorAllOnes(int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Not:i\d+]] Not [ [[Arg]] ]
- // CHECK-DAG: Return [ [[Not]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Not:i\d+>> Not [<<Arg>>]
+ // CHECK-DAG: Return [<<Not>>]
// CHECK-START: int Main.XorAllOnes(int) instruction_simplifier (after)
// CHECK-NOT: Xor
@@ -353,20 +353,20 @@
*/
// CHECK-START: int Main.AddNegs1(int, int) instruction_simplifier (before)
- // CHECK-DAG: [[Arg1:i\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:i\d+]] ParameterValue
- // CHECK-DAG: [[Neg1:i\d+]] Neg [ [[Arg1]] ]
- // CHECK-DAG: [[Neg2:i\d+]] Neg [ [[Arg2]] ]
- // CHECK-DAG: [[Add:i\d+]] Add [ [[Neg1]] [[Neg2]] ]
- // CHECK-DAG: Return [ [[Add]] ]
+ // CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+ // CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg1>>]
+ // CHECK-DAG: <<Neg2:i\d+>> Neg [<<Arg2>>]
+ // CHECK-DAG: <<Add:i\d+>> Add [<<Neg1>>,<<Neg2>>]
+ // CHECK-DAG: Return [<<Add>>]
// CHECK-START: int Main.AddNegs1(int, int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg1:i\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:i\d+]] ParameterValue
+ // CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:i\d+>> ParameterValue
// CHECK-NOT: Neg
- // CHECK-DAG: [[Add:i\d+]] Add [ [[Arg1]] [[Arg2]] ]
- // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Add]] ]
- // CHECK-DAG: Return [ [[Neg]] ]
+ // CHECK-DAG: <<Add:i\d+>> Add [<<Arg1>>,<<Arg2>>]
+ // CHECK-DAG: <<Neg:i\d+>> Neg [<<Add>>]
+ // CHECK-DAG: Return [<<Neg>>]
public static int AddNegs1(int arg1, int arg2) {
return -arg1 + -arg2;
@@ -384,34 +384,34 @@
*/
// CHECK-START: int Main.AddNegs2(int, int) instruction_simplifier (before)
- // CHECK-DAG: [[Arg1:i\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:i\d+]] ParameterValue
- // CHECK-DAG: [[Neg1:i\d+]] Neg [ [[Arg1]] ]
- // CHECK-DAG: [[Neg2:i\d+]] Neg [ [[Arg2]] ]
- // CHECK-DAG: [[Add1:i\d+]] Add [ [[Neg1]] [[Neg2]] ]
- // CHECK-DAG: [[Add2:i\d+]] Add [ [[Neg1]] [[Neg2]] ]
- // CHECK-DAG: [[Or:i\d+]] Or [ [[Add1]] [[Add2]] ]
- // CHECK-DAG: Return [ [[Or]] ]
+ // CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+ // CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg1>>]
+ // CHECK-DAG: <<Neg2:i\d+>> Neg [<<Arg2>>]
+ // CHECK-DAG: <<Add1:i\d+>> Add [<<Neg1>>,<<Neg2>>]
+ // CHECK-DAG: <<Add2:i\d+>> Add [<<Neg1>>,<<Neg2>>]
+ // CHECK-DAG: <<Or:i\d+>> Or [<<Add1>>,<<Add2>>]
+ // CHECK-DAG: Return [<<Or>>]
// CHECK-START: int Main.AddNegs2(int, int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg1:i\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:i\d+]] ParameterValue
- // CHECK-DAG: [[Neg1:i\d+]] Neg [ [[Arg1]] ]
- // CHECK-DAG: [[Neg2:i\d+]] Neg [ [[Arg2]] ]
- // CHECK-DAG: [[Add1:i\d+]] Add [ [[Neg1]] [[Neg2]] ]
- // CHECK-DAG: [[Add2:i\d+]] Add [ [[Neg1]] [[Neg2]] ]
+ // CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+ // CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg1>>]
+ // CHECK-DAG: <<Neg2:i\d+>> Neg [<<Arg2>>]
+ // CHECK-DAG: <<Add1:i\d+>> Add [<<Neg1>>,<<Neg2>>]
+ // CHECK-DAG: <<Add2:i\d+>> Add [<<Neg1>>,<<Neg2>>]
// CHECK-NOT: Neg
- // CHECK-DAG: [[Or:i\d+]] Or [ [[Add1]] [[Add2]] ]
- // CHECK-DAG: Return [ [[Or]] ]
+ // CHECK-DAG: <<Or:i\d+>> Or [<<Add1>>,<<Add2>>]
+ // CHECK-DAG: Return [<<Or>>]
// CHECK-START: int Main.AddNegs2(int, int) GVN (after)
- // CHECK-DAG: [[Arg1:i\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:i\d+]] ParameterValue
- // CHECK-DAG: [[Neg1:i\d+]] Neg [ [[Arg1]] ]
- // CHECK-DAG: [[Neg2:i\d+]] Neg [ [[Arg2]] ]
- // CHECK-DAG: [[Add:i\d+]] Add [ [[Neg1]] [[Neg2]] ]
- // CHECK-DAG: [[Or:i\d+]] Or [ [[Add]] [[Add]] ]
- // CHECK-DAG: Return [ [[Or]] ]
+ // CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+ // CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg1>>]
+ // CHECK-DAG: <<Neg2:i\d+>> Neg [<<Arg2>>]
+ // CHECK-DAG: <<Add:i\d+>> Add [<<Neg1>>,<<Neg2>>]
+ // CHECK-DAG: <<Or:i\d+>> Or [<<Add>>,<<Add>>]
+ // CHECK-DAG: Return [<<Or>>]
public static int AddNegs2(int arg1, int arg2) {
int temp1 = -arg1;
@@ -429,26 +429,26 @@
// CHECK-START: long Main.AddNegs3(long, long) instruction_simplifier (before)
// -------------- Arguments and initial negation operations.
- // CHECK-DAG: [[Arg1:j\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:j\d+]] ParameterValue
- // CHECK-DAG: [[Neg1:j\d+]] Neg [ [[Arg1]] ]
- // CHECK-DAG: [[Neg2:j\d+]] Neg [ [[Arg2]] ]
+ // CHECK-DAG: <<Arg1:j\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:j\d+>> ParameterValue
+ // CHECK-DAG: <<Neg1:j\d+>> Neg [<<Arg1>>]
+ // CHECK-DAG: <<Neg2:j\d+>> Neg [<<Arg2>>]
// CHECK: Goto
// -------------- Loop
// CHECK: SuspendCheck
- // CHECK: [[Add:j\d+]] Add [ [[Neg1]] [[Neg2]] ]
+ // CHECK: <<Add:j\d+>> Add [<<Neg1>>,<<Neg2>>]
// CHECK: Goto
// CHECK-START: long Main.AddNegs3(long, long) instruction_simplifier (after)
// -------------- Arguments and initial negation operations.
- // CHECK-DAG: [[Arg1:j\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:j\d+]] ParameterValue
- // CHECK-DAG: [[Neg1:j\d+]] Neg [ [[Arg1]] ]
- // CHECK-DAG: [[Neg2:j\d+]] Neg [ [[Arg2]] ]
+ // CHECK-DAG: <<Arg1:j\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:j\d+>> ParameterValue
+ // CHECK-DAG: <<Neg1:j\d+>> Neg [<<Arg1>>]
+ // CHECK-DAG: <<Neg2:j\d+>> Neg [<<Arg2>>]
// CHECK: Goto
// -------------- Loop
// CHECK: SuspendCheck
- // CHECK: [[Add:j\d+]] Add [ [[Neg1]] [[Neg2]] ]
+ // CHECK: <<Add:j\d+>> Add [<<Neg1>>,<<Neg2>>]
// CHECK-NOT: Neg
// CHECK: Goto
@@ -469,17 +469,17 @@
*/
// CHECK-START: long Main.AddNeg1(long, long) instruction_simplifier (before)
- // CHECK-DAG: [[Arg1:j\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:j\d+]] ParameterValue
- // CHECK-DAG: [[Neg:j\d+]] Neg [ [[Arg1]] ]
- // CHECK-DAG: [[Add:j\d+]] Add [ [[Neg]] [[Arg2]] ]
- // CHECK-DAG: Return [ [[Add]] ]
+ // CHECK-DAG: <<Arg1:j\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:j\d+>> ParameterValue
+ // CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg1>>]
+ // CHECK-DAG: <<Add:j\d+>> Add [<<Neg>>,<<Arg2>>]
+ // CHECK-DAG: Return [<<Add>>]
// CHECK-START: long Main.AddNeg1(long, long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg1:j\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:j\d+]] ParameterValue
- // CHECK-DAG: [[Sub:j\d+]] Sub [ [[Arg2]] [[Arg1]] ]
- // CHECK-DAG: Return [ [[Sub]] ]
+ // CHECK-DAG: <<Arg1:j\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:j\d+>> ParameterValue
+ // CHECK-DAG: <<Sub:j\d+>> Sub [<<Arg2>>,<<Arg1>>]
+ // CHECK-DAG: Return [<<Sub>>]
// CHECK-START: long Main.AddNeg1(long, long) instruction_simplifier (after)
// CHECK-NOT: Neg
@@ -499,22 +499,22 @@
*/
// CHECK-START: long Main.AddNeg2(long, long) instruction_simplifier (before)
- // CHECK-DAG: [[Arg1:j\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:j\d+]] ParameterValue
- // CHECK-DAG: [[Neg:j\d+]] Neg [ [[Arg2]] ]
- // CHECK-DAG: [[Add1:j\d+]] Add [ [[Arg1]] [[Neg]] ]
- // CHECK-DAG: [[Add2:j\d+]] Add [ [[Arg1]] [[Neg]] ]
- // CHECK-DAG: [[Res:j\d+]] Or [ [[Add1]] [[Add2]] ]
- // CHECK-DAG: Return [ [[Res]] ]
+ // CHECK-DAG: <<Arg1:j\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:j\d+>> ParameterValue
+ // CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg2>>]
+ // CHECK-DAG: <<Add1:j\d+>> Add [<<Arg1>>,<<Neg>>]
+ // CHECK-DAG: <<Add2:j\d+>> Add [<<Arg1>>,<<Neg>>]
+ // CHECK-DAG: <<Res:j\d+>> Or [<<Add1>>,<<Add2>>]
+ // CHECK-DAG: Return [<<Res>>]
// CHECK-START: long Main.AddNeg2(long, long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg1:j\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:j\d+]] ParameterValue
- // CHECK-DAG: [[Neg:j\d+]] Neg [ [[Arg2]] ]
- // CHECK-DAG: [[Add1:j\d+]] Add [ [[Arg1]] [[Neg]] ]
- // CHECK-DAG: [[Add2:j\d+]] Add [ [[Arg1]] [[Neg]] ]
- // CHECK-DAG: [[Res:j\d+]] Or [ [[Add1]] [[Add2]] ]
- // CHECK-DAG: Return [ [[Res]] ]
+ // CHECK-DAG: <<Arg1:j\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:j\d+>> ParameterValue
+ // CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg2>>]
+ // CHECK-DAG: <<Add1:j\d+>> Add [<<Arg1>>,<<Neg>>]
+ // CHECK-DAG: <<Add2:j\d+>> Add [<<Arg1>>,<<Neg>>]
+ // CHECK-DAG: <<Res:j\d+>> Or [<<Add1>>,<<Add2>>]
+ // CHECK-DAG: Return [<<Res>>]
// CHECK-START: long Main.AddNeg2(long, long) instruction_simplifier (after)
// CHECK-NOT: Sub
@@ -530,14 +530,14 @@
*/
// CHECK-START: long Main.NegNeg1(long) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[Neg1:j\d+]] Neg [ [[Arg]] ]
- // CHECK-DAG: [[Neg2:j\d+]] Neg [ [[Neg1]] ]
- // CHECK-DAG: Return [ [[Neg2]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: <<Neg1:j\d+>> Neg [<<Arg>>]
+ // CHECK-DAG: <<Neg2:j\d+>> Neg [<<Neg1>>]
+ // CHECK-DAG: Return [<<Neg2>>]
// CHECK-START: long Main.NegNeg1(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: Return [<<Arg>>]
// CHECK-START: long Main.NegNeg1(long) instruction_simplifier (after)
// CHECK-NOT: Neg
@@ -554,26 +554,26 @@
*/
// CHECK-START: int Main.NegNeg2(int) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Neg1:i\d+]] Neg [ [[Arg]] ]
- // CHECK-DAG: [[Neg2:i\d+]] Neg [ [[Neg1]] ]
- // CHECK-DAG: [[Add:i\d+]] Add [ [[Neg1]] [[Neg2]] ]
- // CHECK-DAG: Return [ [[Add]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg>>]
+ // CHECK-DAG: <<Neg2:i\d+>> Neg [<<Neg1>>]
+ // CHECK-DAG: <<Add:i\d+>> Add [<<Neg1>>,<<Neg2>>]
+ // CHECK-DAG: Return [<<Add>>]
// CHECK-START: int Main.NegNeg2(int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Arg]] [[Arg]] ]
- // CHECK-DAG: Return [ [[Sub]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg>>,<<Arg>>]
+ // CHECK-DAG: Return [<<Sub>>]
// CHECK-START: int Main.NegNeg2(int) instruction_simplifier (after)
// CHECK-NOT: Neg
// CHECK-NOT: Add
// CHECK-START: int Main.NegNeg2(int) constant_folding_after_inlining (after)
- // CHECK: [[Const0:i\d+]] IntConstant 0
+ // CHECK: <<Const0:i\d+>> IntConstant 0
// CHECK-NOT: Neg
// CHECK-NOT: Add
- // CHECK: Return [ [[Const0]] ]
+ // CHECK: Return [<<Const0>>]
public static int NegNeg2(int arg) {
int temp = -arg;
@@ -588,15 +588,15 @@
*/
// CHECK-START: long Main.NegNeg3(long) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[Const0:j\d+]] LongConstant 0
- // CHECK-DAG: [[Neg:j\d+]] Neg [ [[Arg]] ]
- // CHECK-DAG: [[Sub:j\d+]] Sub [ [[Const0]] [[Neg]] ]
- // CHECK-DAG: Return [ [[Sub]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:j\d+>> LongConstant 0
+ // CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg>>]
+ // CHECK-DAG: <<Sub:j\d+>> Sub [<<Const0>>,<<Neg>>]
+ // CHECK-DAG: Return [<<Sub>>]
// CHECK-START: long Main.NegNeg3(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: Return [<<Arg>>]
// CHECK-START: long Main.NegNeg3(long) instruction_simplifier (after)
// CHECK-NOT: Neg
@@ -613,17 +613,17 @@
*/
// CHECK-START: int Main.NegSub1(int, int) instruction_simplifier (before)
- // CHECK-DAG: [[Arg1:i\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:i\d+]] ParameterValue
- // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Arg1]] [[Arg2]] ]
- // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Sub]] ]
- // CHECK-DAG: Return [ [[Neg]] ]
+ // CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+ // CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg1>>,<<Arg2>>]
+ // CHECK-DAG: <<Neg:i\d+>> Neg [<<Sub>>]
+ // CHECK-DAG: Return [<<Neg>>]
// CHECK-START: int Main.NegSub1(int, int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg1:i\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:i\d+]] ParameterValue
- // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Arg2]] [[Arg1]] ]
- // CHECK-DAG: Return [ [[Sub]] ]
+ // CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+ // CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg2>>,<<Arg1>>]
+ // CHECK-DAG: Return [<<Sub>>]
// CHECK-START: int Main.NegSub1(int, int) instruction_simplifier (after)
// CHECK-NOT: Neg
@@ -643,22 +643,22 @@
*/
// CHECK-START: int Main.NegSub2(int, int) instruction_simplifier (before)
- // CHECK-DAG: [[Arg1:i\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:i\d+]] ParameterValue
- // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Arg1]] [[Arg2]] ]
- // CHECK-DAG: [[Neg1:i\d+]] Neg [ [[Sub]] ]
- // CHECK-DAG: [[Neg2:i\d+]] Neg [ [[Sub]] ]
- // CHECK-DAG: [[Or:i\d+]] Or [ [[Neg1]] [[Neg2]] ]
- // CHECK-DAG: Return [ [[Or]] ]
+ // CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+ // CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg1>>,<<Arg2>>]
+ // CHECK-DAG: <<Neg1:i\d+>> Neg [<<Sub>>]
+ // CHECK-DAG: <<Neg2:i\d+>> Neg [<<Sub>>]
+ // CHECK-DAG: <<Or:i\d+>> Or [<<Neg1>>,<<Neg2>>]
+ // CHECK-DAG: Return [<<Or>>]
// CHECK-START: int Main.NegSub2(int, int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg1:i\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:i\d+]] ParameterValue
- // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Arg1]] [[Arg2]] ]
- // CHECK-DAG: [[Neg1:i\d+]] Neg [ [[Sub]] ]
- // CHECK-DAG: [[Neg2:i\d+]] Neg [ [[Sub]] ]
- // CHECK-DAG: [[Or:i\d+]] Or [ [[Neg1]] [[Neg2]] ]
- // CHECK-DAG: Return [ [[Or]] ]
+ // CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+ // CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg1>>,<<Arg2>>]
+ // CHECK-DAG: <<Neg1:i\d+>> Neg [<<Sub>>]
+ // CHECK-DAG: <<Neg2:i\d+>> Neg [<<Sub>>]
+ // CHECK-DAG: <<Or:i\d+>> Or [<<Neg1>>,<<Neg2>>]
+ // CHECK-DAG: Return [<<Or>>]
public static int NegSub2(int arg1, int arg2) {
int temp = arg1 - arg2;
@@ -671,15 +671,15 @@
*/
// CHECK-START: long Main.NotNot1(long) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[ConstF1:j\d+]] LongConstant -1
- // CHECK-DAG: [[Xor1:j\d+]] Xor [ [[Arg]] [[ConstF1]] ]
- // CHECK-DAG: [[Xor2:j\d+]] Xor [ [[Xor1]] [[ConstF1]] ]
- // CHECK-DAG: Return [ [[Xor2]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: <<ConstF1:j\d+>> LongConstant -1
+ // CHECK-DAG: <<Xor1:j\d+>> Xor [<<Arg>>,<<ConstF1>>]
+ // CHECK-DAG: <<Xor2:j\d+>> Xor [<<Xor1>>,<<ConstF1>>]
+ // CHECK-DAG: Return [<<Xor2>>]
// CHECK-START: long Main.NotNot1(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ // CHECK-DAG: Return [<<Arg>>]
// CHECK-START: long Main.NotNot1(long) instruction_simplifier (after)
// CHECK-NOT: Xor
@@ -689,18 +689,18 @@
}
// CHECK-START: int Main.NotNot2(int) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[ConstF1:i\d+]] IntConstant -1
- // CHECK-DAG: [[Xor1:i\d+]] Xor [ [[Arg]] [[ConstF1]] ]
- // CHECK-DAG: [[Xor2:i\d+]] Xor [ [[Xor1]] [[ConstF1]] ]
- // CHECK-DAG: [[Add:i\d+]] Add [ [[Xor1]] [[Xor2]] ]
- // CHECK-DAG: Return [ [[Add]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<ConstF1:i\d+>> IntConstant -1
+ // CHECK-DAG: <<Xor1:i\d+>> Xor [<<Arg>>,<<ConstF1>>]
+ // CHECK-DAG: <<Xor2:i\d+>> Xor [<<Xor1>>,<<ConstF1>>]
+ // CHECK-DAG: <<Add:i\d+>> Add [<<Xor1>>,<<Xor2>>]
+ // CHECK-DAG: Return [<<Add>>]
// CHECK-START: int Main.NotNot2(int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Not:i\d+]] Not [ [[Arg]] ]
- // CHECK-DAG: [[Add:i\d+]] Add [ [[Not]] [[Arg]] ]
- // CHECK-DAG: Return [ [[Add]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: <<Not:i\d+>> Not [<<Arg>>]
+ // CHECK-DAG: <<Add:i\d+>> Add [<<Not>>,<<Arg>>]
+ // CHECK-DAG: Return [<<Add>>]
// CHECK-START: int Main.NotNot2(int) instruction_simplifier (after)
// CHECK-NOT: Xor
@@ -716,18 +716,18 @@
*/
// CHECK-START: int Main.SubNeg1(int, int) instruction_simplifier (before)
- // CHECK-DAG: [[Arg1:i\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:i\d+]] ParameterValue
- // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Arg1]] ]
- // CHECK-DAG: [[Sub:i\d+]] Sub [ [[Neg]] [[Arg2]] ]
- // CHECK-DAG: Return [ [[Sub]] ]
+ // CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+ // CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg1>>]
+ // CHECK-DAG: <<Sub:i\d+>> Sub [<<Neg>>,<<Arg2>>]
+ // CHECK-DAG: Return [<<Sub>>]
// CHECK-START: int Main.SubNeg1(int, int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg1:i\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:i\d+]] ParameterValue
- // CHECK-DAG: [[Add:i\d+]] Add [ [[Arg1]] [[Arg2]] ]
- // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Add]] ]
- // CHECK-DAG: Return [ [[Neg]] ]
+ // CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+ // CHECK-DAG: <<Add:i\d+>> Add [<<Arg1>>,<<Arg2>>]
+ // CHECK-DAG: <<Neg:i\d+>> Neg [<<Add>>]
+ // CHECK-DAG: Return [<<Neg>>]
// CHECK-START: int Main.SubNeg1(int, int) instruction_simplifier (after)
// CHECK-NOT: Sub
@@ -747,22 +747,22 @@
*/
// CHECK-START: int Main.SubNeg2(int, int) instruction_simplifier (before)
- // CHECK-DAG: [[Arg1:i\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:i\d+]] ParameterValue
- // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Arg1]] ]
- // CHECK-DAG: [[Sub1:i\d+]] Sub [ [[Neg]] [[Arg2]] ]
- // CHECK-DAG: [[Sub2:i\d+]] Sub [ [[Neg]] [[Arg2]] ]
- // CHECK-DAG: [[Or:i\d+]] Or [ [[Sub1]] [[Sub2]] ]
- // CHECK-DAG: Return [ [[Or]] ]
+ // CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+ // CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg1>>]
+ // CHECK-DAG: <<Sub1:i\d+>> Sub [<<Neg>>,<<Arg2>>]
+ // CHECK-DAG: <<Sub2:i\d+>> Sub [<<Neg>>,<<Arg2>>]
+ // CHECK-DAG: <<Or:i\d+>> Or [<<Sub1>>,<<Sub2>>]
+ // CHECK-DAG: Return [<<Or>>]
// CHECK-START: int Main.SubNeg2(int, int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg1:i\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:i\d+]] ParameterValue
- // CHECK-DAG: [[Neg:i\d+]] Neg [ [[Arg1]] ]
- // CHECK-DAG: [[Sub1:i\d+]] Sub [ [[Neg]] [[Arg2]] ]
- // CHECK-DAG: [[Sub2:i\d+]] Sub [ [[Neg]] [[Arg2]] ]
- // CHECK-DAG: [[Or:i\d+]] Or [ [[Sub1]] [[Sub2]] ]
- // CHECK-DAG: Return [ [[Or]] ]
+ // CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+ // CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg1>>]
+ // CHECK-DAG: <<Sub1:i\d+>> Sub [<<Neg>>,<<Arg2>>]
+ // CHECK-DAG: <<Sub2:i\d+>> Sub [<<Neg>>,<<Arg2>>]
+ // CHECK-DAG: <<Or:i\d+>> Or [<<Sub1>>,<<Sub2>>]
+ // CHECK-DAG: Return [<<Or>>]
// CHECK-START: int Main.SubNeg2(int, int) instruction_simplifier (after)
// CHECK-NOT: Add
@@ -781,24 +781,24 @@
// CHECK-START: long Main.SubNeg3(long, long) instruction_simplifier (before)
// -------------- Arguments and initial negation operation.
- // CHECK-DAG: [[Arg1:j\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:j\d+]] ParameterValue
- // CHECK-DAG: [[Neg:j\d+]] Neg [ [[Arg1]] ]
+ // CHECK-DAG: <<Arg1:j\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:j\d+>> ParameterValue
+ // CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg1>>]
// CHECK: Goto
// -------------- Loop
// CHECK: SuspendCheck
- // CHECK: [[Sub:j\d+]] Sub [ [[Neg]] [[Arg2]] ]
+ // CHECK: <<Sub:j\d+>> Sub [<<Neg>>,<<Arg2>>]
// CHECK: Goto
// CHECK-START: long Main.SubNeg3(long, long) instruction_simplifier (after)
// -------------- Arguments and initial negation operation.
- // CHECK-DAG: [[Arg1:j\d+]] ParameterValue
- // CHECK-DAG: [[Arg2:j\d+]] ParameterValue
- // CHECK-DAG: [[Neg:j\d+]] Neg [ [[Arg1]] ]
+ // CHECK-DAG: <<Arg1:j\d+>> ParameterValue
+ // CHECK-DAG: <<Arg2:j\d+>> ParameterValue
+ // CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg1>>]
// CHECK-DAG: Goto
// -------------- Loop
// CHECK: SuspendCheck
- // CHECK: [[Sub:j\d+]] Sub [ [[Neg]] [[Arg2]] ]
+ // CHECK: <<Sub:j\d+>> Sub [<<Neg>>,<<Arg2>>]
// CHECK-NOT: Neg
// CHECK: Goto
@@ -812,116 +812,116 @@
}
// CHECK-START: int Main.EqualTrueRhs(boolean) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Cond:z\d+]] Equal [ [[Arg]] [[Const1]] ]
- // CHECK-DAG: If [ [[Cond]] ]
+ // CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Const1>>]
+ // CHECK-DAG: If [<<Cond>>]
// CHECK-START: int Main.EqualTrueRhs(boolean) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: If [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: If [<<Arg>>]
public static int EqualTrueRhs(boolean arg) {
return (arg != true) ? 3 : 5;
}
// CHECK-START: int Main.EqualTrueLhs(boolean) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Cond:z\d+]] Equal [ [[Const1]] [[Arg]] ]
- // CHECK-DAG: If [ [[Cond]] ]
+ // CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: <<Cond:z\d+>> Equal [<<Const1>>,<<Arg>>]
+ // CHECK-DAG: If [<<Cond>>]
// CHECK-START: int Main.EqualTrueLhs(boolean) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: If [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: If [<<Arg>>]
public static int EqualTrueLhs(boolean arg) {
return (true != arg) ? 3 : 5;
}
// CHECK-START: int Main.EqualFalseRhs(boolean) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[Cond:z\d+]] Equal [ [[Arg]] [[Const0]] ]
- // CHECK-DAG: If [ [[Cond]] ]
+ // CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Const0>>]
+ // CHECK-DAG: If [<<Cond>>]
// CHECK-START: int Main.EqualFalseRhs(boolean) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: [[NotArg:z\d+]] BooleanNot [ [[Arg]] ]
- // CHECK-DAG: If [ [[NotArg]] ]
+ // CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
+ // CHECK-DAG: If [<<NotArg>>]
public static int EqualFalseRhs(boolean arg) {
return (arg != false) ? 3 : 5;
}
// CHECK-START: int Main.EqualFalseLhs(boolean) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[Cond:z\d+]] Equal [ [[Const0]] [[Arg]] ]
- // CHECK-DAG: If [ [[Cond]] ]
+ // CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<Cond:z\d+>> Equal [<<Const0>>,<<Arg>>]
+ // CHECK-DAG: If [<<Cond>>]
// CHECK-START: int Main.EqualFalseLhs(boolean) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: [[NotArg:z\d+]] BooleanNot [ [[Arg]] ]
- // CHECK-DAG: If [ [[NotArg]] ]
+ // CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
+ // CHECK-DAG: If [<<NotArg>>]
public static int EqualFalseLhs(boolean arg) {
return (false != arg) ? 3 : 5;
}
// CHECK-START: int Main.NotEqualTrueRhs(boolean) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Cond:z\d+]] NotEqual [ [[Arg]] [[Const1]] ]
- // CHECK-DAG: If [ [[Cond]] ]
+ // CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Arg>>,<<Const1>>]
+ // CHECK-DAG: If [<<Cond>>]
// CHECK-START: int Main.NotEqualTrueRhs(boolean) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: [[NotArg:z\d+]] BooleanNot [ [[Arg]] ]
- // CHECK-DAG: If [ [[NotArg]] ]
+ // CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
+ // CHECK-DAG: If [<<NotArg>>]
public static int NotEqualTrueRhs(boolean arg) {
return (arg == true) ? 3 : 5;
}
// CHECK-START: int Main.NotEqualTrueLhs(boolean) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Cond:z\d+]] NotEqual [ [[Const1]] [[Arg]] ]
- // CHECK-DAG: If [ [[Cond]] ]
+ // CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Const1>>,<<Arg>>]
+ // CHECK-DAG: If [<<Cond>>]
// CHECK-START: int Main.NotEqualTrueLhs(boolean) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: [[NotArg:z\d+]] BooleanNot [ [[Arg]] ]
- // CHECK-DAG: If [ [[NotArg]] ]
+ // CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
+ // CHECK-DAG: If [<<NotArg>>]
public static int NotEqualTrueLhs(boolean arg) {
return (true == arg) ? 3 : 5;
}
// CHECK-START: int Main.NotEqualFalseRhs(boolean) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[Cond:z\d+]] NotEqual [ [[Arg]] [[Const0]] ]
- // CHECK-DAG: If [ [[Cond]] ]
+ // CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Arg>>,<<Const0>>]
+ // CHECK-DAG: If [<<Cond>>]
// CHECK-START: int Main.NotEqualFalseRhs(boolean) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: If [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: If [<<Arg>>]
public static int NotEqualFalseRhs(boolean arg) {
return (arg == false) ? 3 : 5;
}
// CHECK-START: int Main.NotEqualFalseLhs(boolean) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[Cond:z\d+]] NotEqual [ [[Const0]] [[Arg]] ]
- // CHECK-DAG: If [ [[Cond]] ]
+ // CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Const0>>,<<Arg>>]
+ // CHECK-DAG: If [<<Cond>>]
// CHECK-START: int Main.NotEqualFalseLhs(boolean) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: If [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: If [<<Arg>>]
public static int NotEqualFalseLhs(boolean arg) {
return (false == arg) ? 3 : 5;
@@ -934,15 +934,15 @@
*/
// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_types (before)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: [[NotArg:z\d+]] BooleanNot [ [[Arg]] ]
- // CHECK-DAG: [[NotNotArg:z\d+]] BooleanNot [ [[NotArg]] ]
- // CHECK-DAG: Return [ [[NotNotArg]] ]
+ // CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
+ // CHECK-DAG: <<NotNotArg:z\d+>> BooleanNot [<<NotArg>>]
+ // CHECK-DAG: Return [<<NotNotArg>>]
// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_types (after)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: BooleanNot [ [[Arg]] ]
- // CHECK-DAG: Return [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: BooleanNot [<<Arg>>]
+ // CHECK-DAG: Return [<<Arg>>]
// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_types (after)
// CHECK: BooleanNot
@@ -957,16 +957,16 @@
}
// CHECK-START: float Main.Div2(float) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:f\d+]] ParameterValue
- // CHECK-DAG: [[Const2:f\d+]] FloatConstant 2
- // CHECK-DAG: [[Div:f\d+]] Div [ [[Arg]] [[Const2]] ]
- // CHECK-DAG: Return [ [[Div]] ]
+ // CHECK-DAG: <<Arg:f\d+>> ParameterValue
+ // CHECK-DAG: <<Const2:f\d+>> FloatConstant 2
+ // CHECK-DAG: <<Div:f\d+>> Div [<<Arg>>,<<Const2>>]
+ // CHECK-DAG: Return [<<Div>>]
// CHECK-START: float Main.Div2(float) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:f\d+]] ParameterValue
- // CHECK-DAG: [[ConstP5:f\d+]] FloatConstant 0.5
- // CHECK-DAG: [[Mul:f\d+]] Mul [ [[Arg]] [[ConstP5]] ]
- // CHECK-DAG: Return [ [[Mul]] ]
+ // CHECK-DAG: <<Arg:f\d+>> ParameterValue
+ // CHECK-DAG: <<ConstP5:f\d+>> FloatConstant 0.5
+ // CHECK-DAG: <<Mul:f\d+>> Mul [<<Arg>>,<<ConstP5>>]
+ // CHECK-DAG: Return [<<Mul>>]
// CHECK-START: float Main.Div2(float) instruction_simplifier (after)
// CHECK-NOT: Div
@@ -976,16 +976,16 @@
}
// CHECK-START: double Main.Div2(double) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:d\d+]] ParameterValue
- // CHECK-DAG: [[Const2:d\d+]] DoubleConstant 2
- // CHECK-DAG: [[Div:d\d+]] Div [ [[Arg]] [[Const2]] ]
- // CHECK-DAG: Return [ [[Div]] ]
+ // CHECK-DAG: <<Arg:d\d+>> ParameterValue
+ // CHECK-DAG: <<Const2:d\d+>> DoubleConstant 2
+ // CHECK-DAG: <<Div:d\d+>> Div [<<Arg>>,<<Const2>>]
+ // CHECK-DAG: Return [<<Div>>]
// CHECK-START: double Main.Div2(double) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:d\d+]] ParameterValue
- // CHECK-DAG: [[ConstP5:d\d+]] DoubleConstant 0.5
- // CHECK-DAG: [[Mul:d\d+]] Mul [ [[Arg]] [[ConstP5]] ]
- // CHECK-DAG: Return [ [[Mul]] ]
+ // CHECK-DAG: <<Arg:d\d+>> ParameterValue
+ // CHECK-DAG: <<ConstP5:d\d+>> DoubleConstant 0.5
+ // CHECK-DAG: <<Mul:d\d+>> Mul [<<Arg>>,<<ConstP5>>]
+ // CHECK-DAG: Return [<<Mul>>]
// CHECK-START: double Main.Div2(double) instruction_simplifier (after)
// CHECK-NOT: Div
@@ -994,16 +994,16 @@
}
// CHECK-START: float Main.DivMP25(float) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:f\d+]] ParameterValue
- // CHECK-DAG: [[ConstMP25:f\d+]] FloatConstant -0.25
- // CHECK-DAG: [[Div:f\d+]] Div [ [[Arg]] [[ConstMP25]] ]
- // CHECK-DAG: Return [ [[Div]] ]
+ // CHECK-DAG: <<Arg:f\d+>> ParameterValue
+ // CHECK-DAG: <<ConstMP25:f\d+>> FloatConstant -0.25
+ // CHECK-DAG: <<Div:f\d+>> Div [<<Arg>>,<<ConstMP25>>]
+ // CHECK-DAG: Return [<<Div>>]
// CHECK-START: float Main.DivMP25(float) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:f\d+]] ParameterValue
- // CHECK-DAG: [[ConstM4:f\d+]] FloatConstant -4
- // CHECK-DAG: [[Mul:f\d+]] Mul [ [[Arg]] [[ConstM4]] ]
- // CHECK-DAG: Return [ [[Mul]] ]
+ // CHECK-DAG: <<Arg:f\d+>> ParameterValue
+ // CHECK-DAG: <<ConstM4:f\d+>> FloatConstant -4
+ // CHECK-DAG: <<Mul:f\d+>> Mul [<<Arg>>,<<ConstM4>>]
+ // CHECK-DAG: Return [<<Mul>>]
// CHECK-START: float Main.DivMP25(float) instruction_simplifier (after)
// CHECK-NOT: Div
@@ -1013,16 +1013,16 @@
}
// CHECK-START: double Main.DivMP25(double) instruction_simplifier (before)
- // CHECK-DAG: [[Arg:d\d+]] ParameterValue
- // CHECK-DAG: [[ConstMP25:d\d+]] DoubleConstant -0.25
- // CHECK-DAG: [[Div:d\d+]] Div [ [[Arg]] [[ConstMP25]] ]
- // CHECK-DAG: Return [ [[Div]] ]
+ // CHECK-DAG: <<Arg:d\d+>> ParameterValue
+ // CHECK-DAG: <<ConstMP25:d\d+>> DoubleConstant -0.25
+ // CHECK-DAG: <<Div:d\d+>> Div [<<Arg>>,<<ConstMP25>>]
+ // CHECK-DAG: Return [<<Div>>]
// CHECK-START: double Main.DivMP25(double) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:d\d+]] ParameterValue
- // CHECK-DAG: [[ConstM4:d\d+]] DoubleConstant -4
- // CHECK-DAG: [[Mul:d\d+]] Mul [ [[Arg]] [[ConstM4]] ]
- // CHECK-DAG: Return [ [[Mul]] ]
+ // CHECK-DAG: <<Arg:d\d+>> ParameterValue
+ // CHECK-DAG: <<ConstM4:d\d+>> DoubleConstant -4
+ // CHECK-DAG: <<Mul:d\d+>> Mul [<<Arg>>,<<ConstM4>>]
+ // CHECK-DAG: Return [<<Mul>>]
// CHECK-START: double Main.DivMP25(double) instruction_simplifier (after)
// CHECK-NOT: Div
diff --git a/test/462-checker-inlining-across-dex-files/src/Main.java b/test/462-checker-inlining-across-dex-files/src/Main.java
index d5563b8..218c7ce 100644
--- a/test/462-checker-inlining-across-dex-files/src/Main.java
+++ b/test/462-checker-inlining-across-dex-files/src/Main.java
@@ -22,7 +22,7 @@
public class Main {
// CHECK-START: void Main.inlineEmptyMethod() inliner (before)
- // CHECK-DAG: [[Invoke:v\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: <<Invoke:v\d+>> InvokeStaticOrDirect
// CHECK-DAG: ReturnVoid
// CHECK-START: void Main.inlineEmptyMethod() inliner (after)
@@ -33,120 +33,124 @@
}
// CHECK-START: int Main.inlineReturnIntMethod() inliner (before)
- // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Invoke>>]
// CHECK-START: int Main.inlineReturnIntMethod() inliner (after)
// CHECK-NOT: InvokeStaticOrDirect
// CHECK-START: int Main.inlineReturnIntMethod() inliner (after)
- // CHECK-DAG: [[Const38:i\d+]] IntConstant 38
- // CHECK-DAG: Return [ [[Const38]] ]
+ // CHECK-DAG: <<Const38:i\d+>> IntConstant 38
+ // CHECK-DAG: Return [<<Const38>>]
public static int inlineReturnIntMethod() {
return OtherDex.returnIntMethod();
}
// CHECK-START: int Main.dontInlineOtherDexStatic() inliner (before)
- // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Invoke>>]
// CHECK-START: int Main.dontInlineOtherDexStatic() inliner (after)
- // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Invoke>>]
public static int dontInlineOtherDexStatic() {
return OtherDex.returnOtherDexStatic();
}
// CHECK-START: int Main.inlineMainStatic() inliner (before)
- // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Invoke>>]
// CHECK-START: int Main.inlineMainStatic() inliner (after)
// CHECK-NOT: InvokeStaticOrDirect
// CHECK-START: int Main.inlineMainStatic() inliner (after)
- // CHECK-DAG: [[Static:i\d+]] StaticFieldGet
- // CHECK-DAG: Return [ [[Static]] ]
+ // CHECK-DAG: <<Static:i\d+>> StaticFieldGet
+ // CHECK-DAG: Return [<<Static>>]
public static int inlineMainStatic() {
return OtherDex.returnMainStatic();
}
// CHECK-START: int Main.dontInlineRecursiveCall() inliner (before)
- // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Invoke>>]
// CHECK-START: int Main.dontInlineRecursiveCall() inliner (after)
- // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Invoke>>]
public static int dontInlineRecursiveCall() {
return OtherDex.recursiveCall();
}
// CHECK-START: java.lang.String Main.dontInlineReturnString() inliner (before)
- // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ // CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Invoke>>]
// CHECK-START: java.lang.String Main.dontInlineReturnString() inliner (after)
- // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ // CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Invoke>>]
public static String dontInlineReturnString() {
return OtherDex.returnString();
}
// CHECK-START: java.lang.Class Main.dontInlineOtherDexClass() inliner (before)
- // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ // CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Invoke>>]
// CHECK-START: java.lang.Class Main.dontInlineOtherDexClass() inliner (after)
- // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ // CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Invoke>>]
public static Class dontInlineOtherDexClass() {
return OtherDex.returnOtherDexClass();
}
// CHECK-START: java.lang.Class Main.inlineMainClass() inliner (before)
- // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ // CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Invoke>>]
// CHECK-START: java.lang.Class Main.inlineMainClass() inliner (after)
// CHECK-NOT: InvokeStaticOrDirect
// CHECK-START: java.lang.Class Main.inlineMainClass() inliner (after)
- // CHECK-DAG: [[Class:l\d+]] LoadClass
- // CHECK-DAG: Return [ [[Class]] ]
+ // CHECK-DAG: Return [<<Class:l\d+>>]
+ // CHECK-DAG: <<Class>> LoadClass
+ // Note: There are two LoadClass instructions. We obtain the correct
+ // instruction id by matching the Return's input list first.
public static Class inlineMainClass() {
return OtherDex.returnMainClass();
}
// CHECK-START: java.lang.Class Main.dontInlineOtherDexClassStaticCall() inliner (before)
- // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ // CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Invoke>>]
// CHECK-START: java.lang.Class Main.dontInlineOtherDexClassStaticCall() inliner (after)
- // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ // CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Invoke>>]
public static Class dontInlineOtherDexClassStaticCall() {
return OtherDex.returnOtherDexClassStaticCall();
}
// CHECK-START: java.lang.Class Main.inlineOtherDexCallingMain() inliner (before)
- // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ // CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Invoke>>]
// CHECK-START: java.lang.Class Main.inlineOtherDexCallingMain() inliner (after)
// CHECK-NOT: InvokeStaticOrDirect
// CHECK-START: java.lang.Class Main.inlineOtherDexCallingMain() inliner (after)
- // CHECK-DAG: [[Class:l\d+]] LoadClass
- // CHECK-DAG: Return [ [[Class]] ]
+ // CHECK-DAG: Return [<<Class:l\d+>>]
+ // CHECK-DAG: <<Class>> LoadClass
+ // Note: There are two LoadClass instructions. We obtain the correct
+ // instruction id by matching the Return's input list first.
public static Class inlineOtherDexCallingMain() {
return OtherDex.returnOtherDexCallingMain();
diff --git a/test/463-checker-boolean-simplifier/src/Main.java b/test/463-checker-boolean-simplifier/src/Main.java
index 4346103..e237448 100644
--- a/test/463-checker-boolean-simplifier/src/Main.java
+++ b/test/463-checker-boolean-simplifier/src/Main.java
@@ -38,12 +38,12 @@
*/
// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (before)
- // CHECK-DAG: [[Param:z\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: If [ [[Param]] ]
- // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Const1]] [[Const0]] ]
- // CHECK-DAG: Return [ [[Phi]] ]
+ // CHECK-DAG: <<Param:z\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: If [<<Param>>]
+ // CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,<<Const0>>]
+ // CHECK-DAG: Return [<<Phi>>]
// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (before)
// CHECK: Goto
@@ -52,10 +52,10 @@
// CHECK-NOT: Goto
// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after)
- // CHECK-DAG: [[Param:z\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[NotParam:z\d+]] BooleanNot [ [[Param]] ]
- // CHECK-DAG: Return [ [[NotParam]] ]
+ // CHECK-DAG: <<Param:z\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<NotParam:z\d+>> BooleanNot [<<Param>>]
+ // CHECK-DAG: Return [<<NotParam>>]
// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after)
// CHECK-NOT: If
@@ -75,22 +75,22 @@
*/
// CHECK-START: boolean Main.GreaterThan(int, int) boolean_simplifier (before)
- // CHECK-DAG: [[ParamX:i\d+]] ParameterValue
- // CHECK-DAG: [[ParamY:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Cond:z\d+]] GreaterThan [ [[ParamX]] [[ParamY]] ]
- // CHECK-DAG: If [ [[Cond]] ]
- // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Const0]] [[Const1]] ]
- // CHECK-DAG: Return [ [[Phi]] ]
+ // CHECK-DAG: <<ParamX:i\d+>> ParameterValue
+ // CHECK-DAG: <<ParamY:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: <<Cond:z\d+>> GreaterThan [<<ParamX>>,<<ParamY>>]
+ // CHECK-DAG: If [<<Cond>>]
+ // CHECK-DAG: <<Phi:i\d+>> Phi [<<Const0>>,<<Const1>>]
+ // CHECK-DAG: Return [<<Phi>>]
// CHECK-START: boolean Main.GreaterThan(int, int) boolean_simplifier (after)
- // CHECK-DAG: [[ParamX:i\d+]] ParameterValue
- // CHECK-DAG: [[ParamY:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Cond:z\d+]] GreaterThan [ [[ParamX]] [[ParamY]] ]
- // CHECK-DAG: Return [ [[Cond]] ]
+ // CHECK-DAG: <<ParamX:i\d+>> ParameterValue
+ // CHECK-DAG: <<ParamY:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: <<Cond:z\d+>> GreaterThan [<<ParamX>>,<<ParamY>>]
+ // CHECK-DAG: Return [<<Cond>>]
public static boolean GreaterThan(int x, int y) {
return (x <= y) ? false : true;
@@ -102,22 +102,22 @@
*/
// CHECK-START: boolean Main.LessThan(int, int) boolean_simplifier (before)
- // CHECK-DAG: [[ParamX:i\d+]] ParameterValue
- // CHECK-DAG: [[ParamY:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Cond:z\d+]] GreaterThanOrEqual [ [[ParamX]] [[ParamY]] ]
- // CHECK-DAG: If [ [[Cond]] ]
- // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Const1]] [[Const0]] ]
- // CHECK-DAG: Return [ [[Phi]] ]
+ // CHECK-DAG: <<ParamX:i\d+>> ParameterValue
+ // CHECK-DAG: <<ParamY:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: <<Cond:z\d+>> GreaterThanOrEqual [<<ParamX>>,<<ParamY>>]
+ // CHECK-DAG: If [<<Cond>>]
+ // CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,<<Const0>>]
+ // CHECK-DAG: Return [<<Phi>>]
// CHECK-START: boolean Main.LessThan(int, int) boolean_simplifier (after)
- // CHECK-DAG: [[ParamX:i\d+]] ParameterValue
- // CHECK-DAG: [[ParamY:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Cond:z\d+]] LessThan [ [[ParamX]] [[ParamY]] ]
- // CHECK-DAG: Return [ [[Cond]] ]
+ // CHECK-DAG: <<ParamX:i\d+>> ParameterValue
+ // CHECK-DAG: <<ParamY:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: <<Cond:z\d+>> LessThan [<<ParamX>>,<<ParamY>>]
+ // CHECK-DAG: Return [<<Cond>>]
// CHECK-START: boolean Main.LessThan(int, int) boolean_simplifier (after)
// CHECK-NOT: GreaterThanOrEqual
@@ -132,51 +132,51 @@
*/
// CHECK-START: boolean Main.ValuesOrdered(int, int, int) boolean_simplifier (before)
- // CHECK-DAG: [[ParamX:i\d+]] ParameterValue
- // CHECK-DAG: [[ParamY:i\d+]] ParameterValue
- // CHECK-DAG: [[ParamZ:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[CondXY:z\d+]] GreaterThan [ [[ParamX]] [[ParamY]] ]
- // CHECK-DAG: If [ [[CondXY]] ]
- // CHECK-DAG: [[CondYZ:z\d+]] GreaterThan [ [[ParamY]] [[ParamZ]] ]
- // CHECK-DAG: If [ [[CondYZ]] ]
- // CHECK-DAG: [[CondXYZ:z\d+]] NotEqual [ [[PhiXY:i\d+]] [[PhiYZ:i\d+]] ]
- // CHECK-DAG: If [ [[CondXYZ]] ]
- // CHECK-DAG: Return [ [[PhiXYZ:i\d+]] ]
- // CHECK-DAG: [[PhiXY]] Phi [ [[Const1]] [[Const0]] ]
- // CHECK-DAG: [[PhiYZ]] Phi [ [[Const1]] [[Const0]] ]
- // CHECK-DAG: [[PhiXYZ]] Phi [ [[Const1]] [[Const0]] ]
+ // CHECK-DAG: <<ParamX:i\d+>> ParameterValue
+ // CHECK-DAG: <<ParamY:i\d+>> ParameterValue
+ // CHECK-DAG: <<ParamZ:i\d+>> ParameterValue
+ // CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ // CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ // CHECK-DAG: <<CondXY:z\d+>> GreaterThan [<<ParamX>>,<<ParamY>>]
+ // CHECK-DAG: If [<<CondXY>>]
+ // CHECK-DAG: <<CondYZ:z\d+>> GreaterThan [<<ParamY>>,<<ParamZ>>]
+ // CHECK-DAG: If [<<CondYZ>>]
+ // CHECK-DAG: <<CondXYZ:z\d+>> NotEqual [<<PhiXY:i\d+>>,<<PhiYZ:i\d+>>]
+ // CHECK-DAG: If [<<CondXYZ>>]
+ // CHECK-DAG: Return [<<PhiXYZ:i\d+>>]
+ // CHECK-DAG: <<PhiXY>> Phi [<<Const1>>,<<Const0>>]
+ // CHECK-DAG: <<PhiYZ>> Phi [<<Const1>>,<<Const0>>]
+ // CHECK-DAG: <<PhiXYZ>> Phi [<<Const1>>,<<Const0>>]
// CHECK-START: boolean Main.ValuesOrdered(int, int, int) boolean_simplifier (after)
- // CHECK-DAG: [[ParamX:i\d+]] ParameterValue
- // CHECK-DAG: [[ParamY:i\d+]] ParameterValue
- // CHECK-DAG: [[ParamZ:i\d+]] ParameterValue
- // CHECK-DAG: [[CmpXY:z\d+]] LessThanOrEqual [ [[ParamX]] [[ParamY]] ]
- // CHECK-DAG: [[CmpYZ:z\d+]] LessThanOrEqual [ [[ParamY]] [[ParamZ]] ]
- // CHECK-DAG: [[CmpXYZ:z\d+]] Equal [ [[CmpXY]] [[CmpYZ]] ]
- // CHECK-DAG: Return [ [[CmpXYZ]] ]
+ // CHECK-DAG: <<ParamX:i\d+>> ParameterValue
+ // CHECK-DAG: <<ParamY:i\d+>> ParameterValue
+ // CHECK-DAG: <<ParamZ:i\d+>> ParameterValue
+ // CHECK-DAG: <<CmpXY:z\d+>> LessThanOrEqual [<<ParamX>>,<<ParamY>>]
+ // CHECK-DAG: <<CmpYZ:z\d+>> LessThanOrEqual [<<ParamY>>,<<ParamZ>>]
+ // CHECK-DAG: <<CmpXYZ:z\d+>> Equal [<<CmpXY>>,<<CmpYZ>>]
+ // CHECK-DAG: Return [<<CmpXYZ>>]
public static boolean ValuesOrdered(int x, int y, int z) {
return (x <= y) == (y <= z);
}
// CHECK-START: int Main.NegatedCondition(boolean) boolean_simplifier (before)
- // CHECK-DAG: [[Param:z\d+]] ParameterValue
- // CHECK-DAG: [[Const42:i\d+]] IntConstant 42
- // CHECK-DAG: [[Const43:i\d+]] IntConstant 43
- // CHECK-DAG: [[NotParam:z\d+]] BooleanNot [ [[Param]] ]
- // CHECK-DAG: If [ [[NotParam]] ]
- // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Const42]] [[Const43]] ]
- // CHECK-DAG: Return [ [[Phi]] ]
+ // CHECK-DAG: <<Param:z\d+>> ParameterValue
+ // CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ // CHECK-DAG: <<Const43:i\d+>> IntConstant 43
+ // CHECK-DAG: <<NotParam:z\d+>> BooleanNot [<<Param>>]
+ // CHECK-DAG: If [<<NotParam>>]
+ // CHECK-DAG: <<Phi:i\d+>> Phi [<<Const42>>,<<Const43>>]
+ // CHECK-DAG: Return [<<Phi>>]
// CHECK-START: int Main.NegatedCondition(boolean) boolean_simplifier (after)
- // CHECK-DAG: [[Param:z\d+]] ParameterValue
- // CHECK-DAG: [[Const42:i\d+]] IntConstant 42
- // CHECK-DAG: [[Const43:i\d+]] IntConstant 43
- // CHECK-DAG: If [ [[Param]] ]
- // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Const42]] [[Const43]] ]
- // CHECK-DAG: Return [ [[Phi]] ]
+ // CHECK-DAG: <<Param:z\d+>> ParameterValue
+ // CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ // CHECK-DAG: <<Const43:i\d+>> IntConstant 43
+ // CHECK-DAG: If [<<Param>>]
+ // CHECK-DAG: <<Phi:i\d+>> Phi [<<Const42>>,<<Const43>>]
+ // CHECK-DAG: Return [<<Phi>>]
// Note: The fact that branches are swapped is verified by running the test.
diff --git a/test/464-checker-inline-sharpen-calls/src/Main.java b/test/464-checker-inline-sharpen-calls/src/Main.java
index 1b25b42..e451f70 100644
--- a/test/464-checker-inline-sharpen-calls/src/Main.java
+++ b/test/464-checker-inline-sharpen-calls/src/Main.java
@@ -20,7 +20,7 @@
}
// CHECK-START: void Main.inlineSharpenInvokeVirtual(Main) inliner (before)
- // CHECK-DAG: [[Invoke:v\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: <<Invoke:v\d+>> InvokeStaticOrDirect
// CHECK-DAG: ReturnVoid
// CHECK-START: void Main.inlineSharpenInvokeVirtual(Main) inliner (after)
@@ -31,15 +31,15 @@
}
// CHECK-START: int Main.inlineSharpenStringInvoke() inliner (before)
- // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ // CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect
+ // CHECK-DAG: Return [<<Invoke>>]
// CHECK-START: int Main.inlineSharpenStringInvoke() inliner (after)
// CHECK-NOT: InvokeStaticOrDirect
// CHECK-START: int Main.inlineSharpenStringInvoke() inliner (after)
- // CHECK-DAG: [[Field:i\d+]] InstanceFieldGet
- // CHECK-DAG: Return [ [[Field]] ]
+ // CHECK-DAG: <<Field:i\d+>> InstanceFieldGet
+ // CHECK-DAG: Return [<<Field>>]
public static int inlineSharpenStringInvoke() {
return "Foo".length();
diff --git a/test/465-checker-clinit-gvn/src/Main.java b/test/465-checker-clinit-gvn/src/Main.java
index dcaef6f..ac2863c 100644
--- a/test/465-checker-clinit-gvn/src/Main.java
+++ b/test/465-checker-clinit-gvn/src/Main.java
@@ -27,14 +27,14 @@
public final class Main {
// CHECK-START: int Main.accessTwoStatics() GVN (before)
- // CHECK-DAG: [[Class1:l\d+]] LoadClass
- // CHECK-DAG: ClinitCheck [ [[Class1]] ]
- // CHECK-DAG: [[Class2:l\d+]] LoadClass
- // CHECK-DAG: ClinitCheck [ [[Class2]] ]
+ // CHECK-DAG: <<Class1:l\d+>> LoadClass
+ // CHECK-DAG: ClinitCheck [<<Class1>>]
+ // CHECK-DAG: <<Class2:l\d+>> LoadClass
+ // CHECK-DAG: ClinitCheck [<<Class2>>]
// CHECK-START: int Main.accessTwoStatics() GVN (after)
- // CHECK-DAG: [[Class:l\d+]] LoadClass
- // CHECK-DAG: ClinitCheck [ [[Class]] ]
+ // CHECK-DAG: <<Class:l\d+>> LoadClass
+ // CHECK-DAG: ClinitCheck [<<Class>>]
// CHECK-NOT: ClinitCheck
public static int accessTwoStatics() {
@@ -42,14 +42,14 @@
}
// CHECK-START: int Main.accessTwoStaticsCallInBetween() GVN (before)
- // CHECK-DAG: [[Class1:l\d+]] LoadClass
- // CHECK-DAG: ClinitCheck [ [[Class1]] ]
- // CHECK-DAG: [[Class2:l\d+]] LoadClass
- // CHECK-DAG: ClinitCheck [ [[Class2]] ]
+ // CHECK-DAG: <<Class1:l\d+>> LoadClass
+ // CHECK-DAG: ClinitCheck [<<Class1>>]
+ // CHECK-DAG: <<Class2:l\d+>> LoadClass
+ // CHECK-DAG: ClinitCheck [<<Class2>>]
// CHECK-START: int Main.accessTwoStaticsCallInBetween() GVN (after)
- // CHECK-DAG: [[Class:l\d+]] LoadClass
- // CHECK-DAG: ClinitCheck [ [[Class]] ]
+ // CHECK-DAG: <<Class:l\d+>> LoadClass
+ // CHECK-DAG: ClinitCheck [<<Class>>]
// CHECK-NOT: ClinitCheck
public static int accessTwoStaticsCallInBetween() {
diff --git a/test/468-checker-bool-simplifier-regression/smali/TestCase.smali b/test/468-checker-bool-simplifier-regression/smali/TestCase.smali
index f36304d..33e6dc3 100644
--- a/test/468-checker-bool-simplifier-regression/smali/TestCase.smali
+++ b/test/468-checker-bool-simplifier-regression/smali/TestCase.smali
@@ -18,6 +18,19 @@
.field public static value:Z
+# CHECK-START: boolean TestCase.testCase() boolean_simplifier (before)
+# CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+# CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+# CHECK-DAG: <<Value:z\d+>> StaticFieldGet
+# CHECK-DAG: If [<<Value>>]
+# CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,<<Const0>>]
+# CHECK-DAG: Return [<<Phi>>]
+
+# CHECK-START: boolean TestCase.testCase() boolean_simplifier (after)
+# CHECK-DAG: <<Value:z\d+>> StaticFieldGet
+# CHECK-DAG: <<Not:z\d+>> BooleanNot [<<Value>>]
+# CHECK-DAG: Return [<<Not>>]
+
.method public static testCase()Z
.registers 2
sget-boolean v0, LTestCase;->value:Z
diff --git a/test/468-checker-bool-simplifier-regression/src/Main.java b/test/468-checker-bool-simplifier-regression/src/Main.java
index d45f3bf..8fe05c7 100644
--- a/test/468-checker-bool-simplifier-regression/src/Main.java
+++ b/test/468-checker-bool-simplifier-regression/src/Main.java
@@ -18,19 +18,6 @@
public class Main {
- // CHECK-START: boolean TestCase.testCase() boolean_simplifier (before)
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: [[Value:z\d+]] StaticFieldGet
- // CHECK-DAG: If [ [[Value]] ]
- // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Const1]] [[Const0]] ]
- // CHECK-DAG: Return [ [[Phi]] ]
-
- // CHECK-START: boolean TestCase.testCase() boolean_simplifier (after)
- // CHECK-DAG: [[Value:z\d+]] StaticFieldGet
- // CHECK-DAG: [[Not:z\d+]] BooleanNot [ [[Value]] ]
- // CHECK-DAG: Return [ [[Not]] ]
-
public static boolean runTest(boolean input) throws Exception {
Class<?> c = Class.forName("TestCase");
Method m = c.getMethod("testCase");
diff --git a/test/474-checker-boolean-input/src/Main.java b/test/474-checker-boolean-input/src/Main.java
index 9151986..490f7f9 100644
--- a/test/474-checker-boolean-input/src/Main.java
+++ b/test/474-checker-boolean-input/src/Main.java
@@ -28,8 +28,8 @@
*/
// CHECK-START: boolean Main.TestPhiAsBoolean(int) boolean_simplifier (after)
- // CHECK-DAG: [[Phi:i\d+]] Phi
- // CHECK-DAG: BooleanNot [ [[Phi]] ]
+ // CHECK-DAG: <<Phi:i\d+>> Phi
+ // CHECK-DAG: BooleanNot [<<Phi>>]
public static boolean f1;
public static boolean f2;
@@ -48,8 +48,8 @@
*/
// CHECK-START: boolean Main.TestAndAsBoolean(boolean, boolean) boolean_simplifier (after)
- // CHECK-DAG: [[And:i\d+]] And
- // CHECK-DAG: BooleanNot [ [[And]] ]
+ // CHECK-DAG: <<And:i\d+>> And
+ // CHECK-DAG: BooleanNot [<<And>>]
public static boolean InlineAnd(boolean x, boolean y) {
return x & y;
@@ -65,8 +65,8 @@
*/
// CHECK-START: boolean Main.TestOrAsBoolean(boolean, boolean) boolean_simplifier (after)
- // CHECK-DAG: [[Or:i\d+]] Or
- // CHECK-DAG: BooleanNot [ [[Or]] ]
+ // CHECK-DAG: <<Or:i\d+>> Or
+ // CHECK-DAG: BooleanNot [<<Or>>]
public static boolean InlineOr(boolean x, boolean y) {
return x | y;
@@ -82,8 +82,8 @@
*/
// CHECK-START: boolean Main.TestXorAsBoolean(boolean, boolean) boolean_simplifier (after)
- // CHECK-DAG: [[Xor:i\d+]] Xor
- // CHECK-DAG: BooleanNot [ [[Xor]] ]
+ // CHECK-DAG: <<Xor:i\d+>> Xor
+ // CHECK-DAG: BooleanNot [<<Xor>>]
public static boolean InlineXor(boolean x, boolean y) {
return x ^ y;
diff --git a/test/476-checker-ctor-memory-barrier/src/Main.java b/test/476-checker-ctor-memory-barrier/src/Main.java
index 10aa2ab..f24dc4a 100644
--- a/test/476-checker-ctor-memory-barrier/src/Main.java
+++ b/test/476-checker-ctor-memory-barrier/src/Main.java
@@ -14,10 +14,11 @@
* limitations under the License.
*/
+// TODO: Add more tests after we can inline functions with calls.
class ClassWithoutFinals {
// CHECK-START: void ClassWithoutFinals.<init>() register (after)
- // CHECK-NOT: MemoryBarrier {{StoreStore}}
+ // CHECK-NOT: MemoryBarrier kind:StoreStore
public ClassWithoutFinals() {}
}
@@ -26,9 +27,8 @@
public ClassWithFinals obj;
// CHECK-START: void ClassWithFinals.<init>(boolean) register (after)
- // CHECK: MemoryBarrier {{StoreStore}}
- // CHECK-NOT: {{.*}}
- // CHECK: ReturnVoid
+ // CHECK: MemoryBarrier kind:StoreStore
+ // CHECK-NEXT: ReturnVoid
public ClassWithFinals(boolean cond) {
x = 0;
if (cond) {
@@ -38,18 +38,16 @@
}
// CHECK-START: void ClassWithFinals.<init>() register (after)
- // CHECK: MemoryBarrier {{StoreStore}}
- // CHECK-NOT: {{.*}}
- // CHECK: ReturnVoid
+ // CHECK: MemoryBarrier kind:StoreStore
+ // CHECK-NEXT: ReturnVoid
public ClassWithFinals() {
x = 0;
}
// CHECK-START: void ClassWithFinals.<init>(int) register (after)
- // CHECK: MemoryBarrier {{StoreStore}}
- // CHECK: MemoryBarrier {{StoreStore}}
- // CHECK-NOT: {{.*}}
- // CHECK: ReturnVoid
+ // CHECK: MemoryBarrier kind:StoreStore
+ // CHECK: MemoryBarrier kind:StoreStore
+ // CHECK-NEXT: ReturnVoid
public ClassWithFinals(int x) {
// This should have two barriers:
// - one for the constructor
@@ -61,87 +59,135 @@
class InheritFromClassWithFinals extends ClassWithFinals {
// CHECK-START: void InheritFromClassWithFinals.<init>() register (after)
- // CHECK: MemoryBarrier {{StoreStore}}
- // CHECK-NOT: {{.*}}
- // CHECK: ReturnVoid
+ // CHECK: MemoryBarrier kind:StoreStore
+ // CHECK-NEXT: ReturnVoid
// CHECK-START: void InheritFromClassWithFinals.<init>() register (after)
- // CHECK-NOT: InvokeStaticOrDirect
+ // CHECK-NOT: InvokeStaticOrDirect
public InheritFromClassWithFinals() {
// Should inline the super constructor.
}
// CHECK-START: void InheritFromClassWithFinals.<init>(boolean) register (after)
- // CHECK: InvokeStaticOrDirect
+ // CHECK: InvokeStaticOrDirect
// CHECK-START: void InheritFromClassWithFinals.<init>(boolean) register (after)
- // CHECK-NOT: MemoryBarrier {{StoreStore}}
+ // CHECK-NOT: MemoryBarrier kind:StoreStore
public InheritFromClassWithFinals(boolean cond) {
super(cond);
// should not inline the super constructor
}
+
+ // CHECK-START: void InheritFromClassWithFinals.<init>(int) register (after)
+ // CHECK: MemoryBarrier kind:StoreStore
+ // CHECK: MemoryBarrier kind:StoreStore
+ // CHECK: ReturnVoid
+
+ // CHECK-START: void InheritFromClassWithFinals.<init>(int) register (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+ public InheritFromClassWithFinals(int unused) {
+ // Should inline the super constructor and insert a memory barrier.
+
+ // Should inline the new instance call and insert another memory barrier.
+ new InheritFromClassWithFinals();
+ }
}
class HaveFinalsAndInheritFromClassWithFinals extends ClassWithFinals {
final int y;
// CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>() register (after)
- // CHECK: MemoryBarrier {{StoreStore}}
- // CHECK: MemoryBarrier {{StoreStore}}
- // CHECK-NOT: {{.*}}
- // CHECK: ReturnVoid
+ // CHECK: MemoryBarrier kind:StoreStore
+ // CHECK-NEXT: ReturnVoid
// CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>() register (after)
// CHECK-NOT: InvokeStaticOrDirect
public HaveFinalsAndInheritFromClassWithFinals() {
- // Should inline the super constructor.
+ // Should inline the super constructor and remove the memory barrier.
y = 0;
}
// CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>(boolean) register (after)
- // CHECK: InvokeStaticOrDirect
- // CHECK: MemoryBarrier {{StoreStore}}
- // CHECK-NOT: {{.*}}
- // CHECK: ReturnVoid
+ // CHECK: InvokeStaticOrDirect
+ // CHECK: MemoryBarrier kind:StoreStore
+ // CHECK-NEXT: ReturnVoid
public HaveFinalsAndInheritFromClassWithFinals(boolean cond) {
super(cond);
// should not inline the super constructor
y = 0;
}
+
+ // CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>(int) register (after)
+ // CHECK: MemoryBarrier kind:StoreStore
+ // CHECK: MemoryBarrier kind:StoreStore
+ // CHECK: MemoryBarrier kind:StoreStore
+ // CHECK-NEXT: ReturnVoid
+
+ // CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>(int) register (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+ public HaveFinalsAndInheritFromClassWithFinals(int unused) {
+ // Should inline the super constructor and keep just one memory barrier.
+ y = 0;
+
+ // Should inline new instance and keep one barrier.
+ new HaveFinalsAndInheritFromClassWithFinals();
+ // Should inline new instance and keep one barrier.
+ new InheritFromClassWithFinals();
+ }
}
public class Main {
// CHECK-START: ClassWithFinals Main.noInlineNoConstructorBarrier() register (after)
- // CHECK: InvokeStaticOrDirect
+ // CHECK: InvokeStaticOrDirect
// CHECK-START: ClassWithFinals Main.noInlineNoConstructorBarrier() register (after)
- // CHECK-NOT: MemoryBarrier {{StoreStore}}
+ // CHECK-NOT: MemoryBarrier kind:StoreStore
public static ClassWithFinals noInlineNoConstructorBarrier() {
return new ClassWithFinals(false);
}
- // CHECK-START: ClassWithFinals Main.inlineConstructorBarrier() register (after)
- // CHECK: MemoryBarrier {{StoreStore}}
- // CHECK-NOT: {{.*}}
- // CHECK: Return
+ // CHECK-START: void Main.inlineNew() register (after)
+ // CHECK: MemoryBarrier kind:StoreStore
+ // CHECK-NEXT: ReturnVoid
- // CHECK-START: ClassWithFinals Main.inlineConstructorBarrier() register (after)
- // CHECK-NOT: InvokeStaticOrDirect
- public static ClassWithFinals inlineConstructorBarrier() {
- return new ClassWithFinals();
+ // CHECK-START: void Main.inlineNew() register (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+ public static void inlineNew() {
+ new ClassWithFinals();
}
- // CHECK-START: InheritFromClassWithFinals Main.doubleInlineConstructorBarrier() register (after)
- // CHECK: MemoryBarrier {{StoreStore}}
- // CHECK-NOT: {{.*}}
- // CHECK: Return
+ // CHECK-START: void Main.inlineNew1() register (after)
+ // CHECK: MemoryBarrier kind:StoreStore
+ // CHECK-NEXT: ReturnVoid
- // CHECK-START: InheritFromClassWithFinals Main.doubleInlineConstructorBarrier() register (after)
- // CHECK-NOT: InvokeStaticOrDirect
- public static InheritFromClassWithFinals doubleInlineConstructorBarrier() {
- return new InheritFromClassWithFinals();
+ // CHECK-START: void Main.inlineNew1() register (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+ public static void inlineNew1() {
+ new InheritFromClassWithFinals();
}
- public static void main(String[] args) { }
+ // CHECK-START: void Main.inlineNew2() register (after)
+ // CHECK: MemoryBarrier kind:StoreStore
+ // CHECK-NEXT: ReturnVoid
+
+ // CHECK-START: void Main.inlineNew2() register (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+ public static void inlineNew2() {
+ new HaveFinalsAndInheritFromClassWithFinals();
+ }
+
+ // CHECK-START: void Main.inlineNew3() register (after)
+ // CHECK: MemoryBarrier kind:StoreStore
+ // CHECK: MemoryBarrier kind:StoreStore
+ // CHECK-NEXT: ReturnVoid
+
+ // CHECK-START: void Main.inlineNew3() register (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+ public static void inlineNew3() {
+ new HaveFinalsAndInheritFromClassWithFinals();
+ new HaveFinalsAndInheritFromClassWithFinals();
+ }
+
+ public static void main(String[] args) {}
}
diff --git a/test/478-checker-clinit-check-pruning/src/Main.java b/test/478-checker-clinit-check-pruning/src/Main.java
index 6da8945..e8739b8 100644
--- a/test/478-checker-clinit-check-pruning/src/Main.java
+++ b/test/478-checker-clinit-check-pruning/src/Main.java
@@ -24,13 +24,13 @@
*/
// CHECK-START: void Main.invokeStaticInlined() builder (after)
- // CHECK-DAG: [[LoadClass:l\d+]] LoadClass
- // CHECK-DAG: [[ClinitCheck:l\d+]] ClinitCheck [ [[LoadClass]] ]
- // CHECK-DAG: InvokeStaticOrDirect [ [[ClinitCheck]] ]
+ // CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false
+ // CHECK-DAG: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>]
+ // CHECK-DAG: InvokeStaticOrDirect [<<ClinitCheck>>]
// CHECK-START: void Main.invokeStaticInlined() inliner (after)
- // CHECK-DAG: [[LoadClass:l\d+]] LoadClass
- // CHECK-DAG: [[ClinitCheck:l\d+]] ClinitCheck [ [[LoadClass]] ]
+ // CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false
+ // CHECK-DAG: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>]
// CHECK-START: void Main.invokeStaticInlined() inliner (after)
// CHECK-NOT: InvokeStaticOrDirect
@@ -42,7 +42,7 @@
// CFG as it is before the next pass (liveness analysis) instead.
// CHECK-START: void Main.invokeStaticInlined() liveness (before)
- // CHECK-DAG: LoadClass
+ // CHECK-DAG: LoadClass gen_clinit_check:true
// CHECK-START: void Main.invokeStaticInlined() liveness (before)
// CHECK-NOT: ClinitCheck
@@ -67,14 +67,14 @@
*/
// CHECK-START: void Main.invokeStaticNotInlined() builder (after)
- // CHECK-DAG: [[LoadClass:l\d+]] LoadClass
- // CHECK-DAG: [[ClinitCheck:l\d+]] ClinitCheck [ [[LoadClass]] ]
- // CHECK-DAG: InvokeStaticOrDirect [ [[ClinitCheck]] ]
+ // CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false
+ // CHECK-DAG: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>]
+ // CHECK-DAG: InvokeStaticOrDirect [<<ClinitCheck>>]
// CHECK-START: void Main.invokeStaticNotInlined() inliner (after)
- // CHECK-DAG: [[LoadClass:l\d+]] LoadClass
- // CHECK-DAG: [[ClinitCheck:l\d+]] ClinitCheck [ [[LoadClass]] ]
- // CHECK-DAG: InvokeStaticOrDirect [ [[ClinitCheck]] ]
+ // CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false
+ // CHECK-DAG: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>]
+ // CHECK-DAG: InvokeStaticOrDirect [<<ClinitCheck>>]
// The following checks ensure the clinit check and load class
// instructions added by the builder are pruned by the
@@ -260,6 +260,44 @@
}
}
+
+ /*
+ * Verify that if we have a static call immediately after the load class
+ * we don't do generate a clinit check.
+ */
+
+ // CHECK-START: void Main.noClinitBecauseOfInvokeStatic() liveness (before)
+ // CHECK-DAG: <<IntConstant:i\d+>> IntConstant 0
+ // CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false
+ // CHECK-DAG: InvokeStaticOrDirect
+ // CHECK-DAG: StaticFieldSet [<<LoadClass>>,<<IntConstant>>]
+
+ // CHECK-START: void Main.noClinitBecauseOfInvokeStatic() liveness (before)
+ // CHECK-NOT: ClinitCheck
+
+ static void noClinitBecauseOfInvokeStatic() {
+ ClassWithClinit2.staticMethod();
+ ClassWithClinit2.doThrow = false;
+ }
+
+ /*
+ * Verify that if the static call is after a field access, the load class
+ * will generate a clinit check.
+ */
+
+ // CHECK-START: void Main.clinitBecauseOfFieldAccess() liveness (before)
+ // CHECK-DAG: <<IntConstant:i\d+>> IntConstant 0
+ // CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:true
+ // CHECK-DAG: StaticFieldSet [<<LoadClass>>,<<IntConstant>>]
+ // CHECK-DAG: InvokeStaticOrDirect
+
+ // CHECK-START: void Main.clinitBecauseOfFieldAccess() liveness (before)
+ // CHECK-NOT: ClinitCheck
+ static void clinitBecauseOfFieldAccess() {
+ ClassWithClinit2.doThrow = false;
+ ClassWithClinit2.staticMethod();
+ }
+
// TODO: Add a test for the case of a static method whose declaring
// class type index is not available (i.e. when `storage_index`
// equals `DexFile::kDexNoIndex` in
diff --git a/test/480-checker-dead-blocks/src/Main.java b/test/480-checker-dead-blocks/src/Main.java
index 83dbb26..b76755e 100644
--- a/test/480-checker-dead-blocks/src/Main.java
+++ b/test/480-checker-dead-blocks/src/Main.java
@@ -31,19 +31,19 @@
}
// CHECK-START: int Main.testTrueBranch(int, int) dead_code_elimination_final (before)
- // CHECK-DAG: [[ArgX:i\d+]] ParameterValue
- // CHECK-DAG: [[ArgY:i\d+]] ParameterValue
+ // CHECK-DAG: <<ArgX:i\d+>> ParameterValue
+ // CHECK-DAG: <<ArgY:i\d+>> ParameterValue
// CHECK-DAG: If
- // CHECK-DAG: [[Add:i\d+]] Add [ [[ArgX]] [[ArgY]] ]
- // CHECK-DAG: [[Sub:i\d+]] Sub [ [[ArgX]] [[ArgY]] ]
- // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Add]] [[Sub]] ]
- // CHECK-DAG: Return [ [[Phi]] ]
+ // CHECK-DAG: <<Add:i\d+>> Add [<<ArgX>>,<<ArgY>>]
+ // CHECK-DAG: <<Sub:i\d+>> Sub [<<ArgX>>,<<ArgY>>]
+ // CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Sub>>]
+ // CHECK-DAG: Return [<<Phi>>]
// CHECK-START: int Main.testTrueBranch(int, int) dead_code_elimination_final (after)
- // CHECK-DAG: [[ArgX:i\d+]] ParameterValue
- // CHECK-DAG: [[ArgY:i\d+]] ParameterValue
- // CHECK-DAG: [[Add:i\d+]] Add [ [[ArgX]] [[ArgY]] ]
- // CHECK-DAG: Return [ [[Add]] ]
+ // CHECK-DAG: <<ArgX:i\d+>> ParameterValue
+ // CHECK-DAG: <<ArgY:i\d+>> ParameterValue
+ // CHECK-DAG: <<Add:i\d+>> Add [<<ArgX>>,<<ArgY>>]
+ // CHECK-DAG: Return [<<Add>>]
// CHECK-START: int Main.testTrueBranch(int, int) dead_code_elimination_final (after)
// CHECK-NOT: If
@@ -61,19 +61,19 @@
}
// CHECK-START: int Main.testFalseBranch(int, int) dead_code_elimination_final (before)
- // CHECK-DAG: [[ArgX:i\d+]] ParameterValue
- // CHECK-DAG: [[ArgY:i\d+]] ParameterValue
+ // CHECK-DAG: <<ArgX:i\d+>> ParameterValue
+ // CHECK-DAG: <<ArgY:i\d+>> ParameterValue
// CHECK-DAG: If
- // CHECK-DAG: [[Add:i\d+]] Add [ [[ArgX]] [[ArgY]] ]
- // CHECK-DAG: [[Sub:i\d+]] Sub [ [[ArgX]] [[ArgY]] ]
- // CHECK-DAG: [[Phi:i\d+]] Phi [ [[Add]] [[Sub]] ]
- // CHECK-DAG: Return [ [[Phi]] ]
+ // CHECK-DAG: <<Add:i\d+>> Add [<<ArgX>>,<<ArgY>>]
+ // CHECK-DAG: <<Sub:i\d+>> Sub [<<ArgX>>,<<ArgY>>]
+ // CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Sub>>]
+ // CHECK-DAG: Return [<<Phi>>]
// CHECK-START: int Main.testFalseBranch(int, int) dead_code_elimination_final (after)
- // CHECK-DAG: [[ArgX:i\d+]] ParameterValue
- // CHECK-DAG: [[ArgY:i\d+]] ParameterValue
- // CHECK-DAG: [[Sub:i\d+]] Sub [ [[ArgX]] [[ArgY]] ]
- // CHECK-DAG: Return [ [[Sub]] ]
+ // CHECK-DAG: <<ArgX:i\d+>> ParameterValue
+ // CHECK-DAG: <<ArgY:i\d+>> ParameterValue
+ // CHECK-DAG: <<Sub:i\d+>> Sub [<<ArgX>>,<<ArgY>>]
+ // CHECK-DAG: Return [<<Sub>>]
// CHECK-START: int Main.testFalseBranch(int, int) dead_code_elimination_final (after)
// CHECK-NOT: If
@@ -125,8 +125,8 @@
// CHECK-DAG: Add
// CHECK-START: int Main.testDeadLoop(int) dead_code_elimination_final (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: Return [<<Arg>>]
// CHECK-START: int Main.testDeadLoop(int) dead_code_elimination_final (after)
// CHECK-NOT: If
@@ -145,8 +145,8 @@
// CHECK-DAG: Add
// CHECK-START: int Main.testUpdateLoopInformation(int) dead_code_elimination_final (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ // CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ // CHECK-DAG: Return [<<Arg>>]
// CHECK-START: int Main.testUpdateLoopInformation(int) dead_code_elimination_final (after)
// CHECK-NOT: If
diff --git a/test/482-checker-loop-back-edge-use/src/Main.java b/test/482-checker-loop-back-edge-use/src/Main.java
index 74184e8..0ed9267 100644
--- a/test/482-checker-loop-back-edge-use/src/Main.java
+++ b/test/482-checker-loop-back-edge-use/src/Main.java
@@ -18,16 +18,16 @@
public class Main {
// CHECK-START: void Main.loop1(boolean) liveness (after)
- // CHECK: ParameterValue (liveness: 2 ranges: { [2, 22) }, uses: { 17 22 }
- // CHECK: Goto (liveness: 20)
+ // CHECK: ParameterValue liveness:2 ranges:{[2,22)} uses:[17,22]
+ // CHECK: Goto liveness:20
public static void loop1(boolean incoming) {
while (incoming) {}
}
// CHECK-START: void Main.loop2(boolean) liveness (after)
- // CHECK: ParameterValue (liveness: 2 ranges: { [2, 42) }, uses: { 33 38 42 }
- // CHECK: Goto (liveness: 36)
- // CHECK: Goto (liveness: 40)
+ // CHECK: ParameterValue liveness:2 ranges:{[2,42)} uses:[33,38,42]
+ // CHECK: Goto liveness:36
+ // CHECK: Goto liveness:40
public static void loop2(boolean incoming) {
while (true) {
System.out.println("foo");
@@ -36,11 +36,11 @@
}
// CHECK-START: void Main.loop3(boolean) liveness (after)
- // CHECK: ParameterValue (liveness: 2 ranges: { [2, 60) }, uses: { 56 60 }
- // CHECK: Goto (liveness: 58)
+ // CHECK: ParameterValue liveness:2 ranges:{[2,60)} uses:[56,60]
+ // CHECK: Goto liveness:58
// CHECK-START: void Main.loop3(boolean) liveness (after)
- // CHECK-NOT: Goto (liveness: 54)
+ // CHECK-NOT: Goto liveness:54
public static void loop3(boolean incoming) {
// 'incoming' only needs a use at the outer loop's back edge.
while (System.currentTimeMillis() != 42) {
@@ -50,10 +50,10 @@
}
// CHECK-START: void Main.loop4(boolean) liveness (after)
- // CHECK: ParameterValue (liveness: 2 ranges: { [2, 22) }, uses: { 22 }
+ // CHECK: ParameterValue liveness:2 ranges:{[2,22)} uses:[22]
// CHECK-START: void Main.loop4(boolean) liveness (after)
- // CHECK-NOT: Goto (liveness: 20)
+ // CHECK-NOT: Goto liveness:20
public static void loop4(boolean incoming) {
// 'incoming' has no loop use, so should not have back edge uses.
System.out.println(incoming);
@@ -63,9 +63,9 @@
}
// CHECK-START: void Main.loop5(boolean) liveness (after)
- // CHECK: ParameterValue (liveness: 2 ranges: { [2, 50) }, uses: { 33 42 46 50 }
- // CHECK: Goto (liveness: 44)
- // CHECK: Goto (liveness: 48)
+ // CHECK: ParameterValue liveness:2 ranges:{[2,50)} uses:[33,42,46,50]
+ // CHECK: Goto liveness:44
+ // CHECK: Goto liveness:48
public static void loop5(boolean incoming) {
// 'incoming' must have a use at both back edges.
while (Runtime.getRuntime() != null) {
@@ -76,11 +76,11 @@
}
// CHECK-START: void Main.loop6(boolean) liveness (after)
- // CHECK ParameterValue (liveness: 2 ranges: { [2, 46) }, uses: { 24 46 }
- // CHECK: Goto (liveness: 44)
+ // CHECK ParameterValue liveness:2 ranges:{[2,46)} uses:[24,46]
+ // CHECK: Goto liveness:44
// CHECK-START: void Main.loop6(boolean) liveness (after)
- // CHECK-NOT: Goto (liveness: 22)
+ // CHECK-NOT: Goto liveness:22
public static void loop6(boolean incoming) {
// 'incoming' must have a use only at the first loop's back edge.
while (true) {
@@ -90,9 +90,9 @@
}
// CHECK-START: void Main.loop7(boolean) liveness (after)
- // CHECK: ParameterValue (liveness: 2 ranges: { [2, 50) }, uses: { 32 41 46 50 }
- // CHECK: Goto (liveness: 44)
- // CHECK: Goto (liveness: 48)
+ // CHECK: ParameterValue liveness:2 ranges:{[2,50)} uses:[32,41,46,50]
+ // CHECK: Goto liveness:44
+ // CHECK: Goto liveness:48
public static void loop7(boolean incoming) {
// 'incoming' must have a use at both back edges.
while (Runtime.getRuntime() != null) {
@@ -102,9 +102,9 @@
}
// CHECK-START: void Main.loop8() liveness (after)
- // CHECK: StaticFieldGet (liveness: 12 ranges: { [12, 44) }, uses: { 35 40 44 }
- // CHECK: Goto (liveness: 38)
- // CHECK: Goto (liveness: 42)
+ // CHECK: StaticFieldGet liveness:12 ranges:{[12,44)} uses:[35,40,44]
+ // CHECK: Goto liveness:38
+ // CHECK: Goto liveness:42
public static void loop8() {
// 'incoming' must have a use at both back edges.
boolean incoming = field;
@@ -114,8 +114,8 @@
}
// CHECK-START: void Main.loop9() liveness (after)
- // CHECK: StaticFieldGet (liveness: 22 ranges: { [22, 36) }, uses: { 31 36 }
- // CHECK: Goto (liveness: 38)
+ // CHECK: StaticFieldGet liveness:22 ranges:{[22,36)} uses:[31,36]
+ // CHECK: Goto liveness:38
public static void loop9() {
while (Runtime.getRuntime() != null) {
// 'incoming' must only have a use in the inner loop.
diff --git a/test/485-checker-dce-loop-update/smali/TestCase.smali b/test/485-checker-dce-loop-update/smali/TestCase.smali
index 3873ac5..487a5df 100644
--- a/test/485-checker-dce-loop-update/smali/TestCase.smali
+++ b/test/485-checker-dce-loop-update/smali/TestCase.smali
@@ -24,26 +24,26 @@
# CHECK-START: int TestCase.testSingleExit(int, boolean) dead_code_elimination_final (before)
-# CHECK-DAG: [[ArgX:i\d+]] ParameterValue
-# CHECK-DAG: [[ArgY:z\d+]] ParameterValue
-# CHECK-DAG: [[Cst1:i\d+]] IntConstant 1
-# CHECK-DAG: [[Cst5:i\d+]] IntConstant 5
-# CHECK-DAG: [[Cst7:i\d+]] IntConstant 7
-# CHECK-DAG: [[PhiX:i\d+]] Phi [ [[ArgX]] [[Add5:i\d+]] [[Add7:i\d+]] ] loop_header:[[HeaderY:B\d+]]
-# CHECK-DAG: If [ [[ArgY]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: If [ [[Cst1]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: [[Add5]] Add [ [[PhiX]] [[Cst5]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: [[Add7]] Add [ [[PhiX]] [[Cst7]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: Return [ [[PhiX]] ] loop_header:null
+# CHECK-DAG: <<ArgX:i\d+>> ParameterValue
+# CHECK-DAG: <<ArgY:z\d+>> ParameterValue
+# CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
+# CHECK-DAG: <<Cst5:i\d+>> IntConstant 5
+# CHECK-DAG: <<Cst7:i\d+>> IntConstant 7
+# CHECK-DAG: <<PhiX:i\d+>> Phi [<<ArgX>>,<<Add5:i\d+>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>>
+# CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>>
+# CHECK-DAG: If [<<Cst1>>] loop:<<HeaderY>>
+# CHECK-DAG: <<Add5>> Add [<<PhiX>>,<<Cst5>>] loop:<<HeaderY>>
+# CHECK-DAG: <<Add7>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>>
+# CHECK-DAG: Return [<<PhiX>>] loop:none
# CHECK-START: int TestCase.testSingleExit(int, boolean) dead_code_elimination_final (after)
-# CHECK-DAG: [[ArgX:i\d+]] ParameterValue
-# CHECK-DAG: [[ArgY:z\d+]] ParameterValue
-# CHECK-DAG: [[Cst7:i\d+]] IntConstant 7
-# CHECK-DAG: [[PhiX:i\d+]] Phi [ [[ArgX]] [[AddX:i\d+]] ] loop_header:[[HeaderY:B\d+]]
-# CHECK-DAG: If [ [[ArgY]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: [[AddX]] Add [ [[PhiX]] [[Cst7]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: Return [ [[PhiX]] ] loop_header:null
+# CHECK-DAG: <<ArgX:i\d+>> ParameterValue
+# CHECK-DAG: <<ArgY:z\d+>> ParameterValue
+# CHECK-DAG: <<Cst7:i\d+>> IntConstant 7
+# CHECK-DAG: <<PhiX:i\d+>> Phi [<<ArgX>>,<<AddX:i\d+>>] loop:<<HeaderY:B\d+>>
+# CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>>
+# CHECK-DAG: <<AddX>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>>
+# CHECK-DAG: Return [<<PhiX>>] loop:none
.method public static testSingleExit(IZ)I
.registers 3
@@ -74,30 +74,30 @@
# CHECK-START: int TestCase.testMultipleExits(int, boolean, boolean) dead_code_elimination_final (before)
-# CHECK-DAG: [[ArgX:i\d+]] ParameterValue
-# CHECK-DAG: [[ArgY:z\d+]] ParameterValue
-# CHECK-DAG: [[ArgZ:z\d+]] ParameterValue
-# CHECK-DAG: [[Cst1:i\d+]] IntConstant 1
-# CHECK-DAG: [[Cst5:i\d+]] IntConstant 5
-# CHECK-DAG: [[Cst7:i\d+]] IntConstant 7
-# CHECK-DAG: [[PhiX:i\d+]] Phi [ [[ArgX]] [[Add5:i\d+]] [[Add7:i\d+]] ] loop_header:[[HeaderY:B\d+]]
-# CHECK-DAG: If [ [[ArgY]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: If [ [[ArgZ]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: If [ [[Cst1]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: [[Add5]] Add [ [[PhiX]] [[Cst5]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: [[Add7]] Add [ [[PhiX]] [[Cst7]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: Return [ [[PhiX]] ] loop_header:null
+# CHECK-DAG: <<ArgX:i\d+>> ParameterValue
+# CHECK-DAG: <<ArgY:z\d+>> ParameterValue
+# CHECK-DAG: <<ArgZ:z\d+>> ParameterValue
+# CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
+# CHECK-DAG: <<Cst5:i\d+>> IntConstant 5
+# CHECK-DAG: <<Cst7:i\d+>> IntConstant 7
+# CHECK-DAG: <<PhiX:i\d+>> Phi [<<ArgX>>,<<Add5:i\d+>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>>
+# CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>>
+# CHECK-DAG: If [<<ArgZ>>] loop:<<HeaderY>>
+# CHECK-DAG: If [<<Cst1>>] loop:<<HeaderY>>
+# CHECK-DAG: <<Add5>> Add [<<PhiX>>,<<Cst5>>] loop:<<HeaderY>>
+# CHECK-DAG: <<Add7>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>>
+# CHECK-DAG: Return [<<PhiX>>] loop:none
# CHECK-START: int TestCase.testMultipleExits(int, boolean, boolean) dead_code_elimination_final (after)
-# CHECK-DAG: [[ArgX:i\d+]] ParameterValue
-# CHECK-DAG: [[ArgY:z\d+]] ParameterValue
-# CHECK-DAG: [[ArgZ:z\d+]] ParameterValue
-# CHECK-DAG: [[Cst7:i\d+]] IntConstant 7
-# CHECK-DAG: [[PhiX:i\d+]] Phi [ [[ArgX]] [[Add7:i\d+]] ] loop_header:[[HeaderY:B\d+]]
-# CHECK-DAG: If [ [[ArgY]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: [[Add7]] Add [ [[PhiX]] [[Cst7]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: If [ [[ArgZ]] ] loop_header:null
-# CHECK-DAG: Return [ [[PhiX]] ] loop_header:null
+# CHECK-DAG: <<ArgX:i\d+>> ParameterValue
+# CHECK-DAG: <<ArgY:z\d+>> ParameterValue
+# CHECK-DAG: <<ArgZ:z\d+>> ParameterValue
+# CHECK-DAG: <<Cst7:i\d+>> IntConstant 7
+# CHECK-DAG: <<PhiX:i\d+>> Phi [<<ArgX>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>>
+# CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>>
+# CHECK-DAG: <<Add7>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>>
+# CHECK-DAG: If [<<ArgZ>>] loop:none
+# CHECK-DAG: Return [<<PhiX>>] loop:none
.method public static testMultipleExits(IZZ)I
.registers 4
@@ -130,36 +130,36 @@
# CHECK-START: int TestCase.testExitPredecessors(int, boolean, boolean) dead_code_elimination_final (before)
-# CHECK-DAG: [[ArgX:i\d+]] ParameterValue
-# CHECK-DAG: [[ArgY:z\d+]] ParameterValue
-# CHECK-DAG: [[ArgZ:z\d+]] ParameterValue
-# CHECK-DAG: [[Cst1:i\d+]] IntConstant 1
-# CHECK-DAG: [[Cst5:i\d+]] IntConstant 5
-# CHECK-DAG: [[Cst7:i\d+]] IntConstant 7
-# CHECK-DAG: [[Cst9:i\d+]] IntConstant 9
-# CHECK-DAG: [[PhiX1:i\d+]] Phi [ [[ArgX]] [[Add5:i\d+]] [[Add7:i\d+]] ] loop_header:[[HeaderY:B\d+]]
-# CHECK-DAG: If [ [[ArgY]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: If [ [[ArgZ]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: [[Mul9:i\d+]] Mul [ [[PhiX1]] [[Cst9]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: [[PhiX2:i\d+]] Phi [ [[Mul9]] [[PhiX1]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: If [ [[Cst1]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: [[Add5]] Add [ [[PhiX2]] [[Cst5]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: [[Add7]] Add [ [[PhiX1]] [[Cst7]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: Return [ [[PhiX2]] ] loop_header:null
+# CHECK-DAG: <<ArgX:i\d+>> ParameterValue
+# CHECK-DAG: <<ArgY:z\d+>> ParameterValue
+# CHECK-DAG: <<ArgZ:z\d+>> ParameterValue
+# CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
+# CHECK-DAG: <<Cst5:i\d+>> IntConstant 5
+# CHECK-DAG: <<Cst7:i\d+>> IntConstant 7
+# CHECK-DAG: <<Cst9:i\d+>> IntConstant 9
+# CHECK-DAG: <<PhiX1:i\d+>> Phi [<<ArgX>>,<<Add5:i\d+>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>>
+# CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>>
+# CHECK-DAG: If [<<ArgZ>>] loop:<<HeaderY>>
+# CHECK-DAG: <<Mul9:i\d+>> Mul [<<PhiX1>>,<<Cst9>>] loop:<<HeaderY>>
+# CHECK-DAG: <<PhiX2:i\d+>> Phi [<<Mul9>>,<<PhiX1>>] loop:<<HeaderY>>
+# CHECK-DAG: If [<<Cst1>>] loop:<<HeaderY>>
+# CHECK-DAG: <<Add5>> Add [<<PhiX2>>,<<Cst5>>] loop:<<HeaderY>>
+# CHECK-DAG: <<Add7>> Add [<<PhiX1>>,<<Cst7>>] loop:<<HeaderY>>
+# CHECK-DAG: Return [<<PhiX2>>] loop:none
# CHECK-START: int TestCase.testExitPredecessors(int, boolean, boolean) dead_code_elimination_final (after)
-# CHECK-DAG: [[ArgX:i\d+]] ParameterValue
-# CHECK-DAG: [[ArgY:z\d+]] ParameterValue
-# CHECK-DAG: [[ArgZ:z\d+]] ParameterValue
-# CHECK-DAG: [[Cst7:i\d+]] IntConstant 7
-# CHECK-DAG: [[Cst9:i\d+]] IntConstant 9
-# CHECK-DAG: [[PhiX1:i\d+]] Phi [ [[ArgX]] [[Add7:i\d+]] ] loop_header:[[HeaderY:B\d+]]
-# CHECK-DAG: If [ [[ArgY]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: [[Add7]] Add [ [[PhiX1]] [[Cst7]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: If [ [[ArgZ]] ] loop_header:null
-# CHECK-DAG: [[Mul9:i\d+]] Mul [ [[PhiX1]] [[Cst9]] ] loop_header:null
-# CHECK-DAG: [[PhiX2:i\d+]] Phi [ [[Mul9]] [[PhiX1]] ] loop_header:null
-# CHECK-DAG: Return [ [[PhiX2]] ] loop_header:null
+# CHECK-DAG: <<ArgX:i\d+>> ParameterValue
+# CHECK-DAG: <<ArgY:z\d+>> ParameterValue
+# CHECK-DAG: <<ArgZ:z\d+>> ParameterValue
+# CHECK-DAG: <<Cst7:i\d+>> IntConstant 7
+# CHECK-DAG: <<Cst9:i\d+>> IntConstant 9
+# CHECK-DAG: <<PhiX1:i\d+>> Phi [<<ArgX>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>>
+# CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>>
+# CHECK-DAG: <<Add7>> Add [<<PhiX1>>,<<Cst7>>] loop:<<HeaderY>>
+# CHECK-DAG: If [<<ArgZ>>] loop:none
+# CHECK-DAG: <<Mul9:i\d+>> Mul [<<PhiX1>>,<<Cst9>>] loop:none
+# CHECK-DAG: <<PhiX2:i\d+>> Phi [<<Mul9>>,<<PhiX1>>] loop:none
+# CHECK-DAG: Return [<<PhiX2>>] loop:none
.method public static testExitPredecessors(IZZ)I
.registers 4
@@ -197,48 +197,47 @@
# CHECK-START: int TestCase.testInnerLoop(int, boolean, boolean) dead_code_elimination_final (before)
-# CHECK-DAG: [[ArgX:i\d+]] ParameterValue
-# CHECK-DAG: [[ArgY:z\d+]] ParameterValue
-# CHECK-DAG: [[ArgZ:z\d+]] ParameterValue
-# CHECK-DAG: [[Cst0:i\d+]] IntConstant 0
-# CHECK-DAG: [[Cst1:i\d+]] IntConstant 1
-# CHECK-DAG: [[Cst5:i\d+]] IntConstant 5
-# CHECK-DAG: [[Cst7:i\d+]] IntConstant 7
+# CHECK-DAG: <<ArgX:i\d+>> ParameterValue
+# CHECK-DAG: <<ArgY:z\d+>> ParameterValue
+# CHECK-DAG: <<ArgZ:z\d+>> ParameterValue
+# CHECK-DAG: <<Cst0:i\d+>> IntConstant 0
+# CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
+# CHECK-DAG: <<Cst5:i\d+>> IntConstant 5
+# CHECK-DAG: <<Cst7:i\d+>> IntConstant 7
#
-# CHECK-DAG: [[PhiX:i\d+]] Phi [ [[ArgX]] [[Add5:i\d+]] [[Add7:i\d+]] ] loop_header:[[HeaderY:B\d+]]
-# CHECK-DAG: [[PhiZ1:i\d+]] Phi [ [[ArgZ]] [[XorZ:i\d+]] [[PhiZ1]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: If [ [[ArgY]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: <<PhiX:i\d+>> Phi [<<ArgX>>,<<Add5:i\d+>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>>
+# CHECK-DAG: <<PhiZ1:i\d+>> Phi [<<ArgZ>>,<<XorZ:i\d+>>,<<PhiZ1>>] loop:<<HeaderY>>
+# CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>>
#
# ### Inner loop ###
-# CHECK-DAG: [[PhiZ2:i\d+]] Phi [ [[PhiZ1]] [[XorZ]] ] loop_header:[[HeaderZ:B\d+]]
-# CHECK-DAG: [[XorZ]] Xor [ [[PhiZ2]] [[Cst1]] ] loop_header:[[HeaderZ]]
-# CHECK-DAG: [[CondZ:z\d+]] Equal [ [[XorZ]] [[Cst0]] ] loop_header:[[HeaderZ]]
-# CHECK-DAG: If [ [[CondZ]] ] loop_header:[[HeaderZ]]
+# CHECK-DAG: <<PhiZ2:i\d+>> Phi [<<PhiZ1>>,<<XorZ>>] loop:<<HeaderZ:B\d+>>
+# CHECK-DAG: <<XorZ>> Xor [<<PhiZ2>>,<<Cst1>>] loop:<<HeaderZ>>
+# CHECK-DAG: <<CondZ:z\d+>> Equal [<<XorZ>>,<<Cst0>>] loop:<<HeaderZ>>
+# CHECK-DAG: If [<<CondZ>>] loop:<<HeaderZ>>
#
-# CHECK-DAG: [[Add5]] Add [ [[PhiX]] [[Cst5]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: [[Add7]] Add [ [[PhiX]] [[Cst7]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: Return [ [[PhiX]] ] loop_header:null
+# CHECK-DAG: <<Add5>> Add [<<PhiX>>,<<Cst5>>] loop:<<HeaderY>>
+# CHECK-DAG: <<Add7>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>>
+# CHECK-DAG: Return [<<PhiX>>] loop:none
# CHECK-START: int TestCase.testInnerLoop(int, boolean, boolean) dead_code_elimination_final (after)
-# CHECK-DAG: [[ArgX:i\d+]] ParameterValue
-# CHECK-DAG: [[ArgY:z\d+]] ParameterValue
-# CHECK-DAG: [[ArgZ:z\d+]] ParameterValue
-# CHECK-DAG: [[Cst0:i\d+]] IntConstant 0
-# CHECK-DAG: [[Cst1:i\d+]] IntConstant 1
-# CHECK-DAG: [[Cst7:i\d+]] IntConstant 7
+# CHECK-DAG: <<ArgX:i\d+>> ParameterValue
+# CHECK-DAG: <<ArgY:z\d+>> ParameterValue
+# CHECK-DAG: <<ArgZ:z\d+>> ParameterValue
+# CHECK-DAG: <<Cst0:i\d+>> IntConstant 0
+# CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
+# CHECK-DAG: <<Cst7:i\d+>> IntConstant 7
#
-# CHECK-DAG: [[PhiX:i\d+]] Phi [ [[ArgX]] [[Add7:i\d+]] ] loop_header:[[HeaderY:B\d+]]
-# CHECK-DAG: [[PhiZ1:i\d+]] Phi [ [[ArgZ]] [[PhiZ1]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: If [ [[ArgY]] ] loop_header:[[HeaderY]]
-# CHECK-DAG: [[Add7]] Add [ [[PhiX]] [[Cst7]] ] loop_header:[[HeaderY]]
+# CHECK-DAG: <<PhiX:i\d+>> Phi [<<ArgX>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>>
+# CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>>
+# CHECK-DAG: <<Add7>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>>
#
# ### Inner loop ###
-# CHECK-DAG: [[PhiZ2:i\d+]] Phi [ [[PhiZ1]] [[XorZ:i\d+]] ] loop_header:[[HeaderZ:B\d+]]
-# CHECK-DAG: [[XorZ]] Xor [ [[PhiZ2]] [[Cst1]] ] loop_header:[[HeaderZ]]
-# CHECK-DAG: [[CondZ:z\d+]] Equal [ [[XorZ]] [[Cst0]] ] loop_header:[[HeaderZ]]
-# CHECK-DAG: If [ [[CondZ]] ] loop_header:[[HeaderZ]]
+# CHECK-DAG: <<PhiZ:i\d+>> Phi [<<ArgZ>>,<<XorZ:i\d+>>] loop:<<HeaderZ:B\d+>>
+# CHECK-DAG: <<XorZ>> Xor [<<PhiZ>>,<<Cst1>>] loop:<<HeaderZ>>
+# CHECK-DAG: <<CondZ:z\d+>> Equal [<<XorZ>>,<<Cst0>>] loop:<<HeaderZ>>
+# CHECK-DAG: If [<<CondZ>>] loop:<<HeaderZ>>
#
-# CHECK-DAG: Return [ [[PhiX]] ] loop_header:null
+# CHECK-DAG: Return [<<PhiX>>] loop:none
.method public static testInnerLoop(IZZ)I
.registers 4
diff --git a/test/486-checker-must-do-null-check/expected.txt b/test/486-checker-must-do-null-check/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/486-checker-must-do-null-check/expected.txt
diff --git a/test/486-checker-must-do-null-check/info.txt b/test/486-checker-must-do-null-check/info.txt
new file mode 100644
index 0000000..494ff1c
--- /dev/null
+++ b/test/486-checker-must-do-null-check/info.txt
@@ -0,0 +1 @@
+Verifies MustDoNullCheck() on InstanceOf and CheckCast
diff --git a/test/486-checker-must-do-null-check/src/Main.java b/test/486-checker-must-do-null-check/src/Main.java
new file mode 100644
index 0000000..f285566
--- /dev/null
+++ b/test/486-checker-must-do-null-check/src/Main.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ // CHECK-START: void Main.InstanceOfPreChecked(java.lang.Object) instruction_simplifier (after)
+ // CHECK: InstanceOf must_do_null_check:false
+ public void InstanceOfPreChecked(Object o) throws Exception {
+ o.toString();
+ if (o instanceof Main) {
+ throw new Exception();
+ }
+ }
+
+ // CHECK-START: void Main.InstanceOf(java.lang.Object) instruction_simplifier (after)
+ // CHECK: InstanceOf must_do_null_check:true
+ public void InstanceOf(Object o) throws Exception {
+ if (o instanceof Main) {
+ throw new Exception();
+ }
+ }
+
+ // CHECK-START: void Main.CheckCastPreChecked(java.lang.Object) instruction_simplifier (after)
+ // CHECK: CheckCast must_do_null_check:false
+ public void CheckCastPreChecked(Object o) {
+ o.toString();
+ ((Main)o).Bar();
+ }
+
+ // CHECK-START: void Main.CheckCast(java.lang.Object) instruction_simplifier (after)
+ // CHECK: CheckCast must_do_null_check:true
+ public void CheckCast(Object o) {
+ ((Main)o).Bar();
+ }
+
+ void Bar() {throw new RuntimeException();}
+
+ public static void main(String[] sa) {
+ Main t = new Main();
+ }
+}
diff --git a/test/701-easy-div-rem/build b/test/701-easy-div-rem/build
new file mode 100644
index 0000000..1dc8452
--- /dev/null
+++ b/test/701-easy-div-rem/build
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Stop if something fails.
+set -e
+
+# Write out the source file.
+mkdir src
+python ./genMain.py
+
+# Increase the file size limitation for classes.lst as the machine generated
+# source file contains a lot of methods and is quite large.
+ulimit -S 4096
+
+./default-build
diff --git a/test/701-easy-div-rem/genMain.py b/test/701-easy-div-rem/genMain.py
index 80eac34..75eee17 100644
--- a/test/701-easy-div-rem/genMain.py
+++ b/test/701-easy-div-rem/genMain.py
@@ -12,15 +12,28 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+upper_bound_int_pow2 = 31
+upper_bound_long_pow2 = 63
+upper_bound_constant = 100
all_tests = [
({'@INT@': 'int', '@SUFFIX@':''},
- [('CheckDiv', 'idiv_by_pow2_', [2**i for i in range(31)]),
- ('CheckDiv', 'idiv_by_small_', [i for i in range(3, 16) if i not in (4, 8)]),
- ('CheckRem', 'irem_by_pow2_', [2**i for i in range(31)])]),
+ [('CheckDiv', 'idiv_by_pow2_', [2**i for i in range(upper_bound_int_pow2)]),
+ ('CheckDiv', 'idiv_by_pow2_neg_', [-2**i for i in range(upper_bound_int_pow2)]),
+ ('CheckDiv', 'idiv_by_constant_', [i for i in range(1, upper_bound_constant)]),
+ ('CheckDiv', 'idiv_by_constant_neg_', [-i for i in range(1, upper_bound_constant)]),
+ ('CheckRem', 'irem_by_pow2_', [2**i for i in range(upper_bound_int_pow2)]),
+ ('CheckRem', 'irem_by_pow2_neg_', [-2**i for i in range(upper_bound_int_pow2)]),
+ ('CheckRem', 'irem_by_constant_', [i for i in range(1, upper_bound_constant)]),
+ ('CheckRem', 'irem_by_constant_neg_', [-i for i in range(1, upper_bound_constant)])]),
({'@INT@': 'long', '@SUFFIX@': 'l'},
- [('CheckDiv', 'ldiv_by_pow2_', [2**i for i in range(63)]),
- ('CheckDiv', 'ldiv_by_small_', [i for i in range(3, 16) if i not in (4, 8)]),
- ('CheckRem', 'lrem_by_pow2_', [2**i for i in range(63)])])
+ [('CheckDiv', 'ldiv_by_pow2_', [2**i for i in range(upper_bound_long_pow2)]),
+ ('CheckDiv', 'ldiv_by_pow2_neg_', [-2**i for i in range(upper_bound_long_pow2)]),
+ ('CheckDiv', 'ldiv_by_constant_', [i for i in range(1, upper_bound_constant)]),
+ ('CheckDiv', 'ldiv_by_constant_neg_', [-i for i in range(1, upper_bound_constant)]),
+ ('CheckRem', 'lrem_by_pow2_', [2**i for i in range(upper_bound_long_pow2)]),
+ ('CheckRem', 'lrem_by_pow2_neg_', [-2**i for i in range(upper_bound_long_pow2)]),
+ ('CheckRem', 'lrem_by_constant_', [i for i in range(1, upper_bound_constant)]),
+ ('CheckRem', 'lrem_by_constant_neg_', [-i for i in range(1, upper_bound_constant)])])
]
def subst_vars(variables, text):
diff --git a/test/701-easy-div-rem/src/Main.java b/test/701-easy-div-rem/src/Main.java
deleted file mode 100644
index f995f61..0000000
--- a/test/701-easy-div-rem/src/Main.java
+++ /dev/null
@@ -1,529 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-public class Main {
- public static int num_errors = 0;
-
- public static void reportError(String message) {
- if (num_errors == 10) {
- System.out.println("Omitting other error messages...");
- } else if (num_errors < 10) {
- System.out.println(message);
- }
- num_errors += 1;
- }
-
- public static void intCheckDiv(String desc, int result, int dividend, int divisor) {
- int correct_result = dividend / divisor;
- if (result != correct_result) {
- reportError(desc + "(" + dividend + ") == " + result +
- " should be " + correct_result);
- }
- }
- public static void intCheckRem(String desc, int result, int dividend, int divisor) {
- int correct_result = dividend % divisor;
- if (result != correct_result) {
- reportError(desc + "(" + dividend + ") == " + result +
- " should be " + correct_result);
- }
- }
- public static void longCheckDiv(String desc, long result, long dividend, long divisor) {
- long correct_result = dividend / divisor;
- if (result != correct_result) {
- reportError(desc + "(" + dividend + ") == " + result +
- " should be " + correct_result);
- }
- }
- public static void longCheckRem(String desc, long result, long dividend, long divisor) {
- long correct_result = dividend % divisor;
- if (result != correct_result) {
- reportError(desc + "(" + dividend + ") == " + result +
- " should be " + correct_result);
- }
- }
-
- public static int idiv_by_pow2_0(int x) {return x / 1;}
- public static int idiv_by_pow2_1(int x) {return x / 2;}
- public static int idiv_by_pow2_2(int x) {return x / 4;}
- public static int idiv_by_pow2_3(int x) {return x / 8;}
- public static int idiv_by_pow2_4(int x) {return x / 16;}
- public static int idiv_by_pow2_5(int x) {return x / 32;}
- public static int idiv_by_pow2_6(int x) {return x / 64;}
- public static int idiv_by_pow2_7(int x) {return x / 128;}
- public static int idiv_by_pow2_8(int x) {return x / 256;}
- public static int idiv_by_pow2_9(int x) {return x / 512;}
- public static int idiv_by_pow2_10(int x) {return x / 1024;}
- public static int idiv_by_pow2_11(int x) {return x / 2048;}
- public static int idiv_by_pow2_12(int x) {return x / 4096;}
- public static int idiv_by_pow2_13(int x) {return x / 8192;}
- public static int idiv_by_pow2_14(int x) {return x / 16384;}
- public static int idiv_by_pow2_15(int x) {return x / 32768;}
- public static int idiv_by_pow2_16(int x) {return x / 65536;}
- public static int idiv_by_pow2_17(int x) {return x / 131072;}
- public static int idiv_by_pow2_18(int x) {return x / 262144;}
- public static int idiv_by_pow2_19(int x) {return x / 524288;}
- public static int idiv_by_pow2_20(int x) {return x / 1048576;}
- public static int idiv_by_pow2_21(int x) {return x / 2097152;}
- public static int idiv_by_pow2_22(int x) {return x / 4194304;}
- public static int idiv_by_pow2_23(int x) {return x / 8388608;}
- public static int idiv_by_pow2_24(int x) {return x / 16777216;}
- public static int idiv_by_pow2_25(int x) {return x / 33554432;}
- public static int idiv_by_pow2_26(int x) {return x / 67108864;}
- public static int idiv_by_pow2_27(int x) {return x / 134217728;}
- public static int idiv_by_pow2_28(int x) {return x / 268435456;}
- public static int idiv_by_pow2_29(int x) {return x / 536870912;}
- public static int idiv_by_pow2_30(int x) {return x / 1073741824;}
- public static int idiv_by_small_0(int x) {return x / 3;}
- public static int idiv_by_small_1(int x) {return x / 5;}
- public static int idiv_by_small_2(int x) {return x / 6;}
- public static int idiv_by_small_3(int x) {return x / 7;}
- public static int idiv_by_small_4(int x) {return x / 9;}
- public static int idiv_by_small_5(int x) {return x / 10;}
- public static int idiv_by_small_6(int x) {return x / 11;}
- public static int idiv_by_small_7(int x) {return x / 12;}
- public static int idiv_by_small_8(int x) {return x / 13;}
- public static int idiv_by_small_9(int x) {return x / 14;}
- public static int idiv_by_small_10(int x) {return x / 15;}
- public static int irem_by_pow2_0(int x) {return x % 1;}
- public static int irem_by_pow2_1(int x) {return x % 2;}
- public static int irem_by_pow2_2(int x) {return x % 4;}
- public static int irem_by_pow2_3(int x) {return x % 8;}
- public static int irem_by_pow2_4(int x) {return x % 16;}
- public static int irem_by_pow2_5(int x) {return x % 32;}
- public static int irem_by_pow2_6(int x) {return x % 64;}
- public static int irem_by_pow2_7(int x) {return x % 128;}
- public static int irem_by_pow2_8(int x) {return x % 256;}
- public static int irem_by_pow2_9(int x) {return x % 512;}
- public static int irem_by_pow2_10(int x) {return x % 1024;}
- public static int irem_by_pow2_11(int x) {return x % 2048;}
- public static int irem_by_pow2_12(int x) {return x % 4096;}
- public static int irem_by_pow2_13(int x) {return x % 8192;}
- public static int irem_by_pow2_14(int x) {return x % 16384;}
- public static int irem_by_pow2_15(int x) {return x % 32768;}
- public static int irem_by_pow2_16(int x) {return x % 65536;}
- public static int irem_by_pow2_17(int x) {return x % 131072;}
- public static int irem_by_pow2_18(int x) {return x % 262144;}
- public static int irem_by_pow2_19(int x) {return x % 524288;}
- public static int irem_by_pow2_20(int x) {return x % 1048576;}
- public static int irem_by_pow2_21(int x) {return x % 2097152;}
- public static int irem_by_pow2_22(int x) {return x % 4194304;}
- public static int irem_by_pow2_23(int x) {return x % 8388608;}
- public static int irem_by_pow2_24(int x) {return x % 16777216;}
- public static int irem_by_pow2_25(int x) {return x % 33554432;}
- public static int irem_by_pow2_26(int x) {return x % 67108864;}
- public static int irem_by_pow2_27(int x) {return x % 134217728;}
- public static int irem_by_pow2_28(int x) {return x % 268435456;}
- public static int irem_by_pow2_29(int x) {return x % 536870912;}
- public static int irem_by_pow2_30(int x) {return x % 1073741824;}
- public static long ldiv_by_pow2_0(long x) {return x / 1l;}
- public static long ldiv_by_pow2_1(long x) {return x / 2l;}
- public static long ldiv_by_pow2_2(long x) {return x / 4l;}
- public static long ldiv_by_pow2_3(long x) {return x / 8l;}
- public static long ldiv_by_pow2_4(long x) {return x / 16l;}
- public static long ldiv_by_pow2_5(long x) {return x / 32l;}
- public static long ldiv_by_pow2_6(long x) {return x / 64l;}
- public static long ldiv_by_pow2_7(long x) {return x / 128l;}
- public static long ldiv_by_pow2_8(long x) {return x / 256l;}
- public static long ldiv_by_pow2_9(long x) {return x / 512l;}
- public static long ldiv_by_pow2_10(long x) {return x / 1024l;}
- public static long ldiv_by_pow2_11(long x) {return x / 2048l;}
- public static long ldiv_by_pow2_12(long x) {return x / 4096l;}
- public static long ldiv_by_pow2_13(long x) {return x / 8192l;}
- public static long ldiv_by_pow2_14(long x) {return x / 16384l;}
- public static long ldiv_by_pow2_15(long x) {return x / 32768l;}
- public static long ldiv_by_pow2_16(long x) {return x / 65536l;}
- public static long ldiv_by_pow2_17(long x) {return x / 131072l;}
- public static long ldiv_by_pow2_18(long x) {return x / 262144l;}
- public static long ldiv_by_pow2_19(long x) {return x / 524288l;}
- public static long ldiv_by_pow2_20(long x) {return x / 1048576l;}
- public static long ldiv_by_pow2_21(long x) {return x / 2097152l;}
- public static long ldiv_by_pow2_22(long x) {return x / 4194304l;}
- public static long ldiv_by_pow2_23(long x) {return x / 8388608l;}
- public static long ldiv_by_pow2_24(long x) {return x / 16777216l;}
- public static long ldiv_by_pow2_25(long x) {return x / 33554432l;}
- public static long ldiv_by_pow2_26(long x) {return x / 67108864l;}
- public static long ldiv_by_pow2_27(long x) {return x / 134217728l;}
- public static long ldiv_by_pow2_28(long x) {return x / 268435456l;}
- public static long ldiv_by_pow2_29(long x) {return x / 536870912l;}
- public static long ldiv_by_pow2_30(long x) {return x / 1073741824l;}
- public static long ldiv_by_pow2_31(long x) {return x / 2147483648l;}
- public static long ldiv_by_pow2_32(long x) {return x / 4294967296l;}
- public static long ldiv_by_pow2_33(long x) {return x / 8589934592l;}
- public static long ldiv_by_pow2_34(long x) {return x / 17179869184l;}
- public static long ldiv_by_pow2_35(long x) {return x / 34359738368l;}
- public static long ldiv_by_pow2_36(long x) {return x / 68719476736l;}
- public static long ldiv_by_pow2_37(long x) {return x / 137438953472l;}
- public static long ldiv_by_pow2_38(long x) {return x / 274877906944l;}
- public static long ldiv_by_pow2_39(long x) {return x / 549755813888l;}
- public static long ldiv_by_pow2_40(long x) {return x / 1099511627776l;}
- public static long ldiv_by_pow2_41(long x) {return x / 2199023255552l;}
- public static long ldiv_by_pow2_42(long x) {return x / 4398046511104l;}
- public static long ldiv_by_pow2_43(long x) {return x / 8796093022208l;}
- public static long ldiv_by_pow2_44(long x) {return x / 17592186044416l;}
- public static long ldiv_by_pow2_45(long x) {return x / 35184372088832l;}
- public static long ldiv_by_pow2_46(long x) {return x / 70368744177664l;}
- public static long ldiv_by_pow2_47(long x) {return x / 140737488355328l;}
- public static long ldiv_by_pow2_48(long x) {return x / 281474976710656l;}
- public static long ldiv_by_pow2_49(long x) {return x / 562949953421312l;}
- public static long ldiv_by_pow2_50(long x) {return x / 1125899906842624l;}
- public static long ldiv_by_pow2_51(long x) {return x / 2251799813685248l;}
- public static long ldiv_by_pow2_52(long x) {return x / 4503599627370496l;}
- public static long ldiv_by_pow2_53(long x) {return x / 9007199254740992l;}
- public static long ldiv_by_pow2_54(long x) {return x / 18014398509481984l;}
- public static long ldiv_by_pow2_55(long x) {return x / 36028797018963968l;}
- public static long ldiv_by_pow2_56(long x) {return x / 72057594037927936l;}
- public static long ldiv_by_pow2_57(long x) {return x / 144115188075855872l;}
- public static long ldiv_by_pow2_58(long x) {return x / 288230376151711744l;}
- public static long ldiv_by_pow2_59(long x) {return x / 576460752303423488l;}
- public static long ldiv_by_pow2_60(long x) {return x / 1152921504606846976l;}
- public static long ldiv_by_pow2_61(long x) {return x / 2305843009213693952l;}
- public static long ldiv_by_pow2_62(long x) {return x / 4611686018427387904l;}
- public static long ldiv_by_small_0(long x) {return x / 3l;}
- public static long ldiv_by_small_1(long x) {return x / 5l;}
- public static long ldiv_by_small_2(long x) {return x / 6l;}
- public static long ldiv_by_small_3(long x) {return x / 7l;}
- public static long ldiv_by_small_4(long x) {return x / 9l;}
- public static long ldiv_by_small_5(long x) {return x / 10l;}
- public static long ldiv_by_small_6(long x) {return x / 11l;}
- public static long ldiv_by_small_7(long x) {return x / 12l;}
- public static long ldiv_by_small_8(long x) {return x / 13l;}
- public static long ldiv_by_small_9(long x) {return x / 14l;}
- public static long ldiv_by_small_10(long x) {return x / 15l;}
- public static long lrem_by_pow2_0(long x) {return x % 1l;}
- public static long lrem_by_pow2_1(long x) {return x % 2l;}
- public static long lrem_by_pow2_2(long x) {return x % 4l;}
- public static long lrem_by_pow2_3(long x) {return x % 8l;}
- public static long lrem_by_pow2_4(long x) {return x % 16l;}
- public static long lrem_by_pow2_5(long x) {return x % 32l;}
- public static long lrem_by_pow2_6(long x) {return x % 64l;}
- public static long lrem_by_pow2_7(long x) {return x % 128l;}
- public static long lrem_by_pow2_8(long x) {return x % 256l;}
- public static long lrem_by_pow2_9(long x) {return x % 512l;}
- public static long lrem_by_pow2_10(long x) {return x % 1024l;}
- public static long lrem_by_pow2_11(long x) {return x % 2048l;}
- public static long lrem_by_pow2_12(long x) {return x % 4096l;}
- public static long lrem_by_pow2_13(long x) {return x % 8192l;}
- public static long lrem_by_pow2_14(long x) {return x % 16384l;}
- public static long lrem_by_pow2_15(long x) {return x % 32768l;}
- public static long lrem_by_pow2_16(long x) {return x % 65536l;}
- public static long lrem_by_pow2_17(long x) {return x % 131072l;}
- public static long lrem_by_pow2_18(long x) {return x % 262144l;}
- public static long lrem_by_pow2_19(long x) {return x % 524288l;}
- public static long lrem_by_pow2_20(long x) {return x % 1048576l;}
- public static long lrem_by_pow2_21(long x) {return x % 2097152l;}
- public static long lrem_by_pow2_22(long x) {return x % 4194304l;}
- public static long lrem_by_pow2_23(long x) {return x % 8388608l;}
- public static long lrem_by_pow2_24(long x) {return x % 16777216l;}
- public static long lrem_by_pow2_25(long x) {return x % 33554432l;}
- public static long lrem_by_pow2_26(long x) {return x % 67108864l;}
- public static long lrem_by_pow2_27(long x) {return x % 134217728l;}
- public static long lrem_by_pow2_28(long x) {return x % 268435456l;}
- public static long lrem_by_pow2_29(long x) {return x % 536870912l;}
- public static long lrem_by_pow2_30(long x) {return x % 1073741824l;}
- public static long lrem_by_pow2_31(long x) {return x % 2147483648l;}
- public static long lrem_by_pow2_32(long x) {return x % 4294967296l;}
- public static long lrem_by_pow2_33(long x) {return x % 8589934592l;}
- public static long lrem_by_pow2_34(long x) {return x % 17179869184l;}
- public static long lrem_by_pow2_35(long x) {return x % 34359738368l;}
- public static long lrem_by_pow2_36(long x) {return x % 68719476736l;}
- public static long lrem_by_pow2_37(long x) {return x % 137438953472l;}
- public static long lrem_by_pow2_38(long x) {return x % 274877906944l;}
- public static long lrem_by_pow2_39(long x) {return x % 549755813888l;}
- public static long lrem_by_pow2_40(long x) {return x % 1099511627776l;}
- public static long lrem_by_pow2_41(long x) {return x % 2199023255552l;}
- public static long lrem_by_pow2_42(long x) {return x % 4398046511104l;}
- public static long lrem_by_pow2_43(long x) {return x % 8796093022208l;}
- public static long lrem_by_pow2_44(long x) {return x % 17592186044416l;}
- public static long lrem_by_pow2_45(long x) {return x % 35184372088832l;}
- public static long lrem_by_pow2_46(long x) {return x % 70368744177664l;}
- public static long lrem_by_pow2_47(long x) {return x % 140737488355328l;}
- public static long lrem_by_pow2_48(long x) {return x % 281474976710656l;}
- public static long lrem_by_pow2_49(long x) {return x % 562949953421312l;}
- public static long lrem_by_pow2_50(long x) {return x % 1125899906842624l;}
- public static long lrem_by_pow2_51(long x) {return x % 2251799813685248l;}
- public static long lrem_by_pow2_52(long x) {return x % 4503599627370496l;}
- public static long lrem_by_pow2_53(long x) {return x % 9007199254740992l;}
- public static long lrem_by_pow2_54(long x) {return x % 18014398509481984l;}
- public static long lrem_by_pow2_55(long x) {return x % 36028797018963968l;}
- public static long lrem_by_pow2_56(long x) {return x % 72057594037927936l;}
- public static long lrem_by_pow2_57(long x) {return x % 144115188075855872l;}
- public static long lrem_by_pow2_58(long x) {return x % 288230376151711744l;}
- public static long lrem_by_pow2_59(long x) {return x % 576460752303423488l;}
- public static long lrem_by_pow2_60(long x) {return x % 1152921504606846976l;}
- public static long lrem_by_pow2_61(long x) {return x % 2305843009213693952l;}
- public static long lrem_by_pow2_62(long x) {return x % 4611686018427387904l;}
-
- public static void intCheckAll(int x) {
- intCheckDiv("idiv_by_pow2_0", idiv_by_pow2_0(x), x, 1);
- intCheckDiv("idiv_by_pow2_1", idiv_by_pow2_1(x), x, 2);
- intCheckDiv("idiv_by_pow2_2", idiv_by_pow2_2(x), x, 4);
- intCheckDiv("idiv_by_pow2_3", idiv_by_pow2_3(x), x, 8);
- intCheckDiv("idiv_by_pow2_4", idiv_by_pow2_4(x), x, 16);
- intCheckDiv("idiv_by_pow2_5", idiv_by_pow2_5(x), x, 32);
- intCheckDiv("idiv_by_pow2_6", idiv_by_pow2_6(x), x, 64);
- intCheckDiv("idiv_by_pow2_7", idiv_by_pow2_7(x), x, 128);
- intCheckDiv("idiv_by_pow2_8", idiv_by_pow2_8(x), x, 256);
- intCheckDiv("idiv_by_pow2_9", idiv_by_pow2_9(x), x, 512);
- intCheckDiv("idiv_by_pow2_10", idiv_by_pow2_10(x), x, 1024);
- intCheckDiv("idiv_by_pow2_11", idiv_by_pow2_11(x), x, 2048);
- intCheckDiv("idiv_by_pow2_12", idiv_by_pow2_12(x), x, 4096);
- intCheckDiv("idiv_by_pow2_13", idiv_by_pow2_13(x), x, 8192);
- intCheckDiv("idiv_by_pow2_14", idiv_by_pow2_14(x), x, 16384);
- intCheckDiv("idiv_by_pow2_15", idiv_by_pow2_15(x), x, 32768);
- intCheckDiv("idiv_by_pow2_16", idiv_by_pow2_16(x), x, 65536);
- intCheckDiv("idiv_by_pow2_17", idiv_by_pow2_17(x), x, 131072);
- intCheckDiv("idiv_by_pow2_18", idiv_by_pow2_18(x), x, 262144);
- intCheckDiv("idiv_by_pow2_19", idiv_by_pow2_19(x), x, 524288);
- intCheckDiv("idiv_by_pow2_20", idiv_by_pow2_20(x), x, 1048576);
- intCheckDiv("idiv_by_pow2_21", idiv_by_pow2_21(x), x, 2097152);
- intCheckDiv("idiv_by_pow2_22", idiv_by_pow2_22(x), x, 4194304);
- intCheckDiv("idiv_by_pow2_23", idiv_by_pow2_23(x), x, 8388608);
- intCheckDiv("idiv_by_pow2_24", idiv_by_pow2_24(x), x, 16777216);
- intCheckDiv("idiv_by_pow2_25", idiv_by_pow2_25(x), x, 33554432);
- intCheckDiv("idiv_by_pow2_26", idiv_by_pow2_26(x), x, 67108864);
- intCheckDiv("idiv_by_pow2_27", idiv_by_pow2_27(x), x, 134217728);
- intCheckDiv("idiv_by_pow2_28", idiv_by_pow2_28(x), x, 268435456);
- intCheckDiv("idiv_by_pow2_29", idiv_by_pow2_29(x), x, 536870912);
- intCheckDiv("idiv_by_pow2_30", idiv_by_pow2_30(x), x, 1073741824);
- intCheckDiv("idiv_by_small_0", idiv_by_small_0(x), x, 3);
- intCheckDiv("idiv_by_small_1", idiv_by_small_1(x), x, 5);
- intCheckDiv("idiv_by_small_2", idiv_by_small_2(x), x, 6);
- intCheckDiv("idiv_by_small_3", idiv_by_small_3(x), x, 7);
- intCheckDiv("idiv_by_small_4", idiv_by_small_4(x), x, 9);
- intCheckDiv("idiv_by_small_5", idiv_by_small_5(x), x, 10);
- intCheckDiv("idiv_by_small_6", idiv_by_small_6(x), x, 11);
- intCheckDiv("idiv_by_small_7", idiv_by_small_7(x), x, 12);
- intCheckDiv("idiv_by_small_8", idiv_by_small_8(x), x, 13);
- intCheckDiv("idiv_by_small_9", idiv_by_small_9(x), x, 14);
- intCheckDiv("idiv_by_small_10", idiv_by_small_10(x), x, 15);
- intCheckRem("irem_by_pow2_0", irem_by_pow2_0(x), x, 1);
- intCheckRem("irem_by_pow2_1", irem_by_pow2_1(x), x, 2);
- intCheckRem("irem_by_pow2_2", irem_by_pow2_2(x), x, 4);
- intCheckRem("irem_by_pow2_3", irem_by_pow2_3(x), x, 8);
- intCheckRem("irem_by_pow2_4", irem_by_pow2_4(x), x, 16);
- intCheckRem("irem_by_pow2_5", irem_by_pow2_5(x), x, 32);
- intCheckRem("irem_by_pow2_6", irem_by_pow2_6(x), x, 64);
- intCheckRem("irem_by_pow2_7", irem_by_pow2_7(x), x, 128);
- intCheckRem("irem_by_pow2_8", irem_by_pow2_8(x), x, 256);
- intCheckRem("irem_by_pow2_9", irem_by_pow2_9(x), x, 512);
- intCheckRem("irem_by_pow2_10", irem_by_pow2_10(x), x, 1024);
- intCheckRem("irem_by_pow2_11", irem_by_pow2_11(x), x, 2048);
- intCheckRem("irem_by_pow2_12", irem_by_pow2_12(x), x, 4096);
- intCheckRem("irem_by_pow2_13", irem_by_pow2_13(x), x, 8192);
- intCheckRem("irem_by_pow2_14", irem_by_pow2_14(x), x, 16384);
- intCheckRem("irem_by_pow2_15", irem_by_pow2_15(x), x, 32768);
- intCheckRem("irem_by_pow2_16", irem_by_pow2_16(x), x, 65536);
- intCheckRem("irem_by_pow2_17", irem_by_pow2_17(x), x, 131072);
- intCheckRem("irem_by_pow2_18", irem_by_pow2_18(x), x, 262144);
- intCheckRem("irem_by_pow2_19", irem_by_pow2_19(x), x, 524288);
- intCheckRem("irem_by_pow2_20", irem_by_pow2_20(x), x, 1048576);
- intCheckRem("irem_by_pow2_21", irem_by_pow2_21(x), x, 2097152);
- intCheckRem("irem_by_pow2_22", irem_by_pow2_22(x), x, 4194304);
- intCheckRem("irem_by_pow2_23", irem_by_pow2_23(x), x, 8388608);
- intCheckRem("irem_by_pow2_24", irem_by_pow2_24(x), x, 16777216);
- intCheckRem("irem_by_pow2_25", irem_by_pow2_25(x), x, 33554432);
- intCheckRem("irem_by_pow2_26", irem_by_pow2_26(x), x, 67108864);
- intCheckRem("irem_by_pow2_27", irem_by_pow2_27(x), x, 134217728);
- intCheckRem("irem_by_pow2_28", irem_by_pow2_28(x), x, 268435456);
- intCheckRem("irem_by_pow2_29", irem_by_pow2_29(x), x, 536870912);
- intCheckRem("irem_by_pow2_30", irem_by_pow2_30(x), x, 1073741824);
- }
-
- public static void longCheckAll(long x) {
- longCheckDiv("ldiv_by_pow2_0", ldiv_by_pow2_0(x), x, 1l);
- longCheckDiv("ldiv_by_pow2_1", ldiv_by_pow2_1(x), x, 2l);
- longCheckDiv("ldiv_by_pow2_2", ldiv_by_pow2_2(x), x, 4l);
- longCheckDiv("ldiv_by_pow2_3", ldiv_by_pow2_3(x), x, 8l);
- longCheckDiv("ldiv_by_pow2_4", ldiv_by_pow2_4(x), x, 16l);
- longCheckDiv("ldiv_by_pow2_5", ldiv_by_pow2_5(x), x, 32l);
- longCheckDiv("ldiv_by_pow2_6", ldiv_by_pow2_6(x), x, 64l);
- longCheckDiv("ldiv_by_pow2_7", ldiv_by_pow2_7(x), x, 128l);
- longCheckDiv("ldiv_by_pow2_8", ldiv_by_pow2_8(x), x, 256l);
- longCheckDiv("ldiv_by_pow2_9", ldiv_by_pow2_9(x), x, 512l);
- longCheckDiv("ldiv_by_pow2_10", ldiv_by_pow2_10(x), x, 1024l);
- longCheckDiv("ldiv_by_pow2_11", ldiv_by_pow2_11(x), x, 2048l);
- longCheckDiv("ldiv_by_pow2_12", ldiv_by_pow2_12(x), x, 4096l);
- longCheckDiv("ldiv_by_pow2_13", ldiv_by_pow2_13(x), x, 8192l);
- longCheckDiv("ldiv_by_pow2_14", ldiv_by_pow2_14(x), x, 16384l);
- longCheckDiv("ldiv_by_pow2_15", ldiv_by_pow2_15(x), x, 32768l);
- longCheckDiv("ldiv_by_pow2_16", ldiv_by_pow2_16(x), x, 65536l);
- longCheckDiv("ldiv_by_pow2_17", ldiv_by_pow2_17(x), x, 131072l);
- longCheckDiv("ldiv_by_pow2_18", ldiv_by_pow2_18(x), x, 262144l);
- longCheckDiv("ldiv_by_pow2_19", ldiv_by_pow2_19(x), x, 524288l);
- longCheckDiv("ldiv_by_pow2_20", ldiv_by_pow2_20(x), x, 1048576l);
- longCheckDiv("ldiv_by_pow2_21", ldiv_by_pow2_21(x), x, 2097152l);
- longCheckDiv("ldiv_by_pow2_22", ldiv_by_pow2_22(x), x, 4194304l);
- longCheckDiv("ldiv_by_pow2_23", ldiv_by_pow2_23(x), x, 8388608l);
- longCheckDiv("ldiv_by_pow2_24", ldiv_by_pow2_24(x), x, 16777216l);
- longCheckDiv("ldiv_by_pow2_25", ldiv_by_pow2_25(x), x, 33554432l);
- longCheckDiv("ldiv_by_pow2_26", ldiv_by_pow2_26(x), x, 67108864l);
- longCheckDiv("ldiv_by_pow2_27", ldiv_by_pow2_27(x), x, 134217728l);
- longCheckDiv("ldiv_by_pow2_28", ldiv_by_pow2_28(x), x, 268435456l);
- longCheckDiv("ldiv_by_pow2_29", ldiv_by_pow2_29(x), x, 536870912l);
- longCheckDiv("ldiv_by_pow2_30", ldiv_by_pow2_30(x), x, 1073741824l);
- longCheckDiv("ldiv_by_pow2_31", ldiv_by_pow2_31(x), x, 2147483648l);
- longCheckDiv("ldiv_by_pow2_32", ldiv_by_pow2_32(x), x, 4294967296l);
- longCheckDiv("ldiv_by_pow2_33", ldiv_by_pow2_33(x), x, 8589934592l);
- longCheckDiv("ldiv_by_pow2_34", ldiv_by_pow2_34(x), x, 17179869184l);
- longCheckDiv("ldiv_by_pow2_35", ldiv_by_pow2_35(x), x, 34359738368l);
- longCheckDiv("ldiv_by_pow2_36", ldiv_by_pow2_36(x), x, 68719476736l);
- longCheckDiv("ldiv_by_pow2_37", ldiv_by_pow2_37(x), x, 137438953472l);
- longCheckDiv("ldiv_by_pow2_38", ldiv_by_pow2_38(x), x, 274877906944l);
- longCheckDiv("ldiv_by_pow2_39", ldiv_by_pow2_39(x), x, 549755813888l);
- longCheckDiv("ldiv_by_pow2_40", ldiv_by_pow2_40(x), x, 1099511627776l);
- longCheckDiv("ldiv_by_pow2_41", ldiv_by_pow2_41(x), x, 2199023255552l);
- longCheckDiv("ldiv_by_pow2_42", ldiv_by_pow2_42(x), x, 4398046511104l);
- longCheckDiv("ldiv_by_pow2_43", ldiv_by_pow2_43(x), x, 8796093022208l);
- longCheckDiv("ldiv_by_pow2_44", ldiv_by_pow2_44(x), x, 17592186044416l);
- longCheckDiv("ldiv_by_pow2_45", ldiv_by_pow2_45(x), x, 35184372088832l);
- longCheckDiv("ldiv_by_pow2_46", ldiv_by_pow2_46(x), x, 70368744177664l);
- longCheckDiv("ldiv_by_pow2_47", ldiv_by_pow2_47(x), x, 140737488355328l);
- longCheckDiv("ldiv_by_pow2_48", ldiv_by_pow2_48(x), x, 281474976710656l);
- longCheckDiv("ldiv_by_pow2_49", ldiv_by_pow2_49(x), x, 562949953421312l);
- longCheckDiv("ldiv_by_pow2_50", ldiv_by_pow2_50(x), x, 1125899906842624l);
- longCheckDiv("ldiv_by_pow2_51", ldiv_by_pow2_51(x), x, 2251799813685248l);
- longCheckDiv("ldiv_by_pow2_52", ldiv_by_pow2_52(x), x, 4503599627370496l);
- longCheckDiv("ldiv_by_pow2_53", ldiv_by_pow2_53(x), x, 9007199254740992l);
- longCheckDiv("ldiv_by_pow2_54", ldiv_by_pow2_54(x), x, 18014398509481984l);
- longCheckDiv("ldiv_by_pow2_55", ldiv_by_pow2_55(x), x, 36028797018963968l);
- longCheckDiv("ldiv_by_pow2_56", ldiv_by_pow2_56(x), x, 72057594037927936l);
- longCheckDiv("ldiv_by_pow2_57", ldiv_by_pow2_57(x), x, 144115188075855872l);
- longCheckDiv("ldiv_by_pow2_58", ldiv_by_pow2_58(x), x, 288230376151711744l);
- longCheckDiv("ldiv_by_pow2_59", ldiv_by_pow2_59(x), x, 576460752303423488l);
- longCheckDiv("ldiv_by_pow2_60", ldiv_by_pow2_60(x), x, 1152921504606846976l);
- longCheckDiv("ldiv_by_pow2_61", ldiv_by_pow2_61(x), x, 2305843009213693952l);
- longCheckDiv("ldiv_by_pow2_62", ldiv_by_pow2_62(x), x, 4611686018427387904l);
- longCheckDiv("ldiv_by_small_0", ldiv_by_small_0(x), x, 3l);
- longCheckDiv("ldiv_by_small_1", ldiv_by_small_1(x), x, 5l);
- longCheckDiv("ldiv_by_small_2", ldiv_by_small_2(x), x, 6l);
- longCheckDiv("ldiv_by_small_3", ldiv_by_small_3(x), x, 7l);
- longCheckDiv("ldiv_by_small_4", ldiv_by_small_4(x), x, 9l);
- longCheckDiv("ldiv_by_small_5", ldiv_by_small_5(x), x, 10l);
- longCheckDiv("ldiv_by_small_6", ldiv_by_small_6(x), x, 11l);
- longCheckDiv("ldiv_by_small_7", ldiv_by_small_7(x), x, 12l);
- longCheckDiv("ldiv_by_small_8", ldiv_by_small_8(x), x, 13l);
- longCheckDiv("ldiv_by_small_9", ldiv_by_small_9(x), x, 14l);
- longCheckDiv("ldiv_by_small_10", ldiv_by_small_10(x), x, 15l);
- longCheckRem("lrem_by_pow2_0", lrem_by_pow2_0(x), x, 1l);
- longCheckRem("lrem_by_pow2_1", lrem_by_pow2_1(x), x, 2l);
- longCheckRem("lrem_by_pow2_2", lrem_by_pow2_2(x), x, 4l);
- longCheckRem("lrem_by_pow2_3", lrem_by_pow2_3(x), x, 8l);
- longCheckRem("lrem_by_pow2_4", lrem_by_pow2_4(x), x, 16l);
- longCheckRem("lrem_by_pow2_5", lrem_by_pow2_5(x), x, 32l);
- longCheckRem("lrem_by_pow2_6", lrem_by_pow2_6(x), x, 64l);
- longCheckRem("lrem_by_pow2_7", lrem_by_pow2_7(x), x, 128l);
- longCheckRem("lrem_by_pow2_8", lrem_by_pow2_8(x), x, 256l);
- longCheckRem("lrem_by_pow2_9", lrem_by_pow2_9(x), x, 512l);
- longCheckRem("lrem_by_pow2_10", lrem_by_pow2_10(x), x, 1024l);
- longCheckRem("lrem_by_pow2_11", lrem_by_pow2_11(x), x, 2048l);
- longCheckRem("lrem_by_pow2_12", lrem_by_pow2_12(x), x, 4096l);
- longCheckRem("lrem_by_pow2_13", lrem_by_pow2_13(x), x, 8192l);
- longCheckRem("lrem_by_pow2_14", lrem_by_pow2_14(x), x, 16384l);
- longCheckRem("lrem_by_pow2_15", lrem_by_pow2_15(x), x, 32768l);
- longCheckRem("lrem_by_pow2_16", lrem_by_pow2_16(x), x, 65536l);
- longCheckRem("lrem_by_pow2_17", lrem_by_pow2_17(x), x, 131072l);
- longCheckRem("lrem_by_pow2_18", lrem_by_pow2_18(x), x, 262144l);
- longCheckRem("lrem_by_pow2_19", lrem_by_pow2_19(x), x, 524288l);
- longCheckRem("lrem_by_pow2_20", lrem_by_pow2_20(x), x, 1048576l);
- longCheckRem("lrem_by_pow2_21", lrem_by_pow2_21(x), x, 2097152l);
- longCheckRem("lrem_by_pow2_22", lrem_by_pow2_22(x), x, 4194304l);
- longCheckRem("lrem_by_pow2_23", lrem_by_pow2_23(x), x, 8388608l);
- longCheckRem("lrem_by_pow2_24", lrem_by_pow2_24(x), x, 16777216l);
- longCheckRem("lrem_by_pow2_25", lrem_by_pow2_25(x), x, 33554432l);
- longCheckRem("lrem_by_pow2_26", lrem_by_pow2_26(x), x, 67108864l);
- longCheckRem("lrem_by_pow2_27", lrem_by_pow2_27(x), x, 134217728l);
- longCheckRem("lrem_by_pow2_28", lrem_by_pow2_28(x), x, 268435456l);
- longCheckRem("lrem_by_pow2_29", lrem_by_pow2_29(x), x, 536870912l);
- longCheckRem("lrem_by_pow2_30", lrem_by_pow2_30(x), x, 1073741824l);
- longCheckRem("lrem_by_pow2_31", lrem_by_pow2_31(x), x, 2147483648l);
- longCheckRem("lrem_by_pow2_32", lrem_by_pow2_32(x), x, 4294967296l);
- longCheckRem("lrem_by_pow2_33", lrem_by_pow2_33(x), x, 8589934592l);
- longCheckRem("lrem_by_pow2_34", lrem_by_pow2_34(x), x, 17179869184l);
- longCheckRem("lrem_by_pow2_35", lrem_by_pow2_35(x), x, 34359738368l);
- longCheckRem("lrem_by_pow2_36", lrem_by_pow2_36(x), x, 68719476736l);
- longCheckRem("lrem_by_pow2_37", lrem_by_pow2_37(x), x, 137438953472l);
- longCheckRem("lrem_by_pow2_38", lrem_by_pow2_38(x), x, 274877906944l);
- longCheckRem("lrem_by_pow2_39", lrem_by_pow2_39(x), x, 549755813888l);
- longCheckRem("lrem_by_pow2_40", lrem_by_pow2_40(x), x, 1099511627776l);
- longCheckRem("lrem_by_pow2_41", lrem_by_pow2_41(x), x, 2199023255552l);
- longCheckRem("lrem_by_pow2_42", lrem_by_pow2_42(x), x, 4398046511104l);
- longCheckRem("lrem_by_pow2_43", lrem_by_pow2_43(x), x, 8796093022208l);
- longCheckRem("lrem_by_pow2_44", lrem_by_pow2_44(x), x, 17592186044416l);
- longCheckRem("lrem_by_pow2_45", lrem_by_pow2_45(x), x, 35184372088832l);
- longCheckRem("lrem_by_pow2_46", lrem_by_pow2_46(x), x, 70368744177664l);
- longCheckRem("lrem_by_pow2_47", lrem_by_pow2_47(x), x, 140737488355328l);
- longCheckRem("lrem_by_pow2_48", lrem_by_pow2_48(x), x, 281474976710656l);
- longCheckRem("lrem_by_pow2_49", lrem_by_pow2_49(x), x, 562949953421312l);
- longCheckRem("lrem_by_pow2_50", lrem_by_pow2_50(x), x, 1125899906842624l);
- longCheckRem("lrem_by_pow2_51", lrem_by_pow2_51(x), x, 2251799813685248l);
- longCheckRem("lrem_by_pow2_52", lrem_by_pow2_52(x), x, 4503599627370496l);
- longCheckRem("lrem_by_pow2_53", lrem_by_pow2_53(x), x, 9007199254740992l);
- longCheckRem("lrem_by_pow2_54", lrem_by_pow2_54(x), x, 18014398509481984l);
- longCheckRem("lrem_by_pow2_55", lrem_by_pow2_55(x), x, 36028797018963968l);
- longCheckRem("lrem_by_pow2_56", lrem_by_pow2_56(x), x, 72057594037927936l);
- longCheckRem("lrem_by_pow2_57", lrem_by_pow2_57(x), x, 144115188075855872l);
- longCheckRem("lrem_by_pow2_58", lrem_by_pow2_58(x), x, 288230376151711744l);
- longCheckRem("lrem_by_pow2_59", lrem_by_pow2_59(x), x, 576460752303423488l);
- longCheckRem("lrem_by_pow2_60", lrem_by_pow2_60(x), x, 1152921504606846976l);
- longCheckRem("lrem_by_pow2_61", lrem_by_pow2_61(x), x, 2305843009213693952l);
- longCheckRem("lrem_by_pow2_62", lrem_by_pow2_62(x), x, 4611686018427387904l);
- }
-
- public static void main(String[] args) {
- int i;
- long l;
-
- System.out.println("Begin");
-
- System.out.println("Int: checking some equally spaced dividends...");
- for (i = -1000; i < 1000; i += 300) {
- intCheckAll(i);
- intCheckAll(-i);
- }
-
- System.out.println("Int: checking small dividends...");
- for (i = 1; i < 100; i += 1) {
- intCheckAll(i);
- intCheckAll(-i);
- }
-
- System.out.println("Int: checking big dividends...");
- for (i = 0; i < 100; i += 1) {
- intCheckAll(Integer.MAX_VALUE - i);
- intCheckAll(Integer.MIN_VALUE + i);
- }
-
- System.out.println("Long: checking some equally spaced dividends...");
- for (l = 0l; l < 1000000000000l; l += 300000000000l) {
- longCheckAll(l);
- longCheckAll(-l);
- }
-
- System.out.println("Long: checking small dividends...");
- for (l = 1l; l < 100l; l += 1l) {
- longCheckAll(l);
- longCheckAll(-l);
- }
-
- System.out.println("Long: checking big dividends...");
- for (l = 0l; l < 100l; l += 1l) {
- longCheckAll(Long.MAX_VALUE - l);
- longCheckAll(Long.MIN_VALUE + l);
- }
-
- System.out.println("End");
- }
-}
diff --git a/test/702-LargeBranchOffset/build b/test/702-LargeBranchOffset/build
index eacf730..20030fa 100644
--- a/test/702-LargeBranchOffset/build
+++ b/test/702-LargeBranchOffset/build
@@ -17,11 +17,7 @@
# Stop if something fails.
set -e
-# Write out a bunch of source files.
+# Write out the source file.
cpp -P src/Main.java.in src/Main.java
-mkdir classes
-${JAVAC} -d classes src/*.java
-
-${DX} --debug --dex --output=classes.dex classes
-zip $TEST_NAME.jar classes.dex
+./default-build
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index 6ad8787..57d06c4 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -28,6 +28,7 @@
116-nodex2oat/nodex2oat.cc \
117-nopatchoat/nopatchoat.cc \
118-noimage-dex2oat/noimage-dex2oat.cc \
+ 137-cfi/cfi.cc \
139-register-natives/regnative.cc \
454-get-vreg/get_vreg_jni.cc \
455-set-vreg/set_vreg_jni.cc \
@@ -57,7 +58,7 @@
LOCAL_MODULE_TAGS := tests
endif
LOCAL_SRC_FILES := $(LIBARTTEST_COMMON_SRC_FILES)
- LOCAL_SHARED_LIBRARIES += libartd
+ LOCAL_SHARED_LIBRARIES += libartd libbacktrace
LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.libarttest.mk
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index f6c9231..8b074ae 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -264,10 +264,12 @@
117-nopatchoat \
118-noimage-dex2oat \
119-noimage-patchoat \
+ 137-cfi \
138-duplicate-classes-check2
# This test fails without an image.
TEST_ART_BROKEN_NO_IMAGE_RUN_TESTS := \
+ 137-cfi \
138-duplicate-classes-check
ifneq (,$(filter no-dex2oat,$(PREBUILD_TYPES)))
@@ -294,9 +296,13 @@
TEST_ART_BROKEN_FALLBACK_RUN_TESTS :=
+# 137:
+# This test unrolls and expects managed frames, but tracing means we run the interpreter.
+# 802:
# 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 := \
+ 137-cfi \
802-deoptimization
ifneq (,$(filter trace,$(TRACE_TYPES)))
@@ -323,6 +329,7 @@
118-noimage-dex2oat \
119-noimage-patchoat \
131-structural-change \
+ 137-cfi \
139-register-natives \
454-get-vreg \
455-set-vreg \
@@ -338,6 +345,33 @@
TEST_ART_BROKEN_NDEBUG_TESTS :=
+# Known broken tests for the interpreter.
+# CFI unwinding expects managed frames.
+TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := \
+ 137-cfi
+
+ifneq (,$(filter interpreter,$(COMPILER_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+ interpreter,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_INTERPRETER_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_INTERPRETER_RUN_TESTS :=
+
+# Known broken tests for the JIT.
+# CFI unwinding expects managed frames, and the test does not iterate enough to even compile. JIT
+# also uses Generic JNI instead of the JNI compiler.
+TEST_ART_BROKEN_JIT_RUN_TESTS := \
+ 137-cfi
+
+ifneq (,$(filter jit,$(COMPILER_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+ jit,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_JIT_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_JIT_RUN_TESTS :=
+
# Known broken tests for the default compiler (Quick).
TEST_ART_BROKEN_DEFAULT_RUN_TESTS := \
457-regs
@@ -429,8 +463,9 @@
TEST_ART_BROKEN_OPTIMIZING_DEBUGGABLE_RUN_TESTS :=
# Tests that should fail in the read barrier configuration.
+# 137: Read barrier forces interpreter. Cannot run this with the interpreter.
TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS := \
- 098-ddmc # b/20720510
+ 137-cfi
ifeq ($(ART_USE_READ_BARRIER),true)
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -440,6 +475,24 @@
TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS :=
+# Tests that should fail in the heap poisoning configuration.
+# 137: Heap poisoning forces interpreter. Cannot run this with the interpreter.
+TEST_ART_BROKEN_HEAP_POISONING_RUN_TESTS := \
+ 137-cfi
+
+ifeq ($(ART_HEAP_POISONING),true)
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_HEAP_POISONING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_HEAP_POISONING_RUN_TESTS :=
+
+# Test 137-cfi works in 32-bit only until we enable 64-bit ELF files.
+ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),137-cfi,64)
+
# Clear variables ahead of appending to them when defining tests.
$(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=))
$(foreach target, $(TARGET_TYPES), \
diff --git a/test/run-test b/test/run-test
index dc60eda..ed33099 100755
--- a/test/run-test
+++ b/test/run-test
@@ -39,7 +39,7 @@
else
tmp_dir="${TMPDIR}/$USER/${test_dir}"
fi
-checker="${progdir}/../tools/checker.py"
+checker="${progdir}/../tools/checker/checker.py"
export JAVA="java"
export JAVAC="javac -g"
@@ -75,7 +75,7 @@
check_cmd="check"
output="output.txt"
build_output="build-output.txt"
-cfg_output="cfg-output.txt"
+cfg_output="graph.cfg"
lib="libartd.so"
run_args="--quiet"
build_args=""
@@ -504,14 +504,20 @@
if [ '!' -r "$build" ]; then
cp "${progdir}/etc/default-build" build
+else
+ cp "${progdir}/etc/default-build" .
fi
if [ '!' -r "$run" ]; then
cp "${progdir}/etc/default-run" run
+else
+ cp "${progdir}/etc/default-run" .
fi
if [ '!' -r "$check_cmd" ]; then
cp "${progdir}/etc/default-check" check
+else
+ cp "${progdir}/etc/default-check" .
fi
chmod 755 "$build"
diff --git a/tools/checker.py b/tools/checker.py
deleted file mode 100755
index 0bce236..0000000
--- a/tools/checker.py
+++ /dev/null
@@ -1,777 +0,0 @@
-#!/usr/bin/env python2
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-# Checker is a testing tool which compiles a given test file and compares the
-# state of the control-flow graph before and after each optimization pass
-# against a set of assertions specified alongside the tests.
-#
-# Tests are written in Java, turned into DEX and compiled with the Optimizing
-# compiler. "Check lines" are assertions formatted as comments of the Java file.
-# They begin with prefix 'CHECK' followed by a pattern that the engine attempts
-# to match in the compiler-generated output.
-#
-# Assertions are tested in groups which correspond to the individual compiler
-# passes. Each group of check lines therefore must start with a 'CHECK-START'
-# header which specifies the output group it should be tested against. The group
-# name must exactly match one of the groups recognized in the output (they can
-# be listed with the '--list-groups' command-line flag).
-#
-# Matching of check lines is carried out in the order of appearance in the
-# source file. There are three types of check lines:
-# - CHECK: Must match an output line which appears in the output group
-# later than lines matched against any preceeding checks. Output
-# lines must therefore match the check lines in the same order.
-# These are referred to as "in-order" checks in the code.
-# - CHECK-DAG: Must match an output line which appears in the output group
-# later than lines matched against any preceeding in-order checks.
-# In other words, the order of output lines does not matter
-# between consecutive DAG checks.
-# - CHECK-NOT: Must not match any output line which appears in the output group
-# later than lines matched against any preceeding checks and
-# earlier than lines matched against any subsequent checks.
-# Surrounding non-negative checks (or boundaries of the group)
-# therefore create a scope within which the assertion is verified.
-#
-# Check-line patterns are treated as plain text rather than regular expressions
-# but are whitespace agnostic.
-#
-# Actual regex patterns can be inserted enclosed in '{{' and '}}' brackets. If
-# curly brackets need to be used inside the body of the regex, they need to be
-# enclosed in round brackets. For example, the pattern '{{foo{2}}}' will parse
-# the invalid regex 'foo{2', but '{{(fo{2})}}' will match 'foo'.
-#
-# Regex patterns can be named and referenced later. A new variable is defined
-# with '[[name:regex]]' and can be referenced with '[[name]]'. Variables are
-# only valid within the scope of the defining group. Within a group they cannot
-# be redefined or used undefined.
-#
-# Example:
-# The following assertions can be placed in a Java source file:
-#
-# // CHECK-START: int MyClass.MyMethod() constant_folding (after)
-# // CHECK: [[ID:i[0-9]+]] IntConstant {{11|22}}
-# // CHECK: Return [ [[ID]] ]
-#
-# The engine will attempt to match the check lines against the output of the
-# group named on the first line. Together they verify that the CFG after
-# constant folding returns an integer constant with value either 11 or 22.
-#
-
-from __future__ import print_function
-import argparse
-import os
-import re
-import shutil
-import sys
-import tempfile
-
-class Logger(object):
-
- class Level(object):
- NoOutput, Error, Info = range(3)
-
- class Color(object):
- Default, Blue, Gray, Purple, Red = range(5)
-
- @staticmethod
- def terminalCode(color, out=sys.stdout):
- if not out.isatty():
- return ''
- elif color == Logger.Color.Blue:
- return '\033[94m'
- elif color == Logger.Color.Gray:
- return '\033[37m'
- elif color == Logger.Color.Purple:
- return '\033[95m'
- elif color == Logger.Color.Red:
- return '\033[91m'
- else:
- return '\033[0m'
-
- Verbosity = Level.Info
-
- @staticmethod
- def log(text, level=Level.Info, color=Color.Default, newLine=True, out=sys.stdout):
- if level <= Logger.Verbosity:
- text = Logger.Color.terminalCode(color, out) + text + \
- Logger.Color.terminalCode(Logger.Color.Default, out)
- if newLine:
- print(text, file=out)
- else:
- print(text, end="", file=out)
- out.flush()
-
- @staticmethod
- def fail(msg, file=None, line=-1):
- location = ""
- if file:
- location += file + ":"
- if line > 0:
- location += str(line) + ":"
- if location:
- location += " "
-
- Logger.log(location, Logger.Level.Error, color=Logger.Color.Gray, newLine=False, out=sys.stderr)
- Logger.log("error: ", Logger.Level.Error, color=Logger.Color.Red, newLine=False, out=sys.stderr)
- Logger.log(msg, Logger.Level.Error, out=sys.stderr)
- sys.exit(msg)
-
- @staticmethod
- def startTest(name):
- Logger.log("TEST ", color=Logger.Color.Purple, newLine=False)
- Logger.log(name + "... ", newLine=False)
-
- @staticmethod
- def testPassed():
- Logger.log("PASS", color=Logger.Color.Blue)
-
- @staticmethod
- def testFailed(msg, file=None, line=-1):
- Logger.log("FAIL", color=Logger.Color.Red)
- Logger.fail(msg, file, line)
-
-class CommonEqualityMixin:
- """Mixin for class equality as equality of the fields."""
- def __eq__(self, other):
- return (isinstance(other, self.__class__)
- and self.__dict__ == other.__dict__)
-
- def __ne__(self, other):
- return not self.__eq__(other)
-
- def __repr__(self):
- return "<%s: %s>" % (type(self).__name__, str(self.__dict__))
-
-
-class CheckElement(CommonEqualityMixin):
- """Single element of the check line."""
-
- class Variant(object):
- """Supported language constructs."""
- Text, Pattern, VarRef, VarDef, Separator = range(5)
-
- rStartOptional = r"("
- rEndOptional = r")?"
-
- rName = r"([a-zA-Z][a-zA-Z0-9]*)"
- rRegex = r"(.+?)"
- rPatternStartSym = r"(\{\{)"
- rPatternEndSym = r"(\}\})"
- rVariableStartSym = r"(\[\[)"
- rVariableEndSym = r"(\]\])"
- rVariableSeparator = r"(:)"
-
- regexPattern = rPatternStartSym + rRegex + rPatternEndSym
- regexVariable = rVariableStartSym + \
- rName + \
- (rStartOptional + rVariableSeparator + rRegex + rEndOptional) + \
- rVariableEndSym
-
- def __init__(self, variant, name, pattern):
- self.variant = variant
- self.name = name
- self.pattern = pattern
-
- @staticmethod
- def newSeparator():
- return CheckElement(CheckElement.Variant.Separator, None, None)
-
- @staticmethod
- def parseText(text):
- return CheckElement(CheckElement.Variant.Text, None, re.escape(text))
-
- @staticmethod
- def parsePattern(patternElem):
- return CheckElement(CheckElement.Variant.Pattern, None, patternElem[2:-2])
-
- @staticmethod
- def parseVariable(varElem):
- colonPos = varElem.find(":")
- if colonPos == -1:
- # Variable reference
- name = varElem[2:-2]
- return CheckElement(CheckElement.Variant.VarRef, name, None)
- else:
- # Variable definition
- name = varElem[2:colonPos]
- body = varElem[colonPos+1:-2]
- return CheckElement(CheckElement.Variant.VarDef, name, body)
-
-class CheckLine(CommonEqualityMixin):
- """Representation of a single assertion in the check file formed of one or
- more regex elements. Matching against an output line is successful only
- if all regex elements can be matched in the given order."""
-
- class Variant(object):
- """Supported types of assertions."""
- InOrder, DAG, Not = range(3)
-
- def __init__(self, content, variant=Variant.InOrder, fileName=None, lineNo=-1):
- self.fileName = fileName
- self.lineNo = lineNo
- self.content = content.strip()
-
- self.variant = variant
- self.lineParts = self.__parse(self.content)
- if not self.lineParts:
- Logger.fail("Empty check line", self.fileName, self.lineNo)
-
- if self.variant == CheckLine.Variant.Not:
- for elem in self.lineParts:
- if elem.variant == CheckElement.Variant.VarDef:
- Logger.fail("CHECK-NOT lines cannot define variables", self.fileName, self.lineNo)
-
- def __eq__(self, other):
- return (isinstance(other, self.__class__) and
- self.variant == other.variant and
- self.lineParts == other.lineParts)
-
- # Returns True if the given Match object was at the beginning of the line.
- def __isMatchAtStart(self, match):
- return (match is not None) and (match.start() == 0)
-
- # Takes in a list of Match objects and returns the minimal start point among
- # them. If there aren't any successful matches it returns the length of
- # the searched string.
- def __firstMatch(self, matches, string):
- starts = map(lambda m: len(string) if m is None else m.start(), matches)
- return min(starts)
-
- # This method parses the content of a check line stripped of the initial
- # comment symbol and the CHECK keyword.
- def __parse(self, line):
- lineParts = []
- # Loop as long as there is something to parse.
- while line:
- # Search for the nearest occurrence of the special markers.
- matchWhitespace = re.search(r"\s+", line)
- matchPattern = re.search(CheckElement.regexPattern, line)
- matchVariable = re.search(CheckElement.regexVariable, line)
-
- # If one of the above was identified at the current position, extract them
- # from the line, parse them and add to the list of line parts.
- if self.__isMatchAtStart(matchWhitespace):
- # A whitespace in the check line creates a new separator of line parts.
- # This allows for ignored output between the previous and next parts.
- line = line[matchWhitespace.end():]
- lineParts.append(CheckElement.newSeparator())
- elif self.__isMatchAtStart(matchPattern):
- pattern = line[0:matchPattern.end()]
- line = line[matchPattern.end():]
- lineParts.append(CheckElement.parsePattern(pattern))
- elif self.__isMatchAtStart(matchVariable):
- var = line[0:matchVariable.end()]
- line = line[matchVariable.end():]
- lineParts.append(CheckElement.parseVariable(var))
- else:
- # If we're not currently looking at a special marker, this is a plain
- # text match all the way until the first special marker (or the end
- # of the line).
- firstMatch = self.__firstMatch([ matchWhitespace, matchPattern, matchVariable ], line)
- text = line[0:firstMatch]
- line = line[firstMatch:]
- lineParts.append(CheckElement.parseText(text))
- return lineParts
-
- # Returns the regex pattern to be matched in the output line. Variable
- # references are substituted with their current values provided in the
- # 'varState' argument.
- # An exception is raised if a referenced variable is undefined.
- def __generatePattern(self, linePart, varState):
- if linePart.variant == CheckElement.Variant.VarRef:
- try:
- return re.escape(varState[linePart.name])
- except KeyError:
- Logger.testFailed("Use of undefined variable \"" + linePart.name + "\"",
- self.fileName, self.lineNo)
- else:
- return linePart.pattern
-
- def __isSeparated(self, outputLine, matchStart):
- return (matchStart == 0) or (outputLine[matchStart - 1:matchStart].isspace())
-
- # Attempts to match the check line against a line from the output file with
- # the given initial variable values. It returns the new variable state if
- # successful and None otherwise.
- def match(self, outputLine, initialVarState):
- # Do the full matching on a shadow copy of the variable state. If the
- # matching fails half-way, we will not need to revert the state.
- varState = dict(initialVarState)
-
- matchStart = 0
- isAfterSeparator = True
-
- # Now try to parse all of the parts of the check line in the right order.
- # Variable values are updated on-the-fly, meaning that a variable can
- # be referenced immediately after its definition.
- for part in self.lineParts:
- if part.variant == CheckElement.Variant.Separator:
- isAfterSeparator = True
- continue
-
- # Find the earliest match for this line part.
- pattern = self.__generatePattern(part, varState)
- while True:
- match = re.search(pattern, outputLine[matchStart:])
- if (match is None) or (not isAfterSeparator and not self.__isMatchAtStart(match)):
- return None
- matchEnd = matchStart + match.end()
- matchStart += match.start()
-
- # Check if this is a valid match if we expect a whitespace separator
- # before the matched text. Otherwise loop and look for another match.
- if not isAfterSeparator or self.__isSeparated(outputLine, matchStart):
- break
- else:
- matchStart += 1
-
- if part.variant == CheckElement.Variant.VarDef:
- if part.name in varState:
- Logger.testFailed("Multiple definitions of variable \"" + part.name + "\"",
- self.fileName, self.lineNo)
- varState[part.name] = outputLine[matchStart:matchEnd]
-
- matchStart = matchEnd
- isAfterSeparator = False
-
- # All parts were successfully matched. Return the new variable state.
- return varState
-
-
-class CheckGroup(CommonEqualityMixin):
- """Represents a named collection of check lines which are to be matched
- against an output group of the same name."""
-
- def __init__(self, name, lines, fileName=None, lineNo=-1):
- self.fileName = fileName
- self.lineNo = lineNo
-
- if not name:
- Logger.fail("Check group does not have a name", self.fileName, self.lineNo)
- if not lines:
- Logger.fail("Check group does not have a body", self.fileName, self.lineNo)
-
- self.name = name
- self.lines = lines
-
- def __eq__(self, other):
- return (isinstance(other, self.__class__) and
- self.name == other.name and
- self.lines == other.lines)
-
- def __headAndTail(self, list):
- return list[0], list[1:]
-
- # Splits a list of check lines at index 'i' such that lines[i] is the first
- # element whose variant is not equal to the given parameter.
- def __splitByVariant(self, lines, variant):
- i = 0
- while i < len(lines) and lines[i].variant == variant:
- i += 1
- return lines[:i], lines[i:]
-
- # Extracts the first sequence of check lines which are independent of each
- # other's match location, i.e. either consecutive DAG lines or a single
- # InOrder line. Any Not lines preceeding this sequence are also extracted.
- def __nextIndependentChecks(self, checkLines):
- notChecks, checkLines = self.__splitByVariant(checkLines, CheckLine.Variant.Not)
- if not checkLines:
- return notChecks, [], []
-
- head, tail = self.__headAndTail(checkLines)
- if head.variant == CheckLine.Variant.InOrder:
- return notChecks, [head], tail
- else:
- assert head.variant == CheckLine.Variant.DAG
- independentChecks, checkLines = self.__splitByVariant(checkLines, CheckLine.Variant.DAG)
- return notChecks, independentChecks, checkLines
-
- # If successful, returns the line number of the first output line matching the
- # check line and the updated variable state. Otherwise returns -1 and None,
- # respectively. The 'lineFilter' parameter can be used to supply a list of
- # line numbers (counting from 1) which should be skipped.
- def __findFirstMatch(self, checkLine, outputLines, startLineNo, lineFilter, varState):
- matchLineNo = startLineNo
- for outputLine in outputLines:
- if matchLineNo not in lineFilter:
- newVarState = checkLine.match(outputLine, varState)
- if newVarState is not None:
- return matchLineNo, newVarState
- matchLineNo += 1
- return -1, None
-
- # Matches the given positive check lines against the output in order of
- # appearance. Variable state is propagated but the scope of the search remains
- # the same for all checks. Each output line can only be matched once.
- # If all check lines are matched, the resulting variable state is returned
- # together with the remaining output. The function also returns output lines
- # which appear before either of the matched lines so they can be tested
- # against Not checks.
- def __matchIndependentChecks(self, checkLines, outputLines, startLineNo, varState):
- # If no checks are provided, skip over the entire output.
- if not checkLines:
- return outputLines, [], startLineNo + len(outputLines), varState
-
- # Keep track of which lines have been matched.
- matchedLines = []
-
- # Find first unused output line which matches each check line.
- for checkLine in checkLines:
- matchLineNo, varState = \
- self.__findFirstMatch(checkLine, outputLines, startLineNo, matchedLines, varState)
- if varState is None:
- Logger.testFailed("Could not match check line \"" + checkLine.content + "\" " +
- "starting from output line " + str(startLineNo),
- self.fileName, checkLine.lineNo)
- matchedLines.append(matchLineNo)
-
- # Return new variable state and the output lines which lie outside the
- # match locations of this independent group.
- minMatchLineNo = min(matchedLines)
- maxMatchLineNo = max(matchedLines)
- preceedingLines = outputLines[:minMatchLineNo - startLineNo]
- remainingLines = outputLines[maxMatchLineNo - startLineNo + 1:]
- return preceedingLines, remainingLines, maxMatchLineNo + 1, varState
-
- # Makes sure that the given check lines do not match any of the given output
- # lines. Variable state does not change.
- def __matchNotLines(self, checkLines, outputLines, startLineNo, varState):
- for checkLine in checkLines:
- assert checkLine.variant == CheckLine.Variant.Not
- matchLineNo, matchVarState = \
- self.__findFirstMatch(checkLine, outputLines, startLineNo, [], varState)
- if matchVarState is not None:
- Logger.testFailed("CHECK-NOT line \"" + checkLine.content + "\" matches output line " + \
- str(matchLineNo), self.fileName, checkLine.lineNo)
-
- # Matches the check lines in this group against an output group. It is
- # responsible for running the checks in the right order and scope, and
- # for propagating the variable state between the check lines.
- def match(self, outputGroup):
- varState = {}
- checkLines = self.lines
- outputLines = outputGroup.body
- startLineNo = outputGroup.lineNo
-
- while checkLines:
- # Extract the next sequence of location-independent checks to be matched.
- notChecks, independentChecks, checkLines = self.__nextIndependentChecks(checkLines)
-
- # Match the independent checks.
- notOutput, outputLines, newStartLineNo, newVarState = \
- self.__matchIndependentChecks(independentChecks, outputLines, startLineNo, varState)
-
- # Run the Not checks against the output lines which lie between the last
- # two independent groups or the bounds of the output.
- self.__matchNotLines(notChecks, notOutput, startLineNo, varState)
-
- # Update variable state.
- startLineNo = newStartLineNo
- varState = newVarState
-
-class OutputGroup(CommonEqualityMixin):
- """Represents a named part of the test output against which a check group of
- the same name is to be matched."""
-
- def __init__(self, name, body, fileName=None, lineNo=-1):
- if not name:
- Logger.fail("Output group does not have a name", fileName, lineNo)
- if not body:
- Logger.fail("Output group does not have a body", fileName, lineNo)
-
- self.name = name
- self.body = body
- self.lineNo = lineNo
-
- def __eq__(self, other):
- return (isinstance(other, self.__class__) and
- self.name == other.name and
- self.body == other.body)
-
-
-class FileSplitMixin(object):
- """Mixin for representing text files which need to be split into smaller
- chunks before being parsed."""
-
- def _parseStream(self, stream):
- lineNo = 0
- allGroups = []
- currentGroup = None
-
- for line in stream:
- lineNo += 1
- line = line.strip()
- if not line:
- continue
-
- # Let the child class process the line and return information about it.
- # The _processLine method can modify the content of the line (or delete it
- # entirely) and specify whether it starts a new group.
- processedLine, newGroupName = self._processLine(line, lineNo)
- if newGroupName is not None:
- currentGroup = (newGroupName, [], lineNo)
- allGroups.append(currentGroup)
- if processedLine is not None:
- if currentGroup is not None:
- currentGroup[1].append(processedLine)
- else:
- self._exceptionLineOutsideGroup(line, lineNo)
-
- # Finally, take the generated line groups and let the child class process
- # each one before storing the final outcome.
- return list(map(lambda group: self._processGroup(group[0], group[1], group[2]), allGroups))
-
-
-class CheckFile(FileSplitMixin):
- """Collection of check groups extracted from the input test file."""
-
- def __init__(self, prefix, checkStream, fileName=None):
- self.fileName = fileName
- self.prefix = prefix
- self.groups = self._parseStream(checkStream)
-
- # Attempts to parse a check line. The regex searches for a comment symbol
- # followed by the CHECK keyword, given attribute and a colon at the very
- # beginning of the line. Whitespaces are ignored.
- def _extractLine(self, prefix, line):
- rIgnoreWhitespace = r"\s*"
- rCommentSymbols = [r"//", r"#"]
- regexPrefix = rIgnoreWhitespace + \
- r"(" + r"|".join(rCommentSymbols) + r")" + \
- rIgnoreWhitespace + \
- prefix + r":"
-
- # The 'match' function succeeds only if the pattern is matched at the
- # beginning of the line.
- match = re.match(regexPrefix, line)
- if match is not None:
- return line[match.end():].strip()
- else:
- return None
-
- # This function is invoked on each line of the check file and returns a pair
- # which instructs the parser how the line should be handled. If the line is to
- # be included in the current check group, it is returned in the first value.
- # If the line starts a new check group, the name of the group is returned in
- # the second value.
- def _processLine(self, line, lineNo):
- # Lines beginning with 'CHECK-START' start a new check group.
- startLine = self._extractLine(self.prefix + "-START", line)
- if startLine is not None:
- return None, startLine
-
- # Lines starting only with 'CHECK' are matched in order.
- plainLine = self._extractLine(self.prefix, line)
- if plainLine is not None:
- return (plainLine, CheckLine.Variant.InOrder, lineNo), None
-
- # 'CHECK-DAG' lines are no-order assertions.
- dagLine = self._extractLine(self.prefix + "-DAG", line)
- if dagLine is not None:
- return (dagLine, CheckLine.Variant.DAG, lineNo), None
-
- # 'CHECK-NOT' lines are no-order negative assertions.
- notLine = self._extractLine(self.prefix + "-NOT", line)
- if notLine is not None:
- return (notLine, CheckLine.Variant.Not, lineNo), None
-
- # Other lines are ignored.
- return None, None
-
- def _exceptionLineOutsideGroup(self, line, lineNo):
- Logger.fail("Check line not inside a group", self.fileName, lineNo)
-
- # Constructs a check group from the parser-collected check lines.
- def _processGroup(self, name, lines, lineNo):
- checkLines = list(map(lambda line: CheckLine(line[0], line[1], self.fileName, line[2]), lines))
- return CheckGroup(name, checkLines, self.fileName, lineNo)
-
- def match(self, outputFile):
- for checkGroup in self.groups:
- # TODO: Currently does not handle multiple occurrences of the same group
- # name, e.g. when a pass is run multiple times. It will always try to
- # match a check group against the first output group of the same name.
- outputGroup = outputFile.findGroup(checkGroup.name)
- if outputGroup is None:
- Logger.fail("Group \"" + checkGroup.name + "\" not found in the output",
- self.fileName, checkGroup.lineNo)
- Logger.startTest(checkGroup.name)
- checkGroup.match(outputGroup)
- Logger.testPassed()
-
-
-class OutputFile(FileSplitMixin):
- """Representation of the output generated by the test and split into groups
- within which the checks are performed.
-
- C1visualizer format is parsed with a state machine which differentiates
- between the 'compilation' and 'cfg' blocks. The former marks the beginning
- of a method. It is parsed for the method's name but otherwise ignored. Each
- subsequent CFG block represents one stage of the compilation pipeline and
- is parsed into an output group named "<method name> <pass name>".
- """
-
- class ParsingState:
- OutsideBlock, InsideCompilationBlock, StartingCfgBlock, InsideCfgBlock = range(4)
-
- def __init__(self, outputStream, fileName=None):
- self.fileName = fileName
-
- # Initialize the state machine
- self.lastMethodName = None
- self.state = OutputFile.ParsingState.OutsideBlock
- self.groups = self._parseStream(outputStream)
-
- # This function is invoked on each line of the output file and returns a pair
- # which instructs the parser how the line should be handled. If the line is to
- # be included in the current group, it is returned in the first value. If the
- # line starts a new output group, the name of the group is returned in the
- # second value.
- def _processLine(self, line, lineNo):
- if self.state == OutputFile.ParsingState.StartingCfgBlock:
- # Previous line started a new 'cfg' block which means that this one must
- # contain the name of the pass (this is enforced by C1visualizer).
- if re.match("name\s+\"[^\"]+\"", line):
- # Extract the pass name, prepend it with the name of the method and
- # return as the beginning of a new group.
- self.state = OutputFile.ParsingState.InsideCfgBlock
- return (None, self.lastMethodName + " " + line.split("\"")[1])
- else:
- Logger.fail("Expected output group name", self.fileName, lineNo)
-
- elif self.state == OutputFile.ParsingState.InsideCfgBlock:
- if line == "end_cfg":
- self.state = OutputFile.ParsingState.OutsideBlock
- return (None, None)
- else:
- return (line, None)
-
- elif self.state == OutputFile.ParsingState.InsideCompilationBlock:
- # Search for the method's name. Format: method "<name>"
- if re.match("method\s+\"[^\"]*\"", line):
- methodName = line.split("\"")[1].strip()
- if not methodName:
- Logger.fail("Empty method name in output", self.fileName, lineNo)
- self.lastMethodName = methodName
- elif line == "end_compilation":
- self.state = OutputFile.ParsingState.OutsideBlock
- return (None, None)
-
- else:
- assert self.state == OutputFile.ParsingState.OutsideBlock
- if line == "begin_cfg":
- # The line starts a new group but we'll wait until the next line from
- # which we can extract the name of the pass.
- if self.lastMethodName is None:
- Logger.fail("Expected method header", self.fileName, lineNo)
- self.state = OutputFile.ParsingState.StartingCfgBlock
- return (None, None)
- elif line == "begin_compilation":
- self.state = OutputFile.ParsingState.InsideCompilationBlock
- return (None, None)
- else:
- Logger.fail("Output line not inside a group", self.fileName, lineNo)
-
- # Constructs an output group from the parser-collected output lines.
- def _processGroup(self, name, lines, lineNo):
- return OutputGroup(name, lines, self.fileName, lineNo + 1)
-
- def findGroup(self, name):
- for group in self.groups:
- if group.name == name:
- return group
- return None
-
-
-def ParseArguments():
- parser = argparse.ArgumentParser()
- parser.add_argument("tested_file",
- help="text file the checks should be verified against")
- parser.add_argument("source_path", nargs="?",
- help="path to file/folder with checking annotations")
- parser.add_argument("--check-prefix", dest="check_prefix", default="CHECK", metavar="PREFIX",
- help="prefix of checks in the test files (default: CHECK)")
- parser.add_argument("--list-groups", dest="list_groups", action="store_true",
- help="print a list of all groups found in the tested file")
- parser.add_argument("--dump-group", dest="dump_group", metavar="GROUP",
- help="print the contents of an output group")
- parser.add_argument("-q", "--quiet", action="store_true",
- help="print only errors")
- return parser.parse_args()
-
-
-def ListGroups(outputFilename):
- outputFile = OutputFile(open(outputFilename, "r"))
- for group in outputFile.groups:
- Logger.log(group.name)
-
-
-def DumpGroup(outputFilename, groupName):
- outputFile = OutputFile(open(outputFilename, "r"))
- group = outputFile.findGroup(groupName)
- if group:
- lineNo = group.lineNo
- maxLineNo = lineNo + len(group.body)
- lenLineNo = len(str(maxLineNo)) + 2
- for line in group.body:
- Logger.log((str(lineNo) + ":").ljust(lenLineNo) + line)
- lineNo += 1
- else:
- Logger.fail("Group \"" + groupName + "\" not found in the output")
-
-
-# Returns a list of files to scan for check annotations in the given path. Path
-# to a file is returned as a single-element list, directories are recursively
-# traversed and all '.java' files returned.
-def FindCheckFiles(path):
- if not path:
- Logger.fail("No source path provided")
- elif os.path.isfile(path):
- return [ path ]
- elif os.path.isdir(path):
- foundFiles = []
- for root, dirs, files in os.walk(path):
- for file in files:
- if os.path.splitext(file)[1] == ".java":
- foundFiles.append(os.path.join(root, file))
- return foundFiles
- else:
- Logger.fail("Source path \"" + path + "\" not found")
-
-
-def RunChecks(checkPrefix, checkPath, outputFilename):
- outputBaseName = os.path.basename(outputFilename)
- outputFile = OutputFile(open(outputFilename, "r"), outputBaseName)
-
- for checkFilename in FindCheckFiles(checkPath):
- checkBaseName = os.path.basename(checkFilename)
- checkFile = CheckFile(checkPrefix, open(checkFilename, "r"), checkBaseName)
- checkFile.match(outputFile)
-
-
-if __name__ == "__main__":
- args = ParseArguments()
-
- if args.quiet:
- Logger.Verbosity = Logger.Level.Error
-
- if args.list_groups:
- ListGroups(args.tested_file)
- elif args.dump_group:
- DumpGroup(args.tested_file, args.dump_group)
- else:
- RunChecks(args.check_prefix, args.source_path, args.tested_file)
diff --git a/tools/checker/README b/tools/checker/README
new file mode 100644
index 0000000..858a773
--- /dev/null
+++ b/tools/checker/README
@@ -0,0 +1,54 @@
+Checker is a testing tool which compiles a given test file and compares the
+state of the control-flow graph before and after each optimization pass
+against a set of assertions specified alongside the tests.
+
+Tests are written in Java, turned into DEX and compiled with the Optimizing
+compiler. "Check lines" are assertions formatted as comments of the Java file.
+They begin with prefix 'CHECK' followed by a pattern that the engine attempts
+to match in the compiler-generated output.
+
+Assertions are tested in groups which correspond to the individual compiler
+passes. Each group of check lines therefore must start with a 'CHECK-START'
+header which specifies the output group it should be tested against. The group
+name must exactly match one of the groups recognized in the output (they can
+be listed with the '--list-passes' command-line flag).
+
+Matching of check lines is carried out in the order of appearance in the
+source file. There are three types of check lines:
+ - CHECK: Must match an output line which appears in the output group
+ later than lines matched against any preceeding checks. Output
+ lines must therefore match the check lines in the same order.
+ These are referred to as "in-order" checks in the code.
+ - CHECK-DAG: Must match an output line which appears in the output group
+ later than lines matched against any preceeding in-order checks.
+ In other words, the order of output lines does not matter
+ between consecutive DAG checks.
+ - CHECK-NOT: Must not match any output line which appears in the output group
+ later than lines matched against any preceeding checks and
+ earlier than lines matched against any subsequent checks.
+ Surrounding non-negative checks (or boundaries of the group)
+ therefore create a scope within which the assertion is verified.
+
+Check-line patterns are treated as plain text rather than regular expressions
+but are whitespace agnostic.
+
+Actual regex patterns can be inserted enclosed in '{{' and '}}' brackets. If
+curly brackets need to be used inside the body of the regex, they need to be
+enclosed in round brackets. For example, the pattern '{{foo{2}}}' will parse
+the invalid regex 'foo{2', but '{{(fo{2})}}' will match 'foo'.
+
+Regex patterns can be named and referenced later. A new variable is defined
+with '<<name:regex>>' and can be referenced with '<<name>>'. Variables are
+only valid within the scope of the defining group. Within a group they cannot
+be redefined or used undefined.
+
+Example:
+ The following assertions can be placed in a Java source file:
+
+ // CHECK-START: int MyClass.MyMethod() constant_folding (after)
+ // CHECK: <<ID:i\d+>> IntConstant {{11|22}}
+ // CHECK: Return [<<ID>>]
+
+ The engine will attempt to match the check lines against the output of the
+ group named on the first line. Together they verify that the CFG after
+ constant folding returns an integer constant with value either 11 or 22.
diff --git a/tools/checker/checker.py b/tools/checker/checker.py
new file mode 100755
index 0000000..ed630e3
--- /dev/null
+++ b/tools/checker/checker.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python2
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import os
+
+from common.logger import Logger
+from file_format.c1visualizer.parser import ParseC1visualizerStream
+from file_format.checker.parser import ParseCheckerStream
+from match.file import MatchFiles
+
+def ParseArguments():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("tested_file",
+ help="text file the checks should be verified against")
+ parser.add_argument("source_path", nargs="?",
+ help="path to file/folder with checking annotations")
+ parser.add_argument("--check-prefix", dest="check_prefix", default="CHECK", metavar="PREFIX",
+ help="prefix of checks in the test files (default: CHECK)")
+ parser.add_argument("--list-passes", dest="list_passes", action="store_true",
+ help="print a list of all passes found in the tested file")
+ parser.add_argument("--dump-pass", dest="dump_pass", metavar="PASS",
+ help="print a compiler pass dump")
+ parser.add_argument("-q", "--quiet", action="store_true",
+ help="print only errors")
+ return parser.parse_args()
+
+
+def ListPasses(outputFilename):
+ c1File = ParseC1visualizerStream(os.path.basename(outputFilename), open(outputFilename, "r"))
+ for compiler_pass in c1File.passes:
+ Logger.log(compiler_pass.name)
+
+
+def DumpPass(outputFilename, passName):
+ c1File = ParseC1visualizerStream(os.path.basename(outputFilename), open(outputFilename, "r"))
+ compiler_pass = c1File.findPass(passName)
+ if compiler_pass:
+ maxLineNo = compiler_pass.startLineNo + len(compiler_pass.body)
+ lenLineNo = len(str(maxLineNo)) + 2
+ curLineNo = compiler_pass.startLineNo
+ for line in compiler_pass.body:
+ Logger.log((str(curLineNo) + ":").ljust(lenLineNo) + line)
+ curLineNo += 1
+ else:
+ Logger.fail("Pass \"" + passName + "\" not found in the output")
+
+
+def FindCheckerFiles(path):
+ """ Returns a list of files to scan for check annotations in the given path.
+ Path to a file is returned as a single-element list, directories are
+ recursively traversed and all '.java' files returned.
+ """
+ if not path:
+ Logger.fail("No source path provided")
+ elif os.path.isfile(path):
+ return [ path ]
+ elif os.path.isdir(path):
+ foundFiles = []
+ for root, dirs, files in os.walk(path):
+ for file in files:
+ extension = os.path.splitext(file)[1]
+ if extension in [".java", ".smali"]:
+ foundFiles.append(os.path.join(root, file))
+ return foundFiles
+ else:
+ Logger.fail("Source path \"" + path + "\" not found")
+
+
+def RunTests(checkPrefix, checkPath, outputFilename):
+ c1File = ParseC1visualizerStream(os.path.basename(outputFilename), open(outputFilename, "r"))
+ for checkFilename in FindCheckerFiles(checkPath):
+ checkerFile = ParseCheckerStream(os.path.basename(checkFilename),
+ checkPrefix,
+ open(checkFilename, "r"))
+ MatchFiles(checkerFile, c1File)
+
+
+if __name__ == "__main__":
+ args = ParseArguments()
+
+ if args.quiet:
+ Logger.Verbosity = Logger.Level.Error
+
+ if args.list_passes:
+ ListPasses(args.tested_file)
+ elif args.dump_pass:
+ DumpPass(args.tested_file, args.dump_pass)
+ else:
+ RunTests(args.check_prefix, args.source_path, args.tested_file)
diff --git a/tools/checker/common/__init__.py b/tools/checker/common/__init__.py
new file mode 100644
index 0000000..d0a140b
--- /dev/null
+++ b/tools/checker/common/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/tools/checker/common/immutables.py b/tools/checker/common/immutables.py
new file mode 100644
index 0000000..e016867
--- /dev/null
+++ b/tools/checker/common/immutables.py
@@ -0,0 +1,25 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+class ImmutableDict(dict):
+ def __setitem__(self, key, value):
+ raise RuntimeError("Cannot modify ImmutableDict")
+
+ def __delitem__(self, key):
+ raise RuntimeError("Cannot modify ImmutableDict")
+
+ def copyWith(self, key, value):
+ newDict = ImmutableDict(self)
+ dict.__setitem__(newDict, key, value)
+ return newDict
diff --git a/tools/checker/common/logger.py b/tools/checker/common/logger.py
new file mode 100644
index 0000000..28bb458
--- /dev/null
+++ b/tools/checker/common/logger.py
@@ -0,0 +1,81 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import print_function
+import sys
+
+class Logger(object):
+
+ class Level(object):
+ NoOutput, Error, Info = range(3)
+
+ class Color(object):
+ Default, Blue, Gray, Purple, Red = range(5)
+
+ @staticmethod
+ def terminalCode(color, out=sys.stdout):
+ if not out.isatty():
+ return ''
+ elif color == Logger.Color.Blue:
+ return '\033[94m'
+ elif color == Logger.Color.Gray:
+ return '\033[37m'
+ elif color == Logger.Color.Purple:
+ return '\033[95m'
+ elif color == Logger.Color.Red:
+ return '\033[91m'
+ else:
+ return '\033[0m'
+
+ Verbosity = Level.Info
+
+ @staticmethod
+ def log(text, level=Level.Info, color=Color.Default, newLine=True, out=sys.stdout):
+ if level <= Logger.Verbosity:
+ text = Logger.Color.terminalCode(color, out) + text + \
+ Logger.Color.terminalCode(Logger.Color.Default, out)
+ if newLine:
+ print(text, file=out)
+ else:
+ print(text, end="", file=out)
+ out.flush()
+
+ @staticmethod
+ def fail(msg, file=None, line=-1):
+ location = ""
+ if file:
+ location += file + ":"
+ if line > 0:
+ location += str(line) + ":"
+ if location:
+ location += " "
+
+ Logger.log(location, Logger.Level.Error, color=Logger.Color.Gray, newLine=False, out=sys.stderr)
+ Logger.log("error: ", Logger.Level.Error, color=Logger.Color.Red, newLine=False, out=sys.stderr)
+ Logger.log(msg, Logger.Level.Error, out=sys.stderr)
+ sys.exit(msg)
+
+ @staticmethod
+ def startTest(name):
+ Logger.log("TEST ", color=Logger.Color.Purple, newLine=False)
+ Logger.log(name + "... ", newLine=False)
+
+ @staticmethod
+ def testPassed():
+ Logger.log("PASS", color=Logger.Color.Blue)
+
+ @staticmethod
+ def testFailed(msg, file=None, line=-1):
+ Logger.log("FAIL", color=Logger.Color.Red)
+ Logger.fail(msg, file, line)
diff --git a/tools/checker/common/mixins.py b/tools/checker/common/mixins.py
new file mode 100644
index 0000000..819de24
--- /dev/null
+++ b/tools/checker/common/mixins.py
@@ -0,0 +1,26 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+class EqualityMixin:
+ """ Object equality via equality of dictionaries. """
+
+ def __eq__(self, other):
+ return isinstance(other, self.__class__) \
+ and self.__dict__ == other.__dict__
+
+class PrintableMixin:
+ """ Prints object as name-dictionary pair. """
+
+ def __repr__(self):
+ return "<%s: %s>" % (type(self).__name__, str(self.__dict__))
diff --git a/tools/checker/common/testing.py b/tools/checker/common/testing.py
new file mode 100644
index 0000000..1299c07
--- /dev/null
+++ b/tools/checker/common/testing.py
@@ -0,0 +1,22 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+def ToUnicode(string):
+ """ Converts a string into Unicode.
+
+ This is a delegate function for the built-in `unicode`. It checks if the input
+ is not `None`, because `unicode` turns it into an actual "None" string.
+ """
+ assert string is not None
+ return unicode(string)
diff --git a/tools/checker/file_format/__init__.py b/tools/checker/file_format/__init__.py
new file mode 100644
index 0000000..d0a140b
--- /dev/null
+++ b/tools/checker/file_format/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/tools/checker/file_format/c1visualizer/__init__.py b/tools/checker/file_format/c1visualizer/__init__.py
new file mode 100644
index 0000000..d0a140b
--- /dev/null
+++ b/tools/checker/file_format/c1visualizer/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/tools/checker/file_format/c1visualizer/parser.py b/tools/checker/file_format/c1visualizer/parser.py
new file mode 100644
index 0000000..335a195
--- /dev/null
+++ b/tools/checker/file_format/c1visualizer/parser.py
@@ -0,0 +1,87 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from common.logger import Logger
+from file_format.common import SplitStream
+from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass
+
+import re
+
+class C1ParserState:
+ OutsideBlock, InsideCompilationBlock, StartingCfgBlock, InsideCfgBlock = range(4)
+
+ def __init__(self):
+ self.currentState = C1ParserState.OutsideBlock
+ self.lastMethodName = None
+
+def __parseC1Line(line, lineNo, state, fileName):
+ """ This function is invoked on each line of the output file and returns
+ a pair which instructs the parser how the line should be handled. If the
+ line is to be included in the current group, it is returned in the first
+ value. If the line starts a new output group, the name of the group is
+ returned in the second value.
+ """
+ if state.currentState == C1ParserState.StartingCfgBlock:
+ # Previous line started a new 'cfg' block which means that this one must
+ # contain the name of the pass (this is enforced by C1visualizer).
+ if re.match("name\s+\"[^\"]+\"", line):
+ # Extract the pass name, prepend it with the name of the method and
+ # return as the beginning of a new group.
+ state.currentState = C1ParserState.InsideCfgBlock
+ return (None, state.lastMethodName + " " + line.split("\"")[1])
+ else:
+ Logger.fail("Expected output group name", fileName, lineNo)
+
+ elif state.currentState == C1ParserState.InsideCfgBlock:
+ if line == "end_cfg":
+ state.currentState = C1ParserState.OutsideBlock
+ return (None, None)
+ else:
+ return (line, None)
+
+ elif state.currentState == C1ParserState.InsideCompilationBlock:
+ # Search for the method's name. Format: method "<name>"
+ if re.match("method\s+\"[^\"]*\"", line):
+ methodName = line.split("\"")[1].strip()
+ if not methodName:
+ Logger.fail("Empty method name in output", fileName, lineNo)
+ state.lastMethodName = methodName
+ elif line == "end_compilation":
+ state.currentState = C1ParserState.OutsideBlock
+ return (None, None)
+
+ else:
+ assert state.currentState == C1ParserState.OutsideBlock
+ if line == "begin_cfg":
+ # The line starts a new group but we'll wait until the next line from
+ # which we can extract the name of the pass.
+ if state.lastMethodName is None:
+ Logger.fail("Expected method header", fileName, lineNo)
+ state.currentState = C1ParserState.StartingCfgBlock
+ return (None, None)
+ elif line == "begin_compilation":
+ state.currentState = C1ParserState.InsideCompilationBlock
+ return (None, None)
+ else:
+ Logger.fail("C1visualizer line not inside a group", fileName, lineNo)
+
+def ParseC1visualizerStream(fileName, stream):
+ c1File = C1visualizerFile(fileName)
+ state = C1ParserState()
+ fnProcessLine = lambda line, lineNo: __parseC1Line(line, lineNo, state, fileName)
+ fnLineOutsideChunk = lambda line, lineNo: \
+ Logger.fail("C1visualizer line not inside a group", fileName, lineNo)
+ for passName, passLines, startLineNo in SplitStream(stream, fnProcessLine, fnLineOutsideChunk):
+ C1visualizerPass(c1File, passName, passLines, startLineNo + 1)
+ return c1File
diff --git a/tools/checker/file_format/c1visualizer/struct.py b/tools/checker/file_format/c1visualizer/struct.py
new file mode 100644
index 0000000..991564e
--- /dev/null
+++ b/tools/checker/file_format/c1visualizer/struct.py
@@ -0,0 +1,60 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from common.logger import Logger
+from common.mixins import PrintableMixin
+
+class C1visualizerFile(PrintableMixin):
+
+ def __init__(self, fileName):
+ self.fileName = fileName
+ self.passes = []
+
+ def addPass(self, new_pass):
+ self.passes.append(new_pass)
+
+ def findPass(self, name):
+ for entry in self.passes:
+ if entry.name == name:
+ return entry
+ return None
+
+ def __eq__(self, other):
+ return isinstance(other, self.__class__) \
+ and self.passes == other.passes
+
+
+class C1visualizerPass(PrintableMixin):
+
+ def __init__(self, parent, name, body, startLineNo):
+ self.parent = parent
+ self.name = name
+ self.body = body
+ self.startLineNo = startLineNo
+
+ if not self.name:
+ Logger.fail("C1visualizer pass does not have a name", self.fileName, self.startLineNo)
+ if not self.body:
+ Logger.fail("C1visualizer pass does not have a body", self.fileName, self.startLineNo)
+
+ self.parent.addPass(self)
+
+ @property
+ def fileName(self):
+ return self.parent.fileName
+
+ def __eq__(self, other):
+ return isinstance(other, self.__class__) \
+ and self.name == other.name \
+ and self.body == other.body
diff --git a/tools/checker/file_format/c1visualizer/test.py b/tools/checker/file_format/c1visualizer/test.py
new file mode 100644
index 0000000..812a4cf
--- /dev/null
+++ b/tools/checker/file_format/c1visualizer/test.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python2
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from common.testing import ToUnicode
+from file_format.c1visualizer.parser import ParseC1visualizerStream
+from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass
+
+import io
+import unittest
+
+class C1visualizerParser_Test(unittest.TestCase):
+
+ def createFile(self, passList):
+ """ Creates an instance of CheckerFile from provided info.
+
+ Data format: [ ( <case-name>, [ ( <text>, <assert-variant> ), ... ] ), ... ]
+ """
+ c1File = C1visualizerFile("<c1_file>")
+ for passEntry in passList:
+ passName = passEntry[0]
+ passBody = passEntry[1]
+ c1Pass = C1visualizerPass(c1File, passName, passBody, 0)
+ return c1File
+
+ def assertParsesTo(self, c1Text, expectedData):
+ expectedFile = self.createFile(expectedData)
+ actualFile = ParseC1visualizerStream("<c1_file>", io.StringIO(ToUnicode(c1Text)))
+ return self.assertEqual(expectedFile, actualFile)
+
+ def test_EmptyFile(self):
+ self.assertParsesTo("", [])
+
+ def test_SingleGroup(self):
+ self.assertParsesTo(
+ """
+ begin_compilation
+ method "MyMethod"
+ end_compilation
+ begin_cfg
+ name "pass1"
+ foo
+ bar
+ end_cfg
+ """,
+ [ ( "MyMethod pass1", [ "foo", "bar" ] ) ])
+
+ def test_MultipleGroups(self):
+ self.assertParsesTo(
+ """
+ begin_compilation
+ name "xyz1"
+ method "MyMethod1"
+ date 1234
+ end_compilation
+ begin_cfg
+ name "pass1"
+ foo
+ bar
+ end_cfg
+ begin_cfg
+ name "pass2"
+ abc
+ def
+ end_cfg
+ """,
+ [ ( "MyMethod1 pass1", [ "foo", "bar" ] ),
+ ( "MyMethod1 pass2", [ "abc", "def" ] ) ])
+ self.assertParsesTo(
+ """
+ begin_compilation
+ name "xyz1"
+ method "MyMethod1"
+ date 1234
+ end_compilation
+ begin_cfg
+ name "pass1"
+ foo
+ bar
+ end_cfg
+ begin_compilation
+ name "xyz2"
+ method "MyMethod2"
+ date 5678
+ end_compilation
+ begin_cfg
+ name "pass2"
+ abc
+ def
+ end_cfg
+ """,
+ [ ( "MyMethod1 pass1", [ "foo", "bar" ] ),
+ ( "MyMethod2 pass2", [ "abc", "def" ] ) ])
diff --git a/tools/checker/file_format/checker/__init__.py b/tools/checker/file_format/checker/__init__.py
new file mode 100644
index 0000000..d0a140b
--- /dev/null
+++ b/tools/checker/file_format/checker/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/tools/checker/file_format/checker/parser.py b/tools/checker/file_format/checker/parser.py
new file mode 100644
index 0000000..4eed391
--- /dev/null
+++ b/tools/checker/file_format/checker/parser.py
@@ -0,0 +1,147 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from file_format.common import SplitStream
+from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, RegexExpression
+
+import re
+
+def __extractLine(prefix, line):
+ """ Attempts to parse a check line. The regex searches for a comment symbol
+ followed by the CHECK keyword, given attribute and a colon at the very
+ beginning of the line. Whitespaces are ignored.
+ """
+ rIgnoreWhitespace = r"\s*"
+ rCommentSymbols = [r"//", r"#"]
+ regexPrefix = rIgnoreWhitespace + \
+ r"(" + r"|".join(rCommentSymbols) + r")" + \
+ rIgnoreWhitespace + \
+ prefix + r":"
+
+ # The 'match' function succeeds only if the pattern is matched at the
+ # beginning of the line.
+ match = re.match(regexPrefix, line)
+ if match is not None:
+ return line[match.end():].strip()
+ else:
+ return None
+
+def __processLine(line, lineNo, prefix):
+ """ This function is invoked on each line of the check file and returns a pair
+ which instructs the parser how the line should be handled. If the line is
+ to be included in the current check group, it is returned in the first
+ value. If the line starts a new check group, the name of the group is
+ returned in the second value.
+ """
+ # Lines beginning with 'CHECK-START' start a new test case.
+ startLine = __extractLine(prefix + "-START", line)
+ if startLine is not None:
+ return None, startLine
+
+ # Lines starting only with 'CHECK' are matched in order.
+ plainLine = __extractLine(prefix, line)
+ if plainLine is not None:
+ return (plainLine, TestAssertion.Variant.InOrder, lineNo), None
+
+ # 'CHECK-NEXT' lines are in-order but must match the very next line.
+ nextLine = __extractLine(prefix + "-NEXT", line)
+ if nextLine is not None:
+ return (nextLine, TestAssertion.Variant.NextLine, lineNo), None
+
+ # 'CHECK-DAG' lines are no-order assertions.
+ dagLine = __extractLine(prefix + "-DAG", line)
+ if dagLine is not None:
+ return (dagLine, TestAssertion.Variant.DAG, lineNo), None
+
+ # 'CHECK-NOT' lines are no-order negative assertions.
+ notLine = __extractLine(prefix + "-NOT", line)
+ if notLine is not None:
+ return (notLine, TestAssertion.Variant.Not, lineNo), None
+
+ # Other lines are ignored.
+ return None, None
+
+def __isMatchAtStart(match):
+ """ Tests if the given Match occurred at the beginning of the line. """
+ return (match is not None) and (match.start() == 0)
+
+def __firstMatch(matches, string):
+ """ Takes in a list of Match objects and returns the minimal start point among
+ them. If there aren't any successful matches it returns the length of
+ the searched string.
+ """
+ starts = map(lambda m: len(string) if m is None else m.start(), matches)
+ return min(starts)
+
+def ParseCheckerAssertion(parent, line, variant, lineNo):
+ """ This method parses the content of a check line stripped of the initial
+ comment symbol and the CHECK keyword.
+ """
+ assertion = TestAssertion(parent, variant, line, lineNo)
+ # Loop as long as there is something to parse.
+ while line:
+ # Search for the nearest occurrence of the special markers.
+ matchWhitespace = re.search(r"\s+", line)
+ matchPattern = re.search(RegexExpression.Regex.regexPattern, line)
+ matchVariableReference = re.search(RegexExpression.Regex.regexVariableReference, line)
+ matchVariableDefinition = re.search(RegexExpression.Regex.regexVariableDefinition, line)
+
+ # If one of the above was identified at the current position, extract them
+ # from the line, parse them and add to the list of line parts.
+ if __isMatchAtStart(matchWhitespace):
+ # A whitespace in the check line creates a new separator of line parts.
+ # This allows for ignored output between the previous and next parts.
+ line = line[matchWhitespace.end():]
+ assertion.addExpression(RegexExpression.createSeparator())
+ elif __isMatchAtStart(matchPattern):
+ pattern = line[0:matchPattern.end()]
+ pattern = pattern[2:-2]
+ line = line[matchPattern.end():]
+ assertion.addExpression(RegexExpression.createPattern(pattern))
+ elif __isMatchAtStart(matchVariableReference):
+ var = line[0:matchVariableReference.end()]
+ line = line[matchVariableReference.end():]
+ name = var[2:-2]
+ assertion.addExpression(RegexExpression.createVariableReference(name))
+ elif __isMatchAtStart(matchVariableDefinition):
+ var = line[0:matchVariableDefinition.end()]
+ line = line[matchVariableDefinition.end():]
+ colonPos = var.find(":")
+ name = var[2:colonPos]
+ body = var[colonPos+1:-2]
+ assertion.addExpression(RegexExpression.createVariableDefinition(name, body))
+ else:
+ # If we're not currently looking at a special marker, this is a plain
+ # text match all the way until the first special marker (or the end
+ # of the line).
+ firstMatch = __firstMatch([ matchWhitespace,
+ matchPattern,
+ matchVariableReference,
+ matchVariableDefinition ],
+ line)
+ text = line[0:firstMatch]
+ line = line[firstMatch:]
+ assertion.addExpression(RegexExpression.createText(text))
+ return assertion
+
+def ParseCheckerStream(fileName, prefix, stream):
+ checkerFile = CheckerFile(fileName)
+ fnProcessLine = lambda line, lineNo: __processLine(line, lineNo, prefix)
+ fnLineOutsideChunk = lambda line, lineNo: \
+ Logger.fail("C1visualizer line not inside a group", fileName, lineNo)
+ for caseName, caseLines, startLineNo in SplitStream(stream, fnProcessLine, fnLineOutsideChunk):
+ testCase = TestCase(checkerFile, caseName, startLineNo)
+ for caseLine in caseLines:
+ ParseCheckerAssertion(testCase, caseLine[0], caseLine[1], caseLine[2])
+ return checkerFile
diff --git a/tools/checker/file_format/checker/struct.py b/tools/checker/file_format/checker/struct.py
new file mode 100644
index 0000000..6a54142
--- /dev/null
+++ b/tools/checker/file_format/checker/struct.py
@@ -0,0 +1,163 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from common.logger import Logger
+from common.mixins import EqualityMixin, PrintableMixin
+
+import re
+
+class CheckerFile(PrintableMixin):
+
+ def __init__(self, fileName):
+ self.fileName = fileName
+ self.testCases = []
+
+ def addTestCase(self, new_test_case):
+ self.testCases.append(new_test_case)
+
+ def __eq__(self, other):
+ return isinstance(other, self.__class__) \
+ and self.testCases == other.testCases
+
+
+class TestCase(PrintableMixin):
+
+ def __init__(self, parent, name, startLineNo):
+ assert isinstance(parent, CheckerFile)
+
+ self.parent = parent
+ self.name = name
+ self.assertions = []
+ self.startLineNo = startLineNo
+
+ if not self.name:
+ Logger.fail("Test case does not have a name", self.fileName, self.startLineNo)
+
+ self.parent.addTestCase(self)
+
+ @property
+ def fileName(self):
+ return self.parent.fileName
+
+ def addAssertion(self, new_assertion):
+ if new_assertion.variant == TestAssertion.Variant.NextLine:
+ if not self.assertions or \
+ (self.assertions[-1].variant != TestAssertion.Variant.InOrder and \
+ self.assertions[-1].variant != TestAssertion.Variant.NextLine):
+ Logger.fail("A next-line assertion can only be placed after an "
+ "in-order assertion or another next-line assertion.",
+ new_assertion.fileName, new_assertion.lineNo)
+ self.assertions.append(new_assertion)
+
+ def __eq__(self, other):
+ return isinstance(other, self.__class__) \
+ and self.name == other.name \
+ and self.assertions == other.assertions
+
+
+class TestAssertion(PrintableMixin):
+
+ class Variant(object):
+ """Supported types of assertions."""
+ InOrder, NextLine, DAG, Not = range(4)
+
+ def __init__(self, parent, variant, originalText, lineNo):
+ assert isinstance(parent, TestCase)
+
+ self.parent = parent
+ self.variant = variant
+ self.expressions = []
+ self.lineNo = lineNo
+ self.originalText = originalText
+
+ self.parent.addAssertion(self)
+
+ @property
+ def fileName(self):
+ return self.parent.fileName
+
+ def addExpression(self, new_expression):
+ assert isinstance(new_expression, RegexExpression)
+ if self.variant == TestAssertion.Variant.Not:
+ if new_expression.variant == RegexExpression.Variant.VarDef:
+ Logger.fail("CHECK-NOT lines cannot define variables", self.fileName, self.lineNo)
+ self.expressions.append(new_expression)
+
+ def toRegex(self):
+ """ Returns a regex pattern for this entire assertion. Only used in tests. """
+ regex = ""
+ for expression in self.expressions:
+ if expression.variant == RegexExpression.Variant.Separator:
+ regex = regex + ", "
+ else:
+ regex = regex + "(" + expression.pattern + ")"
+ return regex
+
+ def __eq__(self, other):
+ return isinstance(other, self.__class__) \
+ and self.variant == other.variant \
+ and self.expressions == other.expressions
+
+
+class RegexExpression(EqualityMixin, PrintableMixin):
+
+ class Variant(object):
+ """Supported language constructs."""
+ Text, Pattern, VarRef, VarDef, Separator = range(5)
+
+ class Regex(object):
+ rName = r"([a-zA-Z][a-zA-Z0-9]*)"
+ rRegex = r"(.+?)"
+ rPatternStartSym = r"(\{\{)"
+ rPatternEndSym = r"(\}\})"
+ rVariableStartSym = r"(<<)"
+ rVariableEndSym = r"(>>)"
+ rVariableSeparator = r"(:)"
+
+ regexPattern = rPatternStartSym + rRegex + rPatternEndSym
+ regexVariableReference = rVariableStartSym + rName + rVariableEndSym
+ regexVariableDefinition = rVariableStartSym + rName + rVariableSeparator + rRegex + rVariableEndSym
+
+ def __init__(self, variant, name, pattern):
+ self.variant = variant
+ self.name = name
+ self.pattern = pattern
+
+ def __eq__(self, other):
+ return isinstance(other, self.__class__) \
+ and self.variant == other.variant \
+ and self.name == other.name \
+ and self.pattern == other.pattern
+
+ @staticmethod
+ def createSeparator():
+ return RegexExpression(RegexExpression.Variant.Separator, None, None)
+
+ @staticmethod
+ def createText(text):
+ return RegexExpression(RegexExpression.Variant.Text, None, re.escape(text))
+
+ @staticmethod
+ def createPattern(pattern):
+ return RegexExpression(RegexExpression.Variant.Pattern, None, pattern)
+
+ @staticmethod
+ def createVariableReference(name):
+ assert re.match(RegexExpression.Regex.rName, name)
+ return RegexExpression(RegexExpression.Variant.VarRef, name, None)
+
+ @staticmethod
+ def createVariableDefinition(name, pattern):
+ assert re.match(RegexExpression.Regex.rName, name)
+ return RegexExpression(RegexExpression.Variant.VarDef, name, pattern)
diff --git a/tools/checker/file_format/checker/test.py b/tools/checker/file_format/checker/test.py
new file mode 100644
index 0000000..453deed
--- /dev/null
+++ b/tools/checker/file_format/checker/test.py
@@ -0,0 +1,269 @@
+#!/usr/bin/env python2
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from common.testing import ToUnicode
+from file_format.checker.parser import ParseCheckerStream
+from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, RegexExpression
+
+import io
+import unittest
+
+CheckerException = SystemExit
+
+class CheckerParser_PrefixTest(unittest.TestCase):
+
+ def tryParse(self, string):
+ checkerText = u"// CHECK-START: pass\n" + ToUnicode(string)
+ checkFile = ParseCheckerStream("<test-file>", "CHECK", io.StringIO(checkerText))
+ self.assertEqual(len(checkFile.testCases), 1)
+ testCase = checkFile.testCases[0]
+ return len(testCase.assertions) != 0
+
+ def test_InvalidFormat(self):
+ self.assertFalse(self.tryParse("CHECK"))
+ self.assertFalse(self.tryParse(":CHECK"))
+ self.assertFalse(self.tryParse("CHECK:"))
+ self.assertFalse(self.tryParse("//CHECK"))
+ self.assertFalse(self.tryParse("#CHECK"))
+
+ self.assertTrue(self.tryParse("//CHECK:foo"))
+ self.assertTrue(self.tryParse("#CHECK:bar"))
+
+ def test_InvalidLabel(self):
+ self.assertFalse(self.tryParse("//ACHECK:foo"))
+ self.assertFalse(self.tryParse("#ACHECK:foo"))
+
+ def test_NotFirstOnTheLine(self):
+ self.assertFalse(self.tryParse("A// CHECK: foo"))
+ self.assertFalse(self.tryParse("A # CHECK: foo"))
+ self.assertFalse(self.tryParse("// // CHECK: foo"))
+ self.assertFalse(self.tryParse("# # CHECK: foo"))
+
+ def test_WhitespaceAgnostic(self):
+ self.assertTrue(self.tryParse(" //CHECK: foo"))
+ self.assertTrue(self.tryParse("// CHECK: foo"))
+ self.assertTrue(self.tryParse(" //CHECK: foo"))
+ self.assertTrue(self.tryParse("// CHECK: foo"))
+
+
+class CheckerParser_RegexExpressionTest(unittest.TestCase):
+
+ def parseAssertion(self, string, variant=""):
+ checkerText = u"// CHECK-START: pass\n// CHECK" + ToUnicode(variant) + u": " + ToUnicode(string)
+ checkerFile = ParseCheckerStream("<test-file>", "CHECK", io.StringIO(checkerText))
+ self.assertEqual(len(checkerFile.testCases), 1)
+ testCase = checkerFile.testCases[0]
+ self.assertEqual(len(testCase.assertions), 1)
+ return testCase.assertions[0]
+
+ def parseExpression(self, string):
+ line = self.parseAssertion(string)
+ self.assertEqual(1, len(line.expressions))
+ return line.expressions[0]
+
+ def assertEqualsRegex(self, string, expected):
+ self.assertEqual(expected, self.parseAssertion(string).toRegex())
+
+ def assertEqualsText(self, string, text):
+ self.assertEqual(self.parseExpression(string), RegexExpression.createText(text))
+
+ def assertEqualsPattern(self, string, pattern):
+ self.assertEqual(self.parseExpression(string), RegexExpression.createPattern(pattern))
+
+ def assertEqualsVarRef(self, string, name):
+ self.assertEqual(self.parseExpression(string), RegexExpression.createVariableReference(name))
+
+ def assertEqualsVarDef(self, string, name, pattern):
+ self.assertEqual(self.parseExpression(string),
+ RegexExpression.createVariableDefinition(name, pattern))
+
+ def assertVariantNotEqual(self, string, variant):
+ self.assertNotEqual(variant, self.parseExpression(string).variant)
+
+ # Test that individual parts of the line are recognized
+
+ def test_TextOnly(self):
+ self.assertEqualsText("foo", "foo")
+ self.assertEqualsText(" foo ", "foo")
+ self.assertEqualsRegex("f$o^o", "(f\$o\^o)")
+
+ def test_PatternOnly(self):
+ self.assertEqualsPattern("{{a?b.c}}", "a?b.c")
+
+ def test_VarRefOnly(self):
+ self.assertEqualsVarRef("<<ABC>>", "ABC")
+
+ def test_VarDefOnly(self):
+ self.assertEqualsVarDef("<<ABC:a?b.c>>", "ABC", "a?b.c")
+
+ def test_TextWithWhitespace(self):
+ self.assertEqualsRegex("foo bar", "(foo), (bar)")
+ self.assertEqualsRegex("foo bar", "(foo), (bar)")
+
+ def test_TextWithRegex(self):
+ self.assertEqualsRegex("foo{{abc}}bar", "(foo)(abc)(bar)")
+
+ def test_TextWithVar(self):
+ self.assertEqualsRegex("foo<<ABC:abc>>bar", "(foo)(abc)(bar)")
+
+ def test_PlainWithRegexAndWhitespaces(self):
+ self.assertEqualsRegex("foo {{abc}}bar", "(foo), (abc)(bar)")
+ self.assertEqualsRegex("foo{{abc}} bar", "(foo)(abc), (bar)")
+ self.assertEqualsRegex("foo {{abc}} bar", "(foo), (abc), (bar)")
+
+ def test_PlainWithVarAndWhitespaces(self):
+ self.assertEqualsRegex("foo <<ABC:abc>>bar", "(foo), (abc)(bar)")
+ self.assertEqualsRegex("foo<<ABC:abc>> bar", "(foo)(abc), (bar)")
+ self.assertEqualsRegex("foo <<ABC:abc>> bar", "(foo), (abc), (bar)")
+
+ def test_AllKinds(self):
+ self.assertEqualsRegex("foo <<ABC:abc>>{{def}}bar", "(foo), (abc)(def)(bar)")
+ self.assertEqualsRegex("foo<<ABC:abc>> {{def}}bar", "(foo)(abc), (def)(bar)")
+ self.assertEqualsRegex("foo <<ABC:abc>> {{def}} bar", "(foo), (abc), (def), (bar)")
+
+ # # Test that variables and patterns are parsed correctly
+
+ def test_ValidPattern(self):
+ self.assertEqualsPattern("{{abc}}", "abc")
+ self.assertEqualsPattern("{{a[b]c}}", "a[b]c")
+ self.assertEqualsPattern("{{(a{bc})}}", "(a{bc})")
+
+ def test_ValidRef(self):
+ self.assertEqualsVarRef("<<ABC>>", "ABC")
+ self.assertEqualsVarRef("<<A1BC2>>", "A1BC2")
+
+ def test_ValidDef(self):
+ self.assertEqualsVarDef("<<ABC:abc>>", "ABC", "abc")
+ self.assertEqualsVarDef("<<ABC:ab:c>>", "ABC", "ab:c")
+ self.assertEqualsVarDef("<<ABC:a[b]c>>", "ABC", "a[b]c")
+ self.assertEqualsVarDef("<<ABC:(a[bc])>>", "ABC", "(a[bc])")
+
+ def test_Empty(self):
+ self.assertVariantNotEqual("{{}}", RegexExpression.Variant.Pattern)
+ self.assertVariantNotEqual("<<>>", RegexExpression.Variant.VarRef)
+ self.assertVariantNotEqual("<<:>>", RegexExpression.Variant.VarDef)
+
+ def test_InvalidVarName(self):
+ self.assertVariantNotEqual("<<0ABC>>", RegexExpression.Variant.VarRef)
+ self.assertVariantNotEqual("<<AB=C>>", RegexExpression.Variant.VarRef)
+ self.assertVariantNotEqual("<<ABC=>>", RegexExpression.Variant.VarRef)
+ self.assertVariantNotEqual("<<0ABC:abc>>", RegexExpression.Variant.VarDef)
+ self.assertVariantNotEqual("<<AB=C:abc>>", RegexExpression.Variant.VarDef)
+ self.assertVariantNotEqual("<<ABC=:abc>>", RegexExpression.Variant.VarDef)
+
+ def test_BodyMatchNotGreedy(self):
+ self.assertEqualsRegex("{{abc}}{{def}}", "(abc)(def)")
+ self.assertEqualsRegex("<<ABC:abc>><<DEF:def>>", "(abc)(def)")
+
+ def test_NoVarDefsInNotChecks(self):
+ with self.assertRaises(CheckerException):
+ self.parseAssertion("<<ABC:abc>>", "-NOT")
+
+
+class CheckerParser_FileLayoutTest(unittest.TestCase):
+
+ # Creates an instance of CheckerFile from provided info.
+ # Data format: [ ( <case-name>, [ ( <text>, <assert-variant> ), ... ] ), ... ]
+ def createFile(self, caseList):
+ testFile = CheckerFile("<test_file>")
+ for caseEntry in caseList:
+ caseName = caseEntry[0]
+ testCase = TestCase(testFile, caseName, 0)
+ assertionList = caseEntry[1]
+ for assertionEntry in assertionList:
+ content = assertionEntry[0]
+ variant = assertionEntry[1]
+ assertion = TestAssertion(testCase, variant, content, 0)
+ assertion.addExpression(RegexExpression.createText(content))
+ return testFile
+
+ def assertParsesTo(self, checkerText, expectedData):
+ expectedFile = self.createFile(expectedData)
+ actualFile = self.parse(checkerText)
+ return self.assertEqual(expectedFile, actualFile)
+
+ def parse(self, checkerText):
+ return ParseCheckerStream("<test_file>", "CHECK", io.StringIO(ToUnicode(checkerText)))
+
+ def test_EmptyFile(self):
+ self.assertParsesTo("", [])
+
+ def test_SingleGroup(self):
+ self.assertParsesTo(
+ """
+ // CHECK-START: Example Group
+ // CHECK: foo
+ // CHECK: bar
+ """,
+ [ ( "Example Group", [ ("foo", TestAssertion.Variant.InOrder),
+ ("bar", TestAssertion.Variant.InOrder) ] ) ])
+
+ def test_MultipleGroups(self):
+ self.assertParsesTo(
+ """
+ // CHECK-START: Example Group1
+ // CHECK: foo
+ // CHECK: bar
+ // CHECK-START: Example Group2
+ // CHECK: abc
+ // CHECK: def
+ """,
+ [ ( "Example Group1", [ ("foo", TestAssertion.Variant.InOrder),
+ ("bar", TestAssertion.Variant.InOrder) ] ),
+ ( "Example Group2", [ ("abc", TestAssertion.Variant.InOrder),
+ ("def", TestAssertion.Variant.InOrder) ] ) ])
+
+ def test_AssertionVariants(self):
+ self.assertParsesTo(
+ """
+ // CHECK-START: Example Group
+ // CHECK: foo1
+ // CHECK: foo2
+ // CHECK-NEXT: foo3
+ // CHECK-NEXT: foo4
+ // CHECK-NOT: bar
+ // CHECK-DAG: abc
+ // CHECK-DAG: def
+ """,
+ [ ( "Example Group", [ ("foo1", TestAssertion.Variant.InOrder),
+ ("foo2", TestAssertion.Variant.InOrder),
+ ("foo3", TestAssertion.Variant.NextLine),
+ ("foo4", TestAssertion.Variant.NextLine),
+ ("bar", TestAssertion.Variant.Not),
+ ("abc", TestAssertion.Variant.DAG),
+ ("def", TestAssertion.Variant.DAG) ] ) ])
+
+ def test_MisplacedNext(self):
+ with self.assertRaises(CheckerException):
+ self.parse(
+ """
+ // CHECK-START: Example Group
+ // CHECK-DAG: foo
+ // CHECK-NEXT: bar
+ """)
+ with self.assertRaises(CheckerException):
+ self.parse(
+ """
+ // CHECK-START: Example Group
+ // CHECK-NOT: foo
+ // CHECK-NEXT: bar
+ """)
+ with self.assertRaises(CheckerException):
+ self.parse(
+ """
+ // CHECK-START: Example Group
+ // CHECK-NEXT: bar
+ """)
diff --git a/tools/checker/file_format/common.py b/tools/checker/file_format/common.py
new file mode 100644
index 0000000..f91fdeb
--- /dev/null
+++ b/tools/checker/file_format/common.py
@@ -0,0 +1,48 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+def SplitStream(stream, fnProcessLine, fnLineOutsideChunk):
+ """ Reads the given input stream and splits it into chunks based on
+ information extracted from individual lines.
+
+ Arguments:
+ - fnProcessLine: Called on each line with the text and line number. Must
+ return a pair, name of the chunk started on this line and data extracted
+ from this line (or None in both cases).
+ - fnLineOutsideChunk: Called on attempt to attach data prior to creating
+ a chunk.
+ """
+ lineNo = 0
+ allChunks = []
+ currentChunk = None
+
+ for line in stream:
+ lineNo += 1
+ line = line.strip()
+ if not line:
+ continue
+
+ # Let the child class process the line and return information about it.
+ # The _processLine method can modify the content of the line (or delete it
+ # entirely) and specify whether it starts a new group.
+ processedLine, newChunkName = fnProcessLine(line, lineNo)
+ if newChunkName is not None:
+ currentChunk = (newChunkName, [], lineNo)
+ allChunks.append(currentChunk)
+ if processedLine is not None:
+ if currentChunk is not None:
+ currentChunk[1].append(processedLine)
+ else:
+ fnLineOutsideChunk(line, lineNo)
+ return allChunks
diff --git a/tools/checker/match/__init__.py b/tools/checker/match/__init__.py
new file mode 100644
index 0000000..d0a140b
--- /dev/null
+++ b/tools/checker/match/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/tools/checker/match/file.py b/tools/checker/match/file.py
new file mode 100644
index 0000000..b22211a
--- /dev/null
+++ b/tools/checker/match/file.py
@@ -0,0 +1,174 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from collections import namedtuple
+from common.immutables import ImmutableDict
+from common.logger import Logger
+from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass
+from file_format.checker.struct import CheckerFile, TestCase, TestAssertion
+from match.line import MatchLines
+
+MatchScope = namedtuple("MatchScope", ["start", "end"])
+MatchInfo = namedtuple("MatchInfo", ["scope", "variables"])
+
+class MatchFailedException(Exception):
+ def __init__(self, assertion, lineNo):
+ self.assertion = assertion
+ self.lineNo = lineNo
+
+def splitIntoGroups(assertions):
+ """ Breaks up a list of assertions, grouping instructions which should be
+ tested in the same scope (consecutive DAG and NOT instructions).
+ """
+ splitAssertions = []
+ lastVariant = None
+ for assertion in assertions:
+ if (assertion.variant == lastVariant and
+ assertion.variant in [TestAssertion.Variant.DAG, TestAssertion.Variant.Not]):
+ splitAssertions[-1].append(assertion)
+ else:
+ splitAssertions.append([assertion])
+ lastVariant = assertion.variant
+ return splitAssertions
+
+def findMatchingLine(assertion, c1Pass, scope, variables, excludeLines=[]):
+ """ Finds the first line in `c1Pass` which matches `assertion`.
+
+ Scan only lines numbered between `scope.start` and `scope.end` and not on the
+ `excludeLines` list.
+
+ Returns the index of the `c1Pass` line matching the assertion and variables
+ values after the match.
+
+ Raises MatchFailedException if no such `c1Pass` line can be found.
+ """
+ for i in range(scope.start, scope.end):
+ if i in excludeLines: continue
+ newVariables = MatchLines(assertion, c1Pass.body[i], variables)
+ if newVariables is not None:
+ return MatchInfo(MatchScope(i, i), newVariables)
+ raise MatchFailedException(assertion, scope.start)
+
+def matchDagGroup(assertions, c1Pass, scope, variables):
+ """ Attempts to find matching `c1Pass` lines for a group of DAG assertions.
+
+ Assertions are matched in the list order and variable values propagated. Only
+ lines in `scope` are scanned and each line can only match one assertion.
+
+ Returns the range of `c1Pass` lines covered by this group (min/max of matching
+ line numbers) and the variable values after the match of the last assertion.
+
+ Raises MatchFailedException when an assertion cannot be satisfied.
+ """
+ matchedLines = []
+ for assertion in assertions:
+ assert assertion.variant == TestAssertion.Variant.DAG
+ match = findMatchingLine(assertion, c1Pass, scope, variables, matchedLines)
+ variables = match.variables
+ assert match.scope.start == match.scope.end
+ assert match.scope.start not in matchedLines
+ matchedLines.append(match.scope.start)
+ return MatchInfo(MatchScope(min(matchedLines), max(matchedLines)), variables)
+
+def testNotGroup(assertions, c1Pass, scope, variables):
+ """ Verifies that none of the given NOT assertions matches a line inside
+ the given `scope` of `c1Pass` lines.
+
+ Raises MatchFailedException if an assertion matches a line in the scope.
+ """
+ for i in range(scope.start, scope.end):
+ line = c1Pass.body[i]
+ for assertion in assertions:
+ assert assertion.variant == TestAssertion.Variant.Not
+ if MatchLines(assertion, line, variables) is not None:
+ raise MatchFailedException(assertion, i)
+
+def MatchTestCase(testCase, c1Pass):
+ """ Runs a test case against a C1visualizer graph dump.
+
+ Raises MatchFailedException when an assertion cannot be satisfied.
+ """
+ assert testCase.name == c1Pass.name
+
+ matchFrom = 0
+ variables = ImmutableDict()
+ c1Length = len(c1Pass.body)
+
+ # NOT assertions are verified retrospectively, once the scope is known.
+ pendingNotAssertions = None
+
+ # Prepare assertions by grouping those that are verified in the same scope.
+ # We also add None as an EOF assertion that will set scope for NOTs.
+ assertionGroups = splitIntoGroups(testCase.assertions)
+ assertionGroups.append(None)
+
+ for assertionGroup in assertionGroups:
+ if assertionGroup is None:
+ # EOF marker always matches the last+1 line of c1Pass.
+ match = MatchInfo(MatchScope(c1Length, c1Length), None)
+ elif assertionGroup[0].variant == TestAssertion.Variant.Not:
+ # NOT assertions will be tested together with the next group.
+ assert not pendingNotAssertions
+ pendingNotAssertions = assertionGroup
+ continue
+ elif assertionGroup[0].variant == TestAssertion.Variant.InOrder:
+ # Single in-order assertion. Find the first line that matches.
+ assert len(assertionGroup) == 1
+ scope = MatchScope(matchFrom, c1Length)
+ match = findMatchingLine(assertionGroup[0], c1Pass, scope, variables)
+ elif assertionGroup[0].variant == TestAssertion.Variant.NextLine:
+ # Single next-line assertion. Test if the current line matches.
+ assert len(assertionGroup) == 1
+ scope = MatchScope(matchFrom, matchFrom + 1)
+ match = findMatchingLine(assertionGroup[0], c1Pass, scope, variables)
+ else:
+ # A group of DAG assertions. Match them all starting from the same point.
+ assert assertionGroup[0].variant == TestAssertion.Variant.DAG
+ scope = MatchScope(matchFrom, c1Length)
+ match = matchDagGroup(assertionGroup, c1Pass, scope, variables)
+
+ if pendingNotAssertions:
+ # Previous group were NOT assertions. Make sure they don't match any lines
+ # in the [matchFrom, match.start) scope.
+ scope = MatchScope(matchFrom, match.scope.start)
+ testNotGroup(pendingNotAssertions, c1Pass, scope, variables)
+ pendingNotAssertions = None
+
+ # Update state.
+ assert matchFrom <= match.scope.end
+ matchFrom = match.scope.end + 1
+ variables = match.variables
+
+def MatchFiles(checkerFile, c1File):
+ for testCase in checkerFile.testCases:
+ # TODO: Currently does not handle multiple occurrences of the same group
+ # name, e.g. when a pass is run multiple times. It will always try to
+ # match a check group against the first output group of the same name.
+ c1Pass = c1File.findPass(testCase.name)
+ if c1Pass is None:
+ Logger.fail("Test case \"{}\" not found in the CFG file".format(testCase.name),
+ testCase.fileName, testCase.startLineNo)
+
+ Logger.startTest(testCase.name)
+ try:
+ MatchTestCase(testCase, c1Pass)
+ Logger.testPassed()
+ except MatchFailedException as e:
+ lineNo = c1Pass.startLineNo + e.lineNo
+ if e.assertion.variant == TestAssertion.Variant.Not:
+ Logger.testFailed("NOT assertion matched line {}".format(lineNo),
+ e.assertion.fileName, e.assertion.lineNo)
+ else:
+ Logger.testFailed("Assertion could not be matched starting from line {}".format(lineNo),
+ e.assertion.fileName, e.assertion.lineNo)
diff --git a/tools/checker/match/line.py b/tools/checker/match/line.py
new file mode 100644
index 0000000..711d814
--- /dev/null
+++ b/tools/checker/match/line.py
@@ -0,0 +1,96 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from common.logger import Logger
+from file_format.checker.struct import RegexExpression
+
+import re
+
+def headAndTail(list):
+ return list[0], list[1:]
+
+def splitAtSeparators(expressions):
+ """ Splits a list of RegexExpressions at separators. """
+ splitExpressions = []
+ wordStart = 0
+ for index, expression in enumerate(expressions):
+ if expression.variant == RegexExpression.Variant.Separator:
+ splitExpressions.append(expressions[wordStart:index])
+ wordStart = index + 1
+ splitExpressions.append(expressions[wordStart:])
+ return splitExpressions
+
+def matchWords(checkerWord, stringWord, variables, pos):
+ """ Attempts to match a list of RegexExpressions against a string.
+ Returns updated variable dictionary if successful and None otherwise.
+ """
+ for expression in checkerWord:
+ # If `expression` is a variable reference, replace it with the value.
+ if expression.variant == RegexExpression.Variant.VarRef:
+ if expression.name in variables:
+ pattern = re.escape(variables[expression.name])
+ else:
+ Logger.testFailed("Multiple definitions of variable \"{}\"".format(expression.name),
+ pos.fileName, pos.lineNo)
+ else:
+ pattern = expression.pattern
+
+ # Match the expression's regex pattern against the remainder of the word.
+ # Note: re.match will succeed only if matched from the beginning.
+ match = re.match(pattern, stringWord)
+ if not match:
+ return None
+
+ # If `expression` was a variable definition, set the variable's value.
+ if expression.variant == RegexExpression.Variant.VarDef:
+ if expression.name not in variables:
+ variables = variables.copyWith(expression.name, stringWord[:match.end()])
+ else:
+ Logger.testFailed("Multiple definitions of variable \"{}\"".format(expression.name),
+ pos.fileName, pos.lineNo)
+
+ # Move cursor by deleting the matched characters.
+ stringWord = stringWord[match.end():]
+
+ # Make sure the entire word matched, i.e. `stringWord` is empty.
+ if stringWord:
+ return None
+
+ return variables
+
+def MatchLines(checkerLine, stringLine, variables):
+ """ Attempts to match a CHECK line against a string. Returns variable state
+ after the match if successful and None otherwise.
+ """
+ checkerWords = splitAtSeparators(checkerLine.expressions)
+ stringWords = stringLine.split()
+
+ while checkerWords:
+ # Get the next run of RegexExpressions which must match one string word.
+ checkerWord, checkerWords = headAndTail(checkerWords)
+
+ # Keep reading words until a match is found.
+ wordMatched = False
+ while stringWords:
+ stringWord, stringWords = headAndTail(stringWords)
+ newVariables = matchWords(checkerWord, stringWord, variables, checkerLine)
+ if newVariables is not None:
+ wordMatched = True
+ variables = newVariables
+ break
+ if not wordMatched:
+ return None
+
+ # All RegexExpressions matched. Return new variable state.
+ return variables
diff --git a/tools/checker/match/test.py b/tools/checker/match/test.py
new file mode 100644
index 0000000..348c1d2
--- /dev/null
+++ b/tools/checker/match/test.py
@@ -0,0 +1,388 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from common.immutables import ImmutableDict
+from common.testing import ToUnicode
+from file_format.c1visualizer.parser import ParseC1visualizerStream
+from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass
+from file_format.checker.parser import ParseCheckerStream, ParseCheckerAssertion
+from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, RegexExpression
+from match.file import MatchTestCase, MatchFailedException
+from match.line import MatchLines
+
+import io
+import unittest
+
+CheckerException = SystemExit
+
+class MatchLines_Test(unittest.TestCase):
+
+ def createTestAssertion(self, checkerString):
+ checkerFile = CheckerFile("<checker-file>")
+ testCase = TestCase(checkerFile, "TestMethod TestPass", 0)
+ return ParseCheckerAssertion(testCase, checkerString, TestAssertion.Variant.InOrder, 0)
+
+ def tryMatch(self, checkerString, c1String, varState={}):
+ return MatchLines(self.createTestAssertion(checkerString),
+ ToUnicode(c1String),
+ ImmutableDict(varState))
+
+ def assertMatches(self, checkerString, c1String, varState={}):
+ self.assertIsNotNone(self.tryMatch(checkerString, c1String, varState))
+
+ def assertDoesNotMatch(self, checkerString, c1String, varState={}):
+ self.assertIsNone(self.tryMatch(checkerString, c1String, varState))
+
+ def test_TextAndWhitespace(self):
+ self.assertMatches("foo", "foo")
+ self.assertMatches("foo", " foo ")
+ self.assertMatches("foo", "foo bar")
+ self.assertDoesNotMatch("foo", "XfooX")
+ self.assertDoesNotMatch("foo", "zoo")
+
+ self.assertMatches("foo bar", "foo bar")
+ self.assertMatches("foo bar", "abc foo bar def")
+ self.assertMatches("foo bar", "foo foo bar bar")
+
+ self.assertMatches("foo bar", "foo X bar")
+ self.assertDoesNotMatch("foo bar", "foo Xbar")
+
+ def test_Pattern(self):
+ self.assertMatches("foo{{A|B}}bar", "fooAbar")
+ self.assertMatches("foo{{A|B}}bar", "fooBbar")
+ self.assertDoesNotMatch("foo{{A|B}}bar", "fooCbar")
+
+ def test_VariableReference(self):
+ self.assertMatches("foo<<X>>bar", "foobar", {"X": ""})
+ self.assertMatches("foo<<X>>bar", "fooAbar", {"X": "A"})
+ self.assertMatches("foo<<X>>bar", "fooBbar", {"X": "B"})
+ self.assertDoesNotMatch("foo<<X>>bar", "foobar", {"X": "A"})
+ self.assertDoesNotMatch("foo<<X>>bar", "foo bar", {"X": "A"})
+ with self.assertRaises(CheckerException):
+ self.tryMatch("foo<<X>>bar", "foobar", {})
+
+ def test_VariableDefinition(self):
+ self.assertMatches("foo<<X:A|B>>bar", "fooAbar")
+ self.assertMatches("foo<<X:A|B>>bar", "fooBbar")
+ self.assertDoesNotMatch("foo<<X:A|B>>bar", "fooCbar")
+
+ env = self.tryMatch("foo<<X:A.*B>>bar", "fooABbar", {})
+ self.assertEqual(env, {"X": "AB"})
+ env = self.tryMatch("foo<<X:A.*B>>bar", "fooAxxBbar", {})
+ self.assertEqual(env, {"X": "AxxB"})
+
+ self.assertMatches("foo<<X:A|B>>bar<<X>>baz", "fooAbarAbaz")
+ self.assertMatches("foo<<X:A|B>>bar<<X>>baz", "fooBbarBbaz")
+ self.assertDoesNotMatch("foo<<X:A|B>>bar<<X>>baz", "fooAbarBbaz")
+
+ def test_NoVariableRedefinition(self):
+ with self.assertRaises(CheckerException):
+ self.tryMatch("<<X:...>><<X>><<X:...>><<X>>", "foofoobarbar")
+
+ def test_EnvNotChangedOnPartialMatch(self):
+ env = {"Y": "foo"}
+ self.assertDoesNotMatch("<<X:A>>bar", "Abaz", env)
+ self.assertFalse("X" in env.keys())
+
+ def test_VariableContentEscaped(self):
+ self.assertMatches("<<X:..>>foo<<X>>", ".*foo.*")
+ self.assertDoesNotMatch("<<X:..>>foo<<X>>", ".*fooAAAA")
+
+
+class MatchFiles_Test(unittest.TestCase):
+
+ def assertMatches(self, checkerString, c1String):
+ checkerString = \
+ """
+ // CHECK-START: MyMethod MyPass
+ """ + checkerString
+ c1String = \
+ """
+ begin_compilation
+ name "MyMethod"
+ method "MyMethod"
+ date 1234
+ end_compilation
+ begin_cfg
+ name "MyPass"
+ """ + c1String + \
+ """
+ end_cfg
+ """
+ checkerFile = ParseCheckerStream("<test-file>", "CHECK", io.StringIO(ToUnicode(checkerString)))
+ c1File = ParseC1visualizerStream("<c1-file>", io.StringIO(ToUnicode(c1String)))
+ assert len(checkerFile.testCases) == 1
+ assert len(c1File.passes) == 1
+ MatchTestCase(checkerFile.testCases[0], c1File.passes[0])
+
+ def assertDoesNotMatch(self, checkerString, c1String):
+ with self.assertRaises(MatchFailedException):
+ self.assertMatches(checkerString, c1String)
+
+ def test_Text(self):
+ self.assertMatches("// CHECK: foo bar", "foo bar")
+ self.assertDoesNotMatch("// CHECK: foo bar", "abc def")
+
+ def test_Pattern(self):
+ self.assertMatches("// CHECK: abc {{de.}}", "abc de#")
+ self.assertDoesNotMatch("// CHECK: abc {{de.}}", "abc d#f")
+
+ def test_Variables(self):
+ self.assertMatches(
+ """
+ // CHECK: foo<<X:.>>bar
+ // CHECK: abc<<X>>def
+ """,
+ """
+ foo0bar
+ abc0def
+ """)
+ self.assertMatches(
+ """
+ // CHECK: foo<<X:([0-9]+)>>bar
+ // CHECK: abc<<X>>def
+ // CHECK: ### <<X>> ###
+ """,
+ """
+ foo1234bar
+ abc1234def
+ ### 1234 ###
+ """)
+ self.assertDoesNotMatch(
+ """
+ // CHECK: foo<<X:([0-9]+)>>bar
+ // CHECK: abc<<X>>def
+ """,
+ """
+ foo1234bar
+ abc1235def
+ """)
+
+ def test_WholeWordMustMatch(self):
+ self.assertMatches("// CHECK: b{{.}}r", "abc bar def")
+ self.assertDoesNotMatch("// CHECK: b{{.}}r", "abc Xbar def")
+ self.assertDoesNotMatch("// CHECK: b{{.}}r", "abc barX def")
+ self.assertDoesNotMatch("// CHECK: b{{.}}r", "abc b r def")
+
+ def test_InOrderAssertions(self):
+ self.assertMatches(
+ """
+ // CHECK: foo
+ // CHECK: bar
+ """,
+ """
+ foo
+ bar
+ """)
+ self.assertDoesNotMatch(
+ """
+ // CHECK: foo
+ // CHECK: bar
+ """,
+ """
+ bar
+ foo
+ """)
+
+ def test_NextLineAssertions(self):
+ self.assertMatches(
+ """
+ // CHECK: foo
+ // CHECK-NEXT: bar
+ // CHECK-NEXT: abc
+ // CHECK: def
+ """,
+ """
+ foo
+ bar
+ abc
+ def
+ """)
+ self.assertMatches(
+ """
+ // CHECK: foo
+ // CHECK-NEXT: bar
+ // CHECK: def
+ """,
+ """
+ foo
+ bar
+ abc
+ def
+ """)
+ self.assertDoesNotMatch(
+ """
+ // CHECK: foo
+ // CHECK-NEXT: bar
+ """,
+ """
+ foo
+ abc
+ bar
+ """)
+
+ self.assertDoesNotMatch(
+ """
+ // CHECK: foo
+ // CHECK-NEXT: bar
+ """,
+ """
+ bar
+ foo
+ abc
+ """)
+
+ def test_DagAssertions(self):
+ self.assertMatches(
+ """
+ // CHECK-DAG: foo
+ // CHECK-DAG: bar
+ """,
+ """
+ foo
+ bar
+ """)
+ self.assertMatches(
+ """
+ // CHECK-DAG: foo
+ // CHECK-DAG: bar
+ """,
+ """
+ bar
+ foo
+ """)
+
+ def test_DagAssertionsScope(self):
+ self.assertMatches(
+ """
+ // CHECK: foo
+ // CHECK-DAG: abc
+ // CHECK-DAG: def
+ // CHECK: bar
+ """,
+ """
+ foo
+ def
+ abc
+ bar
+ """)
+ self.assertDoesNotMatch(
+ """
+ // CHECK: foo
+ // CHECK-DAG: abc
+ // CHECK-DAG: def
+ // CHECK: bar
+ """,
+ """
+ foo
+ abc
+ bar
+ def
+ """)
+ self.assertDoesNotMatch(
+ """
+ // CHECK: foo
+ // CHECK-DAG: abc
+ // CHECK-DAG: def
+ // CHECK: bar
+ """,
+ """
+ foo
+ def
+ bar
+ abc
+ """)
+
+ def test_NotAssertions(self):
+ self.assertMatches(
+ """
+ // CHECK-NOT: foo
+ """,
+ """
+ abc
+ def
+ """)
+ self.assertDoesNotMatch(
+ """
+ // CHECK-NOT: foo
+ """,
+ """
+ abc foo
+ def
+ """)
+ self.assertDoesNotMatch(
+ """
+ // CHECK-NOT: foo
+ // CHECK-NOT: bar
+ """,
+ """
+ abc
+ def bar
+ """)
+
+ def test_NotAssertionsScope(self):
+ self.assertMatches(
+ """
+ // CHECK: abc
+ // CHECK-NOT: foo
+ // CHECK: def
+ """,
+ """
+ abc
+ def
+ """)
+ self.assertMatches(
+ """
+ // CHECK: abc
+ // CHECK-NOT: foo
+ // CHECK: def
+ """,
+ """
+ abc
+ def
+ foo
+ """)
+ self.assertDoesNotMatch(
+ """
+ // CHECK: abc
+ // CHECK-NOT: foo
+ // CHECK: def
+ """,
+ """
+ abc
+ foo
+ def
+ """)
+
+ def test_LineOnlyMatchesOnce(self):
+ self.assertMatches(
+ """
+ // CHECK-DAG: foo
+ // CHECK-DAG: foo
+ """,
+ """
+ foo
+ abc
+ foo
+ """)
+ self.assertDoesNotMatch(
+ """
+ // CHECK-DAG: foo
+ // CHECK-DAG: foo
+ """,
+ """
+ foo
+ abc
+ bar
+ """)
diff --git a/tools/checker/run_unit_tests.py b/tools/checker/run_unit_tests.py
new file mode 100755
index 0000000..01708db
--- /dev/null
+++ b/tools/checker/run_unit_tests.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python2
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from common.logger import Logger
+from file_format.c1visualizer.test import C1visualizerParser_Test
+from file_format.checker.test import CheckerParser_PrefixTest, \
+ CheckerParser_RegexExpressionTest, \
+ CheckerParser_FileLayoutTest
+from match.test import MatchLines_Test, \
+ MatchFiles_Test
+
+import unittest
+
+if __name__ == '__main__':
+ Logger.Verbosity = Logger.Level.NoOutput
+ unittest.main(verbosity=2)
diff --git a/tools/checker_test.py b/tools/checker_test.py
deleted file mode 100755
index 667ca90..0000000
--- a/tools/checker_test.py
+++ /dev/null
@@ -1,474 +0,0 @@
-#!/usr/bin/env python2
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This is a test file which exercises all feautres supported by the domain-
-# specific markup language implemented by Checker.
-
-import checker
-import io
-import unittest
-
-# The parent type of exception expected to be thrown by Checker during tests.
-# It must be specific enough to not cover exceptions thrown due to actual flaws
-# in Checker.
-CheckerException = SystemExit
-
-
-class TestCheckFile_PrefixExtraction(unittest.TestCase):
- def __tryParse(self, string):
- checkFile = checker.CheckFile(None, [])
- return checkFile._extractLine("CHECK", string)
-
- def test_InvalidFormat(self):
- self.assertIsNone(self.__tryParse("CHECK"))
- self.assertIsNone(self.__tryParse(":CHECK"))
- self.assertIsNone(self.__tryParse("CHECK:"))
- self.assertIsNone(self.__tryParse("//CHECK"))
- self.assertIsNone(self.__tryParse("#CHECK"))
-
- self.assertIsNotNone(self.__tryParse("//CHECK:foo"))
- self.assertIsNotNone(self.__tryParse("#CHECK:bar"))
-
- def test_InvalidLabel(self):
- self.assertIsNone(self.__tryParse("//ACHECK:foo"))
- self.assertIsNone(self.__tryParse("#ACHECK:foo"))
-
- def test_NotFirstOnTheLine(self):
- self.assertIsNone(self.__tryParse("A// CHECK: foo"))
- self.assertIsNone(self.__tryParse("A # CHECK: foo"))
- self.assertIsNone(self.__tryParse("// // CHECK: foo"))
- self.assertIsNone(self.__tryParse("# # CHECK: foo"))
-
- def test_WhitespaceAgnostic(self):
- self.assertIsNotNone(self.__tryParse(" //CHECK: foo"))
- self.assertIsNotNone(self.__tryParse("// CHECK: foo"))
- self.assertIsNotNone(self.__tryParse(" //CHECK: foo"))
- self.assertIsNotNone(self.__tryParse("// CHECK: foo"))
-
-
-class TestCheckLine_Parse(unittest.TestCase):
- def __getPartPattern(self, linePart):
- if linePart.variant == checker.CheckElement.Variant.Separator:
- return "\s+"
- else:
- return linePart.pattern
-
- def __getRegex(self, checkLine):
- return "".join(map(lambda x: "(" + self.__getPartPattern(x) + ")", checkLine.lineParts))
-
- def __tryParse(self, string):
- return checker.CheckLine(string)
-
- def __parsesTo(self, string, expected):
- self.assertEqual(expected, self.__getRegex(self.__tryParse(string)))
-
- def __tryParseNot(self, string):
- return checker.CheckLine(string, checker.CheckLine.Variant.Not)
-
- def __parsesPattern(self, string, pattern):
- line = self.__tryParse(string)
- self.assertEqual(1, len(line.lineParts))
- self.assertEqual(checker.CheckElement.Variant.Pattern, line.lineParts[0].variant)
- self.assertEqual(pattern, line.lineParts[0].pattern)
-
- def __parsesVarRef(self, string, name):
- line = self.__tryParse(string)
- self.assertEqual(1, len(line.lineParts))
- self.assertEqual(checker.CheckElement.Variant.VarRef, line.lineParts[0].variant)
- self.assertEqual(name, line.lineParts[0].name)
-
- def __parsesVarDef(self, string, name, body):
- line = self.__tryParse(string)
- self.assertEqual(1, len(line.lineParts))
- self.assertEqual(checker.CheckElement.Variant.VarDef, line.lineParts[0].variant)
- self.assertEqual(name, line.lineParts[0].name)
- self.assertEqual(body, line.lineParts[0].pattern)
-
- def __doesNotParse(self, string, partType):
- line = self.__tryParse(string)
- self.assertEqual(1, len(line.lineParts))
- self.assertNotEqual(partType, line.lineParts[0].variant)
-
- # Test that individual parts of the line are recognized
-
- def test_TextOnly(self):
- self.__parsesTo("foo", "(foo)")
- self.__parsesTo(" foo ", "(foo)")
- self.__parsesTo("f$o^o", "(f\$o\^o)")
-
- def test_TextWithWhitespace(self):
- self.__parsesTo("foo bar", "(foo)(\s+)(bar)")
- self.__parsesTo("foo bar", "(foo)(\s+)(bar)")
-
- def test_RegexOnly(self):
- self.__parsesPattern("{{a?b.c}}", "a?b.c")
-
- def test_VarRefOnly(self):
- self.__parsesVarRef("[[ABC]]", "ABC")
-
- def test_VarDefOnly(self):
- self.__parsesVarDef("[[ABC:a?b.c]]", "ABC", "a?b.c")
-
- def test_TextWithRegex(self):
- self.__parsesTo("foo{{abc}}bar", "(foo)(abc)(bar)")
-
- def test_TextWithVar(self):
- self.__parsesTo("foo[[ABC:abc]]bar", "(foo)(abc)(bar)")
-
- def test_PlainWithRegexAndWhitespaces(self):
- self.__parsesTo("foo {{abc}}bar", "(foo)(\s+)(abc)(bar)")
- self.__parsesTo("foo{{abc}} bar", "(foo)(abc)(\s+)(bar)")
- self.__parsesTo("foo {{abc}} bar", "(foo)(\s+)(abc)(\s+)(bar)")
-
- def test_PlainWithVarAndWhitespaces(self):
- self.__parsesTo("foo [[ABC:abc]]bar", "(foo)(\s+)(abc)(bar)")
- self.__parsesTo("foo[[ABC:abc]] bar", "(foo)(abc)(\s+)(bar)")
- self.__parsesTo("foo [[ABC:abc]] bar", "(foo)(\s+)(abc)(\s+)(bar)")
-
- def test_AllKinds(self):
- self.__parsesTo("foo [[ABC:abc]]{{def}}bar", "(foo)(\s+)(abc)(def)(bar)")
- self.__parsesTo("foo[[ABC:abc]] {{def}}bar", "(foo)(abc)(\s+)(def)(bar)")
- self.__parsesTo("foo [[ABC:abc]] {{def}} bar", "(foo)(\s+)(abc)(\s+)(def)(\s+)(bar)")
-
- # Test that variables and patterns are parsed correctly
-
- def test_ValidPattern(self):
- self.__parsesPattern("{{abc}}", "abc")
- self.__parsesPattern("{{a[b]c}}", "a[b]c")
- self.__parsesPattern("{{(a{bc})}}", "(a{bc})")
-
- def test_ValidRef(self):
- self.__parsesVarRef("[[ABC]]", "ABC")
- self.__parsesVarRef("[[A1BC2]]", "A1BC2")
-
- def test_ValidDef(self):
- self.__parsesVarDef("[[ABC:abc]]", "ABC", "abc")
- self.__parsesVarDef("[[ABC:ab:c]]", "ABC", "ab:c")
- self.__parsesVarDef("[[ABC:a[b]c]]", "ABC", "a[b]c")
- self.__parsesVarDef("[[ABC:(a[bc])]]", "ABC", "(a[bc])")
-
- def test_Empty(self):
- self.__doesNotParse("{{}}", checker.CheckElement.Variant.Pattern)
- self.__doesNotParse("[[]]", checker.CheckElement.Variant.VarRef)
- self.__doesNotParse("[[:]]", checker.CheckElement.Variant.VarDef)
-
- def test_InvalidVarName(self):
- self.__doesNotParse("[[0ABC]]", checker.CheckElement.Variant.VarRef)
- self.__doesNotParse("[[AB=C]]", checker.CheckElement.Variant.VarRef)
- self.__doesNotParse("[[ABC=]]", checker.CheckElement.Variant.VarRef)
- self.__doesNotParse("[[0ABC:abc]]", checker.CheckElement.Variant.VarDef)
- self.__doesNotParse("[[AB=C:abc]]", checker.CheckElement.Variant.VarDef)
- self.__doesNotParse("[[ABC=:abc]]", checker.CheckElement.Variant.VarDef)
-
- def test_BodyMatchNotGreedy(self):
- self.__parsesTo("{{abc}}{{def}}", "(abc)(def)")
- self.__parsesTo("[[ABC:abc]][[DEF:def]]", "(abc)(def)")
-
- def test_NoVarDefsInNotChecks(self):
- with self.assertRaises(CheckerException):
- self.__tryParseNot("[[ABC:abc]]")
-
-class TestCheckLine_Match(unittest.TestCase):
- def __matchSingle(self, checkString, outputString, varState={}):
- checkLine = checker.CheckLine(checkString)
- newVarState = checkLine.match(outputString, varState)
- self.assertIsNotNone(newVarState)
- return newVarState
-
- def __notMatchSingle(self, checkString, outputString, varState={}):
- checkLine = checker.CheckLine(checkString)
- self.assertIsNone(checkLine.match(outputString, varState))
-
- def test_TextAndWhitespace(self):
- self.__matchSingle("foo", "foo")
- self.__matchSingle("foo", " foo ")
- self.__matchSingle("foo", "foo bar")
- self.__notMatchSingle("foo", "XfooX")
- self.__notMatchSingle("foo", "zoo")
-
- self.__matchSingle("foo bar", "foo bar")
- self.__matchSingle("foo bar", "abc foo bar def")
- self.__matchSingle("foo bar", "foo foo bar bar")
-
- self.__matchSingle("foo bar", "foo X bar")
- self.__notMatchSingle("foo bar", "foo Xbar")
-
- def test_Pattern(self):
- self.__matchSingle("foo{{A|B}}bar", "fooAbar")
- self.__matchSingle("foo{{A|B}}bar", "fooBbar")
- self.__notMatchSingle("foo{{A|B}}bar", "fooCbar")
-
- def test_VariableReference(self):
- self.__matchSingle("foo[[X]]bar", "foobar", {"X": ""})
- self.__matchSingle("foo[[X]]bar", "fooAbar", {"X": "A"})
- self.__matchSingle("foo[[X]]bar", "fooBbar", {"X": "B"})
- self.__notMatchSingle("foo[[X]]bar", "foobar", {"X": "A"})
- self.__notMatchSingle("foo[[X]]bar", "foo bar", {"X": "A"})
- with self.assertRaises(CheckerException):
- self.__matchSingle("foo[[X]]bar", "foobar", {})
-
- def test_VariableDefinition(self):
- self.__matchSingle("foo[[X:A|B]]bar", "fooAbar")
- self.__matchSingle("foo[[X:A|B]]bar", "fooBbar")
- self.__notMatchSingle("foo[[X:A|B]]bar", "fooCbar")
-
- env = self.__matchSingle("foo[[X:A.*B]]bar", "fooABbar", {})
- self.assertEqual(env, {"X": "AB"})
- env = self.__matchSingle("foo[[X:A.*B]]bar", "fooAxxBbar", {})
- self.assertEqual(env, {"X": "AxxB"})
-
- self.__matchSingle("foo[[X:A|B]]bar[[X]]baz", "fooAbarAbaz")
- self.__matchSingle("foo[[X:A|B]]bar[[X]]baz", "fooBbarBbaz")
- self.__notMatchSingle("foo[[X:A|B]]bar[[X]]baz", "fooAbarBbaz")
-
- def test_NoVariableRedefinition(self):
- with self.assertRaises(CheckerException):
- self.__matchSingle("[[X:...]][[X]][[X:...]][[X]]", "foofoobarbar")
-
- def test_EnvNotChangedOnPartialMatch(self):
- env = {"Y": "foo"}
- self.__notMatchSingle("[[X:A]]bar", "Abaz", env)
- self.assertFalse("X" in env.keys())
-
- def test_VariableContentEscaped(self):
- self.__matchSingle("[[X:..]]foo[[X]]", ".*foo.*")
- self.__notMatchSingle("[[X:..]]foo[[X]]", ".*fooAAAA")
-
-
-CheckVariant = checker.CheckLine.Variant
-
-def prepareSingleCheck(line):
- if isinstance(line, str):
- return checker.CheckLine(line)
- else:
- return checker.CheckLine(line[0], line[1])
-
-def prepareChecks(lines):
- if isinstance(lines, str):
- lines = lines.splitlines()
- return list(map(lambda line: prepareSingleCheck(line), lines))
-
-
-class TestCheckGroup_Match(unittest.TestCase):
- def __matchMulti(self, checkLines, outputString):
- checkGroup = checker.CheckGroup("MyGroup", prepareChecks(checkLines))
- outputGroup = checker.OutputGroup("MyGroup", outputString.splitlines())
- return checkGroup.match(outputGroup)
-
- def __notMatchMulti(self, checkString, outputString):
- with self.assertRaises(CheckerException):
- self.__matchMulti(checkString, outputString)
-
- def test_TextAndPattern(self):
- self.__matchMulti("""foo bar
- abc {{def}}""",
- """foo bar
- abc def""");
- self.__matchMulti("""foo bar
- abc {{de.}}""",
- """=======
- foo bar
- =======
- abc de#
- =======""");
- self.__notMatchMulti("""//XYZ: foo bar
- //XYZ: abc {{def}}""",
- """=======
- foo bar
- =======
- abc de#
- =======""");
-
- def test_Variables(self):
- self.__matchMulti("""foo[[X:.]]bar
- abc[[X]]def""",
- """foo bar
- abc def""");
- self.__matchMulti("""foo[[X:([0-9]+)]]bar
- abc[[X]]def
- ### [[X]] ###""",
- """foo1234bar
- abc1234def
- ### 1234 ###""");
-
- def test_Ordering(self):
- self.__matchMulti([("foo", CheckVariant.InOrder),
- ("bar", CheckVariant.InOrder)],
- """foo
- bar""")
- self.__notMatchMulti([("foo", CheckVariant.InOrder),
- ("bar", CheckVariant.InOrder)],
- """bar
- foo""")
- self.__matchMulti([("abc", CheckVariant.DAG),
- ("def", CheckVariant.DAG)],
- """abc
- def""")
- self.__matchMulti([("abc", CheckVariant.DAG),
- ("def", CheckVariant.DAG)],
- """def
- abc""")
- self.__matchMulti([("foo", CheckVariant.InOrder),
- ("abc", CheckVariant.DAG),
- ("def", CheckVariant.DAG),
- ("bar", CheckVariant.InOrder)],
- """foo
- def
- abc
- bar""")
- self.__notMatchMulti([("foo", CheckVariant.InOrder),
- ("abc", CheckVariant.DAG),
- ("def", CheckVariant.DAG),
- ("bar", CheckVariant.InOrder)],
- """foo
- abc
- bar""")
- self.__notMatchMulti([("foo", CheckVariant.InOrder),
- ("abc", CheckVariant.DAG),
- ("def", CheckVariant.DAG),
- ("bar", CheckVariant.InOrder)],
- """foo
- def
- bar""")
-
- def test_NotAssertions(self):
- self.__matchMulti([("foo", CheckVariant.Not)],
- """abc
- def""")
- self.__notMatchMulti([("foo", CheckVariant.Not)],
- """abc foo
- def""")
- self.__notMatchMulti([("foo", CheckVariant.Not),
- ("bar", CheckVariant.Not)],
- """abc
- def bar""")
-
- def test_LineOnlyMatchesOnce(self):
- self.__matchMulti([("foo", CheckVariant.DAG),
- ("foo", CheckVariant.DAG)],
- """foo
- foo""")
- self.__notMatchMulti([("foo", CheckVariant.DAG),
- ("foo", CheckVariant.DAG)],
- """foo
- bar""")
-
-class TestOutputFile_Parse(unittest.TestCase):
- def __parsesTo(self, string, expected):
- if isinstance(string, str):
- string = unicode(string)
- outputStream = io.StringIO(string)
- return self.assertEqual(checker.OutputFile(outputStream).groups, expected)
-
- def test_NoInput(self):
- self.__parsesTo(None, [])
- self.__parsesTo("", [])
-
- def test_SingleGroup(self):
- self.__parsesTo("""begin_compilation
- method "MyMethod"
- end_compilation
- begin_cfg
- name "pass1"
- foo
- bar
- end_cfg""",
- [ checker.OutputGroup("MyMethod pass1", [ "foo", "bar" ]) ])
-
- def test_MultipleGroups(self):
- self.__parsesTo("""begin_compilation
- name "xyz1"
- method "MyMethod1"
- date 1234
- end_compilation
- begin_cfg
- name "pass1"
- foo
- bar
- end_cfg
- begin_cfg
- name "pass2"
- abc
- def
- end_cfg""",
- [ checker.OutputGroup("MyMethod1 pass1", [ "foo", "bar" ]),
- checker.OutputGroup("MyMethod1 pass2", [ "abc", "def" ]) ])
-
- self.__parsesTo("""begin_compilation
- name "xyz1"
- method "MyMethod1"
- date 1234
- end_compilation
- begin_cfg
- name "pass1"
- foo
- bar
- end_cfg
- begin_compilation
- name "xyz2"
- method "MyMethod2"
- date 5678
- end_compilation
- begin_cfg
- name "pass2"
- abc
- def
- end_cfg""",
- [ checker.OutputGroup("MyMethod1 pass1", [ "foo", "bar" ]),
- checker.OutputGroup("MyMethod2 pass2", [ "abc", "def" ]) ])
-
-class TestCheckFile_Parse(unittest.TestCase):
- def __parsesTo(self, string, expected):
- if isinstance(string, str):
- string = unicode(string)
- checkStream = io.StringIO(string)
- return self.assertEqual(checker.CheckFile("CHECK", checkStream).groups, expected)
-
- def test_NoInput(self):
- self.__parsesTo(None, [])
- self.__parsesTo("", [])
-
- def test_SingleGroup(self):
- self.__parsesTo("""// CHECK-START: Example Group
- // CHECK: foo
- // CHECK: bar""",
- [ checker.CheckGroup("Example Group", prepareChecks([ "foo", "bar" ])) ])
-
- def test_MultipleGroups(self):
- self.__parsesTo("""// CHECK-START: Example Group1
- // CHECK: foo
- // CHECK: bar
- // CHECK-START: Example Group2
- // CHECK: abc
- // CHECK: def""",
- [ checker.CheckGroup("Example Group1", prepareChecks([ "foo", "bar" ])),
- checker.CheckGroup("Example Group2", prepareChecks([ "abc", "def" ])) ])
-
- def test_CheckVariants(self):
- self.__parsesTo("""// CHECK-START: Example Group
- // CHECK: foo
- // CHECK-NOT: bar
- // CHECK-DAG: abc
- // CHECK-DAG: def""",
- [ checker.CheckGroup("Example Group",
- prepareChecks([ ("foo", CheckVariant.InOrder),
- ("bar", CheckVariant.Not),
- ("abc", CheckVariant.DAG),
- ("def", CheckVariant.DAG) ])) ])
-
-if __name__ == '__main__':
- checker.Logger.Verbosity = checker.Logger.Level.NoOutput
- unittest.main()
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index a387036..064abc1 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -21,12 +21,19 @@
name: "libcore.java.lang.SystemTest#testSystemProperties_mutable"
},
{
- description: "Differences between vogar and cts",
+ description: "Differences between vogar and cts. Passes with --mode activity",
result: EXEC_FAILED,
modes: [device],
- names: ["libcore.java.lang.OldSystemTest#test_getProperties",
- "org.apache.harmony.tests.java.lang.Process2Test#test_getErrorStream",
- "org.apache.harmony.tests.java.lang.ProcessTest#test_exitValue"]
+ names: ["libcore.java.lang.OldSystemTest#test_getProperties"]
+},
+{
+ description: "Differences between vogar and cts. EACCESS when run with vogar.
+ Passes on host, passes with cts. Passes with vogar with su
+ (--invoke-with \"su root\"). Does not pass after setting chmod
+ 777 all directories on path to socket (on device without su).",
+ result: EXEC_FAILED,
+ modes: [device],
+ names: ["libcore.io.OsTest#testUnixDomainSockets_in_file_system"]
},
{
description: "Failures needing investigation",
@@ -34,15 +41,9 @@
modes: [device],
names: ["libcore.java.util.TimeZoneTest#testDisplayNames",
"libcore.java.util.TimeZoneTest#test_useDaylightTime_Taiwan",
- "libcore.java.util.TimeZoneTest#testAllDisplayNames",
- "libcore.io.OsTest#testUnixDomainSockets_in_file_system",
- "org.apache.harmony.luni.tests.java.net.URLConnectionTest#test_setReadTimeoutI",
"org.apache.harmony.tests.java.util.DateTest#test_Constructor",
"org.apache.harmony.tests.java.util.ScannerTest#test_Constructor_LReadableByteChannel",
- "org.apache.harmony.tests.java.util.TimeZoneTest#test_hasSameRules_Ljava_util_TimeZone",
- "org.apache.harmony.tests.java.text.ChoiceFormatTest#testEscapedPatternWithConsecutiveQuotes",
- "org.apache.harmony.tests.java.text.ChoiceFormatTest#testToPatternWithInfinities",
- "org.apache.harmony.tests.java.text.MessageFormatTest#test19011159"]
+ "org.apache.harmony.tests.java.util.TimeZoneTest#test_hasSameRules_Ljava_util_TimeZone"]
},
{
description: "Failing due to a locale problem on hammerhead.",
@@ -109,18 +110,6 @@
bug: 19165288
},
{
- description: "Bug in libcore",
- result: EXEC_FAILED,
- names: ["libcore.javax.crypto.ECDHKeyAgreementTest#testInit_withUnsupportedPrivateKeyType"],
- bug: 19730263
-},
-{
- description: "Needs to be run as root",
- result: EXEC_FAILED,
- modes: [host],
- names: ["libcore.io.OsTest#test_PacketSocketAddress"]
-},
-{
description: "Needs kernel updates on host/device",
result: EXEC_FAILED,
names: ["libcore.io.OsTest#test_socketPing"]
@@ -130,5 +119,11 @@
modes: [device],
result: EXEC_FAILED,
names: ["org.apache.harmony.tests.java.lang.ProcessManagerTest#testEnvironment"]
+},
+{
+ description: "Crypto failures",
+ result: EXEC_FAILED,
+ names: ["libcore.javax.crypto.CipherTest#testCipher_ShortBlock_Failure",
+ "libcore.javax.crypto.CipherTest#testCipher_Success"]
}
]
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index 1dd443b..77e8004 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -21,11 +21,10 @@
# Jar containing all the tests.
test_jar=out/host/linux-x86/framework/apache-harmony-jdwp-tests-hostdex.jar
-junit_jar=out/host/linux-x86/framework/junit.jar
-if [ ! -f $test_jar -o ! -f $junit_jar ]; then
+if [ ! -f $test_jar ]; then
echo "Before running, you must build jdwp tests and vogar:" \
- "make junit apache-harmony-jdwp-tests-hostdex vogar vogar.jar"
+ "make apache-harmony-jdwp-tests-hostdex vogar vogar.jar"
exit 1
fi
@@ -80,7 +79,6 @@
--vm-arg -Djpda.settings.transportAddress=127.0.0.1:55107 \
--vm-arg -Djpda.settings.debuggeeJavaPath="\"$art_debugee $image $debuggee_args\"" \
--classpath $test_jar \
- --classpath $junit_jar \
--vm-arg -Xcompiler-option --vm-arg --compiler-backend=Optimizing \
--vm-arg -Xcompiler-option --vm-arg --debuggable \
org.apache.harmony.jpda.tests.share.AllTests