JVM_IR KT-47984 transform inplace arguments before inlining

This commit is contained in:
Dmitry Petrov
2021-08-09 16:42:06 +03:00
committed by TeamCityServer
parent 7a99f9ff2e
commit 5096e8c5c4
13 changed files with 278 additions and 11 deletions
@@ -7,9 +7,11 @@ package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.kotlin.codegen.optimization.boxing.isMethodInsnWith
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
import org.jetbrains.kotlin.codegen.optimization.common.remapLocalVariables
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.tree.*
class InplaceArgumentsMethodTransformer : MethodTransformer() {
@@ -25,8 +27,8 @@ class InplaceArgumentsMethodTransformer : MethodTransformer() {
transformMethod(methodContext)
updateLvtEntriesForMovedInstructions(methodContext)
val stackSizeAfter = StackSizeCalculator(internalClassName, methodNode).calculateStackSize()
methodNode.maxStack = stackSizeAfter
methodNode.maxStack = StackSizeCalculator(internalClassName, methodNode).calculateStackSize()
packLocalVariables(methodNode)
}
stripMarkers(methodNode)
}
@@ -287,7 +289,15 @@ class InplaceArgumentsMethodTransformer : MethodTransformer() {
// Not an argument load
insn = insn.next
} else {
// Replace argument load with argument body
// For each argument within this call we have
// <inplaceArgStartMarker>
// <argumentBody>
// <inplaceArgEndMarker>
// store [arg]
// ...
// load [arg]
// Replace 'load [arg]' with '<argumentBody>', drop 'store [arg]' and argument markers.
var argInsn = arg.argStartMarker.next
while (argInsn != arg.argEndMarker) {
// If a LOAD/STORE/IINC instruction was moved,
@@ -374,4 +384,60 @@ class InplaceArgumentsMethodTransformer : MethodTransformer() {
}
}
private fun packLocalVariables(methodNode: MethodNode) {
// After we've dropped argument stores-loads for inplace arguments, some variable slots may become unused.
// Track used slots and remap local variables.
// Keep in mind that 'long' and 'double' variables occupy 2 slots.
val usedLocalVar = BooleanArray(methodNode.maxLocals)
// 'this' and arguments are always "used"
val argTypes = Type.getArgumentTypes(methodNode.desc)
var lastArgIndex = 0
if (methodNode.access and Opcodes.ACC_STATIC == 0) {
usedLocalVar[lastArgIndex++] = true
}
for (argType in argTypes) {
usedLocalVar[lastArgIndex++] = true
if (argType.size == 2) {
usedLocalVar[lastArgIndex++] = true
}
}
// Local variables used in xLOAD/xSTORE/IINC instructions
for (insn in methodNode.instructions) {
when (insn.opcode) {
Opcodes.ILOAD, Opcodes.FLOAD, Opcodes.ALOAD, Opcodes.ISTORE, Opcodes.FSTORE, Opcodes.ASTORE ->
usedLocalVar[(insn as VarInsnNode).`var`] = true
Opcodes.LLOAD, Opcodes.DLOAD, Opcodes.LSTORE, Opcodes.DSTORE -> {
val index = (insn as VarInsnNode).`var`
usedLocalVar[index] = true
usedLocalVar[index + 1] = true
}
Opcodes.IINC ->
usedLocalVar[(insn as IincInsnNode).`var`] = true
}
}
// Local variables mentioned in LVT
for (lv in methodNode.localVariables) {
usedLocalVar[lv.index] = true
val lvd0 = lv.desc[0]
if (lvd0 == 'J' || lvd0 == 'D') { // long || double
usedLocalVar[lv.index + 1] = true
}
}
val newIndex = IntArray(methodNode.maxLocals)
var lastIndex = 0
for (i in newIndex.indices) {
newIndex[i] = lastIndex
if (usedLocalVar[i]) {
++lastIndex
}
}
methodNode.remapLocalVariables(newIndex)
methodNode.maxLocals = lastIndex
}
}
@@ -733,6 +733,7 @@ class MethodInliner(
private fun preprocessNodeBeforeInline(node: MethodNode, returnLabels: Map<String, Label?>) {
try {
InplaceArgumentsMethodTransformer().transform("fake", node)
FixStackWithLabelNormalizationMethodTransformer().transform("fake", node)
} catch (e: Throwable) {
throw wrapException(e, node, "couldn't inline method call")
@@ -301,7 +301,7 @@ internal fun firstLabelInChain(node: LabelNode): LabelNode {
internal fun areLabelsBeforeSameInsn(first: LabelNode, second: LabelNode): Boolean =
firstLabelInChain(first) == firstLabelInChain(second)
internal val MethodNode?.nodeText: String
val MethodNode?.nodeText: String
get() {
if (this == null) {
return "Not generated"
@@ -18228,6 +18228,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineArgsInPlace"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("arrayDequeRemoveAll.kt")
public void testArrayDequeRemoveAll() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/arrayDequeRemoveAll.kt");
}
@Test
@TestMetadata("breakInArgumentExpression.kt")
public void testBreakInArgumentExpression() throws Exception {
@@ -18240,6 +18246,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/inlineArgsInPlace/continueInArgumentExpression.kt");
}
@Test
@TestMetadata("inlineCircularDedepency.kt")
public void testInlineCircularDedepency() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/inlineCircularDedepency.kt");
}
@Test
@TestMetadata("mapSet.kt")
public void testMapSet() throws Exception {
@@ -0,0 +1,113 @@
// IGNORE_BACKEND: WASM
// WITH_RUNTIME
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
class ArrayDeque<E> : AbstractMutableList<E> {
private var head: Int = 0
private var elementData: Array<Any?>
override var size: Int = 0
private set
public constructor(elements: Collection<E>) {
elementData = elements.toTypedArray()
size = elementData.size
if (elementData.isEmpty()) elementData = emptyElementData
}
override fun add(index: Int, element: E) {
TODO()
}
override fun removeAt(index: Int): E {
TODO()
}
override fun set(index: Int, element: E): E {
TODO()
}
override fun get(index: Int): E {
return internalGet(internalIndex(index))
}
@kotlin.internal.InlineOnly
private inline fun internalGet(internalIndex: Int): E {
@Suppress("UNCHECKED_CAST")
return elementData[internalIndex] as E
}
private fun positiveMod(index: Int): Int = if (index >= elementData.size) index - elementData.size else index
private fun negativeMod(index: Int): Int = if (index < 0) index + elementData.size else index
@kotlin.internal.InlineOnly
private inline fun internalIndex(index: Int): Int = positiveMod(head + index)
private fun incremented(index: Int): Int = if (index == elementData.lastIndex) 0 else index + 1
public override fun removeAll(elements: Collection<E>): Boolean = filterInPlace { !elements.contains(it) }
private inline fun filterInPlace(predicate: (E) -> Boolean): Boolean {
if (this.isEmpty() || elementData.isEmpty())
return false
val tail = internalIndex(size)
var newTail = head
var modified = false
if (head < tail) {
for (index in head until tail) {
val element = elementData[index]
if (predicate(element as E))
elementData[newTail++] = element
else
modified = true
}
elementData.fill(null, newTail, tail)
} else {
for (index in head until elementData.size) {
val element = elementData[index]
elementData[index] = null
if (predicate(element as E))
elementData[newTail++] = element
else
modified = true
}
newTail = positiveMod(newTail)
for (index in 0 until tail) {
val element = elementData[index]
elementData[index] = null
if (predicate(element as E)) {
elementData[newTail] = element
newTail = incremented(newTail)
} else {
modified = true
}
}
}
if (modified)
size = negativeMod(newTail - head)
return modified
}
internal companion object {
private val emptyElementData = emptyArray<Any?>()
}
}
fun box(): String {
val ad = ArrayDeque(listOf("X", "Z", "O", "K"))
ad.removeAll(listOf("X", "Z"))
return ad[0] + ad[1]
}
@@ -1,13 +1,14 @@
// IGNORE_BACKEND: WASM
// WITH_RUNTIME
// CHECK_BYTECODE_TEXT
// JVM_IR_TEMPLATES
// 1 ASTORE 1
// 12 ALOAD 1
// JVM_TEMPLATES
// 2 ASTORE 1
// 13 ALOAD 1
// TODO separate bytecode text templates for FIR?
// -- CHECK_BYTECODE_TEXT
// -- JVM_IR_TEMPLATES
// -- 1 ASTORE 1
// -- 12 ALOAD 1
// -- JVM_TEMPLATES
// -- 2 ASTORE 1
// -- 13 ALOAD 1
@Suppress("DEPRECATION_ERROR")
fun box(): String {
@@ -18084,6 +18084,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineArgsInPlace"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@Test
@TestMetadata("arrayDequeRemoveAll.kt")
public void testArrayDequeRemoveAll() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/arrayDequeRemoveAll.kt");
}
@Test
@TestMetadata("breakInArgumentExpression.kt")
public void testBreakInArgumentExpression() throws Exception {
@@ -18096,6 +18102,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/continueInArgumentExpression.kt");
}
@Test
@TestMetadata("inlineCircularDedepency.kt")
public void testInlineCircularDedepency() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/inlineCircularDedepency.kt");
}
@Test
@TestMetadata("mapSet.kt")
public void testMapSet() throws Exception {
@@ -18228,6 +18228,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineArgsInPlace"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("arrayDequeRemoveAll.kt")
public void testArrayDequeRemoveAll() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/arrayDequeRemoveAll.kt");
}
@Test
@TestMetadata("breakInArgumentExpression.kt")
public void testBreakInArgumentExpression() throws Exception {
@@ -18240,6 +18246,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/inlineArgsInPlace/continueInArgumentExpression.kt");
}
@Test
@TestMetadata("inlineCircularDedepency.kt")
public void testInlineCircularDedepency() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/inlineCircularDedepency.kt");
}
@Test
@TestMetadata("mapSet.kt")
public void testMapSet() throws Exception {
@@ -14989,6 +14989,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineArgsInPlace"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@TestMetadata("arrayDequeRemoveAll.kt")
public void testArrayDequeRemoveAll() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/arrayDequeRemoveAll.kt");
}
@TestMetadata("breakInArgumentExpression.kt")
public void testBreakInArgumentExpression() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/breakInArgumentExpression.kt");
@@ -14999,6 +15004,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/inlineArgsInPlace/continueInArgumentExpression.kt");
}
@TestMetadata("inlineCircularDedepency.kt")
public void testInlineCircularDedepency() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/inlineCircularDedepency.kt");
}
@TestMetadata("mapSet.kt")
public void testMapSet() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/mapSet.kt");
@@ -13078,6 +13078,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineArgsInPlace"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
}
@TestMetadata("arrayDequeRemoveAll.kt")
public void testArrayDequeRemoveAll() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/arrayDequeRemoveAll.kt");
}
@TestMetadata("breakInArgumentExpression.kt")
public void testBreakInArgumentExpression() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/breakInArgumentExpression.kt");
@@ -13088,6 +13093,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
runTest("compiler/testData/codegen/box/inlineArgsInPlace/continueInArgumentExpression.kt");
}
@TestMetadata("inlineCircularDedepency.kt")
public void testInlineCircularDedepency() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/inlineCircularDedepency.kt");
}
@TestMetadata("mapSet.kt")
public void testMapSet() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/mapSet.kt");
@@ -12484,6 +12484,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineArgsInPlace"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
}
@TestMetadata("arrayDequeRemoveAll.kt")
public void testArrayDequeRemoveAll() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/arrayDequeRemoveAll.kt");
}
@TestMetadata("breakInArgumentExpression.kt")
public void testBreakInArgumentExpression() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/breakInArgumentExpression.kt");
@@ -12494,6 +12499,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/continueInArgumentExpression.kt");
}
@TestMetadata("inlineCircularDedepency.kt")
public void testInlineCircularDedepency() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/inlineCircularDedepency.kt");
}
@TestMetadata("mapSet.kt")
public void testMapSet() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/mapSet.kt");
@@ -12549,6 +12549,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineArgsInPlace"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
}
@TestMetadata("arrayDequeRemoveAll.kt")
public void testArrayDequeRemoveAll() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/arrayDequeRemoveAll.kt");
}
@TestMetadata("breakInArgumentExpression.kt")
public void testBreakInArgumentExpression() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/breakInArgumentExpression.kt");
@@ -12559,6 +12564,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/continueInArgumentExpression.kt");
}
@TestMetadata("inlineCircularDedepency.kt")
public void testInlineCircularDedepency() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/inlineCircularDedepency.kt");
}
@TestMetadata("mapSet.kt")
public void testMapSet() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/mapSet.kt");
@@ -6639,6 +6639,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineArgsInPlace"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true);
}
@TestMetadata("arrayDequeRemoveAll.kt")
public void testArrayDequeRemoveAll() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/arrayDequeRemoveAll.kt");
}
@TestMetadata("breakInArgumentExpression.kt")
public void testBreakInArgumentExpression() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/breakInArgumentExpression.kt");
@@ -6649,6 +6654,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
runTest("compiler/testData/codegen/box/inlineArgsInPlace/continueInArgumentExpression.kt");
}
@TestMetadata("inlineCircularDedepency.kt")
public void testInlineCircularDedepency() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/inlineCircularDedepency.kt");
}
@TestMetadata("mapSet.kt")
public void testMapSet() throws Exception {
runTest("compiler/testData/codegen/box/inlineArgsInPlace/mapSet.kt");