diff options
85 files changed, 2211 insertions, 866 deletions
diff --git a/Android.mk b/Android.mk index 8859d3a285..b8ba9f26b2 100644 --- a/Android.mk +++ b/Android.mk @@ -33,7 +33,7 @@ endif # Don't bother with tests unless there is a test-art*, build-art*, or related target. art_test_bother := false -ifneq (,$(filter %tests test-art% valgrind-test-art% build-art%,$(MAKECMDGOALS))) +ifneq (,$(filter tests test-art% valgrind-test-art% build-art% checkbuild,$(MAKECMDGOALS))) art_test_bother := true endif @@ -119,6 +119,7 @@ TEST_ART_TARGET_SYNC_DEPS := include $(art_path)/build/Android.common_test.mk include $(art_path)/build/Android.gtest.mk include $(art_path)/test/Android.run-test.mk +include $(art_path)/benchmark/Android.mk # Sync test files to the target, depends upon all things that must be pushed to the target. .PHONY: test-art-target-sync diff --git a/benchmark/Android.mk b/benchmark/Android.mk new file mode 100644 index 0000000000..09aca98337 --- /dev/null +++ b/benchmark/Android.mk @@ -0,0 +1,78 @@ +# +# 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. +# + +LOCAL_PATH := $(call my-dir) + +include art/build/Android.common_build.mk + +LIBARTBENCHMARK_COMMON_SRC_FILES := \ + jni-perf/perf_jni.cc \ + scoped-primitive-array/scoped_primitive_array.cc + +# $(1): target or host +define build-libartbenchmark + ifneq ($(1),target) + ifneq ($(1),host) + $$(error expected target or host for argument 1, received $(1)) + endif + endif + + art_target_or_host := $(1) + + include $(CLEAR_VARS) + LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) + LOCAL_MODULE := libartbenchmark + ifeq ($$(art_target_or_host),target) + LOCAL_MODULE_TAGS := tests + endif + LOCAL_SRC_FILES := $(LIBARTBENCHMARK_COMMON_SRC_FILES) + LOCAL_SHARED_LIBRARIES += libart libbacktrace libnativehelper + LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime + LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk + LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk + ifeq ($$(art_target_or_host),target) + $(call set-target-local-clang-vars) + $(call set-target-local-cflags-vars,debug) + LOCAL_SHARED_LIBRARIES += libdl + LOCAL_MULTILIB := both + # LOCAL_MODULE_PATH_32 := $(ART_TARGET_OUT)/$(ART_TARGET_ARCH_32) + # LOCAL_MODULE_PATH_64 := $(ART_TARGET_OUT)/$(ART_TARGET_ARCH_64) + LOCAL_MODULE_TARGET_ARCH := $(ART_SUPPORTED_ARCH) + include $(BUILD_SHARED_LIBRARY) + 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 + include $(BUILD_HOST_SHARED_LIBRARY) + endif + + # Clear locally used variables. + art_target_or_host := +endef + +ifeq ($(ART_BUILD_TARGET),true) + $(eval $(call build-libartbenchmark,target)) +endif +ifeq ($(ART_BUILD_HOST),true) + $(eval $(call build-libartbenchmark,host)) +endif + +# Clear locally used variables. +LOCAL_PATH := +LIBARTBENCHMARK_COMMON_SRC_FILES := diff --git a/test/999-jni-perf/info.txt b/benchmark/jni-perf/info.txt index 010b57be2b..010b57be2b 100644 --- a/test/999-jni-perf/info.txt +++ b/benchmark/jni-perf/info.txt diff --git a/test/999-jni-perf/perf-jni.cc b/benchmark/jni-perf/perf_jni.cc index 51eeb83250..cd8d520f16 100644 --- a/test/999-jni-perf/perf-jni.cc +++ b/benchmark/jni-perf/perf_jni.cc @@ -24,18 +24,14 @@ namespace art { namespace { -extern "C" JNIEXPORT jint JNICALL Java_Main_perfJniEmptyCall(JNIEnv*, jobject) { - return 0; -} +extern "C" JNIEXPORT void JNICALL Java_JniPerfBenchmark_perfJniEmptyCall(JNIEnv*, jobject) {} -extern "C" JNIEXPORT jint JNICALL Java_Main_perfSOACall(JNIEnv*, jobject) { - ScopedObjectAccess soa(Thread::Current()); - return 0; +extern "C" JNIEXPORT void JNICALL Java_JniPerfBenchmark_perfSOACall(JNIEnv* env, jobject) { + ScopedObjectAccess soa(env); } -extern "C" JNIEXPORT jint JNICALL Java_Main_perfSOAUncheckedCall(JNIEnv*, jobject) { +extern "C" JNIEXPORT void JNICALL Java_JniPerfBenchmark_perfSOAUncheckedCall(JNIEnv*, jobject) { ScopedObjectAccessUnchecked soa(Thread::Current()); - return 0; } } // namespace diff --git a/benchmark/jni-perf/src/JniPerfBenchmark.java b/benchmark/jni-perf/src/JniPerfBenchmark.java new file mode 100644 index 0000000000..b1b21ce0ba --- /dev/null +++ b/benchmark/jni-perf/src/JniPerfBenchmark.java @@ -0,0 +1,54 @@ +/* + * 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 com.google.caliper.SimpleBenchmark; + +public class JniPerfBenchmark extends SimpleBenchmark { + private static final String MSG = "ABCDE"; + + native void perfJniEmptyCall(); + native void perfSOACall(); + native void perfSOAUncheckedCall(); + + public void timeFastJNI(int N) { + // TODO: This might be an intrinsic. + for (long i = 0; i < N; i++) { + char c = MSG.charAt(2); + } + } + + public void timeEmptyCall(int N) { + for (long i = 0; i < N; i++) { + perfJniEmptyCall(); + } + } + + public void timeSOACall(int N) { + for (long i = 0; i < N; i++) { + perfSOACall(); + } + } + + public void timeSOAUncheckedCall(int N) { + for (long i = 0; i < N; i++) { + perfSOAUncheckedCall(); + } + } + + { + System.loadLibrary("artbenchmark"); + } +} diff --git a/test/998-scoped-primitive-array/info.txt b/benchmark/scoped-primitive-array/info.txt index 93abb7ce8f..93abb7ce8f 100644 --- a/test/998-scoped-primitive-array/info.txt +++ b/benchmark/scoped-primitive-array/info.txt diff --git a/benchmark/scoped-primitive-array/scoped_primitive_array.cc b/benchmark/scoped-primitive-array/scoped_primitive_array.cc new file mode 100644 index 0000000000..1664157297 --- /dev/null +++ b/benchmark/scoped-primitive-array/scoped_primitive_array.cc @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jni.h" +#include "ScopedPrimitiveArray.h" + +extern "C" JNIEXPORT jlong JNICALL Java_ScopedPrimitiveArrayBenchmark_measureByteArray( + JNIEnv* env, jclass, int reps, jbyteArray arr) { + jlong ret = 0; + for (jint i = 0; i < reps; ++i) { + ScopedByteArrayRO sc(env, arr); + ret += sc[0] + sc[sc.size() - 1]; + } + return ret; +} + +extern "C" JNIEXPORT jlong JNICALL Java_ScopedPrimitiveArrayBenchmark_measureShortArray( + JNIEnv* env, jclass, int reps, jshortArray arr) { + jlong ret = 0; + for (jint i = 0; i < reps; ++i) { + ScopedShortArrayRO sc(env, arr); + ret += sc[0] + sc[sc.size() - 1]; + } + return ret; +} + +extern "C" JNIEXPORT jlong JNICALL Java_ScopedPrimitiveArrayBenchmark_measureIntArray( + JNIEnv* env, jclass, int reps, jintArray arr) { + jlong ret = 0; + for (jint i = 0; i < reps; ++i) { + ScopedIntArrayRO sc(env, arr); + ret += sc[0] + sc[sc.size() - 1]; + } + return ret; +} + +extern "C" JNIEXPORT jlong JNICALL Java_ScopedPrimitiveArrayBenchmark_measureLongArray( + JNIEnv* env, jclass, int reps, jlongArray arr) { + jlong ret = 0; + for (jint i = 0; i < reps; ++i) { + ScopedLongArrayRO sc(env, arr); + ret += sc[0] + sc[sc.size() - 1]; + } + return ret; +} diff --git a/benchmark/scoped-primitive-array/src/ScopedPrimitiveArrayBenchmark.java b/benchmark/scoped-primitive-array/src/ScopedPrimitiveArrayBenchmark.java new file mode 100644 index 0000000000..be276fe48c --- /dev/null +++ b/benchmark/scoped-primitive-array/src/ScopedPrimitiveArrayBenchmark.java @@ -0,0 +1,93 @@ +/* + * 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 com.google.caliper.SimpleBenchmark; + +public class ScopedPrimitiveArrayBenchmark extends SimpleBenchmark { + // Measure adds the first and last element of the array by using ScopedPrimitiveArray. + static native long measureByteArray(int reps, byte[] arr); + static native long measureShortArray(int reps, short[] arr); + static native long measureIntArray(int reps, int[] arr); + static native long measureLongArray(int reps, long[] arr); + + static final int smallLength = 16; + static final int mediumLength = 256; + static final int largeLength = 8096; + static byte[] smallBytes = new byte[smallLength]; + static byte[] mediumBytes = new byte[mediumLength]; + static byte[] largeBytes = new byte[largeLength]; + static short[] smallShorts = new short[smallLength]; + static short[] mediumShorts = new short[mediumLength]; + static short[] largeShorts = new short[largeLength]; + static int[] smallInts = new int[smallLength]; + static int[] mediumInts = new int[mediumLength]; + static int[] largeInts = new int[largeLength]; + static long[] smallLongs = new long[smallLength]; + static long[] mediumLongs = new long[mediumLength]; + static long[] largeLongs = new long[largeLength]; + + public void timeSmallBytes(int reps) { + measureByteArray(reps, smallBytes); + } + + public void timeMediumBytes(int reps) { + measureByteArray(reps, mediumBytes); + } + + public void timeLargeBytes(int reps) { + measureByteArray(reps, largeBytes); + } + + public void timeSmallShorts(int reps) { + measureShortArray(reps, smallShorts); + } + + public void timeMediumShorts(int reps) { + measureShortArray(reps, mediumShorts); + } + + public void timeLargeShorts(int reps) { + measureShortArray(reps, largeShorts); + } + + public void timeSmallInts(int reps) { + measureIntArray(reps, smallInts); + } + + public void timeMediumInts(int reps) { + measureIntArray(reps, mediumInts); + } + + public void timeLargeInts(int reps) { + measureIntArray(reps, largeInts); + } + + public void timeSmallLongs(int reps) { + measureLongArray(reps, smallLongs); + } + + public void timeMediumLongs(int reps) { + measureLongArray(reps, mediumLongs); + } + + public void timeLargeLongs(int reps) { + measureLongArray(reps, largeLongs); + } + + { + System.loadLibrary("artbenchmark"); + } +} diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index acce68bcc6..a4434872da 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -348,16 +348,6 @@ ART_HOST_CFLAGS += $(art_cflags) -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRE ART_HOST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=default $(art_host_cflags) ART_HOST_ASFLAGS += $(art_asflags) -# Disable -Wpessimizing-move: triggered for art/runtime/base/variant_map.h:261 -# Adding this flag to art_clang_cflags doesn't work because -Wall gets added to -# ART_HOST_CFLAGS (as a part of art_cflags) after -# -Wno-pessimizing-move. Instead, add the flag here to both -# ART_TARGET_CLANG_CFLAGS and ART_HOST_CFLAGS -ifeq ($(ART_HOST_CLANG),true) -ART_HOST_CFLAGS += -Wno-pessimizing-move -endif -ART_TARGET_CLANG_CFLAGS += -Wno-pessimizing-move - # The latest clang update trips over many of the files in art and never finishes # compiling for aarch64 with -O3 (or -O2). Drop back to -O1 while we investigate # to stop punishing the build server. diff --git a/compiler/dex/global_value_numbering_test.cc b/compiler/dex/global_value_numbering_test.cc index c8aa9902c3..f2c2e22d6a 100644 --- a/compiler/dex/global_value_numbering_test.cc +++ b/compiler/dex/global_value_numbering_test.cc @@ -202,7 +202,7 @@ class GlobalValueNumberingTest : public testing::Test { for (size_t j = 0u; j != def->num_successors; ++j) { SuccessorBlockInfo* successor_block_info = static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo), - kArenaAllocSuccessor)); + kArenaAllocSuccessors)); successor_block_info->block = j; successor_block_info->key = 0u; // Not used by class init check elimination. bb->successor_blocks.push_back(successor_block_info); @@ -474,7 +474,7 @@ GlobalValueNumberingTestCatch::GlobalValueNumberingTestCatch() BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u); check_bb->successor_block_list_type = kCatch; SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*> - (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor)); + (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors)); successor_block_info->block = catch_handler->id; check_bb->successor_blocks.push_back(successor_block_info); } @@ -2284,7 +2284,7 @@ TEST_F(GlobalValueNumberingTest, NormalPathToCatchEntry) { BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u); check_bb->successor_block_list_type = kCatch; SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*> - (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor)); + (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors)); successor_block_info->block = catch_handler->id; check_bb->successor_blocks.push_back(successor_block_info); BasicBlock* merge_block = cu_.mir_graph->GetBasicBlock(4u); diff --git a/compiler/dex/gvn_dead_code_elimination_test.cc b/compiler/dex/gvn_dead_code_elimination_test.cc index 4df0a8b98d..28c61a8fca 100644 --- a/compiler/dex/gvn_dead_code_elimination_test.cc +++ b/compiler/dex/gvn_dead_code_elimination_test.cc @@ -209,7 +209,7 @@ class GvnDeadCodeEliminationTest : public testing::Test { for (size_t j = 0u; j != def->num_successors; ++j) { SuccessorBlockInfo* successor_block_info = static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo), - kArenaAllocSuccessor)); + kArenaAllocSuccessors)); successor_block_info->block = j; successor_block_info->key = 0u; // Not used by class init check elimination. bb->successor_blocks.push_back(successor_block_info); diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index 7976a9aea8..4efe4af896 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -572,7 +572,7 @@ BasicBlock* MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffs DCHECK(case_block != nullptr); SuccessorBlockInfo* successor_block_info = static_cast<SuccessorBlockInfo*>(arena_->Alloc(sizeof(SuccessorBlockInfo), - kArenaAllocSuccessor)); + kArenaAllocSuccessors)); successor_block_info->block = case_block->id; successor_block_info->key = (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ? @@ -627,7 +627,7 @@ BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffse catches_.insert(catch_block->start_offset); } SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*> - (arena_->Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor)); + (arena_->Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors)); successor_block_info->block = catch_block->id; successor_block_info->key = iterator.GetHandlerTypeIndex(); cur_block->successor_blocks.push_back(successor_block_info); @@ -2178,7 +2178,7 @@ BasicBlock* BasicBlock::Copy(MIRGraph* mir_graph) { result_bb->successor_blocks.reserve(successor_blocks.size()); for (SuccessorBlockInfo* sbi_old : successor_blocks) { SuccessorBlockInfo* sbi_new = static_cast<SuccessorBlockInfo*>( - arena->Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor)); + arena->Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors)); memcpy(sbi_new, sbi_old, sizeof(SuccessorBlockInfo)); result_bb->successor_blocks.push_back(sbi_new); } diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index 1df6a4f9ce..097abdc018 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -379,7 +379,7 @@ class BasicBlock : public DeletableArenaObject<kArenaAllocBasicBlock> { terminated_by_return(), dominates_return(), use_lvn(), first_mir_insn(), last_mir_insn(), data_flow_info(), dominators(), i_dominated(), dom_frontier(), predecessors(allocator->Adapter(kArenaAllocBBPredecessors)), - successor_blocks(allocator->Adapter(kArenaAllocSuccessor)) { + successor_blocks(allocator->Adapter(kArenaAllocSuccessors)) { } BasicBlockId id; BasicBlockId dfs_id; diff --git a/compiler/dex/mir_graph_test.cc b/compiler/dex/mir_graph_test.cc index 49b7511b42..7858681e00 100644 --- a/compiler/dex/mir_graph_test.cc +++ b/compiler/dex/mir_graph_test.cc @@ -79,7 +79,7 @@ class TopologicalSortOrderTest : public testing::Test { for (size_t j = 0u; j != def->num_successors; ++j) { SuccessorBlockInfo* successor_block_info = static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo), - kArenaAllocSuccessor)); + kArenaAllocSuccessors)); successor_block_info->block = j; successor_block_info->key = 0u; // Not used by class init check elimination. bb->successor_blocks.push_back(successor_block_info); diff --git a/compiler/dex/mir_optimization_test.cc b/compiler/dex/mir_optimization_test.cc index 47123ba28c..a0cedff9b8 100644 --- a/compiler/dex/mir_optimization_test.cc +++ b/compiler/dex/mir_optimization_test.cc @@ -118,7 +118,7 @@ class MirOptimizationTest : public testing::Test { for (size_t j = 0u; j != def->num_successors; ++j) { SuccessorBlockInfo* successor_block_info = static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo), - kArenaAllocSuccessor)); + kArenaAllocSuccessors)); successor_block_info->block = j; successor_block_info->key = 0u; // Not used by class init check elimination. bb->successor_blocks.push_back(successor_block_info); @@ -244,7 +244,7 @@ class MirOptimizationTest : public testing::Test { BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u); check_bb->successor_block_list_type = kCatch; SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*> - (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor)); + (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors)); successor_block_info->block = catch_handler->id; check_bb->successor_blocks.push_back(successor_block_info); } diff --git a/compiler/dex/type_inference_test.cc b/compiler/dex/type_inference_test.cc index 872a8d684b..528a18cc99 100644 --- a/compiler/dex/type_inference_test.cc +++ b/compiler/dex/type_inference_test.cc @@ -322,7 +322,7 @@ class TypeInferenceTest : public testing::Test { for (size_t j = 0u; j != def->num_successors; ++j) { SuccessorBlockInfo* successor_block_info = static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo), - kArenaAllocSuccessor)); + kArenaAllocSuccessors)); successor_block_info->block = j; successor_block_info->key = 0u; // Not used by class init check elimination. bb->successor_blocks.push_back(successor_block_info); diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc index 84201c39a7..b0e83b0058 100644 --- a/compiler/optimizing/boolean_simplifier.cc +++ b/compiler/optimizing/boolean_simplifier.cc @@ -42,9 +42,9 @@ void HBooleanSimplifier::TryRemovingNegatedCondition(HBasicBlock* block) { // successor and the successor can only be reached from them. static bool BlocksDoMergeTogether(HBasicBlock* block1, HBasicBlock* block2) { if (!block1->IsSingleGoto() || !block2->IsSingleGoto()) return false; - HBasicBlock* succ1 = block1->GetSuccessors().Get(0); - HBasicBlock* succ2 = block2->GetSuccessors().Get(0); - return succ1 == succ2 && succ1->GetPredecessors().Size() == 2u; + HBasicBlock* succ1 = block1->GetSuccessor(0); + HBasicBlock* succ2 = block2->GetSuccessor(0); + return succ1 == succ2 && succ1->GetPredecessors().size() == 2u; } // Returns true if the outcome of the branching matches the boolean value of @@ -108,7 +108,7 @@ void HBooleanSimplifier::TryRemovingBooleanSelection(HBasicBlock* block) { if (!BlocksDoMergeTogether(true_block, false_block)) { return; } - HBasicBlock* merge_block = true_block->GetSuccessors().Get(0); + HBasicBlock* merge_block = true_block->GetSuccessor(0); if (!merge_block->HasSinglePhi()) { return; } diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc index ebc0adc64a..70ad408fdf 100644 --- a/compiler/optimizing/bounds_check_elimination.cc +++ b/compiler/optimizing/bounds_check_elimination.cc @@ -275,9 +275,8 @@ class ArrayAccessInsideLoopFinder : public ValueObject { // Loop header of loop_info. Exiting loop is normal. return false; } - const GrowableArray<HBasicBlock*>& successors = block->GetSuccessors(); - for (size_t i = 0; i < successors.Size(); i++) { - if (!loop_info->Contains(*successors.Get(i))) { + for (HBasicBlock* successor : block->GetSuccessors()) { + if (!loop_info->Contains(*successor)) { // One of the successors exits the loop. return true; } @@ -797,8 +796,8 @@ class MonotonicValueRange : public ValueRange { 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. + HBasicBlock* dummy_block = if_block->GetSuccessor(0); // True successor. + HBasicBlock* deopt_block = if_block->GetSuccessor(1); // False successor. dummy_block->AddInstruction(new (graph->GetArena()) HGoto()); deopt_block->AddInstruction(new (graph->GetArena()) HGoto()); @@ -845,14 +844,14 @@ class MonotonicValueRange : public ValueRange { DCHECK(header->IsLoopHeader()); HBasicBlock* pre_header = header->GetDominator(); if (loop_entry_test_block_added) { - DCHECK(deopt_block->GetSuccessors().Get(0) == pre_header); + DCHECK(deopt_block->GetSuccessor(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)); + DCHECK_EQ(deopt_block, header->GetDominator()->GetDominator()->GetSuccessor(1)); } HIntConstant* const_instr = graph->GetIntConstant(constant); @@ -926,7 +925,7 @@ class MonotonicValueRange : public ValueRange { DCHECK(header->IsLoopHeader()); HBasicBlock* pre_header = header->GetDominator(); if (loop_entry_test_block_added) { - DCHECK(deopt_block->GetSuccessors().Get(0) == pre_header); + DCHECK(deopt_block->GetSuccessor(0) == pre_header); } else { DCHECK(deopt_block == pre_header); } @@ -1256,11 +1255,11 @@ class BCEVisitor : public HGraphVisitor { HBasicBlock* true_successor = instruction->IfTrueSuccessor(); // There should be no critical edge at this point. - DCHECK_EQ(true_successor->GetPredecessors().Size(), 1u); + DCHECK_EQ(true_successor->GetPredecessors().size(), 1u); HBasicBlock* false_successor = instruction->IfFalseSuccessor(); // There should be no critical edge at this point. - DCHECK_EQ(false_successor->GetPredecessors().Size(), 1u); + DCHECK_EQ(false_successor->GetPredecessors().size(), 1u); ValueRange* left_range = LookupValueRange(left, block); MonotonicValueRange* left_monotonic_range = nullptr; @@ -1468,10 +1467,10 @@ class BCEVisitor : public HGraphVisitor { // Start with input 1. Input 0 is from the incoming block. HInstruction* input1 = phi->InputAt(1); DCHECK(phi->GetBlock()->GetLoopInformation()->IsBackEdge( - *phi->GetBlock()->GetPredecessors().Get(1))); + *phi->GetBlock()->GetPredecessor(1))); for (size_t i = 2, e = phi->InputCount(); i < e; ++i) { DCHECK(phi->GetBlock()->GetLoopInformation()->IsBackEdge( - *phi->GetBlock()->GetPredecessors().Get(i))); + *phi->GetBlock()->GetPredecessor(i))); if (input1 != phi->InputAt(i)) { return false; } diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 7a3aa58149..0a3f083e10 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -398,8 +398,8 @@ void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item) // Find predecessors which are not covered by the same TryItem range. Such // edges enter the try block and will have a TryBoundary inserted. - for (size_t i = 0; i < try_block->GetPredecessors().Size(); ++i) { - HBasicBlock* predecessor = try_block->GetPredecessors().Get(i); + for (size_t i = 0; i < try_block->GetPredecessors().size(); ++i) { + HBasicBlock* predecessor = try_block->GetPredecessor(i); if (predecessor->IsSingleTryBoundary()) { // The edge was already split because of an exit from a neighbouring // TryItem. We split it again and insert an entry point. @@ -426,8 +426,7 @@ void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item) // Find successors which are not covered by the same TryItem range. Such // edges exit the try block and will have a TryBoundary inserted. - for (size_t i = 0; i < try_block->GetSuccessors().Size(); ++i) { - HBasicBlock* successor = try_block->GetSuccessors().Get(i); + for (HBasicBlock* successor : try_block->GetSuccessors()) { if (successor->IsCatchBlock()) { // A catch block is always considered an entry point into its TryItem. // We therefore assume this is an exit point, regardless of whether @@ -479,6 +478,8 @@ bool HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) { graph_->SetEntryBlock(entry_block_); graph_->SetExitBlock(exit_block_); + graph_->SetHasTryCatch(code_item.tries_size_ != 0); + InitializeLocals(code_item.registers_size_); graph_->SetMaximumNumberOfOutVRegs(code_item.outs_size_); diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 1097adbaf3..3bbff6ae17 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -171,7 +171,7 @@ HBasicBlock* CodeGenerator::GetNextBlockToEmit() const { HBasicBlock* CodeGenerator::FirstNonEmptyBlock(HBasicBlock* block) const { while (block->IsSingleJump()) { - block = block->GetSuccessors().Get(0); + block = block->GetSuccessor(0); } return block; } @@ -248,6 +248,12 @@ void CodeGenerator::CompileInternal(CodeAllocator* allocator, bool is_baseline) GenerateSlowPaths(); + // Emit catch stack maps at the end of the stack map stream as expected by the + // runtime exception handler. + if (!is_baseline && graph_->HasTryCatch()) { + RecordCatchBlockInfo(); + } + // Finalize instructions in assember; Finalize(allocator); } @@ -805,6 +811,73 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, stack_map_stream_.EndStackMapEntry(); } +void CodeGenerator::RecordCatchBlockInfo() { + ArenaAllocator* arena = graph_->GetArena(); + + for (size_t i = 0, e = block_order_->Size(); i < e; ++i) { + HBasicBlock* block = block_order_->Get(i); + if (!block->IsCatchBlock()) { + continue; + } + + uint32_t dex_pc = block->GetDexPc(); + uint32_t num_vregs = graph_->GetNumberOfVRegs(); + uint32_t inlining_depth = 0; // Inlining of catch blocks is not supported at the moment. + uint32_t native_pc = GetAddressOf(block); + uint32_t register_mask = 0; // Not used. + + // The stack mask is not used, so we leave it empty. + ArenaBitVector* stack_mask = new (arena) ArenaBitVector(arena, 0, /* expandable */ true); + + stack_map_stream_.BeginStackMapEntry(dex_pc, + native_pc, + register_mask, + stack_mask, + num_vregs, + inlining_depth); + + HInstruction* current_phi = block->GetFirstPhi(); + for (size_t vreg = 0; vreg < num_vregs; ++vreg) { + while (current_phi != nullptr && current_phi->AsPhi()->GetRegNumber() < vreg) { + HInstruction* next_phi = current_phi->GetNext(); + DCHECK(next_phi == nullptr || + current_phi->AsPhi()->GetRegNumber() <= next_phi->AsPhi()->GetRegNumber()) + << "Phis need to be sorted by vreg number to keep this a linear-time loop."; + current_phi = next_phi; + } + + if (current_phi == nullptr || current_phi->AsPhi()->GetRegNumber() != vreg) { + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kNone, 0); + } else { + Location location = current_phi->GetLiveInterval()->ToLocation(); + switch (location.GetKind()) { + case Location::kStackSlot: { + stack_map_stream_.AddDexRegisterEntry( + DexRegisterLocation::Kind::kInStack, location.GetStackIndex()); + break; + } + case Location::kDoubleStackSlot: { + stack_map_stream_.AddDexRegisterEntry( + DexRegisterLocation::Kind::kInStack, location.GetStackIndex()); + stack_map_stream_.AddDexRegisterEntry( + DexRegisterLocation::Kind::kInStack, location.GetHighStackIndex(kVRegSize)); + ++vreg; + DCHECK_LT(vreg, num_vregs); + break; + } + default: { + // All catch phis must be allocated to a stack slot. + LOG(FATAL) << "Unexpected kind " << location.GetKind(); + UNREACHABLE(); + } + } + } + } + + stack_map_stream_.EndStackMapEntry(); + } +} + void CodeGenerator::EmitEnvironment(HEnvironment* environment, SlowPathCode* slow_path) { if (environment == nullptr) return; @@ -975,6 +1048,13 @@ void CodeGenerator::EmitEnvironment(HEnvironment* environment, SlowPathCode* slo } } +bool CodeGenerator::IsImplicitNullCheckAllowed(HNullCheck* null_check) const { + return compiler_options_.GetImplicitNullChecks() && + // Null checks which might throw into a catch block need to save live + // registers and therefore cannot be done implicitly. + !null_check->CanThrowIntoCatchBlock(); +} + bool CodeGenerator::CanMoveNullCheckToUser(HNullCheck* null_check) { HInstruction* first_next_not_move = null_check->GetNextDisregardingMoves(); @@ -990,10 +1070,6 @@ void CodeGenerator::MaybeRecordImplicitNullCheck(HInstruction* instr) { return; } - if (!compiler_options_.GetImplicitNullChecks()) { - return; - } - if (!instr->CanDoImplicitNullCheckOn(instr->InputAt(0))) { return; } @@ -1005,9 +1081,11 @@ void CodeGenerator::MaybeRecordImplicitNullCheck(HInstruction* instr) { // and needs to record the pc. if (first_prev_not_move != nullptr && first_prev_not_move->IsNullCheck()) { HNullCheck* null_check = first_prev_not_move->AsNullCheck(); - // TODO: The parallel moves modify the environment. Their changes need to be reverted - // otherwise the stack maps at the throw point will not be correct. - RecordPcInfo(null_check, null_check->GetDexPc()); + if (IsImplicitNullCheckAllowed(null_check)) { + // TODO: The parallel moves modify the environment. Their changes need to be + // reverted otherwise the stack maps at the throw point will not be correct. + RecordPcInfo(null_check, null_check->GetDexPc()); + } } } diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index b3c4d727e0..a93d07ad50 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -237,6 +237,17 @@ class CodeGenerator { bool CanMoveNullCheckToUser(HNullCheck* null_check); void MaybeRecordImplicitNullCheck(HInstruction* instruction); + // Records a stack map which the runtime might use to set catch phi values + // during exception delivery. + // TODO: Replace with a catch-entering instruction that records the environment. + void RecordCatchBlockInfo(); + + // Returns true if implicit null checks are allowed in the compiler options + // and if the null check is not inside a try block. We currently cannot do + // implicit null checks in that case because we need the NullCheckSlowPath to + // save live registers, which may be needed by the runtime to set catch phis. + bool IsImplicitNullCheckAllowed(HNullCheck* null_check) const; + void AddSlowPath(SlowPathCode* slow_path) { slow_paths_.Add(slow_path); } diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index a4c58b095a..b3e38f0946 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -66,6 +66,10 @@ class NullCheckSlowPathARM : public SlowPathCodeARM { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } arm_codegen->InvokeRuntime( QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc(), this); } @@ -86,6 +90,10 @@ class DivZeroCheckSlowPathARM : public SlowPathCodeARM { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } arm_codegen->InvokeRuntime( QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc(), this); } @@ -150,6 +158,10 @@ class BoundsCheckSlowPathARM : public SlowPathCodeARM { LocationSummary* locations = instruction_->GetLocations(); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } // We're moving two locations to locations that could overlap, so we need a parallel // move resolver. InvokeRuntimeCallingConvention calling_convention; @@ -2741,8 +2753,10 @@ void InstructionCodeGeneratorARM::VisitRem(HRem* rem) { } void LocationsBuilderARM::VisitDivZeroCheck(HDivZeroCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); if (instruction->HasUses()) { locations->SetOut(Location::SameAsFirstInput()); @@ -3495,8 +3509,10 @@ void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instructi } void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); if (instruction->HasUses()) { locations->SetOut(Location::SameAsFirstInput()); @@ -3524,7 +3540,7 @@ void InstructionCodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruct } void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) { - if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) { + if (codegen_->IsImplicitNullCheckAllowed(instruction)) { GenerateImplicitNullCheck(instruction); } else { GenerateExplicitNullCheck(instruction); @@ -3863,8 +3879,10 @@ void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) { } void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); if (instruction->HasUses()) { diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 6b1457bc31..5094f6761a 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -198,6 +198,10 @@ class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 { CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } // We're moving two locations to locations that could overlap, so we need a parallel // move resolver. InvokeRuntimeCallingConvention calling_convention; @@ -226,6 +230,10 @@ class DivZeroCheckSlowPathARM64 : public SlowPathCodeARM64 { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } arm64_codegen->InvokeRuntime( QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes<kQuickThrowDivZero, void, void>(); @@ -338,6 +346,10 @@ class NullCheckSlowPathARM64 : public SlowPathCodeARM64 { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } arm64_codegen->InvokeRuntime( QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes<kQuickThrowNullPointer, void, void>(); @@ -1580,8 +1592,10 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { } void LocationsBuilderARM64::VisitBoundsCheck(HBoundsCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, ARM64EncodableConstantOrRegister(instruction->InputAt(1), instruction)); if (instruction->HasUses()) { @@ -1977,8 +1991,10 @@ void InstructionCodeGeneratorARM64::VisitDiv(HDiv* div) { } void LocationsBuilderARM64::VisitDivZeroCheck(HDivZeroCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); if (instruction->HasUses()) { locations->SetOut(Location::SameAsFirstInput()); @@ -2875,8 +2891,10 @@ void InstructionCodeGeneratorARM64::VisitBooleanNot(HBooleanNot* instruction) { } void LocationsBuilderARM64::VisitNullCheck(HNullCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); if (instruction->HasUses()) { locations->SetOut(Location::SameAsFirstInput()); @@ -2905,7 +2923,7 @@ void InstructionCodeGeneratorARM64::GenerateExplicitNullCheck(HNullCheck* instru } void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) { - if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) { + if (codegen_->IsImplicitNullCheckAllowed(instruction)) { GenerateImplicitNullCheck(instruction); } else { GenerateExplicitNullCheck(instruction); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 10942ef3a5..8d60026b41 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -118,6 +118,10 @@ class BoundsCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { LocationSummary* locations = instruction_->GetLocations(); CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } // We're moving two locations to locations that could overlap, so we need a parallel // move resolver. InvokeRuntimeCallingConvention calling_convention; @@ -151,6 +155,10 @@ class DivZeroCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc(), @@ -269,6 +277,10 @@ class NullCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc(), @@ -1566,8 +1578,10 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) { } void LocationsBuilderMIPS64::VisitBoundsCheck(HBoundsCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); if (instruction->HasUses()) { @@ -1862,8 +1876,10 @@ void InstructionCodeGeneratorMIPS64::VisitDiv(HDiv* instruction) { } void LocationsBuilderMIPS64::VisitDivZeroCheck(HDivZeroCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); if (instruction->HasUses()) { locations->SetOut(Location::SameAsFirstInput()); @@ -2824,8 +2840,10 @@ void InstructionCodeGeneratorMIPS64::VisitBooleanNot(HBooleanNot* instruction) { } void LocationsBuilderMIPS64::VisitNullCheck(HNullCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); if (instruction->HasUses()) { locations->SetOut(Location::SameAsFirstInput()); @@ -2852,7 +2870,7 @@ void InstructionCodeGeneratorMIPS64::GenerateExplicitNullCheck(HNullCheck* instr } void InstructionCodeGeneratorMIPS64::VisitNullCheck(HNullCheck* instruction) { - if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) { + if (codegen_->IsImplicitNullCheckAllowed(instruction)) { GenerateImplicitNullCheck(instruction); } else { GenerateExplicitNullCheck(instruction); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index a5ad226e0b..dc5c86efc6 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -56,6 +56,10 @@ class NullCheckSlowPathX86 : public SlowPathCodeX86 { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } x86_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc(), @@ -78,6 +82,10 @@ class DivZeroCheckSlowPathX86 : public SlowPathCodeX86 { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } x86_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc(), @@ -125,6 +133,10 @@ class BoundsCheckSlowPathX86 : public SlowPathCodeX86 { __ Bind(GetEntryLabel()); // We're moving two locations to locations that could overlap, so we need a parallel // move resolver. + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } InvokeRuntimeCallingConvention calling_convention; x86_codegen->EmitParallelMoves( locations->InAt(0), @@ -3039,8 +3051,10 @@ void InstructionCodeGeneratorX86::VisitRem(HRem* rem) { } void LocationsBuilderX86::VisitDivZeroCheck(HDivZeroCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); switch (instruction->GetType()) { case Primitive::kPrimByte: case Primitive::kPrimChar: @@ -3984,9 +3998,11 @@ void InstructionCodeGeneratorX86::VisitInstanceFieldGet(HInstanceFieldGet* instr } void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - Location loc = codegen_->GetCompilerOptions().GetImplicitNullChecks() + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); + Location loc = codegen_->IsImplicitNullCheckAllowed(instruction) ? Location::RequiresRegister() : Location::Any(); locations->SetInAt(0, loc); @@ -4019,7 +4035,7 @@ void InstructionCodeGeneratorX86::GenerateExplicitNullCheck(HNullCheck* instruct __ cmpl(Address(ESP, obj.GetStackIndex()), Immediate(0)); } else { DCHECK(obj.IsConstant()) << obj; - DCHECK_EQ(obj.GetConstant()->AsIntConstant()->GetValue(), 0); + DCHECK(obj.GetConstant()->IsNullConstant()); __ jmp(slow_path->GetEntryLabel()); return; } @@ -4027,7 +4043,7 @@ void InstructionCodeGeneratorX86::GenerateExplicitNullCheck(HNullCheck* instruct } void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) { - if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) { + if (codegen_->IsImplicitNullCheckAllowed(instruction)) { GenerateImplicitNullCheck(instruction); } else { GenerateExplicitNullCheck(instruction); @@ -4432,8 +4448,10 @@ void InstructionCodeGeneratorX86::VisitArrayLength(HArrayLength* instruction) { } void LocationsBuilderX86::VisitBoundsCheck(HBoundsCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); if (instruction->HasUses()) { diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 0f3eb74c64..0cf1089cf8 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -57,6 +57,10 @@ class NullCheckSlowPathX86_64 : public SlowPathCodeX86_64 { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } x64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc(), @@ -79,6 +83,10 @@ class DivZeroCheckSlowPathX86_64 : public SlowPathCodeX86_64 { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } x64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc(), @@ -177,6 +185,10 @@ class BoundsCheckSlowPathX86_64 : public SlowPathCodeX86_64 { LocationSummary* locations = instruction_->GetLocations(); CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } // We're moving two locations to locations that could overlap, so we need a parallel // move resolver. InvokeRuntimeCallingConvention calling_convention; @@ -3194,8 +3206,10 @@ void InstructionCodeGeneratorX86_64::VisitRem(HRem* rem) { } void LocationsBuilderX86_64::VisitDivZeroCheck(HDivZeroCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::Any()); if (instruction->HasUses()) { locations->SetOut(Location::SameAsFirstInput()); @@ -3748,9 +3762,11 @@ void InstructionCodeGeneratorX86_64::VisitStaticFieldSet(HStaticFieldSet* instru } void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - Location loc = codegen_->GetCompilerOptions().GetImplicitNullChecks() + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); + Location loc = codegen_->IsImplicitNullCheckAllowed(instruction) ? Location::RequiresRegister() : Location::Any(); locations->SetInAt(0, loc); @@ -3783,7 +3799,7 @@ void InstructionCodeGeneratorX86_64::GenerateExplicitNullCheck(HNullCheck* instr __ cmpl(Address(CpuRegister(RSP), obj.GetStackIndex()), Immediate(0)); } else { DCHECK(obj.IsConstant()) << obj; - DCHECK_EQ(obj.GetConstant()->AsIntConstant()->GetValue(), 0); + DCHECK(obj.GetConstant()->IsNullConstant()); __ jmp(slow_path->GetEntryLabel()); return; } @@ -3791,7 +3807,7 @@ void InstructionCodeGeneratorX86_64::GenerateExplicitNullCheck(HNullCheck* instr } void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) { - if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) { + if (codegen_->IsImplicitNullCheckAllowed(instruction)) { GenerateImplicitNullCheck(instruction); } else { GenerateExplicitNullCheck(instruction); @@ -4175,8 +4191,10 @@ void InstructionCodeGeneratorX86_64::VisitArrayLength(HArrayLength* instruction) } void LocationsBuilderX86_64::VisitBoundsCheck(HBoundsCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); if (instruction->HasUses()) { diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc index 4fbb51d43c..72c67f5651 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -561,7 +561,7 @@ TEST(CodegenTest, NonMaterializedCondition) { ASSERT_FALSE(equal->NeedsMaterialization()); auto hook_before_codegen = [](HGraph* graph_in) { - HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors().Get(0); + HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessor(0); HParallelMove* move = new (graph_in->GetArena()) HParallelMove(graph_in->GetArena()); block->InsertInstructionBefore(move, block->GetLastInstruction()); }; @@ -667,7 +667,7 @@ TEST(CodegenTest, MaterializedCondition1) { code_block->AddInstruction(&ret); auto hook_before_codegen = [](HGraph* graph_in) { - HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors().Get(0); + HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessor(0); HParallelMove* move = new (graph_in->GetArena()) HParallelMove(graph_in->GetArena()); block->InsertInstructionBefore(move, block->GetLastInstruction()); }; @@ -733,7 +733,7 @@ TEST(CodegenTest, MaterializedCondition2) { if_false_block->AddInstruction(&ret_ge); auto hook_before_codegen = [](HGraph* graph_in) { - HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors().Get(0); + HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessor(0); HParallelMove* move = new (graph_in->GetArena()) HParallelMove(graph_in->GetArena()); block->InsertInstructionBefore(move, block->GetLastInstruction()); }; diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc index 50cbf5ca77..509478cfad 100644 --- a/compiler/optimizing/dead_code_elimination.cc +++ b/compiler/optimizing/dead_code_elimination.cc @@ -42,8 +42,8 @@ static void MarkReachableBlocks(HBasicBlock* block, ArenaBitVector* visited) { MarkReachableBlocks(if_instruction->IfFalseSuccessor(), visited); } } else { - for (size_t i = 0, e = block->GetSuccessors().Size(); i < e; ++i) { - MarkReachableBlocks(block->GetSuccessors().Get(i), visited); + for (HBasicBlock* successor : block->GetSuccessors()) { + MarkReachableBlocks(successor, visited); } } } @@ -99,12 +99,12 @@ void HDeadCodeElimination::RemoveDeadBlocks() { // Connect successive blocks created by dead branches. Order does not matter. for (HReversePostOrderIterator it(*graph_); !it.Done();) { HBasicBlock* block = it.Current(); - if (block->IsEntryBlock() || block->GetSuccessors().Size() != 1u) { + if (block->IsEntryBlock() || block->GetSuccessors().size() != 1u) { it.Advance(); continue; } - HBasicBlock* successor = block->GetSuccessors().Get(0); - if (successor->IsExitBlock() || successor->GetPredecessors().Size() != 1u) { + HBasicBlock* successor = block->GetSuccessor(0); + if (successor->IsExitBlock() || successor->GetPredecessors().size() != 1u) { it.Advance(); continue; } diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index 847d5a4e9e..074ed71025 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -29,19 +29,16 @@ void GraphChecker::VisitBasicBlock(HBasicBlock* block) { current_block_ = block; // Check consistency with respect to predecessors of `block`. - const GrowableArray<HBasicBlock*>& predecessors = block->GetPredecessors(); std::map<HBasicBlock*, size_t> predecessors_count; - for (size_t i = 0, e = predecessors.Size(); i < e; ++i) { - HBasicBlock* p = predecessors.Get(i); + for (HBasicBlock* p : block->GetPredecessors()) { ++predecessors_count[p]; } for (auto& pc : predecessors_count) { HBasicBlock* p = pc.first; size_t p_count_in_block_predecessors = pc.second; - const GrowableArray<HBasicBlock*>& p_successors = p->GetSuccessors(); size_t block_count_in_p_successors = 0; - for (size_t j = 0, f = p_successors.Size(); j < f; ++j) { - if (p_successors.Get(j) == block) { + for (HBasicBlock* p_successor : p->GetSuccessors()) { + if (p_successor == block) { ++block_count_in_p_successors; } } @@ -55,19 +52,16 @@ void GraphChecker::VisitBasicBlock(HBasicBlock* block) { } // Check consistency with respect to successors of `block`. - const GrowableArray<HBasicBlock*>& successors = block->GetSuccessors(); std::map<HBasicBlock*, size_t> successors_count; - for (size_t i = 0, e = successors.Size(); i < e; ++i) { - HBasicBlock* s = successors.Get(i); + for (HBasicBlock* s : block->GetSuccessors()) { ++successors_count[s]; } for (auto& sc : successors_count) { HBasicBlock* s = sc.first; size_t s_count_in_block_successors = sc.second; - const GrowableArray<HBasicBlock*>& s_predecessors = s->GetPredecessors(); size_t block_count_in_s_predecessors = 0; - for (size_t j = 0, f = s_predecessors.Size(); j < f; ++j) { - if (s_predecessors.Get(j) == block) { + for (HBasicBlock* s_predecessor : s->GetPredecessors()) { + if (s_predecessor == block) { ++block_count_in_s_predecessors; } } @@ -92,8 +86,7 @@ void GraphChecker::VisitBasicBlock(HBasicBlock* block) { // Ensure that only Return(Void) and Throw jump to Exit. An exiting // TryBoundary may be between a Throw and the Exit if the Throw is in a try. if (block->IsExitBlock()) { - for (size_t i = 0, e = block->GetPredecessors().Size(); i < e; ++i) { - HBasicBlock* predecessor = block->GetPredecessors().Get(i); + for (HBasicBlock* predecessor : block->GetPredecessors()) { if (predecessor->IsSingleTryBoundary() && !predecessor->GetLastInstruction()->AsTryBoundary()->IsEntry()) { HBasicBlock* real_predecessor = predecessor->GetSinglePredecessor(); @@ -178,8 +171,7 @@ void GraphChecker::VisitTryBoundary(HTryBoundary* try_boundary) { try_boundary->GetId(), handler->GetBlockId())); } - if (current_block_->GetSuccessors().Contains( - handler, /* start_from */ it.CurrentSuccessorIndex() + 1)) { + if (current_block_->HasSuccessor(handler, it.CurrentSuccessorIndex() + 1)) { AddError(StringPrintf("Exception handler block %d of %s:%d is listed multiple times.", handler->GetBlockId(), try_boundary->DebugName(), @@ -359,15 +351,15 @@ void SSAChecker::VisitBasicBlock(HBasicBlock* block) { // never exceptional successors. const size_t num_normal_successors = block->NumberOfNormalSuccessors(); for (size_t j = 0; j < num_normal_successors; ++j) { - HBasicBlock* successor = block->GetSuccessors().Get(j); + HBasicBlock* successor = block->GetSuccessor(j); if (successor->IsCatchBlock()) { AddError(StringPrintf("Catch block %d is a normal successor of block %d.", successor->GetBlockId(), block->GetBlockId())); } } - for (size_t j = num_normal_successors, e = block->GetSuccessors().Size(); j < e; ++j) { - HBasicBlock* successor = block->GetSuccessors().Get(j); + for (size_t j = num_normal_successors, e = block->GetSuccessors().size(); j < e; ++j) { + HBasicBlock* successor = block->GetSuccessor(j); if (!successor->IsCatchBlock()) { AddError(StringPrintf("Normal block %d is an exceptional successor of block %d.", successor->GetBlockId(), @@ -381,8 +373,8 @@ void SSAChecker::VisitBasicBlock(HBasicBlock* block) { // not accounted for. if (block->NumberOfNormalSuccessors() > 1) { for (size_t j = 0, e = block->NumberOfNormalSuccessors(); j < e; ++j) { - HBasicBlock* successor = block->GetSuccessors().Get(j); - if (successor->GetPredecessors().Size() > 1) { + HBasicBlock* successor = block->GetSuccessor(j); + if (successor->GetPredecessors().size() > 1) { AddError(StringPrintf("Critical edge between blocks %d and %d.", block->GetBlockId(), successor->GetBlockId())); @@ -390,17 +382,6 @@ void SSAChecker::VisitBasicBlock(HBasicBlock* block) { } } - // Check Phi uniqueness (no two Phis with the same type refer to the same register). - for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { - HPhi* phi = it.Current()->AsPhi(); - if (phi->GetNextEquivalentPhiWithSameType() != nullptr) { - std::stringstream type_str; - type_str << phi->GetType(); - AddError(StringPrintf("Equivalent phi (%d) found for VReg %d with type: %s", - phi->GetId(), phi->GetRegNumber(), type_str.str().c_str())); - } - } - // Ensure try membership information is consistent. if (block->IsCatchBlock()) { if (block->IsTryBlock()) { @@ -417,8 +398,7 @@ void SSAChecker::VisitBasicBlock(HBasicBlock* block) { block->GetBlockId())); } } else { - for (size_t i = 0; i < block->GetPredecessors().Size(); ++i) { - HBasicBlock* predecessor = block->GetPredecessors().Get(i); + for (HBasicBlock* predecessor : block->GetPredecessors()) { const HTryBoundary* incoming_try_entry = predecessor->ComputeTryEntryOfSuccessors(); if (block->IsTryBlock()) { const HTryBoundary& stored_try_entry = block->GetTryCatchInformation()->GetTryEntry(); @@ -469,21 +449,21 @@ void SSAChecker::CheckLoop(HBasicBlock* loop_header) { // Ensure the loop header has only one incoming branch and the remaining // predecessors are back edges. - size_t num_preds = loop_header->GetPredecessors().Size(); + size_t num_preds = loop_header->GetPredecessors().size(); if (num_preds < 2) { AddError(StringPrintf( "Loop header %d has less than two predecessors: %zu.", id, num_preds)); } else { - HBasicBlock* first_predecessor = loop_header->GetPredecessors().Get(0); + HBasicBlock* first_predecessor = loop_header->GetPredecessor(0); if (loop_information->IsBackEdge(*first_predecessor)) { AddError(StringPrintf( "First predecessor of loop header %d is a back edge.", id)); } - for (size_t i = 1, e = loop_header->GetPredecessors().Size(); i < e; ++i) { - HBasicBlock* predecessor = loop_header->GetPredecessors().Get(i); + for (size_t i = 1, e = loop_header->GetPredecessors().size(); i < e; ++i) { + HBasicBlock* predecessor = loop_header->GetPredecessor(i); if (!loop_information->IsBackEdge(*predecessor)) { AddError(StringPrintf( "Loop header %d has multiple incoming (non back edge) blocks.", @@ -586,6 +566,35 @@ static Primitive::Type PrimitiveKind(Primitive::Type type) { } } +static bool IsSameSizeConstant(HInstruction* insn1, HInstruction* insn2) { + return insn1->IsConstant() + && insn2->IsConstant() + && Primitive::Is64BitType(insn1->GetType()) == Primitive::Is64BitType(insn2->GetType()); +} + +static bool IsConstantEquivalent(HInstruction* insn1, HInstruction* insn2, BitVector* visited) { + if (insn1->IsPhi() && + insn1->AsPhi()->IsVRegEquivalentOf(insn2) && + insn1->InputCount() == insn2->InputCount()) { + // Testing only one of the two inputs for recursion is sufficient. + if (visited->IsBitSet(insn1->GetId())) { + return true; + } + visited->SetBit(insn1->GetId()); + + for (size_t i = 0, e = insn1->InputCount(); i < e; ++i) { + if (!IsConstantEquivalent(insn1->InputAt(i), insn2->InputAt(i), visited)) { + return false; + } + } + return true; + } else if (IsSameSizeConstant(insn1, insn2)) { + return insn1->AsConstant()->GetValueAsUint64() == insn2->AsConstant()->GetValueAsUint64(); + } else { + return false; + } +} + void SSAChecker::VisitPhi(HPhi* phi) { VisitInstruction(phi); @@ -621,20 +630,19 @@ void SSAChecker::VisitPhi(HPhi* phi) { } else { // Ensure the number of inputs of a non-catch phi is the same as the number // of its predecessors. - const GrowableArray<HBasicBlock*>& predecessors = - phi->GetBlock()->GetPredecessors(); - if (phi->InputCount() != predecessors.Size()) { + const ArenaVector<HBasicBlock*>& predecessors = phi->GetBlock()->GetPredecessors(); + if (phi->InputCount() != predecessors.size()) { AddError(StringPrintf( "Phi %d in block %d has %zu inputs, " "but block %d has %zu predecessors.", phi->GetId(), phi->GetBlock()->GetBlockId(), phi->InputCount(), - phi->GetBlock()->GetBlockId(), predecessors.Size())); + phi->GetBlock()->GetBlockId(), predecessors.size())); } else { // Ensure phi input at index I either comes from the Ith // predecessor or from a block that dominates this predecessor. for (size_t i = 0, e = phi->InputCount(); i < e; ++i) { HInstruction* input = phi->InputAt(i); - HBasicBlock* predecessor = predecessors.Get(i); + HBasicBlock* predecessor = predecessors[i]; if (!(input->GetBlock() == predecessor || input->GetBlock()->Dominates(predecessor))) { AddError(StringPrintf( @@ -646,6 +654,45 @@ void SSAChecker::VisitPhi(HPhi* phi) { } } } + + // Ensure that catch phis are sorted by their vreg number, as required by + // the register allocator and code generator. This does not apply to normal + // phis which can be constructed artifically. + if (phi->IsCatchPhi()) { + HInstruction* next_phi = phi->GetNext(); + if (next_phi != nullptr && phi->GetRegNumber() > next_phi->AsPhi()->GetRegNumber()) { + AddError(StringPrintf("Catch phis %d and %d in block %d are not sorted by their " + "vreg numbers.", + phi->GetId(), + next_phi->GetId(), + phi->GetBlock()->GetBlockId())); + } + } + + // Test phi equivalents. There should not be two of the same type and they + // should only be created for constants which were untyped in DEX. + for (HInstructionIterator phi_it(phi->GetBlock()->GetPhis()); !phi_it.Done(); phi_it.Advance()) { + HPhi* other_phi = phi_it.Current()->AsPhi(); + if (phi != other_phi && phi->GetRegNumber() == other_phi->GetRegNumber()) { + if (phi->GetType() == other_phi->GetType()) { + std::stringstream type_str; + type_str << phi->GetType(); + AddError(StringPrintf("Equivalent phi (%d) found for VReg %d with type: %s.", + phi->GetId(), + phi->GetRegNumber(), + type_str.str().c_str())); + } else { + ArenaBitVector visited(GetGraph()->GetArena(), 0, /* expandable */ true); + if (!IsConstantEquivalent(phi, other_phi, &visited)) { + AddError(StringPrintf("Two phis (%d and %d) found for VReg %d but they " + "are not equivalents of constants.", + phi->GetId(), + other_phi->GetId(), + phi->GetRegNumber())); + } + } + } + } } void SSAChecker::HandleBooleanInput(HInstruction* instruction, size_t input_index) { diff --git a/compiler/optimizing/graph_test.cc b/compiler/optimizing/graph_test.cc index 59d50926ad..7968e88117 100644 --- a/compiler/optimizing/graph_test.cc +++ b/compiler/optimizing/graph_test.cc @@ -99,7 +99,7 @@ TEST(GraphTest, IfSuccessorSimpleJoinBlock1) { ASSERT_NE(false_block, return_block); // Ensure the new block branches to the join block. - ASSERT_EQ(false_block->GetSuccessors().Get(0), return_block); + ASSERT_EQ(false_block->GetSuccessor(0), return_block); } // Test that the successors of an if block stay consistent after a SimplifyCFG. @@ -134,7 +134,7 @@ TEST(GraphTest, IfSuccessorSimpleJoinBlock2) { ASSERT_NE(true_block, return_block); // Ensure the new block branches to the join block. - ASSERT_EQ(true_block->GetSuccessors().Get(0), return_block); + ASSERT_EQ(true_block->GetSuccessor(0), return_block); } // Test that the successors of an if block stay consistent after a SimplifyCFG. @@ -163,12 +163,12 @@ TEST(GraphTest, IfSuccessorMultipleBackEdges1) { ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfFalseSuccessor(), return_block); // Ensure there is only one back edge. - ASSERT_EQ(if_block->GetPredecessors().Size(), 2u); - ASSERT_EQ(if_block->GetPredecessors().Get(0), entry_block); - ASSERT_NE(if_block->GetPredecessors().Get(1), if_block); + ASSERT_EQ(if_block->GetPredecessors().size(), 2u); + ASSERT_EQ(if_block->GetPredecessor(0), entry_block); + ASSERT_NE(if_block->GetPredecessor(1), if_block); // Ensure the new block is the back edge. - ASSERT_EQ(if_block->GetPredecessors().Get(1), + ASSERT_EQ(if_block->GetPredecessor(1), if_block->GetLastInstruction()->AsIf()->IfTrueSuccessor()); } @@ -198,12 +198,12 @@ TEST(GraphTest, IfSuccessorMultipleBackEdges2) { ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfTrueSuccessor(), return_block); // Ensure there is only one back edge. - ASSERT_EQ(if_block->GetPredecessors().Size(), 2u); - ASSERT_EQ(if_block->GetPredecessors().Get(0), entry_block); - ASSERT_NE(if_block->GetPredecessors().Get(1), if_block); + ASSERT_EQ(if_block->GetPredecessors().size(), 2u); + ASSERT_EQ(if_block->GetPredecessor(0), entry_block); + ASSERT_NE(if_block->GetPredecessor(1), if_block); // Ensure the new block is the back edge. - ASSERT_EQ(if_block->GetPredecessors().Get(1), + ASSERT_EQ(if_block->GetPredecessor(1), if_block->GetLastInstruction()->AsIf()->IfFalseSuccessor()); } @@ -238,11 +238,11 @@ TEST(GraphTest, IfSuccessorMultiplePreHeaders1) { ASSERT_EQ(if_instr->IfFalseSuccessor(), return_block); // Ensure there is only one pre header.. - ASSERT_EQ(loop_block->GetPredecessors().Size(), 2u); + ASSERT_EQ(loop_block->GetPredecessors().size(), 2u); // Ensure the new block is the successor of the true block. - ASSERT_EQ(if_instr->IfTrueSuccessor()->GetSuccessors().Size(), 1u); - ASSERT_EQ(if_instr->IfTrueSuccessor()->GetSuccessors().Get(0), + ASSERT_EQ(if_instr->IfTrueSuccessor()->GetSuccessors().size(), 1u); + ASSERT_EQ(if_instr->IfTrueSuccessor()->GetSuccessor(0), loop_block->GetLoopInformation()->GetPreHeader()); } @@ -276,11 +276,11 @@ TEST(GraphTest, IfSuccessorMultiplePreHeaders2) { ASSERT_EQ(if_instr->IfTrueSuccessor(), return_block); // Ensure there is only one pre header.. - ASSERT_EQ(loop_block->GetPredecessors().Size(), 2u); + ASSERT_EQ(loop_block->GetPredecessors().size(), 2u); // Ensure the new block is the successor of the false block. - ASSERT_EQ(if_instr->IfFalseSuccessor()->GetSuccessors().Size(), 1u); - ASSERT_EQ(if_instr->IfFalseSuccessor()->GetSuccessors().Get(0), + ASSERT_EQ(if_instr->IfFalseSuccessor()->GetSuccessors().size(), 1u); + ASSERT_EQ(if_instr->IfFalseSuccessor()->GetSuccessor(0), loop_block->GetLoopInformation()->GetPreHeader()); } diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 069a7a460b..5b8e386fae 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -240,8 +240,7 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { void PrintPredecessors(HBasicBlock* block) { AddIndent(); output_ << "predecessors"; - for (size_t i = 0, e = block->GetPredecessors().Size(); i < e; ++i) { - HBasicBlock* predecessor = block->GetPredecessors().Get(i); + for (HBasicBlock* predecessor : block->GetPredecessors()) { output_ << " \"B" << predecessor->GetBlockId() << "\" "; } if (block->IsEntryBlock() && (disasm_info_ != nullptr)) { @@ -254,7 +253,7 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { AddIndent(); output_ << "successors"; for (size_t i = 0; i < block->NumberOfNormalSuccessors(); ++i) { - HBasicBlock* successor = block->GetSuccessors().Get(i); + HBasicBlock* successor = block->GetSuccessor(i); output_ << " \"B" << successor->GetBlockId() << "\" "; } output_<< std::endl; @@ -263,8 +262,8 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { void PrintExceptionHandlers(HBasicBlock* block) { AddIndent(); output_ << "xhandlers"; - for (size_t i = block->NumberOfNormalSuccessors(); i < block->GetSuccessors().Size(); ++i) { - HBasicBlock* handler = block->GetSuccessors().Get(i); + for (size_t i = block->NumberOfNormalSuccessors(); i < block->GetSuccessors().size(); ++i) { + HBasicBlock* handler = block->GetSuccessor(i); output_ << " \"B" << handler->GetBlockId() << "\" "; } if (block->IsExitBlock() && diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc index 833dfb00a1..5bb4e8e099 100644 --- a/compiler/optimizing/gvn.cc +++ b/compiler/optimizing/gvn.cc @@ -340,8 +340,8 @@ void GlobalValueNumberer::Run() { void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) { ValueSet* set = nullptr; - const GrowableArray<HBasicBlock*>& predecessors = block->GetPredecessors(); - if (predecessors.Size() == 0 || predecessors.Get(0)->IsEntryBlock()) { + const ArenaVector<HBasicBlock*>& predecessors = block->GetPredecessors(); + if (predecessors.size() == 0 || predecessors[0]->IsEntryBlock()) { // The entry block should only accumulate constant instructions, and // the builder puts constants only in the entry block. // Therefore, there is no need to propagate the value set to the next block. @@ -349,8 +349,8 @@ void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) { } else { HBasicBlock* dominator = block->GetDominator(); ValueSet* dominator_set = sets_.Get(dominator->GetBlockId()); - if (dominator->GetSuccessors().Size() == 1) { - DCHECK_EQ(dominator->GetSuccessors().Get(0), block); + if (dominator->GetSuccessors().size() == 1) { + DCHECK_EQ(dominator->GetSuccessor(0), block); set = dominator_set; } else { // We have to copy if the dominator has other successors, or `block` is not a successor @@ -361,9 +361,9 @@ void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) { if (block->IsLoopHeader()) { DCHECK_EQ(block->GetDominator(), block->GetLoopInformation()->GetPreHeader()); set->Kill(side_effects_.GetLoopEffects(block)); - } else if (predecessors.Size() > 1) { - for (size_t i = 0, e = predecessors.Size(); i < e; ++i) { - set->IntersectWith(sets_.Get(predecessors.Get(i)->GetBlockId())); + } else if (predecessors.size() > 1) { + for (HBasicBlock* predecessor : predecessors) { + set->IntersectWith(sets_.Get(predecessor->GetBlockId())); if (set->IsEmpty()) { break; } diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 0547ce832d..efd4fcfc79 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -423,8 +423,8 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, } 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()) { + for (HBasicBlock* predecessor : exit_block->GetPredecessors()) { + if (predecessor->GetLastInstruction()->IsThrow()) { has_throw_predecessor = true; break; } @@ -506,7 +506,7 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, ReferenceTypeInfo::TypeHandle return_handle = handles_->NewHandle(resolved_method->GetReturnType(true /* resolve */, pointer_size)); return_replacement->SetReferenceTypeInfo(ReferenceTypeInfo::Create( - return_handle, return_handle->IsFinal() /* is_exact */)); + return_handle, return_handle->CannotBeAssignedFromOtherTypes() /* is_exact */)); } } diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc index bc4a663b86..ec4a9ec916 100644 --- a/compiler/optimizing/licm_test.cc +++ b/compiler/optimizing/licm_test.cc @@ -63,8 +63,8 @@ class LICMTest : public testing::Test { // Provide boiler-plate instructions. parameter_ = new (&allocator_) HParameterValue(0, Primitive::kPrimNot); entry_->AddInstruction(parameter_); - constant_ = new (&allocator_) HConstant(Primitive::kPrimInt); - loop_preheader_->AddInstruction(constant_); + constant_ = graph_->GetIntConstant(42); + loop_preheader_->AddInstruction(new (&allocator_) HGoto()); loop_header_->AddInstruction(new (&allocator_) HIf(parameter_)); loop_body_->AddInstruction(new (&allocator_) HGoto()); exit_->AddInstruction(new (&allocator_) HExit()); @@ -99,23 +99,6 @@ class LICMTest : public testing::Test { // The actual LICM tests. // -TEST_F(LICMTest, ConstantHoisting) { - BuildLoop(); - - // Populate the loop with instructions: set array to constant. - HInstruction* constant = new (&allocator_) HConstant(Primitive::kPrimDouble); - loop_body_->InsertInstructionBefore(constant, loop_body_->GetLastInstruction()); - HInstruction* set_array = new (&allocator_) HArraySet( - parameter_, constant_, constant, Primitive::kPrimDouble, 0); - loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction()); - - EXPECT_EQ(constant->GetBlock(), loop_body_); - EXPECT_EQ(set_array->GetBlock(), loop_body_); - PerformLICM(); - EXPECT_EQ(constant->GetBlock(), loop_preheader_); - EXPECT_EQ(set_array->GetBlock(), loop_body_); -} - TEST_F(LICMTest, FieldHoisting) { BuildLoop(); diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 650c8e5fed..cc12a10354 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -20,6 +20,7 @@ #include "ssa_builder.h" #include "base/bit_vector-inl.h" #include "base/bit_utils.h" +#include "mirror/class-inl.h" #include "utils/growable_array.h" #include "scoped_thread_state_change.h" @@ -68,8 +69,8 @@ void HGraph::RemoveDeadBlocks(const ArenaBitVector& visited) { if (!visited.IsBitSet(i)) { HBasicBlock* block = blocks_.Get(i); // We only need to update the successor, which might be live. - for (size_t j = 0; j < block->GetSuccessors().Size(); ++j) { - block->GetSuccessors().Get(j)->RemovePredecessor(block); + for (HBasicBlock* successor : block->GetSuccessors()) { + successor->RemovePredecessor(block); } // Remove the block from the list of blocks, so that further analyses // never see it. @@ -86,8 +87,7 @@ void HGraph::VisitBlockForBackEdges(HBasicBlock* block, visited->SetBit(id); visiting->SetBit(id); - for (size_t i = 0; i < block->GetSuccessors().Size(); i++) { - HBasicBlock* successor = block->GetSuccessors().Get(i); + for (HBasicBlock* successor : block->GetSuccessors()) { if (visiting->IsBitSet(successor->GetBlockId())) { successor->AddBackEdge(block); } else { @@ -134,7 +134,7 @@ void HGraph::ClearDominanceInformation() { } void HBasicBlock::ClearDominanceInformation() { - dominated_blocks_.Reset(); + dominated_blocks_.clear(); dominator_ = nullptr; } @@ -143,8 +143,8 @@ void HGraph::ComputeDominanceInformation() { GrowableArray<size_t> visits(arena_, blocks_.Size()); visits.SetSize(blocks_.Size()); reverse_post_order_.Add(entry_block_); - for (size_t i = 0; i < entry_block_->GetSuccessors().Size(); i++) { - VisitBlockForDominatorTree(entry_block_->GetSuccessors().Get(i), entry_block_, &visits); + for (HBasicBlock* successor : entry_block_->GetSuccessors()) { + VisitBlockForDominatorTree(successor, entry_block_, &visits); } } @@ -179,11 +179,11 @@ void HGraph::VisitBlockForDominatorTree(HBasicBlock* block, // Once all the forward edges have been visited, we know the immediate // dominator of the block. We can then start visiting its successors. if (visits->Get(block->GetBlockId()) == - block->GetPredecessors().Size() - block->NumberOfBackEdges()) { + block->GetPredecessors().size() - block->NumberOfBackEdges()) { block->GetDominator()->AddDominatedBlock(block); reverse_post_order_.Add(block); - for (size_t i = 0; i < block->GetSuccessors().Size(); i++) { - VisitBlockForDominatorTree(block->GetSuccessors().Get(i), block, visits); + for (HBasicBlock* successor : block->GetSuccessors()) { + VisitBlockForDominatorTree(successor, block, visits); } } } @@ -224,14 +224,14 @@ void HGraph::SimplifyLoop(HBasicBlock* header) { // Make sure the loop has only one pre header. This simplifies SSA building by having // to just look at the pre header to know which locals are initialized at entry of the // loop. - size_t number_of_incomings = header->GetPredecessors().Size() - info->NumberOfBackEdges(); + size_t number_of_incomings = header->GetPredecessors().size() - info->NumberOfBackEdges(); if (number_of_incomings != 1) { HBasicBlock* pre_header = new (arena_) HBasicBlock(this, header->GetDexPc()); AddBlock(pre_header); pre_header->AddInstruction(new (arena_) HGoto(header->GetDexPc())); - for (size_t pred = 0; pred < header->GetPredecessors().Size(); ++pred) { - HBasicBlock* predecessor = header->GetPredecessors().Get(pred); + for (size_t pred = 0; pred < header->GetPredecessors().size(); ++pred) { + HBasicBlock* predecessor = header->GetPredecessor(pred); if (!info->IsBackEdge(*predecessor)) { predecessor->ReplaceSuccessor(header, pre_header); pred--; @@ -241,13 +241,13 @@ void HGraph::SimplifyLoop(HBasicBlock* header) { } // Make sure the first predecessor of a loop header is the incoming block. - if (info->IsBackEdge(*header->GetPredecessors().Get(0))) { - HBasicBlock* to_swap = header->GetPredecessors().Get(0); - for (size_t pred = 1, e = header->GetPredecessors().Size(); pred < e; ++pred) { - HBasicBlock* predecessor = header->GetPredecessors().Get(pred); + if (info->IsBackEdge(*header->GetPredecessor(0))) { + HBasicBlock* to_swap = header->GetPredecessor(0); + for (size_t pred = 1, e = header->GetPredecessors().size(); pred < e; ++pred) { + HBasicBlock* predecessor = header->GetPredecessor(pred); if (!info->IsBackEdge(*predecessor)) { - header->predecessors_.Put(pred, to_swap); - header->predecessors_.Put(0, predecessor); + header->predecessors_[pred] = to_swap; + header->predecessors_[0] = predecessor; break; } } @@ -267,7 +267,7 @@ void HGraph::SimplifyLoop(HBasicBlock* header) { } static bool CheckIfPredecessorAtIsExceptional(const HBasicBlock& block, size_t pred_idx) { - HBasicBlock* predecessor = block.GetPredecessors().Get(pred_idx); + HBasicBlock* predecessor = block.GetPredecessor(pred_idx); if (!predecessor->EndsWithTryBoundary()) { // Only edges from HTryBoundary can be exceptional. return false; @@ -296,7 +296,7 @@ void HGraph::SimplifyCatchBlocks() { } bool exceptional_predecessors_only = true; - for (size_t j = 0; j < catch_block->GetPredecessors().Size(); ++j) { + for (size_t j = 0; j < catch_block->GetPredecessors().size(); ++j) { if (!CheckIfPredecessorAtIsExceptional(*catch_block, j)) { exceptional_predecessors_only = false; break; @@ -313,9 +313,9 @@ void HGraph::SimplifyCatchBlocks() { // a MOVE_EXCEPTION instruction, as guaranteed by the verifier. DCHECK(!catch_block->GetFirstInstruction()->IsLoadException()); HBasicBlock* normal_block = catch_block->SplitBefore(catch_block->GetFirstInstruction()); - for (size_t j = 0; j < catch_block->GetPredecessors().Size(); ++j) { + for (size_t j = 0; j < catch_block->GetPredecessors().size(); ++j) { if (!CheckIfPredecessorAtIsExceptional(*catch_block, j)) { - catch_block->GetPredecessors().Get(j)->ReplaceSuccessor(catch_block, normal_block); + catch_block->GetPredecessor(j)->ReplaceSuccessor(catch_block, normal_block); --j; } } @@ -337,7 +337,7 @@ void HGraph::ComputeTryBlockInformation() { // Infer try membership from the first predecessor. Having simplified loops, // the first predecessor can never be a back edge and therefore it must have // been visited already and had its try membership set. - HBasicBlock* first_predecessor = block->GetPredecessors().Get(0); + HBasicBlock* first_predecessor = block->GetPredecessor(0); DCHECK(!block->IsLoopHeader() || !block->GetLoopInformation()->IsBackEdge(*first_predecessor)); const HTryBoundary* try_entry = first_predecessor->ComputeTryEntryOfSuccessors(); if (try_entry != nullptr) { @@ -346,16 +346,6 @@ void HGraph::ComputeTryBlockInformation() { } } -bool HGraph::HasTryCatch() const { - for (size_t i = 0, e = blocks_.Size(); i < e; ++i) { - HBasicBlock* block = blocks_.Get(i); - if (block != nullptr && (block->IsTryBlock() || block->IsCatchBlock())) { - return true; - } - } - return false; -} - void HGraph::SimplifyCFG() { // Simplify the CFG for future analysis, and code generation: // (1): Split critical edges. @@ -364,10 +354,10 @@ void HGraph::SimplifyCFG() { HBasicBlock* block = blocks_.Get(i); if (block == nullptr) continue; if (block->NumberOfNormalSuccessors() > 1) { - for (size_t j = 0; j < block->GetSuccessors().Size(); ++j) { - HBasicBlock* successor = block->GetSuccessors().Get(j); + for (size_t j = 0; j < block->GetSuccessors().size(); ++j) { + HBasicBlock* successor = block->GetSuccessor(j); DCHECK(!successor->IsCatchBlock()); - if (successor->GetPredecessors().Size() > 1) { + if (successor->GetPredecessors().size() > 1) { SplitCriticalEdge(block, successor); --j; } @@ -486,8 +476,8 @@ void HLoopInformation::PopulateRecursive(HBasicBlock* block) { blocks_.SetBit(block->GetBlockId()); block->SetInLoop(this); - for (size_t i = 0, e = block->GetPredecessors().Size(); i < e; ++i) { - PopulateRecursive(block->GetPredecessors().Get(i)); + for (HBasicBlock* predecessor : block->GetPredecessors()) { + PopulateRecursive(predecessor); } } @@ -1138,12 +1128,11 @@ HBasicBlock* HBasicBlock::SplitBefore(HInstruction* cursor) { new_block->instructions_.SetBlockOfInstructions(new_block); AddInstruction(new (GetGraph()->GetArena()) HGoto(new_block->GetDexPc())); - for (size_t i = 0, e = GetSuccessors().Size(); i < e; ++i) { - HBasicBlock* successor = GetSuccessors().Get(i); - new_block->successors_.Add(successor); - successor->predecessors_.Put(successor->GetPredecessorIndexOf(this), new_block); + for (HBasicBlock* successor : GetSuccessors()) { + new_block->successors_.push_back(successor); + successor->predecessors_[successor->GetPredecessorIndexOf(this)] = new_block; } - successors_.Reset(); + successors_.clear(); AddSuccessor(new_block); GetGraph()->AddBlock(new_block); @@ -1163,19 +1152,17 @@ HBasicBlock* HBasicBlock::SplitAfter(HInstruction* cursor) { instructions_.last_instruction_ = cursor; new_block->instructions_.SetBlockOfInstructions(new_block); - for (size_t i = 0, e = GetSuccessors().Size(); i < e; ++i) { - HBasicBlock* successor = GetSuccessors().Get(i); - new_block->successors_.Add(successor); - successor->predecessors_.Put(successor->GetPredecessorIndexOf(this), new_block); + for (HBasicBlock* successor : GetSuccessors()) { + new_block->successors_.push_back(successor); + successor->predecessors_[successor->GetPredecessorIndexOf(this)] = new_block; } - successors_.Reset(); + successors_.clear(); - for (size_t i = 0, e = GetDominatedBlocks().Size(); i < e; ++i) { - HBasicBlock* dominated = GetDominatedBlocks().Get(i); + for (HBasicBlock* dominated : GetDominatedBlocks()) { dominated->dominator_ = new_block; - new_block->dominated_blocks_.Add(dominated); + new_block->dominated_blocks_.push_back(dominated); } - dominated_blocks_.Reset(); + dominated_blocks_.clear(); return new_block; } @@ -1228,7 +1215,7 @@ bool HBasicBlock::HasSinglePhi() const { } bool HTryBoundary::HasSameExceptionHandlersAs(const HTryBoundary& other) const { - if (GetBlock()->GetSuccessors().Size() != other.GetBlock()->GetSuccessors().Size()) { + if (GetBlock()->GetSuccessors().size() != other.GetBlock()->GetSuccessors().size()) { return false; } @@ -1288,7 +1275,7 @@ void HBasicBlock::DisconnectAndDelete() { // Dominators must be removed after all the blocks they dominate. This way // a loop header is removed last, a requirement for correct loop information // iteration. - DCHECK(dominated_blocks_.IsEmpty()); + DCHECK(dominated_blocks_.empty()); // Remove the block from all loops it is included in. for (HLoopInformationOutwardIterator it(*this); !it.Done(); it.Advance()) { @@ -1304,36 +1291,34 @@ void HBasicBlock::DisconnectAndDelete() { // Disconnect the block from its predecessors and update their control-flow // instructions. - for (size_t i = 0, e = predecessors_.Size(); i < e; ++i) { - HBasicBlock* predecessor = predecessors_.Get(i); + for (HBasicBlock* predecessor : predecessors_) { HInstruction* last_instruction = predecessor->GetLastInstruction(); predecessor->RemoveInstruction(last_instruction); predecessor->RemoveSuccessor(this); - if (predecessor->GetSuccessors().Size() == 1u) { + if (predecessor->GetSuccessors().size() == 1u) { DCHECK(last_instruction->IsIf()); predecessor->AddInstruction(new (graph_->GetArena()) HGoto(last_instruction->GetDexPc())); } else { // The predecessor has no remaining successors and therefore must be dead. // We deliberately leave it without a control-flow instruction so that the // SSAChecker fails unless it is not removed during the pass too. - DCHECK_EQ(predecessor->GetSuccessors().Size(), 0u); + DCHECK_EQ(predecessor->GetSuccessors().size(), 0u); } } - predecessors_.Reset(); + predecessors_.clear(); // Disconnect the block from its successors and update their phis. - for (size_t i = 0, e = successors_.Size(); i < e; ++i) { - HBasicBlock* successor = successors_.Get(i); + for (HBasicBlock* successor : successors_) { // Delete this block from the list of predecessors. size_t this_index = successor->GetPredecessorIndexOf(this); - successor->predecessors_.DeleteAt(this_index); + successor->predecessors_.erase(successor->predecessors_.begin() + this_index); // Check that `successor` has other predecessors, otherwise `this` is the // dominator of `successor` which violates the order DCHECKed at the top. - DCHECK(!successor->predecessors_.IsEmpty()); + DCHECK(!successor->predecessors_.empty()); // Remove this block's entries in the successor's phis. - if (successor->predecessors_.Size() == 1u) { + if (successor->predecessors_.size() == 1u) { // The successor has just one predecessor left. Replace phis with the only // remaining input. for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) { @@ -1347,7 +1332,7 @@ void HBasicBlock::DisconnectAndDelete() { } } } - successors_.Reset(); + successors_.clear(); // Disconnect from the dominator. dominator_->RemoveDominatedBlock(this); @@ -1361,11 +1346,9 @@ void HBasicBlock::DisconnectAndDelete() { void HBasicBlock::MergeWith(HBasicBlock* other) { DCHECK_EQ(GetGraph(), other->GetGraph()); - DCHECK(GetDominatedBlocks().Contains(other)); - DCHECK_EQ(GetSuccessors().Size(), 1u); - DCHECK_EQ(GetSuccessors().Get(0), other); - DCHECK_EQ(other->GetPredecessors().Size(), 1u); - DCHECK_EQ(other->GetPredecessors().Get(0), this); + DCHECK(ContainsElement(dominated_blocks_, other)); + DCHECK_EQ(GetSingleSuccessor(), other); + DCHECK_EQ(other->GetSinglePredecessor(), this); DCHECK(other->GetPhis().IsEmpty()); // Move instructions from `other` to `this`. @@ -1385,24 +1368,23 @@ void HBasicBlock::MergeWith(HBasicBlock* other) { } // Update links to the successors of `other`. - successors_.Reset(); - while (!other->successors_.IsEmpty()) { - HBasicBlock* successor = other->successors_.Get(0); + successors_.clear(); + while (!other->successors_.empty()) { + HBasicBlock* successor = other->GetSuccessor(0); successor->ReplacePredecessor(other, this); } // Update the dominator tree. - dominated_blocks_.Delete(other); - for (size_t i = 0, e = other->GetDominatedBlocks().Size(); i < e; ++i) { - HBasicBlock* dominated = other->GetDominatedBlocks().Get(i); - dominated_blocks_.Add(dominated); + RemoveDominatedBlock(other); + for (HBasicBlock* dominated : other->GetDominatedBlocks()) { + dominated_blocks_.push_back(dominated); dominated->SetDominator(this); } - other->dominated_blocks_.Reset(); + other->dominated_blocks_.clear(); other->dominator_ = nullptr; // Clear the list of predecessors of `other` in preparation of deleting it. - other->predecessors_.Reset(); + other->predecessors_.clear(); // Delete `other` from the graph. The function updates reverse post order. graph_->DeleteDeadBlock(other); @@ -1411,11 +1393,10 @@ void HBasicBlock::MergeWith(HBasicBlock* other) { void HBasicBlock::MergeWithInlined(HBasicBlock* other) { DCHECK_NE(GetGraph(), other->GetGraph()); - DCHECK(GetDominatedBlocks().IsEmpty()); - DCHECK(GetSuccessors().IsEmpty()); + DCHECK(GetDominatedBlocks().empty()); + DCHECK(GetSuccessors().empty()); DCHECK(!EndsWithControlFlowInstruction()); - DCHECK_EQ(other->GetPredecessors().Size(), 1u); - DCHECK(other->GetPredecessors().Get(0)->IsEntryBlock()); + DCHECK(other->GetSinglePredecessor()->IsEntryBlock()); DCHECK(other->GetPhis().IsEmpty()); DCHECK(!other->IsInLoop()); @@ -1424,34 +1405,33 @@ void HBasicBlock::MergeWithInlined(HBasicBlock* other) { other->instructions_.SetBlockOfInstructions(this); // Update links to the successors of `other`. - successors_.Reset(); - while (!other->successors_.IsEmpty()) { - HBasicBlock* successor = other->successors_.Get(0); + successors_.clear(); + while (!other->successors_.empty()) { + HBasicBlock* successor = other->GetSuccessor(0); successor->ReplacePredecessor(other, this); } // Update the dominator tree. - for (size_t i = 0, e = other->GetDominatedBlocks().Size(); i < e; ++i) { - HBasicBlock* dominated = other->GetDominatedBlocks().Get(i); - dominated_blocks_.Add(dominated); + for (HBasicBlock* dominated : other->GetDominatedBlocks()) { + dominated_blocks_.push_back(dominated); dominated->SetDominator(this); } - other->dominated_blocks_.Reset(); + other->dominated_blocks_.clear(); other->dominator_ = nullptr; other->graph_ = nullptr; } void HBasicBlock::ReplaceWith(HBasicBlock* other) { - while (!GetPredecessors().IsEmpty()) { - HBasicBlock* predecessor = GetPredecessors().Get(0); + while (!GetPredecessors().empty()) { + HBasicBlock* predecessor = GetPredecessor(0); predecessor->ReplaceSuccessor(this, other); } - while (!GetSuccessors().IsEmpty()) { - HBasicBlock* successor = GetSuccessors().Get(0); + while (!GetSuccessors().empty()) { + HBasicBlock* successor = GetSuccessor(0); successor->ReplacePredecessor(this, other); } - for (size_t i = 0; i < dominated_blocks_.Size(); ++i) { - other->AddDominatedBlock(dominated_blocks_.Get(i)); + for (HBasicBlock* dominated : GetDominatedBlocks()) { + other->AddDominatedBlock(dominated); } GetDominator()->ReplaceDominatedBlock(this, other); other->SetDominator(GetDominator()); @@ -1474,9 +1454,9 @@ static void MakeRoomFor(GrowableArray<HBasicBlock*>* blocks, void HGraph::DeleteDeadBlock(HBasicBlock* block) { DCHECK_EQ(block->GetGraph(), this); - DCHECK(block->GetSuccessors().IsEmpty()); - DCHECK(block->GetPredecessors().IsEmpty()); - DCHECK(block->GetDominatedBlocks().IsEmpty()); + DCHECK(block->GetSuccessors().empty()); + DCHECK(block->GetPredecessors().empty()); + DCHECK(block->GetDominatedBlocks().empty()); DCHECK(block->GetDominator() == nullptr); for (HBackwardInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { @@ -1550,16 +1530,16 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { HBasicBlock* at = invoke->GetBlock(); HBasicBlock* to = at->SplitAfter(invoke); - HBasicBlock* first = entry_block_->GetSuccessors().Get(0); + HBasicBlock* first = entry_block_->GetSuccessor(0); DCHECK(!first->IsInLoop()); at->MergeWithInlined(first); exit_block_->ReplaceWith(to); // Update all predecessors of the exit block (now the `to` block) // to not `HReturn` but `HGoto` instead. - bool returns_void = to->GetPredecessors().Get(0)->GetLastInstruction()->IsReturnVoid(); - if (to->GetPredecessors().Size() == 1) { - HBasicBlock* predecessor = to->GetPredecessors().Get(0); + bool returns_void = to->GetPredecessor(0)->GetLastInstruction()->IsReturnVoid(); + if (to->GetPredecessors().size() == 1) { + HBasicBlock* predecessor = to->GetPredecessor(0); HInstruction* last = predecessor->GetLastInstruction(); if (!returns_void) { return_value = last->InputAt(0); @@ -1573,8 +1553,7 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke->GetType()), to->GetDexPc()); to->AddPhi(return_value->AsPhi()); } - for (size_t i = 0, e = to->GetPredecessors().Size(); i < e; ++i) { - HBasicBlock* predecessor = to->GetPredecessors().Get(i); + for (HBasicBlock* predecessor : to->GetPredecessors()) { HInstruction* last = predecessor->GetLastInstruction(); if (!returns_void) { return_value->AsPhi()->AddInput(last->InputAt(0)); @@ -1726,8 +1705,8 @@ void HGraph::TransformLoopHeaderForBCE(HBasicBlock* header) { AddBlock(new_pre_header); header->ReplacePredecessor(pre_header, new_pre_header); - pre_header->successors_.Reset(); - pre_header->dominated_blocks_.Reset(); + pre_header->successors_.clear(); + pre_header->dominated_blocks_.clear(); pre_header->AddSuccessor(if_block); if_block->AddSuccessor(dummy_block); // True successor @@ -1735,15 +1714,15 @@ void HGraph::TransformLoopHeaderForBCE(HBasicBlock* header) { dummy_block->AddSuccessor(new_pre_header); deopt_block->AddSuccessor(new_pre_header); - pre_header->dominated_blocks_.Add(if_block); + pre_header->dominated_blocks_.push_back(if_block); if_block->SetDominator(pre_header); - if_block->dominated_blocks_.Add(dummy_block); + if_block->dominated_blocks_.push_back(dummy_block); dummy_block->SetDominator(if_block); - if_block->dominated_blocks_.Add(deopt_block); + if_block->dominated_blocks_.push_back(deopt_block); deopt_block->SetDominator(if_block); - if_block->dominated_blocks_.Add(new_pre_header); + if_block->dominated_blocks_.push_back(new_pre_header); new_pre_header->SetDominator(if_block); - new_pre_header->dominated_blocks_.Add(header); + new_pre_header->dominated_blocks_.push_back(header); header->SetDominator(new_pre_header); size_t index_of_header = 0; @@ -1785,7 +1764,7 @@ void HInstruction::SetReferenceTypeInfo(ReferenceTypeInfo rti) { DCHECK(upper_bound_rti.IsSupertypeOf(rti)) << " upper_bound_rti: " << upper_bound_rti << " rti: " << rti; - DCHECK(!upper_bound_rti.GetTypeHandle()->IsFinal() || rti.IsExact()); + DCHECK(!upper_bound_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes() || rti.IsExact()); } } reference_type_info_ = rti; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 23d605b7b5..d52a4f7575 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -17,11 +17,13 @@ #ifndef ART_COMPILER_OPTIMIZING_NODES_H_ #define ART_COMPILER_OPTIMIZING_NODES_H_ +#include <algorithm> #include <array> #include <type_traits> #include "base/arena_containers.h" #include "base/arena_object.h" +#include "base/stl_util.h" #include "dex/compiler_enums.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "handle.h" @@ -155,6 +157,7 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { number_of_in_vregs_(0), temporaries_vreg_slots_(0), has_bounds_checks_(false), + has_try_catch_(false), debuggable_(debuggable), current_instruction_id_(start_instruction_id), dex_file_(dex_file), @@ -280,7 +283,6 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { } uint16_t GetNumberOfVRegs() const { - DCHECK(!in_ssa_form_); return number_of_vregs_; } @@ -358,8 +360,8 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { return instruction_set_; } - // TODO: Remove once the full compilation pipeline is enabled for try/catch. - bool HasTryCatch() const; + bool HasTryCatch() const { return has_try_catch_; } + void SetHasTryCatch(bool value) { has_try_catch_ = value; } private: void VisitBlockForDominatorTree(HBasicBlock* block, @@ -431,6 +433,10 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { // Has bounds checks. We can totally skip BCE if it's false. bool has_bounds_checks_; + // Flag whether there are any try/catch blocks in the graph. We will skip + // try/catch-related passes if false. + bool has_try_catch_; + // Indicates whether the graph should be compiled in a way that // ensures full debuggability. If false, we can apply more // aggressive optimizations that may limit the level of debugging. @@ -630,26 +636,44 @@ class HBasicBlock : public ArenaObject<kArenaAllocBasicBlock> { public: HBasicBlock(HGraph* graph, uint32_t dex_pc = kNoDexPc) : graph_(graph), - predecessors_(graph->GetArena(), kDefaultNumberOfPredecessors), - successors_(graph->GetArena(), kDefaultNumberOfSuccessors), + predecessors_(graph->GetArena()->Adapter(kArenaAllocPredecessors)), + successors_(graph->GetArena()->Adapter(kArenaAllocSuccessors)), loop_information_(nullptr), dominator_(nullptr), - dominated_blocks_(graph->GetArena(), kDefaultNumberOfDominatedBlocks), + dominated_blocks_(graph->GetArena()->Adapter(kArenaAllocDominated)), block_id_(-1), dex_pc_(dex_pc), lifetime_start_(kNoLifetime), lifetime_end_(kNoLifetime), - try_catch_information_(nullptr) {} + try_catch_information_(nullptr) { + predecessors_.reserve(kDefaultNumberOfPredecessors); + successors_.reserve(kDefaultNumberOfSuccessors); + dominated_blocks_.reserve(kDefaultNumberOfDominatedBlocks); + } - const GrowableArray<HBasicBlock*>& GetPredecessors() const { + const ArenaVector<HBasicBlock*>& GetPredecessors() const { return predecessors_; } - const GrowableArray<HBasicBlock*>& GetSuccessors() const { + HBasicBlock* GetPredecessor(size_t pred_idx) const { + DCHECK_LT(pred_idx, predecessors_.size()); + return predecessors_[pred_idx]; + } + + const ArenaVector<HBasicBlock*>& GetSuccessors() const { return successors_; } - const GrowableArray<HBasicBlock*>& GetDominatedBlocks() const { + HBasicBlock* GetSuccessor(size_t succ_idx) const { + DCHECK_LT(succ_idx, successors_.size()); + return successors_[succ_idx]; + } + + bool HasSuccessor(const HBasicBlock* block, size_t start_from = 0u) { + return ContainsElement(successors_, block, start_from); + } + + const ArenaVector<HBasicBlock*>& GetDominatedBlocks() const { return dominated_blocks_; } @@ -689,18 +713,16 @@ class HBasicBlock : public ArenaObject<kArenaAllocBasicBlock> { HBasicBlock* GetDominator() const { return dominator_; } void SetDominator(HBasicBlock* dominator) { dominator_ = dominator; } - void AddDominatedBlock(HBasicBlock* block) { dominated_blocks_.Add(block); } - void RemoveDominatedBlock(HBasicBlock* block) { dominated_blocks_.Delete(block); } + void AddDominatedBlock(HBasicBlock* block) { dominated_blocks_.push_back(block); } + + void RemoveDominatedBlock(HBasicBlock* block) { + RemoveElement(dominated_blocks_, block); + } + void ReplaceDominatedBlock(HBasicBlock* existing, HBasicBlock* new_block) { - for (size_t i = 0, e = dominated_blocks_.Size(); i < e; ++i) { - if (dominated_blocks_.Get(i) == existing) { - dominated_blocks_.Put(i, new_block); - return; - } - } - LOG(FATAL) << "Unreachable"; - UNREACHABLE(); + ReplaceElement(dominated_blocks_, existing, new_block); } + void ClearDominanceInformation(); int NumberOfBackEdges() const { @@ -715,24 +737,22 @@ class HBasicBlock : public ArenaObject<kArenaAllocBasicBlock> { const HInstructionList& GetPhis() const { return phis_; } void AddSuccessor(HBasicBlock* block) { - successors_.Add(block); - block->predecessors_.Add(this); + successors_.push_back(block); + block->predecessors_.push_back(this); } void ReplaceSuccessor(HBasicBlock* existing, HBasicBlock* new_block) { size_t successor_index = GetSuccessorIndexOf(existing); - DCHECK_NE(successor_index, static_cast<size_t>(-1)); existing->RemovePredecessor(this); - new_block->predecessors_.Add(this); - successors_.Put(successor_index, new_block); + new_block->predecessors_.push_back(this); + successors_[successor_index] = new_block; } void ReplacePredecessor(HBasicBlock* existing, HBasicBlock* new_block) { size_t predecessor_index = GetPredecessorIndexOf(existing); - DCHECK_NE(predecessor_index, static_cast<size_t>(-1)); existing->RemoveSuccessor(this); - new_block->successors_.Add(this); - predecessors_.Put(predecessor_index, new_block); + new_block->successors_.push_back(this); + predecessors_[predecessor_index] = new_block; } // Insert `this` between `predecessor` and `successor. This method @@ -740,85 +760,69 @@ class HBasicBlock : public ArenaObject<kArenaAllocBasicBlock> { // `predecessor` and `successor`. void InsertBetween(HBasicBlock* predecessor, HBasicBlock* successor) { size_t predecessor_index = successor->GetPredecessorIndexOf(predecessor); - DCHECK_NE(predecessor_index, static_cast<size_t>(-1)); size_t successor_index = predecessor->GetSuccessorIndexOf(successor); - DCHECK_NE(successor_index, static_cast<size_t>(-1)); - successor->predecessors_.Put(predecessor_index, this); - predecessor->successors_.Put(successor_index, this); - successors_.Add(successor); - predecessors_.Add(predecessor); + successor->predecessors_[predecessor_index] = this; + predecessor->successors_[successor_index] = this; + successors_.push_back(successor); + predecessors_.push_back(predecessor); } void RemovePredecessor(HBasicBlock* block) { - predecessors_.Delete(block); + predecessors_.erase(predecessors_.begin() + GetPredecessorIndexOf(block)); } void RemoveSuccessor(HBasicBlock* block) { - successors_.Delete(block); + successors_.erase(successors_.begin() + GetSuccessorIndexOf(block)); } void ClearAllPredecessors() { - predecessors_.Reset(); + predecessors_.clear(); } void AddPredecessor(HBasicBlock* block) { - predecessors_.Add(block); - block->successors_.Add(this); + predecessors_.push_back(block); + block->successors_.push_back(this); } void SwapPredecessors() { - DCHECK_EQ(predecessors_.Size(), 2u); - HBasicBlock* temp = predecessors_.Get(0); - predecessors_.Put(0, predecessors_.Get(1)); - predecessors_.Put(1, temp); + DCHECK_EQ(predecessors_.size(), 2u); + std::swap(predecessors_[0], predecessors_[1]); } void SwapSuccessors() { - DCHECK_EQ(successors_.Size(), 2u); - HBasicBlock* temp = successors_.Get(0); - successors_.Put(0, successors_.Get(1)); - successors_.Put(1, temp); + DCHECK_EQ(successors_.size(), 2u); + std::swap(successors_[0], successors_[1]); } size_t GetPredecessorIndexOf(HBasicBlock* predecessor) const { - for (size_t i = 0, e = predecessors_.Size(); i < e; ++i) { - if (predecessors_.Get(i) == predecessor) { - return i; - } - } - return -1; + return IndexOfElement(predecessors_, predecessor); } size_t GetSuccessorIndexOf(HBasicBlock* successor) const { - for (size_t i = 0, e = successors_.Size(); i < e; ++i) { - if (successors_.Get(i) == successor) { - return i; - } - } - return -1; + return IndexOfElement(successors_, successor); } HBasicBlock* GetSinglePredecessor() const { - DCHECK_EQ(GetPredecessors().Size(), 1u); - return GetPredecessors().Get(0); + DCHECK_EQ(GetPredecessors().size(), 1u); + return GetPredecessor(0); } HBasicBlock* GetSingleSuccessor() const { - DCHECK_EQ(GetSuccessors().Size(), 1u); - return GetSuccessors().Get(0); + DCHECK_EQ(GetSuccessors().size(), 1u); + return GetSuccessor(0); } // Returns whether the first occurrence of `predecessor` in the list of // predecessors is at index `idx`. bool IsFirstIndexOfPredecessor(HBasicBlock* predecessor, size_t idx) const { - DCHECK_EQ(GetPredecessors().Get(idx), predecessor); + DCHECK_EQ(GetPredecessor(idx), predecessor); return GetPredecessorIndexOf(predecessor) == idx; } // Returns the number of non-exceptional successors. SsaChecker ensures that // these are stored at the beginning of the successor list. size_t NumberOfNormalSuccessors() const { - return EndsWithTryBoundary() ? 1 : GetSuccessors().Size(); + return EndsWithTryBoundary() ? 1 : GetSuccessors().size(); } // Split the block into two blocks just before `cursor`. Returns the newly @@ -883,8 +887,7 @@ class HBasicBlock : public ArenaObject<kArenaAllocBasicBlock> { bool IsLoopPreHeaderFirstPredecessor() const { DCHECK(IsLoopHeader()); - DCHECK(!GetPredecessors().IsEmpty()); - return GetPredecessors().Get(0) == GetLoopInformation()->GetPreHeader(); + return GetPredecessor(0) == GetLoopInformation()->GetPreHeader(); } HLoopInformation* GetLoopInformation() const { @@ -954,13 +957,13 @@ class HBasicBlock : public ArenaObject<kArenaAllocBasicBlock> { private: HGraph* graph_; - GrowableArray<HBasicBlock*> predecessors_; - GrowableArray<HBasicBlock*> successors_; + ArenaVector<HBasicBlock*> predecessors_; + ArenaVector<HBasicBlock*> successors_; HInstructionList instructions_; HInstructionList phis_; HLoopInformation* loop_information_; HBasicBlock* dominator_; - GrowableArray<HBasicBlock*> dominated_blocks_; + ArenaVector<HBasicBlock*> dominated_blocks_; int block_id_; // The dex program counter of the first instruction of this block. const uint32_t dex_pc_; @@ -2188,6 +2191,8 @@ class HConstant : public HExpression<0> { virtual bool IsZero() const { return false; } virtual bool IsOne() const { return false; } + virtual uint64_t GetValueAsUint64() const = 0; + DECLARE_INSTRUCTION(Constant); private: @@ -2200,6 +2205,8 @@ class HNullConstant : public HConstant { return true; } + uint64_t GetValueAsUint64() const OVERRIDE { return 0; } + size_t ComputeHashCode() const OVERRIDE { return 0; } DECLARE_INSTRUCTION(NullConstant); @@ -2217,6 +2224,8 @@ class HIntConstant : public HConstant { public: int32_t GetValue() const { return value_; } + uint64_t GetValueAsUint64() const OVERRIDE { return static_cast<uint64_t>(value_); } + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { DCHECK(other->IsIntConstant()); return other->AsIntConstant()->value_ == value_; @@ -2248,6 +2257,8 @@ class HLongConstant : public HConstant { public: int64_t GetValue() const { return value_; } + uint64_t GetValueAsUint64() const OVERRIDE { return value_; } + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { DCHECK(other->IsLongConstant()); return other->AsLongConstant()->value_ == value_; @@ -2283,11 +2294,11 @@ class HIf : public HTemplateInstruction<1> { bool IsControlFlow() const OVERRIDE { return true; } HBasicBlock* IfTrueSuccessor() const { - return GetBlock()->GetSuccessors().Get(0); + return GetBlock()->GetSuccessor(0); } HBasicBlock* IfFalseSuccessor() const { - return GetBlock()->GetSuccessors().Get(1); + return GetBlock()->GetSuccessor(1); } DECLARE_INSTRUCTION(If); @@ -2315,14 +2326,13 @@ class HTryBoundary : public HTemplateInstruction<0> { bool IsControlFlow() const OVERRIDE { return true; } // Returns the block's non-exceptional successor (index zero). - HBasicBlock* GetNormalFlowSuccessor() const { return GetBlock()->GetSuccessors().Get(0); } + HBasicBlock* GetNormalFlowSuccessor() const { return GetBlock()->GetSuccessor(0); } // Returns whether `handler` is among its exception handlers (non-zero index // successors). bool HasExceptionHandler(const HBasicBlock& handler) const { DCHECK(handler.IsCatchBlock()); - return GetBlock()->GetSuccessors().Contains( - const_cast<HBasicBlock*>(&handler), /* start_from */ 1); + return GetBlock()->HasSuccessor(&handler, 1u /* Skip first successor. */); } // If not present already, adds `handler` to its block's list of exception @@ -2352,8 +2362,8 @@ class HExceptionHandlerIterator : public ValueObject { explicit HExceptionHandlerIterator(const HTryBoundary& try_boundary) : block_(*try_boundary.GetBlock()), index_(block_.NumberOfNormalSuccessors()) {} - bool Done() const { return index_ == block_.GetSuccessors().Size(); } - HBasicBlock* Current() const { return block_.GetSuccessors().Get(index_); } + bool Done() const { return index_ == block_.GetSuccessors().size(); } + HBasicBlock* Current() const { return block_.GetSuccessor(index_); } size_t CurrentSuccessorIndex() const { return index_; } void Advance() { ++index_; } @@ -2868,10 +2878,13 @@ class HFloatConstant : public HConstant { public: float GetValue() const { return value_; } + uint64_t GetValueAsUint64() const OVERRIDE { + return static_cast<uint64_t>(bit_cast<uint32_t, float>(value_)); + } + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { DCHECK(other->IsFloatConstant()); - return bit_cast<uint32_t, float>(other->AsFloatConstant()->value_) == - bit_cast<uint32_t, float>(value_); + return other->AsFloatConstant()->GetValueAsUint64() == GetValueAsUint64(); } size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); } @@ -2909,10 +2922,11 @@ class HDoubleConstant : public HConstant { public: double GetValue() const { return value_; } + uint64_t GetValueAsUint64() const OVERRIDE { return bit_cast<uint64_t, double>(value_); } + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { DCHECK(other->IsDoubleConstant()); - return bit_cast<uint64_t, double>(other->AsDoubleConstant()->value_) == - bit_cast<uint64_t, double>(value_); + return other->AsDoubleConstant()->GetValueAsUint64() == GetValueAsUint64(); } size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); } @@ -4005,6 +4019,13 @@ class HPhi : public HInstruction { bool IsDead() const { return !is_live_; } bool IsLive() const { return is_live_; } + bool IsVRegEquivalentOf(HInstruction* other) const { + return other != nullptr + && other->IsPhi() + && other->AsPhi()->GetBlock() == GetBlock() + && other->AsPhi()->GetRegNumber() == GetRegNumber(); + } + // Returns the next equivalent phi (starting from the current one) or null if there is none. // An equivalent phi is a phi having the same dex register and type. // It assumes that phis with the same dex register are adjacent. diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index f549ba8391..44214606c0 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -485,34 +485,43 @@ static void RunOptimizations(HGraph* graph, RunOptimizations(optimizations1, arraysize(optimizations1), pass_observer); + // TODO: Update passes incompatible with try/catch so we have the same + // pipeline for all methods. if (graph->HasTryCatch()) { - // TODO: Update the optimizations below to work correctly under try/catch - // semantics. The optimizations above suffice for running codegen - // in the meanwhile. - return; + HOptimization* optimizations2[] = { + side_effects, + gvn, + dce2, + // The codegen has a few assumptions that only the instruction simplifier + // can satisfy. For example, the code generator does not expect to see a + // HTypeConversion from a type to the same type. + simplify4, + }; + + RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer); + } else { + MaybeRunInliner(graph, driver, stats, dex_compilation_unit, pass_observer, handles); + + HOptimization* optimizations2[] = { + // BooleanSimplifier depends on the InstructionSimplifier removing + // redundant suspend checks to recognize empty blocks. + boolean_simplify, + fold2, // TODO: if we don't inline we can also skip fold2. + side_effects, + gvn, + licm, + bce, + simplify3, + dce2, + // The codegen has a few assumptions that only the instruction simplifier + // can satisfy. For example, the code generator does not expect to see a + // HTypeConversion from a type to the same type. + simplify4, + }; + + RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer); } - MaybeRunInliner(graph, driver, stats, dex_compilation_unit, pass_observer, handles); - - HOptimization* optimizations2[] = { - // BooleanSimplifier depends on the InstructionSimplifier removing redundant - // suspend checks to recognize empty blocks. - boolean_simplify, - fold2, // TODO: if we don't inline we can also skip fold2. - side_effects, - gvn, - licm, - bce, - simplify3, - dce2, - // The codegen has a few assumptions that only the instruction simplifier can - // satisfy. For example, the code generator does not expect to see a - // HTypeConversion from a type to the same type. - simplify4, - }; - - RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer); - RunArchOptimizations(driver->GetInstructionSet(), graph, stats, pass_observer); } @@ -560,17 +569,18 @@ CompiledMethod* OptimizingCompiler::CompileOptimized(HGraph* graph, CompilerDriver* compiler_driver, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer) const { + if (graph->HasTryCatch() && graph->IsDebuggable()) { + // TODO: b/24054676, stop creating catch phis eagerly to avoid special cases like phis without + // inputs. + return nullptr; + } + ScopedObjectAccess soa(Thread::Current()); StackHandleScopeCollection handles(soa.Self()); soa.Self()->TransitionFromRunnableToSuspended(kNative); RunOptimizations(graph, compiler_driver, compilation_stats_.get(), dex_compilation_unit, pass_observer, &handles); - if (graph->HasTryCatch()) { - soa.Self()->TransitionFromSuspendedToRunnable(); - return nullptr; - } - AllocateRegisters(graph, codegen, pass_observer); ArenaAllocator* arena = graph->GetArena(); diff --git a/compiler/optimizing/pretty_printer.h b/compiler/optimizing/pretty_printer.h index 934514eeda..34850a564c 100644 --- a/compiler/optimizing/pretty_printer.h +++ b/compiler/optimizing/pretty_printer.h @@ -71,23 +71,23 @@ class HPrettyPrinter : public HGraphVisitor { void VisitBasicBlock(HBasicBlock* block) OVERRIDE { PrintString("BasicBlock "); PrintInt(block->GetBlockId()); - const GrowableArray<HBasicBlock*>& predecessors = block->GetPredecessors(); - if (!predecessors.IsEmpty()) { + const ArenaVector<HBasicBlock*>& predecessors = block->GetPredecessors(); + if (!predecessors.empty()) { PrintString(", pred: "); - for (size_t i = 0; i < predecessors.Size() -1; i++) { - PrintInt(predecessors.Get(i)->GetBlockId()); + for (size_t i = 0; i < predecessors.size() -1; i++) { + PrintInt(predecessors[i]->GetBlockId()); PrintString(", "); } - PrintInt(predecessors.Peek()->GetBlockId()); + PrintInt(predecessors.back()->GetBlockId()); } - const GrowableArray<HBasicBlock*>& successors = block->GetSuccessors(); - if (!successors.IsEmpty()) { + const ArenaVector<HBasicBlock*>& successors = block->GetSuccessors(); + if (!successors.empty()) { PrintString(", succ: "); - for (size_t i = 0; i < successors.Size() - 1; i++) { - PrintInt(successors.Get(i)->GetBlockId()); + for (size_t i = 0; i < successors.size() - 1; i++) { + PrintInt(successors[i]->GetBlockId()); PrintString(", "); } - PrintInt(successors.Peek()->GetBlockId()); + PrintInt(successors.back()->GetBlockId()); } PrintNewLine(); HGraphVisitor::VisitBasicBlock(block); @@ -131,7 +131,7 @@ class StringPrettyPrinter : public HPrettyPrinter { PrintString(" "); PrintInt(gota->GetId()); PrintString(": Goto "); - PrintInt(current_block_->GetSuccessors().Get(0)->GetBlockId()); + PrintInt(current_block_->GetSuccessor(0)->GetBlockId()); PrintNewLine(); } diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 0384e46671..a88c5431c5 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -167,7 +167,7 @@ static HBoundType* CreateBoundType(ArenaAllocator* arena, ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); HBoundType* bound_type = new (arena) HBoundType(obj, class_rti, upper_can_be_null); // Narrow the type as much as possible. - if (class_rti.GetTypeHandle()->IsFinal()) { + if (class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes()) { bound_type->SetReferenceTypeInfo( ReferenceTypeInfo::Create(class_rti.GetTypeHandle(), /* is_exact */ true)); } else if (obj_rti.IsValid() && class_rti.IsSupertypeOf(obj_rti)) { @@ -380,7 +380,7 @@ void RTPVisitor::SetClassAsTypeInfo(HInstruction* instr, } else if (klass != nullptr) { ScopedObjectAccess soa(Thread::Current()); ReferenceTypeInfo::TypeHandle handle = handles_->NewHandle(klass); - is_exact = is_exact || klass->IsFinal(); + is_exact = is_exact || klass->CannotBeAssignedFromOtherTypes(); instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact)); } else { instr->SetReferenceTypeInfo( diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index 37c8bc5f51..a4f1f458fd 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -56,6 +56,7 @@ RegisterAllocator::RegisterAllocator(ArenaAllocator* allocator, long_spill_slots_(allocator, kDefaultNumberOfSpillSlots), float_spill_slots_(allocator, kDefaultNumberOfSpillSlots), double_spill_slots_(allocator, kDefaultNumberOfSpillSlots), + catch_phi_spill_slots_(0), safepoints_(allocator, 0), processing_core_registers_(false), number_of_registers_(-1), @@ -124,9 +125,7 @@ void RegisterAllocator::AllocateRegisters() { } } -void RegisterAllocator::BlockRegister(Location location, - size_t start, - size_t end) { +void RegisterAllocator::BlockRegister(Location location, size_t start, size_t end) { int reg = location.reg(); DCHECK(location.IsRegister() || location.IsFpuRegister()); LiveInterval* interval = location.IsRegister() @@ -147,6 +146,19 @@ void RegisterAllocator::BlockRegister(Location location, interval->AddRange(start, end); } +void RegisterAllocator::BlockRegisters(size_t start, size_t end, bool caller_save_only) { + for (size_t i = 0; i < codegen_->GetNumberOfCoreRegisters(); ++i) { + if (!caller_save_only || !codegen_->IsCoreCalleeSaveRegister(i)) { + BlockRegister(Location::RegisterLocation(i), start, end); + } + } + for (size_t i = 0; i < codegen_->GetNumberOfFloatingPointRegisters(); ++i) { + if (!caller_save_only || !codegen_->IsFloatingPointCalleeSaveRegister(i)) { + BlockRegister(Location::FpuRegisterLocation(i), start, end); + } + } +} + void RegisterAllocator::AllocateRegistersInternal() { // Iterate post-order, to ensure the list is sorted, and the last added interval // is the one with the lowest start position. @@ -159,6 +171,13 @@ void RegisterAllocator::AllocateRegistersInternal() { for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) { ProcessInstruction(inst_it.Current()); } + + if (block->IsCatchBlock()) { + // By blocking all registers at the top of each catch block, we force + // intervals used after catch to spill. + size_t position = block->GetLifetimeStart(); + BlockRegisters(position, position + 1); + } } number_of_registers_ = codegen_->GetNumberOfCoreRegisters(); @@ -275,21 +294,7 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { } if (locations->WillCall()) { - // Block all registers. - for (size_t i = 0; i < codegen_->GetNumberOfCoreRegisters(); ++i) { - if (!codegen_->IsCoreCalleeSaveRegister(i)) { - BlockRegister(Location::RegisterLocation(i), - position, - position + 1); - } - } - for (size_t i = 0; i < codegen_->GetNumberOfFloatingPointRegisters(); ++i) { - if (!codegen_->IsFloatingPointCalleeSaveRegister(i)) { - BlockRegister(Location::FpuRegisterLocation(i), - position, - position + 1); - } - } + BlockRegisters(position, position + 1, /* caller_save_only */ true); } for (size_t i = 0; i < instruction->InputCount(); ++i) { @@ -378,6 +383,10 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { DCHECK(output.IsUnallocated() || output.IsConstant()); } + if (instruction->IsPhi() && instruction->AsPhi()->IsCatchPhi()) { + AllocateSpillSlotForCatchPhi(instruction->AsPhi()); + } + // If needed, add interval to the list of unhandled intervals. if (current->HasSpillSlot() || instruction->IsConstant()) { // Split just before first register use. @@ -1212,14 +1221,13 @@ LiveInterval* RegisterAllocator::SplitBetween(LiveInterval* interval, size_t fro * moves in B3. */ if (block_from->GetDominator() != nullptr) { - const GrowableArray<HBasicBlock*>& dominated = block_from->GetDominator()->GetDominatedBlocks(); - for (size_t i = 0; i < dominated.Size(); ++i) { - size_t position = dominated.Get(i)->GetLifetimeStart(); + for (HBasicBlock* dominated : block_from->GetDominator()->GetDominatedBlocks()) { + size_t position = dominated->GetLifetimeStart(); if ((position > from) && (block_to->GetLifetimeStart() > position)) { // Even if we found a better block, we continue iterating in case // a dominated block is closer. // Note that dominated blocks are not sorted in liveness order. - block_to = dominated.Get(i); + block_to = dominated; DCHECK_NE(block_to, block_from); } } @@ -1283,6 +1291,8 @@ void RegisterAllocator::AllocateSpillSlotFor(LiveInterval* interval) { } HInstruction* defined_by = parent->GetDefinedBy(); + DCHECK(!defined_by->IsPhi() || !defined_by->AsPhi()->IsCatchPhi()); + if (defined_by->IsParameterValue()) { // Parameters have their own stack slot. parent->SetSpillSlot(codegen_->GetStackSlotOfParameter(defined_by->AsParameterValue())); @@ -1299,12 +1309,6 @@ void RegisterAllocator::AllocateSpillSlotFor(LiveInterval* interval) { return; } - LiveInterval* last_sibling = interval; - while (last_sibling->GetNextSibling() != nullptr) { - last_sibling = last_sibling->GetNextSibling(); - } - size_t end = last_sibling->GetEnd(); - GrowableArray<size_t>* spill_slots = nullptr; switch (interval->GetType()) { case Primitive::kPrimDouble: @@ -1337,6 +1341,7 @@ void RegisterAllocator::AllocateSpillSlotFor(LiveInterval* interval) { } } + size_t end = interval->GetLastSibling()->GetEnd(); if (parent->NeedsTwoSpillSlots()) { if (slot == spill_slots->Size()) { // We need a new spill slot. @@ -1372,6 +1377,28 @@ static bool IsValidDestination(Location destination) { || destination.IsDoubleStackSlot(); } +void RegisterAllocator::AllocateSpillSlotForCatchPhi(HPhi* phi) { + LiveInterval* interval = phi->GetLiveInterval(); + + HInstruction* previous_phi = phi->GetPrevious(); + DCHECK(previous_phi == nullptr || + previous_phi->AsPhi()->GetRegNumber() <= phi->GetRegNumber()) + << "Phis expected to be sorted by vreg number, so that equivalent phis are adjacent."; + + if (phi->IsVRegEquivalentOf(previous_phi)) { + // This is an equivalent of the previous phi. We need to assign the same + // catch phi slot. + DCHECK(previous_phi->GetLiveInterval()->HasSpillSlot()); + interval->SetSpillSlot(previous_phi->GetLiveInterval()->GetSpillSlot()); + } else { + // Allocate a new spill slot for this catch phi. + // TODO: Reuse spill slots when intervals of phis from different catch + // blocks do not overlap. + interval->SetSpillSlot(catch_phi_spill_slots_); + catch_phi_spill_slots_ += interval->NeedsTwoSpillSlots() ? 2 : 1; + } +} + void RegisterAllocator::AddMove(HParallelMove* move, Location source, Location destination, @@ -1498,7 +1525,7 @@ void RegisterAllocator::InsertParallelMoveAtExitOf(HBasicBlock* block, DCHECK(IsValidDestination(destination)) << destination; if (source.Equals(destination)) return; - DCHECK_EQ(block->GetSuccessors().Size(), 1u); + DCHECK_EQ(block->NumberOfNormalSuccessors(), 1u); HInstruction* last = block->GetLastInstruction(); // We insert moves at exit for phi predecessors and connecting blocks. // A block ending with an if cannot branch to a block with phis because @@ -1725,13 +1752,13 @@ void RegisterAllocator::ConnectSplitSiblings(LiveInterval* interval, // If `from` has only one successor, we can put the moves at the exit of it. Otherwise // we need to put the moves at the entry of `to`. - if (from->GetSuccessors().Size() == 1) { + if (from->NumberOfNormalSuccessors() == 1) { InsertParallelMoveAtExitOf(from, interval->GetParent()->GetDefinedBy(), source->ToLocation(), destination->ToLocation()); } else { - DCHECK_EQ(to->GetPredecessors().Size(), 1u); + DCHECK_EQ(to->GetPredecessors().size(), 1u); InsertParallelMoveAtEntryOf(to, interval->GetParent()->GetDefinedBy(), source->ToLocation(), @@ -1769,17 +1796,25 @@ void RegisterAllocator::Resolve() { } else if (instruction->IsCurrentMethod()) { // The current method is always at offset 0. DCHECK(!current->HasSpillSlot() || (current->GetSpillSlot() == 0)); + } else if (instruction->IsPhi() && instruction->AsPhi()->IsCatchPhi()) { + DCHECK(current->HasSpillSlot()); + size_t slot = current->GetSpillSlot() + + GetNumberOfSpillSlots() + + reserved_out_slots_ + - catch_phi_spill_slots_; + current->SetSpillSlot(slot * kVRegSize); } 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: - // [parameter slots ] - // [double spill slots ] - // [long spill slots ] - // [float spill slots ] - // [int/ref values ] - // [maximum out values ] (number of arguments for calls) - // [art method ]. - uint32_t slot = current->GetSpillSlot(); + // [parameter slots ] + // [catch phi spill slots ] + // [double spill slots ] + // [long spill slots ] + // [float spill slots ] + // [int/ref values ] + // [maximum out values ] (number of arguments for calls) + // [art method ]. + size_t slot = current->GetSpillSlot(); switch (current->GetType()) { case Primitive::kPrimDouble: slot += long_spill_slots_.Size(); @@ -1829,12 +1864,22 @@ void RegisterAllocator::Resolve() { // Resolve non-linear control flow across branches. Order does not matter. for (HLinearOrderIterator it(*codegen_->GetGraph()); !it.Done(); it.Advance()) { HBasicBlock* block = it.Current(); - BitVector* live = liveness_.GetLiveInSet(*block); - for (uint32_t idx : live->Indexes()) { - HInstruction* current = liveness_.GetInstructionFromSsaIndex(idx); - LiveInterval* interval = current->GetLiveInterval(); - for (size_t i = 0, e = block->GetPredecessors().Size(); i < e; ++i) { - ConnectSplitSiblings(interval, block->GetPredecessors().Get(i), block); + if (block->IsCatchBlock()) { + // Instructions live at the top of catch blocks were forced to spill. + if (kIsDebugBuild) { + BitVector* live = liveness_.GetLiveInSet(*block); + for (uint32_t idx : live->Indexes()) { + LiveInterval* interval = liveness_.GetInstructionFromSsaIndex(idx)->GetLiveInterval(); + DCHECK(!interval->GetSiblingAt(block->GetLifetimeStart())->HasRegister()); + } + } + } else { + BitVector* live = liveness_.GetLiveInSet(*block); + for (uint32_t idx : live->Indexes()) { + LiveInterval* interval = liveness_.GetInstructionFromSsaIndex(idx)->GetLiveInterval(); + for (HBasicBlock* predecessor : block->GetPredecessors()) { + ConnectSplitSiblings(interval, predecessor, block); + } } } } @@ -1842,16 +1887,20 @@ void RegisterAllocator::Resolve() { // Resolve phi inputs. Order does not matter. for (HLinearOrderIterator it(*codegen_->GetGraph()); !it.Done(); it.Advance()) { HBasicBlock* current = it.Current(); - for (HInstructionIterator inst_it(current->GetPhis()); !inst_it.Done(); inst_it.Advance()) { - HInstruction* phi = inst_it.Current(); - for (size_t i = 0, e = current->GetPredecessors().Size(); i < e; ++i) { - HBasicBlock* predecessor = current->GetPredecessors().Get(i); - DCHECK_EQ(predecessor->GetSuccessors().Size(), 1u); - HInstruction* input = phi->InputAt(i); - Location source = input->GetLiveInterval()->GetLocationAt( - predecessor->GetLifetimeEnd() - 1); - Location destination = phi->GetLiveInterval()->ToLocation(); - InsertParallelMoveAtExitOf(predecessor, phi, source, destination); + if (current->IsCatchBlock()) { + // Catch phi values are set at runtime by the exception delivery mechanism. + } else { + for (HInstructionIterator inst_it(current->GetPhis()); !inst_it.Done(); inst_it.Advance()) { + HInstruction* phi = inst_it.Current(); + for (size_t i = 0, e = current->GetPredecessors().size(); i < e; ++i) { + HBasicBlock* predecessor = current->GetPredecessor(i); + DCHECK_EQ(predecessor->NumberOfNormalSuccessors(), 1u); + HInstruction* input = phi->InputAt(i); + Location source = input->GetLiveInterval()->GetLocationAt( + predecessor->GetLifetimeEnd() - 1); + Location destination = phi->GetLiveInterval()->ToLocation(); + InsertParallelMoveAtExitOf(predecessor, phi, source, destination); + } } } } diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h index c29fe75921..e0304643e6 100644 --- a/compiler/optimizing/register_allocator.h +++ b/compiler/optimizing/register_allocator.h @@ -29,6 +29,7 @@ class HBasicBlock; class HGraph; class HInstruction; class HParallelMove; +class HPhi; class LiveInterval; class Location; class SsaLivenessAnalysis; @@ -72,7 +73,8 @@ class RegisterAllocator { return int_spill_slots_.Size() + long_spill_slots_.Size() + float_spill_slots_.Size() - + double_spill_slots_.Size(); + + double_spill_slots_.Size() + + catch_phi_spill_slots_; } static constexpr const char* kRegisterAllocatorPassName = "register"; @@ -99,10 +101,17 @@ class RegisterAllocator { // Update the interval for the register in `location` to cover [start, end). void BlockRegister(Location location, size_t start, size_t end); + void BlockRegisters(size_t start, size_t end, bool caller_save_only = false); - // Allocate a spill slot for the given interval. + // Allocate a spill slot for the given interval. Should be called in linear + // order of interval starting positions. void AllocateSpillSlotFor(LiveInterval* interval); + // Allocate a spill slot for the given catch phi. Will allocate the same slot + // for phis which share the same vreg. Must be called in reverse linear order + // of lifetime positions and ascending vreg numbers for correctness. + void AllocateSpillSlotForCatchPhi(HPhi* phi); + // Connect adjacent siblings within blocks. void ConnectSiblings(LiveInterval* interval); @@ -202,6 +211,11 @@ class RegisterAllocator { GrowableArray<size_t> float_spill_slots_; GrowableArray<size_t> double_spill_slots_; + // Spill slots allocated to catch phis. This category is special-cased because + // (1) slots are allocated prior to linear scan and in reverse linear order, + // (2) equivalent phis need to share slots despite having different types. + size_t catch_phi_spill_slots_; + // Instructions that need a safepoint. GrowableArray<HInstruction*> safepoints_; diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index 561c3b4964..e6209b9583 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -241,8 +241,8 @@ void SsaBuilder::BuildSsa() { HBasicBlock* block = loop_headers_.Get(i); for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { HPhi* phi = it.Current()->AsPhi(); - for (size_t pred = 0; pred < block->GetPredecessors().Size(); pred++) { - HInstruction* input = ValueOfLocal(block->GetPredecessors().Get(pred), phi->GetRegNumber()); + for (HBasicBlock* predecessor : block->GetPredecessors()) { + HInstruction* input = ValueOfLocal(predecessor, phi->GetRegNumber()); phi->AddInput(input); } } @@ -369,16 +369,16 @@ void SsaBuilder::VisitBasicBlock(HBasicBlock* block) { // Save the loop header so that the last phase of the analysis knows which // blocks need to be updated. loop_headers_.Add(block); - } else if (block->GetPredecessors().Size() > 0) { + } else if (block->GetPredecessors().size() > 0) { // All predecessors have already been visited because we are visiting in reverse post order. // We merge the values of all locals, creating phis if those values differ. for (size_t local = 0; local < current_locals_->Size(); local++) { bool one_predecessor_has_no_value = false; bool is_different = false; - HInstruction* value = ValueOfLocal(block->GetPredecessors().Get(0), local); + HInstruction* value = ValueOfLocal(block->GetPredecessor(0), local); - for (size_t i = 0, e = block->GetPredecessors().Size(); i < e; ++i) { - HInstruction* current = ValueOfLocal(block->GetPredecessors().Get(i), local); + for (HBasicBlock* predecessor : block->GetPredecessors()) { + HInstruction* current = ValueOfLocal(predecessor, local); if (current == nullptr) { one_predecessor_has_no_value = true; break; @@ -395,9 +395,9 @@ void SsaBuilder::VisitBasicBlock(HBasicBlock* block) { if (is_different) { HPhi* phi = new (GetGraph()->GetArena()) HPhi( - GetGraph()->GetArena(), local, block->GetPredecessors().Size(), Primitive::kPrimVoid); - for (size_t i = 0; i < block->GetPredecessors().Size(); i++) { - HInstruction* pred_value = ValueOfLocal(block->GetPredecessors().Get(i), local); + GetGraph()->GetArena(), local, block->GetPredecessors().size(), Primitive::kPrimVoid); + for (size_t i = 0; i < block->GetPredecessors().size(); i++) { + HInstruction* pred_value = ValueOfLocal(block->GetPredecessor(i), local); phi->SetRawInputAt(i, pred_value); } block->AddPhi(phi); diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index 40502c173b..63635f3127 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -73,7 +73,7 @@ void SsaLivenessAnalysis::LinearizeGraph() { forward_predecessors.SetSize(graph_->GetBlocks().Size()); for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) { HBasicBlock* block = it.Current(); - size_t number_of_forward_predecessors = block->GetPredecessors().Size(); + size_t number_of_forward_predecessors = block->GetPredecessors().size(); if (block->IsLoopHeader()) { number_of_forward_predecessors -= block->GetLoopInformation()->NumberOfBackEdges(); } @@ -89,8 +89,7 @@ void SsaLivenessAnalysis::LinearizeGraph() { do { HBasicBlock* current = worklist.Pop(); graph_->linear_order_.Add(current); - for (size_t i = 0, e = current->GetSuccessors().Size(); i < e; ++i) { - HBasicBlock* successor = current->GetSuccessors().Get(i); + for (HBasicBlock* successor : current->GetSuccessors()) { int block_id = successor->GetBlockId(); size_t number_of_remaining_predecessors = forward_predecessors.Get(block_id); if (number_of_remaining_predecessors == 1) { @@ -185,17 +184,27 @@ void SsaLivenessAnalysis::ComputeLiveRanges() { // Set phi inputs of successors of this block corresponding to this block // as live_in. - for (size_t i = 0, e = block->GetSuccessors().Size(); i < e; ++i) { - HBasicBlock* successor = block->GetSuccessors().Get(i); + for (HBasicBlock* successor : block->GetSuccessors()) { live_in->Union(GetLiveInSet(*successor)); - size_t phi_input_index = successor->GetPredecessorIndexOf(block); - for (HInstructionIterator inst_it(successor->GetPhis()); !inst_it.Done(); inst_it.Advance()) { - HInstruction* phi = inst_it.Current(); - HInstruction* input = phi->InputAt(phi_input_index); - input->GetLiveInterval()->AddPhiUse(phi, phi_input_index, block); - // A phi input whose last user is the phi dies at the end of the predecessor block, - // and not at the phi's lifetime position. - live_in->SetBit(input->GetSsaIndex()); + if (successor->IsCatchBlock()) { + // Inputs of catch phis will be kept alive through their environment + // uses, allowing the runtime to copy their values to the corresponding + // catch phi spill slots when an exception is thrown. + // The only instructions which may not be recorded in the environments + // are constants created by the SSA builder as typed equivalents of + // untyped constants from the bytecode, or phis with only such constants + // as inputs (verified by SSAChecker). Their raw binary value must + // therefore be the same and we only need to keep alive one. + } else { + size_t phi_input_index = successor->GetPredecessorIndexOf(block); + for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) { + HInstruction* phi = phi_it.Current(); + HInstruction* input = phi->InputAt(phi_input_index); + input->GetLiveInterval()->AddPhiUse(phi, phi_input_index, block); + // A phi input whose last user is the phi dies at the end of the predecessor block, + // and not at the phi's lifetime position. + live_in->SetBit(input->GetSsaIndex()); + } } } @@ -296,8 +305,7 @@ bool SsaLivenessAnalysis::UpdateLiveOut(const HBasicBlock& block) { BitVector* live_out = GetLiveOutSet(block); bool changed = false; // The live_out set of a block is the union of live_in sets of its successors. - for (size_t i = 0, e = block.GetSuccessors().Size(); i < e; ++i) { - HBasicBlock* successor = block.GetSuccessors().Get(i); + for (HBasicBlock* successor : block.GetSuccessors()) { if (live_out->Union(GetLiveInSet(*successor))) { changed = true; } @@ -342,8 +350,8 @@ int LiveInterval::FindFirstRegisterHint(size_t* free_until, // 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; + for (HBasicBlock* predecessor : block->GetPredecessors()) { + size_t position = predecessor->GetLifetimeEnd() - 1; // We know positions above GetStart() do not have a location yet. if (position < GetStart()) { LiveInterval* existing = GetParent()->GetSiblingAt(position); @@ -376,17 +384,16 @@ int LiveInterval::FindFirstRegisterHint(size_t* free_until, return reg; } } - const GrowableArray<HBasicBlock*>& predecessors = user->GetBlock()->GetPredecessors(); // If the instruction dies at the phi assignment, we can try having the // same register. - if (end == predecessors.Get(input_index)->GetLifetimeEnd()) { + if (end == user->GetBlock()->GetPredecessor(input_index)->GetLifetimeEnd()) { for (size_t i = 0, e = user->InputCount(); i < e; ++i) { if (i == input_index) { continue; } HInstruction* input = user->InputAt(i); Location location = input->GetLiveInterval()->GetLocationAt( - predecessors.Get(i)->GetLifetimeEnd() - 1); + user->GetBlock()->GetPredecessor(i)->GetLifetimeEnd() - 1); if (location.IsRegisterKind()) { int reg = RegisterOrLowRegister(location); if (free_until[reg] >= use_position) { @@ -420,10 +427,11 @@ int LiveInterval::FindFirstRegisterHint(size_t* free_until, int LiveInterval::FindHintAtDefinition() const { if (defined_by_->IsPhi()) { // Try to use the same register as one of the inputs. - const GrowableArray<HBasicBlock*>& predecessors = defined_by_->GetBlock()->GetPredecessors(); + const ArenaVector<HBasicBlock*>& predecessors = defined_by_->GetBlock()->GetPredecessors(); for (size_t i = 0, e = defined_by_->InputCount(); i < e; ++i) { HInstruction* input = defined_by_->InputAt(i); - size_t end = predecessors.Get(i)->GetLifetimeEnd(); + DCHECK_LT(i, predecessors.size()); + size_t end = predecessors[i]->GetLifetimeEnd(); LiveInterval* input_interval = input->GetLiveInterval()->GetSiblingAt(end - 1); if (input_interval->GetEnd() == end) { // If the input dies at the end of the predecessor, we know its register can diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index a7044de850..ef396cbdba 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -1209,6 +1209,9 @@ class SsaLivenessAnalysis : public ValueObject { // A value that's not live in compiled code may still be needed in interpreter, // due to code motion, etc. if (env_holder->IsDeoptimize()) return true; + // A value live at a throwing instruction in a try block may be copied by + // the exception handler to its location at the top of the catch block. + if (env_holder->CanThrowIntoCatchBlock()) return true; if (instruction->GetBlock()->GetGraph()->IsDebuggable()) return true; return instruction->GetType() == Primitive::kPrimNot; } diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 1f1530fa1e..1f0bac59e0 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -286,7 +286,7 @@ void StackMapStream::FillIn(MemoryRegion region) { stack_map.SetDexRegisterMapOffset( stack_map_encoding_, code_info.GetStackMapAt(entry.same_dex_register_map_as_, stack_map_encoding_) - .GetDexRegisterMapOffset(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( diff --git a/compiler/optimizing/suspend_check_test.cc b/compiler/optimizing/suspend_check_test.cc index 5ca66a1de6..e745d94b89 100644 --- a/compiler/optimizing/suspend_check_test.cc +++ b/compiler/optimizing/suspend_check_test.cc @@ -36,7 +36,7 @@ static void TestCode(const uint16_t* data) { bool graph_built = builder.BuildGraph(*item); ASSERT_TRUE(graph_built); - HBasicBlock* first_block = graph->GetEntryBlock()->GetSuccessors().Get(0); + HBasicBlock* first_block = graph->GetEntryBlock()->GetSuccessor(0); HInstruction* first_instruction = first_block->GetFirstInstruction(); // Account for some tests having a store local as first instruction. ASSERT_TRUE(first_instruction->IsSuspendCheck() diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h index b1d7b4ccf3..b6a228c13c 100644 --- a/compiler/utils/test_dex_file_builder.h +++ b/compiler/utils/test_dex_file_builder.h @@ -216,7 +216,7 @@ class TestDexFileBuilder { std::unique_ptr<const DexFile> dex_file(DexFile::Open( &dex_file_data_[0], dex_file_data_.size(), dex_location, 0u, nullptr, &error_msg)); CHECK(dex_file != nullptr) << error_msg; - return std::move(dex_file); + return dex_file; } uint32_t GetStringIdx(const std::string& type) { diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index e6710ed780..f10799cc28 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -326,9 +326,9 @@ class StubTest : public CommonRuntimeTest { [referrer] "r"(referrer) : "at", "v0", "v1", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "fp", "ra", - "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13", - "f14", "f15", "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24", "f25", "f26", - "f27", "f28", "f29", "f30", "f31", + "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9", "$f10", "$f11", + "$f12", "$f13", "$f14", "$f15", "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", + "$f23", "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31", "memory"); // clobber. #elif defined(__mips__) && defined(__LP64__) __asm__ __volatile__ ( @@ -680,9 +680,9 @@ class StubTest : public CommonRuntimeTest { [referrer] "r"(referrer), [hidden] "r"(hidden) : "at", "v0", "v1", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "fp", "ra", - "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13", - "f14", "f15", "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24", "f25", "f26", - "f27", "f28", "f29", "f30", "f31", + "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9", "$f10", "$f11", + "$f12", "$f13", "$f14", "$f15", "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", + "$f23", "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31", "memory"); // clobber. #elif defined(__mips__) && defined(__LP64__) __asm__ __volatile__ ( diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 5dbea529c5..26839ece11 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -223,28 +223,48 @@ uint32_t ArtMethod::ToDexPc(const uintptr_t pc, bool abort_on_failure) { return DexFile::kDexNoIndex; } -uintptr_t ArtMethod::ToNativeQuickPc(const uint32_t dex_pc, bool abort_on_failure) { +uintptr_t ArtMethod::ToNativeQuickPc(const uint32_t dex_pc, + bool is_catch_handler, + bool abort_on_failure) { const void* entry_point = GetQuickOatEntryPoint(sizeof(void*)); - MappingTable table(entry_point != nullptr ? - GetMappingTable(EntryPointToCodePointer(entry_point), sizeof(void*)) : nullptr); - if (table.TotalSize() == 0) { - DCHECK_EQ(dex_pc, 0U); - return 0; // Special no mapping/pc == 0 case - } - // Assume the caller wants a dex-to-pc mapping so check here first. - typedef MappingTable::DexToPcIterator It; - for (It cur = table.DexToPcBegin(), end = table.DexToPcEnd(); cur != end; ++cur) { - if (cur.DexPc() == dex_pc) { - return reinterpret_cast<uintptr_t>(entry_point) + cur.NativePcOffset(); + if (IsOptimized(sizeof(void*))) { + // Optimized code does not have a mapping table. Search for the dex-to-pc + // mapping in stack maps. + CodeInfo code_info = GetOptimizedCodeInfo(); + StackMapEncoding encoding = code_info.ExtractEncoding(); + + // All stack maps are stored in the same CodeItem section, safepoint stack + // maps first, then catch stack maps. We use `is_catch_dex_pc` to select the + // order of iteration. + StackMap stack_map = + LIKELY(is_catch_handler) ? code_info.GetCatchStackMapForDexPc(dex_pc, encoding) + : code_info.GetStackMapForDexPc(dex_pc, encoding); + if (stack_map.IsValid()) { + return reinterpret_cast<uintptr_t>(entry_point) + stack_map.GetNativePcOffset(encoding); } - } - // Now check pc-to-dex mappings. - typedef MappingTable::PcToDexIterator It2; - for (It2 cur = table.PcToDexBegin(), end = table.PcToDexEnd(); cur != end; ++cur) { - if (cur.DexPc() == dex_pc) { - return reinterpret_cast<uintptr_t>(entry_point) + cur.NativePcOffset(); + } else { + MappingTable table(entry_point != nullptr ? + GetMappingTable(EntryPointToCodePointer(entry_point), sizeof(void*)) : nullptr); + if (table.TotalSize() == 0) { + DCHECK_EQ(dex_pc, 0U); + return 0; // Special no mapping/pc == 0 case + } + // Assume the caller wants a dex-to-pc mapping so check here first. + typedef MappingTable::DexToPcIterator It; + for (It cur = table.DexToPcBegin(), end = table.DexToPcEnd(); cur != end; ++cur) { + if (cur.DexPc() == dex_pc) { + return reinterpret_cast<uintptr_t>(entry_point) + cur.NativePcOffset(); + } + } + // Now check pc-to-dex mappings. + typedef MappingTable::PcToDexIterator It2; + for (It2 cur = table.PcToDexBegin(), end = table.PcToDexEnd(); cur != end; ++cur) { + if (cur.DexPc() == dex_pc) { + return reinterpret_cast<uintptr_t>(entry_point) + cur.NativePcOffset(); + } } } + if (abort_on_failure) { LOG(FATAL) << "Failed to find native offset for dex pc 0x" << std::hex << dex_pc << " in " << PrettyMethod(this); diff --git a/runtime/art_method.h b/runtime/art_method.h index 3f2161f4ee..6c3b13f0e0 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -442,7 +442,9 @@ class ArtMethod FINAL { SHARED_REQUIRES(Locks::mutator_lock_); // Converts a dex PC to a native PC. - uintptr_t ToNativeQuickPc(const uint32_t dex_pc, bool abort_on_failure = true) + uintptr_t ToNativeQuickPc(const uint32_t dex_pc, + bool is_catch_handler, + bool abort_on_failure = true) SHARED_REQUIRES(Locks::mutator_lock_); MethodReference ToMethodReference() SHARED_REQUIRES(Locks::mutator_lock_) { diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc index 3a4bccd94c..a36b0fbb93 100644 --- a/runtime/base/arena_allocator.cc +++ b/runtime/base/arena_allocator.cc @@ -52,13 +52,14 @@ const char* const ArenaAllocatorStatsImpl<kCount>::kAllocNames[] = { "SSA2Dalvik ", "Dalvik2SSA ", "DebugInfo ", - "Successor ", "RegAlloc ", "Data ", - "Preds ", "STL ", "Graph ", "BasicBlock ", + "Predecessors ", + "Successors ", + "Dominated ", "Instruction ", "LoopInfo ", "TryCatchInf ", diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h index af2bfbc944..47defb4ba2 100644 --- a/runtime/base/arena_allocator.h +++ b/runtime/base/arena_allocator.h @@ -62,13 +62,14 @@ enum ArenaAllocKind { kArenaAllocSSAToDalvikMap, kArenaAllocDalvikToSSAMap, kArenaAllocDebugInfo, - kArenaAllocSuccessor, kArenaAllocRegAlloc, kArenaAllocData, - kArenaAllocPredecessors, kArenaAllocSTL, kArenaAllocGraph, kArenaAllocBasicBlock, + kArenaAllocPredecessors, + kArenaAllocSuccessors, + kArenaAllocDominated, kArenaAllocInstruction, kArenaAllocLoopInfo, kArenaAllocTryCatchInfo, diff --git a/runtime/base/stl_util.h b/runtime/base/stl_util.h index 901f25fa0a..0949619640 100644 --- a/runtime/base/stl_util.h +++ b/runtime/base/stl_util.h @@ -20,6 +20,8 @@ #include <algorithm> #include <sstream> +#include "base/logging.h" + namespace art { // Sort and remove duplicates of an STL vector or deque. @@ -94,6 +96,59 @@ std::string ToString(const T& v) { return os.str(); } +// Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below. +struct FreeDelete { + // NOTE: Deleting a const object is valid but free() takes a non-const pointer. + void operator()(const void* ptr) const { + free(const_cast<void*>(ptr)); + } +}; + +// Alias for std::unique_ptr<> that uses the C function free() to delete objects. +template <typename T> +using UniqueCPtr = std::unique_ptr<T, FreeDelete>; + +// C++14 from-the-future import (std::make_unique) +// Invoke the constructor of 'T' with the provided args, and wrap the result in a unique ptr. +template <typename T, typename ... Args> +std::unique_ptr<T> MakeUnique(Args&& ... args) { + return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); +} + +// Find index of the first element with the specified value known to be in the container. +template <typename Container, typename T> +size_t IndexOfElement(const Container& container, const T& value) { + auto it = std::find(container.begin(), container.end(), value); + DCHECK(it != container.end()); // Must exist. + return std::distance(container.begin(), it); +} + +// Remove the first element with the specified value known to be in the container. +template <typename Container, typename T> +void RemoveElement(Container& container, const T& value) { + auto it = std::find(container.begin(), container.end(), value); + DCHECK(it != container.end()); // Must exist. + container.erase(it); +} + +// Replace the first element with the specified old_value known to be in the container. +template <typename Container, typename T> +void ReplaceElement(Container& container, const T& old_value, const T& new_value) { + auto it = std::find(container.begin(), container.end(), old_value); + DCHECK(it != container.end()); // Must exist. + *it = new_value; +} + +// Search for an element with the specified value and return true if it was found, false otherwise. +template <typename Container, typename T> +bool ContainsElement(const Container& container, const T& value, size_t start_pos = 0u) { + DCHECK_LE(start_pos, container.size()); + auto start = container.begin(); + std::advance(start, start_pos); + auto it = std::find(start, container.end(), value); + return it != container.end(); +} + } // namespace art #endif // ART_RUNTIME_BASE_STL_UTIL_H_ diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 4cb9a3b842..ae62e2bfe1 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -31,6 +31,7 @@ #include "art_method-inl.h" #include "base/hash_map.h" #include "base/logging.h" +#include "base/stl_util.h" #include "base/stringprintf.h" #include "class_linker-inl.h" #include "dex_file-inl.h" diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc index 33d756ea12..9f84bd2a39 100644 --- a/runtime/exception_test.cc +++ b/runtime/exception_test.cc @@ -186,13 +186,15 @@ TEST_F(ExceptionTest, StackTraceElement) { fake_stack.push_back(0); } - fake_stack.push_back(method_g_->ToNativeQuickPc(dex_pc)); // return pc + fake_stack.push_back( + method_g_->ToNativeQuickPc(dex_pc, /* is_catch_handler */ false)); // return pc // Create/push fake 16byte stack frame for method g fake_stack.push_back(reinterpret_cast<uintptr_t>(method_g_)); fake_stack.push_back(0); fake_stack.push_back(0); - fake_stack.push_back(method_f_->ToNativeQuickPc(dex_pc)); // return pc + fake_stack.push_back( + method_g_->ToNativeQuickPc(dex_pc, /* is_catch_handler */ false)); // return pc // Create/push fake 16byte stack frame for method f fake_stack.push_back(reinterpret_cast<uintptr_t>(method_f_)); diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index 57af95940d..4cbcdc5d42 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -1073,9 +1073,14 @@ void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) { if (to_ref->GetClass<kVerifyNone, kWithoutReadBarrier>()->IsTypeOfReferenceClass() && to_ref->AsReference()->GetReferent<kWithoutReadBarrier>() != nullptr && !IsInToSpace(to_ref->AsReference()->GetReferent<kWithoutReadBarrier>())) { - // Leave References gray so that GetReferent() will trigger RB. + // Leave this Reference gray in the queue so that GetReferent() will trigger a read barrier. We + // will change it to black or white later in ReferenceQueue::DequeuePendingReference(). CHECK(to_ref->AsReference()->IsEnqueued()) << "Left unenqueued ref gray " << to_ref; } else { + // We may occasionally leave a Reference black or white in the queue if its referent happens to + // be concurrently marked after the Scan() call above has enqueued the Reference, in which case + // the above IsInToSpace() evaluates to true and we change the color from gray to black or white + // here in this else block. #ifdef USE_BAKER_OR_BROOKS_READ_BARRIER if (kUseBakerReadBarrier) { if (region_space_->IsInToSpace(to_ref)) { diff --git a/runtime/gc/reference_queue.cc b/runtime/gc/reference_queue.cc index f5054289e3..0c0ab6d212 100644 --- a/runtime/gc/reference_queue.cc +++ b/runtime/gc/reference_queue.cc @@ -89,19 +89,36 @@ mirror::Reference* ReferenceQueue::DequeuePendingReference() { Heap* heap = Runtime::Current()->GetHeap(); if (kUseBakerOrBrooksReadBarrier && heap->CurrentCollectorType() == kCollectorTypeCC && heap->ConcurrentCopyingCollector()->IsActive()) { - // Clear the gray ptr we left in ConcurrentCopying::ProcessMarkStack(). - // We don't want to do this when the zygote compaction collector (SemiSpace) is running. + // Change the gray ptr we left in ConcurrentCopying::ProcessMarkStackRef() to black or white. + // We check IsActive() above because we don't want to do this when the zygote compaction + // collector (SemiSpace) is running. CHECK(ref != nullptr); - CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::GrayPtr()) - << "ref=" << ref << " rb_ptr=" << ref->GetReadBarrierPointer(); - if (heap->ConcurrentCopyingCollector()->RegionSpace()->IsInToSpace(ref)) { - // Moving objects. - ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(), ReadBarrier::WhitePtr()); - CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::WhitePtr()); + collector::ConcurrentCopying* concurrent_copying = heap->ConcurrentCopyingCollector(); + const bool is_moving = concurrent_copying->RegionSpace()->IsInToSpace(ref); + if (ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr()) { + if (is_moving) { + ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(), ReadBarrier::WhitePtr()); + CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::WhitePtr()); + } else { + ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(), ReadBarrier::BlackPtr()); + CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::BlackPtr()); + } } else { - // Non-moving objects. - ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(), ReadBarrier::BlackPtr()); - CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::BlackPtr()); + // In ConcurrentCopying::ProcessMarkStackRef() we may leave a black or white Reference in the + // queue and find it here, which is OK. Check that the color makes sense depending on whether + // the Reference is moving or not and that the referent has been marked. + if (is_moving) { + CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::WhitePtr()) + << "ref=" << ref << " rb_ptr=" << ref->GetReadBarrierPointer(); + } else { + CHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::BlackPtr()) + << "ref=" << ref << " rb_ptr=" << ref->GetReadBarrierPointer(); + } + mirror::Object* referent = ref->GetReferent<kWithoutReadBarrier>(); + CHECK(referent != nullptr) << "Reference should not have been enqueued if referent is null"; + CHECK(concurrent_copying->IsInToSpace(referent)) + << "ref=" << ref << " rb_ptr=" << ref->GetReadBarrierPointer() + << " referent=" << referent; } } return ref; diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 57c6866aa8..d797d2ad60 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -96,7 +96,8 @@ class CatchBlockStackVisitor FINAL : public StackVisitor { if (found_dex_pc != DexFile::kDexNoIndex) { exception_handler_->SetHandlerMethod(method); exception_handler_->SetHandlerDexPc(found_dex_pc); - exception_handler_->SetHandlerQuickFramePc(method->ToNativeQuickPc(found_dex_pc)); + exception_handler_->SetHandlerQuickFramePc( + method->ToNativeQuickPc(found_dex_pc, /* is_catch_handler */ true)); exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame()); return false; // End stack walk. } @@ -144,6 +145,107 @@ void QuickExceptionHandler::FindCatch(mirror::Throwable* exception) { // Put exception back in root set with clear throw location. self_->SetException(exception_ref.Get()); } + // If the handler is in optimized code, we need to set the catch environment. + if (*handler_quick_frame_ != nullptr && + handler_method_ != nullptr && + handler_method_->IsOptimized(sizeof(void*))) { + SetCatchEnvironmentForOptimizedHandler(&visitor); + } +} + +static VRegKind ToVRegKind(DexRegisterLocation::Kind kind) { + // Slightly hacky since we cannot map DexRegisterLocationKind and VRegKind + // one to one. However, StackVisitor::GetVRegFromOptimizedCode only needs to + // distinguish between core/FPU registers and low/high bits on 64-bit. + switch (kind) { + case DexRegisterLocation::Kind::kConstant: + case DexRegisterLocation::Kind::kInStack: + // VRegKind is ignored. + return VRegKind::kUndefined; + + case DexRegisterLocation::Kind::kInRegister: + // Selects core register. For 64-bit registers, selects low 32 bits. + return VRegKind::kLongLoVReg; + + case DexRegisterLocation::Kind::kInRegisterHigh: + // Selects core register. For 64-bit registers, selects high 32 bits. + return VRegKind::kLongHiVReg; + + case DexRegisterLocation::Kind::kInFpuRegister: + // Selects FPU register. For 64-bit registers, selects low 32 bits. + return VRegKind::kDoubleLoVReg; + + case DexRegisterLocation::Kind::kInFpuRegisterHigh: + // Selects FPU register. For 64-bit registers, selects high 32 bits. + return VRegKind::kDoubleHiVReg; + + default: + LOG(FATAL) << "Unexpected vreg location " + << DexRegisterLocation::PrettyDescriptor(kind); + UNREACHABLE(); + } +} + +void QuickExceptionHandler::SetCatchEnvironmentForOptimizedHandler(StackVisitor* stack_visitor) { + DCHECK(!is_deoptimization_); + DCHECK(*handler_quick_frame_ != nullptr) << "Method should not be called on upcall exceptions"; + DCHECK(handler_method_ != nullptr && handler_method_->IsOptimized(sizeof(void*))); + + if (kDebugExceptionDelivery) { + self_->DumpStack(LOG(INFO) << "Setting catch phis: "); + } + + const size_t number_of_vregs = handler_method_->GetCodeItem()->registers_size_; + CodeInfo code_info = handler_method_->GetOptimizedCodeInfo(); + StackMapEncoding encoding = code_info.ExtractEncoding(); + + // Find stack map of the throwing instruction. + StackMap throw_stack_map = + code_info.GetStackMapForNativePcOffset(stack_visitor->GetNativePcOffset(), encoding); + DCHECK(throw_stack_map.IsValid()); + DexRegisterMap throw_vreg_map = + code_info.GetDexRegisterMapOf(throw_stack_map, encoding, number_of_vregs); + + // Find stack map of the catch block. + StackMap catch_stack_map = code_info.GetCatchStackMapForDexPc(GetHandlerDexPc(), encoding); + DCHECK(catch_stack_map.IsValid()); + DexRegisterMap catch_vreg_map = + code_info.GetDexRegisterMapOf(catch_stack_map, encoding, number_of_vregs); + + // Copy values between them. + for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) { + DexRegisterLocation::Kind catch_location = + catch_vreg_map.GetLocationKind(vreg, number_of_vregs, code_info, encoding); + if (catch_location == DexRegisterLocation::Kind::kNone) { + continue; + } + DCHECK(catch_location == DexRegisterLocation::Kind::kInStack); + + // Get vreg value from its current location. + uint32_t vreg_value; + VRegKind vreg_kind = ToVRegKind(throw_vreg_map.GetLocationKind(vreg, + number_of_vregs, + code_info, + encoding)); + bool get_vreg_success = stack_visitor->GetVReg(stack_visitor->GetMethod(), + vreg, + vreg_kind, + &vreg_value); + CHECK(get_vreg_success) << "VReg " << vreg << " was optimized out (" + << "method=" << PrettyMethod(stack_visitor->GetMethod()) << ", " + << "dex_pc=" << stack_visitor->GetDexPc() << ", " + << "native_pc_offset=" << stack_visitor->GetNativePcOffset() << ")"; + + // Copy value to the catch phi's stack slot. + int32_t slot_offset = catch_vreg_map.GetStackOffsetInBytes(vreg, + number_of_vregs, + code_info, + encoding); + ArtMethod** frame_top = stack_visitor->GetCurrentQuickFrame(); + uint8_t* slot_address = reinterpret_cast<uint8_t*>(frame_top) + slot_offset; + uint32_t* slot_ptr = reinterpret_cast<uint32_t*>(slot_address); + *slot_ptr = vreg_value; + } } // Prepares deoptimization. diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h index 4db95a87ec..2e05c7e1e5 100644 --- a/runtime/quick_exception_handler.h +++ b/runtime/quick_exception_handler.h @@ -49,11 +49,14 @@ class QuickExceptionHandler { // Deoptimize the stack to the upcall. For every compiled frame, we create a "copy" // shadow frame that will be executed with the interpreter. void DeoptimizeStack() SHARED_REQUIRES(Locks::mutator_lock_); - // Update the instrumentation stack by removing all methods that will be unwound // by the exception being thrown. void UpdateInstrumentationStack() SHARED_REQUIRES(Locks::mutator_lock_); + // Set up environment before delivering an exception to optimized code. + void SetCatchEnvironmentForOptimizedHandler(StackVisitor* stack_visitor) + SHARED_REQUIRES(Locks::mutator_lock_); + // Long jump either to a catch handler or to the upcall. NO_RETURN void DoLongJump() SHARED_REQUIRES(Locks::mutator_lock_); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 7c71e1376d..bbadb1eb79 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -57,6 +57,7 @@ #include "atomic.h" #include "base/arena_allocator.h" #include "base/dumpable.h" +#include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "class_linker-inl.h" #include "compiler_callbacks.h" @@ -129,6 +130,7 @@ #include "thread_list.h" #include "trace.h" #include "transaction.h" +#include "utils.h" #include "verifier/method_verifier.h" #include "well_known_classes.h" diff --git a/runtime/stack.cc b/runtime/stack.cc index a765a3fc5e..d956f0e25d 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -325,6 +325,10 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin bool StackVisitor::GetRegisterIfAccessible(uint32_t reg, VRegKind kind, uint32_t* val) const { const bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg); + + // X86 float registers are 64-bit and the logic below does not apply. + DCHECK(!is_float || kRuntimeISA != InstructionSet::kX86); + if (!IsAccessibleRegister(reg, is_float)) { return false; } diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 07b79b5530..a15a08180e 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -1115,7 +1115,7 @@ class CodeInfo { region_.StoreUnaligned<NumberOfStackMapsType>(kNumberOfStackMapsOffset, number_of_stack_maps); } - // Get the size all the stack maps of this CodeInfo object, in bytes. + // Get the size of all the stack maps of this CodeInfo object, in bytes. size_t GetStackMapsSize(const StackMapEncoding& encoding) const { return encoding.ComputeStackMapSize() * GetNumberOfStackMaps(); } @@ -1174,9 +1174,23 @@ class CodeInfo { return StackMap(); } + // Searches the stack map list backwards because catch stack maps are stored + // at the end. + StackMap GetCatchStackMapForDexPc(uint32_t dex_pc, const StackMapEncoding& encoding) const { + for (size_t i = GetNumberOfStackMaps(); i > 0; --i) { + StackMap stack_map = GetStackMapAt(i - 1, encoding); + if (stack_map.GetDexPc(encoding) == dex_pc) { + return stack_map; + } + } + return StackMap(); + } + 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. + // TODO: Safepoint stack maps are sorted by native_pc_offset but catch stack + // maps are not. If we knew that the method does not have try/catch, + // we could do binary search. for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) { StackMap stack_map = GetStackMapAt(i, encoding); if (stack_map.GetNativePcOffset(encoding) == native_pc_offset) { diff --git a/runtime/utils.h b/runtime/utils.h index 16835c2902..3e618247d0 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -294,25 +294,6 @@ void Push32(std::vector<uint8_t, Alloc>* buf, int32_t data) { buf->push_back((data >> 24) & 0xff); } -// Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below. -struct FreeDelete { - // NOTE: Deleting a const object is valid but free() takes a non-const pointer. - void operator()(const void* ptr) const { - free(const_cast<void*>(ptr)); - } -}; - -// Alias for std::unique_ptr<> that uses the C function free() to delete objects. -template <typename T> -using UniqueCPtr = std::unique_ptr<T, FreeDelete>; - -// C++14 from-the-future import (std::make_unique) -// Invoke the constructor of 'T' with the provided args, and wrap the result in a unique ptr. -template <typename T, typename ... Args> -std::unique_ptr<T> MakeUnique(Args&& ... args) { - return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); -} - inline bool TestBitmap(size_t idx, const uint8_t* bitmap) { return ((bitmap[idx / kBitsPerByte] >> (idx % kBitsPerByte)) & 0x01) != 0; } diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 1ed6980c96..3d4f04c70c 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -22,6 +22,7 @@ #include "art_method-inl.h" #include "base/logging.h" #include "base/mutex-inl.h" +#include "base/stl_util.h" #include "base/time_utils.h" #include "class_linker.h" #include "compiler_callbacks.h" diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 5e661a59b8..ba694b7ccc 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_VERIFIER_METHOD_VERIFIER_H_ #include <memory> +#include <sstream> #include <vector> #include "base/macros.h" diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index e14306c0ae..bb756e9771 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -17,6 +17,7 @@ #include "reg_type_cache-inl.h" #include "base/casts.h" +#include "base/stl_util.h" #include "class_linker-inl.h" #include "dex_file-inl.h" #include "mirror/class-inl.h" diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h index 8319de6b28..93948a1755 100644 --- a/runtime/verifier/reg_type_cache.h +++ b/runtime/verifier/reg_type_cache.h @@ -19,7 +19,6 @@ #include "base/casts.h" #include "base/macros.h" -#include "base/stl_util.h" #include "object_callbacks.h" #include "reg_type.h" #include "runtime.h" diff --git a/test/004-ReferenceMap/stack_walk_refmap_jni.cc b/test/004-ReferenceMap/stack_walk_refmap_jni.cc index 767e1de68f..55a77ac2eb 100644 --- a/test/004-ReferenceMap/stack_walk_refmap_jni.cc +++ b/test/004-ReferenceMap/stack_walk_refmap_jni.cc @@ -22,7 +22,9 @@ namespace art { #define CHECK_REGS_CONTAIN_REFS(dex_pc, abort_if_not_found, ...) do { \ int t[] = {__VA_ARGS__}; \ int t_size = sizeof(t) / sizeof(*t); \ - uintptr_t native_quick_pc = m->ToNativeQuickPc(dex_pc, abort_if_not_found); \ + uintptr_t native_quick_pc = m->ToNativeQuickPc(dex_pc, \ + /* is_catch_handler */ false, \ + abort_if_not_found); \ if (native_quick_pc != UINTPTR_MAX) { \ CheckReferences(t, t_size, m->NativeQuickPcOffset(native_quick_pc)); \ } \ diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc index 59722ad00d..78f884266a 100644 --- a/test/137-cfi/cfi.cc +++ b/test/137-cfi/cfi.cc @@ -235,6 +235,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(JNIEnv*, jobj return result ? JNI_TRUE : JNI_FALSE; #else + UNUSED(pid_int); return JNI_FALSE; #endif } diff --git a/test/510-checker-try-catch/smali/RegisterAllocator.smali b/test/510-checker-try-catch/smali/RegisterAllocator.smali new file mode 100644 index 0000000000..fd3c84c483 --- /dev/null +++ b/test/510-checker-try-catch/smali/RegisterAllocator.smali @@ -0,0 +1,94 @@ +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class public LRegisterAllocator; + +.super Ljava/lang/Object; + +# Test that catch phis are allocated to a stack slot, and that equivalent catch +# phis are allocated to the same stack slot. + +## CHECK-START: int RegisterAllocator.testEquivalentCatchPhiSlot_Single(int, int, int) register (after) +## CHECK-DAG: Phi reg:0 is_catch_phi:true locations:{{\[.*\]}}-><<SlotA1:\d+>>(sp) +## CHECK-DAG: Phi reg:0 is_catch_phi:true locations:{{\[.*\]}}-><<SlotA2:\d+>>(sp) +## CHECK-DAG: Phi reg:1 is_catch_phi:true locations:{{\[.*\]}}-><<SlotB:\d+>>(sp) +## CHECK-EVAL: <<SlotA1>> == <<SlotA2>> +## CHECK-EVAL: <<SlotB>> != <<SlotA1>> + +.method public static testEquivalentCatchPhiSlot_Single(III)I + .registers 8 + + :try_start + const/high16 v0, 0x40000000 # float 2 + move v1, p0 + div-int/2addr p0, p1 + + const/high16 v0, 0x41000000 # float 8 + move v1, p1 + div-int/2addr p0, p2 + goto :return + :try_end + .catchall {:try_start .. :try_end} :catch_all + + :catch_all + # 2x CatchPhi for v0, 1x for v1 + if-eqz v1, :use_as_float + + :use_as_int + goto :return + + :use_as_float + float-to-int v0, v0 + + :return + return v0 +.end method + +# Test that wide catch phis are allocated to two stack slots. + +## CHECK-START: long RegisterAllocator.testEquivalentCatchPhiSlot_Wide(int, int, int) register (after) +## CHECK-DAG: Phi reg:0 is_catch_phi:true locations:{{\[.*\]}}->2x<<SlotB1:\d+>>(sp) +## CHECK-DAG: Phi reg:0 is_catch_phi:true locations:{{\[.*\]}}->2x<<SlotB2:\d+>>(sp) +## CHECK-DAG: Phi reg:2 is_catch_phi:true locations:{{\[.*\]}}-><<SlotA:\d+>>(sp) +## CHECK-EVAL: <<SlotB1>> == <<SlotB2>> +## CHECK-EVAL: abs(<<SlotA>> - <<SlotB1>>) >= 8 + +.method public static testEquivalentCatchPhiSlot_Wide(III)J + .registers 8 + + :try_start + const-wide/high16 v0, 0x4000000000000000L # double 2 + move v2, p0 + div-int/2addr p0, p1 + + const-wide/high16 v0, 0x4100000000000000L # double 8 + move v2, p1 + div-int/2addr p0, p2 + goto :return + :try_end + .catchall {:try_start .. :try_end} :catch_all + + :catch_all + # 2x CatchPhi for v0, 1x for v2 + if-eqz v2, :use_as_double + + :use_as_long + goto :return + + :use_as_double + double-to-long v0, v0 + + :return + return-wide v0 +.end method diff --git a/test/510-checker-try-catch/smali/Runtime.smali b/test/510-checker-try-catch/smali/Runtime.smali new file mode 100644 index 0000000000..19b43a3f30 --- /dev/null +++ b/test/510-checker-try-catch/smali/Runtime.smali @@ -0,0 +1,555 @@ +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class public LRuntime; +.super Ljava/lang/Object; + +# The following tests all share the same structure, signature and return values: +# - foo(false, false): normal path, returns 42 +# - foo(true, false): exceptional path #1, returns 3 +# - foo(false, true): exceptional path #2, returns 8 +# - foo(true, true): undefined + + +# Test register allocation of 32-bit core intervals crossing catch block positions. + +## CHECK-START: int Runtime.testUseAfterCatch_int(boolean, boolean) register (after) +## CHECK-NOT: Phi is_catch_phi:true + +.method public static testUseAfterCatch_int(ZZ)I + .registers 6 + + sget-object v0, LRuntime;->intArray:[I + const/4 v1, 0 + aget v1, v0, v1 + const/4 v2, 1 + aget v2, v0, v2 + const/4 v3, 2 + aget v3, v0, v3 + + :try_start + invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V + invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V + :try_end + .catchall {:try_start .. :try_end} :catch_all + + return v3 # Normal path return. + + :catch_all + if-eqz p0, :second_throw + return v1 # Exceptional path #1 return. + + :second_throw + return v2 # Exceptional path #2 return. +.end method + + +# Test register allocation of 64-bit core intervals crossing catch block positions. + +# The sum of the low and high 32 bits treated as integers is returned to prove +# that both vregs allocated correctly. + +## CHECK-START: int Runtime.testUseAfterCatch_long(boolean, boolean) register (after) +## CHECK-NOT: Phi is_catch_phi:true + +.method public static testUseAfterCatch_long(ZZ)I + .registers 10 + + sget-object v0, LRuntime;->longArray:[J + const/4 v1, 0 + aget-wide v1, v0, v1 + const/4 v3, 1 + aget-wide v3, v0, v3 + const/4 v5, 2 + aget-wide v5, v0, v5 + + :try_start + invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V + invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V + :try_end + .catchall {:try_start .. :try_end} :catch_all + + const v0, 32 + ushr-long v7, v5, v0 + long-to-int v5, v5 + long-to-int v7, v7 + add-int/2addr v5, v7 + return v5 # Normal path return. + + :catch_all + const v0, 32 + if-eqz p0, :second_throw + + ushr-long v7, v1, v0 + long-to-int v1, v1 + long-to-int v7, v7 + add-int/2addr v1, v7 + return v1 # Exceptional path #1 return. + + :second_throw + ushr-long v7, v3, v0 + long-to-int v3, v3 + long-to-int v7, v7 + add-int/2addr v3, v7 + return v3 # Exceptional path #2 return. +.end method + + +# Test register allocation of 32-bit floating-point intervals crossing catch block positions. + +## CHECK-START: int Runtime.testUseAfterCatch_float(boolean, boolean) register (after) +## CHECK-NOT: Phi is_catch_phi:true + +.method public static testUseAfterCatch_float(ZZ)I + .registers 6 + + sget-object v0, LRuntime;->floatArray:[F + const/4 v1, 0 + aget v1, v0, v1 + const/4 v2, 1 + aget v2, v0, v2 + const/4 v3, 2 + aget v3, v0, v3 + + :try_start + invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V + invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V + :try_end + .catchall {:try_start .. :try_end} :catch_all + + float-to-int v3, v3 + return v3 # Normal path return. + + :catch_all + if-eqz p0, :second_throw + float-to-int v1, v1 + return v1 # Exceptional path #1 return. + + :second_throw + float-to-int v2, v2 + return v2 # Exceptional path #2 return. +.end method + + +# Test register allocation of 64-bit floating-point intervals crossing catch block positions. + +## CHECK-START: int Runtime.testUseAfterCatch_double(boolean, boolean) register (after) +## CHECK-NOT: Phi is_catch_phi:true + +.method public static testUseAfterCatch_double(ZZ)I + .registers 10 + + sget-object v0, LRuntime;->doubleArray:[D + const/4 v1, 0 + aget-wide v1, v0, v1 + const/4 v3, 1 + aget-wide v3, v0, v3 + const/4 v5, 2 + aget-wide v5, v0, v5 + + :try_start + invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V + invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V + :try_end + .catchall {:try_start .. :try_end} :catch_all + + double-to-int v5, v5 + return v5 # Normal path return. + + :catch_all + if-eqz p0, :second_throw + double-to-int v1, v1 + return v1 # Exceptional path #1 return. + + :second_throw + double-to-int v3, v3 + return v3 # Exceptional path #2 return. +.end method + + +# Test catch-phi runtime support for constant values. + +# Register v0 holds different constants at two throwing instructions. Runtime is +# expected to load them from stack map and copy to the catch phi's location. + +## CHECK-START: int Runtime.testCatchPhi_const(boolean, boolean) register (after) +## CHECK-DAG: <<Const3:i\d+>> IntConstant 3 +## CHECK-DAG: <<Const8:i\d+>> IntConstant 8 +## CHECK-DAG: Phi [<<Const3>>,<<Const8>>] is_catch_phi:true + +.method public static testCatchPhi_const(ZZ)I + .registers 3 + + :try_start + const v0, 3 + invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V + + const v0, 8 + invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V + :try_end + .catchall {:try_start .. :try_end} :catch_all + + const v0, 42 + return v0 # Normal path return. + + :catch_all + return v0 # Exceptional path #1/#2 return. +.end method + + +# Test catch-phi runtime support for 32-bit values stored in core registers. + +# Register v0 holds different integer values at two throwing instructions. +# Runtime is expected to find their location in the stack map and copy the value +# to the location of the catch phi. + +## CHECK-START: int Runtime.testCatchPhi_int(boolean, boolean) register (after) +## CHECK-DAG: <<Val1:i\d+>> ArrayGet +## CHECK-DAG: <<Val2:i\d+>> ArrayGet +## CHECK-DAG: Phi [<<Val1>>,<<Val2>>] is_catch_phi:true + +.method public static testCatchPhi_int(ZZ)I + .registers 6 + + sget-object v0, LRuntime;->intArray:[I + const/4 v1, 0 + aget v1, v0, v1 + const/4 v2, 1 + aget v2, v0, v2 + const/4 v3, 2 + aget v3, v0, v3 + + :try_start + move v0, v1 # Set catch phi value + invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V + + move v0, v2 # Set catch phi value + invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V + :try_end + .catchall {:try_start .. :try_end} :catch_all + + return v3 # Normal path return. + + :catch_all + return v0 # Exceptional path #1/#2 return. +.end method + + +# Test catch-phi runtime support for 64-bit values stored in core registers. + +# Register pair (v0, v1) holds different long values at two throwing instructions. +# Runtime is expected to find their location in the stack map and copy the value +# to the location of the catch phi. The sum of the low and high 32 bits treated +# as integers is returned to prove that both vregs were copied. + +# Note: values will be spilled on x86 because of too few callee-save core registers. + +## CHECK-START: int Runtime.testCatchPhi_long(boolean, boolean) register (after) +## CHECK-DAG: <<Val1:j\d+>> ArrayGet +## CHECK-DAG: <<Val2:j\d+>> ArrayGet +## CHECK-DAG: Phi [<<Val1>>,<<Val2>>] is_catch_phi:true + +.method public static testCatchPhi_long(ZZ)I + .registers 10 + + sget-object v0, LRuntime;->longArray:[J + const/4 v2, 0 + aget-wide v2, v0, v2 + const/4 v4, 1 + aget-wide v4, v0, v4 + const/4 v6, 2 + aget-wide v6, v0, v6 + + :try_start + move-wide v0, v2 # Set catch phi value + invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V + + move-wide v0, v4 # Set catch phi value + invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V + :try_end + .catchall {:try_start .. :try_end} :catch_all + + const v2, 32 + ushr-long v2, v6, v2 + long-to-int v2, v2 + long-to-int v6, v6 + add-int/2addr v6, v2 + return v6 # Normal path return. + + :catch_all + const v2, 32 + ushr-long v2, v0, v2 + long-to-int v2, v2 + long-to-int v0, v0 + add-int/2addr v0, v2 + return v0 # Exceptional path #1/#2 return. +.end method + + +# Test catch-phi runtime support for 32-bit values stored in FPU registers. + +# Register v0 holds different float values at two throwing instructions. Runtime +# is expected to find their location in the stack map and copy the value to the +# location of the catch phi. The value is converted to int and returned. + +# Note: values will be spilled on x86 as there are no callee-save FPU registers. + +## CHECK-START: int Runtime.testCatchPhi_float(boolean, boolean) register (after) +## CHECK-DAG: <<Val1:f\d+>> ArrayGet +## CHECK-DAG: <<Val2:f\d+>> ArrayGet +## CHECK-DAG: Phi [<<Val1>>,<<Val2>>] is_catch_phi:true + +.method public static testCatchPhi_float(ZZ)I + .registers 6 + + sget-object v0, LRuntime;->floatArray:[F + const/4 v1, 0 + aget v1, v0, v1 + const/4 v2, 1 + aget v2, v0, v2 + const/4 v3, 2 + aget v3, v0, v3 + + :try_start + move v0, v1 # Set catch phi value + invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V + + move v0, v2 # Set catch phi value + invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V + :try_end + .catchall {:try_start .. :try_end} :catch_all + + float-to-int v3, v3 + return v3 # Normal path return. + + :catch_all + float-to-int v0, v0 + return v0 # Exceptional path #1/#2 return. +.end method + + +# Test catch-phi runtime support for 64-bit values stored in FPU registers. + +# Register pair (v0, v1) holds different double values at two throwing instructions. +# Runtime is expected to find their location in the stack map and copy the value +# to the location of the catch phi. The value is converted to int and returned. +# Values were chosen so that all 64 bits are used. + +# Note: values will be spilled on x86 as there are no callee-save FPU registers. + +## CHECK-START: int Runtime.testCatchPhi_double(boolean, boolean) register (after) +## CHECK-DAG: <<Val1:d\d+>> ArrayGet +## CHECK-DAG: <<Val2:d\d+>> ArrayGet +## CHECK-DAG: Phi [<<Val1>>,<<Val2>>] is_catch_phi:true + +.method public static testCatchPhi_double(ZZ)I + .registers 10 + + sget-object v0, LRuntime;->doubleArray:[D + const/4 v2, 0 + aget-wide v2, v0, v2 + const/4 v4, 1 + aget-wide v4, v0, v4 + const/4 v6, 2 + aget-wide v6, v0, v6 + + :try_start + move-wide v0, v2 # Set catch phi value + invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V + + move-wide v0, v4 # Set catch phi value + invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V + :try_end + .catchall {:try_start .. :try_end} :catch_all + + double-to-int v6, v6 + return v6 + + :catch_all + double-to-int v0, v0 + return v0 +.end method + +# Test catch-phi runtime support for 32-bit values stored on the stack. + +# Register v0 holds different integer values at two throwing instructions. +# These values were forced to spill by an always-throwing try/catch after their +# definition. Runtime is expected to find their location in the stack map and +# copy the value to the location of the catch phi. The value is then returned. + +## CHECK-START: int Runtime.testCatchPhi_singleSlot(boolean, boolean) register (after) +## CHECK: <<Val1:i\d+>> ArrayGet +## CHECK-NEXT: ParallelMove moves:[{{.*->}}{{\d+}}(sp)] +## CHECK: <<Val2:i\d+>> ArrayGet +## CHECK-NEXT: ParallelMove moves:[{{.*->}}{{\d+}}(sp)] +## CHECK: Phi [<<Val1>>,<<Val2>>] is_catch_phi:true + +.method public static testCatchPhi_singleSlot(ZZ)I + .registers 6 + + sget-object v0, LRuntime;->intArray:[I + const/4 v1, 0 + aget v1, v0, v1 + const/4 v2, 1 + aget v2, v0, v2 + const/4 v3, 2 + aget v3, v0, v3 + + # Insert a try/catch to force v1,v2,v3 to spill. + :try_start_spill + const/4 v0, 1 + invoke-static {v0}, LRuntime;->$noinline$ThrowIfTrue(Z)V + :try_end_spill + .catchall {:try_start_spill .. :try_end_spill} :catch_all_spill + return v0 # Unreachable + :catch_all_spill # Catch and continue + + :try_start + move v0, v1 # Set catch phi value + invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V + + move v0, v2 # Set catch phi value + invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V + :try_end + .catchall {:try_start .. :try_end} :catch_all + + return v3 # Normal path return. + + :catch_all + return v0 # Exceptional path #1/#2 return. +.end method + +# Test catch-phi runtime support for 64-bit values stored on the stack. + +# Register pair (v0, v1) holds different double values at two throwing instructions. +# These values were forced to spill by an always-throwing try/catch after their +# definition. Runtime is expected to find their location in the stack map and +# copy the value to the location of the catch phi. The value is converted to int +# and returned. Values were chosen so that all 64 bits are used. + +## CHECK-START: int Runtime.testCatchPhi_doubleSlot(boolean, boolean) register (after) +## CHECK: <<Val1:d\d+>> ArrayGet +## CHECK-NEXT: ParallelMove moves:[{{.*->}}2x{{\d+}}(sp)] +## CHECK: <<Val2:d\d+>> ArrayGet +## CHECK-NEXT: ParallelMove moves:[{{.*->}}2x{{\d+}}(sp)] +## CHECK: Phi [<<Val1>>,<<Val2>>] is_catch_phi:true + +.method public static testCatchPhi_doubleSlot(ZZ)I + .registers 10 + + sget-object v0, LRuntime;->doubleArray:[D + const/4 v2, 0 + aget-wide v2, v0, v2 + const/4 v4, 1 + aget-wide v4, v0, v4 + const/4 v6, 2 + aget-wide v6, v0, v6 + + # Insert a try/catch to force (v2, v3), (v4, v5), (v6, v7) to spill. + :try_start_spill + const/4 v0, 1 + invoke-static {v0}, LRuntime;->$noinline$ThrowIfTrue(Z)V + :try_end_spill + .catchall {:try_start_spill .. :try_end_spill} :catch_all_spill + return v0 # Unreachable + :catch_all_spill # Catch and continue + + :try_start + move-wide v0, v2 # Set catch phi value + invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V + + move-wide v0, v4 # Set catch phi value + invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V + :try_end + .catchall {:try_start .. :try_end} :catch_all + + double-to-int v6, v6 + return v6 # Normal path return. + + :catch_all + double-to-int v0, v0 + return v0 # Exceptional path #1/#2 return. +.end method + + + +# Helper methods and initialization. + +.method public static $noinline$ThrowIfTrue(Z)V + .registers 2 + if-nez p0, :throw + return-void + + :throw + new-instance v0, Ljava/lang/Exception; + invoke-direct {v0}, Ljava/lang/Exception;-><init>()V + throw v0 +.end method + +.method public static constructor <clinit>()V + .registers 2 + + const/4 v1, 4 + + new-array v0, v1, [I + fill-array-data v0, :array_int + sput-object v0, LRuntime;->intArray:[I + + new-array v0, v1, [J + fill-array-data v0, :array_long + sput-object v0, LRuntime;->longArray:[J + + new-array v0, v1, [F + fill-array-data v0, :array_float + sput-object v0, LRuntime;->floatArray:[F + + new-array v0, v1, [D + fill-array-data v0, :array_double + sput-object v0, LRuntime;->doubleArray:[D + + return-void + +:array_int +.array-data 4 + 0x03 # int 3 + 0x08 # int 8 + 0x2a # int 42 +.end array-data + +:array_long +.array-data 8 + 0x0000000100000002L # long (1 << 32) + 2 + 0x0000000500000003L # long (5 << 32) + 3 + 0x0000001e0000000cL # long (30 << 32) + 12 +.end array-data + +:array_float +.array-data 4 + 0x40400000 # float 3 + 0x41000000 # float 8 + 0x42280000 # float 42 +.end array-data + +:array_double +.array-data 8 + 0x400b333333333333L # double 3.4 + 0x4020cccccccccccdL # double 8.4 + 0x4045333333333333L # double 42.4 +.end array-data +.end method + +.field public static intArray:[I +.field public static longArray:[J +.field public static floatArray:[F +.field public static doubleArray:[D diff --git a/test/510-checker-try-catch/src/Main.java b/test/510-checker-try-catch/src/Main.java index ae78ba0fd0..25cdc0eb12 100644 --- a/test/510-checker-try-catch/src/Main.java +++ b/test/510-checker-try-catch/src/Main.java @@ -14,10 +14,55 @@ * limitations under the License. */ +import java.lang.reflect.Method; + public class Main { // Workaround for b/18051191. class InnerClass {} - public static void main(String[] args) {} + public enum TestPath { + ExceptionalFlow1(true, false, 3), + ExceptionalFlow2(false, true, 8), + NormalFlow(false, false, 42); + + TestPath(boolean arg1, boolean arg2, int expected) { + this.arg1 = arg1; + this.arg2 = arg2; + this.expected = expected; + } + + public boolean arg1; + public boolean arg2; + public int expected; + } + + public static void testMethod(String method) throws Exception { + Class<?> c = Class.forName("Runtime"); + Method m = c.getMethod(method, new Class[] { boolean.class, boolean.class }); + + for (TestPath path : TestPath.values()) { + Object[] arguments = new Object[] { path.arg1, path.arg2 }; + int actual = (Integer) m.invoke(null, arguments); + + if (actual != path.expected) { + throw new Error("Method: \"" + method + "\", path: " + path + ", " + + "expected: " + path.expected + ", actual: " + actual); + } + } + } + + public static void main(String[] args) throws Exception { + testMethod("testUseAfterCatch_int"); + testMethod("testUseAfterCatch_long"); + testMethod("testUseAfterCatch_float"); + testMethod("testUseAfterCatch_double"); + testMethod("testCatchPhi_const"); + testMethod("testCatchPhi_int"); + testMethod("testCatchPhi_long"); + testMethod("testCatchPhi_float"); + testMethod("testCatchPhi_double"); + testMethod("testCatchPhi_singleSlot"); + testMethod("testCatchPhi_doubleSlot"); + } } diff --git a/test/530-checker-regression-reftype-final/expected.txt b/test/530-checker-regression-reftype-final/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/530-checker-regression-reftype-final/expected.txt diff --git a/test/530-checker-regression-reftype-final/info.txt b/test/530-checker-regression-reftype-final/info.txt new file mode 100644 index 0000000000..07789d6e9e --- /dev/null +++ b/test/530-checker-regression-reftype-final/info.txt @@ -0,0 +1 @@ +Regression test for optimizing that used assume that array types are always exact. diff --git a/test/530-checker-regression-reftype-final/smali/TestCase.smali b/test/530-checker-regression-reftype-final/smali/TestCase.smali new file mode 100644 index 0000000000..8fd7bb7edb --- /dev/null +++ b/test/530-checker-regression-reftype-final/smali/TestCase.smali @@ -0,0 +1,59 @@ +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class public LTestCase; +.super Ljava/lang/Object; + +# Inliner used to assign exact type to the artificial multiple-return phi if the +# class type was final which does not hold for arrays. + +# The type information is only used by recursive calls to the inliner and is +# overwritten by the next pass of reference type propagation. Since we do not +# inline any methods from array classes, this bug cannot be triggered and we +# verify it using Checker. + +## CHECK-START: void TestCase.testInliner() reference_type_propagation_after_inlining (before) +## CHECK-DAG: CheckCast [<<Phi:l\d+>>,{{l\d+}}] +## CHECK-DAG: <<Phi>> Phi klass:java.lang.Object[] exact:false + +.method public static testInliner()V + .registers 3 + + invoke-static {}, Ljava/lang/System;->nanoTime()J + move-result-wide v0 + long-to-int v0, v0 + + invoke-static {v0}, LTestCase;->$inline$getArray(I)[Ljava/lang/Object; + move-result-object v0 + + check-cast v0, [LMain$MyClassA; + return-void + +.end method + +.method public static $inline$getArray(I)[Ljava/lang/Object; + .registers 2 + if-eqz p0, :else + + :then + const/4 v0, 2 + new-array v0, v0, [LMain$MyClassA; + return-object v0 + + :else + const/4 v0, 3 + new-array v0, v0, [LMain$MyClassB; + return-object v0 + +.end method diff --git a/test/530-checker-regression-reftype-final/src/Main.java b/test/530-checker-regression-reftype-final/src/Main.java new file mode 100644 index 0000000000..f86b515cae --- /dev/null +++ b/test/530-checker-regression-reftype-final/src/Main.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; + +public class Main { + + class MyClassA {} + class MyClassB extends MyClassA {} + + public static void main(String[] args) throws Exception { + testReferenceTypePropagation(); + invokeTestInliner(); + } + + // Reference type propagation (RTP) used to assume that if a class is final, + // then the type must be exact. This does not hold for arrays which are always + // final, i.e. not extendable, but may be assigned to from values of the + // components type subclasses. + + public static void testReferenceTypePropagation() throws Exception { + boolean expectTrue; + + // Bug #1: RTP would set the type of `array` to exact Object[]. Instruction + // simplifier would then simplify the instanceof to `false`. + Object[] array = $noinline$getArray(); + expectTrue = array instanceof MyClassA[]; + if (!expectTrue) { + throw new Exception("Incorrect type check."); + } + + // Bug #2: This is the true-branch of the instanceof above. The bound type + // for `array` would be again set to exact MyClassA[] and incorrectly + // simplify the second instanceof to `false`. + expectTrue = array instanceof MyClassB[]; + if (!expectTrue) { + throw new Exception("Incorrect type bound."); + } + } + + public static void invokeTestInliner() throws Exception { + Class<?> c = Class.forName("TestCase"); + Method m = c.getMethod("testInliner"); + m.invoke(null); + } + + public static Object[] $noinline$getArray() { + if (doThrow) throw new Error(); + return new MyClassB[2]; + } + + static boolean doThrow = false; +} diff --git a/test/998-scoped-primitive-array/check b/test/998-scoped-primitive-array/check deleted file mode 100755 index 842bdc6ae8..0000000000 --- a/test/998-scoped-primitive-array/check +++ /dev/null @@ -1,22 +0,0 @@ -#!/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. - -# Check that the string "error" isn't present -if grep error "$2"; then - exit 1 -else - exit 0 -fi diff --git a/test/998-scoped-primitive-array/expected.txt b/test/998-scoped-primitive-array/expected.txt deleted file mode 100644 index a965a70ed4..0000000000 --- a/test/998-scoped-primitive-array/expected.txt +++ /dev/null @@ -1 +0,0 @@ -Done diff --git a/test/998-scoped-primitive-array/scoped_primitive_array.cc b/test/998-scoped-primitive-array/scoped_primitive_array.cc deleted file mode 100644 index c224a06313..0000000000 --- a/test/998-scoped-primitive-array/scoped_primitive_array.cc +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "jni.h" -#include "ScopedPrimitiveArray.h" - -extern "C" JNIEXPORT jlong JNICALL Java_Main_measureByteArray(JNIEnv* env, - jclass, - jlong reps, - jbyteArray arr) { - jlong ret = 0; - for (jlong i = 0; i < reps; ++i) { - ScopedByteArrayRO sc(env, arr); - ret += sc[0] + sc[sc.size() - 1]; - } - return ret; -} - -extern "C" JNIEXPORT jlong JNICALL Java_Main_measureShortArray(JNIEnv* env, - jclass, - jlong reps, - jshortArray arr) { - jlong ret = 0; - for (jlong i = 0; i < reps; ++i) { - ScopedShortArrayRO sc(env, arr); - ret += sc[0] + sc[sc.size() - 1]; - } - return ret; -} - -extern "C" JNIEXPORT jlong JNICALL Java_Main_measureIntArray(JNIEnv* env, - jclass, - jlong reps, - jintArray arr) { - jlong ret = 0; - for (jlong i = 0; i < reps; ++i) { - ScopedIntArrayRO sc(env, arr); - ret += sc[0] + sc[sc.size() - 1]; - } - return ret; -} - -extern "C" JNIEXPORT jlong JNICALL Java_Main_measureLongArray(JNIEnv* env, - jclass, - jlong reps, - jlongArray arr) { - jlong ret = 0; - for (jlong i = 0; i < reps; ++i) { - ScopedLongArrayRO sc(env, arr); - ret += sc[0] + sc[sc.size() - 1]; - } - return ret; -} diff --git a/test/998-scoped-primitive-array/src/Main.java b/test/998-scoped-primitive-array/src/Main.java deleted file mode 100644 index 630e0dc1b6..0000000000 --- a/test/998-scoped-primitive-array/src/Main.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -public class Main { - public Main() {} - - // Measure adds the first and last element of the array by using ScopedPrimitiveArray. - static native long measureByteArray(long reps, byte[] arr); - static native long measureShortArray(long reps, short[] arr); - static native long measureIntArray(long reps, int[] arr); - static native long measureLongArray(long reps, long[] arr); - - static void checkEq(long expected, long value) { - if (expected != value) { - System.out.println("error: Expected " + expected + " but got " + value); - } - } - - static void runPerfTest(long reps) { - for (int length = 1; length <= 8192; length *= 8) { - byte[] bytes = new byte[length]; - bytes[0] = 1; - bytes[length - 1] = 2; - short[] shorts = new short[length]; - shorts[0] = 1; - shorts[length - 1] = 2; - int[] ints = new int[length]; - ints[0] = 1; - ints[length - 1] = 2; - long[] longs = new long[length]; - longs[0] = 1; - longs[length - 1] = 2; - long value = 0; - long elapsed = 0; - long start = 0; - - start = System.nanoTime(); - value = measureByteArray(reps, bytes); - elapsed = System.nanoTime() - start; - System.out.println("Byte length=" + length + " ns/op=" + (double) elapsed / reps); - checkEq(value, reps * (long) (bytes[0] + bytes[length - 1])); - - start = System.nanoTime(); - value = measureShortArray(reps, shorts); - elapsed = System.nanoTime() - start; - System.out.println("Short length=" + length + " ns/op=" + (double) elapsed / reps); - checkEq(value, reps * (long) (shorts[0] + shorts[length - 1])); - - start = System.nanoTime(); - value = measureIntArray(reps, ints); - elapsed = System.nanoTime() - start; - System.out.println("Int length=" + length + " ns/op=" + (double) elapsed / reps); - checkEq(value, reps * (ints[0] + ints[length - 1])); - - start = System.nanoTime(); - value = measureLongArray(reps, longs); - elapsed = System.nanoTime() - start; - System.out.println("Long length=" + length + " ns/op=" + (double) elapsed / reps); - checkEq(value, reps * (longs[0] + longs[length - 1])); - } - } - - public static void main(String[] args) { - System.loadLibrary(args[0]); - long iterations = 2000000; - if (args.length > 1) { - iterations = Long.parseLong(args[1], 10); - } - runPerfTest(iterations); - System.out.println("Done"); - } -} diff --git a/test/999-jni-perf/check b/test/999-jni-perf/check deleted file mode 100755 index ffbb8cf17e..0000000000 --- a/test/999-jni-perf/check +++ /dev/null @@ -1,18 +0,0 @@ -#!/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. - -# Only compare the last line. -tail -n 1 "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null
\ No newline at end of file diff --git a/test/999-jni-perf/expected.txt b/test/999-jni-perf/expected.txt deleted file mode 100644 index a965a70ed4..0000000000 --- a/test/999-jni-perf/expected.txt +++ /dev/null @@ -1 +0,0 @@ -Done diff --git a/test/999-jni-perf/src/Main.java b/test/999-jni-perf/src/Main.java deleted file mode 100644 index 032e70011a..0000000000 --- a/test/999-jni-perf/src/Main.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -public class Main { - public Main() { - } - - private static final String MSG = "ABCDE"; - - native int perfJniEmptyCall(); - native int perfSOACall(); - native int perfSOAUncheckedCall(); - - int runPerfTest(long N) { - long start = System.nanoTime(); - for (long i = 0; i < N; i++) { - char c = MSG.charAt(2); - } - long elapse = System.nanoTime() - start; - System.out.println("Fast JNI (charAt): " + (double)elapse / N); - - start = System.nanoTime(); - for (long i = 0; i < N; i++) { - perfJniEmptyCall(); - } - elapse = System.nanoTime() - start; - System.out.println("Empty call: " + (double)elapse / N); - - start = System.nanoTime(); - for (long i = 0; i < N; i++) { - perfSOACall(); - } - elapse = System.nanoTime() - start; - System.out.println("SOA call: " + (double)elapse / N); - - start = System.nanoTime(); - for (long i = 0; i < N; i++) { - perfSOAUncheckedCall(); - } - elapse = System.nanoTime() - start; - System.out.println("SOA unchecked call: " + (double)elapse / N); - - return 0; - } - - public static void main(String[] args) { - System.loadLibrary(args[0]); - long iterations = 1000000; - if (args.length > 1) { - iterations = Long.parseLong(args[1], 10); - } - Main m = new Main(); - m.runPerfTest(iterations); - System.out.println("Done"); - } -} diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk index af945fb66e..7f05a043d8 100644 --- a/test/Android.libarttest.mk +++ b/test/Android.libarttest.mk @@ -38,9 +38,7 @@ LIBARTTEST_COMMON_SRC_FILES := \ 457-regs/regs_jni.cc \ 461-get-reference-vreg/get_reference_vreg_jni.cc \ 466-get-live-vreg/get_live_vreg_jni.cc \ - 497-inlining-and-class-loader/clear_dex_cache.cc \ - 998-scoped-primitive-array/scoped_primitive_array.cc \ - 999-jni-perf/perf-jni.cc + 497-inlining-and-class-loader/clear_dex_cache.cc ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttestd.so diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh index e28de09317..a84365a223 100755 --- a/tools/run-libcore-tests.sh +++ b/tools/run-libcore-tests.sh @@ -32,6 +32,11 @@ if [ ! -f $test_jar ]; then exit 1 fi +emulator="no" +if [ "$ANDROID_SERIAL" = "emulator-5554" ]; then + emulator="yes" +fi + # Packages that currently work correctly with the expectation files. working_packages=("dalvik.system" "libcore.icu" @@ -81,10 +86,12 @@ while true; do # Remove the --debug from the arguments. vogar_args=${vogar_args/$1} vogar_args="$vogar_args --vm-arg -XXlib:libartd.so" - # Increase the timeout, as vogar cannot set individual test - # timeout when being asked to run packages, and some tests go above - # the default timeout. - vogar_args="$vogar_args --timeout 240" + if [ "$emulator" = "no" ]; then + # Increase the timeout, as vogar cannot set individual test + # timeout when being asked to run packages, and some tests go above + # the default timeout. + vogar_args="$vogar_args --timeout 240" + fi shift elif [[ "$1" == "" ]]; then break @@ -93,6 +100,11 @@ while true; do fi done +if [ "$emulator" = "yes" ]; then + # Be very patient with the emulator. + vogar_args="$vogar_args --timeout 480" +fi + # Run the tests using vogar. echo "Running tests for the following test packages:" echo ${working_packages[@]} | tr " " "\n" |