From 4fdc6d9eff2d4654d13cb24e43f341d393aba1c5 Mon Sep 17 00:00:00 2001 From: Chris Larsen Date: Mon, 14 Dec 2015 13:26:14 -0800 Subject: [PATCH 01/41] MIPS32: sun.misc.Unsafe get/put intrinsics. - int sun.misc.Unsafe.getInt(Object o, long offset) - int sun.misc.Unsafe.getIntVolatile(Object o, long offset) - long sun.misc.Unsafe.getLong(Object o, long offset) - long sun.misc.Unsafe.getLongVolatile(Object o, long offset) - Object sun.misc.Unsafe.getObject(Object o, long offset) - Object sun.misc.Unsafe.getObjectVolatile(Object o, long offset) - void sun.misc.Unsafe.putInt(Object o, long offset, int x) - void sun.misc.Unsafe.putOrderedInt(Object o, long offset, int x) - void sun.misc.Unsafe.putIntVolatile(Object o, long offset, int x) - void sun.misc.Unsafe.putObject(Object o, long offset, Object x) - void sun.misc.Unsafe.putOrderedObject(Object o, long offset, Object x) - void sun.misc.Unsafe.putObjectVolatile(Object o, long offset, Object x) - void sun.misc.Unsafe.putLong(Object o, long offset, long x) - void sun.misc.Unsafe.putOrderedLong(Object o, long offset, long x) - void sun.misc.Unsafe.putLongVolatile(Object o, long offset, long x) Change-Id: I89c07a443ee81a5573a083e871b82f446416b71f --- compiler/optimizing/intrinsics_mips.cc | 322 +++++++++++++++++++++++-- 1 file changed, 306 insertions(+), 16 deletions(-) diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index c306cf93a1..76e6bbd880 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -1475,6 +1475,311 @@ void IntrinsicCodeGeneratorMIPS::VisitThreadCurrentThread(HInvoke* invoke) { Thread::PeerOffset().Int32Value()); } +static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { + bool can_call = + invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject || + invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile; + LocationSummary* locations = new (arena) LocationSummary(invoke, + can_call ? + LocationSummary::kCallOnSlowPath : + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::NoLocation()); // Unused receiver. + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +static void GenUnsafeGet(HInvoke* invoke, + Primitive::Type type, + bool is_volatile, + bool is_R6, + CodeGeneratorMIPS* codegen) { + LocationSummary* locations = invoke->GetLocations(); + DCHECK((type == Primitive::kPrimInt) || + (type == Primitive::kPrimLong) || + (type == Primitive::kPrimNot)) << type; + MipsAssembler* assembler = codegen->GetAssembler(); + // Object pointer. + Register base = locations->InAt(1).AsRegister(); + // The "offset" argument is passed as a "long". Since this code is for + // a 32-bit processor, we can only use 32-bit addresses, so we only + // need the low 32-bits of offset. + Register offset_lo = invoke->GetLocations()->InAt(2).AsRegisterPairLow(); + + __ Addu(TMP, base, offset_lo); + if (is_volatile) { + __ Sync(0); + } + if (type == Primitive::kPrimLong) { + Register trg_lo = locations->Out().AsRegisterPairLow(); + Register trg_hi = locations->Out().AsRegisterPairHigh(); + + if (is_R6) { + __ Lw(trg_lo, TMP, 0); + __ Lw(trg_hi, TMP, 4); + } else { + __ Lwr(trg_lo, TMP, 0); + __ Lwl(trg_lo, TMP, 3); + __ Lwr(trg_hi, TMP, 4); + __ Lwl(trg_hi, TMP, 7); + } + } else { + Register trg = locations->Out().AsRegister(); + + if (is_R6) { + __ Lw(trg, TMP, 0); + } else { + __ Lwr(trg, TMP, 0); + __ Lwl(trg, TMP, 3); + } + } +} + +// int sun.misc.Unsafe.getInt(Object o, long offset) +void IntrinsicLocationsBuilderMIPS::VisitUnsafeGet(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafeGet(HInvoke* invoke) { + GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, IsR6(), codegen_); +} + +// int sun.misc.Unsafe.getIntVolatile(Object o, long offset) +void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetVolatile(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetVolatile(HInvoke* invoke) { + GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, IsR6(), codegen_); +} + +// long sun.misc.Unsafe.getLong(Object o, long offset) +void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetLong(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetLong(HInvoke* invoke) { + GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, IsR6(), codegen_); +} + +// long sun.misc.Unsafe.getLongVolatile(Object o, long offset) +void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetLongVolatile(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetLongVolatile(HInvoke* invoke) { + GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, IsR6(), codegen_); +} + +// Object sun.misc.Unsafe.getObject(Object o, long offset) +void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetObject(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetObject(HInvoke* invoke) { + GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, IsR6(), codegen_); +} + +// Object sun.misc.Unsafe.getObjectVolatile(Object o, long offset) +void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetObjectVolatile(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetObjectVolatile(HInvoke* invoke) { + GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, IsR6(), codegen_); +} + +static void CreateIntIntIntIntToVoidLocations(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()); +} + +static void GenUnsafePut(LocationSummary* locations, + Primitive::Type type, + bool is_volatile, + bool is_ordered, + bool is_R6, + CodeGeneratorMIPS* codegen) { + DCHECK((type == Primitive::kPrimInt) || + (type == Primitive::kPrimLong) || + (type == Primitive::kPrimNot)) << type; + MipsAssembler* assembler = codegen->GetAssembler(); + // Object pointer. + Register base = locations->InAt(1).AsRegister(); + // The "offset" argument is passed as a "long", i.e., it's 64-bits in + // size. Since this code is for a 32-bit processor, we can only use + // 32-bit addresses, so we only need the low 32-bits of offset. + Register offset_lo = locations->InAt(2).AsRegisterPairLow(); + + __ Addu(TMP, base, offset_lo); + if (is_volatile || is_ordered) { + __ Sync(0); + } + if ((type == Primitive::kPrimInt) || (type == Primitive::kPrimNot)) { + Register value = locations->InAt(3).AsRegister(); + + if (is_R6) { + __ Sw(value, TMP, 0); + } else { + __ Swr(value, TMP, 0); + __ Swl(value, TMP, 3); + } + } else { + Register value_lo = locations->InAt(3).AsRegisterPairLow(); + Register value_hi = locations->InAt(3).AsRegisterPairHigh(); + + if (is_R6) { + __ Sw(value_lo, TMP, 0); + __ Sw(value_hi, TMP, 4); + } else { + __ Swr(value_lo, TMP, 0); + __ Swl(value_lo, TMP, 3); + __ Swr(value_hi, TMP, 4); + __ Swl(value_hi, TMP, 7); + } + } + + if (is_volatile) { + __ Sync(0); + } + + if (type == Primitive::kPrimNot) { + codegen->MarkGCCard(base, locations->InAt(3).AsRegister()); + } +} + +// void sun.misc.Unsafe.putInt(Object o, long offset, int x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafePut(HInvoke* invoke) { + CreateIntIntIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafePut(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), + Primitive::kPrimInt, + /* is_volatile */ false, + /* is_ordered */ false, + IsR6(), + codegen_); +} + +// void sun.misc.Unsafe.putOrderedInt(Object o, long offset, int x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafePutOrdered(HInvoke* invoke) { + CreateIntIntIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafePutOrdered(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), + Primitive::kPrimInt, + /* is_volatile */ false, + /* is_ordered */ true, + IsR6(), + codegen_); +} + +// void sun.misc.Unsafe.putIntVolatile(Object o, long offset, int x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafePutVolatile(HInvoke* invoke) { + CreateIntIntIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafePutVolatile(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), + Primitive::kPrimInt, + /* is_volatile */ true, + /* is_ordered */ false, + IsR6(), + codegen_); +} + +// void sun.misc.Unsafe.putObject(Object o, long offset, Object x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafePutObject(HInvoke* invoke) { + CreateIntIntIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafePutObject(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), + Primitive::kPrimNot, + /* is_volatile */ false, + /* is_ordered */ false, + IsR6(), + codegen_); +} + +// void sun.misc.Unsafe.putOrderedObject(Object o, long offset, Object x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafePutObjectOrdered(HInvoke* invoke) { + CreateIntIntIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafePutObjectOrdered(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), + Primitive::kPrimNot, + /* is_volatile */ false, + /* is_ordered */ true, + IsR6(), + codegen_); +} + +// void sun.misc.Unsafe.putObjectVolatile(Object o, long offset, Object x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafePutObjectVolatile(HInvoke* invoke) { + CreateIntIntIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafePutObjectVolatile(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), + Primitive::kPrimNot, + /* is_volatile */ true, + /* is_ordered */ false, + IsR6(), + codegen_); +} + +// void sun.misc.Unsafe.putLong(Object o, long offset, long x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafePutLong(HInvoke* invoke) { + CreateIntIntIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafePutLong(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), + Primitive::kPrimLong, + /* is_volatile */ false, + /* is_ordered */ false, + IsR6(), + codegen_); +} + +// void sun.misc.Unsafe.putOrderedLong(Object o, long offset, long x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafePutLongOrdered(HInvoke* invoke) { + CreateIntIntIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafePutLongOrdered(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), + Primitive::kPrimLong, + /* is_volatile */ false, + /* is_ordered */ true, + IsR6(), + codegen_); +} + +// void sun.misc.Unsafe.putLongVolatile(Object o, long offset, long x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafePutLongVolatile(HInvoke* invoke) { + CreateIntIntIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafePutLongVolatile(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), + Primitive::kPrimLong, + /* is_volatile */ true, + /* is_ordered */ false, + IsR6(), + codegen_); +} + // char java.lang.String.charAt(int index) void IntrinsicLocationsBuilderMIPS::VisitStringCharAt(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, @@ -1482,7 +1787,7 @@ void IntrinsicLocationsBuilderMIPS::VisitStringCharAt(HInvoke* invoke) { kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); - // The inputs will be considered live at the last instruction and restored. This will overwrite + // The inputs will be considered live at the last instruction and restored. This would overwrite // the output with kNoOutputOverlap. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); } @@ -2042,21 +2347,6 @@ UNIMPLEMENTED_INTRINSIC(MIPS, MathFloor) UNIMPLEMENTED_INTRINSIC(MIPS, MathRint) UNIMPLEMENTED_INTRINSIC(MIPS, MathRoundDouble) UNIMPLEMENTED_INTRINSIC(MIPS, MathRoundFloat) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGet) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetVolatile) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetLong) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetLongVolatile) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetObject) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetObjectVolatile) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePut) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutOrdered) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutVolatile) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutObject) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutObjectOrdered) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutObjectVolatile) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutLong) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutLongOrdered) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutLongVolatile) UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASInt) UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASLong) UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASObject) -- GitLab From 9b57966e341cd9937c2ef825986e268fda150374 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Fri, 18 Mar 2016 18:29:58 +0000 Subject: [PATCH 02/41] Revamp run-test 431-optimizing-arith-shifts. Only pass (32-bit) int values as distances. Change-Id: Ie6f0422cfc0ca31f5e6b5797fe4f0bfcea003df4 --- .../431-optimizing-arith-shifts/src/Main.java | 390 +++++++++--------- 1 file changed, 194 insertions(+), 196 deletions(-) diff --git a/test/431-optimizing-arith-shifts/src/Main.java b/test/431-optimizing-arith-shifts/src/Main.java index 86422bd8e7..b7a112f6a3 100644 --- a/test/431-optimizing-arith-shifts/src/Main.java +++ b/test/431-optimizing-arith-shifts/src/Main.java @@ -29,304 +29,302 @@ public class Main { } 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)); - - 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)); + 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$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; } } - -- GitLab From 6f0c6cdae6962532c4699df9dd5af6289b86d1c6 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Fri, 18 Mar 2016 17:17:52 -0700 Subject: [PATCH 03/41] Fix a CC 145-alloc-tracking-stress deadlock. When the allocation tracking gets disabled, there may be threads blocking on the system weak access for recording allocations, but when GC reenables the system weak access, it fails to wake up those blocked threads (which causes a deadlock) because the broadcast call is guarded by Heap::IsAllocTrackingEnabled(), which is false at this point. Broadcast in Heap::BroadcastForNewAllocationRecords() regardless of Heap::IsAllocTrackingEnabled(), which is safe. Also apply a similar fix for the non-CC case. Bug: 27467554 Change-Id: I74cf88bceb306589ce11a19a688be223e099e88a --- runtime/gc/allocation_record.cc | 6 ++++++ runtime/gc/heap.cc | 30 +++++++++++++++--------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc index 73190455cf..e3714bbde6 100644 --- a/runtime/gc/allocation_record.cc +++ b/runtime/gc/allocation_record.cc @@ -288,6 +288,12 @@ void AllocRecordObjectMap::RecordAllocation(Thread* self, mirror::Object* obj, m records->new_record_condition_.WaitHoldingLocks(self); } + if (!heap->IsAllocTrackingEnabled()) { + // Return if the allocation tracking has been disabled while waiting for system weak access + // above. + return; + } + DCHECK_LE(records->Size(), records->alloc_record_max_); // Get stack trace. diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index f4fccee034..4ff0c6bfbd 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -3960,31 +3960,31 @@ void Heap::SweepAllocationRecords(IsMarkedVisitor* visitor) const { void Heap::AllowNewAllocationRecords() const { CHECK(!kUseReadBarrier); - if (IsAllocTrackingEnabled()) { - MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_); - if (IsAllocTrackingEnabled()) { - GetAllocationRecords()->AllowNewAllocationRecords(); - } + MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_); + AllocRecordObjectMap* allocation_records = GetAllocationRecords(); + if (allocation_records != nullptr) { + allocation_records->AllowNewAllocationRecords(); } } void Heap::DisallowNewAllocationRecords() const { CHECK(!kUseReadBarrier); - if (IsAllocTrackingEnabled()) { - MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_); - if (IsAllocTrackingEnabled()) { - GetAllocationRecords()->DisallowNewAllocationRecords(); - } + MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_); + AllocRecordObjectMap* allocation_records = GetAllocationRecords(); + if (allocation_records != nullptr) { + allocation_records->DisallowNewAllocationRecords(); } } void Heap::BroadcastForNewAllocationRecords() const { CHECK(kUseReadBarrier); - if (IsAllocTrackingEnabled()) { - MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_); - if (IsAllocTrackingEnabled()) { - GetAllocationRecords()->BroadcastForNewAllocationRecords(); - } + // Always broadcast without checking IsAllocTrackingEnabled() because IsAllocTrackingEnabled() may + // be set to false while some threads are waiting for system weak access in + // AllocRecordObjectMap::RecordAllocation() and we may fail to wake them up. b/27467554. + MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_); + AllocRecordObjectMap* allocation_records = GetAllocationRecords(); + if (allocation_records != nullptr) { + allocation_records->BroadcastForNewAllocationRecords(); } } -- GitLab From b1d91574f80a1d7944c1d32d6e368a8c7f004b4b Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 18 Mar 2016 16:25:38 +0000 Subject: [PATCH 04/41] Call HuntForOriginalReference to get to the null. The null constant might be hiding under a HBoundType (which we could clean up in instruction simplifier, but that is orthogonal). bug:27683874 Change-Id: Ide8ec5bcd439ec0fca5e54175ebeedc5a9f679a3 --- compiler/optimizing/load_store_elimination.cc | 6 ++-- test/586-checker-null-array-get/src/Main.java | 31 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 561dcfbb1f..9601b066e5 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -738,10 +738,12 @@ class LSEVisitor : public HGraphVisitor { DCHECK(instruction->IsArrayGet()) << instruction->DebugName(); HInstruction* array = instruction->AsArrayGet()->GetArray(); DCHECK(array->IsNullCheck()) << array->DebugName(); - DCHECK(array->InputAt(0)->IsNullConstant()) << array->InputAt(0)->DebugName(); + HInstruction* input = HuntForOriginalReference(array->InputAt(0)); + DCHECK(input->IsNullConstant()) << input->DebugName(); array = heap_value->AsArrayGet()->GetArray(); DCHECK(array->IsNullCheck()) << array->DebugName(); - DCHECK(array->InputAt(0)->IsNullConstant()) << array->InputAt(0)->DebugName(); + input = HuntForOriginalReference(array->InputAt(0)); + DCHECK(input->IsNullConstant()) << input->DebugName(); } return; } diff --git a/test/586-checker-null-array-get/src/Main.java b/test/586-checker-null-array-get/src/Main.java index 4b03ff28eb..332cfb0a53 100644 --- a/test/586-checker-null-array-get/src/Main.java +++ b/test/586-checker-null-array-get/src/Main.java @@ -17,6 +17,7 @@ public class Main { public static Object[] getObjectArray() { return null; } public static long[] getLongArray() { return null; } + public static Object getNull() { return null; } public static void main(String[] args) { try { @@ -37,6 +38,36 @@ public class Main { objectField = getObjectArray()[0]; } + /// CHECK-START: void Main.bar() load_store_elimination (after) + /// CHECK-DAG: <> NullConstant + /// CHECK-DAG: <> BoundType [<>] + /// CHECK-DAG: <> NullCheck [<>] + /// CHECK-DAG: <> ArrayGet [<>,{{i\d+}}] + /// CHECK-DAG: <> ArrayGet [<>,{{i\d+}}] + /// CHECK-DAG: <> ArrayGet [<>,{{i\d+}}] + /// CHECK-DAG: <> ArrayGet [<>,{{i\d+}}] + /// CHECK-DAG: <> NullCheck [<>] + /// CHECK-DAG: <> ArrayGet [<>,{{i\d+}}] + /// CHECK-DAG: <> ArrayGet [<>,{{i\d+}}] + /// CHECK-DAG: <> ArrayGet [<>,{{i\d+}}] + /// CHECK-DAG: <> ArrayGet [<>,{{i\d+}}] + public static void bar() { + // We create multiple accesses that will lead the bounds check + // elimination pass to add a HDeoptimize. Not having the bounds check helped + // the load store elimination think it could merge two ArrayGet with different + // types. + String[] array = ((String[])getNull()); + objectField = array[0]; + objectField = array[1]; + objectField = array[2]; + objectField = array[3]; + long[] longArray = getLongArray(); + longField = longArray[0]; + longField = longArray[1]; + longField = longArray[2]; + longField = longArray[3]; + } + public static long longField; public static Object objectField; } -- GitLab From ab5327d3d57d0d3561e697f196da7f2ee2e50290 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 18 Mar 2016 11:36:20 +0000 Subject: [PATCH 05/41] Check if the type of an instruction is erroneous before inlining. We can get HInstructions typed with a class that is in an error state. For such classes, we cannot look at the vtable or imt table as they are not cleanly populated. bug:27683927 Change-Id: I0d64ca470e1cb6cf9b40e9f02bb9b0bb12c2bac1 --- compiler/optimizing/inliner.cc | 4 ++ test/587-inline-class-error/expected.txt | 0 test/587-inline-class-error/info.txt | 2 + .../smali/SuperVerifyError.smali | 27 ++++++++++++++ .../smali/TestCase.smali | 33 +++++++++++++++++ .../smali/VerifyError.smali | 28 ++++++++++++++ test/587-inline-class-error/src/Main.java | 37 +++++++++++++++++++ 7 files changed, 131 insertions(+) create mode 100644 test/587-inline-class-error/expected.txt create mode 100644 test/587-inline-class-error/info.txt create mode 100644 test/587-inline-class-error/smali/SuperVerifyError.smali create mode 100644 test/587-inline-class-error/smali/TestCase.smali create mode 100644 test/587-inline-class-error/smali/VerifyError.smali create mode 100644 test/587-inline-class-error/src/Main.java diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 573b58340c..440a2821c1 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -144,6 +144,10 @@ static ArtMethod* FindVirtualOrInterfaceTarget(HInvoke* invoke, ArtMethod* resol } else if (!resolved_method->GetDeclaringClass()->IsAssignableFrom(info.GetTypeHandle().Get())) { // The method that we're trying to call is not in the receiver's class or super classes. return nullptr; + } else if (info.GetTypeHandle()->IsErroneous()) { + // If the type is erroneous, do not go further, as we are going to query the vtable or + // imt table, that we can only safely do on non-erroneous classes. + return nullptr; } ClassLinker* cl = Runtime::Current()->GetClassLinker(); diff --git a/test/587-inline-class-error/expected.txt b/test/587-inline-class-error/expected.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/587-inline-class-error/info.txt b/test/587-inline-class-error/info.txt new file mode 100644 index 0000000000..7f244f673c --- /dev/null +++ b/test/587-inline-class-error/info.txt @@ -0,0 +1,2 @@ +Regression test for the inliner that used to crash while +trying to find a method for an erroneous class. diff --git a/test/587-inline-class-error/smali/SuperVerifyError.smali b/test/587-inline-class-error/smali/SuperVerifyError.smali new file mode 100644 index 0000000000..b63cba0a3b --- /dev/null +++ b/test/587-inline-class-error/smali/SuperVerifyError.smali @@ -0,0 +1,27 @@ +# 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 LSuperVerifyError; + +.super Ljava/lang/Object; + +.method public final foo()V + .registers 1 + return-void +.end method + +.method public bar()V + .registers 1 + return-void +.end method diff --git a/test/587-inline-class-error/smali/TestCase.smali b/test/587-inline-class-error/smali/TestCase.smali new file mode 100644 index 0000000000..7c991ed003 --- /dev/null +++ b/test/587-inline-class-error/smali/TestCase.smali @@ -0,0 +1,33 @@ +# 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 LTestCase; + +.super Ljava/lang/Object; + +.method public static topLevel()V + .registers 2 + const v0, 0x1 + new-array v0, v0, [LVerifyError; + invoke-static {v0}, LTestCase;->test([LVerifyError;)V + return-void +.end method + +.method public static test([LVerifyError;)V + .registers 2 + const v0, 0x0 + aget-object v1, v1, v0 + invoke-virtual {v1}, LSuperVerifyError;->bar()V + return-void +.end method diff --git a/test/587-inline-class-error/smali/VerifyError.smali b/test/587-inline-class-error/smali/VerifyError.smali new file mode 100644 index 0000000000..b821b7132e --- /dev/null +++ b/test/587-inline-class-error/smali/VerifyError.smali @@ -0,0 +1,28 @@ +# 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 final LVerifyError; + +.super LSuperVerifyError; + +# Override a final method to put this class in the error state. +.method public foo()V + .registers 1 + return-void +.end method + +# Having a static field in the class is needed to get the +# right initialization for the embedded vtable length of a +# class. +.field public static i:I diff --git a/test/587-inline-class-error/src/Main.java b/test/587-inline-class-error/src/Main.java new file mode 100644 index 0000000000..3402fabb2c --- /dev/null +++ b/test/587-inline-class-error/src/Main.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.InvocationTargetException; + +public class Main { + public static void main(String[] args) throws Exception { + try { + Class v = Class.forName("VerifyError"); + throw new Error("Expected LinkageError"); + } catch (LinkageError e) { + // expected + } + + try { + Class.forName("TestCase").getMethod("topLevel").invoke(null); + throw new Error("Expected InvocationTargetException"); + } catch (InvocationTargetException e) { + if (!(e.getCause() instanceof NullPointerException)) { + throw new Error("Expected NullPointerException, got " + e.getCause()); + } + } + } +} -- GitLab From 48a7bae220c823f03f53a58043845c2e58860b22 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Mon, 21 Mar 2016 14:09:54 +0000 Subject: [PATCH 06/41] Pacify g++ about a set-but-unused variable. This fixes the MIPS32 build. Change-Id: Ic1e6d4627d69a3701c2f3e8fdd784d97a4e41ba5 --- runtime/thread.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/thread.cc b/runtime/thread.cc index 217644417c..2a7cd0787d 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -561,6 +561,7 @@ void Thread::InstallImplicitProtection() { // Read every page from the high address to the low. volatile uint8_t dont_optimize_this; + UNUSED(dont_optimize_this); for (uint8_t* p = stack_top; p >= pregion; p -= kPageSize) { dont_optimize_this = *p; } -- GitLab From 590b1362b64d7feeb688d787c1d140d9b7ca78b1 Mon Sep 17 00:00:00 2001 From: Goran Jakovljevic Date: Mon, 21 Mar 2016 14:24:43 +0100 Subject: [PATCH 07/41] Suppress MIPS32 assembler warnings Suppressing "no .cprestore pseudo-op used in PIC code". Suppressing "used $at without .set noat". Fixing some typos. Change-Id: I45267890b070cee3dd7f3708cf73e157a08f798e --- runtime/arch/mips/jni_entrypoints_mips.S | 3 +- runtime/arch/mips/quick_entrypoints_mips.S | 224 +++++++++++++-------- 2 files changed, 142 insertions(+), 85 deletions(-) diff --git a/runtime/arch/mips/jni_entrypoints_mips.S b/runtime/arch/mips/jni_entrypoints_mips.S index 3558efd41f..5c950717c4 100644 --- a/runtime/arch/mips/jni_entrypoints_mips.S +++ b/runtime/arch/mips/jni_entrypoints_mips.S @@ -38,7 +38,8 @@ ENTRY art_jni_dlsym_lookup_stub .cfi_rel_offset 5, 4 sw $a0, 0($sp) .cfi_rel_offset 4, 0 - jal artFindNativeMethod # (Thread*) + la $t9, artFindNativeMethod + jalr $t9 # (Thread*) move $a0, $s1 # pass Thread::Current() lw $a0, 0($sp) # restore registers from stack lw $a1, 4($sp) diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index dbf0abb558..fd1851f2fc 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -373,7 +373,7 @@ ENTRY art_quick_do_long_jump lw $fp, 120($a0) lw $ra, 124($a0) lw $a0, 16($a0) - move $v0, $zero # clear result registers r0 and r1 + move $v0, $zero # clear result registers v0 and v1 (in branch delay slot) jalr $zero, $t9 # do long jump move $v1, $zero END art_quick_do_long_jump @@ -464,7 +464,8 @@ END art_quick_throw_no_such_method .extern \cxx_name SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME # save callee saves in case allocation triggers GC move $a2, rSELF # pass Thread::Current - jal \cxx_name # (method_idx, this, Thread*, $sp) + la $t9, \cxx_name + jalr $t9 # (method_idx, this, Thread*, $sp) addiu $a3, $sp, ARG_SLOT_SIZE # pass $sp (remove arg slots) move $a0, $v0 # save target Method* RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME @@ -514,6 +515,8 @@ INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvo addiu $\index, 1 .endm +#define SPILL_SIZE 32 + /* * Invocation stub for quick code. * On entry: @@ -526,8 +529,9 @@ INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvo */ ENTRY art_quick_invoke_stub sw $a0, 0($sp) # save out a0 - addiu $sp, $sp, -16 # spill s0, s1, fp, ra - .cfi_adjust_cfa_offset 16 + addiu $sp, $sp, -SPILL_SIZE # spill s0, s1, fp, ra and gp + .cfi_adjust_cfa_offset SPILL_SIZE + sw $gp, 16($sp) sw $ra, 12($sp) .cfi_rel_offset 31, 12 sw $fp, 8($sp) @@ -545,16 +549,18 @@ ENTRY art_quick_invoke_stub srl $t0, $t0, 4 # native calling convention only aligns to 8B, sll $sp, $t0, 4 # so we have to ensure ART 16B alignment ourselves. addiu $a0, $sp, 4 # pass stack pointer + ArtMethod* as dest for memcpy - jal memcpy # (dest, src, bytes) + la $t9, memcpy + jalr $t9 # (dest, src, bytes) addiu $sp, $sp, -16 # make space for argument slots for memcpy addiu $sp, $sp, 16 # restore stack after memcpy - lw $a0, 16($fp) # restore ArtMethod* + lw $gp, 16($fp) # restore $gp + lw $a0, SPILL_SIZE($fp) # restore ArtMethod* lw $a1, 4($sp) # a1 = this* addiu $t0, $sp, 8 # t0 = pointer to the current argument (skip ArtMethod* and this*) li $t3, 2 # t3 = gpr_index = 2 (skip A0 and A1) move $t4, $zero # t4 = fp_index = 0 - lw $t1, 20+16($fp) # get shorty (20 is offset from the $sp on entry + 16 as the $fp is - # 16 bytes below the $sp on entry) + lw $t1, 20 + SPILL_SIZE($fp) # get shorty (20 is offset from the $sp on entry + SPILL_SIZE + # as the $fp is SPILL_SIZE bytes below the $sp on entry) addiu $t1, 1 # t1 = shorty + 1 (skip 1 for return type) loop: lbu $t2, 0($t1) # t2 = shorty[i] @@ -619,8 +625,8 @@ loopEnd: .cfi_restore 30 lw $ra, 12($sp) .cfi_restore 31 - addiu $sp, $sp, 16 - .cfi_adjust_cfa_offset -16 + addiu $sp, $sp, SPILL_SIZE + .cfi_adjust_cfa_offset -SPILL_SIZE lw $t0, 16($sp) # get result pointer lw $t1, 20($sp) # get shorty lb $t1, 0($t1) # get result type char @@ -649,8 +655,9 @@ END art_quick_invoke_stub */ ENTRY art_quick_invoke_static_stub sw $a0, 0($sp) # save out a0 - addiu $sp, $sp, -16 # spill s0, s1, fp, ra - .cfi_adjust_cfa_offset 16 + addiu $sp, $sp, -SPILL_SIZE # spill s0, s1, fp, ra and gp + .cfi_adjust_cfa_offset SPILL_SIZE + sw $gp, 16($sp) sw $ra, 12($sp) .cfi_rel_offset 31, 12 sw $fp, 8($sp) @@ -668,15 +675,17 @@ ENTRY art_quick_invoke_static_stub srl $t0, $t0, 4 # native calling convention only aligns to 8B, sll $sp, $t0, 4 # so we have to ensure ART 16B alignment ourselves. addiu $a0, $sp, 4 # pass stack pointer + ArtMethod* as dest for memcpy - jal memcpy # (dest, src, bytes) + la $t9, memcpy + jalr $t9 # (dest, src, bytes) addiu $sp, $sp, -16 # make space for argument slots for memcpy addiu $sp, $sp, 16 # restore stack after memcpy - lw $a0, 16($fp) # restore ArtMethod* + lw $gp, 16($fp) # restore $gp + lw $a0, SPILL_SIZE($fp) # restore ArtMethod* addiu $t0, $sp, 4 # t0 = pointer to the current argument (skip ArtMethod*) li $t3, 1 # t3 = gpr_index = 1 (skip A0) move $t4, $zero # t4 = fp_index = 0 - lw $t1, 20+16($fp) # get shorty (20 is offset from the $sp on entry + 16 as the $fp is - # 16 bytes below the $sp on entry) + lw $t1, 20 + SPILL_SIZE($fp) # get shorty (20 is offset from the $sp on entry + SPILL_SIZE + # as the $fp is SPILL_SIZE bytes below the $sp on entry) addiu $t1, 1 # t1 = shorty + 1 (skip 1 for return type) loopS: lbu $t2, 0($t1) # t2 = shorty[i] @@ -744,8 +753,8 @@ loopEndS: .cfi_restore 30 lw $ra, 12($sp) .cfi_restore 31 - addiu $sp, $sp, 16 - .cfi_adjust_cfa_offset -16 + addiu $sp, $sp, SPILL_SIZE + .cfi_adjust_cfa_offset -SPILL_SIZE lw $t0, 16($sp) # get result pointer lw $t1, 20($sp) # get shorty lb $t1, 0($t1) # get result type char @@ -762,6 +771,8 @@ loopEndS: nop END art_quick_invoke_static_stub +#undef SPILL_SIZE + /* * Entry from managed code that calls artHandleFillArrayDataFromCode and delivers exception on * failure. @@ -770,7 +781,8 @@ END art_quick_invoke_static_stub ENTRY art_quick_handle_fill_data lw $a2, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case exception allocation triggers GC - jal artHandleFillArrayDataFromCode # (payload offset, Array*, method, Thread*) + la $t9, artHandleFillArrayDataFromCode + jalr $t9 # (payload offset, Array*, method, Thread*) move $a3, rSELF # pass Thread::Current RETURN_IF_ZERO END art_quick_handle_fill_data @@ -783,7 +795,8 @@ ENTRY art_quick_lock_object beqz $a0, .Lart_quick_throw_null_pointer_exception_gp_set nop SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case we block - jal artLockObjectFromCode # (Object* obj, Thread*) + la $t9, artLockObjectFromCode + jalr $t9 # (Object* obj, Thread*) move $a1, rSELF # pass Thread::Current RETURN_IF_ZERO END art_quick_lock_object @@ -796,7 +809,8 @@ ENTRY art_quick_unlock_object beqz $a0, .Lart_quick_throw_null_pointer_exception_gp_set nop SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case exception allocation triggers GC - jal artUnlockObjectFromCode # (Object* obj, Thread*) + la $t9, artUnlockObjectFromCode + jalr $t9 # (Object* obj, Thread*) move $a1, rSELF # pass Thread::Current RETURN_IF_ZERO END art_quick_unlock_object @@ -806,27 +820,30 @@ END art_quick_unlock_object */ .extern artThrowClassCastException ENTRY art_quick_check_cast - addiu $sp, $sp, -16 - .cfi_adjust_cfa_offset 16 + addiu $sp, $sp, -32 + .cfi_adjust_cfa_offset 32 + sw $gp, 16($sp) sw $ra, 12($sp) .cfi_rel_offset 31, 12 sw $t9, 8($sp) sw $a1, 4($sp) sw $a0, 0($sp) - jal artIsAssignableFromCode + la $t9, artIsAssignableFromCode + jalr $t9 addiu $sp, $sp, -16 # reserve argument slots on the stack addiu $sp, $sp, 16 + lw $gp, 16($sp) beqz $v0, .Lthrow_class_cast_exception lw $ra, 12($sp) jalr $zero, $ra - addiu $sp, $sp, 16 - .cfi_adjust_cfa_offset -16 + addiu $sp, $sp, 32 + .cfi_adjust_cfa_offset -32 .Lthrow_class_cast_exception: lw $t9, 8($sp) lw $a1, 4($sp) lw $a0, 0($sp) - addiu $sp, $sp, 16 - .cfi_adjust_cfa_offset -16 + addiu $sp, $sp, 32 + .cfi_adjust_cfa_offset -32 SETUP_SAVE_ALL_CALLEE_SAVE_FRAME la $t9, artThrowClassCastException jalr $zero, $t9 # artThrowClassCastException (Class*, Class*, Thread*) @@ -873,8 +890,9 @@ END art_quick_check_cast .ifnc \rObj, $a1 move $a1, \rObj # pass rObj .endif - addiu $a2, $zero, \offset # pass offset - jal artReadBarrierSlow # artReadBarrierSlow(ref, rObj, offset) + addiu $a2, $zero, \offset # pass offset + la $t9, artReadBarrierSlow + jalr $t9 # artReadBarrierSlow(ref, rObj, offset) addiu $sp, $sp, -16 # Use branch delay slot to reserve argument slots on the stack # before the call to artReadBarrierSlow. addiu $sp, $sp, 16 # restore stack after call to artReadBarrierSlow @@ -958,16 +976,19 @@ ENTRY art_quick_aput_obj .cfi_adjust_cfa_offset 32 sw $ra, 28($sp) .cfi_rel_offset 31, 28 + sw $gp, 16($sp) sw $t9, 12($sp) sw $a2, 8($sp) sw $a1, 4($sp) sw $a0, 0($sp) move $a1, $t1 move $a0, $t0 - jal artIsAssignableFromCode # (Class*, Class*) - addiu $sp, $sp, -16 # reserve argument slots on the stack - addiu $sp, $sp, 16 + la $t9, artIsAssignableFromCode + jalr $t9 # (Class*, Class*) + addiu $sp, $sp, -16 # reserve argument slots on the stack + addiu $sp, $sp, 16 lw $ra, 28($sp) + lw $gp, 16($sp) lw $t9, 12($sp) lw $a2, 8($sp) lw $a1, 4($sp) @@ -990,7 +1011,8 @@ END art_quick_aput_obj ENTRY art_quick_get_boolean_static lw $a1, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artGetBooleanStaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*) + la $t9, artGetBooleanStaticFromCode + jalr $t9 # (uint32_t field_idx, const Method* referrer, Thread*) move $a2, rSELF # pass Thread::Current RETURN_IF_NO_EXCEPTION END art_quick_get_boolean_static @@ -1001,7 +1023,8 @@ END art_quick_get_boolean_static ENTRY art_quick_get_byte_static lw $a1, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artGetByteStaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*) + la $t9, artGetByteStaticFromCode + jalr $t9 # (uint32_t field_idx, const Method* referrer, Thread*) move $a2, rSELF # pass Thread::Current RETURN_IF_NO_EXCEPTION END art_quick_get_byte_static @@ -1013,7 +1036,8 @@ END art_quick_get_byte_static ENTRY art_quick_get_char_static lw $a1, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artGetCharStaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*) + la $t9, artGetCharStaticFromCode + jalr $t9 # (uint32_t field_idx, const Method* referrer, Thread*) move $a2, rSELF # pass Thread::Current RETURN_IF_NO_EXCEPTION END art_quick_get_char_static @@ -1024,7 +1048,8 @@ END art_quick_get_char_static ENTRY art_quick_get_short_static lw $a1, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artGetShortStaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*) + la $t9, artGetShortStaticFromCode + jalr $t9 # (uint32_t field_idx, const Method* referrer, Thread*) move $a2, rSELF # pass Thread::Current RETURN_IF_NO_EXCEPTION END art_quick_get_short_static @@ -1036,7 +1061,8 @@ END art_quick_get_short_static ENTRY art_quick_get32_static lw $a1, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artGet32StaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*) + la $t9, artGet32StaticFromCode + jalr $t9 # (uint32_t field_idx, const Method* referrer, Thread*) move $a2, rSELF # pass Thread::Current RETURN_IF_NO_EXCEPTION END art_quick_get32_static @@ -1048,7 +1074,8 @@ END art_quick_get32_static ENTRY art_quick_get64_static lw $a1, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artGet64StaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*) + la $t9, artGet64StaticFromCode + jalr $t9 # (uint32_t field_idx, const Method* referrer, Thread*) move $a2, rSELF # pass Thread::Current RETURN_IF_NO_EXCEPTION END art_quick_get64_static @@ -1060,7 +1087,8 @@ END art_quick_get64_static ENTRY art_quick_get_obj_static lw $a1, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artGetObjStaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*) + la $t9, artGetObjStaticFromCode + jalr $t9 # (uint32_t field_idx, const Method* referrer, Thread*) move $a2, rSELF # pass Thread::Current RETURN_IF_NO_EXCEPTION END art_quick_get_obj_static @@ -1072,7 +1100,8 @@ END art_quick_get_obj_static ENTRY art_quick_get_boolean_instance lw $a2, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artGetBooleanInstanceFromCode # (field_idx, Object*, referrer, Thread*) + la $t9, artGetBooleanInstanceFromCode + jalr $t9 # (field_idx, Object*, referrer, Thread*) move $a3, rSELF # pass Thread::Current RETURN_IF_NO_EXCEPTION END art_quick_get_boolean_instance @@ -1083,7 +1112,8 @@ END art_quick_get_boolean_instance ENTRY art_quick_get_byte_instance lw $a2, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artGetByteInstanceFromCode # (field_idx, Object*, referrer, Thread*) + la $t9, artGetByteInstanceFromCode + jalr $t9 # (field_idx, Object*, referrer, Thread*) move $a3, rSELF # pass Thread::Current RETURN_IF_NO_EXCEPTION END art_quick_get_byte_instance @@ -1095,7 +1125,8 @@ END art_quick_get_byte_instance ENTRY art_quick_get_char_instance lw $a2, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artGetCharInstanceFromCode # (field_idx, Object*, referrer, Thread*) + la $t9, artGetCharInstanceFromCode + jalr $t9 # (field_idx, Object*, referrer, Thread*) move $a3, rSELF # pass Thread::Current RETURN_IF_NO_EXCEPTION END art_quick_get_char_instance @@ -1106,7 +1137,8 @@ END art_quick_get_char_instance ENTRY art_quick_get_short_instance lw $a2, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artGetShortInstanceFromCode # (field_idx, Object*, referrer, Thread*) + la $t9, artGetShortInstanceFromCode + jalr $t9 # (field_idx, Object*, referrer, Thread*) move $a3, rSELF # pass Thread::Current RETURN_IF_NO_EXCEPTION END art_quick_get_short_instance @@ -1118,7 +1150,8 @@ END art_quick_get_short_instance ENTRY art_quick_get32_instance lw $a2, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artGet32InstanceFromCode # (field_idx, Object*, referrer, Thread*) + la $t9, artGet32InstanceFromCode + jalr $t9 # (field_idx, Object*, referrer, Thread*) move $a3, rSELF # pass Thread::Current RETURN_IF_NO_EXCEPTION END art_quick_get32_instance @@ -1130,7 +1163,8 @@ END art_quick_get32_instance ENTRY art_quick_get64_instance lw $a2, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artGet64InstanceFromCode # (field_idx, Object*, referrer, Thread*) + la $t9, artGet64InstanceFromCode + jalr $t9 # (field_idx, Object*, referrer, Thread*) move $a3, rSELF # pass Thread::Current RETURN_IF_NO_EXCEPTION END art_quick_get64_instance @@ -1142,7 +1176,8 @@ END art_quick_get64_instance ENTRY art_quick_get_obj_instance lw $a2, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artGetObjInstanceFromCode # (field_idx, Object*, referrer, Thread*) + la $t9, artGetObjInstanceFromCode + jalr $t9 # (field_idx, Object*, referrer, Thread*) move $a3, rSELF # pass Thread::Current RETURN_IF_NO_EXCEPTION END art_quick_get_obj_instance @@ -1154,7 +1189,8 @@ END art_quick_get_obj_instance ENTRY art_quick_set8_static lw $a2, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artSet8StaticFromCode # (field_idx, new_val, referrer, Thread*) + la $t9, artSet8StaticFromCode + jalr $t9 # (field_idx, new_val, referrer, Thread*) move $a3, rSELF # pass Thread::Current RETURN_IF_ZERO END art_quick_set8_static @@ -1166,7 +1202,8 @@ END art_quick_set8_static ENTRY art_quick_set16_static lw $a2, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artSet16StaticFromCode # (field_idx, new_val, referrer, Thread*, $sp) + la $t9, artSet16StaticFromCode + jalr $t9 # (field_idx, new_val, referrer, Thread*, $sp) move $a3, rSELF # pass Thread::Current RETURN_IF_ZERO END art_quick_set16_static @@ -1178,7 +1215,8 @@ END art_quick_set16_static ENTRY art_quick_set32_static lw $a2, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artSet32StaticFromCode # (field_idx, new_val, referrer, Thread*) + la $t9, artSet32StaticFromCode + jalr $t9 # (field_idx, new_val, referrer, Thread*) move $a3, rSELF # pass Thread::Current RETURN_IF_ZERO END art_quick_set32_static @@ -1191,7 +1229,8 @@ ENTRY art_quick_set64_static lw $a1, 0($sp) # pass referrer's Method* # 64 bit new_val is in a2:a3 pair SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artSet64StaticFromCode # (field_idx, referrer, new_val, Thread*) + la $t9, artSet64StaticFromCode + jalr $t9 # (field_idx, referrer, new_val, Thread*) sw rSELF, 16($sp) # pass Thread::Current RETURN_IF_ZERO END art_quick_set64_static @@ -1203,8 +1242,9 @@ END art_quick_set64_static ENTRY art_quick_set_obj_static lw $a2, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC + la $t9, artSetObjStaticFromCode + jalr $t9 # (field_idx, new_val, referrer, Thread*) move $a3, rSELF # pass Thread::Current - jal artSetObjStaticFromCode # (field_idx, new_val, referrer, Thread*) RETURN_IF_ZERO END art_quick_set_obj_static @@ -1215,7 +1255,8 @@ END art_quick_set_obj_static ENTRY art_quick_set8_instance lw $a3, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artSet8InstanceFromCode # (field_idx, Object*, new_val, referrer, Thread*) + la $t9, artSet8InstanceFromCode + jalr $t9 # (field_idx, Object*, new_val, referrer, Thread*) sw rSELF, 16($sp) # pass Thread::Current RETURN_IF_ZERO END art_quick_set8_instance @@ -1227,7 +1268,8 @@ END art_quick_set8_instance ENTRY art_quick_set16_instance lw $a3, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artSet16InstanceFromCode # (field_idx, Object*, new_val, referrer, Thread*) + la $t9, artSet16InstanceFromCode + jalr $t9 # (field_idx, Object*, new_val, referrer, Thread*) sw rSELF, 16($sp) # pass Thread::Current RETURN_IF_ZERO END art_quick_set16_instance @@ -1239,7 +1281,8 @@ END art_quick_set16_instance ENTRY art_quick_set32_instance lw $a3, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artSet32InstanceFromCode # (field_idx, Object*, new_val, referrer, Thread*) + la $t9, artSet32InstanceFromCode + jalr $t9 # (field_idx, Object*, new_val, referrer, Thread*) sw rSELF, 16($sp) # pass Thread::Current RETURN_IF_ZERO END art_quick_set32_instance @@ -1253,7 +1296,8 @@ ENTRY art_quick_set64_instance # 64 bit new_val is in a2:a3 pair SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC sw rSELF, 20($sp) # pass Thread::Current - jal artSet64InstanceFromCode # (field_idx, Object*, new_val, referrer, Thread*) + la $t9, artSet64InstanceFromCode + jalr $t9 # (field_idx, Object*, new_val, referrer, Thread*) sw $t1, 16($sp) # pass referrer's Method* RETURN_IF_ZERO END art_quick_set64_instance @@ -1265,7 +1309,8 @@ END art_quick_set64_instance ENTRY art_quick_set_obj_instance lw $a3, 0($sp) # pass referrer's Method* SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal artSetObjInstanceFromCode # (field_idx, Object*, new_val, referrer, Thread*) + la $t9, artSetObjInstanceFromCode + jalr $t9 # (field_idx, Object*, new_val, referrer, Thread*) sw rSELF, 16($sp) # pass Thread::Current RETURN_IF_ZERO END art_quick_set_obj_instance @@ -1275,7 +1320,8 @@ END art_quick_set_obj_instance .extern \entrypoint ENTRY \name SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal \entrypoint + la $t9, \entrypoint + jalr $t9 move $a1, rSELF # pass Thread::Current \return END \name @@ -1285,7 +1331,8 @@ END \name .extern \entrypoint ENTRY \name SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal \entrypoint + la $t9, \entrypoint + jalr $t9 move $a2, rSELF # pass Thread::Current \return END \name @@ -1295,7 +1342,8 @@ END \name .extern \entrypoint ENTRY \name SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal \entrypoint + la $t9, \entrypoint + jalr $t9 move $a3, rSELF # pass Thread::Current \return END \name @@ -1305,7 +1353,8 @@ END \name .extern \entrypoint ENTRY \name SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC - jal \entrypoint + la $t9, \entrypoint + jalr $t9 sw rSELF, 16($sp) # pass Thread::Current \return END \name @@ -1415,7 +1464,8 @@ ENTRY art_quick_alloc_object_rosalloc .Lart_quick_alloc_object_rosalloc_slow_path: SETUP_REFS_ONLY_CALLEE_SAVE_FRAME - jal artAllocObjectFromCodeRosAlloc + la $t9, artAllocObjectFromCodeRosAlloc + jalr $t9 move $a2, $s1 # Pass self as argument. RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER @@ -1461,20 +1511,22 @@ ENTRY art_quick_test_suspend nop 1: SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves for stack crawl - jal artTestSuspendFromCode # (Thread*) + la $t9, artTestSuspendFromCode + jalr $t9 # (Thread*) move $a0, rSELF RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN END art_quick_test_suspend /* * Called by managed code that is attempting to call a method on a proxy class. On entry - * r0 holds the proxy method; r1, r2 and r3 may contain arguments. + * a0 holds the proxy method; a1, a2 and a3 may contain arguments. */ .extern artQuickProxyInvokeHandler ENTRY art_quick_proxy_invoke_handler SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_A0 move $a2, rSELF # pass Thread::Current - jal artQuickProxyInvokeHandler # (Method* proxy method, receiver, Thread*, SP) + la $t9, artQuickProxyInvokeHandler + jalr $t9 # (Method* proxy method, receiver, Thread*, SP) addiu $a3, $sp, ARG_SLOT_SIZE # pass $sp (remove arg slots) lw $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_ RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME @@ -1500,7 +1552,8 @@ END art_quick_imt_conflict_trampoline ENTRY art_quick_resolution_trampoline SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME move $a2, rSELF # pass Thread::Current - jal artQuickResolutionTrampoline # (Method* called, receiver, Thread*, SP) + la $t9, artQuickResolutionTrampoline + jalr $t9 # (Method* called, receiver, Thread*, SP) addiu $a3, $sp, ARG_SLOT_SIZE # pass $sp (remove arg slots) beqz $v0, 1f lw $a0, ARG_SLOT_SIZE($sp) # load resolved method to $a0 @@ -1523,7 +1576,8 @@ ENTRY art_quick_generic_jni_trampoline # prepare for call to artQuickGenericJniTrampoline(Thread*, SP) move $a0, rSELF # pass Thread::Current addiu $a1, $sp, ARG_SLOT_SIZE # save $sp (remove arg slots) - jal artQuickGenericJniTrampoline # (Thread*, SP) + la $t9, artQuickGenericJniTrampoline + jalr $t9 # (Thread*, SP) addiu $sp, $sp, -5120 # reserve space on the stack # The C call will have registered the complete save-frame on success. @@ -1552,7 +1606,8 @@ ENTRY art_quick_generic_jni_trampoline move $a2, $v0 # pass result move $a3, $v1 addiu $sp, $sp, -24 # reserve arg slots - jal artQuickGenericJniEndTrampoline + la $t9, artQuickGenericJniEndTrampoline + jalr $t9 s.d $f0, 16($sp) # pass result_f lw $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_ @@ -1577,7 +1632,8 @@ END art_quick_generic_jni_trampoline ENTRY art_quick_to_interpreter_bridge SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME move $a1, rSELF # pass Thread::Current - jal artQuickToInterpreterBridge # (Method* method, Thread*, SP) + la $t9, artQuickToInterpreterBridge + jalr $t9 # (Method* method, Thread*, SP) addiu $a2, $sp, ARG_SLOT_SIZE # pass $sp (remove arg slots) lw $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_ RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME @@ -1599,7 +1655,8 @@ ENTRY art_quick_instrumentation_entry SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME sw $a0, 28($sp) # save arg0 in free arg slot move $a3, $ra # pass $ra - jal artInstrumentationMethodEntryFromCode # (Method*, Object*, Thread*, LR) + la $t9, artInstrumentationMethodEntryFromCode + jalr $t9 # (Method*, Object*, Thread*, LR) move $a2, rSELF # pass Thread::Current move $t9, $v0 # $t9 holds reference to code lw $a0, 28($sp) # restore arg0 from free arg slot @@ -1627,7 +1684,8 @@ art_quick_instrumentation_exit: move $a2, $v0 # pass gpr result move $a3, $v1 addiu $a1, $sp, ARG_SLOT_SIZE+16 # pass $sp (remove arg slots and temp storage) - jal artInstrumentationMethodExitFromCode # (Thread*, SP, gpr_res, fpr_res) + la $t9, artInstrumentationMethodExitFromCode + jalr $t9 # (Thread*, SP, gpr_res, fpr_res) move $a0, rSELF # pass Thread::Current move $t9, $v0 # set aside returned link register move $ra, $v1 # set link register for deoptimization @@ -1635,7 +1693,7 @@ art_quick_instrumentation_exit: lw $v1, ARG_SLOT_SIZE+8($sp) l.d $f0, ARG_SLOT_SIZE($sp) jalr $zero, $t9 # return - addiu $sp, $sp, ARG_SLOT_SIZE+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE+16 # restore stack + addiu $sp, $sp, ARG_SLOT_SIZE+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE+16 # restore stack .cfi_adjust_cfa_offset -(ARG_SLOT_SIZE+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE+16) END art_quick_instrumentation_exit @@ -1646,7 +1704,8 @@ END art_quick_instrumentation_exit .extern artDeoptimize ENTRY art_quick_deoptimize SETUP_SAVE_ALL_CALLEE_SAVE_FRAME - jal artDeoptimize # artDeoptimize(Thread*) + la $t9, artDeoptimize + jalr $t9 # artDeoptimize(Thread*) # Returns caller method's frame size. move $a0, rSELF # pass Thread::current END art_quick_deoptimize @@ -1658,7 +1717,8 @@ END art_quick_deoptimize .extern artDeoptimizeFromCompiledCode ENTRY art_quick_deoptimize_from_compiled_code SETUP_SAVE_ALL_CALLEE_SAVE_FRAME - jal artDeoptimizeFromCompiledCode # artDeoptimizeFromCompiledCode(Thread*) + la $t9, artDeoptimizeFromCompiledCode + jalr $t9 # artDeoptimizeFromCompiledCode(Thread*) # Returns caller method's frame size. move $a0, rSELF # pass Thread::current END art_quick_deoptimize_from_compiled_code @@ -1725,9 +1785,9 @@ END art_quick_shr_long * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low * 6 bits. * On entry: - * r0: low word - * r1: high word - * r2: shift count + * $a0: low word + * $a1: high word + * $a2: shift count */ /* ushr-long vAA, vBB, vCC */ ENTRY_NO_GP art_quick_ushr_long @@ -1753,11 +1813,11 @@ ENTRY_NO_GP art_quick_indexof /* $a1 holds "ch" */ /* $a2 holds "fromIndex" */ lw $t0, MIRROR_STRING_COUNT_OFFSET($a0) # this.length() - slt $at, $a2, $zero # if fromIndex < 0 + slt $t1, $a2, $zero # if fromIndex < 0 #if defined(_MIPS_ARCH_MIPS32R6) || defined(_MIPS_ARCH_MIPS64R6) - seleqz $a2, $a2, $at # fromIndex = 0; + seleqz $a2, $a2, $t1 # fromIndex = 0; #else - movn $a2, $zero, $at # fromIndex = 0; + movn $a2, $zero, $t1 # fromIndex = 0; #endif subu $t0, $t0, $a2 # this.length() - fromIndex blez $t0, 6f # if this.length()-fromIndex <= 0 @@ -1783,8 +1843,6 @@ ENTRY_NO_GP art_quick_indexof nop END art_quick_indexof - .set push - .set noat /* java.lang.String.compareTo(String anotherString) */ ENTRY_NO_GP art_quick_string_compareto /* $a0 holds address of "this" */ @@ -1816,5 +1874,3 @@ ENTRY_NO_GP art_quick_string_compareto j $ra nop END art_quick_string_compareto - - .set pop -- GitLab From 6300fd77033817664aee3056c4ff5fab85698d2b Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Fri, 18 Mar 2016 09:40:17 +0000 Subject: [PATCH 08/41] Code cleanup between debugger and jit. This is to allow secondary dex files to be fully compiled. - No need to do full deopt anymore for breakpoints: code in boot.oat is deoptimized as soon as the debugger attaches (and we decided that existing frames could not be debugged), and application being debugged is compiled debuggable. - jit should only call UpdateMethodsCode if exit stubs are not installed due to lock violation otherwise (the lock level of the jit code cache is lower than the deoptimization lock). This part needs an overall cleanup beyond the scope of this change. Change-Id: I38d85dcb270db746c1d6b0ceb7893a1aad8c9655 --- runtime/class_linker.cc | 28 -------------------- runtime/class_linker.h | 4 --- runtime/debugger.cc | 48 +++++------------------------------ runtime/jit/jit_code_cache.cc | 3 ++- 4 files changed, 9 insertions(+), 74 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 3c69323b20..cef0d7c577 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -7539,34 +7539,6 @@ const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) { return descriptor; } -bool ClassLinker::MayBeCalledWithDirectCodePointer(ArtMethod* m) { - Runtime* const runtime = Runtime::Current(); - if (runtime->UseJit()) { - // JIT can have direct code pointers from any method to any other method. - return true; - } - // Non-image methods don't use direct code pointer. - if (!m->GetDeclaringClass()->IsBootStrapClassLoaded()) { - return false; - } - if (m->IsPrivate()) { - // The method can only be called inside its own oat file. Therefore it won't be called using - // its direct code if the oat file has been compiled in PIC mode. - const DexFile& dex_file = m->GetDeclaringClass()->GetDexFile(); - const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); - if (oat_dex_file == nullptr) { - // No oat file: the method has not been compiled. - return false; - } - const OatFile* oat_file = oat_dex_file->GetOatFile(); - return oat_file != nullptr && !oat_file->IsPic(); - } else { - // The method can be called outside its own oat file. Therefore it won't be called using its - // direct code pointer only if all loaded oat files have been compiled in PIC mode. - return runtime->GetOatFileManager().HaveNonPicOatFile(); - } -} - jobject ClassLinker::CreatePathClassLoader(Thread* self, std::vector& dex_files) { // SOAAlreadyRunnable is protected, and we need something to add a global reference. // We could move the jobject to the callers, but all call-sites do this... diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 36ed8204a6..c368a3adb3 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -549,10 +549,6 @@ class ClassLinker { REQUIRES(!Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); - // Returns true if the method can be called with its direct code pointer, false otherwise. - bool MayBeCalledWithDirectCodePointer(ArtMethod* m) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); - // Creates a GlobalRef PathClassLoader that can be used to load classes from the given dex files. // Note: the objects are not completely set up. Do not use this outside of tests and the compiler. jobject CreatePathClassLoader(Thread* self, std::vector& dex_files) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index bc6589380c..7870314c6c 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -44,7 +44,6 @@ #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" #include "mirror/throwable.h" -#include "quick/inline_method_analyser.h" #include "reflection.h" #include "safe_map.h" #include "scoped_thread_state_change.h" @@ -53,7 +52,6 @@ #include "handle_scope-inl.h" #include "thread_list.h" #include "utf.h" -#include "verifier/method_verifier-inl.h" #include "well_known_classes.h" namespace art { @@ -3237,27 +3235,6 @@ void Dbg::ManageDeoptimization() { CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable); } -static bool IsMethodPossiblyInlined(Thread* self, ArtMethod* m) - SHARED_REQUIRES(Locks::mutator_lock_) { - const DexFile::CodeItem* code_item = m->GetCodeItem(); - if (code_item == nullptr) { - // TODO We should not be asked to watch location in a native or abstract method so the code item - // should never be null. We could just check we never encounter this case. - return false; - } - // Note: method verifier may cause thread suspension. - self->AssertThreadSuspensionIsAllowable(); - StackHandleScope<2> hs(self); - mirror::Class* declaring_class = m->GetDeclaringClass(); - Handle dex_cache(hs.NewHandle(declaring_class->GetDexCache())); - Handle class_loader(hs.NewHandle(declaring_class->GetClassLoader())); - verifier::MethodVerifier verifier(self, dex_cache->GetDexFile(), dex_cache, class_loader, - &m->GetClassDef(), code_item, m->GetDexMethodIndex(), m, - m->GetAccessFlags(), false, true, false, true); - // Note: we don't need to verify the method. - return InlineMethodAnalyser::AnalyseMethodCode(&verifier, nullptr); -} - static const Breakpoint* FindFirstBreakpointForMethod(ArtMethod* m) SHARED_REQUIRES(Locks::mutator_lock_, Locks::breakpoint_lock_) { for (Breakpoint& breakpoint : gBreakpoints) { @@ -3320,33 +3297,22 @@ static DeoptimizationRequest::Kind GetRequiredDeoptimizationKind(Thread* self, } if (first_breakpoint == nullptr) { - // There is no breakpoint on this method yet: we need to deoptimize. If this method may be - // inlined or default, we deoptimize everything; otherwise we deoptimize only this method. We + // There is no breakpoint on this method yet: we need to deoptimize. If this method is default, + // we deoptimize everything; otherwise we deoptimize only this method. We // deoptimize with defaults because we do not know everywhere they are used. It is possible some - // of the copies could be inlined or otherwise missed. + // of the copies could be missed. // TODO Deoptimizing on default methods might not be necessary in all cases. - // Note: IsMethodPossiblyInlined goes into the method verifier and may cause thread suspension. - // Therefore we must not hold any lock when we call it. - bool need_full_deoptimization = m->IsDefault() || IsMethodPossiblyInlined(self, m); + bool need_full_deoptimization = m->IsDefault(); if (need_full_deoptimization) { - VLOG(jdwp) << "Need full deoptimization because of possible inlining or copying of method " + VLOG(jdwp) << "Need full deoptimization because of copying of method " << PrettyMethod(m); return DeoptimizationRequest::kFullDeoptimization; } else { // We don't need to deoptimize if the method has not been compiled. const bool is_compiled = m->HasAnyCompiledCode(); if (is_compiled) { - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - // If the method may be called through its direct code pointer (without loading - // its updated entrypoint), we need full deoptimization to not miss the breakpoint. - if (class_linker->MayBeCalledWithDirectCodePointer(m)) { - VLOG(jdwp) << "Need full deoptimization because of possible direct code call " - << "into image for compiled method " << PrettyMethod(m); - return DeoptimizationRequest::kFullDeoptimization; - } else { - VLOG(jdwp) << "Need selective deoptimization for compiled method " << PrettyMethod(m); - return DeoptimizationRequest::kSelectiveDeoptimization; - } + VLOG(jdwp) << "Need selective deoptimization for compiled method " << PrettyMethod(m); + return DeoptimizationRequest::kSelectiveDeoptimization; } else { // Method is not compiled: we don't need to deoptimize. VLOG(jdwp) << "No need for deoptimization for non-compiled method " << PrettyMethod(m); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index c681ed77f2..344fcb98ee 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -366,7 +366,8 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, if (osr) { number_of_osr_compilations_++; osr_code_map_.Put(method, code_ptr); - } else { + } else if (!Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) { + // TODO(ngeoffray): Clean up instrumentation and code cache interactions. Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( method, method_header->GetEntryPoint()); } -- GitLab From 4b49567218c41e80c3448bbbcaa7e944f789780e Mon Sep 17 00:00:00 2001 From: Yohann Roussel Date: Mon, 21 Mar 2016 16:26:02 +0100 Subject: [PATCH 09/41] Let tests informa Jack of the min api version This is necessarry for compiling source code with default method. Bug: 27371864 Change-Id: I8c9d3690e403729facd2e868cbda761b3488b2f6 --- test/551-checker-shifter-operand/build | 4 ++-- test/970-iface-super-resolution-generated/build | 2 +- test/etc/default-build | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/551-checker-shifter-operand/build b/test/551-checker-shifter-operand/build index 18e8c59e91..a78021f349 100644 --- a/test/551-checker-shifter-operand/build +++ b/test/551-checker-shifter-operand/build @@ -58,8 +58,8 @@ EXPERIMENTAL="" # Setup experimental flag mappings in a bash associative array. declare -A JACK_EXPERIMENTAL_ARGS -JACK_EXPERIMENTAL_ARGS["default-methods"]="-D jack.java.source.version=1.8" -JACK_EXPERIMENTAL_ARGS["lambdas"]="-D jack.java.source.version=1.8" +JACK_EXPERIMENTAL_ARGS["default-methods"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24" +JACK_EXPERIMENTAL_ARGS["lambdas"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24" while true; do if [ "x$1" = "x--dx-option" ]; then diff --git a/test/970-iface-super-resolution-generated/build b/test/970-iface-super-resolution-generated/build index 2d9830b970..fd1b271c1c 100755 --- a/test/970-iface-super-resolution-generated/build +++ b/test/970-iface-super-resolution-generated/build @@ -31,7 +31,7 @@ USES_JAVA_SOURCE="false" if [[ $@ == *"--jvm"* ]]; then USES_JAVA_SOURCE="true" elif [[ "$USE_JACK" == "true" ]]; then - if $JACK -D jack.java.source.version=1.8 2>/dev/null; then + if $JACK -D jack.java.source.version=1.8 -D jack.android.min-api-level=24 2>/dev/null; then USES_JAVA_SOURCE="true" else echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2 diff --git a/test/etc/default-build b/test/etc/default-build index d048757a97..3d84821bf0 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -66,8 +66,8 @@ EXPERIMENTAL="" # Setup experimental flag mappings in a bash associative array. declare -A JACK_EXPERIMENTAL_ARGS -JACK_EXPERIMENTAL_ARGS["default-methods"]="-D jack.java.source.version=1.8" -JACK_EXPERIMENTAL_ARGS["lambdas"]="-D jack.java.source.version=1.8" +JACK_EXPERIMENTAL_ARGS["default-methods"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24" +JACK_EXPERIMENTAL_ARGS["lambdas"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24" while true; do if [ "x$1" = "x--dx-option" ]; then -- GitLab From f6a35de9eeefb20f6446f1b4815b4dcb0161d09c Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 21 Mar 2016 12:01:50 +0000 Subject: [PATCH 10/41] Optimizing: Fix register allocator validation memory usage. Also attribute ArenaBitVector allocations to appropriate passes. This was used to track down the source of the excessive memory alloactions. Bug: 27690481 Change-Id: Ib895984cb7c04e24cbc7abbd8322079bab8ab100 --- compiler/dex/global_value_numbering_test.cc | 2 +- .../dex/gvn_dead_code_elimination_test.cc | 2 +- compiler/dex/mir_dataflow.cc | 6 +- compiler/dex/mir_graph.cc | 8 +-- compiler/dex/mir_optimization.cc | 10 +-- compiler/dex/ssa_transformation.cc | 13 ++-- compiler/dex/type_inference_test.cc | 2 +- compiler/optimizing/builder.cc | 3 +- compiler/optimizing/code_generator.cc | 3 +- compiler/optimizing/dead_code_elimination.cc | 2 +- compiler/optimizing/graph_checker.cc | 5 +- compiler/optimizing/graph_checker.h | 5 +- compiler/optimizing/gvn.cc | 4 +- compiler/optimizing/licm.cc | 2 +- compiler/optimizing/load_store_elimination.cc | 5 +- compiler/optimizing/locations.cc | 2 +- compiler/optimizing/nodes.cc | 4 +- compiler/optimizing/nodes.h | 2 +- compiler/optimizing/register_allocator.cc | 14 +++- compiler/optimizing/ssa_liveness_analysis.h | 6 +- compiler/optimizing/stack_map_stream.cc | 6 +- runtime/base/arena_allocator.cc | 5 +- runtime/base/arena_allocator.h | 10 ++- runtime/base/arena_bit_vector.cc | 72 +++++++++++++++---- runtime/base/arena_bit_vector.h | 44 +++++------- runtime/base/scoped_arena_allocator.h | 5 ++ 26 files changed, 153 insertions(+), 89 deletions(-) diff --git a/compiler/dex/global_value_numbering_test.cc b/compiler/dex/global_value_numbering_test.cc index f2c2e22d6a..7d647e5c3b 100644 --- a/compiler/dex/global_value_numbering_test.cc +++ b/compiler/dex/global_value_numbering_test.cc @@ -364,7 +364,7 @@ class GlobalValueNumberingTest : public testing::Test { allocator_(), gvn_(), value_names_(), - live_in_v_(new (&cu_.arena) ArenaBitVector(&cu_.arena, kMaxSsaRegs, false, kBitMapMisc)) { + live_in_v_(new (&cu_.arena) ArenaBitVector(&cu_.arena, kMaxSsaRegs, false)) { cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); cu_.access_flags = kAccStatic; // Don't let "this" interfere with this test. allocator_.reset(ScopedArenaAllocator::Create(&cu_.arena_stack)); diff --git a/compiler/dex/gvn_dead_code_elimination_test.cc b/compiler/dex/gvn_dead_code_elimination_test.cc index 28c61a8fca..22fb835b70 100644 --- a/compiler/dex/gvn_dead_code_elimination_test.cc +++ b/compiler/dex/gvn_dead_code_elimination_test.cc @@ -473,7 +473,7 @@ class GvnDeadCodeEliminationTest : public testing::Test { gvn_(), dce_(), value_names_(), - live_in_v_(new (&cu_.arena) ArenaBitVector(&cu_.arena, kMaxSsaRegs, false, kBitMapMisc)) { + live_in_v_(new (&cu_.arena) ArenaBitVector(&cu_.arena, kMaxSsaRegs, false)) { cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); cu_.access_flags = kAccStatic; // Don't let "this" interfere with this test. allocator_.reset(ScopedArenaAllocator::Create(&cu_.arena_stack)); diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc index a7ba061984..f1cc5fc4d2 100644 --- a/compiler/dex/mir_dataflow.cc +++ b/compiler/dex/mir_dataflow.cc @@ -989,11 +989,11 @@ bool MIRGraph::FindLocalLiveIn(BasicBlock* bb) { if (bb->data_flow_info == nullptr) return false; use_v = bb->data_flow_info->use_v = - new (arena_) ArenaBitVector(arena_, GetNumOfCodeAndTempVRs(), false, kBitMapUse); + new (arena_) ArenaBitVector(arena_, GetNumOfCodeAndTempVRs(), false); def_v = bb->data_flow_info->def_v = - new (arena_) ArenaBitVector(arena_, GetNumOfCodeAndTempVRs(), false, kBitMapDef); + new (arena_) ArenaBitVector(arena_, GetNumOfCodeAndTempVRs(), false); live_in_v = bb->data_flow_info->live_in_v = - new (arena_) ArenaBitVector(arena_, GetNumOfCodeAndTempVRs(), false, kBitMapLiveIn); + new (arena_) ArenaBitVector(arena_, GetNumOfCodeAndTempVRs(), false); for (mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { uint64_t df_attributes = GetDataFlowAttributes(mir); diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index b0972d98d4..6dc148dfdb 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -1809,7 +1809,7 @@ void MIRGraph::SSATransformationStart() { temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); temp_.ssa.num_vregs = GetNumOfCodeAndTempVRs(); temp_.ssa.work_live_vregs = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_.ssa.num_vregs, false, kBitMapRegisterV); + temp_scoped_alloc_.get(), temp_.ssa.num_vregs, false); } void MIRGraph::SSATransformationEnd() { @@ -1869,7 +1869,7 @@ static BasicBlock* SelectTopologicalSortOrderFallBack( BasicBlock* fall_back = nullptr; size_t fall_back_num_reachable = 0u; // Reuse the same bit vector for each candidate to mark reachable unvisited blocks. - ArenaBitVector candidate_reachable(allocator, mir_graph->GetNumBlocks(), false, kBitMapMisc); + ArenaBitVector candidate_reachable(allocator, mir_graph->GetNumBlocks(), false); AllNodesIterator iter(mir_graph); for (BasicBlock* candidate = iter.Next(); candidate != nullptr; candidate = iter.Next()) { if (candidate->hidden || // Hidden, or @@ -1944,7 +1944,7 @@ void MIRGraph::ComputeTopologicalSortOrder() { ScopedArenaVector visited_cnt_values(num_blocks, 0u, allocator.Adapter()); ScopedArenaVector loop_head_stack(allocator.Adapter()); size_t max_nested_loops = 0u; - ArenaBitVector loop_exit_blocks(&allocator, num_blocks, false, kBitMapMisc); + ArenaBitVector loop_exit_blocks(&allocator, num_blocks, false); loop_exit_blocks.ClearAllBits(); // Count the number of blocks to process and add the entry block(s). @@ -2051,7 +2051,7 @@ void MIRGraph::ComputeTopologicalSortOrder() { } // Compute blocks from which the loop head is reachable and process those blocks first. ArenaBitVector* reachable = - new (&allocator) ArenaBitVector(&allocator, num_blocks, false, kBitMapMisc); + new (&allocator) ArenaBitVector(&allocator, num_blocks, false); loop_head_reachable_from[bb->id] = reachable; ComputeUnvisitedReachableFrom(this, bb->id, reachable, &tmp_stack); // Now mark as loop head. (Even if it's only a fall back when we don't find a true loop.) diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index 6f9dd6d268..0e74a48aa1 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -927,7 +927,7 @@ bool MIRGraph::EliminateNullChecksGate() { temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); temp_.nce.num_vregs = GetNumOfCodeAndTempVRs(); temp_.nce.work_vregs_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_.nce.num_vregs, false, kBitMapNullCheck); + temp_scoped_alloc_.get(), temp_.nce.num_vregs, false); temp_.nce.ending_vregs_to_check_matrix = temp_scoped_alloc_->AllocArray(GetNumBlocks(), kArenaAllocMisc); std::fill_n(temp_.nce.ending_vregs_to_check_matrix, GetNumBlocks(), nullptr); @@ -1095,7 +1095,7 @@ bool MIRGraph::EliminateNullChecks(BasicBlock* bb) { temp_.nce.ending_vregs_to_check_matrix[bb->id] = vregs_to_check; // Create a new vregs_to_check for next BB. temp_.nce.work_vregs_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_.nce.num_vregs, false, kBitMapNullCheck); + temp_scoped_alloc_.get(), temp_.nce.num_vregs, false); } else if (!vregs_to_check->SameBitsSet(old_ending_ssa_regs_to_check)) { nce_changed = true; temp_.nce.ending_vregs_to_check_matrix[bb->id] = vregs_to_check; @@ -1238,7 +1238,7 @@ bool MIRGraph::EliminateClassInitChecksGate() { // 2 bits for each class: is class initialized, is class in dex cache. temp_.cice.num_class_bits = 2u * unique_class_count; temp_.cice.work_classes_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_.cice.num_class_bits, false, kBitMapClInitCheck); + temp_scoped_alloc_.get(), temp_.cice.num_class_bits, false); temp_.cice.ending_classes_to_check_matrix = temp_scoped_alloc_->AllocArray(GetNumBlocks(), kArenaAllocMisc); std::fill_n(temp_.cice.ending_classes_to_check_matrix, GetNumBlocks(), nullptr); @@ -1335,7 +1335,7 @@ bool MIRGraph::EliminateClassInitChecks(BasicBlock* bb) { temp_.cice.ending_classes_to_check_matrix[bb->id] = classes_to_check; // Create a new classes_to_check for next BB. temp_.cice.work_classes_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_.cice.num_class_bits, false, kBitMapClInitCheck); + temp_scoped_alloc_.get(), temp_.cice.num_class_bits, false); } else if (!classes_to_check->Equal(old_ending_classes_to_check)) { changed = true; temp_.cice.ending_classes_to_check_matrix[bb->id] = classes_to_check; @@ -1517,7 +1517,7 @@ void MIRGraph::InlineSpecialMethodsStart() { temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); temp_.smi.num_indexes = method_lowering_infos_.size(); temp_.smi.processed_indexes = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_.smi.num_indexes, false, kBitMapMisc); + temp_scoped_alloc_.get(), temp_.smi.num_indexes, false); temp_.smi.processed_indexes->ClearAllBits(); temp_.smi.lowering_infos = temp_scoped_alloc_->AllocArray(temp_.smi.num_indexes, kArenaAllocGrowableArray); diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc index 6ed666b9f7..6d5b3510b5 100644 --- a/compiler/dex/ssa_transformation.cc +++ b/compiler/dex/ssa_transformation.cc @@ -144,7 +144,7 @@ void MIRGraph::ComputeDefBlockMatrix() { /* Initialize num_register vectors with num_blocks bits each */ for (i = 0; i < num_registers; i++) { temp_.ssa.def_block_matrix[i] = new (temp_scoped_alloc_.get()) ArenaBitVector( - arena_, GetNumBlocks(), false, kBitMapBMatrix); + arena_, GetNumBlocks(), false); temp_.ssa.def_block_matrix[i]->ClearAllBits(); } @@ -248,12 +248,9 @@ void MIRGraph::InitializeDominationInfo(BasicBlock* bb) { int num_total_blocks = GetBasicBlockListCount(); if (bb->dominators == nullptr) { - bb->dominators = new (arena_) ArenaBitVector(arena_, num_total_blocks, - true /* expandable */, kBitMapDominators); - bb->i_dominated = new (arena_) ArenaBitVector(arena_, num_total_blocks, - true /* expandable */, kBitMapIDominated); - bb->dom_frontier = new (arena_) ArenaBitVector(arena_, num_total_blocks, - true /* expandable */, kBitMapDomFrontier); + bb->dominators = new (arena_) ArenaBitVector(arena_, num_total_blocks, true /* expandable */); + bb->i_dominated = new (arena_) ArenaBitVector(arena_, num_total_blocks, true /* expandable */); + bb->dom_frontier = new (arena_) ArenaBitVector(arena_, num_total_blocks, true /* expandable */); } else { bb->dominators->ClearAllBits(); bb->i_dominated->ClearAllBits(); @@ -471,7 +468,7 @@ void MIRGraph::FindPhiNodeBlocks() { } ArenaBitVector* phi_blocks = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), GetNumBlocks(), false, kBitMapBMatrix); + temp_scoped_alloc_.get(), GetNumBlocks(), false); // Reuse the def_block_matrix storage for phi_node_blocks. ArenaBitVector** def_block_matrix = temp_.ssa.def_block_matrix; diff --git a/compiler/dex/type_inference_test.cc b/compiler/dex/type_inference_test.cc index e2c0d32f97..ef5365106d 100644 --- a/compiler/dex/type_inference_test.cc +++ b/compiler/dex/type_inference_test.cc @@ -494,7 +494,7 @@ class TypeInferenceTest : public testing::Test { code_item_(nullptr), ssa_reps_(), allocator_(), - live_in_v_(new (&cu_.arena) ArenaBitVector(&cu_.arena, kMaxSsaRegs, false, kBitMapMisc)), + live_in_v_(new (&cu_.arena) ArenaBitVector(&cu_.arena, kMaxSsaRegs, false)), type_defs_(nullptr), type_count_(0u), ifield_defs_(nullptr), diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 57660c2623..124afbc73b 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -367,7 +367,8 @@ GraphAnalysisResult HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item ArenaBitVector* native_debug_info_locations; if (native_debuggable) { const uint32_t num_instructions = code_item.insns_size_in_code_units_; - native_debug_info_locations = new (arena_) ArenaBitVector (arena_, num_instructions, false); + native_debug_info_locations = + ArenaBitVector::Create(arena_, num_instructions, false, kArenaAllocGraphBuilder); FindNativeDebugInfoLocations(code_item, native_debug_info_locations); } diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index d64a786a9e..32869ec0b4 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -856,7 +856,8 @@ void CodeGenerator::RecordCatchBlockInfo() { uint32_t register_mask = 0; // Not used. // The stack mask is not used, so we leave it empty. - ArenaBitVector* stack_mask = new (arena) ArenaBitVector(arena, 0, /* expandable */ true); + ArenaBitVector* stack_mask = + ArenaBitVector::Create(arena, 0, /* expandable */ true, kArenaAllocCodeGenerator); stack_map_stream_.BeginStackMapEntry(dex_pc, native_pc, diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc index e170e37bdd..d7bf16e0cc 100644 --- a/compiler/optimizing/dead_code_elimination.cc +++ b/compiler/optimizing/dead_code_elimination.cc @@ -97,7 +97,7 @@ void HDeadCodeElimination::RemoveDeadBlocks() { } // Classify blocks as reachable/unreachable. ArenaAllocator* allocator = graph_->GetArena(); - ArenaBitVector live_blocks(allocator, graph_->GetBlocks().size(), false); + ArenaBitVector live_blocks(allocator, graph_->GetBlocks().size(), false, kArenaAllocDCE); MarkReachableBlocks(graph_, &live_blocks); bool removed_one_or_more_blocks = false; diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index 11e3689a82..9491ef6119 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -812,7 +812,10 @@ void GraphChecker::VisitPhi(HPhi* phi) { phi->GetRegNumber(), type_str.str().c_str())); } else { - ArenaBitVector visited(GetGraph()->GetArena(), 0, /* expandable */ true); + ArenaBitVector visited(GetGraph()->GetArena(), + 0, + /* expandable */ true, + kArenaAllocGraphChecker); if (!IsConstantEquivalent(phi, other_phi, &visited)) { AddError(StringPrintf("Two phis (%d and %d) found for VReg %d but they " "are not equivalents of constants.", diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h index 52252cd3d4..8da8457859 100644 --- a/compiler/optimizing/graph_checker.h +++ b/compiler/optimizing/graph_checker.h @@ -30,7 +30,10 @@ class GraphChecker : public HGraphDelegateVisitor { : HGraphDelegateVisitor(graph), errors_(graph->GetArena()->Adapter(kArenaAllocGraphChecker)), dump_prefix_(dump_prefix), - seen_ids_(graph->GetArena(), graph->GetCurrentInstructionId(), false) {} + seen_ids_(graph->GetArena(), + graph->GetCurrentInstructionId(), + false, + kArenaAllocGraphChecker) {} // Check the whole graph (in reverse post-order). void Run() { diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc index b4922789d4..f7eb2adc6c 100644 --- a/compiler/optimizing/gvn.cc +++ b/compiler/optimizing/gvn.cc @@ -40,7 +40,7 @@ class ValueSet : public ArenaObject { : allocator_(allocator), num_buckets_(kMinimumNumberOfBuckets), buckets_(allocator->AllocArray(num_buckets_, kArenaAllocGvn)), - buckets_owned_(allocator, num_buckets_, false), + buckets_owned_(allocator, num_buckets_, false, kArenaAllocGvn), num_entries_(0) { // ArenaAllocator returns zeroed memory, so no need to set buckets to null. DCHECK(IsPowerOfTwo(num_buckets_)); @@ -53,7 +53,7 @@ class ValueSet : public ArenaObject { : allocator_(allocator), num_buckets_(to_copy.IdealBucketCount()), buckets_(allocator->AllocArray(num_buckets_, kArenaAllocGvn)), - buckets_owned_(allocator, num_buckets_, false), + buckets_owned_(allocator, num_buckets_, false, kArenaAllocGvn), num_entries_(to_copy.num_entries_) { // ArenaAllocator returns zeroed memory, so entries of buckets_ and // buckets_owned_ are initialized to null and false, respectively. diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc index 33bb2e8f30..7a1e06b951 100644 --- a/compiler/optimizing/licm.cc +++ b/compiler/optimizing/licm.cc @@ -80,7 +80,7 @@ static void UpdateLoopPhisIn(HEnvironment* environment, HLoopInformation* info) void LICM::Run() { DCHECK(side_effects_.HasRun()); // Only used during debug. - ArenaBitVector visited(graph_->GetArena(), graph_->GetBlocks().size(), false); + ArenaBitVector visited(graph_->GetArena(), graph_->GetBlocks().size(), false, kArenaAllocLICM); // Post order visit to visit inner loops before outer loops. for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) { diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 9601b066e5..e1977b1798 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -186,7 +186,10 @@ class HeapLocationCollector : public HGraphVisitor { : HGraphVisitor(graph), ref_info_array_(graph->GetArena()->Adapter(kArenaAllocLSE)), heap_locations_(graph->GetArena()->Adapter(kArenaAllocLSE)), - aliasing_matrix_(graph->GetArena(), kInitialAliasingMatrixBitVectorSize, true), + aliasing_matrix_(graph->GetArena(), + kInitialAliasingMatrixBitVectorSize, + true, + kArenaAllocLSE), has_heap_stores_(false), has_volatile_(false), has_monitor_operations_(false), diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc index 1ab206f69e..83596da41a 100644 --- a/compiler/optimizing/locations.cc +++ b/compiler/optimizing/locations.cc @@ -37,7 +37,7 @@ LocationSummary::LocationSummary(HInstruction* instruction, if (NeedsSafepoint()) { ArenaAllocator* arena = instruction->GetBlock()->GetGraph()->GetArena(); - stack_mask_ = new (arena) ArenaBitVector(arena, 0, true); + stack_mask_ = ArenaBitVector::Create(arena, 0, true, kArenaAllocLocationSummary); } } diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 98766a31a6..c83340b1f6 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -54,7 +54,7 @@ void HGraph::FindBackEdges(ArenaBitVector* visited) { DCHECK_EQ(visited->GetHighestBitSet(), -1); // Nodes that we're currently visiting, indexed by block id. - ArenaBitVector visiting(arena_, blocks_.size(), false); + ArenaBitVector visiting(arena_, blocks_.size(), false, kArenaAllocGraphBuilder); // Number of successors visited from a given node, indexed by block id. ArenaVector successors_visited(blocks_.size(), 0u, arena_->Adapter()); // Stack of nodes that we're currently visiting (same as marked in "visiting" above). @@ -140,7 +140,7 @@ GraphAnalysisResult HGraph::BuildDominatorTree() { // collect both normal- and exceptional-flow values at the same time. SimplifyCatchBlocks(); - ArenaBitVector visited(arena_, blocks_.size(), false); + ArenaBitVector visited(arena_, blocks_.size(), false, kArenaAllocGraphBuilder); // (2) Find the back edges in the graph doing a DFS traversal. FindBackEdges(&visited); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 46377ee503..673631958a 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -645,7 +645,7 @@ class HLoopInformation : public ArenaObject { irreducible_(false), back_edges_(graph->GetArena()->Adapter(kArenaAllocLoopInfoBackEdges)), // Make bit vector growable, as the number of blocks may change. - blocks_(graph->GetArena(), graph->GetBlocks().size(), true) { + blocks_(graph->GetArena(), graph->GetBlocks().size(), true, kArenaAllocLoopInfoBackEdges) { back_edges_.reserve(kDefaultNumberOfBackEdges); } diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index b8d76b912e..34d9af1e74 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -445,7 +445,7 @@ class AllRangesIterator : public ValueObject { bool RegisterAllocator::ValidateInternal(bool log_fatal_on_failure) const { // To simplify unit testing, we eagerly create the array of intervals, and // call the helper method. - ArenaVector intervals(allocator_->Adapter(kArenaAllocRegisterAllocator)); + ArenaVector intervals(allocator_->Adapter(kArenaAllocRegisterAllocatorValidate)); for (size_t i = 0; i < liveness_.GetNumberOfSsaValues(); ++i) { HInstruction* instruction = liveness_.GetInstructionFromSsaIndex(i); if (ShouldProcess(processing_core_registers_, instruction->GetLiveInterval())) { @@ -483,13 +483,21 @@ bool RegisterAllocator::ValidateIntervals(const ArenaVector& inte ? codegen.GetNumberOfCoreRegisters() : codegen.GetNumberOfFloatingPointRegisters(); ArenaVector liveness_of_values( - allocator->Adapter(kArenaAllocRegisterAllocator)); + allocator->Adapter(kArenaAllocRegisterAllocatorValidate)); liveness_of_values.reserve(number_of_registers + number_of_spill_slots); + size_t max_end = 0u; + for (LiveInterval* start_interval : intervals) { + for (AllRangesIterator it(start_interval); !it.Done(); it.Advance()) { + max_end = std::max(max_end, it.CurrentRange()->GetEnd()); + } + } + // Allocate a bit vector per register. A live interval that has a register // allocated will populate the associated bit vector based on its live ranges. for (size_t i = 0; i < number_of_registers + number_of_spill_slots; ++i) { - liveness_of_values.push_back(new (allocator) ArenaBitVector(allocator, 0, true)); + liveness_of_values.push_back( + ArenaBitVector::Create(allocator, max_end, false, kArenaAllocRegisterAllocatorValidate)); } for (LiveInterval* start_interval : intervals) { diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index a78aedcff5..97f2aeeb1e 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -31,9 +31,9 @@ class BlockInfo : public ArenaObject { public: BlockInfo(ArenaAllocator* allocator, const HBasicBlock& block, size_t number_of_ssa_values) : block_(block), - live_in_(allocator, number_of_ssa_values, false), - live_out_(allocator, number_of_ssa_values, false), - kill_(allocator, number_of_ssa_values, false) { + live_in_(allocator, number_of_ssa_values, false, kArenaAllocSsaLiveness), + live_out_(allocator, number_of_ssa_values, false, kArenaAllocSsaLiveness), + kill_(allocator, number_of_ssa_values, false, kArenaAllocSsaLiveness) { UNUSED(block_); live_in_.ClearAllBits(); live_out_.ClearAllBits(); diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 54cbdf8b66..3f41e3594e 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -37,7 +37,7 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, current_entry_.same_dex_register_map_as_ = kNoSameDexMapFound; if (num_dex_registers != 0) { current_entry_.live_dex_registers_mask = - new (allocator_) ArenaBitVector(allocator_, num_dex_registers, true); + ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream); } else { current_entry_.live_dex_registers_mask = nullptr; } @@ -111,7 +111,7 @@ void StackMapStream::BeginInlineInfoEntry(uint32_t method_index, current_inline_info_.dex_register_locations_start_index = dex_register_locations_.size(); if (num_dex_registers != 0) { current_inline_info_.live_dex_registers_mask = - new (allocator_) ArenaBitVector(allocator_, num_dex_registers, true); + ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream); } else { current_inline_info_.live_dex_registers_mask = nullptr; } @@ -256,7 +256,7 @@ void StackMapStream::FillIn(MemoryRegion region) { // Ensure we reached the end of the Dex registers location_catalog. DCHECK_EQ(location_catalog_offset, dex_register_location_catalog_region.size()); - ArenaBitVector empty_bitmask(allocator_, 0, /* expandable */ false); + ArenaBitVector empty_bitmask(allocator_, 0, /* expandable */ false, kArenaAllocStackMapStream); uintptr_t next_dex_register_map_offset = 0; uintptr_t next_inline_info_offset = 0; for (size_t i = 0, e = stack_maps_.size(); i < e; ++i) { diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc index f871543862..753994392e 100644 --- a/runtime/base/arena_allocator.cc +++ b/runtime/base/arena_allocator.cc @@ -85,17 +85,20 @@ const char* const ArenaAllocatorStatsImpl::kAllocNames[] = { "GVN ", "InductionVar ", "BCE ", + "DCE ", + "LSE ", + "LICM ", "SsaLiveness ", "SsaPhiElim ", "RefTypeProp ", "PrimTypeProp ", "SideEffects ", "RegAllocator ", + "RegAllocVldt ", "StackMapStm ", "CodeGen ", "ParallelMove ", "GraphChecker ", - "LSE ", "Verifier ", }; diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h index 728f897229..f8f7396ed5 100644 --- a/runtime/base/arena_allocator.h +++ b/runtime/base/arena_allocator.h @@ -96,17 +96,20 @@ enum ArenaAllocKind { kArenaAllocGvn, kArenaAllocInductionVarAnalysis, kArenaAllocBoundsCheckElimination, + kArenaAllocDCE, + kArenaAllocLSE, + kArenaAllocLICM, kArenaAllocSsaLiveness, kArenaAllocSsaPhiElimination, kArenaAllocReferenceTypePropagation, kArenaAllocPrimitiveTypePropagation, kArenaAllocSideEffectsAnalysis, kArenaAllocRegisterAllocator, + kArenaAllocRegisterAllocatorValidate, kArenaAllocStackMapStream, kArenaAllocCodeGenerator, kArenaAllocParallelMoveResolver, kArenaAllocGraphChecker, - kArenaAllocLSE, kArenaAllocVerifier, kNumArenaAllocKinds }; @@ -355,6 +358,11 @@ class ArenaAllocator return new_ptr; } + template + T* Alloc(ArenaAllocKind kind = kArenaAllocMisc) { + return AllocArray(1, kind); + } + template T* AllocArray(size_t length, ArenaAllocKind kind = kArenaAllocMisc) { return static_cast(Alloc(length * sizeof(T), kind)); diff --git a/runtime/base/arena_bit_vector.cc b/runtime/base/arena_bit_vector.cc index fbbfd84fcf..5f8f5d2275 100644 --- a/runtime/base/arena_bit_vector.cc +++ b/runtime/base/arena_bit_vector.cc @@ -21,36 +21,78 @@ namespace art { +template +class ArenaBitVectorAllocatorKindImpl; + +template <> +class ArenaBitVectorAllocatorKindImpl { + public: + // Not tracking allocations, ignore the supplied kind and arbitrarily provide kArenaAllocSTL. + explicit ArenaBitVectorAllocatorKindImpl(ArenaAllocKind kind ATTRIBUTE_UNUSED) {} + ArenaBitVectorAllocatorKindImpl(const ArenaBitVectorAllocatorKindImpl&) = default; + ArenaBitVectorAllocatorKindImpl& operator=(const ArenaBitVectorAllocatorKindImpl&) = default; + ArenaAllocKind Kind() { return kArenaAllocGrowableBitMap; } +}; + +template +class ArenaBitVectorAllocatorKindImpl { + public: + explicit ArenaBitVectorAllocatorKindImpl(ArenaAllocKind kind) : kind_(kind) { } + ArenaBitVectorAllocatorKindImpl(const ArenaBitVectorAllocatorKindImpl&) = default; + ArenaBitVectorAllocatorKindImpl& operator=(const ArenaBitVectorAllocatorKindImpl&) = default; + ArenaAllocKind Kind() { return kind_; } + + private: + ArenaAllocKind kind_; +}; + +using ArenaBitVectorAllocatorKind = + ArenaBitVectorAllocatorKindImpl; + template -class ArenaBitVectorAllocator FINAL : public Allocator, - public ArenaObject { +class ArenaBitVectorAllocator FINAL : public Allocator, private ArenaBitVectorAllocatorKind { public: - explicit ArenaBitVectorAllocator(ArenaAlloc* arena) : arena_(arena) {} - ~ArenaBitVectorAllocator() {} + static ArenaBitVectorAllocator* Create(ArenaAlloc* arena, ArenaAllocKind kind) { + void* storage = arena->template Alloc(kind); + return new (storage) ArenaBitVectorAllocator(arena, kind); + } + + ~ArenaBitVectorAllocator() { + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); + } virtual void* Alloc(size_t size) { - return arena_->Alloc(size, kArenaAllocGrowableBitMap); + return arena_->Alloc(size, this->Kind()); } virtual void Free(void*) {} // Nop. private: + ArenaBitVectorAllocator(ArenaAlloc* arena, ArenaAllocKind kind) + : ArenaBitVectorAllocatorKind(kind), arena_(arena) { } + ArenaAlloc* const arena_; + DISALLOW_COPY_AND_ASSIGN(ArenaBitVectorAllocator); }; -ArenaBitVector::ArenaBitVector(ArenaAllocator* arena, unsigned int start_bits, - bool expandable, OatBitMapKind kind) - : BitVector(start_bits, expandable, - new (arena) ArenaBitVectorAllocator(arena)), kind_(kind) { - UNUSED(kind_); +ArenaBitVector::ArenaBitVector(ArenaAllocator* arena, + unsigned int start_bits, + bool expandable, + ArenaAllocKind kind) + : BitVector(start_bits, + expandable, + ArenaBitVectorAllocator::Create(arena, kind)) { } -ArenaBitVector::ArenaBitVector(ScopedArenaAllocator* arena, unsigned int start_bits, - bool expandable, OatBitMapKind kind) - : BitVector(start_bits, expandable, - new (arena) ArenaBitVectorAllocator(arena)), kind_(kind) { - UNUSED(kind_); +ArenaBitVector::ArenaBitVector(ScopedArenaAllocator* arena, + unsigned int start_bits, + bool expandable, + ArenaAllocKind kind) + : BitVector(start_bits, + expandable, + ArenaBitVectorAllocator::Create(arena, kind)) { } } // namespace art diff --git a/runtime/base/arena_bit_vector.h b/runtime/base/arena_bit_vector.h index d6061662c2..d86d622d38 100644 --- a/runtime/base/arena_bit_vector.h +++ b/runtime/base/arena_bit_vector.h @@ -25,44 +25,34 @@ namespace art { class ArenaAllocator; class ScopedArenaAllocator; -// Type of growable bitmap for memory tuning. -enum OatBitMapKind { - kBitMapMisc = 0, - kBitMapUse, - kBitMapDef, - kBitMapLiveIn, - kBitMapBMatrix, - kBitMapDominators, - kBitMapIDominated, - kBitMapDomFrontier, - kBitMapRegisterV, - kBitMapTempSSARegisterV, - kBitMapNullCheck, - kBitMapClInitCheck, - kBitMapPredecessors, - kNumBitMapKinds -}; - -std::ostream& operator<<(std::ostream& os, const OatBitMapKind& kind); - /* * A BitVector implementation that uses Arena allocation. */ class ArenaBitVector : public BitVector, public ArenaObject { public: - ArenaBitVector(ArenaAllocator* arena, uint32_t start_bits, bool expandable, - OatBitMapKind kind = kBitMapMisc); - ArenaBitVector(ScopedArenaAllocator* arena, uint32_t start_bits, bool expandable, - OatBitMapKind kind = kBitMapMisc); + template + static ArenaBitVector* Create(Allocator* arena, + uint32_t start_bits, + bool expandable, + ArenaAllocKind kind = kArenaAllocGrowableBitMap) { + void* storage = arena->template Alloc(kind); + return new (storage) ArenaBitVector(arena, start_bits, expandable, kind); + } + + ArenaBitVector(ArenaAllocator* arena, + uint32_t start_bits, + bool expandable, + ArenaAllocKind kind = kArenaAllocGrowableBitMap); + ArenaBitVector(ScopedArenaAllocator* arena, + uint32_t start_bits, + bool expandable, + ArenaAllocKind kind = kArenaAllocGrowableBitMap); ~ArenaBitVector() {} private: - const OatBitMapKind kind_; // for memory use tuning. TODO: currently unused. - DISALLOW_COPY_AND_ASSIGN(ArenaBitVector); }; - } // namespace art #endif // ART_RUNTIME_BASE_ARENA_BIT_VECTOR_H_ diff --git a/runtime/base/scoped_arena_allocator.h b/runtime/base/scoped_arena_allocator.h index a87153bd77..55044b34e5 100644 --- a/runtime/base/scoped_arena_allocator.h +++ b/runtime/base/scoped_arena_allocator.h @@ -151,6 +151,11 @@ class ScopedArenaAllocator return arena_stack_->Alloc(bytes, kind); } + template + T* Alloc(ArenaAllocKind kind = kArenaAllocMisc) { + return AllocArray(1, kind); + } + template T* AllocArray(size_t length, ArenaAllocKind kind = kArenaAllocMisc) { return static_cast(Alloc(length * sizeof(T), kind)); -- GitLab From 3c94f0945ed596ceee39783fa075f013b65e80a1 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Mon, 21 Mar 2016 17:10:24 +0000 Subject: [PATCH 11/41] Remove Quick from tree. So long, old friend. Change-Id: I0241c798a34b92bf994fed83888da67d6e7f1891 --- build/Android.gtest.mk | 8 - compiler/Android.mk | 64 +- compiler/common_compiler_test.cc | 1 - compiler/compiler.cc | 4 +- compiler/dex/bb_optimizations.cc | 82 - compiler/dex/bb_optimizations.h | 452 --- compiler/dex/compiler_ir.cc | 80 - compiler/dex/compiler_ir.h | 210 - compiler/dex/dataflow_iterator-inl.h | 201 - compiler/dex/dataflow_iterator.h | 405 -- compiler/dex/dex_flags.h | 68 - compiler/dex/dex_types.h | 27 - compiler/dex/global_value_numbering.cc | 237 -- compiler/dex/global_value_numbering.h | 301 -- compiler/dex/global_value_numbering_test.cc | 2428 ------------ compiler/dex/gvn_dead_code_elimination.cc | 1473 ------- compiler/dex/gvn_dead_code_elimination.h | 169 - .../dex/gvn_dead_code_elimination_test.cc | 2201 ----------- compiler/dex/local_value_numbering.cc | 2038 ---------- compiler/dex/local_value_numbering.h | 416 -- compiler/dex/local_value_numbering_test.cc | 920 ----- compiler/dex/mir_analysis.cc | 1433 ------- compiler/dex/mir_dataflow.cc | 1453 ------- compiler/dex/mir_field_info.cc | 158 - compiler/dex/mir_field_info.h | 267 -- compiler/dex/mir_graph.cc | 2589 ------------ compiler/dex/mir_graph.h | 1488 ------- compiler/dex/mir_graph_test.cc | 446 --- compiler/dex/mir_method_info.cc | 194 - compiler/dex/mir_method_info.h | 240 -- compiler/dex/mir_optimization.cc | 1997 ---------- compiler/dex/mir_optimization_test.cc | 1186 ------ compiler/dex/pass.h | 92 - compiler/dex/pass_driver.h | 139 - compiler/dex/pass_driver_me.h | 316 -- compiler/dex/pass_driver_me_opts.cc | 76 - compiler/dex/pass_driver_me_opts.h | 55 - compiler/dex/pass_driver_me_post_opt.cc | 48 - compiler/dex/pass_driver_me_post_opt.h | 45 - compiler/dex/pass_manager.cc | 50 - compiler/dex/pass_manager.h | 150 - compiler/dex/pass_me.h | 226 -- compiler/dex/post_opt_passes.cc | 79 - compiler/dex/post_opt_passes.h | 318 -- compiler/dex/quick/arm/arm_lir.h | 605 --- compiler/dex/quick/arm/assemble_arm.cc | 1687 -------- compiler/dex/quick/arm/backend_arm.h | 32 - compiler/dex/quick/arm/call_arm.cc | 763 ---- compiler/dex/quick/arm/codegen_arm.h | 358 -- compiler/dex/quick/arm/fp_arm.cc | 423 -- compiler/dex/quick/arm/int_arm.cc | 1736 --------- compiler/dex/quick/arm/target_arm.cc | 1015 ----- compiler/dex/quick/arm/utility_arm.cc | 1314 ------- compiler/dex/quick/arm64/arm64_lir.h | 440 --- compiler/dex/quick/arm64/assemble_arm64.cc | 1152 ------ compiler/dex/quick/arm64/backend_arm64.h | 32 - compiler/dex/quick/arm64/call_arm64.cc | 595 --- compiler/dex/quick/arm64/codegen_arm64.h | 416 -- compiler/dex/quick/arm64/fp_arm64.cc | 486 --- compiler/dex/quick/arm64/int_arm64.cc | 1798 --------- compiler/dex/quick/arm64/target_arm64.cc | 912 ----- compiler/dex/quick/arm64/utility_arm64.cc | 1407 ------- compiler/dex/quick/codegen_util.cc | 1451 ------- compiler/dex/quick/dex_file_method_inliner.cc | 442 --- compiler/dex/quick/dex_file_method_inliner.h | 31 - compiler/dex/quick/gen_common.cc | 2253 ----------- compiler/dex/quick/gen_invoke.cc | 1630 -------- compiler/dex/quick/gen_loadstore.cc | 425 -- .../quick/lazy_debug_frame_opcode_writer.cc | 58 - .../quick/lazy_debug_frame_opcode_writer.h | 68 - compiler/dex/quick/local_optimizations.cc | 518 --- compiler/dex/quick/mips/README.mips | 57 - compiler/dex/quick/mips/assemble_mips.cc | 956 ----- compiler/dex/quick/mips/backend_mips.h | 32 - compiler/dex/quick/mips/call_mips.cc | 527 --- compiler/dex/quick/mips/codegen_mips.h | 353 -- compiler/dex/quick/mips/fp_mips.cc | 300 -- compiler/dex/quick/mips/int_mips.cc | 932 ----- compiler/dex/quick/mips/mips_lir.h | 720 ---- compiler/dex/quick/mips/target_mips.cc | 976 ----- compiler/dex/quick/mips/utility_mips.cc | 1115 ------ compiler/dex/quick/mir_to_lir-inl.h | 310 -- compiler/dex/quick/mir_to_lir.cc | 1460 ------- compiler/dex/quick/mir_to_lir.h | 1933 --------- compiler/dex/quick/quick_cfi_test.cc | 157 - .../dex/quick/quick_cfi_test_expected.inc | 215 - compiler/dex/quick/quick_compiler.cc | 934 ----- compiler/dex/quick/quick_compiler.h | 87 - compiler/dex/quick/quick_compiler_factory.h | 29 - compiler/dex/quick/ralloc_util.cc | 1560 -------- compiler/dex/quick/resource_mask.cc | 186 - compiler/dex/quick/resource_mask.h | 169 - compiler/dex/quick/x86/assemble_x86.cc | 2073 ---------- compiler/dex/quick/x86/backend_x86.h | 32 - compiler/dex/quick/x86/call_x86.cc | 424 -- compiler/dex/quick/x86/codegen_x86.h | 985 ----- compiler/dex/quick/x86/fp_x86.cc | 813 ---- compiler/dex/quick/x86/int_x86.cc | 3467 ----------------- .../dex/quick/x86/quick_assemble_x86_test.cc | 273 -- compiler/dex/quick/x86/target_x86.cc | 2654 ------------- compiler/dex/quick/x86/utility_x86.cc | 1167 ------ compiler/dex/quick/x86/x86_lir.h | 739 ---- compiler/dex/reg_location.h | 61 - compiler/dex/reg_storage.h | 348 -- compiler/dex/reg_storage_eq.h | 39 - compiler/dex/ssa_transformation.cc | 607 --- compiler/dex/type_inference.cc | 1074 ----- compiler/dex/type_inference.h | 448 --- compiler/dex/type_inference_test.cc | 2045 ---------- compiler/dex/vreg_analysis.cc | 76 - compiler/driver/compiler_options.cc | 46 - compiler/driver/compiler_options.h | 10 - compiler/driver/dex_compilation_unit.cc | 1 - compiler/oat_test.cc | 1 - compiler/optimizing/optimizing_unit_test.h | 1 - dex2oat/dex2oat.cc | 15 - 116 files changed, 4 insertions(+), 77280 deletions(-) delete mode 100644 compiler/dex/bb_optimizations.cc delete mode 100644 compiler/dex/bb_optimizations.h delete mode 100644 compiler/dex/compiler_ir.cc delete mode 100644 compiler/dex/compiler_ir.h delete mode 100644 compiler/dex/dataflow_iterator-inl.h delete mode 100644 compiler/dex/dataflow_iterator.h delete mode 100644 compiler/dex/dex_flags.h delete mode 100644 compiler/dex/dex_types.h delete mode 100644 compiler/dex/global_value_numbering.cc delete mode 100644 compiler/dex/global_value_numbering.h delete mode 100644 compiler/dex/global_value_numbering_test.cc delete mode 100644 compiler/dex/gvn_dead_code_elimination.cc delete mode 100644 compiler/dex/gvn_dead_code_elimination.h delete mode 100644 compiler/dex/gvn_dead_code_elimination_test.cc delete mode 100644 compiler/dex/local_value_numbering.cc delete mode 100644 compiler/dex/local_value_numbering.h delete mode 100644 compiler/dex/local_value_numbering_test.cc delete mode 100644 compiler/dex/mir_analysis.cc delete mode 100644 compiler/dex/mir_dataflow.cc delete mode 100644 compiler/dex/mir_field_info.cc delete mode 100644 compiler/dex/mir_field_info.h delete mode 100644 compiler/dex/mir_graph.cc delete mode 100644 compiler/dex/mir_graph.h delete mode 100644 compiler/dex/mir_graph_test.cc delete mode 100644 compiler/dex/mir_method_info.cc delete mode 100644 compiler/dex/mir_method_info.h delete mode 100644 compiler/dex/mir_optimization.cc delete mode 100644 compiler/dex/mir_optimization_test.cc delete mode 100644 compiler/dex/pass.h delete mode 100644 compiler/dex/pass_driver.h delete mode 100644 compiler/dex/pass_driver_me.h delete mode 100644 compiler/dex/pass_driver_me_opts.cc delete mode 100644 compiler/dex/pass_driver_me_opts.h delete mode 100644 compiler/dex/pass_driver_me_post_opt.cc delete mode 100644 compiler/dex/pass_driver_me_post_opt.h delete mode 100644 compiler/dex/pass_manager.cc delete mode 100644 compiler/dex/pass_manager.h delete mode 100644 compiler/dex/pass_me.h delete mode 100644 compiler/dex/post_opt_passes.cc delete mode 100644 compiler/dex/post_opt_passes.h delete mode 100644 compiler/dex/quick/arm/arm_lir.h delete mode 100644 compiler/dex/quick/arm/assemble_arm.cc delete mode 100644 compiler/dex/quick/arm/backend_arm.h delete mode 100644 compiler/dex/quick/arm/call_arm.cc delete mode 100644 compiler/dex/quick/arm/codegen_arm.h delete mode 100644 compiler/dex/quick/arm/fp_arm.cc delete mode 100644 compiler/dex/quick/arm/int_arm.cc delete mode 100644 compiler/dex/quick/arm/target_arm.cc delete mode 100644 compiler/dex/quick/arm/utility_arm.cc delete mode 100644 compiler/dex/quick/arm64/arm64_lir.h delete mode 100644 compiler/dex/quick/arm64/assemble_arm64.cc delete mode 100644 compiler/dex/quick/arm64/backend_arm64.h delete mode 100644 compiler/dex/quick/arm64/call_arm64.cc delete mode 100644 compiler/dex/quick/arm64/codegen_arm64.h delete mode 100644 compiler/dex/quick/arm64/fp_arm64.cc delete mode 100644 compiler/dex/quick/arm64/int_arm64.cc delete mode 100644 compiler/dex/quick/arm64/target_arm64.cc delete mode 100644 compiler/dex/quick/arm64/utility_arm64.cc delete mode 100644 compiler/dex/quick/codegen_util.cc delete mode 100644 compiler/dex/quick/gen_common.cc delete mode 100755 compiler/dex/quick/gen_invoke.cc delete mode 100644 compiler/dex/quick/gen_loadstore.cc delete mode 100644 compiler/dex/quick/lazy_debug_frame_opcode_writer.cc delete mode 100644 compiler/dex/quick/lazy_debug_frame_opcode_writer.h delete mode 100644 compiler/dex/quick/local_optimizations.cc delete mode 100644 compiler/dex/quick/mips/README.mips delete mode 100644 compiler/dex/quick/mips/assemble_mips.cc delete mode 100644 compiler/dex/quick/mips/backend_mips.h delete mode 100644 compiler/dex/quick/mips/call_mips.cc delete mode 100644 compiler/dex/quick/mips/codegen_mips.h delete mode 100644 compiler/dex/quick/mips/fp_mips.cc delete mode 100644 compiler/dex/quick/mips/int_mips.cc delete mode 100644 compiler/dex/quick/mips/mips_lir.h delete mode 100644 compiler/dex/quick/mips/target_mips.cc delete mode 100644 compiler/dex/quick/mips/utility_mips.cc delete mode 100644 compiler/dex/quick/mir_to_lir-inl.h delete mode 100644 compiler/dex/quick/mir_to_lir.cc delete mode 100644 compiler/dex/quick/mir_to_lir.h delete mode 100644 compiler/dex/quick/quick_cfi_test.cc delete mode 100644 compiler/dex/quick/quick_cfi_test_expected.inc delete mode 100644 compiler/dex/quick/quick_compiler.cc delete mode 100644 compiler/dex/quick/quick_compiler.h delete mode 100644 compiler/dex/quick/quick_compiler_factory.h delete mode 100644 compiler/dex/quick/ralloc_util.cc delete mode 100644 compiler/dex/quick/resource_mask.cc delete mode 100644 compiler/dex/quick/resource_mask.h delete mode 100644 compiler/dex/quick/x86/assemble_x86.cc delete mode 100644 compiler/dex/quick/x86/backend_x86.h delete mode 100644 compiler/dex/quick/x86/call_x86.cc delete mode 100644 compiler/dex/quick/x86/codegen_x86.h delete mode 100755 compiler/dex/quick/x86/fp_x86.cc delete mode 100755 compiler/dex/quick/x86/int_x86.cc delete mode 100644 compiler/dex/quick/x86/quick_assemble_x86_test.cc delete mode 100755 compiler/dex/quick/x86/target_x86.cc delete mode 100644 compiler/dex/quick/x86/utility_x86.cc delete mode 100644 compiler/dex/quick/x86/x86_lir.h delete mode 100644 compiler/dex/reg_location.h delete mode 100644 compiler/dex/reg_storage.h delete mode 100644 compiler/dex/reg_storage_eq.h delete mode 100644 compiler/dex/ssa_transformation.cc delete mode 100644 compiler/dex/type_inference.cc delete mode 100644 compiler/dex/type_inference.h delete mode 100644 compiler/dex/type_inference_test.cc delete mode 100644 compiler/dex/vreg_analysis.cc diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 33242f1c5d..426c3cac64 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -247,12 +247,6 @@ COMPILER_GTEST_COMMON_SRC_FILES := \ runtime/reflection_test.cc \ compiler/compiled_method_test.cc \ compiler/debug/dwarf/dwarf_test.cc \ - compiler/dex/gvn_dead_code_elimination_test.cc \ - compiler/dex/global_value_numbering_test.cc \ - compiler/dex/local_value_numbering_test.cc \ - compiler/dex/mir_graph_test.cc \ - compiler/dex/mir_optimization_test.cc \ - compiler/dex/type_inference_test.cc \ compiler/driver/compiled_method_storage_test.cc \ compiler/driver/compiler_driver_test.cc \ compiler/elf_writer_test.cc \ @@ -284,7 +278,6 @@ COMPILER_GTEST_COMMON_SRC_FILES := \ compiler/utils/test_dex_file_builder_test.cc \ COMPILER_GTEST_COMMON_SRC_FILES_all := \ - compiler/dex/quick/quick_cfi_test.cc \ compiler/jni/jni_cfi_test.cc \ compiler/optimizing/codegen_test.cc \ compiler/optimizing/constant_folding_test.cc \ @@ -374,7 +367,6 @@ COMPILER_GTEST_HOST_SRC_FILES_mips64 := \ COMPILER_GTEST_HOST_SRC_FILES_x86 := \ $(COMPILER_GTEST_COMMON_SRC_FILES_x86) \ - compiler/dex/quick/x86/quick_assemble_x86_test.cc \ compiler/utils/x86/assembler_x86_test.cc \ COMPILER_GTEST_HOST_SRC_FILES_x86_64 := \ diff --git a/compiler/Android.mk b/compiler/Android.mk index 11ee6dd3a1..f12f00743b 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -21,40 +21,12 @@ include art/build/Android.common_build.mk LIBART_COMPILER_SRC_FILES := \ compiled_method.cc \ debug/elf_debug_writer.cc \ - dex/global_value_numbering.cc \ - dex/gvn_dead_code_elimination.cc \ - dex/local_value_numbering.cc \ - dex/type_inference.cc \ - dex/quick/codegen_util.cc \ - dex/quick/dex_file_method_inliner.cc \ - dex/quick/dex_file_to_method_inliner_map.cc \ - dex/quick/gen_common.cc \ - dex/quick/gen_invoke.cc \ - dex/quick/gen_loadstore.cc \ - dex/quick/lazy_debug_frame_opcode_writer.cc \ - dex/quick/local_optimizations.cc \ - dex/quick/mir_to_lir.cc \ - dex/quick/quick_compiler.cc \ - dex/quick/ralloc_util.cc \ - dex/quick/resource_mask.cc \ dex/dex_to_dex_compiler.cc \ - dex/bb_optimizations.cc \ - dex/compiler_ir.cc \ - dex/mir_analysis.cc \ - dex/mir_dataflow.cc \ - dex/mir_field_info.cc \ - dex/mir_graph.cc \ - dex/mir_method_info.cc \ - dex/mir_optimization.cc \ - dex/post_opt_passes.cc \ - dex/pass_driver_me_opts.cc \ - dex/pass_driver_me_post_opt.cc \ - dex/pass_manager.cc \ - dex/ssa_transformation.cc \ dex/verified_method.cc \ dex/verification_results.cc \ - dex/vreg_analysis.cc \ dex/quick_compiler_callbacks.cc \ + dex/quick/dex_file_method_inliner.cc \ + dex/quick/dex_file_to_method_inliner_map.cc \ driver/compiled_method_storage.cc \ driver/compiler_driver.cc \ driver/compiler_options.cc \ @@ -111,12 +83,6 @@ LIBART_COMPILER_SRC_FILES := \ oat_writer.cc LIBART_COMPILER_SRC_FILES_arm := \ - dex/quick/arm/assemble_arm.cc \ - dex/quick/arm/call_arm.cc \ - dex/quick/arm/fp_arm.cc \ - dex/quick/arm/int_arm.cc \ - dex/quick/arm/target_arm.cc \ - dex/quick/arm/utility_arm.cc \ jni/quick/arm/calling_convention_arm.cc \ linker/arm/relative_patcher_arm_base.cc \ linker/arm/relative_patcher_thumb2.cc \ @@ -133,12 +99,6 @@ LIBART_COMPILER_SRC_FILES_arm := \ # 32bit one. LIBART_COMPILER_SRC_FILES_arm64 := \ $(LIBART_COMPILER_SRC_FILES_arm) \ - dex/quick/arm64/assemble_arm64.cc \ - dex/quick/arm64/call_arm64.cc \ - dex/quick/arm64/fp_arm64.cc \ - dex/quick/arm64/int_arm64.cc \ - dex/quick/arm64/target_arm64.cc \ - dex/quick/arm64/utility_arm64.cc \ jni/quick/arm64/calling_convention_arm64.cc \ linker/arm64/relative_patcher_arm64.cc \ optimizing/code_generator_arm64.cc \ @@ -150,12 +110,6 @@ LIBART_COMPILER_SRC_FILES_arm64 := \ utils/arm64/managed_register_arm64.cc \ LIBART_COMPILER_SRC_FILES_mips := \ - dex/quick/mips/assemble_mips.cc \ - dex/quick/mips/call_mips.cc \ - dex/quick/mips/fp_mips.cc \ - dex/quick/mips/int_mips.cc \ - dex/quick/mips/target_mips.cc \ - dex/quick/mips/utility_mips.cc \ jni/quick/mips/calling_convention_mips.cc \ optimizing/code_generator_mips.cc \ optimizing/intrinsics_mips.cc \ @@ -172,12 +126,6 @@ LIBART_COMPILER_SRC_FILES_mips64 := \ LIBART_COMPILER_SRC_FILES_x86 := \ - dex/quick/x86/assemble_x86.cc \ - dex/quick/x86/call_x86.cc \ - dex/quick/x86/fp_x86.cc \ - dex/quick/x86/int_x86.cc \ - dex/quick/x86/target_x86.cc \ - dex/quick/x86/utility_x86.cc \ jni/quick/x86/calling_convention_x86.cc \ linker/x86/relative_patcher_x86.cc \ linker/x86/relative_patcher_x86_base.cc \ @@ -200,26 +148,20 @@ LIBART_COMPILER_SRC_FILES_x86_64 := \ LIBART_COMPILER_CFLAGS := LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES := \ - dex/quick/resource_mask.h \ dex/compiler_enums.h \ dex/dex_to_dex_compiler.h \ - dex/global_value_numbering.h \ - dex/pass_me.h \ driver/compiler_driver.h \ driver/compiler_options.h \ image_writer.h \ optimizing/locations.h LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES_arm := \ - dex/quick/arm/arm_lir.h \ utils/arm/constants_arm.h LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES_arm64 := \ - $(LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES_arm) \ - dex/quick/arm64/arm64_lir.h + $(LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES_arm) LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES_mips := \ - dex/quick/mips/mips_lir.h \ utils/mips/assembler_mips.h LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES_mips64 := \ diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index 6075cd6fbe..6483ef63b1 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -21,7 +21,6 @@ #include "art_method.h" #include "class_linker.h" #include "compiled_method.h" -#include "dex/pass_manager.h" #include "dex/quick_compiler_callbacks.h" #include "dex/quick/dex_file_to_method_inliner_map.h" #include "dex/verification_results.h" diff --git a/compiler/compiler.cc b/compiler/compiler.cc index 223affad82..16263177d8 100644 --- a/compiler/compiler.cc +++ b/compiler/compiler.cc @@ -17,7 +17,6 @@ #include "compiler.h" #include "base/logging.h" -#include "dex/quick/quick_compiler_factory.h" #include "driver/compiler_driver.h" #include "optimizing/optimizing_compiler.h" #include "utils.h" @@ -27,8 +26,7 @@ namespace art { Compiler* Compiler::Create(CompilerDriver* driver, Compiler::Kind kind) { switch (kind) { case kQuick: - return CreateQuickCompiler(driver); - + // TODO: Remove Quick in options. case kOptimizing: return CreateOptimizingCompiler(driver); diff --git a/compiler/dex/bb_optimizations.cc b/compiler/dex/bb_optimizations.cc deleted file mode 100644 index 11a7e44f98..0000000000 --- a/compiler/dex/bb_optimizations.cc +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "bb_optimizations.h" -#include "dataflow_iterator.h" -#include "dataflow_iterator-inl.h" - -namespace art { - -/* - * Code Layout pass implementation start. - */ -bool CodeLayout::Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - c_unit->mir_graph->LayoutBlocks(bb); - // No need of repeating, so just return false. - return false; -} - -/* - * BasicBlock Combine pass implementation start. - */ -bool BBCombine::Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - c_unit->mir_graph->CombineBlocks(bb); - - // No need of repeating, so just return false. - return false; -} - -/* - * MethodUseCount pass implementation start. - */ -bool MethodUseCount::Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - // First initialize the data. - c_unit->mir_graph->InitializeMethodUses(); - - // Now check if the pass is to be ignored. - bool res = ((c_unit->disable_opt & (1 << kPromoteRegs)) == 0); - - return res; -} - -bool MethodUseCount::Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - c_unit->mir_graph->CountUses(bb); - // No need of repeating, so just return false. - return false; -} - -} // namespace art diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h deleted file mode 100644 index 02d532798c..0000000000 --- a/compiler/dex/bb_optimizations.h +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_BB_OPTIMIZATIONS_H_ -#define ART_COMPILER_DEX_BB_OPTIMIZATIONS_H_ - -#include "base/casts.h" -#include "compiler_ir.h" -#include "dex_flags.h" -#include "pass_me.h" -#include "mir_graph.h" - -namespace art { - -/** - * @class String Change - * @brief Converts calls to String. to StringFactory instead. - */ -class StringChange : public PassME { - public: - StringChange() : PassME("StringChange", kNoNodes) { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->StringChange(); - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->HasInvokes(); - } -}; - -/** - * @class CacheFieldLoweringInfo - * @brief Cache the lowering info for fields used by IGET/IPUT/SGET/SPUT insns. - */ -class CacheFieldLoweringInfo : public PassME { - public: - CacheFieldLoweringInfo() : PassME("CacheFieldLoweringInfo", kNoNodes) { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->DoCacheFieldLoweringInfo(); - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->HasFieldAccess(); - } -}; - -/** - * @class CacheMethodLoweringInfo - * @brief Cache the lowering info for methods called by INVOKEs. - */ -class CacheMethodLoweringInfo : public PassME { - public: - CacheMethodLoweringInfo() : PassME("CacheMethodLoweringInfo", kNoNodes) { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->DoCacheMethodLoweringInfo(); - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->HasInvokes(); - } -}; - -/** - * @class SpecialMethodInliner - * @brief Performs method inlining pass on special kinds of methods. - * @details Special methods are methods that fall in one of the following categories: - * empty, instance getter, instance setter, argument return, and constant return. - */ -class SpecialMethodInliner : public PassME { - public: - SpecialMethodInliner() : PassME("SpecialMethodInliner") { - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->InlineSpecialMethodsGate(); - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->InlineSpecialMethodsStart(); - } - - bool Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - c_unit->mir_graph->InlineSpecialMethods(bb); - // No need of repeating, so just return false. - return false; - } - - void End(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->InlineSpecialMethodsEnd(); - } -}; - -/** - * @class CodeLayout - * @brief Perform the code layout pass. - */ -class CodeLayout : public PassME { - public: - CodeLayout() : PassME("CodeLayout", kAllNodes, kOptimizationBasicBlockChange, "2_post_layout_cfg") { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->VerifyDataflow(); - c_unit->mir_graph->ClearAllVisitedFlags(); - } - - bool Worker(PassDataHolder* data) const; -}; - -/** - * @class NullCheckElimination - * @brief Null check elimination pass. - */ -class NullCheckElimination : public PassME { - public: - NullCheckElimination() - : PassME("NCE", kRepeatingPreOrderDFSTraversal, "3_post_nce_cfg") { - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->EliminateNullChecksGate(); - } - - bool Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - return c_unit->mir_graph->EliminateNullChecks(bb); - } - - void End(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->EliminateNullChecksEnd(); - } -}; - -class ClassInitCheckElimination : public PassME { - public: - ClassInitCheckElimination() - : PassME("ClInitCheckElimination", kRepeatingPreOrderDFSTraversal) { - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->EliminateClassInitChecksGate(); - } - - bool Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - return c_unit->mir_graph->EliminateClassInitChecks(bb); - } - - void End(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->EliminateClassInitChecksEnd(); - } -}; - -/** - * @class GlobalValueNumberingPass - * @brief Performs the global value numbering pass. - */ -class GlobalValueNumberingPass : public PassME { - public: - GlobalValueNumberingPass() - : PassME("GVN", kLoopRepeatingTopologicalSortTraversal, "4_post_gvn_cfg") { - } - - bool Gate(const PassDataHolder* data) const OVERRIDE { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->ApplyGlobalValueNumberingGate(); - } - - bool Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - return c_unit->mir_graph->ApplyGlobalValueNumbering(bb); - } - - void End(PassDataHolder* data) const OVERRIDE { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->ApplyGlobalValueNumberingEnd(); - } -}; - -/** - * @class DeadCodeEliminationPass - * @brief Performs the GVN-based dead code elimination pass. - */ -class DeadCodeEliminationPass : public PassME { - public: - DeadCodeEliminationPass() : PassME("DCE", kPreOrderDFSTraversal, "4_post_dce_cfg") { - } - - bool Gate(const PassDataHolder* data) const OVERRIDE { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->EliminateDeadCodeGate(); - } - - bool Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - return c_unit->mir_graph->EliminateDeadCode(bb); - } - - void End(PassDataHolder* data) const OVERRIDE { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->EliminateDeadCodeEnd(); - } -}; - -/** - * @class GlobalValueNumberingCleanupPass - * @brief Performs the cleanup after global value numbering pass and the dependent - * dead code elimination pass that needs the GVN data. - */ -class GlobalValueNumberingCleanupPass : public PassME { - public: - GlobalValueNumberingCleanupPass() - : PassME("GVNCleanup", kNoNodes, "") { - } - - void Start(PassDataHolder* data) const OVERRIDE { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->GlobalValueNumberingCleanup(); - } -}; - -/** - * @class BBCombine - * @brief Perform the basic block combination pass. - */ -class BBCombine : public PassME { - public: - BBCombine() : PassME("BBCombine", kPreOrderDFSTraversal, "5_post_bbcombine_cfg") { - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->HasTryCatchBlocks() || - ((c_unit->disable_opt & (1 << kSuppressExceptionEdges)) != 0); - } - - bool Worker(PassDataHolder* data) const; -}; - -/** - * @class ConstantPropagation - * @brief Perform a constant propagation pass. - */ -class ConstantPropagation : public PassME { - public: - ConstantPropagation() : PassME("ConstantPropagation") { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->InitializeConstantPropagation(); - } - - bool Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = down_cast(data)->bb; - DCHECK(bb != nullptr); - c_unit->mir_graph->DoConstantPropagation(bb); - // No need of repeating, so just return false. - return false; - } -}; - -/** - * @class MethodUseCount - * @brief Count the register uses of the method - */ -class MethodUseCount : public PassME { - public: - MethodUseCount() : PassME("UseCount") { - } - - bool Worker(PassDataHolder* data) const; - - bool Gate(const PassDataHolder* data) const; -}; - -/** - * @class BasicBlock Optimizations - * @brief Any simple BasicBlock optimization can be put here. - */ -class BBOptimizations : public PassME { - public: - BBOptimizations() - : PassME("BBOptimizations", kNoNodes, kOptimizationBasicBlockChange, "5_post_bbo_cfg") { - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return ((c_unit->disable_opt & (1 << kBBOpt)) == 0); - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->BasicBlockOptimizationStart(); - - /* - * This pass has a different ordering depending on the suppress exception, - * so do the pass here for now: - * - Later, the Start should just change the ordering and we can move the extended - * creation into the pass driver's main job with a new iterator - */ - c_unit->mir_graph->BasicBlockOptimization(); - } - - void End(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->BasicBlockOptimizationEnd(); - down_cast(data)->dirty = !c_unit->mir_graph->DfsOrdersUpToDate(); - } -}; - -/** - * @class SuspendCheckElimination - * @brief Any simple BasicBlock optimization can be put here. - */ -class SuspendCheckElimination : public PassME { - public: - SuspendCheckElimination() - : PassME("SuspendCheckElimination", kTopologicalSortTraversal, "6_post_sce_cfg") { - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return c_unit->mir_graph->EliminateSuspendChecksGate(); - } - - bool Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - return c_unit->mir_graph->EliminateSuspendChecks(bb); - } -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_BB_OPTIMIZATIONS_H_ diff --git a/compiler/dex/compiler_ir.cc b/compiler/dex/compiler_ir.cc deleted file mode 100644 index 6e1853bbd7..0000000000 --- a/compiler/dex/compiler_ir.cc +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "compiler_ir.h" - -#include "arch/instruction_set_features.h" -#include "base/dumpable.h" -#include "dex_flags.h" -#include "dex/quick/mir_to_lir.h" -#include "driver/compiler_driver.h" -#include "mir_graph.h" -#include "utils.h" - -namespace art { - -CompilationUnit::CompilationUnit(ArenaPool* pool, InstructionSet isa, CompilerDriver* driver, - ClassLinker* linker) - : compiler_driver(driver), - class_linker(linker), - dex_file(nullptr), - class_loader(nullptr), - class_def_idx(0), - method_idx(0), - access_flags(0), - invoke_type(kDirect), - shorty(nullptr), - disable_opt(0), - enable_debug(0), - verbose(false), - instruction_set(isa), - target64(Is64BitInstructionSet(isa)), - arena(pool), - arena_stack(pool), - mir_graph(nullptr), - cg(nullptr), - timings("QuickCompiler", true, false), - print_pass(false) { -} - -CompilationUnit::~CompilationUnit() { - overridden_pass_options.clear(); -} - -void CompilationUnit::StartTimingSplit(const char* label) { - if (compiler_driver->GetDumpPasses()) { - timings.StartTiming(label); - } -} - -void CompilationUnit::NewTimingSplit(const char* label) { - if (compiler_driver->GetDumpPasses()) { - timings.EndTiming(); - timings.StartTiming(label); - } -} - -void CompilationUnit::EndTiming() { - if (compiler_driver->GetDumpPasses()) { - timings.EndTiming(); - if (enable_debug & (1 << kDebugTimings)) { - LOG(INFO) << "TIMINGS " << PrettyMethod(method_idx, *dex_file); - LOG(INFO) << Dumpable(timings); - } - } -} - -} // namespace art diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h deleted file mode 100644 index 5203355d06..0000000000 --- a/compiler/dex/compiler_ir.h +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_COMPILER_IR_H_ -#define ART_COMPILER_DEX_COMPILER_IR_H_ - -#include "jni.h" -#include -#include - -#include "arch/instruction_set.h" -#include "base/arena_allocator.h" -#include "base/scoped_arena_allocator.h" -#include "base/timing_logger.h" -#include "invoke_type.h" -#include "safe_map.h" - -namespace art { - -class ClassLinker; -class CompilerDriver; -class DexFile; -class Mir2Lir; -class MIRGraph; - -constexpr size_t kOptionStringMaxLength = 2048; - -/** - * Structure abstracting pass option values, which can be of type string or integer. - */ -struct OptionContent { - OptionContent(const OptionContent& option) : - type(option.type), container(option.container, option.type) {} - - explicit OptionContent(const char* value) : - type(kString), container(value) {} - - explicit OptionContent(int value) : - type(kInteger), container(value) {} - - explicit OptionContent(int64_t value) : - type(kInteger), container(value) {} - - ~OptionContent() { - if (type == kString) { - container.StringDelete(); - } - } - - /** - * Allows for a transparent display of the option content. - */ - friend std::ostream& operator<<(std::ostream& out, const OptionContent& option) { - if (option.type == kString) { - out << option.container.s; - } else { - out << option.container.i; - } - - return out; - } - - inline const char* GetString() const { - return container.s; - } - - inline int64_t GetInteger() const { - return container.i; - } - - /** - * @brief Used to compare a string option value to a given @p value. - * @details Will return whether the internal string option is equal to - * the parameter @p value. It will return false if the type of the - * object is not a string. - * @param value The string to compare to. - * @return Returns whether the internal string option is equal to the - * parameter @p value. - */ - inline bool Equals(const char* value) const { - DCHECK(value != nullptr); - if (type != kString) { - return false; - } - return !strncmp(container.s, value, kOptionStringMaxLength); - } - - /** - * @brief Used to compare an integer option value to a given @p value. - * @details Will return whether the internal integer option is equal to - * the parameter @p value. It will return false if the type of the - * object is not an integer. - * @param value The integer to compare to. - * @return Returns whether the internal integer option is equal to the - * parameter @p value. - */ - inline bool Equals(int64_t value) const { - if (type != kInteger) { - return false; - } - return container.i == value; - } - - /** - * Describes the type of parameters allowed as option values. - */ - enum OptionType { - kString = 0, - kInteger - }; - - OptionType type; - - private: - /** - * Union containing the option value of either type. - */ - union OptionContainer { - OptionContainer(const OptionContainer& c, OptionType t) { - if (t == kString) { - DCHECK(c.s != nullptr); - s = strndup(c.s, kOptionStringMaxLength); - } else { - i = c.i; - } - } - - explicit OptionContainer(const char* value) { - DCHECK(value != nullptr); - s = strndup(value, kOptionStringMaxLength); - } - - explicit OptionContainer(int64_t value) : i(value) {} - ~OptionContainer() {} - - void StringDelete() { - if (s != nullptr) { - free(s); - } - } - - char* s; - int64_t i; - }; - - OptionContainer container; -}; - -struct CompilationUnit { - CompilationUnit(ArenaPool* pool, InstructionSet isa, CompilerDriver* driver, ClassLinker* linker); - ~CompilationUnit(); - - void StartTimingSplit(const char* label); - void NewTimingSplit(const char* label); - void EndTiming(); - - /* - * Fields needed/generated by common frontend and generally used throughout - * the compiler. - */ - CompilerDriver* const compiler_driver; - ClassLinker* const class_linker; // Linker to resolve fields and methods. - const DexFile* dex_file; // DexFile containing the method being compiled. - jobject class_loader; // compiling method's class loader. - uint16_t class_def_idx; // compiling method's defining class definition index. - uint32_t method_idx; // compiling method's index into method_ids of DexFile. - uint32_t access_flags; // compiling method's access flags. - InvokeType invoke_type; // compiling method's invocation type. - const char* shorty; // compiling method's shorty. - uint32_t disable_opt; // opt_control_vector flags. - uint32_t enable_debug; // debugControlVector flags. - bool verbose; - const InstructionSet instruction_set; - const bool target64; - - // TODO: move memory management to mir_graph, or just switch to using standard containers. - ArenaAllocator arena; - ArenaStack arena_stack; // Arenas for ScopedArenaAllocator. - - std::unique_ptr mir_graph; // MIR container. - std::unique_ptr cg; // Target-specific codegen. - TimingLogger timings; - bool print_pass; // Do we want to print a pass or not? - - /** - * @brief Holds pass options for current pass being applied to compilation unit. - * @details This is updated for every pass to contain the overridden pass options - * that were specified by user. The pass itself will check this to see if the - * default settings have been changed. The key is simply the option string without - * the pass name. - */ - SafeMap overridden_pass_options; -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_COMPILER_IR_H_ diff --git a/compiler/dex/dataflow_iterator-inl.h b/compiler/dex/dataflow_iterator-inl.h deleted file mode 100644 index e9402e39e5..0000000000 --- a/compiler/dex/dataflow_iterator-inl.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_DATAFLOW_ITERATOR_INL_H_ -#define ART_COMPILER_DEX_DATAFLOW_ITERATOR_INL_H_ - -#include "dataflow_iterator.h" - -namespace art { - -// Single forward pass over the nodes. -inline BasicBlock* DataflowIterator::ForwardSingleNext() { - BasicBlock* res = nullptr; - - // Are we not yet at the end? - if (idx_ < end_idx_) { - // Get the next index. - BasicBlockId bb_id = (*block_id_list_)[idx_]; - res = mir_graph_->GetBasicBlock(bb_id); - idx_++; - } - - return res; -} - -// Repeat full forward passes over all nodes until no change occurs during a complete pass. -inline BasicBlock* DataflowIterator::ForwardRepeatNext() { - BasicBlock* res = nullptr; - - // Are we at the end and have we changed something? - if ((idx_ >= end_idx_) && changed_ == true) { - // Reset the index. - idx_ = start_idx_; - repeats_++; - changed_ = false; - } - - // Are we not yet at the end? - if (idx_ < end_idx_) { - // Get the BasicBlockId. - BasicBlockId bb_id = (*block_id_list_)[idx_]; - res = mir_graph_->GetBasicBlock(bb_id); - idx_++; - } - - return res; -} - -// Single reverse pass over the nodes. -inline BasicBlock* DataflowIterator::ReverseSingleNext() { - BasicBlock* res = nullptr; - - // Are we not yet at the end? - if (idx_ >= 0) { - // Get the BasicBlockId. - BasicBlockId bb_id = (*block_id_list_)[idx_]; - res = mir_graph_->GetBasicBlock(bb_id); - idx_--; - } - - return res; -} - -// Repeat full backwards passes over all nodes until no change occurs during a complete pass. -inline BasicBlock* DataflowIterator::ReverseRepeatNext() { - BasicBlock* res = nullptr; - - // Are we done and we changed something during the last iteration? - if ((idx_ < 0) && changed_) { - // Reset the index. - idx_ = start_idx_; - repeats_++; - changed_ = false; - } - - // Are we not yet done? - if (idx_ >= 0) { - // Get the BasicBlockId. - BasicBlockId bb_id = (*block_id_list_)[idx_]; - res = mir_graph_->GetBasicBlock(bb_id); - idx_--; - } - - return res; -} - -// AllNodes uses the existing block list, and should be considered unordered. -inline BasicBlock* AllNodesIterator::Next(bool had_change) { - // Update changed: if had_changed is true, we remember it for the whole iteration. - changed_ |= had_change; - - BasicBlock* res = nullptr; - while (idx_ != end_idx_) { - BasicBlock* bb = mir_graph_->GetBlockList()[idx_++]; - DCHECK(bb != nullptr); - if (!bb->hidden) { - res = bb; - break; - } - } - - return res; -} - -inline BasicBlock* TopologicalSortIterator::Next(bool had_change) { - // Update changed: if had_changed is true, we remember it for the whole iteration. - changed_ |= had_change; - - while (loop_head_stack_->size() != 0u && - (*loop_ends_)[loop_head_stack_->back().first] == idx_) { - loop_head_stack_->pop_back(); - } - - if (idx_ == end_idx_) { - return nullptr; - } - - // Get next block and return it. - BasicBlockId idx = idx_; - idx_ += 1; - BasicBlock* bb = mir_graph_->GetBasicBlock((*block_id_list_)[idx]); - DCHECK(bb != nullptr); - if ((*loop_ends_)[idx] != 0u) { - loop_head_stack_->push_back(std::make_pair(idx, false)); // Not recalculating. - } - return bb; -} - -inline BasicBlock* LoopRepeatingTopologicalSortIterator::Next(bool had_change) { - if (idx_ != 0) { - // Mark last processed block visited. - BasicBlock* bb = mir_graph_->GetBasicBlock((*block_id_list_)[idx_ - 1]); - bb->visited = true; - if (had_change) { - // If we had a change we need to revisit the children. - ChildBlockIterator iter(bb, mir_graph_); - for (BasicBlock* child_bb = iter.Next(); child_bb != nullptr; child_bb = iter.Next()) { - child_bb->visited = false; - } - } - } - - while (true) { - // Pop loops we have left and check if we need to recalculate one of them. - // NOTE: We need to do this even if idx_ == end_idx_. - while (loop_head_stack_->size() != 0u && - (*loop_ends_)[loop_head_stack_->back().first] == idx_) { - auto top = loop_head_stack_->back(); - uint16_t loop_head_idx = top.first; - bool recalculated = top.second; - loop_head_stack_->pop_back(); - BasicBlock* loop_head = mir_graph_->GetBasicBlock((*block_id_list_)[loop_head_idx]); - DCHECK(loop_head != nullptr); - if (!recalculated || !loop_head->visited) { - // Recalculating this loop. - loop_head_stack_->push_back(std::make_pair(loop_head_idx, true)); - idx_ = loop_head_idx + 1; - return loop_head; - } - } - - if (idx_ == end_idx_) { - return nullptr; - } - - // Get next block and return it if unvisited. - BasicBlockId idx = idx_; - idx_ += 1; - BasicBlock* bb = mir_graph_->GetBasicBlock((*block_id_list_)[idx]); - DCHECK(bb != nullptr); - if ((*loop_ends_)[idx] != 0u) { - // If bb->visited is false, the loop needs to be processed from scratch. - // Otherwise we mark it as recalculating; for a natural loop we will not - // need to recalculate any block in the loop anyway, and for unnatural - // loops we will recalculate the loop head only if one of its predecessors - // actually changes. - bool recalculating = bb->visited; - loop_head_stack_->push_back(std::make_pair(idx, recalculating)); - } - if (!bb->visited) { - return bb; - } - } -} - -} // namespace art - -#endif // ART_COMPILER_DEX_DATAFLOW_ITERATOR_INL_H_ diff --git a/compiler/dex/dataflow_iterator.h b/compiler/dex/dataflow_iterator.h deleted file mode 100644 index 097c2a40b4..0000000000 --- a/compiler/dex/dataflow_iterator.h +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_DATAFLOW_ITERATOR_H_ -#define ART_COMPILER_DEX_DATAFLOW_ITERATOR_H_ - -#include "base/logging.h" -#include "mir_graph.h" - -namespace art { - - /* - * This class supports iterating over lists of basic blocks in various - * interesting orders. Note that for efficiency, the visit orders have been pre-computed. - * The order itself will not change during the iteration. However, for some uses, - * auxiliary data associated with the basic blocks may be changed during the iteration, - * necessitating another pass over the list. If this behavior is required, use the - * "Repeating" variant. For the repeating variant, the caller must tell the iterator - * whether a change has been made that necessitates another pass. Note that calling Next(true) - * does not affect the iteration order or short-circuit the current pass - it simply tells - * the iterator that once it has finished walking through the block list it should reset and - * do another full pass through the list. - */ - /** - * @class DataflowIterator - * @brief The main iterator class, all other iterators derive of this one to define an iteration order. - */ - class DataflowIterator { - public: - virtual ~DataflowIterator() {} - - /** - * @brief How many times have we repeated the iterator across the BasicBlocks? - * @return the number of iteration repetitions. - */ - int32_t GetRepeatCount() { return repeats_; } - - /** - * @brief Has the user of the iterator reported a change yet? - * @details Does not mean there was or not a change, it is only whether the user passed a true to the Next function call. - * @return whether the user of the iterator reported a change yet. - */ - int32_t GetChanged() { return changed_; } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) = 0; - - protected: - /** - * @param mir_graph the MIRGraph we are interested in. - * @param start_idx the first index we want to iterate across. - * @param end_idx the last index we want to iterate (not included). - */ - DataflowIterator(MIRGraph* mir_graph, int32_t start_idx, int32_t end_idx) - : mir_graph_(mir_graph), - start_idx_(start_idx), - end_idx_(end_idx), - block_id_list_(nullptr), - idx_(0), - repeats_(0), - changed_(false) {} - - /** - * @brief Get the next BasicBlock iterating forward. - * @return the next BasicBlock iterating forward. - */ - virtual BasicBlock* ForwardSingleNext() ALWAYS_INLINE; - - /** - * @brief Get the next BasicBlock iterating backward. - * @return the next BasicBlock iterating backward. - */ - virtual BasicBlock* ReverseSingleNext() ALWAYS_INLINE; - - /** - * @brief Get the next BasicBlock iterating forward, restart if a BasicBlock was reported changed during the last iteration. - * @return the next BasicBlock iterating forward, with chance of repeating the iteration. - */ - virtual BasicBlock* ForwardRepeatNext() ALWAYS_INLINE; - - /** - * @brief Get the next BasicBlock iterating backward, restart if a BasicBlock was reported changed during the last iteration. - * @return the next BasicBlock iterating backward, with chance of repeating the iteration. - */ - virtual BasicBlock* ReverseRepeatNext() ALWAYS_INLINE; - - MIRGraph* const mir_graph_; /**< @brief the MIRGraph */ - const int32_t start_idx_; /**< @brief the start index for the iteration */ - const int32_t end_idx_; /**< @brief the last index for the iteration */ - const ArenaVector* block_id_list_; /**< @brief the list of BasicBlocks we want to iterate on */ - int32_t idx_; /**< @brief Current index for the iterator */ - int32_t repeats_; /**< @brief Number of repeats over the iteration */ - bool changed_; /**< @brief Has something changed during the current iteration? */ - }; // DataflowIterator - - /** - * @class PreOrderDfsIterator - * @brief Used to perform a Pre-order Depth-First-Search Iteration of a MIRGraph. - */ - class PreOrderDfsIterator : public DataflowIterator { - public: - /** - * @brief The constructor, using all of the reachable blocks of the MIRGraph. - * @param mir_graph The MIRGraph considered. - */ - explicit PreOrderDfsIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) { - // Extra setup for the PreOrderDfsIterator. - idx_ = start_idx_; - block_id_list_ = &mir_graph->GetDfsOrder(); - } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) { - // Update changed: if had_changed is true, we remember it for the whole iteration. - changed_ |= had_change; - - return ForwardSingleNext(); - } - }; - - /** - * @class RepeatingPreOrderDfsIterator - * @brief Used to perform a Repeating Pre-order Depth-First-Search Iteration of a MIRGraph. - * @details If there is a change during an iteration, the iteration starts over at the end of the iteration. - */ - class RepeatingPreOrderDfsIterator : public DataflowIterator { - public: - /** - * @brief The constructor, using all of the reachable blocks of the MIRGraph. - * @param mir_graph The MIRGraph considered. - */ - explicit RepeatingPreOrderDfsIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) { - // Extra setup for the RepeatingPreOrderDfsIterator. - idx_ = start_idx_; - block_id_list_ = &mir_graph->GetDfsOrder(); - } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) { - // Update changed: if had_changed is true, we remember it for the whole iteration. - changed_ |= had_change; - - return ForwardRepeatNext(); - } - }; - - /** - * @class RepeatingPostOrderDfsIterator - * @brief Used to perform a Repeating Post-order Depth-First-Search Iteration of a MIRGraph. - * @details If there is a change during an iteration, the iteration starts over at the end of the iteration. - */ - class RepeatingPostOrderDfsIterator : public DataflowIterator { - public: - /** - * @brief The constructor, using all of the reachable blocks of the MIRGraph. - * @param mir_graph The MIRGraph considered. - */ - explicit RepeatingPostOrderDfsIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) { - // Extra setup for the RepeatingPostOrderDfsIterator. - idx_ = start_idx_; - block_id_list_ = &mir_graph->GetDfsPostOrder(); - } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) { - // Update changed: if had_changed is true, we remember it for the whole iteration. - changed_ |= had_change; - - return ForwardRepeatNext(); - } - }; - - /** - * @class ReversePostOrderDfsIterator - * @brief Used to perform a Reverse Post-order Depth-First-Search Iteration of a MIRGraph. - */ - class ReversePostOrderDfsIterator : public DataflowIterator { - public: - /** - * @brief The constructor, using all of the reachable blocks of the MIRGraph. - * @param mir_graph The MIRGraph considered. - */ - explicit ReversePostOrderDfsIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, mir_graph->GetNumReachableBlocks() -1, 0) { - // Extra setup for the ReversePostOrderDfsIterator. - idx_ = start_idx_; - block_id_list_ = &mir_graph->GetDfsPostOrder(); - } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) { - // Update changed: if had_changed is true, we remember it for the whole iteration. - changed_ |= had_change; - - return ReverseSingleNext(); - } - }; - - /** - * @class ReversePostOrderDfsIterator - * @brief Used to perform a Repeating Reverse Post-order Depth-First-Search Iteration of a MIRGraph. - * @details If there is a change during an iteration, the iteration starts over at the end of the iteration. - */ - class RepeatingReversePostOrderDfsIterator : public DataflowIterator { - public: - /** - * @brief The constructor, using all of the reachable blocks of the MIRGraph. - * @param mir_graph The MIRGraph considered. - */ - explicit RepeatingReversePostOrderDfsIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, mir_graph->GetNumReachableBlocks() -1, 0) { - // Extra setup for the RepeatingReversePostOrderDfsIterator - idx_ = start_idx_; - block_id_list_ = &mir_graph->GetDfsPostOrder(); - } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) { - // Update changed: if had_changed is true, we remember it for the whole iteration. - changed_ |= had_change; - - return ReverseRepeatNext(); - } - }; - - /** - * @class PostOrderDOMIterator - * @brief Used to perform a Post-order Domination Iteration of a MIRGraph. - */ - class PostOrderDOMIterator : public DataflowIterator { - public: - /** - * @brief The constructor, using all of the reachable blocks of the MIRGraph. - * @param mir_graph The MIRGraph considered. - */ - explicit PostOrderDOMIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) { - // Extra setup for thePostOrderDOMIterator. - idx_ = start_idx_; - block_id_list_ = &mir_graph->GetDomPostOrder(); - } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) { - // Update changed: if had_changed is true, we remember it for the whole iteration. - changed_ |= had_change; - - return ForwardSingleNext(); - } - }; - - /** - * @class AllNodesIterator - * @brief Used to perform an iteration on all the BasicBlocks a MIRGraph. - */ - class AllNodesIterator : public DataflowIterator { - public: - /** - * @brief The constructor, using all of the reachable blocks of the MIRGraph. - * @param mir_graph The MIRGraph considered. - */ - explicit AllNodesIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, 0, mir_graph->GetBlockList().size()) { - } - - /** - * @brief Resetting the iterator. - */ - void Reset() { - idx_ = 0; - } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) ALWAYS_INLINE; - }; - - /** - * @class TopologicalSortIterator - * @brief Used to perform a Topological Sort Iteration of a MIRGraph. - */ - class TopologicalSortIterator : public DataflowIterator { - public: - /** - * @brief The constructor, using all of the reachable blocks of the MIRGraph. - * @param mir_graph The MIRGraph considered. - */ - explicit TopologicalSortIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder().size()), - loop_ends_(&mir_graph->GetTopologicalSortOrderLoopEnds()), - loop_head_stack_(mir_graph_->GetTopologicalSortOrderLoopHeadStack()) { - // Extra setup for TopologicalSortIterator. - idx_ = start_idx_; - block_id_list_ = &mir_graph->GetTopologicalSortOrder(); - } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) OVERRIDE; - - private: - const ArenaVector* const loop_ends_; - ArenaVector>* const loop_head_stack_; - }; - - /** - * @class LoopRepeatingTopologicalSortIterator - * @brief Used to perform a Topological Sort Iteration of a MIRGraph, repeating loops as needed. - * @details The iterator uses the visited flags to keep track of the blocks that need - * recalculation and keeps a stack of loop heads in the MIRGraph. At the end of the loop - * it returns back to the loop head if it needs to be recalculated. Due to the use of - * the visited flags and the loop head stack in the MIRGraph, it's not possible to use - * two iterators at the same time or modify this data during iteration (though inspection - * of this data is allowed and sometimes even expected). - * - * NOTE: This iterator is not suitable for passes that need to propagate changes to - * predecessors, such as type inferrence. - */ - class LoopRepeatingTopologicalSortIterator : public DataflowIterator { - public: - /** - * @brief The constructor, using all of the reachable blocks of the MIRGraph. - * @param mir_graph The MIRGraph considered. - */ - explicit LoopRepeatingTopologicalSortIterator(MIRGraph* mir_graph) - : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder().size()), - loop_ends_(&mir_graph->GetTopologicalSortOrderLoopEnds()), - loop_head_stack_(mir_graph_->GetTopologicalSortOrderLoopHeadStack()) { - // Extra setup for RepeatingTopologicalSortIterator. - idx_ = start_idx_; - block_id_list_ = &mir_graph->GetTopologicalSortOrder(); - // Clear visited flags and check that the loop head stack is empty. - mir_graph->ClearAllVisitedFlags(); - DCHECK_EQ(loop_head_stack_->size(), 0u); - } - - ~LoopRepeatingTopologicalSortIterator() { - DCHECK_EQ(loop_head_stack_->size(), 0u); - } - - /** - * @brief Get the next BasicBlock depending on iteration order. - * @param had_change did the user of the iteration change the previous BasicBlock. - * @return the next BasicBlock following the iteration order, 0 if finished. - */ - virtual BasicBlock* Next(bool had_change = false) OVERRIDE; - - private: - const ArenaVector* const loop_ends_; - ArenaVector>* const loop_head_stack_; - }; - -} // namespace art - -#endif // ART_COMPILER_DEX_DATAFLOW_ITERATOR_H_ diff --git a/compiler/dex/dex_flags.h b/compiler/dex/dex_flags.h deleted file mode 100644 index e8eb40ccd2..0000000000 --- a/compiler/dex/dex_flags.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_DEX_FLAGS_H_ -#define ART_COMPILER_DEX_DEX_FLAGS_H_ - -namespace art { - -// Suppress optimization if corresponding bit set. -enum OptControlVector { - kLoadStoreElimination = 0, - kLoadHoisting, - kSuppressLoads, - kNullCheckElimination, - kClassInitCheckElimination, - kGlobalValueNumbering, - kGvnDeadCodeElimination, - kLocalValueNumbering, - kPromoteRegs, - kTrackLiveTemps, - kSafeOptimizations, - kBBOpt, - kSuspendCheckElimination, - kMatch, - kPromoteCompilerTemps, - kBranchFusing, - kSuppressExceptionEdges, - kSuppressMethodInlining, -}; - -// Force code generation paths for testing. -enum DebugControlVector { - kDebugVerbose, - kDebugDumpCFG, - kDebugSlowFieldPath, - kDebugSlowInvokePath, - kDebugSlowStringPath, - kDebugSlowTypePath, - kDebugSlowestFieldPath, - kDebugSlowestStringPath, - kDebugExerciseResolveMethod, - kDebugVerifyDataflow, - kDebugShowMemoryUsage, - kDebugShowNops, - kDebugCountOpcodes, - kDebugDumpCheckStats, - kDebugShowSummaryMemoryUsage, - kDebugShowFilterStats, - kDebugTimings, - kDebugCodegenDump -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_DEX_FLAGS_H_ diff --git a/compiler/dex/dex_types.h b/compiler/dex/dex_types.h deleted file mode 100644 index f485c1c345..0000000000 --- a/compiler/dex/dex_types.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_DEX_TYPES_H_ -#define ART_COMPILER_DEX_DEX_TYPES_H_ - -namespace art { - -typedef uint32_t DexOffset; // Dex offset in code units. -typedef uint16_t NarrowDexOffset; // For use in structs, Dex offsets range from 0 .. 0xffff. - -} // namespace art - -#endif // ART_COMPILER_DEX_DEX_TYPES_H_ diff --git a/compiler/dex/global_value_numbering.cc b/compiler/dex/global_value_numbering.cc deleted file mode 100644 index 94ba4fad2a..0000000000 --- a/compiler/dex/global_value_numbering.cc +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "global_value_numbering.h" - -#include "base/bit_vector-inl.h" -#include "base/stl_util.h" -#include "local_value_numbering.h" - -namespace art { - -GlobalValueNumbering::GlobalValueNumbering(CompilationUnit* cu, ScopedArenaAllocator* allocator, - Mode mode) - : cu_(cu), - mir_graph_(cu->mir_graph.get()), - allocator_(allocator), - bbs_processed_(0u), - max_bbs_to_process_(kMaxBbsToProcessMultiplyFactor * mir_graph_->GetNumReachableBlocks()), - last_value_(kNullValue), - modifications_allowed_(true), - mode_(mode), - global_value_map_(std::less(), allocator->Adapter()), - array_location_map_(ArrayLocationComparator(), allocator->Adapter()), - array_location_reverse_map_(allocator->Adapter()), - ref_set_map_(std::less(), allocator->Adapter()), - lvns_(mir_graph_->GetNumBlocks(), nullptr, allocator->Adapter()), - work_lvn_(nullptr), - merge_lvns_(allocator->Adapter()) { -} - -GlobalValueNumbering::~GlobalValueNumbering() { - STLDeleteElements(&lvns_); -} - -LocalValueNumbering* GlobalValueNumbering::PrepareBasicBlock(BasicBlock* bb, - ScopedArenaAllocator* allocator) { - if (UNLIKELY(!Good())) { - return nullptr; - } - if (bb->block_type != kDalvikByteCode && bb->block_type != kEntryBlock) { - DCHECK(bb->first_mir_insn == nullptr); - return nullptr; - } - if (mode_ == kModeGvn && UNLIKELY(bbs_processed_ == max_bbs_to_process_)) { - // If we're still trying to converge, stop now. Otherwise, proceed to apply optimizations. - last_value_ = kNoValue; // Make bad. - return nullptr; - } - if (mode_ == kModeGvnPostProcessing && - mir_graph_->GetTopologicalSortOrderLoopHeadStack()->empty()) { - // Modifications outside loops are performed during the main phase. - return nullptr; - } - if (allocator == nullptr) { - allocator = allocator_; - } - DCHECK(work_lvn_.get() == nullptr); - work_lvn_.reset(new (allocator) LocalValueNumbering(this, bb->id, allocator)); - if (bb->block_type == kEntryBlock) { - work_lvn_->PrepareEntryBlock(); - DCHECK(bb->first_mir_insn == nullptr); // modifications_allowed_ is irrelevant. - } else { - // To avoid repeated allocation on the ArenaStack, reuse a single vector kept as a member. - DCHECK(merge_lvns_.empty()); - // If we're running the full GVN, the RepeatingTopologicalSortIterator keeps the loop - // head stack in the MIRGraph up to date and for a loop head we need to check whether - // we're making the initial computation and need to merge only preceding blocks in the - // topological order, or we're recalculating a loop head and need to merge all incoming - // LVNs. When we're not at a loop head (including having an empty loop head stack) all - // predecessors should be preceding blocks and we shall merge all of them anyway. - bool use_all_predecessors = true; - uint16_t loop_head_idx = 0u; // Used only if !use_all_predecessors. - if (mode_ == kModeGvn && mir_graph_->GetTopologicalSortOrderLoopHeadStack()->size() != 0) { - // Full GVN inside a loop, see if we're at the loop head for the first time. - modifications_allowed_ = false; - auto top = mir_graph_->GetTopologicalSortOrderLoopHeadStack()->back(); - loop_head_idx = top.first; - bool recalculating = top.second; - use_all_predecessors = recalculating || - loop_head_idx != mir_graph_->GetTopologicalSortOrderIndexes()[bb->id]; - } else { - modifications_allowed_ = true; - } - for (BasicBlockId pred_id : bb->predecessors) { - DCHECK_NE(pred_id, NullBasicBlockId); - if (lvns_[pred_id] != nullptr && - (use_all_predecessors || - mir_graph_->GetTopologicalSortOrderIndexes()[pred_id] < loop_head_idx)) { - merge_lvns_.push_back(lvns_[pred_id]); - } - } - // Determine merge type. - LocalValueNumbering::MergeType merge_type = LocalValueNumbering::kNormalMerge; - if (bb->catch_entry) { - merge_type = LocalValueNumbering::kCatchMerge; - } else if (bb->last_mir_insn != nullptr && - IsInstructionReturn(bb->last_mir_insn->dalvikInsn.opcode) && - bb->GetFirstNonPhiInsn() == bb->last_mir_insn) { - merge_type = LocalValueNumbering::kReturnMerge; - } - // At least one predecessor must have been processed before this bb. - CHECK(!merge_lvns_.empty()); - if (merge_lvns_.size() == 1u) { - work_lvn_->MergeOne(*merge_lvns_[0], merge_type); - } else { - work_lvn_->Merge(merge_type); - } - } - return work_lvn_.get(); -} - -bool GlobalValueNumbering::FinishBasicBlock(BasicBlock* bb) { - DCHECK(work_lvn_ != nullptr); - DCHECK_EQ(bb->id, work_lvn_->Id()); - ++bbs_processed_; - merge_lvns_.clear(); - - bool change = false; - if (mode_ == kModeGvn) { - change = (lvns_[bb->id] == nullptr) || !lvns_[bb->id]->Equals(*work_lvn_); - // In GVN mode, keep the latest LVN even if Equals() indicates no change. This is - // to keep the correct values of fields that do not contribute to Equals() as long - // as they depend only on predecessor LVNs' fields that do contribute to Equals(). - // Currently, that's LVN::merge_map_ used by LVN::GetStartingVregValueNumberImpl(). - std::unique_ptr old_lvn(lvns_[bb->id]); - lvns_[bb->id] = work_lvn_.release(); - } else { - DCHECK_EQ(mode_, kModeGvnPostProcessing); // kModeLvn doesn't use FinishBasicBlock(). - DCHECK(lvns_[bb->id] != nullptr); - DCHECK(lvns_[bb->id]->Equals(*work_lvn_)); - work_lvn_.reset(); - } - return change; -} - -uint16_t GlobalValueNumbering::GetArrayLocation(uint16_t base, uint16_t index) { - auto cmp = array_location_map_.key_comp(); - ArrayLocation key = { base, index }; - auto lb = array_location_map_.lower_bound(key); - if (lb != array_location_map_.end() && !cmp(key, lb->first)) { - return lb->second; - } - uint16_t location = static_cast(array_location_reverse_map_.size()); - DCHECK_EQ(location, array_location_reverse_map_.size()); // No overflow. - auto it = array_location_map_.PutBefore(lb, key, location); - array_location_reverse_map_.push_back(&*it); - return location; -} - -bool GlobalValueNumbering::NullCheckedInAllPredecessors( - const ScopedArenaVector& merge_names) const { - // Implicit parameters: - // - *work_lvn_: the LVN for which we're checking predecessors. - // - merge_lvns_: the predecessor LVNs. - DCHECK_EQ(merge_lvns_.size(), merge_names.size()); - for (size_t i = 0, size = merge_lvns_.size(); i != size; ++i) { - const LocalValueNumbering* pred_lvn = merge_lvns_[i]; - uint16_t value_name = merge_names[i]; - if (!pred_lvn->IsValueNullChecked(value_name)) { - // Check if the predecessor has an IF_EQZ/IF_NEZ as the last insn. - const BasicBlock* pred_bb = mir_graph_->GetBasicBlock(pred_lvn->Id()); - if (!HasNullCheckLastInsn(pred_bb, work_lvn_->Id())) { - return false; - } - // IF_EQZ/IF_NEZ checks some sreg, see if that sreg contains the value_name. - int s_reg = pred_bb->last_mir_insn->ssa_rep->uses[0]; - if (pred_lvn->GetSregValue(s_reg) != value_name) { - return false; - } - } - } - return true; -} - -bool GlobalValueNumbering::DivZeroCheckedInAllPredecessors( - const ScopedArenaVector& merge_names) const { - // Implicit parameters: - // - *work_lvn_: the LVN for which we're checking predecessors. - // - merge_lvns_: the predecessor LVNs. - DCHECK_EQ(merge_lvns_.size(), merge_names.size()); - for (size_t i = 0, size = merge_lvns_.size(); i != size; ++i) { - const LocalValueNumbering* pred_lvn = merge_lvns_[i]; - uint16_t value_name = merge_names[i]; - if (!pred_lvn->IsValueDivZeroChecked(value_name)) { - return false; - } - } - return true; -} - -bool GlobalValueNumbering::IsBlockEnteredOnTrue(uint16_t cond, BasicBlockId bb_id) { - DCHECK_NE(cond, kNoValue); - BasicBlock* bb = mir_graph_->GetBasicBlock(bb_id); - if (bb->predecessors.size() == 1u) { - BasicBlockId pred_id = bb->predecessors[0]; - BasicBlock* pred_bb = mir_graph_->GetBasicBlock(pred_id); - if (pred_bb->BranchesToSuccessorOnlyIfNotZero(bb_id)) { - DCHECK(lvns_[pred_id] != nullptr); - uint16_t operand = lvns_[pred_id]->GetSregValue(pred_bb->last_mir_insn->ssa_rep->uses[0]); - if (operand == cond) { - return true; - } - } - } - return false; -} - -bool GlobalValueNumbering::IsTrueInBlock(uint16_t cond, BasicBlockId bb_id) { - // We're not doing proper value propagation, so just see if the condition is used - // with if-nez/if-eqz to branch/fall-through to this bb or one of its dominators. - DCHECK_NE(cond, kNoValue); - if (IsBlockEnteredOnTrue(cond, bb_id)) { - return true; - } - BasicBlock* bb = mir_graph_->GetBasicBlock(bb_id); - for (uint32_t dom_id : bb->dominators->Indexes()) { - if (IsBlockEnteredOnTrue(cond, dom_id)) { - return true; - } - } - return false; -} - -} // namespace art diff --git a/compiler/dex/global_value_numbering.h b/compiler/dex/global_value_numbering.h deleted file mode 100644 index c514f75dcc..0000000000 --- a/compiler/dex/global_value_numbering.h +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_GLOBAL_VALUE_NUMBERING_H_ -#define ART_COMPILER_DEX_GLOBAL_VALUE_NUMBERING_H_ - -#include "base/arena_object.h" -#include "base/logging.h" -#include "base/macros.h" -#include "mir_graph.h" -#include "compiler_ir.h" -#include "dex_flags.h" - -namespace art { - -class LocalValueNumbering; -class MirFieldInfo; - -class GlobalValueNumbering : public DeletableArenaObject { - public: - static constexpr uint16_t kNoValue = 0xffffu; - static constexpr uint16_t kNullValue = 1u; - - enum Mode { - kModeGvn, - kModeGvnPostProcessing, - kModeLvn - }; - - static bool Skip(CompilationUnit* cu) { - return (cu->disable_opt & (1u << kGlobalValueNumbering)) != 0u || - cu->mir_graph->GetMaxNestedLoops() > kMaxAllowedNestedLoops; - } - - // Instance and static field id map is held by MIRGraph to avoid multiple recalculations - // when doing LVN. - template // Container of MirIFieldLoweringInfo or MirSFieldLoweringInfo. - static uint16_t* PrepareGvnFieldIds(ScopedArenaAllocator* allocator, - const Container& field_infos); - - GlobalValueNumbering(CompilationUnit* cu, ScopedArenaAllocator* allocator, Mode mode); - ~GlobalValueNumbering(); - - CompilationUnit* GetCompilationUnit() const { - return cu_; - } - - MIRGraph* GetMirGraph() const { - return mir_graph_; - } - - // Prepare LVN for the basic block. - LocalValueNumbering* PrepareBasicBlock(BasicBlock* bb, - ScopedArenaAllocator* allocator = nullptr); - - // Finish processing the basic block. - bool FinishBasicBlock(BasicBlock* bb); - - // Checks that the value names didn't overflow. - bool Good() const { - return last_value_ < kNoValue; - } - - // Allow modifications. - void StartPostProcessing(); - - bool CanModify() const { - return modifications_allowed_ && Good(); - } - - // Retrieve the LVN with GVN results for a given BasicBlock. - const LocalValueNumbering* GetLvn(BasicBlockId bb_id) const; - - private: - // Allocate a new value name. - uint16_t NewValueName(); - - // Key is concatenation of opcode, operand1, operand2 and modifier, value is value name. - typedef ScopedArenaSafeMap ValueMap; - - static uint64_t BuildKey(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) { - return (static_cast(op) << 48 | static_cast(operand1) << 32 | - static_cast(operand2) << 16 | static_cast(modifier)); - } - - // Look up a value in the global value map, adding a new entry if there was none before. - uint16_t LookupValue(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) { - uint16_t res; - uint64_t key = BuildKey(op, operand1, operand2, modifier); - auto lb = global_value_map_.lower_bound(key); - if (lb != global_value_map_.end() && lb->first == key) { - res = lb->second; - } else { - res = NewValueName(); - global_value_map_.PutBefore(lb, key, res); - } - return res; - } - - // Look up a value in the global value map, don't add a new entry if there was none before. - uint16_t FindValue(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) const { - uint16_t res; - uint64_t key = BuildKey(op, operand1, operand2, modifier); - auto lb = global_value_map_.lower_bound(key); - if (lb != global_value_map_.end() && lb->first == key) { - res = lb->second; - } else { - res = kNoValue; - } - return res; - } - - // Get an instance field id. - uint16_t GetIFieldId(MIR* mir) { - return GetMirGraph()->GetGvnIFieldId(mir); - } - - // Get a static field id. - uint16_t GetSFieldId(MIR* mir) { - return GetMirGraph()->GetGvnSFieldId(mir); - } - - // Get an instance field type based on field id. - uint16_t GetIFieldType(uint16_t field_id) { - return static_cast(GetMirGraph()->GetIFieldLoweringInfo(field_id).MemAccessType()); - } - - // Get a static field type based on field id. - uint16_t GetSFieldType(uint16_t field_id) { - return static_cast(GetMirGraph()->GetSFieldLoweringInfo(field_id).MemAccessType()); - } - - struct ArrayLocation { - uint16_t base; - uint16_t index; - }; - - struct ArrayLocationComparator { - bool operator()(const ArrayLocation& lhs, const ArrayLocation& rhs) const { - if (lhs.base != rhs.base) { - return lhs.base < rhs.base; - } - return lhs.index < rhs.index; - } - }; - - typedef ScopedArenaSafeMap ArrayLocationMap; - - // Get an array location. - uint16_t GetArrayLocation(uint16_t base, uint16_t index); - - // Get the array base from an array location. - uint16_t GetArrayLocationBase(uint16_t location) const { - return array_location_reverse_map_[location]->first.base; - } - - // Get the array index from an array location. - uint16_t GetArrayLocationIndex(uint16_t location) const { - return array_location_reverse_map_[location]->first.index; - } - - // A set of value names. - typedef ScopedArenaSet ValueNameSet; - - // A map from a set of references to the set id. - typedef ScopedArenaSafeMap RefSetIdMap; - - uint16_t GetRefSetId(const ValueNameSet& ref_set) { - uint16_t res = kNoValue; - auto lb = ref_set_map_.lower_bound(ref_set); - if (lb != ref_set_map_.end() && !ref_set_map_.key_comp()(ref_set, lb->first)) { - res = lb->second; - } else { - res = NewValueName(); - ref_set_map_.PutBefore(lb, ref_set, res); - } - return res; - } - - const BasicBlock* GetBasicBlock(uint16_t bb_id) const { - return mir_graph_->GetBasicBlock(bb_id); - } - - static bool HasNullCheckLastInsn(const BasicBlock* pred_bb, BasicBlockId succ_id) { - return pred_bb->BranchesToSuccessorOnlyIfNotZero(succ_id); - } - - bool NullCheckedInAllPredecessors(const ScopedArenaVector& merge_names) const; - - bool DivZeroCheckedInAllPredecessors(const ScopedArenaVector& merge_names) const; - - bool IsBlockEnteredOnTrue(uint16_t cond, BasicBlockId bb_id); - bool IsTrueInBlock(uint16_t cond, BasicBlockId bb_id); - - ScopedArenaAllocator* Allocator() const { - return allocator_; - } - - CompilationUnit* const cu_; - MIRGraph* const mir_graph_; - ScopedArenaAllocator* const allocator_; - - // The maximum number of nested loops that we accept for GVN. - static constexpr size_t kMaxAllowedNestedLoops = 6u; - - // The number of BBs that we need to process grows exponentially with the number - // of nested loops. Don't allow excessive processing for too many nested loops or - // otherwise expensive methods. - static constexpr uint32_t kMaxBbsToProcessMultiplyFactor = 20u; - - uint32_t bbs_processed_; - uint32_t max_bbs_to_process_; // Doesn't apply after the main GVN has converged. - - // We have 32-bit last_value_ so that we can detect when we run out of value names, see Good(). - // We usually don't check Good() until the end of LVN unless we're about to modify code. - uint32_t last_value_; - - // Marks whether code modifications are allowed. The initial GVN is done without code - // modifications to settle the value names. Afterwards, we allow modifications and rerun - // LVN once for each BasicBlock. - bool modifications_allowed_; - - // Specifies the mode of operation. - Mode mode_; - - ValueMap global_value_map_; - ArrayLocationMap array_location_map_; - ScopedArenaVector array_location_reverse_map_; - RefSetIdMap ref_set_map_; - - ScopedArenaVector lvns_; // Owning. - std::unique_ptr work_lvn_; - ScopedArenaVector merge_lvns_; // Not owning. - - friend class LocalValueNumbering; - friend class GlobalValueNumberingTest; - - DISALLOW_COPY_AND_ASSIGN(GlobalValueNumbering); -}; -std::ostream& operator<<(std::ostream& os, const GlobalValueNumbering::Mode& rhs); - -inline const LocalValueNumbering* GlobalValueNumbering::GetLvn(BasicBlockId bb_id) const { - DCHECK_EQ(mode_, kModeGvnPostProcessing); - DCHECK_LT(bb_id, lvns_.size()); - DCHECK(lvns_[bb_id] != nullptr); - return lvns_[bb_id]; -} - -inline void GlobalValueNumbering::StartPostProcessing() { - DCHECK(Good()); - DCHECK_EQ(mode_, kModeGvn); - mode_ = kModeGvnPostProcessing; -} - -inline uint16_t GlobalValueNumbering::NewValueName() { - DCHECK_NE(mode_, kModeGvnPostProcessing); - ++last_value_; - return last_value_; -} - -template // Container of MirIFieldLoweringInfo or MirSFieldLoweringInfo. -uint16_t* GlobalValueNumbering::PrepareGvnFieldIds(ScopedArenaAllocator* allocator, - const Container& field_infos) { - size_t size = field_infos.size(); - uint16_t* field_ids = allocator->AllocArray(size, kArenaAllocMisc); - for (size_t i = 0u; i != size; ++i) { - size_t idx = i; - const MirFieldInfo& cur_info = field_infos[i]; - if (cur_info.IsResolved()) { - for (size_t j = 0; j != i; ++j) { - const MirFieldInfo& prev_info = field_infos[j]; - if (prev_info.IsResolved() && - prev_info.DeclaringDexFile() == cur_info.DeclaringDexFile() && - prev_info.DeclaringFieldIndex() == cur_info.DeclaringFieldIndex()) { - DCHECK_EQ(cur_info.MemAccessType(), prev_info.MemAccessType()); - idx = j; - break; - } - } - } - field_ids[i] = idx; - } - return field_ids; -} - -} // namespace art - -#endif // ART_COMPILER_DEX_GLOBAL_VALUE_NUMBERING_H_ diff --git a/compiler/dex/global_value_numbering_test.cc b/compiler/dex/global_value_numbering_test.cc deleted file mode 100644 index 7d647e5c3b..0000000000 --- a/compiler/dex/global_value_numbering_test.cc +++ /dev/null @@ -1,2428 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "base/logging.h" -#include "dataflow_iterator-inl.h" -#include "dex/mir_field_info.h" -#include "global_value_numbering.h" -#include "local_value_numbering.h" -#include "gtest/gtest.h" - -namespace art { - -class GlobalValueNumberingTest : public testing::Test { - protected: - static constexpr uint16_t kNoValue = GlobalValueNumbering::kNoValue; - - struct IFieldDef { - uint16_t field_idx; - uintptr_t declaring_dex_file; - uint16_t declaring_field_idx; - bool is_volatile; - DexMemAccessType type; - }; - - struct SFieldDef { - uint16_t field_idx; - uintptr_t declaring_dex_file; - uint16_t declaring_field_idx; - bool is_volatile; - DexMemAccessType type; - }; - - struct BBDef { - static constexpr size_t kMaxSuccessors = 4; - static constexpr size_t kMaxPredecessors = 4; - - BBType type; - size_t num_successors; - BasicBlockId successors[kMaxPredecessors]; - size_t num_predecessors; - BasicBlockId predecessors[kMaxPredecessors]; - }; - - struct MIRDef { - static constexpr size_t kMaxSsaDefs = 2; - static constexpr size_t kMaxSsaUses = 4; - - BasicBlockId bbid; - Instruction::Code opcode; - int64_t value; - uint32_t field_info; - size_t num_uses; - int32_t uses[kMaxSsaUses]; - size_t num_defs; - int32_t defs[kMaxSsaDefs]; - }; - -#define DEF_SUCC0() \ - 0u, { } -#define DEF_SUCC1(s1) \ - 1u, { s1 } -#define DEF_SUCC2(s1, s2) \ - 2u, { s1, s2 } -#define DEF_SUCC3(s1, s2, s3) \ - 3u, { s1, s2, s3 } -#define DEF_SUCC4(s1, s2, s3, s4) \ - 4u, { s1, s2, s3, s4 } -#define DEF_PRED0() \ - 0u, { } -#define DEF_PRED1(p1) \ - 1u, { p1 } -#define DEF_PRED2(p1, p2) \ - 2u, { p1, p2 } -#define DEF_PRED3(p1, p2, p3) \ - 3u, { p1, p2, p3 } -#define DEF_PRED4(p1, p2, p3, p4) \ - 4u, { p1, p2, p3, p4 } -#define DEF_BB(type, succ, pred) \ - { type, succ, pred } - -#define DEF_CONST(bb, opcode, reg, value) \ - { bb, opcode, value, 0u, 0, { }, 1, { reg } } -#define DEF_CONST_WIDE(bb, opcode, reg, value) \ - { bb, opcode, value, 0u, 0, { }, 2, { reg, reg + 1 } } -#define DEF_CONST_STRING(bb, opcode, reg, index) \ - { bb, opcode, index, 0u, 0, { }, 1, { reg } } -#define DEF_IGET(bb, opcode, reg, obj, field_info) \ - { bb, opcode, 0u, field_info, 1, { obj }, 1, { reg } } -#define DEF_IGET_WIDE(bb, opcode, reg, obj, field_info) \ - { bb, opcode, 0u, field_info, 1, { obj }, 2, { reg, reg + 1 } } -#define DEF_IPUT(bb, opcode, reg, obj, field_info) \ - { bb, opcode, 0u, field_info, 2, { reg, obj }, 0, { } } -#define DEF_IPUT_WIDE(bb, opcode, reg, obj, field_info) \ - { bb, opcode, 0u, field_info, 3, { reg, reg + 1, obj }, 0, { } } -#define DEF_SGET(bb, opcode, reg, field_info) \ - { bb, opcode, 0u, field_info, 0, { }, 1, { reg } } -#define DEF_SGET_WIDE(bb, opcode, reg, field_info) \ - { bb, opcode, 0u, field_info, 0, { }, 2, { reg, reg + 1 } } -#define DEF_SPUT(bb, opcode, reg, field_info) \ - { bb, opcode, 0u, field_info, 1, { reg }, 0, { } } -#define DEF_SPUT_WIDE(bb, opcode, reg, field_info) \ - { bb, opcode, 0u, field_info, 2, { reg, reg + 1 }, 0, { } } -#define DEF_AGET(bb, opcode, reg, obj, idx) \ - { bb, opcode, 0u, 0u, 2, { obj, idx }, 1, { reg } } -#define DEF_AGET_WIDE(bb, opcode, reg, obj, idx) \ - { bb, opcode, 0u, 0u, 2, { obj, idx }, 2, { reg, reg + 1 } } -#define DEF_APUT(bb, opcode, reg, obj, idx) \ - { bb, opcode, 0u, 0u, 3, { reg, obj, idx }, 0, { } } -#define DEF_APUT_WIDE(bb, opcode, reg, obj, idx) \ - { bb, opcode, 0u, 0u, 4, { reg, reg + 1, obj, idx }, 0, { } } -#define DEF_INVOKE1(bb, opcode, reg) \ - { bb, opcode, 0u, 0u, 1, { reg }, 0, { } } -#define DEF_UNIQUE_REF(bb, opcode, reg) \ - { bb, opcode, 0u, 0u, 0, { }, 1, { reg } } // CONST_CLASS, CONST_STRING, NEW_ARRAY, ... -#define DEF_IFZ(bb, opcode, reg) \ - { bb, opcode, 0u, 0u, 1, { reg }, 0, { } } -#define DEF_MOVE(bb, opcode, reg, src) \ - { bb, opcode, 0u, 0u, 1, { src }, 1, { reg } } -#define DEF_MOVE_WIDE(bb, opcode, reg, src) \ - { bb, opcode, 0u, 0u, 2, { src, src + 1 }, 2, { reg, reg + 1 } } -#define DEF_PHI2(bb, reg, src1, src2) \ - { bb, static_cast(kMirOpPhi), 0, 0u, 2u, { src1, src2 }, 1, { reg } } -#define DEF_BINOP(bb, opcode, result, src1, src2) \ - { bb, opcode, 0u, 0u, 2, { src1, src2 }, 1, { result } } -#define DEF_UNOP(bb, opcode, result, src) DEF_MOVE(bb, opcode, result, src) - - void DoPrepareIFields(const IFieldDef* defs, size_t count) { - cu_.mir_graph->ifield_lowering_infos_.clear(); - cu_.mir_graph->ifield_lowering_infos_.reserve(count); - for (size_t i = 0u; i != count; ++i) { - const IFieldDef* def = &defs[i]; - MirIFieldLoweringInfo field_info(def->field_idx, def->type, false); - if (def->declaring_dex_file != 0u) { - field_info.declaring_dex_file_ = reinterpret_cast(def->declaring_dex_file); - field_info.declaring_field_idx_ = def->declaring_field_idx; - field_info.flags_ &= ~(def->is_volatile ? 0u : MirSFieldLoweringInfo::kFlagIsVolatile); - } - cu_.mir_graph->ifield_lowering_infos_.push_back(field_info); - } - } - - template - void PrepareIFields(const IFieldDef (&defs)[count]) { - DoPrepareIFields(defs, count); - } - - void DoPrepareSFields(const SFieldDef* defs, size_t count) { - cu_.mir_graph->sfield_lowering_infos_.clear(); - cu_.mir_graph->sfield_lowering_infos_.reserve(count); - for (size_t i = 0u; i != count; ++i) { - const SFieldDef* def = &defs[i]; - MirSFieldLoweringInfo field_info(def->field_idx, def->type); - // Mark even unresolved fields as initialized. - field_info.flags_ |= MirSFieldLoweringInfo::kFlagClassIsInitialized; - // NOTE: MirSFieldLoweringInfo::kFlagClassIsInDexCache isn't used by GVN. - if (def->declaring_dex_file != 0u) { - field_info.declaring_dex_file_ = reinterpret_cast(def->declaring_dex_file); - field_info.declaring_field_idx_ = def->declaring_field_idx; - field_info.flags_ &= ~(def->is_volatile ? 0u : MirSFieldLoweringInfo::kFlagIsVolatile); - } - cu_.mir_graph->sfield_lowering_infos_.push_back(field_info); - } - } - - template - void PrepareSFields(const SFieldDef (&defs)[count]) { - DoPrepareSFields(defs, count); - } - - void DoPrepareBasicBlocks(const BBDef* defs, size_t count) { - cu_.mir_graph->block_id_map_.clear(); - cu_.mir_graph->block_list_.clear(); - ASSERT_LT(3u, count); // null, entry, exit and at least one bytecode block. - ASSERT_EQ(kNullBlock, defs[0].type); - ASSERT_EQ(kEntryBlock, defs[1].type); - ASSERT_EQ(kExitBlock, defs[2].type); - for (size_t i = 0u; i != count; ++i) { - const BBDef* def = &defs[i]; - BasicBlock* bb = cu_.mir_graph->CreateNewBB(def->type); - if (def->num_successors <= 2) { - bb->successor_block_list_type = kNotUsed; - bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : 0u; - bb->taken = (def->num_successors >= 2) ? def->successors[1] : 0u; - } else { - bb->successor_block_list_type = kPackedSwitch; - bb->fall_through = 0u; - bb->taken = 0u; - bb->successor_blocks.reserve(def->num_successors); - for (size_t j = 0u; j != def->num_successors; ++j) { - SuccessorBlockInfo* successor_block_info = - static_cast(cu_.arena.Alloc(sizeof(SuccessorBlockInfo), - kArenaAllocSuccessors)); - successor_block_info->block = j; - successor_block_info->key = 0u; // Not used by class init check elimination. - bb->successor_blocks.push_back(successor_block_info); - } - } - bb->predecessors.assign(def->predecessors, def->predecessors + def->num_predecessors); - if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) { - bb->data_flow_info = static_cast( - cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo)); - bb->data_flow_info->live_in_v = live_in_v_; - } - } - ASSERT_EQ(count, cu_.mir_graph->block_list_.size()); - cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1]; - ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type); - cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_[2]; - ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type); - } - - template - void PrepareBasicBlocks(const BBDef (&defs)[count]) { - DoPrepareBasicBlocks(defs, count); - } - - void DoPrepareMIRs(const MIRDef* defs, size_t count) { - mir_count_ = count; - mirs_ = cu_.arena.AllocArray(count, kArenaAllocMIR); - ssa_reps_.resize(count); - for (size_t i = 0u; i != count; ++i) { - const MIRDef* def = &defs[i]; - MIR* mir = &mirs_[i]; - ASSERT_LT(def->bbid, cu_.mir_graph->block_list_.size()); - BasicBlock* bb = cu_.mir_graph->block_list_[def->bbid]; - bb->AppendMIR(mir); - mir->dalvikInsn.opcode = def->opcode; - mir->dalvikInsn.vB = static_cast(def->value); - mir->dalvikInsn.vB_wide = def->value; - if (IsInstructionIGetOrIPut(def->opcode)) { - ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.size()); - mir->meta.ifield_lowering_info = def->field_info; - ASSERT_EQ(cu_.mir_graph->ifield_lowering_infos_[def->field_info].MemAccessType(), - IGetOrIPutMemAccessType(def->opcode)); - } else if (IsInstructionSGetOrSPut(def->opcode)) { - ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.size()); - mir->meta.sfield_lowering_info = def->field_info; - ASSERT_EQ(cu_.mir_graph->sfield_lowering_infos_[def->field_info].MemAccessType(), - SGetOrSPutMemAccessType(def->opcode)); - } else if (def->opcode == static_cast(kMirOpPhi)) { - mir->meta.phi_incoming = - allocator_->AllocArray(def->num_uses, kArenaAllocDFInfo); - ASSERT_EQ(def->num_uses, bb->predecessors.size()); - std::copy(bb->predecessors.begin(), bb->predecessors.end(), mir->meta.phi_incoming); - } - mir->ssa_rep = &ssa_reps_[i]; - mir->ssa_rep->num_uses = def->num_uses; - mir->ssa_rep->uses = const_cast(def->uses); // Not modified by LVN. - mir->ssa_rep->num_defs = def->num_defs; - mir->ssa_rep->defs = const_cast(def->defs); // Not modified by LVN. - mir->dalvikInsn.opcode = def->opcode; - mir->offset = i; // LVN uses offset only for debug output - mir->optimization_flags = 0u; - } - DexFile::CodeItem* code_item = static_cast( - cu_.arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc)); - code_item->insns_size_in_code_units_ = 2u * count; - cu_.mir_graph->current_code_item_ = code_item; - } - - template - void PrepareMIRs(const MIRDef (&defs)[count]) { - DoPrepareMIRs(defs, count); - } - - void DoPrepareVregToSsaMapExit(BasicBlockId bb_id, const int32_t* map, size_t count) { - BasicBlock* bb = cu_.mir_graph->GetBasicBlock(bb_id); - ASSERT_TRUE(bb != nullptr); - ASSERT_TRUE(bb->data_flow_info != nullptr); - bb->data_flow_info->vreg_to_ssa_map_exit = - cu_.arena.AllocArray(count, kArenaAllocDFInfo); - std::copy_n(map, count, bb->data_flow_info->vreg_to_ssa_map_exit); - } - - template - void PrepareVregToSsaMapExit(BasicBlockId bb_id, const int32_t (&map)[count]) { - DoPrepareVregToSsaMapExit(bb_id, map, count); - } - - template - void MarkAsWideSRegs(const int32_t (&sregs)[count]) { - for (int32_t sreg : sregs) { - cu_.mir_graph->reg_location_[sreg].wide = true; - cu_.mir_graph->reg_location_[sreg + 1].wide = true; - cu_.mir_graph->reg_location_[sreg + 1].high_word = true; - } - } - - void PerformGVN() { - DoPerformGVN(); - } - - void PerformPreOrderDfsGVN() { - DoPerformGVN(); - } - - template - void DoPerformGVN() { - cu_.mir_graph->SSATransformationStart(); - cu_.mir_graph->ComputeDFSOrders(); - cu_.mir_graph->ComputeDominators(); - cu_.mir_graph->ComputeTopologicalSortOrder(); - cu_.mir_graph->SSATransformationEnd(); - cu_.mir_graph->temp_.gvn.ifield_ids = GlobalValueNumbering::PrepareGvnFieldIds( - allocator_.get(), cu_.mir_graph->ifield_lowering_infos_); - cu_.mir_graph->temp_.gvn.sfield_ids = GlobalValueNumbering::PrepareGvnFieldIds( - allocator_.get(), cu_.mir_graph->sfield_lowering_infos_); - ASSERT_TRUE(gvn_ == nullptr); - gvn_.reset(new (allocator_.get()) GlobalValueNumbering(&cu_, allocator_.get(), - GlobalValueNumbering::kModeGvn)); - value_names_.resize(mir_count_, 0xffffu); - IteratorType iterator(cu_.mir_graph.get()); - bool change = false; - for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) { - LocalValueNumbering* lvn = gvn_->PrepareBasicBlock(bb); - if (lvn != nullptr) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - value_names_[mir - mirs_] = lvn->GetValueNumber(mir); - } - } - change = (lvn != nullptr) && gvn_->FinishBasicBlock(bb); - ASSERT_TRUE(gvn_->Good()); - } - } - - void PerformGVNCodeModifications() { - ASSERT_TRUE(gvn_ != nullptr); - ASSERT_TRUE(gvn_->Good()); - gvn_->StartPostProcessing(); - TopologicalSortIterator iterator(cu_.mir_graph.get()); - for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) { - LocalValueNumbering* lvn = gvn_->PrepareBasicBlock(bb); - if (lvn != nullptr) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - uint16_t value_name = lvn->GetValueNumber(mir); - ASSERT_EQ(value_name, value_names_[mir - mirs_]); - } - } - bool change = (lvn != nullptr) && gvn_->FinishBasicBlock(bb); - ASSERT_FALSE(change); - ASSERT_TRUE(gvn_->Good()); - } - } - - GlobalValueNumberingTest() - : pool_(), - cu_(&pool_, kRuntimeISA, nullptr, nullptr), - mir_count_(0u), - mirs_(nullptr), - ssa_reps_(), - allocator_(), - gvn_(), - value_names_(), - live_in_v_(new (&cu_.arena) ArenaBitVector(&cu_.arena, kMaxSsaRegs, false)) { - cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); - cu_.access_flags = kAccStatic; // Don't let "this" interfere with this test. - allocator_.reset(ScopedArenaAllocator::Create(&cu_.arena_stack)); - // By default, the zero-initialized reg_location_[.] with ref == false tells LVN that - // 0 constants are integral, not references, and the values are all narrow. - // Nothing else is used by LVN/GVN. Tests can override the default values as needed. - cu_.mir_graph->reg_location_ = - cu_.arena.AllocArray(kMaxSsaRegs, kArenaAllocRegAlloc); - cu_.mir_graph->num_ssa_regs_ = kMaxSsaRegs; - // Bind all possible sregs to live vregs for test purposes. - live_in_v_->SetInitialBits(kMaxSsaRegs); - cu_.mir_graph->ssa_base_vregs_.reserve(kMaxSsaRegs); - cu_.mir_graph->ssa_subscripts_.reserve(kMaxSsaRegs); - for (unsigned int i = 0; i < kMaxSsaRegs; i++) { - cu_.mir_graph->ssa_base_vregs_.push_back(i); - cu_.mir_graph->ssa_subscripts_.push_back(0); - } - // Set shorty for a void-returning method without arguments. - cu_.shorty = "V"; - } - - static constexpr size_t kMaxSsaRegs = 16384u; - - ArenaPool pool_; - CompilationUnit cu_; - size_t mir_count_; - MIR* mirs_; - std::vector ssa_reps_; - std::unique_ptr allocator_; - std::unique_ptr gvn_; - std::vector value_names_; - ArenaBitVector* live_in_v_; -}; - -constexpr uint16_t GlobalValueNumberingTest::kNoValue; - -class GlobalValueNumberingTestDiamond : public GlobalValueNumberingTest { - public: - GlobalValueNumberingTestDiamond(); - - private: - static const BBDef kDiamondBbs[]; -}; - -const GlobalValueNumberingTest::BBDef GlobalValueNumberingTestDiamond::kDiamondBbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)), // Block #3, top of the diamond. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Block #4, left side. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Block #5, right side. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)), // Block #6, bottom. -}; - -GlobalValueNumberingTestDiamond::GlobalValueNumberingTestDiamond() - : GlobalValueNumberingTest() { - PrepareBasicBlocks(kDiamondBbs); -} - -class GlobalValueNumberingTestLoop : public GlobalValueNumberingTest { - public: - GlobalValueNumberingTestLoop(); - - private: - static const BBDef kLoopBbs[]; -}; - -const GlobalValueNumberingTest::BBDef GlobalValueNumberingTestLoop::kLoopBbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)), // "taken" loops to self. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), -}; - -GlobalValueNumberingTestLoop::GlobalValueNumberingTestLoop() - : GlobalValueNumberingTest() { - PrepareBasicBlocks(kLoopBbs); -} - -class GlobalValueNumberingTestCatch : public GlobalValueNumberingTest { - public: - GlobalValueNumberingTestCatch(); - - private: - static const BBDef kCatchBbs[]; -}; - -const GlobalValueNumberingTest::BBDef GlobalValueNumberingTestCatch::kCatchBbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), // The top. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // The throwing insn. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Catch handler. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)), // The merged block. -}; - -GlobalValueNumberingTestCatch::GlobalValueNumberingTestCatch() - : GlobalValueNumberingTest() { - PrepareBasicBlocks(kCatchBbs); - // Mark catch handler. - BasicBlock* catch_handler = cu_.mir_graph->GetBasicBlock(5u); - catch_handler->catch_entry = true; - // Add successor block info to the check block. - BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u); - check_bb->successor_block_list_type = kCatch; - SuccessorBlockInfo* successor_block_info = reinterpret_cast - (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors)); - successor_block_info->block = catch_handler->id; - check_bb->successor_blocks.push_back(successor_block_info); -} - -class GlobalValueNumberingTestTwoConsecutiveLoops : public GlobalValueNumberingTest { - public: - GlobalValueNumberingTestTwoConsecutiveLoops(); - - private: - static const BBDef kTwoConsecutiveLoopsBbs[]; -}; - -const GlobalValueNumberingTest::BBDef -GlobalValueNumberingTestTwoConsecutiveLoops::kTwoConsecutiveLoopsBbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(9)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 6), DEF_PRED2(3, 5)), // "taken" skips over the loop. - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(4)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(7), DEF_PRED1(4)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(8, 9), DEF_PRED2(6, 8)), // "taken" skips over the loop. - DEF_BB(kDalvikByteCode, DEF_SUCC1(7), DEF_PRED1(7)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(7)), -}; - -GlobalValueNumberingTestTwoConsecutiveLoops::GlobalValueNumberingTestTwoConsecutiveLoops() - : GlobalValueNumberingTest() { - PrepareBasicBlocks(kTwoConsecutiveLoopsBbs); -} - -class GlobalValueNumberingTestTwoNestedLoops : public GlobalValueNumberingTest { - public: - GlobalValueNumberingTestTwoNestedLoops(); - - private: - static const BBDef kTwoNestedLoopsBbs[]; -}; - -const GlobalValueNumberingTest::BBDef -GlobalValueNumberingTestTwoNestedLoops::kTwoNestedLoopsBbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(8)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 8), DEF_PRED2(3, 7)), // "taken" skips over the loop. - DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 7), DEF_PRED2(4, 6)), // "taken" skips over the loop. - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), -}; - -GlobalValueNumberingTestTwoNestedLoops::GlobalValueNumberingTestTwoNestedLoops() - : GlobalValueNumberingTest() { - PrepareBasicBlocks(kTwoNestedLoopsBbs); -} - -TEST_F(GlobalValueNumberingTestDiamond, NonAliasingIFields) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - { 3u, 1u, 3u, false, kDexMemAccessWord }, - { 4u, 1u, 4u, false, kDexMemAccessShort }, - { 5u, 1u, 5u, false, kDexMemAccessChar }, - { 6u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. - { 7u, 1u, 7u, false, kDexMemAccessWord }, - { 8u, 0u, 0u, false, kDexMemAccessWord }, // Unresolved. - { 9u, 1u, 9u, false, kDexMemAccessWord }, - { 10u, 1u, 10u, false, kDexMemAccessWord }, - { 11u, 1u, 11u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 100u), - DEF_IGET(3, Instruction::IGET, 1u, 100u, 0u), - DEF_IGET(6, Instruction::IGET, 2u, 100u, 0u), // Same as at the top. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 200u), - DEF_IGET(4, Instruction::IGET, 4u, 200u, 1u), - DEF_IGET(6, Instruction::IGET, 5u, 200u, 1u), // Same as at the left side. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 300u), - DEF_IGET(3, Instruction::IGET, 7u, 300u, 2u), - DEF_CONST(5, Instruction::CONST, 8u, 1000), - DEF_IPUT(5, Instruction::IPUT, 8u, 300u, 2u), - DEF_IGET(6, Instruction::IGET, 10u, 300u, 2u), // Differs from the top and the CONST. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 400u), - DEF_IGET(3, Instruction::IGET, 12u, 400u, 3u), - DEF_CONST(3, Instruction::CONST, 13u, 2000), - DEF_IPUT(4, Instruction::IPUT, 13u, 400u, 3u), - DEF_IPUT(5, Instruction::IPUT, 13u, 400u, 3u), - DEF_IGET(6, Instruction::IGET, 16u, 400u, 3u), // Differs from the top, equals the CONST. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 500u), - DEF_IGET(3, Instruction::IGET_SHORT, 18u, 500u, 4u), - DEF_IGET(3, Instruction::IGET_CHAR, 19u, 500u, 5u), - DEF_IPUT(4, Instruction::IPUT_SHORT, 20u, 500u, 6u), // Clobbers field #4, not #5. - DEF_IGET(6, Instruction::IGET_SHORT, 21u, 500u, 4u), // Differs from the top. - DEF_IGET(6, Instruction::IGET_CHAR, 22u, 500u, 5u), // Same as the top. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 600u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 601u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 602u), - DEF_IGET(3, Instruction::IGET, 26u, 600u, 7u), - DEF_IGET(3, Instruction::IGET, 27u, 601u, 7u), - DEF_IPUT(4, Instruction::IPUT, 28u, 602u, 8u), // Doesn't clobber field #7 for other refs. - DEF_IGET(6, Instruction::IGET, 29u, 600u, 7u), // Same as the top. - DEF_IGET(6, Instruction::IGET, 30u, 601u, 7u), // Same as the top. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 700u), - DEF_CONST(4, Instruction::CONST, 32u, 3000), - DEF_IPUT(4, Instruction::IPUT, 32u, 700u, 9u), - DEF_IPUT(4, Instruction::IPUT, 32u, 700u, 10u), - DEF_CONST(5, Instruction::CONST, 35u, 3001), - DEF_IPUT(5, Instruction::IPUT, 35u, 700u, 9u), - DEF_IPUT(5, Instruction::IPUT, 35u, 700u, 10u), - DEF_IGET(6, Instruction::IGET, 38u, 700u, 9u), - DEF_IGET(6, Instruction::IGET, 39u, 700u, 10u), // Same value as read from field #9. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 800u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 801u), - DEF_CONST(4, Instruction::CONST, 42u, 3000), - DEF_IPUT(4, Instruction::IPUT, 42u, 800u, 11u), - DEF_IPUT(4, Instruction::IPUT, 42u, 801u, 11u), - DEF_CONST(5, Instruction::CONST, 45u, 3001), - DEF_IPUT(5, Instruction::IPUT, 45u, 800u, 11u), - DEF_IPUT(5, Instruction::IPUT, 45u, 801u, 11u), - DEF_IGET(6, Instruction::IGET, 48u, 800u, 11u), - DEF_IGET(6, Instruction::IGET, 49u, 801u, 11u), // Same value as read from ref 46u. - - // Invoke doesn't interfere with non-aliasing refs. There's one test above where a reference - // escapes in the left BB (we let a reference escape if we use it to store to an unresolved - // field) and the INVOKE in the right BB shouldn't interfere with that either. - DEF_INVOKE1(5, Instruction::INVOKE_STATIC, 48u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[1], value_names_[2]); - - EXPECT_EQ(value_names_[4], value_names_[5]); - - EXPECT_NE(value_names_[7], value_names_[10]); - EXPECT_NE(value_names_[8], value_names_[10]); - - EXPECT_NE(value_names_[12], value_names_[16]); - EXPECT_EQ(value_names_[13], value_names_[16]); - - EXPECT_NE(value_names_[18], value_names_[21]); - EXPECT_EQ(value_names_[19], value_names_[22]); - - EXPECT_EQ(value_names_[26], value_names_[29]); - EXPECT_EQ(value_names_[27], value_names_[30]); - - EXPECT_EQ(value_names_[38], value_names_[39]); - - EXPECT_EQ(value_names_[48], value_names_[49]); -} - -TEST_F(GlobalValueNumberingTestDiamond, AliasingIFieldsSingleObject) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - { 3u, 1u, 3u, false, kDexMemAccessWord }, - { 4u, 1u, 4u, false, kDexMemAccessShort }, - { 5u, 1u, 5u, false, kDexMemAccessChar }, - { 6u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. - { 7u, 1u, 7u, false, kDexMemAccessWord }, - { 8u, 1u, 8u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_IGET(3, Instruction::IGET, 0u, 100u, 0u), - DEF_IGET(6, Instruction::IGET, 1u, 100u, 0u), // Same as at the top. - - DEF_IGET(4, Instruction::IGET, 2u, 100u, 1u), - DEF_IGET(6, Instruction::IGET, 3u, 100u, 1u), // Same as at the left side. - - DEF_IGET(3, Instruction::IGET, 4u, 100u, 2u), - DEF_CONST(5, Instruction::CONST, 5u, 1000), - DEF_IPUT(5, Instruction::IPUT, 5u, 100u, 2u), - DEF_IGET(6, Instruction::IGET, 7u, 100u, 2u), // Differs from the top and the CONST. - - DEF_IGET(3, Instruction::IGET, 8u, 100u, 3u), - DEF_CONST(3, Instruction::CONST, 9u, 2000), - DEF_IPUT(4, Instruction::IPUT, 9u, 100u, 3u), - DEF_IPUT(5, Instruction::IPUT, 9u, 100u, 3u), - DEF_IGET(6, Instruction::IGET, 12u, 100u, 3u), // Differs from the top, equals the CONST. - - DEF_IGET(3, Instruction::IGET_SHORT, 13u, 100u, 4u), - DEF_IGET(3, Instruction::IGET_CHAR, 14u, 100u, 5u), - DEF_IPUT(4, Instruction::IPUT_SHORT, 15u, 100u, 6u), // Clobbers field #4, not #5. - DEF_IGET(6, Instruction::IGET_SHORT, 16u, 100u, 4u), // Differs from the top. - DEF_IGET(6, Instruction::IGET_CHAR, 17u, 100u, 5u), // Same as the top. - - DEF_CONST(4, Instruction::CONST, 18u, 3000), - DEF_IPUT(4, Instruction::IPUT, 18u, 100u, 7u), - DEF_IPUT(4, Instruction::IPUT, 18u, 100u, 8u), - DEF_CONST(5, Instruction::CONST, 21u, 3001), - DEF_IPUT(5, Instruction::IPUT, 21u, 100u, 7u), - DEF_IPUT(5, Instruction::IPUT, 21u, 100u, 8u), - DEF_IGET(6, Instruction::IGET, 24u, 100u, 7u), - DEF_IGET(6, Instruction::IGET, 25u, 100u, 8u), // Same value as read from field #7. - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - - EXPECT_EQ(value_names_[2], value_names_[3]); - - EXPECT_NE(value_names_[4], value_names_[7]); - EXPECT_NE(value_names_[5], value_names_[7]); - - EXPECT_NE(value_names_[8], value_names_[12]); - EXPECT_EQ(value_names_[9], value_names_[12]); - - EXPECT_NE(value_names_[13], value_names_[16]); - EXPECT_EQ(value_names_[14], value_names_[17]); - - EXPECT_EQ(value_names_[24], value_names_[25]); -} - -TEST_F(GlobalValueNumberingTestDiamond, AliasingIFieldsTwoObjects) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - { 3u, 1u, 3u, false, kDexMemAccessWord }, - { 4u, 1u, 4u, false, kDexMemAccessShort }, - { 5u, 1u, 5u, false, kDexMemAccessChar }, - { 6u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. - { 7u, 1u, 7u, false, kDexMemAccessWord }, - { 8u, 1u, 8u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_IGET(3, Instruction::IGET, 0u, 100u, 0u), - DEF_IPUT(4, Instruction::IPUT, 1u, 101u, 0u), // May alias with the IGET at the top. - DEF_IGET(6, Instruction::IGET, 2u, 100u, 0u), // Differs from the top. - - DEF_IGET(3, Instruction::IGET, 3u, 100u, 1u), - DEF_IPUT(5, Instruction::IPUT, 3u, 101u, 1u), // If aliasing, stores the same value. - DEF_IGET(6, Instruction::IGET, 5u, 100u, 1u), // Same as the top. - - DEF_IGET(3, Instruction::IGET, 6u, 100u, 2u), - DEF_CONST(5, Instruction::CONST, 7u, 1000), - DEF_IPUT(5, Instruction::IPUT, 7u, 101u, 2u), - DEF_IGET(6, Instruction::IGET, 9u, 100u, 2u), // Differs from the top and the CONST. - - DEF_IGET(3, Instruction::IGET, 10u, 100u, 3u), - DEF_CONST(3, Instruction::CONST, 11u, 2000), - DEF_IPUT(4, Instruction::IPUT, 11u, 101u, 3u), - DEF_IPUT(5, Instruction::IPUT, 11u, 101u, 3u), - DEF_IGET(6, Instruction::IGET, 14u, 100u, 3u), // Differs from the top and the CONST. - - DEF_IGET(3, Instruction::IGET_SHORT, 15u, 100u, 4u), - DEF_IGET(3, Instruction::IGET_CHAR, 16u, 100u, 5u), - DEF_IPUT(4, Instruction::IPUT_SHORT, 17u, 101u, 6u), // Clobbers field #4, not #5. - DEF_IGET(6, Instruction::IGET_SHORT, 18u, 100u, 4u), // Differs from the top. - DEF_IGET(6, Instruction::IGET_CHAR, 19u, 100u, 5u), // Same as the top. - - DEF_CONST(4, Instruction::CONST, 20u, 3000), - DEF_IPUT(4, Instruction::IPUT, 20u, 100u, 7u), - DEF_IPUT(4, Instruction::IPUT, 20u, 101u, 8u), - DEF_CONST(5, Instruction::CONST, 23u, 3001), - DEF_IPUT(5, Instruction::IPUT, 23u, 100u, 7u), - DEF_IPUT(5, Instruction::IPUT, 23u, 101u, 8u), - DEF_IGET(6, Instruction::IGET, 26u, 100u, 7u), - DEF_IGET(6, Instruction::IGET, 27u, 101u, 8u), // Same value as read from field #7. - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[0], value_names_[2]); - - EXPECT_EQ(value_names_[3], value_names_[5]); - - EXPECT_NE(value_names_[6], value_names_[9]); - EXPECT_NE(value_names_[7], value_names_[9]); - - EXPECT_NE(value_names_[10], value_names_[14]); - EXPECT_NE(value_names_[10], value_names_[14]); - - EXPECT_NE(value_names_[15], value_names_[18]); - EXPECT_EQ(value_names_[16], value_names_[19]); - - EXPECT_EQ(value_names_[26], value_names_[27]); -} - -TEST_F(GlobalValueNumberingTestDiamond, SFields) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - { 3u, 1u, 3u, false, kDexMemAccessWord }, - { 4u, 1u, 4u, false, kDexMemAccessShort }, - { 5u, 1u, 5u, false, kDexMemAccessChar }, - { 6u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. - { 7u, 1u, 7u, false, kDexMemAccessWord }, - { 8u, 1u, 8u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_SGET(3, Instruction::SGET, 0u, 0u), - DEF_SGET(6, Instruction::SGET, 1u, 0u), // Same as at the top. - - DEF_SGET(4, Instruction::SGET, 2u, 1u), - DEF_SGET(6, Instruction::SGET, 3u, 1u), // Same as at the left side. - - DEF_SGET(3, Instruction::SGET, 4u, 2u), - DEF_CONST(5, Instruction::CONST, 5u, 100), - DEF_SPUT(5, Instruction::SPUT, 5u, 2u), - DEF_SGET(6, Instruction::SGET, 7u, 2u), // Differs from the top and the CONST. - - DEF_SGET(3, Instruction::SGET, 8u, 3u), - DEF_CONST(3, Instruction::CONST, 9u, 200), - DEF_SPUT(4, Instruction::SPUT, 9u, 3u), - DEF_SPUT(5, Instruction::SPUT, 9u, 3u), - DEF_SGET(6, Instruction::SGET, 12u, 3u), // Differs from the top, equals the CONST. - - DEF_SGET(3, Instruction::SGET_SHORT, 13u, 4u), - DEF_SGET(3, Instruction::SGET_CHAR, 14u, 5u), - DEF_SPUT(4, Instruction::SPUT_SHORT, 15u, 6u), // Clobbers field #4, not #5. - DEF_SGET(6, Instruction::SGET_SHORT, 16u, 4u), // Differs from the top. - DEF_SGET(6, Instruction::SGET_CHAR, 17u, 5u), // Same as the top. - - DEF_CONST(4, Instruction::CONST, 18u, 300), - DEF_SPUT(4, Instruction::SPUT, 18u, 7u), - DEF_SPUT(4, Instruction::SPUT, 18u, 8u), - DEF_CONST(5, Instruction::CONST, 21u, 301), - DEF_SPUT(5, Instruction::SPUT, 21u, 7u), - DEF_SPUT(5, Instruction::SPUT, 21u, 8u), - DEF_SGET(6, Instruction::SGET, 24u, 7u), - DEF_SGET(6, Instruction::SGET, 25u, 8u), // Same value as read from field #7. - }; - - PrepareSFields(sfields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - - EXPECT_EQ(value_names_[2], value_names_[3]); - - EXPECT_NE(value_names_[4], value_names_[7]); - EXPECT_NE(value_names_[5], value_names_[7]); - - EXPECT_NE(value_names_[8], value_names_[12]); - EXPECT_EQ(value_names_[9], value_names_[12]); - - EXPECT_NE(value_names_[13], value_names_[16]); - EXPECT_EQ(value_names_[14], value_names_[17]); - - EXPECT_EQ(value_names_[24], value_names_[25]); -} - -TEST_F(GlobalValueNumberingTestDiamond, NonAliasingArrays) { - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 100u), - DEF_AGET(3, Instruction::AGET, 1u, 100u, 101u), - DEF_AGET(6, Instruction::AGET, 2u, 100u, 101u), // Same as at the top. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 200u), - DEF_IGET(4, Instruction::AGET, 4u, 200u, 201u), - DEF_IGET(6, Instruction::AGET, 5u, 200u, 201u), // Same as at the left side. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 300u), - DEF_AGET(3, Instruction::AGET, 7u, 300u, 301u), - DEF_CONST(5, Instruction::CONST, 8u, 1000), - DEF_APUT(5, Instruction::APUT, 8u, 300u, 301u), - DEF_AGET(6, Instruction::AGET, 10u, 300u, 301u), // Differs from the top and the CONST. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 400u), - DEF_AGET(3, Instruction::AGET, 12u, 400u, 401u), - DEF_CONST(3, Instruction::CONST, 13u, 2000), - DEF_APUT(4, Instruction::APUT, 13u, 400u, 401u), - DEF_APUT(5, Instruction::APUT, 13u, 400u, 401u), - DEF_AGET(6, Instruction::AGET, 16u, 400u, 401u), // Differs from the top, equals the CONST. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 500u), - DEF_AGET(3, Instruction::AGET, 18u, 500u, 501u), - DEF_APUT(4, Instruction::APUT, 19u, 500u, 502u), // Clobbers value at index 501u. - DEF_AGET(6, Instruction::AGET, 20u, 500u, 501u), // Differs from the top. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 600u), - DEF_CONST(4, Instruction::CONST, 22u, 3000), - DEF_APUT(4, Instruction::APUT, 22u, 600u, 601u), - DEF_APUT(4, Instruction::APUT, 22u, 600u, 602u), - DEF_CONST(5, Instruction::CONST, 25u, 3001), - DEF_APUT(5, Instruction::APUT, 25u, 600u, 601u), - DEF_APUT(5, Instruction::APUT, 25u, 600u, 602u), - DEF_AGET(6, Instruction::AGET, 28u, 600u, 601u), - DEF_AGET(6, Instruction::AGET, 29u, 600u, 602u), // Same value as read from index 601u. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 700u), - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 701u), - DEF_AGET(3, Instruction::AGET, 32u, 700u, 702u), - DEF_APUT(4, Instruction::APUT, 33u, 701u, 702u), // Doesn't interfere with unrelated array. - DEF_AGET(6, Instruction::AGET, 34u, 700u, 702u), // Same value as at the top. - }; - - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[1], value_names_[2]); - - EXPECT_EQ(value_names_[4], value_names_[5]); - - EXPECT_NE(value_names_[7], value_names_[10]); - EXPECT_NE(value_names_[8], value_names_[10]); - - EXPECT_NE(value_names_[12], value_names_[16]); - EXPECT_EQ(value_names_[13], value_names_[16]); - - EXPECT_NE(value_names_[18], value_names_[20]); - - EXPECT_NE(value_names_[28], value_names_[22]); - EXPECT_NE(value_names_[28], value_names_[25]); - EXPECT_EQ(value_names_[28], value_names_[29]); - - EXPECT_EQ(value_names_[32], value_names_[34]); -} - -TEST_F(GlobalValueNumberingTestDiamond, AliasingArrays) { - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - // NOTE: We're also testing that these tests really do not interfere with each other. - - DEF_AGET(3, Instruction::AGET_BOOLEAN, 0u, 100u, 101u), - DEF_AGET(6, Instruction::AGET_BOOLEAN, 1u, 100u, 101u), // Same as at the top. - - DEF_IGET(4, Instruction::AGET_OBJECT, 2u, 200u, 201u), - DEF_IGET(6, Instruction::AGET_OBJECT, 3u, 200u, 201u), // Same as at the left side. - - DEF_AGET(3, Instruction::AGET_WIDE, 4u, 300u, 301u), - DEF_CONST(5, Instruction::CONST_WIDE, 6u, 1000), - DEF_APUT(5, Instruction::APUT_WIDE, 6u, 300u, 301u), - DEF_AGET(6, Instruction::AGET_WIDE, 8u, 300u, 301u), // Differs from the top and the CONST. - - DEF_AGET(3, Instruction::AGET_SHORT, 10u, 400u, 401u), - DEF_CONST(3, Instruction::CONST, 11u, 2000), - DEF_APUT(4, Instruction::APUT_SHORT, 11u, 400u, 401u), - DEF_APUT(5, Instruction::APUT_SHORT, 11u, 400u, 401u), - DEF_AGET(6, Instruction::AGET_SHORT, 12u, 400u, 401u), // Differs from the top, == CONST. - - DEF_AGET(3, Instruction::AGET_CHAR, 13u, 500u, 501u), - DEF_APUT(4, Instruction::APUT_CHAR, 14u, 500u, 502u), // Clobbers value at index 501u. - DEF_AGET(6, Instruction::AGET_CHAR, 15u, 500u, 501u), // Differs from the top. - - DEF_AGET(3, Instruction::AGET_BYTE, 16u, 600u, 602u), - DEF_APUT(4, Instruction::APUT_BYTE, 17u, 601u, 602u), // Clobbers values in array 600u. - DEF_AGET(6, Instruction::AGET_BYTE, 18u, 600u, 602u), // Differs from the top. - - DEF_CONST(4, Instruction::CONST, 19u, 3000), - DEF_APUT(4, Instruction::APUT, 19u, 700u, 701u), - DEF_APUT(4, Instruction::APUT, 19u, 700u, 702u), - DEF_CONST(5, Instruction::CONST, 22u, 3001), - DEF_APUT(5, Instruction::APUT, 22u, 700u, 701u), - DEF_APUT(5, Instruction::APUT, 22u, 700u, 702u), - DEF_AGET(6, Instruction::AGET, 25u, 700u, 701u), - DEF_AGET(6, Instruction::AGET, 26u, 700u, 702u), // Same value as read from index 601u. - }; - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 4, 6, 8 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - - EXPECT_EQ(value_names_[2], value_names_[3]); - - EXPECT_NE(value_names_[4], value_names_[7]); - EXPECT_NE(value_names_[5], value_names_[7]); - - EXPECT_NE(value_names_[8], value_names_[12]); - EXPECT_EQ(value_names_[9], value_names_[12]); - - EXPECT_NE(value_names_[13], value_names_[15]); - - EXPECT_NE(value_names_[16], value_names_[18]); - - EXPECT_NE(value_names_[25], value_names_[19]); - EXPECT_NE(value_names_[25], value_names_[22]); - EXPECT_EQ(value_names_[25], value_names_[26]); -} - -TEST_F(GlobalValueNumberingTestDiamond, Phi) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000), - DEF_CONST(4, Instruction::CONST, 1u, 2000), - DEF_CONST(5, Instruction::CONST, 2u, 3000), - DEF_MOVE(4, Instruction::MOVE, 3u, 0u), - DEF_MOVE(4, Instruction::MOVE, 4u, 1u), - DEF_MOVE(5, Instruction::MOVE, 5u, 0u), - DEF_MOVE(5, Instruction::MOVE, 6u, 2u), - DEF_PHI2(6, 7u, 3u, 5u), // Same as CONST 0u (1000). - DEF_PHI2(6, 8u, 3u, 0u), // Same as CONST 0u (1000). - DEF_PHI2(6, 9u, 0u, 5u), // Same as CONST 0u (1000). - DEF_PHI2(6, 10u, 4u, 5u), // Merge 1u (2000) and 0u (1000). - DEF_PHI2(6, 11u, 1u, 5u), // Merge 1u (2000) and 0u (1000). - DEF_PHI2(6, 12u, 4u, 0u), // Merge 1u (2000) and 0u (1000). - DEF_PHI2(6, 13u, 1u, 0u), // Merge 1u (2000) and 0u (1000). - DEF_PHI2(6, 14u, 3u, 6u), // Merge 0u (1000) and 2u (3000). - DEF_PHI2(6, 15u, 0u, 6u), // Merge 0u (1000) and 2u (3000). - DEF_PHI2(6, 16u, 3u, 2u), // Merge 0u (1000) and 2u (3000). - DEF_PHI2(6, 17u, 0u, 2u), // Merge 0u (1000) and 2u (3000). - DEF_PHI2(6, 18u, 4u, 6u), // Merge 1u (2000) and 2u (3000). - DEF_PHI2(6, 19u, 1u, 6u), // Merge 1u (2000) and 2u (3000). - DEF_PHI2(6, 20u, 4u, 2u), // Merge 1u (2000) and 2u (3000). - DEF_PHI2(6, 21u, 1u, 2u), // Merge 1u (2000) and 2u (3000). - }; - - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[7]); - EXPECT_EQ(value_names_[0], value_names_[8]); - EXPECT_EQ(value_names_[0], value_names_[9]); - EXPECT_NE(value_names_[10], value_names_[0]); - EXPECT_NE(value_names_[10], value_names_[1]); - EXPECT_NE(value_names_[10], value_names_[2]); - EXPECT_EQ(value_names_[10], value_names_[11]); - EXPECT_EQ(value_names_[10], value_names_[12]); - EXPECT_EQ(value_names_[10], value_names_[13]); - EXPECT_NE(value_names_[14], value_names_[0]); - EXPECT_NE(value_names_[14], value_names_[1]); - EXPECT_NE(value_names_[14], value_names_[2]); - EXPECT_NE(value_names_[14], value_names_[10]); - EXPECT_EQ(value_names_[14], value_names_[15]); - EXPECT_EQ(value_names_[14], value_names_[16]); - EXPECT_EQ(value_names_[14], value_names_[17]); - EXPECT_NE(value_names_[18], value_names_[0]); - EXPECT_NE(value_names_[18], value_names_[1]); - EXPECT_NE(value_names_[18], value_names_[2]); - EXPECT_NE(value_names_[18], value_names_[10]); - EXPECT_NE(value_names_[18], value_names_[14]); - EXPECT_EQ(value_names_[18], value_names_[19]); - EXPECT_EQ(value_names_[18], value_names_[20]); - EXPECT_EQ(value_names_[18], value_names_[21]); -} - -TEST_F(GlobalValueNumberingTestDiamond, PhiWide) { - static const MIRDef mirs[] = { - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 0u, 1000), - DEF_CONST_WIDE(4, Instruction::CONST_WIDE, 2u, 2000), - DEF_CONST_WIDE(5, Instruction::CONST_WIDE, 4u, 3000), - DEF_MOVE_WIDE(4, Instruction::MOVE_WIDE, 6u, 0u), - DEF_MOVE_WIDE(4, Instruction::MOVE_WIDE, 8u, 2u), - DEF_MOVE_WIDE(5, Instruction::MOVE_WIDE, 10u, 0u), - DEF_MOVE_WIDE(5, Instruction::MOVE_WIDE, 12u, 4u), - DEF_PHI2(6, 14u, 6u, 10u), // Same as CONST_WIDE 0u (1000). - DEF_PHI2(6, 15u, 7u, 11u), // Same as CONST_WIDE 0u (1000), high word. - DEF_PHI2(6, 16u, 6u, 0u), // Same as CONST_WIDE 0u (1000). - DEF_PHI2(6, 17u, 7u, 1u), // Same as CONST_WIDE 0u (1000), high word. - DEF_PHI2(6, 18u, 0u, 10u), // Same as CONST_WIDE 0u (1000). - DEF_PHI2(6, 19u, 1u, 11u), // Same as CONST_WIDE 0u (1000), high word. - DEF_PHI2(6, 20u, 8u, 10u), // Merge 2u (2000) and 0u (1000). - DEF_PHI2(6, 21u, 9u, 11u), // Merge 2u (2000) and 0u (1000), high word. - DEF_PHI2(6, 22u, 2u, 10u), // Merge 2u (2000) and 0u (1000). - DEF_PHI2(6, 23u, 3u, 11u), // Merge 2u (2000) and 0u (1000), high word. - DEF_PHI2(6, 24u, 8u, 0u), // Merge 2u (2000) and 0u (1000). - DEF_PHI2(6, 25u, 9u, 1u), // Merge 2u (2000) and 0u (1000), high word. - DEF_PHI2(6, 26u, 2u, 0u), // Merge 2u (2000) and 0u (1000). - DEF_PHI2(6, 27u, 5u, 1u), // Merge 2u (2000) and 0u (1000), high word. - DEF_PHI2(6, 28u, 6u, 12u), // Merge 0u (1000) and 4u (3000). - DEF_PHI2(6, 29u, 7u, 13u), // Merge 0u (1000) and 4u (3000), high word. - DEF_PHI2(6, 30u, 0u, 12u), // Merge 0u (1000) and 4u (3000). - DEF_PHI2(6, 31u, 1u, 13u), // Merge 0u (1000) and 4u (3000), high word. - DEF_PHI2(6, 32u, 6u, 4u), // Merge 0u (1000) and 4u (3000). - DEF_PHI2(6, 33u, 7u, 5u), // Merge 0u (1000) and 4u (3000), high word. - DEF_PHI2(6, 34u, 0u, 4u), // Merge 0u (1000) and 4u (3000). - DEF_PHI2(6, 35u, 1u, 5u), // Merge 0u (1000) and 4u (3000), high word. - DEF_PHI2(6, 36u, 8u, 12u), // Merge 2u (2000) and 4u (3000). - DEF_PHI2(6, 37u, 9u, 13u), // Merge 2u (2000) and 4u (3000), high word. - DEF_PHI2(6, 38u, 2u, 12u), // Merge 2u (2000) and 4u (3000). - DEF_PHI2(6, 39u, 3u, 13u), // Merge 2u (2000) and 4u (3000), high word. - DEF_PHI2(6, 40u, 8u, 4u), // Merge 2u (2000) and 4u (3000). - DEF_PHI2(6, 41u, 9u, 5u), // Merge 2u (2000) and 4u (3000), high word. - DEF_PHI2(6, 42u, 2u, 4u), // Merge 2u (2000) and 4u (3000). - DEF_PHI2(6, 43u, 3u, 5u), // Merge 2u (2000) and 4u (3000), high word. - }; - - PrepareMIRs(mirs); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - if ((mirs_[i].ssa_rep->defs[0] % 2) == 0) { - const int32_t wide_sregs[] = { mirs_[i].ssa_rep->defs[0] }; - MarkAsWideSRegs(wide_sregs); - } - } - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[7]); - EXPECT_EQ(value_names_[0], value_names_[9]); - EXPECT_EQ(value_names_[0], value_names_[11]); - EXPECT_NE(value_names_[13], value_names_[0]); - EXPECT_NE(value_names_[13], value_names_[1]); - EXPECT_NE(value_names_[13], value_names_[2]); - EXPECT_EQ(value_names_[13], value_names_[15]); - EXPECT_EQ(value_names_[13], value_names_[17]); - EXPECT_EQ(value_names_[13], value_names_[19]); - EXPECT_NE(value_names_[21], value_names_[0]); - EXPECT_NE(value_names_[21], value_names_[1]); - EXPECT_NE(value_names_[21], value_names_[2]); - EXPECT_NE(value_names_[21], value_names_[13]); - EXPECT_EQ(value_names_[21], value_names_[23]); - EXPECT_EQ(value_names_[21], value_names_[25]); - EXPECT_EQ(value_names_[21], value_names_[27]); - EXPECT_NE(value_names_[29], value_names_[0]); - EXPECT_NE(value_names_[29], value_names_[1]); - EXPECT_NE(value_names_[29], value_names_[2]); - EXPECT_NE(value_names_[29], value_names_[13]); - EXPECT_NE(value_names_[29], value_names_[21]); - EXPECT_EQ(value_names_[29], value_names_[31]); - EXPECT_EQ(value_names_[29], value_names_[33]); - EXPECT_EQ(value_names_[29], value_names_[35]); - // High words should get kNoValue. - EXPECT_EQ(value_names_[8], kNoValue); - EXPECT_EQ(value_names_[10], kNoValue); - EXPECT_EQ(value_names_[12], kNoValue); - EXPECT_EQ(value_names_[14], kNoValue); - EXPECT_EQ(value_names_[16], kNoValue); - EXPECT_EQ(value_names_[18], kNoValue); - EXPECT_EQ(value_names_[20], kNoValue); - EXPECT_EQ(value_names_[22], kNoValue); - EXPECT_EQ(value_names_[24], kNoValue); - EXPECT_EQ(value_names_[26], kNoValue); - EXPECT_EQ(value_names_[28], kNoValue); - EXPECT_EQ(value_names_[30], kNoValue); - EXPECT_EQ(value_names_[32], kNoValue); - EXPECT_EQ(value_names_[34], kNoValue); - EXPECT_EQ(value_names_[36], kNoValue); -} - -TEST_F(GlobalValueNumberingTestLoop, NonAliasingIFields) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - { 3u, 1u, 3u, false, kDexMemAccessWord }, - { 4u, 1u, 4u, false, kDexMemAccessWord }, - { 5u, 1u, 5u, false, kDexMemAccessShort }, - { 6u, 1u, 6u, false, kDexMemAccessChar }, - { 7u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. - { 8u, 1u, 8u, false, kDexMemAccessWord }, - { 9u, 0u, 0u, false, kDexMemAccessWord }, // Unresolved. - { 10u, 1u, 10u, false, kDexMemAccessWord }, - { 11u, 1u, 11u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 100u), - DEF_IGET(3, Instruction::IGET, 1u, 100u, 0u), - DEF_IGET(4, Instruction::IGET, 2u, 100u, 0u), // Same as at the top. - DEF_IGET(5, Instruction::IGET, 3u, 100u, 0u), // Same as at the top. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 200u), - DEF_IGET(3, Instruction::IGET, 5u, 200u, 1u), - DEF_IGET(4, Instruction::IGET, 6u, 200u, 1u), // Differs from top... - DEF_IPUT(4, Instruction::IPUT, 7u, 200u, 1u), // Because of this IPUT. - DEF_IGET(5, Instruction::IGET, 8u, 200u, 1u), // Differs from top and the loop IGET. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 300u), - DEF_IGET(3, Instruction::IGET, 10u, 300u, 2u), - DEF_IPUT(4, Instruction::IPUT, 11u, 300u, 2u), // Because of this IPUT... - DEF_IGET(4, Instruction::IGET, 12u, 300u, 2u), // Differs from top. - DEF_IGET(5, Instruction::IGET, 13u, 300u, 2u), // Differs from top but same as the loop IGET. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 400u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 401u), - DEF_CONST(3, Instruction::CONST, 16u, 3000), - DEF_IPUT(3, Instruction::IPUT, 16u, 400u, 3u), - DEF_IPUT(3, Instruction::IPUT, 16u, 400u, 4u), - DEF_IPUT(3, Instruction::IPUT, 16u, 401u, 3u), - DEF_IGET(4, Instruction::IGET, 20u, 400u, 3u), // Differs from 16u and 23u. - DEF_IGET(4, Instruction::IGET, 21u, 400u, 4u), // Same as 20u. - DEF_IGET(4, Instruction::IGET, 22u, 401u, 3u), // Same as 20u. - DEF_CONST(4, Instruction::CONST, 23u, 4000), - DEF_IPUT(4, Instruction::IPUT, 23u, 400u, 3u), - DEF_IPUT(4, Instruction::IPUT, 23u, 400u, 4u), - DEF_IPUT(4, Instruction::IPUT, 23u, 401u, 3u), - DEF_IGET(5, Instruction::IGET, 27u, 400u, 3u), // Differs from 16u and 20u... - DEF_IGET(5, Instruction::IGET, 28u, 400u, 4u), // and same as the CONST 23u - DEF_IGET(5, Instruction::IGET, 29u, 400u, 4u), // and same as the CONST 23u. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 500u), - DEF_IGET(3, Instruction::IGET_SHORT, 31u, 500u, 5u), - DEF_IGET(3, Instruction::IGET_CHAR, 32u, 500u, 6u), - DEF_IPUT(4, Instruction::IPUT_SHORT, 33u, 500u, 7u), // Clobbers field #5, not #6. - DEF_IGET(5, Instruction::IGET_SHORT, 34u, 500u, 5u), // Differs from the top. - DEF_IGET(5, Instruction::IGET_CHAR, 35u, 500u, 6u), // Same as the top. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 600u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 601u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 602u), - DEF_IGET(3, Instruction::IGET, 39u, 600u, 8u), - DEF_IGET(3, Instruction::IGET, 40u, 601u, 8u), - DEF_IPUT(4, Instruction::IPUT, 41u, 602u, 9u), // Doesn't clobber field #8 for other refs. - DEF_IGET(5, Instruction::IGET, 42u, 600u, 8u), // Same as the top. - DEF_IGET(5, Instruction::IGET, 43u, 601u, 8u), // Same as the top. - - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 700u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 701u), - DEF_CONST(3, Instruction::CONST, 46u, 3000), - DEF_IPUT(3, Instruction::IPUT, 46u, 700u, 10u), - DEF_IPUT(3, Instruction::IPUT, 46u, 700u, 11u), - DEF_IPUT(3, Instruction::IPUT, 46u, 701u, 10u), - DEF_IGET(4, Instruction::IGET, 50u, 700u, 10u), // Differs from the CONSTs 46u and 53u. - DEF_IGET(4, Instruction::IGET, 51u, 700u, 11u), // Same as 50u. - DEF_IGET(4, Instruction::IGET, 52u, 701u, 10u), // Same as 50u. - DEF_CONST(4, Instruction::CONST, 53u, 3001), - DEF_IPUT(4, Instruction::IPUT, 53u, 700u, 10u), - DEF_IPUT(4, Instruction::IPUT, 53u, 700u, 11u), - DEF_IPUT(4, Instruction::IPUT, 53u, 701u, 10u), - DEF_IGET(5, Instruction::IGET, 57u, 700u, 10u), // Same as the CONST 53u. - DEF_IGET(5, Instruction::IGET, 58u, 700u, 11u), // Same as the CONST 53u. - DEF_IGET(5, Instruction::IGET, 59u, 701u, 10u), // Same as the CONST 53u. - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[1], value_names_[2]); - EXPECT_EQ(value_names_[1], value_names_[3]); - - EXPECT_NE(value_names_[5], value_names_[6]); - EXPECT_NE(value_names_[5], value_names_[7]); - EXPECT_NE(value_names_[6], value_names_[7]); - - EXPECT_NE(value_names_[10], value_names_[12]); - EXPECT_EQ(value_names_[12], value_names_[13]); - - EXPECT_NE(value_names_[20], value_names_[16]); - EXPECT_NE(value_names_[20], value_names_[23]); - EXPECT_EQ(value_names_[20], value_names_[21]); - EXPECT_EQ(value_names_[20], value_names_[22]); - EXPECT_NE(value_names_[27], value_names_[16]); - EXPECT_NE(value_names_[27], value_names_[20]); - EXPECT_EQ(value_names_[27], value_names_[28]); - EXPECT_EQ(value_names_[27], value_names_[29]); - - EXPECT_NE(value_names_[31], value_names_[34]); - EXPECT_EQ(value_names_[32], value_names_[35]); - - EXPECT_EQ(value_names_[39], value_names_[42]); - EXPECT_EQ(value_names_[40], value_names_[43]); - - EXPECT_NE(value_names_[50], value_names_[46]); - EXPECT_NE(value_names_[50], value_names_[53]); - EXPECT_EQ(value_names_[50], value_names_[51]); - EXPECT_EQ(value_names_[50], value_names_[52]); - EXPECT_EQ(value_names_[57], value_names_[53]); - EXPECT_EQ(value_names_[58], value_names_[53]); - EXPECT_EQ(value_names_[59], value_names_[53]); -} - -TEST_F(GlobalValueNumberingTestLoop, AliasingIFieldsSingleObject) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - { 3u, 1u, 3u, false, kDexMemAccessWord }, - { 4u, 1u, 4u, false, kDexMemAccessWord }, - { 5u, 1u, 5u, false, kDexMemAccessShort }, - { 6u, 1u, 6u, false, kDexMemAccessChar }, - { 7u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_IGET(3, Instruction::IGET, 0u, 100u, 0u), - DEF_IGET(4, Instruction::IGET, 1u, 100u, 0u), // Same as at the top. - DEF_IGET(5, Instruction::IGET, 2u, 100u, 0u), // Same as at the top. - - DEF_IGET(3, Instruction::IGET, 3u, 100u, 1u), - DEF_IGET(4, Instruction::IGET, 4u, 100u, 1u), // Differs from top... - DEF_IPUT(4, Instruction::IPUT, 5u, 100u, 1u), // Because of this IPUT. - DEF_IGET(5, Instruction::IGET, 6u, 100u, 1u), // Differs from top and the loop IGET. - - DEF_IGET(3, Instruction::IGET, 7u, 100u, 2u), - DEF_IPUT(4, Instruction::IPUT, 8u, 100u, 2u), // Because of this IPUT... - DEF_IGET(4, Instruction::IGET, 9u, 100u, 2u), // Differs from top. - DEF_IGET(5, Instruction::IGET, 10u, 100u, 2u), // Differs from top but same as the loop IGET. - - DEF_CONST(3, Instruction::CONST, 11u, 3000), - DEF_IPUT(3, Instruction::IPUT, 11u, 100u, 3u), - DEF_IPUT(3, Instruction::IPUT, 11u, 100u, 4u), - DEF_IGET(4, Instruction::IGET, 14u, 100u, 3u), // Differs from 11u and 16u. - DEF_IGET(4, Instruction::IGET, 15u, 100u, 4u), // Same as 14u. - DEF_CONST(4, Instruction::CONST, 16u, 4000), - DEF_IPUT(4, Instruction::IPUT, 16u, 100u, 3u), - DEF_IPUT(4, Instruction::IPUT, 16u, 100u, 4u), - DEF_IGET(5, Instruction::IGET, 19u, 100u, 3u), // Differs from 11u and 14u... - DEF_IGET(5, Instruction::IGET, 20u, 100u, 4u), // and same as the CONST 16u. - - DEF_IGET(3, Instruction::IGET_SHORT, 21u, 100u, 5u), - DEF_IGET(3, Instruction::IGET_CHAR, 22u, 100u, 6u), - DEF_IPUT(4, Instruction::IPUT_SHORT, 23u, 100u, 7u), // Clobbers field #5, not #6. - DEF_IGET(5, Instruction::IGET_SHORT, 24u, 100u, 5u), // Differs from the top. - DEF_IGET(5, Instruction::IGET_CHAR, 25u, 100u, 6u), // Same as the top. - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[0], value_names_[2]); - - EXPECT_NE(value_names_[3], value_names_[4]); - EXPECT_NE(value_names_[3], value_names_[6]); - EXPECT_NE(value_names_[4], value_names_[6]); - - EXPECT_NE(value_names_[7], value_names_[9]); - EXPECT_EQ(value_names_[9], value_names_[10]); - - EXPECT_NE(value_names_[14], value_names_[11]); - EXPECT_NE(value_names_[14], value_names_[16]); - EXPECT_EQ(value_names_[14], value_names_[15]); - EXPECT_NE(value_names_[19], value_names_[11]); - EXPECT_NE(value_names_[19], value_names_[14]); - EXPECT_EQ(value_names_[19], value_names_[16]); - EXPECT_EQ(value_names_[19], value_names_[20]); - - EXPECT_NE(value_names_[21], value_names_[24]); - EXPECT_EQ(value_names_[22], value_names_[25]); -} - -TEST_F(GlobalValueNumberingTestLoop, AliasingIFieldsTwoObjects) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - { 3u, 1u, 3u, false, kDexMemAccessShort }, - { 4u, 1u, 4u, false, kDexMemAccessChar }, - { 5u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. - { 6u, 1u, 6u, false, kDexMemAccessWord }, - { 7u, 1u, 7u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_IGET(3, Instruction::IGET, 0u, 100u, 0u), - DEF_IPUT(4, Instruction::IPUT, 1u, 101u, 0u), // May alias with the IGET at the top. - DEF_IGET(5, Instruction::IGET, 2u, 100u, 0u), // Differs from the top. - - DEF_IGET(3, Instruction::IGET, 3u, 100u, 1u), - DEF_IPUT(4, Instruction::IPUT, 3u, 101u, 1u), // If aliasing, stores the same value. - DEF_IGET(5, Instruction::IGET, 5u, 100u, 1u), // Same as the top. - - DEF_IGET(3, Instruction::IGET, 6u, 100u, 2u), - DEF_CONST(4, Instruction::CONST, 7u, 1000), - DEF_IPUT(4, Instruction::IPUT, 7u, 101u, 2u), - DEF_IGET(5, Instruction::IGET, 9u, 100u, 2u), // Differs from the top and the CONST. - - DEF_IGET(3, Instruction::IGET_SHORT, 10u, 100u, 3u), - DEF_IGET(3, Instruction::IGET_CHAR, 11u, 100u, 4u), - DEF_IPUT(4, Instruction::IPUT_SHORT, 12u, 101u, 5u), // Clobbers field #3, not #4. - DEF_IGET(5, Instruction::IGET_SHORT, 13u, 100u, 3u), // Differs from the top. - DEF_IGET(5, Instruction::IGET_CHAR, 14u, 100u, 4u), // Same as the top. - - DEF_CONST(3, Instruction::CONST, 15u, 3000), - DEF_IPUT(3, Instruction::IPUT, 15u, 100u, 6u), - DEF_IPUT(3, Instruction::IPUT, 15u, 100u, 7u), - DEF_IPUT(3, Instruction::IPUT, 15u, 101u, 6u), - DEF_IGET(4, Instruction::IGET, 19u, 100u, 6u), // Differs from CONSTs 15u and 22u. - DEF_IGET(4, Instruction::IGET, 20u, 100u, 7u), // Same value as 19u. - DEF_IGET(4, Instruction::IGET, 21u, 101u, 6u), // Same value as read from field #7. - DEF_CONST(4, Instruction::CONST, 22u, 3001), - DEF_IPUT(4, Instruction::IPUT, 22u, 100u, 6u), - DEF_IPUT(4, Instruction::IPUT, 22u, 100u, 7u), - DEF_IPUT(4, Instruction::IPUT, 22u, 101u, 6u), - DEF_IGET(5, Instruction::IGET, 26u, 100u, 6u), // Same as CONST 22u. - DEF_IGET(5, Instruction::IGET, 27u, 100u, 7u), // Same as CONST 22u. - DEF_IGET(5, Instruction::IGET, 28u, 101u, 6u), // Same as CONST 22u. - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[0], value_names_[2]); - - EXPECT_EQ(value_names_[3], value_names_[5]); - - EXPECT_NE(value_names_[6], value_names_[9]); - EXPECT_NE(value_names_[7], value_names_[9]); - - EXPECT_NE(value_names_[10], value_names_[13]); - EXPECT_EQ(value_names_[11], value_names_[14]); - - EXPECT_NE(value_names_[19], value_names_[15]); - EXPECT_NE(value_names_[19], value_names_[22]); - EXPECT_EQ(value_names_[22], value_names_[26]); - EXPECT_EQ(value_names_[22], value_names_[27]); - EXPECT_EQ(value_names_[22], value_names_[28]); -} - -TEST_F(GlobalValueNumberingTestLoop, IFieldToBaseDependency) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - // For the IGET that loads sreg 3u using base 2u, the following IPUT creates a dependency - // from the field value to the base. However, this dependency does not result in an - // infinite loop since the merge of the field value for base 0u gets assigned a value name - // based only on the base 0u, not on the actual value, and breaks the dependency cycle. - DEF_IGET(3, Instruction::IGET, 0u, 100u, 0u), - DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), - DEF_IGET(4, Instruction::IGET, 2u, 0u, 0u), - DEF_IGET(4, Instruction::IGET, 3u, 2u, 0u), - DEF_IPUT(4, Instruction::IPUT, 3u, 0u, 0u), - DEF_IGET(5, Instruction::IGET, 5u, 0u, 0u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[1], value_names_[2]); - EXPECT_EQ(value_names_[3], value_names_[5]); -} - -TEST_F(GlobalValueNumberingTestLoop, SFields) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_SGET(3, Instruction::SGET, 0u, 0u), - DEF_SGET(4, Instruction::SGET, 1u, 0u), // Same as at the top. - DEF_SGET(5, Instruction::SGET, 2u, 0u), // Same as at the top. - - DEF_SGET(3, Instruction::SGET, 3u, 1u), - DEF_SGET(4, Instruction::SGET, 4u, 1u), // Differs from top... - DEF_SPUT(4, Instruction::SPUT, 5u, 1u), // Because of this SPUT. - DEF_SGET(5, Instruction::SGET, 6u, 1u), // Differs from top and the loop SGET. - - DEF_SGET(3, Instruction::SGET, 7u, 2u), - DEF_SPUT(4, Instruction::SPUT, 8u, 2u), // Because of this SPUT... - DEF_SGET(4, Instruction::SGET, 9u, 2u), // Differs from top. - DEF_SGET(5, Instruction::SGET, 10u, 2u), // Differs from top but same as the loop SGET. - }; - - PrepareSFields(sfields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[0], value_names_[2]); - - EXPECT_NE(value_names_[3], value_names_[4]); - EXPECT_NE(value_names_[3], value_names_[6]); - EXPECT_NE(value_names_[4], value_names_[5]); - - EXPECT_NE(value_names_[7], value_names_[9]); - EXPECT_EQ(value_names_[9], value_names_[10]); -} - -TEST_F(GlobalValueNumberingTestLoop, NonAliasingArrays) { - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 100u), - DEF_AGET(3, Instruction::AGET, 1u, 100u, 101u), - DEF_AGET(4, Instruction::AGET, 2u, 100u, 101u), // Same as at the top. - DEF_AGET(5, Instruction::AGET, 3u, 100u, 101u), // Same as at the top. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 200u), - DEF_AGET(3, Instruction::AGET, 5u, 200u, 201u), - DEF_AGET(4, Instruction::AGET, 6u, 200u, 201u), // Differs from top... - DEF_APUT(4, Instruction::APUT, 7u, 200u, 201u), // Because of this IPUT. - DEF_AGET(5, Instruction::AGET, 8u, 200u, 201u), // Differs from top and the loop AGET. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 300u), - DEF_AGET(3, Instruction::AGET, 10u, 300u, 301u), - DEF_APUT(4, Instruction::APUT, 11u, 300u, 301u), // Because of this IPUT... - DEF_AGET(4, Instruction::AGET, 12u, 300u, 301u), // Differs from top. - DEF_AGET(5, Instruction::AGET, 13u, 300u, 301u), // Differs from top but == the loop AGET. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 400u), - DEF_CONST(3, Instruction::CONST, 15u, 3000), - DEF_APUT(3, Instruction::APUT, 15u, 400u, 401u), - DEF_APUT(3, Instruction::APUT, 15u, 400u, 402u), - DEF_AGET(4, Instruction::AGET, 18u, 400u, 401u), // Differs from 15u and 20u. - DEF_AGET(4, Instruction::AGET, 19u, 400u, 402u), // Same as 18u. - DEF_CONST(4, Instruction::CONST, 20u, 4000), - DEF_APUT(4, Instruction::APUT, 20u, 400u, 401u), - DEF_APUT(4, Instruction::APUT, 20u, 400u, 402u), - DEF_AGET(5, Instruction::AGET, 23u, 400u, 401u), // Differs from 15u and 18u... - DEF_AGET(5, Instruction::AGET, 24u, 400u, 402u), // and same as the CONST 20u. - - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 500u), - DEF_AGET(3, Instruction::AGET, 26u, 500u, 501u), - DEF_APUT(4, Instruction::APUT, 27u, 500u, 502u), // Clobbers element at index 501u. - DEF_AGET(5, Instruction::AGET, 28u, 500u, 501u), // Differs from the top. - }; - - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[1], value_names_[2]); - EXPECT_EQ(value_names_[1], value_names_[3]); - - EXPECT_NE(value_names_[5], value_names_[6]); - EXPECT_NE(value_names_[5], value_names_[8]); - EXPECT_NE(value_names_[6], value_names_[8]); - - EXPECT_NE(value_names_[10], value_names_[12]); - EXPECT_EQ(value_names_[12], value_names_[13]); - - EXPECT_NE(value_names_[18], value_names_[15]); - EXPECT_NE(value_names_[18], value_names_[20]); - EXPECT_EQ(value_names_[18], value_names_[19]); - EXPECT_NE(value_names_[23], value_names_[15]); - EXPECT_NE(value_names_[23], value_names_[18]); - EXPECT_EQ(value_names_[23], value_names_[20]); - EXPECT_EQ(value_names_[23], value_names_[24]); - - EXPECT_NE(value_names_[26], value_names_[28]); -} - -TEST_F(GlobalValueNumberingTestLoop, AliasingArrays) { - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_AGET(3, Instruction::AGET_WIDE, 0u, 100u, 101u), - DEF_AGET(4, Instruction::AGET_WIDE, 2u, 100u, 101u), // Same as at the top. - DEF_AGET(5, Instruction::AGET_WIDE, 4u, 100u, 101u), // Same as at the top. - - DEF_AGET(3, Instruction::AGET_BYTE, 6u, 200u, 201u), - DEF_AGET(4, Instruction::AGET_BYTE, 7u, 200u, 201u), // Differs from top... - DEF_APUT(4, Instruction::APUT_BYTE, 8u, 200u, 201u), // Because of this IPUT. - DEF_AGET(5, Instruction::AGET_BYTE, 9u, 200u, 201u), // Differs from top and the loop AGET. - - DEF_AGET(3, Instruction::AGET, 10u, 300u, 301u), - DEF_APUT(4, Instruction::APUT, 11u, 300u, 301u), // Because of this IPUT... - DEF_AGET(4, Instruction::AGET, 12u, 300u, 301u), // Differs from top. - DEF_AGET(5, Instruction::AGET, 13u, 300u, 301u), // Differs from top but == the loop AGET. - - DEF_CONST(3, Instruction::CONST, 14u, 3000), - DEF_APUT(3, Instruction::APUT_CHAR, 14u, 400u, 401u), - DEF_APUT(3, Instruction::APUT_CHAR, 14u, 400u, 402u), - DEF_AGET(4, Instruction::AGET_CHAR, 15u, 400u, 401u), // Differs from 11u and 16u. - DEF_AGET(4, Instruction::AGET_CHAR, 16u, 400u, 402u), // Same as 14u. - DEF_CONST(4, Instruction::CONST, 17u, 4000), - DEF_APUT(4, Instruction::APUT_CHAR, 17u, 400u, 401u), - DEF_APUT(4, Instruction::APUT_CHAR, 17u, 400u, 402u), - DEF_AGET(5, Instruction::AGET_CHAR, 19u, 400u, 401u), // Differs from 11u and 14u... - DEF_AGET(5, Instruction::AGET_CHAR, 20u, 400u, 402u), // and same as the CONST 16u. - - DEF_AGET(3, Instruction::AGET_SHORT, 21u, 500u, 501u), - DEF_APUT(4, Instruction::APUT_SHORT, 22u, 500u, 502u), // Clobbers element at index 501u. - DEF_AGET(5, Instruction::AGET_SHORT, 23u, 500u, 501u), // Differs from the top. - - DEF_AGET(3, Instruction::AGET_OBJECT, 24u, 600u, 601u), - DEF_APUT(4, Instruction::APUT_OBJECT, 25u, 601u, 602u), // Clobbers 600u/601u. - DEF_AGET(5, Instruction::AGET_OBJECT, 26u, 600u, 601u), // Differs from the top. - - DEF_AGET(3, Instruction::AGET_BOOLEAN, 27u, 700u, 701u), - DEF_APUT(4, Instruction::APUT_BOOLEAN, 27u, 701u, 702u), // Storing the same value. - DEF_AGET(5, Instruction::AGET_BOOLEAN, 29u, 700u, 701u), // Differs from the top. - }; - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 0, 2, 4 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[0], value_names_[2]); - - EXPECT_NE(value_names_[3], value_names_[4]); - EXPECT_NE(value_names_[3], value_names_[6]); - EXPECT_NE(value_names_[4], value_names_[6]); - - EXPECT_NE(value_names_[7], value_names_[9]); - EXPECT_EQ(value_names_[9], value_names_[10]); - - EXPECT_NE(value_names_[14], value_names_[11]); - EXPECT_NE(value_names_[14], value_names_[16]); - EXPECT_EQ(value_names_[14], value_names_[15]); - EXPECT_NE(value_names_[19], value_names_[11]); - EXPECT_NE(value_names_[19], value_names_[14]); - EXPECT_EQ(value_names_[19], value_names_[16]); - EXPECT_EQ(value_names_[19], value_names_[20]); - - EXPECT_NE(value_names_[21], value_names_[23]); - - EXPECT_NE(value_names_[24], value_names_[26]); - - EXPECT_EQ(value_names_[27], value_names_[29]); -} - -TEST_F(GlobalValueNumberingTestLoop, Phi) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000), - DEF_PHI2(4, 1u, 0u, 6u), // Merge CONST 0u (1000) with the same. - DEF_PHI2(4, 2u, 0u, 7u), // Merge CONST 0u (1000) with the Phi itself. - DEF_PHI2(4, 3u, 0u, 8u), // Merge CONST 0u (1000) and CONST 4u (2000). - DEF_PHI2(4, 4u, 0u, 9u), // Merge CONST 0u (1000) and Phi 3u. - DEF_CONST(4, Instruction::CONST, 5u, 2000), - DEF_MOVE(4, Instruction::MOVE, 6u, 0u), - DEF_MOVE(4, Instruction::MOVE, 7u, 2u), - DEF_MOVE(4, Instruction::MOVE, 8u, 5u), - DEF_MOVE(4, Instruction::MOVE, 9u, 3u), - }; - - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[1], value_names_[0]); - EXPECT_EQ(value_names_[2], value_names_[0]); - - EXPECT_NE(value_names_[3], value_names_[0]); - EXPECT_NE(value_names_[3], value_names_[5]); - EXPECT_NE(value_names_[4], value_names_[0]); - EXPECT_NE(value_names_[4], value_names_[5]); - EXPECT_NE(value_names_[4], value_names_[3]); -} - -TEST_F(GlobalValueNumberingTestLoop, IFieldLoopVariable) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 0), - DEF_IPUT(3, Instruction::IPUT, 0u, 100u, 0u), - DEF_IGET(4, Instruction::IGET, 2u, 100u, 0u), - DEF_BINOP(4, Instruction::ADD_INT, 3u, 2u, 101u), - DEF_IPUT(4, Instruction::IPUT, 3u, 100u, 0u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[2], value_names_[0]); - EXPECT_NE(value_names_[3], value_names_[0]); - EXPECT_NE(value_names_[3], value_names_[2]); - - - // Set up vreg_to_ssa_map_exit for prologue and loop and set post-processing mode - // as needed for GetStartingVregValueNumber(). - const int32_t prologue_vreg_to_ssa_map_exit[] = { 0 }; - const int32_t loop_vreg_to_ssa_map_exit[] = { 3 }; - PrepareVregToSsaMapExit(3, prologue_vreg_to_ssa_map_exit); - PrepareVregToSsaMapExit(4, loop_vreg_to_ssa_map_exit); - gvn_->StartPostProcessing(); - - // Check that vreg 0 has the same value number as the result of IGET 2u. - const LocalValueNumbering* loop = gvn_->GetLvn(4); - EXPECT_EQ(value_names_[2], loop->GetStartingVregValueNumber(0)); -} - -TEST_F(GlobalValueNumberingTestCatch, IFields) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 200u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 201u), - DEF_IGET(3, Instruction::IGET, 2u, 100u, 0u), - DEF_IGET(3, Instruction::IGET, 3u, 200u, 0u), - DEF_IGET(3, Instruction::IGET, 4u, 201u, 0u), - DEF_INVOKE1(4, Instruction::INVOKE_STATIC, 201u), // Clobbering catch, 201u escapes. - DEF_IGET(4, Instruction::IGET, 6u, 100u, 0u), // Differs from IGET 2u. - DEF_IPUT(4, Instruction::IPUT, 6u, 100u, 1u), - DEF_IPUT(4, Instruction::IPUT, 6u, 101u, 0u), - DEF_IPUT(4, Instruction::IPUT, 6u, 200u, 0u), - DEF_IGET(5, Instruction::IGET, 10u, 100u, 0u), // Differs from IGETs 2u and 6u. - DEF_IGET(5, Instruction::IGET, 11u, 200u, 0u), // Same as the top. - DEF_IGET(5, Instruction::IGET, 12u, 201u, 0u), // Differs from the top, 201u escaped. - DEF_IPUT(5, Instruction::IPUT, 10u, 100u, 1u), - DEF_IPUT(5, Instruction::IPUT, 10u, 101u, 0u), - DEF_IPUT(5, Instruction::IPUT, 10u, 200u, 0u), - DEF_IGET(6, Instruction::IGET, 16u, 100u, 0u), // Differs from IGETs 2u, 6u and 10u. - DEF_IGET(6, Instruction::IGET, 17u, 100u, 1u), // Same as IGET 16u. - DEF_IGET(6, Instruction::IGET, 18u, 101u, 0u), // Same as IGET 16u. - DEF_IGET(6, Instruction::IGET, 19u, 200u, 0u), // Same as IGET 16u. - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[2], value_names_[6]); - EXPECT_NE(value_names_[2], value_names_[10]); - EXPECT_NE(value_names_[6], value_names_[10]); - EXPECT_EQ(value_names_[3], value_names_[11]); - EXPECT_NE(value_names_[4], value_names_[12]); - - EXPECT_NE(value_names_[2], value_names_[16]); - EXPECT_NE(value_names_[6], value_names_[16]); - EXPECT_NE(value_names_[10], value_names_[16]); - EXPECT_EQ(value_names_[16], value_names_[17]); - EXPECT_EQ(value_names_[16], value_names_[18]); - EXPECT_EQ(value_names_[16], value_names_[19]); -} - -TEST_F(GlobalValueNumberingTestCatch, SFields) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_SGET(3, Instruction::SGET, 0u, 0u), - DEF_INVOKE1(4, Instruction::INVOKE_STATIC, 100u), // Clobbering catch. - DEF_SGET(4, Instruction::SGET, 2u, 0u), // Differs from SGET 0u. - DEF_SPUT(4, Instruction::SPUT, 2u, 1u), - DEF_SGET(5, Instruction::SGET, 4u, 0u), // Differs from SGETs 0u and 2u. - DEF_SPUT(5, Instruction::SPUT, 4u, 1u), - DEF_SGET(6, Instruction::SGET, 6u, 0u), // Differs from SGETs 0u, 2u and 4u. - DEF_SGET(6, Instruction::SGET, 7u, 1u), // Same as field #1. - }; - - PrepareSFields(sfields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[0], value_names_[2]); - EXPECT_NE(value_names_[0], value_names_[4]); - EXPECT_NE(value_names_[2], value_names_[4]); - EXPECT_NE(value_names_[0], value_names_[6]); - EXPECT_NE(value_names_[2], value_names_[6]); - EXPECT_NE(value_names_[4], value_names_[6]); - EXPECT_EQ(value_names_[6], value_names_[7]); -} - -TEST_F(GlobalValueNumberingTestCatch, Arrays) { - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 200u), - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 201u), - DEF_AGET(3, Instruction::AGET, 2u, 100u, 101u), - DEF_AGET(3, Instruction::AGET, 3u, 200u, 202u), - DEF_AGET(3, Instruction::AGET, 4u, 200u, 203u), - DEF_AGET(3, Instruction::AGET, 5u, 201u, 202u), - DEF_AGET(3, Instruction::AGET, 6u, 201u, 203u), - DEF_INVOKE1(4, Instruction::INVOKE_STATIC, 201u), // Clobbering catch, 201u escapes. - DEF_AGET(4, Instruction::AGET, 8u, 100u, 101u), // Differs from AGET 2u. - DEF_APUT(4, Instruction::APUT, 8u, 100u, 102u), - DEF_APUT(4, Instruction::APUT, 8u, 200u, 202u), - DEF_APUT(4, Instruction::APUT, 8u, 200u, 203u), - DEF_APUT(4, Instruction::APUT, 8u, 201u, 202u), - DEF_APUT(4, Instruction::APUT, 8u, 201u, 203u), - DEF_AGET(5, Instruction::AGET, 14u, 100u, 101u), // Differs from AGETs 2u and 8u. - DEF_AGET(5, Instruction::AGET, 15u, 200u, 202u), // Same as AGET 3u. - DEF_AGET(5, Instruction::AGET, 16u, 200u, 203u), // Same as AGET 4u. - DEF_AGET(5, Instruction::AGET, 17u, 201u, 202u), // Differs from AGET 5u. - DEF_AGET(5, Instruction::AGET, 18u, 201u, 203u), // Differs from AGET 6u. - DEF_APUT(5, Instruction::APUT, 14u, 100u, 102u), - DEF_APUT(5, Instruction::APUT, 14u, 200u, 202u), - DEF_APUT(5, Instruction::APUT, 14u, 200u, 203u), - DEF_APUT(5, Instruction::APUT, 14u, 201u, 202u), - DEF_APUT(5, Instruction::APUT, 14u, 201u, 203u), - DEF_AGET(6, Instruction::AGET, 24u, 100u, 101u), // Differs from AGETs 2u, 8u and 14u. - DEF_AGET(6, Instruction::AGET, 25u, 100u, 101u), // Same as AGET 24u. - DEF_AGET(6, Instruction::AGET, 26u, 200u, 202u), // Same as AGET 24u. - DEF_AGET(6, Instruction::AGET, 27u, 200u, 203u), // Same as AGET 24u. - DEF_AGET(6, Instruction::AGET, 28u, 201u, 202u), // Same as AGET 24u. - DEF_AGET(6, Instruction::AGET, 29u, 201u, 203u), // Same as AGET 24u. - }; - - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[2], value_names_[8]); - EXPECT_NE(value_names_[2], value_names_[14]); - EXPECT_NE(value_names_[8], value_names_[14]); - EXPECT_EQ(value_names_[3], value_names_[15]); - EXPECT_EQ(value_names_[4], value_names_[16]); - EXPECT_NE(value_names_[5], value_names_[17]); - EXPECT_NE(value_names_[6], value_names_[18]); - EXPECT_NE(value_names_[2], value_names_[24]); - EXPECT_NE(value_names_[8], value_names_[24]); - EXPECT_NE(value_names_[14], value_names_[24]); - EXPECT_EQ(value_names_[24], value_names_[25]); - EXPECT_EQ(value_names_[24], value_names_[26]); - EXPECT_EQ(value_names_[24], value_names_[27]); - EXPECT_EQ(value_names_[24], value_names_[28]); - EXPECT_EQ(value_names_[24], value_names_[29]); -} - -TEST_F(GlobalValueNumberingTestCatch, Phi) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000), - DEF_CONST(3, Instruction::CONST, 1u, 2000), - DEF_MOVE(3, Instruction::MOVE, 2u, 1u), - DEF_INVOKE1(4, Instruction::INVOKE_STATIC, 100u), // Clobbering catch. - DEF_CONST(5, Instruction::CONST, 4u, 1000), - DEF_CONST(5, Instruction::CONST, 5u, 3000), - DEF_MOVE(5, Instruction::MOVE, 6u, 5u), - DEF_PHI2(6, 7u, 0u, 4u), - DEF_PHI2(6, 8u, 0u, 5u), - DEF_PHI2(6, 9u, 0u, 6u), - DEF_PHI2(6, 10u, 1u, 4u), - DEF_PHI2(6, 11u, 1u, 5u), - DEF_PHI2(6, 12u, 1u, 6u), - DEF_PHI2(6, 13u, 2u, 4u), - DEF_PHI2(6, 14u, 2u, 5u), - DEF_PHI2(6, 15u, 2u, 6u), - }; - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - ASSERT_EQ(value_names_[4], value_names_[0]); // Both CONSTs are 1000. - EXPECT_EQ(value_names_[7], value_names_[0]); // Merging CONST 0u and CONST 4u, both 1000. - EXPECT_NE(value_names_[8], value_names_[0]); - EXPECT_NE(value_names_[8], value_names_[5]); - EXPECT_EQ(value_names_[9], value_names_[8]); - EXPECT_NE(value_names_[10], value_names_[1]); - EXPECT_NE(value_names_[10], value_names_[4]); - EXPECT_NE(value_names_[10], value_names_[8]); - EXPECT_NE(value_names_[11], value_names_[1]); - EXPECT_NE(value_names_[11], value_names_[5]); - EXPECT_NE(value_names_[11], value_names_[8]); - EXPECT_NE(value_names_[11], value_names_[10]); - EXPECT_EQ(value_names_[12], value_names_[11]); - EXPECT_EQ(value_names_[13], value_names_[10]); - EXPECT_EQ(value_names_[14], value_names_[11]); - EXPECT_EQ(value_names_[15], value_names_[11]); -} - -TEST_F(GlobalValueNumberingTest, NullCheckIFields) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, // Object. - { 1u, 1u, 1u, false, kDexMemAccessObject }, // Object. - }; - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)), // 4 is fall-through, 5 is taken. - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(3)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(3, 4)), - }; - static const MIRDef mirs[] = { - DEF_IGET(3, Instruction::IGET_OBJECT, 0u, 100u, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 1u, 100u, 1u), - DEF_IGET(3, Instruction::IGET_OBJECT, 2u, 101u, 0u), - DEF_IFZ(3, Instruction::IF_NEZ, 0u), // Null-check for field #0 for taken. - DEF_UNIQUE_REF(4, Instruction::NEW_ARRAY, 4u), - DEF_IPUT(4, Instruction::IPUT_OBJECT, 4u, 100u, 0u), - DEF_IPUT(4, Instruction::IPUT_OBJECT, 4u, 100u, 1u), - DEF_IPUT(4, Instruction::IPUT_OBJECT, 4u, 101u, 0u), - DEF_IGET(5, Instruction::IGET_OBJECT, 8u, 100u, 0u), // 100u/#0, IF_NEZ/NEW_ARRAY. - DEF_IGET(5, Instruction::IGET_OBJECT, 9u, 100u, 1u), // 100u/#1, -/NEW_ARRAY. - DEF_IGET(5, Instruction::IGET_OBJECT, 10u, 101u, 0u), // 101u/#0, -/NEW_ARRAY. - DEF_CONST(5, Instruction::CONST, 11u, 0), - DEF_AGET(5, Instruction::AGET, 12u, 8u, 11u), // Null-check eliminated. - DEF_AGET(5, Instruction::AGET, 13u, 9u, 11u), // Null-check kept. - DEF_AGET(5, Instruction::AGET, 14u, 10u, 11u), // Null-check kept. - }; - static const bool expected_ignore_null_check[] = { - false, true, false, false, // BB #3; unimportant. - false, true, true, true, // BB #4; unimportant. - true, true, true, false, true, false, false, // BB #5; only the last three are important. - }; - - PrepareIFields(ifields); - PrepareBasicBlocks(bbs); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - PerformGVNCodeModifications(); - ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_null_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; - } -} - -TEST_F(GlobalValueNumberingTest, NullCheckSFields) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, - { 1u, 1u, 1u, false, kDexMemAccessObject }, - }; - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)), // 4 is fall-through, 5 is taken. - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(3)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(3, 4)), - }; - static const MIRDef mirs[] = { - DEF_SGET(3, Instruction::SGET_OBJECT, 0u, 0u), - DEF_SGET(3, Instruction::SGET_OBJECT, 1u, 1u), - DEF_IFZ(3, Instruction::IF_NEZ, 0u), // Null-check for field #0 for taken. - DEF_UNIQUE_REF(4, Instruction::NEW_ARRAY, 3u), - DEF_SPUT(4, Instruction::SPUT_OBJECT, 3u, 0u), - DEF_SPUT(4, Instruction::SPUT_OBJECT, 3u, 1u), - DEF_SGET(5, Instruction::SGET_OBJECT, 6u, 0u), // Field #0 is null-checked, IF_NEZ/NEW_ARRAY. - DEF_SGET(5, Instruction::SGET_OBJECT, 7u, 1u), // Field #1 is not null-checked, -/NEW_ARRAY. - DEF_CONST(5, Instruction::CONST, 8u, 0), - DEF_AGET(5, Instruction::AGET, 9u, 6u, 8u), // Null-check eliminated. - DEF_AGET(5, Instruction::AGET, 10u, 7u, 8u), // Null-check kept. - }; - static const bool expected_ignore_null_check[] = { - false, false, false, false, false, false, false, false, false, true, false - }; - - PrepareSFields(sfields); - PrepareBasicBlocks(bbs); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - PerformGVNCodeModifications(); - ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_null_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; - } -} - -TEST_F(GlobalValueNumberingTest, NullCheckArrays) { - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)), // 4 is fall-through, 5 is taken. - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(3)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(3, 4)), - }; - static const MIRDef mirs[] = { - DEF_AGET(3, Instruction::AGET_OBJECT, 0u, 100u, 102u), - DEF_AGET(3, Instruction::AGET_OBJECT, 1u, 100u, 103u), - DEF_AGET(3, Instruction::AGET_OBJECT, 2u, 101u, 102u), - DEF_IFZ(3, Instruction::IF_NEZ, 0u), // Null-check for field #0 for taken. - DEF_UNIQUE_REF(4, Instruction::NEW_ARRAY, 4u), - DEF_APUT(4, Instruction::APUT_OBJECT, 4u, 100u, 102u), - DEF_APUT(4, Instruction::APUT_OBJECT, 4u, 100u, 103u), - DEF_APUT(4, Instruction::APUT_OBJECT, 4u, 101u, 102u), - DEF_AGET(5, Instruction::AGET_OBJECT, 8u, 100u, 102u), // Null-checked, IF_NEZ/NEW_ARRAY. - DEF_AGET(5, Instruction::AGET_OBJECT, 9u, 100u, 103u), // Not null-checked, -/NEW_ARRAY. - DEF_AGET(5, Instruction::AGET_OBJECT, 10u, 101u, 102u), // Not null-checked, -/NEW_ARRAY. - DEF_CONST(5, Instruction::CONST, 11u, 0), - DEF_AGET(5, Instruction::AGET, 12u, 8u, 11u), // Null-check eliminated. - DEF_AGET(5, Instruction::AGET, 13u, 9u, 11u), // Null-check kept. - DEF_AGET(5, Instruction::AGET, 14u, 10u, 11u), // Null-check kept. - }; - static const bool expected_ignore_null_check[] = { - false, true, false, false, // BB #3; unimportant. - false, true, true, true, // BB #4; unimportant. - true, true, true, false, true, false, false, // BB #5; only the last three are important. - }; - - PrepareBasicBlocks(bbs); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - PerformGVNCodeModifications(); - ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_null_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; - } -} - -TEST_F(GlobalValueNumberingTestDiamond, RangeCheckArrays) { - // NOTE: We don't merge range checks when we merge value names for Phis or memory locations. - static const MIRDef mirs[] = { - DEF_AGET(4, Instruction::AGET, 0u, 100u, 101u), - DEF_AGET(5, Instruction::AGET, 1u, 100u, 101u), - DEF_APUT(6, Instruction::APUT, 2u, 100u, 101u), - - DEF_AGET(4, Instruction::AGET, 3u, 200u, 201u), - DEF_AGET(5, Instruction::AGET, 4u, 200u, 202u), - DEF_APUT(6, Instruction::APUT, 5u, 200u, 201u), - - DEF_AGET(4, Instruction::AGET, 6u, 300u, 302u), - DEF_AGET(5, Instruction::AGET, 7u, 301u, 302u), - DEF_APUT(6, Instruction::APUT, 8u, 300u, 302u), - }; - static const bool expected_ignore_null_check[] = { - false, false, true, - false, false, true, - false, false, false, - }; - static const bool expected_ignore_range_check[] = { - false, false, true, - false, false, false, - false, false, false, - }; - - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - PerformGVNCodeModifications(); - ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); - ASSERT_EQ(arraysize(expected_ignore_range_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_null_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; - EXPECT_EQ(expected_ignore_range_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0) << i; - } -} - -TEST_F(GlobalValueNumberingTestDiamond, MergeSameValueInDifferentMemoryLocations) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 100u), - DEF_UNIQUE_REF(3, Instruction::NEW_ARRAY, 200u), - DEF_CONST(4, Instruction::CONST, 2u, 1000), - DEF_IPUT(4, Instruction::IPUT, 2u, 100u, 0u), - DEF_IPUT(4, Instruction::IPUT, 2u, 100u, 1u), - DEF_IPUT(4, Instruction::IPUT, 2u, 101u, 0u), - DEF_APUT(4, Instruction::APUT, 2u, 200u, 202u), - DEF_APUT(4, Instruction::APUT, 2u, 200u, 203u), - DEF_APUT(4, Instruction::APUT, 2u, 201u, 202u), - DEF_APUT(4, Instruction::APUT, 2u, 201u, 203u), - DEF_SPUT(4, Instruction::SPUT, 2u, 0u), - DEF_SPUT(4, Instruction::SPUT, 2u, 1u), - DEF_CONST(5, Instruction::CONST, 12u, 2000), - DEF_IPUT(5, Instruction::IPUT, 12u, 100u, 0u), - DEF_IPUT(5, Instruction::IPUT, 12u, 100u, 1u), - DEF_IPUT(5, Instruction::IPUT, 12u, 101u, 0u), - DEF_APUT(5, Instruction::APUT, 12u, 200u, 202u), - DEF_APUT(5, Instruction::APUT, 12u, 200u, 203u), - DEF_APUT(5, Instruction::APUT, 12u, 201u, 202u), - DEF_APUT(5, Instruction::APUT, 12u, 201u, 203u), - DEF_SPUT(5, Instruction::SPUT, 12u, 0u), - DEF_SPUT(5, Instruction::SPUT, 12u, 1u), - DEF_PHI2(6, 22u, 2u, 12u), - DEF_IGET(6, Instruction::IGET, 23u, 100u, 0u), - DEF_IGET(6, Instruction::IGET, 24u, 100u, 1u), - DEF_IGET(6, Instruction::IGET, 25u, 101u, 0u), - DEF_AGET(6, Instruction::AGET, 26u, 200u, 202u), - DEF_AGET(6, Instruction::AGET, 27u, 200u, 203u), - DEF_AGET(6, Instruction::AGET, 28u, 201u, 202u), - DEF_AGET(6, Instruction::AGET, 29u, 201u, 203u), - DEF_SGET(6, Instruction::SGET, 30u, 0u), - DEF_SGET(6, Instruction::SGET, 31u, 1u), - }; - PrepareIFields(ifields); - PrepareSFields(sfields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[2], value_names_[12]); - EXPECT_NE(value_names_[2], value_names_[22]); - EXPECT_NE(value_names_[12], value_names_[22]); - for (size_t i = 23; i != arraysize(mirs); ++i) { - EXPECT_EQ(value_names_[22], value_names_[i]) << i; - } -} - -TEST_F(GlobalValueNumberingTest, InfiniteLocationLoop) { - // This is a pattern that lead to an infinite loop during the GVN development. This has been - // fixed by rewriting the merging of AliasingValues to merge only locations read from or - // written to in each incoming LVN rather than merging all locations read from or written to - // in any incoming LVN. It also showed up only when the GVN used the DFS ordering instead of - // the "topological" ordering but, since the "topological" ordering is not really topological - // when there are cycles and an optimizing Java compiler (or a tool like proguard) could - // theoretically create any sort of flow graph, this could have shown up in real code. - // - // While we were merging all the locations: - // The first time the Phi evaluates to the same value name as CONST 0u. After the second - // evaluation, when the BB #9 has been processed, the Phi receives its own value name. - // However, the index from the first evaluation keeps disappearing and reappearing in the - // LVN's aliasing_array_value_map_'s load_value_map for BBs #9, #4, #5, #7 because of the - // DFS ordering of LVN evaluation. - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, - }; - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(4)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 2), DEF_PRED2(3, 9)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 7), DEF_PRED1(4)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(9), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(8, 9), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(9), DEF_PRED1(7)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED3(6, 7, 8)), - }; - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 0), - DEF_PHI2(4, 1u, 0u, 10u), - DEF_INVOKE1(6, Instruction::INVOKE_STATIC, 100u), - DEF_IGET(6, Instruction::IGET_OBJECT, 3u, 100u, 0u), - DEF_CONST(6, Instruction::CONST, 4u, 1000), - DEF_APUT(6, Instruction::APUT, 4u, 3u, 1u), // Index is Phi 1u. - DEF_INVOKE1(8, Instruction::INVOKE_STATIC, 100u), - DEF_IGET(8, Instruction::IGET_OBJECT, 7u, 100u, 0u), - DEF_CONST(8, Instruction::CONST, 8u, 2000), - DEF_APUT(8, Instruction::APUT, 9u, 7u, 1u), // Index is Phi 1u. - DEF_CONST(9, Instruction::CONST, 10u, 3000), - }; - PrepareIFields(ifields); - PrepareBasicBlocks(bbs); - PrepareMIRs(mirs); - // Using DFS order for this test. The GVN result should not depend on the used ordering - // once the GVN actually converges. But creating a test for this convergence issue with - // the topological ordering could be a very challenging task. - PerformPreOrderDfsGVN(); -} - -TEST_F(GlobalValueNumberingTestTwoConsecutiveLoops, IFieldAndPhi) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, - }; - static const MIRDef mirs[] = { - DEF_MOVE(3, Instruction::MOVE_OBJECT, 0u, 100u), - DEF_IPUT(3, Instruction::IPUT_OBJECT, 0u, 200u, 0u), - DEF_PHI2(4, 2u, 0u, 3u), - DEF_MOVE(5, Instruction::MOVE_OBJECT, 3u, 300u), - DEF_IPUT(5, Instruction::IPUT_OBJECT, 3u, 200u, 0u), - DEF_MOVE(6, Instruction::MOVE_OBJECT, 5u, 2u), - DEF_IGET(6, Instruction::IGET_OBJECT, 6u, 200u, 0u), - DEF_MOVE(7, Instruction::MOVE_OBJECT, 7u, 5u), - DEF_IGET(7, Instruction::IGET_OBJECT, 8u, 200u, 0u), - DEF_MOVE(8, Instruction::MOVE_OBJECT, 9u, 5u), - DEF_IGET(8, Instruction::IGET_OBJECT, 10u, 200u, 0u), - DEF_MOVE(9, Instruction::MOVE_OBJECT, 11u, 5u), - DEF_IGET(9, Instruction::IGET_OBJECT, 12u, 200u, 0u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[0], value_names_[3]); - EXPECT_NE(value_names_[0], value_names_[2]); - EXPECT_NE(value_names_[3], value_names_[2]); - EXPECT_EQ(value_names_[2], value_names_[5]); - EXPECT_EQ(value_names_[5], value_names_[6]); - EXPECT_EQ(value_names_[5], value_names_[7]); - EXPECT_EQ(value_names_[5], value_names_[8]); - EXPECT_EQ(value_names_[5], value_names_[9]); - EXPECT_EQ(value_names_[5], value_names_[10]); - EXPECT_EQ(value_names_[5], value_names_[11]); - EXPECT_EQ(value_names_[5], value_names_[12]); -} - -TEST_F(GlobalValueNumberingTestTwoConsecutiveLoops, NullCheck) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, - }; - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, - }; - static const MIRDef mirs[] = { - DEF_MOVE(3, Instruction::MOVE_OBJECT, 0u, 100u), - DEF_IGET(3, Instruction::IGET_OBJECT, 1u, 200u, 0u), - DEF_SGET(3, Instruction::SGET_OBJECT, 2u, 0u), - DEF_AGET(3, Instruction::AGET_OBJECT, 3u, 300u, 201u), - DEF_PHI2(4, 4u, 0u, 8u), - DEF_IGET(5, Instruction::IGET_OBJECT, 5u, 200u, 0u), - DEF_SGET(5, Instruction::SGET_OBJECT, 6u, 0u), - DEF_AGET(5, Instruction::AGET_OBJECT, 7u, 300u, 201u), - DEF_MOVE(5, Instruction::MOVE_OBJECT, 8u, 400u), - DEF_IPUT(5, Instruction::IPUT_OBJECT, 4u, 200u, 0u), // PUT the Phi 4u. - DEF_SPUT(5, Instruction::SPUT_OBJECT, 4u, 0u), // PUT the Phi 4u. - DEF_APUT(5, Instruction::APUT_OBJECT, 4u, 300u, 201u), // PUT the Phi 4u. - DEF_MOVE(6, Instruction::MOVE_OBJECT, 12u, 4u), - DEF_IGET(6, Instruction::IGET_OBJECT, 13u, 200u, 0u), - DEF_SGET(6, Instruction::SGET_OBJECT, 14u, 0u), - DEF_AGET(6, Instruction::AGET_OBJECT, 15u, 300u, 201u), - DEF_AGET(6, Instruction::AGET_OBJECT, 16u, 12u, 600u), - DEF_AGET(6, Instruction::AGET_OBJECT, 17u, 13u, 600u), - DEF_AGET(6, Instruction::AGET_OBJECT, 18u, 14u, 600u), - DEF_AGET(6, Instruction::AGET_OBJECT, 19u, 15u, 600u), - DEF_MOVE(8, Instruction::MOVE_OBJECT, 20u, 12u), - DEF_IGET(8, Instruction::IGET_OBJECT, 21u, 200u, 0u), - DEF_SGET(8, Instruction::SGET_OBJECT, 22u, 0u), - DEF_AGET(8, Instruction::AGET_OBJECT, 23u, 300u, 201u), - DEF_AGET(8, Instruction::AGET_OBJECT, 24u, 12u, 600u), - DEF_AGET(8, Instruction::AGET_OBJECT, 25u, 13u, 600u), - DEF_AGET(8, Instruction::AGET_OBJECT, 26u, 14u, 600u), - DEF_AGET(8, Instruction::AGET_OBJECT, 27u, 15u, 600u), - DEF_MOVE(9, Instruction::MOVE_OBJECT, 28u, 12u), - DEF_IGET(9, Instruction::IGET_OBJECT, 29u, 200u, 0u), - DEF_SGET(9, Instruction::SGET_OBJECT, 30u, 0u), - DEF_AGET(9, Instruction::AGET_OBJECT, 31u, 300u, 201u), - DEF_AGET(9, Instruction::AGET_OBJECT, 32u, 12u, 600u), - DEF_AGET(9, Instruction::AGET_OBJECT, 33u, 13u, 600u), - DEF_AGET(9, Instruction::AGET_OBJECT, 34u, 14u, 600u), - DEF_AGET(9, Instruction::AGET_OBJECT, 35u, 15u, 600u), - }; - static const bool expected_ignore_null_check[] = { - false, false, false, false, // BB #3. - false, true, false, true, false, true, false, true, // BBs #4 and #5. - false, true, false, true, false, false, false, false, // BB #6. - false, true, false, true, true, true, true, true, // BB #7. - false, true, false, true, true, true, true, true, // BB #8. - }; - static const bool expected_ignore_range_check[] = { - false, false, false, false, // BB #3. - false, false, false, true, false, false, false, true, // BBs #4 and #5. - false, false, false, true, false, false, false, false, // BB #6. - false, false, false, true, true, true, true, true, // BB #7. - false, false, false, true, true, true, true, true, // BB #8. - }; - - PrepareIFields(ifields); - PrepareSFields(sfields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[0], value_names_[4]); - EXPECT_NE(value_names_[1], value_names_[5]); - EXPECT_NE(value_names_[2], value_names_[6]); - EXPECT_NE(value_names_[3], value_names_[7]); - EXPECT_NE(value_names_[4], value_names_[8]); - EXPECT_EQ(value_names_[4], value_names_[12]); - EXPECT_EQ(value_names_[5], value_names_[13]); - EXPECT_EQ(value_names_[6], value_names_[14]); - EXPECT_EQ(value_names_[7], value_names_[15]); - EXPECT_EQ(value_names_[12], value_names_[20]); - EXPECT_EQ(value_names_[13], value_names_[21]); - EXPECT_EQ(value_names_[14], value_names_[22]); - EXPECT_EQ(value_names_[15], value_names_[23]); - EXPECT_EQ(value_names_[12], value_names_[28]); - EXPECT_EQ(value_names_[13], value_names_[29]); - EXPECT_EQ(value_names_[14], value_names_[30]); - EXPECT_EQ(value_names_[15], value_names_[31]); - PerformGVNCodeModifications(); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_null_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; - EXPECT_EQ(expected_ignore_range_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0) << i; - } -} - -TEST_F(GlobalValueNumberingTestTwoNestedLoops, IFieldAndPhi) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, - }; - static const MIRDef mirs[] = { - DEF_MOVE(3, Instruction::MOVE_OBJECT, 0u, 100u), - DEF_IPUT(3, Instruction::IPUT_OBJECT, 0u, 200u, 0u), - DEF_PHI2(4, 2u, 0u, 11u), - DEF_MOVE(4, Instruction::MOVE_OBJECT, 3u, 2u), - DEF_IGET(4, Instruction::IGET_OBJECT, 4u, 200u, 0u), - DEF_MOVE(5, Instruction::MOVE_OBJECT, 5u, 3u), - DEF_IGET(5, Instruction::IGET_OBJECT, 6u, 200u, 0u), - DEF_MOVE(6, Instruction::MOVE_OBJECT, 7u, 3u), - DEF_IGET(6, Instruction::IGET_OBJECT, 8u, 200u, 0u), - DEF_MOVE(7, Instruction::MOVE_OBJECT, 9u, 3u), - DEF_IGET(7, Instruction::IGET_OBJECT, 10u, 200u, 0u), - DEF_MOVE(7, Instruction::MOVE_OBJECT, 11u, 300u), - DEF_IPUT(7, Instruction::IPUT_OBJECT, 11u, 200u, 0u), - DEF_MOVE(8, Instruction::MOVE_OBJECT, 13u, 3u), - DEF_IGET(8, Instruction::IGET_OBJECT, 14u, 200u, 0u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN(); - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[0], value_names_[11]); - EXPECT_NE(value_names_[0], value_names_[2]); - EXPECT_NE(value_names_[11], value_names_[2]); - EXPECT_EQ(value_names_[2], value_names_[3]); - EXPECT_EQ(value_names_[3], value_names_[4]); - EXPECT_EQ(value_names_[3], value_names_[5]); - EXPECT_EQ(value_names_[3], value_names_[6]); - EXPECT_EQ(value_names_[3], value_names_[7]); - EXPECT_EQ(value_names_[3], value_names_[8]); - EXPECT_EQ(value_names_[3], value_names_[9]); - EXPECT_EQ(value_names_[3], value_names_[10]); - EXPECT_EQ(value_names_[3], value_names_[13]); - EXPECT_EQ(value_names_[3], value_names_[14]); -} - -TEST_F(GlobalValueNumberingTest, NormalPathToCatchEntry) { - // When there's an empty catch block, all the exception paths lead to the next block in - // the normal path and we can also have normal "taken" or "fall-through" branches to that - // path. Check that LocalValueNumbering::PruneNonAliasingRefsForCatch() can handle it. - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(3)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(3, 4)), - }; - static const MIRDef mirs[] = { - DEF_INVOKE1(4, Instruction::INVOKE_STATIC, 100u), - }; - PrepareBasicBlocks(bbs); - BasicBlock* catch_handler = cu_.mir_graph->GetBasicBlock(5u); - catch_handler->catch_entry = true; - // Add successor block info to the check block. - BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u); - check_bb->successor_block_list_type = kCatch; - SuccessorBlockInfo* successor_block_info = reinterpret_cast - (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors)); - successor_block_info->block = catch_handler->id; - check_bb->successor_blocks.push_back(successor_block_info); - BasicBlock* merge_block = cu_.mir_graph->GetBasicBlock(4u); - std::swap(merge_block->taken, merge_block->fall_through); - PrepareMIRs(mirs); - PerformGVN(); -} - -TEST_F(GlobalValueNumberingTestDiamond, DivZeroCheckDiamond) { - static const MIRDef mirs[] = { - DEF_BINOP(3u, Instruction::DIV_INT, 1u, 20u, 21u), - DEF_BINOP(3u, Instruction::DIV_INT, 2u, 24u, 21u), - DEF_BINOP(3u, Instruction::DIV_INT, 3u, 20u, 23u), - DEF_BINOP(4u, Instruction::DIV_INT, 4u, 24u, 22u), - DEF_BINOP(4u, Instruction::DIV_INT, 9u, 24u, 25u), - DEF_BINOP(5u, Instruction::DIV_INT, 5u, 24u, 21u), - DEF_BINOP(5u, Instruction::DIV_INT, 10u, 24u, 26u), - DEF_PHI2(6u, 27u, 25u, 26u), - DEF_BINOP(6u, Instruction::DIV_INT, 12u, 20u, 27u), - DEF_BINOP(6u, Instruction::DIV_INT, 6u, 24u, 21u), - DEF_BINOP(6u, Instruction::DIV_INT, 7u, 20u, 23u), - DEF_BINOP(6u, Instruction::DIV_INT, 8u, 20u, 22u), - }; - - static const bool expected_ignore_div_zero_check[] = { - false, // New divisor seen. - true, // Eliminated since it has first divisor as first one. - false, // New divisor seen. - false, // New divisor seen. - false, // New divisor seen. - true, // Eliminated in dominating block. - false, // New divisor seen. - false, // Phi node. - true, // Eliminated on both sides of diamond and merged via phi. - true, // Eliminated in dominating block. - true, // Eliminated in dominating block. - false, // Only eliminated on one path of diamond. - }; - - PrepareMIRs(mirs); - PerformGVN(); - PerformGVNCodeModifications(); - ASSERT_EQ(arraysize(expected_ignore_div_zero_check), mir_count_); - for (size_t i = 0u; i != mir_count_; ++i) { - int expected = expected_ignore_div_zero_check[i] ? MIR_IGNORE_DIV_ZERO_CHECK : 0u; - EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; - } -} - -TEST_F(GlobalValueNumberingTestDiamond, CheckCastDiamond) { - static const MIRDef mirs[] = { - DEF_UNOP(3u, Instruction::INSTANCE_OF, 0u, 100u), - DEF_UNOP(3u, Instruction::INSTANCE_OF, 1u, 200u), - DEF_IFZ(3u, Instruction::IF_NEZ, 0u), - DEF_INVOKE1(4u, Instruction::CHECK_CAST, 100u), - DEF_INVOKE1(5u, Instruction::CHECK_CAST, 100u), - DEF_INVOKE1(5u, Instruction::CHECK_CAST, 200u), - DEF_INVOKE1(5u, Instruction::CHECK_CAST, 100u), - DEF_INVOKE1(6u, Instruction::CHECK_CAST, 100u), - }; - - static const bool expected_ignore_check_cast[] = { - false, // instance-of - false, // instance-of - false, // if-nez - false, // Not eliminated, fall-through branch. - true, // Eliminated. - false, // Not eliminated, different value. - false, // Not eliminated, different type. - false, // Not eliminated, bottom block. - }; - - PrepareMIRs(mirs); - mirs_[0].dalvikInsn.vC = 1234; // type for instance-of - mirs_[1].dalvikInsn.vC = 1234; // type for instance-of - mirs_[3].dalvikInsn.vB = 1234; // type for check-cast - mirs_[4].dalvikInsn.vB = 1234; // type for check-cast - mirs_[5].dalvikInsn.vB = 1234; // type for check-cast - mirs_[6].dalvikInsn.vB = 4321; // type for check-cast - mirs_[7].dalvikInsn.vB = 1234; // type for check-cast - PerformGVN(); - PerformGVNCodeModifications(); - ASSERT_EQ(arraysize(expected_ignore_check_cast), mir_count_); - for (size_t i = 0u; i != mir_count_; ++i) { - int expected = expected_ignore_check_cast[i] ? MIR_IGNORE_CHECK_CAST : 0u; - EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; - } -} - -TEST_F(GlobalValueNumberingTest, CheckCastDominators) { - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(7)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)), // Block #3, top of the diamond. - DEF_BB(kDalvikByteCode, DEF_SUCC1(7), DEF_PRED1(3)), // Block #4, left side. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Block #5, right side. - DEF_BB(kDalvikByteCode, DEF_SUCC1(7), DEF_PRED1(5)), // Block #6, right side. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 6)), // Block #7, bottom. - }; - static const MIRDef mirs[] = { - DEF_UNOP(3u, Instruction::INSTANCE_OF, 0u, 100u), - DEF_UNOP(3u, Instruction::INSTANCE_OF, 1u, 200u), - DEF_IFZ(3u, Instruction::IF_NEZ, 0u), - DEF_INVOKE1(4u, Instruction::CHECK_CAST, 100u), - DEF_INVOKE1(6u, Instruction::CHECK_CAST, 100u), - DEF_INVOKE1(6u, Instruction::CHECK_CAST, 200u), - DEF_INVOKE1(6u, Instruction::CHECK_CAST, 100u), - DEF_INVOKE1(7u, Instruction::CHECK_CAST, 100u), - }; - - static const bool expected_ignore_check_cast[] = { - false, // instance-of - false, // instance-of - false, // if-nez - false, // Not eliminated, fall-through branch. - true, // Eliminated. - false, // Not eliminated, different value. - false, // Not eliminated, different type. - false, // Not eliminated, bottom block. - }; - - PrepareBasicBlocks(bbs); - PrepareMIRs(mirs); - mirs_[0].dalvikInsn.vC = 1234; // type for instance-of - mirs_[1].dalvikInsn.vC = 1234; // type for instance-of - mirs_[3].dalvikInsn.vB = 1234; // type for check-cast - mirs_[4].dalvikInsn.vB = 1234; // type for check-cast - mirs_[5].dalvikInsn.vB = 1234; // type for check-cast - mirs_[6].dalvikInsn.vB = 4321; // type for check-cast - mirs_[7].dalvikInsn.vB = 1234; // type for check-cast - PerformGVN(); - PerformGVNCodeModifications(); - ASSERT_EQ(arraysize(expected_ignore_check_cast), mir_count_); - for (size_t i = 0u; i != mir_count_; ++i) { - int expected = expected_ignore_check_cast[i] ? MIR_IGNORE_CHECK_CAST : 0u; - EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; - } -} - -} // namespace art diff --git a/compiler/dex/gvn_dead_code_elimination.cc b/compiler/dex/gvn_dead_code_elimination.cc deleted file mode 100644 index 445859cc78..0000000000 --- a/compiler/dex/gvn_dead_code_elimination.cc +++ /dev/null @@ -1,1473 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "gvn_dead_code_elimination.h" - -#include "base/arena_bit_vector.h" -#include "base/bit_vector-inl.h" -#include "base/macros.h" -#include "base/allocator.h" -#include "compiler_enums.h" -#include "dataflow_iterator-inl.h" -#include "dex_instruction.h" -#include "dex/mir_graph.h" -#include "local_value_numbering.h" - -namespace art { - -constexpr uint16_t GvnDeadCodeElimination::kNoValue; -constexpr uint16_t GvnDeadCodeElimination::kNPos; - -inline uint16_t GvnDeadCodeElimination::MIRData::PrevChange(int v_reg) const { - DCHECK(has_def); - DCHECK(v_reg == vreg_def || v_reg == vreg_def + 1); - return (v_reg == vreg_def) ? prev_value.change : prev_value_high.change; -} - -inline void GvnDeadCodeElimination::MIRData::SetPrevChange(int v_reg, uint16_t change) { - DCHECK(has_def); - DCHECK(v_reg == vreg_def || v_reg == vreg_def + 1); - if (v_reg == vreg_def) { - prev_value.change = change; - } else { - prev_value_high.change = change; - } -} - -inline void GvnDeadCodeElimination::MIRData::RemovePrevChange(int v_reg, MIRData* prev_data) { - DCHECK_NE(PrevChange(v_reg), kNPos); - DCHECK(v_reg == prev_data->vreg_def || v_reg == prev_data->vreg_def + 1); - if (vreg_def == v_reg) { - if (prev_data->vreg_def == v_reg) { - prev_value = prev_data->prev_value; - low_def_over_high_word = prev_data->low_def_over_high_word; - } else { - prev_value = prev_data->prev_value_high; - low_def_over_high_word = !prev_data->high_def_over_low_word; - } - } else { - if (prev_data->vreg_def == v_reg) { - prev_value_high = prev_data->prev_value; - high_def_over_low_word = !prev_data->low_def_over_high_word; - } else { - prev_value_high = prev_data->prev_value_high; - high_def_over_low_word = prev_data->high_def_over_low_word; - } - } -} - -GvnDeadCodeElimination::VRegChains::VRegChains(uint32_t num_vregs, ScopedArenaAllocator* alloc) - : num_vregs_(num_vregs), - vreg_data_(alloc->AllocArray(num_vregs, kArenaAllocMisc)), - vreg_high_words_(false, Allocator::GetNoopAllocator(), - BitVector::BitsToWords(num_vregs), - alloc->AllocArray(BitVector::BitsToWords(num_vregs))), - mir_data_(alloc->Adapter()) { - mir_data_.reserve(100); -} - -inline void GvnDeadCodeElimination::VRegChains::Reset() { - DCHECK(mir_data_.empty()); - std::fill_n(vreg_data_, num_vregs_, VRegValue()); - vreg_high_words_.ClearAllBits(); -} - -void GvnDeadCodeElimination::VRegChains::AddMIRWithDef(MIR* mir, int v_reg, bool wide, - uint16_t new_value) { - uint16_t pos = mir_data_.size(); - mir_data_.emplace_back(mir); - MIRData* data = &mir_data_.back(); - data->has_def = true; - data->wide_def = wide; - data->vreg_def = v_reg; - - DCHECK_LT(static_cast(v_reg), num_vregs_); - data->prev_value = vreg_data_[v_reg]; - data->low_def_over_high_word = - (vreg_data_[v_reg].change != kNPos) - ? GetMIRData(vreg_data_[v_reg].change)->vreg_def + 1 == v_reg - : vreg_high_words_.IsBitSet(v_reg); - vreg_data_[v_reg].value = new_value; - vreg_data_[v_reg].change = pos; - vreg_high_words_.ClearBit(v_reg); - - if (wide) { - DCHECK_LT(static_cast(v_reg + 1), num_vregs_); - data->prev_value_high = vreg_data_[v_reg + 1]; - data->high_def_over_low_word = - (vreg_data_[v_reg + 1].change != kNPos) - ? GetMIRData(vreg_data_[v_reg + 1].change)->vreg_def == v_reg + 1 - : !vreg_high_words_.IsBitSet(v_reg + 1); - vreg_data_[v_reg + 1].value = new_value; - vreg_data_[v_reg + 1].change = pos; - vreg_high_words_.SetBit(v_reg + 1); - } -} - -inline void GvnDeadCodeElimination::VRegChains::AddMIRWithoutDef(MIR* mir) { - mir_data_.emplace_back(mir); -} - -void GvnDeadCodeElimination::VRegChains::RemoveLastMIRData() { - MIRData* data = LastMIRData(); - if (data->has_def) { - DCHECK_EQ(vreg_data_[data->vreg_def].change, NumMIRs() - 1u); - vreg_data_[data->vreg_def] = data->prev_value; - DCHECK(!vreg_high_words_.IsBitSet(data->vreg_def)); - if (data->low_def_over_high_word) { - vreg_high_words_.SetBit(data->vreg_def); - } - if (data->wide_def) { - DCHECK_EQ(vreg_data_[data->vreg_def + 1].change, NumMIRs() - 1u); - vreg_data_[data->vreg_def + 1] = data->prev_value_high; - DCHECK(vreg_high_words_.IsBitSet(data->vreg_def + 1)); - if (data->high_def_over_low_word) { - vreg_high_words_.ClearBit(data->vreg_def + 1); - } - } - } - mir_data_.pop_back(); -} - -void GvnDeadCodeElimination::VRegChains::RemoveTrailingNops() { - // There's at least one NOP to drop. There may be more. - MIRData* last_data = LastMIRData(); - DCHECK(!last_data->must_keep && !last_data->has_def); - do { - DCHECK_EQ(static_cast(last_data->mir->dalvikInsn.opcode), static_cast(kMirOpNop)); - mir_data_.pop_back(); - if (mir_data_.empty()) { - break; - } - last_data = LastMIRData(); - } while (!last_data->must_keep && !last_data->has_def); -} - -inline size_t GvnDeadCodeElimination::VRegChains::NumMIRs() const { - return mir_data_.size(); -} - -inline GvnDeadCodeElimination::MIRData* GvnDeadCodeElimination::VRegChains::GetMIRData(size_t pos) { - DCHECK_LT(pos, mir_data_.size()); - return &mir_data_[pos]; -} - -inline GvnDeadCodeElimination::MIRData* GvnDeadCodeElimination::VRegChains::LastMIRData() { - DCHECK(!mir_data_.empty()); - return &mir_data_.back(); -} - -uint32_t GvnDeadCodeElimination::VRegChains::NumVRegs() const { - return num_vregs_; -} - -void GvnDeadCodeElimination::VRegChains::InsertInitialValueHigh(int v_reg, uint16_t value) { - DCHECK_NE(value, kNoValue); - DCHECK_LT(static_cast(v_reg), num_vregs_); - uint16_t change = vreg_data_[v_reg].change; - if (change == kNPos) { - vreg_data_[v_reg].value = value; - vreg_high_words_.SetBit(v_reg); - } else { - while (true) { - MIRData* data = &mir_data_[change]; - DCHECK(data->vreg_def == v_reg || data->vreg_def + 1 == v_reg); - if (data->vreg_def == v_reg) { // Low word, use prev_value. - if (data->prev_value.change == kNPos) { - DCHECK_EQ(data->prev_value.value, kNoValue); - data->prev_value.value = value; - data->low_def_over_high_word = true; - break; - } - change = data->prev_value.change; - } else { // High word, use prev_value_high. - if (data->prev_value_high.change == kNPos) { - DCHECK_EQ(data->prev_value_high.value, kNoValue); - data->prev_value_high.value = value; - break; - } - change = data->prev_value_high.change; - } - } - } -} - -void GvnDeadCodeElimination::VRegChains::UpdateInitialVRegValue(int v_reg, bool wide, - const LocalValueNumbering* lvn) { - DCHECK_LT(static_cast(v_reg), num_vregs_); - if (!wide) { - if (vreg_data_[v_reg].value == kNoValue) { - uint16_t old_value = lvn->GetStartingVregValueNumber(v_reg); - if (old_value == kNoValue) { - // Maybe there was a wide value in v_reg before. Do not check for wide value in v_reg-1, - // that will be done only if we see a definition of v_reg-1, otherwise it's unnecessary. - old_value = lvn->GetStartingVregValueNumberWide(v_reg); - if (old_value != kNoValue) { - InsertInitialValueHigh(v_reg + 1, old_value); - } - } - vreg_data_[v_reg].value = old_value; - DCHECK(!vreg_high_words_.IsBitSet(v_reg)); // Keep marked as low word. - } - } else { - DCHECK_LT(static_cast(v_reg + 1), num_vregs_); - bool check_high = true; - if (vreg_data_[v_reg].value == kNoValue) { - uint16_t old_value = lvn->GetStartingVregValueNumberWide(v_reg); - if (old_value != kNoValue) { - InsertInitialValueHigh(v_reg + 1, old_value); - check_high = false; // High word has been processed. - } else { - // Maybe there was a narrow value before. Do not check for wide value in v_reg-1, - // that will be done only if we see a definition of v_reg-1, otherwise it's unnecessary. - old_value = lvn->GetStartingVregValueNumber(v_reg); - } - vreg_data_[v_reg].value = old_value; - DCHECK(!vreg_high_words_.IsBitSet(v_reg)); // Keep marked as low word. - } - if (check_high && vreg_data_[v_reg + 1].value == kNoValue) { - uint16_t old_value = lvn->GetStartingVregValueNumber(v_reg + 1); - if (old_value == kNoValue && static_cast(v_reg + 2) < num_vregs_) { - // Maybe there was a wide value before. - old_value = lvn->GetStartingVregValueNumberWide(v_reg + 1); - if (old_value != kNoValue) { - InsertInitialValueHigh(v_reg + 2, old_value); - } - } - vreg_data_[v_reg + 1].value = old_value; - DCHECK(!vreg_high_words_.IsBitSet(v_reg + 1)); // Keep marked as low word. - } - } -} - -inline uint16_t GvnDeadCodeElimination::VRegChains::LastChange(int v_reg) { - DCHECK_LT(static_cast(v_reg), num_vregs_); - return vreg_data_[v_reg].change; -} - -inline uint16_t GvnDeadCodeElimination::VRegChains::CurrentValue(int v_reg) { - DCHECK_LT(static_cast(v_reg), num_vregs_); - return vreg_data_[v_reg].value; -} - -uint16_t GvnDeadCodeElimination::VRegChains::FindKillHead(int v_reg, uint16_t cutoff) { - uint16_t current_value = this->CurrentValue(v_reg); - DCHECK_NE(current_value, kNoValue); - uint16_t change = LastChange(v_reg); - DCHECK_LT(change, mir_data_.size()); - DCHECK_GE(change, cutoff); - bool match_high_word = (mir_data_[change].vreg_def != v_reg); - do { - MIRData* data = &mir_data_[change]; - DCHECK(data->vreg_def == v_reg || data->vreg_def + 1 == v_reg); - if (data->vreg_def == v_reg) { // Low word, use prev_value. - if (data->prev_value.value == current_value && - match_high_word == data->low_def_over_high_word) { - break; - } - change = data->prev_value.change; - } else { // High word, use prev_value_high. - if (data->prev_value_high.value == current_value && - match_high_word != data->high_def_over_low_word) { - break; - } - change = data->prev_value_high.change; - } - if (change < cutoff) { - change = kNPos; - } - } while (change != kNPos); - return change; -} - -uint16_t GvnDeadCodeElimination::VRegChains::FindFirstChangeAfter(int v_reg, - uint16_t change) const { - DCHECK_LT(static_cast(v_reg), num_vregs_); - DCHECK_LT(change, mir_data_.size()); - uint16_t result = kNPos; - uint16_t search_change = vreg_data_[v_reg].change; - while (search_change != kNPos && search_change > change) { - result = search_change; - search_change = mir_data_[search_change].PrevChange(v_reg); - } - return result; -} - -void GvnDeadCodeElimination::VRegChains::ReplaceChange(uint16_t old_change, uint16_t new_change) { - const MIRData* old_data = GetMIRData(old_change); - DCHECK(old_data->has_def); - int count = old_data->wide_def ? 2 : 1; - for (int v_reg = old_data->vreg_def, end = old_data->vreg_def + count; v_reg != end; ++v_reg) { - uint16_t next_change = FindFirstChangeAfter(v_reg, old_change); - if (next_change == kNPos) { - DCHECK_EQ(vreg_data_[v_reg].change, old_change); - vreg_data_[v_reg].change = new_change; - DCHECK_EQ(vreg_high_words_.IsBitSet(v_reg), v_reg == old_data->vreg_def + 1); - // No change in vreg_high_words_. - } else { - DCHECK_EQ(mir_data_[next_change].PrevChange(v_reg), old_change); - mir_data_[next_change].SetPrevChange(v_reg, new_change); - } - } -} - -void GvnDeadCodeElimination::VRegChains::RemoveChange(uint16_t change) { - MIRData* data = &mir_data_[change]; - DCHECK(data->has_def); - int count = data->wide_def ? 2 : 1; - for (int v_reg = data->vreg_def, end = data->vreg_def + count; v_reg != end; ++v_reg) { - uint16_t next_change = FindFirstChangeAfter(v_reg, change); - if (next_change == kNPos) { - DCHECK_EQ(vreg_data_[v_reg].change, change); - vreg_data_[v_reg] = (data->vreg_def == v_reg) ? data->prev_value : data->prev_value_high; - DCHECK_EQ(vreg_high_words_.IsBitSet(v_reg), v_reg == data->vreg_def + 1); - if (data->vreg_def == v_reg && data->low_def_over_high_word) { - vreg_high_words_.SetBit(v_reg); - } else if (data->vreg_def != v_reg && data->high_def_over_low_word) { - vreg_high_words_.ClearBit(v_reg); - } - } else { - DCHECK_EQ(mir_data_[next_change].PrevChange(v_reg), change); - mir_data_[next_change].RemovePrevChange(v_reg, data); - } - } -} - -inline bool GvnDeadCodeElimination::VRegChains::IsTopChange(uint16_t change) const { - DCHECK_LT(change, mir_data_.size()); - const MIRData* data = &mir_data_[change]; - DCHECK(data->has_def); - DCHECK_LT(data->wide_def ? data->vreg_def + 1u : data->vreg_def, num_vregs_); - return vreg_data_[data->vreg_def].change == change && - (!data->wide_def || vreg_data_[data->vreg_def + 1u].change == change); -} - -bool GvnDeadCodeElimination::VRegChains::IsSRegUsed(uint16_t first_change, uint16_t last_change, - int s_reg) const { - DCHECK_LE(first_change, last_change); - DCHECK_LE(last_change, mir_data_.size()); - for (size_t c = first_change; c != last_change; ++c) { - SSARepresentation* ssa_rep = mir_data_[c].mir->ssa_rep; - for (int i = 0; i != ssa_rep->num_uses; ++i) { - if (ssa_rep->uses[i] == s_reg) { - return true; - } - } - } - return false; -} - -bool GvnDeadCodeElimination::VRegChains::IsVRegUsed(uint16_t first_change, uint16_t last_change, - int v_reg, MIRGraph* mir_graph) const { - DCHECK_LE(first_change, last_change); - DCHECK_LE(last_change, mir_data_.size()); - for (size_t c = first_change; c != last_change; ++c) { - SSARepresentation* ssa_rep = mir_data_[c].mir->ssa_rep; - for (int i = 0; i != ssa_rep->num_uses; ++i) { - if (mir_graph->SRegToVReg(ssa_rep->uses[i]) == v_reg) { - return true; - } - } - } - return false; -} - -void GvnDeadCodeElimination::VRegChains::RenameSRegUses(uint16_t first_change, uint16_t last_change, - int old_s_reg, int new_s_reg, bool wide) { - for (size_t c = first_change; c != last_change; ++c) { - SSARepresentation* ssa_rep = mir_data_[c].mir->ssa_rep; - for (int i = 0; i != ssa_rep->num_uses; ++i) { - if (ssa_rep->uses[i] == old_s_reg) { - ssa_rep->uses[i] = new_s_reg; - if (wide) { - ++i; - DCHECK_LT(i, ssa_rep->num_uses); - ssa_rep->uses[i] = new_s_reg + 1; - } - } - } - } -} - -void GvnDeadCodeElimination::VRegChains::RenameVRegUses(uint16_t first_change, uint16_t last_change, - int old_s_reg, int old_v_reg, - int new_s_reg, int new_v_reg) { - for (size_t c = first_change; c != last_change; ++c) { - MIR* mir = mir_data_[c].mir; - if (IsInstructionBinOp2Addr(mir->dalvikInsn.opcode) && - mir->ssa_rep->uses[0] == old_s_reg && old_v_reg != new_v_reg) { - // Rewrite binop_2ADDR with plain binop before doing the register rename. - ChangeBinOp2AddrToPlainBinOp(mir); - } - uint64_t df_attr = MIRGraph::GetDataFlowAttributes(mir); - size_t use = 0u; -#define REPLACE_VREG(REG) \ - if ((df_attr & DF_U##REG) != 0) { \ - if (mir->ssa_rep->uses[use] == old_s_reg) { \ - DCHECK_EQ(mir->dalvikInsn.v##REG, static_cast(old_v_reg)); \ - mir->dalvikInsn.v##REG = new_v_reg; \ - mir->ssa_rep->uses[use] = new_s_reg; \ - if ((df_attr & DF_##REG##_WIDE) != 0) { \ - DCHECK_EQ(mir->ssa_rep->uses[use + 1], old_s_reg + 1); \ - mir->ssa_rep->uses[use + 1] = new_s_reg + 1; \ - } \ - } \ - use += ((df_attr & DF_##REG##_WIDE) != 0) ? 2 : 1; \ - } - REPLACE_VREG(A) - REPLACE_VREG(B) - REPLACE_VREG(C) -#undef REPLACE_VREG - // We may encounter an out-of-order Phi which we need to ignore, otherwise we should - // only be asked to rename registers specified by DF_UA, DF_UB and DF_UC. - DCHECK_EQ(use, - static_cast(mir->dalvikInsn.opcode) == kMirOpPhi - ? 0u - : static_cast(mir->ssa_rep->num_uses)); - } -} - -GvnDeadCodeElimination::GvnDeadCodeElimination(const GlobalValueNumbering* gvn, - ScopedArenaAllocator* alloc) - : gvn_(gvn), - mir_graph_(gvn_->GetMirGraph()), - vreg_chains_(mir_graph_->GetNumOfCodeAndTempVRs(), alloc), - bb_(nullptr), - lvn_(nullptr), - no_uses_all_since_(0u), - unused_vregs_(new (alloc) ArenaBitVector(alloc, vreg_chains_.NumVRegs(), false)), - vregs_to_kill_(new (alloc) ArenaBitVector(alloc, vreg_chains_.NumVRegs(), false)), - kill_heads_(alloc->AllocArray(vreg_chains_.NumVRegs(), kArenaAllocMisc)), - changes_to_kill_(alloc->Adapter()), - dependent_vregs_(new (alloc) ArenaBitVector(alloc, vreg_chains_.NumVRegs(), false)) { - changes_to_kill_.reserve(16u); -} - -void GvnDeadCodeElimination::Apply(BasicBlock* bb) { - bb_ = bb; - lvn_ = gvn_->GetLvn(bb->id); - - RecordPass(); - BackwardPass(); - - DCHECK_EQ(no_uses_all_since_, 0u); - lvn_ = nullptr; - bb_ = nullptr; -} - -void GvnDeadCodeElimination::RecordPass() { - // Record MIRs with vreg definition data, eliminate single instructions. - vreg_chains_.Reset(); - DCHECK_EQ(no_uses_all_since_, 0u); - for (MIR* mir = bb_->first_mir_insn; mir != nullptr; mir = mir->next) { - if (RecordMIR(mir)) { - RecordPassTryToKillOverwrittenMoveOrMoveSrc(); - RecordPassTryToKillLastMIR(); - } - } -} - -void GvnDeadCodeElimination::BackwardPass() { - // Now process MIRs in reverse order, trying to eliminate them. - unused_vregs_->ClearAllBits(); // Implicitly depend on all vregs at the end of BB. - while (vreg_chains_.NumMIRs() != 0u) { - if (BackwardPassTryToKillLastMIR()) { - continue; - } - BackwardPassProcessLastMIR(); - } -} - -void GvnDeadCodeElimination::KillMIR(MIRData* data) { - DCHECK(!data->must_keep); - DCHECK(!data->uses_all_vregs); - DCHECK(data->has_def); - DCHECK(data->mir->ssa_rep->num_defs == 1 || data->mir->ssa_rep->num_defs == 2); - - KillMIR(data->mir); - data->has_def = false; - data->is_move = false; - data->is_move_src = false; -} - -void GvnDeadCodeElimination::KillMIR(MIR* mir) { - mir->dalvikInsn.opcode = static_cast(kMirOpNop); - mir->ssa_rep->num_uses = 0; - mir->ssa_rep->num_defs = 0; -} - -void GvnDeadCodeElimination::ChangeBinOp2AddrToPlainBinOp(MIR* mir) { - mir->dalvikInsn.vC = mir->dalvikInsn.vB; - mir->dalvikInsn.vB = mir->dalvikInsn.vA; - mir->dalvikInsn.opcode = static_cast( - mir->dalvikInsn.opcode - Instruction::ADD_INT_2ADDR + Instruction::ADD_INT); -} - -MIR* GvnDeadCodeElimination::CreatePhi(int s_reg) { - int v_reg = mir_graph_->SRegToVReg(s_reg); - MIR* phi = mir_graph_->NewMIR(); - phi->dalvikInsn.opcode = static_cast(kMirOpPhi); - phi->dalvikInsn.vA = v_reg; - phi->offset = bb_->start_offset; - phi->m_unit_index = 0; // Arbitrarily assign all Phi nodes to outermost method. - - phi->ssa_rep = static_cast(mir_graph_->GetArena()->Alloc( - sizeof(SSARepresentation), kArenaAllocDFInfo)); - - mir_graph_->AllocateSSADefData(phi, 1); - phi->ssa_rep->defs[0] = s_reg; - - size_t num_uses = bb_->predecessors.size(); - mir_graph_->AllocateSSAUseData(phi, num_uses); - size_t idx = 0u; - for (BasicBlockId pred_id : bb_->predecessors) { - BasicBlock* pred_bb = mir_graph_->GetBasicBlock(pred_id); - DCHECK(pred_bb != nullptr); - phi->ssa_rep->uses[idx] = pred_bb->data_flow_info->vreg_to_ssa_map_exit[v_reg]; - DCHECK_NE(phi->ssa_rep->uses[idx], INVALID_SREG); - idx++; - } - - phi->meta.phi_incoming = static_cast(mir_graph_->GetArena()->Alloc( - sizeof(BasicBlockId) * num_uses, kArenaAllocDFInfo)); - std::copy(bb_->predecessors.begin(), bb_->predecessors.end(), phi->meta.phi_incoming); - bb_->PrependMIR(phi); - return phi; -} - -MIR* GvnDeadCodeElimination::RenameSRegDefOrCreatePhi(uint16_t def_change, uint16_t last_change, - MIR* mir_to_kill) { - DCHECK(mir_to_kill->ssa_rep->num_defs == 1 || mir_to_kill->ssa_rep->num_defs == 2); - bool wide = (mir_to_kill->ssa_rep->num_defs != 1); - int new_s_reg = mir_to_kill->ssa_rep->defs[0]; - - // Just before we kill mir_to_kill, we need to replace the previous SSA reg assigned to the - // same dalvik reg to keep consistency with subsequent instructions. However, if there's no - // defining MIR for that dalvik reg, the preserved values must come from its predecessors - // and we need to create a new Phi (a degenerate Phi if there's only a single predecessor). - if (def_change == kNPos) { - if (wide) { - DCHECK_EQ(new_s_reg + 1, mir_to_kill->ssa_rep->defs[1]); - DCHECK_EQ(mir_graph_->SRegToVReg(new_s_reg) + 1, mir_graph_->SRegToVReg(new_s_reg + 1)); - CreatePhi(new_s_reg + 1); // High word Phi. - } - MIR* phi = CreatePhi(new_s_reg); - // If this is a degenerate Phi with all inputs being the same SSA reg, we need to its uses. - DCHECK_NE(phi->ssa_rep->num_uses, 0u); - int old_s_reg = phi->ssa_rep->uses[0]; - bool all_same = true; - for (size_t i = 1u, num = phi->ssa_rep->num_uses; i != num; ++i) { - if (phi->ssa_rep->uses[i] != old_s_reg) { - all_same = false; - break; - } - } - if (all_same) { - vreg_chains_.RenameSRegUses(0u, last_change, old_s_reg, new_s_reg, wide); - } - return phi; - } else { - DCHECK_LT(def_change, last_change); - DCHECK_LE(last_change, vreg_chains_.NumMIRs()); - MIRData* def_data = vreg_chains_.GetMIRData(def_change); - DCHECK(def_data->has_def); - int old_s_reg = def_data->mir->ssa_rep->defs[0]; - DCHECK_NE(old_s_reg, new_s_reg); - DCHECK_EQ(mir_graph_->SRegToVReg(old_s_reg), mir_graph_->SRegToVReg(new_s_reg)); - def_data->mir->ssa_rep->defs[0] = new_s_reg; - if (wide) { - if (static_cast(def_data->mir->dalvikInsn.opcode) == kMirOpPhi) { - // Currently the high word Phi is always located after the low word Phi. - MIR* phi_high = def_data->mir->next; - DCHECK(phi_high != nullptr && static_cast(phi_high->dalvikInsn.opcode) == kMirOpPhi); - DCHECK_EQ(phi_high->ssa_rep->defs[0], old_s_reg + 1); - phi_high->ssa_rep->defs[0] = new_s_reg + 1; - } else { - DCHECK_EQ(def_data->mir->ssa_rep->defs[1], old_s_reg + 1); - def_data->mir->ssa_rep->defs[1] = new_s_reg + 1; - } - } - vreg_chains_.RenameSRegUses(def_change + 1u, last_change, old_s_reg, new_s_reg, wide); - return nullptr; - } -} - - -void GvnDeadCodeElimination::BackwardPassProcessLastMIR() { - MIRData* data = vreg_chains_.LastMIRData(); - if (data->uses_all_vregs) { - DCHECK(data->must_keep); - unused_vregs_->ClearAllBits(); - DCHECK_EQ(no_uses_all_since_, vreg_chains_.NumMIRs()); - --no_uses_all_since_; - while (no_uses_all_since_ != 0u && - !vreg_chains_.GetMIRData(no_uses_all_since_ - 1u)->uses_all_vregs) { - --no_uses_all_since_; - } - } else { - if (data->has_def) { - unused_vregs_->SetBit(data->vreg_def); - if (data->wide_def) { - unused_vregs_->SetBit(data->vreg_def + 1); - } - } - for (int i = 0, num_uses = data->mir->ssa_rep->num_uses; i != num_uses; ++i) { - int v_reg = mir_graph_->SRegToVReg(data->mir->ssa_rep->uses[i]); - unused_vregs_->ClearBit(v_reg); - } - } - vreg_chains_.RemoveLastMIRData(); -} - -void GvnDeadCodeElimination::RecordPassKillMoveByRenamingSrcDef(uint16_t src_change, - uint16_t move_change) { - DCHECK_LT(src_change, move_change); - MIRData* src_data = vreg_chains_.GetMIRData(src_change); - MIRData* move_data = vreg_chains_.GetMIRData(move_change); - DCHECK(src_data->is_move_src); - DCHECK_EQ(src_data->wide_def, move_data->wide_def); - DCHECK(move_data->prev_value.change == kNPos || move_data->prev_value.change <= src_change); - DCHECK(!move_data->wide_def || move_data->prev_value_high.change == kNPos || - move_data->prev_value_high.change <= src_change); - - int old_s_reg = src_data->mir->ssa_rep->defs[0]; - // NOTE: old_s_reg may differ from move_data->mir->ssa_rep->uses[0]; value names must match. - int new_s_reg = move_data->mir->ssa_rep->defs[0]; - DCHECK_NE(old_s_reg, new_s_reg); - - if (IsInstructionBinOp2Addr(src_data->mir->dalvikInsn.opcode) && - src_data->vreg_def != move_data->vreg_def) { - // Rewrite binop_2ADDR with plain binop before doing the register rename. - ChangeBinOp2AddrToPlainBinOp(src_data->mir); - } - // Remove src_change from the vreg chain(s). - vreg_chains_.RemoveChange(src_change); - // Replace the move_change with the src_change, copying all necessary data. - src_data->is_move_src = move_data->is_move_src; - src_data->low_def_over_high_word = move_data->low_def_over_high_word; - src_data->high_def_over_low_word = move_data->high_def_over_low_word; - src_data->vreg_def = move_data->vreg_def; - src_data->prev_value = move_data->prev_value; - src_data->prev_value_high = move_data->prev_value_high; - src_data->mir->dalvikInsn.vA = move_data->vreg_def; - src_data->mir->ssa_rep->defs[0] = new_s_reg; - if (move_data->wide_def) { - DCHECK_EQ(src_data->mir->ssa_rep->defs[1], old_s_reg + 1); - src_data->mir->ssa_rep->defs[1] = new_s_reg + 1; - } - vreg_chains_.ReplaceChange(move_change, src_change); - - // Rename uses and kill the move. - vreg_chains_.RenameVRegUses(src_change + 1u, vreg_chains_.NumMIRs(), - old_s_reg, mir_graph_->SRegToVReg(old_s_reg), - new_s_reg, mir_graph_->SRegToVReg(new_s_reg)); - KillMIR(move_data); -} - -void GvnDeadCodeElimination::RecordPassTryToKillOverwrittenMoveOrMoveSrc(uint16_t check_change) { - MIRData* data = vreg_chains_.GetMIRData(check_change); - DCHECK(data->is_move || data->is_move_src); - int32_t dest_s_reg = data->mir->ssa_rep->defs[0]; - - if (data->is_move) { - // Check if source vreg has changed since the MOVE. - int32_t src_s_reg = data->mir->ssa_rep->uses[0]; - uint32_t src_v_reg = mir_graph_->SRegToVReg(src_s_reg); - uint16_t src_change = vreg_chains_.FindFirstChangeAfter(src_v_reg, check_change); - bool wide = data->wide_def; - if (wide) { - uint16_t src_change_high = vreg_chains_.FindFirstChangeAfter(src_v_reg + 1, check_change); - if (src_change_high != kNPos && (src_change == kNPos || src_change_high < src_change)) { - src_change = src_change_high; - } - } - if (src_change == kNPos || - !vreg_chains_.IsSRegUsed(src_change + 1u, vreg_chains_.NumMIRs(), dest_s_reg)) { - // We can simply change all uses of dest to src. - size_t rename_end = (src_change != kNPos) ? src_change + 1u : vreg_chains_.NumMIRs(); - vreg_chains_.RenameVRegUses(check_change + 1u, rename_end, - dest_s_reg, mir_graph_->SRegToVReg(dest_s_reg), - src_s_reg, mir_graph_->SRegToVReg(src_s_reg)); - - // Now, remove the MOVE from the vreg chain(s) and kill it. - vreg_chains_.RemoveChange(check_change); - KillMIR(data); - return; - } - } - - if (data->is_move_src) { - // Try to find a MOVE to a vreg that wasn't changed since check_change. - uint16_t value_name = - data->wide_def ? lvn_->GetSregValueWide(dest_s_reg) : lvn_->GetSregValue(dest_s_reg); - uint32_t dest_v_reg = mir_graph_->SRegToVReg(dest_s_reg); - for (size_t c = check_change + 1u, size = vreg_chains_.NumMIRs(); c != size; ++c) { - MIRData* d = vreg_chains_.GetMIRData(c); - if (d->is_move && d->wide_def == data->wide_def && - (d->prev_value.change == kNPos || d->prev_value.change <= check_change) && - (!d->wide_def || - d->prev_value_high.change == kNPos || d->prev_value_high.change <= check_change)) { - // Compare value names to find move to move. - int32_t src_s_reg = d->mir->ssa_rep->uses[0]; - uint16_t src_name = - (d->wide_def ? lvn_->GetSregValueWide(src_s_reg) : lvn_->GetSregValue(src_s_reg)); - if (value_name == src_name) { - // Check if the move's destination vreg is unused between check_change and the move. - uint32_t new_dest_v_reg = mir_graph_->SRegToVReg(d->mir->ssa_rep->defs[0]); - if (!vreg_chains_.IsVRegUsed(check_change + 1u, c, new_dest_v_reg, mir_graph_) && - (!d->wide_def || - !vreg_chains_.IsVRegUsed(check_change + 1u, c, new_dest_v_reg + 1, mir_graph_))) { - // If the move's destination vreg changed, check if the vreg we're trying - // to rename is unused after that change. - uint16_t dest_change = vreg_chains_.FindFirstChangeAfter(new_dest_v_reg, c); - if (d->wide_def) { - uint16_t dest_change_high = vreg_chains_.FindFirstChangeAfter(new_dest_v_reg + 1, c); - if (dest_change_high != kNPos && - (dest_change == kNPos || dest_change_high < dest_change)) { - dest_change = dest_change_high; - } - } - if (dest_change == kNPos || - !vreg_chains_.IsVRegUsed(dest_change + 1u, size, dest_v_reg, mir_graph_)) { - RecordPassKillMoveByRenamingSrcDef(check_change, c); - return; - } - } - } - } - } - } -} - -void GvnDeadCodeElimination::RecordPassTryToKillOverwrittenMoveOrMoveSrc() { - // Check if we're overwriting a the result of a move or the definition of a source of a move. - // For MOVE_WIDE, we may be overwriting partially; if that's the case, check that the other - // word wasn't previously overwritten - we would have tried to rename back then. - MIRData* data = vreg_chains_.LastMIRData(); - if (!data->has_def) { - return; - } - // NOTE: Instructions such as new-array implicitly use all vregs (if they throw) but they can - // define a move source which can be renamed. Therefore we allow the checked change to be the - // change before no_uses_all_since_. This has no effect on moves as they never use all vregs. - if (data->prev_value.change != kNPos && data->prev_value.change + 1u >= no_uses_all_since_) { - MIRData* check_data = vreg_chains_.GetMIRData(data->prev_value.change); - bool try_to_kill = false; - if (!check_data->is_move && !check_data->is_move_src) { - DCHECK(!try_to_kill); - } else if (!check_data->wide_def) { - // Narrow move; always fully overwritten by the last MIR. - try_to_kill = true; - } else if (data->low_def_over_high_word) { - // Overwriting only the high word; is the low word still valid? - DCHECK_EQ(check_data->vreg_def + 1u, data->vreg_def); - if (vreg_chains_.LastChange(check_data->vreg_def) == data->prev_value.change) { - try_to_kill = true; - } - } else if (!data->wide_def) { - // Overwriting only the low word, is the high word still valid? - if (vreg_chains_.LastChange(data->vreg_def + 1) == data->prev_value.change) { - try_to_kill = true; - } - } else { - // Overwriting both words; was the high word still from the same move? - if (data->prev_value_high.change == data->prev_value.change) { - try_to_kill = true; - } - } - if (try_to_kill) { - RecordPassTryToKillOverwrittenMoveOrMoveSrc(data->prev_value.change); - } - } - if (data->wide_def && data->high_def_over_low_word && - data->prev_value_high.change != kNPos && - data->prev_value_high.change + 1u >= no_uses_all_since_) { - MIRData* check_data = vreg_chains_.GetMIRData(data->prev_value_high.change); - bool try_to_kill = false; - if (!check_data->is_move && !check_data->is_move_src) { - DCHECK(!try_to_kill); - } else if (!check_data->wide_def) { - // Narrow move; always fully overwritten by the last MIR. - try_to_kill = true; - } else if (vreg_chains_.LastChange(check_data->vreg_def + 1) == - data->prev_value_high.change) { - // High word is still valid. - try_to_kill = true; - } - if (try_to_kill) { - RecordPassTryToKillOverwrittenMoveOrMoveSrc(data->prev_value_high.change); - } - } -} - -void GvnDeadCodeElimination::RecordPassTryToKillLastMIR() { - MIRData* last_data = vreg_chains_.LastMIRData(); - if (last_data->must_keep) { - return; - } - if (UNLIKELY(!last_data->has_def)) { - // Must be an eliminated MOVE. Drop its data and data of all eliminated MIRs before it. - vreg_chains_.RemoveTrailingNops(); - return; - } - - // Try to kill a sequence of consecutive definitions of the same vreg. Allow mixing - // wide and non-wide defs; consider high word dead if low word has been overwritten. - uint16_t current_value = vreg_chains_.CurrentValue(last_data->vreg_def); - uint16_t change = vreg_chains_.NumMIRs() - 1u; - MIRData* data = last_data; - while (data->prev_value.value != current_value) { - --change; - if (data->prev_value.change == kNPos || data->prev_value.change != change) { - return; - } - data = vreg_chains_.GetMIRData(data->prev_value.change); - if (data->must_keep || !data->has_def || data->vreg_def != last_data->vreg_def) { - return; - } - } - - bool wide = last_data->wide_def; - if (wide) { - // Check that the low word is valid. - if (data->low_def_over_high_word) { - return; - } - // Check that the high word is valid. - MIRData* high_data = data; - if (!high_data->wide_def) { - uint16_t high_change = vreg_chains_.FindFirstChangeAfter(data->vreg_def + 1, change); - DCHECK_NE(high_change, kNPos); - high_data = vreg_chains_.GetMIRData(high_change); - DCHECK_EQ(high_data->vreg_def, data->vreg_def); - } - if (high_data->prev_value_high.value != current_value || high_data->high_def_over_low_word) { - return; - } - } - - MIR* phi = RenameSRegDefOrCreatePhi(data->prev_value.change, change, last_data->mir); - for (size_t i = 0, count = vreg_chains_.NumMIRs() - change; i != count; ++i) { - KillMIR(vreg_chains_.LastMIRData()->mir); - vreg_chains_.RemoveLastMIRData(); - } - if (phi != nullptr) { - // Though the Phi has been added to the beginning, we can put the MIRData at the end. - vreg_chains_.AddMIRWithDef(phi, phi->dalvikInsn.vA, wide, current_value); - // Reset the previous value to avoid eventually eliminating the Phi itself (unless unused). - last_data = vreg_chains_.LastMIRData(); - last_data->prev_value.value = kNoValue; - last_data->prev_value_high.value = kNoValue; - } -} - -uint16_t GvnDeadCodeElimination::FindChangesToKill(uint16_t first_change, uint16_t last_change) { - // Process dependencies for changes in range [first_change, last_change) and record all - // changes that we need to kill. Return kNPos if there's a dependent change that must be - // kept unconditionally; otherwise the end of the range processed before encountering - // a change that defines a dalvik reg that we need to keep (last_change on full success). - changes_to_kill_.clear(); - dependent_vregs_->ClearAllBits(); - for (size_t change = first_change; change != last_change; ++change) { - MIRData* data = vreg_chains_.GetMIRData(change); - DCHECK(!data->uses_all_vregs); - bool must_not_depend = data->must_keep; - bool depends = false; - // Check if the MIR defines a vreg we're trying to eliminate. - if (data->has_def && vregs_to_kill_->IsBitSet(data->vreg_def)) { - if (change < kill_heads_[data->vreg_def]) { - must_not_depend = true; - } else { - depends = true; - } - } - if (data->has_def && data->wide_def && vregs_to_kill_->IsBitSet(data->vreg_def + 1)) { - if (change < kill_heads_[data->vreg_def + 1]) { - must_not_depend = true; - } else { - depends = true; - } - } - if (!depends) { - // Check for dependency through SSA reg uses. - SSARepresentation* ssa_rep = data->mir->ssa_rep; - for (int i = 0; i != ssa_rep->num_uses; ++i) { - if (dependent_vregs_->IsBitSet(mir_graph_->SRegToVReg(ssa_rep->uses[i]))) { - depends = true; - break; - } - } - } - // Now check if we can eliminate the insn if we need to. - if (depends && must_not_depend) { - return kNPos; - } - if (depends && data->has_def && - vreg_chains_.IsTopChange(change) && !vregs_to_kill_->IsBitSet(data->vreg_def) && - !unused_vregs_->IsBitSet(data->vreg_def) && - (!data->wide_def || !unused_vregs_->IsBitSet(data->vreg_def + 1))) { - // This is a top change but neither unnecessary nor one of the top kill changes. - return change; - } - // Finally, update the data. - if (depends) { - changes_to_kill_.push_back(change); - if (data->has_def) { - dependent_vregs_->SetBit(data->vreg_def); - if (data->wide_def) { - dependent_vregs_->SetBit(data->vreg_def + 1); - } - } - } else { - if (data->has_def) { - dependent_vregs_->ClearBit(data->vreg_def); - if (data->wide_def) { - dependent_vregs_->ClearBit(data->vreg_def + 1); - } - } - } - } - return last_change; -} - -void GvnDeadCodeElimination::BackwardPassTryToKillRevertVRegs() { -} - -bool GvnDeadCodeElimination::BackwardPassTryToKillLastMIR() { - MIRData* last_data = vreg_chains_.LastMIRData(); - if (last_data->must_keep) { - return false; - } - DCHECK(!last_data->uses_all_vregs); - if (!last_data->has_def) { - // Previously eliminated. - DCHECK_EQ(static_cast(last_data->mir->dalvikInsn.opcode), static_cast(kMirOpNop)); - vreg_chains_.RemoveTrailingNops(); - return true; - } - if (unused_vregs_->IsBitSet(last_data->vreg_def) || - (last_data->wide_def && unused_vregs_->IsBitSet(last_data->vreg_def + 1))) { - if (last_data->wide_def) { - // For wide defs, one of the vregs may still be considered needed, fix that. - unused_vregs_->SetBit(last_data->vreg_def); - unused_vregs_->SetBit(last_data->vreg_def + 1); - } - KillMIR(last_data->mir); - vreg_chains_.RemoveLastMIRData(); - return true; - } - - vregs_to_kill_->ClearAllBits(); - size_t num_mirs = vreg_chains_.NumMIRs(); - DCHECK_NE(num_mirs, 0u); - uint16_t kill_change = num_mirs - 1u; - uint16_t start = num_mirs; - size_t num_killed_top_changes = 0u; - while (num_killed_top_changes != kMaxNumTopChangesToKill && - kill_change != kNPos && kill_change != num_mirs) { - ++num_killed_top_changes; - - DCHECK(vreg_chains_.IsTopChange(kill_change)); - MIRData* data = vreg_chains_.GetMIRData(kill_change); - int count = data->wide_def ? 2 : 1; - for (int v_reg = data->vreg_def, end = data->vreg_def + count; v_reg != end; ++v_reg) { - uint16_t kill_head = vreg_chains_.FindKillHead(v_reg, no_uses_all_since_); - if (kill_head == kNPos) { - return false; - } - kill_heads_[v_reg] = kill_head; - vregs_to_kill_->SetBit(v_reg); - start = std::min(start, kill_head); - } - DCHECK_LT(start, vreg_chains_.NumMIRs()); - - kill_change = FindChangesToKill(start, num_mirs); - } - - if (kill_change != num_mirs) { - return false; - } - - // Kill all MIRs marked as dependent. - for (uint32_t v_reg : vregs_to_kill_->Indexes()) { - // Rename s_regs or create Phi only once for each MIR (only for low word). - MIRData* data = vreg_chains_.GetMIRData(vreg_chains_.LastChange(v_reg)); - DCHECK(data->has_def); - if (data->vreg_def == v_reg) { - MIRData* kill_head_data = vreg_chains_.GetMIRData(kill_heads_[v_reg]); - RenameSRegDefOrCreatePhi(kill_head_data->PrevChange(v_reg), num_mirs, data->mir); - } else { - DCHECK_EQ(data->vreg_def + 1u, v_reg); - DCHECK_EQ(vreg_chains_.GetMIRData(kill_heads_[v_reg - 1u])->PrevChange(v_reg - 1u), - vreg_chains_.GetMIRData(kill_heads_[v_reg])->PrevChange(v_reg)); - } - } - for (auto it = changes_to_kill_.rbegin(), end = changes_to_kill_.rend(); it != end; ++it) { - MIRData* data = vreg_chains_.GetMIRData(*it); - DCHECK(!data->must_keep); - DCHECK(data->has_def); - vreg_chains_.RemoveChange(*it); - KillMIR(data); - } - - // Each dependent register not in vregs_to_kill_ is either already marked unused or - // it's one word of a wide register where the other word has been overwritten. - unused_vregs_->UnionIfNotIn(dependent_vregs_, vregs_to_kill_); - - vreg_chains_.RemoveTrailingNops(); - return true; -} - -bool GvnDeadCodeElimination::RecordMIR(MIR* mir) { - bool must_keep = false; - bool uses_all_vregs = false; - bool is_move = false; - uint16_t opcode = mir->dalvikInsn.opcode; - switch (opcode) { - case kMirOpPhi: { - // Determine if this Phi is merging wide regs. - RegLocation raw_dest = gvn_->GetMirGraph()->GetRawDest(mir); - if (raw_dest.high_word) { - // This is the high part of a wide reg. Ignore the Phi. - return false; - } - bool wide = raw_dest.wide; - // Record the value. - DCHECK_EQ(mir->ssa_rep->num_defs, 1); - int s_reg = mir->ssa_rep->defs[0]; - uint16_t new_value = wide ? lvn_->GetSregValueWide(s_reg) : lvn_->GetSregValue(s_reg); - - int v_reg = mir_graph_->SRegToVReg(s_reg); - DCHECK_EQ(vreg_chains_.CurrentValue(v_reg), kNoValue); // No previous def for v_reg. - if (wide) { - DCHECK_EQ(vreg_chains_.CurrentValue(v_reg + 1), kNoValue); - } - vreg_chains_.AddMIRWithDef(mir, v_reg, wide, new_value); - return true; // Avoid the common processing. - } - - case kMirOpNop: - case Instruction::NOP: - // Don't record NOPs. - return false; - - case kMirOpCheck: - must_keep = true; - uses_all_vregs = true; - break; - - case Instruction::RETURN_VOID: - case Instruction::RETURN: - case Instruction::RETURN_OBJECT: - case Instruction::RETURN_WIDE: - case Instruction::GOTO: - case Instruction::GOTO_16: - case Instruction::GOTO_32: - case Instruction::PACKED_SWITCH: - case Instruction::SPARSE_SWITCH: - case Instruction::IF_EQ: - case Instruction::IF_NE: - case Instruction::IF_LT: - case Instruction::IF_GE: - case Instruction::IF_GT: - case Instruction::IF_LE: - case Instruction::IF_EQZ: - case Instruction::IF_NEZ: - case Instruction::IF_LTZ: - case Instruction::IF_GEZ: - case Instruction::IF_GTZ: - case Instruction::IF_LEZ: - case kMirOpFusedCmplFloat: - case kMirOpFusedCmpgFloat: - case kMirOpFusedCmplDouble: - case kMirOpFusedCmpgDouble: - case kMirOpFusedCmpLong: - must_keep = true; - uses_all_vregs = true; // Keep the implicit dependencies on all vregs. - break; - - case Instruction::CONST_CLASS: - case Instruction::CONST_STRING: - case Instruction::CONST_STRING_JUMBO: - // NOTE: While we're currently treating CONST_CLASS, CONST_STRING and CONST_STRING_JUMBO - // as throwing but we could conceivably try and eliminate those exceptions if we're - // retrieving the class/string repeatedly. - must_keep = true; - uses_all_vregs = true; - break; - - case Instruction::MONITOR_ENTER: - case Instruction::MONITOR_EXIT: - // We can actually try and optimize across the acquire operation of MONITOR_ENTER, - // the value names provided by GVN reflect the possible changes to memory visibility. - // NOTE: In ART, MONITOR_ENTER and MONITOR_EXIT can throw only NPE. - must_keep = true; - uses_all_vregs = (mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0; - break; - - case Instruction::INVOKE_DIRECT: - case Instruction::INVOKE_DIRECT_RANGE: - case Instruction::INVOKE_VIRTUAL: - case Instruction::INVOKE_VIRTUAL_RANGE: - case Instruction::INVOKE_SUPER: - case Instruction::INVOKE_SUPER_RANGE: - case Instruction::INVOKE_INTERFACE: - case Instruction::INVOKE_INTERFACE_RANGE: - case Instruction::INVOKE_STATIC: - case Instruction::INVOKE_STATIC_RANGE: - case Instruction::THROW: - case Instruction::FILLED_NEW_ARRAY: - case Instruction::FILLED_NEW_ARRAY_RANGE: - case Instruction::FILL_ARRAY_DATA: - must_keep = true; - uses_all_vregs = true; - break; - - case Instruction::NEW_INSTANCE: - case Instruction::NEW_ARRAY: - must_keep = true; - uses_all_vregs = true; - break; - - case Instruction::CHECK_CAST: - DCHECK_EQ(mir->ssa_rep->num_uses, 1); - must_keep = true; // Keep for type information even if MIR_IGNORE_CHECK_CAST. - uses_all_vregs = (mir->optimization_flags & MIR_IGNORE_CHECK_CAST) == 0; - break; - - case kMirOpNullCheck: - DCHECK_EQ(mir->ssa_rep->num_uses, 1); - if ((mir->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) { - mir->ssa_rep->num_uses = 0; - mir->dalvikInsn.opcode = static_cast(kMirOpNop); - return false; - } - must_keep = true; - uses_all_vregs = true; - break; - - case Instruction::MOVE_RESULT: - case Instruction::MOVE_RESULT_OBJECT: - case Instruction::MOVE_RESULT_WIDE: - break; - - case Instruction::INSTANCE_OF: - break; - - case Instruction::MOVE_EXCEPTION: - must_keep = true; - break; - - case kMirOpCopy: - case Instruction::MOVE: - case Instruction::MOVE_FROM16: - case Instruction::MOVE_16: - case Instruction::MOVE_WIDE: - case Instruction::MOVE_WIDE_FROM16: - case Instruction::MOVE_WIDE_16: - case Instruction::MOVE_OBJECT: - case Instruction::MOVE_OBJECT_FROM16: - case Instruction::MOVE_OBJECT_16: { - is_move = true; - // If the MIR defining src vreg is known, allow renaming all uses of src vreg to dest vreg - // while updating the defining MIR to directly define dest vreg. However, changing Phi's - // def this way doesn't work without changing MIRs in other BBs. - int src_v_reg = mir_graph_->SRegToVReg(mir->ssa_rep->uses[0]); - int src_change = vreg_chains_.LastChange(src_v_reg); - if (src_change != kNPos) { - MIRData* src_data = vreg_chains_.GetMIRData(src_change); - if (static_cast(src_data->mir->dalvikInsn.opcode) != kMirOpPhi) { - src_data->is_move_src = true; - } - } - break; - } - - case Instruction::CONST_4: - case Instruction::CONST_16: - case Instruction::CONST: - case Instruction::CONST_HIGH16: - case Instruction::CONST_WIDE_16: - case Instruction::CONST_WIDE_32: - case Instruction::CONST_WIDE: - case Instruction::CONST_WIDE_HIGH16: - case Instruction::CMPL_FLOAT: - case Instruction::CMPG_FLOAT: - case Instruction::CMPL_DOUBLE: - case Instruction::CMPG_DOUBLE: - case Instruction::CMP_LONG: - case Instruction::NEG_INT: - case Instruction::NOT_INT: - case Instruction::NEG_LONG: - case Instruction::NOT_LONG: - case Instruction::NEG_FLOAT: - case Instruction::NEG_DOUBLE: - case Instruction::INT_TO_LONG: - case Instruction::INT_TO_FLOAT: - case Instruction::INT_TO_DOUBLE: - case Instruction::LONG_TO_INT: - case Instruction::LONG_TO_FLOAT: - case Instruction::LONG_TO_DOUBLE: - case Instruction::FLOAT_TO_INT: - case Instruction::FLOAT_TO_LONG: - case Instruction::FLOAT_TO_DOUBLE: - case Instruction::DOUBLE_TO_INT: - case Instruction::DOUBLE_TO_LONG: - case Instruction::DOUBLE_TO_FLOAT: - case Instruction::INT_TO_BYTE: - case Instruction::INT_TO_CHAR: - case Instruction::INT_TO_SHORT: - case Instruction::ADD_INT: - case Instruction::SUB_INT: - case Instruction::MUL_INT: - case Instruction::AND_INT: - case Instruction::OR_INT: - case Instruction::XOR_INT: - case Instruction::SHL_INT: - case Instruction::SHR_INT: - case Instruction::USHR_INT: - case Instruction::ADD_LONG: - case Instruction::SUB_LONG: - case Instruction::MUL_LONG: - case Instruction::AND_LONG: - case Instruction::OR_LONG: - case Instruction::XOR_LONG: - case Instruction::SHL_LONG: - case Instruction::SHR_LONG: - case Instruction::USHR_LONG: - case Instruction::ADD_FLOAT: - case Instruction::SUB_FLOAT: - case Instruction::MUL_FLOAT: - case Instruction::DIV_FLOAT: - case Instruction::REM_FLOAT: - case Instruction::ADD_DOUBLE: - case Instruction::SUB_DOUBLE: - case Instruction::MUL_DOUBLE: - case Instruction::DIV_DOUBLE: - case Instruction::REM_DOUBLE: - case Instruction::ADD_INT_2ADDR: - case Instruction::SUB_INT_2ADDR: - case Instruction::MUL_INT_2ADDR: - case Instruction::AND_INT_2ADDR: - case Instruction::OR_INT_2ADDR: - case Instruction::XOR_INT_2ADDR: - case Instruction::SHL_INT_2ADDR: - case Instruction::SHR_INT_2ADDR: - case Instruction::USHR_INT_2ADDR: - case Instruction::ADD_LONG_2ADDR: - case Instruction::SUB_LONG_2ADDR: - case Instruction::MUL_LONG_2ADDR: - case Instruction::AND_LONG_2ADDR: - case Instruction::OR_LONG_2ADDR: - case Instruction::XOR_LONG_2ADDR: - case Instruction::SHL_LONG_2ADDR: - case Instruction::SHR_LONG_2ADDR: - case Instruction::USHR_LONG_2ADDR: - case Instruction::ADD_FLOAT_2ADDR: - case Instruction::SUB_FLOAT_2ADDR: - case Instruction::MUL_FLOAT_2ADDR: - case Instruction::DIV_FLOAT_2ADDR: - case Instruction::REM_FLOAT_2ADDR: - case Instruction::ADD_DOUBLE_2ADDR: - case Instruction::SUB_DOUBLE_2ADDR: - case Instruction::MUL_DOUBLE_2ADDR: - case Instruction::DIV_DOUBLE_2ADDR: - case Instruction::REM_DOUBLE_2ADDR: - case Instruction::ADD_INT_LIT16: - case Instruction::RSUB_INT: - case Instruction::MUL_INT_LIT16: - case Instruction::AND_INT_LIT16: - case Instruction::OR_INT_LIT16: - case Instruction::XOR_INT_LIT16: - case Instruction::ADD_INT_LIT8: - case Instruction::RSUB_INT_LIT8: - case Instruction::MUL_INT_LIT8: - case Instruction::AND_INT_LIT8: - case Instruction::OR_INT_LIT8: - case Instruction::XOR_INT_LIT8: - case Instruction::SHL_INT_LIT8: - case Instruction::SHR_INT_LIT8: - case Instruction::USHR_INT_LIT8: - break; - - case Instruction::DIV_INT: - case Instruction::REM_INT: - case Instruction::DIV_LONG: - case Instruction::REM_LONG: - case Instruction::DIV_INT_2ADDR: - case Instruction::REM_INT_2ADDR: - case Instruction::DIV_LONG_2ADDR: - case Instruction::REM_LONG_2ADDR: - if ((mir->optimization_flags & MIR_IGNORE_DIV_ZERO_CHECK) == 0) { - must_keep = true; - uses_all_vregs = true; - } - break; - - case Instruction::DIV_INT_LIT16: - case Instruction::REM_INT_LIT16: - case Instruction::DIV_INT_LIT8: - case Instruction::REM_INT_LIT8: - if (mir->dalvikInsn.vC == 0) { // Explicit division by 0? - must_keep = true; - uses_all_vregs = true; - } - break; - - case Instruction::ARRAY_LENGTH: - if ((mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0) { - must_keep = true; - uses_all_vregs = true; - } - break; - - case Instruction::AGET_OBJECT: - case Instruction::AGET: - case Instruction::AGET_WIDE: - case Instruction::AGET_BOOLEAN: - case Instruction::AGET_BYTE: - case Instruction::AGET_CHAR: - case Instruction::AGET_SHORT: - if ((mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0 || - (mir->optimization_flags & MIR_IGNORE_RANGE_CHECK) == 0) { - must_keep = true; - uses_all_vregs = true; - } - break; - - case Instruction::APUT_OBJECT: - case Instruction::APUT: - case Instruction::APUT_WIDE: - case Instruction::APUT_BYTE: - case Instruction::APUT_BOOLEAN: - case Instruction::APUT_SHORT: - case Instruction::APUT_CHAR: - must_keep = true; - if ((mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0 || - (mir->optimization_flags & MIR_IGNORE_RANGE_CHECK) == 0) { - uses_all_vregs = true; - } - break; - - case Instruction::IGET_OBJECT: - case Instruction::IGET: - case Instruction::IGET_WIDE: - case Instruction::IGET_BOOLEAN: - case Instruction::IGET_BYTE: - case Instruction::IGET_CHAR: - case Instruction::IGET_SHORT: { - const MirIFieldLoweringInfo& info = mir_graph_->GetIFieldLoweringInfo(mir); - if ((mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0 || - !info.IsResolved() || !info.FastGet()) { - must_keep = true; - uses_all_vregs = true; - } else if (info.IsVolatile()) { - must_keep = true; - } - break; - } - - case Instruction::IPUT_OBJECT: - case Instruction::IPUT: - case Instruction::IPUT_WIDE: - case Instruction::IPUT_BOOLEAN: - case Instruction::IPUT_BYTE: - case Instruction::IPUT_CHAR: - case Instruction::IPUT_SHORT: { - must_keep = true; - const MirIFieldLoweringInfo& info = mir_graph_->GetIFieldLoweringInfo(mir); - if ((mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0 || - !info.IsResolved() || !info.FastPut()) { - uses_all_vregs = true; - } - break; - } - - case Instruction::SGET_OBJECT: - case Instruction::SGET: - case Instruction::SGET_WIDE: - case Instruction::SGET_BOOLEAN: - case Instruction::SGET_BYTE: - case Instruction::SGET_CHAR: - case Instruction::SGET_SHORT: { - const MirSFieldLoweringInfo& info = mir_graph_->GetSFieldLoweringInfo(mir); - if ((mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 0 || - !info.IsResolved() || !info.FastGet()) { - must_keep = true; - uses_all_vregs = true; - } else if (info.IsVolatile()) { - must_keep = true; - } - break; - } - - case Instruction::SPUT_OBJECT: - case Instruction::SPUT: - case Instruction::SPUT_WIDE: - case Instruction::SPUT_BOOLEAN: - case Instruction::SPUT_BYTE: - case Instruction::SPUT_CHAR: - case Instruction::SPUT_SHORT: { - must_keep = true; - const MirSFieldLoweringInfo& info = mir_graph_->GetSFieldLoweringInfo(mir); - if ((mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 0 || - !info.IsResolved() || !info.FastPut()) { - uses_all_vregs = true; - } - break; - } - - default: - LOG(FATAL) << "Unexpected opcode: " << opcode; - UNREACHABLE(); - } - - if (mir->ssa_rep->num_defs != 0) { - DCHECK(mir->ssa_rep->num_defs == 1 || mir->ssa_rep->num_defs == 2); - bool wide = (mir->ssa_rep->num_defs == 2); - int s_reg = mir->ssa_rep->defs[0]; - int v_reg = mir_graph_->SRegToVReg(s_reg); - uint16_t new_value = wide ? lvn_->GetSregValueWide(s_reg) : lvn_->GetSregValue(s_reg); - DCHECK_NE(new_value, kNoValue); - - vreg_chains_.UpdateInitialVRegValue(v_reg, wide, lvn_); - vreg_chains_.AddMIRWithDef(mir, v_reg, wide, new_value); - if (is_move) { - // Allow renaming all uses of dest vreg to src vreg. - vreg_chains_.LastMIRData()->is_move = true; - } - } else { - vreg_chains_.AddMIRWithoutDef(mir); - DCHECK(!is_move) << opcode; - } - - if (must_keep) { - MIRData* last_data = vreg_chains_.LastMIRData(); - last_data->must_keep = true; - if (uses_all_vregs) { - last_data->uses_all_vregs = true; - no_uses_all_since_ = vreg_chains_.NumMIRs(); - } - } else { - DCHECK_NE(mir->ssa_rep->num_defs, 0) << opcode; - DCHECK(!uses_all_vregs) << opcode; - } - return true; -} - -} // namespace art diff --git a/compiler/dex/gvn_dead_code_elimination.h b/compiler/dex/gvn_dead_code_elimination.h deleted file mode 100644 index 06022db501..0000000000 --- a/compiler/dex/gvn_dead_code_elimination.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_GVN_DEAD_CODE_ELIMINATION_H_ -#define ART_COMPILER_DEX_GVN_DEAD_CODE_ELIMINATION_H_ - -#include "base/arena_object.h" -#include "base/scoped_arena_containers.h" -#include "global_value_numbering.h" - -namespace art { - -class ArenaBitVector; -class BasicBlock; -class LocalValueNumbering; -class MIR; -class MIRGraph; - -/** - * @class DeadCodeElimination - * @details Eliminate dead code based on the results of global value numbering. - * Also get rid of MOVE insns when we can use the source instead of destination - * without affecting the vreg values at safepoints; this is useful in methods - * with a large number of vregs that frequently move values to and from low vregs - * to accommodate insns that can work only with the low 16 or 256 vregs. - */ -class GvnDeadCodeElimination : public DeletableArenaObject { - public: - GvnDeadCodeElimination(const GlobalValueNumbering* gvn, ScopedArenaAllocator* alloc); - - // Apply the DCE to a basic block. - void Apply(BasicBlock* bb); - - private: - static constexpr uint16_t kNoValue = GlobalValueNumbering::kNoValue; - static constexpr uint16_t kNPos = 0xffffu; - static constexpr size_t kMaxNumTopChangesToKill = 2; - - struct VRegValue { - VRegValue() : value(kNoValue), change(kNPos) { } - - // Value name as reported by GVN, kNoValue if not available. - uint16_t value; - // Index of the change in mir_data_ that defined the value, kNPos if initial value for the BB. - uint16_t change; - }; - - struct MIRData { - explicit MIRData(MIR* m) - : mir(m), uses_all_vregs(false), must_keep(false), is_move(false), is_move_src(false), - has_def(false), wide_def(false), - low_def_over_high_word(false), high_def_over_low_word(false), vreg_def(0u), - prev_value(), prev_value_high() { - } - - uint16_t PrevChange(int v_reg) const; - void SetPrevChange(int v_reg, uint16_t change); - void RemovePrevChange(int v_reg, MIRData* prev_data); - - MIR* mir; - bool uses_all_vregs : 1; // If mir uses all vregs, uses in mir->ssa_rep are irrelevant. - bool must_keep : 1; - bool is_move : 1; - bool is_move_src : 1; - bool has_def : 1; - bool wide_def : 1; - bool low_def_over_high_word : 1; - bool high_def_over_low_word : 1; - uint16_t vreg_def; - VRegValue prev_value; - VRegValue prev_value_high; // For wide defs. - }; - - class VRegChains { - public: - VRegChains(uint32_t num_vregs, ScopedArenaAllocator* alloc); - - void Reset(); - - void AddMIRWithDef(MIR* mir, int v_reg, bool wide, uint16_t new_value); - void AddMIRWithoutDef(MIR* mir); - void RemoveLastMIRData(); - void RemoveTrailingNops(); - - size_t NumMIRs() const; - MIRData* GetMIRData(size_t pos); - MIRData* LastMIRData(); - - uint32_t NumVRegs() const; - void InsertInitialValueHigh(int v_reg, uint16_t value); - void UpdateInitialVRegValue(int v_reg, bool wide, const LocalValueNumbering* lvn); - uint16_t LastChange(int v_reg); - uint16_t CurrentValue(int v_reg); - - uint16_t FindKillHead(int v_reg, uint16_t cutoff); - uint16_t FindFirstChangeAfter(int v_reg, uint16_t change) const; - void ReplaceChange(uint16_t old_change, uint16_t new_change); - void RemoveChange(uint16_t change); - bool IsTopChange(uint16_t change) const; - bool IsSRegUsed(uint16_t first_change, uint16_t last_change, int s_reg) const; - bool IsVRegUsed(uint16_t first_change, uint16_t last_change, int v_reg, - MIRGraph* mir_graph) const; - void RenameSRegUses(uint16_t first_change, uint16_t last_change, - int old_s_reg, int new_s_reg, bool wide); - void RenameVRegUses(uint16_t first_change, uint16_t last_change, - int old_s_reg, int old_v_reg, int new_s_reg, int new_v_reg); - - private: - const uint32_t num_vregs_; - VRegValue* const vreg_data_; - BitVector vreg_high_words_; - ScopedArenaVector mir_data_; - }; - - void RecordPass(); - void BackwardPass(); - - void KillMIR(MIRData* data); - static void KillMIR(MIR* mir); - static void ChangeBinOp2AddrToPlainBinOp(MIR* mir); - MIR* CreatePhi(int s_reg); - MIR* RenameSRegDefOrCreatePhi(uint16_t def_change, uint16_t last_change, MIR* mir_to_kill); - - // Update state variables going backwards through a MIR. - void BackwardPassProcessLastMIR(); - - uint16_t FindChangesToKill(uint16_t first_change, uint16_t last_change); - void BackwardPassTryToKillRevertVRegs(); - bool BackwardPassTryToKillLastMIR(); - - void RecordPassKillMoveByRenamingSrcDef(uint16_t src_change, uint16_t move_change); - void RecordPassTryToKillOverwrittenMoveOrMoveSrc(uint16_t check_change); - void RecordPassTryToKillOverwrittenMoveOrMoveSrc(); - void RecordPassTryToKillLastMIR(); - - bool RecordMIR(MIR* mir); - - const GlobalValueNumbering* const gvn_; - MIRGraph* const mir_graph_; - - VRegChains vreg_chains_; - BasicBlock* bb_; - const LocalValueNumbering* lvn_; - size_t no_uses_all_since_; // The change index after the last change with uses_all_vregs set. - - // Data used when processing MIRs in reverse order. - ArenaBitVector* unused_vregs_; // vregs that are not needed later. - ArenaBitVector* vregs_to_kill_; // vregs that revert to a previous value. - uint16_t* kill_heads_; // For each vreg in vregs_to_kill_, the first change to kill. - ScopedArenaVector changes_to_kill_; - ArenaBitVector* dependent_vregs_; -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_GVN_DEAD_CODE_ELIMINATION_H_ diff --git a/compiler/dex/gvn_dead_code_elimination_test.cc b/compiler/dex/gvn_dead_code_elimination_test.cc deleted file mode 100644 index 22fb835b70..0000000000 --- a/compiler/dex/gvn_dead_code_elimination_test.cc +++ /dev/null @@ -1,2201 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "dataflow_iterator-inl.h" -#include "dex/mir_field_info.h" -#include "global_value_numbering.h" -#include "gvn_dead_code_elimination.h" -#include "local_value_numbering.h" -#include "gtest/gtest.h" - -namespace art { - -class GvnDeadCodeEliminationTest : public testing::Test { - protected: - static constexpr uint16_t kNoValue = GlobalValueNumbering::kNoValue; - - struct IFieldDef { - uint16_t field_idx; - uintptr_t declaring_dex_file; - uint16_t declaring_field_idx; - bool is_volatile; - DexMemAccessType type; - }; - - struct SFieldDef { - uint16_t field_idx; - uintptr_t declaring_dex_file; - uint16_t declaring_field_idx; - bool is_volatile; - DexMemAccessType type; - }; - - struct BBDef { - static constexpr size_t kMaxSuccessors = 4; - static constexpr size_t kMaxPredecessors = 4; - - BBType type; - size_t num_successors; - BasicBlockId successors[kMaxPredecessors]; - size_t num_predecessors; - BasicBlockId predecessors[kMaxPredecessors]; - }; - - struct MIRDef { - static constexpr size_t kMaxSsaDefs = 2; - static constexpr size_t kMaxSsaUses = 4; - - BasicBlockId bbid; - Instruction::Code opcode; - int64_t value; - uint32_t field_info; - size_t num_uses; - int32_t uses[kMaxSsaUses]; - size_t num_defs; - int32_t defs[kMaxSsaDefs]; - }; - -#define DEF_SUCC0() \ - 0u, { } -#define DEF_SUCC1(s1) \ - 1u, { s1 } -#define DEF_SUCC2(s1, s2) \ - 2u, { s1, s2 } -#define DEF_SUCC3(s1, s2, s3) \ - 3u, { s1, s2, s3 } -#define DEF_SUCC4(s1, s2, s3, s4) \ - 4u, { s1, s2, s3, s4 } -#define DEF_PRED0() \ - 0u, { } -#define DEF_PRED1(p1) \ - 1u, { p1 } -#define DEF_PRED2(p1, p2) \ - 2u, { p1, p2 } -#define DEF_PRED3(p1, p2, p3) \ - 3u, { p1, p2, p3 } -#define DEF_PRED4(p1, p2, p3, p4) \ - 4u, { p1, p2, p3, p4 } -#define DEF_BB(type, succ, pred) \ - { type, succ, pred } - -#define DEF_CONST(bb, opcode, reg, value) \ - { bb, opcode, value, 0u, 0, { }, 1, { reg } } -#define DEF_CONST_WIDE(bb, opcode, reg, value) \ - { bb, opcode, value, 0u, 0, { }, 2, { reg, reg + 1 } } -#define DEF_CONST_STRING(bb, opcode, reg, index) \ - { bb, opcode, index, 0u, 0, { }, 1, { reg } } -#define DEF_IGET(bb, opcode, reg, obj, field_info) \ - { bb, opcode, 0u, field_info, 1, { obj }, 1, { reg } } -#define DEF_IGET_WIDE(bb, opcode, reg, obj, field_info) \ - { bb, opcode, 0u, field_info, 1, { obj }, 2, { reg, reg + 1 } } -#define DEF_IPUT(bb, opcode, reg, obj, field_info) \ - { bb, opcode, 0u, field_info, 2, { reg, obj }, 0, { } } -#define DEF_IPUT_WIDE(bb, opcode, reg, obj, field_info) \ - { bb, opcode, 0u, field_info, 3, { reg, reg + 1, obj }, 0, { } } -#define DEF_SGET(bb, opcode, reg, field_info) \ - { bb, opcode, 0u, field_info, 0, { }, 1, { reg } } -#define DEF_SGET_WIDE(bb, opcode, reg, field_info) \ - { bb, opcode, 0u, field_info, 0, { }, 2, { reg, reg + 1 } } -#define DEF_SPUT(bb, opcode, reg, field_info) \ - { bb, opcode, 0u, field_info, 1, { reg }, 0, { } } -#define DEF_SPUT_WIDE(bb, opcode, reg, field_info) \ - { bb, opcode, 0u, field_info, 2, { reg, reg + 1 }, 0, { } } -#define DEF_AGET(bb, opcode, reg, obj, idx) \ - { bb, opcode, 0u, 0u, 2, { obj, idx }, 1, { reg } } -#define DEF_AGET_WIDE(bb, opcode, reg, obj, idx) \ - { bb, opcode, 0u, 0u, 2, { obj, idx }, 2, { reg, reg + 1 } } -#define DEF_APUT(bb, opcode, reg, obj, idx) \ - { bb, opcode, 0u, 0u, 3, { reg, obj, idx }, 0, { } } -#define DEF_APUT_WIDE(bb, opcode, reg, obj, idx) \ - { bb, opcode, 0u, 0u, 4, { reg, reg + 1, obj, idx }, 0, { } } -#define DEF_INVOKE1(bb, opcode, reg) \ - { bb, opcode, 0u, 0u, 1, { reg }, 0, { } } -#define DEF_UNIQUE_REF(bb, opcode, reg) \ - { bb, opcode, 0u, 0u, 0, { }, 1, { reg } } // CONST_CLASS, CONST_STRING, NEW_ARRAY, ... -#define DEF_IFZ(bb, opcode, reg) \ - { bb, opcode, 0u, 0u, 1, { reg }, 0, { } } -#define DEF_MOVE(bb, opcode, reg, src) \ - { bb, opcode, 0u, 0u, 1, { src }, 1, { reg } } -#define DEF_MOVE_WIDE(bb, opcode, reg, src) \ - { bb, opcode, 0u, 0u, 2, { src, src + 1 }, 2, { reg, reg + 1 } } -#define DEF_PHI2(bb, reg, src1, src2) \ - { bb, static_cast(kMirOpPhi), 0, 0u, 2u, { src1, src2 }, 1, { reg } } -#define DEF_UNOP(bb, opcode, result, src1) \ - { bb, opcode, 0u, 0u, 1, { src1 }, 1, { result } } -#define DEF_BINOP(bb, opcode, result, src1, src2) \ - { bb, opcode, 0u, 0u, 2, { src1, src2 }, 1, { result } } -#define DEF_BINOP_WIDE(bb, opcode, result, src1, src2) \ - { bb, opcode, 0u, 0u, 4, { src1, src1 + 1, src2, src2 + 1 }, 2, { result, result + 1 } } - - void DoPrepareIFields(const IFieldDef* defs, size_t count) { - cu_.mir_graph->ifield_lowering_infos_.clear(); - cu_.mir_graph->ifield_lowering_infos_.reserve(count); - for (size_t i = 0u; i != count; ++i) { - const IFieldDef* def = &defs[i]; - MirIFieldLoweringInfo field_info(def->field_idx, def->type, false); - if (def->declaring_dex_file != 0u) { - field_info.declaring_dex_file_ = reinterpret_cast(def->declaring_dex_file); - field_info.declaring_field_idx_ = def->declaring_field_idx; - field_info.flags_ = - MirIFieldLoweringInfo::kFlagFastGet | MirIFieldLoweringInfo::kFlagFastPut | - (field_info.flags_ & ~(def->is_volatile ? 0u : MirIFieldLoweringInfo::kFlagIsVolatile)); - } - cu_.mir_graph->ifield_lowering_infos_.push_back(field_info); - } - } - - template - void PrepareIFields(const IFieldDef (&defs)[count]) { - DoPrepareIFields(defs, count); - } - - void DoPrepareSFields(const SFieldDef* defs, size_t count) { - cu_.mir_graph->sfield_lowering_infos_.clear(); - cu_.mir_graph->sfield_lowering_infos_.reserve(count); - for (size_t i = 0u; i != count; ++i) { - const SFieldDef* def = &defs[i]; - MirSFieldLoweringInfo field_info(def->field_idx, def->type); - // Mark even unresolved fields as initialized. - field_info.flags_ |= MirSFieldLoweringInfo::kFlagClassIsInitialized; - // NOTE: MirSFieldLoweringInfo::kFlagClassIsInDexCache isn't used by GVN. - if (def->declaring_dex_file != 0u) { - field_info.declaring_dex_file_ = reinterpret_cast(def->declaring_dex_file); - field_info.declaring_field_idx_ = def->declaring_field_idx; - field_info.flags_ = - MirSFieldLoweringInfo::kFlagFastGet | MirSFieldLoweringInfo::kFlagFastPut | - (field_info.flags_ & ~(def->is_volatile ? 0u : MirSFieldLoweringInfo::kFlagIsVolatile)); - } - cu_.mir_graph->sfield_lowering_infos_.push_back(field_info); - } - } - - template - void PrepareSFields(const SFieldDef (&defs)[count]) { - DoPrepareSFields(defs, count); - } - - void DoPrepareBasicBlocks(const BBDef* defs, size_t count) { - cu_.mir_graph->block_id_map_.clear(); - cu_.mir_graph->block_list_.clear(); - ASSERT_LT(3u, count); // null, entry, exit and at least one bytecode block. - ASSERT_EQ(kNullBlock, defs[0].type); - ASSERT_EQ(kEntryBlock, defs[1].type); - ASSERT_EQ(kExitBlock, defs[2].type); - for (size_t i = 0u; i != count; ++i) { - const BBDef* def = &defs[i]; - BasicBlock* bb = cu_.mir_graph->CreateNewBB(def->type); - if (def->num_successors <= 2) { - bb->successor_block_list_type = kNotUsed; - bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : 0u; - bb->taken = (def->num_successors >= 2) ? def->successors[1] : 0u; - } else { - bb->successor_block_list_type = kPackedSwitch; - bb->fall_through = 0u; - bb->taken = 0u; - bb->successor_blocks.reserve(def->num_successors); - for (size_t j = 0u; j != def->num_successors; ++j) { - SuccessorBlockInfo* successor_block_info = - static_cast(cu_.arena.Alloc(sizeof(SuccessorBlockInfo), - kArenaAllocSuccessors)); - successor_block_info->block = j; - successor_block_info->key = 0u; // Not used by class init check elimination. - bb->successor_blocks.push_back(successor_block_info); - } - } - bb->predecessors.assign(def->predecessors, def->predecessors + def->num_predecessors); - if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) { - bb->data_flow_info = static_cast( - cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo)); - bb->data_flow_info->live_in_v = live_in_v_; - bb->data_flow_info->vreg_to_ssa_map_exit = nullptr; - } - } - ASSERT_EQ(count, cu_.mir_graph->block_list_.size()); - cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1]; - ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type); - cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_[2]; - ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type); - } - - template - void PrepareBasicBlocks(const BBDef (&defs)[count]) { - DoPrepareBasicBlocks(defs, count); - } - - int SRegToVReg(int32_t s_reg, bool wide) { - int v_reg = cu_.mir_graph->SRegToVReg(s_reg); - CHECK_LT(static_cast(v_reg), num_vregs_); - if (wide) { - CHECK_LT(static_cast(v_reg + 1), num_vregs_); - } - return v_reg; - } - - int SRegToVReg(int32_t* uses, size_t* use, bool wide) { - int v_reg = SRegToVReg(uses[*use], wide); - if (wide) { - CHECK_EQ(uses[*use] + 1, uses[*use + 1]); - *use += 2u; - } else { - *use += 1u; - } - return v_reg; - } - - void DoPrepareMIRs(const MIRDef* defs, size_t count) { - mir_count_ = count; - mirs_ = reinterpret_cast(cu_.arena.Alloc(sizeof(MIR) * count, kArenaAllocMIR)); - ssa_reps_.resize(count); - for (size_t i = 0u; i != count; ++i) { - const MIRDef* def = &defs[i]; - MIR* mir = &mirs_[i]; - ASSERT_LT(def->bbid, cu_.mir_graph->block_list_.size()); - BasicBlock* bb = cu_.mir_graph->block_list_[def->bbid]; - bb->AppendMIR(mir); - mir->dalvikInsn.opcode = def->opcode; - mir->dalvikInsn.vB = static_cast(def->value); - mir->dalvikInsn.vB_wide = def->value; - if (IsInstructionIGetOrIPut(def->opcode)) { - ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.size()); - mir->meta.ifield_lowering_info = def->field_info; - ASSERT_EQ(cu_.mir_graph->ifield_lowering_infos_[def->field_info].MemAccessType(), - IGetOrIPutMemAccessType(def->opcode)); - } else if (IsInstructionSGetOrSPut(def->opcode)) { - ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.size()); - mir->meta.sfield_lowering_info = def->field_info; - ASSERT_EQ(cu_.mir_graph->sfield_lowering_infos_[def->field_info].MemAccessType(), - SGetOrSPutMemAccessType(def->opcode)); - } else if (def->opcode == static_cast(kMirOpPhi)) { - mir->meta.phi_incoming = - allocator_->AllocArray(def->num_uses, kArenaAllocDFInfo); - ASSERT_EQ(def->num_uses, bb->predecessors.size()); - std::copy(bb->predecessors.begin(), bb->predecessors.end(), mir->meta.phi_incoming); - } - mir->ssa_rep = &ssa_reps_[i]; - cu_.mir_graph->AllocateSSAUseData(mir, def->num_uses); - std::copy_n(def->uses, def->num_uses, mir->ssa_rep->uses); - // Keep mir->ssa_rep->fp_use[.] zero-initialized (false). Not used by DCE, only copied. - cu_.mir_graph->AllocateSSADefData(mir, def->num_defs); - std::copy_n(def->defs, def->num_defs, mir->ssa_rep->defs); - // Keep mir->ssa_rep->fp_def[.] zero-initialized (false). Not used by DCE, only copied. - mir->dalvikInsn.opcode = def->opcode; - mir->offset = i; // LVN uses offset only for debug output - mir->optimization_flags = 0u; - uint64_t df_attrs = MIRGraph::GetDataFlowAttributes(mir); - if ((df_attrs & DF_DA) != 0) { - CHECK_NE(def->num_defs, 0u); - mir->dalvikInsn.vA = SRegToVReg(def->defs[0], (df_attrs & DF_A_WIDE) != 0); - bb->data_flow_info->vreg_to_ssa_map_exit[mir->dalvikInsn.vA] = def->defs[0]; - if ((df_attrs & DF_A_WIDE) != 0) { - CHECK_EQ(def->defs[0] + 1, def->defs[1]); - bb->data_flow_info->vreg_to_ssa_map_exit[mir->dalvikInsn.vA + 1u] = def->defs[0] + 1; - } - } - if ((df_attrs & (DF_UA | DF_UB | DF_UC)) != 0) { - size_t use = 0; - if ((df_attrs & DF_UA) != 0) { - mir->dalvikInsn.vA = SRegToVReg(mir->ssa_rep->uses, &use, (df_attrs & DF_A_WIDE) != 0); - } - if ((df_attrs & DF_UB) != 0) { - mir->dalvikInsn.vB = SRegToVReg(mir->ssa_rep->uses, &use, (df_attrs & DF_B_WIDE) != 0); - } - if ((df_attrs & DF_UC) != 0) { - mir->dalvikInsn.vC = SRegToVReg(mir->ssa_rep->uses, &use, (df_attrs & DF_C_WIDE) != 0); - } - DCHECK_EQ(def->num_uses, use); - } - } - DexFile::CodeItem* code_item = static_cast( - cu_.arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc)); - code_item->insns_size_in_code_units_ = 2u * count; - code_item->registers_size_ = kMaxVRegs; - cu_.mir_graph->current_code_item_ = code_item; - } - - template - void PrepareMIRs(const MIRDef (&defs)[count]) { - DoPrepareMIRs(defs, count); - } - - template - void PrepareSRegToVRegMap(const int (&map)[count]) { - cu_.mir_graph->ssa_base_vregs_.assign(map, map + count); - num_vregs_ = *std::max_element(map, map + count) + 1u; - AllNodesIterator iterator(cu_.mir_graph.get()); - for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) { - if (bb->data_flow_info != nullptr) { - bb->data_flow_info->vreg_to_ssa_map_exit = static_cast( - cu_.arena.Alloc(sizeof(int32_t) * num_vregs_, kArenaAllocDFInfo)); - std::fill_n(bb->data_flow_info->vreg_to_ssa_map_exit, num_vregs_, INVALID_SREG); - } - } - } - - void PerformGVN() { - cu_.mir_graph->SSATransformationStart(); - cu_.mir_graph->ComputeDFSOrders(); - cu_.mir_graph->ComputeDominators(); - cu_.mir_graph->ComputeTopologicalSortOrder(); - cu_.mir_graph->SSATransformationEnd(); - cu_.mir_graph->temp_.gvn.ifield_ids = GlobalValueNumbering::PrepareGvnFieldIds( - allocator_.get(), cu_.mir_graph->ifield_lowering_infos_); - cu_.mir_graph->temp_.gvn.sfield_ids = GlobalValueNumbering::PrepareGvnFieldIds( - allocator_.get(), cu_.mir_graph->sfield_lowering_infos_); - ASSERT_TRUE(gvn_ == nullptr); - gvn_.reset(new (allocator_.get()) GlobalValueNumbering(&cu_, allocator_.get(), - GlobalValueNumbering::kModeGvn)); - value_names_.resize(mir_count_, 0xffffu); - LoopRepeatingTopologicalSortIterator iterator(cu_.mir_graph.get()); - bool change = false; - for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) { - LocalValueNumbering* lvn = gvn_->PrepareBasicBlock(bb); - if (lvn != nullptr) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - value_names_[mir - mirs_] = lvn->GetValueNumber(mir); - } - } - change = (lvn != nullptr) && gvn_->FinishBasicBlock(bb); - ASSERT_TRUE(gvn_->Good()); - } - } - - void PerformGVNCodeModifications() { - ASSERT_TRUE(gvn_ != nullptr); - ASSERT_TRUE(gvn_->Good()); - gvn_->StartPostProcessing(); - TopologicalSortIterator iterator(cu_.mir_graph.get()); - for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) { - LocalValueNumbering* lvn = gvn_->PrepareBasicBlock(bb); - if (lvn != nullptr) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - uint16_t value_name = lvn->GetValueNumber(mir); - ASSERT_EQ(value_name, value_names_[mir - mirs_]); - } - } - bool change = (lvn != nullptr) && gvn_->FinishBasicBlock(bb); - ASSERT_FALSE(change); - ASSERT_TRUE(gvn_->Good()); - } - } - - void FillVregToSsaRegExitMaps() { - // Fill in vreg_to_ssa_map_exit for each BB. - PreOrderDfsIterator iterator(cu_.mir_graph.get()); - for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) { - if (bb->block_type == kDalvikByteCode) { - CHECK(!bb->predecessors.empty()); - BasicBlock* pred_bb = cu_.mir_graph->GetBasicBlock(bb->predecessors[0]); - for (size_t v_reg = 0; v_reg != num_vregs_; ++v_reg) { - if (bb->data_flow_info->vreg_to_ssa_map_exit[v_reg] == INVALID_SREG) { - bb->data_flow_info->vreg_to_ssa_map_exit[v_reg] = - pred_bb->data_flow_info->vreg_to_ssa_map_exit[v_reg]; - } - } - } - } - } - - template - void MarkAsWideSRegs(const int32_t (&sregs)[count]) { - for (int32_t sreg : sregs) { - cu_.mir_graph->reg_location_[sreg].wide = true; - cu_.mir_graph->reg_location_[sreg + 1].wide = true; - cu_.mir_graph->reg_location_[sreg + 1].high_word = true; - } - } - - void PerformDCE() { - FillVregToSsaRegExitMaps(); - cu_.mir_graph->GetNumOfCodeAndTempVRs(); - dce_.reset(new (allocator_.get()) GvnDeadCodeElimination(gvn_.get(), allocator_.get())); - PreOrderDfsIterator iterator(cu_.mir_graph.get()); - for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) { - if (bb->block_type == kDalvikByteCode) { - dce_->Apply(bb); - } - } - } - - void PerformGVN_DCE() { - PerformGVN(); - PerformGVNCodeModifications(); // Eliminate null/range checks. - PerformDCE(); - } - - template - void ExpectValueNamesNE(const size_t (&indexes)[count]) { - for (size_t i1 = 0; i1 != count; ++i1) { - size_t idx1 = indexes[i1]; - for (size_t i2 = i1 + 1; i2 != count; ++i2) { - size_t idx2 = indexes[i2]; - EXPECT_NE(value_names_[idx1], value_names_[idx2]) << idx1 << " " << idx2; - } - } - } - - template - void ExpectNoNullCheck(const size_t (&indexes)[count]) { - for (size_t i = 0; i != count; ++i) { - size_t idx = indexes[i]; - EXPECT_EQ(MIR_IGNORE_NULL_CHECK, mirs_[idx].optimization_flags & MIR_IGNORE_NULL_CHECK) - << idx; - } - size_t num_no_null_ck = 0u; - for (size_t i = 0; i != mir_count_; ++i) { - if ((mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) { - ++num_no_null_ck; - } - } - EXPECT_EQ(count, num_no_null_ck); - } - - GvnDeadCodeEliminationTest() - : pool_(), - cu_(&pool_, kRuntimeISA, nullptr, nullptr), - num_vregs_(0u), - mir_count_(0u), - mirs_(nullptr), - ssa_reps_(), - allocator_(), - gvn_(), - dce_(), - value_names_(), - live_in_v_(new (&cu_.arena) ArenaBitVector(&cu_.arena, kMaxSsaRegs, false)) { - cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); - cu_.access_flags = kAccStatic; // Don't let "this" interfere with this test. - allocator_.reset(ScopedArenaAllocator::Create(&cu_.arena_stack)); - // By default, the zero-initialized reg_location_[.] with ref == false tells LVN that - // 0 constants are integral, not references, and the values are all narrow. - // Nothing else is used by LVN/GVN. Tests can override the default values as needed. - cu_.mir_graph->reg_location_ = static_cast(cu_.arena.Alloc( - kMaxSsaRegs * sizeof(cu_.mir_graph->reg_location_[0]), kArenaAllocRegAlloc)); - cu_.mir_graph->num_ssa_regs_ = kMaxSsaRegs; - // Bind all possible sregs to live vregs for test purposes. - live_in_v_->SetInitialBits(kMaxSsaRegs); - cu_.mir_graph->ssa_base_vregs_.reserve(kMaxSsaRegs); - cu_.mir_graph->ssa_subscripts_.reserve(kMaxSsaRegs); - for (unsigned int i = 0; i < kMaxSsaRegs; i++) { - cu_.mir_graph->ssa_base_vregs_.push_back(i); - cu_.mir_graph->ssa_subscripts_.push_back(0); - } - // Set shorty for a void-returning method without arguments. - cu_.shorty = "V"; - } - - static constexpr size_t kMaxSsaRegs = 16384u; - static constexpr size_t kMaxVRegs = 256u; - - ArenaPool pool_; - CompilationUnit cu_; - size_t num_vregs_; - size_t mir_count_; - MIR* mirs_; - std::vector ssa_reps_; - std::unique_ptr allocator_; - std::unique_ptr gvn_; - std::unique_ptr dce_; - std::vector value_names_; - ArenaBitVector* live_in_v_; -}; - -constexpr uint16_t GvnDeadCodeEliminationTest::kNoValue; - -class GvnDeadCodeEliminationTestSimple : public GvnDeadCodeEliminationTest { - public: - GvnDeadCodeEliminationTestSimple(); - - private: - static const BBDef kSimpleBbs[]; -}; - -const GvnDeadCodeEliminationTest::BBDef GvnDeadCodeEliminationTestSimple::kSimpleBbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(3)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(1)), -}; - -GvnDeadCodeEliminationTestSimple::GvnDeadCodeEliminationTestSimple() - : GvnDeadCodeEliminationTest() { - PrepareBasicBlocks(kSimpleBbs); -} - -class GvnDeadCodeEliminationTestDiamond : public GvnDeadCodeEliminationTest { - public: - GvnDeadCodeEliminationTestDiamond(); - - private: - static const BBDef kDiamondBbs[]; -}; - -const GvnDeadCodeEliminationTest::BBDef GvnDeadCodeEliminationTestDiamond::kDiamondBbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)), // Block #3, top of the diamond. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Block #4, left side. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Block #5, right side. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)), // Block #6, bottom. -}; - -GvnDeadCodeEliminationTestDiamond::GvnDeadCodeEliminationTestDiamond() - : GvnDeadCodeEliminationTest() { - PrepareBasicBlocks(kDiamondBbs); -} - -class GvnDeadCodeEliminationTestLoop : public GvnDeadCodeEliminationTest { - public: - GvnDeadCodeEliminationTestLoop(); - - private: - static const BBDef kLoopBbs[]; -}; - -const GvnDeadCodeEliminationTest::BBDef GvnDeadCodeEliminationTestLoop::kLoopBbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)), // "taken" loops to self. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), -}; - -GvnDeadCodeEliminationTestLoop::GvnDeadCodeEliminationTestLoop() - : GvnDeadCodeEliminationTest() { - PrepareBasicBlocks(kLoopBbs); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Rename1) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 2u, 0u), - DEF_IGET(3, Instruction::IGET, 3u, 2u, 1u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 3 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[2]); - - const size_t no_null_ck_indexes[] = { 1, 3 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, true, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the IGET uses the s_reg 0, v_reg 0, defined by mirs_[0]. - ASSERT_EQ(1, mirs_[3].ssa_rep->num_uses); - EXPECT_EQ(0, mirs_[3].ssa_rep->uses[0]); - EXPECT_EQ(0u, mirs_[3].dalvikInsn.vB); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Rename2) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 2u, 0u), - DEF_IGET(3, Instruction::IGET, 3u, 2u, 1u), - DEF_CONST(3, Instruction::CONST, 4u, 1000), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 3, 4 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[2]); - - const size_t no_null_ck_indexes[] = { 1, 3 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, true, false, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the IGET uses the s_reg 0, v_reg 0, defined by mirs_[0]. - ASSERT_EQ(1, mirs_[3].ssa_rep->num_uses); - EXPECT_EQ(0, mirs_[3].ssa_rep->uses[0]); - EXPECT_EQ(0u, mirs_[3].dalvikInsn.vB); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Rename3) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 2u, 0u), - DEF_IGET(3, Instruction::IGET, 3u, 2u, 1u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 0 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 3 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[2]); - - const size_t no_null_ck_indexes[] = { 1, 3 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, true, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the NEW_INSTANCE defines the s_reg 2, v_reg 2, originally defined by the move. - ASSERT_EQ(1, mirs_[0].ssa_rep->num_defs); - EXPECT_EQ(2, mirs_[0].ssa_rep->defs[0]); - EXPECT_EQ(2u, mirs_[0].dalvikInsn.vA); - // Check that the first IGET is using the s_reg 2, v_reg 2. - ASSERT_EQ(1, mirs_[1].ssa_rep->num_uses); - EXPECT_EQ(2, mirs_[1].ssa_rep->uses[0]); - EXPECT_EQ(2u, mirs_[1].dalvikInsn.vB); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Rename4) { - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 1u, 0u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 2u, 1u), - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 3u, 1000u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 0, 1 /* high word */ }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 3 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 3 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[0], value_names_[2]); - - static const bool eliminated[] = { - false, true, true, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the NEW_INSTANCE defines the s_reg 2, v_reg 2, originally defined by the move 2u. - ASSERT_EQ(1, mirs_[0].ssa_rep->num_defs); - EXPECT_EQ(2, mirs_[0].ssa_rep->defs[0]); - EXPECT_EQ(2u, mirs_[0].dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Rename5) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 2u, 1u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 3u, 0u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 4u, 3u), - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 5u, 1000u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 1, 3, 0, 1 /* high word */ }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 5 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 5 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[3]); - EXPECT_EQ(value_names_[0], value_names_[4]); - - static const bool eliminated[] = { - false, false, false, true, true, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the NEW_INSTANCE defines the s_reg 4, v_reg 3, originally defined by the move 4u. - ASSERT_EQ(1, mirs_[0].ssa_rep->num_defs); - EXPECT_EQ(4, mirs_[0].ssa_rep->defs[0]); - EXPECT_EQ(3u, mirs_[0].dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Rename6) { - static const MIRDef mirs[] = { - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 0u, 1000u), - DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 2u, 0u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1 /* high word */, 1, 2 /* high word */ }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 0, 2 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - - static const bool eliminated[] = { - false, true - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the CONST_WIDE defines the s_reg 2, v_reg 1, originally defined by the move 2u. - ASSERT_EQ(2, mirs_[0].ssa_rep->num_defs); - EXPECT_EQ(2, mirs_[0].ssa_rep->defs[0]); - EXPECT_EQ(3, mirs_[0].ssa_rep->defs[1]); - EXPECT_EQ(1u, mirs_[0].dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Rename7) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000u), - DEF_MOVE(3, Instruction::MOVE, 1u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 2u, 0u, 1u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 0 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[0], value_names_[2]); - EXPECT_EQ(value_names_[0], value_names_[1]); - - static const bool eliminated[] = { - false, true, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the CONST defines the s_reg 1, v_reg 1, originally defined by the move 1u. - ASSERT_EQ(1, mirs_[0].ssa_rep->num_defs); - EXPECT_EQ(1, mirs_[0].ssa_rep->defs[0]); - EXPECT_EQ(1u, mirs_[0].dalvikInsn.vA); - // Check that the ADD_INT inputs are both s_reg1, vreg 1. - ASSERT_EQ(2, mirs_[2].ssa_rep->num_uses); - EXPECT_EQ(1, mirs_[2].ssa_rep->uses[0]); - EXPECT_EQ(1, mirs_[2].ssa_rep->uses[1]); - EXPECT_EQ(1u, mirs_[2].dalvikInsn.vB); - EXPECT_EQ(1u, mirs_[2].dalvikInsn.vC); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Rename8) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000u), - DEF_MOVE(3, Instruction::MOVE, 1u, 0u), - DEF_BINOP(3, Instruction::ADD_INT_2ADDR, 2u, 0u, 1u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 0 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[0], value_names_[2]); - EXPECT_EQ(value_names_[0], value_names_[1]); - - static const bool eliminated[] = { - false, true, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the CONST defines the s_reg 1, v_reg 1, originally defined by the move 1u. - ASSERT_EQ(1, mirs_[0].ssa_rep->num_defs); - EXPECT_EQ(1, mirs_[0].ssa_rep->defs[0]); - EXPECT_EQ(1u, mirs_[0].dalvikInsn.vA); - // Check that the ADD_INT_2ADDR was replaced by ADD_INT and inputs are both s_reg 1, vreg 1. - EXPECT_EQ(Instruction::ADD_INT, mirs_[2].dalvikInsn.opcode); - ASSERT_EQ(2, mirs_[2].ssa_rep->num_uses); - EXPECT_EQ(1, mirs_[2].ssa_rep->uses[0]); - EXPECT_EQ(1, mirs_[2].ssa_rep->uses[1]); - EXPECT_EQ(1u, mirs_[2].dalvikInsn.vB); - EXPECT_EQ(1u, mirs_[2].dalvikInsn.vC); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Rename9) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000u), - DEF_BINOP(3, Instruction::ADD_INT_2ADDR, 1u, 0u, 0u), - DEF_MOVE(3, Instruction::MOVE, 2u, 1u), - DEF_CONST(3, Instruction::CONST, 3u, 3000u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 0, 1, 0 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 3 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[1], value_names_[2]); - - static const bool eliminated[] = { - false, false, true, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the ADD_INT_2ADDR was replaced by ADD_INT and output is in s_reg 2, vreg 1. - EXPECT_EQ(Instruction::ADD_INT, mirs_[1].dalvikInsn.opcode); - ASSERT_EQ(2, mirs_[1].ssa_rep->num_uses); - EXPECT_EQ(0, mirs_[1].ssa_rep->uses[0]); - EXPECT_EQ(0, mirs_[1].ssa_rep->uses[1]); - EXPECT_EQ(0u, mirs_[1].dalvikInsn.vB); - EXPECT_EQ(0u, mirs_[1].dalvikInsn.vC); - ASSERT_EQ(1, mirs_[1].ssa_rep->num_defs); - EXPECT_EQ(2, mirs_[1].ssa_rep->defs[0]); - EXPECT_EQ(1u, mirs_[1].dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, NoRename1) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 2u, 1u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 3u, 0u), - DEF_CONST(3, Instruction::CONST, 4u, 1000), - DEF_IGET(3, Instruction::IGET, 5u, 3u, 1u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 1, 0, 1 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 4, 5 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[3]); - - const size_t no_null_ck_indexes[] = { 1, 5 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestSimple, NoRename2) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 2u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 3u, 0u), - DEF_CONST(3, Instruction::CONST, 4u, 1000), - DEF_IGET(3, Instruction::IGET, 5u, 3u, 1u), - DEF_CONST(3, Instruction::CONST, 6u, 2000), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 2, 0, 3, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 4, 5, 6 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[3]); - - const size_t no_null_ck_indexes[] = { 1, 5 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestSimple, NoRename3) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET, 1u, 0u, 0u), - DEF_IGET(3, Instruction::IGET, 2u, 0u, 2u), - DEF_BINOP(3, Instruction::ADD_INT, 3u, 1u, 2u), - DEF_MOVE(3, Instruction::MOVE_OBJECT, 4u, 0u), - DEF_IGET(3, Instruction::IGET, 5u, 4u, 1u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 2, 0 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 5 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[4]); - - const size_t no_null_ck_indexes[] = { 1, 2, 5 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestSimple, NoRename4) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000u), - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 1u), - DEF_CONST(3, Instruction::CONST, 2u, 100u), - DEF_CONST(3, Instruction::CONST, 3u, 200u), - DEF_BINOP(3, Instruction::OR_INT_2ADDR, 4u, 2u, 3u), // 3. Find definition of the move src. - DEF_MOVE(3, Instruction::MOVE, 5u, 0u), // 4. Uses move dest vreg. - DEF_MOVE(3, Instruction::MOVE, 6u, 4u), // 2. Find overwritten move src. - DEF_CONST(3, Instruction::CONST, 7u, 2000u), // 1. Overwrites 4u, look for moves. - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 2, 4, 0, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 7 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[5]); - EXPECT_EQ(value_names_[4], value_names_[6]); - - static const bool eliminated[] = { - false, false, false, false, false, false, false, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Simple1) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, - { 1u, 1u, 1u, false, kDexMemAccessObject }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 1u, 0u, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 2u, 1u, 1u), - DEF_IGET(3, Instruction::IGET, 3u, 2u, 2u), - DEF_IGET(3, Instruction::IGET_OBJECT, 4u, 0u, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 5u, 4u, 1u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 1, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_NE(value_names_[0], value_names_[1]); - EXPECT_NE(value_names_[0], value_names_[2]); - EXPECT_NE(value_names_[0], value_names_[3]); - EXPECT_NE(value_names_[1], value_names_[2]); - EXPECT_NE(value_names_[1], value_names_[3]); - EXPECT_NE(value_names_[2], value_names_[3]); - EXPECT_EQ(value_names_[1], value_names_[4]); - EXPECT_EQ(value_names_[2], value_names_[5]); - - EXPECT_EQ(MIR_IGNORE_NULL_CHECK, mirs_[4].optimization_flags & MIR_IGNORE_NULL_CHECK); - EXPECT_EQ(MIR_IGNORE_NULL_CHECK, mirs_[5].optimization_flags & MIR_IGNORE_NULL_CHECK); - - static const bool eliminated[] = { - false, false, false, false, true, true - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the sregs have been renamed correctly. - ASSERT_EQ(1, mirs_[1].ssa_rep->num_defs); - EXPECT_EQ(4, mirs_[1].ssa_rep->defs[0]); - ASSERT_EQ(1, mirs_[1].ssa_rep->num_uses); - EXPECT_EQ(0, mirs_[1].ssa_rep->uses[0]); - ASSERT_EQ(1, mirs_[2].ssa_rep->num_defs); - EXPECT_EQ(5, mirs_[2].ssa_rep->defs[0]); - ASSERT_EQ(1, mirs_[2].ssa_rep->num_uses); - EXPECT_EQ(4, mirs_[2].ssa_rep->uses[0]); - ASSERT_EQ(1, mirs_[3].ssa_rep->num_defs); - EXPECT_EQ(3, mirs_[3].ssa_rep->defs[0]); - ASSERT_EQ(1, mirs_[3].ssa_rep->num_uses); - EXPECT_EQ(5, mirs_[3].ssa_rep->uses[0]); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Simple2) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST(3, Instruction::CONST, 1u, 1000), - DEF_IGET(3, Instruction::IGET, 2u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT_2ADDR, 3u, 2u, 1u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 4u, 3u), - DEF_IGET(3, Instruction::IGET, 5u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT_2ADDR, 6u, 5u, 1u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 2, 3, 2, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[2], value_names_[5]); - EXPECT_EQ(value_names_[3], value_names_[6]); - - const size_t no_null_ck_indexes[] = { 2, 5 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, true, true - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the sregs have been renamed correctly. - ASSERT_EQ(1, mirs_[3].ssa_rep->num_defs); - EXPECT_EQ(6, mirs_[3].ssa_rep->defs[0]); - ASSERT_EQ(2, mirs_[3].ssa_rep->num_uses); - EXPECT_EQ(2, mirs_[3].ssa_rep->uses[0]); - EXPECT_EQ(1, mirs_[3].ssa_rep->uses[1]); - ASSERT_EQ(1, mirs_[4].ssa_rep->num_defs); - EXPECT_EQ(4, mirs_[4].ssa_rep->defs[0]); - ASSERT_EQ(1, mirs_[4].ssa_rep->num_uses); - EXPECT_EQ(6, mirs_[4].ssa_rep->uses[0]); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Simple3) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST(3, Instruction::CONST, 1u, 1000), - DEF_CONST(3, Instruction::CONST, 2u, 2000), - DEF_CONST(3, Instruction::CONST, 3u, 3000), - DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 5u, 4u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 6u, 5u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 7u, 6u, 3u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 8u, 7u), - DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 10u, 9u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 11u, 10u, 2u), // Simple elimination of ADD+MUL - DEF_BINOP(3, Instruction::SUB_INT, 12u, 11u, 3u), // allows simple elimination of IGET+SUB. - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 5, 4, 6, 4, 5, 5, 4 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[4], value_names_[9]); - EXPECT_EQ(value_names_[5], value_names_[10]); - EXPECT_EQ(value_names_[6], value_names_[11]); - EXPECT_EQ(value_names_[7], value_names_[12]); - - const size_t no_null_ck_indexes[] = { 4, 9 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false, false, false, false, true, true, true, true - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the sregs have been renamed correctly. - ASSERT_EQ(1, mirs_[6].ssa_rep->num_defs); - EXPECT_EQ(11, mirs_[6].ssa_rep->defs[0]); // 6 -> 11 - ASSERT_EQ(2, mirs_[6].ssa_rep->num_uses); - EXPECT_EQ(5, mirs_[6].ssa_rep->uses[0]); - EXPECT_EQ(2, mirs_[6].ssa_rep->uses[1]); - ASSERT_EQ(1, mirs_[7].ssa_rep->num_defs); - EXPECT_EQ(12, mirs_[7].ssa_rep->defs[0]); // 7 -> 12 - ASSERT_EQ(2, mirs_[7].ssa_rep->num_uses); - EXPECT_EQ(11, mirs_[7].ssa_rep->uses[0]); // 6 -> 11 - EXPECT_EQ(3, mirs_[7].ssa_rep->uses[1]); - ASSERT_EQ(1, mirs_[8].ssa_rep->num_defs); - EXPECT_EQ(8, mirs_[8].ssa_rep->defs[0]); - ASSERT_EQ(1, mirs_[8].ssa_rep->num_uses); - EXPECT_EQ(12, mirs_[8].ssa_rep->uses[0]); // 7 -> 12 -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Simple4) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 1u, INT64_C(1)), - DEF_BINOP(3, Instruction::LONG_TO_FLOAT, 3u, 1u, 2u), - DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 5u, 4u), - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 6u, INT64_C(1)), - DEF_BINOP(3, Instruction::LONG_TO_FLOAT, 8u, 6u, 7u), - DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 1, 2, 3, 1, 2, 1, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 1, 6 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[1], value_names_[5]); - EXPECT_EQ(value_names_[2], value_names_[6]); - EXPECT_EQ(value_names_[3], value_names_[7]); - - const size_t no_null_ck_indexes[] = { 3, 7 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - // Simple elimination of CONST_WIDE+LONG_TO_FLOAT allows simple eliminatiion of IGET. - false, false, false, false, false, true, true, true - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the sregs have been renamed correctly. - ASSERT_EQ(1, mirs_[2].ssa_rep->num_defs); - EXPECT_EQ(8, mirs_[2].ssa_rep->defs[0]); // 3 -> 8 - ASSERT_EQ(2, mirs_[2].ssa_rep->num_uses); - EXPECT_EQ(1, mirs_[2].ssa_rep->uses[0]); - EXPECT_EQ(2, mirs_[2].ssa_rep->uses[1]); - ASSERT_EQ(1, mirs_[3].ssa_rep->num_defs); - EXPECT_EQ(9, mirs_[3].ssa_rep->defs[0]); // 4 -> 9 - ASSERT_EQ(1, mirs_[3].ssa_rep->num_uses); - EXPECT_EQ(0, mirs_[3].ssa_rep->uses[0]); - ASSERT_EQ(1, mirs_[4].ssa_rep->num_defs); - EXPECT_EQ(5, mirs_[4].ssa_rep->defs[0]); - ASSERT_EQ(1, mirs_[4].ssa_rep->num_uses); - EXPECT_EQ(9, mirs_[4].ssa_rep->uses[0]); // 4 -> 9 -} - -TEST_F(GvnDeadCodeEliminationTestSimple, KillChain1) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST(3, Instruction::CONST, 1u, 1000), - DEF_CONST(3, Instruction::CONST, 2u, 2000), - DEF_CONST(3, Instruction::CONST, 3u, 3000), - DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 5u, 4u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 6u, 5u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 7u, 6u, 3u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 8u, 7u), - DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 10u, 9u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 11u, 10u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 12u, 11u, 3u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 4, 5, 6, 4, 5, 4, 5 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[4], value_names_[9]); - EXPECT_EQ(value_names_[5], value_names_[10]); - EXPECT_EQ(value_names_[6], value_names_[11]); - EXPECT_EQ(value_names_[7], value_names_[12]); - - const size_t no_null_ck_indexes[] = { 4, 9 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false, false, false, false, true, true, true, true - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the sregs have been renamed correctly. - ASSERT_EQ(1, mirs_[6].ssa_rep->num_defs); - EXPECT_EQ(11, mirs_[6].ssa_rep->defs[0]); // 6 -> 11 - ASSERT_EQ(2, mirs_[6].ssa_rep->num_uses); - EXPECT_EQ(5, mirs_[6].ssa_rep->uses[0]); - EXPECT_EQ(2, mirs_[6].ssa_rep->uses[1]); - ASSERT_EQ(1, mirs_[7].ssa_rep->num_defs); - EXPECT_EQ(12, mirs_[7].ssa_rep->defs[0]); // 7 -> 12 - ASSERT_EQ(2, mirs_[7].ssa_rep->num_uses); - EXPECT_EQ(11, mirs_[7].ssa_rep->uses[0]); // 6 -> 11 - EXPECT_EQ(3, mirs_[7].ssa_rep->uses[1]); - ASSERT_EQ(1, mirs_[8].ssa_rep->num_defs); - EXPECT_EQ(8, mirs_[8].ssa_rep->defs[0]); - ASSERT_EQ(1, mirs_[8].ssa_rep->num_uses); - EXPECT_EQ(12, mirs_[8].ssa_rep->uses[0]); // 7 -> 12 -} - -TEST_F(GvnDeadCodeEliminationTestSimple, KillChain2) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST(3, Instruction::CONST, 1u, 1000), - DEF_CONST(3, Instruction::CONST, 2u, 2000), - DEF_CONST(3, Instruction::CONST, 3u, 3000), - DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 5u, 4u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 6u, 5u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 7u, 6u, 3u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 8u, 7u), - DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 10u, 9u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 11u, 10u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 12u, 11u, 3u), - DEF_CONST(3, Instruction::CONST, 13u, 4000), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 5, 4, 6, 4, 7, 7, 4, 7 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 13 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[4], value_names_[9]); - EXPECT_EQ(value_names_[5], value_names_[10]); - EXPECT_EQ(value_names_[6], value_names_[11]); - EXPECT_EQ(value_names_[7], value_names_[12]); - - const size_t no_null_ck_indexes[] = { 4, 9 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false, false, false, false, true, true, true, true, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the sregs have been renamed correctly. - ASSERT_EQ(1, mirs_[7].ssa_rep->num_defs); - EXPECT_EQ(12, mirs_[7].ssa_rep->defs[0]); // 7 -> 12 - ASSERT_EQ(2, mirs_[7].ssa_rep->num_uses); - EXPECT_EQ(6, mirs_[7].ssa_rep->uses[0]); - EXPECT_EQ(3, mirs_[7].ssa_rep->uses[1]); - ASSERT_EQ(1, mirs_[8].ssa_rep->num_defs); - EXPECT_EQ(8, mirs_[8].ssa_rep->defs[0]); - ASSERT_EQ(1, mirs_[8].ssa_rep->num_uses); - EXPECT_EQ(12, mirs_[8].ssa_rep->uses[0]); // 7 -> 12 -} - -TEST_F(GvnDeadCodeEliminationTestSimple, KillChain3) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST(3, Instruction::CONST, 1u, 1000), - DEF_CONST(3, Instruction::CONST, 2u, 2000), - DEF_CONST(3, Instruction::CONST, 3u, 3000), - DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 5u, 4u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 6u, 5u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 7u, 6u, 3u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 8u, 7u), - DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 10u, 9u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 11u, 10u, 2u), - DEF_CONST(3, Instruction::CONST, 12u, 4000), - DEF_BINOP(3, Instruction::SUB_INT, 13u, 11u, 3u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 5, 4, 6, 4, 7, 4, 7, 4 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 12 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[4], value_names_[9]); - EXPECT_EQ(value_names_[5], value_names_[10]); - EXPECT_EQ(value_names_[6], value_names_[11]); - EXPECT_EQ(value_names_[7], value_names_[13]); - - const size_t no_null_ck_indexes[] = { 4, 9 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false, false, false, false, true, true, true, false, true - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the sregs have been renamed correctly. - ASSERT_EQ(1, mirs_[7].ssa_rep->num_defs); - EXPECT_EQ(13, mirs_[7].ssa_rep->defs[0]); // 7 -> 13 - ASSERT_EQ(2, mirs_[7].ssa_rep->num_uses); - EXPECT_EQ(6, mirs_[7].ssa_rep->uses[0]); - EXPECT_EQ(3, mirs_[7].ssa_rep->uses[1]); - ASSERT_EQ(1, mirs_[8].ssa_rep->num_defs); - EXPECT_EQ(8, mirs_[8].ssa_rep->defs[0]); - ASSERT_EQ(1, mirs_[8].ssa_rep->num_uses); - EXPECT_EQ(13, mirs_[8].ssa_rep->uses[0]); // 7 -> 13 -} - -TEST_F(GvnDeadCodeEliminationTestSimple, KeepChain1) { - // KillChain2 without the final CONST. - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST(3, Instruction::CONST, 1u, 1000), - DEF_CONST(3, Instruction::CONST, 2u, 2000), - DEF_CONST(3, Instruction::CONST, 3u, 3000), - DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 5u, 4u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 6u, 5u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 7u, 6u, 3u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 8u, 7u), - DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 10u, 9u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 11u, 10u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 12u, 11u, 3u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 5, 4, 6, 4, 7, 7, 4 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[4], value_names_[9]); - EXPECT_EQ(value_names_[5], value_names_[10]); - EXPECT_EQ(value_names_[6], value_names_[11]); - EXPECT_EQ(value_names_[7], value_names_[12]); - - const size_t no_null_ck_indexes[] = { 4, 9 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false, false, false, false, false, false, false, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestSimple, KeepChain2) { - // KillChain1 with MIRs in the middle of the chain. - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST(3, Instruction::CONST, 1u, 1000), - DEF_CONST(3, Instruction::CONST, 2u, 2000), - DEF_CONST(3, Instruction::CONST, 3u, 3000), - DEF_IGET(3, Instruction::IGET, 4u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 5u, 4u, 1u), - DEF_BINOP(3, Instruction::MUL_INT, 6u, 5u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 7u, 6u, 3u), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 8u, 7u), - DEF_IGET(3, Instruction::IGET, 9u, 0u, 0u), - DEF_BINOP(3, Instruction::ADD_INT, 10u, 9u, 1u), - DEF_CONST(3, Instruction::CONST, 11u, 4000), - DEF_UNOP(3, Instruction::INT_TO_FLOAT, 12u, 11u), - DEF_BINOP(3, Instruction::MUL_INT, 13u, 10u, 2u), - DEF_BINOP(3, Instruction::SUB_INT, 14u, 13u, 3u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 4, 5, 6, 4, 5, 4, 7, 4, 5 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[4], value_names_[9]); - EXPECT_EQ(value_names_[5], value_names_[10]); - EXPECT_EQ(value_names_[6], value_names_[13]); - EXPECT_EQ(value_names_[7], value_names_[14]); - - const size_t no_null_ck_indexes[] = { 4, 9 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestDiamond, CreatePhi1) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000), - DEF_CONST(4, Instruction::CONST, 1u, 1000), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 0 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - - static const bool eliminated[] = { - false, true, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that we've created a single-input Phi to replace the CONST 3u. - BasicBlock* bb4 = cu_.mir_graph->GetBasicBlock(4); - MIR* phi = bb4->first_mir_insn; - ASSERT_TRUE(phi != nullptr); - ASSERT_EQ(kMirOpPhi, static_cast(phi->dalvikInsn.opcode)); - ASSERT_EQ(1, phi->ssa_rep->num_uses); - EXPECT_EQ(0, phi->ssa_rep->uses[0]); - ASSERT_EQ(1, phi->ssa_rep->num_defs); - EXPECT_EQ(1, phi->ssa_rep->defs[0]); - EXPECT_EQ(0u, phi->dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestDiamond, CreatePhi2) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000), - DEF_MOVE(4, Instruction::MOVE, 1u, 0u), - DEF_CONST(4, Instruction::CONST, 2u, 1000), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 0 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[0], value_names_[2]); - - static const bool eliminated[] = { - false, false, true, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that we've created a single-input Phi to replace the CONST 3u. - BasicBlock* bb4 = cu_.mir_graph->GetBasicBlock(4); - MIR* phi = bb4->first_mir_insn; - ASSERT_TRUE(phi != nullptr); - ASSERT_EQ(kMirOpPhi, static_cast(phi->dalvikInsn.opcode)); - ASSERT_EQ(1, phi->ssa_rep->num_uses); - EXPECT_EQ(0, phi->ssa_rep->uses[0]); - ASSERT_EQ(1, phi->ssa_rep->num_defs); - EXPECT_EQ(2, phi->ssa_rep->defs[0]); - EXPECT_EQ(0u, phi->dalvikInsn.vA); - MIR* move = phi->next; - ASSERT_TRUE(move != nullptr); - ASSERT_EQ(Instruction::MOVE, move->dalvikInsn.opcode); - ASSERT_EQ(1, move->ssa_rep->num_uses); - EXPECT_EQ(2, move->ssa_rep->uses[0]); - ASSERT_EQ(1, move->ssa_rep->num_defs); - EXPECT_EQ(1, move->ssa_rep->defs[0]); - EXPECT_EQ(1u, move->dalvikInsn.vA); - EXPECT_EQ(0u, move->dalvikInsn.vB); -} - -TEST_F(GvnDeadCodeEliminationTestDiamond, CreatePhi3) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST(4, Instruction::CONST, 1u, 1000), - DEF_IPUT(4, Instruction::IPUT, 1u, 0u, 0u), - DEF_CONST(5, Instruction::CONST, 3u, 2000), - DEF_IPUT(5, Instruction::IPUT, 3u, 0u, 0u), - DEF_IGET(6, Instruction::IGET, 5u, 0u, 0u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2 /* dummy */, 1, 2 /* dummy */, 1 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 3, 5 }; - ExpectValueNamesNE(diff_indexes); - - const size_t no_null_ck_indexes[] = { 2, 4, 5 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, true, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that we've created a two-input Phi to replace the IGET 5u. - BasicBlock* bb6 = cu_.mir_graph->GetBasicBlock(6); - MIR* phi = bb6->first_mir_insn; - ASSERT_TRUE(phi != nullptr); - ASSERT_EQ(kMirOpPhi, static_cast(phi->dalvikInsn.opcode)); - ASSERT_EQ(2, phi->ssa_rep->num_uses); - EXPECT_EQ(1, phi->ssa_rep->uses[0]); - EXPECT_EQ(3, phi->ssa_rep->uses[1]); - ASSERT_EQ(1, phi->ssa_rep->num_defs); - EXPECT_EQ(5, phi->ssa_rep->defs[0]); - EXPECT_EQ(1u, phi->dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestDiamond, KillChainInAnotherBlock1) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, // linked list - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 1u, 0u, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 2u, 1u, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 3u, 2u, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 4u, 3u, 0u), - DEF_IFZ(3, Instruction::IF_NEZ, 4u), - DEF_IGET(4, Instruction::IGET_OBJECT, 6u, 0u, 0u), - DEF_IGET(4, Instruction::IGET_OBJECT, 7u, 6u, 0u), - DEF_IGET(4, Instruction::IGET_OBJECT, 8u, 7u, 0u), - DEF_IGET(4, Instruction::IGET_OBJECT, 9u, 8u, 0u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 1, 2, 3 /* dummy */, 1, 2, 1, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[1], value_names_[6]); - EXPECT_EQ(value_names_[2], value_names_[7]); - EXPECT_EQ(value_names_[3], value_names_[8]); - EXPECT_EQ(value_names_[4], value_names_[9]); - - const size_t no_null_ck_indexes[] = { 1, 6, 7, 8, 9 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false, true, true, true, true, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that we've created two single-input Phis to replace the IGET 8u and IGET 9u; - // the IGET 6u and IGET 7u were killed without a replacement. - BasicBlock* bb4 = cu_.mir_graph->GetBasicBlock(4); - MIR* phi1 = bb4->first_mir_insn; - ASSERT_TRUE(phi1 != nullptr); - ASSERT_EQ(kMirOpPhi, static_cast(phi1->dalvikInsn.opcode)); - MIR* phi2 = phi1->next; - ASSERT_TRUE(phi2 != nullptr); - ASSERT_EQ(kMirOpPhi, static_cast(phi2->dalvikInsn.opcode)); - ASSERT_TRUE(phi2->next == &mirs_[6]); - if (phi1->dalvikInsn.vA == 2u) { - std::swap(phi1, phi2); - } - ASSERT_EQ(1, phi1->ssa_rep->num_uses); - EXPECT_EQ(3, phi1->ssa_rep->uses[0]); - ASSERT_EQ(1, phi1->ssa_rep->num_defs); - EXPECT_EQ(8, phi1->ssa_rep->defs[0]); - EXPECT_EQ(1u, phi1->dalvikInsn.vA); - ASSERT_EQ(1, phi2->ssa_rep->num_uses); - EXPECT_EQ(4, phi2->ssa_rep->uses[0]); - ASSERT_EQ(1, phi2->ssa_rep->num_defs); - EXPECT_EQ(9, phi2->ssa_rep->defs[0]); - EXPECT_EQ(2u, phi2->dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestDiamond, KillChainInAnotherBlock2) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, // linked list - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 1u, 0u, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 2u, 1u, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 3u, 2u, 0u), - DEF_IGET(3, Instruction::IGET_OBJECT, 4u, 3u, 0u), - DEF_IFZ(3, Instruction::IF_NEZ, 4u), - DEF_IGET(4, Instruction::IGET_OBJECT, 6u, 0u, 0u), - DEF_IGET(4, Instruction::IGET_OBJECT, 7u, 6u, 0u), - DEF_IGET(4, Instruction::IGET_OBJECT, 8u, 7u, 0u), - DEF_CONST(4, Instruction::CONST, 9u, 1000), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 1, 2, 3 /* dummy */, 1, 2, 1, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 3, 4, 9 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[1], value_names_[6]); - EXPECT_EQ(value_names_[2], value_names_[7]); - EXPECT_EQ(value_names_[3], value_names_[8]); - - const size_t no_null_ck_indexes[] = { 1, 6, 7, 8 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, false, false, true, true, true, false, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that we've created a single-input Phi to replace the IGET 8u; - // the IGET 6u and IGET 7u were killed without a replacement. - BasicBlock* bb4 = cu_.mir_graph->GetBasicBlock(4); - MIR* phi = bb4->first_mir_insn; - ASSERT_TRUE(phi != nullptr); - ASSERT_EQ(kMirOpPhi, static_cast(phi->dalvikInsn.opcode)); - ASSERT_TRUE(phi->next == &mirs_[6]); - ASSERT_EQ(1, phi->ssa_rep->num_uses); - EXPECT_EQ(3, phi->ssa_rep->uses[0]); - ASSERT_EQ(1, phi->ssa_rep->num_defs); - EXPECT_EQ(8, phi->ssa_rep->defs[0]); - EXPECT_EQ(1u, phi->dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestLoop, IFieldLoopVariable) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 0u), - DEF_CONST(3, Instruction::CONST, 1u, 1), - DEF_CONST(3, Instruction::CONST, 2u, 0), - DEF_IPUT(3, Instruction::IPUT, 2u, 0u, 0u), - DEF_IGET(4, Instruction::IGET, 4u, 0u, 0u), - DEF_BINOP(4, Instruction::ADD_INT, 5u, 4u, 1u), - DEF_IPUT(4, Instruction::IPUT, 5u, 0u, 0u), - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3 /* dummy */, 2, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 4, 5 }; - ExpectValueNamesNE(diff_indexes); - - const size_t no_null_ck_indexes[] = { 3, 4, 6 }; - ExpectNoNullCheck(no_null_ck_indexes); - - static const bool eliminated[] = { - false, false, false, false, true, false, false, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that we've created a two-input Phi to replace the IGET 3u. - BasicBlock* bb4 = cu_.mir_graph->GetBasicBlock(4); - MIR* phi = bb4->first_mir_insn; - ASSERT_TRUE(phi != nullptr); - ASSERT_EQ(kMirOpPhi, static_cast(phi->dalvikInsn.opcode)); - ASSERT_TRUE(phi->next == &mirs_[4]); - ASSERT_EQ(2, phi->ssa_rep->num_uses); - EXPECT_EQ(2, phi->ssa_rep->uses[0]); - EXPECT_EQ(5, phi->ssa_rep->uses[1]); - ASSERT_EQ(1, phi->ssa_rep->num_defs); - EXPECT_EQ(4, phi->ssa_rep->defs[0]); - EXPECT_EQ(2u, phi->dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestDiamond, LongOverlaps1) { - static const MIRDef mirs[] = { - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 0u, 1000u), - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 2u, 1000u), - DEF_MOVE_WIDE(4, Instruction::MOVE_WIDE, 4u, 0u), - DEF_MOVE_WIDE(4, Instruction::MOVE_WIDE, 6u, 2u), - DEF_MOVE_WIDE(4, Instruction::MOVE_WIDE, 8u, 4u), - DEF_MOVE_WIDE(4, Instruction::MOVE_WIDE, 10u, 6u), - }; - - // The last insn should overlap the first and second. - static const int32_t sreg_to_vreg_map[] = { 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, 3 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 0, 2, 4, 6, 8, 10 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[0], value_names_[2]); - EXPECT_EQ(value_names_[0], value_names_[3]); - EXPECT_EQ(value_names_[0], value_names_[4]); - - static const bool eliminated[] = { - false, false, false, false, false, false, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestSimple, LongOverlaps2) { - static const MIRDef mirs[] = { - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 0u, 1000u), - DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 2u, 0u), - DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 4u, 2u), - }; - - // The last insn should overlap the first and second. - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 1, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 0, 2, 4 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[0], value_names_[2]); - - static const bool eliminated[] = { - false, true, true, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the CONST_WIDE registers have been correctly renamed. - MIR* const_wide = &mirs_[0]; - ASSERT_EQ(2u, const_wide->ssa_rep->num_defs); - EXPECT_EQ(4, const_wide->ssa_rep->defs[0]); - EXPECT_EQ(5, const_wide->ssa_rep->defs[1]); - EXPECT_EQ(1u, const_wide->dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, LongOverlaps3) { - static const MIRDef mirs[] = { - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 0u, 1000u), - DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 2u, 0u), - DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 4u, 2u), - }; - - // The last insn should overlap the first and second. - static const int32_t sreg_to_vreg_map[] = { 2, 3, 0, 1, 1, 2 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 0, 2, 4 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[0], value_names_[2]); - - static const bool eliminated[] = { - false, true, true, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check that the CONST_WIDE registers have been correctly renamed. - MIR* const_wide = &mirs_[0]; - ASSERT_EQ(2u, const_wide->ssa_rep->num_defs); - EXPECT_EQ(4, const_wide->ssa_rep->defs[0]); - EXPECT_EQ(5, const_wide->ssa_rep->defs[1]); - EXPECT_EQ(1u, const_wide->dalvikInsn.vA); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, MixedOverlaps1) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000u), - DEF_MOVE(3, Instruction::MOVE, 1u, 0u), - DEF_CONST(3, Instruction::CONST, 2u, 2000u), - { 3, Instruction::INT_TO_LONG, 0, 0u, 1, { 2u }, 2, { 3u, 4u } }, - DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 5u, 3u), - DEF_CONST(3, Instruction::CONST, 7u, 3000u), - DEF_CONST(3, Instruction::CONST, 8u, 4000u), - }; - - static const int32_t sreg_to_vreg_map[] = { 1, 2, 0, 0, 1, 3, 4, 0, 1 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 3, 5 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 2, 3, 5, 6 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[3], value_names_[4]); - - static const bool eliminated[] = { - false, true, false, false, true, false, false, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } - // Check renamed registers in CONST. - MIR* cst = &mirs_[0]; - ASSERT_EQ(Instruction::CONST, cst->dalvikInsn.opcode); - ASSERT_EQ(0, cst->ssa_rep->num_uses); - ASSERT_EQ(1, cst->ssa_rep->num_defs); - EXPECT_EQ(1, cst->ssa_rep->defs[0]); - EXPECT_EQ(2u, cst->dalvikInsn.vA); - // Check renamed registers in INT_TO_LONG. - MIR* int_to_long = &mirs_[3]; - ASSERT_EQ(Instruction::INT_TO_LONG, int_to_long->dalvikInsn.opcode); - ASSERT_EQ(1, int_to_long->ssa_rep->num_uses); - EXPECT_EQ(2, int_to_long->ssa_rep->uses[0]); - ASSERT_EQ(2, int_to_long->ssa_rep->num_defs); - EXPECT_EQ(5, int_to_long->ssa_rep->defs[0]); - EXPECT_EQ(6, int_to_long->ssa_rep->defs[1]); - EXPECT_EQ(3u, int_to_long->dalvikInsn.vA); - EXPECT_EQ(0u, int_to_long->dalvikInsn.vB); -} - -TEST_F(GvnDeadCodeEliminationTestSimple, UnusedRegs1) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000u), - DEF_CONST(3, Instruction::CONST, 1u, 2000u), - DEF_BINOP(3, Instruction::ADD_INT, 2u, 1u, 0u), - DEF_CONST(3, Instruction::CONST, 3u, 1000u), // NOT killed (b/21702651). - DEF_BINOP(3, Instruction::ADD_INT, 4u, 1u, 3u), // Killed (RecordPass) - DEF_CONST(3, Instruction::CONST, 5u, 2000u), // Killed with 9u (BackwardPass) - DEF_BINOP(3, Instruction::ADD_INT, 6u, 5u, 0u), // Killed (RecordPass) - DEF_CONST(3, Instruction::CONST, 7u, 4000u), - DEF_MOVE(3, Instruction::MOVE, 8u, 0u), // Killed with 6u (BackwardPass) - }; - - static const int32_t sreg_to_vreg_map[] = { 1, 2, 3, 0, 3, 0, 3, 4, 0 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 7 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[3]); - EXPECT_EQ(value_names_[2], value_names_[4]); - EXPECT_EQ(value_names_[1], value_names_[5]); - EXPECT_EQ(value_names_[2], value_names_[6]); - EXPECT_EQ(value_names_[0], value_names_[8]); - - static const bool eliminated[] = { - false, false, false, false, true, true, true, false, true, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestSimple, UnusedRegs2) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 1000u), - DEF_CONST(3, Instruction::CONST, 1u, 2000u), - DEF_BINOP(3, Instruction::ADD_INT, 2u, 1u, 0u), - DEF_CONST(3, Instruction::CONST, 3u, 1000u), // Killed (BackwardPass; b/21702651) - DEF_BINOP(3, Instruction::ADD_INT, 4u, 1u, 3u), // Killed (RecordPass) - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 5u, 4000u), - { 3, Instruction::LONG_TO_INT, 0, 0u, 2, { 5u, 6u }, 1, { 7u } }, - DEF_BINOP(3, Instruction::ADD_INT, 8u, 7u, 0u), - DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 9u, 4000u), // Killed with 12u (BackwardPass) - DEF_CONST(3, Instruction::CONST, 11u, 6000u), - { 3, Instruction::LONG_TO_INT, 0, 0u, 2, { 9u, 10u }, 1, { 12u } }, // Killed with 9u (BP) - }; - - static const int32_t sreg_to_vreg_map[] = { - 2, 3, 4, 1, 4, 5, 6 /* high word */, 0, 7, 0, 1 /* high word */, 8, 0 - }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 5, 9 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2, 5, 6, 7, 9 }; - ExpectValueNamesNE(diff_indexes); - EXPECT_EQ(value_names_[0], value_names_[3]); - EXPECT_EQ(value_names_[2], value_names_[4]); - EXPECT_EQ(value_names_[5], value_names_[8]); - EXPECT_EQ(value_names_[6], value_names_[10]); - - static const bool eliminated[] = { - false, false, false, true, true, false, false, false, true, false, true, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestSimple, ArrayLengthThrows) { - static const MIRDef mirs[] = { - DEF_CONST(3, Instruction::CONST, 0u, 0), // null - DEF_UNOP(3, Instruction::ARRAY_LENGTH, 1u, 0u), // null.length - DEF_CONST(3, Instruction::CONST, 2u, 1000u), // Overwrite the array-length dest. - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 1 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - PerformGVN_DCE(); - - ASSERT_EQ(arraysize(mirs), value_names_.size()); - static const size_t diff_indexes[] = { 0, 1, 2 }; - ExpectValueNamesNE(diff_indexes); - - static const bool eliminated[] = { - false, false, false, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -TEST_F(GvnDeadCodeEliminationTestSimple, Dependancy) { - static const MIRDef mirs[] = { - DEF_MOVE(3, Instruction::MOVE, 5u, 1u), // move v5,v1 - DEF_MOVE(3, Instruction::MOVE, 6u, 1u), // move v12,v1 - DEF_MOVE(3, Instruction::MOVE, 7u, 0u), // move v13,v0 - DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 8u, 2u), // move v0_1,v2_3 - DEF_MOVE(3, Instruction::MOVE, 10u, 6u), // move v3,v12 - DEF_MOVE(3, Instruction::MOVE, 11u, 4u), // move v2,v4 - DEF_MOVE(3, Instruction::MOVE, 12u, 7u), // move v4,v13 - DEF_MOVE(3, Instruction::MOVE, 13, 11u), // move v12,v2 - DEF_MOVE(3, Instruction::MOVE, 14u, 10u), // move v2,v3 - DEF_MOVE(3, Instruction::MOVE, 15u, 5u), // move v3,v5 - DEF_MOVE(3, Instruction::MOVE, 16u, 12u), // move v5,v4 - }; - - static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 12, 13, 0, 1, 3, 2, 4, 12, 2, 3, 5 }; - PrepareSRegToVRegMap(sreg_to_vreg_map); - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 2, 8 }; - MarkAsWideSRegs(wide_sregs); - PerformGVN_DCE(); - - static const bool eliminated[] = { - false, false, false, false, false, false, false, true, true, false, false, - }; - static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); - for (size_t i = 0; i != arraysize(eliminated); ++i) { - bool actually_eliminated = (static_cast(mirs_[i].dalvikInsn.opcode) == kMirOpNop); - EXPECT_EQ(eliminated[i], actually_eliminated) << i; - } -} - -} // namespace art diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc deleted file mode 100644 index 38f7d1e712..0000000000 --- a/compiler/dex/local_value_numbering.cc +++ /dev/null @@ -1,2038 +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. - */ - -#include "local_value_numbering.h" - -#include "base/bit_utils.h" -#include "global_value_numbering.h" -#include "mir_field_info.h" -#include "mir_graph.h" -#include "utils.h" - -namespace art { - -namespace { // anonymous namespace - -// Operations used for value map keys instead of actual opcode. -static constexpr uint16_t kInvokeMemoryVersionBumpOp = Instruction::INVOKE_VIRTUAL; -static constexpr uint16_t kUnresolvedSFieldOp = Instruction::SGET; -static constexpr uint16_t kResolvedSFieldOp = Instruction::SGET_WIDE; -static constexpr uint16_t kUnresolvedIFieldOp = Instruction::IGET; -static constexpr uint16_t kNonAliasingIFieldLocOp = Instruction::IGET_WIDE; -static constexpr uint16_t kNonAliasingIFieldInitialOp = Instruction::IGET_OBJECT; -static constexpr uint16_t kAliasingIFieldOp = Instruction::IGET_BOOLEAN; -static constexpr uint16_t kAliasingIFieldStartVersionOp = Instruction::IGET_BYTE; -static constexpr uint16_t kAliasingIFieldBumpVersionOp = Instruction::IGET_CHAR; -static constexpr uint16_t kNonAliasingArrayOp = Instruction::AGET; -static constexpr uint16_t kNonAliasingArrayStartVersionOp = Instruction::AGET_WIDE; -static constexpr uint16_t kNonAliasingArrayBumpVersionOp = Instruction::AGET_OBJECT; -static constexpr uint16_t kAliasingArrayOp = Instruction::AGET_BOOLEAN; -static constexpr uint16_t kAliasingArrayStartVersionOp = Instruction::AGET_BYTE; -static constexpr uint16_t kAliasingArrayBumpVersionOp = Instruction::AGET_CHAR; -static constexpr uint16_t kMergeBlockMemoryVersionBumpOp = Instruction::INVOKE_VIRTUAL_RANGE; -static constexpr uint16_t kMergeBlockAliasingIFieldVersionBumpOp = Instruction::IPUT; -static constexpr uint16_t kMergeBlockAliasingIFieldMergeLocationOp = Instruction::IPUT_WIDE; -static constexpr uint16_t kMergeBlockNonAliasingArrayVersionBumpOp = Instruction::APUT; -static constexpr uint16_t kMergeBlockNonAliasingArrayMergeLocationOp = Instruction::APUT_WIDE; -static constexpr uint16_t kMergeBlockAliasingArrayVersionBumpOp = Instruction::APUT_OBJECT; -static constexpr uint16_t kMergeBlockAliasingArrayMergeLocationOp = Instruction::APUT_BOOLEAN; -static constexpr uint16_t kMergeBlockNonAliasingIFieldVersionBumpOp = Instruction::APUT_BYTE; -static constexpr uint16_t kMergeBlockSFieldVersionBumpOp = Instruction::APUT_CHAR; - -} // anonymous namespace - -class LocalValueNumbering::AliasingIFieldVersions { - public: - static uint16_t StartMemoryVersion(GlobalValueNumbering* gvn, const LocalValueNumbering* lvn, - uint16_t field_id) { - uint16_t type = gvn->GetIFieldType(field_id); - return gvn->LookupValue(kAliasingIFieldStartVersionOp, field_id, - lvn->global_memory_version_, lvn->unresolved_ifield_version_[type]); - } - - static uint16_t BumpMemoryVersion(GlobalValueNumbering* gvn, uint16_t old_version, - uint16_t store_ref_set_id, uint16_t stored_value) { - return gvn->LookupValue(kAliasingIFieldBumpVersionOp, old_version, - store_ref_set_id, stored_value); - } - - static uint16_t LookupGlobalValue(GlobalValueNumbering* gvn, - uint16_t field_id, uint16_t base, uint16_t memory_version) { - return gvn->LookupValue(kAliasingIFieldOp, field_id, base, memory_version); - } - - static uint16_t LookupMergeValue(GlobalValueNumbering* gvn, const LocalValueNumbering* lvn, - uint16_t field_id, uint16_t base) { - // If the base/field_id is non-aliasing in lvn, use the non-aliasing value. - uint16_t type = gvn->GetIFieldType(field_id); - if (lvn->IsNonAliasingIField(base, field_id, type)) { - uint16_t loc = gvn->LookupValue(kNonAliasingIFieldLocOp, base, field_id, type); - auto lb = lvn->non_aliasing_ifield_value_map_.find(loc); - return (lb != lvn->non_aliasing_ifield_value_map_.end()) - ? lb->second - : gvn->LookupValue(kNonAliasingIFieldInitialOp, loc, kNoValue, kNoValue); - } - return AliasingValuesMergeGet( - gvn, lvn, &lvn->aliasing_ifield_value_map_, field_id, base); - } - - static bool HasNewBaseVersion(GlobalValueNumbering* gvn, const LocalValueNumbering* lvn, - uint16_t field_id) { - uint16_t type = gvn->GetIFieldType(field_id); - return lvn->unresolved_ifield_version_[type] == lvn->merge_new_memory_version_ || - lvn->global_memory_version_ == lvn->merge_new_memory_version_; - } - - static uint16_t LookupMergeBlockValue(GlobalValueNumbering* gvn, uint16_t lvn_id, - uint16_t field_id) { - return gvn->LookupValue(kMergeBlockAliasingIFieldVersionBumpOp, field_id, kNoValue, lvn_id); - } - - static uint16_t LookupMergeLocationValue(GlobalValueNumbering* gvn, uint16_t lvn_id, - uint16_t field_id, uint16_t base) { - return gvn->LookupValue(kMergeBlockAliasingIFieldMergeLocationOp, field_id, base, lvn_id); - } -}; - -class LocalValueNumbering::NonAliasingArrayVersions { - public: - static uint16_t StartMemoryVersion(GlobalValueNumbering* gvn, - const LocalValueNumbering* lvn ATTRIBUTE_UNUSED, - uint16_t array) { - return gvn->LookupValue(kNonAliasingArrayStartVersionOp, array, kNoValue, kNoValue); - } - - static uint16_t BumpMemoryVersion(GlobalValueNumbering* gvn, uint16_t old_version, - uint16_t store_ref_set_id, uint16_t stored_value) { - return gvn->LookupValue(kNonAliasingArrayBumpVersionOp, old_version, - store_ref_set_id, stored_value); - } - - static uint16_t LookupGlobalValue(GlobalValueNumbering* gvn, - uint16_t array, uint16_t index, uint16_t memory_version) { - return gvn->LookupValue(kNonAliasingArrayOp, array, index, memory_version); - } - - static uint16_t LookupMergeValue(GlobalValueNumbering* gvn, const LocalValueNumbering* lvn, - uint16_t array, uint16_t index) { - return AliasingValuesMergeGet( - gvn, lvn, &lvn->non_aliasing_array_value_map_, array, index); - } - - static bool HasNewBaseVersion(GlobalValueNumbering* gvn ATTRIBUTE_UNUSED, - const LocalValueNumbering* lvn ATTRIBUTE_UNUSED, - uint16_t array ATTRIBUTE_UNUSED) { - return false; // Not affected by global_memory_version_. - } - - static uint16_t LookupMergeBlockValue(GlobalValueNumbering* gvn, uint16_t lvn_id, - uint16_t array) { - return gvn->LookupValue(kMergeBlockNonAliasingArrayVersionBumpOp, array, kNoValue, lvn_id); - } - - static uint16_t LookupMergeLocationValue(GlobalValueNumbering* gvn, uint16_t lvn_id, - uint16_t array, uint16_t index) { - return gvn->LookupValue(kMergeBlockNonAliasingArrayMergeLocationOp, array, index, lvn_id); - } -}; - -class LocalValueNumbering::AliasingArrayVersions { - public: - static uint16_t StartMemoryVersion(GlobalValueNumbering* gvn, const LocalValueNumbering* lvn, - uint16_t type) { - return gvn->LookupValue(kAliasingArrayStartVersionOp, type, lvn->global_memory_version_, - kNoValue); - } - - static uint16_t BumpMemoryVersion(GlobalValueNumbering* gvn, uint16_t old_version, - uint16_t store_ref_set_id, uint16_t stored_value) { - return gvn->LookupValue(kAliasingArrayBumpVersionOp, old_version, - store_ref_set_id, stored_value); - } - - static uint16_t LookupGlobalValue(GlobalValueNumbering* gvn, - uint16_t type, uint16_t location, uint16_t memory_version) { - return gvn->LookupValue(kAliasingArrayOp, type, location, memory_version); - } - - static uint16_t LookupMergeValue(GlobalValueNumbering* gvn, - const LocalValueNumbering* lvn, - uint16_t type, uint16_t location) { - // If the location is non-aliasing in lvn, use the non-aliasing value. - uint16_t array = gvn->GetArrayLocationBase(location); - if (lvn->IsNonAliasingArray(array, type)) { - uint16_t index = gvn->GetArrayLocationIndex(location); - return NonAliasingArrayVersions::LookupMergeValue(gvn, lvn, array, index); - } - return AliasingValuesMergeGet( - gvn, lvn, &lvn->aliasing_array_value_map_, type, location); - } - - static bool HasNewBaseVersion(GlobalValueNumbering* gvn ATTRIBUTE_UNUSED, - const LocalValueNumbering* lvn, - uint16_t type ATTRIBUTE_UNUSED) { - return lvn->global_memory_version_ == lvn->merge_new_memory_version_; - } - - static uint16_t LookupMergeBlockValue(GlobalValueNumbering* gvn, uint16_t lvn_id, - uint16_t type) { - return gvn->LookupValue(kMergeBlockAliasingArrayVersionBumpOp, type, kNoValue, lvn_id); - } - - static uint16_t LookupMergeLocationValue(GlobalValueNumbering* gvn, uint16_t lvn_id, - uint16_t type, uint16_t location) { - return gvn->LookupValue(kMergeBlockAliasingArrayMergeLocationOp, type, location, lvn_id); - } -}; - -template -LocalValueNumbering::AliasingValues* LocalValueNumbering::GetAliasingValues( - Map* map, const typename Map::key_type& key) { - auto lb = map->lower_bound(key); - if (lb == map->end() || map->key_comp()(key, lb->first)) { - lb = map->PutBefore(lb, key, AliasingValues(this)); - } - return &lb->second; -} - -template -void LocalValueNumbering::UpdateAliasingValuesLoadVersion(const KeyType& key, - AliasingValues* values) { - if (values->last_load_memory_version == kNoValue) { - // Get the start version that accounts for aliasing with unresolved fields of the same - // type and make it unique for the field by including the field_id. - uint16_t memory_version = values->memory_version_before_stores; - if (memory_version == kNoValue) { - memory_version = Versions::StartMemoryVersion(gvn_, this, key); - } - if (!values->store_loc_set.empty()) { - uint16_t ref_set_id = gvn_->GetRefSetId(values->store_loc_set); - memory_version = Versions::BumpMemoryVersion(gvn_, memory_version, ref_set_id, - values->last_stored_value); - } - values->last_load_memory_version = memory_version; - } -} - -template -uint16_t LocalValueNumbering::AliasingValuesMergeGet(GlobalValueNumbering* gvn, - const LocalValueNumbering* lvn, - Map* map, const typename Map::key_type& key, - uint16_t location) { - // Retrieve the value name that we would get from - // const_cast(lvn)->HandleAliasingValueGet(map. key, location) - // but don't modify the map. - uint16_t value_name; - auto it = map->find(key); - if (it == map->end()) { - uint16_t start_version = Versions::StartMemoryVersion(gvn, lvn, key); - value_name = Versions::LookupGlobalValue(gvn, key, location, start_version); - } else if (it->second.store_loc_set.count(location) != 0u) { - value_name = it->second.last_stored_value; - } else { - auto load_it = it->second.load_value_map.find(location); - if (load_it != it->second.load_value_map.end()) { - value_name = load_it->second; - } else { - value_name = Versions::LookupGlobalValue(gvn, key, location, it->second.last_load_memory_version); - } - } - return value_name; -} - -template -uint16_t LocalValueNumbering::HandleAliasingValuesGet(Map* map, const typename Map::key_type& key, - uint16_t location) { - // Retrieve the value name for IGET/SGET/AGET, update the map with new value if any. - uint16_t res; - AliasingValues* values = GetAliasingValues(map, key); - if (values->store_loc_set.count(location) != 0u) { - res = values->last_stored_value; - } else { - UpdateAliasingValuesLoadVersion(key, values); - auto lb = values->load_value_map.lower_bound(location); - if (lb != values->load_value_map.end() && lb->first == location) { - res = lb->second; - } else { - res = Versions::LookupGlobalValue(gvn_, key, location, values->last_load_memory_version); - values->load_value_map.PutBefore(lb, location, res); - } - } - return res; -} - -template -bool LocalValueNumbering::HandleAliasingValuesPut(Map* map, const typename Map::key_type& key, - uint16_t location, uint16_t value) { - AliasingValues* values = GetAliasingValues(map, key); - auto load_values_it = values->load_value_map.find(location); - if (load_values_it != values->load_value_map.end() && load_values_it->second == value) { - // This insn can be eliminated, it stores the same value that's already in the field. - return false; - } - if (value == values->last_stored_value) { - auto store_loc_lb = values->store_loc_set.lower_bound(location); - if (store_loc_lb != values->store_loc_set.end() && *store_loc_lb == location) { - // This insn can be eliminated, it stores the same value that's already in the field. - return false; - } - values->store_loc_set.emplace_hint(store_loc_lb, location); - } else { - UpdateAliasingValuesLoadVersion(key, values); - values->memory_version_before_stores = values->last_load_memory_version; - values->last_stored_value = value; - values->store_loc_set.clear(); - values->store_loc_set.insert(location); - } - // Clear the last load memory version and remove all potentially overwritten values. - values->last_load_memory_version = kNoValue; - auto it = values->load_value_map.begin(), end = values->load_value_map.end(); - while (it != end) { - if (it->second == value) { - ++it; - } else { - it = values->load_value_map.erase(it); - } - } - return true; -} - -template -void LocalValueNumbering::CopyAliasingValuesMap(ScopedArenaSafeMap* dest, - const ScopedArenaSafeMap& src) { - // We need each new AliasingValues (or rather its map members) to be constructed - // with our allocator, rather than the allocator of the source. - for (const auto& entry : src) { - auto it = dest->PutBefore(dest->end(), entry.first, AliasingValues(this)); - it->second = entry.second; // Map assignments preserve current allocator. - } -} - -LocalValueNumbering::LocalValueNumbering(GlobalValueNumbering* gvn, uint16_t id, - ScopedArenaAllocator* allocator) - : gvn_(gvn), - id_(id), - sreg_value_map_(std::less(), allocator->Adapter()), - sreg_wide_value_map_(std::less(), allocator->Adapter()), - sfield_value_map_(std::less(), allocator->Adapter()), - non_aliasing_ifield_value_map_(std::less(), allocator->Adapter()), - aliasing_ifield_value_map_(std::less(), allocator->Adapter()), - non_aliasing_array_value_map_(std::less(), allocator->Adapter()), - aliasing_array_value_map_(std::less(), allocator->Adapter()), - global_memory_version_(0u), - non_aliasing_refs_(std::less(), allocator->Adapter()), - escaped_refs_(std::less(), allocator->Adapter()), - escaped_ifield_clobber_set_(EscapedIFieldClobberKeyComparator(), allocator->Adapter()), - escaped_array_clobber_set_(EscapedArrayClobberKeyComparator(), allocator->Adapter()), - range_checked_(RangeCheckKeyComparator() , allocator->Adapter()), - null_checked_(std::less(), allocator->Adapter()), - div_zero_checked_(std::less(), allocator->Adapter()), - merge_names_(allocator->Adapter()), - merge_map_(std::less>(), allocator->Adapter()), - merge_new_memory_version_(kNoValue) { - std::fill_n(unresolved_sfield_version_, arraysize(unresolved_sfield_version_), 0u); - std::fill_n(unresolved_ifield_version_, arraysize(unresolved_ifield_version_), 0u); -} - -bool LocalValueNumbering::Equals(const LocalValueNumbering& other) const { - DCHECK(gvn_ == other.gvn_); - // Compare the maps/sets and memory versions. - return sreg_value_map_ == other.sreg_value_map_ && - sreg_wide_value_map_ == other.sreg_wide_value_map_ && - sfield_value_map_ == other.sfield_value_map_ && - non_aliasing_ifield_value_map_ == other.non_aliasing_ifield_value_map_ && - aliasing_ifield_value_map_ == other.aliasing_ifield_value_map_ && - non_aliasing_array_value_map_ == other.non_aliasing_array_value_map_ && - aliasing_array_value_map_ == other.aliasing_array_value_map_ && - SameMemoryVersion(other) && - non_aliasing_refs_ == other.non_aliasing_refs_ && - escaped_refs_ == other.escaped_refs_ && - escaped_ifield_clobber_set_ == other.escaped_ifield_clobber_set_ && - escaped_array_clobber_set_ == other.escaped_array_clobber_set_ && - range_checked_ == other.range_checked_ && - null_checked_ == other.null_checked_ && - div_zero_checked_ == other.div_zero_checked_; -} - -void LocalValueNumbering::MergeOne(const LocalValueNumbering& other, MergeType merge_type) { - CopyLiveSregValues(&sreg_value_map_, other.sreg_value_map_); - CopyLiveSregValues(&sreg_wide_value_map_, other.sreg_wide_value_map_); - - if (merge_type == kReturnMerge) { - // RETURN or PHI+RETURN. We need only sreg value maps. - return; - } - - non_aliasing_ifield_value_map_ = other.non_aliasing_ifield_value_map_; - CopyAliasingValuesMap(&non_aliasing_array_value_map_, other.non_aliasing_array_value_map_); - non_aliasing_refs_ = other.non_aliasing_refs_; - range_checked_ = other.range_checked_; - null_checked_ = other.null_checked_; - div_zero_checked_ = other.div_zero_checked_; - - const BasicBlock* pred_bb = gvn_->GetBasicBlock(other.Id()); - if (GlobalValueNumbering::HasNullCheckLastInsn(pred_bb, Id())) { - int s_reg = pred_bb->last_mir_insn->ssa_rep->uses[0]; - null_checked_.insert(other.GetOperandValue(s_reg)); - } - - if (merge_type == kCatchMerge) { - // Memory is clobbered. Use new memory version and don't merge aliasing locations. - global_memory_version_ = NewMemoryVersion(&merge_new_memory_version_); - std::fill_n(unresolved_sfield_version_, arraysize(unresolved_sfield_version_), - global_memory_version_); - std::fill_n(unresolved_ifield_version_, arraysize(unresolved_ifield_version_), - global_memory_version_); - PruneNonAliasingRefsForCatch(); - return; - } - - DCHECK(merge_type == kNormalMerge); - global_memory_version_ = other.global_memory_version_; - std::copy_n(other.unresolved_ifield_version_, arraysize(unresolved_sfield_version_), - unresolved_ifield_version_); - std::copy_n(other.unresolved_sfield_version_, arraysize(unresolved_ifield_version_), - unresolved_sfield_version_); - sfield_value_map_ = other.sfield_value_map_; - CopyAliasingValuesMap(&aliasing_ifield_value_map_, other.aliasing_ifield_value_map_); - CopyAliasingValuesMap(&aliasing_array_value_map_, other.aliasing_array_value_map_); - escaped_refs_ = other.escaped_refs_; - escaped_ifield_clobber_set_ = other.escaped_ifield_clobber_set_; - escaped_array_clobber_set_ = other.escaped_array_clobber_set_; -} - -bool LocalValueNumbering::SameMemoryVersion(const LocalValueNumbering& other) const { - return - global_memory_version_ == other.global_memory_version_ && - std::equal(unresolved_ifield_version_, - unresolved_ifield_version_ + arraysize(unresolved_ifield_version_), - other.unresolved_ifield_version_) && - std::equal(unresolved_sfield_version_, - unresolved_sfield_version_ + arraysize(unresolved_sfield_version_), - other.unresolved_sfield_version_); -} - -uint16_t LocalValueNumbering::NewMemoryVersion(uint16_t* new_version) { - if (*new_version == kNoValue) { - *new_version = gvn_->LookupValue(kMergeBlockMemoryVersionBumpOp, 0u, 0u, id_); - } - return *new_version; -} - -void LocalValueNumbering::MergeMemoryVersions(bool clobbered_catch) { - DCHECK_GE(gvn_->merge_lvns_.size(), 2u); - const LocalValueNumbering* cmp = gvn_->merge_lvns_[0]; - // Check if the global version has changed. - bool new_global_version = clobbered_catch; - if (!new_global_version) { - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - if (lvn->global_memory_version_ != cmp->global_memory_version_) { - // Use a new version for everything. - new_global_version = true; - break; - } - } - } - if (new_global_version) { - global_memory_version_ = NewMemoryVersion(&merge_new_memory_version_); - std::fill_n(unresolved_sfield_version_, arraysize(unresolved_sfield_version_), - merge_new_memory_version_); - std::fill_n(unresolved_ifield_version_, arraysize(unresolved_ifield_version_), - merge_new_memory_version_); - } else { - // Initialize with a copy of memory versions from the comparison LVN. - global_memory_version_ = cmp->global_memory_version_; - std::copy_n(cmp->unresolved_ifield_version_, arraysize(unresolved_sfield_version_), - unresolved_ifield_version_); - std::copy_n(cmp->unresolved_sfield_version_, arraysize(unresolved_ifield_version_), - unresolved_sfield_version_); - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - if (lvn == cmp) { - continue; - } - for (size_t i = 0; i != kDexMemAccessTypeCount; ++i) { - if (lvn->unresolved_ifield_version_[i] != cmp->unresolved_ifield_version_[i]) { - unresolved_ifield_version_[i] = NewMemoryVersion(&merge_new_memory_version_); - } - if (lvn->unresolved_sfield_version_[i] != cmp->unresolved_sfield_version_[i]) { - unresolved_sfield_version_[i] = NewMemoryVersion(&merge_new_memory_version_); - } - } - } - } -} - -void LocalValueNumbering::PruneNonAliasingRefsForCatch() { - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - const BasicBlock* bb = gvn_->GetBasicBlock(lvn->Id()); - if (UNLIKELY(bb->taken == id_) || UNLIKELY(bb->fall_through == id_)) { - // Non-exceptional path to a catch handler means that the catch block was actually - // empty and all exceptional paths lead to the shared path after that empty block. - continue; - } - DCHECK_EQ(bb->taken, kNullBlock); - DCHECK_NE(bb->fall_through, kNullBlock); - const BasicBlock* fall_through_bb = gvn_->GetBasicBlock(bb->fall_through); - const MIR* mir = fall_through_bb->first_mir_insn; - DCHECK(mir != nullptr); - // Only INVOKEs can leak and clobber non-aliasing references if they throw. - if ((mir->dalvikInsn.FlagsOf() & Instruction::kInvoke) != 0) { - HandleInvokeArgs(mir, lvn); - } - } -} - - -template -void LocalValueNumbering::IntersectSets() { - DCHECK_GE(gvn_->merge_lvns_.size(), 2u); - - // Find the LVN with the least entries in the set. - const LocalValueNumbering* least_entries_lvn = gvn_->merge_lvns_[0]; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - if ((lvn->*set_ptr).size() < (least_entries_lvn->*set_ptr).size()) { - least_entries_lvn = lvn; - } - } - - // For each key check if it's in all the LVNs. - for (const auto& key : least_entries_lvn->*set_ptr) { - bool checked = true; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - if (lvn != least_entries_lvn && (lvn->*set_ptr).count(key) == 0u) { - checked = false; - break; - } - } - if (checked) { - (this->*set_ptr).emplace_hint((this->*set_ptr).end(), key); - } - } -} - -void LocalValueNumbering::CopyLiveSregValues(SregValueMap* dest, const SregValueMap& src) { - auto dest_end = dest->end(); - ArenaBitVector* live_in_v = gvn_->GetMirGraph()->GetBasicBlock(id_)->data_flow_info->live_in_v; - DCHECK(live_in_v != nullptr); - for (const auto& entry : src) { - bool live = live_in_v->IsBitSet(gvn_->GetMirGraph()->SRegToVReg(entry.first)); - if (live) { - dest->PutBefore(dest_end, entry.first, entry.second); - } - } -} - -template -void LocalValueNumbering::IntersectSregValueMaps() { - DCHECK_GE(gvn_->merge_lvns_.size(), 2u); - - // Find the LVN with the least entries in the set. - const LocalValueNumbering* least_entries_lvn = gvn_->merge_lvns_[0]; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - if ((lvn->*map_ptr).size() < (least_entries_lvn->*map_ptr).size()) { - least_entries_lvn = lvn; - } - } - - // For each key check if it's in all the LVNs. - ArenaBitVector* live_in_v = gvn_->GetMirGraph()->GetBasicBlock(id_)->data_flow_info->live_in_v; - DCHECK(live_in_v != nullptr); - for (const auto& entry : least_entries_lvn->*map_ptr) { - bool live_and_same = live_in_v->IsBitSet(gvn_->GetMirGraph()->SRegToVReg(entry.first)); - if (live_and_same) { - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - if (lvn != least_entries_lvn) { - auto it = (lvn->*map_ptr).find(entry.first); - if (it == (lvn->*map_ptr).end() || !(it->second == entry.second)) { - live_and_same = false; - break; - } - } - } - } - if (live_and_same) { - (this->*map_ptr).PutBefore((this->*map_ptr).end(), entry.first, entry.second); - } - } -} - -// Intersect maps as sets. The value type must be equality-comparable. -template -void LocalValueNumbering::InPlaceIntersectMaps(Map* work_map, const Map& other_map) { - auto work_it = work_map->begin(), work_end = work_map->end(); - auto cmp = work_map->value_comp(); - for (const auto& entry : other_map) { - while (work_it != work_end && - (cmp(*work_it, entry) || - (!cmp(entry, *work_it) && !(work_it->second == entry.second)))) { - work_it = work_map->erase(work_it); - } - if (work_it == work_end) { - return; - } - ++work_it; - } -} - -template -void LocalValueNumbering::MergeSets() { - auto cmp = (this->*set_ptr).value_comp(); - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - auto my_it = (this->*set_ptr).begin(), my_end = (this->*set_ptr).end(); - for (const auto& entry : lvn->*set_ptr) { - while (my_it != my_end && cmp(*my_it, entry)) { - ++my_it; - } - if (my_it != my_end && !cmp(entry, *my_it)) { - // Already handled. - ++my_it; - } else { - // Merge values for this field_id. - (this->*MergeFn)(entry, my_it); // my_it remains valid across inserts to std::set/SafeMap. - } - } - } -} - -void LocalValueNumbering::IntersectAliasingValueLocations(AliasingValues* work_values, - const AliasingValues* values) { - auto cmp = work_values->load_value_map.key_comp(); - auto work_it = work_values->load_value_map.begin(), work_end = work_values->load_value_map.end(); - auto store_it = values->store_loc_set.begin(), store_end = values->store_loc_set.end(); - auto load_it = values->load_value_map.begin(), load_end = values->load_value_map.end(); - while (store_it != store_end || load_it != load_end) { - uint16_t loc; - if (store_it != store_end && (load_it == load_end || *store_it < load_it->first)) { - loc = *store_it; - ++store_it; - } else { - loc = load_it->first; - ++load_it; - DCHECK(store_it == store_end || cmp(loc, *store_it)); - } - while (work_it != work_end && cmp(work_it->first, loc)) { - work_it = work_values->load_value_map.erase(work_it); - } - if (work_it != work_end && !cmp(loc, work_it->first)) { - // The location matches, keep it. - ++work_it; - } - } - while (work_it != work_end) { - work_it = work_values->load_value_map.erase(work_it); - } -} - -void LocalValueNumbering::MergeEscapedRefs(const ValueNameSet::value_type& entry, - ValueNameSet::iterator hint) { - // See if the ref is either escaped or non-aliasing in each predecessor. - bool is_escaped = true; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - if (lvn->non_aliasing_refs_.count(entry) == 0u && - lvn->escaped_refs_.count(entry) == 0u) { - is_escaped = false; - break; - } - } - if (is_escaped) { - escaped_refs_.emplace_hint(hint, entry); - } -} - -void LocalValueNumbering::MergeEscapedIFieldTypeClobberSets( - const EscapedIFieldClobberSet::value_type& entry, EscapedIFieldClobberSet::iterator hint) { - // Insert only type-clobber entries (field_id == kNoValue) of escaped refs. - if (entry.field_id == kNoValue && escaped_refs_.count(entry.base) != 0u) { - escaped_ifield_clobber_set_.emplace_hint(hint, entry); - } -} - -void LocalValueNumbering::MergeEscapedIFieldClobberSets( - const EscapedIFieldClobberSet::value_type& entry, EscapedIFieldClobberSet::iterator hint) { - // Insert only those entries of escaped refs that are not overridden by a type clobber. - if (!(hint == escaped_ifield_clobber_set_.end() && - hint->base == entry.base && hint->type == entry.type) && - escaped_refs_.count(entry.base) != 0u) { - escaped_ifield_clobber_set_.emplace_hint(hint, entry); - } -} - -void LocalValueNumbering::MergeEscapedArrayClobberSets( - const EscapedArrayClobberSet::value_type& entry, EscapedArrayClobberSet::iterator hint) { - if (escaped_refs_.count(entry.base) != 0u) { - escaped_array_clobber_set_.emplace_hint(hint, entry); - } -} - -void LocalValueNumbering::MergeNullChecked() { - DCHECK_GE(gvn_->merge_lvns_.size(), 2u); - - // Find the LVN with the least entries in the set. - const LocalValueNumbering* least_entries_lvn = gvn_->merge_lvns_[0]; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - if (lvn->null_checked_.size() < least_entries_lvn->null_checked_.size()) { - least_entries_lvn = lvn; - } - } - - // For each null-checked value name check if it's null-checked in all the LVNs. - for (const auto& value_name : least_entries_lvn->null_checked_) { - // Merge null_checked_ for this ref. - merge_names_.clear(); - merge_names_.resize(gvn_->merge_lvns_.size(), value_name); - if (gvn_->NullCheckedInAllPredecessors(merge_names_)) { - null_checked_.insert(null_checked_.end(), value_name); - } - } - - // Now check if the least_entries_lvn has a null-check as the last insn. - const BasicBlock* least_entries_bb = gvn_->GetBasicBlock(least_entries_lvn->Id()); - if (gvn_->HasNullCheckLastInsn(least_entries_bb, id_)) { - int s_reg = least_entries_bb->last_mir_insn->ssa_rep->uses[0]; - uint32_t value_name = least_entries_lvn->GetOperandValue(s_reg); - merge_names_.clear(); - merge_names_.resize(gvn_->merge_lvns_.size(), value_name); - if (gvn_->NullCheckedInAllPredecessors(merge_names_)) { - null_checked_.insert(value_name); - } - } -} - -void LocalValueNumbering::MergeDivZeroChecked() { - DCHECK_GE(gvn_->merge_lvns_.size(), 2u); - - // Find the LVN with the least entries in the set. - const LocalValueNumbering* least_entries_lvn = gvn_->merge_lvns_[0]; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - if (lvn->div_zero_checked_.size() < least_entries_lvn->div_zero_checked_.size()) { - least_entries_lvn = lvn; - } - } - - // For each div-zero value name check if it's div-zero checked in all the LVNs. - for (const auto& value_name : least_entries_lvn->div_zero_checked_) { - // Merge null_checked_ for this ref. - merge_names_.clear(); - merge_names_.resize(gvn_->merge_lvns_.size(), value_name); - if (gvn_->DivZeroCheckedInAllPredecessors(merge_names_)) { - div_zero_checked_.insert(div_zero_checked_.end(), value_name); - } - } -} - -void LocalValueNumbering::MergeSFieldValues(const SFieldToValueMap::value_type& entry, - SFieldToValueMap::iterator hint) { - uint16_t field_id = entry.first; - merge_names_.clear(); - uint16_t value_name = kNoValue; - bool same_values = true; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - // Get the value name as in HandleSGet() but don't modify *lvn. - auto it = lvn->sfield_value_map_.find(field_id); - if (it != lvn->sfield_value_map_.end()) { - value_name = it->second; - } else { - uint16_t type = gvn_->GetSFieldType(field_id); - value_name = gvn_->LookupValue(kResolvedSFieldOp, field_id, - lvn->unresolved_sfield_version_[type], - lvn->global_memory_version_); - } - - same_values = same_values && (merge_names_.empty() || value_name == merge_names_.back()); - merge_names_.push_back(value_name); - } - if (same_values) { - // value_name already contains the result. - } else { - auto lb = merge_map_.lower_bound(merge_names_); - if (lb != merge_map_.end() && !merge_map_.key_comp()(merge_names_, lb->first)) { - value_name = lb->second; - } else { - value_name = gvn_->LookupValue(kMergeBlockSFieldVersionBumpOp, field_id, id_, kNoValue); - merge_map_.PutBefore(lb, merge_names_, value_name); - if (gvn_->NullCheckedInAllPredecessors(merge_names_)) { - null_checked_.insert(value_name); - } - } - } - sfield_value_map_.PutBefore(hint, field_id, value_name); -} - -void LocalValueNumbering::MergeNonAliasingIFieldValues(const IFieldLocToValueMap::value_type& entry, - IFieldLocToValueMap::iterator hint) { - uint16_t field_loc = entry.first; - merge_names_.clear(); - uint16_t value_name = kNoValue; - bool same_values = true; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - // Get the value name as in HandleIGet() but don't modify *lvn. - auto it = lvn->non_aliasing_ifield_value_map_.find(field_loc); - if (it != lvn->non_aliasing_ifield_value_map_.end()) { - value_name = it->second; - } else { - value_name = gvn_->LookupValue(kNonAliasingIFieldInitialOp, field_loc, kNoValue, kNoValue); - } - - same_values = same_values && (merge_names_.empty() || value_name == merge_names_.back()); - merge_names_.push_back(value_name); - } - if (same_values) { - // value_name already contains the result. - } else { - auto lb = merge_map_.lower_bound(merge_names_); - if (lb != merge_map_.end() && !merge_map_.key_comp()(merge_names_, lb->first)) { - value_name = lb->second; - } else { - value_name = gvn_->LookupValue(kMergeBlockNonAliasingIFieldVersionBumpOp, field_loc, - id_, kNoValue); - merge_map_.PutBefore(lb, merge_names_, value_name); - if (gvn_->NullCheckedInAllPredecessors(merge_names_)) { - null_checked_.insert(value_name); - } - } - } - non_aliasing_ifield_value_map_.PutBefore(hint, field_loc, value_name); -} - -template -void LocalValueNumbering::MergeAliasingValues(const typename Map::value_type& entry, - typename Map::iterator hint) { - const typename Map::key_type& key = entry.first; - - auto it = (this->*map_ptr).PutBefore(hint, key, AliasingValues(this)); - AliasingValues* my_values = &it->second; - - const AliasingValues* cmp_values = nullptr; - bool same_version = !Versions::HasNewBaseVersion(gvn_, this, key); - uint16_t load_memory_version_for_same_version = kNoValue; - if (same_version) { - // Find the first non-null values. - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - auto value = (lvn->*map_ptr).find(key); - if (value != (lvn->*map_ptr).end()) { - cmp_values = &value->second; - break; - } - } - DCHECK(cmp_values != nullptr); // There must be at least one non-null values. - - // Check if we have identical memory versions, i.e. the global memory version, unresolved - // field version and the values' memory_version_before_stores, last_stored_value - // and store_loc_set are identical. - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - auto value = (lvn->*map_ptr).find(key); - if (value == (lvn->*map_ptr).end()) { - if (cmp_values->memory_version_before_stores != kNoValue) { - same_version = false; - break; - } - } else if (cmp_values->last_stored_value != value->second.last_stored_value || - cmp_values->memory_version_before_stores != value->second.memory_version_before_stores || - cmp_values->store_loc_set != value->second.store_loc_set) { - same_version = false; - break; - } else if (value->second.last_load_memory_version != kNoValue) { - DCHECK(load_memory_version_for_same_version == kNoValue || - load_memory_version_for_same_version == value->second.last_load_memory_version); - load_memory_version_for_same_version = value->second.last_load_memory_version; - } - } - } - - if (same_version) { - // Copy the identical values. - my_values->memory_version_before_stores = cmp_values->memory_version_before_stores; - my_values->last_stored_value = cmp_values->last_stored_value; - my_values->store_loc_set = cmp_values->store_loc_set; - my_values->last_load_memory_version = load_memory_version_for_same_version; - // Merge load values seen in all incoming arcs (i.e. an intersection). - if (!cmp_values->load_value_map.empty()) { - my_values->load_value_map = cmp_values->load_value_map; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - auto value = (lvn->*map_ptr).find(key); - if (value == (lvn->*map_ptr).end() || value->second.load_value_map.empty()) { - my_values->load_value_map.clear(); - break; - } - InPlaceIntersectMaps(&my_values->load_value_map, value->second.load_value_map); - if (my_values->load_value_map.empty()) { - break; - } - } - } - } else { - // Bump version number for the merge. - my_values->memory_version_before_stores = my_values->last_load_memory_version = - Versions::LookupMergeBlockValue(gvn_, id_, key); - - // Calculate the locations that have been either read from or written to in each incoming LVN. - bool first_lvn = true; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - auto value = (lvn->*map_ptr).find(key); - if (value == (lvn->*map_ptr).end()) { - my_values->load_value_map.clear(); - break; - } - if (first_lvn) { - first_lvn = false; - // Copy the first LVN's locations. Values will be overwritten later. - my_values->load_value_map = value->second.load_value_map; - for (uint16_t location : value->second.store_loc_set) { - my_values->load_value_map.Put(location, 0u); - } - } else { - IntersectAliasingValueLocations(my_values, &value->second); - } - } - // Calculate merged values for the intersection. - for (auto& load_value_entry : my_values->load_value_map) { - uint16_t location = load_value_entry.first; - merge_names_.clear(); - uint16_t value_name = kNoValue; - bool same_values = true; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - value_name = Versions::LookupMergeValue(gvn_, lvn, key, location); - same_values = same_values && (merge_names_.empty() || value_name == merge_names_.back()); - merge_names_.push_back(value_name); - } - if (same_values) { - // value_name already contains the result. - } else { - auto lb = merge_map_.lower_bound(merge_names_); - if (lb != merge_map_.end() && !merge_map_.key_comp()(merge_names_, lb->first)) { - value_name = lb->second; - } else { - // NOTE: In addition to the key and id_ which don't change on an LVN recalculation - // during GVN, we also add location which can actually change on recalculation, so the - // value_name below may change. This could lead to an infinite loop if the location - // value name always changed when the refereced value name changes. However, given that - // we assign unique value names for other merges, such as Phis, such a dependency is - // not possible in a well-formed SSA graph. - value_name = Versions::LookupMergeLocationValue(gvn_, id_, key, location); - merge_map_.PutBefore(lb, merge_names_, value_name); - if (gvn_->NullCheckedInAllPredecessors(merge_names_)) { - null_checked_.insert(value_name); - } - } - } - load_value_entry.second = value_name; - } - } -} - -void LocalValueNumbering::Merge(MergeType merge_type) { - DCHECK_GE(gvn_->merge_lvns_.size(), 2u); - - // Always reserve space in merge_names_. Even if we don't use it in Merge() we may need it - // in GetStartingVregValueNumberImpl() when the merge_names_'s allocator is not the top. - merge_names_.reserve(gvn_->merge_lvns_.size()); - - IntersectSregValueMaps<&LocalValueNumbering::sreg_value_map_>(); - IntersectSregValueMaps<&LocalValueNumbering::sreg_wide_value_map_>(); - if (merge_type == kReturnMerge) { - // RETURN or PHI+RETURN. We need only sreg value maps. - return; - } - - MergeMemoryVersions(merge_type == kCatchMerge); - - // Merge non-aliasing maps/sets. - IntersectSets(); - if (!non_aliasing_refs_.empty() && merge_type == kCatchMerge) { - PruneNonAliasingRefsForCatch(); - } - if (!non_aliasing_refs_.empty()) { - MergeSets(); - MergeSets>(); - } - - // We won't do anything complicated for range checks, just calculate the intersection. - IntersectSets(); - - // Merge null_checked_. We may later insert more, such as merged object field values. - MergeNullChecked(); - - // Now merge the div_zero_checked_. - MergeDivZeroChecked(); - - if (merge_type == kCatchMerge) { - // Memory is clobbered. New memory version already created, don't merge aliasing locations. - return; - } - - DCHECK(merge_type == kNormalMerge); - - // Merge escaped refs and clobber sets. - MergeSets(); - if (!escaped_refs_.empty()) { - MergeSets(); - MergeSets(); - MergeSets(); - } - - MergeSets(); - MergeSets>(); - MergeSets>(); -} - -void LocalValueNumbering::PrepareEntryBlock() { - uint32_t vreg = gvn_->GetMirGraph()->GetFirstInVR(); - CompilationUnit* cu = gvn_->GetCompilationUnit(); - const char* shorty = cu->shorty; - ++shorty; // Skip return value. - if ((cu->access_flags & kAccStatic) == 0) { - // If non-static method, mark "this" as non-null - uint16_t value_name = GetOperandValue(vreg); - ++vreg; - null_checked_.insert(value_name); - } - for ( ; *shorty != 0; ++shorty, ++vreg) { - if (*shorty == 'J' || *shorty == 'D') { - uint16_t value_name = GetOperandValueWide(vreg); - SetOperandValueWide(vreg, value_name); - ++vreg; - } - } -} - -uint16_t LocalValueNumbering::MarkNonAliasingNonNull(MIR* mir) { - uint16_t res = GetOperandValue(mir->ssa_rep->defs[0]); - DCHECK(null_checked_.find(res) == null_checked_.end()); - null_checked_.insert(res); - non_aliasing_refs_.insert(res); - return res; -} - -bool LocalValueNumbering::IsNonAliasing(uint16_t reg) const { - return non_aliasing_refs_.find(reg) != non_aliasing_refs_.end(); -} - -bool LocalValueNumbering::IsNonAliasingIField(uint16_t reg, uint16_t field_id, - uint16_t type) const { - if (IsNonAliasing(reg)) { - return true; - } - if (escaped_refs_.find(reg) == escaped_refs_.end()) { - return false; - } - // Check for IPUTs to unresolved fields. - EscapedIFieldClobberKey key1 = { reg, type, kNoValue }; - if (escaped_ifield_clobber_set_.find(key1) != escaped_ifield_clobber_set_.end()) { - return false; - } - // Check for aliased IPUTs to the same field. - EscapedIFieldClobberKey key2 = { reg, type, field_id }; - return escaped_ifield_clobber_set_.find(key2) == escaped_ifield_clobber_set_.end(); -} - -bool LocalValueNumbering::IsNonAliasingArray(uint16_t reg, uint16_t type) const { - if (IsNonAliasing(reg)) { - return true; - } - if (escaped_refs_.count(reg) == 0u) { - return false; - } - // Check for aliased APUTs. - EscapedArrayClobberKey key = { reg, type }; - return escaped_array_clobber_set_.find(key) == escaped_array_clobber_set_.end(); -} - -void LocalValueNumbering::HandleNullCheck(MIR* mir, uint16_t reg) { - auto lb = null_checked_.lower_bound(reg); - if (lb != null_checked_.end() && *lb == reg) { - if (LIKELY(gvn_->CanModify())) { - if (gvn_->GetCompilationUnit()->verbose) { - LOG(INFO) << "Removing null check for 0x" << std::hex << mir->offset; - } - mir->optimization_flags |= MIR_IGNORE_NULL_CHECK; - } - } else { - null_checked_.insert(lb, reg); - } -} - -void LocalValueNumbering::HandleRangeCheck(MIR* mir, uint16_t array, uint16_t index) { - RangeCheckKey key = { array, index }; - auto lb = range_checked_.lower_bound(key); - if (lb != range_checked_.end() && !RangeCheckKeyComparator()(key, *lb)) { - if (LIKELY(gvn_->CanModify())) { - if (gvn_->GetCompilationUnit()->verbose) { - LOG(INFO) << "Removing range check for 0x" << std::hex << mir->offset; - } - mir->optimization_flags |= MIR_IGNORE_RANGE_CHECK; - } - } else { - // Mark range check completed. - range_checked_.insert(lb, key); - } -} - -void LocalValueNumbering::HandleDivZeroCheck(MIR* mir, uint16_t reg) { - auto lb = div_zero_checked_.lower_bound(reg); - if (lb != div_zero_checked_.end() && *lb == reg) { - if (LIKELY(gvn_->CanModify())) { - if (gvn_->GetCompilationUnit()->verbose) { - LOG(INFO) << "Removing div zero check for 0x" << std::hex << mir->offset; - } - mir->optimization_flags |= MIR_IGNORE_DIV_ZERO_CHECK; - } - } else { - div_zero_checked_.insert(lb, reg); - } -} - -void LocalValueNumbering::HandlePutObject(MIR* mir) { - // If we're storing a non-aliasing reference, stop tracking it as non-aliasing now. - uint16_t base = GetOperandValue(mir->ssa_rep->uses[0]); - HandleEscapingRef(base); - if (gvn_->CanModify() && null_checked_.count(base) != 0u) { - if (gvn_->GetCompilationUnit()->verbose) { - LOG(INFO) << "Removing GC card mark value null check for 0x" << std::hex << mir->offset; - } - mir->optimization_flags |= MIR_STORE_NON_NULL_VALUE; - } -} - -void LocalValueNumbering::HandleEscapingRef(uint16_t base) { - auto it = non_aliasing_refs_.find(base); - if (it != non_aliasing_refs_.end()) { - non_aliasing_refs_.erase(it); - escaped_refs_.insert(base); - } -} - -void LocalValueNumbering::HandleInvokeArgs(const MIR* mir, const LocalValueNumbering* mir_lvn) { - const int32_t* uses = mir->ssa_rep->uses; - const int32_t* uses_end = uses + mir->ssa_rep->num_uses; - while (uses != uses_end) { - uint16_t sreg = *uses; - ++uses; - // Avoid LookupValue() so that we don't store new values in the global value map. - auto local_it = mir_lvn->sreg_value_map_.find(sreg); - if (local_it != mir_lvn->sreg_value_map_.end()) { - non_aliasing_refs_.erase(local_it->second); - } else { - uint16_t value_name = gvn_->FindValue(kNoValue, sreg, kNoValue, kNoValue); - if (value_name != kNoValue) { - non_aliasing_refs_.erase(value_name); - } - } - } -} - -uint16_t LocalValueNumbering::HandlePhi(MIR* mir) { - if (gvn_->merge_lvns_.empty()) { - // Running LVN without a full GVN? - return kNoValue; - } - // Determine if this Phi is merging wide regs. - RegLocation raw_dest = gvn_->GetMirGraph()->GetRawDest(mir); - if (raw_dest.high_word) { - // This is the high part of a wide reg. Ignore the Phi. - return kNoValue; - } - bool wide = raw_dest.wide; - // Iterate over *merge_lvns_ and skip incoming sregs for BBs without associated LVN. - merge_names_.clear(); - uint16_t value_name = kNoValue; - bool same_values = true; - BasicBlockId* incoming = mir->meta.phi_incoming; - int32_t* uses = mir->ssa_rep->uses; - int16_t pos = 0; - for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { - DCHECK_LT(pos, mir->ssa_rep->num_uses); - while (incoming[pos] != lvn->Id()) { - ++pos; - DCHECK_LT(pos, mir->ssa_rep->num_uses); - } - int s_reg = uses[pos]; - ++pos; - value_name = wide ? lvn->GetOperandValueWide(s_reg) : lvn->GetOperandValue(s_reg); - - same_values = same_values && (merge_names_.empty() || value_name == merge_names_.back()); - merge_names_.push_back(value_name); - } - if (same_values) { - // value_name already contains the result. - } else { - auto lb = merge_map_.lower_bound(merge_names_); - if (lb != merge_map_.end() && !merge_map_.key_comp()(merge_names_, lb->first)) { - value_name = lb->second; - } else { - value_name = gvn_->LookupValue(kNoValue, mir->ssa_rep->defs[0], kNoValue, kNoValue); - merge_map_.PutBefore(lb, merge_names_, value_name); - if (!wide && gvn_->NullCheckedInAllPredecessors(merge_names_)) { - null_checked_.insert(value_name); - } - if (gvn_->DivZeroCheckedInAllPredecessors(merge_names_)) { - div_zero_checked_.insert(value_name); - } - } - } - if (wide) { - SetOperandValueWide(mir->ssa_rep->defs[0], value_name); - } else { - SetOperandValue(mir->ssa_rep->defs[0], value_name); - } - return value_name; -} - -uint16_t LocalValueNumbering::HandleConst(MIR* mir, uint32_t value) { - RegLocation raw_dest = gvn_->GetMirGraph()->GetRawDest(mir); - uint16_t res; - if (value == 0u && raw_dest.ref) { - res = GlobalValueNumbering::kNullValue; - } else { - Instruction::Code op = raw_dest.fp ? Instruction::CONST_HIGH16 : Instruction::CONST; - res = gvn_->LookupValue(op, Low16Bits(value), High16Bits(value), 0); - } - SetOperandValue(mir->ssa_rep->defs[0], res); - return res; -} - -uint16_t LocalValueNumbering::HandleConstWide(MIR* mir, uint64_t value) { - RegLocation raw_dest = gvn_->GetMirGraph()->GetRawDest(mir); - Instruction::Code op = raw_dest.fp ? Instruction::CONST_HIGH16 : Instruction::CONST; - uint32_t low_word = Low32Bits(value); - uint32_t high_word = High32Bits(value); - uint16_t low_res = gvn_->LookupValue(op, Low16Bits(low_word), High16Bits(low_word), 1); - uint16_t high_res = gvn_->LookupValue(op, Low16Bits(high_word), High16Bits(high_word), 2); - uint16_t res = gvn_->LookupValue(op, low_res, high_res, 3); - SetOperandValueWide(mir->ssa_rep->defs[0], res); - return res; -} - -uint16_t LocalValueNumbering::HandleAGet(MIR* mir, uint16_t opcode) { - uint16_t array = GetOperandValue(mir->ssa_rep->uses[0]); - HandleNullCheck(mir, array); - uint16_t index = GetOperandValue(mir->ssa_rep->uses[1]); - HandleRangeCheck(mir, array, index); - uint16_t type = AGetMemAccessType(static_cast(opcode)); - // Establish value number for loaded register. - uint16_t res; - if (IsNonAliasingArray(array, type)) { - res = HandleAliasingValuesGet(&non_aliasing_array_value_map_, - array, index); - } else { - uint16_t location = gvn_->GetArrayLocation(array, index); - res = HandleAliasingValuesGet(&aliasing_array_value_map_, - type, location); - } - if (opcode == Instruction::AGET_WIDE) { - SetOperandValueWide(mir->ssa_rep->defs[0], res); - } else { - SetOperandValue(mir->ssa_rep->defs[0], res); - } - return res; -} - -void LocalValueNumbering::HandleAPut(MIR* mir, uint16_t opcode) { - int array_idx = (opcode == Instruction::APUT_WIDE) ? 2 : 1; - int index_idx = array_idx + 1; - uint16_t array = GetOperandValue(mir->ssa_rep->uses[array_idx]); - HandleNullCheck(mir, array); - uint16_t index = GetOperandValue(mir->ssa_rep->uses[index_idx]); - HandleRangeCheck(mir, array, index); - - uint16_t type = APutMemAccessType(static_cast(opcode)); - uint16_t value = (opcode == Instruction::APUT_WIDE) - ? GetOperandValueWide(mir->ssa_rep->uses[0]) - : GetOperandValue(mir->ssa_rep->uses[0]); - if (IsNonAliasing(array)) { - bool put_is_live = HandleAliasingValuesPut( - &non_aliasing_array_value_map_, array, index, value); - if (!put_is_live) { - // This APUT can be eliminated, it stores the same value that's already in the field. - // TODO: Eliminate the APUT. - return; - } - } else { - uint16_t location = gvn_->GetArrayLocation(array, index); - bool put_is_live = HandleAliasingValuesPut( - &aliasing_array_value_map_, type, location, value); - if (!put_is_live) { - // This APUT can be eliminated, it stores the same value that's already in the field. - // TODO: Eliminate the APUT. - return; - } - - // Clobber all escaped array refs for this type. - for (uint16_t escaped_array : escaped_refs_) { - EscapedArrayClobberKey clobber_key = { escaped_array, type }; - escaped_array_clobber_set_.insert(clobber_key); - } - } -} - -uint16_t LocalValueNumbering::HandleIGet(MIR* mir, uint16_t opcode) { - uint16_t base = GetOperandValue(mir->ssa_rep->uses[0]); - HandleNullCheck(mir, base); - const MirFieldInfo& field_info = gvn_->GetMirGraph()->GetIFieldLoweringInfo(mir); - uint16_t res; - if (!field_info.IsResolved() || field_info.IsVolatile()) { - // Unresolved fields may be volatile, so handle them as such to be safe. - HandleInvokeOrClInitOrAcquireOp(mir); // Volatile GETs have acquire semantics. - // Volatile fields always get a new memory version; field id is irrelevant. - // Use result s_reg - will be unique. - res = gvn_->LookupValue(kNoValue, mir->ssa_rep->defs[0], kNoValue, kNoValue); - } else { - uint16_t type = IGetMemAccessType(static_cast(opcode)); - uint16_t field_id = gvn_->GetIFieldId(mir); - if (IsNonAliasingIField(base, field_id, type)) { - uint16_t loc = gvn_->LookupValue(kNonAliasingIFieldLocOp, base, field_id, type); - auto lb = non_aliasing_ifield_value_map_.lower_bound(loc); - if (lb != non_aliasing_ifield_value_map_.end() && lb->first == loc) { - res = lb->second; - } else { - res = gvn_->LookupValue(kNonAliasingIFieldInitialOp, loc, kNoValue, kNoValue); - non_aliasing_ifield_value_map_.PutBefore(lb, loc, res); - } - } else { - res = HandleAliasingValuesGet(&aliasing_ifield_value_map_, - field_id, base); - } - } - if (opcode == Instruction::IGET_WIDE) { - SetOperandValueWide(mir->ssa_rep->defs[0], res); - } else { - SetOperandValue(mir->ssa_rep->defs[0], res); - } - return res; -} - -void LocalValueNumbering::HandleIPut(MIR* mir, uint16_t opcode) { - int base_reg = (opcode == Instruction::IPUT_WIDE) ? 2 : 1; - uint16_t base = GetOperandValue(mir->ssa_rep->uses[base_reg]); - HandleNullCheck(mir, base); - uint16_t type = IPutMemAccessType(static_cast(opcode)); - const MirFieldInfo& field_info = gvn_->GetMirGraph()->GetIFieldLoweringInfo(mir); - if (!field_info.IsResolved()) { - // Unresolved fields always alias with everything of the same type. - // Use mir->offset as modifier; without elaborate inlining, it will be unique. - unresolved_ifield_version_[type] = - gvn_->LookupValue(kUnresolvedIFieldOp, kNoValue, kNoValue, mir->offset); - - // For simplicity, treat base as escaped now. - HandleEscapingRef(base); - - // Clobber all fields of escaped references of the same type. - for (uint16_t escaped_ref : escaped_refs_) { - EscapedIFieldClobberKey clobber_key = { escaped_ref, type, kNoValue }; - escaped_ifield_clobber_set_.insert(clobber_key); - } - - // Aliasing fields of the same type may have been overwritten. - auto it = aliasing_ifield_value_map_.begin(), end = aliasing_ifield_value_map_.end(); - while (it != end) { - if (gvn_->GetIFieldType(it->first) != type) { - ++it; - } else { - it = aliasing_ifield_value_map_.erase(it); - } - } - } else if (field_info.IsVolatile()) { - // Nothing to do, resolved volatile fields always get a new memory version anyway and - // can't alias with resolved non-volatile fields. - } else { - uint16_t field_id = gvn_->GetIFieldId(mir); - uint16_t value = (opcode == Instruction::IPUT_WIDE) - ? GetOperandValueWide(mir->ssa_rep->uses[0]) - : GetOperandValue(mir->ssa_rep->uses[0]); - if (IsNonAliasing(base)) { - uint16_t loc = gvn_->LookupValue(kNonAliasingIFieldLocOp, base, field_id, type); - auto lb = non_aliasing_ifield_value_map_.lower_bound(loc); - if (lb != non_aliasing_ifield_value_map_.end() && lb->first == loc) { - if (lb->second == value) { - // This IPUT can be eliminated, it stores the same value that's already in the field. - // TODO: Eliminate the IPUT. - return; - } - lb->second = value; // Overwrite. - } else { - non_aliasing_ifield_value_map_.PutBefore(lb, loc, value); - } - } else { - bool put_is_live = HandleAliasingValuesPut( - &aliasing_ifield_value_map_, field_id, base, value); - if (!put_is_live) { - // This IPUT can be eliminated, it stores the same value that's already in the field. - // TODO: Eliminate the IPUT. - return; - } - - // Clobber all fields of escaped references for this field. - for (uint16_t escaped_ref : escaped_refs_) { - EscapedIFieldClobberKey clobber_key = { escaped_ref, type, field_id }; - escaped_ifield_clobber_set_.insert(clobber_key); - } - } - } -} - -uint16_t LocalValueNumbering::HandleSGet(MIR* mir, uint16_t opcode) { - const MirSFieldLoweringInfo& field_info = gvn_->GetMirGraph()->GetSFieldLoweringInfo(mir); - if (!field_info.IsResolved() || field_info.IsVolatile() || - (!field_info.IsClassInitialized() && - (mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 0)) { - // Volatile SGETs (and unresolved fields are potentially volatile) have acquire semantics - // and class initialization can call arbitrary functions, we need to wipe aliasing values. - HandleInvokeOrClInitOrAcquireOp(mir); - } - uint16_t res; - if (!field_info.IsResolved() || field_info.IsVolatile()) { - // Unresolved fields may be volatile, so handle them as such to be safe. - // Volatile fields always get a new memory version; field id is irrelevant. - // Use result s_reg - will be unique. - res = gvn_->LookupValue(kNoValue, mir->ssa_rep->defs[0], kNoValue, kNoValue); - } else { - uint16_t type = SGetMemAccessType(static_cast(opcode)); - uint16_t field_id = gvn_->GetSFieldId(mir); - auto lb = sfield_value_map_.lower_bound(field_id); - if (lb != sfield_value_map_.end() && lb->first == field_id) { - res = lb->second; - } else { - // Resolved non-volatile static fields can alias with non-resolved fields of the same type, - // so we need to use unresolved_sfield_version_[type] in addition to global_memory_version_ - // to determine the version of the field. - res = gvn_->LookupValue(kResolvedSFieldOp, field_id, - unresolved_sfield_version_[type], global_memory_version_); - sfield_value_map_.PutBefore(lb, field_id, res); - } - } - if (opcode == Instruction::SGET_WIDE) { - SetOperandValueWide(mir->ssa_rep->defs[0], res); - } else { - SetOperandValue(mir->ssa_rep->defs[0], res); - } - return res; -} - -void LocalValueNumbering::HandleSPut(MIR* mir, uint16_t opcode) { - const MirSFieldLoweringInfo& field_info = gvn_->GetMirGraph()->GetSFieldLoweringInfo(mir); - if (!field_info.IsClassInitialized() && - (mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 0) { - // Class initialization can call arbitrary functions, we need to wipe aliasing values. - HandleInvokeOrClInitOrAcquireOp(mir); - } - uint16_t type = SPutMemAccessType(static_cast(opcode)); - if (!field_info.IsResolved()) { - // Unresolved fields always alias with everything of the same type. - // Use mir->offset as modifier; without elaborate inlining, it will be unique. - unresolved_sfield_version_[type] = - gvn_->LookupValue(kUnresolvedSFieldOp, kNoValue, kNoValue, mir->offset); - RemoveSFieldsForType(type); - } else if (field_info.IsVolatile()) { - // Nothing to do, resolved volatile fields always get a new memory version anyway and - // can't alias with resolved non-volatile fields. - } else { - uint16_t field_id = gvn_->GetSFieldId(mir); - uint16_t value = (opcode == Instruction::SPUT_WIDE) - ? GetOperandValueWide(mir->ssa_rep->uses[0]) - : GetOperandValue(mir->ssa_rep->uses[0]); - // Resolved non-volatile static fields can alias with non-resolved fields of the same type, - // so we need to use unresolved_sfield_version_[type] in addition to global_memory_version_ - // to determine the version of the field. - auto lb = sfield_value_map_.lower_bound(field_id); - if (lb != sfield_value_map_.end() && lb->first == field_id) { - if (lb->second == value) { - // This SPUT can be eliminated, it stores the same value that's already in the field. - // TODO: Eliminate the SPUT. - return; - } - lb->second = value; // Overwrite. - } else { - sfield_value_map_.PutBefore(lb, field_id, value); - } - } -} - -void LocalValueNumbering::RemoveSFieldsForType(uint16_t type) { - // Erase all static fields of this type from the sfield_value_map_. - for (auto it = sfield_value_map_.begin(), end = sfield_value_map_.end(); it != end; ) { - if (gvn_->GetSFieldType(it->first) == type) { - it = sfield_value_map_.erase(it); - } else { - ++it; - } - } -} - -void LocalValueNumbering::HandleInvokeOrClInitOrAcquireOp(MIR* mir) { - // Use mir->offset as modifier; without elaborate inlining, it will be unique. - global_memory_version_ = - gvn_->LookupValue(kInvokeMemoryVersionBumpOp, 0u, 0u, mir->offset); - // All static fields and instance fields and array elements of aliasing references, - // including escaped references, may have been modified. - sfield_value_map_.clear(); - aliasing_ifield_value_map_.clear(); - aliasing_array_value_map_.clear(); - escaped_refs_.clear(); - escaped_ifield_clobber_set_.clear(); - escaped_array_clobber_set_.clear(); -} - -uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { - uint16_t res = kNoValue; - uint16_t opcode = mir->dalvikInsn.opcode; - switch (opcode) { - case Instruction::NOP: - case Instruction::RETURN_VOID: - case Instruction::RETURN: - case Instruction::RETURN_OBJECT: - case Instruction::RETURN_WIDE: - case Instruction::GOTO: - case Instruction::GOTO_16: - case Instruction::GOTO_32: - case Instruction::THROW: - case Instruction::FILL_ARRAY_DATA: - case Instruction::PACKED_SWITCH: - case Instruction::SPARSE_SWITCH: - case Instruction::IF_EQ: - case Instruction::IF_NE: - case Instruction::IF_LT: - case Instruction::IF_GE: - case Instruction::IF_GT: - case Instruction::IF_LE: - case Instruction::IF_EQZ: - case Instruction::IF_NEZ: - case Instruction::IF_LTZ: - case Instruction::IF_GEZ: - case Instruction::IF_GTZ: - case Instruction::IF_LEZ: - case kMirOpFusedCmplFloat: - case kMirOpFusedCmpgFloat: - case kMirOpFusedCmplDouble: - case kMirOpFusedCmpgDouble: - case kMirOpFusedCmpLong: - // Nothing defined - take no action. - break; - - case Instruction::MONITOR_ENTER: - HandleNullCheck(mir, GetOperandValue(mir->ssa_rep->uses[0])); - HandleInvokeOrClInitOrAcquireOp(mir); // Acquire operation. - break; - - case Instruction::MONITOR_EXIT: - HandleNullCheck(mir, GetOperandValue(mir->ssa_rep->uses[0])); - // If we're running GVN and CanModify(), uneliminated null check indicates bytecode error. - if ((mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0 && - gvn_->work_lvn_ != nullptr && gvn_->CanModify()) { - LOG(WARNING) << "Bytecode error: MONITOR_EXIT is still null checked at 0x" << std::hex - << mir->offset << " in " << PrettyMethod(gvn_->cu_->method_idx, *gvn_->cu_->dex_file); - } - break; - - case Instruction::FILLED_NEW_ARRAY: - case Instruction::FILLED_NEW_ARRAY_RANGE: - // Nothing defined but the result will be unique and non-null. - if (mir->next != nullptr && mir->next->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) { - uint16_t array = MarkNonAliasingNonNull(mir->next); - // Do not SetOperandValue(), we'll do that when we process the MOVE_RESULT_OBJECT. - if (kLocalValueNumberingEnableFilledNewArrayTracking && mir->ssa_rep->num_uses != 0u) { - AliasingValues* values = GetAliasingValues(&non_aliasing_array_value_map_, array); - // Clear the value if we got a merged version in a loop. - *values = AliasingValues(this); - for (size_t i = 0u, count = mir->ssa_rep->num_uses; i != count; ++i) { - DCHECK_EQ(High16Bits(i), 0u); - uint16_t index = gvn_->LookupValue(Instruction::CONST, i, 0u, 0); - uint16_t value = GetOperandValue(mir->ssa_rep->uses[i]); - values->load_value_map.Put(index, value); - RangeCheckKey key = { array, index }; - range_checked_.insert(key); - } - } - // The MOVE_RESULT_OBJECT will be processed next and we'll return the value name then. - } - // All args escaped (if references). - for (size_t i = 0u, count = mir->ssa_rep->num_uses; i != count; ++i) { - uint16_t reg = GetOperandValue(mir->ssa_rep->uses[i]); - HandleEscapingRef(reg); - } - break; - - case kMirOpNullCheck: - HandleNullCheck(mir, GetOperandValue(mir->ssa_rep->uses[0])); - break; - - case Instruction::INVOKE_DIRECT: - case Instruction::INVOKE_DIRECT_RANGE: - case Instruction::INVOKE_VIRTUAL: - case Instruction::INVOKE_VIRTUAL_RANGE: - case Instruction::INVOKE_SUPER: - case Instruction::INVOKE_SUPER_RANGE: - case Instruction::INVOKE_INTERFACE: - case Instruction::INVOKE_INTERFACE_RANGE: { - // Nothing defined but handle the null check. - uint16_t reg = GetOperandValue(mir->ssa_rep->uses[0]); - HandleNullCheck(mir, reg); - } - FALLTHROUGH_INTENDED; - case Instruction::INVOKE_STATIC: - case Instruction::INVOKE_STATIC_RANGE: - // Make ref args aliasing. - HandleInvokeArgs(mir, this); - HandleInvokeOrClInitOrAcquireOp(mir); - break; - - case Instruction::INSTANCE_OF: { - uint16_t operand = GetOperandValue(mir->ssa_rep->uses[0]); - uint16_t type = mir->dalvikInsn.vC; - res = gvn_->LookupValue(Instruction::INSTANCE_OF, operand, type, kNoValue); - SetOperandValue(mir->ssa_rep->defs[0], res); - } - break; - case Instruction::CHECK_CAST: - if (gvn_->CanModify()) { - // Check if there was an instance-of operation on the same value and if we are - // in a block where its result is true. If so, we can eliminate the check-cast. - uint16_t operand = GetOperandValue(mir->ssa_rep->uses[0]); - uint16_t type = mir->dalvikInsn.vB; - uint16_t cond = gvn_->FindValue(Instruction::INSTANCE_OF, operand, type, kNoValue); - if (cond != kNoValue && gvn_->IsTrueInBlock(cond, Id())) { - if (gvn_->GetCompilationUnit()->verbose) { - LOG(INFO) << "Removing check-cast at 0x" << std::hex << mir->offset; - } - // Don't use kMirOpNop. Keep the check-cast as it defines the type of the register. - mir->optimization_flags |= MIR_IGNORE_CHECK_CAST; - } - } - break; - - case Instruction::MOVE_RESULT: - case Instruction::MOVE_RESULT_OBJECT: - // 1 result, treat as unique each time, use result s_reg - will be unique. - res = GetOperandValue(mir->ssa_rep->defs[0]); - SetOperandValue(mir->ssa_rep->defs[0], res); - break; - case Instruction::MOVE_EXCEPTION: - case Instruction::NEW_INSTANCE: - case Instruction::NEW_ARRAY: - // 1 result, treat as unique each time, use result s_reg - will be unique. - res = MarkNonAliasingNonNull(mir); - SetOperandValue(mir->ssa_rep->defs[0], res); - break; - case Instruction::CONST_CLASS: - DCHECK_EQ(Low16Bits(mir->dalvikInsn.vB), mir->dalvikInsn.vB); - res = gvn_->LookupValue(Instruction::CONST_CLASS, mir->dalvikInsn.vB, 0, 0); - SetOperandValue(mir->ssa_rep->defs[0], res); - null_checked_.insert(res); - non_aliasing_refs_.insert(res); - break; - case Instruction::CONST_STRING: - case Instruction::CONST_STRING_JUMBO: - // These strings are internalized, so assign value based on the string pool index. - res = gvn_->LookupValue(Instruction::CONST_STRING, Low16Bits(mir->dalvikInsn.vB), - High16Bits(mir->dalvikInsn.vB), 0); - SetOperandValue(mir->ssa_rep->defs[0], res); - null_checked_.insert(res); // May already be there. - // NOTE: Hacking the contents of an internalized string via reflection is possible - // but the behavior is undefined. Therefore, we consider the string constant and - // the reference non-aliasing. - // TUNING: We could keep this property even if the reference "escapes". - non_aliasing_refs_.insert(res); // May already be there. - break; - case Instruction::MOVE_RESULT_WIDE: - // 1 wide result, treat as unique each time, use result s_reg - will be unique. - res = GetOperandValueWide(mir->ssa_rep->defs[0]); - SetOperandValueWide(mir->ssa_rep->defs[0], res); - break; - - case kMirOpPhi: - res = HandlePhi(mir); - break; - - case Instruction::MOVE: - case Instruction::MOVE_OBJECT: - case Instruction::MOVE_16: - case Instruction::MOVE_OBJECT_16: - case Instruction::MOVE_FROM16: - case Instruction::MOVE_OBJECT_FROM16: - case kMirOpCopy: - // Just copy value number of source to value number of result. - res = GetOperandValue(mir->ssa_rep->uses[0]); - SetOperandValue(mir->ssa_rep->defs[0], res); - break; - - case Instruction::MOVE_WIDE: - case Instruction::MOVE_WIDE_16: - case Instruction::MOVE_WIDE_FROM16: - // Just copy value number of source to value number of result. - res = GetOperandValueWide(mir->ssa_rep->uses[0]); - SetOperandValueWide(mir->ssa_rep->defs[0], res); - break; - - case Instruction::CONST_HIGH16: - res = HandleConst(mir, mir->dalvikInsn.vB << 16); - break; - case Instruction::CONST: - case Instruction::CONST_4: - case Instruction::CONST_16: - res = HandleConst(mir, mir->dalvikInsn.vB); - break; - - case Instruction::CONST_WIDE_16: - case Instruction::CONST_WIDE_32: - res = HandleConstWide( - mir, - mir->dalvikInsn.vB + - ((mir->dalvikInsn.vB & 0x80000000) != 0 ? UINT64_C(0xffffffff00000000) : 0u)); - break; - - case Instruction::CONST_WIDE: - res = HandleConstWide(mir, mir->dalvikInsn.vB_wide); - break; - - case Instruction::CONST_WIDE_HIGH16: - res = HandleConstWide(mir, static_cast(mir->dalvikInsn.vB) << 48); - break; - - case Instruction::ARRAY_LENGTH: { - // Handle the null check. - uint16_t reg = GetOperandValue(mir->ssa_rep->uses[0]); - HandleNullCheck(mir, reg); - } - FALLTHROUGH_INTENDED; - case Instruction::NEG_INT: - case Instruction::NOT_INT: - case Instruction::NEG_FLOAT: - case Instruction::INT_TO_BYTE: - case Instruction::INT_TO_SHORT: - case Instruction::INT_TO_CHAR: - case Instruction::INT_TO_FLOAT: - case Instruction::FLOAT_TO_INT: { - // res = op + 1 operand - uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); - res = gvn_->LookupValue(opcode, operand1, kNoValue, kNoValue); - SetOperandValue(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::LONG_TO_FLOAT: - case Instruction::LONG_TO_INT: - case Instruction::DOUBLE_TO_FLOAT: - case Instruction::DOUBLE_TO_INT: { - // res = op + 1 wide operand - uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); - res = gvn_->LookupValue(opcode, operand1, kNoValue, kNoValue); - SetOperandValue(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::DOUBLE_TO_LONG: - case Instruction::LONG_TO_DOUBLE: - case Instruction::NEG_LONG: - case Instruction::NOT_LONG: - case Instruction::NEG_DOUBLE: { - // wide res = op + 1 wide operand - uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); - res = gvn_->LookupValue(opcode, operand1, kNoValue, kNoValue); - SetOperandValueWide(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::FLOAT_TO_DOUBLE: - case Instruction::FLOAT_TO_LONG: - case Instruction::INT_TO_DOUBLE: - case Instruction::INT_TO_LONG: { - // wide res = op + 1 operand - uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); - res = gvn_->LookupValue(opcode, operand1, kNoValue, kNoValue); - SetOperandValueWide(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::CMPL_DOUBLE: - case Instruction::CMPG_DOUBLE: - case Instruction::CMP_LONG: { - // res = op + 2 wide operands - uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); - uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]); - res = gvn_->LookupValue(opcode, operand1, operand2, kNoValue); - SetOperandValue(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::DIV_INT: - case Instruction::DIV_INT_2ADDR: - case Instruction::REM_INT: - case Instruction::REM_INT_2ADDR: - HandleDivZeroCheck(mir, GetOperandValue(mir->ssa_rep->uses[1])); - FALLTHROUGH_INTENDED; - - case Instruction::CMPG_FLOAT: - case Instruction::CMPL_FLOAT: - case Instruction::ADD_INT: - case Instruction::ADD_INT_2ADDR: - case Instruction::MUL_INT: - case Instruction::MUL_INT_2ADDR: - case Instruction::AND_INT: - case Instruction::AND_INT_2ADDR: - case Instruction::OR_INT: - case Instruction::OR_INT_2ADDR: - case Instruction::XOR_INT: - case Instruction::XOR_INT_2ADDR: - case Instruction::SUB_INT: - case Instruction::SUB_INT_2ADDR: - case Instruction::SHL_INT: - case Instruction::SHL_INT_2ADDR: - case Instruction::SHR_INT: - case Instruction::SHR_INT_2ADDR: - case Instruction::USHR_INT: - case Instruction::USHR_INT_2ADDR: { - // res = op + 2 operands - uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); - uint16_t operand2 = GetOperandValue(mir->ssa_rep->uses[1]); - res = gvn_->LookupValue(opcode, operand1, operand2, kNoValue); - SetOperandValue(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::DIV_LONG: - case Instruction::REM_LONG: - case Instruction::DIV_LONG_2ADDR: - case Instruction::REM_LONG_2ADDR: - HandleDivZeroCheck(mir, GetOperandValueWide(mir->ssa_rep->uses[2])); - FALLTHROUGH_INTENDED; - - case Instruction::ADD_LONG: - case Instruction::SUB_LONG: - case Instruction::MUL_LONG: - case Instruction::AND_LONG: - case Instruction::OR_LONG: - case Instruction::XOR_LONG: - case Instruction::ADD_LONG_2ADDR: - case Instruction::SUB_LONG_2ADDR: - case Instruction::MUL_LONG_2ADDR: - case Instruction::AND_LONG_2ADDR: - case Instruction::OR_LONG_2ADDR: - case Instruction::XOR_LONG_2ADDR: - case Instruction::ADD_DOUBLE: - case Instruction::SUB_DOUBLE: - case Instruction::MUL_DOUBLE: - case Instruction::DIV_DOUBLE: - case Instruction::REM_DOUBLE: - case Instruction::ADD_DOUBLE_2ADDR: - case Instruction::SUB_DOUBLE_2ADDR: - case Instruction::MUL_DOUBLE_2ADDR: - case Instruction::DIV_DOUBLE_2ADDR: - case Instruction::REM_DOUBLE_2ADDR: { - // wide res = op + 2 wide operands - uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); - uint16_t operand2 = GetOperandValueWide(mir->ssa_rep->uses[2]); - res = gvn_->LookupValue(opcode, operand1, operand2, kNoValue); - SetOperandValueWide(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::SHL_LONG: - case Instruction::SHR_LONG: - case Instruction::USHR_LONG: - case Instruction::SHL_LONG_2ADDR: - case Instruction::SHR_LONG_2ADDR: - case Instruction::USHR_LONG_2ADDR: { - // wide res = op + 1 wide operand + 1 operand - uint16_t operand1 = GetOperandValueWide(mir->ssa_rep->uses[0]); - uint16_t operand2 = GetOperandValue(mir->ssa_rep->uses[2]); - res = gvn_->LookupValue(opcode, operand1, operand2, kNoValue); - SetOperandValueWide(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::ADD_FLOAT: - case Instruction::SUB_FLOAT: - case Instruction::MUL_FLOAT: - case Instruction::DIV_FLOAT: - case Instruction::REM_FLOAT: - case Instruction::ADD_FLOAT_2ADDR: - case Instruction::SUB_FLOAT_2ADDR: - case Instruction::MUL_FLOAT_2ADDR: - case Instruction::DIV_FLOAT_2ADDR: - case Instruction::REM_FLOAT_2ADDR: { - // res = op + 2 operands - uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); - uint16_t operand2 = GetOperandValue(mir->ssa_rep->uses[1]); - res = gvn_->LookupValue(opcode, operand1, operand2, kNoValue); - SetOperandValue(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::RSUB_INT: - case Instruction::ADD_INT_LIT16: - case Instruction::MUL_INT_LIT16: - case Instruction::DIV_INT_LIT16: - case Instruction::REM_INT_LIT16: - case Instruction::AND_INT_LIT16: - case Instruction::OR_INT_LIT16: - case Instruction::XOR_INT_LIT16: - case Instruction::ADD_INT_LIT8: - case Instruction::RSUB_INT_LIT8: - case Instruction::MUL_INT_LIT8: - case Instruction::DIV_INT_LIT8: - case Instruction::REM_INT_LIT8: - case Instruction::AND_INT_LIT8: - case Instruction::OR_INT_LIT8: - case Instruction::XOR_INT_LIT8: - case Instruction::SHL_INT_LIT8: - case Instruction::SHR_INT_LIT8: - case Instruction::USHR_INT_LIT8: { - // Same as res = op + 2 operands, except use vC as operand 2 - uint16_t operand1 = GetOperandValue(mir->ssa_rep->uses[0]); - uint16_t operand2 = gvn_->LookupValue(Instruction::CONST, mir->dalvikInsn.vC, 0, 0); - res = gvn_->LookupValue(opcode, operand1, operand2, kNoValue); - SetOperandValue(mir->ssa_rep->defs[0], res); - } - break; - - case Instruction::AGET_OBJECT: - case Instruction::AGET: - case Instruction::AGET_WIDE: - case Instruction::AGET_BOOLEAN: - case Instruction::AGET_BYTE: - case Instruction::AGET_CHAR: - case Instruction::AGET_SHORT: - res = HandleAGet(mir, opcode); - break; - - case Instruction::APUT_OBJECT: - HandlePutObject(mir); - FALLTHROUGH_INTENDED; - case Instruction::APUT: - case Instruction::APUT_WIDE: - case Instruction::APUT_BYTE: - case Instruction::APUT_BOOLEAN: - case Instruction::APUT_SHORT: - case Instruction::APUT_CHAR: - HandleAPut(mir, opcode); - break; - - case Instruction::IGET_OBJECT: - case Instruction::IGET: - case Instruction::IGET_WIDE: - case Instruction::IGET_BOOLEAN: - case Instruction::IGET_BYTE: - case Instruction::IGET_CHAR: - case Instruction::IGET_SHORT: - res = HandleIGet(mir, opcode); - break; - - case Instruction::IPUT_OBJECT: - HandlePutObject(mir); - FALLTHROUGH_INTENDED; - case Instruction::IPUT: - case Instruction::IPUT_WIDE: - case Instruction::IPUT_BOOLEAN: - case Instruction::IPUT_BYTE: - case Instruction::IPUT_CHAR: - case Instruction::IPUT_SHORT: - HandleIPut(mir, opcode); - break; - - case Instruction::SGET_OBJECT: - case Instruction::SGET: - case Instruction::SGET_WIDE: - case Instruction::SGET_BOOLEAN: - case Instruction::SGET_BYTE: - case Instruction::SGET_CHAR: - case Instruction::SGET_SHORT: - res = HandleSGet(mir, opcode); - break; - - case Instruction::SPUT_OBJECT: - HandlePutObject(mir); - FALLTHROUGH_INTENDED; - case Instruction::SPUT: - case Instruction::SPUT_WIDE: - case Instruction::SPUT_BOOLEAN: - case Instruction::SPUT_BYTE: - case Instruction::SPUT_CHAR: - case Instruction::SPUT_SHORT: - HandleSPut(mir, opcode); - break; - } - return res; -} - -uint16_t LocalValueNumbering::GetEndingVregValueNumberImpl(int v_reg, bool wide) const { - const BasicBlock* bb = gvn_->GetBasicBlock(Id()); - DCHECK(bb != nullptr); - int s_reg = bb->data_flow_info->vreg_to_ssa_map_exit[v_reg]; - if (s_reg == INVALID_SREG) { - return kNoValue; - } - if (gvn_->GetMirGraph()->GetRegLocation(s_reg).wide != wide) { - return kNoValue; - } - if (wide) { - int high_s_reg = bb->data_flow_info->vreg_to_ssa_map_exit[v_reg + 1]; - if (high_s_reg != s_reg + 1) { - return kNoValue; // High word has been overwritten. - } - return GetSregValueWide(s_reg); - } else { - return GetSregValue(s_reg); - } -} - -uint16_t LocalValueNumbering::GetStartingVregValueNumberImpl(int v_reg, bool wide) const { - DCHECK_EQ(gvn_->mode_, GlobalValueNumbering::kModeGvnPostProcessing); - DCHECK(gvn_->CanModify()); - const BasicBlock* bb = gvn_->GetBasicBlock(Id()); - DCHECK(bb != nullptr); - DCHECK_NE(bb->predecessors.size(), 0u); - if (bb->predecessors.size() == 1u) { - return gvn_->GetLvn(bb->predecessors[0])->GetEndingVregValueNumberImpl(v_reg, wide); - } - merge_names_.clear(); - uint16_t value_name = kNoValue; - bool same_values = true; - for (BasicBlockId pred_id : bb->predecessors) { - value_name = gvn_->GetLvn(pred_id)->GetEndingVregValueNumberImpl(v_reg, wide); - if (value_name == kNoValue) { - return kNoValue; - } - same_values = same_values && (merge_names_.empty() || value_name == merge_names_.back()); - merge_names_.push_back(value_name); - } - if (same_values) { - // value_name already contains the result. - } else { - auto lb = merge_map_.lower_bound(merge_names_); - if (lb != merge_map_.end() && !merge_map_.key_comp()(merge_names_, lb->first)) { - value_name = lb->second; - } else { - value_name = kNoValue; // We never assigned a value name to this set of merged names. - } - } - return value_name; -} - -} // namespace art diff --git a/compiler/dex/local_value_numbering.h b/compiler/dex/local_value_numbering.h deleted file mode 100644 index dff5e27521..0000000000 --- a/compiler/dex/local_value_numbering.h +++ /dev/null @@ -1,416 +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. - */ - -#ifndef ART_COMPILER_DEX_LOCAL_VALUE_NUMBERING_H_ -#define ART_COMPILER_DEX_LOCAL_VALUE_NUMBERING_H_ - -#include - -#include "base/arena_object.h" -#include "base/logging.h" -#include "dex_instruction_utils.h" -#include "global_value_numbering.h" - -namespace art { - -class DexFile; - -// Enable/disable tracking values stored in the FILLED_NEW_ARRAY result. -static constexpr bool kLocalValueNumberingEnableFilledNewArrayTracking = true; - -class LocalValueNumbering : public DeletableArenaObject { - private: - static constexpr uint16_t kNoValue = GlobalValueNumbering::kNoValue; - - public: - LocalValueNumbering(GlobalValueNumbering* gvn, BasicBlockId id, ScopedArenaAllocator* allocator); - - BasicBlockId Id() const { - return id_; - } - - bool Equals(const LocalValueNumbering& other) const; - - bool IsValueNullChecked(uint16_t value_name) const { - return null_checked_.find(value_name) != null_checked_.end(); - } - - bool IsValueDivZeroChecked(uint16_t value_name) const { - return div_zero_checked_.find(value_name) != div_zero_checked_.end(); - } - - uint16_t GetSregValue(uint16_t s_reg) const { - DCHECK(!gvn_->GetMirGraph()->GetRegLocation(s_reg).wide); - return GetSregValueImpl(s_reg, &sreg_value_map_); - } - - uint16_t GetSregValueWide(uint16_t s_reg) const { - DCHECK(gvn_->GetMirGraph()->GetRegLocation(s_reg).wide); - return GetSregValueImpl(s_reg, &sreg_wide_value_map_); - } - - // Get the starting value number for a given dalvik register. - uint16_t GetStartingVregValueNumber(int v_reg) const { - return GetStartingVregValueNumberImpl(v_reg, false); - } - - // Get the starting value number for a given wide dalvik register. - uint16_t GetStartingVregValueNumberWide(int v_reg) const { - return GetStartingVregValueNumberImpl(v_reg, true); - } - - enum MergeType { - kNormalMerge, - kCatchMerge, - kReturnMerge, // RETURN or PHI+RETURN. Merge only sreg maps. - }; - - void MergeOne(const LocalValueNumbering& other, MergeType merge_type); - void Merge(MergeType merge_type); // Merge gvn_->merge_lvns_. - void PrepareEntryBlock(); - - uint16_t GetValueNumber(MIR* mir); - - private: - // A set of value names. - typedef GlobalValueNumbering::ValueNameSet ValueNameSet; - - // Key is s_reg, value is value name. - typedef ScopedArenaSafeMap SregValueMap; - - uint16_t GetEndingVregValueNumberImpl(int v_reg, bool wide) const; - uint16_t GetStartingVregValueNumberImpl(int v_reg, bool wide) const; - - uint16_t GetSregValueImpl(int s_reg, const SregValueMap* map) const { - uint16_t res = kNoValue; - auto lb = map->find(s_reg); - if (lb != map->end()) { - res = lb->second; - } else { - res = gvn_->FindValue(kNoValue, s_reg, kNoValue, kNoValue); - } - return res; - } - - void SetOperandValueImpl(uint16_t s_reg, uint16_t value, SregValueMap* map) { - DCHECK_EQ(map->count(s_reg), 0u); - map->Put(s_reg, value); - } - - uint16_t GetOperandValueImpl(int s_reg, const SregValueMap* map) const { - uint16_t res = kNoValue; - auto lb = map->find(s_reg); - if (lb != map->end()) { - res = lb->second; - } else { - // Using the original value; s_reg refers to an input reg. - res = gvn_->LookupValue(kNoValue, s_reg, kNoValue, kNoValue); - } - return res; - } - - void SetOperandValue(uint16_t s_reg, uint16_t value) { - DCHECK_EQ(sreg_wide_value_map_.count(s_reg), 0u); - DCHECK(!gvn_->GetMirGraph()->GetRegLocation(s_reg).wide); - SetOperandValueImpl(s_reg, value, &sreg_value_map_); - } - - uint16_t GetOperandValue(int s_reg) const { - DCHECK_EQ(sreg_wide_value_map_.count(s_reg), 0u); - DCHECK(!gvn_->GetMirGraph()->GetRegLocation(s_reg).wide); - return GetOperandValueImpl(s_reg, &sreg_value_map_); - } - - void SetOperandValueWide(uint16_t s_reg, uint16_t value) { - DCHECK_EQ(sreg_value_map_.count(s_reg), 0u); - DCHECK(gvn_->GetMirGraph()->GetRegLocation(s_reg).wide); - DCHECK(!gvn_->GetMirGraph()->GetRegLocation(s_reg).high_word); - SetOperandValueImpl(s_reg, value, &sreg_wide_value_map_); - } - - uint16_t GetOperandValueWide(int s_reg) const { - DCHECK_EQ(sreg_value_map_.count(s_reg), 0u); - DCHECK(gvn_->GetMirGraph()->GetRegLocation(s_reg).wide); - DCHECK(!gvn_->GetMirGraph()->GetRegLocation(s_reg).high_word); - return GetOperandValueImpl(s_reg, &sreg_wide_value_map_); - } - - struct RangeCheckKey { - uint16_t array; - uint16_t index; - - // NOTE: Can't define this at namespace scope for a private struct. - bool operator==(const RangeCheckKey& other) const { - return array == other.array && index == other.index; - } - }; - - struct RangeCheckKeyComparator { - bool operator()(const RangeCheckKey& lhs, const RangeCheckKey& rhs) const { - if (lhs.array != rhs.array) { - return lhs.array < rhs.array; - } - return lhs.index < rhs.index; - } - }; - - typedef ScopedArenaSet RangeCheckSet; - - // Maps instance field "location" (derived from base, field_id and type) to value name. - typedef ScopedArenaSafeMap IFieldLocToValueMap; - - // Maps static field id to value name - typedef ScopedArenaSafeMap SFieldToValueMap; - - struct EscapedIFieldClobberKey { - uint16_t base; // Or array. - uint16_t type; - uint16_t field_id; // None (kNoValue) for arrays and unresolved instance field stores. - - // NOTE: Can't define this at namespace scope for a private struct. - bool operator==(const EscapedIFieldClobberKey& other) const { - return base == other.base && type == other.type && field_id == other.field_id; - } - }; - - struct EscapedIFieldClobberKeyComparator { - bool operator()(const EscapedIFieldClobberKey& lhs, const EscapedIFieldClobberKey& rhs) const { - // Compare base first. This makes sequential iteration respect the order of base. - if (lhs.base != rhs.base) { - return lhs.base < rhs.base; - } - // Compare type second. This makes the type-clobber entries (field_id == kNoValue) last - // for given base and type and makes it easy to prune unnecessary entries when merging - // escaped_ifield_clobber_set_ from multiple LVNs. - if (lhs.type != rhs.type) { - return lhs.type < rhs.type; - } - return lhs.field_id < rhs.field_id; - } - }; - - typedef ScopedArenaSet - EscapedIFieldClobberSet; - - struct EscapedArrayClobberKey { - uint16_t base; - uint16_t type; - - // NOTE: Can't define this at namespace scope for a private struct. - bool operator==(const EscapedArrayClobberKey& other) const { - return base == other.base && type == other.type; - } - }; - - struct EscapedArrayClobberKeyComparator { - bool operator()(const EscapedArrayClobberKey& lhs, const EscapedArrayClobberKey& rhs) const { - // Compare base first. This makes sequential iteration respect the order of base. - if (lhs.base != rhs.base) { - return lhs.base < rhs.base; - } - return lhs.type < rhs.type; - } - }; - - // Clobber set for previously non-aliasing array refs that escaped. - typedef ScopedArenaSet - EscapedArrayClobberSet; - - // Known location values for an aliasing set. The set can be tied to one of: - // 1. Instance field. The locations are aliasing references used to access the field. - // 2. Non-aliasing array reference. The locations are indexes to the array. - // 3. Aliasing array type. The locations are (reference, index) pair ids assigned by GVN. - // In each case we keep track of the last stored value, if any, and the set of locations - // where it was stored. We also keep track of all values known for the current write state - // (load_value_map), which can be known either because they have been loaded since the last - // store or because they contained the last_stored_value before the store and thus could not - // have changed as a result. - struct AliasingValues { - explicit AliasingValues(LocalValueNumbering* lvn) - : memory_version_before_stores(kNoValue), - last_stored_value(kNoValue), - store_loc_set(std::less(), lvn->null_checked_.get_allocator()), - last_load_memory_version(kNoValue), - load_value_map(std::less(), lvn->null_checked_.get_allocator()) { - } - - uint16_t memory_version_before_stores; // kNoValue if start version for the field. - uint16_t last_stored_value; // Last stored value name, kNoValue if none. - ValueNameSet store_loc_set; // Where was last_stored_value stored. - - // Maps refs (other than stored_to) to currently known values for this field other. On write, - // anything that differs from the written value is removed as it may be overwritten. - uint16_t last_load_memory_version; // kNoValue if not known. - ScopedArenaSafeMap load_value_map; - - // NOTE: Can't define this at namespace scope for a private struct. - bool operator==(const AliasingValues& other) const { - return memory_version_before_stores == other.memory_version_before_stores && - last_load_memory_version == other.last_load_memory_version && - last_stored_value == other.last_stored_value && - store_loc_set == other.store_loc_set && - load_value_map == other.load_value_map; - } - }; - - // Maps instance field id to AliasingValues, locations are object refs. - typedef ScopedArenaSafeMap AliasingIFieldValuesMap; - - // Maps non-aliasing array reference to AliasingValues, locations are array indexes. - typedef ScopedArenaSafeMap NonAliasingArrayValuesMap; - - // Maps aliasing array type to AliasingValues, locations are (array, index) pair ids. - typedef ScopedArenaSafeMap AliasingArrayValuesMap; - - // Helper classes defining versions for updating and merging the AliasingValues maps above. - class AliasingIFieldVersions; - class NonAliasingArrayVersions; - class AliasingArrayVersions; - - template - AliasingValues* GetAliasingValues(Map* map, const typename Map::key_type& key); - - template - void UpdateAliasingValuesLoadVersion(const KeyType& key, AliasingValues* values); - - template - static uint16_t AliasingValuesMergeGet(GlobalValueNumbering* gvn, - const LocalValueNumbering* lvn, - Map* map, const typename Map::key_type& key, - uint16_t location); - - template - uint16_t HandleAliasingValuesGet(Map* map, const typename Map::key_type& key, - uint16_t location); - - template - bool HandleAliasingValuesPut(Map* map, const typename Map::key_type& key, - uint16_t location, uint16_t value); - - template - void CopyAliasingValuesMap(ScopedArenaSafeMap* dest, - const ScopedArenaSafeMap& src); - - uint16_t MarkNonAliasingNonNull(MIR* mir); - bool IsNonAliasing(uint16_t reg) const; - bool IsNonAliasingIField(uint16_t reg, uint16_t field_id, uint16_t type) const; - bool IsNonAliasingArray(uint16_t reg, uint16_t type) const; - void HandleNullCheck(MIR* mir, uint16_t reg); - void HandleRangeCheck(MIR* mir, uint16_t array, uint16_t index); - void HandleDivZeroCheck(MIR* mir, uint16_t reg); - void HandlePutObject(MIR* mir); - void HandleEscapingRef(uint16_t base); - void HandleInvokeArgs(const MIR* mir, const LocalValueNumbering* mir_lvn); - uint16_t HandlePhi(MIR* mir); - uint16_t HandleConst(MIR* mir, uint32_t value); - uint16_t HandleConstWide(MIR* mir, uint64_t value); - uint16_t HandleAGet(MIR* mir, uint16_t opcode); - void HandleAPut(MIR* mir, uint16_t opcode); - uint16_t HandleIGet(MIR* mir, uint16_t opcode); - void HandleIPut(MIR* mir, uint16_t opcode); - uint16_t HandleSGet(MIR* mir, uint16_t opcode); - void HandleSPut(MIR* mir, uint16_t opcode); - void RemoveSFieldsForType(uint16_t type); - void HandleInvokeOrClInitOrAcquireOp(MIR* mir); - - bool SameMemoryVersion(const LocalValueNumbering& other) const; - - uint16_t NewMemoryVersion(uint16_t* new_version); - void MergeMemoryVersions(bool clobbered_catch); - - void PruneNonAliasingRefsForCatch(); - - template - void IntersectSets(); - - void CopyLiveSregValues(SregValueMap* dest, const SregValueMap& src); - - // Intersect SSA reg value maps as sets, ignore dead regs. - template - void IntersectSregValueMaps(); - - // Intersect maps as sets. The value type must be equality-comparable. - template - static void InPlaceIntersectMaps(Map* work_map, const Map& other_map); - - template - void MergeSets(); - - void IntersectAliasingValueLocations(AliasingValues* work_values, const AliasingValues* values); - - void MergeEscapedRefs(const ValueNameSet::value_type& entry, ValueNameSet::iterator hint); - void MergeEscapedIFieldTypeClobberSets(const EscapedIFieldClobberSet::value_type& entry, - EscapedIFieldClobberSet::iterator hint); - void MergeEscapedIFieldClobberSets(const EscapedIFieldClobberSet::value_type& entry, - EscapedIFieldClobberSet::iterator hint); - void MergeEscapedArrayClobberSets(const EscapedArrayClobberSet::value_type& entry, - EscapedArrayClobberSet::iterator hint); - void MergeSFieldValues(const SFieldToValueMap::value_type& entry, - SFieldToValueMap::iterator hint); - void MergeNonAliasingIFieldValues(const IFieldLocToValueMap::value_type& entry, - IFieldLocToValueMap::iterator hint); - void MergeNullChecked(); - void MergeDivZeroChecked(); - - template - void MergeAliasingValues(const typename Map::value_type& entry, typename Map::iterator hint); - - GlobalValueNumbering* gvn_; - - // We're using the block id as a 16-bit operand value for some lookups. - static_assert(sizeof(BasicBlockId) == sizeof(uint16_t), "BasicBlockId must be 16 bit"); - BasicBlockId id_; - - SregValueMap sreg_value_map_; - SregValueMap sreg_wide_value_map_; - - SFieldToValueMap sfield_value_map_; - IFieldLocToValueMap non_aliasing_ifield_value_map_; - AliasingIFieldValuesMap aliasing_ifield_value_map_; - NonAliasingArrayValuesMap non_aliasing_array_value_map_; - AliasingArrayValuesMap aliasing_array_value_map_; - - // Data for dealing with memory clobbering and store/load aliasing. - uint16_t global_memory_version_; - uint16_t unresolved_sfield_version_[kDexMemAccessTypeCount]; - uint16_t unresolved_ifield_version_[kDexMemAccessTypeCount]; - // Value names of references to objects that cannot be reached through a different value name. - ValueNameSet non_aliasing_refs_; - // Previously non-aliasing refs that escaped but can still be used for non-aliasing AGET/IGET. - ValueNameSet escaped_refs_; - // Blacklists for cases where escaped_refs_ can't be used. - EscapedIFieldClobberSet escaped_ifield_clobber_set_; - EscapedArrayClobberSet escaped_array_clobber_set_; - - // Range check and null check elimination. - RangeCheckSet range_checked_; - ValueNameSet null_checked_; - ValueNameSet div_zero_checked_; - - // Reuse one vector for all merges to avoid leaking too much memory on the ArenaStack. - mutable ScopedArenaVector merge_names_; - // Map to identify when different locations merge the same values. - ScopedArenaSafeMap, uint16_t> merge_map_; - // New memory version for merge, kNoValue if all memory versions matched. - uint16_t merge_new_memory_version_; - - DISALLOW_COPY_AND_ASSIGN(LocalValueNumbering); -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_LOCAL_VALUE_NUMBERING_H_ diff --git a/compiler/dex/local_value_numbering_test.cc b/compiler/dex/local_value_numbering_test.cc deleted file mode 100644 index f98969effd..0000000000 --- a/compiler/dex/local_value_numbering_test.cc +++ /dev/null @@ -1,920 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "dex/mir_field_info.h" -#include "global_value_numbering.h" -#include "local_value_numbering.h" -#include "gtest/gtest.h" - -namespace art { - -class LocalValueNumberingTest : public testing::Test { - protected: - struct IFieldDef { - uint16_t field_idx; - uintptr_t declaring_dex_file; - uint16_t declaring_field_idx; - bool is_volatile; - DexMemAccessType type; - }; - - struct SFieldDef { - uint16_t field_idx; - uintptr_t declaring_dex_file; - uint16_t declaring_field_idx; - bool is_volatile; - DexMemAccessType type; - }; - - struct MIRDef { - static constexpr size_t kMaxSsaDefs = 2; - static constexpr size_t kMaxSsaUses = 4; - - Instruction::Code opcode; - int64_t value; - uint32_t field_info; - size_t num_uses; - int32_t uses[kMaxSsaUses]; - size_t num_defs; - int32_t defs[kMaxSsaDefs]; - }; - -#define DEF_CONST(opcode, reg, value) \ - { opcode, value, 0u, 0, { }, 1, { reg } } -#define DEF_CONST_WIDE(opcode, reg, value) \ - { opcode, value, 0u, 0, { }, 2, { reg, reg + 1 } } -#define DEF_CONST_STRING(opcode, reg, index) \ - { opcode, index, 0u, 0, { }, 1, { reg } } -#define DEF_IGET(opcode, reg, obj, field_info) \ - { opcode, 0u, field_info, 1, { obj }, 1, { reg } } -#define DEF_IGET_WIDE(opcode, reg, obj, field_info) \ - { opcode, 0u, field_info, 1, { obj }, 2, { reg, reg + 1 } } -#define DEF_IPUT(opcode, reg, obj, field_info) \ - { opcode, 0u, field_info, 2, { reg, obj }, 0, { } } -#define DEF_IPUT_WIDE(opcode, reg, obj, field_info) \ - { opcode, 0u, field_info, 3, { reg, reg + 1, obj }, 0, { } } -#define DEF_SGET(opcode, reg, field_info) \ - { opcode, 0u, field_info, 0, { }, 1, { reg } } -#define DEF_SGET_WIDE(opcode, reg, field_info) \ - { opcode, 0u, field_info, 0, { }, 2, { reg, reg + 1 } } -#define DEF_SPUT(opcode, reg, field_info) \ - { opcode, 0u, field_info, 1, { reg }, 0, { } } -#define DEF_SPUT_WIDE(opcode, reg, field_info) \ - { opcode, 0u, field_info, 2, { reg, reg + 1 }, 0, { } } -#define DEF_AGET(opcode, reg, obj, idx) \ - { opcode, 0u, 0u, 2, { obj, idx }, 1, { reg } } -#define DEF_AGET_WIDE(opcode, reg, obj, idx) \ - { opcode, 0u, 0u, 2, { obj, idx }, 2, { reg, reg + 1 } } -#define DEF_APUT(opcode, reg, obj, idx) \ - { opcode, 0u, 0u, 3, { reg, obj, idx }, 0, { } } -#define DEF_APUT_WIDE(opcode, reg, obj, idx) \ - { opcode, 0u, 0u, 4, { reg, reg + 1, obj, idx }, 0, { } } -#define DEF_INVOKE1(opcode, reg) \ - { opcode, 0u, 0u, 1, { reg }, 0, { } } -#define DEF_UNIQUE_REF(opcode, reg) \ - { opcode, 0u, 0u, 0, { }, 1, { reg } } // CONST_CLASS, CONST_STRING, NEW_ARRAY, ... -#define DEF_DIV_REM(opcode, result, dividend, divisor) \ - { opcode, 0u, 0u, 2, { dividend, divisor }, 1, { result } } -#define DEF_DIV_REM_WIDE(opcode, result, dividend, divisor) \ - { opcode, 0u, 0u, 4, { dividend, dividend + 1, divisor, divisor + 1 }, 2, { result, result + 1 } } - - void DoPrepareIFields(const IFieldDef* defs, size_t count) { - cu_.mir_graph->ifield_lowering_infos_.clear(); - cu_.mir_graph->ifield_lowering_infos_.reserve(count); - for (size_t i = 0u; i != count; ++i) { - const IFieldDef* def = &defs[i]; - MirIFieldLoweringInfo field_info(def->field_idx, def->type, false); - if (def->declaring_dex_file != 0u) { - field_info.declaring_dex_file_ = reinterpret_cast(def->declaring_dex_file); - field_info.declaring_field_idx_ = def->declaring_field_idx; - field_info.flags_ &= ~(def->is_volatile ? 0u : MirSFieldLoweringInfo::kFlagIsVolatile); - } - cu_.mir_graph->ifield_lowering_infos_.push_back(field_info); - } - } - - template - void PrepareIFields(const IFieldDef (&defs)[count]) { - DoPrepareIFields(defs, count); - } - - void DoPrepareSFields(const SFieldDef* defs, size_t count) { - cu_.mir_graph->sfield_lowering_infos_.clear(); - cu_.mir_graph->sfield_lowering_infos_.reserve(count); - for (size_t i = 0u; i != count; ++i) { - const SFieldDef* def = &defs[i]; - MirSFieldLoweringInfo field_info(def->field_idx, def->type); - // Mark even unresolved fields as initialized. - field_info.flags_ |= MirSFieldLoweringInfo::kFlagClassIsInitialized; - // NOTE: MirSFieldLoweringInfo::kFlagClassIsInDexCache isn't used by LVN. - if (def->declaring_dex_file != 0u) { - field_info.declaring_dex_file_ = reinterpret_cast(def->declaring_dex_file); - field_info.declaring_field_idx_ = def->declaring_field_idx; - field_info.flags_ &= ~(def->is_volatile ? 0u : MirSFieldLoweringInfo::kFlagIsVolatile); - } - cu_.mir_graph->sfield_lowering_infos_.push_back(field_info); - } - } - - template - void PrepareSFields(const SFieldDef (&defs)[count]) { - DoPrepareSFields(defs, count); - } - - void DoPrepareMIRs(const MIRDef* defs, size_t count) { - mir_count_ = count; - mirs_ = cu_.arena.AllocArray(count, kArenaAllocMIR); - ssa_reps_.resize(count); - for (size_t i = 0u; i != count; ++i) { - const MIRDef* def = &defs[i]; - MIR* mir = &mirs_[i]; - mir->dalvikInsn.opcode = def->opcode; - mir->dalvikInsn.vB = static_cast(def->value); - mir->dalvikInsn.vB_wide = def->value; - if (IsInstructionIGetOrIPut(def->opcode)) { - ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.size()); - mir->meta.ifield_lowering_info = def->field_info; - ASSERT_EQ(cu_.mir_graph->ifield_lowering_infos_[def->field_info].MemAccessType(), - IGetOrIPutMemAccessType(def->opcode)); - } else if (IsInstructionSGetOrSPut(def->opcode)) { - ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.size()); - mir->meta.sfield_lowering_info = def->field_info; - ASSERT_EQ(cu_.mir_graph->sfield_lowering_infos_[def->field_info].MemAccessType(), - SGetOrSPutMemAccessType(def->opcode)); - } - mir->ssa_rep = &ssa_reps_[i]; - mir->ssa_rep->num_uses = def->num_uses; - mir->ssa_rep->uses = const_cast(def->uses); // Not modified by LVN. - mir->ssa_rep->num_defs = def->num_defs; - mir->ssa_rep->defs = const_cast(def->defs); // Not modified by LVN. - mir->dalvikInsn.opcode = def->opcode; - mir->offset = i; // LVN uses offset only for debug output - mir->optimization_flags = 0u; - - if (i != 0u) { - mirs_[i - 1u].next = mir; - } - } - mirs_[count - 1u].next = nullptr; - } - - template - void PrepareMIRs(const MIRDef (&defs)[count]) { - DoPrepareMIRs(defs, count); - } - - void MakeSFieldUninitialized(uint32_t sfield_index) { - CHECK_LT(sfield_index, cu_.mir_graph->sfield_lowering_infos_.size()); - cu_.mir_graph->sfield_lowering_infos_[sfield_index].flags_ &= - ~MirSFieldLoweringInfo::kFlagClassIsInitialized; - } - - template - void MarkAsWideSRegs(const int32_t (&sregs)[count]) { - for (int32_t sreg : sregs) { - cu_.mir_graph->reg_location_[sreg].wide = true; - cu_.mir_graph->reg_location_[sreg + 1].wide = true; - cu_.mir_graph->reg_location_[sreg + 1].high_word = true; - } - } - - void PerformLVN() { - cu_.mir_graph->temp_.gvn.ifield_ids = GlobalValueNumbering::PrepareGvnFieldIds( - allocator_.get(), cu_.mir_graph->ifield_lowering_infos_); - cu_.mir_graph->temp_.gvn.sfield_ids = GlobalValueNumbering::PrepareGvnFieldIds( - allocator_.get(), cu_.mir_graph->sfield_lowering_infos_); - gvn_.reset(new (allocator_.get()) GlobalValueNumbering(&cu_, allocator_.get(), - GlobalValueNumbering::kModeLvn)); - lvn_.reset(new (allocator_.get()) LocalValueNumbering(gvn_.get(), 0u, allocator_.get())); - value_names_.resize(mir_count_); - for (size_t i = 0; i != mir_count_; ++i) { - value_names_[i] = lvn_->GetValueNumber(&mirs_[i]); - } - EXPECT_TRUE(gvn_->Good()); - } - - LocalValueNumberingTest() - : pool_(), - cu_(&pool_, kRuntimeISA, nullptr, nullptr), - mir_count_(0u), - mirs_(nullptr), - ssa_reps_(), - allocator_(), - gvn_(), - lvn_(), - value_names_() { - cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); - allocator_.reset(ScopedArenaAllocator::Create(&cu_.arena_stack)); - // By default, the zero-initialized reg_location_[.] with ref == false tells LVN that - // 0 constants are integral, not references, and the values are all narrow. - // Nothing else is used by LVN/GVN. Tests can override the default values as needed. - cu_.mir_graph->reg_location_ = static_cast(cu_.arena.Alloc( - kMaxSsaRegs * sizeof(cu_.mir_graph->reg_location_[0]), kArenaAllocRegAlloc)); - cu_.mir_graph->num_ssa_regs_ = kMaxSsaRegs; - } - - static constexpr size_t kMaxSsaRegs = 16384u; - - ArenaPool pool_; - CompilationUnit cu_; - size_t mir_count_; - MIR* mirs_; - std::vector ssa_reps_; - std::unique_ptr allocator_; - std::unique_ptr gvn_; - std::unique_ptr lvn_; - std::vector value_names_; -}; - -TEST_F(LocalValueNumberingTest, IGetIGetInvokeIGet) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_IGET(Instruction::IGET, 0u, 10u, 0u), - DEF_IGET(Instruction::IGET, 1u, 10u, 0u), - DEF_INVOKE1(Instruction::INVOKE_VIRTUAL, 11u), - DEF_IGET(Instruction::IGET, 2u, 10u, 0u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 4u); - EXPECT_EQ(value_names_[0], value_names_[1]); - EXPECT_NE(value_names_[0], value_names_[3]); - EXPECT_EQ(mirs_[0].optimization_flags, 0u); - EXPECT_EQ(mirs_[1].optimization_flags, MIR_IGNORE_NULL_CHECK); - EXPECT_EQ(mirs_[2].optimization_flags, 0u); - EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK); -} - -TEST_F(LocalValueNumberingTest, IGetIPutIGetIGetIGet) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessObject }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_IGET(Instruction::IGET_OBJECT, 0u, 10u, 0u), - DEF_IPUT(Instruction::IPUT_OBJECT, 1u, 11u, 0u), // May alias. - DEF_IGET(Instruction::IGET_OBJECT, 2u, 10u, 0u), - DEF_IGET(Instruction::IGET, 3u, 0u, 1u), - DEF_IGET(Instruction::IGET, 4u, 2u, 1u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 5u); - EXPECT_NE(value_names_[0], value_names_[2]); - EXPECT_NE(value_names_[3], value_names_[4]); - for (size_t i = 0; i != arraysize(mirs); ++i) { - EXPECT_EQ((i == 2u) ? MIR_IGNORE_NULL_CHECK : 0, - mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, UniquePreserve1) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 10u), - DEF_IGET(Instruction::IGET, 0u, 10u, 0u), - DEF_IPUT(Instruction::IPUT, 1u, 11u, 0u), // No aliasing since 10u is unique. - DEF_IGET(Instruction::IGET, 2u, 10u, 0u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 4u); - EXPECT_EQ(value_names_[1], value_names_[3]); - for (size_t i = 0; i != arraysize(mirs); ++i) { - EXPECT_EQ((i == 1u || i == 3u) ? MIR_IGNORE_NULL_CHECK : 0, - mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, UniquePreserve2) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 11u), - DEF_IGET(Instruction::IGET, 0u, 10u, 0u), - DEF_IPUT(Instruction::IPUT, 1u, 11u, 0u), // No aliasing since 11u is unique. - DEF_IGET(Instruction::IGET, 2u, 10u, 0u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 4u); - EXPECT_EQ(value_names_[1], value_names_[3]); - for (size_t i = 0; i != arraysize(mirs); ++i) { - EXPECT_EQ((i == 2u || i == 3u) ? MIR_IGNORE_NULL_CHECK : 0, - mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, UniquePreserveAndEscape) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 10u), - DEF_IGET(Instruction::IGET, 0u, 10u, 0u), - DEF_INVOKE1(Instruction::INVOKE_VIRTUAL, 11u), // 10u still unique. - DEF_IGET(Instruction::IGET, 2u, 10u, 0u), - DEF_INVOKE1(Instruction::INVOKE_VIRTUAL, 10u), // 10u not unique anymore. - DEF_IGET(Instruction::IGET, 3u, 10u, 0u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 6u); - EXPECT_EQ(value_names_[1], value_names_[3]); - EXPECT_NE(value_names_[1], value_names_[5]); - for (size_t i = 0; i != arraysize(mirs); ++i) { - EXPECT_EQ((i == 1u || i == 3u || i == 4u || i == 5u) ? MIR_IGNORE_NULL_CHECK : 0, - mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, Volatile) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, true, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_IGET(Instruction::IGET, 0u, 10u, 1u), // Volatile. - DEF_IGET(Instruction::IGET, 1u, 0u, 0u), // Non-volatile. - DEF_IGET(Instruction::IGET, 2u, 10u, 1u), // Volatile. - DEF_IGET(Instruction::IGET, 3u, 2u, 1u), // Non-volatile. - DEF_IGET(Instruction::IGET, 4u, 0u, 0u), // Non-volatile. - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 5u); - EXPECT_NE(value_names_[0], value_names_[2]); // Volatile has always different value name. - EXPECT_NE(value_names_[1], value_names_[3]); // Used different base because of volatile. - EXPECT_NE(value_names_[1], value_names_[4]); // Not guaranteed to be the same after "acquire". - - for (size_t i = 0; i != arraysize(mirs); ++i) { - EXPECT_EQ((i == 2u || i == 4u) ? MIR_IGNORE_NULL_CHECK : 0, - mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, UnresolvedIField) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, // Resolved field #1. - { 2u, 1u, 2u, false, kDexMemAccessWide }, // Resolved field #2. - { 3u, 0u, 0u, false, kDexMemAccessWord }, // Unresolved field. - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 30u), - DEF_IGET(Instruction::IGET, 1u, 30u, 0u), // Resolved field #1, unique object. - DEF_IGET(Instruction::IGET, 2u, 31u, 0u), // Resolved field #1. - DEF_IGET_WIDE(Instruction::IGET_WIDE, 3u, 31u, 1u), // Resolved field #2. - DEF_IGET(Instruction::IGET, 5u, 32u, 2u), // Unresolved IGET can be "acquire". - DEF_IGET(Instruction::IGET, 6u, 30u, 0u), // Resolved field #1, unique object. - DEF_IGET(Instruction::IGET, 7u, 31u, 0u), // Resolved field #1. - DEF_IGET_WIDE(Instruction::IGET_WIDE, 8u, 31u, 1u), // Resolved field #2. - DEF_IPUT(Instruction::IPUT, 10u, 32u, 2u), // IPUT clobbers field #1 (#2 is wide). - DEF_IGET(Instruction::IGET, 11u, 30u, 0u), // Resolved field #1, unique object. - DEF_IGET(Instruction::IGET, 12u, 31u, 0u), // Resolved field #1, new value name. - DEF_IGET_WIDE(Instruction::IGET_WIDE, 13u, 31u, 1u), // Resolved field #2. - DEF_IGET_WIDE(Instruction::IGET_WIDE, 15u, 30u, 1u), // Resolved field #2, unique object. - DEF_IPUT(Instruction::IPUT, 17u, 30u, 2u), // IPUT clobbers field #1 (#2 is wide). - DEF_IGET(Instruction::IGET, 18u, 30u, 0u), // Resolved field #1, unique object. - DEF_IGET_WIDE(Instruction::IGET_WIDE, 19u, 30u, 1u), // Resolved field #2, unique object. - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 3, 8, 13, 15, 19 }; - MarkAsWideSRegs(wide_sregs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 16u); - // Unresolved field is potentially volatile, so we need to adhere to the volatile semantics. - EXPECT_EQ(value_names_[1], value_names_[5]); // Unique object. - EXPECT_NE(value_names_[2], value_names_[6]); // Not guaranteed to be the same after "acquire". - EXPECT_NE(value_names_[3], value_names_[7]); // Not guaranteed to be the same after "acquire". - EXPECT_EQ(value_names_[1], value_names_[9]); // Unique object. - EXPECT_NE(value_names_[6], value_names_[10]); // This aliased with unresolved IPUT. - EXPECT_EQ(value_names_[7], value_names_[11]); // Still the same after "release". - EXPECT_EQ(value_names_[12], value_names_[15]); // Still the same after "release". - EXPECT_NE(value_names_[1], value_names_[14]); // This aliased with unresolved IPUT. - EXPECT_EQ(mirs_[0].optimization_flags, 0u); - EXPECT_EQ(mirs_[1].optimization_flags, MIR_IGNORE_NULL_CHECK); - EXPECT_EQ(mirs_[2].optimization_flags, 0u); - EXPECT_EQ(mirs_[3].optimization_flags, MIR_IGNORE_NULL_CHECK); - EXPECT_EQ(mirs_[4].optimization_flags, 0u); - for (size_t i = 5u; i != mir_count_; ++i) { - EXPECT_EQ((i == 1u || i == 3u || i >=5u) ? MIR_IGNORE_NULL_CHECK : 0, - mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, UnresolvedSField) { - static const SFieldDef sfields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, // Resolved field #1. - { 2u, 1u, 2u, false, kDexMemAccessWide }, // Resolved field #2. - { 3u, 0u, 0u, false, kDexMemAccessWord }, // Unresolved field. - }; - static const MIRDef mirs[] = { - DEF_SGET(Instruction::SGET, 0u, 0u), // Resolved field #1. - DEF_SGET_WIDE(Instruction::SGET_WIDE, 1u, 1u), // Resolved field #2. - DEF_SGET(Instruction::SGET, 3u, 2u), // Unresolved SGET can be "acquire". - DEF_SGET(Instruction::SGET, 4u, 0u), // Resolved field #1. - DEF_SGET_WIDE(Instruction::SGET_WIDE, 5u, 1u), // Resolved field #2. - DEF_SPUT(Instruction::SPUT, 7u, 2u), // SPUT clobbers field #1 (#2 is wide). - DEF_SGET(Instruction::SGET, 8u, 0u), // Resolved field #1. - DEF_SGET_WIDE(Instruction::SGET_WIDE, 9u, 1u), // Resolved field #2. - }; - - PrepareSFields(sfields); - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 1, 5, 9 }; - MarkAsWideSRegs(wide_sregs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 8u); - // Unresolved field is potentially volatile, so we need to adhere to the volatile semantics. - EXPECT_NE(value_names_[0], value_names_[3]); // Not guaranteed to be the same after "acquire". - EXPECT_NE(value_names_[1], value_names_[4]); // Not guaranteed to be the same after "acquire". - EXPECT_NE(value_names_[3], value_names_[6]); // This aliased with unresolved IPUT. - EXPECT_EQ(value_names_[4], value_names_[7]); // Still the same after "release". - for (size_t i = 0u; i != mir_count_; ++i) { - EXPECT_EQ(0, mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, UninitializedSField) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, // Resolved field #1. - }; - static const SFieldDef sfields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, // Resolved field #1. - { 2u, 1u, 2u, false, kDexMemAccessWord }, // Resolved field #2; uninitialized. - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 200u), - DEF_IGET(Instruction::IGET, 1u, 100u, 0u), - DEF_IGET(Instruction::IGET, 2u, 200u, 0u), - DEF_SGET(Instruction::SGET, 3u, 0u), - DEF_SGET(Instruction::SGET, 4u, 1u), // Can call (). - DEF_IGET(Instruction::IGET, 5u, 100u, 0u), // Differs from 1u. - DEF_IGET(Instruction::IGET, 6u, 200u, 0u), // Same as 2u. - DEF_SGET(Instruction::SGET, 7u, 0u), // Differs from 3u. - }; - - PrepareIFields(ifields); - PrepareSFields(sfields); - MakeSFieldUninitialized(1u); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 8u); - EXPECT_NE(value_names_[1], value_names_[5]); - EXPECT_EQ(value_names_[2], value_names_[6]); - EXPECT_NE(value_names_[3], value_names_[7]); -} - -TEST_F(LocalValueNumberingTest, ConstString) { - static const MIRDef mirs[] = { - DEF_CONST_STRING(Instruction::CONST_STRING, 0u, 0u), - DEF_CONST_STRING(Instruction::CONST_STRING, 1u, 0u), - DEF_CONST_STRING(Instruction::CONST_STRING, 2u, 2u), - DEF_CONST_STRING(Instruction::CONST_STRING, 3u, 0u), - DEF_INVOKE1(Instruction::INVOKE_DIRECT, 2u), - DEF_CONST_STRING(Instruction::CONST_STRING, 4u, 2u), - }; - - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 6u); - EXPECT_EQ(value_names_[1], value_names_[0]); - EXPECT_NE(value_names_[2], value_names_[0]); - EXPECT_EQ(value_names_[3], value_names_[0]); - EXPECT_EQ(value_names_[5], value_names_[2]); -} - -TEST_F(LocalValueNumberingTest, SameValueInDifferentMemoryLocations) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - }; - static const SFieldDef sfields[] = { - { 3u, 1u, 3u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(Instruction::NEW_ARRAY, 201u), - DEF_IGET(Instruction::IGET, 0u, 100u, 0u), - DEF_IPUT(Instruction::IPUT, 0u, 100u, 1u), - DEF_IPUT(Instruction::IPUT, 0u, 101u, 1u), - DEF_APUT(Instruction::APUT, 0u, 200u, 300u), - DEF_APUT(Instruction::APUT, 0u, 200u, 301u), - DEF_APUT(Instruction::APUT, 0u, 201u, 300u), - DEF_APUT(Instruction::APUT, 0u, 201u, 301u), - DEF_SPUT(Instruction::SPUT, 0u, 0u), - DEF_IGET(Instruction::IGET, 9u, 100u, 0u), - DEF_IGET(Instruction::IGET, 10u, 100u, 1u), - DEF_IGET(Instruction::IGET, 11u, 101u, 1u), - DEF_AGET(Instruction::AGET, 12u, 200u, 300u), - DEF_AGET(Instruction::AGET, 13u, 200u, 301u), - DEF_AGET(Instruction::AGET, 14u, 201u, 300u), - DEF_AGET(Instruction::AGET, 15u, 201u, 301u), - DEF_SGET(Instruction::SGET, 16u, 0u), - }; - - PrepareIFields(ifields); - PrepareSFields(sfields); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 17u); - for (size_t i = 9; i != arraysize(mirs); ++i) { - EXPECT_EQ(value_names_[1], value_names_[i]) << i; - } - for (size_t i = 0; i != arraysize(mirs); ++i) { - int expected_flags = - ((i == 2u || (i >= 5u && i <= 7u) || (i >= 9u && i <= 15u)) ? MIR_IGNORE_NULL_CHECK : 0) | - ((i >= 12u && i <= 15u) ? MIR_IGNORE_RANGE_CHECK : 0); - EXPECT_EQ(expected_flags, mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, UniqueArrayAliasing) { - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(Instruction::NEW_ARRAY, 20u), - DEF_AGET(Instruction::AGET, 1u, 20u, 40u), - DEF_APUT(Instruction::APUT, 2u, 20u, 41u), // May alias with index for sreg 40u. - DEF_AGET(Instruction::AGET, 3u, 20u, 40u), - }; - - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 4u); - EXPECT_NE(value_names_[1], value_names_[3]); - for (size_t i = 0; i != arraysize(mirs); ++i) { - int expected_flags = - ((i >= 1u) ? MIR_IGNORE_NULL_CHECK : 0) | - ((i == 3u) ? MIR_IGNORE_RANGE_CHECK : 0); - EXPECT_EQ(expected_flags, mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, EscapingRefs) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, // Field #1. - { 2u, 1u, 2u, false, kDexMemAccessWord }, // Field #2. - { 3u, 1u, 3u, false, kDexMemAccessObject }, // For storing escaping refs. - { 4u, 1u, 4u, false, kDexMemAccessWide }, // Wide. - { 5u, 0u, 0u, false, kDexMemAccessWord }, // Unresolved field, int. - { 6u, 0u, 0u, false, kDexMemAccessWide }, // Unresolved field, wide. - }; - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 20u), - DEF_IGET(Instruction::IGET, 1u, 20u, 0u), - DEF_IGET(Instruction::IGET, 2u, 20u, 1u), - DEF_IPUT(Instruction::IPUT_OBJECT, 20u, 30u, 2u), // Ref escapes. - DEF_IGET(Instruction::IGET, 4u, 20u, 0u), - DEF_IGET(Instruction::IGET, 5u, 20u, 1u), - DEF_IPUT(Instruction::IPUT, 6u, 31u, 0u), // May alias with field #1. - DEF_IGET(Instruction::IGET, 7u, 20u, 0u), // New value. - DEF_IGET(Instruction::IGET, 8u, 20u, 1u), // Still the same. - DEF_IPUT_WIDE(Instruction::IPUT_WIDE, 9u, 31u, 3u), // No aliasing, different type. - DEF_IGET(Instruction::IGET, 11u, 20u, 0u), - DEF_IGET(Instruction::IGET, 12u, 20u, 1u), - DEF_IPUT_WIDE(Instruction::IPUT_WIDE, 13u, 31u, 5u), // No aliasing, different type. - DEF_IGET(Instruction::IGET, 15u, 20u, 0u), - DEF_IGET(Instruction::IGET, 16u, 20u, 1u), - DEF_IPUT(Instruction::IPUT, 17u, 31u, 4u), // Aliasing, same type. - DEF_IGET(Instruction::IGET, 18u, 20u, 0u), - DEF_IGET(Instruction::IGET, 19u, 20u, 1u), - }; - - PrepareIFields(ifields); - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 9, 13 }; - MarkAsWideSRegs(wide_sregs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 18u); - EXPECT_EQ(value_names_[1], value_names_[4]); - EXPECT_EQ(value_names_[2], value_names_[5]); - EXPECT_NE(value_names_[4], value_names_[7]); // New value. - EXPECT_EQ(value_names_[5], value_names_[8]); - EXPECT_EQ(value_names_[7], value_names_[10]); - EXPECT_EQ(value_names_[8], value_names_[11]); - EXPECT_EQ(value_names_[10], value_names_[13]); - EXPECT_EQ(value_names_[11], value_names_[14]); - EXPECT_NE(value_names_[13], value_names_[16]); // New value. - EXPECT_NE(value_names_[14], value_names_[17]); // New value. - for (size_t i = 0u; i != mir_count_; ++i) { - int expected = - ((i != 0u && i != 3u && i != 6u) ? MIR_IGNORE_NULL_CHECK : 0) | - ((i == 3u) ? MIR_STORE_NON_NULL_VALUE: 0); - EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, EscapingArrayRefs) { - static const MIRDef mirs[] = { - DEF_UNIQUE_REF(Instruction::NEW_ARRAY, 20u), - DEF_AGET(Instruction::AGET, 1u, 20u, 40u), - DEF_AGET(Instruction::AGET, 2u, 20u, 41u), - DEF_APUT(Instruction::APUT_OBJECT, 20u, 30u, 42u), // Array ref escapes. - DEF_AGET(Instruction::AGET, 4u, 20u, 40u), - DEF_AGET(Instruction::AGET, 5u, 20u, 41u), - DEF_APUT_WIDE(Instruction::APUT_WIDE, 6u, 31u, 43u), // No aliasing, different type. - DEF_AGET(Instruction::AGET, 8u, 20u, 40u), - DEF_AGET(Instruction::AGET, 9u, 20u, 41u), - DEF_APUT(Instruction::APUT, 10u, 32u, 40u), // May alias with all elements. - DEF_AGET(Instruction::AGET, 11u, 20u, 40u), // New value (same index name). - DEF_AGET(Instruction::AGET, 12u, 20u, 41u), // New value (different index name). - }; - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 6 }; - MarkAsWideSRegs(wide_sregs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 12u); - EXPECT_EQ(value_names_[1], value_names_[4]); - EXPECT_EQ(value_names_[2], value_names_[5]); - EXPECT_EQ(value_names_[4], value_names_[7]); - EXPECT_EQ(value_names_[5], value_names_[8]); - EXPECT_NE(value_names_[7], value_names_[10]); // New value. - EXPECT_NE(value_names_[8], value_names_[11]); // New value. - for (size_t i = 0u; i != mir_count_; ++i) { - int expected = - ((i != 0u && i != 3u && i != 6u && i != 9u) ? MIR_IGNORE_NULL_CHECK : 0u) | - ((i >= 4 && i != 6u && i != 9u) ? MIR_IGNORE_RANGE_CHECK : 0u) | - ((i == 3u) ? MIR_STORE_NON_NULL_VALUE: 0); - EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, StoringSameValueKeepsMemoryVersion) { - static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false, kDexMemAccessWord }, - { 2u, 1u, 2u, false, kDexMemAccessWord }, - }; - static const SFieldDef sfields[] = { - { 2u, 1u, 2u, false, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_IGET(Instruction::IGET, 0u, 30u, 0u), - DEF_IGET(Instruction::IGET, 1u, 31u, 0u), - DEF_IPUT(Instruction::IPUT, 1u, 31u, 0u), // Store the same value. - DEF_IGET(Instruction::IGET, 3u, 30u, 0u), - DEF_AGET(Instruction::AGET, 4u, 32u, 40u), - DEF_AGET(Instruction::AGET, 5u, 33u, 40u), - DEF_APUT(Instruction::APUT, 5u, 33u, 40u), // Store the same value. - DEF_AGET(Instruction::AGET, 7u, 32u, 40u), - DEF_SGET(Instruction::SGET, 8u, 0u), - DEF_SPUT(Instruction::SPUT, 8u, 0u), // Store the same value. - DEF_SGET(Instruction::SGET, 10u, 0u), - DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 50u), // Test with unique references. - { Instruction::FILLED_NEW_ARRAY, 0, 0u, 2, { 12u, 13u }, 0, { } }, - DEF_UNIQUE_REF(Instruction::MOVE_RESULT_OBJECT, 51u), - DEF_IGET(Instruction::IGET, 14u, 50u, 0u), - DEF_IGET(Instruction::IGET, 15u, 50u, 1u), - DEF_IPUT(Instruction::IPUT, 15u, 50u, 1u), // Store the same value. - DEF_IGET(Instruction::IGET, 17u, 50u, 0u), - DEF_AGET(Instruction::AGET, 18u, 51u, 40u), - DEF_AGET(Instruction::AGET, 19u, 51u, 41u), - DEF_APUT(Instruction::APUT, 19u, 51u, 41u), // Store the same value. - DEF_AGET(Instruction::AGET, 21u, 51u, 40u), - }; - - PrepareIFields(ifields); - PrepareSFields(sfields); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 22u); - EXPECT_NE(value_names_[0], value_names_[1]); - EXPECT_EQ(value_names_[0], value_names_[3]); - EXPECT_NE(value_names_[4], value_names_[5]); - EXPECT_EQ(value_names_[4], value_names_[7]); - EXPECT_EQ(value_names_[8], value_names_[10]); - EXPECT_NE(value_names_[14], value_names_[15]); - EXPECT_EQ(value_names_[14], value_names_[17]); - EXPECT_NE(value_names_[18], value_names_[19]); - EXPECT_EQ(value_names_[18], value_names_[21]); - for (size_t i = 0u; i != mir_count_; ++i) { - int expected = - ((i == 2u || i == 3u || i == 6u || i == 7u || (i >= 14u)) ? MIR_IGNORE_NULL_CHECK : 0u) | - ((i == 6u || i == 7u || i >= 20u) ? MIR_IGNORE_RANGE_CHECK : 0u); - EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, FilledNewArrayTracking) { - if (!kLocalValueNumberingEnableFilledNewArrayTracking) { - // Feature disabled. - return; - } - static const MIRDef mirs[] = { - DEF_CONST(Instruction::CONST, 0u, 100), - DEF_CONST(Instruction::CONST, 1u, 200), - { Instruction::FILLED_NEW_ARRAY, 0, 0u, 2, { 0u, 1u }, 0, { } }, - DEF_UNIQUE_REF(Instruction::MOVE_RESULT_OBJECT, 10u), - DEF_CONST(Instruction::CONST, 20u, 0), - DEF_CONST(Instruction::CONST, 21u, 1), - DEF_AGET(Instruction::AGET, 6u, 10u, 20u), - DEF_AGET(Instruction::AGET, 7u, 10u, 21u), - }; - - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 8u); - EXPECT_EQ(value_names_[0], value_names_[6]); - EXPECT_EQ(value_names_[1], value_names_[7]); - for (size_t i = 0u; i != mir_count_; ++i) { - int expected = (i == 6u || i == 7u) ? (MIR_IGNORE_NULL_CHECK | MIR_IGNORE_RANGE_CHECK) : 0u; - EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; - } -} - -TEST_F(LocalValueNumberingTest, ClInitOnSget) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false, kDexMemAccessObject }, - { 1u, 2u, 1u, false, kDexMemAccessObject }, - }; - static const MIRDef mirs[] = { - DEF_SGET(Instruction::SGET_OBJECT, 0u, 0u), - DEF_AGET(Instruction::AGET, 1u, 0u, 100u), - DEF_SGET(Instruction::SGET_OBJECT, 2u, 1u), - DEF_SGET(Instruction::SGET_OBJECT, 3u, 0u), - DEF_AGET(Instruction::AGET, 4u, 3u, 100u), - }; - - PrepareSFields(sfields); - MakeSFieldUninitialized(1u); - PrepareMIRs(mirs); - PerformLVN(); - ASSERT_EQ(value_names_.size(), 5u); - EXPECT_NE(value_names_[0], value_names_[3]); -} - -TEST_F(LocalValueNumberingTest, DivZeroCheck) { - static const MIRDef mirs[] = { - DEF_DIV_REM(Instruction::DIV_INT, 1u, 10u, 20u), - DEF_DIV_REM(Instruction::DIV_INT, 2u, 20u, 20u), - DEF_DIV_REM(Instruction::DIV_INT_2ADDR, 3u, 10u, 1u), - DEF_DIV_REM(Instruction::REM_INT, 4u, 30u, 20u), - DEF_DIV_REM_WIDE(Instruction::REM_LONG, 5u, 12u, 14u), - DEF_DIV_REM_WIDE(Instruction::DIV_LONG_2ADDR, 7u, 16u, 14u), - }; - - static const bool expected_ignore_div_zero_check[] = { - false, true, false, true, false, true, - }; - - PrepareMIRs(mirs); - static const int32_t wide_sregs[] = { 5, 7, 12, 14, 16 }; - MarkAsWideSRegs(wide_sregs); - PerformLVN(); - for (size_t i = 0u; i != mir_count_; ++i) { - int expected = expected_ignore_div_zero_check[i] ? MIR_IGNORE_DIV_ZERO_CHECK : 0u; - EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; - } -} - -static constexpr int64_t shift_minus_1(size_t by) { - return static_cast(static_cast(INT64_C(-1)) << by); -} - -TEST_F(LocalValueNumberingTest, ConstWide) { - static const MIRDef mirs[] = { - // Core reg constants. - DEF_CONST(Instruction::CONST_WIDE_16, 0u, 0), - DEF_CONST(Instruction::CONST_WIDE_16, 2u, 1), - DEF_CONST(Instruction::CONST_WIDE_16, 4u, -1), - DEF_CONST(Instruction::CONST_WIDE_32, 6u, 1 << 16), - DEF_CONST(Instruction::CONST_WIDE_32, 8u, shift_minus_1(16)), - DEF_CONST(Instruction::CONST_WIDE_32, 10u, (1 << 16) + 1), - DEF_CONST(Instruction::CONST_WIDE_32, 12u, (1 << 16) - 1), - DEF_CONST(Instruction::CONST_WIDE_32, 14u, -(1 << 16) + 1), - DEF_CONST(Instruction::CONST_WIDE_32, 16u, -(1 << 16) - 1), - DEF_CONST(Instruction::CONST_WIDE, 18u, INT64_C(1) << 32), - DEF_CONST(Instruction::CONST_WIDE, 20u, shift_minus_1(32)), - DEF_CONST(Instruction::CONST_WIDE, 22u, (INT64_C(1) << 32) + 1), - DEF_CONST(Instruction::CONST_WIDE, 24u, (INT64_C(1) << 32) - 1), - DEF_CONST(Instruction::CONST_WIDE, 26u, shift_minus_1(32) + 1), - DEF_CONST(Instruction::CONST_WIDE, 28u, shift_minus_1(32) - 1), - DEF_CONST(Instruction::CONST_WIDE_HIGH16, 30u, 1), // Effectively 1 << 48. - DEF_CONST(Instruction::CONST_WIDE_HIGH16, 32u, 0xffff), // Effectively -1 << 48. - DEF_CONST(Instruction::CONST_WIDE, 34u, (INT64_C(1) << 48) + 1), - DEF_CONST(Instruction::CONST_WIDE, 36u, (INT64_C(1) << 48) - 1), - DEF_CONST(Instruction::CONST_WIDE, 38u, shift_minus_1(48) + 1), - DEF_CONST(Instruction::CONST_WIDE, 40u, shift_minus_1(48) - 1), - // FP reg constants. - DEF_CONST(Instruction::CONST_WIDE_16, 42u, 0), - DEF_CONST(Instruction::CONST_WIDE_16, 44u, 1), - DEF_CONST(Instruction::CONST_WIDE_16, 46u, -1), - DEF_CONST(Instruction::CONST_WIDE_32, 48u, 1 << 16), - DEF_CONST(Instruction::CONST_WIDE_32, 50u, shift_minus_1(16)), - DEF_CONST(Instruction::CONST_WIDE_32, 52u, (1 << 16) + 1), - DEF_CONST(Instruction::CONST_WIDE_32, 54u, (1 << 16) - 1), - DEF_CONST(Instruction::CONST_WIDE_32, 56u, -(1 << 16) + 1), - DEF_CONST(Instruction::CONST_WIDE_32, 58u, -(1 << 16) - 1), - DEF_CONST(Instruction::CONST_WIDE, 60u, INT64_C(1) << 32), - DEF_CONST(Instruction::CONST_WIDE, 62u, shift_minus_1(32)), - DEF_CONST(Instruction::CONST_WIDE, 64u, (INT64_C(1) << 32) + 1), - DEF_CONST(Instruction::CONST_WIDE, 66u, (INT64_C(1) << 32) - 1), - DEF_CONST(Instruction::CONST_WIDE, 68u, shift_minus_1(32) + 1), - DEF_CONST(Instruction::CONST_WIDE, 70u, shift_minus_1(32) - 1), - DEF_CONST(Instruction::CONST_WIDE_HIGH16, 72u, 1), // Effectively 1 << 48. - DEF_CONST(Instruction::CONST_WIDE_HIGH16, 74u, 0xffff), // Effectively -1 << 48. - DEF_CONST(Instruction::CONST_WIDE, 76u, (INT64_C(1) << 48) + 1), - DEF_CONST(Instruction::CONST_WIDE, 78u, (INT64_C(1) << 48) - 1), - DEF_CONST(Instruction::CONST_WIDE, 80u, shift_minus_1(48) + 1), - DEF_CONST(Instruction::CONST_WIDE, 82u, shift_minus_1(48) - 1), - }; - - PrepareMIRs(mirs); - for (size_t i = 0; i != arraysize(mirs); ++i) { - const int32_t wide_sregs[] = { mirs_[i].ssa_rep->defs[0] }; - MarkAsWideSRegs(wide_sregs); - } - for (size_t i = arraysize(mirs) / 2u; i != arraysize(mirs); ++i) { - cu_.mir_graph->reg_location_[mirs_[i].ssa_rep->defs[0]].fp = true; - } - PerformLVN(); - for (size_t i = 0u; i != mir_count_; ++i) { - for (size_t j = i + 1u; j != mir_count_; ++j) { - EXPECT_NE(value_names_[i], value_names_[j]) << i << " " << j; - } - } -} - -TEST_F(LocalValueNumberingTest, Const) { - static const MIRDef mirs[] = { - // Core reg constants. - DEF_CONST(Instruction::CONST_4, 0u, 0), - DEF_CONST(Instruction::CONST_4, 1u, 1), - DEF_CONST(Instruction::CONST_4, 2u, -1), - DEF_CONST(Instruction::CONST_16, 3u, 1 << 4), - DEF_CONST(Instruction::CONST_16, 4u, shift_minus_1(4)), - DEF_CONST(Instruction::CONST_16, 5u, (1 << 4) + 1), - DEF_CONST(Instruction::CONST_16, 6u, (1 << 4) - 1), - DEF_CONST(Instruction::CONST_16, 7u, -(1 << 4) + 1), - DEF_CONST(Instruction::CONST_16, 8u, -(1 << 4) - 1), - DEF_CONST(Instruction::CONST_HIGH16, 9u, 1), // Effectively 1 << 16. - DEF_CONST(Instruction::CONST_HIGH16, 10u, 0xffff), // Effectively -1 << 16. - DEF_CONST(Instruction::CONST, 11u, (1 << 16) + 1), - DEF_CONST(Instruction::CONST, 12u, (1 << 16) - 1), - DEF_CONST(Instruction::CONST, 13u, shift_minus_1(16) + 1), - DEF_CONST(Instruction::CONST, 14u, shift_minus_1(16) - 1), - // FP reg constants. - DEF_CONST(Instruction::CONST_4, 15u, 0), - DEF_CONST(Instruction::CONST_4, 16u, 1), - DEF_CONST(Instruction::CONST_4, 17u, -1), - DEF_CONST(Instruction::CONST_16, 18u, 1 << 4), - DEF_CONST(Instruction::CONST_16, 19u, shift_minus_1(4)), - DEF_CONST(Instruction::CONST_16, 20u, (1 << 4) + 1), - DEF_CONST(Instruction::CONST_16, 21u, (1 << 4) - 1), - DEF_CONST(Instruction::CONST_16, 22u, -(1 << 4) + 1), - DEF_CONST(Instruction::CONST_16, 23u, -(1 << 4) - 1), - DEF_CONST(Instruction::CONST_HIGH16, 24u, 1), // Effectively 1 << 16. - DEF_CONST(Instruction::CONST_HIGH16, 25u, 0xffff), // Effectively -1 << 16. - DEF_CONST(Instruction::CONST, 26u, (1 << 16) + 1), - DEF_CONST(Instruction::CONST, 27u, (1 << 16) - 1), - DEF_CONST(Instruction::CONST, 28u, shift_minus_1(16) + 1), - DEF_CONST(Instruction::CONST, 29u, shift_minus_1(16) - 1), - // null reference constant. - DEF_CONST(Instruction::CONST_4, 30u, 0), - }; - - PrepareMIRs(mirs); - static_assert((arraysize(mirs) & 1) != 0, "missing null or unmatched fp/core"); - cu_.mir_graph->reg_location_[arraysize(mirs) - 1].ref = true; - for (size_t i = arraysize(mirs) / 2u; i != arraysize(mirs) - 1; ++i) { - cu_.mir_graph->reg_location_[mirs_[i].ssa_rep->defs[0]].fp = true; - } - PerformLVN(); - for (size_t i = 0u; i != mir_count_; ++i) { - for (size_t j = i + 1u; j != mir_count_; ++j) { - EXPECT_NE(value_names_[i], value_names_[j]) << i << " " << j; - } - } -} - -} // namespace art diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc deleted file mode 100644 index 18ce563fc2..0000000000 --- a/compiler/dex/mir_analysis.cc +++ /dev/null @@ -1,1433 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "base/logging.h" -#include "base/scoped_arena_containers.h" -#include "dataflow_iterator-inl.h" -#include "compiler_ir.h" -#include "dex_flags.h" -#include "dex_instruction-inl.h" -#include "dex/mir_field_info.h" -#include "dex/verified_method.h" -#include "dex/quick/dex_file_method_inliner.h" -#include "dex/quick/dex_file_to_method_inliner_map.h" -#include "driver/compiler_driver.h" -#include "driver/compiler_options.h" -#include "driver/dex_compilation_unit.h" -#include "scoped_thread_state_change.h" -#include "utils.h" - -namespace art { - -enum InstructionAnalysisAttributeOps : uint8_t { - kUninterestingOp = 0, - kArithmeticOp, - kFpOp, - kSingleOp, - kDoubleOp, - kIntOp, - kLongOp, - kBranchOp, - kInvokeOp, - kArrayOp, - kHeavyweightOp, - kSimpleConstOp, - kMoveOp, - kSwitch -}; - -enum InstructionAnalysisAttributeMasks : uint16_t { - kAnNone = 1 << kUninterestingOp, - kAnMath = 1 << kArithmeticOp, - kAnFp = 1 << kFpOp, - kAnLong = 1 << kLongOp, - kAnInt = 1 << kIntOp, - kAnSingle = 1 << kSingleOp, - kAnDouble = 1 << kDoubleOp, - kAnFloatMath = 1 << kFpOp, - kAnBranch = 1 << kBranchOp, - kAnInvoke = 1 << kInvokeOp, - kAnArrayOp = 1 << kArrayOp, - kAnHeavyWeight = 1 << kHeavyweightOp, - kAnSimpleConst = 1 << kSimpleConstOp, - kAnMove = 1 << kMoveOp, - kAnSwitch = 1 << kSwitch, - kAnComputational = kAnMath | kAnArrayOp | kAnMove | kAnSimpleConst, -}; - -// Instruction characteristics used to statically identify computation-intensive methods. -static const uint16_t kAnalysisAttributes[kMirOpLast] = { - // 00 NOP - kAnNone, - - // 01 MOVE vA, vB - kAnMove, - - // 02 MOVE_FROM16 vAA, vBBBB - kAnMove, - - // 03 MOVE_16 vAAAA, vBBBB - kAnMove, - - // 04 MOVE_WIDE vA, vB - kAnMove, - - // 05 MOVE_WIDE_FROM16 vAA, vBBBB - kAnMove, - - // 06 MOVE_WIDE_16 vAAAA, vBBBB - kAnMove, - - // 07 MOVE_OBJECT vA, vB - kAnMove, - - // 08 MOVE_OBJECT_FROM16 vAA, vBBBB - kAnMove, - - // 09 MOVE_OBJECT_16 vAAAA, vBBBB - kAnMove, - - // 0A MOVE_RESULT vAA - kAnMove, - - // 0B MOVE_RESULT_WIDE vAA - kAnMove, - - // 0C MOVE_RESULT_OBJECT vAA - kAnMove, - - // 0D MOVE_EXCEPTION vAA - kAnMove, - - // 0E RETURN_VOID - kAnBranch, - - // 0F RETURN vAA - kAnBranch, - - // 10 RETURN_WIDE vAA - kAnBranch, - - // 11 RETURN_OBJECT vAA - kAnBranch, - - // 12 CONST_4 vA, #+B - kAnSimpleConst, - - // 13 CONST_16 vAA, #+BBBB - kAnSimpleConst, - - // 14 CONST vAA, #+BBBBBBBB - kAnSimpleConst, - - // 15 CONST_HIGH16 VAA, #+BBBB0000 - kAnSimpleConst, - - // 16 CONST_WIDE_16 vAA, #+BBBB - kAnSimpleConst, - - // 17 CONST_WIDE_32 vAA, #+BBBBBBBB - kAnSimpleConst, - - // 18 CONST_WIDE vAA, #+BBBBBBBBBBBBBBBB - kAnSimpleConst, - - // 19 CONST_WIDE_HIGH16 vAA, #+BBBB000000000000 - kAnSimpleConst, - - // 1A CONST_STRING vAA, string@BBBB - kAnNone, - - // 1B CONST_STRING_JUMBO vAA, string@BBBBBBBB - kAnNone, - - // 1C CONST_CLASS vAA, type@BBBB - kAnNone, - - // 1D MONITOR_ENTER vAA - kAnNone, - - // 1E MONITOR_EXIT vAA - kAnNone, - - // 1F CHK_CAST vAA, type@BBBB - kAnNone, - - // 20 INSTANCE_OF vA, vB, type@CCCC - kAnNone, - - // 21 ARRAY_LENGTH vA, vB - kAnArrayOp, - - // 22 NEW_INSTANCE vAA, type@BBBB - kAnHeavyWeight, - - // 23 NEW_ARRAY vA, vB, type@CCCC - kAnHeavyWeight, - - // 24 FILLED_NEW_ARRAY {vD, vE, vF, vG, vA} - kAnHeavyWeight, - - // 25 FILLED_NEW_ARRAY_RANGE {vCCCC .. vNNNN}, type@BBBB - kAnHeavyWeight, - - // 26 FILL_ARRAY_DATA vAA, +BBBBBBBB - kAnNone, - - // 27 THROW vAA - kAnHeavyWeight | kAnBranch, - - // 28 GOTO - kAnBranch, - - // 29 GOTO_16 - kAnBranch, - - // 2A GOTO_32 - kAnBranch, - - // 2B PACKED_SWITCH vAA, +BBBBBBBB - kAnSwitch, - - // 2C SPARSE_SWITCH vAA, +BBBBBBBB - kAnSwitch, - - // 2D CMPL_FLOAT vAA, vBB, vCC - kAnMath | kAnFp | kAnSingle, - - // 2E CMPG_FLOAT vAA, vBB, vCC - kAnMath | kAnFp | kAnSingle, - - // 2F CMPL_DOUBLE vAA, vBB, vCC - kAnMath | kAnFp | kAnDouble, - - // 30 CMPG_DOUBLE vAA, vBB, vCC - kAnMath | kAnFp | kAnDouble, - - // 31 CMP_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // 32 IF_EQ vA, vB, +CCCC - kAnMath | kAnBranch | kAnInt, - - // 33 IF_NE vA, vB, +CCCC - kAnMath | kAnBranch | kAnInt, - - // 34 IF_LT vA, vB, +CCCC - kAnMath | kAnBranch | kAnInt, - - // 35 IF_GE vA, vB, +CCCC - kAnMath | kAnBranch | kAnInt, - - // 36 IF_GT vA, vB, +CCCC - kAnMath | kAnBranch | kAnInt, - - // 37 IF_LE vA, vB, +CCCC - kAnMath | kAnBranch | kAnInt, - - // 38 IF_EQZ vAA, +BBBB - kAnMath | kAnBranch | kAnInt, - - // 39 IF_NEZ vAA, +BBBB - kAnMath | kAnBranch | kAnInt, - - // 3A IF_LTZ vAA, +BBBB - kAnMath | kAnBranch | kAnInt, - - // 3B IF_GEZ vAA, +BBBB - kAnMath | kAnBranch | kAnInt, - - // 3C IF_GTZ vAA, +BBBB - kAnMath | kAnBranch | kAnInt, - - // 3D IF_LEZ vAA, +BBBB - kAnMath | kAnBranch | kAnInt, - - // 3E UNUSED_3E - kAnNone, - - // 3F UNUSED_3F - kAnNone, - - // 40 UNUSED_40 - kAnNone, - - // 41 UNUSED_41 - kAnNone, - - // 42 UNUSED_42 - kAnNone, - - // 43 UNUSED_43 - kAnNone, - - // 44 AGET vAA, vBB, vCC - kAnArrayOp, - - // 45 AGET_WIDE vAA, vBB, vCC - kAnArrayOp, - - // 46 AGET_OBJECT vAA, vBB, vCC - kAnArrayOp, - - // 47 AGET_BOOLEAN vAA, vBB, vCC - kAnArrayOp, - - // 48 AGET_BYTE vAA, vBB, vCC - kAnArrayOp, - - // 49 AGET_CHAR vAA, vBB, vCC - kAnArrayOp, - - // 4A AGET_SHORT vAA, vBB, vCC - kAnArrayOp, - - // 4B APUT vAA, vBB, vCC - kAnArrayOp, - - // 4C APUT_WIDE vAA, vBB, vCC - kAnArrayOp, - - // 4D APUT_OBJECT vAA, vBB, vCC - kAnArrayOp, - - // 4E APUT_BOOLEAN vAA, vBB, vCC - kAnArrayOp, - - // 4F APUT_BYTE vAA, vBB, vCC - kAnArrayOp, - - // 50 APUT_CHAR vAA, vBB, vCC - kAnArrayOp, - - // 51 APUT_SHORT vAA, vBB, vCC - kAnArrayOp, - - // 52 IGET vA, vB, field@CCCC - kAnNone, - - // 53 IGET_WIDE vA, vB, field@CCCC - kAnNone, - - // 54 IGET_OBJECT vA, vB, field@CCCC - kAnNone, - - // 55 IGET_BOOLEAN vA, vB, field@CCCC - kAnNone, - - // 56 IGET_BYTE vA, vB, field@CCCC - kAnNone, - - // 57 IGET_CHAR vA, vB, field@CCCC - kAnNone, - - // 58 IGET_SHORT vA, vB, field@CCCC - kAnNone, - - // 59 IPUT vA, vB, field@CCCC - kAnNone, - - // 5A IPUT_WIDE vA, vB, field@CCCC - kAnNone, - - // 5B IPUT_OBJECT vA, vB, field@CCCC - kAnNone, - - // 5C IPUT_BOOLEAN vA, vB, field@CCCC - kAnNone, - - // 5D IPUT_BYTE vA, vB, field@CCCC - kAnNone, - - // 5E IPUT_CHAR vA, vB, field@CCCC - kAnNone, - - // 5F IPUT_SHORT vA, vB, field@CCCC - kAnNone, - - // 60 SGET vAA, field@BBBB - kAnNone, - - // 61 SGET_WIDE vAA, field@BBBB - kAnNone, - - // 62 SGET_OBJECT vAA, field@BBBB - kAnNone, - - // 63 SGET_BOOLEAN vAA, field@BBBB - kAnNone, - - // 64 SGET_BYTE vAA, field@BBBB - kAnNone, - - // 65 SGET_CHAR vAA, field@BBBB - kAnNone, - - // 66 SGET_SHORT vAA, field@BBBB - kAnNone, - - // 67 SPUT vAA, field@BBBB - kAnNone, - - // 68 SPUT_WIDE vAA, field@BBBB - kAnNone, - - // 69 SPUT_OBJECT vAA, field@BBBB - kAnNone, - - // 6A SPUT_BOOLEAN vAA, field@BBBB - kAnNone, - - // 6B SPUT_BYTE vAA, field@BBBB - kAnNone, - - // 6C SPUT_CHAR vAA, field@BBBB - kAnNone, - - // 6D SPUT_SHORT vAA, field@BBBB - kAnNone, - - // 6E INVOKE_VIRTUAL {vD, vE, vF, vG, vA} - kAnInvoke | kAnHeavyWeight, - - // 6F INVOKE_SUPER {vD, vE, vF, vG, vA} - kAnInvoke | kAnHeavyWeight, - - // 70 INVOKE_DIRECT {vD, vE, vF, vG, vA} - kAnInvoke | kAnHeavyWeight, - - // 71 INVOKE_STATIC {vD, vE, vF, vG, vA} - kAnInvoke | kAnHeavyWeight, - - // 72 INVOKE_INTERFACE {vD, vE, vF, vG, vA} - kAnInvoke | kAnHeavyWeight, - - // 73 RETURN_VOID_NO_BARRIER - kAnBranch, - - // 74 INVOKE_VIRTUAL_RANGE {vCCCC .. vNNNN} - kAnInvoke | kAnHeavyWeight, - - // 75 INVOKE_SUPER_RANGE {vCCCC .. vNNNN} - kAnInvoke | kAnHeavyWeight, - - // 76 INVOKE_DIRECT_RANGE {vCCCC .. vNNNN} - kAnInvoke | kAnHeavyWeight, - - // 77 INVOKE_STATIC_RANGE {vCCCC .. vNNNN} - kAnInvoke | kAnHeavyWeight, - - // 78 INVOKE_INTERFACE_RANGE {vCCCC .. vNNNN} - kAnInvoke | kAnHeavyWeight, - - // 79 UNUSED_79 - kAnNone, - - // 7A UNUSED_7A - kAnNone, - - // 7B NEG_INT vA, vB - kAnMath | kAnInt, - - // 7C NOT_INT vA, vB - kAnMath | kAnInt, - - // 7D NEG_LONG vA, vB - kAnMath | kAnLong, - - // 7E NOT_LONG vA, vB - kAnMath | kAnLong, - - // 7F NEG_FLOAT vA, vB - kAnMath | kAnFp | kAnSingle, - - // 80 NEG_DOUBLE vA, vB - kAnMath | kAnFp | kAnDouble, - - // 81 INT_TO_LONG vA, vB - kAnMath | kAnInt | kAnLong, - - // 82 INT_TO_FLOAT vA, vB - kAnMath | kAnFp | kAnInt | kAnSingle, - - // 83 INT_TO_DOUBLE vA, vB - kAnMath | kAnFp | kAnInt | kAnDouble, - - // 84 LONG_TO_INT vA, vB - kAnMath | kAnInt | kAnLong, - - // 85 LONG_TO_FLOAT vA, vB - kAnMath | kAnFp | kAnLong | kAnSingle, - - // 86 LONG_TO_DOUBLE vA, vB - kAnMath | kAnFp | kAnLong | kAnDouble, - - // 87 FLOAT_TO_INT vA, vB - kAnMath | kAnFp | kAnInt | kAnSingle, - - // 88 FLOAT_TO_LONG vA, vB - kAnMath | kAnFp | kAnLong | kAnSingle, - - // 89 FLOAT_TO_DOUBLE vA, vB - kAnMath | kAnFp | kAnSingle | kAnDouble, - - // 8A DOUBLE_TO_INT vA, vB - kAnMath | kAnFp | kAnInt | kAnDouble, - - // 8B DOUBLE_TO_LONG vA, vB - kAnMath | kAnFp | kAnLong | kAnDouble, - - // 8C DOUBLE_TO_FLOAT vA, vB - kAnMath | kAnFp | kAnSingle | kAnDouble, - - // 8D INT_TO_BYTE vA, vB - kAnMath | kAnInt, - - // 8E INT_TO_CHAR vA, vB - kAnMath | kAnInt, - - // 8F INT_TO_SHORT vA, vB - kAnMath | kAnInt, - - // 90 ADD_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 91 SUB_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 92 MUL_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 93 DIV_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 94 REM_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 95 AND_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 96 OR_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 97 XOR_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 98 SHL_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 99 SHR_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 9A USHR_INT vAA, vBB, vCC - kAnMath | kAnInt, - - // 9B ADD_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // 9C SUB_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // 9D MUL_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // 9E DIV_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // 9F REM_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // A0 AND_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // A1 OR_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // A2 XOR_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // A3 SHL_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // A4 SHR_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // A5 USHR_LONG vAA, vBB, vCC - kAnMath | kAnLong, - - // A6 ADD_FLOAT vAA, vBB, vCC - kAnMath | kAnFp | kAnSingle, - - // A7 SUB_FLOAT vAA, vBB, vCC - kAnMath | kAnFp | kAnSingle, - - // A8 MUL_FLOAT vAA, vBB, vCC - kAnMath | kAnFp | kAnSingle, - - // A9 DIV_FLOAT vAA, vBB, vCC - kAnMath | kAnFp | kAnSingle, - - // AA REM_FLOAT vAA, vBB, vCC - kAnMath | kAnFp | kAnSingle, - - // AB ADD_DOUBLE vAA, vBB, vCC - kAnMath | kAnFp | kAnDouble, - - // AC SUB_DOUBLE vAA, vBB, vCC - kAnMath | kAnFp | kAnDouble, - - // AD MUL_DOUBLE vAA, vBB, vCC - kAnMath | kAnFp | kAnDouble, - - // AE DIV_DOUBLE vAA, vBB, vCC - kAnMath | kAnFp | kAnDouble, - - // AF REM_DOUBLE vAA, vBB, vCC - kAnMath | kAnFp | kAnDouble, - - // B0 ADD_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // B1 SUB_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // B2 MUL_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // B3 DIV_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // B4 REM_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // B5 AND_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // B6 OR_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // B7 XOR_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // B8 SHL_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // B9 SHR_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // BA USHR_INT_2ADDR vA, vB - kAnMath | kAnInt, - - // BB ADD_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // BC SUB_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // BD MUL_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // BE DIV_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // BF REM_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // C0 AND_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // C1 OR_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // C2 XOR_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // C3 SHL_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // C4 SHR_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // C5 USHR_LONG_2ADDR vA, vB - kAnMath | kAnLong, - - // C6 ADD_FLOAT_2ADDR vA, vB - kAnMath | kAnFp | kAnSingle, - - // C7 SUB_FLOAT_2ADDR vA, vB - kAnMath | kAnFp | kAnSingle, - - // C8 MUL_FLOAT_2ADDR vA, vB - kAnMath | kAnFp | kAnSingle, - - // C9 DIV_FLOAT_2ADDR vA, vB - kAnMath | kAnFp | kAnSingle, - - // CA REM_FLOAT_2ADDR vA, vB - kAnMath | kAnFp | kAnSingle, - - // CB ADD_DOUBLE_2ADDR vA, vB - kAnMath | kAnFp | kAnDouble, - - // CC SUB_DOUBLE_2ADDR vA, vB - kAnMath | kAnFp | kAnDouble, - - // CD MUL_DOUBLE_2ADDR vA, vB - kAnMath | kAnFp | kAnDouble, - - // CE DIV_DOUBLE_2ADDR vA, vB - kAnMath | kAnFp | kAnDouble, - - // CF REM_DOUBLE_2ADDR vA, vB - kAnMath | kAnFp | kAnDouble, - - // D0 ADD_INT_LIT16 vA, vB, #+CCCC - kAnMath | kAnInt, - - // D1 RSUB_INT vA, vB, #+CCCC - kAnMath | kAnInt, - - // D2 MUL_INT_LIT16 vA, vB, #+CCCC - kAnMath | kAnInt, - - // D3 DIV_INT_LIT16 vA, vB, #+CCCC - kAnMath | kAnInt, - - // D4 REM_INT_LIT16 vA, vB, #+CCCC - kAnMath | kAnInt, - - // D5 AND_INT_LIT16 vA, vB, #+CCCC - kAnMath | kAnInt, - - // D6 OR_INT_LIT16 vA, vB, #+CCCC - kAnMath | kAnInt, - - // D7 XOR_INT_LIT16 vA, vB, #+CCCC - kAnMath | kAnInt, - - // D8 ADD_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // D9 RSUB_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // DA MUL_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // DB DIV_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // DC REM_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // DD AND_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // DE OR_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // DF XOR_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // E0 SHL_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // E1 SHR_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // E2 USHR_INT_LIT8 vAA, vBB, #+CC - kAnMath | kAnInt, - - // E3 IGET_QUICK - kAnNone, - - // E4 IGET_WIDE_QUICK - kAnNone, - - // E5 IGET_OBJECT_QUICK - kAnNone, - - // E6 IPUT_QUICK - kAnNone, - - // E7 IPUT_WIDE_QUICK - kAnNone, - - // E8 IPUT_OBJECT_QUICK - kAnNone, - - // E9 INVOKE_VIRTUAL_QUICK - kAnInvoke | kAnHeavyWeight, - - // EA INVOKE_VIRTUAL_RANGE_QUICK - kAnInvoke | kAnHeavyWeight, - - // EB IPUT_BOOLEAN_QUICK - kAnNone, - - // EC IPUT_BYTE_QUICK - kAnNone, - - // ED IPUT_CHAR_QUICK - kAnNone, - - // EE IPUT_SHORT_QUICK - kAnNone, - - // EF IGET_BOOLEAN_QUICK - kAnNone, - - // F0 IGET_BYTE_QUICK - kAnNone, - - // F1 IGET_CHAR_QUICK - kAnNone, - - // F2 IGET_SHORT_QUICK - kAnNone, - - // F3 UNUSED_F3 - kAnNone, - - // F4 UNUSED_F4 - kAnNone, - - // F5 UNUSED_F5 - kAnNone, - - // F6 UNUSED_F6 - kAnNone, - - // F7 UNUSED_F7 - kAnNone, - - // F8 UNUSED_F8 - kAnNone, - - // F9 UNUSED_F9 - kAnNone, - - // FA UNUSED_FA - kAnNone, - - // FB UNUSED_FB - kAnNone, - - // FC UNUSED_FC - kAnNone, - - // FD UNUSED_FD - kAnNone, - - // FE UNUSED_FE - kAnNone, - - // FF UNUSED_FF - kAnNone, - - // Beginning of extended MIR opcodes - // 100 MIR_PHI - kAnNone, - - // 101 MIR_COPY - kAnNone, - - // 102 MIR_FUSED_CMPL_FLOAT - kAnNone, - - // 103 MIR_FUSED_CMPG_FLOAT - kAnNone, - - // 104 MIR_FUSED_CMPL_DOUBLE - kAnNone, - - // 105 MIR_FUSED_CMPG_DOUBLE - kAnNone, - - // 106 MIR_FUSED_CMP_LONG - kAnNone, - - // 107 MIR_NOP - kAnNone, - - // 108 MIR_NULL_CHECK - kAnNone, - - // 109 MIR_RANGE_CHECK - kAnNone, - - // 10A MIR_DIV_ZERO_CHECK - kAnNone, - - // 10B MIR_CHECK - kAnNone, - - // 10C MIR_CHECKPART2 - kAnNone, - - // 10D MIR_SELECT - kAnNone, - - // 10E MirOpConstVector - kAnNone, - - // 10F MirOpMoveVector - kAnNone, - - // 110 MirOpPackedMultiply - kAnNone, - - // 111 MirOpPackedAddition - kAnNone, - - // 112 MirOpPackedSubtract - kAnNone, - - // 113 MirOpPackedShiftLeft - kAnNone, - - // 114 MirOpPackedSignedShiftRight - kAnNone, - - // 115 MirOpPackedUnsignedShiftRight - kAnNone, - - // 116 MirOpPackedAnd - kAnNone, - - // 117 MirOpPackedOr - kAnNone, - - // 118 MirOpPackedXor - kAnNone, - - // 119 MirOpPackedAddReduce - kAnNone, - - // 11A MirOpPackedReduce - kAnNone, - - // 11B MirOpPackedSet - kAnNone, - - // 11C MirOpReserveVectorRegisters - kAnNone, - - // 11D MirOpReturnVectorRegisters - kAnNone, - - // 11E MirOpMemBarrier - kAnNone, - - // 11F MirOpPackedArrayGet - kAnArrayOp, - - // 120 MirOpPackedArrayPut - kAnArrayOp, -}; - -struct MethodStats { - int dex_instructions; - int math_ops; - int fp_ops; - int array_ops; - int branch_ops; - int heavyweight_ops; - bool has_computational_loop; - bool has_switch; - float math_ratio; - float fp_ratio; - float array_ratio; - float branch_ratio; - float heavyweight_ratio; -}; - -void MIRGraph::AnalyzeBlock(BasicBlock* bb, MethodStats* stats) { - if (bb->visited || (bb->block_type != kDalvikByteCode)) { - return; - } - bool computational_block = true; - bool has_math = false; - /* - * For the purposes of this scan, we want to treat the set of basic blocks broken - * by an exception edge as a single basic block. We'll scan forward along the fallthrough - * edges until we reach an explicit branch or return. - */ - BasicBlock* ending_bb = bb; - if (ending_bb->last_mir_insn != nullptr) { - uint32_t ending_flags = kAnalysisAttributes[ending_bb->last_mir_insn->dalvikInsn.opcode]; - while ((ending_flags & kAnBranch) == 0) { - ending_bb = GetBasicBlock(ending_bb->fall_through); - ending_flags = kAnalysisAttributes[ending_bb->last_mir_insn->dalvikInsn.opcode]; - } - } - /* - * Ideally, we'd weight the operations by loop nesting level, but to do so we'd - * first need to do some expensive loop detection - and the point of this is to make - * an informed guess before investing in computation. However, we can cheaply detect - * many simple loop forms without having to do full dataflow analysis. - */ - int loop_scale_factor = 1; - // Simple for and while loops - if ((ending_bb->taken != NullBasicBlockId) && (ending_bb->fall_through == NullBasicBlockId)) { - if ((GetBasicBlock(ending_bb->taken)->taken == bb->id) || - (GetBasicBlock(ending_bb->taken)->fall_through == bb->id)) { - loop_scale_factor = 25; - } - } - // Simple do-while loop - if ((ending_bb->taken != NullBasicBlockId) && (ending_bb->taken == bb->id)) { - loop_scale_factor = 25; - } - - BasicBlock* tbb = bb; - bool done = false; - while (!done) { - tbb->visited = true; - for (MIR* mir = tbb->first_mir_insn; mir != nullptr; mir = mir->next) { - if (MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode)) { - // Skip any MIR pseudo-op. - continue; - } - uint16_t flags = kAnalysisAttributes[mir->dalvikInsn.opcode]; - stats->dex_instructions += loop_scale_factor; - if ((flags & kAnBranch) == 0) { - computational_block &= ((flags & kAnComputational) != 0); - } else { - stats->branch_ops += loop_scale_factor; - } - if ((flags & kAnMath) != 0) { - stats->math_ops += loop_scale_factor; - has_math = true; - } - if ((flags & kAnFp) != 0) { - stats->fp_ops += loop_scale_factor; - } - if ((flags & kAnArrayOp) != 0) { - stats->array_ops += loop_scale_factor; - } - if ((flags & kAnHeavyWeight) != 0) { - stats->heavyweight_ops += loop_scale_factor; - } - if ((flags & kAnSwitch) != 0) { - stats->has_switch = true; - } - } - if (tbb == ending_bb) { - done = true; - } else { - tbb = GetBasicBlock(tbb->fall_through); - } - } - if (has_math && computational_block && (loop_scale_factor > 1)) { - stats->has_computational_loop = true; - } -} - -bool MIRGraph::ComputeSkipCompilation(MethodStats* stats, bool skip_default, - std::string* skip_message) { - float count = stats->dex_instructions; - stats->math_ratio = stats->math_ops / count; - stats->fp_ratio = stats->fp_ops / count; - stats->branch_ratio = stats->branch_ops / count; - stats->array_ratio = stats->array_ops / count; - stats->heavyweight_ratio = stats->heavyweight_ops / count; - - if (cu_->enable_debug & (1 << kDebugShowFilterStats)) { - LOG(INFO) << "STATS " << stats->dex_instructions << ", math:" - << stats->math_ratio << ", fp:" - << stats->fp_ratio << ", br:" - << stats->branch_ratio << ", hw:" - << stats->heavyweight_ratio << ", arr:" - << stats->array_ratio << ", hot:" - << stats->has_computational_loop << ", " - << PrettyMethod(cu_->method_idx, *cu_->dex_file); - } - - // Computation intensive? - if (stats->has_computational_loop && (stats->heavyweight_ratio < 0.04)) { - return false; - } - - // Complex, logic-intensive? - if (cu_->compiler_driver->GetCompilerOptions().IsSmallMethod(GetNumDalvikInsns()) && - stats->branch_ratio > 0.3) { - return false; - } - - // Significant floating point? - if (stats->fp_ratio > 0.05) { - return false; - } - - // Significant generic math? - if (stats->math_ratio > 0.3) { - return false; - } - - // If array-intensive, compiling is probably worthwhile. - if (stats->array_ratio > 0.1) { - return false; - } - - // Switch operations benefit greatly from compilation, so go ahead and spend the cycles. - if (stats->has_switch) { - return false; - } - - // If significant in size and high proportion of expensive operations, skip. - if (cu_->compiler_driver->GetCompilerOptions().IsSmallMethod(GetNumDalvikInsns()) && - (stats->heavyweight_ratio > 0.3)) { - *skip_message = "Is a small method with heavyweight ratio " + - std::to_string(stats->heavyweight_ratio); - return true; - } - - return skip_default; -} - - /* - * Will eventually want this to be a bit more sophisticated and happen at verification time. - */ -bool MIRGraph::SkipCompilation(std::string* skip_message) { - const CompilerOptions& compiler_options = cu_->compiler_driver->GetCompilerOptions(); - CompilerOptions::CompilerFilter compiler_filter = compiler_options.GetCompilerFilter(); - if (compiler_filter == CompilerOptions::kEverything) { - return false; - } - - // Contains a pattern we don't want to compile? - if (PuntToInterpreter()) { - *skip_message = "Punt to interpreter set"; - return true; - } - - DCHECK(compiler_options.IsCompilationEnabled()); - - // Set up compilation cutoffs based on current filter mode. - size_t small_cutoff; - size_t default_cutoff; - switch (compiler_filter) { - case CompilerOptions::kBalanced: - small_cutoff = compiler_options.GetSmallMethodThreshold(); - default_cutoff = compiler_options.GetLargeMethodThreshold(); - break; - case CompilerOptions::kSpace: - small_cutoff = compiler_options.GetTinyMethodThreshold(); - default_cutoff = compiler_options.GetSmallMethodThreshold(); - break; - case CompilerOptions::kSpeed: - case CompilerOptions::kTime: - small_cutoff = compiler_options.GetHugeMethodThreshold(); - default_cutoff = compiler_options.GetHugeMethodThreshold(); - break; - default: - LOG(FATAL) << "Unexpected compiler_filter_: " << compiler_filter; - UNREACHABLE(); - } - - // If size < cutoff, assume we'll compile - but allow removal. - bool skip_compilation = (GetNumDalvikInsns() >= default_cutoff); - if (skip_compilation) { - *skip_message = "#Insns >= default_cutoff: " + std::to_string(GetNumDalvikInsns()); - } - - /* - * Filter 1: Huge methods are likely to be machine generated, but some aren't. - * If huge, assume we won't compile, but allow futher analysis to turn it back on. - */ - if (compiler_options.IsHugeMethod(GetNumDalvikInsns())) { - skip_compilation = true; - *skip_message = "Huge method: " + std::to_string(GetNumDalvikInsns()); - // If we're got a huge number of basic blocks, don't bother with further analysis. - if (static_cast(GetNumBlocks()) > (compiler_options.GetHugeMethodThreshold() / 2)) { - return true; - } - } else if (compiler_options.IsLargeMethod(GetNumDalvikInsns()) && - /* If it's large and contains no branches, it's likely to be machine generated initialization */ - (GetBranchCount() == 0)) { - *skip_message = "Large method with no branches"; - return true; - } else if (compiler_filter == CompilerOptions::kSpeed) { - // If not huge, compile. - return false; - } - - // Filter 2: Skip class initializers. - if (((cu_->access_flags & kAccConstructor) != 0) && ((cu_->access_flags & kAccStatic) != 0)) { - *skip_message = "Class initializer"; - return true; - } - - // Filter 3: if this method is a special pattern, go ahead and emit the canned pattern. - if (cu_->compiler_driver->GetMethodInlinerMap() != nullptr && - cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file) - ->IsSpecial(cu_->method_idx)) { - return false; - } - - // Filter 4: if small, just compile. - if (GetNumDalvikInsns() < small_cutoff) { - return false; - } - - // Analyze graph for: - // o floating point computation - // o basic blocks contained in loop with heavy arithmetic. - // o proportion of conditional branches. - - MethodStats stats; - memset(&stats, 0, sizeof(stats)); - - ClearAllVisitedFlags(); - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - AnalyzeBlock(bb, &stats); - } - - return ComputeSkipCompilation(&stats, skip_compilation, skip_message); -} - -void MIRGraph::DoCacheFieldLoweringInfo() { - static constexpr uint32_t kFieldIndexFlagQuickened = 0x80000000; - // All IGET/IPUT/SGET/SPUT instructions take 2 code units and there must also be a RETURN. - const uint32_t max_refs = (GetNumDalvikInsns() - 1u) / 2u; - ScopedArenaAllocator allocator(&cu_->arena_stack); - auto* field_idxs = allocator.AllocArray(max_refs, kArenaAllocMisc); - DexMemAccessType* field_types = allocator.AllocArray( - max_refs, kArenaAllocMisc); - // Find IGET/IPUT/SGET/SPUT insns, store IGET/IPUT fields at the beginning, SGET/SPUT at the end. - size_t ifield_pos = 0u; - size_t sfield_pos = max_refs; - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - if (bb->block_type != kDalvikByteCode) { - continue; - } - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - // Get field index and try to find it among existing indexes. If found, it's usually among - // the last few added, so we'll start the search from ifield_pos/sfield_pos. Though this - // is a linear search, it actually performs much better than map based approach. - const bool is_iget_or_iput = IsInstructionIGetOrIPut(mir->dalvikInsn.opcode); - const bool is_iget_or_iput_quick = IsInstructionIGetQuickOrIPutQuick(mir->dalvikInsn.opcode); - if (is_iget_or_iput || is_iget_or_iput_quick) { - uint32_t field_idx; - DexMemAccessType access_type; - if (is_iget_or_iput) { - field_idx = mir->dalvikInsn.vC; - access_type = IGetOrIPutMemAccessType(mir->dalvikInsn.opcode); - } else { - DCHECK(is_iget_or_iput_quick); - // Set kFieldIndexFlagQuickened so that we don't deduplicate against non quickened field - // indexes. - field_idx = mir->offset | kFieldIndexFlagQuickened; - access_type = IGetQuickOrIPutQuickMemAccessType(mir->dalvikInsn.opcode); - } - size_t i = ifield_pos; - while (i != 0u && field_idxs[i - 1] != field_idx) { - --i; - } - if (i != 0u) { - mir->meta.ifield_lowering_info = i - 1; - DCHECK_EQ(field_types[i - 1], access_type); - } else { - mir->meta.ifield_lowering_info = ifield_pos; - field_idxs[ifield_pos] = field_idx; - field_types[ifield_pos] = access_type; - ++ifield_pos; - } - } else if (IsInstructionSGetOrSPut(mir->dalvikInsn.opcode)) { - auto field_idx = mir->dalvikInsn.vB; - size_t i = sfield_pos; - while (i != max_refs && field_idxs[i] != field_idx) { - ++i; - } - if (i != max_refs) { - mir->meta.sfield_lowering_info = max_refs - i - 1u; - DCHECK_EQ(field_types[i], SGetOrSPutMemAccessType(mir->dalvikInsn.opcode)); - } else { - mir->meta.sfield_lowering_info = max_refs - sfield_pos; - --sfield_pos; - field_idxs[sfield_pos] = field_idx; - field_types[sfield_pos] = SGetOrSPutMemAccessType(mir->dalvikInsn.opcode); - } - } - DCHECK_LE(ifield_pos, sfield_pos); - } - } - - if (ifield_pos != 0u) { - // Resolve instance field infos. - DCHECK_EQ(ifield_lowering_infos_.size(), 0u); - ifield_lowering_infos_.reserve(ifield_pos); - for (size_t pos = 0u; pos != ifield_pos; ++pos) { - const uint32_t field_idx = field_idxs[pos]; - const bool is_quickened = (field_idx & kFieldIndexFlagQuickened) != 0; - const uint32_t masked_field_idx = field_idx & ~kFieldIndexFlagQuickened; - CHECK_LT(masked_field_idx, 1u << 16); - ifield_lowering_infos_.push_back( - MirIFieldLoweringInfo(masked_field_idx, field_types[pos], is_quickened)); - } - ScopedObjectAccess soa(Thread::Current()); - MirIFieldLoweringInfo::Resolve(soa, - cu_->compiler_driver, - GetCurrentDexCompilationUnit(), - ifield_lowering_infos_.data(), - ifield_pos); - } - - if (sfield_pos != max_refs) { - // Resolve static field infos. - DCHECK_EQ(sfield_lowering_infos_.size(), 0u); - sfield_lowering_infos_.reserve(max_refs - sfield_pos); - for (size_t pos = max_refs; pos != sfield_pos;) { - --pos; - sfield_lowering_infos_.push_back(MirSFieldLoweringInfo(field_idxs[pos], field_types[pos])); - } - MirSFieldLoweringInfo::Resolve(cu_->compiler_driver, GetCurrentDexCompilationUnit(), - sfield_lowering_infos_.data(), max_refs - sfield_pos); - } -} - -void MIRGraph::DoCacheMethodLoweringInfo() { - static constexpr uint16_t invoke_types[] = { kVirtual, kSuper, kDirect, kStatic, kInterface }; - static constexpr uint32_t kMethodIdxFlagQuickened = 0x80000000; - - // Embed the map value in the entry to avoid extra padding in 64-bit builds. - struct MapEntry { - // Map key: target_method_idx, invoke_type, devirt_target. Ordered to avoid padding. - const MethodReference* devirt_target; - uint32_t target_method_idx; - uint32_t vtable_idx; - uint16_t invoke_type; - // Map value. - uint32_t lowering_info_index; - }; - - struct MapEntryComparator { - bool operator()(const MapEntry& lhs, const MapEntry& rhs) const { - if (lhs.target_method_idx != rhs.target_method_idx) { - return lhs.target_method_idx < rhs.target_method_idx; - } - if (lhs.invoke_type != rhs.invoke_type) { - return lhs.invoke_type < rhs.invoke_type; - } - if (lhs.vtable_idx != rhs.vtable_idx) { - return lhs.vtable_idx < rhs.vtable_idx; - } - if (lhs.devirt_target != rhs.devirt_target) { - if (lhs.devirt_target == nullptr) { - return true; - } - if (rhs.devirt_target == nullptr) { - return false; - } - return devirt_cmp(*lhs.devirt_target, *rhs.devirt_target); - } - return false; - } - MethodReferenceComparator devirt_cmp; - }; - - ScopedArenaAllocator allocator(&cu_->arena_stack); - - // All INVOKE instructions take 3 code units and there must also be a RETURN. - const uint32_t max_refs = (GetNumDalvikInsns() - 1u) / 3u; - - // Map invoke key (see MapEntry) to lowering info index and vice versa. - // The invoke_map and sequential entries are essentially equivalent to Boost.MultiIndex's - // multi_index_container with one ordered index and one sequential index. - ScopedArenaSet invoke_map(MapEntryComparator(), - allocator.Adapter()); - const MapEntry** sequential_entries = - allocator.AllocArray(max_refs, kArenaAllocMisc); - - // Find INVOKE insns and their devirtualization targets. - const VerifiedMethod* verified_method = GetCurrentDexCompilationUnit()->GetVerifiedMethod(); - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - if (bb->block_type != kDalvikByteCode) { - continue; - } - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - const bool is_quick_invoke = IsInstructionQuickInvoke(mir->dalvikInsn.opcode); - const bool is_invoke = IsInstructionInvoke(mir->dalvikInsn.opcode); - if (is_quick_invoke || is_invoke) { - uint32_t vtable_index = 0; - uint32_t target_method_idx = 0; - uint32_t invoke_type_idx = 0; // Default to virtual (in case of quickened). - DCHECK_EQ(invoke_types[invoke_type_idx], kVirtual); - if (is_quick_invoke) { - // We need to store the vtable index since we can't necessarily recreate it at resolve - // phase if the dequickening resolved to an interface method. - vtable_index = mir->dalvikInsn.vB; - // Fake up the method index by storing the mir offset so that we can read the dequicken - // info in resolve. - target_method_idx = mir->offset | kMethodIdxFlagQuickened; - } else { - DCHECK(is_invoke); - // Decode target method index and invoke type. - invoke_type_idx = InvokeInstructionType(mir->dalvikInsn.opcode); - target_method_idx = mir->dalvikInsn.vB; - } - // Find devirtualization target. - // TODO: The devirt map is ordered by the dex pc here. Is there a way to get INVOKEs - // ordered by dex pc as well? That would allow us to keep an iterator to devirt targets - // and increment it as needed instead of making O(log n) lookups. - const MethodReference* devirt_target = verified_method->GetDevirtTarget(mir->offset); - // Try to insert a new entry. If the insertion fails, we will have found an old one. - MapEntry entry = { - devirt_target, - target_method_idx, - vtable_index, - invoke_types[invoke_type_idx], - static_cast(invoke_map.size()) - }; - auto it = invoke_map.insert(entry).first; // Iterator to either the old or the new entry. - mir->meta.method_lowering_info = it->lowering_info_index; - // If we didn't actually insert, this will just overwrite an existing value with the same. - sequential_entries[it->lowering_info_index] = &*it; - } - } - } - if (invoke_map.empty()) { - return; - } - // Prepare unique method infos, set method info indexes for their MIRs. - const size_t count = invoke_map.size(); - method_lowering_infos_.reserve(count); - for (size_t pos = 0u; pos != count; ++pos) { - const MapEntry* entry = sequential_entries[pos]; - const bool is_quick = (entry->target_method_idx & kMethodIdxFlagQuickened) != 0; - const uint32_t masked_method_idx = entry->target_method_idx & ~kMethodIdxFlagQuickened; - MirMethodLoweringInfo method_info(masked_method_idx, - static_cast(entry->invoke_type), is_quick); - if (entry->devirt_target != nullptr) { - method_info.SetDevirtualizationTarget(*entry->devirt_target); - } - if (is_quick) { - method_info.SetVTableIndex(entry->vtable_idx); - } - method_lowering_infos_.push_back(method_info); - } - MirMethodLoweringInfo::Resolve(cu_->compiler_driver, GetCurrentDexCompilationUnit(), - method_lowering_infos_.data(), count); -} - -} // namespace art diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc deleted file mode 100644 index f1cc5fc4d2..0000000000 --- a/compiler/dex/mir_dataflow.cc +++ /dev/null @@ -1,1453 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "local_value_numbering.h" -#include "dataflow_iterator-inl.h" - -namespace art { - -/* - * Main table containing data flow attributes for each bytecode. The - * first kNumPackedOpcodes entries are for Dalvik bytecode - * instructions, where extended opcode at the MIR level are appended - * afterwards. - * - * TODO - many optimization flags are incomplete - they will only limit the - * scope of optimizations but will not cause mis-optimizations. - */ -const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { - // 00 NOP - DF_NOP, - - // 01 MOVE vA, vB - DF_DA | DF_UB | DF_IS_MOVE, - - // 02 MOVE_FROM16 vAA, vBBBB - DF_DA | DF_UB | DF_IS_MOVE, - - // 03 MOVE_16 vAAAA, vBBBB - DF_DA | DF_UB | DF_IS_MOVE, - - // 04 MOVE_WIDE vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_IS_MOVE, - - // 05 MOVE_WIDE_FROM16 vAA, vBBBB - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_IS_MOVE, - - // 06 MOVE_WIDE_16 vAAAA, vBBBB - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_IS_MOVE, - - // 07 MOVE_OBJECT vA, vB - DF_DA | DF_UB | DF_NULL_TRANSFER_0 | DF_IS_MOVE | DF_REF_A | DF_REF_B, - - // 08 MOVE_OBJECT_FROM16 vAA, vBBBB - DF_DA | DF_UB | DF_NULL_TRANSFER_0 | DF_IS_MOVE | DF_REF_A | DF_REF_B, - - // 09 MOVE_OBJECT_16 vAAAA, vBBBB - DF_DA | DF_UB | DF_NULL_TRANSFER_0 | DF_IS_MOVE | DF_REF_A | DF_REF_B, - - // 0A MOVE_RESULT vAA - DF_DA, - - // 0B MOVE_RESULT_WIDE vAA - DF_DA | DF_A_WIDE, - - // 0C MOVE_RESULT_OBJECT vAA - DF_DA | DF_REF_A, - - // 0D MOVE_EXCEPTION vAA - DF_DA | DF_REF_A | DF_NON_NULL_DST, - - // 0E RETURN_VOID - DF_NOP, - - // 0F RETURN vAA - DF_UA, - - // 10 RETURN_WIDE vAA - DF_UA | DF_A_WIDE, - - // 11 RETURN_OBJECT vAA - DF_UA | DF_REF_A, - - // 12 CONST_4 vA, #+B - DF_DA | DF_SETS_CONST, - - // 13 CONST_16 vAA, #+BBBB - DF_DA | DF_SETS_CONST, - - // 14 CONST vAA, #+BBBBBBBB - DF_DA | DF_SETS_CONST, - - // 15 CONST_HIGH16 VAA, #+BBBB0000 - DF_DA | DF_SETS_CONST, - - // 16 CONST_WIDE_16 vAA, #+BBBB - DF_DA | DF_A_WIDE | DF_SETS_CONST, - - // 17 CONST_WIDE_32 vAA, #+BBBBBBBB - DF_DA | DF_A_WIDE | DF_SETS_CONST, - - // 18 CONST_WIDE vAA, #+BBBBBBBBBBBBBBBB - DF_DA | DF_A_WIDE | DF_SETS_CONST, - - // 19 CONST_WIDE_HIGH16 vAA, #+BBBB000000000000 - DF_DA | DF_A_WIDE | DF_SETS_CONST, - - // 1A CONST_STRING vAA, string@BBBB - DF_DA | DF_REF_A | DF_NON_NULL_DST, - - // 1B CONST_STRING_JUMBO vAA, string@BBBBBBBB - DF_DA | DF_REF_A | DF_NON_NULL_DST, - - // 1C CONST_CLASS vAA, type@BBBB - DF_DA | DF_REF_A | DF_NON_NULL_DST, - - // 1D MONITOR_ENTER vAA - DF_UA | DF_NULL_CHK_A | DF_REF_A, - - // 1E MONITOR_EXIT vAA - DF_UA | DF_NULL_CHK_A | DF_REF_A, - - // 1F CHK_CAST vAA, type@BBBB - DF_UA | DF_REF_A | DF_CHK_CAST | DF_UMS, - - // 20 INSTANCE_OF vA, vB, type@CCCC - DF_DA | DF_UB | DF_CORE_A | DF_REF_B | DF_UMS, - - // 21 ARRAY_LENGTH vA, vB - DF_DA | DF_UB | DF_NULL_CHK_B | DF_CORE_A | DF_REF_B, - - // 22 NEW_INSTANCE vAA, type@BBBB - DF_DA | DF_NON_NULL_DST | DF_REF_A | DF_UMS, - - // 23 NEW_ARRAY vA, vB, type@CCCC - DF_DA | DF_UB | DF_NON_NULL_DST | DF_REF_A | DF_CORE_B | DF_UMS, - - // 24 FILLED_NEW_ARRAY {vD, vE, vF, vG, vA} - DF_FORMAT_35C | DF_NON_NULL_RET | DF_UMS, - - // 25 FILLED_NEW_ARRAY_RANGE {vCCCC .. vNNNN}, type@BBBB - DF_FORMAT_3RC | DF_NON_NULL_RET | DF_UMS, - - // 26 FILL_ARRAY_DATA vAA, +BBBBBBBB - DF_UA | DF_REF_A | DF_UMS, - - // 27 THROW vAA - DF_UA | DF_REF_A | DF_UMS, - - // 28 GOTO - DF_NOP, - - // 29 GOTO_16 - DF_NOP, - - // 2A GOTO_32 - DF_NOP, - - // 2B PACKED_SWITCH vAA, +BBBBBBBB - DF_UA | DF_CORE_A, - - // 2C SPARSE_SWITCH vAA, +BBBBBBBB - DF_UA | DF_CORE_A, - - // 2D CMPL_FLOAT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_FP_B | DF_FP_C | DF_CORE_A, - - // 2E CMPG_FLOAT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_FP_B | DF_FP_C | DF_CORE_A, - - // 2F CMPL_DOUBLE vAA, vBB, vCC - DF_DA | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_B | DF_FP_C | DF_CORE_A, - - // 30 CMPG_DOUBLE vAA, vBB, vCC - DF_DA | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_B | DF_FP_C | DF_CORE_A, - - // 31 CMP_LONG vAA, vBB, vCC - DF_DA | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 32 IF_EQ vA, vB, +CCCC - DF_UA | DF_UB | DF_SAME_TYPE_AB, - - // 33 IF_NE vA, vB, +CCCC - DF_UA | DF_UB | DF_SAME_TYPE_AB, - - // 34 IF_LT vA, vB, +CCCC - DF_UA | DF_UB | DF_SAME_TYPE_AB, - - // 35 IF_GE vA, vB, +CCCC - DF_UA | DF_UB | DF_SAME_TYPE_AB, - - // 36 IF_GT vA, vB, +CCCC - DF_UA | DF_UB | DF_SAME_TYPE_AB, - - // 37 IF_LE vA, vB, +CCCC - DF_UA | DF_UB | DF_SAME_TYPE_AB, - - // 38 IF_EQZ vAA, +BBBB - DF_UA, - - // 39 IF_NEZ vAA, +BBBB - DF_UA, - - // 3A IF_LTZ vAA, +BBBB - DF_UA, - - // 3B IF_GEZ vAA, +BBBB - DF_UA, - - // 3C IF_GTZ vAA, +BBBB - DF_UA, - - // 3D IF_LEZ vAA, +BBBB - DF_UA, - - // 3E UNUSED_3E - DF_NOP, - - // 3F UNUSED_3F - DF_NOP, - - // 40 UNUSED_40 - DF_NOP, - - // 41 UNUSED_41 - DF_NOP, - - // 42 UNUSED_42 - DF_NOP, - - // 43 UNUSED_43 - DF_NOP, - - // 44 AGET vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 45 AGET_WIDE vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 46 AGET_OBJECT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN, - - // 47 AGET_BOOLEAN vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 48 AGET_BYTE vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 49 AGET_CHAR vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 4A AGET_SHORT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 4B APUT vAA, vBB, vCC - DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 4C APUT_WIDE vAA, vBB, vCC - DF_UA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 4D APUT_OBJECT vAA, vBB, vCC - DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN, - - // 4E APUT_BOOLEAN vAA, vBB, vCC - DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 4F APUT_BYTE vAA, vBB, vCC - DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 50 APUT_CHAR vAA, vBB, vCC - DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 51 APUT_SHORT vAA, vBB, vCC - DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 52 IGET vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 53 IGET_WIDE vA, vB, field@CCCC - DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 54 IGET_OBJECT vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN, - - // 55 IGET_BOOLEAN vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 56 IGET_BYTE vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 57 IGET_CHAR vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 58 IGET_SHORT vA, vB, field@CCCC - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 59 IPUT vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 5A IPUT_WIDE vA, vB, field@CCCC - DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 5B IPUT_OBJECT vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN, - - // 5C IPUT_BOOLEAN vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 5D IPUT_BYTE vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 5E IPUT_CHAR vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 5F IPUT_SHORT vA, vB, field@CCCC - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // 60 SGET vAA, field@BBBB - DF_DA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 61 SGET_WIDE vAA, field@BBBB - DF_DA | DF_A_WIDE | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 62 SGET_OBJECT vAA, field@BBBB - DF_DA | DF_REF_A | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 63 SGET_BOOLEAN vAA, field@BBBB - DF_DA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 64 SGET_BYTE vAA, field@BBBB - DF_DA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 65 SGET_CHAR vAA, field@BBBB - DF_DA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 66 SGET_SHORT vAA, field@BBBB - DF_DA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 67 SPUT vAA, field@BBBB - DF_UA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 68 SPUT_WIDE vAA, field@BBBB - DF_UA | DF_A_WIDE | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 69 SPUT_OBJECT vAA, field@BBBB - DF_UA | DF_REF_A | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 6A SPUT_BOOLEAN vAA, field@BBBB - DF_UA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 6B SPUT_BYTE vAA, field@BBBB - DF_UA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 6C SPUT_CHAR vAA, field@BBBB - DF_UA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 6D SPUT_SHORT vAA, field@BBBB - DF_UA | DF_SFIELD | DF_CLINIT | DF_UMS, - - // 6E INVOKE_VIRTUAL {vD, vE, vF, vG, vA} - DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS, - - // 6F INVOKE_SUPER {vD, vE, vF, vG, vA} - DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS, - - // 70 INVOKE_DIRECT {vD, vE, vF, vG, vA} - DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS, - - // 71 INVOKE_STATIC {vD, vE, vF, vG, vA} - DF_FORMAT_35C | DF_CLINIT | DF_UMS, - - // 72 INVOKE_INTERFACE {vD, vE, vF, vG, vA} - DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS, - - // 73 RETURN_VOID_NO_BARRIER - DF_NOP, - - // 74 INVOKE_VIRTUAL_RANGE {vCCCC .. vNNNN} - DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS, - - // 75 INVOKE_SUPER_RANGE {vCCCC .. vNNNN} - DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS, - - // 76 INVOKE_DIRECT_RANGE {vCCCC .. vNNNN} - DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS, - - // 77 INVOKE_STATIC_RANGE {vCCCC .. vNNNN} - DF_FORMAT_3RC | DF_CLINIT | DF_UMS, - - // 78 INVOKE_INTERFACE_RANGE {vCCCC .. vNNNN} - DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS, - - // 79 UNUSED_79 - DF_NOP, - - // 7A UNUSED_7A - DF_NOP, - - // 7B NEG_INT vA, vB - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // 7C NOT_INT vA, vB - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // 7D NEG_LONG vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // 7E NOT_LONG vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // 7F NEG_FLOAT vA, vB - DF_DA | DF_UB | DF_FP_A | DF_FP_B, - - // 80 NEG_DOUBLE vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, - - // 81 INT_TO_LONG vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_CORE_A | DF_CORE_B, - - // 82 INT_TO_FLOAT vA, vB - DF_DA | DF_UB | DF_FP_A | DF_CORE_B, - - // 83 INT_TO_DOUBLE vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_FP_A | DF_CORE_B, - - // 84 LONG_TO_INT vA, vB - DF_DA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // 85 LONG_TO_FLOAT vA, vB - DF_DA | DF_UB | DF_B_WIDE | DF_FP_A | DF_CORE_B, - - // 86 LONG_TO_DOUBLE vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_A | DF_CORE_B, - - // 87 FLOAT_TO_INT vA, vB - DF_DA | DF_UB | DF_FP_B | DF_CORE_A, - - // 88 FLOAT_TO_LONG vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_FP_B | DF_CORE_A, - - // 89 FLOAT_TO_DOUBLE vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_FP_A | DF_FP_B, - - // 8A DOUBLE_TO_INT vA, vB - DF_DA | DF_UB | DF_B_WIDE | DF_FP_B | DF_CORE_A, - - // 8B DOUBLE_TO_LONG vA, vB - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_B | DF_CORE_A, - - // 8C DOUBLE_TO_FLOAT vA, vB - DF_DA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, - - // 8D INT_TO_BYTE vA, vB - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // 8E INT_TO_CHAR vA, vB - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // 8F INT_TO_SHORT vA, vB - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // 90 ADD_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 91 SUB_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 92 MUL_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 93 DIV_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 94 REM_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 95 AND_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 96 OR_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 97 XOR_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 98 SHL_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 99 SHR_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 9A USHR_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 9B ADD_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 9C SUB_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 9D MUL_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 9E DIV_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // 9F REM_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // A0 AND_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // A1 OR_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // A2 XOR_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // A3 SHL_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // A4 SHR_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // A5 USHR_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, - - // A6 ADD_FLOAT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C, - - // A7 SUB_FLOAT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C, - - // A8 MUL_FLOAT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C, - - // A9 DIV_FLOAT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C, - - // AA REM_FLOAT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C, - - // AB ADD_DOUBLE vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C, - - // AC SUB_DOUBLE vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C, - - // AD MUL_DOUBLE vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C, - - // AE DIV_DOUBLE vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C, - - // AF REM_DOUBLE vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C, - - // B0 ADD_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // B1 SUB_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // B2 MUL_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // B3 DIV_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // B4 REM_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // B5 AND_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // B6 OR_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // B7 XOR_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // B8 SHL_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // B9 SHR_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // BA USHR_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // BB ADD_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // BC SUB_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // BD MUL_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // BE DIV_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // BF REM_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // C0 AND_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // C1 OR_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // C2 XOR_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // C3 SHL_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // C4 SHR_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // C5 USHR_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, - - // C6 ADD_FLOAT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B, - - // C7 SUB_FLOAT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B, - - // C8 MUL_FLOAT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B, - - // C9 DIV_FLOAT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B, - - // CA REM_FLOAT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B, - - // CB ADD_DOUBLE_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, - - // CC SUB_DOUBLE_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, - - // CD MUL_DOUBLE_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, - - // CE DIV_DOUBLE_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, - - // CF REM_DOUBLE_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, - - // D0 ADD_INT_LIT16 vA, vB, #+CCCC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // D1 RSUB_INT vA, vB, #+CCCC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // D2 MUL_INT_LIT16 vA, vB, #+CCCC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // D3 DIV_INT_LIT16 vA, vB, #+CCCC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // D4 REM_INT_LIT16 vA, vB, #+CCCC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // D5 AND_INT_LIT16 vA, vB, #+CCCC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // D6 OR_INT_LIT16 vA, vB, #+CCCC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // D7 XOR_INT_LIT16 vA, vB, #+CCCC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // D8 ADD_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // D9 RSUB_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // DA MUL_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // DB DIV_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // DC REM_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // DD AND_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // DE OR_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // DF XOR_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // E0 SHL_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // E1 SHR_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // E2 USHR_INT_LIT8 vAA, vBB, #+CC - DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, - - // E3 IGET_QUICK - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // E4 IGET_WIDE_QUICK - DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // E5 IGET_OBJECT_QUICK - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN, - - // E6 IPUT_QUICK - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // E7 IPUT_WIDE_QUICK - DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // E8 IPUT_OBJECT_QUICK - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN, - - // E9 INVOKE_VIRTUAL_QUICK - DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS, - - // EA INVOKE_VIRTUAL_RANGE_QUICK - DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS, - - // EB IPUT_BOOLEAN_QUICK vA, vB, index - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // EC IPUT_BYTE_QUICK vA, vB, index - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // ED IPUT_CHAR_QUICK vA, vB, index - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // EE IPUT_SHORT_QUICK vA, vB, index - DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // EF IGET_BOOLEAN_QUICK vA, vB, index - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // F0 IGET_BYTE_QUICK vA, vB, index - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // F1 IGET_CHAR_QUICK vA, vB, index - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // F2 IGET_SHORT_QUICK vA, vB, index - DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN, - - // F3 UNUSED_F3 - DF_NOP, - - // F4 UNUSED_F4 - DF_NOP, - - // F5 UNUSED_F5 - DF_NOP, - - // F6 UNUSED_F6 - DF_NOP, - - // F7 UNUSED_F7 - DF_NOP, - - // F8 UNUSED_F8 - DF_NOP, - - // F9 UNUSED_F9 - DF_NOP, - - // FA UNUSED_FA - DF_NOP, - - // FB UNUSED_FB - DF_NOP, - - // FC UNUSED_FC - DF_NOP, - - // FD UNUSED_FD - DF_NOP, - - // FE UNUSED_FE - DF_NOP, - - // FF UNUSED_FF - DF_NOP, - - // Beginning of extended MIR opcodes - // 100 MIR_PHI - DF_DA | DF_NULL_TRANSFER_N, - - // 101 MIR_COPY - DF_DA | DF_UB | DF_IS_MOVE, - - // 102 MIR_FUSED_CMPL_FLOAT - DF_UA | DF_UB | DF_FP_A | DF_FP_B, - - // 103 MIR_FUSED_CMPG_FLOAT - DF_UA | DF_UB | DF_FP_A | DF_FP_B, - - // 104 MIR_FUSED_CMPL_DOUBLE - DF_UA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, - - // 105 MIR_FUSED_CMPG_DOUBLE - DF_UA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, - - // 106 MIR_FUSED_CMP_LONG - DF_UA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, - - // 107 MIR_NOP - DF_NOP, - - // 108 MIR_NULL_CHECK - DF_UA | DF_REF_A | DF_NULL_CHK_A | DF_LVN, - - // 109 MIR_RANGE_CHECK - 0, - - // 10A MIR_DIV_ZERO_CHECK - 0, - - // 10B MIR_CHECK - 0, - - // 10D MIR_SELECT - DF_DA | DF_UB, - - // 10E MirOpConstVector - 0, - - // 10F MirOpMoveVector - 0, - - // 110 MirOpPackedMultiply - 0, - - // 111 MirOpPackedAddition - 0, - - // 112 MirOpPackedSubtract - 0, - - // 113 MirOpPackedShiftLeft - 0, - - // 114 MirOpPackedSignedShiftRight - 0, - - // 115 MirOpPackedUnsignedShiftRight - 0, - - // 116 MirOpPackedAnd - 0, - - // 117 MirOpPackedOr - 0, - - // 118 MirOpPackedXor - 0, - - // 119 MirOpPackedAddReduce - DF_FORMAT_EXTENDED, - - // 11A MirOpPackedReduce - DF_FORMAT_EXTENDED, - - // 11B MirOpPackedSet - DF_FORMAT_EXTENDED, - - // 11C MirOpReserveVectorRegisters - 0, - - // 11D MirOpReturnVectorRegisters - 0, - - // 11E MirOpMemBarrier - 0, - - // 11F MirOpPackedArrayGet - DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 120 MirOpPackedArrayPut - DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN, - - // 121 MirOpMaddInt - DF_FORMAT_EXTENDED, - - // 122 MirOpMsubInt - DF_FORMAT_EXTENDED, - - // 123 MirOpMaddLong - DF_FORMAT_EXTENDED, - - // 124 MirOpMsubLong - DF_FORMAT_EXTENDED, -}; - -/* Any register that is used before being defined is considered live-in */ -void MIRGraph::HandleLiveInUse(ArenaBitVector* use_v, ArenaBitVector* def_v, - ArenaBitVector* live_in_v, int dalvik_reg_id) { - use_v->SetBit(dalvik_reg_id); - if (!def_v->IsBitSet(dalvik_reg_id)) { - live_in_v->SetBit(dalvik_reg_id); - } -} - -/* Mark a reg as being defined */ -void MIRGraph::HandleDef(ArenaBitVector* def_v, int dalvik_reg_id) { - def_v->SetBit(dalvik_reg_id); -} - -void MIRGraph::HandleExtended(ArenaBitVector* use_v, ArenaBitVector* def_v, - ArenaBitVector* live_in_v, - const MIR::DecodedInstruction& d_insn) { - // For vector MIRs, vC contains type information - bool is_vector_type_wide = false; - int type_size = d_insn.vC >> 16; - if (type_size == k64 || type_size == kDouble) { - is_vector_type_wide = true; - } - - switch (static_cast(d_insn.opcode)) { - case kMirOpPackedAddReduce: - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vA); - if (is_vector_type_wide == true) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vA + 1); - } - HandleDef(def_v, d_insn.vA); - if (is_vector_type_wide == true) { - HandleDef(def_v, d_insn.vA + 1); - } - break; - case kMirOpPackedReduce: - HandleDef(def_v, d_insn.vA); - if (is_vector_type_wide == true) { - HandleDef(def_v, d_insn.vA + 1); - } - break; - case kMirOpPackedSet: - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB); - if (is_vector_type_wide == true) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB + 1); - } - break; - case kMirOpMaddInt: - case kMirOpMsubInt: - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB); - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vC); - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.arg[0]); - HandleDef(def_v, d_insn.vA); - break; - case kMirOpMaddLong: - case kMirOpMsubLong: - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB); - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB + 1); - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vC); - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vC + 1); - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.arg[0]); - HandleLiveInUse(use_v, def_v, live_in_v, d_insn.arg[0] + 1); - HandleDef(def_v, d_insn.vA); - HandleDef(def_v, d_insn.vA + 1); - break; - default: - LOG(ERROR) << "Unexpected Extended Opcode " << d_insn.opcode; - break; - } -} - -/* - * Find out live-in variables for natural loops. Variables that are live-in in - * the main loop body are considered to be defined in the entry block. - */ -bool MIRGraph::FindLocalLiveIn(BasicBlock* bb) { - MIR* mir; - ArenaBitVector *use_v, *def_v, *live_in_v; - - if (bb->data_flow_info == nullptr) return false; - - use_v = bb->data_flow_info->use_v = - new (arena_) ArenaBitVector(arena_, GetNumOfCodeAndTempVRs(), false); - def_v = bb->data_flow_info->def_v = - new (arena_) ArenaBitVector(arena_, GetNumOfCodeAndTempVRs(), false); - live_in_v = bb->data_flow_info->live_in_v = - new (arena_) ArenaBitVector(arena_, GetNumOfCodeAndTempVRs(), false); - - for (mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - uint64_t df_attributes = GetDataFlowAttributes(mir); - MIR::DecodedInstruction* d_insn = &mir->dalvikInsn; - - if (df_attributes & DF_HAS_USES) { - if (df_attributes & DF_UA) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vA); - if (df_attributes & DF_A_WIDE) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vA+1); - } - } - if (df_attributes & DF_UB) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vB); - if (df_attributes & DF_B_WIDE) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vB+1); - } - } - if (df_attributes & DF_UC) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vC); - if (df_attributes & DF_C_WIDE) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vC+1); - } - } - } - if (df_attributes & DF_FORMAT_35C) { - for (unsigned int i = 0; i < d_insn->vA; i++) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn->arg[i]); - } - } - if (df_attributes & DF_FORMAT_3RC) { - for (unsigned int i = 0; i < d_insn->vA; i++) { - HandleLiveInUse(use_v, def_v, live_in_v, d_insn->vC+i); - } - } - if (df_attributes & DF_HAS_DEFS) { - HandleDef(def_v, d_insn->vA); - if (df_attributes & DF_A_WIDE) { - HandleDef(def_v, d_insn->vA+1); - } - } - if (df_attributes & DF_FORMAT_EXTENDED) { - HandleExtended(use_v, def_v, live_in_v, mir->dalvikInsn); - } - } - return true; -} - -int MIRGraph::AddNewSReg(int v_reg) { - int subscript = ++ssa_last_defs_[v_reg]; - uint32_t ssa_reg = GetNumSSARegs(); - SetNumSSARegs(ssa_reg + 1); - ssa_base_vregs_.push_back(v_reg); - ssa_subscripts_.push_back(subscript); - DCHECK_EQ(ssa_base_vregs_.size(), ssa_subscripts_.size()); - // If we are expanding very late, update use counts too. - if (ssa_reg > 0 && use_counts_.size() == ssa_reg) { - // Need to expand the counts. - use_counts_.push_back(0); - raw_use_counts_.push_back(0); - } - return ssa_reg; -} - -/* Find out the latest SSA register for a given Dalvik register */ -void MIRGraph::HandleSSAUse(int* uses, int dalvik_reg, int reg_index) { - DCHECK((dalvik_reg >= 0) && (dalvik_reg < static_cast(GetNumOfCodeAndTempVRs()))); - uses[reg_index] = vreg_to_ssa_map_[dalvik_reg]; -} - -/* Setup a new SSA register for a given Dalvik register */ -void MIRGraph::HandleSSADef(int* defs, int dalvik_reg, int reg_index) { - DCHECK((dalvik_reg >= 0) && (dalvik_reg < static_cast(GetNumOfCodeAndTempVRs()))); - int ssa_reg = AddNewSReg(dalvik_reg); - vreg_to_ssa_map_[dalvik_reg] = ssa_reg; - defs[reg_index] = ssa_reg; -} - -void MIRGraph::AllocateSSAUseData(MIR *mir, int num_uses) { - mir->ssa_rep->num_uses = num_uses; - - if (mir->ssa_rep->num_uses_allocated < num_uses) { - mir->ssa_rep->uses = arena_->AllocArray(num_uses, kArenaAllocDFInfo); - } -} - -void MIRGraph::AllocateSSADefData(MIR *mir, int num_defs) { - mir->ssa_rep->num_defs = num_defs; - - if (mir->ssa_rep->num_defs_allocated < num_defs) { - mir->ssa_rep->defs = arena_->AllocArray(num_defs, kArenaAllocDFInfo); - } -} - -/* Look up new SSA names for format_35c instructions */ -void MIRGraph::DataFlowSSAFormat35C(MIR* mir) { - MIR::DecodedInstruction* d_insn = &mir->dalvikInsn; - int num_uses = d_insn->vA; - int i; - - AllocateSSAUseData(mir, num_uses); - - for (i = 0; i < num_uses; i++) { - HandleSSAUse(mir->ssa_rep->uses, d_insn->arg[i], i); - } -} - -/* Look up new SSA names for format_3rc instructions */ -void MIRGraph::DataFlowSSAFormat3RC(MIR* mir) { - MIR::DecodedInstruction* d_insn = &mir->dalvikInsn; - int num_uses = d_insn->vA; - int i; - - AllocateSSAUseData(mir, num_uses); - - for (i = 0; i < num_uses; i++) { - HandleSSAUse(mir->ssa_rep->uses, d_insn->vC+i, i); - } -} - -void MIRGraph::DataFlowSSAFormatExtended(MIR* mir) { - const MIR::DecodedInstruction& d_insn = mir->dalvikInsn; - // For vector MIRs, vC contains type information - bool is_vector_type_wide = false; - int type_size = d_insn.vC >> 16; - if (type_size == k64 || type_size == kDouble) { - is_vector_type_wide = true; - } - - switch (static_cast(mir->dalvikInsn.opcode)) { - case kMirOpPackedAddReduce: - // We have one use, plus one more for wide - AllocateSSAUseData(mir, is_vector_type_wide ? 2 : 1); - HandleSSAUse(mir->ssa_rep->uses, d_insn.vA, 0); - if (is_vector_type_wide == true) { - HandleSSAUse(mir->ssa_rep->uses, d_insn.vA + 1, 1); - } - - // We have a def, plus one more for wide - AllocateSSADefData(mir, is_vector_type_wide ? 2 : 1); - HandleSSADef(mir->ssa_rep->defs, d_insn.vA, 0); - if (is_vector_type_wide == true) { - HandleSSADef(mir->ssa_rep->defs, d_insn.vA + 1, 1); - } - break; - case kMirOpPackedReduce: - // We have a def, plus one more for wide - AllocateSSADefData(mir, is_vector_type_wide ? 2 : 1); - HandleSSADef(mir->ssa_rep->defs, d_insn.vA, 0); - if (is_vector_type_wide == true) { - HandleSSADef(mir->ssa_rep->defs, d_insn.vA + 1, 1); - } - break; - case kMirOpPackedSet: - // We have one use, plus one more for wide - AllocateSSAUseData(mir, is_vector_type_wide ? 2 : 1); - HandleSSAUse(mir->ssa_rep->uses, d_insn.vB, 0); - if (is_vector_type_wide == true) { - HandleSSAUse(mir->ssa_rep->uses, d_insn.vB + 1, 1); - } - break; - case kMirOpMaddInt: - case kMirOpMsubInt: - AllocateSSAUseData(mir, 3); - HandleSSAUse(mir->ssa_rep->uses, d_insn.vB, 0); - HandleSSAUse(mir->ssa_rep->uses, d_insn.vC, 1); - HandleSSAUse(mir->ssa_rep->uses, d_insn.arg[0], 2); - AllocateSSADefData(mir, 1); - HandleSSADef(mir->ssa_rep->defs, d_insn.vA, 0); - break; - case kMirOpMaddLong: - case kMirOpMsubLong: - AllocateSSAUseData(mir, 6); - HandleSSAUse(mir->ssa_rep->uses, d_insn.vB, 0); - HandleSSAUse(mir->ssa_rep->uses, d_insn.vB + 1, 1); - HandleSSAUse(mir->ssa_rep->uses, d_insn.vC, 2); - HandleSSAUse(mir->ssa_rep->uses, d_insn.vC + 1, 3); - HandleSSAUse(mir->ssa_rep->uses, d_insn.arg[0], 4); - HandleSSAUse(mir->ssa_rep->uses, d_insn.arg[0] + 1, 5); - AllocateSSADefData(mir, 2); - HandleSSADef(mir->ssa_rep->defs, d_insn.vA, 0); - HandleSSADef(mir->ssa_rep->defs, d_insn.vA + 1, 1); - break; - default: - LOG(ERROR) << "Missing case for extended MIR: " << mir->dalvikInsn.opcode; - break; - } -} - -/* Entry function to convert a block into SSA representation */ -bool MIRGraph::DoSSAConversion(BasicBlock* bb) { - if (bb->data_flow_info == nullptr) return false; - - /* - * Pruned SSA form: Insert phi nodes for each dalvik register marked in phi_node_blocks - * only if the dalvik register is in the live-in set. - */ - BasicBlockId bb_id = bb->id; - for (int dalvik_reg = GetNumOfCodeAndTempVRs() - 1; dalvik_reg >= 0; dalvik_reg--) { - if (temp_.ssa.phi_node_blocks[dalvik_reg]->IsBitSet(bb_id)) { - if (!bb->data_flow_info->live_in_v->IsBitSet(dalvik_reg)) { - /* Variable will be clobbered before being used - no need for phi */ - vreg_to_ssa_map_[dalvik_reg] = INVALID_SREG; - continue; - } - MIR *phi = NewMIR(); - phi->dalvikInsn.opcode = static_cast(kMirOpPhi); - phi->dalvikInsn.vA = dalvik_reg; - phi->offset = bb->start_offset; - phi->m_unit_index = 0; // Arbitrarily assign all Phi nodes to outermost method. - bb->PrependMIR(phi); - } - } - - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - mir->ssa_rep = - static_cast(arena_->Alloc(sizeof(SSARepresentation), - kArenaAllocDFInfo)); - memset(mir->ssa_rep, 0, sizeof(*mir->ssa_rep)); - - uint64_t df_attributes = GetDataFlowAttributes(mir); - - // If not a pseudo-op, note non-leaf or can throw - if (!MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode)) { - int flags = mir->dalvikInsn.FlagsOf(); - - if ((flags & Instruction::kInvoke) != 0) { - attributes_ &= ~METHOD_IS_LEAF; - } - } - - int num_uses = 0; - - if (df_attributes & DF_FORMAT_35C) { - DataFlowSSAFormat35C(mir); - continue; - } - - if (df_attributes & DF_FORMAT_3RC) { - DataFlowSSAFormat3RC(mir); - continue; - } - - if (df_attributes & DF_FORMAT_EXTENDED) { - DataFlowSSAFormatExtended(mir); - continue; - } - - if (df_attributes & DF_HAS_USES) { - if (df_attributes & DF_UA) { - num_uses++; - if (df_attributes & DF_A_WIDE) { - num_uses++; - } - } - if (df_attributes & DF_UB) { - num_uses++; - if (df_attributes & DF_B_WIDE) { - num_uses++; - } - } - if (df_attributes & DF_UC) { - num_uses++; - if (df_attributes & DF_C_WIDE) { - num_uses++; - } - } - } - - AllocateSSAUseData(mir, num_uses); - - int num_defs = 0; - - if (df_attributes & DF_HAS_DEFS) { - num_defs++; - if (df_attributes & DF_A_WIDE) { - num_defs++; - } - } - - AllocateSSADefData(mir, num_defs); - - MIR::DecodedInstruction* d_insn = &mir->dalvikInsn; - - if (df_attributes & DF_HAS_USES) { - num_uses = 0; - if (df_attributes & DF_UA) { - HandleSSAUse(mir->ssa_rep->uses, d_insn->vA, num_uses++); - if (df_attributes & DF_A_WIDE) { - HandleSSAUse(mir->ssa_rep->uses, d_insn->vA+1, num_uses++); - } - } - if (df_attributes & DF_UB) { - HandleSSAUse(mir->ssa_rep->uses, d_insn->vB, num_uses++); - if (df_attributes & DF_B_WIDE) { - HandleSSAUse(mir->ssa_rep->uses, d_insn->vB+1, num_uses++); - } - } - if (df_attributes & DF_UC) { - HandleSSAUse(mir->ssa_rep->uses, d_insn->vC, num_uses++); - if (df_attributes & DF_C_WIDE) { - HandleSSAUse(mir->ssa_rep->uses, d_insn->vC+1, num_uses++); - } - } - } - if (df_attributes & DF_HAS_DEFS) { - HandleSSADef(mir->ssa_rep->defs, d_insn->vA, 0); - if (df_attributes & DF_A_WIDE) { - HandleSSADef(mir->ssa_rep->defs, d_insn->vA+1, 1); - } - } - } - - /* - * Take a snapshot of Dalvik->SSA mapping at the end of each block. The - * input to PHI nodes can be derived from the snapshot of all - * predecessor blocks. - */ - bb->data_flow_info->vreg_to_ssa_map_exit = - arena_->AllocArray(GetNumOfCodeAndTempVRs(), kArenaAllocDFInfo); - - memcpy(bb->data_flow_info->vreg_to_ssa_map_exit, vreg_to_ssa_map_, - sizeof(int) * GetNumOfCodeAndTempVRs()); - return true; -} - -void MIRGraph::InitializeBasicBlockDataFlow() { - /* - * Allocate the BasicBlockDataFlow structure for the entry and code blocks. - */ - for (BasicBlock* bb : block_list_) { - if (bb->hidden == true) continue; - if (bb->block_type == kDalvikByteCode || - bb->block_type == kEntryBlock || - bb->block_type == kExitBlock) { - bb->data_flow_info = - static_cast(arena_->Alloc(sizeof(BasicBlockDataFlow), - kArenaAllocDFInfo)); - } - } -} - -/* Setup the basic data structures for SSA conversion */ -void MIRGraph::CompilerInitializeSSAConversion() { - size_t num_reg = GetNumOfCodeAndTempVRs(); - - ssa_base_vregs_.clear(); - ssa_base_vregs_.reserve(num_reg + GetDefCount() + 128); - ssa_subscripts_.clear(); - ssa_subscripts_.reserve(num_reg + GetDefCount() + 128); - - /* - * Initial number of SSA registers is equal to the number of Dalvik - * registers. - */ - SetNumSSARegs(num_reg); - - /* - * Initialize the SSA2Dalvik map list. For the first num_reg elements, - * the subscript is 0 so we use the ENCODE_REG_SUB macro to encode the value - * into "(0 << 16) | i" - */ - for (unsigned int i = 0; i < num_reg; i++) { - ssa_base_vregs_.push_back(i); - ssa_subscripts_.push_back(0); - } - - /* - * Initialize the DalvikToSSAMap map. There is one entry for each - * Dalvik register, and the SSA names for those are the same. - */ - vreg_to_ssa_map_ = arena_->AllocArray(num_reg, kArenaAllocDFInfo); - /* Keep track of the higest def for each dalvik reg */ - ssa_last_defs_ = arena_->AllocArray(num_reg, kArenaAllocDFInfo); - - for (unsigned int i = 0; i < num_reg; i++) { - vreg_to_ssa_map_[i] = i; - ssa_last_defs_[i] = 0; - } - - // Create a compiler temporary for Method*. This is done after SSA initialization. - CompilerTemp* method_temp = GetNewCompilerTemp(kCompilerTempSpecialMethodPtr, false); - // The MIR graph keeps track of the sreg for method pointer specially, so record that now. - method_sreg_ = method_temp->s_reg_low; - - InitializeBasicBlockDataFlow(); -} - -uint32_t MIRGraph::GetUseCountWeight(BasicBlock* bb) const { - // Each level of nesting adds *100 to count, up to 3 levels deep. - uint32_t depth = std::min(3U, static_cast(bb->nesting_depth)); - uint32_t weight = std::max(1U, depth * 100); - return weight; -} - -/* - * Count uses, weighting by loop nesting depth. This code only - * counts explicitly used s_regs. A later phase will add implicit - * counts for things such as Method*, null-checked references, etc. - */ -void MIRGraph::CountUses(BasicBlock* bb) { - if (bb->block_type != kDalvikByteCode) { - return; - } - uint32_t weight = GetUseCountWeight(bb); - for (MIR* mir = bb->first_mir_insn; (mir != nullptr); mir = mir->next) { - if (mir->ssa_rep == nullptr) { - continue; - } - for (int i = 0; i < mir->ssa_rep->num_uses; i++) { - int s_reg = mir->ssa_rep->uses[i]; - raw_use_counts_[s_reg] += 1u; - use_counts_[s_reg] += weight; - } - } -} - -/* Verify if all the successor is connected with all the claimed predecessors */ -bool MIRGraph::VerifyPredInfo(BasicBlock* bb) { - for (BasicBlockId pred_id : bb->predecessors) { - BasicBlock* pred_bb = GetBasicBlock(pred_id); - DCHECK(pred_bb != nullptr); - bool found = false; - if (pred_bb->taken == bb->id) { - found = true; - } else if (pred_bb->fall_through == bb->id) { - found = true; - } else if (pred_bb->successor_block_list_type != kNotUsed) { - for (SuccessorBlockInfo* successor_block_info : pred_bb->successor_blocks) { - BasicBlockId succ_bb = successor_block_info->block; - if (succ_bb == bb->id) { - found = true; - break; - } - } - } - if (found == false) { - char block_name1[BLOCK_NAME_LEN], block_name2[BLOCK_NAME_LEN]; - GetBlockName(bb, block_name1); - GetBlockName(pred_bb, block_name2); - DumpCFG("/sdcard/cfg/", false); - LOG(FATAL) << "Successor " << block_name1 << " not found from " - << block_name2; - } - } - return true; -} - -void MIRGraph::VerifyDataflow() { - /* Verify if all blocks are connected as claimed */ - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - VerifyPredInfo(bb); - } -} - -} // namespace art diff --git a/compiler/dex/mir_field_info.cc b/compiler/dex/mir_field_info.cc deleted file mode 100644 index 13bbc3e983..0000000000 --- a/compiler/dex/mir_field_info.cc +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mir_field_info.h" - -#include - -#include "base/logging.h" -#include "dex/verified_method.h" -#include "driver/compiler_driver.h" -#include "driver/compiler_driver-inl.h" -#include "mirror/class_loader.h" // Only to allow casts in Handle. -#include "mirror/dex_cache.h" // Only to allow casts in Handle. -#include "scoped_thread_state_change.h" -#include "handle_scope-inl.h" - -namespace art { - -void MirIFieldLoweringInfo::Resolve(const ScopedObjectAccess& soa, - CompilerDriver* compiler_driver, - const DexCompilationUnit* mUnit, - MirIFieldLoweringInfo* field_infos, size_t count) { - if (kIsDebugBuild) { - DCHECK(field_infos != nullptr); - DCHECK_NE(count, 0u); - for (auto it = field_infos, end = field_infos + count; it != end; ++it) { - MirIFieldLoweringInfo unresolved(it->field_idx_, it->MemAccessType(), it->IsQuickened()); - unresolved.field_offset_ = it->field_offset_; - unresolved.CheckEquals(*it); - } - } - - // We're going to resolve fields and check access in a tight loop. It's better to hold - // the lock and needed references once than re-acquiring them again and again. - StackHandleScope<3> hs(soa.Self()); - Handle dex_cache(hs.NewHandle(compiler_driver->GetDexCache(mUnit))); - Handle class_loader( - hs.NewHandle(compiler_driver->GetClassLoader(soa, mUnit))); - Handle referrer_class(hs.NewHandle( - compiler_driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, mUnit))); - const VerifiedMethod* const verified_method = mUnit->GetVerifiedMethod(); - // Even if the referrer class is unresolved (i.e. we're compiling a method without class - // definition) we still want to resolve fields and record all available info. - for (auto it = field_infos, end = field_infos + count; it != end; ++it) { - uint32_t field_idx; - ArtField* resolved_field; - if (!it->IsQuickened()) { - field_idx = it->field_idx_; - resolved_field = compiler_driver->ResolveField(soa, dex_cache, class_loader, mUnit, - field_idx, false); - } else { - const auto mir_offset = it->field_idx_; - // For quickened instructions, it->field_offset_ actually contains the mir offset. - // We need to use the de-quickening info to get dex file / field idx - auto* field_idx_ptr = verified_method->GetDequickenIndex(mir_offset); - CHECK(field_idx_ptr != nullptr); - field_idx = field_idx_ptr->index; - StackHandleScope<1> hs2(soa.Self()); - auto h_dex_cache = hs2.NewHandle(compiler_driver->FindDexCache(field_idx_ptr->dex_file)); - resolved_field = compiler_driver->ResolveFieldWithDexFile( - soa, h_dex_cache, class_loader, field_idx_ptr->dex_file, field_idx, false); - // Since we don't have a valid field index we can't go slow path later. - CHECK(resolved_field != nullptr); - } - if (UNLIKELY(resolved_field == nullptr)) { - continue; - } - compiler_driver->GetResolvedFieldDexFileLocation(resolved_field, - &it->declaring_dex_file_, &it->declaring_class_idx_, &it->declaring_field_idx_); - bool is_volatile = compiler_driver->IsFieldVolatile(resolved_field); - it->field_offset_ = compiler_driver->GetFieldOffset(resolved_field); - std::pair fast_path = compiler_driver->IsFastInstanceField( - dex_cache.Get(), referrer_class.Get(), resolved_field, field_idx); - it->flags_ = 0u | // Without kFlagIsStatic. - (it->flags_ & (kMemAccessTypeMask << kBitMemAccessTypeBegin)) | - (is_volatile ? kFlagIsVolatile : 0u) | - (fast_path.first ? kFlagFastGet : 0u) | - (fast_path.second ? kFlagFastPut : 0u); - } -} - -void MirSFieldLoweringInfo::Resolve(CompilerDriver* compiler_driver, - const DexCompilationUnit* mUnit, - MirSFieldLoweringInfo* field_infos, size_t count) { - if (kIsDebugBuild) { - DCHECK(field_infos != nullptr); - DCHECK_NE(count, 0u); - for (auto it = field_infos, end = field_infos + count; it != end; ++it) { - MirSFieldLoweringInfo unresolved(it->field_idx_, it->MemAccessType()); - // In 64-bit builds, there's padding after storage_index_, don't include it in memcmp. - size_t size = OFFSETOF_MEMBER(MirSFieldLoweringInfo, storage_index_) + - sizeof(it->storage_index_); - DCHECK_EQ(memcmp(&unresolved, &*it, size), 0); - } - } - - // We're going to resolve fields and check access in a tight loop. It's better to hold - // the lock and needed references once than re-acquiring them again and again. - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<3> hs(soa.Self()); - Handle dex_cache(hs.NewHandle(compiler_driver->GetDexCache(mUnit))); - Handle class_loader( - hs.NewHandle(compiler_driver->GetClassLoader(soa, mUnit))); - Handle referrer_class_handle(hs.NewHandle( - compiler_driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, mUnit))); - // Even if the referrer class is unresolved (i.e. we're compiling a method without class - // definition) we still want to resolve fields and record all available info. - - for (auto it = field_infos, end = field_infos + count; it != end; ++it) { - uint32_t field_idx = it->field_idx_; - ArtField* resolved_field = - compiler_driver->ResolveField(soa, dex_cache, class_loader, mUnit, field_idx, true); - if (UNLIKELY(resolved_field == nullptr)) { - continue; - } - compiler_driver->GetResolvedFieldDexFileLocation(resolved_field, - &it->declaring_dex_file_, &it->declaring_class_idx_, &it->declaring_field_idx_); - bool is_volatile = compiler_driver->IsFieldVolatile(resolved_field) ? 1u : 0u; - - mirror::Class* referrer_class = referrer_class_handle.Get(); - std::pair fast_path = compiler_driver->IsFastStaticField( - dex_cache.Get(), referrer_class, resolved_field, field_idx, &it->storage_index_); - uint16_t flags = kFlagIsStatic | - (it->flags_ & (kMemAccessTypeMask << kBitMemAccessTypeBegin)) | - (is_volatile ? kFlagIsVolatile : 0u) | - (fast_path.first ? kFlagFastGet : 0u) | - (fast_path.second ? kFlagFastPut : 0u); - if (fast_path.first) { - it->field_offset_ = compiler_driver->GetFieldOffset(resolved_field); - bool is_referrers_class = - compiler_driver->IsStaticFieldInReferrerClass(referrer_class, resolved_field); - bool is_class_initialized = - compiler_driver->IsStaticFieldsClassInitialized(referrer_class, resolved_field); - bool is_class_in_dex_cache = !is_referrers_class && // If referrer's class, we don't care. - compiler_driver->CanAssumeTypeIsPresentInDexCache(*dex_cache->GetDexFile(), - it->storage_index_); - flags |= (is_referrers_class ? kFlagIsReferrersClass : 0u) | - (is_class_initialized ? kFlagClassIsInitialized : 0u) | - (is_class_in_dex_cache ? kFlagClassIsInDexCache : 0u); - } - it->flags_ = flags; - } -} - -} // namespace art diff --git a/compiler/dex/mir_field_info.h b/compiler/dex/mir_field_info.h deleted file mode 100644 index b6dc27d26e..0000000000 --- a/compiler/dex/mir_field_info.h +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_MIR_FIELD_INFO_H_ -#define ART_COMPILER_DEX_MIR_FIELD_INFO_H_ - -#include "base/macros.h" -#include "dex_file.h" -#include "dex_instruction_utils.h" -#include "offsets.h" - -namespace art { - -class CompilerDriver; -class DexCompilationUnit; -class ScopedObjectAccess; - -/* - * Field info is calculated from the perspective of the compilation unit that accesses - * the field and stored in that unit's MIRGraph. Therefore it does not need to reference the - * dex file or method for which it has been calculated. However, we do store the declaring - * field index, class index and dex file of the resolved field to help distinguish between fields. - */ - -class MirFieldInfo { - public: - uint16_t FieldIndex() const { - return field_idx_; - } - void SetFieldIndex(uint16_t field_idx) { - field_idx_ = field_idx; - } - - bool IsStatic() const { - return (flags_ & kFlagIsStatic) != 0u; - } - - bool IsResolved() const { - return declaring_dex_file_ != nullptr; - } - - const DexFile* DeclaringDexFile() const { - return declaring_dex_file_; - } - void SetDeclaringDexFile(const DexFile* dex_file) { - declaring_dex_file_ = dex_file; - } - - uint16_t DeclaringClassIndex() const { - return declaring_class_idx_; - } - - uint16_t DeclaringFieldIndex() const { - return declaring_field_idx_; - } - - bool IsVolatile() const { - return (flags_ & kFlagIsVolatile) != 0u; - } - - // IGET_QUICK, IGET_BYTE_QUICK, ... - bool IsQuickened() const { - return (flags_ & kFlagIsQuickened) != 0u; - } - - DexMemAccessType MemAccessType() const { - return static_cast((flags_ >> kBitMemAccessTypeBegin) & kMemAccessTypeMask); - } - - void CheckEquals(const MirFieldInfo& other) const { - CHECK_EQ(field_idx_, other.field_idx_); - CHECK_EQ(flags_, other.flags_); - CHECK_EQ(declaring_field_idx_, other.declaring_field_idx_); - CHECK_EQ(declaring_class_idx_, other.declaring_class_idx_); - CHECK_EQ(declaring_dex_file_, other.declaring_dex_file_); - } - - protected: - enum { - kBitIsStatic = 0, - kBitIsVolatile, - kBitIsQuickened, - kBitMemAccessTypeBegin, - kBitMemAccessTypeEnd = kBitMemAccessTypeBegin + 3, // 3 bits for raw type. - kFieldInfoBitEnd = kBitMemAccessTypeEnd - }; - static constexpr uint16_t kFlagIsVolatile = 1u << kBitIsVolatile; - static constexpr uint16_t kFlagIsStatic = 1u << kBitIsStatic; - static constexpr uint16_t kFlagIsQuickened = 1u << kBitIsQuickened; - static constexpr uint16_t kMemAccessTypeMask = 7u; - static_assert((1u << (kBitMemAccessTypeEnd - kBitMemAccessTypeBegin)) - 1u == kMemAccessTypeMask, - "Invalid raw type mask"); - - MirFieldInfo(uint16_t field_idx, uint16_t flags, DexMemAccessType type) - : field_idx_(field_idx), - flags_(flags | static_cast(type) << kBitMemAccessTypeBegin), - declaring_field_idx_(0u), - declaring_class_idx_(0u), - declaring_dex_file_(nullptr) { - } - - // Make copy-ctor/assign/dtor protected to avoid slicing. - MirFieldInfo(const MirFieldInfo& other) = default; - MirFieldInfo& operator=(const MirFieldInfo& other) = default; - ~MirFieldInfo() = default; - - // The field index in the compiling method's dex file. - uint16_t field_idx_; - // Flags, for volatility and derived class data. - uint16_t flags_; - // The field index in the dex file that defines field, 0 if unresolved. - uint16_t declaring_field_idx_; - // The type index of the class declaring the field, 0 if unresolved. - uint16_t declaring_class_idx_; - // The dex file that defines the class containing the field and the field, null if unresolved. - const DexFile* declaring_dex_file_; -}; - -class MirIFieldLoweringInfo : public MirFieldInfo { - public: - // For each requested instance field retrieve the field's declaring location (dex file, class - // index and field index) and volatility and compute whether we can fast path the access - // with IGET/IPUT. For fast path fields, retrieve the field offset. - static void Resolve(const ScopedObjectAccess& soa, - CompilerDriver* compiler_driver, - const DexCompilationUnit* mUnit, - MirIFieldLoweringInfo* field_infos, - size_t count) - SHARED_REQUIRES(Locks::mutator_lock_); - - // Construct an unresolved instance field lowering info. - MirIFieldLoweringInfo(uint16_t field_idx, DexMemAccessType type, bool is_quickened) - : MirFieldInfo(field_idx, - kFlagIsVolatile | (is_quickened ? kFlagIsQuickened : 0u), - type), // Without kFlagIsStatic. - field_offset_(0u) { - } - - bool FastGet() const { - return (flags_ & kFlagFastGet) != 0u; - } - - bool FastPut() const { - return (flags_ & kFlagFastPut) != 0u; - } - - MemberOffset FieldOffset() const { - return field_offset_; - } - - void CheckEquals(const MirIFieldLoweringInfo& other) const { - MirFieldInfo::CheckEquals(other); - CHECK_EQ(field_offset_.Uint32Value(), other.field_offset_.Uint32Value()); - } - - private: - enum { - kBitFastGet = kFieldInfoBitEnd, - kBitFastPut, - kIFieldLoweringInfoBitEnd - }; - static_assert(kIFieldLoweringInfoBitEnd <= 16, "Too many flags"); - static constexpr uint16_t kFlagFastGet = 1u << kBitFastGet; - static constexpr uint16_t kFlagFastPut = 1u << kBitFastPut; - - // The member offset of the field, 0u if unresolved. - MemberOffset field_offset_; - - friend class NullCheckEliminationTest; - friend class GlobalValueNumberingTest; - friend class GvnDeadCodeEliminationTest; - friend class LocalValueNumberingTest; - friend class TypeInferenceTest; -}; - -class MirSFieldLoweringInfo : public MirFieldInfo { - public: - // For each requested static field retrieve the field's declaring location (dex file, class - // index and field index) and volatility and compute whether we can fast path the access with - // IGET/IPUT. For fast path fields (at least for IGET), retrieve the information needed for - // the field access, i.e. the field offset, whether the field is in the same class as the - // method being compiled, whether the declaring class can be safely assumed to be initialized - // and the type index of the declaring class in the compiled method's dex file. - static void Resolve(CompilerDriver* compiler_driver, const DexCompilationUnit* mUnit, - MirSFieldLoweringInfo* field_infos, size_t count) - REQUIRES(!Locks::mutator_lock_); - - // Construct an unresolved static field lowering info. - MirSFieldLoweringInfo(uint16_t field_idx, DexMemAccessType type) - : MirFieldInfo(field_idx, kFlagIsVolatile | kFlagIsStatic, type), - field_offset_(0u), - storage_index_(DexFile::kDexNoIndex) { - } - - bool FastGet() const { - return (flags_ & kFlagFastGet) != 0u; - } - - bool FastPut() const { - return (flags_ & kFlagFastPut) != 0u; - } - - bool IsReferrersClass() const { - return (flags_ & kFlagIsReferrersClass) != 0u; - } - - bool IsClassInitialized() const { - return (flags_ & kFlagClassIsInitialized) != 0u; - } - - bool IsClassInDexCache() const { - return (flags_ & kFlagClassIsInDexCache) != 0u; - } - - MemberOffset FieldOffset() const { - return field_offset_; - } - - uint32_t StorageIndex() const { - return storage_index_; - } - - private: - enum { - kBitFastGet = kFieldInfoBitEnd, - kBitFastPut, - kBitIsReferrersClass, - kBitClassIsInitialized, - kBitClassIsInDexCache, - kSFieldLoweringInfoBitEnd - }; - static_assert(kSFieldLoweringInfoBitEnd <= 16, "Too many flags"); - static constexpr uint16_t kFlagFastGet = 1u << kBitFastGet; - static constexpr uint16_t kFlagFastPut = 1u << kBitFastPut; - static constexpr uint16_t kFlagIsReferrersClass = 1u << kBitIsReferrersClass; - static constexpr uint16_t kFlagClassIsInitialized = 1u << kBitClassIsInitialized; - static constexpr uint16_t kFlagClassIsInDexCache = 1u << kBitClassIsInDexCache; - - // The member offset of the field, 0u if unresolved. - MemberOffset field_offset_; - // The type index of the declaring class in the compiling method's dex file, - // -1 if the field is unresolved or there's no appropriate TypeId in that dex file. - uint32_t storage_index_; - - friend class ClassInitCheckEliminationTest; - friend class GlobalValueNumberingTest; - friend class GvnDeadCodeEliminationTest; - friend class LocalValueNumberingTest; - friend class TypeInferenceTest; -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_MIR_FIELD_INFO_H_ diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc deleted file mode 100644 index 6dc148dfdb..0000000000 --- a/compiler/dex/mir_graph.cc +++ /dev/null @@ -1,2589 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mir_graph.h" - -#include -#include -#include - -#include "base/bit_vector-inl.h" -#include "base/logging.h" -#include "base/stl_util.h" -#include "base/stringprintf.h" -#include "base/scoped_arena_containers.h" -#include "compiler_ir.h" -#include "dex_file-inl.h" -#include "dex_flags.h" -#include "dex_instruction-inl.h" -#include "driver/compiler_driver.h" -#include "driver/dex_compilation_unit.h" -#include "dex/quick/quick_compiler.h" -#include "leb128.h" -#include "pass_driver_me_post_opt.h" -#include "stack.h" -#include "utils.h" - -namespace art { - -#define MAX_PATTERN_LEN 5 - -const char* MIRGraph::extended_mir_op_names_[kMirOpLast - kMirOpFirst] = { - "Phi", - "Copy", - "FusedCmplFloat", - "FusedCmpgFloat", - "FusedCmplDouble", - "FusedCmpgDouble", - "FusedCmpLong", - "Nop", - "OpNullCheck", - "OpRangeCheck", - "OpDivZeroCheck", - "Check", - "Select", - "ConstVector", - "MoveVector", - "PackedMultiply", - "PackedAddition", - "PackedSubtract", - "PackedShiftLeft", - "PackedSignedShiftRight", - "PackedUnsignedShiftRight", - "PackedAnd", - "PackedOr", - "PackedXor", - "PackedAddReduce", - "PackedReduce", - "PackedSet", - "ReserveVectorRegisters", - "ReturnVectorRegisters", - "MemBarrier", - "PackedArrayGet", - "PackedArrayPut", - "MaddInt", - "MsubInt", - "MaddLong", - "MsubLong", -}; - -MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena) - : reg_location_(nullptr), - block_id_map_(std::less(), arena->Adapter()), - cu_(cu), - ssa_base_vregs_(arena->Adapter(kArenaAllocSSAToDalvikMap)), - ssa_subscripts_(arena->Adapter(kArenaAllocSSAToDalvikMap)), - vreg_to_ssa_map_(nullptr), - ssa_last_defs_(nullptr), - is_constant_v_(nullptr), - constant_values_(nullptr), - use_counts_(arena->Adapter()), - raw_use_counts_(arena->Adapter()), - num_reachable_blocks_(0), - max_num_reachable_blocks_(0), - dfs_orders_up_to_date_(false), - domination_up_to_date_(false), - mir_ssa_rep_up_to_date_(false), - topological_order_up_to_date_(false), - dfs_order_(arena->Adapter(kArenaAllocDfsPreOrder)), - dfs_post_order_(arena->Adapter(kArenaAllocDfsPostOrder)), - dom_post_order_traversal_(arena->Adapter(kArenaAllocDomPostOrder)), - topological_order_(arena->Adapter(kArenaAllocTopologicalSortOrder)), - topological_order_loop_ends_(arena->Adapter(kArenaAllocTopologicalSortOrder)), - topological_order_indexes_(arena->Adapter(kArenaAllocTopologicalSortOrder)), - topological_order_loop_head_stack_(arena->Adapter(kArenaAllocTopologicalSortOrder)), - max_nested_loops_(0u), - i_dom_list_(nullptr), - temp_scoped_alloc_(), - block_list_(arena->Adapter(kArenaAllocBBList)), - try_block_addr_(nullptr), - entry_block_(nullptr), - exit_block_(nullptr), - current_code_item_(nullptr), - m_units_(arena->Adapter()), - method_stack_(arena->Adapter()), - current_method_(kInvalidEntry), - current_offset_(kInvalidEntry), - def_count_(0), - opcode_count_(nullptr), - num_ssa_regs_(0), - extended_basic_blocks_(arena->Adapter()), - method_sreg_(0), - attributes_(METHOD_IS_LEAF), // Start with leaf assumption, change on encountering invoke. - checkstats_(nullptr), - arena_(arena), - backward_branches_(0), - forward_branches_(0), - num_non_special_compiler_temps_(0), - max_available_special_compiler_temps_(1), // We only need the method ptr as a special temp for now. - requested_backend_temp_(false), - compiler_temps_committed_(false), - punt_to_interpreter_(false), - merged_df_flags_(0u), - ifield_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)), - sfield_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)), - method_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)), - suspend_checks_in_loops_(nullptr) { - memset(&temp_, 0, sizeof(temp_)); - use_counts_.reserve(256); - raw_use_counts_.reserve(256); - block_list_.reserve(100); - try_block_addr_ = new (arena_) ArenaBitVector(arena_, 0, true /* expandable */); - - - if (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) { - // X86 requires a temp to keep track of the method address. - // TODO For x86_64, addressing can be done with RIP. When that is implemented, - // this needs to be updated to reserve 0 temps for BE. - max_available_non_special_compiler_temps_ = cu_->target64 ? 2 : 1; - reserved_temps_for_backend_ = max_available_non_special_compiler_temps_; - } else { - // Other architectures do not have a known lower bound for non-special temps. - // We allow the update of the max to happen at BE initialization stage and simply set 0 for now. - max_available_non_special_compiler_temps_ = 0; - reserved_temps_for_backend_ = 0; - } -} - -MIRGraph::~MIRGraph() { - STLDeleteElements(&block_list_); - STLDeleteElements(&m_units_); -} - -/* - * Parse an instruction, return the length of the instruction - */ -int MIRGraph::ParseInsn(const uint16_t* code_ptr, MIR::DecodedInstruction* decoded_instruction) { - const Instruction* inst = Instruction::At(code_ptr); - decoded_instruction->opcode = inst->Opcode(); - decoded_instruction->vA = inst->HasVRegA() ? inst->VRegA() : 0; - decoded_instruction->vB = inst->HasVRegB() ? inst->VRegB() : 0; - decoded_instruction->vB_wide = inst->HasWideVRegB() ? inst->WideVRegB() : 0; - decoded_instruction->vC = inst->HasVRegC() ? inst->VRegC() : 0; - if (inst->HasVarArgs35c()) { - inst->GetVarArgs(decoded_instruction->arg); - } - return inst->SizeInCodeUnits(); -} - - -/* Split an existing block from the specified code offset into two */ -BasicBlock* MIRGraph::SplitBlock(DexOffset code_offset, - BasicBlock* orig_block, BasicBlock** immed_pred_block_p) { - DCHECK_GT(code_offset, orig_block->start_offset); - MIR* insn = orig_block->first_mir_insn; - MIR* prev = nullptr; // Will be set to instruction before split. - while (insn) { - if (insn->offset == code_offset) break; - prev = insn; - insn = insn->next; - } - if (insn == nullptr) { - LOG(FATAL) << "Break split failed"; - } - // Now insn is at the instruction where we want to split, namely - // insn will be the first instruction of the "bottom" block. - // Similarly, prev will be the last instruction of the "top" block - - BasicBlock* bottom_block = CreateNewBB(kDalvikByteCode); - - bottom_block->start_offset = code_offset; - bottom_block->first_mir_insn = insn; - bottom_block->last_mir_insn = orig_block->last_mir_insn; - - /* If this block was terminated by a return, conditional branch or throw, - * the flag needs to go with the bottom block - */ - bottom_block->terminated_by_return = orig_block->terminated_by_return; - orig_block->terminated_by_return = false; - - bottom_block->conditional_branch = orig_block->conditional_branch; - orig_block->conditional_branch = false; - - bottom_block->explicit_throw = orig_block->explicit_throw; - orig_block->explicit_throw = false; - - /* Handle the taken path */ - bottom_block->taken = orig_block->taken; - if (bottom_block->taken != NullBasicBlockId) { - orig_block->taken = NullBasicBlockId; - BasicBlock* bb_taken = GetBasicBlock(bottom_block->taken); - bb_taken->ErasePredecessor(orig_block->id); - bb_taken->predecessors.push_back(bottom_block->id); - } - - /* Handle the fallthrough path */ - bottom_block->fall_through = orig_block->fall_through; - orig_block->fall_through = bottom_block->id; - bottom_block->predecessors.push_back(orig_block->id); - if (bottom_block->fall_through != NullBasicBlockId) { - BasicBlock* bb_fall_through = GetBasicBlock(bottom_block->fall_through); - bb_fall_through->ErasePredecessor(orig_block->id); - bb_fall_through->predecessors.push_back(bottom_block->id); - } - - /* Handle the successor list */ - if (orig_block->successor_block_list_type != kNotUsed) { - bottom_block->successor_block_list_type = orig_block->successor_block_list_type; - bottom_block->successor_blocks.swap(orig_block->successor_blocks); - orig_block->successor_block_list_type = kNotUsed; - DCHECK(orig_block->successor_blocks.empty()); // Empty after the swap() above. - for (SuccessorBlockInfo* successor_block_info : bottom_block->successor_blocks) { - BasicBlock* bb = GetBasicBlock(successor_block_info->block); - if (bb != nullptr) { - bb->ErasePredecessor(orig_block->id); - bb->predecessors.push_back(bottom_block->id); - } - } - } - - orig_block->last_mir_insn = prev; - prev->next = nullptr; - - /* - * Update the immediate predecessor block pointer so that outgoing edges - * can be applied to the proper block. - */ - if (immed_pred_block_p) { - DCHECK_EQ(*immed_pred_block_p, orig_block); - *immed_pred_block_p = bottom_block; - } - - // Associate dex instructions in the bottom block with the new container. - DCHECK(insn != nullptr); - DCHECK(insn != orig_block->first_mir_insn); - DCHECK(insn == bottom_block->first_mir_insn); - DCHECK_EQ(insn->offset, bottom_block->start_offset); - // Scan the "bottom" instructions, remapping them to the - // newly created "bottom" block. - MIR* p = insn; - p->bb = bottom_block->id; - while (p != bottom_block->last_mir_insn) { - p = p->next; - DCHECK(p != nullptr); - p->bb = bottom_block->id; - } - - return bottom_block; -} - -/* - * Given a code offset, find out the block that starts with it. If the offset - * is in the middle of an existing block, split it into two. If immed_pred_block_p - * is not non-null and is the block being split, update *immed_pred_block_p to - * point to the bottom block so that outgoing edges can be set up properly - * (by the caller) - * Utilizes a map for fast lookup of the typical cases. - */ -BasicBlock* MIRGraph::FindBlock(DexOffset code_offset, bool create, - BasicBlock** immed_pred_block_p, - ScopedArenaVector* dex_pc_to_block_map) { - if (UNLIKELY(code_offset >= current_code_item_->insns_size_in_code_units_)) { - // There can be a fall-through out of the method code. We shall record such a block - // here (assuming create==true) and check that it's dead at the end of InlineMethod(). - // Though we're only aware of the cases where code_offset is exactly the same as - // insns_size_in_code_units_, treat greater code_offset the same just in case. - code_offset = current_code_item_->insns_size_in_code_units_; - } - - int block_id = (*dex_pc_to_block_map)[code_offset]; - BasicBlock* bb = GetBasicBlock(block_id); - - if ((bb != nullptr) && (bb->start_offset == code_offset)) { - // Does this containing block start with the desired instruction? - return bb; - } - - // No direct hit. - if (!create) { - return nullptr; - } - - if (bb != nullptr) { - // The target exists somewhere in an existing block. - BasicBlock* bottom_block = SplitBlock(code_offset, bb, bb == *immed_pred_block_p ? immed_pred_block_p : nullptr); - DCHECK(bottom_block != nullptr); - MIR* p = bottom_block->first_mir_insn; - BasicBlock* orig_block = bb; - DCHECK_EQ((*dex_pc_to_block_map)[p->offset], orig_block->id); - // Scan the "bottom" instructions, remapping them to the - // newly created "bottom" block. - (*dex_pc_to_block_map)[p->offset] = bottom_block->id; - while (p != bottom_block->last_mir_insn) { - p = p->next; - DCHECK(p != nullptr); - int opcode = p->dalvikInsn.opcode; - /* - * Some messiness here to ensure that we only enter real opcodes and only the - * first half of a potentially throwing instruction that has been split into - * CHECK and work portions. Since the 2nd half of a split operation is always - * the first in a BasicBlock, we can't hit it here. - */ - if ((opcode == kMirOpCheck) || !MIR::DecodedInstruction::IsPseudoMirOp(opcode)) { - BasicBlockId mapped_id = (*dex_pc_to_block_map)[p->offset]; - // At first glance the instructions should all be mapped to orig_block. - // However, multiple instructions may correspond to the same dex, hence an earlier - // instruction may have already moved the mapping for dex to bottom_block. - DCHECK((mapped_id == orig_block->id) || (mapped_id == bottom_block->id)); - (*dex_pc_to_block_map)[p->offset] = bottom_block->id; - } - } - return bottom_block; - } - - // Create a new block. - bb = CreateNewBB(kDalvikByteCode); - bb->start_offset = code_offset; - (*dex_pc_to_block_map)[bb->start_offset] = bb->id; - return bb; -} - - -/* Identify code range in try blocks and set up the empty catch blocks */ -void MIRGraph::ProcessTryCatchBlocks(ScopedArenaVector* dex_pc_to_block_map) { - int tries_size = current_code_item_->tries_size_; - DexOffset offset; - - if (tries_size == 0) { - return; - } - - for (int i = 0; i < tries_size; i++) { - const DexFile::TryItem* pTry = - DexFile::GetTryItems(*current_code_item_, i); - DexOffset start_offset = pTry->start_addr_; - DexOffset end_offset = start_offset + pTry->insn_count_; - for (offset = start_offset; offset < end_offset; offset++) { - try_block_addr_->SetBit(offset); - } - } - - // Iterate over each of the handlers to enqueue the empty Catch blocks. - const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*current_code_item_, 0); - uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); - for (uint32_t idx = 0; idx < handlers_size; idx++) { - CatchHandlerIterator iterator(handlers_ptr); - for (; iterator.HasNext(); iterator.Next()) { - uint32_t address = iterator.GetHandlerAddress(); - FindBlock(address, true /*create*/, /* immed_pred_block_p */ nullptr, dex_pc_to_block_map); - } - handlers_ptr = iterator.EndDataPointer(); - } -} - -bool MIRGraph::IsBadMonitorExitCatch(NarrowDexOffset monitor_exit_offset, - NarrowDexOffset catch_offset) { - // Catches for monitor-exit during stack unwinding have the pattern - // move-exception (move)* (goto)? monitor-exit throw - // In the currently generated dex bytecode we see these catching a bytecode range including - // either its own or an identical monitor-exit, http://b/15745363 . This function checks if - // it's the case for a given monitor-exit and catch block so that we can ignore it. - // (We don't want to ignore all monitor-exit catches since one could enclose a synchronized - // block in a try-block and catch the NPE, Error or Throwable and we should let it through; - // even though a throwing monitor-exit certainly indicates a bytecode error.) - const Instruction* monitor_exit = Instruction::At(current_code_item_->insns_ + monitor_exit_offset); - DCHECK(monitor_exit->Opcode() == Instruction::MONITOR_EXIT); - int monitor_reg = monitor_exit->VRegA_11x(); - const Instruction* check_insn = Instruction::At(current_code_item_->insns_ + catch_offset); - if (check_insn->Opcode() == Instruction::MOVE_EXCEPTION) { - if (check_insn->VRegA_11x() == monitor_reg) { - // Unexpected move-exception to the same register. Probably not the pattern we're looking for. - return false; - } - check_insn = check_insn->Next(); - } - while (true) { - int dest = -1; - bool wide = false; - switch (check_insn->Opcode()) { - case Instruction::MOVE_WIDE: - wide = true; - FALLTHROUGH_INTENDED; - case Instruction::MOVE_OBJECT: - case Instruction::MOVE: - dest = check_insn->VRegA_12x(); - break; - - case Instruction::MOVE_WIDE_FROM16: - wide = true; - FALLTHROUGH_INTENDED; - case Instruction::MOVE_OBJECT_FROM16: - case Instruction::MOVE_FROM16: - dest = check_insn->VRegA_22x(); - break; - - case Instruction::MOVE_WIDE_16: - wide = true; - FALLTHROUGH_INTENDED; - case Instruction::MOVE_OBJECT_16: - case Instruction::MOVE_16: - dest = check_insn->VRegA_32x(); - break; - - case Instruction::GOTO: - case Instruction::GOTO_16: - case Instruction::GOTO_32: - check_insn = check_insn->RelativeAt(check_insn->GetTargetOffset()); - FALLTHROUGH_INTENDED; - default: - return check_insn->Opcode() == Instruction::MONITOR_EXIT && - check_insn->VRegA_11x() == monitor_reg; - } - - if (dest == monitor_reg || (wide && dest + 1 == monitor_reg)) { - return false; - } - - check_insn = check_insn->Next(); - } -} - -/* Process instructions with the kBranch flag */ -BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, - int width, int flags, const uint16_t* code_ptr, - const uint16_t* code_end, - ScopedArenaVector* dex_pc_to_block_map) { - DexOffset target = cur_offset; - switch (insn->dalvikInsn.opcode) { - case Instruction::GOTO: - case Instruction::GOTO_16: - case Instruction::GOTO_32: - target += insn->dalvikInsn.vA; - break; - case Instruction::IF_EQ: - case Instruction::IF_NE: - case Instruction::IF_LT: - case Instruction::IF_GE: - case Instruction::IF_GT: - case Instruction::IF_LE: - cur_block->conditional_branch = true; - target += insn->dalvikInsn.vC; - break; - case Instruction::IF_EQZ: - case Instruction::IF_NEZ: - case Instruction::IF_LTZ: - case Instruction::IF_GEZ: - case Instruction::IF_GTZ: - case Instruction::IF_LEZ: - cur_block->conditional_branch = true; - target += insn->dalvikInsn.vB; - break; - default: - LOG(FATAL) << "Unexpected opcode(" << insn->dalvikInsn.opcode << ") with kBranch set"; - } - CountBranch(target); - BasicBlock* taken_block = FindBlock(target, /* create */ true, - /* immed_pred_block_p */ &cur_block, - dex_pc_to_block_map); - DCHECK(taken_block != nullptr); - cur_block->taken = taken_block->id; - taken_block->predecessors.push_back(cur_block->id); - - /* Always terminate the current block for conditional branches */ - if (flags & Instruction::kContinue) { - BasicBlock* fallthrough_block = FindBlock(cur_offset + width, - /* create */ - true, - /* immed_pred_block_p */ - &cur_block, - dex_pc_to_block_map); - DCHECK(fallthrough_block != nullptr); - cur_block->fall_through = fallthrough_block->id; - fallthrough_block->predecessors.push_back(cur_block->id); - } else if (code_ptr < code_end) { - FindBlock(cur_offset + width, /* create */ true, /* immed_pred_block_p */ nullptr, dex_pc_to_block_map); - } - return cur_block; -} - -/* Process instructions with the kSwitch flag */ -BasicBlock* MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, - int width, int flags ATTRIBUTE_UNUSED, - ScopedArenaVector* dex_pc_to_block_map) { - const uint16_t* switch_data = - reinterpret_cast(GetCurrentInsns() + cur_offset + - static_cast(insn->dalvikInsn.vB)); - int size; - const int* keyTable; - const int* target_table; - int i; - int first_key; - - /* - * Packed switch data format: - * ushort ident = 0x0100 magic value - * ushort size number of entries in the table - * int first_key first (and lowest) switch case value - * int targets[size] branch targets, relative to switch opcode - * - * Total size is (4+size*2) 16-bit code units. - */ - if (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) { - DCHECK_EQ(static_cast(switch_data[0]), - static_cast(Instruction::kPackedSwitchSignature)); - size = switch_data[1]; - first_key = switch_data[2] | (switch_data[3] << 16); - target_table = reinterpret_cast(&switch_data[4]); - keyTable = nullptr; // Make the compiler happy. - /* - * Sparse switch data format: - * ushort ident = 0x0200 magic value - * ushort size number of entries in the table; > 0 - * int keys[size] keys, sorted low-to-high; 32-bit aligned - * int targets[size] branch targets, relative to switch opcode - * - * Total size is (2+size*4) 16-bit code units. - */ - } else { - DCHECK_EQ(static_cast(switch_data[0]), - static_cast(Instruction::kSparseSwitchSignature)); - size = switch_data[1]; - keyTable = reinterpret_cast(&switch_data[2]); - target_table = reinterpret_cast(&switch_data[2 + size*2]); - first_key = 0; // To make the compiler happy. - } - - if (cur_block->successor_block_list_type != kNotUsed) { - LOG(FATAL) << "Successor block list already in use: " - << static_cast(cur_block->successor_block_list_type); - } - cur_block->successor_block_list_type = - (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ? kPackedSwitch : kSparseSwitch; - cur_block->successor_blocks.reserve(size); - - for (i = 0; i < size; i++) { - BasicBlock* case_block = FindBlock(cur_offset + target_table[i], /* create */ true, - /* immed_pred_block_p */ &cur_block, - dex_pc_to_block_map); - DCHECK(case_block != nullptr); - SuccessorBlockInfo* successor_block_info = - static_cast(arena_->Alloc(sizeof(SuccessorBlockInfo), - kArenaAllocSuccessors)); - successor_block_info->block = case_block->id; - successor_block_info->key = - (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ? - first_key + i : keyTable[i]; - cur_block->successor_blocks.push_back(successor_block_info); - case_block->predecessors.push_back(cur_block->id); - } - - /* Fall-through case */ - BasicBlock* fallthrough_block = FindBlock(cur_offset + width, /* create */ true, - /* immed_pred_block_p */ nullptr, - dex_pc_to_block_map); - DCHECK(fallthrough_block != nullptr); - cur_block->fall_through = fallthrough_block->id; - fallthrough_block->predecessors.push_back(cur_block->id); - return cur_block; -} - -/* Process instructions with the kThrow flag */ -BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, - MIR* insn, - DexOffset cur_offset, - int width, - int flags ATTRIBUTE_UNUSED, - ArenaBitVector* try_block_addr, - const uint16_t* code_ptr, - const uint16_t* code_end, - ScopedArenaVector* dex_pc_to_block_map) { - bool in_try_block = try_block_addr->IsBitSet(cur_offset); - bool is_throw = (insn->dalvikInsn.opcode == Instruction::THROW); - - /* In try block */ - if (in_try_block) { - CatchHandlerIterator iterator(*current_code_item_, cur_offset); - - if (cur_block->successor_block_list_type != kNotUsed) { - LOG(INFO) << PrettyMethod(cu_->method_idx, *cu_->dex_file); - LOG(FATAL) << "Successor block list already in use: " - << static_cast(cur_block->successor_block_list_type); - } - - for (; iterator.HasNext(); iterator.Next()) { - BasicBlock* catch_block = FindBlock(iterator.GetHandlerAddress(), false /* create */, - nullptr /* immed_pred_block_p */, - dex_pc_to_block_map); - if (insn->dalvikInsn.opcode == Instruction::MONITOR_EXIT && - IsBadMonitorExitCatch(insn->offset, catch_block->start_offset)) { - // Don't allow monitor-exit to catch its own exception, http://b/15745363 . - continue; - } - if (cur_block->successor_block_list_type == kNotUsed) { - cur_block->successor_block_list_type = kCatch; - } - catch_block->catch_entry = true; - if (kIsDebugBuild) { - catches_.insert(catch_block->start_offset); - } - SuccessorBlockInfo* successor_block_info = reinterpret_cast - (arena_->Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors)); - successor_block_info->block = catch_block->id; - successor_block_info->key = iterator.GetHandlerTypeIndex(); - cur_block->successor_blocks.push_back(successor_block_info); - catch_block->predecessors.push_back(cur_block->id); - } - in_try_block = (cur_block->successor_block_list_type != kNotUsed); - } - bool build_all_edges = - (cu_->disable_opt & (1 << kSuppressExceptionEdges)) || is_throw || in_try_block; - if (!in_try_block && build_all_edges) { - BasicBlock* eh_block = CreateNewBB(kExceptionHandling); - cur_block->taken = eh_block->id; - eh_block->start_offset = cur_offset; - eh_block->predecessors.push_back(cur_block->id); - } - - if (is_throw) { - cur_block->explicit_throw = true; - if (code_ptr < code_end) { - // Force creation of new block following THROW via side-effect. - FindBlock(cur_offset + width, /* create */ true, /* immed_pred_block_p */ nullptr, dex_pc_to_block_map); - } - if (!in_try_block) { - // Don't split a THROW that can't rethrow - we're done. - return cur_block; - } - } - - if (!build_all_edges) { - /* - * Even though there is an exception edge here, control cannot return to this - * method. Thus, for the purposes of dataflow analysis and optimization, we can - * ignore the edge. Doing this reduces compile time, and increases the scope - * of the basic-block level optimization pass. - */ - return cur_block; - } - - /* - * Split the potentially-throwing instruction into two parts. - * The first half will be a pseudo-op that captures the exception - * edges and terminates the basic block. It always falls through. - * Then, create a new basic block that begins with the throwing instruction - * (minus exceptions). Note: this new basic block must NOT be entered into - * the block_map. If the potentially-throwing instruction is the target of a - * future branch, we need to find the check psuedo half. The new - * basic block containing the work portion of the instruction should - * only be entered via fallthrough from the block containing the - * pseudo exception edge MIR. Note also that this new block is - * not automatically terminated after the work portion, and may - * contain following instructions. - * - * Note also that the dex_pc_to_block_map entry for the potentially - * throwing instruction will refer to the original basic block. - */ - BasicBlock* new_block = CreateNewBB(kDalvikByteCode); - new_block->start_offset = insn->offset; - cur_block->fall_through = new_block->id; - new_block->predecessors.push_back(cur_block->id); - MIR* new_insn = NewMIR(); - *new_insn = *insn; - insn->dalvikInsn.opcode = static_cast(kMirOpCheck); - // Associate the two halves. - insn->meta.throw_insn = new_insn; - new_block->AppendMIR(new_insn); - return new_block; -} - -/* Parse a Dex method and insert it into the MIRGraph at the current insert point. */ -void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_flags, - InvokeType invoke_type ATTRIBUTE_UNUSED, uint16_t class_def_idx, - uint32_t method_idx, jobject class_loader, const DexFile& dex_file, - Handle dex_cache) { - current_code_item_ = code_item; - method_stack_.push_back(std::make_pair(current_method_, current_offset_)); - current_method_ = m_units_.size(); - current_offset_ = 0; - // TODO: will need to snapshot stack image and use that as the mir context identification. - m_units_.push_back(new (arena_) DexCompilationUnit( - cu_, class_loader, Runtime::Current()->GetClassLinker(), dex_file, current_code_item_, - class_def_idx, method_idx, access_flags, - cu_->compiler_driver->GetVerifiedMethod(&dex_file, method_idx), dex_cache)); - const uint16_t* code_ptr = current_code_item_->insns_; - const uint16_t* code_end = - current_code_item_->insns_ + current_code_item_->insns_size_in_code_units_; - - // TODO: need to rework expansion of block list & try_block_addr when inlining activated. - // TUNING: use better estimate of basic blocks for following resize. - block_list_.reserve(block_list_.size() + current_code_item_->insns_size_in_code_units_); - // FindBlock lookup cache. - ScopedArenaAllocator allocator(&cu_->arena_stack); - ScopedArenaVector dex_pc_to_block_map(allocator.Adapter()); - dex_pc_to_block_map.resize(current_code_item_->insns_size_in_code_units_ + - 1 /* Fall-through on last insn; dead or punt to interpreter. */); - - // TODO: replace with explicit resize routine. Using automatic extension side effect for now. - try_block_addr_->SetBit(current_code_item_->insns_size_in_code_units_); - try_block_addr_->ClearBit(current_code_item_->insns_size_in_code_units_); - - // If this is the first method, set up default entry and exit blocks. - if (current_method_ == 0) { - DCHECK(entry_block_ == nullptr); - DCHECK(exit_block_ == nullptr); - DCHECK_EQ(GetNumBlocks(), 0U); - // Use id 0 to represent a null block. - BasicBlock* null_block = CreateNewBB(kNullBlock); - DCHECK_EQ(null_block->id, NullBasicBlockId); - null_block->hidden = true; - entry_block_ = CreateNewBB(kEntryBlock); - exit_block_ = CreateNewBB(kExitBlock); - } else { - UNIMPLEMENTED(FATAL) << "Nested inlining not implemented."; - /* - * Will need to manage storage for ins & outs, push prevous state and update - * insert point. - */ - } - - /* Current block to record parsed instructions */ - BasicBlock* cur_block = CreateNewBB(kDalvikByteCode); - DCHECK_EQ(current_offset_, 0U); - cur_block->start_offset = current_offset_; - // TODO: for inlining support, insert at the insert point rather than entry block. - entry_block_->fall_through = cur_block->id; - cur_block->predecessors.push_back(entry_block_->id); - - /* Identify code range in try blocks and set up the empty catch blocks */ - ProcessTryCatchBlocks(&dex_pc_to_block_map); - - uint64_t merged_df_flags = 0u; - - /* Parse all instructions and put them into containing basic blocks */ - while (code_ptr < code_end) { - MIR *insn = NewMIR(); - insn->offset = current_offset_; - insn->m_unit_index = current_method_; - int width = ParseInsn(code_ptr, &insn->dalvikInsn); - Instruction::Code opcode = insn->dalvikInsn.opcode; - if (opcode_count_ != nullptr) { - opcode_count_[static_cast(opcode)]++; - } - - int flags = insn->dalvikInsn.FlagsOf(); - int verify_flags = Instruction::VerifyFlagsOf(insn->dalvikInsn.opcode); - - uint64_t df_flags = GetDataFlowAttributes(insn); - merged_df_flags |= df_flags; - - if (df_flags & DF_HAS_DEFS) { - def_count_ += (df_flags & DF_A_WIDE) ? 2 : 1; - } - - if (df_flags & DF_LVN) { - cur_block->use_lvn = true; // Run local value numbering on this basic block. - } - - // Check for inline data block signatures. - if (opcode == Instruction::NOP) { - // A simple NOP will have a width of 1 at this point, embedded data NOP > 1. - if ((width == 1) && ((current_offset_ & 0x1) == 0x1) && ((code_end - code_ptr) > 1)) { - // Could be an aligning nop. If an embedded data NOP follows, treat pair as single unit. - uint16_t following_raw_instruction = code_ptr[1]; - if ((following_raw_instruction == Instruction::kSparseSwitchSignature) || - (following_raw_instruction == Instruction::kPackedSwitchSignature) || - (following_raw_instruction == Instruction::kArrayDataSignature)) { - width += Instruction::At(code_ptr + 1)->SizeInCodeUnits(); - } - } - if (width == 1) { - // It is a simple nop - treat normally. - cur_block->AppendMIR(insn); - } else { - DCHECK(cur_block->fall_through == NullBasicBlockId); - DCHECK(cur_block->taken == NullBasicBlockId); - // Unreachable instruction, mark for no continuation and end basic block. - flags &= ~Instruction::kContinue; - FindBlock(current_offset_ + width, /* create */ true, - /* immed_pred_block_p */ nullptr, &dex_pc_to_block_map); - } - } else { - cur_block->AppendMIR(insn); - } - - // Associate the starting dex_pc for this opcode with its containing basic block. - dex_pc_to_block_map[insn->offset] = cur_block->id; - - code_ptr += width; - - if (flags & Instruction::kBranch) { - cur_block = ProcessCanBranch(cur_block, insn, current_offset_, - width, flags, code_ptr, code_end, &dex_pc_to_block_map); - } else if (flags & Instruction::kReturn) { - cur_block->terminated_by_return = true; - cur_block->fall_through = exit_block_->id; - exit_block_->predecessors.push_back(cur_block->id); - /* - * Terminate the current block if there are instructions - * afterwards. - */ - if (code_ptr < code_end) { - /* - * Create a fallthrough block for real instructions - * (incl. NOP). - */ - FindBlock(current_offset_ + width, /* create */ true, - /* immed_pred_block_p */ nullptr, &dex_pc_to_block_map); - } - } else if (flags & Instruction::kThrow) { - cur_block = ProcessCanThrow(cur_block, insn, current_offset_, width, flags, try_block_addr_, - code_ptr, code_end, &dex_pc_to_block_map); - } else if (flags & Instruction::kSwitch) { - cur_block = ProcessCanSwitch(cur_block, insn, current_offset_, width, - flags, &dex_pc_to_block_map); - } - if (verify_flags & Instruction::kVerifyVarArgRange || - verify_flags & Instruction::kVerifyVarArgRangeNonZero) { - /* - * The Quick backend's runtime model includes a gap between a method's - * argument ("in") vregs and the rest of its vregs. Handling a range instruction - * which spans the gap is somewhat complicated, and should not happen - * in normal usage of dx. Punt to the interpreter. - */ - int first_reg_in_range = insn->dalvikInsn.vC; - int last_reg_in_range = first_reg_in_range + insn->dalvikInsn.vA - 1; - if (IsInVReg(first_reg_in_range) != IsInVReg(last_reg_in_range)) { - punt_to_interpreter_ = true; - } - } - current_offset_ += width; - BasicBlock* next_block = FindBlock(current_offset_, /* create */ false, - /* immed_pred_block_p */ nullptr, - &dex_pc_to_block_map); - if (next_block) { - /* - * The next instruction could be the target of a previously parsed - * forward branch so a block is already created. If the current - * instruction is not an unconditional branch, connect them through - * the fall-through link. - */ - DCHECK(cur_block->fall_through == NullBasicBlockId || - GetBasicBlock(cur_block->fall_through) == next_block || - GetBasicBlock(cur_block->fall_through) == exit_block_); - - if ((cur_block->fall_through == NullBasicBlockId) && (flags & Instruction::kContinue)) { - cur_block->fall_through = next_block->id; - next_block->predecessors.push_back(cur_block->id); - } - cur_block = next_block; - } - } - merged_df_flags_ = merged_df_flags; - - if (cu_->enable_debug & (1 << kDebugDumpCFG)) { - DumpCFG("/sdcard/1_post_parse_cfg/", true); - } - - if (cu_->verbose) { - DumpMIRGraph(); - } - - // Check if there's been a fall-through out of the method code. - BasicBlockId out_bb_id = dex_pc_to_block_map[current_code_item_->insns_size_in_code_units_]; - if (UNLIKELY(out_bb_id != NullBasicBlockId)) { - // Eagerly calculate DFS order to determine if the block is dead. - DCHECK(!DfsOrdersUpToDate()); - ComputeDFSOrders(); - BasicBlock* out_bb = GetBasicBlock(out_bb_id); - DCHECK(out_bb != nullptr); - if (out_bb->block_type != kDead) { - LOG(WARNING) << "Live fall-through out of method in " << PrettyMethod(method_idx, dex_file); - SetPuntToInterpreter(true); - } - } -} - -void MIRGraph::ShowOpcodeStats() { - DCHECK(opcode_count_ != nullptr); - LOG(INFO) << "Opcode Count"; - for (int i = 0; i < kNumPackedOpcodes; i++) { - if (opcode_count_[i] != 0) { - LOG(INFO) << "-C- " << Instruction::Name(static_cast(i)) - << " " << opcode_count_[i]; - } - } -} - -uint64_t MIRGraph::GetDataFlowAttributes(Instruction::Code opcode) { - DCHECK_LT((size_t) opcode, (sizeof(oat_data_flow_attributes_) / sizeof(oat_data_flow_attributes_[0]))); - return oat_data_flow_attributes_[opcode]; -} - -uint64_t MIRGraph::GetDataFlowAttributes(MIR* mir) { - DCHECK(mir != nullptr); - Instruction::Code opcode = mir->dalvikInsn.opcode; - return GetDataFlowAttributes(opcode); -} - -// The path can easily surpass FS limits because of parameters etc. Use pathconf to get FS -// restrictions here. Note that a successful invocation will return an actual value. If the path -// is too long for some reason, the return will be ENAMETOOLONG. Then cut off part of the name. -// -// It's possible the path is not valid, or some other errors appear. In that case return false. -static bool CreateDumpFile(std::string& fname, const char* dir_prefix, NarrowDexOffset start_offset, - const char *suffix, int nr, std::string* output) { - std::string dir = StringPrintf("./%s", dir_prefix); - int64_t max_name_length = pathconf(dir.c_str(), _PC_NAME_MAX); - if (max_name_length <= 0) { - PLOG(ERROR) << "Could not get file name restrictions for " << dir; - return false; - } - - std::string name = StringPrintf("%s%x%s_%d.dot", fname.c_str(), start_offset, - suffix == nullptr ? "" : suffix, nr); - std::string fpath; - if (static_cast(name.size()) > max_name_length) { - std::string suffix_str = StringPrintf("_%d.dot", nr); - name = name.substr(0, static_cast(max_name_length) - suffix_str.size()) + suffix_str; - } - // Sanity check. - DCHECK_LE(name.size(), static_cast(max_name_length)); - - *output = StringPrintf("%s%s", dir_prefix, name.c_str()); - return true; -} - -// TODO: use a configurable base prefix, and adjust callers to supply pass name. -/* Dump the CFG into a DOT graph */ -void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks, const char *suffix) { - FILE* file; - static AtomicInteger cnt(0); - - // Increment counter to get a unique file number. - cnt++; - int nr = cnt.LoadRelaxed(); - - std::string fname(PrettyMethod(cu_->method_idx, *cu_->dex_file)); - ReplaceSpecialChars(fname); - std::string fpath; - if (!CreateDumpFile(fname, dir_prefix, GetBasicBlock(GetEntryBlock()->fall_through)->start_offset, - suffix, nr, &fpath)) { - LOG(ERROR) << "Could not create dump file name for " << fname; - return; - } - file = fopen(fpath.c_str(), "w"); - if (file == nullptr) { - PLOG(ERROR) << "Could not open " << fpath << " for DumpCFG."; - return; - } - fprintf(file, "digraph G {\n"); - - fprintf(file, " rankdir=TB\n"); - - int num_blocks = all_blocks ? GetNumBlocks() : num_reachable_blocks_; - int idx; - - for (idx = 0; idx < num_blocks; idx++) { - int block_idx = all_blocks ? idx : dfs_order_[idx]; - BasicBlock* bb = GetBasicBlock(block_idx); - if (bb == nullptr) continue; - if (bb->block_type == kDead) continue; - if (bb->hidden) continue; - if (bb->block_type == kEntryBlock) { - fprintf(file, " entry_%d [shape=Mdiamond];\n", bb->id); - } else if (bb->block_type == kExitBlock) { - fprintf(file, " exit_%d [shape=Mdiamond];\n", bb->id); - } else if (bb->block_type == kDalvikByteCode) { - fprintf(file, " block%04x_%d [shape=record,label = \"{ \\\n", - bb->start_offset, bb->id); - const MIR* mir; - fprintf(file, " {block id %d\\l}%s\\\n", bb->id, - bb->first_mir_insn ? " | " : " "); - for (mir = bb->first_mir_insn; mir; mir = mir->next) { - int opcode = mir->dalvikInsn.opcode; - fprintf(file, " {%04x %s %s %s %s %s %s %s %s %s\\l}%s\\\n", mir->offset, - mir->ssa_rep ? GetDalvikDisassembly(mir) : - !MIR::DecodedInstruction::IsPseudoMirOp(opcode) ? - Instruction::Name(mir->dalvikInsn.opcode) : - extended_mir_op_names_[opcode - kMirOpFirst], - (mir->optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0 ? " no_rangecheck" : " ", - (mir->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0 ? " no_nullcheck" : " ", - (mir->optimization_flags & MIR_IGNORE_SUSPEND_CHECK) != 0 ? " no_suspendcheck" : " ", - (mir->optimization_flags & MIR_STORE_NON_TEMPORAL) != 0 ? " non_temporal" : " ", - (mir->optimization_flags & MIR_CALLEE) != 0 ? " inlined" : " ", - (mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0 ? " cl_inited" : " ", - (mir->optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0 ? " cl_in_cache" : " ", - (mir->optimization_flags & MIR_IGNORE_DIV_ZERO_CHECK) != 0 ? " no_div_check" : " ", - mir->next ? " | " : " "); - } - fprintf(file, " }\"];\n\n"); - } else if (bb->block_type == kExceptionHandling) { - char block_name[BLOCK_NAME_LEN]; - - GetBlockName(bb, block_name); - fprintf(file, " %s [shape=invhouse];\n", block_name); - } - - char block_name1[BLOCK_NAME_LEN], block_name2[BLOCK_NAME_LEN]; - - if (bb->taken != NullBasicBlockId) { - GetBlockName(bb, block_name1); - GetBlockName(GetBasicBlock(bb->taken), block_name2); - fprintf(file, " %s:s -> %s:n [style=dotted]\n", - block_name1, block_name2); - } - if (bb->fall_through != NullBasicBlockId) { - GetBlockName(bb, block_name1); - GetBlockName(GetBasicBlock(bb->fall_through), block_name2); - fprintf(file, " %s:s -> %s:n\n", block_name1, block_name2); - } - - if (bb->successor_block_list_type != kNotUsed) { - fprintf(file, " succ%04x_%d [shape=%s,label = \"{ \\\n", - bb->start_offset, bb->id, - (bb->successor_block_list_type == kCatch) ? "Mrecord" : "record"); - - int last_succ_id = static_cast(bb->successor_blocks.size() - 1u); - int succ_id = 0; - for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) { - BasicBlock* dest_block = GetBasicBlock(successor_block_info->block); - fprintf(file, " { %04x: %04x\\l}%s\\\n", - succ_id, - successor_block_info->key, - dest_block->start_offset, - (succ_id != last_succ_id) ? " | " : " "); - ++succ_id; - } - fprintf(file, " }\"];\n\n"); - - GetBlockName(bb, block_name1); - fprintf(file, " %s:s -> succ%04x_%d:n [style=dashed]\n", - block_name1, bb->start_offset, bb->id); - - // Link the successor pseudo-block with all of its potential targets. - succ_id = 0; - for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) { - BasicBlock* dest_block = GetBasicBlock(successor_block_info->block); - - GetBlockName(dest_block, block_name2); - fprintf(file, " succ%04x_%d:f%d:e -> %s:n\n", bb->start_offset, - bb->id, succ_id++, block_name2); - } - } - fprintf(file, "\n"); - - if (cu_->verbose) { - /* Display the dominator tree */ - GetBlockName(bb, block_name1); - fprintf(file, " cfg%s [label=\"%s\", shape=none];\n", - block_name1, block_name1); - if (bb->i_dom) { - GetBlockName(GetBasicBlock(bb->i_dom), block_name2); - fprintf(file, " cfg%s:s -> cfg%s:n\n\n", block_name2, block_name1); - } - } - } - fprintf(file, "}\n"); - fclose(file); -} - -/* Insert an MIR instruction to the end of a basic block. */ -void BasicBlock::AppendMIR(MIR* mir) { - // Insert it after the last MIR. - InsertMIRListAfter(last_mir_insn, mir, mir); -} - -void BasicBlock::AppendMIRList(MIR* first_list_mir, MIR* last_list_mir) { - // Insert it after the last MIR. - InsertMIRListAfter(last_mir_insn, first_list_mir, last_list_mir); -} - -void BasicBlock::AppendMIRList(const std::vector& insns) { - for (std::vector::const_iterator it = insns.begin(); it != insns.end(); it++) { - MIR* new_mir = *it; - - // Add a copy of each MIR. - InsertMIRListAfter(last_mir_insn, new_mir, new_mir); - } -} - -/* Insert a MIR instruction after the specified MIR. */ -void BasicBlock::InsertMIRAfter(MIR* current_mir, MIR* new_mir) { - InsertMIRListAfter(current_mir, new_mir, new_mir); -} - -void BasicBlock::InsertMIRListAfter(MIR* insert_after, MIR* first_list_mir, MIR* last_list_mir) { - // If no MIR, we are done. - if (first_list_mir == nullptr || last_list_mir == nullptr) { - return; - } - - // If insert_after is null, assume BB is empty. - if (insert_after == nullptr) { - first_mir_insn = first_list_mir; - last_mir_insn = last_list_mir; - last_list_mir->next = nullptr; - } else { - MIR* after_list = insert_after->next; - insert_after->next = first_list_mir; - last_list_mir->next = after_list; - if (after_list == nullptr) { - last_mir_insn = last_list_mir; - } - } - - // Set this BB to be the basic block of the MIRs. - MIR* last = last_list_mir->next; - for (MIR* mir = first_list_mir; mir != last; mir = mir->next) { - mir->bb = id; - } -} - -/* Insert an MIR instruction to the head of a basic block. */ -void BasicBlock::PrependMIR(MIR* mir) { - InsertMIRListBefore(first_mir_insn, mir, mir); -} - -void BasicBlock::PrependMIRList(MIR* first_list_mir, MIR* last_list_mir) { - // Insert it before the first MIR. - InsertMIRListBefore(first_mir_insn, first_list_mir, last_list_mir); -} - -void BasicBlock::PrependMIRList(const std::vector& to_add) { - for (std::vector::const_iterator it = to_add.begin(); it != to_add.end(); it++) { - MIR* mir = *it; - - InsertMIRListBefore(first_mir_insn, mir, mir); - } -} - -/* Insert a MIR instruction before the specified MIR. */ -void BasicBlock::InsertMIRBefore(MIR* current_mir, MIR* new_mir) { - // Insert as a single element list. - return InsertMIRListBefore(current_mir, new_mir, new_mir); -} - -MIR* BasicBlock::FindPreviousMIR(MIR* mir) { - MIR* current = first_mir_insn; - - while (current != nullptr) { - MIR* next = current->next; - - if (next == mir) { - return current; - } - - current = next; - } - - return nullptr; -} - -void BasicBlock::InsertMIRListBefore(MIR* insert_before, MIR* first_list_mir, MIR* last_list_mir) { - // If no MIR, we are done. - if (first_list_mir == nullptr || last_list_mir == nullptr) { - return; - } - - // If insert_before is null, assume BB is empty. - if (insert_before == nullptr) { - first_mir_insn = first_list_mir; - last_mir_insn = last_list_mir; - last_list_mir->next = nullptr; - } else { - if (first_mir_insn == insert_before) { - last_list_mir->next = first_mir_insn; - first_mir_insn = first_list_mir; - } else { - // Find the preceding MIR. - MIR* before_list = FindPreviousMIR(insert_before); - DCHECK(before_list != nullptr); - before_list->next = first_list_mir; - last_list_mir->next = insert_before; - } - } - - // Set this BB to be the basic block of the MIRs. - for (MIR* mir = first_list_mir; mir != last_list_mir->next; mir = mir->next) { - mir->bb = id; - } -} - -bool BasicBlock::RemoveMIR(MIR* mir) { - // Remove as a single element list. - return RemoveMIRList(mir, mir); -} - -bool BasicBlock::RemoveMIRList(MIR* first_list_mir, MIR* last_list_mir) { - if (first_list_mir == nullptr) { - return false; - } - - // Try to find the MIR. - MIR* before_list = nullptr; - MIR* after_list = nullptr; - - // If we are removing from the beginning of the MIR list. - if (first_mir_insn == first_list_mir) { - before_list = nullptr; - } else { - before_list = FindPreviousMIR(first_list_mir); - if (before_list == nullptr) { - // We did not find the mir. - return false; - } - } - - // Remove the BB information and also find the after_list. - for (MIR* mir = first_list_mir; mir != last_list_mir->next; mir = mir->next) { - mir->bb = NullBasicBlockId; - } - - after_list = last_list_mir->next; - - // If there is nothing before the list, after_list is the first_mir. - if (before_list == nullptr) { - first_mir_insn = after_list; - } else { - before_list->next = after_list; - } - - // If there is nothing after the list, before_list is last_mir. - if (after_list == nullptr) { - last_mir_insn = before_list; - } - - return true; -} - -MIR* BasicBlock::GetFirstNonPhiInsn() { - MIR* mir = first_mir_insn; - while (mir != nullptr && static_cast(mir->dalvikInsn.opcode) == kMirOpPhi) { - mir = mir->next; - } - return mir; -} - -MIR* BasicBlock::GetNextUnconditionalMir(MIRGraph* mir_graph, MIR* current) { - MIR* next_mir = nullptr; - - if (current != nullptr) { - next_mir = current->next; - } - - if (next_mir == nullptr) { - // Only look for next MIR that follows unconditionally. - if ((taken == NullBasicBlockId) && (fall_through != NullBasicBlockId)) { - next_mir = mir_graph->GetBasicBlock(fall_through)->first_mir_insn; - } - } - - return next_mir; -} - -static void FillTypeSizeString(uint32_t type_size, std::string* decoded_mir) { - DCHECK(decoded_mir != nullptr); - OpSize type = static_cast(type_size >> 16); - uint16_t vect_size = (type_size & 0xFFFF); - - // Now print the type and vector size. - std::stringstream ss; - ss << " (type:"; - ss << type; - ss << " vectsize:"; - ss << vect_size; - ss << ")"; - - decoded_mir->append(ss.str()); -} - -void MIRGraph::DisassembleExtendedInstr(const MIR* mir, std::string* decoded_mir) { - DCHECK(decoded_mir != nullptr); - int opcode = mir->dalvikInsn.opcode; - SSARepresentation* ssa_rep = mir->ssa_rep; - int defs = (ssa_rep != nullptr) ? ssa_rep->num_defs : 0; - int uses = (ssa_rep != nullptr) ? ssa_rep->num_uses : 0; - - if (opcode < kMirOpFirst) { - return; // It is not an extended instruction. - } - - decoded_mir->append(extended_mir_op_names_[opcode - kMirOpFirst]); - - switch (opcode) { - case kMirOpPhi: { - if (defs > 0 && uses > 0) { - BasicBlockId* incoming = mir->meta.phi_incoming; - decoded_mir->append(StringPrintf(" %s = (%s", - GetSSANameWithConst(ssa_rep->defs[0], true).c_str(), - GetSSANameWithConst(ssa_rep->uses[0], true).c_str())); - decoded_mir->append(StringPrintf(":%d", incoming[0])); - for (int i = 1; i < uses; i++) { - decoded_mir->append(StringPrintf(", %s:%d", GetSSANameWithConst(ssa_rep->uses[i], true).c_str(), incoming[i])); - } - decoded_mir->append(")"); - } - break; - } - case kMirOpCopy: - if (ssa_rep != nullptr) { - decoded_mir->append(" "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[0], false)); - if (defs > 1) { - decoded_mir->append(", "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[1], false)); - } - decoded_mir->append(" = "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[0], false)); - if (uses > 1) { - decoded_mir->append(", "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[1], false)); - } - } else { - decoded_mir->append(StringPrintf(" v%d = v%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - } - break; - case kMirOpFusedCmplFloat: - case kMirOpFusedCmpgFloat: - case kMirOpFusedCmplDouble: - case kMirOpFusedCmpgDouble: - case kMirOpFusedCmpLong: - if (ssa_rep != nullptr) { - decoded_mir->append(" "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[0], false)); - for (int i = 1; i < uses; i++) { - decoded_mir->append(", "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[i], false)); - } - } else { - decoded_mir->append(StringPrintf(" v%d, v%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - } - break; - case kMirOpMoveVector: - decoded_mir->append(StringPrintf(" vect%d = vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedAddition: - decoded_mir->append(StringPrintf(" vect%d = vect%d + vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedMultiply: - decoded_mir->append(StringPrintf(" vect%d = vect%d * vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedSubtract: - decoded_mir->append(StringPrintf(" vect%d = vect%d - vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedAnd: - decoded_mir->append(StringPrintf(" vect%d = vect%d & vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedOr: - decoded_mir->append(StringPrintf(" vect%d = vect%d \\| vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedXor: - decoded_mir->append(StringPrintf(" vect%d = vect%d ^ vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedShiftLeft: - decoded_mir->append(StringPrintf(" vect%d = vect%d \\<\\< %d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedUnsignedShiftRight: - decoded_mir->append(StringPrintf(" vect%d = vect%d \\>\\>\\> %d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedSignedShiftRight: - decoded_mir->append(StringPrintf(" vect%d = vect%d \\>\\> %d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpConstVector: - decoded_mir->append(StringPrintf(" vect%d = %x, %x, %x, %x", mir->dalvikInsn.vA, mir->dalvikInsn.arg[0], - mir->dalvikInsn.arg[1], mir->dalvikInsn.arg[2], mir->dalvikInsn.arg[3])); - break; - case kMirOpPackedSet: - if (ssa_rep != nullptr) { - decoded_mir->append(StringPrintf(" vect%d = %s", mir->dalvikInsn.vA, - GetSSANameWithConst(ssa_rep->uses[0], false).c_str())); - if (uses > 1) { - decoded_mir->append(", "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[1], false)); - } - } else { - decoded_mir->append(StringPrintf(" vect%d = v%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - } - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedAddReduce: - if (ssa_rep != nullptr) { - decoded_mir->append(" "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[0], false)); - if (defs > 1) { - decoded_mir->append(", "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[1], false)); - } - decoded_mir->append(StringPrintf(" = vect%d + %s", mir->dalvikInsn.vB, - GetSSANameWithConst(ssa_rep->uses[0], false).c_str())); - if (uses > 1) { - decoded_mir->append(", "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[1], false)); - } - } else { - decoded_mir->append(StringPrintf("v%d = vect%d + v%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB, mir->dalvikInsn.vA)); - } - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpPackedReduce: - if (ssa_rep != nullptr) { - decoded_mir->append(" "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[0], false)); - if (defs > 1) { - decoded_mir->append(", "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[1], false)); - } - decoded_mir->append(StringPrintf(" = vect%d (extr_idx:%d)", mir->dalvikInsn.vB, mir->dalvikInsn.arg[0])); - } else { - decoded_mir->append(StringPrintf(" v%d = vect%d (extr_idx:%d)", mir->dalvikInsn.vA, - mir->dalvikInsn.vB, mir->dalvikInsn.arg[0])); - } - FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); - break; - case kMirOpReserveVectorRegisters: - case kMirOpReturnVectorRegisters: - decoded_mir->append(StringPrintf(" vect%d - vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB)); - break; - case kMirOpMemBarrier: { - decoded_mir->append(" type:"); - std::stringstream ss; - ss << static_cast(mir->dalvikInsn.vA); - decoded_mir->append(ss.str()); - break; - } - case kMirOpPackedArrayGet: - case kMirOpPackedArrayPut: - decoded_mir->append(StringPrintf(" vect%d", mir->dalvikInsn.vA)); - if (ssa_rep != nullptr) { - decoded_mir->append(StringPrintf(", %s[%s]", - GetSSANameWithConst(ssa_rep->uses[0], false).c_str(), - GetSSANameWithConst(ssa_rep->uses[1], false).c_str())); - } else { - decoded_mir->append(StringPrintf(", v%d[v%d]", mir->dalvikInsn.vB, mir->dalvikInsn.vC)); - } - FillTypeSizeString(mir->dalvikInsn.arg[0], decoded_mir); - break; - case kMirOpMaddInt: - case kMirOpMsubInt: - case kMirOpMaddLong: - case kMirOpMsubLong: - if (ssa_rep != nullptr) { - decoded_mir->append(" "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[0], false)); - if (defs > 1) { - decoded_mir->append(", "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[1], false)); - } - for (int i = 0; i < uses; i++) { - decoded_mir->append(", "); - decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[i], false)); - } - } else { - decoded_mir->append(StringPrintf(" v%d, v%d, v%d, v%d", - mir->dalvikInsn.vA, mir->dalvikInsn.vB, - mir->dalvikInsn.vC, mir->dalvikInsn.arg[0])); - } - break; - default: - break; - } -} - -char* MIRGraph::GetDalvikDisassembly(const MIR* mir) { - MIR::DecodedInstruction insn = mir->dalvikInsn; - std::string str; - int flags = 0; - int opcode = insn.opcode; - char* ret; - bool nop = false; - SSARepresentation* ssa_rep = mir->ssa_rep; - Instruction::Format dalvik_format = Instruction::k10x; // Default to no-operand format. - - // Handle special cases that recover the original dalvik instruction. - if (opcode == kMirOpCheck) { - str.append(extended_mir_op_names_[opcode - kMirOpFirst]); - str.append(": "); - // Recover the original Dex instruction. - insn = mir->meta.throw_insn->dalvikInsn; - ssa_rep = mir->meta.throw_insn->ssa_rep; - opcode = insn.opcode; - } else if (opcode == kMirOpNop) { - str.append("["); - if (mir->offset < current_code_item_->insns_size_in_code_units_) { - // Recover original opcode. - insn.opcode = Instruction::At(current_code_item_->insns_ + mir->offset)->Opcode(); - opcode = insn.opcode; - } - nop = true; - } - int defs = (ssa_rep != nullptr) ? ssa_rep->num_defs : 0; - int uses = (ssa_rep != nullptr) ? ssa_rep->num_uses : 0; - - if (MIR::DecodedInstruction::IsPseudoMirOp(opcode)) { - // Note that this does not check the MIR's opcode in all cases. In cases where it - // recovered dalvik instruction, it uses opcode of that instead of the extended one. - DisassembleExtendedInstr(mir, &str); - } else { - dalvik_format = Instruction::FormatOf(insn.opcode); - flags = insn.FlagsOf(); - str.append(Instruction::Name(insn.opcode)); - - // For invokes-style formats, treat wide regs as a pair of singles. - bool show_singles = ((dalvik_format == Instruction::k35c) || - (dalvik_format == Instruction::k3rc)); - if (defs != 0) { - str.append(" "); - str.append(GetSSANameWithConst(ssa_rep->defs[0], false)); - if (defs > 1) { - str.append(", "); - str.append(GetSSANameWithConst(ssa_rep->defs[1], false)); - } - if (uses != 0) { - str.append(", "); - } - } - for (int i = 0; i < uses; i++) { - str.append(" "); - str.append(GetSSANameWithConst(ssa_rep->uses[i], show_singles)); - if (!show_singles && (reg_location_ != nullptr) && reg_location_[i].wide) { - // For the listing, skip the high sreg. - i++; - } - if (i != (uses - 1)) { - str.append(","); - } - } - - switch (dalvik_format) { - case Instruction::k11n: // Add one immediate from vB. - case Instruction::k21s: - case Instruction::k31i: - case Instruction::k21h: - str.append(StringPrintf(", #0x%x", insn.vB)); - break; - case Instruction::k51l: // Add one wide immediate. - str.append(StringPrintf(", #%" PRId64, insn.vB_wide)); - break; - case Instruction::k21c: // One register, one string/type/method index. - case Instruction::k31c: - str.append(StringPrintf(", index #0x%x", insn.vB)); - break; - case Instruction::k22c: // Two registers, one string/type/method index. - str.append(StringPrintf(", index #0x%x", insn.vC)); - break; - case Instruction::k22s: // Add one immediate from vC. - case Instruction::k22b: - str.append(StringPrintf(", #0x%x", insn.vC)); - break; - default: - // Nothing left to print. - break; - } - - if ((flags & Instruction::kBranch) != 0) { - // For branches, decode the instructions to print out the branch targets. - int offset = 0; - switch (dalvik_format) { - case Instruction::k21t: - offset = insn.vB; - break; - case Instruction::k22t: - offset = insn.vC; - break; - case Instruction::k10t: - case Instruction::k20t: - case Instruction::k30t: - offset = insn.vA; - break; - default: - LOG(FATAL) << "Unexpected branch format " << dalvik_format << " from " << insn.opcode; - break; - } - str.append(StringPrintf(", 0x%x (%c%x)", mir->offset + offset, - offset > 0 ? '+' : '-', offset > 0 ? offset : -offset)); - } - - if (nop) { - str.append("]--optimized away"); - } - } - int length = str.length() + 1; - ret = arena_->AllocArray(length, kArenaAllocDFInfo); - strncpy(ret, str.c_str(), length); - return ret; -} - -/* Turn method name into a legal Linux file name */ -void MIRGraph::ReplaceSpecialChars(std::string& str) { - static const struct { const char before; const char after; } match[] = { - {'/', '-'}, {';', '#'}, {' ', '#'}, {'$', '+'}, - {'(', '@'}, {')', '@'}, {'<', '='}, {'>', '='} - }; - for (unsigned int i = 0; i < sizeof(match)/sizeof(match[0]); i++) { - std::replace(str.begin(), str.end(), match[i].before, match[i].after); - } -} - -std::string MIRGraph::GetSSAName(int ssa_reg) { - // TODO: This value is needed for debugging. Currently, we compute this and then copy to the - // arena. We should be smarter and just place straight into the arena, or compute the - // value more lazily. - int vreg = SRegToVReg(ssa_reg); - if (vreg >= static_cast(GetFirstTempVR())) { - return StringPrintf("t%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg)); - } else { - return StringPrintf("v%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg)); - } -} - -// Similar to GetSSAName, but if ssa name represents an immediate show that as well. -std::string MIRGraph::GetSSANameWithConst(int ssa_reg, bool singles_only) { - if (reg_location_ == nullptr) { - // Pre-SSA - just use the standard name. - return GetSSAName(ssa_reg); - } - if (IsConst(reg_location_[ssa_reg])) { - if (!singles_only && reg_location_[ssa_reg].wide && - !reg_location_[ssa_reg].high_word) { - return StringPrintf("v%d_%d#0x%" PRIx64, SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg), - ConstantValueWide(reg_location_[ssa_reg])); - } else { - return StringPrintf("v%d_%d#0x%x", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg), - ConstantValue(reg_location_[ssa_reg])); - } - } else { - int vreg = SRegToVReg(ssa_reg); - if (vreg >= static_cast(GetFirstTempVR())) { - return StringPrintf("t%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg)); - } else { - return StringPrintf("v%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg)); - } - } -} - -void MIRGraph::GetBlockName(BasicBlock* bb, char* name) { - switch (bb->block_type) { - case kEntryBlock: - snprintf(name, BLOCK_NAME_LEN, "entry_%d", bb->id); - break; - case kExitBlock: - snprintf(name, BLOCK_NAME_LEN, "exit_%d", bb->id); - break; - case kDalvikByteCode: - snprintf(name, BLOCK_NAME_LEN, "block%04x_%d", bb->start_offset, bb->id); - break; - case kExceptionHandling: - snprintf(name, BLOCK_NAME_LEN, "exception%04x_%d", bb->start_offset, - bb->id); - break; - default: - snprintf(name, BLOCK_NAME_LEN, "_%d", bb->id); - break; - } -} - -const char* MIRGraph::GetShortyFromMethodReference(const MethodReference& target_method) { - const DexFile::MethodId& method_id = - target_method.dex_file->GetMethodId(target_method.dex_method_index); - return target_method.dex_file->GetShorty(method_id.proto_idx_); -} - -/* Debug Utility - dump a compilation unit */ -void MIRGraph::DumpMIRGraph() { - const char* block_type_names[] = { - "Null Block", - "Entry Block", - "Code Block", - "Exit Block", - "Exception Handling", - "Catch Block" - }; - - LOG(INFO) << "Compiling " << PrettyMethod(cu_->method_idx, *cu_->dex_file); - LOG(INFO) << GetInsns(0) << " insns"; - LOG(INFO) << GetNumBlocks() << " blocks in total"; - - for (BasicBlock* bb : block_list_) { - LOG(INFO) << StringPrintf("Block %d (%s) (insn %04x - %04x%s)", - bb->id, - block_type_names[bb->block_type], - bb->start_offset, - bb->last_mir_insn ? bb->last_mir_insn->offset : bb->start_offset, - bb->last_mir_insn ? "" : " empty"); - if (bb->taken != NullBasicBlockId) { - LOG(INFO) << " Taken branch: block " << bb->taken - << "(0x" << std::hex << GetBasicBlock(bb->taken)->start_offset << ")"; - } - if (bb->fall_through != NullBasicBlockId) { - LOG(INFO) << " Fallthrough : block " << bb->fall_through - << " (0x" << std::hex << GetBasicBlock(bb->fall_through)->start_offset << ")"; - } - } -} - -/* - * Build an array of location records for the incoming arguments. - * Note: one location record per word of arguments, with dummy - * high-word loc for wide arguments. Also pull up any following - * MOVE_RESULT and incorporate it into the invoke. - */ -CallInfo* MIRGraph::NewMemCallInfo(BasicBlock* bb, MIR* mir, InvokeType type, bool is_range) { - CallInfo* info = static_cast(arena_->Alloc(sizeof(CallInfo), - kArenaAllocMisc)); - MIR* move_result_mir = FindMoveResult(bb, mir); - if (move_result_mir == nullptr) { - info->result.location = kLocInvalid; - } else { - info->result = GetRawDest(move_result_mir); - move_result_mir->dalvikInsn.opcode = static_cast(kMirOpNop); - } - info->num_arg_words = mir->ssa_rep->num_uses; - info->args = (info->num_arg_words == 0) ? nullptr : - arena_->AllocArray(info->num_arg_words, kArenaAllocMisc); - for (size_t i = 0; i < info->num_arg_words; i++) { - info->args[i] = GetRawSrc(mir, i); - } - info->opt_flags = mir->optimization_flags; - info->type = type; - info->is_range = is_range; - if (IsInstructionQuickInvoke(mir->dalvikInsn.opcode)) { - const auto& method_info = GetMethodLoweringInfo(mir); - info->method_ref = method_info.GetTargetMethod(); - } else { - info->method_ref = MethodReference(GetCurrentDexCompilationUnit()->GetDexFile(), - mir->dalvikInsn.vB); - } - info->index = mir->dalvikInsn.vB; - info->offset = mir->offset; - info->mir = mir; - return info; -} - -// Allocate a new MIR. -MIR* MIRGraph::NewMIR() { - MIR* mir = new (arena_) MIR(); - return mir; -} - -// Allocate a new basic block. -BasicBlock* MIRGraph::NewMemBB(BBType block_type, int block_id) { - BasicBlock* bb = new (arena_) BasicBlock(block_id, block_type, arena_); - - // TUNING: better estimate of the exit block predecessors? - bb->predecessors.reserve((block_type == kExitBlock) ? 2048 : 2); - block_id_map_.Put(block_id, block_id); - return bb; -} - -void MIRGraph::InitializeConstantPropagation() { - is_constant_v_ = new (arena_) ArenaBitVector(arena_, GetNumSSARegs(), false); - constant_values_ = arena_->AllocArray(GetNumSSARegs(), kArenaAllocDFInfo); -} - -void MIRGraph::InitializeMethodUses() { - // The gate starts by initializing the use counts. - int num_ssa_regs = GetNumSSARegs(); - use_counts_.clear(); - use_counts_.reserve(num_ssa_regs + 32); - use_counts_.resize(num_ssa_regs, 0u); - raw_use_counts_.clear(); - raw_use_counts_.reserve(num_ssa_regs + 32); - raw_use_counts_.resize(num_ssa_regs, 0u); -} - -void MIRGraph::SSATransformationStart() { - DCHECK(temp_scoped_alloc_.get() == nullptr); - temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - temp_.ssa.num_vregs = GetNumOfCodeAndTempVRs(); - temp_.ssa.work_live_vregs = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_.ssa.num_vregs, false); -} - -void MIRGraph::SSATransformationEnd() { - // Verify the dataflow information after the pass. - if (cu_->enable_debug & (1 << kDebugVerifyDataflow)) { - VerifyDataflow(); - } - - temp_.ssa.num_vregs = 0u; - temp_.ssa.work_live_vregs = nullptr; - DCHECK(temp_.ssa.def_block_matrix == nullptr); - temp_.ssa.phi_node_blocks = nullptr; - DCHECK(temp_scoped_alloc_.get() != nullptr); - temp_scoped_alloc_.reset(); - - // Update the maximum number of reachable blocks. - max_num_reachable_blocks_ = num_reachable_blocks_; - - // Mark MIR SSA representations as up to date. - mir_ssa_rep_up_to_date_ = true; -} - -size_t MIRGraph::GetNumDalvikInsns() const { - size_t cumulative_size = 0u; - bool counted_current_item = false; - const uint8_t size_for_null_code_item = 2u; - - for (auto it : m_units_) { - const DexFile::CodeItem* code_item = it->GetCodeItem(); - // Even if the code item is null, we still count non-zero value so that - // each m_unit is counted as having impact. - cumulative_size += (code_item == nullptr ? - size_for_null_code_item : code_item->insns_size_in_code_units_); - if (code_item == current_code_item_) { - counted_current_item = true; - } - } - - // If the current code item was not counted yet, count it now. - // This can happen for example in unit tests where some fields like m_units_ - // are not initialized. - if (counted_current_item == false) { - cumulative_size += (current_code_item_ == nullptr ? - size_for_null_code_item : current_code_item_->insns_size_in_code_units_); - } - - return cumulative_size; -} - -static BasicBlock* SelectTopologicalSortOrderFallBack( - MIRGraph* mir_graph, const ArenaBitVector* current_loop, - const ScopedArenaVector* visited_cnt_values, ScopedArenaAllocator* allocator, - ScopedArenaVector* tmp_stack) { - // No true loop head has been found but there may be true loop heads after the mess we need - // to resolve. To avoid taking one of those, pick the candidate with the highest number of - // reachable unvisited nodes. That candidate will surely be a part of a loop. - BasicBlock* fall_back = nullptr; - size_t fall_back_num_reachable = 0u; - // Reuse the same bit vector for each candidate to mark reachable unvisited blocks. - ArenaBitVector candidate_reachable(allocator, mir_graph->GetNumBlocks(), false); - AllNodesIterator iter(mir_graph); - for (BasicBlock* candidate = iter.Next(); candidate != nullptr; candidate = iter.Next()) { - if (candidate->hidden || // Hidden, or - candidate->visited || // already processed, or - (*visited_cnt_values)[candidate->id] == 0u || // no processed predecessors, or - (current_loop != nullptr && // outside current loop. - !current_loop->IsBitSet(candidate->id))) { - continue; - } - DCHECK(tmp_stack->empty()); - tmp_stack->push_back(candidate->id); - candidate_reachable.ClearAllBits(); - size_t num_reachable = 0u; - while (!tmp_stack->empty()) { - BasicBlockId current_id = tmp_stack->back(); - tmp_stack->pop_back(); - BasicBlock* current_bb = mir_graph->GetBasicBlock(current_id); - DCHECK(current_bb != nullptr); - ChildBlockIterator child_iter(current_bb, mir_graph); - BasicBlock* child_bb = child_iter.Next(); - for ( ; child_bb != nullptr; child_bb = child_iter.Next()) { - DCHECK(!child_bb->hidden); - if (child_bb->visited || // Already processed, or - (current_loop != nullptr && // outside current loop. - !current_loop->IsBitSet(child_bb->id))) { - continue; - } - if (!candidate_reachable.IsBitSet(child_bb->id)) { - candidate_reachable.SetBit(child_bb->id); - tmp_stack->push_back(child_bb->id); - num_reachable += 1u; - } - } - } - if (fall_back_num_reachable < num_reachable) { - fall_back_num_reachable = num_reachable; - fall_back = candidate; - } - } - return fall_back; -} - -// Compute from which unvisited blocks is bb_id reachable through unvisited blocks. -static void ComputeUnvisitedReachableFrom(MIRGraph* mir_graph, BasicBlockId bb_id, - ArenaBitVector* reachable, - ScopedArenaVector* tmp_stack) { - // NOTE: Loop heads indicated by the "visited" flag. - DCHECK(tmp_stack->empty()); - reachable->ClearAllBits(); - tmp_stack->push_back(bb_id); - while (!tmp_stack->empty()) { - BasicBlockId current_id = tmp_stack->back(); - tmp_stack->pop_back(); - BasicBlock* current_bb = mir_graph->GetBasicBlock(current_id); - DCHECK(current_bb != nullptr); - for (BasicBlockId pred_id : current_bb->predecessors) { - BasicBlock* pred_bb = mir_graph->GetBasicBlock(pred_id); - DCHECK(pred_bb != nullptr); - if (!pred_bb->visited && !reachable->IsBitSet(pred_bb->id)) { - reachable->SetBit(pred_bb->id); - tmp_stack->push_back(pred_bb->id); - } - } - } -} - -void MIRGraph::ComputeTopologicalSortOrder() { - ScopedArenaAllocator allocator(&cu_->arena_stack); - unsigned int num_blocks = GetNumBlocks(); - - ScopedArenaQueue q(allocator.Adapter()); - ScopedArenaVector visited_cnt_values(num_blocks, 0u, allocator.Adapter()); - ScopedArenaVector loop_head_stack(allocator.Adapter()); - size_t max_nested_loops = 0u; - ArenaBitVector loop_exit_blocks(&allocator, num_blocks, false); - loop_exit_blocks.ClearAllBits(); - - // Count the number of blocks to process and add the entry block(s). - unsigned int num_blocks_to_process = 0u; - for (BasicBlock* bb : block_list_) { - if (bb->hidden == true) { - continue; - } - - num_blocks_to_process += 1u; - - if (bb->predecessors.size() == 0u) { - // Add entry block to the queue. - q.push(bb); - } - } - - // Clear the topological order arrays. - topological_order_.clear(); - topological_order_.reserve(num_blocks); - topological_order_loop_ends_.clear(); - topological_order_loop_ends_.resize(num_blocks, 0u); - topological_order_indexes_.clear(); - topological_order_indexes_.resize(num_blocks, static_cast(-1)); - - // Mark all blocks as unvisited. - ClearAllVisitedFlags(); - - // For loop heads, keep track from which blocks they are reachable not going through other - // loop heads. Other loop heads are excluded to detect the heads of nested loops. The children - // in this set go into the loop body, the other children are jumping over the loop. - ScopedArenaVector loop_head_reachable_from(allocator.Adapter()); - loop_head_reachable_from.resize(num_blocks, nullptr); - // Reuse the same temp stack whenever calculating a loop_head_reachable_from[loop_head_id]. - ScopedArenaVector tmp_stack(allocator.Adapter()); - - while (num_blocks_to_process != 0u) { - BasicBlock* bb = nullptr; - if (!q.empty()) { - num_blocks_to_process -= 1u; - // Get top. - bb = q.front(); - q.pop(); - if (bb->visited) { - // Loop head: it was already processed, mark end and copy exit blocks to the queue. - DCHECK(q.empty()) << PrettyMethod(cu_->method_idx, *cu_->dex_file); - uint16_t idx = static_cast(topological_order_.size()); - topological_order_loop_ends_[topological_order_indexes_[bb->id]] = idx; - DCHECK_EQ(loop_head_stack.back(), bb->id); - loop_head_stack.pop_back(); - ArenaBitVector* reachable = - loop_head_stack.empty() ? nullptr : loop_head_reachable_from[loop_head_stack.back()]; - for (BasicBlockId candidate_id : loop_exit_blocks.Indexes()) { - if (reachable == nullptr || reachable->IsBitSet(candidate_id)) { - q.push(GetBasicBlock(candidate_id)); - // NOTE: The BitVectorSet::IndexIterator will not check the pointed-to bit again, - // so clearing the bit has no effect on the iterator. - loop_exit_blocks.ClearBit(candidate_id); - } - } - continue; - } - } else { - // Find the new loop head. - AllNodesIterator iter(this); - while (true) { - BasicBlock* candidate = iter.Next(); - if (candidate == nullptr) { - // We did not find a true loop head, fall back to a reachable block in any loop. - ArenaBitVector* current_loop = - loop_head_stack.empty() ? nullptr : loop_head_reachable_from[loop_head_stack.back()]; - bb = SelectTopologicalSortOrderFallBack(this, current_loop, &visited_cnt_values, - &allocator, &tmp_stack); - DCHECK(bb != nullptr) << PrettyMethod(cu_->method_idx, *cu_->dex_file); - if (kIsDebugBuild && cu_->dex_file != nullptr) { - LOG(INFO) << "Topological sort order: Using fall-back in " - << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " BB #" << bb->id - << " @0x" << std::hex << bb->start_offset - << ", num_blocks = " << std::dec << num_blocks; - } - break; - } - if (candidate->hidden || // Hidden, or - candidate->visited || // already processed, or - visited_cnt_values[candidate->id] == 0u || // no processed predecessors, or - (!loop_head_stack.empty() && // outside current loop. - !loop_head_reachable_from[loop_head_stack.back()]->IsBitSet(candidate->id))) { - continue; - } - - for (BasicBlockId pred_id : candidate->predecessors) { - BasicBlock* pred_bb = GetBasicBlock(pred_id); - DCHECK(pred_bb != nullptr); - if (pred_bb != candidate && !pred_bb->visited && - !pred_bb->dominators->IsBitSet(candidate->id)) { - candidate = nullptr; // Set candidate to null to indicate failure. - break; - } - } - if (candidate != nullptr) { - bb = candidate; - break; - } - } - // Compute blocks from which the loop head is reachable and process those blocks first. - ArenaBitVector* reachable = - new (&allocator) ArenaBitVector(&allocator, num_blocks, false); - loop_head_reachable_from[bb->id] = reachable; - ComputeUnvisitedReachableFrom(this, bb->id, reachable, &tmp_stack); - // Now mark as loop head. (Even if it's only a fall back when we don't find a true loop.) - loop_head_stack.push_back(bb->id); - max_nested_loops = std::max(max_nested_loops, loop_head_stack.size()); - } - - DCHECK_EQ(bb->hidden, false); - DCHECK_EQ(bb->visited, false); - bb->visited = true; - bb->nesting_depth = loop_head_stack.size(); - - // Now add the basic block. - uint16_t idx = static_cast(topological_order_.size()); - topological_order_indexes_[bb->id] = idx; - topological_order_.push_back(bb->id); - - // Update visited_cnt_values for children. - ChildBlockIterator succIter(bb, this); - BasicBlock* successor = succIter.Next(); - for ( ; successor != nullptr; successor = succIter.Next()) { - if (successor->hidden) { - continue; - } - - // One more predecessor was visited. - visited_cnt_values[successor->id] += 1u; - if (visited_cnt_values[successor->id] == successor->predecessors.size()) { - if (loop_head_stack.empty() || - loop_head_reachable_from[loop_head_stack.back()]->IsBitSet(successor->id)) { - q.push(successor); - } else { - DCHECK(!loop_exit_blocks.IsBitSet(successor->id)); - loop_exit_blocks.SetBit(successor->id); - } - } - } - } - - // Prepare the loop head stack for iteration. - topological_order_loop_head_stack_.clear(); - topological_order_loop_head_stack_.reserve(max_nested_loops); - max_nested_loops_ = max_nested_loops; - topological_order_up_to_date_ = true; -} - -bool BasicBlock::IsExceptionBlock() const { - if (block_type == kExceptionHandling) { - return true; - } - return false; -} - -ChildBlockIterator::ChildBlockIterator(BasicBlock* bb, MIRGraph* mir_graph) - : basic_block_(bb), mir_graph_(mir_graph), visited_fallthrough_(false), - visited_taken_(false), have_successors_(false) { - // Check if we actually do have successors. - if (basic_block_ != 0 && basic_block_->successor_block_list_type != kNotUsed) { - have_successors_ = true; - successor_iter_ = basic_block_->successor_blocks.cbegin(); - } -} - -BasicBlock* ChildBlockIterator::Next() { - // We check if we have a basic block. If we don't we cannot get next child. - if (basic_block_ == nullptr) { - return nullptr; - } - - // If we haven't visited fallthrough, return that. - if (visited_fallthrough_ == false) { - visited_fallthrough_ = true; - - BasicBlock* result = mir_graph_->GetBasicBlock(basic_block_->fall_through); - if (result != nullptr) { - return result; - } - } - - // If we haven't visited taken, return that. - if (visited_taken_ == false) { - visited_taken_ = true; - - BasicBlock* result = mir_graph_->GetBasicBlock(basic_block_->taken); - if (result != nullptr) { - return result; - } - } - - // We visited both taken and fallthrough. Now check if we have successors we need to visit. - if (have_successors_ == true) { - // Get information about next successor block. - auto end = basic_block_->successor_blocks.cend(); - while (successor_iter_ != end) { - SuccessorBlockInfo* successor_block_info = *successor_iter_; - ++successor_iter_; - // If block was replaced by zero block, take next one. - if (successor_block_info->block != NullBasicBlockId) { - return mir_graph_->GetBasicBlock(successor_block_info->block); - } - } - } - - // We do not have anything. - return nullptr; -} - -BasicBlock* BasicBlock::Copy(CompilationUnit* c_unit) { - MIRGraph* mir_graph = c_unit->mir_graph.get(); - return Copy(mir_graph); -} - -BasicBlock* BasicBlock::Copy(MIRGraph* mir_graph) { - BasicBlock* result_bb = mir_graph->CreateNewBB(block_type); - - // We don't do a memcpy style copy here because it would lead to a lot of things - // to clean up. Let us do it by hand instead. - // Copy in taken and fallthrough. - result_bb->fall_through = fall_through; - result_bb->taken = taken; - - // Copy successor links if needed. - ArenaAllocator* arena = mir_graph->GetArena(); - - result_bb->successor_block_list_type = successor_block_list_type; - if (result_bb->successor_block_list_type != kNotUsed) { - result_bb->successor_blocks.reserve(successor_blocks.size()); - for (SuccessorBlockInfo* sbi_old : successor_blocks) { - SuccessorBlockInfo* sbi_new = static_cast( - arena->Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors)); - memcpy(sbi_new, sbi_old, sizeof(SuccessorBlockInfo)); - result_bb->successor_blocks.push_back(sbi_new); - } - } - - // Copy offset, method. - result_bb->start_offset = start_offset; - - // Now copy instructions. - for (MIR* mir = first_mir_insn; mir != 0; mir = mir->next) { - // Get a copy first. - MIR* copy = mir->Copy(mir_graph); - - // Append it. - result_bb->AppendMIR(copy); - } - - return result_bb; -} - -MIR* MIR::Copy(MIRGraph* mir_graph) { - MIR* res = mir_graph->NewMIR(); - *res = *this; - - // Remove links - res->next = nullptr; - res->bb = NullBasicBlockId; - res->ssa_rep = nullptr; - - return res; -} - -MIR* MIR::Copy(CompilationUnit* c_unit) { - return Copy(c_unit->mir_graph.get()); -} - -uint32_t SSARepresentation::GetStartUseIndex(Instruction::Code opcode) { - // Default result. - int res = 0; - - // We are basically setting the iputs to their igets counterparts. - switch (opcode) { - case Instruction::IPUT: - case Instruction::IPUT_OBJECT: - case Instruction::IPUT_BOOLEAN: - case Instruction::IPUT_BYTE: - case Instruction::IPUT_CHAR: - case Instruction::IPUT_SHORT: - case Instruction::IPUT_QUICK: - case Instruction::IPUT_OBJECT_QUICK: - case Instruction::IPUT_BOOLEAN_QUICK: - case Instruction::IPUT_BYTE_QUICK: - case Instruction::IPUT_CHAR_QUICK: - case Instruction::IPUT_SHORT_QUICK: - case Instruction::APUT: - case Instruction::APUT_OBJECT: - case Instruction::APUT_BOOLEAN: - case Instruction::APUT_BYTE: - case Instruction::APUT_CHAR: - case Instruction::APUT_SHORT: - case Instruction::SPUT: - case Instruction::SPUT_OBJECT: - case Instruction::SPUT_BOOLEAN: - case Instruction::SPUT_BYTE: - case Instruction::SPUT_CHAR: - case Instruction::SPUT_SHORT: - // Skip the VR containing what to store. - res = 1; - break; - case Instruction::IPUT_WIDE: - case Instruction::IPUT_WIDE_QUICK: - case Instruction::APUT_WIDE: - case Instruction::SPUT_WIDE: - // Skip the two VRs containing what to store. - res = 2; - break; - default: - // Do nothing in the general case. - break; - } - - return res; -} - -/** - * @brief Given a decoded instruction, it checks whether the instruction - * sets a constant and if it does, more information is provided about the - * constant being set. - * @param ptr_value pointer to a 64-bit holder for the constant. - * @param wide Updated by function whether a wide constant is being set by bytecode. - * @return Returns false if the decoded instruction does not represent a constant bytecode. - */ -bool MIR::DecodedInstruction::GetConstant(int64_t* ptr_value, bool* wide) const { - bool sets_const = true; - int64_t value = vB; - - DCHECK(ptr_value != nullptr); - DCHECK(wide != nullptr); - - switch (opcode) { - case Instruction::CONST_4: - case Instruction::CONST_16: - case Instruction::CONST: - *wide = false; - value <<= 32; // In order to get the sign extend. - value >>= 32; - break; - case Instruction::CONST_HIGH16: - *wide = false; - value <<= 48; // In order to get the sign extend. - value >>= 32; - break; - case Instruction::CONST_WIDE_16: - case Instruction::CONST_WIDE_32: - *wide = true; - value <<= 32; // In order to get the sign extend. - value >>= 32; - break; - case Instruction::CONST_WIDE: - *wide = true; - value = vB_wide; - break; - case Instruction::CONST_WIDE_HIGH16: - *wide = true; - value <<= 48; // In order to get the sign extend. - break; - default: - sets_const = false; - break; - } - - if (sets_const) { - *ptr_value = value; - } - - return sets_const; -} - -void BasicBlock::ResetOptimizationFlags(uint16_t reset_flags) { - // Reset flags for all MIRs in bb. - for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) { - mir->optimization_flags &= (~reset_flags); - } -} - -void BasicBlock::Kill(MIRGraph* mir_graph) { - for (BasicBlockId pred_id : predecessors) { - BasicBlock* pred_bb = mir_graph->GetBasicBlock(pred_id); - DCHECK(pred_bb != nullptr); - - // Sadly we have to go through the children by hand here. - pred_bb->ReplaceChild(id, NullBasicBlockId); - } - predecessors.clear(); - - // Mark as dead and hidden. - block_type = kDead; - hidden = true; - - // Detach it from its MIRs so we don't generate code for them. Also detached MIRs - // are updated to know that they no longer have a parent. - for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) { - mir->bb = NullBasicBlockId; - } - first_mir_insn = nullptr; - last_mir_insn = nullptr; - - data_flow_info = nullptr; - - // Erase this bb from all children's predecessors and kill unreachable children. - ChildBlockIterator iter(this, mir_graph); - for (BasicBlock* succ_bb = iter.Next(); succ_bb != nullptr; succ_bb = iter.Next()) { - succ_bb->ErasePredecessor(id); - } - - // Remove links to children. - fall_through = NullBasicBlockId; - taken = NullBasicBlockId; - successor_block_list_type = kNotUsed; - - if (kIsDebugBuild) { - if (catch_entry) { - DCHECK_EQ(mir_graph->catches_.count(start_offset), 1u); - mir_graph->catches_.erase(start_offset); - } - } -} - -bool BasicBlock::IsSSALiveOut(const CompilationUnit* c_unit, int ssa_reg) { - // In order to determine if the ssa reg is live out, we scan all the MIRs. We remember - // the last SSA number of the same dalvik register. At the end, if it is different than ssa_reg, - // then it is not live out of this BB. - int dalvik_reg = c_unit->mir_graph->SRegToVReg(ssa_reg); - - int last_ssa_reg = -1; - - // Walk through the MIRs backwards. - for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) { - // Get ssa rep. - SSARepresentation *ssa_rep = mir->ssa_rep; - - // Go through the defines for this MIR. - for (int i = 0; i < ssa_rep->num_defs; i++) { - DCHECK(ssa_rep->defs != nullptr); - - // Get the ssa reg. - int def_ssa_reg = ssa_rep->defs[i]; - - // Get dalvik reg. - int def_dalvik_reg = c_unit->mir_graph->SRegToVReg(def_ssa_reg); - - // Compare dalvik regs. - if (dalvik_reg == def_dalvik_reg) { - // We found a def of the register that we are being asked about. - // Remember it. - last_ssa_reg = def_ssa_reg; - } - } - } - - if (last_ssa_reg == -1) { - // If we get to this point we couldn't find a define of register user asked about. - // Let's assume the user knows what he's doing so we can be safe and say that if we - // couldn't find a def, it is live out. - return true; - } - - // If it is not -1, we found a match, is it ssa_reg? - return (ssa_reg == last_ssa_reg); -} - -bool BasicBlock::ReplaceChild(BasicBlockId old_bb, BasicBlockId new_bb) { - // We need to check taken, fall_through, and successor_blocks to replace. - bool found = false; - if (taken == old_bb) { - taken = new_bb; - found = true; - } - - if (fall_through == old_bb) { - fall_through = new_bb; - found = true; - } - - if (successor_block_list_type != kNotUsed) { - for (SuccessorBlockInfo* successor_block_info : successor_blocks) { - if (successor_block_info->block == old_bb) { - successor_block_info->block = new_bb; - found = true; - } - } - } - - return found; -} - -void BasicBlock::ErasePredecessor(BasicBlockId old_pred) { - auto pos = std::find(predecessors.begin(), predecessors.end(), old_pred); - DCHECK(pos != predecessors.end()); - // It's faster to move the back() to *pos than erase(pos). - *pos = predecessors.back(); - predecessors.pop_back(); - size_t idx = std::distance(predecessors.begin(), pos); - for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) { - if (static_cast(mir->dalvikInsn.opcode) != kMirOpPhi) { - break; - } - DCHECK_EQ(mir->ssa_rep->num_uses - 1u, predecessors.size()); - DCHECK_EQ(mir->meta.phi_incoming[idx], old_pred); - mir->meta.phi_incoming[idx] = mir->meta.phi_incoming[predecessors.size()]; - mir->ssa_rep->uses[idx] = mir->ssa_rep->uses[predecessors.size()]; - mir->ssa_rep->num_uses = predecessors.size(); - } -} - -void BasicBlock::UpdatePredecessor(BasicBlockId old_pred, BasicBlockId new_pred) { - DCHECK_NE(new_pred, NullBasicBlockId); - auto pos = std::find(predecessors.begin(), predecessors.end(), old_pred); - DCHECK(pos != predecessors.end()); - *pos = new_pred; - size_t idx = std::distance(predecessors.begin(), pos); - for (MIR* mir = first_mir_insn; mir != nullptr; mir = mir->next) { - if (static_cast(mir->dalvikInsn.opcode) != kMirOpPhi) { - break; - } - DCHECK_EQ(mir->meta.phi_incoming[idx], old_pred); - mir->meta.phi_incoming[idx] = new_pred; - } -} - -// Create a new basic block with block_id as num_blocks_ that is -// post-incremented. -BasicBlock* MIRGraph::CreateNewBB(BBType block_type) { - BasicBlockId id = static_cast(block_list_.size()); - BasicBlock* res = NewMemBB(block_type, id); - block_list_.push_back(res); - return res; -} - -void MIRGraph::CalculateBasicBlockInformation(const PassManager* const post_opt_pass_manager) { - /* Create the pass driver and launch it */ - PassDriverMEPostOpt driver(post_opt_pass_manager, cu_); - driver.Launch(); -} - -int MIR::DecodedInstruction::FlagsOf() const { - // Calculate new index. - int idx = static_cast(opcode) - kNumPackedOpcodes; - - // Check if it is an extended or not. - if (idx < 0) { - return Instruction::FlagsOf(opcode); - } - - // For extended, we use a switch. - switch (static_cast(opcode)) { - case kMirOpPhi: - return Instruction::kContinue; - case kMirOpCopy: - return Instruction::kContinue; - case kMirOpFusedCmplFloat: - return Instruction::kContinue | Instruction::kBranch; - case kMirOpFusedCmpgFloat: - return Instruction::kContinue | Instruction::kBranch; - case kMirOpFusedCmplDouble: - return Instruction::kContinue | Instruction::kBranch; - case kMirOpFusedCmpgDouble: - return Instruction::kContinue | Instruction::kBranch; - case kMirOpFusedCmpLong: - return Instruction::kContinue | Instruction::kBranch; - case kMirOpNop: - return Instruction::kContinue; - case kMirOpNullCheck: - return Instruction::kContinue | Instruction::kThrow; - case kMirOpRangeCheck: - return Instruction::kContinue | Instruction::kThrow; - case kMirOpDivZeroCheck: - return Instruction::kContinue | Instruction::kThrow; - case kMirOpCheck: - return Instruction::kContinue | Instruction::kThrow; - case kMirOpSelect: - return Instruction::kContinue; - case kMirOpConstVector: - return Instruction::kContinue; - case kMirOpMoveVector: - return Instruction::kContinue; - case kMirOpPackedMultiply: - return Instruction::kContinue; - case kMirOpPackedAddition: - return Instruction::kContinue; - case kMirOpPackedSubtract: - return Instruction::kContinue; - case kMirOpPackedShiftLeft: - return Instruction::kContinue; - case kMirOpPackedSignedShiftRight: - return Instruction::kContinue; - case kMirOpPackedUnsignedShiftRight: - return Instruction::kContinue; - case kMirOpPackedAnd: - return Instruction::kContinue; - case kMirOpPackedOr: - return Instruction::kContinue; - case kMirOpPackedXor: - return Instruction::kContinue; - case kMirOpPackedAddReduce: - return Instruction::kContinue; - case kMirOpPackedReduce: - return Instruction::kContinue; - case kMirOpPackedSet: - return Instruction::kContinue; - case kMirOpReserveVectorRegisters: - return Instruction::kContinue; - case kMirOpReturnVectorRegisters: - return Instruction::kContinue; - case kMirOpMemBarrier: - return Instruction::kContinue; - case kMirOpPackedArrayGet: - return Instruction::kContinue | Instruction::kThrow; - case kMirOpPackedArrayPut: - return Instruction::kContinue | Instruction::kThrow; - case kMirOpMaddInt: - case kMirOpMsubInt: - case kMirOpMaddLong: - case kMirOpMsubLong: - return Instruction::kContinue; - default: - LOG(WARNING) << "ExtendedFlagsOf: Unhandled case: " << static_cast (opcode); - return 0; - } -} - -const uint16_t* MIRGraph::GetInsns(int m_unit_index) const { - return m_units_[m_unit_index]->GetCodeItem()->insns_; -} - -void MIRGraph::SetPuntToInterpreter(bool val) { - punt_to_interpreter_ = val; - if (val) { - // Disable all subsequent optimizations. They may not be safe to run. (For example, - // LVN/GVN assumes there are no conflicts found by the type inference pass.) - cu_->disable_opt = ~static_castdisable_opt)>(0); - } -} - -} // namespace art diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h deleted file mode 100644 index 3191fe9d57..0000000000 --- a/compiler/dex/mir_graph.h +++ /dev/null @@ -1,1488 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_MIR_GRAPH_H_ -#define ART_COMPILER_DEX_MIR_GRAPH_H_ - -#include - -#include "base/arena_bit_vector.h" -#include "base/arena_containers.h" -#include "base/bit_utils.h" -#include "base/scoped_arena_containers.h" -#include "dex_file.h" -#include "dex_instruction.h" -#include "dex_types.h" -#include "invoke_type.h" -#include "mir_field_info.h" -#include "mir_method_info.h" -#include "reg_location.h" -#include "reg_storage.h" - -namespace art { - -struct CompilationUnit; -class DexCompilationUnit; -class DexFileMethodInliner; -class GlobalValueNumbering; -class GvnDeadCodeElimination; -class PassManager; -class TypeInference; - -// Forward declaration. -class MIRGraph; - -enum DataFlowAttributePos { - kUA = 0, - kUB, - kUC, - kAWide, - kBWide, - kCWide, - kDA, - kIsMove, - kSetsConst, - kFormat35c, - kFormat3rc, - kFormatExtended, // Extended format for extended MIRs. - kNullCheckA, // Null check of A. - kNullCheckB, // Null check of B. - kNullCheckOut0, // Null check out outgoing arg0. - kDstNonNull, // May assume dst is non-null. - kRetNonNull, // May assume retval is non-null. - kNullTransferSrc0, // Object copy src[0] -> dst. - kNullTransferSrcN, // Phi null check state transfer. - kRangeCheckC, // Range check of C. - kCheckCastA, // Check cast of A. - kFPA, - kFPB, - kFPC, - kCoreA, - kCoreB, - kCoreC, - kRefA, - kRefB, - kRefC, - kSameTypeAB, // A and B have the same type but it can be core/ref/fp (IF_cc). - kUsesMethodStar, // Implicit use of Method*. - kUsesIField, // Accesses an instance field (IGET/IPUT). - kUsesSField, // Accesses a static field (SGET/SPUT). - kCanInitializeClass, // Can trigger class initialization (SGET/SPUT/INVOKE_STATIC). - kDoLVN, // Worth computing local value numbers. -}; - -#define DF_NOP UINT64_C(0) -#define DF_UA (UINT64_C(1) << kUA) -#define DF_UB (UINT64_C(1) << kUB) -#define DF_UC (UINT64_C(1) << kUC) -#define DF_A_WIDE (UINT64_C(1) << kAWide) -#define DF_B_WIDE (UINT64_C(1) << kBWide) -#define DF_C_WIDE (UINT64_C(1) << kCWide) -#define DF_DA (UINT64_C(1) << kDA) -#define DF_IS_MOVE (UINT64_C(1) << kIsMove) -#define DF_SETS_CONST (UINT64_C(1) << kSetsConst) -#define DF_FORMAT_35C (UINT64_C(1) << kFormat35c) -#define DF_FORMAT_3RC (UINT64_C(1) << kFormat3rc) -#define DF_FORMAT_EXTENDED (UINT64_C(1) << kFormatExtended) -#define DF_NULL_CHK_A (UINT64_C(1) << kNullCheckA) -#define DF_NULL_CHK_B (UINT64_C(1) << kNullCheckB) -#define DF_NULL_CHK_OUT0 (UINT64_C(1) << kNullCheckOut0) -#define DF_NON_NULL_DST (UINT64_C(1) << kDstNonNull) -#define DF_NON_NULL_RET (UINT64_C(1) << kRetNonNull) -#define DF_NULL_TRANSFER_0 (UINT64_C(1) << kNullTransferSrc0) -#define DF_NULL_TRANSFER_N (UINT64_C(1) << kNullTransferSrcN) -#define DF_RANGE_CHK_C (UINT64_C(1) << kRangeCheckC) -#define DF_CHK_CAST (UINT64_C(1) << kCheckCastA) -#define DF_FP_A (UINT64_C(1) << kFPA) -#define DF_FP_B (UINT64_C(1) << kFPB) -#define DF_FP_C (UINT64_C(1) << kFPC) -#define DF_CORE_A (UINT64_C(1) << kCoreA) -#define DF_CORE_B (UINT64_C(1) << kCoreB) -#define DF_CORE_C (UINT64_C(1) << kCoreC) -#define DF_REF_A (UINT64_C(1) << kRefA) -#define DF_REF_B (UINT64_C(1) << kRefB) -#define DF_REF_C (UINT64_C(1) << kRefC) -#define DF_SAME_TYPE_AB (UINT64_C(1) << kSameTypeAB) -#define DF_UMS (UINT64_C(1) << kUsesMethodStar) -#define DF_IFIELD (UINT64_C(1) << kUsesIField) -#define DF_SFIELD (UINT64_C(1) << kUsesSField) -#define DF_CLINIT (UINT64_C(1) << kCanInitializeClass) -#define DF_LVN (UINT64_C(1) << kDoLVN) - -#define DF_HAS_USES (DF_UA | DF_UB | DF_UC) - -#define DF_HAS_DEFS (DF_DA) - -#define DF_HAS_NULL_CHKS (DF_NULL_CHK_A | \ - DF_NULL_CHK_B | \ - DF_NULL_CHK_OUT0) - -#define DF_HAS_RANGE_CHKS (DF_RANGE_CHK_C) - -#define DF_HAS_NR_CHKS (DF_HAS_NULL_CHKS | \ - DF_HAS_RANGE_CHKS) - -#define DF_A_IS_REG (DF_UA | DF_DA) -#define DF_B_IS_REG (DF_UB) -#define DF_C_IS_REG (DF_UC) -#define DF_USES_FP (DF_FP_A | DF_FP_B | DF_FP_C) -#define DF_NULL_TRANSFER (DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N) -#define DF_IS_INVOKE (DF_FORMAT_35C | DF_FORMAT_3RC) - -enum OatMethodAttributes { - kIsLeaf, // Method is leaf. -}; - -#define METHOD_IS_LEAF (1 << kIsLeaf) - -// Minimum field size to contain Dalvik v_reg number. -#define VREG_NUM_WIDTH 16 - -#define INVALID_VREG (0xFFFFU) -#define INVALID_OFFSET (0xDEADF00FU) - -#define MIR_IGNORE_NULL_CHECK (1 << kMIRIgnoreNullCheck) -#define MIR_IGNORE_RANGE_CHECK (1 << kMIRIgnoreRangeCheck) -#define MIR_IGNORE_CHECK_CAST (1 << kMIRIgnoreCheckCast) -#define MIR_STORE_NON_NULL_VALUE (1 << kMIRStoreNonNullValue) -#define MIR_CLASS_IS_INITIALIZED (1 << kMIRClassIsInitialized) -#define MIR_CLASS_IS_IN_DEX_CACHE (1 << kMIRClassIsInDexCache) -#define MIR_IGNORE_DIV_ZERO_CHECK (1 << kMirIgnoreDivZeroCheck) -#define MIR_INLINED (1 << kMIRInlined) -#define MIR_INLINED_PRED (1 << kMIRInlinedPred) -#define MIR_CALLEE (1 << kMIRCallee) -#define MIR_IGNORE_SUSPEND_CHECK (1 << kMIRIgnoreSuspendCheck) -#define MIR_DUP (1 << kMIRDup) -#define MIR_MARK (1 << kMIRMark) -#define MIR_STORE_NON_TEMPORAL (1 << kMIRStoreNonTemporal) - -#define BLOCK_NAME_LEN 80 - -typedef uint16_t BasicBlockId; -static const BasicBlockId NullBasicBlockId = 0; - -// Leaf optimization is basically the removal of suspend checks from leaf methods. -// This is incompatible with SuspendCheckElimination (SCE) which eliminates suspend -// checks from loops that call any non-intrinsic method, since a loop that calls -// only a leaf method would end up without any suspend checks at all. So turning -// this on automatically disables the SCE in MIRGraph::EliminateSuspendChecksGate(). -// -// Since the Optimizing compiler is actually applying the same optimization, Quick -// must not run SCE anyway, so we enable this optimization as a way to disable SCE -// while keeping a consistent behavior across the backends, b/22657404. -static constexpr bool kLeafOptimization = true; - -/* - * In general, vreg/sreg describe Dalvik registers that originated with dx. However, - * it is useful to have compiler-generated temporary registers and have them treated - * in the same manner as dx-generated virtual registers. This struct records the SSA - * name of compiler-introduced temporaries. - */ -struct CompilerTemp { - int32_t v_reg; // Virtual register number for temporary. - int32_t s_reg_low; // SSA name for low Dalvik word. -}; - -enum CompilerTempType { - kCompilerTempVR, // A virtual register temporary. - kCompilerTempSpecialMethodPtr, // Temporary that keeps track of current method pointer. - kCompilerTempBackend, // Temporary that is used by backend. -}; - -// When debug option enabled, records effectiveness of null and range check elimination. -struct Checkstats { - int32_t null_checks; - int32_t null_checks_eliminated; - int32_t range_checks; - int32_t range_checks_eliminated; -}; - -// Dataflow attributes of a basic block. -struct BasicBlockDataFlow { - ArenaBitVector* use_v; - ArenaBitVector* def_v; - ArenaBitVector* live_in_v; - int32_t* vreg_to_ssa_map_exit; -}; - -/* - * Normalized use/def for a MIR operation using SSA names rather than vregs. Note that - * uses/defs retain the Dalvik convention that long operations operate on a pair of 32-bit - * vregs. For example, "ADD_LONG v0, v2, v3" would have 2 defs (v0/v1) and 4 uses (v2/v3, v4/v5). - * Following SSA renaming, this is the primary struct used by code generators to locate - * operand and result registers. This is a somewhat confusing and unhelpful convention that - * we may want to revisit in the future. - * - * TODO: - * 1. Add accessors for uses/defs and make data private - * 2. Change fp_use/fp_def to a bit array (could help memory usage) - * 3. Combine array storage into internal array and handled via accessors from 1. - */ -struct SSARepresentation { - int32_t* uses; - int32_t* defs; - uint16_t num_uses_allocated; - uint16_t num_defs_allocated; - uint16_t num_uses; - uint16_t num_defs; - - static uint32_t GetStartUseIndex(Instruction::Code opcode); -}; - -/* - * The Midlevel Intermediate Representation node, which may be largely considered a - * wrapper around a Dalvik byte code. - */ -class MIR : public ArenaObject { - public: - /* - * TODO: remove embedded DecodedInstruction to save space, keeping only opcode. Recover - * additional fields on as-needed basis. Question: how to support MIR Pseudo-ops; probably - * need to carry aux data pointer. - */ - struct DecodedInstruction { - uint32_t vA; - uint32_t vB; - uint64_t vB_wide; /* for k51l */ - uint32_t vC; - uint32_t arg[5]; /* vC/D/E/F/G in invoke or filled-new-array */ - Instruction::Code opcode; - - DecodedInstruction() : vA(0), vB(0), vB_wide(0), vC(0), opcode(Instruction::NOP) { - } - - /* - * Given a decoded instruction representing a const bytecode, it updates - * the out arguments with proper values as dictated by the constant bytecode. - */ - bool GetConstant(int64_t* ptr_value, bool* wide) const; - - static bool IsPseudoMirOp(Instruction::Code opcode) { - return static_cast(opcode) >= static_cast(kMirOpFirst); - } - - static bool IsPseudoMirOp(int opcode) { - return opcode >= static_cast(kMirOpFirst); - } - - bool IsInvoke() const { - return ((FlagsOf() & Instruction::kInvoke) == Instruction::kInvoke); - } - - bool IsStore() const { - return ((FlagsOf() & Instruction::kStore) == Instruction::kStore); - } - - bool IsLoad() const { - return ((FlagsOf() & Instruction::kLoad) == Instruction::kLoad); - } - - bool IsConditionalBranch() const { - return (FlagsOf() == (Instruction::kContinue | Instruction::kBranch)); - } - - /** - * @brief Is the register C component of the decoded instruction a constant? - */ - bool IsCFieldOrConstant() const { - return ((FlagsOf() & Instruction::kRegCFieldOrConstant) == Instruction::kRegCFieldOrConstant); - } - - /** - * @brief Is the register C component of the decoded instruction a constant? - */ - bool IsBFieldOrConstant() const { - return ((FlagsOf() & Instruction::kRegBFieldOrConstant) == Instruction::kRegBFieldOrConstant); - } - - bool IsCast() const { - return ((FlagsOf() & Instruction::kCast) == Instruction::kCast); - } - - /** - * @brief Does the instruction clobber memory? - * @details Clobber means that the instruction changes the memory not in a punctual way. - * Therefore any supposition on memory aliasing or memory contents should be disregarded - * when crossing such an instruction. - */ - bool Clobbers() const { - return ((FlagsOf() & Instruction::kClobber) == Instruction::kClobber); - } - - bool IsLinear() const { - return (FlagsOf() & (Instruction::kAdd | Instruction::kSubtract)) != 0; - } - - int FlagsOf() const; - } dalvikInsn; - - NarrowDexOffset offset; // Offset of the instruction in code units. - uint16_t optimization_flags; - int16_t m_unit_index; // From which method was this MIR included - BasicBlockId bb; - MIR* next; - SSARepresentation* ssa_rep; - union { - // Incoming edges for phi node. - BasicBlockId* phi_incoming; - // Establish link from check instruction (kMirOpCheck) to the actual throwing instruction. - MIR* throw_insn; - // Branch condition for fused cmp or select. - ConditionCode ccode; - // IGET/IPUT lowering info index, points to MIRGraph::ifield_lowering_infos_. Due to limit on - // the number of code points (64K) and size of IGET/IPUT insn (2), this will never exceed 32K. - uint32_t ifield_lowering_info; - // SGET/SPUT lowering info index, points to MIRGraph::sfield_lowering_infos_. Due to limit on - // the number of code points (64K) and size of SGET/SPUT insn (2), this will never exceed 32K. - uint32_t sfield_lowering_info; - // INVOKE data index, points to MIRGraph::method_lowering_infos_. Also used for inlined - // CONST and MOVE insn (with MIR_CALLEE) to remember the invoke for type inference. - uint32_t method_lowering_info; - } meta; - - MIR() : offset(0), optimization_flags(0), m_unit_index(0), bb(NullBasicBlockId), - next(nullptr), ssa_rep(nullptr) { - memset(&meta, 0, sizeof(meta)); - } - - uint32_t GetStartUseIndex() const { - return SSARepresentation::GetStartUseIndex(dalvikInsn.opcode); - } - - MIR* Copy(CompilationUnit *c_unit); - MIR* Copy(MIRGraph* mir_Graph); -}; - -struct SuccessorBlockInfo; - -class BasicBlock : public DeletableArenaObject { - public: - BasicBlock(BasicBlockId block_id, BBType type, ArenaAllocator* allocator) - : id(block_id), - dfs_id(), start_offset(), fall_through(), taken(), i_dom(), nesting_depth(), - block_type(type), - successor_block_list_type(kNotUsed), - visited(), hidden(), catch_entry(), explicit_throw(), conditional_branch(), - terminated_by_return(), dominates_return(), use_lvn(), first_mir_insn(), - last_mir_insn(), data_flow_info(), dominators(), i_dominated(), dom_frontier(), - predecessors(allocator->Adapter(kArenaAllocBBPredecessors)), - successor_blocks(allocator->Adapter(kArenaAllocSuccessors)) { - } - BasicBlockId id; - BasicBlockId dfs_id; - NarrowDexOffset start_offset; // Offset in code units. - BasicBlockId fall_through; - BasicBlockId taken; - BasicBlockId i_dom; // Immediate dominator. - uint16_t nesting_depth; - BBType block_type:4; - BlockListType successor_block_list_type:4; - bool visited:1; - bool hidden:1; - bool catch_entry:1; - bool explicit_throw:1; - bool conditional_branch:1; - bool terminated_by_return:1; // Block ends with a Dalvik return opcode. - bool dominates_return:1; // Is a member of return extended basic block. - bool use_lvn:1; // Run local value numbering on this block. - MIR* first_mir_insn; - MIR* last_mir_insn; - BasicBlockDataFlow* data_flow_info; - ArenaBitVector* dominators; - ArenaBitVector* i_dominated; // Set nodes being immediately dominated. - ArenaBitVector* dom_frontier; // Dominance frontier. - ArenaVector predecessors; - ArenaVector successor_blocks; - - void AppendMIR(MIR* mir); - void AppendMIRList(MIR* first_list_mir, MIR* last_list_mir); - void AppendMIRList(const std::vector& insns); - void PrependMIR(MIR* mir); - void PrependMIRList(MIR* first_list_mir, MIR* last_list_mir); - void PrependMIRList(const std::vector& to_add); - void InsertMIRAfter(MIR* current_mir, MIR* new_mir); - void InsertMIRListAfter(MIR* insert_after, MIR* first_list_mir, MIR* last_list_mir); - MIR* FindPreviousMIR(MIR* mir); - void InsertMIRBefore(MIR* insert_before, MIR* list); - void InsertMIRListBefore(MIR* insert_before, MIR* first_list_mir, MIR* last_list_mir); - bool RemoveMIR(MIR* mir); - bool RemoveMIRList(MIR* first_list_mir, MIR* last_list_mir); - - BasicBlock* Copy(CompilationUnit* c_unit); - BasicBlock* Copy(MIRGraph* mir_graph); - - /** - * @brief Reset the optimization_flags field of each MIR. - */ - void ResetOptimizationFlags(uint16_t reset_flags); - - /** - * @brief Kill the BasicBlock. - * @details Unlink predecessors and successors, remove all MIRs, set the block type to kDead - * and set hidden to true. - */ - void Kill(MIRGraph* mir_graph); - - /** - * @brief Is ssa_reg the last SSA definition of that VR in the block? - */ - bool IsSSALiveOut(const CompilationUnit* c_unit, int ssa_reg); - - /** - * @brief Replace the edge going to old_bb to now go towards new_bb. - */ - bool ReplaceChild(BasicBlockId old_bb, BasicBlockId new_bb); - - /** - * @brief Erase the predecessor old_pred. - */ - void ErasePredecessor(BasicBlockId old_pred); - - /** - * @brief Update the predecessor array from old_pred to new_pred. - */ - void UpdatePredecessor(BasicBlockId old_pred, BasicBlockId new_pred); - - /** - * @brief Return first non-Phi insn. - */ - MIR* GetFirstNonPhiInsn(); - - /** - * @brief Checks whether the block ends with if-nez or if-eqz that branches to - * the given successor only if the register in not zero. - */ - bool BranchesToSuccessorOnlyIfNotZero(BasicBlockId succ_id) const { - if (last_mir_insn == nullptr) { - return false; - } - Instruction::Code last_opcode = last_mir_insn->dalvikInsn.opcode; - return ((last_opcode == Instruction::IF_EQZ && fall_through == succ_id) || - (last_opcode == Instruction::IF_NEZ && taken == succ_id)) && - // Make sure the other successor isn't the same (empty if), b/21614284. - (fall_through != taken); - } - - /** - * @brief Used to obtain the next MIR that follows unconditionally. - * @details The implementation does not guarantee that a MIR does not - * follow even if this method returns nullptr. - * @param mir_graph the MIRGraph. - * @param current The MIR for which to find an unconditional follower. - * @return Returns the following MIR if one can be found. - */ - MIR* GetNextUnconditionalMir(MIRGraph* mir_graph, MIR* current); - bool IsExceptionBlock() const; - - private: - DISALLOW_COPY_AND_ASSIGN(BasicBlock); -}; - -/* - * The "blocks" field in "successor_block_list" points to an array of elements with the type - * "SuccessorBlockInfo". For catch blocks, key is type index for the exception. For switch - * blocks, key is the case value. - */ -struct SuccessorBlockInfo { - BasicBlockId block; - int key; -}; - -/** - * @class ChildBlockIterator - * @brief Enable an easy iteration of the children. - */ -class ChildBlockIterator { - public: - /** - * @brief Constructs a child iterator. - * @param bb The basic whose children we need to iterate through. - * @param mir_graph The MIRGraph used to get the basic block during iteration. - */ - ChildBlockIterator(BasicBlock* bb, MIRGraph* mir_graph); - BasicBlock* Next(); - - private: - BasicBlock* basic_block_; - MIRGraph* mir_graph_; - bool visited_fallthrough_; - bool visited_taken_; - bool have_successors_; - ArenaVector::const_iterator successor_iter_; -}; - -/* - * Collection of information describing an invoke, and the destination of - * the subsequent MOVE_RESULT (if applicable). Collected as a unit to enable - * more efficient invoke code generation. - */ -struct CallInfo { - size_t num_arg_words; // Note: word count, not arg count. - RegLocation* args; // One for each word of arguments. - RegLocation result; // Eventual target of MOVE_RESULT. - int opt_flags; - InvokeType type; - uint32_t dex_idx; - MethodReference method_ref; - uint32_t index; // Method idx for invokes, type idx for FilledNewArray. - uintptr_t direct_code; - uintptr_t direct_method; - RegLocation target; // Target of following move_result. - bool skip_this; - bool is_range; - DexOffset offset; // Offset in code units. - MIR* mir; - int32_t string_init_offset; -}; - - -const RegLocation bad_loc = {kLocDalvikFrame, 0, 0, 0, 0, 0, 0, 0, 0, RegStorage(), INVALID_SREG, - INVALID_SREG}; - -class MIRGraph { - public: - MIRGraph(CompilationUnit* cu, ArenaAllocator* arena); - virtual ~MIRGraph(); - - /* - * Examine the graph to determine whether it's worthwile to spend the time compiling - * this method. - */ - bool SkipCompilation(std::string* skip_message); - - /* - * Parse dex method and add MIR at current insert point. Returns id (which is - * actually the index of the method in the m_units_ array). - */ - void InlineMethod(const DexFile::CodeItem* code_item, - uint32_t access_flags, - InvokeType invoke_type, - uint16_t class_def_idx, - uint32_t method_idx, - jobject class_loader, - const DexFile& dex_file, - Handle dex_cache); - - /* Find existing block */ - BasicBlock* FindBlock(DexOffset code_offset, - ScopedArenaVector* dex_pc_to_block_map) { - return FindBlock(code_offset, false, nullptr, dex_pc_to_block_map); - } - - const uint16_t* GetCurrentInsns() const { - return current_code_item_->insns_; - } - - /** - * @brief Used to obtain the raw dex bytecode instruction pointer. - * @param m_unit_index The method index in MIRGraph (caused by having multiple methods). - * This is guaranteed to contain index 0 which is the base method being compiled. - * @return Returns the raw instruction pointer. - */ - const uint16_t* GetInsns(int m_unit_index) const; - - /** - * @brief Used to obtain the raw data table. - * @param mir sparse switch, packed switch, of fill-array-data - * @param table_offset The table offset from start of method. - * @return Returns the raw table pointer. - */ - const uint16_t* GetTable(MIR* mir, uint32_t table_offset) const { - return GetInsns(mir->m_unit_index) + mir->offset + static_cast(table_offset); - } - - unsigned int GetNumBlocks() const { - return block_list_.size(); - } - - /** - * @brief Provides the total size in code units of all instructions in MIRGraph. - * @details Includes the sizes of all methods in compilation unit. - * @return Returns the cumulative sum of all insn sizes (in code units). - */ - size_t GetNumDalvikInsns() const; - - ArenaBitVector* GetTryBlockAddr() const { - return try_block_addr_; - } - - BasicBlock* GetEntryBlock() const { - return entry_block_; - } - - BasicBlock* GetExitBlock() const { - return exit_block_; - } - - BasicBlock* GetBasicBlock(unsigned int block_id) const { - DCHECK_LT(block_id, block_list_.size()); // NOTE: NullBasicBlockId is 0. - return (block_id == NullBasicBlockId) ? nullptr : block_list_[block_id]; - } - - size_t GetBasicBlockListCount() const { - return block_list_.size(); - } - - const ArenaVector& GetBlockList() { - return block_list_; - } - - const ArenaVector& GetDfsOrder() { - return dfs_order_; - } - - const ArenaVector& GetDfsPostOrder() { - return dfs_post_order_; - } - - const ArenaVector& GetDomPostOrder() { - return dom_post_order_traversal_; - } - - int GetDefCount() const { - return def_count_; - } - - ArenaAllocator* GetArena() const { - return arena_; - } - - void EnableOpcodeCounting() { - opcode_count_ = arena_->AllocArray(kNumPackedOpcodes, kArenaAllocMisc); - } - - void ShowOpcodeStats(); - - DexCompilationUnit* GetCurrentDexCompilationUnit() const { - return m_units_[current_method_]; - } - - /** - * @brief Dump a CFG into a dot file format. - * @param dir_prefix the directory the file will be created in. - * @param all_blocks does the dumper use all the basic blocks or use the reachable blocks. - * @param suffix does the filename require a suffix or not (default = nullptr). - */ - void DumpCFG(const char* dir_prefix, bool all_blocks, const char* suffix = nullptr); - - bool HasCheckCast() const { - return (merged_df_flags_ & DF_CHK_CAST) != 0u; - } - - bool HasFieldAccess() const { - return (merged_df_flags_ & (DF_IFIELD | DF_SFIELD)) != 0u; - } - - bool HasStaticFieldAccess() const { - return (merged_df_flags_ & DF_SFIELD) != 0u; - } - - bool HasInvokes() const { - // NOTE: These formats include the rare filled-new-array/range. - return (merged_df_flags_ & (DF_FORMAT_35C | DF_FORMAT_3RC)) != 0u; - } - - void DoCacheFieldLoweringInfo(); - - const MirIFieldLoweringInfo& GetIFieldLoweringInfo(MIR* mir) const { - return GetIFieldLoweringInfo(mir->meta.ifield_lowering_info); - } - - const MirIFieldLoweringInfo& GetIFieldLoweringInfo(uint32_t lowering_info) const { - DCHECK_LT(lowering_info, ifield_lowering_infos_.size()); - return ifield_lowering_infos_[lowering_info]; - } - - size_t GetIFieldLoweringInfoCount() const { - return ifield_lowering_infos_.size(); - } - - const MirSFieldLoweringInfo& GetSFieldLoweringInfo(MIR* mir) const { - return GetSFieldLoweringInfo(mir->meta.sfield_lowering_info); - } - - const MirSFieldLoweringInfo& GetSFieldLoweringInfo(uint32_t lowering_info) const { - DCHECK_LT(lowering_info, sfield_lowering_infos_.size()); - return sfield_lowering_infos_[lowering_info]; - } - - size_t GetSFieldLoweringInfoCount() const { - return sfield_lowering_infos_.size(); - } - - void DoCacheMethodLoweringInfo(); - - const MirMethodLoweringInfo& GetMethodLoweringInfo(MIR* mir) const { - return GetMethodLoweringInfo(mir->meta.method_lowering_info); - } - - const MirMethodLoweringInfo& GetMethodLoweringInfo(uint32_t lowering_info) const { - DCHECK_LT(lowering_info, method_lowering_infos_.size()); - return method_lowering_infos_[lowering_info]; - } - - size_t GetMethodLoweringInfoCount() const { - return method_lowering_infos_.size(); - } - - void ComputeInlineIFieldLoweringInfo(uint16_t field_idx, MIR* invoke, MIR* iget_or_iput); - - void InitRegLocations(); - - void RemapRegLocations(); - - void DumpRegLocTable(RegLocation* table, int count); - - void BasicBlockOptimizationStart(); - void BasicBlockOptimization(); - void BasicBlockOptimizationEnd(); - - void StringChange(); - - const ArenaVector& GetTopologicalSortOrder() { - DCHECK(!topological_order_.empty()); - return topological_order_; - } - - const ArenaVector& GetTopologicalSortOrderLoopEnds() { - DCHECK(!topological_order_loop_ends_.empty()); - return topological_order_loop_ends_; - } - - const ArenaVector& GetTopologicalSortOrderIndexes() { - DCHECK(!topological_order_indexes_.empty()); - return topological_order_indexes_; - } - - ArenaVector>* GetTopologicalSortOrderLoopHeadStack() { - DCHECK(!topological_order_.empty()); // Checking the main array, not the stack. - return &topological_order_loop_head_stack_; - } - - size_t GetMaxNestedLoops() const { - return max_nested_loops_; - } - - bool IsLoopHead(BasicBlockId bb_id) { - return topological_order_loop_ends_[topological_order_indexes_[bb_id]] != 0u; - } - - bool IsConst(int32_t s_reg) const { - return is_constant_v_->IsBitSet(s_reg); - } - - bool IsConst(RegLocation loc) const { - return loc.orig_sreg < 0 ? false : IsConst(loc.orig_sreg); - } - - int32_t ConstantValue(RegLocation loc) const { - DCHECK(IsConst(loc)); - return constant_values_[loc.orig_sreg]; - } - - int32_t ConstantValue(int32_t s_reg) const { - DCHECK(IsConst(s_reg)); - return constant_values_[s_reg]; - } - - /** - * @brief Used to obtain 64-bit value of a pair of ssa registers. - * @param s_reg_low The ssa register representing the low bits. - * @param s_reg_high The ssa register representing the high bits. - * @return Retusn the 64-bit constant value. - */ - int64_t ConstantValueWide(int32_t s_reg_low, int32_t s_reg_high) const { - DCHECK(IsConst(s_reg_low)); - DCHECK(IsConst(s_reg_high)); - return (static_cast(constant_values_[s_reg_high]) << 32) | - Low32Bits(static_cast(constant_values_[s_reg_low])); - } - - int64_t ConstantValueWide(RegLocation loc) const { - DCHECK(IsConst(loc)); - DCHECK(!loc.high_word); // Do not allow asking for the high partner. - DCHECK_LT(loc.orig_sreg + 1, GetNumSSARegs()); - return (static_cast(constant_values_[loc.orig_sreg + 1]) << 32) | - Low32Bits(static_cast(constant_values_[loc.orig_sreg])); - } - - /** - * @brief Used to mark ssa register as being constant. - * @param ssa_reg The ssa register. - * @param value The constant value of ssa register. - */ - void SetConstant(int32_t ssa_reg, int32_t value); - - /** - * @brief Used to mark ssa register and its wide counter-part as being constant. - * @param ssa_reg The ssa register. - * @param value The 64-bit constant value of ssa register and its pair. - */ - void SetConstantWide(int32_t ssa_reg, int64_t value); - - bool IsConstantNullRef(RegLocation loc) const { - return loc.ref && loc.is_const && (ConstantValue(loc) == 0); - } - - int GetNumSSARegs() const { - return num_ssa_regs_; - } - - void SetNumSSARegs(int new_num) { - /* - * TODO: It's theoretically possible to exceed 32767, though any cases which did - * would be filtered out with current settings. When orig_sreg field is removed - * from RegLocation, expand s_reg_low to handle all possible cases and remove DCHECK(). - */ - CHECK_EQ(new_num, static_cast(new_num)); - num_ssa_regs_ = new_num; - } - - unsigned int GetNumReachableBlocks() const { - return num_reachable_blocks_; - } - - uint32_t GetUseCount(int sreg) const { - DCHECK_LT(static_cast(sreg), use_counts_.size()); - return use_counts_[sreg]; - } - - uint32_t GetRawUseCount(int sreg) const { - DCHECK_LT(static_cast(sreg), raw_use_counts_.size()); - return raw_use_counts_[sreg]; - } - - int GetSSASubscript(int ssa_reg) const { - DCHECK_LT(static_cast(ssa_reg), ssa_subscripts_.size()); - return ssa_subscripts_[ssa_reg]; - } - - RegLocation GetRawSrc(MIR* mir, int num) { - DCHECK(num < mir->ssa_rep->num_uses); - RegLocation res = reg_location_[mir->ssa_rep->uses[num]]; - return res; - } - - RegLocation GetRawDest(MIR* mir) { - DCHECK_GT(mir->ssa_rep->num_defs, 0); - RegLocation res = reg_location_[mir->ssa_rep->defs[0]]; - return res; - } - - RegLocation GetDest(MIR* mir) { - RegLocation res = GetRawDest(mir); - DCHECK(!res.wide); - return res; - } - - RegLocation GetSrc(MIR* mir, int num) { - RegLocation res = GetRawSrc(mir, num); - DCHECK(!res.wide); - return res; - } - - RegLocation GetDestWide(MIR* mir) { - RegLocation res = GetRawDest(mir); - DCHECK(res.wide); - return res; - } - - RegLocation GetSrcWide(MIR* mir, int low) { - RegLocation res = GetRawSrc(mir, low); - DCHECK(res.wide); - return res; - } - - RegLocation GetBadLoc() { - return bad_loc; - } - - int GetMethodSReg() const { - return method_sreg_; - } - - /** - * @brief Used to obtain the number of compiler temporaries being used. - * @return Returns the number of compiler temporaries. - */ - size_t GetNumUsedCompilerTemps() const { - // Assume that the special temps will always be used. - return GetNumNonSpecialCompilerTemps() + max_available_special_compiler_temps_; - } - - /** - * @brief Used to obtain number of bytes needed for special temps. - * @details This space is always needed because temps have special location on stack. - * @return Returns number of bytes for the special temps. - */ - size_t GetNumBytesForSpecialTemps() const; - - /** - * @brief Used by backend as a hint for maximum number of bytes for non-special temps. - * @details Returns 4 bytes for each temp because that is the maximum amount needed - * for storing each temp. The BE could be smarter though and allocate a smaller - * spill region. - * @return Returns the maximum number of bytes needed for non-special temps. - */ - size_t GetMaximumBytesForNonSpecialTemps() const { - return GetNumNonSpecialCompilerTemps() * sizeof(uint32_t); - } - - /** - * @brief Used to obtain the number of non-special compiler temporaries being used. - * @return Returns the number of non-special compiler temporaries. - */ - size_t GetNumNonSpecialCompilerTemps() const { - return num_non_special_compiler_temps_; - } - - /** - * @brief Used to set the total number of available non-special compiler temporaries. - * @details Can fail setting the new max if there are more temps being used than the new_max. - * @param new_max The new maximum number of non-special compiler temporaries. - * @return Returns true if the max was set and false if failed to set. - */ - bool SetMaxAvailableNonSpecialCompilerTemps(size_t new_max) { - // Make sure that enough temps still exist for backend and also that the - // new max can still keep around all of the already requested temps. - if (new_max < (GetNumNonSpecialCompilerTemps() + reserved_temps_for_backend_)) { - return false; - } else { - max_available_non_special_compiler_temps_ = new_max; - return true; - } - } - - /** - * @brief Provides the number of non-special compiler temps available for use by ME. - * @details Even if this returns zero, special compiler temps are guaranteed to be available. - * Additionally, this makes sure to not use any temps reserved for BE only. - * @return Returns the number of available temps. - */ - size_t GetNumAvailableVRTemps(); - - /** - * @brief Used to obtain the maximum number of compiler temporaries that can be requested. - * @return Returns the maximum number of compiler temporaries, whether used or not. - */ - size_t GetMaxPossibleCompilerTemps() const { - return max_available_special_compiler_temps_ + max_available_non_special_compiler_temps_; - } - - /** - * @brief Used to signal that the compiler temps have been committed. - * @details This should be used once the number of temps can no longer change, - * such as after frame size is committed and cannot be changed. - */ - void CommitCompilerTemps() { - compiler_temps_committed_ = true; - } - - /** - * @brief Used to obtain a new unique compiler temporary. - * @details Two things are done for convenience when allocating a new compiler - * temporary. The ssa register is automatically requested and the information - * about reg location is filled. This helps when the temp is requested post - * ssa initialization, such as when temps are requested by the backend. - * @warning If the temp requested will be used for ME and have multiple versions, - * the sreg provided by the temp will be invalidated on next ssa recalculation. - * @param ct_type Type of compiler temporary requested. - * @param wide Whether we should allocate a wide temporary. - * @return Returns the newly created compiler temporary. - */ - CompilerTemp* GetNewCompilerTemp(CompilerTempType ct_type, bool wide); - - /** - * @brief Used to remove last created compiler temporary when it's not needed. - * @param temp the temporary to remove. - */ - void RemoveLastCompilerTemp(CompilerTempType ct_type, bool wide, CompilerTemp* temp); - - bool MethodIsLeaf() { - return attributes_ & METHOD_IS_LEAF; - } - - RegLocation GetRegLocation(int index) { - DCHECK((index >= 0) && (index < num_ssa_regs_)); - return reg_location_[index]; - } - - RegLocation GetMethodLoc() { - return reg_location_[method_sreg_]; - } - - bool IsBackEdge(BasicBlock* branch_bb, BasicBlockId target_bb_id) { - DCHECK_NE(target_bb_id, NullBasicBlockId); - DCHECK_LT(target_bb_id, topological_order_indexes_.size()); - DCHECK_LT(branch_bb->id, topological_order_indexes_.size()); - return topological_order_indexes_[target_bb_id] <= topological_order_indexes_[branch_bb->id]; - } - - bool IsSuspendCheckEdge(BasicBlock* branch_bb, BasicBlockId target_bb_id) { - if (!IsBackEdge(branch_bb, target_bb_id)) { - return false; - } - if (suspend_checks_in_loops_ == nullptr) { - // We didn't run suspend check elimination. - return true; - } - uint16_t target_depth = GetBasicBlock(target_bb_id)->nesting_depth; - return (suspend_checks_in_loops_[branch_bb->id] & (1u << (target_depth - 1u))) == 0; - } - - void CountBranch(DexOffset target_offset) { - if (target_offset <= current_offset_) { - backward_branches_++; - } else { - forward_branches_++; - } - } - - int GetBranchCount() { - return backward_branches_ + forward_branches_; - } - - // Is this vreg in the in set? - bool IsInVReg(uint32_t vreg) { - return (vreg >= GetFirstInVR()) && (vreg < GetFirstTempVR()); - } - - uint32_t GetNumOfCodeVRs() const { - return current_code_item_->registers_size_; - } - - uint32_t GetNumOfCodeAndTempVRs() const { - // Include all of the possible temps so that no structures overflow when initialized. - return GetNumOfCodeVRs() + GetMaxPossibleCompilerTemps(); - } - - uint32_t GetNumOfLocalCodeVRs() const { - // This also refers to the first "in" VR. - return GetNumOfCodeVRs() - current_code_item_->ins_size_; - } - - uint32_t GetNumOfInVRs() const { - return current_code_item_->ins_size_; - } - - uint32_t GetNumOfOutVRs() const { - return current_code_item_->outs_size_; - } - - uint32_t GetFirstInVR() const { - return GetNumOfLocalCodeVRs(); - } - - uint32_t GetFirstTempVR() const { - // Temp VRs immediately follow code VRs. - return GetNumOfCodeVRs(); - } - - uint32_t GetFirstSpecialTempVR() const { - // Special temps appear first in the ordering before non special temps. - return GetFirstTempVR(); - } - - uint32_t GetFirstNonSpecialTempVR() const { - // We always leave space for all the special temps before the non-special ones. - return GetFirstSpecialTempVR() + max_available_special_compiler_temps_; - } - - bool HasTryCatchBlocks() const { - return current_code_item_->tries_size_ != 0; - } - - void DumpCheckStats(); - MIR* FindMoveResult(BasicBlock* bb, MIR* mir); - - /* Return the base virtual register for a SSA name */ - int SRegToVReg(int ssa_reg) const { - return ssa_base_vregs_[ssa_reg]; - } - - void VerifyDataflow(); - void CheckForDominanceFrontier(BasicBlock* dom_bb, const BasicBlock* succ_bb); - bool EliminateNullChecksGate(); - bool EliminateNullChecks(BasicBlock* bb); - void EliminateNullChecksEnd(); - void InferTypesStart(); - bool InferTypes(BasicBlock* bb); - void InferTypesEnd(); - bool EliminateClassInitChecksGate(); - bool EliminateClassInitChecks(BasicBlock* bb); - void EliminateClassInitChecksEnd(); - bool ApplyGlobalValueNumberingGate(); - bool ApplyGlobalValueNumbering(BasicBlock* bb); - void ApplyGlobalValueNumberingEnd(); - bool EliminateDeadCodeGate(); - bool EliminateDeadCode(BasicBlock* bb); - void EliminateDeadCodeEnd(); - void GlobalValueNumberingCleanup(); - bool EliminateSuspendChecksGate(); - bool EliminateSuspendChecks(BasicBlock* bb); - - uint16_t GetGvnIFieldId(MIR* mir) const { - DCHECK(IsInstructionIGetOrIPut(mir->dalvikInsn.opcode)); - DCHECK_LT(mir->meta.ifield_lowering_info, ifield_lowering_infos_.size()); - DCHECK(temp_.gvn.ifield_ids != nullptr); - return temp_.gvn.ifield_ids[mir->meta.ifield_lowering_info]; - } - - uint16_t GetGvnSFieldId(MIR* mir) const { - DCHECK(IsInstructionSGetOrSPut(mir->dalvikInsn.opcode)); - DCHECK_LT(mir->meta.sfield_lowering_info, sfield_lowering_infos_.size()); - DCHECK(temp_.gvn.sfield_ids != nullptr); - return temp_.gvn.sfield_ids[mir->meta.sfield_lowering_info]; - } - - bool PuntToInterpreter() { - return punt_to_interpreter_; - } - - void SetPuntToInterpreter(bool val); - - void DisassembleExtendedInstr(const MIR* mir, std::string* decoded_mir); - char* GetDalvikDisassembly(const MIR* mir); - void ReplaceSpecialChars(std::string& str); - std::string GetSSAName(int ssa_reg); - std::string GetSSANameWithConst(int ssa_reg, bool singles_only); - void GetBlockName(BasicBlock* bb, char* name); - const char* GetShortyFromMethodReference(const MethodReference& target_method); - void DumpMIRGraph(); - CallInfo* NewMemCallInfo(BasicBlock* bb, MIR* mir, InvokeType type, bool is_range); - BasicBlock* NewMemBB(BBType block_type, int block_id); - MIR* NewMIR(); - MIR* AdvanceMIR(BasicBlock** p_bb, MIR* mir); - BasicBlock* NextDominatedBlock(BasicBlock* bb); - bool LayoutBlocks(BasicBlock* bb); - void ComputeTopologicalSortOrder(); - BasicBlock* CreateNewBB(BBType block_type); - - bool InlineSpecialMethodsGate(); - void InlineSpecialMethodsStart(); - void InlineSpecialMethods(BasicBlock* bb); - void InlineSpecialMethodsEnd(); - - /** - * @brief Perform the initial preparation for the Method Uses. - */ - void InitializeMethodUses(); - - /** - * @brief Perform the initial preparation for the Constant Propagation. - */ - void InitializeConstantPropagation(); - - /** - * @brief Perform the initial preparation for the SSA Transformation. - */ - void SSATransformationStart(); - - /** - * @brief Insert a the operands for the Phi nodes. - * @param bb the considered BasicBlock. - * @return true - */ - bool InsertPhiNodeOperands(BasicBlock* bb); - - /** - * @brief Perform the cleanup after the SSA Transformation. - */ - void SSATransformationEnd(); - - /** - * @brief Perform constant propagation on a BasicBlock. - * @param bb the considered BasicBlock. - */ - void DoConstantPropagation(BasicBlock* bb); - - /** - * @brief Get use count weight for a given block. - * @param bb the BasicBlock. - */ - uint32_t GetUseCountWeight(BasicBlock* bb) const; - - /** - * @brief Count the uses in the BasicBlock - * @param bb the BasicBlock - */ - void CountUses(BasicBlock* bb); - - static uint64_t GetDataFlowAttributes(Instruction::Code opcode); - static uint64_t GetDataFlowAttributes(MIR* mir); - - /** - * @brief Combine BasicBlocks - * @param the BasicBlock we are considering - */ - void CombineBlocks(BasicBlock* bb); - - void ClearAllVisitedFlags(); - - void AllocateSSAUseData(MIR *mir, int num_uses); - void AllocateSSADefData(MIR *mir, int num_defs); - void CalculateBasicBlockInformation(const PassManager* const post_opt); - void ComputeDFSOrders(); - void ComputeDefBlockMatrix(); - void ComputeDominators(); - void CompilerInitializeSSAConversion(); - virtual void InitializeBasicBlockDataFlow(); - void FindPhiNodeBlocks(); - void DoDFSPreOrderSSARename(BasicBlock* block); - - bool DfsOrdersUpToDate() const { - return dfs_orders_up_to_date_; - } - - bool DominationUpToDate() const { - return domination_up_to_date_; - } - - bool MirSsaRepUpToDate() const { - return mir_ssa_rep_up_to_date_; - } - - bool TopologicalOrderUpToDate() const { - return topological_order_up_to_date_; - } - - /* - * IsDebugBuild sanity check: keep track of the Dex PCs for catch entries so that later on - * we can verify that all catch entries have native PC entries. - */ - std::set catches_; - - // TODO: make these private. - RegLocation* reg_location_; // Map SSA names to location. - ArenaSafeMap block_id_map_; // Block collapse lookup cache. - - static const char* extended_mir_op_names_[kMirOpLast - kMirOpFirst]; - - void HandleSSADef(int* defs, int dalvik_reg, int reg_index); - - protected: - int FindCommonParent(int block1, int block2); - void ComputeSuccLineIn(ArenaBitVector* dest, const ArenaBitVector* src1, - const ArenaBitVector* src2); - void HandleLiveInUse(ArenaBitVector* use_v, ArenaBitVector* def_v, - ArenaBitVector* live_in_v, int dalvik_reg_id); - void HandleDef(ArenaBitVector* def_v, int dalvik_reg_id); - void HandleExtended(ArenaBitVector* use_v, ArenaBitVector* def_v, - ArenaBitVector* live_in_v, - const MIR::DecodedInstruction& d_insn); - bool DoSSAConversion(BasicBlock* bb); - int ParseInsn(const uint16_t* code_ptr, MIR::DecodedInstruction* decoded_instruction); - bool ContentIsInsn(const uint16_t* code_ptr); - BasicBlock* SplitBlock(DexOffset code_offset, BasicBlock* orig_block, - BasicBlock** immed_pred_block_p); - BasicBlock* FindBlock(DexOffset code_offset, bool create, BasicBlock** immed_pred_block_p, - ScopedArenaVector* dex_pc_to_block_map); - void ProcessTryCatchBlocks(ScopedArenaVector* dex_pc_to_block_map); - bool IsBadMonitorExitCatch(NarrowDexOffset monitor_exit_offset, NarrowDexOffset catch_offset); - BasicBlock* ProcessCanBranch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width, - int flags, const uint16_t* code_ptr, const uint16_t* code_end, - ScopedArenaVector* dex_pc_to_block_map); - BasicBlock* ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width, - int flags, - ScopedArenaVector* dex_pc_to_block_map); - BasicBlock* ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width, - int flags, ArenaBitVector* try_block_addr, const uint16_t* code_ptr, - const uint16_t* code_end, - ScopedArenaVector* dex_pc_to_block_map); - int AddNewSReg(int v_reg); - void HandleSSAUse(int* uses, int dalvik_reg, int reg_index); - void DataFlowSSAFormat35C(MIR* mir); - void DataFlowSSAFormat3RC(MIR* mir); - void DataFlowSSAFormatExtended(MIR* mir); - bool FindLocalLiveIn(BasicBlock* bb); - bool VerifyPredInfo(BasicBlock* bb); - BasicBlock* NeedsVisit(BasicBlock* bb); - BasicBlock* NextUnvisitedSuccessor(BasicBlock* bb); - void MarkPreOrder(BasicBlock* bb); - void RecordDFSOrders(BasicBlock* bb); - void ComputeDomPostOrderTraversal(BasicBlock* bb); - int GetSSAUseCount(int s_reg); - bool BasicBlockOpt(BasicBlock* bb); - void MultiplyAddOpt(BasicBlock* bb); - - /** - * @brief Check whether the given MIR is possible to throw an exception. - * @param mir The mir to check. - * @return Returns 'true' if the given MIR might throw an exception. - */ - bool CanThrow(MIR* mir) const; - - /** - * @brief Combine multiply and add/sub MIRs into corresponding extended MAC MIR. - * @param mul_mir The multiply MIR to be combined. - * @param add_mir The add/sub MIR to be combined. - * @param mul_is_first_addend 'true' if multiply product is the first addend of add operation. - * @param is_wide 'true' if the operations are long type. - * @param is_sub 'true' if it is a multiply-subtract operation. - */ - void CombineMultiplyAdd(MIR* mul_mir, MIR* add_mir, bool mul_is_first_addend, - bool is_wide, bool is_sub); - /* - * @brief Check whether the first MIR anti-depends on the second MIR. - * @details To check whether one of first MIR's uses of vregs is redefined by the second MIR, - * i.e. there is a write-after-read dependency. - * @param first The first MIR. - * @param second The second MIR. - * @param Returns true if there is a write-after-read dependency. - */ - bool HasAntiDependency(MIR* first, MIR* second); - - bool BuildExtendedBBList(class BasicBlock* bb); - bool FillDefBlockMatrix(BasicBlock* bb); - void InitializeDominationInfo(BasicBlock* bb); - bool ComputeblockIDom(BasicBlock* bb); - bool ComputeBlockDominators(BasicBlock* bb); - bool SetDominators(BasicBlock* bb); - bool ComputeBlockLiveIns(BasicBlock* bb); - bool ComputeDominanceFrontier(BasicBlock* bb); - - void CountChecks(BasicBlock* bb); - void AnalyzeBlock(BasicBlock* bb, struct MethodStats* stats); - bool ComputeSkipCompilation(struct MethodStats* stats, bool skip_default, - std::string* skip_message); - - CompilationUnit* const cu_; - ArenaVector ssa_base_vregs_; - ArenaVector ssa_subscripts_; - // Map original Dalvik virtual reg i to the current SSA name. - int32_t* vreg_to_ssa_map_; // length == method->registers_size - int* ssa_last_defs_; // length == method->registers_size - ArenaBitVector* is_constant_v_; // length == num_ssa_reg - int* constant_values_; // length == num_ssa_reg - // Use counts of ssa names. - ArenaVector use_counts_; // Weighted by nesting depth - ArenaVector raw_use_counts_; // Not weighted - unsigned int num_reachable_blocks_; - unsigned int max_num_reachable_blocks_; - bool dfs_orders_up_to_date_; - bool domination_up_to_date_; - bool mir_ssa_rep_up_to_date_; - bool topological_order_up_to_date_; - ArenaVector dfs_order_; - ArenaVector dfs_post_order_; - ArenaVector dom_post_order_traversal_; - ArenaVector topological_order_; - // Indexes in topological_order_ need to be only as big as the BasicBlockId. - static_assert(sizeof(BasicBlockId) == sizeof(uint16_t), "Assuming 16 bit BasicBlockId"); - // For each loop head, remember the past-the-end index of the end of the loop. 0 if not loop head. - ArenaVector topological_order_loop_ends_; - // Map BB ids to topological_order_ indexes. 0xffff if not included (hidden or null block). - ArenaVector topological_order_indexes_; - // Stack of the loop head indexes and recalculation flags for RepeatingTopologicalSortIterator. - ArenaVector> topological_order_loop_head_stack_; - size_t max_nested_loops_; - int* i_dom_list_; - std::unique_ptr temp_scoped_alloc_; - // Union of temporaries used by different passes. - union { - // Class init check elimination. - struct { - size_t num_class_bits; // 2 bits per class: class initialized and class in dex cache. - ArenaBitVector* work_classes_to_check; - ArenaBitVector** ending_classes_to_check_matrix; // num_blocks_ x num_class_bits. - uint16_t* indexes; - } cice; - // Null check elimination. - struct { - size_t num_vregs; - ArenaBitVector* work_vregs_to_check; - ArenaBitVector** ending_vregs_to_check_matrix; // num_blocks_ x num_vregs. - } nce; - // Special method inlining. - struct { - size_t num_indexes; - ArenaBitVector* processed_indexes; - uint16_t* lowering_infos; - } smi; - // SSA transformation. - struct { - size_t num_vregs; - ArenaBitVector* work_live_vregs; - ArenaBitVector** def_block_matrix; // num_vregs x num_blocks_. - ArenaBitVector** phi_node_blocks; // num_vregs x num_blocks_. - TypeInference* ti; - } ssa; - // Global value numbering. - struct { - GlobalValueNumbering* gvn; - uint16_t* ifield_ids; // Part of GVN/LVN but cached here for LVN to avoid recalculation. - uint16_t* sfield_ids; // Ditto. - GvnDeadCodeElimination* dce; - } gvn; - } temp_; - static const int kInvalidEntry = -1; - ArenaVector block_list_; - ArenaBitVector* try_block_addr_; - BasicBlock* entry_block_; - BasicBlock* exit_block_; - const DexFile::CodeItem* current_code_item_; - ArenaVector m_units_; // List of methods included in this graph - typedef std::pair MIRLocation; // Insert point, (m_unit_ index, offset) - ArenaVector method_stack_; // Include stack - int current_method_; - DexOffset current_offset_; // Offset in code units - int def_count_; // Used to estimate size of ssa name storage. - int* opcode_count_; // Dex opcode coverage stats. - int num_ssa_regs_; // Number of names following SSA transformation. - ArenaVector extended_basic_blocks_; // Heads of block "traces". - int method_sreg_; - unsigned int attributes_; - Checkstats* checkstats_; - ArenaAllocator* const arena_; - int backward_branches_; - int forward_branches_; - size_t num_non_special_compiler_temps_; // Keeps track of allocated non-special compiler temps. These are VRs that are in compiler temp region on stack. - size_t max_available_non_special_compiler_temps_; // Keeps track of maximum available non-special temps. - size_t max_available_special_compiler_temps_; // Keeps track of maximum available special temps. - bool requested_backend_temp_; // Keeps track whether BE temps have been requested. - size_t reserved_temps_for_backend_; // Keeps track of the remaining temps that are reserved for BE. - bool compiler_temps_committed_; // Keeps track whether number of temps has been frozen (for example post frame size calculation). - bool punt_to_interpreter_; // Difficult or not worthwhile - just interpret. - uint64_t merged_df_flags_; - ArenaVector ifield_lowering_infos_; - ArenaVector sfield_lowering_infos_; - ArenaVector method_lowering_infos_; - - // In the suspend check elimination pass we determine for each basic block and enclosing - // loop whether there's guaranteed to be a suspend check on the path from the loop head - // to this block. If so, we can eliminate the back-edge suspend check. - // The bb->id is index into suspend_checks_in_loops_ and the loop head's depth is bit index - // in a suspend_checks_in_loops_[bb->id]. - uint32_t* suspend_checks_in_loops_; - - static const uint64_t oat_data_flow_attributes_[kMirOpLast]; - - friend class MirOptimizationTest; - friend class ClassInitCheckEliminationTest; - friend class SuspendCheckEliminationTest; - friend class NullCheckEliminationTest; - friend class GlobalValueNumberingTest; - friend class GvnDeadCodeEliminationTest; - friend class LocalValueNumberingTest; - friend class TopologicalSortOrderTest; - friend class TypeInferenceTest; - friend class QuickCFITest; - friend class QuickAssembleX86TestBase; -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_MIR_GRAPH_H_ diff --git a/compiler/dex/mir_graph_test.cc b/compiler/dex/mir_graph_test.cc deleted file mode 100644 index 7858681e00..0000000000 --- a/compiler/dex/mir_graph_test.cc +++ /dev/null @@ -1,446 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "compiler_ir.h" -#include "dataflow_iterator-inl.h" -#include "mir_graph.h" -#include "gtest/gtest.h" - -namespace art { - -class TopologicalSortOrderTest : public testing::Test { - protected: - struct BBDef { - static constexpr size_t kMaxSuccessors = 4; - static constexpr size_t kMaxPredecessors = 4; - - BBType type; - size_t num_successors; - BasicBlockId successors[kMaxPredecessors]; - size_t num_predecessors; - BasicBlockId predecessors[kMaxPredecessors]; - }; - -#define DEF_SUCC0() \ - 0u, { } -#define DEF_SUCC1(s1) \ - 1u, { s1 } -#define DEF_SUCC2(s1, s2) \ - 2u, { s1, s2 } -#define DEF_SUCC3(s1, s2, s3) \ - 3u, { s1, s2, s3 } -#define DEF_SUCC4(s1, s2, s3, s4) \ - 4u, { s1, s2, s3, s4 } -#define DEF_PRED0() \ - 0u, { } -#define DEF_PRED1(p1) \ - 1u, { p1 } -#define DEF_PRED2(p1, p2) \ - 2u, { p1, p2 } -#define DEF_PRED3(p1, p2, p3) \ - 3u, { p1, p2, p3 } -#define DEF_PRED4(p1, p2, p3, p4) \ - 4u, { p1, p2, p3, p4 } -#define DEF_BB(type, succ, pred) \ - { type, succ, pred } - - void DoPrepareBasicBlocks(const BBDef* defs, size_t count) { - cu_.mir_graph->block_id_map_.clear(); - cu_.mir_graph->block_list_.clear(); - ASSERT_LT(3u, count); // null, entry, exit and at least one bytecode block. - ASSERT_EQ(kNullBlock, defs[0].type); - ASSERT_EQ(kEntryBlock, defs[1].type); - ASSERT_EQ(kExitBlock, defs[2].type); - for (size_t i = 0u; i != count; ++i) { - const BBDef* def = &defs[i]; - BasicBlock* bb = cu_.mir_graph->CreateNewBB(def->type); - if (def->num_successors <= 2) { - bb->successor_block_list_type = kNotUsed; - bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : 0u; - bb->taken = (def->num_successors >= 2) ? def->successors[1] : 0u; - } else { - bb->successor_block_list_type = kPackedSwitch; - bb->fall_through = 0u; - bb->taken = 0u; - bb->successor_blocks.reserve(def->num_successors); - for (size_t j = 0u; j != def->num_successors; ++j) { - SuccessorBlockInfo* successor_block_info = - static_cast(cu_.arena.Alloc(sizeof(SuccessorBlockInfo), - kArenaAllocSuccessors)); - successor_block_info->block = j; - successor_block_info->key = 0u; // Not used by class init check elimination. - bb->successor_blocks.push_back(successor_block_info); - } - } - bb->predecessors.assign(def->predecessors, def->predecessors + def->num_predecessors); - if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) { - bb->data_flow_info = static_cast( - cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo)); - } - } - ASSERT_EQ(count, cu_.mir_graph->block_list_.size()); - cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1]; - ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type); - cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_[2]; - ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type); - - DexFile::CodeItem* code_item = static_cast(cu_.arena.Alloc(sizeof(DexFile::CodeItem), - kArenaAllocMisc)); - cu_.mir_graph->current_code_item_ = code_item; - } - - template - void PrepareBasicBlocks(const BBDef (&defs)[count]) { - DoPrepareBasicBlocks(defs, count); - } - - void ComputeTopologicalSortOrder() { - cu_.mir_graph->SSATransformationStart(); - cu_.mir_graph->ComputeDFSOrders(); - cu_.mir_graph->ComputeDominators(); - cu_.mir_graph->ComputeTopologicalSortOrder(); - cu_.mir_graph->SSATransformationEnd(); - ASSERT_FALSE(cu_.mir_graph->topological_order_.empty()); - ASSERT_FALSE(cu_.mir_graph->topological_order_loop_ends_.empty()); - ASSERT_FALSE(cu_.mir_graph->topological_order_indexes_.empty()); - ASSERT_EQ(cu_.mir_graph->GetNumBlocks(), cu_.mir_graph->topological_order_indexes_.size()); - for (size_t i = 0, size = cu_.mir_graph->GetTopologicalSortOrder().size(); i != size; ++i) { - ASSERT_LT(cu_.mir_graph->topological_order_[i], cu_.mir_graph->GetNumBlocks()); - BasicBlockId id = cu_.mir_graph->topological_order_[i]; - EXPECT_EQ(i, cu_.mir_graph->topological_order_indexes_[id]); - } - } - - void DoCheckOrder(const BasicBlockId* ids, size_t count) { - ASSERT_EQ(count, cu_.mir_graph->GetTopologicalSortOrder().size()); - for (size_t i = 0; i != count; ++i) { - EXPECT_EQ(ids[i], cu_.mir_graph->GetTopologicalSortOrder()[i]) << i; - } - } - - template - void CheckOrder(const BasicBlockId (&ids)[count]) { - DoCheckOrder(ids, count); - } - - void DoCheckLoopEnds(const uint16_t* ends, size_t count) { - for (size_t i = 0; i != count; ++i) { - ASSERT_LT(i, cu_.mir_graph->GetTopologicalSortOrderLoopEnds().size()); - EXPECT_EQ(ends[i], cu_.mir_graph->GetTopologicalSortOrderLoopEnds()[i]) << i; - } - } - - template - void CheckLoopEnds(const uint16_t (&ends)[count]) { - DoCheckLoopEnds(ends, count); - } - - TopologicalSortOrderTest() - : pool_(), - cu_(&pool_, kRuntimeISA, nullptr, nullptr) { - cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); - } - - ArenaPool pool_; - CompilationUnit cu_; -}; - -TEST_F(TopologicalSortOrderTest, DoWhile) { - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)), // "taken" loops to self. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), - }; - const BasicBlockId expected_order[] = { - 1, 3, 4, 5, 2 - }; - const uint16_t loop_ends[] = { - 0, 0, 3, 0, 0 - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); -} - -TEST_F(TopologicalSortOrderTest, While) { - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED2(1, 4)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(3), DEF_PRED1(3)), // Loops to 3. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)), - }; - const BasicBlockId expected_order[] = { - 1, 3, 4, 5, 2 - }; - const uint16_t loop_ends[] = { - 0, 3, 0, 0, 0 - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); -} - -TEST_F(TopologicalSortOrderTest, WhileWithTwoBackEdges) { - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 6), DEF_PRED3(1, 4, 5)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 3), DEF_PRED1(3)), // Loops to 3. - DEF_BB(kDalvikByteCode, DEF_SUCC1(3), DEF_PRED1(4)), // Loops to 3. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)), - }; - const BasicBlockId expected_order[] = { - 1, 3, 4, 5, 6, 2 - }; - const uint16_t loop_ends[] = { - 0, 4, 0, 0, 0, 0 - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); -} - -TEST_F(TopologicalSortOrderTest, NestedLoop) { - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(7)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 7), DEF_PRED2(1, 6)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 6), DEF_PRED2(3, 5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(4)), // Loops to 4. - DEF_BB(kDalvikByteCode, DEF_SUCC1(3), DEF_PRED1(4)), // Loops to 3. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)), - }; - const BasicBlockId expected_order[] = { - 1, 3, 4, 5, 6, 7, 2 - }; - const uint16_t loop_ends[] = { - 0, 5, 4, 0, 0, 0, 0 - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); -} - -TEST_F(TopologicalSortOrderTest, NestedLoopHeadLoops) { - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 6), DEF_PRED2(1, 4)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 3), DEF_PRED2(3, 5)), // Nested head, loops to 3. - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(4)), // Loops to 4. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)), - }; - const BasicBlockId expected_order[] = { - 1, 3, 4, 5, 6, 2 - }; - const uint16_t loop_ends[] = { - 0, 4, 4, 0, 0, 0 - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); -} - -TEST_F(TopologicalSortOrderTest, NestedLoopSameBackBranchBlock) { - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 6), DEF_PRED2(1, 5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED2(3, 5)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 3), DEF_PRED1(4)), // Loops to 4 and 3. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)), - }; - const BasicBlockId expected_order[] = { - 1, 3, 4, 5, 6, 2 - }; - const uint16_t loop_ends[] = { - 0, 4, 4, 0, 0, 0 - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); -} - -TEST_F(TopologicalSortOrderTest, TwoReorderedInnerLoops) { - // This is a simplified version of real code graph where the branch from 8 to 5 must prevent - // the block 5 from being considered a loop head before processing the loop 7-8. - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(9)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 9), DEF_PRED2(1, 5)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 7), DEF_PRED1(3)), // Branch over loop in 5. - DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 3), DEF_PRED3(4, 6, 8)), // Loops to 4; inner loop. - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(5)), // Loops to 5. - DEF_BB(kDalvikByteCode, DEF_SUCC1(8), DEF_PRED2(4, 8)), // Loop head. - DEF_BB(kDalvikByteCode, DEF_SUCC2(7, 5), DEF_PRED1(7)), // Loops to 7; branches to 5. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)), - }; - const BasicBlockId expected_order[] = { - 1, 3, 4, 7, 8, 5, 6, 9, 2 - }; - const uint16_t loop_ends[] = { - 0, 7, 0, 5, 0, 7, 0, 0, 0 - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); -} - -TEST_F(TopologicalSortOrderTest, NestedLoopWithBackEdgeAfterOuterLoopBackEdge) { - // This is a simplified version of real code graph. The back-edge from 7 to the inner - // loop head 4 comes after the back-edge from 6 to the outer loop head 3. To make this - // appear a bit more complex, there's also a back-edge from 5 to 4. - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(7)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED2(1, 6)), // Outer loop head. - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 6), DEF_PRED3(3, 5, 7)), // Inner loop head. - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(4)), // Loops to inner loop head 4. - DEF_BB(kDalvikByteCode, DEF_SUCC2(7, 3), DEF_PRED1(4)), // Loops to outer loop head 3. - DEF_BB(kDalvikByteCode, DEF_SUCC2(2, 4), DEF_PRED1(6)), // Loops to inner loop head 4. - }; - const BasicBlockId expected_order[] = { - // NOTE: The 5 goes before 6 only because 5 is a "fall-through" from 4 while 6 is "taken". - 1, 3, 4, 5, 6, 7, 2 - }; - const uint16_t loop_ends[] = { - 0, 6, 6, 0, 0, 0, 0 - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); -} - -TEST_F(TopologicalSortOrderTest, LoopWithTwoEntryPoints) { - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(7)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED2(3, 6)), // Fall-back block is chosen as - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED2(3, 4)), // the earlier from these two. - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 7), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(6)), - }; - const BasicBlockId expected_order[] = { - 1, 3, 4, 5, 6, 7, 2 - }; - const uint16_t loop_ends[] = { - 0, 0, 5, 0, 0, 0, 0 - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); -} - -TEST_F(TopologicalSortOrderTest, UnnaturalLoops) { - const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(10)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED2(11, 3)), // Unnatural loop head (top-level). - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED2(3, 4)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(9, 7), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(8), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(9), DEF_PRED2(10, 7)), // Unnatural loop head (nested). - DEF_BB(kDalvikByteCode, DEF_SUCC1(10), DEF_PRED2(6, 8)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(8, 11), DEF_PRED1(9)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 2), DEF_PRED1(10)), - }; - const BasicBlockId expected_order[] = { - 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 2 - }; - const uint16_t loop_ends[] = { - 0, 0, 10, 0, 0, 0, 9, 0, 0, 0, 0, - }; - - PrepareBasicBlocks(bbs); - ComputeTopologicalSortOrder(); - CheckOrder(expected_order); - CheckLoopEnds(loop_ends); - - const std::pair expected_and_change[] = { - { 1, false }, - { 3, false }, - { 4, true }, // Initial run of the outer loop. - { 5, true }, - { 6, true }, - { 7, true }, - { 8, true }, // Initial run of the inner loop. - { 9, true }, - { 10, true }, - { 8, true }, // Recalculation of the inner loop - changed. - { 9, true }, - { 10, true }, - { 8, false }, // Recalculation of the inner loop - unchanged. - { 11, true }, - { 4, true }, // Recalculation of the outer loop - changed. - { 5, true }, - { 6, true }, - { 7, false }, // No change: skip inner loop head because inputs are unchanged. - { 9, true }, - { 10, true }, - { 8, true }, // Recalculation of the inner loop - changed. - { 9, true }, - { 10, true }, - { 8, false }, // Recalculation of the inner loop - unchanged. - { 11, true }, - { 4, false }, // Recalculation of the outer loop - unchanged. - { 2, false }, - }; - size_t pos = 0; - LoopRepeatingTopologicalSortIterator iter(cu_.mir_graph.get()); - bool change = false; - for (BasicBlock* bb = iter.Next(change); bb != nullptr; bb = iter.Next(change)) { - ASSERT_NE(arraysize(expected_and_change), pos); - ASSERT_EQ(expected_and_change[pos].first, bb->id) << pos; - change = expected_and_change[pos].second; - ++pos; - } - ASSERT_EQ(arraysize(expected_and_change), pos); -} - -} // namespace art diff --git a/compiler/dex/mir_method_info.cc b/compiler/dex/mir_method_info.cc deleted file mode 100644 index c250bd9fd2..0000000000 --- a/compiler/dex/mir_method_info.cc +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -# include "mir_method_info.h" - -#include "dex/compiler_ir.h" -#include "dex/quick/dex_file_method_inliner.h" -#include "dex/quick/dex_file_to_method_inliner_map.h" -#include "dex/verified_method.h" -#include "driver/compiler_driver.h" -#include "driver/dex_compilation_unit.h" -#include "driver/compiler_driver-inl.h" -#include "driver/compiler_options.h" -#include "mirror/class_loader.h" // Only to allow casts in Handle. -#include "mirror/dex_cache.h" // Only to allow casts in Handle. -#include "scoped_thread_state_change.h" -#include "handle_scope-inl.h" - -namespace art { - -void MirMethodLoweringInfo::Resolve(CompilerDriver* compiler_driver, - const DexCompilationUnit* mUnit, - MirMethodLoweringInfo* method_infos, size_t count) { - if (kIsDebugBuild) { - DCHECK(method_infos != nullptr); - DCHECK_NE(count, 0u); - for (auto it = method_infos, end = method_infos + count; it != end; ++it) { - MirMethodLoweringInfo unresolved(it->MethodIndex(), it->GetInvokeType(), it->IsQuickened()); - unresolved.declaring_dex_file_ = it->declaring_dex_file_; - unresolved.vtable_idx_ = it->vtable_idx_; - if (it->target_dex_file_ != nullptr) { - unresolved.target_dex_file_ = it->target_dex_file_; - unresolved.target_method_idx_ = it->target_method_idx_; - } - if (kIsDebugBuild) { - unresolved.CheckEquals(*it); - } - } - } - - // We're going to resolve methods and check access in a tight loop. It's better to hold - // the lock and needed references once than re-acquiring them again and again. - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<4> hs(soa.Self()); - Handle dex_cache(hs.NewHandle(compiler_driver->GetDexCache(mUnit))); - Handle class_loader( - hs.NewHandle(compiler_driver->GetClassLoader(soa, mUnit))); - Handle referrer_class(hs.NewHandle( - compiler_driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, mUnit))); - auto current_dex_cache(hs.NewHandle(nullptr)); - // Even if the referrer class is unresolved (i.e. we're compiling a method without class - // definition) we still want to resolve methods and record all available info. - Runtime* const runtime = Runtime::Current(); - const DexFile* const dex_file = mUnit->GetDexFile(); - const bool use_jit = runtime->UseJit(); - const VerifiedMethod* const verified_method = mUnit->GetVerifiedMethod(); - DexFileToMethodInlinerMap* inliner_map = compiler_driver->GetMethodInlinerMap(); - DexFileMethodInliner* default_inliner = - (inliner_map != nullptr) ? inliner_map->GetMethodInliner(dex_file) : nullptr; - - for (auto it = method_infos, end = method_infos + count; it != end; ++it) { - // For quickened invokes, the dex method idx is actually the mir offset. - if (it->IsQuickened()) { - const auto* dequicken_ref = verified_method->GetDequickenIndex(it->method_idx_); - CHECK(dequicken_ref != nullptr); - it->target_dex_file_ = dequicken_ref->dex_file; - it->target_method_idx_ = dequicken_ref->index; - } - // Remember devirtualized invoke target and set the called method to the default. - MethodReference devirt_ref(it->target_dex_file_, it->target_method_idx_); - MethodReference* devirt_target = (it->target_dex_file_ != nullptr) ? &devirt_ref : nullptr; - InvokeType invoke_type = it->GetInvokeType(); - ArtMethod* resolved_method = nullptr; - - bool string_init = false; - if (default_inliner->IsStringInitMethodIndex(it->MethodIndex())) { - string_init = true; - invoke_type = kDirect; - } - - if (!it->IsQuickened()) { - it->target_dex_file_ = dex_file; - it->target_method_idx_ = it->MethodIndex(); - current_dex_cache.Assign(dex_cache.Get()); - resolved_method = compiler_driver->ResolveMethod(soa, dex_cache, class_loader, mUnit, - it->target_method_idx_, invoke_type, true); - } else { - // The method index is actually the dex PC in this case. - // Calculate the proper dex file and target method idx. - - // We must be in JIT mode if we get here. - CHECK(use_jit); - - // The invoke type better be virtual, except for the string init special case above. - CHECK_EQ(invoke_type, string_init ? kDirect : kVirtual); - // Don't devirt if we are in a different dex file since we can't have direct invokes in - // another dex file unless we always put a direct / patch pointer. - devirt_target = nullptr; - current_dex_cache.Assign(runtime->GetClassLinker()->FindDexCache( - soa.Self(), *it->target_dex_file_)); - CHECK(current_dex_cache.Get() != nullptr); - DexCompilationUnit cu( - mUnit->GetCompilationUnit(), mUnit->GetClassLoader(), mUnit->GetClassLinker(), - *it->target_dex_file_, nullptr /* code_item not used */, 0u /* class_def_idx not used */, - it->target_method_idx_, 0u /* access_flags not used */, - nullptr /* verified_method not used */, - current_dex_cache); - resolved_method = compiler_driver->ResolveMethod(soa, current_dex_cache, class_loader, &cu, - it->target_method_idx_, invoke_type, false); - if (resolved_method == nullptr) { - // If the method is null then it should be a miranda method, in this case try - // re-loading it, this time as an interface method. The actual miranda method is in the - // vtable, but it will resolve to an interface method. - resolved_method = compiler_driver->ResolveMethod( - soa, current_dex_cache, class_loader, &cu, it->target_method_idx_, kInterface, false); - CHECK(resolved_method != nullptr); - } - if (resolved_method != nullptr) { - // Since this was a dequickened virtual, it is guaranteed to be resolved. However, it may be - // resolved to an interface method. If this is the case then change the invoke type to - // interface with the assumption that sharp_type will be kVirtual. - if (resolved_method->GetInvokeType() == kInterface) { - it->flags_ = (it->flags_ & ~(kInvokeTypeMask << kBitInvokeTypeBegin)) | - (static_cast(kInterface) << kBitInvokeTypeBegin); - } - } - } - if (UNLIKELY(resolved_method == nullptr)) { - continue; - } - - compiler_driver->GetResolvedMethodDexFileLocation(resolved_method, - &it->declaring_dex_file_, &it->declaring_class_idx_, &it->declaring_method_idx_); - if (!it->IsQuickened()) { - // For quickened invoke virtuals we may have desharpened to an interface method which - // wont give us the right method index, in this case blindly dispatch or else we can't - // compile the method. Converting the invoke to interface dispatch doesn't work since we - // have no way to get the dex method index for quickened invoke virtuals in the interface - // trampolines. - it->vtable_idx_ = - compiler_driver->GetResolvedMethodVTableIndex(resolved_method, invoke_type); - } - - MethodReference target_method(it->target_dex_file_, it->target_method_idx_); - int fast_path_flags = compiler_driver->IsFastInvoke( - soa, current_dex_cache, class_loader, mUnit, referrer_class.Get(), resolved_method, - &invoke_type, &target_method, devirt_target, &it->direct_code_, &it->direct_method_); - const bool is_referrers_class = referrer_class.Get() == resolved_method->GetDeclaringClass(); - const bool is_class_initialized = - compiler_driver->IsMethodsClassInitialized(referrer_class.Get(), resolved_method); - - // Check if the target method is intrinsic or special. - InlineMethodFlags is_intrinsic_or_special = kNoInlineMethodFlags; - if (inliner_map != nullptr) { - auto* inliner = (target_method.dex_file == dex_file) - ? default_inliner - : inliner_map->GetMethodInliner(target_method.dex_file); - is_intrinsic_or_special = inliner->IsIntrinsicOrSpecial(target_method.dex_method_index); - } - - uint16_t other_flags = it->flags_ & - ~(kFlagFastPath | kFlagIsIntrinsic | kFlagIsSpecial | kFlagClassIsInitialized | - (kInvokeTypeMask << kBitSharpTypeBegin)); - it->flags_ = other_flags | - // String init path is a special always-fast path. - (fast_path_flags != 0 || string_init ? kFlagFastPath : 0u) | - ((is_intrinsic_or_special & kInlineIntrinsic) != 0 ? kFlagIsIntrinsic : 0u) | - ((is_intrinsic_or_special & kInlineSpecial) != 0 ? kFlagIsSpecial : 0u) | - (static_cast(invoke_type) << kBitSharpTypeBegin) | - (is_referrers_class ? kFlagIsReferrersClass : 0u) | - (is_class_initialized ? kFlagClassIsInitialized : 0u); - it->target_dex_file_ = target_method.dex_file; - it->target_method_idx_ = target_method.dex_method_index; - it->stats_flags_ = fast_path_flags; - if (string_init) { - it->direct_code_ = 0; - } - } -} - -} // namespace art diff --git a/compiler/dex/mir_method_info.h b/compiler/dex/mir_method_info.h deleted file mode 100644 index 4512f35a99..0000000000 --- a/compiler/dex/mir_method_info.h +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_MIR_METHOD_INFO_H_ -#define ART_COMPILER_DEX_MIR_METHOD_INFO_H_ - -#include "base/logging.h" -#include "base/macros.h" -#include "base/mutex.h" -#include "invoke_type.h" -#include "method_reference.h" - -namespace art { - -class CompilerDriver; -class DexCompilationUnit; -class DexFile; - -class MirMethodInfo { - public: - uint16_t MethodIndex() const { - return method_idx_; - } - - bool IsStatic() const { - return (flags_ & kFlagIsStatic) != 0u; - } - - bool IsResolved() const { - return declaring_dex_file_ != nullptr; - } - - const DexFile* DeclaringDexFile() const { - return declaring_dex_file_; - } - void SetDeclaringDexFile(const DexFile* dex_file) { - declaring_dex_file_ = dex_file; - } - - uint16_t DeclaringClassIndex() const { - return declaring_class_idx_; - } - - uint16_t DeclaringMethodIndex() const { - return declaring_method_idx_; - } - - protected: - enum { - kBitIsStatic = 0, - kMethodInfoBitEnd - }; - static_assert(kMethodInfoBitEnd <= 16, "Too many flags"); - static constexpr uint16_t kFlagIsStatic = 1u << kBitIsStatic; - - MirMethodInfo(uint16_t method_idx, uint16_t flags) - : method_idx_(method_idx), - flags_(flags), - declaring_method_idx_(0u), - declaring_class_idx_(0u), - declaring_dex_file_(nullptr) { - } - - // Make copy-ctor/assign/dtor protected to avoid slicing. - MirMethodInfo(const MirMethodInfo& other) = default; - MirMethodInfo& operator=(const MirMethodInfo& other) = default; - ~MirMethodInfo() = default; - - // The method index in the compiling method's dex file. - uint16_t method_idx_; - // Flags, for volatility and derived class data. - uint16_t flags_; - // The method index in the dex file that defines the method, 0 if unresolved. - uint16_t declaring_method_idx_; - // The type index of the class declaring the method, 0 if unresolved. - uint16_t declaring_class_idx_; - // The dex file that defines the class containing the method and the method, - // null if unresolved. - const DexFile* declaring_dex_file_; -}; - -class MirMethodLoweringInfo : public MirMethodInfo { - public: - // For each requested method retrieve the method's declaring location (dex file, class - // index and method index) and compute whether we can fast path the method call. For fast - // path methods, retrieve the method's vtable index and direct code and method when applicable. - static void Resolve(CompilerDriver* compiler_driver, const DexCompilationUnit* mUnit, - MirMethodLoweringInfo* method_infos, size_t count) - REQUIRES(!Locks::mutator_lock_); - - MirMethodLoweringInfo(uint16_t method_idx, InvokeType type, bool is_quickened) - : MirMethodInfo(method_idx, - ((type == kStatic) ? kFlagIsStatic : 0u) | - (static_cast(type) << kBitInvokeTypeBegin) | - (static_cast(type) << kBitSharpTypeBegin) | - (is_quickened ? kFlagQuickened : 0u)), - direct_code_(0u), - direct_method_(0u), - target_dex_file_(nullptr), - target_method_idx_(0u), - vtable_idx_(0u), - stats_flags_(0) { - } - - void SetDevirtualizationTarget(const MethodReference& ref) { - DCHECK(target_dex_file_ == nullptr); - DCHECK_EQ(target_method_idx_, 0u); - DCHECK_LE(ref.dex_method_index, 0xffffu); - target_dex_file_ = ref.dex_file; - target_method_idx_ = ref.dex_method_index; - } - - bool FastPath() const { - return (flags_ & kFlagFastPath) != 0u; - } - - bool IsIntrinsic() const { - return (flags_ & kFlagIsIntrinsic) != 0u; - } - - bool IsSpecial() const { - return (flags_ & kFlagIsSpecial) != 0u; - } - - bool IsReferrersClass() const { - return (flags_ & kFlagIsReferrersClass) != 0; - } - - bool IsClassInitialized() const { - return (flags_ & kFlagClassIsInitialized) != 0u; - } - - // Returns true iff the method invoke is INVOKE_VIRTUAL_QUICK or INVOKE_VIRTUAL_RANGE_QUICK. - bool IsQuickened() const { - return (flags_ & kFlagQuickened) != 0u; - } - - InvokeType GetInvokeType() const { - return static_cast((flags_ >> kBitInvokeTypeBegin) & kInvokeTypeMask); - } - - art::InvokeType GetSharpType() const { - return static_cast((flags_ >> kBitSharpTypeBegin) & kInvokeTypeMask); - } - - MethodReference GetTargetMethod() const { - return MethodReference(target_dex_file_, target_method_idx_); - } - - uint16_t VTableIndex() const { - return vtable_idx_; - } - void SetVTableIndex(uint16_t index) { - vtable_idx_ = index; - } - - uintptr_t DirectCode() const { - return direct_code_; - } - - uintptr_t DirectMethod() const { - return direct_method_; - } - - int StatsFlags() const { - return stats_flags_; - } - - void CheckEquals(const MirMethodLoweringInfo& info) const { - CHECK_EQ(method_idx_, info.method_idx_); - CHECK_EQ(flags_, info.flags_); - CHECK_EQ(declaring_method_idx_, info.declaring_method_idx_); - CHECK_EQ(declaring_class_idx_, info.declaring_class_idx_); - CHECK_EQ(declaring_dex_file_, info.declaring_dex_file_); - CHECK_EQ(direct_code_, info.direct_code_); - CHECK_EQ(direct_method_, info.direct_method_); - CHECK_EQ(target_dex_file_, info.target_dex_file_); - CHECK_EQ(target_method_idx_, info.target_method_idx_); - CHECK_EQ(vtable_idx_, info.vtable_idx_); - CHECK_EQ(stats_flags_, info.stats_flags_); - } - - private: - enum { - kBitFastPath = kMethodInfoBitEnd, - kBitIsIntrinsic, - kBitIsSpecial, - kBitInvokeTypeBegin, - kBitInvokeTypeEnd = kBitInvokeTypeBegin + 3, // 3 bits for invoke type. - kBitSharpTypeBegin = kBitInvokeTypeEnd, - kBitSharpTypeEnd = kBitSharpTypeBegin + 3, // 3 bits for sharp type. - kBitIsReferrersClass = kBitSharpTypeEnd, - kBitClassIsInitialized, - kBitQuickened, - kMethodLoweringInfoBitEnd - }; - static_assert(kMethodLoweringInfoBitEnd <= 16, "Too many flags"); - static constexpr uint16_t kFlagFastPath = 1u << kBitFastPath; - static constexpr uint16_t kFlagIsIntrinsic = 1u << kBitIsIntrinsic; - static constexpr uint16_t kFlagIsSpecial = 1u << kBitIsSpecial; - static constexpr uint16_t kFlagIsReferrersClass = 1u << kBitIsReferrersClass; - static constexpr uint16_t kFlagClassIsInitialized = 1u << kBitClassIsInitialized; - static constexpr uint16_t kFlagQuickened = 1u << kBitQuickened; - static constexpr uint16_t kInvokeTypeMask = 7u; - static_assert((1u << (kBitInvokeTypeEnd - kBitInvokeTypeBegin)) - 1u == kInvokeTypeMask, - "assert invoke type bits failed"); - static_assert((1u << (kBitSharpTypeEnd - kBitSharpTypeBegin)) - 1u == kInvokeTypeMask, - "assert sharp type bits failed"); - - uintptr_t direct_code_; - uintptr_t direct_method_; - // Before Resolve(), target_dex_file_ and target_method_idx_ hold the verification-based - // devirtualized invoke target if available, null and 0u otherwise. - // After Resolve() they hold the actual target method that will be called; it will be either - // a devirtualized target method or the compilation's unit's dex file and MethodIndex(). - const DexFile* target_dex_file_; - uint16_t target_method_idx_; - uint16_t vtable_idx_; - int stats_flags_; - - friend class MirOptimizationTest; - friend class TypeInferenceTest; -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_MIR_METHOD_INFO_H_ diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc deleted file mode 100644 index 0e74a48aa1..0000000000 --- a/compiler/dex/mir_optimization.cc +++ /dev/null @@ -1,1997 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "base/bit_vector-inl.h" -#include "base/logging.h" -#include "base/scoped_arena_containers.h" -#include "class_linker-inl.h" -#include "dataflow_iterator-inl.h" -#include "dex/verified_method.h" -#include "dex_flags.h" -#include "driver/compiler_driver.h" -#include "driver/dex_compilation_unit.h" -#include "global_value_numbering.h" -#include "gvn_dead_code_elimination.h" -#include "local_value_numbering.h" -#include "mir_field_info.h" -#include "mirror/string.h" -#include "quick/dex_file_method_inliner.h" -#include "quick/dex_file_to_method_inliner_map.h" -#include "stack.h" -#include "thread-inl.h" -#include "type_inference.h" -#include "utils.h" - -namespace art { - -static unsigned int Predecessors(BasicBlock* bb) { - return bb->predecessors.size(); -} - -/* Setup a constant value for opcodes thare have the DF_SETS_CONST attribute */ -void MIRGraph::SetConstant(int32_t ssa_reg, int32_t value) { - is_constant_v_->SetBit(ssa_reg); - constant_values_[ssa_reg] = value; - reg_location_[ssa_reg].is_const = true; -} - -void MIRGraph::SetConstantWide(int32_t ssa_reg, int64_t value) { - is_constant_v_->SetBit(ssa_reg); - is_constant_v_->SetBit(ssa_reg + 1); - constant_values_[ssa_reg] = Low32Bits(value); - constant_values_[ssa_reg + 1] = High32Bits(value); - reg_location_[ssa_reg].is_const = true; - reg_location_[ssa_reg + 1].is_const = true; -} - -void MIRGraph::DoConstantPropagation(BasicBlock* bb) { - MIR* mir; - - for (mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - // Skip pass if BB has MIR without SSA representation. - if (mir->ssa_rep == nullptr) { - return; - } - - uint64_t df_attributes = GetDataFlowAttributes(mir); - - MIR::DecodedInstruction* d_insn = &mir->dalvikInsn; - - if (!(df_attributes & DF_HAS_DEFS)) continue; - - /* Handle instructions that set up constants directly */ - if (df_attributes & DF_SETS_CONST) { - if (df_attributes & DF_DA) { - int32_t vB = static_cast(d_insn->vB); - switch (d_insn->opcode) { - case Instruction::CONST_4: - case Instruction::CONST_16: - case Instruction::CONST: - SetConstant(mir->ssa_rep->defs[0], vB); - break; - case Instruction::CONST_HIGH16: - SetConstant(mir->ssa_rep->defs[0], vB << 16); - break; - case Instruction::CONST_WIDE_16: - case Instruction::CONST_WIDE_32: - SetConstantWide(mir->ssa_rep->defs[0], static_cast(vB)); - break; - case Instruction::CONST_WIDE: - SetConstantWide(mir->ssa_rep->defs[0], d_insn->vB_wide); - break; - case Instruction::CONST_WIDE_HIGH16: - SetConstantWide(mir->ssa_rep->defs[0], static_cast(vB) << 48); - break; - default: - break; - } - } - /* Handle instructions that set up constants directly */ - } else if (df_attributes & DF_IS_MOVE) { - int i; - - for (i = 0; i < mir->ssa_rep->num_uses; i++) { - if (!is_constant_v_->IsBitSet(mir->ssa_rep->uses[i])) break; - } - /* Move a register holding a constant to another register */ - if (i == mir->ssa_rep->num_uses) { - SetConstant(mir->ssa_rep->defs[0], constant_values_[mir->ssa_rep->uses[0]]); - if (df_attributes & DF_A_WIDE) { - SetConstant(mir->ssa_rep->defs[1], constant_values_[mir->ssa_rep->uses[1]]); - } - } - } - } - /* TODO: implement code to handle arithmetic operations */ -} - -/* Advance to next strictly dominated MIR node in an extended basic block */ -MIR* MIRGraph::AdvanceMIR(BasicBlock** p_bb, MIR* mir) { - BasicBlock* bb = *p_bb; - if (mir != nullptr) { - mir = mir->next; - while (mir == nullptr) { - bb = GetBasicBlock(bb->fall_through); - if ((bb == nullptr) || Predecessors(bb) != 1) { - // mir is null and we cannot proceed further. - break; - } else { - *p_bb = bb; - mir = bb->first_mir_insn; - } - } - } - return mir; -} - -/* - * To be used at an invoke mir. If the logically next mir node represents - * a move-result, return it. Else, return nullptr. If a move-result exists, - * it is required to immediately follow the invoke with no intervening - * opcodes or incoming arcs. However, if the result of the invoke is not - * used, a move-result may not be present. - */ -MIR* MIRGraph::FindMoveResult(BasicBlock* bb, MIR* mir) { - BasicBlock* tbb = bb; - mir = AdvanceMIR(&tbb, mir); - while (mir != nullptr) { - if ((mir->dalvikInsn.opcode == Instruction::MOVE_RESULT) || - (mir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) || - (mir->dalvikInsn.opcode == Instruction::MOVE_RESULT_WIDE)) { - break; - } - // Keep going if pseudo op, otherwise terminate - if (MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode)) { - mir = AdvanceMIR(&tbb, mir); - } else { - mir = nullptr; - } - } - return mir; -} - -BasicBlock* MIRGraph::NextDominatedBlock(BasicBlock* bb) { - if (bb->block_type == kDead) { - return nullptr; - } - DCHECK((bb->block_type == kEntryBlock) || (bb->block_type == kDalvikByteCode) - || (bb->block_type == kExitBlock)); - BasicBlock* bb_taken = GetBasicBlock(bb->taken); - BasicBlock* bb_fall_through = GetBasicBlock(bb->fall_through); - if (((bb_fall_through == nullptr) && (bb_taken != nullptr)) && - ((bb_taken->block_type == kDalvikByteCode) || (bb_taken->block_type == kExitBlock))) { - // Follow simple unconditional branches. - bb = bb_taken; - } else { - // Follow simple fallthrough - bb = (bb_taken != nullptr) ? nullptr : bb_fall_through; - } - if (bb == nullptr || (Predecessors(bb) != 1)) { - return nullptr; - } - DCHECK((bb->block_type == kDalvikByteCode) || (bb->block_type == kExitBlock)); - return bb; -} - -static MIR* FindPhi(BasicBlock* bb, int ssa_name) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - if (static_cast(mir->dalvikInsn.opcode) == kMirOpPhi) { - for (int i = 0; i < mir->ssa_rep->num_uses; i++) { - if (mir->ssa_rep->uses[i] == ssa_name) { - return mir; - } - } - } - } - return nullptr; -} - -static SelectInstructionKind SelectKind(MIR* mir) { - // Work with the case when mir is null. - if (mir == nullptr) { - return kSelectNone; - } - switch (mir->dalvikInsn.opcode) { - case Instruction::MOVE: - case Instruction::MOVE_OBJECT: - case Instruction::MOVE_16: - case Instruction::MOVE_OBJECT_16: - case Instruction::MOVE_FROM16: - case Instruction::MOVE_OBJECT_FROM16: - return kSelectMove; - case Instruction::CONST: - case Instruction::CONST_4: - case Instruction::CONST_16: - return kSelectConst; - case Instruction::GOTO: - case Instruction::GOTO_16: - case Instruction::GOTO_32: - return kSelectGoto; - default: - return kSelectNone; - } -} - -static constexpr ConditionCode kIfCcZConditionCodes[] = { - kCondEq, kCondNe, kCondLt, kCondGe, kCondGt, kCondLe -}; - -static_assert(arraysize(kIfCcZConditionCodes) == Instruction::IF_LEZ - Instruction::IF_EQZ + 1, - "if_ccz_ccodes_size1"); - -static constexpr ConditionCode ConditionCodeForIfCcZ(Instruction::Code opcode) { - return kIfCcZConditionCodes[opcode - Instruction::IF_EQZ]; -} - -static_assert(ConditionCodeForIfCcZ(Instruction::IF_EQZ) == kCondEq, "if_eqz ccode"); -static_assert(ConditionCodeForIfCcZ(Instruction::IF_NEZ) == kCondNe, "if_nez ccode"); -static_assert(ConditionCodeForIfCcZ(Instruction::IF_LTZ) == kCondLt, "if_ltz ccode"); -static_assert(ConditionCodeForIfCcZ(Instruction::IF_GEZ) == kCondGe, "if_gez ccode"); -static_assert(ConditionCodeForIfCcZ(Instruction::IF_GTZ) == kCondGt, "if_gtz ccode"); -static_assert(ConditionCodeForIfCcZ(Instruction::IF_LEZ) == kCondLe, "if_lez ccode"); - -int MIRGraph::GetSSAUseCount(int s_reg) { - DCHECK_LT(static_cast(s_reg), ssa_subscripts_.size()); - return raw_use_counts_[s_reg]; -} - -size_t MIRGraph::GetNumBytesForSpecialTemps() const { - // This logic is written with assumption that Method* is only special temp. - DCHECK_EQ(max_available_special_compiler_temps_, 1u); - return InstructionSetPointerSize(cu_->instruction_set); -} - -size_t MIRGraph::GetNumAvailableVRTemps() { - // First take into account all temps reserved for backend. - if (max_available_non_special_compiler_temps_ < reserved_temps_for_backend_) { - return 0; - } - - // Calculate remaining ME temps available. - size_t remaining_me_temps = max_available_non_special_compiler_temps_ - - reserved_temps_for_backend_; - - if (num_non_special_compiler_temps_ >= remaining_me_temps) { - return 0; - } else { - return remaining_me_temps - num_non_special_compiler_temps_; - } -} - -// FIXME - will probably need to revisit all uses of this, as type not defined. -static const RegLocation temp_loc = {kLocCompilerTemp, - 0, 1 /*defined*/, 0, 0, 0, 0, 0, 1 /*home*/, - RegStorage(), INVALID_SREG, INVALID_SREG}; - -CompilerTemp* MIRGraph::GetNewCompilerTemp(CompilerTempType ct_type, bool wide) { - // Once the compiler temps have been committed, new ones cannot be requested anymore. - DCHECK_EQ(compiler_temps_committed_, false); - // Make sure that reserved for BE set is sane. - DCHECK_LE(reserved_temps_for_backend_, max_available_non_special_compiler_temps_); - - bool verbose = cu_->verbose; - const char* ct_type_str = nullptr; - - if (verbose) { - switch (ct_type) { - case kCompilerTempBackend: - ct_type_str = "backend"; - break; - case kCompilerTempSpecialMethodPtr: - ct_type_str = "method*"; - break; - case kCompilerTempVR: - ct_type_str = "VR"; - break; - default: - ct_type_str = "unknown"; - break; - } - LOG(INFO) << "CompilerTemps: A compiler temp of type " << ct_type_str << " that is " - << (wide ? "wide is being requested." : "not wide is being requested."); - } - - CompilerTemp *compiler_temp = static_cast(arena_->Alloc(sizeof(CompilerTemp), - kArenaAllocRegAlloc)); - - // Create the type of temp requested. Special temps need special handling because - // they have a specific virtual register assignment. - if (ct_type == kCompilerTempSpecialMethodPtr) { - // This has a special location on stack which is 32-bit or 64-bit depending - // on mode. However, we don't want to overlap with non-special section - // and thus even for 64-bit, we allow only a non-wide temp to be requested. - DCHECK_EQ(wide, false); - - // The vreg is always the first special temp for method ptr. - compiler_temp->v_reg = GetFirstSpecialTempVR(); - - CHECK(reg_location_ == nullptr); - } else if (ct_type == kCompilerTempBackend) { - requested_backend_temp_ = true; - - // Make sure that we are not exceeding temps reserved for BE. - // Since VR temps cannot be requested once the BE temps are requested, we - // allow reservation of VR temps as well for BE. We - size_t available_temps = reserved_temps_for_backend_ + GetNumAvailableVRTemps(); - size_t needed_temps = wide ? 2u : 1u; - if (available_temps < needed_temps) { - if (verbose) { - LOG(INFO) << "CompilerTemps: Not enough temp(s) of type " << ct_type_str - << " are available."; - } - return nullptr; - } - - // Update the remaining reserved temps since we have now used them. - // Note that the code below is actually subtracting to remove them from reserve - // once they have been claimed. It is careful to not go below zero. - reserved_temps_for_backend_ = - std::max(reserved_temps_for_backend_, needed_temps) - needed_temps; - - // The new non-special compiler temp must receive a unique v_reg. - compiler_temp->v_reg = GetFirstNonSpecialTempVR() + num_non_special_compiler_temps_; - num_non_special_compiler_temps_++; - } else if (ct_type == kCompilerTempVR) { - // Once we start giving out BE temps, we don't allow anymore ME temps to be requested. - // This is done in order to prevent problems with ssa since these structures are allocated - // and managed by the ME. - DCHECK_EQ(requested_backend_temp_, false); - - // There is a limit to the number of non-special temps so check to make sure it wasn't exceeded. - size_t available_temps = GetNumAvailableVRTemps(); - if (available_temps <= 0 || (available_temps <= 1 && wide)) { - if (verbose) { - LOG(INFO) << "CompilerTemps: Not enough temp(s) of type " << ct_type_str - << " are available."; - } - return nullptr; - } - - // The new non-special compiler temp must receive a unique v_reg. - compiler_temp->v_reg = GetFirstNonSpecialTempVR() + num_non_special_compiler_temps_; - num_non_special_compiler_temps_++; - } else { - UNIMPLEMENTED(FATAL) << "No handling for compiler temp type " << ct_type_str << "."; - } - - // We allocate an sreg as well to make developer life easier. - // However, if this is requested from an ME pass that will recalculate ssa afterwards, - // this sreg is no longer valid. The caller should be aware of this. - compiler_temp->s_reg_low = AddNewSReg(compiler_temp->v_reg); - - if (verbose) { - LOG(INFO) << "CompilerTemps: New temp of type " << ct_type_str << " with v" - << compiler_temp->v_reg << " and s" << compiler_temp->s_reg_low << " has been created."; - } - - if (wide) { - // Only non-special temps are handled as wide for now. - // Note that the number of non special temps is incremented below. - DCHECK(ct_type == kCompilerTempBackend || ct_type == kCompilerTempVR); - - // Ensure that the two registers are consecutive. - int ssa_reg_low = compiler_temp->s_reg_low; - int ssa_reg_high = AddNewSReg(compiler_temp->v_reg + 1); - num_non_special_compiler_temps_++; - - if (verbose) { - LOG(INFO) << "CompilerTemps: The wide part of temp of type " << ct_type_str << " is v" - << compiler_temp->v_reg + 1 << " and s" << ssa_reg_high << "."; - } - - if (reg_location_ != nullptr) { - reg_location_[ssa_reg_high] = temp_loc; - reg_location_[ssa_reg_high].high_word = true; - reg_location_[ssa_reg_high].s_reg_low = ssa_reg_low; - reg_location_[ssa_reg_high].wide = true; - } - } - - // If the register locations have already been allocated, add the information - // about the temp. We will not overflow because they have been initialized - // to support the maximum number of temps. For ME temps that have multiple - // ssa versions, the structures below will be expanded on the post pass cleanup. - if (reg_location_ != nullptr) { - int ssa_reg_low = compiler_temp->s_reg_low; - reg_location_[ssa_reg_low] = temp_loc; - reg_location_[ssa_reg_low].s_reg_low = ssa_reg_low; - reg_location_[ssa_reg_low].wide = wide; - } - - return compiler_temp; -} - -void MIRGraph::RemoveLastCompilerTemp(CompilerTempType ct_type, bool wide, CompilerTemp* temp) { - // Once the compiler temps have been committed, it's too late for any modifications. - DCHECK_EQ(compiler_temps_committed_, false); - - size_t used_temps = wide ? 2u : 1u; - - if (ct_type == kCompilerTempBackend) { - DCHECK(requested_backend_temp_); - - // Make the temps available to backend again. - reserved_temps_for_backend_ += used_temps; - } else if (ct_type == kCompilerTempVR) { - DCHECK(!requested_backend_temp_); - } else { - UNIMPLEMENTED(FATAL) << "No handling for compiler temp type " << static_cast(ct_type); - } - - // Reduce the number of non-special compiler temps. - DCHECK_LE(used_temps, num_non_special_compiler_temps_); - num_non_special_compiler_temps_ -= used_temps; - - // Check that this was really the last temp. - DCHECK_EQ(static_cast(temp->v_reg), - GetFirstNonSpecialTempVR() + num_non_special_compiler_temps_); - - if (cu_->verbose) { - LOG(INFO) << "Last temporary has been removed."; - } -} - -static bool EvaluateBranch(Instruction::Code opcode, int32_t src1, int32_t src2) { - bool is_taken; - switch (opcode) { - case Instruction::IF_EQ: is_taken = (src1 == src2); break; - case Instruction::IF_NE: is_taken = (src1 != src2); break; - case Instruction::IF_LT: is_taken = (src1 < src2); break; - case Instruction::IF_GE: is_taken = (src1 >= src2); break; - case Instruction::IF_GT: is_taken = (src1 > src2); break; - case Instruction::IF_LE: is_taken = (src1 <= src2); break; - case Instruction::IF_EQZ: is_taken = (src1 == 0); break; - case Instruction::IF_NEZ: is_taken = (src1 != 0); break; - case Instruction::IF_LTZ: is_taken = (src1 < 0); break; - case Instruction::IF_GEZ: is_taken = (src1 >= 0); break; - case Instruction::IF_GTZ: is_taken = (src1 > 0); break; - case Instruction::IF_LEZ: is_taken = (src1 <= 0); break; - default: - LOG(FATAL) << "Unexpected opcode " << opcode; - UNREACHABLE(); - } - return is_taken; -} - -/* Do some MIR-level extended basic block optimizations */ -bool MIRGraph::BasicBlockOpt(BasicBlock* bb) { - if (bb->block_type == kDead) { - return true; - } - // Currently multiply-accumulate backend supports are only available on arm32 and arm64. - if (cu_->instruction_set == kArm64 || cu_->instruction_set == kThumb2) { - MultiplyAddOpt(bb); - } - bool use_lvn = bb->use_lvn && (cu_->disable_opt & (1u << kLocalValueNumbering)) == 0u; - std::unique_ptr allocator; - std::unique_ptr global_valnum; - std::unique_ptr local_valnum; - if (use_lvn) { - allocator.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - global_valnum.reset(new (allocator.get()) GlobalValueNumbering(cu_, allocator.get(), - GlobalValueNumbering::kModeLvn)); - local_valnum.reset(new (allocator.get()) LocalValueNumbering(global_valnum.get(), bb->id, - allocator.get())); - } - while (bb != nullptr) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - // TUNING: use the returned value number for CSE. - if (use_lvn) { - local_valnum->GetValueNumber(mir); - } - // Look for interesting opcodes, skip otherwise - Instruction::Code opcode = mir->dalvikInsn.opcode; - switch (opcode) { - case Instruction::IF_EQ: - case Instruction::IF_NE: - case Instruction::IF_LT: - case Instruction::IF_GE: - case Instruction::IF_GT: - case Instruction::IF_LE: - if (!IsConst(mir->ssa_rep->uses[1])) { - break; - } - FALLTHROUGH_INTENDED; - case Instruction::IF_EQZ: - case Instruction::IF_NEZ: - case Instruction::IF_LTZ: - case Instruction::IF_GEZ: - case Instruction::IF_GTZ: - case Instruction::IF_LEZ: - // Result known at compile time? - if (IsConst(mir->ssa_rep->uses[0])) { - int32_t rhs = (mir->ssa_rep->num_uses == 2) ? ConstantValue(mir->ssa_rep->uses[1]) : 0; - bool is_taken = EvaluateBranch(opcode, ConstantValue(mir->ssa_rep->uses[0]), rhs); - BasicBlockId edge_to_kill = is_taken ? bb->fall_through : bb->taken; - if (is_taken) { - // Replace with GOTO. - bb->fall_through = NullBasicBlockId; - mir->dalvikInsn.opcode = Instruction::GOTO; - mir->dalvikInsn.vA = - IsInstructionIfCc(opcode) ? mir->dalvikInsn.vC : mir->dalvikInsn.vB; - } else { - // Make NOP. - bb->taken = NullBasicBlockId; - mir->dalvikInsn.opcode = static_cast(kMirOpNop); - } - mir->ssa_rep->num_uses = 0; - BasicBlock* successor_to_unlink = GetBasicBlock(edge_to_kill); - successor_to_unlink->ErasePredecessor(bb->id); - // We have changed the graph structure. - dfs_orders_up_to_date_ = false; - domination_up_to_date_ = false; - topological_order_up_to_date_ = false; - // Keep MIR SSA rep, the worst that can happen is a Phi with just 1 input. - } - break; - case Instruction::CMPL_FLOAT: - case Instruction::CMPL_DOUBLE: - case Instruction::CMPG_FLOAT: - case Instruction::CMPG_DOUBLE: - case Instruction::CMP_LONG: - if ((cu_->disable_opt & (1 << kBranchFusing)) != 0) { - // Bitcode doesn't allow this optimization. - break; - } - if (mir->next != nullptr) { - MIR* mir_next = mir->next; - // Make sure result of cmp is used by next insn and nowhere else - if (IsInstructionIfCcZ(mir_next->dalvikInsn.opcode) && - (mir->ssa_rep->defs[0] == mir_next->ssa_rep->uses[0]) && - (GetSSAUseCount(mir->ssa_rep->defs[0]) == 1)) { - mir_next->meta.ccode = ConditionCodeForIfCcZ(mir_next->dalvikInsn.opcode); - switch (opcode) { - case Instruction::CMPL_FLOAT: - mir_next->dalvikInsn.opcode = - static_cast(kMirOpFusedCmplFloat); - break; - case Instruction::CMPL_DOUBLE: - mir_next->dalvikInsn.opcode = - static_cast(kMirOpFusedCmplDouble); - break; - case Instruction::CMPG_FLOAT: - mir_next->dalvikInsn.opcode = - static_cast(kMirOpFusedCmpgFloat); - break; - case Instruction::CMPG_DOUBLE: - mir_next->dalvikInsn.opcode = - static_cast(kMirOpFusedCmpgDouble); - break; - case Instruction::CMP_LONG: - mir_next->dalvikInsn.opcode = - static_cast(kMirOpFusedCmpLong); - break; - default: LOG(ERROR) << "Unexpected opcode: " << opcode; - } - mir->dalvikInsn.opcode = static_cast(kMirOpNop); - // Clear use count of temp VR. - use_counts_[mir->ssa_rep->defs[0]] = 0; - raw_use_counts_[mir->ssa_rep->defs[0]] = 0; - // Copy the SSA information that is relevant. - mir_next->ssa_rep->num_uses = mir->ssa_rep->num_uses; - mir_next->ssa_rep->uses = mir->ssa_rep->uses; - mir_next->ssa_rep->num_defs = 0; - mir->ssa_rep->num_uses = 0; - mir->ssa_rep->num_defs = 0; - // Copy in the decoded instruction information for potential SSA re-creation. - mir_next->dalvikInsn.vA = mir->dalvikInsn.vB; - mir_next->dalvikInsn.vB = mir->dalvikInsn.vC; - } - } - break; - default: - break; - } - // Is this the select pattern? - // TODO: flesh out support for Mips. NOTE: llvm's select op doesn't quite work here. - // TUNING: expand to support IF_xx compare & branches - if ((cu_->instruction_set == kArm64 || cu_->instruction_set == kThumb2 || - cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) && - IsInstructionIfCcZ(mir->dalvikInsn.opcode)) { - BasicBlock* ft = GetBasicBlock(bb->fall_through); - DCHECK(ft != nullptr); - BasicBlock* ft_ft = GetBasicBlock(ft->fall_through); - BasicBlock* ft_tk = GetBasicBlock(ft->taken); - - BasicBlock* tk = GetBasicBlock(bb->taken); - DCHECK(tk != nullptr); - BasicBlock* tk_ft = GetBasicBlock(tk->fall_through); - BasicBlock* tk_tk = GetBasicBlock(tk->taken); - - /* - * In the select pattern, the taken edge goes to a block that unconditionally - * transfers to the rejoin block and the fall_though edge goes to a block that - * unconditionally falls through to the rejoin block. - */ - if ((tk_ft == nullptr) && (ft_tk == nullptr) && (tk_tk == ft_ft) && - (Predecessors(tk) == 1) && (Predecessors(ft) == 1)) { - /* - * Okay - we have the basic diamond shape. - */ - - // TODO: Add logic for LONG. - // Are the block bodies something we can handle? - if ((ft->first_mir_insn == ft->last_mir_insn) && - (tk->first_mir_insn != tk->last_mir_insn) && - (tk->first_mir_insn->next == tk->last_mir_insn) && - ((SelectKind(ft->first_mir_insn) == kSelectMove) || - (SelectKind(ft->first_mir_insn) == kSelectConst)) && - (SelectKind(ft->first_mir_insn) == SelectKind(tk->first_mir_insn)) && - (SelectKind(tk->last_mir_insn) == kSelectGoto)) { - // Almost there. Are the instructions targeting the same vreg? - MIR* if_true = tk->first_mir_insn; - MIR* if_false = ft->first_mir_insn; - // It's possible that the target of the select isn't used - skip those (rare) cases. - MIR* phi = FindPhi(tk_tk, if_true->ssa_rep->defs[0]); - if ((phi != nullptr) && (if_true->dalvikInsn.vA == if_false->dalvikInsn.vA)) { - /* - * We'll convert the IF_EQZ/IF_NEZ to a SELECT. We need to find the - * Phi node in the merge block and delete it (while using the SSA name - * of the merge as the target of the SELECT. Delete both taken and - * fallthrough blocks, and set fallthrough to merge block. - * NOTE: not updating other dataflow info (no longer used at this point). - * If this changes, need to update i_dom, etc. here (and in CombineBlocks). - */ - mir->meta.ccode = ConditionCodeForIfCcZ(mir->dalvikInsn.opcode); - mir->dalvikInsn.opcode = static_cast(kMirOpSelect); - bool const_form = (SelectKind(if_true) == kSelectConst); - if ((SelectKind(if_true) == kSelectMove)) { - if (IsConst(if_true->ssa_rep->uses[0]) && - IsConst(if_false->ssa_rep->uses[0])) { - const_form = true; - if_true->dalvikInsn.vB = ConstantValue(if_true->ssa_rep->uses[0]); - if_false->dalvikInsn.vB = ConstantValue(if_false->ssa_rep->uses[0]); - } - } - if (const_form) { - /* - * TODO: If both constants are the same value, then instead of generating - * a select, we should simply generate a const bytecode. This should be - * considered after inlining which can lead to CFG of this form. - */ - // "true" set val in vB - mir->dalvikInsn.vB = if_true->dalvikInsn.vB; - // "false" set val in vC - mir->dalvikInsn.vC = if_false->dalvikInsn.vB; - } else { - DCHECK_EQ(SelectKind(if_true), kSelectMove); - DCHECK_EQ(SelectKind(if_false), kSelectMove); - int32_t* src_ssa = arena_->AllocArray(3, kArenaAllocDFInfo); - src_ssa[0] = mir->ssa_rep->uses[0]; - src_ssa[1] = if_true->ssa_rep->uses[0]; - src_ssa[2] = if_false->ssa_rep->uses[0]; - mir->ssa_rep->uses = src_ssa; - mir->ssa_rep->num_uses = 3; - } - AllocateSSADefData(mir, 1); - /* - * There is usually a Phi node in the join block for our two cases. If the - * Phi node only contains our two cases as input, we will use the result - * SSA name of the Phi node as our select result and delete the Phi. If - * the Phi node has more than two operands, we will arbitrarily use the SSA - * name of the "false" path, delete the SSA name of the "true" path from the - * Phi node (and fix up the incoming arc list). - */ - if (phi->ssa_rep->num_uses == 2) { - mir->ssa_rep->defs[0] = phi->ssa_rep->defs[0]; - // Rather than changing the Phi to kMirOpNop, remove it completely. - // This avoids leaving other Phis after kMirOpNop (i.e. a non-Phi) insn. - tk_tk->RemoveMIR(phi); - int dead_false_def = if_false->ssa_rep->defs[0]; - raw_use_counts_[dead_false_def] = use_counts_[dead_false_def] = 0; - } else { - int live_def = if_false->ssa_rep->defs[0]; - mir->ssa_rep->defs[0] = live_def; - } - int dead_true_def = if_true->ssa_rep->defs[0]; - raw_use_counts_[dead_true_def] = use_counts_[dead_true_def] = 0; - // Update ending vreg->sreg map for GC maps generation. - int def_vreg = SRegToVReg(mir->ssa_rep->defs[0]); - bb->data_flow_info->vreg_to_ssa_map_exit[def_vreg] = mir->ssa_rep->defs[0]; - // We want to remove ft and tk and link bb directly to ft_ft. First, we need - // to update all Phi inputs correctly with UpdatePredecessor(ft->id, bb->id) - // since the live_def above comes from ft->first_mir_insn (if_false). - DCHECK(if_false == ft->first_mir_insn); - ft_ft->UpdatePredecessor(ft->id, bb->id); - // Correct the rest of the links between bb, ft and ft_ft. - ft->ErasePredecessor(bb->id); - ft->fall_through = NullBasicBlockId; - bb->fall_through = ft_ft->id; - // Now we can kill tk and ft. - tk->Kill(this); - ft->Kill(this); - // NOTE: DFS order, domination info and topological order are still usable - // despite the newly dead blocks. - } - } - } - } - } - bb = ((cu_->disable_opt & (1 << kSuppressExceptionEdges)) != 0) ? NextDominatedBlock(bb) : - nullptr; - } - if (use_lvn && UNLIKELY(!global_valnum->Good())) { - LOG(WARNING) << "LVN overflow in " << PrettyMethod(cu_->method_idx, *cu_->dex_file); - } - - return true; -} - -/* Collect stats on number of checks removed */ -void MIRGraph::CountChecks(class BasicBlock* bb) { - if (bb->data_flow_info != nullptr) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - if (mir->ssa_rep == nullptr) { - continue; - } - uint64_t df_attributes = GetDataFlowAttributes(mir); - if (df_attributes & DF_HAS_NULL_CHKS) { - checkstats_->null_checks++; - if (mir->optimization_flags & MIR_IGNORE_NULL_CHECK) { - checkstats_->null_checks_eliminated++; - } - } - if (df_attributes & DF_HAS_RANGE_CHKS) { - checkstats_->range_checks++; - if (mir->optimization_flags & MIR_IGNORE_RANGE_CHECK) { - checkstats_->range_checks_eliminated++; - } - } - } - } -} - -/* Try to make common case the fallthrough path. */ -bool MIRGraph::LayoutBlocks(BasicBlock* bb) { - // TODO: For now, just looking for direct throws. Consider generalizing for profile feedback. - if (!bb->explicit_throw) { - return false; - } - - // If we visited it, we are done. - if (bb->visited) { - return false; - } - bb->visited = true; - - BasicBlock* walker = bb; - while (true) { - // Check termination conditions. - if ((walker->block_type == kEntryBlock) || (Predecessors(walker) != 1)) { - break; - } - DCHECK(!walker->predecessors.empty()); - BasicBlock* prev = GetBasicBlock(walker->predecessors[0]); - - // If we visited the predecessor, we are done. - if (prev->visited) { - return false; - } - prev->visited = true; - - if (prev->conditional_branch) { - if (GetBasicBlock(prev->fall_through) == walker) { - // Already done - return. - break; - } - DCHECK_EQ(walker, GetBasicBlock(prev->taken)); - // Got one. Flip it and exit. - Instruction::Code opcode = prev->last_mir_insn->dalvikInsn.opcode; - switch (opcode) { - case Instruction::IF_EQ: opcode = Instruction::IF_NE; break; - case Instruction::IF_NE: opcode = Instruction::IF_EQ; break; - case Instruction::IF_LT: opcode = Instruction::IF_GE; break; - case Instruction::IF_GE: opcode = Instruction::IF_LT; break; - case Instruction::IF_GT: opcode = Instruction::IF_LE; break; - case Instruction::IF_LE: opcode = Instruction::IF_GT; break; - case Instruction::IF_EQZ: opcode = Instruction::IF_NEZ; break; - case Instruction::IF_NEZ: opcode = Instruction::IF_EQZ; break; - case Instruction::IF_LTZ: opcode = Instruction::IF_GEZ; break; - case Instruction::IF_GEZ: opcode = Instruction::IF_LTZ; break; - case Instruction::IF_GTZ: opcode = Instruction::IF_LEZ; break; - case Instruction::IF_LEZ: opcode = Instruction::IF_GTZ; break; - default: LOG(FATAL) << "Unexpected opcode " << opcode; - } - prev->last_mir_insn->dalvikInsn.opcode = opcode; - BasicBlockId t_bb = prev->taken; - prev->taken = prev->fall_through; - prev->fall_through = t_bb; - break; - } - walker = prev; - } - return false; -} - -/* Combine any basic blocks terminated by instructions that we now know can't throw */ -void MIRGraph::CombineBlocks(class BasicBlock* bb) { - // Loop here to allow combining a sequence of blocks - while ((bb->block_type == kDalvikByteCode) && - (bb->last_mir_insn != nullptr) && - (static_cast(bb->last_mir_insn->dalvikInsn.opcode) == kMirOpCheck)) { - MIR* mir = bb->last_mir_insn; - DCHECK(bb->first_mir_insn != nullptr); - - // Get the paired insn and check if it can still throw. - MIR* throw_insn = mir->meta.throw_insn; - if (CanThrow(throw_insn)) { - break; - } - - // OK - got one. Combine - BasicBlock* bb_next = GetBasicBlock(bb->fall_through); - DCHECK(!bb_next->catch_entry); - DCHECK_EQ(bb_next->predecessors.size(), 1u); - - // Now move instructions from bb_next to bb. Start off with doing a sanity check - // that kMirOpCheck's throw instruction is first one in the bb_next. - DCHECK_EQ(bb_next->first_mir_insn, throw_insn); - // Now move all instructions (throw instruction to last one) from bb_next to bb. - MIR* last_to_move = bb_next->last_mir_insn; - bb_next->RemoveMIRList(throw_insn, last_to_move); - bb->InsertMIRListAfter(bb->last_mir_insn, throw_insn, last_to_move); - // The kMirOpCheck instruction is not needed anymore. - mir->dalvikInsn.opcode = static_cast(kMirOpNop); - bb->RemoveMIR(mir); - - // Before we overwrite successors, remove their predecessor links to bb. - bb_next->ErasePredecessor(bb->id); - if (bb->taken != NullBasicBlockId) { - DCHECK_EQ(bb->successor_block_list_type, kNotUsed); - BasicBlock* bb_taken = GetBasicBlock(bb->taken); - // bb->taken will be overwritten below. - DCHECK_EQ(bb_taken->block_type, kExceptionHandling); - DCHECK_EQ(bb_taken->predecessors.size(), 1u); - DCHECK_EQ(bb_taken->predecessors[0], bb->id); - bb_taken->predecessors.clear(); - bb_taken->block_type = kDead; - DCHECK(bb_taken->data_flow_info == nullptr); - } else { - DCHECK_EQ(bb->successor_block_list_type, kCatch); - for (SuccessorBlockInfo* succ_info : bb->successor_blocks) { - if (succ_info->block != NullBasicBlockId) { - BasicBlock* succ_bb = GetBasicBlock(succ_info->block); - DCHECK(succ_bb->catch_entry); - succ_bb->ErasePredecessor(bb->id); - } - } - } - // Use the successor info from the next block - bb->successor_block_list_type = bb_next->successor_block_list_type; - bb->successor_blocks.swap(bb_next->successor_blocks); // Swap instead of copying. - bb_next->successor_block_list_type = kNotUsed; - // Use the ending block linkage from the next block - bb->fall_through = bb_next->fall_through; - bb_next->fall_through = NullBasicBlockId; - bb->taken = bb_next->taken; - bb_next->taken = NullBasicBlockId; - /* - * If lower-half of pair of blocks to combine contained - * a return or a conditional branch or an explicit throw, - * move the flag to the newly combined block. - */ - bb->terminated_by_return = bb_next->terminated_by_return; - bb->conditional_branch = bb_next->conditional_branch; - bb->explicit_throw = bb_next->explicit_throw; - // Merge the use_lvn flag. - bb->use_lvn |= bb_next->use_lvn; - - // Kill the unused block. - bb_next->data_flow_info = nullptr; - - /* - * NOTE: we aren't updating all dataflow info here. Should either make sure this pass - * happens after uses of i_dominated, dom_frontier or update the dataflow info here. - * NOTE: GVN uses bb->data_flow_info->live_in_v which is unaffected by the block merge. - */ - - // Kill bb_next and remap now-dead id to parent. - bb_next->block_type = kDead; - bb_next->data_flow_info = nullptr; // Must be null for dead blocks. (Relied on by the GVN.) - block_id_map_.Overwrite(bb_next->id, bb->id); - // Update predecessors in children. - ChildBlockIterator iter(bb, this); - for (BasicBlock* child = iter.Next(); child != nullptr; child = iter.Next()) { - child->UpdatePredecessor(bb_next->id, bb->id); - } - - // DFS orders, domination and topological order are not up to date anymore. - dfs_orders_up_to_date_ = false; - domination_up_to_date_ = false; - topological_order_up_to_date_ = false; - - // Now, loop back and see if we can keep going - } -} - -bool MIRGraph::EliminateNullChecksGate() { - if ((cu_->disable_opt & (1 << kNullCheckElimination)) != 0 || - (merged_df_flags_ & DF_HAS_NULL_CHKS) == 0) { - return false; - } - - DCHECK(temp_scoped_alloc_.get() == nullptr); - temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - temp_.nce.num_vregs = GetNumOfCodeAndTempVRs(); - temp_.nce.work_vregs_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_.nce.num_vregs, false); - temp_.nce.ending_vregs_to_check_matrix = - temp_scoped_alloc_->AllocArray(GetNumBlocks(), kArenaAllocMisc); - std::fill_n(temp_.nce.ending_vregs_to_check_matrix, GetNumBlocks(), nullptr); - - // reset MIR_MARK - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - mir->optimization_flags &= ~MIR_MARK; - } - } - - return true; -} - -/* - * Eliminate unnecessary null checks for a basic block. - */ -bool MIRGraph::EliminateNullChecks(BasicBlock* bb) { - if (bb->block_type != kDalvikByteCode && bb->block_type != kEntryBlock) { - // Ignore the kExitBlock as well. - DCHECK(bb->first_mir_insn == nullptr); - return false; - } - - ArenaBitVector* vregs_to_check = temp_.nce.work_vregs_to_check; - /* - * Set initial state. Catch blocks don't need any special treatment. - */ - if (bb->block_type == kEntryBlock) { - vregs_to_check->ClearAllBits(); - // Assume all ins are objects. - for (uint16_t in_reg = GetFirstInVR(); - in_reg < GetNumOfCodeVRs(); in_reg++) { - vregs_to_check->SetBit(in_reg); - } - if ((cu_->access_flags & kAccStatic) == 0) { - // If non-static method, mark "this" as non-null. - int this_reg = GetFirstInVR(); - vregs_to_check->ClearBit(this_reg); - } - } else { - DCHECK_EQ(bb->block_type, kDalvikByteCode); - // Starting state is union of all incoming arcs. - bool copied_first = false; - for (BasicBlockId pred_id : bb->predecessors) { - if (temp_.nce.ending_vregs_to_check_matrix[pred_id] == nullptr) { - continue; - } - BasicBlock* pred_bb = GetBasicBlock(pred_id); - DCHECK(pred_bb != nullptr); - MIR* null_check_insn = nullptr; - // Check to see if predecessor had an explicit null-check. - if (pred_bb->BranchesToSuccessorOnlyIfNotZero(bb->id)) { - // Remember the null check insn if there's no other predecessor requiring null check. - if (!copied_first || !vregs_to_check->IsBitSet(pred_bb->last_mir_insn->dalvikInsn.vA)) { - null_check_insn = pred_bb->last_mir_insn; - DCHECK(null_check_insn != nullptr); - } - } - if (!copied_first) { - copied_first = true; - vregs_to_check->Copy(temp_.nce.ending_vregs_to_check_matrix[pred_id]); - } else { - vregs_to_check->Union(temp_.nce.ending_vregs_to_check_matrix[pred_id]); - } - if (null_check_insn != nullptr) { - vregs_to_check->ClearBit(null_check_insn->dalvikInsn.vA); - } - } - DCHECK(copied_first); // At least one predecessor must have been processed before this bb. - } - // At this point, vregs_to_check shows which sregs have an object definition with - // no intervening uses. - - // Walk through the instruction in the block, updating as necessary - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - uint64_t df_attributes = GetDataFlowAttributes(mir); - - if ((df_attributes & DF_NULL_TRANSFER_N) != 0u) { - // The algorithm was written in a phi agnostic way. - continue; - } - - // Might need a null check? - if (df_attributes & DF_HAS_NULL_CHKS) { - int src_vreg; - if (df_attributes & DF_NULL_CHK_OUT0) { - DCHECK_NE(df_attributes & DF_IS_INVOKE, 0u); - src_vreg = mir->dalvikInsn.vC; - } else if (df_attributes & DF_NULL_CHK_B) { - DCHECK_NE(df_attributes & DF_REF_B, 0u); - src_vreg = mir->dalvikInsn.vB; - } else { - DCHECK_NE(df_attributes & DF_NULL_CHK_A, 0u); - DCHECK_NE(df_attributes & DF_REF_A, 0u); - src_vreg = mir->dalvikInsn.vA; - } - if (!vregs_to_check->IsBitSet(src_vreg)) { - // Eliminate the null check. - mir->optimization_flags |= MIR_MARK; - } else { - // Do the null check. - mir->optimization_flags &= ~MIR_MARK; - // Mark src_vreg as null-checked. - vregs_to_check->ClearBit(src_vreg); - } - } - - if ((df_attributes & DF_A_WIDE) || - (df_attributes & (DF_REF_A | DF_SETS_CONST | DF_NULL_TRANSFER)) == 0) { - continue; - } - - /* - * First, mark all object definitions as requiring null check. - * Note: we can't tell if a CONST definition might be used as an object, so treat - * them all as object definitions. - */ - if ((df_attributes & (DF_DA | DF_REF_A)) == (DF_DA | DF_REF_A) || - (df_attributes & DF_SETS_CONST)) { - vregs_to_check->SetBit(mir->dalvikInsn.vA); - } - - // Then, remove mark from all object definitions we know are non-null. - if (df_attributes & DF_NON_NULL_DST) { - // Mark target of NEW* as non-null - DCHECK_NE(df_attributes & DF_REF_A, 0u); - vregs_to_check->ClearBit(mir->dalvikInsn.vA); - } - - // Mark non-null returns from invoke-style NEW* - if (df_attributes & DF_NON_NULL_RET) { - MIR* next_mir = mir->next; - // Next should be an MOVE_RESULT_OBJECT - if (UNLIKELY(next_mir == nullptr)) { - // The MethodVerifier makes sure there's no MOVE_RESULT at the catch entry or branch - // target, so the MOVE_RESULT cannot be broken away into another block. - LOG(WARNING) << "Unexpected end of block following new"; - } else if (UNLIKELY(next_mir->dalvikInsn.opcode != Instruction::MOVE_RESULT_OBJECT)) { - LOG(WARNING) << "Unexpected opcode following new: " << next_mir->dalvikInsn.opcode; - } else { - // Mark as null checked. - vregs_to_check->ClearBit(next_mir->dalvikInsn.vA); - } - } - - // Propagate null check state on register copies. - if (df_attributes & DF_NULL_TRANSFER_0) { - DCHECK_EQ(df_attributes | ~(DF_DA | DF_REF_A | DF_UB | DF_REF_B), static_cast(-1)); - if (vregs_to_check->IsBitSet(mir->dalvikInsn.vB)) { - vregs_to_check->SetBit(mir->dalvikInsn.vA); - } else { - vregs_to_check->ClearBit(mir->dalvikInsn.vA); - } - } - } - - // Did anything change? - bool nce_changed = false; - ArenaBitVector* old_ending_ssa_regs_to_check = temp_.nce.ending_vregs_to_check_matrix[bb->id]; - if (old_ending_ssa_regs_to_check == nullptr) { - DCHECK(temp_scoped_alloc_.get() != nullptr); - nce_changed = vregs_to_check->GetHighestBitSet() != -1; - temp_.nce.ending_vregs_to_check_matrix[bb->id] = vregs_to_check; - // Create a new vregs_to_check for next BB. - temp_.nce.work_vregs_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_.nce.num_vregs, false); - } else if (!vregs_to_check->SameBitsSet(old_ending_ssa_regs_to_check)) { - nce_changed = true; - temp_.nce.ending_vregs_to_check_matrix[bb->id] = vregs_to_check; - temp_.nce.work_vregs_to_check = old_ending_ssa_regs_to_check; // Reuse for next BB. - } - return nce_changed; -} - -void MIRGraph::EliminateNullChecksEnd() { - // Clean up temporaries. - temp_.nce.num_vregs = 0u; - temp_.nce.work_vregs_to_check = nullptr; - temp_.nce.ending_vregs_to_check_matrix = nullptr; - DCHECK(temp_scoped_alloc_.get() != nullptr); - temp_scoped_alloc_.reset(); - - // converge MIR_MARK with MIR_IGNORE_NULL_CHECK - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - constexpr int kMarkToIgnoreNullCheckShift = kMIRMark - kMIRIgnoreNullCheck; - static_assert(kMarkToIgnoreNullCheckShift > 0, "Not a valid right-shift"); - uint16_t mirMarkAdjustedToIgnoreNullCheck = - (mir->optimization_flags & MIR_MARK) >> kMarkToIgnoreNullCheckShift; - mir->optimization_flags |= mirMarkAdjustedToIgnoreNullCheck; - } - } -} - -void MIRGraph::InferTypesStart() { - DCHECK(temp_scoped_alloc_ != nullptr); - temp_.ssa.ti = new (temp_scoped_alloc_.get()) TypeInference(this, temp_scoped_alloc_.get()); -} - -/* - * Perform type and size inference for a basic block. - */ -bool MIRGraph::InferTypes(BasicBlock* bb) { - if (bb->data_flow_info == nullptr) return false; - - DCHECK(temp_.ssa.ti != nullptr); - return temp_.ssa.ti->Apply(bb); -} - -void MIRGraph::InferTypesEnd() { - DCHECK(temp_.ssa.ti != nullptr); - temp_.ssa.ti->Finish(); - delete temp_.ssa.ti; - temp_.ssa.ti = nullptr; -} - -bool MIRGraph::EliminateClassInitChecksGate() { - if ((cu_->disable_opt & (1 << kClassInitCheckElimination)) != 0 || - (merged_df_flags_ & DF_CLINIT) == 0) { - return false; - } - - DCHECK(temp_scoped_alloc_.get() == nullptr); - temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - - // Each insn we use here has at least 2 code units, offset/2 will be a unique index. - const size_t end = (GetNumDalvikInsns() + 1u) / 2u; - temp_.cice.indexes = temp_scoped_alloc_->AllocArray(end, kArenaAllocGrowableArray); - std::fill_n(temp_.cice.indexes, end, 0xffffu); - - uint32_t unique_class_count = 0u; - { - // Get unique_class_count and store indexes in temp_insn_data_ using a map on a nested - // ScopedArenaAllocator. - - // Embed the map value in the entry to save space. - struct MapEntry { - // Map key: the class identified by the declaring dex file and type index. - const DexFile* declaring_dex_file; - uint16_t declaring_class_idx; - // Map value: index into bit vectors of classes requiring initialization checks. - uint16_t index; - }; - struct MapEntryComparator { - bool operator()(const MapEntry& lhs, const MapEntry& rhs) const { - if (lhs.declaring_class_idx != rhs.declaring_class_idx) { - return lhs.declaring_class_idx < rhs.declaring_class_idx; - } - return lhs.declaring_dex_file < rhs.declaring_dex_file; - } - }; - - ScopedArenaAllocator allocator(&cu_->arena_stack); - ScopedArenaSet class_to_index_map(MapEntryComparator(), - allocator.Adapter()); - - // First, find all SGET/SPUTs that may need class initialization checks, record INVOKE_STATICs. - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - if (bb->block_type == kDalvikByteCode) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - if (IsInstructionSGetOrSPut(mir->dalvikInsn.opcode)) { - const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(mir); - if (!field_info.IsReferrersClass()) { - DCHECK_LT(class_to_index_map.size(), 0xffffu); - MapEntry entry = { - // Treat unresolved fields as if each had its own class. - field_info.IsResolved() ? field_info.DeclaringDexFile() - : nullptr, - field_info.IsResolved() ? field_info.DeclaringClassIndex() - : field_info.FieldIndex(), - static_cast(class_to_index_map.size()) - }; - uint16_t index = class_to_index_map.insert(entry).first->index; - // Using offset/2 for index into temp_.cice.indexes. - temp_.cice.indexes[mir->offset / 2u] = index; - } - } else if (IsInstructionInvokeStatic(mir->dalvikInsn.opcode)) { - const MirMethodLoweringInfo& method_info = GetMethodLoweringInfo(mir); - DCHECK(method_info.IsStatic()); - if (method_info.FastPath() && !method_info.IsReferrersClass()) { - MapEntry entry = { - method_info.DeclaringDexFile(), - method_info.DeclaringClassIndex(), - static_cast(class_to_index_map.size()) - }; - uint16_t index = class_to_index_map.insert(entry).first->index; - // Using offset/2 for index into temp_.cice.indexes. - temp_.cice.indexes[mir->offset / 2u] = index; - } - } - } - } - } - unique_class_count = static_cast(class_to_index_map.size()); - } - - if (unique_class_count == 0u) { - // All SGET/SPUTs refer to initialized classes. Nothing to do. - temp_.cice.indexes = nullptr; - temp_scoped_alloc_.reset(); - return false; - } - - // 2 bits for each class: is class initialized, is class in dex cache. - temp_.cice.num_class_bits = 2u * unique_class_count; - temp_.cice.work_classes_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_.cice.num_class_bits, false); - temp_.cice.ending_classes_to_check_matrix = - temp_scoped_alloc_->AllocArray(GetNumBlocks(), kArenaAllocMisc); - std::fill_n(temp_.cice.ending_classes_to_check_matrix, GetNumBlocks(), nullptr); - DCHECK_GT(temp_.cice.num_class_bits, 0u); - return true; -} - -/* - * Eliminate unnecessary class initialization checks for a basic block. - */ -bool MIRGraph::EliminateClassInitChecks(BasicBlock* bb) { - DCHECK_EQ((cu_->disable_opt & (1 << kClassInitCheckElimination)), 0u); - if (bb->block_type != kDalvikByteCode && bb->block_type != kEntryBlock) { - // Ignore the kExitBlock as well. - DCHECK(bb->first_mir_insn == nullptr); - return false; - } - - /* - * Set initial state. Catch blocks don't need any special treatment. - */ - ArenaBitVector* classes_to_check = temp_.cice.work_classes_to_check; - DCHECK(classes_to_check != nullptr); - if (bb->block_type == kEntryBlock) { - classes_to_check->SetInitialBits(temp_.cice.num_class_bits); - } else { - // Starting state is union of all incoming arcs. - bool copied_first = false; - for (BasicBlockId pred_id : bb->predecessors) { - if (temp_.cice.ending_classes_to_check_matrix[pred_id] == nullptr) { - continue; - } - if (!copied_first) { - copied_first = true; - classes_to_check->Copy(temp_.cice.ending_classes_to_check_matrix[pred_id]); - } else { - classes_to_check->Union(temp_.cice.ending_classes_to_check_matrix[pred_id]); - } - } - DCHECK(copied_first); // At least one predecessor must have been processed before this bb. - } - // At this point, classes_to_check shows which classes need clinit checks. - - // Walk through the instruction in the block, updating as necessary - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - uint16_t index = temp_.cice.indexes[mir->offset / 2u]; - if (index != 0xffffu) { - bool check_initialization = false; - bool check_dex_cache = false; - - // NOTE: index != 0xffff does not guarantee that this is an SGET/SPUT/INVOKE_STATIC. - // Dex instructions with width 1 can have the same offset/2. - - if (IsInstructionSGetOrSPut(mir->dalvikInsn.opcode)) { - check_initialization = true; - check_dex_cache = true; - } else if (IsInstructionInvokeStatic(mir->dalvikInsn.opcode)) { - check_initialization = true; - // NOTE: INVOKE_STATIC doesn't guarantee that the type will be in the dex cache. - } - - if (check_dex_cache) { - uint32_t check_dex_cache_index = 2u * index + 1u; - if (!classes_to_check->IsBitSet(check_dex_cache_index)) { - // Eliminate the class init check. - mir->optimization_flags |= MIR_CLASS_IS_IN_DEX_CACHE; - } else { - // Do the class init check. - mir->optimization_flags &= ~MIR_CLASS_IS_IN_DEX_CACHE; - } - classes_to_check->ClearBit(check_dex_cache_index); - } - if (check_initialization) { - uint32_t check_clinit_index = 2u * index; - if (!classes_to_check->IsBitSet(check_clinit_index)) { - // Eliminate the class init check. - mir->optimization_flags |= MIR_CLASS_IS_INITIALIZED; - } else { - // Do the class init check. - mir->optimization_flags &= ~MIR_CLASS_IS_INITIALIZED; - } - // Mark the class as initialized. - classes_to_check->ClearBit(check_clinit_index); - } - } - } - - // Did anything change? - bool changed = false; - ArenaBitVector* old_ending_classes_to_check = temp_.cice.ending_classes_to_check_matrix[bb->id]; - if (old_ending_classes_to_check == nullptr) { - DCHECK(temp_scoped_alloc_.get() != nullptr); - changed = classes_to_check->GetHighestBitSet() != -1; - temp_.cice.ending_classes_to_check_matrix[bb->id] = classes_to_check; - // Create a new classes_to_check for next BB. - temp_.cice.work_classes_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_.cice.num_class_bits, false); - } else if (!classes_to_check->Equal(old_ending_classes_to_check)) { - changed = true; - temp_.cice.ending_classes_to_check_matrix[bb->id] = classes_to_check; - temp_.cice.work_classes_to_check = old_ending_classes_to_check; // Reuse for next BB. - } - return changed; -} - -void MIRGraph::EliminateClassInitChecksEnd() { - // Clean up temporaries. - temp_.cice.num_class_bits = 0u; - temp_.cice.work_classes_to_check = nullptr; - temp_.cice.ending_classes_to_check_matrix = nullptr; - DCHECK(temp_.cice.indexes != nullptr); - temp_.cice.indexes = nullptr; - DCHECK(temp_scoped_alloc_.get() != nullptr); - temp_scoped_alloc_.reset(); -} - -static void DisableGVNDependentOptimizations(CompilationUnit* cu) { - cu->disable_opt |= (1u << kGvnDeadCodeElimination); -} - -bool MIRGraph::ApplyGlobalValueNumberingGate() { - if (GlobalValueNumbering::Skip(cu_)) { - DisableGVNDependentOptimizations(cu_); - return false; - } - - DCHECK(temp_scoped_alloc_ == nullptr); - temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - temp_.gvn.ifield_ids = - GlobalValueNumbering::PrepareGvnFieldIds(temp_scoped_alloc_.get(), ifield_lowering_infos_); - temp_.gvn.sfield_ids = - GlobalValueNumbering::PrepareGvnFieldIds(temp_scoped_alloc_.get(), sfield_lowering_infos_); - DCHECK(temp_.gvn.gvn == nullptr); - temp_.gvn.gvn = new (temp_scoped_alloc_.get()) GlobalValueNumbering( - cu_, temp_scoped_alloc_.get(), GlobalValueNumbering::kModeGvn); - return true; -} - -bool MIRGraph::ApplyGlobalValueNumbering(BasicBlock* bb) { - DCHECK(temp_.gvn.gvn != nullptr); - LocalValueNumbering* lvn = temp_.gvn.gvn->PrepareBasicBlock(bb); - if (lvn != nullptr) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - lvn->GetValueNumber(mir); - } - } - bool change = (lvn != nullptr) && temp_.gvn.gvn->FinishBasicBlock(bb); - return change; -} - -void MIRGraph::ApplyGlobalValueNumberingEnd() { - // Perform modifications. - DCHECK(temp_.gvn.gvn != nullptr); - if (temp_.gvn.gvn->Good()) { - temp_.gvn.gvn->StartPostProcessing(); - if (max_nested_loops_ != 0u) { - TopologicalSortIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - ScopedArenaAllocator allocator(&cu_->arena_stack); // Reclaim memory after each LVN. - LocalValueNumbering* lvn = temp_.gvn.gvn->PrepareBasicBlock(bb, &allocator); - if (lvn != nullptr) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - lvn->GetValueNumber(mir); - } - bool change = temp_.gvn.gvn->FinishBasicBlock(bb); - DCHECK(!change) << PrettyMethod(cu_->method_idx, *cu_->dex_file); - } - } - } - // GVN was successful, running the LVN would be useless. - cu_->disable_opt |= (1u << kLocalValueNumbering); - } else { - LOG(WARNING) << "GVN failed for " << PrettyMethod(cu_->method_idx, *cu_->dex_file); - DisableGVNDependentOptimizations(cu_); - } -} - -bool MIRGraph::EliminateDeadCodeGate() { - if ((cu_->disable_opt & (1 << kGvnDeadCodeElimination)) != 0 || temp_.gvn.gvn == nullptr) { - return false; - } - DCHECK(temp_scoped_alloc_ != nullptr); - temp_.gvn.dce = new (temp_scoped_alloc_.get()) GvnDeadCodeElimination(temp_.gvn.gvn, - temp_scoped_alloc_.get()); - return true; -} - -bool MIRGraph::EliminateDeadCode(BasicBlock* bb) { - DCHECK(temp_scoped_alloc_ != nullptr); - DCHECK(temp_.gvn.gvn != nullptr); - if (bb->block_type != kDalvikByteCode) { - return false; - } - DCHECK(temp_.gvn.dce != nullptr); - temp_.gvn.dce->Apply(bb); - return false; // No need to repeat. -} - -void MIRGraph::EliminateDeadCodeEnd() { - if (kIsDebugBuild) { - // DCE can make some previously dead vregs alive again. Make sure the obsolete - // live-in information is not used anymore. - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - if (bb->data_flow_info != nullptr) { - bb->data_flow_info->live_in_v = nullptr; - } - } - } -} - -void MIRGraph::GlobalValueNumberingCleanup() { - // If the GVN didn't run, these pointers should be null and everything is effectively no-op. - delete temp_.gvn.dce; - temp_.gvn.dce = nullptr; - delete temp_.gvn.gvn; - temp_.gvn.gvn = nullptr; - temp_.gvn.ifield_ids = nullptr; - temp_.gvn.sfield_ids = nullptr; - temp_scoped_alloc_.reset(); -} - -void MIRGraph::ComputeInlineIFieldLoweringInfo(uint16_t field_idx, MIR* invoke, MIR* iget_or_iput) { - uint32_t method_index = invoke->meta.method_lowering_info; - if (temp_.smi.processed_indexes->IsBitSet(method_index)) { - iget_or_iput->meta.ifield_lowering_info = temp_.smi.lowering_infos[method_index]; - DCHECK_EQ(field_idx, GetIFieldLoweringInfo(iget_or_iput).FieldIndex()); - return; - } - - const MirMethodLoweringInfo& method_info = GetMethodLoweringInfo(invoke); - MethodReference target = method_info.GetTargetMethod(); - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<1> hs(soa.Self()); - Handle dex_cache( - hs.NewHandle(cu_->class_linker->FindDexCache(hs.Self(), *target.dex_file))); - DexCompilationUnit inlined_unit(cu_, - cu_->class_loader, - cu_->class_linker, - *target.dex_file, - nullptr /* code_item not used */, - 0u /* class_def_idx not used */, - target.dex_method_index, - 0u /* access_flags not used */, - nullptr /* verified_method not used */, - dex_cache); - DexMemAccessType type = IGetOrIPutMemAccessType(iget_or_iput->dalvikInsn.opcode); - MirIFieldLoweringInfo inlined_field_info(field_idx, type, false); - MirIFieldLoweringInfo::Resolve(soa, cu_->compiler_driver, &inlined_unit, &inlined_field_info, 1u); - DCHECK(inlined_field_info.IsResolved()); - - uint32_t field_info_index = ifield_lowering_infos_.size(); - ifield_lowering_infos_.push_back(inlined_field_info); - temp_.smi.processed_indexes->SetBit(method_index); - temp_.smi.lowering_infos[method_index] = field_info_index; - iget_or_iput->meta.ifield_lowering_info = field_info_index; -} - -bool MIRGraph::InlineSpecialMethodsGate() { - if ((cu_->disable_opt & (1 << kSuppressMethodInlining)) != 0 || - method_lowering_infos_.size() == 0u) { - return false; - } - if (cu_->compiler_driver->GetMethodInlinerMap() == nullptr) { - // This isn't the Quick compiler. - return false; - } - return true; -} - -void MIRGraph::InlineSpecialMethodsStart() { - // Prepare for inlining getters/setters. Since we're inlining at most 1 IGET/IPUT from - // each INVOKE, we can index the data by the MIR::meta::method_lowering_info index. - - DCHECK(temp_scoped_alloc_.get() == nullptr); - temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - temp_.smi.num_indexes = method_lowering_infos_.size(); - temp_.smi.processed_indexes = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_.smi.num_indexes, false); - temp_.smi.processed_indexes->ClearAllBits(); - temp_.smi.lowering_infos = - temp_scoped_alloc_->AllocArray(temp_.smi.num_indexes, kArenaAllocGrowableArray); -} - -void MIRGraph::InlineSpecialMethods(BasicBlock* bb) { - if (bb->block_type != kDalvikByteCode) { - return; - } - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - if (MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode)) { - continue; - } - if (!(mir->dalvikInsn.FlagsOf() & Instruction::kInvoke)) { - continue; - } - const MirMethodLoweringInfo& method_info = GetMethodLoweringInfo(mir); - if (!method_info.FastPath() || !method_info.IsSpecial()) { - continue; - } - - InvokeType sharp_type = method_info.GetSharpType(); - if ((sharp_type != kDirect) && (sharp_type != kStatic)) { - continue; - } - - if (sharp_type == kStatic) { - bool needs_clinit = !method_info.IsClassInitialized() && - ((mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) == 0); - if (needs_clinit) { - continue; - } - } - - DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr); - MethodReference target = method_info.GetTargetMethod(); - if (cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(target.dex_file) - ->GenInline(this, bb, mir, target.dex_method_index)) { - if (cu_->verbose || cu_->print_pass) { - LOG(INFO) << "SpecialMethodInliner: Inlined " << method_info.GetInvokeType() << " (" - << sharp_type << ") call to \"" << PrettyMethod(target.dex_method_index, - *target.dex_file) - << "\" from \"" << PrettyMethod(cu_->method_idx, *cu_->dex_file) - << "\" @0x" << std::hex << mir->offset; - } - } - } -} - -void MIRGraph::InlineSpecialMethodsEnd() { - // Clean up temporaries. - DCHECK(temp_.smi.lowering_infos != nullptr); - temp_.smi.lowering_infos = nullptr; - temp_.smi.num_indexes = 0u; - DCHECK(temp_.smi.processed_indexes != nullptr); - temp_.smi.processed_indexes = nullptr; - DCHECK(temp_scoped_alloc_.get() != nullptr); - temp_scoped_alloc_.reset(); -} - -void MIRGraph::DumpCheckStats() { - Checkstats* stats = - static_cast(arena_->Alloc(sizeof(Checkstats), kArenaAllocDFInfo)); - checkstats_ = stats; - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - CountChecks(bb); - } - if (stats->null_checks > 0) { - float eliminated = static_cast(stats->null_checks_eliminated); - float checks = static_cast(stats->null_checks); - LOG(INFO) << "Null Checks: " << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " - << stats->null_checks_eliminated << " of " << stats->null_checks << " -> " - << (eliminated/checks) * 100.0 << "%"; - } - if (stats->range_checks > 0) { - float eliminated = static_cast(stats->range_checks_eliminated); - float checks = static_cast(stats->range_checks); - LOG(INFO) << "Range Checks: " << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " " - << stats->range_checks_eliminated << " of " << stats->range_checks << " -> " - << (eliminated/checks) * 100.0 << "%"; - } -} - -bool MIRGraph::BuildExtendedBBList(class BasicBlock* bb) { - if (bb->visited) return false; - if (!((bb->block_type == kEntryBlock) || (bb->block_type == kDalvikByteCode) - || (bb->block_type == kExitBlock))) { - // Ignore special blocks - bb->visited = true; - return false; - } - // Must be head of extended basic block. - BasicBlock* start_bb = bb; - extended_basic_blocks_.push_back(bb->id); - bool terminated_by_return = false; - bool do_local_value_numbering = false; - // Visit blocks strictly dominated by this head. - while (bb != nullptr) { - bb->visited = true; - terminated_by_return |= bb->terminated_by_return; - do_local_value_numbering |= bb->use_lvn; - bb = NextDominatedBlock(bb); - } - if (terminated_by_return || do_local_value_numbering) { - // Do lvn for all blocks in this extended set. - bb = start_bb; - while (bb != nullptr) { - bb->use_lvn = do_local_value_numbering; - bb->dominates_return = terminated_by_return; - bb = NextDominatedBlock(bb); - } - } - return false; // Not iterative - return value will be ignored -} - -void MIRGraph::BasicBlockOptimizationStart() { - if ((cu_->disable_opt & (1 << kLocalValueNumbering)) == 0) { - temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - temp_.gvn.ifield_ids = - GlobalValueNumbering::PrepareGvnFieldIds(temp_scoped_alloc_.get(), ifield_lowering_infos_); - temp_.gvn.sfield_ids = - GlobalValueNumbering::PrepareGvnFieldIds(temp_scoped_alloc_.get(), sfield_lowering_infos_); - } -} - -void MIRGraph::BasicBlockOptimization() { - if ((cu_->disable_opt & (1 << kSuppressExceptionEdges)) != 0) { - ClearAllVisitedFlags(); - PreOrderDfsIterator iter2(this); - for (BasicBlock* bb = iter2.Next(); bb != nullptr; bb = iter2.Next()) { - BuildExtendedBBList(bb); - } - // Perform extended basic block optimizations. - for (unsigned int i = 0; i < extended_basic_blocks_.size(); i++) { - BasicBlockOpt(GetBasicBlock(extended_basic_blocks_[i])); - } - } else { - PreOrderDfsIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - BasicBlockOpt(bb); - } - } -} - -void MIRGraph::BasicBlockOptimizationEnd() { - // Clean up after LVN. - temp_.gvn.ifield_ids = nullptr; - temp_.gvn.sfield_ids = nullptr; - temp_scoped_alloc_.reset(); -} - -void MIRGraph::StringChange() { - AllNodesIterator iter(this); - for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - // Look for new instance opcodes, skip otherwise - Instruction::Code opcode = mir->dalvikInsn.opcode; - if (opcode == Instruction::NEW_INSTANCE) { - uint32_t type_idx = mir->dalvikInsn.vB; - if (cu_->compiler_driver->IsStringTypeIndex(type_idx, cu_->dex_file)) { - LOG(FATAL) << "Quick cannot compile String allocations"; - } - } else if ((opcode == Instruction::INVOKE_DIRECT) || - (opcode == Instruction::INVOKE_DIRECT_RANGE)) { - uint32_t method_idx = mir->dalvikInsn.vB; - DexFileMethodInliner* inliner = - cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file); - if (inliner->IsStringInitMethodIndex(method_idx)) { - LOG(FATAL) << "Quick cannot compile String allocations"; - } - } - } - } -} - -bool MIRGraph::EliminateSuspendChecksGate() { - if (kLeafOptimization || // Incompatible (could create loops without suspend checks). - (cu_->disable_opt & (1 << kSuspendCheckElimination)) != 0 || // Disabled. - GetMaxNestedLoops() == 0u || // Nothing to do. - GetMaxNestedLoops() >= 32u || // Only 32 bits in suspend_checks_in_loops_[.]. - // Exclude 32 as well to keep bit shifts well-defined. - !HasInvokes()) { // No invokes to actually eliminate any suspend checks. - return false; - } - suspend_checks_in_loops_ = arena_->AllocArray(GetNumBlocks(), kArenaAllocMisc); - return true; -} - -bool MIRGraph::EliminateSuspendChecks(BasicBlock* bb) { - if (bb->block_type != kDalvikByteCode) { - return false; - } - DCHECK_EQ(GetTopologicalSortOrderLoopHeadStack()->size(), bb->nesting_depth); - if (bb->nesting_depth == 0u) { - // Out of loops. - DCHECK_EQ(suspend_checks_in_loops_[bb->id], 0u); // The array was zero-initialized. - return false; - } - uint32_t suspend_checks_in_loops = (1u << bb->nesting_depth) - 1u; // Start with all loop heads. - bool found_invoke = false; - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - if ((IsInstructionInvoke(mir->dalvikInsn.opcode) || - IsInstructionQuickInvoke(mir->dalvikInsn.opcode)) && - !GetMethodLoweringInfo(mir).IsIntrinsic()) { - // Non-intrinsic invoke, rely on a suspend point in the invoked method. - found_invoke = true; - break; - } - } - if (!found_invoke) { - // Intersect suspend checks from predecessors. - uint16_t bb_topo_idx = topological_order_indexes_[bb->id]; - uint32_t pred_mask_union = 0u; - for (BasicBlockId pred_id : bb->predecessors) { - uint16_t pred_topo_idx = topological_order_indexes_[pred_id]; - if (pred_topo_idx < bb_topo_idx) { - // Determine the loop depth of the predecessors relative to this block. - size_t pred_loop_depth = topological_order_loop_head_stack_.size(); - while (pred_loop_depth != 0u && - pred_topo_idx < topological_order_loop_head_stack_[pred_loop_depth - 1].first) { - --pred_loop_depth; - } - DCHECK_LE(pred_loop_depth, GetBasicBlock(pred_id)->nesting_depth); - uint32_t pred_mask = (1u << pred_loop_depth) - 1u; - // Intersect pred_mask bits in suspend_checks_in_loops with - // suspend_checks_in_loops_[pred_id]. - uint32_t pred_loops_without_checks = pred_mask & ~suspend_checks_in_loops_[pred_id]; - suspend_checks_in_loops = suspend_checks_in_loops & ~pred_loops_without_checks; - pred_mask_union |= pred_mask; - } - } - // DCHECK_EQ() may not hold for unnatural loop heads, so use DCHECK_GE(). - DCHECK_GE(((1u << (IsLoopHead(bb->id) ? bb->nesting_depth - 1u: bb->nesting_depth)) - 1u), - pred_mask_union); - suspend_checks_in_loops &= pred_mask_union; - } - suspend_checks_in_loops_[bb->id] = suspend_checks_in_loops; - if (suspend_checks_in_loops == 0u) { - return false; - } - // Apply MIR_IGNORE_SUSPEND_CHECK if appropriate. - if (bb->taken != NullBasicBlockId) { - DCHECK(bb->last_mir_insn != nullptr); - DCHECK(IsInstructionIfCc(bb->last_mir_insn->dalvikInsn.opcode) || - IsInstructionIfCcZ(bb->last_mir_insn->dalvikInsn.opcode) || - IsInstructionGoto(bb->last_mir_insn->dalvikInsn.opcode) || - (static_cast(bb->last_mir_insn->dalvikInsn.opcode) >= kMirOpFusedCmplFloat && - static_cast(bb->last_mir_insn->dalvikInsn.opcode) <= kMirOpFusedCmpLong)); - if (!IsSuspendCheckEdge(bb, bb->taken) && - (bb->fall_through == NullBasicBlockId || !IsSuspendCheckEdge(bb, bb->fall_through))) { - bb->last_mir_insn->optimization_flags |= MIR_IGNORE_SUSPEND_CHECK; - } - } else if (bb->fall_through != NullBasicBlockId && IsSuspendCheckEdge(bb, bb->fall_through)) { - // We've got a fall-through suspend edge. Add an artificial GOTO to force suspend check. - MIR* mir = NewMIR(); - mir->dalvikInsn.opcode = Instruction::GOTO; - mir->dalvikInsn.vA = 0; // Branch offset. - mir->offset = GetBasicBlock(bb->fall_through)->start_offset; - mir->m_unit_index = current_method_; - mir->ssa_rep = reinterpret_cast( - arena_->Alloc(sizeof(SSARepresentation), kArenaAllocDFInfo)); // Zero-initialized. - bb->AppendMIR(mir); - std::swap(bb->fall_through, bb->taken); // The fall-through has become taken. - } - return true; -} - -bool MIRGraph::CanThrow(MIR* mir) const { - if ((mir->dalvikInsn.FlagsOf() & Instruction::kThrow) == 0) { - return false; - } - const int opt_flags = mir->optimization_flags; - uint64_t df_attributes = GetDataFlowAttributes(mir); - - // First, check if the insn can still throw NPE. - if (((df_attributes & DF_HAS_NULL_CHKS) != 0) && ((opt_flags & MIR_IGNORE_NULL_CHECK) == 0)) { - return true; - } - - // Now process specific instructions. - if ((df_attributes & DF_IFIELD) != 0) { - // The IGET/IPUT family. We have processed the IGET/IPUT null check above. - DCHECK_NE(opt_flags & MIR_IGNORE_NULL_CHECK, 0); - // If not fast, weird things can happen and the insn can throw. - const MirIFieldLoweringInfo& field_info = GetIFieldLoweringInfo(mir); - bool fast = (df_attributes & DF_DA) != 0 ? field_info.FastGet() : field_info.FastPut(); - return !fast; - } else if ((df_attributes & DF_SFIELD) != 0) { - // The SGET/SPUT family. Check for potentially throwing class initialization. - // Also, if not fast, weird things can happen and the insn can throw. - const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(mir); - bool fast = (df_attributes & DF_DA) != 0 ? field_info.FastGet() : field_info.FastPut(); - bool is_class_initialized = field_info.IsClassInitialized() || - ((mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0); - return !(fast && is_class_initialized); - } else if ((df_attributes & DF_HAS_RANGE_CHKS) != 0) { - // Only AGET/APUT have range checks. We have processed the AGET/APUT null check above. - DCHECK_NE(opt_flags & MIR_IGNORE_NULL_CHECK, 0); - // Non-throwing only if range check has been eliminated. - return ((opt_flags & MIR_IGNORE_RANGE_CHECK) == 0); - } else if (mir->dalvikInsn.opcode == Instruction::CHECK_CAST && - (opt_flags & MIR_IGNORE_CHECK_CAST) != 0) { - return false; - } else if (mir->dalvikInsn.opcode == Instruction::ARRAY_LENGTH || - static_cast(mir->dalvikInsn.opcode) == kMirOpNullCheck) { - // No more checks for these (null check was processed above). - return false; - } - return true; -} - -bool MIRGraph::HasAntiDependency(MIR* first, MIR* second) { - DCHECK(first->ssa_rep != nullptr); - DCHECK(second->ssa_rep != nullptr); - if ((second->ssa_rep->num_defs > 0) && (first->ssa_rep->num_uses > 0)) { - int vreg0 = SRegToVReg(second->ssa_rep->defs[0]); - int vreg1 = (second->ssa_rep->num_defs == 2) ? - SRegToVReg(second->ssa_rep->defs[1]) : INVALID_VREG; - for (int i = 0; i < first->ssa_rep->num_uses; i++) { - int32_t use = SRegToVReg(first->ssa_rep->uses[i]); - if (use == vreg0 || use == vreg1) { - return true; - } - } - } - return false; -} - -void MIRGraph::CombineMultiplyAdd(MIR* mul_mir, MIR* add_mir, bool mul_is_first_addend, - bool is_wide, bool is_sub) { - if (is_wide) { - if (is_sub) { - add_mir->dalvikInsn.opcode = static_cast(kMirOpMsubLong); - } else { - add_mir->dalvikInsn.opcode = static_cast(kMirOpMaddLong); - } - } else { - if (is_sub) { - add_mir->dalvikInsn.opcode = static_cast(kMirOpMsubInt); - } else { - add_mir->dalvikInsn.opcode = static_cast(kMirOpMaddInt); - } - } - add_mir->ssa_rep->num_uses = is_wide ? 6 : 3; - int32_t addend0 = INVALID_SREG; - int32_t addend1 = INVALID_SREG; - if (is_wide) { - addend0 = mul_is_first_addend ? add_mir->ssa_rep->uses[2] : add_mir->ssa_rep->uses[0]; - addend1 = mul_is_first_addend ? add_mir->ssa_rep->uses[3] : add_mir->ssa_rep->uses[1]; - } else { - addend0 = mul_is_first_addend ? add_mir->ssa_rep->uses[1] : add_mir->ssa_rep->uses[0]; - } - - AllocateSSAUseData(add_mir, add_mir->ssa_rep->num_uses); - add_mir->ssa_rep->uses[0] = mul_mir->ssa_rep->uses[0]; - add_mir->ssa_rep->uses[1] = mul_mir->ssa_rep->uses[1]; - // Clear the original multiply product ssa use count, as it is not used anymore. - raw_use_counts_[mul_mir->ssa_rep->defs[0]] = 0; - use_counts_[mul_mir->ssa_rep->defs[0]] = 0; - if (is_wide) { - DCHECK_EQ(add_mir->ssa_rep->num_uses, 6); - add_mir->ssa_rep->uses[2] = mul_mir->ssa_rep->uses[2]; - add_mir->ssa_rep->uses[3] = mul_mir->ssa_rep->uses[3]; - add_mir->ssa_rep->uses[4] = addend0; - add_mir->ssa_rep->uses[5] = addend1; - raw_use_counts_[mul_mir->ssa_rep->defs[1]] = 0; - use_counts_[mul_mir->ssa_rep->defs[1]] = 0; - } else { - DCHECK_EQ(add_mir->ssa_rep->num_uses, 3); - add_mir->ssa_rep->uses[2] = addend0; - } - // Copy in the decoded instruction information. - add_mir->dalvikInsn.vB = SRegToVReg(add_mir->ssa_rep->uses[0]); - if (is_wide) { - add_mir->dalvikInsn.vC = SRegToVReg(add_mir->ssa_rep->uses[2]); - add_mir->dalvikInsn.arg[0] = SRegToVReg(add_mir->ssa_rep->uses[4]); - } else { - add_mir->dalvikInsn.vC = SRegToVReg(add_mir->ssa_rep->uses[1]); - add_mir->dalvikInsn.arg[0] = SRegToVReg(add_mir->ssa_rep->uses[2]); - } - // Original multiply MIR is set to Nop. - mul_mir->dalvikInsn.opcode = static_cast(kMirOpNop); -} - -void MIRGraph::MultiplyAddOpt(BasicBlock* bb) { - if (bb->block_type == kDead) { - return; - } - ScopedArenaAllocator allocator(&cu_->arena_stack); - ScopedArenaSafeMap ssa_mul_map(std::less(), allocator.Adapter()); - ScopedArenaSafeMap::iterator map_it; - for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - Instruction::Code opcode = mir->dalvikInsn.opcode; - bool is_sub = true; - bool is_candidate_multiply = false; - switch (opcode) { - case Instruction::MUL_INT: - case Instruction::MUL_INT_2ADDR: - is_candidate_multiply = true; - break; - case Instruction::MUL_LONG: - case Instruction::MUL_LONG_2ADDR: - if (cu_->target64) { - is_candidate_multiply = true; - } - break; - case Instruction::ADD_INT: - case Instruction::ADD_INT_2ADDR: - is_sub = false; - FALLTHROUGH_INTENDED; - case Instruction::SUB_INT: - case Instruction::SUB_INT_2ADDR: - if (((map_it = ssa_mul_map.find(mir->ssa_rep->uses[0])) != ssa_mul_map.end()) && !is_sub) { - // a*b+c - CombineMultiplyAdd(map_it->second, mir, true /* product is the first addend */, - false /* is_wide */, false /* is_sub */); - ssa_mul_map.erase(mir->ssa_rep->uses[0]); - } else if ((map_it = ssa_mul_map.find(mir->ssa_rep->uses[1])) != ssa_mul_map.end()) { - // c+a*b or c-a*b - CombineMultiplyAdd(map_it->second, mir, false /* product is the second addend */, - false /* is_wide */, is_sub); - ssa_mul_map.erase(map_it); - } - break; - case Instruction::ADD_LONG: - case Instruction::ADD_LONG_2ADDR: - is_sub = false; - FALLTHROUGH_INTENDED; - case Instruction::SUB_LONG: - case Instruction::SUB_LONG_2ADDR: - if (!cu_->target64) { - break; - } - if ((map_it = ssa_mul_map.find(mir->ssa_rep->uses[0])) != ssa_mul_map.end() && !is_sub) { - // a*b+c - CombineMultiplyAdd(map_it->second, mir, true /* product is the first addend */, - true /* is_wide */, false /* is_sub */); - ssa_mul_map.erase(map_it); - } else if ((map_it = ssa_mul_map.find(mir->ssa_rep->uses[2])) != ssa_mul_map.end()) { - // c+a*b or c-a*b - CombineMultiplyAdd(map_it->second, mir, false /* product is the second addend */, - true /* is_wide */, is_sub); - ssa_mul_map.erase(map_it); - } - break; - default: - if (!ssa_mul_map.empty() && CanThrow(mir)) { - // Should not combine multiply and add MIRs across potential exception. - ssa_mul_map.clear(); - } - break; - } - - // Exclude the case when an MIR writes a vreg which is previous candidate multiply MIR's uses. - // It is because that current RA may allocate the same physical register to them. For this - // kind of cases, the multiplier has been updated, we should not use updated value to the - // multiply-add insn. - if (ssa_mul_map.size() > 0) { - for (auto it = ssa_mul_map.begin(); it != ssa_mul_map.end();) { - MIR* mul = it->second; - if (HasAntiDependency(mul, mir)) { - it = ssa_mul_map.erase(it); - } else { - ++it; - } - } - } - - if (is_candidate_multiply && - (GetRawUseCount(mir->ssa_rep->defs[0]) == 1) && (mir->next != nullptr)) { - ssa_mul_map.Put(mir->ssa_rep->defs[0], mir); - } - } -} - -} // namespace art diff --git a/compiler/dex/mir_optimization_test.cc b/compiler/dex/mir_optimization_test.cc deleted file mode 100644 index a0cedff9b8..0000000000 --- a/compiler/dex/mir_optimization_test.cc +++ /dev/null @@ -1,1186 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "base/logging.h" -#include "dataflow_iterator.h" -#include "dataflow_iterator-inl.h" -#include "dex/compiler_ir.h" -#include "dex/mir_field_info.h" -#include "gtest/gtest.h" - -namespace art { - -class MirOptimizationTest : public testing::Test { - protected: - struct BBDef { - static constexpr size_t kMaxSuccessors = 4; - static constexpr size_t kMaxPredecessors = 4; - - BBType type; - size_t num_successors; - BasicBlockId successors[kMaxPredecessors]; - size_t num_predecessors; - BasicBlockId predecessors[kMaxPredecessors]; - }; - - struct MethodDef { - uint16_t method_idx; - uintptr_t declaring_dex_file; - uint16_t declaring_class_idx; - uint16_t declaring_method_idx; - InvokeType invoke_type; - InvokeType sharp_type; - bool is_referrers_class; - bool is_initialized; - }; - - struct MIRDef { - BasicBlockId bbid; - Instruction::Code opcode; - uint32_t field_or_method_info; - uint32_t vA; - uint32_t vB; - uint32_t vC; - }; - -#define DEF_SUCC0() \ - 0u, { } -#define DEF_SUCC1(s1) \ - 1u, { s1 } -#define DEF_SUCC2(s1, s2) \ - 2u, { s1, s2 } -#define DEF_SUCC3(s1, s2, s3) \ - 3u, { s1, s2, s3 } -#define DEF_SUCC4(s1, s2, s3, s4) \ - 4u, { s1, s2, s3, s4 } -#define DEF_PRED0() \ - 0u, { } -#define DEF_PRED1(p1) \ - 1u, { p1 } -#define DEF_PRED2(p1, p2) \ - 2u, { p1, p2 } -#define DEF_PRED3(p1, p2, p3) \ - 3u, { p1, p2, p3 } -#define DEF_PRED4(p1, p2, p3, p4) \ - 4u, { p1, p2, p3, p4 } -#define DEF_BB(type, succ, pred) \ - { type, succ, pred } - -#define DEF_SGET_SPUT(bb, opcode, vA, field_info) \ - { bb, opcode, field_info, vA, 0u, 0u } -#define DEF_IGET_IPUT(bb, opcode, vA, vB, field_info) \ - { bb, opcode, field_info, vA, vB, 0u } -#define DEF_AGET_APUT(bb, opcode, vA, vB, vC) \ - { bb, opcode, 0u, vA, vB, vC } -#define DEF_INVOKE(bb, opcode, vC, method_info) \ - { bb, opcode, method_info, 0u, 0u, vC } -#define DEF_OTHER0(bb, opcode) \ - { bb, opcode, 0u, 0u, 0u, 0u } -#define DEF_OTHER1(bb, opcode, vA) \ - { bb, opcode, 0u, vA, 0u, 0u } -#define DEF_OTHER2(bb, opcode, vA, vB) \ - { bb, opcode, 0u, vA, vB, 0u } - - void DoPrepareBasicBlocks(const BBDef* defs, size_t count) { - cu_.mir_graph->block_id_map_.clear(); - cu_.mir_graph->block_list_.clear(); - ASSERT_LT(3u, count); // null, entry, exit and at least one bytecode block. - ASSERT_EQ(kNullBlock, defs[0].type); - ASSERT_EQ(kEntryBlock, defs[1].type); - ASSERT_EQ(kExitBlock, defs[2].type); - for (size_t i = 0u; i != count; ++i) { - const BBDef* def = &defs[i]; - BasicBlock* bb = cu_.mir_graph->CreateNewBB(def->type); - if (def->num_successors <= 2) { - bb->successor_block_list_type = kNotUsed; - bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : 0u; - bb->taken = (def->num_successors >= 2) ? def->successors[1] : 0u; - } else { - bb->successor_block_list_type = kPackedSwitch; - bb->fall_through = 0u; - bb->taken = 0u; - bb->successor_blocks.reserve(def->num_successors); - for (size_t j = 0u; j != def->num_successors; ++j) { - SuccessorBlockInfo* successor_block_info = - static_cast(cu_.arena.Alloc(sizeof(SuccessorBlockInfo), - kArenaAllocSuccessors)); - successor_block_info->block = j; - successor_block_info->key = 0u; // Not used by class init check elimination. - bb->successor_blocks.push_back(successor_block_info); - } - } - bb->predecessors.assign(def->predecessors, def->predecessors + def->num_predecessors); - if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) { - bb->data_flow_info = static_cast( - cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo)); - } - } - ASSERT_EQ(count, cu_.mir_graph->block_list_.size()); - cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1]; - ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type); - cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_[2]; - ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type); - } - - template - void PrepareBasicBlocks(const BBDef (&defs)[count]) { - DoPrepareBasicBlocks(defs, count); - } - - void PrepareSingleBlock() { - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(3)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(1)), - }; - PrepareBasicBlocks(bbs); - } - - void PrepareDiamond() { - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)), - }; - PrepareBasicBlocks(bbs); - } - - void PrepareLoop() { - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)), // "taken" loops to self. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), - }; - PrepareBasicBlocks(bbs); - } - - void PrepareNestedLoopsWhile_While() { - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(8)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 8), DEF_PRED2(3, 7)), // Outer while loop head. - DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 7), DEF_PRED2(4, 6)), // Inner while loop head. - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(5)), // "taken" loops to inner head. - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(5)), // "taken" loops to outer head. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), - }; - PrepareBasicBlocks(bbs); - } - - void PrepareNestedLoopsWhile_WhileWhile() { - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(10)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 10), DEF_PRED2(3, 9)), // Outer while loop head. - DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 7), DEF_PRED2(4, 6)), // Inner while loop head 1. - DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(5)), // Loops to inner head 1. - DEF_BB(kDalvikByteCode, DEF_SUCC2(8, 9), DEF_PRED2(5, 8)), // Inner while loop head 2. - DEF_BB(kDalvikByteCode, DEF_SUCC1(7), DEF_PRED1(7)), // loops to inner head 2. - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(7)), // loops to outer head. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), - }; - PrepareBasicBlocks(bbs); - } - - void PrepareNestedLoopsWhile_WhileWhile_WithExtraEdge() { - // Extra edge from the first inner loop body to second inner loop body (6u->8u). - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(10)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 10), DEF_PRED2(3, 9)), // Outer while loop head. - DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 7), DEF_PRED2(4, 6)), // Inner while loop head 1. - DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 8), DEF_PRED1(5)), // Loops to inner head 1. - DEF_BB(kDalvikByteCode, DEF_SUCC2(8, 9), DEF_PRED2(5, 8)), // Inner while loop head 2. - DEF_BB(kDalvikByteCode, DEF_SUCC1(7), DEF_PRED2(7, 6)), // loops to inner head 2. - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(7)), // loops to outer head. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)), - }; - PrepareBasicBlocks(bbs); - } - - void PrepareCatch() { - static const BBDef bbs[] = { - DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), - DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()), - DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)), - DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), // The top. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // The throwing insn. - DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Catch handler. - DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)), // The merged block. - }; - PrepareBasicBlocks(bbs); - BasicBlock* catch_handler = cu_.mir_graph->GetBasicBlock(5u); - catch_handler->catch_entry = true; - // Add successor block info to the check block. - BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u); - check_bb->successor_block_list_type = kCatch; - SuccessorBlockInfo* successor_block_info = reinterpret_cast - (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessors)); - successor_block_info->block = catch_handler->id; - check_bb->successor_blocks.push_back(successor_block_info); - } - - void DoPrepareMethods(const MethodDef* defs, size_t count) { - cu_.mir_graph->method_lowering_infos_.clear(); - cu_.mir_graph->method_lowering_infos_.reserve(count); - for (size_t i = 0u; i != count; ++i) { - const MethodDef* def = &defs[i]; - MirMethodLoweringInfo method_info(def->method_idx, def->invoke_type, false); - if (def->declaring_dex_file != 0u) { - method_info.declaring_dex_file_ = reinterpret_cast(def->declaring_dex_file); - method_info.declaring_class_idx_ = def->declaring_class_idx; - method_info.declaring_method_idx_ = def->declaring_method_idx; - } - ASSERT_EQ(def->invoke_type != kStatic, def->sharp_type != kStatic); - method_info.flags_ = - ((def->invoke_type == kStatic) ? MirMethodLoweringInfo::kFlagIsStatic : 0u) | - MirMethodLoweringInfo::kFlagFastPath | - (static_cast(def->invoke_type) << MirMethodLoweringInfo::kBitInvokeTypeBegin) | - (static_cast(def->sharp_type) << MirMethodLoweringInfo::kBitSharpTypeBegin) | - ((def->is_referrers_class) ? MirMethodLoweringInfo::kFlagIsReferrersClass : 0u) | - ((def->is_initialized == kStatic) ? MirMethodLoweringInfo::kFlagClassIsInitialized : 0u); - ASSERT_EQ(def->declaring_dex_file != 0u, method_info.IsResolved()); - cu_.mir_graph->method_lowering_infos_.push_back(method_info); - } - } - - template - void PrepareMethods(const MethodDef (&defs)[count]) { - DoPrepareMethods(defs, count); - } - - void DoPrepareMIRs(const MIRDef* defs, size_t count) { - mir_count_ = count; - mirs_ = cu_.arena.AllocArray(count, kArenaAllocMIR); - uint64_t merged_df_flags = 0u; - for (size_t i = 0u; i != count; ++i) { - const MIRDef* def = &defs[i]; - MIR* mir = &mirs_[i]; - mir->dalvikInsn.opcode = def->opcode; - ASSERT_LT(def->bbid, cu_.mir_graph->block_list_.size()); - BasicBlock* bb = cu_.mir_graph->block_list_[def->bbid]; - bb->AppendMIR(mir); - if (IsInstructionIGetOrIPut(def->opcode)) { - ASSERT_LT(def->field_or_method_info, cu_.mir_graph->ifield_lowering_infos_.size()); - mir->meta.ifield_lowering_info = def->field_or_method_info; - ASSERT_EQ(cu_.mir_graph->ifield_lowering_infos_[def->field_or_method_info].MemAccessType(), - IGetOrIPutMemAccessType(def->opcode)); - } else if (IsInstructionSGetOrSPut(def->opcode)) { - ASSERT_LT(def->field_or_method_info, cu_.mir_graph->sfield_lowering_infos_.size()); - mir->meta.sfield_lowering_info = def->field_or_method_info; - ASSERT_EQ(cu_.mir_graph->sfield_lowering_infos_[def->field_or_method_info].MemAccessType(), - SGetOrSPutMemAccessType(def->opcode)); - } else if (IsInstructionInvoke(def->opcode)) { - ASSERT_LT(def->field_or_method_info, cu_.mir_graph->method_lowering_infos_.size()); - mir->meta.method_lowering_info = def->field_or_method_info; - } - mir->dalvikInsn.vA = def->vA; - mir->dalvikInsn.vB = def->vB; - mir->dalvikInsn.vC = def->vC; - mir->ssa_rep = nullptr; - mir->offset = 2 * i; // All insns need to be at least 2 code units long. - mir->optimization_flags = 0u; - merged_df_flags |= MIRGraph::GetDataFlowAttributes(def->opcode); - } - cu_.mir_graph->merged_df_flags_ = merged_df_flags; - - code_item_ = static_cast( - cu_.arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc)); - memset(code_item_, 0, sizeof(DexFile::CodeItem)); - code_item_->insns_size_in_code_units_ = 2u * count; - cu_.mir_graph->current_code_item_ = code_item_; - } - - template - void PrepareMIRs(const MIRDef (&defs)[count]) { - DoPrepareMIRs(defs, count); - } - - MirOptimizationTest() - : pool_(), - cu_(&pool_, kRuntimeISA, nullptr, nullptr), - mir_count_(0u), - mirs_(nullptr), - code_item_(nullptr) { - cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); - cu_.access_flags = kAccStatic; // Don't let "this" interfere with this test. - } - - ArenaPool pool_; - CompilationUnit cu_; - size_t mir_count_; - MIR* mirs_; - DexFile::CodeItem* code_item_; -}; - -class ClassInitCheckEliminationTest : public MirOptimizationTest { - protected: - struct SFieldDef { - uint16_t field_idx; - uintptr_t declaring_dex_file; - uint16_t declaring_class_idx; - uint16_t declaring_field_idx; - DexMemAccessType type; - }; - - void DoPrepareSFields(const SFieldDef* defs, size_t count) { - cu_.mir_graph->sfield_lowering_infos_.clear(); - cu_.mir_graph->sfield_lowering_infos_.reserve(count); - for (size_t i = 0u; i != count; ++i) { - const SFieldDef* def = &defs[i]; - MirSFieldLoweringInfo field_info(def->field_idx, def->type); - if (def->declaring_dex_file != 0u) { - field_info.declaring_dex_file_ = reinterpret_cast(def->declaring_dex_file); - field_info.declaring_class_idx_ = def->declaring_class_idx; - field_info.declaring_field_idx_ = def->declaring_field_idx; - // We don't care about the volatile flag in these tests. - } - ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved()); - ASSERT_FALSE(field_info.IsClassInitialized()); - cu_.mir_graph->sfield_lowering_infos_.push_back(field_info); - } - } - - template - void PrepareSFields(const SFieldDef (&defs)[count]) { - DoPrepareSFields(defs, count); - } - - void PerformClassInitCheckElimination() { - cu_.mir_graph->ComputeDFSOrders(); - bool gate_result = cu_.mir_graph->EliminateClassInitChecksGate(); - ASSERT_TRUE(gate_result); - RepeatingPreOrderDfsIterator iterator(cu_.mir_graph.get()); - bool change = false; - for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) { - change = cu_.mir_graph->EliminateClassInitChecks(bb); - } - cu_.mir_graph->EliminateClassInitChecksEnd(); - } - - ClassInitCheckEliminationTest() - : MirOptimizationTest() { - } -}; - -class NullCheckEliminationTest : public MirOptimizationTest { - protected: - struct IFieldDef { - uint16_t field_idx; - uintptr_t declaring_dex_file; - uint16_t declaring_class_idx; - uint16_t declaring_field_idx; - DexMemAccessType type; - }; - - void DoPrepareIFields(const IFieldDef* defs, size_t count) { - cu_.mir_graph->ifield_lowering_infos_.clear(); - cu_.mir_graph->ifield_lowering_infos_.reserve(count); - for (size_t i = 0u; i != count; ++i) { - const IFieldDef* def = &defs[i]; - MirIFieldLoweringInfo field_info(def->field_idx, def->type, false); - if (def->declaring_dex_file != 0u) { - field_info.declaring_dex_file_ = reinterpret_cast(def->declaring_dex_file); - field_info.declaring_class_idx_ = def->declaring_class_idx; - field_info.declaring_field_idx_ = def->declaring_field_idx; - // We don't care about the volatile flag in these tests. - } - ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved()); - cu_.mir_graph->ifield_lowering_infos_.push_back(field_info); - } - } - - template - void PrepareIFields(const IFieldDef (&defs)[count]) { - DoPrepareIFields(defs, count); - } - - void PerformNullCheckElimination() { - // Make vregs in range [100, 1000) input registers, i.e. requiring a null check. - code_item_->registers_size_ = 1000; - code_item_->ins_size_ = 900; - - cu_.mir_graph->ComputeDFSOrders(); - bool gate_result = cu_.mir_graph->EliminateNullChecksGate(); - ASSERT_TRUE(gate_result); - RepeatingPreOrderDfsIterator iterator(cu_.mir_graph.get()); - bool change = false; - for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) { - change = cu_.mir_graph->EliminateNullChecks(bb); - } - cu_.mir_graph->EliminateNullChecksEnd(); - } - - NullCheckEliminationTest() - : MirOptimizationTest() { - static const MethodDef methods[] = { - { 0u, 1u, 0u, 0u, kDirect, kDirect, false, false }, // Dummy. - }; - PrepareMethods(methods); - } -}; - -class SuspendCheckEliminationTest : public MirOptimizationTest { - protected: - bool IsBackEdge(BasicBlockId branch_bb, BasicBlockId target_bb) { - BasicBlock* branch = cu_.mir_graph->GetBasicBlock(branch_bb); - return target_bb != NullBasicBlockId && cu_.mir_graph->IsBackEdge(branch, target_bb); - } - - bool IsSuspendCheckEdge(BasicBlockId branch_bb, BasicBlockId target_bb) { - BasicBlock* branch = cu_.mir_graph->GetBasicBlock(branch_bb); - return cu_.mir_graph->IsSuspendCheckEdge(branch, target_bb); - } - - void PerformSuspendCheckElimination() { - cu_.mir_graph->SSATransformationStart(); - cu_.mir_graph->ComputeDFSOrders(); - cu_.mir_graph->ComputeDominators(); - cu_.mir_graph->ComputeTopologicalSortOrder(); - cu_.mir_graph->SSATransformationEnd(); - - bool gate_result = cu_.mir_graph->EliminateSuspendChecksGate(); - ASSERT_NE(gate_result, kLeafOptimization); - if (kLeafOptimization) { - // Even with kLeafOptimization on and Gate() refusing to allow SCE, we want - // to run the SCE test to avoid bitrot, so we need to initialize explicitly. - cu_.mir_graph->suspend_checks_in_loops_ = - cu_.mir_graph->arena_->AllocArray(cu_.mir_graph->GetNumBlocks(), - kArenaAllocMisc); - } - - TopologicalSortIterator iterator(cu_.mir_graph.get()); - bool change = false; - for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) { - change = cu_.mir_graph->EliminateSuspendChecks(bb); - } - } - - SuspendCheckEliminationTest() - : MirOptimizationTest() { - static const MethodDef methods[] = { - { 0u, 1u, 0u, 0u, kDirect, kDirect, false, false }, // Dummy. - }; - PrepareMethods(methods); - } -}; - -TEST_F(ClassInitCheckEliminationTest, SingleBlock) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 1u, 1u, kDexMemAccessWord }, - { 2u, 1u, 2u, 2u, kDexMemAccessWord }, - { 3u, 1u, 3u, 3u, kDexMemAccessWord }, // Same declaring class as sfield[4]. - { 4u, 1u, 3u, 4u, kDexMemAccessWord }, // Same declaring class as sfield[3]. - { 5u, 0u, 0u, 0u, kDexMemAccessWord }, // Unresolved. - }; - static const MIRDef mirs[] = { - DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 5u), // Unresolved. - DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 0u), - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u), - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 2u), - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 5u), // Unresolved. - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u), - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u), - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 2u), - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 5u), // Unresolved. - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 3u), - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 4u), - }; - static const bool expected_ignore_clinit_check[] = { - false, false, false, false, true, true, true, true, true, false, true - }; - - PrepareSFields(sfields); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformClassInitCheckElimination(); - ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_clinit_check[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i; - EXPECT_EQ(expected_ignore_clinit_check[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i; - } -} - -TEST_F(ClassInitCheckEliminationTest, SingleBlockWithInvokes) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 1u, 1u, kDexMemAccessWord }, - { 2u, 1u, 2u, 2u, kDexMemAccessWord }, - }; - static const MethodDef methods[] = { - { 0u, 1u, 0u, 0u, kStatic, kStatic, false, false }, - { 1u, 1u, 1u, 1u, kStatic, kStatic, false, false }, - { 2u, 1u, 2u, 2u, kStatic, kStatic, false, false }, - }; - static const MIRDef mirs[] = { - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u), - DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u), - DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u), - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u), - DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u), - DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u), - }; - static const bool expected_class_initialized[] = { - false, true, false, true, false, true - }; - static const bool expected_class_in_dex_cache[] = { - false, false, false, false, false, false - }; - - PrepareSFields(sfields); - PrepareMethods(methods); - PrepareSingleBlock(); - PrepareMIRs(mirs); - PerformClassInitCheckElimination(); - ASSERT_EQ(arraysize(expected_class_initialized), mir_count_); - ASSERT_EQ(arraysize(expected_class_in_dex_cache), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_class_initialized[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i; - EXPECT_EQ(expected_class_in_dex_cache[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i; - } -} - -TEST_F(ClassInitCheckEliminationTest, Diamond) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 1u, 1u, kDexMemAccessWord }, - { 2u, 1u, 2u, 2u, kDexMemAccessWord }, - { 3u, 1u, 3u, 3u, kDexMemAccessWord }, - { 4u, 1u, 4u, 4u, kDexMemAccessWord }, - { 5u, 1u, 5u, 5u, kDexMemAccessWord }, - { 6u, 1u, 6u, 6u, kDexMemAccessWord }, - { 7u, 1u, 7u, 7u, kDexMemAccessWord }, - { 8u, 1u, 8u, 8u, kDexMemAccessWord }, // Same declaring class as sfield[9]. - { 9u, 1u, 8u, 9u, kDexMemAccessWord }, // Same declaring class as sfield[8]. - { 10u, 0u, 0u, 0u, kDexMemAccessWord }, // Unresolved. - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 10u), // Unresolved. - DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 10u), // Unresolved. - DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 0u), - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 0u), // Eliminated (BB #3 dominates #6). - DEF_SGET_SPUT(4u, Instruction::SPUT, 0u, 1u), - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 1u), // Not eliminated (BB #4 doesn't dominate #6). - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 2u), - DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u), // Eliminated (BB #3 dominates #4). - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 3u), - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 3u), // Eliminated (BB #3 dominates #5). - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 4u), - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 4u), // Eliminated (BB #3 dominates #6). - DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 5u), - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 5u), // Not eliminated (BB #4 doesn't dominate #6). - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 6u), - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 6u), // Not eliminated (BB #5 doesn't dominate #6). - DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 7u), - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 7u), - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 7u), // Eliminated (initialized in both #3 and #4). - DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 8u), - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 9u), - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 8u), // Eliminated (with sfield[9] in BB #5). - DEF_SGET_SPUT(6u, Instruction::SPUT, 0u, 9u), // Eliminated (with sfield[8] in BB #4). - }; - static const bool expected_ignore_clinit_check[] = { - false, true, // Unresolved: sfield[10] - false, true, // sfield[0] - false, false, // sfield[1] - false, true, // sfield[2] - false, true, // sfield[3] - false, true, // sfield[4] - false, false, // sfield[5] - false, false, // sfield[6] - false, false, true, // sfield[7] - false, false, true, true, // sfield[8], sfield[9] - }; - - PrepareSFields(sfields); - PrepareDiamond(); - PrepareMIRs(mirs); - PerformClassInitCheckElimination(); - ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_clinit_check[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i; - EXPECT_EQ(expected_ignore_clinit_check[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i; - } -} - -TEST_F(ClassInitCheckEliminationTest, DiamondWithInvokes) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 1u, 1u, kDexMemAccessWord }, - { 2u, 1u, 2u, 2u, kDexMemAccessWord }, - { 3u, 1u, 3u, 3u, kDexMemAccessWord }, - { 4u, 1u, 4u, 4u, kDexMemAccessWord }, - }; - static const MethodDef methods[] = { - { 0u, 1u, 0u, 0u, kStatic, kStatic, false, false }, - { 1u, 1u, 1u, 1u, kStatic, kStatic, false, false }, - { 2u, 1u, 2u, 2u, kStatic, kStatic, false, false }, - { 3u, 1u, 3u, 3u, kStatic, kStatic, false, false }, - { 4u, 1u, 4u, 4u, kStatic, kStatic, false, false }, - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 0u), - DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u), - DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u), - DEF_SGET_SPUT(6u, Instruction::SPUT, 0u, 1u), - DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u), - DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u), - DEF_SGET_SPUT(6u, Instruction::SPUT, 0u, 2u), - DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u /* dummy */, 3u), - DEF_SGET_SPUT(5u, Instruction::SPUT, 0u, 3u), - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 3u), - DEF_SGET_SPUT(4u, Instruction::SPUT, 0u, 4u), - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 4u), - DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u /* dummy */, 4u), - }; - static const bool expected_class_initialized[] = { - false, true, // BB #3 SPUT, BB#6 INVOKE_STATIC - false, true, // BB #3 INVOKE_STATIC, BB#6 SPUT - false, false, true, // BB #4 SGET, BB #5 INVOKE_STATIC, BB #6 SPUT - false, false, true, // BB #4 INVOKE_STATIC, BB #5 SPUT, BB #6 SGET - false, false, true, // BB #4 SPUT, BB #5 SGET, BB #6 INVOKE_STATIC - }; - static const bool expected_class_in_dex_cache[] = { - false, false, // BB #3 SPUT, BB#6 INVOKE_STATIC - false, false, // BB #3 INVOKE_STATIC, BB#6 SPUT - false, false, false, // BB #4 SGET, BB #5 INVOKE_STATIC, BB #6 SPUT - false, false, false, // BB #4 INVOKE_STATIC, BB #5 SPUT, BB #6 SGET - false, false, false, // BB #4 SPUT, BB #5 SGET, BB #6 INVOKE_STATIC - }; - - PrepareSFields(sfields); - PrepareMethods(methods); - PrepareDiamond(); - PrepareMIRs(mirs); - PerformClassInitCheckElimination(); - ASSERT_EQ(arraysize(expected_class_initialized), mir_count_); - ASSERT_EQ(arraysize(expected_class_in_dex_cache), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_class_initialized[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i; - EXPECT_EQ(expected_class_in_dex_cache[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i; - } -} - -TEST_F(ClassInitCheckEliminationTest, Loop) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 1u, 1u, kDexMemAccessWord }, - { 2u, 1u, 2u, 2u, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u), - DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 0u), // Eliminated. - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u), - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 1u), // Eliminated. - DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u), - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 2u), // Eliminated. - }; - static const bool expected_ignore_clinit_check[] = { - false, true, false, true, false, true, - }; - - PrepareSFields(sfields); - PrepareLoop(); - PrepareMIRs(mirs); - PerformClassInitCheckElimination(); - ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_clinit_check[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i; - EXPECT_EQ(expected_ignore_clinit_check[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i; - } -} - -TEST_F(ClassInitCheckEliminationTest, LoopWithInvokes) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - }; - static const MethodDef methods[] = { - { 0u, 1u, 0u, 0u, kStatic, kStatic, false, false }, - { 1u, 1u, 1u, 1u, kStatic, kStatic, false, false }, - { 2u, 1u, 2u, 2u, kStatic, kStatic, false, false }, - }; - static const MIRDef mirs[] = { - DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u), - DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u /* dummy */, 0u), - DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u), - DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u /* dummy */, 1u), - DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u), - DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u /* dummy */, 2u), - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 0u), - }; - static const bool expected_class_initialized[] = { - false, true, false, true, false, true, true, - }; - static const bool expected_class_in_dex_cache[] = { - false, false, false, false, false, false, false, - }; - - PrepareSFields(sfields); - PrepareMethods(methods); - PrepareLoop(); - PrepareMIRs(mirs); - PerformClassInitCheckElimination(); - ASSERT_EQ(arraysize(expected_class_initialized), mir_count_); - ASSERT_EQ(arraysize(expected_class_in_dex_cache), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_class_initialized[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i; - EXPECT_EQ(expected_class_in_dex_cache[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i; - } -} - -TEST_F(ClassInitCheckEliminationTest, Catch) { - static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 1u, 1u, kDexMemAccessWord }, - { 2u, 1u, 2u, 2u, kDexMemAccessWord }, - { 3u, 1u, 3u, 3u, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u), // Before the exception edge. - DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 1u), // Before the exception edge. - DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 2u), // After the exception edge. - DEF_SGET_SPUT(4u, Instruction::SGET, 0u, 3u), // After the exception edge. - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 0u), // In catch handler; eliminated. - DEF_SGET_SPUT(5u, Instruction::SGET, 0u, 2u), // In catch handler; not eliminated. - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 0u), // Class init check eliminated. - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 1u), // Class init check eliminated. - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 2u), // Class init check eliminated. - DEF_SGET_SPUT(6u, Instruction::SGET, 0u, 3u), // Class init check not eliminated. - }; - static const bool expected_ignore_clinit_check[] = { - false, false, false, false, true, false, true, true, true, false - }; - - PrepareSFields(sfields); - PrepareCatch(); - PrepareMIRs(mirs); - PerformClassInitCheckElimination(); - ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_clinit_check[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0) << i; - EXPECT_EQ(expected_ignore_clinit_check[i], - (mirs_[i].optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0) << i; - } -} - -TEST_F(NullCheckEliminationTest, SingleBlock) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 0u, 1u, kDexMemAccessWord }, - { 2u, 1u, 0u, 2u, kDexMemAccessObject }, - }; - static const MIRDef mirs[] = { - DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 0u, 100u, 2u), - DEF_IGET_IPUT(3u, Instruction::IGET, 1u, 0u, 1u), - DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 2u, 100u, 2u), // Differs from 0u (no LVN here). - DEF_IGET_IPUT(3u, Instruction::IGET, 3u, 2u, 1u), - DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 101u, 0u), - DEF_IGET_IPUT(3u, Instruction::IGET, 5u, 102u, 0u), - DEF_IGET_IPUT(3u, Instruction::IGET, 6u, 103u, 0u), - DEF_IGET_IPUT(3u, Instruction::IGET, 7u, 103u, 1u), - DEF_IGET_IPUT(3u, Instruction::IPUT, 8u, 104u, 0u), - DEF_IGET_IPUT(3u, Instruction::IPUT, 9u, 104u, 1u), - DEF_IGET_IPUT(3u, Instruction::IGET, 10u, 105u, 0u), - DEF_IGET_IPUT(3u, Instruction::IPUT, 11u, 105u, 1u), - DEF_IGET_IPUT(3u, Instruction::IPUT, 12u, 106u, 0u), - DEF_IGET_IPUT(3u, Instruction::IGET, 13u, 106u, 1u), - DEF_INVOKE(3u, Instruction::INVOKE_DIRECT, 107, 0u /* dummy */), - DEF_IGET_IPUT(3u, Instruction::IGET, 15u, 107u, 1u), - DEF_IGET_IPUT(3u, Instruction::IGET, 16u, 108u, 0u), - DEF_INVOKE(3u, Instruction::INVOKE_DIRECT, 108, 0u /* dummy */), - DEF_AGET_APUT(3u, Instruction::AGET, 18u, 109u, 110u), - DEF_AGET_APUT(3u, Instruction::APUT, 19u, 109u, 111u), - DEF_OTHER2(3u, Instruction::ARRAY_LENGTH, 20u, 112u), - DEF_AGET_APUT(3u, Instruction::AGET, 21u, 112u, 113u), - DEF_OTHER1(3u, Instruction::MONITOR_ENTER, 114u), - DEF_OTHER1(3u, Instruction::MONITOR_EXIT, 114u), - }; - static const bool expected_ignore_null_check[] = { - false, false, true, false /* Not doing LVN. */, - false, true /* Set before running NCE. */, - false, true, // IGET, IGET - false, true, // IPUT, IPUT - false, true, // IGET, IPUT - false, true, // IPUT, IGET - false, true, // INVOKE, IGET - false, true, // IGET, INVOKE - false, true, // AGET, APUT - false, true, // ARRAY_LENGTH, AGET - false, true, // MONITOR_ENTER, MONITOR_EXIT - }; - - PrepareIFields(ifields); - PrepareSingleBlock(); - PrepareMIRs(mirs); - - // Mark IGET 5u as null-checked to test that NCE doesn't clear this flag. - mirs_[5u].optimization_flags |= MIR_IGNORE_NULL_CHECK; - - PerformNullCheckElimination(); - ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_null_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; - } -} - -TEST_F(NullCheckEliminationTest, Diamond) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 0u, 1u, kDexMemAccessWord }, - { 2u, 1u, 0u, 2u, kDexMemAccessObject }, // int[]. - }; - static const MIRDef mirs[] = { - // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. - DEF_IGET_IPUT(3u, Instruction::IPUT, 0u, 100u, 0u), - DEF_IGET_IPUT(6u, Instruction::IGET, 1u, 100u, 1u), // Eliminated (BB #3 dominates #6). - DEF_IGET_IPUT(3u, Instruction::IGET, 2u, 101u, 0u), - DEF_IGET_IPUT(4u, Instruction::IPUT, 3u, 101u, 0u), // Eliminated (BB #3 dominates #4). - DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 102u, 0u), - DEF_IGET_IPUT(5u, Instruction::IPUT, 5u, 102u, 1u), // Eliminated (BB #3 dominates #5). - DEF_IGET_IPUT(4u, Instruction::IPUT, 6u, 103u, 0u), - DEF_IGET_IPUT(6u, Instruction::IPUT, 7u, 103u, 1u), // Not eliminated (going through BB #5). - DEF_IGET_IPUT(5u, Instruction::IGET, 8u, 104u, 1u), - DEF_IGET_IPUT(6u, Instruction::IGET, 9u, 104u, 0u), // Not eliminated (going through BB #4). - DEF_INVOKE(4u, Instruction::INVOKE_DIRECT, 105u, 0u /* dummy */), - DEF_IGET_IPUT(5u, Instruction::IGET, 11u, 105u, 1u), - DEF_IGET_IPUT(6u, Instruction::IPUT, 12u, 105u, 0u), // Eliminated. - DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 13u, 106u, 2u), - DEF_OTHER1(3u, Instruction::IF_EQZ, 13u), // Last insn in the BB #3. - DEF_OTHER2(5u, Instruction::NEW_ARRAY, 13u, 107u), - DEF_AGET_APUT(6u, Instruction::AGET, 16u, 13u, 108u), // Eliminated. - }; - static const bool expected_ignore_null_check[] = { - false, true, // BB #3 IPUT, BB #6 IGET - false, true, // BB #3 IGET, BB #4 IPUT - false, true, // BB #3 IGET, BB #5 IPUT - false, false, // BB #4 IPUT, BB #6 IPUT - false, false, // BB #5 IGET, BB #6 IGET - false, false, true, // BB #4 INVOKE, BB #5 IGET, BB #6 IPUT - false, false, // BB #3 IGET_OBJECT & IF_EQZ - false, true, // BB #5 NEW_ARRAY, BB #6 AGET - }; - - PrepareIFields(ifields); - PrepareDiamond(); - PrepareMIRs(mirs); - PerformNullCheckElimination(); - ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_null_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; - } -} - -TEST_F(NullCheckEliminationTest, Loop) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 1u, 1u, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_IGET_IPUT(3u, Instruction::IGET, 0u, 100u, 0u), - DEF_IGET_IPUT(4u, Instruction::IGET, 1u, 101u, 0u), - DEF_IGET_IPUT(5u, Instruction::IGET, 2u, 100u, 1u), // Eliminated. - DEF_IGET_IPUT(5u, Instruction::IGET, 3u, 101u, 1u), // Eliminated. - DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 102u, 0u), - DEF_IGET_IPUT(4u, Instruction::IGET, 5u, 102u, 1u), // Not eliminated (MOVE_OBJECT_16). - DEF_OTHER2(4u, Instruction::MOVE_OBJECT_16, 102u, 103u), - }; - static const bool expected_ignore_null_check[] = { - false, false, true, true, - false, false, false, - }; - - PrepareIFields(ifields); - PrepareLoop(); - PrepareMIRs(mirs); - PerformNullCheckElimination(); - ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_null_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; - } -} - -TEST_F(NullCheckEliminationTest, Catch) { - static const IFieldDef ifields[] = { - { 0u, 1u, 0u, 0u, kDexMemAccessWord }, - { 1u, 1u, 1u, 1u, kDexMemAccessWord }, - }; - static const MIRDef mirs[] = { - DEF_IGET_IPUT(3u, Instruction::IGET, 0u, 100u, 0u), // Before the exception edge. - DEF_IGET_IPUT(3u, Instruction::IGET, 1u, 101u, 0u), // Before the exception edge. - DEF_IGET_IPUT(4u, Instruction::IGET, 2u, 102u, 0u), // After the exception edge. - DEF_IGET_IPUT(4u, Instruction::IGET, 3u, 103u, 0u), // After the exception edge. - DEF_IGET_IPUT(5u, Instruction::IGET, 4u, 100u, 1u), // In catch handler; eliminated. - DEF_IGET_IPUT(5u, Instruction::IGET, 5u, 102u, 1u), // In catch handler; not eliminated. - DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 100u, 0u), // Null check eliminated. - DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 101u, 1u), // Null check eliminated. - DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 102u, 0u), // Null check eliminated. - DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 103u, 1u), // Null check not eliminated. - }; - static const bool expected_ignore_null_check[] = { - false, false, false, false, true, false, true, true, true, false - }; - - PrepareIFields(ifields); - PrepareCatch(); - PrepareMIRs(mirs); - PerformNullCheckElimination(); - ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_); - for (size_t i = 0u; i != arraysize(mirs); ++i) { - EXPECT_EQ(expected_ignore_null_check[i], - (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i; - } -} - -TEST_F(SuspendCheckEliminationTest, LoopNoElimination) { - static const MIRDef mirs[] = { - DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u, 0u), // Force the pass to run. - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge back. - }; - - PrepareLoop(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(4u, 4u)); - EXPECT_TRUE(IsSuspendCheckEdge(4u, 4u)); // Suspend point on loop to self. -} - -TEST_F(SuspendCheckEliminationTest, LoopElimination) { - static const MIRDef mirs[] = { - DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in the loop. - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge back. - }; - - PrepareLoop(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(4u, 4u)); - EXPECT_FALSE(IsSuspendCheckEdge(4u, 4u)); // No suspend point on loop to self. -} - -TEST_F(SuspendCheckEliminationTest, While_While_NoElimination) { - static const MIRDef mirs[] = { - DEF_INVOKE(3u, Instruction::INVOKE_STATIC, 0u, 0u), // Force the pass to run. - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop. - DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop. - DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head. - DEF_OTHER0(7u, Instruction::GOTO), // Edge back to outer loop head. - }; - - PrepareNestedLoopsWhile_While(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(6u, 5u)); - EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u)); - ASSERT_TRUE(IsBackEdge(7u, 4u)); - EXPECT_TRUE(IsSuspendCheckEdge(7u, 4u)); -} - -TEST_F(SuspendCheckEliminationTest, While_While_InvokeInOuterLoopHead) { - static const MIRDef mirs[] = { - DEF_INVOKE(4u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in outer loop head. - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop. - DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop. - DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head. - DEF_OTHER0(7u, Instruction::GOTO), // Edge back to outer loop head. - }; - - PrepareNestedLoopsWhile_While(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(6u, 5u)); - EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u)); - ASSERT_TRUE(IsBackEdge(7u, 4u)); - EXPECT_FALSE(IsSuspendCheckEdge(7u, 4u)); -} - -TEST_F(SuspendCheckEliminationTest, While_While_InvokeInOuterLoopBody) { - static const MIRDef mirs[] = { - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop. - DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop. - DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head. - DEF_INVOKE(7u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in outer loop body. - DEF_OTHER0(7u, Instruction::GOTO), // Edge back to outer loop head. - }; - - PrepareNestedLoopsWhile_While(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(6u, 5u)); - EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u)); - ASSERT_TRUE(IsBackEdge(7u, 4u)); - EXPECT_FALSE(IsSuspendCheckEdge(7u, 4u)); -} - -TEST_F(SuspendCheckEliminationTest, While_While_InvokeInInnerLoopHead) { - static const MIRDef mirs[] = { - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop. - DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in inner loop head. - DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop. - DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head. - DEF_OTHER0(7u, Instruction::GOTO), // Edge back to outer loop head. - }; - - PrepareNestedLoopsWhile_While(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(6u, 5u)); - EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u)); - ASSERT_TRUE(IsBackEdge(7u, 4u)); - EXPECT_FALSE(IsSuspendCheckEdge(7u, 4u)); -} - -TEST_F(SuspendCheckEliminationTest, While_While_InvokeInInnerLoopBody) { - static const MIRDef mirs[] = { - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop. - DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop. - DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in inner loop body. - DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head. - DEF_OTHER0(7u, Instruction::GOTO), // Edge back to outer loop head. - }; - - PrepareNestedLoopsWhile_While(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(6u, 5u)); - EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u)); - ASSERT_TRUE(IsBackEdge(7u, 4u)); - EXPECT_TRUE(IsSuspendCheckEdge(7u, 4u)); -} - -TEST_F(SuspendCheckEliminationTest, While_WhileWhile_InvokeInFirstInnerLoopHead) { - static const MIRDef mirs[] = { - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop. - DEF_INVOKE(5u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in first inner loop head. - DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 1. - DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head. - DEF_OTHER1(7u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 2. - DEF_OTHER0(8u, Instruction::GOTO), // Edge back to inner loop 2 head. - DEF_OTHER0(9u, Instruction::GOTO), // Edge back to outer loop head. - }; - - PrepareNestedLoopsWhile_WhileWhile(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(6u, 5u)); - EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u)); - ASSERT_TRUE(IsBackEdge(8u, 7u)); - EXPECT_TRUE(IsSuspendCheckEdge(8u, 7u)); - ASSERT_TRUE(IsBackEdge(9u, 4u)); - EXPECT_FALSE(IsSuspendCheckEdge(9u, 4u)); -} - -TEST_F(SuspendCheckEliminationTest, While_WhileWhile_InvokeInFirstInnerLoopBody) { - static const MIRDef mirs[] = { - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop. - DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 1. - DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in first inner loop body. - DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head. - DEF_OTHER1(7u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 2. - DEF_OTHER0(8u, Instruction::GOTO), // Edge back to inner loop 2 head. - DEF_OTHER0(9u, Instruction::GOTO), // Edge back to outer loop head. - }; - - PrepareNestedLoopsWhile_WhileWhile(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(6u, 5u)); - EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u)); - ASSERT_TRUE(IsBackEdge(8u, 7u)); - EXPECT_TRUE(IsSuspendCheckEdge(8u, 7u)); - ASSERT_TRUE(IsBackEdge(9u, 4u)); - EXPECT_TRUE(IsSuspendCheckEdge(9u, 4u)); -} - -TEST_F(SuspendCheckEliminationTest, While_WhileWhile_WithExtraEdge_InvokeInFirstInnerLoopBody) { - static const MIRDef mirs[] = { - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop. - DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 1. - DEF_INVOKE(6u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in first inner loop body. - DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head. - DEF_OTHER1(7u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 2. - DEF_OTHER0(8u, Instruction::GOTO), // Edge back to inner loop 2 head. - DEF_OTHER0(9u, Instruction::GOTO), // Edge back to outer loop head. - }; - - PrepareNestedLoopsWhile_WhileWhile_WithExtraEdge(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(6u, 5u)); - EXPECT_FALSE(IsSuspendCheckEdge(6u, 5u)); - ASSERT_TRUE(IsBackEdge(8u, 7u)); - EXPECT_TRUE(IsSuspendCheckEdge(8u, 7u)); // Unaffected by the extra edge. - ASSERT_TRUE(IsBackEdge(9u, 4u)); - EXPECT_TRUE(IsSuspendCheckEdge(9u, 4u)); -} - -TEST_F(SuspendCheckEliminationTest, While_WhileWhile_WithExtraEdge_InvokeInSecondInnerLoopHead) { - static const MIRDef mirs[] = { - DEF_OTHER1(4u, Instruction::IF_NEZ, 1u), // Edge out of outer loop. - DEF_OTHER1(5u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 1. - DEF_OTHER0(6u, Instruction::GOTO), // Edge back to inner loop head. - DEF_INVOKE(7u, Instruction::INVOKE_STATIC, 0u, 0u), // Invoke in second inner loop head. - DEF_OTHER1(7u, Instruction::IF_NEZ, 2u), // Edge out of inner loop 2. - DEF_OTHER0(8u, Instruction::GOTO), // Edge back to inner loop 2 head. - DEF_OTHER0(9u, Instruction::GOTO), // Edge back to outer loop head. - }; - - PrepareNestedLoopsWhile_WhileWhile_WithExtraEdge(); - PrepareMIRs(mirs); - PerformSuspendCheckElimination(); - ASSERT_TRUE(IsBackEdge(6u, 5u)); - EXPECT_TRUE(IsSuspendCheckEdge(6u, 5u)); - ASSERT_TRUE(IsBackEdge(8u, 7u)); - EXPECT_FALSE(IsSuspendCheckEdge(8u, 7u)); // Unaffected by the extra edge. - ASSERT_TRUE(IsBackEdge(9u, 4u)); - EXPECT_FALSE(IsSuspendCheckEdge(9u, 4u)); -} - -} // namespace art diff --git a/compiler/dex/pass.h b/compiler/dex/pass.h deleted file mode 100644 index 16414efada..0000000000 --- a/compiler/dex/pass.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_PASS_H_ -#define ART_COMPILER_DEX_PASS_H_ - -#include - -#include "base/logging.h" - -namespace art { - -// Forward declarations. -class BasicBlock; -class Pass; - -// Empty Pass Data Class, can be extended by any pass extending the base Pass class. -class PassDataHolder { -}; - -/** - * @class Pass - * @brief Base Pass class, can be extended to perform a more defined way of doing the work call. - */ -class Pass { - public: - explicit Pass(const char* name) - : pass_name_(name) { - } - - virtual ~Pass() { - } - - virtual const char* GetName() const { - return pass_name_; - } - - /** - * @brief Gate for the pass: determines whether to execute the pass or not considering a CompilationUnit - * @param data the PassDataHolder. - * @return whether or not to execute the pass. - */ - virtual bool Gate(const PassDataHolder* data ATTRIBUTE_UNUSED) const { - // Base class says yes. - return true; - } - - /** - * @brief Start of the pass: called before the Worker function. - */ - virtual void Start(PassDataHolder* data ATTRIBUTE_UNUSED) const { - } - - /** - * @brief End of the pass: called after the WalkBasicBlocks function. - */ - virtual void End(PassDataHolder* data ATTRIBUTE_UNUSED) const { - } - - /** - * @param data the object containing data necessary for the pass. - * @return whether or not there is a change when walking the BasicBlock - */ - virtual bool Worker(PassDataHolder* data ATTRIBUTE_UNUSED) const { - // Passes that do all their work in Start() or End() should not allow useless node iteration. - LOG(FATAL) << "Unsupported default Worker() used for " << GetName(); - UNREACHABLE(); - } - - protected: - /** @brief The pass name: used for searching for a pass when running a particular pass or debugging. */ - const char* const pass_name_; - - private: - // In order to make the all passes not copy-friendly. - DISALLOW_COPY_AND_ASSIGN(Pass); -}; -} // namespace art -#endif // ART_COMPILER_DEX_PASS_H_ diff --git a/compiler/dex/pass_driver.h b/compiler/dex/pass_driver.h deleted file mode 100644 index 34a6f630f1..0000000000 --- a/compiler/dex/pass_driver.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_PASS_DRIVER_H_ -#define ART_COMPILER_DEX_PASS_DRIVER_H_ - -#include - -#include "base/logging.h" -#include "pass.h" -#include "pass_manager.h" - -namespace art { - -class Pass; -class PassDataHolder; -class PassDriver; -class PassManager; - -// Empty holder for the constructor. -class PassDriverDataHolder { -}; - -/** - * @class PassDriver - * @brief PassDriver is the wrapper around all Pass instances in order to execute them - */ -class PassDriver { - public: - explicit PassDriver(const PassManager* const pass_manager) : pass_manager_(pass_manager) { - pass_list_ = *pass_manager_->GetDefaultPassList(); - DCHECK(!pass_list_.empty()); - } - - virtual ~PassDriver() { - } - - /** - * @brief Insert a Pass: can warn if multiple passes have the same name. - */ - void InsertPass(const Pass* new_pass) { - DCHECK(new_pass != nullptr); - DCHECK(new_pass->GetName() != nullptr); - DCHECK_NE(new_pass->GetName()[0], 0); - - // It is an error to override an existing pass. - DCHECK(GetPass(new_pass->GetName()) == nullptr) - << "Pass name " << new_pass->GetName() << " already used."; - // Now add to the list. - pass_list_.push_back(new_pass); - } - - /** - * @brief Run a pass using the name as key. - * @return whether the pass was applied. - */ - virtual bool RunPass(const char* pass_name) { - // Paranoid: c_unit cannot be null and we need a pass name. - DCHECK(pass_name != nullptr); - DCHECK_NE(pass_name[0], 0); - - const Pass* cur_pass = GetPass(pass_name); - - if (cur_pass != nullptr) { - return RunPass(cur_pass); - } - - // Return false, we did not find the pass. - return false; - } - - /** - * @brief Runs all the passes with the pass_list_. - */ - void Launch() { - for (const Pass* cur_pass : pass_list_) { - RunPass(cur_pass); - } - } - - /** - * @brief Searches for a particular pass. - * @param the name of the pass to be searched for. - */ - const Pass* GetPass(const char* name) const { - for (const Pass* cur_pass : pass_list_) { - if (strcmp(name, cur_pass->GetName()) == 0) { - return cur_pass; - } - } - return nullptr; - } - - /** - * @brief Run a pass using the Pass itself. - * @param time_split do we want a time split request(default: false)? - * @return whether the pass was applied. - */ - virtual bool RunPass(const Pass* pass, bool time_split = false) = 0; - - protected: - /** - * @brief Apply a patch: perform start/work/end functions. - */ - virtual void ApplyPass(PassDataHolder* data, const Pass* pass) { - pass->Start(data); - DispatchPass(pass); - pass->End(data); - } - - /** - * @brief Dispatch a patch. - * Gives the ability to add logic when running the patch. - */ - virtual void DispatchPass(const Pass* pass ATTRIBUTE_UNUSED) { - } - - /** @brief List of passes: provides the order to execute the passes. - * Passes are owned by pass_manager_. */ - std::vector pass_list_; - - const PassManager* const pass_manager_; -}; - -} // namespace art -#endif // ART_COMPILER_DEX_PASS_DRIVER_H_ diff --git a/compiler/dex/pass_driver_me.h b/compiler/dex/pass_driver_me.h deleted file mode 100644 index d0af71c061..0000000000 --- a/compiler/dex/pass_driver_me.h +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_PASS_DRIVER_ME_H_ -#define ART_COMPILER_DEX_PASS_DRIVER_ME_H_ - -#include -#include - -#include "bb_optimizations.h" -#include "dataflow_iterator.h" -#include "dataflow_iterator-inl.h" -#include "dex_flags.h" -#include "pass_driver.h" -#include "pass_manager.h" -#include "pass_me.h" -#include "safe_map.h" - -namespace art { - -class PassManager; -class PassManagerOptions; - -class PassDriverME: public PassDriver { - public: - PassDriverME(const PassManager* const pass_manager, CompilationUnit* cu) - : PassDriver(pass_manager), pass_me_data_holder_(), dump_cfg_folder_("/sdcard/") { - pass_me_data_holder_.bb = nullptr; - pass_me_data_holder_.c_unit = cu; - } - - ~PassDriverME() { - } - - void DispatchPass(const Pass* pass) { - VLOG(compiler) << "Dispatching " << pass->GetName(); - const PassME* me_pass = down_cast(pass); - - DataFlowAnalysisMode mode = me_pass->GetTraversal(); - - switch (mode) { - case kPreOrderDFSTraversal: - DoWalkBasicBlocks(&pass_me_data_holder_, me_pass); - break; - case kRepeatingPreOrderDFSTraversal: - DoWalkBasicBlocks(&pass_me_data_holder_, me_pass); - break; - case kRepeatingPostOrderDFSTraversal: - DoWalkBasicBlocks(&pass_me_data_holder_, me_pass); - break; - case kReversePostOrderDFSTraversal: - DoWalkBasicBlocks(&pass_me_data_holder_, me_pass); - break; - case kRepeatingReversePostOrderDFSTraversal: - DoWalkBasicBlocks(&pass_me_data_holder_, me_pass); - break; - case kPostOrderDOMTraversal: - DoWalkBasicBlocks(&pass_me_data_holder_, me_pass); - break; - case kTopologicalSortTraversal: - DoWalkBasicBlocks(&pass_me_data_holder_, me_pass); - break; - case kLoopRepeatingTopologicalSortTraversal: - DoWalkBasicBlocks(&pass_me_data_holder_, me_pass); - break; - case kAllNodes: - DoWalkBasicBlocks(&pass_me_data_holder_, me_pass); - break; - case kNoNodes: - break; - default: - LOG(FATAL) << "Iterator mode not handled in dispatcher: " << mode; - break; - } - } - - bool RunPass(const Pass* pass, bool time_split) OVERRIDE { - // Paranoid: c_unit and pass cannot be null, and the pass should have a name. - DCHECK(pass != nullptr); - DCHECK(pass->GetName() != nullptr && pass->GetName()[0] != 0); - CompilationUnit* c_unit = pass_me_data_holder_.c_unit; - DCHECK(c_unit != nullptr); - - // Do we perform a time split - if (time_split) { - c_unit->NewTimingSplit(pass->GetName()); - } - - // First, work on determining pass verbosity. - bool old_print_pass = c_unit->print_pass; - c_unit->print_pass = pass_manager_->GetOptions().GetPrintAllPasses(); - auto* const options = &pass_manager_->GetOptions(); - const std::string& print_pass_list = options->GetPrintPassList(); - if (!print_pass_list.empty() && strstr(print_pass_list.c_str(), pass->GetName()) != nullptr) { - c_unit->print_pass = true; - } - - // Next, check if there are any overridden settings for the pass that change default - // configuration. - c_unit->overridden_pass_options.clear(); - FillOverriddenPassSettings(options, pass->GetName(), c_unit->overridden_pass_options); - if (c_unit->print_pass) { - for (auto setting_it : c_unit->overridden_pass_options) { - LOG(INFO) << "Overridden option \"" << setting_it.first << ":" - << setting_it.second << "\" for pass \"" << pass->GetName() << "\""; - } - } - - // Check the pass gate first. - bool should_apply_pass = pass->Gate(&pass_me_data_holder_); - if (should_apply_pass) { - // Applying the pass: first start, doWork, and end calls. - this->ApplyPass(&pass_me_data_holder_, pass); - - bool should_dump = (c_unit->enable_debug & (1 << kDebugDumpCFG)) != 0; - - const std::string& dump_pass_list = pass_manager_->GetOptions().GetDumpPassList(); - if (!dump_pass_list.empty()) { - const bool found = strstr(dump_pass_list.c_str(), pass->GetName()); - should_dump = should_dump || found; - } - - if (should_dump) { - // Do we want to log it? - if ((c_unit->enable_debug& (1 << kDebugDumpCFG)) != 0) { - // Do we have a pass folder? - const PassME* me_pass = (down_cast(pass)); - const char* passFolder = me_pass->GetDumpCFGFolder(); - DCHECK(passFolder != nullptr); - - if (passFolder[0] != 0) { - // Create directory prefix. - std::string prefix = GetDumpCFGFolder(); - prefix += passFolder; - prefix += "/"; - - c_unit->mir_graph->DumpCFG(prefix.c_str(), false); - } - } - } - } - - // Before wrapping up with this pass, restore old pass verbosity flag. - c_unit->print_pass = old_print_pass; - - // If the pass gate passed, we can declare success. - return should_apply_pass; - } - - static void PrintPassOptions(PassManager* manager) { - for (const auto* pass : *manager->GetDefaultPassList()) { - const PassME* me_pass = down_cast(pass); - if (me_pass->HasOptions()) { - LOG(INFO) << "Pass options for \"" << me_pass->GetName() << "\" are:"; - SafeMap overridden_settings; - FillOverriddenPassSettings(&manager->GetOptions(), me_pass->GetName(), - overridden_settings); - me_pass->PrintPassOptions(overridden_settings); - } - } - } - - const char* GetDumpCFGFolder() const { - return dump_cfg_folder_; - } - - protected: - /** @brief The data holder that contains data needed for the PassDriverME. */ - PassMEDataHolder pass_me_data_holder_; - - /** @brief Dump CFG base folder: where is the base folder for dumping CFGs. */ - const char* dump_cfg_folder_; - - static void DoWalkBasicBlocks(PassMEDataHolder* data, const PassME* pass, - DataflowIterator* iterator) { - // Paranoid: Check the iterator before walking the BasicBlocks. - DCHECK(iterator != nullptr); - bool change = false; - for (BasicBlock* bb = iterator->Next(change); bb != nullptr; bb = iterator->Next(change)) { - data->bb = bb; - change = pass->Worker(data); - } - } - - template - inline static void DoWalkBasicBlocks(PassMEDataHolder* data, const PassME* pass) { - DCHECK(data != nullptr); - CompilationUnit* c_unit = data->c_unit; - DCHECK(c_unit != nullptr); - Iterator iterator(c_unit->mir_graph.get()); - DoWalkBasicBlocks(data, pass, &iterator); - } - - /** - * @brief Fills the settings_to_fill by finding all of the applicable options in the - * overridden_pass_options_list_. - * @param pass_name The pass name for which to fill settings. - * @param settings_to_fill Fills the options to contain the mapping of name of option to the new - * configuration. - */ - static void FillOverriddenPassSettings( - const PassManagerOptions* options, const char* pass_name, - SafeMap& settings_to_fill) { - const std::string& settings = options->GetOverriddenPassOptions(); - const size_t settings_len = settings.size(); - - // Before anything, check if we care about anything right now. - if (settings_len == 0) { - return; - } - - const size_t pass_name_len = strlen(pass_name); - const size_t min_setting_size = 4; // 2 delimiters, 1 setting name, 1 setting - size_t search_pos = 0; - - // If there is no room for pass options, exit early. - if (settings_len < pass_name_len + min_setting_size) { - return; - } - - do { - search_pos = settings.find(pass_name, search_pos); - - // Check if we found this pass name in rest of string. - if (search_pos == std::string::npos) { - // No more settings for this pass. - break; - } - - // The string contains the pass name. Now check that there is - // room for the settings: at least one char for setting name, - // two chars for two delimiter, and at least one char for setting. - if (search_pos + pass_name_len + min_setting_size >= settings_len) { - // No more settings for this pass. - break; - } - - // Update the current search position to not include the pass name. - search_pos += pass_name_len; - - // The format must be "PassName:SettingName:#" where # is the setting. - // Thus look for the first ":" which must exist. - if (settings[search_pos] != ':') { - // Missing delimiter right after pass name. - continue; - } else { - search_pos += 1; - } - - // Now look for the actual setting by finding the next ":" delimiter. - const size_t setting_name_pos = search_pos; - size_t setting_pos = settings.find(':', setting_name_pos); - - if (setting_pos == std::string::npos) { - // Missing a delimiter that would capture where setting starts. - continue; - } else if (setting_pos == setting_name_pos) { - // Missing setting thus did not move from setting name - continue; - } else { - // Skip the delimiter. - setting_pos += 1; - } - - // Look for the terminating delimiter which must be a comma. - size_t next_configuration_separator = settings.find(',', setting_pos); - if (next_configuration_separator == std::string::npos) { - next_configuration_separator = settings_len; - } - - // Prevent end of string errors. - if (next_configuration_separator == setting_pos) { - continue; - } - - // Get the actual setting itself. - std::string setting_string = - settings.substr(setting_pos, next_configuration_separator - setting_pos); - - std::string setting_name = - settings.substr(setting_name_pos, setting_pos - setting_name_pos - 1); - - // We attempt to convert the option value to integer. Strtoll is being used to - // convert because it is exception safe. - char* end_ptr = nullptr; - const char* setting_ptr = setting_string.c_str(); - DCHECK(setting_ptr != nullptr); // Paranoid: setting_ptr must be a valid pointer. - int64_t int_value = strtoll(setting_ptr, &end_ptr, 0); - DCHECK(end_ptr != nullptr); // Paranoid: end_ptr must be set by the strtoll call. - - // If strtoll call succeeded, the option is now considered as integer. - if (*setting_ptr != '\0' && end_ptr != setting_ptr && *end_ptr == '\0') { - settings_to_fill.Put(setting_name, OptionContent(int_value)); - } else { - // Otherwise, it is considered as a string. - settings_to_fill.Put(setting_name, OptionContent(setting_string.c_str())); - } - search_pos = next_configuration_separator; - } while (true); - } -}; -} // namespace art -#endif // ART_COMPILER_DEX_PASS_DRIVER_ME_H_ diff --git a/compiler/dex/pass_driver_me_opts.cc b/compiler/dex/pass_driver_me_opts.cc deleted file mode 100644 index 375003bf1f..0000000000 --- a/compiler/dex/pass_driver_me_opts.cc +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "pass_driver_me_opts.h" - -#include "base/logging.h" -#include "base/macros.h" -#include "bb_optimizations.h" -#include "dataflow_iterator.h" -#include "dataflow_iterator-inl.h" -#include "pass_driver_me_opts.h" -#include "pass_manager.h" -#include "post_opt_passes.h" - -namespace art { - -void PassDriverMEOpts::SetupPasses(PassManager* pass_manager) { - /* - * Create the pass list. These passes are immutable and are shared across the threads. - * - * Advantage is that there will be no race conditions here. - * Disadvantage is the passes can't change their internal states depending on CompilationUnit: - * - This is not yet an issue: no current pass would require it. - */ - pass_manager->AddPass(new StringChange); - pass_manager->AddPass(new CacheFieldLoweringInfo); - pass_manager->AddPass(new CacheMethodLoweringInfo); - pass_manager->AddPass(new CalculatePredecessors); - pass_manager->AddPass(new DFSOrders); - pass_manager->AddPass(new ClassInitCheckElimination); - pass_manager->AddPass(new SpecialMethodInliner); - pass_manager->AddPass(new NullCheckElimination); - pass_manager->AddPass(new BBCombine); - pass_manager->AddPass(new CodeLayout); - pass_manager->AddPass(new GlobalValueNumberingPass); - pass_manager->AddPass(new DeadCodeEliminationPass); - pass_manager->AddPass(new GlobalValueNumberingCleanupPass); - pass_manager->AddPass(new ConstantPropagation); - pass_manager->AddPass(new MethodUseCount); - pass_manager->AddPass(new BBOptimizations); - pass_manager->AddPass(new SuspendCheckElimination); -} - -void PassDriverMEOpts::ApplyPass(PassDataHolder* data, const Pass* pass) { - const PassME* const pass_me = down_cast(pass); - DCHECK(pass_me != nullptr); - PassMEDataHolder* const pass_me_data_holder = down_cast(data); - // Set to dirty. - pass_me_data_holder->dirty = true; - // First call the base class' version. - PassDriver::ApplyPass(data, pass); - // Now we care about flags. - if ((pass_me->GetFlag(kOptimizationBasicBlockChange) == true) || - (pass_me->GetFlag(kOptimizationDefUsesChange) == true)) { - // Is it dirty at least? - if (pass_me_data_holder->dirty == true) { - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - c_unit->mir_graph.get()->CalculateBasicBlockInformation(post_opt_pass_manager_); - } - } -} - -} // namespace art diff --git a/compiler/dex/pass_driver_me_opts.h b/compiler/dex/pass_driver_me_opts.h deleted file mode 100644 index c8093d0a02..0000000000 --- a/compiler/dex/pass_driver_me_opts.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_PASS_DRIVER_ME_OPTS_H_ -#define ART_COMPILER_DEX_PASS_DRIVER_ME_OPTS_H_ - -#include "pass_driver_me.h" - -namespace art { - -// Forward Declarations. -struct CompilationUnit; -class Pass; -class PassDataHolder; -class PassManager; - -class PassDriverMEOpts : public PassDriverME { - public: - PassDriverMEOpts(const PassManager* const manager, - const PassManager* const post_opt_pass_manager, - CompilationUnit* cu) - : PassDriverME(manager, cu), post_opt_pass_manager_(post_opt_pass_manager) { - } - - ~PassDriverMEOpts() { - } - - /** - * @brief Write and allocate corresponding passes into the pass manager. - */ - static void SetupPasses(PassManager* pass_manasger); - - /** - * @brief Apply a patch: perform start/work/end functions. - */ - virtual void ApplyPass(PassDataHolder* data, const Pass* pass) OVERRIDE; - - const PassManager* const post_opt_pass_manager_; -}; - -} // namespace art -#endif // ART_COMPILER_DEX_PASS_DRIVER_ME_OPTS_H_ diff --git a/compiler/dex/pass_driver_me_post_opt.cc b/compiler/dex/pass_driver_me_post_opt.cc deleted file mode 100644 index b35bc3d7d3..0000000000 --- a/compiler/dex/pass_driver_me_post_opt.cc +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "pass_driver_me_post_opt.h" - -#include "base/macros.h" -#include "post_opt_passes.h" -#include "pass_manager.h" - -namespace art { - -void PassDriverMEPostOpt::SetupPasses(PassManager* pass_manager) { - /* - * Create the pass list. These passes are immutable and are shared across the threads. - * - * Advantage is that there will be no race conditions here. - * Disadvantage is the passes can't change their internal states depending on CompilationUnit: - * - This is not yet an issue: no current pass would require it. - */ - // The initial list of passes to be used by the PassDriveMEPostOpt. - pass_manager->AddPass(new DFSOrders); - pass_manager->AddPass(new BuildDomination); - pass_manager->AddPass(new TopologicalSortOrders); - pass_manager->AddPass(new InitializeSSATransformation); - pass_manager->AddPass(new ClearPhiInstructions); - pass_manager->AddPass(new DefBlockMatrix); - pass_manager->AddPass(new FindPhiNodeBlocksPass); - pass_manager->AddPass(new SSAConversion); - pass_manager->AddPass(new PhiNodeOperands); - pass_manager->AddPass(new PerformInitRegLocations); - pass_manager->AddPass(new TypeInferencePass); - pass_manager->AddPass(new FinishSSATransformation); -} - -} // namespace art diff --git a/compiler/dex/pass_driver_me_post_opt.h b/compiler/dex/pass_driver_me_post_opt.h deleted file mode 100644 index 94176dbf0f..0000000000 --- a/compiler/dex/pass_driver_me_post_opt.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_PASS_DRIVER_ME_POST_OPT_H_ -#define ART_COMPILER_DEX_PASS_DRIVER_ME_POST_OPT_H_ - -#include "pass_driver_me.h" - -namespace art { - -// Forward Declarations. -struct CompilationUnit; -class Pass; -class PassDataHolder; - -class PassDriverMEPostOpt : public PassDriverME { - public: - PassDriverMEPostOpt(const PassManager* const manager, CompilationUnit* cu) - : PassDriverME(manager, cu) { - } - - ~PassDriverMEPostOpt() { - } - - /** - * @brief Write and allocate corresponding passes into the pass manager. - */ - static void SetupPasses(PassManager* pass_manager); -}; - -} // namespace art -#endif // ART_COMPILER_DEX_PASS_DRIVER_ME_POST_OPT_H_ diff --git a/compiler/dex/pass_manager.cc b/compiler/dex/pass_manager.cc deleted file mode 100644 index 6377a6c07a..0000000000 --- a/compiler/dex/pass_manager.cc +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "pass_manager.h" - -#include "base/stl_util.h" -#include "pass_me.h" - -namespace art { - -PassManager::PassManager(const PassManagerOptions& options) : options_(options) { -} - -PassManager::~PassManager() { - STLDeleteElements(&passes_); -} - -void PassManager::CreateDefaultPassList() { - default_pass_list_.clear(); - // Add each pass which isn't disabled into default_pass_list_. - for (const auto* pass : passes_) { - if (options_.GetDisablePassList().find(pass->GetName()) != std::string::npos) { - VLOG(compiler) << "Skipping disabled pass " << pass->GetName(); - } else { - default_pass_list_.push_back(pass); - } - } -} - -void PassManager::PrintPassNames() const { - LOG(INFO) << "Loop Passes are:"; - for (const Pass* cur_pass : default_pass_list_) { - LOG(INFO) << "\t-" << cur_pass->GetName(); - } -} - -} // namespace art diff --git a/compiler/dex/pass_manager.h b/compiler/dex/pass_manager.h deleted file mode 100644 index 68e488d128..0000000000 --- a/compiler/dex/pass_manager.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_PASS_MANAGER_H_ -#define ART_COMPILER_DEX_PASS_MANAGER_H_ - -#include -#include - -#include "base/logging.h" - -namespace art { - -class Pass; - -class PassManagerOptions { - public: - PassManagerOptions() - : default_print_passes_(false), - print_pass_names_(false), - print_pass_options_(false) { - } - explicit PassManagerOptions(const PassManagerOptions&) = default; - - void SetPrintPassNames(bool b) { - print_pass_names_ = b; - } - - void SetPrintAllPasses() { - default_print_passes_ = true; - } - bool GetPrintAllPasses() const { - return default_print_passes_; - } - - void SetDisablePassList(const std::string& list) { - disable_pass_list_ = list; - } - const std::string& GetDisablePassList() const { - return disable_pass_list_; - } - - void SetPrintPassList(const std::string& list) { - print_pass_list_ = list; - } - const std::string& GetPrintPassList() const { - return print_pass_list_; - } - - void SetDumpPassList(const std::string& list) { - dump_pass_list_ = list; - } - const std::string& GetDumpPassList() const { - return dump_pass_list_; - } - - /** - * @brief Used to set a string that contains the overridden pass options. - * @details An overridden pass option means that the pass uses this option - * instead of using its default option. - * @param s The string passed by user with overridden options. The string is in format - * Pass1Name:Pass1Option:Pass1Setting,Pass2Name:Pass2Option::Pass2Setting - */ - void SetOverriddenPassOptions(const std::string& list) { - overridden_pass_options_list_ = list; - } - const std::string& GetOverriddenPassOptions() const { - return overridden_pass_options_list_; - } - - void SetPrintPassOptions(bool b) { - print_pass_options_ = b; - } - bool GetPrintPassOptions() const { - return print_pass_options_; - } - - private: - /** @brief Do we, by default, want to be printing the log messages? */ - bool default_print_passes_; - - /** @brief What are the passes we want to be printing the log messages? */ - std::string print_pass_list_; - - /** @brief What are the passes we want to be dumping the CFG? */ - std::string dump_pass_list_; - - /** @brief String of all options that should be overridden for selected passes */ - std::string overridden_pass_options_list_; - - /** @brief String of all options that should be overridden for selected passes */ - std::string disable_pass_list_; - - /** @brief Whether or not we print all the passes when we create the pass manager */ - bool print_pass_names_; - - /** @brief Whether or not we print all the pass options when we create the pass manager */ - bool print_pass_options_; -}; - -/** - * @class PassManager - * @brief Owns passes - */ -class PassManager { - public: - explicit PassManager(const PassManagerOptions& options); - virtual ~PassManager(); - void CreateDefaultPassList(); - void AddPass(const Pass* pass) { - passes_.push_back(pass); - } - /** - * @brief Print the pass names of all the passes available. - */ - void PrintPassNames() const; - const std::vector* GetDefaultPassList() const { - return &default_pass_list_; - } - const PassManagerOptions& GetOptions() const { - return options_; - } - - private: - /** @brief The set of possible passes. */ - std::vector passes_; - - /** @brief The default pass list is used to initialize pass_list_. */ - std::vector default_pass_list_; - - /** @brief Pass manager options. */ - PassManagerOptions options_; - - DISALLOW_COPY_AND_ASSIGN(PassManager); -}; -} // namespace art -#endif // ART_COMPILER_DEX_PASS_MANAGER_H_ diff --git a/compiler/dex/pass_me.h b/compiler/dex/pass_me.h deleted file mode 100644 index d3cf393368..0000000000 --- a/compiler/dex/pass_me.h +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_PASS_ME_H_ -#define ART_COMPILER_DEX_PASS_ME_H_ - -#include - -#include "base/logging.h" -#include "pass.h" -#include "compiler_ir.h" -#include "safe_map.h" - -namespace art { - -// Forward declarations. -class BasicBlock; -struct CompilationUnit; - -/** - * @brief OptimizationFlag is an enumeration to perform certain tasks for a given pass. - * @details Each enum should be a power of 2 to be correctly used. - */ -enum OptimizationFlag { - kOptimizationBasicBlockChange = 1, /// @brief Has there been a change to a BasicBlock? - kOptimizationDefUsesChange = 2, /// @brief Has there been a change to a def-use? - kLoopStructureChange = 4, /// @brief Has there been a loop structural change? -}; -std::ostream& operator<<(std::ostream& os, const OptimizationFlag& rhs); - -// Data holder class. -class PassMEDataHolder: public PassDataHolder { - public: - CompilationUnit* c_unit; - BasicBlock* bb; - void* data; /**< @brief Any data the pass wants to use */ - bool dirty; /**< @brief Has the pass rendered the CFG dirty, requiring post-opt? */ -}; - -enum DataFlowAnalysisMode { - kAllNodes = 0, /// @brief All nodes. - kPreOrderDFSTraversal, /// @brief Depth-First-Search / Pre-Order. - kRepeatingPreOrderDFSTraversal, /// @brief Depth-First-Search / Repeating Pre-Order. - kReversePostOrderDFSTraversal, /// @brief Depth-First-Search / Reverse Post-Order. - kRepeatingPostOrderDFSTraversal, /// @brief Depth-First-Search / Repeating Post-Order. - kRepeatingReversePostOrderDFSTraversal, /// @brief Depth-First-Search / Repeating Reverse Post-Order. - kPostOrderDOMTraversal, /// @brief Dominator tree / Post-Order. - kTopologicalSortTraversal, /// @brief Topological Order traversal. - kLoopRepeatingTopologicalSortTraversal, /// @brief Loop-repeating Topological Order traversal. - kNoNodes, /// @brief Skip BasicBlock traversal. -}; -std::ostream& operator<<(std::ostream& os, const DataFlowAnalysisMode& rhs); - -/** - * @class Pass - * @brief Pass is the Pass structure for the optimizations. - * @details The following structure has the different optimization passes that we are going to do. - */ -class PassME : public Pass { - public: - explicit PassME(const char* name, DataFlowAnalysisMode type = kAllNodes, - unsigned int flags = 0u, const char* dump = "") - : Pass(name), traversal_type_(type), flags_(flags), dump_cfg_folder_(dump) { - } - - PassME(const char* name, DataFlowAnalysisMode type, const char* dump) - : Pass(name), traversal_type_(type), flags_(0), dump_cfg_folder_(dump) { - } - - PassME(const char* name, const char* dump) - : Pass(name), traversal_type_(kAllNodes), flags_(0), dump_cfg_folder_(dump) { - } - - ~PassME() { - default_options_.clear(); - } - - virtual DataFlowAnalysisMode GetTraversal() const { - return traversal_type_; - } - - /** - * @return Returns whether the pass has any configurable options. - */ - bool HasOptions() const { - return default_options_.size() != 0; - } - - /** - * @brief Prints the pass options along with default settings if there are any. - * @details The printing is done using LOG(INFO). - */ - void PrintPassDefaultOptions() const { - for (const auto& option : default_options_) { - LOG(INFO) << "\t" << option.first << ":" << option.second; - } - } - - /** - * @brief Prints the pass options along with either default or overridden setting. - * @param overridden_options The overridden settings for this pass. - */ - void PrintPassOptions(SafeMap& overridden_options) const { - // We walk through the default options only to get the pass names. We use GetPassOption to - // also consider the overridden ones. - for (const auto& option : default_options_) { - LOG(INFO) << "\t" << option.first << ":" - << GetPassOption(option.first, overridden_options); - } - } - - /** - * @brief Used to obtain the option structure for a pass. - * @details Will return the overridden option if it exists or default one otherwise. - * @param option_name The name of option whose setting to look for. - * @param c_unit The compilation unit currently being handled. - * @return Returns the option structure containing the option value. - */ - const OptionContent& GetPassOption(const char* option_name, CompilationUnit* c_unit) const { - return GetPassOption(option_name, c_unit->overridden_pass_options); - } - - /** - * @brief Used to obtain the option for a pass as a string. - * @details Will return the overridden option if it exists or default one otherwise. - * It will return nullptr if the required option value is not a string. - * @param option_name The name of option whose setting to look for. - * @param c_unit The compilation unit currently being handled. - * @return Returns the overridden option if it exists or the default one otherwise. - */ - const char* GetStringPassOption(const char* option_name, CompilationUnit* c_unit) const { - return GetStringPassOption(option_name, c_unit->overridden_pass_options); - } - - /** - * @brief Used to obtain the pass option value as an integer. - * @details Will return the overridden option if it exists or default one otherwise. - * It will return 0 if the required option value is not an integer. - * @param c_unit The compilation unit currently being handled. - * @return Returns the overriden option if it exists or the default one otherwise. - */ - int64_t GetIntegerPassOption(const char* option_name, CompilationUnit* c_unit) const { - return GetIntegerPassOption(option_name, c_unit->overridden_pass_options); - } - - const char* GetDumpCFGFolder() const { - return dump_cfg_folder_; - } - - bool GetFlag(OptimizationFlag flag) const { - return (flags_ & flag); - } - - protected: - const OptionContent& GetPassOption(const char* option_name, - const SafeMap& overridden_options) const { - DCHECK(option_name != nullptr); - - // First check if there are any overridden settings. - auto overridden_it = overridden_options.find(std::string(option_name)); - if (overridden_it != overridden_options.end()) { - return overridden_it->second; - } else { - // Otherwise, there must be a default value for this option name. - auto default_it = default_options_.find(option_name); - // An invalid option is being requested. - if (default_it == default_options_.end()) { - LOG(FATAL) << "Fatal: Cannot find an option named \"" << option_name << "\""; - } - - return default_it->second; - } - } - - const char* GetStringPassOption(const char* option_name, - const SafeMap& overridden_options) const { - const OptionContent& option_content = GetPassOption(option_name, overridden_options); - if (option_content.type != OptionContent::kString) { - return nullptr; - } - - return option_content.GetString(); - } - - int64_t GetIntegerPassOption(const char* option_name, - const SafeMap& overridden_options) const { - const OptionContent& option_content = GetPassOption(option_name, overridden_options); - if (option_content.type != OptionContent::kInteger) { - return 0; - } - - return option_content.GetInteger(); - } - - /** @brief Type of traversal: determines the order to execute the pass on the BasicBlocks. */ - const DataFlowAnalysisMode traversal_type_; - - /** @brief Flags for additional directives: used to determine if a particular - * post-optimization pass is necessary. */ - const unsigned int flags_; - - /** @brief CFG Dump Folder: what sub-folder to use for dumping the CFGs post pass. */ - const char* const dump_cfg_folder_; - - /** - * @brief Contains a map of options with the default settings. - * @details The constructor of the specific pass instance should fill this - * with default options. - * */ - SafeMap default_options_; -}; -} // namespace art -#endif // ART_COMPILER_DEX_PASS_ME_H_ diff --git a/compiler/dex/post_opt_passes.cc b/compiler/dex/post_opt_passes.cc deleted file mode 100644 index 9262440ae2..0000000000 --- a/compiler/dex/post_opt_passes.cc +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "post_opt_passes.h" - -#include "dataflow_iterator-inl.h" - -namespace art { - -bool ClearPhiInstructions::Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - MIR* mir = bb->first_mir_insn; - - while (mir != nullptr) { - MIR* next = mir->next; - - Instruction::Code opcode = mir->dalvikInsn.opcode; - - if (opcode == static_cast (kMirOpPhi)) { - bb->RemoveMIR(mir); - } - - mir = next; - } - - // We do not care in reporting a change or not in the MIR. - return false; -} - -void CalculatePredecessors::Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - // First get the MIRGraph here to factorize a bit the code. - MIRGraph *mir_graph = c_unit->mir_graph.get(); - - // First clear all predecessors. - AllNodesIterator first(mir_graph); - for (BasicBlock* bb = first.Next(); bb != nullptr; bb = first.Next()) { - bb->predecessors.clear(); - } - - // Now calculate all predecessors. - AllNodesIterator second(mir_graph); - for (BasicBlock* bb = second.Next(); bb != nullptr; bb = second.Next()) { - // We only care about non hidden blocks. - if (bb->hidden == true) { - continue; - } - - // Create iterator for visiting children. - ChildBlockIterator child_iter(bb, mir_graph); - - // Now iterate through the children to set the predecessor bits. - for (BasicBlock* child = child_iter.Next(); child != nullptr; child = child_iter.Next()) { - child->predecessors.push_back(bb->id); - } - } -} - -} // namespace art diff --git a/compiler/dex/post_opt_passes.h b/compiler/dex/post_opt_passes.h deleted file mode 100644 index e9fa0eb578..0000000000 --- a/compiler/dex/post_opt_passes.h +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_POST_OPT_PASSES_H_ -#define ART_COMPILER_DEX_POST_OPT_PASSES_H_ - -#include "base/casts.h" -#include "base/logging.h" -#include "compiler_ir.h" -#include "dex_flags.h" -#include "mir_graph.h" -#include "pass_me.h" - -namespace art { - -/** - * @class PassMEMirSsaRep - * @brief Convenience class for passes that check MIRGraph::MirSsaRepUpToDate(). - */ -class PassMEMirSsaRep : public PassME { - public: - PassMEMirSsaRep(const char* name, DataFlowAnalysisMode type = kAllNodes) - : PassME(name, type) { - } - - bool Gate(const PassDataHolder* data) const OVERRIDE { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return !c_unit->mir_graph->MirSsaRepUpToDate(); - } -}; - -/** - * @class InitializeSSATransformation - * @brief There is some data that needs to be initialized before performing - * the post optimization passes. - */ -class InitializeSSATransformation : public PassMEMirSsaRep { - public: - InitializeSSATransformation() : PassMEMirSsaRep("InitializeSSATransformation", kNoNodes) { - } - - void Start(PassDataHolder* data) const { - // New blocks may have been inserted so the first thing we do is ensure that - // the c_unit's number of blocks matches the actual count of basic blocks. - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->SSATransformationStart(); - c_unit->mir_graph->CompilerInitializeSSAConversion(); - } -}; - -/** - * @class ClearPhiInformation - * @brief Clear the PHI nodes from the CFG. - */ -class ClearPhiInstructions : public PassMEMirSsaRep { - public: - ClearPhiInstructions() : PassMEMirSsaRep("ClearPhiInstructions") { - } - - bool Worker(PassDataHolder* data) const; -}; - -/** - * @class CalculatePredecessors - * @brief Calculate the predecessor BitVector of each Basicblock. - */ -class CalculatePredecessors : public PassME { - public: - CalculatePredecessors() : PassME("CalculatePredecessors", kNoNodes) { - } - - void Start(PassDataHolder* data) const; -}; - -/** - * @class DFSOrders - * @brief Compute the DFS order of the MIR graph - */ -class DFSOrders : public PassME { - public: - DFSOrders() : PassME("DFSOrders", kNoNodes) { - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return !c_unit->mir_graph->DfsOrdersUpToDate(); - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph.get()->ComputeDFSOrders(); - } -}; - -/** - * @class BuildDomination - * @brief Build the domination information of the MIR Graph - */ -class BuildDomination : public PassME { - public: - BuildDomination() : PassME("BuildDomination", kNoNodes) { - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return !c_unit->mir_graph->DominationUpToDate(); - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->ComputeDominators(); - } - - void End(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - // Verify the dataflow information after the pass. - if (c_unit->enable_debug & (1 << kDebugVerifyDataflow)) { - c_unit->mir_graph->VerifyDataflow(); - } - } -}; - -/** - * @class TopologicalSortOrders - * @brief Compute the topological sort order of the MIR graph - */ -class TopologicalSortOrders : public PassME { - public: - TopologicalSortOrders() : PassME("TopologicalSortOrders", kNoNodes) { - } - - bool Gate(const PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - return !c_unit->mir_graph->TopologicalOrderUpToDate(); - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph.get()->ComputeTopologicalSortOrder(); - } -}; - -/** - * @class DefBlockMatrix - * @brief Calculate the matrix of definition per basic block - */ -class DefBlockMatrix : public PassMEMirSsaRep { - public: - DefBlockMatrix() : PassMEMirSsaRep("DefBlockMatrix", kNoNodes) { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph.get()->ComputeDefBlockMatrix(); - } -}; - -/** - * @class FindPhiNodeBlocksPass - * @brief Pass to find out where we need to insert the phi nodes for the SSA conversion. - */ -class FindPhiNodeBlocksPass : public PassMEMirSsaRep { - public: - FindPhiNodeBlocksPass() : PassMEMirSsaRep("FindPhiNodeBlocks", kNoNodes) { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph.get()->FindPhiNodeBlocks(); - } -}; - -/** - * @class SSAConversion - * @brief Pass for SSA conversion of MIRs - */ -class SSAConversion : public PassMEMirSsaRep { - public: - SSAConversion() : PassMEMirSsaRep("SSAConversion", kNoNodes) { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - MIRGraph *mir_graph = c_unit->mir_graph.get(); - mir_graph->ClearAllVisitedFlags(); - mir_graph->DoDFSPreOrderSSARename(mir_graph->GetEntryBlock()); - } -}; - -/** - * @class PhiNodeOperands - * @brief Pass to insert the Phi node operands to basic blocks - */ -class PhiNodeOperands : public PassMEMirSsaRep { - public: - PhiNodeOperands() : PassMEMirSsaRep("PhiNodeOperands", kPreOrderDFSTraversal) { - } - - bool Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = down_cast(data)->bb; - DCHECK(bb != nullptr); - c_unit->mir_graph->InsertPhiNodeOperands(bb); - // No need of repeating, so just return false. - return false; - } -}; - -/** - * @class InitRegLocations - * @brief Initialize Register Locations. - */ -class PerformInitRegLocations : public PassMEMirSsaRep { - public: - PerformInitRegLocations() : PassMEMirSsaRep("PerformInitRegLocation", kNoNodes) { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->InitRegLocations(); - } -}; - -/** - * @class TypeInferencePass - * @brief Type inference pass. - */ -class TypeInferencePass : public PassMEMirSsaRep { - public: - TypeInferencePass() : PassMEMirSsaRep("TypeInference", kRepeatingPreOrderDFSTraversal) { - } - - void Start(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph->InferTypesStart(); - } - - bool Worker(PassDataHolder* data) const { - DCHECK(data != nullptr); - PassMEDataHolder* pass_me_data_holder = down_cast(data); - CompilationUnit* c_unit = pass_me_data_holder->c_unit; - DCHECK(c_unit != nullptr); - BasicBlock* bb = pass_me_data_holder->bb; - DCHECK(bb != nullptr); - return c_unit->mir_graph->InferTypes(bb); - } - - void End(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph.get()->InferTypesEnd(); - } -}; - -/** - * @class FinishSSATransformation - * @brief There is some data that needs to be freed after performing the post optimization passes. - */ -class FinishSSATransformation : public PassMEMirSsaRep { - public: - FinishSSATransformation() : PassMEMirSsaRep("FinishSSATransformation", kNoNodes) { - } - - void End(PassDataHolder* data) const { - DCHECK(data != nullptr); - CompilationUnit* c_unit = down_cast(data)->c_unit; - DCHECK(c_unit != nullptr); - c_unit->mir_graph.get()->SSATransformationEnd(); - } -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_POST_OPT_PASSES_H_ diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h deleted file mode 100644 index 971745930e..0000000000 --- a/compiler/dex/quick/arm/arm_lir.h +++ /dev/null @@ -1,605 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DEX_QUICK_ARM_ARM_LIR_H_ -#define ART_COMPILER_DEX_QUICK_ARM_ARM_LIR_H_ - -#include "dex/compiler_enums.h" -#include "dex/reg_location.h" -#include "dex/reg_storage.h" - -namespace art { - -/* - * Runtime register usage conventions. - * - * r0-r3: Argument registers in both Dalvik and C/C++ conventions. - * However, for Dalvik->Dalvik calls we'll pass the target's Method* - * pointer in r0 as a hidden arg0. Otherwise used as codegen scratch - * registers. - * r0-r1: As in C/C++ r0 is 32-bit return register and r0/r1 is 64-bit - * r4 : If ARM_R4_SUSPEND_FLAG is set then reserved as a suspend check/debugger - * assist flag, otherwise a callee save promotion target. - * r5 : Callee save (promotion target) - * r6 : Callee save (promotion target) - * r7 : Callee save (promotion target) - * r8 : Callee save (promotion target) - * r9 : (rARM_SELF) is reserved (pointer to thread-local storage) - * r10 : Callee save (promotion target) - * r11 : Callee save (promotion target) - * r12 : Scratch, may be trashed by linkage stubs - * r13 : (sp) is reserved - * r14 : (lr) is reserved - * r15 : (pc) is reserved - * - * 5 core temps that codegen can use (r0, r1, r2, r3, r12) - * 7 core registers that can be used for promotion - * - * Floating pointer registers - * s0-s31 - * d0-d15, where d0={s0,s1}, d1={s2,s3}, ... , d15={s30,s31} - * - * s16-s31 (d8-d15) preserved across C calls - * s0-s15 (d0-d7) trashed across C calls - * - * s0-s15/d0-d7 used as codegen temp/scratch - * s16-s31/d8-d31 can be used for promotion. - * - * Calling convention - * o On a call to a Dalvik method, pass target's Method* in r0 - * o r1-r3 will be used for up to the first 3 words of arguments - * o Arguments past the first 3 words will be placed in appropriate - * out slots by the caller. - * o If a 64-bit argument would span the register/memory argument - * boundary, it will instead be fully passed in the frame. - * o Maintain a 16-byte stack alignment - * - * Stack frame diagram (stack grows down, higher addresses at top): - * - * +------------------------+ - * | IN[ins-1] | {Note: resides in caller's frame} - * | . | - * | IN[0] | - * | caller's Method* | - * +========================+ {Note: start of callee's frame} - * | spill region | {variable sized - will include lr if non-leaf.} - * +------------------------+ - * | ...filler word... | {Note: used as 2nd word of V[locals-1] if long] - * +------------------------+ - * | V[locals-1] | - * | V[locals-2] | - * | . | - * | . | - * | V[1] | - * | V[0] | - * +------------------------+ - * | 0 to 3 words padding | - * +------------------------+ - * | OUT[outs-1] | - * | OUT[outs-2] | - * | . | - * | OUT[0] | - * | cur_method* | <<== sp w/ 16-byte alignment - * +========================+ - */ - -// First FP callee save. -#define ARM_FP_CALLEE_SAVE_BASE 16 -// Flag for using R4 to do suspend check -// #define ARM_R4_SUSPEND_FLAG - -enum ArmResourceEncodingPos { - kArmGPReg0 = 0, - kArmRegSP = 13, - kArmRegLR = 14, - kArmRegPC = 15, - kArmFPReg0 = 16, - kArmFPReg16 = 32, - kArmRegEnd = 48, -}; - -enum ArmNativeRegisterPool { // private marker to avoid generate-operator-out.py from processing. - r0 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 0, - r1 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 1, - r2 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 2, - r3 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 3, -#ifdef ARM_R4_SUSPEND_FLAG - rARM_SUSPEND = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 4, -#else - r4 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 4, -#endif - r5 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 5, - r6 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 6, - r7 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 7, - r8 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 8, - rARM_SELF = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 9, - r10 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 10, - r11 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 11, - r12 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 12, - r13sp = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 13, - rARM_SP = r13sp, - r14lr = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 14, - rARM_LR = r14lr, - r15pc = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 15, - rARM_PC = r15pc, - - fr0 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 0, - fr1 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 1, - fr2 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 2, - fr3 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 3, - fr4 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 4, - fr5 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 5, - fr6 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 6, - fr7 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 7, - fr8 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 8, - fr9 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 9, - fr10 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 10, - fr11 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 11, - fr12 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 12, - fr13 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 13, - fr14 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 14, - fr15 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 15, - fr16 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 16, - fr17 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 17, - fr18 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 18, - fr19 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 19, - fr20 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 20, - fr21 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 21, - fr22 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 22, - fr23 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 23, - fr24 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 24, - fr25 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 25, - fr26 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 26, - fr27 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 27, - fr28 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 28, - fr29 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 29, - fr30 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 30, - fr31 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 31, - - dr0 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 0, - dr1 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 1, - dr2 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 2, - dr3 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 3, - dr4 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 4, - dr5 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 5, - dr6 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 6, - dr7 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 7, - dr8 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 8, - dr9 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 9, - dr10 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 10, - dr11 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 11, - dr12 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 12, - dr13 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 13, - dr14 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 14, - dr15 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 15, -#if 0 - // Enable when def/use and runtime able to handle these. - dr16 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 16, - dr17 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 17, - dr18 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 18, - dr19 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 19, - dr20 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 20, - dr21 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 21, - dr22 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 22, - dr23 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 23, - dr24 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 24, - dr25 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 25, - dr26 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 26, - dr27 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 27, - dr28 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 28, - dr29 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 29, - dr30 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 30, - dr31 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 31, -#endif -}; - -constexpr RegStorage rs_r0(RegStorage::kValid | r0); -constexpr RegStorage rs_r1(RegStorage::kValid | r1); -constexpr RegStorage rs_r2(RegStorage::kValid | r2); -constexpr RegStorage rs_r3(RegStorage::kValid | r3); -#ifdef ARM_R4_SUSPEND_FLAG -constexpr RegStorage rs_rARM_SUSPEND(RegStorage::kValid | rARM_SUSPEND); -#else -constexpr RegStorage rs_r4(RegStorage::kValid | r4); -#endif -constexpr RegStorage rs_r5(RegStorage::kValid | r5); -constexpr RegStorage rs_r6(RegStorage::kValid | r6); -constexpr RegStorage rs_r7(RegStorage::kValid | r7); -constexpr RegStorage rs_r8(RegStorage::kValid | r8); -constexpr RegStorage rs_rARM_SELF(RegStorage::kValid | rARM_SELF); -constexpr RegStorage rs_r10(RegStorage::kValid | r10); -constexpr RegStorage rs_r11(RegStorage::kValid | r11); -constexpr RegStorage rs_r12(RegStorage::kValid | r12); -constexpr RegStorage rs_r13sp(RegStorage::kValid | r13sp); -constexpr RegStorage rs_rARM_SP(RegStorage::kValid | rARM_SP); -constexpr RegStorage rs_r14lr(RegStorage::kValid | r14lr); -constexpr RegStorage rs_rARM_LR(RegStorage::kValid | rARM_LR); -constexpr RegStorage rs_r15pc(RegStorage::kValid | r15pc); -constexpr RegStorage rs_rARM_PC(RegStorage::kValid | rARM_PC); -constexpr RegStorage rs_invalid(RegStorage::kInvalid); - -constexpr RegStorage rs_fr0(RegStorage::kValid | fr0); -constexpr RegStorage rs_fr1(RegStorage::kValid | fr1); -constexpr RegStorage rs_fr2(RegStorage::kValid | fr2); -constexpr RegStorage rs_fr3(RegStorage::kValid | fr3); -constexpr RegStorage rs_fr4(RegStorage::kValid | fr4); -constexpr RegStorage rs_fr5(RegStorage::kValid | fr5); -constexpr RegStorage rs_fr6(RegStorage::kValid | fr6); -constexpr RegStorage rs_fr7(RegStorage::kValid | fr7); -constexpr RegStorage rs_fr8(RegStorage::kValid | fr8); -constexpr RegStorage rs_fr9(RegStorage::kValid | fr9); -constexpr RegStorage rs_fr10(RegStorage::kValid | fr10); -constexpr RegStorage rs_fr11(RegStorage::kValid | fr11); -constexpr RegStorage rs_fr12(RegStorage::kValid | fr12); -constexpr RegStorage rs_fr13(RegStorage::kValid | fr13); -constexpr RegStorage rs_fr14(RegStorage::kValid | fr14); -constexpr RegStorage rs_fr15(RegStorage::kValid | fr15); -constexpr RegStorage rs_fr16(RegStorage::kValid | fr16); -constexpr RegStorage rs_fr17(RegStorage::kValid | fr17); -constexpr RegStorage rs_fr18(RegStorage::kValid | fr18); -constexpr RegStorage rs_fr19(RegStorage::kValid | fr19); -constexpr RegStorage rs_fr20(RegStorage::kValid | fr20); -constexpr RegStorage rs_fr21(RegStorage::kValid | fr21); -constexpr RegStorage rs_fr22(RegStorage::kValid | fr22); -constexpr RegStorage rs_fr23(RegStorage::kValid | fr23); -constexpr RegStorage rs_fr24(RegStorage::kValid | fr24); -constexpr RegStorage rs_fr25(RegStorage::kValid | fr25); -constexpr RegStorage rs_fr26(RegStorage::kValid | fr26); -constexpr RegStorage rs_fr27(RegStorage::kValid | fr27); -constexpr RegStorage rs_fr28(RegStorage::kValid | fr28); -constexpr RegStorage rs_fr29(RegStorage::kValid | fr29); -constexpr RegStorage rs_fr30(RegStorage::kValid | fr30); -constexpr RegStorage rs_fr31(RegStorage::kValid | fr31); - -constexpr RegStorage rs_dr0(RegStorage::kValid | dr0); -constexpr RegStorage rs_dr1(RegStorage::kValid | dr1); -constexpr RegStorage rs_dr2(RegStorage::kValid | dr2); -constexpr RegStorage rs_dr3(RegStorage::kValid | dr3); -constexpr RegStorage rs_dr4(RegStorage::kValid | dr4); -constexpr RegStorage rs_dr5(RegStorage::kValid | dr5); -constexpr RegStorage rs_dr6(RegStorage::kValid | dr6); -constexpr RegStorage rs_dr7(RegStorage::kValid | dr7); -constexpr RegStorage rs_dr8(RegStorage::kValid | dr8); -constexpr RegStorage rs_dr9(RegStorage::kValid | dr9); -constexpr RegStorage rs_dr10(RegStorage::kValid | dr10); -constexpr RegStorage rs_dr11(RegStorage::kValid | dr11); -constexpr RegStorage rs_dr12(RegStorage::kValid | dr12); -constexpr RegStorage rs_dr13(RegStorage::kValid | dr13); -constexpr RegStorage rs_dr14(RegStorage::kValid | dr14); -constexpr RegStorage rs_dr15(RegStorage::kValid | dr15); -#if 0 -constexpr RegStorage rs_dr16(RegStorage::kValid | dr16); -constexpr RegStorage rs_dr17(RegStorage::kValid | dr17); -constexpr RegStorage rs_dr18(RegStorage::kValid | dr18); -constexpr RegStorage rs_dr19(RegStorage::kValid | dr19); -constexpr RegStorage rs_dr20(RegStorage::kValid | dr20); -constexpr RegStorage rs_dr21(RegStorage::kValid | dr21); -constexpr RegStorage rs_dr22(RegStorage::kValid | dr22); -constexpr RegStorage rs_dr23(RegStorage::kValid | dr23); -constexpr RegStorage rs_dr24(RegStorage::kValid | dr24); -constexpr RegStorage rs_dr25(RegStorage::kValid | dr25); -constexpr RegStorage rs_dr26(RegStorage::kValid | dr26); -constexpr RegStorage rs_dr27(RegStorage::kValid | dr27); -constexpr RegStorage rs_dr28(RegStorage::kValid | dr28); -constexpr RegStorage rs_dr29(RegStorage::kValid | dr29); -constexpr RegStorage rs_dr30(RegStorage::kValid | dr30); -constexpr RegStorage rs_dr31(RegStorage::kValid | dr31); -#endif - -// RegisterLocation templates return values (r0, r0/r1, s0, or d0). -// Note: The return locations are shared between quick code and quick helper. This follows quick -// ABI. Quick helper assembly routine needs to handle the ABI differences. -const RegLocation arm_loc_c_return = - {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, rs_r0, INVALID_SREG, INVALID_SREG}; -const RegLocation arm_loc_c_return_wide = - {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, - RegStorage::MakeRegPair(rs_r0, rs_r1), INVALID_SREG, INVALID_SREG}; -const RegLocation arm_loc_c_return_float = kArm32QuickCodeUseSoftFloat - ? arm_loc_c_return - : RegLocation({kLocPhysReg, 0, 0, 0, 1, 0, 0, 0, 1, rs_fr0, INVALID_SREG, INVALID_SREG}); -const RegLocation arm_loc_c_return_double = kArm32QuickCodeUseSoftFloat - ? arm_loc_c_return_wide - : RegLocation({kLocPhysReg, 1, 0, 0, 1, 0, 0, 0, 1, rs_dr0, INVALID_SREG, INVALID_SREG}); - -enum ArmShiftEncodings { - kArmLsl = 0x0, - kArmLsr = 0x1, - kArmAsr = 0x2, - kArmRor = 0x3 -}; - -/* - * The following enum defines the list of supported Thumb instructions by the - * assembler. Their corresponding EncodingMap positions will be defined in - * Assemble.cc. - */ -enum ArmOpcode { - kArmFirst = 0, - kArm16BitData = kArmFirst, // DATA [0] rd[15..0]. - kThumbAdcRR, // adc [0100000101] rm[5..3] rd[2..0]. - kThumbAddRRI3, // add(1) [0001110] imm_3[8..6] rn[5..3] rd[2..0]. - kThumbAddRI8, // add(2) [00110] rd[10..8] imm_8[7..0]. - kThumbAddRRR, // add(3) [0001100] rm[8..6] rn[5..3] rd[2..0]. - kThumbAddRRLH, // add(4) [01000100] H12[01] rm[5..3] rd[2..0]. - kThumbAddRRHL, // add(4) [01001000] H12[10] rm[5..3] rd[2..0]. - kThumbAddRRHH, // add(4) [01001100] H12[11] rm[5..3] rd[2..0]. - kThumbAddPcRel, // add(5) [10100] rd[10..8] imm_8[7..0]. - kThumbAddSpRel, // add(6) [10101] rd[10..8] imm_8[7..0]. - kThumbAddSpI7, // add(7) [101100000] imm_7[6..0]. - kThumbAndRR, // and [0100000000] rm[5..3] rd[2..0]. - kThumbAsrRRI5, // asr(1) [00010] imm_5[10..6] rm[5..3] rd[2..0]. - kThumbAsrRR, // asr(2) [0100000100] rs[5..3] rd[2..0]. - kThumbBCond, // b(1) [1101] cond[11..8] offset_8[7..0]. - kThumbBUncond, // b(2) [11100] offset_11[10..0]. - kThumbBicRR, // bic [0100001110] rm[5..3] rd[2..0]. - kThumbBkpt, // bkpt [10111110] imm_8[7..0]. - kThumbBlx1, // blx(1) [111] H[10] offset_11[10..0]. - kThumbBlx2, // blx(1) [111] H[01] offset_11[10..0]. - kThumbBl1, // blx(1) [111] H[10] offset_11[10..0]. - kThumbBl2, // blx(1) [111] H[11] offset_11[10..0]. - kThumbBlxR, // blx(2) [010001111] rm[6..3] [000]. - kThumbBx, // bx [010001110] H2[6..6] rm[5..3] SBZ[000]. - kThumbCmnRR, // cmn [0100001011] rm[5..3] rd[2..0]. - kThumbCmpRI8, // cmp(1) [00101] rn[10..8] imm_8[7..0]. - kThumbCmpRR, // cmp(2) [0100001010] rm[5..3] rd[2..0]. - kThumbCmpLH, // cmp(3) [01000101] H12[01] rm[5..3] rd[2..0]. - kThumbCmpHL, // cmp(3) [01000110] H12[10] rm[5..3] rd[2..0]. - kThumbCmpHH, // cmp(3) [01000111] H12[11] rm[5..3] rd[2..0]. - kThumbEorRR, // eor [0100000001] rm[5..3] rd[2..0]. - kThumbLdmia, // ldmia [11001] rn[10..8] reglist [7..0]. - kThumbLdrRRI5, // ldr(1) [01101] imm_5[10..6] rn[5..3] rd[2..0]. - kThumbLdrRRR, // ldr(2) [0101100] rm[8..6] rn[5..3] rd[2..0]. - kThumbLdrPcRel, // ldr(3) [01001] rd[10..8] imm_8[7..0]. - kThumbLdrSpRel, // ldr(4) [10011] rd[10..8] imm_8[7..0]. - kThumbLdrbRRI5, // ldrb(1) [01111] imm_5[10..6] rn[5..3] rd[2..0]. - kThumbLdrbRRR, // ldrb(2) [0101110] rm[8..6] rn[5..3] rd[2..0]. - kThumbLdrhRRI5, // ldrh(1) [10001] imm_5[10..6] rn[5..3] rd[2..0]. - kThumbLdrhRRR, // ldrh(2) [0101101] rm[8..6] rn[5..3] rd[2..0]. - kThumbLdrsbRRR, // ldrsb [0101011] rm[8..6] rn[5..3] rd[2..0]. - kThumbLdrshRRR, // ldrsh [0101111] rm[8..6] rn[5..3] rd[2..0]. - kThumbLslRRI5, // lsl(1) [00000] imm_5[10..6] rm[5..3] rd[2..0]. - kThumbLslRR, // lsl(2) [0100000010] rs[5..3] rd[2..0]. - kThumbLsrRRI5, // lsr(1) [00001] imm_5[10..6] rm[5..3] rd[2..0]. - kThumbLsrRR, // lsr(2) [0100000011] rs[5..3] rd[2..0]. - kThumbMovImm, // mov(1) [00100] rd[10..8] imm_8[7..0]. - kThumbMovRR, // mov(2) [0001110000] rn[5..3] rd[2..0]. - kThumbMovRR_H2H, // mov(3) [01000111] H12[11] rm[5..3] rd[2..0]. - kThumbMovRR_H2L, // mov(3) [01000110] H12[01] rm[5..3] rd[2..0]. - kThumbMovRR_L2H, // mov(3) [01000101] H12[10] rm[5..3] rd[2..0]. - kThumbMul, // mul [0100001101] rm[5..3] rd[2..0]. - kThumbMvn, // mvn [0100001111] rm[5..3] rd[2..0]. - kThumbNeg, // neg [0100001001] rm[5..3] rd[2..0]. - kThumbOrr, // orr [0100001100] rm[5..3] rd[2..0]. - kThumbPop, // pop [1011110] r[8..8] rl[7..0]. - kThumbPush, // push [1011010] r[8..8] rl[7..0]. - kThumbRev, // rev [1011101000] rm[5..3] rd[2..0] - kThumbRevsh, // revsh [1011101011] rm[5..3] rd[2..0] - kThumbRorRR, // ror [0100000111] rs[5..3] rd[2..0]. - kThumbSbc, // sbc [0100000110] rm[5..3] rd[2..0]. - kThumbStmia, // stmia [11000] rn[10..8] reglist [7.. 0]. - kThumbStrRRI5, // str(1) [01100] imm_5[10..6] rn[5..3] rd[2..0]. - kThumbStrRRR, // str(2) [0101000] rm[8..6] rn[5..3] rd[2..0]. - kThumbStrSpRel, // str(3) [10010] rd[10..8] imm_8[7..0]. - kThumbStrbRRI5, // strb(1) [01110] imm_5[10..6] rn[5..3] rd[2..0]. - kThumbStrbRRR, // strb(2) [0101010] rm[8..6] rn[5..3] rd[2..0]. - kThumbStrhRRI5, // strh(1) [10000] imm_5[10..6] rn[5..3] rd[2..0]. - kThumbStrhRRR, // strh(2) [0101001] rm[8..6] rn[5..3] rd[2..0]. - kThumbSubRRI3, // sub(1) [0001111] imm_3[8..6] rn[5..3] rd[2..0]*/ - kThumbSubRI8, // sub(2) [00111] rd[10..8] imm_8[7..0]. - kThumbSubRRR, // sub(3) [0001101] rm[8..6] rn[5..3] rd[2..0]. - kThumbSubSpI7, // sub(4) [101100001] imm_7[6..0]. - kThumbSwi, // swi [11011111] imm_8[7..0]. - kThumbTst, // tst [0100001000] rm[5..3] rn[2..0]. - kThumb2Vldrs, // vldr low sx [111011011001] rn[19..16] rd[15-12] [1010] imm_8[7..0]. - kThumb2Vldrd, // vldr low dx [111011011001] rn[19..16] rd[15-12] [1011] imm_8[7..0]. - kThumb2Vmuls, // vmul vd, vn, vm [111011100010] rn[19..16] rd[15-12] [10100000] rm[3..0]. - kThumb2Vmuld, // vmul vd, vn, vm [111011100010] rn[19..16] rd[15-12] [10110000] rm[3..0]. - kThumb2Vstrs, // vstr low sx [111011011000] rn[19..16] rd[15-12] [1010] imm_8[7..0]. - kThumb2Vstrd, // vstr low dx [111011011000] rn[19..16] rd[15-12] [1011] imm_8[7..0]. - kThumb2Vsubs, // vsub vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10100040] rm[3..0]. - kThumb2Vsubd, // vsub vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10110040] rm[3..0]. - kThumb2Vadds, // vadd vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10100000] rm[3..0]. - kThumb2Vaddd, // vadd vd, vn, vm [111011100011] rn[19..16] rd[15-12] [10110000] rm[3..0]. - kThumb2Vdivs, // vdiv vd, vn, vm [111011101000] rn[19..16] rd[15-12] [10100000] rm[3..0]. - kThumb2Vdivd, // vdiv vd, vn, vm [111011101000] rn[19..16] rd[15-12] [10110000] rm[3..0]. - kThumb2VmlaF64, // vmla.F64 vd, vn, vm [111011100000] vn[19..16] vd[15..12] [10110000] vm[3..0]. - kThumb2VcvtIF, // vcvt.F32.S32 vd, vm [1110111010111000] vd[15..12] [10101100] vm[3..0]. - kThumb2VcvtFI, // vcvt.S32.F32 vd, vm [1110111010111101] vd[15..12] [10101100] vm[3..0]. - kThumb2VcvtDI, // vcvt.S32.F32 vd, vm [1110111010111101] vd[15..12] [10111100] vm[3..0]. - kThumb2VcvtFd, // vcvt.F64.F32 vd, vm [1110111010110111] vd[15..12] [10101100] vm[3..0]. - kThumb2VcvtDF, // vcvt.F32.F64 vd, vm [1110111010110111] vd[15..12] [10111100] vm[3..0]. - kThumb2VcvtF64S32, // vcvt.F64.S32 vd, vm [1110111010111000] vd[15..12] [10111100] vm[3..0]. - kThumb2VcvtF64U32, // vcvt.F64.U32 vd, vm [1110111010111000] vd[15..12] [10110100] vm[3..0]. - kThumb2Vsqrts, // vsqrt.f32 vd, vm [1110111010110001] vd[15..12] [10101100] vm[3..0]. - kThumb2Vsqrtd, // vsqrt.f64 vd, vm [1110111010110001] vd[15..12] [10111100] vm[3..0]. - kThumb2MovI8M, // mov(T2) rd, # [11110] i [00001001111] imm3 rd[11..8] imm8. - kThumb2MovImm16, // mov(T3) rd, # [11110] i [0010100] imm4 [0] imm3 rd[11..8] imm8. - kThumb2StrRRI12, // str(Imm,T3) rd,[rn,#imm12] [111110001100] rn[19..16] rt[15..12] imm12[11..0]. - kThumb2LdrRRI12, // str(Imm,T3) rd,[rn,#imm12] [111110001100] rn[19..16] rt[15..12] imm12[11..0]. - kThumb2StrRRI8Predec, // str(Imm,T4) rd,[rn,#-imm8] [111110000100] rn[19..16] rt[15..12] [1100] imm[7..0]. - kThumb2LdrRRI8Predec, // ldr(Imm,T4) rd,[rn,#-imm8] [111110000101] rn[19..16] rt[15..12] [1100] imm[7..0]. - kThumb2Cbnz, // cbnz rd,