[JVM] Force lock object in JVM synchronized implementation into local.
This fixes a performance problem in the case where the lock object is a capture and the monitor enter/exit happens directly on field loads. When the locking happens on field loads instead of a local, the JVM cannot prove that locking is balanced. That has the consequence that the code is runs very slow (always in the interpreter). ^KT-48367 Fixed.
This commit is contained in:
committed by
Alexander Udalov
parent
9fd777cb7d
commit
a12b22c04d
+6
@@ -307,6 +307,12 @@ public class FirBytecodeTextTestGenerated extends AbstractFirBytecodeTextTest {
|
|||||||
runTest("compiler/testData/codegen/bytecodeText/kt46615.kt");
|
runTest("compiler/testData/codegen/bytecodeText/kt46615.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("kt48367.kt")
|
||||||
|
public void testKt48367() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/kt48367.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("kt5016.kt")
|
@TestMetadata("kt5016.kt")
|
||||||
public void testKt5016() throws Exception {
|
public void testKt5016() throws Exception {
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
fun foo(block: () -> String): String = block()
|
||||||
|
inline fun bar(crossinline f: () -> String) = foo { f() }
|
||||||
|
|
||||||
|
fun flaf() {
|
||||||
|
val revoked = "A"
|
||||||
|
bar {
|
||||||
|
synchronized (revoked) {
|
||||||
|
"B"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The field $revoked$inlined should be loaded only once and stored in a local
|
||||||
|
// that is used for the monitor enter/exit instructions. Locking and unlocking
|
||||||
|
// directly on a field load makes it hard for the JVM to prove that locking is
|
||||||
|
// balanced which causes the code to be interpreted. See KT-48367 for details.
|
||||||
|
|
||||||
|
// 1 GETFIELD Kt48367Kt\$flaf\$\$inlined\$bar\$1.\$revoked\$inlined : Ljava/lang/String;
|
||||||
|
// 1 MONITORENTER
|
||||||
|
// 2 MONITOREXIT
|
||||||
+6
@@ -295,6 +295,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
runTest("compiler/testData/codegen/bytecodeText/kt3845.kt");
|
runTest("compiler/testData/codegen/bytecodeText/kt3845.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("kt48367.kt")
|
||||||
|
public void testKt48367() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/kt48367.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("kt5016.kt")
|
@TestMetadata("kt5016.kt")
|
||||||
public void testKt5016() throws Exception {
|
public void testKt5016() throws Exception {
|
||||||
|
|||||||
+6
@@ -307,6 +307,12 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
|
|||||||
runTest("compiler/testData/codegen/bytecodeText/kt46615.kt");
|
runTest("compiler/testData/codegen/bytecodeText/kt46615.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("kt48367.kt")
|
||||||
|
public void testKt48367() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/kt48367.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("kt5016.kt")
|
@TestMetadata("kt5016.kt")
|
||||||
public void testKt5016() throws Exception {
|
public void testKt5016() throws Exception {
|
||||||
|
|||||||
@@ -19,13 +19,18 @@ public actual inline fun <R> synchronized(lock: Any, block: () -> R): R {
|
|||||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Force the lock object into a local and use that local for monitor enter/exit.
|
||||||
|
// This ensures that the JVM can prove that locking is balanced which is a
|
||||||
|
// prerequisite for using fast locking implementations. See KT-48367 for details.
|
||||||
|
val lockLocal = lock
|
||||||
|
|
||||||
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE", "INVISIBLE_MEMBER")
|
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE", "INVISIBLE_MEMBER")
|
||||||
monitorEnter(lock)
|
monitorEnter(lockLocal)
|
||||||
try {
|
try {
|
||||||
return block()
|
return block()
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE", "INVISIBLE_MEMBER")
|
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE", "INVISIBLE_MEMBER")
|
||||||
monitorExit(lock)
|
monitorExit(lockLocal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user