[Wasm] Switch to iso-recursive types
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt
index 01dd4071..3251bef 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt
@@ -46,7 +46,8 @@
             }
             context.wasmSymbols.wasmClassId,
             context.wasmSymbols.wasmInterfaceId,
-            context.wasmSymbols.wasmRefCast -> {
+            context.wasmSymbols.wasmRefCast,
+            context.wasmSymbols.refTest -> {
                 call.getTypeArgument(0)?.getClass()?.enqueue(from, "generic intrinsic ${call.symbol.owner.name}")
                 true
             }
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt
index b269e75..7bf26bb 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt
@@ -23,6 +23,7 @@
 import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
 import org.jetbrains.kotlin.ir.declarations.*
 import org.jetbrains.kotlin.ir.expressions.*
+import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
 import org.jetbrains.kotlin.ir.types.*
 import org.jetbrains.kotlin.ir.util.defaultType
 import org.jetbrains.kotlin.ir.util.getInlineClassBackingField
@@ -217,7 +218,7 @@
                 generateDefaultInitializerForType(context.transformType(field.type), body)
         }
 
-        body.buildGetGlobal(context.referenceClassRTT(klass.symbol))
+        generateClassRTT(klass.symbol)
         body.buildStructNew(wasmGcType)
         generateCall(expression)
     }
@@ -247,7 +248,7 @@
             body.buildConstI32Symbol(klassId)
             body.buildConstI32(0) // Any::_hashCode
             generateExpression(call.getValueArgument(0)!!)
-            body.buildGetGlobal(context.referenceClassRTT(klass.symbol))
+            generateClassRTT(klass.symbol)
             body.buildStructNew(structTypeName)
             return
         }
@@ -313,8 +314,12 @@
     }
 
     private fun generateTypeRTT(type: IrType) {
-        val rtClass = type.getRuntimeClass?.symbol ?: context.backendContext.irBuiltIns.anyClass
-        body.buildGetGlobal(context.referenceClassRTT(rtClass))
+        val klass = type.getRuntimeClass?.symbol ?: context.backendContext.irBuiltIns.anyClass
+        generateClassRTT(klass)
+    }
+
+    private fun generateClassRTT(klass: IrClassSymbol) {
+        body.buildRttCanon(context.referenceGcType(klass))
     }
 
     // Return true if generated.
