Inline across dex files.
Change-Id: I5c2c44f5130b50f0bad21a6877a3935dc60b4a85
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index ec7fd62..cbb41b1 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -616,8 +616,8 @@
DCHECK((optimized_invoke_type == invoke_type) || (optimized_invoke_type != kDirect)
|| compiler_driver_->GetCompilerOptions().GetCompilePic());
bool is_recursive =
- (target_method.dex_method_index == outer_compilation_unit_->GetDexMethodIndex());
- DCHECK(!is_recursive || (target_method.dex_file == outer_compilation_unit_->GetDexFile()));
+ (target_method.dex_method_index == dex_compilation_unit_->GetDexMethodIndex());
+ DCHECK(!is_recursive || (target_method.dex_file == dex_compilation_unit_->GetDexFile()));
invoke = new (arena_) HInvokeStaticOrDirect(
arena_, number_of_arguments, return_type, dex_pc, target_method.dex_method_index,
is_recursive, optimized_invoke_type);
@@ -711,7 +711,7 @@
uint16_t field_index = instruction.VRegB_21c();
ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<4> hs(soa.Self());
+ StackHandleScope<5> hs(soa.Self());
Handle<mirror::DexCache> dex_cache(hs.NewHandle(
dex_compilation_unit_->GetClassLinker()->FindDexCache(*dex_compilation_unit_->GetDexFile())));
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
@@ -724,8 +724,10 @@
return false;
}
+ Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
+ outer_compilation_unit_->GetClassLinker()->FindDexCache(*outer_compilation_unit_->GetDexFile())));
Handle<mirror::Class> referrer_class(hs.NewHandle(compiler_driver_->ResolveCompilingMethodsClass(
- soa, dex_cache, class_loader, outer_compilation_unit_)));
+ soa, outer_dex_cache, class_loader, outer_compilation_unit_)));
// The index at which the field's class is stored in the DexCache's type array.
uint32_t storage_index;
@@ -738,7 +740,7 @@
// TODO: find out why this check is needed.
bool is_in_dex_cache = compiler_driver_->CanAssumeTypeIsPresentInDexCache(
- *outer_compilation_unit_->GetDexFile(), storage_index);
+ *dex_compilation_unit_->GetDexFile(), storage_index);
bool is_initialized = resolved_field->GetDeclaringClass()->IsInitialized() && is_in_dex_cache;
bool is_referrer_class = (referrer_class.Get() == resolved_field->GetDeclaringClass());
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 968fe3e..82d6357 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -85,11 +85,9 @@
return false;
}
+ bool can_use_dex_cache = true;
if (resolved_method->GetDexFile()->GetLocation().compare(outer_dex_file.GetLocation()) != 0) {
- VLOG(compiler) << "Did not inline "
- << PrettyMethod(method_index, outer_dex_file)
- << " because it is in a different dex file";
- return false;
+ can_use_dex_cache = false;
}
const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
@@ -124,7 +122,7 @@
return false;
}
- if (!TryBuildAndInline(resolved_method, invoke_instruction, method_index)) {
+ if (!TryBuildAndInline(resolved_method, invoke_instruction, method_index, can_use_dex_cache)) {
resolved_method->SetShouldNotInline();
return false;
}
@@ -136,7 +134,8 @@
bool HInliner::TryBuildAndInline(Handle<mirror::ArtMethod> resolved_method,
HInvoke* invoke_instruction,
- uint32_t method_index) const {
+ uint32_t method_index,
+ bool can_use_dex_cache) const {
ScopedObjectAccess soa(Thread::Current());
const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
const DexFile& outer_dex_file = *outer_compilation_unit_.GetDexFile();
@@ -145,10 +144,10 @@
nullptr,
outer_compilation_unit_.GetClassLoader(),
outer_compilation_unit_.GetClassLinker(),
- outer_dex_file,
+ *resolved_method->GetDexFile(),
code_item,
resolved_method->GetDeclaringClass()->GetDexClassDefIndex(),
- method_index,
+ resolved_method->GetDexMethodIndex(),
resolved_method->GetAccessFlags(),
nullptr);
@@ -159,7 +158,7 @@
HGraphBuilder builder(callee_graph,
&dex_compilation_unit,
&outer_compilation_unit_,
- &outer_dex_file,
+ resolved_method->GetDexFile(),
compiler_driver_,
&inline_stats);
@@ -200,7 +199,7 @@
if (depth_ + 1 < kDepthLimit) {
HInliner inliner(
- callee_graph, outer_compilation_unit_, compiler_driver_, stats_, depth_ + 1);
+ callee_graph, dex_compilation_unit, compiler_driver_, stats_, depth_ + 1);
inliner.Run();
}
@@ -235,6 +234,13 @@
<< " needs an environment";
return false;
}
+
+ if (!can_use_dex_cache && current->NeedsDexCache()) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " could not be inlined because " << current->DebugName()
+ << " it is in a different dex file and requires access to the dex cache";
+ return false;
+ }
}
}
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 1251977..4b7e2ff 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -48,7 +48,8 @@
bool TryInline(HInvoke* invoke_instruction, uint32_t method_index, InvokeType invoke_type) const;
bool TryBuildAndInline(Handle<mirror::ArtMethod> resolved_method,
HInvoke* invoke_instruction,
- uint32_t method_index) const;
+ uint32_t method_index,
+ bool can_use_dex_cache) const;
const DexCompilationUnit& outer_compilation_unit_;
CompilerDriver* const compiler_driver_;
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index a35fa1d..07ff8ba 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1200,6 +1200,8 @@
return NeedsEnvironment() || IsLoadClass() || IsLoadString();
}
+ virtual bool NeedsDexCache() const { return false; }
+
protected:
virtual const HUserRecord<HInstruction*> InputRecordAt(size_t i) const = 0;
virtual void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) = 0;
@@ -2090,6 +2092,7 @@
InvokeType GetInvokeType() const { return invoke_type_; }
bool IsRecursive() const { return is_recursive_; }
+ bool NeedsDexCache() const OVERRIDE { return !IsRecursive(); }
DECLARE_INSTRUCTION(InvokeStaticOrDirect);
@@ -2972,6 +2975,8 @@
return loaded_class_rti_.IsExact();
}
+ bool NeedsDexCache() const OVERRIDE { return !is_referrers_class_; }
+
DECLARE_INSTRUCTION(LoadClass);
private:
@@ -3007,6 +3012,7 @@
// TODO: Can we deopt or debug when we resolve a string?
bool NeedsEnvironment() const OVERRIDE { return false; }
+ bool NeedsDexCache() const OVERRIDE { return true; }
DECLARE_INSTRUCTION(LoadString);
diff --git a/test/462-checker-inlining-across-dex-files/expected.txt b/test/462-checker-inlining-across-dex-files/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/462-checker-inlining-across-dex-files/expected.txt
diff --git a/test/462-checker-inlining-across-dex-files/info.txt b/test/462-checker-inlining-across-dex-files/info.txt
new file mode 100644
index 0000000..57008c3
--- /dev/null
+++ b/test/462-checker-inlining-across-dex-files/info.txt
@@ -0,0 +1 @@
+Check our inlining heuristics across dex files in optimizing.
diff --git a/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java b/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java
new file mode 100644
index 0000000..61f4e43
--- /dev/null
+++ b/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java
@@ -0,0 +1,50 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+public class OtherDex {
+ public static void emptyMethod() {
+ }
+
+ public static int returnIntMethod() {
+ return 38;
+ }
+
+ public static int returnOtherDexStatic() {
+ return myStatic;
+ }
+
+ public static int returnMainStatic() {
+ return Main.myStatic;
+ }
+
+ public static int recursiveCall() {
+ return recursiveCall();
+ }
+
+ public static String returnString() {
+ return "OtherDex";
+ }
+
+ public static Class returnOtherDexClass() {
+ return OtherDex.class;
+ }
+
+ public static Class returnMainClass() {
+ return Main.class;
+ }
+
+ static int myStatic = 1;
+}
diff --git a/test/462-checker-inlining-across-dex-files/src/Main.java b/test/462-checker-inlining-across-dex-files/src/Main.java
new file mode 100644
index 0000000..23956c0
--- /dev/null
+++ b/test/462-checker-inlining-across-dex-files/src/Main.java
@@ -0,0 +1,139 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+public class Main {
+
+ // CHECK-START: void Main.inlineEmptyMethod() inliner (before)
+ // CHECK-DAG: [[Invoke:v\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: ReturnVoid
+
+ // CHECK-START: void Main.inlineEmptyMethod() inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ public static void inlineEmptyMethod() {
+ OtherDex.emptyMethod();
+ }
+
+ // CHECK-START: int Main.inlineReturnIntMethod() inliner (before)
+ // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: int Main.inlineReturnIntMethod() inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ // CHECK-START: int Main.inlineReturnIntMethod() inliner (after)
+ // CHECK-DAG: [[Const38:i\d+]] IntConstant 38
+ // CHECK-DAG: Return [ [[Const38]] ]
+
+ public static int inlineReturnIntMethod() {
+ return OtherDex.returnIntMethod();
+ }
+
+ // CHECK-START: int Main.dontInlineOtherDexStatic() inliner (before)
+ // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: int Main.dontInlineOtherDexStatic() inliner (after)
+ // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+ public static int dontInlineOtherDexStatic() {
+ return OtherDex.returnOtherDexStatic();
+ }
+
+ // CHECK-START: int Main.inlineMainStatic() inliner (before)
+ // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: int Main.inlineMainStatic() inliner (after)
+ // CHECK-DAG: [[Static:i\d+]] StaticFieldGet
+ // CHECK-DAG: Return [ [[Static]] ]
+ public static int inlineMainStatic() {
+ return OtherDex.returnMainStatic();
+ }
+
+ // CHECK-START: int Main.dontInlineRecursiveCall() inliner (before)
+ // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: int Main.dontInlineRecursiveCall() inliner (after)
+ // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+ public static int dontInlineRecursiveCall() {
+ return OtherDex.recursiveCall();
+ }
+
+ // CHECK-START: java.lang.String Main.dontInlineReturnString() inliner (before)
+ // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: java.lang.String Main.dontInlineReturnString() inliner (after)
+ // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+ public static String dontInlineReturnString() {
+ return OtherDex.returnString();
+ }
+
+ // CHECK-START: java.lang.Class Main.dontInlineOtherDexClass() inliner (before)
+ // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: java.lang.Class Main.dontInlineOtherDexClass() inliner (after)
+ // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+ public static Class dontInlineOtherDexClass() {
+ return OtherDex.returnOtherDexClass();
+ }
+
+ // CHECK-START: java.lang.Class Main.inlineMainClass() inliner (before)
+ // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: java.lang.Class Main.inlineMainClass() inliner (after)
+ // CHECK-DAG: [[Class:l\d+]] LoadClass
+ // CHECK-DAG: Return [ [[Class]] ]
+ public static Class inlineMainClass() {
+ return OtherDex.returnMainClass();
+ }
+
+ public static void main(String[] args) {
+ inlineEmptyMethod();
+ if (inlineReturnIntMethod() != 38) {
+ throw new Error("Expected 38");
+ }
+
+ if (dontInlineOtherDexStatic() != 1) {
+ throw new Error("Expected 1");
+ }
+
+ if (inlineMainStatic() != 42) {
+ throw new Error("Expected 42");
+ }
+
+ if (dontInlineReturnString() != "OtherDex") {
+ throw new Error("Expected OtherDex");
+ }
+
+ if (dontInlineOtherDexClass() != OtherDex.class) {
+ throw new Error("Expected " + OtherDex.class);
+ }
+
+ if (inlineMainClass() != Main.class) {
+ throw new Error("Expected " + Main.class);
+ }
+ }
+
+ public static int myStatic = 42;
+}
diff --git a/test/etc/default-build b/test/etc/default-build
index 58c9564..928de57 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -39,7 +39,7 @@
fi
mkdir classes
-${JAVAC} -d classes `find src -name '*.java'`
+${JAVAC} -implicit:none -classpath src-multidex -d classes `find src -name '*.java'`
if [ -d src2 ]; then
${JAVAC} -d classes `find src2 -name '*.java'`
@@ -75,3 +75,14 @@
if [ ${NEED_DEX} = "true" ]; then
zip $TEST_NAME.jar classes.dex
fi
+
+# Create a single jar with two dex files for multidex.
+if [ -d src-multidex ]; then
+ mkdir classes2
+ ${JAVAC} -implicit:none -classpath src -d classes2 `find src-multidex -name '*.java'`
+ if [ ${NEED_DEX} = "true" ]; then
+ ${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex \
+ --dump-width=1000 ${DX_FLAGS} classes2
+ zip $TEST_NAME.jar classes.dex classes2.dex
+ fi
+fi