[JS IR] Materialize Unit more aggressively for IrCalls

Unit materialization for IrCall is required for operations with dynamic type.

However it produces useless Unit_getInstance() calls,
especially in return expressions. The pach also adds some heuristics
for reducing the amount of Unit_getInstance() calls from return expressions:
do not add Unit_getInstance() if return value has Unit type.

Relaited to KT-51139
^KT-23252 Fixed
This commit is contained in:
Alexander Korepanov
2022-04-13 21:03:33 +03:00
committed by Space
parent 677ec12b50
commit 57f16e801f
6 changed files with 258 additions and 26 deletions
@@ -0,0 +1,51 @@
// DONT_TARGET_EXACT_BACKEND: JS
// KJS_WITH_FULL_RUNTIME
fun demo(): Unit {}
fun testFunctionUsage() {
val u1 = demo()
val d1: dynamic = u1
assertTrue(d1 == u1)
assertEquals(u1.toString(), "kotlin.Unit")
}
fun testFunctionCall() {
fun test(u1: Unit): Boolean {
val d1: dynamic = u1
assertEquals(u1.toString(), "kotlin.Unit")
return d1 == u1
}
assertTrue(test(demo()))
}
fun testObjectField() {
val o = object { val u1 = demo() }
val d1: dynamic = o.u1
assertTrue(d1 == o.u1)
assertEquals(o.u1.toString(), "kotlin.Unit")
}
fun testClassField() {
class Test(val u1: Unit)
val o = Test(demo())
val d1: dynamic = o.u1
assertTrue(d1 == o.u1)
assertEquals(o.u1.toString(), "kotlin.Unit")
}
fun testContainer() {
val l = listOf<Unit>(demo())
val d1: dynamic = l.last()
assertTrue(d1 == l.last())
assertEquals(l.last().toString(), "kotlin.Unit")
}
fun box(): String {
testFunctionUsage()
testFunctionCall()
testObjectField()
testClassField()
testContainer()
return "OK"
}
@@ -0,0 +1,123 @@
// DONT_TARGET_EXACT_BACKEND: JS
var demoCallCounter = 0
fun flushDemoCallCounter() = demoCallCounter.also { demoCallCounter = 0 }
fun demo(): Unit { ++demoCallCounter }
inline fun inlineDemo(): Unit {
demo()
}
// CHECK_CALLED_IN_SCOPE: scope=testUsualCall function=Unit_getInstance
fun testUsualCall() {
val x = demo()
assertEquals(flushDemoCallCounter(), 1)
assertEquals(x.toString(), "kotlin.Unit")
}
// CHECK_NOT_CALLED_IN_SCOPE: scope=testUsualCallNoVar function=Unit_getInstance
fun testUsualCallNoVar() {
demo()
assertEquals(flushDemoCallCounter(), 1)
}
// CHECK_CALLED_IN_SCOPE: scope=testUsualCallInReturn function=Unit_getInstance
fun testUsualCallInReturn() {
// CHECK_NOT_CALLED_IN_SCOPE: scope=testUsualCallInReturn$test function=Unit_getInstance
fun test() { return demo() }
val x = test()
assertEquals(flushDemoCallCounter(), 1)
assertEquals(x.toString(), "kotlin.Unit")
}
// CHECK_NOT_CALLED_IN_SCOPE: scope=testUsualCallInReturnNoVar function=Unit_getInstance
fun testUsualCallInReturnNoVar() {
// CHECK_NOT_CALLED_IN_SCOPE: scope=testUsualCallInReturnNoVar$test function=Unit_getInstance
fun test() { return demo() }
test()
assertEquals(flushDemoCallCounter(), 1)
}
// CHECK_CALLED_IN_SCOPE: scope=testUsualCallInExpressionBody function=Unit_getInstance
fun testUsualCallInExpressionBody() {
// CHECK_NOT_CALLED_IN_SCOPE: scope=testUsualCallInExpressionBody$test function=Unit_getInstance
fun test() = demo()
val x = test()
assertEquals(flushDemoCallCounter(), 1)
assertEquals(x.toString(), "kotlin.Unit")
}
// CHECK_NOT_CALLED_IN_SCOPE: scope=testUsualCallInExpressionBodyNoVar function=Unit_getInstance
fun testUsualCallInExpressionBodyNoVar() {
// CHECK_NOT_CALLED_IN_SCOPE: scope=testUsualCallInExpressionBodyNoVar$test function=Unit_getInstance
fun test() = demo()
test()
assertEquals(flushDemoCallCounter(), 1)
}
// CHECK_CALLED_IN_SCOPE: scope=testInlineCall function=Unit_getInstance
fun testInlineCall() {
val x = inlineDemo()
assertEquals(flushDemoCallCounter(), 1)
assertEquals(x.toString(), "kotlin.Unit")
}
// CHECK_NOT_CALLED_IN_SCOPE: scope=testUsualCallNoVar function=Unit_getInstance
fun testInlineCallNoVar() {
inlineDemo()
assertEquals(flushDemoCallCounter(), 1)
}
// CHECK_CALLED_IN_SCOPE: scope=testInlineCallInReturn function=Unit_getInstance
fun testInlineCallInReturn() {
// CHECK_NOT_CALLED_IN_SCOPE: scope=testInlineCallInReturn$test function=Unit_getInstance
fun test() { return inlineDemo() }
val x = test()
assertEquals(flushDemoCallCounter(), 1)
assertEquals(x.toString(), "kotlin.Unit")
}
// CHECK_NOT_CALLED_IN_SCOPE: scope=testInlineCallInReturnNoVar function=Unit_getInstance
fun testInlineCallInReturnNoVar() {
// CHECK_NOT_CALLED_IN_SCOPE: scope=testInlineCallInReturnNoVar$test function=Unit_getInstance
fun test() { return inlineDemo() }
test()
assertEquals(flushDemoCallCounter(), 1)
}
// CHECK_CALLED_IN_SCOPE: scope=testInlineCallInExpressionBody function=Unit_getInstance
fun testInlineCallInExpressionBody() {
// CHECK_NOT_CALLED_IN_SCOPE: scope=testInlineCallInExpressionBody$test function=Unit_getInstance
fun test() = inlineDemo()
val x = test()
assertEquals(flushDemoCallCounter(), 1)
assertEquals(x.toString(), "kotlin.Unit")
}
// CHECK_NOT_CALLED_IN_SCOPE: scope=testInlineCallInExpressionBodyNoVar function=Unit_getInstance
fun testInlineCallInExpressionBodyNoVar() {
// CHECK_NOT_CALLED_IN_SCOPE: scope=testInlineCallInExpressionBodyNoVar$test function=Unit_getInstance
fun test() = inlineDemo()
test()
assertEquals(flushDemoCallCounter(), 1)
}
fun box(): String {
testUsualCall()
testUsualCallNoVar()
testUsualCallInReturn()
testUsualCallInReturnNoVar()
testUsualCallInExpressionBody()
testUsualCallInExpressionBodyNoVar()
testInlineCall()
testInlineCallNoVar()
testInlineCallInReturn()
testInlineCallInReturnNoVar()
testInlineCallInExpressionBody()
testInlineCallInExpressionBodyNoVar()
return "OK"
}
+1 -1
View File
@@ -15,4 +15,4 @@ suspend fun bar(): Unit {
}
// LINES(JS): 39 4 4 4 7 5 5 45 45 5 93 45 5 5 6 4 4 4 9 15 9 9 9 * 9 15 10 10 11 11 11 11 11 * 11 12 12 13 13 13 13 13 13 13 14 14 * 9 15 9 9 9 9
// LINES(JS_IR): 19 19 * 50 50 93 93 3 45 45 3 45 6 6 19 * 9 9 9 9 9 9 9 9 * 9 9 * 10 10 * 11 * 11 12 12 * 13 * 13 13 14 14
// LINES(JS_IR): 19 19 * 50 50 93 93 3 45 3 45 45 6 6 19 * 9 9 9 9 9 9 9 9 * 9 9 * 10 10 * 11 * 11 12 12 * 13 * 13 13 14 14