[WASM] Initial infrastructure
- New module ":compiler:backend.wasm"
- Initial compiler infra (driver, phaser, context)
- Subset of Wasm AST
- Skeleton of IR -> Wasm AST
- Wasm AST -> WAT transformer
- Testing infra
- SpiderMonkey jsshell tool
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile(project(":compiler:util"))
|
||||
compile(project(":compiler:frontend"))
|
||||
compile(project(":compiler:backend-common"))
|
||||
compile(project(":compiler:ir.tree"))
|
||||
compile(project(":compiler:ir.psi2ir"))
|
||||
compile(project(":compiler:ir.backend.common"))
|
||||
compile(project(":compiler:ir.serialization.common"))
|
||||
compile(project(":compiler:ir.serialization.js"))
|
||||
compile(project(":js:js.ast"))
|
||||
compile(project(":js:js.frontend"))
|
||||
compile(project(":compiler:backend.js"))
|
||||
|
||||
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.ir.Ir
|
||||
import org.jetbrains.kotlin.backend.common.ir.Symbols
|
||||
import org.jetbrains.kotlin.backend.js.JsDeclarationFactory
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsSharedVariablesManager
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrExternalPackageFragmentSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrExternalPackageFragmentSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
class WasmBackendContext(
|
||||
val module: ModuleDescriptor,
|
||||
override val irBuiltIns: IrBuiltIns,
|
||||
symbolTable: SymbolTable,
|
||||
irModuleFragment: IrModuleFragment,
|
||||
val additionalExportedDeclarations: Set<FqName>,
|
||||
override val configuration: CompilerConfiguration
|
||||
) : CommonBackendContext {
|
||||
override val builtIns = module.builtIns
|
||||
override var inVerbosePhase: Boolean = false
|
||||
|
||||
// Place to store declarations excluded from code generation
|
||||
val excludedDeclarations: IrPackageFragment by lazy {
|
||||
IrExternalPackageFragmentImpl(
|
||||
DescriptorlessExternalPackageFragmentSymbol(),
|
||||
FqName("kotlin")
|
||||
)
|
||||
}
|
||||
|
||||
override val declarationFactory = JsDeclarationFactory()
|
||||
|
||||
val objectToGetInstanceFunction = mutableMapOf<IrClassSymbol, IrSimpleFunction>()
|
||||
override val internalPackageFqn = FqName("kotlin.wasm")
|
||||
|
||||
private val internalPackageFragment = IrExternalPackageFragmentImpl(
|
||||
IrExternalPackageFragmentSymbolImpl(
|
||||
EmptyPackageFragmentDescriptor(builtIns.builtInsModule, FqName("kotlin.wasm.internal"))
|
||||
)
|
||||
)
|
||||
|
||||
override val sharedVariablesManager = JsSharedVariablesManager(irBuiltIns, internalPackageFragment)
|
||||
|
||||
val wasmSymbols: WasmSymbols = WasmSymbols(this@WasmBackendContext, symbolTable)
|
||||
override val ir = object : Ir<WasmBackendContext>(this, irModuleFragment) {
|
||||
override val symbols: Symbols<WasmBackendContext> = wasmSymbols
|
||||
override fun shouldGenerateHandlerParameterForDefaultBodyFun() = true
|
||||
}
|
||||
|
||||
override fun log(message: () -> String) {
|
||||
/*TODO*/
|
||||
if (inVerbosePhase) print(message())
|
||||
}
|
||||
|
||||
override fun report(element: IrElement?, irFile: IrFile?, message: String, isError: Boolean) {
|
||||
/*TODO*/
|
||||
print(message)
|
||||
}
|
||||
}
|
||||
|
||||
class DescriptorlessExternalPackageFragmentSymbol : IrExternalPackageFragmentSymbol {
|
||||
override val descriptor: PackageFragmentDescriptor
|
||||
get() = error("Operation is unsupported")
|
||||
|
||||
private var _owner: IrExternalPackageFragment? = null
|
||||
override val owner get() = _owner!!
|
||||
|
||||
override val isBound get() = _owner != null
|
||||
|
||||
override fun bind(owner: IrExternalPackageFragment) {
|
||||
_owner = owner
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.*
|
||||
import org.jetbrains.kotlin.backend.common.lower.*
|
||||
import org.jetbrains.kotlin.backend.common.lower.inline.FunctionInlining
|
||||
import org.jetbrains.kotlin.backend.common.phaser.*
|
||||
import org.jetbrains.kotlin.backend.wasm.lower.BuiltInsLowering
|
||||
import org.jetbrains.kotlin.backend.wasm.lower.WasmBlockDecomposerLowering
|
||||
import org.jetbrains.kotlin.backend.wasm.lower.excludeDeclarationsFromCodegen
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.inline.RemoveInlineFunctionsWithReifiedTypeParametersLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.inline.ReturnableBlockLowering
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
|
||||
|
||||
private fun ClassLoweringPass.runOnFilesPostfix(moduleFragment: IrModuleFragment) = moduleFragment.files.forEach { runOnFilePostfix(it) }
|
||||
|
||||
private fun validationCallback(context: WasmBackendContext, module: IrModuleFragment) {
|
||||
val validatorConfig = IrValidatorConfig(
|
||||
abortOnError = true,
|
||||
ensureAllNodesAreDifferent = true,
|
||||
checkTypes = false,
|
||||
checkDescriptors = false
|
||||
)
|
||||
module.accept(IrValidator(context, validatorConfig), null)
|
||||
module.accept(CheckDeclarationParentsVisitor, null)
|
||||
}
|
||||
|
||||
val validationAction = makeVerifyAction(::validationCallback)
|
||||
|
||||
private fun makeWasmModulePhase(
|
||||
lowering: (WasmBackendContext) -> FileLoweringPass,
|
||||
name: String,
|
||||
description: String,
|
||||
prerequisite: Set<AnyNamedPhase> = emptySet()
|
||||
) = makeIrModulePhase<WasmBackendContext>(lowering, name, description, prerequisite, actions = setOf(validationAction, defaultDumper))
|
||||
|
||||
private fun makeCustomWasmModulePhase(
|
||||
op: (WasmBackendContext, IrModuleFragment) -> Unit,
|
||||
description: String,
|
||||
name: String,
|
||||
prerequisite: Set<AnyNamedPhase> = emptySet()
|
||||
) = namedIrModulePhase(
|
||||
name,
|
||||
description,
|
||||
prerequisite,
|
||||
actions = setOf(defaultDumper, validationAction),
|
||||
nlevels = 0,
|
||||
lower = object : SameTypeCompilerPhase<WasmBackendContext, IrModuleFragment> {
|
||||
override fun invoke(
|
||||
phaseConfig: PhaseConfig,
|
||||
phaserState: PhaserState<IrModuleFragment>,
|
||||
context: WasmBackendContext,
|
||||
input: IrModuleFragment
|
||||
): IrModuleFragment {
|
||||
op(context, input)
|
||||
return input
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
private val validateIrBeforeLowering = makeCustomWasmModulePhase(
|
||||
{ context, module -> validationCallback(context, module) },
|
||||
name = "ValidateIrBeforeLowering",
|
||||
description = "Validate IR before lowering"
|
||||
)
|
||||
|
||||
private val validateIrAfterLowering = makeCustomWasmModulePhase(
|
||||
{ context, module -> validationCallback(context, module) },
|
||||
name = "ValidateIrAfterLowering",
|
||||
description = "Validate IR after lowering"
|
||||
)
|
||||
|
||||
private val expectDeclarationsRemovingPhase = makeWasmModulePhase(
|
||||
::ExpectDeclarationsRemoveLowering,
|
||||
name = "ExpectDeclarationsRemoving",
|
||||
description = "Remove expect declaration from module fragment"
|
||||
)
|
||||
|
||||
private val lateinitLoweringPhase = makeWasmModulePhase(
|
||||
::LateinitLowering,
|
||||
name = "LateinitLowering",
|
||||
description = "Insert checks for lateinit field references"
|
||||
)
|
||||
|
||||
// TODO make all lambda-related stuff work with IrFunctionExpression and drop this phase
|
||||
private val provisionalFunctionExpressionPhase = makeWasmModulePhase(
|
||||
{ ProvisionalFunctionExpressionLowering() },
|
||||
name = "FunctionExpression",
|
||||
description = "Transform IrFunctionExpression to a local function reference"
|
||||
)
|
||||
|
||||
private val arrayConstructorPhase = makeWasmModulePhase(
|
||||
::ArrayConstructorLowering,
|
||||
name = "ArrayConstructor",
|
||||
description = "Transform `Array(size) { index -> value }` into a loop"
|
||||
)
|
||||
|
||||
private val functionInliningPhase = makeCustomWasmModulePhase(
|
||||
{ context, module ->
|
||||
FunctionInlining(context).inline(module)
|
||||
module.patchDeclarationParents()
|
||||
},
|
||||
name = "FunctionInliningPhase",
|
||||
description = "Perform function inlining",
|
||||
prerequisite = setOf(expectDeclarationsRemovingPhase)
|
||||
)
|
||||
|
||||
private val removeInlineFunctionsWithReifiedTypeParametersLoweringPhase = makeWasmModulePhase(
|
||||
{ RemoveInlineFunctionsWithReifiedTypeParametersLowering() },
|
||||
name = "RemoveInlineFunctionsWithReifiedTypeParametersLowering",
|
||||
description = "Remove Inline functions with reified parameters from context",
|
||||
prerequisite = setOf(functionInliningPhase)
|
||||
)
|
||||
|
||||
private val tailrecLoweringPhase = makeWasmModulePhase(
|
||||
::TailrecLowering,
|
||||
name = "TailrecLowering",
|
||||
description = "Replace `tailrec` callsites with equivalent loop"
|
||||
)
|
||||
|
||||
private val enumClassConstructorLoweringPhase = makeWasmModulePhase(
|
||||
::EnumClassConstructorLowering,
|
||||
name = "EnumClassConstructorLowering",
|
||||
description = "Transform Enum Class into regular Class"
|
||||
)
|
||||
|
||||
|
||||
private val sharedVariablesLoweringPhase = makeWasmModulePhase(
|
||||
::SharedVariablesLowering,
|
||||
name = "SharedVariablesLowering",
|
||||
description = "Box captured mutable variables"
|
||||
)
|
||||
|
||||
private val localDelegatedPropertiesLoweringPhase = makeWasmModulePhase(
|
||||
{ LocalDelegatedPropertiesLowering() },
|
||||
name = "LocalDelegatedPropertiesLowering",
|
||||
description = "Transform Local Delegated properties"
|
||||
)
|
||||
|
||||
private val localDeclarationsLoweringPhase = makeWasmModulePhase(
|
||||
::LocalDeclarationsLowering,
|
||||
name = "LocalDeclarationsLowering",
|
||||
description = "Move local declarations into nearest declaration container",
|
||||
prerequisite = setOf(sharedVariablesLoweringPhase, localDelegatedPropertiesLoweringPhase)
|
||||
)
|
||||
|
||||
private val localClassExtractionPhase = makeWasmModulePhase(
|
||||
::LocalClassPopupLowering,
|
||||
name = "LocalClassExtractionPhase",
|
||||
description = "Move local declarations into nearest declaration container",
|
||||
prerequisite = setOf(localDeclarationsLoweringPhase)
|
||||
)
|
||||
|
||||
private val innerClassesLoweringPhase = makeWasmModulePhase(
|
||||
::InnerClassesLowering,
|
||||
name = "InnerClassesLowering",
|
||||
description = "Capture outer this reference to inner class"
|
||||
)
|
||||
|
||||
private val innerClassConstructorCallsLoweringPhase = makeWasmModulePhase(
|
||||
::InnerClassConstructorCallsLowering,
|
||||
name = "InnerClassConstructorCallsLowering",
|
||||
description = "Replace inner class constructor invocation"
|
||||
)
|
||||
|
||||
private val defaultArgumentStubGeneratorPhase = makeWasmModulePhase(
|
||||
::DefaultArgumentStubGenerator,
|
||||
name = "DefaultArgumentStubGenerator",
|
||||
description = "Generate synthetic stubs for functions with default parameter values"
|
||||
)
|
||||
|
||||
private val defaultParameterInjectorPhase = makeWasmModulePhase(
|
||||
{ context -> DefaultParameterInjector(context, skipExternalMethods = true) },
|
||||
name = "DefaultParameterInjector",
|
||||
description = "Replace callsite with default parameters with corresponding stub function",
|
||||
prerequisite = setOf(innerClassesLoweringPhase)
|
||||
)
|
||||
|
||||
private val defaultParameterCleanerPhase = makeWasmModulePhase(
|
||||
::DefaultParameterCleaner,
|
||||
name = "DefaultParameterCleaner",
|
||||
description = "Clean default parameters up"
|
||||
)
|
||||
|
||||
//private val jsDefaultCallbackGeneratorPhase = makeJsModulePhase(
|
||||
// ::JsDefaultCallbackGenerator,
|
||||
// name = "JsDefaultCallbackGenerator",
|
||||
// description = "Build binding for super calls with default parameters"
|
||||
//)
|
||||
|
||||
//private val varargLoweringPhase = makeJsModulePhase(
|
||||
// ::VarargLowering,
|
||||
// name = "VarargLowering",
|
||||
// description = "Lower vararg arguments"
|
||||
//)
|
||||
|
||||
private val propertiesLoweringPhase = makeWasmModulePhase(
|
||||
{ context -> PropertiesLowering(context, skipExternalProperties = true, generateAnnotationFields = true) },
|
||||
name = "PropertiesLowering",
|
||||
description = "Move fields and accessors out from its property"
|
||||
)
|
||||
|
||||
private val primaryConstructorLoweringPhase = makeWasmModulePhase(
|
||||
::PrimaryConstructorLowering,
|
||||
name = "PrimaryConstructorLowering",
|
||||
description = "Creates primary constructor if it doesn't exist"
|
||||
)
|
||||
|
||||
private val initializersLoweringPhase = makeCustomWasmModulePhase(
|
||||
{ context, module -> InitializersLowering(context, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, false).lower(module) },
|
||||
name = "InitializersLowering",
|
||||
description = "Merge init block and field initializers into [primary] constructor",
|
||||
prerequisite = setOf(primaryConstructorLoweringPhase)
|
||||
)
|
||||
|
||||
private val excludeDeclarationsFromCodegenPhase = makeCustomWasmModulePhase(
|
||||
{ context, module ->
|
||||
excludeDeclarationsFromCodegen(context, module)
|
||||
},
|
||||
name = "ExcludeDeclarationsFromCodegen",
|
||||
description = "Move excluded declarations to separate place"
|
||||
)
|
||||
|
||||
private val returnableBlockLoweringPhase = makeWasmModulePhase(
|
||||
::ReturnableBlockLowering,
|
||||
name = "ReturnableBlockLowering",
|
||||
description = "Replace returnable block with do-while loop",
|
||||
prerequisite = setOf(functionInliningPhase)
|
||||
)
|
||||
|
||||
private val bridgesConstructionPhase = makeWasmModulePhase(
|
||||
::BridgesConstruction,
|
||||
name = "BridgesConstruction",
|
||||
description = "Generate bridges"
|
||||
)
|
||||
|
||||
private val inlineClassLoweringPhase = makeCustomWasmModulePhase(
|
||||
{ context, module ->
|
||||
InlineClassLowering(context).run {
|
||||
inlineClassDeclarationLowering.runOnFilesPostfix(module)
|
||||
inlineClassUsageLowering.lower(module)
|
||||
}
|
||||
},
|
||||
name = "InlineClassLowering",
|
||||
description = "Handle inline classes"
|
||||
)
|
||||
|
||||
//private val autoboxingTransformerPhase = makeJsModulePhase(
|
||||
// ::AutoboxingTransformer,
|
||||
// name = "AutoboxingTransformer",
|
||||
// description = "Insert box/unbox intrinsics"
|
||||
//)
|
||||
|
||||
private val blockDecomposerLoweringPhase = makeCustomWasmModulePhase(
|
||||
{ context, module ->
|
||||
WasmBlockDecomposerLowering(context).lower(module)
|
||||
module.patchDeclarationParents()
|
||||
},
|
||||
name = "BlockDecomposerLowering",
|
||||
description = "Transform statement-like-expression nodes into pure-statement to make it easily transform into JS"
|
||||
)
|
||||
|
||||
//private val classReferenceLoweringPhase = makeJsModulePhase(
|
||||
// ::ClassReferenceLowering,
|
||||
// name = "ClassReferenceLowering",
|
||||
// description = "Handle class references"
|
||||
//)
|
||||
//
|
||||
//private val primitiveCompanionLoweringPhase = makeJsModulePhase(
|
||||
// ::PrimitiveCompanionLowering,
|
||||
// name = "PrimitiveCompanionLowering",
|
||||
// description = "Replace common companion object access with platform one"
|
||||
//)
|
||||
//
|
||||
//private val constLoweringPhase = makeJsModulePhase(
|
||||
// ::ConstLowering,
|
||||
// name = "ConstLowering",
|
||||
// description = "Wrap Long and Char constants into constructor invocation"
|
||||
//)
|
||||
//
|
||||
//private val callsLoweringPhase = makeJsModulePhase(
|
||||
// ::CallsLowering,
|
||||
// name = "CallsLowering",
|
||||
// description = "Handle intrinsics"
|
||||
//)
|
||||
//
|
||||
//private val testGenerationPhase = makeJsModulePhase(
|
||||
// ::TestGenerator,
|
||||
// name = "TestGenerationLowering",
|
||||
// description = "Generate invocations to kotlin.test suite and test functions"
|
||||
//)
|
||||
//
|
||||
private val staticMembersLoweringPhase = makeWasmModulePhase(
|
||||
::StaticMembersLowering,
|
||||
name = "StaticMembersLowering",
|
||||
description = "Move static member declarations to top-level"
|
||||
)
|
||||
|
||||
private val builtInsLoweringPhase = makeWasmModulePhase(
|
||||
::BuiltInsLowering,
|
||||
name = "BuiltInsLowering",
|
||||
description = "Lower IR buildins"
|
||||
)
|
||||
|
||||
private val objectDeclarationLoweringPhase = makeCustomWasmModulePhase(
|
||||
{ context, module -> ObjectUsageLowering(context, context.objectToGetInstanceFunction).lower(module) },
|
||||
name = "ObjectDeclarationLowering",
|
||||
description = "Create lazy object instance generator functions"
|
||||
)
|
||||
|
||||
private val objectUsageLoweringPhase = makeCustomWasmModulePhase(
|
||||
{ context, module -> ObjectUsageLowering(context, context.objectToGetInstanceFunction).lower(module) },
|
||||
name = "ObjectUsageLowering",
|
||||
description = "Transform IrGetObjectValue into instance generator call"
|
||||
)
|
||||
|
||||
val wasmPhases = namedIrModulePhase<WasmBackendContext>(
|
||||
name = "IrModuleLowering",
|
||||
description = "IR module lowering",
|
||||
lower = validateIrBeforeLowering then
|
||||
excludeDeclarationsFromCodegenPhase then
|
||||
expectDeclarationsRemovingPhase then
|
||||
provisionalFunctionExpressionPhase then
|
||||
|
||||
// TODO: Need some helpers from stdlib
|
||||
// arrayConstructorPhase then
|
||||
|
||||
functionInliningPhase then
|
||||
lateinitLoweringPhase then
|
||||
tailrecLoweringPhase then
|
||||
|
||||
enumClassConstructorLoweringPhase then
|
||||
|
||||
sharedVariablesLoweringPhase then
|
||||
localDelegatedPropertiesLoweringPhase then
|
||||
localDeclarationsLoweringPhase then
|
||||
localClassExtractionPhase then
|
||||
innerClassesLoweringPhase then
|
||||
innerClassConstructorCallsLoweringPhase then
|
||||
propertiesLoweringPhase then
|
||||
primaryConstructorLoweringPhase then
|
||||
initializersLoweringPhase then
|
||||
// Common prefix ends
|
||||
|
||||
builtInsLoweringPhase then
|
||||
|
||||
// TODO: Commonize enumEntryToGetInstanceFunction
|
||||
// Commonize array literal creation
|
||||
// Extract external enum lowering to JS part
|
||||
//
|
||||
// enumClassLoweringPhase then
|
||||
// enumUsageLoweringPhase then
|
||||
|
||||
|
||||
// TODO: Requires stdlib
|
||||
// suspendFunctionsLoweringPhase then
|
||||
|
||||
returnableBlockLoweringPhase then
|
||||
|
||||
// TODO: Callable reference lowering is too JS specific.
|
||||
// Should we reuse JVM or Native lowering?
|
||||
// callableReferenceLoweringPhase then
|
||||
|
||||
defaultArgumentStubGeneratorPhase then
|
||||
defaultParameterInjectorPhase then
|
||||
defaultParameterCleanerPhase then
|
||||
|
||||
// TODO: Investigate
|
||||
// jsDefaultCallbackGeneratorPhase then
|
||||
|
||||
removeInlineFunctionsWithReifiedTypeParametersLoweringPhase then
|
||||
|
||||
|
||||
// TODO: Varargs are too platform-specific. Reimplement.
|
||||
// varargLoweringPhase then
|
||||
|
||||
// TODO: Investigate exception proposal
|
||||
// multipleCatchesLoweringPhase then
|
||||
|
||||
bridgesConstructionPhase then
|
||||
|
||||
// TODO: Reimplement
|
||||
// typeOperatorLoweringPhase then
|
||||
|
||||
// TODO: Reimplement
|
||||
// secondaryConstructorLoweringPhase then
|
||||
// secondaryFactoryInjectorLoweringPhase then
|
||||
|
||||
// TODO: Reimplement
|
||||
// classReferenceLoweringPhase then
|
||||
|
||||
inlineClassLoweringPhase then
|
||||
|
||||
// TODO: Commonize box/unbox intrinsics
|
||||
// autoboxingTransformerPhase then
|
||||
|
||||
blockDecomposerLoweringPhase then
|
||||
|
||||
// TODO: Reimplement
|
||||
// constLoweringPhase then
|
||||
|
||||
objectDeclarationLoweringPhase then
|
||||
objectUsageLoweringPhase then
|
||||
staticMembersLoweringPhase then
|
||||
|
||||
validateIrAfterLowering
|
||||
)
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.Symbols
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.util.ReferenceSymbolTable
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.types.SimpleType
|
||||
|
||||
class WasmSymbols(
|
||||
context: WasmBackendContext,
|
||||
private val symbolTable: ReferenceSymbolTable
|
||||
) : Symbols<WasmBackendContext>(context, symbolTable) {
|
||||
|
||||
override val ThrowNullPointerException
|
||||
get() = TODO()
|
||||
override val ThrowNoWhenBranchMatchedException
|
||||
get() = TODO()
|
||||
override val ThrowTypeCastException
|
||||
get() = TODO()
|
||||
override val ThrowUninitializedPropertyAccessException
|
||||
get() = TODO()
|
||||
override val defaultConstructorMarker
|
||||
get() = TODO()
|
||||
override val stringBuilder
|
||||
get() = TODO()
|
||||
override val copyRangeTo: Map<ClassDescriptor, IrSimpleFunctionSymbol>
|
||||
get() = TODO()
|
||||
override val coroutineImpl
|
||||
get() = TODO()
|
||||
override val coroutineSuspendedGetter
|
||||
get() = TODO()
|
||||
override val getContinuation
|
||||
get() = TODO()
|
||||
override val coroutineContextGetter by lazy {
|
||||
context.excludedDeclarations.addFunction {
|
||||
name = Name.identifier("coroutineContextGetter\$Stub")
|
||||
}.symbol
|
||||
}
|
||||
|
||||
override val suspendCoroutineUninterceptedOrReturn
|
||||
get() = TODO()
|
||||
override val coroutineGetContext
|
||||
get() = TODO()
|
||||
override val returnIfSuspended
|
||||
get() = TODO()
|
||||
|
||||
private val wasmInternalPackage = context.module.getPackage(FqName("kotlin.wasm.internal"))
|
||||
|
||||
val equalityFunctions = mapOf(
|
||||
context.irBuiltIns.booleanType to getInternalFunction("wasm_i32_eq"),
|
||||
context.irBuiltIns.byteType to getInternalFunction("wasm_i32_eq"),
|
||||
context.irBuiltIns.shortType to getInternalFunction("wasm_i32_eq"),
|
||||
context.irBuiltIns.charType to getInternalFunction("wasm_i32_eq"),
|
||||
context.irBuiltIns.intType to getInternalFunction("wasm_i32_eq"),
|
||||
context.irBuiltIns.longType to getInternalFunction("wasm_i64_eq"),
|
||||
context.irBuiltIns.floatType to getInternalFunction("wasm_f32_eq"),
|
||||
context.irBuiltIns.doubleType to getInternalFunction("wasm_f64_eq")
|
||||
)
|
||||
|
||||
private fun wasmString(simpleType: SimpleType): String = with(context.irBuiltIns) {
|
||||
when (simpleType) {
|
||||
bool, byte, short, char, int -> "i32"
|
||||
float -> "f32"
|
||||
double -> "f64"
|
||||
long -> "i64"
|
||||
else -> error("Unkonow primitive type")
|
||||
}
|
||||
}
|
||||
|
||||
val irBuiltInsToWasmIntrinsics = context.irBuiltIns.run {
|
||||
mapOf(
|
||||
lessFunByOperandType to "lt",
|
||||
lessOrEqualFunByOperandType to "le",
|
||||
greaterOrEqualFunByOperandType to "ge",
|
||||
greaterFunByOperandType to "gt"
|
||||
).map { (typeToBuiltIn, wasmOp) ->
|
||||
typeToBuiltIn.map { (type, builtin) ->
|
||||
val wasmType = wasmString(type)
|
||||
val markSign = if (wasmType == "i32" || wasmType == "i64") "_s" else ""
|
||||
builtin to getInternalFunction("wasm_${wasmType}_$wasmOp$markSign")
|
||||
}
|
||||
}.flatten().toMap()
|
||||
}
|
||||
|
||||
val stringGetLiteral = getInternalFunction("stringLiteral")
|
||||
|
||||
private fun findClass(memberScope: MemberScope, name: Name): ClassDescriptor =
|
||||
memberScope.getContributedClassifier(name, NoLookupLocation.FROM_BACKEND) as ClassDescriptor
|
||||
|
||||
private fun findFunctions(memberScope: MemberScope, name: Name): List<SimpleFunctionDescriptor> =
|
||||
memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND).toList()
|
||||
|
||||
private fun findProperty(memberScope: MemberScope, name: Name): List<PropertyDescriptor> =
|
||||
memberScope.getContributedVariables(name, NoLookupLocation.FROM_BACKEND).toList()
|
||||
|
||||
internal fun getClass(fqName: FqName): ClassDescriptor =
|
||||
findClass(context.module.getPackage(fqName.parent()).memberScope, fqName.shortName())
|
||||
|
||||
internal fun getProperty(fqName: FqName): PropertyDescriptor =
|
||||
findProperty(context.module.getPackage(fqName.parent()).memberScope, fqName.shortName()).single()
|
||||
|
||||
internal fun getInternalFunction(name: String): IrSimpleFunctionSymbol {
|
||||
val tmp = findFunctions(wasmInternalPackage.memberScope, Name.identifier(name)).single()
|
||||
return symbolTable.referenceSimpleFunction(tmp)
|
||||
}
|
||||
|
||||
private fun getIrClass(fqName: FqName): IrClassSymbol = symbolTable.referenceClass(getClass(fqName))
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.ast
|
||||
|
||||
// TODO: Abstract out S-expression part of dumping?
|
||||
|
||||
fun WasmInstruction.toWat(ident: String = ""): String =
|
||||
"$ident($mnemonic${immediate.toWat()}${operands.joinToString("") { " " + it.toWat("") }})"
|
||||
|
||||
fun WasmImmediate.toWat(): String = when (this) {
|
||||
WasmImmediate.None -> ""
|
||||
is WasmImmediate.DeclarationReference -> " $$name"
|
||||
// SpiderMonkey jsshell won't parse Uppercase letters in literals
|
||||
is WasmImmediate.LiteralValue<*> -> " $value".toLowerCase()
|
||||
}
|
||||
|
||||
fun wasmModuleToWat(module: WasmModule): String =
|
||||
"(module\n${module.fields.joinToString("") { wasmModuleFieldToWat(it) + "\n" }})"
|
||||
|
||||
fun wasmFunctionToWat(function: WasmFunction): String {
|
||||
val watId = "$${function.name}"
|
||||
val watImport = function.importPair?.let { importPair ->
|
||||
" (import ${toWasString(importPair.module)} ${toWasString(importPair.name)})"
|
||||
} ?: ""
|
||||
val watLocals = function.locals.joinToString("") { " " + wasmLocalToWat(it) + "\n" }
|
||||
val watParameters = function.parameters.joinToString("") { " " + wasmParameterToWat(it, function.importPair == null) }
|
||||
val watResult = function.returnType?.let { type -> " (result ${type.mnemonic})" } ?: ""
|
||||
val watBody = function.instructions.joinToString("") { it.toWat(" ") + "\n" }
|
||||
return " (func $watId$watImport$watParameters$watResult\n$watLocals$watBody )"
|
||||
}
|
||||
|
||||
fun wasmParameterToWat(parameter: WasmParameter, includeName: Boolean): String {
|
||||
val name = if (includeName) " $${parameter.name}" else ""
|
||||
return "(param$name ${parameter.type.mnemonic})"
|
||||
}
|
||||
|
||||
fun wasmLocalToWat(local: WasmLocal): String =
|
||||
local.run { "(local $$name ${type.mnemonic})" }
|
||||
|
||||
fun wasmGlobalToWat(global: WasmGlobal): String {
|
||||
val watMut = if (global.isMutable) "mut " else ""
|
||||
val watInit = global.init?.toWat("") ?: ""
|
||||
return global.run { " (global $$name ($watMut${type.mnemonic}) $watInit)" }
|
||||
}
|
||||
|
||||
fun wasmExportToWat(export: WasmExport): String =
|
||||
export.run { " (export \"$exportedName\" (${kind.keyword} $$wasmName))" }
|
||||
|
||||
fun wasmModuleFieldToWat(moduleField: WasmModuleField): String =
|
||||
when (moduleField) {
|
||||
is WasmFunction -> wasmFunctionToWat(moduleField)
|
||||
is WasmGlobal -> wasmGlobalToWat(moduleField)
|
||||
is WasmExport -> wasmExportToWat(moduleField)
|
||||
is WasmModuleFieldList -> moduleField.fields.joinToString("") { wasmModuleFieldToWat(it) + "\n" }
|
||||
}
|
||||
|
||||
fun toWasString(s: String): String {
|
||||
// TODO: escape characters according to
|
||||
// https://webassembly.github.io/spec/core/text/values.html#strings
|
||||
return "\"" + s + "\""
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.ast
|
||||
|
||||
import org.jetbrains.kotlin.backend.wasm.utils.WasmImportPair
|
||||
|
||||
class WasmModule(
|
||||
val fields: List<WasmModuleField>
|
||||
)
|
||||
|
||||
sealed class WasmModuleField
|
||||
|
||||
class WasmModuleFieldList(
|
||||
val fields: List<WasmModuleField>
|
||||
) : WasmModuleField()
|
||||
|
||||
class WasmFunction(
|
||||
val name: String,
|
||||
val parameters: List<WasmParameter>,
|
||||
val returnType: WasmValueType?,
|
||||
val locals: List<WasmLocal>,
|
||||
val instructions: List<WasmInstruction>,
|
||||
val importPair: WasmImportPair?
|
||||
) : WasmModuleField()
|
||||
|
||||
class WasmParameter(
|
||||
val name: String,
|
||||
val type: WasmValueType
|
||||
)
|
||||
|
||||
class WasmLocal(
|
||||
val name: String,
|
||||
val type: WasmValueType
|
||||
)
|
||||
|
||||
class WasmGlobal(
|
||||
val name: String,
|
||||
val type: WasmValueType,
|
||||
val isMutable: Boolean,
|
||||
val init: WasmInstruction?
|
||||
) : WasmModuleField()
|
||||
|
||||
class WasmExport(
|
||||
val wasmName: String,
|
||||
val exportedName: String,
|
||||
val kind: Kind
|
||||
) : WasmModuleField() {
|
||||
enum class Kind(val keyword: String) {
|
||||
FUNCTION("func"),
|
||||
GLOBAL("global")
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.ast
|
||||
|
||||
|
||||
sealed class WasmImmediate {
|
||||
object None : WasmImmediate()
|
||||
class DeclarationReference(val name: String) : WasmImmediate()
|
||||
class LiteralValue<T : Number>(val value: T) : WasmImmediate()
|
||||
}
|
||||
|
||||
sealed class WasmInstruction(
|
||||
val mnemonic: String,
|
||||
val immediate: WasmImmediate = WasmImmediate.None,
|
||||
val operands: List<WasmInstruction> = emptyList()
|
||||
)
|
||||
|
||||
class WasmSimpleInstruction(mnemonic: String, operands: List<WasmInstruction>) :
|
||||
WasmInstruction(mnemonic, operands = operands)
|
||||
|
||||
class WasmNop : WasmInstruction("nop")
|
||||
|
||||
class WasmReturn(values: List<WasmInstruction>) :
|
||||
WasmInstruction("return", operands = values)
|
||||
|
||||
class WasmDrop(instructions: List<WasmInstruction>) :
|
||||
WasmInstruction("drop", operands = instructions)
|
||||
|
||||
class WasmCall(name: String, operands: List<WasmInstruction>) :
|
||||
WasmInstruction("call", WasmImmediate.DeclarationReference(name), operands)
|
||||
|
||||
class WasmGetLocal(name: String) :
|
||||
WasmInstruction("get_local", WasmImmediate.DeclarationReference(name))
|
||||
|
||||
class WasmGetGlobal(name: String) :
|
||||
WasmInstruction("get_global", WasmImmediate.DeclarationReference(name))
|
||||
|
||||
class WasmSetGlobal(name: String, value: WasmInstruction) :
|
||||
WasmInstruction("set_global", WasmImmediate.DeclarationReference(name), listOf(value))
|
||||
|
||||
class WasmSetLocal(name: String, value: WasmInstruction) :
|
||||
WasmInstruction("set_local", WasmImmediate.DeclarationReference(name), listOf(value))
|
||||
|
||||
class WasmIf(condition: WasmInstruction, thenInstructions: WasmThen?, elseInstruction: WasmElse?) :
|
||||
WasmInstruction("if", operands = listOfNotNull(condition, thenInstructions, elseInstruction))
|
||||
|
||||
class WasmThen(inst: WasmInstruction) :
|
||||
WasmInstruction("then", operands = listOf(inst))
|
||||
|
||||
class WasmElse(inst: WasmInstruction) :
|
||||
WasmInstruction("else", operands = listOf(inst))
|
||||
|
||||
class WasmBlock(instructions: List<WasmInstruction>) :
|
||||
WasmInstruction("block", operands = instructions)
|
||||
|
||||
sealed class WasmConst<KotlinType : Number, WasmType : WasmValueType>(value: KotlinType, type: WasmType) :
|
||||
WasmInstruction(type.mnemonic + ".const", WasmImmediate.LiteralValue<KotlinType>(value))
|
||||
|
||||
class WasmI32Const(value: Int) : WasmConst<Int, WasmI32>(value, WasmI32)
|
||||
class WasmI64Const(value: Long) : WasmConst<Long, WasmI64>(value, WasmI64)
|
||||
class WasmF32Const(value: Float) : WasmConst<Float, WasmF32>(value, WasmF32)
|
||||
class WasmF64Const(value: Double) : WasmConst<Double, WasmF64>(value, WasmF64)
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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.ast
|
||||
|
||||
sealed class WasmValueType(val mnemonic: String)
|
||||
|
||||
object WasmI32 : WasmValueType("i32")
|
||||
object WasmI64 : WasmValueType("i64")
|
||||
object WasmF32 : WasmValueType("f32")
|
||||
object WasmF64 : WasmValueType("f64")
|
||||
|
||||
object WasmAnyRef : WasmValueType("anyref")
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 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.codegen
|
||||
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.TODO
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||
|
||||
interface BaseTransformer<out R, in D> : IrElementVisitor<R, D> {
|
||||
override fun visitElement(element: IrElement, data: D): R {
|
||||
TODO(element)
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.wasm.ast.*
|
||||
import org.jetbrains.kotlin.backend.wasm.utils.getWasmImportAnnotation
|
||||
import org.jetbrains.kotlin.backend.wasm.utils.getWasmInstructionAnnotation
|
||||
import org.jetbrains.kotlin.backend.wasm.utils.hasExcludedFromCodegenAnnotation
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.util.isAnnotationClass
|
||||
import org.jetbrains.kotlin.ir.util.isFakeOverride
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
|
||||
class DeclarationTransformer : BaseTransformer<WasmModuleField?, WasmCodegenContext> {
|
||||
override fun visitSimpleFunction(declaration: IrSimpleFunction, data: WasmCodegenContext): WasmModuleField? {
|
||||
if (declaration.hasExcludedFromCodegenAnnotation())
|
||||
return null
|
||||
if (declaration.getWasmInstructionAnnotation() != null)
|
||||
return null
|
||||
if (declaration.isFakeOverride)
|
||||
return null
|
||||
// Virtual functions are not supported yet
|
||||
if (declaration.origin == IrDeclarationOrigin.BRIDGE)
|
||||
return null
|
||||
|
||||
// Collect local variables
|
||||
val localNames = wasmNameTable<IrValueDeclaration>()
|
||||
|
||||
val wasmName = data.getGlobalName(declaration)
|
||||
|
||||
val irParameters = declaration.run {
|
||||
listOfNotNull(dispatchReceiverParameter, extensionReceiverParameter) + valueParameters
|
||||
}
|
||||
|
||||
val wasmParameters = irParameters.map { parameter ->
|
||||
val name = localNames.declareFreshName(parameter, parameter.name.asString())
|
||||
WasmParameter(name, data.transformType(parameter.type))
|
||||
}
|
||||
|
||||
val wasmReturnType = when {
|
||||
declaration.returnType.isUnit() -> null
|
||||
else -> data.transformType(declaration.returnType)
|
||||
}
|
||||
|
||||
val importedName = declaration.getWasmImportAnnotation()
|
||||
if (importedName != null) {
|
||||
data.imports.add(
|
||||
WasmFunction(
|
||||
name = wasmName,
|
||||
parameters = wasmParameters,
|
||||
returnType = wasmReturnType,
|
||||
locals = emptyList(),
|
||||
instructions = emptyList(),
|
||||
importPair = importedName
|
||||
)
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
val body = declaration.body
|
||||
?: error("Function ${declaration.fqNameWhenAvailable} without a body")
|
||||
|
||||
data.localNames = localNames.names
|
||||
val locals = mutableListOf<WasmLocal>()
|
||||
body.acceptChildrenVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitVariable(declaration: IrVariable) {
|
||||
val name = localNames.declareFreshName(declaration, declaration.name.asString())
|
||||
locals += WasmLocal(name, data.transformType(declaration.type))
|
||||
super.visitVariable(declaration)
|
||||
}
|
||||
})
|
||||
|
||||
return WasmFunction(
|
||||
name = wasmName,
|
||||
parameters = wasmParameters,
|
||||
returnType = wasmReturnType,
|
||||
locals = locals,
|
||||
instructions = bodyToWasmInstructionList(body, data),
|
||||
importPair = null
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitConstructor(declaration: IrConstructor, data: WasmCodegenContext): WasmModuleField? {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass, data: WasmCodegenContext): WasmModuleField? {
|
||||
if (declaration.isAnnotationClass) return null
|
||||
if (declaration.hasExcludedFromCodegenAnnotation()) return null
|
||||
|
||||
val wasmMembers = declaration.declarations.mapNotNull { member ->
|
||||
when (member) {
|
||||
is IrSimpleFunction -> this.visitSimpleFunction(member, data)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
return WasmModuleFieldList(wasmMembers)
|
||||
}
|
||||
|
||||
override fun visitField(declaration: IrField, data: WasmCodegenContext): WasmModuleField {
|
||||
return WasmGlobal(
|
||||
name = data.getGlobalName(declaration),
|
||||
type = data.transformType(declaration.type),
|
||||
isMutable = true,
|
||||
// TODO: move non-constexpr initializers out
|
||||
init = declaration.initializer?.let {
|
||||
expressionToWasmInstruction(it.expression, data)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.isElseBranch
|
||||
import org.jetbrains.kotlin.backend.wasm.ast.*
|
||||
import org.jetbrains.kotlin.backend.wasm.utils.getWasmInstructionAnnotation
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.types.isUnit
|
||||
import org.jetbrains.kotlin.ir.util.dump
|
||||
|
||||
class ExpressionTransformer : BaseTransformer<WasmInstruction, WasmCodegenContext> {
|
||||
override fun visitVararg(expression: IrVararg, data: WasmCodegenContext): WasmInstruction {
|
||||
TODO("Support arrays")
|
||||
}
|
||||
|
||||
override fun visitExpressionBody(body: IrExpressionBody, data: WasmCodegenContext): WasmInstruction =
|
||||
body.expression.accept(this, data)
|
||||
|
||||
override fun visitFunctionReference(expression: IrFunctionReference, data: WasmCodegenContext): WasmInstruction {
|
||||
TODO("?")
|
||||
}
|
||||
|
||||
override fun <T> visitConst(expression: IrConst<T>, data: WasmCodegenContext): WasmInstruction {
|
||||
return when (val kind = expression.kind) {
|
||||
is IrConstKind.Null -> TODO()
|
||||
is IrConstKind.String -> {
|
||||
val value = kind.valueOf(expression)
|
||||
val index = data.stringLiterals.size
|
||||
data.stringLiterals.add(value)
|
||||
val funName = data.getGlobalName(data.backendContext.wasmSymbols.stringGetLiteral.owner)
|
||||
val operand = WasmI32Const(index)
|
||||
WasmCall(funName, listOf(operand))
|
||||
}
|
||||
is IrConstKind.Boolean -> WasmI32Const(if (kind.valueOf(expression)) 1 else 0)
|
||||
is IrConstKind.Byte -> WasmI32Const(kind.valueOf(expression).toInt())
|
||||
is IrConstKind.Short -> WasmI32Const(kind.valueOf(expression).toInt())
|
||||
is IrConstKind.Int -> WasmI32Const(kind.valueOf(expression))
|
||||
is IrConstKind.Long -> WasmI64Const(kind.valueOf(expression))
|
||||
is IrConstKind.Char -> WasmI32Const(kind.valueOf(expression).toInt())
|
||||
is IrConstKind.Float -> WasmF32Const(kind.valueOf(expression))
|
||||
is IrConstKind.Double -> WasmF64Const(kind.valueOf(expression))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitStringConcatenation(expression: IrStringConcatenation, data: WasmCodegenContext): WasmInstruction {
|
||||
TODO("Implement kotlin.String")
|
||||
}
|
||||
|
||||
override fun visitGetField(expression: IrGetField, data: WasmCodegenContext): WasmInstruction {
|
||||
val fieldName = data.getGlobalName(expression.symbol.owner)
|
||||
if (expression.receiver != null)
|
||||
TODO("Support member fields")
|
||||
|
||||
return WasmGetGlobal(fieldName)
|
||||
}
|
||||
|
||||
override fun visitGetValue(expression: IrGetValue, data: WasmCodegenContext): WasmInstruction =
|
||||
WasmGetLocal(data.getLocalName(expression.symbol.owner))
|
||||
|
||||
override fun visitGetObjectValue(expression: IrGetObjectValue, data: WasmCodegenContext): WasmInstruction {
|
||||
TODO("IrGetObjectValue")
|
||||
}
|
||||
|
||||
override fun visitSetField(expression: IrSetField, data: WasmCodegenContext): WasmInstruction {
|
||||
val fieldName = data.getGlobalName(expression.symbol.owner)
|
||||
if (expression.receiver != null)
|
||||
TODO("Support member fields")
|
||||
|
||||
val value = expression.value.accept(this, data)
|
||||
return WasmSetGlobal(fieldName, value)
|
||||
}
|
||||
|
||||
override fun visitSetVariable(expression: IrSetVariable, data: WasmCodegenContext): WasmInstruction {
|
||||
val fieldName = data.getLocalName(expression.symbol.owner)
|
||||
val value = expression.value.accept(this, data)
|
||||
return WasmSetLocal(fieldName, value)
|
||||
}
|
||||
|
||||
override fun visitConstructorCall(expression: IrConstructorCall, data: WasmCodegenContext): WasmInstruction {
|
||||
TODO("IrConstructorCall")
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall, data: WasmCodegenContext): WasmInstruction {
|
||||
val function = expression.symbol.owner.realOverrideTarget
|
||||
require(function is IrSimpleFunction) { "Only IrSimpleFunction could be called via IrCall" }
|
||||
val valueArgs = (0 until expression.valueArgumentsCount).mapNotNull { expression.getValueArgument(it) }
|
||||
val irArguments = listOfNotNull(expression.dispatchReceiver, expression.extensionReceiver) + valueArgs
|
||||
val wasmArguments = irArguments.map { expressionToWasmInstruction(it, data) }
|
||||
|
||||
val wasmInstruction = function.getWasmInstructionAnnotation()
|
||||
if (wasmInstruction != null) {
|
||||
if (wasmInstruction == "nop") {
|
||||
return wasmArguments.single()
|
||||
}
|
||||
return WasmSimpleInstruction(wasmInstruction, wasmArguments)
|
||||
}
|
||||
|
||||
val name = data.getGlobalName(function)
|
||||
return WasmCall(name, wasmArguments)
|
||||
}
|
||||
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: WasmCodegenContext): WasmInstruction {
|
||||
val wasmArgument = expressionToWasmInstruction(expression.argument, data)
|
||||
when (expression.operator) {
|
||||
IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> return wasmArgument
|
||||
}
|
||||
TODO("IrTypeOperatorCall:\n ${expression.dump()}")
|
||||
}
|
||||
|
||||
override fun visitGetEnumValue(expression: IrGetEnumValue, data: WasmCodegenContext): WasmInstruction {
|
||||
TODO("IrGetEnumValue")
|
||||
}
|
||||
|
||||
override fun visitBlockBody(body: IrBlockBody, data: WasmCodegenContext): WasmInstruction {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override fun visitContainerExpression(expression: IrContainerExpression, data: WasmCodegenContext): WasmInstruction {
|
||||
val expressions = expression.statements.map { it.accept(this, data) }
|
||||
|
||||
if (!expression.type.isUnit())
|
||||
return WasmBlock(expressions + listOf(WasmDrop(emptyList())))
|
||||
|
||||
return WasmBlock(expressions)
|
||||
}
|
||||
|
||||
override fun visitExpression(expression: IrExpression, data: WasmCodegenContext): WasmInstruction {
|
||||
return expressionToWasmInstruction(expression, data)
|
||||
}
|
||||
|
||||
override fun visitBreak(jump: IrBreak, data: WasmCodegenContext): WasmInstruction {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override fun visitContinue(jump: IrContinue, data: WasmCodegenContext): WasmInstruction {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override fun visitReturn(expression: IrReturn, data: WasmCodegenContext): WasmInstruction {
|
||||
if (expression.value.type.isUnit()) return WasmReturn(emptyList())
|
||||
|
||||
return WasmReturn(listOf(expressionToWasmInstruction(expression.value, data)))
|
||||
}
|
||||
|
||||
override fun visitThrow(expression: IrThrow, data: WasmCodegenContext): WasmInstruction {
|
||||
TODO("IrThrow")
|
||||
}
|
||||
|
||||
override fun visitVariable(declaration: IrVariable, data: WasmCodegenContext): WasmInstruction {
|
||||
val init = declaration.initializer ?: return WasmNop()
|
||||
val varName = data.getLocalName(declaration)
|
||||
return WasmSetLocal(varName, expressionToWasmInstruction(init, data))
|
||||
}
|
||||
|
||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: WasmCodegenContext): WasmInstruction {
|
||||
TODO("IrDelegatingConstructorCall")
|
||||
}
|
||||
|
||||
override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall, data: WasmCodegenContext): WasmInstruction {
|
||||
TODO("IrInstanceInitializerCall")
|
||||
}
|
||||
|
||||
override fun visitTry(aTry: IrTry, data: WasmCodegenContext): WasmInstruction {
|
||||
TODO("IrTry")
|
||||
}
|
||||
|
||||
override fun visitWhen(expression: IrWhen, data: WasmCodegenContext): WasmInstruction {
|
||||
return expression.branches.foldRight(null) { br: IrBranch, inst: WasmInstruction? ->
|
||||
val body = expressionToWasmInstruction(br.result, data)
|
||||
if (isElseBranch(br)) body
|
||||
else {
|
||||
val condition = expressionToWasmInstruction(br.condition, data)
|
||||
WasmIf(condition, WasmThen(body), inst?.let { WasmElse(inst) })
|
||||
}
|
||||
}!!
|
||||
}
|
||||
|
||||
override fun visitWhileLoop(loop: IrWhileLoop, data: WasmCodegenContext): WasmInstruction {
|
||||
TODO("IrWhileLoop")
|
||||
}
|
||||
|
||||
override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: WasmCodegenContext): WasmInstruction {
|
||||
TODO("IrDoWhileLoop")
|
||||
}
|
||||
|
||||
override fun visitSyntheticBody(body: IrSyntheticBody, data: WasmCodegenContext): WasmInstruction {
|
||||
TODO("IrSyntheticBody")
|
||||
}
|
||||
|
||||
override fun visitDynamicMemberExpression(expression: IrDynamicMemberExpression, data: WasmCodegenContext): WasmInstruction =
|
||||
error("Dynamic operators are not supported for WASM target")
|
||||
|
||||
override fun visitDynamicOperatorExpression(expression: IrDynamicOperatorExpression, data: WasmCodegenContext): WasmInstruction =
|
||||
error("Dynamic operators are not supported for WASM target")
|
||||
}
|
||||
|
||||
fun expressionToWasmInstruction(expression: IrExpression, context: WasmCodegenContext): WasmInstruction {
|
||||
return expression.accept(ExpressionTransformer(), context)
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
|
||||
import org.jetbrains.kotlin.backend.wasm.WasmCompilerResult
|
||||
import org.jetbrains.kotlin.backend.wasm.ast.WasmExport
|
||||
import org.jetbrains.kotlin.backend.wasm.ast.WasmModule
|
||||
import org.jetbrains.kotlin.backend.wasm.ast.wasmModuleToWat
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.jsAssignment
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsArrayLiteral
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsBlock
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsNameRef
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsStringLiteral
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
class IrModuleToWasm(private val backendContext: WasmBackendContext) {
|
||||
fun generateModule(module: IrModuleFragment): WasmCompilerResult {
|
||||
val nameTable = generateWatTopLevelNames(module.files)
|
||||
val context = WasmCodegenContext(nameTable, backendContext)
|
||||
val irDeclarations = module.files.flatMap { it.declarations }
|
||||
val wasmDeclarations = irDeclarations.mapNotNull { it.accept(DeclarationTransformer(), context) }
|
||||
val exports = generateExports(module, context)
|
||||
|
||||
|
||||
val wasmModule = WasmModule(context.imports + wasmDeclarations + exports)
|
||||
val wat = wasmModuleToWat(wasmModule)
|
||||
return WasmCompilerResult(wat, generateStringLiteralsSupport(context.stringLiterals))
|
||||
}
|
||||
|
||||
private fun generateStringLiteralsSupport(literals: List<String>): String {
|
||||
return JsBlock(
|
||||
jsAssignment(
|
||||
JsNameRef("stringLiterals", "runtime"),
|
||||
JsArrayLiteral(literals.map { JsStringLiteral(it) })
|
||||
).makeStmt()
|
||||
).toString()
|
||||
}
|
||||
|
||||
private fun generateExports(module: IrModuleFragment, context: WasmCodegenContext): List<WasmExport> {
|
||||
val exports = mutableListOf<WasmExport>()
|
||||
for (file in module.files) {
|
||||
for (declaration in file.declarations) {
|
||||
exports.addIfNotNull(generateExport(declaration, context))
|
||||
}
|
||||
}
|
||||
return exports
|
||||
}
|
||||
|
||||
private fun generateExport(declaration: IrDeclaration, context: WasmCodegenContext): WasmExport? {
|
||||
if (declaration !is IrDeclarationWithVisibility ||
|
||||
declaration !is IrDeclarationWithName ||
|
||||
declaration !is IrSimpleFunction ||
|
||||
declaration.visibility != Visibilities.PUBLIC
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!declaration.isExported(context))
|
||||
return null
|
||||
|
||||
val internalName = context.getGlobalName(declaration)
|
||||
val exportedName = sanitizeName(declaration.name.identifier)
|
||||
|
||||
return WasmExport(
|
||||
wasmName = internalName,
|
||||
exportedName = exportedName,
|
||||
kind = WasmExport.Kind.FUNCTION
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun IrFunction.isExported(context: WasmCodegenContext): Boolean =
|
||||
fqNameWhenAvailable in context.backendContext.additionalExportedDeclarations
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.isTopLevel
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
|
||||
fun <T> wasmNameTable() = NameTable<T>(sanitizer = ::sanitizeWatIdentifier)
|
||||
|
||||
fun generateWatTopLevelNames(packages: List<IrPackageFragment>): Map<IrDeclarationWithName, String> {
|
||||
val names = wasmNameTable<IrDeclarationWithName>()
|
||||
|
||||
fun nameTopLevelDecl(declaration: IrDeclarationWithName) {
|
||||
val suggestedName = declaration.fqNameWhenAvailable?.toString()
|
||||
?: "fqname???" + declaration.name.asString()
|
||||
names.declareFreshName(declaration, suggestedName)
|
||||
}
|
||||
|
||||
for (p in packages) {
|
||||
p.acceptChildrenVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitSimpleFunction(declaration: IrSimpleFunction) {
|
||||
nameTopLevelDecl(declaration)
|
||||
super.visitSimpleFunction(declaration)
|
||||
}
|
||||
|
||||
override fun visitField(declaration: IrField) {
|
||||
if (declaration.isTopLevel)
|
||||
nameTopLevelDecl(declaration)
|
||||
super.visitField(declaration)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return names.names
|
||||
}
|
||||
|
||||
fun sanitizeWatIdentifier(ident: String): String {
|
||||
if (ident.isEmpty())
|
||||
return "_"
|
||||
if (ident.all(::isValidWatIdentifier))
|
||||
return ident
|
||||
return ident.map { if (isValidWatIdentifier(it)) it else "_" }.joinToString("")
|
||||
}
|
||||
|
||||
// https://webassembly.github.io/spec/core/text/values.html#text-id
|
||||
fun isValidWatIdentifier(c: Char): Boolean =
|
||||
c in '0'..'9' || c in 'A'..'Z' || c in 'a'..'z'
|
||||
// TODO: SpiderMonkey js shell can't parse some of the
|
||||
// permitted identifiers: '?', '<'
|
||||
// || c in "!#$%&′*+-./:<=>?@\\^_`|~"
|
||||
|| c in "$.@_"
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.wasm.ast.WasmInstruction
|
||||
import org.jetbrains.kotlin.backend.wasm.ast.WasmNop
|
||||
import org.jetbrains.kotlin.backend.wasm.ast.WasmSetLocal
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
|
||||
class StatementTransformer : BaseTransformer<WasmInstruction, WasmCodegenContext> {
|
||||
override fun visitVariable(declaration: IrVariable, data: WasmCodegenContext): WasmInstruction {
|
||||
val init = declaration.initializer ?: return WasmNop()
|
||||
val varName = data.getLocalName(declaration)
|
||||
return WasmSetLocal(varName, expressionToWasmInstruction(init, data))
|
||||
}
|
||||
|
||||
override fun visitExpression(expression: IrExpression, data: WasmCodegenContext): WasmInstruction {
|
||||
return expressionToWasmInstruction(expression, data)
|
||||
}
|
||||
}
|
||||
|
||||
fun statementToWasmInstruction(statement: IrStatement, context: WasmCodegenContext): WasmInstruction {
|
||||
return statement.accept(StatementTransformer(), context)
|
||||
}
|
||||
|
||||
fun bodyToWasmInstructionList(body: IrBody, context: WasmCodegenContext): List<WasmInstruction> {
|
||||
if (body is IrBlockBody) {
|
||||
return body.statements.map { statementToWasmInstruction(it, context) }
|
||||
} else TODO()
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.wasm.ast.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
|
||||
|
||||
fun WasmCodegenContext.transformType(irType: IrType): WasmValueType =
|
||||
when {
|
||||
irType.isBoolean() -> WasmI32
|
||||
irType.isByte() -> WasmI32
|
||||
irType.isShort() -> WasmI32
|
||||
irType.isInt() -> WasmI32
|
||||
irType.isLong() -> WasmI64
|
||||
irType.isChar() -> WasmI32
|
||||
irType.isFloat() -> WasmF32
|
||||
irType.isDouble() -> WasmF64
|
||||
irType.isString() -> WasmAnyRef
|
||||
irType.isAny() || irType.isNullableAny() -> WasmAnyRef
|
||||
else ->
|
||||
TODO("Unsupported type: ${irType.render()}")
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
|
||||
import org.jetbrains.kotlin.backend.wasm.ast.WasmModuleField
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
|
||||
class WasmCodegenContext(
|
||||
private val topLevelNames: Map<IrDeclarationWithName, String>,
|
||||
val backendContext: WasmBackendContext
|
||||
) {
|
||||
val imports = mutableListOf<WasmModuleField>()
|
||||
var localNames: Map<IrValueDeclaration, String> = emptyMap()
|
||||
val stringLiterals = mutableListOf<String>()
|
||||
|
||||
fun getGlobalName(declaration: IrDeclarationWithName): String =
|
||||
topLevelNames[declaration]
|
||||
?: error("Can't find name for ${declaration.fqNameWhenAvailable}")
|
||||
|
||||
fun getLocalName(declaration: IrValueDeclaration): String =
|
||||
localNames[declaration]
|
||||
?: error("Can't find local name for ${declaration.fqNameWhenAvailable}")
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
|
||||
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
|
||||
import org.jetbrains.kotlin.backend.wasm.codegen.IrModuleToWasm
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.ir.backend.js.loadIr
|
||||
import org.jetbrains.kotlin.ir.backend.js.sortDependencies
|
||||
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
|
||||
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
|
||||
import org.jetbrains.kotlin.library.KotlinLibrary
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
data class WasmCompilerResult(val wat: String, val js: String)
|
||||
|
||||
fun compileWasm(
|
||||
project: Project,
|
||||
files: List<KtFile>,
|
||||
configuration: CompilerConfiguration,
|
||||
phaseConfig: PhaseConfig,
|
||||
allDependencies: List<KotlinLibrary>,
|
||||
friendDependencies: List<KotlinLibrary>,
|
||||
exportedDeclarations: Set<FqName> = emptySet()
|
||||
): WasmCompilerResult {
|
||||
val (moduleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer) =
|
||||
loadIr(project, files, configuration, allDependencies, friendDependencies)
|
||||
|
||||
val moduleDescriptor = moduleFragment.descriptor
|
||||
val context = WasmBackendContext(moduleDescriptor, irBuiltIns, symbolTable, moduleFragment, exportedDeclarations, configuration)
|
||||
|
||||
// Load declarations referenced during `context` initialization
|
||||
dependencyModules.forEach {
|
||||
ExternalDependenciesGenerator(
|
||||
it.descriptor,
|
||||
symbolTable,
|
||||
irBuiltIns,
|
||||
deserializer = deserializer
|
||||
).generateUnboundSymbolsAsDependencies()
|
||||
}
|
||||
|
||||
// Since modules should be initialized in the correct topological order we sort them
|
||||
val irFiles = sortDependencies(dependencyModules).flatMap { it.files } + moduleFragment.files
|
||||
|
||||
moduleFragment.files.clear()
|
||||
moduleFragment.files += irFiles
|
||||
|
||||
// Create stubs
|
||||
ExternalDependenciesGenerator(
|
||||
moduleDescriptor = moduleDescriptor,
|
||||
symbolTable = symbolTable,
|
||||
irBuiltIns = irBuiltIns
|
||||
).generateUnboundSymbolsAsDependencies()
|
||||
moduleFragment.patchDeclarationParents()
|
||||
|
||||
wasmPhases.invokeToplevel(phaseConfig, context, moduleFragment)
|
||||
|
||||
return IrModuleToWasm(context).generateModule(moduleFragment)
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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.ir.backend.js.lower.AbstractBlockDecomposerLowering
|
||||
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
|
||||
class WasmBlockDecomposerLowering(val context: WasmBackendContext) : AbstractBlockDecomposerLowering(context) {
|
||||
override fun unreachableExpression(): IrExpression = TODO()
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.wasm.WasmBackendContext
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.util.irCall
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
|
||||
class BuiltInsLowering(val context: WasmBackendContext) : FileLoweringPass {
|
||||
private val irBuiltins = context.irBuiltIns
|
||||
private val symbols = context.wasmSymbols
|
||||
|
||||
fun transformCall(call: IrCall): IrExpression {
|
||||
when (val symbol = call.symbol) {
|
||||
irBuiltins.eqeqSymbol, irBuiltins.eqeqeqSymbol, in irBuiltins.ieee754equalsFunByOperandType.values -> {
|
||||
val type = call.getValueArgument(0)!!.type
|
||||
val newSymbol = symbols.equalityFunctions[type]
|
||||
?: error("Unsupported equality operator with type: ${type.render()}")
|
||||
return irCall(call, newSymbol)
|
||||
}
|
||||
in symbols.irBuiltInsToWasmIntrinsics.keys -> {
|
||||
val newSymbol = symbols.irBuiltInsToWasmIntrinsics[symbol]!!
|
||||
return irCall(call, newSymbol)
|
||||
}
|
||||
}
|
||||
return call
|
||||
}
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
val newExpression = transformCall(expression)
|
||||
newExpression.transformChildrenVoid(this)
|
||||
return newExpression
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.ir.addChild
|
||||
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
|
||||
import org.jetbrains.kotlin.backend.wasm.utils.hasExcludedFromCodegenAnnotation
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
private val BODILESS_BUILTIN_CLASSES = listOf(
|
||||
"kotlin.Nothing",
|
||||
"kotlin.Array",
|
||||
"kotlin.Any",
|
||||
"kotlin.ByteArray",
|
||||
"kotlin.CharArray",
|
||||
"kotlin.ShortArray",
|
||||
"kotlin.IntArray",
|
||||
"kotlin.LongArray",
|
||||
"kotlin.FloatArray",
|
||||
"kotlin.DoubleArray",
|
||||
"kotlin.BooleanArray",
|
||||
"kotlin.Boolean",
|
||||
"kotlin.Function",
|
||||
"kotlin.Throwable",
|
||||
"kotlin.Suppress",
|
||||
"kotlin.SinceKotlin",
|
||||
"kotlin.Deprecated",
|
||||
"kotlin.ReplaceWith",
|
||||
"kotlin.DeprecationLevel",
|
||||
"kotlin.UnsafeVariance",
|
||||
"kotlin.reflect.KType",
|
||||
"kotlin.reflect.KTypeProjection",
|
||||
"kotlin.reflect.Companion",
|
||||
"kotlin.reflect.KTypeParameter",
|
||||
"kotlin.reflect.KDeclarationContainer",
|
||||
"kotlin.reflect.KProperty",
|
||||
"kotlin.reflect.KProperty0",
|
||||
"kotlin.reflect.KProperty1",
|
||||
"kotlin.reflect.KProperty2",
|
||||
"kotlin.reflect.KMutableProperty0",
|
||||
"kotlin.reflect.KMutableProperty",
|
||||
"kotlin.reflect.KMutableProperty1",
|
||||
"kotlin.reflect.KMutableProperty2",
|
||||
"kotlin.reflect.Accessor",
|
||||
"kotlin.reflect.Getter",
|
||||
"kotlin.reflect.KFunction",
|
||||
"kotlin.reflect.KVariance",
|
||||
"kotlin.reflect.KVisibility",
|
||||
"kotlin.reflect.KClass",
|
||||
"kotlin.reflect.KCallable",
|
||||
"kotlin.reflect.KClassifier",
|
||||
"kotlin.reflect.KParameter",
|
||||
"kotlin.reflect.Kind",
|
||||
"kotlin.reflect.KAnnotatedElement",
|
||||
"kotlin.annotation.Target",
|
||||
"kotlin.annotation.AnnotationTarget",
|
||||
"kotlin.annotation.Retention",
|
||||
"kotlin.annotation.AnnotationRetention",
|
||||
"kotlin.annotation.MustBeDocumented",
|
||||
"kotlin.Unit",
|
||||
"kotlin.collections.BooleanIterator",
|
||||
"kotlin.collections.CharIterator",
|
||||
"kotlin.collections.ByteIterator",
|
||||
"kotlin.collections.ShortIterator",
|
||||
"kotlin.collections.IntIterator",
|
||||
"kotlin.collections.FloatIterator",
|
||||
"kotlin.collections.LongIterator",
|
||||
"kotlin.collections.DoubleIterator",
|
||||
"kotlin.internal.PlatformDependent",
|
||||
"kotlin.CharSequence",
|
||||
"kotlin.Annotation",
|
||||
"kotlin.Comparable",
|
||||
"kotlin.collections.Collection",
|
||||
"kotlin.collections.Iterable",
|
||||
"kotlin.collections.List",
|
||||
"kotlin.collections.Map",
|
||||
"kotlin.collections.Set",
|
||||
"kotlin.collections.MutableCollection",
|
||||
"kotlin.collections.MutableIterable",
|
||||
"kotlin.collections.MutableSet",
|
||||
"kotlin.collections.MutableList",
|
||||
"kotlin.collections.MutableMap",
|
||||
"kotlin.collections.Entry",
|
||||
"kotlin.collections.MutableEntry",
|
||||
"kotlin.Number",
|
||||
"kotlin.Enum",
|
||||
"kotlin.collections.Iterator",
|
||||
"kotlin.collections.ListIterator",
|
||||
"kotlin.collections.MutableIterator",
|
||||
"kotlin.collections.MutableListIterator"
|
||||
).map { FqName(it) }.toSet()
|
||||
|
||||
fun excludeDeclarationsFromCodegen(context: WasmBackendContext, module: IrModuleFragment) {
|
||||
|
||||
fun isExcluded(declaration: IrDeclaration): Boolean {
|
||||
if (declaration is IrDeclarationWithName && declaration.fqNameWhenAvailable in BODILESS_BUILTIN_CLASSES)
|
||||
return true
|
||||
|
||||
if (declaration.hasExcludedFromCodegenAnnotation())
|
||||
return true
|
||||
|
||||
val parentFile = declaration.parent as? IrFile
|
||||
if (parentFile?.hasExcludedFromCodegenAnnotation() == true)
|
||||
return true
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
for (file in module.files) {
|
||||
val it = file.declarations.iterator()
|
||||
while (it.hasNext()) {
|
||||
val d = it.next() as? IrDeclarationWithName ?: continue
|
||||
if (isExcluded(d)) {
|
||||
it.remove()
|
||||
context.excludedDeclarations.addChild(d)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.utils
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getSingleConstStringArgument
|
||||
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||
import org.jetbrains.kotlin.ir.util.getAnnotation
|
||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
fun IrAnnotationContainer.hasExcludedFromCodegenAnnotation(): Boolean =
|
||||
hasAnnotation(FqName("kotlin.wasm.internal.ExcludedFromCodegen"))
|
||||
|
||||
fun IrAnnotationContainer.getWasmInstructionAnnotation(): String? =
|
||||
getAnnotation(FqName("kotlin.wasm.internal.WasmInstruction"))?.getSingleConstStringArgument()
|
||||
|
||||
class WasmImportPair(val module: String, val name: String)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun IrAnnotationContainer.getWasmImportAnnotation(): WasmImportPair? =
|
||||
getAnnotation(FqName("kotlin.wasm.internal.WasmImport"))?.let {
|
||||
WasmImportPair(
|
||||
(it.getValueArgument(0) as IrConst<String>).value,
|
||||
(it.getValueArgument(1) as IrConst<String>).value
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user