Get rid of redundant boxing when comparing inline class instances

^KT-33722: Fixed
diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java
index d04eae3..f8022aa 100644
--- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java
+++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java
@@ -3836,6 +3836,12 @@
         }
 
         @Test
+        @TestMetadata("kt33722.kt")
+        public void testKt33722() throws Exception {
+            runTest("compiler/testData/codegen/bytecodeText/inlineClasses/kt33722.kt");
+        }
+
+        @Test
         @TestMetadata("mangledInlineClassInterfaceImplementation.kt")
         public void testMangledInlineClassInterfaceImplementation() throws Exception {
             runTest("compiler/testData/codegen/bytecodeText/inlineClasses/mangledInlineClassInterfaceImplementation.kt");
diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt
index d3a36aa..27f0ba1 100644
--- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt
+++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt
@@ -22,6 +22,7 @@
 import org.jetbrains.kotlin.ir.declarations.*
 import org.jetbrains.kotlin.ir.expressions.*
 import org.jetbrains.kotlin.ir.expressions.impl.*
+import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
 import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
 import org.jetbrains.kotlin.ir.transformStatement
 import org.jetbrains.kotlin.ir.types.*
@@ -344,32 +345,49 @@
                 coerceInlineClasses(arg, expression.symbol.owner.dispatchReceiverParameter!!.type, expression.type)
             }
             // Specialize calls to equals when the left argument is a value of inline class type.
-            expression.isSpecializedInlineClassEqEq -> {
+            expression.isSpecializedInlineClassEqEq || expression.isSpecializedInlineClassEquals -> {
                 expression.transformChildrenVoid()
+                val leftOp: IrExpression
+                val rightOp: IrExpression
+                if (expression.isSpecializedInlineClassEqEq) {
+                    leftOp = expression.getValueArgument(0)!!
+                    rightOp = expression.getValueArgument(1)!!
+                } else {
+                    leftOp = expression.dispatchReceiver!!
+                    rightOp = expression.getValueArgument(0)!!
+                }
                 context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, expression.startOffset, expression.endOffset)
-                    .specializeEqualsCall(expression.getValueArgument(0)!!, expression.getValueArgument(1)!!)
+                    .specializeEqualsCall(leftOp, rightOp)
                     ?: expression
             }
-
             else ->
                 super.visitCall(expression)
         }
 
+    private val IrCall.isSpecializedInlineClassEquals: Boolean
+        get() {
+            return isSpecializedInlineClassEqualityCheck { symbol.owner.isEquals() }
+        }
+
     private val IrCall.isSpecializedInlineClassEqEq: Boolean
         get() {
             // Note that reference equality (x === y) is not allowed on values of inline class type,
             // so it is enough to check for eqeq.
-            if (symbol != context.irBuiltIns.eqeqSymbol)
-                return false
-
-            val leftClass = getValueArgument(0)?.type?.classOrNull?.owner?.takeIf { it.isSingleFieldValueClass }
-                ?: return false
-
-            // Before version 1.4, we cannot rely on the Result.equals-impl0 method
-            return (leftClass.fqNameWhenAvailable != StandardNames.RESULT_FQ_NAME) ||
-                    context.state.languageVersionSettings.apiVersion >= ApiVersion.KOTLIN_1_4
+            return isSpecializedInlineClassEqualityCheck { symbol == context.irBuiltIns.eqeqSymbol }
         }
 
+    private inline fun IrCall.isSpecializedInlineClassEqualityCheck(calleePredicate: (IrSimpleFunctionSymbol) -> Boolean): Boolean {
+
+        if (!calleePredicate(symbol)) return false
+
+        val leftClass = getValueArgument(0)?.type?.classOrNull?.owner?.takeIf { it.isSingleFieldValueClass }
+            ?: return false
+
+        // Before version 1.4, we cannot rely on the Result.equals-impl0 method
+        return (leftClass.fqNameWhenAvailable != StandardNames.RESULT_FQ_NAME) ||
+                context.state.languageVersionSettings.apiVersion >= ApiVersion.KOTLIN_1_4
+    }
+
     override fun visitGetField(expression: IrGetField): IrExpression {
         val field = expression.symbol.owner
         val parent = field.parent
diff --git a/compiler/testData/codegen/bytecodeText/inlineClasses/equalsIsCalledByInlineClass.kt b/compiler/testData/codegen/bytecodeText/inlineClasses/equalsIsCalledByInlineClass.kt
index ace25a5..4ba4b2f 100644
--- a/compiler/testData/codegen/bytecodeText/inlineClasses/equalsIsCalledByInlineClass.kt
+++ b/compiler/testData/codegen/bytecodeText/inlineClasses/equalsIsCalledByInlineClass.kt
@@ -9,6 +9,10 @@
 fun testNZ(z: Z?) = z?.equals(z)
 
 // @TestKt.class:
+// JVM_IR_TEMPLATES
 // 0 INVOKESTATIC Z\$Erased\.equals
 // 0 INVOKESTATIC Z\-Erased\.equals
-// 3 INVOKESTATIC Z\.equals-impl \(ILjava/lang/Object;\)Z
\ No newline at end of file
+// 1 INVOKESTATIC Z\.equals-impl0 \(II\)Z
+// 1 INVOKESTATIC Z\.equals-impl \(ILjava/lang/Object;\)Z
+// 1 INVOKEVIRTUAL Z.equals
+// 0 INVOKEVIRTUAL Z.unbox-impl
\ No newline at end of file
diff --git a/compiler/testData/codegen/bytecodeText/inlineClasses/kt33722.kt b/compiler/testData/codegen/bytecodeText/inlineClasses/kt33722.kt
new file mode 100644
index 0000000..52c27dd
--- /dev/null
+++ b/compiler/testData/codegen/bytecodeText/inlineClasses/kt33722.kt
@@ -0,0 +1,17 @@
+// WITH_STDLIB
+// TARGET_BACKEND: JVM_IR
+
+fun foo() {
+    val result = Result.success("yes!")
+    val other = Result.success("nope")
+
+    result == other
+    result != other
+
+    result.equals(other)
+    !result.equals(other)
+}
+
+// CHECK_BYTECODE_TEXT
+// 0 INVOKESTATIC kotlin/Result.box-impl
+// 4 INVOKESTATIC kotlin/Result.equals-impl0
\ No newline at end of file
diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java
index baa71aa..fdbd577 100644
--- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java
+++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java
@@ -3836,6 +3836,12 @@
         }
 
         @Test
+        @TestMetadata("kt33722.kt")
+        public void testKt33722() throws Exception {
+            runTest("compiler/testData/codegen/bytecodeText/inlineClasses/kt33722.kt");
+        }
+
+        @Test
         @TestMetadata("mangledInlineClassInterfaceImplementation.kt")
         public void testMangledInlineClassInterfaceImplementation() throws Exception {
             runTest("compiler/testData/codegen/bytecodeText/inlineClasses/mangledInlineClassInterfaceImplementation.kt");