@@ -323,7 +328,7 @@
         call: IrFunctionAccessExpression,
         function: IrFunction
     ): Boolean {
-        if (tryToGenerateWasmOpIntrinsicCall(call, function)) {
+        if (tryToGenerateWasmOpIntrinsicCall(function)) {
             return true
         }
 
@@ -348,6 +353,12 @@
                 body.buildRefCast()
             }
 
+            wasmSymbols.refTest -> {
+                val toType = call.getTypeArgument(0)!!
+                generateTypeRTT(toType)
+                body.buildInstr(WasmOp.REF_TEST)
+            }
+
             wasmSymbols.unboxIntrinsic -> {
                 val fromType = call.getTypeArgument(0)!!
 
@@ -544,7 +555,7 @@
     }
 
     // Return true if function is recognized as intrinsic.
-    fun tryToGenerateWasmOpIntrinsicCall(call: IrFunctionAccessExpression, function: IrFunction): Boolean {
+    fun tryToGenerateWasmOpIntrinsicCall(function: IrFunction): Boolean {
         if (function.hasWasmNoOpCastAnnotation()) {
             return true
         }
@@ -554,17 +565,7 @@
             val op = WasmOp.valueOf(opString)
             var immediates = emptyArray<WasmImmediate>()
             when (op.immediates.size) {
-                0 -> {
-                    when (op) {
-                        WasmOp.REF_TEST -> {
-                            val toIrType = call.getTypeArgument(0)!!
-                            // ref.test takes RTT as a second operand
-                            generateTypeRTT(toIrType)
-                        }
-                        else -> {
-                        }
-                    }
-                }
+                0 -> {}
                 1 -> {
                     immediates = arrayOf(
                         when (val imm = op.immediates[0]) {
@@ -572,6 +573,9 @@
                                 WasmImmediate.MemArg(0u, 0u)
                             WasmImmediateKind.STRUCT_TYPE_IDX ->
                                 WasmImmediate.GcType(context.referenceGcType(function.dispatchReceiverParameter!!.type.classOrNull!!))
+                            WasmImmediateKind.TYPE_IDX ->
+                                WasmImmediate.TypeIdx(context.referenceGcType(function.dispatchReceiverParameter!!.type.classOrNull!!))
+
                             else ->
                                 error("Immediate $imm is unsupported")
                         }
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt
index d117414..0462420 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt
@@ -86,7 +86,6 @@
 
         val wasmFunctionType =
             WasmFunctionType(
-                name = watName,
                 parameterTypes = irParameters.map { context.transformValueParameterType(it) },
                 resultTypes = listOfNotNull(resultType)
             )
@@ -105,17 +104,19 @@
             "Sanity check that $declaration is a real function that can be used in calls"
         }
 
+        val functionTypeSymbol = context.referenceFunctionType(declaration.symbol)
+
         if (importedName != null) {
             // Imported functions don't have bodies. Declaring the signature:
             context.defineFunction(
                 declaration.symbol,
-                WasmFunction.Imported(watName, wasmFunctionType, importedName)
+                WasmFunction.Imported(watName, functionTypeSymbol, importedName)
             )
             // TODO: Support re-export of imported functions.
             return
         }
 
-        val function = WasmFunction.Defined(watName, wasmFunctionType)
+        val function = WasmFunction.Defined(watName, functionTypeSymbol)
         val functionCodegenContext = WasmFunctionCodegenContextImpl(
             declaration,
             function,
@@ -201,6 +202,8 @@
             context.registerInterface(symbol)
         } else {
             val nameStr = declaration.fqNameWhenAvailable.toString()
+            val metadata = context.getClassMetadata(symbol)
+            val superClass = metadata.superClass
             val structType = WasmStructDeclaration(
                 name = nameStr,
                 fields = declaration.allFields(irBuiltIns).map {
@@ -209,40 +212,11 @@
                         type = context.transformFieldType(it.type),
                         isMutable = true
                     )
-                }
+                },
+                superClass?.let { context.referenceGcType(superClass.klass.symbol) }
             )
 
             context.defineGcType(symbol, structType)
-
-            var depth = 0
-            val metadata = context.getClassMetadata(symbol)
-            var subMetadata = metadata
-            while (true) {
-                subMetadata = subMetadata.superClass ?: break
-                depth++
-            }
-
-            val initBody = mutableListOf<WasmInstr>()
-            val wasmExpressionGenerator = WasmIrExpressionBuilder(initBody)
-
-            val wasmGcType = context.referenceGcType(symbol)
-            val superClass = metadata.superClass
-            if (superClass != null) {
-                val superRTT = context.referenceClassRTT(superClass.klass.symbol)
-                wasmExpressionGenerator.buildGetGlobal(superRTT)
-                wasmExpressionGenerator.buildRttSub(wasmGcType)
-            } else {
-                wasmExpressionGenerator.buildRttCanon(wasmGcType)
-            }
-
-            val rtt = WasmGlobal(
-                name = "rtt_of_$nameStr",
-                isMutable = false,
-                type = WasmRtt(depth, WasmSymbol(structType)),
-                init = initBody
-            )
-
-            context.defineRTT(symbol, rtt)
             context.registerClass(symbol)
             context.generateTypeInfo(symbol, binaryDataStruct(metadata))
 
@@ -395,7 +369,7 @@
     WasmF32 -> g.buildConstF32(0f)
     WasmF64 -> g.buildConstF64(0.0)
     is WasmRefNullType -> g.buildRefNull(type.heapType)
-    is WasmExternRef, is WasmAnyRef -> g.buildRefNull(WasmHeapType.Simple.Extern)
+    is WasmAnyRef -> g.buildRefNull(WasmHeapType.Simple.Any)
     WasmUnreachableType -> error("Unreachable type can't be initialized")
     else -> error("Unknown value type ${type.name}")
 }
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/TypeTransformer.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/TypeTransformer.kt
index f787b57..b3bb08f 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/TypeTransformer.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/TypeTransformer.kt
@@ -82,11 +82,11 @@
                 WasmF64
 
             builtIns.nothingNType ->
-                WasmExternRef
+                WasmAnyRef
 
             // Value will not be created. Just using a random Wasm type.
             builtIns.nothingType ->
-                WasmExternRef
+                WasmAnyRef
 
             symbols.voidType ->
                 error("Void type can't be used as a value")
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmBaseCodegenContext.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmBaseCodegenContext.kt
index 7c1678b..97e78c5 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmBaseCodegenContext.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmBaseCodegenContext.kt
@@ -29,7 +29,6 @@
     fun referenceClassId(irClass: IrClassSymbol): WasmSymbol<Int>
     fun referenceInterfaceId(irInterface: IrClassSymbol): WasmSymbol<Int>
     fun referenceVirtualFunctionId(irFunction: IrSimpleFunctionSymbol): WasmSymbol<Int>
-    fun referenceClassRTT(irClass: IrClassSymbol): WasmSymbol<WasmGlobal>
 
     fun referenceSignatureId(signature: WasmSignature): WasmSymbol<Int>
 
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt
index 35b3077..84c4975 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt
@@ -5,7 +5,6 @@
 
 package org.jetbrains.kotlin.backend.wasm.ir2wasm
 
-import org.jetbrains.kotlin.backend.common.push
 import org.jetbrains.kotlin.backend.wasm.lower.WasmSignature
 import org.jetbrains.kotlin.ir.IrBuiltIns
 import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
@@ -35,11 +34,7 @@
     val stringLiteralId =
         ReferencableElements<String, Int>()
 
-    val runtimeTypes =
-        ReferencableAndDefinable<IrClassSymbol, WasmGlobal>()
-
     val tagFuncType = WasmFunctionType(
-        "ex_handling_tag",
         listOf(
             WasmRefNullType(WasmHeapType.Type(gcTypes.reference(irBuiltIns.throwableClass)))
         ),
@@ -124,9 +119,18 @@
     fun linkWasmCompiledFragments(): WasmModule {
         bind(functions.unbound, functions.defined)
         bind(globals.unbound, globals.defined)
-        bind(functionTypes.unbound, functionTypes.defined)
+
         bind(gcTypes.unbound, gcTypes.defined)
-        bind(runtimeTypes.unbound, runtimeTypes.defined)
+
+        // Associate function types to a single canonical function type
+        val canonicalFunctionTypes =
+            functionTypes.elements.associateWithTo(LinkedHashMap()) { it }
+
+        functionTypes.unbound.forEach { (irSymbol, wasmSymbol) ->
+            if (irSymbol !in functionTypes.defined)
+                error("Can't link symbol ${irSymbolDebugDump(irSymbol)}")
+            wasmSymbol.bind(canonicalFunctionTypes.getValue(functionTypes.defined.getValue(irSymbol)))
+        }
 
         val klassIds = mutableMapOf<IrClassSymbol, Int>()
         var currentDataSectionAddress = 0
@@ -261,8 +265,8 @@
             )
         }
 
-        val masterInitFunctionType = WasmFunctionType("__init_t", emptyList(), emptyList())
-        val masterInitFunction = WasmFunction.Defined("__init", masterInitFunctionType)
+        val masterInitFunctionType = WasmFunctionType(emptyList(), emptyList())
+        val masterInitFunction = WasmFunction.Defined("__init", WasmSymbol(masterInitFunctionType))
         with(WasmIrExpressionBuilder(masterInitFunction.instructions)) {
             initFunctions.sortedBy { it.priority }.forEach {
                 buildCall(WasmSymbol(it.function))
@@ -284,18 +288,28 @@
 
         val importedFunctions = functions.elements.filterIsInstance<WasmFunction.Imported>()
 
-        // Sorting by depth for a valid init order
-        val sortedRttGlobals = runtimeTypes.elements.sortedBy { (it.type as WasmRtt).depth }
+        fun wasmTypeDeclarationOrderKey(declaration: WasmTypeDeclaration): Int {
+            return when (declaration) {
+                is WasmArrayDeclaration -> 0
+                is WasmFunctionType -> 0
+                is WasmStructDeclaration ->
+                    // Subtype depth
+                    declaration.superType?.let { wasmTypeDeclarationOrderKey(it.owner) + 1 } ?: 0
+            }
+        }
+
+        val sortedGcTypes = gcTypes.elements.sortedBy(::wasmTypeDeclarationOrderKey)
 
         val module = WasmModule(
-            functionTypes = functionTypes.elements + tagFuncType + masterInitFunctionType,
-            gcTypes = gcTypes.elements,
+            functionTypes = canonicalFunctionTypes.values.toList() + tagFuncType + masterInitFunctionType,
+            gcTypes = sortedGcTypes,
+            gcTypesInRecursiveGroup = true,
             importsInOrder = importedFunctions,
             importedFunctions = importedFunctions,
             definedFunctions = functions.elements.filterIsInstance<WasmFunction.Defined>() + masterInitFunction,
             tables = listOf(table) + interfaceMethodTables.elements,
             memories = listOf(memory),
-            globals = globals.elements + sortedRttGlobals,
+            globals = globals.elements,
             exports = exports,
             startFunction = null,  // Module is initialized via export call
             elements = listOf(elements) + interfaceTableElements,
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContext.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContext.kt
index 8048b95..a3803c3 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContext.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContext.kt
@@ -18,7 +18,6 @@
     fun defineFunction(irFunction: IrFunctionSymbol, wasmFunction: WasmFunction)
     fun defineGlobal(irField: IrFieldSymbol, wasmGlobal: WasmGlobal)
     fun defineGcType(irClass: IrClassSymbol, wasmType: WasmTypeDeclaration)
-    fun defineRTT(irClass: IrClassSymbol, wasmGlobal: WasmGlobal)
     fun defineFunctionType(irFunction: IrFunctionSymbol, wasmFunctionType: WasmFunctionType)
     fun defineInterfaceMethodTable(irFunction: IrFunctionSymbol, wasmTable: WasmTable)
     fun addJsFun(importName: String, jsCode: String)
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContextImpl.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContextImpl.kt
index 2d62e5f..7c32ae9 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContextImpl.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContextImpl.kt
@@ -106,10 +106,6 @@
         wasmFragment.gcTypes.define(irClass, wasmType)
     }
 
-    override fun defineRTT(irClass: IrClassSymbol, wasmGlobal: WasmGlobal) {
-        wasmFragment.runtimeTypes.define(irClass, wasmGlobal)
-    }
-
     override fun defineFunctionType(irFunction: IrFunctionSymbol, wasmFunctionType: WasmFunctionType) {
         wasmFragment.functionTypes.define(irFunction, wasmFunctionType)
     }
@@ -157,9 +153,6 @@
         return wasmFragment.gcTypes.reference(irClass)
     }
 
-    override fun referenceClassRTT(irClass: IrClassSymbol): WasmSymbol<WasmGlobal> =
-        wasmFragment.runtimeTypes.reference(irClass)
-
     override fun referenceFunctionType(irFunction: IrFunctionSymbol): WasmSymbol<WasmFunctionType> =
         wasmFragment.functionTypes.reference(irFunction)
 
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/GenericReturnTypeLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/GenericReturnTypeLowering.kt
index c8f8993..b384b05 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/GenericReturnTypeLowering.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/GenericReturnTypeLowering.kt
@@ -51,9 +51,6 @@
         val function: IrSimpleFunction =
             call.symbol.owner as? IrSimpleFunction ?: return call
 
-        if (!function.realOverrideTarget.returnType.isTypeParameter())
-            return call
-
         val erasedReturnType: IrType =
             function.realOverrideTarget.returnType.eraseUpperBoundType()
 
@@ -72,11 +69,6 @@
             )
 
             context.createIrBuilder(scopeOwnerSymbol).apply {
-                if (call.type.isUnit()) {
-                    return irComposite(call) {
-                        +newCall
-                    }
-                }
                 return irImplicitCast(newCall, call.type)
             }
         }
diff --git a/compiler/testData/codegen/box/controlStructures/kt513.kt b/compiler/testData/codegen/box/controlStructures/kt513.kt
index 3859af0..e0beb88 100644
--- a/compiler/testData/codegen/box/controlStructures/kt513.kt
+++ b/compiler/testData/codegen/box/controlStructures/kt513.kt
@@ -1,3 +1,6 @@
+// V8 Crash https://bugs.chromium.org/p/v8/issues/detail?id=12640
+// IGNORE_BACKEND: WASM
+
 // IGNORE_BACKEND: JS_IR
 // IGNORE_BACKEND: JS_IR_ES6
 // WITH_STDLIB
diff --git a/compiler/testData/codegen/box/defaultArguments/function/covariantOverride.kt b/compiler/testData/codegen/box/defaultArguments/function/covariantOverride.kt
index 5bd56f1..fddf101 100644
--- a/compiler/testData/codegen/box/defaultArguments/function/covariantOverride.kt
+++ b/compiler/testData/codegen/box/defaultArguments/function/covariantOverride.kt
@@ -1,7 +1,3 @@
-// IGNORE_BACKEND: WASM
-// WASM_MUTE_REASON: FAKE_OVERRIDE_ISSUES
-// On wasm this will produce conflicting return types, foo will return Any but we will try to interpret it as String.
-// Before wasm native strings this worked by chance because we added unbox intrinsic for strings.
 
 open class Foo {
     open fun foo(x: CharSequence = "O"): CharSequence = x
diff --git a/compiler/testData/codegen/box/defaultArguments/function/covariantOverrideGeneric.kt b/compiler/testData/codegen/box/defaultArguments/function/covariantOverrideGeneric.kt
index f04dcc9..c982d8a 100644
--- a/compiler/testData/codegen/box/defaultArguments/function/covariantOverrideGeneric.kt
+++ b/compiler/testData/codegen/box/defaultArguments/function/covariantOverrideGeneric.kt
@@ -1,7 +1,3 @@
-// IGNORE_BACKEND: WASM
-// WASM_MUTE_REASON: FAKE_OVERRIDE_ISSUES
-// On wasm this will produce conflicting return types, foo will return Any but we will try to interpret it as String.
-// Before wasm native strings this worked by chance because we added unbox intrinsic for strings.
 
 open class Foo {
     open fun foo(x: CharSequence = "O"): CharSequence = x
diff --git a/compiler/testData/codegen/box/lazyCodegen/safeCallAndArray.kt b/compiler/testData/codegen/box/lazyCodegen/safeCallAndArray.kt
index 90b61d6..029374d 100644
--- a/compiler/testData/codegen/box/lazyCodegen/safeCallAndArray.kt
+++ b/compiler/testData/codegen/box/lazyCodegen/safeCallAndArray.kt
@@ -1,3 +1,6 @@
+// V8 Crash https://bugs.chromium.org/p/v8/issues/detail?id=12640
+// IGNORE_BACKEND: WASM
+
 class C {
     fun calc() : String {
         return "OK"
diff --git a/compiler/testData/codegen/box/regressions/resolvedCallForGetOperator.kt b/compiler/testData/codegen/box/regressions/resolvedCallForGetOperator.kt
index bf8abb3..1e1e5f8 100644
--- a/compiler/testData/codegen/box/regressions/resolvedCallForGetOperator.kt
+++ b/compiler/testData/codegen/box/regressions/resolvedCallForGetOperator.kt
@@ -1,3 +1,6 @@
+// V8 Crash https://bugs.chromium.org/p/v8/issues/detail?id=12640
+// IGNORE_BACKEND: WASM
+
 // WITH_STDLIB
 
 val targetNameLists: Map<String, String> = mapOf("1"         to "OK")
diff --git a/js/js.tests/build.gradle.kts b/js/js.tests/build.gradle.kts
index 8ed125b..55a0633 100644
--- a/js/js.tests/build.gradle.kts
+++ b/js/js.tests/build.gradle.kts
@@ -153,7 +153,7 @@
 }
 
 val v8edition = "rel" // rel or dbg
-val v8version = "9.2.212"
+val v8version = "10.0.132"
 val v8fileName = "v8-${v8osString}-${v8edition}-${v8version}"
 val v8url = "https://storage.googleapis.com/chromium-v8/official/canary/$v8fileName.zip"
 
diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/testOld/BasicWasmBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/testOld/BasicWasmBoxTest.kt
index 010bf48..9f0a21e 100644
--- a/js/js.tests/test/org/jetbrains/kotlin/js/testOld/BasicWasmBoxTest.kt
+++ b/js/js.tests/test/org/jetbrains/kotlin/js/testOld/BasicWasmBoxTest.kt
@@ -179,7 +179,6 @@
                 File(dir, "test.js").writeText(testJs)
                 ExternalTool(System.getProperty("javascript.engine.path.V8"))
                     .run(
-                        "--experimental-wasm-typed-funcref",
                         "--experimental-wasm-gc",
                         "--experimental-wasm-eh",
                         *jsFilesBefore.map { File(it).absolutePath }.toTypedArray(),
diff --git a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt
index decb962..cc94bb4 100644
--- a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt
+++ b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt
@@ -285,7 +285,6 @@
 @WasmOp(WasmOp.REF_EQ)
 public external fun wasm_ref_eq(a: Any?, b: Any?): Boolean
 
-@WasmOp(WasmOp.REF_TEST)
 public external fun <T> wasm_ref_test(a: Any?): Boolean
 
 // ---
diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Declarations.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Declarations.kt
index c0492de..706b532 100644
--- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Declarations.kt
+++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Declarations.kt
@@ -9,6 +9,7 @@
 class WasmModule(
     val functionTypes: List<WasmFunctionType> = emptyList(),
     val gcTypes: List<WasmTypeDeclaration> = emptyList(),
+    val gcTypesInRecursiveGroup: Boolean,
 
     val importsInOrder: List<WasmNamedModuleField> = emptyList(),
     val importedFunctions: List<WasmFunction.Imported> = emptyList(),
@@ -38,18 +39,18 @@
 
 sealed class WasmFunction(
     override val name: String,
-    val type: WasmFunctionType
+    val type: WasmSymbolReadOnly<WasmFunctionType>
 ) : WasmNamedModuleField() {
     class Defined(
         name: String,
-        type: WasmFunctionType,
+        type: WasmSymbolReadOnly<WasmFunctionType>,
         val locals: MutableList<WasmLocal> = mutableListOf(),
         val instructions: MutableList<WasmInstr> = mutableListOf()
     ) : WasmFunction(name, type)
 
     class Imported(
         name: String,
-        type: WasmFunctionType,
+        type: WasmSymbolReadOnly<WasmFunctionType>,
         val importPair: WasmImportPair
     ) : WasmFunction(name, type)
 }
@@ -146,15 +147,15 @@
     override val name: String
 ) : WasmNamedModuleField()
 
-class WasmFunctionType(
-    name: String,
+data class WasmFunctionType(
     val parameterTypes: List<WasmType>,
     val resultTypes: List<WasmType>
-) : WasmTypeDeclaration(name)
+) : WasmTypeDeclaration("")
 
 class WasmStructDeclaration(
     name: String,
-    val fields: List<WasmStructFieldDeclaration>
+    val fields: List<WasmStructFieldDeclaration>,
+    val superType: WasmSymbolReadOnly<WasmTypeDeclaration>?
 ) : WasmTypeDeclaration(name)
 
 class WasmArrayDeclaration(
diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt
index 1896595..1e549e4 100644
--- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt
+++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt
@@ -343,6 +343,8 @@
     // GC
     STRUCT_NEW_WITH_RTT("struct.new_with_rtt", 0xFB_01, STRUCT_TYPE_IDX),
     STRUCT_NEW_DEFAULT_WITH_RTT("struct.new_default_with_rtt", 0xFB_02, STRUCT_TYPE_IDX),
+    STRUCT_NEW("struct.new", 0xFB_07, STRUCT_TYPE_IDX),
+    STRUCT_NEW_DEFAULT("struct.new_default", 0xFB_08, STRUCT_TYPE_IDX),
     STRUCT_GET("struct.get", 0xFB_03, listOf(STRUCT_TYPE_IDX, STRUCT_FIELD_IDX)),
     STRUCT_GET_S("struct.get_s", 0xFB_04, listOf(STRUCT_TYPE_IDX, STRUCT_FIELD_IDX)),
     STRUCT_GET_U("struct.get_u", 0xFB_05, listOf(STRUCT_TYPE_IDX, STRUCT_FIELD_IDX)),
@@ -350,6 +352,8 @@
 
     ARRAY_NEW_WITH_RTT("array.new_with_rtt", 0xFB_11, STRUCT_TYPE_IDX),
     ARRAY_NEW_DEFAULT_WITH_RTT("array.new_default_with_rtt", 0xFB_12, STRUCT_TYPE_IDX),
+    ARRAY_NEW("array.new", 0xFB_1B, STRUCT_TYPE_IDX),
+    ARRAY_NEW_DEFAULT("array.new_default", 0xFB_1C, STRUCT_TYPE_IDX),
     ARRAY_GET("array.get", 0xFB_13, listOf(STRUCT_TYPE_IDX)),
     ARRAY_GET_S("array.get_s", 0xFB_14, listOf(STRUCT_TYPE_IDX)),
     ARRAY_GET_U("array.get_u", 0xFB_15, listOf(STRUCT_TYPE_IDX)),
@@ -362,9 +366,10 @@
 
     RTT_CANON("rtt.canon", 0xFB_30, TYPE_IDX),
 
-    RTT_SUB("rtt.sub", 0xFB_31, TYPE_IDX),
     REF_TEST("ref.test", 0xFB_40),
+    REF_TEST_STATIC("ref.test_static", 0xFB_44, STRUCT_TYPE_IDX),
     REF_CAST("ref.cast", 0xFB_41),
+    REF_CAST_STATIC("ref.cast_static", 0xFB_45, STRUCT_TYPE_IDX),
 
     BR_ON_CAST("br_on_cast", 0xFB_42, listOf(LABEL_IDX)),
 
diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Types.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Types.kt
index b719cc0..48106e7 100644
--- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Types.kt
+++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Types.kt
@@ -22,22 +22,21 @@
 object WasmI8 : WasmType("i8", -0x6)
 object WasmI16 : WasmType("i16", -0x7)
 object WasmFuncRef : WasmType("funcref", -0x10)
-object WasmExternRef : WasmType("externref", -0x11)
 object WasmAnyRef : WasmType("anyref", -0x12)
 object WasmEqRef : WasmType("eqref", -0x13)
 
-class WasmRefNullType(val heapType: WasmHeapType) : WasmType("ref null", -0x14)
-class WasmRefType(val heapType: WasmHeapType) : WasmType("ref", -0x15)
+data class WasmRefNullType(val heapType: WasmHeapType) : WasmType("ref null", -0x14)
+data class WasmRefType(val heapType: WasmHeapType) : WasmType("ref", -0x15)
 
 @Suppress("unused")
 object WasmI31Ref : WasmType("i31ref", -0x16)
-class WasmRtt(val depth: Int, val type: WasmSymbolReadOnly<WasmTypeDeclaration>) : WasmType("rtt", -0x17)
+data class WasmRtt(val type: WasmSymbolReadOnly<WasmTypeDeclaration>) : WasmType("rtt", -0x18)
 
 @Suppress("unused")
 object WasmDataRef : WasmType("dataref", -0x19)
 
 sealed class WasmHeapType {
-    class Type(val type: WasmSymbolReadOnly<WasmTypeDeclaration>) : WasmHeapType() {
+    data class Type(val type: WasmSymbolReadOnly<WasmTypeDeclaration>) : WasmHeapType() {
         override fun toString(): String {
             return "Type:$type"
         }
@@ -45,7 +44,7 @@
 
     sealed class Simple(val name: String, val code: Byte) : WasmHeapType() {
         object Func : Simple("func", -0x10)
-        object Extern : Simple("extern", -0x11)
+        object Any : Simple("any", -0x12)
         object Eq : Simple("eq", -0x13)
 
         @Suppress("unused")
@@ -70,7 +69,7 @@
         is WasmRefType -> heapType
         is WasmRefNullType -> heapType
         is WasmEqRef -> WasmHeapType.Simple.Eq
-        is WasmExternRef -> WasmHeapType.Simple.Extern
+        is WasmAnyRef -> WasmHeapType.Simple.Any
         is WasmFuncRef -> WasmHeapType.Simple.Func
         else -> error("Unknown heap type for type $this")
     }
diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/WasmExpressionBuilder.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/WasmExpressionBuilder.kt
index 14b5695..6eacbfe 100644
--- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/WasmExpressionBuilder.kt
+++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/WasmExpressionBuilder.kt
@@ -142,12 +142,12 @@
         buildInstr(WasmOp.REF_CAST)
     }
 
-    fun buildRefNull(type: WasmHeapType) {
-        buildInstr(WasmOp.REF_NULL, WasmImmediate.HeapType(WasmRefType(type)))
+    fun buildRefCastStatic(type: WasmSymbolReadOnly<WasmTypeDeclaration>) {
+        buildInstr(WasmOp.REF_CAST_STATIC, WasmImmediate.TypeIdx(type))
     }
 
-    fun buildRttSub(decl: WasmSymbol<WasmTypeDeclaration>) {
-        buildInstr(WasmOp.RTT_SUB, WasmImmediate.TypeIdx(decl))
+    fun buildRefNull(type: WasmHeapType) {
+        buildInstr(WasmOp.REF_NULL, WasmImmediate.HeapType(WasmRefType(type)))
     }
 
     fun buildRttCanon(decl: WasmSymbol<WasmTypeDeclaration>) {
diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmBinaryToIR.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmBinaryToIR.kt
index e0346e7..c30a150 100644
--- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmBinaryToIR.kt
+++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmBinaryToIR.kt
@@ -97,7 +97,7 @@
                                     val type = functionTypes[b.readVarUInt32AsInt()]
                                     importedFunctions += WasmFunction.Imported(
                                         name = "",
-                                        type = type,
+                                        type = WasmSymbol(type),
                                         importPair = importPair,
                                     ).also { importsInOrder.add(it) }
                                 }
@@ -141,7 +141,7 @@
                             definedFunctions.add(
                                 WasmFunction.Defined(
                                     "",
-                                    functionType,
+                                    WasmSymbol(functionType),
                                     locals = functionType.parameterTypes.mapIndexed { index, wasmType ->
                                         WasmLocal(index, "", wasmType, true)
                                     }.toMutableList()
@@ -330,6 +330,7 @@
         return WasmModule(
             functionTypes = functionTypes,
             gcTypes = gcTypes,
+            gcTypesInRecursiveGroup = false,
             importsInOrder = importsInOrder,
             importedFunctions = importedFunctions,
             importedMemories = importedMemories,
@@ -450,7 +451,7 @@
             (-0x20).toByte() -> {
                 val types = mapVector { readValueType() }
                 val returnTypes = mapVector { readValueType() }
-                return WasmFunctionType("", types, returnTypes)
+                return WasmFunctionType(types, returnTypes)
             }
 
             else -> TODO()
@@ -466,7 +467,6 @@
         WasmI8,
         WasmI16,
         WasmFuncRef,
-        WasmExternRef,
         WasmAnyRef,
         WasmEqRef
     ).associateBy { it.code }
@@ -490,7 +490,7 @@
 
         return when (code.toInt()) {
             0x70 -> WasmFuncRef
-            0x6F -> WasmExternRef
+            0x6F -> WasmAnyRef
             else -> error("Unsupported heap type ${code.toString(16)}")
         }
     }
diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt
index 057f198..dc3f3f2 100644
--- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt
+++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt
@@ -11,7 +11,12 @@
 import java.io.ByteArrayOutputStream
 import java.io.OutputStream
 
-class WasmIrToBinary(outputStream: OutputStream, val module: WasmModule, val moduleName: String, val emitNameSection: Boolean) {
+class WasmIrToBinary(
+    outputStream: OutputStream,
+    val module: WasmModule,
+    val moduleName: String,
+    val emitNameSection: Boolean,
+) {
     var b: ByteWriter = ByteWriter.OutputStream(outputStream)
 
     fun appendWasmModule() {
@@ -21,13 +26,17 @@
         with(module) {
             // type section
             appendSection(1u) {
+                if (module.gcTypesInRecursiveGroup) {
+                    appendVectorSize(1)
+                    b.writeByte(0x4f)
+                }
                 appendVectorSize(functionTypes.size + gcTypes.size)
                 functionTypes.forEach { appendFunctionTypeDeclaration(it) }
                 gcTypes.forEach {
                     when (it) {
                         is WasmStructDeclaration -> appendStructTypeDeclaration(it)
                         is WasmArrayDeclaration -> appendArrayTypeDeclaration(it)
-                        is WasmFunctionType -> {}
+                        is WasmFunctionType -> error("Function type in GC types")
                     }
                 }
             }
@@ -277,6 +286,12 @@
     }
 
     private fun appendStructTypeDeclaration(type: WasmStructDeclaration) {
+        val superType = type.superType
+        if (superType != null) {
+            b.writeVarInt7(-0x30)
+            appendVectorSize(1)
+            appendModuleFieldReference(superType.owner)
+        }
         b.writeVarInt7(-0x21)
         b.writeVarUInt32(type.fields.size)
         type.fields.forEach {
@@ -303,11 +318,11 @@
         b.writeString(function.importPair.moduleName)
         b.writeString(function.importPair.declarationName)
         b.writeByte(0)  // Function external kind.
-        b.writeVarUInt32(function.type.index)
+        b.writeVarUInt32(function.type.owner.index)
     }
 
     private fun appendDefinedFunction(function: WasmFunction.Defined) {
-        b.writeVarUInt32(function.type.index)
+        b.writeVarUInt32(function.type.owner.index)
     }
 
     private fun appendTable(table: WasmTable) {
@@ -483,7 +498,6 @@
             appendHeapType(type.heapType)
         }
         if (type is WasmRtt) {
-            b.writeVarUInt32(type.depth)
             appendModuleFieldReference(type.type.owner)
         }
     }
diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToText.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToText.kt
index 3e80118..c900836 100644
--- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToText.kt
+++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToText.kt
@@ -246,12 +246,26 @@
         }
     }
 
+    private inline fun maybeSubType(superType: WasmTypeDeclaration?, body: () -> Unit) {
+        if (superType != null) {
+            sameLineList("sub") {
+                appendModuleFieldReference(superType)
+                body()
+            }
+        } else {
+            body()
+        }
+    }
+
+
     private fun appendStructTypeDeclaration(type: WasmStructDeclaration) {
         newLineList("type") {
             appendModuleFieldReference(type)
-            sameLineList("struct") {
-                type.fields.forEach {
-                    appendStructField(it)
+            maybeSubType(type.superType?.owner) {
+                sameLineList("struct") {
+                    type.fields.forEach {
+                        appendStructField(it)
+                    }
                 }
             }
         }
@@ -287,9 +301,9 @@
             appendModuleFieldReference(function)
             sameLineList("type") { appendModuleFieldReference(function.type) }
             function.locals.forEach { if (it.isParameter) appendLocal(it) }
-            if (function.type.resultTypes.isNotEmpty()) {
+            if (function.type.owner.resultTypes.isNotEmpty()) {
                 sameLineList("result") {
-                    function.type.resultTypes.forEach { appendType(it) }
+                    function.type.owner.resultTypes.forEach { appendType(it) }
                 }
             }
             function.locals.forEach { if (!it.isParameter) appendLocal(it) }
@@ -438,7 +452,7 @@
     fun appendReferencedType(type: WasmType) {
         when (type) {
             is WasmFuncRef -> appendElement("func")
-            is WasmExternRef -> appendElement("extern")
+            is WasmAnyRef -> appendElement("any")
             else -> TODO()
         }
     }
@@ -457,7 +471,6 @@
 
             is WasmRtt ->
                 sameLineList("rtt") {
-                    appendElement(type.depth.toString())
                     appendModuleFieldReference(type.type.owner)
                 }
 
@@ -489,6 +502,10 @@
         if (id != 0) appendElement(id.toString())
     }
 
+    fun appendModuleFieldReference(field: WasmSymbolReadOnly<WasmNamedModuleField>) {
+        appendModuleFieldReference(field.owner)
+    }
+
     fun appendModuleFieldReference(field: WasmNamedModuleField) {
         val id = field.id
             ?: error("${field::class} ${field.name} ID is unlinked")