Merge "Fix and improve shift and rotate operations."
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 07bd0e3..c747ffa 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -1986,7 +1986,7 @@
const ImageInfo& image_info,
bool* quick_is_interpreted) {
DCHECK(!method->IsResolutionMethod()) << PrettyMethod(method);
- DCHECK(!method->IsImtConflictMethod()) << PrettyMethod(method);
+ DCHECK_NE(method, Runtime::Current()->GetImtConflictMethod()) << PrettyMethod(method);
DCHECK(!method->IsImtUnimplementedMethod()) << PrettyMethod(method);
DCHECK(method->IsInvokable()) << PrettyMethod(method);
DCHECK(!IsInBootImage(method)) << PrettyMethod(method);
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 055061d..528fe44 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -17,7 +17,6 @@
#include "graph_checker.h"
#include <algorithm>
-#include <map>
#include <string>
#include <sstream>
@@ -32,19 +31,19 @@
current_block_ = block;
// Check consistency with respect to predecessors of `block`.
- ArenaSafeMap<HBasicBlock*, size_t> predecessors_count(
- std::less<HBasicBlock*>(), GetGraph()->GetArena()->Adapter(kArenaAllocGraphChecker));
- for (HBasicBlock* p : block->GetPredecessors()) {
- auto it = predecessors_count.find(p);
- if (it != predecessors_count.end()) {
- ++it->second;
- } else {
- predecessors_count.Put(p, 1u);
+ // Note: Counting duplicates with a sorted vector uses up to 6x less memory
+ // than ArenaSafeMap<HBasicBlock*, size_t>.
+ ArenaVector<HBasicBlock*> sorted_predecessors(
+ block->GetPredecessors().begin(),
+ block->GetPredecessors().end(),
+ GetGraph()->GetArena()->Adapter(kArenaAllocGraphChecker));
+ std::sort(sorted_predecessors.begin(), sorted_predecessors.end());
+ for (auto it = sorted_predecessors.begin(), end = sorted_predecessors.end(); it != end; ) {
+ HBasicBlock* p = *it++;
+ size_t p_count_in_block_predecessors = 1u;
+ for (; it != end && *it == p; ++it) {
+ ++p_count_in_block_predecessors;
}
- }
- for (auto& pc : predecessors_count) {
- HBasicBlock* p = pc.first;
- size_t p_count_in_block_predecessors = pc.second;
size_t block_count_in_p_successors =
std::count(p->GetSuccessors().begin(), p->GetSuccessors().end(), block);
if (p_count_in_block_predecessors != block_count_in_p_successors) {
@@ -57,19 +56,19 @@
}
// Check consistency with respect to successors of `block`.
- ArenaSafeMap<HBasicBlock*, size_t> successors_count(
- std::less<HBasicBlock*>(), GetGraph()->GetArena()->Adapter(kArenaAllocGraphChecker));
- for (HBasicBlock* s : block->GetSuccessors()) {
- auto it = successors_count.find(s);
- if (it != successors_count.end()) {
- ++it->second;
- } else {
- successors_count.Put(s, 1u);
+ // Note: Counting duplicates with a sorted vector uses up to 6x less memory
+ // than ArenaSafeMap<HBasicBlock*, size_t>.
+ ArenaVector<HBasicBlock*> sorted_successors(
+ block->GetSuccessors().begin(),
+ block->GetSuccessors().end(),
+ GetGraph()->GetArena()->Adapter(kArenaAllocGraphChecker));
+ std::sort(sorted_successors.begin(), sorted_successors.end());
+ for (auto it = sorted_successors.begin(), end = sorted_successors.end(); it != end; ) {
+ HBasicBlock* s = *it++;
+ size_t s_count_in_block_successors = 1u;
+ for (; it != end && *it == s; ++it) {
+ ++s_count_in_block_successors;
}
- }
- for (auto& sc : successors_count) {
- HBasicBlock* s = sc.first;
- size_t s_count_in_block_successors = sc.second;
size_t block_count_in_s_predecessors =
std::count(s->GetPredecessors().begin(), s->GetPredecessors().end(), block);
if (s_count_in_block_successors != block_count_in_s_predecessors) {
@@ -920,6 +919,19 @@
}
}
+void GraphChecker::VisitNeg(HNeg* instruction) {
+ VisitInstruction(instruction);
+ Primitive::Type input_type = instruction->InputAt(0)->GetType();
+ Primitive::Type result_type = instruction->GetType();
+ if (result_type != Primitive::PrimitiveKind(input_type)) {
+ AddError(StringPrintf("Binary operation %s %d has a result type different "
+ "from its input kind: %s vs %s.",
+ instruction->DebugName(), instruction->GetId(),
+ Primitive::PrettyDescriptor(result_type),
+ Primitive::PrettyDescriptor(input_type)));
+ }
+}
+
void GraphChecker::VisitBinaryOperation(HBinaryOperation* op) {
VisitInstruction(op);
Primitive::Type lhs_type = op->InputAt(0)->GetType();
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index 8da8457..27d5621 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -59,6 +59,7 @@
void VisitInstanceOf(HInstanceOf* check) OVERRIDE;
void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE;
void VisitLoadException(HLoadException* load) OVERRIDE;
+ void VisitNeg(HNeg* instruction) OVERRIDE;
void VisitPackedSwitch(HPackedSwitch* instruction) OVERRIDE;
void VisitReturn(HReturn* ret) OVERRIDE;
void VisitReturnVoid(HReturnVoid* ret) OVERRIDE;
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 4f1e90c..3a9d242 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -382,6 +382,8 @@
void VisitArraySet(HArraySet* array_set) OVERRIDE {
StartAttributeStream("value_can_be_null") << std::boolalpha
<< array_set->GetValueCanBeNull() << std::noboolalpha;
+ StartAttributeStream("needs_type_check") << std::boolalpha
+ << array_set->NeedsTypeCheck() << std::noboolalpha;
}
void VisitCompare(HCompare* compare) OVERRIDE {
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index 8d24e26..266cb10 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -789,7 +789,9 @@
min++;
}
// Do both bounds fit the range?
- int64_t value;
+ // Note: The `value` is initialized to please valgrind - the compiler can reorder
+ // the return value check with the `value` check, b/27651442 .
+ int64_t value = 0;
return IsAtLeast(lower_expr, &value) && value >= min &&
IsAtMost(lower_expr, &value) && value <= max &&
IsAtLeast(upper_expr, &value) && value >= min &&
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 72ea4b6..5b263b3 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -1539,7 +1539,10 @@
HInstruction* distance = invoke->InputAt(1);
// Replace the invoke with an HRor.
if (is_left) {
- distance = new (GetGraph()->GetArena()) HNeg(distance->GetType(), distance);
+ // Unconditionally set the type of the negated distance to `int`,
+ // as shift and rotate operations expect a 32-bit (or narrower)
+ // value for their distance input.
+ distance = new (GetGraph()->GetArena()) HNeg(Primitive::kPrimInt, distance);
invoke->GetBlock()->InsertInstructionBefore(distance, invoke);
}
HRor* ror = new (GetGraph()->GetArena()) HRor(type, value, distance);
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 76e6bbd..1280587 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1780,6 +1780,99 @@
codegen_);
}
+static void CreateIntIntIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetInAt(3, Location::RequiresRegister());
+ locations->SetInAt(4, Location::RequiresRegister());
+
+ locations->SetOut(Location::RequiresRegister());
+}
+
+static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorMIPS* codegen) {
+ MipsAssembler* assembler = codegen->GetAssembler();
+ bool isR6 = codegen->GetInstructionSetFeatures().IsR6();
+ Register base = locations->InAt(1).AsRegister<Register>();
+ Register offset_lo = locations->InAt(2).AsRegisterPairLow<Register>();
+ Register expected = locations->InAt(3).AsRegister<Register>();
+ Register value = locations->InAt(4).AsRegister<Register>();
+ Register out = locations->Out().AsRegister<Register>();
+
+ DCHECK_NE(base, out);
+ DCHECK_NE(offset_lo, out);
+ DCHECK_NE(expected, out);
+
+ if (type == Primitive::kPrimNot) {
+ // Mark card for object assuming new value is stored.
+ codegen->MarkGCCard(base, value);
+ }
+
+ // do {
+ // tmp_value = [tmp_ptr] - expected;
+ // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
+ // result = tmp_value != 0;
+
+ MipsLabel loop_head, exit_loop;
+ __ Addu(TMP, base, offset_lo);
+ __ Sync(0);
+ __ Bind(&loop_head);
+ if ((type == Primitive::kPrimInt) || (type == Primitive::kPrimNot)) {
+ if (isR6) {
+ __ LlR6(out, TMP);
+ } else {
+ __ LlR2(out, TMP);
+ }
+ } else {
+ LOG(FATAL) << "Unsupported op size " << type;
+ UNREACHABLE();
+ }
+ __ Subu(out, out, expected); // If we didn't get the 'expected'
+ __ Sltiu(out, out, 1); // value, set 'out' to false, and
+ __ Beqz(out, &exit_loop); // return.
+ __ Move(out, value); // Use 'out' for the 'store conditional' instruction.
+ // If we use 'value' directly, we would lose 'value'
+ // in the case that the store fails. Whether the
+ // store succeeds, or fails, it will load the
+ // correct boolean value into the 'out' register.
+ // This test isn't really necessary. We only support Primitive::kPrimInt,
+ // Primitive::kPrimNot, and we already verified that we're working on one
+ // of those two types. It's left here in case the code needs to support
+ // other types in the future.
+ if ((type == Primitive::kPrimInt) || (type == Primitive::kPrimNot)) {
+ if (isR6) {
+ __ ScR6(out, TMP);
+ } else {
+ __ ScR2(out, TMP);
+ }
+ }
+ __ Beqz(out, &loop_head); // If we couldn't do the read-modify-write
+ // cycle atomically then retry.
+ __ Bind(&exit_loop);
+ __ Sync(0);
+}
+
+// boolean sun.misc.Unsafe.compareAndSwapInt(Object o, long offset, int expected, int x)
+void IntrinsicLocationsBuilderMIPS::VisitUnsafeCASInt(HInvoke* invoke) {
+ CreateIntIntIntIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitUnsafeCASInt(HInvoke* invoke) {
+ GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
+}
+
+// boolean sun.misc.Unsafe.compareAndSwapObject(Object o, long offset, Object expected, Object x)
+void IntrinsicLocationsBuilderMIPS::VisitUnsafeCASObject(HInvoke* invoke) {
+ CreateIntIntIntIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitUnsafeCASObject(HInvoke* invoke) {
+ GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
+}
+
// char java.lang.String.charAt(int index)
void IntrinsicLocationsBuilderMIPS::VisitStringCharAt(HInvoke* invoke) {
LocationSummary* locations = new (arena_) LocationSummary(invoke,
@@ -2347,9 +2440,7 @@
UNIMPLEMENTED_INTRINSIC(MIPS, MathRint)
UNIMPLEMENTED_INTRINSIC(MIPS, MathRoundDouble)
UNIMPLEMENTED_INTRINSIC(MIPS, MathRoundFloat)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASInt)
UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASLong)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASObject)
UNIMPLEMENTED_INTRINSIC(MIPS, ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(MIPS, StringGetCharsNoCheck)
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index c83340b..e08e8fb 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -162,7 +162,7 @@
// (6) Compute the dominance information and the reverse post order.
ComputeDominanceInformation();
- // (7) Analyze loops discover through back edge analysis, and
+ // (7) Analyze loops discovered through back edge analysis, and
// set the loop information on each block.
GraphAnalysisResult result = AnalyzeLoops();
if (result != kAnalysisSuccess) {
@@ -247,7 +247,7 @@
}
// Populate `dominated_blocks_` information after computing all dominators.
- // The potential presence of irreducible loops require to do it after.
+ // The potential presence of irreducible loops requires to do it after.
for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
if (!block->IsEntryBlock()) {
@@ -460,7 +460,7 @@
if (block->IsLoopHeader()) {
SimplifyLoop(block);
} else if (!block->IsEntryBlock() && block->GetFirstInstruction()->IsSuspendCheck()) {
- // We are being called by the dead code elimiation pass, and what used to be
+ // We are being called by the dead code elimination pass, and what used to be
// a loop got dismantled. Just remove the suspend check.
block->RemoveInstruction(block->GetFirstInstruction());
}
@@ -2373,7 +2373,7 @@
env_uses_.Clear();
}
-// Returns an instruction with the opposite boolean value from 'cond'.
+// Returns an instruction with the opposite Boolean value from 'cond'.
HInstruction* HGraph::InsertOppositeCondition(HInstruction* cond, HInstruction* cursor) {
ArenaAllocator* allocator = GetArena();
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 53da069..521bd14 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -4148,7 +4148,9 @@
class HNeg : public HUnaryOperation {
public:
HNeg(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc)
- : HUnaryOperation(result_type, input, dex_pc) {}
+ : HUnaryOperation(result_type, input, dex_pc) {
+ DCHECK_EQ(result_type, Primitive::PrimitiveKind(input->GetType()));
+ }
template <typename T> T Compute(T x) const { return -x; }
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 4d2469c..f1c5581 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -84,6 +84,8 @@
namespace art {
+static constexpr size_t kArenaAllocatorMemoryReportThreshold = 8 * MB;
+
/**
* Used by the code generator, to allocate the code in a vector.
*/
@@ -761,13 +763,6 @@
pass_observer.DumpDisassembly();
}
- if (kArenaAllocatorCountAllocations) {
- if (arena->BytesAllocated() > 4 * MB) {
- MemStats mem_stats(arena->GetMemStats());
- LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(mem_stats);
- }
- }
-
return codegen.release();
}
@@ -812,6 +807,13 @@
if (codegen.get() != nullptr) {
MaybeRecordStat(MethodCompilationStat::kCompiled);
method = Emit(&arena, &code_allocator, codegen.get(), compiler_driver, code_item);
+
+ if (kArenaAllocatorCountAllocations) {
+ if (arena.BytesAllocated() > kArenaAllocatorMemoryReportThreshold) {
+ MemStats mem_stats(arena.GetMemStats());
+ LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(mem_stats);
+ }
+ }
}
} else {
if (compiler_driver->GetCompilerOptions().VerifyAtRuntime()) {
@@ -890,6 +892,13 @@
if (codegen.get() == nullptr) {
return false;
}
+
+ if (kArenaAllocatorCountAllocations) {
+ if (arena.BytesAllocated() > kArenaAllocatorMemoryReportThreshold) {
+ MemStats mem_stats(arena.GetMemStats());
+ LOG(INFO) << PrettyMethod(method_idx, *dex_file) << " " << Dumpable<MemStats>(mem_stats);
+ }
+ }
}
size_t stack_map_size = codegen->ComputeStackMapsSize();
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 0ad104e..fc72727 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -47,6 +47,19 @@
bound_type->GetBlock()->RemoveInstruction(bound_type);
}
+void PrepareForRegisterAllocation::VisitArraySet(HArraySet* instruction) {
+ HInstruction* value = instruction->GetValue();
+ // PrepareForRegisterAllocation::VisitBoundType may have replaced a
+ // BoundType (as value input of this ArraySet) with a NullConstant.
+ // If so, this ArraySet no longer needs a type check.
+ if (value->IsNullConstant()) {
+ DCHECK_EQ(value->GetType(), Primitive::kPrimNot);
+ if (instruction->NeedsTypeCheck()) {
+ instruction->ClearNeedsTypeCheck();
+ }
+ }
+}
+
void PrepareForRegisterAllocation::VisitClinitCheck(HClinitCheck* check) {
// Try to find a static invoke or a new-instance from which this check originated.
HInstruction* implicit_clinit = nullptr;
diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h
index c90724c..a679148 100644
--- a/compiler/optimizing/prepare_for_register_allocation.h
+++ b/compiler/optimizing/prepare_for_register_allocation.h
@@ -40,6 +40,7 @@
void VisitDivZeroCheck(HDivZeroCheck* check) OVERRIDE;
void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE;
void VisitBoundType(HBoundType* bound_type) OVERRIDE;
+ void VisitArraySet(HArraySet* instruction) OVERRIDE;
void VisitClinitCheck(HClinitCheck* check) OVERRIDE;
void VisitCondition(HCondition* condition) OVERRIDE;
void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE;
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index 34d9af1..44bede8 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -1927,7 +1927,13 @@
BitVector* live = liveness_.GetLiveInSet(*block);
for (uint32_t idx : live->Indexes()) {
LiveInterval* interval = liveness_.GetInstructionFromSsaIndex(idx)->GetLiveInterval();
- DCHECK(!interval->GetSiblingAt(block->GetLifetimeStart())->HasRegister());
+ LiveInterval* sibling = interval->GetSiblingAt(block->GetLifetimeStart());
+ // `GetSiblingAt` returns the sibling that contains a position, but there could be
+ // a lifetime hole in it. `CoversSlow` returns whether the interval is live at that
+ // position.
+ if (sibling->CoversSlow(block->GetLifetimeStart())) {
+ DCHECK(!sibling->HasRegister());
+ }
}
}
} else {
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 7c41813..a1798c0 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -485,6 +485,28 @@
EmitI(0x2e, rs, rt, imm16);
}
+void MipsAssembler::LlR2(Register rt, Register base, int16_t imm16) {
+ CHECK(!IsR6());
+ EmitI(0x30, base, rt, imm16);
+}
+
+void MipsAssembler::ScR2(Register rt, Register base, int16_t imm16) {
+ CHECK(!IsR6());
+ EmitI(0x38, base, rt, imm16);
+}
+
+void MipsAssembler::LlR6(Register rt, Register base, int16_t imm9) {
+ CHECK(IsR6());
+ CHECK(IsInt<9>(imm9));
+ EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x36);
+}
+
+void MipsAssembler::ScR6(Register rt, Register base, int16_t imm9) {
+ CHECK(IsR6());
+ CHECK(IsInt<9>(imm9));
+ EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x26);
+}
+
void MipsAssembler::Slt(Register rd, Register rs, Register rt) {
EmitR(0, rs, rt, rd, 0, 0x2a);
}
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index a7179fd..ffac4c4 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -191,6 +191,11 @@
void Swl(Register rt, Register rs, uint16_t imm16);
void Swr(Register rt, Register rs, uint16_t imm16);
+ void LlR2(Register rt, Register base, int16_t imm16 = 0);
+ void ScR2(Register rt, Register base, int16_t imm16 = 0);
+ void LlR6(Register rt, Register base, int16_t imm9 = 0);
+ void ScR6(Register rt, Register base, int16_t imm9 = 0);
+
void Slt(Register rd, Register rs, Register rt);
void Sltu(Register rd, Register rs, Register rt);
void Slti(Register rt, Register rs, uint16_t imm16);
diff --git a/compiler/utils/mips/assembler_mips_test.cc b/compiler/utils/mips/assembler_mips_test.cc
index 9e27f07..cec43ba 100644
--- a/compiler/utils/mips/assembler_mips_test.cc
+++ b/compiler/utils/mips/assembler_mips_test.cc
@@ -489,6 +489,14 @@
DriverStr(RepeatRRIb(&mips::MipsAssembler::Swr, -16, "swr ${reg1}, {imm}(${reg2})"), "Swr");
}
+TEST_F(AssemblerMIPSTest, LlR2) {
+ DriverStr(RepeatRRIb(&mips::MipsAssembler::LlR2, -16, "ll ${reg1}, {imm}(${reg2})"), "LlR2");
+}
+
+TEST_F(AssemblerMIPSTest, ScR2) {
+ DriverStr(RepeatRRIb(&mips::MipsAssembler::ScR2, -16, "sc ${reg1}, {imm}(${reg2})"), "ScR2");
+}
+
TEST_F(AssemblerMIPSTest, Slt) {
DriverStr(RepeatRRR(&mips::MipsAssembler::Slt, "slt ${reg1}, ${reg2}, ${reg3}"), "Slt");
}
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index 428266f..a95ea64 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -317,6 +317,7 @@
{ kITypeMask, 42u << kOpcodeShift, "swl", "TO", },
{ kITypeMask, 43u << kOpcodeShift, "sw", "TO", },
{ kITypeMask, 46u << kOpcodeShift, "swr", "TO", },
+ { kITypeMask, 48u << kOpcodeShift, "ll", "TO", },
{ kITypeMask, 49u << kOpcodeShift, "lwc1", "tO", },
{ kJTypeMask, 50u << kOpcodeShift, "bc", "P" },
{ kITypeMask, 53u << kOpcodeShift, "ldc1", "tO", },
@@ -327,6 +328,7 @@
{ kITypeMask | (1 << 24), (54u << kOpcodeShift) | (1 << 24), "beqzc", "Sb" },
{ kITypeMask | (1 << 25), (54u << kOpcodeShift) | (1 << 25), "beqzc", "Sb" },
{ kITypeMask, 55u << kOpcodeShift, "ld", "TO", },
+ { kITypeMask, 56u << kOpcodeShift, "sc", "TO", },
{ kITypeMask, 57u << kOpcodeShift, "swc1", "tO", },
{ kITypeMask | (0x1f << 16), (59u << kOpcodeShift) | (30 << 16), "auipc", "Si" },
{ kITypeMask | (0x3 << 19), (59u << kOpcodeShift) | (0 << 19), "addiupc", "Sp" },
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 9ac18e8..48a9d91 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1997,9 +1997,13 @@
image_header_.GetPointerSize())) {
indent_os << StringPrintf("OAT CODE: %p\n", quick_oat_code_begin);
}
- } else if (method->IsAbstract() || method->IsCalleeSaveMethod() ||
- method->IsResolutionMethod() || method->IsImtConflictMethod() ||
- method->IsImtUnimplementedMethod() || method->IsClassInitializer()) {
+ } else if (method->IsAbstract() ||
+ method->IsCalleeSaveMethod() ||
+ method->IsResolutionMethod() ||
+ (method == Runtime::Current()->GetImtConflictMethod()) ||
+ method->IsImtUnimplementedMethod() ||
+ method->IsClassInitializer()) {
+ // Don't print information for these.
} else {
const DexFile::CodeItem* code_item = method->GetCodeItem();
size_t dex_instruction_bytes = code_item->insns_size_in_code_units_ * 2;
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 82ec0b7..5a901f1 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -1225,11 +1225,35 @@
END art_quick_proxy_invoke_handler
/*
- * Called to resolve an imt conflict. r12 is a hidden argument that holds the target method's
- * dex method index.
+ * Called to resolve an imt conflict.
+ * r0 is the conflict ArtMethod.
+ * r12 is a hidden argument that holds the target interface method's dex method index.
+ *
+ * Note that this stub writes to r0, r4, and r12.
*/
ENTRY art_quick_imt_conflict_trampoline
- mov r0, r12
+ ldr r4, [sp, #0] // Load referrer
+ ldr r4, [r4, #ART_METHOD_DEX_CACHE_METHODS_OFFSET_32] // Load dex cache methods array
+ ldr r12, [r4, r12, lsl #POINTER_SIZE_SHIFT] // Load interface method
+ ldr r0, [r0, #ART_METHOD_JNI_OFFSET_32] // Load ImtConflictTable
+ ldr r4, [r0] // Load first entry in ImtConflictTable.
+.Limt_table_iterate:
+ cmp r4, r12
+ // Branch if found. Benchmarks have shown doing a branch here is better.
+ beq .Limt_table_found
+ // If the entry is null, the interface method is not in the ImtConflictTable.
+ cbz r4, .Lconflict_trampoline
+ // Iterate over the entries of the ImtConflictTable.
+ ldr r4, [r0, #(2 * __SIZEOF_POINTER__)]!
+ b .Limt_table_iterate
+.Limt_table_found:
+ // We successuflly hit an entry in the table. Load the target method
+ // and jump to it.
+ ldr r0, [r0, #__SIZEOF_POINTER__]
+ ldr pc, [r0, #ART_METHOD_QUICK_CODE_OFFSET_32]
+.Lconflict_trampoline:
+ // Call the runtime stub to populate the ImtConflictTable and jump to the
+ // resolved method.
INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
END art_quick_imt_conflict_trampoline
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 65d5b46..8b497fe 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1757,12 +1757,37 @@
END art_quick_proxy_invoke_handler
/*
- * Called to resolve an imt conflict. xIP1 is a hidden argument that holds the target method's
- * dex method index.
+ * Called to resolve an imt conflict.
+ * x0 is the conflict ArtMethod.
+ * xIP1 is a hidden argument that holds the target interface method's dex method index.
+ *
+ * Note that this stub writes to xIP0, xIP1, and x0.
*/
.extern artInvokeInterfaceTrampoline
ENTRY art_quick_imt_conflict_trampoline
- mov x0, xIP1
+ ldr xIP0, [sp, #0] // Load referrer
+ ldr xIP0, [xIP0, #ART_METHOD_DEX_CACHE_METHODS_OFFSET_64] // Load dex cache methods array
+ ldr xIP0, [xIP0, xIP1, lsl #POINTER_SIZE_SHIFT] // Load interface method
+ ldr xIP1, [x0, #ART_METHOD_JNI_OFFSET_64] // Load ImtConflictTable
+ ldr x0, [xIP1] // Load first entry in ImtConflictTable.
+.Limt_table_iterate:
+ cmp x0, xIP0
+ // Branch if found. Benchmarks have shown doing a branch here is better.
+ beq .Limt_table_found
+ // If the entry is null, the interface method is not in the ImtConflictTable.
+ cbz x0, .Lconflict_trampoline
+ // Iterate over the entries of the ImtConflictTable.
+ ldr x0, [xIP1, #(2 * __SIZEOF_POINTER__)]!
+ b .Limt_table_iterate
+.Limt_table_found:
+ // We successuflly hit an entry in the table. Load the target method
+ // and jump to it.
+ ldr x0, [xIP1, #__SIZEOF_POINTER__]
+ ldr xIP0, [x0, #ART_METHOD_QUICK_CODE_OFFSET_64]
+ br xIP0
+.Lconflict_trampoline:
+ // Call the runtime stub to populate the ImtConflictTable and jump to the
+ // resolved method.
INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
END art_quick_imt_conflict_trampoline
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 4236c28..1c4b470 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -1999,7 +1999,9 @@
// 1. imt_conflict
// Contains.
-
+ // TODO(ngeoffray): Re-enable this test. They are now broken with the ImtConflictTable.
+ // b/27794971
+/*
size_t result =
Invoke3WithReferrerAndHidden(0U, reinterpret_cast<size_t>(array_list.Get()),
reinterpret_cast<size_t>(obj.Get()),
@@ -2025,18 +2027,18 @@
ASSERT_FALSE(self->IsExceptionPending());
EXPECT_EQ(static_cast<size_t>(JNI_TRUE), result);
-
+*/
// 2. regular interface trampoline
- result = Invoke3WithReferrer(static_cast<size_t>(inf_contains->GetDexMethodIndex()),
- reinterpret_cast<size_t>(array_list.Get()),
- reinterpret_cast<size_t>(obj.Get()),
- StubTest::GetEntrypoint(self,
- kQuickInvokeInterfaceTrampolineWithAccessCheck),
- self, contains_amethod);
+ size_t result = Invoke3WithReferrer(static_cast<size_t>(inf_contains->GetDexMethodIndex()),
+ reinterpret_cast<size_t>(array_list.Get()),
+ reinterpret_cast<size_t>(obj.Get()),
+ StubTest::GetEntrypoint(self,
+ kQuickInvokeInterfaceTrampolineWithAccessCheck),
+ self, contains_amethod);
ASSERT_FALSE(self->IsExceptionPending());
- EXPECT_EQ(static_cast<size_t>(JNI_TRUE), result);
+ EXPECT_EQ(static_cast<size_t>(JNI_FALSE), result);
result = Invoke3WithReferrer(
static_cast<size_t>(inf_contains->GetDexMethodIndex()),
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 125570d..82ac574 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1402,11 +1402,42 @@
END_FUNCTION art_quick_proxy_invoke_handler
/*
- * Called to resolve an imt conflict. xmm7 is a hidden argument that holds the target method's
- * dex method index.
+ * Called to resolve an imt conflict.
+ * eax is the conflict ArtMethod.
+ * xmm7 is a hidden argument that holds the target interface method's dex method index.
+ *
+ * Note that this stub writes to eax.
+ * Because of lack of free registers, it also saves and restores edi.
*/
DEFINE_FUNCTION art_quick_imt_conflict_trampoline
+ PUSH EDI
+ movl 8(%esp), %edi // Load referrer
+ movl ART_METHOD_DEX_CACHE_METHODS_OFFSET_32(%edi), %edi // Load dex cache methods array
+ pushl ART_METHOD_JNI_OFFSET_32(%eax) // Push ImtConflictTable.
+ CFI_ADJUST_CFA_OFFSET(4)
movd %xmm7, %eax // get target method index stored in xmm7
+ movl 0(%edi, %eax, __SIZEOF_POINTER__), %edi // Load interface method
+ popl %eax // Pop ImtConflictTable.
+ CFI_ADJUST_CFA_OFFSET(-4)
+.Limt_table_iterate:
+ cmpl %edi, 0(%eax)
+ jne .Limt_table_next_entry
+ // We successuflly hit an entry in the table. Load the target method
+ // and jump to it.
+ POP EDI
+ movl __SIZEOF_POINTER__(%eax), %eax
+ jmp *ART_METHOD_QUICK_CODE_OFFSET_32(%eax)
+.Limt_table_next_entry:
+ // If the entry is null, the interface method is not in the ImtConflictTable.
+ cmpl LITERAL(0), 0(%eax)
+ jz .Lconflict_trampoline
+ // Iterate over the entries of the ImtConflictTable.
+ addl LITERAL(2 * __SIZEOF_POINTER__), %eax
+ jmp .Limt_table_iterate
+.Lconflict_trampoline:
+ // Call the runtime stub to populate the ImtConflictTable and jump to the
+ // resolved method.
+ POP EDI
INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
END_FUNCTION art_quick_imt_conflict_trampoline
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index dee8d3c..90049cc 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1314,14 +1314,37 @@
/*
* Called to resolve an imt conflict.
- * rax is a hidden argument that holds the target method's dex method index.
+ * rdi is the conflict ArtMethod.
+ * rax is a hidden argument that holds the target interface method's dex method index.
+ *
+ * Note that this stub writes to r10 and rdi.
*/
DEFINE_FUNCTION art_quick_imt_conflict_trampoline
#if defined(__APPLE__)
int3
int3
#else
- movq %rax, %rdi
+ movq __SIZEOF_POINTER__(%rsp), %r10 // Load referrer
+ movq ART_METHOD_DEX_CACHE_METHODS_OFFSET_64(%r10), %r10 // Load dex cache methods array
+ movq 0(%r10, %rax, __SIZEOF_POINTER__), %r10 // Load interface method
+ movq ART_METHOD_JNI_OFFSET_64(%rdi), %rdi // Load ImtConflictTable
+.Limt_table_iterate:
+ cmpq %r10, 0(%rdi)
+ jne .Limt_table_next_entry
+ // We successuflly hit an entry in the table. Load the target method
+ // and jump to it.
+ movq __SIZEOF_POINTER__(%rdi), %rdi
+ jmp *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi)
+.Limt_table_next_entry:
+ // If the entry is null, the interface method is not in the ImtConflictTable.
+ cmpq LITERAL(0), 0(%rdi)
+ jz .Lconflict_trampoline
+ // Iterate over the entries of the ImtConflictTable.
+ addq LITERAL(2 * __SIZEOF_POINTER__), %rdi
+ jmp .Limt_table_iterate
+.Lconflict_trampoline:
+ // Call the runtime stub to populate the ImtConflictTable and jump to the
+ // resolved method.
INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
#endif // __APPLE__
END_FUNCTION art_quick_imt_conflict_trampoline
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 8541210..6449efa 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -264,13 +264,6 @@
return result;
}
-inline bool ArtMethod::IsImtConflictMethod() {
- bool result = this == Runtime::Current()->GetImtConflictMethod();
- // Check that if we do think it is phony it looks like the imt conflict method.
- DCHECK(!result || IsRuntimeMethod());
- return result;
-}
-
inline bool ArtMethod::IsImtUnimplementedMethod() {
bool result = this == Runtime::Current()->GetImtUnimplementedMethod();
// Check that if we do think it is phony it looks like the imt unimplemented method.
@@ -463,7 +456,10 @@
interface_method->VisitRoots(visitor, pointer_size);
}
visitor.VisitRoot(declaring_class_.AddressWithoutBarrier());
- if (!IsNative()) {
+ // Runtime methods and native methods use the same field as the profiling info for
+ // storing their own data (jni entrypoint for native methods, and ImtConflictTable for
+ // some runtime methods).
+ if (!IsNative() && !IsRuntimeMethod()) {
ProfilingInfo* profiling_info = GetProfilingInfo(pointer_size);
if (profiling_info != nullptr) {
profiling_info->VisitRoots(visitor);
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 5ca362c..3dbcd58 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -44,6 +44,76 @@
class PointerArray;
} // namespace mirror
+// Table to resolve IMT conflicts at runtime. The table is attached to
+// the jni entrypoint of IMT conflict ArtMethods.
+// The table contains a list of pairs of { interface_method, implementation_method }
+// with the last entry being null to make an assembly implementation of a lookup
+// faster.
+class ImtConflictTable {
+ public:
+ // Build a new table copying `other` and adding the new entry formed of
+ // the pair { `interface_method`, `implementation_method` }
+ ImtConflictTable(ImtConflictTable* other,
+ ArtMethod* interface_method,
+ ArtMethod* implementation_method) {
+ size_t index = 0;
+ while (other->entries_[index].interface_method != nullptr) {
+ entries_[index] = other->entries_[index];
+ index++;
+ }
+ entries_[index].interface_method = interface_method;
+ entries_[index].implementation_method = implementation_method;
+ // Add the null marker.
+ entries_[index + 1].interface_method = nullptr;
+ entries_[index + 1].implementation_method = nullptr;
+ }
+
+ // Lookup the implementation ArtMethod associated to `interface_method`. Return null
+ // if not found.
+ ArtMethod* Lookup(ArtMethod* interface_method) const {
+ uint32_t table_index = 0;
+ ArtMethod* current_interface_method;
+ while ((current_interface_method = entries_[table_index].interface_method) != nullptr) {
+ if (current_interface_method == interface_method) {
+ return entries_[table_index].implementation_method;
+ }
+ table_index++;
+ }
+ return nullptr;
+ }
+
+ // Compute the size in bytes taken by this table.
+ size_t ComputeSize() const {
+ uint32_t table_index = 0;
+ size_t total_size = 0;
+ while ((entries_[table_index].interface_method) != nullptr) {
+ total_size += sizeof(Entry);
+ table_index++;
+ }
+ // Add the end marker.
+ return total_size + sizeof(Entry);
+ }
+
+ // Compute the size in bytes needed for copying the given `table` and add
+ // one more entry.
+ static size_t ComputeSizeWithOneMoreEntry(ImtConflictTable* table) {
+ return table->ComputeSize() + sizeof(Entry);
+ }
+
+ struct Entry {
+ ArtMethod* interface_method;
+ ArtMethod* implementation_method;
+ };
+
+ private:
+ // Array of entries that the assembly stubs will iterate over. Note that this is
+ // not fixed size, and we allocate data prior to calling the constructor
+ // of ImtConflictTable.
+ Entry entries_[0];
+
+ DISALLOW_COPY_AND_ASSIGN(ImtConflictTable);
+};
+
class ArtMethod FINAL {
public:
ArtMethod() : access_flags_(0), dex_code_item_offset_(0), dex_method_index_(0),
@@ -338,6 +408,15 @@
return reinterpret_cast<ProfilingInfo*>(GetEntryPointFromJniPtrSize(pointer_size));
}
+ ImtConflictTable* GetImtConflictTable(size_t pointer_size) {
+ DCHECK(IsRuntimeMethod());
+ return reinterpret_cast<ImtConflictTable*>(GetEntryPointFromJniPtrSize(pointer_size));
+ }
+
+ ALWAYS_INLINE void SetImtConflictTable(ImtConflictTable* table) {
+ SetEntryPointFromJniPtrSize(table, sizeof(void*));
+ }
+
ALWAYS_INLINE void SetProfilingInfo(ProfilingInfo* info) {
SetEntryPointFromJniPtrSize(info, sizeof(void*));
}
@@ -376,8 +455,6 @@
bool IsResolutionMethod() SHARED_REQUIRES(Locks::mutator_lock_);
- bool IsImtConflictMethod() SHARED_REQUIRES(Locks::mutator_lock_);
-
bool IsImtUnimplementedMethod() SHARED_REQUIRES(Locks::mutator_lock_);
MethodReference ToMethodReference() SHARED_REQUIRES(Locks::mutator_lock_) {
@@ -537,7 +614,7 @@
GcRoot<mirror::Class>* dex_cache_resolved_types_;
// Pointer to JNI function registered to this method, or a function to resolve the JNI function,
- // or the profiling data for non-native methods.
+ // or the profiling data for non-native methods, or an ImtConflictTable.
void* entry_point_from_jni_;
// Method dispatch from quick compiled code invokes this pointer which may cause bridging into
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 1f24f45..942f9de 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -299,6 +299,14 @@
ADD_TEST_EQ(ART_METHOD_DEX_CACHE_TYPES_OFFSET_64,
art::ArtMethod::DexCacheResolvedTypesOffset(8).Int32Value())
+#define ART_METHOD_JNI_OFFSET_32 28
+ADD_TEST_EQ(ART_METHOD_JNI_OFFSET_32,
+ art::ArtMethod::EntryPointFromJniOffset(4).Int32Value())
+
+#define ART_METHOD_JNI_OFFSET_64 40
+ADD_TEST_EQ(ART_METHOD_JNI_OFFSET_64,
+ art::ArtMethod::EntryPointFromJniOffset(8).Int32Value())
+
#define ART_METHOD_QUICK_CODE_OFFSET_32 32
ADD_TEST_EQ(ART_METHOD_QUICK_CODE_OFFSET_32,
art::ArtMethod::EntryPointFromQuickCompiledCodeOffset(4).Int32Value())
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index 7539943..70ff60f 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -33,27 +33,9 @@
template <bool kCount>
const char* const ArenaAllocatorStatsImpl<kCount>::kAllocNames[] = {
"Misc ",
- "BBList ",
- "BBPreds ",
- "DfsPreOrd ",
- "DfsPostOrd ",
- "DomPostOrd ",
- "TopoOrd ",
- "Lowering ",
- "LIR ",
- "LIR masks ",
"SwitchTbl ",
- "FillArray ",
"SlowPaths ",
- "MIR ",
- "DataFlow ",
- "GrowList ",
"GrowBitMap ",
- "SSA2Dalvik ",
- "Dalvik2SSA ",
- "DebugInfo ",
- "RegAlloc ",
- "Data ",
"STL ",
"GraphBuilder ",
"Graph ",
@@ -80,7 +62,6 @@
"MoveOperands ",
"CodeBuffer ",
"StackMaps ",
- "BaselineMaps ",
"Optimization ",
"GVN ",
"InductionVar ",
@@ -91,7 +72,6 @@
"SsaLiveness ",
"SsaPhiElim ",
"RefTypeProp ",
- "PrimTypeProp ",
"SideEffects ",
"RegAllocator ",
"RegAllocVldt ",
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index f8f7396..697f7e0 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -44,27 +44,9 @@
// Type of allocation for memory tuning.
enum ArenaAllocKind {
kArenaAllocMisc,
- kArenaAllocBBList,
- kArenaAllocBBPredecessors,
- kArenaAllocDfsPreOrder,
- kArenaAllocDfsPostOrder,
- kArenaAllocDomPostOrder,
- kArenaAllocTopologicalSortOrder,
- kArenaAllocLoweringInfo,
- kArenaAllocLIR,
- kArenaAllocLIRResourceMask,
kArenaAllocSwitchTable,
- kArenaAllocFillArrayData,
kArenaAllocSlowPaths,
- kArenaAllocMIR,
- kArenaAllocDFInfo,
- kArenaAllocGrowableArray,
kArenaAllocGrowableBitMap,
- kArenaAllocSSAToDalvikMap,
- kArenaAllocDalvikToSSAMap,
- kArenaAllocDebugInfo,
- kArenaAllocRegAlloc,
- kArenaAllocData,
kArenaAllocSTL,
kArenaAllocGraphBuilder,
kArenaAllocGraph,
@@ -91,7 +73,6 @@
kArenaAllocMoveOperands,
kArenaAllocCodeBuffer,
kArenaAllocStackMaps,
- kArenaAllocBaselineMaps,
kArenaAllocOptimization,
kArenaAllocGvn,
kArenaAllocInductionVarAnalysis,
@@ -102,7 +83,6 @@
kArenaAllocSsaLiveness,
kArenaAllocSsaPhiElimination,
kArenaAllocReferenceTypePropagation,
- kArenaAllocPrimitiveTypePropagation,
kArenaAllocSideEffectsAnalysis,
kArenaAllocRegisterAllocator,
kArenaAllocRegisterAllocatorValidate,
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index c1115da..99e38d9 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -498,10 +498,11 @@
object_array_string->SetComponentType(java_lang_String.Get());
SetClassRoot(kJavaLangStringArrayClass, object_array_string.Get());
+ LinearAlloc* linear_alloc = runtime->GetLinearAlloc();
// Create runtime resolution and imt conflict methods.
runtime->SetResolutionMethod(runtime->CreateResolutionMethod());
- runtime->SetImtConflictMethod(runtime->CreateImtConflictMethod());
- runtime->SetImtUnimplementedMethod(runtime->CreateImtConflictMethod());
+ runtime->SetImtConflictMethod(runtime->CreateImtConflictMethod(linear_alloc));
+ runtime->SetImtUnimplementedMethod(runtime->CreateImtConflictMethod(linear_alloc));
// Setup boot_class_path_ and register class_path now that we can use AllocObjectArray to create
// DexCache instances. Needs to be after String, Field, Method arrays since AllocDexCache uses
@@ -5904,9 +5905,11 @@
// Place method in imt if entry is empty, place conflict otherwise.
if (*imt_ref == unimplemented_method) {
*imt_ref = current_method;
- } else if (*imt_ref != imt_conflict_method) {
+ } else if (!(*imt_ref)->IsRuntimeMethod()) {
// If we are not a conflict and we have the same signature and name as the imt
// entry, it must be that we overwrote a superclass vtable entry.
+ // Note that we have checked IsRuntimeMethod, as there may be multiple different
+ // conflict methods.
MethodNameAndSignatureComparator imt_comparator(
(*imt_ref)->GetInterfaceMethodIfProxy(image_pointer_size));
if (imt_comparator.HasSameNameAndSignature(
@@ -5915,6 +5918,11 @@
} else {
*imt_ref = imt_conflict_method;
}
+ } else {
+ // Place the default conflict method. Note that there may be an existing conflict
+ // method in the IMT, but it could be one tailored to the super class, with a
+ // specific ImtConflictTable.
+ *imt_ref = imt_conflict_method;
}
}
@@ -7654,12 +7662,12 @@
return soa.Env()->NewGlobalRef(local_ref.get());
}
-ArtMethod* ClassLinker::CreateRuntimeMethod() {
+ArtMethod* ClassLinker::CreateRuntimeMethod(LinearAlloc* linear_alloc) {
const size_t method_alignment = ArtMethod::Alignment(image_pointer_size_);
const size_t method_size = ArtMethod::Size(image_pointer_size_);
LengthPrefixedArray<ArtMethod>* method_array = AllocArtMethodArray(
Thread::Current(),
- Runtime::Current()->GetLinearAlloc(),
+ linear_alloc,
1);
ArtMethod* method = &method_array->At(0, method_size, method_alignment);
CHECK(method != nullptr);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index c368a3a..b4fbe1c 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -565,7 +565,7 @@
REQUIRES(Locks::classlinker_classes_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
- ArtMethod* CreateRuntimeMethod();
+ ArtMethod* CreateRuntimeMethod(LinearAlloc* linear_alloc);
// Clear the ArrayClass cache. This is necessary when cleaning up for the image, as the cache
// entries are roots, but potentially not image classes.
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 3e6b453..5344cdd 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -548,7 +548,7 @@
uint32_t imt_index = resolved_method->GetDexMethodIndex() % mirror::Class::kImtSize;
ArtMethod* imt_method = (*this_object)->GetClass()->GetEmbeddedImTableEntry(
imt_index, class_linker->GetImagePointerSize());
- if (!imt_method->IsImtConflictMethod() && !imt_method->IsImtUnimplementedMethod()) {
+ if (!imt_method->IsRuntimeMethod()) {
if (kIsDebugBuild) {
mirror::Class* klass = (*this_object)->GetClass();
ArtMethod* method = klass->FindVirtualMethodForInterface(
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 7005aa5..35f2102 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -23,6 +23,7 @@
#include "entrypoints/runtime_asm_entrypoints.h"
#include "gc/accounting/card_table-inl.h"
#include "interpreter/interpreter.h"
+#include "linear_alloc.h"
#include "method_reference.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache-inl.h"
@@ -2118,48 +2119,73 @@
return artInvokeCommon<kVirtual, true>(method_idx, this_object, self, sp);
}
-// Determine target of interface dispatch. This object is known non-null.
-extern "C" TwoWordReturn artInvokeInterfaceTrampoline(uint32_t dex_method_idx,
+// Determine target of interface dispatch. This object is known non-null. First argument
+// is there for consistency but should not be used, as some architectures overwrite it
+// in the assembly trampoline.
+extern "C" TwoWordReturn artInvokeInterfaceTrampoline(uint32_t deadbeef ATTRIBUTE_UNUSED,
mirror::Object* this_object,
- Thread* self, ArtMethod** sp)
+ Thread* self,
+ ArtMethod** sp)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> cls(hs.NewHandle(this_object->GetClass()));
+
// The optimizing compiler currently does not inline methods that have an interface
// invocation. We use the outer method directly to avoid fetching a stack map, which is
// more expensive.
ArtMethod* caller_method = QuickArgumentVisitor::GetOuterMethod(sp);
DCHECK_EQ(caller_method, QuickArgumentVisitor::GetCallingMethod(sp));
+
+ // Fetch the dex_method_idx of the target interface method from the caller.
+ uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
+
+ const DexFile::CodeItem* code_item = caller_method->GetCodeItem();
+ CHECK_LT(dex_pc, code_item->insns_size_in_code_units_);
+ const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]);
+ Instruction::Code instr_code = instr->Opcode();
+ CHECK(instr_code == Instruction::INVOKE_INTERFACE ||
+ instr_code == Instruction::INVOKE_INTERFACE_RANGE)
+ << "Unexpected call into interface trampoline: " << instr->DumpString(nullptr);
+ uint32_t dex_method_idx;
+ if (instr_code == Instruction::INVOKE_INTERFACE) {
+ dex_method_idx = instr->VRegB_35c();
+ } else {
+ CHECK_EQ(instr_code, Instruction::INVOKE_INTERFACE_RANGE);
+ dex_method_idx = instr->VRegB_3rc();
+ }
+
ArtMethod* interface_method = caller_method->GetDexCacheResolvedMethod(
dex_method_idx, sizeof(void*));
DCHECK(interface_method != nullptr) << dex_method_idx << " " << PrettyMethod(caller_method);
- ArtMethod* method;
+ ArtMethod* method = nullptr;
+
if (LIKELY(interface_method->GetDexMethodIndex() != DexFile::kDexNoIndex)) {
- method = this_object->GetClass()->FindVirtualMethodForInterface(
- interface_method, sizeof(void*));
+ // If the dex cache already resolved the interface method, look whether we have
+ // a match in the ImtConflictTable.
+ uint32_t imt_index = interface_method->GetDexMethodIndex();
+ ArtMethod* conflict_method = cls->GetEmbeddedImTableEntry(
+ imt_index % mirror::Class::kImtSize, sizeof(void*));
+ DCHECK(conflict_method->IsRuntimeMethod()) << PrettyMethod(conflict_method);
+ ImtConflictTable* current_table = conflict_method->GetImtConflictTable(sizeof(void*));
+ method = current_table->Lookup(interface_method);
+ if (method != nullptr) {
+ return GetTwoWordSuccessValue(
+ reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCode()),
+ reinterpret_cast<uintptr_t>(method));
+ }
+
+ // No match, use the IfTable.
+ method = cls->FindVirtualMethodForInterface(interface_method, sizeof(void*));
if (UNLIKELY(method == nullptr)) {
ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(
interface_method, this_object, caller_method);
return GetTwoWordFailureValue(); // Failure.
}
} else {
+ // The dex cache did not resolve the method, look it up in the dex file
+ // of the caller,
DCHECK_EQ(interface_method, Runtime::Current()->GetResolutionMethod());
- if (kIsDebugBuild) {
- uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
- const DexFile::CodeItem* code = caller_method->GetCodeItem();
- CHECK_LT(dex_pc, code->insns_size_in_code_units_);
- const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
- Instruction::Code instr_code = instr->Opcode();
- CHECK(instr_code == Instruction::INVOKE_INTERFACE ||
- instr_code == Instruction::INVOKE_INTERFACE_RANGE)
- << "Unexpected call into interface trampoline: " << instr->DumpString(nullptr);
- if (instr_code == Instruction::INVOKE_INTERFACE) {
- CHECK_EQ(dex_method_idx, instr->VRegB_35c());
- } else {
- CHECK_EQ(instr_code, Instruction::INVOKE_INTERFACE_RANGE);
- CHECK_EQ(dex_method_idx, instr->VRegB_3rc());
- }
- }
-
const DexFile* dex_file = caller_method->GetDeclaringClass()->GetDexCache()
->GetDexFile();
uint32_t shorty_len;
@@ -2179,7 +2205,50 @@
CHECK(self->IsExceptionPending());
return GetTwoWordFailureValue(); // Failure.
}
+ interface_method = caller_method->GetDexCacheResolvedMethod(dex_method_idx, sizeof(void*));
+ DCHECK(!interface_method->IsRuntimeMethod());
}
+
+ // We arrive here if we have found an implementation, and it is not in the ImtConflictTable.
+ // We create a new table with the new pair { interface_method, method }.
+ uint32_t imt_index = interface_method->GetDexMethodIndex();
+ ArtMethod* conflict_method = cls->GetEmbeddedImTableEntry(
+ imt_index % mirror::Class::kImtSize, sizeof(void*));
+ ImtConflictTable* current_table = conflict_method->GetImtConflictTable(sizeof(void*));
+ Runtime* runtime = Runtime::Current();
+ LinearAlloc* linear_alloc = (cls->GetClassLoader() == nullptr)
+ ? runtime->GetLinearAlloc()
+ : cls->GetClassLoader()->GetAllocator();
+ bool is_new_entry = (conflict_method == runtime->GetImtConflictMethod());
+
+ // Create a new entry if the existing one is the shared conflict method.
+ ArtMethod* new_conflict_method = is_new_entry
+ ? runtime->CreateImtConflictMethod(linear_alloc)
+ : conflict_method;
+
+ // Allocate a new table. Note that we will leak this table at the next conflict,
+ // but that's a tradeoff compared to making the table fixed size.
+ void* data = linear_alloc->Alloc(
+ self, ImtConflictTable::ComputeSizeWithOneMoreEntry(current_table));
+ CHECK(data != nullptr) << "Out of memory";
+ ImtConflictTable* new_table = new (data) ImtConflictTable(
+ current_table, interface_method, method);
+
+ // Do a fence to ensure threads see the data in the table before it is assigned
+ // to the conlict method.
+ // Note that there is a race in the presence of multiple threads and we may leak
+ // memory from the LinearAlloc, but that's a tradeoff compared to using
+ // atomic operations.
+ QuasiAtomic::ThreadFenceRelease();
+ new_conflict_method->SetImtConflictTable(new_table);
+ if (is_new_entry) {
+ // Update the IMT if we create a new conflict method. No fence needed here, as the
+ // data is consistent.
+ cls->SetEmbeddedImTableEntry(imt_index % mirror::Class::kImtSize,
+ new_conflict_method,
+ sizeof(void*));
+ }
+
const void* code = method->GetEntryPointFromQuickCompiledCode();
// When we return, the caller will branch to this address, so it had better not be 0!
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index c893a0f..7bd85ec 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -2419,8 +2419,6 @@
gc::Heap* heap = Runtime::Current()->GetHeap();
bool is_copy = array_data != elements;
size_t bytes = array->GetLength() * component_size;
- VLOG(heap) << "Release primitive array " << soa.Env() << " array_data " << array_data
- << " elements " << elements;
if (is_copy) {
// Sanity check: If elements is not the same as the java array's data, it better not be a
// heap address. TODO: This might be slow to check, may be worth keeping track of which
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 1d64d6d..941217f 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1596,8 +1596,10 @@
}
}
-ArtMethod* Runtime::CreateImtConflictMethod() {
- auto* method = Runtime::Current()->GetClassLinker()->CreateRuntimeMethod();
+static ImtConflictTable::Entry empty_entry = { nullptr, nullptr };
+
+ArtMethod* Runtime::CreateImtConflictMethod(LinearAlloc* linear_alloc) {
+ auto* method = Runtime::Current()->GetClassLinker()->CreateRuntimeMethod(linear_alloc);
// When compiling, the code pointer will get set later when the image is loaded.
if (IsAotCompiler()) {
size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
@@ -1605,6 +1607,7 @@
} else {
method->SetEntryPointFromQuickCompiledCode(GetQuickImtConflictStub());
}
+ method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
return method;
}
@@ -1612,10 +1615,11 @@
CHECK(method != nullptr);
CHECK(method->IsRuntimeMethod());
imt_conflict_method_ = method;
+ method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
}
ArtMethod* Runtime::CreateResolutionMethod() {
- auto* method = Runtime::Current()->GetClassLinker()->CreateRuntimeMethod();
+ auto* method = GetClassLinker()->CreateRuntimeMethod(GetLinearAlloc());
// When compiling, the code pointer will get set later when the image is loaded.
if (IsAotCompiler()) {
size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
@@ -1627,7 +1631,7 @@
}
ArtMethod* Runtime::CreateCalleeSaveMethod() {
- auto* method = Runtime::Current()->GetClassLinker()->CreateRuntimeMethod();
+ auto* method = GetClassLinker()->CreateRuntimeMethod(GetLinearAlloc());
size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
method->SetEntryPointFromQuickCompiledCodePtrSize(nullptr, pointer_size);
DCHECK_NE(instruction_set_, kNone);
@@ -1929,6 +1933,7 @@
CHECK(method != nullptr);
CHECK(method->IsRuntimeMethod());
imt_unimplemented_method_ = method;
+ method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
}
bool Runtime::IsVerificationEnabled() const {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index ac6e689..6a6fdb7 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -386,7 +386,8 @@
void SetImtConflictMethod(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
void SetImtUnimplementedMethod(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
- ArtMethod* CreateImtConflictMethod() SHARED_REQUIRES(Locks::mutator_lock_);
+ ArtMethod* CreateImtConflictMethod(LinearAlloc* linear_alloc)
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Returns a special method that describes all callee saves being spilled to the stack.
enum CalleeSaveType {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 4dc6d57..42b5a4a 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -44,6 +44,7 @@
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "gc_map.h"
#include "gc/accounting/card_table-inl.h"
+#include "gc/accounting/heap_bitmap-inl.h"
#include "gc/allocator/rosalloc.h"
#include "gc/heap.h"
#include "gc/space/space-inl.h"
diff --git a/test/097-duplicate-method/build b/test/097-duplicate-method/build
deleted file mode 100644
index 4525549..0000000
--- a/test/097-duplicate-method/build
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2012 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Stop if something fails.
-set -e
-
-mkdir classes
-
-if [ ${USE_JACK} = "true" ]; then
- ${JACK} --output-jack src.jack src
-
- ${JASMIN} -d classes src/*.j
- jar cf jasmin.jill.jar -C classes .
-
- # We set jack.import.type.policy=keep-first to consider class definitions from jasmin first.
- ${JACK} --import jasmin.jill.jar --import src.jack -D jack.import.type.policy=keep-first --output-dex .
-else
- ${JAVAC} -d classes src/*.java
- ${JASMIN} -d classes src/*.j
-
- ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes
-fi
-zip $TEST_NAME.jar classes.dex
diff --git a/test/097-duplicate-method/classes.dex b/test/097-duplicate-method/classes.dex
new file mode 100644
index 0000000..18f8958
--- /dev/null
+++ b/test/097-duplicate-method/classes.dex
Binary files differ
diff --git a/test/097-duplicate-method/info.txt b/test/097-duplicate-method/info.txt
index 4e7e0ee..ed0daed 100644
--- a/test/097-duplicate-method/info.txt
+++ b/test/097-duplicate-method/info.txt
@@ -1,2 +1,7 @@
This is a test to verify that duplicate methods in a dex file are handled
properly (all but the first are ignored).
+
+We need to build a dex file with duplicate methods. We cannot do that
+with Jack (this is invalid) or smali (it does not keep duplicate
+methods, only one is in the dex). Therefore, having a precompiled
+dex file allows to run the test on whatever toolchain.
diff --git a/test/097-duplicate-method/src/Main.java b/test/097-duplicate-method/src/Main.java
deleted file mode 100644
index bb3d36a..0000000
--- a/test/097-duplicate-method/src/Main.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-/**
- * Certain class files generated by smali can have encoded methods with an
- * identical method_idx. In these cases, the behavior should be to only use the
- * first one, and to ignore all following duplicates.
- */
-public class Main {
- public static void main(String args[]) {
- if (Test.run() != null) {
- System.out.println("Success!");
- }
- }
-}
diff --git a/test/097-duplicate-method/src/Test.j b/test/097-duplicate-method/src/Test.j
deleted file mode 100644
index f96a9a3..0000000
--- a/test/097-duplicate-method/src/Test.j
+++ /dev/null
@@ -1,29 +0,0 @@
-; Copyright (C) 2012 The Android Open Source Project
-;
-; Licensed under the Apache License, Version 2.0 (the "License");
-; you may not use this file except in compliance with the License.
-; You may obtain a copy of the License at
-;
-; http://www.apache.org/licenses/LICENSE-2.0
-;
-; Unless required by applicable law or agreed to in writing, software
-; distributed under the License is distributed on an "AS IS" BASIS,
-; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-; See the License for the specific language governing permissions and
-; limitations under the License.
-
-.class Test
-.super java/lang/Object
-
-.method public static run()Ljava/lang/Object;
- .limit stack 2
- new java/lang/Object
- dup
- invokespecial java/lang/Object/<init>()V
- areturn
-.end method
-
-.method public static run()Ljava/lang/Object;
- aconst_null
- areturn
-.end method
diff --git a/test/097-duplicate-method/src/Test.java b/test/097-duplicate-method/src/Test.java
deleted file mode 100644
index 7dd61e6..0000000
--- a/test/097-duplicate-method/src/Test.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * This class and method will be overwritten by smali to return non-null.
- */
-public class Test {
- public static Object run() {
- return null;
- }
-}
diff --git a/test/431-optimizing-arith-shifts/src/Main.java b/test/431-optimizing-arith-shifts/src/Main.java
index 86422bd..b7a112f 100644
--- a/test/431-optimizing-arith-shifts/src/Main.java
+++ b/test/431-optimizing-arith-shifts/src/Main.java
@@ -29,304 +29,302 @@
}
public static void main(String[] args) {
- shlInt();
- shlLong();
- shrInt();
- shrLong();
- ushrInt();
- ushrLong();
+ testShlInt();
+ testShlLong();
+ testShrInt();
+ testShrLong();
+ testUShrInt();
+ testUShrLong();
}
- private static void shlInt() {
- expectEquals(48, $opt$ShlConst2(12));
- expectEquals(12, $opt$ShlConst0(12));
- expectEquals(-48, $opt$Shl(-12, 2));
- expectEquals(1024, $opt$Shl(32, 5));
+ private static void testShlInt() {
+ expectEquals(48, $opt$ShlIntConst2(12));
+ expectEquals(12, $opt$ShlIntConst0(12));
+ expectEquals(-48, $opt$ShlInt(-12, 2));
+ expectEquals(1024, $opt$ShlInt(32, 5));
- expectEquals(7, $opt$Shl(7, 0));
- expectEquals(14, $opt$Shl(7, 1));
- expectEquals(0, $opt$Shl(0, 30));
+ expectEquals(7, $opt$ShlInt(7, 0));
+ expectEquals(14, $opt$ShlInt(7, 1));
+ expectEquals(0, $opt$ShlInt(0, 30));
- expectEquals(1073741824L, $opt$Shl(1, 30));
- expectEquals(Integer.MIN_VALUE, $opt$Shl(1, 31)); // overflow
- expectEquals(Integer.MIN_VALUE, $opt$Shl(1073741824, 1)); // overflow
- expectEquals(1073741824, $opt$Shl(268435456, 2));
+ expectEquals(1073741824L, $opt$ShlInt(1, 30));
+ expectEquals(Integer.MIN_VALUE, $opt$ShlInt(1, 31)); // overflow
+ expectEquals(Integer.MIN_VALUE, $opt$ShlInt(1073741824, 1)); // overflow
+ expectEquals(1073741824, $opt$ShlInt(268435456, 2));
// Only the 5 lower bits should be used for shifting (& 0x1f).
- expectEquals(7, $opt$Shl(7, 32)); // 32 & 0x1f = 0
- expectEquals(14, $opt$Shl(7, 33)); // 33 & 0x1f = 1
- expectEquals(32, $opt$Shl(1, 101)); // 101 & 0x1f = 5
+ expectEquals(7, $opt$ShlInt(7, 32)); // 32 & 0x1f = 0
+ expectEquals(14, $opt$ShlInt(7, 33)); // 33 & 0x1f = 1
+ expectEquals(32, $opt$ShlInt(1, 101)); // 101 & 0x1f = 5
- expectEquals(Integer.MIN_VALUE, $opt$Shl(1, -1)); // -1 & 0x1f = 31
- expectEquals(14, $opt$Shl(7, -31)); // -31 & 0x1f = 1
- expectEquals(7, $opt$Shl(7, -32)); // -32 & 0x1f = 0
- expectEquals(-536870912, $opt$Shl(7, -3)); // -3 & 0x1f = 29
+ expectEquals(Integer.MIN_VALUE, $opt$ShlInt(1, -1)); // -1 & 0x1f = 31
+ expectEquals(14, $opt$ShlInt(7, -31)); // -31 & 0x1f = 1
+ expectEquals(7, $opt$ShlInt(7, -32)); // -32 & 0x1f = 0
+ expectEquals(-536870912, $opt$ShlInt(7, -3)); // -3 & 0x1f = 29
- expectEquals(Integer.MIN_VALUE, $opt$Shl(7, Integer.MAX_VALUE));
- expectEquals(7, $opt$Shl(7, Integer.MIN_VALUE));
+ expectEquals(Integer.MIN_VALUE, $opt$ShlInt(7, Integer.MAX_VALUE));
+ expectEquals(7, $opt$ShlInt(7, Integer.MIN_VALUE));
}
- private static void shlLong() {
- expectEquals(48L, $opt$ShlConst2(12L));
- expectEquals(12L, $opt$ShlConst0(12L));
- expectEquals(-48L, $opt$Shl(-12L, 2L));
- expectEquals(1024L, $opt$Shl(32L, 5L));
+ private static void testShlLong() {
+ expectEquals(48L, $opt$ShlLongConst2(12L));
+ expectEquals(12L, $opt$ShlLongConst0(12L));
+ expectEquals(-48L, $opt$ShlLong(-12L, 2));
+ expectEquals(1024L, $opt$ShlLong(32L, 5));
- expectEquals(7L, $opt$Shl(7L, 0L));
- expectEquals(14L, $opt$Shl(7L, 1L));
- expectEquals(0L, $opt$Shl(0L, 30L));
+ expectEquals(7L, $opt$ShlLong(7L, 0));
+ expectEquals(14L, $opt$ShlLong(7L, 1));
+ expectEquals(0L, $opt$ShlLong(0L, 30));
- expectEquals(1073741824L, $opt$Shl(1L, 30L));
- expectEquals(2147483648L, $opt$Shl(1L, 31L));
- expectEquals(2147483648L, $opt$Shl(1073741824L, 1L));
+ expectEquals(1073741824L, $opt$ShlLong(1L, 30));
+ expectEquals(2147483648L, $opt$ShlLong(1L, 31));
+ expectEquals(2147483648L, $opt$ShlLong(1073741824L, 1));
// Long shifts can use up to 6 lower bits.
- expectEquals(4294967296L, $opt$Shl(1L, 32L));
- expectEquals(60129542144L, $opt$Shl(7L, 33L));
- expectEquals(Long.MIN_VALUE, $opt$Shl(1L, 63L)); // overflow
+ expectEquals(4294967296L, $opt$ShlLong(1L, 32));
+ expectEquals(60129542144L, $opt$ShlLong(7L, 33));
+ expectEquals(Long.MIN_VALUE, $opt$ShlLong(1L, 63)); // overflow
// Only the 6 lower bits should be used for shifting (& 0x3f).
- expectEquals(7L, $opt$Shl(7L, 64L)); // 64 & 0x3f = 0
- expectEquals(14L, $opt$Shl(7L, 65L)); // 65 & 0x3f = 1
- expectEquals(137438953472L, $opt$Shl(1L, 101L)); // 101 & 0x3f = 37
+ expectEquals(7L, $opt$ShlLong(7L, 64)); // 64 & 0x3f = 0
+ expectEquals(14L, $opt$ShlLong(7L, 65)); // 65 & 0x3f = 1
+ expectEquals(137438953472L, $opt$ShlLong(1L, 101)); // 101 & 0x3f = 37
- expectEquals(Long.MIN_VALUE, $opt$Shl(1L, -1L)); // -1 & 0x3f = 63
- expectEquals(14L, $opt$Shl(7L, -63L)); // -63 & 0x3f = 1
- expectEquals(7L, $opt$Shl(7L, -64L)); // -64 & 0x3f = 0
- expectEquals(2305843009213693952L, $opt$Shl(1L, -3L)); // -3 & 0x3f = 61
+ expectEquals(Long.MIN_VALUE, $opt$ShlLong(1L, -1)); // -1 & 0x3f = 63
+ expectEquals(14L, $opt$ShlLong(7L, -63)); // -63 & 0x3f = 1
+ expectEquals(7L, $opt$ShlLong(7L, -64)); // -64 & 0x3f = 0
+ expectEquals(2305843009213693952L, $opt$ShlLong(1L, -3)); // -3 & 0x3f = 61
- expectEquals(Long.MIN_VALUE, $opt$Shl(7L, Long.MAX_VALUE));
- expectEquals(7L, $opt$Shl(7L, Long.MIN_VALUE));
+ expectEquals(Long.MIN_VALUE, $opt$ShlLong(7L, Integer.MAX_VALUE));
+ expectEquals(7L, $opt$ShlLong(7L, Integer.MIN_VALUE));
// Exercise some special cases handled by backends/simplifier.
- expectEquals(24L, $opt$ShlConst1(12L));
- expectEquals(0x2345678900000000L, $opt$ShlConst32(0x123456789L));
- expectEquals(0x2490249000000000L, $opt$ShlConst33(0x12481248L));
- expectEquals(0x4920492000000000L, $opt$ShlConst34(0x12481248L));
- expectEquals(0x9240924000000000L, $opt$ShlConst35(0x12481248L));
+ expectEquals(24L, $opt$ShlLongConst1(12L));
+ expectEquals(0x2345678900000000L, $opt$ShlLongConst32(0x123456789L));
+ expectEquals(0x2490249000000000L, $opt$ShlLongConst33(0x12481248L));
+ expectEquals(0x4920492000000000L, $opt$ShlLongConst34(0x12481248L));
+ expectEquals(0x9240924000000000L, $opt$ShlLongConst35(0x12481248L));
}
- private static void shrInt() {
- expectEquals(3, $opt$ShrConst2(12));
- expectEquals(12, $opt$ShrConst0(12));
- expectEquals(-3, $opt$Shr(-12, 2));
- expectEquals(1, $opt$Shr(32, 5));
+ private static void testShrInt() {
+ expectEquals(3, $opt$ShrIntConst2(12));
+ expectEquals(12, $opt$ShrIntConst0(12));
+ expectEquals(-3, $opt$ShrInt(-12, 2));
+ expectEquals(1, $opt$ShrInt(32, 5));
- expectEquals(7, $opt$Shr(7, 0));
- expectEquals(3, $opt$Shr(7, 1));
- expectEquals(0, $opt$Shr(0, 30));
- expectEquals(0, $opt$Shr(1, 30));
- expectEquals(-1, $opt$Shr(-1, 30));
+ expectEquals(7, $opt$ShrInt(7, 0));
+ expectEquals(3, $opt$ShrInt(7, 1));
+ expectEquals(0, $opt$ShrInt(0, 30));
+ expectEquals(0, $opt$ShrInt(1, 30));
+ expectEquals(-1, $opt$ShrInt(-1, 30));
- expectEquals(0, $opt$Shr(Integer.MAX_VALUE, 31));
- expectEquals(-1, $opt$Shr(Integer.MIN_VALUE, 31));
+ expectEquals(0, $opt$ShrInt(Integer.MAX_VALUE, 31));
+ expectEquals(-1, $opt$ShrInt(Integer.MIN_VALUE, 31));
// Only the 5 lower bits should be used for shifting (& 0x1f).
- expectEquals(7, $opt$Shr(7, 32)); // 32 & 0x1f = 0
- expectEquals(3, $opt$Shr(7, 33)); // 33 & 0x1f = 1
+ expectEquals(7, $opt$ShrInt(7, 32)); // 32 & 0x1f = 0
+ expectEquals(3, $opt$ShrInt(7, 33)); // 33 & 0x1f = 1
- expectEquals(0, $opt$Shr(1, -1)); // -1 & 0x1f = 31
- expectEquals(3, $opt$Shr(7, -31)); // -31 & 0x1f = 1
- expectEquals(7, $opt$Shr(7, -32)); // -32 & 0x1f = 0
- expectEquals(-4, $opt$Shr(Integer.MIN_VALUE, -3)); // -3 & 0x1f = 29
+ expectEquals(0, $opt$ShrInt(1, -1)); // -1 & 0x1f = 31
+ expectEquals(3, $opt$ShrInt(7, -31)); // -31 & 0x1f = 1
+ expectEquals(7, $opt$ShrInt(7, -32)); // -32 & 0x1f = 0
+ expectEquals(-4, $opt$ShrInt(Integer.MIN_VALUE, -3)); // -3 & 0x1f = 29
- expectEquals(0, $opt$Shr(7, Integer.MAX_VALUE));
- expectEquals(7, $opt$Shr(7, Integer.MIN_VALUE));
+ expectEquals(0, $opt$ShrInt(7, Integer.MAX_VALUE));
+ expectEquals(7, $opt$ShrInt(7, Integer.MIN_VALUE));
}
- private static void shrLong() {
- expectEquals(3L, $opt$ShrConst2(12L));
- expectEquals(12L, $opt$ShrConst0(12L));
- expectEquals(-3L, $opt$Shr(-12L, 2L));
- expectEquals(1, $opt$Shr(32, 5));
+ private static void testShrLong() {
+ expectEquals(3L, $opt$ShrLongConst2(12L));
+ expectEquals(12L, $opt$ShrLongConst0(12L));
+ expectEquals(-3L, $opt$ShrLong(-12L, 2));
+ expectEquals(1, $opt$ShrLong(32, 5));
- expectEquals(7L, $opt$Shr(7L, 0L));
- expectEquals(3L, $opt$Shr(7L, 1L));
- expectEquals(0L, $opt$Shr(0L, 30L));
- expectEquals(0L, $opt$Shr(1L, 30L));
- expectEquals(-1L, $opt$Shr(-1L, 30L));
+ expectEquals(7L, $opt$ShrLong(7L, 0));
+ expectEquals(3L, $opt$ShrLong(7L, 1));
+ expectEquals(0L, $opt$ShrLong(0L, 30));
+ expectEquals(0L, $opt$ShrLong(1L, 30));
+ expectEquals(-1L, $opt$ShrLong(-1L, 30));
-
- expectEquals(1L, $opt$Shr(1073741824L, 30L));
- expectEquals(1L, $opt$Shr(2147483648L, 31L));
- expectEquals(1073741824L, $opt$Shr(2147483648L, 1L));
+ expectEquals(1L, $opt$ShrLong(1073741824L, 30));
+ expectEquals(1L, $opt$ShrLong(2147483648L, 31));
+ expectEquals(1073741824L, $opt$ShrLong(2147483648L, 1));
// Long shifts can use up to 6 lower bits.
- expectEquals(1L, $opt$Shr(4294967296L, 32L));
- expectEquals(7L, $opt$Shr(60129542144L, 33L));
- expectEquals(0L, $opt$Shr(Long.MAX_VALUE, 63L));
- expectEquals(-1L, $opt$Shr(Long.MIN_VALUE, 63L));
+ expectEquals(1L, $opt$ShrLong(4294967296L, 32));
+ expectEquals(7L, $opt$ShrLong(60129542144L, 33));
+ expectEquals(0L, $opt$ShrLong(Long.MAX_VALUE, 63));
+ expectEquals(-1L, $opt$ShrLong(Long.MIN_VALUE, 63));
// Only the 6 lower bits should be used for shifting (& 0x3f).
- expectEquals(7L, $opt$Shr(7L, 64L)); // 64 & 0x3f = 0
- expectEquals(3L, $opt$Shr(7L, 65L)); // 65 & 0x3f = 1
+ expectEquals(7L, $opt$ShrLong(7L, 64)); // 64 & 0x3f = 0
+ expectEquals(3L, $opt$ShrLong(7L, 65)); // 65 & 0x3f = 1
- expectEquals(-1L, $opt$Shr(Long.MIN_VALUE, -1L)); // -1 & 0x3f = 63
- expectEquals(3L, $opt$Shr(7L, -63L)); // -63 & 0x3f = 1
- expectEquals(7L, $opt$Shr(7L, -64L)); // -64 & 0x3f = 0
- expectEquals(1L, $opt$Shr(2305843009213693952L, -3L)); // -3 & 0x3f = 61
- expectEquals(-4L, $opt$Shr(Integer.MIN_VALUE, -3)); // -3 & 0x1f = 29
+ expectEquals(-1L, $opt$ShrLong(Long.MIN_VALUE, -1)); // -1 & 0x3f = 63
+ expectEquals(3L, $opt$ShrLong(7L, -63)); // -63 & 0x3f = 1
+ expectEquals(7L, $opt$ShrLong(7L, -64)); // -64 & 0x3f = 0
+ expectEquals(1L, $opt$ShrLong(2305843009213693952L, -3)); // -3 & 0x3f = 61
+ expectEquals(-1L, $opt$ShrLong(Integer.MIN_VALUE, -3)); // -3 & 0x1f = 29
- expectEquals(0L, $opt$Shr(7L, Long.MAX_VALUE));
- expectEquals(7L, $opt$Shr(7L, Long.MIN_VALUE));
+ expectEquals(0L, $opt$ShrLong(7L, Integer.MAX_VALUE));
+ expectEquals(7L, $opt$ShrLong(7L, Integer.MIN_VALUE));
}
- private static void ushrInt() {
- expectEquals(3, $opt$UShrConst2(12));
- expectEquals(12, $opt$UShrConst0(12));
- expectEquals(1073741821, $opt$UShr(-12, 2));
- expectEquals(1, $opt$UShr(32, 5));
+ private static void testUShrInt() {
+ expectEquals(3, $opt$UShrIntConst2(12));
+ expectEquals(12, $opt$UShrIntConst0(12));
+ expectEquals(1073741821, $opt$UShrInt(-12, 2));
+ expectEquals(1, $opt$UShrInt(32, 5));
- expectEquals(7, $opt$UShr(7, 0));
- expectEquals(3, $opt$UShr(7, 1));
- expectEquals(0, $opt$UShr(0, 30));
- expectEquals(0, $opt$UShr(1, 30));
- expectEquals(3, $opt$UShr(-1, 30));
+ expectEquals(7, $opt$UShrInt(7, 0));
+ expectEquals(3, $opt$UShrInt(7, 1));
+ expectEquals(0, $opt$UShrInt(0, 30));
+ expectEquals(0, $opt$UShrInt(1, 30));
+ expectEquals(3, $opt$UShrInt(-1, 30));
- expectEquals(0, $opt$UShr(Integer.MAX_VALUE, 31));
- expectEquals(1, $opt$UShr(Integer.MIN_VALUE, 31));
+ expectEquals(0, $opt$UShrInt(Integer.MAX_VALUE, 31));
+ expectEquals(1, $opt$UShrInt(Integer.MIN_VALUE, 31));
// Only the 5 lower bits should be used for shifting (& 0x1f).
- expectEquals(7, $opt$UShr(7, 32)); // 32 & 0x1f = 0
- expectEquals(3, $opt$UShr(7, 33)); // 33 & 0x1f = 1
+ expectEquals(7, $opt$UShrInt(7, 32)); // 32 & 0x1f = 0
+ expectEquals(3, $opt$UShrInt(7, 33)); // 33 & 0x1f = 1
- expectEquals(0, $opt$UShr(1, -1)); // -1 & 0x1f = 31
- expectEquals(3, $opt$UShr(7, -31)); // -31 & 0x1f = 1
- expectEquals(7, $opt$UShr(7, -32)); // -32 & 0x1f = 0
- expectEquals(4, $opt$UShr(Integer.MIN_VALUE, -3)); // -3 & 0x1f = 29
+ expectEquals(0, $opt$UShrInt(1, -1)); // -1 & 0x1f = 31
+ expectEquals(3, $opt$UShrInt(7, -31)); // -31 & 0x1f = 1
+ expectEquals(7, $opt$UShrInt(7, -32)); // -32 & 0x1f = 0
+ expectEquals(4, $opt$UShrInt(Integer.MIN_VALUE, -3)); // -3 & 0x1f = 29
- expectEquals(0, $opt$UShr(7, Integer.MAX_VALUE));
- expectEquals(7, $opt$UShr(7, Integer.MIN_VALUE));
+ expectEquals(0, $opt$UShrInt(7, Integer.MAX_VALUE));
+ expectEquals(7, $opt$UShrInt(7, Integer.MIN_VALUE));
}
- private static void ushrLong() {
- expectEquals(3L, $opt$UShrConst2(12L));
- expectEquals(12L, $opt$UShrConst0(12L));
- expectEquals(4611686018427387901L, $opt$UShr(-12L, 2L));
- expectEquals(1, $opt$UShr(32, 5));
+ private static void testUShrLong() {
+ expectEquals(3L, $opt$UShrLongConst2(12L));
+ expectEquals(12L, $opt$UShrLongConst0(12L));
+ expectEquals(4611686018427387901L, $opt$UShrLong(-12L, 2));
+ expectEquals(1, $opt$UShrLong(32, 5));
- expectEquals(7L, $opt$UShr(7L, 0L));
- expectEquals(3L, $opt$UShr(7L, 1L));
- expectEquals(0L, $opt$UShr(0L, 30L));
- expectEquals(0L, $opt$UShr(1L, 30L));
- expectEquals(17179869183L, $opt$UShr(-1L, 30L));
+ expectEquals(7L, $opt$UShrLong(7L, 0));
+ expectEquals(3L, $opt$UShrLong(7L, 1));
+ expectEquals(0L, $opt$UShrLong(0L, 30));
+ expectEquals(0L, $opt$UShrLong(1L, 30));
+ expectEquals(17179869183L, $opt$UShrLong(-1L, 30));
-
- expectEquals(1L, $opt$UShr(1073741824L, 30L));
- expectEquals(1L, $opt$UShr(2147483648L, 31L));
- expectEquals(1073741824L, $opt$UShr(2147483648L, 1L));
+ expectEquals(1L, $opt$UShrLong(1073741824L, 30));
+ expectEquals(1L, $opt$UShrLong(2147483648L, 31));
+ expectEquals(1073741824L, $opt$UShrLong(2147483648L, 1));
// Long shifts can use use up to 6 lower bits.
- expectEquals(1L, $opt$UShr(4294967296L, 32L));
- expectEquals(7L, $opt$UShr(60129542144L, 33L));
- expectEquals(0L, $opt$UShr(Long.MAX_VALUE, 63L));
- expectEquals(1L, $opt$UShr(Long.MIN_VALUE, 63L));
+ expectEquals(1L, $opt$UShrLong(4294967296L, 32));
+ expectEquals(7L, $opt$UShrLong(60129542144L, 33));
+ expectEquals(0L, $opt$UShrLong(Long.MAX_VALUE, 63));
+ expectEquals(1L, $opt$UShrLong(Long.MIN_VALUE, 63));
// Only the 6 lower bits should be used for shifting (& 0x3f).
- expectEquals(7L, $opt$UShr(7L, 64L)); // 64 & 0x3f = 0
- expectEquals(3L, $opt$UShr(7L, 65L)); // 65 & 0x3f = 1
+ expectEquals(7L, $opt$UShrLong(7L, 64)); // 64 & 0x3f = 0
+ expectEquals(3L, $opt$UShrLong(7L, 65)); // 65 & 0x3f = 1
- expectEquals(1L, $opt$UShr(Long.MIN_VALUE, -1L)); // -1 & 0x3f = 63
- expectEquals(3L, $opt$UShr(7L, -63L)); // -63 & 0x3f = 1
- expectEquals(7L, $opt$UShr(7L, -64L)); // -64 & 0x3f = 0
- expectEquals(1L, $opt$UShr(2305843009213693952L, -3L)); // -3 & 0x3f = 61
- expectEquals(4L, $opt$UShr(Long.MIN_VALUE, -3L)); // -3 & 0x3f = 61
+ expectEquals(1L, $opt$UShrLong(Long.MIN_VALUE, -1)); // -1 & 0x3f = 63
+ expectEquals(3L, $opt$UShrLong(7L, -63)); // -63 & 0x3f = 1
+ expectEquals(7L, $opt$UShrLong(7L, -64)); // -64 & 0x3f = 0
+ expectEquals(1L, $opt$UShrLong(2305843009213693952L, -3)); // -3 & 0x3f = 61
+ expectEquals(4L, $opt$UShrLong(Long.MIN_VALUE, -3)); // -3 & 0x3f = 61
- expectEquals(0L, $opt$UShr(7L, Long.MAX_VALUE));
- expectEquals(7L, $opt$UShr(7L, Long.MIN_VALUE));
+ expectEquals(0L, $opt$UShrLong(7L, Integer.MAX_VALUE));
+ expectEquals(7L, $opt$UShrLong(7L, Integer.MIN_VALUE));
}
- static int $opt$Shl(int a, int b) {
- return a << b;
+
+ static int $opt$ShlInt(int value, int distance) {
+ return value << distance;
}
- static long $opt$Shl(long a, long b) {
- return a << b;
+ static long $opt$ShlLong(long value, int distance) {
+ return value << distance;
}
- static int $opt$Shr(int a, int b) {
- return a >> b;
+ static int $opt$ShrInt(int value, int distance) {
+ return value >> distance;
}
- static long $opt$Shr(long a, long b) {
- return a >> b;
+ static long $opt$ShrLong(long value, int distance) {
+ return value >> distance;
}
- static int $opt$UShr(int a, int b) {
- return a >>> b;
+ static int $opt$UShrInt(int value, int distance) {
+ return value >>> distance;
}
- static long $opt$UShr(long a, long b) {
- return a >>> b;
+ static long $opt$UShrLong(long value, int distance) {
+ return value >>> distance;
}
- static int $opt$ShlConst2(int a) {
- return a << 2;
+ static int $opt$ShlIntConst2(int value) {
+ return value << 2;
}
- static long $opt$ShlConst2(long a) {
- return a << 2L;
+ static long $opt$ShlLongConst2(long value) {
+ return value << 2;
}
- static int $opt$ShrConst2(int a) {
- return a >> 2;
+ static int $opt$ShrIntConst2(int value) {
+ return value >> 2;
}
- static long $opt$ShrConst2(long a) {
- return a >> 2L;
+ static long $opt$ShrLongConst2(long value) {
+ return value >> 2;
}
- static int $opt$UShrConst2(int a) {
- return a >>> 2;
+ static int $opt$UShrIntConst2(int value) {
+ return value >>> 2;
}
- static long $opt$UShrConst2(long a) {
- return a >>> 2L;
+ static long $opt$UShrLongConst2(long value) {
+ return value >>> 2;
}
- static int $opt$ShlConst0(int a) {
- return a << 0;
+ static int $opt$ShlIntConst0(int value) {
+ return value << 0;
}
- static long $opt$ShlConst0(long a) {
- return a << 0L;
+ static long $opt$ShlLongConst0(long value) {
+ return value << 0;
}
- static int $opt$ShrConst0(int a) {
- return a >> 0;
+ static int $opt$ShrIntConst0(int value) {
+ return value >> 0;
}
- static long $opt$ShrConst0(long a) {
- return a >> 0L;
+ static long $opt$ShrLongConst0(long value) {
+ return value >> 0;
}
- static int $opt$UShrConst0(int a) {
- return a >>> 0;
+ static int $opt$UShrIntConst0(int value) {
+ return value >>> 0;
}
- static long $opt$UShrConst0(long a) {
- return a >>> 0L;
+ static long $opt$UShrLongConst0(long value) {
+ return value >>> 0;
}
- static long $opt$ShlConst1(long a) {
- return a << 1L;
+ static long $opt$ShlLongConst1(long value) {
+ return value << 1;
}
- static long $opt$ShlConst32(long a) {
- return a << 32L;
+ static long $opt$ShlLongConst32(long value) {
+ return value << 32;
}
- static long $opt$ShlConst33(long a) {
- return a << 33L;
+ static long $opt$ShlLongConst33(long value) {
+ return value << 33;
}
- static long $opt$ShlConst34(long a) {
- return a << 34L;
+ static long $opt$ShlLongConst34(long value) {
+ return value << 34;
}
- static long $opt$ShlConst35(long a) {
- return a << 35L;
+ static long $opt$ShlLongConst35(long value) {
+ return value << 35;
}
}
-
diff --git a/test/469-condition-materialization-regression/expected.txt b/test/469-condition-materialization/expected.txt
similarity index 100%
rename from test/469-condition-materialization-regression/expected.txt
rename to test/469-condition-materialization/expected.txt
diff --git a/test/469-condition-materialization-regression/info.txt b/test/469-condition-materialization/info.txt
similarity index 100%
rename from test/469-condition-materialization-regression/info.txt
rename to test/469-condition-materialization/info.txt
diff --git a/test/469-condition-materialization-regression/src/Main.java b/test/469-condition-materialization/src/Main.java
similarity index 100%
rename from test/469-condition-materialization-regression/src/Main.java
rename to test/469-condition-materialization/src/Main.java
diff --git a/test/476-clinit-check-inlining-static-invoke/expected.txt b/test/476-clinit-inline-static-invoke/expected.txt
similarity index 100%
rename from test/476-clinit-check-inlining-static-invoke/expected.txt
rename to test/476-clinit-inline-static-invoke/expected.txt
diff --git a/test/476-clinit-check-inlining-static-invoke/info.txt b/test/476-clinit-inline-static-invoke/info.txt
similarity index 100%
rename from test/476-clinit-check-inlining-static-invoke/info.txt
rename to test/476-clinit-inline-static-invoke/info.txt
diff --git a/test/476-clinit-check-inlining-static-invoke/src/Main.java b/test/476-clinit-inline-static-invoke/src/Main.java
similarity index 100%
rename from test/476-clinit-check-inlining-static-invoke/src/Main.java
rename to test/476-clinit-inline-static-invoke/src/Main.java
diff --git a/test/565-checker-rotate/src/Main.java b/test/565-checker-rotate/src/Main.java
index d7f57d8..aadb597 100644
--- a/test/565-checker-rotate/src/Main.java
+++ b/test/565-checker-rotate/src/Main.java
@@ -320,6 +320,48 @@
}
+ /// CHECK-START: int Main.rotateLeftIntWithByteDistance(int, byte) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+ /// CHECK: <<ArgVal:i\d+>> ParameterValue
+ /// CHECK: <<ArgDist:b\d+>> ParameterValue
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>,<<Method>>] intrinsic:IntegerRotateLeft
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.rotateLeftIntWithByteDistance(int, byte) instruction_simplifier (after)
+ /// CHECK: <<ArgVal:i\d+>> ParameterValue
+ /// CHECK: <<ArgDist:b\d+>> ParameterValue
+ /// CHECK-DAG: <<NegDist:i\d+>> Neg [<<ArgDist>>]
+ /// CHECK-DAG: <<Result:i\d+>> Ror [<<ArgVal>>,<<NegDist>>]
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.rotateLeftIntWithByteDistance(int, byte) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ private static int rotateLeftIntWithByteDistance(int value, byte distance) {
+ return Integer.rotateLeft(value, distance);
+ }
+
+ /// CHECK-START: int Main.rotateRightIntWithByteDistance(int, byte) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+ /// CHECK: <<ArgVal:i\d+>> ParameterValue
+ /// CHECK: <<ArgDist:b\d+>> ParameterValue
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>,<<Method>>] intrinsic:IntegerRotateRight
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.rotateRightIntWithByteDistance(int, byte) instruction_simplifier (after)
+ /// CHECK: <<ArgVal:i\d+>> ParameterValue
+ /// CHECK: <<ArgDist:b\d+>> ParameterValue
+ /// CHECK-DAG: <<Result:i\d+>> Ror [<<ArgVal>>,<<ArgDist>>]
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.rotateRightIntWithByteDistance(int, byte) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ private static int rotateRightIntWithByteDistance(int value, byte distance) {
+ return Integer.rotateRight(value, distance);
+ }
+
+
public static void testRotateLeftBoolean() {
for (int i = 0; i < 40; i++) { // overshoot a bit
int j = i & 31;
@@ -518,6 +560,45 @@
}
+ public static void testRotateLeftIntWithByteDistance() {
+ expectEqualsInt(0x00000001, rotateLeftIntWithByteDistance(0x00000001, (byte)0));
+ expectEqualsInt(0x00000002, rotateLeftIntWithByteDistance(0x00000001, (byte)1));
+ expectEqualsInt(0x80000000, rotateLeftIntWithByteDistance(0x00000001, (byte)31));
+ expectEqualsInt(0x00000001, rotateLeftIntWithByteDistance(0x00000001, (byte)32)); // overshoot
+ expectEqualsInt(0x00000003, rotateLeftIntWithByteDistance(0x80000001, (byte)1));
+ expectEqualsInt(0x00000006, rotateLeftIntWithByteDistance(0x80000001, (byte)2));
+ expectEqualsInt(0x23456781, rotateLeftIntWithByteDistance(0x12345678, (byte)4));
+ expectEqualsInt(0xBCDEF09A, rotateLeftIntWithByteDistance(0x9ABCDEF0, (byte)8));
+ for (byte i = 0; i < 40; i++) { // overshoot a bit
+ byte j = (byte)(i & 31);
+ expectEqualsInt(0x00000000, rotateLeftIntWithByteDistance(0x00000000, i));
+ expectEqualsInt(0xFFFFFFFF, rotateLeftIntWithByteDistance(0xFFFFFFFF, i));
+ expectEqualsInt(1 << j, rotateLeftIntWithByteDistance(0x00000001, i));
+ expectEqualsInt((0x12345678 << j) | (0x12345678 >>> -j),
+ rotateLeftIntWithByteDistance(0x12345678, i));
+ }
+ }
+
+ public static void testRotateRightIntWithByteDistance() {
+ expectEqualsInt(0x80000000, rotateRightIntWithByteDistance(0x80000000, (byte)0));
+ expectEqualsInt(0x40000000, rotateRightIntWithByteDistance(0x80000000, (byte)1));
+ expectEqualsInt(0x00000001, rotateRightIntWithByteDistance(0x80000000, (byte)31));
+ expectEqualsInt(0x80000000, rotateRightIntWithByteDistance(0x80000000, (byte)32)); // overshoot
+ expectEqualsInt(0xC0000000, rotateRightIntWithByteDistance(0x80000001, (byte)1));
+ expectEqualsInt(0x60000000, rotateRightIntWithByteDistance(0x80000001, (byte)2));
+ expectEqualsInt(0x81234567, rotateRightIntWithByteDistance(0x12345678, (byte)4));
+ expectEqualsInt(0xF09ABCDE, rotateRightIntWithByteDistance(0x9ABCDEF0, (byte)8));
+ for (byte i = 0; i < 40; i++) { // overshoot a bit
+ byte j = (byte)(i & 31);
+ expectEqualsInt(0x00000000, rotateRightIntWithByteDistance(0x00000000, i));
+ expectEqualsInt(0xFFFFFFFF, rotateRightIntWithByteDistance(0xFFFFFFFF, i));
+ expectEqualsInt(0x80000000 >>> j, rotateRightIntWithByteDistance(0x80000000, i));
+ expectEqualsInt((0x12345678 >>> j) | (0x12345678 << -j),
+ rotateRightIntWithByteDistance(0x12345678, i));
+ }
+ }
+
+
public static void main(String args[]) {
testRotateLeftBoolean();
testRotateLeftByte();
@@ -533,6 +614,10 @@
testRotateRightInt();
testRotateRightLong();
+ // Also exercise distance values with types other than int.
+ testRotateLeftIntWithByteDistance();
+ testRotateRightIntWithByteDistance();
+
System.out.println("passed");
}
diff --git a/test/588-checker-irreducible-lifetime-hole/expected.txt b/test/588-checker-irreducible-lifetime-hole/expected.txt
new file mode 100644
index 0000000..d81cc07
--- /dev/null
+++ b/test/588-checker-irreducible-lifetime-hole/expected.txt
@@ -0,0 +1 @@
+42
diff --git a/test/588-checker-irreducible-lifetime-hole/info.txt b/test/588-checker-irreducible-lifetime-hole/info.txt
new file mode 100644
index 0000000..a2861a9
--- /dev/null
+++ b/test/588-checker-irreducible-lifetime-hole/info.txt
@@ -0,0 +1,3 @@
+Regression test for optimizing that used to have a too
+strong DCHECK in the presence of a combination of irreducible loops
+and try/catch.
diff --git a/test/588-checker-irreducible-lifetime-hole/smali/IrreducibleLoop.smali b/test/588-checker-irreducible-lifetime-hole/smali/IrreducibleLoop.smali
new file mode 100644
index 0000000..207c77e
--- /dev/null
+++ b/test/588-checker-irreducible-lifetime-hole/smali/IrreducibleLoop.smali
@@ -0,0 +1,71 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LIrreducibleLoop;
+
+.super Ljava/lang/Object;
+
+## CHECK-START-X86: int IrreducibleLoop.simpleLoop(int) dead_code_elimination (before)
+## CHECK-DAG: <<Method:(i|j)\d+>> CurrentMethod
+## CHECK-DAG: <<Constant:i\d+>> IntConstant 42
+## CHECK-DAG: Goto irreducible:true
+## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>,<<Method>>] loop:none
+## CHECK-DAG: InvokeStaticOrDirect [{{i\d+}},<<Method>>] loop:none
+.method public static simpleLoop(I)I
+ .registers 3
+ const/16 v0, 42
+ invoke-static {v0}, LIrreducibleLoop;->$noinline$m(I)V
+ if-eqz p0, :b22
+ goto :b34
+
+ :b34
+ goto :b20
+
+ :b20
+ if-nez p0, :b45
+ goto :b46
+
+ :b46
+ goto :b21
+
+ :b21
+ goto :b34
+
+ :b22
+ :try_start
+ div-int v0, v0, v0
+ :try_end
+ .catchall {:try_start .. :try_end} :b34
+ goto :b20
+
+ :b45
+ invoke-static {v0}, LIrreducibleLoop;->$noinline$m(I)V
+ goto :b26
+
+ :b26
+ return v0
+.end method
+
+.method public static $noinline$m(I)V
+ .registers 3
+ const/16 v0, 0
+ sget-boolean v1,LIrreducibleLoop;->doThrow:Z
+ if-eqz v1, :exit
+ # Prevent inlining.
+ throw v0
+ :exit
+ return-void
+.end method
+
+.field public static doThrow:Z
diff --git a/test/588-checker-irreducible-lifetime-hole/src/Main.java b/test/588-checker-irreducible-lifetime-hole/src/Main.java
new file mode 100644
index 0000000..94e3357
--- /dev/null
+++ b/test/588-checker-irreducible-lifetime-hole/src/Main.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("IrreducibleLoop");
+ Method m = c.getMethod("simpleLoop", int.class);
+ Object[] arguments = { 42 };
+ System.out.println(m.invoke(null, arguments));
+ }
+}
diff --git a/test/469-condition-materialization-regression/expected.txt b/test/589-super-imt/expected.txt
similarity index 100%
copy from test/469-condition-materialization-regression/expected.txt
copy to test/589-super-imt/expected.txt
diff --git a/test/589-super-imt/info.txt b/test/589-super-imt/info.txt
new file mode 100644
index 0000000..c815dc9
--- /dev/null
+++ b/test/589-super-imt/info.txt
@@ -0,0 +1,2 @@
+Test what the IMT is properly set for a subclass, and that the
+subclass won't use the ImtConflictTable table of its super class.
diff --git a/test/589-super-imt/src/Main.java b/test/589-super-imt/src/Main.java
new file mode 100644
index 0000000..e381ca7
--- /dev/null
+++ b/test/589-super-imt/src/Main.java
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface Itf {
+ public Class<?> method1();
+ public Class<?> method2();
+ public Class<?> method3();
+ public Class<?> method4();
+ public Class<?> method5();
+ public Class<?> method6();
+ public Class<?> method7();
+ public Class<?> method8();
+ public Class<?> method9();
+ public Class<?> method10();
+ public Class<?> method11();
+ public Class<?> method12();
+ public Class<?> method13();
+ public Class<?> method14();
+ public Class<?> method15();
+ public Class<?> method16();
+ public Class<?> method17();
+ public Class<?> method18();
+ public Class<?> method19();
+ public Class<?> method20();
+ public Class<?> method21();
+ public Class<?> method22();
+ public Class<?> method23();
+ public Class<?> method24();
+ public Class<?> method25();
+ public Class<?> method26();
+ public Class<?> method27();
+ public Class<?> method28();
+ public Class<?> method29();
+ public Class<?> method30();
+ public Class<?> method31();
+ public Class<?> method32();
+ public Class<?> method33();
+ public Class<?> method34();
+ public Class<?> method35();
+ public Class<?> method36();
+ public Class<?> method37();
+ public Class<?> method38();
+ public Class<?> method39();
+ public Class<?> method40();
+ public Class<?> method41();
+ public Class<?> method42();
+ public Class<?> method43();
+ public Class<?> method44();
+ public Class<?> method45();
+ public Class<?> method46();
+ public Class<?> method47();
+ public Class<?> method48();
+ public Class<?> method49();
+ public Class<?> method50();
+ public Class<?> method51();
+ public Class<?> method52();
+ public Class<?> method53();
+ public Class<?> method54();
+ public Class<?> method55();
+ public Class<?> method56();
+ public Class<?> method57();
+ public Class<?> method58();
+ public Class<?> method59();
+ public Class<?> method60();
+ public Class<?> method61();
+ public Class<?> method62();
+ public Class<?> method63();
+ public Class<?> method64();
+ public Class<?> method65();
+ public Class<?> method66();
+ public Class<?> method67();
+ public Class<?> method68();
+ public Class<?> method69();
+ public Class<?> method70();
+ public Class<?> method71();
+ public Class<?> method72();
+ public Class<?> method73();
+ public Class<?> method74();
+ public Class<?> method75();
+ public Class<?> method76();
+ public Class<?> method77();
+ public Class<?> method78();
+ public Class<?> method79();
+}
+
+public class Main implements Itf {
+ public static Itf main;
+ public static void main(String[] args) {
+ main = new Main();
+ callMains();
+ main = new SubMain();
+ callSubMains();
+ }
+
+ public static void callMains() {
+ // We loop to artificially create branches. The compiler will
+ // not compile this method otherwise.
+ for (int i = 0; i < 2; ++i) {
+ expectEquals(main.method1(), Main.class);
+ expectEquals(main.method2(), Main.class);
+ expectEquals(main.method3(), Main.class);
+ expectEquals(main.method4(), Main.class);
+ expectEquals(main.method5(), Main.class);
+ expectEquals(main.method6(), Main.class);
+ expectEquals(main.method7(), Main.class);
+ expectEquals(main.method8(), Main.class);
+ expectEquals(main.method9(), Main.class);
+ expectEquals(main.method10(), Main.class);
+ expectEquals(main.method11(), Main.class);
+ expectEquals(main.method12(), Main.class);
+ expectEquals(main.method13(), Main.class);
+ expectEquals(main.method14(), Main.class);
+ expectEquals(main.method15(), Main.class);
+ expectEquals(main.method16(), Main.class);
+ expectEquals(main.method17(), Main.class);
+ expectEquals(main.method18(), Main.class);
+ expectEquals(main.method19(), Main.class);
+ expectEquals(main.method20(), Main.class);
+ expectEquals(main.method21(), Main.class);
+ expectEquals(main.method22(), Main.class);
+ expectEquals(main.method23(), Main.class);
+ expectEquals(main.method24(), Main.class);
+ expectEquals(main.method25(), Main.class);
+ expectEquals(main.method26(), Main.class);
+ expectEquals(main.method27(), Main.class);
+ expectEquals(main.method28(), Main.class);
+ expectEquals(main.method29(), Main.class);
+ expectEquals(main.method30(), Main.class);
+ expectEquals(main.method31(), Main.class);
+ expectEquals(main.method32(), Main.class);
+ expectEquals(main.method33(), Main.class);
+ expectEquals(main.method34(), Main.class);
+ expectEquals(main.method35(), Main.class);
+ expectEquals(main.method36(), Main.class);
+ expectEquals(main.method37(), Main.class);
+ expectEquals(main.method38(), Main.class);
+ expectEquals(main.method39(), Main.class);
+ expectEquals(main.method40(), Main.class);
+ expectEquals(main.method41(), Main.class);
+ expectEquals(main.method42(), Main.class);
+ expectEquals(main.method43(), Main.class);
+ expectEquals(main.method44(), Main.class);
+ expectEquals(main.method45(), Main.class);
+ expectEquals(main.method46(), Main.class);
+ expectEquals(main.method47(), Main.class);
+ expectEquals(main.method48(), Main.class);
+ expectEquals(main.method49(), Main.class);
+ expectEquals(main.method50(), Main.class);
+ expectEquals(main.method51(), Main.class);
+ expectEquals(main.method52(), Main.class);
+ expectEquals(main.method53(), Main.class);
+ expectEquals(main.method54(), Main.class);
+ expectEquals(main.method55(), Main.class);
+ expectEquals(main.method56(), Main.class);
+ expectEquals(main.method57(), Main.class);
+ expectEquals(main.method58(), Main.class);
+ expectEquals(main.method59(), Main.class);
+ expectEquals(main.method60(), Main.class);
+ expectEquals(main.method61(), Main.class);
+ expectEquals(main.method62(), Main.class);
+ expectEquals(main.method63(), Main.class);
+ expectEquals(main.method64(), Main.class);
+ expectEquals(main.method65(), Main.class);
+ expectEquals(main.method66(), Main.class);
+ expectEquals(main.method67(), Main.class);
+ expectEquals(main.method68(), Main.class);
+ expectEquals(main.method69(), Main.class);
+ expectEquals(main.method70(), Main.class);
+ expectEquals(main.method71(), Main.class);
+ expectEquals(main.method72(), Main.class);
+ expectEquals(main.method73(), Main.class);
+ expectEquals(main.method74(), Main.class);
+ expectEquals(main.method75(), Main.class);
+ expectEquals(main.method76(), Main.class);
+ expectEquals(main.method77(), Main.class);
+ expectEquals(main.method78(), Main.class);
+ expectEquals(main.method79(), Main.class);
+ }
+ }
+
+ public static void callSubMains() {
+ // We loop to artificially create branches. The compiler will
+ // not compile this method otherwise.
+ for (int i = 0; i < 2; ++i) {
+ expectEquals(main.method1(), SubMain.class);
+ expectEquals(main.method2(), SubMain.class);
+ expectEquals(main.method3(), SubMain.class);
+ expectEquals(main.method4(), SubMain.class);
+ expectEquals(main.method5(), SubMain.class);
+ expectEquals(main.method6(), SubMain.class);
+ expectEquals(main.method7(), SubMain.class);
+ expectEquals(main.method8(), SubMain.class);
+ expectEquals(main.method9(), SubMain.class);
+ expectEquals(main.method10(), SubMain.class);
+ expectEquals(main.method11(), SubMain.class);
+ expectEquals(main.method12(), SubMain.class);
+ expectEquals(main.method13(), SubMain.class);
+ expectEquals(main.method14(), SubMain.class);
+ expectEquals(main.method15(), SubMain.class);
+ expectEquals(main.method16(), SubMain.class);
+ expectEquals(main.method17(), SubMain.class);
+ expectEquals(main.method18(), SubMain.class);
+ expectEquals(main.method19(), SubMain.class);
+ expectEquals(main.method20(), SubMain.class);
+ expectEquals(main.method21(), SubMain.class);
+ expectEquals(main.method22(), SubMain.class);
+ expectEquals(main.method23(), SubMain.class);
+ expectEquals(main.method24(), SubMain.class);
+ expectEquals(main.method25(), SubMain.class);
+ expectEquals(main.method26(), SubMain.class);
+ expectEquals(main.method27(), SubMain.class);
+ expectEquals(main.method28(), SubMain.class);
+ expectEquals(main.method29(), SubMain.class);
+ expectEquals(main.method30(), SubMain.class);
+ expectEquals(main.method31(), SubMain.class);
+ expectEquals(main.method32(), SubMain.class);
+ expectEquals(main.method33(), SubMain.class);
+ expectEquals(main.method34(), SubMain.class);
+ expectEquals(main.method35(), SubMain.class);
+ expectEquals(main.method36(), SubMain.class);
+ expectEquals(main.method37(), SubMain.class);
+ expectEquals(main.method38(), SubMain.class);
+ expectEquals(main.method39(), SubMain.class);
+ expectEquals(main.method40(), SubMain.class);
+ expectEquals(main.method41(), SubMain.class);
+ expectEquals(main.method42(), SubMain.class);
+ expectEquals(main.method43(), SubMain.class);
+ expectEquals(main.method44(), SubMain.class);
+ expectEquals(main.method45(), SubMain.class);
+ expectEquals(main.method46(), SubMain.class);
+ expectEquals(main.method47(), SubMain.class);
+ expectEquals(main.method48(), SubMain.class);
+ expectEquals(main.method49(), SubMain.class);
+ expectEquals(main.method50(), SubMain.class);
+ expectEquals(main.method51(), SubMain.class);
+ expectEquals(main.method52(), SubMain.class);
+ expectEquals(main.method53(), SubMain.class);
+ expectEquals(main.method54(), SubMain.class);
+ expectEquals(main.method55(), SubMain.class);
+ expectEquals(main.method56(), SubMain.class);
+ expectEquals(main.method57(), SubMain.class);
+ expectEquals(main.method58(), SubMain.class);
+ expectEquals(main.method59(), SubMain.class);
+ expectEquals(main.method60(), SubMain.class);
+ expectEquals(main.method61(), SubMain.class);
+ expectEquals(main.method62(), SubMain.class);
+ expectEquals(main.method63(), SubMain.class);
+ expectEquals(main.method64(), SubMain.class);
+ expectEquals(main.method65(), SubMain.class);
+ expectEquals(main.method66(), SubMain.class);
+ expectEquals(main.method67(), SubMain.class);
+ expectEquals(main.method68(), SubMain.class);
+ expectEquals(main.method69(), SubMain.class);
+ expectEquals(main.method70(), SubMain.class);
+ expectEquals(main.method71(), SubMain.class);
+ expectEquals(main.method72(), SubMain.class);
+ expectEquals(main.method73(), SubMain.class);
+ expectEquals(main.method74(), SubMain.class);
+ expectEquals(main.method75(), SubMain.class);
+ expectEquals(main.method76(), SubMain.class);
+ expectEquals(main.method77(), SubMain.class);
+ expectEquals(main.method78(), SubMain.class);
+ expectEquals(main.method79(), SubMain.class);
+ }
+ }
+
+ public static void expectEquals(Object actual, Object expected) {
+ if (!actual.equals(expected)) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public Class<?> method1() { return Main.class; }
+ public Class<?> method2() { return Main.class; }
+ public Class<?> method3() { return Main.class; }
+ public Class<?> method4() { return Main.class; }
+ public Class<?> method5() { return Main.class; }
+ public Class<?> method6() { return Main.class; }
+ public Class<?> method7() { return Main.class; }
+ public Class<?> method8() { return Main.class; }
+ public Class<?> method9() { return Main.class; }
+ public Class<?> method10() { return Main.class; }
+ public Class<?> method11() { return Main.class; }
+ public Class<?> method12() { return Main.class; }
+ public Class<?> method13() { return Main.class; }
+ public Class<?> method14() { return Main.class; }
+ public Class<?> method15() { return Main.class; }
+ public Class<?> method16() { return Main.class; }
+ public Class<?> method17() { return Main.class; }
+ public Class<?> method18() { return Main.class; }
+ public Class<?> method19() { return Main.class; }
+ public Class<?> method20() { return Main.class; }
+ public Class<?> method21() { return Main.class; }
+ public Class<?> method22() { return Main.class; }
+ public Class<?> method23() { return Main.class; }
+ public Class<?> method24() { return Main.class; }
+ public Class<?> method25() { return Main.class; }
+ public Class<?> method26() { return Main.class; }
+ public Class<?> method27() { return Main.class; }
+ public Class<?> method28() { return Main.class; }
+ public Class<?> method29() { return Main.class; }
+ public Class<?> method30() { return Main.class; }
+ public Class<?> method31() { return Main.class; }
+ public Class<?> method32() { return Main.class; }
+ public Class<?> method33() { return Main.class; }
+ public Class<?> method34() { return Main.class; }
+ public Class<?> method35() { return Main.class; }
+ public Class<?> method36() { return Main.class; }
+ public Class<?> method37() { return Main.class; }
+ public Class<?> method38() { return Main.class; }
+ public Class<?> method39() { return Main.class; }
+ public Class<?> method40() { return Main.class; }
+ public Class<?> method41() { return Main.class; }
+ public Class<?> method42() { return Main.class; }
+ public Class<?> method43() { return Main.class; }
+ public Class<?> method44() { return Main.class; }
+ public Class<?> method45() { return Main.class; }
+ public Class<?> method46() { return Main.class; }
+ public Class<?> method47() { return Main.class; }
+ public Class<?> method48() { return Main.class; }
+ public Class<?> method49() { return Main.class; }
+ public Class<?> method50() { return Main.class; }
+ public Class<?> method51() { return Main.class; }
+ public Class<?> method52() { return Main.class; }
+ public Class<?> method53() { return Main.class; }
+ public Class<?> method54() { return Main.class; }
+ public Class<?> method55() { return Main.class; }
+ public Class<?> method56() { return Main.class; }
+ public Class<?> method57() { return Main.class; }
+ public Class<?> method58() { return Main.class; }
+ public Class<?> method59() { return Main.class; }
+ public Class<?> method60() { return Main.class; }
+ public Class<?> method61() { return Main.class; }
+ public Class<?> method62() { return Main.class; }
+ public Class<?> method63() { return Main.class; }
+ public Class<?> method64() { return Main.class; }
+ public Class<?> method65() { return Main.class; }
+ public Class<?> method66() { return Main.class; }
+ public Class<?> method67() { return Main.class; }
+ public Class<?> method68() { return Main.class; }
+ public Class<?> method69() { return Main.class; }
+ public Class<?> method70() { return Main.class; }
+ public Class<?> method71() { return Main.class; }
+ public Class<?> method72() { return Main.class; }
+ public Class<?> method73() { return Main.class; }
+ public Class<?> method74() { return Main.class; }
+ public Class<?> method75() { return Main.class; }
+ public Class<?> method76() { return Main.class; }
+ public Class<?> method77() { return Main.class; }
+ public Class<?> method78() { return Main.class; }
+ public Class<?> method79() { return Main.class; }
+}
+
+class SubMain extends Main {
+ public Class<?> method1() { return SubMain.class; }
+ public Class<?> method2() { return SubMain.class; }
+ public Class<?> method3() { return SubMain.class; }
+ public Class<?> method4() { return SubMain.class; }
+ public Class<?> method5() { return SubMain.class; }
+ public Class<?> method6() { return SubMain.class; }
+ public Class<?> method7() { return SubMain.class; }
+ public Class<?> method8() { return SubMain.class; }
+ public Class<?> method9() { return SubMain.class; }
+ public Class<?> method10() { return SubMain.class; }
+ public Class<?> method11() { return SubMain.class; }
+ public Class<?> method12() { return SubMain.class; }
+ public Class<?> method13() { return SubMain.class; }
+ public Class<?> method14() { return SubMain.class; }
+ public Class<?> method15() { return SubMain.class; }
+ public Class<?> method16() { return SubMain.class; }
+ public Class<?> method17() { return SubMain.class; }
+ public Class<?> method18() { return SubMain.class; }
+ public Class<?> method19() { return SubMain.class; }
+ public Class<?> method20() { return SubMain.class; }
+ public Class<?> method21() { return SubMain.class; }
+ public Class<?> method22() { return SubMain.class; }
+ public Class<?> method23() { return SubMain.class; }
+ public Class<?> method24() { return SubMain.class; }
+ public Class<?> method25() { return SubMain.class; }
+ public Class<?> method26() { return SubMain.class; }
+ public Class<?> method27() { return SubMain.class; }
+ public Class<?> method28() { return SubMain.class; }
+ public Class<?> method29() { return SubMain.class; }
+ public Class<?> method30() { return SubMain.class; }
+ public Class<?> method31() { return SubMain.class; }
+ public Class<?> method32() { return SubMain.class; }
+ public Class<?> method33() { return SubMain.class; }
+ public Class<?> method34() { return SubMain.class; }
+ public Class<?> method35() { return SubMain.class; }
+ public Class<?> method36() { return SubMain.class; }
+ public Class<?> method37() { return SubMain.class; }
+ public Class<?> method38() { return SubMain.class; }
+ public Class<?> method39() { return SubMain.class; }
+ public Class<?> method40() { return SubMain.class; }
+ public Class<?> method41() { return SubMain.class; }
+ public Class<?> method42() { return SubMain.class; }
+ public Class<?> method43() { return SubMain.class; }
+ public Class<?> method44() { return SubMain.class; }
+ public Class<?> method45() { return SubMain.class; }
+ public Class<?> method46() { return SubMain.class; }
+ public Class<?> method47() { return SubMain.class; }
+ public Class<?> method48() { return SubMain.class; }
+ public Class<?> method49() { return SubMain.class; }
+ public Class<?> method50() { return SubMain.class; }
+ public Class<?> method51() { return SubMain.class; }
+ public Class<?> method52() { return SubMain.class; }
+ public Class<?> method53() { return SubMain.class; }
+ public Class<?> method54() { return SubMain.class; }
+ public Class<?> method55() { return SubMain.class; }
+ public Class<?> method56() { return SubMain.class; }
+ public Class<?> method57() { return SubMain.class; }
+ public Class<?> method58() { return SubMain.class; }
+ public Class<?> method59() { return SubMain.class; }
+ public Class<?> method60() { return SubMain.class; }
+ public Class<?> method61() { return SubMain.class; }
+ public Class<?> method62() { return SubMain.class; }
+ public Class<?> method63() { return SubMain.class; }
+ public Class<?> method64() { return SubMain.class; }
+ public Class<?> method65() { return SubMain.class; }
+ public Class<?> method66() { return SubMain.class; }
+ public Class<?> method67() { return SubMain.class; }
+ public Class<?> method68() { return SubMain.class; }
+ public Class<?> method69() { return SubMain.class; }
+ public Class<?> method70() { return SubMain.class; }
+ public Class<?> method71() { return SubMain.class; }
+ public Class<?> method72() { return SubMain.class; }
+ public Class<?> method73() { return SubMain.class; }
+ public Class<?> method74() { return SubMain.class; }
+ public Class<?> method75() { return SubMain.class; }
+ public Class<?> method76() { return SubMain.class; }
+ public Class<?> method77() { return SubMain.class; }
+ public Class<?> method78() { return SubMain.class; }
+ public Class<?> method79() { return SubMain.class; }
+}
diff --git a/test/590-checker-array-set-null-regression/expected.txt b/test/590-checker-array-set-null-regression/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/590-checker-array-set-null-regression/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/590-checker-array-set-null-regression/info.txt b/test/590-checker-array-set-null-regression/info.txt
new file mode 100644
index 0000000..fe173a3
--- /dev/null
+++ b/test/590-checker-array-set-null-regression/info.txt
@@ -0,0 +1,11 @@
+Regression test for art::PrepareForRegisterAllocation, which replaces
+
+ ArraySet[array, index, BoundType[NullConstant]]
+
+with
+
+ ArraySet[array, index, NullConstant]
+
+but used to forget to remove the "need for a type check" bit in the
+ArraySet, thus failing "!may_need_runtime_call_for_type_check"
+assertions in code generators.
diff --git a/test/590-checker-array-set-null-regression/src/Main.java b/test/590-checker-array-set-null-regression/src/Main.java
new file mode 100644
index 0000000..792ee4e
--- /dev/null
+++ b/test/590-checker-array-set-null-regression/src/Main.java
@@ -0,0 +1,68 @@
+/*
+ * 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 {
+
+ public static void main(String args[]) {
+ Element[] elements = new Element[51];
+ testArraySetCheckCastNull(elements);
+
+ System.out.println("passed");
+ }
+
+ /// CHECK-START: void Main.testArraySetCheckCastNull(Main$Element[]) builder (after)
+ /// CHECK: <<Array:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Index:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<Null:l\d+>> NullConstant
+ /// CHECK-DAG: <<Class:l\d+>> LoadClass
+ /// CHECK-DAG: CheckCast [<<Null>>,<<Class>>]
+ /// CHECK-DAG: <<CheckedValue:l\d+>> BoundType [<<Null>>] klass:Main$Element can_be_null:true
+ /// CHECK-DAG: <<CheckedArray:l\d+>> NullCheck [<<Array>>]
+ /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<CheckedArray>>]
+ /// CHECK-DAG: <<CheckedIndex:i\d+>> BoundsCheck [<<Index>>,<<Length>>]
+ /// CHECK-DAG: <<ArraySet:v\d+>> ArraySet [<<CheckedArray>>,<<CheckedIndex>>,<<CheckedValue>>] needs_type_check:true
+
+ /// CHECK-START: void Main.testArraySetCheckCastNull(Main$Element[]) instruction_simplifier (after)
+ /// CHECK-NOT: CheckCast
+
+ /// CHECK-START: void Main.testArraySetCheckCastNull(Main$Element[]) prepare_for_register_allocation (before)
+ /// CHECK: <<Array:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Index:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<Null:l\d+>> NullConstant
+ /// CHECK-DAG: <<Class:l\d+>> LoadClass
+ /// CHECK-DAG: <<CheckedValue:l\d+>> BoundType [<<Null>>]
+ /// CHECK-DAG: <<CheckedArray:l\d+>> NullCheck [<<Array>>]
+ /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<CheckedArray>>]
+ /// CHECK-DAG: <<CheckedIndex:i\d+>> BoundsCheck [<<Index>>,<<Length>>]
+ /// CHECK-DAG: <<ArraySet:v\d+>> ArraySet [<<CheckedArray>>,<<CheckedIndex>>,<<CheckedValue>>] needs_type_check:true
+
+ /// CHECK-START: void Main.testArraySetCheckCastNull(Main$Element[]) prepare_for_register_allocation (after)
+ /// CHECK: <<Array:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Index:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<Null:l\d+>> NullConstant
+ /// CHECK-DAG: <<Class:l\d+>> LoadClass
+ /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<Array>>]
+ /// CHECK-DAG: <<ArraySet:v\d+>> ArraySet [<<Array>>,<<Index>>,<<Null>>] needs_type_check:false
+
+ static void testArraySetCheckCastNull(Element[] elements) {
+ Object object = null;
+ Element element = (Element) object;
+ elements[42] = element;
+ }
+
+ class Element {}
+
+}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 1b00270..11a38cb 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -218,11 +218,9 @@
$(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)
# Disable 137-cfi (b/27391690).
# Disable 577-profile-foreign-dex (b/27454772).
TEST_ART_BROKEN_ALL_TARGET_TESTS := \
- 097-duplicate-method \
577-profile-foreign-dex \
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -295,9 +293,12 @@
TEST_ART_BROKEN_PREBUILD_RUN_TESTS :=
# 554-jit-profile-file is disabled because it needs a primary oat file to know what it should save.
+# 529 and 555: b/27784033
TEST_ART_BROKEN_NO_PREBUILD_TESTS := \
117-nopatchoat \
- 554-jit-profile-file
+ 554-jit-profile-file \
+ 529-checker-unresolved \
+ 555-checker-regression-x86const
ifneq (,$(filter no-prebuild,$(PREBUILD_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),no-prebuild, \