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
// needs to be inserted before the code that actually uses it.
generateAssertFieldIfNeeded(info)
if (info.generateAssertField) {
generateAssertField()
}
val shouldSpillStack = node.requiresEmptyStackOnEntry()
if (shouldSpillStack) {
@@ -193,7 +195,7 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
processor.substituteLocalVarTable(intoNode)
}
protected abstract fun generateAssertFieldIfNeeded(info: RootInliningContext)
protected abstract fun generateAssertField()
private fun isInlinedToInlineFunInKotlinRuntime(): Boolean {
val codegen = this.codegen as? ExpressionCodegen ?: return false
@@ -51,11 +51,8 @@ class PsiInlineCodegen(
state.languageVersionSettings, state.unifiedNullChecks
),
), CallGenerator {
override fun generateAssertFieldIfNeeded(info: RootInliningContext) {
if (info.generateAssertField) {
codegen.parentCodegen.generateAssertField()
}
}
override fun generateAssertField() =
codegen.parentCodegen.generateAssertField()
override fun genCallInner(
callableMethod: Callable,
@@ -894,6 +894,12 @@ public class FirBlackBoxInlineCodegenTestGenerated extends AbstractFirBlackBoxIn
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
@TestMetadata("jvmInlineUsedAsNoinline.kt")
public void testJvmInlineUsedAsNoinline() throws Exception {
@@ -197,22 +197,19 @@ class ClassCodegen private constructor(
}
}
fun generateAssertFieldIfNeeded(generatingClInit: Boolean): IrExpression? {
if (irClass.hasAssertionsDisabledField(context))
return null
val topLevelClass = generateSequence(this) { it.parentClassCodegen }.last().irClass
val field = irClass.buildAssertionsDisabledField(context, topLevelClass)
generateField(field)
// Normally, `InitializersLowering` would move the initializer to <clinit>, but
// it's obviously too late for that.
val init = IrSetFieldImpl(
field.startOffset, field.endOffset, field.symbol, null,
field.initializer!!.expression, context.irBuiltIns.unitType
)
irClass.declarations.add(0, field)
// Normally, `InitializersLowering` would move the initializer to <clinit>, but it's obviously too late for that.
val init = with(field) {
IrSetFieldImpl(startOffset, endOffset, symbol, null, initializer!!.expression, context.irBuiltIns.unitType)
}
if (generatingClInit) {
// Too late to modify the IR; have to ask the currently active `ExpressionCodegen`
// to generate this statement directly.
// Too late to modify the IR; have to ask the currently active `ExpressionCodegen` to generate this statement
// directly. At least we know that nothing before this point uses the field.
return init
}
val classInitializer = irClass.functions.singleOrNull { it.name.asString() == "<clinit>" } ?: irClass.addFunction {
@@ -221,6 +218,7 @@ class ClassCodegen private constructor(
}.apply {
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)
return null
}
@@ -46,13 +46,11 @@ class IrInlineCodegen(
InlineCodegen<ExpressionCodegen>(codegen, state, signature, typeParameterMappings, sourceCompiler, reifiedTypeInliner),
IrInlineCallGenerator {
override fun generateAssertFieldIfNeeded(info: RootInliningContext) {
if (info.generateAssertField) {
// 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.
val isClInit = info.callSiteInfo.method.name == "<clinit>"
codegen.classCodegen.generateAssertFieldIfNeeded(isClInit)?.accept(codegen, BlockInfo())?.discard()
}
override fun generateAssertField() {
// 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.
val isClInit = sourceCompiler.inlineCallSiteInfo.method.name == "<clinit>"
codegen.classCodegen.generateAssertFieldIfNeeded(isClInit)?.accept(codegen, BlockInfo())?.discard()
}
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");
}
@Test
@TestMetadata("jvmInlineIntoTwoMethods.kt")
public void testJvmInlineIntoTwoMethods() throws Exception {
runTest("compiler/testData/codegen/boxInline/assert/jvmInlineIntoTwoMethods.kt");
}
@Test
@TestMetadata("jvmInlineUsedAsNoinline.kt")
public void testJvmInlineUsedAsNoinline() throws Exception {
@@ -894,6 +894,12 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
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
@TestMetadata("jvmInlineUsedAsNoinline.kt")
public void testJvmInlineUsedAsNoinline() throws Exception {
@@ -894,6 +894,12 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli
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
@TestMetadata("jvmInlineUsedAsNoinline.kt")
public void testJvmInlineUsedAsNoinline() throws Exception {
@@ -894,6 +894,12 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC
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
@TestMetadata("jvmInlineUsedAsNoinline.kt")
public void testJvmInlineUsedAsNoinline() throws Exception {
@@ -894,6 +894,12 @@ public class JvmIrAgainstOldBoxInlineTestGenerated extends AbstractJvmIrAgainstO
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
@TestMetadata("jvmInlineUsedAsNoinline.kt")
public void testJvmInlineUsedAsNoinline() throws Exception {
@@ -894,6 +894,12 @@ public class JvmOldAgainstIrBoxInlineTestGenerated extends AbstractJvmOldAgainst
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
@TestMetadata("jvmInlineUsedAsNoinline.kt")
public void testJvmInlineUsedAsNoinline() throws Exception {