summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/Android.mk2
-rw-r--r--compiler/debug/elf_debug_loc_writer.h3
-rw-r--r--compiler/dex/compiler_enums.h1
-rw-r--r--compiler/dex/quick/dex_file_method_inliner.cc11
-rw-r--r--compiler/dex/quick/dex_file_method_inliner.h2
-rw-r--r--compiler/image_test.cc5
-rw-r--r--compiler/image_writer.cc23
-rw-r--r--compiler/optimizing/builder.cc8
-rw-r--r--compiler/optimizing/code_generator_arm.cc29
-rw-r--r--compiler/optimizing/code_generator_arm.h2
-rw-r--r--compiler/optimizing/code_generator_arm64.cc65
-rw-r--r--compiler/optimizing/code_generator_arm64.h2
-rw-r--r--compiler/optimizing/code_generator_mips.cc2
-rw-r--r--compiler/optimizing/code_generator_x86.cc2
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc2
-rw-r--r--compiler/optimizing/graph_visualizer.cc14
-rw-r--r--compiler/optimizing/induction_var_analysis.cc8
-rw-r--r--compiler/optimizing/induction_var_range.cc8
-rw-r--r--compiler/optimizing/inliner.cc3
-rw-r--r--compiler/optimizing/instruction_simplifier.cc84
-rw-r--r--compiler/optimizing/instruction_simplifier_arm.cc30
-rw-r--r--compiler/optimizing/instruction_simplifier_arm.h58
-rw-r--r--compiler/optimizing/instruction_simplifier_arm64.cc174
-rw-r--r--compiler/optimizing/instruction_simplifier_arm64.h9
-rw-r--r--compiler/optimizing/instruction_simplifier_shared.cc189
-rw-r--r--compiler/optimizing/instruction_simplifier_shared.h28
-rw-r--r--compiler/optimizing/intrinsics.cc4
-rw-r--r--compiler/optimizing/intrinsics_arm.cc66
-rw-r--r--compiler/optimizing/intrinsics_arm64.cc66
-rw-r--r--compiler/optimizing/intrinsics_list.h2
-rw-r--r--compiler/optimizing/intrinsics_mips.cc2
-rw-r--r--compiler/optimizing/intrinsics_mips64.cc2
-rw-r--r--compiler/optimizing/intrinsics_x86.cc2
-rw-r--r--compiler/optimizing/intrinsics_x86_64.cc2
-rw-r--r--compiler/optimizing/nodes.cc11
-rw-r--r--compiler/optimizing/nodes.h642
-rw-r--r--compiler/optimizing/nodes_arm64.h68
-rw-r--r--compiler/optimizing/nodes_shared.h58
-rw-r--r--compiler/optimizing/optimizing_compiler.cc4
-rw-r--r--compiler/optimizing/stack_map_stream.cc98
-rw-r--r--compiler/optimizing/stack_map_stream.h7
-rw-r--r--dex2oat/dex2oat.cc9
-rw-r--r--runtime/base/bit_field.h11
-rw-r--r--runtime/base/bit_vector.h5
-rw-r--r--runtime/check_reference_map_visitor.h3
-rw-r--r--runtime/class_linker.cc17
-rw-r--r--runtime/class_linker.h6
-rw-r--r--runtime/debugger.cc32
-rw-r--r--runtime/gc/heap.cc9
-rw-r--r--runtime/gc/heap.h3
-rw-r--r--runtime/gc/space/image_space.cc11
-rw-r--r--runtime/image.h1
-rw-r--r--runtime/instrumentation.cc23
-rw-r--r--runtime/instrumentation.h5
-rw-r--r--runtime/interpreter/interpreter_goto_table_impl.cc12
-rw-r--r--runtime/interpreter/interpreter_switch_impl.cc12
-rw-r--r--runtime/interpreter/mterp/arm/bincmp.S25
-rw-r--r--runtime/interpreter/mterp/arm/zcmp.S23
-rw-r--r--runtime/interpreter/mterp/arm64/bincmp.S29
-rw-r--r--runtime/interpreter/mterp/arm64/zcmp.S27
-rw-r--r--runtime/interpreter/mterp/out/mterp_arm.S288
-rw-r--r--runtime/interpreter/mterp/out/mterp_arm64.S336
-rw-r--r--runtime/interpreter/mterp/out/mterp_x86.S90
-rw-r--r--runtime/interpreter/mterp/x86/footer.S6
-rw-r--r--runtime/interpreter/mterp/x86/invoke.S7
-rw-r--r--runtime/jit/jit.cc6
-rw-r--r--runtime/jit/jit_code_cache.cc11
-rw-r--r--runtime/jit/jit_code_cache.h2
-rw-r--r--runtime/oat_file.h4
-rw-r--r--runtime/oat_file_manager.cc19
-rw-r--r--runtime/primitive.h1
-rw-r--r--runtime/quick/inline_method_analyser.h2
-rw-r--r--runtime/quick_exception_handler.cc14
-rw-r--r--runtime/stack.cc11
-rw-r--r--runtime/stack_map.cc27
-rw-r--r--runtime/stack_map.h37
-rwxr-xr-xtest/004-JniTest/build28
-rw-r--r--test/004-JniTest/expected.txt3
-rw-r--r--test/004-JniTest/jni_test.cc19
-rw-r--r--test/004-JniTest/src/Main.java17
-rw-r--r--test/020-string/expected.txt6
-rw-r--r--test/020-string/src/Main.java45
-rw-r--r--test/082-inline-execute/src/Main.java2
-rw-r--r--test/449-checker-bce/src/Main.java74
-rw-r--r--test/530-checker-loops/src/Main.java992
-rw-r--r--test/530-checker-loops2/expected.txt0
-rw-r--r--test/530-checker-loops2/info.txt1
-rw-r--r--test/530-checker-loops2/src/Main.java999
-rw-r--r--test/550-checker-multiply-accumulate/src/Main.java215
-rw-r--r--test/564-checker-negbitwise/expected.txt0
-rw-r--r--test/564-checker-negbitwise/info.txt1
-rw-r--r--test/564-checker-negbitwise/src/Main.java207
-rw-r--r--test/577-checker-fp2int/expected.txt1
-rw-r--r--test/577-checker-fp2int/info.txt1
-rw-r--r--test/577-checker-fp2int/src/Main.java122
-rw-r--r--test/Android.run-test.mk12
-rw-r--r--tools/libcore_failures.txt7
97 files changed, 3564 insertions, 2110 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 2cbafea573..7a257b649f 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -142,7 +142,9 @@ LIBART_COMPILER_SRC_FILES_arm64 := \
jni/quick/arm64/calling_convention_arm64.cc \
linker/arm64/relative_patcher_arm64.cc \
optimizing/code_generator_arm64.cc \
+ optimizing/instruction_simplifier_arm.cc \
optimizing/instruction_simplifier_arm64.cc \
+ optimizing/instruction_simplifier_shared.cc \
optimizing/intrinsics_arm64.cc \
utils/arm64/assembler_arm64.cc \
utils/arm64/managed_register_arm64.cc \
diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h
index 8fd20aa428..32f624acd3 100644
--- a/compiler/debug/elf_debug_loc_writer.h
+++ b/compiler/debug/elf_debug_loc_writer.h
@@ -232,8 +232,7 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info,
// kInStackLargeOffset and kConstantLargeValue are hidden by GetKind().
// kInRegisterHigh and kInFpuRegisterHigh should be handled by
// the special cases above and they should not occur alone.
- LOG(ERROR) << "Unexpected register location kind: "
- << DexRegisterLocation::PrettyDescriptor(kind);
+ LOG(ERROR) << "Unexpected register location kind: " << kind;
break;
}
if (is64bitValue) {
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
index b78b3d7d75..8800e4b08f 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -569,6 +569,7 @@ enum MemBarrierKind {
kStoreStore,
kAnyAny,
kNTStoreStore,
+ kLastBarrierKind = kNTStoreStore
};
std::ostream& operator<<(std::ostream& os, const MemBarrierKind& kind);
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index 209f101199..ad4ddadd2f 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -37,6 +37,8 @@ namespace { // anonymous namespace
static constexpr bool kIntrinsicIsStatic[] = {
true, // kIntrinsicDoubleCvt
true, // kIntrinsicFloatCvt
+ true, // kIntrinsicFloat2Int
+ true, // kIntrinsicDouble2Long
true, // kIntrinsicFloatIsInfinite
true, // kIntrinsicDoubleIsInfinite
true, // kIntrinsicFloatIsNaN
@@ -106,6 +108,8 @@ static_assert(arraysize(kIntrinsicIsStatic) == kInlineOpNop,
"arraysize of kIntrinsicIsStatic unexpected");
static_assert(kIntrinsicIsStatic[kIntrinsicDoubleCvt], "DoubleCvt must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicFloatCvt], "FloatCvt must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicFloat2Int], "Float2Int must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicDouble2Long], "Double2Long must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicFloatIsInfinite], "FloatIsInfinite must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicDoubleIsInfinite], "DoubleIsInfinite must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicFloatIsNaN], "FloatIsNaN must be static");
@@ -277,6 +281,8 @@ const char* const DexFileMethodInliner::kNameCacheNames[] = {
"equals", // kNameCacheEquals
"getCharsNoCheck", // kNameCacheGetCharsNoCheck
"isEmpty", // kNameCacheIsEmpty
+ "floatToIntBits", // kNameCacheFloatToIntBits
+ "doubleToLongBits", // kNameCacheDoubleToLongBits
"isInfinite", // kNameCacheIsInfinite
"isNaN", // kNameCacheIsNaN
"indexOf", // kNameCacheIndexOf
@@ -472,6 +478,9 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods
INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0),
INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, kIntrinsicFlagToFloatingPoint),
+ INTRINSIC(JavaLangFloat, FloatToIntBits, F_I, kIntrinsicFloat2Int, 0),
+ INTRINSIC(JavaLangDouble, DoubleToLongBits, D_J, kIntrinsicDouble2Long, 0),
+
INTRINSIC(JavaLangFloat, IsInfinite, F_Z, kIntrinsicFloatIsInfinite, 0),
INTRINSIC(JavaLangDouble, IsInfinite, D_Z, kIntrinsicDoubleIsInfinite, 0),
INTRINSIC(JavaLangFloat, IsNaN, F_Z, kIntrinsicFloatIsNaN, 0),
@@ -791,6 +800,8 @@ bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) {
intrinsic.d.data & kIntrinsicFlagIsOrdered);
case kIntrinsicSystemArrayCopyCharArray:
return backend->GenInlinedArrayCopyCharArray(info);
+ case kIntrinsicFloat2Int:
+ case kIntrinsicDouble2Long:
case kIntrinsicFloatIsInfinite:
case kIntrinsicDoubleIsInfinite:
case kIntrinsicFloatIsNaN:
diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h
index 59b8a533ae..b465db2c54 100644
--- a/compiler/dex/quick/dex_file_method_inliner.h
+++ b/compiler/dex/quick/dex_file_method_inliner.h
@@ -190,6 +190,8 @@ class DexFileMethodInliner {
kNameCacheEquals,
kNameCacheGetCharsNoCheck,
kNameCacheIsEmpty,
+ kNameCacheFloatToIntBits,
+ kNameCacheDoubleToLongBits,
kNameCacheIsInfinite,
kNameCacheIsNaN,
kNameCacheIndexOf,
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 992af29545..5763cec43f 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -289,6 +289,11 @@ TEST_F(ImageTest, WriteReadLZ4) {
TestWriteRead(ImageHeader::kStorageModeLZ4);
}
+TEST_F(ImageTest, WriteReadLZ4HC) {
+ TestWriteRead(ImageHeader::kStorageModeLZ4HC);
+}
+
+
TEST_F(ImageTest, ImageHeaderIsValid) {
uint32_t image_begin = ART_BASE_ADDRESS;
uint32_t image_size_ = 16 * KB;
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 5eff8f37ec..871435b85f 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -18,6 +18,7 @@
#include <sys/stat.h>
#include <lz4.h>
+#include <lz4hc.h>
#include <memory>
#include <numeric>
@@ -224,18 +225,28 @@ bool ImageWriter::Write(int image_fd,
char* image_data = reinterpret_cast<char*>(image_info.image_->Begin()) + sizeof(ImageHeader);
size_t data_size;
const char* image_data_to_write;
+ const uint64_t compress_start_time = NanoTime();
CHECK_EQ(image_header->storage_mode_, image_storage_mode_);
switch (image_storage_mode_) {
case ImageHeader::kStorageModeLZ4: {
- size_t compressed_max_size = LZ4_compressBound(image_data_size);
+ const size_t compressed_max_size = LZ4_compressBound(image_data_size);
compressed_data.reset(new char[compressed_max_size]);
data_size = LZ4_compress(
reinterpret_cast<char*>(image_info.image_->Begin()) + sizeof(ImageHeader),
&compressed_data[0],
image_data_size);
- image_data_to_write = &compressed_data[0];
- VLOG(compiler) << "Compressed from " << image_data_size << " to " << data_size;
+
+ break;
+ }
+ case ImageHeader::kStorageModeLZ4HC: {
+ // Bound is same as non HC.
+ const size_t compressed_max_size = LZ4_compressBound(image_data_size);
+ compressed_data.reset(new char[compressed_max_size]);
+ data_size = LZ4_compressHC(
+ reinterpret_cast<char*>(image_info.image_->Begin()) + sizeof(ImageHeader),
+ &compressed_data[0],
+ image_data_size);
break;
}
case ImageHeader::kStorageModeUncompressed: {
@@ -249,6 +260,12 @@ bool ImageWriter::Write(int image_fd,
}
}
+ if (compressed_data != nullptr) {
+ image_data_to_write = &compressed_data[0];
+ VLOG(compiler) << "Compressed from " << image_data_size << " to " << data_size << " in "
+ << PrettyDuration(NanoTime() - compress_start_time);
+ }
+
// Write header first, as uncompressed.
image_header->data_size_ = data_size;
if (!image_file->WriteFully(image_info.image_->Begin(), sizeof(ImageHeader))) {
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 35ec7d41ff..57660c2623 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -282,7 +282,7 @@ void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item)
// Found a predecessor not covered by the same TryItem. Insert entering
// boundary block.
HTryBoundary* try_entry =
- new (arena_) HTryBoundary(HTryBoundary::kEntry, try_block->GetDexPc());
+ new (arena_) HTryBoundary(HTryBoundary::BoundaryKind::kEntry, try_block->GetDexPc());
try_block->CreateImmediateDominator()->AddInstruction(try_entry);
LinkToCatchBlocks(try_entry, code_item, entry.second);
break;
@@ -316,7 +316,7 @@ void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item)
// Insert TryBoundary and link to catch blocks.
HTryBoundary* try_exit =
- new (arena_) HTryBoundary(HTryBoundary::kExit, successor->GetDexPc());
+ new (arena_) HTryBoundary(HTryBoundary::BoundaryKind::kExit, successor->GetDexPc());
graph_->SplitEdge(try_block, successor)->AddInstruction(try_exit);
LinkToCatchBlocks(try_exit, code_item, entry.second);
}
@@ -2843,7 +2843,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32
case Instruction::MONITOR_ENTER: {
current_block_->AddInstruction(new (arena_) HMonitorOperation(
LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc),
- HMonitorOperation::kEnter,
+ HMonitorOperation::OperationKind::kEnter,
dex_pc));
break;
}
@@ -2851,7 +2851,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32
case Instruction::MONITOR_EXIT: {
current_block_->AddInstruction(new (arena_) HMonitorOperation(
LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc),
- HMonitorOperation::kExit,
+ HMonitorOperation::OperationKind::kExit,
dex_pc));
break;
}
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index cdbb9c31aa..aa9b01f30b 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -6413,6 +6413,33 @@ Literal* CodeGeneratorARM::DeduplicateMethodCodeLiteral(MethodReference target_m
return DeduplicateMethodLiteral(target_method, &call_patches_);
}
+void LocationsBuilderARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall);
+ locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
+ Location::RequiresRegister());
+ locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
+ locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
+ LocationSummary* locations = instr->GetLocations();
+ Register res = locations->Out().AsRegister<Register>();
+ Register accumulator =
+ locations->InAt(HMultiplyAccumulate::kInputAccumulatorIndex).AsRegister<Register>();
+ Register mul_left =
+ locations->InAt(HMultiplyAccumulate::kInputMulLeftIndex).AsRegister<Register>();
+ Register mul_right =
+ locations->InAt(HMultiplyAccumulate::kInputMulRightIndex).AsRegister<Register>();
+
+ if (instr->GetOpKind() == HInstruction::kAdd) {
+ __ mla(res, mul_left, mul_right, accumulator);
+ } else {
+ __ mls(res, mul_left, mul_right, accumulator);
+ }
+}
+
void LocationsBuilderARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
// Nothing to do, this should be removed during prepare for register allocator.
LOG(FATAL) << "Unreachable";
@@ -6567,7 +6594,7 @@ void LocationsBuilderARM::VisitClassTableGet(HClassTableGet* instruction) {
void InstructionCodeGeneratorARM::VisitClassTableGet(HClassTableGet* instruction) {
LocationSummary* locations = instruction->GetLocations();
uint32_t method_offset = 0;
- if (instruction->GetTableKind() == HClassTableGet::kVTable) {
+ if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
method_offset = mirror::Class::EmbeddedVTableEntryOffset(
instruction->GetIndex(), kArmPointerSize).SizeValue();
} else {
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 2e4dc1e014..06e7c0015c 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -159,6 +159,7 @@ class LocationsBuilderARM : public HGraphVisitor {
FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
@@ -197,6 +198,7 @@ class InstructionCodeGeneratorARM : public InstructionCodeGenerator {
FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 814f8b4d51..985dc056f6 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1862,6 +1862,36 @@ void InstructionCodeGeneratorARM64::VisitAnd(HAnd* instruction) {
HandleBinaryOp(instruction);
}
+void LocationsBuilderARM64::VisitArm64BitwiseNegatedRight(HArm64BitwiseNegatedRight* instr) {
+ DCHECK(Primitive::IsIntegralType(instr->GetType())) << instr->GetType();
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr);
+ locations->SetInAt(0, Location::RequiresRegister());
+ // There is no immediate variant of negated bitwise instructions in AArch64.
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARM64::VisitArm64BitwiseNegatedRight(
+ HArm64BitwiseNegatedRight* instr) {
+ Register dst = OutputRegister(instr);
+ Register lhs = InputRegisterAt(instr, 0);
+ Register rhs = InputRegisterAt(instr, 1);
+
+ switch (instr->GetOpKind()) {
+ case HInstruction::kAnd:
+ __ Bic(dst, lhs, rhs);
+ break;
+ case HInstruction::kOr:
+ __ Orn(dst, lhs, rhs);
+ break;
+ case HInstruction::kXor:
+ __ Eon(dst, lhs, rhs);
+ break;
+ default:
+ LOG(FATAL) << "Unreachable";
+ }
+}
+
void LocationsBuilderARM64::VisitArm64DataProcWithShifterOp(
HArm64DataProcWithShifterOp* instruction) {
DCHECK(instruction->GetType() == Primitive::kPrimInt ||
@@ -1959,21 +1989,27 @@ void InstructionCodeGeneratorARM64::VisitArm64IntermediateAddress(
Operand(InputOperandAt(instruction, 1)));
}
-void LocationsBuilderARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instr) {
+void LocationsBuilderARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall);
- locations->SetInAt(HArm64MultiplyAccumulate::kInputAccumulatorIndex,
- Location::RequiresRegister());
- locations->SetInAt(HArm64MultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
- locations->SetInAt(HArm64MultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
+ HInstruction* accumulator = instr->InputAt(HMultiplyAccumulate::kInputAccumulatorIndex);
+ if (instr->GetOpKind() == HInstruction::kSub &&
+ accumulator->IsConstant() &&
+ accumulator->AsConstant()->IsZero()) {
+ // Don't allocate register for Mneg instruction.
+ } else {
+ locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
+ Location::RequiresRegister());
+ }
+ locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
+ locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
-void InstructionCodeGeneratorARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instr) {
+void InstructionCodeGeneratorARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
Register res = OutputRegister(instr);
- Register accumulator = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputAccumulatorIndex);
- Register mul_left = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputMulLeftIndex);
- Register mul_right = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputMulRightIndex);
+ Register mul_left = InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex);
+ Register mul_right = InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex);
// Avoid emitting code that could trigger Cortex A53's erratum 835769.
// This fixup should be carried out for all multiply-accumulate instructions:
@@ -1993,10 +2029,17 @@ void InstructionCodeGeneratorARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyA
}
if (instr->GetOpKind() == HInstruction::kAdd) {
+ Register accumulator = InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex);
__ Madd(res, mul_left, mul_right, accumulator);
} else {
DCHECK(instr->GetOpKind() == HInstruction::kSub);
- __ Msub(res, mul_left, mul_right, accumulator);
+ HInstruction* accum_instr = instr->InputAt(HMultiplyAccumulate::kInputAccumulatorIndex);
+ if (accum_instr->IsConstant() && accum_instr->AsConstant()->IsZero()) {
+ __ Mneg(res, mul_left, mul_right);
+ } else {
+ Register accumulator = InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex);
+ __ Msub(res, mul_left, mul_right, accumulator);
+ }
}
}
@@ -5017,7 +5060,7 @@ void LocationsBuilderARM64::VisitClassTableGet(HClassTableGet* instruction) {
void InstructionCodeGeneratorARM64::VisitClassTableGet(HClassTableGet* instruction) {
LocationSummary* locations = instruction->GetLocations();
uint32_t method_offset = 0;
- if (instruction->GetTableKind() == HClassTableGet::kVTable) {
+ if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
method_offset = mirror::Class::EmbeddedVTableEntryOffset(
instruction->GetIndex(), kArm64PointerSize).SizeValue();
} else {
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 3527261835..10f1e7f008 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -196,6 +196,7 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator {
FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
@@ -285,6 +286,7 @@ class LocationsBuilderARM64 : public HGraphVisitor {
FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 8d3d94b79d..f3c12efd8d 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -5245,7 +5245,7 @@ void LocationsBuilderMIPS::VisitClassTableGet(HClassTableGet* instruction) {
void InstructionCodeGeneratorMIPS::VisitClassTableGet(HClassTableGet* instruction) {
LocationSummary* locations = instruction->GetLocations();
uint32_t method_offset = 0;
- if (instruction->GetTableKind() == HClassTableGet::kVTable) {
+ if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
method_offset = mirror::Class::EmbeddedVTableEntryOffset(
instruction->GetIndex(), kMipsPointerSize).SizeValue();
} else {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 88e42f3faf..6b4a18c688 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -4127,7 +4127,7 @@ void LocationsBuilderX86::VisitClassTableGet(HClassTableGet* instruction) {
void InstructionCodeGeneratorX86::VisitClassTableGet(HClassTableGet* instruction) {
LocationSummary* locations = instruction->GetLocations();
uint32_t method_offset = 0;
- if (instruction->GetTableKind() == HClassTableGet::kVTable) {
+ if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
method_offset = mirror::Class::EmbeddedVTableEntryOffset(
instruction->GetIndex(), kX86PointerSize).SizeValue();
} else {
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index bb24c6f59c..c132663016 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -3998,7 +3998,7 @@ void LocationsBuilderX86_64::VisitClassTableGet(HClassTableGet* instruction) {
void InstructionCodeGeneratorX86_64::VisitClassTableGet(HClassTableGet* instruction) {
LocationSummary* locations = instruction->GetLocations();
uint32_t method_offset = 0;
- if (instruction->GetTableKind() == HClassTableGet::kVTable) {
+ if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
method_offset = mirror::Class::EmbeddedVTableEntryOffset(
instruction->GetIndex(), kX86_64PointerSize).SizeValue();
} else {
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index c0263e4e5b..b9638f2027 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -436,17 +436,23 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
StartAttributeStream("kind") << (try_boundary->IsEntry() ? "entry" : "exit");
}
+#if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64)
+ void VisitMultiplyAccumulate(HMultiplyAccumulate* instruction) OVERRIDE {
+ StartAttributeStream("kind") << instruction->GetOpKind();
+ }
+#endif
+
#ifdef ART_ENABLE_CODEGEN_arm64
+ void VisitArm64BitwiseNegatedRight(HArm64BitwiseNegatedRight* instruction) OVERRIDE {
+ StartAttributeStream("kind") << instruction->GetOpKind();
+ }
+
void VisitArm64DataProcWithShifterOp(HArm64DataProcWithShifterOp* instruction) OVERRIDE {
StartAttributeStream("kind") << instruction->GetInstrKind() << "+" << instruction->GetOpKind();
if (HArm64DataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())) {
StartAttributeStream("shift") << instruction->GetShiftAmount();
}
}
-
- void VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instruction) OVERRIDE {
- StartAttributeStream("kind") << instruction->GetOpKind();
- }
#endif
bool IsPass(const char* name) {
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index a1e1cde9df..82a898a9f1 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -552,9 +552,11 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop,
if (!IsExact(stride_expr, &stride_value)) {
return;
}
- // Rewrite condition i != U into i < U or i > U if end condition is reached exactly.
- if (cmp == kCondNE && ((stride_value == +1 && IsTaken(lower_expr, upper_expr, kCondLT)) ||
- (stride_value == -1 && IsTaken(lower_expr, upper_expr, kCondGT)))) {
+ // Rewrite condition i != U into strict end condition i < U or i > U if this end condition
+ // is reached exactly (tested by verifying if the loop has a unit stride and the non-strict
+ // condition would be always taken).
+ if (cmp == kCondNE && ((stride_value == +1 && IsTaken(lower_expr, upper_expr, kCondLE)) ||
+ (stride_value == -1 && IsTaken(lower_expr, upper_expr, kCondGE)))) {
cmp = stride_value > 0 ? kCondLT : kCondGT;
}
// Normalize a linear loop control with a nonzero stride:
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index b162696a42..f9b6910acd 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -216,6 +216,14 @@ bool InductionVarRange::IsConstant(HInductionVarAnalysis::InductionInfo* info,
}
}
} while (RefineOuter(&v_min, &v_max));
+ // Exploit array length + c >= c, with c <= 0 to avoid arithmetic wrap-around anomalies
+ // (e.g. array length == maxint and c == 1 would yield minint).
+ if (request == kAtLeast) {
+ if (v_min.a_constant == 1 && v_min.b_constant <= 0 && v_min.instruction->IsArrayLength()) {
+ *value = v_min.b_constant;
+ return true;
+ }
+ }
}
return false;
}
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 02a1acc240..d55009554f 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -651,7 +651,8 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction,
HClassTableGet* class_table_get = new (graph_->GetArena()) HClassTableGet(
receiver_class,
type,
- invoke_instruction->IsInvokeVirtual() ? HClassTableGet::kVTable : HClassTableGet::kIMTable,
+ invoke_instruction->IsInvokeVirtual() ? HClassTableGet::TableKind::kVTable
+ : HClassTableGet::TableKind::kIMTable,
method_offset,
invoke_instruction->GetDexPc());
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 13d3f752c3..f8a9a94e62 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -93,6 +93,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor {
void SimplifyStringEquals(HInvoke* invoke);
void SimplifyCompare(HInvoke* invoke, bool has_zero_op);
void SimplifyIsNaN(HInvoke* invoke);
+ void SimplifyFP2Int(HInvoke* invoke);
OptimizingCompilerStats* stats_;
bool simplification_occurred_ = false;
@@ -1562,26 +1563,71 @@ void InstructionSimplifierVisitor::SimplifyIsNaN(HInvoke* invoke) {
invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, condition);
}
+void InstructionSimplifierVisitor::SimplifyFP2Int(HInvoke* invoke) {
+ DCHECK(invoke->IsInvokeStaticOrDirect());
+ uint32_t dex_pc = invoke->GetDexPc();
+ HInstruction* x = invoke->InputAt(0);
+ Primitive::Type type = x->GetType();
+ // Set proper bit pattern for NaN and replace intrinsic with raw version.
+ HInstruction* nan;
+ if (type == Primitive::kPrimDouble) {
+ nan = GetGraph()->GetLongConstant(0x7ff8000000000000L);
+ invoke->SetIntrinsic(Intrinsics::kDoubleDoubleToRawLongBits,
+ kNeedsEnvironmentOrCache,
+ kNoSideEffects,
+ kNoThrow);
+ } else {
+ DCHECK_EQ(type, Primitive::kPrimFloat);
+ nan = GetGraph()->GetIntConstant(0x7fc00000);
+ invoke->SetIntrinsic(Intrinsics::kFloatFloatToRawIntBits,
+ kNeedsEnvironmentOrCache,
+ kNoSideEffects,
+ kNoThrow);
+ }
+ // Test IsNaN(x), which is the same as x != x.
+ HCondition* condition = new (GetGraph()->GetArena()) HNotEqual(x, x, dex_pc);
+ condition->SetBias(ComparisonBias::kLtBias);
+ invoke->GetBlock()->InsertInstructionBefore(condition, invoke->GetNext());
+ // Select between the two.
+ HInstruction* select = new (GetGraph()->GetArena()) HSelect(condition, nan, invoke, dex_pc);
+ invoke->GetBlock()->InsertInstructionBefore(select, condition->GetNext());
+ invoke->ReplaceWithExceptInReplacementAtIndex(select, 0); // false at index 0
+}
+
void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) {
- if (instruction->GetIntrinsic() == Intrinsics::kStringEquals) {
- SimplifyStringEquals(instruction);
- } else if (instruction->GetIntrinsic() == Intrinsics::kSystemArrayCopy) {
- SimplifySystemArrayCopy(instruction);
- } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerRotateRight ||
- instruction->GetIntrinsic() == Intrinsics::kLongRotateRight) {
- SimplifyRotate(instruction, false);
- } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerRotateLeft ||
- instruction->GetIntrinsic() == Intrinsics::kLongRotateLeft) {
- SimplifyRotate(instruction, true);
- } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerCompare ||
- instruction->GetIntrinsic() == Intrinsics::kLongCompare) {
- SimplifyCompare(instruction, /* is_signum */ false);
- } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerSignum ||
- instruction->GetIntrinsic() == Intrinsics::kLongSignum) {
- SimplifyCompare(instruction, /* is_signum */ true);
- } else if (instruction->GetIntrinsic() == Intrinsics::kFloatIsNaN ||
- instruction->GetIntrinsic() == Intrinsics::kDoubleIsNaN) {
- SimplifyIsNaN(instruction);
+ switch (instruction->GetIntrinsic()) {
+ case Intrinsics::kStringEquals:
+ SimplifyStringEquals(instruction);
+ break;
+ case Intrinsics::kSystemArrayCopy:
+ SimplifySystemArrayCopy(instruction);
+ break;
+ case Intrinsics::kIntegerRotateRight:
+ case Intrinsics::kLongRotateRight:
+ SimplifyRotate(instruction, false);
+ break;
+ case Intrinsics::kIntegerRotateLeft:
+ case Intrinsics::kLongRotateLeft:
+ SimplifyRotate(instruction, true);
+ break;
+ case Intrinsics::kIntegerCompare:
+ case Intrinsics::kLongCompare:
+ SimplifyCompare(instruction, /* is_signum */ false);
+ break;
+ case Intrinsics::kIntegerSignum:
+ case Intrinsics::kLongSignum:
+ SimplifyCompare(instruction, /* is_signum */ true);
+ break;
+ case Intrinsics::kFloatIsNaN:
+ case Intrinsics::kDoubleIsNaN:
+ SimplifyIsNaN(instruction);
+ break;
+ case Intrinsics::kFloatFloatToIntBits:
+ case Intrinsics::kDoubleDoubleToLongBits:
+ SimplifyFP2Int(instruction);
+ break;
+ default:
+ break;
}
}
diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc
new file mode 100644
index 0000000000..db1f9a79aa
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_arm.cc
@@ -0,0 +1,30 @@
+/*
+ * 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 "instruction_simplifier_arm.h"
+#include "instruction_simplifier_shared.h"
+
+namespace art {
+namespace arm {
+
+void InstructionSimplifierArmVisitor::VisitMul(HMul* instruction) {
+ if (TryCombineMultiplyAccumulate(instruction, kArm)) {
+ RecordSimplification();
+ }
+}
+
+} // namespace arm
+} // namespace art
diff --git a/compiler/optimizing/instruction_simplifier_arm.h b/compiler/optimizing/instruction_simplifier_arm.h
new file mode 100644
index 0000000000..379b95d6ae
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_arm.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_
+#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+namespace arm {
+
+class InstructionSimplifierArmVisitor : public HGraphVisitor {
+ public:
+ InstructionSimplifierArmVisitor(HGraph* graph, OptimizingCompilerStats* stats)
+ : HGraphVisitor(graph), stats_(stats) {}
+
+ private:
+ void RecordSimplification() {
+ if (stats_ != nullptr) {
+ stats_->RecordStat(kInstructionSimplificationsArch);
+ }
+ }
+
+ void VisitMul(HMul* instruction) OVERRIDE;
+
+ OptimizingCompilerStats* stats_;
+};
+
+
+class InstructionSimplifierArm : public HOptimization {
+ public:
+ InstructionSimplifierArm(HGraph* graph, OptimizingCompilerStats* stats)
+ : HOptimization(graph, "instruction_simplifier_arm", stats) {}
+
+ void Run() OVERRIDE {
+ InstructionSimplifierArmVisitor visitor(graph_, stats_);
+ visitor.VisitReversePostOrder();
+ }
+};
+
+} // namespace arm
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_
diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc
index 4bcfc54791..c2bbdccc29 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.cc
+++ b/compiler/optimizing/instruction_simplifier_arm64.cc
@@ -17,6 +17,7 @@
#include "instruction_simplifier_arm64.h"
#include "common_arm64.h"
+#include "instruction_simplifier_shared.h"
#include "mirror/array-inl.h"
namespace art {
@@ -179,67 +180,53 @@ bool InstructionSimplifierArm64Visitor::TryMergeIntoUsersShifterOperand(HInstruc
return true;
}
-bool InstructionSimplifierArm64Visitor::TrySimpleMultiplyAccumulatePatterns(
- HMul* mul, HBinaryOperation* input_binop, HInstruction* input_other) {
- DCHECK(Primitive::IsIntOrLongType(mul->GetType()));
- DCHECK(input_binop->IsAdd() || input_binop->IsSub());
- DCHECK_NE(input_binop, input_other);
- if (!input_binop->HasOnlyOneNonEnvironmentUse()) {
- return false;
- }
-
- // Try to interpret patterns like
- // a * (b <+/-> 1)
- // as
- // (a * b) <+/-> a
- HInstruction* input_a = input_other;
- HInstruction* input_b = nullptr; // Set to a non-null value if we found a pattern to optimize.
- HInstruction::InstructionKind op_kind;
-
- if (input_binop->IsAdd()) {
- if ((input_binop->GetConstantRight() != nullptr) && input_binop->GetConstantRight()->IsOne()) {
- // Interpret
- // a * (b + 1)
- // as
- // (a * b) + a
- input_b = input_binop->GetLeastConstantLeft();
- op_kind = HInstruction::kAdd;
- }
- } else {
- DCHECK(input_binop->IsSub());
- if (input_binop->GetRight()->IsConstant() &&
- input_binop->GetRight()->AsConstant()->IsMinusOne()) {
- // Interpret
- // a * (b - (-1))
- // as
- // a + (a * b)
- input_b = input_binop->GetLeft();
- op_kind = HInstruction::kAdd;
- } else if (input_binop->GetLeft()->IsConstant() &&
- input_binop->GetLeft()->AsConstant()->IsOne()) {
- // Interpret
- // a * (1 - b)
- // as
- // a - (a * b)
- input_b = input_binop->GetRight();
- op_kind = HInstruction::kSub;
+bool InstructionSimplifierArm64Visitor::TryMergeNegatedInput(HBinaryOperation* op) {
+ DCHECK(op->IsAnd() || op->IsOr() || op->IsXor()) << op->DebugName();
+ HInstruction* left = op->GetLeft();
+ HInstruction* right = op->GetRight();
+
+ // Only consider the case where there is exactly one Not, with 2 Not's De
+ // Morgan's laws should be applied instead.
+ if (left->IsNot() ^ right->IsNot()) {
+ HInstruction* hnot = (left->IsNot() ? left : right);
+ HInstruction* hother = (left->IsNot() ? right : left);
+
+ // Only do the simplification if the Not has only one use and can thus be
+ // safely removed. Even though ARM64 negated bitwise operations do not have
+ // an immediate variant (only register), we still do the simplification when
+ // `hother` is a constant, because it removes an instruction if the constant
+ // cannot be encoded as an immediate:
+ // mov r0, #large_constant
+ // neg r2, r1
+ // and r0, r0, r2
+ // becomes:
+ // mov r0, #large_constant
+ // bic r0, r0, r1
+ if (hnot->HasOnlyOneNonEnvironmentUse()) {
+ // Replace code looking like
+ // NOT tmp, mask
+ // AND dst, src, tmp (respectively ORR, EOR)
+ // with
+ // BIC dst, src, mask (respectively ORN, EON)
+ HInstruction* src = hnot->AsNot()->GetInput();
+
+ HArm64BitwiseNegatedRight* neg_op = new (GetGraph()->GetArena())
+ HArm64BitwiseNegatedRight(op->GetType(), op->GetKind(), hother, src, op->GetDexPc());
+
+ op->GetBlock()->ReplaceAndRemoveInstructionWith(op, neg_op);
+ hnot->GetBlock()->RemoveInstruction(hnot);
+ RecordSimplification();
+ return true;
}
}
- if (input_b == nullptr) {
- // We did not find a pattern we can optimize.
- return false;
- }
-
- HArm64MultiplyAccumulate* mulacc = new(GetGraph()->GetArena()) HArm64MultiplyAccumulate(
- mul->GetType(), op_kind, input_a, input_a, input_b, mul->GetDexPc());
-
- mul->GetBlock()->ReplaceAndRemoveInstructionWith(mul, mulacc);
- input_binop->GetBlock()->RemoveInstruction(input_binop);
-
return false;
}
+void InstructionSimplifierArm64Visitor::VisitAnd(HAnd* instruction) {
+ TryMergeNegatedInput(instruction);
+}
+
void InstructionSimplifierArm64Visitor::VisitArrayGet(HArrayGet* instruction) {
TryExtractArrayAccessAddress(instruction,
instruction->GetArray(),
@@ -255,76 +242,13 @@ void InstructionSimplifierArm64Visitor::VisitArraySet(HArraySet* instruction) {
}
void InstructionSimplifierArm64Visitor::VisitMul(HMul* instruction) {
- Primitive::Type type = instruction->GetType();
- if (!Primitive::IsIntOrLongType(type)) {
- return;
- }
-
- HInstruction* use = instruction->HasNonEnvironmentUses()
- ? instruction->GetUses().GetFirst()->GetUser()
- : nullptr;
-
- if (instruction->HasOnlyOneNonEnvironmentUse() && (use->IsAdd() || use->IsSub())) {
- // Replace code looking like
- // MUL tmp, x, y
- // SUB dst, acc, tmp
- // with
- // MULSUB dst, acc, x, y
- // Note that we do not want to (unconditionally) perform the merge when the
- // multiplication has multiple uses and it can be merged in all of them.
- // Multiple uses could happen on the same control-flow path, and we would
- // then increase the amount of work. In the future we could try to evaluate
- // whether all uses are on different control-flow paths (using dominance and
- // reverse-dominance information) and only perform the merge when they are.
- HInstruction* accumulator = nullptr;
- HBinaryOperation* binop = use->AsBinaryOperation();
- HInstruction* binop_left = binop->GetLeft();
- HInstruction* binop_right = binop->GetRight();
- // Be careful after GVN. This should not happen since the `HMul` has only
- // one use.
- DCHECK_NE(binop_left, binop_right);
- if (binop_right == instruction) {
- accumulator = binop_left;
- } else if (use->IsAdd()) {
- DCHECK_EQ(binop_left, instruction);
- accumulator = binop_right;
- }
-
- if (accumulator != nullptr) {
- HArm64MultiplyAccumulate* mulacc =
- new (GetGraph()->GetArena()) HArm64MultiplyAccumulate(type,
- binop->GetKind(),
- accumulator,
- instruction->GetLeft(),
- instruction->GetRight());
-
- binop->GetBlock()->ReplaceAndRemoveInstructionWith(binop, mulacc);
- DCHECK(!instruction->HasUses());
- instruction->GetBlock()->RemoveInstruction(instruction);
- RecordSimplification();
- return;
- }
- }
-
- // Use multiply accumulate instruction for a few simple patterns.
- // We prefer not applying the following transformations if the left and
- // right inputs perform the same operation.
- // We rely on GVN having squashed the inputs if appropriate. However the
- // results are still correct even if that did not happen.
- if (instruction->GetLeft() == instruction->GetRight()) {
- return;
+ if (TryCombineMultiplyAccumulate(instruction, kArm64)) {
+ RecordSimplification();
}
+}
- HInstruction* left = instruction->GetLeft();
- HInstruction* right = instruction->GetRight();
- if ((right->IsAdd() || right->IsSub()) &&
- TrySimpleMultiplyAccumulatePatterns(instruction, right->AsBinaryOperation(), left)) {
- return;
- }
- if ((left->IsAdd() || left->IsSub()) &&
- TrySimpleMultiplyAccumulatePatterns(instruction, left->AsBinaryOperation(), right)) {
- return;
- }
+void InstructionSimplifierArm64Visitor::VisitOr(HOr* instruction) {
+ TryMergeNegatedInput(instruction);
}
void InstructionSimplifierArm64Visitor::VisitShl(HShl* instruction) {
@@ -359,5 +283,9 @@ void InstructionSimplifierArm64Visitor::VisitUShr(HUShr* instruction) {
}
}
+void InstructionSimplifierArm64Visitor::VisitXor(HXor* instruction) {
+ TryMergeNegatedInput(instruction);
+}
+
} // namespace arm64
} // namespace art
diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h
index b7f490bb8c..cf8458713f 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.h
+++ b/compiler/optimizing/instruction_simplifier_arm64.h
@@ -51,18 +51,21 @@ class InstructionSimplifierArm64Visitor : public HGraphVisitor {
return TryMergeIntoShifterOperand(use, bitfield_op, true);
}
- bool TrySimpleMultiplyAccumulatePatterns(HMul* mul,
- HBinaryOperation* input_binop,
- HInstruction* input_other);
+ // For bitwise operations (And/Or/Xor) with a negated input, try to use
+ // a negated bitwise instruction.
+ bool TryMergeNegatedInput(HBinaryOperation* op);
// HInstruction visitors, sorted alphabetically.
+ void VisitAnd(HAnd* instruction) OVERRIDE;
void VisitArrayGet(HArrayGet* instruction) OVERRIDE;
void VisitArraySet(HArraySet* instruction) OVERRIDE;
void VisitMul(HMul* instruction) OVERRIDE;
+ void VisitOr(HOr* instruction) OVERRIDE;
void VisitShl(HShl* instruction) OVERRIDE;
void VisitShr(HShr* instruction) OVERRIDE;
void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE;
void VisitUShr(HUShr* instruction) OVERRIDE;
+ void VisitXor(HXor* instruction) OVERRIDE;
OptimizingCompilerStats* stats_;
};
diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc
new file mode 100644
index 0000000000..45d196fa6d
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_shared.cc
@@ -0,0 +1,189 @@
+/*
+ * 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 "instruction_simplifier_shared.h"
+
+namespace art {
+
+namespace {
+
+bool TrySimpleMultiplyAccumulatePatterns(HMul* mul,
+ HBinaryOperation* input_binop,
+ HInstruction* input_other) {
+ DCHECK(Primitive::IsIntOrLongType(mul->GetType()));
+ DCHECK(input_binop->IsAdd() || input_binop->IsSub());
+ DCHECK_NE(input_binop, input_other);
+ if (!input_binop->HasOnlyOneNonEnvironmentUse()) {
+ return false;
+ }
+
+ // Try to interpret patterns like
+ // a * (b <+/-> 1)
+ // as
+ // (a * b) <+/-> a
+ HInstruction* input_a = input_other;
+ HInstruction* input_b = nullptr; // Set to a non-null value if we found a pattern to optimize.
+ HInstruction::InstructionKind op_kind;
+
+ if (input_binop->IsAdd()) {
+ if ((input_binop->GetConstantRight() != nullptr) && input_binop->GetConstantRight()->IsOne()) {
+ // Interpret
+ // a * (b + 1)
+ // as
+ // (a * b) + a
+ input_b = input_binop->GetLeastConstantLeft();
+ op_kind = HInstruction::kAdd;
+ }
+ } else {
+ DCHECK(input_binop->IsSub());
+ if (input_binop->GetRight()->IsConstant() &&
+ input_binop->GetRight()->AsConstant()->IsMinusOne()) {
+ // Interpret
+ // a * (b - (-1))
+ // as
+ // a + (a * b)
+ input_b = input_binop->GetLeft();
+ op_kind = HInstruction::kAdd;
+ } else if (input_binop->GetLeft()->IsConstant() &&
+ input_binop->GetLeft()->AsConstant()->IsOne()) {
+ // Interpret
+ // a * (1 - b)
+ // as
+ // a - (a * b)
+ input_b = input_binop->GetRight();
+ op_kind = HInstruction::kSub;
+ }
+ }
+
+ if (input_b == nullptr) {
+ // We did not find a pattern we can optimize.
+ return false;
+ }
+
+ ArenaAllocator* arena = mul->GetBlock()->GetGraph()->GetArena();
+ HMultiplyAccumulate* mulacc = new(arena) HMultiplyAccumulate(
+ mul->GetType(), op_kind, input_a, input_a, input_b, mul->GetDexPc());
+
+ mul->GetBlock()->ReplaceAndRemoveInstructionWith(mul, mulacc);
+ input_binop->GetBlock()->RemoveInstruction(input_binop);
+
+ return true;
+}
+
+} // namespace
+
+bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa) {
+ Primitive::Type type = mul->GetType();
+ switch (isa) {
+ case kArm:
+ case kThumb2:
+ if (type != Primitive::kPrimInt) {
+ return false;
+ }
+ break;
+ case kArm64:
+ if (!Primitive::IsIntOrLongType(type)) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ HInstruction* use = mul->HasNonEnvironmentUses()
+ ? mul->GetUses().GetFirst()->GetUser()
+ : nullptr;
+
+ ArenaAllocator* arena = mul->GetBlock()->GetGraph()->GetArena();
+
+ if (mul->HasOnlyOneNonEnvironmentUse()) {
+ if (use->IsAdd() || use->IsSub()) {
+ // Replace code looking like
+ // MUL tmp, x, y
+ // SUB dst, acc, tmp
+ // with
+ // MULSUB dst, acc, x, y
+ // Note that we do not want to (unconditionally) perform the merge when the
+ // multiplication has multiple uses and it can be merged in all of them.
+ // Multiple uses could happen on the same control-flow path, and we would
+ // then increase the amount of work. In the future we could try to evaluate
+ // whether all uses are on different control-flow paths (using dominance and
+ // reverse-dominance information) and only perform the merge when they are.
+ HInstruction* accumulator = nullptr;
+ HBinaryOperation* binop = use->AsBinaryOperation();
+ HInstruction* binop_left = binop->GetLeft();
+ HInstruction* binop_right = binop->GetRight();
+ // Be careful after GVN. This should not happen since the `HMul` has only
+ // one use.
+ DCHECK_NE(binop_left, binop_right);
+ if (binop_right == mul) {
+ accumulator = binop_left;
+ } else if (use->IsAdd()) {
+ DCHECK_EQ(binop_left, mul);
+ accumulator = binop_right;
+ }
+
+ if (accumulator != nullptr) {
+ HMultiplyAccumulate* mulacc =
+ new (arena) HMultiplyAccumulate(type,
+ binop->GetKind(),
+ accumulator,
+ mul->GetLeft(),
+ mul->GetRight());
+
+ binop->GetBlock()->ReplaceAndRemoveInstructionWith(binop, mulacc);
+ DCHECK(!mul->HasUses());
+ mul->GetBlock()->RemoveInstruction(mul);
+ return true;
+ }
+ } else if (use->IsNeg() && isa != kArm) {
+ HMultiplyAccumulate* mulacc =
+ new (arena) HMultiplyAccumulate(type,
+ HInstruction::kSub,
+ mul->GetBlock()->GetGraph()->GetConstant(type, 0),
+ mul->GetLeft(),
+ mul->GetRight());
+
+ use->GetBlock()->ReplaceAndRemoveInstructionWith(use, mulacc);
+ DCHECK(!mul->HasUses());
+ mul->GetBlock()->RemoveInstruction(mul);
+ return true;
+ }
+ }
+
+ // Use multiply accumulate instruction for a few simple patterns.
+ // We prefer not applying the following transformations if the left and
+ // right inputs perform the same operation.
+ // We rely on GVN having squashed the inputs if appropriate. However the
+ // results are still correct even if that did not happen.
+ if (mul->GetLeft() == mul->GetRight()) {
+ return false;
+ }
+
+ HInstruction* left = mul->GetLeft();
+ HInstruction* right = mul->GetRight();
+ if ((right->IsAdd() || right->IsSub()) &&
+ TrySimpleMultiplyAccumulatePatterns(mul, right->AsBinaryOperation(), left)) {
+ return true;
+ }
+ if ((left->IsAdd() || left->IsSub()) &&
+ TrySimpleMultiplyAccumulatePatterns(mul, left->AsBinaryOperation(), right)) {
+ return true;
+ }
+ return false;
+}
+
+} // namespace art
diff --git a/compiler/optimizing/instruction_simplifier_shared.h b/compiler/optimizing/instruction_simplifier_shared.h
new file mode 100644
index 0000000000..9832ecc058
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_shared.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_
+#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_
+
+#include "nodes.h"
+
+namespace art {
+
+bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa);
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 316e86b4c9..3ed0278871 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -130,6 +130,10 @@ static Intrinsics GetIntrinsic(InlineMethod method) {
case kIntrinsicFloatCvt:
return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ?
Intrinsics::kFloatFloatToRawIntBits : Intrinsics::kFloatIntBitsToFloat;
+ case kIntrinsicFloat2Int:
+ return Intrinsics::kFloatFloatToIntBits;
+ case kIntrinsicDouble2Long:
+ return Intrinsics::kDoubleDoubleToLongBits;
// Floating-point tests.
case kIntrinsicFloatIsInfinite:
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 8cbdcbbcaf..4ce919ee39 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -1909,6 +1909,69 @@ void IntrinsicCodeGeneratorARM::VisitShortReverseBytes(HInvoke* invoke) {
__ revsh(out, in);
}
+void IntrinsicLocationsBuilderARM::VisitStringGetCharsNoCheck(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetInAt(3, Location::RequiresRegister());
+ locations->SetInAt(4, Location::RequiresRegister());
+
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorARM::VisitStringGetCharsNoCheck(HInvoke* invoke) {
+ ArmAssembler* assembler = GetAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ // Check assumption that sizeof(Char) is 2 (used in scaling below).
+ const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
+ DCHECK_EQ(char_size, 2u);
+
+ // Location of data in char array buffer.
+ const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
+
+ // Location of char array data in string.
+ const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
+
+ // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
+ // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
+ Register srcObj = locations->InAt(0).AsRegister<Register>();
+ Register srcBegin = locations->InAt(1).AsRegister<Register>();
+ Register srcEnd = locations->InAt(2).AsRegister<Register>();
+ Register dstObj = locations->InAt(3).AsRegister<Register>();
+ Register dstBegin = locations->InAt(4).AsRegister<Register>();
+
+ Register src_ptr = locations->GetTemp(0).AsRegister<Register>();
+ Register src_ptr_end = locations->GetTemp(1).AsRegister<Register>();
+ Register dst_ptr = locations->GetTemp(2).AsRegister<Register>();
+ Register tmp = locations->GetTemp(3).AsRegister<Register>();
+
+ // src range to copy.
+ __ add(src_ptr, srcObj, ShifterOperand(value_offset));
+ __ add(src_ptr_end, src_ptr, ShifterOperand(srcEnd, LSL, 1));
+ __ add(src_ptr, src_ptr, ShifterOperand(srcBegin, LSL, 1));
+
+ // dst to be copied.
+ __ add(dst_ptr, dstObj, ShifterOperand(data_offset));
+ __ add(dst_ptr, dst_ptr, ShifterOperand(dstBegin, LSL, 1));
+
+ // Do the copy.
+ Label loop, done;
+ __ Bind(&loop);
+ __ cmp(src_ptr, ShifterOperand(src_ptr_end));
+ __ b(&done, EQ);
+ __ ldrh(tmp, Address(src_ptr, char_size, Address::PostIndex));
+ __ strh(tmp, Address(dst_ptr, char_size, Address::PostIndex));
+ __ b(&loop);
+ __ Bind(&done);
+}
+
// Unimplemented intrinsics.
#define UNIMPLEMENTED_INTRINSIC(Name) \
@@ -1933,7 +1996,6 @@ UNIMPLEMENTED_INTRINSIC(MathRoundFloat) // Could be done by changing rounding
UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) // High register pressure.
UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
-UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
@@ -1944,6 +2006,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits)
+UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits)
UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index b5f15fe22d..4be1695a94 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1602,6 +1602,69 @@ void IntrinsicCodeGeneratorARM64::VisitMathNextAfter(HInvoke* invoke) {
GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickNextAfter);
}
+void IntrinsicLocationsBuilderARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetInAt(3, Location::RequiresRegister());
+ locations->SetInAt(4, Location::RequiresRegister());
+
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
+ vixl::MacroAssembler* masm = GetVIXLAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ // Check assumption that sizeof(Char) is 2 (used in scaling below).
+ const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
+ DCHECK_EQ(char_size, 2u);
+
+ // Location of data in char array buffer.
+ const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
+
+ // Location of char array data in string.
+ const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
+
+ // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
+ // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
+ Register srcObj = XRegisterFrom(locations->InAt(0));
+ Register srcBegin = XRegisterFrom(locations->InAt(1));
+ Register srcEnd = XRegisterFrom(locations->InAt(2));
+ Register dstObj = XRegisterFrom(locations->InAt(3));
+ Register dstBegin = XRegisterFrom(locations->InAt(4));
+
+ Register src_ptr = XRegisterFrom(locations->GetTemp(0));
+ Register src_ptr_end = XRegisterFrom(locations->GetTemp(1));
+
+ UseScratchRegisterScope temps(masm);
+ Register dst_ptr = temps.AcquireX();
+ Register tmp = temps.AcquireW();
+
+ // src range to copy.
+ __ Add(src_ptr, srcObj, Operand(value_offset));
+ __ Add(src_ptr_end, src_ptr, Operand(srcEnd, LSL, 1));
+ __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1));
+
+ // dst to be copied.
+ __ Add(dst_ptr, dstObj, Operand(data_offset));
+ __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1));
+
+ // Do the copy.
+ vixl::Label loop, done;
+ __ Bind(&loop);
+ __ Cmp(src_ptr, src_ptr_end);
+ __ B(&done, eq);
+ __ Ldrh(tmp, MemOperand(src_ptr, char_size, vixl::PostIndex));
+ __ Strh(tmp, MemOperand(dst_ptr, char_size, vixl::PostIndex));
+ __ B(&loop);
+ __ Bind(&done);
+}
+
// Unimplemented intrinsics.
#define UNIMPLEMENTED_INTRINSIC(Name) \
@@ -1615,7 +1678,6 @@ UNIMPLEMENTED_INTRINSIC(LongBitCount)
UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(SystemArrayCopy)
UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
-UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
@@ -1626,6 +1688,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits)
+UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits)
UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h
index 88217b308e..e1aea924cf 100644
--- a/compiler/optimizing/intrinsics_list.h
+++ b/compiler/optimizing/intrinsics_list.h
@@ -23,10 +23,12 @@
#define INTRINSICS_LIST(V) \
V(DoubleDoubleToRawLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
+ V(DoubleDoubleToLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
V(DoubleIsInfinite, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
V(DoubleIsNaN, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
V(DoubleLongBitsToDouble, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
V(FloatFloatToRawIntBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
+ V(FloatFloatToIntBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
V(FloatIsInfinite, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
V(FloatIsNaN, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
V(FloatIntBitsToFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 2f183c3a62..a737d8100a 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1772,6 +1772,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits)
+UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits)
UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerCompare)
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index bd4f5329da..ca2652b74a 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1824,6 +1824,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits)
+UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits)
UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerCompare)
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 260a8773fb..0df4553f56 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -2642,6 +2642,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits)
+UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits)
UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 93e8c00e5a..2a9e684d11 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2719,6 +2719,8 @@ UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits)
+UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits)
UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index f9acb089ee..27a5b97f5f 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -2206,7 +2206,8 @@ void HInstruction::SetReferenceTypeInfo(ReferenceTypeInfo rti) {
CheckAgainstUpperBound(rti, AsBoundType()->GetUpperBound());
}
}
- reference_type_info_ = rti;
+ reference_type_handle_ = rti.GetTypeHandle();
+ SetPackedFlag<kFlagReferenceTypeIsExact>(rti.IsExact());
}
void HBoundType::SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be_null) {
@@ -2217,17 +2218,15 @@ void HBoundType::SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be
CheckAgainstUpperBound(GetReferenceTypeInfo(), upper_bound);
}
upper_bound_ = upper_bound;
- upper_can_be_null_ = can_be_null;
+ SetPackedFlag<kFlagUpperCanBeNull>(can_be_null);
}
-ReferenceTypeInfo::ReferenceTypeInfo() : type_handle_(TypeHandle()), is_exact_(false) {}
-
-ReferenceTypeInfo::ReferenceTypeInfo(TypeHandle type_handle, bool is_exact)
- : type_handle_(type_handle), is_exact_(is_exact) {
+ReferenceTypeInfo ReferenceTypeInfo::Create(TypeHandle type_handle, bool is_exact) {
if (kIsDebugBuild) {
ScopedObjectAccess soa(Thread::Current());
DCHECK(IsValidHandle(type_handle));
}
+ return ReferenceTypeInfo(type_handle, is_exact);
}
std::ostream& operator<<(std::ostream& os, const ReferenceTypeInfo& rhs) {
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index b355883a72..9eddfc7e0e 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -154,8 +154,9 @@ class ReferenceTypeInfo : ValueObject {
public:
typedef Handle<mirror::Class> TypeHandle;
- static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact) {
- // The constructor will check that the type_handle is valid.
+ static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact);
+
+ static ReferenceTypeInfo CreateUnchecked(TypeHandle type_handle, bool is_exact) {
return ReferenceTypeInfo(type_handle, is_exact);
}
@@ -254,8 +255,9 @@ class ReferenceTypeInfo : ValueObject {
}
private:
- ReferenceTypeInfo();
- ReferenceTypeInfo(TypeHandle type_handle, bool is_exact);
+ ReferenceTypeInfo() : type_handle_(TypeHandle()), is_exact_(false) {}
+ ReferenceTypeInfo(TypeHandle type_handle, bool is_exact)
+ : type_handle_(type_handle), is_exact_(is_exact) { }
// The class of the object.
TypeHandle type_handle_;
@@ -1247,6 +1249,16 @@ class HLoopInformationOutwardIterator : public ValueObject {
M(UShr, BinaryOperation) \
M(Xor, BinaryOperation) \
+/*
+ * Instructions, shared across several (not all) architectures.
+ */
+#if !defined(ART_ENABLE_CODEGEN_arm) && !defined(ART_ENABLE_CODEGEN_arm64)
+#define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M)
+#else
+#define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) \
+ M(MultiplyAccumulate, Instruction)
+#endif
+
#ifndef ART_ENABLE_CODEGEN_arm
#define FOR_EACH_CONCRETE_INSTRUCTION_ARM(M)
#else
@@ -1258,9 +1270,9 @@ class HLoopInformationOutwardIterator : public ValueObject {
#define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M)
#else
#define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \
+ M(Arm64BitwiseNegatedRight, Instruction) \
M(Arm64DataProcWithShifterOp, Instruction) \
- M(Arm64IntermediateAddress, Instruction) \
- M(Arm64MultiplyAccumulate, Instruction)
+ M(Arm64IntermediateAddress, Instruction)
#endif
#define FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M)
@@ -1281,6 +1293,7 @@ class HLoopInformationOutwardIterator : public ValueObject {
#define FOR_EACH_CONCRETE_INSTRUCTION(M) \
FOR_EACH_CONCRETE_INSTRUCTION_COMMON(M) \
+ FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) \
FOR_EACH_CONCRETE_INSTRUCTION_ARM(M) \
FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \
FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M) \
@@ -1837,13 +1850,15 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> {
dex_pc_(dex_pc),
id_(-1),
ssa_index_(-1),
- emitted_at_use_site_(false),
+ packed_fields_(0u),
environment_(nullptr),
locations_(nullptr),
live_interval_(nullptr),
lifetime_position_(kNoLifetime),
side_effects_(side_effects),
- reference_type_info_(ReferenceTypeInfo::CreateInvalid()) {}
+ reference_type_handle_(ReferenceTypeInfo::CreateInvalid().GetTypeHandle()) {
+ SetPackedFlag<kFlagReferenceTypeIsExact>(ReferenceTypeInfo::CreateInvalid().IsExact());
+ }
virtual ~HInstruction() {}
@@ -1912,7 +1927,8 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> {
ReferenceTypeInfo GetReferenceTypeInfo() const {
DCHECK_EQ(GetType(), Primitive::kPrimNot);
- return reference_type_info_;
+ return ReferenceTypeInfo::CreateUnchecked(reference_type_handle_,
+ GetPackedFlag<kFlagReferenceTypeIsExact>());;
}
void AddUseAt(HInstruction* user, size_t index) {
@@ -2101,13 +2117,45 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> {
// 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; }
+ bool IsEmittedAtUseSite() const { return GetPackedFlag<kFlagEmittedAtUseSite>(); }
+ void MarkEmittedAtUseSite() { SetPackedFlag<kFlagEmittedAtUseSite>(true); }
protected:
+ // 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.
+ static constexpr size_t kFlagEmittedAtUseSite = 0u;
+ static constexpr size_t kFlagReferenceTypeIsExact = kFlagEmittedAtUseSite + 1;
+ static constexpr size_t kNumberOfGenericPackedBits = kFlagReferenceTypeIsExact + 1;
+ static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte;
+
virtual const HUserRecord<HInstruction*> InputRecordAt(size_t i) const = 0;
virtual void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) = 0;
+ uint32_t GetPackedFields() const {
+ return packed_fields_;
+ }
+
+ template <size_t flag>
+ bool GetPackedFlag() const {
+ return (packed_fields_ & (1u << flag)) != 0u;
+ }
+
+ template <size_t flag>
+ void SetPackedFlag(bool value = true) {
+ packed_fields_ = (packed_fields_ & ~(1u << flag)) | ((value ? 1u : 0u) << flag);
+ }
+
+ template <typename BitFieldType>
+ typename BitFieldType::value_type GetPackedField() const {
+ return BitFieldType::Decode(packed_fields_);
+ }
+
+ template <typename BitFieldType>
+ void SetPackedField(typename BitFieldType::value_type value) {
+ DCHECK(IsUint<BitFieldType::size>(static_cast<uintptr_t>(value)));
+ packed_fields_ = BitFieldType::Update(value, packed_fields_);
+ }
+
private:
void RemoveEnvironmentUser(HUseListNode<HEnvironment*>* use_node) { env_uses_.Remove(use_node); }
@@ -2124,9 +2172,8 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> {
// 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_;
+ // Packed fields.
+ uint32_t packed_fields_;
// List of instructions that have this instruction as input.
HUseList<HInstruction*> uses_;
@@ -2150,8 +2197,10 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> {
SideEffects side_effects_;
+ // The reference handle part of the reference type info.
+ // The IsExact() flag is stored in packed fields.
// TODO: for primitive types this should be marked as invalid.
- ReferenceTypeInfo reference_type_info_;
+ ReferenceTypeInfo::TypeHandle reference_type_handle_;
friend class GraphChecker;
friend class HBasicBlock;
@@ -2277,13 +2326,23 @@ template<intptr_t N>
class HExpression : public HTemplateInstruction<N> {
public:
HExpression<N>(Primitive::Type type, SideEffects side_effects, uint32_t dex_pc)
- : HTemplateInstruction<N>(side_effects, dex_pc), type_(type) {}
+ : HTemplateInstruction<N>(side_effects, dex_pc) {
+ this->template SetPackedField<TypeField>(type);
+ }
virtual ~HExpression() {}
- Primitive::Type GetType() const OVERRIDE { return type_; }
+ Primitive::Type GetType() const OVERRIDE {
+ return TypeField::Decode(this->GetPackedFields());
+ }
protected:
- Primitive::Type type_;
+ static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
+ static constexpr size_t kNumberOfExpressionPackedBits = kFieldType + kFieldTypeSize;
+ static_assert(kNumberOfExpressionPackedBits <= HInstruction::kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+ using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>;
};
// Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow
@@ -2573,13 +2632,16 @@ class HIf : public HTemplateInstruction<1> {
// higher indices in no particular order.
class HTryBoundary : public HTemplateInstruction<0> {
public:
- enum BoundaryKind {
+ enum class BoundaryKind {
kEntry,
kExit,
+ kLast = kExit
};
explicit HTryBoundary(BoundaryKind kind, uint32_t dex_pc = kNoDexPc)
- : HTemplateInstruction(SideEffects::None(), dex_pc), kind_(kind) {}
+ : HTemplateInstruction(SideEffects::None(), dex_pc) {
+ SetPackedField<BoundaryKindField>(kind);
+ }
bool IsControlFlow() const OVERRIDE { return true; }
@@ -2605,14 +2667,22 @@ class HTryBoundary : public HTemplateInstruction<0> {
}
}
- bool IsEntry() const { return kind_ == BoundaryKind::kEntry; }
+ BoundaryKind GetBoundaryKind() const { return GetPackedField<BoundaryKindField>(); }
+ bool IsEntry() const { return GetBoundaryKind() == BoundaryKind::kEntry; }
bool HasSameExceptionHandlersAs(const HTryBoundary& other) const;
DECLARE_INSTRUCTION(TryBoundary);
private:
- const BoundaryKind kind_;
+ static constexpr size_t kFieldBoundaryKind = kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldBoundaryKindSize =
+ MinimumBitsToStore(static_cast<size_t>(BoundaryKind::kLast));
+ static constexpr size_t kNumberOfTryBoundaryPackedBits =
+ kFieldBoundaryKind + kFieldBoundaryKindSize;
+ static_assert(kNumberOfTryBoundaryPackedBits <= kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+ using BoundaryKindField = BitField<BoundaryKind, kFieldBoundaryKind, kFieldBoundaryKindSize>;
DISALLOW_COPY_AND_ASSIGN(HTryBoundary);
};
@@ -2658,9 +2728,10 @@ class HCurrentMethod : public HExpression<0> {
// of a class.
class HClassTableGet : public HExpression<1> {
public:
- enum TableKind {
+ enum class TableKind {
kVTable,
kIMTable,
+ kLast = kIMTable
};
HClassTableGet(HInstruction* cls,
Primitive::Type type,
@@ -2668,26 +2739,33 @@ class HClassTableGet : public HExpression<1> {
size_t index,
uint32_t dex_pc)
: HExpression(type, SideEffects::None(), dex_pc),
- index_(index),
- table_kind_(kind) {
+ index_(index) {
+ SetPackedField<TableKindField>(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_;
+ other->AsClassTableGet()->GetPackedFields() == GetPackedFields();
}
- TableKind GetTableKind() const { return table_kind_; }
+ TableKind GetTableKind() const { return GetPackedField<TableKindField>(); }
size_t GetIndex() const { return index_; }
DECLARE_INSTRUCTION(ClassTableGet);
private:
+ static constexpr size_t kFieldTableKind = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFieldTableKindSize =
+ MinimumBitsToStore(static_cast<size_t>(TableKind::kLast));
+ static constexpr size_t kNumberOfClassTableGetPackedBits = kFieldTableKind + kFieldTableKindSize;
+ static_assert(kNumberOfClassTableGetPackedBits <= kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+ using TableKindField = BitField<TableKind, kFieldTableKind, kFieldTableKind>;
+
// The index of the ArtMethod in the table.
const size_t index_;
- const TableKind table_kind_;
DISALLOW_COPY_AND_ASSIGN(HClassTableGet);
};
@@ -2854,6 +2932,7 @@ enum class ComparisonBias {
kNoBias, // bias is not applicable (i.e. for long operation)
kGtBias, // return 1 for NaN comparisons
kLtBias, // return -1 for NaN comparisons
+ kLast = kLtBias
};
std::ostream& operator<<(std::ostream& os, const ComparisonBias& rhs);
@@ -2861,8 +2940,9 @@ std::ostream& operator<<(std::ostream& os, const ComparisonBias& rhs);
class HCondition : public HBinaryOperation {
public:
HCondition(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
- : HBinaryOperation(Primitive::kPrimBoolean, first, second, SideEffects::None(), dex_pc),
- bias_(ComparisonBias::kNoBias) {}
+ : HBinaryOperation(Primitive::kPrimBoolean, first, second, SideEffects::None(), dex_pc) {
+ SetPackedField<ComparisonBiasField>(ComparisonBias::kNoBias);
+ }
// For code generation purposes, returns whether this instruction is just before
// `instruction`, and disregard moves in between.
@@ -2874,12 +2954,12 @@ class HCondition : public HBinaryOperation {
virtual IfCondition GetOppositeCondition() const = 0;
- bool IsGtBias() const { return bias_ == ComparisonBias::kGtBias; }
- ComparisonBias GetBias() const { return bias_; }
- void SetBias(ComparisonBias bias) { bias_ = bias; }
+ bool IsGtBias() const { return GetBias() == ComparisonBias::kGtBias; }
+ ComparisonBias GetBias() const { return GetPackedField<ComparisonBiasField>(); }
+ void SetBias(ComparisonBias bias) { SetPackedField<ComparisonBiasField>(bias); }
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
- return bias_ == other->AsCondition()->bias_;
+ return GetPackedFields() == other->AsCondition()->GetPackedFields();
}
bool IsFPConditionTrueIfNaN() const {
@@ -2895,6 +2975,16 @@ class HCondition : public HBinaryOperation {
}
protected:
+ // Needed if we merge a HCompare into a HCondition.
+ static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFieldComparisonBiasSize =
+ MinimumBitsToStore(static_cast<size_t>(ComparisonBias::kLast));
+ static constexpr size_t kNumberOfConditionPackedBits =
+ kFieldComparisonBias + kFieldComparisonBiasSize;
+ static_assert(kNumberOfConditionPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using ComparisonBiasField =
+ BitField<ComparisonBias, kFieldComparisonBias, kFieldComparisonBiasSize>;
+
template <typename T>
int32_t Compare(T x, T y) const { return x > y ? 1 : (x < y ? -1 : 0); }
@@ -2912,9 +3002,6 @@ class HCondition : public HBinaryOperation {
}
private:
- // Needed if we merge a HCompare into a HCondition.
- ComparisonBias bias_;
-
DISALLOW_COPY_AND_ASSIGN(HCondition);
};
@@ -3327,8 +3414,8 @@ class HCompare : public HBinaryOperation {
first,
second,
SideEffectsForArchRuntimeCalls(type),
- dex_pc),
- bias_(bias) {
+ dex_pc) {
+ SetPackedField<ComparisonBiasField>(bias);
DCHECK_EQ(type, first->GetType());
DCHECK_EQ(type, second->GetType());
}
@@ -3363,16 +3450,16 @@ class HCompare : public HBinaryOperation {
}
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
- return bias_ == other->AsCompare()->bias_;
+ return GetPackedFields() == other->AsCompare()->GetPackedFields();
}
- ComparisonBias GetBias() const { return bias_; }
+ ComparisonBias GetBias() const { return GetPackedField<ComparisonBiasField>(); }
// Does this compare instruction have a "gt bias" (vs an "lt bias")?
- // Only meaninfgul for floating-point comparisons.
+ // Only meaningful for floating-point comparisons.
bool IsGtBias() const {
DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType();
- return bias_ == ComparisonBias::kGtBias;
+ return GetBias() == ComparisonBias::kGtBias;
}
static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type type) {
@@ -3383,6 +3470,15 @@ class HCompare : public HBinaryOperation {
DECLARE_INSTRUCTION(Compare);
protected:
+ static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFieldComparisonBiasSize =
+ MinimumBitsToStore(static_cast<size_t>(ComparisonBias::kLast));
+ static constexpr size_t kNumberOfComparePackedBits =
+ kFieldComparisonBias + kFieldComparisonBiasSize;
+ static_assert(kNumberOfComparePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using ComparisonBiasField =
+ BitField<ComparisonBias, kFieldComparisonBias, kFieldComparisonBiasSize>;
+
// Return an integer constant containing the result of a comparison evaluated at compile time.
HIntConstant* MakeConstantComparison(int32_t value, uint32_t dex_pc) const {
DCHECK(value == -1 || value == 0 || value == 1) << value;
@@ -3390,8 +3486,6 @@ class HCompare : public HBinaryOperation {
}
private:
- const ComparisonBias bias_;
-
DISALLOW_COPY_AND_ASSIGN(HCompare);
};
@@ -3459,9 +3553,9 @@ class HNewInstance : public HExpression<2> {
: HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc),
type_index_(type_index),
dex_file_(dex_file),
- can_throw_(can_throw),
- finalizable_(finalizable),
entrypoint_(entrypoint) {
+ SetPackedFlag<kFlagCanThrow>(can_throw);
+ SetPackedFlag<kFlagFinalizable>(finalizable);
SetRawInputAt(0, cls);
SetRawInputAt(1, current_method);
}
@@ -3475,9 +3569,9 @@ class HNewInstance : public HExpression<2> {
// It may throw when called on type that's not instantiable/accessible.
// It can throw OOME.
// TODO: distinguish between the two cases so we can for example allow allocation elimination.
- bool CanThrow() const OVERRIDE { return can_throw_ || true; }
+ bool CanThrow() const OVERRIDE { return GetPackedFlag<kFlagCanThrow>() || true; }
- bool IsFinalizable() const { return finalizable_; }
+ bool IsFinalizable() const { return GetPackedFlag<kFlagFinalizable>(); }
bool CanBeNull() const OVERRIDE { return false; }
@@ -3492,10 +3586,14 @@ class HNewInstance : public HExpression<2> {
DECLARE_INSTRUCTION(NewInstance);
private:
+ static constexpr size_t kFlagCanThrow = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFlagFinalizable = kFlagCanThrow + 1;
+ static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1;
+ static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+
const uint16_t type_index_;
const DexFile& dex_file_;
- const bool can_throw_;
- const bool finalizable_;
QuickEntrypointEnum entrypoint_;
DISALLOW_COPY_AND_ASSIGN(HNewInstance);
@@ -3545,12 +3643,14 @@ class HInvoke : public HInstruction {
// inputs at the end of their list of inputs.
uint32_t GetNumberOfArguments() const { return number_of_arguments_; }
- Primitive::Type GetType() const OVERRIDE { return return_type_; }
+ Primitive::Type GetType() const OVERRIDE { return GetPackedField<ReturnTypeField>(); }
uint32_t GetDexMethodIndex() const { return dex_method_index_; }
const DexFile& GetDexFile() const { return GetEnvironment()->GetDexFile(); }
- InvokeType GetOriginalInvokeType() const { return original_invoke_type_; }
+ InvokeType GetOriginalInvokeType() const {
+ return GetPackedField<OriginalInvokeTypeField>();
+ }
Intrinsics GetIntrinsic() const {
return intrinsic_;
@@ -3565,7 +3665,7 @@ class HInvoke : public HInstruction {
return GetEnvironment()->IsFromInlinedInvoke();
}
- bool CanThrow() const OVERRIDE { return can_throw_; }
+ bool CanThrow() const OVERRIDE { return GetPackedFlag<kFlagCanThrow>(); }
bool CanBeMoved() const OVERRIDE { return IsIntrinsic(); }
@@ -3586,6 +3686,20 @@ class HInvoke : public HInstruction {
DECLARE_ABSTRACT_INSTRUCTION(Invoke);
protected:
+ static constexpr size_t kFieldOriginalInvokeType = kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldOriginalInvokeTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(kMaxInvokeType));
+ static constexpr size_t kFieldReturnType =
+ kFieldOriginalInvokeType + kFieldOriginalInvokeTypeSize;
+ static constexpr size_t kFieldReturnTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
+ static constexpr size_t kFlagCanThrow = kFieldReturnType + kFieldReturnTypeSize;
+ static constexpr size_t kNumberOfInvokePackedBits = kFlagCanThrow + 1;
+ static_assert(kNumberOfInvokePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using OriginalInvokeTypeField =
+ BitField<InvokeType, kFieldOriginalInvokeType, kFieldOriginalInvokeTypeSize>;
+ using ReturnTypeField = BitField<Primitive::Type, kFieldReturnType, kFieldReturnTypeSize>;
+
HInvoke(ArenaAllocator* arena,
uint32_t number_of_arguments,
uint32_t number_of_other_inputs,
@@ -3598,12 +3712,12 @@ class HInvoke : public HInstruction {
number_of_arguments_(number_of_arguments),
inputs_(number_of_arguments + number_of_other_inputs,
arena->Adapter(kArenaAllocInvokeInputs)),
- return_type_(return_type),
dex_method_index_(dex_method_index),
- original_invoke_type_(original_invoke_type),
- can_throw_(true),
intrinsic_(Intrinsics::kNone),
intrinsic_optimizations_(0) {
+ SetPackedField<ReturnTypeField>(return_type);
+ SetPackedField<OriginalInvokeTypeField>(original_invoke_type);
+ SetPackedFlag<kFlagCanThrow>(true);
}
const HUserRecord<HInstruction*> InputRecordAt(size_t index) const OVERRIDE {
@@ -3614,14 +3728,11 @@ class HInvoke : public HInstruction {
inputs_[index] = input;
}
- void SetCanThrow(bool can_throw) { can_throw_ = can_throw; }
+ void SetCanThrow(bool can_throw) { SetPackedFlag<kFlagCanThrow>(can_throw); }
uint32_t number_of_arguments_;
ArenaVector<HUserRecord<HInstruction*>> inputs_;
- const Primitive::Type return_type_;
const uint32_t dex_method_index_;
- const InvokeType original_invoke_type_;
- bool can_throw_;
Intrinsics intrinsic_;
// A magic word holding optimizations for intrinsics. See intrinsics.h.
@@ -3662,6 +3773,7 @@ class HInvokeStaticOrDirect : public HInvoke {
kNone, // Class already initialized.
kExplicit, // Static call having explicit clinit check as last input.
kImplicit, // Static call implicitly requiring a clinit check.
+ kLast = kImplicit
};
// Determines how to load the target ArtMethod*.
@@ -3682,7 +3794,7 @@ class HInvokeStaticOrDirect : public HInvoke {
// the image relocatable or not.
kDirectAddressWithFixup,
- // Load from resoved methods array in the dex cache using a PC-relative load.
+ // Load from resolved methods array in the dex cache using a PC-relative load.
// Used when we need to use the dex cache, for example for invoke-static that
// may cause class initialization (the entry may point to a resolution method),
// and we know that we can access the dex cache arrays using a PC-relative load.
@@ -3754,10 +3866,11 @@ class HInvokeStaticOrDirect : public HInvoke {
dex_pc,
method_index,
original_invoke_type),
- optimized_invoke_type_(optimized_invoke_type),
- clinit_check_requirement_(clinit_check_requirement),
target_method_(target_method),
- dispatch_info_(dispatch_info) { }
+ dispatch_info_(dispatch_info) {
+ SetPackedField<OptimizedInvokeTypeField>(optimized_invoke_type);
+ SetPackedField<ClinitCheckRequirementField>(clinit_check_requirement);
+ }
void SetDispatchInfo(const DispatchInfo& dispatch_info) {
bool had_current_method_input = HasCurrentMethodInput();
@@ -3789,7 +3902,7 @@ class HInvokeStaticOrDirect : public HInvoke {
}
bool CanBeNull() const OVERRIDE {
- return return_type_ == Primitive::kPrimNot && !IsStringInit();
+ return GetPackedField<ReturnTypeField>() == Primitive::kPrimNot && !IsStringInit();
}
// Get the index of the special input, if any.
@@ -3800,9 +3913,12 @@ class HInvokeStaticOrDirect : public HInvoke {
uint32_t GetSpecialInputIndex() const { return GetNumberOfArguments(); }
bool HasSpecialInput() const { return GetNumberOfArguments() != InputCount(); }
- InvokeType GetOptimizedInvokeType() const { return optimized_invoke_type_; }
+ InvokeType GetOptimizedInvokeType() const {
+ return GetPackedField<OptimizedInvokeTypeField>();
+ }
+
void SetOptimizedInvokeType(InvokeType invoke_type) {
- optimized_invoke_type_ = invoke_type;
+ SetPackedField<OptimizedInvokeTypeField>(invoke_type);
}
MethodLoadKind GetMethodLoadKind() const { return dispatch_info_.method_load_kind; }
@@ -3849,7 +3965,9 @@ class HInvokeStaticOrDirect : public HInvoke {
return dispatch_info_.direct_code_ptr;
}
- ClinitCheckRequirement GetClinitCheckRequirement() const { return clinit_check_requirement_; }
+ ClinitCheckRequirement GetClinitCheckRequirement() const {
+ return GetPackedField<ClinitCheckRequirementField>();
+ }
// Is this instruction a call to a static method?
bool IsStatic() const {
@@ -3867,7 +3985,7 @@ class HInvokeStaticOrDirect : public HInvoke {
DCHECK(last_input->IsLoadClass() || last_input->IsClinitCheck()) << last_input->DebugName();
RemoveAsUserOfInput(last_input_index);
inputs_.pop_back();
- clinit_check_requirement_ = new_requirement;
+ SetPackedField<ClinitCheckRequirementField>(new_requirement);
DCHECK(!IsStaticWithExplicitClinitCheck());
}
@@ -3883,13 +4001,13 @@ class HInvokeStaticOrDirect : public HInvoke {
// Is this a call to a static method whose declaring class has an
// explicit initialization check in the graph?
bool IsStaticWithExplicitClinitCheck() const {
- return IsStatic() && (clinit_check_requirement_ == ClinitCheckRequirement::kExplicit);
+ return IsStatic() && (GetClinitCheckRequirement() == ClinitCheckRequirement::kExplicit);
}
// Is this a call to a static method whose declaring class has an
// implicit intialization check requirement?
bool IsStaticWithImplicitClinitCheck() const {
- return IsStatic() && (clinit_check_requirement_ == ClinitCheckRequirement::kImplicit);
+ return IsStatic() && (GetClinitCheckRequirement() == ClinitCheckRequirement::kImplicit);
}
// Does this method load kind need the current method as an input?
@@ -3918,8 +4036,23 @@ class HInvokeStaticOrDirect : public HInvoke {
void RemoveInputAt(size_t index);
private:
- InvokeType optimized_invoke_type_;
- ClinitCheckRequirement clinit_check_requirement_;
+ static constexpr size_t kFieldOptimizedInvokeType = kNumberOfInvokePackedBits;
+ static constexpr size_t kFieldOptimizedInvokeTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(kMaxInvokeType));
+ static constexpr size_t kFieldClinitCheckRequirement =
+ kFieldOptimizedInvokeType + kFieldOptimizedInvokeTypeSize;
+ static constexpr size_t kFieldClinitCheckRequirementSize =
+ MinimumBitsToStore(static_cast<size_t>(ClinitCheckRequirement::kLast));
+ static constexpr size_t kNumberOfInvokeStaticOrDirectPackedBits =
+ kFieldClinitCheckRequirement + kFieldClinitCheckRequirementSize;
+ static_assert(kNumberOfInvokeStaticOrDirectPackedBits <= kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+ using OptimizedInvokeTypeField =
+ BitField<InvokeType, kFieldOptimizedInvokeType, kFieldOptimizedInvokeTypeSize>;
+ using ClinitCheckRequirementField = BitField<ClinitCheckRequirement,
+ kFieldClinitCheckRequirement,
+ kFieldClinitCheckRequirementSize>;
+
// The target method may refer to different dex file or method index than the original
// invoke. This happens for sharpened calls and for calls where a method was redeclared
// in derived class to increase visibility.
@@ -4585,32 +4718,35 @@ class HParameterValue : public HExpression<0> {
: HExpression(parameter_type, SideEffects::None(), kNoDexPc),
dex_file_(dex_file),
type_index_(type_index),
- index_(index),
- is_this_(is_this),
- can_be_null_(!is_this) {}
+ index_(index) {
+ SetPackedFlag<kFlagIsThis>(is_this);
+ SetPackedFlag<kFlagCanBeNull>(!is_this);
+ }
const DexFile& GetDexFile() const { return dex_file_; }
uint16_t GetTypeIndex() const { return type_index_; }
uint8_t GetIndex() const { return index_; }
- bool IsThis() const { return is_this_; }
+ bool IsThis() const { return GetPackedFlag<kFlagIsThis>(); }
- bool CanBeNull() const OVERRIDE { return can_be_null_; }
- void SetCanBeNull(bool can_be_null) { can_be_null_ = can_be_null; }
+ bool CanBeNull() const OVERRIDE { return GetPackedFlag<kFlagCanBeNull>(); }
+ void SetCanBeNull(bool can_be_null) { SetPackedFlag<kFlagCanBeNull>(can_be_null); }
DECLARE_INSTRUCTION(ParameterValue);
private:
+ // Whether or not the parameter value corresponds to 'this' argument.
+ static constexpr size_t kFlagIsThis = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFlagCanBeNull = kFlagIsThis + 1;
+ static constexpr size_t kNumberOfParameterValuePackedBits = kFlagCanBeNull + 1;
+ static_assert(kNumberOfParameterValuePackedBits <= kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+
const DexFile& dex_file_;
const uint16_t type_index_;
// The index of this parameter in the parameters list. Must be less
// than HGraph::number_of_in_vregs_.
const uint8_t index_;
- // Whether or not the parameter value corresponds to 'this' argument.
- const bool is_this_;
-
- bool can_be_null_;
-
DISALLOW_COPY_AND_ASSIGN(HParameterValue);
};
@@ -4735,14 +4871,14 @@ class HPhi : public HInstruction {
uint32_t dex_pc = kNoDexPc)
: HInstruction(SideEffects::None(), dex_pc),
inputs_(number_of_inputs, arena->Adapter(kArenaAllocPhiInputs)),
- reg_number_(reg_number),
- type_(ToPhiType(type)),
- // Phis are constructed live and marked dead if conflicting or unused.
- // Individual steps of SsaBuilder should assume that if a phi has been
- // marked dead, it can be ignored and will be removed by SsaPhiElimination.
- is_live_(true),
- can_be_null_(true) {
- DCHECK_NE(type_, Primitive::kPrimVoid);
+ reg_number_(reg_number) {
+ SetPackedField<TypeField>(ToPhiType(type));
+ DCHECK_NE(GetType(), Primitive::kPrimVoid);
+ // Phis are constructed live and marked dead if conflicting or unused.
+ // Individual steps of SsaBuilder should assume that if a phi has been
+ // marked dead, it can be ignored and will be removed by SsaPhiElimination.
+ SetPackedFlag<kFlagIsLive>(true);
+ SetPackedFlag<kFlagCanBeNull>(true);
}
// Returns a type equivalent to the given `type`, but that a `HPhi` can hold.
@@ -4765,27 +4901,27 @@ class HPhi : public HInstruction {
void AddInput(HInstruction* input);
void RemoveInputAt(size_t index);
- Primitive::Type GetType() const OVERRIDE { return type_; }
+ Primitive::Type GetType() const OVERRIDE { return GetPackedField<TypeField>(); }
void SetType(Primitive::Type new_type) {
// Make sure that only valid type changes occur. The following are allowed:
// (1) int -> float/ref (primitive type propagation),
// (2) long -> double (primitive type propagation).
- DCHECK(type_ == new_type ||
- (type_ == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) ||
- (type_ == Primitive::kPrimInt && new_type == Primitive::kPrimNot) ||
- (type_ == Primitive::kPrimLong && new_type == Primitive::kPrimDouble));
- type_ = new_type;
+ DCHECK(GetType() == new_type ||
+ (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) ||
+ (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimNot) ||
+ (GetType() == Primitive::kPrimLong && new_type == Primitive::kPrimDouble));
+ SetPackedField<TypeField>(new_type);
}
- bool CanBeNull() const OVERRIDE { return can_be_null_; }
- void SetCanBeNull(bool can_be_null) { can_be_null_ = can_be_null; }
+ bool CanBeNull() const OVERRIDE { return GetPackedFlag<kFlagCanBeNull>(); }
+ void SetCanBeNull(bool can_be_null) { SetPackedFlag<kFlagCanBeNull>(can_be_null); }
uint32_t GetRegNumber() const { return reg_number_; }
- void SetDead() { is_live_ = false; }
- void SetLive() { is_live_ = true; }
- bool IsDead() const { return !is_live_; }
- bool IsLive() const { return is_live_; }
+ void SetDead() { SetPackedFlag<kFlagIsLive>(false); }
+ void SetLive() { SetPackedFlag<kFlagIsLive>(true); }
+ bool IsDead() const { return !IsLive(); }
+ bool IsLive() const { return GetPackedFlag<kFlagIsLive>(); }
bool IsVRegEquivalentOf(HInstruction* other) const {
return other != nullptr
@@ -4820,11 +4956,17 @@ class HPhi : public HInstruction {
}
private:
+ static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
+ static constexpr size_t kFlagIsLive = kFieldType + kFieldTypeSize;
+ static constexpr size_t kFlagCanBeNull = kFlagIsLive + 1;
+ static constexpr size_t kNumberOfPhiPackedBits = kFlagCanBeNull + 1;
+ static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>;
+
ArenaVector<HUserRecord<HInstruction*> > inputs_;
const uint32_t reg_number_;
- Primitive::Type type_;
- bool is_live_;
- bool can_be_null_;
DISALLOW_COPY_AND_ASSIGN(HPhi);
};
@@ -4963,8 +5105,8 @@ class HInstanceFieldSet : public HTemplateInstruction<2> {
field_idx,
declaring_class_def_index,
dex_file,
- dex_cache),
- value_can_be_null_(true) {
+ dex_cache) {
+ SetPackedFlag<kFlagValueCanBeNull>(true);
SetRawInputAt(0, object);
SetRawInputAt(1, value);
}
@@ -4978,14 +5120,18 @@ class HInstanceFieldSet : public HTemplateInstruction<2> {
Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
bool IsVolatile() const { return field_info_.IsVolatile(); }
HInstruction* GetValue() const { return InputAt(1); }
- bool GetValueCanBeNull() const { return value_can_be_null_; }
- void ClearValueCanBeNull() { value_can_be_null_ = false; }
+ bool GetValueCanBeNull() const { return GetPackedFlag<kFlagValueCanBeNull>(); }
+ void ClearValueCanBeNull() { SetPackedFlag<kFlagValueCanBeNull>(false); }
DECLARE_INSTRUCTION(InstanceFieldSet);
private:
+ static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits;
+ static constexpr size_t kNumberOfInstanceFieldSetPackedBits = kFlagValueCanBeNull + 1;
+ static_assert(kNumberOfInstanceFieldSetPackedBits <= kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+
const FieldInfo field_info_;
- bool value_can_be_null_;
DISALLOW_COPY_AND_ASSIGN(HInstanceFieldSet);
};
@@ -5054,11 +5200,11 @@ class HArraySet : public HTemplateInstruction<3> {
SideEffects::ArrayWriteOfType(expected_component_type).Union(
SideEffectsForArchRuntimeCalls(value->GetType())).Union(
additional_side_effects),
- dex_pc),
- expected_component_type_(expected_component_type),
- needs_type_check_(value->GetType() == Primitive::kPrimNot),
- value_can_be_null_(true),
- static_type_of_array_is_object_array_(false) {
+ dex_pc) {
+ SetPackedField<ExpectedComponentTypeField>(expected_component_type);
+ SetPackedFlag<kFlagNeedsTypeCheck>(value->GetType() == Primitive::kPrimNot);
+ SetPackedFlag<kFlagValueCanBeNull>(true);
+ SetPackedFlag<kFlagStaticTypeOfArrayIsObjectArray>(false);
SetRawInputAt(0, array);
SetRawInputAt(1, index);
SetRawInputAt(2, value);
@@ -5066,11 +5212,11 @@ class HArraySet : public HTemplateInstruction<3> {
bool NeedsEnvironment() const OVERRIDE {
// We call a runtime method to throw ArrayStoreException.
- return needs_type_check_;
+ return NeedsTypeCheck();
}
// Can throw ArrayStoreException.
- bool CanThrow() const OVERRIDE { return needs_type_check_; }
+ bool CanThrow() const OVERRIDE { return NeedsTypeCheck(); }
bool CanDoImplicitNullCheckOn(HInstruction* obj ATTRIBUTE_UNUSED) const OVERRIDE {
// TODO: Same as for ArrayGet.
@@ -5078,20 +5224,22 @@ class HArraySet : public HTemplateInstruction<3> {
}
void ClearNeedsTypeCheck() {
- needs_type_check_ = false;
+ SetPackedFlag<kFlagNeedsTypeCheck>(false);
}
void ClearValueCanBeNull() {
- value_can_be_null_ = false;
+ SetPackedFlag<kFlagValueCanBeNull>(false);
}
void SetStaticTypeOfArrayIsObjectArray() {
- static_type_of_array_is_object_array_ = true;
+ SetPackedFlag<kFlagStaticTypeOfArrayIsObjectArray>(true);
}
- bool GetValueCanBeNull() const { return value_can_be_null_; }
- bool NeedsTypeCheck() const { return needs_type_check_; }
- bool StaticTypeOfArrayIsObjectArray() const { return static_type_of_array_is_object_array_; }
+ bool GetValueCanBeNull() const { return GetPackedFlag<kFlagValueCanBeNull>(); }
+ bool NeedsTypeCheck() const { return GetPackedFlag<kFlagNeedsTypeCheck>(); }
+ bool StaticTypeOfArrayIsObjectArray() const {
+ return GetPackedFlag<kFlagStaticTypeOfArrayIsObjectArray>();
+ }
HInstruction* GetArray() const { return InputAt(0); }
HInstruction* GetIndex() const { return InputAt(1); }
@@ -5105,11 +5253,11 @@ class HArraySet : public HTemplateInstruction<3> {
Primitive::Type value_type = GetValue()->GetType();
return ((value_type == Primitive::kPrimFloat) || (value_type == Primitive::kPrimDouble))
? value_type
- : expected_component_type_;
+ : GetRawExpectedComponentType();
}
Primitive::Type GetRawExpectedComponentType() const {
- return expected_component_type_;
+ return GetPackedField<ExpectedComponentTypeField>();
}
static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type value_type) {
@@ -5119,12 +5267,20 @@ class HArraySet : public HTemplateInstruction<3> {
DECLARE_INSTRUCTION(ArraySet);
private:
- const Primitive::Type expected_component_type_;
- bool needs_type_check_;
- bool value_can_be_null_;
+ static constexpr size_t kFieldExpectedComponentType = kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldExpectedComponentTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
+ static constexpr size_t kFlagNeedsTypeCheck =
+ kFieldExpectedComponentType + kFieldExpectedComponentTypeSize;
+ static constexpr size_t kFlagValueCanBeNull = kFlagNeedsTypeCheck + 1;
// Cached information for the reference_type_info_ so that codegen
// does not need to inspect the static type.
- bool static_type_of_array_is_object_array_;
+ static constexpr size_t kFlagStaticTypeOfArrayIsObjectArray = kFlagValueCanBeNull + 1;
+ static constexpr size_t kNumberOfArraySetPackedBits =
+ kFlagStaticTypeOfArrayIsObjectArray + 1;
+ static_assert(kNumberOfArraySetPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using ExpectedComponentTypeField =
+ BitField<Primitive::Type, kFieldExpectedComponentType, kFieldExpectedComponentTypeSize>;
DISALLOW_COPY_AND_ASSIGN(HArraySet);
};
@@ -5234,14 +5390,15 @@ class HLoadClass : public HExpression<1> {
: HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc),
type_index_(type_index),
dex_file_(dex_file),
- is_referrers_class_(is_referrers_class),
- generate_clinit_check_(false),
- needs_access_check_(needs_access_check),
- is_in_dex_cache_(is_in_dex_cache),
loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) {
// Referrers class should not need access check. We never inline unverified
// methods so we can't possibly end up in this situation.
- DCHECK(!is_referrers_class_ || !needs_access_check_);
+ DCHECK(!is_referrers_class || !needs_access_check);
+
+ SetPackedFlag<kFlagIsReferrersClass>(is_referrers_class);
+ SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check);
+ SetPackedFlag<kFlagIsInDexCache>(is_in_dex_cache);
+ SetPackedFlag<kFlagGenerateClInitCheck>(false);
SetRawInputAt(0, current_method);
}
@@ -5252,39 +5409,31 @@ class HLoadClass : public HExpression<1> {
// Whether or not we need to generate the clinit check is processed in
// prepare_for_register_allocator based on existing HInvokes and HClinitChecks.
return other->AsLoadClass()->type_index_ == type_index_ &&
- other->AsLoadClass()->needs_access_check_ == needs_access_check_;
+ other->AsLoadClass()->GetPackedFields() == GetPackedFields();
}
size_t ComputeHashCode() const OVERRIDE { return type_index_; }
uint16_t GetTypeIndex() const { return type_index_; }
- bool IsReferrersClass() const { return is_referrers_class_; }
bool CanBeNull() const OVERRIDE { return false; }
bool NeedsEnvironment() const OVERRIDE {
return CanCallRuntime();
}
- bool MustGenerateClinitCheck() const {
- return generate_clinit_check_;
- }
-
void SetMustGenerateClinitCheck(bool generate_clinit_check) {
// The entrypoint the code generator is going to call does not do
// clinit of the class.
DCHECK(!NeedsAccessCheck());
- generate_clinit_check_ = generate_clinit_check;
+ SetPackedFlag<kFlagGenerateClInitCheck>(generate_clinit_check);
}
bool CanCallRuntime() const {
return MustGenerateClinitCheck() ||
- (!is_referrers_class_ && !is_in_dex_cache_) ||
- needs_access_check_;
+ (!IsReferrersClass() && !IsInDexCache()) ||
+ NeedsAccessCheck();
}
- bool NeedsAccessCheck() const {
- return needs_access_check_;
- }
bool CanThrow() const OVERRIDE {
return CanCallRuntime();
@@ -5302,25 +5451,31 @@ class HLoadClass : public HExpression<1> {
const DexFile& GetDexFile() { return dex_file_; }
- bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return !is_referrers_class_; }
+ bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return !IsReferrersClass(); }
static SideEffects SideEffectsForArchRuntimeCalls() {
return SideEffects::CanTriggerGC();
}
- bool IsInDexCache() const { return is_in_dex_cache_; }
+ bool IsReferrersClass() const { return GetPackedFlag<kFlagIsReferrersClass>(); }
+ bool NeedsAccessCheck() const { return GetPackedFlag<kFlagNeedsAccessCheck>(); }
+ bool IsInDexCache() const { return GetPackedFlag<kFlagIsInDexCache>(); }
+ bool MustGenerateClinitCheck() const { return GetPackedFlag<kFlagGenerateClInitCheck>(); }
DECLARE_INSTRUCTION(LoadClass);
private:
- const uint16_t type_index_;
- const DexFile& dex_file_;
- const bool is_referrers_class_;
+ static constexpr size_t kFlagIsReferrersClass = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFlagNeedsAccessCheck = kFlagIsReferrersClass + 1;
+ static constexpr size_t kFlagIsInDexCache = kFlagNeedsAccessCheck + 1;
// Whether this instruction must generate the initialization check.
// Used for code generation.
- bool generate_clinit_check_;
- const bool needs_access_check_;
- const bool is_in_dex_cache_;
+ static constexpr size_t kFlagGenerateClInitCheck = kFlagIsInDexCache + 1;
+ static constexpr size_t kNumberOfLoadClassPackedBits = kFlagGenerateClInitCheck + 1;
+ static_assert(kNumberOfLoadClassPackedBits < kMaxNumberOfPackedBits, "Too many packed fields.");
+
+ const uint16_t type_index_;
+ const DexFile& dex_file_;
ReferenceTypeInfo loaded_class_rti_;
@@ -5334,8 +5489,8 @@ class HLoadString : public HExpression<1> {
uint32_t dex_pc,
bool is_in_dex_cache)
: HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc),
- string_index_(string_index),
- is_in_dex_cache_(is_in_dex_cache) {
+ string_index_(string_index) {
+ SetPackedFlag<kFlagIsInDexCache>(is_in_dex_cache);
SetRawInputAt(0, current_method);
}
@@ -5354,18 +5509,22 @@ class HLoadString : public HExpression<1> {
bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return true; }
bool CanBeNull() const OVERRIDE { return false; }
- bool IsInDexCache() const { return is_in_dex_cache_; }
bool CanThrow() const OVERRIDE { return !IsInDexCache(); }
static SideEffects SideEffectsForArchRuntimeCalls() {
return SideEffects::CanTriggerGC();
}
+ bool IsInDexCache() const { return GetPackedFlag<kFlagIsInDexCache>(); }
+
DECLARE_INSTRUCTION(LoadString);
private:
+ static constexpr size_t kFlagIsInDexCache = kNumberOfExpressionPackedBits;
+ static constexpr size_t kNumberOfLoadStringPackedBits = kFlagIsInDexCache + 1;
+ static_assert(kNumberOfLoadStringPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+
const uint32_t string_index_;
- const bool is_in_dex_cache_;
DISALLOW_COPY_AND_ASSIGN(HLoadString);
};
@@ -5472,8 +5631,8 @@ class HStaticFieldSet : public HTemplateInstruction<2> {
field_idx,
declaring_class_def_index,
dex_file,
- dex_cache),
- value_can_be_null_(true) {
+ dex_cache) {
+ SetPackedFlag<kFlagValueCanBeNull>(true);
SetRawInputAt(0, cls);
SetRawInputAt(1, value);
}
@@ -5484,14 +5643,18 @@ class HStaticFieldSet : public HTemplateInstruction<2> {
bool IsVolatile() const { return field_info_.IsVolatile(); }
HInstruction* GetValue() const { return InputAt(1); }
- bool GetValueCanBeNull() const { return value_can_be_null_; }
- void ClearValueCanBeNull() { value_can_be_null_ = false; }
+ bool GetValueCanBeNull() const { return GetPackedFlag<kFlagValueCanBeNull>(); }
+ void ClearValueCanBeNull() { SetPackedFlag<kFlagValueCanBeNull>(false); }
DECLARE_INSTRUCTION(StaticFieldSet);
private:
+ static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits;
+ static constexpr size_t kNumberOfStaticFieldSetPackedBits = kFlagValueCanBeNull + 1;
+ static_assert(kNumberOfStaticFieldSetPackedBits <= kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+
const FieldInfo field_info_;
- bool value_can_be_null_;
DISALLOW_COPY_AND_ASSIGN(HStaticFieldSet);
};
@@ -5529,8 +5692,8 @@ class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> {
uint32_t field_index,
uint32_t dex_pc)
: HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc),
- field_type_(field_type),
field_index_(field_index) {
+ SetPackedField<FieldTypeField>(field_type);
DCHECK_EQ(field_type, value->GetType());
SetRawInputAt(0, obj);
SetRawInputAt(1, value);
@@ -5539,13 +5702,21 @@ class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> {
bool NeedsEnvironment() const OVERRIDE { return true; }
bool CanThrow() const OVERRIDE { return true; }
- Primitive::Type GetFieldType() const { return field_type_; }
+ Primitive::Type GetFieldType() const { return GetPackedField<FieldTypeField>(); }
uint32_t GetFieldIndex() const { return field_index_; }
DECLARE_INSTRUCTION(UnresolvedInstanceFieldSet);
private:
- const Primitive::Type field_type_;
+ static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldFieldTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
+ static constexpr size_t kNumberOfUnresolvedStaticFieldSetPackedBits =
+ kFieldFieldType + kFieldFieldTypeSize;
+ static_assert(kNumberOfUnresolvedStaticFieldSetPackedBits <= HInstruction::kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+ using FieldTypeField = BitField<Primitive::Type, kFieldFieldType, kFieldFieldTypeSize>;
+
const uint32_t field_index_;
DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldSet);
@@ -5581,8 +5752,8 @@ class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> {
uint32_t field_index,
uint32_t dex_pc)
: HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc),
- field_type_(field_type),
field_index_(field_index) {
+ SetPackedField<FieldTypeField>(field_type);
DCHECK_EQ(field_type, value->GetType());
SetRawInputAt(0, value);
}
@@ -5590,13 +5761,21 @@ class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> {
bool NeedsEnvironment() const OVERRIDE { return true; }
bool CanThrow() const OVERRIDE { return true; }
- Primitive::Type GetFieldType() const { return field_type_; }
+ Primitive::Type GetFieldType() const { return GetPackedField<FieldTypeField>(); }
uint32_t GetFieldIndex() const { return field_index_; }
DECLARE_INSTRUCTION(UnresolvedStaticFieldSet);
private:
- const Primitive::Type field_type_;
+ static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldFieldTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
+ static constexpr size_t kNumberOfUnresolvedStaticFieldSetPackedBits =
+ kFieldFieldType + kFieldFieldTypeSize;
+ static_assert(kNumberOfUnresolvedStaticFieldSetPackedBits <= HInstruction::kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+ using FieldTypeField = BitField<Primitive::Type, kFieldFieldType, kFieldFieldTypeSize>;
+
const uint32_t field_index_;
DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldSet);
@@ -5660,7 +5839,8 @@ enum class TypeCheckKind {
kAbstractClassCheck, // Can just walk the super class chain, starting one up.
kInterfaceCheck, // No optimization yet when checking against an interface.
kArrayObjectCheck, // Can just check if the array is not primitive.
- kArrayCheck // No optimization yet when checking against a generic array.
+ kArrayCheck, // No optimization yet when checking against a generic array.
+ kLast = kArrayCheck
};
std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs);
@@ -5673,9 +5853,9 @@ class HInstanceOf : public HExpression<2> {
uint32_t dex_pc)
: HExpression(Primitive::kPrimBoolean,
SideEffectsForArchRuntimeCalls(check_kind),
- dex_pc),
- check_kind_(check_kind),
- must_do_null_check_(true) {
+ dex_pc) {
+ SetPackedField<TypeCheckKindField>(check_kind);
+ SetPackedFlag<kFlagMustDoNullCheck>(true);
SetRawInputAt(0, object);
SetRawInputAt(1, constant);
}
@@ -5687,16 +5867,14 @@ class HInstanceOf : public HExpression<2> {
}
bool NeedsEnvironment() const OVERRIDE {
- return CanCallRuntime(check_kind_);
+ return CanCallRuntime(GetTypeCheckKind());
}
- bool IsExactCheck() const { return check_kind_ == TypeCheckKind::kExactCheck; }
-
- TypeCheckKind GetTypeCheckKind() const { return check_kind_; }
-
// Used only in code generation.
- bool MustDoNullCheck() const { return must_do_null_check_; }
- void ClearMustDoNullCheck() { must_do_null_check_ = false; }
+ bool MustDoNullCheck() const { return GetPackedFlag<kFlagMustDoNullCheck>(); }
+ void ClearMustDoNullCheck() { SetPackedFlag<kFlagMustDoNullCheck>(false); }
+ TypeCheckKind GetTypeCheckKind() const { return GetPackedField<TypeCheckKindField>(); }
+ bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; }
static bool CanCallRuntime(TypeCheckKind check_kind) {
// Mips currently does runtime calls for any other checks.
@@ -5710,8 +5888,13 @@ class HInstanceOf : public HExpression<2> {
DECLARE_INSTRUCTION(InstanceOf);
private:
- const TypeCheckKind check_kind_;
- bool must_do_null_check_;
+ static constexpr size_t kFieldTypeCheckKind = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFieldTypeCheckKindSize =
+ MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast));
+ static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize;
+ static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagMustDoNullCheck + 1;
+ static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>;
DISALLOW_COPY_AND_ASSIGN(HInstanceOf);
};
@@ -5720,28 +5903,35 @@ class HBoundType : public HExpression<1> {
public:
HBoundType(HInstruction* input, uint32_t dex_pc = kNoDexPc)
: HExpression(Primitive::kPrimNot, SideEffects::None(), dex_pc),
- upper_bound_(ReferenceTypeInfo::CreateInvalid()),
- upper_can_be_null_(true),
- can_be_null_(true) {
+ upper_bound_(ReferenceTypeInfo::CreateInvalid()) {
+ SetPackedFlag<kFlagUpperCanBeNull>(true);
+ SetPackedFlag<kFlagCanBeNull>(true);
DCHECK_EQ(input->GetType(), Primitive::kPrimNot);
SetRawInputAt(0, input);
}
// {Get,Set}Upper* should only be used in reference type propagation.
const ReferenceTypeInfo& GetUpperBound() const { return upper_bound_; }
- bool GetUpperCanBeNull() const { return upper_can_be_null_; }
+ bool GetUpperCanBeNull() const { return GetPackedFlag<kFlagUpperCanBeNull>(); }
void SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be_null);
void SetCanBeNull(bool can_be_null) {
- DCHECK(upper_can_be_null_ || !can_be_null);
- can_be_null_ = can_be_null;
+ DCHECK(GetUpperCanBeNull() || !can_be_null);
+ SetPackedFlag<kFlagCanBeNull>(can_be_null);
}
- bool CanBeNull() const OVERRIDE { return can_be_null_; }
+ bool CanBeNull() const OVERRIDE { return GetPackedFlag<kFlagCanBeNull>(); }
DECLARE_INSTRUCTION(BoundType);
private:
+ // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this
+ // is false then CanBeNull() cannot be true).
+ static constexpr size_t kFlagUpperCanBeNull = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFlagCanBeNull = kFlagUpperCanBeNull + 1;
+ static constexpr size_t kNumberOfBoundTypePackedBits = kFlagCanBeNull + 1;
+ static_assert(kNumberOfBoundTypePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+
// Encodes the most upper class that this instruction can have. In other words
// it is always the case that GetUpperBound().IsSupertypeOf(GetReferenceType()).
// It is used to bound the type in cases like:
@@ -5749,10 +5939,6 @@ class HBoundType : public HExpression<1> {
// // uper_bound_ will be ClassX
// }
ReferenceTypeInfo upper_bound_;
- // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this
- // is false then can_be_null_ cannot be true).
- bool upper_can_be_null_;
- bool can_be_null_;
DISALLOW_COPY_AND_ASSIGN(HBoundType);
};
@@ -5763,9 +5949,9 @@ class HCheckCast : public HTemplateInstruction<2> {
HLoadClass* constant,
TypeCheckKind check_kind,
uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc),
- check_kind_(check_kind),
- must_do_null_check_(true) {
+ : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) {
+ SetPackedField<TypeCheckKindField>(check_kind);
+ SetPackedFlag<kFlagMustDoNullCheck>(true);
SetRawInputAt(0, object);
SetRawInputAt(1, constant);
}
@@ -5783,17 +5969,21 @@ class HCheckCast : public HTemplateInstruction<2> {
bool CanThrow() const OVERRIDE { return true; }
- bool MustDoNullCheck() const { return must_do_null_check_; }
- void ClearMustDoNullCheck() { must_do_null_check_ = false; }
- TypeCheckKind GetTypeCheckKind() const { return check_kind_; }
-
- bool IsExactCheck() const { return check_kind_ == TypeCheckKind::kExactCheck; }
+ bool MustDoNullCheck() const { return GetPackedFlag<kFlagMustDoNullCheck>(); }
+ void ClearMustDoNullCheck() { SetPackedFlag<kFlagMustDoNullCheck>(false); }
+ TypeCheckKind GetTypeCheckKind() const { return GetPackedField<TypeCheckKindField>(); }
+ bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; }
DECLARE_INSTRUCTION(CheckCast);
private:
- const TypeCheckKind check_kind_;
- bool must_do_null_check_;
+ static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldTypeCheckKindSize =
+ MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast));
+ static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize;
+ static constexpr size_t kNumberOfCheckCastPackedBits = kFlagMustDoNullCheck + 1;
+ static_assert(kNumberOfCheckCastPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>;
DISALLOW_COPY_AND_ASSIGN(HCheckCast);
};
@@ -5802,30 +5992,40 @@ class HMemoryBarrier : public HTemplateInstruction<0> {
public:
explicit HMemoryBarrier(MemBarrierKind barrier_kind, uint32_t dex_pc = kNoDexPc)
: HTemplateInstruction(
- SideEffects::AllWritesAndReads(), dex_pc), // Assume write/read on all fields/arrays.
- barrier_kind_(barrier_kind) {}
+ SideEffects::AllWritesAndReads(), dex_pc) { // Assume write/read on all fields/arrays.
+ SetPackedField<BarrierKindField>(barrier_kind);
+ }
- MemBarrierKind GetBarrierKind() { return barrier_kind_; }
+ MemBarrierKind GetBarrierKind() { return GetPackedField<BarrierKindField>(); }
DECLARE_INSTRUCTION(MemoryBarrier);
private:
- const MemBarrierKind barrier_kind_;
+ static constexpr size_t kFieldBarrierKind = HInstruction::kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldBarrierKindSize =
+ MinimumBitsToStore(static_cast<size_t>(kLastBarrierKind));
+ static constexpr size_t kNumberOfMemoryBarrierPackedBits =
+ kFieldBarrierKind + kFieldBarrierKindSize;
+ static_assert(kNumberOfMemoryBarrierPackedBits <= kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+ using BarrierKindField = BitField<MemBarrierKind, kFieldBarrierKind, kFieldBarrierKindSize>;
DISALLOW_COPY_AND_ASSIGN(HMemoryBarrier);
};
class HMonitorOperation : public HTemplateInstruction<1> {
public:
- enum OperationKind {
+ enum class OperationKind {
kEnter,
kExit,
+ kLast = kExit
};
HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc)
: HTemplateInstruction(
- SideEffects::AllExceptGCDependency(), dex_pc), // Assume write/read on all fields/arrays.
- kind_(kind) {
+ SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays.
+ dex_pc) {
+ SetPackedField<OperationKindField>(kind);
SetRawInputAt(0, object);
}
@@ -5839,13 +6039,20 @@ class HMonitorOperation : public HTemplateInstruction<1> {
return IsEnter();
}
-
- bool IsEnter() const { return kind_ == kEnter; }
+ OperationKind GetOperationKind() const { return GetPackedField<OperationKindField>(); }
+ bool IsEnter() const { return GetOperationKind() == OperationKind::kEnter; }
DECLARE_INSTRUCTION(MonitorOperation);
private:
- const OperationKind kind_;
+ static constexpr size_t kFieldOperationKind = HInstruction::kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldOperationKindSize =
+ MinimumBitsToStore(static_cast<size_t>(OperationKind::kLast));
+ static constexpr size_t kNumberOfMonitorOperationPackedBits =
+ kFieldOperationKind + kFieldOperationKindSize;
+ static_assert(kNumberOfMonitorOperationPackedBits <= HInstruction::kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+ using OperationKindField = BitField<OperationKind, kFieldOperationKind, kFieldOperationKindSize>;
private:
DISALLOW_COPY_AND_ASSIGN(HMonitorOperation);
@@ -6022,6 +6229,9 @@ class HParallelMove : public HTemplateInstruction<0> {
} // namespace art
+#if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64)
+#include "nodes_shared.h"
+#endif
#ifdef ART_ENABLE_CODEGEN_arm
#include "nodes_arm.h"
#endif
diff --git a/compiler/optimizing/nodes_arm64.h b/compiler/optimizing/nodes_arm64.h
index 445cdab191..75a71e78b8 100644
--- a/compiler/optimizing/nodes_arm64.h
+++ b/compiler/optimizing/nodes_arm64.h
@@ -118,38 +118,64 @@ class HArm64IntermediateAddress : public HExpression<2> {
DISALLOW_COPY_AND_ASSIGN(HArm64IntermediateAddress);
};
-class HArm64MultiplyAccumulate : public HExpression<3> {
+class HArm64BitwiseNegatedRight : public HBinaryOperation {
public:
- HArm64MultiplyAccumulate(Primitive::Type type,
- InstructionKind op,
- HInstruction* accumulator,
- HInstruction* mul_left,
- HInstruction* mul_right,
- uint32_t dex_pc = kNoDexPc)
- : HExpression(type, SideEffects::None(), dex_pc), op_kind_(op) {
- SetRawInputAt(kInputAccumulatorIndex, accumulator);
- SetRawInputAt(kInputMulLeftIndex, mul_left);
- SetRawInputAt(kInputMulRightIndex, mul_right);
+ HArm64BitwiseNegatedRight(Primitive::Type result_type,
+ InstructionKind op,
+ HInstruction* left,
+ HInstruction* right,
+ uint32_t dex_pc = kNoDexPc)
+ : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc),
+ op_kind_(op) {
+ DCHECK(op == HInstruction::kAnd || op == HInstruction::kOr || op == HInstruction::kXor) << op;
}
- static constexpr int kInputAccumulatorIndex = 0;
- static constexpr int kInputMulLeftIndex = 1;
- static constexpr int kInputMulRightIndex = 2;
+ template <typename T, typename U>
+ auto Compute(T x, U y) const -> decltype(x & ~y) {
+ static_assert(std::is_same<decltype(x & ~y), decltype(x | ~y)>::value &&
+ std::is_same<decltype(x & ~y), decltype(x ^ ~y)>::value,
+ "Inconsistent negated bitwise types");
+ switch (op_kind_) {
+ case HInstruction::kAnd:
+ return x & ~y;
+ case HInstruction::kOr:
+ return x | ~y;
+ case HInstruction::kXor:
+ return x ^ ~y;
+ default:
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+ }
+ }
- bool CanBeMoved() const OVERRIDE { return true; }
- bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
- return op_kind_ == other->AsArm64MultiplyAccumulate()->op_kind_;
+ HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
+ return GetBlock()->GetGraph()->GetLongConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
+ HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for float values";
+ UNREACHABLE();
+ }
+ HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
+ HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for double values";
+ UNREACHABLE();
}
InstructionKind GetOpKind() const { return op_kind_; }
- DECLARE_INSTRUCTION(Arm64MultiplyAccumulate);
+ DECLARE_INSTRUCTION(Arm64BitwiseNegatedRight);
private:
- // Indicates if this is a MADD or MSUB.
- InstructionKind op_kind_;
+ // Specifies the bitwise operation, which will be then negated.
+ const InstructionKind op_kind_;
- DISALLOW_COPY_AND_ASSIGN(HArm64MultiplyAccumulate);
+ DISALLOW_COPY_AND_ASSIGN(HArm64BitwiseNegatedRight);
};
} // namespace art
diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h
new file mode 100644
index 0000000000..b04b622838
--- /dev/null
+++ b/compiler/optimizing/nodes_shared.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_NODES_SHARED_H_
+#define ART_COMPILER_OPTIMIZING_NODES_SHARED_H_
+
+namespace art {
+
+class HMultiplyAccumulate : public HExpression<3> {
+ public:
+ HMultiplyAccumulate(Primitive::Type type,
+ InstructionKind op,
+ HInstruction* accumulator,
+ HInstruction* mul_left,
+ HInstruction* mul_right,
+ uint32_t dex_pc = kNoDexPc)
+ : HExpression(type, SideEffects::None(), dex_pc), op_kind_(op) {
+ SetRawInputAt(kInputAccumulatorIndex, accumulator);
+ SetRawInputAt(kInputMulLeftIndex, mul_left);
+ SetRawInputAt(kInputMulRightIndex, mul_right);
+ }
+
+ static constexpr int kInputAccumulatorIndex = 0;
+ static constexpr int kInputMulLeftIndex = 1;
+ static constexpr int kInputMulRightIndex = 2;
+
+ bool CanBeMoved() const OVERRIDE { return true; }
+ bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
+ return op_kind_ == other->AsMultiplyAccumulate()->op_kind_;
+ }
+
+ InstructionKind GetOpKind() const { return op_kind_; }
+
+ DECLARE_INSTRUCTION(MultiplyAccumulate);
+
+ private:
+ // Indicates if this is a MADD or MSUB.
+ const InstructionKind op_kind_;
+
+ DISALLOW_COPY_AND_ASSIGN(HMultiplyAccumulate);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_NODES_SHARED_H_
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index b1891c979e..5a9f2583fd 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -60,6 +60,7 @@
#include "induction_var_analysis.h"
#include "inliner.h"
#include "instruction_simplifier.h"
+#include "instruction_simplifier_arm.h"
#include "intrinsics.h"
#include "jit/debugger_interface.h"
#include "jit/jit_code_cache.h"
@@ -438,7 +439,10 @@ static void RunArchOptimizations(InstructionSet instruction_set,
case kThumb2:
case kArm: {
arm::DexCacheArrayFixups* fixups = new (arena) arm::DexCacheArrayFixups(graph, stats);
+ arm::InstructionSimplifierArm* simplifier =
+ new (arena) arm::InstructionSimplifierArm(graph, stats);
HOptimization* arm_optimizations[] = {
+ simplifier,
fixups
};
RunOptimizations(arm_optimizations, arraysize(arm_optimizations), pass_observer);
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index 4784de1380..54cbdf8b66 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -63,8 +63,7 @@ void StackMapStream::EndStackMapEntry() {
void StackMapStream::AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value) {
if (kind != DexRegisterLocation::Kind::kNone) {
// Ensure we only use non-compressed location kind at this stage.
- DCHECK(DexRegisterLocation::IsShortLocationKind(kind))
- << DexRegisterLocation::PrettyDescriptor(kind);
+ DCHECK(DexRegisterLocation::IsShortLocationKind(kind)) << kind;
DexRegisterLocation location(kind, value);
// Look for Dex register `location` in the location catalog (using the
@@ -257,6 +256,7 @@ void StackMapStream::FillIn(MemoryRegion region) {
// Ensure we reached the end of the Dex registers location_catalog.
DCHECK_EQ(location_catalog_offset, dex_register_location_catalog_region.size());
+ ArenaBitVector empty_bitmask(allocator_, 0, /* expandable */ false);
uintptr_t next_dex_register_map_offset = 0;
uintptr_t next_inline_info_offset = 0;
for (size_t i = 0, e = stack_maps_.size(); i < e; ++i) {
@@ -268,6 +268,9 @@ void StackMapStream::FillIn(MemoryRegion region) {
stack_map.SetRegisterMask(stack_map_encoding_, entry.register_mask);
if (entry.sp_mask != nullptr) {
stack_map.SetStackMask(stack_map_encoding_, *entry.sp_mask);
+ } else {
+ // The MemoryRegion does not have to be zeroed, so make sure we clear the bits.
+ stack_map.SetStackMask(stack_map_encoding_, empty_bitmask);
}
if (entry.num_dex_registers == 0 || (entry.live_dex_registers_mask->NumSetBits() == 0)) {
@@ -344,6 +347,11 @@ void StackMapStream::FillIn(MemoryRegion region) {
}
}
}
+
+ // Verify all written data in debug build.
+ if (kIsDebugBuild) {
+ CheckCodeInfo(region);
+ }
}
void StackMapStream::FillInDexRegisterMap(DexRegisterMap dex_register_map,
@@ -423,4 +431,90 @@ bool StackMapStream::HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEn
return true;
}
+// Helper for CheckCodeInfo - check that register map has the expected content.
+void StackMapStream::CheckDexRegisterMap(const CodeInfo& code_info,
+ const DexRegisterMap& dex_register_map,
+ size_t num_dex_registers,
+ BitVector* live_dex_registers_mask,
+ size_t dex_register_locations_index) const {
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ for (size_t reg = 0; reg < num_dex_registers; reg++) {
+ // Find the location we tried to encode.
+ DexRegisterLocation expected = DexRegisterLocation::None();
+ if (live_dex_registers_mask->IsBitSet(reg)) {
+ size_t catalog_index = dex_register_locations_[dex_register_locations_index++];
+ expected = location_catalog_entries_[catalog_index];
+ }
+ // Compare to the seen location.
+ if (expected.GetKind() == DexRegisterLocation::Kind::kNone) {
+ DCHECK(!dex_register_map.IsValid() || !dex_register_map.IsDexRegisterLive(reg));
+ } else {
+ DCHECK(dex_register_map.IsDexRegisterLive(reg));
+ DexRegisterLocation seen = dex_register_map.GetDexRegisterLocation(
+ reg, num_dex_registers, code_info, encoding);
+ DCHECK_EQ(expected.GetKind(), seen.GetKind());
+ DCHECK_EQ(expected.GetValue(), seen.GetValue());
+ }
+ }
+ if (num_dex_registers == 0) {
+ DCHECK(!dex_register_map.IsValid());
+ }
+}
+
+// Check that all StackMapStream inputs are correctly encoded by trying to read them back.
+void StackMapStream::CheckCodeInfo(MemoryRegion region) const {
+ CodeInfo code_info(region);
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ DCHECK_EQ(code_info.GetNumberOfStackMaps(), stack_maps_.size());
+ for (size_t s = 0; s < stack_maps_.size(); ++s) {
+ const StackMap stack_map = code_info.GetStackMapAt(s, encoding);
+ StackMapEntry entry = stack_maps_[s];
+
+ // Check main stack map fields.
+ DCHECK_EQ(stack_map.GetNativePcOffset(encoding), entry.native_pc_offset);
+ DCHECK_EQ(stack_map.GetDexPc(encoding), entry.dex_pc);
+ DCHECK_EQ(stack_map.GetRegisterMask(encoding), entry.register_mask);
+ MemoryRegion stack_mask = stack_map.GetStackMask(encoding);
+ if (entry.sp_mask != nullptr) {
+ DCHECK_GE(stack_mask.size_in_bits(), entry.sp_mask->GetNumberOfBits());
+ for (size_t b = 0; b < stack_mask.size_in_bits(); b++) {
+ DCHECK_EQ(stack_mask.LoadBit(b), entry.sp_mask->IsBitSet(b));
+ }
+ } else {
+ for (size_t b = 0; b < stack_mask.size_in_bits(); b++) {
+ DCHECK_EQ(stack_mask.LoadBit(b), 0u);
+ }
+ }
+
+ CheckDexRegisterMap(code_info,
+ code_info.GetDexRegisterMapOf(
+ stack_map, encoding, entry.num_dex_registers),
+ entry.num_dex_registers,
+ entry.live_dex_registers_mask,
+ entry.dex_register_locations_start_index);
+
+ // Check inline info.
+ DCHECK_EQ(stack_map.HasInlineInfo(encoding), (entry.inlining_depth != 0));
+ if (entry.inlining_depth != 0) {
+ InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
+ DCHECK_EQ(inline_info.GetDepth(), entry.inlining_depth);
+ for (size_t d = 0; d < entry.inlining_depth; ++d) {
+ size_t inline_info_index = entry.inline_infos_start_index + d;
+ DCHECK_LT(inline_info_index, inline_infos_.size());
+ InlineInfoEntry inline_entry = inline_infos_[inline_info_index];
+ DCHECK_EQ(inline_info.GetDexPcAtDepth(d), inline_entry.dex_pc);
+ DCHECK_EQ(inline_info.GetMethodIndexAtDepth(d), inline_entry.method_index);
+ DCHECK_EQ(inline_info.GetInvokeTypeAtDepth(d), inline_entry.invoke_type);
+
+ CheckDexRegisterMap(code_info,
+ code_info.GetDexRegisterMapAtDepth(
+ d, inline_info, encoding, inline_entry.num_dex_registers),
+ inline_entry.num_dex_registers,
+ inline_entry.live_dex_registers_mask,
+ inline_entry.dex_register_locations_start_index);
+ }
+ }
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index fc27a2b446..016a911424 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -167,6 +167,13 @@ class StackMapStream : public ValueObject {
const BitVector& live_dex_registers_mask,
uint32_t start_index_in_dex_register_locations) const;
+ void CheckDexRegisterMap(const CodeInfo& code_info,
+ const DexRegisterMap& dex_register_map,
+ size_t num_dex_registers,
+ BitVector* live_dex_registers_mask,
+ size_t dex_register_locations_index) const;
+ void CheckCodeInfo(MemoryRegion region) const;
+
ArenaAllocator* allocator_;
ArenaVector<StackMapEntry> stack_maps_;
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index cac12d1920..dfcb4bcaec 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -217,7 +217,7 @@ NO_RETURN static void Usage(const char* fmt, ...) {
UsageError(" --image=<file.art>: specifies an output image filename.");
UsageError(" Example: --image=/system/framework/boot.art");
UsageError("");
- UsageError(" --image-format=(uncompressed|lz4):");
+ UsageError(" --image-format=(uncompressed|lz4|lz4hc):");
UsageError(" Which format to store the image.");
UsageError(" Example: --image-format=lz4");
UsageError(" Default: uncompressed");
@@ -681,6 +681,8 @@ class Dex2Oat FINAL {
const StringPiece format_str = option.substr(substr.length());
if (format_str == "lz4") {
image_storage_mode_ = ImageHeader::kStorageModeLZ4;
+ } else if (format_str == "lz4hc") {
+ image_storage_mode_ = ImageHeader::kStorageModeLZ4HC;
} else if (format_str == "uncompressed") {
image_storage_mode_ = ImageHeader::kStorageModeUncompressed;
} else {
@@ -696,11 +698,6 @@ class Dex2Oat FINAL {
Usage("Can't have both --image and (--app-image-fd or --app-image-file)");
}
- if (IsBootImage()) {
- // We need the boot image to always be debuggable.
- compiler_options_->debuggable_ = true;
- }
-
if (oat_filenames_.empty() && oat_fd_ == -1) {
Usage("Output must be supplied with either --oat-file or --oat-fd");
}
diff --git a/runtime/base/bit_field.h b/runtime/base/bit_field.h
index fd65d500aa..a80ca28d2e 100644
--- a/runtime/base/bit_field.h
+++ b/runtime/base/bit_field.h
@@ -26,9 +26,18 @@ static constexpr uintptr_t kUintPtrTOne = 1U;
// BitField is a template for encoding and decoding a bit field inside
// an unsigned machine word.
-template<typename T, int position, int size>
+template<typename T, size_t kPosition, size_t kSize>
class BitField {
public:
+ typedef T value_type;
+ static constexpr size_t position = kPosition;
+ static constexpr size_t size = kSize;
+
+ static_assert(position < sizeof(uintptr_t) * kBitsPerByte, "Invalid position.");
+ static_assert(size != 0u, "Invalid size.");
+ static_assert(size <= sizeof(uintptr_t) * kBitsPerByte, "Invalid size.");
+ static_assert(size + position <= sizeof(uintptr_t) * kBitsPerByte, "Invalid position + size.");
+
// Tells whether the provided value fits into the bit field.
static bool IsValid(T value) {
return (static_cast<uintptr_t>(value) & ~((kUintPtrTOne << size) - 1)) == 0;
diff --git a/runtime/base/bit_vector.h b/runtime/base/bit_vector.h
index 9b55e708c8..424ebb70f6 100644
--- a/runtime/base/bit_vector.h
+++ b/runtime/base/bit_vector.h
@@ -229,6 +229,11 @@ class BitVector {
*/
int GetHighestBitSet() const;
+ // Minimum number of bits required to store this vector, 0 if none are set.
+ size_t GetNumberOfBits() const {
+ return GetHighestBitSet() + 1;
+ }
+
// Is bit set in storage. (No range check.)
static bool IsBitSet(const uint32_t* storage, uint32_t idx) {
return (storage[WordIndex(idx)] & BitMask(idx)) != 0;
diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h
index b9ea475149..fcf3326c81 100644
--- a/runtime/check_reference_map_visitor.h
+++ b/runtime/check_reference_map_visitor.h
@@ -100,8 +100,7 @@ class CheckReferenceMapVisitor : public StackVisitor {
CHECK_EQ(location.GetValue(), 0);
break;
default:
- LOG(FATAL) << "Unexpected location kind"
- << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind());
+ LOG(FATAL) << "Unexpected location kind " << location.GetInternalKind();
}
}
}
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 9ea082769a..cd4daeb7bd 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2608,18 +2608,6 @@ const void* ClassLinker::GetOatMethodQuickCodeFor(ArtMethod* method) {
return nullptr;
}
-const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file,
- uint16_t class_def_idx,
- uint32_t method_idx) {
- bool found;
- OatFile::OatClass oat_class = FindOatClass(dex_file, class_def_idx, &found);
- if (!found) {
- return nullptr;
- }
- uint32_t oat_method_idx = GetOatMethodIndexFromMethodIndex(dex_file, class_def_idx, method_idx);
- return oat_class.GetOatMethod(oat_method_idx).GetQuickCode();
-}
-
bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* quick_code) {
if (UNLIKELY(method->IsNative() || method->IsProxyMethod())) {
return false;
@@ -2650,6 +2638,11 @@ bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void*
return true;
}
+ if (Dbg::IsDebuggerActive()) {
+ // Boot image classes are AOT-compiled as non-debuggable.
+ return runtime->GetHeap()->IsInBootImageOatFile(quick_code);
+ }
+
return false;
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index a9448f732c..aa55dac7be 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -472,12 +472,6 @@ class ClassLinker {
const void* GetQuickOatCodeFor(ArtMethod* method)
SHARED_REQUIRES(Locks::mutator_lock_);
- // Get the oat code for a method from a method index.
- const void* GetQuickOatCodeFor(const DexFile& dex_file,
- uint16_t class_def_idx,
- uint32_t method_idx)
- SHARED_REQUIRES(Locks::mutator_lock_);
-
// Get compiled code for a method, return null if no code
// exists. This is unlike Get..OatCodeFor which will return a bridge
// or interpreter entrypoint.
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 904490aa8c..bc6589380c 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -28,6 +28,7 @@
#include "class_linker-inl.h"
#include "dex_file-inl.h"
#include "dex_instruction.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/allocation_record.h"
#include "gc/scoped_gc_critical_section.h"
@@ -570,6 +571,29 @@ bool Dbg::RequiresDeoptimization() {
return !Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly();
}
+// Used to patch boot image method entry point to interpreter bridge.
+class UpdateEntryPointsClassVisitor : public ClassVisitor {
+ public:
+ explicit UpdateEntryPointsClassVisitor(instrumentation::Instrumentation* instrumentation)
+ : instrumentation_(instrumentation) {}
+
+ bool operator()(mirror::Class* klass) OVERRIDE REQUIRES(Locks::mutator_lock_) {
+ auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ for (auto& m : klass->GetMethods(pointer_size)) {
+ const void* code = m.GetEntryPointFromQuickCompiledCode();
+ if (Runtime::Current()->GetHeap()->IsInBootImageOatFile(code) &&
+ !m.IsNative() &&
+ !m.IsProxyMethod()) {
+ instrumentation_->UpdateMethodsCode(&m, GetQuickToInterpreterBridge());
+ }
+ }
+ return true;
+ }
+
+ private:
+ instrumentation::Instrumentation* const instrumentation_;
+};
+
void Dbg::GoActive() {
// Enable all debugging features, including scans for breakpoints.
// This is a no-op if we're already active.
@@ -598,6 +622,14 @@ void Dbg::GoActive() {
}
Runtime* runtime = Runtime::Current();
+ // Since boot image code is AOT compiled as not debuggable, we need to patch
+ // entry points of methods in boot image to interpreter bridge.
+ if (!runtime->GetInstrumentation()->IsForcedInterpretOnly()) {
+ ScopedObjectAccess soa(self);
+ UpdateEntryPointsClassVisitor visitor(runtime->GetInstrumentation());
+ runtime->GetClassLinker()->VisitClasses(&visitor);
+ }
+
ScopedSuspendAll ssa(__FUNCTION__);
if (RequiresDeoptimization()) {
runtime->GetInstrumentation()->EnableDeoptimization();
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index a656fb8faf..4bee46285d 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -4058,6 +4058,15 @@ bool Heap::ObjectIsInBootImageSpace(mirror::Object* obj) const {
return false;
}
+bool Heap::IsInBootImageOatFile(const void* p) const {
+ for (gc::space::ImageSpace* space : boot_image_spaces_) {
+ if (space->GetOatFile()->Contains(p)) {
+ return true;
+ }
+ }
+ return false;
+}
+
void Heap::GetBootImagesSize(uint32_t* boot_image_begin,
uint32_t* boot_image_end,
uint32_t* boot_oat_begin,
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index a181e23b53..6edb548155 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -605,6 +605,9 @@ class Heap {
bool ObjectIsInBootImageSpace(mirror::Object* obj) const
SHARED_REQUIRES(Locks::mutator_lock_);
+ bool IsInBootImageOatFile(const void* p) const
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
void GetBootImagesSize(uint32_t* boot_image_begin,
uint32_t* boot_image_end,
uint32_t* boot_oat_begin,
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 894ce9af72..4ef36a449d 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1252,7 +1252,8 @@ ImageSpace* ImageSpace::Init(const char* image_filename,
// Only care about the error message for the last address in addresses. We want to avoid the
// overhead of printing the process maps if we can relocate.
std::string* out_error_msg = (address == addresses.back()) ? &temp_error_msg : nullptr;
- if (image_header->GetStorageMode() == ImageHeader::kStorageModeUncompressed) {
+ const ImageHeader::StorageMode storage_mode = image_header->GetStorageMode();
+ if (storage_mode == ImageHeader::kStorageModeUncompressed) {
map.reset(MemMap::MapFileAtAddress(address,
image_header->GetImageSize(),
PROT_READ | PROT_WRITE,
@@ -1264,6 +1265,12 @@ ImageSpace* ImageSpace::Init(const char* image_filename,
image_filename,
/*out*/out_error_msg));
} else {
+ if (storage_mode != ImageHeader::kStorageModeLZ4 &&
+ storage_mode != ImageHeader::kStorageModeLZ4HC) {
+ *error_msg = StringPrintf("Invalid storage mode in image header %d",
+ static_cast<int>(storage_mode));
+ return nullptr;
+ }
// Reserve output and decompress into it.
map.reset(MemMap::MapAnonymous(image_location,
address,
@@ -1289,6 +1296,8 @@ ImageSpace* ImageSpace::Init(const char* image_filename,
}
memcpy(map->Begin(), image_header, sizeof(ImageHeader));
const uint64_t start = NanoTime();
+ // LZ4HC and LZ4 have same internal format, both use LZ4_decompress.
+ TimingLogger::ScopedTiming timing2("LZ4 decompress image", &logger);
const size_t decompressed_size = LZ4_decompress_safe(
reinterpret_cast<char*>(temp_map->Begin()) + sizeof(ImageHeader),
reinterpret_cast<char*>(map->Begin()) + write_offset,
diff --git a/runtime/image.h b/runtime/image.h
index 146ee00c84..8e5dbad57d 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -81,6 +81,7 @@ class PACKED(4) ImageHeader {
enum StorageMode : uint32_t {
kStorageModeUncompressed,
kStorageModeLZ4,
+ kStorageModeLZ4HC,
kStorageModeCount, // Number of elements in enum.
};
static constexpr StorageMode kDefaultStorageMode = kStorageModeUncompressed;
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 748463529e..b107b72b55 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -104,6 +104,14 @@ static void UpdateEntrypoints(ArtMethod* method, const void* quick_code)
method->SetEntryPointFromQuickCompiledCode(quick_code);
}
+bool Instrumentation::NeedDebugVersionForBootImageCode(ArtMethod* method, const void* code) const
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ return Dbg::IsDebuggerActive() &&
+ Runtime::Current()->GetHeap()->IsInBootImageOatFile(code) &&
+ !method->IsNative() &&
+ !method->IsProxyMethod();
+}
+
void Instrumentation::InstallStubsForMethod(ArtMethod* method) {
if (!method->IsInvokable() || method->IsProxyMethod()) {
// Do not change stubs for these methods.
@@ -124,6 +132,9 @@ void Instrumentation::InstallStubsForMethod(ArtMethod* method) {
new_quick_code = GetQuickToInterpreterBridge();
} else if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) {
new_quick_code = class_linker->GetQuickOatCodeFor(method);
+ if (NeedDebugVersionForBootImageCode(method, new_quick_code)) {
+ new_quick_code = GetQuickToInterpreterBridge();
+ }
} else {
new_quick_code = GetQuickResolutionStub();
}
@@ -136,10 +147,13 @@ void Instrumentation::InstallStubsForMethod(ArtMethod* method) {
// class, all its static methods code will be set to the instrumentation entry point.
// For more details, see ClassLinker::FixupStaticTrampolines.
if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) {
- if (entry_exit_stubs_installed_) {
+ new_quick_code = class_linker->GetQuickOatCodeFor(method);
+ if (NeedDebugVersionForBootImageCode(method, new_quick_code)) {
+ // Oat code should not be used. Don't install instrumentation stub and
+ // use interpreter for instrumentation.
+ new_quick_code = GetQuickToInterpreterBridge();
+ } else if (entry_exit_stubs_installed_) {
new_quick_code = GetQuickInstrumentationEntryPoint();
- } else {
- new_quick_code = class_linker->GetQuickOatCodeFor(method);
}
} else {
new_quick_code = GetQuickResolutionStub();
@@ -775,6 +789,9 @@ void Instrumentation::Undeoptimize(ArtMethod* method) {
UpdateEntrypoints(method, GetQuickResolutionStub());
} else {
const void* quick_code = class_linker->GetQuickOatCodeFor(method);
+ if (NeedDebugVersionForBootImageCode(method, quick_code)) {
+ quick_code = GetQuickToInterpreterBridge();
+ }
UpdateEntrypoints(method, quick_code);
}
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index e3cbf53873..2e4be6b689 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -247,6 +247,11 @@ class Instrumentation {
return forced_interpret_only_;
}
+ // Code is in boot image oat file which isn't compiled as debuggable.
+ // Need debug version (interpreter or jitted) if that's the case.
+ bool NeedDebugVersionForBootImageCode(ArtMethod* method, const void* code) const
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
bool AreExitStubsInstalled() const {
return instrumentation_stubs_installed_;
}
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index ca8598e5e6..12d6fdc00d 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -792,6 +792,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF
}
ADVANCE(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
ADVANCE(2);
}
}
@@ -810,6 +811,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF
}
ADVANCE(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
ADVANCE(2);
}
}
@@ -828,6 +830,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF
}
ADVANCE(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
ADVANCE(2);
}
}
@@ -846,6 +849,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF
}
ADVANCE(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
ADVANCE(2);
}
}
@@ -864,6 +868,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF
}
ADVANCE(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
ADVANCE(2);
}
}
@@ -882,6 +887,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF
}
ADVANCE(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
ADVANCE(2);
}
}
@@ -899,6 +905,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF
}
ADVANCE(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
ADVANCE(2);
}
}
@@ -916,6 +923,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF
}
ADVANCE(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
ADVANCE(2);
}
}
@@ -933,6 +941,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF
}
ADVANCE(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
ADVANCE(2);
}
}
@@ -950,6 +959,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF
}
ADVANCE(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
ADVANCE(2);
}
}
@@ -967,6 +977,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF
}
ADVANCE(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
ADVANCE(2);
}
}
@@ -984,6 +995,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF
}
ADVANCE(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
ADVANCE(2);
}
}
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 25dbab2494..0488dbf028 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -712,6 +712,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
}
inst = inst->RelativeAt(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
}
break;
@@ -727,6 +728,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
}
inst = inst->RelativeAt(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
}
break;
@@ -742,6 +744,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
}
inst = inst->RelativeAt(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
}
break;
@@ -757,6 +760,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
}
inst = inst->RelativeAt(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
}
break;
@@ -772,6 +776,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
}
inst = inst->RelativeAt(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
}
break;
@@ -787,6 +792,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
}
inst = inst->RelativeAt(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
}
break;
@@ -801,6 +807,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
}
inst = inst->RelativeAt(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
}
break;
@@ -815,6 +822,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
}
inst = inst->RelativeAt(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
}
break;
@@ -829,6 +837,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
}
inst = inst->RelativeAt(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
}
break;
@@ -843,6 +852,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
}
inst = inst->RelativeAt(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
}
break;
@@ -857,6 +867,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
}
inst = inst->RelativeAt(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
}
break;
@@ -871,6 +882,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
}
inst = inst->RelativeAt(offset);
} else {
+ BRANCH_INSTRUMENTATION(2);
inst = inst->Next_2xx();
}
break;
diff --git a/runtime/interpreter/mterp/arm/bincmp.S b/runtime/interpreter/mterp/arm/bincmp.S
index 774e1676b7..cfad7147e2 100644
--- a/runtime/interpreter/mterp/arm/bincmp.S
+++ b/runtime/interpreter/mterp/arm/bincmp.S
@@ -6,14 +6,15 @@
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_PROFILE_BRANCHES
mov r1, rINST, lsr #12 @ r1<- B
ubfx r0, rINST, #8, #4 @ r0<- A
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- b${revcmp} .L_${opcode}_not_taken
+ mov${revcmp} rINST, #2
+#if MTERP_PROFILE_BRANCHES
+ @ TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov r0, rSELF
add r1, rFP, #OFF_FP_SHADOWFRAME
@@ -21,28 +22,10 @@
bl MterpProfileBranch @ (self, shadow_frame, offset)
cmp r0, #0
bne MterpOnStackReplacement @ Note: offset must be in rINST
+#endif
adds r2, rINST, rINST @ convert to bytes, check sign
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
-.L_${opcode}_not_taken:
- FETCH_ADVANCE_INST 2 @ update rPC, load rINST
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#else
- mov r1, rINST, lsr #12 @ r1<- B
- ubfx r0, rINST, #8, #4 @ r0<- A
- GET_VREG r3, r1 @ r3<- vB
- GET_VREG r2, r0 @ r2<- vA
- ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- FETCH_S rINST, 1 @ rINST<- branch offset, in code units
- cmp r2, r3 @ compare (vA, vB)
- mov${revcmp} rINST, #2 @ rINST<- BYTE branch dist for not-taken
- adds r2, rINST, rINST @ convert to bytes, check sign
- FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
- bmi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#endif
diff --git a/runtime/interpreter/mterp/arm/zcmp.S b/runtime/interpreter/mterp/arm/zcmp.S
index 800804d95e..3d7dec006d 100644
--- a/runtime/interpreter/mterp/arm/zcmp.S
+++ b/runtime/interpreter/mterp/arm/zcmp.S
@@ -6,13 +6,14 @@
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_PROFILE_BRANCHES
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
FETCH_S rINST, 1 @ rINST<- branch offset, in code units
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- b${revcmp} .L_${opcode}_not_taken
+ mov${revcmp} rINST, #2
+#if MTERP_PROFILE_BRANCHES
+ @ TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov r0, rSELF
add r1, rFP, #OFF_FP_SHADOWFRAME
@@ -20,25 +21,9 @@
bl MterpProfileBranch @ (self, shadow_frame, offset)
cmp r0, #0
bne MterpOnStackReplacement @ Note: offset must be in rINST
+#endif
adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
-.L_${opcode}_not_taken:
- FETCH_ADVANCE_INST 2 @ update rPC, load rINST
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#else
- mov r0, rINST, lsr #8 @ r0<- AA
- GET_VREG r2, r0 @ r2<- vAA
- FETCH_S rINST, 1 @ rINST<- branch offset, in code units
- ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- cmp r2, #0 @ compare (vA, 0)
- mov${revcmp} rINST, #2 @ rINST<- inst branch dist for not-taken
- adds r1, rINST, rINST @ convert to bytes & set flags
- FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
- bmi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#endif
diff --git a/runtime/interpreter/mterp/arm64/bincmp.S b/runtime/interpreter/mterp/arm64/bincmp.S
index ed850fc49d..2356ecbb89 100644
--- a/runtime/interpreter/mterp/arm64/bincmp.S
+++ b/runtime/interpreter/mterp/arm64/bincmp.S
@@ -6,43 +6,26 @@
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_PROFILE_BRANCHES
lsr w1, wINST, #12 // w1<- B
ubfx w0, wINST, #8, #4 // w0<- A
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
- FETCH_S wINST, 1 // wINST<- branch offset, in code units
+ FETCH_S w1, 1 // w1<- branch offset, in code units
+ mov w0, #2 // Offset if branch not taken
cmp w2, w3 // compare (vA, vB)
- b.${condition} .L_${opcode}_taken
- FETCH_ADVANCE_INST 2 // update rPC, load wINST
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-.L_${opcode}_taken:
+ csel wINST, w1, w0, ${condition} // Branch if true, stashing result in callee save reg.
+#if MTERP_PROFILE_BRANCHES
+ // TUINING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
sbfm x2, xINST, 0, 31 // Sign extend branch offset
bl MterpProfileBranch // (self, shadow_frame, offset)
cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
-#else
- lsr w1, wINST, #12 // w1<- B
- ubfx w0, wINST, #8, #4 // w0<- A
- GET_VREG w3, w1 // w3<- vB
- GET_VREG w2, w0 // w2<- vA
- FETCH_S w1, 1 // w1<- branch offset, in code units
- mov w0, #2 // Offset if branch not taken
- cmp w2, w3 // compare (vA, vB)
- csel wINST, w1, w0, ${condition} // Branch if true, stashing result in callee save reg.
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- adds w2, wINST, wINST // convert to bytes, check sign
- FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- b.mi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-#endif
diff --git a/runtime/interpreter/mterp/arm64/zcmp.S b/runtime/interpreter/mterp/arm64/zcmp.S
index e528d9f030..3f1e1b180f 100644
--- a/runtime/interpreter/mterp/arm64/zcmp.S
+++ b/runtime/interpreter/mterp/arm64/zcmp.S
@@ -6,39 +6,24 @@
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_PROFILE_BRANCHES
lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
- FETCH_S wINST, 1 // w1<- branch offset, in code units
+ FETCH_S w1, 1 // w1<- branch offset, in code units
+ mov w0, #2 // Branch offset if not taken
cmp w2, #0 // compare (vA, 0)
- b.${condition} .L_${opcode}_taken
- FETCH_ADVANCE_INST 2 // update rPC, load wINST
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-.L_${opcode}_taken:
+ csel wINST, w1, w0, ${condition} // Branch if true, stashing result in callee save reg
+#if MTERP_PROFILE_BRANCHES
+ // TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
sbfm x2, xINST, 0, 31
bl MterpProfileBranch // (self, shadow_frame, offset)
cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
adds w2, wINST, wINST // convert to bytes & set flags
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
-#else
- lsr w0, wINST, #8 // w0<- AA
- GET_VREG w2, w0 // w2<- vAA
- FETCH_S w1, 1 // w1<- branch offset, in code units
- mov w0, #2 // Branch offset if not taken
- cmp w2, #0 // compare (vA, 0)
- csel wINST, w1, w0, ${condition} // Branch if true, stashing result in callee save reg
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- adds w2, wINST, wINST // convert to bytes & set flags
- FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- b.mi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-#endif
diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S
index 94cbd2d10e..2b74d4c86e 100644
--- a/runtime/interpreter/mterp/out/mterp_arm.S
+++ b/runtime/interpreter/mterp/out/mterp_arm.S
@@ -1534,14 +1534,15 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_PROFILE_BRANCHES
mov r1, rINST, lsr #12 @ r1<- B
ubfx r0, rINST, #8, #4 @ r0<- A
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- bne .L_op_if_eq_not_taken
+ movne rINST, #2
+#if MTERP_PROFILE_BRANCHES
+ @ TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov r0, rSELF
add r1, rFP, #OFF_FP_SHADOWFRAME
@@ -1549,31 +1550,13 @@ artMterpAsmInstructionStart = .L_op_nop
bl MterpProfileBranch @ (self, shadow_frame, offset)
cmp r0, #0
bne MterpOnStackReplacement @ Note: offset must be in rINST
+#endif
adds r2, rINST, rINST @ convert to bytes, check sign
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
-.L_op_if_eq_not_taken:
- FETCH_ADVANCE_INST 2 @ update rPC, load rINST
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#else
- mov r1, rINST, lsr #12 @ r1<- B
- ubfx r0, rINST, #8, #4 @ r0<- A
- GET_VREG r3, r1 @ r3<- vB
- GET_VREG r2, r0 @ r2<- vA
- ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- FETCH_S rINST, 1 @ rINST<- branch offset, in code units
- cmp r2, r3 @ compare (vA, vB)
- movne rINST, #2 @ rINST<- BYTE branch dist for not-taken
- adds r2, rINST, rINST @ convert to bytes, check sign
- FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
- bmi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#endif
/* ------------------------------ */
@@ -1589,14 +1572,15 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_PROFILE_BRANCHES
mov r1, rINST, lsr #12 @ r1<- B
ubfx r0, rINST, #8, #4 @ r0<- A
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- beq .L_op_if_ne_not_taken
+ moveq rINST, #2
+#if MTERP_PROFILE_BRANCHES
+ @ TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov r0, rSELF
add r1, rFP, #OFF_FP_SHADOWFRAME
@@ -1604,31 +1588,13 @@ artMterpAsmInstructionStart = .L_op_nop
bl MterpProfileBranch @ (self, shadow_frame, offset)
cmp r0, #0
bne MterpOnStackReplacement @ Note: offset must be in rINST
+#endif
adds r2, rINST, rINST @ convert to bytes, check sign
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
-.L_op_if_ne_not_taken:
- FETCH_ADVANCE_INST 2 @ update rPC, load rINST
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#else
- mov r1, rINST, lsr #12 @ r1<- B
- ubfx r0, rINST, #8, #4 @ r0<- A
- GET_VREG r3, r1 @ r3<- vB
- GET_VREG r2, r0 @ r2<- vA
- ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- FETCH_S rINST, 1 @ rINST<- branch offset, in code units
- cmp r2, r3 @ compare (vA, vB)
- moveq rINST, #2 @ rINST<- BYTE branch dist for not-taken
- adds r2, rINST, rINST @ convert to bytes, check sign
- FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
- bmi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#endif
/* ------------------------------ */
@@ -1644,14 +1610,15 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_PROFILE_BRANCHES
mov r1, rINST, lsr #12 @ r1<- B
ubfx r0, rINST, #8, #4 @ r0<- A
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- bge .L_op_if_lt_not_taken
+ movge rINST, #2
+#if MTERP_PROFILE_BRANCHES
+ @ TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov r0, rSELF
add r1, rFP, #OFF_FP_SHADOWFRAME
@@ -1659,31 +1626,13 @@ artMterpAsmInstructionStart = .L_op_nop
bl MterpProfileBranch @ (self, shadow_frame, offset)
cmp r0, #0
bne MterpOnStackReplacement @ Note: offset must be in rINST
+#endif
adds r2, rINST, rINST @ convert to bytes, check sign
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
-.L_op_if_lt_not_taken:
- FETCH_ADVANCE_INST 2 @ update rPC, load rINST
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#else
- mov r1, rINST, lsr #12 @ r1<- B
- ubfx r0, rINST, #8, #4 @ r0<- A
- GET_VREG r3, r1 @ r3<- vB
- GET_VREG r2, r0 @ r2<- vA
- ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- FETCH_S rINST, 1 @ rINST<- branch offset, in code units
- cmp r2, r3 @ compare (vA, vB)
- movge rINST, #2 @ rINST<- BYTE branch dist for not-taken
- adds r2, rINST, rINST @ convert to bytes, check sign
- FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
- bmi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#endif
/* ------------------------------ */
@@ -1699,14 +1648,15 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_PROFILE_BRANCHES
mov r1, rINST, lsr #12 @ r1<- B
ubfx r0, rINST, #8, #4 @ r0<- A
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- blt .L_op_if_ge_not_taken
+ movlt rINST, #2
+#if MTERP_PROFILE_BRANCHES
+ @ TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov r0, rSELF
add r1, rFP, #OFF_FP_SHADOWFRAME
@@ -1714,31 +1664,13 @@ artMterpAsmInstructionStart = .L_op_nop
bl MterpProfileBranch @ (self, shadow_frame, offset)
cmp r0, #0
bne MterpOnStackReplacement @ Note: offset must be in rINST
+#endif
adds r2, rINST, rINST @ convert to bytes, check sign
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
-.L_op_if_ge_not_taken:
- FETCH_ADVANCE_INST 2 @ update rPC, load rINST
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#else
- mov r1, rINST, lsr #12 @ r1<- B
- ubfx r0, rINST, #8, #4 @ r0<- A
- GET_VREG r3, r1 @ r3<- vB
- GET_VREG r2, r0 @ r2<- vA
- ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- FETCH_S rINST, 1 @ rINST<- branch offset, in code units
- cmp r2, r3 @ compare (vA, vB)
- movlt rINST, #2 @ rINST<- BYTE branch dist for not-taken
- adds r2, rINST, rINST @ convert to bytes, check sign
- FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
- bmi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#endif
/* ------------------------------ */
@@ -1754,14 +1686,15 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_PROFILE_BRANCHES
mov r1, rINST, lsr #12 @ r1<- B
ubfx r0, rINST, #8, #4 @ r0<- A
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- ble .L_op_if_gt_not_taken
+ movle rINST, #2
+#if MTERP_PROFILE_BRANCHES
+ @ TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov r0, rSELF
add r1, rFP, #OFF_FP_SHADOWFRAME
@@ -1769,31 +1702,13 @@ artMterpAsmInstructionStart = .L_op_nop
bl MterpProfileBranch @ (self, shadow_frame, offset)
cmp r0, #0
bne MterpOnStackReplacement @ Note: offset must be in rINST
+#endif
adds r2, rINST, rINST @ convert to bytes, check sign
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
-.L_op_if_gt_not_taken:
- FETCH_ADVANCE_INST 2 @ update rPC, load rINST
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#else
- mov r1, rINST, lsr #12 @ r1<- B
- ubfx r0, rINST, #8, #4 @ r0<- A
- GET_VREG r3, r1 @ r3<- vB
- GET_VREG r2, r0 @ r2<- vA
- ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- FETCH_S rINST, 1 @ rINST<- branch offset, in code units
- cmp r2, r3 @ compare (vA, vB)
- movle rINST, #2 @ rINST<- BYTE branch dist for not-taken
- adds r2, rINST, rINST @ convert to bytes, check sign
- FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
- bmi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#endif
/* ------------------------------ */
@@ -1809,14 +1724,15 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_PROFILE_BRANCHES
mov r1, rINST, lsr #12 @ r1<- B
ubfx r0, rINST, #8, #4 @ r0<- A
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- bgt .L_op_if_le_not_taken
+ movgt rINST, #2
+#if MTERP_PROFILE_BRANCHES
+ @ TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov r0, rSELF
add r1, rFP, #OFF_FP_SHADOWFRAME
@@ -1824,31 +1740,13 @@ artMterpAsmInstructionStart = .L_op_nop
bl MterpProfileBranch @ (self, shadow_frame, offset)
cmp r0, #0
bne MterpOnStackReplacement @ Note: offset must be in rINST
+#endif
adds r2, rINST, rINST @ convert to bytes, check sign
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
-.L_op_if_le_not_taken:
- FETCH_ADVANCE_INST 2 @ update rPC, load rINST
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#else
- mov r1, rINST, lsr #12 @ r1<- B
- ubfx r0, rINST, #8, #4 @ r0<- A
- GET_VREG r3, r1 @ r3<- vB
- GET_VREG r2, r0 @ r2<- vA
- ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- FETCH_S rINST, 1 @ rINST<- branch offset, in code units
- cmp r2, r3 @ compare (vA, vB)
- movgt rINST, #2 @ rINST<- BYTE branch dist for not-taken
- adds r2, rINST, rINST @ convert to bytes, check sign
- FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
- bmi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#endif
/* ------------------------------ */
@@ -1864,13 +1762,14 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_PROFILE_BRANCHES
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
FETCH_S rINST, 1 @ rINST<- branch offset, in code units
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- bne .L_op_if_eqz_not_taken
+ movne rINST, #2
+#if MTERP_PROFILE_BRANCHES
+ @ TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov r0, rSELF
add r1, rFP, #OFF_FP_SHADOWFRAME
@@ -1878,28 +1777,12 @@ artMterpAsmInstructionStart = .L_op_nop
bl MterpProfileBranch @ (self, shadow_frame, offset)
cmp r0, #0
bne MterpOnStackReplacement @ Note: offset must be in rINST
+#endif
adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
-.L_op_if_eqz_not_taken:
- FETCH_ADVANCE_INST 2 @ update rPC, load rINST
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#else
- mov r0, rINST, lsr #8 @ r0<- AA
- GET_VREG r2, r0 @ r2<- vAA
- FETCH_S rINST, 1 @ rINST<- branch offset, in code units
- ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- cmp r2, #0 @ compare (vA, 0)
- movne rINST, #2 @ rINST<- inst branch dist for not-taken
- adds r1, rINST, rINST @ convert to bytes & set flags
- FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
- bmi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#endif
/* ------------------------------ */
@@ -1915,13 +1798,14 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_PROFILE_BRANCHES
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
FETCH_S rINST, 1 @ rINST<- branch offset, in code units
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- beq .L_op_if_nez_not_taken
+ moveq rINST, #2
+#if MTERP_PROFILE_BRANCHES
+ @ TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov r0, rSELF
add r1, rFP, #OFF_FP_SHADOWFRAME
@@ -1929,28 +1813,12 @@ artMterpAsmInstructionStart = .L_op_nop
bl MterpProfileBranch @ (self, shadow_frame, offset)
cmp r0, #0
bne MterpOnStackReplacement @ Note: offset must be in rINST
+#endif
adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
-.L_op_if_nez_not_taken:
- FETCH_ADVANCE_INST 2 @ update rPC, load rINST
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#else
- mov r0, rINST, lsr #8 @ r0<- AA
- GET_VREG r2, r0 @ r2<- vAA
- FETCH_S rINST, 1 @ rINST<- branch offset, in code units
- ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- cmp r2, #0 @ compare (vA, 0)
- moveq rINST, #2 @ rINST<- inst branch dist for not-taken
- adds r1, rINST, rINST @ convert to bytes & set flags
- FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
- bmi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#endif
/* ------------------------------ */
@@ -1966,13 +1834,14 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_PROFILE_BRANCHES
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
FETCH_S rINST, 1 @ rINST<- branch offset, in code units
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- bge .L_op_if_ltz_not_taken
+ movge rINST, #2
+#if MTERP_PROFILE_BRANCHES
+ @ TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov r0, rSELF
add r1, rFP, #OFF_FP_SHADOWFRAME
@@ -1980,28 +1849,12 @@ artMterpAsmInstructionStart = .L_op_nop
bl MterpProfileBranch @ (self, shadow_frame, offset)
cmp r0, #0
bne MterpOnStackReplacement @ Note: offset must be in rINST
+#endif
adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
-.L_op_if_ltz_not_taken:
- FETCH_ADVANCE_INST 2 @ update rPC, load rINST
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#else
- mov r0, rINST, lsr #8 @ r0<- AA
- GET_VREG r2, r0 @ r2<- vAA
- FETCH_S rINST, 1 @ rINST<- branch offset, in code units
- ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- cmp r2, #0 @ compare (vA, 0)
- movge rINST, #2 @ rINST<- inst branch dist for not-taken
- adds r1, rINST, rINST @ convert to bytes & set flags
- FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
- bmi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#endif
/* ------------------------------ */
@@ -2017,13 +1870,14 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_PROFILE_BRANCHES
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
FETCH_S rINST, 1 @ rINST<- branch offset, in code units
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- blt .L_op_if_gez_not_taken
+ movlt rINST, #2
+#if MTERP_PROFILE_BRANCHES
+ @ TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov r0, rSELF
add r1, rFP, #OFF_FP_SHADOWFRAME
@@ -2031,28 +1885,12 @@ artMterpAsmInstructionStart = .L_op_nop
bl MterpProfileBranch @ (self, shadow_frame, offset)
cmp r0, #0
bne MterpOnStackReplacement @ Note: offset must be in rINST
+#endif
adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
-.L_op_if_gez_not_taken:
- FETCH_ADVANCE_INST 2 @ update rPC, load rINST
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#else
- mov r0, rINST, lsr #8 @ r0<- AA
- GET_VREG r2, r0 @ r2<- vAA
- FETCH_S rINST, 1 @ rINST<- branch offset, in code units
- ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- cmp r2, #0 @ compare (vA, 0)
- movlt rINST, #2 @ rINST<- inst branch dist for not-taken
- adds r1, rINST, rINST @ convert to bytes & set flags
- FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
- bmi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#endif
/* ------------------------------ */
@@ -2068,13 +1906,14 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_PROFILE_BRANCHES
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
FETCH_S rINST, 1 @ rINST<- branch offset, in code units
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- ble .L_op_if_gtz_not_taken
+ movle rINST, #2
+#if MTERP_PROFILE_BRANCHES
+ @ TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov r0, rSELF
add r1, rFP, #OFF_FP_SHADOWFRAME
@@ -2082,28 +1921,12 @@ artMterpAsmInstructionStart = .L_op_nop
bl MterpProfileBranch @ (self, shadow_frame, offset)
cmp r0, #0
bne MterpOnStackReplacement @ Note: offset must be in rINST
+#endif
adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
-.L_op_if_gtz_not_taken:
- FETCH_ADVANCE_INST 2 @ update rPC, load rINST
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#else
- mov r0, rINST, lsr #8 @ r0<- AA
- GET_VREG r2, r0 @ r2<- vAA
- FETCH_S rINST, 1 @ rINST<- branch offset, in code units
- ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- cmp r2, #0 @ compare (vA, 0)
- movle rINST, #2 @ rINST<- inst branch dist for not-taken
- adds r1, rINST, rINST @ convert to bytes & set flags
- FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
- bmi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#endif
/* ------------------------------ */
@@ -2119,13 +1942,14 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_PROFILE_BRANCHES
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
FETCH_S rINST, 1 @ rINST<- branch offset, in code units
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- bgt .L_op_if_lez_not_taken
+ movgt rINST, #2
+#if MTERP_PROFILE_BRANCHES
+ @ TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov r0, rSELF
add r1, rFP, #OFF_FP_SHADOWFRAME
@@ -2133,28 +1957,12 @@ artMterpAsmInstructionStart = .L_op_nop
bl MterpProfileBranch @ (self, shadow_frame, offset)
cmp r0, #0
bne MterpOnStackReplacement @ Note: offset must be in rINST
+#endif
adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
-.L_op_if_lez_not_taken:
- FETCH_ADVANCE_INST 2 @ update rPC, load rINST
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#else
- mov r0, rINST, lsr #8 @ r0<- AA
- GET_VREG r2, r0 @ r2<- vAA
- FETCH_S rINST, 1 @ rINST<- branch offset, in code units
- ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- cmp r2, #0 @ compare (vA, 0)
- movgt rINST, #2 @ rINST<- inst branch dist for not-taken
- adds r1, rINST, rINST @ convert to bytes & set flags
- FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
- bmi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip @ extract opcode from rINST
- GOTO_OPCODE ip @ jump to next instruction
-#endif
/* ------------------------------ */
diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S
index e4825f0489..c7c0fb5b79 100644
--- a/runtime/interpreter/mterp/out/mterp_arm64.S
+++ b/runtime/interpreter/mterp/out/mterp_arm64.S
@@ -1372,46 +1372,29 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_PROFILE_BRANCHES
lsr w1, wINST, #12 // w1<- B
ubfx w0, wINST, #8, #4 // w0<- A
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
- FETCH_S wINST, 1 // wINST<- branch offset, in code units
+ FETCH_S w1, 1 // w1<- branch offset, in code units
+ mov w0, #2 // Offset if branch not taken
cmp w2, w3 // compare (vA, vB)
- b.eq .L_op_if_eq_taken
- FETCH_ADVANCE_INST 2 // update rPC, load wINST
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-.L_op_if_eq_taken:
+ csel wINST, w1, w0, eq // Branch if true, stashing result in callee save reg.
+#if MTERP_PROFILE_BRANCHES
+ // TUINING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
sbfm x2, xINST, 0, 31 // Sign extend branch offset
bl MterpProfileBranch // (self, shadow_frame, offset)
cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
-#else
- lsr w1, wINST, #12 // w1<- B
- ubfx w0, wINST, #8, #4 // w0<- A
- GET_VREG w3, w1 // w3<- vB
- GET_VREG w2, w0 // w2<- vA
- FETCH_S w1, 1 // w1<- branch offset, in code units
- mov w0, #2 // Offset if branch not taken
- cmp w2, w3 // compare (vA, vB)
- csel wINST, w1, w0, eq // Branch if true, stashing result in callee save reg.
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- adds w2, wINST, wINST // convert to bytes, check sign
- FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- b.mi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-#endif
/* ------------------------------ */
@@ -1427,46 +1410,29 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_PROFILE_BRANCHES
lsr w1, wINST, #12 // w1<- B
ubfx w0, wINST, #8, #4 // w0<- A
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
- FETCH_S wINST, 1 // wINST<- branch offset, in code units
+ FETCH_S w1, 1 // w1<- branch offset, in code units
+ mov w0, #2 // Offset if branch not taken
cmp w2, w3 // compare (vA, vB)
- b.ne .L_op_if_ne_taken
- FETCH_ADVANCE_INST 2 // update rPC, load wINST
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-.L_op_if_ne_taken:
+ csel wINST, w1, w0, ne // Branch if true, stashing result in callee save reg.
+#if MTERP_PROFILE_BRANCHES
+ // TUINING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
sbfm x2, xINST, 0, 31 // Sign extend branch offset
bl MterpProfileBranch // (self, shadow_frame, offset)
cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
-#else
- lsr w1, wINST, #12 // w1<- B
- ubfx w0, wINST, #8, #4 // w0<- A
- GET_VREG w3, w1 // w3<- vB
- GET_VREG w2, w0 // w2<- vA
- FETCH_S w1, 1 // w1<- branch offset, in code units
- mov w0, #2 // Offset if branch not taken
- cmp w2, w3 // compare (vA, vB)
- csel wINST, w1, w0, ne // Branch if true, stashing result in callee save reg.
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- adds w2, wINST, wINST // convert to bytes, check sign
- FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- b.mi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-#endif
/* ------------------------------ */
@@ -1482,46 +1448,29 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_PROFILE_BRANCHES
lsr w1, wINST, #12 // w1<- B
ubfx w0, wINST, #8, #4 // w0<- A
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
- FETCH_S wINST, 1 // wINST<- branch offset, in code units
+ FETCH_S w1, 1 // w1<- branch offset, in code units
+ mov w0, #2 // Offset if branch not taken
cmp w2, w3 // compare (vA, vB)
- b.lt .L_op_if_lt_taken
- FETCH_ADVANCE_INST 2 // update rPC, load wINST
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-.L_op_if_lt_taken:
+ csel wINST, w1, w0, lt // Branch if true, stashing result in callee save reg.
+#if MTERP_PROFILE_BRANCHES
+ // TUINING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
sbfm x2, xINST, 0, 31 // Sign extend branch offset
bl MterpProfileBranch // (self, shadow_frame, offset)
cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
-#else
- lsr w1, wINST, #12 // w1<- B
- ubfx w0, wINST, #8, #4 // w0<- A
- GET_VREG w3, w1 // w3<- vB
- GET_VREG w2, w0 // w2<- vA
- FETCH_S w1, 1 // w1<- branch offset, in code units
- mov w0, #2 // Offset if branch not taken
- cmp w2, w3 // compare (vA, vB)
- csel wINST, w1, w0, lt // Branch if true, stashing result in callee save reg.
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- adds w2, wINST, wINST // convert to bytes, check sign
- FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- b.mi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-#endif
/* ------------------------------ */
@@ -1537,46 +1486,29 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_PROFILE_BRANCHES
lsr w1, wINST, #12 // w1<- B
ubfx w0, wINST, #8, #4 // w0<- A
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
- FETCH_S wINST, 1 // wINST<- branch offset, in code units
+ FETCH_S w1, 1 // w1<- branch offset, in code units
+ mov w0, #2 // Offset if branch not taken
cmp w2, w3 // compare (vA, vB)
- b.ge .L_op_if_ge_taken
- FETCH_ADVANCE_INST 2 // update rPC, load wINST
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-.L_op_if_ge_taken:
+ csel wINST, w1, w0, ge // Branch if true, stashing result in callee save reg.
+#if MTERP_PROFILE_BRANCHES
+ // TUINING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
sbfm x2, xINST, 0, 31 // Sign extend branch offset
bl MterpProfileBranch // (self, shadow_frame, offset)
cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
-#else
- lsr w1, wINST, #12 // w1<- B
- ubfx w0, wINST, #8, #4 // w0<- A
- GET_VREG w3, w1 // w3<- vB
- GET_VREG w2, w0 // w2<- vA
- FETCH_S w1, 1 // w1<- branch offset, in code units
- mov w0, #2 // Offset if branch not taken
- cmp w2, w3 // compare (vA, vB)
- csel wINST, w1, w0, ge // Branch if true, stashing result in callee save reg.
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- adds w2, wINST, wINST // convert to bytes, check sign
- FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- b.mi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-#endif
/* ------------------------------ */
@@ -1592,46 +1524,29 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_PROFILE_BRANCHES
lsr w1, wINST, #12 // w1<- B
ubfx w0, wINST, #8, #4 // w0<- A
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
- FETCH_S wINST, 1 // wINST<- branch offset, in code units
+ FETCH_S w1, 1 // w1<- branch offset, in code units
+ mov w0, #2 // Offset if branch not taken
cmp w2, w3 // compare (vA, vB)
- b.gt .L_op_if_gt_taken
- FETCH_ADVANCE_INST 2 // update rPC, load wINST
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-.L_op_if_gt_taken:
+ csel wINST, w1, w0, gt // Branch if true, stashing result in callee save reg.
+#if MTERP_PROFILE_BRANCHES
+ // TUINING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
sbfm x2, xINST, 0, 31 // Sign extend branch offset
bl MterpProfileBranch // (self, shadow_frame, offset)
cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
-#else
- lsr w1, wINST, #12 // w1<- B
- ubfx w0, wINST, #8, #4 // w0<- A
- GET_VREG w3, w1 // w3<- vB
- GET_VREG w2, w0 // w2<- vA
- FETCH_S w1, 1 // w1<- branch offset, in code units
- mov w0, #2 // Offset if branch not taken
- cmp w2, w3 // compare (vA, vB)
- csel wINST, w1, w0, gt // Branch if true, stashing result in callee save reg.
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- adds w2, wINST, wINST // convert to bytes, check sign
- FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- b.mi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-#endif
/* ------------------------------ */
@@ -1647,46 +1562,29 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_PROFILE_BRANCHES
lsr w1, wINST, #12 // w1<- B
ubfx w0, wINST, #8, #4 // w0<- A
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
- FETCH_S wINST, 1 // wINST<- branch offset, in code units
+ FETCH_S w1, 1 // w1<- branch offset, in code units
+ mov w0, #2 // Offset if branch not taken
cmp w2, w3 // compare (vA, vB)
- b.le .L_op_if_le_taken
- FETCH_ADVANCE_INST 2 // update rPC, load wINST
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-.L_op_if_le_taken:
+ csel wINST, w1, w0, le // Branch if true, stashing result in callee save reg.
+#if MTERP_PROFILE_BRANCHES
+ // TUINING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
sbfm x2, xINST, 0, 31 // Sign extend branch offset
bl MterpProfileBranch // (self, shadow_frame, offset)
cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
-#else
- lsr w1, wINST, #12 // w1<- B
- ubfx w0, wINST, #8, #4 // w0<- A
- GET_VREG w3, w1 // w3<- vB
- GET_VREG w2, w0 // w2<- vA
- FETCH_S w1, 1 // w1<- branch offset, in code units
- mov w0, #2 // Offset if branch not taken
- cmp w2, w3 // compare (vA, vB)
- csel wINST, w1, w0, le // Branch if true, stashing result in callee save reg.
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- adds w2, wINST, wINST // convert to bytes, check sign
- FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- b.mi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-#endif
/* ------------------------------ */
@@ -1702,42 +1600,27 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_PROFILE_BRANCHES
lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
- FETCH_S wINST, 1 // w1<- branch offset, in code units
+ FETCH_S w1, 1 // w1<- branch offset, in code units
+ mov w0, #2 // Branch offset if not taken
cmp w2, #0 // compare (vA, 0)
- b.eq .L_op_if_eqz_taken
- FETCH_ADVANCE_INST 2 // update rPC, load wINST
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-.L_op_if_eqz_taken:
+ csel wINST, w1, w0, eq // Branch if true, stashing result in callee save reg
+#if MTERP_PROFILE_BRANCHES
+ // TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
sbfm x2, xINST, 0, 31
bl MterpProfileBranch // (self, shadow_frame, offset)
cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
adds w2, wINST, wINST // convert to bytes & set flags
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
-#else
- lsr w0, wINST, #8 // w0<- AA
- GET_VREG w2, w0 // w2<- vAA
- FETCH_S w1, 1 // w1<- branch offset, in code units
- mov w0, #2 // Branch offset if not taken
- cmp w2, #0 // compare (vA, 0)
- csel wINST, w1, w0, eq // Branch if true, stashing result in callee save reg
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- adds w2, wINST, wINST // convert to bytes & set flags
- FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- b.mi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-#endif
/* ------------------------------ */
@@ -1753,42 +1636,27 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_PROFILE_BRANCHES
lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
- FETCH_S wINST, 1 // w1<- branch offset, in code units
+ FETCH_S w1, 1 // w1<- branch offset, in code units
+ mov w0, #2 // Branch offset if not taken
cmp w2, #0 // compare (vA, 0)
- b.ne .L_op_if_nez_taken
- FETCH_ADVANCE_INST 2 // update rPC, load wINST
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-.L_op_if_nez_taken:
+ csel wINST, w1, w0, ne // Branch if true, stashing result in callee save reg
+#if MTERP_PROFILE_BRANCHES
+ // TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
sbfm x2, xINST, 0, 31
bl MterpProfileBranch // (self, shadow_frame, offset)
cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
adds w2, wINST, wINST // convert to bytes & set flags
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
-#else
- lsr w0, wINST, #8 // w0<- AA
- GET_VREG w2, w0 // w2<- vAA
- FETCH_S w1, 1 // w1<- branch offset, in code units
- mov w0, #2 // Branch offset if not taken
- cmp w2, #0 // compare (vA, 0)
- csel wINST, w1, w0, ne // Branch if true, stashing result in callee save reg
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- adds w2, wINST, wINST // convert to bytes & set flags
- FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- b.mi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-#endif
/* ------------------------------ */
@@ -1804,42 +1672,27 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_PROFILE_BRANCHES
lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
- FETCH_S wINST, 1 // w1<- branch offset, in code units
+ FETCH_S w1, 1 // w1<- branch offset, in code units
+ mov w0, #2 // Branch offset if not taken
cmp w2, #0 // compare (vA, 0)
- b.lt .L_op_if_ltz_taken
- FETCH_ADVANCE_INST 2 // update rPC, load wINST
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-.L_op_if_ltz_taken:
+ csel wINST, w1, w0, lt // Branch if true, stashing result in callee save reg
+#if MTERP_PROFILE_BRANCHES
+ // TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
sbfm x2, xINST, 0, 31
bl MterpProfileBranch // (self, shadow_frame, offset)
cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
adds w2, wINST, wINST // convert to bytes & set flags
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
-#else
- lsr w0, wINST, #8 // w0<- AA
- GET_VREG w2, w0 // w2<- vAA
- FETCH_S w1, 1 // w1<- branch offset, in code units
- mov w0, #2 // Branch offset if not taken
- cmp w2, #0 // compare (vA, 0)
- csel wINST, w1, w0, lt // Branch if true, stashing result in callee save reg
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- adds w2, wINST, wINST // convert to bytes & set flags
- FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- b.mi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-#endif
/* ------------------------------ */
@@ -1855,42 +1708,27 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_PROFILE_BRANCHES
lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
- FETCH_S wINST, 1 // w1<- branch offset, in code units
+ FETCH_S w1, 1 // w1<- branch offset, in code units
+ mov w0, #2 // Branch offset if not taken
cmp w2, #0 // compare (vA, 0)
- b.ge .L_op_if_gez_taken
- FETCH_ADVANCE_INST 2 // update rPC, load wINST
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-.L_op_if_gez_taken:
+ csel wINST, w1, w0, ge // Branch if true, stashing result in callee save reg
+#if MTERP_PROFILE_BRANCHES
+ // TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
sbfm x2, xINST, 0, 31
bl MterpProfileBranch // (self, shadow_frame, offset)
cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
adds w2, wINST, wINST // convert to bytes & set flags
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
-#else
- lsr w0, wINST, #8 // w0<- AA
- GET_VREG w2, w0 // w2<- vAA
- FETCH_S w1, 1 // w1<- branch offset, in code units
- mov w0, #2 // Branch offset if not taken
- cmp w2, #0 // compare (vA, 0)
- csel wINST, w1, w0, ge // Branch if true, stashing result in callee save reg
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- adds w2, wINST, wINST // convert to bytes & set flags
- FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- b.mi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-#endif
/* ------------------------------ */
@@ -1906,42 +1744,27 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_PROFILE_BRANCHES
lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
- FETCH_S wINST, 1 // w1<- branch offset, in code units
+ FETCH_S w1, 1 // w1<- branch offset, in code units
+ mov w0, #2 // Branch offset if not taken
cmp w2, #0 // compare (vA, 0)
- b.gt .L_op_if_gtz_taken
- FETCH_ADVANCE_INST 2 // update rPC, load wINST
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-.L_op_if_gtz_taken:
+ csel wINST, w1, w0, gt // Branch if true, stashing result in callee save reg
+#if MTERP_PROFILE_BRANCHES
+ // TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
sbfm x2, xINST, 0, 31
bl MterpProfileBranch // (self, shadow_frame, offset)
cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
adds w2, wINST, wINST // convert to bytes & set flags
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
-#else
- lsr w0, wINST, #8 // w0<- AA
- GET_VREG w2, w0 // w2<- vAA
- FETCH_S w1, 1 // w1<- branch offset, in code units
- mov w0, #2 // Branch offset if not taken
- cmp w2, #0 // compare (vA, 0)
- csel wINST, w1, w0, gt // Branch if true, stashing result in callee save reg
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- adds w2, wINST, wINST // convert to bytes & set flags
- FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- b.mi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-#endif
/* ------------------------------ */
@@ -1957,42 +1780,27 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_PROFILE_BRANCHES
lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
- FETCH_S wINST, 1 // w1<- branch offset, in code units
+ FETCH_S w1, 1 // w1<- branch offset, in code units
+ mov w0, #2 // Branch offset if not taken
cmp w2, #0 // compare (vA, 0)
- b.le .L_op_if_lez_taken
- FETCH_ADVANCE_INST 2 // update rPC, load wINST
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-.L_op_if_lez_taken:
+ csel wINST, w1, w0, le // Branch if true, stashing result in callee save reg
+#if MTERP_PROFILE_BRANCHES
+ // TUNING: once measurements are complete, remove #if and hand-schedule.
EXPORT_PC
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
sbfm x2, xINST, 0, 31
bl MterpProfileBranch // (self, shadow_frame, offset)
cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
adds w2, wINST, wINST // convert to bytes & set flags
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
-#else
- lsr w0, wINST, #8 // w0<- AA
- GET_VREG w2, w0 // w2<- vAA
- FETCH_S w1, 1 // w1<- branch offset, in code units
- mov w0, #2 // Branch offset if not taken
- cmp w2, #0 // compare (vA, 0)
- csel wINST, w1, w0, le // Branch if true, stashing result in callee save reg
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- adds w2, wINST, wINST // convert to bytes & set flags
- FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- b.mi MterpCheckSuspendAndContinue
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-#endif
/* ------------------------------ */
diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S
index b05360b6ae..567550f41f 100644
--- a/runtime/interpreter/mterp/out/mterp_x86.S
+++ b/runtime/interpreter/mterp/out/mterp_x86.S
@@ -2989,8 +2989,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
call SYMBOL(MterpInvokeVirtual)
testb %al, %al
jz MterpException
+ ADVANCE_PC 3
+ call SYMBOL(MterpShouldSwitchInterpreters)
+ testb %al, %al
+ jnz MterpFallback
RESTORE_IBASE
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
+ FETCH_INST
+ GOTO_NEXT
/*
* Handle a virtual method call.
@@ -3022,8 +3027,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
call SYMBOL(MterpInvokeSuper)
testb %al, %al
jz MterpException
+ ADVANCE_PC 3
+ call SYMBOL(MterpShouldSwitchInterpreters)
+ testb %al, %al
+ jnz MterpFallback
RESTORE_IBASE
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
+ FETCH_INST
+ GOTO_NEXT
/*
* Handle a "super" method call.
@@ -3055,8 +3065,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
call SYMBOL(MterpInvokeDirect)
testb %al, %al
jz MterpException
+ ADVANCE_PC 3
+ call SYMBOL(MterpShouldSwitchInterpreters)
+ testb %al, %al
+ jnz MterpFallback
RESTORE_IBASE
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
+ FETCH_INST
+ GOTO_NEXT
/* ------------------------------ */
@@ -3081,8 +3096,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
call SYMBOL(MterpInvokeStatic)
testb %al, %al
jz MterpException
+ ADVANCE_PC 3
+ call SYMBOL(MterpShouldSwitchInterpreters)
+ testb %al, %al
+ jnz MterpFallback
RESTORE_IBASE
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
+ FETCH_INST
+ GOTO_NEXT
@@ -3108,8 +3128,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
call SYMBOL(MterpInvokeInterface)
testb %al, %al
jz MterpException
+ ADVANCE_PC 3
+ call SYMBOL(MterpShouldSwitchInterpreters)
+ testb %al, %al
+ jnz MterpFallback
RESTORE_IBASE
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
+ FETCH_INST
+ GOTO_NEXT
/*
* Handle an interface method call.
@@ -3155,8 +3180,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
call SYMBOL(MterpInvokeVirtualRange)
testb %al, %al
jz MterpException
+ ADVANCE_PC 3
+ call SYMBOL(MterpShouldSwitchInterpreters)
+ testb %al, %al
+ jnz MterpFallback
RESTORE_IBASE
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
+ FETCH_INST
+ GOTO_NEXT
/* ------------------------------ */
@@ -3181,8 +3211,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
call SYMBOL(MterpInvokeSuperRange)
testb %al, %al
jz MterpException
+ ADVANCE_PC 3
+ call SYMBOL(MterpShouldSwitchInterpreters)
+ testb %al, %al
+ jnz MterpFallback
RESTORE_IBASE
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
+ FETCH_INST
+ GOTO_NEXT
/* ------------------------------ */
@@ -3207,8 +3242,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
call SYMBOL(MterpInvokeDirectRange)
testb %al, %al
jz MterpException
+ ADVANCE_PC 3
+ call SYMBOL(MterpShouldSwitchInterpreters)
+ testb %al, %al
+ jnz MterpFallback
RESTORE_IBASE
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
+ FETCH_INST
+ GOTO_NEXT
/* ------------------------------ */
@@ -3233,8 +3273,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
call SYMBOL(MterpInvokeStaticRange)
testb %al, %al
jz MterpException
+ ADVANCE_PC 3
+ call SYMBOL(MterpShouldSwitchInterpreters)
+ testb %al, %al
+ jnz MterpFallback
RESTORE_IBASE
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
+ FETCH_INST
+ GOTO_NEXT
/* ------------------------------ */
@@ -3259,8 +3304,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
call SYMBOL(MterpInvokeInterfaceRange)
testb %al, %al
jz MterpException
+ ADVANCE_PC 3
+ call SYMBOL(MterpShouldSwitchInterpreters)
+ testb %al, %al
+ jnz MterpFallback
RESTORE_IBASE
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
+ FETCH_INST
+ GOTO_NEXT
/* ------------------------------ */
@@ -6002,8 +6052,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
call SYMBOL(MterpInvokeVirtualQuick)
testb %al, %al
jz MterpException
+ ADVANCE_PC 3
+ call SYMBOL(MterpShouldSwitchInterpreters)
+ testb %al, %al
+ jnz MterpFallback
RESTORE_IBASE
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
+ FETCH_INST
+ GOTO_NEXT
/* ------------------------------ */
@@ -6028,8 +6083,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
call SYMBOL(MterpInvokeVirtualQuickRange)
testb %al, %al
jz MterpException
+ ADVANCE_PC 3
+ call SYMBOL(MterpShouldSwitchInterpreters)
+ testb %al, %al
+ jnz MterpFallback
RESTORE_IBASE
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
+ FETCH_INST
+ GOTO_NEXT
/* ------------------------------ */
@@ -12851,13 +12911,17 @@ MterpException:
call SYMBOL(MterpHandleException)
testb %al, %al
jz MterpExceptionReturn
- REFRESH_IBASE
movl OFF_FP_CODE_ITEM(rFP), %eax
movl OFF_FP_DEX_PC(rFP), %ecx
lea CODEITEM_INSNS_OFFSET(%eax), rPC
lea (rPC, %ecx, 2), rPC
movl rPC, OFF_FP_DEX_PC_PTR(rFP)
+ /* Do we need to switch interpreters? */
+ call SYMBOL(MterpShouldSwitchInterpreters)
+ testb %al, %al
+ jnz MterpFallback
/* resume execution at catch block */
+ REFRESH_IBASE
FETCH_INST
GOTO_NEXT
/* NOTE: no fallthrough */
diff --git a/runtime/interpreter/mterp/x86/footer.S b/runtime/interpreter/mterp/x86/footer.S
index c67491e577..64d72d7709 100644
--- a/runtime/interpreter/mterp/x86/footer.S
+++ b/runtime/interpreter/mterp/x86/footer.S
@@ -115,13 +115,17 @@ MterpException:
call SYMBOL(MterpHandleException)
testb %al, %al
jz MterpExceptionReturn
- REFRESH_IBASE
movl OFF_FP_CODE_ITEM(rFP), %eax
movl OFF_FP_DEX_PC(rFP), %ecx
lea CODEITEM_INSNS_OFFSET(%eax), rPC
lea (rPC, %ecx, 2), rPC
movl rPC, OFF_FP_DEX_PC_PTR(rFP)
+ /* Do we need to switch interpreters? */
+ call SYMBOL(MterpShouldSwitchInterpreters)
+ testb %al, %al
+ jnz MterpFallback
/* resume execution at catch block */
+ REFRESH_IBASE
FETCH_INST
GOTO_NEXT
/* NOTE: no fallthrough */
diff --git a/runtime/interpreter/mterp/x86/invoke.S b/runtime/interpreter/mterp/x86/invoke.S
index bbd88cf40b..c23053becb 100644
--- a/runtime/interpreter/mterp/x86/invoke.S
+++ b/runtime/interpreter/mterp/x86/invoke.S
@@ -16,5 +16,10 @@
call SYMBOL($helper)
testb %al, %al
jz MterpException
+ ADVANCE_PC 3
+ call SYMBOL(MterpShouldSwitchInterpreters)
+ testb %al, %al
+ jnz MterpFallback
RESTORE_IBASE
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
+ FETCH_INST
+ GOTO_NEXT
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index bdc7ee2428..3e66ce20eb 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -63,7 +63,8 @@ void Jit::DumpInfo(std::ostream& os) {
<< "JIT data cache size=" << PrettySize(code_cache_->DataCacheSize()) << "\n"
<< "JIT current capacity=" << PrettySize(code_cache_->GetCurrentCapacity()) << "\n"
<< "JIT number of compiled code=" << code_cache_->NumberOfCompiledCode() << "\n"
- << "JIT total number of compilations=" << code_cache_->NumberOfCompilations() << "\n";
+ << "JIT total number of compilations=" << code_cache_->NumberOfCompilations() << "\n"
+ << "JIT total number of osr compilations=" << code_cache_->NumberOfOsrCompilations() << "\n";
cumulative_timings_.Dump(os);
}
@@ -369,8 +370,7 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread,
continue;
}
- DCHECK(location == DexRegisterLocation::Kind::kInStack)
- << DexRegisterLocation::PrettyDescriptor(location);
+ DCHECK_EQ(location, DexRegisterLocation::Kind::kInStack);
int32_t vreg_value = shadow_frame->GetVReg(vreg);
int32_t slot_offset = vreg_map.GetStackOffsetInBytes(vreg,
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 6348ddad58..8858b486f9 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -126,7 +126,10 @@ JitCodeCache::JitCodeCache(MemMap* code_map,
has_done_full_collection_(false),
last_update_time_ns_(0),
garbage_collect_code_(garbage_collect_code),
- number_of_compilations_(0) {
+ used_memory_for_data_(0),
+ used_memory_for_code_(0),
+ number_of_compilations_(0),
+ number_of_osr_compilations_(0) {
DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity);
code_mspace_ = create_mspace_with_base(code_map_->Begin(), code_end_, false /*locked*/);
@@ -336,6 +339,7 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self,
MutexLock mu(self, lock_);
method_code_map_.Put(code_ptr, method);
if (osr) {
+ number_of_osr_compilations_++;
osr_code_map_.Put(method, code_ptr);
} else {
Runtime::Current()->GetInstrumentation()->UpdateMethodsCode(
@@ -364,6 +368,11 @@ size_t JitCodeCache::NumberOfCompilations() {
return number_of_compilations_;
}
+size_t JitCodeCache::NumberOfOsrCompilations() {
+ MutexLock mu(Thread::Current(), lock_);
+ return number_of_osr_compilations_;
+}
+
size_t JitCodeCache::CodeCacheSize() {
MutexLock mu(Thread::Current(), lock_);
return CodeCacheSizeLocked();
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index e5b8e6ca17..4574edfb46 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -73,6 +73,7 @@ class JitCodeCache {
// Number of compilations done throughout the lifetime of the JIT.
size_t NumberOfCompilations() REQUIRES(!lock_);
+ size_t NumberOfOsrCompilations() REQUIRES(!lock_);
bool NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr)
SHARED_REQUIRES(Locks::mutator_lock_)
@@ -304,6 +305,7 @@ class JitCodeCache {
// Number of compilations done throughout the lifetime of the JIT.
size_t number_of_compilations_ GUARDED_BY(lock_);
+ size_t number_of_osr_compilations_ GUARDED_BY(lock_);
DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache);
};
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 910163c787..fb91a8cdff 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -228,6 +228,10 @@ class OatFile {
return End() - Begin();
}
+ bool Contains(const void* p) const {
+ return p >= Begin() && p < End();
+ }
+
size_t BssSize() const {
return BssEnd() - BssBegin();
}
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 18cf81aa7c..ea26d58767 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -16,6 +16,8 @@
#include "oat_file_manager.h"
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+#include <cutils/trace.h>
#include <memory>
#include <queue>
#include <vector>
@@ -386,13 +388,15 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
ScopedSuspendAll ssa("Add image space");
runtime->GetHeap()->AddSpace(image_space.get());
}
- added_image_space = true;
- if (runtime->GetClassLinker()->AddImageSpace(image_space.get(),
- h_loader,
- dex_elements,
- dex_location,
- /*out*/&dex_files,
- /*out*/&temp_error_msg)) {
+ ATRACE_BEGIN(StringPrintf("Adding image space for location %s", dex_location).c_str());
+ added_image_space = runtime->GetClassLinker()->AddImageSpace(image_space.get(),
+ h_loader,
+ dex_elements,
+ dex_location,
+ /*out*/&dex_files,
+ /*out*/&temp_error_msg);
+ ATRACE_END();
+ if (added_image_space) {
// Successfully added image space to heap, release the map so that it does not get
// freed.
image_space.release();
@@ -407,7 +411,6 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
ScopedSuspendAll ssa("Remove image space");
runtime->GetHeap()->RemoveSpace(image_space.get());
}
- added_image_space = false;
// Non-fatal, don't update error_msg.
}
}
diff --git a/runtime/primitive.h b/runtime/primitive.h
index ca42c4790c..2454a2117b 100644
--- a/runtime/primitive.h
+++ b/runtime/primitive.h
@@ -46,6 +46,7 @@ class Primitive {
kPrimFloat,
kPrimDouble,
kPrimVoid,
+ kPrimLast = kPrimVoid
};
static Type GetType(char type) {
diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h
index 0b09a70be4..7e84b405e7 100644
--- a/runtime/quick/inline_method_analyser.h
+++ b/runtime/quick/inline_method_analyser.h
@@ -37,6 +37,8 @@ class MethodVerifier;
enum InlineMethodOpcode : uint16_t {
kIntrinsicDoubleCvt,
kIntrinsicFloatCvt,
+ kIntrinsicFloat2Int,
+ kIntrinsicDouble2Long,
kIntrinsicFloatIsInfinite,
kIntrinsicDoubleIsInfinite,
kIntrinsicFloatIsNaN,
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 2dfa860dcb..6317f5e401 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -204,8 +204,7 @@ static VRegKind ToVRegKind(DexRegisterLocation::Kind kind) {
return VRegKind::kDoubleHiVReg;
default:
- LOG(FATAL) << "Unexpected vreg location "
- << DexRegisterLocation::PrettyDescriptor(kind);
+ LOG(FATAL) << "Unexpected vreg location " << kind;
UNREACHABLE();
}
}
@@ -456,12 +455,11 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor {
}
default: {
LOG(FATAL)
- << "Unexpected location kind"
- << DexRegisterLocation::PrettyDescriptor(
- vreg_map.GetLocationInternalKind(vreg,
- number_of_vregs,
- code_info,
- encoding));
+ << "Unexpected location kind "
+ << vreg_map.GetLocationInternalKind(vreg,
+ number_of_vregs,
+ code_info,
+ encoding);
UNREACHABLE();
}
}
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 5faff93b97..b1f1ed61b4 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -352,12 +352,11 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin
return false;
default:
LOG(FATAL)
- << "Unexpected location kind"
- << DexRegisterLocation::PrettyDescriptor(
- dex_register_map.GetLocationInternalKind(vreg,
- number_of_dex_registers,
- code_info,
- encoding));
+ << "Unexpected location kind "
+ << dex_register_map.GetLocationInternalKind(vreg,
+ number_of_dex_registers,
+ code_info,
+ encoding);
UNREACHABLE();
}
}
diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc
index 5544507c06..30934360ac 100644
--- a/runtime/stack_map.cc
+++ b/runtime/stack_map.cc
@@ -27,6 +27,31 @@ constexpr size_t DexRegisterLocationCatalog::kNoLocationEntryIndex;
constexpr uint32_t StackMap::kNoDexRegisterMap;
constexpr uint32_t StackMap::kNoInlineInfo;
+std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation::Kind& kind) {
+ using Kind = DexRegisterLocation::Kind;
+ switch (kind) {
+ case Kind::kNone:
+ return stream << "none";
+ case Kind::kInStack:
+ return stream << "in stack";
+ case Kind::kInRegister:
+ return stream << "in register";
+ case Kind::kInRegisterHigh:
+ return stream << "in register high";
+ case Kind::kInFpuRegister:
+ return stream << "in fpu register";
+ case Kind::kInFpuRegisterHigh:
+ return stream << "in fpu register high";
+ case Kind::kConstant:
+ return stream << "as constant";
+ case Kind::kInStackLargeOffset:
+ return stream << "in stack (large offset)";
+ case Kind::kConstantLargeValue:
+ return stream << "as constant (large value)";
+ }
+ return stream << "Kind<" << static_cast<uint32_t>(kind) << ">";
+}
+
DexRegisterLocation::Kind DexRegisterMap::GetLocationInternalKind(
uint16_t dex_register_number,
uint16_t number_of_dex_registers,
@@ -97,7 +122,7 @@ static void DumpRegisterMapping(std::ostream& os,
const std::string& prefix = "v",
const std::string& suffix = "") {
os << prefix << dex_register_num << ": "
- << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind())
+ << location.GetInternalKind()
<< " (" << location.GetValue() << ")" << suffix << '\n';
}
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 97eb805501..dbf23aafc3 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -110,30 +110,6 @@ class DexRegisterLocation {
sizeof(Kind) == 1u,
"art::DexRegisterLocation::Kind has a size different from one byte.");
- static const char* PrettyDescriptor(Kind kind) {
- switch (kind) {
- case Kind::kNone:
- return "none";
- case Kind::kInStack:
- return "in stack";
- case Kind::kInRegister:
- return "in register";
- case Kind::kInRegisterHigh:
- return "in register high";
- case Kind::kInFpuRegister:
- return "in fpu register";
- case Kind::kInFpuRegisterHigh:
- return "in fpu register high";
- case Kind::kConstant:
- return "as constant";
- case Kind::kInStackLargeOffset:
- return "in stack (large offset)";
- case Kind::kConstantLargeValue:
- return "as constant (large value)";
- }
- UNREACHABLE();
- }
-
static bool IsShortLocationKind(Kind kind) {
switch (kind) {
case Kind::kInStack:
@@ -149,7 +125,7 @@ class DexRegisterLocation {
return false;
case Kind::kNone:
- LOG(FATAL) << "Unexpected location kind " << PrettyDescriptor(kind);
+ LOG(FATAL) << "Unexpected location kind";
}
UNREACHABLE();
}
@@ -215,6 +191,8 @@ class DexRegisterLocation {
friend class DexRegisterLocationHashFn;
};
+std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation::Kind& kind);
+
/**
* Store information on unique Dex register locations used in a method.
* The information is of the form:
@@ -349,7 +327,7 @@ class DexRegisterLocationCatalog {
case DexRegisterLocation::Kind::kConstantLargeValue:
case DexRegisterLocation::Kind::kInStackLargeOffset:
case DexRegisterLocation::Kind::kNone:
- LOG(FATAL) << "Unexpected location kind " << DexRegisterLocation::PrettyDescriptor(kind);
+ LOG(FATAL) << "Unexpected location kind " << kind;
}
UNREACHABLE();
}
@@ -373,7 +351,7 @@ class DexRegisterLocationCatalog {
case DexRegisterLocation::Kind::kConstantLargeValue:
case DexRegisterLocation::Kind::kInStackLargeOffset:
case DexRegisterLocation::Kind::kNone:
- LOG(FATAL) << "Unexpected location kind " << DexRegisterLocation::PrettyDescriptor(kind);
+ LOG(FATAL) << "Unexpected location kind " << kind;
}
UNREACHABLE();
}
@@ -515,8 +493,7 @@ class DexRegisterMap {
const StackMapEncoding& enc) const {
DexRegisterLocation location =
GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info, enc);
- DCHECK(location.GetKind() == DexRegisterLocation::Kind::kConstant)
- << DexRegisterLocation::PrettyDescriptor(location.GetKind());
+ DCHECK_EQ(location.GetKind(), DexRegisterLocation::Kind::kConstant);
return location.GetValue();
}
@@ -530,7 +507,7 @@ class DexRegisterMap {
location.GetInternalKind() == DexRegisterLocation::Kind::kInRegisterHigh ||
location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegister ||
location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegisterHigh)
- << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind());
+ << location.GetInternalKind();
return location.GetValue();
}
diff --git a/test/004-JniTest/build b/test/004-JniTest/build
new file mode 100755
index 0000000000..e8e9f31ef4
--- /dev/null
+++ b/test/004-JniTest/build
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# Copyright 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.
+# Make us exit on a failure.
+#
+set -e
+
+# Hard-wired use of experimental jack.
+# TODO: fix this temporary work-around for lambdas, see b/19467889
+export USE_JACK=true
+# export JACK_SERVER=false
+# export JACK_REPOSITORY="${ANDROID_BUILD_TOP}/prebuilts/sdk/tools/jacks"
+
+# e.g. /foo/bar/jack-3.10.ALPHA.jar -> 3.10.ALPHA
+# export JACK_VERSION="$(find "$JACK_REPOSITORY" -name '*ALPHA*' | sed 's/.*jack-//g' | sed 's/[.]jar//g')"
+./default-build "$@" --experimental lambdas
diff --git a/test/004-JniTest/expected.txt b/test/004-JniTest/expected.txt
index 155c6ae5f3..f7e404d30b 100644
--- a/test/004-JniTest/expected.txt
+++ b/test/004-JniTest/expected.txt
@@ -55,3 +55,6 @@ Calling method AbstractInterface->JniCallSoftConflictMethod on object of type Co
DefaultInterface.JniCallSoftConflictMethod
Calling method ConflictInterface->JniCallConflictDefaultMethod on object of type ConcreteClass
EXCEPTION OCCURED: java.lang.IncompatibleClassChangeError: Conflicting default method implementations void ConflictInterface.JniCallConflictDefaultMethod()
+hi-lambda: λ
+hi-default δλ
+hi-default δλ
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index f632331fe3..2bdf8d1e71 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -721,3 +721,22 @@ class JniCallDefaultMethodsTest {
extern "C" JNIEXPORT void JNICALL Java_Main_testCallDefaultMethods(JNIEnv* env) {
JniCallDefaultMethodsTest(env).Test();
}
+
+static void InvokeSpecificMethod(JNIEnv* env, jobject obj, const char* method) {
+ jclass lambda_class = env->FindClass("LambdaInterface");
+ assert(!env->ExceptionCheck());
+ assert(lambda_class != nullptr);
+ jmethodID method_id = env->GetMethodID(lambda_class, method, "()V");
+ assert(!env->ExceptionCheck());
+ env->CallVoidMethod(obj, method_id);
+ assert(!env->ExceptionCheck());
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_testInvokeLambdaDefaultMethod(
+ JNIEnv* e, jclass, jobject l) {
+ InvokeSpecificMethod(e, l, "sayHiTwice");
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_testInvokeLambdaMethod(JNIEnv* e, jclass, jobject l) {
+ InvokeSpecificMethod(e, l, "sayHi");
+}
diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java
index 9f4a8522e7..e0530d8d62 100644
--- a/test/004-JniTest/src/Main.java
+++ b/test/004-JniTest/src/Main.java
@@ -40,6 +40,10 @@ public class Main {
testProxyGetMethodID();
testJniCriticalSectionAndGc();
testCallDefaultMethods();
+ String lambda = "λ";
+ testInvokeLambdaMethod(() -> { System.out.println("hi-lambda: " + lambda); });
+ String def = "δ";
+ testInvokeLambdaDefaultMethod(() -> { System.out.println("hi-default " + def + lambda); });
}
private static native void testCallDefaultMethods();
@@ -255,6 +259,19 @@ public class Main {
}
private static native void enterJniCriticalSection(int arraySize, byte[] array0, byte[] array);
+
+ private static native void testInvokeLambdaMethod(LambdaInterface iface);
+
+ private static native void testInvokeLambdaDefaultMethod(LambdaInterface iface);
+}
+
+@FunctionalInterface
+interface LambdaInterface {
+ public void sayHi();
+ public default void sayHiTwice() {
+ sayHi();
+ sayHi();
+ }
}
class JniCallNonvirtualTest {
diff --git a/test/020-string/expected.txt b/test/020-string/expected.txt
index 081fea3a41..76b8929bd7 100644
--- a/test/020-string/expected.txt
+++ b/test/020-string/expected.txt
@@ -5,3 +5,9 @@ Compare unicode: -65302
Got expected exception
subStr is 'uick brown fox jumps over the lazy '
Indexes are: 0:-1:0:43:33:-1:18:13:13:-1:18:18:-1:13:-1:-1:-1
+Got expected exception
+Got expected exception
+Got expected exception
+Got expected exception
+Got expected exception
+llo And
diff --git a/test/020-string/src/Main.java b/test/020-string/src/Main.java
index b876e6ad21..710808255c 100644
--- a/test/020-string/src/Main.java
+++ b/test/020-string/src/Main.java
@@ -25,6 +25,7 @@ public class Main {
basicTest();
indexTest();
constructorTest();
+ copyTest();
}
public static void basicTest() {
@@ -117,4 +118,48 @@ public class Main {
String s14 = new String(codePoints, 1, 3);
String s15 = new String(stringBuilder);
}
+
+ public static void copyTest() {
+ String src = new String("Hello Android");
+ char[] dst = new char[7];
+ char[] tmp = null;
+
+ try {
+ src.getChars(2, 9, tmp, 0);
+ System.out.println("GLITCH: expected exception");
+ } catch (NullPointerException npe) {
+ System.out.println("Got expected exception");
+ }
+
+ try {
+ src.getChars(-1, 9, dst, 0);
+ System.out.println("GLITCH: expected exception");
+ } catch (StringIndexOutOfBoundsException sioobe) {
+ System.out.println("Got expected exception");
+ }
+
+ try {
+ src.getChars(2, 19, dst, 0);
+ System.out.println("GLITCH: expected exception");
+ } catch (StringIndexOutOfBoundsException sioobe) {
+ System.out.println("Got expected exception");
+ }
+
+ try {
+ src.getChars(2, 1, dst, 0);
+ System.out.println("GLITCH: expected exception");
+ } catch (StringIndexOutOfBoundsException sioobe) {
+ System.out.println("Got expected exception");
+ }
+
+ try {
+ src.getChars(2, 10, dst, 0);
+ System.out.println("GLITCH: expected exception");
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ System.out.println("Got expected exception");
+ }
+
+ src.getChars(2, 9, dst, 0);
+ System.out.println(new String(dst));
+ }
}
diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java
index 5b3fa14076..93a9005fe0 100644
--- a/test/082-inline-execute/src/Main.java
+++ b/test/082-inline-execute/src/Main.java
@@ -1039,6 +1039,7 @@ public class Main {
Assert.assertEquals(StrictMath.round(-2.9d), -3l);
Assert.assertEquals(StrictMath.round(-3.0d), -3l);
Assert.assertEquals(StrictMath.round(0.49999999999999994d), 0l);
+ Assert.assertEquals(StrictMath.round(9007199254740991.0d), 9007199254740991l); // 2^53 - 1
Assert.assertEquals(StrictMath.round(Double.NaN), (long)+0.0d);
Assert.assertEquals(StrictMath.round(Long.MAX_VALUE + 1.0d), Long.MAX_VALUE);
Assert.assertEquals(StrictMath.round(Long.MIN_VALUE - 1.0d), Long.MIN_VALUE);
@@ -1062,6 +1063,7 @@ public class Main {
Assert.assertEquals(StrictMath.round(-3.0f), -3);
// 0.4999999701976776123046875
Assert.assertEquals(StrictMath.round(Float.intBitsToFloat(0x3EFFFFFF)), (int)+0.0f);
+ Assert.assertEquals(StrictMath.round(16777215.0f), 16777215); // 2^24 - 1
Assert.assertEquals(StrictMath.round(Float.NaN), (int)+0.0f);
Assert.assertEquals(StrictMath.round(Integer.MAX_VALUE + 1.0f), Integer.MAX_VALUE);
Assert.assertEquals(StrictMath.round(Integer.MIN_VALUE - 1.0f), Integer.MIN_VALUE);
diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
index 32bbc5b61d..66e1d92cc2 100644
--- a/test/449-checker-bce/src/Main.java
+++ b/test/449-checker-bce/src/Main.java
@@ -137,20 +137,16 @@ public class Main {
/// CHECK: ArraySet
/// CHECK: BoundsCheck
/// CHECK: ArraySet
- /// CHECK: BoundsCheck
- /// CHECK: ArraySet
/// CHECK-START: void Main.$opt$noinline$constantIndexing2(int[]) BCE (after)
- /// CHECK-NOT: Deoptimize
- /// CHECK: BoundsCheck
- /// CHECK: ArraySet
- /// CHECK: BoundsCheck
+ /// CHECK: Deoptimize
+ /// CHECK-NOT: BoundsCheck
/// CHECK: ArraySet
- /// CHECK: BoundsCheck
+ /// CHECK-NOT: BoundsCheck
/// CHECK: ArraySet
- /// CHECK: BoundsCheck
+ /// CHECK-NOT: BoundsCheck
/// CHECK: ArraySet
- /// CHECK: BoundsCheck
+ /// CHECK-NOT: BoundsCheck
/// CHECK: ArraySet
static void $opt$noinline$constantIndexing2(int[] array) {
@@ -158,8 +154,7 @@ public class Main {
array[2] = 1;
array[3] = 1;
array[4] = 1;
- array[-1] = 1; // prevents the whole opt on [-1:4]
- if (array[1] == 1) {
+ if (array[1] != 1) {
throw new Error("");
}
}
@@ -173,8 +168,41 @@ public class Main {
/// CHECK: ArraySet
/// CHECK: BoundsCheck
/// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
/// CHECK-START: void Main.constantIndexing2b(int[]) BCE (after)
+ /// CHECK-NOT: Deoptimize
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+
+ static void constantIndexing2b(int[] array) {
+ array[0] = 6;
+ array[1] = 6;
+ array[2] = 6;
+ array[3] = 6;
+ array[-1] = 1; // prevents the whole opt on [-1:4]
+ }
+
+ /// CHECK-START: void Main.constantIndexing2c(int[]) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+
+ /// CHECK-START: void Main.constantIndexing2c(int[]) BCE (after)
/// CHECK: Deoptimize
/// CHECK-NOT: BoundsCheck
/// CHECK: ArraySet
@@ -185,7 +213,7 @@ public class Main {
/// CHECK-NOT: BoundsCheck
/// CHECK: ArraySet
- static void constantIndexing2b(int[] array) {
+ static void constantIndexing2c(int[] array) {
array[0] = 7;
array[1] = 7;
array[2] = 7;
@@ -440,31 +468,37 @@ public class Main {
System.out.println("constant indices 1 failed!");
}
+ $opt$noinline$constantIndexing2(a6);
+ if (a6[0] != 0 || a6[1] != 1 || a6[2] != 1 ||
+ a6[3] != 1 || a6[4] != 1 || a6[5] != 11) {
+ System.out.println("constant indices 2 failed!");
+ }
+
caught = false;
try {
- $opt$noinline$constantIndexing2(a6);
+ constantIndexing2b(a6);
} catch (ArrayIndexOutOfBoundsException e) {
caught = true;
}
- if (!caught || a6[0] != 0 || a6[1] != 1 || a6[2] != 1 ||
- a6[3] != 1 || a6[4] != 1 || a6[5] != 11) {
- System.out.println("constant indices 2 failed!");
+ if (!caught || a6[0] != 6 || a6[1] != 6 || a6[2] != 6 ||
+ a6[3] != 6 || a6[4] != 1 || a6[5] != 11) {
+ System.out.println("constant indices 2b failed!");
}
caught = false;
try {
- constantIndexing2b(a1);
+ constantIndexing2c(a1);
} catch (ArrayIndexOutOfBoundsException e) {
caught = true;
}
if (!caught || a1[0] != 7) {
- System.out.println("constant indices 2b failed!");
+ System.out.println("constant indices 2c failed!");
}
- constantIndexing2b(a6);
+ constantIndexing2c(a6);
if (a6[0] != 7 || a6[1] != 7 || a6[2] != 7 ||
a6[3] != 7 || a6[4] != 1 || a6[5] != 11) {
- System.out.println("constant indices 2b failed!");
+ System.out.println("constant indices 2c failed!");
}
int[] b4 = new int[4];
diff --git a/test/530-checker-loops/src/Main.java b/test/530-checker-loops/src/Main.java
index 0c32491e4e..d5111b0c14 100644
--- a/test/530-checker-loops/src/Main.java
+++ b/test/530-checker-loops/src/Main.java
@@ -394,6 +394,34 @@ public class Main {
return result;
}
+ /// CHECK-START: int Main.linearForNEArrayLengthUp(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.linearForNEArrayLengthUp(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int linearForNEArrayLengthUp(int[] x) {
+ int result = 0;
+ for (int i = 0; i != x.length; i++) {
+ result += x[i];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.linearForNEArrayLengthDown(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.linearForNEArrayLengthDown(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int linearForNEArrayLengthDown(int[] x) {
+ int result = 0;
+ for (int i = x.length - 1; i != -1; i--) {
+ result += x[i];
+ }
+ return result;
+ }
+
/// CHECK-START: int Main.linearDoWhileUp() BCE (before)
/// CHECK-DAG: BoundsCheck
//
@@ -605,729 +633,7 @@ public class Main {
}
}
- /// CHECK-START: void Main.bubble(int[]) BCE (before)
- /// CHECK-DAG: BoundsCheck
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.bubble(int[]) BCE (after)
- /// CHECK-NOT: BoundsCheck
- // TODO: also CHECK-NOT: Deoptimize, see b/27151190
- private static void bubble(int[] a) {
- for (int i = a.length; --i >= 0;) {
- for (int j = 0; j < i; j++) {
- if (a[j] > a[j+1]) {
- int tmp = a[j];
- a[j] = a[j+1];
- a[j+1] = tmp;
- }
- }
- }
- }
-
- /// CHECK-START: int Main.periodicIdiom(int) BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.periodicIdiom(int) BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int periodicIdiom(int tc) {
- int[] x = { 1, 3 };
- // Loop with periodic sequence (0, 1).
- int k = 0;
- int result = 0;
- for (int i = 0; i < tc; i++) {
- result += x[k];
- k = 1 - k;
- }
- return result;
- }
-
- /// CHECK-START: int Main.periodicSequence2(int) BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.periodicSequence2(int) BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int periodicSequence2(int tc) {
- int[] x = { 1, 3 };
- // Loop with periodic sequence (0, 1).
- int k = 0;
- int l = 1;
- int result = 0;
- for (int i = 0; i < tc; i++) {
- result += x[k];
- int t = l;
- l = k;
- k = t;
- }
- return result;
- }
-
- /// CHECK-START: int Main.periodicSequence4(int) BCE (before)
- /// CHECK-DAG: BoundsCheck
- /// CHECK-DAG: BoundsCheck
- /// CHECK-DAG: BoundsCheck
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.periodicSequence4(int) BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int periodicSequence4(int tc) {
- int[] x = { 1, 3, 5, 7 };
- // Loop with periodic sequence (0, 1, 2, 3).
- int k = 0;
- int l = 1;
- int m = 2;
- int n = 3;
- int result = 0;
- for (int i = 0; i < tc; i++) {
- result += x[k] + x[l] + x[m] + x[n]; // all used at once
- int t = n;
- n = k;
- k = l;
- l = m;
- m = t;
- }
- return result;
- }
-
- /// CHECK-START: int Main.justRightUp1() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justRightUp1() BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int justRightUp1() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int result = 0;
- for (int i = Integer.MAX_VALUE - 10, k = 0; i < Integer.MAX_VALUE; i++) {
- result += x[k++];
- }
- return result;
- }
-
- /// CHECK-START: int Main.justRightUp2() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justRightUp2() BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int justRightUp2() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int result = 0;
- for (int i = Integer.MAX_VALUE - 10; i < Integer.MAX_VALUE; i++) {
- result += x[i - Integer.MAX_VALUE + 10];
- }
- return result;
- }
-
- /// CHECK-START: int Main.justRightUp3() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justRightUp3() BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int justRightUp3() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int result = 0;
- for (int i = Integer.MAX_VALUE - 10, k = 0; i <= Integer.MAX_VALUE - 1; i++) {
- result += x[k++];
- }
- return result;
- }
-
- /// CHECK-START: int Main.justOOBUp() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justOOBUp() BCE (after)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justOOBUp() BCE (after)
- /// CHECK-NOT: Deoptimize
- private static int justOOBUp() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int result = 0;
- // Infinite loop!
- for (int i = Integer.MAX_VALUE - 9, k = 0; i <= Integer.MAX_VALUE; i++) {
- result += x[k++];
- }
- return result;
- }
-
- /// CHECK-START: int Main.justRightDown1() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justRightDown1() BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int justRightDown1() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int result = 0;
- for (int i = Integer.MIN_VALUE + 10, k = 0; i > Integer.MIN_VALUE; i--) {
- result += x[k++];
- }
- return result;
- }
-
- /// CHECK-START: int Main.justRightDown2() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justRightDown2() BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int justRightDown2() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int result = 0;
- for (int i = Integer.MIN_VALUE + 10; i > Integer.MIN_VALUE; i--) {
- result += x[Integer.MAX_VALUE + i];
- }
- return result;
- }
-
- /// CHECK-START: int Main.justRightDown3() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justRightDown3() BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int justRightDown3() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int result = 0;
- for (int i = Integer.MIN_VALUE + 10, k = 0; i >= Integer.MIN_VALUE + 1; i--) {
- result += x[k++];
- }
- return result;
- }
-
- /// CHECK-START: int Main.justOOBDown() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justOOBDown() BCE (after)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justOOBDown() BCE (after)
- /// CHECK-NOT: Deoptimize
- private static int justOOBDown() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int result = 0;
- // Infinite loop!
- for (int i = Integer.MIN_VALUE + 9, k = 0; i >= Integer.MIN_VALUE; i--) {
- result += x[k++];
- }
- return result;
- }
-
- /// CHECK-START: void Main.lowerOOB(int[]) BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.lowerOOB(int[]) BCE (after)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.lowerOOB(int[]) BCE (after)
- /// CHECK-NOT: Deoptimize
- private static void lowerOOB(int[] x) {
- // OOB!
- for (int i = -1; i < x.length; i++) {
- sResult += x[i];
- }
- }
-
- /// CHECK-START: void Main.upperOOB(int[]) BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.upperOOB(int[]) BCE (after)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.upperOOB(int[]) BCE (after)
- /// CHECK-NOT: Deoptimize
- private static void upperOOB(int[] x) {
- // OOB!
- for (int i = 0; i <= x.length; i++) {
- sResult += x[i];
- }
- }
-
- /// CHECK-START: void Main.doWhileUpOOB() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.doWhileUpOOB() BCE (after)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.doWhileUpOOB() BCE (after)
- /// CHECK-NOT: Deoptimize
- private static void doWhileUpOOB() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int i = 0;
- // OOB!
- do {
- sResult += x[i++];
- } while (i <= x.length);
- }
-
- /// CHECK-START: void Main.doWhileDownOOB() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.doWhileDownOOB() BCE (after)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.doWhileDownOOB() BCE (after)
- /// CHECK-NOT: Deoptimize
- private static void doWhileDownOOB() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int i = x.length - 1;
- // OOB!
- do {
- sResult += x[i--];
- } while (-1 <= i);
- }
-
- /// CHECK-START: void Main.hiddenOOB1(int) BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.hiddenOOB1(int) BCE (after)
- /// CHECK-DAG: Deoptimize
- //
- /// CHECK-START: void Main.hiddenOOB1(int) BCE (after)
- /// CHECK-NOT: BoundsCheck
- private static void hiddenOOB1(int lo) {
- int[] a = { 1 } ;
- for (int i = lo; i <= 10; i++) {
- // Dangerous loop where careless static range analysis would yield strict upper bound
- // on index j of 5. When, for instance, lo and thus i = -2147483648, the upper bound
- // becomes really positive due to arithmetic wrap-around, causing OOB.
- // Dynamic BCE is feasible though, since it checks the range.
- for (int j = 4; j < i - 5; j++) {
- sResult += a[j - 4];
- }
- }
- }
-
- /// CHECK-START: void Main.hiddenOOB2(int) BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.hiddenOOB2(int) BCE (after)
- /// CHECK-DAG: Deoptimize
- //
- /// CHECK-START: void Main.hiddenOOB2(int) BCE (after)
- /// CHECK-NOT: BoundsCheck
- private static void hiddenOOB2(int hi) {
- int[] a = { 1 } ;
- for (int i = 0; i < hi; i++) {
- // Dangerous loop where careless static range analysis would yield strict lower bound
- // on index j of 5. When, for instance, hi and thus i = 2147483647, the upper bound
- // becomes really negative due to arithmetic wrap-around, causing OOB.
- // Dynamic BCE is feasible though, since it checks the range.
- for (int j = 6; j > i + 5; j--) {
- sResult += a[j - 6];
- }
- }
- }
-
- /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after)
- /// CHECK-NOT: Deoptimize
- private static void hiddenInfiniteOOB() {
- int[] a = { 11 } ;
- for (int i = -1; i <= 0; i++) {
- // Dangerous loop where careless static range analysis would yield a safe upper bound
- // of -3. In reality, due to arithmetic wrap-around (when i = -1, j <= 2147483647;
- // whereas when i = 0, j <= -3), this is an infinite loop that goes OOB.
- for (int j = -3; j <= 2147483646 * i - 3; j++) {
- sResult += a[j + 3];
- }
- }
- }
-
- /// CHECK-START: void Main.hiddenFiniteOOB() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after)
- /// CHECK-DAG: Deoptimize
- //
- /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after)
- /// CHECK-NOT: BoundsCheck
- private static void hiddenFiniteOOB() {
- int[] a = { 111 } ;
- for (int i = -1; i <= 0; i++) {
- // Dangerous loop similar as above where the loop is now finite, but the
- // loop still goes out of bounds for i = -1 due to the large upper bound.
- // Dynamic BCE is feasible though, since it checks the range.
- for (int j = -4; j < 2147483646 * i - 3; j++) {
- sResult += a[j + 4];
- }
- }
- }
-
- /// CHECK-START: int[] Main.add() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int[] Main.add() BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int[] add() {
- int[] a = new int[10];
- for (int i = 0; i <= 3; i++) {
- for (int j = 0; j <= 6; j++) {
- a[i + j] += 1;
- }
- }
- return a;
- }
-
- /// CHECK-START: int[] Main.multiply1() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int[] Main.multiply1() BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int[] multiply1() {
- int[] a = new int[10];
- try {
- for (int i = 0; i <= 3; i++) {
- for (int j = 0; j <= 3; j++) {
- // Range [0,9]: safe.
- a[i * j] += 1;
- }
- }
- } catch (Exception e) {
- a[0] += 1000;
- }
- return a;
- }
-
- /// CHECK-START: int[] Main.multiply2() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int[] Main.multiply2() BCE (after)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int[] Main.multiply2() BCE (after)
- /// CHECK-NOT: Deoptimize
- static int[] multiply2() {
- int[] a = new int[10];
- try {
- for (int i = -3; i <= 3; i++) {
- for (int j = -3; j <= 3; j++) {
- // Range [-9,9]: unsafe.
- a[i * j] += 1;
- }
- }
- } catch (Exception e) {
- a[0] += 1000;
- }
- return a;
- }
-
- /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (before)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: NullCheck loop:<<Loop>>
- /// CHECK-DAG: ArrayLength loop:<<Loop>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: Deoptimize loop:none
- //
- /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after)
- /// CHECK-NOT: NullCheck loop:{{B\d+}}
- /// CHECK-NOT: ArrayLength loop:{{B\d+}}
- /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
- private static int linearDynamicBCE1(int[] x, int lo, int hi) {
- int result = 0;
- for (int i = lo; i < hi; i++) {
- sResult += x[i];
- }
- return result;
- }
-
- /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (before)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: NullCheck loop:<<Loop>>
- /// CHECK-DAG: ArrayLength loop:<<Loop>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: Deoptimize loop:none
- //
- /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after)
- /// CHECK-NOT: NullCheck loop:{{B\d+}}
- /// CHECK-NOT: ArrayLength loop:{{B\d+}}
- /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
- private static int linearDynamicBCE2(int[] x, int lo, int hi, int offset) {
- int result = 0;
- for (int i = lo; i < hi; i++) {
- sResult += x[offset + i];
- }
- return result;
- }
-
- /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (before)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: NullCheck loop:<<Loop>>
- /// CHECK-DAG: ArrayLength loop:<<Loop>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: Deoptimize loop:none
- //
- /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after)
- /// CHECK-NOT: NullCheck loop:{{B\d+}}
- /// CHECK-NOT: ArrayLength loop:{{B\d+}}
- /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
- private static int wrapAroundDynamicBCE(int[] x) {
- int w = 9;
- int result = 0;
- for (int i = 0; i < 10; i++) {
- result += x[w];
- w = i;
- }
- return result;
- }
-
- /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (before)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: NullCheck loop:<<Loop>>
- /// CHECK-DAG: ArrayLength loop:<<Loop>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: Deoptimize loop:none
- //
- /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after)
- /// CHECK-NOT: NullCheck loop:{{B\d+}}
- /// CHECK-NOT: ArrayLength loop:{{B\d+}}
- /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
- private static int periodicDynamicBCE(int[] x) {
- int k = 0;
- int result = 0;
- for (int i = 0; i < 10; i++) {
- result += x[k];
- k = 1 - k;
- }
- return result;
- }
-
- /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: NullCheck loop:<<Loop>>
- /// CHECK-DAG: ArrayLength loop:<<Loop>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: Deoptimize loop:none
- //
- /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
- /// CHECK-NOT: NullCheck loop:{{B\d+}}
- /// CHECK-NOT: ArrayLength loop:{{B\d+}}
- /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
- static int dynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) {
- // This loop could be infinite for hi = max int. Since i is also used
- // as subscript, however, dynamic bce can proceed.
- int result = 0;
- for (int i = lo; i <= hi; i++) {
- result += x[i];
- }
- return result;
- }
-
- /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
- /// CHECK-NOT: Deoptimize
- static int noDynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) {
- // As above, but now the index is not used as subscript,
- // and dynamic bce is not applied.
- int result = 0;
- for (int k = 0, i = lo; i <= hi; i++) {
- result += x[k++];
- }
- return result;
- }
-
- /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (before)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after)
- /// CHECK-NOT: Deoptimize
- static int noDynamicBCEMixedInductionTypes(int[] x, long lo, long hi) {
- int result = 0;
- // Mix of int and long induction.
- int k = 0;
- for (long i = lo; i < hi; i++) {
- result += x[k++];
- }
- return result;
- }
-
- /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (before)
- /// CHECK-DAG: BoundsCheck loop:<<InnerLoop:B\d+>>
- /// CHECK-DAG: ArrayGet loop:<<InnerLoop>>
- /// CHECK-DAG: If loop:<<InnerLoop>>
- /// CHECK-DAG: If loop:<<OuterLoop:B\d+>>
- /// CHECK-EVAL: "<<InnerLoop>>" != "<<OuterLoop>>"
- //
- /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after)
- /// CHECK-DAG: ArrayGet loop:<<InnerLoop:B\d+>>
- /// CHECK-DAG: Deoptimize loop:<<OuterLoop:B\d+>>
- /// CHECK-EVAL: "<<InnerLoop>>" != "<<OuterLoop>>"
- //
- /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after)
- /// CHECK-NOT: BoundsCheck
- //
- // No additional top tests were introduced.
- /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after)
- /// CHECK-DAG: If
- /// CHECK-DAG: If
- /// CHECK-NOT: If
- static int dynamicBCEConstantRange(int[] x) {
- int result = 0;
- for (int i = 2; i <= 6; i++) {
- // Range analysis sees that innermost loop is finite and always taken.
- for (int j = i - 2; j <= i + 2; j++) {
- result += x[j];
- }
- }
- return result;
- }
-
- /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (before)
- /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
- /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
- //
- /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after)
- // Order matters:
- /// CHECK: Deoptimize loop:<<Loop:B\d+>>
- // CHECK-NOT: Goto loop:<<Loop>>
- /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
- /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
- /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
- /// CHECK: Goto loop:<<Loop>>
- //
- /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after)
- /// CHECK-DAG: Deoptimize loop:none
- static int dynamicBCEAndConstantIndices(int[] x, int[][] a, int lo, int hi) {
- // Deliberately test array length on a before the loop so that only bounds checks
- // on constant subscripts remain, making them a viable candidate for hoisting.
- if (a.length == 0) {
- return -1;
- }
- // Loop that allows BCE on x[i].
- int result = 0;
- for (int i = lo; i < hi; i++) {
- result += x[i];
- if ((i % 10) != 0) {
- // None of the subscripts inside a conditional are removed by dynamic bce,
- // making them a candidate for deoptimization based on constant indices.
- // Compiler should ensure the array loads are not subsequently hoisted
- // "above" the deoptimization "barrier" on the bounds.
- a[0][i] = 1;
- a[1][i] = 2;
- a[99][i] = 3;
- }
- }
- return result;
- }
-
- /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (before)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: ArrayGet loop:<<Loop>>
- /// CHECK-DAG: ArrayGet loop:<<Loop>>
- /// CHECK-DAG: ArrayGet loop:<<Loop>>
- /// CHECK-DAG: ArrayGet loop:<<Loop>>
- /// CHECK-DAG: ArrayGet loop:<<Loop>>
- /// CHECK-DAG: ArrayGet loop:<<Loop>>
- /// CHECK-DAG: ArrayGet loop:<<Loop>>
- /// CHECK-DAG: ArrayGet loop:<<Loop>>
- // For brevity, just test occurrence of at least one of each in the loop:
- /// CHECK-DAG: NullCheck loop:<<Loop>>
- /// CHECK-DAG: ArrayLength loop:<<Loop>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-NOT: ArrayGet loop:<<Loop>>
- //
- /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after)
- /// CHECK-NOT: NullCheck loop:{{B\d+}}
- /// CHECK-NOT: ArrayLength loop:{{B\d+}}
- /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
- //
- /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after)
- /// CHECK-DAG: Deoptimize loop:none
- static int dynamicBCEAndConstantIndicesAllPrimTypes(int[] q,
- boolean[] r,
- byte[] s,
- char[] t,
- short[] u,
- int[] v,
- long[] w,
- float[] x,
- double[] y, int lo, int hi) {
- int result = 0;
- for (int i = lo; i < hi; i++) {
- // All constant index array references can be hoisted out of the loop during BCE on q[i].
- result += q[i] + (r[0] ? 1 : 0) + (int) s[0] + (int) t[0] + (int) u[0] + (int) v[0] +
- (int) w[0] + (int) x[0] + (int) y[0];
- }
- return result;
- }
-
- /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (before)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: NullCheck loop:<<Loop>>
- /// CHECK-DAG: ArrayLength loop:<<Loop>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- /// CHECK-DAG: ArrayGet loop:<<Loop>>
- /// CHECK-DAG: NullCheck loop:<<Loop>>
- /// CHECK-DAG: ArrayLength loop:<<Loop>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: Deoptimize loop:none
- //
- /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after)
- /// CHECK-NOT: ArrayLength loop:{{B\d+}}
- /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
- static int dynamicBCEAndConstantIndexRefType(int[] q, Integer[] z, int lo, int hi) {
- int result = 0;
- for (int i = lo; i < hi; i++) {
- // Similar to above, but now implicit call to intValue() may prevent hoisting
- // z[0] itself during BCE on q[i]. Therefore, we just check BCE on q[i].
- result += q[i] + z[0];
- }
- return result;
- }
-
- //
- // Verifier.
- //
-
public static void main(String[] args) {
- // Set to run expensive tests for correctness too.
- boolean HEAVY = false;
-
int[] empty = { };
int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
@@ -1392,6 +698,8 @@ public class Main {
// Special forms.
expectEquals(55, linearForNEUp());
expectEquals(55, linearForNEDown());
+ expectEquals(55, linearForNEArrayLengthUp(x));
+ expectEquals(55, linearForNEArrayLengthDown(x));
expectEquals(55, linearDoWhileUp());
expectEquals(55, linearDoWhileDown());
expectEquals(55, linearShort());
@@ -1401,246 +709,6 @@ public class Main {
linearTriangularOnParameter(10);
linearTriangularVariationsInnerStrict(10);
linearTriangularVariationsInnerNonStrict(10);
-
- // Sorting.
- int[] sort = { 5, 4, 1, 9, 10, 2, 7, 6, 3, 8 };
- bubble(sort);
- for (int i = 0; i < 10; i++) {
- expectEquals(sort[i], x[i]);
- }
-
- // Periodic adds (1, 3), one at the time.
- expectEquals(0, periodicIdiom(-1));
- for (int tc = 0; tc < 32; tc++) {
- int expected = (tc >> 1) << 2;
- if ((tc & 1) != 0)
- expected += 1;
- expectEquals(expected, periodicIdiom(tc));
- }
-
- // Periodic adds (1, 3), one at the time.
- expectEquals(0, periodicSequence2(-1));
- for (int tc = 0; tc < 32; tc++) {
- int expected = (tc >> 1) << 2;
- if ((tc & 1) != 0)
- expected += 1;
- expectEquals(expected, periodicSequence2(tc));
- }
-
- // Periodic adds (1, 3, 5, 7), all at once.
- expectEquals(0, periodicSequence4(-1));
- for (int tc = 0; tc < 32; tc++) {
- expectEquals(tc * 16, periodicSequence4(tc));
- }
-
- // Large bounds.
- expectEquals(55, justRightUp1());
- expectEquals(55, justRightUp2());
- expectEquals(55, justRightUp3());
- expectEquals(55, justRightDown1());
- expectEquals(55, justRightDown2());
- expectEquals(55, justRightDown3());
- sResult = 0;
- try {
- justOOBUp();
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult = 1;
- }
- expectEquals(1, sResult);
- sResult = 0;
- try {
- justOOBDown();
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult = 1;
- }
- expectEquals(1, sResult);
-
- // Lower bound goes OOB.
- sResult = 0;
- try {
- lowerOOB(x);
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1000, sResult);
-
- // Upper bound goes OOB.
- sResult = 0;
- try {
- upperOOB(x);
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1055, sResult);
-
- // Do while up goes OOB.
- sResult = 0;
- try {
- doWhileUpOOB();
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1055, sResult);
-
- // Do while down goes OOB.
- sResult = 0;
- try {
- doWhileDownOOB();
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1055, sResult);
-
- // Hidden OOB.
- sResult = 0;
- try {
- hiddenOOB1(10); // no OOB
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1, sResult);
- sResult = 0;
- try {
- hiddenOOB1(-2147483648); // OOB
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1001, sResult);
- sResult = 0;
- try {
- hiddenOOB2(1); // no OOB
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1, sResult);
- if (HEAVY) {
- sResult = 0;
- try {
- hiddenOOB2(2147483647); // OOB
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1002, sResult);
- }
- sResult = 0;
- try {
- hiddenInfiniteOOB();
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1011, sResult);
- sResult = 0;
- try {
- hiddenFiniteOOB();
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1111, sResult);
-
- // Addition.
- {
- int[] e1 ={ 1, 2, 3, 4, 4, 4, 4, 3, 2, 1 };
- int[] a1 = add();
- for (int i = 0; i < 10; i++) {
- expectEquals(a1[i], e1[i]);
- }
- }
-
- // Multiplication.
- {
- int[] e1 = { 7, 1, 2, 2, 1, 0, 2, 0, 0, 1 };
- int[] a1 = multiply1();
- for (int i = 0; i < 10; i++) {
- expectEquals(a1[i], e1[i]);
- }
- int[] e2 = { 1001, 0, 0, 1, 0, 0, 1, 0, 0, 1 };
- int[] a2 = multiply2();
- for (int i = 0; i < 10; i++) {
- expectEquals(a2[i], e2[i]);
- }
- }
-
- // Dynamic BCE.
- sResult = 0;
- try {
- linearDynamicBCE1(x, -1, x.length);
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1000, sResult);
- sResult = 0;
- linearDynamicBCE1(x, 0, x.length);
- expectEquals(55, sResult);
- sResult = 0;
- try {
- linearDynamicBCE1(x, 0, x.length + 1);
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1055, sResult);
-
- // Dynamic BCE with offset.
- sResult = 0;
- try {
- linearDynamicBCE2(x, 0, x.length, -1);
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1000, sResult);
- sResult = 0;
- linearDynamicBCE2(x, 0, x.length, 0);
- expectEquals(55, sResult);
- sResult = 0;
- try {
- linearDynamicBCE2(x, 0, x.length, 1);
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1054, sResult);
-
- // Dynamic BCE candidates.
- expectEquals(55, wrapAroundDynamicBCE(x));
- expectEquals(15, periodicDynamicBCE(x));
- expectEquals(55, dynamicBCEPossiblyInfiniteLoop(x, 0, 9));
- expectEquals(55, noDynamicBCEPossiblyInfiniteLoop(x, 0, 9));
- expectEquals(55, noDynamicBCEMixedInductionTypes(x, 0, 10));
- expectEquals(125, dynamicBCEConstantRange(x));
-
- // Dynamic BCE combined with constant indices.
- int[][] a;
- a = new int[0][0];
- expectEquals(-1, dynamicBCEAndConstantIndices(x, a, 0, 10));
- a = new int[100][10];
- expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10));
- for (int i = 0; i < 10; i++) {
- expectEquals((i % 10) != 0 ? 1 : 0, a[0][i]);
- expectEquals((i % 10) != 0 ? 2 : 0, a[1][i]);
- expectEquals((i % 10) != 0 ? 3 : 0, a[99][i]);
- }
- a = new int[2][10];
- sResult = 0;
- try {
- expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10));
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult = 1;
- }
- expectEquals(1, sResult);
- expectEquals(a[0][1], 1);
- expectEquals(a[1][1], 2);
-
- // Dynamic BCE combined with constant indices of all types.
- boolean[] x1 = { true };
- byte[] x2 = { 2 };
- char[] x3 = { 3 };
- short[] x4 = { 4 };
- int[] x5 = { 5 };
- long[] x6 = { 6 };
- float[] x7 = { 7 };
- double[] x8 = { 8 };
- expectEquals(415,
- dynamicBCEAndConstantIndicesAllPrimTypes(x, x1, x2, x3, x4, x5, x6, x7, x8, 0, 10));
- Integer[] x9 = { 9 };
- expectEquals(145, dynamicBCEAndConstantIndexRefType(x, x9, 0, 10));
}
private static void expectEquals(int expected, int result) {
diff --git a/test/530-checker-loops2/expected.txt b/test/530-checker-loops2/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/530-checker-loops2/expected.txt
diff --git a/test/530-checker-loops2/info.txt b/test/530-checker-loops2/info.txt
new file mode 100644
index 0000000000..f5d334d011
--- /dev/null
+++ b/test/530-checker-loops2/info.txt
@@ -0,0 +1 @@
+Test on loop optimizations.
diff --git a/test/530-checker-loops2/src/Main.java b/test/530-checker-loops2/src/Main.java
new file mode 100644
index 0000000000..64be1a2be4
--- /dev/null
+++ b/test/530-checker-loops2/src/Main.java
@@ -0,0 +1,999 @@
+/*
+ * 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.
+ */
+
+//
+// Test on loop optimizations.
+//
+public class Main {
+
+ static int sResult;
+
+ //
+ // Various sequence variables used in bound checks.
+ //
+
+ /// CHECK-START: void Main.bubble(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.bubble(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ // TODO: also CHECK-NOT: Deoptimize, see b/27151190
+ private static void bubble(int[] a) {
+ for (int i = a.length; --i >= 0;) {
+ for (int j = 0; j < i; j++) {
+ if (a[j] > a[j+1]) {
+ int tmp = a[j];
+ a[j] = a[j+1];
+ a[j+1] = tmp;
+ }
+ }
+ }
+ }
+
+ /// CHECK-START: int Main.periodicIdiom(int) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.periodicIdiom(int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int periodicIdiom(int tc) {
+ int[] x = { 1, 3 };
+ // Loop with periodic sequence (0, 1).
+ int k = 0;
+ int result = 0;
+ for (int i = 0; i < tc; i++) {
+ result += x[k];
+ k = 1 - k;
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.periodicSequence2(int) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.periodicSequence2(int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int periodicSequence2(int tc) {
+ int[] x = { 1, 3 };
+ // Loop with periodic sequence (0, 1).
+ int k = 0;
+ int l = 1;
+ int result = 0;
+ for (int i = 0; i < tc; i++) {
+ result += x[k];
+ int t = l;
+ l = k;
+ k = t;
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.periodicSequence4(int) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.periodicSequence4(int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int periodicSequence4(int tc) {
+ int[] x = { 1, 3, 5, 7 };
+ // Loop with periodic sequence (0, 1, 2, 3).
+ int k = 0;
+ int l = 1;
+ int m = 2;
+ int n = 3;
+ int result = 0;
+ for (int i = 0; i < tc; i++) {
+ result += x[k] + x[l] + x[m] + x[n]; // all used at once
+ int t = n;
+ n = k;
+ k = l;
+ l = m;
+ m = t;
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justRightUp1() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justRightUp1() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int justRightUp1() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ for (int i = Integer.MAX_VALUE - 10, k = 0; i < Integer.MAX_VALUE; i++) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justRightUp2() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justRightUp2() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int justRightUp2() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ for (int i = Integer.MAX_VALUE - 10; i < Integer.MAX_VALUE; i++) {
+ result += x[i - Integer.MAX_VALUE + 10];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justRightUp3() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justRightUp3() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int justRightUp3() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ for (int i = Integer.MAX_VALUE - 10, k = 0; i <= Integer.MAX_VALUE - 1; i++) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justOOBUp() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justOOBUp() BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justOOBUp() BCE (after)
+ /// CHECK-NOT: Deoptimize
+ private static int justOOBUp() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ // Infinite loop!
+ for (int i = Integer.MAX_VALUE - 9, k = 0; i <= Integer.MAX_VALUE; i++) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justRightDown1() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justRightDown1() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int justRightDown1() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ for (int i = Integer.MIN_VALUE + 10, k = 0; i > Integer.MIN_VALUE; i--) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justRightDown2() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justRightDown2() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int justRightDown2() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ for (int i = Integer.MIN_VALUE + 10; i > Integer.MIN_VALUE; i--) {
+ result += x[Integer.MAX_VALUE + i];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justRightDown3() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justRightDown3() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int justRightDown3() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ for (int i = Integer.MIN_VALUE + 10, k = 0; i >= Integer.MIN_VALUE + 1; i--) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justOOBDown() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justOOBDown() BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justOOBDown() BCE (after)
+ /// CHECK-NOT: Deoptimize
+ private static int justOOBDown() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ // Infinite loop!
+ for (int i = Integer.MIN_VALUE + 9, k = 0; i >= Integer.MIN_VALUE; i--) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: void Main.lowerOOB(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.lowerOOB(int[]) BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.lowerOOB(int[]) BCE (after)
+ /// CHECK-NOT: Deoptimize
+ private static void lowerOOB(int[] x) {
+ // OOB!
+ for (int i = -1; i < x.length; i++) {
+ sResult += x[i];
+ }
+ }
+
+ /// CHECK-START: void Main.upperOOB(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.upperOOB(int[]) BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.upperOOB(int[]) BCE (after)
+ /// CHECK-NOT: Deoptimize
+ private static void upperOOB(int[] x) {
+ // OOB!
+ for (int i = 0; i <= x.length; i++) {
+ sResult += x[i];
+ }
+ }
+
+ /// CHECK-START: void Main.doWhileUpOOB() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.doWhileUpOOB() BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.doWhileUpOOB() BCE (after)
+ /// CHECK-NOT: Deoptimize
+ private static void doWhileUpOOB() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int i = 0;
+ // OOB!
+ do {
+ sResult += x[i++];
+ } while (i <= x.length);
+ }
+
+ /// CHECK-START: void Main.doWhileDownOOB() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.doWhileDownOOB() BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.doWhileDownOOB() BCE (after)
+ /// CHECK-NOT: Deoptimize
+ private static void doWhileDownOOB() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int i = x.length - 1;
+ // OOB!
+ do {
+ sResult += x[i--];
+ } while (-1 <= i);
+ }
+
+ /// CHECK-START: void Main.hiddenOOB1(int) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.hiddenOOB1(int) BCE (after)
+ /// CHECK-DAG: Deoptimize
+ //
+ /// CHECK-START: void Main.hiddenOOB1(int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static void hiddenOOB1(int lo) {
+ int[] a = { 1 } ;
+ for (int i = lo; i <= 10; i++) {
+ // Dangerous loop where careless static range analysis would yield strict upper bound
+ // on index j of 5. When, for instance, lo and thus i = -2147483648, the upper bound
+ // becomes really positive due to arithmetic wrap-around, causing OOB.
+ // Dynamic BCE is feasible though, since it checks the range.
+ for (int j = 4; j < i - 5; j++) {
+ sResult += a[j - 4];
+ }
+ }
+ }
+
+ /// CHECK-START: void Main.hiddenOOB2(int) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.hiddenOOB2(int) BCE (after)
+ /// CHECK-DAG: Deoptimize
+ //
+ /// CHECK-START: void Main.hiddenOOB2(int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static void hiddenOOB2(int hi) {
+ int[] a = { 1 } ;
+ for (int i = 0; i < hi; i++) {
+ // Dangerous loop where careless static range analysis would yield strict lower bound
+ // on index j of 5. When, for instance, hi and thus i = 2147483647, the upper bound
+ // becomes really negative due to arithmetic wrap-around, causing OOB.
+ // Dynamic BCE is feasible though, since it checks the range.
+ for (int j = 6; j > i + 5; j--) {
+ sResult += a[j - 6];
+ }
+ }
+ }
+
+ /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after)
+ /// CHECK-NOT: Deoptimize
+ private static void hiddenInfiniteOOB() {
+ int[] a = { 11 } ;
+ for (int i = -1; i <= 0; i++) {
+ // Dangerous loop where careless static range analysis would yield a safe upper bound
+ // of -3. In reality, due to arithmetic wrap-around (when i = -1, j <= 2147483647;
+ // whereas when i = 0, j <= -3), this is an infinite loop that goes OOB.
+ for (int j = -3; j <= 2147483646 * i - 3; j++) {
+ sResult += a[j + 3];
+ }
+ }
+ }
+
+ /// CHECK-START: void Main.hiddenFiniteOOB() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after)
+ /// CHECK-DAG: Deoptimize
+ //
+ /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static void hiddenFiniteOOB() {
+ int[] a = { 111 } ;
+ for (int i = -1; i <= 0; i++) {
+ // Dangerous loop similar as above where the loop is now finite, but the
+ // loop still goes out of bounds for i = -1 due to the large upper bound.
+ // Dynamic BCE is feasible though, since it checks the range.
+ for (int j = -4; j < 2147483646 * i - 3; j++) {
+ sResult += a[j + 4];
+ }
+ }
+ }
+
+ /// CHECK-START: int[] Main.add() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int[] Main.add() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int[] add() {
+ int[] a = new int[10];
+ for (int i = 0; i <= 3; i++) {
+ for (int j = 0; j <= 6; j++) {
+ a[i + j] += 1;
+ }
+ }
+ return a;
+ }
+
+ /// CHECK-START: int[] Main.multiply1() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int[] Main.multiply1() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int[] multiply1() {
+ int[] a = new int[10];
+ try {
+ for (int i = 0; i <= 3; i++) {
+ for (int j = 0; j <= 3; j++) {
+ // Range [0,9]: safe.
+ a[i * j] += 1;
+ }
+ }
+ } catch (Exception e) {
+ a[0] += 1000;
+ }
+ return a;
+ }
+
+ /// CHECK-START: int[] Main.multiply2() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int[] Main.multiply2() BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int[] Main.multiply2() BCE (after)
+ /// CHECK-NOT: Deoptimize
+ static int[] multiply2() {
+ int[] a = new int[10];
+ try {
+ for (int i = -3; i <= 3; i++) {
+ for (int j = -3; j <= 3; j++) {
+ // Range [-9,9]: unsafe.
+ a[i * j] += 1;
+ }
+ }
+ } catch (Exception e) {
+ a[0] += 1000;
+ }
+ return a;
+ }
+
+ /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (before)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: NullCheck loop:<<Loop>>
+ /// CHECK-DAG: ArrayLength loop:<<Loop>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: Deoptimize loop:none
+ //
+ /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after)
+ /// CHECK-NOT: NullCheck loop:{{B\d+}}
+ /// CHECK-NOT: ArrayLength loop:{{B\d+}}
+ /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
+ private static int linearDynamicBCE1(int[] x, int lo, int hi) {
+ int result = 0;
+ for (int i = lo; i < hi; i++) {
+ sResult += x[i];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (before)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: NullCheck loop:<<Loop>>
+ /// CHECK-DAG: ArrayLength loop:<<Loop>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: Deoptimize loop:none
+ //
+ /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after)
+ /// CHECK-NOT: NullCheck loop:{{B\d+}}
+ /// CHECK-NOT: ArrayLength loop:{{B\d+}}
+ /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
+ private static int linearDynamicBCE2(int[] x, int lo, int hi, int offset) {
+ int result = 0;
+ for (int i = lo; i < hi; i++) {
+ sResult += x[offset + i];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (before)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: NullCheck loop:<<Loop>>
+ /// CHECK-DAG: ArrayLength loop:<<Loop>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: Deoptimize loop:none
+ //
+ /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after)
+ /// CHECK-NOT: NullCheck loop:{{B\d+}}
+ /// CHECK-NOT: ArrayLength loop:{{B\d+}}
+ /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
+ private static int wrapAroundDynamicBCE(int[] x) {
+ int w = 9;
+ int result = 0;
+ for (int i = 0; i < 10; i++) {
+ result += x[w];
+ w = i;
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (before)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: NullCheck loop:<<Loop>>
+ /// CHECK-DAG: ArrayLength loop:<<Loop>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: Deoptimize loop:none
+ //
+ /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after)
+ /// CHECK-NOT: NullCheck loop:{{B\d+}}
+ /// CHECK-NOT: ArrayLength loop:{{B\d+}}
+ /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
+ private static int periodicDynamicBCE(int[] x) {
+ int k = 0;
+ int result = 0;
+ for (int i = 0; i < 10; i++) {
+ result += x[k];
+ k = 1 - k;
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: NullCheck loop:<<Loop>>
+ /// CHECK-DAG: ArrayLength loop:<<Loop>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: Deoptimize loop:none
+ //
+ /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
+ /// CHECK-NOT: NullCheck loop:{{B\d+}}
+ /// CHECK-NOT: ArrayLength loop:{{B\d+}}
+ /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
+ static int dynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) {
+ // This loop could be infinite for hi = max int. Since i is also used
+ // as subscript, however, dynamic bce can proceed.
+ int result = 0;
+ for (int i = lo; i <= hi; i++) {
+ result += x[i];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
+ /// CHECK-NOT: Deoptimize
+ static int noDynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) {
+ // As above, but now the index is not used as subscript,
+ // and dynamic bce is not applied.
+ int result = 0;
+ for (int k = 0, i = lo; i <= hi; i++) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (before)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after)
+ /// CHECK-NOT: Deoptimize
+ static int noDynamicBCEMixedInductionTypes(int[] x, long lo, long hi) {
+ int result = 0;
+ // Mix of int and long induction.
+ int k = 0;
+ for (long i = lo; i < hi; i++) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck loop:<<InnerLoop:B\d+>>
+ /// CHECK-DAG: ArrayGet loop:<<InnerLoop>>
+ /// CHECK-DAG: If loop:<<InnerLoop>>
+ /// CHECK-DAG: If loop:<<OuterLoop:B\d+>>
+ /// CHECK-EVAL: "<<InnerLoop>>" != "<<OuterLoop>>"
+ //
+ /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:<<InnerLoop:B\d+>>
+ /// CHECK-DAG: Deoptimize loop:<<OuterLoop:B\d+>>
+ /// CHECK-EVAL: "<<InnerLoop>>" != "<<OuterLoop>>"
+ //
+ /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ //
+ // No additional top tests were introduced.
+ /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after)
+ /// CHECK-DAG: If
+ /// CHECK-DAG: If
+ /// CHECK-NOT: If
+ static int dynamicBCEConstantRange(int[] x) {
+ int result = 0;
+ for (int i = 2; i <= 6; i++) {
+ // Range analysis sees that innermost loop is finite and always taken.
+ for (int j = i - 2; j <= i + 2; j++) {
+ result += x[j];
+ }
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (before)
+ /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after)
+ // Order matters:
+ /// CHECK: Deoptimize loop:<<Loop:B\d+>>
+ // CHECK-NOT: Goto loop:<<Loop>>
+ /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
+ /// CHECK: Goto loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after)
+ /// CHECK-DAG: Deoptimize loop:none
+ static int dynamicBCEAndConstantIndices(int[] x, int[][] a, int lo, int hi) {
+ // Deliberately test array length on a before the loop so that only bounds checks
+ // on constant subscripts remain, making them a viable candidate for hoisting.
+ if (a.length == 0) {
+ return -1;
+ }
+ // Loop that allows BCE on x[i].
+ int result = 0;
+ for (int i = lo; i < hi; i++) {
+ result += x[i];
+ if ((i % 10) != 0) {
+ // None of the subscripts inside a conditional are removed by dynamic bce,
+ // making them a candidate for deoptimization based on constant indices.
+ // Compiler should ensure the array loads are not subsequently hoisted
+ // "above" the deoptimization "barrier" on the bounds.
+ a[0][i] = 1;
+ a[1][i] = 2;
+ a[99][i] = 3;
+ }
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (before)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: ArrayGet loop:<<Loop>>
+ // For brevity, just test occurrence of at least one of each in the loop:
+ /// CHECK-DAG: NullCheck loop:<<Loop>>
+ /// CHECK-DAG: ArrayLength loop:<<Loop>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-NOT: ArrayGet loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after)
+ /// CHECK-NOT: NullCheck loop:{{B\d+}}
+ /// CHECK-NOT: ArrayLength loop:{{B\d+}}
+ /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
+ //
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after)
+ /// CHECK-DAG: Deoptimize loop:none
+ static int dynamicBCEAndConstantIndicesAllPrimTypes(int[] q,
+ boolean[] r,
+ byte[] s,
+ char[] t,
+ short[] u,
+ int[] v,
+ long[] w,
+ float[] x,
+ double[] y, int lo, int hi) {
+ int result = 0;
+ for (int i = lo; i < hi; i++) {
+ // All constant index array references can be hoisted out of the loop during BCE on q[i].
+ result += q[i] + (r[0] ? 1 : 0) + (int) s[0] + (int) t[0] + (int) u[0] + (int) v[0] +
+ (int) w[0] + (int) x[0] + (int) y[0];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (before)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: NullCheck loop:<<Loop>>
+ /// CHECK-DAG: ArrayLength loop:<<Loop>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ /// CHECK-DAG: ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: NullCheck loop:<<Loop>>
+ /// CHECK-DAG: ArrayLength loop:<<Loop>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Deoptimize loop:none
+ //
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after)
+ /// CHECK-NOT: ArrayLength loop:{{B\d+}}
+ /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
+ static int dynamicBCEAndConstantIndexRefType(int[] q, Integer[] z, int lo, int hi) {
+ int result = 0;
+ for (int i = lo; i < hi; i++) {
+ // Similar to above, but now implicit call to intValue() may prevent hoisting
+ // z[0] itself during BCE on q[i]. Therefore, we just check BCE on q[i].
+ result += q[i] + z[0];
+ }
+ return result;
+ }
+
+ //
+ // Verifier.
+ //
+
+ public static void main(String[] args) {
+ // Set to run expensive tests for correctness too.
+ boolean HEAVY = false;
+
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+
+ // Sorting.
+ int[] sort = { 5, 4, 1, 9, 10, 2, 7, 6, 3, 8 };
+ bubble(sort);
+ for (int i = 0; i < 10; i++) {
+ expectEquals(sort[i], x[i]);
+ }
+
+ // Periodic adds (1, 3), one at the time.
+ expectEquals(0, periodicIdiom(-1));
+ for (int tc = 0; tc < 32; tc++) {
+ int expected = (tc >> 1) << 2;
+ if ((tc & 1) != 0)
+ expected += 1;
+ expectEquals(expected, periodicIdiom(tc));
+ }
+
+ // Periodic adds (1, 3), one at the time.
+ expectEquals(0, periodicSequence2(-1));
+ for (int tc = 0; tc < 32; tc++) {
+ int expected = (tc >> 1) << 2;
+ if ((tc & 1) != 0)
+ expected += 1;
+ expectEquals(expected, periodicSequence2(tc));
+ }
+
+ // Periodic adds (1, 3, 5, 7), all at once.
+ expectEquals(0, periodicSequence4(-1));
+ for (int tc = 0; tc < 32; tc++) {
+ expectEquals(tc * 16, periodicSequence4(tc));
+ }
+
+ // Large bounds.
+ expectEquals(55, justRightUp1());
+ expectEquals(55, justRightUp2());
+ expectEquals(55, justRightUp3());
+ expectEquals(55, justRightDown1());
+ expectEquals(55, justRightDown2());
+ expectEquals(55, justRightDown3());
+ sResult = 0;
+ try {
+ justOOBUp();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult = 1;
+ }
+ expectEquals(1, sResult);
+ sResult = 0;
+ try {
+ justOOBDown();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult = 1;
+ }
+ expectEquals(1, sResult);
+
+ // Lower bound goes OOB.
+ sResult = 0;
+ try {
+ lowerOOB(x);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1000, sResult);
+
+ // Upper bound goes OOB.
+ sResult = 0;
+ try {
+ upperOOB(x);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1055, sResult);
+
+ // Do while up goes OOB.
+ sResult = 0;
+ try {
+ doWhileUpOOB();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1055, sResult);
+
+ // Do while down goes OOB.
+ sResult = 0;
+ try {
+ doWhileDownOOB();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1055, sResult);
+
+ // Hidden OOB.
+ sResult = 0;
+ try {
+ hiddenOOB1(10); // no OOB
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1, sResult);
+ sResult = 0;
+ try {
+ hiddenOOB1(-2147483648); // OOB
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1001, sResult);
+ sResult = 0;
+ try {
+ hiddenOOB2(1); // no OOB
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1, sResult);
+ if (HEAVY) {
+ sResult = 0;
+ try {
+ hiddenOOB2(2147483647); // OOB
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1002, sResult);
+ }
+ sResult = 0;
+ try {
+ hiddenInfiniteOOB();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1011, sResult);
+ sResult = 0;
+ try {
+ hiddenFiniteOOB();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1111, sResult);
+
+ // Addition.
+ {
+ int[] e1 ={ 1, 2, 3, 4, 4, 4, 4, 3, 2, 1 };
+ int[] a1 = add();
+ for (int i = 0; i < 10; i++) {
+ expectEquals(a1[i], e1[i]);
+ }
+ }
+
+ // Multiplication.
+ {
+ int[] e1 = { 7, 1, 2, 2, 1, 0, 2, 0, 0, 1 };
+ int[] a1 = multiply1();
+ for (int i = 0; i < 10; i++) {
+ expectEquals(a1[i], e1[i]);
+ }
+ int[] e2 = { 1001, 0, 0, 1, 0, 0, 1, 0, 0, 1 };
+ int[] a2 = multiply2();
+ for (int i = 0; i < 10; i++) {
+ expectEquals(a2[i], e2[i]);
+ }
+ }
+
+ // Dynamic BCE.
+ sResult = 0;
+ try {
+ linearDynamicBCE1(x, -1, x.length);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1000, sResult);
+ sResult = 0;
+ linearDynamicBCE1(x, 0, x.length);
+ expectEquals(55, sResult);
+ sResult = 0;
+ try {
+ linearDynamicBCE1(x, 0, x.length + 1);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1055, sResult);
+
+ // Dynamic BCE with offset.
+ sResult = 0;
+ try {
+ linearDynamicBCE2(x, 0, x.length, -1);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1000, sResult);
+ sResult = 0;
+ linearDynamicBCE2(x, 0, x.length, 0);
+ expectEquals(55, sResult);
+ sResult = 0;
+ try {
+ linearDynamicBCE2(x, 0, x.length, 1);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1054, sResult);
+
+ // Dynamic BCE candidates.
+ expectEquals(55, wrapAroundDynamicBCE(x));
+ expectEquals(15, periodicDynamicBCE(x));
+ expectEquals(55, dynamicBCEPossiblyInfiniteLoop(x, 0, 9));
+ expectEquals(55, noDynamicBCEPossiblyInfiniteLoop(x, 0, 9));
+ expectEquals(55, noDynamicBCEMixedInductionTypes(x, 0, 10));
+ expectEquals(125, dynamicBCEConstantRange(x));
+
+ // Dynamic BCE combined with constant indices.
+ int[][] a;
+ a = new int[0][0];
+ expectEquals(-1, dynamicBCEAndConstantIndices(x, a, 0, 10));
+ a = new int[100][10];
+ expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10));
+ for (int i = 0; i < 10; i++) {
+ expectEquals((i % 10) != 0 ? 1 : 0, a[0][i]);
+ expectEquals((i % 10) != 0 ? 2 : 0, a[1][i]);
+ expectEquals((i % 10) != 0 ? 3 : 0, a[99][i]);
+ }
+ a = new int[2][10];
+ sResult = 0;
+ try {
+ expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10));
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult = 1;
+ }
+ expectEquals(1, sResult);
+ expectEquals(a[0][1], 1);
+ expectEquals(a[1][1], 2);
+
+ // Dynamic BCE combined with constant indices of all types.
+ boolean[] x1 = { true };
+ byte[] x2 = { 2 };
+ char[] x3 = { 3 };
+ short[] x4 = { 4 };
+ int[] x5 = { 5 };
+ long[] x6 = { 6 };
+ float[] x7 = { 7 };
+ double[] x8 = { 8 };
+ expectEquals(415,
+ dynamicBCEAndConstantIndicesAllPrimTypes(x, x1, x2, x3, x4, x5, x6, x7, x8, 0, 10));
+ Integer[] x9 = { 9 };
+ expectEquals(145, dynamicBCEAndConstantIndexRefType(x, x9, 0, 10));
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/550-checker-multiply-accumulate/src/Main.java b/test/550-checker-multiply-accumulate/src/Main.java
index 87a89bd9dc..09376a2054 100644
--- a/test/550-checker-multiply-accumulate/src/Main.java
+++ b/test/550-checker-multiply-accumulate/src/Main.java
@@ -47,7 +47,7 @@ public class Main {
/// CHECK: <<Acc:i\d+>> ParameterValue
/// CHECK: <<Left:i\d+>> ParameterValue
/// CHECK: <<Right:i\d+>> ParameterValue
- /// CHECK: <<MulAdd:i\d+>> Arm64MultiplyAccumulate [<<Acc>>,<<Left>>,<<Right>>] kind:Add
+ /// CHECK: <<MulAdd:i\d+>> MultiplyAccumulate [<<Acc>>,<<Left>>,<<Right>>] kind:Add
/// CHECK: Return [<<MulAdd>>]
/// CHECK-START-ARM64: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm64 (after)
@@ -57,6 +57,28 @@ public class Main {
/// CHECK-START-ARM64: int Main.$opt$noinline$mulAdd(int, int, int) disassembly (after)
/// CHECK: madd w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}}
+ /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm (before)
+ /// CHECK: <<Acc:i\d+>> ParameterValue
+ /// CHECK: <<Left:i\d+>> ParameterValue
+ /// CHECK: <<Right:i\d+>> ParameterValue
+ /// CHECK: <<Mul:i\d+>> Mul [<<Left>>,<<Right>>]
+ /// CHECK: <<Add:i\d+>> Add [<<Acc>>,<<Mul>>]
+ /// CHECK: Return [<<Add>>]
+
+ /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm (after)
+ /// CHECK: <<Acc:i\d+>> ParameterValue
+ /// CHECK: <<Left:i\d+>> ParameterValue
+ /// CHECK: <<Right:i\d+>> ParameterValue
+ /// CHECK: <<MulAdd:i\d+>> MultiplyAccumulate [<<Acc>>,<<Left>>,<<Right>>] kind:Add
+ /// CHECK: Return [<<MulAdd>>]
+
+ /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm (after)
+ /// CHECK-NOT: Mul
+ /// CHECK-NOT: Add
+
+ /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) disassembly (after)
+ /// CHECK: mla r{{\d+}}, r{{\d+}}, r{{\d+}}, r{{\d+}}
+
public static int $opt$noinline$mulAdd(int acc, int left, int right) {
if (doThrow) throw new Error();
return acc + left * right;
@@ -78,7 +100,7 @@ public class Main {
/// CHECK: <<Acc:j\d+>> ParameterValue
/// CHECK: <<Left:j\d+>> ParameterValue
/// CHECK: <<Right:j\d+>> ParameterValue
- /// CHECK: <<MulSub:j\d+>> Arm64MultiplyAccumulate [<<Acc>>,<<Left>>,<<Right>>] kind:Sub
+ /// CHECK: <<MulSub:j\d+>> MultiplyAccumulate [<<Acc>>,<<Left>>,<<Right>>] kind:Sub
/// CHECK: Return [<<MulSub>>]
/// CHECK-START-ARM64: long Main.$opt$noinline$mulSub(long, long, long) instruction_simplifier_arm64 (after)
@@ -88,6 +110,17 @@ public class Main {
/// CHECK-START-ARM64: long Main.$opt$noinline$mulSub(long, long, long) disassembly (after)
/// CHECK: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}}
+ /// CHECK-START-ARM: long Main.$opt$noinline$mulSub(long, long, long) instruction_simplifier_arm (before)
+ /// CHECK: <<Acc:j\d+>> ParameterValue
+ /// CHECK: <<Left:j\d+>> ParameterValue
+ /// CHECK: <<Right:j\d+>> ParameterValue
+ /// CHECK: <<Mul:j\d+>> Mul [<<Left>>,<<Right>>]
+ /// CHECK: <<Sub:j\d+>> Sub [<<Acc>>,<<Mul>>]
+ /// CHECK: Return [<<Sub>>]
+
+ /// CHECK-START-ARM: long Main.$opt$noinline$mulSub(long, long, long) instruction_simplifier_arm (after)
+ /// CHECK-NOT: MultiplyAccumulate
+
public static long $opt$noinline$mulSub(long acc, long left, long right) {
if (doThrow) throw new Error();
return acc - left * right;
@@ -117,7 +150,28 @@ public class Main {
/// CHECK: Return [<<Or>>]
/// CHECK-START-ARM64: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm64 (after)
- /// CHECK-NOT: Arm64MultiplyAccumulate
+ /// CHECK-NOT: MultiplyAccumulate
+
+ /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm (before)
+ /// CHECK: <<Acc:i\d+>> ParameterValue
+ /// CHECK: <<Left:i\d+>> ParameterValue
+ /// CHECK: <<Right:i\d+>> ParameterValue
+ /// CHECK: <<Mul:i\d+>> Mul [<<Left>>,<<Right>>]
+ /// CHECK: <<Add:i\d+>> Add [<<Acc>>,<<Mul>>]
+ /// CHECK: <<Or:i\d+>> Or [<<Mul>>,<<Add>>]
+ /// CHECK: Return [<<Or>>]
+
+ /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm (after)
+ /// CHECK: <<Acc:i\d+>> ParameterValue
+ /// CHECK: <<Left:i\d+>> ParameterValue
+ /// CHECK: <<Right:i\d+>> ParameterValue
+ /// CHECK: <<Mul:i\d+>> Mul [<<Left>>,<<Right>>]
+ /// CHECK: <<Add:i\d+>> Add [<<Acc>>,<<Mul>>]
+ /// CHECK: <<Or:i\d+>> Or [<<Mul>>,<<Add>>]
+ /// CHECK: Return [<<Or>>]
+
+ /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm (after)
+ /// CHECK-NOT: MultiplyAccumulate
public static int $opt$noinline$multipleUses1(int acc, int left, int right) {
if (doThrow) throw new Error();
@@ -151,7 +205,30 @@ public class Main {
/// CHECK: Return [<<Res>>]
/// CHECK-START-ARM64: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm64 (after)
- /// CHECK-NOT: Arm64MultiplyAccumulate
+ /// CHECK-NOT: MultiplyAccumulate
+
+ /// CHECK-START-ARM: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm (before)
+ /// CHECK: <<Acc:j\d+>> ParameterValue
+ /// CHECK: <<Left:j\d+>> ParameterValue
+ /// CHECK: <<Right:j\d+>> ParameterValue
+ /// CHECK: <<Mul:j\d+>> Mul [<<Left>>,<<Right>>]
+ /// CHECK: <<Add:j\d+>> Add [<<Acc>>,<<Mul>>]
+ /// CHECK: <<Sub:j\d+>> Sub [<<Acc>>,<<Mul>>]
+ /// CHECK: <<Res:j\d+>> Add [<<Add>>,<<Sub>>]
+ /// CHECK: Return [<<Res>>]
+
+ /// CHECK-START-ARM: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm (after)
+ /// CHECK: <<Acc:j\d+>> ParameterValue
+ /// CHECK: <<Left:j\d+>> ParameterValue
+ /// CHECK: <<Right:j\d+>> ParameterValue
+ /// CHECK: <<Mul:j\d+>> Mul [<<Left>>,<<Right>>]
+ /// CHECK: <<Add:j\d+>> Add [<<Acc>>,<<Mul>>]
+ /// CHECK: <<Sub:j\d+>> Sub [<<Acc>>,<<Mul>>]
+ /// CHECK: <<Res:j\d+>> Add [<<Add>>,<<Sub>>]
+ /// CHECK: Return [<<Res>>]
+
+ /// CHECK-START-ARM: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm (after)
+ /// CHECK-NOT: MultiplyAccumulate
public static long $opt$noinline$multipleUses2(long acc, long left, long right) {
@@ -176,7 +253,7 @@ public class Main {
/// CHECK-START-ARM64: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm64 (after)
/// CHECK: <<Acc:i\d+>> ParameterValue
/// CHECK: <<Var:i\d+>> ParameterValue
- /// CHECK: <<MulAdd:i\d+>> Arm64MultiplyAccumulate [<<Acc>>,<<Acc>>,<<Var>>] kind:Add
+ /// CHECK: <<MulAdd:i\d+>> MultiplyAccumulate [<<Acc>>,<<Acc>>,<<Var>>] kind:Add
/// CHECK: Return [<<MulAdd>>]
/// CHECK-START-ARM64: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm64 (after)
@@ -186,6 +263,27 @@ public class Main {
/// CHECK-START-ARM64: int Main.$opt$noinline$mulPlusOne(int, int) disassembly (after)
/// CHECK: madd w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}}
+ /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm (before)
+ /// CHECK: <<Acc:i\d+>> ParameterValue
+ /// CHECK: <<Var:i\d+>> ParameterValue
+ /// CHECK: <<Const1:i\d+>> IntConstant 1
+ /// CHECK: <<Add:i\d+>> Add [<<Var>>,<<Const1>>]
+ /// CHECK: <<Mul:i\d+>> Mul [<<Acc>>,<<Add>>]
+ /// CHECK: Return [<<Mul>>]
+
+ /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm (after)
+ /// CHECK: <<Acc:i\d+>> ParameterValue
+ /// CHECK: <<Var:i\d+>> ParameterValue
+ /// CHECK: <<MulAdd:i\d+>> MultiplyAccumulate [<<Acc>>,<<Acc>>,<<Var>>] kind:Add
+ /// CHECK: Return [<<MulAdd>>]
+
+ /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm (after)
+ /// CHECK-NOT: Mul
+ /// CHECK-NOT: Add
+
+ /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) disassembly (after)
+ /// CHECK: mla r{{\d+}}, r{{\d+}}, r{{\d+}}, r{{\d+}}
+
public static int $opt$noinline$mulPlusOne(int acc, int var) {
if (doThrow) throw new Error();
return acc * (var + 1);
@@ -207,7 +305,7 @@ public class Main {
/// CHECK-START-ARM64: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm64 (after)
/// CHECK: <<Acc:j\d+>> ParameterValue
/// CHECK: <<Var:j\d+>> ParameterValue
- /// CHECK: <<MulSub:j\d+>> Arm64MultiplyAccumulate [<<Acc>>,<<Acc>>,<<Var>>] kind:Sub
+ /// CHECK: <<MulSub:j\d+>> MultiplyAccumulate [<<Acc>>,<<Acc>>,<<Var>>] kind:Sub
/// CHECK: Return [<<MulSub>>]
/// CHECK-START-ARM64: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm64 (after)
@@ -217,11 +315,114 @@ public class Main {
/// CHECK-START-ARM64: long Main.$opt$noinline$mulMinusOne(long, long) disassembly (after)
/// CHECK: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}}
+ /// CHECK-START-ARM: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm (before)
+ /// CHECK: <<Acc:j\d+>> ParameterValue
+ /// CHECK: <<Var:j\d+>> ParameterValue
+ /// CHECK: <<Const1:j\d+>> LongConstant 1
+ /// CHECK: <<Sub:j\d+>> Sub [<<Const1>>,<<Var>>]
+ /// CHECK: <<Mul:j\d+>> Mul [<<Acc>>,<<Sub>>]
+ /// CHECK: Return [<<Mul>>]
+
+ /// CHECK-START-ARM: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm (after)
+ /// CHECK-NOT: MultiplyAccumulate
public static long $opt$noinline$mulMinusOne(long acc, long var) {
if (doThrow) throw new Error();
return acc * (1 - var);
}
+ /**
+ * Test basic merging of `MUL+NEG` into `MULNEG`.
+ */
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm64 (before)
+ /// CHECK: <<Left:i\d+>> ParameterValue
+ /// CHECK: <<Right:i\d+>> ParameterValue
+ /// CHECK: <<Mul:i\d+>> Mul [<<Left>>,<<Right>>]
+ /// CHECK: <<Neg:i\d+>> Neg [<<Mul>>]
+ /// CHECK: Return [<<Neg>>]
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm64 (after)
+ /// CHECK: <<Left:i\d+>> ParameterValue
+ /// CHECK: <<Right:i\d+>> ParameterValue
+ /// CHECK: <<Const0:i\d+>> IntConstant 0
+ /// CHECK: <<MulNeg:i\d+>> MultiplyAccumulate [<<Const0>>,<<Left>>,<<Right>>] kind:Sub
+ /// CHECK: Return [<<MulNeg>>]
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm64 (after)
+ /// CHECK-NOT: Mul
+ /// CHECK-NOT: Neg
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) disassembly (after)
+ /// CHECK: mneg w{{\d+}}, w{{\d+}}, w{{\d+}}
+
+ /// CHECK-START-ARM: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm (before)
+ /// CHECK: <<Left:i\d+>> ParameterValue
+ /// CHECK: <<Right:i\d+>> ParameterValue
+ /// CHECK: <<Mul:i\d+>> Mul [<<Left>>,<<Right>>]
+ /// CHECK: <<Neg:i\d+>> Neg [<<Mul>>]
+ /// CHECK: Return [<<Neg>>]
+
+ /// CHECK-START-ARM: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm (after)
+ /// CHECK: <<Left:i\d+>> ParameterValue
+ /// CHECK: <<Right:i\d+>> ParameterValue
+ /// CHECK: <<Mul:i\d+>> Mul [<<Left>>,<<Right>>]
+ /// CHECK: <<Neg:i\d+>> Neg [<<Mul>>]
+ /// CHECK: Return [<<Neg>>]
+
+ /// CHECK-START-ARM: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm (after)
+ /// CHECK-NOT: MultiplyAccumulate
+
+ public static int $opt$noinline$mulNeg(int left, int right) {
+ if (doThrow) throw new Error();
+ return - (left * right);
+ }
+
+ /**
+ * Test basic merging of `MUL+NEG` into `MULNEG`.
+ */
+
+ /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm64 (before)
+ /// CHECK: <<Left:j\d+>> ParameterValue
+ /// CHECK: <<Right:j\d+>> ParameterValue
+ /// CHECK: <<Mul:j\d+>> Mul [<<Left>>,<<Right>>]
+ /// CHECK: <<Neg:j\d+>> Neg [<<Mul>>]
+ /// CHECK: Return [<<Neg>>]
+
+ /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm64 (after)
+ /// CHECK: <<Left:j\d+>> ParameterValue
+ /// CHECK: <<Right:j\d+>> ParameterValue
+ /// CHECK: <<Const0:j\d+>> LongConstant 0
+ /// CHECK: <<MulNeg:j\d+>> MultiplyAccumulate [<<Const0>>,<<Left>>,<<Right>>] kind:Sub
+ /// CHECK: Return [<<MulNeg>>]
+
+ /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm64 (after)
+ /// CHECK-NOT: Mul
+ /// CHECK-NOT: Neg
+
+ /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) disassembly (after)
+ /// CHECK: mneg x{{\d+}}, x{{\d+}}, x{{\d+}}
+
+ /// CHECK-START-ARM: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm (before)
+ /// CHECK: <<Left:j\d+>> ParameterValue
+ /// CHECK: <<Right:j\d+>> ParameterValue
+ /// CHECK: <<Mul:j\d+>> Mul [<<Left>>,<<Right>>]
+ /// CHECK: <<Neg:j\d+>> Neg [<<Mul>>]
+ /// CHECK: Return [<<Neg>>]
+
+ /// CHECK-START-ARM: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm (after)
+ /// CHECK: <<Left:j\d+>> ParameterValue
+ /// CHECK: <<Right:j\d+>> ParameterValue
+ /// CHECK: <<Mul:j\d+>> Mul [<<Left>>,<<Right>>]
+ /// CHECK: <<Neg:j\d+>> Neg [<<Mul>>]
+ /// CHECK: Return [<<Neg>>]
+
+ /// CHECK-START-ARM: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm (after)
+ /// CHECK-NOT: MultiplyAccumulate
+
+ public static long $opt$noinline$mulNeg(long left, long right) {
+ if (doThrow) throw new Error();
+ return - (left * right);
+ }
public static void main(String[] args) {
assertIntEquals(7, $opt$noinline$mulAdd(1, 2, 3));
@@ -230,5 +431,7 @@ public class Main {
assertLongEquals(20, $opt$noinline$multipleUses2(10, 11, 12));
assertIntEquals(195, $opt$noinline$mulPlusOne(13, 14));
assertLongEquals(-225, $opt$noinline$mulMinusOne(15, 16));
+ assertIntEquals(-306, $opt$noinline$mulNeg(17, 18));
+ assertLongEquals(-380, $opt$noinline$mulNeg(19, 20));
}
}
diff --git a/test/564-checker-negbitwise/expected.txt b/test/564-checker-negbitwise/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/564-checker-negbitwise/expected.txt
diff --git a/test/564-checker-negbitwise/info.txt b/test/564-checker-negbitwise/info.txt
new file mode 100644
index 0000000000..28b9e9e832
--- /dev/null
+++ b/test/564-checker-negbitwise/info.txt
@@ -0,0 +1 @@
+Test negated bitwise operations simplification on ARM64.
diff --git a/test/564-checker-negbitwise/src/Main.java b/test/564-checker-negbitwise/src/Main.java
new file mode 100644
index 0000000000..3de7be7161
--- /dev/null
+++ b/test/564-checker-negbitwise/src/Main.java
@@ -0,0 +1,207 @@
+/*
+ * 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 {
+
+ // A dummy value to defeat inlining of these routines.
+ static boolean doThrow = false;
+
+ public static void assertIntEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void assertLongEquals(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ /**
+ * Test merging of `NOT+AND` into `BIC`.
+ */
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm64 (before)
+ /// CHECK: <<Base:i\d+>> ParameterValue
+ /// CHECK: <<Mask:i\d+>> ParameterValue
+ /// CHECK: <<Not:i\d+>> Not [<<Mask>>]
+ /// CHECK: <<Op:i\d+>> And [<<Base>>,<<Not>>]
+ /// CHECK: Return [<<Op>>]
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm64 (after)
+ /// CHECK: <<Base:i\d+>> ParameterValue
+ /// CHECK: <<Mask:i\d+>> ParameterValue
+ /// CHECK: <<NegOp:i\d+>> Arm64BitwiseNegatedRight [<<Base>>,<<Mask>>] kind:And
+ /// CHECK: Return [<<NegOp>>]
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm64 (after)
+ /// CHECK-NOT: Not
+ /// CHECK-NOT: And
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$notAnd(int, int) disassembly (after)
+ /// CHECK: bic w{{\d+}}, w{{\d+}}, w{{\d+}}
+
+ public static int $opt$noinline$notAnd(int base, int mask) {
+ if (doThrow) throw new Error();
+ return base & ~mask;
+ }
+
+ /**
+ * Test merging of `NOT+ORR` into `ORN`.
+ */
+
+ /// CHECK-START-ARM64: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm64 (before)
+ /// CHECK: <<Base:j\d+>> ParameterValue
+ /// CHECK: <<Mask:j\d+>> ParameterValue
+ /// CHECK: <<Not:j\d+>> Not [<<Mask>>]
+ /// CHECK: <<Op:j\d+>> Or [<<Base>>,<<Not>>]
+ /// CHECK: Return [<<Op>>]
+
+ /// CHECK-START-ARM64: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm64 (after)
+ /// CHECK: <<Base:j\d+>> ParameterValue
+ /// CHECK: <<Mask:j\d+>> ParameterValue
+ /// CHECK: <<NegOp:j\d+>> Arm64BitwiseNegatedRight [<<Base>>,<<Mask>>] kind:Or
+ /// CHECK: Return [<<NegOp>>]
+
+ /// CHECK-START-ARM64: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm64 (after)
+ /// CHECK-NOT: Not
+ /// CHECK-NOT: Or
+
+ /// CHECK-START-ARM64: long Main.$opt$noinline$notOr(long, long) disassembly (after)
+ /// CHECK: orn x{{\d+}}, x{{\d+}}, x{{\d+}}
+
+ public static long $opt$noinline$notOr(long base, long mask) {
+ if (doThrow) throw new Error();
+ return base | ~mask;
+ }
+
+ /**
+ * Test merging of `NOT+EOR` into `EON`.
+ */
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm64 (before)
+ /// CHECK: <<Base:i\d+>> ParameterValue
+ /// CHECK: <<Mask:i\d+>> ParameterValue
+ /// CHECK: <<Not:i\d+>> Not [<<Mask>>]
+ /// CHECK: <<Op:i\d+>> Xor [<<Base>>,<<Not>>]
+ /// CHECK: Return [<<Op>>]
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm64 (after)
+ /// CHECK: <<Base:i\d+>> ParameterValue
+ /// CHECK: <<Mask:i\d+>> ParameterValue
+ /// CHECK: <<NegOp:i\d+>> Arm64BitwiseNegatedRight [<<Base>>,<<Mask>>] kind:Xor
+ /// CHECK: Return [<<NegOp>>]
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm64 (after)
+ /// CHECK-NOT: Not
+ /// CHECK-NOT: Xor
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$notXor(int, int) disassembly (after)
+ /// CHECK: eon w{{\d+}}, w{{\d+}}, w{{\d+}}
+
+ public static int $opt$noinline$notXor(int base, int mask) {
+ if (doThrow) throw new Error();
+ return base ^ ~mask;
+ }
+
+ /**
+ * Check that the transformation is also done when the base is a constant.
+ */
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$notXorConstant(int) instruction_simplifier_arm64 (before)
+ /// CHECK: <<Mask:i\d+>> ParameterValue
+ /// CHECK: <<Constant:i\d+>> IntConstant
+ /// CHECK: <<Not:i\d+>> Not [<<Mask>>]
+ /// CHECK: <<Op:i\d+>> Xor [<<Not>>,<<Constant>>]
+ /// CHECK: Return [<<Op>>]
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$notXorConstant(int) instruction_simplifier_arm64 (after)
+ /// CHECK: <<Mask:i\d+>> ParameterValue
+ /// CHECK: <<Constant:i\d+>> IntConstant
+ /// CHECK: <<NegOp:i\d+>> Arm64BitwiseNegatedRight [<<Constant>>,<<Mask>>] kind:Xor
+ /// CHECK: Return [<<NegOp>>]
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$notXorConstant(int) instruction_simplifier_arm64 (after)
+ /// CHECK-NOT: Not
+ /// CHECK-NOT: Xor
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$notXorConstant(int) disassembly (after)
+ /// CHECK: mov <<Reg:w\d+>>, #0xf
+ /// CHECK: eon w{{\d+}}, <<Reg>>, w{{\d+}}
+
+ public static int $opt$noinline$notXorConstant(int mask) {
+ if (doThrow) throw new Error();
+ return 0xf ^ ~mask;
+ }
+
+ /**
+ * Check that no transformation is done when Not has multiple uses.
+ */
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$notAndMultipleUses(int, int) instruction_simplifier_arm64 (before)
+ /// CHECK: <<Base:i\d+>> ParameterValue
+ /// CHECK: <<Mask:i\d+>> ParameterValue
+ /// CHECK: <<One:i\d+>> IntConstant
+ /// CHECK: <<Not:i\d+>> Not [<<Mask>>]
+ /// CHECK: <<Op1:i\d+>> And [<<Not>>,<<One>>]
+ /// CHECK: <<Op2:i\d+>> And [<<Base>>,<<Not>>]
+ /// CHECK: <<Add:i\d+>> Add [<<Op1>>,<<Op2>>]
+ /// CHECK: Return [<<Add>>]
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$notAndMultipleUses(int, int) instruction_simplifier_arm64 (after)
+ /// CHECK: <<Base:i\d+>> ParameterValue
+ /// CHECK: <<Mask:i\d+>> ParameterValue
+ /// CHECK: <<One:i\d+>> IntConstant
+ /// CHECK: <<Not:i\d+>> Not [<<Mask>>]
+ /// CHECK: <<Op1:i\d+>> And [<<Not>>,<<One>>]
+ /// CHECK: <<Op2:i\d+>> And [<<Base>>,<<Not>>]
+ /// CHECK: <<Add:i\d+>> Add [<<Op1>>,<<Op2>>]
+ /// CHECK: Return [<<Add>>]
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$notAndMultipleUses(int, int) instruction_simplifier_arm64 (after)
+ /// CHECK-NOT: Arm64BitwiseNegatedRight
+
+ public static int $opt$noinline$notAndMultipleUses(int base, int mask) {
+ if (doThrow) throw new Error();
+ int tmp = ~mask;
+ return (tmp & 0x1) + (base & tmp);
+ }
+
+ /**
+ * Check that no transformation is done when both inputs are Not's.
+ */
+
+ // We don't check the instructions before the pass, since if De Morgan's laws
+ // have been applied then Not/Not/Or is replaced by And/Not.
+
+ /// CHECK-START-ARM64: int Main.$opt$noinline$deMorganOr(int, int) instruction_simplifier_arm64 (after)
+ /// CHECK-NOT: Arm64BitwiseNegatedRight
+
+ public static int $opt$noinline$deMorganOr(int a, int b) {
+ if (doThrow) throw new Error();
+ return ~a | ~b;
+ }
+
+ public static void main(String[] args) {
+ assertIntEquals(0xe, $opt$noinline$notAnd(0xf, 0x1));
+ assertLongEquals(~0x0, $opt$noinline$notOr(0xf, 0x1));
+ assertIntEquals(~0xe, $opt$noinline$notXor(0xf, 0x1));
+ assertIntEquals(~0xe, $opt$noinline$notXorConstant(0x1));
+ assertIntEquals(0xe, $opt$noinline$notAndMultipleUses(0xf, 0x1));
+ assertIntEquals(~0x1, $opt$noinline$deMorganOr(0x3, 0x1));
+ }
+}
diff --git a/test/577-checker-fp2int/expected.txt b/test/577-checker-fp2int/expected.txt
new file mode 100644
index 0000000000..b0aad4deb5
--- /dev/null
+++ b/test/577-checker-fp2int/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/577-checker-fp2int/info.txt b/test/577-checker-fp2int/info.txt
new file mode 100644
index 0000000000..d22a0eab9d
--- /dev/null
+++ b/test/577-checker-fp2int/info.txt
@@ -0,0 +1 @@
+Unit test for float/double to raw bits conversions.
diff --git a/test/577-checker-fp2int/src/Main.java b/test/577-checker-fp2int/src/Main.java
new file mode 100644
index 0000000000..e3f1230beb
--- /dev/null
+++ b/test/577-checker-fp2int/src/Main.java
@@ -0,0 +1,122 @@
+/*
+ * 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: int Main.f2int(float) instruction_simplifier (before)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:FloatFloatToIntBits
+ /// CHECK-DAG: Return [<<Result>>]
+ //
+ /// CHECK-START: int Main.f2int(float) instruction_simplifier (after)
+ /// CHECK-DAG: <<Raw:i\d+>> InvokeStaticOrDirect [<<Arg:f\d+>>] intrinsic:FloatFloatToRawIntBits
+ /// CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Arg>>,<<Arg>>]
+ /// CHECK-DAG: <<Result:i\d+>> Select [<<Raw>>,{{i\d+}},<<Cond>>]
+ /// CHECK-DAG: Return [<<Result>>]
+ private static int f2int(float f) {
+ return Float.floatToIntBits(f);
+ }
+
+ /// CHECK-START: long Main.d2long(double) instruction_simplifier (before)
+ /// CHECK-DAG: <<Result:j\d+>> InvokeStaticOrDirect intrinsic:DoubleDoubleToLongBits
+ /// CHECK-DAG: Return [<<Result>>]
+ //
+ /// CHECK-START: long Main.d2long(double) instruction_simplifier (after)
+ /// CHECK-DAG: <<Raw:j\d+>> InvokeStaticOrDirect [<<Arg:d\d+>>] intrinsic:DoubleDoubleToRawLongBits
+ /// CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Arg>>,<<Arg>>]
+ /// CHECK-DAG: <<Result:j\d+>> Select [<<Raw>>,{{j\d+}},<<Cond>>]
+ /// CHECK-DAG: Return [<<Result>>]
+ private static long d2long(double d) {
+ return Double.doubleToLongBits(d);
+ }
+
+ public static void main(String args[]) {
+ // A few distinct numbers.
+ expectEquals32(0xff800000, f2int(Float.NEGATIVE_INFINITY));
+ expectEquals32(0xbf800000, f2int(-1.0f));
+ expectEquals32(0x80000000, f2int(-0.0f));
+ expectEquals32(0x00000000, f2int(+0.0f));
+ expectEquals32(0x3f800000, f2int(+1.0f));
+ expectEquals32(0x7f800000, f2int(Float.POSITIVE_INFINITY));
+
+ // A few others.
+ for (int i = 0; i <= 100; i++) {
+ expectEquals32(i, f2int(Float.intBitsToFloat(i)));
+ }
+
+ // A few NaN numbers.
+ float[] fvals = {
+ Float.intBitsToFloat(0x7f800001),
+ Float.intBitsToFloat(0x7fa00000),
+ Float.intBitsToFloat(0x7fc00000),
+ Float.intBitsToFloat(0x7fffffff),
+ Float.intBitsToFloat(0xff800001),
+ Float.intBitsToFloat(0xffa00000),
+ Float.intBitsToFloat(0xffc00000),
+ Float.intBitsToFloat(0xffffffff)
+ };
+ for (int i = 0; i < fvals.length; i++) {
+ expectEquals32(0x7fc00000, f2int(fvals[i]));
+ }
+
+ // A few distinct numbers.
+ expectEquals64(0xfff0000000000000L, d2long(Double.NEGATIVE_INFINITY));
+ expectEquals64(0xbff0000000000000L, d2long(-1.0d));
+ expectEquals64(0x8000000000000000L, d2long(-0.0d));
+ expectEquals64(0x0000000000000000L, d2long(+0.0d));
+ expectEquals64(0x3ff0000000000000L, d2long(+1.0d));
+ expectEquals64(0x7ff0000000000000L, d2long(Double.POSITIVE_INFINITY));
+
+ // A few others.
+ for (long l = 0; l <= 100; l++) {
+ expectEquals64(l, d2long(Double.longBitsToDouble(l)));
+ }
+
+ // A few NaN numbers.
+ double[] dvals = {
+ Double.longBitsToDouble(0x7ff0000000000001L),
+ Double.longBitsToDouble(0x7ff4000000000000L),
+ Double.longBitsToDouble(0x7ff8000000000000L),
+ Double.longBitsToDouble(0x7fffffffffffffffL),
+ Double.longBitsToDouble(0xfff0000000000001L),
+ Double.longBitsToDouble(0xfff4000000000000L),
+ Double.longBitsToDouble(0xfff8000000000000L),
+ Double.longBitsToDouble(0xffffffffffffffffL)
+ };
+ for (int i = 0; i < dvals.length; i++) {
+ expectEquals64(0x7ff8000000000000L, d2long(dvals[i]));
+ }
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals32(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: "
+ + Integer.toHexString(expected)
+ + ", found: "
+ + Integer.toHexString(result));
+ }
+ }
+
+ private static void expectEquals64(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: "
+ + Long.toHexString(expected)
+ + ", found: "
+ + Long.toHexString(result));
+ }
+ }
+}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 364be59919..167ad859d2 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -220,6 +220,18 @@ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),
$(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
$(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(ART_TEST_RUN_TEST_SKIP), $(ALL_ADDRESS_SIZES))
+
+# Disable 097-duplicate-method while investigation (broken by latest Jack release, b/27358065)
+TEST_ART_BROKEN_ALL_TARGET_TESTS := \
+ 097-duplicate-method
+
+ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+ $(COMPILER_TYPES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_ALL_TARGET_TESTS), \
+ $(ALL_ADDRESS_SIZES))
+
+TEST_ART_BROKEN_ALL_TARGET_TESTS :=
+
# Tests that are timing sensitive and flaky on heavily loaded systems.
TEST_ART_TIMING_SENSITIVE_RUN_TESTS := \
002-sleep \
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index e6394a999f..46100ae15c 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -208,13 +208,6 @@
"org.apache.harmony.tests.java.util.TimerTaskTest#test_scheduledExecutionTime"]
},
{
- description: "'cat -' does not work anymore",
- result: EXEC_FAILED,
- bug: 26395656,
- modes: [device],
- names: ["org.apache.harmony.tests.java.lang.ProcessTest#test_getOutputStream"]
-},
-{
description: "Missing resource in classpath",
result: EXEC_FAILED,
modes: [device],