diff options
author | Simon Ochsenreither <simon@ochsenreither.de> | 2015-09-16 17:31:02 +0200 |
---|---|---|
committer | Simon Ochsenreither <simon@ochsenreither.de> | 2015-10-27 15:43:08 +0100 |
commit | 1a8daa2d6cbf46a7cdd9180c11c641adabcf6d92 (patch) | |
tree | b7014fdfe397864c621322bfe00ff3307df5017e /test/pending | |
parent | 4321ea458ad1258f273ee22a4c6a7606ab054501 (diff) | |
download | scala-1a8daa2d6cbf46a7cdd9180c11c641adabcf6d92.tar.gz scala-1a8daa2d6cbf46a7cdd9180c11c641adabcf6d92.tar.bz2 scala-1a8daa2d6cbf46a7cdd9180c11c641adabcf6d92.zip |
Remove GenASM, merge remaining common code snippets
With GenBCode being the default and only supported backend for Java 8,
we can get rid of GenASM.
This commit also fixes/migrates/moves to pending/deletes tests which
depended on GenASM before.
Diffstat (limited to 'test/pending')
31 files changed, 1503 insertions, 0 deletions
diff --git a/test/pending/jvm/constant-optimization/Foo_1.flags b/test/pending/jvm/constant-optimization/Foo_1.flags new file mode 100644 index 0000000000..9691c0985d --- /dev/null +++ b/test/pending/jvm/constant-optimization/Foo_1.flags @@ -0,0 +1 @@ +// constant otimization not there yet, -Yopt:nullness-tracking not enough. diff --git a/test/pending/jvm/constant-optimization/Foo_1.scala b/test/pending/jvm/constant-optimization/Foo_1.scala new file mode 100644 index 0000000000..6f408044d7 --- /dev/null +++ b/test/pending/jvm/constant-optimization/Foo_1.scala @@ -0,0 +1,9 @@ +class Foo_1 { + def foo() { + // constant optimization should eliminate all branches + val i = 1 + val x = if (i != 1) null else "good" + val y = if (x == null) "good" else x + "" + println(y) + } +}
\ No newline at end of file diff --git a/test/pending/jvm/constant-optimization/Test.scala b/test/pending/jvm/constant-optimization/Test.scala new file mode 100644 index 0000000000..dc0f8f6103 --- /dev/null +++ b/test/pending/jvm/constant-optimization/Test.scala @@ -0,0 +1,27 @@ + +import scala.tools.partest.BytecodeTest +import scala.tools.asm +import asm.tree.InsnList +import scala.collection.JavaConverters._ + +object Test extends BytecodeTest { + val comparisons = Set(asm.Opcodes.IF_ACMPEQ, asm.Opcodes.IF_ACMPNE, asm.Opcodes.IF_ICMPEQ, asm.Opcodes.IF_ICMPGE, asm.Opcodes.IF_ICMPGT, asm.Opcodes.IF_ICMPLE, + asm.Opcodes.IF_ICMPLT, asm.Opcodes.IF_ICMPNE, asm.Opcodes.IFEQ, asm.Opcodes.IFGE, asm.Opcodes.IFGT, asm.Opcodes.IFLE, asm.Opcodes.IFLT, + asm.Opcodes.IFNE, asm.Opcodes.IFNONNULL, asm.Opcodes.IFNULL) + + def show: Unit = { + val classNode = loadClassNode("Foo_1") + val methodNode = getMethod(classNode, "foo") + // after optimization there should be no comparisons left + val expected = 0 + + val got = countComparisons(methodNode.instructions) + assert(got == expected, s"expected $expected but got $got comparisons") + } + + def countComparisons(insnList: InsnList): Int = { + def isComparison(node: asm.tree.AbstractInsnNode): Boolean = + (comparisons contains node.getOpcode) + insnList.iterator.asScala count isComparison + } +}
\ No newline at end of file diff --git a/test/pending/jvm/patmat_opt_ignore_underscore.check b/test/pending/jvm/patmat_opt_ignore_underscore.check new file mode 100644 index 0000000000..43f53aba12 --- /dev/null +++ b/test/pending/jvm/patmat_opt_ignore_underscore.check @@ -0,0 +1 @@ +bytecode identical diff --git a/test/pending/jvm/patmat_opt_ignore_underscore.flags b/test/pending/jvm/patmat_opt_ignore_underscore.flags new file mode 100644 index 0000000000..453b6b7895 --- /dev/null +++ b/test/pending/jvm/patmat_opt_ignore_underscore.flags @@ -0,0 +1 @@ +-Yopt:l:project
\ No newline at end of file diff --git a/test/pending/jvm/patmat_opt_ignore_underscore/Analyzed_1.scala b/test/pending/jvm/patmat_opt_ignore_underscore/Analyzed_1.scala new file mode 100644 index 0000000000..b0506018f6 --- /dev/null +++ b/test/pending/jvm/patmat_opt_ignore_underscore/Analyzed_1.scala @@ -0,0 +1,29 @@ +// this class's bytecode, compiled under -optimize is analyzed by the test +// method a's bytecode should be identical to method b's bytecode +// this is not the best test for shielding against regressing on this particular issue, +// but it sets the stage for checking the bytecode emitted by the pattern matcher and +// comparing it to manually tuned code using if/then/else etc. +class SameBytecode { + case class Foo(x: Any, y: String) + + def a = + Foo(1, "a") match { + case Foo(_: String, y) => y + } + + // this method's body holds the tree that should be generated by the pattern matcher for method a (-Xprint:patmat) + // the test checks that bytecode for a and b is identical (modulo line numbers) + // we can't diff trees as they are quite different (patmat uses jumps to labels that cannot be expressed in source, for example) + // note that the actual tree is quite bad: we do an unnecessary null check, isInstanceOf and local val (x3) + // some of these will be fixed soon (the initial null check is for the scrutinee, which is harder to fix in patmat) + def b: String = { + val x1 = Foo(1, "a") + if (x1.ne(null)) { + if (x1.x.isInstanceOf[String]) { + return x1.y + } + } + + throw new MatchError(x1) + } +}
\ No newline at end of file diff --git a/test/pending/jvm/patmat_opt_ignore_underscore/test.scala b/test/pending/jvm/patmat_opt_ignore_underscore/test.scala new file mode 100644 index 0000000000..d6630e80a0 --- /dev/null +++ b/test/pending/jvm/patmat_opt_ignore_underscore/test.scala @@ -0,0 +1,18 @@ +/* + * filter: inliner warning; re-run with + */ +import scala.tools.partest.BytecodeTest + +import scala.tools.nsc.util.JavaClassPath +import java.io.InputStream +import scala.tools.asm +import asm.ClassReader +import asm.tree.{ClassNode, InsnList} +import scala.collection.JavaConverters._ + +object Test extends BytecodeTest { + def show: Unit = { + val classNode = loadClassNode("SameBytecode") + sameBytecode(getMethod(classNode, "a"), getMethod(classNode, "b")) + } +} diff --git a/test/pending/jvm/patmat_opt_no_nullcheck.check b/test/pending/jvm/patmat_opt_no_nullcheck.check new file mode 100644 index 0000000000..43f53aba12 --- /dev/null +++ b/test/pending/jvm/patmat_opt_no_nullcheck.check @@ -0,0 +1 @@ +bytecode identical diff --git a/test/pending/jvm/patmat_opt_no_nullcheck.flags b/test/pending/jvm/patmat_opt_no_nullcheck.flags new file mode 100644 index 0000000000..453b6b7895 --- /dev/null +++ b/test/pending/jvm/patmat_opt_no_nullcheck.flags @@ -0,0 +1 @@ +-Yopt:l:project
\ No newline at end of file diff --git a/test/pending/jvm/patmat_opt_no_nullcheck/Analyzed_1.scala b/test/pending/jvm/patmat_opt_no_nullcheck/Analyzed_1.scala new file mode 100644 index 0000000000..1e4d564cdf --- /dev/null +++ b/test/pending/jvm/patmat_opt_no_nullcheck/Analyzed_1.scala @@ -0,0 +1,24 @@ +// this class's bytecode, compiled under -optimize is analyzed by the test +// method a's bytecode should be identical to method b's bytecode +case class Foo(x: Any) + +class SameBytecode { + def a = + (Foo(1): Any) match { + case Foo(_: String) => + } + + // there's no null check + def b: Unit = { + val x1: Any = Foo(1) + if (x1.isInstanceOf[Foo]) { + val x3 = x1.asInstanceOf[Foo] + if (x3.x.isInstanceOf[String]) { + val x = () + return + } + } + + throw new MatchError(x1) + } +}
\ No newline at end of file diff --git a/test/pending/jvm/patmat_opt_no_nullcheck/test.scala b/test/pending/jvm/patmat_opt_no_nullcheck/test.scala new file mode 100644 index 0000000000..5a4a398b67 --- /dev/null +++ b/test/pending/jvm/patmat_opt_no_nullcheck/test.scala @@ -0,0 +1,14 @@ +/* + * filter: inliner warning; re-run with + */ +import scala.tools.partest.{ BytecodeTest, ASMConverters } + +object Test extends BytecodeTest { + def show: Unit = { + val classNode = loadClassNode("SameBytecode") + // ASM and GenBCode assign variable slots slightly differently + val instrsA = ASMConverters.instructionsFromMethod(getMethod(classNode, "a")) + val instrsB = ASMConverters.instructionsFromMethod(getMethod(classNode, "b")) + assert(ASMConverters.equivalentBytecode(instrsA, instrsB), diffInstructions(instrsA, instrsB)) // doesn't work + } +} diff --git a/test/pending/jvm/patmat_opt_primitive_typetest.check b/test/pending/jvm/patmat_opt_primitive_typetest.check new file mode 100644 index 0000000000..43f53aba12 --- /dev/null +++ b/test/pending/jvm/patmat_opt_primitive_typetest.check @@ -0,0 +1 @@ +bytecode identical diff --git a/test/pending/jvm/patmat_opt_primitive_typetest.flags b/test/pending/jvm/patmat_opt_primitive_typetest.flags new file mode 100644 index 0000000000..19c578e4ad --- /dev/null +++ b/test/pending/jvm/patmat_opt_primitive_typetest.flags @@ -0,0 +1 @@ +-Yopt:l:project diff --git a/test/pending/jvm/patmat_opt_primitive_typetest/Analyzed_1.scala b/test/pending/jvm/patmat_opt_primitive_typetest/Analyzed_1.scala new file mode 100644 index 0000000000..c961082fa7 --- /dev/null +++ b/test/pending/jvm/patmat_opt_primitive_typetest/Analyzed_1.scala @@ -0,0 +1,24 @@ +// this class's bytecode, compiled under -optimize is analyzed by the test +// method a's bytecode should be identical to method b's bytecode +class SameBytecode { + case class Foo(x: Int, y: String) + + def a = + Foo(1, "a") match { + case Foo(_: Int, y) => y + } + + // this method's body holds the tree that should be generated by the pattern matcher for method a (-Xprint:patmat) + // the test checks that bytecode for a and b is identical (modulo line numbers) + // we can't diff trees as they are quite different (patmat uses jumps to labels that cannot be expressed in source, for example) + // note that the actual tree is quite bad: we do an unnecessary null check, and local val (x3) + // some of these will be fixed soon (the initial null check is for the scrutinee, which is harder to fix in patmat) + def b: String = { + val x1 = Foo(1, "a") + if (x1.ne(null)) { + return x1.y + } + + throw new MatchError(x1) + } +}
\ No newline at end of file diff --git a/test/pending/jvm/patmat_opt_primitive_typetest/test.scala b/test/pending/jvm/patmat_opt_primitive_typetest/test.scala new file mode 100644 index 0000000000..2927e763d5 --- /dev/null +++ b/test/pending/jvm/patmat_opt_primitive_typetest/test.scala @@ -0,0 +1,8 @@ +import scala.tools.partest.BytecodeTest + +object Test extends BytecodeTest { + def show: Unit = { + val classNode = loadClassNode("SameBytecode") + sameBytecode(getMethod(classNode, "a"), getMethod(classNode, "b")) + } +} diff --git a/test/pending/jvm/t7006.check b/test/pending/jvm/t7006.check new file mode 100644 index 0000000000..6294b14d62 --- /dev/null +++ b/test/pending/jvm/t7006.check @@ -0,0 +1,29 @@ +[running phase parser on Foo_1.scala] +[running phase namer on Foo_1.scala] +[running phase packageobjects on Foo_1.scala] +[running phase typer on Foo_1.scala] +[running phase patmat on Foo_1.scala] +[running phase superaccessors on Foo_1.scala] +[running phase extmethods on Foo_1.scala] +[running phase pickler on Foo_1.scala] +[running phase refchecks on Foo_1.scala] +[running phase uncurry on Foo_1.scala] +[running phase tailcalls on Foo_1.scala] +[running phase specialize on Foo_1.scala] +[running phase explicitouter on Foo_1.scala] +[running phase erasure on Foo_1.scala] +[running phase posterasure on Foo_1.scala] +[running phase lazyvals on Foo_1.scala] +[running phase lambdalift on Foo_1.scala] +[running phase constructors on Foo_1.scala] +[running phase flatten on Foo_1.scala] +[running phase mixin on Foo_1.scala] +[running phase cleanup on Foo_1.scala] +[running phase delambdafy on Foo_1.scala] +[running phase icode on Foo_1.scala] +[running phase inliner on Foo_1.scala] +[running phase inlinehandlers on Foo_1.scala] +[running phase closelim on Foo_1.scala] +[running phase constopt on Foo_1.scala] +[running phase dce on Foo_1.scala] +[running phase jvm on icode] diff --git a/test/pending/jvm/t7006/Foo_1.flags b/test/pending/jvm/t7006/Foo_1.flags new file mode 100644 index 0000000000..5d1b6b2644 --- /dev/null +++ b/test/pending/jvm/t7006/Foo_1.flags @@ -0,0 +1 @@ +-Yopt:l:project -Ydebug -Xfatal-warnings diff --git a/test/pending/jvm/t7006/Foo_1.scala b/test/pending/jvm/t7006/Foo_1.scala new file mode 100644 index 0000000000..3985557d9f --- /dev/null +++ b/test/pending/jvm/t7006/Foo_1.scala @@ -0,0 +1,10 @@ +class Foo_1 { + def foo { + try { + val x = 3 // this will be optimized away, leaving a useless jump only block + } finally { + print("hello") + } + while(true){} // ensure infinite loop doesn't break the algorithm + } +} diff --git a/test/pending/jvm/t7006/Test.scala b/test/pending/jvm/t7006/Test.scala new file mode 100644 index 0000000000..7b4a8c45fb --- /dev/null +++ b/test/pending/jvm/t7006/Test.scala @@ -0,0 +1,21 @@ +import scala.tools.partest.BytecodeTest +import scala.tools.asm +import asm.tree.InsnList +import scala.collection.JavaConverters._ + +object Test extends BytecodeTest { + def show: Unit = { + val classNode = loadClassNode("Foo_1") + val methodNode = getMethod(classNode, "foo") + val nopCount = count(methodNode.instructions, asm.Opcodes.NOP) + val gotoCount = count(methodNode.instructions, asm.Opcodes.GOTO) + assert(nopCount == 0, s"NOPs expected: 0, actual: $nopCount") + assert(gotoCount == 1, s"GOTOs expected: 1, actual: $gotoCount") + } + + def count(insnList: InsnList, opcode: Int): Int = { + def isNop(node: asm.tree.AbstractInsnNode): Boolean = + (node.getOpcode == opcode) + insnList.iterator.asScala.count(isNop) + } +} diff --git a/test/pending/pos/inliner2.flags b/test/pending/pos/inliner2.flags new file mode 100644 index 0000000000..4bf93a9c2a --- /dev/null +++ b/test/pending/pos/inliner2.flags @@ -0,0 +1,35 @@ +-optimise -Ybackend:GenASM -Xfatal-warnings +/* +This is what we get with 2.11.2-M3 and -Yopt:l:project: + + public final int bob1(); + Code: + 0: aload_0 + 1: aload_0 + 2: astore 6 + 4: aload 6 + 6: invokedynamic #62, 0 // InvokeDynamic #0:apply$mcZ$sp:(LA;)Lscala/runtime/java8/JFunction0$mcZ$sp; + 11: checkcast #29 // class scala/Function0 + 14: invokedynamic #71, 0 // InvokeDynamic #1:apply$mcI$sp:()Lscala/runtime/java8/JFunction0$mcI$sp; + 19: checkcast #29 // class scala/Function0 + 22: invokedynamic #76, 0 // InvokeDynamic #2:apply$mcI$sp:()Lscala/runtime/java8/JFunction0$mcI$sp; + 27: checkcast #29 // class scala/Function0 + 30: astore 4 + 32: astore_3 + 33: astore_2 + 34: astore_1 + 35: aload_2 + 36: pop + 37: aload 6 + 39: invokevirtual #53 // Method A$$$anonfun$1:()Z + 42: ifeq 54 + 45: aload_3 + 46: invokeinterface #36, 1 // InterfaceMethod scala/Function0.apply:()Ljava/lang/Object; + 51: goto 61 + 54: aload 4 + 56: invokeinterface #36, 1 // InterfaceMethod scala/Function0.apply:()Ljava/lang/Object; + 61: astore 5 + 63: aload 5 + 65: invokestatic #82 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I + 68: ireturn +*/
\ No newline at end of file diff --git a/test/pending/pos/inliner2.scala b/test/pending/pos/inliner2.scala new file mode 100644 index 0000000000..bc83e04312 --- /dev/null +++ b/test/pending/pos/inliner2.scala @@ -0,0 +1,57 @@ +// This isn't actually testing much, because no warning is emitted in versions +// before the fix which comes with this because the method isn't even considered +// for inlining due to the bug. +class A { + private var debug = false + @inline private def ifelse[T](cond: => Boolean, ifPart: => T, elsePart: => T): T = + if (cond) ifPart else elsePart + + final def bob1() = ifelse(debug, 1, 2) + final def bob2() = if (debug) 1 else 2 +} +// Cool: +// +// % ls -1 /tmp/2901/ +// A$$anonfun$bob1$1.class +// A$$anonfun$bob1$2.class +// A$$anonfun$bob1$3.class +// A.class +// % ls -1 /tmp/trunk +// A.class +// +// Observations: +// +// (1) The inlined version accesses the field: the explicit one calls the accessor. +// (2) The inlined version fails to eliminate boxing. With reference types it emits +// an unneeded checkcast. +// (3) The private var debug is mangled to A$$debug, but after inlining it is never accessed +// from outside of the class and doesn't need mangling. +// (4) We could forego emitting bytecode for ifelse entirely if it has been +// inlined at all sites. +// +// Generated bytecode for the above: +// +// public final int bob1(); +// Code: +// Stack=1, Locals=1, Args_size=1 +// 0: aload_0 +// 1: getfield #11; //Field A$$debug:Z +// 4: ifeq 14 +// 7: iconst_1 +// 8: invokestatic #41; //Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer; +// 11: goto 18 +// 14: iconst_2 +// 15: invokestatic #41; //Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer; +// 18: invokestatic #45; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I +// 21: ireturn +// +// public final int bob2(); +// Code: +// Stack=1, Locals=1, Args_size=1 +// 0: aload_0 +// 1: invokevirtual #48; //Method A$$debug:()Z +// 4: ifeq 11 +// 7: iconst_1 +// 8: goto 12 +// 11: iconst_2 +// 12: ireturn diff --git a/test/pending/pos/sealed-final.flags b/test/pending/pos/sealed-final.flags new file mode 100644 index 0000000000..63d024a0ba --- /dev/null +++ b/test/pending/pos/sealed-final.flags @@ -0,0 +1,41 @@ +-Xfatal-warnings -Yinline-warnings -Ybackend:GenASM -optimise +/* +The new flag settings could be + -Yopt-warnings -Yopt:l:project + +The issue here is that things are being inlined, but a lot of +redundant load/store instructions are left behind: + +2.11.7: + + public int f(); + Code: + 0: getstatic #19 // Field Foo$.MODULE$:LFoo$; + 3: invokevirtual #23 // Method Foo$.mkFoo:()LFoo; + 6: pop + 7: bipush 10 + 9: iconst_1 + 10: iadd + 11: ireturn + + +2.12.0-M3: + + public int f(); + Code: + 0: getstatic #19 // Field Foo$.MODULE$:LFoo$; + 3: invokevirtual #23 // Method Foo$.mkFoo:()LFoo; + 6: bipush 10 + 8: istore_2 + 9: dup + 10: ifnonnull 15 + 13: aconst_null + 14: athrow + 15: astore_1 + 16: iload_2 + 17: iconst_1 + 18: iadd + 19: istore_3 + 20: iload_3 + 21: ireturn +*/
\ No newline at end of file diff --git a/test/pending/pos/sealed-final.scala b/test/pending/pos/sealed-final.scala new file mode 100644 index 0000000000..bdedb5c1f6 --- /dev/null +++ b/test/pending/pos/sealed-final.scala @@ -0,0 +1,14 @@ +sealed abstract class Foo { + @inline def bar(x: Int) = x + 1 +} +object Foo { + def mkFoo(): Foo = new Baz2 +} + +object Baz1 extends Foo +final class Baz2 extends Foo + +object Test { + // bar should be inlined now + def f = Foo.mkFoo() bar 10 +} diff --git a/test/pending/run/inline-ex-handlers.check b/test/pending/run/inline-ex-handlers.check new file mode 100644 index 0000000000..fce32771b4 --- /dev/null +++ b/test/pending/run/inline-ex-handlers.check @@ -0,0 +1,491 @@ +--- a ++++ b +@@ -171,5 +171,5 @@ + def productElement(x$1: Int (INT)): Object { +- locals: value x$1, value x1 ++ locals: value x$1, value x1, variable boxed1 + startBlock: 1 +- blocks: [1,2,3,4] ++ blocks: [1,3,4] + +@@ -186,2 +186,4 @@ + 92 LOAD_LOCAL(value x$1) ++ 92 STORE_LOCAL(variable boxed1) ++ 92 LOAD_LOCAL(variable boxed1) + 92 BOX INT +@@ -194,5 +196,2 @@ + 92 CALL_METHOD MyException.message (dynamic) +- 92 JUMP 2 +- +- 2: + 92 RETURN(REF(class Object)) +@@ -246,3 +245,3 @@ + startBlock: 1 +- blocks: [1,2,3,4,5,6,7,8,11,12,13,14,15,16,17,18] ++ blocks: [1,2,3,4,5,6,8,11,12,13,14,15,16,17,18] + +@@ -257,5 +256,2 @@ + 92 SCOPE_ENTER value x1 +- 92 JUMP 7 +- +- 7: + 92 LOAD_LOCAL(value x1) +@@ -408,5 +404,5 @@ + def main(args: Array[String] (ARRAY[REF(class String)])): Unit { +- locals: value args, variable result, value ex6, value x4, value x5, value message, value x ++ locals: value args, variable result, value ex6, value x4, value x5, value x + startBlock: 1 +- blocks: [1,2,3,4,5,8,10,11,13] ++ blocks: [1,2,3,5,8,10,11,13,14] + +@@ -434,4 +430,13 @@ + 103 CALL_METHOD MyException.<init> (static-instance) +- 103 THROW(MyException) ++ ? STORE_LOCAL(value ex6) ++ ? JUMP 14 + ++ 14: ++ 101 LOAD_LOCAL(value ex6) ++ 101 STORE_LOCAL(value x4) ++ 101 SCOPE_ENTER value x4 ++ 106 LOAD_LOCAL(value x4) ++ 106 IS_INSTANCE REF(class MyException) ++ 106 CZJUMP (BOOL)NE ? 5 : 8 ++ + 13: +@@ -447,5 +452,2 @@ + 101 SCOPE_ENTER value x4 +- 101 JUMP 4 +- +- 4: + 106 LOAD_LOCAL(value x4) +@@ -459,8 +461,5 @@ + 106 SCOPE_ENTER value x5 +- 106 LOAD_LOCAL(value x5) +- 106 CALL_METHOD MyException.message (dynamic) +- 106 STORE_LOCAL(value message) +- 106 SCOPE_ENTER value message + 106 LOAD_MODULE object Predef +- 106 LOAD_LOCAL(value message) ++ ? LOAD_LOCAL(value x5) ++ 106 CALL_METHOD MyException.message (dynamic) + 106 CALL_METHOD scala.Predef.println (dynamic) +@@ -536,3 +535,3 @@ + startBlock: 1 +- blocks: [1,2,3,4,6,7,9,10] ++ blocks: [1,3,4,6,7,9,10,11,12,13] + +@@ -565,4 +564,9 @@ + 306 CALL_METHOD MyException.<init> (static-instance) +- 306 THROW(MyException) ++ ? JUMP 11 + ++ 11: ++ ? LOAD_LOCAL(variable monitor4) ++ 305 MONITOR_EXIT ++ ? JUMP 12 ++ + 9: +@@ -571,3 +575,3 @@ + 305 MONITOR_EXIT +- ? THROW(Throwable) ++ ? JUMP 12 + +@@ -577,4 +581,11 @@ + 304 MONITOR_EXIT +- ? THROW(Throwable) ++ ? STORE_LOCAL(value t) ++ ? JUMP 13 + ++ 12: ++ ? LOAD_LOCAL(variable monitor3) ++ 304 MONITOR_EXIT ++ ? STORE_LOCAL(value t) ++ ? JUMP 13 ++ + 3: +@@ -591,5 +602,14 @@ + 310 CALL_METHOD scala.Predef.println (dynamic) +- 310 JUMP 2 ++ 300 RETURN(UNIT) + +- 2: ++ 13: ++ 310 LOAD_MODULE object Predef ++ 310 CALL_PRIMITIVE(StartConcat) ++ 310 CONSTANT("Caught crash: ") ++ 310 CALL_PRIMITIVE(StringConcat(REF(class String))) ++ 310 LOAD_LOCAL(value t) ++ 310 CALL_METHOD java.lang.Throwable.toString (dynamic) ++ 310 CALL_PRIMITIVE(StringConcat(REF(class String))) ++ 310 CALL_PRIMITIVE(EndConcat) ++ 310 CALL_METHOD scala.Predef.println (dynamic) + 300 RETURN(UNIT) +@@ -601,6 +621,6 @@ + with finalizer: null +- catch (Throwable) in Vector(7, 9, 10) starting at: 6 ++ catch (Throwable) in Vector(7, 9, 10, 11) starting at: 6 + consisting of blocks: List(6) + with finalizer: null +- catch (Throwable) in Vector(4, 6, 7, 9, 10) starting at: 3 ++ catch (Throwable) in Vector(4, 6, 7, 9, 10, 11, 12) starting at: 3 + consisting of blocks: List(3) +@@ -636,3 +656,3 @@ + startBlock: 1 +- blocks: [1,3,4,5,6,8,9] ++ blocks: [1,3,4,5,6,8,9,10,11] + +@@ -660,4 +680,10 @@ + 78 CALL_METHOD java.lang.IllegalArgumentException.<init> (static-instance) +- 78 THROW(IllegalArgumentException) ++ ? STORE_LOCAL(value e) ++ ? JUMP 10 + ++ 10: ++ 81 LOAD_LOCAL(value e) ++ ? STORE_LOCAL(variable exc1) ++ ? JUMP 11 ++ + 8: +@@ -686,3 +712,4 @@ + 81 LOAD_LOCAL(value e) +- 81 THROW(Exception) ++ ? STORE_LOCAL(variable exc1) ++ ? JUMP 11 + +@@ -703,2 +730,15 @@ + ++ 11: ++ 83 LOAD_MODULE object Predef ++ 83 CONSTANT("finally") ++ 83 CALL_METHOD scala.Predef.println (dynamic) ++ 84 LOAD_LOCAL(variable result) ++ 84 CONSTANT(1) ++ 84 CALL_PRIMITIVE(Arithmetic(SUB,INT)) ++ 84 CONSTANT(2) ++ 84 CALL_PRIMITIVE(Arithmetic(DIV,INT)) ++ 84 STORE_LOCAL(variable result) ++ 84 LOAD_LOCAL(variable exc1) ++ 84 THROW(Throwable) ++ + } +@@ -708,3 +748,3 @@ + with finalizer: null +- catch (<none>) in Vector(4, 5, 6, 8) starting at: 3 ++ catch (<none>) in Vector(4, 5, 6, 8, 10) starting at: 3 + consisting of blocks: List(3) +@@ -732,5 +772,5 @@ + def main(args: Array[String] (ARRAY[REF(class String)])): Unit { +- locals: value args, variable result, value ex6, variable exc2, value x4, value x5, value message, value x, value ex6, value x4, value x5, value message, value x ++ locals: value args, variable result, value ex6, variable exc2, value x4, value x5, value x, value ex6, value x4, value x5, value x + startBlock: 1 +- blocks: [1,3,4,5,6,9,13,14,15,18,20,21,23,24] ++ blocks: [1,3,4,5,6,9,13,14,15,18,20,21,23,24,25,26,27] + +@@ -758,4 +798,11 @@ + 172 CALL_METHOD MyException.<init> (static-instance) +- 172 THROW(MyException) ++ ? STORE_LOCAL(value ex6) ++ ? JUMP 25 + ++ 25: ++ 170 LOAD_LOCAL(value ex6) ++ 170 STORE_LOCAL(value x4) ++ 170 SCOPE_ENTER value x4 ++ 170 JUMP 14 ++ + 23: +@@ -798,8 +845,5 @@ + 175 SCOPE_ENTER value x5 +- 175 LOAD_LOCAL(value x5) +- 175 CALL_METHOD MyException.message (dynamic) +- 175 STORE_LOCAL(value message) +- 175 SCOPE_ENTER value message + 176 LOAD_MODULE object Predef +- 176 LOAD_LOCAL(value message) ++ ? LOAD_LOCAL(value x5) ++ 176 CALL_METHOD MyException.message (dynamic) + 176 CALL_METHOD scala.Predef.println (dynamic) +@@ -807,5 +851,7 @@ + 177 DUP(REF(class MyException)) +- 177 LOAD_LOCAL(value message) ++ ? LOAD_LOCAL(value x5) ++ 177 CALL_METHOD MyException.message (dynamic) + 177 CALL_METHOD MyException.<init> (static-instance) +- 177 THROW(MyException) ++ ? STORE_LOCAL(value ex6) ++ ? JUMP 26 + +@@ -813,3 +859,4 @@ + 170 LOAD_LOCAL(value ex6) +- 170 THROW(Throwable) ++ ? STORE_LOCAL(value ex6) ++ ? JUMP 26 + +@@ -823,2 +870,8 @@ + ++ 26: ++ 169 LOAD_LOCAL(value ex6) ++ 169 STORE_LOCAL(value x4) ++ 169 SCOPE_ENTER value x4 ++ 169 JUMP 5 ++ + 5: +@@ -833,8 +886,5 @@ + 180 SCOPE_ENTER value x5 +- 180 LOAD_LOCAL(value x5) +- 180 CALL_METHOD MyException.message (dynamic) +- 180 STORE_LOCAL(value message) +- 180 SCOPE_ENTER value message + 181 LOAD_MODULE object Predef +- 181 LOAD_LOCAL(value message) ++ ? LOAD_LOCAL(value x5) ++ 181 CALL_METHOD MyException.message (dynamic) + 181 CALL_METHOD scala.Predef.println (dynamic) +@@ -842,5 +892,7 @@ + 182 DUP(REF(class MyException)) +- 182 LOAD_LOCAL(value message) ++ ? LOAD_LOCAL(value x5) ++ 182 CALL_METHOD MyException.message (dynamic) + 182 CALL_METHOD MyException.<init> (static-instance) +- 182 THROW(MyException) ++ ? STORE_LOCAL(variable exc2) ++ ? JUMP 27 + +@@ -848,3 +900,4 @@ + 169 LOAD_LOCAL(value ex6) +- 169 THROW(Throwable) ++ ? STORE_LOCAL(variable exc2) ++ ? JUMP 27 + +@@ -865,2 +918,15 @@ + ++ 27: ++ 184 LOAD_MODULE object Predef ++ 184 CONSTANT("finally") ++ 184 CALL_METHOD scala.Predef.println (dynamic) ++ 185 LOAD_LOCAL(variable result) ++ 185 CONSTANT(1) ++ 185 CALL_PRIMITIVE(Arithmetic(SUB,INT)) ++ 185 CONSTANT(2) ++ 185 CALL_PRIMITIVE(Arithmetic(DIV,INT)) ++ 185 STORE_LOCAL(variable result) ++ 185 LOAD_LOCAL(variable exc2) ++ 185 THROW(Throwable) ++ + } +@@ -870,6 +936,6 @@ + with finalizer: null +- catch (Throwable) in Vector(13, 14, 15, 18, 20, 21, 23) starting at: 4 ++ catch (Throwable) in Vector(13, 14, 15, 18, 20, 21, 23, 25) starting at: 4 + consisting of blocks: List(9, 8, 6, 5, 4) + with finalizer: null +- catch (<none>) in Vector(4, 5, 6, 9, 13, 14, 15, 18, 20, 21, 23) starting at: 3 ++ catch (<none>) in Vector(4, 5, 6, 9, 13, 14, 15, 18, 20, 21, 23, 25, 26) starting at: 3 + consisting of blocks: List(3) +@@ -897,5 +963,5 @@ + def main(args: Array[String] (ARRAY[REF(class String)])): Unit { +- locals: value args, variable result, value e, value ex6, value x4, value x5, value message, value x ++ locals: value args, variable result, value e, value ex6, value x4, value x5, value x + startBlock: 1 +- blocks: [1,2,3,6,7,8,11,13,14,16] ++ blocks: [1,2,3,6,7,8,11,13,14,16,17] + +@@ -923,4 +989,11 @@ + 124 CALL_METHOD MyException.<init> (static-instance) +- 124 THROW(MyException) ++ ? STORE_LOCAL(value ex6) ++ ? JUMP 17 + ++ 17: ++ 122 LOAD_LOCAL(value ex6) ++ 122 STORE_LOCAL(value x4) ++ 122 SCOPE_ENTER value x4 ++ 122 JUMP 7 ++ + 16: +@@ -948,8 +1021,5 @@ + 127 SCOPE_ENTER value x5 +- 127 LOAD_LOCAL(value x5) +- 127 CALL_METHOD MyException.message (dynamic) +- 127 STORE_LOCAL(value message) +- 127 SCOPE_ENTER value message + 127 LOAD_MODULE object Predef +- 127 LOAD_LOCAL(value message) ++ ? LOAD_LOCAL(value x5) ++ 127 CALL_METHOD MyException.message (dynamic) + 127 CALL_METHOD scala.Predef.println (dynamic) +@@ -982,3 +1052,3 @@ + with finalizer: null +- catch (IllegalArgumentException) in Vector(6, 7, 8, 11, 13, 14, 16) starting at: 3 ++ catch (IllegalArgumentException) in Vector(6, 7, 8, 11, 13, 14, 16, 17) starting at: 3 + consisting of blocks: List(3) +@@ -1006,5 +1076,5 @@ + def main(args: Array[String] (ARRAY[REF(class String)])): Unit { +- locals: value args, variable result, value ex6, value x4, value x5, value message, value x, value e ++ locals: value args, variable result, value ex6, value x4, value x5, value x, value e + startBlock: 1 +- blocks: [1,2,3,4,5,8,12,13,14,16] ++ blocks: [1,2,3,5,8,12,13,14,16,17] + +@@ -1032,4 +1102,13 @@ + 148 CALL_METHOD MyException.<init> (static-instance) +- 148 THROW(MyException) ++ ? STORE_LOCAL(value ex6) ++ ? JUMP 17 + ++ 17: ++ 145 LOAD_LOCAL(value ex6) ++ 145 STORE_LOCAL(value x4) ++ 145 SCOPE_ENTER value x4 ++ 154 LOAD_LOCAL(value x4) ++ 154 IS_INSTANCE REF(class MyException) ++ 154 CZJUMP (BOOL)NE ? 5 : 8 ++ + 16: +@@ -1053,5 +1132,2 @@ + 145 SCOPE_ENTER value x4 +- 145 JUMP 4 +- +- 4: + 154 LOAD_LOCAL(value x4) +@@ -1065,8 +1141,5 @@ + 154 SCOPE_ENTER value x5 +- 154 LOAD_LOCAL(value x5) +- 154 CALL_METHOD MyException.message (dynamic) +- 154 STORE_LOCAL(value message) +- 154 SCOPE_ENTER value message + 154 LOAD_MODULE object Predef +- 154 LOAD_LOCAL(value message) ++ ? LOAD_LOCAL(value x5) ++ 154 CALL_METHOD MyException.message (dynamic) + 154 CALL_METHOD scala.Predef.println (dynamic) +@@ -1287,3 +1360,3 @@ + startBlock: 1 +- blocks: [1,2,3,4,5,7] ++ blocks: [1,2,3,4,5,7,8] + +@@ -1311,4 +1384,11 @@ + 38 CALL_METHOD java.lang.IllegalArgumentException.<init> (static-instance) +- 38 THROW(IllegalArgumentException) ++ ? STORE_LOCAL(value e) ++ ? JUMP 8 + ++ 8: ++ 42 LOAD_MODULE object Predef ++ 42 CONSTANT("IllegalArgumentException") ++ 42 CALL_METHOD scala.Predef.println (dynamic) ++ 42 JUMP 2 ++ + 7: +@@ -1358,5 +1438,5 @@ + def main(args: Array[String] (ARRAY[REF(class String)])): Unit { +- locals: value args, variable result, value ex6, value x4, value x5, value message, value x ++ locals: value args, variable result, value ex6, value x4, value x5, value x + startBlock: 1 +- blocks: [1,2,3,4,5,8,10,11,13,14,16] ++ blocks: [1,2,3,5,8,10,11,13,14,16,17] + +@@ -1384,3 +1464,4 @@ + 203 CALL_METHOD MyException.<init> (static-instance) +- 203 THROW(MyException) ++ ? STORE_LOCAL(value ex6) ++ ? JUMP 17 + +@@ -1404,4 +1485,13 @@ + 209 CALL_METHOD MyException.<init> (static-instance) +- 209 THROW(MyException) ++ ? STORE_LOCAL(value ex6) ++ ? JUMP 17 + ++ 17: ++ 200 LOAD_LOCAL(value ex6) ++ 200 STORE_LOCAL(value x4) ++ 200 SCOPE_ENTER value x4 ++ 212 LOAD_LOCAL(value x4) ++ 212 IS_INSTANCE REF(class MyException) ++ 212 CZJUMP (BOOL)NE ? 5 : 8 ++ + 16: +@@ -1417,5 +1507,2 @@ + 200 SCOPE_ENTER value x4 +- 200 JUMP 4 +- +- 4: + 212 LOAD_LOCAL(value x4) +@@ -1429,8 +1516,5 @@ + 212 SCOPE_ENTER value x5 +- 212 LOAD_LOCAL(value x5) +- 212 CALL_METHOD MyException.message (dynamic) +- 212 STORE_LOCAL(value message) +- 212 SCOPE_ENTER value message + 213 LOAD_MODULE object Predef +- 213 LOAD_LOCAL(value message) ++ ? LOAD_LOCAL(value x5) ++ 213 CALL_METHOD MyException.message (dynamic) + 213 CALL_METHOD scala.Predef.println (dynamic) +@@ -1478,3 +1562,3 @@ + startBlock: 1 +- blocks: [1,2,3,4,5,7] ++ blocks: [1,2,3,4,5,7,8] + +@@ -1502,4 +1586,11 @@ + 58 CALL_METHOD java.lang.IllegalArgumentException.<init> (static-instance) +- 58 THROW(IllegalArgumentException) ++ ? STORE_LOCAL(value e) ++ ? JUMP 8 + ++ 8: ++ 62 LOAD_MODULE object Predef ++ 62 CONSTANT("RuntimeException") ++ 62 CALL_METHOD scala.Predef.println (dynamic) ++ 62 JUMP 2 ++ + 7: +@@ -1551,3 +1642,3 @@ + startBlock: 1 +- blocks: [1,3,4] ++ blocks: [1,3,4,5] + +@@ -1571,4 +1662,9 @@ + 229 CALL_METHOD MyException.<init> (static-instance) +- 229 THROW(MyException) ++ ? JUMP 5 + ++ 5: ++ ? LOAD_LOCAL(variable monitor1) ++ 228 MONITOR_EXIT ++ 228 THROW(Throwable) ++ + 3: +@@ -1577,3 +1673,3 @@ + 228 MONITOR_EXIT +- ? THROW(Throwable) ++ 228 THROW(Throwable) + +@@ -1605,5 +1701,5 @@ + def main(args: Array[String] (ARRAY[REF(class String)])): Unit { +- locals: value args, variable result, variable monitor2, variable monitorResult1 ++ locals: value exception$1, value args, variable result, variable monitor2, variable monitorResult1 + startBlock: 1 +- blocks: [1,3,4] ++ blocks: [1,3,4,5] + +@@ -1630,4 +1726,12 @@ + 245 CALL_METHOD MyException.<init> (static-instance) +- 245 THROW(MyException) ++ ? STORE_LOCAL(value exception$1) ++ ? DROP ConcatClass ++ ? LOAD_LOCAL(value exception$1) ++ ? JUMP 5 + ++ 5: ++ ? LOAD_LOCAL(variable monitor2) ++ 244 MONITOR_EXIT ++ 244 THROW(Throwable) ++ + 3: +@@ -1636,3 +1740,3 @@ + 244 MONITOR_EXIT +- ? THROW(Throwable) ++ 244 THROW(Throwable) diff --git a/test/pending/run/inline-ex-handlers.scala b/test/pending/run/inline-ex-handlers.scala new file mode 100644 index 0000000000..964594d258 --- /dev/null +++ b/test/pending/run/inline-ex-handlers.scala @@ -0,0 +1,329 @@ +import scala.tools.partest.IcodeComparison + +object Test extends IcodeComparison { + override def printIcodeAfterPhase = "inlinehandlers" +} + +import scala.util.Random._ + +/** There should be no inlining taking place in this class */ +object TestInlineHandlersNoInline { + + def main(args: Array[String]): Unit = { + println("TestInlineHandlersNoInline") + var result = -1 + + try { + if (nextInt % 2 == 0) + throw new IllegalArgumentException("something") + result = 1 + } catch { + case e: StackOverflowError => + println("Stack overflow") + } + + result + } +} + +/** Just a simple inlining should take place in this class */ +object TestInlineHandlersSimpleInline { + + def main(args: Array[String]): Unit = { + println("TestInlineHandlersSimpleInline") + var result = -1 + + try { + if (nextInt % 2 == 0) + throw new IllegalArgumentException("something") + result = 1 + } catch { + case e: IllegalArgumentException => + println("IllegalArgumentException") + } + + result + } +} + +/** Inlining should take place because the handler is taking a superclass of the exception thrown */ +object TestInlineHandlersSubclassInline { + + def main(args: Array[String]): Unit = { + println("TestInlineHandlersSubclassInline") + var result = -1 + + try { + if (nextInt % 2 == 0) + throw new IllegalArgumentException("something") + result = 1 + } catch { + case e: RuntimeException => + println("RuntimeException") + } + + result + } +} + +/** For this class, the finally handler should be inlined */ +object TestInlineHandlersFinallyInline { + + def main(args: Array[String]): Unit = { + println("TestInlineHandlersFinallyInline") + var result = -1 + + try { + if (nextInt % 2 == 0) + throw new IllegalArgumentException("something") + result = 1 + } catch { + case e: Exception => throw e + } finally { + println("finally") + result = (result - 1) / 2 + } + + result + } +} + + +case class MyException(message: String) extends RuntimeException(message) + +/** For this class, we test inlining for a case class error */ +object TestInlineHandlersCaseClassExceptionInline { + + def main(args: Array[String]): Unit = { + println("TestInlineHandlersCaseClassExceptionInline") + var result = -1 + + try { + if (nextInt % 2 == 0) + throw new MyException("something") + result = 1 + } catch { + case MyException(message) => println(message) + } + + result + } +} + + +/** For this class, inline should take place in the inner handler */ +object TestInlineHandlersNestedHandlerInnerInline { + + def main(args: Array[String]): Unit = { + println("TestInlineHandlersNestedHandlersInnerInline") + var result = -1 + + try { + try { + if (nextInt % 2 == 0) + throw new MyException("something") + result = 1 + } catch { + case MyException(message) => println(message) + } + } catch { + case e: IllegalArgumentException => println("IllegalArgumentException") + } + + result + } +} + + +/** For this class, inline should take place in the outer handler */ +object TestInlineHandlersNestedHandlerOuterInline { + + def main(args: Array[String]): Unit = { + println("TestInlineHandlersNestedHandlersOuterInline") + var result = -1 + + try { + try { + if (nextInt % 2 == 0) + throw new MyException("something") + result = 1 + } catch { + case e: IllegalArgumentException => println("IllegalArgumentException") + } + } catch { + case MyException(message) => println(message) + } + + result + } +} + + +/** For this class, inline should take place in the all handlers (inner, outer and finally) */ +object TestInlineHandlersNestedHandlerAllInline { + + def main(args: Array[String]): Unit = { + println("TestInlineHandlersNestedHandlersOuterInline") + var result = -1 + + try { + try { + if (nextInt % 2 == 0) + throw new MyException("something") + result = 1 + } catch { + case MyException(message) => + println(message) + throw MyException(message) + } + } catch { + case MyException(message) => + println(message) + throw MyException(message) + } finally { + println("finally") + result = (result - 1) / 2 + } + + result + } +} + + +/** This class is meant to test whether the inline handler is copied only once for multiple inlines */ +object TestInlineHandlersSingleCopy { + + def main(args: Array[String]): Unit = { + println("TestInlineHandlersSingleCopy") + var result = -1 + + try { + + if (nextInt % 2 == 0) + throw new MyException("something") + + println("A side effect in the middle") + result = 3 // another one + + if (nextInt % 3 == 2) + throw new MyException("something else") + result = 1 + } catch { + case MyException(message) => + println(message) + } + + result + } +} + +/** This should test the special exception handler for synchronized blocks */ +object TestInlineHandlersSynchronized { + + def main(args: Array[String]): Unit = { + println("TestInlineHandlersSynchronized") + var result = "hello" + + // any exception thrown here will be caught by a default handler that does MONTIOR_EXIT on result :) + result.synchronized { + throw MyException(result) + } + + result.length + } +} + +/** This should test the special exception handler for synchronized blocks with stack */ +object TestInlineHandlersSynchronizedWithStack { + + def main(args: Array[String]): Unit = { + println("TestInlineHandlersSynchronizedWithStack") + var result = "hello" + + // any exception thrown here will be caught by a default handler that does MONTIOR_EXIT on result :) + result = "abc" + result.synchronized { + throw MyException(result) + } + + result.length + } +} + +/** This test should trigger a bug in the dead code elimination phase - it actually crashes ICodeCheckers +object TestInlineHandlersSynchronizedWithStackDoubleThrow { + + def main(args: Array[String]): Unit = { + println("TestInlineHandlersSynchronizedWithStackDoubleThrow") + var result = "a" + + // any exception thrown here will be caught by a default handler that does MONTIOR_EXIT on result :) + result += result.synchronized { throw MyException(result) } + result += result.synchronized { throw MyException(result) } + + result.length + } +} +*/ + +/** This test should check the preciseness of the inliner: it should not do any inlining here +* as it is not able to discern between the different exceptions +*/ +object TestInlineHandlersPreciseness { + + def main(args: Array[String]): Unit = { + println("TestInlineHandlersCorrectHandler") + + try { + val exception: Throwable = + if (scala.util.Random.nextInt % 2 == 0) + new IllegalArgumentException("even") + else + new StackOverflowError("odd") + throw exception + } catch { + case e: IllegalArgumentException => + println("Correct, IllegalArgumentException") + case e: StackOverflowError => + println("Correct, StackOverflowException") + case t: Throwable => + println("WROOOONG, not Throwable!") + } + } +} + +/** This check should verify that the double no-local exception handler is duplicated correctly */ +object TestInlineHandlersDoubleNoLocal { + + val a1: String = "a" + val a2: String = "b" + + def main(args: Array[String]): Unit = { + println("TestInlineHandlersDoubleNoLocal") + + try { + a1.synchronized { + a2. synchronized { + throw new MyException("crash") + } + } + } catch { + case t: Throwable => println("Caught crash: " + t.toString) + } + + /* try { + val exception: Throwable = + if (scala.util.Random.nextInt % 2 == 0) + new IllegalArgumentException("even") + else + new StackOverflowError("odd") + throw exception + } catch { + case e: IllegalArgumentException => + println("Correct, IllegalArgumentException") + case e: StackOverflowError => + println("Correct, StackOverflowException") + case t: Throwable => + println("WROOOONG, not Throwable!") + }*/ + } +} diff --git a/test/pending/run/t5313.check b/test/pending/run/t5313.check new file mode 100644 index 0000000000..7a48b2b711 --- /dev/null +++ b/test/pending/run/t5313.check @@ -0,0 +1,12 @@ +STORE_LOCAL(variable kept1) +STORE_LOCAL(value result) +STORE_LOCAL(variable kept1) +STORE_LOCAL(variable kept2) +STORE_LOCAL(value kept3) +STORE_LOCAL(variable kept2) +STORE_LOCAL(variable kept4) +STORE_LOCAL(variable kept4) +STORE_LOCAL(variable kept5) +STORE_LOCAL(variable kept5) +STORE_LOCAL(variable kept6) +STORE_LOCAL(variable kept6) diff --git a/test/pending/run/t5313.scala b/test/pending/run/t5313.scala new file mode 100644 index 0000000000..4a5b076e6e --- /dev/null +++ b/test/pending/run/t5313.scala @@ -0,0 +1,54 @@ +import scala.tools.partest.IcodeComparison + +object Test extends IcodeComparison { + override def printIcodeAfterPhase = "dce" + + override def extraSettings: String = super.extraSettings + " -Yopt:l:classpath" + + override def code = + """class Foo { + def randomBoolean = scala.util.Random.nextInt % 2 == 0 + def bar = { + var kept1 = new Object + val result = new java.lang.ref.WeakReference(kept1) + kept1 = null // we can't eliminate this assignment because result can observe + // when the object has no more references. See SI-5313 + kept1 = new Object // but we can eliminate this one because kept1 has already been clobbered + var erased2 = null // we can eliminate this store because it's never used + val erased3 = erased2 // and this + var erased4 = erased2 // and this + val erased5 = erased4 // and this + var kept2: Object = new Object // ultimately can't be eliminated + while(randomBoolean) { + val kept3 = kept2 + kept2 = null // this can't, because it clobbers kept2, which is used + erased4 = null // safe to eliminate + println(kept3) + } + var kept4 = new Object // have to keep, it's used + try + println(kept4) + catch { + case _ : Throwable => kept4 = null // have to keep, it clobbers kept4 which is used + } + var kept5 = new Object + print(kept5) + kept5 = null // can't eliminate it's a clobber and it's used + print(kept5) + kept5 = null // can eliminate because we don't care about clobbers of nulls + while(randomBoolean) { + var kept6: AnyRef = null // not used, but have to keep because it clobbers the next used store + // on the back edge of the loop + kept6 = new Object // used + println(kept6) + } + result + } + }""".stripMargin + + override def show() { + val storeLocal = "STORE_LOCAL" + val lines1 = collectIcode() filter (_ contains storeLocal) map (x => x.drop(x.indexOf(storeLocal))) + println(lines1 mkString "\n") + } +} diff --git a/test/pending/run/t6955.scala b/test/pending/run/t6955.scala new file mode 100644 index 0000000000..787617eff1 --- /dev/null +++ b/test/pending/run/t6955.scala @@ -0,0 +1,33 @@ +import scala.tools.partest.IcodeComparison + +// this class should compile to code that uses switches (twice) +class Switches { + type Tag = Byte + + def switchBad(i: Tag): Int = i match { // notice type of i is Tag = Byte + case 1 => 1 + case 2 => 2 + case 3 => 3 + case _ => 0 + } + + // this worked before, should keep working + def switchOkay(i: Byte): Int = i match { + case 1 => 1 + case 2 => 2 + case 3 => 3 + case _ => 0 + } +} + +object Test extends IcodeComparison { + // ensure we get two switches out of this -- ignore the rest of the output for robustness + // exclude the constant we emit for the "SWITCH ..." string below (we get the icode for all the code you see in this file) + override def show() = { + val expected = 2 + val actual = (collectIcode() filter { + x => x.indexOf("SWITCH ...") >= 0 && x.indexOf("CONSTANT(") == -1 + }).size + assert(actual == expected, s"switches expected: $expected, actual: $actual") + } +} diff --git a/test/pending/run/t6956.scala b/test/pending/run/t6956.scala new file mode 100644 index 0000000000..57d721807d --- /dev/null +++ b/test/pending/run/t6956.scala @@ -0,0 +1,31 @@ +import scala.tools.partest.IcodeComparison + +class Switches { + private[this] final val ONE = 1 + + def switchBad(i: Byte): Int = i match { + case ONE => 1 + case 2 => 2 + case 3 => 3 + case _ => 0 + } + + def switchOkay(i: Byte): Int = i match { + case 1 => 1 + case 2 => 2 + case 3 => 3 + case _ => 0 + } +} + +object Test extends IcodeComparison { + // ensure we get two switches out of this -- ignore the rest of the output for robustness + // exclude the constant we emit for the "SWITCH ..." string below (we get the icode for all the code you see in this file) + override def show() = { + val expected = 2 + val actual = (collectIcode() filter { + x => x.indexOf("SWITCH ...") >= 0 && x.indexOf("CONSTANT(") == -1 + }).size + assert(actual == expected, s"switches expected: $expected, actual: $actual") + } +} diff --git a/test/pending/run/test-cpp.check b/test/pending/run/test-cpp.check new file mode 100644 index 0000000000..ff4c9bf2bf --- /dev/null +++ b/test/pending/run/test-cpp.check @@ -0,0 +1,81 @@ +--- a ++++ b +@@ -54,3 +54,3 @@ + def main(args: Array[String] (ARRAY[REF(class String)])): Unit { +- locals: value args, value x, value y ++ locals: value args + startBlock: 1 +@@ -59,10 +59,6 @@ + 1: +- 52 CONSTANT(2) +- 52 STORE_LOCAL(value x) + 52 SCOPE_ENTER value x +- 53 LOAD_LOCAL(value x) +- 53 STORE_LOCAL(value y) + 53 SCOPE_ENTER value y + 54 LOAD_MODULE object Predef +- 54 LOAD_LOCAL(value y) ++ 54 CONSTANT(2) + 54 BOX INT +@@ -109,3 +105,3 @@ + def main(args: Array[String] (ARRAY[REF(class String)])): Unit { +- locals: value args, value x, value y ++ locals: value args, value x + startBlock: 1 +@@ -118,7 +114,5 @@ + 81 SCOPE_ENTER value x +- 82 LOAD_LOCAL(value x) +- 82 STORE_LOCAL(value y) + 82 SCOPE_ENTER value y + 83 LOAD_MODULE object Predef +- 83 LOAD_LOCAL(value y) ++ 83 LOAD_LOCAL(value x) + 83 BOX INT +@@ -152,3 +146,3 @@ + def main(args: Array[String] (ARRAY[REF(class String)])): Unit { +- locals: value args, value x, value y ++ locals: value args + startBlock: 1 +@@ -157,10 +151,6 @@ + 1: +- 66 THIS(TestAliasChainDerefThis) +- 66 STORE_LOCAL(value x) + 66 SCOPE_ENTER value x +- 67 LOAD_LOCAL(value x) +- 67 STORE_LOCAL(value y) + 67 SCOPE_ENTER value y + 68 LOAD_MODULE object Predef +- 68 LOAD_LOCAL(value y) ++ 68 THIS(Object) + 68 CALL_METHOD scala.Predef.println (dynamic) +@@ -193,3 +183,3 @@ + def test(x: Int (INT)): Unit { +- locals: value x, value y ++ locals: value x + startBlock: 1 +@@ -198,7 +188,5 @@ + 1: +- 29 LOAD_LOCAL(value x) +- 29 STORE_LOCAL(value y) + 29 SCOPE_ENTER value y + 30 LOAD_MODULE object Predef +- 30 LOAD_LOCAL(value y) ++ 30 LOAD_LOCAL(value x) + 30 BOX INT +@@ -240,7 +228,5 @@ + 96 SCOPE_ENTER variable x +- 97 LOAD_LOCAL(variable x) +- 97 STORE_LOCAL(variable y) + 97 SCOPE_ENTER variable y + 98 LOAD_MODULE object Predef +- 98 LOAD_LOCAL(variable y) ++ 98 LOAD_LOCAL(variable x) + 98 BOX INT +@@ -250,6 +236,4 @@ + 100 STORE_LOCAL(variable y) +- 101 LOAD_LOCAL(variable y) +- 101 STORE_LOCAL(variable x) + 102 LOAD_MODULE object Predef +- 102 LOAD_LOCAL(variable x) ++ 102 LOAD_LOCAL(variable y) + 102 BOX INT diff --git a/test/pending/run/test-cpp.scala b/test/pending/run/test-cpp.scala new file mode 100644 index 0000000000..4fca67d51e --- /dev/null +++ b/test/pending/run/test-cpp.scala @@ -0,0 +1,104 @@ +/** + * The only change is in the decision to replace a LOAD_LOCAL(l) + * in the copy-propagation performed before ClosureElimination. + * + * In the general case, the local variable 'l' is connected through + * an alias chain with other local variables and at the end of the + * alias chain there may be a Value, call it 'v'. + * + * If 'v' is cheaper to access (it is a Deref(This) or Const(_)), then + * replace the instruction to load it from the cheaper place. + * Otherwise, we use the local variable at the end of the alias chain + * instead of 'l'. + */ + +import scala.tools.partest.IcodeComparison + +object Test extends IcodeComparison { + override def printIcodeAfterPhase = "dce" +} + +import scala.util.Random._ + +/** + * The example in the bug report (Issue-5321): an alias chain which store + * an Unknown. Should remove local variable 'y'. + */ +object TestBugReport { + def test(x: Int) = { + val y = x + println(y) + } +} + +/** + * The code taken from scala.tools.nsc.settings.Settings: + * After inlining of the setter is performed, there is an opportunity for + * copy-propagation to eliminate some local variables. + */ +object TestSetterInline { + private var _postSetHook: this.type => Unit = (x: this.type) => () + def withPostSetHook(f: this.type => Unit): this.type = { _postSetHook = f ; this } +} + + +/** + * The access of the local variable 'y' should be replaced by the + * constant. + */ +object TestAliasChainConstant { + + def main(args: Array[String]): Unit = { + val x = 2 + val y = x + println(y) + } +} + +/** + * At the end of the alias chain we have a reference to 'this'. + * The local variables should be all discarded and replace by a + * direct reference to this + */ +class TestAliasChainDerefThis { + + def main(args: Array[String]): Unit = { + val x = this + val y = x + println(y) + } +} + +/** + * At the end of the alias chain, there is the value of a field. + * The use of variable 'y' should be replaced by 'x', not by an access + * to the field 'f' since it is more costly. + */ +object TestAliasChainDerefField { + def f = nextInt + + def main(args: Array[String]): Unit = { + val x = f + val y = x + println(y) + } +} + + +/** + * The first time 'println' is called, 'x' is replaced by 'y' + * and the second time, 'y' is replaced by 'x'. But none of them + * can be removed. + */ +object TestDifferentBindings { + + def main(args: Array[String]): Unit = { + var x = nextInt + var y = x + println(y) + + y = nextInt + x = y + println(x) + } +} |