am b89a8c66: Fix compaction bug in Class_getDeclaredMethodsUnchecked
* commit 'b89a8c66e360575d2dc1ec1229235735c56968ff':
Fix compaction bug in Class_getDeclaredMethodsUnchecked
diff --git a/Android.mk b/Android.mk
index 3467f1d..54a33b2 100644
--- a/Android.mk
+++ b/Android.mk
@@ -119,6 +119,10 @@
# Sync test files to the target, depends upon all things that must be pushed to the target.
.PHONY: test-art-target-sync
+# Check if we need to sync. In case ART_TEST_ANDROID_ROOT is not empty,
+# the code below uses 'adb push' instead of 'adb sync', which does not
+# check if the files on the device have changed.
+ifneq ($(ART_TEST_NO_SYNC),true)
ifeq ($(ART_TEST_ANDROID_ROOT),)
test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS)
adb root
@@ -130,9 +134,7 @@
adb wait-for-device push $(ANDROID_PRODUCT_OUT)/system $(ART_TEST_ANDROID_ROOT)
adb push $(ANDROID_PRODUCT_OUT)/data /data
endif
-
-# Undefine variable now its served its purpose.
-TEST_ART_TARGET_SYNC_DEPS :=
+endif
# "mm test-art" to build and run all tests on host and device
.PHONY: test-art
@@ -377,6 +379,15 @@
build-art-target: $(TARGET_OUT_EXECUTABLES)/art $(ART_TARGET_DEPENDENCIES) $(TARGET_CORE_IMG_OUTS)
########################################################################
+# Rules for building all dependencies for tests.
+
+.PHONY: build-art-host-tests
+build-art-host-tests: build-art-host $(TEST_ART_RUN_TEST_DEPENDENCIES) $(ART_TEST_HOST_RUN_TEST_DEPENDENCIES) $(ART_TEST_HOST_GTEST_DEPENDENCIES)
+
+.PHONY: build-art-target-tests
+build-art-target-tests: build-art-target $(TEST_ART_RUN_TEST_DEPENDENCIES) $(TEST_ART_TARGET_SYNC_DEPS)
+
+########################################################################
# targets to switch back and forth from libdvm to libart
.PHONY: use-art
@@ -467,3 +478,4 @@
# Clear locally used variables.
art_dont_bother :=
art_test_bother :=
+TEST_ART_TARGET_SYNC_DEPS :=
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index b84154b..ace6a73 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -68,6 +68,9 @@
ART_HOST_CFLAGS :=
ART_TARGET_CFLAGS :=
+ART_HOST_ASFLAGS :=
+ART_TARGET_ASFLAGS :=
+
# Clang build support.
# Host.
@@ -199,6 +202,9 @@
-fvisibility=protected \
$(art_default_gc_type_cflags)
+# Base set of asflags used by all things ART.
+art_asflags :=
+
# Missing declarations: too many at the moment, as we use "extern" quite a bit.
# -Wmissing-declarations \
@@ -217,10 +223,12 @@
ifeq ($(ART_HEAP_POISONING),true)
art_cflags += -DART_HEAP_POISONING=1
+ art_asflags += -DART_HEAP_POISONING=1
endif
ifeq ($(ART_USE_READ_BARRIER),true)
art_cflags += -DART_USE_READ_BARRIER=1
+ art_asflags += -DART_USE_READ_BARRIER=1
endif
ifeq ($(ART_USE_TLAB),true)
@@ -258,11 +266,13 @@
endif
ART_HOST_CFLAGS += $(art_cflags) -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS)
ART_HOST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=default
+ART_HOST_ASFLAGS += $(art_asflags)
ifndef LIBART_IMG_TARGET_BASE_ADDRESS
$(error LIBART_IMG_TARGET_BASE_ADDRESS unset)
endif
ART_TARGET_CFLAGS += $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS)
+ART_TARGET_ASFLAGS += $(art_asflags)
ART_HOST_NON_DEBUG_CFLAGS := $(art_host_non_debug_cflags)
ART_TARGET_NON_DEBUG_CFLAGS := $(art_target_non_debug_cflags)
@@ -292,6 +302,7 @@
# Clear locals now they've served their purpose.
art_cflags :=
+art_asflags :=
art_debug_cflags :=
art_non_debug_cflags :=
art_host_non_debug_cflags :=
@@ -311,6 +322,7 @@
define set-target-local-cflags-vars
LOCAL_CFLAGS += $(ART_TARGET_CFLAGS)
LOCAL_CFLAGS_x86 += $(ART_TARGET_CFLAGS_x86)
+ LOCAL_ASFLAGS += $(ART_TARGET_ASFLAGS)
LOCAL_LDFLAGS += $(ART_TARGET_LDFLAGS)
art_target_cflags_ndebug_or_debug := $(1)
ifeq ($$(art_target_cflags_ndebug_or_debug),debug)
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/build/Android.executable.mk b/build/Android.executable.mk
index dfea6e1..7b03682 100644
--- a/build/Android.executable.mk
+++ b/build/Android.executable.mk
@@ -70,13 +70,14 @@
endif
ifeq ($$(art_target_or_host),target)
- $(call set-target-local-clang-vars)
- $(call set-target-local-cflags-vars,$(6))
+ $(call set-target-local-clang-vars)
+ $(call set-target-local-cflags-vars,$(6))
LOCAL_SHARED_LIBRARIES += libdl
else # host
LOCAL_CLANG := $(ART_HOST_CLANG)
LOCAL_LDLIBS := $(ART_HOST_LDLIBS)
LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
+ LOCAL_ASFLAGS += $(ART_HOST_ASFLAGS)
ifeq ($$(art_ndebug_or_debug),debug)
LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS)
else
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 69472b7..4fc184e 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -291,6 +291,7 @@
LOCAL_MODULE_TAGS := optional
LOCAL_CPP_EXTENSION := cc
LOCAL_CFLAGS := $(ART_HOST_CFLAGS)
+LOCAL_ASFLAGS := $(ART_HOST_ASFLAGS)
LOCAL_SRC_FILES := runtime/common_runtime_test.cc compiler/common_compiler_test.cc
LOCAL_C_INCLUDES := $(ART_C_INCLUDES) art/runtime art/compiler
LOCAL_SHARED_LIBRARIES := libartd libartd-compiler
@@ -313,6 +314,7 @@
ART_TEST_TARGET_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_GTEST_RULES :=
+ART_TEST_HOST_GTEST_DEPENDENCIES :=
ART_GTEST_TARGET_ANDROID_ROOT := '/system'
ifneq ($(ART_TEST_ANDROID_ROOT),)
@@ -374,11 +376,15 @@
gtest_exe := $$(HOST_OUT_EXECUTABLES)/$(1)$$($(2)ART_PHONY_TEST_HOST_SUFFIX)
# Dependencies for all host gtests.
gtest_deps := $$(HOST_CORE_DEX_LOCATIONS) \
- $$($(2)ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$$(ART_HOST_SHLIB_EXTENSION)
+ $$($(2)ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$$(ART_HOST_SHLIB_EXTENSION) \
+ $$(gtest_exe) \
+ $$(ART_GTEST_$(1)_HOST_DEPS) \
+ $(foreach file,$(ART_GTEST_$(1)_DEX_DEPS),$(ART_TEST_HOST_GTEST_$(file)_DEX))
+ ART_TEST_HOST_GTEST_DEPENDENCIES += $$(gtest_deps)
.PHONY: $$(gtest_rule)
-$$(gtest_rule): $$(gtest_exe) $$(ART_GTEST_$(1)_HOST_DEPS) $(foreach file,$(ART_GTEST_$(1)_DEX_DEPS),$(ART_TEST_HOST_GTEST_$(file)_DEX)) $$(gtest_deps)
+$$(gtest_rule): $$(gtest_exe) $$(gtest_deps)
$(hide) ($$(call ART_TEST_SKIP,$$@) && $$< && $$(call ART_TEST_PASSED,$$@)) \
|| $$(call ART_TEST_FAILED,$$@)
@@ -388,7 +394,7 @@
.PHONY: valgrind-$$(gtest_rule)
-valgrind-$$(gtest_rule): $$(gtest_exe) $$(ART_GTEST_$(1)_HOST_DEPS) $(foreach file,$(ART_GTEST_$(1)_DEX_DEPS),$(ART_TEST_HOST_GTEST_$(file)_DEX)) $$(gtest_deps) $(ART_VALGRIND_DEPENDENCIES)
+valgrind-$$(gtest_rule): $$(gtest_exe) $$(gtest_deps) $(ART_VALGRIND_DEPENDENCIES)
$(hide) $$(call ART_TEST_SKIP,$$@) && \
VALGRIND_LIB=$(HOST_OUT)/lib64/valgrind \
$(HOST_OUT_EXECUTABLES)/valgrind --leak-check=full --error-exitcode=1 $$< && \
@@ -484,6 +490,7 @@
else # host
LOCAL_CLANG := $$(ART_HOST_CLANG)
LOCAL_CFLAGS += $$(ART_HOST_CFLAGS) $$(ART_HOST_DEBUG_CFLAGS)
+ LOCAL_ASFLAGS += $$(ART_HOST_ASFLAGS)
LOCAL_SHARED_LIBRARIES += libicuuc-host libicui18n-host libnativehelper libziparchive-host libz-host libvixld
LOCAL_LDLIBS := $(ART_HOST_LDLIBS) -lpthread -ldl
LOCAL_IS_HOST_MODULE := true
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 710b130..728469c 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -113,7 +113,7 @@
--oat-location=$$(PRIVATE_CORE_OAT_NAME) --image=$$(PRIVATE_CORE_IMG_NAME) \
--base=$$(LIBART_IMG_HOST_BASE_ADDRESS) --instruction-set=$$($(3)ART_HOST_ARCH) \
--instruction-set-features=$$($(3)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES) \
- --host --android-root=$$(HOST_OUT) --include-patch-information \
+ --host --android-root=$$(HOST_OUT) --include-patch-information --generate-debug-info \
$$(PRIVATE_CORE_COMPILE_OPTIONS)
$$(core_oat_name): $$(core_image_name)
@@ -232,7 +232,7 @@
--base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) --instruction-set=$$($(3)TARGET_ARCH) \
--instruction-set-variant=$$($(3)DEX2OAT_TARGET_CPU_VARIANT) \
--instruction-set-features=$$($(3)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
- --android-root=$$(PRODUCT_OUT)/system --include-patch-information \
+ --android-root=$$(PRODUCT_OUT)/system --include-patch-information --generate-debug-info \
$$(PRIVATE_CORE_COMPILE_OPTIONS) || (rm $$(PRIVATE_CORE_OAT_NAME); exit 1)
$$(core_oat_name): $$(core_image_name)
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 1386439..6192be7 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -262,6 +262,13 @@
EXPECT_SINGLE_PARSE_FAIL("-verbose:blablabla", CmdlineResult::kUsage); // invalid verbose opt
{
+ const char* log_args = "-verbose:deopt";
+ LogVerbosity log_verbosity = LogVerbosity();
+ log_verbosity.deopt = true;
+ EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose);
+ }
+
+ {
const char* log_args = "-verbose:oat";
LogVerbosity log_verbosity = LogVerbosity();
log_verbosity.oat = true;
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index f38478c..28bd754 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -580,6 +580,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/Android.mk b/compiler/Android.mk
index 3f5271d..67536f0 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -234,6 +234,7 @@
else # host
LOCAL_CLANG := $(ART_HOST_CLANG)
LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
+ LOCAL_ASFLAGS += $(ART_HOST_ASFLAGS)
LOCAL_LDLIBS := $(ART_HOST_LDLIBS)
ifeq ($$(art_ndebug_or_debug),debug)
LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS)
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/dex/quick/arm/fp_arm.cc b/compiler/dex/quick/arm/fp_arm.cc
index 94fc474..1a5c108 100644
--- a/compiler/dex/quick/arm/fp_arm.cc
+++ b/compiler/dex/quick/arm/fp_arm.cc
@@ -191,30 +191,8 @@
GenConversionCall(kQuickF2l, rl_dest, rl_src, kCoreReg);
return;
case Instruction::LONG_TO_FLOAT: {
- rl_src = LoadValueWide(rl_src, kFPReg);
- RegisterInfo* info = GetRegInfo(rl_src.reg);
- RegStorage src_low = info->FindMatchingView(RegisterInfo::kLowSingleStorageMask)->GetReg();
- DCHECK(src_low.Valid());
- RegStorage src_high = info->FindMatchingView(RegisterInfo::kHighSingleStorageMask)->GetReg();
- DCHECK(src_high.Valid());
- rl_result = EvalLoc(rl_dest, kFPReg, true);
- // Allocate temp registers.
- RegStorage high_val = AllocTempDouble();
- RegStorage low_val = AllocTempDouble();
- RegStorage const_val = AllocTempDouble();
- // Long to double.
- NewLIR2(kThumb2VcvtF64S32, high_val.GetReg(), src_high.GetReg());
- NewLIR2(kThumb2VcvtF64U32, low_val.GetReg(), src_low.GetReg());
- LoadConstantWide(const_val, INT64_C(0x41f0000000000000));
- NewLIR3(kThumb2VmlaF64, low_val.GetReg(), high_val.GetReg(), const_val.GetReg());
- // Double to float.
- NewLIR2(kThumb2VcvtDF, rl_result.reg.GetReg(), low_val.GetReg());
- // Free temp registers.
- FreeTemp(high_val);
- FreeTemp(low_val);
- FreeTemp(const_val);
- // Store result.
- StoreValue(rl_dest, rl_result);
+ CheckEntrypointTypes<kQuickL2f, float, int64_t>(); // float -> kFPReg
+ GenConversionCall(kQuickL2f, rl_dest, rl_src, kFPReg);
return;
}
case Instruction::DOUBLE_TO_LONG:
diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h
index c530a8b..2253d10 100644
--- a/compiler/dex/quick/arm64/arm64_lir.h
+++ b/compiler/dex/quick/arm64/arm64_lir.h
@@ -35,14 +35,15 @@
* r16-r17: Also known as ip0-ip1, respectively. Used as scratch registers by
* the linker, by the trampolines and other stubs (the backend uses
* these as temporary registers).
- * r18 : (rxSELF) is reserved (pointer to thread-local storage).
- * r19-r29: Callee save registers (promotion targets).
+ * r18 : Caller save register (used as temporary register).
+ * r19 : (rxSELF) is reserved (pointer to thread-local storage).
+ * r20-r29: Callee save registers (promotion targets).
* r30 : (lr) is reserved (the link register).
* rsp : (sp) is reserved (the stack pointer).
* rzr : (zr) is reserved (the zero register).
*
- * 18 core temps that codegen can use (r0-r17).
- * 10 core registers that can be used for promotion.
+ * 19 core temps that codegen can use (r0-r18).
+ * 9 core registers that can be used for promotion.
*
* Floating-point registers
* v0-v31
@@ -145,7 +146,7 @@
// Aliases which are not defined in "ARM Architecture Reference, register names".
rxIP0 = rx16,
rxIP1 = rx17,
- rxSELF = rx18,
+ rxSELF = rx19,
rxLR = rx30,
/*
* FIXME: It's a bit awkward to define both 32 and 64-bit views of these - we'll only ever use
@@ -154,7 +155,7 @@
*/
rwIP0 = rw16,
rwIP1 = rw17,
- rwSELF = rw18,
+ rwSELF = rw19,
rwLR = rw30,
};
diff --git a/compiler/dex/quick/arm64/target_arm64.cc b/compiler/dex/quick/arm64/target_arm64.cc
index d5de18d..6efa11e 100644
--- a/compiler/dex/quick/arm64/target_arm64.cc
+++ b/compiler/dex/quick/arm64/target_arm64.cc
@@ -51,19 +51,17 @@
rs_d8, rs_d9, rs_d10, rs_d11, rs_d12, rs_d13, rs_d14, rs_d15,
rs_d16, rs_d17, rs_d18, rs_d19, rs_d20, rs_d21, rs_d22, rs_d23,
rs_d24, rs_d25, rs_d26, rs_d27, rs_d28, rs_d29, rs_d30, rs_d31};
-// Note: we are not able to call to C function since rs_xSELF is a special register need to be
-// preserved but would be scratched by native functions follow aapcs64.
static constexpr RegStorage reserved_regs_arr[] = {rs_wSELF, rs_wsp, rs_wLR, rs_wzr};
static constexpr RegStorage reserved64_regs_arr[] = {rs_xSELF, rs_sp, rs_xLR, rs_xzr};
static constexpr RegStorage core_temps_arr[] =
{rs_w0, rs_w1, rs_w2, rs_w3, rs_w4, rs_w5, rs_w6, rs_w7,
rs_w8, rs_w9, rs_w10, rs_w11, rs_w12, rs_w13, rs_w14, rs_w15, rs_w16,
- rs_w17};
+ rs_w17, rs_w18};
static constexpr RegStorage core64_temps_arr[] =
{rs_x0, rs_x1, rs_x2, rs_x3, rs_x4, rs_x5, rs_x6, rs_x7,
rs_x8, rs_x9, rs_x10, rs_x11, rs_x12, rs_x13, rs_x14, rs_x15, rs_x16,
- rs_x17};
+ rs_x17, rs_x18};
static constexpr RegStorage sp_temps_arr[] =
{rs_f0, rs_f1, rs_f2, rs_f3, rs_f4, rs_f5, rs_f6, rs_f7,
rs_f16, rs_f17, rs_f18, rs_f19, rs_f20, rs_f21, rs_f22, rs_f23,
@@ -691,6 +689,7 @@
Clobber(rs_x15);
Clobber(rs_x16);
Clobber(rs_x17);
+ Clobber(rs_x18);
Clobber(rs_x30);
Clobber(rs_f0);
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index f4bf31f..c803e65 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -670,7 +670,7 @@
void Mir2Lir::CreateMappingTables() {
- bool generate_src_map = cu_->compiler_driver->GetCompilerOptions().GetIncludeDebugSymbols();
+ bool generate_src_map = cu_->compiler_driver->GetCompilerOptions().GetGenerateDebugInfo();
uint32_t pc2dex_data_size = 0u;
uint32_t pc2dex_entries = 0u;
@@ -1071,7 +1071,7 @@
pc_rel_temp_(nullptr),
dex_cache_arrays_min_offset_(std::numeric_limits<uint32_t>::max()),
cfi_(&last_lir_insn_,
- cu->compiler_driver->GetCompilerOptions().GetIncludeCFI(),
+ cu->compiler_driver->GetCompilerOptions().GetGenerateDebugInfo(),
arena),
in_to_reg_storage_mapping_(arena) {
switch_tables_.reserve(4);
diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc
index 8694ebc..dd68dd4 100644
--- a/compiler/dex/quick/quick_cfi_test.cc
+++ b/compiler/dex/quick/quick_cfi_test.cc
@@ -59,8 +59,7 @@
false,
CompilerOptions::kDefaultTopKProfileThreshold,
false,
- true, // include_debug_symbols.
- true, // include_cfi
+ true, // generate_debug_info.
false,
false,
false,
diff --git a/compiler/dex/quick/quick_cfi_test_expected.inc b/compiler/dex/quick/quick_cfi_test_expected.inc
index 52d66a4..3032697 100644
--- a/compiler/dex/quick/quick_cfi_test_expected.inc
+++ b/compiler/dex/quick/quick_cfi_test_expected.inc
@@ -33,15 +33,15 @@
// 0x00000014: .cfi_def_cfa_offset: 64
static constexpr uint8_t expected_asm_kArm64[] = {
- 0xFF, 0x03, 0x01, 0xD1, 0xE8, 0xA7, 0x01, 0x6D, 0xF3, 0xD3, 0x02, 0xA9,
+ 0xFF, 0x03, 0x01, 0xD1, 0xE8, 0xA7, 0x01, 0x6D, 0xF4, 0xD7, 0x02, 0xA9,
0xFE, 0x1F, 0x00, 0xF9, 0xE0, 0x03, 0x00, 0xF9, 0xE8, 0xA7, 0x41, 0x6D,
- 0xF3, 0xD3, 0x42, 0xA9, 0xFE, 0x1F, 0x40, 0xF9, 0xFF, 0x03, 0x01, 0x91,
+ 0xF4, 0xD7, 0x42, 0xA9, 0xFE, 0x1F, 0x40, 0xF9, 0xFF, 0x03, 0x01, 0x91,
0xC0, 0x03, 0x5F, 0xD6,
};
static constexpr uint8_t expected_cfi_kArm64[] = {
- 0x44, 0x0E, 0x40, 0x44, 0x05, 0x48, 0x0A, 0x05, 0x49, 0x08, 0x44, 0x93,
- 0x06, 0x94, 0x04, 0x44, 0x9E, 0x02, 0x44, 0x0A, 0x44, 0x06, 0x48, 0x06,
- 0x49, 0x44, 0xD3, 0xD4, 0x44, 0xDE, 0x44, 0x0E, 0x00, 0x44, 0x0B, 0x0E,
+ 0x44, 0x0E, 0x40, 0x44, 0x05, 0x48, 0x0A, 0x05, 0x49, 0x08, 0x44, 0x94,
+ 0x06, 0x95, 0x04, 0x44, 0x9E, 0x02, 0x44, 0x0A, 0x44, 0x06, 0x48, 0x06,
+ 0x49, 0x44, 0xD4, 0xD5, 0x44, 0xDE, 0x44, 0x0E, 0x00, 0x44, 0x0B, 0x0E,
0x40,
};
// 0x00000000: sub sp, sp, #0x40 (64)
@@ -49,9 +49,9 @@
// 0x00000004: stp d8, d9, [sp, #24]
// 0x00000008: .cfi_offset_extended: r72 at cfa-40
// 0x00000008: .cfi_offset_extended: r73 at cfa-32
-// 0x00000008: stp x19, x20, [sp, #40]
-// 0x0000000c: .cfi_offset: r19 at cfa-24
-// 0x0000000c: .cfi_offset: r20 at cfa-16
+// 0x00000008: stp x20, x21, [sp, #40]
+// 0x0000000c: .cfi_offset: r20 at cfa-24
+// 0x0000000c: .cfi_offset: r21 at cfa-16
// 0x0000000c: str lr, [sp, #56]
// 0x00000010: .cfi_offset: r30 at cfa-8
// 0x00000010: str x0, [sp]
@@ -59,9 +59,9 @@
// 0x00000014: ldp d8, d9, [sp, #24]
// 0x00000018: .cfi_restore_extended: r72
// 0x00000018: .cfi_restore_extended: r73
-// 0x00000018: ldp x19, x20, [sp, #40]
-// 0x0000001c: .cfi_restore: r19
+// 0x00000018: ldp x20, x21, [sp, #40]
// 0x0000001c: .cfi_restore: r20
+// 0x0000001c: .cfi_restore: r21
// 0x0000001c: ldr lr, [sp, #56]
// 0x00000020: .cfi_restore: r30
// 0x00000020: add sp, sp, #0x40 (64)
diff --git a/compiler/dex/quick/x86/quick_assemble_x86_test.cc b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
index f58f206..798e23f 100644
--- a/compiler/dex/quick/x86/quick_assemble_x86_test.cc
+++ b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
@@ -42,8 +42,7 @@
false,
CompilerOptions::kDefaultTopKProfileThreshold,
false,
- false,
- false,
+ CompilerOptions::kDefaultGenerateDebugInfo,
false,
false,
false,
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index ac7a4a7..6d48598 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -98,7 +98,7 @@
}
size_t ref_bitmap_bytes = RoundUp(ref_bitmap_bits, kBitsPerByte) / kBitsPerByte;
// There are 2 bytes to encode the number of entries.
- if (num_entries >= 65536) {
+ if (num_entries > std::numeric_limits<uint16_t>::max()) {
LOG(WARNING) << "Cannot encode GC map for method with " << num_entries << " entries: "
<< PrettyMethod(method_verifier->GetMethodReference().dex_method_index,
*method_verifier->GetMethodReference().dex_file);
diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h
index 242e3df..07f9a9b 100644
--- a/compiler/dex/verified_method.h
+++ b/compiler/dex/verified_method.h
@@ -120,7 +120,7 @@
DequickenMap dequicken_map_;
SafeCastSet safe_cast_set_;
- bool has_verification_failures_;
+ bool has_verification_failures_ = false;
// Copy of mapping generated by verifier of dex PCs of string init invocations
// to the set of other registers that the receiver has been copied into.
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index e963c12..22fcf87 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -76,8 +76,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.
@@ -1242,7 +1242,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/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index c5fc98a..226e6b7 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -30,8 +30,7 @@
include_patch_information_(kDefaultIncludePatchInformation),
top_k_profile_threshold_(kDefaultTopKProfileThreshold),
debuggable_(false),
- include_debug_symbols_(kDefaultIncludeDebugSymbols),
- include_cfi_(false),
+ generate_debug_info_(kDefaultGenerateDebugInfo),
implicit_null_checks_(true),
implicit_so_checks_(true),
implicit_suspend_checks_(false),
@@ -56,8 +55,7 @@
bool include_patch_information,
double top_k_profile_threshold,
bool debuggable,
- bool include_debug_symbols,
- bool include_cfi,
+ bool generate_debug_info,
bool implicit_null_checks,
bool implicit_so_checks,
bool implicit_suspend_checks,
@@ -76,8 +74,7 @@
include_patch_information_(include_patch_information),
top_k_profile_threshold_(top_k_profile_threshold),
debuggable_(debuggable),
- include_debug_symbols_(include_debug_symbols),
- include_cfi_(include_cfi),
+ generate_debug_info_(generate_debug_info),
implicit_null_checks_(implicit_null_checks),
implicit_so_checks_(implicit_so_checks),
implicit_suspend_checks_(implicit_suspend_checks),
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index bf3f8ec..356663b 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -49,7 +49,7 @@
static const size_t kDefaultTinyMethodThreshold = 20;
static const size_t kDefaultNumDexMethodsThreshold = 900;
static constexpr double kDefaultTopKProfileThreshold = 90.0;
- static const bool kDefaultIncludeDebugSymbols = kIsDebugBuild;
+ static const bool kDefaultGenerateDebugInfo = kIsDebugBuild;
static const bool kDefaultIncludePatchInformation = false;
CompilerOptions();
@@ -64,8 +64,7 @@
bool include_patch_information,
double top_k_profile_threshold,
bool debuggable,
- bool include_debug_symbols,
- bool include_cfi,
+ bool generate_debug_info,
bool implicit_null_checks,
bool implicit_so_checks,
bool implicit_suspend_checks,
@@ -146,13 +145,8 @@
return debuggable_;
}
- bool GetIncludeDebugSymbols() const {
- return include_debug_symbols_;
- }
-
- bool GetIncludeCFI() const {
- // include-debug-symbols implies include-cfi.
- return include_cfi_ || include_debug_symbols_;
+ bool GetGenerateDebugInfo() const {
+ return generate_debug_info_;
}
bool GetImplicitNullChecks() const {
@@ -212,8 +206,7 @@
// When using a profile file only the top K% of the profiled samples will be compiled.
const double top_k_profile_threshold_;
const bool debuggable_;
- const bool include_debug_symbols_;
- const bool include_cfi_;
+ const bool generate_debug_info_;
const bool implicit_null_checks_;
const bool implicit_so_checks_;
const bool implicit_suspend_checks_;
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..4d423d0 100644
--- a/compiler/dwarf/dwarf_test.cc
+++ b/compiler/dwarf/dwarf_test.cc
@@ -29,6 +29,8 @@
// Run the tests only on host since we need objdump.
#ifndef HAVE_ANDROID_OS
+constexpr CFIFormat kCFIFormat = DW_DEBUG_FRAME_FORMAT;
+
TEST_F(DwarfTest, DebugFrame) {
const bool is64bit = false;
@@ -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 370c744..f819c49 100644
--- a/compiler/dwarf/dwarf_test.h
+++ b/compiler/dwarf/dwarf_test.h
@@ -68,7 +68,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);
@@ -85,9 +85,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());
@@ -166,7 +166,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 2c68bb8..bbd962f 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -166,6 +166,10 @@
patched_(false), patch_(patch), patch_base_section_(patch_base_section) {
}
+ RawSection(const std::string& name, Elf_Word type)
+ : RawSection(name, type, 0, nullptr, 0, 1, 0, nullptr, nullptr) {
+ }
+
Elf_Word GetSize() const OVERRIDE {
return buffer_.size();
}
@@ -263,7 +267,7 @@
class StrtabSection FINAL : public Section {
public:
StrtabSection(const std::string& name, Elf_Word flags)
- : Section(name, SHT_STRTAB, flags, nullptr, 0, 1, 1) {
+ : Section(name, SHT_STRTAB, flags, nullptr, 0, 1, 0) {
buffer_.reserve(4 * KB);
// The first entry of strtab must be empty string.
buffer_ += '\0';
@@ -306,7 +310,7 @@
SymtabSection(const std::string& name, Elf_Word type, Elf_Word flags,
StrtabSection* strtab)
- : Section(name, type, flags, strtab, 0, sizeof(Elf_Word), sizeof(Elf_Sym)),
+ : Section(name, type, flags, strtab, 0, sizeof(Elf_Off), sizeof(Elf_Sym)),
strtab_(strtab) {
}
@@ -499,7 +503,7 @@
text_(".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR,
nullptr, 0, kPageSize, 0, text_size, text_writer),
bss_(".bss", bss_size),
- dynamic_(".dynamic", &dynsym_),
+ dynamic_(".dynamic", &dynstr_),
strtab_(".strtab", 0),
symtab_(".symtab", SHT_SYMTAB, 0, &strtab_),
shstrtab_(".shstrtab", 0) {
@@ -641,11 +645,10 @@
// It is easiest to just reserve a fixed amount of space for them.
constexpr size_t kMaxProgramHeaders = 8;
constexpr size_t kProgramHeadersOffset = sizeof(Elf_Ehdr);
- constexpr size_t kProgramHeadersSize = sizeof(Elf_Phdr) * kMaxProgramHeaders;
// Layout of all sections - determine the final file offsets and addresses.
// This must be done after we have built all sections and know their size.
- Elf_Off file_offset = kProgramHeadersOffset + kProgramHeadersSize;
+ Elf_Off file_offset = kProgramHeadersOffset + sizeof(Elf_Phdr) * kMaxProgramHeaders;
Elf_Addr load_address = file_offset;
std::vector<Elf_Shdr> section_headers;
section_headers.reserve(1u + sections.size());
@@ -674,7 +677,7 @@
// Collect section headers into continuous array for convenience.
section_headers.push_back(*header);
}
- Elf_Off section_headers_offset = RoundUp(file_offset, sizeof(Elf_Word));
+ Elf_Off section_headers_offset = RoundUp(file_offset, sizeof(Elf_Off));
// Create program headers now that we know the layout of the whole file.
// Each segment contains one or more sections which are mapped together.
@@ -682,8 +685,7 @@
// PT_LOAD does the mapping. Other PT_* types allow the program to locate
// interesting parts of memory and their addresses overlap with PT_LOAD.
std::vector<Elf_Phdr> program_headers;
- program_headers.push_back(MakeProgramHeader(PT_PHDR, PF_R,
- kProgramHeadersOffset, kProgramHeadersSize, sizeof(Elf_Word)));
+ program_headers.push_back(Elf_Phdr()); // Placeholder for PT_PHDR.
// Create the main LOAD R segment which spans all sections up to .rodata.
const Elf_Shdr* rodata = rodata_.GetHeader();
program_headers.push_back(MakeProgramHeader(PT_LOAD, PF_R,
@@ -709,6 +711,9 @@
program_headers.push_back(MakeProgramHeader(PT_GNU_EH_FRAME, PF_R, *eh_frame_hdr));
}
}
+ DCHECK_EQ(program_headers[0].p_type, 0u); // Check placeholder.
+ program_headers[0] = MakeProgramHeader(PT_PHDR, PF_R,
+ kProgramHeadersOffset, program_headers.size() * sizeof(Elf_Phdr), sizeof(Elf_Off));
CHECK_LE(program_headers.size(), kMaxProgramHeaders);
// Create the main ELF header.
@@ -777,10 +782,12 @@
template<typename T>
static bool WriteArray(File* elf_file, const T* data, size_t count) {
- DCHECK(data != nullptr);
- if (!elf_file->WriteFully(data, count * sizeof(T))) {
- PLOG(ERROR) << "Failed to write to file " << elf_file->GetPath();
- return false;
+ if (count != 0) {
+ DCHECK(data != nullptr);
+ if (!elf_file->WriteFully(data, count * sizeof(T))) {
+ PLOG(ERROR) << "Failed to write to file " << elf_file->GetPath();
+ return false;
+ }
}
return true;
}
diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc
index dbbe82e..c68bbc0 100644
--- a/compiler/elf_writer_debug.cc
+++ b/compiler/elf_writer_debug.cc
@@ -30,9 +30,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);
@@ -58,7 +59,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: {
@@ -81,7 +83,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:
@@ -97,7 +100,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: {
@@ -123,7 +127,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: {
@@ -149,7 +154,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:
@@ -159,58 +165,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);
+ }
}
}
@@ -235,6 +244,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.
@@ -272,7 +282,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);
@@ -317,7 +327,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..dce1e86 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -19,6 +19,7 @@
#include <unordered_map>
#include <unordered_set>
+#include "base/casts.h"
#include "base/logging.h"
#include "base/unix_file/fd_file.h"
#include "compiled_method.h"
@@ -37,6 +38,24 @@
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.
+// Let's use .debug_frame because it is easier to strip or compress.
+constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_DEBUG_FRAME_FORMAT;
+
+// The ARM specification defines three special mapping symbols
+// $a, $t and $d which mark ARM, Thumb and data ranges respectively.
+// These symbols can be used by tools, for example, to pretty
+// print instructions correctly. Objdump will use them if they
+// exist, but it will still work well without them.
+// However, these extra symbols take space, so let's just generate
+// one symbol which marks the whole .text section as code.
+constexpr bool kGenerateSingleArmMappingSymbol = true;
+
template <typename ElfTypes>
bool ElfWriterQuick<ElfTypes>::Create(File* elf_file,
OatWriter* oat_writer,
@@ -51,36 +70,17 @@
template <typename ElfTypes>
static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, OatWriter* oat_writer);
-// Encode patch locations in .oat_patches format.
+// Encode patch locations as LEB128 list of deltas between consecutive addresses.
template <typename ElfTypes>
-void ElfWriterQuick<ElfTypes>::EncodeOatPatches(
- const OatWriter::PatchLocationsMap& sections,
- std::vector<uint8_t>* buffer) {
- for (const auto& section : sections) {
- const std::string& name = section.first;
- std::vector<uintptr_t>* locations = section.second.get();
- DCHECK(!name.empty());
- std::sort(locations->begin(), locations->end());
- // Reserve buffer space - guess 2 bytes per ULEB128.
- buffer->reserve(buffer->size() + name.size() + locations->size() * 2);
- // Write null-terminated section name.
- const uint8_t* name_data = reinterpret_cast<const uint8_t*>(name.c_str());
- buffer->insert(buffer->end(), name_data, name_data + name.size() + 1);
- // Write placeholder for data length.
- size_t length_pos = buffer->size();
- EncodeUnsignedLeb128(buffer, UINT32_MAX);
- // Write LEB128 encoded list of advances (deltas between consequtive addresses).
- size_t data_pos = buffer->size();
- uintptr_t address = 0; // relative to start of section.
- for (uintptr_t location : *locations) {
- DCHECK_LT(location - address, UINT32_MAX) << "Large gap between patch locations";
- EncodeUnsignedLeb128(buffer, location - address);
- address = location;
- }
- // Update length.
- UpdateUnsignedLeb128(buffer->data() + length_pos, buffer->size() - data_pos);
+void ElfWriterQuick<ElfTypes>::EncodeOatPatches(const std::vector<uintptr_t>& locations,
+ std::vector<uint8_t>* buffer) {
+ buffer->reserve(buffer->size() + locations.size() * 2); // guess 2 bytes per ULEB128.
+ uintptr_t address = 0; // relative to start of section.
+ for (uintptr_t location : locations) {
+ DCHECK_GE(location, address) << "Patch locations are not in sorted order";
+ EncodeUnsignedLeb128(buffer, dchecked_integral_cast<uint32_t>(location - address));
+ address = location;
}
- buffer->push_back(0); // End of sections.
}
class RodataWriter FINAL : public CodeOutput {
@@ -156,61 +156,95 @@
isa, rodata_size, &rodata_writer, text_size, &text_writer, bss_size));
// Add debug sections.
- // They are stack allocated here (in the same scope as the builder),
- // but they are registred with the builder only if they are used.
+ // They are allocated here (in the same scope as the builder),
+ // but they are registered with the builder only if they are used.
using RawSection = typename ElfBuilder<ElfTypes>::RawSection;
const auto* text = builder->GetText();
const bool is64bit = Is64BitInstructionSet(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_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);
- RawSection debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
- RawSection debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0,
- Patch<Elf_Addr, uint32_t, kAbsoluteAddress>, text);
+ const int pointer_size = GetInstructionSetPointerSize(isa);
+ std::unique_ptr<RawSection> eh_frame(new RawSection(
+ ".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0,
+ is64bit ? Patch<Elf_Addr, uint64_t, kPointerRelativeAddress> :
+ Patch<Elf_Addr, uint32_t, kPointerRelativeAddress>,
+ text));
+ std::unique_ptr<RawSection> eh_frame_hdr(new RawSection(
+ ".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0,
+ Patch<Elf_Addr, uint32_t, kSectionRelativeAddress>, text));
+ std::unique_ptr<RawSection> debug_frame(new RawSection(
+ ".debug_frame", SHT_PROGBITS, 0, nullptr, 0, pointer_size, 0,
+ is64bit ? Patch<Elf_Addr, uint64_t, kAbsoluteAddress> :
+ Patch<Elf_Addr, uint32_t, kAbsoluteAddress>,
+ text));
+ std::unique_ptr<RawSection> debug_frame_oat_patches(new RawSection(
+ ".debug_frame.oat_patches", SHT_OAT_PATCH));
+ std::unique_ptr<RawSection> debug_info(new RawSection(
+ ".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0,
+ Patch<Elf_Addr, uint32_t, kAbsoluteAddress>, text));
+ std::unique_ptr<RawSection> debug_info_oat_patches(new RawSection(
+ ".debug_info.oat_patches", SHT_OAT_PATCH));
+ std::unique_ptr<RawSection> debug_abbrev(new RawSection(
+ ".debug_abbrev", SHT_PROGBITS));
+ std::unique_ptr<RawSection> debug_str(new RawSection(
+ ".debug_str", SHT_PROGBITS));
+ std::unique_ptr<RawSection> debug_line(new RawSection(
+ ".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0,
+ Patch<Elf_Addr, uint32_t, kAbsoluteAddress>, text));
+ std::unique_ptr<RawSection> debug_line_oat_patches(new RawSection(
+ ".debug_line.oat_patches", SHT_OAT_PATCH));
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 (compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
+ if (compiler_driver_->GetCompilerOptions().GetGenerateDebugInfo()) {
+ // Generate CFI (stack unwinding information).
+ 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.get());
+ builder->RegisterSection(eh_frame_hdr.get());
+ } 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.get());
+ EncodeOatPatches(*debug_frame->GetPatchLocations(),
+ debug_frame_oat_patches->GetBuffer());
+ builder->RegisterSection(debug_frame_oat_patches.get());
+ }
// Add methods to .symtab.
WriteDebugSymbols(builder.get(), oat_writer);
// Generate DWARF .debug_* sections.
dwarf::WriteDebugSections(
compiler_driver_, oat_writer,
- debug_info.GetBuffer(), debug_info.GetPatchLocations(),
- debug_abbrev.GetBuffer(),
- debug_str.GetBuffer(),
- debug_line.GetBuffer(), debug_line.GetPatchLocations());
- builder->RegisterSection(&debug_info);
- builder->RegisterSection(&debug_abbrev);
- builder->RegisterSection(&debug_str);
- builder->RegisterSection(&debug_line);
- *oat_writer->GetAbsolutePatchLocationsFor(".debug_info") =
- *debug_info.GetPatchLocations();
- *oat_writer->GetAbsolutePatchLocationsFor(".debug_line") =
- *debug_line.GetPatchLocations();
+ debug_info->GetBuffer(), debug_info->GetPatchLocations(),
+ debug_abbrev->GetBuffer(),
+ debug_str->GetBuffer(),
+ debug_line->GetBuffer(), debug_line->GetPatchLocations());
+ builder->RegisterSection(debug_info.get());
+ EncodeOatPatches(*debug_info->GetPatchLocations(),
+ debug_info_oat_patches->GetBuffer());
+ builder->RegisterSection(debug_info_oat_patches.get());
+ builder->RegisterSection(debug_abbrev.get());
+ builder->RegisterSection(debug_str.get());
+ builder->RegisterSection(debug_line.get());
+ EncodeOatPatches(*debug_line->GetPatchLocations(),
+ debug_line_oat_patches->GetBuffer());
+ builder->RegisterSection(debug_line_oat_patches.get());
}
}
- // Add relocation section.
- RawSection oat_patches(".oat_patches", SHT_OAT_PATCH, 0, nullptr, 0, 1, 0);
- if (compiler_driver_->GetCompilerOptions().GetIncludePatchInformation() ||
- // ElfWriter::Fixup will be called regardless and it needs to be able
- // to patch debug sections so we have to include patches for them.
- compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
- EncodeOatPatches(oat_writer->GetAbsolutePatchLocations(), oat_patches.GetBuffer());
- builder->RegisterSection(&oat_patches);
+ // Add relocation section for .text.
+ std::unique_ptr<RawSection> text_oat_patches(new RawSection(
+ ".text.oat_patches", SHT_OAT_PATCH));
+ if (compiler_driver_->GetCompilerOptions().GetIncludePatchInformation()) {
+ // Note that ElfWriter::Fixup will be called regardless and therefore
+ // we need to include oat_patches for debug sections unconditionally.
+ EncodeOatPatches(oat_writer->GetAbsolutePatchLocations(),
+ text_oat_patches->GetBuffer());
+ builder->RegisterSection(text_oat_patches.get());
}
return builder->Write(elf_file_);
@@ -219,6 +253,7 @@
template <typename ElfTypes>
static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, OatWriter* oat_writer) {
const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetMethodDebugInfo();
+ bool generated_mapping_symbol = false;
// Find all addresses (low_pc) which contain deduped methods.
// The first instance of method is not marked deduped_, but the rest is.
@@ -247,9 +282,14 @@
// Conforming to aaelf, add $t mapping symbol to indicate start of a sequence of thumb2
// instructions, so that disassembler tools can correctly disassemble.
+ // Note that even if we generate just a single mapping symbol, ARM's Streamline
+ // requires it to match function symbol. Just address 0 does not work.
if (it->compiled_method_->GetInstructionSet() == kThumb2) {
- symtab->AddSymbol("$t", builder->GetText(), it->low_pc_ & ~1, true,
- 0, STB_LOCAL, STT_NOTYPE);
+ if (!generated_mapping_symbol || !kGenerateSingleArmMappingSymbol) {
+ symtab->AddSymbol("$t", builder->GetText(), it->low_pc_ & ~1, true,
+ 0, STB_LOCAL, STT_NOTYPE);
+ generated_mapping_symbol = true;
+ }
}
}
}
diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h
index 955b568..fd202ee 100644
--- a/compiler/elf_writer_quick.h
+++ b/compiler/elf_writer_quick.h
@@ -35,7 +35,7 @@
const CompilerDriver& driver)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static void EncodeOatPatches(const OatWriter::PatchLocationsMap& sections,
+ static void EncodeOatPatches(const std::vector<uintptr_t>& locations,
std::vector<uint8_t>* buffer);
protected:
diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc
index 08523d8..ccf34b8 100644
--- a/compiler/elf_writer_test.cc
+++ b/compiler/elf_writer_test.cc
@@ -88,73 +88,41 @@
}
}
-// Run only on host since we do unaligned memory accesses.
-#ifndef HAVE_ANDROID_OS
-
-static void PatchSection(const std::vector<uintptr_t>& patch_locations,
- std::vector<uint8_t>* section, int32_t delta) {
- for (uintptr_t location : patch_locations) {
- *reinterpret_cast<int32_t*>(section->data() + location) += delta;
- }
-}
-
TEST_F(ElfWriterTest, EncodeDecodeOatPatches) {
- std::vector<uint8_t> oat_patches; // Encoded patches.
+ const std::vector<std::vector<uintptr_t>> test_data {
+ { 0, 4, 8, 15, 128, 200 },
+ { 8, 8 + 127 },
+ { 8, 8 + 128 },
+ { },
+ };
+ for (const auto& patch_locations : test_data) {
+ constexpr int32_t delta = 0x11235813;
- // Encode patch locations for a few sections.
- OatWriter::PatchLocationsMap sections;
- std::vector<uintptr_t> patches0 { 0, 4, 8, 15, 128, 200 }; // NOLINT
- sections.emplace(".section0", std::unique_ptr<std::vector<uintptr_t>>(
- new std::vector<uintptr_t> { patches0 }));
- std::vector<uintptr_t> patches1 { 8, 127 }; // NOLINT
- sections.emplace(".section1", std::unique_ptr<std::vector<uintptr_t>>(
- new std::vector<uintptr_t> { patches1 }));
- std::vector<uintptr_t> patches2 { }; // NOLINT
- sections.emplace(".section2", std::unique_ptr<std::vector<uintptr_t>>(
- new std::vector<uintptr_t> { patches2 }));
- ElfWriterQuick32::EncodeOatPatches(sections, &oat_patches);
+ // Encode patch locations.
+ std::vector<uint8_t> oat_patches;
+ ElfWriterQuick32::EncodeOatPatches(patch_locations, &oat_patches);
- // Create buffers to be patched.
- std::vector<uint8_t> initial_data(256);
- for (size_t i = 0; i < initial_data.size(); i++) {
- initial_data[i] = i;
+ // Create buffer to be patched.
+ std::vector<uint8_t> initial_data(256);
+ for (size_t i = 0; i < initial_data.size(); i++) {
+ initial_data[i] = i;
+ }
+
+ // Patch manually.
+ std::vector<uint8_t> expected = initial_data;
+ for (uintptr_t location : patch_locations) {
+ typedef __attribute__((__aligned__(1))) uint32_t UnalignedAddress;
+ *reinterpret_cast<UnalignedAddress*>(expected.data() + location) += delta;
+ }
+
+ // Decode and apply patch locations.
+ std::vector<uint8_t> actual = initial_data;
+ ElfFileImpl32::ApplyOatPatches(
+ oat_patches.data(), oat_patches.data() + oat_patches.size(), delta,
+ actual.data(), actual.data() + actual.size());
+
+ EXPECT_EQ(expected, actual);
}
- std::vector<uint8_t> section0_expected = initial_data;
- std::vector<uint8_t> section1_expected = initial_data;
- std::vector<uint8_t> section2_expected = initial_data;
- std::vector<uint8_t> section0_actual = initial_data;
- std::vector<uint8_t> section1_actual = initial_data;
- std::vector<uint8_t> section2_actual = initial_data;
-
- // Patch manually.
- constexpr int32_t delta = 0x11235813;
- PatchSection(patches0, §ion0_expected, delta);
- PatchSection(patches1, §ion1_expected, delta);
- PatchSection(patches2, §ion2_expected, delta);
-
- // Decode and apply patch locations.
- bool section0_successful = ElfFileImpl32::ApplyOatPatches(
- oat_patches.data(), oat_patches.data() + oat_patches.size(),
- ".section0", delta,
- section0_actual.data(), section0_actual.data() + section0_actual.size());
- EXPECT_TRUE(section0_successful);
- EXPECT_EQ(section0_expected, section0_actual);
-
- bool section1_successful = ElfFileImpl32::ApplyOatPatches(
- oat_patches.data(), oat_patches.data() + oat_patches.size(),
- ".section1", delta,
- section1_actual.data(), section1_actual.data() + section1_actual.size());
- EXPECT_TRUE(section1_successful);
- EXPECT_EQ(section1_expected, section1_actual);
-
- bool section2_successful = ElfFileImpl32::ApplyOatPatches(
- oat_patches.data(), oat_patches.data() + oat_patches.size(),
- ".section2", delta,
- section2_actual.data(), section2_actual.data() + section2_actual.size());
- EXPECT_TRUE(section2_successful);
- EXPECT_EQ(section2_expected, section2_actual);
}
-#endif
-
} // namespace art
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index e8461b0..73e121f 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -1113,9 +1113,9 @@
heap->VisitObjects(CopyAndFixupObjectsCallback, this);
// Fix up the object previously had hash codes.
for (const auto& hash_pair : saved_hashcode_map_) {
- Object* const obj = hash_pair.first;
- DCHECK_EQ(obj->GetLockWord(false).ReadBarrierState(), 0U);
- obj->SetLockWord(LockWord::FromHashCode(hash_pair.second, 0U), false);
+ Object* obj = hash_pair.first;
+ DCHECK_EQ(obj->GetLockWord<kVerifyNone>(false).ReadBarrierState(), 0U);
+ obj->SetLockWord<kVerifyNone>(LockWord::FromHashCode(hash_pair.second, 0U), false);
}
saved_hashcode_map_.clear();
}
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index ce277cd..a1d8226 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -74,8 +74,7 @@
false,
CompilerOptions::kDefaultTopKProfileThreshold,
false, // TODO: Think about debuggability of JIT-compiled code.
- false,
- false,
+ CompilerOptions::kDefaultGenerateDebugInfo,
false,
false,
false,
diff --git a/compiler/jni/jni_cfi_test_expected.inc b/compiler/jni/jni_cfi_test_expected.inc
index 09b6034..dd4496f 100644
--- a/compiler/jni/jni_cfi_test_expected.inc
+++ b/compiler/jni/jni_cfi_test_expected.inc
@@ -84,14 +84,13 @@
0xFF, 0x03, 0x03, 0xD1, 0xF3, 0x53, 0x06, 0xA9, 0xF5, 0x5B, 0x07, 0xA9,
0xF7, 0x63, 0x08, 0xA9, 0xF9, 0x6B, 0x09, 0xA9, 0xFB, 0x73, 0x0A, 0xA9,
0xFD, 0x7B, 0x0B, 0xA9, 0xE8, 0x27, 0x02, 0x6D, 0xEA, 0x2F, 0x03, 0x6D,
- 0xEC, 0x37, 0x04, 0x6D, 0xEE, 0x3F, 0x05, 0x6D, 0xF5, 0x03, 0x12, 0xAA,
- 0xE0, 0x03, 0x00, 0xF9, 0xE1, 0xCB, 0x00, 0xB9, 0xE0, 0xCF, 0x00, 0xBD,
- 0xE2, 0xD3, 0x00, 0xB9, 0xE3, 0xD7, 0x00, 0xB9, 0xFF, 0x83, 0x00, 0xD1,
- 0xFF, 0x83, 0x00, 0x91, 0xF2, 0x03, 0x15, 0xAA, 0xF3, 0x53, 0x46, 0xA9,
- 0xF5, 0x5B, 0x47, 0xA9, 0xF7, 0x63, 0x48, 0xA9, 0xF9, 0x6B, 0x49, 0xA9,
- 0xFB, 0x73, 0x4A, 0xA9, 0xFD, 0x7B, 0x4B, 0xA9, 0xE8, 0x27, 0x42, 0x6D,
- 0xEA, 0x2F, 0x43, 0x6D, 0xEC, 0x37, 0x44, 0x6D, 0xEE, 0x3F, 0x45, 0x6D,
- 0xFF, 0x03, 0x03, 0x91, 0xC0, 0x03, 0x5F, 0xD6,
+ 0xEC, 0x37, 0x04, 0x6D, 0xEE, 0x3F, 0x05, 0x6D, 0xE0, 0x03, 0x00, 0xF9,
+ 0xE1, 0xCB, 0x00, 0xB9, 0xE0, 0xCF, 0x00, 0xBD, 0xE2, 0xD3, 0x00, 0xB9,
+ 0xE3, 0xD7, 0x00, 0xB9, 0xFF, 0x83, 0x00, 0xD1, 0xFF, 0x83, 0x00, 0x91,
+ 0xF3, 0x53, 0x46, 0xA9, 0xF5, 0x5B, 0x47, 0xA9, 0xF7, 0x63, 0x48, 0xA9,
+ 0xF9, 0x6B, 0x49, 0xA9, 0xFB, 0x73, 0x4A, 0xA9, 0xFD, 0x7B, 0x4B, 0xA9,
+ 0xE8, 0x27, 0x42, 0x6D, 0xEA, 0x2F, 0x43, 0x6D, 0xEC, 0x37, 0x44, 0x6D,
+ 0xEE, 0x3F, 0x45, 0x6D, 0xFF, 0x03, 0x03, 0x91, 0xC0, 0x03, 0x5F, 0xD6,
};
static constexpr uint8_t expected_cfi_kArm64[] = {
0x44, 0x0E, 0xC0, 0x01, 0x44, 0x93, 0x18, 0x94, 0x16, 0x44, 0x95, 0x14,
@@ -99,15 +98,15 @@
0x44, 0x9B, 0x08, 0x9C, 0x06, 0x44, 0x9D, 0x04, 0x9E, 0x02, 0x44, 0x05,
0x48, 0x28, 0x05, 0x49, 0x26, 0x44, 0x05, 0x4A, 0x24, 0x05, 0x4B, 0x22,
0x44, 0x05, 0x4C, 0x20, 0x05, 0x4D, 0x1E, 0x44, 0x05, 0x4E, 0x1C, 0x05,
- 0x4F, 0x1A, 0x5C, 0x0E, 0xE0, 0x01, 0x44, 0x0E, 0xC0, 0x01, 0x44, 0x0A,
- 0x44, 0xD3, 0xD4, 0x44, 0xD5, 0xD6, 0x44, 0xD7, 0xD8, 0x44, 0xD9, 0xDA,
- 0x44, 0xDB, 0xDC, 0x44, 0xDD, 0xDE, 0x44, 0x06, 0x48, 0x06, 0x49, 0x44,
- 0x06, 0x4A, 0x06, 0x4B, 0x44, 0x06, 0x4C, 0x06, 0x4D, 0x44, 0x06, 0x4E,
- 0x06, 0x4F, 0x44, 0x0E, 0x00, 0x44, 0x0B, 0x0E, 0xC0, 0x01,
+ 0x4F, 0x1A, 0x58, 0x0E, 0xE0, 0x01, 0x44, 0x0E, 0xC0, 0x01, 0x0A, 0x44,
+ 0xD3, 0xD4, 0x44, 0xD5, 0xD6, 0x44, 0xD7, 0xD8, 0x44, 0xD9, 0xDA, 0x44,
+ 0xDB, 0xDC, 0x44, 0xDD, 0xDE, 0x44, 0x06, 0x48, 0x06, 0x49, 0x44, 0x06,
+ 0x4A, 0x06, 0x4B, 0x44, 0x06, 0x4C, 0x06, 0x4D, 0x44, 0x06, 0x4E, 0x06,
+ 0x4F, 0x44, 0x0E, 0x00, 0x44, 0x0B, 0x0E, 0xC0, 0x01,
};
// 0x00000000: sub sp, sp, #0xc0 (192)
// 0x00000004: .cfi_def_cfa_offset: 192
-// 0x00000004: stp x19, x20, [sp, #96]
+// 0x00000004: stp tr, x20, [sp, #96]
// 0x00000008: .cfi_offset: r19 at cfa-96
// 0x00000008: .cfi_offset: r20 at cfa-88
// 0x00000008: stp x21, x22, [sp, #112]
@@ -137,53 +136,51 @@
// 0x00000028: stp d14, d15, [sp, #80]
// 0x0000002c: .cfi_offset_extended: r78 at cfa-112
// 0x0000002c: .cfi_offset_extended: r79 at cfa-104
-// 0x0000002c: mov x21, tr
-// 0x00000030: str x0, [sp]
-// 0x00000034: str w1, [sp, #200]
-// 0x00000038: str s0, [sp, #204]
-// 0x0000003c: str w2, [sp, #208]
-// 0x00000040: str w3, [sp, #212]
-// 0x00000044: sub sp, sp, #0x20 (32)
-// 0x00000048: .cfi_def_cfa_offset: 224
-// 0x00000048: add sp, sp, #0x20 (32)
-// 0x0000004c: .cfi_def_cfa_offset: 192
-// 0x0000004c: mov tr, x21
-// 0x00000050: .cfi_remember_state
-// 0x00000050: ldp x19, x20, [sp, #96]
-// 0x00000054: .cfi_restore: r19
-// 0x00000054: .cfi_restore: r20
-// 0x00000054: ldp x21, x22, [sp, #112]
-// 0x00000058: .cfi_restore: r21
-// 0x00000058: .cfi_restore: r22
-// 0x00000058: ldp x23, x24, [sp, #128]
-// 0x0000005c: .cfi_restore: r23
-// 0x0000005c: .cfi_restore: r24
-// 0x0000005c: ldp x25, x26, [sp, #144]
-// 0x00000060: .cfi_restore: r25
-// 0x00000060: .cfi_restore: r26
-// 0x00000060: ldp x27, x28, [sp, #160]
-// 0x00000064: .cfi_restore: r27
-// 0x00000064: .cfi_restore: r28
-// 0x00000064: ldp x29, lr, [sp, #176]
-// 0x00000068: .cfi_restore: r29
-// 0x00000068: .cfi_restore: r30
-// 0x00000068: ldp d8, d9, [sp, #32]
-// 0x0000006c: .cfi_restore_extended: r72
-// 0x0000006c: .cfi_restore_extended: r73
-// 0x0000006c: ldp d10, d11, [sp, #48]
-// 0x00000070: .cfi_restore_extended: r74
-// 0x00000070: .cfi_restore_extended: r75
-// 0x00000070: ldp d12, d13, [sp, #64]
-// 0x00000074: .cfi_restore_extended: r76
-// 0x00000074: .cfi_restore_extended: r77
-// 0x00000074: ldp d14, d15, [sp, #80]
-// 0x00000078: .cfi_restore_extended: r78
-// 0x00000078: .cfi_restore_extended: r79
-// 0x00000078: add sp, sp, #0xc0 (192)
-// 0x0000007c: .cfi_def_cfa_offset: 0
-// 0x0000007c: ret
-// 0x00000080: .cfi_restore_state
-// 0x00000080: .cfi_def_cfa_offset: 192
+// 0x0000002c: str x0, [sp]
+// 0x00000030: str w1, [sp, #200]
+// 0x00000034: str s0, [sp, #204]
+// 0x00000038: str w2, [sp, #208]
+// 0x0000003c: str w3, [sp, #212]
+// 0x00000040: sub sp, sp, #0x20 (32)
+// 0x00000044: .cfi_def_cfa_offset: 224
+// 0x00000044: add sp, sp, #0x20 (32)
+// 0x00000048: .cfi_def_cfa_offset: 192
+// 0x00000048: .cfi_remember_state
+// 0x00000048: ldp tr, x20, [sp, #96]
+// 0x0000004c: .cfi_restore: r19
+// 0x0000004c: .cfi_restore: r20
+// 0x0000004c: ldp x21, x22, [sp, #112]
+// 0x00000050: .cfi_restore: r21
+// 0x00000050: .cfi_restore: r22
+// 0x00000050: ldp x23, x24, [sp, #128]
+// 0x00000054: .cfi_restore: r23
+// 0x00000054: .cfi_restore: r24
+// 0x00000054: ldp x25, x26, [sp, #144]
+// 0x00000058: .cfi_restore: r25
+// 0x00000058: .cfi_restore: r26
+// 0x00000058: ldp x27, x28, [sp, #160]
+// 0x0000005c: .cfi_restore: r27
+// 0x0000005c: .cfi_restore: r28
+// 0x0000005c: ldp x29, lr, [sp, #176]
+// 0x00000060: .cfi_restore: r29
+// 0x00000060: .cfi_restore: r30
+// 0x00000060: ldp d8, d9, [sp, #32]
+// 0x00000064: .cfi_restore_extended: r72
+// 0x00000064: .cfi_restore_extended: r73
+// 0x00000064: ldp d10, d11, [sp, #48]
+// 0x00000068: .cfi_restore_extended: r74
+// 0x00000068: .cfi_restore_extended: r75
+// 0x00000068: ldp d12, d13, [sp, #64]
+// 0x0000006c: .cfi_restore_extended: r76
+// 0x0000006c: .cfi_restore_extended: r77
+// 0x0000006c: ldp d14, d15, [sp, #80]
+// 0x00000070: .cfi_restore_extended: r78
+// 0x00000070: .cfi_restore_extended: r79
+// 0x00000070: add sp, sp, #0xc0 (192)
+// 0x00000074: .cfi_def_cfa_offset: 0
+// 0x00000074: ret
+// 0x00000078: .cfi_restore_state
+// 0x00000078: .cfi_def_cfa_offset: 192
static constexpr uint8_t expected_asm_kX86[] = {
0x57, 0x56, 0x55, 0x83, 0xC4, 0xE4, 0x50, 0x89, 0x4C, 0x24, 0x34, 0xF3,
diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.cc b/compiler/jni/quick/arm64/calling_convention_arm64.cc
index b094747..9aef10e 100644
--- a/compiler/jni/quick/arm64/calling_convention_arm64.cc
+++ b/compiler/jni/quick/arm64/calling_convention_arm64.cc
@@ -184,7 +184,7 @@
// Jni function is the native function which the java code wants to call.
// Jni method is the method that compiled by jni compiler.
// Call chain: managed code(java) --> jni method --> jni function.
- // Thread register(X18, scratched by aapcs64) is not saved on stack, it is saved in ETR(X21).
+ // Thread register(X19) is saved on stack.
return 1 << X19 | 1 << X20 | 1 << X21 | 1 << X22 | 1 << X23 | 1 << X24 |
1 << X25 | 1 << X26 | 1 << X27 | 1 << X28 | 1 << X29 | 1 << LR;
}
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 0347c5e..4d7d86c 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -94,7 +94,7 @@
// Assembler that holds generated instructions
std::unique_ptr<Assembler> jni_asm(Assembler::Create(instruction_set));
- jni_asm->cfi().SetEnabled(driver->GetCompilerOptions().GetIncludeCFI());
+ jni_asm->cfi().SetEnabled(driver->GetCompilerOptions().GetGenerateDebugInfo());
// Offsets into data structures
// TODO: if cross compiling these offsets are for the host not the target
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 633bf64..a98a304 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -351,9 +351,8 @@
public:
InitCodeMethodVisitor(OatWriter* writer, size_t offset)
: OatDexMethodVisitor(writer, offset),
- text_absolute_patch_locations_(writer->GetAbsolutePatchLocationsFor(".text")),
debuggable_(writer->GetCompilerDriver()->GetCompilerOptions().GetDebuggable()) {
- text_absolute_patch_locations_->reserve(
+ writer_->absolute_patch_locations_.reserve(
writer_->compiler_driver_->GetNonRelativeLinkerPatchCount());
}
@@ -444,14 +443,13 @@
uintptr_t base_loc = offset_ - code_size - writer_->oat_header_->GetExecutableOffset();
for (const LinkerPatch& patch : compiled_method->GetPatches()) {
if (!patch.IsPcRelative()) {
- text_absolute_patch_locations_->push_back(base_loc + patch.LiteralOffset());
+ writer_->absolute_patch_locations_.push_back(base_loc + patch.LiteralOffset());
}
}
}
}
- if (writer_->compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols() ||
- writer_->compiler_driver_->GetCompilerOptions().GetIncludeCFI()) {
+ if (writer_->compiler_driver_->GetCompilerOptions().GetGenerateDebugInfo()) {
// Record debug information for this function if we are doing that.
const uint32_t quick_code_start = quick_code_offset -
writer_->oat_header_->GetExecutableOffset() - thumb_offset;
@@ -547,9 +545,6 @@
// so we can simply compare the pointers to find out if things are duplicated.
SafeMap<const CompiledMethod*, uint32_t, CodeOffsetsKeyComparator> dedupe_map_;
- // Patch locations for the .text section.
- std::vector<uintptr_t>* const text_absolute_patch_locations_;
-
// Cache of compiler's --debuggable option.
const bool debuggable_;
};
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 6f1b4ec..82b9377 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -19,7 +19,6 @@
#include <stdint.h>
#include <cstddef>
-#include <map>
#include <memory>
#include "linker/relative_patcher.h" // For linker::RelativePatcherTargetProvider.
@@ -82,8 +81,6 @@
//
class OatWriter {
public:
- typedef std::map<std::string, std::unique_ptr<std::vector<uintptr_t>>> PatchLocationsMap;
-
OatWriter(const std::vector<const DexFile*>& dex_files,
uint32_t image_file_location_oat_checksum,
uintptr_t image_file_location_oat_begin,
@@ -105,19 +102,10 @@
return bss_size_;
}
- const PatchLocationsMap& GetAbsolutePatchLocations() const {
+ const std::vector<uintptr_t>& GetAbsolutePatchLocations() const {
return absolute_patch_locations_;
}
- std::vector<uintptr_t>* GetAbsolutePatchLocationsFor(const char* section_name) {
- auto it = absolute_patch_locations_.emplace(
- std::string(section_name), std::unique_ptr<std::vector<uintptr_t>>());
- if (it.second) { // Inserted new item.
- it.first->second.reset(new std::vector<uintptr_t>());
- }
- return it.first->second.get();
- }
-
bool WriteRodata(OutputStream* out);
bool WriteCode(OutputStream* out);
@@ -339,9 +327,8 @@
std::unique_ptr<linker::RelativePatcher> relative_patcher_;
- // The locations of absolute patches relative to the start of section.
- // The map's key is the ELF's section name (including the dot).
- PatchLocationsMap absolute_patch_locations_;
+ // The locations of absolute patches relative to the start of the executable section.
+ std::vector<uintptr_t> absolute_patch_locations_;
// Map method reference to assigned offset.
// Wrap the map in a class implementing linker::RelativePatcherTargetProvider.
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index b2b5496..9d5e3fd 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -126,11 +126,14 @@
return instruction_ == bound.instruction_ && constant_ == bound.constant_;
}
- static HInstruction* FromArrayLengthToNewArrayIfPossible(HInstruction* instruction) {
- // Null check on the NewArray should have been eliminated by instruction
- // simplifier already.
- if (instruction->IsArrayLength() && instruction->InputAt(0)->IsNewArray()) {
- return instruction->InputAt(0)->AsNewArray();
+ static HInstruction* FromArrayLengthToArray(HInstruction* instruction) {
+ DCHECK(instruction->IsArrayLength() || instruction->IsNewArray());
+ if (instruction->IsArrayLength()) {
+ HInstruction* input = instruction->InputAt(0);
+ if (input->IsNullCheck()) {
+ input = input->AsNullCheck()->InputAt(0);
+ }
+ return input;
}
return instruction;
}
@@ -146,8 +149,9 @@
// Some bounds are created with HNewArray* as the instruction instead
// of HArrayLength*. They are treated the same.
- instruction1 = FromArrayLengthToNewArrayIfPossible(instruction1);
- instruction2 = FromArrayLengthToNewArrayIfPossible(instruction2);
+ // HArrayLength with the same array input are considered equal also.
+ instruction1 = FromArrayLengthToArray(instruction1);
+ instruction2 = FromArrayLengthToArray(instruction2);
return instruction1 == instruction2;
}
@@ -271,7 +275,7 @@
// Loop header of loop_info. Exiting loop is normal.
return false;
}
- const GrowableArray<HBasicBlock*> successors = block->GetSuccessors();
+ const GrowableArray<HBasicBlock*>& successors = block->GetSuccessors();
for (size_t i = 0; i < successors.Size(); i++) {
if (!loop_info->Contains(*successors.Get(i))) {
// One of the successors exits the loop.
@@ -293,8 +297,14 @@
void Run() {
HLoopInformation* loop_info = induction_variable_->GetBlock()->GetLoopInformation();
- for (HBlocksInLoopIterator it_loop(*loop_info); !it_loop.Done(); it_loop.Advance()) {
- HBasicBlock* block = it_loop.Current();
+ HBlocksInLoopReversePostOrderIterator it_loop(*loop_info);
+ HBasicBlock* block = it_loop.Current();
+ DCHECK(block == induction_variable_->GetBlock());
+ // Skip loop header. Since narrowed value range of a MonotonicValueRange only
+ // applies to the loop body (after the test at the end of the loop header).
+ it_loop.Advance();
+ for (; !it_loop.Done(); it_loop.Advance()) {
+ block = it_loop.Current();
DCHECK(block->IsInLoop());
if (!DominatesAllBackEdges(block, loop_info)) {
// In order not to trigger deoptimization unnecessarily, make sure
@@ -308,30 +318,35 @@
// that the loop will loop through the full monotonic value range from
// initial_ to end_. So adding deoptimization might be too aggressive and can
// trigger deoptimization unnecessarily even if the loop won't actually throw
- // AIOOBE. Otherwise, the loop induction variable is going to cover the full
- // monotonic value range from initial_ to end_, and deoptimizations are added
- // iff the loop will throw AIOOBE.
+ // AIOOBE.
found_array_length_ = nullptr;
return;
}
for (HInstruction* instruction = block->GetFirstInstruction();
instruction != nullptr;
instruction = instruction->GetNext()) {
- if (!instruction->IsArrayGet() && !instruction->IsArraySet()) {
- continue;
- }
- HInstruction* index = instruction->InputAt(1);
- if (!index->IsBoundsCheck()) {
+ if (!instruction->IsBoundsCheck()) {
continue;
}
- HArrayLength* array_length = index->InputAt(1)->AsArrayLength();
- if (array_length == nullptr) {
- DCHECK(index->InputAt(1)->IsIntConstant());
+ HInstruction* length_value = instruction->InputAt(1);
+ if (length_value->IsIntConstant()) {
// TODO: may optimize for constant case.
continue;
}
+ DCHECK(!length_value->IsPhi());
+ if (length_value->IsPhi()) {
+ // Outer loop shouldn't collect bounds checks inside inner
+ // loop because the inner loop body doen't dominate
+ // outer loop's back edges. However just to be on the safe side,
+ // if there are any such cases, we just skip over them.
+ continue;
+ }
+
+ DCHECK(length_value->IsArrayLength());
+ HArrayLength* array_length = length_value->AsArrayLength();
+
HInstruction* array = array_length->InputAt(0);
if (array->IsNullCheck()) {
array = array->AsNullCheck()->InputAt(0);
@@ -347,7 +362,7 @@
continue;
}
- index = index->AsBoundsCheck()->InputAt(0);
+ HInstruction* index = instruction->AsBoundsCheck()->InputAt(0);
HInstruction* left = index;
int32_t right = 0;
if (left == induction_variable_ ||
@@ -375,7 +390,7 @@
// The instruction that corresponds to a MonotonicValueRange.
HInstruction* induction_variable_;
- // The array length of the array that's accessed inside the loop.
+ // The array length of the array that's accessed inside the loop body.
HArrayLength* found_array_length_;
// The lowest and highest constant offsets relative to induction variable
@@ -411,6 +426,8 @@
ValueBound GetLower() const { return lower_; }
ValueBound GetUpper() const { return upper_; }
+ bool IsConstantValueRange() { return lower_.IsConstant() && upper_.IsConstant(); }
+
// If it's certain that this value range fits in other_range.
virtual bool FitsIn(ValueRange* other_range) const {
if (other_range == nullptr) {
@@ -495,13 +512,30 @@
ValueBound GetBound() const { return bound_; }
void SetEnd(HInstruction* end) { end_ = end; }
void SetInclusive(bool inclusive) { inclusive_ = inclusive; }
- HBasicBlock* GetLoopHead() const {
+ HBasicBlock* GetLoopHeader() const {
DCHECK(induction_variable_->GetBlock()->IsLoopHeader());
return induction_variable_->GetBlock();
}
MonotonicValueRange* AsMonotonicValueRange() OVERRIDE { return this; }
+ HBasicBlock* GetLoopHeaderSuccesorInLoop() {
+ HBasicBlock* header = GetLoopHeader();
+ HInstruction* instruction = header->GetLastInstruction();
+ DCHECK(instruction->IsIf());
+ HIf* h_if = instruction->AsIf();
+ HLoopInformation* loop_info = header->GetLoopInformation();
+ bool true_successor_in_loop = loop_info->Contains(*h_if->IfTrueSuccessor());
+ bool false_successor_in_loop = loop_info->Contains(*h_if->IfFalseSuccessor());
+
+ // Just in case it's some strange loop structure.
+ if (true_successor_in_loop && false_successor_in_loop) {
+ return nullptr;
+ }
+ DCHECK(true_successor_in_loop || false_successor_in_loop);
+ return false_successor_in_loop ? h_if->IfFalseSuccessor() : h_if->IfTrueSuccessor();
+ }
+
// If it's certain that this value range fits in other_range.
bool FitsIn(ValueRange* other_range) const OVERRIDE {
if (other_range == nullptr) {
@@ -593,12 +627,114 @@
}
}
+ // Try to add HDeoptimize's in the loop pre-header first to narrow this range.
+ // For example, this loop:
+ //
+ // for (int i = start; i < end; i++) {
+ // array[i - 1] = array[i] + array[i + 1];
+ // }
+ //
+ // will be transformed to:
+ //
+ // int array_length_in_loop_body_if_needed;
+ // if (start >= end) {
+ // array_length_in_loop_body_if_needed = 0;
+ // } else {
+ // if (start < 1) deoptimize();
+ // if (array == null) deoptimize();
+ // array_length = array.length;
+ // if (end > array_length - 1) deoptimize;
+ // array_length_in_loop_body_if_needed = array_length;
+ // }
+ // for (int i = start; i < end; i++) {
+ // // No more null check and bounds check.
+ // // array.length value is replaced with array_length_in_loop_body_if_needed
+ // // in the loop body.
+ // array[i - 1] = array[i] + array[i + 1];
+ // }
+ //
+ // We basically first go through the loop body and find those array accesses whose
+ // index is at a constant offset from the induction variable ('i' in the above example),
+ // and update offset_low and offset_high along the way. We then add the following
+ // deoptimizations in the loop pre-header (suppose end is not inclusive).
+ // if (start < -offset_low) deoptimize();
+ // if (end >= array.length - offset_high) deoptimize();
+ // It might be necessary to first hoist array.length (and the null check on it) out of
+ // the loop with another deoptimization.
+ //
+ // In order not to trigger deoptimization unnecessarily, we want to make a strong
+ // guarantee that no deoptimization is triggered if the loop body itself doesn't
+ // throw AIOOBE. (It's the same as saying if deoptimization is triggered, the loop
+ // body must throw AIOOBE).
+ // This is achieved by the following:
+ // 1) We only process loops that iterate through the full monotonic range from
+ // initial_ to end_. We do the following checks to make sure that's the case:
+ // a) The loop doesn't have early exit (via break, return, etc.)
+ // b) The increment_ is 1/-1. An increment of 2, for example, may skip end_.
+ // 2) We only collect array accesses of blocks in the loop body that dominate
+ // all loop back edges, these array accesses are guaranteed to happen
+ // at each loop iteration.
+ // With 1) and 2), if the loop body doesn't throw AIOOBE, collected array accesses
+ // when the induction variable is at initial_ and end_ must be in a legal range.
+ // Since the added deoptimizations are basically checking the induction variable
+ // at initial_ and end_ values, no deoptimization will be triggered either.
+ //
+ // A special case is the loop body isn't entered at all. In that case, we may still
+ // add deoptimization due to the analysis described above. In order not to trigger
+ // deoptimization, we do a test between initial_ and end_ first and skip over
+ // the added deoptimization.
+ ValueRange* NarrowWithDeoptimization() {
+ if (increment_ != 1 && increment_ != -1) {
+ // In order not to trigger deoptimization unnecessarily, we want to
+ // make sure the loop iterates through the full range from initial_ to
+ // end_ so that boundaries are covered by the loop. An increment of 2,
+ // for example, may skip end_.
+ return this;
+ }
+
+ if (end_ == nullptr) {
+ // No full info to add deoptimization.
+ return this;
+ }
+
+ HBasicBlock* header = induction_variable_->GetBlock();
+ DCHECK(header->IsLoopHeader());
+ HBasicBlock* pre_header = header->GetLoopInformation()->GetPreHeader();
+ if (!initial_->GetBlock()->Dominates(pre_header) ||
+ !end_->GetBlock()->Dominates(pre_header)) {
+ // Can't add a check in loop pre-header if the value isn't available there.
+ return this;
+ }
+
+ ArrayAccessInsideLoopFinder finder(induction_variable_);
+
+ if (!finder.HasFoundArrayLength()) {
+ // No array access was found inside the loop that can benefit
+ // from deoptimization.
+ return this;
+ }
+
+ if (!AddDeoptimization(finder)) {
+ return this;
+ }
+
+ // After added deoptimizations, induction variable fits in
+ // [-offset_low, array.length-1-offset_high], adjusted with collected offsets.
+ ValueBound lower = ValueBound(0, -finder.GetOffsetLow());
+ ValueBound upper = ValueBound(finder.GetFoundArrayLength(), -1 - finder.GetOffsetHigh());
+ // We've narrowed the range after added deoptimizations.
+ return new (GetAllocator()) ValueRange(GetAllocator(), lower, upper);
+ }
+
// Returns true if adding a (constant >= value) check for deoptimization
// is allowed and will benefit compiled code.
- bool CanAddDeoptimizationConstant(HInstruction* value,
- int32_t constant,
- bool* is_proven) {
+ bool CanAddDeoptimizationConstant(HInstruction* value, int32_t constant, bool* is_proven) {
*is_proven = false;
+ HBasicBlock* header = induction_variable_->GetBlock();
+ DCHECK(header->IsLoopHeader());
+ HBasicBlock* pre_header = header->GetLoopInformation()->GetPreHeader();
+ DCHECK(value->GetBlock()->Dominates(pre_header));
+
// See if we can prove the relationship first.
if (value->IsIntConstant()) {
if (value->AsIntConstant()->GetValue() >= constant) {
@@ -615,22 +751,118 @@
return true;
}
+ // Try to filter out cases that the loop entry test will never be true.
+ bool LoopEntryTestUseful() {
+ if (initial_->IsIntConstant() && end_->IsIntConstant()) {
+ int32_t initial_val = initial_->AsIntConstant()->GetValue();
+ int32_t end_val = end_->AsIntConstant()->GetValue();
+ if (increment_ == 1) {
+ if (inclusive_) {
+ return initial_val > end_val;
+ } else {
+ return initial_val >= end_val;
+ }
+ } else {
+ DCHECK_EQ(increment_, -1);
+ if (inclusive_) {
+ return initial_val < end_val;
+ } else {
+ return initial_val <= end_val;
+ }
+ }
+ }
+ return true;
+ }
+
+ // Returns the block for adding deoptimization.
+ HBasicBlock* TransformLoopForDeoptimizationIfNeeded() {
+ HBasicBlock* header = induction_variable_->GetBlock();
+ DCHECK(header->IsLoopHeader());
+ HBasicBlock* pre_header = header->GetLoopInformation()->GetPreHeader();
+ // Deoptimization is only added when both initial_ and end_ are defined
+ // before the loop.
+ DCHECK(initial_->GetBlock()->Dominates(pre_header));
+ DCHECK(end_->GetBlock()->Dominates(pre_header));
+
+ // If it can be proven the loop body is definitely entered (unless exception
+ // is thrown in the loop header for which triggering deoptimization is fine),
+ // there is no need for tranforming the loop. In that case, deoptimization
+ // will just be added in the loop pre-header.
+ if (!LoopEntryTestUseful()) {
+ return pre_header;
+ }
+
+ HGraph* graph = header->GetGraph();
+ graph->TransformLoopHeaderForBCE(header);
+ HBasicBlock* new_pre_header = header->GetDominator();
+ DCHECK(new_pre_header == header->GetLoopInformation()->GetPreHeader());
+ HBasicBlock* if_block = new_pre_header->GetDominator();
+ HBasicBlock* dummy_block = if_block->GetSuccessors().Get(0); // True successor.
+ HBasicBlock* deopt_block = if_block->GetSuccessors().Get(1); // False successor.
+
+ dummy_block->AddInstruction(new (graph->GetArena()) HGoto());
+ deopt_block->AddInstruction(new (graph->GetArena()) HGoto());
+ new_pre_header->AddInstruction(new (graph->GetArena()) HGoto());
+ return deopt_block;
+ }
+
+ // Adds a test between initial_ and end_ to see if the loop body is entered.
+ // If the loop body isn't entered at all, it jumps to the loop pre-header (after
+ // transformation) to avoid any deoptimization.
+ void AddLoopBodyEntryTest() {
+ HBasicBlock* header = induction_variable_->GetBlock();
+ DCHECK(header->IsLoopHeader());
+ HBasicBlock* pre_header = header->GetLoopInformation()->GetPreHeader();
+ HBasicBlock* if_block = pre_header->GetDominator();
+ HGraph* graph = header->GetGraph();
+
+ HCondition* cond;
+ if (increment_ == 1) {
+ if (inclusive_) {
+ cond = new (graph->GetArena()) HGreaterThan(initial_, end_);
+ } else {
+ cond = new (graph->GetArena()) HGreaterThanOrEqual(initial_, end_);
+ }
+ } else {
+ DCHECK_EQ(increment_, -1);
+ if (inclusive_) {
+ cond = new (graph->GetArena()) HLessThan(initial_, end_);
+ } else {
+ cond = new (graph->GetArena()) HLessThanOrEqual(initial_, end_);
+ }
+ }
+ HIf* h_if = new (graph->GetArena()) HIf(cond);
+ if_block->AddInstruction(cond);
+ if_block->AddInstruction(h_if);
+ }
+
// Adds a check that (value >= constant), and HDeoptimize otherwise.
void AddDeoptimizationConstant(HInstruction* value,
- int32_t constant) {
- HBasicBlock* block = induction_variable_->GetBlock();
- DCHECK(block->IsLoopHeader());
- HGraph* graph = block->GetGraph();
- HBasicBlock* pre_header = block->GetLoopInformation()->GetPreHeader();
- HSuspendCheck* suspend_check = block->GetLoopInformation()->GetSuspendCheck();
+ int32_t constant,
+ HBasicBlock* deopt_block,
+ bool loop_entry_test_block_added) {
+ HBasicBlock* header = induction_variable_->GetBlock();
+ DCHECK(header->IsLoopHeader());
+ HBasicBlock* pre_header = header->GetDominator();
+ if (loop_entry_test_block_added) {
+ DCHECK(deopt_block->GetSuccessors().Get(0) == pre_header);
+ } else {
+ DCHECK(deopt_block == pre_header);
+ }
+ HGraph* graph = header->GetGraph();
+ HSuspendCheck* suspend_check = header->GetLoopInformation()->GetSuspendCheck();
+ if (loop_entry_test_block_added) {
+ DCHECK_EQ(deopt_block, header->GetDominator()->GetDominator()->GetSuccessors().Get(1));
+ }
+
HIntConstant* const_instr = graph->GetIntConstant(constant);
HCondition* cond = new (graph->GetArena()) HLessThan(value, const_instr);
HDeoptimize* deoptimize = new (graph->GetArena())
HDeoptimize(cond, suspend_check->GetDexPc());
- pre_header->InsertInstructionBefore(cond, pre_header->GetLastInstruction());
- pre_header->InsertInstructionBefore(deoptimize, pre_header->GetLastInstruction());
+ deopt_block->InsertInstructionBefore(cond, deopt_block->GetLastInstruction());
+ deopt_block->InsertInstructionBefore(deoptimize, deopt_block->GetLastInstruction());
deoptimize->CopyEnvironmentFromWithLoopPhiAdjustment(
- suspend_check->GetEnvironment(), block);
+ suspend_check->GetEnvironment(), header);
}
// Returns true if adding a (value <= array_length + offset) check for deoptimization
@@ -640,6 +872,26 @@
int32_t offset,
bool* is_proven) {
*is_proven = false;
+ HBasicBlock* header = induction_variable_->GetBlock();
+ DCHECK(header->IsLoopHeader());
+ HBasicBlock* pre_header = header->GetLoopInformation()->GetPreHeader();
+ DCHECK(value->GetBlock()->Dominates(pre_header));
+
+ if (array_length->GetBlock() == header) {
+ // array_length_in_loop_body_if_needed only has correct value when the loop
+ // body is entered. We bail out in this case. Usually array_length defined
+ // in the loop header is already hoisted by licm.
+ return false;
+ } else {
+ // array_length is defined either before the loop header already, or in
+ // the loop body since it's used in the loop body. If it's defined in the loop body,
+ // a phi array_length_in_loop_body_if_needed is used to replace it. In that case,
+ // all the uses of array_length must be dominated by its definition in the loop
+ // body. array_length_in_loop_body_if_needed is guaranteed to be the same as
+ // array_length once the loop body is entered so all the uses of the phi will
+ // use the correct value.
+ }
+
if (offset > 0) {
// There might be overflow issue.
// TODO: handle this, possibly with some distance relationship between
@@ -667,56 +919,99 @@
// Adds a check that (value <= array_length + offset), and HDeoptimize otherwise.
void AddDeoptimizationArrayLength(HInstruction* value,
HArrayLength* array_length,
- int32_t offset) {
- HBasicBlock* block = induction_variable_->GetBlock();
- DCHECK(block->IsLoopHeader());
- HGraph* graph = block->GetGraph();
- HBasicBlock* pre_header = block->GetLoopInformation()->GetPreHeader();
- HSuspendCheck* suspend_check = block->GetLoopInformation()->GetSuspendCheck();
+ int32_t offset,
+ HBasicBlock* deopt_block,
+ bool loop_entry_test_block_added) {
+ HBasicBlock* header = induction_variable_->GetBlock();
+ DCHECK(header->IsLoopHeader());
+ HBasicBlock* pre_header = header->GetDominator();
+ if (loop_entry_test_block_added) {
+ DCHECK(deopt_block->GetSuccessors().Get(0) == pre_header);
+ } else {
+ DCHECK(deopt_block == pre_header);
+ }
+ HGraph* graph = header->GetGraph();
+ HSuspendCheck* suspend_check = header->GetLoopInformation()->GetSuspendCheck();
// We may need to hoist null-check and array_length out of loop first.
- if (!array_length->GetBlock()->Dominates(pre_header)) {
+ if (!array_length->GetBlock()->Dominates(deopt_block)) {
+ // array_length must be defined in the loop body.
+ DCHECK(header->GetLoopInformation()->Contains(*array_length->GetBlock()));
+ DCHECK(array_length->GetBlock() != header);
+
HInstruction* array = array_length->InputAt(0);
HNullCheck* null_check = array->AsNullCheck();
if (null_check != nullptr) {
array = null_check->InputAt(0);
}
- // We've already made sure array is defined before the loop when collecting
+ // We've already made sure the array is defined before the loop when collecting
// array accesses for the loop.
- DCHECK(array->GetBlock()->Dominates(pre_header));
- if (null_check != nullptr && !null_check->GetBlock()->Dominates(pre_header)) {
+ DCHECK(array->GetBlock()->Dominates(deopt_block));
+ if (null_check != nullptr && !null_check->GetBlock()->Dominates(deopt_block)) {
// Hoist null check out of loop with a deoptimization.
HNullConstant* null_constant = graph->GetNullConstant();
HCondition* null_check_cond = new (graph->GetArena()) HEqual(array, null_constant);
// TODO: for one dex_pc, share the same deoptimization slow path.
HDeoptimize* null_check_deoptimize = new (graph->GetArena())
HDeoptimize(null_check_cond, suspend_check->GetDexPc());
- pre_header->InsertInstructionBefore(null_check_cond, pre_header->GetLastInstruction());
- pre_header->InsertInstructionBefore(
- null_check_deoptimize, pre_header->GetLastInstruction());
+ deopt_block->InsertInstructionBefore(
+ null_check_cond, deopt_block->GetLastInstruction());
+ deopt_block->InsertInstructionBefore(
+ null_check_deoptimize, deopt_block->GetLastInstruction());
// Eliminate null check in the loop.
null_check->ReplaceWith(array);
null_check->GetBlock()->RemoveInstruction(null_check);
null_check_deoptimize->CopyEnvironmentFromWithLoopPhiAdjustment(
- suspend_check->GetEnvironment(), block);
+ suspend_check->GetEnvironment(), header);
}
- // Hoist array_length out of loop.
- array_length->MoveBefore(pre_header->GetLastInstruction());
+
+ HArrayLength* new_array_length = new (graph->GetArena()) HArrayLength(array);
+ deopt_block->InsertInstructionBefore(new_array_length, deopt_block->GetLastInstruction());
+
+ if (loop_entry_test_block_added) {
+ // Replace array_length defined inside the loop body with a phi
+ // array_length_in_loop_body_if_needed. This is a synthetic phi so there is
+ // no vreg number for it.
+ HPhi* phi = new (graph->GetArena()) HPhi(
+ graph->GetArena(), kNoRegNumber, 2, Primitive::kPrimInt);
+ // Set to 0 if the loop body isn't entered.
+ phi->SetRawInputAt(0, graph->GetIntConstant(0));
+ // Set to array.length if the loop body is entered.
+ phi->SetRawInputAt(1, new_array_length);
+ pre_header->AddPhi(phi);
+ array_length->ReplaceWith(phi);
+ // Make sure phi is only used after the loop body is entered.
+ if (kIsDebugBuild) {
+ for (HUseIterator<HInstruction*> it(phi->GetUses());
+ !it.Done();
+ it.Advance()) {
+ HInstruction* user = it.Current()->GetUser();
+ DCHECK(GetLoopHeaderSuccesorInLoop()->Dominates(user->GetBlock()));
+ }
+ }
+ } else {
+ array_length->ReplaceWith(new_array_length);
+ }
+
+ array_length->GetBlock()->RemoveInstruction(array_length);
+ // Use new_array_length for deopt.
+ array_length = new_array_length;
}
- HIntConstant* offset_instr = graph->GetIntConstant(offset);
- HAdd* add = new (graph->GetArena()) HAdd(Primitive::kPrimInt, array_length, offset_instr);
- HCondition* cond = new (graph->GetArena()) HGreaterThan(value, add);
- HDeoptimize* deoptimize = new (graph->GetArena())
- HDeoptimize(cond, suspend_check->GetDexPc());
- pre_header->InsertInstructionBefore(add, pre_header->GetLastInstruction());
- pre_header->InsertInstructionBefore(cond, pre_header->GetLastInstruction());
- pre_header->InsertInstructionBefore(deoptimize, pre_header->GetLastInstruction());
- deoptimize->CopyEnvironmentFromWithLoopPhiAdjustment(
- suspend_check->GetEnvironment(), block);
+ HInstruction* added = array_length;
+ if (offset != 0) {
+ HIntConstant* offset_instr = graph->GetIntConstant(offset);
+ added = new (graph->GetArena()) HAdd(Primitive::kPrimInt, array_length, offset_instr);
+ deopt_block->InsertInstructionBefore(added, deopt_block->GetLastInstruction());
+ }
+ HCondition* cond = new (graph->GetArena()) HGreaterThan(value, added);
+ HDeoptimize* deopt = new (graph->GetArena()) HDeoptimize(cond, suspend_check->GetDexPc());
+ deopt_block->InsertInstructionBefore(cond, deopt_block->GetLastInstruction());
+ deopt_block->InsertInstructionBefore(deopt, deopt_block->GetLastInstruction());
+ deopt->CopyEnvironmentFromWithLoopPhiAdjustment(suspend_check->GetEnvironment(), header);
}
- // Add deoptimizations in loop pre-header with the collected array access
+ // Adds deoptimizations in loop pre-header with the collected array access
// data so that value ranges can be established in loop body.
// Returns true if deoptimizations are successfully added, or if it's proven
// it's not necessary.
@@ -733,70 +1028,60 @@
return false;
}
+ HBasicBlock* deopt_block;
+ bool loop_entry_test_block_added = false;
bool is_constant_proven, is_length_proven;
+
+ HInstruction* const_comparing_instruction;
+ int32_t const_compared_to;
+ HInstruction* array_length_comparing_instruction;
+ int32_t array_length_offset;
if (increment_ == 1) {
// Increasing from initial_ to end_.
- int32_t offset = inclusive_ ? -offset_high - 1 : -offset_high;
- if (CanAddDeoptimizationConstant(initial_, -offset_low, &is_constant_proven) &&
- CanAddDeoptimizationArrayLength(end_, array_length, offset, &is_length_proven)) {
- if (!is_constant_proven) {
- AddDeoptimizationConstant(initial_, -offset_low);
+ const_comparing_instruction = initial_;
+ const_compared_to = -offset_low;
+ array_length_comparing_instruction = end_;
+ array_length_offset = inclusive_ ? -offset_high - 1 : -offset_high;
+ } else {
+ const_comparing_instruction = end_;
+ const_compared_to = inclusive_ ? -offset_low : -offset_low - 1;
+ array_length_comparing_instruction = initial_;
+ array_length_offset = -offset_high - 1;
+ }
+
+ if (CanAddDeoptimizationConstant(const_comparing_instruction,
+ const_compared_to,
+ &is_constant_proven) &&
+ CanAddDeoptimizationArrayLength(array_length_comparing_instruction,
+ array_length,
+ array_length_offset,
+ &is_length_proven)) {
+ if (!is_constant_proven || !is_length_proven) {
+ deopt_block = TransformLoopForDeoptimizationIfNeeded();
+ loop_entry_test_block_added = (deopt_block != pre_header);
+ if (loop_entry_test_block_added) {
+ // Loop body may be entered.
+ AddLoopBodyEntryTest();
}
- if (!is_length_proven) {
- AddDeoptimizationArrayLength(end_, array_length, offset);
- }
- return true;
}
- } else if (increment_ == -1) {
- // Decreasing from initial_ to end_.
- int32_t constant = inclusive_ ? -offset_low : -offset_low - 1;
- if (CanAddDeoptimizationConstant(end_, constant, &is_constant_proven) &&
- CanAddDeoptimizationArrayLength(
- initial_, array_length, -offset_high - 1, &is_length_proven)) {
- if (!is_constant_proven) {
- AddDeoptimizationConstant(end_, constant);
- }
- if (!is_length_proven) {
- AddDeoptimizationArrayLength(initial_, array_length, -offset_high - 1);
- }
- return true;
+ if (!is_constant_proven) {
+ AddDeoptimizationConstant(const_comparing_instruction,
+ const_compared_to,
+ deopt_block,
+ loop_entry_test_block_added);
}
+ if (!is_length_proven) {
+ AddDeoptimizationArrayLength(array_length_comparing_instruction,
+ array_length,
+ array_length_offset,
+ deopt_block,
+ loop_entry_test_block_added);
+ }
+ return true;
}
return false;
}
- // Try to add HDeoptimize's in the loop pre-header first to narrow this range.
- ValueRange* NarrowWithDeoptimization() {
- if (increment_ != 1 && increment_ != -1) {
- // TODO: possibly handle overflow/underflow issues with deoptimization.
- return this;
- }
-
- if (end_ == nullptr) {
- // No full info to add deoptimization.
- return this;
- }
-
- ArrayAccessInsideLoopFinder finder(induction_variable_);
-
- if (!finder.HasFoundArrayLength()) {
- // No array access was found inside the loop that can benefit
- // from deoptimization.
- return this;
- }
-
- if (!AddDeoptimization(finder)) {
- return this;
- }
-
- // After added deoptimizations, induction variable fits in
- // [-offset_low, array.length-1-offset_high], adjusted with collected offsets.
- ValueBound lower = ValueBound(0, -finder.GetOffsetLow());
- ValueBound upper = ValueBound(finder.GetFoundArrayLength(), -1 - finder.GetOffsetHigh());
- // We've narrowed the range after added deoptimizations.
- return new (GetAllocator()) ValueRange(GetAllocator(), lower, upper);
- }
-
private:
HPhi* const induction_variable_; // Induction variable for this monotonic value range.
HInstruction* const initial_; // Initial value.
@@ -819,12 +1104,17 @@
// it's likely some AIOOBE will be thrown.
static constexpr int32_t kMaxConstantForAddingDeoptimize = INT_MAX - 1024 * 1024;
+ // Added blocks for loop body entry test.
+ bool IsAddedBlock(HBasicBlock* block) const {
+ return block->GetBlockId() >= initial_block_size_;
+ }
+
explicit BCEVisitor(HGraph* graph)
- : HGraphVisitor(graph),
- maps_(graph->GetBlocks().Size()),
- need_to_revisit_block_(false) {}
+ : HGraphVisitor(graph), maps_(graph->GetBlocks().Size()),
+ need_to_revisit_block_(false), initial_block_size_(graph->GetBlocks().Size()) {}
void VisitBasicBlock(HBasicBlock* block) OVERRIDE {
+ DCHECK(!IsAddedBlock(block));
first_constant_index_bounds_check_map_.clear();
HGraphVisitor::VisitBasicBlock(block);
if (need_to_revisit_block_) {
@@ -839,6 +1129,10 @@
private:
// Return the map of proven value ranges at the beginning of a basic block.
ArenaSafeMap<int, ValueRange*>* GetValueRangeMap(HBasicBlock* basic_block) {
+ if (IsAddedBlock(basic_block)) {
+ // Added blocks don't keep value ranges.
+ return nullptr;
+ }
int block_id = basic_block->GetBlockId();
if (maps_.at(block_id) == nullptr) {
std::unique_ptr<ArenaSafeMap<int, ValueRange*>> map(
@@ -853,8 +1147,12 @@
ValueRange* LookupValueRange(HInstruction* instruction, HBasicBlock* basic_block) {
while (basic_block != nullptr) {
ArenaSafeMap<int, ValueRange*>* map = GetValueRangeMap(basic_block);
- if (map->find(instruction->GetId()) != map->end()) {
- return map->Get(instruction->GetId());
+ if (map != nullptr) {
+ if (map->find(instruction->GetId()) != map->end()) {
+ return map->Get(instruction->GetId());
+ }
+ } else {
+ DCHECK(IsAddedBlock(basic_block));
}
basic_block = basic_block->GetDominator();
}
@@ -971,7 +1269,7 @@
if (left_range != nullptr) {
left_monotonic_range = left_range->AsMonotonicValueRange();
if (left_monotonic_range != nullptr) {
- HBasicBlock* loop_head = left_monotonic_range->GetLoopHead();
+ HBasicBlock* loop_head = left_monotonic_range->GetLoopHeader();
if (instruction->GetBlock() != loop_head) {
// For monotonic value range, don't handle `instruction`
// if it's not defined in the loop header.
@@ -1013,7 +1311,7 @@
// Update the info for monotonic value range.
if (left_monotonic_range->GetInductionVariable() == left &&
left_monotonic_range->GetIncrement() < 0 &&
- block == left_monotonic_range->GetLoopHead() &&
+ block == left_monotonic_range->GetLoopHeader() &&
instruction->IfFalseSuccessor()->GetLoopInformation() == block->GetLoopInformation()) {
left_monotonic_range->SetEnd(right);
left_monotonic_range->SetInclusive(cond == kCondLT);
@@ -1047,7 +1345,7 @@
// Update the info for monotonic value range.
if (left_monotonic_range->GetInductionVariable() == left &&
left_monotonic_range->GetIncrement() > 0 &&
- block == left_monotonic_range->GetLoopHead() &&
+ block == left_monotonic_range->GetLoopHeader() &&
instruction->IfFalseSuccessor()->GetLoopInformation() == block->GetLoopInformation()) {
left_monotonic_range->SetEnd(right);
left_monotonic_range->SetInclusive(cond == kCondGT);
@@ -1083,7 +1381,16 @@
HBasicBlock* block = bounds_check->GetBlock();
HInstruction* index = bounds_check->InputAt(0);
HInstruction* array_length = bounds_check->InputAt(1);
- DCHECK(array_length->IsIntConstant() || array_length->IsArrayLength());
+ DCHECK(array_length->IsIntConstant() ||
+ array_length->IsArrayLength() ||
+ array_length->IsPhi());
+
+ if (array_length->IsPhi()) {
+ // Input 1 of the phi contains the real array.length once the loop body is
+ // entered. That value will be used for bound analysis. The graph is still
+ // strickly in SSA form.
+ array_length = array_length->AsPhi()->InputAt(1)->AsArrayLength();
+ }
if (!index->IsIntConstant()) {
ValueRange* index_range = LookupValueRange(index, block);
@@ -1238,25 +1545,26 @@
}
if (left_range->IsMonotonicValueRange() &&
- block == left_range->AsMonotonicValueRange()->GetLoopHead()) {
+ block == left_range->AsMonotonicValueRange()->GetLoopHeader()) {
// The comparison is for an induction variable in the loop header.
DCHECK(left == left_range->AsMonotonicValueRange()->GetInductionVariable());
- HBasicBlock* loop_body_successor;
- if (LIKELY(block->GetLoopInformation()->
- Contains(*instruction->IfFalseSuccessor()))) {
- loop_body_successor = instruction->IfFalseSuccessor();
- } else {
- loop_body_successor = instruction->IfTrueSuccessor();
+ HBasicBlock* loop_body_successor =
+ left_range->AsMonotonicValueRange()->GetLoopHeaderSuccesorInLoop();
+ if (loop_body_successor == nullptr) {
+ // In case it's some strange loop structure.
+ return;
}
ValueRange* new_left_range = LookupValueRange(left, loop_body_successor);
- if (new_left_range == left_range) {
+ if ((new_left_range == left_range) ||
+ // Range narrowed with deoptimization is usually more useful than
+ // a constant range.
+ new_left_range->IsConstantValueRange()) {
// We are not successful in narrowing the monotonic value range to
// a regular value range. Try using deoptimization.
new_left_range = left_range->AsMonotonicValueRange()->
NarrowWithDeoptimization();
if (new_left_range != left_range) {
- GetValueRangeMap(instruction->IfFalseSuccessor())->
- Overwrite(left->GetId(), new_left_range);
+ GetValueRangeMap(loop_body_successor)->Overwrite(left->GetId(), new_left_range);
}
}
}
@@ -1511,6 +1819,9 @@
// eliminate those bounds checks.
bool need_to_revisit_block_;
+ // Initial number of blocks.
+ int32_t initial_block_size_;
+
DISALLOW_COPY_AND_ASSIGN(BCEVisitor);
};
@@ -1527,7 +1838,22 @@
// value can be narrowed further down in the dominator tree.
//
// TODO: only visit blocks that dominate some array accesses.
- visitor.VisitReversePostOrder();
+ HBasicBlock* last_visited_block = nullptr;
+ for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ HBasicBlock* current = it.Current();
+ if (current == last_visited_block) {
+ // We may insert blocks into the reverse post order list when processing
+ // a loop header. Don't process it again.
+ DCHECK(current->IsLoopHeader());
+ continue;
+ }
+ if (visitor.IsAddedBlock(current)) {
+ // Skip added blocks. Their effects are already taken care of.
+ continue;
+ }
+ visitor.VisitBasicBlock(current);
+ last_visited_block = current;
+ }
}
} // namespace art
diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc
index 163458f..4701bdd 100644
--- a/compiler/optimizing/bounds_check_elimination_test.cc
+++ b/compiler/optimizing/bounds_check_elimination_test.cc
@@ -440,22 +440,16 @@
HInstruction* bounds_check = nullptr;
HGraph* graph = BuildSSAGraph1(&allocator, &bounds_check, 0, 1);
graph->BuildDominatorTree();
+ graph->AnalyzeNaturalLoops();
+ RunSimplifierAndGvn(graph);
BoundsCheckElimination bounds_check_elimination(graph);
bounds_check_elimination.Run();
- ASSERT_FALSE(IsRemoved(bounds_check));
-
- // This time add gvn. Need gvn to eliminate the second
- // HArrayLength which uses the null check as its input.
- graph = BuildSSAGraph1(&allocator, &bounds_check, 0, 1);
- graph->BuildDominatorTree();
- RunSimplifierAndGvn(graph);
- BoundsCheckElimination bounds_check_elimination_after_gvn(graph);
- bounds_check_elimination_after_gvn.Run();
ASSERT_TRUE(IsRemoved(bounds_check));
// for (int i=1; i<array.length; i++) { array[i] = 10; // Can eliminate. }
graph = BuildSSAGraph1(&allocator, &bounds_check, 1, 1);
graph->BuildDominatorTree();
+ graph->AnalyzeNaturalLoops();
RunSimplifierAndGvn(graph);
BoundsCheckElimination bounds_check_elimination_with_initial_1(graph);
bounds_check_elimination_with_initial_1.Run();
@@ -464,6 +458,7 @@
// for (int i=-1; i<array.length; i++) { array[i] = 10; // Can't eliminate. }
graph = BuildSSAGraph1(&allocator, &bounds_check, -1, 1);
graph->BuildDominatorTree();
+ graph->AnalyzeNaturalLoops();
RunSimplifierAndGvn(graph);
BoundsCheckElimination bounds_check_elimination_with_initial_minus_1(graph);
bounds_check_elimination_with_initial_minus_1.Run();
@@ -472,6 +467,7 @@
// for (int i=0; i<=array.length; i++) { array[i] = 10; // Can't eliminate. }
graph = BuildSSAGraph1(&allocator, &bounds_check, 0, 1, kCondGT);
graph->BuildDominatorTree();
+ graph->AnalyzeNaturalLoops();
RunSimplifierAndGvn(graph);
BoundsCheckElimination bounds_check_elimination_with_greater_than(graph);
bounds_check_elimination_with_greater_than.Run();
@@ -481,6 +477,7 @@
// array[i] = 10; // Can't eliminate due to overflow concern. }
graph = BuildSSAGraph1(&allocator, &bounds_check, 0, 2);
graph->BuildDominatorTree();
+ graph->AnalyzeNaturalLoops();
RunSimplifierAndGvn(graph);
BoundsCheckElimination bounds_check_elimination_with_increment_2(graph);
bounds_check_elimination_with_increment_2.Run();
@@ -489,6 +486,7 @@
// for (int i=1; i<array.length; i += 2) { array[i] = 10; // Can eliminate. }
graph = BuildSSAGraph1(&allocator, &bounds_check, 1, 2);
graph->BuildDominatorTree();
+ graph->AnalyzeNaturalLoops();
RunSimplifierAndGvn(graph);
BoundsCheckElimination bounds_check_elimination_with_increment_2_from_1(graph);
bounds_check_elimination_with_increment_2_from_1.Run();
@@ -579,22 +577,16 @@
HInstruction* bounds_check = nullptr;
HGraph* graph = BuildSSAGraph2(&allocator, &bounds_check, 0);
graph->BuildDominatorTree();
+ graph->AnalyzeNaturalLoops();
+ RunSimplifierAndGvn(graph);
BoundsCheckElimination bounds_check_elimination(graph);
bounds_check_elimination.Run();
- ASSERT_FALSE(IsRemoved(bounds_check));
-
- // This time add gvn. Need gvn to eliminate the second
- // HArrayLength which uses the null check as its input.
- graph = BuildSSAGraph2(&allocator, &bounds_check, 0);
- graph->BuildDominatorTree();
- RunSimplifierAndGvn(graph);
- BoundsCheckElimination bounds_check_elimination_after_gvn(graph);
- bounds_check_elimination_after_gvn.Run();
ASSERT_TRUE(IsRemoved(bounds_check));
// for (int i=array.length; i>1; i--) { array[i-1] = 10; // Can eliminate. }
graph = BuildSSAGraph2(&allocator, &bounds_check, 1);
graph->BuildDominatorTree();
+ graph->AnalyzeNaturalLoops();
RunSimplifierAndGvn(graph);
BoundsCheckElimination bounds_check_elimination_with_initial_1(graph);
bounds_check_elimination_with_initial_1.Run();
@@ -603,6 +595,7 @@
// for (int i=array.length; i>-1; i--) { array[i-1] = 10; // Can't eliminate. }
graph = BuildSSAGraph2(&allocator, &bounds_check, -1);
graph->BuildDominatorTree();
+ graph->AnalyzeNaturalLoops();
RunSimplifierAndGvn(graph);
BoundsCheckElimination bounds_check_elimination_with_initial_minus_1(graph);
bounds_check_elimination_with_initial_minus_1.Run();
@@ -611,6 +604,7 @@
// for (int i=array.length; i>=0; i--) { array[i-1] = 10; // Can't eliminate. }
graph = BuildSSAGraph2(&allocator, &bounds_check, 0, -1, kCondLT);
graph->BuildDominatorTree();
+ graph->AnalyzeNaturalLoops();
RunSimplifierAndGvn(graph);
BoundsCheckElimination bounds_check_elimination_with_less_than(graph);
bounds_check_elimination_with_less_than.Run();
@@ -619,6 +613,7 @@
// for (int i=array.length; i>0; i-=2) { array[i-1] = 10; // Can eliminate. }
graph = BuildSSAGraph2(&allocator, &bounds_check, 0, -2);
graph->BuildDominatorTree();
+ graph->AnalyzeNaturalLoops();
RunSimplifierAndGvn(graph);
BoundsCheckElimination bounds_check_elimination_increment_minus_2(graph);
bounds_check_elimination_increment_minus_2.Run();
@@ -646,8 +641,13 @@
HBasicBlock* block = new (allocator) HBasicBlock(graph);
graph->AddBlock(block);
entry->AddSuccessor(block);
- HInstruction* new_array = new (allocator)
- HNewArray(constant_10, 0, Primitive::kPrimInt, kQuickAllocArray);
+ HInstruction* new_array = new (allocator) HNewArray(
+ constant_10,
+ graph->GetCurrentMethod(),
+ 0,
+ Primitive::kPrimInt,
+ graph->GetDexFile(),
+ kQuickAllocArray);
block->AddInstruction(new_array);
block->AddInstruction(new (allocator) HGoto());
@@ -705,15 +705,17 @@
HInstruction* bounds_check = nullptr;
HGraph* graph = BuildSSAGraph3(&allocator, &bounds_check, 0, 1, kCondGE);
graph->BuildDominatorTree();
+ graph->AnalyzeNaturalLoops();
RunSimplifierAndGvn(graph);
- BoundsCheckElimination bounds_check_elimination_after_gvn(graph);
- bounds_check_elimination_after_gvn.Run();
+ BoundsCheckElimination bounds_check_elimination(graph);
+ bounds_check_elimination.Run();
ASSERT_TRUE(IsRemoved(bounds_check));
// int[] array = new int[10];
// for (int i=1; i<10; i++) { array[i] = 10; // Can eliminate. }
graph = BuildSSAGraph3(&allocator, &bounds_check, 1, 1, kCondGE);
graph->BuildDominatorTree();
+ graph->AnalyzeNaturalLoops();
RunSimplifierAndGvn(graph);
BoundsCheckElimination bounds_check_elimination_with_initial_1(graph);
bounds_check_elimination_with_initial_1.Run();
@@ -723,6 +725,7 @@
// for (int i=0; i<=10; i++) { array[i] = 10; // Can't eliminate. }
graph = BuildSSAGraph3(&allocator, &bounds_check, 0, 1, kCondGT);
graph->BuildDominatorTree();
+ graph->AnalyzeNaturalLoops();
RunSimplifierAndGvn(graph);
BoundsCheckElimination bounds_check_elimination_with_greater_than(graph);
bounds_check_elimination_with_greater_than.Run();
@@ -732,6 +735,7 @@
// for (int i=1; i<10; i+=8) { array[i] = 10; // Can eliminate. }
graph = BuildSSAGraph3(&allocator, &bounds_check, 1, 8, kCondGE);
graph->BuildDominatorTree();
+ graph->AnalyzeNaturalLoops();
RunSimplifierAndGvn(graph);
BoundsCheckElimination bounds_check_elimination_increment_8(graph);
bounds_check_elimination_increment_8.Run();
@@ -823,22 +827,16 @@
HInstruction* bounds_check = nullptr;
HGraph* graph = BuildSSAGraph4(&allocator, &bounds_check, 0);
graph->BuildDominatorTree();
+ graph->AnalyzeNaturalLoops();
+ RunSimplifierAndGvn(graph);
BoundsCheckElimination bounds_check_elimination(graph);
bounds_check_elimination.Run();
- ASSERT_FALSE(IsRemoved(bounds_check));
-
- // This time add gvn. Need gvn to eliminate the second
- // HArrayLength which uses the null check as its input.
- graph = BuildSSAGraph4(&allocator, &bounds_check, 0);
- graph->BuildDominatorTree();
- RunSimplifierAndGvn(graph);
- BoundsCheckElimination bounds_check_elimination_after_gvn(graph);
- bounds_check_elimination_after_gvn.Run();
ASSERT_TRUE(IsRemoved(bounds_check));
// for (int i=1; i<array.length; i++) { array[array.length-i-1] = 10; // Can eliminate. }
graph = BuildSSAGraph4(&allocator, &bounds_check, 1);
graph->BuildDominatorTree();
+ graph->AnalyzeNaturalLoops();
RunSimplifierAndGvn(graph);
BoundsCheckElimination bounds_check_elimination_with_initial_1(graph);
bounds_check_elimination_with_initial_1.Run();
@@ -847,6 +845,7 @@
// for (int i=0; i<=array.length; i++) { array[array.length-i] = 10; // Can't eliminate. }
graph = BuildSSAGraph4(&allocator, &bounds_check, 0, kCondGT);
graph->BuildDominatorTree();
+ graph->AnalyzeNaturalLoops();
RunSimplifierAndGvn(graph);
BoundsCheckElimination bounds_check_elimination_with_greater_than(graph);
bounds_check_elimination_with_greater_than.Run();
@@ -1022,6 +1021,7 @@
outer_body_add->AddSuccessor(outer_header);
graph->BuildDominatorTree();
+ graph->AnalyzeNaturalLoops();
RunSimplifierAndGvn(graph);
// gvn should remove the same bounds check.
ASSERT_FALSE(IsRemoved(bounds_check1));
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 58416ee..e4680ff 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());
@@ -654,8 +652,8 @@
DCHECK((optimized_invoke_type == invoke_type) || (optimized_invoke_type != kDirect)
|| compiler_driver_->GetCompilerOptions().GetCompilePic());
bool is_recursive =
- (target_method.dex_method_index == dex_compilation_unit_->GetDexMethodIndex());
- DCHECK(!is_recursive || (target_method.dex_file == dex_compilation_unit_->GetDexFile()));
+ (target_method.dex_method_index == outer_compilation_unit_->GetDexMethodIndex())
+ && (target_method.dex_file == outer_compilation_unit_->GetDexFile());
if (optimized_invoke_type == kStatic) {
ScopedObjectAccess soa(Thread::Current());
@@ -712,8 +710,12 @@
clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone;
} else {
clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit;
- HLoadClass* load_class =
- new (arena_) HLoadClass(storage_index, is_referrer_class, dex_pc);
+ HLoadClass* load_class = new (arena_) HLoadClass(
+ graph_->GetCurrentMethod(),
+ storage_index,
+ *dex_compilation_unit_->GetDexFile(),
+ is_referrer_class,
+ dex_pc);
current_block_->AddInstruction(load_class);
clinit_check = new (arena_) HClinitCheck(load_class, dex_pc);
current_block_->AddInstruction(clinit_check);
@@ -746,13 +748,11 @@
for (size_t i = start_index; i < number_of_vreg_arguments; i++, argument_index++) {
Primitive::Type type = Primitive::GetType(descriptor[descriptor_index++]);
bool is_wide = (type == Primitive::kPrimLong) || (type == Primitive::kPrimDouble);
- if (!is_range && is_wide && args[i] + 1 != args[i + 1]) {
- LOG(WARNING) << "Non sequential register pair in " << dex_compilation_unit_->GetSymbol()
- << " at " << dex_pc;
- // We do not implement non sequential register pair.
- MaybeRecordStat(MethodCompilationStat::kNotCompiledNonSequentialRegPair);
- return false;
- }
+ // Longs and doubles should be in pairs, that is, sequential registers. The verifier should
+ // reject any class where this is violated.
+ DCHECK(is_range || !is_wide || (args[i] + 1 == args[i + 1]))
+ << "Non sequential register pair in " << dex_compilation_unit_->GetSymbol()
+ << " at " << dex_pc;
HInstruction* arg = LoadLocal(is_range ? register_index + i : args[i], type);
invoke->SetArgumentAt(argument_index, arg);
if (is_wide) {
@@ -761,6 +761,11 @@
}
DCHECK_EQ(argument_index, number_of_arguments);
+ if (invoke->IsInvokeStaticOrDirect()) {
+ invoke->SetArgumentAt(argument_index, graph_->GetCurrentMethod());
+ argument_index++;
+ }
+
if (clinit_check_requirement == HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit) {
// Add the class initialization check as last input of `invoke`.
DCHECK(clinit_check != nullptr);
@@ -825,13 +830,17 @@
value,
field_type,
resolved_field->GetOffset(),
- resolved_field->IsVolatile()));
+ resolved_field->IsVolatile(),
+ field_index,
+ *dex_file_));
} else {
current_block_->AddInstruction(new (arena_) HInstanceFieldGet(
current_block_->GetLastInstruction(),
field_type,
resolved_field->GetOffset(),
- resolved_field->IsVolatile()));
+ resolved_field->IsVolatile(),
+ field_index,
+ *dex_file_));
UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
}
@@ -916,7 +925,11 @@
*outer_compilation_unit_->GetDexFile(), storage_index);
bool is_initialized = resolved_field->GetDeclaringClass()->IsInitialized() && is_in_dex_cache;
- HLoadClass* constant = new (arena_) HLoadClass(storage_index, is_referrer_class, dex_pc);
+ HLoadClass* constant = new (arena_) HLoadClass(graph_->GetCurrentMethod(),
+ storage_index,
+ *dex_compilation_unit_->GetDexFile(),
+ is_referrer_class,
+ dex_pc);
current_block_->AddInstruction(constant);
HInstruction* cls = constant;
@@ -932,13 +945,20 @@
temps.Add(cls);
HInstruction* value = LoadLocal(source_or_dest_reg, field_type);
DCHECK_EQ(value->GetType(), field_type);
- current_block_->AddInstruction(
- new (arena_) HStaticFieldSet(cls, value, field_type, resolved_field->GetOffset(),
- resolved_field->IsVolatile()));
+ current_block_->AddInstruction(new (arena_) HStaticFieldSet(cls,
+ value,
+ field_type,
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ *dex_file_));
} else {
- current_block_->AddInstruction(
- new (arena_) HStaticFieldGet(cls, field_type, resolved_field->GetOffset(),
- resolved_field->IsVolatile()));
+ current_block_->AddInstruction(new (arena_) HStaticFieldGet(cls,
+ field_type,
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ *dex_file_));
UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
}
return true;
@@ -1027,7 +1047,12 @@
QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
? kQuickAllocArrayWithAccessCheck
: kQuickAllocArray;
- HInstruction* object = new (arena_) HNewArray(length, dex_pc, type_index, entrypoint);
+ HInstruction* object = new (arena_) HNewArray(length,
+ graph_->GetCurrentMethod(),
+ dex_pc,
+ type_index,
+ *dex_compilation_unit_->GetDexFile(),
+ entrypoint);
current_block_->AddInstruction(object);
const char* descriptor = dex_file_->StringByTypeIdx(type_index);
@@ -1152,7 +1177,11 @@
}
HInstruction* object = LoadLocal(reference, Primitive::kPrimNot);
HLoadClass* cls = new (arena_) HLoadClass(
- type_index, IsOutermostCompilingClass(type_index), dex_pc);
+ graph_->GetCurrentMethod(),
+ type_index,
+ *dex_compilation_unit_->GetDexFile(),
+ IsOutermostCompilingClass(type_index),
+ dex_pc);
current_block_->AddInstruction(cls);
// The class needs a temporary before being used by the type check.
Temporaries temps(graph_);
@@ -1977,7 +2006,12 @@
? kQuickAllocObjectWithAccessCheck
: kQuickAllocObject;
- current_block_->AddInstruction(new (arena_) HNewInstance(dex_pc, type_index, entrypoint));
+ current_block_->AddInstruction(new (arena_) HNewInstance(
+ graph_->GetCurrentMethod(),
+ dex_pc,
+ type_index,
+ *dex_compilation_unit_->GetDexFile(),
+ entrypoint));
UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
}
break;
@@ -1989,8 +2023,12 @@
QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
? kQuickAllocArrayWithAccessCheck
: kQuickAllocArray;
- current_block_->AddInstruction(
- new (arena_) HNewArray(length, dex_pc, type_index, entrypoint));
+ current_block_->AddInstruction(new (arena_) HNewArray(length,
+ graph_->GetCurrentMethod(),
+ dex_pc,
+ type_index,
+ *dex_compilation_unit_->GetDexFile(),
+ entrypoint));
UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction());
break;
}
@@ -2135,13 +2173,15 @@
}
case Instruction::CONST_STRING: {
- current_block_->AddInstruction(new (arena_) HLoadString(instruction.VRegB_21c(), dex_pc));
+ current_block_->AddInstruction(
+ new (arena_) HLoadString(graph_->GetCurrentMethod(), instruction.VRegB_21c(), dex_pc));
UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
break;
}
case Instruction::CONST_STRING_JUMBO: {
- current_block_->AddInstruction(new (arena_) HLoadString(instruction.VRegB_31c(), dex_pc));
+ current_block_->AddInstruction(
+ new (arena_) HLoadString(graph_->GetCurrentMethod(), instruction.VRegB_31c(), dex_pc));
UpdateLocal(instruction.VRegA_31c(), current_block_->GetLastInstruction());
break;
}
@@ -2162,8 +2202,12 @@
MaybeRecordStat(MethodCompilationStat::kNotCompiledCantAccesType);
return false;
}
- current_block_->AddInstruction(
- new (arena_) HLoadClass(type_index, IsOutermostCompilingClass(type_index), dex_pc));
+ current_block_->AddInstruction(new (arena_) HLoadClass(
+ graph_->GetCurrentMethod(),
+ type_index,
+ *dex_compilation_unit_->GetDexFile(),
+ IsOutermostCompilingClass(type_index),
+ dex_pc));
UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
break;
}
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index a5d5305..049b3e3 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -288,6 +288,33 @@
}
}
+void CodeGenerator::CreateCommonInvokeLocationSummary(
+ HInvoke* invoke, InvokeDexCallingConventionVisitor* visitor) {
+ ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
+ LocationSummary* locations = new (allocator) LocationSummary(invoke, LocationSummary::kCall);
+
+ for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) {
+ HInstruction* input = invoke->InputAt(i);
+ locations->SetInAt(i, visitor->GetNextLocation(input->GetType()));
+ }
+
+ locations->SetOut(visitor->GetReturnLocation(invoke->GetType()));
+
+ if (invoke->IsInvokeStaticOrDirect()) {
+ HInvokeStaticOrDirect* call = invoke->AsInvokeStaticOrDirect();
+ if (call->IsStringInit()) {
+ locations->AddTemp(visitor->GetMethodLocation());
+ } else if (call->IsRecursive()) {
+ locations->SetInAt(call->GetCurrentMethodInputIndex(), visitor->GetMethodLocation());
+ } else {
+ locations->AddTemp(visitor->GetMethodLocation());
+ locations->SetInAt(call->GetCurrentMethodInputIndex(), Location::RequiresRegister());
+ }
+ } else {
+ locations->AddTemp(visitor->GetMethodLocation());
+ }
+}
+
void CodeGenerator::BlockIfInRegister(Location location, bool is_out) const {
// The DCHECKS below check that a register is not specified twice in
// the summary. The out location can overlap with an input, so we need
@@ -652,23 +679,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()) {
@@ -681,23 +719,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;
}
@@ -708,41 +757,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;
}
@@ -751,16 +803,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);
}
}
@@ -771,17 +825,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);
}
}
@@ -793,16 +848,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;
@@ -813,23 +869,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;
}
@@ -837,7 +893,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 c6317f1..c6ebf6d 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,17 +102,21 @@
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);
};
class InvokeDexCallingConventionVisitor {
public:
virtual Location GetNextLocation(Primitive::Type type) = 0;
+ virtual Location GetReturnLocation(Primitive::Type type) const = 0;
+ virtual Location GetMethodLocation() const = 0;
protected:
InvokeDexCallingConventionVisitor() {}
@@ -331,6 +340,9 @@
virtual ParallelMoveResolver* GetMoveResolver() = 0;
+ static void CreateCommonInvokeLocationSummary(
+ HInvoke* invoke, InvokeDexCallingConventionVisitor* visitor);
+
protected:
CodeGenerator(HGraph* graph,
size_t number_of_core_registers,
@@ -438,6 +450,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 2b1131d..3d3e35d 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -18,6 +18,7 @@
#include "arch/arm/instruction_set_features_arm.h"
#include "art_method.h"
+#include "code_generator_utils.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "gc/accounting/card_table.h"
#include "intrinsics.h"
@@ -40,6 +41,7 @@
}
static constexpr int kCurrentMethodStackOffset = 0;
+static constexpr Register kMethodRegisterArgument = R0;
// We unconditionally allocate R5 to ensure we can do long operations
// with baseline.
@@ -53,7 +55,7 @@
// S registers. Therefore there is no need to block it.
static constexpr DRegister DTMP = D31;
-#define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())->
+#define __ down_cast<ArmAssembler*>(codegen->GetAssembler())->
#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmWordSize, x).Int32Value()
class NullCheckSlowPathARM : public SlowPathCodeARM {
@@ -316,7 +318,7 @@
#undef __
#undef __
-#define __ reinterpret_cast<ArmAssembler*>(GetAssembler())->
+#define __ down_cast<ArmAssembler*>(GetAssembler())->
inline Condition ARMCondition(IfCondition cond) {
switch (cond) {
@@ -347,11 +349,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 +392,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));
@@ -543,7 +545,7 @@
uint32_t push_mask = (core_spill_mask_ & (~(1 << PC))) | 1 << LR;
__ PushList(push_mask);
__ cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(push_mask));
- __ cfi().RelOffsetForMany(DWARFReg(R0), 0, push_mask, kArmWordSize);
+ __ cfi().RelOffsetForMany(DWARFReg(kMethodRegisterArgument), 0, push_mask, kArmWordSize);
if (fpu_spill_mask_ != 0) {
SRegister start_register = SRegister(LeastSignificantBit(fpu_spill_mask_));
__ vpushs(start_register, POPCOUNT(fpu_spill_mask_));
@@ -553,7 +555,7 @@
int adjust = GetFrameSize() - FrameEntrySpillSize();
__ AddConstant(SP, -adjust);
__ cfi().AdjustCFAOffset(adjust);
- __ StoreToOffset(kStoreWord, R0, SP, 0);
+ __ StoreToOffset(kStoreWord, kMethodRegisterArgument, SP, 0);
}
void CodeGeneratorARM::GenerateFrameExit() {
@@ -679,7 +681,7 @@
return Location();
}
-Location InvokeDexCallingConventionVisitorARM::GetReturnLocation(Primitive::Type type) {
+Location InvokeDexCallingConventionVisitorARM::GetReturnLocation(Primitive::Type type) const {
switch (type) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
@@ -705,9 +707,14 @@
case Primitive::kPrimVoid:
return Location();
}
+
UNREACHABLE();
}
+Location InvokeDexCallingConventionVisitorARM::GetMethodLocation() const {
+ return Location::RegisterLocation(kMethodRegisterArgument);
+}
+
void CodeGeneratorARM::Move32(Location destination, Location source) {
if (source.Equals(destination)) {
return;
@@ -802,11 +809,11 @@
void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstruction* move_for) {
LocationSummary* locations = instruction->GetLocations();
- if (locations != nullptr && locations->Out().Equals(location)) {
+ if (instruction->IsCurrentMethod()) {
+ Move32(location, Location::StackSlot(kCurrentMethodStackOffset));
+ } else if (locations != nullptr && locations->Out().Equals(location)) {
return;
- }
-
- if (locations != nullptr && locations->Out().IsConstant()) {
+ } else if (locations != nullptr && locations->Out().IsConstant()) {
HConstant* const_to_move = locations->Out().GetConstant();
if (const_to_move->IsIntConstant() || const_to_move->IsNullConstant()) {
int32_t value = GetInt32ValueOf(const_to_move);
@@ -1027,19 +1034,19 @@
GenerateTestAndBranch(deoptimize, slow_path_entry, nullptr, slow_path_entry);
}
-void LocationsBuilderARM::VisitCondition(HCondition* comp) {
+void LocationsBuilderARM::VisitCondition(HCondition* cond) {
LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall);
+ new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RegisterOrConstant(comp->InputAt(1)));
- if (comp->NeedsMaterialization()) {
+ locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
+ if (cond->NeedsMaterialization()) {
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
}
-void InstructionCodeGeneratorARM::VisitCondition(HCondition* comp) {
- if (!comp->NeedsMaterialization()) return;
- LocationSummary* locations = comp->GetLocations();
+void InstructionCodeGeneratorARM::VisitCondition(HCondition* cond) {
+ if (!cond->NeedsMaterialization()) return;
+ LocationSummary* locations = cond->GetLocations();
Register left = locations->InAt(0).AsRegister<Register>();
if (locations->InAt(1).IsRegister()) {
@@ -1056,11 +1063,11 @@
__ cmp(left, ShifterOperand(temp));
}
}
- __ it(ARMCondition(comp->GetCondition()), kItElse);
+ __ it(ARMCondition(cond->GetCondition()), kItElse);
__ mov(locations->Out().AsRegister<Register>(), ShifterOperand(1),
- ARMCondition(comp->GetCondition()));
+ ARMCondition(cond->GetCondition()));
__ mov(locations->Out().AsRegister<Register>(), ShifterOperand(0),
- ARMOppositeCondition(comp->GetCondition()));
+ ARMOppositeCondition(cond->GetCondition()));
}
void LocationsBuilderARM::VisitEqual(HEqual* comp) {
@@ -1253,11 +1260,6 @@
HandleInvoke(invoke);
}
-void CodeGeneratorARM::LoadCurrentMethod(Register reg) {
- DCHECK(RequiresCurrentMethod());
- __ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset);
-}
-
static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARM* codegen) {
if (invoke->GetLocations()->Intrinsified()) {
IntrinsicCodeGeneratorARM intrinsic(codegen);
@@ -1276,24 +1278,15 @@
return;
}
- Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
-
- codegen_->GenerateStaticOrDirectCall(invoke, temp);
+ LocationSummary* locations = invoke->GetLocations();
+ codegen_->GenerateStaticOrDirectCall(
+ invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
void LocationsBuilderARM::HandleInvoke(HInvoke* invoke) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
- locations->AddTemp(Location::RegisterLocation(R0));
-
InvokeDexCallingConventionVisitorARM calling_convention_visitor;
- for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) {
- HInstruction* input = invoke->InputAt(i);
- locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType()));
- }
-
- locations->SetOut(calling_convention_visitor.GetReturnLocation(invoke->GetType()));
+ CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
}
void LocationsBuilderARM::VisitInvokeVirtual(HInvokeVirtual* invoke) {
@@ -1318,12 +1311,8 @@
Location receiver = locations->InAt(0);
uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
// temp = object->GetClass();
- if (receiver.IsStackSlot()) {
- __ LoadFromOffset(kLoadWord, temp, SP, receiver.GetStackIndex());
- __ LoadFromOffset(kLoadWord, temp, temp, class_offset);
- } else {
- __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset);
- }
+ DCHECK(receiver.IsRegister());
+ __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset);
codegen_->MaybeRecordImplicitNullCheck(invoke);
// temp = temp->GetMethodAt(method_offset);
uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
@@ -1454,11 +1443,12 @@
Primitive::Type input_type = conversion->GetInputType();
DCHECK_NE(result_type, input_type);
- // The float-to-long and double-to-long type conversions rely on a
- // call to the runtime.
+ // The float-to-long, double-to-long and long-to-float type conversions
+ // rely on a call to the runtime.
LocationSummary::CallKind call_kind =
- ((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble)
- && result_type == Primitive::kPrimLong)
+ (((input_type == Primitive::kPrimFloat || input_type == Primitive::kPrimDouble)
+ && result_type == Primitive::kPrimLong)
+ || (input_type == Primitive::kPrimLong && result_type == Primitive::kPrimFloat))
? LocationSummary::kCall
: LocationSummary::kNoCall;
LocationSummary* locations =
@@ -1601,15 +1591,14 @@
locations->SetOut(Location::RequiresFpuRegister());
break;
- case Primitive::kPrimLong:
+ case Primitive::kPrimLong: {
// Processing a Dex `long-to-float' instruction.
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresFpuRegister());
- locations->AddTemp(Location::RequiresRegister());
- locations->AddTemp(Location::RequiresRegister());
- locations->AddTemp(Location::RequiresFpuRegister());
- locations->AddTemp(Location::RequiresFpuRegister());
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetInAt(0, Location::RegisterPairLocation(
+ calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
+ locations->SetOut(Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
break;
+ }
case Primitive::kPrimDouble:
// Processing a Dex `double-to-float' instruction.
@@ -1640,8 +1629,7 @@
// Processing a Dex `long-to-double' instruction.
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresFpuRegister());
- locations->AddTemp(Location::RequiresRegister());
- locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresFpuRegister());
locations->AddTemp(Location::RequiresFpuRegister());
break;
@@ -1818,47 +1806,13 @@
break;
}
- case Primitive::kPrimLong: {
+ case Primitive::kPrimLong:
// Processing a Dex `long-to-float' instruction.
- Register low = in.AsRegisterPairLow<Register>();
- Register high = in.AsRegisterPairHigh<Register>();
- SRegister output = out.AsFpuRegister<SRegister>();
- Register constant_low = locations->GetTemp(0).AsRegister<Register>();
- Register constant_high = locations->GetTemp(1).AsRegister<Register>();
- SRegister temp1_s = locations->GetTemp(2).AsFpuRegisterPairLow<SRegister>();
- DRegister temp1_d = FromLowSToD(temp1_s);
- SRegister temp2_s = locations->GetTemp(3).AsFpuRegisterPairLow<SRegister>();
- DRegister temp2_d = FromLowSToD(temp2_s);
-
- // Operations use doubles for precision reasons (each 32-bit
- // half of a long fits in the 53-bit mantissa of a double,
- // but not in the 24-bit mantissa of a float). This is
- // especially important for the low bits. The result is
- // eventually converted to float.
-
- // temp1_d = int-to-double(high)
- __ vmovsr(temp1_s, high);
- __ vcvtdi(temp1_d, temp1_s);
- // Using vmovd to load the `k2Pow32EncodingForDouble` constant
- // as an immediate value into `temp2_d` does not work, as
- // this instruction only transfers 8 significant bits of its
- // immediate operand. Instead, use two 32-bit core
- // registers to load `k2Pow32EncodingForDouble` into
- // `temp2_d`.
- __ LoadImmediate(constant_low, Low32Bits(k2Pow32EncodingForDouble));
- __ LoadImmediate(constant_high, High32Bits(k2Pow32EncodingForDouble));
- __ vmovdrr(temp2_d, constant_low, constant_high);
- // temp1_d = temp1_d * 2^32
- __ vmuld(temp1_d, temp1_d, temp2_d);
- // temp2_d = unsigned-to-double(low)
- __ vmovsr(temp2_s, low);
- __ vcvtdu(temp2_d, temp2_s);
- // temp1_d = temp1_d + temp2_d
- __ vaddd(temp1_d, temp1_d, temp2_d);
- // output = double-to-float(temp1_d);
- __ vcvtsd(output, temp1_d);
+ codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pL2f),
+ conversion,
+ conversion->GetDexPc(),
+ nullptr);
break;
- }
case Primitive::kPrimDouble:
// Processing a Dex `double-to-float' instruction.
@@ -1893,29 +1847,21 @@
Register high = in.AsRegisterPairHigh<Register>();
SRegister out_s = out.AsFpuRegisterPairLow<SRegister>();
DRegister out_d = FromLowSToD(out_s);
- Register constant_low = locations->GetTemp(0).AsRegister<Register>();
- Register constant_high = locations->GetTemp(1).AsRegister<Register>();
- SRegister temp_s = locations->GetTemp(2).AsFpuRegisterPairLow<SRegister>();
+ SRegister temp_s = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>();
DRegister temp_d = FromLowSToD(temp_s);
+ SRegister constant_s = locations->GetTemp(1).AsFpuRegisterPairLow<SRegister>();
+ DRegister constant_d = FromLowSToD(constant_s);
- // out_d = int-to-double(high)
- __ vmovsr(out_s, high);
- __ vcvtdi(out_d, out_s);
- // Using vmovd to load the `k2Pow32EncodingForDouble` constant
- // as an immediate value into `temp_d` does not work, as
- // this instruction only transfers 8 significant bits of its
- // immediate operand. Instead, use two 32-bit core
- // registers to load `k2Pow32EncodingForDouble` into `temp_d`.
- __ LoadImmediate(constant_low, Low32Bits(k2Pow32EncodingForDouble));
- __ LoadImmediate(constant_high, High32Bits(k2Pow32EncodingForDouble));
- __ vmovdrr(temp_d, constant_low, constant_high);
- // out_d = out_d * 2^32
- __ vmuld(out_d, out_d, temp_d);
- // temp_d = unsigned-to-double(low)
- __ vmovsr(temp_s, low);
- __ vcvtdu(temp_d, temp_s);
- // out_d = out_d + temp_d
- __ vaddd(out_d, out_d, temp_d);
+ // temp_d = int-to-double(high)
+ __ vmovsr(temp_s, high);
+ __ vcvtdi(temp_d, temp_s);
+ // constant_d = k2Pow32EncodingForDouble
+ __ LoadDImmediate(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble));
+ // out_d = unsigned-to-double(low)
+ __ vmovsr(out_s, low);
+ __ vcvtdu(out_d, out_s);
+ // out_d += temp_d * constant_d
+ __ vmlad(out_d, temp_d, constant_d);
break;
}
@@ -2180,11 +2126,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();
+ uint32_t abs_imm = static_cast<uint32_t>(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.
@@ -2195,7 +2264,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);
@@ -2239,7 +2321,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>());
@@ -2291,8 +2375,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;
}
@@ -2301,7 +2388,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);
@@ -2358,7 +2458,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>();
@@ -2599,13 +2701,12 @@
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
InvokeRuntimeCallingConvention calling_convention;
locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
- locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+ locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
locations->SetOut(Location::RegisterLocation(R0));
}
void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) {
InvokeRuntimeCallingConvention calling_convention;
- codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
__ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
codegen_->InvokeRuntime(GetThreadOffset<kArmWordSize>(instruction->GetEntrypoint()).Int32Value(),
instruction,
@@ -2618,14 +2719,13 @@
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
InvokeRuntimeCallingConvention calling_convention;
locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
- locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
locations->SetOut(Location::RegisterLocation(R0));
locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+ locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
}
void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) {
InvokeRuntimeCallingConvention calling_convention;
- codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(2));
__ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
codegen_->InvokeRuntime(GetThreadOffset<kArmWordSize>(instruction->GetEntrypoint()).Int32Value(),
instruction,
@@ -2645,9 +2745,19 @@
locations->SetOut(location);
}
-void InstructionCodeGeneratorARM::VisitParameterValue(HParameterValue* instruction) {
+void InstructionCodeGeneratorARM::VisitParameterValue(
+ HParameterValue* instruction ATTRIBUTE_UNUSED) {
// Nothing to do, the parameter is already at its location.
- UNUSED(instruction);
+}
+
+void LocationsBuilderARM::VisitCurrentMethod(HCurrentMethod* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetOut(Location::RegisterLocation(kMethodRegisterArgument));
+}
+
+void InstructionCodeGeneratorARM::VisitCurrentMethod(HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
+ // Nothing to do, the method is already at its location.
}
void LocationsBuilderARM::VisitNot(HNot* not_) {
@@ -2721,7 +2831,7 @@
Location left = locations->InAt(0);
Location right = locations->InAt(1);
- Label less, greater, done;
+ NearLabel less, greater, done;
Primitive::Type type = compare->InputAt(0)->GetType();
switch (type) {
case Primitive::kPrimLong: {
@@ -2780,22 +2890,22 @@
void InstructionCodeGeneratorARM::GenerateMemoryBarrier(MemBarrierKind kind) {
// TODO (ported from quick): revisit Arm barrier kinds
- DmbOptions flavour = DmbOptions::ISH; // quiet c++ warnings
+ DmbOptions flavor = DmbOptions::ISH; // quiet c++ warnings
switch (kind) {
case MemBarrierKind::kAnyStore:
case MemBarrierKind::kLoadAny:
case MemBarrierKind::kAnyAny: {
- flavour = DmbOptions::ISH;
+ flavor = DmbOptions::ISH;
break;
}
case MemBarrierKind::kStoreStore: {
- flavour = DmbOptions::ISHST;
+ flavor = DmbOptions::ISHST;
break;
}
default:
LOG(FATAL) << "Unexpected memory barrier " << kind;
}
- __ dmb(flavour);
+ __ dmb(flavor);
}
void InstructionCodeGeneratorARM::GenerateWideAtomicLoad(Register addr,
@@ -2817,7 +2927,7 @@
Register temp1,
Register temp2,
HInstruction* instruction) {
- Label fail;
+ NearLabel fail;
if (offset != 0) {
__ LoadImmediate(temp1, offset);
__ add(IP, addr, ShifterOperand(temp1));
@@ -2875,7 +2985,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();
@@ -2964,7 +3075,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) {
@@ -3091,7 +3203,7 @@
}
void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -3115,7 +3227,7 @@
}
void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) {
@@ -3385,7 +3497,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);
@@ -3490,13 +3602,21 @@
__ b(slow_path->GetEntryLabel(), CS);
}
-void CodeGeneratorARM::MarkGCCard(Register temp, Register card, Register object, Register value) {
- Label is_null;
- __ CompareAndBranchIfZero(value, &is_null);
+void CodeGeneratorARM::MarkGCCard(Register temp,
+ Register card,
+ Register object,
+ Register value,
+ bool can_be_null) {
+ NearLabel 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) {
@@ -3787,21 +3907,25 @@
: LocationSummary::kNoCall;
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+ locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
}
void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) {
- Register out = cls->GetLocations()->Out().AsRegister<Register>();
+ LocationSummary* locations = cls->GetLocations();
+ Register out = locations->Out().AsRegister<Register>();
+ Register current_method = locations->InAt(0).AsRegister<Register>();
if (cls->IsReferrersClass()) {
DCHECK(!cls->CanCallRuntime());
DCHECK(!cls->MustGenerateClinitCheck());
- codegen_->LoadCurrentMethod(out);
- __ LoadFromOffset(kLoadWord, out, out, ArtMethod::DeclaringClassOffset().Int32Value());
+ __ LoadFromOffset(
+ kLoadWord, out, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
} else {
DCHECK(cls->CanCallRuntime());
- codegen_->LoadCurrentMethod(out);
- __ LoadFromOffset(
- kLoadWord, out, out, ArtMethod::DexCacheResolvedTypesOffset().Int32Value());
+ __ LoadFromOffset(kLoadWord,
+ out,
+ current_method,
+ ArtMethod::DexCacheResolvedTypesOffset().Int32Value());
__ LoadFromOffset(kLoadWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM(
@@ -3849,6 +3973,7 @@
void LocationsBuilderARM::VisitLoadString(HLoadString* load) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+ locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
}
@@ -3856,9 +3981,11 @@
SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load);
codegen_->AddSlowPath(slow_path);
- Register out = load->GetLocations()->Out().AsRegister<Register>();
- codegen_->LoadCurrentMethod(out);
- __ LoadFromOffset(kLoadWord, out, out, ArtMethod::DeclaringClassOffset().Int32Value());
+ LocationSummary* locations = load->GetLocations();
+ Register out = locations->Out().AsRegister<Register>();
+ Register current_method = locations->InAt(0).AsRegister<Register>();
+ __ LoadFromOffset(
+ kLoadWord, out, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
__ LoadFromOffset(kLoadWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value());
__ LoadFromOffset(kLoadWord, out, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
__ cmp(out, ShifterOperand(0));
@@ -3909,14 +4036,13 @@
Register cls = locations->InAt(1).AsRegister<Register>();
Register out = locations->Out().AsRegister<Register>();
uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
- Label done, zero;
+ NearLabel done, zero;
SlowPathCodeARM* slow_path = nullptr;
// Return 0 if `obj` is null.
// avoid null check if we know obj is not null.
if (instruction->MustDoNullCheck()) {
- __ cmp(obj, ShifterOperand(0));
- __ b(&zero, EQ);
+ __ CompareAndBranchIfZero(obj, &zero);
}
// Compare the class of `obj` with `cls`.
__ LoadFromOffset(kLoadWord, out, obj, class_offset);
@@ -3967,16 +4093,19 @@
instruction, locations->InAt(1), locations->GetTemp(0), instruction->GetDexPc());
codegen_->AddSlowPath(slow_path);
+ NearLabel done;
// avoid null check if we know obj is not null.
if (instruction->MustDoNullCheck()) {
- __ cmp(obj, ShifterOperand(0));
- __ b(slow_path->GetExitLabel(), EQ);
+ __ CompareAndBranchIfZero(obj, &done);
}
// Compare the class of `obj` with `cls`.
__ LoadFromOffset(kLoadWord, temp, obj, class_offset);
__ cmp(temp, ShifterOperand(cls));
__ b(slow_path->GetEntryLabel(), NE);
__ Bind(slow_path->GetExitLabel());
+ if (instruction->MustDoNullCheck()) {
+ __ Bind(&done);
+ }
}
void LocationsBuilderARM::VisitMonitorOperation(HMonitorOperation* instruction) {
@@ -4066,9 +4195,7 @@
}
}
-void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Register temp) {
- DCHECK_EQ(temp, kArtMethodRegister);
-
+void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
// TODO: Implement all kinds of calls:
// 1) boot -> boot
// 2) app -> boot
@@ -4077,32 +4204,40 @@
// Currently we implement the app -> app logic, which looks up in the resolve cache.
if (invoke->IsStringInit()) {
+ Register reg = temp.AsRegister<Register>();
// temp = thread->string_init_entrypoint
- __ LoadFromOffset(kLoadWord, temp, TR, invoke->GetStringInitOffset());
+ __ LoadFromOffset(kLoadWord, reg, TR, invoke->GetStringInitOffset());
// LR = temp[offset_of_quick_compiled_code]
- __ LoadFromOffset(kLoadWord, LR, temp,
+ __ LoadFromOffset(kLoadWord, LR, reg,
ArtMethod::EntryPointFromQuickCompiledCodeOffset(
kArmWordSize).Int32Value());
// LR()
__ blx(LR);
+ } else if (invoke->IsRecursive()) {
+ __ bl(GetFrameEntryLabel());
} else {
- // temp = method;
- LoadCurrentMethod(temp);
- if (!invoke->IsRecursive()) {
- // temp = temp->dex_cache_resolved_methods_;
- __ LoadFromOffset(
- kLoadWord, temp, temp, ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
- // temp = temp[index_in_cache]
- __ LoadFromOffset(
- kLoadWord, temp, temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex()));
- // LR = temp[offset_of_quick_compiled_code]
- __ LoadFromOffset(kLoadWord, LR, temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(
- kArmWordSize).Int32Value());
- // LR()
- __ blx(LR);
+ Location current_method = invoke->GetLocations()->InAt(invoke->GetCurrentMethodInputIndex());
+ Register method_reg;
+ Register reg = temp.AsRegister<Register>();
+ if (current_method.IsRegister()) {
+ method_reg = current_method.AsRegister<Register>();
} else {
- __ bl(GetFrameEntryLabel());
+ DCHECK(invoke->GetLocations()->Intrinsified());
+ DCHECK(!current_method.IsValid());
+ method_reg = reg;
+ __ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset);
}
+ // reg = current_method->dex_cache_resolved_methods_;
+ __ LoadFromOffset(
+ kLoadWord, reg, method_reg, ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
+ // reg = reg[index_in_cache]
+ __ LoadFromOffset(
+ kLoadWord, reg, reg, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex()));
+ // LR = reg[offset_of_quick_compiled_code]
+ __ LoadFromOffset(kLoadWord, LR, reg, ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kArmWordSize).Int32Value());
+ // LR()
+ __ blx(LR);
}
DCHECK(!IsLeafMethod());
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index c410fa8..824e48c 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -86,7 +86,8 @@
virtual ~InvokeDexCallingConventionVisitorARM() {}
Location GetNextLocation(Primitive::Type type) OVERRIDE;
- Location GetReturnLocation(Primitive::Type type);
+ Location GetReturnLocation(Primitive::Type type) const OVERRIDE;
+ Location GetMethodLocation() const OVERRIDE;
private:
InvokeDexCallingConvention calling_convention;
@@ -138,10 +139,16 @@
#define DECLARE_VISIT_INSTRUCTION(name, super) \
void Visit##name(H##name* instr);
- FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
+ void VisitInstruction(HInstruction* instruction) OVERRIDE {
+ LOG(FATAL) << "Unreachable instruction " << instruction->DebugName()
+ << " (id " << instruction->GetId() << ")";
+ }
+
private:
void HandleInvoke(HInvoke* invoke);
void HandleBitwiseOperation(HBinaryOperation* operation);
@@ -162,10 +169,16 @@
#define DECLARE_VISIT_INSTRUCTION(name, super) \
void Visit##name(H##name* instr);
- FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
+ void VisitInstruction(HInstruction* instruction) OVERRIDE {
+ LOG(FATAL) << "Unreachable instruction " << instruction->DebugName()
+ << " (id " << instruction->GetId() << ")";
+ }
+
ArmAssembler* GetAssembler() const { return assembler_; }
private:
@@ -183,7 +196,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);
@@ -191,6 +206,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_;
@@ -264,15 +283,12 @@
// Helper method to move a 64bits value between two locations.
void Move64(Location destination, Location source);
- // Load current method into `reg`.
- void LoadCurrentMethod(Register reg);
-
// Generate code to invoke a runtime entry point.
void InvokeRuntime(
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);
@@ -294,7 +310,7 @@
Label* GetFrameEntryLabel() { return &frame_entry_label_; }
- void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Register temp);
+ void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
private:
// Labels for each block that will be compiled.
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 55ef66f..3c8f117 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -18,6 +18,7 @@
#include "arch/arm64/instruction_set_features_arm64.h"
#include "art_method.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 int kCurrentMethodStackOffset = 0;
@@ -82,7 +84,6 @@
}
Location ARM64ReturnLocation(Primitive::Type return_type) {
- DCHECK_NE(return_type, Primitive::kPrimVoid);
// Note that in practice, `LocationFrom(x0)` and `LocationFrom(w0)` create the
// same Location object, and so do `LocationFrom(d0)` and `LocationFrom(s0)`,
// but we use the exact registers for clarity.
@@ -92,6 +93,8 @@
return LocationFrom(d0);
} else if (return_type == Primitive::kPrimLong) {
return LocationFrom(x0);
+ } else if (return_type == Primitive::kPrimVoid) {
+ return Location::NoLocation();
} else {
return LocationFrom(w0);
}
@@ -104,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,
@@ -398,6 +483,10 @@
return next_location;
}
+Location InvokeDexCallingConventionVisitorARM64::GetMethodLocation() const {
+ return LocationFrom(kArtMethodRegister);
+}
+
CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph,
const Arm64InstructionSetFeatures& isa_features,
const CompilerOptions& compiler_options)
@@ -528,6 +617,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));
}
@@ -536,16 +638,16 @@
Location location,
HInstruction* move_for) {
LocationSummary* locations = instruction->GetLocations();
- if (locations != nullptr && locations->Out().Equals(location)) {
- return;
- }
-
Primitive::Type type = instruction->GetType();
DCHECK_NE(type, Primitive::kPrimVoid);
- if (instruction->IsIntConstant()
- || instruction->IsLongConstant()
- || instruction->IsNullConstant()) {
+ if (instruction->IsCurrentMethod()) {
+ MoveLocation(location, Location::DoubleStackSlot(kCurrentMethodStackOffset));
+ } else if (locations != nullptr && locations->Out().Equals(location)) {
+ return;
+ } else if (instruction->IsIntConstant()
+ || instruction->IsLongConstant()
+ || instruction->IsNullConstant()) {
int64_t value = GetInt64ValueOf(instruction->AsConstant());
if (location.IsRegister()) {
Register dst = RegisterFrom(location, type);
@@ -603,16 +705,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 {
@@ -690,11 +796,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) {
@@ -965,12 +1071,6 @@
}
}
-void CodeGeneratorARM64::LoadCurrentMethod(vixl::Register current_method) {
- DCHECK(RequiresCurrentMethod());
- CHECK(current_method.IsX());
- __ Ldr(current_method, MemOperand(sp, kCurrentMethodStackOffset));
-}
-
void CodeGeneratorARM64::InvokeRuntime(int32_t entry_point_offset,
HInstruction* instruction,
uint32_t dex_pc,
@@ -978,14 +1078,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,
@@ -1169,7 +1267,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());
@@ -1195,7 +1294,7 @@
}
if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
- codegen_->MarkGCCard(obj, Register(value));
+ codegen_->MarkGCCard(obj, Register(value), value_can_be_null);
}
}
@@ -1421,7 +1520,7 @@
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
if (CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue())) {
- codegen_->MarkGCCard(obj, value.W());
+ codegen_->MarkGCCard(obj, value.W(), instruction->GetValueCanBeNull());
}
}
}
@@ -1604,6 +1703,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());
+ uint64_t abs_imm = static_cast<uint64_t>(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);
@@ -1611,7 +1856,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;
@@ -1632,7 +1877,7 @@
switch (type) {
case Primitive::kPrimInt:
case Primitive::kPrimLong:
- __ Sdiv(OutputRegister(div), InputRegisterAt(div, 0), InputRegisterAt(div, 1));
+ GenerateDivRemIntegral(div);
break;
case Primitive::kPrimFloat:
@@ -1847,7 +2092,7 @@
}
void InstructionCodeGeneratorARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) {
@@ -1917,20 +2162,8 @@
}
void LocationsBuilderARM64::HandleInvoke(HInvoke* invoke) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
- locations->AddTemp(LocationFrom(x0));
-
InvokeDexCallingConventionVisitorARM64 calling_convention_visitor;
- for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) {
- HInstruction* input = invoke->InputAt(i);
- locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType()));
- }
-
- Primitive::Type return_type = invoke->GetType();
- if (return_type != Primitive::kPrimVoid) {
- locations->SetOut(calling_convention_visitor.GetReturnLocation(return_type));
- }
+ CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
}
void LocationsBuilderARM64::VisitInvokeInterface(HInvokeInterface* invoke) {
@@ -2003,9 +2236,8 @@
return false;
}
-void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Register temp) {
+void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
// Make sure that ArtMethod* is passed in kArtMethodRegister as per the calling convention.
- DCHECK(temp.Is(kArtMethodRegister));
size_t index_in_cache = GetCachePointerOffset(invoke->GetDexMethodIndex());
// TODO: Implement all kinds of calls:
@@ -2016,30 +2248,39 @@
// Currently we implement the app -> app logic, which looks up in the resolve cache.
if (invoke->IsStringInit()) {
+ Register reg = XRegisterFrom(temp);
// temp = thread->string_init_entrypoint
- __ Ldr(temp.X(), MemOperand(tr, invoke->GetStringInitOffset()));
+ __ Ldr(reg.X(), MemOperand(tr, invoke->GetStringInitOffset()));
// LR = temp->entry_point_from_quick_compiled_code_;
__ Ldr(lr, MemOperand(
- temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64WordSize).Int32Value()));
+ reg, ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64WordSize).Int32Value()));
// lr()
__ Blr(lr);
+ } else if (invoke->IsRecursive()) {
+ __ Bl(&frame_entry_label_);
} else {
- // temp = method;
- LoadCurrentMethod(temp.X());
- if (!invoke->IsRecursive()) {
- // temp = temp->dex_cache_resolved_methods_;
- __ Ldr(temp.W(), MemOperand(temp.X(),
- ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
- // temp = temp[index_in_cache];
- __ Ldr(temp.X(), MemOperand(temp, index_in_cache));
- // lr = temp->entry_point_from_quick_compiled_code_;
- __ Ldr(lr, MemOperand(temp.X(), ArtMethod::EntryPointFromQuickCompiledCodeOffset(
- kArm64WordSize).Int32Value()));
- // lr();
- __ Blr(lr);
+ Location current_method = invoke->GetLocations()->InAt(invoke->GetCurrentMethodInputIndex());
+ Register reg = XRegisterFrom(temp);
+ Register method_reg;
+ if (current_method.IsRegister()) {
+ method_reg = XRegisterFrom(current_method);
} else {
- __ Bl(&frame_entry_label_);
+ DCHECK(invoke->GetLocations()->Intrinsified());
+ DCHECK(!current_method.IsValid());
+ method_reg = reg;
+ __ Ldr(reg.X(), MemOperand(sp, kCurrentMethodStackOffset));
}
+
+ // temp = current_method->dex_cache_resolved_methods_;
+ __ Ldr(reg.W(), MemOperand(method_reg.X(),
+ ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
+ // temp = temp[index_in_cache];
+ __ Ldr(reg.X(), MemOperand(reg, index_in_cache));
+ // lr = temp->entry_point_from_quick_compiled_code_;
+ __ Ldr(lr, MemOperand(reg.X(), ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kArm64WordSize).Int32Value()));
+ // lr();
+ __ Blr(lr);
}
DCHECK(!IsLeafMethod());
@@ -2055,8 +2296,9 @@
}
BlockPoolsScope block_pools(GetVIXLAssembler());
- Register temp = XRegisterFrom(invoke->GetLocations()->GetTemp(0));
- codegen_->GenerateStaticOrDirectCall(invoke, temp);
+ LocationSummary* locations = invoke->GetLocations();
+ codegen_->GenerateStaticOrDirectCall(
+ invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
@@ -2075,14 +2317,8 @@
BlockPoolsScope block_pools(GetVIXLAssembler());
- // temp = object->GetClass();
- if (receiver.IsStackSlot()) {
- __ Ldr(temp.W(), MemOperand(sp, receiver.GetStackIndex()));
- __ Ldr(temp.W(), HeapOperand(temp.W(), class_offset));
- } else {
- DCHECK(receiver.IsRegister());
- __ Ldr(temp.W(), HeapOperandFrom(receiver, class_offset));
- }
+ DCHECK(receiver.IsRegister());
+ __ Ldr(temp.W(), HeapOperandFrom(receiver, class_offset));
codegen_->MaybeRecordImplicitNullCheck(invoke);
// temp = temp->GetMethodAt(method_offset);
__ Ldr(temp, MemOperand(temp, method_offset));
@@ -2098,20 +2334,20 @@
LocationSummary::CallKind call_kind = cls->CanCallRuntime() ? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+ locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
}
void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) {
Register out = OutputRegister(cls);
+ Register current_method = InputRegisterAt(cls, 0);
if (cls->IsReferrersClass()) {
DCHECK(!cls->CanCallRuntime());
DCHECK(!cls->MustGenerateClinitCheck());
- codegen_->LoadCurrentMethod(out.X());
- __ Ldr(out, MemOperand(out.X(), ArtMethod::DeclaringClassOffset().Int32Value()));
+ __ Ldr(out, MemOperand(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
} else {
DCHECK(cls->CanCallRuntime());
- codegen_->LoadCurrentMethod(out.X());
- __ Ldr(out, MemOperand(out.X(), ArtMethod::DexCacheResolvedTypesOffset().Int32Value()));
+ __ Ldr(out, MemOperand(current_method, ArtMethod::DexCacheResolvedTypesOffset().Int32Value()));
__ Ldr(out, HeapOperand(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM64(
@@ -2150,6 +2386,7 @@
void LocationsBuilderARM64::VisitLoadString(HLoadString* load) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+ locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
}
@@ -2158,8 +2395,8 @@
codegen_->AddSlowPath(slow_path);
Register out = OutputRegister(load);
- codegen_->LoadCurrentMethod(out.X());
- __ Ldr(out, MemOperand(out.X(), ArtMethod::DeclaringClassOffset().Int32Value()));
+ Register current_method = InputRegisterAt(load, 0);
+ __ Ldr(out, MemOperand(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
__ Ldr(out, HeapOperand(out, mirror::Class::DexCacheStringsOffset()));
__ Ldr(out, HeapOperand(out, CodeGenerator::GetCacheOffset(load->GetStringIndex())));
__ Cbz(out, slow_path->GetEntryLabel());
@@ -2283,9 +2520,9 @@
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
InvokeRuntimeCallingConvention calling_convention;
locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
- locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(2)));
locations->SetOut(LocationFrom(x0));
locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1)));
+ locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(2)));
CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck,
void*, uint32_t, int32_t, ArtMethod*>();
}
@@ -2295,9 +2532,6 @@
InvokeRuntimeCallingConvention calling_convention;
Register type_index = RegisterFrom(locations->GetTemp(0), Primitive::kPrimInt);
DCHECK(type_index.Is(w0));
- Register current_method = RegisterFrom(locations->GetTemp(1), Primitive::kPrimLong);
- DCHECK(current_method.Is(x2));
- codegen_->LoadCurrentMethod(current_method.X());
__ Mov(type_index, instruction->GetTypeIndex());
codegen_->InvokeRuntime(
GetThreadOffset<kArm64WordSize>(instruction->GetEntrypoint()).Int32Value(),
@@ -2312,7 +2546,7 @@
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
InvokeRuntimeCallingConvention calling_convention;
locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
- locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(1)));
+ locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1)));
locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, ArtMethod*>();
}
@@ -2321,9 +2555,6 @@
LocationSummary* locations = instruction->GetLocations();
Register type_index = RegisterFrom(locations->GetTemp(0), Primitive::kPrimInt);
DCHECK(type_index.Is(w0));
- Register current_method = RegisterFrom(locations->GetTemp(1), Primitive::kPrimNot);
- DCHECK(current_method.Is(w1));
- codegen_->LoadCurrentMethod(current_method.X());
__ Mov(type_index, instruction->GetTypeIndex());
codegen_->InvokeRuntime(
GetThreadOffset<kArm64WordSize>(instruction->GetEntrypoint()).Int32Value(),
@@ -2426,9 +2657,20 @@
locations->SetOut(location);
}
-void InstructionCodeGeneratorARM64::VisitParameterValue(HParameterValue* instruction) {
+void InstructionCodeGeneratorARM64::VisitParameterValue(
+ HParameterValue* instruction ATTRIBUTE_UNUSED) {
// Nothing to do, the parameter is already at its location.
- UNUSED(instruction);
+}
+
+void LocationsBuilderARM64::VisitCurrentMethod(HCurrentMethod* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetOut(LocationFrom(kArtMethodRegister));
+}
+
+void InstructionCodeGeneratorARM64::VisitCurrentMethod(
+ HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
+ // Nothing to do, the method is already at its location.
}
void LocationsBuilderARM64::VisitPhi(HPhi* instruction) {
@@ -2454,7 +2696,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;
@@ -2479,14 +2721,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;
}
@@ -2596,7 +2831,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 3486cde..f96810f 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -44,7 +44,7 @@
};
static constexpr size_t kParameterFPRegistersLength = arraysize(kParameterFPRegisters);
-const vixl::Register tr = vixl::x18; // Thread Register
+const vixl::Register tr = vixl::x19; // Thread Register
static const vixl::Register kArtMethodRegister = vixl::x0; // Method register on invoke.
const vixl::CPURegList vixl_reserved_core_registers(vixl::ip0, vixl::ip1);
@@ -52,10 +52,10 @@
const vixl::CPURegList runtime_reserved_core_registers(tr, vixl::lr);
-// Callee-saved registers defined by AAPCS64.
+// Callee-saved registers AAPCS64 (without x19 - Thread Register)
const vixl::CPURegList callee_saved_core_registers(vixl::CPURegister::kRegister,
vixl::kXRegSize,
- vixl::x19.code(),
+ vixl::x20.code(),
vixl::x30.code());
const vixl::CPURegList callee_saved_fp_registers(vixl::CPURegister::kFPRegister,
vixl::kDRegSize,
@@ -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_;
@@ -112,7 +115,7 @@
kParameterFPRegistersLength,
kArm64PointerSize) {}
- Location GetReturnLocation(Primitive::Type return_type) {
+ Location GetReturnLocation(Primitive::Type return_type) const {
return ARM64ReturnLocation(return_type);
}
@@ -127,9 +130,10 @@
virtual ~InvokeDexCallingConventionVisitorARM64() {}
Location GetNextLocation(Primitive::Type type) OVERRIDE;
- Location GetReturnLocation(Primitive::Type return_type) {
+ Location GetReturnLocation(Primitive::Type return_type) const OVERRIDE {
return calling_convention.GetReturnLocation(return_type);
}
+ Location GetMethodLocation() const OVERRIDE;
private:
InvokeDexCallingConvention calling_convention;
@@ -143,10 +147,16 @@
#define DECLARE_VISIT_INSTRUCTION(name, super) \
void Visit##name(H##name* instr) OVERRIDE;
- FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+
+ FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION)
+
#undef DECLARE_VISIT_INSTRUCTION
- void LoadCurrentMethod(XRegister reg);
+ void VisitInstruction(HInstruction* instruction) OVERRIDE {
+ LOG(FATAL) << "Unreachable instruction " << instruction->DebugName()
+ << " (id " << instruction->GetId() << ")";
+ }
Arm64Assembler* GetAssembler() const { return assembler_; }
vixl::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->vixl_masm_; }
@@ -156,7 +166,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);
@@ -165,6 +177,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_;
@@ -179,9 +196,17 @@
#define DECLARE_VISIT_INSTRUCTION(name, super) \
void Visit##name(H##name* instr) OVERRIDE;
- FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+
+ FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION)
+
#undef DECLARE_VISIT_INSTRUCTION
+ void VisitInstruction(HInstruction* instruction) OVERRIDE {
+ LOG(FATAL) << "Unreachable instruction " << instruction->DebugName()
+ << " (id " << instruction->GetId() << ")";
+ }
+
private:
void HandleBinaryOp(HBinaryOperation* instr);
void HandleFieldSet(HInstruction* instruction);
@@ -229,15 +254,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;
@@ -268,7 +286,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.
@@ -279,10 +297,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.
@@ -324,7 +342,6 @@
Primitive::Type type = Primitive::kPrimVoid);
void Load(Primitive::Type type, vixl::CPURegister dst, const vixl::MemOperand& src);
void Store(Primitive::Type type, vixl::CPURegister rt, const vixl::MemOperand& dst);
- void LoadCurrentMethod(vixl::Register current_method);
void LoadAcquire(HInstruction* instruction, vixl::CPURegister dst, const vixl::MemOperand& src);
void StoreRelease(Primitive::Type type, vixl::CPURegister rt, const vixl::MemOperand& dst);
@@ -340,7 +357,7 @@
return false;
}
- void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, vixl::Register temp);
+ void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
private:
// Labels for each block that will be compiled.
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 60fd29b..e39a1c2 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -36,6 +36,7 @@
namespace x86 {
static constexpr int kCurrentMethodStackOffset = 0;
+static constexpr Register kMethodRegisterArgument = EAX;
static constexpr Register kCoreCalleeSaves[] = { EBP, ESI, EDI };
@@ -43,7 +44,7 @@
static constexpr int kFakeReturnRegister = Register(8);
-#define __ reinterpret_cast<X86Assembler*>(codegen->GetAssembler())->
+#define __ down_cast<X86Assembler*>(codegen->GetAssembler())->
class NullCheckSlowPathX86 : public SlowPathCodeX86 {
public:
@@ -323,7 +324,7 @@
};
#undef __
-#define __ reinterpret_cast<X86Assembler*>(GetAssembler())->
+#define __ down_cast<X86Assembler*>(GetAssembler())->
inline Condition X86Condition(IfCondition cond) {
switch (cond) {
@@ -340,11 +341,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) {
@@ -498,7 +499,7 @@
int adjust = GetFrameSize() - FrameEntrySpillSize();
__ subl(ESP, Immediate(adjust));
__ cfi().AdjustCFAOffset(adjust);
- __ movl(Address(ESP, kCurrentMethodStackOffset), EAX);
+ __ movl(Address(ESP, kCurrentMethodStackOffset), kMethodRegisterArgument);
}
void CodeGeneratorX86::GenerateFrameExit() {
@@ -526,11 +527,6 @@
__ Bind(GetLabelOf(block));
}
-void CodeGeneratorX86::LoadCurrentMethod(Register reg) {
- DCHECK(RequiresCurrentMethod());
- __ movl(reg, Address(ESP, kCurrentMethodStackOffset));
-}
-
Location CodeGeneratorX86::GetStackLocation(HLoadLocal* load) const {
switch (load->GetType()) {
case Primitive::kPrimLong:
@@ -555,6 +551,34 @@
UNREACHABLE();
}
+Location InvokeDexCallingConventionVisitorX86::GetReturnLocation(Primitive::Type type) const {
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot:
+ return Location::RegisterLocation(EAX);
+
+ case Primitive::kPrimLong:
+ return Location::RegisterPairLocation(EAX, EDX);
+
+ case Primitive::kPrimVoid:
+ return Location::NoLocation();
+
+ case Primitive::kPrimDouble:
+ case Primitive::kPrimFloat:
+ return Location::FpuRegisterLocation(XMM0);
+ }
+
+ UNREACHABLE();
+}
+
+Location InvokeDexCallingConventionVisitorX86::GetMethodLocation() const {
+ return Location::RegisterLocation(kMethodRegisterArgument);
+}
+
Location InvokeDexCallingConventionVisitorX86::GetNextLocation(Primitive::Type type) {
switch (type) {
case Primitive::kPrimBoolean:
@@ -717,11 +741,11 @@
void CodeGeneratorX86::Move(HInstruction* instruction, Location location, HInstruction* move_for) {
LocationSummary* locations = instruction->GetLocations();
- if (locations != nullptr && locations->Out().Equals(location)) {
+ if (instruction->IsCurrentMethod()) {
+ Move32(location, Location::StackSlot(kCurrentMethodStackOffset));
+ } else if (locations != nullptr && locations->Out().Equals(location)) {
return;
- }
-
- if (locations != nullptr && locations->Out().IsConstant()) {
+ } else if (locations != nullptr && locations->Out().IsConstant()) {
HConstant* const_to_move = locations->Out().GetConstant();
if (const_to_move->IsIntConstant() || const_to_move->IsNullConstant()) {
Immediate imm(GetInt32ValueOf(const_to_move));
@@ -976,27 +1000,26 @@
default:
LOG(FATAL) << "Unknown local type " << store->InputAt(1)->GetType();
}
- store->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitStoreLocal(HStoreLocal* store) {
UNUSED(store);
}
-void LocationsBuilderX86::VisitCondition(HCondition* comp) {
+void LocationsBuilderX86::VisitCondition(HCondition* cond) {
LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall);
+ new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::Any());
- if (comp->NeedsMaterialization()) {
+ if (cond->NeedsMaterialization()) {
// We need a byte register.
locations->SetOut(Location::RegisterLocation(ECX));
}
}
-void InstructionCodeGeneratorX86::VisitCondition(HCondition* comp) {
- if (comp->NeedsMaterialization()) {
- LocationSummary* locations = comp->GetLocations();
+void InstructionCodeGeneratorX86::VisitCondition(HCondition* cond) {
+ if (cond->NeedsMaterialization()) {
+ LocationSummary* locations = cond->GetLocations();
Register reg = locations->Out().AsRegister<Register>();
// Clear register: setcc only sets the low byte.
__ xorl(reg, reg);
@@ -1014,7 +1037,7 @@
} else {
__ cmpl(lhs.AsRegister<Register>(), Address(ESP, rhs.GetStackIndex()));
}
- __ setb(X86Condition(comp->GetCondition()), reg);
+ __ setb(X86Condition(cond->GetCondition()), reg);
}
}
@@ -1207,6 +1230,17 @@
}
HandleInvoke(invoke);
+
+ if (codegen_->IsBaseline()) {
+ // Baseline does not have enough registers if the current method also
+ // needs a register. We therefore do not require a register for it, and let
+ // the code generation of the invoke handle it.
+ LocationSummary* locations = invoke->GetLocations();
+ Location location = locations->InAt(invoke->GetCurrentMethodInputIndex());
+ if (location.IsUnallocated() && location.GetPolicy() == Location::kRequiresRegister) {
+ locations->SetInAt(invoke->GetCurrentMethodInputIndex(), Location::NoLocation());
+ }
+ }
}
static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorX86* codegen) {
@@ -1227,8 +1261,9 @@
return;
}
+ LocationSummary* locations = invoke->GetLocations();
codegen_->GenerateStaticOrDirectCall(
- invoke, invoke->GetLocations()->GetTemp(0).AsRegister<Register>());
+ invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
@@ -1237,40 +1272,8 @@
}
void LocationsBuilderX86::HandleInvoke(HInvoke* invoke) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
- locations->AddTemp(Location::RegisterLocation(EAX));
-
InvokeDexCallingConventionVisitorX86 calling_convention_visitor;
- for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) {
- HInstruction* input = invoke->InputAt(i);
- locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType()));
- }
-
- switch (invoke->GetType()) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimInt:
- case Primitive::kPrimNot:
- locations->SetOut(Location::RegisterLocation(EAX));
- break;
-
- case Primitive::kPrimLong:
- locations->SetOut(Location::RegisterPairLocation(EAX, EDX));
- break;
-
- case Primitive::kPrimVoid:
- break;
-
- case Primitive::kPrimDouble:
- case Primitive::kPrimFloat:
- locations->SetOut(Location::FpuRegisterLocation(XMM0));
- break;
- }
-
- invoke->SetLocations(locations);
+ CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
}
void InstructionCodeGeneratorX86::VisitInvokeVirtual(HInvokeVirtual* invoke) {
@@ -1280,13 +1283,8 @@
LocationSummary* locations = invoke->GetLocations();
Location receiver = locations->InAt(0);
uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
- // temp = object->GetClass();
- if (receiver.IsStackSlot()) {
- __ movl(temp, Address(ESP, receiver.GetStackIndex()));
- __ movl(temp, Address(temp, class_offset));
- } else {
- __ movl(temp, Address(receiver.AsRegister<Register>(), class_offset));
- }
+ DCHECK(receiver.IsRegister());
+ __ movl(temp, Address(receiver.AsRegister<Register>(), class_offset));
codegen_->MaybeRecordImplicitNullCheck(invoke);
// temp = temp->GetMethodAt(method_offset);
__ movl(temp, Address(temp, method_offset));
@@ -1959,6 +1957,8 @@
if (second.IsRegister()) {
if (out.AsRegister<Register>() == first.AsRegister<Register>()) {
__ addl(out.AsRegister<Register>(), second.AsRegister<Register>());
+ } else if (out.AsRegister<Register>() == second.AsRegister<Register>()) {
+ __ addl(out.AsRegister<Register>(), first.AsRegister<Register>());
} else {
__ leal(out.AsRegister<Register>(), Address(
first.AsRegister<Register>(), second.AsRegister<Register>(), TIMES_1, 0));
@@ -2963,14 +2963,12 @@
locations->SetOut(Location::RegisterLocation(EAX));
InvokeRuntimeCallingConvention calling_convention;
locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
- locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+ locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
}
void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) {
InvokeRuntimeCallingConvention calling_convention;
- codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
__ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex()));
-
__ fs()->call(Address::Absolute(GetThreadOffset<kX86WordSize>(instruction->GetEntrypoint())));
codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
@@ -2983,13 +2981,12 @@
locations->SetOut(Location::RegisterLocation(EAX));
InvokeRuntimeCallingConvention calling_convention;
locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
- locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+ locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
}
void InstructionCodeGeneratorX86::VisitNewArray(HNewArray* instruction) {
InvokeRuntimeCallingConvention calling_convention;
- codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(2));
__ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex()));
__ fs()->call(Address::Absolute(GetThreadOffset<kX86WordSize>(instruction->GetEntrypoint())));
@@ -3010,8 +3007,17 @@
locations->SetOut(location);
}
-void InstructionCodeGeneratorX86::VisitParameterValue(HParameterValue* instruction) {
- UNUSED(instruction);
+void InstructionCodeGeneratorX86::VisitParameterValue(
+ HParameterValue* instruction ATTRIBUTE_UNUSED) {
+}
+
+void LocationsBuilderX86::VisitCurrentMethod(HCurrentMethod* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetOut(Location::RegisterLocation(kMethodRegisterArgument));
+}
+
+void InstructionCodeGeneratorX86::VisitCurrentMethod(HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
}
void LocationsBuilderX86::VisitNot(HNot* not_) {
@@ -3194,7 +3200,7 @@
void CodeGeneratorX86::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
- Register temp) {
+ Location temp) {
// TODO: Implement all kinds of calls:
// 1) boot -> boot
// 2) app -> boot
@@ -3204,40 +3210,57 @@
if (invoke->IsStringInit()) {
// temp = thread->string_init_entrypoint
- __ fs()->movl(temp, Address::Absolute(invoke->GetStringInitOffset()));
+ Register reg = temp.AsRegister<Register>();
+ __ fs()->movl(reg, Address::Absolute(invoke->GetStringInitOffset()));
// (temp + offset_of_quick_compiled_code)()
__ call(Address(
- temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
+ reg, ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
+ } else if (invoke->IsRecursive()) {
+ __ call(GetFrameEntryLabel());
} else {
- // temp = method;
- LoadCurrentMethod(temp);
- if (!invoke->IsRecursive()) {
- // temp = temp->dex_cache_resolved_methods_;
- __ movl(temp, Address(temp, ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
- // temp = temp[index_in_cache]
- __ movl(temp, Address(temp,
- CodeGenerator::GetCachePointerOffset(invoke->GetDexMethodIndex())));
- // (temp + offset_of_quick_compiled_code)()
- __ call(Address(temp,
- ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
+ Location current_method = invoke->GetLocations()->InAt(invoke->GetCurrentMethodInputIndex());
+
+ Register method_reg;
+ Register reg = temp.AsRegister<Register>();
+ if (current_method.IsRegister()) {
+ method_reg = current_method.AsRegister<Register>();
} else {
- __ call(GetFrameEntryLabel());
+ DCHECK(IsBaseline() || invoke->GetLocations()->Intrinsified());
+ DCHECK(!current_method.IsValid());
+ method_reg = reg;
+ __ movl(reg, Address(ESP, kCurrentMethodStackOffset));
}
+ // temp = temp->dex_cache_resolved_methods_;
+ __ movl(reg, Address(method_reg, ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
+ // temp = temp[index_in_cache]
+ __ movl(reg, Address(reg,
+ CodeGenerator::GetCachePointerOffset(invoke->GetDexMethodIndex())));
+ // (temp + offset_of_quick_compiled_code)()
+ __ call(Address(reg,
+ ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value()));
}
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) {
@@ -3382,7 +3405,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();
@@ -3455,7 +3479,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) {
@@ -3476,7 +3500,7 @@
}
void InstructionCodeGeneratorX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
@@ -3484,7 +3508,7 @@
}
void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
void LocationsBuilderX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -3818,7 +3842,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);
@@ -3900,7 +3925,6 @@
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitArrayLength(HArrayLength* instruction) {
@@ -4270,20 +4294,22 @@
: LocationSummary::kNoCall;
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+ locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
}
void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) {
- Register out = cls->GetLocations()->Out().AsRegister<Register>();
+ LocationSummary* locations = cls->GetLocations();
+ Register out = locations->Out().AsRegister<Register>();
+ Register current_method = locations->InAt(0).AsRegister<Register>();
if (cls->IsReferrersClass()) {
DCHECK(!cls->CanCallRuntime());
DCHECK(!cls->MustGenerateClinitCheck());
- codegen_->LoadCurrentMethod(out);
- __ movl(out, Address(out, ArtMethod::DeclaringClassOffset().Int32Value()));
+ __ movl(out, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
} else {
DCHECK(cls->CanCallRuntime());
- codegen_->LoadCurrentMethod(out);
- __ movl(out, Address(out, ArtMethod::DexCacheResolvedTypesOffset().Int32Value()));
+ __ movl(out, Address(
+ current_method, ArtMethod::DexCacheResolvedTypesOffset().Int32Value()));
__ movl(out, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathX86(
@@ -4329,6 +4355,7 @@
void LocationsBuilderX86::VisitLoadString(HLoadString* load) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+ locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
}
@@ -4336,9 +4363,10 @@
SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86(load);
codegen_->AddSlowPath(slow_path);
- Register out = load->GetLocations()->Out().AsRegister<Register>();
- codegen_->LoadCurrentMethod(out);
- __ movl(out, Address(out, ArtMethod::DeclaringClassOffset().Int32Value()));
+ LocationSummary* locations = load->GetLocations();
+ Register out = locations->Out().AsRegister<Register>();
+ Register current_method = locations->InAt(0).AsRegister<Register>();
+ __ movl(out, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
__ movl(out, Address(out, mirror::Class::DexCacheStringsOffset().Int32Value()));
__ movl(out, Address(out, CodeGenerator::GetCacheOffset(load->GetStringIndex())));
__ testl(out, out);
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 43214fe..696d8d5 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -83,6 +83,8 @@
virtual ~InvokeDexCallingConventionVisitorX86() {}
Location GetNextLocation(Primitive::Type type) OVERRIDE;
+ Location GetReturnLocation(Primitive::Type type) const OVERRIDE;
+ Location GetMethodLocation() const OVERRIDE;
private:
InvokeDexCallingConvention calling_convention;
@@ -122,10 +124,16 @@
#define DECLARE_VISIT_INSTRUCTION(name, super) \
void Visit##name(H##name* instr) OVERRIDE;
- FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_X86(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
+ void VisitInstruction(HInstruction* instruction) OVERRIDE {
+ LOG(FATAL) << "Unreachable instruction " << instruction->DebugName()
+ << " (id " << instruction->GetId() << ")";
+ }
+
private:
void HandleBitwiseOperation(HBinaryOperation* instruction);
void HandleInvoke(HInvoke* invoke);
@@ -146,10 +154,16 @@
#define DECLARE_VISIT_INSTRUCTION(name, super) \
void Visit##name(H##name* instr) OVERRIDE;
- FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_X86(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
+ void VisitInstruction(HInstruction* instruction) OVERRIDE {
+ LOG(FATAL) << "Unreachable instruction " << instruction->DebugName()
+ << " (id " << instruction->GetId() << ")";
+ }
+
X86Assembler* GetAssembler() const { return assembler_; }
private:
@@ -172,7 +186,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.
@@ -259,12 +275,14 @@
void Move64(Location destination, Location source);
// Generate a call to a static or direct method.
- void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Register temp);
+ void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
// Emit a write barrier.
- void MarkGCCard(Register temp, Register card, Register object, Register value);
-
- void LoadCurrentMethod(Register reg);
+ void MarkGCCard(Register temp,
+ Register card,
+ Register object,
+ Register value,
+ bool value_can_be_null);
Label* GetLabelOf(HBasicBlock* block) const {
return CommonGetLabelOf<Label>(block_labels_.GetRawStorage(), block);
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index b0174b9..bfc827d 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -39,14 +39,14 @@
static constexpr Register TMP = R11;
static constexpr int kCurrentMethodStackOffset = 0;
+static constexpr Register kMethodRegisterArgument = RDI;
static constexpr Register kCoreCalleeSaves[] = { RBX, RBP, R12, R13, R14, R15 };
static constexpr FloatRegister kFpuCalleeSaves[] = { XMM12, XMM13, XMM14, XMM15 };
static constexpr int kC2ConditionMask = 0x400;
-
-#define __ reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler())->
+#define __ down_cast<X86_64Assembler*>(codegen->GetAssembler())->
class NullCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
public:
@@ -343,7 +343,7 @@
};
#undef __
-#define __ reinterpret_cast<X86_64Assembler*>(GetAssembler())->
+#define __ down_cast<X86_64Assembler*>(GetAssembler())->
inline Condition X86_64Condition(IfCondition cond) {
switch (cond) {
@@ -360,7 +360,7 @@
}
void CodeGeneratorX86_64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
- CpuRegister temp) {
+ Location temp) {
// All registers are assumed to be correctly set up.
// TODO: Implement all kinds of calls:
@@ -371,37 +371,46 @@
// Currently we implement the app -> app logic, which looks up in the resolve cache.
if (invoke->IsStringInit()) {
+ CpuRegister reg = temp.AsRegister<CpuRegister>();
// temp = thread->string_init_entrypoint
- __ gs()->movl(temp, Address::Absolute(invoke->GetStringInitOffset()));
+ __ gs()->movl(reg, Address::Absolute(invoke->GetStringInitOffset()));
// (temp + offset_of_quick_compiled_code)()
- __ call(Address(temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ __ call(Address(reg, ArtMethod::EntryPointFromQuickCompiledCodeOffset(
kX86_64WordSize).SizeValue()));
+ } else if (invoke->IsRecursive()) {
+ __ call(&frame_entry_label_);
} else {
- // temp = method;
- LoadCurrentMethod(temp);
- if (!invoke->IsRecursive()) {
- // temp = temp->dex_cache_resolved_methods_;
- __ movl(temp, Address(temp, ArtMethod::DexCacheResolvedMethodsOffset().SizeValue()));
- // temp = temp[index_in_cache]
- __ movq(temp, Address(
- temp, CodeGenerator::GetCachePointerOffset(invoke->GetDexMethodIndex())));
- // (temp + offset_of_quick_compiled_code)()
- __ call(Address(temp, ArtMethod::EntryPointFromQuickCompiledCodeOffset(
- kX86_64WordSize).SizeValue()));
+ CpuRegister reg = temp.AsRegister<CpuRegister>();
+ Location current_method = invoke->GetLocations()->InAt(invoke->GetCurrentMethodInputIndex());
+ Register method_reg;
+ if (current_method.IsRegister()) {
+ method_reg = current_method.AsRegister<Register>();
} else {
- __ call(&frame_entry_label_);
+ DCHECK(invoke->GetLocations()->Intrinsified());
+ DCHECK(!current_method.IsValid());
+ method_reg = reg.AsRegister();
+ __ movq(reg, Address(CpuRegister(RSP), kCurrentMethodStackOffset));
}
+ // temp = temp->dex_cache_resolved_methods_;
+ __ movl(reg, Address(CpuRegister(method_reg),
+ ArtMethod::DexCacheResolvedMethodsOffset().SizeValue()));
+ // temp = temp[index_in_cache]
+ __ movq(reg, Address(
+ reg, CodeGenerator::GetCachePointerOffset(invoke->GetDexMethodIndex())));
+ // (temp + offset_of_quick_compiled_code)()
+ __ call(Address(reg, ArtMethod::EntryPointFromQuickCompiledCodeOffset(
+ kX86_64WordSize).SizeValue()));
}
DCHECK(!IsLeafMethod());
}
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) {
@@ -546,7 +555,8 @@
}
}
- __ movq(Address(CpuRegister(RSP), kCurrentMethodStackOffset), CpuRegister(RDI));
+ __ movq(Address(CpuRegister(RSP), kCurrentMethodStackOffset),
+ CpuRegister(kMethodRegisterArgument));
}
void CodeGeneratorX86_64::GenerateFrameExit() {
@@ -584,11 +594,6 @@
__ Bind(GetLabelOf(block));
}
-void CodeGeneratorX86_64::LoadCurrentMethod(CpuRegister reg) {
- DCHECK(RequiresCurrentMethod());
- __ movq(reg, Address(CpuRegister(RSP), kCurrentMethodStackOffset));
-}
-
Location CodeGeneratorX86_64::GetStackLocation(HLoadLocal* load) const {
switch (load->GetType()) {
case Primitive::kPrimLong:
@@ -690,11 +695,11 @@
Location location,
HInstruction* move_for) {
LocationSummary* locations = instruction->GetLocations();
- if (locations != nullptr && locations->Out().Equals(location)) {
+ if (instruction->IsCurrentMethod()) {
+ Move(location, Location::DoubleStackSlot(kCurrentMethodStackOffset));
+ } else if (locations != nullptr && locations->Out().Equals(location)) {
return;
- }
-
- if (locations != nullptr && locations->Out().IsConstant()) {
+ } else if (locations != nullptr && locations->Out().IsConstant()) {
HConstant* const_to_move = locations->Out().GetConstant();
if (const_to_move->IsIntConstant() || const_to_move->IsNullConstant()) {
Immediate imm(GetInt32ValueOf(const_to_move));
@@ -945,19 +950,19 @@
UNUSED(store);
}
-void LocationsBuilderX86_64::VisitCondition(HCondition* comp) {
+void LocationsBuilderX86_64::VisitCondition(HCondition* cond) {
LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall);
+ new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::Any());
- if (comp->NeedsMaterialization()) {
+ if (cond->NeedsMaterialization()) {
locations->SetOut(Location::RequiresRegister());
}
}
-void InstructionCodeGeneratorX86_64::VisitCondition(HCondition* comp) {
- if (comp->NeedsMaterialization()) {
- LocationSummary* locations = comp->GetLocations();
+void InstructionCodeGeneratorX86_64::VisitCondition(HCondition* cond) {
+ if (cond->NeedsMaterialization()) {
+ LocationSummary* locations = cond->GetLocations();
CpuRegister reg = locations->Out().AsRegister<CpuRegister>();
// Clear register: setcc only sets the low byte.
__ xorl(reg, reg);
@@ -975,7 +980,7 @@
} else {
__ cmpl(lhs.AsRegister<CpuRegister>(), Address(CpuRegister(RSP), rhs.GetStackIndex()));
}
- __ setcc(X86_64Condition(comp->GetCondition()), reg);
+ __ setcc(X86_64Condition(cond->GetCondition()), reg);
}
}
@@ -1244,6 +1249,32 @@
codegen_->GenerateFrameExit();
}
+Location InvokeDexCallingConventionVisitorX86_64::GetReturnLocation(Primitive::Type type) const {
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot:
+ case Primitive::kPrimLong:
+ return Location::RegisterLocation(RAX);
+
+ case Primitive::kPrimVoid:
+ return Location::NoLocation();
+
+ case Primitive::kPrimDouble:
+ case Primitive::kPrimFloat:
+ return Location::FpuRegisterLocation(XMM0);
+ }
+
+ UNREACHABLE();
+}
+
+Location InvokeDexCallingConventionVisitorX86_64::GetMethodLocation() const {
+ return Location::RegisterLocation(kMethodRegisterArgument);
+}
+
Location InvokeDexCallingConventionVisitorX86_64::GetNextLocation(Primitive::Type type) {
switch (type) {
case Primitive::kPrimBoolean:
@@ -1331,42 +1362,15 @@
return;
}
+ LocationSummary* locations = invoke->GetLocations();
codegen_->GenerateStaticOrDirectCall(
- invoke,
- invoke->GetLocations()->GetTemp(0).AsRegister<CpuRegister>());
+ invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
void LocationsBuilderX86_64::HandleInvoke(HInvoke* invoke) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
- locations->AddTemp(Location::RegisterLocation(RDI));
-
InvokeDexCallingConventionVisitorX86_64 calling_convention_visitor;
- for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) {
- HInstruction* input = invoke->InputAt(i);
- locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType()));
- }
-
- switch (invoke->GetType()) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimInt:
- case Primitive::kPrimNot:
- case Primitive::kPrimLong:
- locations->SetOut(Location::RegisterLocation(RAX));
- break;
-
- case Primitive::kPrimVoid:
- break;
-
- case Primitive::kPrimDouble:
- case Primitive::kPrimFloat:
- locations->SetOut(Location::FpuRegisterLocation(XMM0));
- break;
- }
+ CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
}
void LocationsBuilderX86_64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
@@ -1390,12 +1394,8 @@
Location receiver = locations->InAt(0);
size_t class_offset = mirror::Object::ClassOffset().SizeValue();
// temp = object->GetClass();
- if (receiver.IsStackSlot()) {
- __ movl(temp, Address(CpuRegister(RSP), receiver.GetStackIndex()));
- __ movl(temp, Address(temp, class_offset));
- } else {
- __ movl(temp, Address(receiver.AsRegister<CpuRegister>(), class_offset));
- }
+ DCHECK(receiver.IsRegister());
+ __ movl(temp, Address(receiver.AsRegister<CpuRegister>(), class_offset));
codegen_->MaybeRecordImplicitNullCheck(invoke);
// temp = temp->GetMethodAt(method_offset);
__ movq(temp, Address(temp, method_offset));
@@ -2118,6 +2118,8 @@
if (second.IsRegister()) {
if (out.AsRegister<Register>() == first.AsRegister<Register>()) {
__ addl(out.AsRegister<CpuRegister>(), second.AsRegister<CpuRegister>());
+ } else if (out.AsRegister<Register>() == second.AsRegister<Register>()) {
+ __ addl(out.AsRegister<CpuRegister>(), first.AsRegister<CpuRegister>());
} else {
__ leal(out.AsRegister<CpuRegister>(), Address(
first.AsRegister<CpuRegister>(), second.AsRegister<CpuRegister>(), TIMES_1, 0));
@@ -2141,6 +2143,8 @@
if (second.IsRegister()) {
if (out.AsRegister<Register>() == first.AsRegister<Register>()) {
__ addq(out.AsRegister<CpuRegister>(), second.AsRegister<CpuRegister>());
+ } else if (out.AsRegister<Register>() == second.AsRegister<Register>()) {
+ __ addq(out.AsRegister<CpuRegister>(), first.AsRegister<CpuRegister>());
} else {
__ leaq(out.AsRegister<CpuRegister>(), Address(
first.AsRegister<CpuRegister>(), second.AsRegister<CpuRegister>(), TIMES_1, 0));
@@ -3016,13 +3020,12 @@
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
InvokeRuntimeCallingConvention calling_convention;
locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
- locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+ locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
locations->SetOut(Location::RegisterLocation(RAX));
}
void InstructionCodeGeneratorX86_64::VisitNewInstance(HNewInstance* instruction) {
InvokeRuntimeCallingConvention calling_convention;
- codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1)));
codegen_->Load64BitValue(CpuRegister(calling_convention.GetRegisterAt(0)),
instruction->GetTypeIndex());
__ gs()->call(
@@ -3037,14 +3040,13 @@
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
InvokeRuntimeCallingConvention calling_convention;
locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
- locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
locations->SetOut(Location::RegisterLocation(RAX));
locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+ locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
}
void InstructionCodeGeneratorX86_64::VisitNewArray(HNewArray* instruction) {
InvokeRuntimeCallingConvention calling_convention;
- codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(2)));
codegen_->Load64BitValue(CpuRegister(calling_convention.GetRegisterAt(0)),
instruction->GetTypeIndex());
@@ -3067,9 +3069,20 @@
locations->SetOut(location);
}
-void InstructionCodeGeneratorX86_64::VisitParameterValue(HParameterValue* instruction) {
+void InstructionCodeGeneratorX86_64::VisitParameterValue(
+ HParameterValue* instruction ATTRIBUTE_UNUSED) {
// Nothing to do, the parameter is already at its location.
- UNUSED(instruction);
+}
+
+void LocationsBuilderX86_64::VisitCurrentMethod(HCurrentMethod* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetOut(Location::RegisterLocation(kMethodRegisterArgument));
+}
+
+void InstructionCodeGeneratorX86_64::VisitCurrentMethod(
+ HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
+ // Nothing to do, the method is already at its location.
}
void LocationsBuilderX86_64::VisitNot(HNot* not_) {
@@ -3250,7 +3263,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();
@@ -3330,7 +3344,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) {
@@ -3343,7 +3357,7 @@
}
void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
void LocationsBuilderX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -3367,7 +3381,7 @@
}
void InstructionCodeGeneratorX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) {
@@ -3672,7 +3686,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);
@@ -3817,16 +3832,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) {
@@ -4117,20 +4137,22 @@
: LocationSummary::kNoCall;
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+ locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
}
void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) {
- CpuRegister out = cls->GetLocations()->Out().AsRegister<CpuRegister>();
+ LocationSummary* locations = cls->GetLocations();
+ CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+ CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>();
if (cls->IsReferrersClass()) {
DCHECK(!cls->CanCallRuntime());
DCHECK(!cls->MustGenerateClinitCheck());
- codegen_->LoadCurrentMethod(out);
- __ movl(out, Address(out, ArtMethod::DeclaringClassOffset().Int32Value()));
+ __ movl(out, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
} else {
DCHECK(cls->CanCallRuntime());
- codegen_->LoadCurrentMethod(out);
- __ movl(out, Address(out, ArtMethod::DexCacheResolvedTypesOffset().Int32Value()));
+ __ movl(out, Address(
+ current_method, ArtMethod::DexCacheResolvedTypesOffset().Int32Value()));
__ movl(out, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathX86_64(
cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
@@ -4166,6 +4188,7 @@
void LocationsBuilderX86_64::VisitLoadString(HLoadString* load) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+ locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
}
@@ -4173,9 +4196,10 @@
SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86_64(load);
codegen_->AddSlowPath(slow_path);
- CpuRegister out = load->GetLocations()->Out().AsRegister<CpuRegister>();
- codegen_->LoadCurrentMethod(CpuRegister(out));
- __ movl(out, Address(out, ArtMethod::DeclaringClassOffset().Int32Value()));
+ LocationSummary* locations = load->GetLocations();
+ CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+ CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>();
+ __ movl(out, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
__ movl(out, Address(out, mirror::Class::DexCacheStringsOffset().Int32Value()));
__ movl(out, Address(out, CodeGenerator::GetCacheOffset(load->GetStringIndex())));
__ testl(out, out);
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 4be401a..215754c 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -76,6 +76,8 @@
virtual ~InvokeDexCallingConventionVisitorX86_64() {}
Location GetNextLocation(Primitive::Type type) OVERRIDE;
+ Location GetReturnLocation(Primitive::Type type) const OVERRIDE;
+ Location GetMethodLocation() const OVERRIDE;
private:
InvokeDexCallingConvention calling_convention;
@@ -132,10 +134,16 @@
#define DECLARE_VISIT_INSTRUCTION(name, super) \
void Visit##name(H##name* instr) OVERRIDE;
- FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_X86_64(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
+ void VisitInstruction(HInstruction* instruction) OVERRIDE {
+ LOG(FATAL) << "Unreachable instruction " << instruction->DebugName()
+ << " (id " << instruction->GetId() << ")";
+ }
+
private:
void HandleInvoke(HInvoke* invoke);
void HandleBitwiseOperation(HBinaryOperation* operation);
@@ -156,10 +164,16 @@
#define DECLARE_VISIT_INSTRUCTION(name, super) \
void Visit##name(H##name* instr) OVERRIDE;
- FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_X86_64(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
+ void VisitInstruction(HInstruction* instruction) OVERRIDE {
+ LOG(FATAL) << "Unreachable instruction " << instruction->DebugName()
+ << " (id " << instruction->GetId() << ")";
+ }
+
X86_64Assembler* GetAssembler() const { return assembler_; }
private:
@@ -176,7 +190,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);
@@ -250,13 +266,15 @@
}
// 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);
- void LoadCurrentMethod(CpuRegister reg);
-
Label* GetLabelOf(HBasicBlock* block) const {
return CommonGetLabelOf<Label>(block_labels_.GetRawStorage(), block);
}
@@ -269,7 +287,7 @@
return false;
}
- void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, CpuRegister temp);
+ void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
const X86_64InstructionSetFeatures& GetInstructionSetFeatures() const {
return isa_features_;
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..17a006c 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 {
@@ -121,7 +122,12 @@
if (!inst->HasSideEffects()
&& !inst->CanThrow()
&& !inst->IsSuspendCheck()
- && !inst->IsMemoryBarrier() // If we added an explicit barrier then we should keep it.
+ // The current method needs to stay in the graph in case of inlining.
+ // It is always passed anyway, and keeping it in the graph does not
+ // affect the generated code.
+ && !inst->IsCurrentMethod()
+ // If we added an explicit barrier then we should keep it.
+ && !inst->IsMemoryBarrier()
&& !inst->HasUses()) {
block->RemoveInstruction(inst);
MaybeRecordStat(MethodCompilationStat::kRemovedDeadInstruction);
@@ -132,6 +138,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..fd2e4e8 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,106 @@
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;
+ }
+
+ void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE {
+ StartAttributeStream("dex_file_index") << invoke->GetDexMethodIndex();
+ StartAttributeStream("recursive") << std::boolalpha
+ << invoke->IsRecursive()
+ << std::noboolalpha;
}
bool IsPass(const char* name) {
@@ -203,65 +293,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 +372,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/gvn_test.cc b/compiler/optimizing/gvn_test.cc
index c3ce7e1..d8a09ff 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -40,23 +40,40 @@
graph->AddBlock(block);
entry->AddSuccessor(block);
- block->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
- MemberOffset(42), false));
- block->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
- MemberOffset(42), false));
+ block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ Primitive::kPrimNot,
+ MemberOffset(42),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile()));
+ block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ Primitive::kPrimNot,
+ MemberOffset(42),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile()));
HInstruction* to_remove = block->GetLastInstruction();
- block->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
- MemberOffset(43), false));
+ block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ Primitive::kPrimNot,
+ MemberOffset(43),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile()));
HInstruction* different_offset = block->GetLastInstruction();
// Kill the value.
- block->AddInstruction(new (&allocator) HInstanceFieldSet(
- parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false));
- block->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot,
- MemberOffset(42), false));
+ block->AddInstruction(new (&allocator) HInstanceFieldSet(parameter,
+ parameter,
+ Primitive::kPrimNot,
+ MemberOffset(42),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile()));
+ block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ Primitive::kPrimNot,
+ MemberOffset(42),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile()));
HInstruction* use_after_kill = block->GetLastInstruction();
block->AddInstruction(new (&allocator) HExit());
@@ -88,9 +105,12 @@
HBasicBlock* block = new (&allocator) HBasicBlock(graph);
graph->AddBlock(block);
entry->AddSuccessor(block);
- block->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
- MemberOffset(42), false));
+ block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ Primitive::kPrimBoolean,
+ MemberOffset(42),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile()));
block->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
HBasicBlock* then = new (&allocator) HBasicBlock(graph);
@@ -105,17 +125,26 @@
then->AddSuccessor(join);
else_->AddSuccessor(join);
- then->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
- MemberOffset(42), false));
+ then->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ Primitive::kPrimBoolean,
+ MemberOffset(42),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile()));
then->AddInstruction(new (&allocator) HGoto());
- else_->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
- MemberOffset(42), false));
+ else_->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ Primitive::kPrimBoolean,
+ MemberOffset(42),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile()));
else_->AddInstruction(new (&allocator) HGoto());
- join->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
- MemberOffset(42), false));
+ join->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ Primitive::kPrimBoolean,
+ MemberOffset(42),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile()));
join->AddInstruction(new (&allocator) HExit());
graph->TryBuildingSsa();
@@ -144,9 +173,12 @@
HBasicBlock* block = new (&allocator) HBasicBlock(graph);
graph->AddBlock(block);
entry->AddSuccessor(block);
- block->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
- MemberOffset(42), false));
+ block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ Primitive::kPrimBoolean,
+ MemberOffset(42),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile()));
block->AddInstruction(new (&allocator) HGoto());
HBasicBlock* loop_header = new (&allocator) HBasicBlock(graph);
@@ -161,26 +193,40 @@
loop_header->AddSuccessor(exit);
loop_body->AddSuccessor(loop_header);
- loop_header->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
- MemberOffset(42), false));
+ loop_header->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ Primitive::kPrimBoolean,
+ MemberOffset(42),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile()));
HInstruction* field_get_in_loop_header = loop_header->GetLastInstruction();
loop_header->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
// Kill inside the loop body to prevent field gets inside the loop header
// and the body to be GVN'ed.
- loop_body->AddInstruction(new (&allocator) HInstanceFieldSet(
- parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false));
+ loop_body->AddInstruction(new (&allocator) HInstanceFieldSet(parameter,
+ parameter,
+ Primitive::kPrimNot,
+ MemberOffset(42),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile()));
HInstruction* field_set = loop_body->GetLastInstruction();
- loop_body->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
- MemberOffset(42), false));
+ loop_body->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ Primitive::kPrimBoolean,
+ MemberOffset(42),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile()));
HInstruction* field_get_in_loop_body = loop_body->GetLastInstruction();
loop_body->AddInstruction(new (&allocator) HGoto());
- exit->AddInstruction(
- new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean,
- MemberOffset(42), false));
+ exit->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
+ Primitive::kPrimBoolean,
+ MemberOffset(42),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile()));
HInstruction* field_get_in_exit = exit->GetLastInstruction();
exit->AddInstruction(new (&allocator) HExit());
@@ -266,8 +312,13 @@
// Check that the loops don't have side effects.
{
// Make one block with a side effect.
- entry->AddInstruction(new (&allocator) HInstanceFieldSet(
- parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false));
+ entry->AddInstruction(new (&allocator) HInstanceFieldSet(parameter,
+ parameter,
+ Primitive::kPrimNot,
+ MemberOffset(42),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile()));
SideEffectsAnalysis side_effects(graph);
side_effects.Run();
@@ -280,8 +331,13 @@
// Check that the side effects of the outer loop does not affect the inner loop.
{
outer_loop_body->InsertInstructionBefore(
- new (&allocator) HInstanceFieldSet(
- parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false),
+ new (&allocator) HInstanceFieldSet(parameter,
+ parameter,
+ Primitive::kPrimNot,
+ MemberOffset(42),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile()),
outer_loop_body->GetLastInstruction());
SideEffectsAnalysis side_effects(graph);
@@ -297,8 +353,13 @@
{
outer_loop_body->RemoveInstruction(outer_loop_body->GetFirstInstruction());
inner_loop_body->InsertInstructionBefore(
- new (&allocator) HInstanceFieldSet(
- parameter, parameter, Primitive::kPrimNot, MemberOffset(42), false),
+ new (&allocator) HInstanceFieldSet(parameter,
+ parameter,
+ Primitive::kPrimNot,
+ MemberOffset(42),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile()),
inner_loop_body->GetLastInstruction());
SideEffectsAnalysis side_effects(graph);
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index e517323..5aeaad2 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -27,6 +27,7 @@
#include "mirror/class_loader.h"
#include "mirror/dex_cache.h"
#include "nodes.h"
+#include "reference_type_propagation.h"
#include "register_allocator.h"
#include "ssa_phi_elimination.h"
#include "scoped_thread_state_change.h"
@@ -36,8 +37,8 @@
namespace art {
-static constexpr int kMaxInlineCodeUnits = 100;
-static constexpr int kDepthLimit = 5;
+static constexpr int kMaxInlineCodeUnits = 18;
+static constexpr int kDepthLimit = 3;
void HInliner::Run() {
if (graph_->IsDebuggable()) {
@@ -46,22 +47,36 @@
return;
}
const GrowableArray<HBasicBlock*>& blocks = graph_->GetReversePostOrder();
+ HBasicBlock* next_block = blocks.Get(0);
for (size_t i = 0; i < blocks.Size(); ++i) {
- HBasicBlock* block = blocks.Get(i);
+ // Because we are changing the graph when inlining, we need to remember the next block.
+ // This avoids doing the inlining work again on the inlined blocks.
+ if (blocks.Get(i) != next_block) {
+ continue;
+ }
+ HBasicBlock* block = next_block;
+ next_block = (i == blocks.Size() - 1) ? nullptr : blocks.Get(i + 1);
for (HInstruction* instruction = block->GetFirstInstruction(); instruction != nullptr;) {
HInstruction* next = instruction->GetNext();
- HInvokeStaticOrDirect* call = instruction->AsInvokeStaticOrDirect();
+ HInvoke* call = instruction->AsInvoke();
// As long as the call is not intrinsified, it is worth trying to inline.
if (call != nullptr && call->GetIntrinsic() == Intrinsics::kNone) {
// We use the original invoke type to ensure the resolution of the called method
// works properly.
- if (!TryInline(call, call->GetDexMethodIndex(), call->GetOriginalInvokeType())) {
+ if (!TryInline(call, call->GetDexMethodIndex())) {
if (kIsDebugBuild) {
std::string callee_name =
PrettyMethod(call->GetDexMethodIndex(), *outer_compilation_unit_.GetDexFile());
bool should_inline = callee_name.find("$inline$") != std::string::npos;
CHECK(!should_inline) << "Could not inline " << callee_name;
}
+ } else {
+ if (kIsDebugBuild) {
+ std::string callee_name =
+ PrettyMethod(call->GetDexMethodIndex(), *outer_compilation_unit_.GetDexFile());
+ bool must_not_inline = callee_name.find("$noinline$") != std::string::npos;
+ CHECK(!must_not_inline) << "Should not have inlined " << callee_name;
+ }
}
}
instruction = next;
@@ -69,30 +84,132 @@
}
}
-bool HInliner::TryInline(HInvoke* invoke_instruction,
- uint32_t method_index,
- InvokeType invoke_type) const {
+static bool IsMethodOrDeclaringClassFinal(ArtMethod* method)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return method->IsFinal() || method->GetDeclaringClass()->IsFinal();
+}
+
+/**
+ * Given the `resolved_method` looked up in the dex cache, try to find
+ * the actual runtime target of an interface or virtual call.
+ * Return nullptr if the runtime target cannot be proven.
+ */
+static ArtMethod* FindVirtualOrInterfaceTarget(HInvoke* invoke, ArtMethod* resolved_method)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (IsMethodOrDeclaringClassFinal(resolved_method)) {
+ // No need to lookup further, the resolved method will be the target.
+ return resolved_method;
+ }
+
+ HInstruction* receiver = invoke->InputAt(0);
+ if (receiver->IsNullCheck()) {
+ // Due to multiple levels of inlining within the same pass, it might be that
+ // null check does not have the reference type of the actual receiver.
+ receiver = receiver->InputAt(0);
+ }
+ ReferenceTypeInfo info = receiver->GetReferenceTypeInfo();
+ if (info.IsTop()) {
+ // We have no information on the receiver.
+ return nullptr;
+ } else if (!info.IsExact()) {
+ // We currently only support inlining with known receivers.
+ // TODO: Remove this check, we should be able to inline final methods
+ // on unknown receivers.
+ return nullptr;
+ } else if (info.GetTypeHandle()->IsInterface()) {
+ // Statically knowing that the receiver has an interface type cannot
+ // help us find what is the target method.
+ return nullptr;
+ } else if (!resolved_method->GetDeclaringClass()->IsAssignableFrom(info.GetTypeHandle().Get())) {
+ // The method that we're trying to call is not in the receiver's class or super classes.
+ return nullptr;
+ }
+
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ size_t pointer_size = cl->GetImagePointerSize();
+ if (invoke->IsInvokeInterface()) {
+ resolved_method = info.GetTypeHandle()->FindVirtualMethodForInterface(
+ resolved_method, pointer_size);
+ } else {
+ DCHECK(invoke->IsInvokeVirtual());
+ resolved_method = info.GetTypeHandle()->FindVirtualMethodForVirtual(
+ resolved_method, pointer_size);
+ }
+
+ if (resolved_method == nullptr) {
+ // The information we had on the receiver was not enough to find
+ // the target method. Since we check above the exact type of the receiver,
+ // the only reason this can happen is an IncompatibleClassChangeError.
+ return nullptr;
+ } else if (resolved_method->IsAbstract()) {
+ // The information we had on the receiver was not enough to find
+ // the target method. Since we check above the exact type of the receiver,
+ // the only reason this can happen is an IncompatibleClassChangeError.
+ return nullptr;
+ } else if (IsMethodOrDeclaringClassFinal(resolved_method)) {
+ // A final method has to be the target method.
+ return resolved_method;
+ } else if (info.IsExact()) {
+ // If we found a method and the receiver's concrete type is statically
+ // known, we know for sure the target.
+ return resolved_method;
+ } else {
+ // Even if we did find a method, the receiver type was not enough to
+ // statically find the runtime target.
+ return nullptr;
+ }
+}
+
+static uint32_t FindMethodIndexIn(ArtMethod* method,
+ const DexFile& dex_file,
+ uint32_t referrer_index)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (method->GetDexFile()->GetLocation().compare(dex_file.GetLocation()) == 0) {
+ return method->GetDexMethodIndex();
+ } else {
+ return method->FindDexMethodIndexInOtherDexFile(dex_file, referrer_index);
+ }
+}
+
+bool HInliner::TryInline(HInvoke* invoke_instruction, uint32_t method_index) const {
ScopedObjectAccess soa(Thread::Current());
const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
VLOG(compiler) << "Try inlining " << PrettyMethod(method_index, caller_dex_file);
- StackHandleScope<3> hs(soa.Self());
- Handle<mirror::DexCache> dex_cache(
- hs.NewHandle(caller_compilation_unit_.GetClassLinker()->FindDexCache(caller_dex_file)));
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
- soa.Decode<mirror::ClassLoader*>(caller_compilation_unit_.GetClassLoader())));
- ArtMethod* resolved_method(compiler_driver_->ResolveMethod(
- soa, dex_cache, class_loader, &caller_compilation_unit_, method_index, invoke_type));
+ ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
+ // We can query the dex cache directly. The verifier has populated it already.
+ ArtMethod* resolved_method = class_linker->FindDexCache(caller_dex_file)->GetResolvedMethod(
+ method_index, class_linker->GetImagePointerSize());
if (resolved_method == nullptr) {
+ // Method cannot be resolved if it is in another dex file we do not have access to.
VLOG(compiler) << "Method cannot be resolved " << PrettyMethod(method_index, caller_dex_file);
return false;
}
- bool can_use_dex_cache = true;
+ if (!invoke_instruction->IsInvokeStaticOrDirect()) {
+ resolved_method = FindVirtualOrInterfaceTarget(invoke_instruction, resolved_method);
+ if (resolved_method == nullptr) {
+ VLOG(compiler) << "Interface or virtual call to "
+ << PrettyMethod(method_index, caller_dex_file)
+ << " could not be statically determined";
+ return false;
+ }
+ // We have found a method, but we need to find where that method is for the caller's
+ // dex file.
+ method_index = FindMethodIndexIn(resolved_method, caller_dex_file, method_index);
+ if (method_index == DexFile::kDexNoIndex) {
+ VLOG(compiler) << "Interface or virtual call to "
+ << PrettyMethod(resolved_method)
+ << " cannot be inlined because unaccessible to caller";
+ return false;
+ }
+ }
+
+ bool same_dex_file = true;
const DexFile& outer_dex_file = *outer_compilation_unit_.GetDexFile();
if (resolved_method->GetDexFile()->GetLocation().compare(outer_dex_file.GetLocation()) != 0) {
- can_use_dex_cache = false;
+ same_dex_file = false;
}
const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
@@ -139,7 +256,7 @@
return false;
}
- if (!TryBuildAndInline(resolved_method, invoke_instruction, method_index, can_use_dex_cache)) {
+ if (!TryBuildAndInline(resolved_method, invoke_instruction, method_index, same_dex_file)) {
return false;
}
@@ -151,7 +268,7 @@
bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
HInvoke* invoke_instruction,
uint32_t method_index,
- bool can_use_dex_cache) const {
+ bool same_dex_file) const {
ScopedObjectAccess soa(Thread::Current());
const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
@@ -167,11 +284,38 @@
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());
+ }
+ }
+
+ InvokeType invoke_type = invoke_instruction->GetOriginalInvokeType();
+ if (invoke_type == kInterface) {
+ // We have statically resolved the dispatch. To please the class linker
+ // at runtime, we change this call as if it was a virtual call.
+ invoke_type = kVirtual;
+ }
HGraph* callee_graph = new (graph_->GetArena()) HGraph(
graph_->GetArena(),
caller_dex_file,
method_index,
+ requires_ctor_barrier,
compiler_driver_->GetInstructionSet(),
+ invoke_type,
graph_->IsDebuggable(),
graph_->GetCurrentInstructionId());
@@ -210,11 +354,13 @@
// Run simple optimizations on the graph.
HDeadCodeElimination dce(callee_graph, stats_);
HConstantFolding fold(callee_graph);
+ ReferenceTypePropagation type_propagation(callee_graph, handles_);
InstructionSimplifier simplify(callee_graph, stats_);
HOptimization* optimizations[] = {
&dce,
&fold,
+ &type_propagation,
&simplify,
};
@@ -228,11 +374,37 @@
outer_compilation_unit_,
dex_compilation_unit,
compiler_driver_,
+ handles_,
stats_,
depth_ + 1);
inliner.Run();
}
+ // TODO: We should abort only if all predecessors throw. However,
+ // HGraph::InlineInto currently does not handle an exit block with
+ // a throw predecessor.
+ HBasicBlock* exit_block = callee_graph->GetExitBlock();
+ if (exit_block == nullptr) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
+ << " could not be inlined because it has an infinite loop";
+ resolved_method->SetShouldNotInline();
+ return false;
+ }
+
+ bool has_throw_predecessor = false;
+ for (size_t i = 0, e = exit_block->GetPredecessors().Size(); i < e; ++i) {
+ if (exit_block->GetPredecessors().Get(i)->GetLastInstruction()->IsThrow()) {
+ has_throw_predecessor = true;
+ break;
+ }
+ }
+ if (has_throw_predecessor) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
+ << " could not be inlined because one branch always throws";
+ resolved_method->SetShouldNotInline();
+ return false;
+ }
+
HReversePostOrderIterator it(*callee_graph);
it.Advance(); // Past the entry block, it does not contain instructions that prevent inlining.
for (; !it.Done(); it.Advance()) {
@@ -248,25 +420,24 @@
!instr_it.Done();
instr_it.Advance()) {
HInstruction* current = instr_it.Current();
- if (current->IsSuspendCheck()) {
- continue;
- }
- if (current->CanThrow()) {
+ if (current->IsInvokeInterface()) {
+ // Disable inlining of interface calls. The cost in case of entering the
+ // resolution conflict is currently too high.
VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
- << " could not be inlined because " << current->DebugName()
- << " can throw";
+ << " could not be inlined because it has an interface call.";
+ resolved_method->SetShouldNotInline();
return false;
}
- if (current->NeedsEnvironment()) {
+ if (!same_dex_file && current->NeedsEnvironment()) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
<< " could not be inlined because " << current->DebugName()
- << " needs an environment";
+ << " needs an environment and is in a different dex file";
return false;
}
- if (!can_use_dex_cache && current->NeedsDexCache()) {
+ if (!same_dex_file && current->NeedsDexCache()) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
<< " could not be inlined because " << current->DebugName()
<< " it is in a different dex file and requires access to the dex cache";
@@ -279,10 +450,6 @@
callee_graph->InlineInto(graph_, invoke_instruction);
- if (callee_graph->HasBoundsChecks()) {
- graph_->SetHasBoundsChecks(true);
- }
-
return true;
}
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 831bdf2..7465278 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -34,29 +34,32 @@
const DexCompilationUnit& outer_compilation_unit,
const DexCompilationUnit& caller_compilation_unit,
CompilerDriver* compiler_driver,
+ StackHandleScopeCollection* handles,
OptimizingCompilerStats* stats,
size_t depth = 0)
: HOptimization(outer_graph, true, kInlinerPassName, stats),
outer_compilation_unit_(outer_compilation_unit),
caller_compilation_unit_(caller_compilation_unit),
compiler_driver_(compiler_driver),
- depth_(depth) {}
+ depth_(depth),
+ handles_(handles) {}
void Run() OVERRIDE;
static constexpr const char* kInlinerPassName = "inliner";
private:
- bool TryInline(HInvoke* invoke_instruction, uint32_t method_index, InvokeType invoke_type) const;
+ bool TryInline(HInvoke* invoke_instruction, uint32_t method_index) const;
bool TryBuildAndInline(ArtMethod* resolved_method,
HInvoke* invoke_instruction,
uint32_t method_index,
- bool can_use_dex_cache) const;
+ bool same_dex_file) const;
const DexCompilationUnit& outer_compilation_unit_;
const DexCompilationUnit& caller_compilation_unit_;
CompilerDriver* const compiler_driver_;
const size_t depth_;
+ StackHandleScopeCollection* const handles_;
DISALLOW_COPY_AND_ASSIGN(HInliner);
};
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 46fad17..98a5841 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,33 +175,117 @@
}
}
-void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) {
- HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
- if (!check_cast->InputAt(0)->CanBeNull()) {
- check_cast->ClearMustDoNullCheck();
+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;
+}
- if (!load_class->IsResolved()) {
+// Returns whether doing a type test between the class of `object` against `klass` has
+// a statically known outcome. The result of the test is stored in `outcome`.
+static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bool* outcome) {
+ if (!klass->IsResolved()) {
// If the class couldn't be resolve it's not safe to compare against it. It's
// default type would be Top which might be wider that the actual class type
// and thus producing wrong results.
- return;
+ return false;
}
- ReferenceTypeInfo obj_rti = check_cast->InputAt(0)->GetReferenceTypeInfo();
- ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
+
+ ReferenceTypeInfo obj_rti = object->GetReferenceTypeInfo();
+ ReferenceTypeInfo class_rti = klass->GetLoadedClassRTI();
ScopedObjectAccess soa(Thread::Current());
if (class_rti.IsSupertypeOf(obj_rti)) {
+ *outcome = true;
+ return true;
+ } else if (obj_rti.IsExact()) {
+ // The test failed at compile time so will also fail at runtime.
+ *outcome = false;
+ return true;
+ } else if (!class_rti.IsInterface() && !obj_rti.IsSupertypeOf(class_rti)) {
+ // Different type hierarchy. The test will fail.
+ *outcome = false;
+ return true;
+ }
+ return false;
+}
+
+void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) {
+ HInstruction* object = check_cast->InputAt(0);
+ if (!object->CanBeNull() || IsDominatedByInputNullCheck(check_cast)) {
+ check_cast->ClearMustDoNullCheck();
+ }
+
+ if (object->IsNullConstant()) {
check_cast->GetBlock()->RemoveInstruction(check_cast);
if (stats_ != nullptr) {
stats_->RecordStat(MethodCompilationStat::kRemovedCheckedCast);
}
+ return;
+ }
+
+ bool outcome;
+ if (TypeCheckHasKnownOutcome(check_cast->InputAt(1)->AsLoadClass(), object, &outcome)) {
+ if (outcome) {
+ check_cast->GetBlock()->RemoveInstruction(check_cast);
+ if (stats_ != nullptr) {
+ stats_->RecordStat(MethodCompilationStat::kRemovedCheckedCast);
+ }
+ } else {
+ // Don't do anything for exceptional cases for now. Ideally we should remove
+ // all instructions and blocks this instruction dominates.
+ }
}
}
void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) {
- if (!instruction->InputAt(0)->CanBeNull()) {
+ HInstruction* object = instruction->InputAt(0);
+ bool can_be_null = true;
+ if (!object->CanBeNull() || IsDominatedByInputNullCheck(instruction)) {
+ can_be_null = false;
instruction->ClearMustDoNullCheck();
}
+
+ HGraph* graph = GetGraph();
+ if (object->IsNullConstant()) {
+ instruction->ReplaceWith(graph->GetIntConstant(0));
+ instruction->GetBlock()->RemoveInstruction(instruction);
+ RecordSimplification();
+ return;
+ }
+
+ bool outcome;
+ if (TypeCheckHasKnownOutcome(instruction->InputAt(1)->AsLoadClass(), object, &outcome)) {
+ if (outcome && can_be_null) {
+ // Type test will succeed, we just need a null test.
+ HNotEqual* test = new (graph->GetArena()) HNotEqual(graph->GetNullConstant(), object);
+ instruction->GetBlock()->InsertInstructionBefore(test, instruction);
+ instruction->ReplaceWith(test);
+ } else {
+ // We've statically determined the result of the instanceof.
+ instruction->ReplaceWith(graph->GetIntConstant(outcome));
+ }
+ RecordSimplification();
+ instruction->GetBlock()->RemoveInstruction(instruction);
+ }
+}
+
+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) {
@@ -294,6 +383,10 @@
instruction->ClearNeedsTypeCheck();
}
}
+
+ if (!value->CanBeNull()) {
+ instruction->ClearValueCanBeNull();
+ }
}
void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruction) {
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 9e18f11..8ef13e1 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -327,9 +327,6 @@
// TODO: Refactor DexFileMethodInliner and have something nicer than InlineMethod.
void IntrinsicsRecognizer::Run() {
- DexFileMethodInliner* inliner = driver_->GetMethodInlinerMap()->GetMethodInliner(dex_file_);
- DCHECK(inliner != nullptr);
-
for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
@@ -338,6 +335,9 @@
if (inst->IsInvoke()) {
HInvoke* invoke = inst->AsInvoke();
InlineMethod method;
+ DexFileMethodInliner* inliner =
+ driver_->GetMethodInlinerMap()->GetMethodInliner(&invoke->GetDexFile());
+ DCHECK(inliner != nullptr);
if (inliner->IsIntrinsic(invoke->GetDexMethodIndex(), &method)) {
Intrinsics intrinsic = GetIntrinsic(method);
@@ -345,7 +345,7 @@
if (!CheckInvokeType(intrinsic, invoke)) {
LOG(WARNING) << "Found an intrinsic with unexpected invoke type: "
<< intrinsic << " for "
- << PrettyMethod(invoke->GetDexMethodIndex(), *dex_file_);
+ << PrettyMethod(invoke->GetDexMethodIndex(), invoke->GetDexFile());
} else {
invoke->SetIntrinsic(intrinsic);
}
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index c243ef3..741fb64 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -30,16 +30,15 @@
// Recognize intrinsics from HInvoke nodes.
class IntrinsicsRecognizer : public HOptimization {
public:
- IntrinsicsRecognizer(HGraph* graph, const DexFile* dex_file, CompilerDriver* driver)
+ IntrinsicsRecognizer(HGraph* graph, CompilerDriver* driver)
: HOptimization(graph, true, kIntrinsicsRecognizerPassName),
- dex_file_(dex_file), driver_(driver) {}
+ driver_(driver) {}
void Run() OVERRIDE;
static constexpr const char* kIntrinsicsRecognizerPassName = "intrinsics_recognition";
private:
- const DexFile* dex_file_;
CompilerDriver* driver_;
DISALLOW_COPY_AND_ASSIGN(IntrinsicsRecognizer);
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index db35b8f..749bedf 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -101,7 +101,8 @@
MoveArguments(invoke_, codegen);
if (invoke_->IsInvokeStaticOrDirect()) {
- codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), kArtMethodRegister);
+ codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
+ Location::RegisterLocation(kArtMethodRegister));
RecordPcInfo(codegen, invoke_, invoke_->GetDexPc());
} else {
UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
@@ -657,7 +658,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 +727,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 957373f..c108ad5 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -110,7 +110,8 @@
MoveArguments(invoke_, codegen);
if (invoke_->IsInvokeStaticOrDirect()) {
- codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), kArtMethodRegister);
+ codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
+ LocationFrom(kArtMethodRegister));
RecordPcInfo(codegen, invoke_, invoke_->GetDexPc());
} else {
UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
@@ -797,7 +798,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 +858,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 989dd0d..424ac7c 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -138,7 +138,8 @@
MoveArguments(invoke_, codegen);
if (invoke_->IsInvokeStaticOrDirect()) {
- codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), EAX);
+ codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
+ Location::RegisterLocation(EAX));
RecordPcInfo(codegen, invoke_, invoke_->GetDexPc());
} else {
UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
@@ -732,7 +733,8 @@
MoveArguments(invoke, codegen);
DCHECK(invoke->IsInvokeStaticOrDirect());
- codegen->GenerateStaticOrDirectCall(invoke->AsInvokeStaticOrDirect(), EAX);
+ codegen->GenerateStaticOrDirectCall(invoke->AsInvokeStaticOrDirect(),
+ Location::RegisterLocation(EAX));
codegen->RecordPcInfo(invoke, invoke->GetDexPc());
// Copy the result back to the expected output.
@@ -1503,10 +1505,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 +1606,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 c245cb6..8915314 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -129,7 +129,8 @@
MoveArguments(invoke_, codegen);
if (invoke_->IsInvokeStaticOrDirect()) {
- codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), CpuRegister(RDI));
+ codegen->GenerateStaticOrDirectCall(
+ invoke_->AsInvokeStaticOrDirect(), Location::RegisterLocation(RDI));
RecordPcInfo(codegen, invoke_, invoke_->GetDexPc());
} else {
UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
@@ -609,7 +610,8 @@
MoveArguments(invoke, codegen);
DCHECK(invoke->IsInvokeStaticOrDirect());
- codegen->GenerateStaticOrDirectCall(invoke->AsInvokeStaticOrDirect(), CpuRegister(RDI));
+ codegen->GenerateStaticOrDirectCall(
+ invoke->AsInvokeStaticOrDirect(), Location::RegisterLocation(RDI));
codegen->RecordPcInfo(invoke, invoke->GetDexPc());
// Copy the result back to the expected output.
@@ -1374,10 +1376,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 +1463,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/locations.h b/compiler/optimizing/locations.h
index 09bbb33..f41a782 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -481,7 +481,6 @@
bool intrinsified = false);
void SetInAt(uint32_t at, Location location) {
- DCHECK(inputs_.Get(at).IsUnallocated() || inputs_.Get(at).IsInvalid());
inputs_.Put(at, location);
}
@@ -525,6 +524,8 @@
return temps_.Size();
}
+ bool HasTemps() const { return !temps_.IsEmpty(); }
+
Location Out() const { return output_; }
bool CanCall() const { return call_kind_ != kNoCall; }
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 2bad682..4baa05c 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 "base/bit_utils.h"
@@ -294,6 +295,20 @@
return cached_null_constant_;
}
+HCurrentMethod* HGraph::GetCurrentMethod() {
+ if (cached_current_method_ == nullptr) {
+ cached_current_method_ = new (arena_) HCurrentMethod(
+ Is64BitInstructionSet(instruction_set_) ? Primitive::kPrimLong : Primitive::kPrimInt);
+ if (entry_block_->GetFirstInstruction() == nullptr) {
+ entry_block_->AddInstruction(cached_current_method_);
+ } else {
+ entry_block_->InsertInstructionBefore(
+ cached_current_method_, entry_block_->GetFirstInstruction());
+ }
+ }
+ return cached_current_method_;
+}
+
HConstant* HGraph::GetConstant(Primitive::Type type, int64_t value) {
switch (type) {
case Primitive::Type::kPrimBoolean:
@@ -795,6 +810,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());
@@ -1212,11 +1305,39 @@
block->RemovePhi(it.Current()->AsPhi());
}
+ if (block->IsExitBlock()) {
+ exit_block_ = nullptr;
+ }
+
reverse_post_order_.Delete(block);
blocks_.Put(block->GetBlockId(), nullptr);
}
void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
+ DCHECK(HasExitBlock()) << "Unimplemented scenario";
+ // Update the environments in this graph to have the invoke's environment
+ // as parent.
+ {
+ HReversePostOrderIterator it(*this);
+ it.Advance(); // Skip the entry block, we do not need to update the entry's suspend check.
+ for (; !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ for (HInstructionIterator instr_it(block->GetInstructions());
+ !instr_it.Done();
+ instr_it.Advance()) {
+ HInstruction* current = instr_it.Current();
+ if (current->NeedsEnvironment()) {
+ current->GetEnvironment()->SetAndCopyParentChain(
+ outer_graph->GetArena(), invoke->GetEnvironment());
+ }
+ }
+ }
+ }
+ outer_graph->UpdateMaximumNumberOfOutVRegs(GetMaximumNumberOfOutVRegs());
+ if (HasBoundsChecks()) {
+ outer_graph->SetHasBoundsChecks(true);
+ }
+
if (GetBlocks().Size() == 3) {
// Simple case of an entry block, a body block, and an exit block.
// Put the body block's instruction into `invoke`'s block.
@@ -1377,6 +1498,8 @@
DCHECK(parameter_index != last_input_index);
}
current->ReplaceWith(invoke->InputAt(parameter_index++));
+ } else if (current->IsCurrentMethod()) {
+ current->ReplaceWith(outer_graph->GetCurrentMethod());
} else {
DCHECK(current->IsGoto() || current->IsSuspendCheck());
entry_block_->RemoveInstruction(current);
@@ -1387,6 +1510,81 @@
invoke->GetBlock()->RemoveInstruction(invoke);
}
+/*
+ * Loop will be transformed to:
+ * old_pre_header
+ * |
+ * if_block
+ * / \
+ * dummy_block deopt_block
+ * \ /
+ * new_pre_header
+ * |
+ * header
+ */
+void HGraph::TransformLoopHeaderForBCE(HBasicBlock* header) {
+ DCHECK(header->IsLoopHeader());
+ HBasicBlock* pre_header = header->GetDominator();
+
+ // Need this to avoid critical edge.
+ HBasicBlock* if_block = new (arena_) HBasicBlock(this, header->GetDexPc());
+ // Need this to avoid critical edge.
+ HBasicBlock* dummy_block = new (arena_) HBasicBlock(this, header->GetDexPc());
+ HBasicBlock* deopt_block = new (arena_) HBasicBlock(this, header->GetDexPc());
+ HBasicBlock* new_pre_header = new (arena_) HBasicBlock(this, header->GetDexPc());
+ AddBlock(if_block);
+ AddBlock(dummy_block);
+ AddBlock(deopt_block);
+ AddBlock(new_pre_header);
+
+ header->ReplacePredecessor(pre_header, new_pre_header);
+ pre_header->successors_.Reset();
+ pre_header->dominated_blocks_.Reset();
+
+ pre_header->AddSuccessor(if_block);
+ if_block->AddSuccessor(dummy_block); // True successor
+ if_block->AddSuccessor(deopt_block); // False successor
+ dummy_block->AddSuccessor(new_pre_header);
+ deopt_block->AddSuccessor(new_pre_header);
+
+ pre_header->dominated_blocks_.Add(if_block);
+ if_block->SetDominator(pre_header);
+ if_block->dominated_blocks_.Add(dummy_block);
+ dummy_block->SetDominator(if_block);
+ if_block->dominated_blocks_.Add(deopt_block);
+ deopt_block->SetDominator(if_block);
+ if_block->dominated_blocks_.Add(new_pre_header);
+ new_pre_header->SetDominator(if_block);
+ new_pre_header->dominated_blocks_.Add(header);
+ header->SetDominator(new_pre_header);
+
+ size_t index_of_header = 0;
+ while (reverse_post_order_.Get(index_of_header) != header) {
+ index_of_header++;
+ }
+ MakeRoomFor(&reverse_post_order_, 4, index_of_header - 1);
+ reverse_post_order_.Put(index_of_header++, if_block);
+ reverse_post_order_.Put(index_of_header++, dummy_block);
+ reverse_post_order_.Put(index_of_header++, deopt_block);
+ reverse_post_order_.Put(index_of_header++, new_pre_header);
+
+ HLoopInformation* info = pre_header->GetLoopInformation();
+ if (info != nullptr) {
+ if_block->SetLoopInformation(info);
+ dummy_block->SetLoopInformation(info);
+ deopt_block->SetLoopInformation(info);
+ new_pre_header->SetLoopInformation(info);
+ for (HLoopInformationOutwardIterator loop_it(*pre_header);
+ !loop_it.Done();
+ loop_it.Advance()) {
+ loop_it.Current()->Add(if_block);
+ loop_it.Current()->Add(dummy_block);
+ loop_it.Current()->Add(deopt_block);
+ loop_it.Current()->Add(new_pre_header);
+ }
+ }
+}
+
std::ostream& operator<<(std::ostream& os, const ReferenceTypeInfo& rhs) {
ScopedObjectAccess soa(Thread::Current());
os << "["
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index ef60d76..126b3b9 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -35,6 +35,7 @@
class GraphChecker;
class HBasicBlock;
+class HCurrentMethod;
class HDoubleConstant;
class HEnvironment;
class HFloatConstant;
@@ -60,6 +61,10 @@
static constexpr uint32_t kMaxIntShiftValue = 0x1f;
static constexpr uint64_t kMaxLongShiftValue = 0x3f;
+static constexpr uint32_t kUnknownFieldIndex = static_cast<uint32_t>(-1);
+
+static constexpr InvokeType kInvalidInvokeType = static_cast<InvokeType>(-1);
+
enum IfCondition {
kCondEQ,
kCondNE,
@@ -120,7 +125,9 @@
HGraph(ArenaAllocator* arena,
const DexFile& dex_file,
uint32_t method_idx,
+ bool should_generate_constructor_barrier,
InstructionSet instruction_set,
+ InvokeType invoke_type = kInvalidInvokeType,
bool debuggable = false,
int start_instruction_id = 0)
: arena_(arena),
@@ -138,12 +145,16 @@
current_instruction_id_(start_instruction_id),
dex_file_(dex_file),
method_idx_(method_idx),
+ invoke_type_(invoke_type),
+ in_ssa_form_(false),
+ should_generate_constructor_barrier_(should_generate_constructor_barrier),
instruction_set_(instruction_set),
cached_null_constant_(nullptr),
cached_int_constants_(std::less<int32_t>(), arena->Adapter()),
cached_float_constants_(std::less<int32_t>(), arena->Adapter()),
cached_long_constants_(std::less<int64_t>(), arena->Adapter()),
- cached_double_constants_(std::less<int64_t>(), arena->Adapter()) {}
+ cached_double_constants_(std::less<int64_t>(), arena->Adapter()),
+ cached_current_method_(nullptr) {}
ArenaAllocator* GetArena() const { return arena_; }
const GrowableArray<HBasicBlock*>& GetBlocks() const { return blocks_; }
@@ -151,6 +162,7 @@
HBasicBlock* GetEntryBlock() const { return entry_block_; }
HBasicBlock* GetExitBlock() const { return exit_block_; }
+ bool HasExitBlock() const { return exit_block_ != nullptr; }
void SetEntryBlock(HBasicBlock* block) { entry_block_ = block; }
void SetExitBlock(HBasicBlock* block) { exit_block_ = block; }
@@ -167,6 +179,7 @@
// users remaining when being visited.
if (!AnalyzeNaturalLoops()) return false;
TransformToSsa();
+ in_ssa_form_ = true;
return true;
}
@@ -182,6 +195,10 @@
// Inline this graph in `outer_graph`, replacing the given `invoke` instruction.
void InlineInto(HGraph* outer_graph, HInvoke* invoke);
+ // Need to add a couple of blocks to test if the loop body is entered and
+ // put deoptimization instructions, etc.
+ void TransformLoopHeaderForBCE(HBasicBlock* header);
+
// Removes `block` from the graph.
void DeleteDeadBlock(HBasicBlock* block);
@@ -209,11 +226,16 @@
maximum_number_of_out_vregs_ = new_value;
}
+ void UpdateMaximumNumberOfOutVRegs(uint16_t other_value) {
+ maximum_number_of_out_vregs_ = std::max(maximum_number_of_out_vregs_, other_value);
+ }
+
void UpdateTemporariesVRegSlots(size_t slots) {
temporaries_vreg_slots_ = std::max(slots, temporaries_vreg_slots_);
}
size_t GetTemporariesVRegSlots() const {
+ DCHECK(!in_ssa_form_);
return temporaries_vreg_slots_;
}
@@ -222,6 +244,7 @@
}
uint16_t GetNumberOfVRegs() const {
+ DCHECK(!in_ssa_form_);
return number_of_vregs_;
}
@@ -230,6 +253,7 @@
}
uint16_t GetNumberOfLocalVRegs() const {
+ DCHECK(!in_ssa_form_);
return number_of_vregs_ - number_of_in_vregs_;
}
@@ -249,6 +273,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
@@ -269,6 +297,8 @@
return CreateConstant(bit_cast<int64_t, double>(value), &cached_double_constants_);
}
+ HCurrentMethod* GetCurrentMethod();
+
HBasicBlock* FindCommonDominator(HBasicBlock* first, HBasicBlock* second) const;
const DexFile& GetDexFile() const {
@@ -279,6 +309,10 @@
return method_idx_;
}
+ InvokeType GetInvokeType() const {
+ return invoke_type_;
+ }
+
private:
void VisitBlockForDominatorTree(HBasicBlock* block,
HBasicBlock* predecessor,
@@ -361,6 +395,16 @@
// 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_;
+
+ // Whether the graph has been transformed to SSA form. Only used
+ // in debug mode to ensure we are not using properties only valid
+ // for non-SSA form (like the number of temporaries).
+ bool in_ssa_form_;
+
+ const bool should_generate_constructor_barrier_;
+
const InstructionSet instruction_set_;
// Cached constants.
@@ -370,6 +414,8 @@
ArenaSafeMap<int64_t, HLongConstant*> cached_long_constants_;
ArenaSafeMap<int64_t, HDoubleConstant*> cached_double_constants_;
+ HCurrentMethod* cached_current_method_;
+
friend class SsaBuilder; // For caching constants.
friend class SsaLivenessAnalysis; // For the linear order.
ART_FRIEND_TEST(GraphTest, IfSuccessorSimpleJoinBlock1);
@@ -782,7 +828,7 @@
DISALLOW_COPY_AND_ASSIGN(HLoopInformationOutwardIterator);
};
-#define FOR_EACH_CONCRETE_INSTRUCTION(M) \
+#define FOR_EACH_CONCRETE_INSTRUCTION_COMMON(M) \
M(Add, BinaryOperation) \
M(And, BinaryOperation) \
M(ArrayGet, Instruction) \
@@ -795,6 +841,7 @@
M(ClinitCheck, Instruction) \
M(Compare, BinaryOperation) \
M(Condition, BinaryOperation) \
+ M(CurrentMethod, Instruction) \
M(Deoptimize, Instruction) \
M(Div, BinaryOperation) \
M(DivZeroCheck, Instruction) \
@@ -851,6 +898,21 @@
M(UShr, BinaryOperation) \
M(Xor, BinaryOperation) \
+#define FOR_EACH_CONCRETE_INSTRUCTION_ARM(M)
+
+#define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M)
+
+#define FOR_EACH_CONCRETE_INSTRUCTION_X86(M)
+
+#define FOR_EACH_CONCRETE_INSTRUCTION_X86_64(M)
+
+#define FOR_EACH_CONCRETE_INSTRUCTION(M) \
+ FOR_EACH_CONCRETE_INSTRUCTION_COMMON(M) \
+ FOR_EACH_CONCRETE_INSTRUCTION_ARM(M) \
+ FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \
+ FOR_EACH_CONCRETE_INSTRUCTION_X86(M) \
+ FOR_EACH_CONCRETE_INSTRUCTION_X86_64(M)
+
#define FOR_EACH_INSTRUCTION(M) \
FOR_EACH_CONCRETE_INSTRUCTION(M) \
M(Constant, Instruction) \
@@ -1097,13 +1159,17 @@
size_t number_of_vregs,
const DexFile& dex_file,
uint32_t method_idx,
- uint32_t dex_pc)
+ uint32_t dex_pc,
+ InvokeType invoke_type,
+ HInstruction* holder)
: 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),
+ holder_(holder) {
vregs_.SetSize(number_of_vregs);
for (size_t i = 0; i < number_of_vregs; i++) {
vregs_.Put(i, HUserRecord<HEnvironment*>());
@@ -1115,16 +1181,25 @@
}
}
+ HEnvironment(ArenaAllocator* arena, const HEnvironment& to_copy, HInstruction* holder)
+ : HEnvironment(arena,
+ to_copy.Size(),
+ to_copy.GetDexFile(),
+ to_copy.GetMethodIdx(),
+ to_copy.GetDexPc(),
+ to_copy.GetInvokeType(),
+ holder) {}
+
void SetAndCopyParentChain(ArenaAllocator* allocator, HEnvironment* parent) {
- parent_ = new (allocator) HEnvironment(allocator,
- parent->Size(),
- parent->GetDexFile(),
- parent->GetMethodIdx(),
- parent->GetDexPc());
- if (parent->GetParent() != nullptr) {
- parent_->SetAndCopyParentChain(allocator, parent->GetParent());
+ if (parent_ != nullptr) {
+ parent_->SetAndCopyParentChain(allocator, parent);
+ } else {
+ parent_ = new (allocator) HEnvironment(allocator, *parent, holder_);
+ parent_->CopyFrom(parent);
+ if (parent->GetParent() != nullptr) {
+ parent_->SetAndCopyParentChain(allocator, parent->GetParent());
+ }
}
- parent_->CopyFrom(parent);
}
void CopyFrom(const GrowableArray<HInstruction*>& locals);
@@ -1165,10 +1240,18 @@
return method_idx_;
}
+ InvokeType GetInvokeType() const {
+ return invoke_type_;
+ }
+
const DexFile& GetDexFile() const {
return dex_file_;
}
+ HInstruction* GetHolder() const {
+ return holder_;
+ }
+
private:
// Record instructions' use entries of this environment for constant-time removal.
// It should only be called by HInstruction when a new environment use is added.
@@ -1184,6 +1267,11 @@
const DexFile& dex_file_;
const uint32_t method_idx_;
const uint32_t dex_pc_;
+ const InvokeType invoke_type_;
+
+ // The instruction that holds this environment. Only used in debug mode
+ // to ensure the graph is consistent.
+ HInstruction* const holder_;
friend class HInstruction;
@@ -1212,6 +1300,9 @@
bool IsExact() const { return is_exact_; }
bool IsTop() const { return is_top_; }
+ bool IsInterface() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return !IsTop() && GetTypeHandle()->IsInterface();
+ }
Handle<mirror::Class> GetTypeHandle() const { return type_handle_; }
@@ -1391,18 +1482,18 @@
HEnvironment* GetEnvironment() const { return environment_; }
// Set the `environment_` field. Raw because this method does not
// update the uses lists.
- void SetRawEnvironment(HEnvironment* environment) { environment_ = environment; }
+ void SetRawEnvironment(HEnvironment* environment) {
+ DCHECK(environment_ == nullptr);
+ DCHECK_EQ(environment->GetHolder(), this);
+ environment_ = environment;
+ }
// Set the environment of this instruction, copying it from `environment`. While
// copying, the uses lists are being updated.
void CopyEnvironmentFrom(HEnvironment* environment) {
+ DCHECK(environment_ == nullptr);
ArenaAllocator* allocator = GetBlock()->GetGraph()->GetArena();
- environment_ = new (allocator) HEnvironment(
- allocator,
- environment->Size(),
- environment->GetDexFile(),
- environment->GetMethodIdx(),
- environment->GetDexPc());
+ environment_ = new (allocator) HEnvironment(allocator, *environment, this);
environment_->CopyFrom(environment);
if (environment->GetParent() != nullptr) {
environment_->SetAndCopyParentChain(allocator, environment->GetParent());
@@ -1411,17 +1502,13 @@
void CopyEnvironmentFromWithLoopPhiAdjustment(HEnvironment* environment,
HBasicBlock* block) {
+ DCHECK(environment_ == nullptr);
ArenaAllocator* allocator = GetBlock()->GetGraph()->GetArena();
- environment_ = new (allocator) HEnvironment(
- allocator,
- environment->Size(),
- environment->GetDexFile(),
- environment->GetMethodIdx(),
- environment->GetDexPc());
+ environment_ = new (allocator) HEnvironment(allocator, *environment, this);
+ 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
@@ -1807,6 +1894,19 @@
DISALLOW_COPY_AND_ASSIGN(HDeoptimize);
};
+// Represents the ArtMethod that was passed as a first argument to
+// the method. It is used by instructions that depend on it, like
+// instructions that work with the dex cache.
+class HCurrentMethod : public HExpression<0> {
+ public:
+ explicit HCurrentMethod(Primitive::Type type) : HExpression(type, SideEffects::None()) {}
+
+ DECLARE_INSTRUCTION(CurrentMethod);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HCurrentMethod);
+};
+
class HUnaryOperation : public HExpression<1> {
public:
HUnaryOperation(Primitive::Type result_type, HInstruction* input)
@@ -2201,15 +2301,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);
@@ -2239,15 +2340,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);
@@ -2369,6 +2471,9 @@
uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
uint32_t GetDexMethodIndex() const { return dex_method_index_; }
+ const DexFile& GetDexFile() const { return GetEnvironment()->GetDexFile(); }
+
+ InvokeType GetOriginalInvokeType() const { return original_invoke_type_; }
Intrinsics GetIntrinsic() const {
return intrinsic_;
@@ -2378,6 +2483,12 @@
intrinsic_ = intrinsic;
}
+ bool IsInlined() const {
+ return GetEnvironment()->GetParent() != nullptr;
+ }
+
+ bool CanThrow() const OVERRIDE { return true; }
+
DECLARE_INSTRUCTION(Invoke);
protected:
@@ -2386,13 +2497,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);
@@ -2408,6 +2521,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:
@@ -2436,11 +2550,13 @@
ClinitCheckRequirement clinit_check_requirement)
: HInvoke(arena,
number_of_arguments,
- clinit_check_requirement == ClinitCheckRequirement::kExplicit ? 1u : 0u,
+ // There is one extra argument for the HCurrentMethod node, and
+ // potentially one other if the clinit check is explicit.
+ clinit_check_requirement == ClinitCheckRequirement::kExplicit ? 2u : 1u,
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),
@@ -2453,12 +2569,12 @@
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(); }
bool IsStringInit() const { return string_init_offset_ != 0; }
int32_t GetStringInitOffset() const { return string_init_offset_; }
+ uint32_t GetCurrentMethodInputIndex() const { return GetNumberOfArguments(); }
// Is this instruction a call to a static method?
bool IsStatic() const {
@@ -2511,7 +2627,6 @@
}
private:
- const InvokeType original_invoke_type_;
const InvokeType invoke_type_;
const bool is_recursive_;
ClinitCheckRequirement clinit_check_requirement_;
@@ -2530,7 +2645,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 {
@@ -2556,7 +2671,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 {
@@ -2575,16 +2690,24 @@
DISALLOW_COPY_AND_ASSIGN(HInvokeInterface);
};
-class HNewInstance : public HExpression<0> {
+class HNewInstance : public HExpression<1> {
public:
- HNewInstance(uint32_t dex_pc, uint16_t type_index, QuickEntrypointEnum entrypoint)
+ HNewInstance(HCurrentMethod* current_method,
+ uint32_t dex_pc,
+ uint16_t type_index,
+ const DexFile& dex_file,
+ QuickEntrypointEnum entrypoint)
: HExpression(Primitive::kPrimNot, SideEffects::None()),
dex_pc_(dex_pc),
type_index_(type_index),
- entrypoint_(entrypoint) {}
+ dex_file_(dex_file),
+ entrypoint_(entrypoint) {
+ SetRawInputAt(0, current_method);
+ }
uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
uint16_t GetTypeIndex() const { return type_index_; }
+ const DexFile& GetDexFile() const { return dex_file_; }
// Calls runtime so needs an environment.
bool NeedsEnvironment() const OVERRIDE { return true; }
@@ -2603,6 +2726,7 @@
private:
const uint32_t dex_pc_;
const uint16_t type_index_;
+ const DexFile& dex_file_;
const QuickEntrypointEnum entrypoint_;
DISALLOW_COPY_AND_ASSIGN(HNewInstance);
@@ -2622,21 +2746,26 @@
DISALLOW_COPY_AND_ASSIGN(HNeg);
};
-class HNewArray : public HExpression<1> {
+class HNewArray : public HExpression<2> {
public:
HNewArray(HInstruction* length,
+ HCurrentMethod* current_method,
uint32_t dex_pc,
uint16_t type_index,
+ const DexFile& dex_file,
QuickEntrypointEnum entrypoint)
: HExpression(Primitive::kPrimNot, SideEffects::None()),
dex_pc_(dex_pc),
type_index_(type_index),
+ dex_file_(dex_file),
entrypoint_(entrypoint) {
SetRawInputAt(0, length);
+ SetRawInputAt(1, current_method);
}
uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
uint16_t GetTypeIndex() const { return type_index_; }
+ const DexFile& GetDexFile() const { return dex_file_; }
// Calls runtime so needs an environment.
bool NeedsEnvironment() const OVERRIDE { return true; }
@@ -2653,6 +2782,7 @@
private:
const uint32_t dex_pc_;
const uint16_t type_index_;
+ const DexFile& dex_file_;
const QuickEntrypointEnum entrypoint_;
DISALLOW_COPY_AND_ASSIGN(HNewArray);
@@ -2904,6 +3034,8 @@
bool CanBeNull() const OVERRIDE { return !is_this_; }
+ bool IsThis() const { return is_this_; }
+
DECLARE_INSTRUCTION(ParameterValue);
private:
@@ -2984,6 +3116,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:
@@ -3101,17 +3237,29 @@
class FieldInfo : public ValueObject {
public:
- FieldInfo(MemberOffset field_offset, Primitive::Type field_type, bool is_volatile)
- : field_offset_(field_offset), field_type_(field_type), is_volatile_(is_volatile) {}
+ FieldInfo(MemberOffset field_offset,
+ Primitive::Type field_type,
+ bool is_volatile,
+ uint32_t index,
+ const DexFile& dex_file)
+ : field_offset_(field_offset),
+ field_type_(field_type),
+ is_volatile_(is_volatile),
+ index_(index),
+ dex_file_(dex_file) {}
MemberOffset GetFieldOffset() const { return field_offset_; }
Primitive::Type GetFieldType() const { return field_type_; }
+ uint32_t GetFieldIndex() const { return index_; }
+ const DexFile& GetDexFile() const { return dex_file_; }
bool IsVolatile() const { return is_volatile_; }
private:
const MemberOffset field_offset_;
const Primitive::Type field_type_;
const bool is_volatile_;
+ uint32_t index_;
+ const DexFile& dex_file_;
};
class HInstanceFieldGet : public HExpression<1> {
@@ -3119,9 +3267,11 @@
HInstanceFieldGet(HInstruction* value,
Primitive::Type field_type,
MemberOffset field_offset,
- bool is_volatile)
+ bool is_volatile,
+ uint32_t field_idx,
+ const DexFile& dex_file)
: HExpression(field_type, SideEffects::DependsOnSomething()),
- field_info_(field_offset, field_type, is_volatile) {
+ field_info_(field_offset, field_type, is_volatile, field_idx, dex_file) {
SetRawInputAt(0, value);
}
@@ -3159,9 +3309,12 @@
HInstruction* value,
Primitive::Type field_type,
MemberOffset field_offset,
- bool is_volatile)
+ bool is_volatile,
+ uint32_t field_idx,
+ const DexFile& dex_file)
: HTemplateInstruction(SideEffects::ChangesSomething()),
- field_info_(field_offset, field_type, is_volatile) {
+ field_info_(field_offset, field_type, is_volatile, field_idx, dex_file),
+ value_can_be_null_(true) {
SetRawInputAt(0, object);
SetRawInputAt(1, value);
}
@@ -3175,11 +3328,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);
};
@@ -3228,7 +3384,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);
@@ -3240,6 +3397,9 @@
return needs_type_check_;
}
+ // Can throw ArrayStoreException.
+ bool CanThrow() const OVERRIDE { return needs_type_check_; }
+
bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE {
UNUSED(obj);
// TODO: Same as for ArrayGet.
@@ -3250,6 +3410,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_; }
@@ -3275,6 +3440,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);
};
@@ -3387,17 +3553,22 @@
/**
* Instruction to load a Class object.
*/
-class HLoadClass : public HExpression<0> {
+class HLoadClass : public HExpression<1> {
public:
- HLoadClass(uint16_t type_index,
+ HLoadClass(HCurrentMethod* current_method,
+ uint16_t type_index,
+ const DexFile& dex_file,
bool is_referrers_class,
uint32_t dex_pc)
: HExpression(Primitive::kPrimNot, SideEffects::None()),
type_index_(type_index),
+ dex_file_(dex_file),
is_referrers_class_(is_referrers_class),
dex_pc_(dex_pc),
generate_clinit_check_(false),
- loaded_class_rti_(ReferenceTypeInfo::CreateTop(/* is_exact */ false)) {}
+ loaded_class_rti_(ReferenceTypeInfo::CreateTop(/* is_exact */ false)) {
+ SetRawInputAt(0, current_method);
+ }
bool CanBeMoved() const OVERRIDE { return true; }
@@ -3421,8 +3592,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 {
@@ -3449,12 +3620,15 @@
return loaded_class_rti_.IsExact();
}
+ const DexFile& GetDexFile() { return dex_file_; }
+
bool NeedsDexCache() const OVERRIDE { return !is_referrers_class_; }
DECLARE_INSTRUCTION(LoadClass);
private:
const uint16_t type_index_;
+ const DexFile& dex_file_;
const bool is_referrers_class_;
const uint32_t dex_pc_;
// Whether this instruction must generate the initialization check.
@@ -3466,12 +3640,14 @@
DISALLOW_COPY_AND_ASSIGN(HLoadClass);
};
-class HLoadString : public HExpression<0> {
+class HLoadString : public HExpression<1> {
public:
- HLoadString(uint32_t string_index, uint32_t dex_pc)
+ HLoadString(HCurrentMethod* current_method, uint32_t string_index, uint32_t dex_pc)
: HExpression(Primitive::kPrimNot, SideEffects::None()),
string_index_(string_index),
- dex_pc_(dex_pc) {}
+ dex_pc_(dex_pc) {
+ SetRawInputAt(0, current_method);
+ }
bool CanBeMoved() const OVERRIDE { return true; }
@@ -3536,9 +3712,11 @@
HStaticFieldGet(HInstruction* cls,
Primitive::Type field_type,
MemberOffset field_offset,
- bool is_volatile)
+ bool is_volatile,
+ uint32_t field_idx,
+ const DexFile& dex_file)
: HExpression(field_type, SideEffects::DependsOnSomething()),
- field_info_(field_offset, field_type, is_volatile) {
+ field_info_(field_offset, field_type, is_volatile, field_idx, dex_file) {
SetRawInputAt(0, cls);
}
@@ -3573,9 +3751,12 @@
HInstruction* value,
Primitive::Type field_type,
MemberOffset field_offset,
- bool is_volatile)
+ bool is_volatile,
+ uint32_t field_idx,
+ const DexFile& dex_file)
: HTemplateInstruction(SideEffects::ChangesSomething()),
- field_info_(field_offset, field_type, is_volatile) {
+ field_info_(field_offset, field_type, is_volatile, field_idx, dex_file),
+ value_can_be_null_(true) {
SetRawInputAt(0, cls);
SetRawInputAt(1, value);
}
@@ -3586,11 +3767,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);
};
@@ -3893,7 +4077,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));
@@ -4082,6 +4268,39 @@
DISALLOW_COPY_AND_ASSIGN(HBlocksInLoopIterator);
};
+// Iterator over the blocks that art part of the loop. Includes blocks part
+// of an inner loop. The order in which the blocks are iterated is reverse
+// post order.
+class HBlocksInLoopReversePostOrderIterator : public ValueObject {
+ public:
+ explicit HBlocksInLoopReversePostOrderIterator(const HLoopInformation& info)
+ : blocks_in_loop_(info.GetBlocks()),
+ blocks_(info.GetHeader()->GetGraph()->GetReversePostOrder()),
+ index_(0) {
+ if (!blocks_in_loop_.IsBitSet(blocks_.Get(index_)->GetBlockId())) {
+ Advance();
+ }
+ }
+
+ bool Done() const { return index_ == blocks_.Size(); }
+ HBasicBlock* Current() const { return blocks_.Get(index_); }
+ void Advance() {
+ ++index_;
+ for (size_t e = blocks_.Size(); index_ < e; ++index_) {
+ if (blocks_in_loop_.IsBitSet(blocks_.Get(index_)->GetBlockId())) {
+ break;
+ }
+ }
+ }
+
+ private:
+ const BitVector& blocks_in_loop_;
+ const GrowableArray<HBasicBlock*>& blocks_;
+ size_t index_;
+
+ DISALLOW_COPY_AND_ASSIGN(HBlocksInLoopReversePostOrderIterator);
+};
+
inline int64_t Int64FromConstant(HConstant* constant) {
DCHECK(constant->IsIntConstant() || constant->IsLongConstant());
return constant->IsIntConstant() ? constant->AsIntConstant()->GetValue()
diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc
index 2736453..fef77aa 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);
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, with_environment);
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, nullptr);
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, nullptr);
parent2->CopyFrom(array);
parent1->SetAndCopyParentChain(&allocator, parent2);
diff --git a/compiler/optimizing/optimizing_cfi_test_expected.inc b/compiler/optimizing/optimizing_cfi_test_expected.inc
index 9ccc011..2c2c55f 100644
--- a/compiler/optimizing/optimizing_cfi_test_expected.inc
+++ b/compiler/optimizing/optimizing_cfi_test_expected.inc
@@ -32,20 +32,20 @@
// 0x00000012: .cfi_def_cfa_offset: 64
static constexpr uint8_t expected_asm_kArm64[] = {
- 0xE0, 0x0F, 0x1C, 0xF8, 0xF3, 0xD3, 0x02, 0xA9, 0xFE, 0x1F, 0x00, 0xF9,
- 0xE8, 0xA7, 0x01, 0x6D, 0xE8, 0xA7, 0x41, 0x6D, 0xF3, 0xD3, 0x42, 0xA9,
+ 0xE0, 0x0F, 0x1C, 0xF8, 0xF4, 0xD7, 0x02, 0xA9, 0xFE, 0x1F, 0x00, 0xF9,
+ 0xE8, 0xA7, 0x01, 0x6D, 0xE8, 0xA7, 0x41, 0x6D, 0xF4, 0xD7, 0x42, 0xA9,
0xFE, 0x1F, 0x40, 0xF9, 0xFF, 0x03, 0x01, 0x91, 0xC0, 0x03, 0x5F, 0xD6,
};
static constexpr uint8_t expected_cfi_kArm64[] = {
- 0x44, 0x0E, 0x40, 0x44, 0x93, 0x06, 0x94, 0x04, 0x44, 0x9E, 0x02, 0x44,
+ 0x44, 0x0E, 0x40, 0x44, 0x94, 0x06, 0x95, 0x04, 0x44, 0x9E, 0x02, 0x44,
0x05, 0x48, 0x0A, 0x05, 0x49, 0x08, 0x0A, 0x44, 0x06, 0x48, 0x06, 0x49,
- 0x44, 0xD3, 0xD4, 0x44, 0xDE, 0x44, 0x0E, 0x00, 0x44, 0x0B, 0x0E, 0x40,
+ 0x44, 0xD4, 0xD5, 0x44, 0xDE, 0x44, 0x0E, 0x00, 0x44, 0x0B, 0x0E, 0x40,
};
// 0x00000000: str x0, [sp, #-64]!
// 0x00000004: .cfi_def_cfa_offset: 64
-// 0x00000004: stp x19, x20, [sp, #40]
-// 0x00000008: .cfi_offset: r19 at cfa-24
-// 0x00000008: .cfi_offset: r20 at cfa-16
+// 0x00000004: stp x20, x21, [sp, #40]
+// 0x00000008: .cfi_offset: r20 at cfa-24
+// 0x00000008: .cfi_offset: r21 at cfa-16
// 0x00000008: str lr, [sp, #56]
// 0x0000000c: .cfi_offset: r30 at cfa-8
// 0x0000000c: stp d8, d9, [sp, #24]
@@ -55,9 +55,9 @@
// 0x00000010: ldp d8, d9, [sp, #24]
// 0x00000014: .cfi_restore_extended: r72
// 0x00000014: .cfi_restore_extended: r73
-// 0x00000014: ldp x19, x20, [sp, #40]
-// 0x00000018: .cfi_restore: r19
+// 0x00000014: ldp x20, x21, [sp, #40]
// 0x00000018: .cfi_restore: r20
+// 0x00000018: .cfi_restore: r21
// 0x00000018: ldr lr, [sp, #56]
// 0x0000001c: .cfi_restore: r30
// 0x0000001c: add sp, sp, #0x40 (64)
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index c7b2c67..bf0b9fa 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -223,7 +223,6 @@
CompiledMethod* CompileOptimized(HGraph* graph,
CodeGenerator* codegen,
CompilerDriver* driver,
- const DexFile& dex_file,
const DexCompilationUnit& dex_compilation_unit,
PassInfoPrinter* pass_info) const;
@@ -316,7 +315,6 @@
static void RunOptimizations(HGraph* graph,
CompilerDriver* driver,
OptimizingCompilerStats* stats,
- const DexFile& dex_file,
const DexCompilationUnit& dex_compilation_unit,
PassInfoPrinter* pass_info_printer,
StackHandleScopeCollection* handles) {
@@ -328,24 +326,31 @@
InstructionSimplifier simplify1(graph, stats);
HBooleanSimplifier boolean_simplify(graph);
- HInliner inliner(graph, dex_compilation_unit, dex_compilation_unit, driver, stats);
+ HInliner inliner(graph, dex_compilation_unit, dex_compilation_unit, driver, handles, stats);
HConstantFolding fold2(graph, "constant_folding_after_inlining");
SideEffectsAnalysis side_effects(graph);
GVNOptimization gvn(graph, side_effects);
LICM licm(graph, side_effects);
BoundsCheckElimination bce(graph);
- ReferenceTypePropagation type_propagation(graph, dex_file, dex_compilation_unit, handles);
+ ReferenceTypePropagation type_propagation(graph, handles);
InstructionSimplifier simplify2(graph, stats, "instruction_simplifier_after_types");
+ InstructionSimplifier simplify3(graph, stats, "last_instruction_simplifier");
+ ReferenceTypePropagation type_propagation2(graph, handles);
- IntrinsicsRecognizer intrinsics(graph, dex_compilation_unit.GetDexFile(), driver);
+ IntrinsicsRecognizer intrinsics(graph, driver);
HOptimization* optimizations[] = {
&intrinsics,
&dce1,
&fold1,
&simplify1,
+ &type_propagation,
+ &simplify2,
&inliner,
+ // Run another type propagation phase: inlining will open up more opprotunities
+ // to remove checkast/instanceof and null checks.
+ &type_propagation2,
// BooleanSimplifier depends on the InstructionSimplifier removing redundant
// suspend checks to recognize empty blocks.
&boolean_simplify,
@@ -354,8 +359,7 @@
&gvn,
&licm,
&bce,
- &type_propagation,
- &simplify2,
+ &simplify3,
&dce2,
};
@@ -391,12 +395,11 @@
CompiledMethod* OptimizingCompiler::CompileOptimized(HGraph* graph,
CodeGenerator* codegen,
CompilerDriver* compiler_driver,
- const DexFile& dex_file,
const DexCompilationUnit& dex_compilation_unit,
PassInfoPrinter* pass_info_printer) const {
StackHandleScopeCollection handles(Thread::Current());
RunOptimizations(graph, compiler_driver, compilation_stats_.get(),
- dex_file, dex_compilation_unit, pass_info_printer, &handles);
+ dex_compilation_unit, pass_info_printer, &handles);
AllocateRegisters(graph, codegen, pass_info_printer);
@@ -404,7 +407,7 @@
codegen->CompileOptimized(&allocator);
DefaultSrcMap src_mapping_table;
- if (compiler_driver->GetCompilerOptions().GetIncludeDebugSymbols()) {
+ if (compiler_driver->GetCompilerOptions().GetGenerateDebugInfo()) {
codegen->BuildSourceMap(&src_mapping_table);
}
@@ -441,7 +444,7 @@
std::vector<uint8_t> mapping_table;
codegen->BuildMappingTable(&mapping_table);
DefaultSrcMap src_mapping_table;
- if (compiler_driver->GetCompilerOptions().GetIncludeDebugSymbols()) {
+ if (compiler_driver->GetCompilerOptions().GetGenerateDebugInfo()) {
codegen->BuildSourceMap(&src_mapping_table);
}
std::vector<uint8_t> vmap_table;
@@ -512,10 +515,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->GetInstructionSet(),
- compiler_driver->GetCompilerOptions().GetDebuggable());
+ &arena, dex_file, method_idx, requires_barrier, compiler_driver->GetInstructionSet(),
+ 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.
@@ -533,7 +540,7 @@
return nullptr;
}
codegen->GetAssembler()->cfi().SetEnabled(
- compiler_driver->GetCompilerOptions().GetIncludeCFI());
+ compiler_driver->GetCompilerOptions().GetGenerateDebugInfo());
PassInfoPrinter pass_info_printer(graph,
method_name.c_str(),
@@ -581,7 +588,6 @@
return CompileOptimized(graph,
codegen.get(),
compiler_driver,
- dex_file,
dex_compilation_unit,
&pass_info_printer);
} else if (shouldOptimize && can_allocate_registers) {
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index b6b1bb1..b988813 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -19,6 +19,7 @@
#include <sstream>
#include <string>
+#include <type_traits>
#include "atomic.h"
@@ -38,7 +39,6 @@
kNotCompiledHugeMethod,
kNotCompiledLargeMethodNoBranches,
kNotCompiledNoCodegen,
- kNotCompiledNonSequentialRegPair,
kNotCompiledPathological,
kNotCompiledSpaceFilter,
kNotCompiledUnhandledInstruction,
@@ -84,14 +84,15 @@
for (int i = 0; i < kLastStat; i++) {
if (compile_stats_[i] != 0) {
- LOG(INFO) << PrintMethodCompilationStat(i) << ": " << compile_stats_[i];
+ LOG(INFO) << PrintMethodCompilationStat(static_cast<MethodCompilationStat>(i)) << ": "
+ << compile_stats_[i];
}
}
}
}
private:
- std::string PrintMethodCompilationStat(int stat) const {
+ std::string PrintMethodCompilationStat(MethodCompilationStat stat) const {
switch (stat) {
case kAttemptCompilation : return "kAttemptCompilation";
case kCompiledBaseline : return "kCompiledBaseline";
@@ -106,7 +107,6 @@
case kNotCompiledHugeMethod : return "kNotCompiledHugeMethod";
case kNotCompiledLargeMethodNoBranches : return "kNotCompiledLargeMethodNoBranches";
case kNotCompiledNoCodegen : return "kNotCompiledNoCodegen";
- case kNotCompiledNonSequentialRegPair : return "kNotCompiledNonSequentialRegPair";
case kNotCompiledPathological : return "kNotCompiledPathological";
case kNotCompiledSpaceFilter : return "kNotCompiledSpaceFilter";
case kNotCompiledUnhandledInstruction : return "kNotCompiledUnhandledInstruction";
@@ -120,9 +120,12 @@
case kRemovedCheckedCast: return "kRemovedCheckedCast";
case kRemovedDeadInstruction: return "kRemovedDeadInstruction";
case kRemovedNullCheck: return "kRemovedNullCheck";
- default: LOG(FATAL) << "invalid stat";
+
+ case kLastStat: break; // Invalid to print out.
}
- return "";
+ LOG(FATAL) << "invalid stat "
+ << static_cast<std::underlying_type<MethodCompilationStat>::type>(stat);
+ UNREACHABLE();
}
AtomicInteger compile_stats_[kLastStat];
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index 3ef96fa..86c22ed 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -74,8 +74,8 @@
inline HGraph* CreateGraph(ArenaAllocator* allocator) {
return new (allocator) HGraph(
- allocator, *reinterpret_cast<DexFile*>(allocator->Alloc(sizeof(DexFile))), -1, kRuntimeISA,
- false);
+ allocator, *reinterpret_cast<DexFile*>(allocator->Alloc(sizeof(DexFile))), -1, false,
+ kRuntimeISA);
}
// 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..a249aa9 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,19 @@
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.
+ // There is one special case where we still need the clinit check, when inlining. Because
+ // currently the callee is responsible for reporting parameters to the GC, the code
+ // that walks the stack during `artQuickResolutionTrampoline` cannot be interrupted for GC.
+ // Therefore we cannot allocate any object in that code, including loading a new class.
+ if (last_input == invoke->GetPrevious() && !invoke->IsInlined()) {
+ 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,
@@ -95,7 +106,7 @@
// If the load class instruction is no longer used, remove it from
// the graph.
- if (!last_input->HasUses()) {
+ if (!last_input->HasUses() && !(last_input->MustGenerateClinitCheck() && invoke->IsInlined())) {
last_input->GetBlock()->RemoveInstruction(last_input);
}
}
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index e93e061..3d81c20 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -35,15 +35,22 @@
void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
// TODO: handle other instructions that give type info
- // (NewArray/Call/Field accesses/array accesses)
+ // (Call/array accesses)
// Initialize exact types first for faster convergence.
for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* instr = it.Current();
+ // TODO: Make ReferenceTypePropagation a visitor or create a new one.
if (instr->IsNewInstance()) {
VisitNewInstance(instr->AsNewInstance());
} else if (instr->IsLoadClass()) {
VisitLoadClass(instr->AsLoadClass());
+ } else if (instr->IsNewArray()) {
+ VisitNewArray(instr->AsNewArray());
+ } else if (instr->IsInstanceFieldGet()) {
+ VisitInstanceFieldGet(instr->AsInstanceFieldGet());
+ } else if (instr->IsStaticFieldGet()) {
+ VisitStaticFieldGet(instr->AsStaticFieldGet());
}
}
@@ -159,20 +166,65 @@
}
}
-void ReferenceTypePropagation::VisitNewInstance(HNewInstance* instr) {
- ScopedObjectAccess soa(Thread::Current());
- mirror::DexCache* dex_cache = dex_compilation_unit_.GetClassLinker()->FindDexCache(dex_file_);
- // Get type from dex cache assuming it was populated by the verifier.
- mirror::Class* resolved_class = dex_cache->GetResolvedType(instr->GetTypeIndex());
- if (resolved_class != nullptr) {
- MutableHandle<mirror::Class> handle = handles_->NewHandle(resolved_class);
- instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, true));
+void ReferenceTypePropagation::SetClassAsTypeInfo(HInstruction* instr,
+ mirror::Class* klass,
+ bool is_exact) {
+ if (klass != nullptr) {
+ ScopedObjectAccess soa(Thread::Current());
+ MutableHandle<mirror::Class> handle = handles_->NewHandle(klass);
+ is_exact = is_exact || klass->IsFinal();
+ instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact));
}
}
+void ReferenceTypePropagation::UpdateReferenceTypeInfo(HInstruction* instr,
+ uint16_t type_idx,
+ const DexFile& dex_file,
+ bool is_exact) {
+ DCHECK_EQ(instr->GetType(), Primitive::kPrimNot);
+
+ ScopedObjectAccess soa(Thread::Current());
+ mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file);
+ // Get type from dex cache assuming it was populated by the verifier.
+ SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx), is_exact);
+}
+
+void ReferenceTypePropagation::VisitNewInstance(HNewInstance* instr) {
+ UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true);
+}
+
+void ReferenceTypePropagation::VisitNewArray(HNewArray* instr) {
+ UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true);
+}
+
+void ReferenceTypePropagation::UpdateFieldAccessTypeInfo(HInstruction* instr,
+ const FieldInfo& info) {
+ // The field index is unknown only during tests.
+ if (instr->GetType() != Primitive::kPrimNot || info.GetFieldIndex() == kUnknownFieldIndex) {
+ return;
+ }
+
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ mirror::DexCache* dex_cache = cl->FindDexCache(info.GetDexFile());
+ ArtField* field = cl->GetResolvedField(info.GetFieldIndex(), dex_cache);
+ DCHECK(field != nullptr);
+ mirror::Class* klass = field->GetType<false>();
+ SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
+}
+
+void ReferenceTypePropagation::VisitInstanceFieldGet(HInstanceFieldGet* instr) {
+ UpdateFieldAccessTypeInfo(instr, instr->GetFieldInfo());
+}
+
+void ReferenceTypePropagation::VisitStaticFieldGet(HStaticFieldGet* instr) {
+ UpdateFieldAccessTypeInfo(instr, instr->GetFieldInfo());
+}
+
void ReferenceTypePropagation::VisitLoadClass(HLoadClass* instr) {
ScopedObjectAccess soa(Thread::Current());
- mirror::DexCache* dex_cache = dex_compilation_unit_.GetClassLinker()->FindDexCache(dex_file_);
+ mirror::DexCache* dex_cache =
+ Runtime::Current()->GetClassLinker()->FindDexCache(instr->GetDexFile());
// Get type from dex cache assuming it was populated by the verifier.
mirror::Class* resolved_class = dex_cache->GetResolvedType(instr->GetTypeIndex());
if (resolved_class != nullptr) {
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 733e18e..0a1d4c4 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -30,13 +30,8 @@
*/
class ReferenceTypePropagation : public HOptimization {
public:
- ReferenceTypePropagation(HGraph* graph,
- const DexFile& dex_file,
- const DexCompilationUnit& dex_compilation_unit,
- StackHandleScopeCollection* handles)
+ ReferenceTypePropagation(HGraph* graph, StackHandleScopeCollection* handles)
: HOptimization(graph, true, kReferenceTypePropagationPassName),
- dex_file_(dex_file),
- dex_compilation_unit_(dex_compilation_unit),
handles_(handles),
worklist_(graph->GetArena(), kDefaultWorklistSize) {}
@@ -47,14 +42,23 @@
private:
void VisitNewInstance(HNewInstance* new_instance);
void VisitLoadClass(HLoadClass* load_class);
+ void VisitNewArray(HNewArray* instr);
void VisitPhi(HPhi* phi);
void VisitBasicBlock(HBasicBlock* block);
+ void UpdateFieldAccessTypeInfo(HInstruction* instr, const FieldInfo& info);
+ void SetClassAsTypeInfo(HInstruction* instr, mirror::Class* klass, bool is_exact);
void UpdateBoundType(HBoundType* bound_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void UpdatePhi(HPhi* phi) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void BoundTypeForIfNotNull(HBasicBlock* block);
void BoundTypeForIfInstanceOf(HBasicBlock* block);
+ void UpdateReferenceTypeInfo(HInstruction* instr,
+ uint16_t type_idx,
+ const DexFile& dex_file,
+ bool is_exact);
+ void VisitInstanceFieldGet(HInstanceFieldGet* instr);
+ void VisitStaticFieldGet(HStaticFieldGet* instr);
void ProcessWorklist();
void AddToWorklist(HInstruction* instr);
@@ -66,8 +70,6 @@
ReferenceTypeInfo MergeTypes(const ReferenceTypeInfo& a, const ReferenceTypeInfo& b)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const DexFile& dex_file_;
- const DexCompilationUnit& dex_compilation_unit_;
StackHandleScopeCollection* handles_;
GrowableArray<HInstruction*> worklist_;
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index 5f439c8..e38e49c 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -484,8 +484,9 @@
LiveInterval* current = it.CurrentInterval();
HInstruction* defined_by = current->GetParent()->GetDefinedBy();
if (current->GetParent()->HasSpillSlot()
- // Parameters have their own stack slot.
- && !(defined_by != nullptr && defined_by->IsParameterValue())) {
+ // Parameters and current method have their own stack slot.
+ && !(defined_by != nullptr && (defined_by->IsParameterValue()
+ || defined_by->IsCurrentMethod()))) {
BitVector* liveness_of_spill_slot = liveness_of_values.Get(number_of_registers
+ current->GetParent()->GetSpillSlot() / kVRegSize
- number_of_out_slots);
@@ -713,13 +714,15 @@
if (defined_by != nullptr && !current->IsSplit()) {
LocationSummary* locations = defined_by->GetLocations();
if (!locations->OutputCanOverlapWithInputs() && locations->Out().IsUnallocated()) {
- for (HInputIterator it(defined_by); !it.Done(); it.Advance()) {
+ for (size_t i = 0, e = defined_by->InputCount(); i < e; ++i) {
// Take the last interval of the input. It is the location of that interval
// that will be used at `defined_by`.
- LiveInterval* interval = it.Current()->GetLiveInterval()->GetLastSibling();
+ LiveInterval* interval = defined_by->InputAt(i)->GetLiveInterval()->GetLastSibling();
// Note that interval may have not been processed yet.
// TODO: Handle non-split intervals last in the work list.
- if (interval->HasRegister() && interval->SameRegisterKind(*current)) {
+ if (locations->InAt(i).IsValid()
+ && interval->HasRegister()
+ && interval->SameRegisterKind(*current)) {
// The input must be live until the end of `defined_by`, to comply to
// the linear scan algorithm. So we use `defined_by`'s end lifetime
// position to check whether the input is dead or is inactive after
@@ -777,7 +780,7 @@
} else if (current->IsLowInterval()) {
reg = FindAvailableRegisterPair(free_until, current->GetStart());
} else {
- reg = FindAvailableRegister(free_until);
+ reg = FindAvailableRegister(free_until, current);
}
}
@@ -801,9 +804,9 @@
current->SetRegister(reg);
if (!current->IsDeadAt(free_until[reg])) {
// If the register is only available for a subset of live ranges
- // covered by `current`, split `current` at the position where
+ // covered by `current`, split `current` before the position where
// the register is not available anymore.
- LiveInterval* split = Split(current, free_until[reg]);
+ LiveInterval* split = SplitBetween(current, current->GetStart(), free_until[reg]);
DCHECK(split != nullptr);
AddSorted(unhandled_, split);
}
@@ -841,14 +844,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;
@@ -973,7 +1014,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]);
}
@@ -1210,6 +1251,11 @@
return;
}
+ if (defined_by->IsCurrentMethod()) {
+ parent->SetSpillSlot(0);
+ return;
+ }
+
if (defined_by->IsConstant()) {
// Constants don't need a spill slot.
return;
@@ -1483,7 +1529,10 @@
void RegisterAllocator::ConnectSiblings(LiveInterval* interval) {
LiveInterval* current = interval;
- if (current->HasSpillSlot() && current->HasRegister()) {
+ if (current->HasSpillSlot()
+ && current->HasRegister()
+ // Currently, we spill unconditionnally the current method in the code generators.
+ && !interval->GetDefinedBy()->IsCurrentMethod()) {
// We spill eagerly, so move must be at definition.
InsertMoveAfter(interval->GetDefinedBy(),
interval->ToLocation(),
@@ -1538,7 +1587,7 @@
while (env_use != nullptr && env_use->GetPosition() <= range->GetEnd()) {
DCHECK(current->CoversSlow(env_use->GetPosition())
|| (env_use->GetPosition() == range->GetEnd()));
- HEnvironment* environment = env_use->GetUser()->GetEnvironment();
+ HEnvironment* environment = env_use->GetEnvironment();
environment->SetLocationAt(env_use->GetInputIndex(), source);
env_use = env_use->GetNext();
}
@@ -1679,6 +1728,9 @@
} else if (current->HasSpillSlot()) {
current->SetSpillSlot(current->GetSpillSlot() + codegen_->GetFrameSize());
}
+ } else if (instruction->IsCurrentMethod()) {
+ // The current method is always at offset 0.
+ DCHECK(!current->HasSpillSlot() || (current->GetSpillSlot() == 0));
} else if (current->HasSpillSlot()) {
// Adjust the stack slot, now that we know the number of them for each type.
// The way this implementation lays out the stack is the following:
diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h
index 97bd777..c29fe75 100644
--- a/compiler/optimizing/register_allocator.h
+++ b/compiler/optimizing/register_allocator.h
@@ -141,7 +141,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/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index b72ffb8..b7da362 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -426,6 +426,13 @@
// Add an artifical range to cover the temps that will be put in the unhandled list.
LiveInterval* unhandled = graph->GetEntryBlock()->GetFirstInstruction()->GetLiveInterval();
unhandled->AddLoopRange(0, 60);
+
+ // Populate the instructions in the liveness object, to please the register allocator.
+ for (size_t i = 0; i < 60; ++i) {
+ liveness.instructions_from_lifetime_position_.Add(
+ graph->GetEntryBlock()->GetFirstInstruction());
+ }
+
// For SSA value intervals, only an interval resulted from a split may intersect
// with inactive intervals.
unhandled = register_allocator.Split(unhandled, 5);
@@ -474,8 +481,12 @@
graph->AddBlock(block);
entry->AddSuccessor(block);
- HInstruction* test = new (allocator) HInstanceFieldGet(
- parameter, Primitive::kPrimBoolean, MemberOffset(22), false);
+ HInstruction* test = new (allocator) HInstanceFieldGet(parameter,
+ Primitive::kPrimBoolean,
+ MemberOffset(22),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile());
block->AddInstruction(test);
block->AddInstruction(new (allocator) HIf(test));
HBasicBlock* then = new (allocator) HBasicBlock(graph);
@@ -494,10 +505,18 @@
*phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
join->AddPhi(*phi);
- *input1 = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt,
- MemberOffset(42), false);
- *input2 = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt,
- MemberOffset(42), false);
+ *input1 = new (allocator) HInstanceFieldGet(parameter,
+ Primitive::kPrimInt,
+ MemberOffset(42),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile());
+*input2 = new (allocator) HInstanceFieldGet(parameter,
+ Primitive::kPrimInt,
+ MemberOffset(42),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile());
then->AddInstruction(*input1);
else_->AddInstruction(*input2);
join->AddInstruction(new (allocator) HExit());
@@ -604,8 +623,12 @@
graph->AddBlock(block);
entry->AddSuccessor(block);
- *field = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt,
- MemberOffset(42), false);
+ *field = new (allocator) HInstanceFieldGet(parameter,
+ Primitive::kPrimInt,
+ MemberOffset(42),
+ false,
+ kUnknownFieldIndex,
+ graph->GetDexFile());
block->AddInstruction(*field);
*ret = new (allocator) HReturn(*field);
block->AddInstruction(*ret);
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 59a2852..c4612af 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,9 @@
current_locals_->Size(),
GetGraph()->GetDexFile(),
GetGraph()->GetMethodIdx(),
- instruction->GetDexPc());
+ instruction->GetDexPc(),
+ GetGraph()->GetInvokeType(),
+ instruction);
environment->CopyFrom(*current_locals_);
instruction->SetRawEnvironment(environment);
}
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index 250eb04..701dbb0 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -242,7 +242,7 @@
HInstruction* input = current->InputAt(i);
// Some instructions 'inline' their inputs, that is they do not need
// to be materialized.
- if (input->HasSsaIndex()) {
+ if (input->HasSsaIndex() && current->GetLocations()->InAt(i).IsValid()) {
live_in->SetBit(input->GetSsaIndex());
input->GetLiveInterval()->AddUse(current, /* environment */ nullptr, i);
}
@@ -341,6 +341,7 @@
// starts at. If one location is a register we return it as a hint. This
// will avoid a move between the two blocks.
HBasicBlock* block = liveness.GetBlockFromPosition(GetStart() / 2);
+ size_t next_register_use = FirstRegisterUse();
for (size_t i = 0; i < block->GetPredecessors().Size(); ++i) {
size_t position = block->GetPredecessors().Get(i)->GetLifetimeEnd() - 1;
// We know positions above GetStart() do not have a location yet.
@@ -348,7 +349,9 @@
LiveInterval* existing = GetParent()->GetSiblingAt(position);
if (existing != nullptr
&& existing->HasRegister()
- && (free_until[existing->GetRegister()] > GetStart())) {
+ // It's worth using that register if it is available until
+ // the next use.
+ && (free_until[existing->GetRegister()] >= next_register_use)) {
return existing->GetRegister();
}
}
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 82c5454..220ee6a 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 {
@@ -117,6 +117,7 @@
|| user->IsPhi()
|| (GetPosition() == user->GetLifetimePosition() + 1)
|| (GetPosition() == user->GetLifetimePosition()));
+ DCHECK(environment == nullptr || user == nullptr);
DCHECK(next_ == nullptr || next->GetPosition() >= GetPosition());
}
@@ -128,6 +129,7 @@
void SetNext(UsePosition* next) { next_ = next; }
HInstruction* GetUser() const { return user_; }
+ HEnvironment* GetEnvironment() const { return environment_; }
bool GetIsEnvironment() const { return environment_ != nullptr; }
bool IsSynthesized() const { return user_ == nullptr; }
@@ -280,7 +282,7 @@
}
DCHECK(first_use_->GetPosition() + 1 == position);
UsePosition* new_use = new (allocator_) UsePosition(
- instruction, environment, input_index, position, cursor->GetNext());
+ instruction, nullptr /* environment */, input_index, position, cursor->GetNext());
cursor->SetNext(new_use);
if (first_range_->GetEnd() == first_use_->GetPosition()) {
first_range_->end_ = position;
@@ -290,10 +292,10 @@
if (is_environment) {
first_env_use_ = new (allocator_) UsePosition(
- instruction, environment, input_index, position, first_env_use_);
+ nullptr /* instruction */, environment, input_index, position, first_env_use_);
} else {
first_use_ = new (allocator_) UsePosition(
- instruction, environment, input_index, position, first_use_);
+ instruction, nullptr /* environment */, input_index, position, first_use_);
}
if (is_environment && !keep_alive) {
@@ -392,7 +394,7 @@
first_range_->start_ = from;
} else {
// Instruction without uses.
- DCHECK(!defined_by_->HasNonEnvironmentUses());
+ DCHECK(first_use_ == nullptr);
DCHECK(from == defined_by_->GetLifetimePosition());
first_range_ = last_range_ = range_search_start_ =
new (allocator_) LiveRange(from, from + 2, nullptr);
@@ -542,6 +544,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;
@@ -1208,6 +1219,7 @@
size_t number_of_ssa_values_;
ART_FRIEND_TEST(RegisterAllocatorTest, SpillInactive);
+ ART_FRIEND_TEST(RegisterAllocatorTest, FreeUntil);
DISALLOW_COPY_AND_ASSIGN(SsaLivenessAnalysis);
};
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index 8344fc3..42b9182 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() {
@@ -106,25 +133,28 @@
stack_mask_size_ = RoundUp(stack_mask_number_of_bits, kBitsPerByte) / kBitsPerByte;
inline_info_size_ = ComputeInlineInfoSize();
dex_register_maps_size_ = ComputeDexRegisterMapsSize();
- stack_maps_size_ = stack_maps_.Size()
- * StackMap::ComputeStackMapSize(stack_mask_size_,
- inline_info_size_,
- dex_register_maps_size_,
- dex_pc_max_,
- native_pc_offset_max_,
- register_mask_max_);
+ stack_map_encoding_ = StackMapEncoding::CreateFromSizes(stack_mask_size_,
+ inline_info_size_,
+ dex_register_maps_size_,
+ dex_pc_max_,
+ native_pc_offset_max_,
+ register_mask_max_);
+ stack_maps_size_ = stack_maps_.Size() * stack_map_encoding_.ComputeStackMapSize();
dex_register_location_catalog_size_ = ComputeDexRegisterLocationCatalogSize();
// Note: use RoundUp to word-size here if you want CodeInfo objects to be word aligned.
needed_size_ = CodeInfo::kFixedSize
- + dex_register_location_catalog_size_
+ stack_maps_size_
+ + dex_register_location_catalog_size_
+ dex_register_maps_size_
+ inline_info_size_;
- dex_register_location_catalog_start_ = CodeInfo::kFixedSize;
- stack_maps_start_ = dex_register_location_catalog_start_ + dex_register_location_catalog_size_;
- dex_register_maps_start_ = stack_maps_start_ + stack_maps_size_;
+ stack_maps_start_ = CodeInfo::kFixedSize;
+ // TODO: Move the catalog at the end. It is currently too expensive at runtime
+ // to compute its size (note that we do not encode that size in the CodeInfo).
+ dex_register_location_catalog_start_ = stack_maps_start_ + stack_maps_size_;
+ dex_register_maps_start_ =
+ dex_register_location_catalog_start_ + dex_register_location_catalog_size_;
inline_infos_start_ = dex_register_maps_start_ + dex_register_maps_size_;
return needed_size_;
@@ -142,17 +172,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 +198,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;
@@ -197,14 +235,9 @@
MemoryRegion inline_infos_region = region.Subregion(
inline_infos_start_, inline_info_size_);
- code_info.SetEncoding(inline_info_size_,
- dex_register_maps_size_,
- dex_pc_max_,
- native_pc_offset_max_,
- register_mask_max_);
+ code_info.SetEncoding(stack_map_encoding_);
code_info.SetNumberOfStackMaps(stack_maps_.Size());
- code_info.SetStackMaskSize(stack_mask_size_);
- DCHECK_EQ(code_info.GetStackMapsSize(), stack_maps_size_);
+ DCHECK_EQ(code_info.GetStackMapsSize(code_info.ExtractEncoding()), stack_maps_size_);
// Set the Dex register location catalog.
code_info.SetNumberOfDexRegisterLocationCatalogEntries(location_catalog_entries_.Size());
@@ -225,56 +258,42 @@
uintptr_t next_dex_register_map_offset = 0;
uintptr_t next_inline_info_offset = 0;
for (size_t i = 0, e = stack_maps_.Size(); i < e; ++i) {
- StackMap stack_map = code_info.GetStackMapAt(i);
+ StackMap stack_map = code_info.GetStackMapAt(i, stack_map_encoding_);
StackMapEntry entry = stack_maps_.Get(i);
- stack_map.SetDexPc(code_info, entry.dex_pc);
- stack_map.SetNativePcOffset(code_info, entry.native_pc_offset);
- stack_map.SetRegisterMask(code_info, entry.register_mask);
+ stack_map.SetDexPc(stack_map_encoding_, entry.dex_pc);
+ stack_map.SetNativePcOffset(stack_map_encoding_, entry.native_pc_offset);
+ stack_map.SetRegisterMask(stack_map_encoding_, entry.register_mask);
if (entry.sp_mask != nullptr) {
- stack_map.SetStackMask(code_info, *entry.sp_mask);
+ stack_map.SetStackMask(stack_map_encoding_, *entry.sp_mask);
}
if (entry.num_dex_registers == 0) {
// No dex map available.
- stack_map.SetDexRegisterMapOffset(code_info, StackMap::kNoDexRegisterMap);
+ stack_map.SetDexRegisterMapOffset(stack_map_encoding_, StackMap::kNoDexRegisterMap);
} else {
// Search for an entry with the same dex map.
if (entry.same_dex_register_map_as_ != kNoSameDexMapFound) {
// If we have a hit reuse the offset.
- stack_map.SetDexRegisterMapOffset(code_info,
- code_info.GetStackMapAt(entry.same_dex_register_map_as_)
- .GetDexRegisterMapOffset(code_info));
+ stack_map.SetDexRegisterMapOffset(
+ stack_map_encoding_,
+ code_info.GetStackMapAt(entry.same_dex_register_map_as_, stack_map_encoding_)
+ .GetDexRegisterMapOffset(stack_map_encoding_));
} 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());
+ stack_map_encoding_, 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);
}
}
@@ -288,21 +307,64 @@
// Currently relative to the dex register map.
stack_map.SetInlineDescriptorOffset(
- code_info, inline_region.start() - dex_register_locations_region.start());
+ stack_map_encoding_, 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) {
- stack_map.SetInlineDescriptorOffset(code_info, StackMap::kNoInlineInfo);
+ stack_map.SetInlineDescriptorOffset(stack_map_encoding_, StackMap::kNoInlineInfo);
}
}
}
}
+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..274d573 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,8 @@
ArenaSafeMap<uint32_t, GrowableArray<uint32_t>> dex_map_hash_to_stack_map_indices_;
StackMapEntry current_entry_;
+ InlineInfoEntry current_inline_info_;
+ StackMapEncoding stack_map_encoding_;
size_t stack_mask_size_;
size_t inline_info_size_;
size_t dex_register_maps_size_;
@@ -165,6 +182,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..b4ac1b4 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();
@@ -51,32 +51,33 @@
stream.FillIn(region);
CodeInfo code_info(region);
- ASSERT_EQ(0u, code_info.GetStackMaskSize());
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ ASSERT_EQ(0u, encoding.NumberOfBytesForStackMask());
ASSERT_EQ(1u, code_info.GetNumberOfStackMaps());
uint32_t number_of_location_catalog_entries =
code_info.GetNumberOfDexRegisterLocationCatalogEntries();
ASSERT_EQ(2u, number_of_location_catalog_entries);
- DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog();
+ DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding);
// The Dex register location catalog contains:
// - one 1-byte short Dex register location, and
// - one 5-byte large Dex register location.
size_t expected_location_catalog_size = 1u + 5u;
ASSERT_EQ(expected_location_catalog_size, location_catalog.Size());
- StackMap stack_map = code_info.GetStackMapAt(0);
- ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
- ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
- ASSERT_EQ(0u, stack_map.GetDexPc(code_info));
- ASSERT_EQ(64u, stack_map.GetNativePcOffset(code_info));
- ASSERT_EQ(0x3u, stack_map.GetRegisterMask(code_info));
+ StackMap stack_map = code_info.GetStackMapAt(0, encoding);
+ ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding)));
+ ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding)));
+ ASSERT_EQ(0u, stack_map.GetDexPc(encoding));
+ ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding));
+ ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding));
- MemoryRegion stack_mask = stack_map.GetStackMask(code_info);
+ MemoryRegion stack_mask = stack_map.GetStackMask(encoding);
ASSERT_TRUE(SameBits(stack_mask, sp_mask));
- ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info));
+ ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding));
DexRegisterMap dex_register_map =
- code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
+ code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers);
ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0));
ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers));
@@ -86,16 +87,17 @@
size_t expected_dex_register_map_size = 1u + 1u;
ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size());
- ASSERT_EQ(Kind::kInStack,
- dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info));
- ASSERT_EQ(Kind::kConstant,
- dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info));
- ASSERT_EQ(Kind::kInStack,
- dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info));
- ASSERT_EQ(Kind::kConstantLargeValue,
- dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info));
- ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info));
- ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info));
+ ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationKind(
+ 0, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind(
+ 1, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationInternalKind(
+ 0, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind(
+ 1, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes(
+ 0, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info, encoding));
size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(
0, number_of_dex_registers, number_of_location_catalog_entries);
@@ -112,7 +114,7 @@
ASSERT_EQ(0, location0.GetValue());
ASSERT_EQ(-2, location1.GetValue());
- ASSERT_FALSE(stack_map.HasInlineInfo(code_info));
+ ASSERT_FALSE(stack_map.HasInlineInfo(encoding));
}
TEST(StackMapTest, Test2) {
@@ -124,19 +126,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);
+ sp_mask2.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();
@@ -145,13 +150,14 @@
stream.FillIn(region);
CodeInfo code_info(region);
- ASSERT_EQ(1u, code_info.GetStackMaskSize());
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ ASSERT_EQ(2u, encoding.NumberOfBytesForStackMask());
ASSERT_EQ(2u, code_info.GetNumberOfStackMaps());
uint32_t number_of_location_catalog_entries =
code_info.GetNumberOfDexRegisterLocationCatalogEntries();
ASSERT_EQ(4u, number_of_location_catalog_entries);
- DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog();
+ DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding);
// The Dex register location catalog contains:
// - three 1-byte short Dex register locations, and
// - one 5-byte large Dex register location.
@@ -160,19 +166,19 @@
// First stack map.
{
- StackMap stack_map = code_info.GetStackMapAt(0);
- ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
- ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
- ASSERT_EQ(0u, stack_map.GetDexPc(code_info));
- ASSERT_EQ(64u, stack_map.GetNativePcOffset(code_info));
- ASSERT_EQ(0x3u, stack_map.GetRegisterMask(code_info));
+ StackMap stack_map = code_info.GetStackMapAt(0, encoding);
+ ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding)));
+ ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding)));
+ ASSERT_EQ(0u, stack_map.GetDexPc(encoding));
+ ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding));
+ ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding));
- MemoryRegion stack_mask = stack_map.GetStackMask(code_info);
+ MemoryRegion stack_mask = stack_map.GetStackMask(encoding);
ASSERT_TRUE(SameBits(stack_mask, sp_mask1));
- ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info));
+ ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding));
DexRegisterMap dex_register_map =
- code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
+ code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers);
ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0));
ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers));
@@ -182,16 +188,17 @@
size_t expected_dex_register_map_size = 1u + 1u;
ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size());
- ASSERT_EQ(Kind::kInStack,
- dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info));
- ASSERT_EQ(Kind::kConstant,
- dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info));
- ASSERT_EQ(Kind::kInStack,
- dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info));
- ASSERT_EQ(Kind::kConstantLargeValue,
- dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info));
- ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info));
- ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info));
+ ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationKind(
+ 0, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind(
+ 1, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(Kind::kInStack, dex_register_map.GetLocationInternalKind(
+ 0, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind(
+ 1, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes(
+ 0, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info, encoding));
size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(
0, number_of_dex_registers, number_of_location_catalog_entries);
@@ -208,28 +215,32 @@
ASSERT_EQ(0, location0.GetValue());
ASSERT_EQ(-2, location1.GetValue());
- ASSERT_TRUE(stack_map.HasInlineInfo(code_info));
- InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map);
+ ASSERT_TRUE(stack_map.HasInlineInfo(encoding));
+ InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
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.
{
- StackMap stack_map = code_info.GetStackMapAt(1);
- ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u)));
- ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u)));
- ASSERT_EQ(1u, stack_map.GetDexPc(code_info));
- ASSERT_EQ(128u, stack_map.GetNativePcOffset(code_info));
- ASSERT_EQ(0xFFu, stack_map.GetRegisterMask(code_info));
+ StackMap stack_map = code_info.GetStackMapAt(1, encoding);
+ ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u, encoding)));
+ ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u, encoding)));
+ ASSERT_EQ(1u, stack_map.GetDexPc(encoding));
+ ASSERT_EQ(128u, stack_map.GetNativePcOffset(encoding));
+ ASSERT_EQ(0xFFu, stack_map.GetRegisterMask(encoding));
- MemoryRegion stack_mask = stack_map.GetStackMask(code_info);
+ MemoryRegion stack_mask = stack_map.GetStackMask(encoding);
ASSERT_TRUE(SameBits(stack_mask, sp_mask2));
- ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info));
+ ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding));
DexRegisterMap dex_register_map =
- code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
+ code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers);
ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0));
ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers));
@@ -239,16 +250,18 @@
size_t expected_dex_register_map_size = 1u + 1u;
ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size());
- ASSERT_EQ(Kind::kInRegister,
- dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info));
- ASSERT_EQ(Kind::kInFpuRegister,
- dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info));
- ASSERT_EQ(Kind::kInRegister,
- dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info));
- ASSERT_EQ(Kind::kInFpuRegister,
- dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info));
- ASSERT_EQ(18, dex_register_map.GetMachineRegister(0, number_of_dex_registers, code_info));
- ASSERT_EQ(3, dex_register_map.GetMachineRegister(1, number_of_dex_registers, code_info));
+ ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationKind(
+ 0, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationKind(
+ 1, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(Kind::kInRegister, dex_register_map.GetLocationInternalKind(
+ 0, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(Kind::kInFpuRegister, dex_register_map.GetLocationInternalKind(
+ 1, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(18, dex_register_map.GetMachineRegister(
+ 0, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(3, dex_register_map.GetMachineRegister(
+ 1, number_of_dex_registers, code_info, encoding));
size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(
0, number_of_dex_registers, number_of_location_catalog_entries);
@@ -265,7 +278,7 @@
ASSERT_EQ(18, location0.GetValue());
ASSERT_EQ(3, location1.GetValue());
- ASSERT_FALSE(stack_map.HasInlineInfo(code_info));
+ ASSERT_FALSE(stack_map.HasInlineInfo(encoding));
}
}
@@ -277,8 +290,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();
@@ -287,28 +300,29 @@
stream.FillIn(region);
CodeInfo code_info(region);
- ASSERT_EQ(0u, code_info.GetStackMaskSize());
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ ASSERT_EQ(0u, encoding.NumberOfBytesForStackMask());
ASSERT_EQ(1u, code_info.GetNumberOfStackMaps());
uint32_t number_of_location_catalog_entries =
code_info.GetNumberOfDexRegisterLocationCatalogEntries();
ASSERT_EQ(1u, number_of_location_catalog_entries);
- DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog();
+ DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding);
// The Dex register location catalog contains:
// - one 5-byte large Dex register location.
size_t expected_location_catalog_size = 5u;
ASSERT_EQ(expected_location_catalog_size, location_catalog.Size());
- StackMap stack_map = code_info.GetStackMapAt(0);
- ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
- ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
- ASSERT_EQ(0u, stack_map.GetDexPc(code_info));
- ASSERT_EQ(64u, stack_map.GetNativePcOffset(code_info));
- ASSERT_EQ(0x3u, stack_map.GetRegisterMask(code_info));
+ StackMap stack_map = code_info.GetStackMapAt(0, encoding);
+ ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding)));
+ ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding)));
+ ASSERT_EQ(0u, stack_map.GetDexPc(encoding));
+ ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding));
+ ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding));
- ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info));
+ ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding));
DexRegisterMap dex_register_map =
- code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
+ code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers);
ASSERT_FALSE(dex_register_map.IsDexRegisterLive(0));
ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1));
ASSERT_EQ(1u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers));
@@ -318,15 +332,15 @@
size_t expected_dex_register_map_size = 1u + 0u;
ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size());
- ASSERT_EQ(Kind::kNone,
- dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info));
- ASSERT_EQ(Kind::kConstant,
- dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info));
- ASSERT_EQ(Kind::kNone,
- dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info));
- ASSERT_EQ(Kind::kConstantLargeValue,
- dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info));
- ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info));
+ ASSERT_EQ(Kind::kNone, dex_register_map.GetLocationKind(
+ 0, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(Kind::kConstant, dex_register_map.GetLocationKind(
+ 1, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(Kind::kNone, dex_register_map.GetLocationInternalKind(
+ 0, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(Kind::kConstantLargeValue, dex_register_map.GetLocationInternalKind(
+ 1, number_of_dex_registers, code_info, encoding));
+ ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info, encoding));
size_t index0 = dex_register_map.GetLocationCatalogEntryIndex(
0, number_of_dex_registers, number_of_location_catalog_entries);
@@ -343,7 +357,7 @@
ASSERT_EQ(0, location0.GetValue());
ASSERT_EQ(-2, location1.GetValue());
- ASSERT_FALSE(stack_map.HasInlineInfo(code_info));
+ ASSERT_FALSE(stack_map.HasInlineInfo(encoding));
}
// Generate a stack map whose dex register offset is
@@ -364,13 +378,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();
@@ -380,6 +394,7 @@
stream.FillIn(region);
CodeInfo code_info(region);
+ StackMapEncoding encoding = code_info.ExtractEncoding();
// The location catalog contains two entries (DexRegisterLocation(kConstant, 0)
// and DexRegisterLocation(kConstant, 1)), therefore the location catalog index
// has a size of 1 bit.
@@ -395,20 +410,20 @@
// locations (that is, 127 bytes of data).
// Hence it has a size of 255 bytes, and therefore...
ASSERT_EQ(128u, DexRegisterMap::GetLiveBitMaskSize(number_of_dex_registers));
- StackMap stack_map0 = code_info.GetStackMapAt(0);
+ StackMap stack_map0 = code_info.GetStackMapAt(0, encoding);
DexRegisterMap dex_register_map0 =
- code_info.GetDexRegisterMapOf(stack_map0, number_of_dex_registers);
+ code_info.GetDexRegisterMapOf(stack_map0, encoding, number_of_dex_registers);
ASSERT_EQ(127u, dex_register_map0.GetLocationMappingDataSize(number_of_dex_registers,
number_of_location_catalog_entries));
ASSERT_EQ(255u, dex_register_map0.Size());
- StackMap stack_map1 = code_info.GetStackMapAt(1);
- ASSERT_TRUE(stack_map1.HasDexRegisterMap(code_info));
+ StackMap stack_map1 = code_info.GetStackMapAt(1, encoding);
+ ASSERT_TRUE(stack_map1.HasDexRegisterMap(encoding));
// ...the offset of the second Dex register map (relative to the
// beginning of the Dex register maps region) is 255 (i.e.,
// kNoDexRegisterMapSmallEncoding).
- ASSERT_NE(stack_map1.GetDexRegisterMapOffset(code_info), StackMap::kNoDexRegisterMap);
- ASSERT_EQ(stack_map1.GetDexRegisterMapOffset(code_info), 0xFFu);
+ ASSERT_NE(stack_map1.GetDexRegisterMapOffset(encoding), StackMap::kNoDexRegisterMap);
+ ASSERT_EQ(stack_map1.GetDexRegisterMapOffset(encoding), 0xFFu);
}
TEST(StackMapTest, TestShareDexRegisterMap) {
@@ -420,18 +435,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();
@@ -440,28 +455,30 @@
stream.FillIn(region);
CodeInfo ci(region);
+ StackMapEncoding encoding = ci.ExtractEncoding();
+
// Verify first stack map.
- StackMap sm0 = ci.GetStackMapAt(0);
- DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, number_of_dex_registers);
- ASSERT_EQ(0, dex_registers0.GetMachineRegister(0, number_of_dex_registers, ci));
- ASSERT_EQ(-2, dex_registers0.GetConstant(1, number_of_dex_registers, ci));
+ StackMap sm0 = ci.GetStackMapAt(0, encoding);
+ DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, encoding, number_of_dex_registers);
+ ASSERT_EQ(0, dex_registers0.GetMachineRegister(0, number_of_dex_registers, ci, encoding));
+ ASSERT_EQ(-2, dex_registers0.GetConstant(1, number_of_dex_registers, ci, encoding));
// Verify second stack map.
- StackMap sm1 = ci.GetStackMapAt(1);
- DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1, number_of_dex_registers);
- ASSERT_EQ(0, dex_registers1.GetMachineRegister(0, number_of_dex_registers, ci));
- ASSERT_EQ(-2, dex_registers1.GetConstant(1, number_of_dex_registers, ci));
+ StackMap sm1 = ci.GetStackMapAt(1, encoding);
+ DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1, encoding, number_of_dex_registers);
+ ASSERT_EQ(0, dex_registers1.GetMachineRegister(0, number_of_dex_registers, ci, encoding));
+ ASSERT_EQ(-2, dex_registers1.GetConstant(1, number_of_dex_registers, ci, encoding));
// Verify third stack map.
- StackMap sm2 = ci.GetStackMapAt(2);
- DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2, number_of_dex_registers);
- ASSERT_EQ(2, dex_registers2.GetMachineRegister(0, number_of_dex_registers, ci));
- ASSERT_EQ(-2, dex_registers2.GetConstant(1, number_of_dex_registers, ci));
+ StackMap sm2 = ci.GetStackMapAt(2, encoding);
+ DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2, encoding, number_of_dex_registers);
+ ASSERT_EQ(2, dex_registers2.GetMachineRegister(0, number_of_dex_registers, ci, encoding));
+ ASSERT_EQ(-2, dex_registers2.GetConstant(1, number_of_dex_registers, ci, encoding));
// Verify dex register map offsets.
- ASSERT_EQ(sm0.GetDexRegisterMapOffset(ci), sm1.GetDexRegisterMapOffset(ci));
- ASSERT_NE(sm0.GetDexRegisterMapOffset(ci), sm2.GetDexRegisterMapOffset(ci));
- ASSERT_NE(sm1.GetDexRegisterMapOffset(ci), sm2.GetDexRegisterMapOffset(ci));
+ ASSERT_EQ(sm0.GetDexRegisterMapOffset(encoding), sm1.GetDexRegisterMapOffset(encoding));
+ ASSERT_NE(sm0.GetDexRegisterMapOffset(encoding), sm2.GetDexRegisterMapOffset(encoding));
+ ASSERT_NE(sm1.GetDexRegisterMapOffset(encoding), sm2.GetDexRegisterMapOffset(encoding));
}
TEST(StackMapTest, TestNoDexRegisterMap) {
@@ -480,24 +497,197 @@
stream.FillIn(region);
CodeInfo code_info(region);
- ASSERT_EQ(0u, code_info.GetStackMaskSize());
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ ASSERT_EQ(0u, encoding.NumberOfBytesForStackMask());
ASSERT_EQ(1u, code_info.GetNumberOfStackMaps());
uint32_t number_of_location_catalog_entries =
code_info.GetNumberOfDexRegisterLocationCatalogEntries();
ASSERT_EQ(0u, number_of_location_catalog_entries);
- DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog();
+ DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding);
ASSERT_EQ(0u, location_catalog.Size());
- StackMap stack_map = code_info.GetStackMapAt(0);
- ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
- ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
- ASSERT_EQ(0u, stack_map.GetDexPc(code_info));
- ASSERT_EQ(64u, stack_map.GetNativePcOffset(code_info));
- ASSERT_EQ(0x3u, stack_map.GetRegisterMask(code_info));
+ StackMap stack_map = code_info.GetStackMapAt(0, encoding);
+ ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding)));
+ ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding)));
+ ASSERT_EQ(0u, stack_map.GetDexPc(encoding));
+ ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding));
+ ASSERT_EQ(0x3u, stack_map.GetRegisterMask(encoding));
- ASSERT_FALSE(stack_map.HasDexRegisterMap(code_info));
- ASSERT_FALSE(stack_map.HasInlineInfo(code_info));
+ ASSERT_FALSE(stack_map.HasDexRegisterMap(encoding));
+ ASSERT_FALSE(stack_map.HasInlineInfo(encoding));
+}
+
+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);
+ StackMapEncoding encoding = ci.ExtractEncoding();
+
+ {
+ // Verify first stack map.
+ StackMap sm0 = ci.GetStackMapAt(0, encoding);
+
+ DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, encoding, 2);
+ ASSERT_EQ(0, dex_registers0.GetStackOffsetInBytes(0, 2, ci, encoding));
+ ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci, encoding));
+
+ InlineInfo if0 = ci.GetInlineInfoOf(sm0, encoding);
+ 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, encoding, 1);
+ ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding));
+
+ DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if0, encoding, 3);
+ ASSERT_EQ(16, dex_registers2.GetStackOffsetInBytes(0, 3, ci, encoding));
+ ASSERT_EQ(20, dex_registers2.GetConstant(1, 3, ci, encoding));
+ ASSERT_EQ(15, dex_registers2.GetMachineRegister(2, 3, ci, encoding));
+ }
+
+ {
+ // Verify second stack map.
+ StackMap sm1 = ci.GetStackMapAt(1, encoding);
+
+ DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm1, encoding, 2);
+ ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci, encoding));
+ ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci, encoding));
+
+ InlineInfo if1 = ci.GetInlineInfoOf(sm1, encoding);
+ 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, encoding, 1);
+ ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding));
+
+ DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(1, if1, encoding, 3);
+ ASSERT_EQ(80, dex_registers2.GetStackOffsetInBytes(0, 3, ci, encoding));
+ ASSERT_EQ(10, dex_registers2.GetConstant(1, 3, ci, encoding));
+ ASSERT_EQ(5, dex_registers2.GetMachineRegister(2, 3, ci, encoding));
+
+ ASSERT_FALSE(if1.HasDexRegisterMapAtDepth(2));
+ }
+
+ {
+ // Verify third stack map.
+ StackMap sm2 = ci.GetStackMapAt(2, encoding);
+
+ DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm2, encoding, 2);
+ ASSERT_FALSE(dex_registers0.IsDexRegisterLive(0));
+ ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci, encoding));
+ ASSERT_FALSE(sm2.HasInlineInfo(encoding));
+ }
+
+ {
+ // Verify fourth stack map.
+ StackMap sm3 = ci.GetStackMapAt(3, encoding);
+
+ DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm3, encoding, 2);
+ ASSERT_EQ(56, dex_registers0.GetStackOffsetInBytes(0, 2, ci, encoding));
+ ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci, encoding));
+
+ InlineInfo if2 = ci.GetInlineInfoOf(sm3, encoding);
+ 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, encoding, 1);
+ ASSERT_EQ(2, dex_registers1.GetMachineRegister(0, 1, ci, encoding));
+
+ DexRegisterMap dex_registers2 = ci.GetDexRegisterMapAtDepth(2, if2, encoding, 2);
+ ASSERT_FALSE(dex_registers2.IsDexRegisterLive(0));
+ ASSERT_EQ(3, dex_registers2.GetMachineRegister(1, 2, ci, encoding));
+ }
}
} // namespace art
diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc
index cbbc116..0086fe8 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 c673c6b..350efca 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -33,6 +33,16 @@
class Arm32Assembler;
class Thumb2Assembler;
+// This class indicates that the label and its uses
+// will fall into a range that is encodable in 16bits on thumb2.
+class NearLabel : public Label {
+ public:
+ NearLabel() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NearLabel);
+};
+
class ShifterOperand {
public:
ShifterOperand() : type_(kUnknown), rm_(kNoRegister), rs_(kNoRegister),
@@ -398,6 +408,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;
@@ -517,6 +529,9 @@
// Branch instructions.
virtual void b(Label* label, Condition cond = AL) = 0;
+ virtual void b(NearLabel* label, Condition cond = AL) {
+ b(static_cast<Label*>(label), cond);
+ }
virtual void bl(Label* label, Condition cond = AL) = 0;
virtual void blx(Register rm, Condition cond = AL) = 0;
virtual void bx(Register rm, Condition cond = AL) = 0;
@@ -652,6 +667,9 @@
virtual void Bind(Label* label) = 0;
virtual void CompareAndBranchIfZero(Register r, Label* label) = 0;
+ virtual void CompareAndBranchIfZero(Register r, NearLabel* label) {
+ CompareAndBranchIfZero(r, static_cast<Label*>(label));
+ }
virtual void CompareAndBranchIfNonZero(Register r, Label* label) = 0;
//
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index 1c8ea42..cdf62bf 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 80582e5..3164623 100644
--- a/compiler/utils/arm/assembler_arm32.h
+++ b/compiler/utils/arm/assembler_arm32.h
@@ -89,6 +89,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;
@@ -198,8 +200,8 @@
void vpopd(DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
// Branch instructions.
- void b(Label* label, Condition cond = AL);
- void bl(Label* label, Condition cond = AL);
+ void b(Label* label, Condition cond = AL) OVERRIDE;
+ void bl(Label* label, Condition cond = AL) OVERRIDE;
void blx(Register rm, Condition cond = AL) OVERRIDE;
void bx(Register rm, Condition cond = AL) OVERRIDE;
void Lsl(Register rd, Register rm, uint32_t shift_imm, bool setcc = false,
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 85f6670..26cb6c3 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);
@@ -653,11 +671,17 @@
EmitVFPddd(cond, B23 | B21 | B20 | B18 | B16 | B6, dd, D0, D0);
}
+
void Thumb2Assembler::b(Label* label, Condition cond) {
EmitBranch(cond, label, false, false);
}
+void Thumb2Assembler::b(NearLabel* label, Condition cond) {
+ EmitBranch(cond, label, false, false, /* is_near */ true);
+}
+
+
void Thumb2Assembler::bl(Label* label, Condition cond) {
CheckCondition(cond);
EmitBranch(cond, label, true, false);
@@ -740,13 +764,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 +809,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;
}
@@ -1345,6 +1375,7 @@
uint16_t Thumb2Assembler::EmitCompareAndBranch(Register rn, uint16_t prev, bool n) {
+ CHECK(IsLowRegister(rn));
uint32_t location = buffer_.Size();
// This is always unresolved as it must be a forward branch.
@@ -1589,7 +1620,7 @@
}
-void Thumb2Assembler::EmitBranch(Condition cond, Label* label, bool link, bool x) {
+void Thumb2Assembler::EmitBranch(Condition cond, Label* label, bool link, bool x, bool is_near) {
uint32_t pc = buffer_.Size();
Branch::Type branch_type;
if (cond == AL) {
@@ -1614,15 +1645,14 @@
// 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.
}
} 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_) {
+ uint16_t branch_id = AddBranch(branch_type, pc, cond, is_near); // Unresolved branch.
+ if (force_32bit_ || (!CanRelocateBranches() && !is_near)) {
Emit16(static_cast<uint16_t>(label->position_)); // Emit current label link.
Emit16(0); // another 16 bits.
} else {
@@ -2176,6 +2206,9 @@
if (label->IsBound()) {
LOG(FATAL) << "cbz can only be used to branch forwards";
UNREACHABLE();
+ } else if (IsHighRegister(rn)) {
+ LOG(FATAL) << "cbz can only be used with low registers";
+ UNREACHABLE();
} else {
uint16_t branchid = EmitCompareAndBranch(rn, static_cast<uint16_t>(label->position_), false);
label->LinkTo(branchid);
@@ -2188,6 +2221,9 @@
if (label->IsBound()) {
LOG(FATAL) << "cbnz can only be used to branch forwards";
UNREACHABLE();
+ } else if (IsHighRegister(rn)) {
+ LOG(FATAL) << "cbnz can only be used with low registers";
+ UNREACHABLE();
} else {
uint16_t branchid = EmitCompareAndBranch(rn, static_cast<uint16_t>(label->position_), true);
label->LinkTo(branchid);
@@ -2258,7 +2294,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 +2754,31 @@
void Thumb2Assembler::CompareAndBranchIfZero(Register r, Label* label) {
- if (force_32bit_branches_) {
+ if (CanRelocateBranches() && IsLowRegister(r)) {
+ cbz(r, label);
+ } else {
cmp(r, ShifterOperand(0));
b(label, EQ);
- } else {
+ }
+}
+
+
+void Thumb2Assembler::CompareAndBranchIfZero(Register r, NearLabel* label) {
+ if (IsLowRegister(r)) {
cbz(r, label);
+ } else {
+ cmp(r, ShifterOperand(0));
+ b(label, EQ);
}
}
void Thumb2Assembler::CompareAndBranchIfNonZero(Register r, Label* label) {
- if (force_32bit_branches_) {
+ if (CanRelocateBranches() && IsLowRegister(r)) {
+ 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 05caeb3..2382b74 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -30,8 +30,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) {
@@ -51,8 +51,8 @@
return force_32bit_;
}
- bool IsForced32BitBranches() const {
- return force_32bit_branches_;
+ bool CanRelocateBranches() const {
+ return can_relocate_branches_;
}
void FinalizeInstructions(const MemoryRegion& region) OVERRIDE {
@@ -111,6 +111,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;
@@ -236,6 +238,7 @@
// Branch instructions.
void b(Label* label, Condition cond = AL);
+ void b(NearLabel* label, Condition cond = AL);
void bl(Label* label, Condition cond = AL);
void blx(Label* label);
void blx(Register rm, Condition cond = AL) OVERRIDE;
@@ -270,6 +273,7 @@
void Mov(Register rd, Register rm, Condition cond = AL) OVERRIDE;
void CompareAndBranchIfZero(Register r, Label* label) OVERRIDE;
+ void CompareAndBranchIfZero(Register r, NearLabel* label) OVERRIDE;
void CompareAndBranchIfNonZero(Register r, Label* label) OVERRIDE;
// Memory barriers.
@@ -428,7 +432,7 @@
void EmitVPushPop(uint32_t reg, int nregs, bool push, bool dbl, Condition cond);
- void EmitBranch(Condition cond, Label* label, bool link, bool x);
+ void EmitBranch(Condition cond, Label* label, bool link, bool x, bool is_near = false);
static int32_t EncodeBranchOffset(int32_t offset, int32_t inst);
static int DecodeBranchOffset(int32_t inst);
int32_t EncodeTstOffset(int offset, int32_t inst);
@@ -436,8 +440,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];
@@ -552,13 +560,26 @@
// Resolve a branch when the target is known. If this causes the
// size of the branch to change return true. Otherwise return false.
bool Resolve(uint32_t target) {
+ uint32_t old_target = 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) {
+ if (old_target == kUnresolved) {
+ // Check that the size has not increased.
+ DCHECK(!(CalculateSize() == k32Bit && size_ == k16Bit));
+ } else {
+ DCHECK(CalculateSize() == size_);
+ }
+ }
+ return false;
}
- return false;
}
// Move a cbz/cbnz branch. This is always forward.
@@ -574,6 +595,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;
}
@@ -586,9 +608,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;
@@ -630,15 +652,17 @@
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;
+ if (IsCompareAndBranch()) {
+ // Compare and branch instructions can only be encoded on 16 bits.
+ 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;
@@ -699,8 +723,15 @@
}
// Add an unresolved branch and return its id.
- uint16_t AddBranch(Branch::Type type, uint32_t location, Condition cond = AL) {
- branches_.push_back(new Branch(this, type, location, cond));
+ uint16_t AddBranch(Branch::Type type,
+ uint32_t location,
+ Condition cond = AL,
+ bool is_near = false) {
+ Branch* branch = new Branch(this, type, location, cond);
+ if (is_near) {
+ branch->ResetSize(Branch::k16Bit);
+ }
+ branches_.push_back(branch);
return branches_.size() - 1;
}
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/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc
index 7d98a30..cc78002 100644
--- a/compiler/utils/arm64/assembler_arm64.cc
+++ b/compiler/utils/arm64/assembler_arm64.cc
@@ -51,11 +51,11 @@
}
void Arm64Assembler::GetCurrentThread(ManagedRegister tr) {
- ___ Mov(reg_x(tr.AsArm64().AsXRegister()), reg_x(ETR));
+ ___ Mov(reg_x(tr.AsArm64().AsXRegister()), reg_x(TR));
}
void Arm64Assembler::GetCurrentThread(FrameOffset offset, ManagedRegister /* scratch */) {
- StoreToOffset(ETR, SP, offset.Int32Value());
+ StoreToOffset(TR, SP, offset.Int32Value());
}
// See Arm64 PCS Section 5.2.2.1.
@@ -167,7 +167,7 @@
Arm64ManagedRegister scratch = m_scratch.AsArm64();
CHECK(scratch.IsXRegister()) << scratch;
LoadImmediate(scratch.AsXRegister(), imm);
- StoreToOffset(scratch.AsXRegister(), ETR, offs.Int32Value());
+ StoreToOffset(scratch.AsXRegister(), TR, offs.Int32Value());
}
void Arm64Assembler::StoreStackOffsetToThread64(ThreadOffset<8> tr_offs,
@@ -176,14 +176,14 @@
Arm64ManagedRegister scratch = m_scratch.AsArm64();
CHECK(scratch.IsXRegister()) << scratch;
AddConstant(scratch.AsXRegister(), SP, fr_offs.Int32Value());
- StoreToOffset(scratch.AsXRegister(), ETR, tr_offs.Int32Value());
+ StoreToOffset(scratch.AsXRegister(), TR, tr_offs.Int32Value());
}
void Arm64Assembler::StoreStackPointerToThread64(ThreadOffset<8> tr_offs) {
vixl::UseScratchRegisterScope temps(vixl_masm_);
vixl::Register temp = temps.AcquireX();
___ Mov(temp, reg_x(SP));
- ___ Str(temp, MEM_OP(reg_x(ETR), tr_offs.Int32Value()));
+ ___ Str(temp, MEM_OP(reg_x(TR), tr_offs.Int32Value()));
}
void Arm64Assembler::StoreSpanning(FrameOffset dest_off, ManagedRegister m_source,
@@ -284,7 +284,7 @@
}
void Arm64Assembler::LoadFromThread64(ManagedRegister m_dst, ThreadOffset<8> src, size_t size) {
- return Load(m_dst.AsArm64(), ETR, src.Int32Value(), size);
+ return Load(m_dst.AsArm64(), TR, src.Int32Value(), size);
}
void Arm64Assembler::LoadRef(ManagedRegister m_dst, FrameOffset offs) {
@@ -319,7 +319,7 @@
void Arm64Assembler::LoadRawPtrFromThread64(ManagedRegister m_dst, ThreadOffset<8> offs) {
Arm64ManagedRegister dst = m_dst.AsArm64();
CHECK(dst.IsXRegister()) << dst;
- LoadFromOffset(dst.AsXRegister(), ETR, offs.Int32Value());
+ LoadFromOffset(dst.AsXRegister(), TR, offs.Int32Value());
}
// Copying routines.
@@ -357,7 +357,7 @@
ManagedRegister m_scratch) {
Arm64ManagedRegister scratch = m_scratch.AsArm64();
CHECK(scratch.IsXRegister()) << scratch;
- LoadFromOffset(scratch.AsXRegister(), ETR, tr_offs.Int32Value());
+ LoadFromOffset(scratch.AsXRegister(), TR, tr_offs.Int32Value());
StoreToOffset(scratch.AsXRegister(), SP, fr_offs.Int32Value());
}
@@ -367,7 +367,7 @@
Arm64ManagedRegister scratch = m_scratch.AsArm64();
CHECK(scratch.IsXRegister()) << scratch;
LoadFromOffset(scratch.AsXRegister(), SP, fr_offs.Int32Value());
- StoreToOffset(scratch.AsXRegister(), ETR, tr_offs.Int32Value());
+ StoreToOffset(scratch.AsXRegister(), TR, tr_offs.Int32Value());
}
void Arm64Assembler::CopyRef(FrameOffset dest, FrameOffset src,
@@ -611,7 +611,7 @@
Arm64ManagedRegister scratch = m_scratch.AsArm64();
Arm64Exception *current_exception = new Arm64Exception(scratch, stack_adjust);
exception_blocks_.push_back(current_exception);
- LoadFromOffset(scratch.AsXRegister(), ETR, Thread::ExceptionOffset<8>().Int32Value());
+ LoadFromOffset(scratch.AsXRegister(), TR, Thread::ExceptionOffset<8>().Int32Value());
___ Cbnz(reg_x(scratch.AsXRegister()), current_exception->Entry());
}
@@ -628,12 +628,7 @@
// Pass exception object as argument.
// Don't care about preserving X0 as this won't return.
___ Mov(reg_x(X0), reg_x(exception->scratch_.AsXRegister()));
- ___ Ldr(temp, MEM_OP(reg_x(ETR), QUICK_ENTRYPOINT_OFFSET(8, pDeliverException).Int32Value()));
-
- // Move ETR(Callee saved) back to TR(Caller saved) reg. We use ETR on calls
- // to external functions that might trash TR. We do not need the original
- // ETR(X21) saved in BuildFrame().
- ___ Mov(reg_x(TR), reg_x(ETR));
+ ___ Ldr(temp, MEM_OP(reg_x(TR), QUICK_ENTRYPOINT_OFFSET(8, pDeliverException).Int32Value()));
___ Blr(temp);
// Call should never return.
@@ -714,12 +709,7 @@
SpillRegisters(core_reg_list, frame_size - core_reg_size);
SpillRegisters(fp_reg_list, frame_size - core_reg_size - fp_reg_size);
- // Note: This is specific to JNI method frame.
- // We will need to move TR(Caller saved in AAPCS) to ETR(Callee saved in AAPCS). The original
- // (ETR)X21 has been saved on stack. In this way, we can restore TR later.
- DCHECK(!core_reg_list.IncludesAliasOf(reg_x(TR)));
- DCHECK(core_reg_list.IncludesAliasOf(reg_x(ETR)));
- ___ Mov(reg_x(ETR), reg_x(TR));
+ DCHECK(core_reg_list.IncludesAliasOf(reg_x(TR)));
// Write ArtMethod*
DCHECK(X0 == method_reg.AsArm64().AsXRegister());
@@ -771,11 +761,7 @@
DCHECK_GE(frame_size, core_reg_size + fp_reg_size + kArm64PointerSize);
DCHECK_ALIGNED(frame_size, kStackAlignment);
- // Note: This is specific to JNI method frame.
- // Restore TR(Caller saved in AAPCS) from ETR(Callee saved in AAPCS).
- DCHECK(!core_reg_list.IncludesAliasOf(reg_x(TR)));
- DCHECK(core_reg_list.IncludesAliasOf(reg_x(ETR)));
- ___ Mov(reg_x(TR), reg_x(ETR));
+ DCHECK(core_reg_list.IncludesAliasOf(reg_x(TR)));
cfi_.RememberState();
diff --git a/compiler/utils/arm64/managed_register_arm64_test.cc b/compiler/utils/arm64/managed_register_arm64_test.cc
index 32c2e62..e27115d 100644
--- a/compiler/utils/arm64/managed_register_arm64_test.cc
+++ b/compiler/utils/arm64/managed_register_arm64_test.cc
@@ -623,7 +623,7 @@
EXPECT_TRUE(vixl::x29.Is(Arm64Assembler::reg_x(X29)));
EXPECT_TRUE(vixl::x30.Is(Arm64Assembler::reg_x(X30)));
- EXPECT_TRUE(vixl::x18.Is(Arm64Assembler::reg_x(TR)));
+ EXPECT_TRUE(vixl::x19.Is(Arm64Assembler::reg_x(TR)));
EXPECT_TRUE(vixl::ip0.Is(Arm64Assembler::reg_x(IP0)));
EXPECT_TRUE(vixl::ip1.Is(Arm64Assembler::reg_x(IP1)));
EXPECT_TRUE(vixl::x29.Is(Arm64Assembler::reg_x(FP)));
diff --git a/compiler/utils/assembler_test_base.h b/compiler/utils/assembler_test_base.h
index 574051a..c8b3fe5 100644
--- a/compiler/utils/assembler_test_base.h
+++ b/compiler/utils/assembler_test_base.h
@@ -216,9 +216,9 @@
bool success = Exec(args, error_msg);
if (!success) {
- LOG(INFO) << "Assembler command line:";
+ LOG(ERROR) << "Assembler command line:";
for (std::string arg : args) {
- LOG(INFO) << arg;
+ LOG(ERROR) << arg;
}
}
return success;
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 772fa9a..1a2c9a9 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");
@@ -1333,6 +1338,24 @@
delete assembler;
}
+TEST(Thumb2AssemblerTest, CompareAndBranch) {
+ arm::Thumb2Assembler* assembler = static_cast<arm::Thumb2Assembler*>(Assembler::Create(kThumb2));
+
+ arm::NearLabel label;
+ __ CompareAndBranchIfZero(arm::R0, &label);
+ __ CompareAndBranchIfZero(arm::R11, &label);
+ __ CompareAndBranchIfNonZero(arm::R0, &label);
+ __ CompareAndBranchIfNonZero(arm::R11, &label);
+ __ Bind(&label);
+
+ size_t cs = __ CodeSize();
+ std::vector<uint8_t> managed_code(cs);
+ MemoryRegion code(&managed_code[0], managed_code.size());
+ __ FinalizeInstructions(code);
+ dump(managed_code, "CompareAndBranch");
+ delete assembler;
+}
+
#undef __
} // namespace arm
} // namespace art
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 3d03234..841d6a0 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -4822,6 +4822,16 @@
" 30: f8a4 0040 strh.w r0, [r4, #64] ; 0x40\n",
nullptr
};
+const char* CompareAndBranchResults[] = {
+ " 0: b130 cbz r0, 10 <CompareAndBranch+0x10>\n",
+ " 2: f1bb 0f00 cmp.w fp, #0\n",
+ " 6: d003 beq.n 10 <CompareAndBranch+0x10>\n",
+ " 8: b910 cbnz r0, 10 <CompareAndBranch+0x10>\n",
+ " a: f1bb 0f00 cmp.w fp, #0\n",
+ " e: d1ff bne.n 10 <CompareAndBranch+0x10>\n",
+ nullptr
+};
+
std::map<std::string, const char**> test_results;
void setup_results() {
test_results["SimpleMov"] = SimpleMovResults;
@@ -4869,4 +4879,5 @@
test_results["LoadStoreRegOffset"] = LoadStoreRegOffsetResults;
test_results["LoadStoreLiteral"] = LoadStoreLiteralResults;
test_results["LoadStoreLimits"] = LoadStoreLimitsResults;
+ test_results["CompareAndBranch"] = CompareAndBranchResults;
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 7fa4328..d45ab1b 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -230,14 +230,14 @@
UsageError("");
UsageError(" --no-include-patch-information: Do not include patching information.");
UsageError("");
- UsageError(" --include-debug-symbols: Include ELF symbols in this oat file");
+ UsageError(" -g");
+ UsageError(" --generate-debug-info: Generate debug information for native debugging,");
+ UsageError(" such as stack unwinding information, ELF symbols and DWARF sections.");
+ UsageError(" This generates all the available information. Unneeded parts can be");
+ UsageError(" stripped using standard command line tools such as strip or objcopy.");
+ UsageError(" (enabled by default in debug builds, disabled by default otherwise)");
UsageError("");
- UsageError(" --no-include-debug-symbols: Do not include ELF symbols in this oat file");
- UsageError("");
- UsageError(" --include-cfi: Include call frame information in the .eh_frame section.");
- UsageError(" The --include-debug-symbols option implies --include-cfi.");
- UsageError("");
- UsageError(" --no-include-cfi: Do not include call frame information in the .eh_frame section.");
+ UsageError(" --no-generate-debug-info: Do not generate debug information for native debugging.");
UsageError("");
UsageError(" --runtime-arg <argument>: used to specify various arguments for the runtime,");
UsageError(" such as initial heap size, maximum heap size, and verbose output.");
@@ -499,8 +499,7 @@
bool debuggable = false;
bool include_patch_information = CompilerOptions::kDefaultIncludePatchInformation;
- bool include_debug_symbols = kIsDebugBuild;
- bool include_cfi = kIsDebugBuild;
+ bool generate_debug_info = kIsDebugBuild;
bool watch_dog_enabled = true;
bool abort_on_hard_verifier_error = false;
bool requested_specific_compiler = false;
@@ -682,18 +681,13 @@
dump_cfg_file_name_ = option.substr(strlen("--dump-cfg=")).data();
} else if (option == "--dump-stats") {
dump_stats_ = true;
- } else if (option == "--include-debug-symbols" || option == "--no-strip-symbols") {
- include_debug_symbols = true;
- } else if (option == "--no-include-debug-symbols" || option == "--strip-symbols") {
- include_debug_symbols = false;
- } else if (option == "--include-cfi") {
- include_cfi = true;
- } else if (option == "--no-include-cfi") {
- include_cfi = false;
+ } else if (option == "--generate-debug-info" || option == "-g") {
+ generate_debug_info = true;
+ } else if (option == "--no-generate-debug-info") {
+ generate_debug_info = false;
} else if (option == "--debuggable") {
debuggable = true;
- include_debug_symbols = true;
- include_cfi = true;
+ generate_debug_info = true;
} else if (option.starts_with("--profile-file=")) {
profile_file_ = option.substr(strlen("--profile-file=")).data();
VLOG(compiler) << "dex2oat: profile file is " << profile_file_;
@@ -933,10 +927,6 @@
break;
}
- if (debuggable) {
- // TODO: Consider adding CFI info and symbols here.
- }
-
compiler_options_.reset(new CompilerOptions(compiler_filter,
huge_method_threshold,
large_method_threshold,
@@ -946,8 +936,7 @@
include_patch_information,
top_k_profile_threshold,
debuggable,
- include_debug_symbols,
- include_cfi,
+ generate_debug_info,
implicit_null_checks,
implicit_so_checks,
implicit_suspend_checks,
diff --git a/disassembler/Android.mk b/disassembler/Android.mk
index 691c43f..039986c 100644
--- a/disassembler/Android.mk
+++ b/disassembler/Android.mk
@@ -59,12 +59,13 @@
LOCAL_SRC_FILES := $$(LIBART_DISASSEMBLER_SRC_FILES)
ifeq ($$(art_target_or_host),target)
- $(call set-target-local-clang-vars)
- $(call set-target-local-cflags-vars,$(2))
+ $(call set-target-local-clang-vars)
+ $(call set-target-local-cflags-vars,$(2))
else # host
LOCAL_CLANG := $(ART_HOST_CLANG)
LOCAL_LDLIBS := $(ART_HOST_LDLIBS)
LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
+ LOCAL_ASFLAGS += $(ART_HOST_ASFLAGS)
ifeq ($$(art_ndebug_or_debug),debug)
LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS)
else
diff --git a/disassembler/disassembler_arm64.cc b/disassembler/disassembler_arm64.cc
index d195efc..348b2a5 100644
--- a/disassembler/disassembler_arm64.cc
+++ b/disassembler/disassembler_arm64.cc
@@ -31,8 +31,7 @@
// runtime/arch/arm64/registers_arm64.h. We do not include that file to
// avoid a dependency on libart.
enum {
- TR = 18,
- ETR = 21,
+ TR = 19,
IP0 = 16,
IP1 = 17,
FP = 29,
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 7ab6626..9e9dea6 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -306,6 +306,7 @@
OatDumperOptions(bool dump_raw_mapping_table,
bool dump_raw_gc_map,
bool dump_vmap,
+ bool dump_code_info_stack_maps,
bool disassemble_code,
bool absolute_addresses,
const char* class_filter,
@@ -317,6 +318,7 @@
: dump_raw_mapping_table_(dump_raw_mapping_table),
dump_raw_gc_map_(dump_raw_gc_map),
dump_vmap_(dump_vmap),
+ dump_code_info_stack_maps_(dump_code_info_stack_maps),
disassemble_code_(disassemble_code),
absolute_addresses_(absolute_addresses),
class_filter_(class_filter),
@@ -330,6 +332,7 @@
const bool dump_raw_mapping_table_;
const bool dump_raw_gc_map_;
const bool dump_vmap_;
+ const bool dump_code_info_stack_maps_;
const bool disassemble_code_;
const bool absolute_addresses_;
const char* const class_filter_;
@@ -1013,15 +1016,13 @@
void DumpVmapData(std::ostream& os,
const OatFile::OatMethod& oat_method,
const DexFile::CodeItem* code_item) {
- if (oat_method.GetGcMap() == nullptr) {
- // If the native GC map is null, then this method has been
- // compiled with the optimizing compiler. The optimizing
- // compiler currently outputs its stack maps in the vmap table.
+ if (IsMethodGeneratedByOptimizingCompiler(oat_method, code_item)) {
+ // The optimizing compiler outputs its CodeInfo data in the vmap table.
const void* raw_code_info = oat_method.GetVmapTable();
if (raw_code_info != nullptr) {
CodeInfo code_info(raw_code_info);
DCHECK(code_item != nullptr);
- DumpCodeInfo(os, code_info, *code_item);
+ DumpCodeInfo(os, code_info, oat_method, *code_item);
}
} else {
// Otherwise, display the vmap table.
@@ -1036,8 +1037,12 @@
// Display a CodeInfo object emitted by the optimizing compiler.
void DumpCodeInfo(std::ostream& os,
const CodeInfo& code_info,
+ const OatFile::OatMethod& oat_method,
const DexFile::CodeItem& code_item) {
- code_info.Dump(os, code_item.registers_size_);
+ code_info.Dump(os,
+ oat_method.GetCodeOffset(),
+ code_item.registers_size_,
+ options_.dump_code_info_stack_maps_);
}
// Display a vmap table.
@@ -1200,6 +1205,23 @@
}
}
+ uint32_t DumpInformationAtOffset(std::ostream& os,
+ const OatFile::OatMethod& oat_method,
+ const DexFile::CodeItem* code_item,
+ size_t offset,
+ bool suspend_point_mapping) {
+ if (IsMethodGeneratedByOptimizingCompiler(oat_method, code_item)) {
+ if (suspend_point_mapping) {
+ DumpDexRegisterMapAtOffset(os, oat_method, code_item, offset);
+ }
+ // The return value is not used in the case of a method compiled
+ // with the optimizing compiler.
+ return DexFile::kDexNoIndex;
+ } else {
+ return DumpMappingAtOffset(os, oat_method, offset, suspend_point_mapping);
+ }
+ }
+
uint32_t DumpMappingAtOffset(std::ostream& os, const OatFile::OatMethod& oat_method,
size_t offset, bool suspend_point_mapping) {
MappingTable table(oat_method.GetMappingTable());
@@ -1302,6 +1324,37 @@
}
}
+ // Has `oat_method` -- corresponding to the Dex `code_item` -- been compiled by
+ // the optimizing compiler?
+ static bool IsMethodGeneratedByOptimizingCompiler(const OatFile::OatMethod& oat_method,
+ const DexFile::CodeItem* code_item) {
+ // If the native GC map is null and the Dex `code_item` is not
+ // null, then this method has been compiled with the optimizing
+ // compiler.
+ return oat_method.GetGcMap() == nullptr && code_item != nullptr;
+ }
+
+ void DumpDexRegisterMapAtOffset(std::ostream& os,
+ const OatFile::OatMethod& oat_method,
+ const DexFile::CodeItem* code_item,
+ size_t offset) {
+ // This method is only relevant for oat methods compiled with the
+ // optimizing compiler.
+ DCHECK(IsMethodGeneratedByOptimizingCompiler(oat_method, code_item));
+
+ // The optimizing compiler outputs its CodeInfo data in the vmap table.
+ const void* raw_code_info = oat_method.GetVmapTable();
+ if (raw_code_info != nullptr) {
+ CodeInfo code_info(raw_code_info);
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ StackMap stack_map = code_info.GetStackMapForNativePcOffset(offset, encoding);
+ if (stack_map.IsValid()) {
+ stack_map.Dump(
+ os, code_info, encoding, oat_method.GetCodeOffset(), code_item->registers_size_);
+ }
+ }
+ }
+
verifier::MethodVerifier* DumpVerifier(std::ostream& os, uint32_t dex_method_idx,
const DexFile* dex_file,
const DexFile::ClassDef& class_def,
@@ -1337,11 +1390,11 @@
size_t offset = 0;
while (offset < code_size) {
if (!bad_input) {
- DumpMappingAtOffset(os, oat_method, offset, false);
+ DumpInformationAtOffset(os, oat_method, code_item, offset, false);
}
offset += disassembler_->Dump(os, quick_native_pc + offset);
if (!bad_input) {
- uint32_t dex_pc = DumpMappingAtOffset(os, oat_method, offset, true);
+ uint32_t dex_pc = DumpInformationAtOffset(os, oat_method, code_item, offset, true);
if (dex_pc != DexFile::kDexNoIndex) {
DumpGcMapAtNativePcOffset(os, oat_method, code_item, offset);
if (verifier != nullptr) {
@@ -2317,6 +2370,8 @@
dump_raw_gc_map_ = true;
} else if (option == "--no-dump:vmap") {
dump_vmap_ = false;
+ } else if (option =="--dump:code_info_stack_maps") {
+ dump_code_info_stack_maps_ = true;
} else if (option == "--no-disassemble") {
disassemble_code_ = false;
} else if (option.starts_with("--symbolize=")) {
@@ -2396,6 +2451,9 @@
" --no-dump:vmap may be used to disable vmap dumping.\n"
" Example: --no-dump:vmap\n"
"\n"
+ " --dump:code_info_stack_maps enables dumping of stack maps in CodeInfo sections.\n"
+ " Example: --dump:code_info_stack_maps\n"
+ "\n"
" --no-disassemble may be used to disable disassembly.\n"
" Example: --no-disassemble\n"
"\n"
@@ -2436,6 +2494,7 @@
bool dump_raw_mapping_table_ = false;
bool dump_raw_gc_map_ = false;
bool dump_vmap_ = true;
+ bool dump_code_info_stack_maps_ = false;
bool disassemble_code_ = true;
bool symbolize_ = false;
bool list_classes_ = false;
@@ -2455,6 +2514,7 @@
args_->dump_raw_mapping_table_,
args_->dump_raw_gc_map_,
args_->dump_vmap_,
+ args_->dump_code_info_stack_maps_,
args_->disassemble_code_,
absolute_addresses,
args_->class_filter_,
diff --git a/runtime/Android.mk b/runtime/Android.mk
index b38f9bc..5ed6955 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -45,6 +45,7 @@
dex_file_verifier.cc \
dex_instruction.cc \
elf_file.cc \
+ gc/allocation_record.cc \
gc/allocator/dlmalloc.cc \
gc/allocator/rosalloc.cc \
gc/accounting/bitmap.cc \
@@ -451,6 +452,7 @@
endif
LOCAL_CFLAGS += $$(ART_HOST_CFLAGS)
LOCAL_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES="$(LIBART_HOST_DEFAULT_INSTRUCTION_SET_FEATURES)"
+ LOCAL_ASFLAGS += $$(ART_HOST_ASFLAGS)
ifeq ($$(art_ndebug_or_debug),debug)
LOCAL_CFLAGS += $$(ART_HOST_DEBUG_CFLAGS)
diff --git a/runtime/arch/arm/asm_support_arm.S b/runtime/arch/arm/asm_support_arm.S
index 2af636e..665d2a3 100644
--- a/runtime/arch/arm/asm_support_arm.S
+++ b/runtime/arch/arm/asm_support_arm.S
@@ -123,4 +123,18 @@
END \name
.endm
+// Macros to poison (negate) the reference for heap poisoning.
+.macro POISON_HEAP_REF rRef
+#ifdef USE_HEAP_POISONING
+ rsb \rRef, \rRef, #0
+#endif // USE_HEAP_POISONING
+.endm
+
+// Macros to unpoison (negate) the reference for heap poisoning.
+.macro UNPOISON_HEAP_REF rRef
+#ifdef USE_HEAP_POISONING
+ rsb \rRef, \rRef, #0
+#endif // USE_HEAP_POISONING
+.endm
+
#endif // ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_S_
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index cafc868..2f2654d 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -127,11 +127,13 @@
qpoints->pFmodf = fmodf;
qpoints->pD2l = art_d2l;
qpoints->pF2l = art_f2l;
+ qpoints->pL2f = art_l2f;
} else {
qpoints->pFmod = art_quick_fmod;
qpoints->pFmodf = art_quick_fmodf;
qpoints->pD2l = art_quick_d2l;
qpoints->pF2l = art_quick_f2l;
+ qpoints->pL2f = art_quick_l2f;
}
// Intrinsics
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 5ae291a..b06d2ca 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
@@ -620,12 +616,16 @@
ENTRY art_quick_aput_obj
cbz r2, .Ldo_aput_null
ldr r3, [r0, #MIRROR_OBJECT_CLASS_OFFSET]
+ UNPOISON_HEAP_REF r3
ldr ip, [r2, #MIRROR_OBJECT_CLASS_OFFSET]
+ UNPOISON_HEAP_REF ip
ldr r3, [r3, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET]
+ UNPOISON_HEAP_REF r3
cmp r3, ip @ value's type == array's component type - trivial assignability
bne .Lcheck_assignability
.Ldo_aput:
add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
+ POISON_HEAP_REF r2
str r2, [r3, r1, lsl #2]
ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET]
lsr r0, r0, #7
@@ -653,6 +653,7 @@
.cfi_restore lr
.cfi_adjust_cfa_offset -16
add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
+ POISON_HEAP_REF r2
str r2, [r3, r1, lsl #2]
ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET]
lsr r0, r0, #7
@@ -894,10 +895,7 @@
* dex method index.
*/
ENTRY art_quick_imt_conflict_trampoline
- ldr r0, [sp, #0] @ load caller Method*
- ldr r0, [r0, #ART_METHOD_DEX_CACHE_METHODS_OFFSET] @ load dex_cache_resolved_methods
- add r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET @ get starting address of data
- ldr r0, [r0, r12, lsl 2] @ load the target method
+ mov r0, r12
b art_quick_invoke_interface_trampoline
END art_quick_imt_conflict_trampoline
@@ -1475,3 +1473,18 @@
vmov r0, s0
b art_f2l
END art_quick_f2l
+
+ /* float art_l2f(int64_t l) */
+ .extern art_l2f
+ENTRY art_quick_l2f
+ push {lr}
+ .cfi_adjust_cfa_offset 4
+ .cfi_rel_offset lr, 0
+ sub sp, #4
+ .cfi_adjust_cfa_offset 4
+ bl art_l2f
+ vmov s0, r0
+ add sp, #4
+ .cfi_adjust_cfa_offset -4
+ pop {pc}
+END art_quick_l2f
diff --git a/runtime/arch/arm64/asm_support_arm64.S b/runtime/arch/arm64/asm_support_arm64.S
index 39a8aa5..bcf55e3 100644
--- a/runtime/arch/arm64/asm_support_arm64.S
+++ b/runtime/arch/arm64/asm_support_arm64.S
@@ -22,9 +22,7 @@
// Define special registers.
// Register holding Thread::Current().
-#define xSELF x18
-// x18 is not preserved by aapcs64, save it on xETR(External Thread reg) for restore and later use.
-#define xETR x21
+#define xSELF x19
// Frame Pointer
#define xFP x29
// Link Register
@@ -57,4 +55,18 @@
END \name
.endm
+// Macros to poison (negate) the reference for heap poisoning.
+.macro POISON_HEAP_REF rRef
+#ifdef USE_HEAP_POISONING
+ neg \rRef, \rRef
+#endif // USE_HEAP_POISONING
+.endm
+
+// Macros to unpoison (negate) the reference for heap poisoning.
+.macro UNPOISON_HEAP_REF rRef
+#ifdef USE_HEAP_POISONING
+ neg \rRef, \rRef
+#endif // USE_HEAP_POISONING
+.endm
+
#endif // ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_S_
diff --git a/runtime/arch/arm64/asm_support_arm64.h b/runtime/arch/arm64/asm_support_arm64.h
index 998f567..989ecc6 100644
--- a/runtime/arch/arm64/asm_support_arm64.h
+++ b/runtime/arch/arm64/asm_support_arm64.h
@@ -20,7 +20,7 @@
#include "asm_support.h"
#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 176
-#define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 112
+#define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 96
#define FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE 224
#endif // ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_H_
diff --git a/runtime/arch/arm64/context_arm64.cc b/runtime/arch/arm64/context_arm64.cc
index 91c0fc9..60becc6 100644
--- a/runtime/arch/arm64/context_arm64.cc
+++ b/runtime/arch/arm64/context_arm64.cc
@@ -90,6 +90,7 @@
gprs_[X13] = nullptr;
gprs_[X14] = nullptr;
gprs_[X15] = nullptr;
+ gprs_[X18] = nullptr;
// d0-d7, d16-d31 are caller-saved; d8-d15 are callee-saved.
diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc
index 8c8f8d5..2ce2a29 100644
--- a/runtime/arch/arm64/entrypoints_init_arm64.cc
+++ b/runtime/arch/arm64/entrypoints_init_arm64.cc
@@ -27,16 +27,9 @@
namespace art {
// Cast entrypoints.
-extern "C" uint32_t art_quick_assignable_from_code(const mirror::Class* klass,
+extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass,
const mirror::Class* ref_class);
-// Single-precision FP arithmetics.
-extern "C" float art_quick_fmodf(float a, float b); // REM_FLOAT[_2ADDR]
-
-// Double-precision FP arithmetics.
-extern "C" double art_quick_fmod(double a, double b); // REM_DOUBLE[_2ADDR]
-
-
void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
QuickEntryPoints* qpoints) {
// Interpreter
@@ -50,7 +43,7 @@
ResetQuickAllocEntryPoints(qpoints);
// Cast
- qpoints->pInstanceofNonTrivial = art_quick_assignable_from_code;
+ qpoints->pInstanceofNonTrivial = artIsAssignableFromCode;
qpoints->pCheckCast = art_quick_check_cast;
// DexCache
@@ -110,9 +103,9 @@
qpoints->pCmpgFloat = nullptr;
qpoints->pCmplDouble = nullptr;
qpoints->pCmplFloat = nullptr;
- qpoints->pFmod = art_quick_fmod;
+ qpoints->pFmod = fmod;
qpoints->pL2d = nullptr;
- qpoints->pFmodf = art_quick_fmodf;
+ qpoints->pFmodf = fmodf;
qpoints->pL2f = nullptr;
qpoints->pD2iz = nullptr;
qpoints->pF2iz = nullptr;
@@ -129,7 +122,7 @@
// Intrinsics
qpoints->pIndexOf = art_quick_indexof;
qpoints->pStringCompareTo = art_quick_string_compareto;
- qpoints->pMemcpy = art_quick_memcpy;
+ qpoints->pMemcpy = memcpy;
// Invocation
qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index f90a6b0..78d3116 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -32,6 +32,7 @@
// xIP0 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefAndArgs] .
THIS_LOAD_REQUIRES_READ_BARRIER
+
// Loads appropriate callee-save-method.
ldr xIP0, [xIP0, RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET ]
@@ -43,43 +44,40 @@
#error "SAVE_ALL_CALLEE_SAVE_FRAME(ARM64) size not as expected."
#endif
- // FP callee-saves
- stp d8, d9, [sp, #8]
- stp d10, d11, [sp, #24]
- stp d12, d13, [sp, #40]
- stp d14, d15, [sp, #56]
+ // Stack alignment filler [sp, #8].
+ // FP callee-saves.
+ stp d8, d9, [sp, #16]
+ stp d10, d11, [sp, #32]
+ stp d12, d13, [sp, #48]
+ stp d14, d15, [sp, #64]
- // Thread register and x19 (callee-save)
- stp xSELF, x19, [sp, #72]
- .cfi_rel_offset x18, 72
+ // GP callee-saves
+ stp x19, x20, [sp, #80]
.cfi_rel_offset x19, 80
-
- // callee-saves
- stp x20, x21, [sp, #88]
.cfi_rel_offset x20, 88
+
+ stp x21, x22, [sp, #96]
.cfi_rel_offset x21, 96
-
- stp x22, x23, [sp, #104]
.cfi_rel_offset x22, 104
+
+ stp x23, x24, [sp, #112]
.cfi_rel_offset x23, 112
-
- stp x24, x25, [sp, #120]
.cfi_rel_offset x24, 120
+
+ stp x25, x26, [sp, #128]
.cfi_rel_offset x25, 128
-
- stp x26, x27, [sp, #136]
.cfi_rel_offset x26, 136
+
+ stp x27, x28, [sp, #144]
.cfi_rel_offset x27, 144
-
- stp x28, x29, [sp, #152]
.cfi_rel_offset x28, 152
- .cfi_rel_offset x29, 160
- str xLR, [sp, #168]
+ stp x29, xLR, [sp, #160]
+ .cfi_rel_offset x29, 160
.cfi_rel_offset x30, 168
- // Loads appropriate callee-save-method
- str xIP0, [sp] // Store ArtMethod* Runtime::callee_save_methods_[kRefsAndArgs]
+ // Store ArtMethod* Runtime::callee_save_methods_[kRefsAndArgs].
+ str xIP0, [sp]
// Place sp in Thread::Current()->top_quick_frame.
mov xIP0, sp
str xIP0, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET]
@@ -96,50 +94,46 @@
// Our registers aren't intermixed - just spill in order.
ldr xIP0, [xIP0] // xIP0 = & (art::Runtime * art::Runtime.instance_) .
- // xIP0 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefAndArgs] .
+ // xIP0 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefOnly] .
THIS_LOAD_REQUIRES_READ_BARRIER
+
// Loads appropriate callee-save-method.
ldr xIP0, [xIP0, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET ]
- sub sp, sp, #112
- .cfi_adjust_cfa_offset 112
+ sub sp, sp, #96
+ .cfi_adjust_cfa_offset 96
// Ugly compile-time check, but we only have the preprocessor.
-#if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 112)
+#if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 96)
#error "REFS_ONLY_CALLEE_SAVE_FRAME(ARM64) size not as expected."
#endif
- // Callee-saves
- stp x19, x20, [sp, #16]
- .cfi_rel_offset x19, 16
- .cfi_rel_offset x20, 24
+ // GP callee-saves.
+ // x20 paired with ArtMethod* - see below.
+ stp x21, x22, [sp, #16]
+ .cfi_rel_offset x21, 16
+ .cfi_rel_offset x22, 24
- stp x21, x22, [sp, #32]
- .cfi_rel_offset x21, 32
- .cfi_rel_offset x22, 40
+ stp x23, x24, [sp, #32]
+ .cfi_rel_offset x23, 32
+ .cfi_rel_offset x24, 40
- stp x23, x24, [sp, #48]
- .cfi_rel_offset x23, 48
- .cfi_rel_offset x24, 56
+ stp x25, x26, [sp, #48]
+ .cfi_rel_offset x25, 48
+ .cfi_rel_offset x26, 56
- stp x25, x26, [sp, #64]
- .cfi_rel_offset x25, 64
- .cfi_rel_offset x26, 72
+ stp x27, x28, [sp, #64]
+ .cfi_rel_offset x27, 64
+ .cfi_rel_offset x28, 72
- stp x27, x28, [sp, #80]
- .cfi_rel_offset x27, 80
- .cfi_rel_offset x28, 88
+ stp x29, xLR, [sp, #80]
+ .cfi_rel_offset x29, 80
+ .cfi_rel_offset x30, 88
- // x29(callee-save) and LR
- stp x29, xLR, [sp, #96]
- .cfi_rel_offset x29, 96
- .cfi_rel_offset x30, 104
+ // Store ArtMethod* Runtime::callee_save_methods_[kRefsOnly].
+ stp xIP0, x20, [sp]
+ .cfi_rel_offset x20, 8
- // Save xSELF to xETR.
- mov xETR, xSELF
-
- // Loads appropriate callee-save-method
- str xIP0, [sp] // Store ArtMethod* Runtime::callee_save_methods_[kRefsOnly]
// Place sp in Thread::Current()->top_quick_frame.
mov xIP0, sp
str xIP0, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET]
@@ -147,48 +141,37 @@
// TODO: Probably no need to restore registers preserved by aapcs64.
.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
- // Restore xSELF.
- mov xSELF, xETR
-
- // Callee-saves
- ldp x19, x20, [sp, #16]
- .cfi_restore x19
+ // Callee-saves.
+ ldr x20, [sp, #8]
.cfi_restore x20
- ldp x21, x22, [sp, #32]
+ ldp x21, x22, [sp, #16]
.cfi_restore x21
.cfi_restore x22
- ldp x23, x24, [sp, #48]
+ ldp x23, x24, [sp, #32]
.cfi_restore x23
.cfi_restore x24
- ldp x25, x26, [sp, #64]
+ ldp x25, x26, [sp, #48]
.cfi_restore x25
.cfi_restore x26
- ldp x27, x28, [sp, #80]
+ ldp x27, x28, [sp, #64]
.cfi_restore x27
.cfi_restore x28
- // x29(callee-save) and LR
- ldp x29, xLR, [sp, #96]
+ ldp x29, xLR, [sp, #80]
.cfi_restore x29
.cfi_restore x30
- add sp, sp, #112
- .cfi_adjust_cfa_offset -112
+ add sp, sp, #96
+ .cfi_adjust_cfa_offset -96
.endm
.macro POP_REFS_ONLY_CALLEE_SAVE_FRAME
- // Restore xSELF as it might be scratched.
- mov xSELF, xETR
- // ETR
- ldr xETR, [sp, #32]
- .cfi_restore x21
-
- add sp, sp, #112
- .cfi_adjust_cfa_offset -112
+ add sp, sp, #96
+ .cfi_adjust_cfa_offset - 96
.endm
.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
@@ -206,31 +189,29 @@
#error "REFS_AND_ARGS_CALLEE_SAVE_FRAME(ARM64) size not as expected."
#endif
+ // Stack alignment filler [sp, #8].
// FP args.
- stp d0, d1, [sp, #8]
- stp d2, d3, [sp, #24]
- stp d4, d5, [sp, #40]
- stp d6, d7, [sp, #56]
+ stp d0, d1, [sp, #16]
+ stp d2, d3, [sp, #32]
+ stp d4, d5, [sp, #48]
+ stp d6, d7, [sp, #64]
// Core args.
- str x1, [sp, 72]
- .cfi_rel_offset x1, 72
+ stp x1, x2, [sp, #80]
+ .cfi_rel_offset x1, 80
+ .cfi_rel_offset x2, 88
- stp x2, x3, [sp, #80]
- .cfi_rel_offset x2, 80
- .cfi_rel_offset x3, 88
+ stp x3, x4, [sp, #96]
+ .cfi_rel_offset x3, 96
+ .cfi_rel_offset x4, 104
- stp x4, x5, [sp, #96]
- .cfi_rel_offset x4, 96
- .cfi_rel_offset x5, 104
+ stp x5, x6, [sp, #112]
+ .cfi_rel_offset x5, 112
+ .cfi_rel_offset x6, 120
- stp x6, x7, [sp, #112]
- .cfi_rel_offset x6, 112
- .cfi_rel_offset x7, 120
-
- // Callee-saves.
- stp x19, x20, [sp, #128]
- .cfi_rel_offset x19, 128
+ // x7, Callee-saves.
+ stp x7, x20, [sp, #128]
+ .cfi_rel_offset x7, 128
.cfi_rel_offset x20, 136
stp x21, x22, [sp, #144]
@@ -249,13 +230,11 @@
.cfi_rel_offset x27, 192
.cfi_rel_offset x28, 200
- // x29(callee-save) and LR
+ // x29(callee-save) and LR.
stp x29, xLR, [sp, #208]
.cfi_rel_offset x29, 208
.cfi_rel_offset x30, 216
- // Save xSELF to xETR.
- mov xETR, xSELF
.endm
/*
@@ -293,34 +272,28 @@
// TODO: Probably no need to restore registers preserved by aapcs64.
.macro RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
- // Restore xSELF.
- mov xSELF, xETR
-
// FP args.
- ldp d0, d1, [sp, #8]
- ldp d2, d3, [sp, #24]
- ldp d4, d5, [sp, #40]
- ldp d6, d7, [sp, #56]
+ ldp d0, d1, [sp, #16]
+ ldp d2, d3, [sp, #32]
+ ldp d4, d5, [sp, #48]
+ ldp d6, d7, [sp, #64]
// Core args.
- ldr x1, [sp, 72]
+ ldp x1, x2, [sp, #80]
.cfi_restore x1
-
- ldp x2, x3, [sp, #80]
.cfi_restore x2
+
+ ldp x3, x4, [sp, #96]
.cfi_restore x3
-
- ldp x4, x5, [sp, #96]
.cfi_restore x4
+
+ ldp x5, x6, [sp, #112]
.cfi_restore x5
-
- ldp x6, x7, [sp, #112]
.cfi_restore x6
- .cfi_restore x7
- // Callee-saves.
- ldp x19, x20, [sp, #128]
- .cfi_restore x19
+ // x7, Callee-saves.
+ ldp x7, x20, [sp, #128]
+ .cfi_restore x7
.cfi_restore x20
ldp x21, x22, [sp, #144]
@@ -339,7 +312,7 @@
.cfi_restore x27
.cfi_restore x28
- // x29(callee-save) and LR
+ // x29(callee-save) and LR.
ldp x29, xLR, [sp, #208]
.cfi_restore x29
.cfi_restore x30
@@ -461,8 +434,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
@@ -485,10 +457,9 @@
// Helper signature is always
// (method_idx, *this_object, *caller_method, *self, sp)
- ldr x2, [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
@@ -1106,13 +1077,12 @@
.extern artThrowClassCastException
ENTRY art_quick_check_cast
// Store arguments and link register
- sub sp, sp, #32 // Stack needs to be 16b aligned on calls
+ // Stack needs to be 16B aligned on calls.
+ stp x0, x1, [sp,#-32]!
.cfi_adjust_cfa_offset 32
- stp x0, x1, [sp]
.cfi_rel_offset x0, 0
.cfi_rel_offset x1, 8
- stp xSELF, xLR, [sp, #16]
- .cfi_rel_offset x18, 16
+ str xLR, [sp, #24]
.cfi_rel_offset x30, 24
// Call runtime code
@@ -1122,25 +1092,21 @@
cbz x0, .Lthrow_class_cast_exception
// Restore and return
- ldp x0, x1, [sp]
+ ldr xLR, [sp, #24]
+ .cfi_restore x30
+ ldp x0, x1, [sp], #32
.cfi_restore x0
.cfi_restore x1
- ldp xSELF, xLR, [sp, #16]
- .cfi_restore x18
- .cfi_restore x30
- add sp, sp, #32
.cfi_adjust_cfa_offset -32
ret
.Lthrow_class_cast_exception:
// Restore
- ldp x0, x1, [sp]
+ ldr xLR, [sp, #24]
+ .cfi_restore x30
+ ldp x0, x1, [sp], #32
.cfi_restore x0
.cfi_restore x1
- ldp xSELF, xLR, [sp, #16]
- .cfi_restore x18
- .cfi_restore x30
- add sp, sp, #32
.cfi_adjust_cfa_offset -32
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context
@@ -1180,15 +1146,19 @@
cbz x2, .Ldo_aput_null
ldr w3, [x0, #MIRROR_OBJECT_CLASS_OFFSET] // Heap reference = 32b
// This also zero-extends to x3
+ UNPOISON_HEAP_REF w3
ldr w4, [x2, #MIRROR_OBJECT_CLASS_OFFSET] // Heap reference = 32b
// This also zero-extends to x4
+ UNPOISON_HEAP_REF w4
ldr w3, [x3, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET] // Heap reference = 32b
// This also zero-extends to x3
+ UNPOISON_HEAP_REF w3
cmp w3, w4 // value's type == array's component type - trivial assignability
bne .Lcheck_assignability
.Ldo_aput:
add x3, x0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
// "Compress" = do nothing
+ POISON_HEAP_REF w2
str w2, [x3, x1, lsl #2] // Heap reference = 32b
ldr x3, [xSELF, #THREAD_CARD_TABLE_OFFSET]
lsr x0, x0, #7
@@ -1201,16 +1171,13 @@
ret
.Lcheck_assignability:
// Store arguments and link register
- sub sp, sp, #48 // Stack needs to be 16b aligned on calls
- .cfi_adjust_cfa_offset 48
- stp x0, x1, [sp]
+ stp x0, x1, [sp,#-32]!
+ .cfi_adjust_cfa_offset 32
.cfi_rel_offset x0, 0
.cfi_rel_offset x1, 8
- stp x2, xSELF, [sp, #16]
+ stp x2, xLR, [sp, #16]
.cfi_rel_offset x2, 16
- .cfi_rel_offset x18, 24
- str xLR, [sp, #32]
- .cfi_rel_offset x30, 32
+ .cfi_rel_offset x30, 24
// Call runtime code
mov x0, x3 // Heap reference, 32b, "uncompress" = do nothing, already zero-extended
@@ -1221,35 +1188,30 @@
cbz x0, .Lthrow_array_store_exception
// Restore
- ldp x0, x1, [sp]
+ ldp x2, x30, [sp, #16]
+ .cfi_restore x2
+ .cfi_restore x30
+ ldp x0, x1, [sp], #32
.cfi_restore x0
.cfi_restore x1
- ldp x2, xSELF, [sp, #16]
- .cfi_restore x2
- .cfi_restore x18
- ldr xLR, [sp, #32]
- .cfi_restore x30
- add sp, sp, #48
- .cfi_adjust_cfa_offset -48
+ .cfi_adjust_cfa_offset -32
add x3, x0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
// "Compress" = do nothing
+ POISON_HEAP_REF w2
str w2, [x3, x1, lsl #2] // Heap reference = 32b
ldr x3, [xSELF, #THREAD_CARD_TABLE_OFFSET]
lsr x0, x0, #7
strb w3, [x3, x0]
ret
.Lthrow_array_store_exception:
- ldp x0, x1, [sp]
+ ldp x2, x30, [sp, #16]
+ .cfi_restore x2
+ .cfi_restore x30
+ ldp x0, x1, [sp], #32
.cfi_restore x0
.cfi_restore x1
- ldp x2, xSELF, [sp, #16]
- .cfi_restore x2
- .cfi_restore x18
- ldr xLR, [sp, #32]
- .cfi_restore x30
- add sp, sp, #48
- .cfi_adjust_cfa_offset -48
+ .cfi_adjust_cfa_offset -32
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
mov x1, x2 // Pass value.
@@ -1450,8 +1412,7 @@
mov x2, xSELF // pass Thread::Current
mov x3, sp // pass SP
bl artQuickProxyInvokeHandler // (Method* proxy method, receiver, Thread*, SP)
- // Use xETR as xSELF might be scratched by native function above.
- ldr x2, [xETR, THREAD_EXCEPTION_OFFSET]
+ ldr x2, [xSELF, THREAD_EXCEPTION_OFFSET]
cbnz x2, .Lexception_in_proxy // success if no exception is pending
RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME // Restore frame
fmov d0, x0 // Store result in d0 in case it was float or double
@@ -1466,10 +1427,7 @@
* dex method index.
*/
ENTRY art_quick_imt_conflict_trampoline
- ldr x0, [sp, #0] // load caller Method*
- ldr w0, [x0, #ART_METHOD_DEX_CACHE_METHODS_OFFSET] // load dex_cache_resolved_methods
- add x0, x0, #MIRROR_LONG_ARRAY_DATA_OFFSET // get starting address of data
- ldr x0, [x0, xIP1, lsl 3] // load the target method
+ mov x0, xIP1
b art_quick_invoke_interface_trampoline
END art_quick_imt_conflict_trampoline
@@ -1601,15 +1559,14 @@
// prepare for artQuickGenericJniEndTrampoline call
// (Thread*, result, result_f)
// x0 x1 x2 <= C calling convention
- mov x1, x0 // Result (from saved)
- mov x0, xETR // Thread register, original xSELF might be scratched by native code.
+ mov x1, x0 // Result (from saved).
+ mov x0, xSELF // Thread register.
fmov x2, d0 // d0 will contain floating point result, but needs to go into x2
bl artQuickGenericJniEndTrampoline
// Pending exceptions possible.
- // Use xETR as xSELF might be scratched by native code
- ldr x2, [xETR, THREAD_EXCEPTION_OFFSET]
+ ldr x2, [xSELF, THREAD_EXCEPTION_OFFSET]
cbnz x2, .Lexception_in_native
// Tear down the alloca.
@@ -1624,8 +1581,6 @@
ret
.Lexception_in_native:
- // Restore xSELF. It might have been scratched by native code.
- mov xSELF, xETR
// Move to x1 then sp to please assembler.
ldr x1, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET]
mov sp, x1
@@ -1921,21 +1876,3 @@
csel x0, x0, x14, ne // x0 := x0 != 0 ? x14(prev x0=length diff) : x1.
ret
END art_quick_string_compareto
-
-// Macro to facilitate adding new entrypoints which call to native function directly.
-// Currently, xSELF is the only thing we need to take care of between managed code and AAPCS.
-// But we might introduce more differences.
-.macro NATIVE_DOWNCALL name, entrypoint
- .extern \entrypoint
-ENTRY \name
- stp xSELF, xLR, [sp, #-16]!
- bl \entrypoint
- ldp xSELF, xLR, [sp], #16
- ret
-END \name
-.endm
-
-NATIVE_DOWNCALL art_quick_fmod fmod
-NATIVE_DOWNCALL art_quick_fmodf fmodf
-NATIVE_DOWNCALL art_quick_memcpy memcpy
-NATIVE_DOWNCALL art_quick_assignable_from_code artIsAssignableFromCode
diff --git a/runtime/arch/arm64/quick_method_frame_info_arm64.h b/runtime/arch/arm64/quick_method_frame_info_arm64.h
index dfb3f99..b525309 100644
--- a/runtime/arch/arm64/quick_method_frame_info_arm64.h
+++ b/runtime/arch/arm64/quick_method_frame_info_arm64.h
@@ -33,18 +33,17 @@
(1 << art::arm64::LR);
// Callee saved registers
static constexpr uint32_t kArm64CalleeSaveRefSpills =
- (1 << art::arm64::X19) | (1 << art::arm64::X20) | (1 << art::arm64::X21) |
- (1 << art::arm64::X22) | (1 << art::arm64::X23) | (1 << art::arm64::X24) |
- (1 << art::arm64::X25) | (1 << art::arm64::X26) | (1 << art::arm64::X27) |
- (1 << art::arm64::X28) | (1 << art::arm64::X29);
+ (1 << art::arm64::X20) | (1 << art::arm64::X21) | (1 << art::arm64::X22) |
+ (1 << art::arm64::X23) | (1 << art::arm64::X24) | (1 << art::arm64::X25) |
+ (1 << art::arm64::X26) | (1 << art::arm64::X27) | (1 << art::arm64::X28) |
+ (1 << art::arm64::X29);
// X0 is the method pointer. Not saved.
static constexpr uint32_t kArm64CalleeSaveArgSpills =
(1 << art::arm64::X1) | (1 << art::arm64::X2) | (1 << art::arm64::X3) |
(1 << art::arm64::X4) | (1 << art::arm64::X5) | (1 << art::arm64::X6) |
(1 << art::arm64::X7);
static constexpr uint32_t kArm64CalleeSaveAllSpills =
- // Thread register.
- (1 << art::arm64::X18);
+ (1 << art::arm64::X19);
static constexpr uint32_t kArm64CalleeSaveFpAlwaysSpills = 0;
static constexpr uint32_t kArm64CalleeSaveFpRefSpills = 0;
diff --git a/runtime/arch/arm64/registers_arm64.h b/runtime/arch/arm64/registers_arm64.h
index 51ae184..4683fc3 100644
--- a/runtime/arch/arm64/registers_arm64.h
+++ b/runtime/arch/arm64/registers_arm64.h
@@ -60,8 +60,7 @@
// different enum value to distinguish between the two.
kNumberOfXRegisters = 33,
// Aliases.
- TR = X18, // ART Thread Register - Managed Runtime (Caller Saved Reg)
- ETR = X21, // ART Thread Register - External Calls (Callee Saved Reg)
+ TR = X19, // ART Thread Register - Managed Runtime (Callee Saved Reg)
IP0 = X16, // Used as scratch by VIXL.
IP1 = X17, // Used as scratch by ART JNI Assembler.
FP = X29,
diff --git a/runtime/arch/memcmp16_test.cc b/runtime/arch/memcmp16_test.cc
index 5ba06f8..9ba7de1 100644
--- a/runtime/arch/memcmp16_test.cc
+++ b/runtime/arch/memcmp16_test.cc
@@ -144,10 +144,10 @@
ASSERT_EQ(expected, computed) << "Run " << round << ", c1=" << count1 << " c2=" << count2;
if (count1 > 0U) {
- delete s1;
+ delete[] s1;
}
if (count2 > 0U) {
- delete s2;
+ delete[] s2;
}
}
}
diff --git a/runtime/arch/mips/asm_support_mips.S b/runtime/arch/mips/asm_support_mips.S
index eea6537..51e224c 100644
--- a/runtime/arch/mips/asm_support_mips.S
+++ b/runtime/arch/mips/asm_support_mips.S
@@ -115,5 +115,18 @@
#endif /* mips_isa_rev */
+// Macros to poison (negate) the reference for heap poisoning.
+.macro POISON_HEAP_REF rRef
+#ifdef USE_HEAP_POISONING
+ subu \rRef, $zero, \rRef
+#endif // USE_HEAP_POISONING
+.endm
+
+// Macros to unpoison (negate) the reference for heap poisoning.
+.macro UNPOISON_HEAP_REF rRef
+#ifdef USE_HEAP_POISONING
+ subu \rRef, $zero, \rRef
+#endif // USE_HEAP_POISONING
+.endm
#endif // ART_RUNTIME_ARCH_MIPS_ASM_SUPPORT_MIPS_S_
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index d148747..cc1de43 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -447,8 +447,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
@@ -464,15 +463,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:
@@ -653,13 +650,17 @@
beqz $a2, .Ldo_aput_null
nop
lw $t0, MIRROR_OBJECT_CLASS_OFFSET($a0)
+ UNPOISON_HEAP_REF $t0
lw $t1, MIRROR_OBJECT_CLASS_OFFSET($a2)
+ UNPOISON_HEAP_REF $t1
lw $t0, MIRROR_CLASS_COMPONENT_TYPE_OFFSET($t0)
+ UNPOISON_HEAP_REF $t0
bne $t1, $t0, .Lcheck_assignability # value's type == array's component type - trivial assignability
nop
.Ldo_aput:
sll $a1, $a1, 2
add $t0, $a0, $a1
+ POISON_HEAP_REF $a2
sw $a2, MIRROR_OBJECT_ARRAY_DATA_OFFSET($t0)
lw $t0, THREAD_CARD_TABLE_OFFSET(rSELF)
srl $t1, $a0, 7
@@ -1102,13 +1103,9 @@
* dex method index.
*/
ENTRY art_quick_imt_conflict_trampoline
- lw $a0, 0($sp) # load caller Method*
- lw $a0, ART_METHOD_DEX_CACHE_METHODS_OFFSET($a0) # load dex_cache_resolved_methods
- sll $t0, 2 # convert target method offset to bytes
- add $a0, $t0 # get address of target method
- lw $a0, MIRROR_OBJECT_ARRAY_DATA_OFFSET($a0) # load the target method
la $t9, art_quick_invoke_interface_trampoline
jalr $zero, $t9
+ move $a0, $t0
END art_quick_imt_conflict_trampoline
.extern artQuickResolutionTrampoline
diff --git a/runtime/arch/mips64/asm_support_mips64.S b/runtime/arch/mips64/asm_support_mips64.S
index 2613777..b859c70 100644
--- a/runtime/arch/mips64/asm_support_mips64.S
+++ b/runtime/arch/mips64/asm_support_mips64.S
@@ -69,5 +69,18 @@
END \name
.endm
+// Macros to poison (negate) the reference for heap poisoning.
+.macro POISON_HEAP_REF rRef
+#ifdef USE_HEAP_POISONING
+ subu \rRef, $zero, \rRef
+#endif // USE_HEAP_POISONING
+.endm
+
+// Macros to unpoison (negate) the reference for heap poisoning.
+.macro UNPOISON_HEAP_REF rRef
+#ifdef USE_HEAP_POISONING
+ subu \rRef, $zero, \rRef
+#endif // USE_HEAP_POISONING
+.endm
#endif // ART_RUNTIME_ARCH_MIPS64_ASM_SUPPORT_MIPS64_S_
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index bdd8a30..37c6c5b 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
@@ -913,13 +912,17 @@
beq $a2, $zero, .Ldo_aput_null
nop
lwu $t0, MIRROR_OBJECT_CLASS_OFFSET($a0)
+ UNPOISON_HEAP_REF $t0
lwu $t1, MIRROR_OBJECT_CLASS_OFFSET($a2)
+ UNPOISON_HEAP_REF $t1
lwu $t0, MIRROR_CLASS_COMPONENT_TYPE_OFFSET($t0)
+ UNPOISON_HEAP_REF $t0
bne $t1, $t0, .Lcheck_assignability # value's type == array's component type - trivial assignability
nop
.Ldo_aput:
dsll $a1, $a1, 2
daddu $t0, $a0, $a1
+ POISON_HEAP_REF $a2
sw $a2, MIRROR_OBJECT_ARRAY_DATA_OFFSET($t0)
ld $t0, THREAD_CARD_TABLE_OFFSET(rSELF)
dsrl $t1, $a0, 7
@@ -1366,14 +1369,10 @@
* dex method index.
*/
ENTRY art_quick_imt_conflict_trampoline
- ld $a0, 0($sp) # load caller Method*
- lwu $a0, ART_METHOD_DEX_CACHE_METHODS_OFFSET($a0) # load dex_cache_resolved_methods
- dsll $t0, 3 # convert target method offset to bytes
- daddu $a0, $t0 # get address of target method
dla $t9, art_quick_invoke_interface_trampoline
.cpreturn
jalr $zero, $t9
- lwu $a0, MIRROR_LONG_ARRAY_DATA_OFFSET($a0) # load the target method
+ move $a0, $t0
END art_quick_imt_conflict_trampoline
.extern artQuickResolutionTrampoline
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 13acaa7..05b42f5 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -173,7 +173,7 @@
// Load call params into the right registers.
"ldp x0, x1, [sp]\n\t"
"ldp x2, x3, [sp, #16]\n\t"
- "ldr x18, [sp, #32]\n\t"
+ "ldr x19, [sp, #32]\n\t"
"add sp, sp, #48\n\t"
".cfi_adjust_cfa_offset -48\n\t"
@@ -526,7 +526,7 @@
// Load call params into the right registers.
"ldp x0, x1, [sp]\n\t"
"ldp x2, x3, [sp, #16]\n\t"
- "ldp x18, x17, [sp, #32]\n\t"
+ "ldp x19, x17, [sp, #32]\n\t"
"add sp, sp, #48\n\t"
".cfi_adjust_cfa_offset -48\n\t"
@@ -1124,7 +1124,7 @@
TEST_F(StubTest, APutObj) {
- TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
+ TEST_DISABLED_FOR_READ_BARRIER();
#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \
(defined(__x86_64__) && !defined(__APPLE__))
@@ -1258,7 +1258,7 @@
}
TEST_F(StubTest, AllocObject) {
- TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
+ TEST_DISABLED_FOR_READ_BARRIER();
#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \
(defined(__x86_64__) && !defined(__APPLE__))
@@ -1385,7 +1385,7 @@
}
TEST_F(StubTest, AllocObjectArray) {
- TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
+ TEST_DISABLED_FOR_READ_BARRIER();
#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \
(defined(__x86_64__) && !defined(__APPLE__))
@@ -1474,7 +1474,7 @@
TEST_F(StubTest, StringCompareTo) {
- TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
+ TEST_DISABLED_FOR_READ_BARRIER();
#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
// TODO: Check the "Unresolved" allocation stubs
@@ -2152,7 +2152,7 @@
}
TEST_F(StubTest, Fields8) {
- TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
+ TEST_DISABLED_FOR_READ_BARRIER();
Thread* self = Thread::Current();
@@ -2166,7 +2166,7 @@
}
TEST_F(StubTest, Fields16) {
- TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
+ TEST_DISABLED_FOR_READ_BARRIER();
Thread* self = Thread::Current();
@@ -2180,7 +2180,7 @@
}
TEST_F(StubTest, Fields32) {
- TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
+ TEST_DISABLED_FOR_READ_BARRIER();
Thread* self = Thread::Current();
@@ -2193,7 +2193,7 @@
}
TEST_F(StubTest, FieldsObj) {
- TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
+ TEST_DISABLED_FOR_READ_BARRIER();
Thread* self = Thread::Current();
@@ -2206,7 +2206,7 @@
}
TEST_F(StubTest, Fields64) {
- TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
+ TEST_DISABLED_FOR_READ_BARRIER();
Thread* self = Thread::Current();
@@ -2221,7 +2221,7 @@
TEST_F(StubTest, IMT) {
#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \
(defined(__x86_64__) && !defined(__APPLE__))
- TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
+ TEST_DISABLED_FOR_READ_BARRIER();
Thread* self = Thread::Current();
@@ -2342,7 +2342,7 @@
TEST_F(StubTest, StringIndexOf) {
#if defined(__arm__) || defined(__aarch64__)
- TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
+ TEST_DISABLED_FOR_READ_BARRIER();
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
diff --git a/runtime/arch/x86/asm_support_x86.S b/runtime/arch/x86/asm_support_x86.S
index 122428b..2159f0e 100644
--- a/runtime/arch/x86/asm_support_x86.S
+++ b/runtime/arch/x86/asm_support_x86.S
@@ -179,4 +179,18 @@
#endif
END_MACRO
+// Macros to poison (negate) the reference for heap poisoning.
+MACRO1(POISON_HEAP_REF, rRef)
+#ifdef USE_HEAP_POISONING
+ neg REG_VAR(rRef, 0)
+#endif // USE_HEAP_POISONING
+END_MACRO
+
+// Macros to unpoison (negate) the reference for heap poisoning.
+MACRO1(UNPOISON_HEAP_REF, rRef)
+#ifdef USE_HEAP_POISONING
+ neg REG_VAR(rRef, 0)
+#endif // USE_HEAP_POISONING
+END_MACRO
+
#endif // ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_S_
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 9cebb4e..44b67ca 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -33,6 +33,7 @@
movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg, 0)), REG_VAR(temp_reg, 1)
movl (REG_VAR(temp_reg, 1)), REG_VAR(temp_reg, 1)
// Push save all callee-save method.
+ THIS_LOAD_REQUIRES_READ_BARRIER
pushl RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET(REG_VAR(temp_reg, 1))
CFI_ADJUST_CFA_OFFSET(4)
// Store esp as the top quick frame.
@@ -59,6 +60,7 @@
movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg, 0)), REG_VAR(temp_reg, 1)
movl (REG_VAR(temp_reg, 1)), REG_VAR(temp_reg, 1)
// Push save all callee-save method.
+ THIS_LOAD_REQUIRES_READ_BARRIER
pushl RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET(REG_VAR(temp_reg, 1))
CFI_ADJUST_CFA_OFFSET(4)
// Store esp as the top quick frame.
@@ -104,6 +106,7 @@
movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg, 0)), REG_VAR(temp_reg, 1)
movl (REG_VAR(temp_reg, 1)), REG_VAR(temp_reg, 1)
// Push save all callee-save method.
+ THIS_LOAD_REQUIRES_READ_BARRIER
pushl RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET(REG_VAR(temp_reg, 1))
CFI_ADJUST_CFA_OFFSET(4)
// Store esp as the stop quick frame.
@@ -278,8 +281,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 +299,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
@@ -1147,11 +1145,22 @@
test %edx, %edx // store of null
jz .Ldo_aput_null
movl MIRROR_OBJECT_CLASS_OFFSET(%eax), %ebx
+ UNPOISON_HEAP_REF ebx
movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%ebx), %ebx
+ UNPOISON_HEAP_REF ebx
// value's type == array's component type - trivial assignability
+#ifdef USE_HEAP_POISONING
+ PUSH eax // save eax
+ movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %eax
+ UNPOISON_HEAP_REF eax
+ cmpl %eax, %ebx
+ POP eax // restore eax
+#else
cmpl MIRROR_OBJECT_CLASS_OFFSET(%edx), %ebx
+#endif
jne .Lcheck_assignability
.Ldo_aput:
+ POISON_HEAP_REF edx
movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4)
movl %fs:THREAD_CARD_TABLE_OFFSET, %edx
shrl LITERAL(7), %eax
@@ -1166,7 +1175,13 @@
PUSH edx
subl LITERAL(8), %esp // alignment padding
CFI_ADJUST_CFA_OFFSET(8)
+#ifdef USE_HEAP_POISONING
+ movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %eax // pass arg2 - type of the value to be stored
+ UNPOISON_HEAP_REF eax
+ PUSH eax
+#else
pushl MIRROR_OBJECT_CLASS_OFFSET(%edx) // pass arg2 - type of the value to be stored
+#endif
CFI_ADJUST_CFA_OFFSET(4)
PUSH ebx // pass arg1 - component type of the array
call SYMBOL(artIsAssignableFromCode) // (Class* a, Class* b)
@@ -1177,6 +1192,7 @@
POP edx
POP ecx
POP eax
+ POISON_HEAP_REF edx
movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4) // do the aput
movl %fs:THREAD_CARD_TABLE_OFFSET, %edx
shrl LITERAL(7), %eax
@@ -1390,16 +1406,11 @@
END_FUNCTION art_quick_proxy_invoke_handler
/*
- * Called to resolve an imt conflict. xmm0 is a hidden argument that holds the target method's
+ * Called to resolve an imt conflict. xmm7 is a hidden argument that holds the target method's
* dex method index.
*/
DEFINE_FUNCTION art_quick_imt_conflict_trampoline
- PUSH ecx
- movl 8(%esp), %eax // load caller Method*
- movl ART_METHOD_DEX_CACHE_METHODS_OFFSET(%eax), %eax // load dex_cache_resolved_methods
- movd %xmm7, %ecx // get target method index stored in xmm0
- movl MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4), %eax // load the target method
- POP ecx
+ movd %xmm7, %eax // get target method index stored in xmm7
jmp SYMBOL(art_quick_invoke_interface_trampoline)
END_FUNCTION art_quick_imt_conflict_trampoline
diff --git a/runtime/arch/x86_64/asm_support_x86_64.S b/runtime/arch/x86_64/asm_support_x86_64.S
index 5964314..b2b6c2d 100644
--- a/runtime/arch/x86_64/asm_support_x86_64.S
+++ b/runtime/arch/x86_64/asm_support_x86_64.S
@@ -170,4 +170,18 @@
int3
END_MACRO
+// Macros to poison (negate) the reference for heap poisoning.
+MACRO1(POISON_HEAP_REF, rRef)
+#ifdef USE_HEAP_POISONING
+ negl REG_VAR(rRef, 0)
+#endif // USE_HEAP_POISONING
+END_MACRO
+
+// Macros to unpoison (negate) the reference for heap poisoning.
+MACRO1(UNPOISON_HEAP_REF, rRef)
+#ifdef USE_HEAP_POISONING
+ negl REG_VAR(rRef, 0)
+#endif // USE_HEAP_POISONING
+END_MACRO
+
#endif // ART_RUNTIME_ARCH_X86_64_ASM_SUPPORT_X86_64_S_
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index bd199db..66dfe5a 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -339,8 +339,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
@@ -360,11 +359,10 @@
// Helper signature is always
// (method_idx, *this_object, *caller_method, *self, sp)
- movq FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE(%rsp), %rdx // 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
@@ -921,8 +919,10 @@
// RDI: uint32_t type_idx, RSI: ArtMethod*
// RDX, RCX, R8, R9: free. RAX: return val.
movl ART_METHOD_DEX_CACHE_TYPES_OFFSET(%rsi), %edx // Load dex cache resolved types array
+ UNPOISON_HEAP_REF edx
// Load the class
movl MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdx, %rdi, MIRROR_OBJECT_ARRAY_COMPONENT_SIZE), %edx
+ UNPOISON_HEAP_REF edx
testl %edx, %edx // Check null class
jz .Lart_quick_alloc_object_tlab_slow_path
// Check class status.
@@ -1191,12 +1191,21 @@
jz .Ldo_aput_null
movl MIRROR_OBJECT_CLASS_OFFSET(%edi), %ecx
// movq MIRROR_OBJECT_CLASS_OFFSET(%rdi), %rcx
+ UNPOISON_HEAP_REF ecx
movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%ecx), %ecx
// movq MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%rcx), %rcx
+ UNPOISON_HEAP_REF ecx
+#ifdef USE_HEAP_POISONING
+ movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %eax // rax is free.
+ UNPOISON_HEAP_REF eax
+ cmpl %eax, %ecx // value's type == array's component type - trivial assignability
+#else
cmpl MIRROR_OBJECT_CLASS_OFFSET(%edx), %ecx // value's type == array's component type - trivial assignability
// cmpq MIRROR_CLASS_OFFSET(%rdx), %rcx
+#endif
jne .Lcheck_assignability
.Ldo_aput:
+ POISON_HEAP_REF edx
movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4)
// movq %rdx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
movq %gs:THREAD_CARD_TABLE_OFFSET, %rdx
@@ -1219,6 +1228,7 @@
// "Uncompress" = do nothing, as already zero-extended on load.
movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %esi // Pass arg2 = value's class.
+ UNPOISON_HEAP_REF esi
movq %rcx, %rdi // Pass arg1 = array's component type.
call SYMBOL(artIsAssignableFromCode) // (Class* a, Class* b)
@@ -1235,6 +1245,7 @@
POP rsi
POP rdi
+ POISON_HEAP_REF edx
movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4)
// movq %rdx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
movq %gs:THREAD_CARD_TABLE_OFFSET, %rdx
@@ -1338,9 +1349,7 @@
int3
int3
#else
- movq 8(%rsp), %rdi // load caller Method*
- movl ART_METHOD_DEX_CACHE_METHODS_OFFSET(%rdi), %edi // load dex_cache_resolved_methods
- movq MIRROR_LONG_ARRAY_DATA_OFFSET(%rdi, %rax, 8), %rdi // load the target method
+ movq %rax, %rdi
jmp art_quick_invoke_interface_trampoline
#endif // __APPLE__
END_FUNCTION art_quick_imt_conflict_trampoline
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index fbaf0ae..c78a851 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -182,29 +182,33 @@
uint32_t sought_offset = pc - reinterpret_cast<uintptr_t>(entry_point);
if (IsOptimized(sizeof(void*))) {
CodeInfo code_info = GetOptimizedCodeInfo();
- return code_info.GetStackMapForNativePcOffset(sought_offset).GetDexPc(code_info);
- }
-
- MappingTable table(entry_point != nullptr ?
- GetMappingTable(EntryPointToCodePointer(entry_point), sizeof(void*)) : nullptr);
- if (table.TotalSize() == 0) {
- // NOTE: Special methods (see Mir2Lir::GenSpecialCase()) have an empty mapping
- // but they have no suspend checks and, consequently, we never call ToDexPc() for them.
- DCHECK(IsNative() || IsCalleeSaveMethod() || IsProxyMethod()) << PrettyMethod(this);
- return DexFile::kDexNoIndex; // Special no mapping case
- }
- // Assume the caller wants a pc-to-dex mapping so check here first.
- typedef MappingTable::PcToDexIterator It;
- for (It cur = table.PcToDexBegin(), end = table.PcToDexEnd(); cur != end; ++cur) {
- if (cur.NativePcOffset() == sought_offset) {
- return cur.DexPc();
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ StackMap stack_map = code_info.GetStackMapForNativePcOffset(sought_offset, encoding);
+ if (stack_map.IsValid()) {
+ return stack_map.GetDexPc(encoding);
}
- }
- // Now check dex-to-pc mappings.
- typedef MappingTable::DexToPcIterator It2;
- for (It2 cur = table.DexToPcBegin(), end = table.DexToPcEnd(); cur != end; ++cur) {
- if (cur.NativePcOffset() == sought_offset) {
- return cur.DexPc();
+ } else {
+ MappingTable table(entry_point != nullptr ?
+ GetMappingTable(EntryPointToCodePointer(entry_point), sizeof(void*)) : nullptr);
+ if (table.TotalSize() == 0) {
+ // NOTE: Special methods (see Mir2Lir::GenSpecialCase()) have an empty mapping
+ // but they have no suspend checks and, consequently, we never call ToDexPc() for them.
+ DCHECK(IsNative() || IsCalleeSaveMethod() || IsProxyMethod()) << PrettyMethod(this);
+ return DexFile::kDexNoIndex; // Special no mapping case
+ }
+ // Assume the caller wants a pc-to-dex mapping so check here first.
+ typedef MappingTable::PcToDexIterator It;
+ for (It cur = table.PcToDexBegin(), end = table.PcToDexEnd(); cur != end; ++cur) {
+ if (cur.NativePcOffset() == sought_offset) {
+ return cur.DexPc();
+ }
+ }
+ // Now check dex-to-pc mappings.
+ typedef MappingTable::DexToPcIterator It2;
+ for (It2 cur = table.DexToPcBegin(), end = table.DexToPcEnd(); cur != end; ++cur) {
+ if (cur.NativePcOffset() == sought_offset) {
+ return cur.DexPc();
+ }
}
}
if (abort_on_failure) {
@@ -424,7 +428,9 @@
// exception was thrown to force the activations to be removed from the
// stack. Continue execution in the interpreter.
self->ClearException();
- ShadowFrame* shadow_frame = self->GetAndClearDeoptimizationShadowFrame(result);
+ ShadowFrame* shadow_frame =
+ self->PopStackedShadowFrame(StackedShadowFrameType::kDeoptimizationShadowFrame);
+ result->SetJ(self->PopDeoptimizationReturnValue().GetJ());
self->SetTopOfStack(nullptr);
self->SetTopOfShadowStack(shadow_frame);
interpreter::EnterInterpreterFromDeoptimize(self, shadow_frame, result);
diff --git a/runtime/base/bit_vector.h b/runtime/base/bit_vector.h
index 17835f5..afa8dc1 100644
--- a/runtime/base/bit_vector.h
+++ b/runtime/base/bit_vector.h
@@ -21,6 +21,7 @@
#include <iterator>
#include "base/bit_utils.h"
+#include "globals.h"
namespace art {
@@ -229,6 +230,19 @@
// Number of bits set in range [0, end) in storage. (No range check.)
static uint32_t NumSetBits(const uint32_t* storage, uint32_t end);
+ // Fill given memory region with the contents of the vector and zero padding.
+ void CopyTo(void* dst, size_t len) const {
+ DCHECK_LE(static_cast<size_t>(GetHighestBitSet() + 1), len * kBitsPerByte);
+ size_t vec_len = GetSizeOf();
+ if (vec_len < len) {
+ void* dst_padding = reinterpret_cast<uint8_t*>(dst) + vec_len;
+ memcpy(dst, storage_, vec_len);
+ memset(dst_padding, 0, len - vec_len);
+ } else {
+ memcpy(dst, storage_, len);
+ }
+ }
+
void Dump(std::ostream& os, const char* prefix) const;
private:
diff --git a/runtime/base/bit_vector_test.cc b/runtime/base/bit_vector_test.cc
index c51b9b0..19c01f2 100644
--- a/runtime/base/bit_vector_test.cc
+++ b/runtime/base/bit_vector_test.cc
@@ -211,4 +211,62 @@
}
}
+TEST(BitVector, CopyTo) {
+ {
+ // Test copying an empty BitVector. Padding should fill `buf` with zeroes.
+ BitVector bv(0, true, Allocator::GetMallocAllocator());
+ uint32_t buf;
+
+ bv.CopyTo(&buf, sizeof(buf));
+ EXPECT_EQ(0u, bv.GetSizeOf());
+ EXPECT_EQ(0u, buf);
+ }
+
+ {
+ // Test copying when `bv.storage_` and `buf` are of equal lengths.
+ BitVector bv(0, true, Allocator::GetMallocAllocator());
+ uint32_t buf;
+
+ bv.SetBit(0);
+ bv.SetBit(17);
+ bv.SetBit(26);
+ EXPECT_EQ(sizeof(buf), bv.GetSizeOf());
+
+ bv.CopyTo(&buf, sizeof(buf));
+ EXPECT_EQ(0x04020001u, buf);
+ }
+
+ {
+ // Test copying when the `bv.storage_` is longer than `buf`. As long as
+ // `buf` is long enough to hold all set bits, copying should succeed.
+ BitVector bv(0, true, Allocator::GetMallocAllocator());
+ uint8_t buf[5];
+
+ bv.SetBit(18);
+ bv.SetBit(39);
+ EXPECT_LT(sizeof(buf), bv.GetSizeOf());
+
+ bv.CopyTo(buf, sizeof(buf));
+ EXPECT_EQ(0x00u, buf[0]);
+ EXPECT_EQ(0x00u, buf[1]);
+ EXPECT_EQ(0x04u, buf[2]);
+ EXPECT_EQ(0x00u, buf[3]);
+ EXPECT_EQ(0x80u, buf[4]);
+ }
+
+ {
+ // Test zero padding when `bv.storage_` is shorter than `buf`.
+ BitVector bv(0, true, Allocator::GetMallocAllocator());
+ uint32_t buf[2];
+
+ bv.SetBit(18);
+ bv.SetBit(31);
+ EXPECT_GT(sizeof(buf), bv.GetSizeOf());
+
+ bv.CopyTo(buf, sizeof(buf));
+ EXPECT_EQ(0x80040000U, buf[0]);
+ EXPECT_EQ(0x00000000U, buf[1]);
+ }
+}
+
} // namespace art
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/base/unix_file/random_access_file_test.h b/runtime/base/unix_file/random_access_file_test.h
index e7ace4c..91858c2 100644
--- a/runtime/base/unix_file/random_access_file_test.h
+++ b/runtime/base/unix_file/random_access_file_test.h
@@ -82,7 +82,7 @@
void TestReadContent(const std::string& content, RandomAccessFile* file) {
const int buf_size = content.size() + 10;
- std::unique_ptr<char> buf(new char[buf_size]);
+ std::unique_ptr<char[]> buf(new char[buf_size]);
// Can't read from a negative offset.
ASSERT_EQ(-EINVAL, file->Read(buf.get(), 0, -123));
diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h
index d323379..504b753 100644
--- a/runtime/check_reference_map_visitor.h
+++ b/runtime/check_reference_map_visitor.h
@@ -65,17 +65,18 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ArtMethod* m = GetMethod();
CodeInfo code_info = m->GetOptimizedCodeInfo();
- StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
uint16_t number_of_dex_registers = m->GetCodeItem()->registers_size_;
DexRegisterMap dex_register_map =
- code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers);
- MemoryRegion stack_mask = stack_map.GetStackMask(code_info);
- uint32_t register_mask = stack_map.GetRegisterMask(code_info);
+ code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers);
+ MemoryRegion stack_mask = stack_map.GetStackMask(encoding);
+ uint32_t register_mask = stack_map.GetRegisterMask(encoding);
for (int i = 0; i < number_of_references; ++i) {
int reg = registers[i];
CHECK(reg < m->GetCodeItem()->registers_size_);
- DexRegisterLocation location =
- dex_register_map.GetDexRegisterLocation(reg, number_of_dex_registers, code_info);
+ DexRegisterLocation location = dex_register_map.GetDexRegisterLocation(
+ reg, number_of_dex_registers, code_info, encoding);
switch (location.GetKind()) {
case DexRegisterLocation::Kind::kNone:
// Not set, should not be a reference.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 91812e7..dc8a3d1 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4813,11 +4813,11 @@
}
ArtMethod* interface_method = interface->GetVirtualMethod(j, image_pointer_size_);
uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
- auto*& imt_ref = out_imt[imt_index];
- if (imt_ref == unimplemented_method) {
- imt_ref = method;
- } else if (imt_ref != conflict_method) {
- imt_ref = conflict_method;
+ auto** imt_ref = &out_imt[imt_index];
+ if (*imt_ref == unimplemented_method) {
+ *imt_ref = method;
+ } else if (*imt_ref != conflict_method) {
+ *imt_ref = conflict_method;
}
}
}
@@ -4972,45 +4972,54 @@
++out;
}
}
+ StrideIterator<ArtMethod> out(
+ reinterpret_cast<uintptr_t>(virtuals) + old_method_count * method_size, method_size);
+ // Copy the mirada methods before making a copy of the vtable so that moving GC doesn't miss
+ // any roots. This is necessary since these miranda methods wont get their roots visited from
+ // the class table root visiting until they are copied to the new virtuals array.
+ const size_t old_vtable_count = vtable->GetLength();
+ const size_t new_vtable_count = old_vtable_count + miranda_methods.size();
+ size_t method_idx = old_vtable_count;
+ for (auto* mir_method : miranda_methods) {
+ ArtMethod* out_method = &*out;
+ // Leave the declaring class alone as type indices are relative to it
+ out_method->CopyFrom(mir_method, image_pointer_size_);
+ out_method->SetAccessFlags(out_method->GetAccessFlags() | kAccMiranda);
+ out_method->SetMethodIndex(0xFFFF & method_idx);
+ move_table.emplace(mir_method, out_method);
+ ++out;
+ ++method_idx;
+ }
+ DCHECK_EQ(new_vtable_count, method_idx);
UpdateClassVirtualMethods(klass.Get(), virtuals, new_method_count);
// Done copying methods, they are all reachable from the class now, so we can end the no thread
// suspension assert.
self->EndAssertNoThreadSuspension(old_cause);
-
- size_t old_vtable_count = vtable->GetLength();
- const size_t new_vtable_count = old_vtable_count + miranda_methods.size();
vtable.Assign(down_cast<mirror::PointerArray*>(vtable->CopyOf(self, new_vtable_count)));
if (UNLIKELY(vtable.Get() == nullptr)) {
self->AssertPendingOOMException();
return false;
}
- StrideIterator<ArtMethod> out(
- reinterpret_cast<uintptr_t>(virtuals) + old_method_count * method_size, method_size);
- for (auto* mir_method : miranda_methods) {
- ArtMethod* out_method = &*out;
- out->CopyFrom(mir_method, image_pointer_size_);
- // Leave the declaring class alone as type indices are relative to it
- out_method->SetAccessFlags(out_method->GetAccessFlags() | kAccMiranda);
- out_method->SetMethodIndex(0xFFFF & old_vtable_count);
- vtable->SetElementPtrSize(old_vtable_count, out_method, image_pointer_size_);
- move_table.emplace(mir_method, out_method);
- ++out;
- ++old_vtable_count;
- }
-
// Update old vtable methods.
- for (size_t i = 0; i < old_vtable_count - miranda_methods.size(); ++i) {
- auto* m = vtable->GetElementPtrSize<ArtMethod*>(i, image_pointer_size_);
+ for (method_idx = 0; method_idx < old_vtable_count; ++method_idx) {
+ auto* m = vtable->GetElementPtrSize<ArtMethod*>(method_idx, image_pointer_size_);
DCHECK(m != nullptr) << PrettyClass(klass.Get());
auto it = move_table.find(m);
if (it != move_table.end()) {
auto* new_m = it->second;
DCHECK(new_m != nullptr) << PrettyClass(klass.Get());
- vtable->SetElementPtrSize(i, new_m, image_pointer_size_);
+ vtable->SetElementPtrSize(method_idx, new_m, image_pointer_size_);
}
}
+ // Update miranda methods.
+ out = StrideIterator<ArtMethod>(
+ reinterpret_cast<uintptr_t>(virtuals) + old_method_count * method_size, method_size);
+ for (; method_idx < new_vtable_count; ++method_idx) {
+ vtable->SetElementPtrSize(method_idx, &*out, image_pointer_size_);
+ ++out;
+ }
+
klass->SetVTable(vtable.Get());
- CHECK_EQ(old_vtable_count, new_vtable_count);
// Go fix up all the stale miranda pointers.
for (size_t i = 0; i < ifcount; ++i) {
for (size_t j = 0, count = iftable->GetMethodArrayCount(i); j < count; ++j) {
@@ -5865,7 +5874,7 @@
ArtField* const parent_field =
mirror::Class::FindField(self, hs.NewHandle(h_path_class_loader->GetClass()), "parent",
"Ljava/lang/ClassLoader;");
- DCHECK(parent_field!= nullptr);
+ DCHECK(parent_field != nullptr);
mirror::Object* boot_cl =
soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self);
parent_field->SetObject<false>(h_path_class_loader.Get(), boot_cl);
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 34fdd8d..0987c00 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -181,6 +181,13 @@
return; \
}
+// TODO: When read barrier works with the compiler, get rid of this.
+#define TEST_DISABLED_FOR_READ_BARRIER() \
+ if (kUseReadBarrier) { \
+ printf("WARNING: TEST DISABLED FOR READ BARRIER\n"); \
+ return; \
+ }
+
#define TEST_DISABLED_FOR_MIPS() \
if (kRuntimeISA == kMips) { \
printf("WARNING: TEST DISABLED FOR MIPS\n"); \
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 24615e2..3c75daf 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -29,6 +29,7 @@
#include "dex_file-inl.h"
#include "dex_instruction.h"
#include "gc/accounting/card_table-inl.h"
+#include "gc/allocation_record.h"
#include "gc/space/large_object_space.h"
#include "gc/space/space-inl.h"
#include "handle_scope.h"
@@ -61,127 +62,30 @@
// The key identifying the debugger to update instrumentation.
static constexpr const char* kDbgInstrumentationKey = "Debugger";
-static const size_t kMaxAllocRecordStackDepth = 16; // Max 255.
-static const size_t kDefaultNumAllocRecords = 64*1024; // Must be a power of 2. 2BE can hold 64k-1.
-
-// Limit alloc_record_count to the 2BE value that is the limit of the current protocol.
+// Limit alloc_record_count to the 2BE value (64k-1) that is the limit of the current protocol.
static uint16_t CappedAllocRecordCount(size_t alloc_record_count) {
- if (alloc_record_count > 0xffff) {
- return 0xffff;
+ size_t cap = 0xffff;
+#ifdef HAVE_ANDROID_OS
+ // Check whether there's a system property overriding the number of recent records.
+ const char* propertyName = "dalvik.vm.recentAllocMax";
+ char recentAllocMaxString[PROPERTY_VALUE_MAX];
+ if (property_get(propertyName, recentAllocMaxString, "") > 0) {
+ char* end;
+ size_t value = strtoul(recentAllocMaxString, &end, 10);
+ if (*end != '\0') {
+ LOG(ERROR) << "Ignoring " << propertyName << " '" << recentAllocMaxString
+ << "' --- invalid";
+ } else {
+ cap = value;
+ }
+ }
+#endif
+ if (alloc_record_count > cap) {
+ return cap;
}
return alloc_record_count;
}
-class AllocRecordStackTraceElement {
- public:
- AllocRecordStackTraceElement() : method_(nullptr), dex_pc_(0) {
- }
-
- int32_t LineNumber() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ArtMethod* method = Method();
- DCHECK(method != nullptr);
- return method->GetLineNumFromDexPC(DexPc());
- }
-
- ArtMethod* Method() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ScopedObjectAccessUnchecked soa(Thread::Current());
- return soa.DecodeMethod(method_);
- }
-
- void SetMethod(ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ScopedObjectAccessUnchecked soa(Thread::Current());
- method_ = soa.EncodeMethod(m);
- }
-
- uint32_t DexPc() const {
- return dex_pc_;
- }
-
- void SetDexPc(uint32_t pc) {
- dex_pc_ = pc;
- }
-
- private:
- jmethodID method_;
- uint32_t dex_pc_;
-};
-
-jobject Dbg::TypeCache::Add(mirror::Class* t) {
- ScopedObjectAccessUnchecked soa(Thread::Current());
- JNIEnv* const env = soa.Env();
- ScopedLocalRef<jobject> local_ref(soa.Env(), soa.AddLocalReference<jobject>(t));
- const int32_t hash_code = soa.Decode<mirror::Class*>(local_ref.get())->IdentityHashCode();
- auto range = objects_.equal_range(hash_code);
- for (auto it = range.first; it != range.second; ++it) {
- if (soa.Decode<mirror::Class*>(it->second) == soa.Decode<mirror::Class*>(local_ref.get())) {
- // Found a matching weak global, return it.
- return it->second;
- }
- }
- const jobject weak_global = env->NewWeakGlobalRef(local_ref.get());
- objects_.insert(std::make_pair(hash_code, weak_global));
- return weak_global;
-}
-
-void Dbg::TypeCache::Clear() {
- JavaVMExt* vm = Runtime::Current()->GetJavaVM();
- Thread* self = Thread::Current();
- for (const auto& p : objects_) {
- vm->DeleteWeakGlobalRef(self, p.second);
- }
- objects_.clear();
-}
-
-class AllocRecord {
- public:
- AllocRecord() : type_(nullptr), byte_count_(0), thin_lock_id_(0) {}
-
- mirror::Class* Type() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return down_cast<mirror::Class*>(Thread::Current()->DecodeJObject(type_));
- }
-
- void SetType(mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_,
- Locks::alloc_tracker_lock_) {
- type_ = Dbg::type_cache_.Add(t);
- }
-
- size_t GetDepth() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- size_t depth = 0;
- while (depth < kMaxAllocRecordStackDepth && stack_[depth].Method() != nullptr) {
- ++depth;
- }
- return depth;
- }
-
- size_t ByteCount() const {
- return byte_count_;
- }
-
- void SetByteCount(size_t count) {
- byte_count_ = count;
- }
-
- uint16_t ThinLockId() const {
- return thin_lock_id_;
- }
-
- void SetThinLockId(uint16_t id) {
- thin_lock_id_ = id;
- }
-
- AllocRecordStackTraceElement* StackElement(size_t index) {
- DCHECK_LT(index, kMaxAllocRecordStackDepth);
- return &stack_[index];
- }
-
- private:
- jobject type_; // This is a weak global.
- size_t byte_count_;
- uint16_t thin_lock_id_;
- // Unused entries have null method.
- AllocRecordStackTraceElement stack_[kMaxAllocRecordStackDepth];
-};
-
class Breakpoint {
public:
Breakpoint(ArtMethod* method, uint32_t dex_pc,
@@ -382,13 +286,6 @@
bool Dbg::gDisposed = false;
ObjectRegistry* Dbg::gRegistry = nullptr;
-// Recent allocation tracking.
-AllocRecord* Dbg::recent_allocation_records_ = nullptr; // TODO: CircularBuffer<AllocRecord>
-size_t Dbg::alloc_record_max_ = 0;
-size_t Dbg::alloc_record_head_ = 0;
-size_t Dbg::alloc_record_count_ = 0;
-Dbg::TypeCache Dbg::type_cache_;
-
// Deoptimization support.
std::vector<DeoptimizationRequest> Dbg::deoptimization_requests_;
size_t Dbg::full_deoptimization_event_count_ = 0;
@@ -4665,177 +4562,41 @@
Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"), sizeof(heap_id), heap_id);
}
-static size_t GetAllocTrackerMax() {
-#ifdef HAVE_ANDROID_OS
- // Check whether there's a system property overriding the number of records.
- const char* propertyName = "dalvik.vm.allocTrackerMax";
- char allocRecordMaxString[PROPERTY_VALUE_MAX];
- if (property_get(propertyName, allocRecordMaxString, "") > 0) {
- char* end;
- size_t value = strtoul(allocRecordMaxString, &end, 10);
- if (*end != '\0') {
- LOG(ERROR) << "Ignoring " << propertyName << " '" << allocRecordMaxString
- << "' --- invalid";
- return kDefaultNumAllocRecords;
- }
- if (!IsPowerOfTwo(value)) {
- LOG(ERROR) << "Ignoring " << propertyName << " '" << allocRecordMaxString
- << "' --- not power of two";
- return kDefaultNumAllocRecords;
- }
- return value;
- }
-#endif
- return kDefaultNumAllocRecords;
-}
-
void Dbg::SetAllocTrackingEnabled(bool enable) {
- Thread* self = Thread::Current();
- if (enable) {
- {
- MutexLock mu(self, *Locks::alloc_tracker_lock_);
- if (recent_allocation_records_ != nullptr) {
- return; // Already enabled, bail.
- }
- alloc_record_max_ = GetAllocTrackerMax();
- LOG(INFO) << "Enabling alloc tracker (" << alloc_record_max_ << " entries of "
- << kMaxAllocRecordStackDepth << " frames, taking "
- << PrettySize(sizeof(AllocRecord) * alloc_record_max_) << ")";
- DCHECK_EQ(alloc_record_head_, 0U);
- DCHECK_EQ(alloc_record_count_, 0U);
- recent_allocation_records_ = new AllocRecord[alloc_record_max_];
- CHECK(recent_allocation_records_ != nullptr);
- }
- Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints();
- } else {
- {
- ScopedObjectAccess soa(self); // For type_cache_.Clear();
- MutexLock mu(self, *Locks::alloc_tracker_lock_);
- if (recent_allocation_records_ == nullptr) {
- return; // Already disabled, bail.
- }
- LOG(INFO) << "Disabling alloc tracker";
- delete[] recent_allocation_records_;
- recent_allocation_records_ = nullptr;
- alloc_record_head_ = 0;
- alloc_record_count_ = 0;
- type_cache_.Clear();
- }
- // If an allocation comes in before we uninstrument, we will safely drop it on the floor.
- Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints();
- }
-}
-
-struct AllocRecordStackVisitor : public StackVisitor {
- AllocRecordStackVisitor(Thread* thread, AllocRecord* record_in)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
- record(record_in),
- depth(0) {}
-
- // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
- // annotalysis.
- bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
- if (depth >= kMaxAllocRecordStackDepth) {
- return false;
- }
- ArtMethod* m = GetMethod();
- if (!m->IsRuntimeMethod()) {
- record->StackElement(depth)->SetMethod(m);
- record->StackElement(depth)->SetDexPc(GetDexPc());
- ++depth;
- }
- return true;
- }
-
- ~AllocRecordStackVisitor() {
- // Clear out any unused stack trace elements.
- for (; depth < kMaxAllocRecordStackDepth; ++depth) {
- record->StackElement(depth)->SetMethod(nullptr);
- record->StackElement(depth)->SetDexPc(0);
- }
- }
-
- AllocRecord* record;
- size_t depth;
-};
-
-void Dbg::RecordAllocation(Thread* self, mirror::Class* type, size_t byte_count) {
- MutexLock mu(self, *Locks::alloc_tracker_lock_);
- if (recent_allocation_records_ == nullptr) {
- // In the process of shutting down recording, bail.
- return;
- }
-
- // Advance and clip.
- if (++alloc_record_head_ == alloc_record_max_) {
- alloc_record_head_ = 0;
- }
-
- // Fill in the basics.
- AllocRecord* record = &recent_allocation_records_[alloc_record_head_];
- record->SetType(type);
- record->SetByteCount(byte_count);
- record->SetThinLockId(self->GetThreadId());
-
- // Fill in the stack trace.
- AllocRecordStackVisitor visitor(self, record);
- visitor.WalkStack();
-
- if (alloc_record_count_ < alloc_record_max_) {
- ++alloc_record_count_;
- }
-}
-
-// Returns the index of the head element.
-//
-// We point at the most-recently-written record, so if alloc_record_count_ is 1
-// we want to use the current element. Take "head+1" and subtract count
-// from it.
-//
-// We need to handle underflow in our circular buffer, so we add
-// alloc_record_max_ and then mask it back down.
-size_t Dbg::HeadIndex() {
- return (Dbg::alloc_record_head_ + 1 + Dbg::alloc_record_max_ - Dbg::alloc_record_count_) &
- (Dbg::alloc_record_max_ - 1);
+ gc::AllocRecordObjectMap::SetAllocTrackingEnabled(enable);
}
void Dbg::DumpRecentAllocations() {
ScopedObjectAccess soa(Thread::Current());
MutexLock mu(soa.Self(), *Locks::alloc_tracker_lock_);
- if (recent_allocation_records_ == nullptr) {
+ if (!Runtime::Current()->GetHeap()->IsAllocTrackingEnabled()) {
LOG(INFO) << "Not recording tracked allocations";
return;
}
+ gc::AllocRecordObjectMap* records = Runtime::Current()->GetHeap()->GetAllocationRecords();
+ CHECK(records != nullptr);
- // "i" is the head of the list. We want to start at the end of the
- // list and move forward to the tail.
- size_t i = HeadIndex();
- const uint16_t capped_count = CappedAllocRecordCount(Dbg::alloc_record_count_);
+ const uint16_t capped_count = CappedAllocRecordCount(records->Size());
uint16_t count = capped_count;
- LOG(INFO) << "Tracked allocations, (head=" << alloc_record_head_ << " count=" << count << ")";
- while (count--) {
- AllocRecord* record = &recent_allocation_records_[i];
+ LOG(INFO) << "Tracked allocations, (count=" << count << ")";
+ for (auto it = records->RBegin(), end = records->REnd();
+ count > 0 && it != end; count--, it++) {
+ const gc::AllocRecord* record = it->second;
- LOG(INFO) << StringPrintf(" Thread %-2d %6zd bytes ", record->ThinLockId(), record->ByteCount())
- << PrettyClass(record->Type());
+ LOG(INFO) << StringPrintf(" Thread %-2d %6zd bytes ", record->GetTid(), record->ByteCount())
+ << PrettyClass(it->first.Read()->GetClass());
- for (size_t stack_frame = 0; stack_frame < kMaxAllocRecordStackDepth; ++stack_frame) {
- AllocRecordStackTraceElement* stack_element = record->StackElement(stack_frame);
- ArtMethod* m = stack_element->Method();
- if (m == nullptr) {
- break;
- }
- LOG(INFO) << " " << PrettyMethod(m) << " line " << stack_element->LineNumber();
+ for (size_t stack_frame = 0, depth = record->GetDepth(); stack_frame < depth; ++stack_frame) {
+ const gc::AllocRecordStackTraceElement& stack_element = record->StackElement(stack_frame);
+ ArtMethod* m = stack_element.GetMethod();
+ LOG(INFO) << " " << PrettyMethod(m) << " line " << stack_element.ComputeLineNumber();
}
// pause periodically to help logcat catch up
if ((count % 5) == 0) {
usleep(40000);
}
-
- i = (i + 1) & (alloc_record_max_ - 1);
}
}
@@ -4937,6 +4698,15 @@
std::vector<uint8_t> bytes;
{
MutexLock mu(self, *Locks::alloc_tracker_lock_);
+ gc::AllocRecordObjectMap* records = Runtime::Current()->GetHeap()->GetAllocationRecords();
+ // In case this method is called when allocation tracker is disabled,
+ // we should still send some data back.
+ gc::AllocRecordObjectMap dummy;
+ if (records == nullptr) {
+ CHECK(!Runtime::Current()->GetHeap()->IsAllocTrackingEnabled());
+ records = &dummy;
+ }
+
//
// Part 1: generate string tables.
//
@@ -4944,26 +4714,23 @@
StringTable method_names;
StringTable filenames;
- const uint16_t capped_count = CappedAllocRecordCount(Dbg::alloc_record_count_);
+ const uint16_t capped_count = CappedAllocRecordCount(records->Size());
uint16_t count = capped_count;
- size_t idx = HeadIndex();
- while (count--) {
- AllocRecord* record = &recent_allocation_records_[idx];
+ for (auto it = records->RBegin(), end = records->REnd();
+ count > 0 && it != end; count--, it++) {
+ const gc::AllocRecord* record = it->second;
std::string temp;
- class_names.Add(record->Type()->GetDescriptor(&temp));
- for (size_t i = 0; i < kMaxAllocRecordStackDepth; i++) {
- ArtMethod* m = record->StackElement(i)->Method();
- if (m != nullptr) {
- class_names.Add(m->GetDeclaringClassDescriptor());
- method_names.Add(m->GetName());
- filenames.Add(GetMethodSourceFile(m));
- }
+ class_names.Add(it->first.Read()->GetClass()->GetDescriptor(&temp));
+ for (size_t i = 0, depth = record->GetDepth(); i < depth; i++) {
+ ArtMethod* m = record->StackElement(i).GetMethod();
+ class_names.Add(m->GetDeclaringClassDescriptor());
+ method_names.Add(m->GetName());
+ filenames.Add(GetMethodSourceFile(m));
}
-
- idx = (idx + 1) & (alloc_record_max_ - 1);
}
- LOG(INFO) << "allocation records: " << capped_count;
+ LOG(INFO) << "recent allocation records: " << capped_count;
+ LOG(INFO) << "allocation records all objects: " << records->Size();
//
// Part 2: Generate the output and store it in the buffer.
@@ -4991,20 +4758,23 @@
JDWP::Append2BE(bytes, method_names.Size());
JDWP::Append2BE(bytes, filenames.Size());
- idx = HeadIndex();
std::string temp;
- for (count = capped_count; count != 0; --count) {
+ count = capped_count;
+ // The last "count" number of allocation records in "records" are the most recent "count" number
+ // of allocations. Reverse iterate to get them. The most recent allocation is sent first.
+ for (auto it = records->RBegin(), end = records->REnd();
+ count > 0 && it != end; count--, it++) {
// For each entry:
// (4b) total allocation size
// (2b) thread id
// (2b) allocated object's class name index
// (1b) stack depth
- AllocRecord* record = &recent_allocation_records_[idx];
+ const gc::AllocRecord* record = it->second;
size_t stack_depth = record->GetDepth();
size_t allocated_object_class_name_index =
- class_names.IndexOf(record->Type()->GetDescriptor(&temp));
+ class_names.IndexOf(it->first.Read()->GetClass()->GetDescriptor(&temp));
JDWP::Append4BE(bytes, record->ByteCount());
- JDWP::Append2BE(bytes, record->ThinLockId());
+ JDWP::Append2BE(bytes, static_cast<uint16_t>(record->GetTid()));
JDWP::Append2BE(bytes, allocated_object_class_name_index);
JDWP::Append1BE(bytes, stack_depth);
@@ -5014,16 +4784,15 @@
// (2b) method name
// (2b) method source file
// (2b) line number, clipped to 32767; -2 if native; -1 if no source
- ArtMethod* m = record->StackElement(stack_frame)->Method();
+ ArtMethod* m = record->StackElement(stack_frame).GetMethod();
size_t class_name_index = class_names.IndexOf(m->GetDeclaringClassDescriptor());
size_t method_name_index = method_names.IndexOf(m->GetName());
size_t file_name_index = filenames.IndexOf(GetMethodSourceFile(m));
JDWP::Append2BE(bytes, class_name_index);
JDWP::Append2BE(bytes, method_name_index);
JDWP::Append2BE(bytes, file_name_index);
- JDWP::Append2BE(bytes, record->StackElement(stack_frame)->LineNumber());
+ JDWP::Append2BE(bytes, record->StackElement(stack_frame).ComputeLineNumber());
}
- idx = (idx + 1) & (alloc_record_max_ - 1);
}
// (xb) class name strings
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 7c586a4..3b92d36 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -23,7 +23,6 @@
#include <pthread.h>
-#include <map>
#include <set>
#include <string>
#include <vector>
@@ -32,7 +31,6 @@
#include "jdwp/jdwp.h"
#include "jni.h"
#include "jvalue.h"
-#include "object_callbacks.h"
#include "thread_state.h"
namespace art {
@@ -41,7 +39,6 @@
class Object;
class Throwable;
} // namespace mirror
-class AllocRecord;
class ArtField;
class ArtMethod;
class ObjectRegistry;
@@ -202,19 +199,6 @@
class Dbg {
public:
- class TypeCache {
- public:
- // Returns a weak global for the input type. Deduplicates.
- jobject Add(mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_,
- Locks::alloc_tracker_lock_);
- // Clears the type cache and deletes all the weak global refs.
- void Clear() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_,
- Locks::alloc_tracker_lock_);
-
- private:
- std::multimap<int32_t, jobject> objects_;
- };
-
static void SetJdwpAllowed(bool allowed);
static void StartJdwp();
@@ -655,19 +639,12 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
- * Recent allocation tracking support.
+ * Allocation tracking support.
*/
- static void RecordAllocation(Thread* self, mirror::Class* type, size_t byte_count)
- LOCKS_EXCLUDED(Locks::alloc_tracker_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void SetAllocTrackingEnabled(bool enabled) LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
- static bool IsAllocTrackingEnabled() {
- return recent_allocation_records_ != nullptr;
- }
static jbyteArray GetRecentAllocations()
LOCKS_EXCLUDED(Locks::alloc_tracker_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static size_t HeadIndex() EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
static void DumpRecentAllocations() LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
enum HpifWhen {
@@ -755,11 +732,6 @@
static bool IsForcedInterpreterNeededForUpcallImpl(Thread* thread, ArtMethod* m)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static AllocRecord* recent_allocation_records_ PT_GUARDED_BY(Locks::alloc_tracker_lock_);
- static size_t alloc_record_max_ GUARDED_BY(Locks::alloc_tracker_lock_);
- static size_t alloc_record_head_ GUARDED_BY(Locks::alloc_tracker_lock_);
- static size_t alloc_record_count_ GUARDED_BY(Locks::alloc_tracker_lock_);
-
// Indicates whether the debugger is making requests.
static bool gDebuggerActive;
@@ -784,9 +756,6 @@
static size_t* GetReferenceCounterForEvent(uint32_t instrumentation_event);
- // Weak global type cache, TODO improve this.
- static TypeCache type_cache_ GUARDED_BY(Locks::alloc_tracker_lock_);
-
// Instrumentation event reference counters.
// TODO we could use an array instead of having all these dedicated counters. Instrumentation
// events are bits of a mask so we could convert them to array index.
@@ -798,7 +767,6 @@
static size_t exception_catch_event_ref_count_ GUARDED_BY(Locks::deoptimization_lock_);
static uint32_t instrumentation_events_ GUARDED_BY(Locks::mutator_lock_);
- friend class AllocRecord; // For type_cache_ with proper annotalysis.
DISALLOW_COPY_AND_ASSIGN(Dbg);
};
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index d017601..7ac264a 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -264,13 +264,18 @@
// Raw code_item.
struct CodeItem {
- uint16_t registers_size_;
- uint16_t ins_size_;
- uint16_t outs_size_;
- uint16_t tries_size_;
- uint32_t debug_info_off_; // file offset to debug info stream
+ uint16_t registers_size_; // the number of registers used by this code
+ // (locals + parameters)
+ uint16_t ins_size_; // the number of words of incoming arguments to the method
+ // that this code is for
+ uint16_t outs_size_; // the number of words of outgoing argument space required
+ // by this code for method invocation
+ uint16_t tries_size_; // the number of try_items for this instance. If non-zero,
+ // then these appear as the tries array just after the
+ // insns in this instance.
+ uint32_t debug_info_off_; // file offset to debug info stream
uint32_t insns_size_in_code_units_; // size of the insns array, in 2 byte code units
- uint16_t insns_[1];
+ uint16_t insns_[1]; // actual array of bytecode.
private:
DISALLOW_COPY_AND_ASSIGN(CodeItem);
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index a66c38e..5fa58f7 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -465,7 +465,9 @@
}
bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, uint32_t access_flags,
- uint32_t code_offset, bool expect_direct) {
+ uint32_t code_offset,
+ std::unordered_set<uint32_t>& direct_method_indexes,
+ bool expect_direct) {
if (!CheckIndex(idx, header_->method_ids_size_, "class_data_item method_idx")) {
return false;
}
@@ -480,6 +482,13 @@
return false;
}
+ if (expect_direct) {
+ direct_method_indexes.insert(idx);
+ } else if (direct_method_indexes.find(idx) != direct_method_indexes.end()) {
+ ErrorStringPrintf("Found virtual method with same index as direct method: %d", idx);
+ return false;
+ }
+
constexpr uint32_t access_method_mask = kAccJavaFlagsMask | kAccConstructor |
kAccDeclaredSynchronized;
if (UNLIKELY(((access_flags & ~access_method_mask) != 0) ||
@@ -682,6 +691,7 @@
bool DexFileVerifier::CheckIntraClassDataItem() {
ClassDataItemIterator it(*dex_file_, ptr_);
+ std::unordered_set<uint32_t> direct_method_indexes;
// These calls use the raw access flags to check whether the whole dex field is valid.
@@ -697,13 +707,13 @@
}
for (; it.HasNextDirectMethod(); it.Next()) {
if (!CheckClassDataItemMethod(it.GetMemberIndex(), it.GetRawMemberAccessFlags(),
- it.GetMethodCodeItemOffset(), true)) {
+ it.GetMethodCodeItemOffset(), direct_method_indexes, true)) {
return false;
}
}
for (; it.HasNextVirtualMethod(); it.Next()) {
if (!CheckClassDataItemMethod(it.GetMemberIndex(), it.GetRawMemberAccessFlags(),
- it.GetMethodCodeItemOffset(), false)) {
+ it.GetMethodCodeItemOffset(), direct_method_indexes, false)) {
return false;
}
}
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index 877dfc2..ccc40d4 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -59,6 +59,7 @@
uint32_t* handler_offsets, uint32_t handlers_size);
bool CheckClassDataItemField(uint32_t idx, uint32_t access_flags, bool expect_static);
bool CheckClassDataItemMethod(uint32_t idx, uint32_t access_flags, uint32_t code_offset,
+ std::unordered_set<uint32_t>& direct_method_indexes,
bool expect_direct);
bool CheckPadding(size_t offset, uint32_t aligned_offset);
bool CheckEncodedValue();
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 0c5210d..9fd8c87 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -1401,88 +1401,53 @@
}
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;
- }
+bool ElfFileImpl<ElfTypes>::FixupDebugSections(Elf_Addr base_address_delta) {
if (base_address_delta == 0) {
return true;
}
- if (!ApplyOatPatchesTo(".debug_info", base_address_delta)) {
- return false;
- }
- if (!ApplyOatPatchesTo(".debug_line", base_address_delta)) {
- return false;
- }
- return true;
+ return ApplyOatPatchesTo(".debug_frame", base_address_delta) &&
+ ApplyOatPatchesTo(".debug_info", base_address_delta) &&
+ ApplyOatPatchesTo(".debug_line", base_address_delta);
}
template <typename ElfTypes>
bool ElfFileImpl<ElfTypes>::ApplyOatPatchesTo(
- const char* target_section_name,
- typename std::make_signed<Elf_Off>::type delta) {
- auto patches_section = FindSectionByName(".oat_patches");
+ const char* target_section_name, Elf_Addr delta) {
+ auto target_section = FindSectionByName(target_section_name);
+ if (target_section == nullptr) {
+ return true;
+ }
+ std::string patches_name = target_section_name + std::string(".oat_patches");
+ auto patches_section = FindSectionByName(patches_name.c_str());
if (patches_section == nullptr) {
- LOG(ERROR) << ".oat_patches section not found.";
+ LOG(ERROR) << patches_name << " section not found.";
return false;
}
if (patches_section->sh_type != SHT_OAT_PATCH) {
- LOG(ERROR) << "Unexpected type of .oat_patches.";
+ LOG(ERROR) << "Unexpected type of " << patches_name;
return false;
}
- auto target_section = FindSectionByName(target_section_name);
- if (target_section == nullptr) {
- LOG(ERROR) << target_section_name << " section not found.";
- return false;
- }
- if (!ApplyOatPatches(
+ ApplyOatPatches(
Begin() + patches_section->sh_offset,
Begin() + patches_section->sh_offset + patches_section->sh_size,
- target_section_name, delta,
+ delta,
Begin() + target_section->sh_offset,
- Begin() + target_section->sh_offset + target_section->sh_size)) {
- LOG(ERROR) << target_section_name << " section not found in .oat_patches.";
- }
+ Begin() + target_section->sh_offset + target_section->sh_size);
return true;
}
-// Apply .oat_patches to given section.
+// Apply LEB128 encoded patches to given section.
template <typename ElfTypes>
-bool ElfFileImpl<ElfTypes>::ApplyOatPatches(
- const uint8_t* patches, const uint8_t* patches_end,
- const char* target_section_name,
- typename std::make_signed<Elf_Off>::type delta,
+void ElfFileImpl<ElfTypes>::ApplyOatPatches(
+ const uint8_t* patches, const uint8_t* patches_end, Elf_Addr delta,
uint8_t* to_patch, const uint8_t* to_patch_end) {
- // Read null-terminated section name.
- const char* section_name;
- while ((section_name = reinterpret_cast<const char*>(patches))[0] != '\0') {
- patches += strlen(section_name) + 1;
- uint32_t length = DecodeUnsignedLeb128(&patches);
- const uint8_t* next_section = patches + length;
- // Is it the section we want to patch?
- if (strcmp(section_name, target_section_name) == 0) {
- // Read LEB128 encoded list of advances.
- while (patches < next_section) {
- DCHECK_LT(patches, patches_end) << "Unexpected end of .oat_patches.";
- to_patch += DecodeUnsignedLeb128(&patches);
- DCHECK_LT(to_patch, to_patch_end) << "Patch past the end of " << section_name;
- // TODO: 32-bit vs 64-bit. What is the right type to use here?
- auto* patch_loc = reinterpret_cast<typename std::make_signed<Elf_Off>::type*>(to_patch);
- *patch_loc += delta;
- }
- return true;
- }
- patches = next_section;
+ typedef __attribute__((__aligned__(1))) Elf_Addr UnalignedAddress;
+ while (patches < patches_end) {
+ to_patch += DecodeUnsignedLeb128(&patches);
+ DCHECK_LE(patches, patches_end) << "Unexpected end of patch list.";
+ DCHECK_LT(to_patch, to_patch_end) << "Patch past the end of section.";
+ *reinterpret_cast<UnalignedAddress*>(to_patch) += delta;
}
- return false;
}
template <typename ElfTypes>
diff --git a/runtime/elf_file_impl.h b/runtime/elf_file_impl.h
index 3ad096f..0f466bd 100644
--- a/runtime/elf_file_impl.h
+++ b/runtime/elf_file_impl.h
@@ -119,12 +119,9 @@
bool FixupProgramHeaders(Elf_Addr base_address);
bool FixupSymbols(Elf_Addr base_address, bool dynamic);
bool FixupRelocations(Elf_Addr base_address);
- bool FixupDebugSections(typename std::make_signed<Elf_Off>::type base_address_delta);
- bool ApplyOatPatchesTo(const char* target_section_name,
- typename std::make_signed<Elf_Off>::type base_address_delta);
- static bool ApplyOatPatches(const uint8_t* patches, const uint8_t* patches_end,
- const char* target_section_name,
- typename std::make_signed<Elf_Off>::type delta,
+ bool FixupDebugSections(Elf_Addr base_address_delta);
+ bool ApplyOatPatchesTo(const char* target_section_name, Elf_Addr base_address_delta);
+ static void ApplyOatPatches(const uint8_t* patches, const uint8_t* patches_end, Elf_Addr delta,
uint8_t* to_patch, const uint8_t* to_patch_end);
bool Strip(std::string* error_msg);
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index a4dd55c..b0cbd02 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -38,25 +38,74 @@
namespace art {
-inline ArtMethod* GetCalleeSaveMethodCaller(Thread* self, Runtime::CalleeSaveType type)
+inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method,
+ uint32_t method_index,
+ InvokeType invoke_type)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ArtMethod* caller = outer_method->GetDexCacheResolvedMethod(method_index, sizeof(void*));
+ if (!caller->IsRuntimeMethod()) {
+ return caller;
+ }
+
+ // The method in the dex cache can be the runtime method responsible for invoking
+ // the stub that will then update the dex cache. Therefore, we need to do the
+ // resolution ourselves.
+
+ StackHandleScope<2> hs(Thread::Current());
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(outer_method->GetClassLoader()));
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(outer_method->GetDexCache()));
+ return class_linker->ResolveMethod(
+ *outer_method->GetDexFile(), method_index, dex_cache, class_loader, nullptr, invoke_type);
+}
+
+inline ArtMethod* GetCalleeSaveMethodCaller(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, Runtime::Current()->GetCalleeSaveMethod(type));
+ DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(type));
const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA, type);
auto** caller_sp = reinterpret_cast<ArtMethod**>(
- reinterpret_cast<uintptr_t>(refs_only_sp) + callee_frame_size);
- auto* caller = *caller_sp;
+ reinterpret_cast<uintptr_t>(sp) + callee_frame_size);
+ ArtMethod* outer_method = *caller_sp;
+ ArtMethod* caller = outer_method;
- if (kIsDebugBuild) {
- NthCallerVisitor visitor(self, 1, true);
+ if ((outer_method != nullptr) && outer_method->IsOptimized(sizeof(void*))) {
+ const size_t callee_return_pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, type);
+ uintptr_t caller_pc = *reinterpret_cast<uintptr_t*>(
+ (reinterpret_cast<uint8_t*>(sp) + callee_return_pc_offset));
+ uintptr_t native_pc_offset = outer_method->NativeQuickPcOffset(caller_pc);
+ CodeInfo code_info = outer_method->GetOptimizedCodeInfo();
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
+ DCHECK(stack_map.IsValid());
+ if (stack_map.HasInlineInfo(encoding)) {
+ InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
+ uint32_t method_index = inline_info.GetMethodIndexAtDepth(inline_info.GetDepth() - 1);
+ InvokeType invoke_type = static_cast<InvokeType>(
+ inline_info.GetInvokeTypeAtDepth(inline_info.GetDepth() - 1));
+ caller = GetResolvedMethod(outer_method, method_index, invoke_type);
+ }
+ }
+
+ 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 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/callee_save_frame.h b/runtime/entrypoints/quick/callee_save_frame.h
index 521c549..7a44158 100644
--- a/runtime/entrypoints/quick/callee_save_frame.h
+++ b/runtime/entrypoints/quick/callee_save_frame.h
@@ -36,22 +36,24 @@
class ScopedQuickEntrypointChecks {
public:
- explicit ScopedQuickEntrypointChecks(Thread *self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : self_(self) {
- if (kIsDebugBuild) {
+ explicit ScopedQuickEntrypointChecks(Thread *self,
+ bool entry_check = kIsDebugBuild,
+ bool exit_check = kIsDebugBuild)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : self_(self), exit_check_(exit_check) {
+ if (entry_check) {
TestsOnEntry();
}
}
- explicit ScopedQuickEntrypointChecks() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : self_(kIsDebugBuild ? Thread::Current() : nullptr) {
+ ScopedQuickEntrypointChecks() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : self_(kIsDebugBuild ? Thread::Current() : nullptr), exit_check_(kIsDebugBuild) {
if (kIsDebugBuild) {
TestsOnEntry();
}
}
~ScopedQuickEntrypointChecks() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (kIsDebugBuild) {
+ if (exit_check_) {
TestsOnExit();
}
}
@@ -68,6 +70,7 @@
}
Thread* const self_;
+ bool exit_check_;
};
static constexpr size_t GetCalleeSaveFrameSize(InstructionSet isa, Runtime::CalleeSaveType type) {
diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h
index c7aaa20..fbf028d 100644
--- a/runtime/entrypoints/quick/quick_default_externs.h
+++ b/runtime/entrypoints/quick/quick_default_externs.h
@@ -80,6 +80,7 @@
// Math entrypoints.
extern "C" int64_t art_quick_d2l(double);
extern "C" int64_t art_quick_f2l(float);
+extern "C" float art_quick_l2f(int64_t);
extern "C" int64_t art_quick_ldiv(int64_t, int64_t);
extern "C" int64_t art_quick_lmod(int64_t, int64_t);
extern "C" int64_t art_quick_lmul(int64_t, int64_t);
diff --git a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
index 3eefeef..f1b5445 100644
--- a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "art_method-inl.h"
+#include "base/logging.h"
#include "callee_save_frame.h"
#include "dex_file-inl.h"
#include "interpreter/interpreter.h"
@@ -28,6 +30,13 @@
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->PushAndClearDeoptimizationReturnValue();
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 67649d4..3cefc47 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_instrumentation_entrypoints.cc b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
index 7eb73c3..2b5c15b 100644
--- a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc
@@ -29,7 +29,9 @@
Thread* self,
uintptr_t lr)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ScopedQuickEntrypointChecks sqec(self);
+ // Instrumentation changes the stack. Thus, when exiting, the stack cannot be verified, so skip
+ // that part.
+ ScopedQuickEntrypointChecks sqec(self, kIsDebugBuild, false);
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
const void* result;
if (instrumentation->IsDeoptimized(method)) {
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index bc15cc7..4f76ebd 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -89,7 +89,7 @@
// | LR |
// | X29 |
// | : |
- // | X19 |
+ // | X20 |
// | X7 |
// | : |
// | X1 |
@@ -291,14 +291,43 @@
return reinterpret_cast<StackReference<mirror::Object>*>(this_arg_address)->AsMirrorPtr();
}
- static ArtMethod* GetCallingMethod(ArtMethod** sp)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ static ArtMethod* GetCallingMethod(ArtMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK((*sp)->IsCalleeSaveMethod());
- uint8_t* previous_sp = reinterpret_cast<uint8_t*>(sp) +
- kQuickCalleeSaveFrame_RefAndArgs_FrameSize;
+ return GetCalleeSaveMethodCaller(sp, Runtime::kRefsAndArgs);
+ }
+
+ static ArtMethod* GetOuterMethod(ArtMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK((*sp)->IsCalleeSaveMethod());
+ uint8_t* previous_sp =
+ reinterpret_cast<uint8_t*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_FrameSize;
return *reinterpret_cast<ArtMethod**>(previous_sp);
}
+ static uint32_t GetCallingDexPc(ArtMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK((*sp)->IsCalleeSaveMethod());
+ const size_t callee_frame_size = GetCalleeSaveFrameSize(kRuntimeISA, Runtime::kRefsAndArgs);
+ ArtMethod** caller_sp = reinterpret_cast<ArtMethod**>(
+ reinterpret_cast<uintptr_t>(sp) + callee_frame_size);
+ ArtMethod* outer_method = *caller_sp;
+ uintptr_t outer_pc = QuickArgumentVisitor::GetCallingPc(sp);
+ uintptr_t outer_pc_offset = outer_method->NativeQuickPcOffset(outer_pc);
+
+ if (outer_method->IsOptimized(sizeof(void*))) {
+ CodeInfo code_info = outer_method->GetOptimizedCodeInfo();
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ StackMap stack_map = code_info.GetStackMapForNativePcOffset(outer_pc_offset, encoding);
+ DCHECK(stack_map.IsValid());
+ if (stack_map.HasInlineInfo(encoding)) {
+ InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
+ return inline_info.GetDexPcAtDepth(inline_info.GetDepth() - 1);
+ } else {
+ return stack_map.GetDexPc(encoding);
+ }
+ } else {
+ return outer_method->ToDexPc(outer_pc);
+ }
+ }
+
// For the given quick ref and args quick frame, return the caller's PC.
static uintptr_t GetCallingPc(ArtMethod** sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK((*sp)->IsCalleeSaveMethod());
@@ -647,7 +676,7 @@
ArtMethod* caller = QuickArgumentVisitor::GetCallingMethod(sp);
if (UNLIKELY(Dbg::IsForcedInterpreterNeededForUpcall(self, caller))) {
self->SetException(Thread::GetDeoptimizationException());
- self->SetDeoptimizationReturnValue(result);
+ self->SetDeoptimizationReturnValue(result, shorty[0] == 'L');
}
// No need to restore the args since the method has already been run by the interpreter.
@@ -814,7 +843,10 @@
extern "C" const void* artQuickResolutionTrampoline(
ArtMethod* called, mirror::Object* receiver, Thread* self, ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ScopedQuickEntrypointChecks sqec(self);
+ // The resolution trampoline stashes the resolved method into the callee-save frame to transport
+ // it. Thus, when exiting, the stack cannot be verified (as the resolved method most likely
+ // does not have the same stack layout as the callee-save method).
+ ScopedQuickEntrypointChecks sqec(self, kIsDebugBuild, false);
// Start new JNI local reference state
JNIEnvExt* env = self->GetJniEnv();
ScopedObjectAccessUnchecked soa(env);
@@ -823,12 +855,13 @@
// Compute details about the called method (avoid GCs)
ClassLinker* linker = Runtime::Current()->GetClassLinker();
- ArtMethod* caller = QuickArgumentVisitor::GetCallingMethod(sp);
InvokeType invoke_type;
MethodReference called_method(nullptr, 0);
const bool called_method_known_on_entry = !called->IsRuntimeMethod();
+ 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();
@@ -1941,14 +1974,11 @@
// 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,
- ArtMethod* caller_method, Thread* self, ArtMethod** sp);
-
-template<InvokeType type, bool access_check>
-static TwoWordReturn artInvokeCommon(uint32_t method_idx, mirror::Object* this_object,
- ArtMethod* caller_method, Thread* self, ArtMethod** sp) {
+static TwoWordReturn artInvokeCommon(uint32_t method_idx, mirror::Object* this_object, Thread* self,
+ ArtMethod** sp) {
ScopedQuickEntrypointChecks sqec(self);
DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs));
+ ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp);
ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check, type);
if (UNLIKELY(method == nullptr)) {
const DexFile* dex_file = caller_method->GetDeclaringClass()->GetDexCache()->GetDexFile();
@@ -1984,11 +2014,8 @@
// Explicit artInvokeCommon template function declarations to please analysis tool.
#define EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(type, access_check) \
template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \
- TwoWordReturn artInvokeCommon<type, access_check>(uint32_t method_idx, \
- mirror::Object* this_object, \
- ArtMethod* caller_method, \
- Thread* self, \
- ArtMethod** sp) \
+ TwoWordReturn artInvokeCommon<type, access_check>( \
+ uint32_t method_idx, mirror::Object* this_object, Thread* self, ArtMethod** sp)
EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kVirtual, false);
EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kVirtual, true);
@@ -2004,53 +2031,49 @@
// See comments in runtime_support_asm.S
extern "C" TwoWordReturn artInvokeInterfaceTrampolineWithAccessCheck(
- uint32_t method_idx, mirror::Object* this_object,
- ArtMethod* caller_method, Thread* self, ArtMethod** sp)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon<kInterface, true>(method_idx, this_object,
- caller_method, self, sp);
+ uint32_t method_idx, mirror::Object* this_object, Thread* self, ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return artInvokeCommon<kInterface, true>(method_idx, this_object, self, sp);
}
extern "C" TwoWordReturn artInvokeDirectTrampolineWithAccessCheck(
- uint32_t method_idx, mirror::Object* this_object,
- ArtMethod* caller_method, Thread* self, ArtMethod** sp)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon<kDirect, true>(method_idx, this_object, caller_method,
- self, sp);
+ uint32_t method_idx, mirror::Object* this_object, Thread* self, ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return artInvokeCommon<kDirect, true>(method_idx, this_object, self, sp);
}
extern "C" TwoWordReturn artInvokeStaticTrampolineWithAccessCheck(
- uint32_t method_idx, mirror::Object* this_object,
- ArtMethod* caller_method, Thread* self, ArtMethod** sp)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon<kStatic, true>(method_idx, this_object, caller_method,
- self, sp);
+ uint32_t method_idx, mirror::Object* this_object, Thread* self, ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return artInvokeCommon<kStatic, true>(method_idx, this_object, self, sp);
}
extern "C" TwoWordReturn artInvokeSuperTrampolineWithAccessCheck(
- uint32_t method_idx, mirror::Object* this_object,
- ArtMethod* caller_method, Thread* self, ArtMethod** sp)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon<kSuper, true>(method_idx, this_object, caller_method,
- self, sp);
+ uint32_t method_idx, mirror::Object* this_object, Thread* self, ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return artInvokeCommon<kSuper, true>(method_idx, this_object, self, sp);
}
extern "C" TwoWordReturn artInvokeVirtualTrampolineWithAccessCheck(
- uint32_t method_idx, mirror::Object* this_object,
- ArtMethod* caller_method, Thread* self, ArtMethod** sp)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon<kVirtual, true>(method_idx, this_object, caller_method,
- self, sp);
+ uint32_t method_idx, mirror::Object* this_object, Thread* self, ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ 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(ArtMethod* interface_method,
+extern "C" TwoWordReturn artInvokeInterfaceTrampoline(uint32_t dex_method_idx,
mirror::Object* this_object,
- ArtMethod* caller_method,
- Thread* self,
- ArtMethod** sp)
+ Thread* self, ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
+ // The optimizing compiler currently does not inline methods that have an interface
+ // invocation. We use the outer method directly to avoid fetching a stack map, which is
+ // more expensive.
+ ArtMethod* caller_method = QuickArgumentVisitor::GetOuterMethod(sp);
+ DCHECK_EQ(caller_method, QuickArgumentVisitor::GetCallingMethod(sp));
+ ArtMethod* interface_method = caller_method->GetDexCacheResolvedMethod(
+ dex_method_idx, sizeof(void*));
+ DCHECK(interface_method != nullptr) << dex_method_idx << " " << PrettyMethod(caller_method);
ArtMethod* method;
if (LIKELY(interface_method->GetDexMethodIndex() != DexFile::kDexNoIndex)) {
method = this_object->GetClass()->FindVirtualMethodForInterface(
@@ -2062,26 +2085,21 @@
}
} else {
DCHECK_EQ(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);
- 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]);
- Instruction::Code instr_code = instr->Opcode();
- CHECK(instr_code == Instruction::INVOKE_INTERFACE ||
- instr_code == Instruction::INVOKE_INTERFACE_RANGE)
- << "Unexpected call into interface trampoline: " << instr->DumpString(nullptr);
- uint32_t dex_method_idx;
- if (instr_code == Instruction::INVOKE_INTERFACE) {
- dex_method_idx = instr->VRegB_35c();
- } else {
- DCHECK_EQ(instr_code, Instruction::INVOKE_INTERFACE_RANGE);
- dex_method_idx = instr->VRegB_3rc();
+ if (kIsDebugBuild) {
+ 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]);
+ Instruction::Code instr_code = instr->Opcode();
+ CHECK(instr_code == Instruction::INVOKE_INTERFACE ||
+ instr_code == Instruction::INVOKE_INTERFACE_RANGE)
+ << "Unexpected call into interface trampoline: " << instr->DumpString(nullptr);
+ if (instr_code == Instruction::INVOKE_INTERFACE) {
+ CHECK_EQ(dex_method_idx, instr->VRegB_35c());
+ } else {
+ CHECK_EQ(instr_code, Instruction::INVOKE_INTERFACE_RANGE);
+ CHECK_EQ(dex_method_idx, instr->VRegB_3rc());
+ }
}
const DexFile* dex_file = caller_method->GetDeclaringClass()->GetDexCache()
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 963dd02..0a5ebfa 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -72,6 +72,8 @@
EXPECT_OFFSET_DIFFP(Thread, tls32_, throwing_OutOfMemoryError, no_thread_suspension, 4);
EXPECT_OFFSET_DIFFP(Thread, tls32_, no_thread_suspension, thread_exit_check_count, 4);
EXPECT_OFFSET_DIFFP(Thread, tls32_, thread_exit_check_count, handling_signal_, 4);
+ EXPECT_OFFSET_DIFFP(Thread, tls32_, handling_signal_,
+ deoptimization_return_value_is_reference, 4);
// TODO: Better connection. Take alignment into account.
EXPECT_OFFSET_DIFF_GT3(Thread, tls32_.thread_exit_check_count, tls64_.trace_clock_base, 4,
@@ -103,11 +105,11 @@
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, long_jump_context, instrumentation_stack, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, instrumentation_stack, debug_invoke_req, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, debug_invoke_req, single_step_control, sizeof(void*));
- EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, single_step_control, deoptimization_shadow_frame,
+ EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, single_step_control, stacked_shadow_frame_record,
sizeof(void*));
- EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, deoptimization_shadow_frame,
- shadow_frame_under_construction, sizeof(void*));
- EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, shadow_frame_under_construction, name, sizeof(void*));
+ EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stacked_shadow_frame_record,
+ deoptimization_return_value_stack, sizeof(void*));
+ EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, deoptimization_return_value_stack, name, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, name, pthread_self, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, pthread_self, last_no_thread_suspension_cause,
sizeof(void*));
diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc
new file mode 100644
index 0000000..a385363
--- /dev/null
+++ b/runtime/gc/allocation_record.cc
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "allocation_record.h"
+
+#include "art_method-inl.h"
+#include "base/stl_util.h"
+#include "stack.h"
+
+#ifdef HAVE_ANDROID_OS
+#include "cutils/properties.h"
+#endif
+
+namespace art {
+namespace gc {
+
+int32_t AllocRecordStackTraceElement::ComputeLineNumber() const {
+ DCHECK(method_ != nullptr);
+ return method_->GetLineNumFromDexPC(dex_pc_);
+}
+
+void AllocRecordObjectMap::SetProperties() {
+#ifdef HAVE_ANDROID_OS
+ // Check whether there's a system property overriding the max number of records.
+ const char* propertyName = "dalvik.vm.allocTrackerMax";
+ char allocMaxString[PROPERTY_VALUE_MAX];
+ if (property_get(propertyName, allocMaxString, "") > 0) {
+ char* end;
+ size_t value = strtoul(allocMaxString, &end, 10);
+ if (*end != '\0') {
+ LOG(ERROR) << "Ignoring " << propertyName << " '" << allocMaxString
+ << "' --- invalid";
+ } else {
+ alloc_record_max_ = value;
+ }
+ }
+ // Check whether there's a system property overriding the max depth of stack trace.
+ propertyName = "dalvik.vm.allocStackDepth";
+ char stackDepthString[PROPERTY_VALUE_MAX];
+ if (property_get(propertyName, stackDepthString, "") > 0) {
+ char* end;
+ size_t value = strtoul(stackDepthString, &end, 10);
+ if (*end != '\0') {
+ LOG(ERROR) << "Ignoring " << propertyName << " '" << stackDepthString
+ << "' --- invalid";
+ } else {
+ max_stack_depth_ = value;
+ }
+ }
+#endif
+}
+
+AllocRecordObjectMap::~AllocRecordObjectMap() {
+ STLDeleteValues(&entries_);
+}
+
+void AllocRecordObjectMap::SweepAllocationRecords(IsMarkedCallback* callback, void* arg) {
+ VLOG(heap) << "Start SweepAllocationRecords()";
+ size_t count_deleted = 0, count_moved = 0;
+ for (auto it = entries_.begin(), end = entries_.end(); it != end;) {
+ // This does not need a read barrier because this is called by GC.
+ mirror::Object* old_object = it->first.Read<kWithoutReadBarrier>();
+ AllocRecord* record = it->second;
+ mirror::Object* new_object = callback(old_object, arg);
+ if (new_object == nullptr) {
+ delete record;
+ it = entries_.erase(it);
+ ++count_deleted;
+ } else {
+ if (old_object != new_object) {
+ it->first = GcRoot<mirror::Object>(new_object);
+ ++count_moved;
+ }
+ ++it;
+ }
+ }
+ VLOG(heap) << "Deleted " << count_deleted << " allocation records";
+ VLOG(heap) << "Updated " << count_moved << " allocation records";
+}
+
+struct AllocRecordStackVisitor : public StackVisitor {
+ AllocRecordStackVisitor(Thread* thread, AllocRecordStackTrace* trace_in, size_t max)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ trace(trace_in),
+ depth(0),
+ max_depth(max) {}
+
+ // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
+ // annotalysis.
+ bool VisitFrame() OVERRIDE NO_THREAD_SAFETY_ANALYSIS {
+ if (depth >= max_depth) {
+ return false;
+ }
+ ArtMethod* m = GetMethod();
+ if (!m->IsRuntimeMethod()) {
+ trace->SetStackElementAt(depth, m, GetDexPc());
+ ++depth;
+ }
+ return true;
+ }
+
+ ~AllocRecordStackVisitor() {
+ trace->SetDepth(depth);
+ }
+
+ AllocRecordStackTrace* trace;
+ size_t depth;
+ const size_t max_depth;
+};
+
+void AllocRecordObjectMap::SetAllocTrackingEnabled(bool enable) {
+ Thread* self = Thread::Current();
+ Heap* heap = Runtime::Current()->GetHeap();
+ if (enable) {
+ {
+ MutexLock mu(self, *Locks::alloc_tracker_lock_);
+ if (heap->IsAllocTrackingEnabled()) {
+ return; // Already enabled, bail.
+ }
+ AllocRecordObjectMap* records = new AllocRecordObjectMap();
+ CHECK(records != nullptr);
+ records->SetProperties();
+ std::string self_name;
+ self->GetThreadName(self_name);
+ if (self_name == "JDWP") {
+ records->alloc_ddm_thread_id_ = self->GetTid();
+ }
+ size_t sz = sizeof(AllocRecordStackTraceElement) * records->max_stack_depth_ +
+ sizeof(AllocRecord) + sizeof(AllocRecordStackTrace);
+ LOG(INFO) << "Enabling alloc tracker (" << records->alloc_record_max_ << " entries of "
+ << records->max_stack_depth_ << " frames, taking up to "
+ << PrettySize(sz * records->alloc_record_max_) << ")";
+ heap->SetAllocationRecords(records);
+ heap->SetAllocTrackingEnabled(true);
+ }
+ Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints();
+ } else {
+ {
+ MutexLock mu(self, *Locks::alloc_tracker_lock_);
+ if (!heap->IsAllocTrackingEnabled()) {
+ return; // Already disabled, bail.
+ }
+ heap->SetAllocTrackingEnabled(false);
+ LOG(INFO) << "Disabling alloc tracker";
+ heap->SetAllocationRecords(nullptr);
+ }
+ // If an allocation comes in before we uninstrument, we will safely drop it on the floor.
+ Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints();
+ }
+}
+
+void AllocRecordObjectMap::RecordAllocation(Thread* self, mirror::Object* obj, size_t byte_count) {
+ MutexLock mu(self, *Locks::alloc_tracker_lock_);
+ Heap* heap = Runtime::Current()->GetHeap();
+ if (!heap->IsAllocTrackingEnabled()) {
+ // In the process of shutting down recording, bail.
+ return;
+ }
+
+ AllocRecordObjectMap* records = heap->GetAllocationRecords();
+ DCHECK(records != nullptr);
+
+ // Do not record for DDM thread
+ if (records->alloc_ddm_thread_id_ == self->GetTid()) {
+ return;
+ }
+
+ DCHECK_LE(records->Size(), records->alloc_record_max_);
+
+ // Remove oldest record.
+ if (records->Size() == records->alloc_record_max_) {
+ records->RemoveOldest();
+ }
+
+ // Get stack trace.
+ const size_t max_depth = records->max_stack_depth_;
+ AllocRecordStackTrace* trace = new AllocRecordStackTrace(self->GetTid(), max_depth);
+ // add scope to make "visitor" destroyed promptly, in order to set the trace->depth_
+ {
+ AllocRecordStackVisitor visitor(self, trace, max_depth);
+ visitor.WalkStack();
+ }
+
+ // Fill in the basics.
+ AllocRecord* record = new AllocRecord(byte_count, trace);
+
+ records->Put(obj, record);
+ DCHECK_LE(records->Size(), records->alloc_record_max_);
+}
+
+} // namespace gc
+} // namespace art
diff --git a/runtime/gc/allocation_record.h b/runtime/gc/allocation_record.h
new file mode 100644
index 0000000..45b3406
--- /dev/null
+++ b/runtime/gc/allocation_record.h
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_GC_ALLOCATION_RECORD_H_
+#define ART_RUNTIME_GC_ALLOCATION_RECORD_H_
+
+#include <list>
+
+#include "base/mutex.h"
+#include "object_callbacks.h"
+#include "gc_root.h"
+
+namespace art {
+
+class ArtMethod;
+class Thread;
+
+namespace mirror {
+ class Class;
+ class Object;
+}
+
+namespace gc {
+
+class AllocRecordStackTraceElement {
+ public:
+ AllocRecordStackTraceElement() : method_(nullptr), dex_pc_(0) {}
+
+ int32_t ComputeLineNumber() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ ArtMethod* GetMethod() const {
+ return method_;
+ }
+
+ void SetMethod(ArtMethod* m) {
+ method_ = m;
+ }
+
+ uint32_t GetDexPc() const {
+ return dex_pc_;
+ }
+
+ void SetDexPc(uint32_t pc) {
+ dex_pc_ = pc;
+ }
+
+ bool operator==(const AllocRecordStackTraceElement& other) const {
+ if (this == &other) return true;
+ return method_ == other.method_ && dex_pc_ == other.dex_pc_;
+ }
+
+ private:
+ ArtMethod* method_;
+ uint32_t dex_pc_;
+};
+
+class AllocRecordStackTrace {
+ public:
+ static constexpr size_t kHashMultiplier = 17;
+
+ AllocRecordStackTrace(pid_t tid, size_t max_depth)
+ : tid_(tid), depth_(0), stack_(new AllocRecordStackTraceElement[max_depth]) {}
+
+ ~AllocRecordStackTrace() {
+ delete[] stack_;
+ }
+
+ pid_t GetTid() const {
+ return tid_;
+ }
+
+ size_t GetDepth() const {
+ return depth_;
+ }
+
+ void SetDepth(size_t depth) {
+ depth_ = depth;
+ }
+
+ const AllocRecordStackTraceElement& GetStackElement(size_t index) const {
+ DCHECK_LT(index, depth_);
+ return stack_[index];
+ }
+
+ void SetStackElementAt(size_t index, ArtMethod* m, uint32_t dex_pc) {
+ stack_[index].SetMethod(m);
+ stack_[index].SetDexPc(dex_pc);
+ }
+
+ bool operator==(const AllocRecordStackTrace& other) const {
+ if (this == &other) return true;
+ if (depth_ != other.depth_) return false;
+ for (size_t i = 0; i < depth_; ++i) {
+ if (!(stack_[i] == other.stack_[i])) return false;
+ }
+ return true;
+ }
+
+ private:
+ const pid_t tid_;
+ size_t depth_;
+ AllocRecordStackTraceElement* const stack_;
+};
+
+struct HashAllocRecordTypes {
+ size_t operator()(const AllocRecordStackTraceElement& r) const {
+ return std::hash<void*>()(reinterpret_cast<void*>(r.GetMethod())) *
+ AllocRecordStackTrace::kHashMultiplier + std::hash<uint32_t>()(r.GetDexPc());
+ }
+
+ size_t operator()(const AllocRecordStackTrace& r) const {
+ size_t depth = r.GetDepth();
+ size_t result = r.GetTid() * AllocRecordStackTrace::kHashMultiplier + depth;
+ for (size_t i = 0; i < depth; ++i) {
+ result = result * AllocRecordStackTrace::kHashMultiplier + (*this)(r.GetStackElement(i));
+ }
+ return result;
+ }
+};
+
+template <typename T> struct HashAllocRecordTypesPtr {
+ size_t operator()(const T* r) const {
+ if (r == nullptr) return 0;
+ return HashAllocRecordTypes()(*r);
+ }
+};
+
+template <typename T> struct EqAllocRecordTypesPtr {
+ bool operator()(const T* r1, const T* r2) const {
+ if (r1 == r2) return true;
+ if (r1 == nullptr || r2 == nullptr) return false;
+ return *r1 == *r2;
+ }
+};
+
+class AllocRecord {
+ public:
+ // All instances of AllocRecord should be managed by an instance of AllocRecordObjectMap.
+ AllocRecord(size_t count, AllocRecordStackTrace* trace)
+ : byte_count_(count), trace_(trace) {}
+
+ ~AllocRecord() {
+ delete trace_;
+ }
+
+ size_t GetDepth() const {
+ return trace_->GetDepth();
+ }
+
+ const AllocRecordStackTrace* GetStackTrace() const {
+ return trace_;
+ }
+
+ size_t ByteCount() const {
+ return byte_count_;
+ }
+
+ pid_t GetTid() const {
+ return trace_->GetTid();
+ }
+
+ const AllocRecordStackTraceElement& StackElement(size_t index) const {
+ return trace_->GetStackElement(index);
+ }
+
+ private:
+ const size_t byte_count_;
+ // TODO: Currently trace_ is like a std::unique_ptr,
+ // but in future with deduplication it could be a std::shared_ptr.
+ const AllocRecordStackTrace* const trace_;
+};
+
+class AllocRecordObjectMap {
+ public:
+ // Since the entries contain weak roots, they need a read barrier. Do not directly access
+ // the mirror::Object pointers in it. Use functions that contain read barriers.
+ // No need for "const AllocRecord*" in the list, because all fields of AllocRecord are const.
+ typedef std::list<std::pair<GcRoot<mirror::Object>, AllocRecord*>> EntryList;
+
+ // "static" because it is part of double-checked locking. It needs to check a bool first,
+ // in order to make sure the AllocRecordObjectMap object is not null.
+ static void RecordAllocation(Thread* self, mirror::Object* obj, size_t byte_count)
+ LOCKS_EXCLUDED(Locks::alloc_tracker_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ static void SetAllocTrackingEnabled(bool enabled) LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
+
+ AllocRecordObjectMap() EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_)
+ : alloc_record_max_(kDefaultNumAllocRecords),
+ max_stack_depth_(kDefaultAllocStackDepth),
+ alloc_ddm_thread_id_(0) {}
+
+ ~AllocRecordObjectMap();
+
+ void Put(mirror::Object* obj, AllocRecord* record)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_) {
+ entries_.emplace_back(GcRoot<mirror::Object>(obj), record);
+ }
+
+ size_t Size() const SHARED_LOCKS_REQUIRED(Locks::alloc_tracker_lock_) {
+ return entries_.size();
+ }
+
+ void SweepAllocationRecords(IsMarkedCallback* callback, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
+
+ void RemoveOldest()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_) {
+ DCHECK(!entries_.empty());
+ delete entries_.front().second;
+ entries_.pop_front();
+ }
+
+ // TODO: Is there a better way to hide the entries_'s type?
+ EntryList::iterator Begin()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_) {
+ return entries_.begin();
+ }
+
+ EntryList::iterator End()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_) {
+ return entries_.end();
+ }
+
+ EntryList::reverse_iterator RBegin()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_) {
+ return entries_.rbegin();
+ }
+
+ EntryList::reverse_iterator REnd()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_) {
+ return entries_.rend();
+ }
+
+ private:
+ static constexpr size_t kDefaultNumAllocRecords = 512 * 1024;
+ static constexpr size_t kDefaultAllocStackDepth = 4;
+ size_t alloc_record_max_ GUARDED_BY(Locks::alloc_tracker_lock_);
+ // The implementation always allocates max_stack_depth_ number of frames for each stack trace.
+ // As long as the max depth is not very large, this is not a waste of memory since most stack
+ // traces will fill up the max depth number of the frames.
+ size_t max_stack_depth_ GUARDED_BY(Locks::alloc_tracker_lock_);
+ pid_t alloc_ddm_thread_id_ GUARDED_BY(Locks::alloc_tracker_lock_);
+ EntryList entries_ GUARDED_BY(Locks::alloc_tracker_lock_);
+
+ void SetProperties() EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
+};
+
+} // namespace gc
+} // namespace art
+#endif // ART_RUNTIME_GC_ALLOCATION_RECORD_H_
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 2d54330..ee4568e 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -22,6 +22,7 @@
#include "base/time_utils.h"
#include "debugger.h"
#include "gc/accounting/card_table-inl.h"
+#include "gc/allocation_record.h"
#include "gc/collector/semi_space.h"
#include "gc/space/bump_pointer_space-inl.h"
#include "gc/space/dlmalloc_space-inl.h"
@@ -168,11 +169,11 @@
PushOnAllocationStack(self, &obj);
}
if (kInstrumented) {
- if (Dbg::IsAllocTrackingEnabled()) {
- Dbg::RecordAllocation(self, klass, bytes_allocated);
+ if (IsAllocTrackingEnabled()) {
+ AllocRecordObjectMap::RecordAllocation(self, obj, bytes_allocated);
}
} else {
- DCHECK(!Dbg::IsAllocTrackingEnabled());
+ DCHECK(!IsAllocTrackingEnabled());
}
// IsConcurrentGc() isn't known at compile time so we can optimize by not checking it for
// the BumpPointer or TLAB allocators. This is nice since it allows the entire if statement to be
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index eabbbec..22207ee 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -209,7 +209,8 @@
blocking_gc_count_last_window_(0U),
gc_count_rate_histogram_("gc count rate histogram", 1U, kGcCountRateMaxBucketCount),
blocking_gc_count_rate_histogram_("blocking gc count rate histogram", 1U,
- kGcCountRateMaxBucketCount) {
+ kGcCountRateMaxBucketCount),
+ alloc_tracking_enabled_(false) {
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
LOG(INFO) << "Heap() entering";
}
@@ -1064,6 +1065,7 @@
STLDeleteElements(&garbage_collectors_);
// If we don't reset then the mark stack complains in its destructor.
allocation_stack_->Reset();
+ allocation_records_.reset();
live_stack_->Reset();
STLDeleteValues(&mod_union_tables_);
STLDeleteValues(&remembered_sets_);
@@ -3674,5 +3676,18 @@
}
}
+void Heap::SetAllocationRecords(AllocRecordObjectMap* records) {
+ allocation_records_.reset(records);
+}
+
+void Heap::SweepAllocationRecords(IsMarkedCallback* visitor, void* arg) const {
+ if (IsAllocTrackingEnabled()) {
+ MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_);
+ if (IsAllocTrackingEnabled()) {
+ GetAllocationRecords()->SweepAllocationRecords(visitor, arg);
+ }
+ }
+}
+
} // namespace gc
} // namespace art
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index dac747b..18244c8 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -58,6 +58,7 @@
namespace gc {
+class AllocRecordObjectMap;
class ReferenceProcessor;
class TaskProcessor;
@@ -684,6 +685,27 @@
void DumpGcCountRateHistogram(std::ostream& os) const;
void DumpBlockingGcCountRateHistogram(std::ostream& os) const;
+ // Allocation tracking support
+ // Callers to this function use double-checked locking to ensure safety on allocation_records_
+ bool IsAllocTrackingEnabled() const {
+ return alloc_tracking_enabled_.LoadRelaxed();
+ }
+
+ void SetAllocTrackingEnabled(bool enabled) EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_) {
+ alloc_tracking_enabled_.StoreRelaxed(enabled);
+ }
+
+ AllocRecordObjectMap* GetAllocationRecords() const
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_) {
+ return allocation_records_.get();
+ }
+
+ void SetAllocationRecords(AllocRecordObjectMap* records)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
+
+ void SweepAllocationRecords(IsMarkedCallback* visitor, void* arg) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
private:
class ConcurrentGCTask;
class CollectorTransitionTask;
@@ -1192,6 +1214,11 @@
// The histogram of the number of blocking GC invocations per window duration.
Histogram<uint64_t> blocking_gc_count_rate_histogram_ GUARDED_BY(gc_complete_lock_);
+ // Allocation tracking support
+ Atomic<bool> alloc_tracking_enabled_;
+ std::unique_ptr<AllocRecordObjectMap> allocation_records_
+ GUARDED_BY(Locks::alloc_tracker_lock_);
+
friend class CollectorTransitionTask;
friend class collector::GarbageCollector;
friend class collector::MarkCompact;
diff --git a/runtime/globals.h b/runtime/globals.h
index fe699c6..d70f3ab 100644
--- a/runtime/globals.h
+++ b/runtime/globals.h
@@ -97,7 +97,7 @@
kUseTableLookupReadBarrier;
// If true, references within the heap are poisoned (negated).
-#ifdef ART_HEAP_POISONING
+#ifdef USE_HEAP_POISONING
static constexpr bool kPoisonHeapReferences = true;
#else
static constexpr bool kPoisonHeapReferences = false;
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 6e0e56e..f32d5a1 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -48,6 +48,7 @@
#include "dex_file-inl.h"
#include "gc_root.h"
#include "gc/accounting/heap_bitmap.h"
+#include "gc/allocation_record.h"
#include "gc/heap.h"
#include "gc/space/space.h"
#include "globals.h"
@@ -68,14 +69,13 @@
static constexpr bool kDirectStream = true;
static constexpr uint32_t kHprofTime = 0;
-static constexpr uint32_t kHprofNullStackTrace = 0;
static constexpr uint32_t kHprofNullThread = 0;
static constexpr size_t kMaxObjectsPerSegment = 128;
static constexpr size_t kMaxBytesPerSegment = 4096;
// The static field-name for the synthetic object generated to account for class static overhead.
-static constexpr const char* kStaticOverheadName = "$staticOverhead";
+static constexpr const char* kClassOverheadName = "$classOverhead";
enum HprofTag {
HPROF_TAG_STRING = 0x01,
@@ -144,6 +144,10 @@
typedef uint32_t HprofStringId;
typedef uint32_t HprofClassObjectId;
+typedef uint32_t HprofClassSerialNumber;
+typedef uint32_t HprofStackTraceSerialNumber;
+typedef uint32_t HprofStackFrameId;
+static constexpr HprofStackTraceSerialNumber kHprofNullStackTrace = 0;
class EndianOutput {
public:
@@ -194,6 +198,10 @@
AddU4(PointerToLowMemUInt32(value));
}
+ void AddStackTraceSerialNumber(HprofStackTraceSerialNumber value) {
+ AddU4(value);
+ }
+
// The ID for the synthetic object generated to account for class static overhead.
void AddClassStaticsId(const mirror::Class* value) {
AddU4(1 | PointerToLowMemUInt32(value));
@@ -415,13 +423,21 @@
start_ns_(NanoTime()),
current_heap_(HPROF_HEAP_DEFAULT),
objects_in_segment_(0),
- next_string_id_(0x400000) {
+ next_string_id_(0x400000),
+ next_class_serial_number_(1) {
LOG(INFO) << "hprof: heap dump \"" << filename_ << "\" starting...";
}
void Dump()
EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
- LOCKS_EXCLUDED(Locks::heap_bitmap_lock_) {
+ LOCKS_EXCLUDED(Locks::heap_bitmap_lock_, Locks::alloc_tracker_lock_) {
+ {
+ MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_);
+ if (Runtime::Current()->GetHeap()->IsAllocTrackingEnabled()) {
+ PopulateAllocationTrackingTraces();
+ }
+ }
+
// First pass to measure the size of the dump.
size_t overall_size;
size_t max_length;
@@ -480,11 +496,11 @@
objects_in_segment_ = 0;
if (header_first) {
- ProcessHeader();
+ ProcessHeader(true);
ProcessBody();
} else {
ProcessBody();
- ProcessHeader();
+ ProcessHeader(false);
}
}
@@ -501,21 +517,29 @@
output_->EndRecord();
}
- void ProcessHeader() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ void ProcessHeader(bool string_first) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Write the header.
WriteFixedHeader();
// Write the string and class tables, and any stack traces, to the header.
// (jhat requires that these appear before any of the data in the body that refers to them.)
- WriteStringTable();
+ // jhat also requires the string table appear before class table and stack traces.
+ // However, WriteStackTraces() can modify the string table, so it's necessary to call
+ // WriteStringTable() last in the first pass, to compute the correct length of the output.
+ if (string_first) {
+ WriteStringTable();
+ }
WriteClassTable();
WriteStackTraces();
+ if (!string_first) {
+ WriteStringTable();
+ }
output_->EndRecord();
}
void WriteClassTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- uint32_t nextSerialNumber = 1;
-
- for (mirror::Class* c : classes_) {
+ for (const auto& p : classes_) {
+ mirror::Class* c = p.first;
+ HprofClassSerialNumber sn = p.second;
CHECK(c != nullptr);
output_->StartNewRecord(HPROF_TAG_LOAD_CLASS, kHprofTime);
// LOAD CLASS format:
@@ -523,9 +547,9 @@
// ID: class object ID. We use the address of the class object structure as its ID.
// U4: stack trace serial number
// ID: class name string ID
- __ AddU4(nextSerialNumber++);
+ __ AddU4(sn);
__ AddObjectId(c);
- __ AddU4(kHprofNullStackTrace);
+ __ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(c));
__ AddStringId(LookupClassNameId(c));
}
}
@@ -567,15 +591,31 @@
HprofClassObjectId LookupClassId(mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (c != nullptr) {
- auto result = classes_.insert(c);
- const mirror::Class* present = *result.first;
- CHECK_EQ(present, c);
- // Make sure that we've assigned a string ID for this class' name
- LookupClassNameId(c);
+ auto it = classes_.find(c);
+ if (it == classes_.end()) {
+ // first time to see this class
+ HprofClassSerialNumber sn = next_class_serial_number_++;
+ classes_.Put(c, sn);
+ // Make sure that we've assigned a string ID for this class' name
+ LookupClassNameId(c);
+ }
}
return PointerToLowMemUInt32(c);
}
+ HprofStackTraceSerialNumber LookupStackTraceSerialNumber(const mirror::Object* obj)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ auto r = allocation_records_.find(obj);
+ if (r == allocation_records_.end()) {
+ return kHprofNullStackTrace;
+ } else {
+ const gc::AllocRecordStackTrace* trace = r->second;
+ auto result = traces_.find(trace);
+ CHECK(result != traces_.end());
+ return result->second;
+ }
+ }
+
HprofStringId LookupStringId(mirror::String* string) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return LookupStringId(string->ToModifiedUtf8());
}
@@ -622,12 +662,66 @@
__ AddU4(static_cast<uint32_t>(nowMs & 0xFFFFFFFF));
}
- void WriteStackTraces() {
+ void WriteStackTraces() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Write a dummy stack trace record so the analysis tools don't freak out.
output_->StartNewRecord(HPROF_TAG_STACK_TRACE, kHprofTime);
- __ AddU4(kHprofNullStackTrace);
+ __ AddStackTraceSerialNumber(kHprofNullStackTrace);
__ AddU4(kHprofNullThread);
__ AddU4(0); // no frames
+
+ // TODO: jhat complains "WARNING: Stack trace not found for serial # -1", but no trace should
+ // have -1 as its serial number (as long as HprofStackTraceSerialNumber doesn't overflow).
+ for (const auto& it : traces_) {
+ const gc::AllocRecordStackTrace* trace = it.first;
+ HprofStackTraceSerialNumber trace_sn = it.second;
+ size_t depth = trace->GetDepth();
+
+ // First write stack frames of the trace
+ for (size_t i = 0; i < depth; ++i) {
+ const gc::AllocRecordStackTraceElement* frame = &trace->GetStackElement(i);
+ ArtMethod* method = frame->GetMethod();
+ CHECK(method != nullptr);
+ output_->StartNewRecord(HPROF_TAG_STACK_FRAME, kHprofTime);
+ // STACK FRAME format:
+ // ID: stack frame ID. We use the address of the AllocRecordStackTraceElement object as its ID.
+ // ID: method name string ID
+ // ID: method signature string ID
+ // ID: source file name string ID
+ // U4: class serial number
+ // U4: >0, line number; 0, no line information available; -1, unknown location
+ auto frame_result = frames_.find(frame);
+ CHECK(frame_result != frames_.end());
+ __ AddU4(frame_result->second);
+ __ AddStringId(LookupStringId(method->GetName()));
+ __ AddStringId(LookupStringId(method->GetSignature().ToString()));
+ const char* source_file = method->GetDeclaringClassSourceFile();
+ if (source_file == nullptr) {
+ source_file = "";
+ }
+ __ AddStringId(LookupStringId(source_file));
+ auto class_result = classes_.find(method->GetDeclaringClass());
+ CHECK(class_result != classes_.end());
+ __ AddU4(class_result->second);
+ __ AddU4(frame->ComputeLineNumber());
+ }
+
+ // Then write the trace itself
+ output_->StartNewRecord(HPROF_TAG_STACK_TRACE, kHprofTime);
+ // STACK TRACE format:
+ // U4: stack trace serial number. We use the address of the AllocRecordStackTrace object as its serial number.
+ // U4: thread serial number. We use Thread::GetTid().
+ // U4: number of frames
+ // [ID]*: series of stack frame ID's
+ __ AddStackTraceSerialNumber(trace_sn);
+ __ AddU4(trace->GetTid());
+ __ AddU4(depth);
+ for (size_t i = 0; i < depth; ++i) {
+ const gc::AllocRecordStackTraceElement* frame = &trace->GetStackElement(i);
+ auto frame_result = frames_.find(frame);
+ CHECK(frame_result != frames_.end());
+ __ AddU4(frame_result->second);
+ }
+ }
}
bool DumpToDdmsBuffered(size_t overall_size ATTRIBUTE_UNUSED, size_t max_length ATTRIBUTE_UNUSED)
@@ -723,6 +817,40 @@
return true;
}
+ void PopulateAllocationTrackingTraces()
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::alloc_tracker_lock_) {
+ gc::AllocRecordObjectMap* records = Runtime::Current()->GetHeap()->GetAllocationRecords();
+ CHECK(records != nullptr);
+ HprofStackTraceSerialNumber next_trace_sn = kHprofNullStackTrace + 1;
+ HprofStackFrameId next_frame_id = 0;
+
+ for (auto it = records->Begin(), end = records->End(); it != end; ++it) {
+ const mirror::Object* obj = it->first.Read();
+ const gc::AllocRecordStackTrace* trace = it->second->GetStackTrace();
+
+ // Copy the pair into a real hash map to speed up look up.
+ auto records_result = allocation_records_.emplace(obj, trace);
+ // The insertion should always succeed, i.e. no duplicate object pointers in "records"
+ CHECK(records_result.second);
+
+ // Generate serial numbers for traces, and IDs for frames.
+ auto traces_result = traces_.find(trace);
+ if (traces_result == traces_.end()) {
+ traces_.emplace(trace, next_trace_sn++);
+ // only check frames if the trace is newly discovered
+ for (size_t i = 0, depth = trace->GetDepth(); i < depth; ++i) {
+ const gc::AllocRecordStackTraceElement* frame = &trace->GetStackElement(i);
+ auto frames_result = frames_.find(frame);
+ if (frames_result == frames_.end()) {
+ frames_.emplace(frame, next_frame_id++);
+ }
+ }
+ }
+ }
+ CHECK_EQ(traces_.size(), next_trace_sn - kHprofNullStackTrace - 1);
+ CHECK_EQ(frames_.size(), next_frame_id);
+ }
+
// If direct_to_ddms_ is set, "filename_" and "fd" will be ignored.
// Otherwise, "filename_" must be valid, though if "fd" >= 0 it will
// only be used for debug messages.
@@ -737,9 +865,18 @@
HprofHeapId current_heap_; // Which heap we're currently dumping.
size_t objects_in_segment_;
- std::set<mirror::Class*> classes_;
HprofStringId next_string_id_;
SafeMap<std::string, HprofStringId> strings_;
+ HprofClassSerialNumber next_class_serial_number_;
+ SafeMap<mirror::Class*, HprofClassSerialNumber> classes_;
+
+ std::unordered_map<const gc::AllocRecordStackTrace*, HprofStackTraceSerialNumber,
+ gc::HashAllocRecordTypesPtr<gc::AllocRecordStackTrace>,
+ gc::EqAllocRecordTypesPtr<gc::AllocRecordStackTrace>> traces_;
+ std::unordered_map<const gc::AllocRecordStackTraceElement*, HprofStackFrameId,
+ gc::HashAllocRecordTypesPtr<gc::AllocRecordStackTraceElement>,
+ gc::EqAllocRecordTypesPtr<gc::AllocRecordStackTraceElement>> frames_;
+ std::unordered_map<const mirror::Object*, const gc::AllocRecordStackTrace*> allocation_records_;
DISALLOW_COPY_AND_ASSIGN(Hprof);
};
@@ -881,10 +1018,6 @@
++objects_in_segment_;
}
-static int StackTraceSerialNumber(const mirror::Object* /*obj*/) {
- return kHprofNullStackTrace;
-}
-
void Hprof::DumpHeapObject(mirror::Object* obj) {
// Ignore classes that are retired.
if (obj->IsClass() && obj->AsClass()->IsRetired()) {
@@ -959,24 +1092,30 @@
// Class is allocated but not yet loaded: we cannot access its fields or super class.
return;
}
- size_t sFieldCount = klass->NumStaticFields();
- if (sFieldCount != 0) {
- int byteLength = sFieldCount * sizeof(JValue); // TODO bogus; fields are packed
+ const size_t num_static_fields = klass->NumStaticFields();
+ // Total class size including embedded IMT, embedded vtable, and static fields.
+ const size_t class_size = klass->GetClassSize();
+ // Class size excluding static fields (relies on reference fields being the first static fields).
+ const size_t class_size_without_overhead = sizeof(mirror::Class);
+ CHECK_LE(class_size_without_overhead, class_size);
+ const size_t overhead_size = class_size - class_size_without_overhead;
+
+ if (overhead_size != 0) {
// Create a byte array to reflect the allocation of the
// StaticField array at the end of this class.
__ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
__ AddClassStaticsId(klass);
- __ AddU4(StackTraceSerialNumber(klass));
- __ AddU4(byteLength);
+ __ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(klass));
+ __ AddU4(overhead_size);
__ AddU1(hprof_basic_byte);
- for (int i = 0; i < byteLength; ++i) {
+ for (size_t i = 0; i < overhead_size; ++i) {
__ AddU1(0);
}
}
__ AddU1(HPROF_CLASS_DUMP);
__ AddClassId(LookupClassId(klass));
- __ AddU4(StackTraceSerialNumber(klass));
+ __ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(klass));
__ AddClassId(LookupClassId(klass->GetSuperClass()));
__ AddObjectId(klass->GetClassLoader());
__ AddObjectId(nullptr); // no signer
@@ -986,7 +1125,7 @@
if (klass->IsClassClass()) {
// ClassObjects have their static fields appended, so aren't all the same size.
// But they're at least this size.
- __ AddU4(sizeof(mirror::Class)); // instance size
+ __ AddU4(class_size_without_overhead); // instance size
} else if (klass->IsStringClass()) {
// Strings are variable length with character data at the end like arrays.
// This outputs the size of an empty string.
@@ -1000,15 +1139,15 @@
__ AddU2(0); // empty const pool
// Static fields
- if (sFieldCount == 0) {
- __ AddU2((uint16_t)0);
+ if (overhead_size == 0) {
+ __ AddU2(static_cast<uint16_t>(0));
} else {
- __ AddU2((uint16_t)(sFieldCount+1));
- __ AddStringId(LookupStringId(kStaticOverheadName));
+ __ AddU2(static_cast<uint16_t>(num_static_fields + 1));
+ __ AddStringId(LookupStringId(kClassOverheadName));
__ AddU1(hprof_basic_object);
__ AddClassStaticsId(klass);
- for (size_t i = 0; i < sFieldCount; ++i) {
+ for (size_t i = 0; i < num_static_fields; ++i) {
ArtField* f = klass->GetStaticField(i);
size_t size;
@@ -1072,7 +1211,7 @@
__ AddU1(HPROF_OBJECT_ARRAY_DUMP);
__ AddObjectId(obj);
- __ AddU4(StackTraceSerialNumber(obj));
+ __ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(obj));
__ AddU4(length);
__ AddClassId(LookupClassId(klass));
@@ -1087,7 +1226,7 @@
__ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
__ AddObjectId(obj);
- __ AddU4(StackTraceSerialNumber(obj));
+ __ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(obj));
__ AddU4(length);
__ AddU1(t);
@@ -1108,7 +1247,7 @@
// obj is an instance object.
__ AddU1(HPROF_INSTANCE_DUMP);
__ AddObjectId(obj);
- __ AddU4(StackTraceSerialNumber(obj));
+ __ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(obj));
__ AddClassId(LookupClassId(klass));
// Reserve some space for the length of the instance data, which we won't
@@ -1170,7 +1309,7 @@
__ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
__ AddObjectId(value);
- __ AddU4(StackTraceSerialNumber(obj));
+ __ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(obj));
__ AddU4(s->GetLength());
__ AddU1(hprof_basic_char);
__ AddU2List(s->GetValue(), s->GetLength());
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 4ced23d..d37ddcb 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -1019,7 +1019,7 @@
PrettyMethod(method).c_str(),
return_value.GetJ()) << *self;
}
- self->SetDeoptimizationReturnValue(return_value);
+ self->SetDeoptimizationReturnValue(return_value, return_shorty == 'L');
return GetTwoWordSuccessValue(*return_pc,
reinterpret_cast<uintptr_t>(GetQuickDeoptimizationEntryPoint()));
} else {
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 1ed1a64..0f6f788 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -450,10 +450,13 @@
static inline void AssignRegister(ShadowFrame* new_shadow_frame, const ShadowFrame& shadow_frame,
size_t dest_reg, size_t src_reg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- // If both register locations contains the same value, the register probably holds a reference.
// Uint required, so that sign extension does not make this wrong on 64b systems
uint32_t src_value = shadow_frame.GetVReg(src_reg);
mirror::Object* o = shadow_frame.GetVRegReference<kVerifyNone>(src_reg);
+
+ // If both register locations contains the same value, the register probably holds a reference.
+ // Note: As an optimization, non-moving collectors leave a stale reference value
+ // in the references array even after the original vreg was overwritten to a non-reference.
if (src_value == reinterpret_cast<uintptr_t>(o)) {
new_shadow_frame->SetVRegReference(dest_reg, o);
} else {
@@ -517,7 +520,8 @@
// Slow path.
// We might need to do class loading, which incurs a thread state change to kNative. So
// register the shadow frame as under construction and allow suspension again.
- self->SetShadowFrameUnderConstruction(new_shadow_frame);
+ ScopedStackedShadowFramePusher pusher(
+ self, new_shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction);
self->EndAssertNoThreadSuspension(old_cause);
// We need to do runtime check on reference assignment. We need to load the shorty
@@ -590,8 +594,6 @@
break;
}
}
- // We're done with the construction.
- self->ClearShadowFrameUnderConstruction();
} else {
// Fast path: no extra checks.
if (is_range) {
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index dd7aa40..fcf083c 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -56,7 +56,7 @@
template<bool do_access_check, bool transaction_active>
JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register) {
- bool do_assignability_check = do_access_check;
+ constexpr bool do_assignability_check = do_access_check;
if (UNLIKELY(!shadow_frame.HasReferenceArray())) {
LOG(FATAL) << "Invalid shadow frame for interpreter use";
return JValue();
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 6ab4455..cb36183 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -406,7 +406,7 @@
CHECK_NON_NULL_ARGUMENT(java_class);
ScopedObjectAccess soa(env);
mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
- return soa.AddLocalReference<jclass>(c->GetSuperClass());
+ return soa.AddLocalReference<jclass>(c->IsInterface() ? nullptr : c->GetSuperClass());
}
// Note: java_class1 should be safely castable to java_class2, and
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 99eb365..2a0cb28 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -1238,7 +1238,7 @@
ASSERT_NE(runnable_interface, nullptr);
ASSERT_TRUE(env_->IsSameObject(object_class, env_->GetSuperclass(string_class)));
ASSERT_EQ(env_->GetSuperclass(object_class), nullptr);
- ASSERT_TRUE(env_->IsSameObject(object_class, env_->GetSuperclass(runnable_interface)));
+ ASSERT_EQ(env_->GetSuperclass(runnable_interface), nullptr);
// Null as class should fail.
CheckJniAbortCatcher jni_abort_catcher;
diff --git a/runtime/jvalue.h b/runtime/jvalue.h
index b39567b..6a6d198 100644
--- a/runtime/jvalue.h
+++ b/runtime/jvalue.h
@@ -61,6 +61,8 @@
uint8_t GetZ() const { return z; }
void SetZ(uint8_t new_z) { z = new_z; }
+ mirror::Object** GetGCRoot() { return &l; }
+
private:
uint8_t z;
int8_t b;
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index d6d71f2..189306d 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -221,7 +221,7 @@
// We call this here so that we can try and generate a full error
// message with the overlapping mapping. There's no guarantee that
// that there will be an overlap though, since
- // - The kernel is not *required* to honour expected_ptr unless MAP_FIXED is
+ // - The kernel is not *required* to honor expected_ptr unless MAP_FIXED is
// true, even if there is no overlap
// - There might have been an overlap at the point of mmap, but the
// overlapping region has since been unmapped.
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index d343292..88d75ab 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -239,7 +239,7 @@
}
template<typename T>
-template<bool kTransactionActive, bool kCheckTransaction>
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
inline void PrimitiveArray<T>::SetWithoutChecks(int32_t i, T value) {
if (kCheckTransaction) {
DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
@@ -247,7 +247,7 @@
if (kTransactionActive) {
Runtime::Current()->RecordWriteArray(this, i, GetWithoutChecks(i));
}
- DCHECK(CheckIsValidIndex(i));
+ DCHECK(CheckIsValidIndex<kVerifyFlags>(i));
GetData()[i] = value;
}
// Backward copy where elements are of aligned appropriately for T. Count is in T sized units.
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index c4f6c84..e65611d 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -133,7 +133,9 @@
// TODO fix thread safety analysis broken by the use of template. This should be
// SHARED_LOCKS_REQUIRED(Locks::mutator_lock_).
- template<bool kTransactionActive, bool kCheckTransaction = true>
+ template<bool kTransactionActive,
+ bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetWithoutChecks(int32_t i, T value) ALWAYS_INLINE NO_THREAD_SAFETY_ANALYSIS;
/*
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 835b94a..8c9222f 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -666,7 +666,7 @@
inline void Class::VisitReferences(mirror::Class* klass, const Visitor& visitor) {
VisitInstanceFieldsReferences<kVisitClass>(klass, visitor);
// Right after a class is allocated, but not yet loaded
- // (kStatusNotReady, see ClassLinkder::LoadClass()), GC may find it
+ // (kStatusNotReady, see ClassLinker::LoadClass()), GC may find it
// and scan it. IsTemp() may call Class::GetAccessFlags() but may
// fail in the DCHECK in Class::GetAccessFlags() because the class
// status is kStatusNotReady. To avoid it, rely on IsResolved()
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index ba8a693..551e7e2 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1207,7 +1207,12 @@
// Descriptor for the class such as "java.lang.Class" or "[C". Lazily initialized by ComputeName
HeapReference<String> name_;
- // The superclass, or null if this is java.lang.Object, an interface or primitive type.
+ // The superclass, or null if this is java.lang.Object or a primitive type.
+ //
+ // Note that interfaces have java.lang.Object as their
+ // superclass. This doesn't match the expectations in JNI
+ // GetSuperClass or java.lang.Class.getSuperClass() which need to
+ // check for interfaces and return null.
HeapReference<Class> super_class_;
// If class verify fails, we must return same error on subsequent tries.
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 05c44e5..e019d5a 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -60,19 +60,23 @@
OFFSET_OF_OBJECT_MEMBER(Object, klass_), new_klass);
}
+template<VerifyObjectFlags kVerifyFlags>
inline LockWord Object::GetLockWord(bool as_volatile) {
if (as_volatile) {
- return LockWord(GetField32Volatile(OFFSET_OF_OBJECT_MEMBER(Object, monitor_)));
+ return LockWord(GetField32Volatile<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Object, monitor_)));
}
- return LockWord(GetField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_)));
+ return LockWord(GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Object, monitor_)));
}
+template<VerifyObjectFlags kVerifyFlags>
inline void Object::SetLockWord(LockWord new_val, bool as_volatile) {
// Force use of non-transactional mode and do not check.
if (as_volatile) {
- SetField32Volatile<false, false>(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), new_val.GetValue());
+ SetField32Volatile<false, false, kVerifyFlags>(
+ OFFSET_OF_OBJECT_MEMBER(Object, monitor_), new_val.GetValue());
} else {
- SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), new_val.GetValue());
+ SetField32<false, false, kVerifyFlags>(
+ OFFSET_OF_OBJECT_MEMBER(Object, monitor_), new_val.GetValue());
}
}
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 60c756a..f1c96b5 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -125,7 +125,9 @@
// As_volatile can be false if the mutators are suspended. This is an optimization since it
// avoids the barriers.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
LockWord GetLockWord(bool as_volatile) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
void SetLockWord(LockWord new_val, bool as_volatile) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool CasLockWordWeakSequentiallyConsistent(LockWord old_val, LockWord new_val)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index 9f6cd11..8d9c08d 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -176,11 +176,13 @@
}
template <bool kIsInstrumented>
-inline String* String::AllocFromCharArray(Thread* self, int32_t array_length,
+inline String* String::AllocFromCharArray(Thread* self, int32_t count,
Handle<CharArray> array, int32_t offset,
gc::AllocatorType allocator_type) {
- SetStringCountAndValueVisitorFromCharArray visitor(array_length, array, offset);
- String* new_string = Alloc<kIsInstrumented>(self, array_length, allocator_type, visitor);
+ // It is a caller error to have a count less than the actual array's size.
+ DCHECK_GE(array->GetLength(), count);
+ SetStringCountAndValueVisitorFromCharArray visitor(count, array, offset);
+ String* new_string = Alloc<kIsInstrumented>(self, count, allocator_type, visitor);
return new_string;
}
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index a8f16d7..af06385 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -95,7 +95,7 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template <bool kIsInstrumented>
- ALWAYS_INLINE static String* AllocFromCharArray(Thread* self, int32_t array_length,
+ ALWAYS_INLINE static String* AllocFromCharArray(Thread* self, int32_t count,
Handle<CharArray> array, int32_t offset,
gc::AllocatorType allocator_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 1a7a3e5..1d06706 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -65,7 +65,7 @@
DEBUG_ENABLE_SAFEMODE = 1 << 3,
DEBUG_ENABLE_JNI_LOGGING = 1 << 4,
DEBUG_ENABLE_JIT = 1 << 5,
- DEBUG_GENERATE_CFI = 1 << 6,
+ DEBUG_GENERATE_DEBUG_INFO = 1 << 6,
};
Runtime* const runtime = Runtime::Current();
@@ -112,10 +112,10 @@
}
runtime->GetJITOptions()->SetUseJIT(use_jit);
- const bool generate_cfi = (debug_flags & DEBUG_GENERATE_CFI) != 0;
- if (generate_cfi) {
- runtime->AddCompilerOption("--include-cfi");
- debug_flags &= ~DEBUG_GENERATE_CFI;
+ const bool generate_debug_info = (debug_flags & DEBUG_GENERATE_DEBUG_INFO) != 0;
+ if (generate_debug_info) {
+ runtime->AddCompilerOption("--generate-debug-info");
+ debug_flags &= ~DEBUG_GENERATE_DEBUG_INFO;
}
// This is for backwards compatibility with Dalvik.
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 0c39f2b..1515630 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -55,60 +55,29 @@
return nullptr;
}
-static jint VMClassLoader_getBootClassPathSize(JNIEnv*, jclass) {
- return Runtime::Current()->GetClassLinker()->GetBootClassPath().size();
-}
-
/*
- * Returns a string URL for a resource with the specified 'javaName' in
- * entry 'index' of the boot class path.
- *
- * We return a newly-allocated String in the following form:
- *
- * jar:file://path!/name
- *
- * Where "path" is the bootstrap class path entry and "name" is the string
- * passed into this method. "path" needs to be an absolute path (starting
- * with '/'); if it's not we'd need to make it absolute as part of forming
- * the URL string.
+ * Returns an array of entries from the boot classpath that could contain resources.
*/
-static jstring VMClassLoader_getBootClassPathResource(JNIEnv* env, jclass, jstring javaName,
- jint index) {
- ScopedUtfChars name(env, javaName);
- if (name.c_str() == nullptr) {
- return nullptr;
- }
-
+static jobjectArray VMClassLoader_getBootClassPathEntries(JNIEnv* env, jclass) {
const std::vector<const DexFile*>& path =
Runtime::Current()->GetClassLinker()->GetBootClassPath();
- if (index < 0 || size_t(index) >= path.size()) {
- return nullptr;
- }
- const DexFile* dex_file = path[index];
+ jclass stringClass = env->FindClass("java/lang/String");
+ jobjectArray array = env->NewObjectArray(path.size(), stringClass, nullptr);
+ for (size_t i = 0; i < path.size(); ++i) {
+ const DexFile* dex_file = path[i];
- // For multidex locations, e.g., x.jar:classes2.dex, we want to look into x.jar.
- const std::string& location(dex_file->GetBaseLocation());
+ // For multidex locations, e.g., x.jar:classes2.dex, we want to look into x.jar.
+ const std::string& location(dex_file->GetBaseLocation());
- std::string error_msg;
- std::unique_ptr<ZipArchive> zip_archive(ZipArchive::Open(location.c_str(), &error_msg));
- if (zip_archive.get() == nullptr) {
- LOG(WARNING) << "Failed to open zip archive '" << location << "': " << error_msg;
- return nullptr;
+ jstring javaPath = env->NewStringUTF(location.c_str());
+ env->SetObjectArrayElement(array, i, javaPath);
}
- std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(name.c_str(), &error_msg));
- if (zip_entry.get() == nullptr) {
- return nullptr;
- }
-
- std::string url;
- StringAppendF(&url, "jar:file://%s!/%s", location.c_str(), name.c_str());
- return env->NewStringUTF(url.c_str());
+ return array;
}
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMClassLoader, findLoadedClass, "!(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;"),
- NATIVE_METHOD(VMClassLoader, getBootClassPathResource, "(Ljava/lang/String;I)Ljava/lang/String;"),
- NATIVE_METHOD(VMClassLoader, getBootClassPathSize, "!()I"),
+ NATIVE_METHOD(VMClassLoader, getBootClassPathEntries, "()[Ljava/lang/String;"),
};
void register_java_lang_VMClassLoader(JNIEnv* env) {
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
index b96ddc8..9ce4a02 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
@@ -38,7 +38,7 @@
}
static jboolean DdmVmInternal_getRecentAllocationStatus(JNIEnv*, jclass) {
- return Dbg::IsAllocTrackingEnabled();
+ return Runtime::Current()->GetHeap()->IsAllocTrackingEnabled();
}
/*
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 0bc834f..4b563b5 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -368,23 +368,28 @@
return true;
}
-bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognized,
- RuntimeArgumentMap* runtime_options) {
+// Intended for local changes only.
+static void MaybeOverrideVerbosity() {
// gLogVerbosity.class_linker = true; // TODO: don't check this in!
// gLogVerbosity.compiler = true; // TODO: don't check this in!
+ // gLogVerbosity.deopt = true; // TODO: don't check this in!
// gLogVerbosity.gc = true; // TODO: don't check this in!
// gLogVerbosity.heap = true; // TODO: don't check this in!
// gLogVerbosity.jdwp = true; // TODO: don't check this in!
// gLogVerbosity.jit = true; // TODO: don't check this in!
// gLogVerbosity.jni = true; // TODO: don't check this in!
// gLogVerbosity.monitor = true; // TODO: don't check this in!
+ // gLogVerbosity.oat = true; // TODO: don't check this in!
// gLogVerbosity.profiler = true; // TODO: don't check this in!
// gLogVerbosity.signals = true; // TODO: don't check this in!
// gLogVerbosity.startup = true; // TODO: don't check this in!
// gLogVerbosity.third_party_jni = true; // TODO: don't check this in!
// gLogVerbosity.threads = true; // TODO: don't check this in!
// gLogVerbosity.verifier = true; // TODO: don't check this in!
+}
+bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognized,
+ RuntimeArgumentMap* runtime_options) {
for (size_t i = 0; i < options.size(); ++i) {
if (true && options[0].first == "-Xzygote") {
LOG(INFO) << "option[" << i << "]=" << options[i].first;
@@ -453,6 +458,8 @@
}
}
+ MaybeOverrideVerbosity();
+
// -Xprofile:
Trace::SetDefaultClockSource(args.GetOrDefault(M::ProfileClock));
diff --git a/runtime/profiler.cc b/runtime/profiler.cc
index ab28a9a..87b0d43 100644
--- a/runtime/profiler.cc
+++ b/runtime/profiler.cc
@@ -304,7 +304,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/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 8c9782a..02baad7 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -163,8 +163,8 @@
: StackVisitor(self, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
self_(self),
exception_handler_(exception_handler),
- prev_shadow_frame_(nullptr) {
- CHECK(!self_->HasDeoptimizationShadowFrame());
+ prev_shadow_frame_(nullptr),
+ stacked_shadow_frame_pushed_(false) {
}
bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -174,6 +174,13 @@
// This is the upcall, we remember the frame and last pc so that we may long jump to them.
exception_handler_->SetHandlerQuickFramePc(GetCurrentQuickFramePc());
exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
+ if (!stacked_shadow_frame_pushed_) {
+ // In case there is no deoptimized shadow frame for this upcall, we still
+ // need to push a nullptr to the stack since there is always a matching pop after
+ // the long jump.
+ self_->PushStackedShadowFrame(nullptr, StackedShadowFrameType::kDeoptimizationShadowFrame);
+ stacked_shadow_frame_pushed_ = true;
+ }
return false; // End stack walk.
} else if (method->IsRuntimeMethod()) {
// Ignore callee save method.
@@ -204,111 +211,116 @@
bool verifier_success = verifier.Verify();
CHECK(verifier_success) << PrettyMethod(m);
ShadowFrame* new_frame = ShadowFrame::CreateDeoptimizedFrame(num_regs, nullptr, m, dex_pc);
- self_->SetShadowFrameUnderConstruction(new_frame);
- const std::vector<int32_t> kinds(verifier.DescribeVRegs(dex_pc));
+ {
+ ScopedStackedShadowFramePusher pusher(self_, new_frame,
+ StackedShadowFrameType::kShadowFrameUnderConstruction);
+ const std::vector<int32_t> kinds(verifier.DescribeVRegs(dex_pc));
- // Markers for dead values, used when the verifier knows a Dex register is undefined,
- // or when the compiler knows the register has not been initialized, or is not used
- // anymore in the method.
- static constexpr uint32_t kDeadValue = 0xEBADDE09;
- static constexpr uint64_t kLongDeadValue = 0xEBADDE09EBADDE09;
- for (uint16_t reg = 0; reg < num_regs; ++reg) {
- VRegKind kind = GetVRegKind(reg, kinds);
- switch (kind) {
- case kUndefined:
- new_frame->SetVReg(reg, kDeadValue);
- break;
- case kConstant:
- new_frame->SetVReg(reg, kinds.at((reg * 2) + 1));
- break;
- case kReferenceVReg: {
- uint32_t value = 0;
- // Check IsReferenceVReg in case the compiled GC map doesn't agree with the verifier.
- // We don't want to copy a stale reference into the shadow frame as a reference.
- // b/20736048
- if (GetVReg(m, reg, kind, &value) && IsReferenceVReg(m, reg)) {
- new_frame->SetVRegReference(reg, reinterpret_cast<mirror::Object*>(value));
- } else {
+ // Markers for dead values, used when the verifier knows a Dex register is undefined,
+ // or when the compiler knows the register has not been initialized, or is not used
+ // anymore in the method.
+ static constexpr uint32_t kDeadValue = 0xEBADDE09;
+ static constexpr uint64_t kLongDeadValue = 0xEBADDE09EBADDE09;
+ for (uint16_t reg = 0; reg < num_regs; ++reg) {
+ VRegKind kind = GetVRegKind(reg, kinds);
+ switch (kind) {
+ case kUndefined:
new_frame->SetVReg(reg, kDeadValue);
+ break;
+ case kConstant:
+ new_frame->SetVReg(reg, kinds.at((reg * 2) + 1));
+ break;
+ case kReferenceVReg: {
+ uint32_t value = 0;
+ // Check IsReferenceVReg in case the compiled GC map doesn't agree with the verifier.
+ // We don't want to copy a stale reference into the shadow frame as a reference.
+ // b/20736048
+ if (GetVReg(m, reg, kind, &value) && IsReferenceVReg(m, reg)) {
+ new_frame->SetVRegReference(reg, reinterpret_cast<mirror::Object*>(value));
+ } else {
+ new_frame->SetVReg(reg, kDeadValue);
+ }
+ break;
}
- break;
+ case kLongLoVReg:
+ if (GetVRegKind(reg + 1, kinds) == kLongHiVReg) {
+ // Treat it as a "long" register pair.
+ uint64_t value = 0;
+ if (GetVRegPair(m, reg, kLongLoVReg, kLongHiVReg, &value)) {
+ new_frame->SetVRegLong(reg, value);
+ } else {
+ new_frame->SetVRegLong(reg, kLongDeadValue);
+ }
+ } else {
+ uint32_t value = 0;
+ if (GetVReg(m, reg, kind, &value)) {
+ new_frame->SetVReg(reg, value);
+ } else {
+ new_frame->SetVReg(reg, kDeadValue);
+ }
+ }
+ break;
+ case kLongHiVReg:
+ if (GetVRegKind(reg - 1, kinds) == kLongLoVReg) {
+ // Nothing to do: we treated it as a "long" register pair.
+ } else {
+ uint32_t value = 0;
+ if (GetVReg(m, reg, kind, &value)) {
+ new_frame->SetVReg(reg, value);
+ } else {
+ new_frame->SetVReg(reg, kDeadValue);
+ }
+ }
+ break;
+ case kDoubleLoVReg:
+ if (GetVRegKind(reg + 1, kinds) == kDoubleHiVReg) {
+ uint64_t value = 0;
+ if (GetVRegPair(m, reg, kDoubleLoVReg, kDoubleHiVReg, &value)) {
+ // Treat it as a "double" register pair.
+ new_frame->SetVRegLong(reg, value);
+ } else {
+ new_frame->SetVRegLong(reg, kLongDeadValue);
+ }
+ } else {
+ uint32_t value = 0;
+ if (GetVReg(m, reg, kind, &value)) {
+ new_frame->SetVReg(reg, value);
+ } else {
+ new_frame->SetVReg(reg, kDeadValue);
+ }
+ }
+ break;
+ case kDoubleHiVReg:
+ if (GetVRegKind(reg - 1, kinds) == kDoubleLoVReg) {
+ // Nothing to do: we treated it as a "double" register pair.
+ } else {
+ uint32_t value = 0;
+ if (GetVReg(m, reg, kind, &value)) {
+ new_frame->SetVReg(reg, value);
+ } else {
+ new_frame->SetVReg(reg, kDeadValue);
+ }
+ }
+ break;
+ default:
+ uint32_t value = 0;
+ if (GetVReg(m, reg, kind, &value)) {
+ new_frame->SetVReg(reg, value);
+ } else {
+ new_frame->SetVReg(reg, kDeadValue);
+ }
+ break;
}
- case kLongLoVReg:
- if (GetVRegKind(reg + 1, kinds) == kLongHiVReg) {
- // Treat it as a "long" register pair.
- uint64_t value = 0;
- if (GetVRegPair(m, reg, kLongLoVReg, kLongHiVReg, &value)) {
- new_frame->SetVRegLong(reg, value);
- } else {
- new_frame->SetVRegLong(reg, kLongDeadValue);
- }
- } else {
- uint32_t value = 0;
- if (GetVReg(m, reg, kind, &value)) {
- new_frame->SetVReg(reg, value);
- } else {
- new_frame->SetVReg(reg, kDeadValue);
- }
- }
- break;
- case kLongHiVReg:
- if (GetVRegKind(reg - 1, kinds) == kLongLoVReg) {
- // Nothing to do: we treated it as a "long" register pair.
- } else {
- uint32_t value = 0;
- if (GetVReg(m, reg, kind, &value)) {
- new_frame->SetVReg(reg, value);
- } else {
- new_frame->SetVReg(reg, kDeadValue);
- }
- }
- break;
- case kDoubleLoVReg:
- if (GetVRegKind(reg + 1, kinds) == kDoubleHiVReg) {
- uint64_t value = 0;
- if (GetVRegPair(m, reg, kDoubleLoVReg, kDoubleHiVReg, &value)) {
- // Treat it as a "double" register pair.
- new_frame->SetVRegLong(reg, value);
- } else {
- new_frame->SetVRegLong(reg, kLongDeadValue);
- }
- } else {
- uint32_t value = 0;
- if (GetVReg(m, reg, kind, &value)) {
- new_frame->SetVReg(reg, value);
- } else {
- new_frame->SetVReg(reg, kDeadValue);
- }
- }
- break;
- case kDoubleHiVReg:
- if (GetVRegKind(reg - 1, kinds) == kDoubleLoVReg) {
- // Nothing to do: we treated it as a "double" register pair.
- } else {
- uint32_t value = 0;
- if (GetVReg(m, reg, kind, &value)) {
- new_frame->SetVReg(reg, value);
- } else {
- new_frame->SetVReg(reg, kDeadValue);
- }
- }
- break;
- default:
- uint32_t value = 0;
- if (GetVReg(m, reg, kind, &value)) {
- new_frame->SetVReg(reg, value);
- } else {
- new_frame->SetVReg(reg, kDeadValue);
- }
- break;
}
}
if (prev_shadow_frame_ != nullptr) {
prev_shadow_frame_->SetLink(new_frame);
} else {
- self_->SetDeoptimizationShadowFrame(new_frame);
+ // Will be popped after the long jump after DeoptimizeStack(),
+ // right before interpreter::EnterInterpreterFromDeoptimize().
+ stacked_shadow_frame_pushed_ = true;
+ self_->PushStackedShadowFrame(new_frame, StackedShadowFrameType::kDeoptimizationShadowFrame);
}
- self_->ClearShadowFrameUnderConstruction();
prev_shadow_frame_ = new_frame;
return true;
}
@@ -316,6 +328,7 @@
Thread* const self_;
QuickExceptionHandler* const exception_handler_;
ShadowFrame* prev_shadow_frame_;
+ bool stacked_shadow_frame_pushed_;
DISALLOW_COPY_AND_ASSIGN(DeoptimizeStackVisitor);
};
diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h
index d341ee1..8d84c35 100644
--- a/runtime/read_barrier-inl.h
+++ b/runtime/read_barrier-inl.h
@@ -31,7 +31,7 @@
template <typename MirrorType, ReadBarrierOption kReadBarrierOption, bool kMaybeDuringStartup>
inline MirrorType* ReadBarrier::Barrier(
mirror::Object* obj, MemberOffset offset, mirror::HeapReference<MirrorType>* ref_addr) {
- const bool with_read_barrier = kReadBarrierOption == kWithReadBarrier;
+ constexpr bool with_read_barrier = kReadBarrierOption == kWithReadBarrier;
if (with_read_barrier && kUseBakerReadBarrier) {
// The higher bits of the rb ptr, rb_ptr_high_bits (must be zero)
// is used to create artificial data dependency from the is_gray
diff --git a/runtime/read_barrier_c.h b/runtime/read_barrier_c.h
index 88bda3a..4f408dd 100644
--- a/runtime/read_barrier_c.h
+++ b/runtime/read_barrier_c.h
@@ -31,6 +31,10 @@
// #define USE_TABLE_LOOKUP_READ_BARRIER
#endif
+#ifdef ART_HEAP_POISONING
+#define USE_HEAP_POISONING
+#endif
+
#if defined(USE_BAKER_READ_BARRIER) || defined(USE_BROOKS_READ_BARRIER)
#define USE_BAKER_OR_BROOKS_READ_BARRIER
#endif
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 4a2a0c9..66ec7cc 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -402,6 +402,7 @@
GetInternTable()->SweepInternTableWeaks(visitor, arg);
GetMonitorList()->SweepMonitorList(visitor, arg);
GetJavaVM()->SweepJniWeakGlobals(visitor, arg);
+ GetHeap()->SweepAllocationRecords(visitor, arg);
}
bool Runtime::Create(const RuntimeOptions& options, bool ignore_unrecognized) {
@@ -1475,6 +1476,11 @@
monitor_list_->DisallowNewMonitors();
intern_table_->DisallowNewInterns();
java_vm_->DisallowNewWeakGlobals();
+ // TODO: add a similar call for heap.allocation_records_, otherwise some of the newly allocated
+ // objects that are not marked might be swept from the records, making the records incomplete.
+ // It is safe for now since the only effect is that those objects do not have allocation records.
+ // The number of such objects should be small, and current allocation tracker cannot collect
+ // allocation records for all objects anyway.
}
void Runtime::AllowNewSystemWeaks() {
diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc
index d65e18e..f0b3c4e 100644
--- a/runtime/runtime_linux.cc
+++ b/runtime/runtime_linux.cc
@@ -340,6 +340,9 @@
<< "Thread: " << tid << " \"" << thread_name << "\"\n"
<< "Registers:\n" << Dumpable<UContext>(thread_context) << "\n"
<< "Backtrace:\n" << Dumpable<Backtrace>(thread_backtrace);
+ if (kIsDebugBuild && signal_number == SIGSEGV) {
+ PrintFileToLog("/proc/self/maps", LogSeverity::INTERNAL_FATAL);
+ }
Runtime* runtime = Runtime::Current();
if (runtime != nullptr) {
if (IsTimeoutSignal(signal_number)) {
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 8b504c1..4a307d5 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -30,6 +30,8 @@
// If a default value is omitted here, T{} is used as the default value, which is
// almost-always the value of the type as if it was memset to all 0.
//
+// Please keep the columns aligned if possible when adding new rows.
+//
// Parse-able keys from the command line.
RUNTIME_OPTIONS_KEY (Unit, Zygote)
@@ -62,11 +64,11 @@
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)
-RUNTIME_OPTIONS_KEY (MemoryKiB, JITCodeCacheCapacity, jit::JitCodeCache::kDefaultCapacity)
+RUNTIME_OPTIONS_KEY (bool, UseJIT, false)
+RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold, jit::Jit::kDefaultCompileThreshold)
+RUNTIME_OPTIONS_KEY (MemoryKiB, JITCodeCacheCapacity, jit::JitCodeCache::kDefaultCapacity)
RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
HSpaceCompactForOOMMinIntervalsMs,\
MsToNs(100 * 1000)) // 100s
@@ -105,9 +107,12 @@
ImageCompilerOptions) // -Ximage-compiler-option ...
RUNTIME_OPTIONS_KEY (bool, Verify, true)
RUNTIME_OPTIONS_KEY (std::string, NativeBridge)
+RUNTIME_OPTIONS_KEY (unsigned int, ZygoteMaxFailedBoots, 10)
+RUNTIME_OPTIONS_KEY (Unit, NoDexFileFallback)
RUNTIME_OPTIONS_KEY (std::string, CpuAbiList)
// Not parse-able from command line, but can be provided explicitly.
+// (Do not add anything here that is defined in ParsedOptions::MakeParser)
RUNTIME_OPTIONS_KEY (const std::vector<const DexFile*>*, \
BootClassPathDexList) // TODO: make unique_ptr
RUNTIME_OPTIONS_KEY (InstructionSet, ImageInstructionSet, kRuntimeISA)
@@ -120,7 +125,5 @@
// We don't call abort(3) by default; see
// Runtime::Abort.
RUNTIME_OPTIONS_KEY (void (*)(), HookAbort, nullptr)
-RUNTIME_OPTIONS_KEY (unsigned int, ZygoteMaxFailedBoots, 10)
-RUNTIME_OPTIONS_KEY (Unit, NoDexFileFallback)
#undef RUNTIME_OPTIONS_KEY
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 6cca4d2..5aeca98 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -19,6 +19,7 @@
#include "arch/context.h"
#include "art_method-inl.h"
#include "base/hex_dump.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "gc_map.h"
#include "gc/space/image_space.h"
@@ -103,15 +104,49 @@
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 {
+ ArtMethod* outer_method = *GetCurrentQuickFrame();
+ uint32_t native_pc_offset = outer_method->NativeQuickPcOffset(cur_quick_frame_pc_);
+ CodeInfo code_info = outer_method->GetOptimizedCodeInfo();
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
+ DCHECK(stack_map.IsValid());
+ return code_info.GetInlineInfoOf(stack_map, encoding);
+}
+
+ArtMethod* StackVisitor::GetMethod() const {
+ 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;
+ InlineInfo inline_info = GetCurrentInlineInfo();
+ uint32_t method_index = inline_info.GetMethodIndexAtDepth(depth_in_stack_map);
+ InvokeType invoke_type =
+ static_cast<InvokeType>(inline_info.GetInvokeTypeAtDepth(depth_in_stack_map));
+ return GetResolvedMethod(*GetCurrentQuickFrame(), method_index, invoke_type);
+ } else {
+ return *cur_quick_frame_;
+ }
+ }
+ 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;
}
@@ -229,35 +264,51 @@
bool StackVisitor::GetVRegFromOptimizedCode(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_);
+
+ ArtMethod* outer_method = *GetCurrentQuickFrame();
+ const void* code_pointer = outer_method->GetQuickOatCodePointer(sizeof(void*));
+ DCHECK(code_pointer != nullptr);
+ CodeInfo code_info = outer_method->GetOptimizedCodeInfo();
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+
+ uint32_t native_pc_offset = outer_method->NativeQuickPcOffset(cur_quick_frame_pc_);
+ StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
+ DCHECK(stack_map.IsValid());
+ 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, encoding),
+ encoding,
+ number_of_dex_registers)
+ : code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers);
+
DexRegisterLocation::Kind location_kind =
- dex_register_map.GetLocationKind(vreg, number_of_dex_registers, code_info);
+ dex_register_map.GetLocationKind(vreg, number_of_dex_registers, code_info, encoding);
switch (location_kind) {
case DexRegisterLocation::Kind::kInStack: {
- const int32_t offset =
- dex_register_map.GetStackOffsetInBytes(vreg, number_of_dex_registers, code_info);
+ const int32_t offset = dex_register_map.GetStackOffsetInBytes(vreg,
+ number_of_dex_registers,
+ code_info,
+ encoding);
const uint8_t* addr = reinterpret_cast<const uint8_t*>(cur_quick_frame_) + offset;
*val = *reinterpret_cast<const uint32_t*>(addr);
return true;
}
case DexRegisterLocation::Kind::kInRegister:
case DexRegisterLocation::Kind::kInFpuRegister: {
- uint32_t reg = dex_register_map.GetMachineRegister(vreg, number_of_dex_registers, code_info);
+ uint32_t reg =
+ dex_register_map.GetMachineRegister(vreg, number_of_dex_registers, code_info, encoding);
return GetRegisterIfAccessible(reg, kind, val);
}
case DexRegisterLocation::Kind::kConstant:
- *val = dex_register_map.GetConstant(vreg, number_of_dex_registers, code_info);
+ *val = dex_register_map.GetConstant(vreg, number_of_dex_registers, code_info, encoding);
return true;
case DexRegisterLocation::Kind::kNone:
return false;
@@ -265,7 +316,10 @@
LOG(FATAL)
<< "Unexpected location kind"
<< DexRegisterLocation::PrettyDescriptor(
- dex_register_map.GetLocationInternalKind(vreg, number_of_dex_registers, code_info));
+ dex_register_map.GetLocationInternalKind(vreg,
+ number_of_dex_registers,
+ code_info,
+ encoding));
UNREACHABLE();
}
}
@@ -734,6 +788,28 @@
ArtMethod* method = *cur_quick_frame_;
while (method != nullptr) {
SanityCheckFrame();
+
+ if ((walk_kind_ == StackWalkKind::kIncludeInlinedFrames)
+ && method->IsOptimized(sizeof(void*))) {
+ CodeInfo code_info = method->GetOptimizedCodeInfo();
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ uint32_t native_pc_offset = method->NativeQuickPcOffset(cur_quick_frame_pc_);
+ StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
+ if (stack_map.IsValid() && stack_map.HasInlineInfo(encoding)) {
+ InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
+ 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;
+ }
+ cur_depth_++;
+ }
+ }
+ }
+
bool should_continue = VisitFrame();
if (UNLIKELY(!should_continue)) {
return;
diff --git a/runtime/stack.h b/runtime/stack.h
index 38dfe1b..d60714f 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -36,9 +36,10 @@
class ArtMethod;
class Context;
-class ShadowFrame;
class HandleScope;
+class InlineInfo;
class ScopedObjectAccess;
+class ShadowFrame;
class StackVisitor;
class Thread;
@@ -94,6 +95,8 @@
}
~ShadowFrame() {}
+ // TODO(iam): Clean references array up since they're always there,
+ // we don't need to do conditionals.
bool HasReferenceArray() const {
return true;
}
@@ -148,6 +151,9 @@
return *reinterpret_cast<unaligned_double*>(vreg);
}
+ // Look up the reference given its virtual register number.
+ // If this returns non-null then this does not mean the vreg is currently a reference
+ // on non-moving collectors. Check that the raw reg with GetVReg is equal to this if not certain.
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
mirror::Object* GetVRegReference(size_t i) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK_LT(i, NumberOfVRegs());
@@ -282,6 +288,8 @@
ShadowFrame(uint32_t num_vregs, ShadowFrame* link, ArtMethod* method,
uint32_t dex_pc, bool has_reference_array)
: number_of_vregs_(num_vregs), link_(link), method_(method), dex_pc_(dex_pc) {
+ // TODO(iam): Remove this parameter, it's an an artifact of portable removal
+ DCHECK(has_reference_array);
if (has_reference_array) {
memset(vregs_, 0, num_vregs * (sizeof(uint32_t) + sizeof(StackReference<mirror::Object>)));
} else {
@@ -305,6 +313,15 @@
ShadowFrame* link_;
ArtMethod* method_;
uint32_t dex_pc_;
+
+ // This is a two-part array:
+ // - [0..number_of_vregs) holds the raw virtual registers, and each element here is always 4
+ // bytes.
+ // - [number_of_vregs..number_of_vregs*2) holds only reference registers. Each element here is
+ // ptr-sized.
+ // In other words when a primitive is stored in vX, the second (reference) part of the array will
+ // be null. When a reference is stored in vX, the second (reference) part of the array will be a
+ // copy of vX.
uint32_t vregs_[0];
DISALLOW_IMPLICIT_CONSTRUCTORS(ShadowFrame);
@@ -425,15 +442,7 @@
void WalkStack(bool include_transitions = false)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- 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_;
- } else {
- return nullptr;
- }
- }
+ ArtMethod* GetMethod() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsShadowFrame() const {
return cur_shadow_frame_ != nullptr;
@@ -576,7 +585,7 @@
}
bool IsInInlinedFrame() const {
- return false;
+ return current_inlining_depth_ != 0;
}
uintptr_t GetCurrentQuickFramePc() const {
@@ -668,6 +677,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_;
@@ -677,6 +688,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..f8fc2a9 100644
--- a/runtime/stack_map.cc
+++ b/runtime/stack_map.cc
@@ -18,6 +18,8 @@
#include <stdint.h>
+#include "indenter.h"
+
namespace art {
constexpr size_t DexRegisterLocationCatalog::kNoLocationEntryIndex;
@@ -26,9 +28,10 @@
DexRegisterLocation::Kind DexRegisterMap::GetLocationInternalKind(uint16_t dex_register_number,
uint16_t number_of_dex_registers,
- const CodeInfo& code_info) const {
+ const CodeInfo& code_info,
+ const StackMapEncoding& enc) const {
DexRegisterLocationCatalog dex_register_location_catalog =
- code_info.GetDexRegisterLocationCatalog();
+ code_info.GetDexRegisterLocationCatalog(enc);
size_t location_catalog_entry_index = GetLocationCatalogEntryIndex(
dex_register_number,
number_of_dex_registers,
@@ -38,9 +41,10 @@
DexRegisterLocation DexRegisterMap::GetDexRegisterLocation(uint16_t dex_register_number,
uint16_t number_of_dex_registers,
- const CodeInfo& code_info) const {
+ const CodeInfo& code_info,
+ const StackMapEncoding& enc) const {
DexRegisterLocationCatalog dex_register_location_catalog =
- code_info.GetDexRegisterLocationCatalog();
+ code_info.GetDexRegisterLocationCatalog(enc);
size_t location_catalog_entry_index = GetLocationCatalogEntryIndex(
dex_register_number,
number_of_dex_registers,
@@ -48,230 +52,183 @@
return dex_register_location_catalog.GetDexRegisterLocation(location_catalog_entry_index);
}
-// Loads `number_of_bytes` at the given `offset` and assemble a uint32_t. If `check_max` is true,
-// this method converts a maximum value of size `number_of_bytes` into a uint32_t 0xFFFFFFFF.
-static uint32_t LoadAt(MemoryRegion region,
- size_t number_of_bytes,
- size_t offset,
- bool check_max = false) {
+uint32_t StackMap::LoadAt(size_t number_of_bytes, size_t offset, bool check_max) const {
if (number_of_bytes == 0u) {
DCHECK(!check_max);
return 0;
} else if (number_of_bytes == 1u) {
- uint8_t value = region.LoadUnaligned<uint8_t>(offset);
- if (check_max && value == 0xFF) {
- return -1;
- } else {
- return value;
- }
+ uint8_t value = region_.LoadUnaligned<uint8_t>(offset);
+ return (check_max && value == 0xFF) ? -1 : value;
} else if (number_of_bytes == 2u) {
- uint16_t value = region.LoadUnaligned<uint16_t>(offset);
- if (check_max && value == 0xFFFF) {
- return -1;
- } else {
- return value;
- }
+ uint16_t value = region_.LoadUnaligned<uint16_t>(offset);
+ return (check_max && value == 0xFFFF) ? -1 : value;
} else if (number_of_bytes == 3u) {
- uint16_t low = region.LoadUnaligned<uint16_t>(offset);
- uint16_t high = region.LoadUnaligned<uint8_t>(offset + sizeof(uint16_t));
+ uint16_t low = region_.LoadUnaligned<uint16_t>(offset);
+ uint16_t high = region_.LoadUnaligned<uint8_t>(offset + sizeof(uint16_t));
uint32_t value = (high << 16) + low;
- if (check_max && value == 0xFFFFFF) {
- return -1;
- } else {
- return value;
- }
+ return (check_max && value == 0xFFFFFF) ? -1 : value;
} else {
DCHECK_EQ(number_of_bytes, 4u);
- return region.LoadUnaligned<uint32_t>(offset);
+ return region_.LoadUnaligned<uint32_t>(offset);
}
}
-static void StoreAt(MemoryRegion region, size_t number_of_bytes, size_t offset, uint32_t value) {
+void StackMap::StoreAt(size_t number_of_bytes, size_t offset, uint32_t value) const {
if (number_of_bytes == 0u) {
DCHECK_EQ(value, 0u);
} else if (number_of_bytes == 1u) {
- region.StoreUnaligned<uint8_t>(offset, value);
+ region_.StoreUnaligned<uint8_t>(offset, value);
} else if (number_of_bytes == 2u) {
- region.StoreUnaligned<uint16_t>(offset, value);
+ region_.StoreUnaligned<uint16_t>(offset, value);
} else if (number_of_bytes == 3u) {
- region.StoreUnaligned<uint16_t>(offset, Low16Bits(value));
- region.StoreUnaligned<uint8_t>(offset + sizeof(uint16_t), High16Bits(value));
+ region_.StoreUnaligned<uint16_t>(offset, Low16Bits(value));
+ region_.StoreUnaligned<uint8_t>(offset + sizeof(uint16_t), High16Bits(value));
} else {
- region.StoreUnaligned<uint32_t>(offset, value);
+ region_.StoreUnaligned<uint32_t>(offset, value);
DCHECK_EQ(number_of_bytes, 4u);
}
}
-uint32_t StackMap::GetDexPc(const CodeInfo& info) const {
- return LoadAt(region_, info.NumberOfBytesForDexPc(), info.ComputeStackMapDexPcOffset());
-}
-
-void StackMap::SetDexPc(const CodeInfo& info, uint32_t dex_pc) {
- StoreAt(region_, info.NumberOfBytesForDexPc(), info.ComputeStackMapDexPcOffset(), dex_pc);
-}
-
-uint32_t StackMap::GetNativePcOffset(const CodeInfo& info) const {
- return LoadAt(region_, info.NumberOfBytesForNativePc(), info.ComputeStackMapNativePcOffset());
-}
-
-void StackMap::SetNativePcOffset(const CodeInfo& info, uint32_t native_pc_offset) {
- StoreAt(region_, info.NumberOfBytesForNativePc(), info.ComputeStackMapNativePcOffset(), native_pc_offset);
-}
-
-uint32_t StackMap::GetDexRegisterMapOffset(const CodeInfo& info) const {
- return LoadAt(region_,
- info.NumberOfBytesForDexRegisterMap(),
- info.ComputeStackMapDexRegisterMapOffset(),
- /* check_max */ true);
-}
-
-void StackMap::SetDexRegisterMapOffset(const CodeInfo& info, uint32_t offset) {
- StoreAt(region_,
- info.NumberOfBytesForDexRegisterMap(),
- info.ComputeStackMapDexRegisterMapOffset(),
- offset);
-}
-
-uint32_t StackMap::GetInlineDescriptorOffset(const CodeInfo& info) const {
- if (!info.HasInlineInfo()) return kNoInlineInfo;
- return LoadAt(region_,
- info.NumberOfBytesForInlineInfo(),
- info.ComputeStackMapInlineInfoOffset(),
- /* check_max */ true);
-}
-
-void StackMap::SetInlineDescriptorOffset(const CodeInfo& info, uint32_t offset) {
- DCHECK(info.HasInlineInfo());
- StoreAt(region_,
- info.NumberOfBytesForInlineInfo(),
- info.ComputeStackMapInlineInfoOffset(),
- offset);
-}
-
-uint32_t StackMap::GetRegisterMask(const CodeInfo& info) const {
- return LoadAt(region_,
- info.NumberOfBytesForRegisterMask(),
- info.ComputeStackMapRegisterMaskOffset());
-}
-
-void StackMap::SetRegisterMask(const CodeInfo& info, uint32_t mask) {
- StoreAt(region_,
- info.NumberOfBytesForRegisterMask(),
- info.ComputeStackMapRegisterMaskOffset(),
- mask);
-}
-
-size_t StackMap::ComputeStackMapSizeInternal(size_t stack_mask_size,
- size_t number_of_bytes_for_inline_info,
- size_t number_of_bytes_for_dex_map,
- size_t number_of_bytes_for_dex_pc,
- size_t number_of_bytes_for_native_pc,
- size_t number_of_bytes_for_register_mask) {
- return stack_mask_size
- + number_of_bytes_for_inline_info
- + number_of_bytes_for_dex_map
- + number_of_bytes_for_dex_pc
- + number_of_bytes_for_native_pc
- + number_of_bytes_for_register_mask;
-}
-
-size_t StackMap::ComputeStackMapSize(size_t stack_mask_size,
- size_t inline_info_size,
- size_t dex_register_map_size,
- size_t dex_pc_max,
- size_t native_pc_max,
- size_t register_mask_max) {
- return ComputeStackMapSizeInternal(
- stack_mask_size,
- inline_info_size == 0
- ? 0
- // + 1 to also encode kNoInlineInfo.
- : CodeInfo::EncodingSizeInBytes(inline_info_size + dex_register_map_size + 1),
- // + 1 to also encode kNoDexRegisterMap.
- CodeInfo::EncodingSizeInBytes(dex_register_map_size + 1),
- CodeInfo::EncodingSizeInBytes(dex_pc_max),
- CodeInfo::EncodingSizeInBytes(native_pc_max),
- CodeInfo::EncodingSizeInBytes(register_mask_max));
-}
-
-MemoryRegion StackMap::GetStackMask(const CodeInfo& info) const {
- return region_.Subregion(info.ComputeStackMapStackMaskOffset(), info.GetStackMaskSize());
-}
-
static void DumpRegisterMapping(std::ostream& os,
size_t dex_register_num,
DexRegisterLocation location,
const std::string& prefix = "v",
const std::string& suffix = "") {
- os << " " << prefix << dex_register_num << ": "
- << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind())
- << " (" << location.GetValue() << ")" << suffix << '\n';
+ Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indented_os(&indent_filter);
+ indented_os << prefix << dex_register_num << ": "
+ << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind())
+ << " (" << location.GetValue() << ")" << suffix << '\n';
}
-void CodeInfo::DumpStackMapHeader(std::ostream& os, size_t stack_map_num) const {
- StackMap stack_map = GetStackMapAt(stack_map_num);
- os << " StackMap " << stack_map_num
- << std::hex
- << " (dex_pc=0x" << stack_map.GetDexPc(*this)
- << ", native_pc_offset=0x" << stack_map.GetNativePcOffset(*this)
- << ", dex_register_map_offset=0x" << stack_map.GetDexRegisterMapOffset(*this)
- << ", inline_info_offset=0x" << stack_map.GetInlineDescriptorOffset(*this)
- << ", register_mask=0x" << stack_map.GetRegisterMask(*this)
- << std::dec
- << ", stack_mask=0b";
- MemoryRegion stack_mask = stack_map.GetStackMask(*this);
- for (size_t i = 0, e = stack_mask.size_in_bits(); i < e; ++i) {
- os << stack_mask.LoadBit(e - i - 1);
- }
- os << ")\n";
-};
-
-void CodeInfo::Dump(std::ostream& os, uint16_t number_of_dex_registers) const {
+void CodeInfo::Dump(std::ostream& os,
+ uint32_t code_offset,
+ uint16_t number_of_dex_registers,
+ bool dump_stack_maps) const {
+ StackMapEncoding encoding = ExtractEncoding();
uint32_t code_info_size = GetOverallSize();
size_t number_of_stack_maps = GetNumberOfStackMaps();
- os << " Optimized CodeInfo (size=" << code_info_size
- << ", number_of_dex_registers=" << number_of_dex_registers
- << ", number_of_stack_maps=" << number_of_stack_maps
- << ", has_inline_info=" << HasInlineInfo()
- << ", number_of_bytes_for_inline_info=" << NumberOfBytesForInlineInfo()
- << ", number_of_bytes_for_dex_register_map=" << NumberOfBytesForDexRegisterMap()
- << ", number_of_bytes_for_dex_pc=" << NumberOfBytesForDexPc()
- << ", number_of_bytes_for_native_pc=" << NumberOfBytesForNativePc()
- << ", number_of_bytes_for_register_mask=" << NumberOfBytesForRegisterMask()
- << ")\n";
-
+ Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indented_os(&indent_filter);
+ indented_os << "Optimized CodeInfo (size=" << code_info_size
+ << ", number_of_dex_registers=" << number_of_dex_registers
+ << ", number_of_stack_maps=" << number_of_stack_maps
+ << ", has_inline_info=" << encoding.HasInlineInfo()
+ << ", number_of_bytes_for_inline_info=" << encoding.NumberOfBytesForInlineInfo()
+ << ", number_of_bytes_for_dex_register_map="
+ << encoding.NumberOfBytesForDexRegisterMap()
+ << ", number_of_bytes_for_dex_pc=" << encoding.NumberOfBytesForDexPc()
+ << ", number_of_bytes_for_native_pc=" << encoding.NumberOfBytesForNativePc()
+ << ", number_of_bytes_for_register_mask=" << encoding.NumberOfBytesForRegisterMask()
+ << ")\n";
// Display the Dex register location catalog.
- size_t number_of_location_catalog_entries = GetNumberOfDexRegisterLocationCatalogEntries();
- size_t location_catalog_size_in_bytes = GetDexRegisterLocationCatalogSize();
- os << " DexRegisterLocationCatalog (number_of_entries=" << number_of_location_catalog_entries
- << ", size_in_bytes=" << location_catalog_size_in_bytes << ")\n";
- DexRegisterLocationCatalog dex_register_location_catalog = GetDexRegisterLocationCatalog();
- for (size_t i = 0; i < number_of_location_catalog_entries; ++i) {
- DexRegisterLocation location = dex_register_location_catalog.GetDexRegisterLocation(i);
- DumpRegisterMapping(os, i, location, "entry ");
- }
-
+ GetDexRegisterLocationCatalog(encoding).Dump(indented_os, *this);
// Display stack maps along with (live) Dex register maps.
- for (size_t i = 0; i < number_of_stack_maps; ++i) {
- StackMap stack_map = GetStackMapAt(i);
- 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)) + "]");
- }
- }
+ if (dump_stack_maps) {
+ for (size_t i = 0; i < number_of_stack_maps; ++i) {
+ StackMap stack_map = GetStackMapAt(i, encoding);
+ stack_map.Dump(indented_os,
+ *this,
+ encoding,
+ code_offset,
+ number_of_dex_registers,
+ " " + std::to_string(i));
}
}
- // 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 DexRegisterLocationCatalog::Dump(std::ostream& os, const CodeInfo& code_info) {
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ size_t number_of_location_catalog_entries =
+ code_info.GetNumberOfDexRegisterLocationCatalogEntries();
+ size_t location_catalog_size_in_bytes = code_info.GetDexRegisterLocationCatalogSize(encoding);
+ Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indented_os(&indent_filter);
+ indented_os
+ << "DexRegisterLocationCatalog (number_of_entries=" << number_of_location_catalog_entries
+ << ", size_in_bytes=" << location_catalog_size_in_bytes << ")\n";
+ for (size_t i = 0; i < number_of_location_catalog_entries; ++i) {
+ DexRegisterLocation location = GetDexRegisterLocation(i);
+ DumpRegisterMapping(indented_os, i, location, "entry ");
+ }
+}
+
+void DexRegisterMap::Dump(std::ostream& os,
+ const CodeInfo& code_info,
+ uint16_t number_of_dex_registers) const {
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ size_t number_of_location_catalog_entries =
+ code_info.GetNumberOfDexRegisterLocationCatalogEntries();
+ Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indented_os(&indent_filter);
+ // 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,
+ encoding);
+ DumpRegisterMapping(
+ indented_os, j, location, "v",
+ "\t[entry " + std::to_string(static_cast<int>(location_catalog_entry_index)) + "]");
+ }
+ }
+}
+
+void StackMap::Dump(std::ostream& os,
+ const CodeInfo& code_info,
+ const StackMapEncoding& encoding,
+ uint32_t code_offset,
+ uint16_t number_of_dex_registers,
+ const std::string& header_suffix) const {
+ Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indented_os(&indent_filter);
+ indented_os << "StackMap" << header_suffix
+ << std::hex
+ << " [native_pc=0x" << code_offset + GetNativePcOffset(encoding) << "]"
+ << " (dex_pc=0x" << GetDexPc(encoding)
+ << ", native_pc_offset=0x" << GetNativePcOffset(encoding)
+ << ", dex_register_map_offset=0x" << GetDexRegisterMapOffset(encoding)
+ << ", inline_info_offset=0x" << GetInlineDescriptorOffset(encoding)
+ << ", register_mask=0x" << GetRegisterMask(encoding)
+ << std::dec
+ << ", stack_mask=0b";
+ MemoryRegion stack_mask = GetStackMask(encoding);
+ for (size_t i = 0, e = stack_mask.size_in_bits(); i < e; ++i) {
+ indented_os << stack_mask.LoadBit(e - i - 1);
+ }
+ indented_os << ")\n";
+ if (HasDexRegisterMap(encoding)) {
+ DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(
+ *this, encoding, number_of_dex_registers);
+ dex_register_map.Dump(os, code_info, number_of_dex_registers);
+ }
+}
+
+void InlineInfo::Dump(std::ostream& os,
+ const CodeInfo& code_info,
+ uint16_t number_of_dex_registers[]) const {
+ Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indented_os(&indent_filter);
+ indented_os << "InlineInfo with depth " << static_cast<uint32_t>(GetDepth()) << "\n";
+
+ for (size_t i = 0; i < GetDepth(); ++i) {
+ indented_os << " At depth " << i
+ << std::hex
+ << " (dex_pc=0x" << GetDexPcAtDepth(i)
+ << ", method_index=0x" << GetMethodIndexAtDepth(i)
+ << ")\n";
+ if (HasDexRegisterMapAtDepth(i)) {
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ DexRegisterMap dex_register_map =
+ code_info.GetDexRegisterMapAtDepth(i, *this, encoding, number_of_dex_registers[i]);
+ dex_register_map.Dump(indented_os, code_info, number_of_dex_registers[i]);
+ }
+ }
}
} // namespace art
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 6cc1709..4e42008 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -32,6 +32,7 @@
static constexpr size_t kVRegSize = 4;
class CodeInfo;
+class StackMapEncoding;
/**
* Classes in the following file are wrapper on stack map information backed
@@ -39,47 +40,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:
@@ -397,6 +357,8 @@
return region_.size();
}
+ void Dump(std::ostream& os, const CodeInfo& code_info);
+
// Special (invalid) Dex register location catalog entry index meaning
// that there is no location for a given Dex register (i.e., it is
// mapped to a DexRegisterLocation::Kind::kNone location).
@@ -476,26 +438,30 @@
// Get the surface kind of Dex register `dex_register_number`.
DexRegisterLocation::Kind GetLocationKind(uint16_t dex_register_number,
uint16_t number_of_dex_registers,
- const CodeInfo& code_info) const {
+ const CodeInfo& code_info,
+ const StackMapEncoding& enc) const {
return DexRegisterLocation::ConvertToSurfaceKind(
- GetLocationInternalKind(dex_register_number, number_of_dex_registers, code_info));
+ GetLocationInternalKind(dex_register_number, number_of_dex_registers, code_info, enc));
}
// Get the internal kind of Dex register `dex_register_number`.
DexRegisterLocation::Kind GetLocationInternalKind(uint16_t dex_register_number,
uint16_t number_of_dex_registers,
- const CodeInfo& code_info) const;
+ const CodeInfo& code_info,
+ const StackMapEncoding& enc) const;
// Get the Dex register location `dex_register_number`.
DexRegisterLocation GetDexRegisterLocation(uint16_t dex_register_number,
uint16_t number_of_dex_registers,
- const CodeInfo& code_info) const;
+ const CodeInfo& code_info,
+ const StackMapEncoding& enc) const;
int32_t GetStackOffsetInBytes(uint16_t dex_register_number,
uint16_t number_of_dex_registers,
- const CodeInfo& code_info) const {
+ const CodeInfo& code_info,
+ const StackMapEncoding& enc) const {
DexRegisterLocation location =
- GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info);
+ GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info, enc);
DCHECK(location.GetKind() == DexRegisterLocation::Kind::kInStack);
// GetDexRegisterLocation returns the offset in bytes.
return location.GetValue();
@@ -503,18 +469,21 @@
int32_t GetConstant(uint16_t dex_register_number,
uint16_t number_of_dex_registers,
- const CodeInfo& code_info) const {
+ const CodeInfo& code_info,
+ const StackMapEncoding& enc) const {
DexRegisterLocation location =
- GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info);
- DCHECK(location.GetKind() == DexRegisterLocation::Kind::kConstant);
+ GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info, enc);
+ DCHECK(location.GetKind() == DexRegisterLocation::Kind::kConstant)
+ << DexRegisterLocation::PrettyDescriptor(location.GetKind());
return location.GetValue();
}
int32_t GetMachineRegister(uint16_t dex_register_number,
uint16_t number_of_dex_registers,
- const CodeInfo& code_info) const {
+ const CodeInfo& code_info,
+ const StackMapEncoding& enc) const {
DexRegisterLocation location =
- GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info);
+ GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info, enc);
DCHECK(location.GetInternalKind() == DexRegisterLocation::Kind::kInRegister
|| location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegister)
<< DexRegisterLocation::PrettyDescriptor(location.GetInternalKind());
@@ -641,6 +610,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`.
@@ -664,6 +635,111 @@
friend class StackMapStream;
};
+class StackMapEncoding {
+ public:
+ StackMapEncoding() {}
+
+ StackMapEncoding(size_t stack_mask_size,
+ size_t bytes_for_inline_info,
+ size_t bytes_for_dex_register_map,
+ size_t bytes_for_dex_pc,
+ size_t bytes_for_native_pc,
+ size_t bytes_for_register_mask)
+ : bytes_for_stack_mask_(stack_mask_size),
+ bytes_for_inline_info_(bytes_for_inline_info),
+ bytes_for_dex_register_map_(bytes_for_dex_register_map),
+ bytes_for_dex_pc_(bytes_for_dex_pc),
+ bytes_for_native_pc_(bytes_for_native_pc),
+ bytes_for_register_mask_(bytes_for_register_mask) {}
+
+ static StackMapEncoding CreateFromSizes(size_t stack_mask_size,
+ size_t inline_info_size,
+ size_t dex_register_map_size,
+ size_t dex_pc_max,
+ size_t native_pc_max,
+ size_t register_mask_max) {
+ return StackMapEncoding(
+ stack_mask_size,
+ // + 1 to also encode kNoInlineInfo: if an inline info offset
+ // is at 0xFF, we want to overflow to a larger encoding, because it will
+ // conflict with kNoInlineInfo.
+ // The offset is relative to the dex register map. TODO: Change this.
+ inline_info_size == 0
+ ? 0
+ : EncodingSizeInBytes(dex_register_map_size + inline_info_size + 1),
+ // + 1 to also encode kNoDexRegisterMap: if a dex register map offset
+ // is at 0xFF, we want to overflow to a larger encoding, because it will
+ // conflict with kNoDexRegisterMap.
+ EncodingSizeInBytes(dex_register_map_size + 1),
+ EncodingSizeInBytes(dex_pc_max),
+ EncodingSizeInBytes(native_pc_max),
+ EncodingSizeInBytes(register_mask_max));
+ }
+
+ // Get the size of one stack map of this CodeInfo object, in bytes.
+ // All stack maps of a CodeInfo have the same size.
+ size_t ComputeStackMapSize() const {
+ return bytes_for_register_mask_
+ + bytes_for_stack_mask_
+ + bytes_for_inline_info_
+ + bytes_for_dex_register_map_
+ + bytes_for_dex_pc_
+ + bytes_for_native_pc_;
+ }
+
+ bool HasInlineInfo() const { return bytes_for_inline_info_ > 0; }
+
+ size_t NumberOfBytesForStackMask() const { return bytes_for_stack_mask_; }
+ size_t NumberOfBytesForInlineInfo() const { return bytes_for_inline_info_; }
+ size_t NumberOfBytesForDexRegisterMap() const { return bytes_for_dex_register_map_; }
+ size_t NumberOfBytesForDexPc() const { return bytes_for_dex_pc_; }
+ size_t NumberOfBytesForNativePc() const { return bytes_for_native_pc_; }
+ size_t NumberOfBytesForRegisterMask() const { return bytes_for_register_mask_; }
+
+ size_t ComputeStackMapRegisterMaskOffset() const {
+ return kRegisterMaskOffset;
+ }
+
+ size_t ComputeStackMapStackMaskOffset() const {
+ return ComputeStackMapRegisterMaskOffset() + bytes_for_register_mask_;
+ }
+
+ size_t ComputeStackMapDexPcOffset() const {
+ return ComputeStackMapStackMaskOffset() + bytes_for_stack_mask_;
+ }
+
+ size_t ComputeStackMapNativePcOffset() const {
+ return ComputeStackMapDexPcOffset() + bytes_for_dex_pc_;
+ }
+
+ size_t ComputeStackMapDexRegisterMapOffset() const {
+ return ComputeStackMapNativePcOffset() + bytes_for_native_pc_;
+ }
+
+ size_t ComputeStackMapInlineInfoOffset() const {
+ return ComputeStackMapDexRegisterMapOffset() + bytes_for_dex_register_map_;
+ }
+
+ private:
+ static size_t EncodingSizeInBytes(size_t max_element) {
+ DCHECK(IsUint<32>(max_element));
+ return (max_element == 0) ? 0
+ : IsUint<8>(max_element) ? 1
+ : IsUint<16>(max_element) ? 2
+ : IsUint<24>(max_element) ? 3
+ : 4;
+ }
+
+ static constexpr int kRegisterMaskOffset = 0;
+
+ size_t bytes_for_stack_mask_;
+ size_t bytes_for_inline_info_;
+ size_t bytes_for_dex_register_map_;
+ size_t bytes_for_dex_pc_;
+ size_t bytes_for_native_pc_;
+ size_t bytes_for_register_mask_;
+};
+
/**
* A Stack Map holds compilation information for a specific PC necessary for:
* - Mapping it to a dex PC,
@@ -675,49 +751,85 @@
* 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:
+ StackMap() {}
explicit StackMap(MemoryRegion region) : region_(region) {}
- uint32_t GetDexPc(const CodeInfo& info) const;
+ bool IsValid() const { return region_.pointer() != nullptr; }
- void SetDexPc(const CodeInfo& info, uint32_t dex_pc);
-
- uint32_t GetNativePcOffset(const CodeInfo& info) const;
-
- void SetNativePcOffset(const CodeInfo& info, uint32_t native_pc_offset);
-
- uint32_t GetDexRegisterMapOffset(const CodeInfo& info) const;
-
- void SetDexRegisterMapOffset(const CodeInfo& info, uint32_t offset);
-
- uint32_t GetInlineDescriptorOffset(const CodeInfo& info) const;
-
- void SetInlineDescriptorOffset(const CodeInfo& info, uint32_t offset);
-
- uint32_t GetRegisterMask(const CodeInfo& info) const;
-
- void SetRegisterMask(const CodeInfo& info, uint32_t mask);
-
- MemoryRegion GetStackMask(const CodeInfo& info) const;
-
- void SetStackMask(const CodeInfo& info, const BitVector& sp_map) {
- MemoryRegion region = GetStackMask(info);
- for (size_t i = 0; i < region.size_in_bits(); i++) {
- region.StoreBit(i, sp_map.IsBitSet(i));
- }
+ uint32_t GetDexPc(const StackMapEncoding& encoding) const {
+ return LoadAt(encoding.NumberOfBytesForDexPc(), encoding.ComputeStackMapDexPcOffset());
}
- bool HasDexRegisterMap(const CodeInfo& info) const {
- return GetDexRegisterMapOffset(info) != kNoDexRegisterMap;
+ void SetDexPc(const StackMapEncoding& encoding, uint32_t dex_pc) {
+ StoreAt(encoding.NumberOfBytesForDexPc(), encoding.ComputeStackMapDexPcOffset(), dex_pc);
}
- bool HasInlineInfo(const CodeInfo& info) const {
- return GetInlineDescriptorOffset(info) != kNoInlineInfo;
+ uint32_t GetNativePcOffset(const StackMapEncoding& encoding) const {
+ return LoadAt(encoding.NumberOfBytesForNativePc(), encoding.ComputeStackMapNativePcOffset());
+ }
+
+ void SetNativePcOffset(const StackMapEncoding& encoding, uint32_t native_pc_offset) {
+ StoreAt(encoding.NumberOfBytesForNativePc(),
+ encoding.ComputeStackMapNativePcOffset(),
+ native_pc_offset);
+ }
+
+ uint32_t GetDexRegisterMapOffset(const StackMapEncoding& encoding) const {
+ return LoadAt(encoding.NumberOfBytesForDexRegisterMap(),
+ encoding.ComputeStackMapDexRegisterMapOffset(),
+ /* check_max */ true);
+ }
+
+ void SetDexRegisterMapOffset(const StackMapEncoding& encoding, uint32_t offset) {
+ StoreAt(encoding.NumberOfBytesForDexRegisterMap(),
+ encoding.ComputeStackMapDexRegisterMapOffset(),
+ offset);
+ }
+
+ uint32_t GetInlineDescriptorOffset(const StackMapEncoding& encoding) const {
+ if (!encoding.HasInlineInfo()) return kNoInlineInfo;
+ return LoadAt(encoding.NumberOfBytesForInlineInfo(),
+ encoding.ComputeStackMapInlineInfoOffset(),
+ /* check_max */ true);
+ }
+
+ void SetInlineDescriptorOffset(const StackMapEncoding& encoding, uint32_t offset) {
+ DCHECK(encoding.HasInlineInfo());
+ StoreAt(encoding.NumberOfBytesForInlineInfo(),
+ encoding.ComputeStackMapInlineInfoOffset(),
+ offset);
+ }
+
+ uint32_t GetRegisterMask(const StackMapEncoding& encoding) const {
+ return LoadAt(encoding.NumberOfBytesForRegisterMask(),
+ encoding.ComputeStackMapRegisterMaskOffset());
+ }
+
+ void SetRegisterMask(const StackMapEncoding& encoding, uint32_t mask) {
+ StoreAt(encoding.NumberOfBytesForRegisterMask(),
+ encoding.ComputeStackMapRegisterMaskOffset(),
+ mask);
+ }
+
+ MemoryRegion GetStackMask(const StackMapEncoding& encoding) const {
+ return region_.Subregion(encoding.ComputeStackMapStackMaskOffset(),
+ encoding.NumberOfBytesForStackMask());
+ }
+
+ void SetStackMask(const StackMapEncoding& encoding, const BitVector& sp_map) {
+ MemoryRegion region = GetStackMask(encoding);
+ sp_map.CopyTo(region.start(), region.size());
+ }
+
+ bool HasDexRegisterMap(const StackMapEncoding& encoding) const {
+ return GetDexRegisterMapOffset(encoding) != kNoDexRegisterMap;
+ }
+
+ bool HasInlineInfo(const StackMapEncoding& encoding) const {
+ return GetInlineDescriptorOffset(encoding) != kNoInlineInfo;
}
bool Equals(const StackMap& other) const {
@@ -725,12 +837,12 @@
&& region_.size() == other.region_.size();
}
- static size_t ComputeStackMapSize(size_t stack_mask_size,
- size_t inline_info_size,
- size_t dex_register_map_size,
- size_t dex_pc_max,
- size_t native_pc_max,
- size_t register_mask_max);
+ void Dump(std::ostream& os,
+ const CodeInfo& code_info,
+ const StackMapEncoding& encoding,
+ uint32_t code_offset,
+ uint16_t number_of_dex_registers,
+ const std::string& header_suffix = "") const;
// Special (invalid) offset for the DexRegisterMapOffset field meaning
// that there is no Dex register map for this stack map.
@@ -741,25 +853,105 @@
static constexpr uint32_t kNoInlineInfo = -1;
private:
- static size_t ComputeStackMapSizeInternal(size_t stack_mask_size,
- size_t number_of_bytes_for_inline_info,
- size_t number_of_bytes_for_dex_map,
- size_t number_of_bytes_for_dex_pc,
- size_t number_of_bytes_for_native_pc,
- size_t number_of_bytes_for_register_mask);
-
// TODO: Instead of plain types such as "uint32_t", introduce
// typedefs (and document the memory layout of StackMap).
- static constexpr int kRegisterMaskOffset = 0;
static constexpr int kFixedSize = 0;
+ // Loads `number_of_bytes` at the given `offset` and assemble a uint32_t. If `check_max` is true,
+ // this method converts a maximum value of size `number_of_bytes` into a uint32_t 0xFFFFFFFF.
+ uint32_t LoadAt(size_t number_of_bytes, size_t offset, bool check_max = false) const;
+ void StoreAt(size_t number_of_bytes, size_t offset, uint32_t value) const;
+
+ MemoryRegion region_;
+
+ 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.
* The information is of the form:
@@ -775,39 +967,23 @@
region_ = MemoryRegion(const_cast<void*>(data), size);
}
- static size_t EncodingSizeInBytes(size_t max_element) {
- DCHECK(IsUint<32>(max_element));
- return (max_element == 0) ? 0
- : IsUint<8>(max_element) ? 1
- : IsUint<16>(max_element) ? 2
- : IsUint<24>(max_element) ? 3
- : 4;
+ StackMapEncoding ExtractEncoding() const {
+ return StackMapEncoding(region_.LoadUnaligned<uint32_t>(kStackMaskSizeOffset),
+ GetNumberOfBytesForEncoding(kInlineInfoBitOffset),
+ GetNumberOfBytesForEncoding(kDexRegisterMapBitOffset),
+ GetNumberOfBytesForEncoding(kDexPcBitOffset),
+ GetNumberOfBytesForEncoding(kNativePcBitOffset),
+ GetNumberOfBytesForEncoding(kRegisterMaskBitOffset));
}
- void SetEncoding(size_t inline_info_size,
- size_t dex_register_map_size,
- size_t dex_pc_max,
- size_t native_pc_max,
- size_t register_mask_max) {
- if (inline_info_size != 0) {
- region_.StoreBit(kHasInlineInfoBitOffset, 1);
- // + 1 to also encode kNoInlineInfo: if an inline info offset
- // is at 0xFF, we want to overflow to a larger encoding, because it will
- // conflict with kNoInlineInfo.
- // The offset is relative to the dex register map. TODO: Change this.
- SetEncodingAt(kInlineInfoBitOffset,
- EncodingSizeInBytes(dex_register_map_size + inline_info_size + 1));
- } else {
- region_.StoreBit(kHasInlineInfoBitOffset, 0);
- SetEncodingAt(kInlineInfoBitOffset, 0);
- }
- // + 1 to also encode kNoDexRegisterMap: if a dex register map offset
- // is at 0xFF, we want to overflow to a larger encoding, because it will
- // conflict with kNoDexRegisterMap.
- SetEncodingAt(kDexRegisterMapBitOffset, EncodingSizeInBytes(dex_register_map_size + 1));
- SetEncodingAt(kDexPcBitOffset, EncodingSizeInBytes(dex_pc_max));
- SetEncodingAt(kNativePcBitOffset, EncodingSizeInBytes(native_pc_max));
- SetEncodingAt(kRegisterMaskBitOffset, EncodingSizeInBytes(register_mask_max));
+ void SetEncoding(const StackMapEncoding& encoding) {
+ region_.StoreUnaligned<uint32_t>(kStackMaskSizeOffset, encoding.NumberOfBytesForStackMask());
+ region_.StoreBit(kHasInlineInfoBitOffset, encoding.NumberOfBytesForInlineInfo() != 0);
+ SetEncodingAt(kInlineInfoBitOffset, encoding.NumberOfBytesForInlineInfo());
+ SetEncodingAt(kDexRegisterMapBitOffset, encoding.NumberOfBytesForDexRegisterMap());
+ SetEncodingAt(kDexPcBitOffset, encoding.NumberOfBytesForDexPc());
+ SetEncodingAt(kNativePcBitOffset, encoding.NumberOfBytesForNativePc());
+ SetEncodingAt(kRegisterMaskBitOffset, encoding.NumberOfBytesForRegisterMask());
}
void SetEncodingAt(size_t bit_offset, size_t number_of_bytes) {
@@ -828,68 +1004,15 @@
return region_.LoadBit(kHasInlineInfoBitOffset);
}
- size_t NumberOfBytesForInlineInfo() const {
- return GetNumberOfBytesForEncoding(kInlineInfoBitOffset);
- }
-
- size_t NumberOfBytesForDexRegisterMap() const {
- return GetNumberOfBytesForEncoding(kDexRegisterMapBitOffset);
- }
-
- size_t NumberOfBytesForRegisterMask() const {
- return GetNumberOfBytesForEncoding(kRegisterMaskBitOffset);
- }
-
- size_t NumberOfBytesForNativePc() const {
- return GetNumberOfBytesForEncoding(kNativePcBitOffset);
- }
-
- size_t NumberOfBytesForDexPc() const {
- return GetNumberOfBytesForEncoding(kDexPcBitOffset);
- }
-
- size_t ComputeStackMapRegisterMaskOffset() const {
- return StackMap::kRegisterMaskOffset;
- }
-
- size_t ComputeStackMapStackMaskOffset() const {
- return ComputeStackMapRegisterMaskOffset()
- + (NumberOfBytesForRegisterMask() * sizeof(uint8_t));
- }
-
- size_t ComputeStackMapDexPcOffset() const {
- return ComputeStackMapStackMaskOffset() + GetStackMaskSize();
- }
-
- size_t ComputeStackMapNativePcOffset() const {
- return ComputeStackMapDexPcOffset()
- + (NumberOfBytesForDexPc() * sizeof(uint8_t));
- }
-
- size_t ComputeStackMapDexRegisterMapOffset() const {
- return ComputeStackMapNativePcOffset()
- + (NumberOfBytesForNativePc() * sizeof(uint8_t));
- }
-
- size_t ComputeStackMapInlineInfoOffset() const {
- CHECK(HasInlineInfo());
- return ComputeStackMapDexRegisterMapOffset()
- + (NumberOfBytesForDexRegisterMap() * sizeof(uint8_t));
- }
-
- uint32_t GetDexRegisterLocationCatalogOffset() const {
- return kFixedSize;
- }
-
- DexRegisterLocationCatalog GetDexRegisterLocationCatalog() const {
+ DexRegisterLocationCatalog GetDexRegisterLocationCatalog(const StackMapEncoding& encoding) const {
return DexRegisterLocationCatalog(region_.Subregion(
- GetDexRegisterLocationCatalogOffset(),
- GetDexRegisterLocationCatalogSize()));
+ GetDexRegisterLocationCatalogOffset(encoding),
+ GetDexRegisterLocationCatalogSize(encoding)));
}
- StackMap GetStackMapAt(size_t i) const {
- size_t size = StackMapSize();
- return StackMap(GetStackMaps().Subregion(i * size, size));
+ StackMap GetStackMapAt(size_t i, const StackMapEncoding& encoding) const {
+ size_t stack_map_size = encoding.ComputeStackMapSize();
+ return StackMap(GetStackMaps(encoding).Subregion(i * stack_map_size, stack_map_size));
}
uint32_t GetOverallSize() const {
@@ -908,19 +1031,11 @@
region_.StoreUnaligned<uint32_t>(kNumberOfDexRegisterLocationCatalogEntriesOffset, num_entries);
}
- uint32_t GetDexRegisterLocationCatalogSize() const {
- return ComputeDexRegisterLocationCatalogSize(GetDexRegisterLocationCatalogOffset(),
+ uint32_t GetDexRegisterLocationCatalogSize(const StackMapEncoding& encoding) const {
+ return ComputeDexRegisterLocationCatalogSize(GetDexRegisterLocationCatalogOffset(encoding),
GetNumberOfDexRegisterLocationCatalogEntries());
}
- uint32_t GetStackMaskSize() const {
- return region_.LoadUnaligned<uint32_t>(kStackMaskSizeOffset);
- }
-
- void SetStackMaskSize(uint32_t size) {
- region_.StoreUnaligned<uint32_t>(kStackMaskSizeOffset, size);
- }
-
size_t GetNumberOfStackMaps() const {
return region_.LoadUnaligned<uint32_t>(kNumberOfStackMapsOffset);
}
@@ -929,70 +1044,86 @@
region_.StoreUnaligned<uint32_t>(kNumberOfStackMapsOffset, number_of_stack_maps);
}
- // Get the size of one stack map of this CodeInfo object, in bytes.
- // All stack maps of a CodeInfo have the same size.
- size_t StackMapSize() const {
- return StackMap::ComputeStackMapSizeInternal(GetStackMaskSize(),
- NumberOfBytesForInlineInfo(),
- NumberOfBytesForDexRegisterMap(),
- NumberOfBytesForDexPc(),
- NumberOfBytesForNativePc(),
- NumberOfBytesForRegisterMask());
- }
-
// Get the size all the stack maps of this CodeInfo object, in bytes.
- size_t GetStackMapsSize() const {
- return StackMapSize() * GetNumberOfStackMaps();
+ size_t GetStackMapsSize(const StackMapEncoding& encoding) const {
+ return encoding.ComputeStackMapSize() * GetNumberOfStackMaps();
}
- size_t GetDexRegisterMapsOffset() const {
- return GetStackMapsOffset() + GetStackMapsSize();
+ uint32_t GetDexRegisterLocationCatalogOffset(const StackMapEncoding& encoding) const {
+ return GetStackMapsOffset() + GetStackMapsSize(encoding);
+ }
+
+ size_t GetDexRegisterMapsOffset(const StackMapEncoding& encoding) const {
+ return GetDexRegisterLocationCatalogOffset(encoding)
+ + GetDexRegisterLocationCatalogSize(encoding);
}
uint32_t GetStackMapsOffset() const {
- return GetDexRegisterLocationCatalogOffset() + GetDexRegisterLocationCatalogSize();
+ return kFixedSize;
}
- DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, uint32_t number_of_dex_registers) const {
- DCHECK(stack_map.HasDexRegisterMap(*this));
- uint32_t offset = GetDexRegisterMapsOffset() + stack_map.GetDexRegisterMapOffset(*this);
+ DexRegisterMap GetDexRegisterMapOf(StackMap stack_map,
+ const StackMapEncoding& encoding,
+ uint32_t number_of_dex_registers) const {
+ DCHECK(stack_map.HasDexRegisterMap(encoding));
+ uint32_t offset = GetDexRegisterMapsOffset(encoding)
+ + stack_map.GetDexRegisterMapOffset(encoding);
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();
+ // Return the `DexRegisterMap` pointed by `inline_info` at depth `depth`.
+ DexRegisterMap GetDexRegisterMapAtDepth(uint8_t depth,
+ InlineInfo inline_info,
+ const StackMapEncoding& encoding,
+ uint32_t number_of_dex_registers) const {
+ DCHECK(inline_info.HasDexRegisterMapAtDepth(depth));
+ uint32_t offset = GetDexRegisterMapsOffset(encoding)
+ + 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 StackMapEncoding& encoding) const {
+ DCHECK(stack_map.HasInlineInfo(encoding));
+ uint32_t offset = stack_map.GetInlineDescriptorOffset(encoding)
+ + GetDexRegisterMapsOffset(encoding);
uint8_t depth = region_.LoadUnaligned<uint8_t>(offset);
return InlineInfo(region_.Subregion(offset,
InlineInfo::kFixedSize + depth * InlineInfo::SingleEntrySize()));
}
- StackMap GetStackMapForDexPc(uint32_t dex_pc) const {
+ StackMap GetStackMapForDexPc(uint32_t dex_pc, const StackMapEncoding& encoding) const {
for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
- StackMap stack_map = GetStackMapAt(i);
- if (stack_map.GetDexPc(*this) == dex_pc) {
+ StackMap stack_map = GetStackMapAt(i, encoding);
+ if (stack_map.GetDexPc(encoding) == dex_pc) {
return stack_map;
}
}
- LOG(FATAL) << "Unreachable";
- UNREACHABLE();
+ return StackMap();
}
- StackMap GetStackMapForNativePcOffset(uint32_t native_pc_offset) const {
+ StackMap GetStackMapForNativePcOffset(uint32_t native_pc_offset,
+ const StackMapEncoding& encoding) const {
// TODO: stack maps are sorted by native pc, we can do a binary search.
for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
- StackMap stack_map = GetStackMapAt(i);
- if (stack_map.GetNativePcOffset(*this) == native_pc_offset) {
+ StackMap stack_map = GetStackMapAt(i, encoding);
+ if (stack_map.GetNativePcOffset(encoding) == native_pc_offset) {
return stack_map;
}
}
- LOG(FATAL) << "Unreachable";
- UNREACHABLE();
+ return StackMap();
}
- void Dump(std::ostream& os, uint16_t number_of_dex_registers) const;
- void DumpStackMapHeader(std::ostream& os, size_t stack_map_num) const;
+ // Dump this CodeInfo object on `os`. `code_offset` is the (absolute)
+ // native PC of the compiled method and `number_of_dex_registers` the
+ // number of Dex virtual registers used in this method. If
+ // `dump_stack_maps` is true, also dump the stack maps and the
+ // associated Dex register maps.
+ void Dump(std::ostream& os,
+ uint32_t code_offset,
+ uint16_t number_of_dex_registers,
+ bool dump_stack_maps) const;
private:
// TODO: Instead of plain types such as "uint32_t", introduce
@@ -1013,10 +1144,10 @@
static constexpr int kNativePcBitOffset = kDexPcBitOffset + 3;
static constexpr int kRegisterMaskBitOffset = kNativePcBitOffset + 3;
- MemoryRegion GetStackMaps() const {
+ MemoryRegion GetStackMaps(const StackMapEncoding& encoding) const {
return region_.size() == 0
? MemoryRegion()
- : region_.Subregion(GetStackMapsOffset(), GetStackMapsSize());
+ : region_.Subregion(GetStackMapsOffset(), GetStackMapsSize(encoding));
}
// Compute the size of the Dex register map associated to the stack map at
diff --git a/runtime/stride_iterator.h b/runtime/stride_iterator.h
index 5971524..a680302 100644
--- a/runtime/stride_iterator.h
+++ b/runtime/stride_iterator.h
@@ -62,7 +62,8 @@
private:
uintptr_t ptr_;
- const size_t stride_;
+ // Not const for operator=.
+ size_t stride_;
};
} // namespace art
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 845345a..fe98b0a 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -147,29 +147,82 @@
ResetQuickAllocEntryPoints(&tlsPtr_.quick_entrypoints);
}
-void Thread::SetDeoptimizationShadowFrame(ShadowFrame* sf) {
- tlsPtr_.deoptimization_shadow_frame = sf;
+class DeoptimizationReturnValueRecord {
+ public:
+ DeoptimizationReturnValueRecord(const JValue& ret_val,
+ bool is_reference,
+ DeoptimizationReturnValueRecord* link)
+ : ret_val_(ret_val), is_reference_(is_reference), link_(link) {}
+
+ JValue GetReturnValue() const { return ret_val_; }
+ bool IsReference() const { return is_reference_; }
+ DeoptimizationReturnValueRecord* GetLink() const { return link_; }
+ mirror::Object** GetGCRoot() {
+ DCHECK(is_reference_);
+ return ret_val_.GetGCRoot();
+ }
+
+ private:
+ JValue ret_val_;
+ const bool is_reference_;
+ DeoptimizationReturnValueRecord* const link_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeoptimizationReturnValueRecord);
+};
+
+class StackedShadowFrameRecord {
+ public:
+ StackedShadowFrameRecord(ShadowFrame* shadow_frame,
+ StackedShadowFrameType type,
+ StackedShadowFrameRecord* link)
+ : shadow_frame_(shadow_frame),
+ type_(type),
+ link_(link) {}
+
+ ShadowFrame* GetShadowFrame() const { return shadow_frame_; }
+ StackedShadowFrameType GetType() const { return type_; }
+ StackedShadowFrameRecord* GetLink() const { return link_; }
+
+ private:
+ ShadowFrame* const shadow_frame_;
+ const StackedShadowFrameType type_;
+ StackedShadowFrameRecord* const link_;
+
+ DISALLOW_COPY_AND_ASSIGN(StackedShadowFrameRecord);
+};
+
+void Thread::PushAndClearDeoptimizationReturnValue() {
+ DeoptimizationReturnValueRecord* record = new DeoptimizationReturnValueRecord(
+ tls64_.deoptimization_return_value,
+ tls32_.deoptimization_return_value_is_reference,
+ tlsPtr_.deoptimization_return_value_stack);
+ tlsPtr_.deoptimization_return_value_stack = record;
+ ClearDeoptimizationReturnValue();
}
-void Thread::SetDeoptimizationReturnValue(const JValue& ret_val) {
- tls64_.deoptimization_return_value.SetJ(ret_val.GetJ());
+JValue Thread::PopDeoptimizationReturnValue() {
+ DeoptimizationReturnValueRecord* record = tlsPtr_.deoptimization_return_value_stack;
+ DCHECK(record != nullptr);
+ tlsPtr_.deoptimization_return_value_stack = record->GetLink();
+ JValue ret_val(record->GetReturnValue());
+ delete record;
+ return ret_val;
}
-ShadowFrame* Thread::GetAndClearDeoptimizationShadowFrame(JValue* ret_val) {
- ShadowFrame* sf = tlsPtr_.deoptimization_shadow_frame;
- tlsPtr_.deoptimization_shadow_frame = nullptr;
- ret_val->SetJ(tls64_.deoptimization_return_value.GetJ());
- return sf;
+void Thread::PushStackedShadowFrame(ShadowFrame* sf, StackedShadowFrameType type) {
+ StackedShadowFrameRecord* record = new StackedShadowFrameRecord(
+ sf, type, tlsPtr_.stacked_shadow_frame_record);
+ tlsPtr_.stacked_shadow_frame_record = record;
}
-void Thread::SetShadowFrameUnderConstruction(ShadowFrame* sf) {
- sf->SetLink(tlsPtr_.shadow_frame_under_construction);
- tlsPtr_.shadow_frame_under_construction = sf;
-}
-
-void Thread::ClearShadowFrameUnderConstruction() {
- CHECK_NE(static_cast<ShadowFrame*>(nullptr), tlsPtr_.shadow_frame_under_construction);
- tlsPtr_.shadow_frame_under_construction = tlsPtr_.shadow_frame_under_construction->GetLink();
+ShadowFrame* Thread::PopStackedShadowFrame(StackedShadowFrameType type) {
+ StackedShadowFrameRecord* record = tlsPtr_.stacked_shadow_frame_record;
+ DCHECK(record != nullptr);
+ DCHECK_EQ(record->GetType(), type);
+ tlsPtr_.stacked_shadow_frame_record = record->GetLink();
+ ShadowFrame* shadow_frame = record->GetShadowFrame();
+ delete record;
+ return shadow_frame;
}
void Thread::InitTid() {
@@ -2273,8 +2326,10 @@
const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m, sizeof(void*));
uintptr_t native_pc_offset = m->NativeQuickPcOffset(GetCurrentQuickFramePc(), entry_point);
CodeInfo code_info = m->GetOptimizedCodeInfo();
- StackMap map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
- MemoryRegion mask = map.GetStackMask(code_info);
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ StackMap map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
+ DCHECK(map.IsValid());
+ MemoryRegion mask = map.GetStackMask(encoding);
// Visit stack entries that hold pointers.
for (size_t i = 0; i < mask.size_in_bits(); ++i) {
if (mask.LoadBit(i)) {
@@ -2290,7 +2345,7 @@
}
}
// Visit callee-save registers that hold pointers.
- uint32_t register_mask = map.GetRegisterMask(code_info);
+ uint32_t register_mask = map.GetRegisterMask(encoding);
for (size_t i = 0; i < BitSizeOf<uint32_t>(); ++i) {
if (register_mask & (1 << i)) {
mirror::Object** ref_addr = reinterpret_cast<mirror::Object**>(GetGPRAddress(i));
@@ -2385,21 +2440,27 @@
if (tlsPtr_.debug_invoke_req != nullptr) {
tlsPtr_.debug_invoke_req->VisitRoots(visitor, RootInfo(kRootDebugger, thread_id));
}
- if (tlsPtr_.deoptimization_shadow_frame != nullptr) {
+ if (tlsPtr_.stacked_shadow_frame_record != nullptr) {
RootCallbackVisitor visitor_to_callback(visitor, thread_id);
ReferenceMapVisitor<RootCallbackVisitor> mapper(this, nullptr, visitor_to_callback);
- for (ShadowFrame* shadow_frame = tlsPtr_.deoptimization_shadow_frame; shadow_frame != nullptr;
- shadow_frame = shadow_frame->GetLink()) {
- mapper.VisitShadowFrame(shadow_frame);
+ for (StackedShadowFrameRecord* record = tlsPtr_.stacked_shadow_frame_record;
+ record != nullptr;
+ record = record->GetLink()) {
+ for (ShadowFrame* shadow_frame = record->GetShadowFrame();
+ shadow_frame != nullptr;
+ shadow_frame = shadow_frame->GetLink()) {
+ mapper.VisitShadowFrame(shadow_frame);
+ }
}
}
- if (tlsPtr_.shadow_frame_under_construction != nullptr) {
- RootCallbackVisitor visitor_to_callback(visitor, thread_id);
- ReferenceMapVisitor<RootCallbackVisitor> mapper(this, nullptr, visitor_to_callback);
- for (ShadowFrame* shadow_frame = tlsPtr_.shadow_frame_under_construction;
- shadow_frame != nullptr;
- shadow_frame = shadow_frame->GetLink()) {
- mapper.VisitShadowFrame(shadow_frame);
+ if (tlsPtr_.deoptimization_return_value_stack != nullptr) {
+ for (DeoptimizationReturnValueRecord* record = tlsPtr_.deoptimization_return_value_stack;
+ record != nullptr;
+ record = record->GetLink()) {
+ if (record->IsReference()) {
+ visitor->VisitRootIfNonNull(record->GetGCRoot(),
+ RootInfo(kRootThreadObject, thread_id));
+ }
}
}
for (auto* verifier = tlsPtr_.method_verifier; verifier != nullptr; verifier = verifier->link_) {
diff --git a/runtime/thread.h b/runtime/thread.h
index 3f0d0a5..9311bef 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -74,6 +74,7 @@
class Closure;
class Context;
struct DebugInvokeReq;
+class DeoptimizationReturnValueRecord;
class DexFile;
class JavaVMExt;
struct JNIEnvExt;
@@ -82,6 +83,7 @@
class ScopedObjectAccessAlreadyRunnable;
class ShadowFrame;
class SingleStepControl;
+class StackedShadowFrameRecord;
class Thread;
class ThreadList;
@@ -99,6 +101,11 @@
kCheckpointRequest = 2 // Request that the thread do some checkpoint work and then continue.
};
+enum class StackedShadowFrameType {
+ kShadowFrameUnderConstruction,
+ kDeoptimizationShadowFrame
+};
+
static constexpr size_t kNumRosAllocThreadLocalSizeBrackets = 34;
// Thread's stack layout for implicit stack overflow checks:
@@ -790,21 +797,25 @@
return reinterpret_cast<mirror::Throwable*>(-1);
}
- void SetDeoptimizationShadowFrame(ShadowFrame* sf);
- void SetDeoptimizationReturnValue(const JValue& ret_val);
-
- ShadowFrame* GetAndClearDeoptimizationShadowFrame(JValue* ret_val);
-
- bool HasDeoptimizationShadowFrame() const {
- return tlsPtr_.deoptimization_shadow_frame != nullptr;
+ // Currently deoptimization invokes verifier which can trigger class loading
+ // and execute Java code, so there might be nested deoptimizations happening.
+ // We need to save the ongoing deoptimization shadow frames and return
+ // values on stacks.
+ void SetDeoptimizationReturnValue(const JValue& ret_val, bool is_reference) {
+ tls64_.deoptimization_return_value.SetJ(ret_val.GetJ());
+ tls32_.deoptimization_return_value_is_reference = is_reference;
}
-
- void SetShadowFrameUnderConstruction(ShadowFrame* sf);
- void ClearShadowFrameUnderConstruction();
-
- bool HasShadowFrameUnderConstruction() const {
- return tlsPtr_.shadow_frame_under_construction != nullptr;
+ bool IsDeoptimizationReturnValueReference() {
+ return tls32_.deoptimization_return_value_is_reference;
}
+ void ClearDeoptimizationReturnValue() {
+ tls64_.deoptimization_return_value.SetJ(0);
+ tls32_.deoptimization_return_value_is_reference = false;
+ }
+ void PushAndClearDeoptimizationReturnValue();
+ JValue PopDeoptimizationReturnValue();
+ void PushStackedShadowFrame(ShadowFrame* sf, StackedShadowFrameType type);
+ ShadowFrame* PopStackedShadowFrame(StackedShadowFrameType type);
std::deque<instrumentation::InstrumentationStackFrame>* GetInstrumentationStack() {
return tlsPtr_.instrumentation_stack;
@@ -1048,7 +1059,8 @@
explicit tls_32bit_sized_values(bool is_daemon) :
suspend_count(0), debug_suspend_count(0), thin_lock_thread_id(0), tid(0),
daemon(is_daemon), throwing_OutOfMemoryError(false), no_thread_suspension(0),
- thread_exit_check_count(0), handling_signal_(false), suspended_at_suspend_check(false),
+ thread_exit_check_count(0), handling_signal_(false),
+ deoptimization_return_value_is_reference(false), suspended_at_suspend_check(false),
ready_for_debug_invoke(false), debug_method_entry_(false) {
}
@@ -1089,6 +1101,10 @@
// True if signal is being handled by this thread.
bool32_t handling_signal_;
+ // True if the return value for interpreter after deoptimization is a reference.
+ // For gc purpose.
+ bool32_t deoptimization_return_value_is_reference;
+
// True if the thread is suspended in FullSuspendCheck(). This is
// used to distinguish runnable threads that are suspended due to
// a normal suspend check from other threads.
@@ -1124,8 +1140,9 @@
stack_trace_sample(nullptr), wait_next(nullptr), monitor_enter_object(nullptr),
top_handle_scope(nullptr), class_loader_override(nullptr), long_jump_context(nullptr),
instrumentation_stack(nullptr), debug_invoke_req(nullptr), single_step_control(nullptr),
- deoptimization_shadow_frame(nullptr), shadow_frame_under_construction(nullptr), name(nullptr),
- pthread_self(0), last_no_thread_suspension_cause(nullptr), thread_local_start(nullptr),
+ stacked_shadow_frame_record(nullptr), deoptimization_return_value_stack(nullptr),
+ name(nullptr), pthread_self(0),
+ last_no_thread_suspension_cause(nullptr), thread_local_start(nullptr),
thread_local_pos(nullptr), thread_local_end(nullptr), thread_local_objects(0),
thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr),
nested_signal_state(nullptr), flip_function(nullptr), method_verifier(nullptr) {
@@ -1201,11 +1218,13 @@
// JDWP single-stepping support.
SingleStepControl* single_step_control;
- // Shadow frame stack that is used temporarily during the deoptimization of a method.
- ShadowFrame* deoptimization_shadow_frame;
+ // For gc purpose, a shadow frame record stack that keeps track of:
+ // 1) shadow frames under construction.
+ // 2) deoptimization shadow frames.
+ StackedShadowFrameRecord* stacked_shadow_frame_record;
- // Shadow frame stack that is currently under construction but not yet on the stack
- ShadowFrame* shadow_frame_under_construction;
+ // Deoptimization return value record stack.
+ DeoptimizationReturnValueRecord* deoptimization_return_value_stack;
// A cached copy of the java.lang.Thread's name.
std::string* name;
@@ -1293,7 +1312,25 @@
const char* const old_cause_;
};
+class ScopedStackedShadowFramePusher {
+ public:
+ ScopedStackedShadowFramePusher(Thread* self, ShadowFrame* sf, StackedShadowFrameType type)
+ : self_(self), type_(type) {
+ self_->PushStackedShadowFrame(sf, type);
+ }
+ ~ScopedStackedShadowFramePusher() {
+ self_->PopStackedShadowFrame(type_);
+ }
+
+ private:
+ Thread* const self_;
+ const StackedShadowFrameType type_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedStackedShadowFramePusher);
+};
+
std::ostream& operator<<(std::ostream& os, const Thread& thread);
+std::ostream& operator<<(std::ostream& os, const StackedShadowFrameType& thread);
} // namespace art
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index aa54b17..b86a7ee 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -2388,10 +2388,7 @@
case Instruction::INVOKE_DIRECT:
case Instruction::INVOKE_DIRECT_RANGE: {
bool is_range = (inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE);
- ArtMethod* called_method = VerifyInvocationArgs(inst,
- METHOD_DIRECT,
- is_range,
- false);
+ ArtMethod* called_method = VerifyInvocationArgs(inst, METHOD_DIRECT, is_range, false);
const char* return_type_descriptor;
bool is_constructor;
const RegType* return_type = nullptr;
@@ -2471,10 +2468,7 @@
case Instruction::INVOKE_STATIC:
case Instruction::INVOKE_STATIC_RANGE: {
bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE);
- ArtMethod* called_method = VerifyInvocationArgs(inst,
- METHOD_STATIC,
- is_range,
- false);
+ ArtMethod* called_method = VerifyInvocationArgs(inst, METHOD_STATIC, is_range, false);
const char* descriptor;
if (called_method == nullptr) {
uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
@@ -2496,10 +2490,7 @@
case Instruction::INVOKE_INTERFACE:
case Instruction::INVOKE_INTERFACE_RANGE: {
bool is_range = (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE);
- ArtMethod* abs_method = VerifyInvocationArgs(inst,
- METHOD_INTERFACE,
- is_range,
- false);
+ ArtMethod* abs_method = VerifyInvocationArgs(inst, METHOD_INTERFACE, is_range, false);
if (abs_method != nullptr) {
mirror::Class* called_interface = abs_method->GetDeclaringClass();
if (!called_interface->IsInterface() && !called_interface->IsObjectClass()) {
@@ -3327,7 +3318,10 @@
}
if (method_type != METHOD_INTERFACE && !actual_arg_type.IsZero()) {
const RegType* res_method_class;
- if (res_method != nullptr) {
+ // Miranda methods have the declaring interface as their declaring class, not the abstract
+ // class. It would be wrong to use this for the type check (interface type checks are
+ // postponed to runtime).
+ if (res_method != nullptr && !res_method->IsMiranda()) {
mirror::Class* klass = res_method->GetDeclaringClass();
std::string temp;
res_method_class = ®_types_.FromClass(klass->GetDescriptor(&temp), klass,
@@ -3378,11 +3372,27 @@
<< " but expected " << reg_type;
return nullptr;
}
- } else if (!work_line_->VerifyRegisterType(this, get_reg, reg_type)) {
- // Continue on soft failures. We need to find possible hard failures to avoid problems in the
- // compiler.
- if (have_pending_hard_failure_) {
- return nullptr;
+ } else {
+ if (!work_line_->VerifyRegisterType(this, get_reg, reg_type)) {
+ // Continue on soft failures. We need to find possible hard failures to avoid problems in
+ // the compiler.
+ if (have_pending_hard_failure_) {
+ return nullptr;
+ }
+ } else if (reg_type.IsLongOrDoubleTypes()) {
+ // Check that registers are consecutive (for non-range invokes). Invokes are the only
+ // instructions not specifying register pairs by the first component, but require them
+ // nonetheless. Only check when there's an actual register in the parameters. If there's
+ // none, this will fail below.
+ if (!is_range && sig_registers + 1 < expected_args) {
+ uint32_t second_reg = arg[sig_registers + 1];
+ if (second_reg != get_reg + 1) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation, long or double parameter "
+ "at index " << sig_registers << " is not a pair: " << get_reg << " + "
+ << second_reg << ".";
+ return nullptr;
+ }
+ }
}
}
sig_registers += reg_type.IsLongOrDoubleTypes() ? 2 : 1;
diff --git a/sigchainlib/Android.mk b/sigchainlib/Android.mk
index e1aae11..11f44fe 100644
--- a/sigchainlib/Android.mk
+++ b/sigchainlib/Android.mk
@@ -22,6 +22,7 @@
LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
LOCAL_MODULE_TAGS := optional
LOCAL_CFLAGS += $(ART_TARGET_CFLAGS)
+LOCAL_ASFLAGS += $(ART_TARGET_ASFLAGS)
LOCAL_SRC_FILES := sigchain_dummy.cc
LOCAL_CLANG = $(ART_TARGET_CLANG)
LOCAL_MODULE:= libsigchain
@@ -36,6 +37,7 @@
LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
LOCAL_MODULE_TAGS := optional
LOCAL_CFLAGS += $(ART_TARGET_CFLAGS)
+LOCAL_ASFLAGS += $(ART_TARGET_ASFLAGS)
LOCAL_SRC_FILES := sigchain.cc
LOCAL_CLANG = $(ART_TARGET_CLANG)
LOCAL_MODULE:= libsigchain
@@ -51,6 +53,7 @@
LOCAL_MODULE_TAGS := optional
LOCAL_IS_HOST_MODULE := true
LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
+LOCAL_ASFLAGS += $(ART_HOST_ASFLAGS)
LOCAL_CLANG = $(ART_HOST_CLANG)
LOCAL_SRC_FILES := sigchain_dummy.cc
LOCAL_MODULE:= libsigchain
@@ -65,6 +68,7 @@
LOCAL_MODULE_TAGS := optional
LOCAL_IS_HOST_MODULE := true
LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
+LOCAL_ASFLAGS += $(ART_HOST_ASFLAGS)
LOCAL_CLANG = $(ART_HOST_CLANG)
LOCAL_SRC_FILES := sigchain.cc
LOCAL_MODULE:= libsigchain
diff --git a/test/004-StackWalk/src/Main.java b/test/004-StackWalk/src/Main.java
index 1e2a91b..9a1d0ab 100644
--- a/test/004-StackWalk/src/Main.java
+++ b/test/004-StackWalk/src/Main.java
@@ -2,9 +2,14 @@
public Main() {
}
- int f() {
+ int $noinline$f() throws Exception {
g(1);
g(2);
+
+ // This loop currently defeats inlining of `f`.
+ for (int i = 0; i < 10; i++) {
+ Thread.sleep(0);
+ }
return 0;
}
@@ -86,8 +91,8 @@
System.loadLibrary("arttest");
}
- public static void main(String[] args) {
+ public static void main(String[] args) throws Exception {
Main st = new Main();
- st.f();
+ st.$noinline$f();
}
}
diff --git a/test/004-StackWalk/stack_walk_jni.cc b/test/004-StackWalk/stack_walk_jni.cc
index f66b715..6b15514 100644
--- a/test/004-StackWalk/stack_walk_jni.cc
+++ b/test/004-StackWalk/stack_walk_jni.cc
@@ -45,11 +45,11 @@
if (m_name == "f") {
if (gJava_StackWalk_refmap_calls == 1) {
CHECK_EQ(1U, GetDexPc());
- CHECK_REGS(1);
+ CHECK_REGS(4);
} else {
CHECK_EQ(gJava_StackWalk_refmap_calls, 2);
CHECK_EQ(5U, GetDexPc());
- CHECK_REGS(1);
+ CHECK_REGS(4);
}
} else if (m_name == "g") {
if (gJava_StackWalk_refmap_calls == 1) {
diff --git a/test/067-preemptive-unpark/src/Main.java b/test/067-preemptive-unpark/src/Main.java
index 2c099b9..beb3262 100644
--- a/test/067-preemptive-unpark/src/Main.java
+++ b/test/067-preemptive-unpark/src/Main.java
@@ -40,22 +40,24 @@
/**
* Set up {@link #UNSAFE}.
*/
- public static void setUp() {
+ public static void setUp() throws Exception{
/*
* Subvert the access check to get the unique Unsafe instance.
* We can do this because there's no security manager
* installed when running the test.
*/
+ Field field = null;
try {
- Field field = Unsafe.class.getDeclaredField("THE_ONE");
- field.setAccessible(true);
-
- UNSAFE = (Unsafe) field.get(null);
- } catch (NoSuchFieldException ex) {
- throw new RuntimeException(ex);
- } catch (IllegalAccessException ex) {
- throw new RuntimeException(ex);
+ field = Unsafe.class.getDeclaredField("THE_ONE");
+ } catch (NoSuchFieldException e1) {
+ try {
+ field = Unsafe.class.getDeclaredField("theUnsafe");
+ } catch (NoSuchFieldException e2) {
+ throw new RuntimeException("Failed to find THE_ONE or theUnsafe");
+ }
}
+ field.setAccessible(true);
+ UNSAFE = (Unsafe) field.get(null);
}
/**
diff --git a/test/098-ddmc/src/Main.java b/test/098-ddmc/src/Main.java
index f41ff2a..f74a17e 100644
--- a/test/098-ddmc/src/Main.java
+++ b/test/098-ddmc/src/Main.java
@@ -43,8 +43,10 @@
System.out.println("Confirm when we overflow, we don't roll over to zero. b/17392248");
final int overflowAllocations = 64 * 1024; // Won't fit in unsigned 16-bit value.
+ // Keep the new objects live so they are not garbage collected.
+ Object[] objects = new Object[overflowAllocations];
for (int i = 0; i < overflowAllocations; i++) {
- new Object();
+ objects[i] = new Object();
}
Allocations after = new Allocations(DdmVmInternal.getRecentAllocations());
System.out.println("before < overflowAllocations=" + (before.numberOfEntries < overflowAllocations));
@@ -67,7 +69,7 @@
DdmVmInternal.enableRecentAllocations(true);
System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
for (int i = 0; i < 16 * 1024; i++) {
- new String("fnord");
+ objects[i] = new String("fnord");
}
Allocations first = new Allocations(DdmVmInternal.getRecentAllocations());
DdmVmInternal.enableRecentAllocations(true);
diff --git a/test/133-static-invoke-super/src/Main.java b/test/133-static-invoke-super/src/Main.java
index 7cfd099..e694998 100644
--- a/test/133-static-invoke-super/src/Main.java
+++ b/test/133-static-invoke-super/src/Main.java
@@ -26,14 +26,14 @@
run(timing);
}
- static int testBasis(int interations) {
- (new SubClass()).testDirect(interations);
- return interations;
+ static int testBasis(int iterations) {
+ (new SubClass()).testDirect(iterations);
+ return iterations;
}
- static int testStatic(int interations) {
- (new SubClass()).testStatic(interations);
- return interations;
+ static int testStatic(int iterations) {
+ (new SubClass()).testStatic(iterations);
+ return iterations;
}
static public void run(boolean timing) {
diff --git a/test/135-MirandaDispatch/expected.txt b/test/135-MirandaDispatch/expected.txt
index 134d8d0..5b098e5 100644
--- a/test/135-MirandaDispatch/expected.txt
+++ b/test/135-MirandaDispatch/expected.txt
@@ -1 +1,2 @@
+b/21646347
Finishing
diff --git a/test/135-MirandaDispatch/smali/b_21646347.smali b/test/135-MirandaDispatch/smali/b_21646347.smali
new file mode 100644
index 0000000..b4979a5
--- /dev/null
+++ b/test/135-MirandaDispatch/smali/b_21646347.smali
@@ -0,0 +1,15 @@
+.class public LB21646347;
+
+# If an invoke-virtual dispatches to a miranda method, ensure that we test for the receiver
+# being a subclass of the abstract class, not postpone the check because the miranda method's
+# declaring class is an interface.
+
+.super Ljava/lang/Object;
+
+.method public static run(LB21646347;)V
+ .registers 1
+ # Invoke the miranda method on an object of this class. This should fail type-checking,
+ # instead of letting this pass as the declaring class is an interface.
+ invoke-virtual {v0}, LMain$AbstractClass;->m()V
+ return-void
+.end method
diff --git a/test/135-MirandaDispatch/src/Main.java b/test/135-MirandaDispatch/src/Main.java
index bb005b0..ada8cef 100644
--- a/test/135-MirandaDispatch/src/Main.java
+++ b/test/135-MirandaDispatch/src/Main.java
@@ -46,6 +46,15 @@
if (counter != loopIterations * loopIterations) {
System.out.println("Expected " + loopIterations * loopIterations + " got " + counter);
}
+
+ try {
+ Class<?> b21646347 = Class.forName("B21646347");
+ throw new RuntimeException("Expected a VerifyError");
+ } catch (VerifyError expected) {
+ System.out.println("b/21646347");
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
System.out.println("Finishing");
}
}
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
new file mode 100644
index 0000000..83f7711
--- /dev/null
+++ b/test/137-cfi/cfi.cc
@@ -0,0 +1,221 @@
+/*
+ * 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;
+ }
+ }
+ }
+ }
+
+ printf("Can not find %s in backtrace:\n", seq[cur_search_index].c_str());
+ for (Backtrace::const_iterator it = bt->begin(); it != bt->end(); ++it) {
+ if (BacktraceMap::IsValid(it->map)) {
+ printf(" %s\n", it->func_name.c_str());
+ }
+ }
+
+ 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)) {
+ printf("Can not unwind in process.\n");
+ return JNI_FALSE;
+ } else if (bt->NumFrames() == 0) {
+ printf("No frames for unwind in process.\n");
+ 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.
+ "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)", // Framework method.
+ "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.
+ printf("Failed to attach to other process.\n");
+ 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)) {
+ printf("Can not unwind other process.\n");
+ result = false;
+ } else if (bt->NumFrames() == 0) {
+ printf("No frames for unwind of other process.\n");
+ 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.
+ "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)", // Framework method.
+ "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..658ba53
--- /dev/null
+++ b/test/137-cfi/src/Main.java
@@ -0,0 +1,170 @@
+/*
+ * 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;
+import java.util.Arrays;
+import java.util.Comparator;
+
+public class Main implements Comparator<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;
+
+ private boolean passed;
+
+ 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();
+ throw new RuntimeException("Didn't expect to get back...");
+ }
+
+ private void runPrimary() {
+ // First do the in-process unwinding.
+ if (TEST_LOCAL_UNWINDING && !foo()) {
+ 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() {
+ // Call bar via Arrays.binarySearch.
+ // This tests that we can unwind from framework code.
+ Main[] array = { this, this, this };
+ Arrays.binarySearch(array, 0, 3, this /* value */, this /* comparator */);
+ return passed;
+ }
+
+ public int compare(Main lhs, Main rhs) {
+ passed = bar(secondary);
+ // Returning "equal" ensures that we terminate search
+ // after first item and thus call bar() only once.
+ return 0;
+ }
+
+ 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..9f8f417 100644
--- a/test/422-type-conversion/src/Main.java
+++ b/test/422-type-conversion/src/Main.java
@@ -138,552 +138,554 @@
}
private static void byteToLong() {
- assertLongEquals(1L, $opt$ByteToLong((byte)1));
- assertLongEquals(0L, $opt$ByteToLong((byte)0));
- assertLongEquals(-1L, $opt$ByteToLong((byte)-1));
- assertLongEquals(51L, $opt$ByteToLong((byte)51));
- assertLongEquals(-51L, $opt$ByteToLong((byte)-51));
- assertLongEquals(127L, $opt$ByteToLong((byte)127)); // 2^7 - 1
- assertLongEquals(-127L, $opt$ByteToLong((byte)-127)); // -(2^7 - 1)
- assertLongEquals(-128L, $opt$ByteToLong((byte)-128)); // -(2^7)
+ assertLongEquals(1L, $opt$noinline$ByteToLong((byte)1));
+ assertLongEquals(0L, $opt$noinline$ByteToLong((byte)0));
+ assertLongEquals(-1L, $opt$noinline$ByteToLong((byte)-1));
+ assertLongEquals(51L, $opt$noinline$ByteToLong((byte)51));
+ assertLongEquals(-51L, $opt$noinline$ByteToLong((byte)-51));
+ assertLongEquals(127L, $opt$noinline$ByteToLong((byte)127)); // 2^7 - 1
+ assertLongEquals(-127L, $opt$noinline$ByteToLong((byte)-127)); // -(2^7 - 1)
+ assertLongEquals(-128L, $opt$noinline$ByteToLong((byte)-128)); // -(2^7)
}
private static void shortToLong() {
- assertLongEquals(1L, $opt$ShortToLong((short)1));
- assertLongEquals(0L, $opt$ShortToLong((short)0));
- assertLongEquals(-1L, $opt$ShortToLong((short)-1));
- assertLongEquals(51L, $opt$ShortToLong((short)51));
- assertLongEquals(-51L, $opt$ShortToLong((short)-51));
- assertLongEquals(32767L, $opt$ShortToLong((short)32767)); // 2^15 - 1
- assertLongEquals(-32767L, $opt$ShortToLong((short)-32767)); // -(2^15 - 1)
- assertLongEquals(-32768L, $opt$ShortToLong((short)-32768)); // -(2^15)
+ assertLongEquals(1L, $opt$noinline$ShortToLong((short)1));
+ assertLongEquals(0L, $opt$noinline$ShortToLong((short)0));
+ assertLongEquals(-1L, $opt$noinline$ShortToLong((short)-1));
+ assertLongEquals(51L, $opt$noinline$ShortToLong((short)51));
+ assertLongEquals(-51L, $opt$noinline$ShortToLong((short)-51));
+ assertLongEquals(32767L, $opt$noinline$ShortToLong((short)32767)); // 2^15 - 1
+ assertLongEquals(-32767L, $opt$noinline$ShortToLong((short)-32767)); // -(2^15 - 1)
+ assertLongEquals(-32768L, $opt$noinline$ShortToLong((short)-32768)); // -(2^15)
}
private static void intToLong() {
- assertLongEquals(1L, $opt$IntToLong(1));
- assertLongEquals(0L, $opt$IntToLong(0));
- assertLongEquals(-1L, $opt$IntToLong(-1));
- assertLongEquals(51L, $opt$IntToLong(51));
- assertLongEquals(-51L, $opt$IntToLong(-51));
- assertLongEquals(2147483647L, $opt$IntToLong(2147483647)); // 2^31 - 1
- assertLongEquals(-2147483647L, $opt$IntToLong(-2147483647)); // -(2^31 - 1)
- assertLongEquals(-2147483648L, $opt$IntToLong(-2147483648)); // -(2^31)
+ assertLongEquals(1L, $opt$noinline$IntToLong(1));
+ assertLongEquals(0L, $opt$noinline$IntToLong(0));
+ assertLongEquals(-1L, $opt$noinline$IntToLong(-1));
+ assertLongEquals(51L, $opt$noinline$IntToLong(51));
+ assertLongEquals(-51L, $opt$noinline$IntToLong(-51));
+ assertLongEquals(2147483647L, $opt$noinline$IntToLong(2147483647)); // 2^31 - 1
+ assertLongEquals(-2147483647L, $opt$noinline$IntToLong(-2147483647)); // -(2^31 - 1)
+ assertLongEquals(-2147483648L, $opt$noinline$IntToLong(-2147483648)); // -(2^31)
}
private static void charToLong() {
- assertLongEquals(1L, $opt$CharToLong((char)1));
- assertLongEquals(0L, $opt$CharToLong((char)0));
- assertLongEquals(51L, $opt$CharToLong((char)51));
- assertLongEquals(32767L, $opt$CharToLong((char)32767)); // 2^15 - 1
- assertLongEquals(65535L, $opt$CharToLong((char)65535)); // 2^16 - 1
- assertLongEquals(65535L, $opt$CharToLong((char)-1));
- assertLongEquals(65485L, $opt$CharToLong((char)-51));
- assertLongEquals(32769L, $opt$CharToLong((char)-32767)); // -(2^15 - 1)
- assertLongEquals(32768L, $opt$CharToLong((char)-32768)); // -(2^15)
+ assertLongEquals(1L, $opt$noinline$CharToLong((char)1));
+ assertLongEquals(0L, $opt$noinline$CharToLong((char)0));
+ assertLongEquals(51L, $opt$noinline$CharToLong((char)51));
+ assertLongEquals(32767L, $opt$noinline$CharToLong((char)32767)); // 2^15 - 1
+ assertLongEquals(65535L, $opt$noinline$CharToLong((char)65535)); // 2^16 - 1
+ assertLongEquals(65535L, $opt$noinline$CharToLong((char)-1));
+ assertLongEquals(65485L, $opt$noinline$CharToLong((char)-51));
+ assertLongEquals(32769L, $opt$noinline$CharToLong((char)-32767)); // -(2^15 - 1)
+ assertLongEquals(32768L, $opt$noinline$CharToLong((char)-32768)); // -(2^15)
}
private static void byteToFloat() {
- assertFloatEquals(1F, $opt$ByteToFloat((byte)1));
- assertFloatEquals(0F, $opt$ByteToFloat((byte)0));
- assertFloatEquals(-1F, $opt$ByteToFloat((byte)-1));
- assertFloatEquals(51F, $opt$ByteToFloat((byte)51));
- assertFloatEquals(-51F, $opt$ByteToFloat((byte)-51));
- assertFloatEquals(127F, $opt$ByteToFloat((byte)127)); // 2^7 - 1
- assertFloatEquals(-127F, $opt$ByteToFloat((byte)-127)); // -(2^7 - 1)
- assertFloatEquals(-128F, $opt$ByteToFloat((byte)-128)); // -(2^7)
+ assertFloatEquals(1F, $opt$noinline$ByteToFloat((byte)1));
+ assertFloatEquals(0F, $opt$noinline$ByteToFloat((byte)0));
+ assertFloatEquals(-1F, $opt$noinline$ByteToFloat((byte)-1));
+ assertFloatEquals(51F, $opt$noinline$ByteToFloat((byte)51));
+ assertFloatEquals(-51F, $opt$noinline$ByteToFloat((byte)-51));
+ assertFloatEquals(127F, $opt$noinline$ByteToFloat((byte)127)); // 2^7 - 1
+ assertFloatEquals(-127F, $opt$noinline$ByteToFloat((byte)-127)); // -(2^7 - 1)
+ assertFloatEquals(-128F, $opt$noinline$ByteToFloat((byte)-128)); // -(2^7)
}
private static void shortToFloat() {
- assertFloatEquals(1F, $opt$ShortToFloat((short)1));
- assertFloatEquals(0F, $opt$ShortToFloat((short)0));
- assertFloatEquals(-1F, $opt$ShortToFloat((short)-1));
- assertFloatEquals(51F, $opt$ShortToFloat((short)51));
- assertFloatEquals(-51F, $opt$ShortToFloat((short)-51));
- assertFloatEquals(32767F, $opt$ShortToFloat((short)32767)); // 2^15 - 1
- assertFloatEquals(-32767F, $opt$ShortToFloat((short)-32767)); // -(2^15 - 1)
- assertFloatEquals(-32768F, $opt$ShortToFloat((short)-32768)); // -(2^15)
+ assertFloatEquals(1F, $opt$noinline$ShortToFloat((short)1));
+ assertFloatEquals(0F, $opt$noinline$ShortToFloat((short)0));
+ assertFloatEquals(-1F, $opt$noinline$ShortToFloat((short)-1));
+ assertFloatEquals(51F, $opt$noinline$ShortToFloat((short)51));
+ assertFloatEquals(-51F, $opt$noinline$ShortToFloat((short)-51));
+ assertFloatEquals(32767F, $opt$noinline$ShortToFloat((short)32767)); // 2^15 - 1
+ assertFloatEquals(-32767F, $opt$noinline$ShortToFloat((short)-32767)); // -(2^15 - 1)
+ assertFloatEquals(-32768F, $opt$noinline$ShortToFloat((short)-32768)); // -(2^15)
}
private static void intToFloat() {
- assertFloatEquals(1F, $opt$IntToFloat(1));
- assertFloatEquals(0F, $opt$IntToFloat(0));
- assertFloatEquals(-1F, $opt$IntToFloat(-1));
- assertFloatEquals(51F, $opt$IntToFloat(51));
- assertFloatEquals(-51F, $opt$IntToFloat(-51));
- assertFloatEquals(16777215F, $opt$IntToFloat(16777215)); // 2^24 - 1
- assertFloatEquals(-16777215F, $opt$IntToFloat(-16777215)); // -(2^24 - 1)
- assertFloatEquals(16777216F, $opt$IntToFloat(16777216)); // 2^24
- assertFloatEquals(-16777216F, $opt$IntToFloat(-16777216)); // -(2^24)
- assertFloatEquals(2147483647F, $opt$IntToFloat(2147483647)); // 2^31 - 1
- assertFloatEquals(-2147483648F, $opt$IntToFloat(-2147483648)); // -(2^31)
+ assertFloatEquals(1F, $opt$noinline$IntToFloat(1));
+ assertFloatEquals(0F, $opt$noinline$IntToFloat(0));
+ assertFloatEquals(-1F, $opt$noinline$IntToFloat(-1));
+ assertFloatEquals(51F, $opt$noinline$IntToFloat(51));
+ assertFloatEquals(-51F, $opt$noinline$IntToFloat(-51));
+ assertFloatEquals(16777215F, $opt$noinline$IntToFloat(16777215)); // 2^24 - 1
+ assertFloatEquals(-16777215F, $opt$noinline$IntToFloat(-16777215)); // -(2^24 - 1)
+ assertFloatEquals(16777216F, $opt$noinline$IntToFloat(16777216)); // 2^24
+ assertFloatEquals(-16777216F, $opt$noinline$IntToFloat(-16777216)); // -(2^24)
+ assertFloatEquals(2147483647F, $opt$noinline$IntToFloat(2147483647)); // 2^31 - 1
+ assertFloatEquals(-2147483648F, $opt$noinline$IntToFloat(-2147483648)); // -(2^31)
}
private static void charToFloat() {
- assertFloatEquals(1F, $opt$CharToFloat((char)1));
- assertFloatEquals(0F, $opt$CharToFloat((char)0));
- assertFloatEquals(51F, $opt$CharToFloat((char)51));
- assertFloatEquals(32767F, $opt$CharToFloat((char)32767)); // 2^15 - 1
- assertFloatEquals(65535F, $opt$CharToFloat((char)65535)); // 2^16 - 1
- assertFloatEquals(65535F, $opt$CharToFloat((char)-1));
- assertFloatEquals(65485F, $opt$CharToFloat((char)-51));
- assertFloatEquals(32769F, $opt$CharToFloat((char)-32767)); // -(2^15 - 1)
- assertFloatEquals(32768F, $opt$CharToFloat((char)-32768)); // -(2^15)
+ assertFloatEquals(1F, $opt$noinline$CharToFloat((char)1));
+ assertFloatEquals(0F, $opt$noinline$CharToFloat((char)0));
+ assertFloatEquals(51F, $opt$noinline$CharToFloat((char)51));
+ assertFloatEquals(32767F, $opt$noinline$CharToFloat((char)32767)); // 2^15 - 1
+ assertFloatEquals(65535F, $opt$noinline$CharToFloat((char)65535)); // 2^16 - 1
+ assertFloatEquals(65535F, $opt$noinline$CharToFloat((char)-1));
+ assertFloatEquals(65485F, $opt$noinline$CharToFloat((char)-51));
+ assertFloatEquals(32769F, $opt$noinline$CharToFloat((char)-32767)); // -(2^15 - 1)
+ assertFloatEquals(32768F, $opt$noinline$CharToFloat((char)-32768)); // -(2^15)
}
private static void byteToDouble() {
- assertDoubleEquals(1D, $opt$ByteToDouble((byte)1));
- assertDoubleEquals(0D, $opt$ByteToDouble((byte)0));
- assertDoubleEquals(-1D, $opt$ByteToDouble((byte)-1));
- assertDoubleEquals(51D, $opt$ByteToDouble((byte)51));
- assertDoubleEquals(-51D, $opt$ByteToDouble((byte)-51));
- assertDoubleEquals(127D, $opt$ByteToDouble((byte)127)); // 2^7 - 1
- assertDoubleEquals(-127D, $opt$ByteToDouble((byte)-127)); // -(2^7 - 1)
- assertDoubleEquals(-128D, $opt$ByteToDouble((byte)-128)); // -(2^7)
+ assertDoubleEquals(1D, $opt$noinline$ByteToDouble((byte)1));
+ assertDoubleEquals(0D, $opt$noinline$ByteToDouble((byte)0));
+ assertDoubleEquals(-1D, $opt$noinline$ByteToDouble((byte)-1));
+ assertDoubleEquals(51D, $opt$noinline$ByteToDouble((byte)51));
+ assertDoubleEquals(-51D, $opt$noinline$ByteToDouble((byte)-51));
+ assertDoubleEquals(127D, $opt$noinline$ByteToDouble((byte)127)); // 2^7 - 1
+ assertDoubleEquals(-127D, $opt$noinline$ByteToDouble((byte)-127)); // -(2^7 - 1)
+ assertDoubleEquals(-128D, $opt$noinline$ByteToDouble((byte)-128)); // -(2^7)
}
private static void shortToDouble() {
- assertDoubleEquals(1D, $opt$ShortToDouble((short)1));
- assertDoubleEquals(0D, $opt$ShortToDouble((short)0));
- assertDoubleEquals(-1D, $opt$ShortToDouble((short)-1));
- assertDoubleEquals(51D, $opt$ShortToDouble((short)51));
- assertDoubleEquals(-51D, $opt$ShortToDouble((short)-51));
- assertDoubleEquals(32767D, $opt$ShortToDouble((short)32767)); // 2^15 - 1
- assertDoubleEquals(-32767D, $opt$ShortToDouble((short)-32767)); // -(2^15 - 1)
- assertDoubleEquals(-32768D, $opt$ShortToDouble((short)-32768)); // -(2^15)
+ assertDoubleEquals(1D, $opt$noinline$ShortToDouble((short)1));
+ assertDoubleEquals(0D, $opt$noinline$ShortToDouble((short)0));
+ assertDoubleEquals(-1D, $opt$noinline$ShortToDouble((short)-1));
+ assertDoubleEquals(51D, $opt$noinline$ShortToDouble((short)51));
+ assertDoubleEquals(-51D, $opt$noinline$ShortToDouble((short)-51));
+ assertDoubleEquals(32767D, $opt$noinline$ShortToDouble((short)32767)); // 2^15 - 1
+ assertDoubleEquals(-32767D, $opt$noinline$ShortToDouble((short)-32767)); // -(2^15 - 1)
+ assertDoubleEquals(-32768D, $opt$noinline$ShortToDouble((short)-32768)); // -(2^15)
}
private static void intToDouble() {
- assertDoubleEquals(1D, $opt$IntToDouble(1));
- assertDoubleEquals(0D, $opt$IntToDouble(0));
- assertDoubleEquals(-1D, $opt$IntToDouble(-1));
- assertDoubleEquals(51D, $opt$IntToDouble(51));
- assertDoubleEquals(-51D, $opt$IntToDouble(-51));
- assertDoubleEquals(16777216D, $opt$IntToDouble(16777216)); // 2^24
- assertDoubleEquals(-16777216D, $opt$IntToDouble(-16777216)); // -(2^24)
- assertDoubleEquals(2147483647D, $opt$IntToDouble(2147483647)); // 2^31 - 1
- assertDoubleEquals(-2147483648D, $opt$IntToDouble(-2147483648)); // -(2^31)
+ assertDoubleEquals(1D, $opt$noinline$IntToDouble(1));
+ assertDoubleEquals(0D, $opt$noinline$IntToDouble(0));
+ assertDoubleEquals(-1D, $opt$noinline$IntToDouble(-1));
+ assertDoubleEquals(51D, $opt$noinline$IntToDouble(51));
+ assertDoubleEquals(-51D, $opt$noinline$IntToDouble(-51));
+ assertDoubleEquals(16777216D, $opt$noinline$IntToDouble(16777216)); // 2^24
+ assertDoubleEquals(-16777216D, $opt$noinline$IntToDouble(-16777216)); // -(2^24)
+ assertDoubleEquals(2147483647D, $opt$noinline$IntToDouble(2147483647)); // 2^31 - 1
+ assertDoubleEquals(-2147483648D, $opt$noinline$IntToDouble(-2147483648)); // -(2^31)
}
private static void charToDouble() {
- assertDoubleEquals(1D, $opt$CharToDouble((char)1));
- assertDoubleEquals(0D, $opt$CharToDouble((char)0));
- assertDoubleEquals(51D, $opt$CharToDouble((char)51));
- assertDoubleEquals(32767D, $opt$CharToDouble((char)32767)); // 2^15 - 1
- assertDoubleEquals(65535D, $opt$CharToDouble((char)65535)); // 2^16 - 1
- assertDoubleEquals(65535D, $opt$CharToDouble((char)-1));
- assertDoubleEquals(65485D, $opt$CharToDouble((char)-51));
- assertDoubleEquals(32769D, $opt$CharToDouble((char)-32767)); // -(2^15 - 1)
- assertDoubleEquals(32768D, $opt$CharToDouble((char)-32768)); // -(2^15)
+ assertDoubleEquals(1D, $opt$noinline$CharToDouble((char)1));
+ assertDoubleEquals(0D, $opt$noinline$CharToDouble((char)0));
+ assertDoubleEquals(51D, $opt$noinline$CharToDouble((char)51));
+ assertDoubleEquals(32767D, $opt$noinline$CharToDouble((char)32767)); // 2^15 - 1
+ assertDoubleEquals(65535D, $opt$noinline$CharToDouble((char)65535)); // 2^16 - 1
+ assertDoubleEquals(65535D, $opt$noinline$CharToDouble((char)-1));
+ assertDoubleEquals(65485D, $opt$noinline$CharToDouble((char)-51));
+ assertDoubleEquals(32769D, $opt$noinline$CharToDouble((char)-32767)); // -(2^15 - 1)
+ assertDoubleEquals(32768D, $opt$noinline$CharToDouble((char)-32768)); // -(2^15)
}
private static void longToInt() {
- assertIntEquals(1, $opt$LongToInt(1L));
- assertIntEquals(0, $opt$LongToInt(0L));
- assertIntEquals(-1, $opt$LongToInt(-1L));
- assertIntEquals(51, $opt$LongToInt(51L));
- assertIntEquals(-51, $opt$LongToInt(-51L));
- assertIntEquals(2147483647, $opt$LongToInt(2147483647L)); // 2^31 - 1
- assertIntEquals(-2147483647, $opt$LongToInt(-2147483647L)); // -(2^31 - 1)
- assertIntEquals(-2147483648, $opt$LongToInt(-2147483648L)); // -(2^31)
- assertIntEquals(-2147483648, $opt$LongToInt(2147483648L)); // (2^31)
- assertIntEquals(2147483647, $opt$LongToInt(-2147483649L)); // -(2^31 + 1)
- assertIntEquals(-1, $opt$LongToInt(9223372036854775807L)); // 2^63 - 1
- assertIntEquals(1, $opt$LongToInt(-9223372036854775807L)); // -(2^63 - 1)
- assertIntEquals(0, $opt$LongToInt(-9223372036854775808L)); // -(2^63)
+ assertIntEquals(1, $opt$noinline$LongToInt(1L));
+ assertIntEquals(0, $opt$noinline$LongToInt(0L));
+ assertIntEquals(-1, $opt$noinline$LongToInt(-1L));
+ assertIntEquals(51, $opt$noinline$LongToInt(51L));
+ assertIntEquals(-51, $opt$noinline$LongToInt(-51L));
+ assertIntEquals(2147483647, $opt$noinline$LongToInt(2147483647L)); // 2^31 - 1
+ assertIntEquals(-2147483647, $opt$noinline$LongToInt(-2147483647L)); // -(2^31 - 1)
+ assertIntEquals(-2147483648, $opt$noinline$LongToInt(-2147483648L)); // -(2^31)
+ assertIntEquals(-2147483648, $opt$noinline$LongToInt(2147483648L)); // (2^31)
+ assertIntEquals(2147483647, $opt$noinline$LongToInt(-2147483649L)); // -(2^31 + 1)
+ assertIntEquals(-1, $opt$noinline$LongToInt(9223372036854775807L)); // 2^63 - 1
+ assertIntEquals(1, $opt$noinline$LongToInt(-9223372036854775807L)); // -(2^63 - 1)
+ assertIntEquals(0, $opt$noinline$LongToInt(-9223372036854775808L)); // -(2^63)
- assertIntEquals(42, $opt$LongLiteralToInt());
+ assertIntEquals(42, $opt$noinline$LongLiteralToInt());
// Ensure long-to-int conversions truncates values as expected.
- assertLongEquals(1L, $opt$IntToLong($opt$LongToInt(4294967297L))); // 2^32 + 1
- assertLongEquals(0L, $opt$IntToLong($opt$LongToInt(4294967296L))); // 2^32
- assertLongEquals(-1L, $opt$IntToLong($opt$LongToInt(4294967295L))); // 2^32 - 1
- assertLongEquals(0L, $opt$IntToLong($opt$LongToInt(0L)));
- assertLongEquals(1L, $opt$IntToLong($opt$LongToInt(-4294967295L))); // -(2^32 - 1)
- assertLongEquals(0L, $opt$IntToLong($opt$LongToInt(-4294967296L))); // -(2^32)
- assertLongEquals(-1, $opt$IntToLong($opt$LongToInt(-4294967297L))); // -(2^32 + 1)
+ assertLongEquals(1L, $opt$noinline$IntToLong($opt$noinline$LongToInt(4294967297L))); // 2^32 + 1
+ assertLongEquals(0L, $opt$noinline$IntToLong($opt$noinline$LongToInt(4294967296L))); // 2^32
+ assertLongEquals(-1L, $opt$noinline$IntToLong($opt$noinline$LongToInt(4294967295L))); // 2^32 - 1
+ assertLongEquals(0L, $opt$noinline$IntToLong($opt$noinline$LongToInt(0L)));
+ assertLongEquals(1L, $opt$noinline$IntToLong($opt$noinline$LongToInt(-4294967295L))); // -(2^32 - 1)
+ assertLongEquals(0L, $opt$noinline$IntToLong($opt$noinline$LongToInt(-4294967296L))); // -(2^32)
+ assertLongEquals(-1, $opt$noinline$IntToLong($opt$noinline$LongToInt(-4294967297L))); // -(2^32 + 1)
}
private static void longToFloat() {
- assertFloatEquals(1F, $opt$LongToFloat(1L));
- assertFloatEquals(0F, $opt$LongToFloat(0L));
- assertFloatEquals(-1F, $opt$LongToFloat(-1L));
- assertFloatEquals(51F, $opt$LongToFloat(51L));
- assertFloatEquals(-51F, $opt$LongToFloat(-51L));
- assertFloatEquals(2147483647F, $opt$LongToFloat(2147483647L)); // 2^31 - 1
- assertFloatEquals(-2147483647F, $opt$LongToFloat(-2147483647L)); // -(2^31 - 1)
- assertFloatEquals(-2147483648F, $opt$LongToFloat(-2147483648L)); // -(2^31)
- assertFloatEquals(2147483648F, $opt$LongToFloat(2147483648L)); // (2^31)
- assertFloatEquals(-2147483649F, $opt$LongToFloat(-2147483649L)); // -(2^31 + 1)
- assertFloatEquals(4294967296F, $opt$LongToFloat(4294967296L)); // (2^32)
- assertFloatEquals(-4294967296F, $opt$LongToFloat(-4294967296L)); // -(2^32)
- assertFloatEquals(140739635871745F, $opt$LongToFloat(140739635871745L)); // 1 + 2^15 + 2^31 + 2^47
- assertFloatEquals(-140739635871745F, $opt$LongToFloat(-140739635871745L)); // -(1 + 2^15 + 2^31 + 2^47)
- assertFloatEquals(9223372036854775807F, $opt$LongToFloat(9223372036854775807L)); // 2^63 - 1
- assertFloatEquals(-9223372036854775807F, $opt$LongToFloat(-9223372036854775807L)); // -(2^63 - 1)
- assertFloatEquals(-9223372036854775808F, $opt$LongToFloat(-9223372036854775808L)); // -(2^63)
+ assertFloatEquals(1F, $opt$noinline$LongToFloat(1L));
+ assertFloatEquals(0F, $opt$noinline$LongToFloat(0L));
+ assertFloatEquals(-1F, $opt$noinline$LongToFloat(-1L));
+ assertFloatEquals(51F, $opt$noinline$LongToFloat(51L));
+ assertFloatEquals(-51F, $opt$noinline$LongToFloat(-51L));
+ assertFloatEquals(2147483647F, $opt$noinline$LongToFloat(2147483647L)); // 2^31 - 1
+ assertFloatEquals(-2147483647F, $opt$noinline$LongToFloat(-2147483647L)); // -(2^31 - 1)
+ assertFloatEquals(-2147483648F, $opt$noinline$LongToFloat(-2147483648L)); // -(2^31)
+ assertFloatEquals(2147483648F, $opt$noinline$LongToFloat(2147483648L)); // (2^31)
+ assertFloatEquals(-2147483649F, $opt$noinline$LongToFloat(-2147483649L)); // -(2^31 + 1)
+ assertFloatEquals(4294967296F, $opt$noinline$LongToFloat(4294967296L)); // (2^32)
+ assertFloatEquals(-4294967296F, $opt$noinline$LongToFloat(-4294967296L)); // -(2^32)
+ assertFloatEquals(140739635871745F, $opt$noinline$LongToFloat(140739635871745L)); // 1 + 2^15 + 2^31 + 2^47
+ assertFloatEquals(-140739635871745F, $opt$noinline$LongToFloat(-140739635871745L)); // -(1 + 2^15 + 2^31 + 2^47)
+ assertFloatEquals(9223372036854775807F, $opt$noinline$LongToFloat(9223372036854775807L)); // 2^63 - 1
+ assertFloatEquals(-9223372036854775807F, $opt$noinline$LongToFloat(-9223372036854775807L)); // -(2^63 - 1)
+ assertFloatEquals(-9223372036854775808F, $opt$noinline$LongToFloat(-9223372036854775808L)); // -(2^63)
}
private static void longToDouble() {
- assertDoubleEquals(1D, $opt$LongToDouble(1L));
- assertDoubleEquals(0D, $opt$LongToDouble(0L));
- assertDoubleEquals(-1D, $opt$LongToDouble(-1L));
- assertDoubleEquals(51D, $opt$LongToDouble(51L));
- assertDoubleEquals(-51D, $opt$LongToDouble(-51L));
- assertDoubleEquals(2147483647D, $opt$LongToDouble(2147483647L)); // 2^31 - 1
- assertDoubleEquals(-2147483647D, $opt$LongToDouble(-2147483647L)); // -(2^31 - 1)
- assertDoubleEquals(-2147483648D, $opt$LongToDouble(-2147483648L)); // -(2^31)
- assertDoubleEquals(2147483648D, $opt$LongToDouble(2147483648L)); // (2^31)
- assertDoubleEquals(-2147483649D, $opt$LongToDouble(-2147483649L)); // -(2^31 + 1)
- assertDoubleEquals(4294967296D, $opt$LongToDouble(4294967296L)); // (2^32)
- assertDoubleEquals(-4294967296D, $opt$LongToDouble(-4294967296L)); // -(2^32)
- assertDoubleEquals(140739635871745D, $opt$LongToDouble(140739635871745L)); // 1 + 2^15 + 2^31 + 2^47
- assertDoubleEquals(-140739635871745D, $opt$LongToDouble(-140739635871745L)); // -(1 + 2^15 + 2^31 + 2^47)
- assertDoubleEquals(9223372036854775807D, $opt$LongToDouble(9223372036854775807L)); // 2^63 - 1
- assertDoubleEquals(-9223372036854775807D, $opt$LongToDouble(-9223372036854775807L)); // -(2^63 - 1)
- assertDoubleEquals(-9223372036854775808D, $opt$LongToDouble(-9223372036854775808L)); // -(2^63)
+ assertDoubleEquals(1D, $opt$noinline$LongToDouble(1L));
+ assertDoubleEquals(0D, $opt$noinline$LongToDouble(0L));
+ assertDoubleEquals(-1D, $opt$noinline$LongToDouble(-1L));
+ assertDoubleEquals(51D, $opt$noinline$LongToDouble(51L));
+ assertDoubleEquals(-51D, $opt$noinline$LongToDouble(-51L));
+ assertDoubleEquals(2147483647D, $opt$noinline$LongToDouble(2147483647L)); // 2^31 - 1
+ assertDoubleEquals(-2147483647D, $opt$noinline$LongToDouble(-2147483647L)); // -(2^31 - 1)
+ assertDoubleEquals(-2147483648D, $opt$noinline$LongToDouble(-2147483648L)); // -(2^31)
+ assertDoubleEquals(2147483648D, $opt$noinline$LongToDouble(2147483648L)); // (2^31)
+ assertDoubleEquals(-2147483649D, $opt$noinline$LongToDouble(-2147483649L)); // -(2^31 + 1)
+ assertDoubleEquals(4294967296D, $opt$noinline$LongToDouble(4294967296L)); // (2^32)
+ assertDoubleEquals(-4294967296D, $opt$noinline$LongToDouble(-4294967296L)); // -(2^32)
+ assertDoubleEquals(140739635871745D, $opt$noinline$LongToDouble(140739635871745L)); // 1 + 2^15 + 2^31 + 2^47
+ assertDoubleEquals(-140739635871745D, $opt$noinline$LongToDouble(-140739635871745L)); // -(1 + 2^15 + 2^31 + 2^47)
+ assertDoubleEquals(9223372036854775807D, $opt$noinline$LongToDouble(9223372036854775807L)); // 2^63 - 1
+ assertDoubleEquals(-9223372036854775807D, $opt$noinline$LongToDouble(-9223372036854775807L)); // -(2^63 - 1)
+ assertDoubleEquals(-9223372036854775808D, $opt$noinline$LongToDouble(-9223372036854775808L)); // -(2^63)
}
private static void floatToInt() {
- assertIntEquals(1, $opt$FloatToInt(1F));
- assertIntEquals(0, $opt$FloatToInt(0F));
- assertIntEquals(0, $opt$FloatToInt(-0F));
- assertIntEquals(-1, $opt$FloatToInt(-1F));
- assertIntEquals(51, $opt$FloatToInt(51F));
- assertIntEquals(-51, $opt$FloatToInt(-51F));
- assertIntEquals(0, $opt$FloatToInt(0.5F));
- assertIntEquals(0, $opt$FloatToInt(0.4999999F));
- assertIntEquals(0, $opt$FloatToInt(-0.4999999F));
- assertIntEquals(0, $opt$FloatToInt(-0.5F));
- assertIntEquals(42, $opt$FloatToInt(42.199F));
- assertIntEquals(-42, $opt$FloatToInt(-42.199F));
- assertIntEquals(2147483647, $opt$FloatToInt(2147483647F)); // 2^31 - 1
- assertIntEquals(-2147483648, $opt$FloatToInt(-2147483647F)); // -(2^31 - 1)
- assertIntEquals(-2147483648, $opt$FloatToInt(-2147483648F)); // -(2^31)
- assertIntEquals(2147483647, $opt$FloatToInt(2147483648F)); // (2^31)
- assertIntEquals(-2147483648, $opt$FloatToInt(-2147483649F)); // -(2^31 + 1)
- assertIntEquals(2147483647, $opt$FloatToInt(9223372036854775807F)); // 2^63 - 1
- assertIntEquals(-2147483648, $opt$FloatToInt(-9223372036854775807F)); // -(2^63 - 1)
- assertIntEquals(-2147483648, $opt$FloatToInt(-9223372036854775808F)); // -(2^63)
- assertIntEquals(0, $opt$FloatToInt(Float.NaN));
- assertIntEquals(2147483647, $opt$FloatToInt(Float.POSITIVE_INFINITY));
- assertIntEquals(-2147483648, $opt$FloatToInt(Float.NEGATIVE_INFINITY));
+ assertIntEquals(1, $opt$noinline$FloatToInt(1F));
+ assertIntEquals(0, $opt$noinline$FloatToInt(0F));
+ assertIntEquals(0, $opt$noinline$FloatToInt(-0F));
+ assertIntEquals(-1, $opt$noinline$FloatToInt(-1F));
+ assertIntEquals(51, $opt$noinline$FloatToInt(51F));
+ assertIntEquals(-51, $opt$noinline$FloatToInt(-51F));
+ assertIntEquals(0, $opt$noinline$FloatToInt(0.5F));
+ assertIntEquals(0, $opt$noinline$FloatToInt(0.4999999F));
+ assertIntEquals(0, $opt$noinline$FloatToInt(-0.4999999F));
+ assertIntEquals(0, $opt$noinline$FloatToInt(-0.5F));
+ assertIntEquals(42, $opt$noinline$FloatToInt(42.199F));
+ assertIntEquals(-42, $opt$noinline$FloatToInt(-42.199F));
+ assertIntEquals(2147483647, $opt$noinline$FloatToInt(2147483647F)); // 2^31 - 1
+ assertIntEquals(-2147483648, $opt$noinline$FloatToInt(-2147483647F)); // -(2^31 - 1)
+ assertIntEquals(-2147483648, $opt$noinline$FloatToInt(-2147483648F)); // -(2^31)
+ assertIntEquals(2147483647, $opt$noinline$FloatToInt(2147483648F)); // (2^31)
+ assertIntEquals(-2147483648, $opt$noinline$FloatToInt(-2147483649F)); // -(2^31 + 1)
+ assertIntEquals(2147483647, $opt$noinline$FloatToInt(9223372036854775807F)); // 2^63 - 1
+ assertIntEquals(-2147483648, $opt$noinline$FloatToInt(-9223372036854775807F)); // -(2^63 - 1)
+ assertIntEquals(-2147483648, $opt$noinline$FloatToInt(-9223372036854775808F)); // -(2^63)
+ assertIntEquals(0, $opt$noinline$FloatToInt(Float.NaN));
+ assertIntEquals(2147483647, $opt$noinline$FloatToInt(Float.POSITIVE_INFINITY));
+ assertIntEquals(-2147483648, $opt$noinline$FloatToInt(Float.NEGATIVE_INFINITY));
}
private static void floatToLong() {
- assertLongEquals(1L, $opt$FloatToLong(1F));
- assertLongEquals(0L, $opt$FloatToLong(0F));
- assertLongEquals(0L, $opt$FloatToLong(-0F));
- assertLongEquals(-1L, $opt$FloatToLong(-1F));
- assertLongEquals(51L, $opt$FloatToLong(51F));
- assertLongEquals(-51L, $opt$FloatToLong(-51F));
- assertLongEquals(0L, $opt$FloatToLong(0.5F));
- assertLongEquals(0L, $opt$FloatToLong(0.4999999F));
- assertLongEquals(0L, $opt$FloatToLong(-0.4999999F));
- assertLongEquals(0L, $opt$FloatToLong(-0.5F));
- assertLongEquals(42L, $opt$FloatToLong(42.199F));
- assertLongEquals(-42L, $opt$FloatToLong(-42.199F));
- assertLongEquals(2147483648L, $opt$FloatToLong(2147483647F)); // 2^31 - 1
- assertLongEquals(-2147483648L, $opt$FloatToLong(-2147483647F)); // -(2^31 - 1)
- assertLongEquals(-2147483648L, $opt$FloatToLong(-2147483648F)); // -(2^31)
- assertLongEquals(2147483648L, $opt$FloatToLong(2147483648F)); // (2^31)
- assertLongEquals(-2147483648L, $opt$FloatToLong(-2147483649F)); // -(2^31 + 1)
- assertLongEquals(9223372036854775807L, $opt$FloatToLong(9223372036854775807F)); // 2^63 - 1
- assertLongEquals(-9223372036854775808L, $opt$FloatToLong(-9223372036854775807F)); // -(2^63 - 1)
- assertLongEquals(-9223372036854775808L, $opt$FloatToLong(-9223372036854775808F)); // -(2^63)
- assertLongEquals(0L, $opt$FloatToLong(Float.NaN));
- assertLongEquals(9223372036854775807L, $opt$FloatToLong(Float.POSITIVE_INFINITY));
- assertLongEquals(-9223372036854775808L, $opt$FloatToLong(Float.NEGATIVE_INFINITY));
+ assertLongEquals(1L, $opt$noinline$FloatToLong(1F));
+ assertLongEquals(0L, $opt$noinline$FloatToLong(0F));
+ assertLongEquals(0L, $opt$noinline$FloatToLong(-0F));
+ assertLongEquals(-1L, $opt$noinline$FloatToLong(-1F));
+ assertLongEquals(51L, $opt$noinline$FloatToLong(51F));
+ assertLongEquals(-51L, $opt$noinline$FloatToLong(-51F));
+ assertLongEquals(0L, $opt$noinline$FloatToLong(0.5F));
+ assertLongEquals(0L, $opt$noinline$FloatToLong(0.4999999F));
+ assertLongEquals(0L, $opt$noinline$FloatToLong(-0.4999999F));
+ assertLongEquals(0L, $opt$noinline$FloatToLong(-0.5F));
+ assertLongEquals(42L, $opt$noinline$FloatToLong(42.199F));
+ assertLongEquals(-42L, $opt$noinline$FloatToLong(-42.199F));
+ assertLongEquals(2147483648L, $opt$noinline$FloatToLong(2147483647F)); // 2^31 - 1
+ assertLongEquals(-2147483648L, $opt$noinline$FloatToLong(-2147483647F)); // -(2^31 - 1)
+ assertLongEquals(-2147483648L, $opt$noinline$FloatToLong(-2147483648F)); // -(2^31)
+ assertLongEquals(2147483648L, $opt$noinline$FloatToLong(2147483648F)); // (2^31)
+ assertLongEquals(-2147483648L, $opt$noinline$FloatToLong(-2147483649F)); // -(2^31 + 1)
+ assertLongEquals(9223372036854775807L, $opt$noinline$FloatToLong(9223372036854775807F)); // 2^63 - 1
+ assertLongEquals(-9223372036854775808L, $opt$noinline$FloatToLong(-9223372036854775807F)); // -(2^63 - 1)
+ assertLongEquals(-9223372036854775808L, $opt$noinline$FloatToLong(-9223372036854775808F)); // -(2^63)
+ assertLongEquals(0L, $opt$noinline$FloatToLong(Float.NaN));
+ assertLongEquals(9223372036854775807L, $opt$noinline$FloatToLong(Float.POSITIVE_INFINITY));
+ assertLongEquals(-9223372036854775808L, $opt$noinline$FloatToLong(Float.NEGATIVE_INFINITY));
}
private static void floatToDouble() {
- assertDoubleEquals(1D, $opt$FloatToDouble(1F));
- assertDoubleEquals(0D, $opt$FloatToDouble(0F));
- assertDoubleEquals(0D, $opt$FloatToDouble(-0F));
- assertDoubleEquals(-1D, $opt$FloatToDouble(-1F));
- assertDoubleEquals(51D, $opt$FloatToDouble(51F));
- assertDoubleEquals(-51D, $opt$FloatToDouble(-51F));
- assertDoubleEquals(0.5D, $opt$FloatToDouble(0.5F));
- assertDoubleEquals(0.49999991059303284D, $opt$FloatToDouble(0.4999999F));
- assertDoubleEquals(-0.49999991059303284D, $opt$FloatToDouble(-0.4999999F));
- assertDoubleEquals(-0.5D, $opt$FloatToDouble(-0.5F));
- assertDoubleEquals(42.19900131225586D, $opt$FloatToDouble(42.199F));
- assertDoubleEquals(-42.19900131225586D, $opt$FloatToDouble(-42.199F));
- assertDoubleEquals(2147483648D, $opt$FloatToDouble(2147483647F)); // 2^31 - 1
- assertDoubleEquals(-2147483648D, $opt$FloatToDouble(-2147483647F)); // -(2^31 - 1)
- assertDoubleEquals(-2147483648D, $opt$FloatToDouble(-2147483648F)); // -(2^31)
- assertDoubleEquals(2147483648D, $opt$FloatToDouble(2147483648F)); // (2^31)
- assertDoubleEquals(-2147483648D, $opt$FloatToDouble(-2147483649F)); // -(2^31 + 1)
- assertDoubleEquals(9223372036854775807D, $opt$FloatToDouble(9223372036854775807F)); // 2^63 - 1
- assertDoubleEquals(-9223372036854775807D, $opt$FloatToDouble(-9223372036854775807F)); // -(2^63 - 1)
- assertDoubleEquals(-9223372036854775808D, $opt$FloatToDouble(-9223372036854775808F)); // -(2^63)
- assertDoubleIsNaN($opt$FloatToDouble(Float.NaN));
- assertDoubleEquals(Double.POSITIVE_INFINITY, $opt$FloatToDouble(Float.POSITIVE_INFINITY));
- assertDoubleEquals(Double.NEGATIVE_INFINITY, $opt$FloatToDouble(Float.NEGATIVE_INFINITY));
+ assertDoubleEquals(1D, $opt$noinline$FloatToDouble(1F));
+ assertDoubleEquals(0D, $opt$noinline$FloatToDouble(0F));
+ assertDoubleEquals(0D, $opt$noinline$FloatToDouble(-0F));
+ assertDoubleEquals(-1D, $opt$noinline$FloatToDouble(-1F));
+ assertDoubleEquals(51D, $opt$noinline$FloatToDouble(51F));
+ assertDoubleEquals(-51D, $opt$noinline$FloatToDouble(-51F));
+ assertDoubleEquals(0.5D, $opt$noinline$FloatToDouble(0.5F));
+ assertDoubleEquals(0.49999991059303284D, $opt$noinline$FloatToDouble(0.4999999F));
+ assertDoubleEquals(-0.49999991059303284D, $opt$noinline$FloatToDouble(-0.4999999F));
+ assertDoubleEquals(-0.5D, $opt$noinline$FloatToDouble(-0.5F));
+ assertDoubleEquals(42.19900131225586D, $opt$noinline$FloatToDouble(42.199F));
+ assertDoubleEquals(-42.19900131225586D, $opt$noinline$FloatToDouble(-42.199F));
+ assertDoubleEquals(2147483648D, $opt$noinline$FloatToDouble(2147483647F)); // 2^31 - 1
+ assertDoubleEquals(-2147483648D, $opt$noinline$FloatToDouble(-2147483647F)); // -(2^31 - 1)
+ assertDoubleEquals(-2147483648D, $opt$noinline$FloatToDouble(-2147483648F)); // -(2^31)
+ assertDoubleEquals(2147483648D, $opt$noinline$FloatToDouble(2147483648F)); // (2^31)
+ assertDoubleEquals(-2147483648D, $opt$noinline$FloatToDouble(-2147483649F)); // -(2^31 + 1)
+ assertDoubleEquals(9223372036854775807D, $opt$noinline$FloatToDouble(9223372036854775807F)); // 2^63 - 1
+ assertDoubleEquals(-9223372036854775807D, $opt$noinline$FloatToDouble(-9223372036854775807F)); // -(2^63 - 1)
+ assertDoubleEquals(-9223372036854775808D, $opt$noinline$FloatToDouble(-9223372036854775808F)); // -(2^63)
+ assertDoubleIsNaN($opt$noinline$FloatToDouble(Float.NaN));
+ assertDoubleEquals(Double.POSITIVE_INFINITY, $opt$noinline$FloatToDouble(Float.POSITIVE_INFINITY));
+ assertDoubleEquals(Double.NEGATIVE_INFINITY, $opt$noinline$FloatToDouble(Float.NEGATIVE_INFINITY));
}
private static void doubleToInt() {
- assertIntEquals(1, $opt$DoubleToInt(1D));
- assertIntEquals(0, $opt$DoubleToInt(0D));
- assertIntEquals(0, $opt$DoubleToInt(-0D));
- assertIntEquals(-1, $opt$DoubleToInt(-1D));
- assertIntEquals(51, $opt$DoubleToInt(51D));
- assertIntEquals(-51, $opt$DoubleToInt(-51D));
- assertIntEquals(0, $opt$DoubleToInt(0.5D));
- assertIntEquals(0, $opt$DoubleToInt(0.4999999D));
- assertIntEquals(0, $opt$DoubleToInt(-0.4999999D));
- assertIntEquals(0, $opt$DoubleToInt(-0.5D));
- assertIntEquals(42, $opt$DoubleToInt(42.199D));
- assertIntEquals(-42, $opt$DoubleToInt(-42.199D));
- assertIntEquals(2147483647, $opt$DoubleToInt(2147483647D)); // 2^31 - 1
- assertIntEquals(-2147483647, $opt$DoubleToInt(-2147483647D)); // -(2^31 - 1)
- assertIntEquals(-2147483648, $opt$DoubleToInt(-2147483648D)); // -(2^31)
- assertIntEquals(2147483647, $opt$DoubleToInt(2147483648D)); // (2^31)
- assertIntEquals(-2147483648, $opt$DoubleToInt(-2147483649D)); // -(2^31 + 1)
- assertIntEquals(2147483647, $opt$DoubleToInt(9223372036854775807D)); // 2^63 - 1
- assertIntEquals(-2147483648, $opt$DoubleToInt(-9223372036854775807D)); // -(2^63 - 1)
- assertIntEquals(-2147483648, $opt$DoubleToInt(-9223372036854775808D)); // -(2^63)
- assertIntEquals(0, $opt$DoubleToInt(Double.NaN));
- assertIntEquals(2147483647, $opt$DoubleToInt(Double.POSITIVE_INFINITY));
- assertIntEquals(-2147483648, $opt$DoubleToInt(Double.NEGATIVE_INFINITY));
+ assertIntEquals(1, $opt$noinline$DoubleToInt(1D));
+ assertIntEquals(0, $opt$noinline$DoubleToInt(0D));
+ assertIntEquals(0, $opt$noinline$DoubleToInt(-0D));
+ assertIntEquals(-1, $opt$noinline$DoubleToInt(-1D));
+ assertIntEquals(51, $opt$noinline$DoubleToInt(51D));
+ assertIntEquals(-51, $opt$noinline$DoubleToInt(-51D));
+ assertIntEquals(0, $opt$noinline$DoubleToInt(0.5D));
+ assertIntEquals(0, $opt$noinline$DoubleToInt(0.4999999D));
+ assertIntEquals(0, $opt$noinline$DoubleToInt(-0.4999999D));
+ assertIntEquals(0, $opt$noinline$DoubleToInt(-0.5D));
+ assertIntEquals(42, $opt$noinline$DoubleToInt(42.199D));
+ assertIntEquals(-42, $opt$noinline$DoubleToInt(-42.199D));
+ assertIntEquals(2147483647, $opt$noinline$DoubleToInt(2147483647D)); // 2^31 - 1
+ assertIntEquals(-2147483647, $opt$noinline$DoubleToInt(-2147483647D)); // -(2^31 - 1)
+ assertIntEquals(-2147483648, $opt$noinline$DoubleToInt(-2147483648D)); // -(2^31)
+ assertIntEquals(2147483647, $opt$noinline$DoubleToInt(2147483648D)); // (2^31)
+ assertIntEquals(-2147483648, $opt$noinline$DoubleToInt(-2147483649D)); // -(2^31 + 1)
+ assertIntEquals(2147483647, $opt$noinline$DoubleToInt(9223372036854775807D)); // 2^63 - 1
+ assertIntEquals(-2147483648, $opt$noinline$DoubleToInt(-9223372036854775807D)); // -(2^63 - 1)
+ assertIntEquals(-2147483648, $opt$noinline$DoubleToInt(-9223372036854775808D)); // -(2^63)
+ assertIntEquals(0, $opt$noinline$DoubleToInt(Double.NaN));
+ assertIntEquals(2147483647, $opt$noinline$DoubleToInt(Double.POSITIVE_INFINITY));
+ assertIntEquals(-2147483648, $opt$noinline$DoubleToInt(Double.NEGATIVE_INFINITY));
}
private static void doubleToLong() {
- assertLongEquals(1L, $opt$DoubleToLong(1D));
- assertLongEquals(0L, $opt$DoubleToLong(0D));
- assertLongEquals(0L, $opt$DoubleToLong(-0D));
- assertLongEquals(-1L, $opt$DoubleToLong(-1D));
- assertLongEquals(51L, $opt$DoubleToLong(51D));
- assertLongEquals(-51L, $opt$DoubleToLong(-51D));
- assertLongEquals(0L, $opt$DoubleToLong(0.5D));
- assertLongEquals(0L, $opt$DoubleToLong(0.4999999D));
- assertLongEquals(0L, $opt$DoubleToLong(-0.4999999D));
- assertLongEquals(0L, $opt$DoubleToLong(-0.5D));
- assertLongEquals(42L, $opt$DoubleToLong(42.199D));
- assertLongEquals(-42L, $opt$DoubleToLong(-42.199D));
- assertLongEquals(2147483647L, $opt$DoubleToLong(2147483647D)); // 2^31 - 1
- assertLongEquals(-2147483647L, $opt$DoubleToLong(-2147483647D)); // -(2^31 - 1)
- assertLongEquals(-2147483648L, $opt$DoubleToLong(-2147483648D)); // -(2^31)
- assertLongEquals(2147483648L, $opt$DoubleToLong(2147483648D)); // (2^31)
- assertLongEquals(-2147483649L, $opt$DoubleToLong(-2147483649D)); // -(2^31 + 1)
- assertLongEquals(9223372036854775807L, $opt$DoubleToLong(9223372036854775807D)); // 2^63 - 1
- assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(-9223372036854775807D)); // -(2^63 - 1)
- assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(-9223372036854775808D)); // -(2^63)
- assertLongEquals(0L, $opt$DoubleToLong(Double.NaN));
- assertLongEquals(9223372036854775807L, $opt$DoubleToLong(Double.POSITIVE_INFINITY));
- assertLongEquals(-9223372036854775808L, $opt$DoubleToLong(Double.NEGATIVE_INFINITY));
+ assertLongEquals(1L, $opt$noinline$DoubleToLong(1D));
+ assertLongEquals(0L, $opt$noinline$DoubleToLong(0D));
+ assertLongEquals(0L, $opt$noinline$DoubleToLong(-0D));
+ assertLongEquals(-1L, $opt$noinline$DoubleToLong(-1D));
+ assertLongEquals(51L, $opt$noinline$DoubleToLong(51D));
+ assertLongEquals(-51L, $opt$noinline$DoubleToLong(-51D));
+ assertLongEquals(0L, $opt$noinline$DoubleToLong(0.5D));
+ assertLongEquals(0L, $opt$noinline$DoubleToLong(0.4999999D));
+ assertLongEquals(0L, $opt$noinline$DoubleToLong(-0.4999999D));
+ assertLongEquals(0L, $opt$noinline$DoubleToLong(-0.5D));
+ assertLongEquals(42L, $opt$noinline$DoubleToLong(42.199D));
+ assertLongEquals(-42L, $opt$noinline$DoubleToLong(-42.199D));
+ assertLongEquals(2147483647L, $opt$noinline$DoubleToLong(2147483647D)); // 2^31 - 1
+ assertLongEquals(-2147483647L, $opt$noinline$DoubleToLong(-2147483647D)); // -(2^31 - 1)
+ assertLongEquals(-2147483648L, $opt$noinline$DoubleToLong(-2147483648D)); // -(2^31)
+ assertLongEquals(2147483648L, $opt$noinline$DoubleToLong(2147483648D)); // (2^31)
+ assertLongEquals(-2147483649L, $opt$noinline$DoubleToLong(-2147483649D)); // -(2^31 + 1)
+ assertLongEquals(9223372036854775807L, $opt$noinline$DoubleToLong(9223372036854775807D)); // 2^63 - 1
+ assertLongEquals(-9223372036854775808L, $opt$noinline$DoubleToLong(-9223372036854775807D)); // -(2^63 - 1)
+ assertLongEquals(-9223372036854775808L, $opt$noinline$DoubleToLong(-9223372036854775808D)); // -(2^63)
+ assertLongEquals(0L, $opt$noinline$DoubleToLong(Double.NaN));
+ assertLongEquals(9223372036854775807L, $opt$noinline$DoubleToLong(Double.POSITIVE_INFINITY));
+ assertLongEquals(-9223372036854775808L, $opt$noinline$DoubleToLong(Double.NEGATIVE_INFINITY));
}
private static void doubleToFloat() {
- assertFloatEquals(1F, $opt$DoubleToFloat(1D));
- assertFloatEquals(0F, $opt$DoubleToFloat(0D));
- assertFloatEquals(0F, $opt$DoubleToFloat(-0D));
- assertFloatEquals(-1F, $opt$DoubleToFloat(-1D));
- assertFloatEquals(51F, $opt$DoubleToFloat(51D));
- assertFloatEquals(-51F, $opt$DoubleToFloat(-51D));
- assertFloatEquals(0.5F, $opt$DoubleToFloat(0.5D));
- assertFloatEquals(0.4999999F, $opt$DoubleToFloat(0.4999999D));
- assertFloatEquals(-0.4999999F, $opt$DoubleToFloat(-0.4999999D));
- assertFloatEquals(-0.5F, $opt$DoubleToFloat(-0.5D));
- assertFloatEquals(42.199F, $opt$DoubleToFloat(42.199D));
- assertFloatEquals(-42.199F, $opt$DoubleToFloat(-42.199D));
- assertFloatEquals(2147483648F, $opt$DoubleToFloat(2147483647D)); // 2^31 - 1
- assertFloatEquals(-2147483648F, $opt$DoubleToFloat(-2147483647D)); // -(2^31 - 1)
- assertFloatEquals(-2147483648F, $opt$DoubleToFloat(-2147483648D)); // -(2^31)
- assertFloatEquals(2147483648F, $opt$DoubleToFloat(2147483648D)); // (2^31)
- assertFloatEquals(-2147483648F, $opt$DoubleToFloat(-2147483649D)); // -(2^31 + 1)
- assertFloatEquals(9223372036854775807F, $opt$DoubleToFloat(9223372036854775807D)); // 2^63 - 1
- assertFloatEquals(-9223372036854775807F, $opt$DoubleToFloat(-9223372036854775807D)); // -(2^63 - 1)
- assertFloatEquals(-9223372036854775808F, $opt$DoubleToFloat(-9223372036854775808D)); // -(2^63)
- assertFloatIsNaN($opt$DoubleToFloat(Float.NaN));
- assertFloatEquals(Float.POSITIVE_INFINITY, $opt$DoubleToFloat(Double.POSITIVE_INFINITY));
- assertFloatEquals(Float.NEGATIVE_INFINITY, $opt$DoubleToFloat(Double.NEGATIVE_INFINITY));
+ assertFloatEquals(1F, $opt$noinline$DoubleToFloat(1D));
+ assertFloatEquals(0F, $opt$noinline$DoubleToFloat(0D));
+ assertFloatEquals(0F, $opt$noinline$DoubleToFloat(-0D));
+ assertFloatEquals(-1F, $opt$noinline$DoubleToFloat(-1D));
+ assertFloatEquals(51F, $opt$noinline$DoubleToFloat(51D));
+ assertFloatEquals(-51F, $opt$noinline$DoubleToFloat(-51D));
+ assertFloatEquals(0.5F, $opt$noinline$DoubleToFloat(0.5D));
+ assertFloatEquals(0.4999999F, $opt$noinline$DoubleToFloat(0.4999999D));
+ assertFloatEquals(-0.4999999F, $opt$noinline$DoubleToFloat(-0.4999999D));
+ assertFloatEquals(-0.5F, $opt$noinline$DoubleToFloat(-0.5D));
+ assertFloatEquals(42.199F, $opt$noinline$DoubleToFloat(42.199D));
+ assertFloatEquals(-42.199F, $opt$noinline$DoubleToFloat(-42.199D));
+ assertFloatEquals(2147483648F, $opt$noinline$DoubleToFloat(2147483647D)); // 2^31 - 1
+ assertFloatEquals(-2147483648F, $opt$noinline$DoubleToFloat(-2147483647D)); // -(2^31 - 1)
+ assertFloatEquals(-2147483648F, $opt$noinline$DoubleToFloat(-2147483648D)); // -(2^31)
+ assertFloatEquals(2147483648F, $opt$noinline$DoubleToFloat(2147483648D)); // (2^31)
+ assertFloatEquals(-2147483648F, $opt$noinline$DoubleToFloat(-2147483649D)); // -(2^31 + 1)
+ assertFloatEquals(9223372036854775807F, $opt$noinline$DoubleToFloat(9223372036854775807D)); // 2^63 - 1
+ assertFloatEquals(-9223372036854775807F, $opt$noinline$DoubleToFloat(-9223372036854775807D)); // -(2^63 - 1)
+ assertFloatEquals(-9223372036854775808F, $opt$noinline$DoubleToFloat(-9223372036854775808D)); // -(2^63)
+ assertFloatIsNaN($opt$noinline$DoubleToFloat(Float.NaN));
+ assertFloatEquals(Float.POSITIVE_INFINITY, $opt$noinline$DoubleToFloat(Double.POSITIVE_INFINITY));
+ assertFloatEquals(Float.NEGATIVE_INFINITY, $opt$noinline$DoubleToFloat(Double.NEGATIVE_INFINITY));
}
private static void shortToByte() {
- assertByteEquals((byte)1, $opt$ShortToByte((short)1));
- assertByteEquals((byte)0, $opt$ShortToByte((short)0));
- assertByteEquals((byte)-1, $opt$ShortToByte((short)-1));
- assertByteEquals((byte)51, $opt$ShortToByte((short)51));
- assertByteEquals((byte)-51, $opt$ShortToByte((short)-51));
- assertByteEquals((byte)127, $opt$ShortToByte((short)127)); // 2^7 - 1
- assertByteEquals((byte)-127, $opt$ShortToByte((short)-127)); // -(2^7 - 1)
- assertByteEquals((byte)-128, $opt$ShortToByte((short)-128)); // -(2^7)
- assertByteEquals((byte)-128, $opt$ShortToByte((short)128)); // 2^7
- assertByteEquals((byte)127, $opt$ShortToByte((short)-129)); // -(2^7 + 1)
- assertByteEquals((byte)-1, $opt$ShortToByte((short)32767)); // 2^15 - 1
- assertByteEquals((byte)0, $opt$ShortToByte((short)-32768)); // -(2^15)
+ assertByteEquals((byte)1, $opt$noinline$ShortToByte((short)1));
+ assertByteEquals((byte)0, $opt$noinline$ShortToByte((short)0));
+ assertByteEquals((byte)-1, $opt$noinline$ShortToByte((short)-1));
+ assertByteEquals((byte)51, $opt$noinline$ShortToByte((short)51));
+ assertByteEquals((byte)-51, $opt$noinline$ShortToByte((short)-51));
+ assertByteEquals((byte)127, $opt$noinline$ShortToByte((short)127)); // 2^7 - 1
+ assertByteEquals((byte)-127, $opt$noinline$ShortToByte((short)-127)); // -(2^7 - 1)
+ assertByteEquals((byte)-128, $opt$noinline$ShortToByte((short)-128)); // -(2^7)
+ assertByteEquals((byte)-128, $opt$noinline$ShortToByte((short)128)); // 2^7
+ assertByteEquals((byte)127, $opt$noinline$ShortToByte((short)-129)); // -(2^7 + 1)
+ assertByteEquals((byte)-1, $opt$noinline$ShortToByte((short)32767)); // 2^15 - 1
+ assertByteEquals((byte)0, $opt$noinline$ShortToByte((short)-32768)); // -(2^15)
}
private static void intToByte() {
- assertByteEquals((byte)1, $opt$IntToByte(1));
- assertByteEquals((byte)0, $opt$IntToByte(0));
- assertByteEquals((byte)-1, $opt$IntToByte(-1));
- assertByteEquals((byte)51, $opt$IntToByte(51));
- assertByteEquals((byte)-51, $opt$IntToByte(-51));
- assertByteEquals((byte)127, $opt$IntToByte(127)); // 2^7 - 1
- assertByteEquals((byte)-127, $opt$IntToByte(-127)); // -(2^7 - 1)
- assertByteEquals((byte)-128, $opt$IntToByte(-128)); // -(2^7)
- assertByteEquals((byte)-128, $opt$IntToByte(128)); // 2^7
- assertByteEquals((byte)127, $opt$IntToByte(-129)); // -(2^7 + 1)
- assertByteEquals((byte)-1, $opt$IntToByte(2147483647)); // 2^31 - 1
- assertByteEquals((byte)0, $opt$IntToByte(-2147483648)); // -(2^31)
+ assertByteEquals((byte)1, $opt$noinline$IntToByte(1));
+ assertByteEquals((byte)0, $opt$noinline$IntToByte(0));
+ assertByteEquals((byte)-1, $opt$noinline$IntToByte(-1));
+ assertByteEquals((byte)51, $opt$noinline$IntToByte(51));
+ assertByteEquals((byte)-51, $opt$noinline$IntToByte(-51));
+ assertByteEquals((byte)127, $opt$noinline$IntToByte(127)); // 2^7 - 1
+ assertByteEquals((byte)-127, $opt$noinline$IntToByte(-127)); // -(2^7 - 1)
+ assertByteEquals((byte)-128, $opt$noinline$IntToByte(-128)); // -(2^7)
+ assertByteEquals((byte)-128, $opt$noinline$IntToByte(128)); // 2^7
+ assertByteEquals((byte)127, $opt$noinline$IntToByte(-129)); // -(2^7 + 1)
+ assertByteEquals((byte)-1, $opt$noinline$IntToByte(2147483647)); // 2^31 - 1
+ assertByteEquals((byte)0, $opt$noinline$IntToByte(-2147483648)); // -(2^31)
}
private static void charToByte() {
- assertByteEquals((byte)1, $opt$CharToByte((char)1));
- assertByteEquals((byte)0, $opt$CharToByte((char)0));
- assertByteEquals((byte)51, $opt$CharToByte((char)51));
- assertByteEquals((byte)127, $opt$CharToByte((char)127)); // 2^7 - 1
- assertByteEquals((byte)-128, $opt$CharToByte((char)128)); // 2^7
- assertByteEquals((byte)-1, $opt$CharToByte((char)32767)); // 2^15 - 1
- assertByteEquals((byte)-1, $opt$CharToByte((char)65535)); // 2^16 - 1
- assertByteEquals((byte)-1, $opt$CharToByte((char)-1));
- assertByteEquals((byte)-51, $opt$CharToByte((char)-51));
- assertByteEquals((byte)-127, $opt$CharToByte((char)-127)); // -(2^7 - 1)
- assertByteEquals((byte)-128, $opt$CharToByte((char)-128)); // -(2^7)
- assertByteEquals((byte)127, $opt$CharToByte((char)-129)); // -(2^7 + 1)
+ assertByteEquals((byte)1, $opt$noinline$CharToByte((char)1));
+ assertByteEquals((byte)0, $opt$noinline$CharToByte((char)0));
+ assertByteEquals((byte)51, $opt$noinline$CharToByte((char)51));
+ assertByteEquals((byte)127, $opt$noinline$CharToByte((char)127)); // 2^7 - 1
+ assertByteEquals((byte)-128, $opt$noinline$CharToByte((char)128)); // 2^7
+ assertByteEquals((byte)-1, $opt$noinline$CharToByte((char)32767)); // 2^15 - 1
+ assertByteEquals((byte)-1, $opt$noinline$CharToByte((char)65535)); // 2^16 - 1
+ assertByteEquals((byte)-1, $opt$noinline$CharToByte((char)-1));
+ assertByteEquals((byte)-51, $opt$noinline$CharToByte((char)-51));
+ assertByteEquals((byte)-127, $opt$noinline$CharToByte((char)-127)); // -(2^7 - 1)
+ assertByteEquals((byte)-128, $opt$noinline$CharToByte((char)-128)); // -(2^7)
+ assertByteEquals((byte)127, $opt$noinline$CharToByte((char)-129)); // -(2^7 + 1)
}
private static void byteToShort() {
- assertShortEquals((short)1, $opt$ByteToShort((byte)1));
- assertShortEquals((short)0, $opt$ByteToShort((byte)0));
- assertShortEquals((short)-1, $opt$ByteToShort((byte)-1));
- assertShortEquals((short)51, $opt$ByteToShort((byte)51));
- assertShortEquals((short)-51, $opt$ByteToShort((byte)-51));
- assertShortEquals((short)127, $opt$ByteToShort((byte)127)); // 2^7 - 1
- assertShortEquals((short)-127, $opt$ByteToShort((byte)-127)); // -(2^7 - 1)
- assertShortEquals((short)-128, $opt$ByteToShort((byte)-128)); // -(2^7)
+ assertShortEquals((short)1, $opt$noinline$ByteToShort((byte)1));
+ assertShortEquals((short)0, $opt$noinline$ByteToShort((byte)0));
+ assertShortEquals((short)-1, $opt$noinline$ByteToShort((byte)-1));
+ assertShortEquals((short)51, $opt$noinline$ByteToShort((byte)51));
+ assertShortEquals((short)-51, $opt$noinline$ByteToShort((byte)-51));
+ assertShortEquals((short)127, $opt$noinline$ByteToShort((byte)127)); // 2^7 - 1
+ assertShortEquals((short)-127, $opt$noinline$ByteToShort((byte)-127)); // -(2^7 - 1)
+ assertShortEquals((short)-128, $opt$noinline$ByteToShort((byte)-128)); // -(2^7)
}
private static void intToShort() {
- assertShortEquals((short)1, $opt$IntToShort(1));
- assertShortEquals((short)0, $opt$IntToShort(0));
- assertShortEquals((short)-1, $opt$IntToShort(-1));
- assertShortEquals((short)51, $opt$IntToShort(51));
- assertShortEquals((short)-51, $opt$IntToShort(-51));
- assertShortEquals((short)32767, $opt$IntToShort(32767)); // 2^15 - 1
- assertShortEquals((short)-32767, $opt$IntToShort(-32767)); // -(2^15 - 1)
- assertShortEquals((short)-32768, $opt$IntToShort(-32768)); // -(2^15)
- assertShortEquals((short)-32768, $opt$IntToShort(32768)); // 2^15
- assertShortEquals((short)32767, $opt$IntToShort(-32769)); // -(2^15 + 1)
- assertShortEquals((short)-1, $opt$IntToShort(2147483647)); // 2^31 - 1
- assertShortEquals((short)0, $opt$IntToShort(-2147483648)); // -(2^31)
+ assertShortEquals((short)1, $opt$noinline$IntToShort(1));
+ assertShortEquals((short)0, $opt$noinline$IntToShort(0));
+ assertShortEquals((short)-1, $opt$noinline$IntToShort(-1));
+ assertShortEquals((short)51, $opt$noinline$IntToShort(51));
+ assertShortEquals((short)-51, $opt$noinline$IntToShort(-51));
+ assertShortEquals((short)32767, $opt$noinline$IntToShort(32767)); // 2^15 - 1
+ assertShortEquals((short)-32767, $opt$noinline$IntToShort(-32767)); // -(2^15 - 1)
+ assertShortEquals((short)-32768, $opt$noinline$IntToShort(-32768)); // -(2^15)
+ assertShortEquals((short)-32768, $opt$noinline$IntToShort(32768)); // 2^15
+ assertShortEquals((short)32767, $opt$noinline$IntToShort(-32769)); // -(2^15 + 1)
+ assertShortEquals((short)-1, $opt$noinline$IntToShort(2147483647)); // 2^31 - 1
+ assertShortEquals((short)0, $opt$noinline$IntToShort(-2147483648)); // -(2^31)
}
private static void charToShort() {
- assertShortEquals((short)1, $opt$CharToShort((char)1));
- assertShortEquals((short)0, $opt$CharToShort((char)0));
- assertShortEquals((short)51, $opt$CharToShort((char)51));
- assertShortEquals((short)32767, $opt$CharToShort((char)32767)); // 2^15 - 1
- assertShortEquals((short)-32768, $opt$CharToShort((char)32768)); // 2^15
- assertShortEquals((short)-32767, $opt$CharToShort((char)32769)); // 2^15
- assertShortEquals((short)-1, $opt$CharToShort((char)65535)); // 2^16 - 1
- assertShortEquals((short)-1, $opt$CharToShort((char)-1));
- assertShortEquals((short)-51, $opt$CharToShort((char)-51));
- assertShortEquals((short)-32767, $opt$CharToShort((char)-32767)); // -(2^15 - 1)
- assertShortEquals((short)-32768, $opt$CharToShort((char)-32768)); // -(2^15)
- assertShortEquals((short)32767, $opt$CharToShort((char)-32769)); // -(2^15 + 1)
+ assertShortEquals((short)1, $opt$noinline$CharToShort((char)1));
+ assertShortEquals((short)0, $opt$noinline$CharToShort((char)0));
+ assertShortEquals((short)51, $opt$noinline$CharToShort((char)51));
+ assertShortEquals((short)32767, $opt$noinline$CharToShort((char)32767)); // 2^15 - 1
+ assertShortEquals((short)-32768, $opt$noinline$CharToShort((char)32768)); // 2^15
+ assertShortEquals((short)-32767, $opt$noinline$CharToShort((char)32769)); // 2^15
+ assertShortEquals((short)-1, $opt$noinline$CharToShort((char)65535)); // 2^16 - 1
+ assertShortEquals((short)-1, $opt$noinline$CharToShort((char)-1));
+ assertShortEquals((short)-51, $opt$noinline$CharToShort((char)-51));
+ assertShortEquals((short)-32767, $opt$noinline$CharToShort((char)-32767)); // -(2^15 - 1)
+ assertShortEquals((short)-32768, $opt$noinline$CharToShort((char)-32768)); // -(2^15)
+ assertShortEquals((short)32767, $opt$noinline$CharToShort((char)-32769)); // -(2^15 + 1)
}
private static void byteToChar() {
- assertCharEquals((char)1, $opt$ByteToChar((byte)1));
- assertCharEquals((char)0, $opt$ByteToChar((byte)0));
- assertCharEquals((char)65535, $opt$ByteToChar((byte)-1));
- assertCharEquals((char)51, $opt$ByteToChar((byte)51));
- assertCharEquals((char)65485, $opt$ByteToChar((byte)-51));
- assertCharEquals((char)127, $opt$ByteToChar((byte)127)); // 2^7 - 1
- assertCharEquals((char)65409, $opt$ByteToChar((byte)-127)); // -(2^7 - 1)
- assertCharEquals((char)65408, $opt$ByteToChar((byte)-128)); // -(2^7)
+ assertCharEquals((char)1, $opt$noinline$ByteToChar((byte)1));
+ assertCharEquals((char)0, $opt$noinline$ByteToChar((byte)0));
+ assertCharEquals((char)65535, $opt$noinline$ByteToChar((byte)-1));
+ assertCharEquals((char)51, $opt$noinline$ByteToChar((byte)51));
+ assertCharEquals((char)65485, $opt$noinline$ByteToChar((byte)-51));
+ assertCharEquals((char)127, $opt$noinline$ByteToChar((byte)127)); // 2^7 - 1
+ assertCharEquals((char)65409, $opt$noinline$ByteToChar((byte)-127)); // -(2^7 - 1)
+ assertCharEquals((char)65408, $opt$noinline$ByteToChar((byte)-128)); // -(2^7)
}
private static void shortToChar() {
- assertCharEquals((char)1, $opt$ShortToChar((short)1));
- assertCharEquals((char)0, $opt$ShortToChar((short)0));
- assertCharEquals((char)65535, $opt$ShortToChar((short)-1));
- assertCharEquals((char)51, $opt$ShortToChar((short)51));
- assertCharEquals((char)65485, $opt$ShortToChar((short)-51));
- assertCharEquals((char)32767, $opt$ShortToChar((short)32767)); // 2^15 - 1
- assertCharEquals((char)32769, $opt$ShortToChar((short)-32767)); // -(2^15 - 1)
- assertCharEquals((char)32768, $opt$ShortToChar((short)-32768)); // -(2^15)
+ assertCharEquals((char)1, $opt$noinline$ShortToChar((short)1));
+ assertCharEquals((char)0, $opt$noinline$ShortToChar((short)0));
+ assertCharEquals((char)65535, $opt$noinline$ShortToChar((short)-1));
+ assertCharEquals((char)51, $opt$noinline$ShortToChar((short)51));
+ assertCharEquals((char)65485, $opt$noinline$ShortToChar((short)-51));
+ assertCharEquals((char)32767, $opt$noinline$ShortToChar((short)32767)); // 2^15 - 1
+ assertCharEquals((char)32769, $opt$noinline$ShortToChar((short)-32767)); // -(2^15 - 1)
+ assertCharEquals((char)32768, $opt$noinline$ShortToChar((short)-32768)); // -(2^15)
}
private static void intToChar() {
- assertCharEquals((char)1, $opt$IntToChar(1));
- assertCharEquals((char)0, $opt$IntToChar(0));
- assertCharEquals((char)65535, $opt$IntToChar(-1));
- assertCharEquals((char)51, $opt$IntToChar(51));
- assertCharEquals((char)65485, $opt$IntToChar(-51));
- assertCharEquals((char)32767, $opt$IntToChar(32767)); // 2^15 - 1
- assertCharEquals((char)32769, $opt$IntToChar(-32767)); // -(2^15 - 1)
- assertCharEquals((char)32768, $opt$IntToChar(32768)); // 2^15
- assertCharEquals((char)32768, $opt$IntToChar(-32768)); // -(2^15)
- assertCharEquals((char)65535, $opt$IntToChar(65535)); // 2^16 - 1
- assertCharEquals((char)1, $opt$IntToChar(-65535)); // -(2^16 - 1)
- assertCharEquals((char)0, $opt$IntToChar(65536)); // 2^16
- assertCharEquals((char)0, $opt$IntToChar(-65536)); // -(2^16)
- assertCharEquals((char)65535, $opt$IntToChar(2147483647)); // 2^31 - 1
- assertCharEquals((char)0, $opt$IntToChar(-2147483648)); // -(2^31)
+ assertCharEquals((char)1, $opt$noinline$IntToChar(1));
+ assertCharEquals((char)0, $opt$noinline$IntToChar(0));
+ assertCharEquals((char)65535, $opt$noinline$IntToChar(-1));
+ assertCharEquals((char)51, $opt$noinline$IntToChar(51));
+ assertCharEquals((char)65485, $opt$noinline$IntToChar(-51));
+ assertCharEquals((char)32767, $opt$noinline$IntToChar(32767)); // 2^15 - 1
+ assertCharEquals((char)32769, $opt$noinline$IntToChar(-32767)); // -(2^15 - 1)
+ assertCharEquals((char)32768, $opt$noinline$IntToChar(32768)); // 2^15
+ assertCharEquals((char)32768, $opt$noinline$IntToChar(-32768)); // -(2^15)
+ assertCharEquals((char)65535, $opt$noinline$IntToChar(65535)); // 2^16 - 1
+ assertCharEquals((char)1, $opt$noinline$IntToChar(-65535)); // -(2^16 - 1)
+ assertCharEquals((char)0, $opt$noinline$IntToChar(65536)); // 2^16
+ assertCharEquals((char)0, $opt$noinline$IntToChar(-65536)); // -(2^16)
+ assertCharEquals((char)65535, $opt$noinline$IntToChar(2147483647)); // 2^31 - 1
+ assertCharEquals((char)0, $opt$noinline$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$noinline$ByteToLong(byte a) { if (doThrow) throw new Error(); return (long)a; }
+ static long $opt$noinline$ShortToLong(short a) { if (doThrow) throw new Error(); return (long)a; }
+ static long $opt$noinline$IntToLong(int a) { if (doThrow) throw new Error(); return (long)a; }
+ static long $opt$noinline$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$noinline$ByteToFloat(byte a) { if (doThrow) throw new Error(); return (float)a; }
+ static float $opt$noinline$ShortToFloat(short a) { if (doThrow) throw new Error(); return (float)a; }
+ static float $opt$noinline$IntToFloat(int a) { if (doThrow) throw new Error(); return (float)a; }
+ static float $opt$noinline$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$noinline$ByteToDouble(byte a) { if (doThrow) throw new Error(); return (double)a; }
+ static double $opt$noinline$ShortToDouble(short a) { if (doThrow) throw new Error(); return (double)a; }
+ static double $opt$noinline$IntToDouble(int a) { if (doThrow) throw new Error(); return (double)a; }
+ static double $opt$noinline$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$noinline$LongToInt(long a) { if (doThrow) throw new Error(); return (int)a; }
+ static int $opt$noinline$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$noinline$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$noinline$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$noinline$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$noinline$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$noinline$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$noinline$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$noinline$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$noinline$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$noinline$ShortToByte(short a) { if (doThrow) throw new Error(); return (byte)a; }
+ static byte $opt$noinline$IntToByte(int a) { if (doThrow) throw new Error(); return (byte)a; }
+ static byte $opt$noinline$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$noinline$ByteToShort(byte a) { if (doThrow) throw new Error(); return (short)a; }
+ static short $opt$noinline$IntToShort(int a) { if (doThrow) throw new Error(); return (short)a; }
+ static short $opt$noinline$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$noinline$ByteToChar(byte a) { if (doThrow) throw new Error(); return (char)a; }
+ static char $opt$noinline$ShortToChar(short a) { if (doThrow) throw new Error(); return (char)a; }
+ static char $opt$noinline$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..3899d7f 100644
--- a/test/441-checker-inliner/src/Main.java
+++ b/test/441-checker-inliner/src/Main.java
@@ -16,133 +16,133 @@
public class Main {
- // CHECK-START: void Main.InlineVoid() inliner (before)
- // CHECK-DAG: [[Const42:i\d+]] IntConstant 42
- // CHECK-DAG: InvokeStaticOrDirect
- // CHECK-DAG: InvokeStaticOrDirect [ [[Const42]] ]
+ /// CHECK-START: void Main.InlineVoid() inliner (before)
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: InvokeStaticOrDirect
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Const42>>,{{[ij]\d+}}]
- // CHECK-START: void Main.InlineVoid() inliner (after)
- // CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-START: void Main.InlineVoid() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
public static void InlineVoid() {
returnVoid();
returnVoidWithOneParameter(42);
}
- // 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-START: int Main.InlineParameter(int) inliner (before)
+ /// CHECK-DAG: <<Param:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect [<<Param>>,{{[ij]\d+}}]
+ /// CHECK-DAG: Return [<<Result>>]
- // CHECK-START: int Main.InlineParameter(int) inliner (after)
- // CHECK-DAG: [[Param:i\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Param]] ]
+ /// CHECK-START: int Main.InlineParameter(int) inliner (after)
+ /// 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-START: long Main.InlineWideParameter(long) inliner (before)
+ /// CHECK-DAG: <<Param:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Result:j\d+>> InvokeStaticOrDirect [<<Param>>,{{[ij]\d+}}]
+ /// CHECK-DAG: Return [<<Result>>]
- // CHECK-START: long Main.InlineWideParameter(long) inliner (after)
- // CHECK-DAG: [[Param:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Param]] ]
+ /// CHECK-START: long Main.InlineWideParameter(long) inliner (after)
+ /// 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-START: java.lang.Object Main.InlineReferenceParameter(java.lang.Object) inliner (before)
+ /// CHECK-DAG: <<Param:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Result:l\d+>> InvokeStaticOrDirect [<<Param>>,{{[ij]\d+}}]
+ /// 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-START: java.lang.Object Main.InlineReferenceParameter(java.lang.Object) inliner (after)
+ /// 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-START: int Main.InlineInt() inliner (before)
+ /// 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-START: int Main.InlineInt() inliner (after)
+ /// 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-START: long Main.InlineWide() inliner (before)
+ /// 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-START: long Main.InlineWide() inliner (after)
+ /// 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-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-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-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>>]
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-START: int Main.InlineFieldAccess() inliner (before)
+ /// 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-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-START: int Main.InlineFieldAccess() inliner (after)
- // CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-START: int Main.InlineFieldAccess() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
public static int InlineFieldAccess() {
return incCounter();
}
- // 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-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>>,{{[ij]\d+}}]
+ /// CHECK-DAG: <<Sub:i\d+>> InvokeStaticOrDirect [<<Const5>>,<<Const3>>,{{[ij]\d+}}]
+ /// 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-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>>]
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..b7863be 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-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-START: int Main.IntNegation() constant_folding (after)
- // CHECK-DAG: [[ConstN42:i\d+]] IntConstant -42
- // CHECK-DAG: Return [ [[ConstN42]] ]
+ /// CHECK-START: int Main.IntNegation() constant_folding (after)
+ /// CHECK-DAG: <<ConstN42:i\d+>> IntConstant -42
+ /// CHECK-DAG: Return [<<ConstN42>>]
public static int IntNegation() {
int x, y;
@@ -54,15 +72,15 @@
* on addition.
*/
- // 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-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-START: int Main.IntAddition1() constant_folding (after)
- // CHECK-DAG: [[Const3:i\d+]] IntConstant 3
- // CHECK-DAG: Return [ [[Const3]] ]
+ /// CHECK-START: int Main.IntAddition1() constant_folding (after)
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: Return [<<Const3>>]
public static int IntAddition1() {
int a, b, c;
@@ -77,19 +95,19 @@
* on addition.
*/
- // 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-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-START: int Main.IntAddition2() constant_folding (after)
- // CHECK-DAG: [[Const14:i\d+]] IntConstant 14
- // CHECK-DAG: Return [ [[Const14]] ]
+ /// CHECK-START: int Main.IntAddition2() constant_folding (after)
+ /// CHECK-DAG: <<Const14:i\d+>> IntConstant 14
+ /// CHECK-DAG: Return [<<Const14>>]
public static int IntAddition2() {
int a, b, c;
@@ -108,15 +126,15 @@
* on subtraction.
*/
- // 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-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-START: int Main.IntSubtraction() constant_folding (after)
- // CHECK-DAG: [[Const4:i\d+]] IntConstant 4
- // CHECK-DAG: Return [ [[Const4]] ]
+ /// CHECK-START: int Main.IntSubtraction() constant_folding (after)
+ /// CHECK-DAG: <<Const4:i\d+>> IntConstant 4
+ /// CHECK-DAG: Return [<<Const4>>]
public static int IntSubtraction() {
int a, b, c;
@@ -131,15 +149,15 @@
* on addition.
*/
- // 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-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-START: long Main.LongAddition() constant_folding (after)
- // CHECK-DAG: [[Const3:j\d+]] LongConstant 3
- // CHECK-DAG: Return [ [[Const3]] ]
+ /// CHECK-START: long Main.LongAddition() constant_folding (after)
+ /// CHECK-DAG: <<Const3:j\d+>> LongConstant 3
+ /// CHECK-DAG: Return [<<Const3>>]
public static long LongAddition() {
long a, b, c;
@@ -154,15 +172,15 @@
* on subtraction.
*/
- // 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-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-START: long Main.LongSubtraction() constant_folding (after)
- // CHECK-DAG: [[Const4:j\d+]] LongConstant 4
- // CHECK-DAG: Return [ [[Const4]] ]
+ /// CHECK-START: long Main.LongSubtraction() constant_folding (after)
+ /// CHECK-DAG: <<Const4:j\d+>> LongConstant 4
+ /// CHECK-DAG: Return [<<Const4>>]
public static long LongSubtraction() {
long a, b, c;
@@ -176,15 +194,15 @@
* Three-register program with a constant (static) condition.
*/
- // 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-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-START: int Main.StaticCondition() constant_folding (after)
- // CHECK-DAG: [[Const1:i\d+]] IntConstant 1
- // CHECK-DAG: If [ [[Const1]] ]
+ /// CHECK-START: int Main.StaticCondition() constant_folding (after)
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: If [<<Const1>>]
public static int StaticCondition() {
int a, b, c;
@@ -206,19 +224,19 @@
* (forward) post-order traversal of the the dominator tree.
*/
- // 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-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-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-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>>]
public static int JumpsAndConditionals(boolean cond) {
int a, b, c;
@@ -235,178 +253,394 @@
* Test optimizations of arithmetic identities yielding a constant result.
*/
- // 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-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-START: int Main.And0(int) constant_folding (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-NOT: And
- // CHECK-DAG: Return [ [[Const0]] ]
+ /// CHECK-START: int Main.And0(int) constant_folding (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-NOT: And
+ /// 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-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-START: long Main.Mul0(long) constant_folding (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[Const0:j\d+]] LongConstant 0
- // CHECK-NOT: Mul
- // CHECK-DAG: Return [ [[Const0]] ]
+ /// CHECK-START: long Main.Mul0(long) constant_folding (after)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:j\d+>> LongConstant 0
+ /// CHECK-NOT: Mul
+ /// 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-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-START: int Main.OrAllOnes(int) constant_folding (after)
- // CHECK-DAG: [[ConstF:i\d+]] IntConstant -1
- // CHECK-NOT: Or
- // CHECK-DAG: Return [ [[ConstF]] ]
+ /// CHECK-START: int Main.OrAllOnes(int) constant_folding (after)
+ /// CHECK-DAG: <<ConstF:i\d+>> IntConstant -1
+ /// CHECK-NOT: Or
+ /// 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-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-START: long Main.Rem0(long) constant_folding (after)
- // CHECK-DAG: [[Const0:j\d+]] LongConstant 0
- // CHECK-NOT: Rem
- // CHECK-DAG: Return [ [[Const0]] ]
+ /// CHECK-START: long Main.Rem0(long) constant_folding (after)
+ /// CHECK-DAG: <<Const0:j\d+>> LongConstant 0
+ /// CHECK-NOT: Rem
+ /// 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-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-START: int Main.Rem1(int) constant_folding (after)
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-NOT: Rem
- // CHECK-DAG: Return [ [[Const0]] ]
+ /// CHECK-START: int Main.Rem1(int) constant_folding (after)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-NOT: Rem
+ /// 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-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 [<<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-NOT: Rem
- // CHECK-DAG: Return [ [[Const0]] ]
+ /// CHECK-START: long Main.RemN1(long) constant_folding (after)
+ /// CHECK-DAG: <<Const0:j\d+>> LongConstant 0
+ /// CHECK-NOT: Rem
+ /// 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-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-START: int Main.Shl0(int) constant_folding (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-NOT: Shl
- // CHECK-DAG: Return [ [[Const0]] ]
+ /// CHECK-START: int Main.Shl0(int) constant_folding (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-NOT: Shl
+ /// 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-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-START: long Main.Shr0(int) constant_folding (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:j\d+]] LongConstant 0
- // CHECK-NOT: Shr
- // CHECK-DAG: Return [ [[Const0]] ]
+ /// CHECK-START: long Main.Shr0(int) constant_folding (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:j\d+>> LongConstant 0
+ /// CHECK-NOT: Shr
+ /// 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-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-START: long Main.SubSameLong(long) constant_folding (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: [[Const0:j\d+]] LongConstant 0
- // CHECK-NOT: Sub
- // CHECK-DAG: Return [ [[Const0]] ]
+ /// CHECK-START: long Main.SubSameLong(long) constant_folding (after)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:j\d+>> LongConstant 0
+ /// CHECK-NOT: Sub
+ /// 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-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-START: int Main.UShr0(int) constant_folding (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-NOT: UShr
- // CHECK-DAG: Return [ [[Const0]] ]
+ /// CHECK-START: int Main.UShr0(int) constant_folding (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-NOT: UShr
+ /// 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-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-START: int Main.XorSameInt(int) constant_folding (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: [[Const0:i\d+]] IntConstant 0
- // CHECK-NOT: Xor
- // CHECK-DAG: Return [ [[Const0]] ]
+ /// CHECK-START: int Main.XorSameInt(int) constant_folding (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-NOT: Xor
+ /// 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/444-checker-nce/src/Main.java b/test/444-checker-nce/src/Main.java
index 501d79c..32122e4 100644
--- a/test/444-checker-nce/src/Main.java
+++ b/test/444-checker-nce/src/Main.java
@@ -16,63 +16,63 @@
public class Main {
- // CHECK-START: Main Main.keepTest(Main) instruction_simplifier_after_types (before)
- // CHECK: NullCheck
- // CHECK: InvokeStaticOrDirect
+ /// CHECK-START: Main Main.keepTest(Main) instruction_simplifier_after_types (before)
+ /// CHECK: NullCheck
+ /// CHECK: InvokeStaticOrDirect
- // CHECK-START: Main Main.keepTest(Main) instruction_simplifier_after_types (after)
- // CHECK: NullCheck
- // CHECK: InvokeStaticOrDirect
+ /// CHECK-START: Main Main.keepTest(Main) instruction_simplifier_after_types (after)
+ /// CHECK: NullCheck
+ /// CHECK: InvokeStaticOrDirect
public Main keepTest(Main m) {
return m.g();
}
- // CHECK-START: Main Main.thisTest() instruction_simplifier (before)
- // CHECK: NullCheck
- // CHECK: InvokeStaticOrDirect
+ /// CHECK-START: Main Main.thisTest() ssa_builder (after)
+ /// CHECK: NullCheck
+ /// CHECK: InvokeStaticOrDirect
- // CHECK-START: Main Main.thisTest() instruction_simplifier (after)
- // CHECK-NOT: NullCheck
- // CHECK: InvokeStaticOrDirect
+ /// CHECK-START: Main Main.thisTest() instruction_simplifier_after_types (after)
+ /// CHECK-NOT: NullCheck
+ /// CHECK: InvokeStaticOrDirect
public Main thisTest() {
return g();
}
- // CHECK-START: Main Main.newInstanceRemoveTest() instruction_simplifier (before)
- // CHECK: NewInstance
- // CHECK: NullCheck
- // CHECK: InvokeStaticOrDirect
- // CHECK: NullCheck
- // CHECK: InvokeStaticOrDirect
+ /// CHECK-START: Main Main.newInstanceRemoveTest() ssa_builder (after)
+ /// CHECK: NewInstance
+ /// CHECK: NullCheck
+ /// CHECK: InvokeStaticOrDirect
+ /// CHECK: NullCheck
+ /// CHECK: InvokeStaticOrDirect
- // CHECK-START: Main Main.newInstanceRemoveTest() instruction_simplifier (after)
- // CHECK-NOT: NullCheck
+ /// CHECK-START: Main Main.newInstanceRemoveTest() instruction_simplifier_after_types (after)
+ /// CHECK-NOT: NullCheck
public Main newInstanceRemoveTest() {
Main m = new Main();
return m.g();
}
- // CHECK-START: Main Main.newArrayRemoveTest() instruction_simplifier (before)
- // CHECK: NewArray
- // CHECK: NullCheck
- // CHECK: ArrayGet
+ /// CHECK-START: Main Main.newArrayRemoveTest() ssa_builder (after)
+ /// CHECK: NewArray
+ /// CHECK: NullCheck
+ /// CHECK: ArrayGet
- // CHECK-START: Main Main.newArrayRemoveTest() instruction_simplifier (after)
- // CHECK: NewArray
- // CHECK-NOT: NullCheck
- // CHECK: ArrayGet
+ /// CHECK-START: Main Main.newArrayRemoveTest() instruction_simplifier_after_types (after)
+ /// CHECK: NewArray
+ /// CHECK-NOT: NullCheck
+ /// CHECK: ArrayGet
public Main newArrayRemoveTest() {
Main[] ms = new Main[1];
return ms[0];
}
- // CHECK-START: Main Main.ifRemoveTest(boolean) instruction_simplifier_after_types (before)
- // CHECK: NewInstance
- // CHECK: NullCheck
+ /// CHECK-START: Main Main.ifRemoveTest(boolean) instruction_simplifier_after_types (before)
+ /// CHECK: NewInstance
+ /// CHECK: NullCheck
- // CHECK-START: Main Main.ifRemoveTest(boolean) instruction_simplifier_after_types (after)
- // CHECK: NewInstance
- // CHECK-NOT: NullCheck
+ /// CHECK-START: Main Main.ifRemoveTest(boolean) instruction_simplifier_after_types (after)
+ /// CHECK: NewInstance
+ /// CHECK-NOT: NullCheck
public Main ifRemoveTest(boolean flag) {
Main m = null;
if (flag) {
@@ -83,13 +83,13 @@
return m.g();
}
- // CHECK-START: Main Main.ifKeepTest(boolean) instruction_simplifier_after_types (before)
- // CHECK: NewInstance
- // CHECK: NullCheck
+ /// CHECK-START: Main Main.ifKeepTest(boolean) instruction_simplifier_after_types (before)
+ /// CHECK: NewInstance
+ /// CHECK: NullCheck
- // CHECK-START: Main Main.ifKeepTest(boolean) instruction_simplifier_after_types (after)
- // CHECK: NewInstance
- // CHECK: NullCheck
+ /// CHECK-START: Main Main.ifKeepTest(boolean) instruction_simplifier_after_types (after)
+ /// CHECK: NewInstance
+ /// CHECK: NullCheck
public Main ifKeepTest(boolean flag) {
Main m = null;
if (flag) {
@@ -98,11 +98,11 @@
return m.g();
}
- // CHECK-START: Main Main.forRemoveTest(int) instruction_simplifier_after_types (before)
- // CHECK: NullCheck
+ /// CHECK-START: Main Main.forRemoveTest(int) instruction_simplifier_after_types (before)
+ /// CHECK: NullCheck
- // CHECK-START: Main Main.forRemoveTest(int) instruction_simplifier_after_types (after)
- // CHECK-NOT: NullCheck
+ /// CHECK-START: Main Main.forRemoveTest(int) instruction_simplifier_after_types (after)
+ /// CHECK-NOT: NullCheck
public Main forRemoveTest(int count) {
Main a = new Main();
Main m = new Main();
@@ -114,11 +114,11 @@
return m.g();
}
- // CHECK-START: Main Main.forKeepTest(int) instruction_simplifier_after_types (before)
- // CHECK: NullCheck
+ /// CHECK-START: Main Main.forKeepTest(int) instruction_simplifier_after_types (before)
+ /// CHECK: NullCheck
- // CHECK-START: Main Main.forKeepTest(int) instruction_simplifier_after_types (after)
- // CHECK: NullCheck
+ /// CHECK-START: Main Main.forKeepTest(int) instruction_simplifier_after_types (after)
+ /// CHECK: NullCheck
public Main forKeepTest(int count) {
Main a = new Main();
Main m = new Main();
@@ -132,11 +132,11 @@
return m.g();
}
- // CHECK-START: Main Main.phiFlowRemoveTest(int) instruction_simplifier_after_types (before)
- // CHECK: NullCheck
+ /// CHECK-START: Main Main.phiFlowRemoveTest(int) instruction_simplifier_after_types (before)
+ /// CHECK: NullCheck
- // CHECK-START: Main Main.phiFlowRemoveTest(int) instruction_simplifier_after_types (after)
- // CHECK-NOT: NullCheck
+ /// CHECK-START: Main Main.phiFlowRemoveTest(int) instruction_simplifier_after_types (after)
+ /// CHECK-NOT: NullCheck
public Main phiFlowRemoveTest(int count) {
Main a = new Main();
Main m = new Main();
@@ -154,11 +154,11 @@
return n.g();
}
- // CHECK-START: Main Main.phiFlowKeepTest(int) instruction_simplifier_after_types (before)
- // CHECK: NullCheck
+ /// CHECK-START: Main Main.phiFlowKeepTest(int) instruction_simplifier_after_types (before)
+ /// CHECK: NullCheck
- // CHECK-START: Main Main.phiFlowKeepTest(int) instruction_simplifier_after_types (after)
- // CHECK: NullCheck
+ /// CHECK-START: Main Main.phiFlowKeepTest(int) instruction_simplifier_after_types (after)
+ /// CHECK: NullCheck
public Main phiFlowKeepTest(int count) {
Main a = new Main();
Main m = new Main();
@@ -178,11 +178,11 @@
return n.g();
}
- // CHECK-START: Main Main.scopeRemoveTest(int, Main) instruction_simplifier (before)
- // CHECK: NullCheck
+ /// CHECK-START: Main Main.scopeRemoveTest(int, Main) ssa_builder (after)
+ /// CHECK: NullCheck
- // CHECK-START: Main Main.scopeRemoveTest(int, Main) instruction_simplifier (after)
- // CHECK-NOT: NullCheck
+ /// CHECK-START: Main Main.scopeRemoveTest(int, Main) instruction_simplifier_after_types (after)
+ /// CHECK-NOT: NullCheck
public Main scopeRemoveTest(int count, Main a) {
Main m = null;
for (int i = 0; i < count; i++) {
@@ -196,11 +196,11 @@
return m;
}
- // CHECK-START: Main Main.scopeKeepTest(int, Main) instruction_simplifier_after_types (before)
- // CHECK: NullCheck
+ /// CHECK-START: Main Main.scopeKeepTest(int, Main) instruction_simplifier_after_types (before)
+ /// CHECK: NullCheck
- // CHECK-START: Main Main.scopeKeepTest(int, Main) instruction_simplifier_after_types (after)
- // CHECK: NullCheck
+ /// CHECK-START: Main Main.scopeKeepTest(int, Main) instruction_simplifier_after_types (after)
+ /// CHECK: NullCheck
public Main scopeKeepTest(int count, Main a) {
Main m = new Main();
for (int i = 0; i < count; i++) {
@@ -214,11 +214,11 @@
return m;
}
- // CHECK-START: Main Main.scopeIfNotNullRemove(Main) instruction_simplifier_after_types (before)
- // CHECK: NullCheck
+ /// CHECK-START: Main Main.scopeIfNotNullRemove(Main) instruction_simplifier_after_types (before)
+ /// CHECK: NullCheck
- // CHECK-START: Main Main.scopeIfNotNullRemove(Main) instruction_simplifier_after_types (after)
- // CHECK-NOT: NullCheck
+ /// CHECK-START: Main Main.scopeIfNotNullRemove(Main) instruction_simplifier_after_types (after)
+ /// CHECK-NOT: NullCheck
public Main scopeIfNotNullRemove(Main m) {
if (m != null) {
return m.g();
@@ -226,11 +226,11 @@
return m;
}
- // CHECK-START: Main Main.scopeIfKeep(Main) instruction_simplifier_after_types (before)
- // CHECK: NullCheck
+ /// CHECK-START: Main Main.scopeIfKeep(Main) instruction_simplifier_after_types (before)
+ /// CHECK: NullCheck
- // CHECK-START: Main Main.scopeIfKeep(Main) instruction_simplifier_after_types (after)
- // CHECK: NullCheck
+ /// CHECK-START: Main Main.scopeIfKeep(Main) instruction_simplifier_after_types (after)
+ /// CHECK: NullCheck
public Main scopeIfKeep(Main m) {
if (m == null) {
m = new Main();
@@ -258,12 +258,12 @@
class ListElement {
private ListElement next;
- // CHECK-START: boolean ListElement.isShorter(ListElement, ListElement) instruction_simplifier_after_types (before)
- // CHECK: NullCheck
- // CHECK: NullCheck
+ /// CHECK-START: boolean ListElement.isShorter(ListElement, ListElement) instruction_simplifier_after_types (before)
+ /// CHECK: NullCheck
+ /// CHECK: NullCheck
- // CHECK-START: boolean ListElement.isShorter(ListElement, ListElement) instruction_simplifier_after_types (after)
- // CHECK-NOT: NullCheck
+ /// CHECK-START: boolean ListElement.isShorter(ListElement, ListElement) instruction_simplifier_after_types (after)
+ /// CHECK-NOT: NullCheck
static boolean isShorter(ListElement x, ListElement y) {
ListElement xTail = x;
ListElement yTail = y;
diff --git a/test/445-checker-licm/src/Main.java b/test/445-checker-licm/src/Main.java
index 91ac2ed..42f9a11 100644
--- a/test/445-checker-licm/src/Main.java
+++ b/test/445-checker-licm/src/Main.java
@@ -16,14 +16,14 @@
public class Main {
- // CHECK-START: int Main.div() licm (before)
- // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+ /// CHECK-START: int Main.div() licm (before)
+ /// CHECK-DAG: Div loop:{{B\d+}}
- // CHECK-START: int Main.div() licm (after)
- // CHECK-NOT: Div ( loop_header:{{B\d+}} )
+ /// CHECK-START: int Main.div() licm (after)
+ /// CHECK-NOT: Div loop:{{B\d+}}
- // CHECK-START: int Main.div() licm (after)
- // CHECK-DAG: Div ( loop_header:null )
+ /// CHECK-START: int Main.div() licm (after)
+ /// CHECK-DAG: Div loop:none
public static int div() {
int result = 0;
@@ -33,14 +33,14 @@
return result;
}
- // CHECK-START: int Main.innerDiv() licm (before)
- // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+ /// CHECK-START: int Main.innerDiv() licm (before)
+ /// CHECK-DAG: Div loop:{{B\d+}}
- // CHECK-START: int Main.innerDiv() licm (after)
- // CHECK-NOT: Div ( loop_header:{{B\d+}} )
+ /// CHECK-START: int Main.innerDiv() licm (after)
+ /// CHECK-NOT: Div loop:{{B\d+}}
- // CHECK-START: int Main.innerDiv() licm (after)
- // CHECK-DAG: Div ( loop_header:null )
+ /// CHECK-START: int Main.innerDiv() licm (after)
+ /// CHECK-DAG: Div loop:none
public static int innerDiv() {
int result = 0;
@@ -52,11 +52,11 @@
return result;
}
- // CHECK-START: int Main.innerDiv2() licm (before)
- // CHECK-DAG: Mul ( loop_header:{{B4}} )
+ /// CHECK-START: int Main.innerDiv2() licm (before)
+ /// CHECK-DAG: Mul loop:B4
- // CHECK-START: int Main.innerDiv2() licm (after)
- // CHECK-DAG: Mul ( loop_header:{{B2}} )
+ /// CHECK-START: int Main.innerDiv2() licm (after)
+ /// CHECK-DAG: Mul loop:B2
public static int innerDiv2() {
int result = 0;
@@ -71,11 +71,11 @@
return result;
}
- // CHECK-START: int Main.innerDiv3(int, int) licm (before)
- // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+ /// CHECK-START: int Main.innerDiv3(int, int) licm (before)
+ /// CHECK-DAG: Div loop:{{B\d+}}
- // CHECK-START: int Main.innerDiv3(int, int) licm (after)
- // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+ /// CHECK-START: int Main.innerDiv3(int, int) licm (after)
+ /// CHECK-DAG: Div loop:{{B\d+}}
public static int innerDiv3(int a, int b) {
int result = 0;
@@ -87,17 +87,17 @@
return result;
}
- // 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-START: int Main.arrayLength(int[]) licm (before)
+ /// 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-START: int Main.arrayLength(int[]) licm (after)
+ /// 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-START: int Main.arrayLength(int[]) licm (after)
+ /// 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..de00a09 100644
--- a/test/446-checker-inliner2/src/Main.java
+++ b/test/446-checker-inliner2/src/Main.java
@@ -16,16 +16,16 @@
public class Main {
- // CHECK-START: int Main.inlineInstanceCall(Main) inliner (before)
- // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ /// CHECK-START: int Main.inlineInstanceCall(Main) inliner (before)
+ /// 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-NOT: InvokeStaticOrDirect
- // CHECK-START: int Main.inlineInstanceCall(Main) inliner (after)
- // CHECK-DAG: [[Field:i\d+]] InstanceFieldGet
- // CHECK-DAG: Return [ [[Field]] ]
+ /// CHECK-START: int Main.inlineInstanceCall(Main) inliner (after)
+ /// CHECK-DAG: <<Field:i\d+>> InstanceFieldGet
+ /// CHECK-DAG: Return [<<Field>>]
public static int inlineInstanceCall(Main m) {
return m.foo();
@@ -37,16 +37,16 @@
int field = 42;
- // CHECK-START: int Main.inlineNestedCall() inliner (before)
- // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ /// CHECK-START: int Main.inlineNestedCall() inliner (before)
+ /// 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-NOT: InvokeStaticOrDirect
- // CHECK-START: int Main.inlineNestedCall() inliner (after)
- // CHECK-DAG: [[Const38:i\d+]] IntConstant 38
- // CHECK-DAG: Return [ [[Const38]] ]
+ /// CHECK-START: int Main.inlineNestedCall() inliner (after)
+ /// 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..e3fdffd 100644
--- a/test/447-checker-inliner3/src/Main.java
+++ b/test/447-checker-inliner3/src/Main.java
@@ -16,12 +16,12 @@
public class Main {
- // CHECK-START: int Main.inlineIfThenElse() inliner (before)
- // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ /// CHECK-START: int Main.inlineIfThenElse() inliner (before)
+ /// CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect
+ /// CHECK-DAG: Return [<<Invoke>>]
- // CHECK-START: int Main.inlineIfThenElse() inliner (after)
- // CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-START: int Main.inlineIfThenElse() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
public static int inlineIfThenElse() {
return foo(true);
@@ -35,11 +35,11 @@
}
}
- // CHECK-START: int Main.inlineInLoop() inliner (before)
- // CHECK-DAG: InvokeStaticOrDirect
+ /// CHECK-START: int Main.inlineInLoop() inliner (before)
+ /// CHECK-DAG: InvokeStaticOrDirect
- // CHECK-START: int Main.inlineInLoop() inliner (after)
- // CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-START: int Main.inlineInLoop() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
public static int inlineInLoop() {
int result = 0;
@@ -49,11 +49,11 @@
return result;
}
- // CHECK-START: int Main.inlineInLoopHeader() inliner (before)
- // CHECK-DAG: InvokeStaticOrDirect
+ /// CHECK-START: int Main.inlineInLoopHeader() inliner (before)
+ /// CHECK-DAG: InvokeStaticOrDirect
- // CHECK-START: int Main.inlineInLoopHeader() inliner (after)
- // CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-START: int Main.inlineInLoopHeader() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
public static int inlineInLoopHeader() {
int result = 0;
diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
index f90d85d..ed6fc1e 100644
--- a/test/449-checker-bce/src/Main.java
+++ b/test/449-checker-bce/src/Main.java
@@ -16,21 +16,21 @@
public class Main {
- // CHECK-START: int Main.sieve(int) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: int Main.sieve(int) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
- // CHECK-START: int Main.sieve(int) BCE (after)
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: int Main.sieve(int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
static int sieve(int size) {
int primeCount = 0;
@@ -47,25 +47,25 @@
}
- // CHECK-START: void Main.narrow(int[], int) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.narrow(int[], int) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
- // CHECK-START: void Main.narrow(int[], int) BCE (after)
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.narrow(int[], int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
static void narrow(int[] array, int offset) {
if (offset < 0) {
@@ -108,18 +108,18 @@
}
- // CHECK-START: void Main.constantIndexing1(int[]) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.constantIndexing1(int[]) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
- // CHECK-START: void Main.constantIndexing1(int[]) BCE (after)
- // CHECK-NOT: Deoptimize
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.constantIndexing1(int[]) BCE (after)
+ /// CHECK-NOT: Deoptimize
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
static void constantIndexing1(int[] array) {
array[5] = 1;
@@ -127,29 +127,29 @@
}
- // CHECK-START: void Main.constantIndexing2(int[]) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.constantIndexing2(int[]) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
- // CHECK-START: void Main.constantIndexing2(int[]) BCE (after)
- // CHECK: LessThanOrEqual
- // CHECK: Deoptimize
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.constantIndexing2(int[]) BCE (after)
+ /// CHECK: LessThanOrEqual
+ /// CHECK: Deoptimize
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
static void constantIndexing2(int[] array) {
array[1] = 1;
@@ -160,45 +160,45 @@
}
- // CHECK-START: int[] Main.constantIndexing3(int[], int[], boolean) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: int[] Main.constantIndexing3(int[], int[], boolean) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
- // CHECK-START: int[] Main.constantIndexing3(int[], int[], boolean) BCE (after)
- // CHECK: LessThanOrEqual
- // CHECK: Deoptimize
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: LessThanOrEqual
- // CHECK: Deoptimize
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: int[] Main.constantIndexing3(int[], int[], boolean) BCE (after)
+ /// CHECK: LessThanOrEqual
+ /// CHECK: Deoptimize
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: LessThanOrEqual
+ /// CHECK: Deoptimize
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
static int[] constantIndexing3(int[] array1, int[] array2, boolean copy) {
if (!copy) {
@@ -212,14 +212,14 @@
}
- // CHECK-START: void Main.constantIndexing4(int[]) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.constantIndexing4(int[]) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
- // CHECK-START: void Main.constantIndexing4(int[]) BCE (after)
- // CHECK-NOT: LessThanOrEqual
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.constantIndexing4(int[]) BCE (after)
+ /// CHECK-NOT: LessThanOrEqual
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
// There is only one array access. It's not beneficial
// to create a compare with deoptimization instruction.
@@ -228,18 +228,18 @@
}
- // CHECK-START: void Main.constantIndexing5(int[]) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.constantIndexing5(int[]) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
- // CHECK-START: void Main.constantIndexing5(int[]) BCE (after)
- // CHECK-NOT: Deoptimize
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.constantIndexing5(int[]) BCE (after)
+ /// CHECK-NOT: Deoptimize
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
static void constantIndexing5(int[] array) {
// We don't apply the deoptimization for very large constant index
@@ -249,37 +249,37 @@
array[Integer.MAX_VALUE - 998] = 1;
}
- // CHECK-START: void Main.loopPattern1(int[]) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.loopPattern1(int[]) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
- // CHECK-START: void Main.loopPattern1(int[]) BCE (after)
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.loopPattern1(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
static void loopPattern1(int[] array) {
for (int i = 0; i < array.length; i++) {
@@ -316,33 +316,33 @@
}
- // CHECK-START: void Main.loopPattern2(int[]) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.loopPattern2(int[]) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
- // CHECK-START: void Main.loopPattern2(int[]) BCE (after)
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.loopPattern2(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
static void loopPattern2(int[] array) {
for (int i = array.length - 1; i >= 0; i--) {
@@ -372,13 +372,13 @@
}
- // CHECK-START: void Main.loopPattern3(int[]) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.loopPattern3(int[]) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
- // CHECK-START: void Main.loopPattern3(int[]) BCE (after)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.loopPattern3(int[]) BCE (after)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
static void loopPattern3(int[] array) {
java.util.Random random = new java.util.Random();
@@ -393,29 +393,29 @@
}
- // CHECK-START: void Main.constantNewArray() BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.constantNewArray() BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
- // CHECK-START: void Main.constantNewArray() BCE (after)
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.constantNewArray() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
static void constantNewArray() {
int[] array = new int[10];
@@ -437,13 +437,13 @@
return 1;
}
- // CHECK-START: void Main.circularBufferProducer() BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.circularBufferProducer() BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
- // CHECK-START: void Main.circularBufferProducer() BCE (after)
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.circularBufferProducer() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
static void circularBufferProducer() {
byte[] array = new byte[4096];
@@ -455,17 +455,17 @@
}
- // CHECK-START: void Main.pyramid1(int[]) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.pyramid1(int[]) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
- // CHECK-START: void Main.pyramid1(int[]) BCE (after)
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.pyramid1(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
// Set array to something like {0, 1, 2, 3, 2, 1, 0}.
static void pyramid1(int[] array) {
@@ -476,17 +476,17 @@
}
- // CHECK-START: void Main.pyramid2(int[]) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.pyramid2(int[]) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
- // CHECK-START: void Main.pyramid2(int[]) BCE (after)
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.pyramid2(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
// Set array to something like {0, 1, 2, 3, 2, 1, 0}.
static void pyramid2(int[] array) {
@@ -497,17 +497,17 @@
}
- // CHECK-START: void Main.pyramid3(int[]) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.pyramid3(int[]) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
- // CHECK-START: void Main.pyramid3(int[]) BCE (after)
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.pyramid3(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
// Set array to something like {0, 1, 2, 3, 2, 1, 0}.
static void pyramid3(int[] array) {
@@ -518,17 +518,17 @@
}
- // CHECK-START: boolean Main.isPyramid(int[]) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
+ /// CHECK-START: boolean Main.isPyramid(int[]) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
- // CHECK-START: boolean Main.isPyramid(int[]) BCE (after)
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
+ /// CHECK-START: boolean Main.isPyramid(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
static boolean isPyramid(int[] array) {
int i = 0;
@@ -546,43 +546,43 @@
}
- // CHECK-START: void Main.bubbleSort(int[]) GVN (before)
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.bubbleSort(int[]) GVN (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
- // CHECK-START: void Main.bubbleSort(int[]) GVN (after)
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK-NOT: ArrayGet
- // CHECK-NOT: ArrayGet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.bubbleSort(int[]) GVN (after)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK-NOT: ArrayGet
+ /// CHECK-NOT: ArrayGet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
- // CHECK-START: void Main.bubbleSort(int[]) BCE (after)
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
- // CHECK-NOT: ArrayGet
- // CHECK-NOT: ArrayGet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.bubbleSort(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK-NOT: ArrayGet
+ /// CHECK-NOT: ArrayGet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
static void bubbleSort(int[] array) {
for (int i = 0; i < array.length - 1; i++) {
@@ -610,22 +610,28 @@
int sum;
- // CHECK-START: void Main.foo1(int[], int, int) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
+ /// CHECK-START: void Main.foo1(int[], int, int) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
- // CHECK-START: void Main.foo1(int[], int, int) BCE (after)
- // CHECK: Deoptimize
- // CHECK: Deoptimize
- // CHECK: Deoptimize
- // CHECK-NOT: Deoptimize
- // CHECK: Phi
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
+ /// CHECK-START: void Main.foo1(int[], int, int) BCE (after)
+ /// CHECK: Phi
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ // Added blocks for deoptimization.
+ /// CHECK: If
+ /// CHECK: Goto
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK-NOT: Deoptimize
+ /// CHECK: Goto
+ /// CHECK: Phi
+ /// CHECK: Goto
void foo1(int[] array, int start, int end) {
// Three HDeoptimize will be added. One for
@@ -639,22 +645,28 @@
}
- // CHECK-START: void Main.foo2(int[], int, int) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
+ /// CHECK-START: void Main.foo2(int[], int, int) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
- // CHECK-START: void Main.foo2(int[], int, int) BCE (after)
- // CHECK: Deoptimize
- // CHECK: Deoptimize
- // CHECK: Deoptimize
- // CHECK-NOT: Deoptimize
- // CHECK: Phi
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
+ /// CHECK-START: void Main.foo2(int[], int, int) BCE (after)
+ /// CHECK: Phi
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ // Added blocks for deoptimization.
+ /// CHECK: If
+ /// CHECK: Goto
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK-NOT: Deoptimize
+ /// CHECK: Goto
+ /// CHECK: Phi
+ /// CHECK: Goto
void foo2(int[] array, int start, int end) {
// Three HDeoptimize will be added. One for
@@ -668,21 +680,27 @@
}
- // CHECK-START: void Main.foo3(int[], int) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
+ /// CHECK-START: void Main.foo3(int[], int) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
- // CHECK-START: void Main.foo3(int[], int) BCE (after)
- // CHECK: Deoptimize
- // CHECK: Deoptimize
- // CHECK-NOT: Deoptimize
- // CHECK: Phi
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
+ /// CHECK-START: void Main.foo3(int[], int) BCE (after)
+ /// CHECK: Phi
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ // Added blocks for deoptimization.
+ /// CHECK: If
+ /// CHECK: Goto
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK-NOT: Deoptimize
+ /// CHECK: Goto
+ /// CHECK: Phi
+ /// CHECK: Goto
void foo3(int[] array, int end) {
// Two HDeoptimize will be added. One for end < array.length,
@@ -694,21 +712,28 @@
}
}
- // CHECK-START: void Main.foo4(int[], int) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
- // CHECK-START: void Main.foo4(int[], int) BCE (after)
- // CHECK: Deoptimize
- // CHECK: Deoptimize
- // CHECK-NOT: Deoptimize
- // CHECK: Phi
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
+ /// CHECK-START: void Main.foo4(int[], int) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+
+ /// CHECK-START: void Main.foo4(int[], int) BCE (after)
+ /// CHECK: Phi
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ // Added blocks for deoptimization.
+ /// CHECK: If
+ /// CHECK: Goto
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK-NOT: Deoptimize
+ /// CHECK: Goto
+ /// CHECK: Phi
+ /// CHECK: Goto
void foo4(int[] array, int end) {
// Two HDeoptimize will be added. One for end <= array.length,
@@ -721,28 +746,35 @@
}
- // CHECK-START: void Main.foo5(int[], int) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
+ /// CHECK-START: void Main.foo5(int[], int) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
- // CHECK-START: void Main.foo5(int[], int) BCE (after)
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
- // CHECK: Deoptimize
- // CHECK-NOT: Deoptimize
- // CHECK: Phi
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
+ /// CHECK-START: void Main.foo5(int[], int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: Phi
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ // Added blocks for deoptimization.
+ /// CHECK: If
+ /// CHECK: Goto
+ /// CHECK: Deoptimize
+ /// CHECK-NOT: Deoptimize
+ /// CHECK: Goto
+ // array.length is defined before the loop header so no phi is needed.
+ /// CHECK-NOT: Phi
+ /// CHECK: Goto
void foo5(int[] array, int end) {
// Bounds check in this loop can be eliminated without deoptimization.
@@ -759,38 +791,45 @@
}
- // CHECK-START: void Main.foo6(int[], int, int) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.foo6(int[], int, int) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
- // CHECK-START: void Main.foo6(int[], int, int) BCE (after)
- // CHECK: Deoptimize
- // CHECK: Deoptimize
- // CHECK: Deoptimize
- // CHECK-NOT: Deoptimize
- // CHECK: Phi
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.foo6(int[], int, int) BCE (after)
+ /// CHECK: Phi
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ // Added blocks for deoptimization.
+ /// CHECK: If
+ /// CHECK: Goto
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK-NOT: Deoptimize
+ /// CHECK: Goto
+ /// CHECK: Phi
+ /// CHECK: Goto
+ /// CHECK-NOT: Deoptimize
void foo6(int[] array, int start, int end) {
// Three HDeoptimize will be added. One for
@@ -803,22 +842,28 @@
}
- // CHECK-START: void Main.foo7(int[], int, int, boolean) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
+ /// CHECK-START: void Main.foo7(int[], int, int, boolean) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
- // CHECK-START: void Main.foo7(int[], int, int, boolean) BCE (after)
- // CHECK: Deoptimize
- // CHECK: Deoptimize
- // CHECK: Deoptimize
- // CHECK-NOT: Deoptimize
- // CHECK: Phi
- // CHECK: BoundsCheck
- // CHECK: ArrayGet
- // CHECK-NOT: BoundsCheck
- // CHECK: ArrayGet
+ /// CHECK-START: void Main.foo7(int[], int, int, boolean) BCE (after)
+ /// CHECK: Phi
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ // Added blocks for deoptimization.
+ /// CHECK: If
+ /// CHECK: Goto
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK-NOT: Deoptimize
+ /// CHECK: Goto
+ /// CHECK: Phi
+ /// CHECK: Goto
void foo7(int[] array, int start, int end, boolean lowEnd) {
// Three HDeoptimize will be added. One for
@@ -837,14 +882,81 @@
}
- // CHECK-START: void Main.partialLooping(int[], int, int) BCE (before)
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.foo8(int[][], int, int) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
- // CHECK-START: void Main.partialLooping(int[], int, int) BCE (after)
- // CHECK-NOT: Deoptimize
- // CHECK: BoundsCheck
- // CHECK: ArraySet
+ /// CHECK-START: void Main.foo8(int[][], int, int) BCE (after)
+ /// CHECK: Phi
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+ /// CHECK: Phi
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ // Added blocks for deoptimization.
+ /// CHECK: If
+ /// CHECK: Goto
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK-NOT: Deoptimize
+ /// CHECK: Goto
+ /// CHECK: Phi
+ /// CHECK: Goto
+
+ void foo8(int[][] matrix, int start, int end) {
+ // Three HDeoptimize will be added for the outer loop.
+ // start >= 0, end <= matrix.length, and null check on matrix.
+ // Three HDeoptimize will be added for the inner loop
+ // start >= 0 (TODO: this may be optimized away),
+ // end <= row.length, and null check on row.
+ for (int i = start; i < end; i++) {
+ int[] row = matrix[i];
+ for (int j = start; j < end; j++) {
+ row[j] = 1;
+ }
+ }
+ }
+
+
+ /// CHECK-START: void Main.foo9(int[]) BCE (before)
+ /// CHECK: NullCheck
+ /// CHECK: BoundsCheck
+ /// CHECK: ArrayGet
+
+ /// CHECK-START: void Main.foo9(int[]) BCE (after)
+ // The loop is guaranteed to be entered. No need to transform the
+ // loop for loop body entry test.
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK-NOT: Deoptimize
+ /// CHECK: Phi
+ /// CHECK-NOT: NullCheck
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArrayGet
+
+ void foo9(int[] array) {
+ // Two HDeoptimize will be added. One for
+ // 10 <= array.length, and one for null check on array.
+ for (int i = 0 ; i < 10; i++) {
+ sum += array[i];
+ }
+ }
+
+
+ /// CHECK-START: void Main.partialLooping(int[], int, int) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+
+ /// CHECK-START: void Main.partialLooping(int[], int, int) BCE (after)
+ /// CHECK-NOT: Deoptimize
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
void partialLooping(int[] array, int start, int end) {
// This loop doesn't cover the full range of [start, end) so
@@ -951,6 +1063,13 @@
main.foo6(new int[10], 2, 7);
main = new Main();
+ int[] array9 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+ main.foo9(array9);
+ if (main.sum != 45) {
+ System.out.println("foo9 failed!");
+ }
+
+ main = new Main();
int[] array = new int[4];
main.partialLooping(new int[3], 0, 4);
if ((array[0] != 1) && (array[1] != 1) &&
@@ -983,8 +1102,8 @@
}
// Make sure this method is compiled with optimizing.
- // CHECK-START: void Main.main(java.lang.String[]) register (after)
- // CHECK: ParallelMove
+ /// CHECK-START: void Main.main(java.lang.String[]) register (after)
+ /// CHECK: ParallelMove
public static void main(String[] args) {
sieve(20);
diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java
index 640aeb8..4056275 100644
--- a/test/450-checker-types/src/Main.java
+++ b/test/450-checker-types/src/Main.java
@@ -16,25 +16,25 @@
interface Interface {
- void f();
+ void $noinline$f();
}
class Super implements Interface {
- public void f() {
+ public void $noinline$f() {
throw new RuntimeException();
}
}
class SubclassA extends Super {
- public void f() {
+ public void $noinline$f() {
throw new RuntimeException();
}
- public String h() {
+ public String $noinline$h() {
throw new RuntimeException();
}
- void g() {
+ void $noinline$g() {
throw new RuntimeException();
}
}
@@ -43,61 +43,61 @@
}
class SubclassB extends Super {
- public void f() {
+ public void $noinline$f() {
throw new RuntimeException();
}
- void g() {
+ void $noinline$g() {
throw new RuntimeException();
}
}
public class Main {
- // CHECK-START: void Main.testSimpleRemove() instruction_simplifier_after_types (before)
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testSimpleRemove() instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
- // CHECK-START: void Main.testSimpleRemove() instruction_simplifier_after_types (after)
- // CHECK-NOT: CheckCast
+ /// CHECK-START: void Main.testSimpleRemove() instruction_simplifier_after_types (after)
+ /// CHECK-NOT: CheckCast
public void testSimpleRemove() {
Super s = new SubclassA();
- ((SubclassA)s).g();
+ ((SubclassA)s).$noinline$g();
}
- // CHECK-START: void Main.testSimpleKeep(Super) instruction_simplifier_after_types (before)
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testSimpleKeep(Super) instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
- // CHECK-START: void Main.testSimpleKeep(Super) instruction_simplifier_after_types (after)
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testSimpleKeep(Super) instruction_simplifier_after_types (after)
+ /// CHECK: CheckCast
public void testSimpleKeep(Super s) {
- ((SubclassA)s).f();
+ ((SubclassA)s).$noinline$f();
}
- // CHECK-START: java.lang.String Main.testClassRemove() instruction_simplifier_after_types (before)
- // CHECK: CheckCast
+ /// CHECK-START: java.lang.String Main.testClassRemove() instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
- // CHECK-START: java.lang.String Main.testClassRemove() instruction_simplifier_after_types (after)
- // CHECK-NOT: CheckCast
+ /// CHECK-START: java.lang.String Main.testClassRemove() instruction_simplifier_after_types (after)
+ /// CHECK-NOT: CheckCast
public String testClassRemove() {
Object s = SubclassA.class;
return ((Class)s).getName();
}
- // CHECK-START: java.lang.String Main.testClassKeep() instruction_simplifier_after_types (before)
- // CHECK: CheckCast
+ /// CHECK-START: java.lang.String Main.testClassKeep() instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
- // CHECK-START: java.lang.String Main.testClassKeep() instruction_simplifier_after_types (after)
- // CHECK: CheckCast
+ /// CHECK-START: java.lang.String Main.testClassKeep() instruction_simplifier_after_types (after)
+ /// CHECK: CheckCast
public String testClassKeep() {
Object s = SubclassA.class;
- return ((SubclassA)s).h();
+ return ((SubclassA)s).$noinline$h();
}
- // CHECK-START: void Main.testIfRemove(int) instruction_simplifier_after_types (before)
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testIfRemove(int) instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
- // CHECK-START: void Main.testIfRemove(int) instruction_simplifier_after_types (after)
- // CHECK-NOT: CheckCast
+ /// CHECK-START: void Main.testIfRemove(int) instruction_simplifier_after_types (after)
+ /// CHECK-NOT: CheckCast
public void testIfRemove(int x) {
Super s;
if (x % 2 == 0) {
@@ -105,14 +105,14 @@
} else {
s = new SubclassC();
}
- ((SubclassA)s).g();
+ ((SubclassA)s).$noinline$g();
}
- // CHECK-START: void Main.testIfKeep(int) instruction_simplifier_after_types (before)
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testIfKeep(int) instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
- // CHECK-START: void Main.testIfKeep(int) instruction_simplifier_after_types (after)
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testIfKeep(int) instruction_simplifier_after_types (after)
+ /// CHECK: CheckCast
public void testIfKeep(int x) {
Super s;
if (x % 2 == 0) {
@@ -120,14 +120,14 @@
} else {
s = new SubclassB();
}
- ((SubclassA)s).g();
+ ((SubclassA)s).$noinline$g();
}
- // CHECK-START: void Main.testForRemove(int) instruction_simplifier_after_types (before)
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testForRemove(int) instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
- // CHECK-START: void Main.testForRemove(int) instruction_simplifier_after_types (after)
- // CHECK-NOT: CheckCast
+ /// CHECK-START: void Main.testForRemove(int) instruction_simplifier_after_types (after)
+ /// CHECK-NOT: CheckCast
public void testForRemove(int x) {
Super s = new SubclassA();
for (int i = 0 ; i < x; i++) {
@@ -135,14 +135,14 @@
s = new SubclassC();
}
}
- ((SubclassA)s).g();
+ ((SubclassA)s).$noinline$g();
}
- // CHECK-START: void Main.testForKeep(int) instruction_simplifier_after_types (before)
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testForKeep(int) instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
- // CHECK-START: void Main.testForKeep(int) instruction_simplifier_after_types (after)
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testForKeep(int) instruction_simplifier_after_types (after)
+ /// CHECK: CheckCast
public void testForKeep(int x) {
Super s = new SubclassA();
for (int i = 0 ; i < x; i++) {
@@ -150,14 +150,14 @@
s = new SubclassC();
}
}
- ((SubclassC)s).g();
+ ((SubclassC)s).$noinline$g();
}
- // CHECK-START: void Main.testPhiFromCall(int) instruction_simplifier_after_types (before)
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testPhiFromCall(int) instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
- // CHECK-START: void Main.testPhiFromCall(int) instruction_simplifier_after_types (after)
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testPhiFromCall(int) instruction_simplifier_after_types (after)
+ /// CHECK: CheckCast
public void testPhiFromCall(int i) {
Object x;
if (i % 2 == 0) {
@@ -165,61 +165,61 @@
} else {
x = newObject(); // this one will have an unknown type.
}
- ((SubclassC)x).g();
+ ((SubclassC)x).$noinline$g();
}
- // CHECK-START: void Main.testInstanceOf(java.lang.Object) instruction_simplifier_after_types (before)
- // CHECK: CheckCast
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testInstanceOf(java.lang.Object) instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
+ /// CHECK: CheckCast
- // CHECK-START: void Main.testInstanceOf(java.lang.Object) instruction_simplifier_after_types (after)
- // CHECK-NOT: CheckCast
+ /// CHECK-START: void Main.testInstanceOf(java.lang.Object) instruction_simplifier_after_types (after)
+ /// CHECK-NOT: CheckCast
public void testInstanceOf(Object o) {
if (o instanceof SubclassC) {
- ((SubclassC)o).g();
+ ((SubclassC)o).$noinline$g();
}
if (o instanceof SubclassB) {
- ((SubclassB)o).g();
+ ((SubclassB)o).$noinline$g();
}
}
- // CHECK-START: void Main.testInstanceOfKeep(java.lang.Object) instruction_simplifier_after_types (before)
- // CHECK: CheckCast
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testInstanceOfKeep(java.lang.Object) instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
+ /// CHECK: CheckCast
- // CHECK-START: void Main.testInstanceOfKeep(java.lang.Object) instruction_simplifier_after_types (after)
- // CHECK: CheckCast
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testInstanceOfKeep(java.lang.Object) instruction_simplifier_after_types (after)
+ /// CHECK: CheckCast
+ /// CHECK: CheckCast
public void testInstanceOfKeep(Object o) {
if (o instanceof SubclassC) {
- ((SubclassB)o).g();
+ ((SubclassB)o).$noinline$g();
}
if (o instanceof SubclassB) {
- ((SubclassA)o).g();
+ ((SubclassA)o).$noinline$g();
}
}
- // CHECK-START: void Main.testInstanceOfNested(java.lang.Object) instruction_simplifier_after_types (before)
- // CHECK: CheckCast
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testInstanceOfNested(java.lang.Object) instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
+ /// CHECK: CheckCast
- // CHECK-START: void Main.testInstanceOfNested(java.lang.Object) instruction_simplifier_after_types (after)
- // CHECK-NOT: CheckCast
+ /// CHECK-START: void Main.testInstanceOfNested(java.lang.Object) instruction_simplifier_after_types (after)
+ /// CHECK-NOT: CheckCast
public void testInstanceOfNested(Object o) {
if (o instanceof SubclassC) {
if (o instanceof SubclassB) {
- ((SubclassB)o).g();
+ ((SubclassB)o).$noinline$g();
} else {
- ((SubclassC)o).g();
+ ((SubclassC)o).$noinline$g();
}
}
}
- // CHECK-START: void Main.testInstanceOfWithPhi(int) instruction_simplifier_after_types (before)
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testInstanceOfWithPhi(int) instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
- // CHECK-START: void Main.testInstanceOfWithPhi(int) instruction_simplifier_after_types (after)
- // CHECK-NOT: CheckCast
+ /// CHECK-START: void Main.testInstanceOfWithPhi(int) instruction_simplifier_after_types (after)
+ /// CHECK-NOT: CheckCast
public void testInstanceOfWithPhi(int i) {
Object o;
if (i == 0) {
@@ -229,15 +229,15 @@
}
if (o instanceof SubclassB) {
- ((SubclassB)o).g();
+ ((SubclassB)o).$noinline$g();
}
}
- // CHECK-START: void Main.testInstanceOfInFor(int) instruction_simplifier_after_types (before)
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testInstanceOfInFor(int) instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
- // CHECK-START: void Main.testInstanceOfInFor(int) instruction_simplifier_after_types (after)
- // CHECK-NOT: CheckCast
+ /// CHECK-START: void Main.testInstanceOfInFor(int) instruction_simplifier_after_types (after)
+ /// CHECK-NOT: CheckCast
public void testInstanceOfInFor(int n) {
Object o = new SubclassA();
for (int i = 0; i < n; i++) {
@@ -245,28 +245,28 @@
o = new SubclassB();
}
if (o instanceof SubclassB) {
- ((SubclassB)o).g();
+ ((SubclassB)o).$noinline$g();
}
}
}
- // CHECK-START: void Main.testInstanceOfSubclass() instruction_simplifier_after_types (before)
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testInstanceOfSubclass() instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
- // CHECK-START: void Main.testInstanceOfSubclass() instruction_simplifier_after_types (after)
- // CHECK-NOT: CheckCast
+ /// CHECK-START: void Main.testInstanceOfSubclass() instruction_simplifier_after_types (after)
+ /// CHECK-NOT: CheckCast
public void testInstanceOfSubclass() {
Object o = new SubclassA();
if (o instanceof Super) {
- ((SubclassA)o).g();
+ ((SubclassA)o).$noinline$g();
}
}
- // CHECK-START: void Main.testInstanceOfWithPhiSubclass(int) instruction_simplifier_after_types (before)
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testInstanceOfWithPhiSubclass(int) instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
- // CHECK-START: void Main.testInstanceOfWithPhiSubclass(int) instruction_simplifier_after_types (after)
- // CHECK-NOT: CheckCast
+ /// CHECK-START: void Main.testInstanceOfWithPhiSubclass(int) instruction_simplifier_after_types (after)
+ /// CHECK-NOT: CheckCast
public void testInstanceOfWithPhiSubclass(int i) {
Object o;
if (i == 0) {
@@ -276,15 +276,15 @@
}
if (o instanceof Super) {
- ((SubclassA)o).g();
+ ((SubclassA)o).$noinline$g();
}
}
- // CHECK-START: void Main.testInstanceOfWithPhiTop(int) instruction_simplifier_after_types (before)
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testInstanceOfWithPhiTop(int) instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
- // CHECK-START: void Main.testInstanceOfWithPhiTop(int) instruction_simplifier_after_types (after)
- // CHECK-NOT: CheckCast
+ /// CHECK-START: void Main.testInstanceOfWithPhiTop(int) instruction_simplifier_after_types (after)
+ /// CHECK-NOT: CheckCast
public void testInstanceOfWithPhiTop(int i) {
Object o;
if (i == 0) {
@@ -294,20 +294,20 @@
}
if (o instanceof Super) {
- ((Super)o).f();
+ ((Super)o).$noinline$f();
}
}
- // CHECK-START: void Main.testInstanceOfSubclassInFor(int) instruction_simplifier_after_types (before)
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testInstanceOfSubclassInFor(int) instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
- // CHECK-START: void Main.testInstanceOfSubclassInFor(int) instruction_simplifier_after_types (after)
- // CHECK-NOT: CheckCast
+ /// CHECK-START: void Main.testInstanceOfSubclassInFor(int) instruction_simplifier_after_types (after)
+ /// CHECK-NOT: CheckCast
public void testInstanceOfSubclassInFor(int n) {
Object o = new SubclassA();
for (int i = 0; i < n; i++) {
if (o instanceof Super) {
- ((SubclassA)o).g();
+ ((SubclassA)o).$noinline$g();
}
if (i / 2 == 0) {
o = new SubclassC();
@@ -315,16 +315,16 @@
}
}
- // CHECK-START: void Main.testInstanceOfTopInFor(int) instruction_simplifier_after_types (before)
- // CHECK: CheckCast
+ /// CHECK-START: void Main.testInstanceOfTopInFor(int) instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
- // CHECK-START: void Main.testInstanceOfTopInFor(int) instruction_simplifier_after_types (after)
- // CHECK-NOT: CheckCast
+ /// CHECK-START: void Main.testInstanceOfTopInFor(int) instruction_simplifier_after_types (after)
+ /// CHECK-NOT: CheckCast
public void testInstanceOfTopInFor(int n) {
Object o = new SubclassA();
for (int i = 0; i < n; i++) {
if (o instanceof Super) {
- ((Super)o).f();
+ ((Super)o).$noinline$f();
}
if (i / 2 == 0) {
o = new Object();
@@ -340,6 +340,30 @@
}
}
+ public SubclassA a = new SubclassA();
+ public static SubclassA b = new SubclassA();
+
+ /// CHECK-START: void Main.testInstanceFieldGetSimpleRemove() instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
+
+ /// CHECK-START: void Main.testInstanceFieldGetSimpleRemove() instruction_simplifier_after_types (after)
+ /// CHECK-NOT: CheckCast
+ public void testInstanceFieldGetSimpleRemove() {
+ Main m = new Main();
+ Super a = m.a;
+ ((SubclassA)a).$noinline$g();
+ }
+
+ /// CHECK-START: void Main.testStaticFieldGetSimpleRemove() instruction_simplifier_after_types (before)
+ /// CHECK: CheckCast
+
+ /// CHECK-START: void Main.testStaticFieldGetSimpleRemove() instruction_simplifier_after_types (after)
+ /// CHECK-NOT: CheckCast
+ public void testStaticFieldGetSimpleRemove() {
+ Super b = Main.b;
+ ((SubclassA)b).$noinline$g();
+ }
+
public static void main(String[] args) {
}
}
diff --git a/test/451-spill-splot/src/Main.java b/test/451-spill-splot/src/Main.java
index f631ebd..b2f39f3 100644
--- a/test/451-spill-splot/src/Main.java
+++ b/test/451-spill-splot/src/Main.java
@@ -48,37 +48,37 @@
for (int count = 0; count < 2; count++) {
System.out.println(aa + bb + cc + dd + ee + ff + gg + hh + ii + jj + kk + ll + mm + nn);
System.out.println(a + b + c + d + e + f + g + h + i + j);
- a = computeDouble();
- b = computeDouble();
- c = computeDouble();
- d = computeDouble();
- e = computeDouble();
- f = computeDouble();
- g = computeDouble();
- h = computeDouble();
- i = computeDouble();
- j = computeDouble();
+ a = $noinline$computeDouble();
+ b = $noinline$computeDouble();
+ c = $noinline$computeDouble();
+ d = $noinline$computeDouble();
+ e = $noinline$computeDouble();
+ f = $noinline$computeDouble();
+ g = $noinline$computeDouble();
+ h = $noinline$computeDouble();
+ i = $noinline$computeDouble();
+ j = $noinline$computeDouble();
System.out.println(a + b + c + d + e + f + g + h + i + j);
- aa = computeFloat();
- bb = computeFloat();
- cc = computeFloat();
- dd = computeFloat();
- ee = computeFloat();
- ff = computeFloat();
- gg = computeFloat();
- hh = computeFloat();
- ii = computeFloat();
- jj = computeFloat();
- kk = computeFloat();
- ll = computeFloat();
- mm = computeFloat();
- nn = computeFloat();
+ aa = $noinline$computeFloat();
+ bb = $noinline$computeFloat();
+ cc = $noinline$computeFloat();
+ dd = $noinline$computeFloat();
+ ee = $noinline$computeFloat();
+ ff = $noinline$computeFloat();
+ gg = $noinline$computeFloat();
+ hh = $noinline$computeFloat();
+ ii = $noinline$computeFloat();
+ jj = $noinline$computeFloat();
+ kk = $noinline$computeFloat();
+ ll = $noinline$computeFloat();
+ mm = $noinline$computeFloat();
+ nn = $noinline$computeFloat();
}
}
static boolean doThrow = false;
- public static double computeDouble() {
+ public static double $noinline$computeDouble() {
if (doThrow) {
// Try defeating inlining.
throw new Error();
@@ -86,7 +86,7 @@
return 2.0;
}
- public static float computeFloat() {
+ public static float $noinline$computeFloat() {
if (doThrow) {
// Try defeating inlining.
throw new Error();
diff --git a/test/455-checker-gvn/src/Main.java b/test/455-checker-gvn/src/Main.java
index e94fc46..9824f27 100644
--- a/test/455-checker-gvn/src/Main.java
+++ b/test/455-checker-gvn/src/Main.java
@@ -19,15 +19,15 @@
System.out.println(foo(3, 4));
}
- // CHECK-START: int Main.foo(int, int) GVN (before)
- // CHECK: Add
- // CHECK: Add
- // CHECK: Add
+ /// CHECK-START: int Main.foo(int, int) GVN (before)
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK: Add
- // CHECK-START: int Main.foo(int, int) GVN (after)
- // CHECK: Add
- // CHECK: Add
- // CHECK-NOT: Add
+ /// CHECK-START: int Main.foo(int, int) GVN (after)
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK-NOT: Add
public static int foo(int x, int y) {
int sum1 = x + y;
diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java
index efb7b83..ef18f64 100644
--- a/test/458-checker-instruction-simplification/src/Main.java
+++ b/test/458-checker-instruction-simplification/src/Main.java
@@ -50,296 +50,296 @@
* Tiny programs exercising optimizations of arithmetic identities.
*/
- // 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-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-START: long Main.Add0(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ /// CHECK-START: long Main.Add0(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
- // CHECK-START: long Main.Add0(long) instruction_simplifier (after)
- // CHECK-NOT: Add
+ /// CHECK-START: long Main.Add0(long) instruction_simplifier (after)
+ /// CHECK-NOT: Add
public static long Add0(long arg) {
return 0 + arg;
}
- // 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-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-START: int Main.AndAllOnes(int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ /// CHECK-START: int Main.AndAllOnes(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
- // CHECK-START: int Main.AndAllOnes(int) instruction_simplifier (after)
- // CHECK-NOT: And
+ /// CHECK-START: int Main.AndAllOnes(int) instruction_simplifier (after)
+ /// CHECK-NOT: And
public static int AndAllOnes(int arg) {
return arg & -1;
}
- // 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-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-START: long Main.Div1(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ /// CHECK-START: long Main.Div1(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
- // CHECK-START: long Main.Div1(long) instruction_simplifier (after)
- // CHECK-NOT: Div
+ /// CHECK-START: long Main.Div1(long) instruction_simplifier (after)
+ /// CHECK-NOT: Div
public static long Div1(long arg) {
return arg / 1;
}
- // 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-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-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-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-START: int Main.DivN1(int) instruction_simplifier (after)
- // CHECK-NOT: Div
+ /// CHECK-START: int Main.DivN1(int) instruction_simplifier (after)
+ /// CHECK-NOT: Div
public static int DivN1(int arg) {
return arg / -1;
}
- // 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-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-START: long Main.Mul1(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ /// CHECK-START: long Main.Mul1(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
- // CHECK-START: long Main.Mul1(long) instruction_simplifier (after)
- // CHECK-NOT: Mul
+ /// CHECK-START: long Main.Mul1(long) instruction_simplifier (after)
+ /// CHECK-NOT: Mul
public static long Mul1(long arg) {
return arg * 1;
}
- // 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-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-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-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-START: int Main.MulN1(int) instruction_simplifier (after)
- // CHECK-NOT: Mul
+ /// CHECK-START: int Main.MulN1(int) instruction_simplifier (after)
+ /// CHECK-NOT: Mul
public static int MulN1(int arg) {
return arg * -1;
}
- // 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-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-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-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-START: long Main.MulPowerOfTwo128(long) instruction_simplifier (after)
- // CHECK-NOT: Mul
+ /// CHECK-START: long Main.MulPowerOfTwo128(long) instruction_simplifier (after)
+ /// CHECK-NOT: Mul
public static long MulPowerOfTwo128(long arg) {
return arg * 128;
}
- // 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-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-START: int Main.Or0(int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ /// CHECK-START: int Main.Or0(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
- // CHECK-START: int Main.Or0(int) instruction_simplifier (after)
- // CHECK-NOT: Or
+ /// CHECK-START: int Main.Or0(int) instruction_simplifier (after)
+ /// CHECK-NOT: Or
public static int Or0(int arg) {
return arg | 0;
}
- // 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-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-START: long Main.OrSame(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ /// CHECK-START: long Main.OrSame(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
- // CHECK-START: long Main.OrSame(long) instruction_simplifier (after)
- // CHECK-NOT: Or
+ /// CHECK-START: long Main.OrSame(long) instruction_simplifier (after)
+ /// CHECK-NOT: Or
public static long OrSame(long arg) {
return arg | arg;
}
- // 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-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-START: int Main.Shl0(int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ /// CHECK-START: int Main.Shl0(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
- // CHECK-START: int Main.Shl0(int) instruction_simplifier (after)
- // CHECK-NOT: Shl
+ /// CHECK-START: int Main.Shl0(int) instruction_simplifier (after)
+ /// CHECK-NOT: Shl
public static int Shl0(int arg) {
return arg << 0;
}
- // 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-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-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-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-START: int Main.Shl1(int) instruction_simplifier (after)
- // CHECK-NOT: Shl
+ /// CHECK-START: int Main.Shl1(int) instruction_simplifier (after)
+ /// CHECK-NOT: Shl
public static int Shl1(int arg) {
return arg << 1;
}
- // 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-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-START: long Main.Shr0(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ /// CHECK-START: long Main.Shr0(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
- // CHECK-START: long Main.Shr0(long) instruction_simplifier (after)
- // CHECK-NOT: Shr
+ /// CHECK-START: long Main.Shr0(long) instruction_simplifier (after)
+ /// CHECK-NOT: Shr
public static long Shr0(long arg) {
return arg >> 0;
}
- // 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-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-START: long Main.Sub0(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ /// CHECK-START: long Main.Sub0(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
- // CHECK-START: long Main.Sub0(long) instruction_simplifier (after)
- // CHECK-NOT: Sub
+ /// CHECK-START: long Main.Sub0(long) instruction_simplifier (after)
+ /// CHECK-NOT: Sub
public static long Sub0(long arg) {
return arg - 0;
}
- // 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-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-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-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-START: int Main.SubAliasNeg(int) instruction_simplifier (after)
- // CHECK-NOT: Sub
+ /// CHECK-START: int Main.SubAliasNeg(int) instruction_simplifier (after)
+ /// CHECK-NOT: Sub
public static int SubAliasNeg(int arg) {
return 0 - arg;
}
- // 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-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-START: long Main.UShr0(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ /// CHECK-START: long Main.UShr0(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
- // CHECK-START: long Main.UShr0(long) instruction_simplifier (after)
- // CHECK-NOT: UShr
+ /// CHECK-START: long Main.UShr0(long) instruction_simplifier (after)
+ /// CHECK-NOT: UShr
public static long UShr0(long arg) {
return arg >>> 0;
}
- // 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-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-START: int Main.Xor0(int) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:i\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ /// CHECK-START: int Main.Xor0(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
- // CHECK-START: int Main.Xor0(int) instruction_simplifier (after)
- // CHECK-NOT: Xor
+ /// CHECK-START: int Main.Xor0(int) instruction_simplifier (after)
+ /// CHECK-NOT: Xor
public static int Xor0(int arg) {
return arg ^ 0;
}
- // 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-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-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-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-START: int Main.XorAllOnes(int) instruction_simplifier (after)
- // CHECK-NOT: Xor
+ /// CHECK-START: int Main.XorAllOnes(int) instruction_simplifier (after)
+ /// CHECK-NOT: Xor
public static int XorAllOnes(int arg) {
return arg ^ -1;
@@ -352,21 +352,21 @@
* `InstructionSimplifierVisitor::TryMoveNegOnInputsAfterBinop`.
*/
- // 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-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-START: int Main.AddNegs1(int, int) instruction_simplifier (after)
- // 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-START: int Main.AddNegs1(int, int) instruction_simplifier (after)
+ /// 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>>]
public static int AddNegs1(int arg1, int arg2) {
return -arg1 + -arg2;
@@ -383,35 +383,35 @@
* increasing the register pressure by creating or extending live ranges.
*/
- // 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-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-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-NOT: Neg
- // 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-NOT: Neg
+ /// 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-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>>]
public static int AddNegs2(int arg1, int arg2) {
int temp1 = -arg1;
@@ -427,30 +427,30 @@
* the loop.
*/
- // 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: Goto
- // -------------- Loop
- // CHECK: SuspendCheck
- // CHECK: [[Add:j\d+]] Add [ [[Neg1]] [[Neg2]] ]
- // CHECK: Goto
+ /// 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: Goto
+ // -------------- Loop
+ /// CHECK: SuspendCheck
+ /// 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: Goto
- // -------------- Loop
- // CHECK: SuspendCheck
- // CHECK: [[Add:j\d+]] Add [ [[Neg1]] [[Neg2]] ]
- // CHECK-NOT: Neg
- // 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: Goto
+ // -------------- Loop
+ /// CHECK: SuspendCheck
+ /// CHECK: <<Add:j\d+>> Add [<<Neg1>>,<<Neg2>>]
+ /// CHECK-NOT: Neg
+ /// CHECK: Goto
public static long AddNegs3(long arg1, long arg2) {
long res = 0;
@@ -468,22 +468,22 @@
* The transformation tested is implemented in `InstructionSimplifierVisitor::VisitAdd`.
*/
- // 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-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-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-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-START: long Main.AddNeg1(long, long) instruction_simplifier (after)
- // CHECK-NOT: Neg
- // CHECK-NOT: Add
+ /// CHECK-START: long Main.AddNeg1(long, long) instruction_simplifier (after)
+ /// CHECK-NOT: Neg
+ /// CHECK-NOT: Add
public static long AddNeg1(long arg1, long arg2) {
return -arg1 + arg2;
@@ -498,26 +498,26 @@
* increasing the register pressure by creating or extending live ranges.
*/
- // 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-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-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-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-START: long Main.AddNeg2(long, long) instruction_simplifier (after)
- // CHECK-NOT: Sub
+ /// CHECK-START: long Main.AddNeg2(long, long) instruction_simplifier (after)
+ /// CHECK-NOT: Sub
public static long AddNeg2(long arg1, long arg2) {
long temp = -arg2;
@@ -529,18 +529,18 @@
* The transformation tested is implemented in `InstructionSimplifierVisitor::VisitNeg`.
*/
- // 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-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-START: long Main.NegNeg1(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ /// CHECK-START: long Main.NegNeg1(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
- // CHECK-START: long Main.NegNeg1(long) instruction_simplifier (after)
- // CHECK-NOT: Neg
+ /// CHECK-START: long Main.NegNeg1(long) instruction_simplifier (after)
+ /// CHECK-NOT: Neg
public static long NegNeg1(long arg) {
return -(-arg);
@@ -553,27 +553,27 @@
* and in `InstructionSimplifierVisitor::VisitAdd`.
*/
- // 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-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-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-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-START: int Main.NegNeg2(int) instruction_simplifier (after)
- // CHECK-NOT: Neg
- // CHECK-NOT: Add
+ /// 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-NOT: Neg
- // CHECK-NOT: Add
- // CHECK: Return [ [[Const0]] ]
+ /// CHECK-START: int Main.NegNeg2(int) constant_folding_after_inlining (after)
+ /// CHECK: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-NOT: Neg
+ /// CHECK-NOT: Add
+ /// CHECK: Return [<<Const0>>]
public static int NegNeg2(int arg) {
int temp = -arg;
@@ -587,20 +587,20 @@
* and in `InstructionSimplifierVisitor::VisitSub`.
*/
- // 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-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-START: long Main.NegNeg3(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ /// CHECK-START: long Main.NegNeg3(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
- // CHECK-START: long Main.NegNeg3(long) instruction_simplifier (after)
- // CHECK-NOT: Neg
- // CHECK-NOT: Sub
+ /// CHECK-START: long Main.NegNeg3(long) instruction_simplifier (after)
+ /// CHECK-NOT: Neg
+ /// CHECK-NOT: Sub
public static long NegNeg3(long arg) {
return 0 - -arg;
@@ -612,21 +612,21 @@
* The transformation tested is implemented in `InstructionSimplifierVisitor::VisitNeg`.
*/
- // 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-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-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-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-START: int Main.NegSub1(int, int) instruction_simplifier (after)
- // CHECK-NOT: Neg
+ /// CHECK-START: int Main.NegSub1(int, int) instruction_simplifier (after)
+ /// CHECK-NOT: Neg
public static int NegSub1(int arg1, int arg2) {
return -(arg1 - arg2);
@@ -642,23 +642,23 @@
* increasing the register pressure by creating or extending live ranges.
*/
- // 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-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-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-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>>]
public static int NegSub2(int arg1, int arg2) {
int temp = arg1 - arg2;
@@ -670,40 +670,40 @@
* The transformation tested is implemented in `InstructionSimplifierVisitor::VisitNot`.
*/
- // 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-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-START: long Main.NotNot1(long) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:j\d+]] ParameterValue
- // CHECK-DAG: Return [ [[Arg]] ]
+ /// CHECK-START: long Main.NotNot1(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
- // CHECK-START: long Main.NotNot1(long) instruction_simplifier (after)
- // CHECK-NOT: Xor
+ /// CHECK-START: long Main.NotNot1(long) instruction_simplifier (after)
+ /// CHECK-NOT: Xor
public static long NotNot1(long arg) {
return ~~arg;
}
- // 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-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-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-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-START: int Main.NotNot2(int) instruction_simplifier (after)
- // CHECK-NOT: Xor
+ /// CHECK-START: int Main.NotNot2(int) instruction_simplifier (after)
+ /// CHECK-NOT: Xor
public static int NotNot2(int arg) {
int temp = ~arg;
@@ -715,22 +715,22 @@
* The transformation tested is implemented in `InstructionSimplifierVisitor::VisitSub`.
*/
- // 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-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-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-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-START: int Main.SubNeg1(int, int) instruction_simplifier (after)
- // CHECK-NOT: Sub
+ /// CHECK-START: int Main.SubNeg1(int, int) instruction_simplifier (after)
+ /// CHECK-NOT: Sub
public static int SubNeg1(int arg1, int arg2) {
return -arg1 - arg2;
@@ -746,26 +746,26 @@
* increasing the register pressure by creating or extending live ranges.
*/
- // 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-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-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-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-START: int Main.SubNeg2(int, int) instruction_simplifier (after)
- // CHECK-NOT: Add
+ /// CHECK-START: int Main.SubNeg2(int, int) instruction_simplifier (after)
+ /// CHECK-NOT: Add
public static int SubNeg2(int arg1, int arg2) {
int temp = -arg1;
@@ -779,28 +779,28 @@
* the loop.
*/
- // 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: Goto
- // -------------- Loop
- // CHECK: SuspendCheck
- // CHECK: [[Sub:j\d+]] Sub [ [[Neg]] [[Arg2]] ]
- // CHECK: Goto
+ /// 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: Goto
+ // -------------- Loop
+ /// CHECK: SuspendCheck
+ /// 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: Goto
- // -------------- Loop
- // CHECK: SuspendCheck
- // CHECK: [[Sub:j\d+]] Sub [ [[Neg]] [[Arg2]] ]
- // CHECK-NOT: Neg
- // 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: Goto
+ // -------------- Loop
+ /// CHECK: SuspendCheck
+ /// CHECK: <<Sub:j\d+>> Sub [<<Neg>>,<<Arg2>>]
+ /// CHECK-NOT: Neg
+ /// CHECK: Goto
public static long SubNeg3(long arg1, long arg2) {
long res = 0;
@@ -811,117 +811,117 @@
return res;
}
- // 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-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-START: int Main.EqualTrueRhs(boolean) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: If [ [[Arg]] ]
+ /// CHECK-START: int Main.EqualTrueRhs(boolean) instruction_simplifier (after)
+ /// 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-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-START: int Main.EqualTrueLhs(boolean) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: If [ [[Arg]] ]
+ /// CHECK-START: int Main.EqualTrueLhs(boolean) instruction_simplifier (after)
+ /// 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-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-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-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>>]
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-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-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-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>>]
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-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-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-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>>]
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-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-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-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>>]
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-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-START: int Main.NotEqualFalseRhs(boolean) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: If [ [[Arg]] ]
+ /// CHECK-START: int Main.NotEqualFalseRhs(boolean) instruction_simplifier (after)
+ /// 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-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-START: int Main.NotEqualFalseLhs(boolean) instruction_simplifier (after)
- // CHECK-DAG: [[Arg:z\d+]] ParameterValue
- // CHECK-DAG: If [ [[Arg]] ]
+ /// CHECK-START: int Main.NotEqualFalseLhs(boolean) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: If [<<Arg>>]
public static int NotEqualFalseLhs(boolean arg) {
return (false == arg) ? 3 : 5;
@@ -933,20 +933,20 @@
* remove the second.
*/
- // 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-START: boolean Main.NotNotBool(boolean) last_instruction_simplifier (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-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-START: boolean Main.NotNotBool(boolean) last_instruction_simplifier (after)
+ /// 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
- // CHECK-NOT: BooleanNot
+ /// CHECK-START: boolean Main.NotNotBool(boolean) last_instruction_simplifier (after)
+ /// CHECK: BooleanNot
+ /// CHECK-NOT: BooleanNot
public static boolean NegateValue(boolean arg) {
return !arg;
@@ -956,76 +956,76 @@
return !(NegateValue(arg));
}
- // 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-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-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-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-START: float Main.Div2(float) instruction_simplifier (after)
- // CHECK-NOT: Div
+ /// CHECK-START: float Main.Div2(float) instruction_simplifier (after)
+ /// CHECK-NOT: Div
public static float Div2(float arg) {
return arg / 2.0f;
}
- // 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-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-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-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-START: double Main.Div2(double) instruction_simplifier (after)
- // CHECK-NOT: Div
+ /// CHECK-START: double Main.Div2(double) instruction_simplifier (after)
+ /// CHECK-NOT: Div
public static double Div2(double arg) {
return arg / 2.0;
}
- // 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-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-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-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-START: float Main.DivMP25(float) instruction_simplifier (after)
- // CHECK-NOT: Div
+ /// CHECK-START: float Main.DivMP25(float) instruction_simplifier (after)
+ /// CHECK-NOT: Div
public static float DivMP25(float arg) {
return arg / -0.25f;
}
- // 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-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-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-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-START: double Main.DivMP25(double) instruction_simplifier (after)
- // CHECK-NOT: Div
+ /// CHECK-START: double Main.DivMP25(double) instruction_simplifier (after)
+ /// CHECK-NOT: Div
public static double DivMP25(double arg) {
return arg / -0.25f;
}
diff --git a/test/458-long-to-fpu/info.txt b/test/458-long-to-fpu/info.txt
index 7459cfb..cf51df5c 100644
--- a/test/458-long-to-fpu/info.txt
+++ b/test/458-long-to-fpu/info.txt
@@ -1,2 +1,2 @@
Regression test for x86's code generator, which had a bug in
-the long-to-float and long-to-double implementations.
+the long-to-float and long-to-double implementations.
diff --git a/test/458-long-to-fpu/src/Main.java b/test/458-long-to-fpu/src/Main.java
index a8b6e78..d1615dc 100644
--- a/test/458-long-to-fpu/src/Main.java
+++ b/test/458-long-to-fpu/src/Main.java
@@ -16,27 +16,30 @@
public class Main {
public static void main(String[] args) {
- System.out.println(floatConvert(false));
- System.out.println(doubleConvert(false));
+ System.out.println($noinline$FloatConvert(false));
+ System.out.println($noinline$DoubleConvert(false));
}
- public static long floatConvert(boolean flag) {
- if (flag) {
- // Try defeating inlining.
- floatConvert(false);
+ // A dummy value to defeat inlining of these routines.
+ static boolean doThrow = false;
+
+ public static long $noinline$FloatConvert(boolean flag) {
+ // Try defeating inlining.
+ if (doThrow) {
+ throw new Error();
}
long l = myLong;
myFloat = (float)l;
return l;
}
- public static long doubleConvert(boolean flag) {
- if (flag) {
- // Try defeating inlining.
- floatConvert(false);
+ public static long $noinline$DoubleConvert(boolean flag) {
+ // Try defeating inlining.
+ if (doThrow) {
+ throw new Error();
}
long l = myLong;
- myFloat = (float)l;
+ myDouble = (double)l;
return l;
}
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..64979ca 100644
--- a/test/462-checker-inlining-across-dex-files/src/Main.java
+++ b/test/462-checker-inlining-across-dex-files/src/Main.java
@@ -21,132 +21,136 @@
public class Main {
- // CHECK-START: void Main.inlineEmptyMethod() inliner (before)
- // CHECK-DAG: [[Invoke:v\d+]] InvokeStaticOrDirect
- // CHECK-DAG: ReturnVoid
+ /// CHECK-START: void Main.inlineEmptyMethod() inliner (before)
+ /// CHECK-DAG: <<Invoke:v\d+>> InvokeStaticOrDirect
+ /// CHECK-DAG: ReturnVoid
- // CHECK-START: void Main.inlineEmptyMethod() inliner (after)
- // CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-START: void Main.inlineEmptyMethod() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
public static void inlineEmptyMethod() {
OtherDex.emptyMethod();
}
- // CHECK-START: int Main.inlineReturnIntMethod() inliner (before)
- // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ /// CHECK-START: int Main.inlineReturnIntMethod() inliner (before)
+ /// 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-NOT: InvokeStaticOrDirect
- // CHECK-START: int Main.inlineReturnIntMethod() inliner (after)
- // CHECK-DAG: [[Const38:i\d+]] IntConstant 38
- // CHECK-DAG: Return [ [[Const38]] ]
+ /// CHECK-START: int Main.inlineReturnIntMethod() inliner (after)
+ /// 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-START: int Main.dontInlineOtherDexStatic() inliner (before)
+ /// 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-START: int Main.dontInlineOtherDexStatic() inliner (after)
+ /// 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-START: int Main.inlineMainStatic() inliner (before)
+ /// 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-NOT: InvokeStaticOrDirect
- // CHECK-START: int Main.inlineMainStatic() inliner (after)
- // CHECK-DAG: [[Static:i\d+]] StaticFieldGet
- // CHECK-DAG: Return [ [[Static]] ]
+ /// CHECK-START: int Main.inlineMainStatic() inliner (after)
+ /// 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-START: int Main.dontInlineRecursiveCall() inliner (before)
+ /// 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-START: int Main.dontInlineRecursiveCall() inliner (after)
+ /// 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-START: java.lang.String Main.dontInlineReturnString() inliner (before)
+ /// 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-START: java.lang.String Main.dontInlineReturnString() inliner (after)
+ /// 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-START: java.lang.Class Main.dontInlineOtherDexClass() inliner (before)
+ /// 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-START: java.lang.Class Main.dontInlineOtherDexClass() inliner (after)
+ /// 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-START: java.lang.Class Main.inlineMainClass() inliner (before)
+ /// 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-NOT: InvokeStaticOrDirect
- // CHECK-START: java.lang.Class Main.inlineMainClass() inliner (after)
- // CHECK-DAG: [[Class:l\d+]] LoadClass
- // CHECK-DAG: Return [ [[Class]] ]
+ /// CHECK-START: java.lang.Class Main.inlineMainClass() inliner (after)
+ /// 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-START: java.lang.Class Main.dontInlineOtherDexClassStaticCall() inliner (before)
+ /// 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-START: java.lang.Class Main.dontInlineOtherDexClassStaticCall() inliner (after)
+ /// 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-START: java.lang.Class Main.inlineOtherDexCallingMain() inliner (before)
+ /// 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-NOT: InvokeStaticOrDirect
- // CHECK-START: java.lang.Class Main.inlineOtherDexCallingMain() inliner (after)
- // CHECK-DAG: [[Class:l\d+]] LoadClass
- // CHECK-DAG: Return [ [[Class]] ]
+ /// CHECK-START: java.lang.Class Main.inlineOtherDexCallingMain() inliner (after)
+ /// 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..0b75930 100644
--- a/test/463-checker-boolean-simplifier/src/Main.java
+++ b/test/463-checker-boolean-simplifier/src/Main.java
@@ -37,33 +37,33 @@
* empty branches removed.
*/
- // 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-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-START: boolean Main.BooleanNot(boolean) boolean_simplifier (before)
- // CHECK: Goto
- // CHECK: Goto
- // CHECK: Goto
- // CHECK-NOT: Goto
+ /// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (before)
+ /// CHECK: Goto
+ /// CHECK: Goto
+ /// CHECK: Goto
+ /// 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-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-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after)
- // CHECK-NOT: If
- // CHECK-NOT: Phi
+ /// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after)
+ /// CHECK-NOT: If
+ /// CHECK-NOT: Phi
- // CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after)
- // CHECK: Goto
- // CHECK-NOT: Goto
+ /// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after)
+ /// CHECK: Goto
+ /// CHECK-NOT: Goto
public static boolean BooleanNot(boolean x) {
return !x;
@@ -74,23 +74,23 @@
* and 0 when False.
*/
- // 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-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-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-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>>]
public static boolean GreaterThan(int x, int y) {
return (x <= y) ? false : true;
@@ -101,26 +101,26 @@
* and 1 when False.
*/
- // 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-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-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-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-START: boolean Main.LessThan(int, int) boolean_simplifier (after)
- // CHECK-NOT: GreaterThanOrEqual
+ /// CHECK-START: boolean Main.LessThan(int, int) boolean_simplifier (after)
+ /// CHECK-NOT: GreaterThanOrEqual
public static boolean LessThan(int x, int y) {
return (x < y) ? true : false;
@@ -131,57 +131,57 @@
* Note that Phis are discovered retrospectively.
*/
- // 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-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-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-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>>]
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-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-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-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>>]
// Note: The fact that branches are swapped is verified by running the test.
- // CHECK-START: int Main.NegatedCondition(boolean) boolean_simplifier (after)
- // CHECK-NOT: BooleanNot
+ /// CHECK-START: int Main.NegatedCondition(boolean) boolean_simplifier (after)
+ /// CHECK-NOT: BooleanNot
public static int NegatedCondition(boolean x) {
if (x != false) {
diff --git a/test/464-checker-inline-sharpen-calls/src/Main.java b/test/464-checker-inline-sharpen-calls/src/Main.java
index 1b25b42..876496f 100644
--- a/test/464-checker-inline-sharpen-calls/src/Main.java
+++ b/test/464-checker-inline-sharpen-calls/src/Main.java
@@ -19,27 +19,27 @@
public void invokeVirtual() {
}
- // CHECK-START: void Main.inlineSharpenInvokeVirtual(Main) inliner (before)
- // CHECK-DAG: [[Invoke:v\d+]] InvokeStaticOrDirect
- // CHECK-DAG: ReturnVoid
+ /// CHECK-START: void Main.inlineSharpenInvokeVirtual(Main) inliner (before)
+ /// CHECK-DAG: <<Invoke:v\d+>> InvokeStaticOrDirect
+ /// CHECK-DAG: ReturnVoid
- // CHECK-START: void Main.inlineSharpenInvokeVirtual(Main) inliner (after)
- // CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-START: void Main.inlineSharpenInvokeVirtual(Main) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
public static void inlineSharpenInvokeVirtual(Main m) {
m.invokeVirtual();
}
- // CHECK-START: int Main.inlineSharpenStringInvoke() inliner (before)
- // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
- // CHECK-DAG: Return [ [[Invoke]] ]
+ /// CHECK-START: int Main.inlineSharpenStringInvoke() inliner (before)
+ /// 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-NOT: InvokeStaticOrDirect
- // CHECK-START: int Main.inlineSharpenStringInvoke() inliner (after)
- // CHECK-DAG: [[Field:i\d+]] InstanceFieldGet
- // CHECK-DAG: Return [ [[Field]] ]
+ /// CHECK-START: int Main.inlineSharpenStringInvoke() inliner (after)
+ /// 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..704e9fe 100644
--- a/test/465-checker-clinit-gvn/src/Main.java
+++ b/test/465-checker-clinit-gvn/src/Main.java
@@ -26,31 +26,31 @@
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-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-START: int Main.accessTwoStatics() GVN (after)
- // CHECK-DAG: [[Class:l\d+]] LoadClass
- // CHECK-DAG: ClinitCheck [ [[Class]] ]
- // CHECK-NOT: ClinitCheck
+ /// CHECK-START: int Main.accessTwoStatics() GVN (after)
+ /// CHECK-DAG: <<Class:l\d+>> LoadClass
+ /// CHECK-DAG: ClinitCheck [<<Class>>]
+ /// CHECK-NOT: ClinitCheck
public static int accessTwoStatics() {
return OtherClass.b - OtherClass.a;
}
- // 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-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-START: int Main.accessTwoStaticsCallInBetween() GVN (after)
- // CHECK-DAG: [[Class:l\d+]] LoadClass
- // CHECK-DAG: ClinitCheck [ [[Class]] ]
- // CHECK-NOT: ClinitCheck
+ /// CHECK-START: int Main.accessTwoStaticsCallInBetween() GVN (after)
+ /// CHECK-DAG: <<Class:l\d+>> LoadClass
+ /// CHECK-DAG: ClinitCheck [<<Class>>]
+ /// CHECK-NOT: ClinitCheck
public static int accessTwoStaticsCallInBetween() {
int b = OtherClass.b;
diff --git a/test/466-get-live-vreg/src/Main.java b/test/466-get-live-vreg/src/Main.java
index 3118085..851506b 100644
--- a/test/466-get-live-vreg/src/Main.java
+++ b/test/466-get-live-vreg/src/Main.java
@@ -53,18 +53,30 @@
}
public static void main(String[] args) {
- if (testLiveArgument(42) != 42) {
- throw new Error("Expected 42");
+ if (testLiveArgument(staticField3) != staticField3) {
+ throw new Error("Expected " + staticField3);
}
- if (testLiveArgument(42) != 42) {
- throw new Error("Expected 42");
+ if (testLiveArgument(staticField3) != staticField3) {
+ throw new Error("Expected " + staticField3);
}
- testIntervalHole(1, true);
- testIntervalHole(1, false);
+ testWrapperIntervalHole(1, true);
+ testWrapperIntervalHole(1, false);
+ }
+
+ // Wrapper method to avoid inlining, which affects liveness
+ // of dex registers.
+ static void testWrapperIntervalHole(int arg, boolean test) {
+ try {
+ Thread.sleep(0);
+ testIntervalHole(arg, test);
+ } catch (Exception e) {
+ throw new Error(e);
+ }
}
static int staticField1;
static int staticField2;
+ static int staticField3 = 42;
}
diff --git a/test/468-checker-bool-simplifier-regression/smali/TestCase.smali b/test/468-checker-bool-simplifier-regression/smali/TestCase.smali
index f36304d..da1c5ec 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/473-checker-inliner-constants/src/Main.java b/test/473-checker-inliner-constants/src/Main.java
index 79d89b0..85f6565 100644
--- a/test/473-checker-inliner-constants/src/Main.java
+++ b/test/473-checker-inliner-constants/src/Main.java
@@ -16,13 +16,13 @@
public class Main {
- // CHECK-START: java.lang.Object Main.InlineNullConstant() inliner (before)
- // CHECK: NullConstant
- // CHECK-NOT: NullConstant
+ /// CHECK-START: java.lang.Object Main.InlineNullConstant() inliner (before)
+ /// CHECK: NullConstant
+ /// CHECK-NOT: NullConstant
- // CHECK-START: java.lang.Object Main.InlineNullConstant() inliner (after)
- // CHECK: NullConstant
- // CHECK-NOT: NullConstant
+ /// CHECK-START: java.lang.Object Main.InlineNullConstant() inliner (after)
+ /// CHECK: NullConstant
+ /// CHECK-NOT: NullConstant
public static Object returnNullConstant(Object x) {
return null;
@@ -32,13 +32,13 @@
return returnNullConstant(null);
}
- // CHECK-START: int Main.InlineIntConstant() inliner (before)
- // CHECK: IntConstant 42
- // CHECK-NOT: IntConstant 42
+ /// CHECK-START: int Main.InlineIntConstant() inliner (before)
+ /// CHECK: IntConstant 42
+ /// CHECK-NOT: IntConstant 42
- // CHECK-START: int Main.InlineIntConstant() inliner (after)
- // CHECK: IntConstant 42
- // CHECK-NOT: IntConstant 42
+ /// CHECK-START: int Main.InlineIntConstant() inliner (after)
+ /// CHECK: IntConstant 42
+ /// CHECK-NOT: IntConstant 42
public static int returnIntConstant(int x) {
return 42;
@@ -48,13 +48,13 @@
return returnIntConstant(42);
}
- // CHECK-START: long Main.InlineLongConstant() inliner (before)
- // CHECK: LongConstant 42
- // CHECK-NOT: LongConstant 42
+ /// CHECK-START: long Main.InlineLongConstant() inliner (before)
+ /// CHECK: LongConstant 42
+ /// CHECK-NOT: LongConstant 42
- // CHECK-START: long Main.InlineLongConstant() inliner (after)
- // CHECK: LongConstant 42
- // CHECK-NOT: LongConstant 42
+ /// CHECK-START: long Main.InlineLongConstant() inliner (after)
+ /// CHECK: LongConstant 42
+ /// CHECK-NOT: LongConstant 42
public static long returnLongConstant(long x) {
return 42L;
diff --git a/test/474-checker-boolean-input/src/Main.java b/test/474-checker-boolean-input/src/Main.java
index 9151986..86d0f7c 100644
--- a/test/474-checker-boolean-input/src/Main.java
+++ b/test/474-checker-boolean-input/src/Main.java
@@ -27,9 +27,9 @@
* we implement a suitable type analysis.
*/
- // CHECK-START: boolean Main.TestPhiAsBoolean(int) boolean_simplifier (after)
- // CHECK-DAG: [[Phi:i\d+]] Phi
- // CHECK-DAG: BooleanNot [ [[Phi]] ]
+ /// CHECK-START: boolean Main.TestPhiAsBoolean(int) boolean_simplifier (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi
+ /// CHECK-DAG: BooleanNot [<<Phi>>]
public static boolean f1;
public static boolean f2;
@@ -47,9 +47,9 @@
* we implement a suitable type analysis.
*/
- // CHECK-START: boolean Main.TestAndAsBoolean(boolean, boolean) boolean_simplifier (after)
- // CHECK-DAG: [[And:i\d+]] And
- // CHECK-DAG: BooleanNot [ [[And]] ]
+ /// CHECK-START: boolean Main.TestAndAsBoolean(boolean, boolean) boolean_simplifier (after)
+ /// CHECK-DAG: <<And:i\d+>> And
+ /// CHECK-DAG: BooleanNot [<<And>>]
public static boolean InlineAnd(boolean x, boolean y) {
return x & y;
@@ -64,9 +64,9 @@
* we implement a suitable type analysis.
*/
- // CHECK-START: boolean Main.TestOrAsBoolean(boolean, boolean) boolean_simplifier (after)
- // CHECK-DAG: [[Or:i\d+]] Or
- // CHECK-DAG: BooleanNot [ [[Or]] ]
+ /// CHECK-START: boolean Main.TestOrAsBoolean(boolean, boolean) boolean_simplifier (after)
+ /// CHECK-DAG: <<Or:i\d+>> Or
+ /// CHECK-DAG: BooleanNot [<<Or>>]
public static boolean InlineOr(boolean x, boolean y) {
return x | y;
@@ -81,9 +81,9 @@
* we implement a suitable type analysis.
*/
- // CHECK-START: boolean Main.TestXorAsBoolean(boolean, boolean) boolean_simplifier (after)
- // CHECK-DAG: [[Xor:i\d+]] Xor
- // CHECK-DAG: BooleanNot [ [[Xor]] ]
+ /// CHECK-START: boolean Main.TestXorAsBoolean(boolean, boolean) boolean_simplifier (after)
+ /// 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..e709ba0 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-START: void ClassWithoutFinals.<init>() register (after)
+ /// CHECK-NOT: MemoryBarrier kind:StoreStore
public ClassWithoutFinals() {}
}
@@ -25,10 +26,9 @@
public final int x;
public ClassWithFinals obj;
- // CHECK-START: void ClassWithFinals.<init>(boolean) register (after)
- // CHECK: MemoryBarrier {{StoreStore}}
- // CHECK-NOT: {{.*}}
- // CHECK: ReturnVoid
+ /// CHECK-START: void ClassWithFinals.<init>(boolean) register (after)
+ /// CHECK: MemoryBarrier kind:StoreStore
+ /// CHECK-NEXT: ReturnVoid
public ClassWithFinals(boolean cond) {
x = 0;
if (cond) {
@@ -37,19 +37,17 @@
}
}
- // CHECK-START: void ClassWithFinals.<init>() register (after)
- // CHECK: MemoryBarrier {{StoreStore}}
- // CHECK-NOT: {{.*}}
- // CHECK: ReturnVoid
+ /// CHECK-START: void ClassWithFinals.<init>() register (after)
+ /// 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-START: void ClassWithFinals.<init>(int) register (after)
+ /// CHECK: MemoryBarrier kind:StoreStore
+ /// CHECK: MemoryBarrier kind:StoreStore
+ /// CHECK-NEXT: ReturnVoid
public ClassWithFinals(int x) {
// This should have two barriers:
// - one for the constructor
@@ -60,88 +58,136 @@
}
class InheritFromClassWithFinals extends ClassWithFinals {
- // CHECK-START: void InheritFromClassWithFinals.<init>() register (after)
- // CHECK: MemoryBarrier {{StoreStore}}
- // CHECK-NOT: {{.*}}
- // CHECK: ReturnVoid
+ /// CHECK-START: void InheritFromClassWithFinals.<init>() register (after)
+ /// CHECK: MemoryBarrier kind:StoreStore
+ /// CHECK-NEXT: ReturnVoid
- // CHECK-START: void InheritFromClassWithFinals.<init>() register (after)
- // CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-START: void InheritFromClassWithFinals.<init>() register (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
public InheritFromClassWithFinals() {
// Should inline the super constructor.
}
- // CHECK-START: void InheritFromClassWithFinals.<init>(boolean) register (after)
- // CHECK: InvokeStaticOrDirect
+ /// CHECK-START: void InheritFromClassWithFinals.<init>(boolean) register (after)
+ /// CHECK: InvokeStaticOrDirect
- // CHECK-START: void InheritFromClassWithFinals.<init>(boolean) register (after)
- // CHECK-NOT: MemoryBarrier {{StoreStore}}
+ /// CHECK-START: void InheritFromClassWithFinals.<init>(boolean) register (after)
+ /// 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-START: void HaveFinalsAndInheritFromClassWithFinals.<init>() register (after)
+ /// CHECK: MemoryBarrier kind:StoreStore
+ /// CHECK-NEXT: ReturnVoid
- // CHECK-START: void HaveFinalsAndInheritFromClassWithFinals.<init>() register (after)
- // CHECK-NOT: InvokeStaticOrDirect
+ /// 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-START: void HaveFinalsAndInheritFromClassWithFinals.<init>(boolean) register (after)
+ /// 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-START: ClassWithFinals Main.noInlineNoConstructorBarrier() register (after)
+ /// CHECK: InvokeStaticOrDirect
- // CHECK-START: ClassWithFinals Main.noInlineNoConstructorBarrier() register (after)
- // CHECK-NOT: MemoryBarrier {{StoreStore}}
+ /// CHECK-START: ClassWithFinals Main.noInlineNoConstructorBarrier() register (after)
+ /// 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/477-checker-bound-type/src/Main.java b/test/477-checker-bound-type/src/Main.java
index b30028d..fe52e83 100644
--- a/test/477-checker-bound-type/src/Main.java
+++ b/test/477-checker-bound-type/src/Main.java
@@ -17,8 +17,8 @@
public class Main {
- // CHECK-START: java.lang.Object Main.boundTypeForIf(java.lang.Object) reference_type_propagation (after)
- // CHECK: BoundType
+ /// CHECK-START: java.lang.Object Main.boundTypeForIf(java.lang.Object) reference_type_propagation (after)
+ /// CHECK: BoundType
public static Object boundTypeForIf(Object a) {
if (a != null) {
return a.toString();
@@ -27,8 +27,8 @@
}
}
- // CHECK-START: java.lang.Object Main.boundTypeForInstanceOf(java.lang.Object) reference_type_propagation (after)
- // CHECK: BoundType
+ /// CHECK-START: java.lang.Object Main.boundTypeForInstanceOf(java.lang.Object) reference_type_propagation (after)
+ /// CHECK: BoundType
public static Object boundTypeForInstanceOf(Object a) {
if (a instanceof Main) {
return (Main)a;
@@ -37,8 +37,8 @@
}
}
- // CHECK-START: java.lang.Object Main.noBoundTypeForIf(java.lang.Object) reference_type_propagation (after)
- // CHECK-NOT: BoundType
+ /// CHECK-START: java.lang.Object Main.noBoundTypeForIf(java.lang.Object) reference_type_propagation (after)
+ /// CHECK-NOT: BoundType
public static Object noBoundTypeForIf(Object a) {
if (a == null) {
return new Object();
@@ -47,8 +47,8 @@
}
}
- // CHECK-START: java.lang.Object Main.noBoundTypeForInstanceOf(java.lang.Object) reference_type_propagation (after)
- // CHECK-NOT: BoundType
+ /// CHECK-START: java.lang.Object Main.noBoundTypeForInstanceOf(java.lang.Object) reference_type_propagation (after)
+ /// CHECK-NOT: BoundType
public static Object noBoundTypeForInstanceOf(Object a) {
if (a instanceof Main) {
return new Object();
diff --git a/test/477-long-to-float-conversion-precision/info.txt b/test/477-long-to-float-conversion-precision/info.txt
index d9d41d7..1e07cf3 100644
--- a/test/477-long-to-float-conversion-precision/info.txt
+++ b/test/477-long-to-float-conversion-precision/info.txt
@@ -1 +1 @@
-Tests for type conversions precision.
+Regression test for type conversion precision.
diff --git a/test/477-long-to-float-conversion-precision/src/Main.java b/test/477-long-to-float-conversion-precision/src/Main.java
index bc17053..cd97039 100644
--- a/test/477-long-to-float-conversion-precision/src/Main.java
+++ b/test/477-long-to-float-conversion-precision/src/Main.java
@@ -30,9 +30,8 @@
}
private static void longToFloat() {
- // The result for this test case is slightly less accurate on ARM,
- // due to the implementation of long-to-float type conversions for
- // this architecture (both in Quick and Optimizing).
+ // The result for this test case used to be slightly less accurate
+ // on ARM (both in Quick and Optimizing).
assertFloatEquals(Float.intBitsToFloat(-555858671), $opt$LongToFloat(-8008112895877447681L));
}
diff --git a/test/478-checker-clinit-check-pruning/src/Main.java b/test/478-checker-clinit-check-pruning/src/Main.java
index 6da8945..e6aab63 100644
--- a/test/478-checker-clinit-check-pruning/src/Main.java
+++ b/test/478-checker-clinit-check-pruning/src/Main.java
@@ -23,17 +23,17 @@
* removed before register allocation & code generation.
*/
- // 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-START: void Main.invokeStaticInlined() builder (after)
+ /// CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false
+ /// CHECK-DAG: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>]
+ /// CHECK-DAG: InvokeStaticOrDirect [{{[ij]\d+}},<<ClinitCheck>>]
- // CHECK-START: void Main.invokeStaticInlined() inliner (after)
- // CHECK-DAG: [[LoadClass:l\d+]] LoadClass
- // CHECK-DAG: [[ClinitCheck:l\d+]] ClinitCheck [ [[LoadClass]] ]
+ /// CHECK-START: void Main.invokeStaticInlined() inliner (after)
+ /// 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
+ /// CHECK-START: void Main.invokeStaticInlined() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
// The following checks ensure the clinit check instruction added by
// the builder is pruned by the PrepareForRegisterAllocation, while
@@ -41,12 +41,12 @@
// graph is not dumped after (nor before) this step, we check the
// CFG as it is before the next pass (liveness analysis) instead.
- // CHECK-START: void Main.invokeStaticInlined() liveness (before)
- // CHECK-DAG: LoadClass
+ /// CHECK-START: void Main.invokeStaticInlined() liveness (before)
+ /// CHECK-DAG: LoadClass gen_clinit_check:true
- // CHECK-START: void Main.invokeStaticInlined() liveness (before)
- // CHECK-NOT: ClinitCheck
- // CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-START: void Main.invokeStaticInlined() liveness (before)
+ /// CHECK-NOT: ClinitCheck
+ /// CHECK-NOT: InvokeStaticOrDirect
static void invokeStaticInlined() {
ClassWithClinit1.$opt$inline$StaticMethod();
@@ -66,15 +66,15 @@
* initialization check of the called method's declaring class.
*/
- // 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-START: void Main.invokeStaticNotInlined() builder (after)
+ /// CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false
+ /// CHECK-DAG: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>]
+ /// CHECK-DAG: InvokeStaticOrDirect [{{[ij]\d+}},<<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-START: void Main.invokeStaticNotInlined() inliner (after)
+ /// CHECK-DAG: <<LoadClass:l\d+>> LoadClass gen_clinit_check:false
+ /// CHECK-DAG: <<ClinitCheck:l\d+>> ClinitCheck [<<LoadClass>>]
+ /// CHECK-DAG: InvokeStaticOrDirect [{{[ij]\d+}},<<ClinitCheck>>]
// The following checks ensure the clinit check and load class
// instructions added by the builder are pruned by the
@@ -82,15 +82,15 @@
// dumped after (nor before) this step, we check the CFG as it is
// before the next pass (liveness analysis) instead.
- // CHECK-START: void Main.invokeStaticNotInlined() liveness (before)
- // CHECK-DAG: InvokeStaticOrDirect
+ /// CHECK-START: void Main.invokeStaticNotInlined() liveness (before)
+ /// CHECK-DAG: InvokeStaticOrDirect
- // CHECK-START: void Main.invokeStaticNotInlined() liveness (before)
- // CHECK-NOT: LoadClass
- // CHECK-NOT: ClinitCheck
+ /// CHECK-START: void Main.invokeStaticNotInlined() liveness (before)
+ /// CHECK-NOT: LoadClass
+ /// CHECK-NOT: ClinitCheck
static void invokeStaticNotInlined() {
- ClassWithClinit2.staticMethod();
+ ClassWithClinit2.$noinline$staticMethod();
}
static class ClassWithClinit2 {
@@ -100,7 +100,7 @@
static boolean doThrow = false;
- static void staticMethod() {
+ static void $noinline$staticMethod() {
if (doThrow) {
// Try defeating inlining.
throw new Error();
@@ -114,17 +114,17 @@
* explicit clinit check.
*/
- // CHECK-START: void Main$ClassWithClinit3.invokeStaticInlined() builder (after)
- // CHECK-DAG: InvokeStaticOrDirect
+ /// CHECK-START: void Main$ClassWithClinit3.invokeStaticInlined() builder (after)
+ /// CHECK-DAG: InvokeStaticOrDirect
- // CHECK-START: void Main$ClassWithClinit3.invokeStaticInlined() builder (after)
- // CHECK-NOT: LoadClass
- // CHECK-NOT: ClinitCheck
+ /// CHECK-START: void Main$ClassWithClinit3.invokeStaticInlined() builder (after)
+ /// CHECK-NOT: LoadClass
+ /// CHECK-NOT: ClinitCheck
- // CHECK-START: void Main$ClassWithClinit3.invokeStaticInlined() inliner (after)
- // CHECK-NOT: LoadClass
- // CHECK-NOT: ClinitCheck
- // CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-START: void Main$ClassWithClinit3.invokeStaticInlined() inliner (after)
+ /// CHECK-NOT: LoadClass
+ /// CHECK-NOT: ClinitCheck
+ /// CHECK-NOT: InvokeStaticOrDirect
static class ClassWithClinit3 {
static void invokeStaticInlined() {
@@ -149,27 +149,27 @@
* require an explicit clinit check.
*/
- // CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() builder (after)
- // CHECK-DAG: InvokeStaticOrDirect
+ /// CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() builder (after)
+ /// CHECK-DAG: InvokeStaticOrDirect
- // CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() builder (after)
- // CHECK-NOT: LoadClass
- // CHECK-NOT: ClinitCheck
+ /// CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() builder (after)
+ /// CHECK-NOT: LoadClass
+ /// CHECK-NOT: ClinitCheck
- // CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() inliner (after)
- // CHECK-DAG: InvokeStaticOrDirect
+ /// CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() inliner (after)
+ /// CHECK-DAG: InvokeStaticOrDirect
- // CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() inliner (after)
- // CHECK-NOT: LoadClass
- // CHECK-NOT: ClinitCheck
+ /// CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() inliner (after)
+ /// CHECK-NOT: LoadClass
+ /// CHECK-NOT: ClinitCheck
static class ClassWithClinit4 {
static void invokeStaticNotInlined() {
// The invocation of invokeStaticNotInlined triggers the
// initialization of ClassWithClinit4, meaning that the
- // hereinbelow call to staticMethod does not need a clinit
+ // call to staticMethod below does not need a clinit
// check.
- staticMethod();
+ $noinline$staticMethod();
}
static {
@@ -178,7 +178,7 @@
static boolean doThrow = false;
- static void staticMethod() {
+ static void $noinline$staticMethod() {
if (doThrow) {
// Try defeating inlining.
throw new Error();
@@ -192,17 +192,17 @@
* explicit clinit check.
*/
- // CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() builder (after)
- // CHECK-DAG: InvokeStaticOrDirect
+ /// CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() builder (after)
+ /// CHECK-DAG: InvokeStaticOrDirect
- // CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() builder (after)
- // CHECK-NOT: LoadClass
- // CHECK-NOT: ClinitCheck
+ /// CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() builder (after)
+ /// CHECK-NOT: LoadClass
+ /// CHECK-NOT: ClinitCheck
- // CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() inliner (after)
- // CHECK-NOT: LoadClass
- // CHECK-NOT: ClinitCheck
- // CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() inliner (after)
+ /// CHECK-NOT: LoadClass
+ /// CHECK-NOT: ClinitCheck
+ /// CHECK-NOT: InvokeStaticOrDirect
static class ClassWithClinit5 {
static void $opt$inline$StaticMethod() {
@@ -225,24 +225,24 @@
* explicit clinit check.
*/
- // CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() builder (after)
- // CHECK-DAG: InvokeStaticOrDirect
+ /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() builder (after)
+ /// CHECK-DAG: InvokeStaticOrDirect
- // CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() builder (after)
- // CHECK-NOT: LoadClass
- // CHECK-NOT: ClinitCheck
+ /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() builder (after)
+ /// CHECK-NOT: LoadClass
+ /// CHECK-NOT: ClinitCheck
- // CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() inliner (after)
- // CHECK-DAG: InvokeStaticOrDirect
+ /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() inliner (after)
+ /// CHECK-DAG: InvokeStaticOrDirect
- // CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() inliner (after)
- // CHECK-NOT: LoadClass
- // CHECK-NOT: ClinitCheck
+ /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() inliner (after)
+ /// CHECK-NOT: LoadClass
+ /// CHECK-NOT: ClinitCheck
static class ClassWithClinit6 {
static boolean doThrow = false;
- static void staticMethod() {
+ static void $noinline$staticMethod() {
if (doThrow) {
// Try defeating inlining.
throw new Error();
@@ -256,10 +256,48 @@
static class SubClassOfClassWithClinit6 extends ClassWithClinit6 {
static void invokeStaticNotInlined() {
- ClassWithClinit6.staticMethod();
+ ClassWithClinit6.$noinline$staticMethod();
}
}
+
+ /*
+ * 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.$noinline$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.$noinline$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/478-checker-inliner-nested-loop/src/Main.java b/test/478-checker-inliner-nested-loop/src/Main.java
index df583d9..aa02349 100644
--- a/test/478-checker-inliner-nested-loop/src/Main.java
+++ b/test/478-checker-inliner-nested-loop/src/Main.java
@@ -33,12 +33,12 @@
return result;
}
- // CHECK-START: int Main.NestedLoop(int, int) inliner (before)
- // CHECK-NOT: Mul
+ /// CHECK-START: int Main.NestedLoop(int, int) inliner (before)
+ /// CHECK-NOT: Mul
- // CHECK-START: int Main.NestedLoop(int, int) inliner (after)
- // CHECK: Mul
- // CHECK-NOT: Mul
+ /// CHECK-START: int Main.NestedLoop(int, int) inliner (after)
+ /// CHECK: Mul
+ /// CHECK-NOT: Mul
public static int NestedLoop(int max_x, int max_y) {
int total = 0;
diff --git a/test/480-checker-dead-blocks/src/Main.java b/test/480-checker-dead-blocks/src/Main.java
index 83dbb26..4cc1634 100644
--- a/test/480-checker-dead-blocks/src/Main.java
+++ b/test/480-checker-dead-blocks/src/Main.java
@@ -30,25 +30,25 @@
return false;
}
- // 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: 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-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: 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-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-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-START: int Main.testTrueBranch(int, int) dead_code_elimination_final (after)
- // CHECK-NOT: If
- // CHECK-NOT: Sub
- // CHECK-NOT: Phi
+ /// CHECK-START: int Main.testTrueBranch(int, int) dead_code_elimination_final (after)
+ /// CHECK-NOT: If
+ /// CHECK-NOT: Sub
+ /// CHECK-NOT: Phi
public static int testTrueBranch(int x, int y) {
int z;
@@ -60,25 +60,25 @@
return z;
}
- // 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: 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-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: 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-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-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-START: int Main.testFalseBranch(int, int) dead_code_elimination_final (after)
- // CHECK-NOT: If
- // CHECK-NOT: Add
- // CHECK-NOT: Phi
+ /// CHECK-START: int Main.testFalseBranch(int, int) dead_code_elimination_final (after)
+ /// CHECK-NOT: If
+ /// CHECK-NOT: Add
+ /// CHECK-NOT: Phi
public static int testFalseBranch(int x, int y) {
int z;
@@ -90,11 +90,11 @@
return z;
}
- // CHECK-START: int Main.testRemoveLoop(int) dead_code_elimination_final (before)
- // CHECK: Mul
+ /// CHECK-START: int Main.testRemoveLoop(int) dead_code_elimination_final (before)
+ /// CHECK: Mul
- // CHECK-START: int Main.testRemoveLoop(int) dead_code_elimination_final (after)
- // CHECK-NOT: Mul
+ /// CHECK-START: int Main.testRemoveLoop(int) dead_code_elimination_final (after)
+ /// CHECK-NOT: Mul
public static int testRemoveLoop(int x) {
if (inlineFalse()) {
@@ -105,13 +105,13 @@
return x;
}
- // CHECK-START: int Main.testInfiniteLoop(int) dead_code_elimination_final (before)
- // CHECK-DAG: Return
- // CHECK-DAG: Exit
+ /// CHECK-START: int Main.testInfiniteLoop(int) dead_code_elimination_final (before)
+ /// CHECK-DAG: Return
+ /// CHECK-DAG: Exit
- // CHECK-START: int Main.testInfiniteLoop(int) dead_code_elimination_final (after)
- // CHECK-NOT: Return
- // CHECK-NOT: Exit
+ /// CHECK-START: int Main.testInfiniteLoop(int) dead_code_elimination_final (after)
+ /// CHECK-NOT: Return
+ /// CHECK-NOT: Exit
public static int testInfiniteLoop(int x) {
while (inlineTrue()) {
@@ -120,17 +120,17 @@
return x;
}
- // CHECK-START: int Main.testDeadLoop(int) dead_code_elimination_final (before)
- // CHECK-DAG: If
- // CHECK-DAG: Add
+ /// CHECK-START: int Main.testDeadLoop(int) dead_code_elimination_final (before)
+ /// CHECK-DAG: If
+ /// 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-START: int Main.testDeadLoop(int) dead_code_elimination_final (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
- // CHECK-START: int Main.testDeadLoop(int) dead_code_elimination_final (after)
- // CHECK-NOT: If
- // CHECK-NOT: Add
+ /// CHECK-START: int Main.testDeadLoop(int) dead_code_elimination_final (after)
+ /// CHECK-NOT: If
+ /// CHECK-NOT: Add
public static int testDeadLoop(int x) {
while (inlineFalse()) {
@@ -139,18 +139,18 @@
return x;
}
- // CHECK-START: int Main.testUpdateLoopInformation(int) dead_code_elimination_final (before)
- // CHECK-DAG: If
- // CHECK-DAG: If
- // CHECK-DAG: Add
+ /// CHECK-START: int Main.testUpdateLoopInformation(int) dead_code_elimination_final (before)
+ /// CHECK-DAG: If
+ /// CHECK-DAG: If
+ /// 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-START: int Main.testUpdateLoopInformation(int) dead_code_elimination_final (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
- // CHECK-START: int Main.testUpdateLoopInformation(int) dead_code_elimination_final (after)
- // CHECK-NOT: If
- // CHECK-NOT: Add
+ /// CHECK-START: int Main.testUpdateLoopInformation(int) dead_code_elimination_final (after)
+ /// CHECK-NOT: If
+ /// CHECK-NOT: Add
public static int testUpdateLoopInformation(int x) {
// Use of Or in the condition generates a dead loop where not all of its
@@ -161,16 +161,16 @@
return x;
}
- // CHECK-START: int Main.testRemoveSuspendCheck(int, int) dead_code_elimination_final (before)
- // CHECK: SuspendCheck
- // CHECK: SuspendCheck
- // CHECK: SuspendCheck
- // CHECK-NOT: SuspendCheck
+ /// CHECK-START: int Main.testRemoveSuspendCheck(int, int) dead_code_elimination_final (before)
+ /// CHECK: SuspendCheck
+ /// CHECK: SuspendCheck
+ /// CHECK: SuspendCheck
+ /// CHECK-NOT: SuspendCheck
- // CHECK-START: int Main.testRemoveSuspendCheck(int, int) dead_code_elimination_final (after)
- // CHECK: SuspendCheck
- // CHECK: SuspendCheck
- // CHECK-NOT: SuspendCheck
+ /// CHECK-START: int Main.testRemoveSuspendCheck(int, int) dead_code_elimination_final (after)
+ /// CHECK: SuspendCheck
+ /// CHECK: SuspendCheck
+ /// CHECK-NOT: SuspendCheck
public static int testRemoveSuspendCheck(int x, int y) {
// Inner loop will leave behind the header with its SuspendCheck. DCE must
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..5754723 100644
--- a/test/482-checker-loop-back-edge-use/src/Main.java
+++ b/test/482-checker-loop-back-edge-use/src/Main.java
@@ -17,17 +17,17 @@
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-START: void Main.loop1(boolean) liveness (after)
+ /// 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-START: void Main.loop2(boolean) liveness (after)
+ /// CHECK: ParameterValue liveness:4 ranges:{[4,44)} uses:[35,40,44]
+ /// CHECK: Goto liveness:38
+ /// CHECK: Goto liveness:42
public static void loop2(boolean incoming) {
while (true) {
System.out.println("foo");
@@ -35,12 +35,12 @@
}
}
- // CHECK-START: void Main.loop3(boolean) liveness (after)
- // CHECK: ParameterValue (liveness: 2 ranges: { [2, 60) }, uses: { 56 60 }
- // CHECK: Goto (liveness: 58)
+ /// CHECK-START: void Main.loop3(boolean) liveness (after)
+ /// CHECK: ParameterValue liveness:4 ranges:{[4,62)} uses:[58,62]
+ /// CHECK: Goto liveness:60
- // CHECK-START: void Main.loop3(boolean) liveness (after)
- // CHECK-NOT: Goto (liveness: 54)
+ /// CHECK-START: void Main.loop3(boolean) liveness (after)
+ /// CHECK-NOT: Goto liveness:56
public static void loop3(boolean incoming) {
// 'incoming' only needs a use at the outer loop's back edge.
while (System.currentTimeMillis() != 42) {
@@ -49,11 +49,11 @@
}
}
- // CHECK-START: void Main.loop4(boolean) liveness (after)
- // CHECK: ParameterValue (liveness: 2 ranges: { [2, 22) }, uses: { 22 }
+ /// CHECK-START: void Main.loop4(boolean) liveness (after)
+ /// CHECK: ParameterValue liveness:4 ranges:{[4,24)} uses:[24]
- // CHECK-START: void Main.loop4(boolean) liveness (after)
- // CHECK-NOT: Goto (liveness: 20)
+ /// CHECK-START: void Main.loop4(boolean) liveness (after)
+ /// CHECK-NOT: Goto liveness:22
public static void loop4(boolean incoming) {
// 'incoming' has no loop use, so should not have back edge uses.
System.out.println(incoming);
@@ -62,10 +62,10 @@
}
}
- // 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-START: void Main.loop5(boolean) liveness (after)
+ /// CHECK: ParameterValue liveness:4 ranges:{[4,52)} uses:[35,44,48,52]
+ /// CHECK: Goto liveness:46
+ /// CHECK: Goto liveness:50
public static void loop5(boolean incoming) {
// 'incoming' must have a use at both back edges.
while (Runtime.getRuntime() != null) {
@@ -75,12 +75,12 @@
}
}
- // CHECK-START: void Main.loop6(boolean) liveness (after)
- // CHECK ParameterValue (liveness: 2 ranges: { [2, 46) }, uses: { 24 46 }
- // CHECK: Goto (liveness: 44)
+ /// CHECK-START: void Main.loop6(boolean) liveness (after)
+ /// CHECK: ParameterValue liveness:4 ranges:{[4,48)} uses:[26,48]
+ /// CHECK: Goto liveness:46
- // CHECK-START: void Main.loop6(boolean) liveness (after)
- // CHECK-NOT: Goto (liveness: 22)
+ /// CHECK-START: void Main.loop6(boolean) liveness (after)
+ /// CHECK-NOT: Goto liveness:24
public static void loop6(boolean incoming) {
// 'incoming' must have a use only at the first loop's back edge.
while (true) {
@@ -89,10 +89,10 @@
}
}
- // 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-START: void Main.loop7(boolean) liveness (after)
+ /// CHECK: ParameterValue liveness:4 ranges:{[4,52)} uses:[34,43,48,52]
+ /// CHECK: Goto liveness:46
+ /// CHECK: Goto liveness:50
public static void loop7(boolean incoming) {
// 'incoming' must have a use at both back edges.
while (Runtime.getRuntime() != null) {
@@ -101,10 +101,10 @@
}
}
- // 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-START: void Main.loop8() liveness (after)
+ /// CHECK: StaticFieldGet liveness:14 ranges:{[14,46)} uses:[37,42,46]
+ /// CHECK: Goto liveness:40
+ /// CHECK: Goto liveness:44
public static void loop8() {
// 'incoming' must have a use at both back edges.
boolean incoming = field;
@@ -113,9 +113,9 @@
}
}
- // CHECK-START: void Main.loop9() liveness (after)
- // CHECK: StaticFieldGet (liveness: 22 ranges: { [22, 36) }, uses: { 31 36 }
- // CHECK: Goto (liveness: 38)
+ /// CHECK-START: void Main.loop9() liveness (after)
+ /// CHECK: StaticFieldGet liveness:24 ranges:{[24,38)} uses:[33,38]
+ /// CHECK: Goto liveness:40
public static void loop9() {
while (Runtime.getRuntime() != null) {
// 'incoming' must only have a use in the inner loop.
diff --git a/test/484-checker-register-hints/src/Main.java b/test/484-checker-register-hints/src/Main.java
index 33952d9..3715ca2 100644
--- a/test/484-checker-register-hints/src/Main.java
+++ b/test/484-checker-register-hints/src/Main.java
@@ -16,21 +16,21 @@
public class Main {
- // CHECK-START: void Main.test1(boolean, int, int, int, int, int) register (after)
- // CHECK: name "B0"
- // CHECK-NOT: ParallelMove
- // CHECK: name "B1"
- // CHECK-NOT: end_block
- // CHECK: If
- // CHECK-NOT: ParallelMove
- // CHECK: name "B3"
- // CHECK-NOT: end_block
- // CHECK: ArraySet
+ /// CHECK-START: void Main.test1(boolean, int, int, int, int, int) register (after)
+ /// CHECK: name "B0"
+ /// CHECK-NOT: ParallelMove
+ /// CHECK: name "B1"
+ /// CHECK-NOT: end_block
+ /// CHECK: If
+ /// CHECK-NOT: ParallelMove
+ /// CHECK: name "B3"
+ /// CHECK-NOT: end_block
+ /// CHECK: ArraySet
// We could check here that there is a parallel move, but it's only valid
// for some architectures (for example x86), as other architectures may
// not do move at all.
- // CHECK: end_block
- // CHECK-NOT: ParallelMove
+ /// CHECK: end_block
+ /// CHECK-NOT: ParallelMove
public static void test1(boolean z, int a, int b, int c, int d, int m) {
int e = live1;
@@ -51,21 +51,21 @@
live1 = e + f + g;
}
- // CHECK-START: void Main.test2(boolean, int, int, int, int, int) register (after)
- // CHECK: name "B0"
- // CHECK-NOT: ParallelMove
- // CHECK: name "B1"
- // CHECK-NOT: end_block
- // CHECK: If
- // CHECK-NOT: ParallelMove
- // CHECK: name "B3"
- // CHECK-NOT: end_block
- // CHECK: ArraySet
+ /// CHECK-START: void Main.test2(boolean, int, int, int, int, int) register (after)
+ /// CHECK: name "B0"
+ /// CHECK-NOT: ParallelMove
+ /// CHECK: name "B1"
+ /// CHECK-NOT: end_block
+ /// CHECK: If
+ /// CHECK-NOT: ParallelMove
+ /// CHECK: name "B3"
+ /// CHECK-NOT: end_block
+ /// CHECK: ArraySet
// We could check here that there is a parallel move, but it's only valid
// for some architectures (for example x86), as other architectures may
// not do move at all.
- // CHECK: end_block
- // CHECK-NOT: ParallelMove
+ /// CHECK: end_block
+ /// CHECK-NOT: ParallelMove
public static void test2(boolean z, int a, int b, int c, int d, int m) {
int e = live1;
@@ -85,21 +85,21 @@
live1 = e + f + g;
}
- // CHECK-START: void Main.test3(boolean, int, int, int, int, int) register (after)
- // CHECK: name "B0"
- // CHECK-NOT: ParallelMove
- // CHECK: name "B1"
- // CHECK-NOT: end_block
- // CHECK: If
- // CHECK-NOT: ParallelMove
- // CHECK: name "B6"
- // CHECK-NOT: end_block
- // CHECK: ArraySet
+ /// CHECK-START: void Main.test3(boolean, int, int, int, int, int) register (after)
+ /// CHECK: name "B0"
+ /// CHECK-NOT: ParallelMove
+ /// CHECK: name "B1"
+ /// CHECK-NOT: end_block
+ /// CHECK: If
+ /// CHECK-NOT: ParallelMove
+ /// CHECK: name "B6"
+ /// CHECK-NOT: end_block
+ /// CHECK: ArraySet
// We could check here that there is a parallel move, but it's only valid
// for some architectures (for example x86), as other architectures may
// not do move at all.
- // CHECK: end_block
- // CHECK-NOT: ParallelMove
+ /// CHECK: end_block
+ /// CHECK-NOT: ParallelMove
public static void test3(boolean z, int a, int b, int c, int d, int m) {
// Same version as test2, but with branches reversed, to ensure
diff --git a/test/485-checker-dce-loop-update/smali/TestCase.smali b/test/485-checker-dce-loop-update/smali/TestCase.smali
index 3873ac5..da27bf6 100644
--- a/test/485-checker-dce-loop-update/smali/TestCase.smali
+++ b/test/485-checker-dce-loop-update/smali/TestCase.smali
@@ -23,27 +23,27 @@
.end method
-# 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-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:<<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-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:<<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
@@ -73,31 +73,31 @@
.end method
-# 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-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:<<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-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:<<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
@@ -129,37 +129,37 @@
.end method
-# 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-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:<<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-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:<<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
@@ -196,49 +196,48 @@
.end method
-# 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-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: [[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]]
+# ### Inner loop ###
+## 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-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: [[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]]
+# ### Inner loop ###
+## 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..e8ff6a4
--- /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).$noinline$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).$noinline$Bar();
+ }
+
+ void $noinline$Bar() {throw new RuntimeException();}
+
+ public static void main(String[] sa) {
+ Main t = new Main();
+ }
+}
diff --git a/test/487-checker-inline-calls/expected.txt b/test/487-checker-inline-calls/expected.txt
new file mode 100644
index 0000000..2230482
--- /dev/null
+++ b/test/487-checker-inline-calls/expected.txt
@@ -0,0 +1,6 @@
+java.lang.Error
+ at Main.inline3(Main.java:48)
+ at Main.inline2(Main.java:44)
+ at Main.inline1(Main.java:40)
+ at Main.doTopCall(Main.java:36)
+ at Main.main(Main.java:21)
diff --git a/test/487-checker-inline-calls/info.txt b/test/487-checker-inline-calls/info.txt
new file mode 100644
index 0000000..9f5df8b
--- /dev/null
+++ b/test/487-checker-inline-calls/info.txt
@@ -0,0 +1 @@
+Checker test for ensuring inlining preserves stack traces.
diff --git a/test/487-checker-inline-calls/src/Main.java b/test/487-checker-inline-calls/src/Main.java
new file mode 100644
index 0000000..70384d5
--- /dev/null
+++ b/test/487-checker-inline-calls/src/Main.java
@@ -0,0 +1,50 @@
+/*
+ * 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 {
+ public static void main(String[] args) {
+ try {
+ doTopCall();
+ } catch (Error e) {
+ e.printStackTrace();
+ }
+ }
+
+ // We check that some inlining happened by checking the
+ // method index of the called method.
+
+ /// CHECK-START: void Main.doTopCall() inliner (before)
+ /// CHECK: InvokeStaticOrDirect dex_file_index:2
+
+ /// CHECK-START: void Main.doTopCall() inliner (after)
+ /// CHECK: InvokeStaticOrDirect dex_file_index:4
+ public static void doTopCall() {
+ inline1();
+ }
+
+ public static void inline1() {
+ inline2();
+ }
+
+ public static void inline2() {
+ inline3();
+ }
+
+ public static void inline3() {
+ throw new Error();
+ }
+}
diff --git a/test/488-checker-inline-recursive-calls/expected.txt b/test/488-checker-inline-recursive-calls/expected.txt
new file mode 100644
index 0000000..f615d3a
--- /dev/null
+++ b/test/488-checker-inline-recursive-calls/expected.txt
@@ -0,0 +1,7 @@
+java.lang.Error
+ at Main.inline3(Main.java:51)
+ at Main.doTopCall(Main.java:37)
+ at Main.inline2(Main.java:47)
+ at Main.inline1(Main.java:43)
+ at Main.doTopCall(Main.java:34)
+ at Main.main(Main.java:21)
diff --git a/test/488-checker-inline-recursive-calls/info.txt b/test/488-checker-inline-recursive-calls/info.txt
new file mode 100644
index 0000000..9abd93c
--- /dev/null
+++ b/test/488-checker-inline-recursive-calls/info.txt
@@ -0,0 +1 @@
+Checker test for inlining calls that in turn call the outer method.
diff --git a/test/488-checker-inline-recursive-calls/src/Main.java b/test/488-checker-inline-recursive-calls/src/Main.java
new file mode 100644
index 0000000..c1f25b3
--- /dev/null
+++ b/test/488-checker-inline-recursive-calls/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 {
+ public static void main(String[] args) {
+ try {
+ doTopCall(true);
+ } catch (Error e) {
+ e.printStackTrace();
+ }
+ }
+
+ /// CHECK-START: void Main.doTopCall(boolean) inliner (before)
+ /// CHECK-NOT: InvokeStaticOrDirect recursive:true
+
+ /// CHECK-START: void Main.doTopCall(boolean) inliner (after)
+ /// CHECK: InvokeStaticOrDirect recursive:true
+ public static void doTopCall(boolean first_call) {
+ if (first_call) {
+ inline1();
+ } else {
+ while (true) {
+ inline3();
+ }
+ }
+ }
+
+ public static void inline1() {
+ inline2();
+ }
+
+ public static void inline2() {
+ doTopCall(false);
+ }
+
+ public static void inline3() {
+ throw new Error();
+ }
+}
diff --git a/test/489-current-method-regression/expected.txt b/test/489-current-method-regression/expected.txt
new file mode 100644
index 0000000..cced94c
--- /dev/null
+++ b/test/489-current-method-regression/expected.txt
@@ -0,0 +1 @@
+In bar
diff --git a/test/489-current-method-regression/info.txt b/test/489-current-method-regression/info.txt
new file mode 100644
index 0000000..da03a4f
--- /dev/null
+++ b/test/489-current-method-regression/info.txt
@@ -0,0 +1,2 @@
+Regression test for a crash due to the removal
+of HCurrentMethod from the optimizing graph.
diff --git a/test/489-current-method-regression/src/Main.java b/test/489-current-method-regression/src/Main.java
new file mode 100644
index 0000000..7d102f5
--- /dev/null
+++ b/test/489-current-method-regression/src/Main.java
@@ -0,0 +1,34 @@
+/*
+ * 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 {
+ public static void main(String[] args) {
+ System.out.println(foo(1, 0));
+ }
+
+ public static String foo(int a, int b) {
+ if (a == 42) {
+ // The class loading will be seen as dead code by
+ // the optimizer.
+ Class c = Main.class;
+ }
+ return new Main().bar();
+ }
+
+ public String bar() {
+ return "In bar";
+ }
+}
diff --git a/test/490-checker-inline/expected.txt b/test/490-checker-inline/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/490-checker-inline/expected.txt
diff --git a/test/490-checker-inline/info.txt b/test/490-checker-inline/info.txt
new file mode 100644
index 0000000..0e42d77
--- /dev/null
+++ b/test/490-checker-inline/info.txt
@@ -0,0 +1 @@
+Check that we inline virtual and interface calls.
diff --git a/test/490-checker-inline/src/Main.java b/test/490-checker-inline/src/Main.java
new file mode 100644
index 0000000..21a0189
--- /dev/null
+++ b/test/490-checker-inline/src/Main.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+interface Itf {
+ public void invokeInterface();
+}
+
+public class Main implements Itf {
+
+ public void invokeInterface () {
+ }
+
+ public void invokeVirtual() {
+ }
+
+ public static Main createMain() {
+ return new Main();
+ }
+
+ public static Itf createItf() {
+ return new Main();
+ }
+
+ /// CHECK-START: void Main.testMethod() inliner (before)
+ /// CHECK-DAG: InvokeVirtual
+ /// CHECK-DAG: InvokeInterface
+
+ /// CHECK-START: void Main.testMethod() inliner (after)
+ /// CHECK-NOT: Invoke{{.*}}
+
+ public static void testMethod() {
+ createMain().invokeVirtual();
+ createItf().invokeInterface();
+ }
+
+ public static void main(String[] args) {
+ testMethod();
+ }
+}
diff --git a/test/491-current-method/expected.txt b/test/491-current-method/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/491-current-method/expected.txt
diff --git a/test/491-current-method/info.txt b/test/491-current-method/info.txt
new file mode 100644
index 0000000..e9678da
--- /dev/null
+++ b/test/491-current-method/info.txt
@@ -0,0 +1,2 @@
+Regression test for optimizing that used to
+crash in the presence of slow paths with intrinsics.
diff --git a/test/491-current-method/src/Main.java b/test/491-current-method/src/Main.java
new file mode 100644
index 0000000..87ef052
--- /dev/null
+++ b/test/491-current-method/src/Main.java
@@ -0,0 +1,70 @@
+/*
+ * 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 Main {
+
+ // The code below is written in a way that will crash
+ // the generated code at the time of submission of this test.
+ // Therefore, changes to the register allocator may
+ // affect the reproducibility of the crash.
+ public static void $noinline$foo(int a, int b, int c) {
+ // The division on x86 will take EAX and EDX, leaving ECX
+ // to put the ART current method.
+ c = c / 42;
+ // We use the empty string for forcing the slow path.
+ // The slow path for charAt when it is intrinsified, will
+ // move the parameter to ECX, and therefore overwrite the ART
+ // current method.
+ "".charAt(c);
+
+ // Do more things in the method to prevent inlining.
+ c = c / 42;
+ "".charAt(c);
+ c = c / 42;
+ "".charAt(c);
+ c = c / 42;
+ "".charAt(c);
+ c = c / 42;
+ "".charAt(c);
+ c = c / 42;
+ "".charAt(c);
+ c = c / 42;
+ "".charAt(c);
+ c = c / 42;
+ "".charAt(c);
+ c = c / 42;
+ "".charAt(c);
+ c = c / 42;
+ "".charAt(c);
+ c = c / 42;
+ "".charAt(c);
+ c = c / 42;
+ "".charAt(c);
+ }
+
+ public static void main(String[] args) {
+ boolean didThrow = false;
+ try {
+ $noinline$foo(1, 2, 3);
+ } catch (Throwable e) {
+ didThrow = true;
+ }
+
+ if (!didThrow) {
+ throw new Error("Expected an exception from charAt");
+ }
+ }
+}
diff --git a/test/492-checker-inline-invoke-interface/expected.txt b/test/492-checker-inline-invoke-interface/expected.txt
new file mode 100644
index 0000000..b0014d7
--- /dev/null
+++ b/test/492-checker-inline-invoke-interface/expected.txt
@@ -0,0 +1,5 @@
+Hello from clinit
+java.lang.Exception
+ at ForceStatic.<clinit>(Main.java:24)
+ at Main.$inline$foo(Main.java:31)
+ at Main.main(Main.java:48)
diff --git a/test/492-checker-inline-invoke-interface/info.txt b/test/492-checker-inline-invoke-interface/info.txt
new file mode 100644
index 0000000..4a0a5ff
--- /dev/null
+++ b/test/492-checker-inline-invoke-interface/info.txt
@@ -0,0 +1 @@
+Checker test to ensure we can inline interface calls.
diff --git a/test/492-checker-inline-invoke-interface/src/Main.java b/test/492-checker-inline-invoke-interface/src/Main.java
new file mode 100644
index 0000000..9a45485
--- /dev/null
+++ b/test/492-checker-inline-invoke-interface/src/Main.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+interface Itf {
+ public void $inline$foo();
+}
+
+class ForceStatic {
+ static {
+ System.out.println("Hello from clinit");
+ new Exception().printStackTrace();
+ }
+ static int field;
+}
+
+public class Main implements Itf {
+ public void $inline$foo() {
+ int a = ForceStatic.field;
+ }
+
+ /// CHECK-START: void Main.main(java.lang.String[]) inliner (before)
+ /// CHECK: InvokeStaticOrDirect
+ /// CHECK: InvokeStaticOrDirect
+
+ /// CHECK-START: void Main.main(java.lang.String[]) inliner (before)
+ /// CHECK-NOT: ClinitCheck
+
+ /// CHECK-START: void Main.main(java.lang.String[]) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ /// CHECK-START: void Main.main(java.lang.String[]) inliner (after)
+ /// CHECK: ClinitCheck
+ public static void main(String[] args) {
+ Itf itf = new Main();
+ itf.$inline$foo();
+ }
+}
diff --git a/test/493-checker-inline-invoke-interface/expected.txt b/test/493-checker-inline-invoke-interface/expected.txt
new file mode 100644
index 0000000..93620a6
--- /dev/null
+++ b/test/493-checker-inline-invoke-interface/expected.txt
@@ -0,0 +1,5 @@
+Hello from clinit
+java.lang.Exception
+ at ForceStatic.<clinit>(Main.java:24)
+ at Main.foo(Main.java:31)
+ at Main.main(Main.java:42)
diff --git a/test/493-checker-inline-invoke-interface/info.txt b/test/493-checker-inline-invoke-interface/info.txt
new file mode 100644
index 0000000..bac9c82
--- /dev/null
+++ b/test/493-checker-inline-invoke-interface/info.txt
@@ -0,0 +1,2 @@
+Check that we can optimize interface calls without
+requiring the verifier to sharpen them.
diff --git a/test/493-checker-inline-invoke-interface/src/Main.java b/test/493-checker-inline-invoke-interface/src/Main.java
new file mode 100644
index 0000000..44b727f
--- /dev/null
+++ b/test/493-checker-inline-invoke-interface/src/Main.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface Itf {
+ public void foo();
+}
+
+class ForceStatic {
+ static {
+ System.out.println("Hello from clinit");
+ new Exception().printStackTrace();
+ }
+ static int field;
+}
+
+public class Main implements Itf {
+ public void foo() {
+ int a = ForceStatic.field;
+ }
+
+ /// CHECK-START: void Main.main(java.lang.String[]) inliner (before)
+ /// CHECK: InvokeStaticOrDirect
+ /// CHECK: InvokeInterface
+
+ /// CHECK-START: void Main.main(java.lang.String[]) inliner (after)
+ /// CHECK-NOT: Invoke{{.*}}
+ public static void main(String[] args) {
+ Itf itf = bar();
+ itf.foo();
+ }
+
+ public static Itf bar() {
+ return new Main();
+ }
+}
diff --git a/test/494-checker-instanceof-tests/expected.txt b/test/494-checker-instanceof-tests/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/494-checker-instanceof-tests/expected.txt
diff --git a/test/494-checker-instanceof-tests/info.txt b/test/494-checker-instanceof-tests/info.txt
new file mode 100644
index 0000000..59e20bd
--- /dev/null
+++ b/test/494-checker-instanceof-tests/info.txt
@@ -0,0 +1 @@
+Checker test for optimizations on instanceof.
diff --git a/test/494-checker-instanceof-tests/src/Main.java b/test/494-checker-instanceof-tests/src/Main.java
new file mode 100644
index 0000000..bff9c72
--- /dev/null
+++ b/test/494-checker-instanceof-tests/src/Main.java
@@ -0,0 +1,203 @@
+/*
+ * 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 {
+ public static boolean $inline$classTypeTest(Object o) {
+ return o instanceof SubMain;
+ }
+
+ public static boolean $inline$interfaceTypeTest(Object o) {
+ return o instanceof Itf;
+ }
+
+ public static SubMain subMain;
+ public static Main mainField;
+ public static Unrelated unrelatedField;
+ public static FinalUnrelated finalUnrelatedField;
+
+ /// CHECK-START: boolean Main.classTypeTestNull() register (after)
+ /// CHECK-DAG: <<Const:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<Const>>]
+ public static boolean classTypeTestNull() {
+ return $inline$classTypeTest(null);
+ }
+
+ /// CHECK-START: boolean Main.classTypeTestExactMain() register (after)
+ /// CHECK-DAG: <<Const:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<Const>>]
+ public static boolean classTypeTestExactMain() {
+ return $inline$classTypeTest(new Main());
+ }
+
+ /// CHECK-START: boolean Main.classTypeTestExactSubMain() register (after)
+ /// CHECK-DAG: <<Const:i\d+>> IntConstant 1
+ /// CHECK-DAG: Return [<<Const>>]
+ public static boolean classTypeTestExactSubMain() {
+ return $inline$classTypeTest(new SubMain());
+ }
+
+ /// CHECK-START: boolean Main.classTypeTestSubMainOrNull() register (after)
+ /// CHECK-DAG: <<Value:z\d+>> NotEqual
+ /// CHECK-DAG: Return [<<Value>>]
+ public static boolean classTypeTestSubMainOrNull() {
+ return $inline$classTypeTest(subMain);
+ }
+
+ /// CHECK-START: boolean Main.classTypeTestMainOrNull() register (after)
+ /// CHECK-DAG: <<Value:z\d+>> InstanceOf
+ /// CHECK-DAG: Return [<<Value>>]
+ public static boolean classTypeTestMainOrNull() {
+ return $inline$classTypeTest(mainField);
+ }
+
+ /// CHECK-START: boolean Main.classTypeTestUnrelated() register (after)
+ /// CHECK-DAG: <<Const:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<Const>>]
+ public static boolean classTypeTestUnrelated() {
+ return $inline$classTypeTest(unrelatedField);
+ }
+
+ /// CHECK-START: boolean Main.classTypeTestFinalUnrelated() register (after)
+ /// CHECK-DAG: <<Const:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<Const>>]
+ public static boolean classTypeTestFinalUnrelated() {
+ return $inline$classTypeTest(finalUnrelatedField);
+ }
+
+ /// CHECK-START: boolean Main.interfaceTypeTestNull() register (after)
+ /// CHECK-DAG: <<Const:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<Const>>]
+ public static boolean interfaceTypeTestNull() {
+ return $inline$interfaceTypeTest(null);
+ }
+
+ /// CHECK-START: boolean Main.interfaceTypeTestExactMain() register (after)
+ /// CHECK-DAG: <<Const:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<Const>>]
+ public static boolean interfaceTypeTestExactMain() {
+ return $inline$interfaceTypeTest(new Main());
+ }
+
+ /// CHECK-START: boolean Main.interfaceTypeTestExactSubMain() register (after)
+ /// CHECK-DAG: <<Const:i\d+>> IntConstant 1
+ /// CHECK-DAG: Return [<<Const>>]
+ public static boolean interfaceTypeTestExactSubMain() {
+ return $inline$interfaceTypeTest(new SubMain());
+ }
+
+ /// CHECK-START: boolean Main.interfaceTypeTestSubMainOrNull() register (after)
+ /// CHECK-DAG: <<Value:z\d+>> NotEqual
+ /// CHECK-DAG: Return [<<Value>>]
+ public static boolean interfaceTypeTestSubMainOrNull() {
+ return $inline$interfaceTypeTest(subMain);
+ }
+
+ /// CHECK-START: boolean Main.interfaceTypeTestMainOrNull() register (after)
+ /// CHECK-DAG: <<Value:z\d+>> InstanceOf
+ /// CHECK-DAG: Return [<<Value>>]
+ public static boolean interfaceTypeTestMainOrNull() {
+ return $inline$interfaceTypeTest(mainField);
+ }
+
+ /// CHECK-START: boolean Main.interfaceTypeTestUnrelated() register (after)
+ /// CHECK-DAG: <<Value:z\d+>> InstanceOf
+ /// CHECK-DAG: Return [<<Value>>]
+ public static boolean interfaceTypeTestUnrelated() {
+ // This method is the main difference between doing an instanceof on an interface
+ // or a class. We have to keep the instanceof in case a subclass of Unrelated
+ // implements the interface.
+ return $inline$interfaceTypeTest(unrelatedField);
+ }
+
+ /// CHECK-START: boolean Main.interfaceTypeTestFinalUnrelated() register (after)
+ /// CHECK-DAG: <<Const:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<Const>>]
+ public static boolean interfaceTypeTestFinalUnrelated() {
+ return $inline$interfaceTypeTest(finalUnrelatedField);
+ }
+
+ public static void expect(boolean expected, boolean actual) {
+ if (expected != actual) {
+ throw new Error("Unexpected result");
+ }
+ }
+
+ public static void main(String[] args) {
+ expect(false, classTypeTestNull());
+ expect(false, classTypeTestExactMain());
+ expect(true, classTypeTestExactSubMain());
+
+ subMain = null;
+ expect(false, classTypeTestSubMainOrNull());
+ subMain = new SubMain();
+ expect(true, classTypeTestSubMainOrNull());
+
+ mainField = null;
+ expect(false, classTypeTestMainOrNull());
+ mainField = new Main();
+ expect(false, classTypeTestMainOrNull());
+ mainField = new SubMain();
+ expect(true, classTypeTestMainOrNull());
+
+ unrelatedField = null;
+ expect(false, classTypeTestUnrelated());
+ unrelatedField = new Unrelated();
+ expect(false, classTypeTestUnrelated());
+
+ finalUnrelatedField = null;
+ expect(false, classTypeTestFinalUnrelated());
+ finalUnrelatedField = new FinalUnrelated();
+ expect(false, classTypeTestFinalUnrelated());
+
+ expect(false, interfaceTypeTestNull());
+ expect(false, interfaceTypeTestExactMain());
+ expect(true, interfaceTypeTestExactSubMain());
+
+ subMain = null;
+ expect(false, interfaceTypeTestSubMainOrNull());
+ subMain = new SubMain();
+ expect(true, interfaceTypeTestSubMainOrNull());
+
+ mainField = null;
+ expect(false, interfaceTypeTestMainOrNull());
+ mainField = new Main();
+ expect(false, interfaceTypeTestMainOrNull());
+ mainField = new SubMain();
+ expect(true, interfaceTypeTestMainOrNull());
+
+ unrelatedField = null;
+ expect(false, interfaceTypeTestUnrelated());
+ unrelatedField = new Unrelated();
+ expect(false, interfaceTypeTestUnrelated());
+
+ finalUnrelatedField = null;
+ expect(false, interfaceTypeTestFinalUnrelated());
+ finalUnrelatedField = new FinalUnrelated();
+ expect(false, interfaceTypeTestFinalUnrelated());
+ }
+}
+
+interface Itf {
+}
+
+class SubMain extends Main implements Itf {
+}
+
+class Unrelated {
+}
+
+final class FinalUnrelated {
+}
diff --git a/test/495-checker-checkcast-tests/expected.txt b/test/495-checker-checkcast-tests/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/495-checker-checkcast-tests/expected.txt
diff --git a/test/495-checker-checkcast-tests/info.txt b/test/495-checker-checkcast-tests/info.txt
new file mode 100644
index 0000000..4517b22
--- /dev/null
+++ b/test/495-checker-checkcast-tests/info.txt
@@ -0,0 +1 @@
+Checker tests for optimizations on checkcast.
diff --git a/test/495-checker-checkcast-tests/src/Main.java b/test/495-checker-checkcast-tests/src/Main.java
new file mode 100644
index 0000000..aa6d5a7
--- /dev/null
+++ b/test/495-checker-checkcast-tests/src/Main.java
@@ -0,0 +1,204 @@
+/*
+ * 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 {
+ public static boolean $inline$classTypeTest(Object o) {
+ return ((SubMain)o) == o;
+ }
+
+ public static boolean $inline$interfaceTypeTest(Object o) {
+ return ((Itf)o) == o;
+ }
+
+ public static SubMain subMain;
+ public static Main mainField;
+ public static Unrelated unrelatedField;
+ public static FinalUnrelated finalUnrelatedField;
+
+ /// CHECK-START: boolean Main.classTypeTestNull() register (after)
+ /// CHECK-NOT: CheckCast
+ public static boolean classTypeTestNull() {
+ return $inline$classTypeTest(null);
+ }
+
+ /// CHECK-START: boolean Main.classTypeTestExactMain() register (after)
+ /// CHECK: CheckCast
+ public static boolean classTypeTestExactMain() {
+ return $inline$classTypeTest(new Main());
+ }
+
+ /// CHECK-START: boolean Main.classTypeTestExactSubMain() register (after)
+ /// CHECK-NOT: CheckCast
+ public static boolean classTypeTestExactSubMain() {
+ return $inline$classTypeTest(new SubMain());
+ }
+
+ /// CHECK-START: boolean Main.classTypeTestSubMainOrNull() register (after)
+ /// CHECK-NOT: CheckCast
+ public static boolean classTypeTestSubMainOrNull() {
+ return $inline$classTypeTest(subMain);
+ }
+
+ /// CHECK-START: boolean Main.classTypeTestMainOrNull() register (after)
+ /// CHECK: CheckCast
+ public static boolean classTypeTestMainOrNull() {
+ return $inline$classTypeTest(mainField);
+ }
+
+ /// CHECK-START: boolean Main.classTypeTestUnrelated() register (after)
+ /// CHECK: CheckCast
+ public static boolean classTypeTestUnrelated() {
+ return $inline$classTypeTest(unrelatedField);
+ }
+
+ /// CHECK-START: boolean Main.classTypeTestFinalUnrelated() register (after)
+ /// CHECK: CheckCast
+ public static boolean classTypeTestFinalUnrelated() {
+ return $inline$classTypeTest(finalUnrelatedField);
+ }
+
+ /// CHECK-START: boolean Main.interfaceTypeTestNull() register (after)
+ /// CHECK-NOT: CheckCast
+ public static boolean interfaceTypeTestNull() {
+ return $inline$interfaceTypeTest(null);
+ }
+
+ /// CHECK-START: boolean Main.interfaceTypeTestExactMain() register (after)
+ /// CHECK: CheckCast
+ public static boolean interfaceTypeTestExactMain() {
+ return $inline$interfaceTypeTest(new Main());
+ }
+
+ /// CHECK-START: boolean Main.interfaceTypeTestExactSubMain() register (after)
+ /// CHECK-NOT: CheckCast
+ public static boolean interfaceTypeTestExactSubMain() {
+ return $inline$interfaceTypeTest(new SubMain());
+ }
+
+ /// CHECK-START: boolean Main.interfaceTypeTestSubMainOrNull() register (after)
+ /// CHECK-NOT: CheckCast
+ public static boolean interfaceTypeTestSubMainOrNull() {
+ return $inline$interfaceTypeTest(subMain);
+ }
+
+ /// CHECK-START: boolean Main.interfaceTypeTestMainOrNull() register (after)
+ /// CHECK: CheckCast
+ public static boolean interfaceTypeTestMainOrNull() {
+ return $inline$interfaceTypeTest(mainField);
+ }
+
+ /// CHECK-START: boolean Main.interfaceTypeTestUnrelated() register (after)
+ /// CHECK: CheckCast
+ public static boolean interfaceTypeTestUnrelated() {
+ return $inline$interfaceTypeTest(unrelatedField);
+ }
+
+ /// CHECK-START: boolean Main.interfaceTypeTestFinalUnrelated() register (after)
+ /// CHECK: CheckCast
+ public static boolean interfaceTypeTestFinalUnrelated() {
+ return $inline$interfaceTypeTest(finalUnrelatedField);
+ }
+
+ public static void main(String[] args) {
+ classTypeTestNull();
+ try {
+ classTypeTestExactMain();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
+ classTypeTestExactSubMain();
+
+ subMain = null;
+ classTypeTestSubMainOrNull();
+ subMain = new SubMain();
+ classTypeTestSubMainOrNull();
+
+ mainField = null;
+ classTypeTestMainOrNull();
+ mainField = new Main();
+ try {
+ classTypeTestMainOrNull();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
+ mainField = new SubMain();
+ classTypeTestMainOrNull();
+
+ unrelatedField = null;
+ classTypeTestUnrelated();
+ unrelatedField = new Unrelated();
+ try {
+ classTypeTestUnrelated();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
+
+ finalUnrelatedField = null;
+ classTypeTestFinalUnrelated();
+ finalUnrelatedField = new FinalUnrelated();
+ try {
+ classTypeTestFinalUnrelated();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
+
+ interfaceTypeTestNull();
+ try {
+ interfaceTypeTestExactMain();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
+ interfaceTypeTestExactSubMain();
+
+ subMain = null;
+ interfaceTypeTestSubMainOrNull();
+ subMain = new SubMain();
+ interfaceTypeTestSubMainOrNull();
+
+ mainField = null;
+ interfaceTypeTestMainOrNull();
+ mainField = new Main();
+ try {
+ interfaceTypeTestMainOrNull();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
+ mainField = new SubMain();
+ interfaceTypeTestMainOrNull();
+
+ unrelatedField = null;
+ interfaceTypeTestUnrelated();
+ unrelatedField = new Unrelated();
+ try {
+ interfaceTypeTestUnrelated();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
+
+ finalUnrelatedField = null;
+ interfaceTypeTestFinalUnrelated();
+ finalUnrelatedField = new FinalUnrelated();
+ try {
+ interfaceTypeTestFinalUnrelated();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
+ }
+}
+
+interface Itf {
+}
+
+class SubMain extends Main implements Itf {
+}
+
+class Unrelated {
+}
+
+final class FinalUnrelated {
+}
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/800-smali/expected.txt b/test/800-smali/expected.txt
index a6b216b..8565637 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -16,4 +16,5 @@
MoveExceptionOnEntry
EmptySparseSwitch
b/20224106
+b/17410612
Done!
diff --git a/test/800-smali/smali/b_17410612.smali b/test/800-smali/smali/b_17410612.smali
new file mode 100644
index 0000000..17718cb
--- /dev/null
+++ b/test/800-smali/smali/b_17410612.smali
@@ -0,0 +1,14 @@
+.class public LB17410612;
+
+# Test that an invoke with a long parameter has the long parameter in
+# a pair. This should fail in the verifier and not an abort in the compiler.
+
+.super Ljava/lang/Object;
+
+.method public static run()V
+ .registers 4
+ const-wide v0, 0 # Make (v0, v1) a long
+ const-wide v2, 0 # Make (v2, v3) a long
+ invoke-static {v0, v3}, Ljava/lang/Long;->valueOf(J)Ljava/lang/Long;
+ return-void
+.end method
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 3e88364..33df06d 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -81,6 +81,8 @@
null));
testCases.add(new TestCase("b/20224106", "B20224106", "run", null, new VerifyError(),
0));
+ testCases.add(new TestCase("b/17410612", "B17410612", "run", null, new VerifyError(),
+ 0));
}
public void runTests() {
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index 6ad8787..847ad0d 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
@@ -73,6 +74,7 @@
else # host
LOCAL_CLANG := $(ART_HOST_CLANG)
LOCAL_CFLAGS := $(ART_HOST_CFLAGS) $(ART_HOST_DEBUG_CFLAGS)
+ LOCAL_ASFLAGS := $(ART_HOST_ASFLAGS)
LOCAL_LDLIBS := $(ART_HOST_LDLIBS) -ldl -lpthread
LOCAL_IS_HOST_MODULE := true
LOCAL_MULTILIB := both
diff --git a/test/Android.libnativebridgetest.mk b/test/Android.libnativebridgetest.mk
index 5a5f725..e8cc7e4 100644
--- a/test/Android.libnativebridgetest.mk
+++ b/test/Android.libnativebridgetest.mk
@@ -60,6 +60,7 @@
else # host
LOCAL_CLANG := $(ART_HOST_CLANG)
LOCAL_CFLAGS := $(ART_HOST_CFLAGS) $(ART_HOST_DEBUG_CFLAGS)
+ LOCAL_ASFLAGS := $(ART_HOST_ASFLAGS)
LOCAL_SHARED_LIBRARIES := libcutils
LOCAL_LDLIBS := $(ART_HOST_LDLIBS) -ldl -lpthread
ifeq ($(HOST_OS),linux)
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index c9e512e..e95f147 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -32,11 +32,14 @@
# an empty file touched in the intermediate directory.
TEST_ART_RUN_TEST_BUILD_RULES :=
+# Dependencies for actually running a run-test.
+TEST_ART_RUN_TEST_DEPENDENCIES := $(DX) $(HOST_OUT_EXECUTABLES)/jasmin $(HOST_OUT_EXECUTABLES)/smali $(HOST_OUT_EXECUTABLES)/dexmerger
+
# Helper to create individual build targets for tests. Must be called with $(eval).
# $(1): the test number
define define-build-art-run-test
dmart_target := $(art_run_tests_dir)/art-run-tests/$(1)/touch
-$$(dmart_target): $(DX) $(HOST_OUT_EXECUTABLES)/jasmin $(HOST_OUT_EXECUTABLES)/smali $(HOST_OUT_EXECUTABLES)/dexmerger
+$$(dmart_target): $(TEST_ART_RUN_TEST_DEPENDENCIES)
$(hide) rm -rf $$(dir $$@) && mkdir -p $$(dir $$@)
$(hide) DX=$(abspath $(DX)) JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) \
SMALI=$(abspath $(HOST_OUT_EXECUTABLES)/smali) \
@@ -269,10 +272,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)))
@@ -299,9 +304,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)))
@@ -328,6 +337,7 @@
118-noimage-dex2oat \
119-noimage-patchoat \
131-structural-change \
+ 137-cfi \
139-register-natives \
454-get-vreg \
455-set-vreg \
@@ -343,6 +353,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
@@ -355,29 +392,6 @@
TEST_ART_BROKEN_DEFAULT_RUN_TESTS :=
-# Known broken tests for Quick's and Optimizing's ARM back ends.
-TEST_ART_BROKEN_ARM_RUN_TESTS := 477-long-to-float-conversion-precision # b/20413424
-
-ifeq ($(TARGET_ARCH),arm)
- ifneq (,$(filter 32,$(ALL_ADDRESS_SIZES)))
- ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
- $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
- $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_ARM_RUN_TESTS),32)
- endif
-endif
-
-ifdef TARGET_2ND_ARCH
- ifeq ($(TARGET_2ND_ARCH),arm)
- ifneq (,$(filter 32,$(ALL_ADDRESS_SIZES)))
- ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
- $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
- $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_ARM_RUN_TESTS),32)
- endif
- endif
-endif
-
-TEST_ART_BROKEN_ARM_RUN_TESTS :=
-
# Known broken tests for the arm64 optimizing compiler backend.
TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS :=
@@ -434,8 +448,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), \
@@ -445,6 +460,19 @@
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 :=
+
# 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/etc/run-test-jar b/test/etc/run-test-jar
index 240ed41..09841bf 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -296,6 +296,10 @@
else
FLAGS="$FLAGS -Xnorelocate"
COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xnorelocate"
+ if [ "$HOST" = "y" ]; then
+ # Increase ulimit to 64MB in case we are running hprof test.
+ ulimit -S 64000 || exit 1
+ fi
fi
if [ "$HOST" = "n" ]; then
diff --git a/test/run-test b/test/run-test
index dc60eda..ed03321 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=""
@@ -96,6 +96,7 @@
gc_verify="false"
gc_stress="false"
always_clean="no"
+never_clean="no"
have_dex2oat="yes"
have_patchoat="yes"
have_image="yes"
@@ -270,6 +271,9 @@
elif [ "x$1" = "x--always-clean" ]; then
always_clean="yes"
shift
+ elif [ "x$1" = "x--never-clean" ]; then
+ never_clean="yes"
+ shift
elif [ "x$1" = "x--dex2oat-swap" ]; then
run_args="${run_args} --dex2oat-swap"
shift
@@ -472,6 +476,7 @@
echo " --gcstress Run with gc stress testing"
echo " --gcverify Run with gc verification"
echo " --always-clean Delete the test files even if the test fails."
+ echo " --never-clean Keep the test files even if the test succeeds."
echo " --android-root [path] The path on target for the android root. (/system by default)."
echo " --dex2oat-swap Use a dex2oat swap file."
) 1>&2
@@ -504,14 +509,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"
@@ -662,7 +673,7 @@
) 1>&2
# Clean up test files.
-if [ "$always_clean" = "yes" -o "$good" = "yes" ]; then
+if [ "$always_clean" = "yes" -o "$good" = "yes" ] && [ "$never_clean" = "no" ]; then
cd "$oldwd"
rm -rf "$tmp_dir"
if [ "$target_mode" = "yes" -a "$build_exit" = "0" ]; then
diff --git a/tools/art b/tools/art
index f167a73..2ee8940 100644
--- a/tools/art
+++ b/tools/art
@@ -97,7 +97,7 @@
-XXlib:$LIBART \
-Xnorelocate \
-Ximage:$ANDROID_ROOT/framework/core.art \
- -Xcompiler-option --include-debug-symbols \
+ -Xcompiler-option --generate-debug-info \
"$@"
EXIT_STATUS=$?
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
new file mode 100755
index 0000000..77e6b1a
--- /dev/null
+++ b/tools/buildbot-build.sh
@@ -0,0 +1,77 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+if [ ! -d art ]; then
+ echo "Script needs to be run at the root of the android tree"
+ exit 1
+fi
+
+common_targets="vogar vogar.jar core-tests apache-harmony-jdwp-tests-hostdex out/host/linux-x86/bin/adb jsr166-tests"
+android_root="/data/local/tmp/system"
+linker="linker"
+mode="target"
+j_arg="-j$(nproc)"
+make_command=
+
+if [[ "$TARGET_PRODUCT" == "armv8" ]]; then
+ linker="linker64"
+fi
+
+if [[ "$ART_TEST_ANDROID_ROOT" != "" ]]; then
+ android_root="$ART_TEST_ANDROID_ROOT"
+fi
+
+while true; do
+ if [[ "$1" == "--host" ]]; then
+ mode="host"
+ shift
+ elif [[ "$1" == "--target" ]]; then
+ mode="target"
+ shift
+ elif [[ "$1" == "--32" ]]; then
+ linker="linker"
+ shift
+ elif [[ "$1" == "--64" ]]; then
+ linker="linker64"
+ shift
+ elif [[ "$1" == "--android-root" ]]; then
+ shift
+ android_root=$1
+ shift
+ elif [[ "$1" == -j* ]]; then
+ j_arg=$1
+ shift
+ elif [[ "$1" == "" ]]; then
+ break
+ fi
+done
+
+if [[ $mode == "host" ]]; then
+ make_command="make $j_arg build-art-host-tests $common_targets"
+ echo "Executing $make_command"
+ $make_command
+elif [[ $mode == "target" ]]; then
+ # We need to provide our own linker in case the linker on the device
+ # is out of date.
+ env="TARGET_GLOBAL_LDFLAGS=-Wl,-dynamic-linker=$android_root/bin/$linker"
+ # Use '-e' to force the override of TARGET_GLOBAL_LDFLAGS.
+ # Also, we build extra tools that will be used by tests, so that
+ # they are compiled with our own linker.
+ make_command="make -e $j_arg build-art-target-tests $common_targets libjavacrypto linker toybox toolbox sh"
+ echo "Executing env $env $make_command"
+ env $env $make_command
+fi
+
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..33735cb
--- /dev/null
+++ b/tools/checker/file_format/checker/parser.py
@@ -0,0 +1,153 @@
+# 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.checker.struct import CheckerFile, TestCase, TestAssertion, RegexExpression
+
+import re
+
+def __isCheckerLine(line):
+ return line.startswith("///") or line.startswith("##")
+
+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, fileName):
+ """ 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.
+ """
+ if not __isCheckerLine(line):
+ return None, None
+
+ # 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
+
+ Logger.fail("Checker assertion could not be parsed", fileName, lineNo)
+
+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, fileName)
+ fnLineOutsideChunk = lambda line, lineNo: \
+ Logger.fail("Checker 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..ff24cc1
--- /dev/null
+++ b/tools/checker/file_format/checker/test.py
@@ -0,0 +1,282 @@
+#!/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)
+ return ParseCheckerStream("<test-file>", "CHECK", io.StringIO(checkerText))
+
+ def assertParses(self, string):
+ checkFile = self.tryParse(string)
+ self.assertEqual(len(checkFile.testCases), 1)
+ self.assertNotEqual(len(checkFile.testCases[0].assertions), 0)
+
+ def assertIgnored(self, string):
+ checkFile = self.tryParse(string)
+ self.assertEqual(len(checkFile.testCases), 1)
+ self.assertEqual(len(checkFile.testCases[0].assertions), 0)
+
+ def assertInvalid(self, string):
+ with self.assertRaises(CheckerException):
+ self.tryParse(string)
+
+ def test_ValidFormat(self):
+ self.assertParses("///CHECK:foo")
+ self.assertParses("##CHECK:bar")
+
+ def test_InvalidFormat(self):
+ self.assertIgnored("CHECK")
+ self.assertIgnored(":CHECK")
+ self.assertIgnored("CHECK:")
+ self.assertIgnored("//CHECK")
+ self.assertIgnored("#CHECK")
+ self.assertInvalid("///CHECK")
+ self.assertInvalid("##CHECK")
+
+ def test_InvalidPrefix(self):
+ self.assertInvalid("///ACHECK:foo")
+ self.assertInvalid("##ACHECK:foo")
+
+ def test_NotFirstOnTheLine(self):
+ self.assertIgnored("A/// CHECK: foo")
+ self.assertIgnored("A # CHECK: foo")
+ self.assertInvalid("/// /// CHECK: foo")
+ self.assertInvalid("## ## CHECK: foo")
+
+ def test_WhitespaceAgnostic(self):
+ self.assertParses(" ///CHECK: foo")
+ self.assertParses("/// CHECK: foo")
+ self.assertParses(" ///CHECK: foo")
+ self.assertParses("/// 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..ca748c7
--- /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/generate-operator-out.py b/tools/generate-operator-out.py
index 2b57222..c74508d 100755
--- a/tools/generate-operator-out.py
+++ b/tools/generate-operator-out.py
@@ -154,10 +154,12 @@
sys.stderr.write('%s\n' % (rest))
Confused(filename, line_number, raw_line)
- if len(enclosing_classes) > 0:
- if is_enum_class:
- enum_value = enum_name + '::' + enum_value
- else:
+ # If the enum is scoped, we must prefix enum value with enum name (which is already prefixed
+ # by enclosing classes).
+ if is_enum_class:
+ enum_value = enum_name + '::' + enum_value
+ else:
+ if len(enclosing_classes) > 0:
enum_value = '::'.join(enclosing_classes) + '::' + enum_value
_ENUMS[enum_name].append((enum_value, enum_text))
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index a387036..b053f0d 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -21,28 +21,35 @@
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: "Failures needing investigation",
+ 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: "Issue with incorrect device time (1970)",
result: EXEC_FAILED,
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"],
+ bug: 20879084
+},
+{
+ description: "Issue with incorrect device time (1970). Test assumes that DateTime.now()
+ is greater then a date in 1998.",
+ result: EXEC_FAILED,
+ modes: [device],
+ names: ["org.apache.harmony.tests.java.util.DateTest#test_Constructor"]
},
{
description: "Failing due to a locale problem on hammerhead.",
@@ -74,7 +81,8 @@
"libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithJarHttpURLConnection",
"org.apache.harmony.luni.tests.internal.net.www.protocol.http.HttpURLConnectionTest",
"org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest",
- "org.apache.harmony.luni.tests.java.net.URLConnectionTest"
+ "org.apache.harmony.luni.tests.java.net.URLConnectionTest",
+ "org.apache.harmony.tests.java.util.ScannerTest#test_Constructor_LReadableByteChannel"
]
},
{
@@ -109,18 +117,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 +126,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
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index a19fd15..344d2ded 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -19,11 +19,16 @@
exit 1
fi
-# Jar containing all the tests.
+# Jar containing jsr166 tests.
+jsr166_test_jar=out/target/common/obj/JAVA_LIBRARIES/jsr166-tests_intermediates/javalib.jar
+
+# Jar containing all the other tests.
test_jar=out/target/common/obj/JAVA_LIBRARIES/core-tests_intermediates/javalib.jar
+
if [ ! -f $test_jar ]; then
- echo "Before running, you must build core-tests and vogar: make core-tests vogar vogar.jar"
+ echo "Before running, you must build core-tests, jsr166-tests and vogar: \
+ make core-tests jsr166-tests vogar vogar.jar"
exit 1
fi
@@ -54,9 +59,10 @@
"org.apache.harmony.tests.java.util"
"org.apache.harmony.tests.java.text"
"org.apache.harmony.tests.javax.security"
- "tests.java.lang.String")
+ "tests.java.lang.String"
+ "jsr166")
# Run the tests using vogar.
echo "Running tests for the following test packages:"
echo ${working_packages[@]} | tr " " "\n"
-vogar $@ --expectations art/tools/libcore_failures.txt --classpath $test_jar ${working_packages[@]}
+vogar $@ --expectations art/tools/libcore_failures.txt --classpath $jsr166_test_jar --classpath $test_jar ${working_packages[@]}