summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/optimizing/code_generator.cc7
-rw-r--r--compiler/optimizing/nodes.h46
-rw-r--r--compiler/optimizing/side_effects_test.cc1
-rw-r--r--test/510-checker-try-catch/smali/Runtime.smali70
-rw-r--r--test/510-checker-try-catch/src/Main.java119
-rw-r--r--test/527-checker-array-access-split/src/Main.java27
-rw-r--r--test/706-checker-scheduler/src/Main.java8
7 files changed, 265 insertions, 13 deletions
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index a13efcaee2..095b273182 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -1514,7 +1514,12 @@ void CodeGenerator::ValidateInvokeRuntime(QuickEntrypointEnum entrypoint,
<< " instruction->GetSideEffects().ToString()="
<< instruction->GetSideEffects().ToString();
} else {
- DCHECK(instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()) ||
+ // 'CanTriggerGC' side effect is used to restrict optimization of instructions which depend
+ // on GC (e.g. IntermediateAddress) - to ensure they are not alive across GC points. However
+ // if execution never returns to the compiled code from a GC point this restriction is
+ // unnecessary - in particular for fatal slow paths which might trigger GC.
+ DCHECK((slow_path->IsFatal() && !instruction->GetLocations()->WillCall()) ||
+ instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()) ||
// When (non-Baker) read barriers are enabled, some instructions
// use a slow path to emit a read barrier, which does not trigger
// GC.
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 8b9e1da0d3..48bc5e8c15 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1626,6 +1626,21 @@ using HConstInputsRef = TransformArrayRef<const HUserRecord<HInstruction*>, HInp
* the same, and any reference read depends on any reference read without
* further regard of its type).
*
+ * kDependsOnGCBit is defined in the following way: instructions with kDependsOnGCBit must not be
+ * alive across the point where garbage collection might happen.
+ *
+ * Note: Instructions with kCanTriggerGCBit do not depend on each other.
+ *
+ * kCanTriggerGCBit must be used for instructions for which GC might happen on the path across
+ * those instructions from the compiler perspective (between this instruction and the next one
+ * in the IR).
+ *
+ * Note: Instructions which can cause GC only on a fatal slow path do not need
+ * kCanTriggerGCBit as the execution never returns to the instruction next to the exceptional
+ * one. However the execution may return to compiled code if there is a catch block in the
+ * current method; for this purpose the TryBoundary exit instruction has kCanTriggerGCBit
+ * set.
+ *
* The internal representation uses 38-bit and is described in the table below.
* The first line indicates the side effect, and for field/array accesses the
* second line indicates the type of the access (in the order of the
@@ -1698,10 +1713,17 @@ class SideEffects : public ValueObject {
return SideEffects(TypeFlag(type, kArrayReadOffset));
}
+ // Returns whether GC might happen across this instruction from the compiler perspective so
+ // the next instruction in the IR would see that.
+ //
+ // See the SideEffect class comments.
static SideEffects CanTriggerGC() {
return SideEffects(1ULL << kCanTriggerGCBit);
}
+ // Returns whether the instruction must not be alive across a GC point.
+ //
+ // See the SideEffect class comments.
static SideEffects DependsOnGC() {
return SideEffects(1ULL << kDependsOnGCBit);
}
@@ -3123,8 +3145,15 @@ class HTryBoundary FINAL : public HExpression<0> {
kLast = kExit
};
+ // SideEffects::CanTriggerGC prevents instructions with SideEffects::DependOnGC to be alive
+ // across the catch block entering edges as GC might happen during throwing an exception.
+ // TryBoundary with BoundaryKind::kExit is conservatively used for that as there is no
+ // HInstruction which a catch block must start from.
explicit HTryBoundary(BoundaryKind kind, uint32_t dex_pc = kNoDexPc)
- : HExpression(kTryBoundary, SideEffects::None(), dex_pc) {
+ : HExpression(kTryBoundary,
+ (kind == BoundaryKind::kExit) ? SideEffects::CanTriggerGC()
+ : SideEffects::None(),
+ dex_pc) {
SetPackedField<BoundaryKindField>(kind);
}
@@ -5150,9 +5179,10 @@ class HAbs FINAL : public HUnaryOperation {
class HDivZeroCheck FINAL : public HExpression<1> {
public:
// `HDivZeroCheck` can trigger GC, as it may call the `ArithmeticException`
- // constructor.
+ // constructor. However it can only do it on a fatal slow path so execution never returns to the
+ // instruction following the current one; thus 'SideEffects::None()' is used.
HDivZeroCheck(HInstruction* value, uint32_t dex_pc)
- : HExpression(kDivZeroCheck, value->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
+ : HExpression(kDivZeroCheck, value->GetType(), SideEffects::None(), dex_pc) {
SetRawInputAt(0, value);
}
@@ -5629,9 +5659,10 @@ static constexpr uint32_t kNoRegNumber = -1;
class HNullCheck FINAL : public HExpression<1> {
public:
// `HNullCheck` can trigger GC, as it may call the `NullPointerException`
- // constructor.
+ // constructor. However it can only do it on a fatal slow path so execution never returns to the
+ // instruction following the current one; thus 'SideEffects::None()' is used.
HNullCheck(HInstruction* value, uint32_t dex_pc)
- : HExpression(kNullCheck, value->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
+ : HExpression(kNullCheck, value->GetType(), SideEffects::None(), dex_pc) {
SetRawInputAt(0, value);
}
@@ -6058,12 +6089,13 @@ class HArrayLength FINAL : public HExpression<1> {
class HBoundsCheck FINAL : public HExpression<2> {
public:
// `HBoundsCheck` can trigger GC, as it may call the `IndexOutOfBoundsException`
- // constructor.
+ // constructor. However it can only do it on a fatal slow path so execution never returns to the
+ // instruction following the current one; thus 'SideEffects::None()' is used.
HBoundsCheck(HInstruction* index,
HInstruction* length,
uint32_t dex_pc,
bool is_string_char_at = false)
- : HExpression(kBoundsCheck, index->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
+ : HExpression(kBoundsCheck, index->GetType(), SideEffects::None(), dex_pc) {
DCHECK_EQ(DataType::Type::kInt32, DataType::Kind(index->GetType()));
SetPackedFlag<kFlagIsStringCharAt>(is_string_char_at);
SetRawInputAt(0, index);
diff --git a/compiler/optimizing/side_effects_test.cc b/compiler/optimizing/side_effects_test.cc
index 97317124ef..4b0be07f3b 100644
--- a/compiler/optimizing/side_effects_test.cc
+++ b/compiler/optimizing/side_effects_test.cc
@@ -202,6 +202,7 @@ TEST(SideEffectsTest, GC) {
EXPECT_TRUE(depends_on_gc.MayDependOn(all_changes));
EXPECT_TRUE(depends_on_gc.Union(can_trigger_gc).MayDependOn(all_changes));
EXPECT_FALSE(can_trigger_gc.MayDependOn(all_changes));
+ EXPECT_FALSE(can_trigger_gc.MayDependOn(can_trigger_gc));
EXPECT_TRUE(all_changes.Includes(can_trigger_gc));
EXPECT_FALSE(all_changes.Includes(depends_on_gc));
diff --git a/test/510-checker-try-catch/smali/Runtime.smali b/test/510-checker-try-catch/smali/Runtime.smali
index 19b43a3f30..d080a0c94b 100644
--- a/test/510-checker-try-catch/smali/Runtime.smali
+++ b/test/510-checker-try-catch/smali/Runtime.smali
@@ -549,6 +549,76 @@
.end array-data
.end method
+
+## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after)
+## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+## CHECK-DAG: <<Offset:i\d+>> IntConstant 12
+## CHECK-DAG: <<IndexParam:i\d+>> ParameterValue
+## CHECK-DAG: <<Array:l\d+>> ParameterValue
+
+## CHECK-DAG: <<NullCh1:l\d+>> NullCheck [<<Array>>]
+## CHECK-DAG: <<Length:i\d+>> ArrayLength
+## CHECK-DAG: <<BoundsCh1:i\d+>> BoundsCheck [<<IndexParam>>,<<Length>>]
+## CHECK-DAG: <<IntAddr1:i\d+>> IntermediateAddress [<<NullCh1>>,<<Offset>>]
+## CHECK-DAG: ArrayGet [<<IntAddr1>>,<<BoundsCh1>>]
+## CHECK-DAG: TryBoundary
+
+## CHECK-DAG: <<Xplus1:i\d+>> Add [<<IndexParam>>,<<Const1>>]
+## CHECK-DAG: <<BoundsCh2:i\d+>> BoundsCheck [<<Xplus1>>,<<Length>>]
+## CHECK-DAG: ArrayGet [<<IntAddr1>>,<<BoundsCh2>>]
+## CHECK-DAG: TryBoundary
+
+## CHECK-DAG: <<Phi:i\d+>> Phi [<<Xplus1>>]
+## CHECK-DAG: <<Phiplus1:i\d+>> Add [<<Phi>>,<<Const1>>]
+## CHECK-DAG: <<BoundsCh3:i\d+>> BoundsCheck [<<Phiplus1>>,<<Length>>]
+## CHECK-DAG: <<IntAddr3:i\d+>> IntermediateAddress [<<NullCh1>>,<<Offset>>]
+## CHECK-DAG: ArrayGet [<<IntAddr3>>,<<BoundsCh3>>]
+
+## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after)
+## CHECK: NullCheck
+## CHECK-NOT: NullCheck
+
+## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after)
+## CHECK: IntermediateAddress
+## CHECK: IntermediateAddress
+## CHECK-NOT: IntermediateAddress
+
+## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after)
+## CHECK: BoundsCheck
+## CHECK: BoundsCheck
+## CHECK: BoundsCheck
+## CHECK-NOT: BoundsCheck
+
+## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after)
+## CHECK: ArrayGet
+## CHECK: ArrayGet
+## CHECK: ArrayGet
+## CHECK-NOT: ArrayGet
+.method public static testIntAddressCatch(I[I)I
+ .registers 4
+ aget v0, p1, p0
+ add-int v1, v0, v0
+
+ :try_start
+ const/4 v0, 0x1
+ add-int p0, p0, v0
+ aget v0, p1, p0
+
+ :try_end
+ .catch Ljava/lang/ArithmException; {:try_start .. :try_end} :catch_block
+
+ :return
+ add-int v1, v1, v0
+ return v1
+
+ :catch_block
+ const/4 v0, 0x1
+ add-int p0, p0, v0
+ aget v0, p1, p0
+
+ goto :return
+.end method
+
.field public static intArray:[I
.field public static longArray:[J
.field public static floatArray:[F
diff --git a/test/510-checker-try-catch/src/Main.java b/test/510-checker-try-catch/src/Main.java
index d6dcd30f3c..18658cd5e0 100644
--- a/test/510-checker-try-catch/src/Main.java
+++ b/test/510-checker-try-catch/src/Main.java
@@ -37,6 +37,114 @@ public class Main {
public int expected;
}
+ // Test that IntermediateAddress instruction is not alive across BoundsCheck which can throw to
+ // a catch block.
+ //
+ /// CHECK-START-{ARM,ARM64}: void Main.boundsCheckAndCatch(int, int[], int[]) GVN$after_arch (before)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+ /// CHECK-DAG: <<Offset:i\d+>> IntConstant 12
+ /// CHECK-DAG: <<IndexParam:i\d+>> ParameterValue
+ /// CHECK-DAG: <<ArrayA:l\d+>> ParameterValue
+ /// CHECK-DAG: <<ArrayB:l\d+>> ParameterValue
+ //
+
+ /// CHECK-DAG: <<NullCh1:l\d+>> NullCheck [<<ArrayA>>]
+ /// CHECK-DAG: <<LengthA:i\d+>> ArrayLength
+ /// CHECK-DAG: <<BoundsCh1:i\d+>> BoundsCheck [<<IndexParam>>,<<LengthA>>]
+ /// CHECK-DAG: <<IntAddr1:i\d+>> IntermediateAddress [<<NullCh1>>,<<Offset>>]
+ /// CHECK-DAG: ArraySet [<<IntAddr1>>,<<BoundsCh1>>,<<Const1>>]
+ /// CHECK-DAG: TryBoundary
+ //
+ /// CHECK-DAG: <<IntAddr2:i\d+>> IntermediateAddress [<<NullCh1>>,<<Offset>>]
+ /// CHECK-DAG: ArraySet [<<IntAddr2>>,<<BoundsCh1>>,<<Const2>>]
+ /// CHECK-DAG: <<NullChB:l\d+>> NullCheck [<<ArrayB>>]
+ /// CHECK-DAG: <<LengthB:i\d+>> ArrayLength
+ /// CHECK-DAG: <<BoundsChB:i\d+>> BoundsCheck [<<Const0>>,<<LengthB>>]
+ /// CHECK-DAG: <<GetB:i\d+>> ArrayGet [<<NullChB>>,<<BoundsChB>>]
+ /// CHECK-DAG: <<ZeroCheck:i\d+>> DivZeroCheck [<<IndexParam>>]
+ /// CHECK-DAG: <<Div:i\d+>> Div [<<GetB>>,<<ZeroCheck>>]
+ /// CHECK-DAG: <<Xplus1:i\d+>> Add [<<IndexParam>>,<<Const1>>]
+ /// CHECK-DAG: <<BoundsCh2:i\d+>> BoundsCheck [<<Xplus1>>,<<LengthA>>]
+ /// CHECK-DAG: <<IntAddr3:i\d+>> IntermediateAddress [<<NullCh1>>,<<Offset>>]
+ /// CHECK-DAG: ArraySet [<<IntAddr3>>,<<BoundsCh2>>,<<Div>>]
+ /// CHECK-DAG: TryBoundary
+ //
+ /// CHECK-DAG: ClearException
+ /// CHECK-DAG: <<IntAddr4:i\d+>> IntermediateAddress [<<NullCh1>>,<<Offset>>]
+ /// CHECK-DAG: ArraySet [<<IntAddr4>>,<<BoundsCh1>>,<<Const1>>]
+ //
+ /// CHECK-NOT: NullCheck
+ /// CHECK-NOT: IntermediateAddress
+
+ /// CHECK-START-{ARM,ARM64}: void Main.boundsCheckAndCatch(int, int[], int[]) GVN$after_arch (after)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+ /// CHECK-DAG: <<Offset:i\d+>> IntConstant 12
+ /// CHECK-DAG: <<IndexParam:i\d+>> ParameterValue
+ /// CHECK-DAG: <<ArrayA:l\d+>> ParameterValue
+ /// CHECK-DAG: <<ArrayB:l\d+>> ParameterValue
+ //
+ /// CHECK-DAG: <<NullCh1:l\d+>> NullCheck [<<ArrayA>>]
+ /// CHECK-DAG: <<LengthA:i\d+>> ArrayLength
+ /// CHECK-DAG: <<BoundsCh1:i\d+>> BoundsCheck [<<IndexParam>>,<<LengthA>>]
+ /// CHECK-DAG: <<IntAddr1:i\d+>> IntermediateAddress [<<NullCh1>>,<<Offset>>]
+ /// CHECK-DAG: ArraySet [<<IntAddr1>>,<<BoundsCh1>>,<<Const1>>]
+ /// CHECK-DAG: TryBoundary
+ //
+ /// CHECK-DAG: ArraySet [<<IntAddr1>>,<<BoundsCh1>>,<<Const2>>]
+ /// CHECK-DAG: <<NullChB:l\d+>> NullCheck [<<ArrayB>>]
+ /// CHECK-DAG: <<LengthB:i\d+>> ArrayLength
+ /// CHECK-DAG: <<BoundsChB:i\d+>> BoundsCheck [<<Const0>>,<<LengthB>>]
+ /// CHECK-DAG: <<GetB:i\d+>> ArrayGet [<<NullChB>>,<<BoundsChB>>]
+ /// CHECK-DAG: <<ZeroCheck:i\d+>> DivZeroCheck [<<IndexParam>>]
+ /// CHECK-DAG: <<Div:i\d+>> Div [<<GetB>>,<<ZeroCheck>>]
+ /// CHECK-DAG: <<Xplus1:i\d+>> Add [<<IndexParam>>,<<Const1>>]
+ /// CHECK-DAG: <<BoundsCh2:i\d+>> BoundsCheck [<<Xplus1>>,<<LengthA>>]
+ /// CHECK-DAG: ArraySet [<<IntAddr1>>,<<BoundsCh2>>,<<Div>>]
+ /// CHECK-DAG: TryBoundary
+ //
+ /// CHECK-DAG: ClearException
+ /// CHECK-DAG: <<IntAddr4:i\d+>> IntermediateAddress [<<NullCh1>>,<<Offset>>]
+ /// CHECK-DAG: ArraySet [<<IntAddr4>>,<<BoundsCh1>>,<<Const1>>]
+ //
+ /// CHECK-NOT: NullCheck
+ /// CHECK-NOT: IntermediateAddress
+
+ // Make sure that BoundsCheck, DivZeroCheck and NullCheck don't stop IntermediateAddress sharing.
+ public static void boundsCheckAndCatch(int x, int[] a, int[] b) {
+ a[x] = 1;
+ try {
+ a[x] = 2;
+ a[x + 1] = b[0] / x;
+ } catch (Exception e) {
+ a[x] = 1;
+ }
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public final static int ARRAY_SIZE = 128;
+
+ public static void testBoundsCheckAndCatch() {
+ int[] a = new int[ARRAY_SIZE];
+ int[] b = new int[ARRAY_SIZE];
+
+ int index = ARRAY_SIZE - 2;
+ boundsCheckAndCatch(index, a, b);
+ expectEquals(2, a[index]);
+
+ index = ARRAY_SIZE - 1;
+ boundsCheckAndCatch(index, a, b);
+ expectEquals(1, a[index]);
+ }
+
public static void testMethod(String method) throws Exception {
Class<?> c = Class.forName("Runtime");
Method m = c.getMethod(method, boolean.class, boolean.class);
@@ -52,6 +160,14 @@ public class Main {
}
}
+ public static void testIntAddressCatch() throws Exception {
+ int[] a = new int[3];
+
+ Class<?> c = Class.forName("Runtime");
+ Method m = c.getMethod("testIntAddressCatch", int.class, Class.forName("[I"));
+ m.invoke(null, 0, a);
+ }
+
public static void main(String[] args) throws Exception {
testMethod("testUseAfterCatch_int");
testMethod("testUseAfterCatch_long");
@@ -64,5 +180,8 @@ public class Main {
testMethod("testCatchPhi_double");
testMethod("testCatchPhi_singleSlot");
testMethod("testCatchPhi_doubleSlot");
+
+ testBoundsCheckAndCatch();
+ testIntAddressCatch();
}
}
diff --git a/test/527-checker-array-access-split/src/Main.java b/test/527-checker-array-access-split/src/Main.java
index 935b37858d..f83c924de9 100644
--- a/test/527-checker-array-access-split/src/Main.java
+++ b/test/527-checker-array-access-split/src/Main.java
@@ -552,6 +552,28 @@ public class Main {
return (int)s;
}
+ //
+ // Check that IntermediateAddress can be shared across BoundsCheck, DivZeroCheck and NullCheck -
+ // instruction which have fatal slow paths.
+ //
+ /// CHECK-START-{ARM,ARM64}: void Main.checkGVNForFatalChecks(int, int, char[], int[]) GVN$after_arch (before)
+ /// CHECK: IntermediateAddress
+ /// CHECK: IntermediateAddress
+ //
+ /// CHECK-NOT: IntermediateAddress
+
+ /// CHECK-START-{ARM,ARM64}: void Main.checkGVNForFatalChecks(int, int, char[], int[]) GVN$after_arch (after)
+ /// CHECK: IntermediateAddress
+ //
+ /// CHECK-NOT: IntermediateAddress
+ public final static void checkGVNForFatalChecks(int begin, int end, char[] buf1, int[] buf2) {
+ buf1[begin] = 'a';
+ buf2[0] = begin / end;
+ buf1[end] = 'n';
+ }
+
+ public final static int ARRAY_SIZE = 128;
+
public static void main(String[] args) {
int[] array = {123, 456, 789};
@@ -575,5 +597,10 @@ public class Main {
assertIntEquals(2097152, canMergeAfterBCE2());
assertIntEquals(18, checkLongFloatDouble());
+
+ char[] c1 = new char[ARRAY_SIZE];
+ int[] i1 = new int[ARRAY_SIZE];
+ checkGVNForFatalChecks(1, 2, c1, i1);
+ assertIntEquals('n', c1[2]);
}
}
diff --git a/test/706-checker-scheduler/src/Main.java b/test/706-checker-scheduler/src/Main.java
index 9f4caecccd..af18193388 100644
--- a/test/706-checker-scheduler/src/Main.java
+++ b/test/706-checker-scheduler/src/Main.java
@@ -292,8 +292,7 @@ public class Main {
/// CHECK: <<ArraySet1:v\d+>> ArraySet [<<Addr1>>,{{i\d+}},{{i\d+}}] loop:<<Loop>> outer_loop:none
/// CHECK: <<ArrayGet2:i\d+>> ArrayGet [<<NullB>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
/// CHECK: Add loop:<<Loop>> outer_loop:none
- /// CHECK: <<Addr2:i\d+>> IntermediateAddress [<<NullA>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
- /// CHECK: <<ArraySet2:v\d+>> ArraySet [<<Addr2>>,{{i\d+}},{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK: <<ArraySet2:v\d+>> ArraySet [<<Addr1>>,{{i\d+}},{{i\d+}}] loop:<<Loop>> outer_loop:none
/// CHECK: Add loop:<<Loop>> outer_loop:none
/// CHECK-START-ARM64: void Main.CrossOverLoop(int[], int[]) scheduler (after)
@@ -303,13 +302,12 @@ public class Main {
/// CHECK: <<NullA:l\d+>> NullCheck [<<ParamA>>] loop:none
/// CHECK: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK: <<ArrayGet1:i\d+>> ArrayGet [<<NullB>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
- /// CHECK: Add loop:<<Loop>> outer_loop:none
/// CHECK: <<Addr1:i\d+>> IntermediateAddress [<<NullA>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK: Add [<<ArrayGet1>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
/// CHECK: <<ArraySet1:v\d+>> ArraySet [<<Addr1>>,{{i\d+}},{{i\d+}}] loop:<<Loop>> outer_loop:none
/// CHECK: <<ArrayGet2:i\d+>> ArrayGet [<<NullB>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
/// CHECK: Add loop:<<Loop>> outer_loop:none
- /// CHECK: <<Addr2:i\d+>> IntermediateAddress [<<NullA>>,{{i\d+}}] loop:<<Loop>> outer_loop:none
- /// CHECK: <<ArraySet2:v\d+>> ArraySet [<<Addr2>>,{{i\d+}},{{i\d+}}] loop:<<Loop>> outer_loop:none
+ /// CHECK: <<ArraySet2:v\d+>> ArraySet [<<Addr1>>,{{i\d+}},{{i\d+}}] loop:<<Loop>> outer_loop:none
/// CHECK: Add loop:<<Loop>> outer_loop:none
private static void CrossOverLoop(int a[], int b[]) {
b[20] = 99;