[Wasm] Basic CLI
-Xwasm option that will produce wasm instead of JS when used with -Xir-produce-js Does not affect klib production
This commit is contained in:
+4
-1
@@ -178,6 +178,9 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
|
|||||||
@Argument(value = "-Xerror-tolerance-policy", description = "Set up error tolerance policy (NONE, SEMANTIC, SYNTAX, ALL)")
|
@Argument(value = "-Xerror-tolerance-policy", description = "Set up error tolerance policy (NONE, SEMANTIC, SYNTAX, ALL)")
|
||||||
var errorTolerancePolicy: String? by NullableStringFreezableVar(null)
|
var errorTolerancePolicy: String? by NullableStringFreezableVar(null)
|
||||||
|
|
||||||
|
@Argument(value = "-Xwasm", description = "Use experimental WebAssembly compiler backend")
|
||||||
|
var wasm: Boolean by FreezableVar(false)
|
||||||
|
|
||||||
override fun checkIrSupport(languageVersionSettings: LanguageVersionSettings, collector: MessageCollector) {
|
override fun checkIrSupport(languageVersionSettings: LanguageVersionSettings, collector: MessageCollector) {
|
||||||
if (!isIrBackendEnabled()) return
|
if (!isIrBackendEnabled()) return
|
||||||
|
|
||||||
@@ -196,4 +199,4 @@ fun K2JSCompilerArguments.isPreIrBackendDisabled(): Boolean =
|
|||||||
irOnly || irProduceJs || irProduceKlibFile
|
irOnly || irProduceJs || irProduceKlibFile
|
||||||
|
|
||||||
fun K2JSCompilerArguments.isIrBackendEnabled(): Boolean =
|
fun K2JSCompilerArguments.isIrBackendEnabled(): Boolean =
|
||||||
irProduceKlibDir || irProduceJs || irProduceKlibFile
|
irProduceKlibDir || irProduceJs || irProduceKlibFile || wasm
|
||||||
@@ -12,6 +12,7 @@ dependencies {
|
|||||||
compile(project(":compiler:ir.backend.common"))
|
compile(project(":compiler:ir.backend.common"))
|
||||||
compile(project(":compiler:ir.serialization.js"))
|
compile(project(":compiler:ir.serialization.js"))
|
||||||
compile(project(":compiler:backend.js"))
|
compile(project(":compiler:backend.js"))
|
||||||
|
compile(project(":compiler:backend.wasm"))
|
||||||
compile(project(":js:js.translator"))
|
compile(project(":js:js.translator"))
|
||||||
compile(project(":js:js.serializer"))
|
compile(project(":js:js.serializer"))
|
||||||
compile(project(":js:js.dce"))
|
compile(project(":js:js.dce"))
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ package org.jetbrains.kotlin.cli.js
|
|||||||
import com.intellij.openapi.Disposable
|
import com.intellij.openapi.Disposable
|
||||||
import com.intellij.openapi.util.io.FileUtil
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
|
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
|
||||||
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion
|
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion
|
||||||
|
import org.jetbrains.kotlin.backend.wasm.compileWasm
|
||||||
|
import org.jetbrains.kotlin.backend.wasm.wasmPhases
|
||||||
import org.jetbrains.kotlin.cli.common.*
|
import org.jetbrains.kotlin.cli.common.*
|
||||||
import org.jetbrains.kotlin.cli.common.ExitCode.COMPILATION_ERROR
|
import org.jetbrains.kotlin.cli.common.ExitCode.COMPILATION_ERROR
|
||||||
import org.jetbrains.kotlin.cli.common.ExitCode.OK
|
import org.jetbrains.kotlin.cli.common.ExitCode.OK
|
||||||
@@ -34,6 +37,7 @@ import org.jetbrains.kotlin.ir.backend.js.*
|
|||||||
import org.jetbrains.kotlin.ir.declarations.persistent.PersistentIrFactory
|
import org.jetbrains.kotlin.ir.declarations.persistent.PersistentIrFactory
|
||||||
import org.jetbrains.kotlin.js.config.*
|
import org.jetbrains.kotlin.js.config.*
|
||||||
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
|
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||||
import org.jetbrains.kotlin.util.Logger
|
import org.jetbrains.kotlin.util.Logger
|
||||||
@@ -216,6 +220,33 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
|||||||
MainModule.SourceFiles(sourcesFiles)
|
MainModule.SourceFiles(sourcesFiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arguments.wasm) {
|
||||||
|
val res = compileWasm(
|
||||||
|
projectJs,
|
||||||
|
mainModule,
|
||||||
|
AnalyzerWithCompilerReport(config.configuration),
|
||||||
|
config.configuration,
|
||||||
|
PhaseConfig(wasmPhases),
|
||||||
|
allDependencies = resolvedLibraries,
|
||||||
|
friendDependencies = friendDependencies,
|
||||||
|
exportedDeclarations = setOf(FqName("main"))
|
||||||
|
)
|
||||||
|
val outputWasmFile = outputFile.withReplacedExtensionOrNull(outputFile.extension, "wasm")!!
|
||||||
|
outputWasmFile.writeBytes(res.wasm)
|
||||||
|
val outputWatFile = outputFile.withReplacedExtensionOrNull(outputFile.extension, "wat")!!
|
||||||
|
outputWatFile.writeText(res.wat)
|
||||||
|
|
||||||
|
val runner = """
|
||||||
|
const wasmBinary = read(String.raw`${outputWasmFile.absoluteFile}`, 'binary');
|
||||||
|
const wasmModule = new WebAssembly.Module(wasmBinary);
|
||||||
|
const wasmInstance = new WebAssembly.Instance(wasmModule, { runtime, js_code });
|
||||||
|
wasmInstance.exports.main();
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
outputFile.writeText(res.js + "\n" + runner)
|
||||||
|
return OK
|
||||||
|
}
|
||||||
|
|
||||||
val compiledModule = try {
|
val compiledModule = try {
|
||||||
compile(
|
compile(
|
||||||
projectJs,
|
projectJs,
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class WasmCompilerResult(val wat: String, val js: String, val wasm: ByteArray)
|
|||||||
|
|
||||||
fun compileWasm(
|
fun compileWasm(
|
||||||
project: Project,
|
project: Project,
|
||||||
files: List<KtFile>,
|
mainModule: MainModule,
|
||||||
analyzer: AbstractAnalyzerWithCompilerReport,
|
analyzer: AbstractAnalyzerWithCompilerReport,
|
||||||
configuration: CompilerConfiguration,
|
configuration: CompilerConfiguration,
|
||||||
phaseConfig: PhaseConfig,
|
phaseConfig: PhaseConfig,
|
||||||
@@ -42,21 +42,26 @@ fun compileWasm(
|
|||||||
): WasmCompilerResult {
|
): WasmCompilerResult {
|
||||||
val (moduleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer) =
|
val (moduleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer) =
|
||||||
loadIr(
|
loadIr(
|
||||||
project, MainModule.SourceFiles(files), analyzer, configuration, allDependencies, friendDependencies,
|
project, mainModule, analyzer, configuration, allDependencies, friendDependencies,
|
||||||
PersistentIrFactory
|
PersistentIrFactory
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val allModules = when (mainModule) {
|
||||||
|
is MainModule.SourceFiles -> dependencyModules + listOf(moduleFragment)
|
||||||
|
is MainModule.Klib -> dependencyModules
|
||||||
|
}
|
||||||
|
|
||||||
val moduleDescriptor = moduleFragment.descriptor
|
val moduleDescriptor = moduleFragment.descriptor
|
||||||
val context = WasmBackendContext(moduleDescriptor, irBuiltIns, symbolTable, moduleFragment, exportedDeclarations, configuration)
|
val context = WasmBackendContext(moduleDescriptor, irBuiltIns, symbolTable, moduleFragment, exportedDeclarations, configuration)
|
||||||
|
|
||||||
// Load declarations referenced during `context` initialization
|
// Load declarations referenced during `context` initialization
|
||||||
dependencyModules.forEach {
|
allModules.forEach {
|
||||||
val irProviders = generateTypicalIrProviderList(it.descriptor, irBuiltIns, symbolTable, deserializer)
|
val irProviders = generateTypicalIrProviderList(it.descriptor, irBuiltIns, symbolTable, deserializer)
|
||||||
ExternalDependenciesGenerator(symbolTable, irProviders, configuration.languageVersionSettings)
|
ExternalDependenciesGenerator(symbolTable, irProviders, configuration.languageVersionSettings)
|
||||||
.generateUnboundSymbolsAsDependencies()
|
.generateUnboundSymbolsAsDependencies()
|
||||||
}
|
}
|
||||||
|
|
||||||
val irFiles = dependencyModules.flatMap { it.files } + moduleFragment.files
|
val irFiles = allModules.flatMap { it.files }
|
||||||
|
|
||||||
moduleFragment.files.clear()
|
moduleFragment.files.clear()
|
||||||
moduleFragment.files += irFiles
|
moduleFragment.files += irFiles
|
||||||
@@ -77,13 +82,86 @@ fun compileWasm(
|
|||||||
watGenerator.appendWasmModule(linkedModule)
|
watGenerator.appendWasmModule(linkedModule)
|
||||||
val wat = watGenerator.toString()
|
val wat = watGenerator.toString()
|
||||||
|
|
||||||
|
val js = compiledWasmModule.generateJs()
|
||||||
|
|
||||||
val os = ByteArrayOutputStream()
|
val os = ByteArrayOutputStream()
|
||||||
WasmIrToBinary(os, linkedModule).appendWasmModule()
|
WasmIrToBinary(os, linkedModule).appendWasmModule()
|
||||||
val byteArray = os.toByteArray()
|
val byteArray = os.toByteArray()
|
||||||
|
|
||||||
return WasmCompilerResult(
|
return WasmCompilerResult(
|
||||||
wat,
|
wat = wat,
|
||||||
generateStringLiteralsSupport(compiledWasmModule.stringLiterals),
|
js = js,
|
||||||
wasm = byteArray
|
wasm = byteArray
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun WasmCompiledModuleFragment.generateJs(): String {
|
||||||
|
val runtime = """
|
||||||
|
const runtime = {
|
||||||
|
String_plus(str1, str2) {
|
||||||
|
return str1 + String(str2);
|
||||||
|
},
|
||||||
|
|
||||||
|
String_getLength(str) {
|
||||||
|
return str.length;
|
||||||
|
},
|
||||||
|
|
||||||
|
String_getChar(str, index) {
|
||||||
|
return str.charCodeAt(index);
|
||||||
|
},
|
||||||
|
|
||||||
|
String_compareTo(str1, str2) {
|
||||||
|
if (str1 > str2) return 1;
|
||||||
|
if (str1 < str2) return -1;
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
String_equals(str, other) {
|
||||||
|
return str === other;
|
||||||
|
},
|
||||||
|
|
||||||
|
String_subsequence(str, startIndex, endIndex) {
|
||||||
|
return str.substring(startIndex, endIndex);
|
||||||
|
},
|
||||||
|
|
||||||
|
String_getLiteral(index) {
|
||||||
|
return runtime.stringLiterals[index];
|
||||||
|
},
|
||||||
|
|
||||||
|
coerceToString(value) {
|
||||||
|
return String(value);
|
||||||
|
},
|
||||||
|
|
||||||
|
Char_toString(char) {
|
||||||
|
return String.fromCharCode(char)
|
||||||
|
},
|
||||||
|
|
||||||
|
JsArray_new(size) {
|
||||||
|
return new Array(size);
|
||||||
|
},
|
||||||
|
|
||||||
|
JsArray_get(array, index) {
|
||||||
|
return array[index];
|
||||||
|
},
|
||||||
|
|
||||||
|
JsArray_set(array, index, value) {
|
||||||
|
array[index] = value;
|
||||||
|
},
|
||||||
|
|
||||||
|
JsArray_getSize(array) {
|
||||||
|
return array.length;
|
||||||
|
},
|
||||||
|
|
||||||
|
identity(x) {
|
||||||
|
return x;
|
||||||
|
},
|
||||||
|
|
||||||
|
println(value) {
|
||||||
|
console.log(">>> " + value)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
return runtime + generateStringLiteralsSupport(stringLiterals)
|
||||||
|
}
|
||||||
|
|||||||
+1
@@ -21,6 +21,7 @@ where advanced options include:
|
|||||||
-Xir-produce-klib-file Generate packed klib into file specified by -output. Disables pre-IR backend
|
-Xir-produce-klib-file Generate packed klib into file specified by -output. Disables pre-IR backend
|
||||||
-Xmetadata-only Generate *.meta.js and *.kjsm files only
|
-Xmetadata-only Generate *.meta.js and *.kjsm files only
|
||||||
-Xtyped-arrays Translate primitive arrays to JS typed arrays
|
-Xtyped-arrays Translate primitive arrays to JS typed arrays
|
||||||
|
-Xwasm Use experimental WebAssembly compiler backend
|
||||||
-Xallow-kotlin-package Allow compiling code in package 'kotlin' and allow not requiring kotlin.stdlib in module-info
|
-Xallow-kotlin-package Allow compiling code in package 'kotlin' and allow not requiring kotlin.stdlib in module-info
|
||||||
-Xallow-result-return-type Allow compiling code when `kotlin.Result` is used as a return type
|
-Xallow-result-return-type Allow compiling code when `kotlin.Result` is used as a return type
|
||||||
-Xcheck-phase-conditions Check pre- and postconditions on phases
|
-Xcheck-phase-conditions Check pre- and postconditions on phases
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
|||||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.config.*
|
import org.jetbrains.kotlin.config.*
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.MainModule
|
||||||
import org.jetbrains.kotlin.ir.backend.js.loadKlib
|
import org.jetbrains.kotlin.ir.backend.js.loadKlib
|
||||||
import org.jetbrains.kotlin.js.config.JsConfig
|
import org.jetbrains.kotlin.js.config.JsConfig
|
||||||
import org.jetbrains.kotlin.js.facade.TranslationUnit
|
import org.jetbrains.kotlin.js.facade.TranslationUnit
|
||||||
@@ -114,7 +115,7 @@ abstract class BasicWasmBoxTest(
|
|||||||
testFunction: String
|
testFunction: String
|
||||||
) {
|
) {
|
||||||
val filesToCompile = units.map { (it as TranslationUnit.SourceFile).file }
|
val filesToCompile = units.map { (it as TranslationUnit.SourceFile).file }
|
||||||
val debugMode =getBoolean("kotlin.js.debugMode")
|
val debugMode = getBoolean("kotlin.js.debugMode")
|
||||||
|
|
||||||
val phaseConfig = if (debugMode) {
|
val phaseConfig = if (debugMode) {
|
||||||
val allPhasesSet = wasmPhases.toPhaseMap().values.toSet()
|
val allPhasesSet = wasmPhases.toPhaseMap().values.toSet()
|
||||||
@@ -137,7 +138,7 @@ abstract class BasicWasmBoxTest(
|
|||||||
|
|
||||||
val compilerResult = compileWasm(
|
val compilerResult = compileWasm(
|
||||||
project = config.project,
|
project = config.project,
|
||||||
files = filesToCompile,
|
mainModule = MainModule.SourceFiles(filesToCompile),
|
||||||
analyzer = AnalyzerWithCompilerReport(config.configuration),
|
analyzer = AnalyzerWithCompilerReport(config.configuration),
|
||||||
configuration = config.configuration,
|
configuration = config.configuration,
|
||||||
phaseConfig = phaseConfig,
|
phaseConfig = phaseConfig,
|
||||||
@@ -150,8 +151,6 @@ abstract class BasicWasmBoxTest(
|
|||||||
outputWatFile.write(compilerResult.wat)
|
outputWatFile.write(compilerResult.wat)
|
||||||
outputWasmFile.writeBytes(compilerResult.wasm)
|
outputWasmFile.writeBytes(compilerResult.wasm)
|
||||||
|
|
||||||
val runtime = File("libraries/stdlib/wasm/runtime/runtime.js").readText()
|
|
||||||
|
|
||||||
val testRunner = """
|
val testRunner = """
|
||||||
const wasmBinary = read(String.raw`${outputWasmFile.absoluteFile}`, 'binary');
|
const wasmBinary = read(String.raw`${outputWasmFile.absoluteFile}`, 'binary');
|
||||||
const wasmModule = new WebAssembly.Module(wasmBinary);
|
const wasmModule = new WebAssembly.Module(wasmBinary);
|
||||||
@@ -162,7 +161,7 @@ abstract class BasicWasmBoxTest(
|
|||||||
throw `Wrong box result '${'$'}{actualResult}'; Expected "OK"`;
|
throw `Wrong box result '${'$'}{actualResult}'; Expected "OK"`;
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
outputJsFile.write(runtime + "\n" + compilerResult.js + "\n" + testRunner)
|
outputJsFile.write(compilerResult.js + "\n" + testRunner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,100 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const runtime = {
|
|
||||||
/**
|
|
||||||
* kotlin.String#plus(other: Any?): String
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
String_plus(str1, str2) {
|
|
||||||
if (typeof str1 != "string") throw `Illegal argument str1: ${str1}`;
|
|
||||||
return str1 + String(str2);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {number}
|
|
||||||
*/
|
|
||||||
String_getLength(str) {
|
|
||||||
if (typeof str != "string") throw `Illegal argument x: ${str}`;
|
|
||||||
return str.length;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {number}
|
|
||||||
*/
|
|
||||||
String_getChar(str, index) {
|
|
||||||
if (typeof str != "string") throw `Illegal argument str: ${str}`;
|
|
||||||
if (typeof index != "number") throw `Illegal argument index: ${index}`;
|
|
||||||
return str.charCodeAt(index);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {number}
|
|
||||||
*/
|
|
||||||
String_compareTo(str1, str2) {
|
|
||||||
if (str1 > str2) return 1;
|
|
||||||
if (str1 < str2) return -1;
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
String_equals(str, other) {
|
|
||||||
// if (typeof str != "string") throw `Illegal argument str: ${str}`;
|
|
||||||
return str === other;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
String_subsequence(str, startIndex, endIndex) {
|
|
||||||
return str.substring(startIndex, endIndex);
|
|
||||||
},
|
|
||||||
|
|
||||||
String_getLiteral(index) {
|
|
||||||
return runtime.stringLiterals[index];
|
|
||||||
},
|
|
||||||
|
|
||||||
coerceToString(value) {
|
|
||||||
return String(value);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
Char_toString(char) {
|
|
||||||
return String.fromCharCode(char)
|
|
||||||
},
|
|
||||||
|
|
||||||
JsArray_new(size) {
|
|
||||||
if (typeof size != "number") throw `Illegal argument size: ${size}`;
|
|
||||||
return new Array(size);
|
|
||||||
},
|
|
||||||
|
|
||||||
JsArray_get(array, index) {
|
|
||||||
if (typeof index != "number") throw `Illegal argument index: ${index}`;
|
|
||||||
if (array.length <= index) throw `Index out of bounds: index=${index} length=${array.length}`;
|
|
||||||
return array[index];
|
|
||||||
},
|
|
||||||
|
|
||||||
JsArray_set(array, index, value) {
|
|
||||||
if (typeof index != "number") throw `Illegal argument index: ${index}`;
|
|
||||||
if (array.length <= index) throw `Index out of bounds: index=${index} length=${array.length}`;
|
|
||||||
array[index] = value;
|
|
||||||
},
|
|
||||||
|
|
||||||
JsArray_getSize(array) {
|
|
||||||
return array.length;
|
|
||||||
},
|
|
||||||
|
|
||||||
identity(x) {
|
|
||||||
return x;
|
|
||||||
},
|
|
||||||
|
|
||||||
println(value) {
|
|
||||||
console.log(">>> " + value)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user