// alias analysis and won't be as effective.
bool has_volatile_; // If there are volatile field accesses.
bool has_monitor_operations_; // If there are monitor operations.
- bool may_deoptimize_;
+ bool may_deoptimize_; // Only true for HDeoptimize with single-frame deoptimization.
DISALLOW_COPY_AND_ASSIGN(HeapLocationCollector);
};
}
// At this point, stores in possibly_removed_stores_ can be safely removed.
- size = possibly_removed_stores_.size();
- for (size_t i = 0; i < size; i++) {
+ for (size_t i = 0, e = possibly_removed_stores_.size(); i < e; i++) {
HInstruction* store = possibly_removed_stores_[i];
DCHECK(store->IsInstanceFieldSet() || store->IsStaticFieldSet() || store->IsArraySet());
store->GetBlock()->RemoveInstruction(store);
}
- // TODO: remove unnecessary allocations.
- // Eliminate instructions in singleton_new_instances_ that:
- // - don't have uses,
- // - don't have finalizers,
- // - are instantiable and accessible,
- // - have no/separate clinit check.
+ // Eliminate allocations that are not used.
+ for (size_t i = 0, e = singleton_new_instances_.size(); i < e; i++) {
+ HInstruction* new_instance = singleton_new_instances_[i];
+ if (!new_instance->HasNonEnvironmentUses()) {
+ new_instance->RemoveEnvironmentUsers();
+ new_instance->GetBlock()->RemoveInstruction(new_instance);
+ }
+ }
}
private:
if (!heap_location_collector_.MayDeoptimize() &&
ref_info->IsSingletonAndNotReturned() &&
!new_instance->IsFinalizable() &&
- !new_instance->CanThrow()) {
- // TODO: add new_instance to singleton_new_instances_ and enable allocation elimination.
+ !new_instance->NeedsAccessCheck()) {
+ singleton_new_instances_.push_back(new_instance);
}
ArenaVector<HInstruction*>& heap_values =
heap_values_for_[new_instance->GetBlock()->GetBlockId()];
uint32_t dex_pc,
uint16_t type_index,
const DexFile& dex_file,
- bool can_throw,
+ bool needs_access_check,
bool finalizable,
QuickEntrypointEnum entrypoint)
: HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc),
type_index_(type_index),
dex_file_(dex_file),
entrypoint_(entrypoint) {
- SetPackedFlag<kFlagCanThrow>(can_throw);
+ SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check);
SetPackedFlag<kFlagFinalizable>(finalizable);
SetRawInputAt(0, cls);
SetRawInputAt(1, current_method);
// Calls runtime so needs an environment.
bool NeedsEnvironment() const OVERRIDE { return true; }
- // It may throw when called on type that's not instantiable/accessible.
- // It can throw OOME.
- // TODO: distinguish between the two cases so we can for example allow allocation elimination.
- bool CanThrow() const OVERRIDE { return GetPackedFlag<kFlagCanThrow>() || true; }
+ // Can throw errors when out-of-memory or if it's not instantiable/accessible.
+ bool CanThrow() const OVERRIDE { return true; }
+
+ // Needs to call into runtime to make sure it's instantiable/accessible.
+ bool NeedsAccessCheck() const { return GetPackedFlag<kFlagNeedsAccessCheck>(); }
bool IsFinalizable() const { return GetPackedFlag<kFlagFinalizable>(); }
DECLARE_INSTRUCTION(NewInstance);
private:
- static constexpr size_t kFlagCanThrow = kNumberOfExpressionPackedBits;
- static constexpr size_t kFlagFinalizable = kFlagCanThrow + 1;
+ static constexpr size_t kFlagNeedsAccessCheck = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFlagFinalizable = kFlagNeedsAccessCheck + 1;
static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1;
static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits,
"Too many packed fields.");
}
}
+interface Filter {
+ public boolean isValid(int i);
+}
+
public class Main {
/// CHECK-START: double Main.calcCircleArea(double) load_store_elimination (before)
/// CHECK: InstanceFieldGet
/// CHECK-START: double Main.calcCircleArea(double) load_store_elimination (after)
- /// CHECK: NewInstance
+ /// CHECK-NOT: NewInstance
/// CHECK-NOT: InstanceFieldSet
/// CHECK-NOT: InstanceFieldGet
}
/// CHECK-START: int Main.test3(TestClass) load_store_elimination (before)
- /// CHECK: NewInstance
/// CHECK: StaticFieldGet
/// CHECK: NewInstance
/// CHECK: InstanceFieldSet
/// CHECK: InstanceFieldGet
/// CHECK-START: int Main.test3(TestClass) load_store_elimination (after)
- /// CHECK: NewInstance
/// CHECK: StaticFieldGet
/// CHECK: NewInstance
/// CHECK: InstanceFieldSet
// A new allocation (even non-singleton) shouldn't alias with pre-existing values.
static int test3(TestClass obj) {
- // Do an allocation here to avoid the HLoadClass and HClinitCheck
- // at the second allocation.
- new TestClass();
TestClass obj1 = TestClass.sTestClassObj;
TestClass obj2 = new TestClass(); // Cannot alias with obj or obj1 which pre-exist.
obj.next = obj2; // Make obj2 a non-singleton.
/// CHECK: InstanceFieldGet
/// CHECK-START: int Main.test8() load_store_elimination (after)
- /// CHECK: NewInstance
+ /// CHECK-NOT: NewInstance
/// CHECK-NOT: InstanceFieldSet
/// CHECK: InvokeVirtual
/// CHECK-NOT: NullCheck
/// CHECK: InstanceFieldGet
/// CHECK-START: int Main.test16() load_store_elimination (after)
- /// CHECK: NewInstance
+ /// CHECK-NOT: NewInstance
/// CHECK-NOT: InstanceFieldSet
/// CHECK-NOT: InstanceFieldGet
/// CHECK-START: int Main.test17() load_store_elimination (after)
/// CHECK: <<Const0:i\d+>> IntConstant 0
- /// CHECK: NewInstance
+ /// CHECK-NOT: NewInstance
/// CHECK-NOT: InstanceFieldSet
/// CHECK-NOT: InstanceFieldGet
/// CHECK: Return [<<Const0>>]
/// CHECK: InstanceFieldGet
/// CHECK-START: int Main.test22() load_store_elimination (after)
- /// CHECK: NewInstance
+ /// CHECK-NOT: NewInstance
/// CHECK-NOT: InstanceFieldSet
- /// CHECK: NewInstance
+ /// CHECK-NOT: NewInstance
/// CHECK-NOT: InstanceFieldSet
/// CHECK-NOT: InstanceFieldGet
- /// CHECK: NewInstance
+ /// CHECK-NOT: NewInstance
/// CHECK-NOT: InstanceFieldSet
/// CHECK-NOT: InstanceFieldGet
/// CHECK-NOT: InstanceFieldGet
/// CHECK: Select
// Test that HSelect creates alias.
- public static int $noinline$testHSelect(boolean b) {
+ static int $noinline$testHSelect(boolean b) {
if (sFlag) {
throw new Error();
}
return obj2.i;
}
- public static void assertIntEquals(int result, int expected) {
+ static int sumWithFilter(int[] array, Filter f) {
+ int sum = 0;
+ for (int i = 0; i < array.length; i++) {
+ if (f.isValid(array[i])) {
+ sum += array[i];
+ }
+ }
+ return sum;
+ }
+
+ /// CHECK-START: int Main.sumWithinRange(int[], int, int) load_store_elimination (before)
+ /// CHECK: NewInstance
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.sumWithinRange(int[], int, int) load_store_elimination (after)
+ /// CHECK-NOT: NewInstance
+ /// CHECK-NOT: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldGet
+
+ // A lambda-style allocation can be eliminated after inlining.
+ static int sumWithinRange(int[] array, final int low, final int high) {
+ Filter filter = new Filter() {
+ public boolean isValid(int i) {
+ return (i >= low) && (i <= high);
+ }
+ };
+ return sumWithFilter(array, filter);
+ }
+
+ static void assertIntEquals(int result, int expected) {
if (expected != result) {
throw new Error("Expected: " + expected + ", found: " + result);
}
}
- public static void assertFloatEquals(float result, float expected) {
+ static void assertFloatEquals(float result, float expected) {
if (expected != result) {
throw new Error("Expected: " + expected + ", found: " + result);
}
}
- public static void assertDoubleEquals(double result, double expected) {
+ static void assertDoubleEquals(double result, double expected) {
if (expected != result) {
throw new Error("Expected: " + expected + ", found: " + result);
}
assertFloatEquals(test24(), 8.0f);
testFinalizableByForcingGc();
assertIntEquals($noinline$testHSelect(true), 0xdead);
+ int[] array = {2, 5, 9, -1, -3, 10, 8, 4};
+ assertIntEquals(sumWithinRange(array, 1, 5), 11);
}
static boolean sFlag;