[Wasm] Generate [K][Suspend]FunctionN on demand and support big arity

This commit is contained in:
Svyatoslav Kuzmich
2021-10-06 14:09:41 +03:00
parent 64a30cd95e
commit 1ed2748334
19 changed files with 38 additions and 468 deletions
@@ -341,13 +341,6 @@ private val wasmNullSpecializationLowering = makeWasmModulePhase(
description = "Specialize assigning Nothing? values to other types."
)
private val wasmFunctionInterfaceReplacer = makeWasmModulePhase(
::WasmFunctionInterfaceReplacer,
name = "WasmFunctionInterfaceReplacer",
description = "Replace function interface with concrete runtime interfaces"
)
private val staticMembersLoweringPhase = makeWasmModulePhase(
::StaticMembersLowering,
name = "StaticMembersLowering",
@@ -527,7 +520,6 @@ val wasmPhases = NamedCompilerPhase(
virtualDispatchReceiverExtractionPhase then
staticMembersLoweringPhase then
wasmFunctionInterfaceReplacer then
wasmNullSpecializationLowering then
validateIrAfterLowering
)
@@ -168,18 +168,6 @@ class WasmSymbols(
val kTypeStub = getInternalFunction("kTypeStub")
private val functionNInterfaces = (0..22).map { arity ->
getIrClass(FqName("kotlin.wasm.internal.Function$arity"))
}
val functionNInvokeMethods by lazy {
functionNInterfaces.map { interfaceSymbol ->
interfaceSymbol.owner.declarations.filterIsInstance<IrSimpleFunction>().single { method ->
method.name == OperatorNameConventions.INVOKE
}.symbol
}
}
val arraysCopyInto = findFunctions(collectionsPackage.memberScope, Name.identifier("copyInto"))
.map { symbolTable.referenceSimpleFunction(it) }
@@ -208,9 +196,6 @@ class WasmSymbols(
}
}
override fun functionN(n: Int): IrClassSymbol =
functionNInterfaces[n]
private fun findClass(memberScope: MemberScope, name: Name): ClassDescriptor =
memberScope.getContributedClassifier(name, NoLookupLocation.FROM_BACKEND) as ClassDescriptor
@@ -32,7 +32,12 @@ fun compileWasm(
): WasmCompilerResult {
val mainModule = depsDescriptors.mainModule
val configuration = depsDescriptors.compilerConfiguration
val (moduleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer) = loadIr(depsDescriptors, irFactory, verifySignatures = false)
val (moduleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer) = loadIr(
depsDescriptors,
irFactory,
verifySignatures = false,
loadFunctionInterfacesIntoStdlib = true,
)
val allModules = when (mainModule) {
is MainModule.SourceFiles -> dependencyModules + listOf(moduleFragment)
@@ -180,12 +180,6 @@ class WasmModuleCodegenContextImpl(
override fun referenceInterfaceId(irInterface: IrClassSymbol): WasmSymbol<Int> {
// HACK to substitute kotlin.Function5 with kotlin.wasm.internal.Function5
val defaultType = irInterface.defaultType
if (defaultType.isFunction()) {
val n = irInterface.owner.typeParameters.size - 1
return wasmFragment.interfaceId.reference(backendContext.wasmSymbols.functionN(n))
}
return wasmFragment.interfaceId.reference(irInterface)
}
@@ -123,24 +123,9 @@ class BuiltInsLowering(val context: WasmBackendContext) : FileLoweringPass {
}
}
val nativeInvokeArity = getKotlinFunctionInvokeArity(call)
if (nativeInvokeArity != null) {
return irCall(call, symbols.functionNInvokeMethods[nativeInvokeArity])
}
return call
}
private fun getKotlinFunctionInvokeArity(call: IrCall): Int? {
val simpleFunction = call.symbol.owner as? IrSimpleFunction ?: return null
val receiverType = simpleFunction.dispatchReceiverParameter?.type ?: return null
if (simpleFunction.isSuspend) return null
if (simpleFunction.name == OperatorNameConventions.INVOKE && receiverType.isFunction()) {
return simpleFunction.valueParameters.size
}
return null
}
override fun lower(irFile: IrFile) {
val builder = context.createIrBuilder(irFile.symbol)
irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
@@ -1,54 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.typeWithArguments
import org.jetbrains.kotlin.ir.util.isFunction
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
/**
* Replace kotlin.Function{N} etc super types with kotlin.wasm.internal.Function{N} super types.
* This is a workaround for having a concrete IR for these interfaces.
*/
class WasmFunctionInterfaceReplacer(val context: WasmBackendContext) : FileLoweringPass {
private fun replaceType(type: IrType): IrType {
if (type.isFunction()) {
require(type is IrSimpleType)
val klass: IrClass = type.classifier.owner as IrClass
// No need to replace just "kotlin.Function"
if (klass.name.identifier == "Function") {
return type
}
val arity = klass.typeParameters.size - 1
return context.wasmSymbols.functionN(arity).typeWithArguments(type.arguments)
}
return type
}
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
override fun visitClassNew(declaration: IrClass): IrStatement {
if (declaration.superTypes.any { it.isFunction() }) {
declaration.superTypes = declaration.superTypes.map { replaceType(it) }
}
return super.visitClassNew(declaration)
}
})
}
}
@@ -83,8 +83,12 @@ abstract class IrAbstractDescriptorBasedFunctionFactory {
@OptIn(ObsoleteDescriptorBasedAPI::class)
class IrDescriptorBasedFunctionFactory(
private val irBuiltIns: IrBuiltInsOverDescriptors, private val symbolTable: SymbolTable
private val irBuiltIns: IrBuiltInsOverDescriptors,
private val symbolTable: SymbolTable,
getPackageFragment: ((PackageFragmentDescriptor) -> IrPackageFragment)? = null
) : IrAbstractDescriptorBasedFunctionFactory() {
val getPackageFragment =
getPackageFragment ?: symbolTable::declareExternalPackageFragmentIfNotExists
// TODO: Lazieness
@@ -230,18 +234,18 @@ class IrDescriptorBasedFunctionFactory(
private val kotlinPackageFragment: IrPackageFragment by lazy {
irBuiltIns.builtIns.getFunction(0).let {
symbolTable.declareExternalPackageFragmentIfNotExists(it.containingDeclaration as PackageFragmentDescriptor)
getPackageFragment(it.containingDeclaration as PackageFragmentDescriptor)
}
}
private val kotlinCoroutinesPackageFragment: IrPackageFragment by lazy {
irBuiltIns.builtIns.getSuspendFunction(0).let {
symbolTable.declareExternalPackageFragmentIfNotExists(it.containingDeclaration as PackageFragmentDescriptor)
getPackageFragment(it.containingDeclaration as PackageFragmentDescriptor)
}
}
private val kotlinReflectPackageFragment: IrPackageFragment by lazy {
irBuiltIns.kPropertyClass.descriptor.let {
symbolTable.declareExternalPackageFragmentIfNotExists(it.containingDeclaration as PackageFragmentDescriptor)
getPackageFragment(it.containingDeclaration as PackageFragmentDescriptor)
}
}
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.ir.IrBuiltIns
@@ -38,13 +39,12 @@ import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerIr
import org.jetbrains.kotlin.ir.declarations.IrFactory
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltInsOverDescriptors
import org.jetbrains.kotlin.ir.descriptors.IrDescriptorBasedFunctionFactory
import org.jetbrains.kotlin.ir.linkage.IrDeserializer
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.util.noUnboundLeft
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult
import org.jetbrains.kotlin.js.config.ErrorTolerancePolicy
@@ -256,7 +256,8 @@ fun loadIr(
depsDescriptors: ModulesStructure,
irFactory: IrFactory,
verifySignatures: Boolean,
filesToLoad: Set<String>? = null
filesToLoad: Set<String>? = null,
loadFunctionInterfacesIntoStdlib: Boolean = false,
): IrModuleInfo {
val project = depsDescriptors.project
val mainModule = depsDescriptors.mainModule
@@ -268,11 +269,26 @@ fun loadIr(
val signaturer = IdSignatureDescriptor(JsManglerDesc)
val symbolTable = SymbolTable(signaturer, irFactory)
lateinit var stdlibModule: IrModuleFragment
val createFunctionFactoryCallback =
if (loadFunctionInterfacesIntoStdlib) {
{ packageFragmentDescriptor: PackageFragmentDescriptor ->
IrFileImpl(NaiveSourceBasedFileEntryImpl("[K][Suspend]Functions"), packageFragmentDescriptor, stdlibModule)
.also { stdlibModule.files += it }
}
} else {
null
}
when (mainModule) {
is MainModule.SourceFiles -> {
assert(filesToLoad == null)
val (psi2IrContext, _) = preparePsi2Ir(depsDescriptors, errorPolicy, symbolTable)
val irBuiltIns = psi2IrContext.irBuiltIns
(irBuiltIns as IrBuiltInsOverDescriptors).functionFactory =
IrDescriptorBasedFunctionFactory(irBuiltIns, symbolTable, createFunctionFactoryCallback)
val feContext = psi2IrContext.run {
JsIrLinker.JsFePluginContext(moduleDescriptor, symbolTable, typeTranslator, irBuiltIns)
}
@@ -301,6 +317,7 @@ fun loadIr(
}
}
}
stdlibModule = deserializedModuleFragments.first()
val moduleFragment = psi2IrContext.generateModuleFragmentWithPlugins(project, mainModule.files, irLinker, messageLogger)
symbolTable.noUnboundLeft("Unbound symbols left after linker")
@@ -332,6 +349,7 @@ fun loadIr(
val typeTranslator =
TypeTranslatorImpl(symbolTable, depsDescriptors.compilerConfiguration.languageVersionSettings, moduleDescriptor)
val irBuiltIns = IrBuiltInsOverDescriptors(moduleDescriptor.builtIns, typeTranslator, symbolTable)
irBuiltIns.functionFactory = IrDescriptorBasedFunctionFactory(irBuiltIns, symbolTable, createFunctionFactoryCallback)
val loweredIcData = if (!depsDescriptors.icUseStdlibCache && !depsDescriptors.icUseStdlibCache) emptyMap() else {
val result = mutableMapOf<ModuleDescriptor, SerializedIcData>()
@@ -390,6 +408,7 @@ fun loadIr(
}
}
}
stdlibModule = deserializedModuleFragments.first()
val moduleFragment = deserializedModuleFragments.last()
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: BIG_ARITY
// !LANGUAGE: +NewInference
fun foo(
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: FUNCTION_REFERENCES
// WITH_RUNTIME
// IGNORE_BACKEND: JS
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: BIG_ARITY
// IGNORE_BACKEND: NATIVE
var result = "Fail"
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: BIG_ARITY
// !LANGUAGE: +FunctionTypesWithBigArity
class A(val value: Int) {
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: BIG_ARITY
// !LANGUAGE: +FunctionTypesWithBigArity
// IGNORE_BACKEND: JS_IR, JS
// IGNORE_BACKEND: JS_IR_ES6
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: BIG_ARITY
// !LANGUAGE: +FunctionTypesWithBigArity
class A
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: BIG_ARITY
// !LANGUAGE: +FunctionTypesWithBigArity
class A(val value: Int)
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: BIG_ARITY
// !LANGUAGE: +FunctionTypesWithBigArity
class A {
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: BIG_ARITY
interface A
object O : A
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: WASM
inline class Z(val value: Long)