Merge "Remove ALWAYS_INLINE from ReadBarrier::Mark()."
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 6f32e07..87eff82 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -66,7 +66,6 @@
jit/jit_compiler.cc \
jni/quick/calling_convention.cc \
jni/quick/jni_compiler.cc \
- optimizing/boolean_simplifier.cc \
optimizing/bounds_check_elimination.cc \
optimizing/builder.cc \
optimizing/code_generator.cc \
@@ -94,6 +93,7 @@
optimizing/prepare_for_register_allocation.cc \
optimizing/reference_type_propagation.cc \
optimizing/register_allocator.cc \
+ optimizing/select_generator.cc \
optimizing/sharpening.cc \
optimizing/side_effects_analysis.cc \
optimizing/ssa_builder.cc \
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 478d169..6774758 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -179,7 +179,7 @@
if (compiler_options_->GetGenerateDebugInfo()) {
#ifdef __ANDROID__
- const char* prefix = GetAndroidData();
+ const char* prefix = "/data/misc/trace";
#else
const char* prefix = "/tmp";
#endif
@@ -188,7 +188,8 @@
std::string perf_filename = std::string(prefix) + "/perf-" + std::to_string(getpid()) + ".map";
perf_file_.reset(OS::CreateEmptyFileWriteOnly(perf_filename.c_str()));
if (perf_file_ == nullptr) {
- LOG(FATAL) << "Could not create perf file at " << perf_filename;
+ LOG(ERROR) << "Could not create perf file at " << perf_filename <<
+ " Are you on a user build? Perf only works on userdebug/eng builds";
}
}
}
@@ -223,7 +224,7 @@
ArtMethod* method_to_compile = method->GetInterfaceMethodIfProxy(sizeof(void*));
JitCodeCache* const code_cache = runtime->GetJit()->GetCodeCache();
success = compiler_driver_->GetCompiler()->JitCompile(self, code_cache, method_to_compile);
- if (success && compiler_options_->GetGenerateDebugInfo()) {
+ if (success && perf_file_ != nullptr) {
const void* ptr = method_to_compile->GetEntryPointFromQuickCompiledCode();
std::ostringstream stream;
stream << std::hex
diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc
deleted file mode 100644
index f0cafc8..0000000
--- a/compiler/optimizing/boolean_simplifier.cc
+++ /dev/null
@@ -1,136 +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 "boolean_simplifier.h"
-
-namespace art {
-
-void HBooleanSimplifier::TryRemovingNegatedCondition(HBasicBlock* block) {
- DCHECK(block->EndsWithIf());
-
- // Check if the condition is a Boolean negation.
- HIf* if_instruction = block->GetLastInstruction()->AsIf();
- HInstruction* boolean_not = if_instruction->InputAt(0);
- if (!boolean_not->IsBooleanNot()) {
- return;
- }
-
- // Make BooleanNot's input the condition of the If and swap branches.
- if_instruction->ReplaceInput(boolean_not->InputAt(0), 0);
- block->SwapSuccessors();
-
- // Remove the BooleanNot if it is now unused.
- if (!boolean_not->HasUses()) {
- boolean_not->GetBlock()->RemoveInstruction(boolean_not);
- }
-}
-
-// Returns true if 'block1' and 'block2' are empty, merge into the same single
-// 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()[0];
- HBasicBlock* succ2 = block2->GetSuccessors()[0];
- return succ1 == succ2 && succ1->GetPredecessors().size() == 2u;
-}
-
-// Returns true if the outcome of the branching matches the boolean value of
-// the branching condition.
-static bool PreservesCondition(HInstruction* input_true, HInstruction* input_false) {
- return input_true->IsIntConstant() && input_true->AsIntConstant()->IsOne()
- && input_false->IsIntConstant() && input_false->AsIntConstant()->IsZero();
-}
-
-// Returns true if the outcome of the branching is exactly opposite of the
-// boolean value of the branching condition.
-static bool NegatesCondition(HInstruction* input_true, HInstruction* input_false) {
- return input_true->IsIntConstant() && input_true->AsIntConstant()->IsZero()
- && input_false->IsIntConstant() && input_false->AsIntConstant()->IsOne();
-}
-
-void HBooleanSimplifier::TryRemovingBooleanSelection(HBasicBlock* block) {
- DCHECK(block->EndsWithIf());
-
- // Find elements of the pattern.
- HIf* if_instruction = block->GetLastInstruction()->AsIf();
- HBasicBlock* true_block = if_instruction->IfTrueSuccessor();
- HBasicBlock* false_block = if_instruction->IfFalseSuccessor();
- if (!BlocksDoMergeTogether(true_block, false_block)) {
- return;
- }
- HBasicBlock* merge_block = true_block->GetSuccessors()[0];
- if (!merge_block->HasSinglePhi()) {
- return;
- }
- HPhi* phi = merge_block->GetFirstPhi()->AsPhi();
- HInstruction* true_value = phi->InputAt(merge_block->GetPredecessorIndexOf(true_block));
- HInstruction* false_value = phi->InputAt(merge_block->GetPredecessorIndexOf(false_block));
-
- // Check if the selection negates/preserves the value of the condition and
- // if so, generate a suitable replacement instruction.
- HInstruction* if_condition = if_instruction->InputAt(0);
-
- // Don't change FP compares. The definition of compares involving NaNs forces
- // the compares to be done as written by the user.
- if (if_condition->IsCondition() &&
- Primitive::IsFloatingPointType(if_condition->InputAt(0)->GetType())) {
- return;
- }
-
- HInstruction* replacement;
- if (NegatesCondition(true_value, false_value)) {
- replacement = graph_->InsertOppositeCondition(if_condition, if_instruction);
- } else if (PreservesCondition(true_value, false_value)) {
- replacement = if_condition;
- } else {
- return;
- }
-
- // Replace the selection outcome with the new instruction.
- phi->ReplaceWith(replacement);
- merge_block->RemovePhi(phi);
-
- // Delete the true branch and merge the resulting chain of blocks
- // 'block->false_block->merge_block' into one.
- true_block->DisconnectAndDelete();
- block->MergeWith(false_block);
- block->MergeWith(merge_block);
-
- // No need to update any dominance information, as we are simplifying
- // a simple diamond shape, where the join block is merged with the
- // entry block. Any following blocks would have had the join block
- // as a dominator, and `MergeWith` handles changing that to the
- // entry block.
-}
-
-void HBooleanSimplifier::Run() {
- // Iterate in post order in the unlikely case that removing one occurrence of
- // the selection pattern empties a branch block of another occurrence.
- // Otherwise the order does not matter.
- for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
- if (!block->EndsWithIf()) continue;
-
- // If condition is negated, remove the negation and swap the branches.
- TryRemovingNegatedCondition(block);
-
- // If this is a boolean-selection diamond pattern, replace its result with
- // the condition value (or its negation) and simplify the graph.
- TryRemovingBooleanSelection(block);
- }
-}
-
-} // namespace art
diff --git a/compiler/optimizing/boolean_simplifier.h b/compiler/optimizing/boolean_simplifier.h
deleted file mode 100644
index e12a12c..0000000
--- a/compiler/optimizing/boolean_simplifier.h
+++ /dev/null
@@ -1,81 +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.
- */
-
-// This optimization recognizes two common patterns:
-// (a) Boolean selection: Casting a boolean to an integer or negating it is
-// carried out with an If statement selecting from zero/one integer
-// constants. Because Boolean values are represented as zero/one, the
-// pattern can be replaced with the condition instruction itself or its
-// negation, depending on the layout.
-// (b) Negated condition: Instruction simplifier may replace an If's condition
-// with a boolean value. If this value is the result of a Boolean negation,
-// the true/false branches can be swapped and negation removed.
-
-// Example: Negating a boolean value
-// B1:
-// z1 ParameterValue
-// i2 IntConstant 0
-// i3 IntConstant 1
-// v4 Goto B2
-// B2:
-// z5 NotEquals [ z1 i2 ]
-// v6 If [ z5 ] then B3 else B4
-// B3:
-// v7 Goto B5
-// B4:
-// v8 Goto B5
-// B5:
-// i9 Phi [ i3 i2 ]
-// v10 Return [ i9 ]
-// turns into
-// B1:
-// z1 ParameterValue
-// i2 IntConstant 0
-// v4 Goto B2
-// B2:
-// z11 Equals [ z1 i2 ]
-// v10 Return [ z11 ]
-// B3, B4, B5: removed
-
-// Note: in order to recognize empty blocks, this optimization must be run
-// after the instruction simplifier has removed redundant suspend checks.
-
-#ifndef ART_COMPILER_OPTIMIZING_BOOLEAN_SIMPLIFIER_H_
-#define ART_COMPILER_OPTIMIZING_BOOLEAN_SIMPLIFIER_H_
-
-#include "optimization.h"
-
-namespace art {
-
-class HBooleanSimplifier : public HOptimization {
- public:
- explicit HBooleanSimplifier(HGraph* graph)
- : HOptimization(graph, kBooleanSimplifierPassName) {}
-
- void Run() OVERRIDE;
-
- static constexpr const char* kBooleanSimplifierPassName = "boolean_simplifier";
-
- private:
- void TryRemovingNegatedCondition(HBasicBlock* block);
- void TryRemovingBooleanSelection(HBasicBlock* block);
-
- DISALLOW_COPY_AND_ASSIGN(HBooleanSimplifier);
-};
-
-} // namespace art
-
-#endif // ART_COMPILER_OPTIMIZING_BOOLEAN_SIMPLIFIER_H_
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 5f00f0a..c2d9edd 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1285,11 +1285,9 @@
}
void CodeGeneratorARM::MoveLocation(Location dst, Location src, Primitive::Type dst_type) {
- if (Primitive::Is64BitType(dst_type)) {
- Move64(dst, src);
- } else {
- Move32(dst, src);
- }
+ HParallelMove move(GetGraph()->GetArena());
+ move.AddMove(src, dst, dst_type, nullptr);
+ GetMoveResolver()->EmitNativeCode(&move);
}
void CodeGeneratorARM::AddLocationAsTemp(Location location, LocationSummary* locations) {
@@ -1612,6 +1610,32 @@
/* false_target */ nullptr);
}
+void LocationsBuilderARM::VisitSelect(HSelect* select) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
+ if (Primitive::IsFloatingPointType(select->GetType())) {
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ } else {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
+ if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
+ locations->SetInAt(2, Location::RequiresRegister());
+ }
+ locations->SetOut(Location::SameAsFirstInput());
+}
+
+void InstructionCodeGeneratorARM::VisitSelect(HSelect* select) {
+ LocationSummary* locations = select->GetLocations();
+ Label false_target;
+ GenerateTestAndBranch(select,
+ /* condition_input_index */ 2,
+ /* true_target */ nullptr,
+ &false_target);
+ codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
+ __ Bind(&false_target);
+}
+
void LocationsBuilderARM::VisitNativeDebugInfo(HNativeDebugInfo* info) {
new (GetGraph()->GetArena()) LocationSummary(info);
}
@@ -1632,7 +1656,7 @@
case Primitive::kPrimLong:
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
- if (cond->NeedsMaterialization()) {
+ if (!cond->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
}
break;
@@ -1641,7 +1665,7 @@
case Primitive::kPrimDouble:
locations->SetInAt(0, Location::RequiresFpuRegister());
locations->SetInAt(1, Location::RequiresFpuRegister());
- if (cond->NeedsMaterialization()) {
+ if (!cond->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
break;
@@ -1649,14 +1673,14 @@
default:
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
- if (cond->NeedsMaterialization()) {
+ if (!cond->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
}
}
void InstructionCodeGeneratorARM::HandleCondition(HCondition* cond) {
- if (!cond->NeedsMaterialization()) {
+ if (cond->IsEmittedAtUseSite()) {
return;
}
@@ -4973,6 +4997,8 @@
if (source.IsRegister()) {
if (destination.IsRegister()) {
__ Mov(destination.AsRegister<Register>(), source.AsRegister<Register>());
+ } else if (destination.IsFpuRegister()) {
+ __ vmovsr(destination.AsFpuRegister<SRegister>(), source.AsRegister<Register>());
} else {
DCHECK(destination.IsStackSlot());
__ StoreToOffset(kStoreWord, source.AsRegister<Register>(),
@@ -4990,7 +5016,9 @@
__ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
}
} else if (source.IsFpuRegister()) {
- if (destination.IsFpuRegister()) {
+ if (destination.IsRegister()) {
+ __ vmovrs(destination.AsRegister<Register>(), source.AsFpuRegister<SRegister>());
+ } else if (destination.IsFpuRegister()) {
__ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>());
} else {
DCHECK(destination.IsStackSlot());
@@ -5014,6 +5042,10 @@
if (destination.IsRegisterPair()) {
__ Mov(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>());
__ Mov(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>());
+ } else if (destination.IsFpuRegisterPair()) {
+ __ vmovdrr(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
+ source.AsRegisterPairLow<Register>(),
+ source.AsRegisterPairHigh<Register>());
} else {
DCHECK(destination.IsDoubleStackSlot()) << destination;
DCHECK(ExpectedPairLayout(source));
@@ -5021,7 +5053,11 @@
kStoreWordPair, source.AsRegisterPairLow<Register>(), SP, destination.GetStackIndex());
}
} else if (source.IsFpuRegisterPair()) {
- if (destination.IsFpuRegisterPair()) {
+ if (destination.IsRegisterPair()) {
+ __ vmovrrd(destination.AsRegisterPairLow<Register>(),
+ destination.AsRegisterPairHigh<Register>(),
+ FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()));
+ } else if (destination.IsFpuRegisterPair()) {
__ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()));
} else {
@@ -6599,6 +6635,29 @@
}
}
+void LocationsBuilderARM::VisitClassTableGet(HClassTableGet* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARM::VisitClassTableGet(HClassTableGet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ uint32_t method_offset = 0;
+ if (instruction->GetTableKind() == HClassTableGet::kVTable) {
+ method_offset = mirror::Class::EmbeddedVTableEntryOffset(
+ instruction->GetIndex(), kArmPointerSize).SizeValue();
+ } else {
+ method_offset = mirror::Class::EmbeddedImTableEntryOffset(
+ instruction->GetIndex() % mirror::Class::kImtSize, kArmPointerSize).Uint32Value();
+ }
+ __ LoadFromOffset(kLoadWord,
+ locations->Out().AsRegister<Register>(),
+ locations->InAt(0).AsRegister<Register>(),
+ method_offset);
+}
+
#undef __
#undef QUICK_ENTRY_POINT
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index c0e3959..a59024e 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2510,13 +2510,13 @@
locations->SetInAt(1, ARM64EncodableConstantOrRegister(instruction->InputAt(1), instruction));
}
- if (instruction->NeedsMaterialization()) {
+ if (!instruction->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
}
void InstructionCodeGeneratorARM64::HandleCondition(HCondition* instruction) {
- if (!instruction->NeedsMaterialization()) {
+ if (instruction->IsEmittedAtUseSite()) {
return;
}
@@ -3004,6 +3004,32 @@
/* false_target */ nullptr);
}
+void LocationsBuilderARM64::VisitSelect(HSelect* select) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
+ if (Primitive::IsFloatingPointType(select->GetType())) {
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ } else {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
+ if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
+ locations->SetInAt(2, Location::RequiresRegister());
+ }
+ locations->SetOut(Location::SameAsFirstInput());
+}
+
+void InstructionCodeGeneratorARM64::VisitSelect(HSelect* select) {
+ LocationSummary* locations = select->GetLocations();
+ vixl::Label false_target;
+ GenerateTestAndBranch(select,
+ /* condition_input_index */ 2,
+ /* true_target */ nullptr,
+ &false_target);
+ codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
+ __ Bind(&false_target);
+}
+
void LocationsBuilderARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
new (GetGraph()->GetArena()) LocationSummary(info);
}
@@ -4983,6 +5009,29 @@
__ Bind(slow_path->GetExitLabel());
}
+void LocationsBuilderARM64::VisitClassTableGet(HClassTableGet* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARM64::VisitClassTableGet(HClassTableGet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ uint32_t method_offset = 0;
+ if (instruction->GetTableKind() == HClassTableGet::kVTable) {
+ method_offset = mirror::Class::EmbeddedVTableEntryOffset(
+ instruction->GetIndex(), kArm64PointerSize).SizeValue();
+ } else {
+ method_offset = mirror::Class::EmbeddedImTableEntryOffset(
+ instruction->GetIndex() % mirror::Class::kImtSize, kArm64PointerSize).Uint32Value();
+ }
+ __ Ldr(XRegisterFrom(locations->Out()),
+ MemOperand(XRegisterFrom(locations->InAt(0)), method_offset));
+}
+
+
+
#undef __
#undef QUICK_ENTRY_POINT
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 5bd136a..85ffd66 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -614,6 +614,31 @@
Exchange(loc1.GetStackIndex(), loc2.GetStackIndex(), /* double_slot */ false);
} else if (loc1.IsDoubleStackSlot() && loc2.IsDoubleStackSlot()) {
Exchange(loc1.GetStackIndex(), loc2.GetStackIndex(), /* double_slot */ true);
+ } else if ((loc1.IsRegister() && loc2.IsStackSlot()) ||
+ (loc1.IsStackSlot() && loc2.IsRegister())) {
+ Register reg = loc1.IsRegister() ? loc1.AsRegister<Register>()
+ : loc2.AsRegister<Register>();
+ intptr_t offset = loc1.IsStackSlot() ? loc1.GetStackIndex()
+ : loc2.GetStackIndex();
+ __ Move(TMP, reg);
+ __ LoadFromOffset(kLoadWord, reg, SP, offset);
+ __ StoreToOffset(kStoreWord, TMP, SP, offset);
+ } else if ((loc1.IsRegisterPair() && loc2.IsDoubleStackSlot()) ||
+ (loc1.IsDoubleStackSlot() && loc2.IsRegisterPair())) {
+ Register reg_l = loc1.IsRegisterPair() ? loc1.AsRegisterPairLow<Register>()
+ : loc2.AsRegisterPairLow<Register>();
+ Register reg_h = loc1.IsRegisterPair() ? loc1.AsRegisterPairHigh<Register>()
+ : loc2.AsRegisterPairHigh<Register>();
+ intptr_t offset_l = loc1.IsDoubleStackSlot() ? loc1.GetStackIndex()
+ : loc2.GetStackIndex();
+ intptr_t offset_h = loc1.IsDoubleStackSlot() ? loc1.GetHighStackIndex(kMipsWordSize)
+ : loc2.GetHighStackIndex(kMipsWordSize);
+ __ Move(TMP, reg_l);
+ __ Move(AT, reg_h);
+ __ LoadFromOffset(kLoadWord, reg_l, SP, offset_l);
+ __ LoadFromOffset(kLoadWord, reg_h, SP, offset_h);
+ __ StoreToOffset(kStoreWord, TMP, SP, offset_l);
+ __ StoreToOffset(kStoreWord, AT, SP, offset_h);
} else {
LOG(FATAL) << "Swap between " << loc1 << " and " << loc2 << " is unsupported";
}
@@ -2245,13 +2270,13 @@
locations->SetInAt(1, Location::RequiresFpuRegister());
break;
}
- if (instruction->NeedsMaterialization()) {
+ if (!instruction->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
}
void InstructionCodeGeneratorMIPS::HandleCondition(HCondition* instruction) {
- if (!instruction->NeedsMaterialization()) {
+ if (instruction->IsEmittedAtUseSite()) {
return;
}
@@ -3381,6 +3406,32 @@
/* false_target */ nullptr);
}
+void LocationsBuilderMIPS::VisitSelect(HSelect* select) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
+ if (Primitive::IsFloatingPointType(select->GetType())) {
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ } else {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
+ if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
+ locations->SetInAt(2, Location::RequiresRegister());
+ }
+ locations->SetOut(Location::SameAsFirstInput());
+}
+
+void InstructionCodeGeneratorMIPS::VisitSelect(HSelect* select) {
+ LocationSummary* locations = select->GetLocations();
+ MipsLabel false_target;
+ GenerateTestAndBranch(select,
+ /* condition_input_index */ 2,
+ /* true_target */ nullptr,
+ &false_target);
+ codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
+ __ Bind(&false_target);
+}
+
void LocationsBuilderMIPS::VisitNativeDebugInfo(HNativeDebugInfo* info) {
new (GetGraph()->GetArena()) LocationSummary(info);
}
@@ -5236,6 +5287,14 @@
codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
}
+void LocationsBuilderMIPS::VisitClassTableGet(HClassTableGet*) {
+ UNIMPLEMENTED(FATAL) << "ClassTableGet is unimplemented on mips";
+}
+
+void InstructionCodeGeneratorMIPS::VisitClassTableGet(HClassTableGet*) {
+ UNIMPLEMENTED(FATAL) << "ClassTableGet is unimplemented on mips";
+}
+
#undef __
#undef QUICK_ENTRY_POINT
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index e3115f4..3c928de 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -1872,13 +1872,13 @@
locations->SetInAt(1, Location::RequiresFpuRegister());
break;
}
- if (instruction->NeedsMaterialization()) {
+ if (!instruction->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
}
void InstructionCodeGeneratorMIPS64::HandleCondition(HCondition* instruction) {
- if (!instruction->NeedsMaterialization()) {
+ if (instruction->IsEmittedAtUseSite()) {
return;
}
@@ -2748,6 +2748,32 @@
/* false_target */ nullptr);
}
+void LocationsBuilderMIPS64::VisitSelect(HSelect* select) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
+ if (Primitive::IsFloatingPointType(select->GetType())) {
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ } else {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
+ if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
+ locations->SetInAt(2, Location::RequiresRegister());
+ }
+ locations->SetOut(Location::SameAsFirstInput());
+}
+
+void InstructionCodeGeneratorMIPS64::VisitSelect(HSelect* select) {
+ LocationSummary* locations = select->GetLocations();
+ Mips64Label false_target;
+ GenerateTestAndBranch(select,
+ /* condition_input_index */ 2,
+ /* true_target */ nullptr,
+ &false_target);
+ codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
+ __ Bind(&false_target);
+}
+
void LocationsBuilderMIPS64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
new (GetGraph()->GetArena()) LocationSummary(info);
}
@@ -4267,5 +4293,14 @@
}
}
+void LocationsBuilderMIPS64::VisitClassTableGet(HClassTableGet*) {
+ UNIMPLEMENTED(FATAL) << "ClassTableGet is unimplemented on mips64";
+}
+
+void InstructionCodeGeneratorMIPS64::VisitClassTableGet(HClassTableGet*) {
+ UNIMPLEMENTED(FATAL) << "ClassTableGet is unimplemented on mips64";
+}
+
} // namespace mips64
} // namespace art
+
diff --git a/compiler/optimizing/code_generator_utils.cc b/compiler/optimizing/code_generator_utils.cc
index 644a3fb..96fe2a1 100644
--- a/compiler/optimizing/code_generator_utils.cc
+++ b/compiler/optimizing/code_generator_utils.cc
@@ -96,7 +96,7 @@
}
bool IsBooleanValueOrMaterializedCondition(HInstruction* cond_input) {
- return !cond_input->IsCondition() || cond_input->AsCondition()->NeedsMaterialization();
+ return !cond_input->IsCondition() || !cond_input->IsEmittedAtUseSite();
}
} // namespace art
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 50c4ba2..18d70da 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1218,11 +1218,14 @@
}
void CodeGeneratorX86::MoveLocation(Location dst, Location src, Primitive::Type dst_type) {
- if (Primitive::Is64BitType(dst_type)) {
- Move64(dst, src);
+ HParallelMove move(GetGraph()->GetArena());
+ if (dst_type == Primitive::kPrimLong && !src.IsConstant() && !src.IsFpuRegister()) {
+ move.AddMove(src.ToLow(), dst.ToLow(), Primitive::kPrimInt, nullptr);
+ move.AddMove(src.ToHigh(), dst.ToHigh(), Primitive::kPrimInt, nullptr);
} else {
- Move32(dst, src);
+ move.AddMove(src, dst, dst_type, nullptr);
}
+ GetMoveResolver()->EmitNativeCode(&move);
}
void CodeGeneratorX86::AddLocationAsTemp(Location location, LocationSummary* locations) {
@@ -1559,10 +1562,36 @@
void InstructionCodeGeneratorX86::VisitDeoptimize(HDeoptimize* deoptimize) {
SlowPathCode* slow_path = deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathX86>(deoptimize);
- GenerateTestAndBranch(deoptimize,
- /* condition_input_index */ 0,
- slow_path->GetEntryLabel(),
- /* false_target */ static_cast<Label*>(nullptr));
+ GenerateTestAndBranch<Label>(deoptimize,
+ /* condition_input_index */ 0,
+ slow_path->GetEntryLabel(),
+ /* false_target */ nullptr);
+}
+
+void LocationsBuilderX86::VisitSelect(HSelect* select) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
+ Primitive::Type select_type = select->GetType();
+ HInstruction* cond = select->GetCondition();
+
+ if (Primitive::IsFloatingPointType(select_type)) {
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ } else {
+ locations->SetInAt(0, Location::RequiresRegister());
+ }
+ locations->SetInAt(1, Location::Any());
+ if (IsBooleanValueOrMaterializedCondition(cond)) {
+ locations->SetInAt(2, Location::Any());
+ }
+ locations->SetOut(Location::SameAsFirstInput());
+}
+
+void InstructionCodeGeneratorX86::VisitSelect(HSelect* select) {
+ LocationSummary* locations = select->GetLocations();
+ NearLabel false_target;
+ GenerateTestAndBranch<NearLabel>(
+ select, /* condition_input_index */ 2, /* true_target */ nullptr, &false_target);
+ codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
+ __ Bind(&false_target);
}
void LocationsBuilderX86::VisitNativeDebugInfo(HNativeDebugInfo* info) {
@@ -1628,7 +1657,7 @@
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
- if (cond->NeedsMaterialization()) {
+ if (!cond->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister());
}
break;
@@ -1637,7 +1666,7 @@
case Primitive::kPrimDouble: {
locations->SetInAt(0, Location::RequiresFpuRegister());
locations->SetInAt(1, Location::RequiresFpuRegister());
- if (cond->NeedsMaterialization()) {
+ if (!cond->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister());
}
break;
@@ -1645,7 +1674,7 @@
default:
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::Any());
- if (cond->NeedsMaterialization()) {
+ if (!cond->IsEmittedAtUseSite()) {
// We need a byte register.
locations->SetOut(Location::RegisterLocation(ECX));
}
@@ -1654,7 +1683,7 @@
}
void InstructionCodeGeneratorX86::HandleCondition(HCondition* cond) {
- if (!cond->NeedsMaterialization()) {
+ if (cond->IsEmittedAtUseSite()) {
return;
}
@@ -2657,7 +2686,11 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::Any());
+ if (add->InputAt(1)->IsX86LoadFromConstantTable()) {
+ DCHECK(add->InputAt(1)->IsEmittedAtUseSite());
+ } else {
+ locations->SetInAt(1, Location::Any());
+ }
locations->SetOut(Location::SameAsFirstInput());
break;
}
@@ -2721,7 +2754,7 @@
__ addss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (add->InputAt(1)->IsX86LoadFromConstantTable()) {
HX86LoadFromConstantTable* const_area = add->InputAt(1)->AsX86LoadFromConstantTable();
- DCHECK(!const_area->NeedsMaterialization());
+ DCHECK(const_area->IsEmittedAtUseSite());
__ addss(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralFloatAddress(
const_area->GetConstant()->AsFloatConstant()->GetValue(),
@@ -2738,7 +2771,7 @@
__ addsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (add->InputAt(1)->IsX86LoadFromConstantTable()) {
HX86LoadFromConstantTable* const_area = add->InputAt(1)->AsX86LoadFromConstantTable();
- DCHECK(!const_area->NeedsMaterialization());
+ DCHECK(const_area->IsEmittedAtUseSite());
__ addsd(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralDoubleAddress(
const_area->GetConstant()->AsDoubleConstant()->GetValue(),
@@ -2769,7 +2802,11 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::Any());
+ if (sub->InputAt(1)->IsX86LoadFromConstantTable()) {
+ DCHECK(sub->InputAt(1)->IsEmittedAtUseSite());
+ } else {
+ locations->SetInAt(1, Location::Any());
+ }
locations->SetOut(Location::SameAsFirstInput());
break;
}
@@ -2819,7 +2856,7 @@
__ subss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (sub->InputAt(1)->IsX86LoadFromConstantTable()) {
HX86LoadFromConstantTable* const_area = sub->InputAt(1)->AsX86LoadFromConstantTable();
- DCHECK(!const_area->NeedsMaterialization());
+ DCHECK(const_area->IsEmittedAtUseSite());
__ subss(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralFloatAddress(
const_area->GetConstant()->AsFloatConstant()->GetValue(),
@@ -2836,7 +2873,7 @@
__ subsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (sub->InputAt(1)->IsX86LoadFromConstantTable()) {
HX86LoadFromConstantTable* const_area = sub->InputAt(1)->AsX86LoadFromConstantTable();
- DCHECK(!const_area->NeedsMaterialization());
+ DCHECK(const_area->IsEmittedAtUseSite());
__ subsd(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralDoubleAddress(
const_area->GetConstant()->AsDoubleConstant()->GetValue(),
@@ -2879,7 +2916,11 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::Any());
+ if (mul->InputAt(1)->IsX86LoadFromConstantTable()) {
+ DCHECK(mul->InputAt(1)->IsEmittedAtUseSite());
+ } else {
+ locations->SetInAt(1, Location::Any());
+ }
locations->SetOut(Location::SameAsFirstInput());
break;
}
@@ -3000,7 +3041,7 @@
__ mulss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (mul->InputAt(1)->IsX86LoadFromConstantTable()) {
HX86LoadFromConstantTable* const_area = mul->InputAt(1)->AsX86LoadFromConstantTable();
- DCHECK(!const_area->NeedsMaterialization());
+ DCHECK(const_area->IsEmittedAtUseSite());
__ mulss(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralFloatAddress(
const_area->GetConstant()->AsFloatConstant()->GetValue(),
@@ -3018,7 +3059,7 @@
__ mulsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (mul->InputAt(1)->IsX86LoadFromConstantTable()) {
HX86LoadFromConstantTable* const_area = mul->InputAt(1)->AsX86LoadFromConstantTable();
- DCHECK(!const_area->NeedsMaterialization());
+ DCHECK(const_area->IsEmittedAtUseSite());
__ mulsd(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralDoubleAddress(
const_area->GetConstant()->AsDoubleConstant()->GetValue(),
@@ -3372,7 +3413,11 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::Any());
+ if (div->InputAt(1)->IsX86LoadFromConstantTable()) {
+ DCHECK(div->InputAt(1)->IsEmittedAtUseSite());
+ } else {
+ locations->SetInAt(1, Location::Any());
+ }
locations->SetOut(Location::SameAsFirstInput());
break;
}
@@ -3399,7 +3444,7 @@
__ divss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (div->InputAt(1)->IsX86LoadFromConstantTable()) {
HX86LoadFromConstantTable* const_area = div->InputAt(1)->AsX86LoadFromConstantTable();
- DCHECK(!const_area->NeedsMaterialization());
+ DCHECK(const_area->IsEmittedAtUseSite());
__ divss(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralFloatAddress(
const_area->GetConstant()->AsFloatConstant()->GetValue(),
@@ -3416,7 +3461,7 @@
__ divsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (div->InputAt(1)->IsX86LoadFromConstantTable()) {
HX86LoadFromConstantTable* const_area = div->InputAt(1)->AsX86LoadFromConstantTable();
- DCHECK(!const_area->NeedsMaterialization());
+ DCHECK(const_area->IsEmittedAtUseSite());
__ divsd(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralDoubleAddress(
const_area->GetConstant()->AsDoubleConstant()->GetValue(),
@@ -3957,6 +4002,27 @@
void InstructionCodeGeneratorX86::VisitCurrentMethod(HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
}
+void LocationsBuilderX86::VisitClassTableGet(HClassTableGet* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorX86::VisitClassTableGet(HClassTableGet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ uint32_t method_offset = 0;
+ if (instruction->GetTableKind() == HClassTableGet::kVTable) {
+ method_offset = mirror::Class::EmbeddedVTableEntryOffset(
+ instruction->GetIndex(), kX86PointerSize).SizeValue();
+ } else {
+ method_offset = mirror::Class::EmbeddedImTableEntryOffset(
+ instruction->GetIndex() % mirror::Class::kImtSize, kX86PointerSize).Uint32Value();
+ }
+ __ movl(locations->Out().AsRegister<Register>(),
+ Address(locations->InAt(0).AsRegister<Register>(), method_offset));
+}
+
void LocationsBuilderX86::VisitNot(HNot* not_) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall);
@@ -5465,13 +5531,31 @@
if (source.IsRegister()) {
if (destination.IsRegister()) {
__ movl(destination.AsRegister<Register>(), source.AsRegister<Register>());
+ } else if (destination.IsFpuRegister()) {
+ __ movd(destination.AsFpuRegister<XmmRegister>(), source.AsRegister<Register>());
} else {
DCHECK(destination.IsStackSlot());
__ movl(Address(ESP, destination.GetStackIndex()), source.AsRegister<Register>());
}
+ } else if (source.IsRegisterPair()) {
+ size_t elem_size = Primitive::ComponentSize(Primitive::kPrimInt);
+ // Create stack space for 2 elements.
+ __ subl(ESP, Immediate(2 * elem_size));
+ __ movl(Address(ESP, 0), source.AsRegisterPairLow<Register>());
+ __ movl(Address(ESP, elem_size), source.AsRegisterPairHigh<Register>());
+ __ movsd(destination.AsFpuRegister<XmmRegister>(), Address(ESP, 0));
+ // And remove the temporary stack space we allocated.
+ __ addl(ESP, Immediate(2 * elem_size));
} else if (source.IsFpuRegister()) {
- if (destination.IsFpuRegister()) {
+ if (destination.IsRegister()) {
+ __ movd(destination.AsRegister<Register>(), source.AsFpuRegister<XmmRegister>());
+ } else if (destination.IsFpuRegister()) {
__ movaps(destination.AsFpuRegister<XmmRegister>(), source.AsFpuRegister<XmmRegister>());
+ } else if (destination.IsRegisterPair()) {
+ XmmRegister src_reg = source.AsFpuRegister<XmmRegister>();
+ __ movd(destination.AsRegisterPairLow<Register>(), src_reg);
+ __ psrlq(src_reg, Immediate(32));
+ __ movd(destination.AsRegisterPairHigh<Register>(), src_reg);
} else if (destination.IsStackSlot()) {
__ movss(Address(ESP, destination.GetStackIndex()), source.AsFpuRegister<XmmRegister>());
} else {
@@ -5488,7 +5572,11 @@
MoveMemoryToMemory32(destination.GetStackIndex(), source.GetStackIndex());
}
} else if (source.IsDoubleStackSlot()) {
- if (destination.IsFpuRegister()) {
+ if (destination.IsRegisterPair()) {
+ __ movl(destination.AsRegisterPairLow<Register>(), Address(ESP, source.GetStackIndex()));
+ __ movl(destination.AsRegisterPairHigh<Register>(),
+ Address(ESP, source.GetHighStackIndex(kX86WordSize)));
+ } else if (destination.IsFpuRegister()) {
__ movsd(destination.AsFpuRegister<XmmRegister>(), Address(ESP, source.GetStackIndex()));
} else {
DCHECK(destination.IsDoubleStackSlot()) << destination;
@@ -6865,7 +6953,7 @@
locations->SetInAt(1, Location::ConstantLocation(insn->GetConstant()));
// If we don't need to be materialized, we only need the inputs to be set.
- if (!insn->NeedsMaterialization()) {
+ if (insn->IsEmittedAtUseSite()) {
return;
}
@@ -6885,7 +6973,7 @@
}
void InstructionCodeGeneratorX86::VisitX86LoadFromConstantTable(HX86LoadFromConstantTable* insn) {
- if (!insn->NeedsMaterialization()) {
+ if (insn->IsEmittedAtUseSite()) {
return;
}
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index fd18834..86ffb0f 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1558,10 +1558,36 @@
void InstructionCodeGeneratorX86_64::VisitDeoptimize(HDeoptimize* deoptimize) {
SlowPathCode* slow_path = deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathX86_64>(deoptimize);
- GenerateTestAndBranch(deoptimize,
- /* condition_input_index */ 0,
- slow_path->GetEntryLabel(),
- /* false_target */ static_cast<Label*>(nullptr));
+ GenerateTestAndBranch<Label>(deoptimize,
+ /* condition_input_index */ 0,
+ slow_path->GetEntryLabel(),
+ /* false_target */ nullptr);
+}
+
+void LocationsBuilderX86_64::VisitSelect(HSelect* select) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
+ if (Primitive::IsFloatingPointType(select->GetType())) {
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ } else {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
+ if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
+ locations->SetInAt(2, Location::RequiresRegister());
+ }
+ locations->SetOut(Location::SameAsFirstInput());
+}
+
+void InstructionCodeGeneratorX86_64::VisitSelect(HSelect* select) {
+ LocationSummary* locations = select->GetLocations();
+ NearLabel false_target;
+ GenerateTestAndBranch<NearLabel>(select,
+ /* condition_input_index */ 2,
+ /* true_target */ nullptr,
+ &false_target);
+ codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
+ __ Bind(&false_target);
}
void LocationsBuilderX86_64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
@@ -1638,13 +1664,13 @@
locations->SetInAt(1, Location::Any());
break;
}
- if (cond->NeedsMaterialization()) {
+ if (!cond->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister());
}
}
void InstructionCodeGeneratorX86_64::HandleCondition(HCondition* cond) {
- if (!cond->NeedsMaterialization()) {
+ if (cond->IsEmittedAtUseSite()) {
return;
}
@@ -3959,6 +3985,27 @@
// Nothing to do, the method is already at its location.
}
+void LocationsBuilderX86_64::VisitClassTableGet(HClassTableGet* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorX86_64::VisitClassTableGet(HClassTableGet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ uint32_t method_offset = 0;
+ if (instruction->GetTableKind() == HClassTableGet::kVTable) {
+ method_offset = mirror::Class::EmbeddedVTableEntryOffset(
+ instruction->GetIndex(), kX86_64PointerSize).SizeValue();
+ } else {
+ method_offset = mirror::Class::EmbeddedImTableEntryOffset(
+ instruction->GetIndex() % mirror::Class::kImtSize, kX86_64PointerSize).Uint32Value();
+ }
+ __ movq(locations->Out().AsRegister<CpuRegister>(),
+ Address(locations->InAt(0).AsRegister<CpuRegister>(), method_offset));
+}
+
void LocationsBuilderX86_64::VisitNot(HNot* not_) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall);
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index 19d63de..322a577 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -671,10 +671,10 @@
then_block->AddInstruction(new (&allocator) HReturn(constant0));
else_block->AddInstruction(new (&allocator) HReturn(constant1));
- ASSERT_TRUE(equal->NeedsMaterialization());
+ ASSERT_FALSE(equal->IsEmittedAtUseSite());
TransformToSsa(graph);
PrepareForRegisterAllocation(graph).Run();
- ASSERT_FALSE(equal->NeedsMaterialization());
+ ASSERT_TRUE(equal->IsEmittedAtUseSite());
auto hook_before_codegen = [](HGraph* graph_in) {
HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors()[0];
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 3113677..962e77d 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -859,8 +859,12 @@
value));
}
} else if (input->GetType() == Primitive::kPrimInt
- && (input->IsPhi() || input->IsAnd() || input->IsOr() || input->IsXor())) {
- // TODO: We need a data-flow analysis to determine if the Phi or
+ && (input->IsPhi() ||
+ input->IsAnd() ||
+ input->IsOr() ||
+ input->IsXor() ||
+ input->IsSelect())) {
+ // TODO: We need a data-flow analysis to determine if the Phi or Select or
// binary operation is actually Boolean. Allow for now.
} else if (input->GetType() != Primitive::kPrimBoolean) {
AddError(StringPrintf(
@@ -893,6 +897,11 @@
HandleBooleanInput(instruction, 0);
}
+void SSAChecker::VisitSelect(HSelect* instruction) {
+ VisitInstruction(instruction);
+ HandleBooleanInput(instruction, 2);
+}
+
void SSAChecker::VisitBooleanNot(HBooleanNot* instruction) {
VisitInstruction(instruction);
HandleBooleanInput(instruction, 0);
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index 2e16bfe..8724cde 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -126,6 +126,7 @@
void VisitCondition(HCondition* op) OVERRIDE;
void VisitIf(HIf* instruction) OVERRIDE;
void VisitPackedSwitch(HPackedSwitch* instruction) OVERRIDE;
+ void VisitSelect(HSelect* instruction) OVERRIDE;
void VisitBooleanNot(HBooleanNot* instruction) OVERRIDE;
void VisitConstant(HConstant* instruction) OVERRIDE;
void VisitBoundType(HBoundType* instruction) OVERRIDE;
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 2805162..9d796c1 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -507,6 +507,7 @@
|| IsPass(HDeadCodeElimination::kFinalDeadCodeEliminationPassName)
|| IsPass(HDeadCodeElimination::kInitialDeadCodeEliminationPassName)
|| IsPass(BoundsCheckElimination::kBoundsCheckEliminationPassName)
+ || IsPass(RegisterAllocator::kRegisterAllocatorPassName)
|| IsPass(SsaBuilder::kSsaBuilderPassName)) {
HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
if (info == nullptr) {
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 2e79df1..51fef7c 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -21,6 +21,8 @@
#include "class_linker.h"
#include "constant_folding.h"
#include "dead_code_elimination.h"
+#include "dex/verified_method.h"
+#include "dex/verification_results.h"
#include "driver/compiler_driver-inl.h"
#include "driver/compiler_options.h"
#include "driver/dex_compilation_unit.h"
@@ -32,13 +34,12 @@
#include "optimizing_compiler.h"
#include "reference_type_propagation.h"
#include "register_allocator.h"
+#include "quick/inline_method_analyser.h"
#include "sharpening.h"
#include "ssa_builder.h"
#include "ssa_phi_elimination.h"
#include "scoped_thread_state_change.h"
#include "thread.h"
-#include "dex/verified_method.h"
-#include "dex/verification_results.h"
namespace art {
@@ -296,9 +297,29 @@
return false;
}
+HInstanceFieldGet* HInliner::BuildGetReceiverClass(ClassLinker* class_linker,
+ HInstruction* receiver,
+ uint32_t dex_pc) const {
+ ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0);
+ DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_");
+ return new (graph_->GetArena()) HInstanceFieldGet(
+ receiver,
+ Primitive::kPrimNot,
+ field->GetOffset(),
+ field->IsVolatile(),
+ field->GetDexFieldIndex(),
+ field->GetDeclaringClass()->GetDexClassDefIndex(),
+ *field->GetDexFile(),
+ handles_->NewHandle(field->GetDexCache()),
+ dex_pc);
+}
+
bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
const InlineCache& ic) {
+ DCHECK(invoke_instruction->IsInvokeVirtual() || invoke_instruction->IsInvokeInterface())
+ << invoke_instruction->DebugName();
+
const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
uint32_t class_index = FindClassIndexIn(ic.GetMonomorphicType(), caller_dex_file);
if (class_index == DexFile::kDexNoIndex) {
@@ -328,18 +349,8 @@
}
// We successfully inlined, now add a guard.
- ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0);
- DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_");
- HInstanceFieldGet* field_get = new (graph_->GetArena()) HInstanceFieldGet(
- receiver,
- Primitive::kPrimNot,
- field->GetOffset(),
- field->IsVolatile(),
- field->GetDexFieldIndex(),
- field->GetDeclaringClass()->GetDexClassDefIndex(),
- *field->GetDexFile(),
- handles_->NewHandle(field->GetDexCache()),
- invoke_instruction->GetDexPc());
+ HInstanceFieldGet* receiver_class = BuildGetReceiverClass(
+ class_linker, receiver, invoke_instruction->GetDexPc());
bool is_referrer =
(ic.GetMonomorphicType() == outermost_graph_->GetArtMethod()->GetDeclaringClass());
@@ -351,16 +362,16 @@
/* needs_access_check */ false,
/* is_in_dex_cache */ true);
- HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, field_get);
+ HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class);
HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
compare, invoke_instruction->GetDexPc());
// TODO: Extend reference type propagation to understand the guard.
if (cursor != nullptr) {
- bb_cursor->InsertInstructionAfter(field_get, cursor);
+ bb_cursor->InsertInstructionAfter(receiver_class, cursor);
} else {
- bb_cursor->InsertInstructionBefore(field_get, bb_cursor->GetFirstInstruction());
+ bb_cursor->InsertInstructionBefore(receiver_class, bb_cursor->GetFirstInstruction());
}
- bb_cursor->InsertInstructionAfter(load_class, field_get);
+ bb_cursor->InsertInstructionAfter(load_class, receiver_class);
bb_cursor->InsertInstructionAfter(compare, load_class);
bb_cursor->InsertInstructionAfter(deoptimize, compare);
deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
@@ -374,13 +385,101 @@
return true;
}
-bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction ATTRIBUTE_UNUSED,
+bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
- const InlineCache& ic ATTRIBUTE_UNUSED) {
- // TODO
- VLOG(compiler) << "Unimplemented polymorphic inlining for "
- << PrettyMethod(resolved_method);
- return false;
+ const InlineCache& ic) {
+ DCHECK(invoke_instruction->IsInvokeVirtual() || invoke_instruction->IsInvokeInterface())
+ << invoke_instruction->DebugName();
+ // This optimization only works under JIT for now.
+ DCHECK(Runtime::Current()->UseJit());
+ if (graph_->GetInstructionSet() == kMips || graph_->GetInstructionSet() == kMips64) {
+ // TODO: Support HClassTableGet for mips and mips64.
+ return false;
+ }
+ ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
+ size_t pointer_size = class_linker->GetImagePointerSize();
+
+ DCHECK(resolved_method != nullptr);
+ ArtMethod* actual_method = nullptr;
+ // Check whether we are actually calling the same method among
+ // the different types seen.
+ for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) {
+ if (ic.GetTypeAt(i) == nullptr) {
+ break;
+ }
+ ArtMethod* new_method = nullptr;
+ if (invoke_instruction->IsInvokeInterface()) {
+ new_method = ic.GetTypeAt(i)->FindVirtualMethodForInterface(
+ resolved_method, pointer_size);
+ } else {
+ DCHECK(invoke_instruction->IsInvokeVirtual());
+ new_method = ic.GetTypeAt(i)->FindVirtualMethodForVirtual(
+ resolved_method, pointer_size);
+ }
+ if (actual_method == nullptr) {
+ actual_method = new_method;
+ } else if (actual_method != new_method) {
+ // Different methods, bailout.
+ return false;
+ }
+ }
+
+ HInstruction* receiver = invoke_instruction->InputAt(0);
+ HInstruction* cursor = invoke_instruction->GetPrevious();
+ HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
+
+ if (!TryInline(invoke_instruction, actual_method, /* do_rtp */ false)) {
+ return false;
+ }
+
+ // We successfully inlined, now add a guard.
+ HInstanceFieldGet* receiver_class = BuildGetReceiverClass(
+ class_linker, receiver, invoke_instruction->GetDexPc());
+
+ size_t method_offset = invoke_instruction->IsInvokeVirtual()
+ ? actual_method->GetVtableIndex()
+ : invoke_instruction->AsInvokeInterface()->GetImtIndex();
+
+ Primitive::Type type = Is64BitInstructionSet(graph_->GetInstructionSet())
+ ? Primitive::kPrimLong
+ : Primitive::kPrimInt;
+ HClassTableGet* class_table_get = new (graph_->GetArena()) HClassTableGet(
+ receiver_class,
+ type,
+ invoke_instruction->IsInvokeVirtual() ? HClassTableGet::kVTable : HClassTableGet::kIMTable,
+ method_offset,
+ invoke_instruction->GetDexPc());
+
+ HConstant* constant;
+ if (type == Primitive::kPrimLong) {
+ constant = graph_->GetLongConstant(
+ reinterpret_cast<intptr_t>(actual_method), invoke_instruction->GetDexPc());
+ } else {
+ constant = graph_->GetIntConstant(
+ reinterpret_cast<intptr_t>(actual_method), invoke_instruction->GetDexPc());
+ }
+
+ HNotEqual* compare = new (graph_->GetArena()) HNotEqual(class_table_get, constant);
+ HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
+ compare, invoke_instruction->GetDexPc());
+ // TODO: Extend reference type propagation to understand the guard.
+ if (cursor != nullptr) {
+ bb_cursor->InsertInstructionAfter(receiver_class, cursor);
+ } else {
+ bb_cursor->InsertInstructionBefore(receiver_class, bb_cursor->GetFirstInstruction());
+ }
+ bb_cursor->InsertInstructionAfter(class_table_get, receiver_class);
+ bb_cursor->InsertInstructionAfter(compare, class_table_get);
+ bb_cursor->InsertInstructionAfter(deoptimize, compare);
+ deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
+
+ // Run type propagation to get the guard typed.
+ ReferenceTypePropagation rtp_fixup(graph_, handles_);
+ rtp_fixup.Run();
+
+ MaybeRecordStat(kInlinedPolymorphicCall);
+
+ return true;
}
bool HInliner::TryInline(HInvoke* invoke_instruction, ArtMethod* method, bool do_rtp) {
@@ -390,6 +489,11 @@
// dex file here (though the transitivity of an inline chain would allow checking the calller).
if (!compiler_driver_->MayInline(method->GetDexFile(),
outer_compilation_unit_.GetDexFile())) {
+ if (TryPatternSubstitution(invoke_instruction, method, do_rtp)) {
+ VLOG(compiler) << "Successfully replaced pattern of invoke " << PrettyMethod(method);
+ MaybeRecordStat(kReplacedInvokeWithSimplePattern);
+ return true;
+ }
VLOG(compiler) << "Won't inline " << PrettyMethod(method) << " in "
<< outer_compilation_unit_.GetDexFile()->GetLocation() << " ("
<< caller_compilation_unit_.GetDexFile()->GetLocation() << ") from "
@@ -461,6 +565,140 @@
return true;
}
+static HInstruction* GetInvokeInputForArgVRegIndex(HInvoke* invoke_instruction,
+ size_t arg_vreg_index)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ size_t input_index = 0;
+ for (size_t i = 0; i < arg_vreg_index; ++i, ++input_index) {
+ DCHECK_LT(input_index, invoke_instruction->GetNumberOfArguments());
+ if (Primitive::Is64BitType(invoke_instruction->InputAt(input_index)->GetType())) {
+ ++i;
+ DCHECK_NE(i, arg_vreg_index);
+ }
+ }
+ DCHECK_LT(input_index, invoke_instruction->GetNumberOfArguments());
+ return invoke_instruction->InputAt(input_index);
+}
+
+// Try to recognize known simple patterns and replace invoke call with appropriate instructions.
+bool HInliner::TryPatternSubstitution(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
+ bool do_rtp) {
+ InlineMethod inline_method;
+ if (!InlineMethodAnalyser::AnalyseMethodCode(resolved_method, &inline_method)) {
+ return false;
+ }
+
+ HInstruction* return_replacement = nullptr;
+ switch (inline_method.opcode) {
+ case kInlineOpNop:
+ DCHECK_EQ(invoke_instruction->GetType(), Primitive::kPrimVoid);
+ break;
+ case kInlineOpReturnArg:
+ return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction,
+ inline_method.d.return_data.arg);
+ break;
+ case kInlineOpNonWideConst:
+ if (resolved_method->GetShorty()[0] == 'L') {
+ DCHECK_EQ(inline_method.d.data, 0u);
+ return_replacement = graph_->GetNullConstant();
+ } else {
+ return_replacement = graph_->GetIntConstant(static_cast<int32_t>(inline_method.d.data));
+ }
+ break;
+ case kInlineOpIGet: {
+ const InlineIGetIPutData& data = inline_method.d.ifield_data;
+ if (data.method_is_static || data.object_arg != 0u) {
+ // TODO: Needs null check.
+ return false;
+ }
+ HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, data.object_arg);
+ HInstanceFieldGet* iget = CreateInstanceFieldGet(resolved_method, data.field_idx, obj);
+ DCHECK_EQ(iget->GetFieldOffset().Uint32Value(), data.field_offset);
+ DCHECK_EQ(iget->IsVolatile() ? 1u : 0u, data.is_volatile);
+ invoke_instruction->GetBlock()->InsertInstructionBefore(iget, invoke_instruction);
+ return_replacement = iget;
+ break;
+ }
+ case kInlineOpIPut: {
+ const InlineIGetIPutData& data = inline_method.d.ifield_data;
+ if (data.method_is_static || data.object_arg != 0u) {
+ // TODO: Needs null check.
+ return false;
+ }
+ HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, data.object_arg);
+ HInstruction* value = GetInvokeInputForArgVRegIndex(invoke_instruction, data.src_arg);
+ HInstanceFieldSet* iput = CreateInstanceFieldSet(resolved_method, data.field_idx, obj, value);
+ DCHECK_EQ(iput->GetFieldOffset().Uint32Value(), data.field_offset);
+ DCHECK_EQ(iput->IsVolatile() ? 1u : 0u, data.is_volatile);
+ invoke_instruction->GetBlock()->InsertInstructionBefore(iput, invoke_instruction);
+ if (data.return_arg_plus1 != 0u) {
+ size_t return_arg = data.return_arg_plus1 - 1u;
+ return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction, return_arg);
+ }
+ break;
+ }
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ UNREACHABLE();
+ }
+
+ if (return_replacement != nullptr) {
+ invoke_instruction->ReplaceWith(return_replacement);
+ }
+ invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction);
+
+ FixUpReturnReferenceType(resolved_method, invoke_instruction, return_replacement, do_rtp);
+ return true;
+}
+
+HInstanceFieldGet* HInliner::CreateInstanceFieldGet(ArtMethod* resolved_method,
+ uint32_t field_index,
+ HInstruction* obj)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
+ size_t pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
+ ArtField* resolved_field = dex_cache->GetResolvedField(field_index, pointer_size);
+ DCHECK(resolved_field != nullptr);
+ HInstanceFieldGet* iget = new (graph_->GetArena()) HInstanceFieldGet(
+ obj,
+ resolved_field->GetTypeAsPrimitiveType(),
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
+ *resolved_method->GetDexFile(),
+ dex_cache,
+ kNoDexPc);
+ if (iget->GetType() == Primitive::kPrimNot) {
+ ReferenceTypePropagation rtp(graph_, handles_);
+ rtp.Visit(iget);
+ }
+ return iget;
+}
+
+HInstanceFieldSet* HInliner::CreateInstanceFieldSet(ArtMethod* resolved_method,
+ uint32_t field_index,
+ HInstruction* obj,
+ HInstruction* value)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
+ size_t pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
+ ArtField* resolved_field = dex_cache->GetResolvedField(field_index, pointer_size);
+ DCHECK(resolved_field != nullptr);
+ HInstanceFieldSet* iput = new (graph_->GetArena()) HInstanceFieldSet(
+ obj,
+ value,
+ resolved_field->GetTypeAsPrimitiveType(),
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
+ *resolved_method->GetDexFile(),
+ dex_cache,
+ kNoDexPc);
+ return iput;
+}
bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
HInvoke* invoke_instruction,
bool same_dex_file,
@@ -717,7 +955,14 @@
if (return_replacement != nullptr) {
DCHECK_EQ(graph_, return_replacement->GetBlock()->GetGraph());
}
+ FixUpReturnReferenceType(resolved_method, invoke_instruction, return_replacement, do_rtp);
+ return true;
+}
+void HInliner::FixUpReturnReferenceType(ArtMethod* resolved_method,
+ HInvoke* invoke_instruction,
+ HInstruction* return_replacement,
+ bool do_rtp) {
// Check the integrity of reference types and run another type propagation if needed.
if (return_replacement != nullptr) {
if (return_replacement->GetType() == Primitive::kPrimNot) {
@@ -751,8 +996,6 @@
}
}
}
-
- return true;
}
} // namespace art
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 8de510e..0127d55 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -65,6 +65,20 @@
bool TryInline(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp = true)
SHARED_REQUIRES(Locks::mutator_lock_);
+ // Try to recognize known simple patterns and replace invoke call with appropriate instructions.
+ bool TryPatternSubstitution(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Create a new HInstanceFieldGet.
+ HInstanceFieldGet* CreateInstanceFieldGet(ArtMethod* resolved_method,
+ uint32_t field_index,
+ HInstruction* obj);
+ // Create a new HInstanceFieldSet.
+ HInstanceFieldSet* CreateInstanceFieldSet(ArtMethod* resolved_method,
+ uint32_t field_index,
+ HInstruction* obj,
+ HInstruction* value);
+
// Try to inline the target of a monomorphic call. If successful, the code
// in the graph will look like:
// if (receiver.getClass() != ic.GetMonomorphicType()) deopt
@@ -85,6 +99,17 @@
bool same_dex_file,
bool do_rtp = true);
+ HInstanceFieldGet* BuildGetReceiverClass(ClassLinker* class_linker,
+ HInstruction* receiver,
+ uint32_t dex_pc) const
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ void FixUpReturnReferenceType(ArtMethod* resolved_method,
+ HInvoke* invoke_instruction,
+ HInstruction* return_replacement,
+ bool do_rtp)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
HGraph* const outermost_graph_;
const DexCompilationUnit& outer_compilation_unit_;
const DexCompilationUnit& caller_compilation_unit_;
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 49fc8c7..7d3a723 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -76,6 +76,8 @@
void VisitSub(HSub* instruction) OVERRIDE;
void VisitUShr(HUShr* instruction) OVERRIDE;
void VisitXor(HXor* instruction) OVERRIDE;
+ void VisitSelect(HSelect* select) OVERRIDE;
+ void VisitIf(HIf* instruction) OVERRIDE;
void VisitInstanceOf(HInstanceOf* instruction) OVERRIDE;
void VisitInvoke(HInvoke* invoke) OVERRIDE;
void VisitDeoptimize(HDeoptimize* deoptimize) OVERRIDE;
@@ -559,14 +561,86 @@
}
void InstructionSimplifierVisitor::VisitBooleanNot(HBooleanNot* bool_not) {
- HInstruction* parent = bool_not->InputAt(0);
- if (parent->IsBooleanNot()) {
- HInstruction* value = parent->InputAt(0);
- // Replace (!(!bool_value)) with bool_value
- bool_not->ReplaceWith(value);
+ HInstruction* input = bool_not->InputAt(0);
+ HInstruction* replace_with = nullptr;
+
+ if (input->IsIntConstant()) {
+ // Replace !(true/false) with false/true.
+ if (input->AsIntConstant()->IsOne()) {
+ replace_with = GetGraph()->GetIntConstant(0);
+ } else {
+ DCHECK(input->AsIntConstant()->IsZero());
+ replace_with = GetGraph()->GetIntConstant(1);
+ }
+ } else if (input->IsBooleanNot()) {
+ // Replace (!(!bool_value)) with bool_value.
+ replace_with = input->InputAt(0);
+ } else if (input->IsCondition() &&
+ // Don't change FP compares. The definition of compares involving
+ // NaNs forces the compares to be done as written by the user.
+ !Primitive::IsFloatingPointType(input->InputAt(0)->GetType())) {
+ // Replace condition with its opposite.
+ replace_with = GetGraph()->InsertOppositeCondition(input->AsCondition(), bool_not);
+ }
+
+ if (replace_with != nullptr) {
+ bool_not->ReplaceWith(replace_with);
bool_not->GetBlock()->RemoveInstruction(bool_not);
- // It is possible that `parent` is dead at this point but we leave
- // its removal to DCE for simplicity.
+ RecordSimplification();
+ }
+}
+
+void InstructionSimplifierVisitor::VisitSelect(HSelect* select) {
+ HInstruction* replace_with = nullptr;
+ HInstruction* condition = select->GetCondition();
+ HInstruction* true_value = select->GetTrueValue();
+ HInstruction* false_value = select->GetFalseValue();
+
+ if (condition->IsBooleanNot()) {
+ // Change ((!cond) ? x : y) to (cond ? y : x).
+ condition = condition->InputAt(0);
+ std::swap(true_value, false_value);
+ select->ReplaceInput(false_value, 0);
+ select->ReplaceInput(true_value, 1);
+ select->ReplaceInput(condition, 2);
+ RecordSimplification();
+ }
+
+ if (true_value == false_value) {
+ // Replace (cond ? x : x) with (x).
+ replace_with = true_value;
+ } else if (condition->IsIntConstant()) {
+ if (condition->AsIntConstant()->IsOne()) {
+ // Replace (true ? x : y) with (x).
+ replace_with = true_value;
+ } else {
+ // Replace (false ? x : y) with (y).
+ DCHECK(condition->AsIntConstant()->IsZero());
+ replace_with = false_value;
+ }
+ } else if (true_value->IsIntConstant() && false_value->IsIntConstant()) {
+ if (true_value->AsIntConstant()->IsOne() && false_value->AsIntConstant()->IsZero()) {
+ // Replace (cond ? true : false) with (cond).
+ replace_with = condition;
+ } else if (true_value->AsIntConstant()->IsZero() && false_value->AsIntConstant()->IsOne()) {
+ // Replace (cond ? false : true) with (!cond).
+ replace_with = GetGraph()->InsertOppositeCondition(condition, select);
+ }
+ }
+
+ if (replace_with != nullptr) {
+ select->ReplaceWith(replace_with);
+ select->GetBlock()->RemoveInstruction(select);
+ RecordSimplification();
+ }
+}
+
+void InstructionSimplifierVisitor::VisitIf(HIf* instruction) {
+ HInstruction* condition = instruction->InputAt(0);
+ if (condition->IsBooleanNot()) {
+ // Swap successors if input is negated.
+ instruction->ReplaceInput(condition->InputAt(0), 0);
+ instruction->GetBlock()->SwapSuccessors();
RecordSimplification();
}
}
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 97fe587..e8912b3 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -807,7 +807,8 @@
}
static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* arena,
- HInvoke* invoke) {
+ HInvoke* invoke,
+ Primitive::Type type) {
LocationSummary* locations = new (arena) LocationSummary(invoke,
LocationSummary::kNoCall,
kIntrinsified);
@@ -817,11 +818,15 @@
locations->SetInAt(3, Location::RequiresRegister());
locations->SetInAt(4, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ // If heap poisoning is enabled, we don't want the unpoisoning
+ // operations to potentially clobber the output.
+ Location::OutputOverlap overlaps = (kPoisonHeapReferences && type == Primitive::kPrimNot)
+ ? Location::kOutputOverlap
+ : Location::kNoOutputOverlap;
+ locations->SetOut(Location::RequiresRegister(), overlaps);
locations->AddTemp(Location::RequiresRegister()); // Pointer.
locations->AddTemp(Location::RequiresRegister()); // Temp 1.
- locations->AddTemp(Location::RequiresRegister()); // Temp 2.
}
static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM* codegen) {
@@ -856,7 +861,12 @@
if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
codegen->GetAssembler()->PoisonHeapReference(expected_lo);
- codegen->GetAssembler()->PoisonHeapReference(value_lo);
+ if (value_lo == expected_lo) {
+ // Do not poison `value_lo`, as it is the same register as
+ // `expected_lo`, which has just been poisoned.
+ } else {
+ codegen->GetAssembler()->PoisonHeapReference(value_lo);
+ }
}
// do {
@@ -892,13 +902,18 @@
__ mov(out, ShifterOperand(0), CC);
if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
- codegen->GetAssembler()->UnpoisonHeapReference(value_lo);
codegen->GetAssembler()->UnpoisonHeapReference(expected_lo);
+ if (value_lo == expected_lo) {
+ // Do not unpoison `value_lo`, as it is the same register as
+ // `expected_lo`, which has just been unpoisoned.
+ } else {
+ codegen->GetAssembler()->UnpoisonHeapReference(value_lo);
+ }
}
}
void IntrinsicLocationsBuilderARM::VisitUnsafeCASInt(HInvoke* invoke) {
- CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke);
+ CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke, Primitive::kPrimInt);
}
void IntrinsicLocationsBuilderARM::VisitUnsafeCASObject(HInvoke* invoke) {
// The UnsafeCASObject intrinsic is missing a read barrier, and
@@ -906,16 +921,12 @@
// Turn it off temporarily as a quick fix, until the read barrier is
// implemented (see TODO in GenCAS below).
//
- // Also, the UnsafeCASObject intrinsic does not always work when heap
- // poisoning is enabled (it breaks run-test 004-UnsafeTest); turn it
- // off temporarily as a quick fix (b/26204023).
- //
- // TODO(rpl): Fix these two issues and re-enable this intrinsic.
- if (kEmitCompilerReadBarrier || kPoisonHeapReferences) {
+ // TODO(rpl): Fix this issue and re-enable this intrinsic with read barriers.
+ if (kEmitCompilerReadBarrier) {
return;
}
- CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke);
+ CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke, Primitive::kPrimNot);
}
void IntrinsicCodeGeneratorARM::VisitUnsafeCASInt(HInvoke* invoke) {
GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index c888f01..d5ed585 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -986,7 +986,9 @@
codegen_);
}
-static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
+static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena,
+ HInvoke* invoke,
+ Primitive::Type type) {
LocationSummary* locations = new (arena) LocationSummary(invoke,
LocationSummary::kNoCall,
kIntrinsified);
@@ -996,7 +998,12 @@
locations->SetInAt(3, Location::RequiresRegister());
locations->SetInAt(4, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ // If heap poisoning is enabled, we don't want the unpoisoning
+ // operations to potentially clobber the output.
+ Location::OutputOverlap overlaps = (kPoisonHeapReferences && type == Primitive::kPrimNot)
+ ? Location::kOutputOverlap
+ : Location::kNoOutputOverlap;
+ locations->SetOut(Location::RequiresRegister(), overlaps);
}
static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM64* codegen) {
@@ -1027,7 +1034,12 @@
if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
codegen->GetAssembler()->PoisonHeapReference(expected);
- codegen->GetAssembler()->PoisonHeapReference(value);
+ if (value.Is(expected)) {
+ // Do not poison `value`, as it is the same register as
+ // `expected`, which has just been poisoned.
+ } else {
+ codegen->GetAssembler()->PoisonHeapReference(value);
+ }
}
// do {
@@ -1077,16 +1089,21 @@
__ Cset(out, eq);
if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
- codegen->GetAssembler()->UnpoisonHeapReference(value);
codegen->GetAssembler()->UnpoisonHeapReference(expected);
+ if (value.Is(expected)) {
+ // Do not unpoison `value`, as it is the same register as
+ // `expected`, which has just been unpoisoned.
+ } else {
+ codegen->GetAssembler()->UnpoisonHeapReference(value);
+ }
}
}
void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) {
- CreateIntIntIntIntIntToInt(arena_, invoke);
+ CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimInt);
}
void IntrinsicLocationsBuilderARM64::VisitUnsafeCASLong(HInvoke* invoke) {
- CreateIntIntIntIntIntToInt(arena_, invoke);
+ CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimLong);
}
void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) {
// The UnsafeCASObject intrinsic is missing a read barrier, and
@@ -1094,16 +1111,12 @@
// Turn it off temporarily as a quick fix, until the read barrier is
// implemented (see TODO in GenCAS below).
//
- // Also, the UnsafeCASObject intrinsic does not always work when heap
- // poisoning is enabled (it breaks run-test 004-UnsafeTest); turn it
- // off temporarily as a quick fix (b/26204023).
- //
- // TODO(rpl): Fix these two issues and re-enable this intrinsic.
- if (kEmitCompilerReadBarrier || kPoisonHeapReferences) {
+ // TODO(rpl): Fix this issue and re-enable this intrinsic with read barriers.
+ if (kEmitCompilerReadBarrier) {
return;
}
- CreateIntIntIntIntIntToInt(arena_, invoke);
+ CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimNot);
}
void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) {
diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc
index 926f939..991f8f7 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -278,9 +278,9 @@
// Test for the phi.
interval = liveness.GetInstructionFromSsaIndex(3)->GetLiveInterval();
range = interval->GetFirstRange();
- // Instruction is consumed by the if.
+ // Instruction is input of non-materialized Equal and hence live until If.
ASSERT_EQ(14u, range->GetStart());
- ASSERT_EQ(17u, range->GetEnd());
+ ASSERT_EQ(19u, range->GetEnd());
ASSERT_TRUE(range->GetNext() == nullptr);
}
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 92f758d..c057eca 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -722,6 +722,22 @@
RemoveInstruction(initial);
}
+void HBasicBlock::MoveInstructionBefore(HInstruction* insn, HInstruction* cursor) {
+ DCHECK(!cursor->IsPhi());
+ DCHECK(!insn->IsPhi());
+ DCHECK(!insn->IsControlFlow());
+ DCHECK(insn->CanBeMoved());
+ DCHECK(!insn->HasSideEffects());
+
+ HBasicBlock* from_block = insn->GetBlock();
+ HBasicBlock* to_block = cursor->GetBlock();
+ DCHECK(from_block != to_block);
+
+ from_block->RemoveInstruction(insn, /* ensure_safety */ false);
+ insn->SetBlock(to_block);
+ to_block->instructions_.InsertInstructionBefore(insn, cursor);
+}
+
static void Add(HInstructionList* instruction_list,
HBasicBlock* block,
HInstruction* instruction) {
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 41c2f17..d90c1fb 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1011,6 +1011,7 @@
// Replace instruction `initial` with `replacement` within this block.
void ReplaceAndRemoveInstructionWith(HInstruction* initial,
HInstruction* replacement);
+ void MoveInstructionBefore(HInstruction* insn, HInstruction* cursor);
void AddPhi(HPhi* phi);
void InsertPhiAfter(HPhi* instruction, HPhi* cursor);
// RemoveInstruction and RemovePhi delete a given instruction from the respective
@@ -1161,6 +1162,7 @@
M(BoundsCheck, Instruction) \
M(BoundType, Instruction) \
M(CheckCast, Instruction) \
+ M(ClassTableGet, Instruction) \
M(ClearException, Instruction) \
M(ClinitCheck, Instruction) \
M(Compare, BinaryOperation) \
@@ -1220,6 +1222,7 @@
M(UnresolvedInstanceFieldSet, Instruction) \
M(UnresolvedStaticFieldGet, Instruction) \
M(UnresolvedStaticFieldSet, Instruction) \
+ M(Select, Instruction) \
M(StoreLocal, Instruction) \
M(Sub, BinaryOperation) \
M(SuspendCheck, Instruction) \
@@ -1819,6 +1822,7 @@
dex_pc_(dex_pc),
id_(-1),
ssa_index_(-1),
+ emitted_at_use_site_(false),
environment_(nullptr),
locations_(nullptr),
live_interval_(nullptr),
@@ -2081,6 +2085,9 @@
// The caller must ensure that this is safe to do.
void RemoveEnvironmentUsers();
+ bool IsEmittedAtUseSite() const { return emitted_at_use_site_; }
+ void MarkEmittedAtUseSite() { emitted_at_use_site_ = true; }
+
protected:
virtual const HUserRecord<HInstruction*> InputRecordAt(size_t i) const = 0;
virtual void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) = 0;
@@ -2102,6 +2109,10 @@
// When doing liveness analysis, instructions that have uses get an SSA index.
int ssa_index_;
+ // If set, the machine code for this instruction is assumed to be generated by
+ // its users. Used by liveness analysis to compute use positions accordingly.
+ bool emitted_at_use_site_;
+
// List of instructions that have this instruction as input.
HUseList<HInstruction*> uses_;
@@ -2542,6 +2553,44 @@
DISALLOW_COPY_AND_ASSIGN(HCurrentMethod);
};
+// Fetches an ArtMethod from the virtual table or the interface method table
+// of a class.
+class HClassTableGet : public HExpression<1> {
+ public:
+ enum TableKind {
+ kVTable,
+ kIMTable,
+ };
+ HClassTableGet(HInstruction* cls,
+ Primitive::Type type,
+ TableKind kind,
+ size_t index,
+ uint32_t dex_pc)
+ : HExpression(type, SideEffects::None(), dex_pc),
+ index_(index),
+ table_kind_(kind) {
+ SetRawInputAt(0, cls);
+ }
+
+ bool CanBeMoved() const OVERRIDE { return true; }
+ bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
+ return other->AsClassTableGet()->GetIndex() == index_ &&
+ other->AsClassTableGet()->GetTableKind() == table_kind_;
+ }
+
+ TableKind GetTableKind() const { return table_kind_; }
+ size_t GetIndex() const { return index_; }
+
+ DECLARE_INSTRUCTION(ClassTableGet);
+
+ private:
+ // The index of the ArtMethod in the table.
+ const size_t index_;
+ const TableKind table_kind_;
+
+ DISALLOW_COPY_AND_ASSIGN(HClassTableGet);
+};
+
// PackedSwitch (jump table). A block ending with a PackedSwitch instruction will
// have one successor for each entry in the switch table, and the final successor
// will be the block containing the next Dex opcode.
@@ -2711,12 +2760,8 @@
public:
HCondition(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
: HBinaryOperation(Primitive::kPrimBoolean, first, second, SideEffects::None(), dex_pc),
- needs_materialization_(true),
bias_(ComparisonBias::kNoBias) {}
- bool NeedsMaterialization() const { return needs_materialization_; }
- void ClearNeedsMaterialization() { needs_materialization_ = false; }
-
// For code generation purposes, returns whether this instruction is just before
// `instruction`, and disregard moves in between.
bool IsBeforeWhenDisregardMoves(HInstruction* instruction) const;
@@ -2748,10 +2793,6 @@
}
private:
- // For register allocation purposes, returns whether this instruction needs to be
- // materialized (that is, not just be in the processor flags).
- bool needs_materialization_;
-
// Needed if we merge a HCompare into a HCondition.
ComparisonBias bias_;
@@ -5586,6 +5627,41 @@
DISALLOW_COPY_AND_ASSIGN(HMonitorOperation);
};
+class HSelect : public HExpression<3> {
+ public:
+ HSelect(HInstruction* condition,
+ HInstruction* true_value,
+ HInstruction* false_value,
+ uint32_t dex_pc)
+ : HExpression(HPhi::ToPhiType(true_value->GetType()), SideEffects::None(), dex_pc) {
+ DCHECK_EQ(HPhi::ToPhiType(true_value->GetType()), HPhi::ToPhiType(false_value->GetType()));
+
+ // First input must be `true_value` or `false_value` to allow codegens to
+ // use the SameAsFirstInput allocation policy. We make it `false_value`, so
+ // that architectures which implement HSelect as a conditional move also
+ // will not need to invert the condition.
+ SetRawInputAt(0, false_value);
+ SetRawInputAt(1, true_value);
+ SetRawInputAt(2, condition);
+ }
+
+ HInstruction* GetFalseValue() const { return InputAt(0); }
+ HInstruction* GetTrueValue() const { return InputAt(1); }
+ HInstruction* GetCondition() const { return InputAt(2); }
+
+ bool CanBeMoved() const OVERRIDE { return true; }
+ bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; }
+
+ bool CanBeNull() const OVERRIDE {
+ return GetTrueValue()->CanBeNull() || GetFalseValue()->CanBeNull();
+ }
+
+ DECLARE_INSTRUCTION(Select);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HSelect);
+};
+
class MoveOperands : public ArenaObject<kArenaAllocMoveOperands> {
public:
MoveOperands(Location source,
diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h
index 556217b..b1bf939 100644
--- a/compiler/optimizing/nodes_x86.h
+++ b/compiler/optimizing/nodes_x86.h
@@ -36,16 +36,12 @@
class HX86LoadFromConstantTable : public HExpression<2> {
public:
HX86LoadFromConstantTable(HX86ComputeBaseMethodAddress* method_base,
- HConstant* constant,
- bool needs_materialization = true)
- : HExpression(constant->GetType(), SideEffects::None(), kNoDexPc),
- needs_materialization_(needs_materialization) {
+ HConstant* constant)
+ : HExpression(constant->GetType(), SideEffects::None(), kNoDexPc) {
SetRawInputAt(0, method_base);
SetRawInputAt(1, constant);
}
- bool NeedsMaterialization() const { return needs_materialization_; }
-
HX86ComputeBaseMethodAddress* GetBaseMethodAddress() const {
return InputAt(0)->AsX86ComputeBaseMethodAddress();
}
@@ -57,8 +53,6 @@
DECLARE_INSTRUCTION(X86LoadFromConstantTable);
private:
- const bool needs_materialization_;
-
DISALLOW_COPY_AND_ASSIGN(HX86LoadFromConstantTable);
};
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 3fac914..bdc664b 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -38,7 +38,6 @@
#include "base/dumpable.h"
#include "base/macros.h"
#include "base/timing_logger.h"
-#include "boolean_simplifier.h"
#include "bounds_check_elimination.h"
#include "builder.h"
#include "code_generator.h"
@@ -73,6 +72,7 @@
#include "reference_type_propagation.h"
#include "register_allocator.h"
#include "oat_quick_method_header.h"
+#include "select_generator.h"
#include "sharpening.h"
#include "side_effects_analysis.h"
#include "ssa_builder.h"
@@ -512,7 +512,7 @@
graph, stats, HDeadCodeElimination::kFinalDeadCodeEliminationPassName);
HConstantFolding* fold1 = new (arena) HConstantFolding(graph);
InstructionSimplifier* simplify1 = new (arena) InstructionSimplifier(graph, stats);
- HBooleanSimplifier* boolean_simplify = new (arena) HBooleanSimplifier(graph);
+ HSelectGenerator* select_generator = new (arena) HSelectGenerator(graph);
HConstantFolding* fold2 = new (arena) HConstantFolding(graph, "constant_folding_after_inlining");
HConstantFolding* fold3 = new (arena) HConstantFolding(graph, "constant_folding_after_bce");
SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
@@ -540,9 +540,9 @@
MaybeRunInliner(graph, codegen, driver, stats, dex_compilation_unit, pass_observer, handles);
HOptimization* optimizations2[] = {
- // BooleanSimplifier depends on the InstructionSimplifier removing
+ // SelectGenerator depends on the InstructionSimplifier removing
// redundant suspend checks to recognize empty blocks.
- boolean_simplify,
+ select_generator,
fold2, // TODO: if we don't inline we can also skip fold2.
side_effects,
gvn,
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index f8035aa..52a7b10 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -29,6 +29,7 @@
kAttemptCompilation = 0,
kCompiled,
kInlinedInvoke,
+ kReplacedInvokeWithSimplePattern,
kInstructionSimplifications,
kInstructionSimplificationsArch,
kUnresolvedMethod,
@@ -51,6 +52,7 @@
kNotCompiledVerificationError,
kNotCompiledVerifyAtRuntime,
kInlinedMonomorphicCall,
+ kInlinedPolymorphicCall,
kMonomorphicCall,
kPolymorphicCall,
kMegamorphicCall,
@@ -96,6 +98,7 @@
case kAttemptCompilation : name = "AttemptCompilation"; break;
case kCompiled : name = "Compiled"; break;
case kInlinedInvoke : name = "InlinedInvoke"; break;
+ case kReplacedInvokeWithSimplePattern: name = "ReplacedInvokeWithSimplePattern"; break;
case kInstructionSimplifications: name = "InstructionSimplifications"; break;
case kInstructionSimplificationsArch: name = "InstructionSimplificationsArch"; break;
case kUnresolvedMethod : name = "UnresolvedMethod"; break;
@@ -118,6 +121,7 @@
case kNotCompiledVerificationError : name = "NotCompiledVerificationError"; break;
case kNotCompiledVerifyAtRuntime : name = "NotCompiledVerifyAtRuntime"; break;
case kInlinedMonomorphicCall: name = "InlinedMonomorphicCall"; break;
+ case kInlinedPolymorphicCall: name = "InlinedPolymorphicCall"; break;
case kMonomorphicCall: name = "MonomorphicCall"; break;
case kPolymorphicCall: name = "PolymorphicCall"; break;
case kMegamorphicCall: name = "kMegamorphicCall"; break;
diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc
index 1394dfa..a2180bc 100644
--- a/compiler/optimizing/pc_relative_fixups_x86.cc
+++ b/compiler/optimizing/pc_relative_fixups_x86.cc
@@ -115,7 +115,10 @@
void ReplaceInput(HInstruction* insn, HConstant* value, int input_index, bool materialize) {
InitializePCRelativeBasePointer();
HX86LoadFromConstantTable* load_constant =
- new (GetGraph()->GetArena()) HX86LoadFromConstantTable(base_, value, materialize);
+ new (GetGraph()->GetArena()) HX86LoadFromConstantTable(base_, value);
+ if (!materialize) {
+ load_constant->MarkEmittedAtUseSite();
+ }
insn->GetBlock()->InsertInstructionBefore(load_constant, insn);
insn->ReplaceInput(load_constant, input_index);
}
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 63ef600..324d84f 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -127,24 +127,37 @@
}
}
-void PrepareForRegisterAllocation::VisitCondition(HCondition* condition) {
- bool needs_materialization = false;
- if (!condition->GetUses().HasOnlyOneUse() || !condition->GetEnvUses().IsEmpty()) {
- needs_materialization = true;
- } else {
- HInstruction* user = condition->GetUses().GetFirst()->GetUser();
- if (!user->IsIf() && !user->IsDeoptimize()) {
- needs_materialization = true;
+bool PrepareForRegisterAllocation::CanEmitConditionAt(HCondition* condition,
+ HInstruction* user) const {
+ if (condition->GetNext() != user) {
+ return false;
+ }
+
+ if (user->IsIf() || user->IsDeoptimize()) {
+ return true;
+ }
+
+ if (user->IsSelect() && user->AsSelect()->GetCondition() == condition) {
+ if (GetGraph()->GetInstructionSet() == kX86) {
+ // Long values and long condition inputs result in 8 required core registers.
+ // We don't have that many on x86. Materialize the condition in such case.
+ return user->GetType() != Primitive::kPrimLong ||
+ condition->InputAt(1)->GetType() != Primitive::kPrimLong ||
+ condition->InputAt(1)->IsConstant();
} else {
- // TODO: if there is no intervening instructions with side-effect between this condition
- // and the If instruction, we should move the condition just before the If.
- if (condition->GetNext() != user) {
- needs_materialization = true;
- }
+ return true;
}
}
- if (!needs_materialization) {
- condition->ClearNeedsMaterialization();
+
+ return false;
+}
+
+void PrepareForRegisterAllocation::VisitCondition(HCondition* condition) {
+ if (condition->HasOnlyOneNonEnvironmentUse()) {
+ HInstruction* user = condition->GetUses().GetFirst()->GetUser();
+ if (CanEmitConditionAt(condition, user)) {
+ condition->MarkEmittedAtUseSite();
+ }
}
}
@@ -165,7 +178,8 @@
}
}
-bool PrepareForRegisterAllocation::CanMoveClinitCheck(HInstruction* input, HInstruction* user) {
+bool PrepareForRegisterAllocation::CanMoveClinitCheck(HInstruction* input,
+ HInstruction* user) const {
// Determine if input and user come from the same dex instruction, so that we can move
// the clinit check responsibility from one to the other, i.e. from HClinitCheck (user)
// to HLoadClass (input), or from HClinitCheck (input) to HInvokeStaticOrDirect (user).
diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h
index 9b24342..c8b8b0d 100644
--- a/compiler/optimizing/prepare_for_register_allocation.h
+++ b/compiler/optimizing/prepare_for_register_allocation.h
@@ -42,7 +42,8 @@
void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE;
void VisitNewInstance(HNewInstance* instruction) OVERRIDE;
- bool CanMoveClinitCheck(HInstruction* input, HInstruction* user);
+ bool CanMoveClinitCheck(HInstruction* input, HInstruction* user) const;
+ bool CanEmitConditionAt(HCondition* condition, HInstruction* user) const;
DISALLOW_COPY_AND_ASSIGN(PrepareForRegisterAllocation);
};
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 779f319..1224a48 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -94,7 +94,6 @@
: HOptimization(graph, name),
handle_cache_(handles),
worklist_(graph->GetArena()->Adapter(kArenaAllocReferenceTypePropagation)) {
- worklist_.reserve(kDefaultWorklistSize);
}
void ReferenceTypePropagation::ValidateTypes() {
@@ -125,7 +124,14 @@
}
}
+void ReferenceTypePropagation::Visit(HInstruction* instruction) {
+ RTPVisitor visitor(graph_, &handle_cache_, &worklist_);
+ instruction->Accept(&visitor);
+}
+
void ReferenceTypePropagation::Run() {
+ worklist_.reserve(kDefaultWorklistSize);
+
// To properly propagate type info we need to visit in the dominator-based order.
// Reverse post order guarantees a node's dominators are visited first.
// We take advantage of this order in `VisitBasicBlock`.
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 47ba027..a7f10a6 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -35,6 +35,9 @@
StackHandleScopeCollection* handles,
const char* name = kReferenceTypePropagationPassName);
+ // Visit a single instruction.
+ void Visit(HInstruction* instruction);
+
void Run() OVERRIDE;
static constexpr const char* kReferenceTypePropagationPassName = "reference_type_propagation";
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index d77639d..5cd30ad 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -179,7 +179,7 @@
}
if (block->IsCatchBlock() ||
- (block->GetLoopInformation() != nullptr && block->GetLoopInformation()->IsIrreducible())) {
+ (block->IsLoopHeader() && block->GetLoopInformation()->IsIrreducible())) {
// By blocking all registers at the top of each catch block or irreducible loop, we force
// intervals belonging to the live-in set of the catch/header block to be spilled.
// TODO(ngeoffray): Phis in this block could be allocated in register.
@@ -1749,8 +1749,10 @@
}
// Find the intervals that cover `from` and `to`.
- LiveInterval* destination = interval->GetSiblingAt(to->GetLifetimeStart());
- LiveInterval* source = interval->GetSiblingAt(from->GetLifetimeEnd() - 1);
+ size_t destination_position = to->GetLifetimeStart();
+ size_t source_position = from->GetLifetimeEnd() - 1;
+ LiveInterval* destination = interval->GetSiblingAt(destination_position);
+ LiveInterval* source = interval->GetSiblingAt(source_position);
if (destination == source) {
// Interval was not split.
@@ -1759,7 +1761,8 @@
LiveInterval* parent = interval->GetParent();
HInstruction* defined_by = parent->GetDefinedBy();
- if (destination == nullptr) {
+ if (codegen_->GetGraph()->HasIrreducibleLoops() &&
+ (destination == nullptr || !destination->CoversSlow(destination_position))) {
// Our live_in fixed point calculation has found that the instruction is live
// in the `to` block because it will eventually enter an irreducible loop. Our
// live interval computation however does not compute a fixed point, and
@@ -1775,18 +1778,41 @@
return;
}
+ Location location_source;
+ // `GetSiblingAt` returns the interval whose start and end cover `position`,
+ // but does not check whether the interval is inactive at that position.
+ // The only situation where the interval is inactive at that position is in the
+ // presence of irreducible loops for constants and ArtMethod.
+ if (codegen_->GetGraph()->HasIrreducibleLoops() &&
+ (source == nullptr || !source->CoversSlow(source_position))) {
+ DCHECK(IsMaterializableEntryBlockInstructionOfGraphWithIrreducibleLoop(defined_by));
+ if (defined_by->IsConstant()) {
+ location_source = defined_by->GetLocations()->Out();
+ } else {
+ DCHECK(defined_by->IsCurrentMethod());
+ location_source = parent->NeedsTwoSpillSlots()
+ ? Location::DoubleStackSlot(parent->GetSpillSlot())
+ : Location::StackSlot(parent->GetSpillSlot());
+ }
+ } else {
+ DCHECK(source != nullptr);
+ DCHECK(source->CoversSlow(source_position));
+ DCHECK(destination->CoversSlow(destination_position));
+ location_source = source->ToLocation();
+ }
+
// 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->GetNormalSuccessors().size() == 1) {
InsertParallelMoveAtExitOf(from,
defined_by,
- source->ToLocation(),
+ location_source,
destination->ToLocation());
} else {
DCHECK_EQ(to->GetPredecessors().size(), 1u);
InsertParallelMoveAtEntryOf(to,
defined_by,
- source->ToLocation(),
+ location_source,
destination->ToLocation());
}
}
@@ -1890,7 +1916,7 @@
for (HLinearOrderIterator it(*codegen_->GetGraph()); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
if (block->IsCatchBlock() ||
- (block->GetLoopInformation() != nullptr && block->GetLoopInformation()->IsIrreducible())) {
+ (block->IsLoopHeader() && block->GetLoopInformation()->IsIrreducible())) {
// Instructions live at the top of catch blocks or irreducible loop header
// were forced to spill.
if (kIsDebugBuild) {
diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc
new file mode 100644
index 0000000..105b30a
--- /dev/null
+++ b/compiler/optimizing/select_generator.cc
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "select_generator.h"
+
+namespace art {
+
+static constexpr size_t kMaxInstructionsInBranch = 1u;
+
+// Returns true if `block` has only one predecessor, ends with a Goto and
+// contains at most `kMaxInstructionsInBranch` other movable instruction with
+// no side-effects.
+static bool IsSimpleBlock(HBasicBlock* block) {
+ if (block->GetPredecessors().size() != 1u) {
+ return false;
+ }
+ DCHECK(block->GetPhis().IsEmpty());
+
+ size_t num_instructions = 0u;
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* instruction = it.Current();
+ if (instruction->IsControlFlow()) {
+ return instruction->IsGoto() && num_instructions <= kMaxInstructionsInBranch;
+ } else if (instruction->CanBeMoved() && !instruction->HasSideEffects()) {
+ num_instructions++;
+ } else {
+ return false;
+ }
+ }
+
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+}
+
+// Returns true if 'block1' and 'block2' are empty, merge into the same single
+// successor and the successor can only be reached from them.
+static bool BlocksMergeTogether(HBasicBlock* block1, HBasicBlock* block2) {
+ return block1->GetSingleSuccessor() == block2->GetSingleSuccessor();
+}
+
+// Returns nullptr if `block` has either no phis or there is more than one phi
+// with different inputs at `index1` and `index2`. Otherwise returns that phi.
+static HPhi* GetSingleChangedPhi(HBasicBlock* block, size_t index1, size_t index2) {
+ DCHECK_NE(index1, index2);
+
+ HPhi* select_phi = nullptr;
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ HPhi* phi = it.Current()->AsPhi();
+ if (phi->InputAt(index1) != phi->InputAt(index2)) {
+ if (select_phi == nullptr) {
+ // First phi with different inputs for the two indices found.
+ select_phi = phi;
+ } else {
+ // More than one phis has different inputs for the two indices.
+ return nullptr;
+ }
+ }
+ }
+ return select_phi;
+}
+
+void HSelectGenerator::Run() {
+ // Iterate in post order in the unlikely case that removing one occurrence of
+ // the selection pattern empties a branch block of another occurrence.
+ // Otherwise the order does not matter.
+ for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ if (!block->EndsWithIf()) continue;
+
+ // Find elements of the diamond pattern.
+ HIf* if_instruction = block->GetLastInstruction()->AsIf();
+ HBasicBlock* true_block = if_instruction->IfTrueSuccessor();
+ HBasicBlock* false_block = if_instruction->IfFalseSuccessor();
+ DCHECK_NE(true_block, false_block);
+ if (!IsSimpleBlock(true_block) ||
+ !IsSimpleBlock(false_block) ||
+ !BlocksMergeTogether(true_block, false_block)) {
+ continue;
+ }
+ HBasicBlock* merge_block = true_block->GetSingleSuccessor();
+
+ // If the branches are not empty, move instructions in front of the If.
+ // TODO(dbrazdil): This puts an instruction between If and its condition.
+ // Implement moving of conditions to first users if possible.
+ if (!true_block->IsSingleGoto()) {
+ true_block->MoveInstructionBefore(true_block->GetFirstInstruction(), if_instruction);
+ }
+ if (!false_block->IsSingleGoto()) {
+ false_block->MoveInstructionBefore(false_block->GetFirstInstruction(), if_instruction);
+ }
+ DCHECK(true_block->IsSingleGoto());
+ DCHECK(false_block->IsSingleGoto());
+
+ // Find the resulting true/false values.
+ size_t predecessor_index_true = merge_block->GetPredecessorIndexOf(true_block);
+ size_t predecessor_index_false = merge_block->GetPredecessorIndexOf(false_block);
+ DCHECK_NE(predecessor_index_true, predecessor_index_false);
+
+ HPhi* phi = GetSingleChangedPhi(merge_block, predecessor_index_true, predecessor_index_false);
+ if (phi == nullptr) {
+ continue;
+ }
+ HInstruction* true_value = phi->InputAt(predecessor_index_true);
+ HInstruction* false_value = phi->InputAt(predecessor_index_false);
+
+ // Create the Select instruction and insert it in front of the If.
+ HSelect* select = new (graph_->GetArena()) HSelect(if_instruction->InputAt(0),
+ true_value,
+ false_value,
+ if_instruction->GetDexPc());
+ if (phi->GetType() == Primitive::kPrimNot) {
+ select->SetReferenceTypeInfo(phi->GetReferenceTypeInfo());
+ }
+ block->InsertInstructionBefore(select, if_instruction);
+
+ // Remove the true branch which removes the corresponding Phi input.
+ // If left only with the false branch, the Phi is automatically removed.
+ phi->ReplaceInput(select, predecessor_index_false);
+ bool only_two_predecessors = (merge_block->GetPredecessors().size() == 2u);
+ true_block->DisconnectAndDelete();
+ DCHECK_EQ(only_two_predecessors, phi->GetBlock() == nullptr);
+
+ // Merge remaining blocks which are now connected with Goto.
+ DCHECK_EQ(block->GetSingleSuccessor(), false_block);
+ block->MergeWith(false_block);
+ if (only_two_predecessors) {
+ DCHECK_EQ(block->GetSingleSuccessor(), merge_block);
+ block->MergeWith(merge_block);
+ }
+
+ // No need to update dominance information, as we are simplifying
+ // a simple diamond shape, where the join block is merged with the
+ // entry block. Any following blocks would have had the join block
+ // as a dominator, and `MergeWith` handles changing that to the
+ // entry block.
+ }
+}
+
+} // namespace art
diff --git a/compiler/optimizing/select_generator.h b/compiler/optimizing/select_generator.h
new file mode 100644
index 0000000..f9d6d4d
--- /dev/null
+++ b/compiler/optimizing/select_generator.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This optimization recognizes the common diamond selection pattern and
+ * replaces it with an instance of the HSelect instruction.
+ *
+ * Recognized pattern:
+ *
+ * If [ Condition ]
+ * / \
+ * false branch true branch
+ * \ /
+ * Phi [FalseValue, TrueValue]
+ *
+ * The pattern will be simplified if `true_branch` and `false_branch` each
+ * contain at most one instruction without any side effects.
+ *
+ * Blocks are merged into one and Select replaces the If and the Phi:
+ * true branch
+ * false branch
+ * Select [FalseValue, TrueValue, Condition]
+ *
+ * Note: In order to recognize no side-effect blocks, this optimization must be
+ * run after the instruction simplifier has removed redundant suspend checks.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_SELECT_GENERATOR_H_
+#define ART_COMPILER_OPTIMIZING_SELECT_GENERATOR_H_
+
+#include "optimization.h"
+
+namespace art {
+
+class HSelectGenerator : public HOptimization {
+ public:
+ explicit HSelectGenerator(HGraph* graph)
+ : HOptimization(graph, kSelectGeneratorPassName) {}
+
+ void Run() OVERRIDE;
+
+ static constexpr const char* kSelectGeneratorPassName = "select_generator";
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HSelectGenerator);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_SELECT_GENERATOR_H_
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index a5609fc..7ed3c84 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -242,19 +242,53 @@
}
if (instruction != nullptr) {
instruction->GetLiveInterval()->AddUse(
- current, environment, i, should_be_live);
+ current, environment, i, /* actual_user */ nullptr, should_be_live);
}
}
}
- // All inputs of an instruction must be live.
- for (size_t i = 0, e = current->InputCount(); i < e; ++i) {
- HInstruction* input = current->InputAt(i);
- // Some instructions 'inline' their inputs, that is they do not need
- // to be materialized.
- if (input->HasSsaIndex() && current->GetLocations()->InAt(i).IsValid()) {
- live_in->SetBit(input->GetSsaIndex());
- input->GetLiveInterval()->AddUse(current, /* environment */ nullptr, i);
+ // Process inputs of instructions.
+ if (current->IsEmittedAtUseSite()) {
+ if (kIsDebugBuild) {
+ DCHECK(!current->GetLocations()->Out().IsValid());
+ for (HUseIterator<HInstruction*> use_it(current->GetUses());
+ !use_it.Done();
+ use_it.Advance()) {
+ HInstruction* user = use_it.Current()->GetUser();
+ size_t index = use_it.Current()->GetIndex();
+ DCHECK(!user->GetLocations()->InAt(index).IsValid());
+ }
+ DCHECK(!current->HasEnvironmentUses());
+ }
+ } else {
+ for (size_t i = 0, e = current->InputCount(); i < e; ++i) {
+ HInstruction* input = current->InputAt(i);
+ bool has_in_location = current->GetLocations()->InAt(i).IsValid();
+ bool has_out_location = input->GetLocations()->Out().IsValid();
+
+ if (has_in_location) {
+ DCHECK(has_out_location);
+ DCHECK(input->HasSsaIndex());
+ // `Input` generates a result used by `current`. Add use and update
+ // the live-in set.
+ input->GetLiveInterval()->AddUse(current, /* environment */ nullptr, i);
+ live_in->SetBit(input->GetSsaIndex());
+ } else if (has_out_location) {
+ // `Input` generates a result but it is not used by `current`.
+ } else {
+ // `Input` is inlined into `current`. Walk over its inputs and record
+ // uses at `current`.
+ DCHECK(input->IsEmittedAtUseSite());
+ for (size_t i2 = 0, e2 = input->InputCount(); i2 < e2; ++i2) {
+ HInstruction* inlined_input = input->InputAt(i2);
+ DCHECK(inlined_input->HasSsaIndex()) << "Recursive inlining not allowed.";
+ if (input->GetLocations()->InAt(i2).IsValid()) {
+ live_in->SetBit(inlined_input->GetSsaIndex());
+ inlined_input->GetLiveInterval()->AddUse(
+ /* owner */ input, /* environment */ nullptr, i2, /* actual_user */ current);
+ }
+ }
+ }
}
}
}
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 572a7b6..a78aedc 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -113,10 +113,6 @@
input_index_(input_index),
position_(position),
next_(next) {
- DCHECK((user == nullptr)
- || user->IsPhi()
- || (GetPosition() == user->GetLifetimePosition() + 1)
- || (GetPosition() == user->GetLifetimePosition()));
DCHECK(environment == nullptr || user == nullptr);
DCHECK(next_ == nullptr || next->GetPosition() >= GetPosition());
}
@@ -243,21 +239,30 @@
AddRange(position, position + 1);
}
+ // Record use of an input. The use will be recorded as an environment use if
+ // `environment` is not null and as register use otherwise. If `actual_user`
+ // is specified, the use will be recorded at `actual_user`'s lifetime position.
void AddUse(HInstruction* instruction,
HEnvironment* environment,
size_t input_index,
+ HInstruction* actual_user = nullptr,
bool keep_alive = false) {
- // Set the use within the instruction.
bool is_environment = (environment != nullptr);
- size_t position = instruction->GetLifetimePosition() + 1;
LocationSummary* locations = instruction->GetLocations();
+ if (actual_user == nullptr) {
+ actual_user = instruction;
+ }
+
+ // Set the use within the instruction.
+ size_t position = actual_user->GetLifetimePosition() + 1;
if (!is_environment) {
if (locations->IsFixedInput(input_index) || locations->OutputUsesSameAs(input_index)) {
// For fixed inputs and output same as input, the register allocator
// requires to have inputs die at the instruction, so that input moves use the
// location of the input just before that instruction (and not potential moves due
// to splitting).
- position = instruction->GetLifetimePosition();
+ DCHECK_EQ(instruction, actual_user);
+ position = actual_user->GetLifetimePosition();
} else if (!locations->InAt(input_index).IsValid()) {
return;
}
@@ -267,11 +272,8 @@
AddBackEdgeUses(*instruction->GetBlock());
}
- DCHECK(position == instruction->GetLifetimePosition()
- || position == instruction->GetLifetimePosition() + 1);
-
if ((first_use_ != nullptr)
- && (first_use_->GetUser() == instruction)
+ && (first_use_->GetUser() == actual_user)
&& (first_use_->GetPosition() < position)) {
// The user uses the instruction multiple times, and one use dies before the other.
// We update the use list so that the latter is first.
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index bbed09c..f56fc38 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -380,6 +380,10 @@
UsageError(" --multi-image: specify that separate oat and image files be generated for each "
"input dex file.");
UsageError("");
+ UsageError(" --force-determinism: force the compiler to emit a deterministic output.");
+ UsageError(" This option is incompatible with read barriers (e.g., if dex2oat has been");
+ UsageError(" built with the environment variable `ART_USE_READ_BARRIER` set to `true`).");
+ UsageError("");
std::cerr << "See log for usage error information\n";
exit(EXIT_FAILURE);
}
@@ -924,8 +928,11 @@
// Fill some values into the key-value store for the oat header.
key_value_store_.reset(new SafeMap<std::string, std::string>());
- // Automatically force determinism for the boot image in a host build.
- if (!kIsTargetBuild && IsBootImage()) {
+ // Automatically force determinism for the boot image in a host
+ // build, except when read barriers are enabled, as the former
+ // switches the GC to a non-concurrent one by passing the
+ // option `-Xgc:nonconcurrent` (see below).
+ if (!kIsTargetBuild && IsBootImage() && !kEmitCompilerReadBarrier) {
force_determinism_ = true;
}
compiler_options_->force_determinism_ = force_determinism_;
@@ -1173,6 +1180,9 @@
} else if (option.starts_with("--no-inline-from=")) {
no_inline_from_string_ = option.substr(strlen("--no-inline-from=")).data();
} else if (option == "--force-determinism") {
+ if (kEmitCompilerReadBarrier) {
+ Usage("Cannot use --force-determinism with read barriers");
+ }
force_determinism_ = true;
} else if (!compiler_options_->ParseCompilerOption(option, Usage)) {
Usage("Unknown argument %s", option.data());
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index a1f6eee..af08fc4 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -153,6 +153,7 @@
strtab->Start();
strtab->Write(""); // strtab should start with empty string.
+ AddTrampolineSymbols();
Walk(&art::OatSymbolizer::AddSymbol);
strtab->End();
@@ -165,6 +166,35 @@
return builder_->Good();
}
+ void AddTrampolineSymbol(const char* name, uint32_t code_offset) {
+ if (code_offset != 0) {
+ uint32_t name_offset = builder_->GetStrTab()->Write(name);
+ uint64_t symbol_value = code_offset - oat_file_->GetOatHeader().GetExecutableOffset();
+ // Specifying 0 as the symbol size means that the symbol lasts until the next symbol or until
+ // the end of the section in case of the last symbol.
+ builder_->GetSymTab()->Add(name_offset, builder_->GetText(), symbol_value,
+ /* is_relative */ true, /* size */ 0, STB_GLOBAL, STT_FUNC);
+ }
+ }
+
+ void AddTrampolineSymbols() {
+ const OatHeader& oat_header = oat_file_->GetOatHeader();
+ AddTrampolineSymbol("interpreterToInterpreterBridge",
+ oat_header.GetInterpreterToInterpreterBridgeOffset());
+ AddTrampolineSymbol("interpreterToCompiledCodeBridge",
+ oat_header.GetInterpreterToCompiledCodeBridgeOffset());
+ AddTrampolineSymbol("jniDlsymLookup",
+ oat_header.GetJniDlsymLookupOffset());
+ AddTrampolineSymbol("quickGenericJniTrampoline",
+ oat_header.GetQuickGenericJniTrampolineOffset());
+ AddTrampolineSymbol("quickImtConflictTrampoline",
+ oat_header.GetQuickImtConflictTrampolineOffset());
+ AddTrampolineSymbol("quickResolutionTrampoline",
+ oat_header.GetQuickResolutionTrampolineOffset());
+ AddTrampolineSymbol("quickToInterpreterBridge",
+ oat_header.GetQuickToInterpreterBridgeOffset());
+ }
+
void Walk(Callback callback) {
std::vector<const OatFile::OatDexFile*> oat_dex_files = oat_file_->GetOatDexFiles();
for (size_t i = 0; i < oat_dex_files.size(); i++) {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index bb709e8..0e67f49 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1533,6 +1533,14 @@
}
}
if (!equal) {
+ VLOG(image) << "Image dex files " << image_dex_file_names.size();
+ for (mirror::String* name : image_dex_file_names) {
+ VLOG(image) << name->ToModifiedUtf8();
+ }
+ VLOG(image) << "Loader dex files " << loader_dex_file_names.size();
+ for (mirror::String* name : loader_dex_file_names) {
+ VLOG(image) << name->ToModifiedUtf8();
+ }
*error_msg = "Rejecting application image due to class loader mismatch";
return false;
}
@@ -5612,10 +5620,10 @@
k,
iface,
image_pointer_size_)) {
- LOG(WARNING) << "Conflicting default method implementations found: "
- << PrettyMethod(current_method) << " and "
- << PrettyMethod(*out_default_method) << " in class "
- << PrettyClass(klass.Get()) << " conflict.";
+ VLOG(class_linker) << "Conflicting default method implementations found: "
+ << PrettyMethod(current_method) << " and "
+ << PrettyMethod(*out_default_method) << " in class "
+ << PrettyClass(klass.Get()) << " conflict.";
*out_default_method = nullptr;
return DefaultMethodSearchResult::kDefaultConflict;
} else {
@@ -6245,15 +6253,7 @@
} // case kAbstractFound
}
if (LIKELY(fill_tables)) {
- if (current_method != nullptr) {
- // We found a default method implementation. Record it in the iftable and IMT.
- method_array->SetElementPtrSize(j, current_method, image_pointer_size_);
- SetIMTRef(unimplemented_method,
- imt_conflict_method,
- image_pointer_size_,
- current_method,
- /*out*/imt_ptr);
- } else if (!super_interface) {
+ if (current_method == nullptr && !super_interface) {
// We could not find an implementation for this method and since it is a brand new
// interface we searched the entire vtable (and all default methods) for an
// implementation but couldn't find one. We therefore need to make a miranda method.
@@ -6269,7 +6269,17 @@
new(miranda_method) ArtMethod(interface_method, image_pointer_size_);
miranda_methods.push_back(miranda_method);
}
- method_array->SetElementPtrSize(j, miranda_method, image_pointer_size_);
+ current_method = miranda_method;
+ }
+
+ if (current_method != nullptr) {
+ // We found a default method implementation. Record it in the iftable and IMT.
+ method_array->SetElementPtrSize(j, current_method, image_pointer_size_);
+ SetIMTRef(unimplemented_method,
+ imt_conflict_method,
+ image_pointer_size_,
+ current_method,
+ /*out*/imt_ptr);
}
}
} // For each method in interface end.
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 84483b4..d76a8d1 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -357,7 +357,11 @@
std::unique_ptr<MemMap> main_mem_map_2;
// Gross hack to make dex2oat deterministic.
- if (requested_alloc_space_begin == nullptr && Runtime::Current()->IsAotCompiler()) {
+ if (foreground_collector_type_ == kCollectorTypeMS &&
+ requested_alloc_space_begin == nullptr &&
+ Runtime::Current()->IsAotCompiler()) {
+ // Currently only enabled for MS collector since that is what the deterministic dex2oat uses.
+ // b/26849108
requested_alloc_space_begin = reinterpret_cast<uint8_t*>(kAllocSpaceBeginForDeterministicAoT);
}
uint8_t* request_begin = requested_alloc_space_begin;
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index 5e7f1a2..8356814 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -277,11 +277,7 @@
MutexLock mu2(self, *Locks::reference_queue_finalizer_references_lock_);
if (!reference->IsEnqueued()) {
CHECK(reference->IsFinalizerReferenceInstance());
- if (Runtime::Current()->IsActiveTransaction()) {
- reference->SetPendingNext<true>(reference);
- } else {
- reference->SetPendingNext<false>(reference);
- }
+ reference->SetPendingNext(reference);
return true;
}
return false;
diff --git a/runtime/gc/reference_queue.cc b/runtime/gc/reference_queue.cc
index 56957ba..67dcc2d 100644
--- a/runtime/gc/reference_queue.cc
+++ b/runtime/gc/reference_queue.cc
@@ -49,17 +49,9 @@
list_ = ref;
} else {
mirror::Reference* head = list_->GetPendingNext();
- if (Runtime::Current()->IsActiveTransaction()) {
- ref->SetPendingNext<true>(head);
- } else {
- ref->SetPendingNext<false>(head);
- }
+ ref->SetPendingNext(head);
}
- if (Runtime::Current()->IsActiveTransaction()) {
- list_->SetPendingNext<true>(ref);
- } else {
- list_->SetPendingNext<false>(ref);
- }
+ list_->SetPendingNext(ref);
}
mirror::Reference* ReferenceQueue::DequeuePendingReference() {
@@ -74,18 +66,10 @@
list_ = nullptr;
} else {
mirror::Reference* next = head->GetPendingNext();
- if (Runtime::Current()->IsActiveTransaction()) {
- list_->SetPendingNext<true>(next);
- } else {
- list_->SetPendingNext<false>(next);
- }
+ list_->SetPendingNext(next);
ref = head;
}
- if (Runtime::Current()->IsActiveTransaction()) {
- ref->SetPendingNext<true>(nullptr);
- } else {
- ref->SetPendingNext<false>(nullptr);
- }
+ ref->SetPendingNext(nullptr);
Heap* heap = Runtime::Current()->GetHeap();
if (kUseBakerOrBrooksReadBarrier && heap->CurrentCollectorType() == kCollectorTypeCC &&
heap->ConcurrentCopyingCollector()->IsActive()) {
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 891e280..f607923 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1041,7 +1041,7 @@
auto* dex_caches = image_header.GetImageRoot<kWithoutReadBarrier>(ImageHeader::kDexCaches)->
AsObjectArray<mirror::DexCache>();
for (int32_t i = 0, count = dex_caches->GetLength(); i < count; ++i) {
- mirror::DexCache* dex_cache = dex_caches->Get(i);
+ mirror::DexCache* dex_cache = dex_caches->Get<kVerifyNone, kWithoutReadBarrier>(i);
// Fix up dex cache pointers.
GcRoot<mirror::String>* strings = dex_cache->GetStrings();
if (strings != nullptr) {
@@ -1049,7 +1049,7 @@
if (strings != new_strings) {
dex_cache->SetFieldPtr64<false>(mirror::DexCache::StringsOffset(), new_strings);
}
- dex_cache->FixupStrings(new_strings, fixup_adapter);
+ dex_cache->FixupStrings<kWithoutReadBarrier>(new_strings, fixup_adapter);
}
GcRoot<mirror::Class>* types = dex_cache->GetResolvedTypes();
if (types != nullptr) {
@@ -1057,7 +1057,7 @@
if (types != new_types) {
dex_cache->SetFieldPtr64<false>(mirror::DexCache::ResolvedTypesOffset(), new_types);
}
- dex_cache->FixupResolvedTypes(new_types, fixup_adapter);
+ dex_cache->FixupResolvedTypes<kWithoutReadBarrier>(new_types, fixup_adapter);
}
ArtMethod** methods = dex_cache->GetResolvedMethods();
if (methods != nullptr) {
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index f540814..4e0146c 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -167,6 +167,7 @@
// Don't compile the method if we are supposed to be deoptimized.
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (instrumentation->AreAllMethodsDeoptimized() || instrumentation->IsDeoptimized(method)) {
+ VLOG(jit) << "JIT not compiling " << PrettyMethod(method) << " due to deoptimization";
return false;
}
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index bec6e13..c6e7fef 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -737,8 +737,16 @@
if (ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
return false;
}
- MutexLock mu(self, lock_);
+
+ // Compiling requires a profiling info object to notify compilation. Create
+ // one if it hasn't been done before.
ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*));
+ if (info == nullptr) {
+ ProfilingInfo::Create(self, method, /* retry_allocation */ true);
+ }
+
+ MutexLock mu(self, lock_);
+ info = method->GetProfilingInfo(sizeof(void*));
if (info == nullptr || info->IsMethodBeingCompiled()) {
return false;
}
diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h
index ddaf02f..ab72373 100644
--- a/runtime/jit/profiling_info.h
+++ b/runtime/jit/profiling_info.h
@@ -68,8 +68,13 @@
return !classes_[1].IsNull() && classes_[kIndividualCacheSize - 1].IsNull();
}
- private:
+ mirror::Class* GetTypeAt(size_t i) const SHARED_REQUIRES(Locks::mutator_lock_) {
+ return classes_[i].Read();
+ }
+
static constexpr uint16_t kIndividualCacheSize = 5;
+
+ private:
uint32_t dex_pc_;
GcRoot<mirror::Class> classes_[kIndividualCacheSize];
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index b6f424b..b3439f7 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -381,19 +381,26 @@
return (T)static_cast<uintptr_t>(AsIntArray()->GetWithoutChecks(idx));
}
-template<bool kTransactionActive, bool kUnchecked, typename T>
-inline void PointerArray::SetElementPtrSize(uint32_t idx, T element, size_t ptr_size) {
+template<bool kTransactionActive, bool kUnchecked>
+inline void PointerArray::SetElementPtrSize(uint32_t idx, uint64_t element, size_t ptr_size) {
if (ptr_size == 8) {
(kUnchecked ? down_cast<LongArray*>(static_cast<Object*>(this)) : AsLongArray())->
- SetWithoutChecks<kTransactionActive>(idx, (uint64_t)(element));
+ SetWithoutChecks<kTransactionActive>(idx, element);
} else {
DCHECK_EQ(ptr_size, 4u);
- DCHECK_LE((uintptr_t)element, 0xFFFFFFFFu);
+ DCHECK_LE(element, static_cast<uint64_t>(0xFFFFFFFFu));
(kUnchecked ? down_cast<IntArray*>(static_cast<Object*>(this)) : AsIntArray())
- ->SetWithoutChecks<kTransactionActive>(idx, static_cast<uint32_t>((uintptr_t)element));
+ ->SetWithoutChecks<kTransactionActive>(idx, static_cast<uint32_t>(element));
}
}
+template<bool kTransactionActive, bool kUnchecked, typename T>
+inline void PointerArray::SetElementPtrSize(uint32_t idx, T* element, size_t ptr_size) {
+ SetElementPtrSize<kTransactionActive, kUnchecked>(idx,
+ reinterpret_cast<uintptr_t>(element),
+ ptr_size);
+}
+
template <typename Visitor>
inline void PointerArray::Fixup(mirror::PointerArray* dest,
size_t pointer_size,
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 50d77eb..2bd6c5b 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -187,8 +187,11 @@
T GetElementPtrSize(uint32_t idx, size_t ptr_size)
SHARED_REQUIRES(Locks::mutator_lock_);
+ template<bool kTransactionActive = false, bool kUnchecked = false>
+ void SetElementPtrSize(uint32_t idx, uint64_t element, size_t ptr_size)
+ SHARED_REQUIRES(Locks::mutator_lock_);
template<bool kTransactionActive = false, bool kUnchecked = false, typename T>
- void SetElementPtrSize(uint32_t idx, T element, size_t ptr_size)
+ void SetElementPtrSize(uint32_t idx, T* element, size_t ptr_size)
SHARED_REQUIRES(Locks::mutator_lock_);
// Fixup the pointers in the dest arrays by passing our pointers through the visitor. Only copies
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index b49fc74..b97d994 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -457,6 +457,16 @@
return nullptr;
}
+ArtMethod* Class::FindDeclaredDirectMethodByName(const StringPiece& name, size_t pointer_size) {
+ for (auto& method : GetDirectMethods(pointer_size)) {
+ ArtMethod* const np_method = method.GetInterfaceMethodIfProxy(pointer_size);
+ if (name == np_method->GetName()) {
+ return &method;
+ }
+ }
+ return nullptr;
+}
+
// TODO These should maybe be changed to be named FindOwnedVirtualMethod or something similar
// because they do not only find 'declared' methods and will return copied methods. This behavior is
// desired and correct but the naming can lead to confusion because in the java language declared
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 195bbc9..ea614fd 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -922,6 +922,9 @@
ArtMethod* FindDeclaredVirtualMethodByName(const StringPiece& name, size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
+ ArtMethod* FindDeclaredDirectMethodByName(const StringPiece& name, size_t pointer_size)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
ArtMethod* FindVirtualMethod(const StringPiece& name, const StringPiece& signature,
size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 2ecc9fb..2da3d84 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -142,12 +142,11 @@
}
}
-template <typename Visitor>
+template <ReadBarrierOption kReadBarrierOption, typename Visitor>
inline void DexCache::FixupStrings(GcRoot<mirror::String>* dest, const Visitor& visitor) {
GcRoot<mirror::String>* src = GetStrings();
for (size_t i = 0, count = NumStrings(); i < count; ++i) {
- // TODO: Probably don't need read barrier for most callers.
- mirror::String* source = src[i].Read();
+ mirror::String* source = src[i].Read<kReadBarrierOption>();
mirror::String* new_source = visitor(source);
if (source != new_source) {
dest[i] = GcRoot<mirror::String>(new_source);
@@ -155,12 +154,11 @@
}
}
-template <typename Visitor>
+template <ReadBarrierOption kReadBarrierOption, typename Visitor>
inline void DexCache::FixupResolvedTypes(GcRoot<mirror::Class>* dest, const Visitor& visitor) {
GcRoot<mirror::Class>* src = GetResolvedTypes();
for (size_t i = 0, count = NumResolvedTypes(); i < count; ++i) {
- // TODO: Probably don't need read barrier for most callers.
- mirror::Class* source = src[i].Read();
+ mirror::Class* source = src[i].Read<kReadBarrierOption>();
mirror::Class* new_source = visitor(source);
if (source != new_source) {
dest[i] = GcRoot<mirror::Class>(new_source);
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 0002076..7912510 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -61,11 +61,11 @@
void Fixup(ArtMethod* trampoline, size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
- template <typename Visitor>
+ template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
void FixupStrings(GcRoot<mirror::String>* dest, const Visitor& visitor)
SHARED_REQUIRES(Locks::mutator_lock_);
- template <typename Visitor>
+ template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
void FixupResolvedTypes(GcRoot<mirror::Class>* dest, const Visitor& visitor)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/mirror/reference.h b/runtime/mirror/reference.h
index 51ae760..5e467ab 100644
--- a/runtime/mirror/reference.h
+++ b/runtime/mirror/reference.h
@@ -22,6 +22,7 @@
#include "object.h"
#include "object_callbacks.h"
#include "read_barrier_option.h"
+#include "runtime.h"
#include "thread.h"
namespace art {
@@ -80,9 +81,14 @@
Reference* GetPendingNext() SHARED_REQUIRES(Locks::mutator_lock_) {
return GetFieldObject<Reference>(PendingNextOffset());
}
- template<bool kTransactionActive>
- void SetPendingNext(Reference* pending_next) SHARED_REQUIRES(Locks::mutator_lock_) {
- SetFieldObject<kTransactionActive>(PendingNextOffset(), pending_next);
+
+ void SetPendingNext(Reference* pending_next)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ if (Runtime::Current()->IsActiveTransaction()) {
+ SetFieldObject<true>(PendingNextOffset(), pending_next);
+ } else {
+ SetFieldObject<false>(PendingNextOffset(), pending_next);
+ }
}
bool IsEnqueued() SHARED_REQUIRES(Locks::mutator_lock_) {
diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc
index 6554394..17306c9 100644
--- a/runtime/quick/inline_method_analyser.cc
+++ b/runtime/quick/inline_method_analyser.cc
@@ -71,14 +71,37 @@
// we need to be able to detect possibly inlined method, we pass a null inline method to indicate
// we don't want to take unresolved methods and fields into account during analysis.
bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier,
- InlineMethod* method) {
+ InlineMethod* result) {
DCHECK(verifier != nullptr);
if (!Runtime::Current()->UseJit()) {
- DCHECK_EQ(verifier->CanLoadClasses(), method != nullptr);
+ DCHECK_EQ(verifier->CanLoadClasses(), result != nullptr);
}
+
+ // Note: verifier->GetMethod() may be null.
+ return AnalyseMethodCode(verifier->CodeItem(),
+ verifier->GetMethodReference(),
+ (verifier->GetAccessFlags() & kAccStatic) != 0u,
+ verifier->GetMethod(),
+ result);
+}
+
+bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method, InlineMethod* result) {
+ const DexFile::CodeItem* code_item = method->GetCodeItem();
+ if (code_item == nullptr) {
+ // Native or abstract.
+ return false;
+ }
+ return AnalyseMethodCode(
+ code_item, method->ToMethodReference(), method->IsStatic(), method, result);
+}
+
+bool InlineMethodAnalyser::AnalyseMethodCode(const DexFile::CodeItem* code_item,
+ const MethodReference& method_ref,
+ bool is_static,
+ ArtMethod* method,
+ InlineMethod* result) {
// We currently support only plain return or 2-instruction methods.
- const DexFile::CodeItem* code_item = verifier->CodeItem();
DCHECK_NE(code_item->insns_size_in_code_units_, 0u);
const Instruction* instruction = Instruction::At(code_item->insns_);
Instruction::Code opcode = instruction->Opcode();
@@ -86,21 +109,21 @@
switch (opcode) {
case Instruction::RETURN_VOID:
if (method != nullptr) {
- method->opcode = kInlineOpNop;
- method->flags = kInlineSpecial;
- method->d.data = 0u;
+ result->opcode = kInlineOpNop;
+ result->flags = kInlineSpecial;
+ result->d.data = 0u;
}
return true;
case Instruction::RETURN:
case Instruction::RETURN_OBJECT:
case Instruction::RETURN_WIDE:
- return AnalyseReturnMethod(code_item, method);
+ return AnalyseReturnMethod(code_item, result);
case Instruction::CONST:
case Instruction::CONST_4:
case Instruction::CONST_16:
case Instruction::CONST_HIGH16:
// TODO: Support wide constants (RETURN_WIDE).
- return AnalyseConstMethod(code_item, method);
+ return AnalyseConstMethod(code_item, result);
case Instruction::IGET:
case Instruction::IGET_OBJECT:
case Instruction::IGET_BOOLEAN:
@@ -112,7 +135,7 @@
// case Instruction::IGET_QUICK:
// case Instruction::IGET_WIDE_QUICK:
// case Instruction::IGET_OBJECT_QUICK:
- return AnalyseIGetMethod(verifier, method);
+ return AnalyseIGetMethod(code_item, method_ref, is_static, method, result);
case Instruction::IPUT:
case Instruction::IPUT_OBJECT:
case Instruction::IPUT_BOOLEAN:
@@ -124,7 +147,7 @@
// case Instruction::IPUT_QUICK:
// case Instruction::IPUT_WIDE_QUICK:
// case Instruction::IPUT_OBJECT_QUICK:
- return AnalyseIPutMethod(verifier, method);
+ return AnalyseIPutMethod(code_item, method_ref, is_static, method, result);
default:
return false;
}
@@ -194,9 +217,11 @@
return true;
}
-bool InlineMethodAnalyser::AnalyseIGetMethod(verifier::MethodVerifier* verifier,
+bool InlineMethodAnalyser::AnalyseIGetMethod(const DexFile::CodeItem* code_item,
+ const MethodReference& method_ref,
+ bool is_static,
+ ArtMethod* method,
InlineMethod* result) {
- const DexFile::CodeItem* code_item = verifier->CodeItem();
const Instruction* instruction = Instruction::At(code_item->insns_);
Instruction::Code opcode = instruction->Opcode();
DCHECK(IsInstructionIGet(opcode));
@@ -227,10 +252,10 @@
return false; // Not returning the value retrieved by IGET?
}
- if ((verifier->GetAccessFlags() & kAccStatic) != 0u || object_arg != 0u) {
+ if (is_static || object_arg != 0u) {
// TODO: Implement inlining of IGET on non-"this" registers (needs correct stack trace for NPE).
// Allow synthetic accessors. We don't care about losing their stack frame in NPE.
- if (!IsSyntheticAccessor(verifier->GetMethodReference())) {
+ if (!IsSyntheticAccessor(method_ref)) {
return false;
}
}
@@ -243,13 +268,13 @@
if (result != nullptr) {
InlineIGetIPutData* data = &result->d.ifield_data;
- if (!ComputeSpecialAccessorInfo(field_idx, false, verifier, data)) {
+ if (!ComputeSpecialAccessorInfo(method, field_idx, false, data)) {
return false;
}
result->opcode = kInlineOpIGet;
result->flags = kInlineSpecial;
data->op_variant = IGetVariant(opcode);
- data->method_is_static = (verifier->GetAccessFlags() & kAccStatic) != 0u ? 1u : 0u;
+ data->method_is_static = is_static ? 1u : 0u;
data->object_arg = object_arg; // Allow IGET on any register, not just "this".
data->src_arg = 0u;
data->return_arg_plus1 = 0u;
@@ -257,9 +282,11 @@
return true;
}
-bool InlineMethodAnalyser::AnalyseIPutMethod(verifier::MethodVerifier* verifier,
+bool InlineMethodAnalyser::AnalyseIPutMethod(const DexFile::CodeItem* code_item,
+ const MethodReference& method_ref,
+ bool is_static,
+ ArtMethod* method,
InlineMethod* result) {
- const DexFile::CodeItem* code_item = verifier->CodeItem();
const Instruction* instruction = Instruction::At(code_item->insns_);
Instruction::Code opcode = instruction->Opcode();
DCHECK(IsInstructionIPut(opcode));
@@ -292,10 +319,10 @@
uint32_t object_arg = object_reg - arg_start;
uint32_t src_arg = src_reg - arg_start;
- if ((verifier->GetAccessFlags() & kAccStatic) != 0u || object_arg != 0u) {
+ if (is_static || object_arg != 0u) {
// TODO: Implement inlining of IPUT on non-"this" registers (needs correct stack trace for NPE).
// Allow synthetic accessors. We don't care about losing their stack frame in NPE.
- if (!IsSyntheticAccessor(verifier->GetMethodReference())) {
+ if (!IsSyntheticAccessor(method_ref)) {
return false;
}
}
@@ -310,13 +337,13 @@
if (result != nullptr) {
InlineIGetIPutData* data = &result->d.ifield_data;
- if (!ComputeSpecialAccessorInfo(field_idx, true, verifier, data)) {
+ if (!ComputeSpecialAccessorInfo(method, field_idx, true, data)) {
return false;
}
result->opcode = kInlineOpIPut;
result->flags = kInlineSpecial;
data->op_variant = IPutVariant(opcode);
- data->method_is_static = (verifier->GetAccessFlags() & kAccStatic) != 0u ? 1u : 0u;
+ data->method_is_static = is_static ? 1u : 0u;
data->object_arg = object_arg; // Allow IPUT on any register, not just "this".
data->src_arg = src_arg;
data->return_arg_plus1 = return_arg_plus1;
@@ -324,15 +351,17 @@
return true;
}
-bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(uint32_t field_idx, bool is_put,
- verifier::MethodVerifier* verifier,
+bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(ArtMethod* method,
+ uint32_t field_idx,
+ bool is_put,
InlineIGetIPutData* result) {
- mirror::DexCache* dex_cache = verifier->GetDexCache();
- uint32_t method_idx = verifier->GetMethodReference().dex_method_index;
- auto* cl = Runtime::Current()->GetClassLinker();
- ArtMethod* method = dex_cache->GetResolvedMethod(method_idx, cl->GetImagePointerSize());
- ArtField* field = cl->GetResolvedField(field_idx, dex_cache);
- if (method == nullptr || field == nullptr || field->IsStatic()) {
+ if (method == nullptr) {
+ return false;
+ }
+ mirror::DexCache* dex_cache = method->GetDexCache();
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ ArtField* field = dex_cache->GetResolvedField(field_idx, pointer_size);
+ if (field == nullptr || field->IsStatic()) {
return false;
}
mirror::Class* method_class = method->GetDeclaringClass();
diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h
index 1bb816b..046d225 100644
--- a/runtime/quick/inline_method_analyser.h
+++ b/runtime/quick/inline_method_analyser.h
@@ -188,7 +188,9 @@
* @param method placeholder for the inline method data.
* @return true if the method is a candidate for inlining, false otherwise.
*/
- static bool AnalyseMethodCode(verifier::MethodVerifier* verifier, InlineMethod* method)
+ static bool AnalyseMethodCode(verifier::MethodVerifier* verifier, InlineMethod* result)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+ static bool AnalyseMethodCode(ArtMethod* method, InlineMethod* result)
SHARED_REQUIRES(Locks::mutator_lock_);
static constexpr bool IsInstructionIGet(Instruction::Code opcode) {
@@ -211,17 +213,32 @@
static bool IsSyntheticAccessor(MethodReference ref);
private:
+ static bool AnalyseMethodCode(const DexFile::CodeItem* code_item,
+ const MethodReference& method_ref,
+ bool is_static,
+ ArtMethod* method,
+ InlineMethod* result)
+ SHARED_REQUIRES(Locks::mutator_lock_);
static bool AnalyseReturnMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
static bool AnalyseConstMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
- static bool AnalyseIGetMethod(verifier::MethodVerifier* verifier, InlineMethod* result)
+ static bool AnalyseIGetMethod(const DexFile::CodeItem* code_item,
+ const MethodReference& method_ref,
+ bool is_static,
+ ArtMethod* method,
+ InlineMethod* result)
SHARED_REQUIRES(Locks::mutator_lock_);
- static bool AnalyseIPutMethod(verifier::MethodVerifier* verifier, InlineMethod* result)
+ static bool AnalyseIPutMethod(const DexFile::CodeItem* code_item,
+ const MethodReference& method_ref,
+ bool is_static,
+ ArtMethod* method,
+ InlineMethod* result)
SHARED_REQUIRES(Locks::mutator_lock_);
// Can we fast path instance field access in a verified accessor?
// If yes, computes field's offset and volatility and whether the method is static or not.
- static bool ComputeSpecialAccessorInfo(uint32_t field_idx, bool is_put,
- verifier::MethodVerifier* verifier,
+ static bool ComputeSpecialAccessorInfo(ArtMethod* method,
+ uint32_t field_idx,
+ bool is_put,
InlineIGetIPutData* result)
SHARED_REQUIRES(Locks::mutator_lock_);
};
diff --git a/runtime/verifier/method_verifier-inl.h b/runtime/verifier/method_verifier-inl.h
index f52d011..def61db 100644
--- a/runtime/verifier/method_verifier-inl.h
+++ b/runtime/verifier/method_verifier-inl.h
@@ -50,6 +50,10 @@
return dex_cache_.Get();
}
+inline ArtMethod* MethodVerifier::GetMethod() const {
+ return mirror_method_;
+}
+
inline MethodReference MethodVerifier::GetMethodReference() const {
return MethodReference(dex_file_, dex_method_idx_);
}
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index a26e0fb..613d5af 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -270,6 +270,7 @@
ALWAYS_INLINE InstructionFlags& GetInstructionFlags(size_t index);
mirror::ClassLoader* GetClassLoader() SHARED_REQUIRES(Locks::mutator_lock_);
mirror::DexCache* GetDexCache() SHARED_REQUIRES(Locks::mutator_lock_);
+ ArtMethod* GetMethod() const SHARED_REQUIRES(Locks::mutator_lock_);
MethodReference GetMethodReference() const;
uint32_t GetAccessFlags() const;
bool HasCheckCasts() const;
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index 71ed4a2..30f613c 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -155,7 +155,7 @@
} else {
klass = class_linker->LookupClass(self, descriptor, ComputeModifiedUtf8Hash(descriptor),
loader);
- if (klass != nullptr && !klass->IsLoaded()) {
+ if (klass != nullptr && !klass->IsResolved()) {
// We found the class but without it being loaded its not safe for use.
klass = nullptr;
}
diff --git a/test/004-UnsafeTest/src/Main.java b/test/004-UnsafeTest/src/Main.java
index 5b22e88..a9a7a05 100644
--- a/test/004-UnsafeTest/src/Main.java
+++ b/test/004-UnsafeTest/src/Main.java
@@ -110,23 +110,35 @@
check(unsafe.getObject(t, objectOffset), objectValue, "Unsafe.getObject(Object, long)");
if (unsafe.compareAndSwapInt(t, intOffset, 0, 1)) {
- System.out.println("Unexpectedly succeeding compareAndSwap...");
+ System.out.println("Unexpectedly succeeding compareAndSwapInt(t, intOffset, 0, 1)");
}
if (!unsafe.compareAndSwapInt(t, intOffset, intValue, 0)) {
- System.out.println("Unexpectedly not succeeding compareAndSwap...");
+ System.out.println(
+ "Unexpectedly not succeeding compareAndSwapInt(t, intOffset, intValue, 0)");
}
if (!unsafe.compareAndSwapInt(t, intOffset, 0, 1)) {
- System.out.println("Unexpectedly not succeeding compareAndSwap...");
+ System.out.println("Unexpectedly not succeeding compareAndSwapInt(t, intOffset, 0, 1)");
+ }
+ // Exercise sun.misc.Unsafe.compareAndSwapInt using the same
+ // integer (1) for the `expectedValue` and `newValue` arguments.
+ if (!unsafe.compareAndSwapInt(t, intOffset, 1, 1)) {
+ System.out.println("Unexpectedly not succeeding compareAndSwapInt(t, intOffset, 1, 1)");
}
if (unsafe.compareAndSwapLong(t, longOffset, 0, 1)) {
- System.out.println("Unexpectedly succeeding compareAndSwapLong...");
+ System.out.println("Unexpectedly succeeding compareAndSwapLong(t, longOffset, 0, 1)");
}
if (!unsafe.compareAndSwapLong(t, longOffset, longValue, 0)) {
- System.out.println("Unexpectedly not succeeding compareAndSwapLong...");
+ System.out.println(
+ "Unexpectedly not succeeding compareAndSwapLong(t, longOffset, longValue, 0)");
}
if (!unsafe.compareAndSwapLong(t, longOffset, 0, 1)) {
- System.out.println("Unexpectedly not succeeding compareAndSwapLong...");
+ System.out.println("Unexpectedly not succeeding compareAndSwapLong(t, longOffset, 0, 1)");
+ }
+ // Exercise sun.misc.Unsafe.compareAndSwapLong using the same
+ // integer (1) for the `expectedValue` and `newValue` arguments.
+ if (!unsafe.compareAndSwapLong(t, longOffset, 1, 1)) {
+ System.out.println("Unexpectedly not succeeding compareAndSwapLong(t, longOffset, 1, 1)");
}
// We do not use `null` as argument to sun.misc.Unsafe.compareAndSwapObject
@@ -135,31 +147,41 @@
// references). This way, when heap poisoning is enabled, we can
// better exercise its implementation within that method.
if (unsafe.compareAndSwapObject(t, objectOffset, new Object(), new Object())) {
- System.out.println("Unexpectedly succeeding compareAndSwapObject...");
+ System.out.println("Unexpectedly succeeding " +
+ "compareAndSwapObject(t, objectOffset, new Object(), new Object())");
}
Object objectValue2 = new Object();
if (!unsafe.compareAndSwapObject(t, objectOffset, objectValue, objectValue2)) {
- System.out.println("Unexpectedly not succeeding compareAndSwapObject...");
+ System.out.println("Unexpectedly not succeeding " +
+ "compareAndSwapObject(t, objectOffset, objectValue, objectValue2)");
}
Object objectValue3 = new Object();
if (!unsafe.compareAndSwapObject(t, objectOffset, objectValue2, objectValue3)) {
- System.out.println("Unexpectedly not succeeding compareAndSwapObject...");
+ System.out.println("Unexpectedly not succeeding " +
+ "compareAndSwapObject(t, objectOffset, objectValue2, objectValue3)");
}
-
+ // Exercise sun.misc.Unsafe.compareAndSwapObject using the same
+ // object (`objectValue3`) for the `expectedValue` and `newValue` arguments.
+ if (!unsafe.compareAndSwapObject(t, objectOffset, objectValue3, objectValue3)) {
+ System.out.println("Unexpectedly not succeeding " +
+ "compareAndSwapObject(t, objectOffset, objectValue3, objectValue3)");
+ }
// Exercise sun.misc.Unsafe.compareAndSwapObject using the same
// object (`t`) for the `obj` and `newValue` arguments.
if (!unsafe.compareAndSwapObject(t, objectOffset, objectValue3, t)) {
- System.out.println("Unexpectedly not succeeding compareAndSwapObject...");
+ System.out.println(
+ "Unexpectedly not succeeding compareAndSwapObject(t, objectOffset, objectValue3, t)");
}
// Exercise sun.misc.Unsafe.compareAndSwapObject using the same
// object (`t`) for the `obj`, `expectedValue` and `newValue` arguments.
if (!unsafe.compareAndSwapObject(t, objectOffset, t, t)) {
- System.out.println("Unexpectedly not succeeding compareAndSwapObject...");
+ System.out.println("Unexpectedly not succeeding compareAndSwapObject(t, objectOffset, t, t)");
}
// Exercise sun.misc.Unsafe.compareAndSwapObject using the same
// object (`t`) for the `obj` and `expectedValue` arguments.
if (!unsafe.compareAndSwapObject(t, objectOffset, t, new Object())) {
- System.out.println("Unexpectedly not succeeding compareAndSwapObject...");
+ System.out.println(
+ "Unexpectedly not succeeding compareAndSwapObject(t, objectOffset, t, new Object())");
}
}
diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java
index 0e07f47..5479818 100644
--- a/test/442-checker-constant-folding/src/Main.java
+++ b/test/442-checker-constant-folding/src/Main.java
@@ -666,11 +666,11 @@
/// CHECK-START: int Main.StaticConditionNulls() constant_folding_after_inlining (before)
/// CHECK-DAG: <<Null:l\d+>> NullConstant
/// CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Null>>,<<Null>>]
- /// CHECK-DAG: If [<<Cond>>]
+ /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<Cond>>]
/// CHECK-START: int Main.StaticConditionNulls() constant_folding_after_inlining (after)
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
- /// CHECK-DAG: If [<<Const0>>]
+ /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<Const0>>]
/// CHECK-START: int Main.StaticConditionNulls() constant_folding_after_inlining (after)
/// CHECK-NOT: NotEqual
diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java
index 92cf807..d48b30e 100644
--- a/test/450-checker-types/src/Main.java
+++ b/test/450-checker-types/src/Main.java
@@ -275,23 +275,6 @@
}
}
- /// CHECK-START: void Main.testNotInstanceOf_Inlined(java.lang.Object) inliner (after)
- /// CHECK-DAG: <<IOf:z\d+>> InstanceOf
- /// CHECK-DAG: <<Not:z\d+>> BooleanNot [<<IOf>>]
- /// CHECK-DAG: If [<<Not>>]
-
- /// CHECK-START: void Main.testNotInstanceOf_Inlined(java.lang.Object) instruction_simplifier_after_bce (before)
- /// CHECK: CheckCast
- /// CHECK-NOT: CheckCast
-
- /// CHECK-START: void Main.testNotInstanceOf_Inlined(java.lang.Object) instruction_simplifier_after_bce (after)
- /// CHECK-NOT: CheckCast
- public void testNotInstanceOf_Inlined(Object o) {
- if ($inline$InstanceofSubclassC(o)) {
- ((SubclassC)o).$noinline$g();
- }
- }
-
/// CHECK-START: void Main.testInstanceOfKeep(java.lang.Object) instruction_simplifier (before)
/// CHECK: CheckCast
/// CHECK: CheckCast
diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java
index 0fd7801..3c8abeb 100644
--- a/test/458-checker-instruction-simplification/src/Main.java
+++ b/test/458-checker-instruction-simplification/src/Main.java
@@ -969,6 +969,13 @@
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
/// CHECK-DAG: If [<<Arg>>]
+ /// CHECK-START: int Main.EqualTrueRhs(boolean) instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const3>>,<<Const5>>,<<Arg>>]
+ /// CHECK-DAG: Return [<<Select>>]
+
public static int EqualTrueRhs(boolean arg) {
return (arg != true) ? 3 : 5;
}
@@ -983,6 +990,13 @@
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
/// CHECK-DAG: If [<<Arg>>]
+ /// CHECK-START: int Main.EqualTrueLhs(boolean) instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const3>>,<<Const5>>,<<Arg>>]
+ /// CHECK-DAG: Return [<<Select>>]
+
public static int EqualTrueLhs(boolean arg) {
return (true != arg) ? 3 : 5;
}
@@ -995,8 +1009,14 @@
/// CHECK-START: int Main.EqualFalseRhs(boolean) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
- /// CHECK-DAG: If [<<NotArg>>]
+ /// CHECK-DAG: If [<<Arg>>]
+
+ /// CHECK-START: int Main.EqualFalseRhs(boolean) instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const5>>,<<Const3>>,<<Arg>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static int EqualFalseRhs(boolean arg) {
return (arg != false) ? 3 : 5;
@@ -1010,8 +1030,14 @@
/// CHECK-START: int Main.EqualFalseLhs(boolean) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
- /// CHECK-DAG: If [<<NotArg>>]
+ /// CHECK-DAG: If [<<Arg>>]
+
+ /// CHECK-START: int Main.EqualFalseLhs(boolean) instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const5>>,<<Const3>>,<<Arg>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static int EqualFalseLhs(boolean arg) {
return (false != arg) ? 3 : 5;
@@ -1025,8 +1051,14 @@
/// CHECK-START: int Main.NotEqualTrueRhs(boolean) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
- /// CHECK-DAG: If [<<NotArg>>]
+ /// CHECK-DAG: If [<<Arg>>]
+
+ /// CHECK-START: int Main.NotEqualTrueRhs(boolean) instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const5>>,<<Const3>>,<<Arg>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static int NotEqualTrueRhs(boolean arg) {
return (arg == true) ? 3 : 5;
@@ -1040,8 +1072,14 @@
/// CHECK-START: int Main.NotEqualTrueLhs(boolean) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
- /// CHECK-DAG: If [<<NotArg>>]
+ /// CHECK-DAG: If [<<Arg>>]
+
+ /// CHECK-START: int Main.NotEqualTrueLhs(boolean) instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const5>>,<<Const3>>,<<Arg>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static int NotEqualTrueLhs(boolean arg) {
return (true == arg) ? 3 : 5;
@@ -1057,6 +1095,13 @@
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
/// CHECK-DAG: If [<<Arg>>]
+ /// CHECK-START: int Main.NotEqualFalseRhs(boolean) instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const3>>,<<Const5>>,<<Arg>>]
+ /// CHECK-DAG: Return [<<Select>>]
+
public static int NotEqualFalseRhs(boolean arg) {
return (arg == false) ? 3 : 5;
}
@@ -1071,38 +1116,51 @@
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
/// CHECK-DAG: If [<<Arg>>]
+ /// CHECK-START: int Main.NotEqualFalseLhs(boolean) instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const3>>,<<Const5>>,<<Arg>>]
+ /// CHECK-DAG: Return [<<Select>>]
+
public static int NotEqualFalseLhs(boolean arg) {
return (false == arg) ? 3 : 5;
}
/// CHECK-START: boolean Main.EqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (before)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
- /// CHECK-DAG: <<BoolNot:z\d+>> BooleanNot [<<Arg>>]
- /// CHECK-DAG: <<Cond:z\d+>> Equal [<<BoolNot>>,<<Const2>>]
- /// CHECK-DAG: Return [<<Cond>>]
+ /// CHECK-DAG: <<NotArg:i\d+>> Select [<<Const1>>,<<Const0>>,<<Arg>>]
+ /// CHECK-DAG: <<Cond:z\d+>> Equal [<<NotArg>>,<<Const2>>]
+ /// CHECK-DAG: <<NotCond:i\d+>> Select [<<Const1>>,<<Const0>>,<<Cond>>]
+ /// CHECK-DAG: Return [<<NotCond>>]
/// CHECK-START: boolean Main.EqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (after)
- /// CHECK-DAG: <<False:i\d+>> IntConstant 0
- /// CHECK-DAG: Return [<<False>>]
+ /// CHECK-DAG: <<True:i\d+>> IntConstant 1
+ /// CHECK-DAG: Return [<<True>>]
public static boolean EqualBoolVsIntConst(boolean arg) {
- return (arg ? 0 : 1) == 2;
+ return (arg ? 0 : 1) != 2;
}
/// CHECK-START: boolean Main.NotEqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (before)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
- /// CHECK-DAG: <<BoolNot:z\d+>> BooleanNot [<<Arg>>]
- /// CHECK-DAG: <<Cond:z\d+>> NotEqual [<<BoolNot>>,<<Const2>>]
- /// CHECK-DAG: Return [<<Cond>>]
+ /// CHECK-DAG: <<NotArg:i\d+>> Select [<<Const1>>,<<Const0>>,<<Arg>>]
+ /// CHECK-DAG: <<Cond:z\d+>> NotEqual [<<NotArg>>,<<Const2>>]
+ /// CHECK-DAG: <<NotCond:i\d+>> Select [<<Const1>>,<<Const0>>,<<Cond>>]
+ /// CHECK-DAG: Return [<<NotCond>>]
/// CHECK-START: boolean Main.NotEqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (after)
- /// CHECK-DAG: <<True:i\d+>> IntConstant 1
- /// CHECK-DAG: Return [<<True>>]
+ /// CHECK-DAG: <<False:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<False>>]
public static boolean NotEqualBoolVsIntConst(boolean arg) {
- return (arg ? 0 : 1) != 2;
+ return (arg ? 0 : 1) == 2;
}
/*
@@ -1113,19 +1171,16 @@
/// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_bce (before)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
- /// CHECK-DAG: <<NotNotArg:z\d+>> BooleanNot [<<NotArg>>]
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<NotArg:i\d+>> Select [<<Const1>>,<<Const0>>,<<Arg>>]
+ /// CHECK-DAG: <<NotNotArg:i\d+>> Select [<<Const1>>,<<Const0>>,<<NotArg>>]
/// CHECK-DAG: Return [<<NotNotArg>>]
/// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_bce (after)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: BooleanNot [<<Arg>>]
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_bce (after)
- /// CHECK: BooleanNot
- /// CHECK-NOT: BooleanNot
-
public static boolean NegateValue(boolean arg) {
return !arg;
}
@@ -1254,8 +1309,14 @@
/// CHECK-START: int Main.booleanFieldNotEqualOne() instruction_simplifier (after)
/// CHECK-DAG: <<Field:z\d+>> StaticFieldGet
- /// CHECK-DAG: <<Not:z\d+>> BooleanNot [<<Field>>]
- /// CHECK-DAG: If [<<Not>>]
+ /// CHECK-DAG: If [<<Field>>]
+
+ /// CHECK-START: int Main.booleanFieldNotEqualOne() instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Field:z\d+>> StaticFieldGet
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
+ /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const54>>,<<Const13>>,<<Field>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static int booleanFieldNotEqualOne() {
return (booleanField == true) ? 13 : 54;
@@ -1269,8 +1330,14 @@
/// CHECK-START: int Main.booleanFieldEqualZero() instruction_simplifier (after)
/// CHECK-DAG: <<Field:z\d+>> StaticFieldGet
- /// CHECK-DAG: <<Not:z\d+>> BooleanNot [<<Field>>]
- /// CHECK-DAG: If [<<Not>>]
+ /// CHECK-DAG: If [<<Field>>]
+
+ /// CHECK-START: int Main.booleanFieldEqualZero() instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Field:z\d+>> StaticFieldGet
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
+ /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const54>>,<<Const13>>,<<Field>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static int booleanFieldEqualZero() {
return (booleanField != false) ? 13 : 54;
@@ -1278,18 +1345,27 @@
/// CHECK-START: int Main.intConditionNotEqualOne(int) instruction_simplifier_after_bce (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
/// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
- /// CHECK-DAG: <<GT:z\d+>> GreaterThan [<<Arg>>,<<Const42>>]
+ /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
+ /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Arg>>,<<Const42>>]
+ /// CHECK-DAG: <<GT:i\d+>> Select [<<Const1>>,<<Const0>>,<<LE>>]
/// CHECK-DAG: <<NE:z\d+>> NotEqual [<<GT>>,<<Const1>>]
- /// CHECK-DAG: If [<<NE>>]
+ /// CHECK-DAG: <<Result:i\d+>> Select [<<Const13>>,<<Const54>>,<<NE>>]
+ /// CHECK-DAG: Return [<<Result>>]
/// CHECK-START: int Main.intConditionNotEqualOne(int) instruction_simplifier_after_bce (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
/// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
- /// CHECK-DAG: If [<<LE:z\d+>>]
+ /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
+ /// CHECK-DAG: <<Result:i\d+>> Select [<<Const13>>,<<Const54>>,<<LE:z\d+>>]
/// CHECK-DAG: <<LE>> LessThanOrEqual [<<Arg>>,<<Const42>>]
- // Note that we match `LE` from If because there are two identical LessThanOrEqual instructions.
+ /// CHECK-DAG: Return [<<Result>>]
+ // Note that we match `LE` from Select because there are two identical
+ // LessThanOrEqual instructions.
public static int intConditionNotEqualOne(int i) {
return ((i > 42) == true) ? 13 : 54;
@@ -1298,17 +1374,26 @@
/// CHECK-START: int Main.intConditionEqualZero(int) instruction_simplifier_after_bce (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
/// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
- /// CHECK-DAG: <<GT:z\d+>> GreaterThan [<<Arg>>,<<Const42>>]
- /// CHECK-DAG: <<EQ:z\d+>> Equal [<<GT>>,<<Const0>>]
- /// CHECK-DAG: If [<<EQ>>]
+ /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
+ /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Arg>>,<<Const42>>]
+ /// CHECK-DAG: <<GT:i\d+>> Select [<<Const1>>,<<Const0>>,<<LE>>]
+ /// CHECK-DAG: <<NE:z\d+>> Equal [<<GT>>,<<Const0>>]
+ /// CHECK-DAG: <<Result:i\d+>> Select [<<Const13>>,<<Const54>>,<<NE>>]
+ /// CHECK-DAG: Return [<<Result>>]
/// CHECK-START: int Main.intConditionEqualZero(int) instruction_simplifier_after_bce (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
/// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
- /// CHECK-DAG: If [<<LE:z\d+>>]
+ /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
+ /// CHECK-DAG: <<Result:i\d+>> Select [<<Const13>>,<<Const54>>,<<LE:z\d+>>]
/// CHECK-DAG: <<LE>> LessThanOrEqual [<<Arg>>,<<Const42>>]
- // Note that we match `LE` from If because there are two identical LessThanOrEqual instructions.
+ /// CHECK-DAG: Return [<<Result>>]
+ // Note that we match `LE` from Select because there are two identical
+ // LessThanOrEqual instructions.
public static int intConditionEqualZero(int i) {
return ((i > 42) != false) ? 13 : 54;
@@ -1316,17 +1401,33 @@
// Test that conditions on float/double are not flipped.
+ /// CHECK-START: int Main.floatConditionNotEqualOne(float) ssa_builder (after)
+ /// CHECK: LessThanOrEqual
+
/// CHECK-START: int Main.floatConditionNotEqualOne(float) register (before)
- /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
- /// CHECK-DAG: NotEqual [{{i\d+}},<<Const1>>]
+ /// CHECK-DAG: <<Arg:f\d+>> ParameterValue
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
+ /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
+ /// CHECK-DAG: <<Const42:f\d+>> FloatConstant 42
+ /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Arg>>,<<Const42>>]
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const13>>,<<Const54>>,<<LE>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static int floatConditionNotEqualOne(float f) {
return ((f > 42.0f) == true) ? 13 : 54;
}
+ /// CHECK-START: int Main.doubleConditionEqualZero(double) ssa_builder (after)
+ /// CHECK: LessThanOrEqual
+
/// CHECK-START: int Main.doubleConditionEqualZero(double) register (before)
- /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
- /// CHECK-DAG: Equal [{{i\d+}},<<Const0>>]
+ /// CHECK-DAG: <<Arg:d\d+>> ParameterValue
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
+ /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
+ /// CHECK-DAG: <<Const42:d\d+>> DoubleConstant 42
+ /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Arg>>,<<Const42>>]
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const13>>,<<Const54>>,<<LE>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static int doubleConditionEqualZero(double d) {
return ((d > 42.0) != false) ? 13 : 54;
@@ -1374,6 +1475,10 @@
assertIntEquals(NotEqualTrueLhs(true), 3);
assertIntEquals(NotEqualFalseRhs(true), 5);
assertIntEquals(NotEqualFalseLhs(true), 5);
+ assertBooleanEquals(EqualBoolVsIntConst(true), true);
+ assertBooleanEquals(EqualBoolVsIntConst(true), true);
+ assertBooleanEquals(NotEqualBoolVsIntConst(false), false);
+ assertBooleanEquals(NotEqualBoolVsIntConst(false), false);
assertBooleanEquals(NotNotBool(true), true);
assertBooleanEquals(NotNotBool(false), false);
assertFloatEquals(Div2(100.0f), 50.0f);
diff --git a/test/463-checker-boolean-simplifier/src/Main.java b/test/463-checker-boolean-simplifier/src/Main.java
index 61510d8..682f126 100644
--- a/test/463-checker-boolean-simplifier/src/Main.java
+++ b/test/463-checker-boolean-simplifier/src/Main.java
@@ -37,7 +37,7 @@
* empty branches removed.
*/
- /// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (before)
+ /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (before)
/// CHECK-DAG: <<Param:z\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
@@ -45,23 +45,24 @@
/// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,<<Const0>>]
/// CHECK-DAG: Return [<<Phi>>]
- /// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (before)
+ /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (before)
/// CHECK: Goto
/// CHECK: Goto
/// CHECK: Goto
/// CHECK-NOT: Goto
- /// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (after)
/// CHECK-DAG: <<Param:z\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
- /// CHECK-DAG: <<NotParam:z\d+>> BooleanNot [<<Param>>]
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<NotParam:i\d+>> Select [<<Const1>>,<<Const0>>,<<Param>>]
/// CHECK-DAG: Return [<<NotParam>>]
- /// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (after)
/// CHECK-NOT: If
/// CHECK-NOT: Phi
- /// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (after)
/// CHECK: Goto
/// CHECK-NOT: Goto
@@ -74,7 +75,7 @@
* and 0 when False.
*/
- /// CHECK-START: boolean Main.GreaterThan(int, int) boolean_simplifier (before)
+ /// CHECK-START: boolean Main.GreaterThan(int, int) select_generator (before)
/// CHECK-DAG: <<ParamX:i\d+>> ParameterValue
/// CHECK-DAG: <<ParamY:i\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
@@ -84,13 +85,14 @@
/// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const0>>,<<Const1>>]
/// CHECK-DAG: Return [<<Phi>>]
- /// CHECK-START: boolean Main.GreaterThan(int, int) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.GreaterThan(int, int) select_generator (after)
/// CHECK-DAG: <<ParamX:i\d+>> ParameterValue
/// CHECK-DAG: <<ParamY:i\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Cond:z\d+>> GreaterThan [<<ParamX>>,<<ParamY>>]
- /// CHECK-DAG: Return [<<Cond>>]
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const0>>,<<Const1>>,<<Cond>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static boolean GreaterThan(int x, int y) {
return (x <= y) ? false : true;
@@ -101,7 +103,7 @@
* and 1 when False.
*/
- /// CHECK-START: boolean Main.LessThan(int, int) boolean_simplifier (before)
+ /// CHECK-START: boolean Main.LessThan(int, int) select_generator (before)
/// CHECK-DAG: <<ParamX:i\d+>> ParameterValue
/// CHECK-DAG: <<ParamY:i\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
@@ -111,13 +113,14 @@
/// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,<<Const0>>]
/// CHECK-DAG: Return [<<Phi>>]
- /// CHECK-START: boolean Main.LessThan(int, int) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.LessThan(int, int) select_generator (after)
/// CHECK-DAG: <<ParamX:i\d+>> ParameterValue
/// CHECK-DAG: <<ParamY:i\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Cond:z\d+>> LessThan [<<ParamX>>,<<ParamY>>]
- /// CHECK-DAG: Return [<<Cond>>]
+ /// CHECK-DAG: <<Cond:z\d+>> GreaterThanOrEqual [<<ParamX>>,<<ParamY>>]
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const1>>,<<Const0>>,<<Cond>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static boolean LessThan(int x, int y) {
return (x < y) ? true : false;
@@ -128,7 +131,7 @@
* Note that Phis are discovered retrospectively.
*/
- /// CHECK-START: boolean Main.ValuesOrdered(int, int, int) boolean_simplifier (before)
+ /// CHECK-START: boolean Main.ValuesOrdered(int, int, int) select_generator (before)
/// CHECK-DAG: <<ParamX:i\d+>> ParameterValue
/// CHECK-DAG: <<ParamY:i\d+>> ParameterValue
/// CHECK-DAG: <<ParamZ:i\d+>> ParameterValue
@@ -145,29 +148,25 @@
/// CHECK-DAG: <<PhiYZ>> Phi [<<Const1>>,<<Const0>>]
/// CHECK-DAG: <<PhiXYZ>> Phi [<<Const1>>,<<Const0>>]
- /// CHECK-START: boolean Main.ValuesOrdered(int, int, int) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.ValuesOrdered(int, int, int) select_generator (after)
/// CHECK-DAG: <<ParamX:i\d+>> ParameterValue
/// CHECK-DAG: <<ParamY:i\d+>> ParameterValue
/// CHECK-DAG: <<ParamZ:i\d+>> ParameterValue
- /// CHECK-DAG: <<CmpXY:z\d+>> LessThanOrEqual [<<ParamX>>,<<ParamY>>]
- /// CHECK-DAG: <<CmpYZ:z\d+>> LessThanOrEqual [<<ParamY>>,<<ParamZ>>]
- /// CHECK-DAG: <<CmpXYZ:z\d+>> Equal [<<CmpXY>>,<<CmpYZ>>]
- /// CHECK-DAG: Return [<<CmpXYZ>>]
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<CmpXY:z\d+>> GreaterThan [<<ParamX>>,<<ParamY>>]
+ /// CHECK-DAG: <<SelXY:i\d+>> Select [<<Const1>>,<<Const0>>,<<CmpXY>>]
+ /// CHECK-DAG: <<CmpYZ:z\d+>> GreaterThan [<<ParamY>>,<<ParamZ>>]
+ /// CHECK-DAG: <<SelYZ:i\d+>> Select [<<Const1>>,<<Const0>>,<<CmpYZ>>]
+ /// CHECK-DAG: <<CmpXYZ:z\d+>> NotEqual [<<SelXY>>,<<SelYZ>>]
+ /// CHECK-DAG: <<SelXYZ:i\d+>> Select [<<Const1>>,<<Const0>>,<<CmpXYZ>>]
+ /// CHECK-DAG: Return [<<SelXYZ>>]
public static boolean ValuesOrdered(int x, int y, int z) {
return (x <= y) == (y <= z);
}
- /// CHECK-START: int Main.NegatedCondition(boolean) boolean_simplifier (before)
- /// CHECK-DAG: <<Param:z\d+>> ParameterValue
- /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
- /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43
- /// CHECK-DAG: <<NotParam:z\d+>> BooleanNot [<<Param>>]
- /// CHECK-DAG: If [<<NotParam>>]
- /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const42>>,<<Const43>>]
- /// CHECK-DAG: Return [<<Phi>>]
-
- /// CHECK-START: int Main.NegatedCondition(boolean) boolean_simplifier (after)
+ /// CHECK-START: int Main.NegatedCondition(boolean) select_generator (before)
/// CHECK-DAG: <<Param:z\d+>> ParameterValue
/// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
/// CHECK-DAG: <<Const43:i\d+>> IntConstant 43
@@ -175,9 +174,14 @@
/// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const42>>,<<Const43>>]
/// CHECK-DAG: Return [<<Phi>>]
- // Note: The fact that branches are swapped is verified by running the test.
+ /// CHECK-START: int Main.NegatedCondition(boolean) select_generator (after)
+ /// CHECK-DAG: <<Param:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const43>>,<<Const42>>,<<Param>>]
+ /// CHECK-DAG: Return [<<Select>>]
- /// CHECK-START: int Main.NegatedCondition(boolean) boolean_simplifier (after)
+ /// CHECK-START: int Main.NegatedCondition(boolean) select_generator (after)
/// CHECK-NOT: BooleanNot
public static int NegatedCondition(boolean x) {
@@ -188,6 +192,179 @@
}
}
+ /// CHECK-START: int Main.SimpleTrueBlock(boolean, int) select_generator (after)
+ /// CHECK-DAG: <<ParamX:z\d+>> ParameterValue
+ /// CHECK-DAG: <<ParamY:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<ParamY>>,<<Const42>>]
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const43>>,<<Add>>,<<ParamX>>]
+ /// CHECK-DAG: Return [<<Select>>]
+
+ /// CHECK-START: int Main.SimpleTrueBlock(boolean, int) select_generator (after)
+ /// CHECK-NOT: If
+
+ public static int SimpleTrueBlock(boolean x, int y) {
+ return x ? y + 42 : 43;
+ }
+
+ /// CHECK-START: int Main.SimpleFalseBlock(boolean, int) select_generator (after)
+ /// CHECK-DAG: <<ParamX:z\d+>> ParameterValue
+ /// CHECK-DAG: <<ParamY:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<ParamY>>,<<Const43>>]
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Add>>,<<Const42>>,<<ParamX>>]
+ /// CHECK-DAG: Return [<<Select>>]
+
+ /// CHECK-START: int Main.SimpleFalseBlock(boolean, int) select_generator (after)
+ /// CHECK-NOT: If
+
+ public static int SimpleFalseBlock(boolean x, int y) {
+ return x ? 42 : y + 43;
+ }
+
+ /// CHECK-START: int Main.SimpleBothBlocks(boolean, int, int) select_generator (after)
+ /// CHECK-DAG: <<ParamX:z\d+>> ParameterValue
+ /// CHECK-DAG: <<ParamY:i\d+>> ParameterValue
+ /// CHECK-DAG: <<ParamZ:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43
+ /// CHECK-DAG: <<AddTrue:i\d+>> Add [<<ParamY>>,<<Const42>>]
+ /// CHECK-DAG: <<AddFalse:i\d+>> Add [<<ParamZ>>,<<Const43>>]
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<AddFalse>>,<<AddTrue>>,<<ParamX>>]
+ /// CHECK-DAG: Return [<<Select>>]
+
+ /// CHECK-START: int Main.SimpleBothBlocks(boolean, int, int) select_generator (after)
+ /// CHECK-NOT: If
+
+ public static int SimpleBothBlocks(boolean x, int y, int z) {
+ return x ? y + 42 : z + 43;
+ }
+
+ /// CHECK-START: int Main.ThreeBlocks(boolean, boolean) select_generator (after)
+ /// CHECK-DAG: <<ParamX:z\d+>> ParameterValue
+ /// CHECK-DAG: <<ParamY:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Select23:i\d+>> Select [<<Const3>>,<<Const2>>,<<ParamY>>]
+ /// CHECK-DAG: <<Select123:i\d+>> Select [<<Select23>>,<<Const1>>,<<ParamX>>]
+ /// CHECK-DAG: Return [<<Select123>>]
+
+ public static int ThreeBlocks(boolean x, boolean y) {
+ if (x) {
+ return 1;
+ } else if (y) {
+ return 2;
+ } else {
+ return 3;
+ }
+ }
+
+ /// CHECK-START: int Main.MultiplePhis() select_generator (before)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<PhiX:i\d+>> Phi [<<Const0>>,<<Const13>>,<<Const42>>]
+ /// CHECK-DAG: <<PhiY:i\d+>> Phi [<<Const1>>,<<Add:i\d+>>,<<Add>>]
+ /// CHECK-DAG: <<Add>> Add [<<PhiY>>,<<Const1>>]
+ /// CHECK-DAG: <<Cond:z\d+>> LessThanOrEqual [<<Add>>,<<Const1>>]
+ /// CHECK-DAG: If [<<Cond>>]
+ /// CHECK-DAG: Return [<<PhiX>>]
+
+ /// CHECK-START: int Main.MultiplePhis() select_generator (after)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<PhiX:i\d+>> Phi [<<Const0>>,<<Select:i\d+>>]
+ /// CHECK-DAG: <<PhiY:i\d+>> Phi [<<Const1>>,<<Add:i\d+>>]
+ /// CHECK-DAG: <<Add>> Add [<<PhiY>>,<<Const1>>]
+ /// CHECK-DAG: <<Cond:z\d+>> LessThanOrEqual [<<Add>>,<<Const1>>]
+ /// CHECK-DAG: <<Select>> Select [<<Const13>>,<<Const42>>,<<Cond>>]
+ /// CHECK-DAG: Return [<<PhiX>>]
+
+ public static int MultiplePhis() {
+ int x = 0;
+ int y = 1;
+ while (y++ < 10) {
+ if (y > 1) {
+ x = 13;
+ } else {
+ x = 42;
+ }
+ }
+ return x;
+ }
+
+ /// CHECK-START: int Main.TrueBlockWithTooManyInstructions(boolean) select_generator (before)
+ /// CHECK-DAG: <<This:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Cond:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+ /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43
+ /// CHECK-DAG: If [<<Cond>>]
+ /// CHECK-DAG: <<Iget:i\d+>> InstanceFieldGet [<<This>>]
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Iget>>,<<Const2>>]
+ /// CHECK-DAG: Phi [<<Add>>,<<Const43>>]
+
+ /// CHECK-START: int Main.TrueBlockWithTooManyInstructions(boolean) select_generator (after)
+ /// CHECK-NOT: Select
+
+ public int TrueBlockWithTooManyInstructions(boolean x) {
+ return x ? (read_field + 2) : 43;
+ }
+
+ /// CHECK-START: int Main.FalseBlockWithTooManyInstructions(boolean) select_generator (before)
+ /// CHECK-DAG: <<This:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Cond:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: If [<<Cond>>]
+ /// CHECK-DAG: <<Iget:i\d+>> InstanceFieldGet [<<This>>]
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Iget>>,<<Const3>>]
+ /// CHECK-DAG: Phi [<<Const42>>,<<Add>>]
+
+ /// CHECK-START: int Main.FalseBlockWithTooManyInstructions(boolean) select_generator (after)
+ /// CHECK-NOT: Select
+
+ public int FalseBlockWithTooManyInstructions(boolean x) {
+ return x ? 42 : (read_field + 3);
+ }
+
+ /// CHECK-START: int Main.TrueBlockWithSideEffects(boolean) select_generator (before)
+ /// CHECK-DAG: <<This:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Cond:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43
+ /// CHECK-DAG: If [<<Cond>>]
+ /// CHECK-DAG: InstanceFieldSet [<<This>>,<<Const42>>]
+ /// CHECK-DAG: Phi [<<Const42>>,<<Const43>>]
+
+ /// CHECK-START: int Main.TrueBlockWithSideEffects(boolean) select_generator (after)
+ /// CHECK-NOT: Select
+
+ public int TrueBlockWithSideEffects(boolean x) {
+ return x ? (write_field = 42) : 43;
+ }
+
+ /// CHECK-START: int Main.FalseBlockWithSideEffects(boolean) select_generator (before)
+ /// CHECK-DAG: <<This:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Cond:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43
+ /// CHECK-DAG: If [<<Cond>>]
+ /// CHECK-DAG: InstanceFieldSet [<<This>>,<<Const43>>]
+ /// CHECK-DAG: Phi [<<Const42>>,<<Const43>>]
+
+ /// CHECK-START: int Main.FalseBlockWithSideEffects(boolean) select_generator (after)
+ /// CHECK-NOT: Select
+
+ public int FalseBlockWithSideEffects(boolean x) {
+ return x ? 42 : (write_field = 43);
+ }
+
public static void main(String[] args) {
assertBoolEquals(false, BooleanNot(true));
assertBoolEquals(true, BooleanNot(false));
@@ -206,5 +383,30 @@
assertBoolEquals(false, ValuesOrdered(5, 5, 3));
assertIntEquals(42, NegatedCondition(true));
assertIntEquals(43, NegatedCondition(false));
+ assertIntEquals(46, SimpleTrueBlock(true, 4));
+ assertIntEquals(43, SimpleTrueBlock(false, 4));
+ assertIntEquals(42, SimpleFalseBlock(true, 7));
+ assertIntEquals(50, SimpleFalseBlock(false, 7));
+ assertIntEquals(48, SimpleBothBlocks(true, 6, 2));
+ assertIntEquals(45, SimpleBothBlocks(false, 6, 2));
+ assertIntEquals(1, ThreeBlocks(true, true));
+ assertIntEquals(1, ThreeBlocks(true, false));
+ assertIntEquals(2, ThreeBlocks(false, true));
+ assertIntEquals(3, ThreeBlocks(false, false));
+ assertIntEquals(13, MultiplePhis());
+
+ Main m = new Main();
+ assertIntEquals(42, m.TrueBlockWithTooManyInstructions(true));
+ assertIntEquals(43, m.TrueBlockWithTooManyInstructions(false));
+ assertIntEquals(42, m.FalseBlockWithTooManyInstructions(true));
+ assertIntEquals(43, m.FalseBlockWithTooManyInstructions(false));
+ assertIntEquals(42, m.TrueBlockWithSideEffects(true));
+ assertIntEquals(43, m.TrueBlockWithSideEffects(false));
+ assertIntEquals(42, m.FalseBlockWithSideEffects(true));
+ assertIntEquals(43, m.FalseBlockWithSideEffects(false));
}
+
+ // These need to be instance fields so as to not generate a LoadClass for iget/iput.
+ public int read_field = 40;
+ public int write_field = 42;
}
diff --git a/test/468-checker-bool-simplifier-regression/smali/TestCase.smali b/test/468-checker-bool-simplifier-regression/smali/TestCase.smali
index da1c5ec..87ad21e 100644
--- a/test/468-checker-bool-simplifier-regression/smali/TestCase.smali
+++ b/test/468-checker-bool-simplifier-regression/smali/TestCase.smali
@@ -18,7 +18,7 @@
.field public static value:Z
-## CHECK-START: boolean TestCase.testCase() boolean_simplifier (before)
+## CHECK-START: boolean TestCase.testCase() select_generator (before)
## CHECK-DAG: <<Const0:i\d+>> IntConstant 0
## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
## CHECK-DAG: <<Value:z\d+>> StaticFieldGet
@@ -26,10 +26,12 @@
## CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,<<Const0>>]
## CHECK-DAG: Return [<<Phi>>]
-## CHECK-START: boolean TestCase.testCase() boolean_simplifier (after)
+## CHECK-START: boolean TestCase.testCase() select_generator (after)
+## CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
## CHECK-DAG: <<Value:z\d+>> StaticFieldGet
-## CHECK-DAG: <<Not:z\d+>> BooleanNot [<<Value>>]
-## CHECK-DAG: Return [<<Not>>]
+## CHECK-DAG: <<Select:i\d+>> Select [<<Const1>>,<<Const0>>,<<Value>>]
+## CHECK-DAG: Return [<<Select>>]
.method public static testCase()Z
.registers 2
diff --git a/test/474-checker-boolean-input/src/Main.java b/test/474-checker-boolean-input/src/Main.java
index a2b219d..fbc28d8 100644
--- a/test/474-checker-boolean-input/src/Main.java
+++ b/test/474-checker-boolean-input/src/Main.java
@@ -27,9 +27,9 @@
* we implement a suitable type analysis.
*/
- /// CHECK-START: boolean Main.TestPhiAsBoolean(int) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.TestPhiAsBoolean(int) select_generator (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi
- /// CHECK-DAG: BooleanNot [<<Phi>>]
+ /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<Phi>>]
public static boolean f1;
public static boolean f2;
@@ -47,9 +47,9 @@
* we implement a suitable type analysis.
*/
- /// CHECK-START: boolean Main.TestAndAsBoolean(boolean, boolean) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.TestAndAsBoolean(boolean, boolean) select_generator (after)
/// CHECK-DAG: <<And:i\d+>> And
- /// CHECK-DAG: BooleanNot [<<And>>]
+ /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<And>>]
public static boolean InlineAnd(boolean x, boolean y) {
return x & y;
@@ -64,9 +64,9 @@
* we implement a suitable type analysis.
*/
- /// CHECK-START: boolean Main.TestOrAsBoolean(boolean, boolean) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.TestOrAsBoolean(boolean, boolean) select_generator (after)
/// CHECK-DAG: <<Or:i\d+>> Or
- /// CHECK-DAG: BooleanNot [<<Or>>]
+ /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<Or>>]
public static boolean InlineOr(boolean x, boolean y) {
return x | y;
@@ -81,9 +81,9 @@
* we implement a suitable type analysis.
*/
- /// CHECK-START: boolean Main.TestXorAsBoolean(boolean, boolean) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.TestXorAsBoolean(boolean, boolean) select_generator (after)
/// CHECK-DAG: <<Xor:i\d+>> Xor
- /// CHECK-DAG: BooleanNot [<<Xor>>]
+ /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<Xor>>]
public static boolean InlineXor(boolean x, boolean y) {
return x ^ y;
diff --git a/test/480-checker-dead-blocks/src/Main.java b/test/480-checker-dead-blocks/src/Main.java
index 5adafaf..e5171f0 100644
--- a/test/480-checker-dead-blocks/src/Main.java
+++ b/test/480-checker-dead-blocks/src/Main.java
@@ -56,6 +56,8 @@
z = x + y;
} else {
z = x - y;
+ // Prevent HSelect simplification by having a branch with multiple instructions.
+ System.nanoTime();
}
return z;
}
@@ -86,6 +88,8 @@
z = x + y;
} else {
z = x - y;
+ // Prevent HSelect simplification by having a branch with multiple instructions.
+ System.nanoTime();
}
return z;
}
diff --git a/test/485-checker-dce-loop-update/smali/TestCase.smali b/test/485-checker-dce-loop-update/smali/TestCase.smali
index 1de0bae..056f22c 100644
--- a/test/485-checker-dce-loop-update/smali/TestCase.smali
+++ b/test/485-checker-dce-loop-update/smali/TestCase.smali
@@ -137,15 +137,14 @@
## CHECK-DAG: <<Cst5:i\d+>> IntConstant 5
## CHECK-DAG: <<Cst7:i\d+>> IntConstant 7
## CHECK-DAG: <<Cst11:i\d+>> IntConstant 11
-## CHECK-DAG: <<PhiX1:i\d+>> Phi [<<ArgX>>,<<Add5:i\d+>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>>
+## CHECK-DAG: <<PhiX:i\d+>> Phi [<<ArgX>>,<<Add5:i\d+>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>>
## CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>>
-## CHECK-DAG: If [<<ArgZ>>] loop:<<HeaderY>>
-## CHECK-DAG: <<Mul9:i\d+>> Mul [<<PhiX1>>,<<Cst11>>] loop:<<HeaderY>>
-## CHECK-DAG: <<PhiX2:i\d+>> Phi [<<PhiX1>>,<<Mul9>>] loop:<<HeaderY>>
+## CHECK-DAG: <<Mul9:i\d+>> Mul [<<PhiX>>,<<Cst11>>] loop:<<HeaderY>>
+## CHECK-DAG: <<SelX:i\d+>> Select [<<PhiX>>,<<Mul9>>,<<ArgZ>>] loop:<<HeaderY>>
## CHECK-DAG: If [<<Cst1>>] loop:<<HeaderY>>
-## CHECK-DAG: <<Add5>> Add [<<PhiX2>>,<<Cst5>>] loop:<<HeaderY>>
-## CHECK-DAG: <<Add7>> Add [<<PhiX1>>,<<Cst7>>] loop:<<HeaderY>>
-## CHECK-DAG: Return [<<PhiX2>>] loop:none
+## CHECK-DAG: <<Add5>> Add [<<SelX>>,<<Cst5>>] loop:<<HeaderY>>
+## CHECK-DAG: <<Add7>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>>
+## CHECK-DAG: Return [<<SelX>>] loop:none
## CHECK-START: int TestCase.testExitPredecessors(int, boolean, boolean) dead_code_elimination_final (after)
## CHECK-DAG: <<ArgX:i\d+>> ParameterValue
@@ -153,13 +152,12 @@
## CHECK-DAG: <<ArgZ:z\d+>> ParameterValue
## CHECK-DAG: <<Cst7:i\d+>> IntConstant 7
## CHECK-DAG: <<Cst11:i\d+>> IntConstant 11
-## CHECK-DAG: <<PhiX1:i\d+>> Phi [<<ArgX>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>>
+## CHECK-DAG: <<PhiX:i\d+>> Phi [<<ArgX>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>>
## CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>>
-## CHECK-DAG: <<Add7>> Add [<<PhiX1>>,<<Cst7>>] loop:<<HeaderY>>
-## CHECK-DAG: If [<<ArgZ>>] loop:none
-## CHECK-DAG: <<Mul9:i\d+>> Mul [<<PhiX1>>,<<Cst11>>] loop:none
-## CHECK-DAG: <<PhiX2:i\d+>> Phi [<<PhiX1>>,<<Mul9>>] loop:none
-## CHECK-DAG: Return [<<PhiX2>>] loop:none
+## CHECK-DAG: <<Add7>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>>
+## CHECK-DAG: <<Mul9:i\d+>> Mul [<<PhiX>>,<<Cst11>>] loop:none
+## CHECK-DAG: <<SelX:i\d+>> Select [<<PhiX>>,<<Mul9>>,<<ArgZ>>] loop:none
+## CHECK-DAG: Return [<<SelX>>] loop:none
.method public static testExitPredecessors(IZZ)I
.registers 4
diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java
index baee7b3..f87326c 100644
--- a/test/530-checker-lse/src/Main.java
+++ b/test/530-checker-lse/src/Main.java
@@ -595,19 +595,16 @@
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<True>>]
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Float8>>]
/// CHECK-DAG: <<GetTest:z\d+>> InstanceFieldGet [<<Obj>>]
- /// CHECK-DAG: If [<<GetTest>>]
/// CHECK-DAG: <<GetField:f\d+>> InstanceFieldGet [<<Obj>>]
- /// CHECK-DAG: <<Phi:f\d+>> Phi [<<Float42>>,<<GetField>>]
- /// CHECK-DAG: Return [<<Phi>>]
+ /// CHECK-DAG: <<Select:f\d+>> Select [<<Float42>>,<<GetField>>,<<GetTest>>]
+ /// CHECK-DAG: Return [<<Select>>]
/// CHECK-START: float Main.test24() load_store_elimination (after)
/// CHECK-DAG: <<True:i\d+>> IntConstant 1
/// CHECK-DAG: <<Float8:f\d+>> FloatConstant 8
/// CHECK-DAG: <<Float42:f\d+>> FloatConstant 42
- /// CHECK-DAG: <<Obj:l\d+>> NewInstance
- /// CHECK-DAG: If [<<True>>]
- /// CHECK-DAG: <<Phi:f\d+>> Phi [<<Float42>>,<<Float8>>]
- /// CHECK-DAG: Return [<<Phi>>]
+ /// CHECK-DAG: <<Select:f\d+>> Select [<<Float42>>,<<Float8>>,<<True>>]
+ /// CHECK-DAG: Return [<<Select>>]
static float test24() {
float a = 42.0f;
diff --git a/test/543-checker-dce-trycatch/smali/TestCase.smali b/test/543-checker-dce-trycatch/smali/TestCase.smali
index 1756fa4..62511df 100644
--- a/test/543-checker-dce-trycatch/smali/TestCase.smali
+++ b/test/543-checker-dce-trycatch/smali/TestCase.smali
@@ -15,6 +15,8 @@
.class public LTestCase;
.super Ljava/lang/Object;
+.field public static sField:I
+
.method private static $inline$False()Z
.registers 1
const/4 v0, 0x0
@@ -240,24 +242,25 @@
shr-int/2addr p2, p3
:try_start
- const v1, 0xa # dead catch phi input, defined in entry block (HInstruction)
- add-int v2, p0, p1 # dead catch phi input, defined in the dead block (HInstruction)
+ const v1, 0xa # dead catch phi input, defined in entry block (HInstruction)
+ add-int v2, p0, p1 # dead catch phi input, defined in the dead block (HInstruction)
move v3, v2
if-eqz v3, :define_phi
+ sput v3, LTestCase;->sField:I # beat HSelect simplification (has side-effects, does not throw)
const v3, 0xf
:define_phi
- # v3 = Phi [Add, 0xf] # dead catch phi input, defined in the dead block (HPhi)
+ # v3 = Phi [Add, 0xf] # dead catch phi input, defined in the dead block (HPhi)
div-int/2addr p0, v2
:else
- const v1, 0xb # live catch phi input
- const v2, 0xc # live catch phi input
- const v3, 0x10 # live catch phi input
+ const v1, 0xb # live catch phi input
+ const v2, 0xc # live catch phi input
+ const v3, 0x10 # live catch phi input
div-int/2addr p0, p3
- const v1, 0xd # live catch phi input
- const v2, 0xe # live catch phi input
- const v3, 0x11 # live catch phi input
+ const v1, 0xd # live catch phi input
+ const v2, 0xe # live catch phi input
+ const v3, 0x11 # live catch phi input
div-int/2addr p0, p1
:try_end
.catchall {:try_start .. :try_end} :catch_all
diff --git a/test/565-checker-condition-liveness/expected.txt b/test/565-checker-condition-liveness/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/565-checker-condition-liveness/expected.txt
diff --git a/test/565-checker-condition-liveness/info.txt b/test/565-checker-condition-liveness/info.txt
new file mode 100644
index 0000000..67b6ceb
--- /dev/null
+++ b/test/565-checker-condition-liveness/info.txt
@@ -0,0 +1 @@
+Test the use positions of inputs of non-materialized conditions.
\ No newline at end of file
diff --git a/test/565-checker-condition-liveness/src/Main.java b/test/565-checker-condition-liveness/src/Main.java
new file mode 100644
index 0000000..a811e5b
--- /dev/null
+++ b/test/565-checker-condition-liveness/src/Main.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+ /// CHECK-START: void Main.main(java.lang.String[]) liveness (after)
+ /// CHECK: <<X:i\d+>> ArrayLength uses:[<<UseInput:\d+>>]
+ /// CHECK: <<Y:i\d+>> StaticFieldGet uses:[<<UseInput>>]
+ /// CHECK: <<Cond:z\d+>> LessThanOrEqual [<<X>>,<<Y>>]
+ /// CHECK-NEXT: If [<<Cond>>] liveness:<<LivIf:\d+>>
+ /// CHECK-EVAL: <<UseInput>> == <<LivIf>> + 1
+
+ public static void main(String[] args) {
+ int x = args.length;
+ int y = field;
+ if (x > y) {
+ System.nanoTime();
+ }
+ }
+
+ public static int field = 42;
+}
diff --git a/test/565-checker-irreducible-loop/expected.txt b/test/565-checker-irreducible-loop/expected.txt
new file mode 100644
index 0000000..6ed281c
--- /dev/null
+++ b/test/565-checker-irreducible-loop/expected.txt
@@ -0,0 +1,2 @@
+1
+1
diff --git a/test/565-checker-irreducible-loop/info.txt b/test/565-checker-irreducible-loop/info.txt
new file mode 100644
index 0000000..1e0dd02
--- /dev/null
+++ b/test/565-checker-irreducible-loop/info.txt
@@ -0,0 +1,2 @@
+Regression test for optimizing in the presence of
+an irreducible loop.
diff --git a/test/565-checker-irreducible-loop/smali/IrreducibleLoop.smali b/test/565-checker-irreducible-loop/smali/IrreducibleLoop.smali
new file mode 100644
index 0000000..29547ca
--- /dev/null
+++ b/test/565-checker-irreducible-loop/smali/IrreducibleLoop.smali
@@ -0,0 +1,101 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LIrreducibleLoop;
+
+.super Ljava/lang/Object;
+
+# Check that both the irreducible loop and the other loop entry
+# move the constant-folded value to where it's expected.
+
+## CHECK-START-X86: int IrreducibleLoop.test1(int, long) register (after)
+## CHECK-DAG: ParallelMove {{.*84->.*}} loop:none
+## CHECK-DAG: ParallelMove {{.*84->.*}} loop:{{B\d+}} irreducible:true
+.method public static test1(IJ)I
+ .registers 10
+ const/16 v6, 2
+ const/16 v4, 1
+ const-wide/16 v0, 42
+ add-long v2, v0, v0
+
+ if-eqz p0, :loop_entry
+ goto :other_loop_pre_entry
+
+ # The then part: beginning of the irreducible loop.
+ :loop_entry
+ if-eqz p0, :exit
+ cmp-long v6, v2, p1
+ :other_loop_entry
+ sub-int p0, p0, v4
+ goto :loop_entry
+
+ # The other block branching to the irreducible loop.
+ # In that block, v4 has no live range.
+ :other_loop_pre_entry
+ goto :other_loop_entry
+
+ :exit
+ return v6
+.end method
+
+# Check that the compiler does not crash when
+# a live interval is found while connecting siblings, but that
+# live interval is inactive at the desired position.
+
+## CHECK-START-X86: int IrreducibleLoop.test2(int, long) register (after)
+## CHECK-DAG: ParallelMove {{.*84->.*}} loop:none
+## CHECK-DAG: ParallelMove {{.*84->.*}} loop:{{B\d+}} irreducible:true
+.method public static test2(IJ)I
+ .registers 14
+ const/16 v6, 2
+ const/16 v4, 1
+ const-wide/16 v0, 42
+ const-wide/16 v8, 68
+ add-long v2, v0, v0
+
+ if-eqz p0, :loop_entry
+ goto :other_loop_pre_entry
+
+ # The then part: beginning of the irreducible loop.
+ :loop_entry
+ if-eqz p0, :exit
+ cmp-long v6, v2, p1
+ :other_loop_entry
+ sub-int p0, p0, v4
+ goto :loop_entry
+
+ # The other block branching to the irreducible loop.
+ :other_loop_pre_entry
+ # Make v2 have a register location.
+ sput-wide v2, LIrreducibleLoop;->myField:J
+ # Stress register allocator on x86 to split v2.
+ sput-wide v0, LIrreducibleLoop;->myField:J
+ sput-wide p1, LIrreducibleLoop;->myField:J
+ sput-wide v8, LIrreducibleLoop;->myField:J
+ if-eqz p0, :join
+ # Stress register allocator on x86 to split v2.
+ sput-wide p1, LIrreducibleLoop;->myField:J
+ sput-wide v8, LIrreducibleLoop;->myField:J
+ sput-wide v0, LIrreducibleLoop;->myField:J
+ # Last use of v2 before the irreducible loop, that
+ # will create an interval hole.
+ sput-wide v2, LIrreducibleLoop;->myField:J
+ :join
+ goto :other_loop_entry
+
+ :exit
+ return v6
+.end method
+
+.field public static volatile myField:J
diff --git a/test/565-checker-irreducible-loop/src/Main.java b/test/565-checker-irreducible-loop/src/Main.java
new file mode 100644
index 0000000..e48bd6b
--- /dev/null
+++ b/test/565-checker-irreducible-loop/src/Main.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("IrreducibleLoop");
+ {
+ Method m = c.getMethod("test1", int.class, long.class);
+ Object[] arguments = { 42, 31L };
+ System.out.println(m.invoke(null, arguments));
+ }
+
+ {
+ Method m = c.getMethod("test2", int.class, long.class);
+ Object[] arguments = { 42, 31L };
+ System.out.println(m.invoke(null, arguments));
+ }
+ }
+}
diff --git a/test/566-checker-codegen-select/expected.txt b/test/566-checker-codegen-select/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/566-checker-codegen-select/expected.txt
diff --git a/test/566-checker-codegen-select/info.txt b/test/566-checker-codegen-select/info.txt
new file mode 100644
index 0000000..67b6ceb
--- /dev/null
+++ b/test/566-checker-codegen-select/info.txt
@@ -0,0 +1 @@
+Test the use positions of inputs of non-materialized conditions.
\ No newline at end of file
diff --git a/test/566-checker-codegen-select/src/Main.java b/test/566-checker-codegen-select/src/Main.java
new file mode 100644
index 0000000..edb31e6
--- /dev/null
+++ b/test/566-checker-codegen-select/src/Main.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+ /// CHECK-START: long Main.$noinline$longSelect(long) register (before)
+ /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{j\d+}},{{j\d+}}]
+ /// CHECK-NEXT: Select [{{j\d+}},{{j\d+}},<<Cond>>]
+
+ // Condition must be materialized on X86 because it would need too many
+ // registers otherwise.
+ /// CHECK-START-X86: long Main.$noinline$longSelect(long) disassembly (after)
+ /// CHECK: LessThanOrEqual
+ /// CHECK-NEXT: cmp
+ /// CHECK: Select
+
+ public long $noinline$longSelect(long param) {
+ if (doThrow) { throw new Error(); }
+ long val_true = longB;
+ long val_false = longC;
+ return (param > longA) ? val_true : val_false;
+ }
+
+ /// CHECK-START: long Main.$noinline$longSelect_Constant(long) register (before)
+ /// CHECK: <<Const:j\d+>> LongConstant
+ /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{j\d+}},<<Const>>]
+ /// CHECK-NEXT: Select [{{j\d+}},{{j\d+}},<<Cond>>]
+
+ // Condition can be non-materialized on X86 because the condition does not
+ // request 4 registers any more.
+ /// CHECK-START-X86: long Main.$noinline$longSelect_Constant(long) disassembly (after)
+ /// CHECK: LessThanOrEqual
+ /// CHECK-NEXT: Select
+
+ public long $noinline$longSelect_Constant(long param) {
+ if (doThrow) { throw new Error(); }
+ long val_true = longB;
+ long val_false = longC;
+ return (param > 3L) ? val_true : val_false;
+ }
+
+ public static void main(String[] args) {
+ Main m = new Main();
+ assertLongEquals(5L, m.$noinline$longSelect(4L));
+ assertLongEquals(7L, m.$noinline$longSelect(2L));
+ assertLongEquals(5L, m.$noinline$longSelect_Constant(4L));
+ assertLongEquals(7L, m.$noinline$longSelect_Constant(2L));
+ }
+
+ public static void assertLongEquals(long expected, long actual) {
+ if (expected != actual) {
+ throw new Error(expected + " != " + actual);
+ }
+ }
+
+ public boolean doThrow = false;
+
+ public long longA = 3L;
+ public long longB = 5L;
+ public long longC = 7L;
+}
diff --git a/test/566-polymorphic-inlining/expected.txt b/test/566-polymorphic-inlining/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/566-polymorphic-inlining/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/566-polymorphic-inlining/info.txt b/test/566-polymorphic-inlining/info.txt
new file mode 100644
index 0000000..1a47f89
--- /dev/null
+++ b/test/566-polymorphic-inlining/info.txt
@@ -0,0 +1 @@
+Test polymorphic inlining.
diff --git a/test/566-polymorphic-inlining/polymorphic_inline.cc b/test/566-polymorphic-inlining/polymorphic_inline.cc
new file mode 100644
index 0000000..55eac5c
--- /dev/null
+++ b/test/566-polymorphic-inlining/polymorphic_inline.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "art_method.h"
+#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
+#include "oat_quick_method_header.h"
+#include "scoped_thread_state_change.h"
+#include "stack_map.h"
+
+namespace art {
+
+static void do_checks(jclass cls, const char* method_name) {
+ ScopedObjectAccess soa(Thread::Current());
+ mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ jit::JitCodeCache* code_cache = jit->GetCodeCache();
+ ArtMethod* method = klass->FindDeclaredDirectMethodByName(method_name, sizeof(void*));
+ jit->CompileMethod(method, soa.Self());
+
+ OatQuickMethodHeader* header = OatQuickMethodHeader::FromEntryPoint(
+ method->GetEntryPointFromQuickCompiledCode());
+ CHECK(code_cache->ContainsPc(header->GetCode()));
+
+ CodeInfo info = header->GetOptimizedCodeInfo();
+ CHECK(info.HasInlineInfo());
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_ensureJittedAndPolymorphicInline(JNIEnv*, jclass cls) {
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit == nullptr) {
+ return;
+ }
+
+ do_checks(cls, "testInvokeVirtual");
+ do_checks(cls, "testInvokeInterface");
+}
+
+} // namespace art
diff --git a/test/566-polymorphic-inlining/src/Main.java b/test/566-polymorphic-inlining/src/Main.java
new file mode 100644
index 0000000..7283e86
--- /dev/null
+++ b/test/566-polymorphic-inlining/src/Main.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface Itf {
+ public Class sameInvokeInterface();
+}
+
+public class Main implements Itf {
+ public static void assertEquals(Object expected, Object actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ Main[] mains = new Main[3];
+ Itf[] itfs = new Itf[3];
+ itfs[0] = mains[0] = new Main();
+ itfs[1] = mains[1] = new Subclass();
+ itfs[2] = mains[2] = new OtherSubclass();
+
+ // Make testInvokeVirtual and testInvokeInterface hot to get them jitted.
+ // We pass Main and Subclass to get polymorphic inlining based on calling
+ // the same method.
+ for (int i = 0; i < 10000; ++i) {
+ testInvokeVirtual(mains[0]);
+ testInvokeVirtual(mains[1]);
+ testInvokeInterface(itfs[0]);
+ testInvokeInterface(itfs[1]);
+ }
+
+ ensureJittedAndPolymorphicInline();
+
+ // At this point, the JIT should have compiled both methods, and inline
+ // sameInvokeVirtual and sameInvokeInterface.
+ assertEquals(Main.class, testInvokeVirtual(mains[0]));
+ assertEquals(Main.class, testInvokeVirtual(mains[1]));
+
+ assertEquals(Itf.class, testInvokeInterface(itfs[0]));
+ assertEquals(Itf.class, testInvokeInterface(itfs[1]));
+
+ // This will trigger a deoptimization of the compiled code.
+ assertEquals(OtherSubclass.class, testInvokeVirtual(mains[2]));
+ assertEquals(OtherSubclass.class, testInvokeInterface(itfs[2]));
+ }
+
+ public Class sameInvokeVirtual() {
+ field.getClass(); // null check to ensure we get an inlined frame in the CodeInfo
+ return Main.class;
+ }
+
+ public Class sameInvokeInterface() {
+ field.getClass(); // null check to ensure we get an inlined frame in the CodeInfo
+ return Itf.class;
+ }
+
+ public static Class testInvokeInterface(Itf i) {
+ return i.sameInvokeInterface();
+ }
+
+ public static Class testInvokeVirtual(Main m) {
+ return m.sameInvokeVirtual();
+ }
+
+ public Object field = new Object();
+
+ public static native void ensureJittedAndPolymorphicInline();
+}
+
+class Subclass extends Main {
+}
+
+class OtherSubclass extends Main {
+ public Class sameInvokeVirtual() {
+ return OtherSubclass.class;
+ }
+
+ public Class sameInvokeInterface() {
+ return OtherSubclass.class;
+ }
+}
diff --git a/test/569-checker-pattern-replacement/expected.txt b/test/569-checker-pattern-replacement/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/569-checker-pattern-replacement/expected.txt
diff --git a/test/569-checker-pattern-replacement/info.txt b/test/569-checker-pattern-replacement/info.txt
new file mode 100644
index 0000000..4dfa932
--- /dev/null
+++ b/test/569-checker-pattern-replacement/info.txt
@@ -0,0 +1 @@
+Test pattern substitution used when we cannot inline.
diff --git a/test/569-checker-pattern-replacement/run b/test/569-checker-pattern-replacement/run
new file mode 100755
index 0000000..f7e9df2
--- /dev/null
+++ b/test/569-checker-pattern-replacement/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+exec ${RUN} "$@" \
+ -Xcompiler-option --no-inline-from=core-oj,569-checker-pattern-replacement.jar:classes2.dex
diff --git a/test/569-checker-pattern-replacement/src-multidex/Second.java b/test/569-checker-pattern-replacement/src-multidex/Second.java
new file mode 100644
index 0000000..cba1dc8
--- /dev/null
+++ b/test/569-checker-pattern-replacement/src-multidex/Second.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public final class Second {
+ public static void staticNop(int unused) { }
+
+ public void nop() { }
+
+ public static Object staticReturnArg2(int unused1, String arg2) {
+ return arg2;
+ }
+
+ public long returnArg1(long arg1) {
+ return arg1;
+ }
+
+ public static int staticReturn9() {
+ return 9;
+ }
+
+ public int return7(Object unused) {
+ return 7;
+ }
+
+ public static String staticReturnNull() {
+ return null;
+ }
+
+ public Object returnNull() {
+ return null;
+ }
+
+ public int getInstanceIntField() {
+ return instanceIntField;
+ }
+
+ public double getInstanceDoubleField(int unused1) {
+ return instanceDoubleField;
+ }
+
+ public Object getInstanceObjectField(long unused1) {
+ return instanceObjectField;
+ }
+
+ public String getInstanceStringField(Object unused1, String unused2, long unused3) {
+ return instanceStringField;
+ }
+
+ public static int staticGetInstanceIntField(Second s) {
+ return s.instanceIntField;
+ }
+
+ public double getInstanceDoubleFieldFromParam(Second s) {
+ return s.instanceDoubleField;
+ }
+
+ public int getStaticIntField() {
+ return staticIntField;
+ }
+
+ public void setInstanceLongField(int ignored, long value) {
+ instanceLongField = value;
+ }
+
+ public int setInstanceLongFieldReturnArg2(long value, int arg2) {
+ instanceLongField = value;
+ return arg2;
+ }
+
+ public static void staticSetInstanceLongField(Second s, long value) {
+ s.instanceLongField = value;
+ }
+
+ public void setInstanceLongFieldThroughParam(Second s, long value) {
+ s.instanceLongField = value;
+ }
+
+ public void setStaticFloatField(float value) {
+ staticFloatField = value;
+ }
+
+ public int instanceIntField = 42;
+ public double instanceDoubleField = -42.0;
+ public Object instanceObjectField = null;
+ public String instanceStringField = "dummy";
+ public long instanceLongField = 0; // Overwritten by setters.
+
+ public static int staticIntField = 4242;
+ public static float staticFloatField = 0.0f; // Overwritten by setters.
+}
diff --git a/test/569-checker-pattern-replacement/src/Main.java b/test/569-checker-pattern-replacement/src/Main.java
new file mode 100644
index 0000000..9a85c81
--- /dev/null
+++ b/test/569-checker-pattern-replacement/src/Main.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ /// CHECK-START: void Main.staticNop() inliner (before)
+ /// CHECK: InvokeStaticOrDirect
+
+ /// CHECK-START: void Main.staticNop() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static void staticNop() {
+ Second.staticNop(11);
+ }
+
+ /// CHECK-START: void Main.nop(Second) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: void Main.nop(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static void nop(Second s) {
+ s.nop();
+ }
+
+ /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (before)
+ /// CHECK-DAG: <<Value:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Ignored:i\d+>> IntConstant 77
+ /// CHECK-DAG: <<ClinitCk:l\d+>> ClinitCheck
+ /// CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect [<<Ignored>>,<<Value>>,<<ClinitCk>>]
+ /// CHECK-DAG: Return [<<Invoke>>]
+
+ /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (after)
+ /// CHECK-DAG: <<Value:l\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Value>>]
+
+ /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static Object staticReturnArg2(String value) {
+ return Second.staticReturnArg2(77, value);
+ }
+
+ /// CHECK-START: long Main.returnArg1(Second, long) inliner (before)
+ /// CHECK-DAG: <<Second:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Value:j\d+>> ParameterValue
+ /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<Second>>]
+ /// CHECK-DAG: <<Invoke:j\d+>> InvokeVirtual [<<NullCk>>,<<Value>>]
+ /// CHECK-DAG: Return [<<Invoke>>]
+
+ /// CHECK-START: long Main.returnArg1(Second, long) inliner (after)
+ /// CHECK-DAG: <<Value:j\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Value>>]
+
+ /// CHECK-START: long Main.returnArg1(Second, long) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static long returnArg1(Second s, long value) {
+ return s.returnArg1(value);
+ }
+
+ /// CHECK-START: int Main.staticReturn9() inliner (before)
+ /// CHECK: {{i\d+}} InvokeStaticOrDirect
+
+ /// CHECK-START: int Main.staticReturn9() inliner (before)
+ /// CHECK-NOT: IntConstant 9
+
+ /// CHECK-START: int Main.staticReturn9() inliner (after)
+ /// CHECK-DAG: <<Const9:i\d+>> IntConstant 9
+ /// CHECK-DAG: Return [<<Const9>>]
+
+ /// CHECK-START: int Main.staticReturn9() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static int staticReturn9() {
+ return Second.staticReturn9();
+ }
+
+ /// CHECK-START: int Main.return7(Second) inliner (before)
+ /// CHECK: {{i\d+}} InvokeVirtual
+
+ /// CHECK-START: int Main.return7(Second) inliner (before)
+ /// CHECK-NOT: IntConstant 7
+
+ /// CHECK-START: int Main.return7(Second) inliner (after)
+ /// CHECK-DAG: <<Const7:i\d+>> IntConstant 7
+ /// CHECK-DAG: Return [<<Const7>>]
+
+ /// CHECK-START: int Main.return7(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static int return7(Second s) {
+ return s.return7(null);
+ }
+
+ /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (before)
+ /// CHECK: {{l\d+}} InvokeStaticOrDirect
+
+ /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (before)
+ /// CHECK-NOT: NullConstant
+
+ /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (after)
+ /// CHECK-DAG: <<Null:l\d+>> NullConstant
+ /// CHECK-DAG: Return [<<Null>>]
+
+ /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static String staticReturnNull() {
+ return Second.staticReturnNull();
+ }
+
+ /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (before)
+ /// CHECK: {{l\d+}} InvokeVirtual
+
+ /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (before)
+ /// CHECK-NOT: NullConstant
+
+ /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (after)
+ /// CHECK-DAG: <<Null:l\d+>> NullConstant
+ /// CHECK-DAG: Return [<<Null>>]
+
+ /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static Object returnNull(Second s) {
+ return s.returnNull();
+ }
+
+ /// CHECK-START: int Main.getInt(Second) inliner (before)
+ /// CHECK: {{i\d+}} InvokeVirtual
+
+ /// CHECK-START: int Main.getInt(Second) inliner (after)
+ /// CHECK: {{i\d+}} InstanceFieldGet
+
+ /// CHECK-START: int Main.getInt(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static int getInt(Second s) {
+ return s.getInstanceIntField();
+ }
+
+ /// CHECK-START: double Main.getDouble(Second) inliner (before)
+ /// CHECK: {{d\d+}} InvokeVirtual
+
+ /// CHECK-START: double Main.getDouble(Second) inliner (after)
+ /// CHECK: {{d\d+}} InstanceFieldGet
+
+ /// CHECK-START: double Main.getDouble(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static double getDouble(Second s) {
+ return s.getInstanceDoubleField(22);
+ }
+
+ /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (before)
+ /// CHECK: {{l\d+}} InvokeVirtual
+
+ /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (after)
+ /// CHECK: {{l\d+}} InstanceFieldGet
+
+ /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static Object getObject(Second s) {
+ return s.getInstanceObjectField(-1L);
+ }
+
+ /// CHECK-START: java.lang.String Main.getString(Second) inliner (before)
+ /// CHECK: {{l\d+}} InvokeVirtual
+
+ /// CHECK-START: java.lang.String Main.getString(Second) inliner (after)
+ /// CHECK: {{l\d+}} InstanceFieldGet
+
+ /// CHECK-START: java.lang.String Main.getString(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static String getString(Second s) {
+ return s.getInstanceStringField(null, "whatever", 1234L);
+ }
+
+ /// CHECK-START: int Main.staticGetInt(Second) inliner (before)
+ /// CHECK: {{i\d+}} InvokeStaticOrDirect
+
+ /// CHECK-START: int Main.staticGetInt(Second) inliner (after)
+ /// CHECK: {{i\d+}} InvokeStaticOrDirect
+
+ /// CHECK-START: int Main.staticGetInt(Second) inliner (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ public static int staticGetInt(Second s) {
+ return Second.staticGetInstanceIntField(s);
+ }
+
+ /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (before)
+ /// CHECK: {{d\d+}} InvokeVirtual
+
+ /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (after)
+ /// CHECK: {{d\d+}} InvokeVirtual
+
+ /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ public static double getDoubleFromParam(Second s) {
+ return s.getInstanceDoubleFieldFromParam(s);
+ }
+
+ /// CHECK-START: int Main.getStaticInt(Second) inliner (before)
+ /// CHECK: {{i\d+}} InvokeVirtual
+
+ /// CHECK-START: int Main.getStaticInt(Second) inliner (after)
+ /// CHECK: {{i\d+}} InvokeVirtual
+
+ /// CHECK-START: int Main.getStaticInt(Second) inliner (after)
+ /// CHECK-NOT: InstanceFieldGet
+ /// CHECK-NOT: StaticFieldGet
+
+ public static int getStaticInt(Second s) {
+ return s.getStaticIntField();
+ }
+
+ /// CHECK-START: long Main.setLong(Second, long) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: long Main.setLong(Second, long) inliner (after)
+ /// CHECK: InstanceFieldSet
+
+ /// CHECK-START: long Main.setLong(Second, long) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static long setLong(Second s, long value) {
+ s.setInstanceLongField(-1, value);
+ return s.instanceLongField;
+ }
+
+ /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (after)
+ /// CHECK-DAG: <<Second:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Value:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<Second>>]
+ /// CHECK-DAG: InstanceFieldSet [<<NullCk>>,<<Value>>]
+ /// CHECK-DAG: <<NullCk2:l\d+>> NullCheck [<<Second>>]
+ /// CHECK-DAG: <<IGet:j\d+>> InstanceFieldGet [<<NullCk2>>]
+ /// CHECK-DAG: <<Conv:j\d+>> TypeConversion [<<Arg2>>]
+ /// CHECK-DAG: <<Add:j\d+>> Add [<<IGet>>,<<Conv>>]
+ /// CHECK-DAG: Return [<<Add>>]
+
+ /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static long setLongReturnArg2(Second s, long value, int arg2) {
+ int result = s.setInstanceLongFieldReturnArg2(value, arg2);
+ return s.instanceLongField + result;
+ }
+
+ /// CHECK-START: long Main.staticSetLong(Second, long) inliner (before)
+ /// CHECK: InvokeStaticOrDirect
+
+ /// CHECK-START: long Main.staticSetLong(Second, long) inliner (after)
+ /// CHECK: InvokeStaticOrDirect
+
+ /// CHECK-START: long Main.staticSetLong(Second, long) inliner (after)
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static long staticSetLong(Second s, long value) {
+ Second.staticSetInstanceLongField(s, value);
+ return s.instanceLongField;
+ }
+
+ /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (after)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (after)
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static long setLongThroughParam(Second s, long value) {
+ s.setInstanceLongFieldThroughParam(s, value);
+ return s.instanceLongField;
+ }
+
+ /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (after)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (after)
+ /// CHECK-NOT: InstanceFieldSet
+ /// CHECK-NOT: StaticFieldSet
+
+ public static float setStaticFloat(Second s, float value) {
+ s.setStaticFloatField(value);
+ return s.staticFloatField;
+ }
+
+ /// CHECK-START: java.lang.Object Main.newObject() inliner (before)
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>] method_name:java.lang.Object.<init>
+
+ /// CHECK-START: java.lang.Object Main.newObject() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static Object newObject() {
+ return new Object();
+ }
+
+ public static void main(String[] args) throws Exception {
+ Second s = new Second();
+
+ // Replaced NOP pattern.
+ staticNop();
+ nop(s);
+ // Replaced "return arg" pattern.
+ assertEquals("arbitrary string", staticReturnArg2("arbitrary string"));
+ assertEquals(4321L, returnArg1(s, 4321L));
+ // Replaced "return const" pattern.
+ assertEquals(9, staticReturn9());
+ assertEquals(7, return7(s));
+ assertEquals(null, staticReturnNull());
+ assertEquals(null, returnNull(s));
+ // Replaced IGET pattern.
+ assertEquals(42, getInt(s));
+ assertEquals(-42.0, getDouble(s));
+ assertEquals(null, getObject(s));
+ assertEquals("dummy", getString(s));
+ // Not replaced IGET pattern.
+ assertEquals(42, staticGetInt(s));
+ assertEquals(-42.0, getDoubleFromParam(s));
+ // SGET.
+ assertEquals(4242, getStaticInt(s));
+ // Replaced IPUT pattern.
+ assertEquals(111L, setLong(s, 111L));
+ assertEquals(345L, setLongReturnArg2(s, 222L, 123));
+ // Not replaced IPUT pattern.
+ assertEquals(222L, staticSetLong(s, 222L));
+ assertEquals(333L, setLongThroughParam(s, 333L));
+ // SPUT.
+ assertEquals(-11.5f, setStaticFloat(s, -11.5f));
+
+ if (newObject() == null) {
+ throw new AssertionError("new Object() cannot be null.");
+ }
+ }
+
+ private static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new AssertionError("Wrong result: " + expected + " != " + actual);
+ }
+ }
+
+ private static void assertEquals(double expected, double actual) {
+ if (expected != actual) {
+ throw new AssertionError("Wrong result: " + expected + " != " + actual);
+ }
+ }
+
+ private static void assertEquals(Object expected, Object actual) {
+ if (expected != actual && (expected == null || !expected.equals(actual))) {
+ throw new AssertionError("Wrong result: " + expected + " != " + actual);
+ }
+ }
+}
diff --git a/test/972-default-imt-collision/expected.txt b/test/972-default-imt-collision/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/972-default-imt-collision/expected.txt
diff --git a/test/972-default-imt-collision/info.txt b/test/972-default-imt-collision/info.txt
new file mode 100644
index 0000000..adecee3
--- /dev/null
+++ b/test/972-default-imt-collision/info.txt
@@ -0,0 +1 @@
+Test for interaction of miranda and non-miranda methods on interface dispatch.
diff --git a/test/972-default-imt-collision/smali/Iface1.smali b/test/972-default-imt-collision/smali/Iface1.smali
new file mode 100644
index 0000000..088c29c
--- /dev/null
+++ b/test/972-default-imt-collision/smali/Iface1.smali
@@ -0,0 +1,32 @@
+# /*
+# * Copyright (C) 2016 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+#
+# public interface Iface1 {
+# public default void sayHi() {
+# System.out.println("FAILED: We should never invoke this method!");
+# }
+# }
+
+.class public abstract interface LIface1;
+.super Ljava/lang/Object;
+
+.method public sayHi()V
+ .locals 2
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v0, "FAILED: We should never invoke this method!"
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
diff --git a/test/972-default-imt-collision/smali/Iface2.smali b/test/972-default-imt-collision/smali/Iface2.smali
new file mode 100644
index 0000000..4320e7e
--- /dev/null
+++ b/test/972-default-imt-collision/smali/Iface2.smali
@@ -0,0 +1,277 @@
+# /*
+# * Copyright (C) 2016 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+#
+# public interface Iface2 {
+# public void notImplementedMethod0();
+# public void notImplementedMethod1();
+# public void notImplementedMethod2();
+# public void notImplementedMethod3();
+# public void notImplementedMethod4();
+# public void notImplementedMethod5();
+# public void notImplementedMethod6();
+# public void notImplementedMethod7();
+# public void notImplementedMethod8();
+# public void notImplementedMethod9();
+# public void notImplementedMethod10();
+# public void notImplementedMethod11();
+# public void notImplementedMethod12();
+# public void notImplementedMethod13();
+# public void notImplementedMethod14();
+# public void notImplementedMethod15();
+# public void notImplementedMethod16();
+# public void notImplementedMethod17();
+# public void notImplementedMethod18();
+# public void notImplementedMethod19();
+# public void notImplementedMethod20();
+# public void notImplementedMethod21();
+# public void notImplementedMethod22();
+# public void notImplementedMethod23();
+# public void notImplementedMethod24();
+# public void notImplementedMethod25();
+# public void notImplementedMethod26();
+# public void notImplementedMethod27();
+# public void notImplementedMethod28();
+# public void notImplementedMethod29();
+# public void notImplementedMethod30();
+# public void notImplementedMethod31();
+# public void notImplementedMethod32();
+# public void notImplementedMethod33();
+# public void notImplementedMethod34();
+# public void notImplementedMethod35();
+# public void notImplementedMethod36();
+# public void notImplementedMethod37();
+# public void notImplementedMethod38();
+# public void notImplementedMethod39();
+# public void notImplementedMethod40();
+# public void notImplementedMethod41();
+# public void notImplementedMethod42();
+# public void notImplementedMethod43();
+# public void notImplementedMethod44();
+# public void notImplementedMethod45();
+# public void notImplementedMethod46();
+# public void notImplementedMethod47();
+# public void notImplementedMethod48();
+# public void notImplementedMethod49();
+# public void notImplementedMethod50();
+# public void notImplementedMethod51();
+# public void notImplementedMethod52();
+# public void notImplementedMethod53();
+# public void notImplementedMethod54();
+# public void notImplementedMethod55();
+# public void notImplementedMethod56();
+# public void notImplementedMethod57();
+# public void notImplementedMethod58();
+# public void notImplementedMethod59();
+# public void notImplementedMethod60();
+# public void notImplementedMethod61();
+# public void notImplementedMethod62();
+# public void notImplementedMethod63();
+# }
+
+.class public abstract interface LIface2;
+.super Ljava/lang/Object;
+
+.method public abstract notImplementedMethod0()V
+.end method
+
+.method public abstract notImplementedMethod1()V
+.end method
+
+.method public abstract notImplementedMethod2()V
+.end method
+
+.method public abstract notImplementedMethod3()V
+.end method
+
+.method public abstract notImplementedMethod4()V
+.end method
+
+.method public abstract notImplementedMethod5()V
+.end method
+
+.method public abstract notImplementedMethod6()V
+.end method
+
+.method public abstract notImplementedMethod7()V
+.end method
+
+.method public abstract notImplementedMethod8()V
+.end method
+
+.method public abstract notImplementedMethod9()V
+.end method
+
+.method public abstract notImplementedMethod10()V
+.end method
+
+.method public abstract notImplementedMethod11()V
+.end method
+
+.method public abstract notImplementedMethod12()V
+.end method
+
+.method public abstract notImplementedMethod13()V
+.end method
+
+.method public abstract notImplementedMethod14()V
+.end method
+
+.method public abstract notImplementedMethod15()V
+.end method
+
+.method public abstract notImplementedMethod16()V
+.end method
+
+.method public abstract notImplementedMethod17()V
+.end method
+
+.method public abstract notImplementedMethod18()V
+.end method
+
+.method public abstract notImplementedMethod19()V
+.end method
+
+.method public abstract notImplementedMethod20()V
+.end method
+
+.method public abstract notImplementedMethod21()V
+.end method
+
+.method public abstract notImplementedMethod22()V
+.end method
+
+.method public abstract notImplementedMethod23()V
+.end method
+
+.method public abstract notImplementedMethod24()V
+.end method
+
+.method public abstract notImplementedMethod25()V
+.end method
+
+.method public abstract notImplementedMethod26()V
+.end method
+
+.method public abstract notImplementedMethod27()V
+.end method
+
+.method public abstract notImplementedMethod28()V
+.end method
+
+.method public abstract notImplementedMethod29()V
+.end method
+
+.method public abstract notImplementedMethod30()V
+.end method
+
+.method public abstract notImplementedMethod31()V
+.end method
+
+.method public abstract notImplementedMethod32()V
+.end method
+
+.method public abstract notImplementedMethod33()V
+.end method
+
+.method public abstract notImplementedMethod34()V
+.end method
+
+.method public abstract notImplementedMethod35()V
+.end method
+
+.method public abstract notImplementedMethod36()V
+.end method
+
+.method public abstract notImplementedMethod37()V
+.end method
+
+.method public abstract notImplementedMethod38()V
+.end method
+
+.method public abstract notImplementedMethod39()V
+.end method
+
+.method public abstract notImplementedMethod40()V
+.end method
+
+.method public abstract notImplementedMethod41()V
+.end method
+
+.method public abstract notImplementedMethod42()V
+.end method
+
+.method public abstract notImplementedMethod43()V
+.end method
+
+.method public abstract notImplementedMethod44()V
+.end method
+
+.method public abstract notImplementedMethod45()V
+.end method
+
+.method public abstract notImplementedMethod46()V
+.end method
+
+.method public abstract notImplementedMethod47()V
+.end method
+
+.method public abstract notImplementedMethod48()V
+.end method
+
+.method public abstract notImplementedMethod49()V
+.end method
+
+.method public abstract notImplementedMethod50()V
+.end method
+
+.method public abstract notImplementedMethod51()V
+.end method
+
+.method public abstract notImplementedMethod52()V
+.end method
+
+.method public abstract notImplementedMethod53()V
+.end method
+
+.method public abstract notImplementedMethod54()V
+.end method
+
+.method public abstract notImplementedMethod55()V
+.end method
+
+.method public abstract notImplementedMethod56()V
+.end method
+
+.method public abstract notImplementedMethod57()V
+.end method
+
+.method public abstract notImplementedMethod58()V
+.end method
+
+.method public abstract notImplementedMethod59()V
+.end method
+
+.method public abstract notImplementedMethod60()V
+.end method
+
+.method public abstract notImplementedMethod61()V
+.end method
+
+.method public abstract notImplementedMethod62()V
+.end method
+
+.method public abstract notImplementedMethod63()V
+.end method
diff --git a/test/972-default-imt-collision/smali/Klass.smali b/test/972-default-imt-collision/smali/Klass.smali
new file mode 100644
index 0000000..a98f9a7
--- /dev/null
+++ b/test/972-default-imt-collision/smali/Klass.smali
@@ -0,0 +1,993 @@
+# /*
+# * Copyright (C) 2016 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+# public class Klass implements Iface1, Iface2 {
+# public static void testMe(Iface2 me) {
+# try {
+# me.notImplementedMethod0();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod0!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod1();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod1!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod2();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod2!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod3();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod3!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod4();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod4!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod5();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod5!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod6();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod6!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod7();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod7!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod8();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod8!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod9();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod9!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod10();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod10!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod11();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod11!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod12();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod12!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod13();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod13!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod14();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod14!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod15();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod15!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod16();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod16!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod17();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod17!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod18();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod18!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod19();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod19!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod20();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod20!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod21();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod21!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod22();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod22!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod23();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod23!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod24();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod24!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod25();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod25!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod26();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod26!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod27();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod27!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod28();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod28!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod29();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod29!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod30();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod30!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod31();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod31!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod32();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod32!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod33();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod33!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod34();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod34!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod35();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod35!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod36();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod36!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod37();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod37!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod38();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod38!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod39();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod39!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod40();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod40!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod41();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod41!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod42();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod42!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod43();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod43!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod44();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod44!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod45();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod45!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod46();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod46!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod47();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod47!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod48();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod48!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod49();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod49!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod50();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod50!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod51();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod51!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod52();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod52!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod53();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod53!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod54();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod54!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod55();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod55!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod56();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod56!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod57();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod57!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod58();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod58!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod59();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod59!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod60();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod60!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod61();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod61!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod62();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod62!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod63();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod63!");
+# } catch (AbstractMethodError e) { /* do nothing */ } }
+# }
+# }
+
+.class public LKlass;
+.super Ljava/lang/Object;
+.implements LIface1;
+.implements LIface2;
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public static testMe(LIface2;)V
+.locals 5
+ :try_0_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod0()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod0!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_0_end
+ :try_0_end
+ .catch Ljava/lang/AbstractMethodError; {:try_0_start .. :try_0_end} :catch_0_end
+ :catch_0_end
+
+
+ :try_1_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod1()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod1!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_1_end
+ :try_1_end
+ .catch Ljava/lang/AbstractMethodError; {:try_1_start .. :try_1_end} :catch_1_end
+ :catch_1_end
+
+
+ :try_2_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod2()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod2!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_2_end
+ :try_2_end
+ .catch Ljava/lang/AbstractMethodError; {:try_2_start .. :try_2_end} :catch_2_end
+ :catch_2_end
+
+
+ :try_3_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod3()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod3!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_3_end
+ :try_3_end
+ .catch Ljava/lang/AbstractMethodError; {:try_3_start .. :try_3_end} :catch_3_end
+ :catch_3_end
+
+
+ :try_4_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod4()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod4!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_4_end
+ :try_4_end
+ .catch Ljava/lang/AbstractMethodError; {:try_4_start .. :try_4_end} :catch_4_end
+ :catch_4_end
+
+
+ :try_5_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod5()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod5!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_5_end
+ :try_5_end
+ .catch Ljava/lang/AbstractMethodError; {:try_5_start .. :try_5_end} :catch_5_end
+ :catch_5_end
+
+
+ :try_6_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod6()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod6!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_6_end
+ :try_6_end
+ .catch Ljava/lang/AbstractMethodError; {:try_6_start .. :try_6_end} :catch_6_end
+ :catch_6_end
+
+
+ :try_7_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod7()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod7!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_7_end
+ :try_7_end
+ .catch Ljava/lang/AbstractMethodError; {:try_7_start .. :try_7_end} :catch_7_end
+ :catch_7_end
+
+
+ :try_8_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod8()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod8!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_8_end
+ :try_8_end
+ .catch Ljava/lang/AbstractMethodError; {:try_8_start .. :try_8_end} :catch_8_end
+ :catch_8_end
+
+
+ :try_9_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod9()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod9!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_9_end
+ :try_9_end
+ .catch Ljava/lang/AbstractMethodError; {:try_9_start .. :try_9_end} :catch_9_end
+ :catch_9_end
+
+
+ :try_10_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod10()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod10!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_10_end
+ :try_10_end
+ .catch Ljava/lang/AbstractMethodError; {:try_10_start .. :try_10_end} :catch_10_end
+ :catch_10_end
+
+
+ :try_11_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod11()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod11!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_11_end
+ :try_11_end
+ .catch Ljava/lang/AbstractMethodError; {:try_11_start .. :try_11_end} :catch_11_end
+ :catch_11_end
+
+
+ :try_12_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod12()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod12!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_12_end
+ :try_12_end
+ .catch Ljava/lang/AbstractMethodError; {:try_12_start .. :try_12_end} :catch_12_end
+ :catch_12_end
+
+
+ :try_13_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod13()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod13!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_13_end
+ :try_13_end
+ .catch Ljava/lang/AbstractMethodError; {:try_13_start .. :try_13_end} :catch_13_end
+ :catch_13_end
+
+
+ :try_14_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod14()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod14!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_14_end
+ :try_14_end
+ .catch Ljava/lang/AbstractMethodError; {:try_14_start .. :try_14_end} :catch_14_end
+ :catch_14_end
+
+
+ :try_15_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod15()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod15!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_15_end
+ :try_15_end
+ .catch Ljava/lang/AbstractMethodError; {:try_15_start .. :try_15_end} :catch_15_end
+ :catch_15_end
+
+
+ :try_16_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod16()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod16!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_16_end
+ :try_16_end
+ .catch Ljava/lang/AbstractMethodError; {:try_16_start .. :try_16_end} :catch_16_end
+ :catch_16_end
+
+
+ :try_17_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod17()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod17!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_17_end
+ :try_17_end
+ .catch Ljava/lang/AbstractMethodError; {:try_17_start .. :try_17_end} :catch_17_end
+ :catch_17_end
+
+
+ :try_18_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod18()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod18!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_18_end
+ :try_18_end
+ .catch Ljava/lang/AbstractMethodError; {:try_18_start .. :try_18_end} :catch_18_end
+ :catch_18_end
+
+
+ :try_19_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod19()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod19!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_19_end
+ :try_19_end
+ .catch Ljava/lang/AbstractMethodError; {:try_19_start .. :try_19_end} :catch_19_end
+ :catch_19_end
+
+
+ :try_20_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod20()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod20!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_20_end
+ :try_20_end
+ .catch Ljava/lang/AbstractMethodError; {:try_20_start .. :try_20_end} :catch_20_end
+ :catch_20_end
+
+
+ :try_21_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod21()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod21!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_21_end
+ :try_21_end
+ .catch Ljava/lang/AbstractMethodError; {:try_21_start .. :try_21_end} :catch_21_end
+ :catch_21_end
+
+
+ :try_22_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod22()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod22!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_22_end
+ :try_22_end
+ .catch Ljava/lang/AbstractMethodError; {:try_22_start .. :try_22_end} :catch_22_end
+ :catch_22_end
+
+
+ :try_23_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod23()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod23!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_23_end
+ :try_23_end
+ .catch Ljava/lang/AbstractMethodError; {:try_23_start .. :try_23_end} :catch_23_end
+ :catch_23_end
+
+
+ :try_24_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod24()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod24!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_24_end
+ :try_24_end
+ .catch Ljava/lang/AbstractMethodError; {:try_24_start .. :try_24_end} :catch_24_end
+ :catch_24_end
+
+
+ :try_25_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod25()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod25!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_25_end
+ :try_25_end
+ .catch Ljava/lang/AbstractMethodError; {:try_25_start .. :try_25_end} :catch_25_end
+ :catch_25_end
+
+
+ :try_26_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod26()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod26!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_26_end
+ :try_26_end
+ .catch Ljava/lang/AbstractMethodError; {:try_26_start .. :try_26_end} :catch_26_end
+ :catch_26_end
+
+
+ :try_27_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod27()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod27!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_27_end
+ :try_27_end
+ .catch Ljava/lang/AbstractMethodError; {:try_27_start .. :try_27_end} :catch_27_end
+ :catch_27_end
+
+
+ :try_28_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod28()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod28!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_28_end
+ :try_28_end
+ .catch Ljava/lang/AbstractMethodError; {:try_28_start .. :try_28_end} :catch_28_end
+ :catch_28_end
+
+
+ :try_29_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod29()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod29!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_29_end
+ :try_29_end
+ .catch Ljava/lang/AbstractMethodError; {:try_29_start .. :try_29_end} :catch_29_end
+ :catch_29_end
+
+
+ :try_30_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod30()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod30!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_30_end
+ :try_30_end
+ .catch Ljava/lang/AbstractMethodError; {:try_30_start .. :try_30_end} :catch_30_end
+ :catch_30_end
+
+
+ :try_31_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod31()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod31!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_31_end
+ :try_31_end
+ .catch Ljava/lang/AbstractMethodError; {:try_31_start .. :try_31_end} :catch_31_end
+ :catch_31_end
+
+
+ :try_32_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod32()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod32!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_32_end
+ :try_32_end
+ .catch Ljava/lang/AbstractMethodError; {:try_32_start .. :try_32_end} :catch_32_end
+ :catch_32_end
+
+
+ :try_33_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod33()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod33!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_33_end
+ :try_33_end
+ .catch Ljava/lang/AbstractMethodError; {:try_33_start .. :try_33_end} :catch_33_end
+ :catch_33_end
+
+
+ :try_34_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod34()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod34!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_34_end
+ :try_34_end
+ .catch Ljava/lang/AbstractMethodError; {:try_34_start .. :try_34_end} :catch_34_end
+ :catch_34_end
+
+
+ :try_35_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod35()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod35!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_35_end
+ :try_35_end
+ .catch Ljava/lang/AbstractMethodError; {:try_35_start .. :try_35_end} :catch_35_end
+ :catch_35_end
+
+
+ :try_36_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod36()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod36!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_36_end
+ :try_36_end
+ .catch Ljava/lang/AbstractMethodError; {:try_36_start .. :try_36_end} :catch_36_end
+ :catch_36_end
+
+
+ :try_37_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod37()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod37!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_37_end
+ :try_37_end
+ .catch Ljava/lang/AbstractMethodError; {:try_37_start .. :try_37_end} :catch_37_end
+ :catch_37_end
+
+
+ :try_38_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod38()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod38!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_38_end
+ :try_38_end
+ .catch Ljava/lang/AbstractMethodError; {:try_38_start .. :try_38_end} :catch_38_end
+ :catch_38_end
+
+
+ :try_39_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod39()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod39!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_39_end
+ :try_39_end
+ .catch Ljava/lang/AbstractMethodError; {:try_39_start .. :try_39_end} :catch_39_end
+ :catch_39_end
+
+
+ :try_40_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod40()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod40!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_40_end
+ :try_40_end
+ .catch Ljava/lang/AbstractMethodError; {:try_40_start .. :try_40_end} :catch_40_end
+ :catch_40_end
+
+
+ :try_41_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod41()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod41!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_41_end
+ :try_41_end
+ .catch Ljava/lang/AbstractMethodError; {:try_41_start .. :try_41_end} :catch_41_end
+ :catch_41_end
+
+
+ :try_42_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod42()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod42!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_42_end
+ :try_42_end
+ .catch Ljava/lang/AbstractMethodError; {:try_42_start .. :try_42_end} :catch_42_end
+ :catch_42_end
+
+
+ :try_43_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod43()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod43!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_43_end
+ :try_43_end
+ .catch Ljava/lang/AbstractMethodError; {:try_43_start .. :try_43_end} :catch_43_end
+ :catch_43_end
+
+
+ :try_44_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod44()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod44!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_44_end
+ :try_44_end
+ .catch Ljava/lang/AbstractMethodError; {:try_44_start .. :try_44_end} :catch_44_end
+ :catch_44_end
+
+
+ :try_45_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod45()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod45!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_45_end
+ :try_45_end
+ .catch Ljava/lang/AbstractMethodError; {:try_45_start .. :try_45_end} :catch_45_end
+ :catch_45_end
+
+
+ :try_46_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod46()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod46!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_46_end
+ :try_46_end
+ .catch Ljava/lang/AbstractMethodError; {:try_46_start .. :try_46_end} :catch_46_end
+ :catch_46_end
+
+
+ :try_47_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod47()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod47!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_47_end
+ :try_47_end
+ .catch Ljava/lang/AbstractMethodError; {:try_47_start .. :try_47_end} :catch_47_end
+ :catch_47_end
+
+
+ :try_48_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod48()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod48!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_48_end
+ :try_48_end
+ .catch Ljava/lang/AbstractMethodError; {:try_48_start .. :try_48_end} :catch_48_end
+ :catch_48_end
+
+
+ :try_49_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod49()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod49!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_49_end
+ :try_49_end
+ .catch Ljava/lang/AbstractMethodError; {:try_49_start .. :try_49_end} :catch_49_end
+ :catch_49_end
+
+
+ :try_50_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod50()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod50!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_50_end
+ :try_50_end
+ .catch Ljava/lang/AbstractMethodError; {:try_50_start .. :try_50_end} :catch_50_end
+ :catch_50_end
+
+
+ :try_51_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod51()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod51!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_51_end
+ :try_51_end
+ .catch Ljava/lang/AbstractMethodError; {:try_51_start .. :try_51_end} :catch_51_end
+ :catch_51_end
+
+
+ :try_52_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod52()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod52!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_52_end
+ :try_52_end
+ .catch Ljava/lang/AbstractMethodError; {:try_52_start .. :try_52_end} :catch_52_end
+ :catch_52_end
+
+
+ :try_53_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod53()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod53!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_53_end
+ :try_53_end
+ .catch Ljava/lang/AbstractMethodError; {:try_53_start .. :try_53_end} :catch_53_end
+ :catch_53_end
+
+
+ :try_54_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod54()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod54!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_54_end
+ :try_54_end
+ .catch Ljava/lang/AbstractMethodError; {:try_54_start .. :try_54_end} :catch_54_end
+ :catch_54_end
+
+
+ :try_55_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod55()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod55!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_55_end
+ :try_55_end
+ .catch Ljava/lang/AbstractMethodError; {:try_55_start .. :try_55_end} :catch_55_end
+ :catch_55_end
+
+
+ :try_56_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod56()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod56!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_56_end
+ :try_56_end
+ .catch Ljava/lang/AbstractMethodError; {:try_56_start .. :try_56_end} :catch_56_end
+ :catch_56_end
+
+
+ :try_57_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod57()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod57!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_57_end
+ :try_57_end
+ .catch Ljava/lang/AbstractMethodError; {:try_57_start .. :try_57_end} :catch_57_end
+ :catch_57_end
+
+
+ :try_58_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod58()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod58!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_58_end
+ :try_58_end
+ .catch Ljava/lang/AbstractMethodError; {:try_58_start .. :try_58_end} :catch_58_end
+ :catch_58_end
+
+
+ :try_59_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod59()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod59!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_59_end
+ :try_59_end
+ .catch Ljava/lang/AbstractMethodError; {:try_59_start .. :try_59_end} :catch_59_end
+ :catch_59_end
+
+
+ :try_60_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod60()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod60!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_60_end
+ :try_60_end
+ .catch Ljava/lang/AbstractMethodError; {:try_60_start .. :try_60_end} :catch_60_end
+ :catch_60_end
+
+
+ :try_61_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod61()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod61!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_61_end
+ :try_61_end
+ .catch Ljava/lang/AbstractMethodError; {:try_61_start .. :try_61_end} :catch_61_end
+ :catch_61_end
+
+
+ :try_62_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod62()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod62!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_62_end
+ :try_62_end
+ .catch Ljava/lang/AbstractMethodError; {:try_62_start .. :try_62_end} :catch_62_end
+ :catch_62_end
+
+
+ :try_63_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod63()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod63!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_63_end
+ :try_63_end
+ .catch Ljava/lang/AbstractMethodError; {:try_63_start .. :try_63_end} :catch_63_end
+ :catch_63_end
+ return-void
+.end method
diff --git a/test/972-default-imt-collision/src/Main.java b/test/972-default-imt-collision/src/Main.java
new file mode 100644
index 0000000..6819e43
--- /dev/null
+++ b/test/972-default-imt-collision/src/Main.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import java.lang.reflect.*;
+
+public class Main {
+ public static void main(String[] args) {
+ try {
+ Class<?> c = Class.forName("Klass");
+ Object o = c.newInstance();
+ Class<?> iface = Class.forName("Iface2");
+ Method test = c.getMethod("testMe", iface);
+ test.invoke(null, o);
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.out.println("FAILED: could not run testMe!");
+ }
+ }
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index b922b45..faaf1f0 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -39,7 +39,8 @@
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 \
- 543-env-long-ref/env_long_ref.cc
+ 543-env-long-ref/env_long_ref.cc \
+ 566-polymorphic-inlining/polymorphic_inline.cc
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttestd.so
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 429af6a..d6c2983 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -526,11 +526,7 @@
TEST_ART_BROKEN_OPTIMIZING_DEBUGGABLE_RUN_TESTS :=
# Tests that should fail in the read barrier configuration with the interpreter.
-# 004: Occasional timeout: "TEST TIMED OUT!" (b/26786154).
-# 141: Occasional failures: "Aborted" (b/25866001).
-TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS := \
- 004-ThreadStress \
- 141-class-unload
+TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS :=
# Tests that should fail in the read barrier configuration with the default (Quick) compiler (AOT).
# Quick has no support for read barriers and punts to the interpreter, so this list is composed of
@@ -540,7 +536,6 @@
$(TEST_ART_BROKEN_INTERPRETER_RUN_TESTS)
# Tests that should fail in the read barrier configuration with the Optimizing compiler (AOT).
-# 004: Occasional timeout: "TEST TIMED OUT!" (b/26786154).
# 484: Baker's fast path based read barrier compiler instrumentation generates code containing
# more parallel moves on x86, thus some Checker assertions may fail.
# 527: On ARM64, the read barrier instrumentation does not support the HArm64IntermediateAddress
@@ -548,18 +543,13 @@
# 537: Expects an array copy to be intrinsified on x86-64, but calling-on-slowpath intrinsics are
# not yet handled in the read barrier configuration.
TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := \
- 004-ThreadStress \
484-checker-register-hints \
527-checker-array-access-split \
537-checker-arraycopy
# Tests that should fail in the read barrier configuration with JIT.
-# 004: Occasional timeout: "TEST TIMED OUT!" (b/26786154).
-# 141: Disabled because of intermittent failures on the ART Builtbot (b/25866001).
# 496: Occasional timeout: "Fault message: timeout: the monitored command dumped core" (b/26786304).
TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS := \
- 004-ThreadStress \
- 141-class-unload \
496-checker-inlining-and-class-loader
ifeq ($(ART_USE_READ_BARRIER),true)