diff options
Diffstat (limited to 'test')
| -rw-r--r-- | test/117-nopatchoat/nopatchoat.cc | 12 | ||||
| -rwxr-xr-x | test/117-nopatchoat/run | 2 | ||||
| -rw-r--r-- | test/117-nopatchoat/src/Main.java | 10 | ||||
| -rw-r--r-- | test/142-classloader2/expected.txt | 1 | ||||
| -rw-r--r-- | test/142-classloader2/info.txt | 1 | ||||
| -rw-r--r-- | test/142-classloader2/smali/MyPathClassLoader.smali | 13 | ||||
| -rw-r--r-- | test/142-classloader2/src-ex/A.java | 22 | ||||
| -rw-r--r-- | test/142-classloader2/src/A.java | 22 | ||||
| -rw-r--r-- | test/142-classloader2/src/Main.java | 76 | ||||
| -rw-r--r-- | test/482-checker-loop-back-edge-use/src/Main.java | 117 | ||||
| -rw-r--r-- | test/510-checker-try-catch/smali/Builder.smali | 209 | ||||
| -rw-r--r-- | test/530-checker-loops/src/Main.java | 282 |
12 files changed, 651 insertions, 116 deletions
diff --git a/test/117-nopatchoat/nopatchoat.cc b/test/117-nopatchoat/nopatchoat.cc index 7eac412681..3e533ad62e 100644 --- a/test/117-nopatchoat/nopatchoat.cc +++ b/test/117-nopatchoat/nopatchoat.cc @@ -16,7 +16,10 @@ #include "class_linker.h" #include "dex_file-inl.h" +#include "gc/heap.h" +#include "gc/space/image_space.h" #include "mirror/class-inl.h" +#include "runtime.h" #include "scoped_thread_state_change.h" #include "thread.h" @@ -31,6 +34,11 @@ class NoPatchoatTest { return dex_file.GetOatDexFile(); } + static bool isRelocationDeltaZero() { + gc::space::ImageSpace* space = Runtime::Current()->GetHeap()->GetImageSpace(); + return space != nullptr && space->GetImageHeader().GetPatchDelta() == 0; + } + static bool hasExecutableOat(jclass cls) { const OatFile::OatDexFile* oat_dex_file = getOatDexFile(cls); @@ -49,6 +57,10 @@ class NoPatchoatTest { } }; +extern "C" JNIEXPORT jboolean JNICALL Java_Main_isRelocationDeltaZero(JNIEnv*, jclass) { + return NoPatchoatTest::isRelocationDeltaZero(); +} + extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasExecutableOat(JNIEnv*, jclass cls) { return NoPatchoatTest::hasExecutableOat(cls); } diff --git a/test/117-nopatchoat/run b/test/117-nopatchoat/run index c749c74345..c634900218 100755 --- a/test/117-nopatchoat/run +++ b/test/117-nopatchoat/run @@ -36,8 +36,6 @@ fi # Make sure we can run without relocation echo "Run without dex2oat/patchoat" -# /bin/false is actually not even there for either, so the exec will fail. -# Unfortunately there is no equivalent to /bin/false in android. ${RUN} ${flags} --runtime-option -Xnodex2oat # Make sure we can run with the oat file. diff --git a/test/117-nopatchoat/src/Main.java b/test/117-nopatchoat/src/Main.java index 223e12084d..5cca309847 100644 --- a/test/117-nopatchoat/src/Main.java +++ b/test/117-nopatchoat/src/Main.java @@ -18,9 +18,13 @@ public class Main { public static void main(String[] args) { System.loadLibrary(args[0]); + // With a relocationDelta of 0, the runtime has no way to determine if the oat file in + // ANDROID_DATA has been relocated, since a non-relocated oat file always has a 0 delta. + // Hitting this condition should be rare and ideally we would prevent it from happening but + // there is no way to do so without major changes to the run-test framework. boolean executable_correct = (isPic() ? - hasExecutableOat() == true : - hasExecutableOat() == isDex2OatEnabled()); + hasExecutableOat() == true : + hasExecutableOat() == (isDex2OatEnabled() || isRelocationDeltaZero())); System.out.println( "dex2oat & patchoat are " + ((isDex2OatEnabled()) ? "enabled" : "disabled") + @@ -50,4 +54,6 @@ public class Main { private native static boolean hasOat(); private native static boolean hasExecutableOat(); + + private native static boolean isRelocationDeltaZero(); } diff --git a/test/142-classloader2/expected.txt b/test/142-classloader2/expected.txt new file mode 100644 index 0000000000..86f5e220e2 --- /dev/null +++ b/test/142-classloader2/expected.txt @@ -0,0 +1 @@ +Everything OK. diff --git a/test/142-classloader2/info.txt b/test/142-classloader2/info.txt new file mode 100644 index 0000000000..eb821a8ddb --- /dev/null +++ b/test/142-classloader2/info.txt @@ -0,0 +1 @@ +Check sub-classing of PathClassLoader. diff --git a/test/142-classloader2/smali/MyPathClassLoader.smali b/test/142-classloader2/smali/MyPathClassLoader.smali new file mode 100644 index 0000000000..553abd46c9 --- /dev/null +++ b/test/142-classloader2/smali/MyPathClassLoader.smali @@ -0,0 +1,13 @@ +# Simple subclass of PathClassLoader with methods overridden. +# We need to use smali right now to subclass a libcore class, see b/24304298. + +.class public LMyPathClassLoader; + +.super Ldalvik/system/PathClassLoader; + +# Simple forwarding constructor. +.method public constructor <init>(Ljava/lang/String;Ljava/lang/ClassLoader;)V + .registers 3 + invoke-direct {p0, p1, p2}, Ldalvik/system/PathClassLoader;-><init>(Ljava/lang/String;Ljava/lang/ClassLoader;)V + return-void +.end method diff --git a/test/142-classloader2/src-ex/A.java b/test/142-classloader2/src-ex/A.java new file mode 100644 index 0000000000..d5fa1f9df7 --- /dev/null +++ b/test/142-classloader2/src-ex/A.java @@ -0,0 +1,22 @@ +/* + * 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. + */ + +/** + * Identical class to the main src, except with a different value, so we can distinguish them. + */ +public class A { + public static String value = "Ex-A"; +} diff --git a/test/142-classloader2/src/A.java b/test/142-classloader2/src/A.java new file mode 100644 index 0000000000..532df51878 --- /dev/null +++ b/test/142-classloader2/src/A.java @@ -0,0 +1,22 @@ +/* + * 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. + */ + +/** + * Main class, with a simple value. + */ +public class A { + public static String value = "Src-A"; +} diff --git a/test/142-classloader2/src/Main.java b/test/142-classloader2/src/Main.java new file mode 100644 index 0000000000..86c61ebc3a --- /dev/null +++ b/test/142-classloader2/src/Main.java @@ -0,0 +1,76 @@ +/* + * 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. + */ + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; + +/** + * PathClassLoader test. + */ +public class Main { + + private static ClassLoader createClassLoader(String dexPath, ClassLoader parent) { + try { + Class<?> myClassLoaderClass = Class.forName("MyPathClassLoader"); + Constructor constructor = myClassLoaderClass.getConstructor(String.class, + ClassLoader.class); + return (ClassLoader)constructor.newInstance(dexPath, parent); + } catch (Exception e) { + // Ups, not available?!?! + throw new RuntimeException(e); + } + } + + /** + * Main entry point. + */ + public static void main(String[] args) throws Exception { + // Check the class-path for the second file. We'll use that one as the source of the + // new classloader. + String cp = System.getProperty("java.class.path"); + if (cp.split(System.getProperty("path.separator")).length != 1) { + throw new IllegalStateException("Didn't find exactly one classpath element in " + cp); + } + if (!cp.endsWith("classloader2.jar")) { + throw new IllegalStateException("Don't understand classpath " + cp); + } + cp = cp.replace("classloader2.jar", "classloader2-ex.jar"); + + ClassLoader myClassLoader = createClassLoader( + cp, ClassLoader.getSystemClassLoader().getParent()); + + // Now load our test class. + Class<?> srcClass = A.class; + Class<?> exClass = myClassLoader.loadClass("A"); + + // First check: classes should be different. + if (srcClass == exClass) { + throw new IllegalStateException("Loaded class instances are the same"); + } + + // Secondary checks: get the static field values and make sure they aren't the same. + String srcValue = (String)srcClass.getDeclaredField("value").get(null); + if (!"Src-A".equals(srcValue)) { + throw new IllegalStateException("Expected Src-A, found " + srcValue); + } + String exValue = (String)exClass.getDeclaredField("value").get(null); + if (!"Ex-A".equals(exValue)) { + throw new IllegalStateException("Expected Ex-A, found " + exValue); + } + + System.out.println("Everything OK."); + } +} diff --git a/test/482-checker-loop-back-edge-use/src/Main.java b/test/482-checker-loop-back-edge-use/src/Main.java index 2cfb04d652..6b4da9de27 100644 --- a/test/482-checker-loop-back-edge-use/src/Main.java +++ b/test/482-checker-loop-back-edge-use/src/Main.java @@ -18,16 +18,27 @@ public class Main { /// CHECK-START: void Main.loop1(boolean) liveness (after) - /// CHECK: ParameterValue liveness:2 ranges:{[2,22)} uses:[17,22] - /// CHECK: Goto liveness:20 + /// CHECK: <<Arg:z\d+>> ParameterValue liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopUse:\d+>>)} uses:[<<ArgUse:\d+>>,<<ArgLoopUse>>] + /// CHECK: If [<<Arg>>] liveness:<<IfLiv:\d+>> + /// CHECK: Goto liveness:<<GotoLiv:\d+>> + /// CHECK: Exit + /// CHECK-EVAL: <<IfLiv>> + 1 == <<ArgUse>> + /// CHECK-EVAL: <<GotoLiv>> + 2 == <<ArgLoopUse>> + public static void loop1(boolean incoming) { while (incoming) {} } /// CHECK-START: void Main.loop2(boolean) liveness (after) - /// CHECK: ParameterValue liveness:4 ranges:{[4,44)} uses:[35,40,44] - /// CHECK: Goto liveness:38 - /// CHECK: Goto liveness:42 + /// CHECK: <<Arg:z\d+>> ParameterValue liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopUse2:\d+>>)} uses:[<<ArgUse:\d+>>,<<ArgLoopUse1:\d+>>,<<ArgLoopUse2>>] + /// CHECK: If [<<Arg>>] liveness:<<IfLiv:\d+>> + /// CHECK: Goto liveness:<<GotoLiv1:\d+>> + /// CHECK: Goto liveness:<<GotoLiv2:\d+>> + /// CHECK-EVAL: <<IfLiv>> + 1 == <<ArgUse>> + /// CHECK-EVAL: <<GotoLiv1>> < <<GotoLiv2>> + /// CHECK-EVAL: <<GotoLiv1>> + 2 == <<ArgLoopUse1>> + /// CHECK-EVAL: <<GotoLiv2>> + 2 == <<ArgLoopUse2>> + public static void loop2(boolean incoming) { while (true) { System.out.println("foo"); @@ -36,11 +47,14 @@ public class Main { } /// CHECK-START: void Main.loop3(boolean) liveness (after) - /// CHECK: ParameterValue liveness:4 ranges:{[4,60)} uses:[56,60] - /// CHECK: Goto liveness:58 + /// CHECK: <<Arg:z\d+>> ParameterValue liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopUse:\d+>>)} uses:[<<ArgUse:\d+>>,<<ArgLoopUse>>] + /// CHECK: Goto liveness:<<GotoLiv1:\d+>> + /// CHECK: InvokeVirtual [{{l\d+}},<<Arg>>] method_name:java.io.PrintStream.println liveness:<<InvokeLiv:\d+>> + /// CHECK: Goto liveness:<<GotoLiv2:\d+>> + /// CHECK-EVAL: <<InvokeLiv>> == <<ArgUse>> + /// CHECK-EVAL: <<GotoLiv1>> < <<GotoLiv2>> + /// CHECK-EVAL: <<GotoLiv2>> + 2 == <<ArgLoopUse>> - // CHECK-START: void Main.loop3(boolean) liveness (after) - // CHECK-NOT: Goto liveness:50 public static void loop3(boolean incoming) { // 'incoming' only needs a use at the outer loop's back edge. while (System.currentTimeMillis() != 42) { @@ -49,11 +63,11 @@ public class Main { } } - // CHECK-START: void Main.loop4(boolean) liveness (after) - // CHECK: ParameterValue liveness:4 ranges:{[4,22)} uses:[22] + /// CHECK-START: void Main.loop4(boolean) liveness (after) + /// CHECK: <<Arg:z\d+>> ParameterValue liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgUse:\d+>>)} uses:[<<ArgUse>>] + /// CHECK: InvokeVirtual [{{l\d+}},<<Arg>>] method_name:java.io.PrintStream.println liveness:<<InvokeLiv:\d+>> + /// CHECK-EVAL: <<InvokeLiv>> == <<ArgUse>> - // CHECK-START: void Main.loop4(boolean) liveness (after) - // CHECK-NOT: Goto liveness:18 public static void loop4(boolean incoming) { // 'incoming' has no loop use, so should not have back edge uses. System.out.println(incoming); @@ -63,59 +77,98 @@ public class Main { } /// CHECK-START: void Main.loop5(boolean) liveness (after) - /// CHECK: ParameterValue liveness:4 ranges:{[4,54)} uses:[37,46,50,54] - /// CHECK: Goto liveness:48 - /// CHECK: Goto liveness:52 + /// CHECK: <<Arg:z\d+>> ParameterValue liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopUse2:\d+>>)} uses:[<<ArgUse:\d+>>,<<ArgLoopUse1:\d+>>,<<ArgLoopUse2>>] + /// CHECK: InvokeVirtual [{{l\d+}},<<Arg>>] method_name:java.io.PrintStream.println liveness:<<InvokeLiv:\d+>> + /// CHECK: Goto liveness:<<GotoLiv1:\d+>> + /// CHECK: Goto liveness:<<GotoLiv2:\d+>> + /// CHECK: Exit + /// CHECK-EVAL: <<InvokeLiv>> == <<ArgUse>> + /// CHECK-EVAL: <<GotoLiv1>> < <<GotoLiv2>> + /// CHECK-EVAL: <<GotoLiv1>> + 2 == <<ArgLoopUse1>> + /// CHECK-EVAL: <<GotoLiv2>> + 2 == <<ArgLoopUse2>> + public static void loop5(boolean incoming) { // 'incoming' must have a use at both back edges. - while (Runtime.getRuntime() != null) { - while (incoming) { + for (long i = System.nanoTime(); i < 42; ++i) { + for (long j = System.currentTimeMillis(); j != 42; ++j) { System.out.println(incoming); } } } /// CHECK-START: void Main.loop6(boolean) liveness (after) - /// CHECK: ParameterValue liveness:4 ranges:{[4,50)} uses:[26,50] - /// CHECK: Goto liveness:48 + /// CHECK: <<Arg:z\d+>> ParameterValue liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopUse:\d+>>)} uses:[<<ArgUse:\d+>>,<<ArgLoopUse>>] + /// CHECK: InvokeVirtual [{{l\d+}},<<Arg>>] method_name:java.io.PrintStream.println liveness:<<InvokeLiv:\d+>> + /// CHECK: Add + /// CHECK: Goto liveness:<<GotoLiv1:\d+>> + /// CHECK: Add + /// CHECK: Goto liveness:<<GotoLiv2:\d+>> + /// CHECK: Exit + /// CHECK-EVAL: <<InvokeLiv>> == <<ArgUse>> + /// CHECK-EVAL: <<GotoLiv1>> < <<GotoLiv2>> + /// CHECK-EVAL: <<GotoLiv2>> + 2 == <<ArgLoopUse>> - /// CHECK-START: void Main.loop6(boolean) liveness (after) - /// CHECK-NOT: Goto liveness:24 public static void loop6(boolean incoming) { // 'incoming' must have a use only at the first loop's back edge. - while (true) { + for (long i = System.nanoTime(); i < 42; ++i) { System.out.println(incoming); - while (Runtime.getRuntime() != null) {} + for (long j = System.currentTimeMillis(); j != 42; ++j) {} } } /// CHECK-START: void Main.loop7(boolean) liveness (after) - /// CHECK: ParameterValue liveness:4 ranges:{[4,54)} uses:[36,45,50,54] - /// CHECK: Goto liveness:48 - /// CHECK: Goto liveness:52 + /// CHECK: <<Arg:z\d+>> ParameterValue liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopUse2:\d+>>)} uses:[<<ArgUse1:\d+>>,<<ArgUse2:\d+>>,<<ArgLoopUse1:\d+>>,<<ArgLoopUse2>>] + /// CHECK: InvokeVirtual [{{l\d+}},<<Arg>>] method_name:java.io.PrintStream.println liveness:<<InvokeLiv:\d+>> + /// CHECK: If [<<Arg>>] liveness:<<IfLiv:\d+>> + /// CHECK: Goto liveness:<<GotoLiv1:\d+>> + /// CHECK: Goto liveness:<<GotoLiv2:\d+>> + /// CHECK: Exit + /// CHECK-EVAL: <<InvokeLiv>> == <<ArgUse1>> + /// CHECK-EVAL: <<IfLiv>> + 1 == <<ArgUse2>> + /// CHECK-EVAL: <<GotoLiv1>> < <<GotoLiv2>> + /// CHECK-EVAL: <<GotoLiv1>> + 2 == <<ArgLoopUse1>> + /// CHECK-EVAL: <<GotoLiv2>> + 2 == <<ArgLoopUse2>> + public static void loop7(boolean incoming) { // 'incoming' must have a use at both back edges. while (Runtime.getRuntime() != null) { System.out.println(incoming); while (incoming) {} + System.nanoTime(); // beat back edge splitting } } /// CHECK-START: void Main.loop8() liveness (after) - /// CHECK: StaticFieldGet liveness:14 ranges:{[14,48)} uses:[39,44,48] - /// CHECK: Goto liveness:42 - /// CHECK: Goto liveness:46 + /// CHECK: <<Arg:z\d+>> StaticFieldGet liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopUse2:\d+>>)} uses:[<<ArgUse:\d+>>,<<ArgLoopUse1:\d+>>,<<ArgLoopUse2>>] + /// CHECK: If [<<Arg>>] liveness:<<IfLiv:\d+>> + /// CHECK: Goto liveness:<<GotoLiv1:\d+>> + /// CHECK: Goto liveness:<<GotoLiv2:\d+>> + /// CHECK: Exit + /// CHECK-EVAL: <<IfLiv>> + 1 == <<ArgUse>> + /// CHECK-EVAL: <<GotoLiv1>> < <<GotoLiv2>> + /// CHECK-EVAL: <<GotoLiv1>> + 2 == <<ArgLoopUse1>> + /// CHECK-EVAL: <<GotoLiv2>> + 2 == <<ArgLoopUse2>> + public static void loop8() { // 'incoming' must have a use at both back edges. boolean incoming = field; while (Runtime.getRuntime() != null) { + System.nanoTime(); // beat pre-header creation while (incoming) {} + System.nanoTime(); // beat back edge splitting } } /// CHECK-START: void Main.loop9() liveness (after) - /// CHECK: StaticFieldGet liveness:26 ranges:{[26,40)} uses:[35,40] - /// CHECK: Goto liveness:42 + /// CHECK: <<Arg:z\d+>> StaticFieldGet liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopUse:\d+>>)} uses:[<<ArgUse:\d+>>,<<ArgLoopUse>>] + /// CHECK: If [<<Arg>>] liveness:<<IfLiv:\d+>> + /// CHECK: Goto liveness:<<GotoLiv1:\d+>> + /// CHECK: Goto liveness:<<GotoLiv2:\d+>> + /// CHECK: Exit + /// CHECK-EVAL: <<IfLiv>> + 1 == <<ArgUse>> + /// CHECK-EVAL: <<GotoLiv1>> < <<GotoLiv2>> + /// CHECK-EVAL: <<GotoLiv1>> + 2 == <<ArgLoopUse>> + public static void loop9() { while (Runtime.getRuntime() != null) { // 'incoming' must only have a use in the inner loop. diff --git a/test/510-checker-try-catch/smali/Builder.smali b/test/510-checker-try-catch/smali/Builder.smali index 2274ba4d43..1fde5edc23 100644 --- a/test/510-checker-try-catch/smali/Builder.smali +++ b/test/510-checker-try-catch/smali/Builder.smali @@ -59,7 +59,7 @@ ## CHECK: StoreLocal [v0,<<Minus2>>] ## CHECK: name "<<BCatch3>>" -## CHECK: predecessors "<<BEnterTry1>>" "<<BExitTry1>>" "<<BEnterTry2>>" "<<BExitTry2>>" +## CHECK: predecessors "<<BEnterTry1>>" "<<BEnterTry2>>" "<<BExitTry1>>" "<<BExitTry2>>" ## CHECK: successors "<<BReturn>>" ## CHECK: flags "catch_block" ## CHECK: StoreLocal [v0,<<Minus3>>] @@ -70,18 +70,18 @@ ## CHECK: xhandlers "<<BCatch1>>" "<<BCatch3>>" ## CHECK: TryBoundary kind:entry -## CHECK: name "<<BExitTry1>>" -## CHECK: predecessors "<<BTry1>>" -## CHECK: successors "<<BAdd>>" -## CHECK: xhandlers "<<BCatch1>>" "<<BCatch3>>" -## CHECK: TryBoundary kind:exit - ## CHECK: name "<<BEnterTry2>>" ## CHECK: predecessors "<<BAdd>>" ## CHECK: successors "<<BTry2>>" ## CHECK: xhandlers "<<BCatch2>>" "<<BCatch3>>" ## CHECK: TryBoundary kind:entry +## CHECK: name "<<BExitTry1>>" +## CHECK: predecessors "<<BTry1>>" +## CHECK: successors "<<BAdd>>" +## CHECK: xhandlers "<<BCatch1>>" "<<BCatch3>>" +## CHECK: TryBoundary kind:exit + ## CHECK: name "<<BExitTry2>>" ## CHECK: predecessors "<<BTry2>>" ## CHECK: successors "<<BReturn>>" @@ -121,8 +121,7 @@ goto :return .end method -# Test that multiple try-entry blocks are generated if there are multiple entry -# points into the try block. +# Tests try-entry block when there are multiple entry points into the try block. ## CHECK-START: int Builder.testMultipleEntries(int, int, int, int) builder (after) @@ -142,20 +141,20 @@ ## CHECK: name "<<BTry1:B\d+>>" ## CHECK: predecessors "<<BEnterTry1>>" -## CHECK: successors "<<BTry2:B\d+>>" +## CHECK: successors "<<BExitTry1:B\d+>>" ## CHECK: Div -## CHECK: name "<<BTry2>>" -## CHECK: predecessors "<<BEnterTry2>>" "<<BTry1>>" -## CHECK: successors "<<BExitTry:B\d+>>" +## CHECK: name "<<BTry2:B\d+>>" +## CHECK: predecessors "<<BEnterTry2>>" +## CHECK: successors "<<BExitTry2:B\d+>>" ## CHECK: Div ## CHECK: name "<<BReturn:B\d+>>" -## CHECK: predecessors "<<BExitTry>>" "<<BCatch:B\d+>>" +## CHECK: predecessors "<<BExitTry2>>" "<<BCatch:B\d+>>" ## CHECK: Return ## CHECK: name "<<BCatch>>" -## CHECK: predecessors "<<BEnterTry1>>" "<<BEnterTry2>>" "<<BExitTry>>" +## CHECK: predecessors "<<BEnterTry1>>" "<<BEnterTry2>>" "<<BExitTry1>>" "<<BExitTry2>>" ## CHECK: successors "<<BReturn>>" ## CHECK: flags "catch_block" ## CHECK: StoreLocal [v0,<<Minus1>>] @@ -167,12 +166,18 @@ ## CHECK: TryBoundary kind:entry ## CHECK: name "<<BEnterTry2>>" -## CHECK: predecessors "<<BIf>>" +## CHECK: predecessors "<<BIf>>" "<<BExitTry1>>" ## CHECK: successors "<<BTry2>>" ## CHECK: xhandlers "<<BCatch>>" ## CHECK: TryBoundary kind:entry -## CHECK: name "<<BExitTry>>" +## CHECK: name "<<BExitTry1>>" +## CHECK: predecessors "<<BTry1>>" +## CHECK: successors "<<BEnterTry2>>" +## CHECK: xhandlers "<<BCatch>>" +## CHECK: TryBoundary kind:exit + +## CHECK: name "<<BExitTry2>>" ## CHECK: predecessors "<<BTry2>>" ## CHECK: successors "<<BReturn>>" ## CHECK: xhandlers "<<BCatch>>" @@ -314,18 +319,18 @@ ## CHECK: xhandlers "<<BCatch1>>" ## CHECK: TryBoundary kind:entry -## CHECK: name "<<BExit1>>" -## CHECK: predecessors "<<BTry1>>" -## CHECK: successors "<<BEnter2>>" -## CHECK: xhandlers "<<BCatch1>>" -## CHECK: TryBoundary kind:exit - ## CHECK: name "<<BEnter2>>" ## CHECK: predecessors "<<BExit1>>" ## CHECK: successors "<<BTry2>>" ## CHECK: xhandlers "<<BCatch2>>" ## CHECK: TryBoundary kind:entry +## CHECK: name "<<BExit1>>" +## CHECK: predecessors "<<BTry1>>" +## CHECK: successors "<<BEnter2>>" +## CHECK: xhandlers "<<BCatch1>>" +## CHECK: TryBoundary kind:exit + ## CHECK: name "<<BExit2>>" ## CHECK: predecessors "<<BTry2>>" ## CHECK: successors "<<BReturn>>" @@ -402,18 +407,18 @@ ## CHECK: xhandlers "<<BCatch1>>" ## CHECK: TryBoundary kind:entry -## CHECK: name "<<BExit1>>" -## CHECK: predecessors "<<BTry1>>" -## CHECK: successors "<<BReturn>>" -## CHECK: xhandlers "<<BCatch1>>" -## CHECK: TryBoundary kind:exit - ## CHECK: name "<<BEnter2>>" ## CHECK: predecessors "<<BGoto>>" ## CHECK: successors "<<BTry2>>" ## CHECK: xhandlers "<<BCatch2>>" ## CHECK: TryBoundary kind:entry +## CHECK: name "<<BExit1>>" +## CHECK: predecessors "<<BTry1>>" +## CHECK: successors "<<BReturn>>" +## CHECK: xhandlers "<<BCatch1>>" +## CHECK: TryBoundary kind:exit + ## CHECK: name "<<BExit2>>" ## CHECK: predecessors "<<BTry2>>" ## CHECK: successors "<<BEnter1>>" @@ -483,7 +488,7 @@ ## CHECK: StoreLocal [v0,<<Minus1>>] ## CHECK: name "<<BCatchAll>>" -## CHECK: predecessors "<<BEnter1>>" "<<BExit1>>" "<<BEnter2>>" "<<BExit2>>" "<<BEnter3>>" "<<BExit3>>" +## CHECK: predecessors "<<BEnter1>>" "<<BEnter2>>" "<<BEnter3>>" "<<BExit1>>" "<<BExit2>>" "<<BExit3>>" ## CHECK: successors "<<BReturn>>" ## CHECK: flags "catch_block" ## CHECK: StoreLocal [v0,<<Minus2>>] @@ -494,30 +499,30 @@ ## CHECK: xhandlers "<<BCatchAll>>" ## CHECK: TryBoundary kind:entry -## CHECK: name "<<BExit1>>" -## CHECK: predecessors "<<BTry1>>" -## CHECK: successors "<<BEnter2>>" -## CHECK: xhandlers "<<BCatchAll>>" -## CHECK: TryBoundary kind:exit - ## CHECK: name "<<BEnter2>>" ## CHECK: predecessors "<<BExit1>>" ## CHECK: successors "<<BTry2>>" ## CHECK: xhandlers "<<BCatchArith>>" "<<BCatchAll>>" ## CHECK: TryBoundary kind:entry -## CHECK: name "<<BExit2>>" -## CHECK: predecessors "<<BTry2>>" -## CHECK: successors "<<BEnter3>>" -## CHECK: xhandlers "<<BCatchArith>>" "<<BCatchAll>>" -## CHECK: TryBoundary kind:exit - ## CHECK: name "<<BEnter3>>" ## CHECK: predecessors "<<BExit2>>" ## CHECK: successors "<<BTry3>>" ## CHECK: xhandlers "<<BCatchAll>>" ## CHECK: TryBoundary kind:entry +## CHECK: name "<<BExit1>>" +## CHECK: predecessors "<<BTry1>>" +## CHECK: successors "<<BEnter2>>" +## CHECK: xhandlers "<<BCatchAll>>" +## CHECK: TryBoundary kind:exit + +## CHECK: name "<<BExit2>>" +## CHECK: predecessors "<<BTry2>>" +## CHECK: successors "<<BEnter3>>" +## CHECK: xhandlers "<<BCatchArith>>" "<<BCatchAll>>" +## CHECK: TryBoundary kind:exit + ## CHECK: name "<<BExit3>>" ## CHECK: predecessors "<<BTry3>>" ## CHECK: successors "<<BReturn>>" @@ -577,7 +582,7 @@ ## CHECK: Div ## CHECK: name "<<BCatch>>" -## CHECK: predecessors "<<BEnterTry1>>" "<<BExitTry1>>" "<<BEnterTry2>>" "<<BExitTry2>>" +## CHECK: predecessors "<<BEnterTry1>>" "<<BEnterTry2>>" "<<BExitTry1>>" "<<BExitTry2>>" ## CHECK: successors "<<BReturn>>" ## CHECK: flags "catch_block" ## CHECK: StoreLocal [v0,<<Minus1>>] @@ -588,18 +593,18 @@ ## CHECK: xhandlers "<<BCatch>>" ## CHECK: TryBoundary kind:entry -## CHECK: name "<<BExitTry1>>" -## CHECK: predecessors "<<BTry1>>" -## CHECK: successors "<<BOutside>>" -## CHECK: xhandlers "<<BCatch>>" -## CHECK: TryBoundary kind:exit - ## CHECK: name "<<BEnterTry2>>" ## CHECK: predecessors "<<BOutside>>" ## CHECK: successors "<<BTry2>>" ## CHECK: xhandlers "<<BCatch>>" ## CHECK: TryBoundary kind:entry +## CHECK: name "<<BExitTry1>>" +## CHECK: predecessors "<<BTry1>>" +## CHECK: successors "<<BOutside>>" +## CHECK: xhandlers "<<BCatch>>" +## CHECK: TryBoundary kind:exit + ## CHECK: name "<<BExitTry2>>" ## CHECK: predecessors "<<BTry2>>" ## CHECK: successors "<<BReturn>>" @@ -647,21 +652,21 @@ ## CHECK: name "<<BTry1:B\d+>>" ## CHECK: predecessors "<<BEnterTry1>>" -## CHECK: successors "<<BTry2:B\d+>>" +## CHECK: successors "<<BExitTry1:B\d+>>" ## CHECK: Div -## CHECK: name "<<BTry2>>" -## CHECK: predecessors "<<BEnterTry2>>" "<<BTry1>>" -## CHECK: successors "<<BExitTry:B\d+>>" +## CHECK: name "<<BTry2:B\d+>>" +## CHECK: predecessors "<<BEnterTry2>>" +## CHECK: successors "<<BExitTry2:B\d+>>" ## CHECK: Div ## CHECK: name "<<BOutside>>" -## CHECK: predecessors "<<BPSwitch1>>" "<<BExitTry>>" +## CHECK: predecessors "<<BPSwitch1>>" "<<BExitTry2>>" ## CHECK: successors "<<BCatchReturn:B\d+>>" ## CHECK: Div ## CHECK: name "<<BCatchReturn>>" -## CHECK: predecessors "<<BOutside>>" "<<BEnterTry1>>" "<<BEnterTry2>>" "<<BExitTry>>" +## CHECK: predecessors "<<BOutside>>" "<<BEnterTry1>>" "<<BEnterTry2>>" "<<BExitTry1>>" "<<BExitTry2>>" ## CHECK: flags "catch_block" ## CHECK: Return @@ -677,7 +682,13 @@ ## CHECK: xhandlers "<<BCatchReturn>>" ## CHECK: TryBoundary kind:entry -## CHECK: name "<<BExitTry>>" +## CHECK: name "<<BExitTry1>>" +## CHECK: predecessors "<<BTry1>>" +## CHECK: successors "<<BEnterTry2>>" +## CHECK: xhandlers "<<BCatchReturn>>" +## CHECK: TryBoundary kind:exit + +## CHECK: name "<<BExitTry2>>" ## CHECK: predecessors "<<BTry2>>" ## CHECK: successors "<<BOutside>>" ## CHECK: xhandlers "<<BCatchReturn>>" @@ -741,7 +752,7 @@ ## CHECK: Div ## CHECK: name "<<BCatchReturn>>" -## CHECK: predecessors "<<BOutside>>" "<<BEnterTry1>>" "<<BExitTry1>>" "<<BEnterTry2>>" "<<BExitTry2>>" +## CHECK: predecessors "<<BOutside>>" "<<BEnterTry1>>" "<<BEnterTry2>>" "<<BExitTry1>>" "<<BExitTry2>>" ## CHECK: flags "catch_block" ## CHECK: Return @@ -751,18 +762,18 @@ ## CHECK: xhandlers "<<BCatchReturn>>" ## CHECK: TryBoundary kind:entry -## CHECK: name "<<BExitTry1>>" -## CHECK: predecessors "<<BPSwitch0>>" -## CHECK: successors "<<BPSwitch1>>" -## CHECK: xhandlers "<<BCatchReturn>>" -## CHECK: TryBoundary kind:exit - ## CHECK: name "<<BEnterTry2>>" ## CHECK: predecessors "<<BPSwitch1>>" ## CHECK: successors "<<BTry1>>" ## CHECK: xhandlers "<<BCatchReturn>>" ## CHECK: TryBoundary kind:entry +## CHECK: name "<<BExitTry1>>" +## CHECK: predecessors "<<BPSwitch0>>" +## CHECK: successors "<<BPSwitch1>>" +## CHECK: xhandlers "<<BCatchReturn>>" +## CHECK: TryBoundary kind:exit + ## CHECK: name "<<BExitTry2>>" ## CHECK: predecessors "<<BTry2>>" ## CHECK: successors "<<BOutside>>" @@ -907,7 +918,7 @@ ## CHECK: Div ## CHECK: name "<<BCatch:B\d+>>" -## CHECK: predecessors "<<BExitTry1>>" "<<BEnterTry1>>" "<<BExitTry1>>" "<<BEnterTry2:B\d+>>" "<<BExitTry2:B\d+>>" +## CHECK: predecessors "<<BExitTry1>>" "<<BEnterTry1>>" "<<BEnterTry2:B\d+>>" "<<BExitTry1>>" "<<BExitTry2:B\d+>>" ## CHECK: successors "<<BEnterTry2>>" ## CHECK: flags "catch_block" @@ -928,18 +939,18 @@ ## CHECK: xhandlers "<<BCatch>>" ## CHECK: TryBoundary kind:entry -## CHECK: name "<<BExitTry1>>" -## CHECK: predecessors "<<BTry1>>" -## CHECK: successors "<<BCatch>>" -## CHECK: xhandlers "<<BCatch>>" -## CHECK: TryBoundary kind:exit - ## CHECK: name "<<BEnterTry2>>" ## CHECK: predecessors "<<BCatch>>" ## CHECK: successors "<<BTry2>>" ## CHECK: xhandlers "<<BCatch>>" ## CHECK: TryBoundary kind:entry +## CHECK: name "<<BExitTry1>>" +## CHECK: predecessors "<<BTry1>>" +## CHECK: successors "<<BCatch>>" +## CHECK: xhandlers "<<BCatch>>" +## CHECK: TryBoundary kind:exit + ## CHECK: name "<<BExitTry2>>" ## CHECK: predecessors "<<BTry2>>" ## CHECK: successors "<<BReturn>>" @@ -1001,18 +1012,18 @@ ## CHECK: xhandlers "<<BCatch2>>" ## CHECK: TryBoundary kind:entry -## CHECK: name "<<BExitTry1>>" -## CHECK: predecessors "<<BTry1>>" -## CHECK: successors "<<BCatch2>>" -## CHECK: xhandlers "<<BCatch2>>" -## CHECK: TryBoundary kind:exit - ## CHECK: name "<<BEnterTry2>>" ## CHECK: predecessors "<<BCatch2>>" ## CHECK: successors "<<BTry2>>" ## CHECK: xhandlers "<<BCatch1>>" ## CHECK: TryBoundary kind:entry +## CHECK: name "<<BExitTry1>>" +## CHECK: predecessors "<<BTry1>>" +## CHECK: successors "<<BCatch2>>" +## CHECK: xhandlers "<<BCatch2>>" +## CHECK: TryBoundary kind:exit + ## CHECK: name "<<BExitTry2>>" ## CHECK: predecessors "<<BTry2>>" ## CHECK: successors "<<BReturn>>" @@ -1037,6 +1048,52 @@ return p0 .end method +# Test graph with try/catch inside a loop. + +## CHECK-START: int Builder.testTryInLoop(int, int) builder (after) + +## CHECK: name "B0" +## CHECK: successors "<<BEnterTry:B\d+>>" + +## CHECK: name "<<BTry:B\d+>>" +## CHECK: predecessors "<<BEnterTry>>" +## CHECK: successors "<<BExitTry:B\d+>>" +## CHECK: Div + +## CHECK: name "<<BCatch:B\d+>>" +## CHECK: predecessors "<<BEnterTry>>" "<<BExitTry>>" +## CHECK: successors "<<BEnterTry>>" +## CHECK: flags "catch_block" + +## CHECK: name "<<BExit:B\d+>>" +## CHECK-NOT: predecessors "{{B\d+}}" +## CHECK: end_block + +## CHECK: name "<<BEnterTry>>" +## CHECK: predecessors "B0" +## CHECK: successors "<<BTry>>" +## CHECK: xhandlers "<<BCatch>>" +## CHECK: TryBoundary kind:entry + +## CHECK: name "<<BExitTry>>" +## CHECK: predecessors "<<BTry>>" +## CHECK: successors "<<BEnterTry>>" +## CHECK: xhandlers "<<BCatch>>" +## CHECK: TryBoundary kind:exit + +.method public static testTryInLoop(II)I + .registers 3 + + :try_start + div-int/2addr p0, p1 + goto :try_start + :try_end + .catchall {:try_start .. :try_end} :catch_all + + :catch_all + goto :try_start +.end method + # Test that a MOVE_RESULT instruction is placed into the same block as the # INVOKE it follows, even if there is a try boundary between them. diff --git a/test/530-checker-loops/src/Main.java b/test/530-checker-loops/src/Main.java index e518a61f88..1c5b5d65b9 100644 --- a/test/530-checker-loops/src/Main.java +++ b/test/530-checker-loops/src/Main.java @@ -62,6 +62,19 @@ public class Main { return result; } + /// CHECK-START: int Main.linearVeryObscure(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-START: int Main.linearVeryObscure(int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + private static int linearVeryObscure(int[] x) { + int result = 0; + for (int i = 0; i < x.length; i++) { + int k = (-i) + (i << 5) + i - (32 * i) + 5 + (int) i; + result += x[k - 5]; + } + return result; + } + /// CHECK-START: int Main.linearWhile(int[]) BCE (before) /// CHECK-DAG: BoundsCheck /// CHECK-START: int Main.linearWhile(int[]) BCE (after) @@ -75,6 +88,42 @@ public class Main { return result; } + /// CHECK-START: int Main.linearThreeWayPhi(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-START: int Main.linearThreeWayPhi(int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + private static int linearThreeWayPhi(int[] x) { + int result = 0; + for (int i = 0; i < x.length; ) { + if (x[i] == 5) { + i++; + continue; + } + result += x[i++]; + } + return result; + } + + /// CHECK-START: int Main.linearFourWayPhi(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-START: int Main.linearFourWayPhi(int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + private static int linearFourWayPhi(int[] x) { + int result = 0; + for (int i = 0; i < x.length; ) { + if (x[i] == 5) { + i++; + continue; + } else if (x[i] == 6) { + i++; + result += 7; + continue; + } + result += x[i++]; + } + return result; + } + /// CHECK-START: int Main.wrapAroundThenLinear(int[]) BCE (before) /// CHECK-DAG: BoundsCheck /// CHECK-START: int Main.wrapAroundThenLinear(int[]) BCE (after) @@ -90,6 +139,25 @@ public class Main { return result; } + /// CHECK-START: int Main.wrapAroundThenLinearThreeWayPhi(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-START: int Main.wrapAroundThenLinearThreeWayPhi(int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + private static int wrapAroundThenLinearThreeWayPhi(int[] x) { + // Loop with wrap around (length - 1, 0, 1, 2, ..). + int w = x.length - 1; + int result = 0; + for (int i = 0; i < x.length; ) { + if (x[w] == 1) { + w = i++; + continue; + } + result += x[w]; + w = i++; + } + return result; + } + /// CHECK-START: int[] Main.linearWithParameter(int) BCE (before) /// CHECK-DAG: BoundsCheck /// CHECK-START: int[] Main.linearWithParameter(int) BCE (after) @@ -102,6 +170,19 @@ public class Main { return x; } + /// CHECK-START: int[] Main.linearCopy(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-START: int[] Main.linearCopy(int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + private static int[] linearCopy(int x[]) { + int n = x.length; + int y[] = new int[n]; + for (int i = 0; i < n; i++) { + y[i] = x[i]; + } + return y; + } + /// CHECK-START: int Main.linearWithCompoundStride() BCE (before) /// CHECK-DAG: BoundsCheck /// CHECK-START: int Main.linearWithCompoundStride() BCE (after) @@ -126,7 +207,7 @@ public class Main { int result = 0; int k = 0; // Range analysis has no problem with a trip-count defined by a - // reasonably large positive stride. + // reasonably large positive stride far away from upper bound. for (int i = 1; i <= 10 * 10000000 + 1; i += 10000000) { result += x[k++]; } @@ -143,7 +224,7 @@ public class Main { int k = 0; // Range analysis conservatively bails due to potential of wrap-around // arithmetic while computing the trip-count for this very large stride. - for (int i = 1; i < 2147483647; i += 195225786) { + for (int i = 1; i < Integer.MAX_VALUE; i += 195225786) { result += x[k++]; } return result; @@ -158,7 +239,7 @@ public class Main { int result = 0; int k = 0; // Range analysis has no problem with a trip-count defined by a - // reasonably large negative stride. + // reasonably large negative stride far away from lower bound. for (int i = -1; i >= -10 * 10000000 - 1; i -= 10000000) { result += x[k++]; } @@ -175,12 +256,54 @@ public class Main { int k = 0; // Range analysis conservatively bails due to potential of wrap-around // arithmetic while computing the trip-count for this very large stride. - for (int i = -2; i > -2147483648; i -= 195225786) { + for (int i = -2; i > Integer.MIN_VALUE; i -= 195225786) { result += x[k++]; } return result; } + /// CHECK-START: int Main.linearForNE() BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-START: int Main.linearForNE() BCE (after) + /// CHECK-NOT: BoundsCheck + private static int linearForNE() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = 0; i != 10; i++) { + result += x[i]; + } + return result; + } + + /// CHECK-START: int Main.linearDoWhile() BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-START: int Main.linearDoWhile() BCE (after) + /// CHECK-DAG: BoundsCheck + private static int linearDoWhile() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + int i = 0; + // TODO: make this work + do { + result += x[i++]; + } while (i < 10); + return result; + } + + /// CHECK-START: int Main.linearShort() BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-START: int Main.linearShort() BCE (after) + /// CHECK-DAG: BoundsCheck + private static int linearShort() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + // TODO: make this work + for (short i = 0; i < 10; i++) { + result += x[i]; + } + return result; + } + /// CHECK-START: int Main.periodicIdiom(int) BCE (before) /// CHECK-DAG: BoundsCheck /// CHECK-START: int Main.periodicIdiom(int) BCE (after) @@ -242,6 +365,112 @@ public class Main { return result; } + /// CHECK-START: int Main.justRightUp1() BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-START: int Main.justRightUp1() BCE (after) + /// CHECK-NOT: BoundsCheck + private static int justRightUp1() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MAX_VALUE - 10, k = 0; i < Integer.MAX_VALUE; i++) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justRightUp2() BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-START: int Main.justRightUp2() BCE (after) + /// CHECK-NOT: BoundsCheck + private static int justRightUp2() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MAX_VALUE - 10; i < Integer.MAX_VALUE; i++) { + result += x[i - Integer.MAX_VALUE + 10]; + } + return result; + } + + /// CHECK-START: int Main.justRightUp3() BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-START: int Main.justRightUp3() BCE (after) + /// CHECK-NOT: BoundsCheck + private static int justRightUp3() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MAX_VALUE - 10, k = 0; i <= Integer.MAX_VALUE - 1; i++) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justOOBUp() BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-START: int Main.justOOBUp() BCE (after) + /// CHECK-DAG: BoundsCheck + private static int justOOBUp() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + // Infinite loop! + for (int i = Integer.MAX_VALUE - 9, k = 0; i <= Integer.MAX_VALUE; i++) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justRightDown1() BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-START: int Main.justRightDown1() BCE (after) + /// CHECK-NOT: BoundsCheck + private static int justRightDown1() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MIN_VALUE + 10, k = 0; i > Integer.MIN_VALUE; i--) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justRightDown2() BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-START: int Main.justRightDown2() BCE (after) + /// CHECK-NOT: BoundsCheck + private static int justRightDown2() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MIN_VALUE + 10; i > Integer.MIN_VALUE; i--) { + result += x[Integer.MAX_VALUE + i]; + } + return result; + } + + /// CHECK-START: int Main.justRightDown3() BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-START: int Main.justRightDown3() BCE (after) + /// CHECK-NOT: BoundsCheck + private static int justRightDown3() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MIN_VALUE + 10, k = 0; i >= Integer.MIN_VALUE + 1; i--) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justOOBDown() BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-START: int Main.justOOBDown() BCE (after) + /// CHECK-DAG: BoundsCheck + private static int justOOBDown() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + // Infinite loop! + for (int i = Integer.MIN_VALUE + 9, k = 0; i >= Integer.MIN_VALUE; i--) { + result += x[k++]; + } + return result; + } + // // Cases that actually go out of bounds. These test cases // ensure the exceptions are thrown at the right places. @@ -274,10 +503,18 @@ public class Main { expectEquals(55, linearDown(x)); expectEquals(0, linearObscure(empty)); expectEquals(55, linearObscure(x)); + expectEquals(0, linearVeryObscure(empty)); + expectEquals(55, linearVeryObscure(x)); expectEquals(0, linearWhile(empty)); expectEquals(55, linearWhile(x)); + expectEquals(0, linearThreeWayPhi(empty)); + expectEquals(50, linearThreeWayPhi(x)); + expectEquals(0, linearFourWayPhi(empty)); + expectEquals(51, linearFourWayPhi(x)); expectEquals(0, wrapAroundThenLinear(empty)); expectEquals(55, wrapAroundThenLinear(x)); + expectEquals(0, wrapAroundThenLinearThreeWayPhi(empty)); + expectEquals(54, wrapAroundThenLinearThreeWayPhi(x)); // Linear with parameter. sResult = 0; @@ -295,6 +532,16 @@ public class Main { } } + // Linear copy. + expectEquals(0, linearCopy(empty).length); + { + int[] r = linearCopy(x); + expectEquals(x.length, r.length); + for (int i = 0; i < x.length; i++) { + expectEquals(x[i], r[i]); + } + } + // Linear with non-unit strides. expectEquals(56, linearWithCompoundStride()); expectEquals(66, linearWithLargePositiveStride()); @@ -302,6 +549,11 @@ public class Main { expectEquals(66, linearWithLargeNegativeStride()); expectEquals(66, linearWithVeryLargeNegativeStride()); + // Special forms. + expectEquals(55, linearForNE()); + expectEquals(55, linearDoWhile()); + expectEquals(55, linearShort()); + // Periodic adds (1, 3), one at the time. expectEquals(0, periodicIdiom(-1)); for (int tc = 0; tc < 32; tc++) { @@ -326,6 +578,28 @@ public class Main { expectEquals(tc * 16, periodicSequence4(tc)); } + // Large bounds. + expectEquals(55, justRightUp1()); + expectEquals(55, justRightUp2()); + expectEquals(55, justRightUp3()); + expectEquals(55, justRightDown1()); + expectEquals(55, justRightDown2()); + expectEquals(55, justRightDown3()); + sResult = 0; + try { + justOOBUp(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult = 1; + } + expectEquals(1, sResult); + sResult = 0; + try { + justOOBDown(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult = 1; + } + expectEquals(1, sResult); + // Lower bound goes OOB. sResult = 0; try { |