JVM_IR: do not generate $assertionsDisabled twice during inlining

This commit is contained in:
pyos
2021-06-28 19:18:51 +02:00
committed by max-kammerer
parent 123c76053f
commit e64cda61d5
12 changed files with 93 additions and 24 deletions
@@ -121,7 +121,9 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
// In case `codegen.visitor` is `<clinit>`, initializer for the `$assertionsDisabled` field // In case `codegen.visitor` is `<clinit>`, initializer for the `$assertionsDisabled` field
// needs to be inserted before the code that actually uses it. // needs to be inserted before the code that actually uses it.
generateAssertFieldIfNeeded(info) if (info.generateAssertField) {
generateAssertField()
}
val shouldSpillStack = node.requiresEmptyStackOnEntry() val shouldSpillStack = node.requiresEmptyStackOnEntry()
if (shouldSpillStack) { if (shouldSpillStack) {
@@ -193,7 +195,7 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
processor.substituteLocalVarTable(intoNode) processor.substituteLocalVarTable(intoNode)
} }
protected abstract fun generateAssertFieldIfNeeded(info: RootInliningContext) protected abstract fun generateAssertField()
private fun isInlinedToInlineFunInKotlinRuntime(): Boolean { private fun isInlinedToInlineFunInKotlinRuntime(): Boolean {
val codegen = this.codegen as? ExpressionCodegen ?: return false val codegen = this.codegen as? ExpressionCodegen ?: return false
@@ -51,11 +51,8 @@ class PsiInlineCodegen(
state.languageVersionSettings, state.unifiedNullChecks state.languageVersionSettings, state.unifiedNullChecks
), ),
), CallGenerator { ), CallGenerator {
override fun generateAssertFieldIfNeeded(info: RootInliningContext) { override fun generateAssertField() =
if (info.generateAssertField) { codegen.parentCodegen.generateAssertField()
codegen.parentCodegen.generateAssertField()
}
}
override fun genCallInner( override fun genCallInner(
callableMethod: Callable, callableMethod: Callable,
@@ -894,6 +894,12 @@ public class FirBlackBoxInlineCodegenTestGenerated extends AbstractFirBlackBoxIn
runTest("compiler/testData/codegen/boxInline/assert/jvmDoubleInline.kt"); runTest("compiler/testData/codegen/boxInline/assert/jvmDoubleInline.kt");
} }
@Test
@TestMetadata("jvmInlineIntoTwoMethods.kt")
public void testJvmInlineIntoTwoMethods() throws Exception {
runTest("compiler/testData/codegen/boxInline/assert/jvmInlineIntoTwoMethods.kt");
}
@Test @Test
@TestMetadata("jvmInlineUsedAsNoinline.kt") @TestMetadata("jvmInlineUsedAsNoinline.kt")
public void testJvmInlineUsedAsNoinline() throws Exception { public void testJvmInlineUsedAsNoinline() throws Exception {
@@ -197,22 +197,19 @@ class ClassCodegen private constructor(
} }
} }
fun generateAssertFieldIfNeeded(generatingClInit: Boolean): IrExpression? { fun generateAssertFieldIfNeeded(generatingClInit: Boolean): IrExpression? {
if (irClass.hasAssertionsDisabledField(context)) if (irClass.hasAssertionsDisabledField(context))
return null return null
val topLevelClass = generateSequence(this) { it.parentClassCodegen }.last().irClass val topLevelClass = generateSequence(this) { it.parentClassCodegen }.last().irClass
val field = irClass.buildAssertionsDisabledField(context, topLevelClass) val field = irClass.buildAssertionsDisabledField(context, topLevelClass)
generateField(field) irClass.declarations.add(0, field)
// Normally, `InitializersLowering` would move the initializer to <clinit>, but // Normally, `InitializersLowering` would move the initializer to <clinit>, but it's obviously too late for that.
// it's obviously too late for that. val init = with(field) {
val init = IrSetFieldImpl( IrSetFieldImpl(startOffset, endOffset, symbol, null, initializer!!.expression, context.irBuiltIns.unitType)
field.startOffset, field.endOffset, field.symbol, null, }
field.initializer!!.expression, context.irBuiltIns.unitType
)
if (generatingClInit) { if (generatingClInit) {
// Too late to modify the IR; have to ask the currently active `ExpressionCodegen` // Too late to modify the IR; have to ask the currently active `ExpressionCodegen` to generate this statement
// to generate this statement directly. // directly. At least we know that nothing before this point uses the field.
return init return init
} }
val classInitializer = irClass.functions.singleOrNull { it.name.asString() == "<clinit>" } ?: irClass.addFunction { val classInitializer = irClass.functions.singleOrNull { it.name.asString() == "<clinit>" } ?: irClass.addFunction {
@@ -221,6 +218,7 @@ class ClassCodegen private constructor(
}.apply { }.apply {
body = IrBlockBodyImpl(startOffset, endOffset) body = IrBlockBodyImpl(startOffset, endOffset)
} }
// Should be initialized first in case some inline function call in `<clinit>` also uses assertions.
(classInitializer.body as IrBlockBody).statements.add(0, init) (classInitializer.body as IrBlockBody).statements.add(0, init)
return null return null
} }
@@ -46,13 +46,11 @@ class IrInlineCodegen(
InlineCodegen<ExpressionCodegen>(codegen, state, signature, typeParameterMappings, sourceCompiler, reifiedTypeInliner), InlineCodegen<ExpressionCodegen>(codegen, state, signature, typeParameterMappings, sourceCompiler, reifiedTypeInliner),
IrInlineCallGenerator { IrInlineCallGenerator {
override fun generateAssertFieldIfNeeded(info: RootInliningContext) { override fun generateAssertField() {
if (info.generateAssertField) { // May be inlining code into `<clinit>`, in which case it's too late to modify the IR and
// May be inlining code into `<clinit>`, in which case it's too late to modify the IR and // `generateAssertFieldIfNeeded` will return a statement for which we need to emit bytecode.
// `generateAssertFieldIfNeeded` will return a statement for which we need to emit bytecode. val isClInit = sourceCompiler.inlineCallSiteInfo.method.name == "<clinit>"
val isClInit = info.callSiteInfo.method.name == "<clinit>" codegen.classCodegen.generateAssertFieldIfNeeded(isClInit)?.accept(codegen, BlockInfo())?.discard()
codegen.classCodegen.generateAssertFieldIfNeeded(isClInit)?.accept(codegen, BlockInfo())?.discard()
}
} }
override fun genValueAndPut( override fun genValueAndPut(
@@ -0,0 +1,32 @@
// FULL_JDK
// WITH_RUNTIME
// TARGET_BACKEND: JVM
// ASSERTIONS_MODE: jvm
// FILE: inline.kt
package test
class A {
inline fun assert(message: String): Nothing {
assert(false) { message }
throw IllegalStateException("unreachable")
}
}
// FILE: inlineSite.kt
import test.*
class Checker {
fun o(): Nothing = A().assert("O")
fun k(): Nothing = A().assert("K")
}
class Dummy
fun box(): String {
var c = Dummy::class.java.classLoader.apply {
setDefaultAssertionStatus(true)
}.loadClass("Checker").newInstance() as Checker
val o = try { c.o() } catch (e: AssertionError) { e.message }
val k = try { c.k() } catch (e: AssertionError) { e.message }
return o + k
}
@@ -894,6 +894,12 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
runTest("compiler/testData/codegen/boxInline/assert/jvmDoubleInline.kt"); runTest("compiler/testData/codegen/boxInline/assert/jvmDoubleInline.kt");
} }
@Test
@TestMetadata("jvmInlineIntoTwoMethods.kt")
public void testJvmInlineIntoTwoMethods() throws Exception {
runTest("compiler/testData/codegen/boxInline/assert/jvmInlineIntoTwoMethods.kt");
}
@Test @Test
@TestMetadata("jvmInlineUsedAsNoinline.kt") @TestMetadata("jvmInlineUsedAsNoinline.kt")
public void testJvmInlineUsedAsNoinline() throws Exception { public void testJvmInlineUsedAsNoinline() throws Exception {
@@ -894,6 +894,12 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
runTest("compiler/testData/codegen/boxInline/assert/jvmDoubleInline.kt"); runTest("compiler/testData/codegen/boxInline/assert/jvmDoubleInline.kt");
} }
@Test
@TestMetadata("jvmInlineIntoTwoMethods.kt")
public void testJvmInlineIntoTwoMethods() throws Exception {
runTest("compiler/testData/codegen/boxInline/assert/jvmInlineIntoTwoMethods.kt");
}
@Test @Test
@TestMetadata("jvmInlineUsedAsNoinline.kt") @TestMetadata("jvmInlineUsedAsNoinline.kt")
public void testJvmInlineUsedAsNoinline() throws Exception { public void testJvmInlineUsedAsNoinline() throws Exception {
@@ -894,6 +894,12 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli
runTest("compiler/testData/codegen/boxInline/assert/jvmDoubleInline.kt"); runTest("compiler/testData/codegen/boxInline/assert/jvmDoubleInline.kt");
} }
@Test
@TestMetadata("jvmInlineIntoTwoMethods.kt")
public void testJvmInlineIntoTwoMethods() throws Exception {
runTest("compiler/testData/codegen/boxInline/assert/jvmInlineIntoTwoMethods.kt");
}
@Test @Test
@TestMetadata("jvmInlineUsedAsNoinline.kt") @TestMetadata("jvmInlineUsedAsNoinline.kt")
public void testJvmInlineUsedAsNoinline() throws Exception { public void testJvmInlineUsedAsNoinline() throws Exception {
@@ -894,6 +894,12 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC
runTest("compiler/testData/codegen/boxInline/assert/jvmDoubleInline.kt"); runTest("compiler/testData/codegen/boxInline/assert/jvmDoubleInline.kt");
} }
@Test
@TestMetadata("jvmInlineIntoTwoMethods.kt")
public void testJvmInlineIntoTwoMethods() throws Exception {
runTest("compiler/testData/codegen/boxInline/assert/jvmInlineIntoTwoMethods.kt");
}
@Test @Test
@TestMetadata("jvmInlineUsedAsNoinline.kt") @TestMetadata("jvmInlineUsedAsNoinline.kt")
public void testJvmInlineUsedAsNoinline() throws Exception { public void testJvmInlineUsedAsNoinline() throws Exception {
@@ -894,6 +894,12 @@ public class JvmIrAgainstOldBoxInlineTestGenerated extends AbstractJvmIrAgainstO
runTest("compiler/testData/codegen/boxInline/assert/jvmDoubleInline.kt"); runTest("compiler/testData/codegen/boxInline/assert/jvmDoubleInline.kt");
} }
@Test
@TestMetadata("jvmInlineIntoTwoMethods.kt")
public void testJvmInlineIntoTwoMethods() throws Exception {
runTest("compiler/testData/codegen/boxInline/assert/jvmInlineIntoTwoMethods.kt");
}
@Test @Test
@TestMetadata("jvmInlineUsedAsNoinline.kt") @TestMetadata("jvmInlineUsedAsNoinline.kt")
public void testJvmInlineUsedAsNoinline() throws Exception { public void testJvmInlineUsedAsNoinline() throws Exception {
@@ -894,6 +894,12 @@ public class JvmOldAgainstIrBoxInlineTestGenerated extends AbstractJvmOldAgainst
runTest("compiler/testData/codegen/boxInline/assert/jvmDoubleInline.kt"); runTest("compiler/testData/codegen/boxInline/assert/jvmDoubleInline.kt");
} }
@Test
@TestMetadata("jvmInlineIntoTwoMethods.kt")
public void testJvmInlineIntoTwoMethods() throws Exception {
runTest("compiler/testData/codegen/boxInline/assert/jvmInlineIntoTwoMethods.kt");
}
@Test @Test
@TestMetadata("jvmInlineUsedAsNoinline.kt") @TestMetadata("jvmInlineUsedAsNoinline.kt")
public void testJvmInlineUsedAsNoinline() throws Exception { public void testJvmInlineUsedAsNoinline() throws Exception {