[K/Wasm] Add Binaryen sizes to Wasm size tests
This commit is contained in:
+2
@@ -1,7 +1,9 @@
|
|||||||
// TARGET_BACKEND: WASM
|
// TARGET_BACKEND: WASM
|
||||||
|
|
||||||
|
// RUN_THIRD_PARTY_OPTIMIZER
|
||||||
// WASM_DCE_EXPECTED_OUTPUT_SIZE: wasm 12_559
|
// WASM_DCE_EXPECTED_OUTPUT_SIZE: wasm 12_559
|
||||||
// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 4_526
|
// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 4_526
|
||||||
|
// WASM_OPT_EXPECTED_OUTPUT_SIZE: 2_640
|
||||||
|
|
||||||
// FILE: test.kt
|
// FILE: test.kt
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
// TARGET_BACKEND: WASM
|
// TARGET_BACKEND: WASM
|
||||||
|
|
||||||
|
// RUN_THIRD_PARTY_OPTIMIZER
|
||||||
// WASM_DCE_EXPECTED_OUTPUT_SIZE: wasm 38_992
|
// WASM_DCE_EXPECTED_OUTPUT_SIZE: wasm 38_992
|
||||||
// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 4_552
|
// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 4_552
|
||||||
|
// WASM_OPT_EXPECTED_OUTPUT_SIZE: 9_847
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
println("Hello, World!")
|
println("Hello, World!")
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
// TARGET_BACKEND: WASM
|
// TARGET_BACKEND: WASM
|
||||||
|
|
||||||
|
// RUN_THIRD_PARTY_OPTIMIZER
|
||||||
// WASM_DCE_EXPECTED_OUTPUT_SIZE: wasm 13_922
|
// WASM_DCE_EXPECTED_OUTPUT_SIZE: wasm 13_922
|
||||||
// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 5_081
|
// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 5_081
|
||||||
|
// WASM_OPT_EXPECTED_OUTPUT_SIZE: 4_317
|
||||||
|
|
||||||
// FILE: test.kt
|
// FILE: test.kt
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
// TARGET_BACKEND: WASM
|
// TARGET_BACKEND: WASM
|
||||||
|
|
||||||
|
// RUN_THIRD_PARTY_OPTIMIZER
|
||||||
// WASM_DCE_EXPECTED_OUTPUT_SIZE: wasm 17_619
|
// WASM_DCE_EXPECTED_OUTPUT_SIZE: wasm 17_619
|
||||||
// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 4_398
|
// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 4_398
|
||||||
|
// WASM_OPT_EXPECTED_OUTPUT_SIZE: 5_841
|
||||||
|
|
||||||
object Simple
|
object Simple
|
||||||
|
|
||||||
|
|||||||
+2
@@ -1,6 +1,8 @@
|
|||||||
// TARGET_BACKEND: WASM
|
// TARGET_BACKEND: WASM
|
||||||
|
|
||||||
|
// RUN_THIRD_PARTY_OPTIMIZER
|
||||||
// WASM_DCE_EXPECTED_OUTPUT_SIZE: wasm 12_601
|
// WASM_DCE_EXPECTED_OUTPUT_SIZE: wasm 12_601
|
||||||
// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 4_398
|
// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 4_398
|
||||||
|
// WASM_OPT_EXPECTED_OUTPUT_SIZE: 3_743
|
||||||
|
|
||||||
fun box() = "OK"
|
fun box() = "OK"
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
// TARGET_BACKEND: JS_IR
|
// TARGET_BACKEND: JS_IR
|
||||||
// TARGET_BACKEND: WASM
|
// TARGET_BACKEND: WASM
|
||||||
|
|
||||||
|
// RUN_THIRD_PARTY_OPTIMIZER
|
||||||
// WASM_DCE_EXPECTED_OUTPUT_SIZE: wasm 13_109
|
// WASM_DCE_EXPECTED_OUTPUT_SIZE: wasm 13_109
|
||||||
|
// WASM_OPT_EXPECTED_OUTPUT_SIZE: 3_900
|
||||||
|
|
||||||
interface I {
|
interface I {
|
||||||
fun foo() = "OK"
|
fun foo() = "OK"
|
||||||
|
|||||||
+4
@@ -39,4 +39,8 @@ object WasmEnvironmentConfigurationDirectives : SimpleDirectivesContainer() {
|
|||||||
description = "Enables generation of source map",
|
description = "Enables generation of source map",
|
||||||
applicability = DirectiveApplicability.Global
|
applicability = DirectiveApplicability.Global
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val RUN_THIRD_PARTY_OPTIMIZER by directive(
|
||||||
|
description = "Also run third-party optimizer (for now, only binaryen is supported) after the main compilation",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ object BinaryArtifacts {
|
|||||||
class Wasm(
|
class Wasm(
|
||||||
val compilerResult: WasmCompilerResult,
|
val compilerResult: WasmCompilerResult,
|
||||||
val compilerResultWithDCE: WasmCompilerResult,
|
val compilerResultWithDCE: WasmCompilerResult,
|
||||||
|
val compilerResultWithOptimizer: WasmCompilerResult?,
|
||||||
) : ResultingArtifact.Binary<Wasm>() {
|
) : ResultingArtifact.Binary<Wasm>() {
|
||||||
override val kind: BinaryKind<Wasm>
|
override val kind: BinaryKind<Wasm>
|
||||||
get() = ArtifactKinds.Wasm
|
get() = ArtifactKinds.Wasm
|
||||||
|
|||||||
+2
-33
@@ -13,6 +13,7 @@ import org.gradle.work.NormalizeLineEndings
|
|||||||
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrCompilation
|
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrCompilation
|
||||||
import org.jetbrains.kotlin.gradle.tasks.registerTask
|
import org.jetbrains.kotlin.gradle.tasks.registerTask
|
||||||
import org.jetbrains.kotlin.gradle.utils.newFileProperty
|
import org.jetbrains.kotlin.gradle.utils.newFileProperty
|
||||||
|
import org.jetbrains.kotlin.platform.wasm.BinaryenConfig
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@DisableCachingByDefault
|
@DisableCachingByDefault
|
||||||
@@ -33,39 +34,7 @@ constructor() : AbstractExecTask<BinaryenExec>(BinaryenExec::class.java) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Input
|
@Input
|
||||||
var binaryenArgs: MutableList<String> = mutableListOf(
|
var binaryenArgs: MutableList<String> = BinaryenConfig.binaryenArgs.toMutableList()
|
||||||
// Proposals
|
|
||||||
"--enable-gc",
|
|
||||||
"--enable-reference-types",
|
|
||||||
"--enable-exception-handling",
|
|
||||||
"--enable-bulk-memory", // For array initialization from data sections
|
|
||||||
|
|
||||||
// Other options
|
|
||||||
"--enable-nontrapping-float-to-int",
|
|
||||||
// It's turned out that it's not safe
|
|
||||||
// "--closed-world",
|
|
||||||
|
|
||||||
// Optimizations:
|
|
||||||
// Note the order and repetition of the next options matter.
|
|
||||||
//
|
|
||||||
// About Binaryen optimizations:
|
|
||||||
// GC Optimization Guidebook -- https://github.com/WebAssembly/binaryen/wiki/GC-Optimization-Guidebook
|
|
||||||
// Optimizer Cookbook -- https://github.com/WebAssembly/binaryen/wiki/Optimizer-Cookbook
|
|
||||||
//
|
|
||||||
"--inline-functions-with-loops",
|
|
||||||
"--traps-never-happen",
|
|
||||||
"--fast-math",
|
|
||||||
// without "--type-merging" it produces increases the size
|
|
||||||
// "--type-ssa",
|
|
||||||
"-O3",
|
|
||||||
"-O3",
|
|
||||||
"--gufa",
|
|
||||||
"-O3",
|
|
||||||
// requires --closed-world
|
|
||||||
// "--type-merging",
|
|
||||||
"-O3",
|
|
||||||
"-Oz",
|
|
||||||
)
|
|
||||||
|
|
||||||
@PathSensitive(PathSensitivity.RELATIVE)
|
@PathSensitive(PathSensitivity.RELATIVE)
|
||||||
@InputFile
|
@InputFile
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2023 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.
|
||||||
|
*/
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.tasks.testing.Test
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.binaryen.BinaryenRootExtension
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.binaryen.BinaryenRootPlugin
|
||||||
|
|
||||||
|
private object BinaryenUtils {
|
||||||
|
lateinit var binaryenPlugin: BinaryenRootExtension
|
||||||
|
|
||||||
|
fun useBinaryenPlugin(project: Project) {
|
||||||
|
binaryenPlugin = BinaryenRootPlugin.apply(project.rootProject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Project.useBinaryenPlugin() {
|
||||||
|
BinaryenUtils.useBinaryenPlugin(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Test.setupBinaryen() {
|
||||||
|
dependsOn(BinaryenUtils.binaryenPlugin.setupTaskProvider)
|
||||||
|
val binaryenExecutablePath = project.provider {
|
||||||
|
BinaryenUtils.binaryenPlugin.requireConfigured().executablePath.absolutePath
|
||||||
|
}
|
||||||
|
doFirst {
|
||||||
|
systemProperty("binaryen.path", binaryenExecutablePath.get())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2023 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.platform.wasm
|
||||||
|
|
||||||
|
object BinaryenConfig {
|
||||||
|
val binaryenArgs = listOf(
|
||||||
|
// Proposals
|
||||||
|
"--enable-gc",
|
||||||
|
"--enable-reference-types",
|
||||||
|
"--enable-exception-handling",
|
||||||
|
"--enable-bulk-memory", // For array initialization from data sections
|
||||||
|
|
||||||
|
// Other options
|
||||||
|
"--enable-nontrapping-float-to-int",
|
||||||
|
// It's turned out that it's not safe
|
||||||
|
// "--closed-world",
|
||||||
|
|
||||||
|
// Optimizations:
|
||||||
|
// Note the order and repetition of the next options matter.
|
||||||
|
//
|
||||||
|
// About Binaryen optimizations:
|
||||||
|
// GC Optimization Guidebook -- https://github.com/WebAssembly/binaryen/wiki/GC-Optimization-Guidebook
|
||||||
|
// Optimizer Cookbook -- https://github.com/WebAssembly/binaryen/wiki/Optimizer-Cookbook
|
||||||
|
//
|
||||||
|
"--inline-functions-with-loops",
|
||||||
|
"--traps-never-happen",
|
||||||
|
"--fast-math",
|
||||||
|
// without "--type-merging" it produces increases the size
|
||||||
|
// "--type-ssa",
|
||||||
|
"-O3",
|
||||||
|
"-O3",
|
||||||
|
"--gufa",
|
||||||
|
"-O3",
|
||||||
|
// requires --closed-world
|
||||||
|
// "--type-merging",
|
||||||
|
"-O3",
|
||||||
|
"-Oz",
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -79,6 +79,7 @@ val generationRoot = projectDir.resolve("tests-gen")
|
|||||||
|
|
||||||
useD8Plugin()
|
useD8Plugin()
|
||||||
useNodeJsPlugin()
|
useNodeJsPlugin()
|
||||||
|
useBinaryenPlugin()
|
||||||
optInToExperimentalCompilerApi()
|
optInToExperimentalCompilerApi()
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@@ -147,6 +148,7 @@ fun Project.wasmProjectTest(
|
|||||||
workingDir = rootDir
|
workingDir = rootDir
|
||||||
setupV8()
|
setupV8()
|
||||||
setupNodeJs()
|
setupNodeJs()
|
||||||
|
setupBinaryen()
|
||||||
setupSpiderMonkey()
|
setupSpiderMonkey()
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
setupWasmStdlib("js")
|
setupWasmStdlib("js")
|
||||||
|
|||||||
+19
-1
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.wasm.test.converters
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
|
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
|
||||||
import org.jetbrains.kotlin.backend.common.phaser.toPhaseMap
|
import org.jetbrains.kotlin.backend.common.phaser.toPhaseMap
|
||||||
|
import org.jetbrains.kotlin.backend.wasm.WasmCompilerResult
|
||||||
import org.jetbrains.kotlin.backend.wasm.compileToLoweredIr
|
import org.jetbrains.kotlin.backend.wasm.compileToLoweredIr
|
||||||
import org.jetbrains.kotlin.backend.wasm.compileWasm
|
import org.jetbrains.kotlin.backend.wasm.compileWasm
|
||||||
import org.jetbrains.kotlin.backend.wasm.dce.eliminateDeadDeclarations
|
import org.jetbrains.kotlin.backend.wasm.dce.eliminateDeadDeclarations
|
||||||
@@ -32,12 +33,15 @@ import org.jetbrains.kotlin.test.model.BinaryArtifacts
|
|||||||
import org.jetbrains.kotlin.test.model.TestModule
|
import org.jetbrains.kotlin.test.model.TestModule
|
||||||
import org.jetbrains.kotlin.test.services.*
|
import org.jetbrains.kotlin.test.services.*
|
||||||
import org.jetbrains.kotlin.test.services.configuration.WasmEnvironmentConfigurator
|
import org.jetbrains.kotlin.test.services.configuration.WasmEnvironmentConfigurator
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||||
import org.jetbrains.kotlin.wasm.test.handlers.getWasmTestOutputDirectory
|
import org.jetbrains.kotlin.wasm.test.handlers.getWasmTestOutputDirectory
|
||||||
|
import org.jetbrains.kotlin.wasm.test.tools.WasmOptimizer
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class WasmBackendFacade(
|
class WasmBackendFacade(
|
||||||
private val testServices: TestServices
|
private val testServices: TestServices
|
||||||
) : AbstractTestFacade<BinaryArtifacts.KLib, BinaryArtifacts.Wasm>() {
|
) : AbstractTestFacade<BinaryArtifacts.KLib, BinaryArtifacts.Wasm>() {
|
||||||
|
private val supportedOptimizer: WasmOptimizer = WasmOptimizer.Binaryen
|
||||||
|
|
||||||
override val inputKind = ArtifactKinds.KLib
|
override val inputKind = ArtifactKinds.KLib
|
||||||
override val outputKind = ArtifactKinds.Wasm
|
override val outputKind = ArtifactKinds.Wasm
|
||||||
@@ -126,13 +130,27 @@ class WasmBackendFacade(
|
|||||||
|
|
||||||
return BinaryArtifacts.Wasm(
|
return BinaryArtifacts.Wasm(
|
||||||
compilerResult,
|
compilerResult,
|
||||||
compilerResultWithDCE
|
compilerResultWithDCE,
|
||||||
|
runIf(WasmEnvironmentConfigurationDirectives.RUN_THIRD_PARTY_OPTIMIZER in testServices.moduleStructure.allDirectives) {
|
||||||
|
compilerResultWithDCE.runThirdPartyOptimizer()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun shouldRunAnalysis(module: TestModule): Boolean {
|
override fun shouldRunAnalysis(module: TestModule): Boolean {
|
||||||
return WasmEnvironmentConfigurator.isMainModule(module, testServices)
|
return WasmEnvironmentConfigurator.isMainModule(module, testServices)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun WasmCompilerResult.runThirdPartyOptimizer(): WasmCompilerResult {
|
||||||
|
val (newWasm, newWat) = supportedOptimizer.run(wasm, withText = wat != null)
|
||||||
|
return WasmCompilerResult(
|
||||||
|
wat = newWat,
|
||||||
|
jsUninstantiatedWrapper = jsUninstantiatedWrapper,
|
||||||
|
jsWrapper = jsWrapper,
|
||||||
|
sourceMap = null,
|
||||||
|
wasm = newWasm
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun extractTestPackage(testServices: TestServices): String? {
|
fun extractTestPackage(testServices: TestServices): String? {
|
||||||
|
|||||||
@@ -7,12 +7,9 @@ package org.jetbrains.kotlin.wasm.test.handlers
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.backend.wasm.WasmCompilerResult
|
import org.jetbrains.kotlin.backend.wasm.WasmCompilerResult
|
||||||
import org.jetbrains.kotlin.backend.wasm.writeCompilationResult
|
import org.jetbrains.kotlin.backend.wasm.writeCompilationResult
|
||||||
import org.jetbrains.kotlin.js.JavaScript
|
|
||||||
import org.jetbrains.kotlin.test.DebugMode
|
import org.jetbrains.kotlin.test.DebugMode
|
||||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||||
import org.jetbrains.kotlin.test.directives.WasmEnvironmentConfigurationDirectives
|
|
||||||
import org.jetbrains.kotlin.test.directives.WasmEnvironmentConfigurationDirectives.RUN_UNIT_TESTS
|
import org.jetbrains.kotlin.test.directives.WasmEnvironmentConfigurationDirectives.RUN_UNIT_TESTS
|
||||||
import org.jetbrains.kotlin.test.model.TestFile
|
|
||||||
import org.jetbrains.kotlin.test.services.TestServices
|
import org.jetbrains.kotlin.test.services.TestServices
|
||||||
import org.jetbrains.kotlin.test.services.moduleStructure
|
import org.jetbrains.kotlin.test.services.moduleStructure
|
||||||
import org.jetbrains.kotlin.wasm.test.tools.WasmVM
|
import org.jetbrains.kotlin.wasm.test.tools.WasmVM
|
||||||
@@ -21,6 +18,8 @@ import java.io.File
|
|||||||
class WasiBoxRunner(
|
class WasiBoxRunner(
|
||||||
testServices: TestServices
|
testServices: TestServices
|
||||||
) : AbstractWasmArtifactsCollector(testServices) {
|
) : AbstractWasmArtifactsCollector(testServices) {
|
||||||
|
private val vmsToCheck: List<WasmVM> = listOf(WasmVM.NodeJs)
|
||||||
|
|
||||||
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {
|
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {
|
||||||
if (!someAssertionWasFailed) {
|
if (!someAssertionWasFailed) {
|
||||||
runWasmCode()
|
runWasmCode()
|
||||||
@@ -66,6 +65,7 @@ class WasiBoxRunner(
|
|||||||
dir.mkdirs()
|
dir.mkdirs()
|
||||||
|
|
||||||
writeCompilationResult(res, dir, baseFileName)
|
writeCompilationResult(res, dir, baseFileName)
|
||||||
|
|
||||||
File(dir, "test.mjs").writeText(testWasi)
|
File(dir, "test.mjs").writeText(testWasi)
|
||||||
|
|
||||||
if (debugMode >= DebugMode.DEBUG) {
|
if (debugMode >= DebugMode.DEBUG) {
|
||||||
@@ -79,25 +79,27 @@ class WasiBoxRunner(
|
|||||||
val testFileText = originalFile.readText()
|
val testFileText = originalFile.readText()
|
||||||
val failsIn: List<String> = InTextDirectivesUtils.findListWithPrefixes(testFileText, "// WASM_FAILS_IN: ")
|
val failsIn: List<String> = InTextDirectivesUtils.findListWithPrefixes(testFileText, "// WASM_FAILS_IN: ")
|
||||||
|
|
||||||
val exception = WasmVM.NodeJs.runWithCathedExceptions(
|
val exceptions = vmsToCheck.mapNotNull { vm ->
|
||||||
debugMode = debugMode,
|
vm.runWithCathedExceptions(
|
||||||
disableExceptions = false,
|
debugMode = debugMode,
|
||||||
failsIn = failsIn,
|
disableExceptions = false,
|
||||||
entryMjs = "test.mjs",
|
failsIn = failsIn,
|
||||||
jsFilePaths = emptyList(),
|
entryMjs = "test.mjs",
|
||||||
workingDirectory = dir
|
jsFilePaths = emptyList(),
|
||||||
)
|
workingDirectory = dir
|
||||||
|
)
|
||||||
if (exception != null) {
|
|
||||||
throw exception
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode == "dce") {
|
processExceptions(exceptions)
|
||||||
checkExpectedOutputSize(debugMode, testFileText, dir)
|
|
||||||
|
when (mode) {
|
||||||
|
"dce" -> checkExpectedDceOutputSize(debugMode, testFileText, dir)
|
||||||
|
"optimized" -> checkExpectedOptimizedOutputSize(debugMode, testFileText, dir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writeToFilesAndRunTest("dev", artifacts.compilerResult)
|
writeToFilesAndRunTest("dev", artifacts.compilerResult)
|
||||||
writeToFilesAndRunTest("dce", artifacts.compilerResultWithDCE)
|
writeToFilesAndRunTest("dce", artifacts.compilerResultWithDCE)
|
||||||
|
artifacts.compilerResultWithOptimizer?.let { writeToFilesAndRunTest("optimized", it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,13 +7,10 @@ package org.jetbrains.kotlin.wasm.test.handlers
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.backend.wasm.WasmCompilerResult
|
import org.jetbrains.kotlin.backend.wasm.WasmCompilerResult
|
||||||
import org.jetbrains.kotlin.backend.wasm.writeCompilationResult
|
import org.jetbrains.kotlin.backend.wasm.writeCompilationResult
|
||||||
import org.jetbrains.kotlin.js.JavaScript
|
|
||||||
import org.jetbrains.kotlin.test.DebugMode
|
import org.jetbrains.kotlin.test.DebugMode
|
||||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||||
import org.jetbrains.kotlin.test.directives.WasmEnvironmentConfigurationDirectives
|
|
||||||
import org.jetbrains.kotlin.test.directives.WasmEnvironmentConfigurationDirectives.DISABLE_WASM_EXCEPTION_HANDLING
|
import org.jetbrains.kotlin.test.directives.WasmEnvironmentConfigurationDirectives.DISABLE_WASM_EXCEPTION_HANDLING
|
||||||
import org.jetbrains.kotlin.test.directives.WasmEnvironmentConfigurationDirectives.RUN_UNIT_TESTS
|
import org.jetbrains.kotlin.test.directives.WasmEnvironmentConfigurationDirectives.RUN_UNIT_TESTS
|
||||||
import org.jetbrains.kotlin.test.model.TestFile
|
|
||||||
import org.jetbrains.kotlin.test.services.TestServices
|
import org.jetbrains.kotlin.test.services.TestServices
|
||||||
import org.jetbrains.kotlin.test.services.moduleStructure
|
import org.jetbrains.kotlin.test.services.moduleStructure
|
||||||
import org.jetbrains.kotlin.wasm.test.tools.WasmVM
|
import org.jetbrains.kotlin.wasm.test.tools.WasmVM
|
||||||
@@ -22,6 +19,8 @@ import java.io.File
|
|||||||
class WasmBoxRunner(
|
class WasmBoxRunner(
|
||||||
testServices: TestServices
|
testServices: TestServices
|
||||||
) : AbstractWasmArtifactsCollector(testServices) {
|
) : AbstractWasmArtifactsCollector(testServices) {
|
||||||
|
private val vmsToCheck: List<WasmVM> = listOf(WasmVM.V8, WasmVM.SpiderMonkey)
|
||||||
|
|
||||||
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {
|
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {
|
||||||
if (!someAssertionWasFailed) {
|
if (!someAssertionWasFailed) {
|
||||||
runWasmCode()
|
runWasmCode()
|
||||||
@@ -71,6 +70,7 @@ class WasmBoxRunner(
|
|||||||
dir.mkdirs()
|
dir.mkdirs()
|
||||||
|
|
||||||
writeCompilationResult(res, dir, baseFileName)
|
writeCompilationResult(res, dir, baseFileName)
|
||||||
|
|
||||||
File(dir, "test.mjs").writeText(testJs)
|
File(dir, "test.mjs").writeText(testJs)
|
||||||
|
|
||||||
val (jsFilePaths) = collectedJsArtifacts.saveJsArtifacts(dir)
|
val (jsFilePaths) = collectedJsArtifacts.saveJsArtifacts(dir)
|
||||||
@@ -117,7 +117,7 @@ class WasmBoxRunner(
|
|||||||
|
|
||||||
val disableExceptions = DISABLE_WASM_EXCEPTION_HANDLING in testServices.moduleStructure.allDirectives
|
val disableExceptions = DISABLE_WASM_EXCEPTION_HANDLING in testServices.moduleStructure.allDirectives
|
||||||
|
|
||||||
val exceptions = listOf(WasmVM.V8, WasmVM.SpiderMonkey).mapNotNull { vm ->
|
val exceptions = vmsToCheck.mapNotNull { vm ->
|
||||||
vm.runWithCathedExceptions(
|
vm.runWithCathedExceptions(
|
||||||
debugMode = debugMode,
|
debugMode = debugMode,
|
||||||
disableExceptions = disableExceptions,
|
disableExceptions = disableExceptions,
|
||||||
@@ -130,13 +130,15 @@ class WasmBoxRunner(
|
|||||||
|
|
||||||
processExceptions(exceptions)
|
processExceptions(exceptions)
|
||||||
|
|
||||||
if (mode == "dce") {
|
when (mode) {
|
||||||
checkExpectedOutputSize(debugMode, testFileText, dir)
|
"dce" -> checkExpectedDceOutputSize(debugMode, testFileText, dir)
|
||||||
|
"optimized" -> checkExpectedOptimizedOutputSize(debugMode, testFileText, dir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writeToFilesAndRunTest("dev", artifacts.compilerResult)
|
writeToFilesAndRunTest("dev", artifacts.compilerResult)
|
||||||
writeToFilesAndRunTest("dce", artifacts.compilerResultWithDCE)
|
writeToFilesAndRunTest("dce", artifacts.compilerResultWithDCE)
|
||||||
|
artifacts.compilerResultWithOptimizer?.let { writeToFilesAndRunTest("optimized", it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,8 +171,8 @@ internal fun WasmVM.runWithCathedExceptions(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkExpectedOutputSize(debugMode: DebugMode, testFileContent: String, testDir: File) {
|
fun checkExpectedDceOutputSize(debugMode: DebugMode, testFileContent: String, testDir: File) {
|
||||||
val expectedSizes =
|
val expectedDceSizes =
|
||||||
InTextDirectivesUtils.findListWithPrefixes(testFileContent, "// WASM_DCE_EXPECTED_OUTPUT_SIZE: ")
|
InTextDirectivesUtils.findListWithPrefixes(testFileContent, "// WASM_DCE_EXPECTED_OUTPUT_SIZE: ")
|
||||||
.map {
|
.map {
|
||||||
val i = it.indexOf(' ')
|
val i = it.indexOf(' ')
|
||||||
@@ -178,10 +180,27 @@ fun checkExpectedOutputSize(debugMode: DebugMode, testFileContent: String, testD
|
|||||||
val size = it.substring(i + 1)
|
val size = it.substring(i + 1)
|
||||||
extension.trim().lowercase() to size.filter(Char::isDigit).toInt()
|
extension.trim().lowercase() to size.filter(Char::isDigit).toInt()
|
||||||
}
|
}
|
||||||
|
assertExpectedSizesMatchActual(debugMode, testDir, expectedDceSizes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkExpectedOptimizedOutputSize(debugMode: DebugMode, testFileContent: String, testDir: File) {
|
||||||
|
val expectedOptimizeSizes = InTextDirectivesUtils
|
||||||
|
.findListWithPrefixes(testFileContent, "// WASM_OPT_EXPECTED_OUTPUT_SIZE: ")
|
||||||
|
.lastOrNull()
|
||||||
|
?.filter(Char::isDigit)
|
||||||
|
?.toInt() ?: return
|
||||||
|
|
||||||
|
assertExpectedSizesMatchActual(debugMode, testDir, listOf("wasm" to expectedOptimizeSizes))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertExpectedSizesMatchActual(
|
||||||
|
debugMode: DebugMode,
|
||||||
|
testDir: File,
|
||||||
|
fileExtensionToItsExpectedSize: Iterable<Pair<String, Int>>
|
||||||
|
) {
|
||||||
val filesByExtension = testDir.listFiles()?.groupBy { it.extension }.orEmpty()
|
val filesByExtension = testDir.listFiles()?.groupBy { it.extension }.orEmpty()
|
||||||
|
|
||||||
val errors = expectedSizes.mapNotNull { (extension, expectedSize) ->
|
val errors = fileExtensionToItsExpectedSize.mapNotNull { (extension, expectedSize) ->
|
||||||
val totalSize = filesByExtension[extension].orEmpty().sumOf { it.length() }
|
val totalSize = filesByExtension[extension].orEmpty().sumOf { it.length() }
|
||||||
|
|
||||||
val thresholdPercent = 1
|
val thresholdPercent = 1
|
||||||
@@ -204,4 +223,4 @@ fun checkExpectedOutputSize(debugMode: DebugMode, testFileContent: String, testD
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (errors.isNotEmpty()) throw AssertionError(errors.joinToString("\n"))
|
if (errors.isNotEmpty()) throw AssertionError(errors.joinToString("\n"))
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2023 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.wasm.test.tools
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.platform.wasm.BinaryenConfig
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.test.fail
|
||||||
|
|
||||||
|
sealed interface WasmOptimizer {
|
||||||
|
fun run(wasmInput: ByteArray, withText: Boolean = false): OptimizationResult
|
||||||
|
|
||||||
|
data class OptimizationResult(val wasm: ByteArray, val wat: String?)
|
||||||
|
|
||||||
|
object Binaryen : WasmOptimizer {
|
||||||
|
private val binaryenPath = System.getProperty("binaryen.path")
|
||||||
|
|
||||||
|
override fun run(wasmInput: ByteArray, withText: Boolean): OptimizationResult {
|
||||||
|
val command = arrayOf(binaryenPath, *BinaryenConfig.binaryenArgs.toTypedArray())
|
||||||
|
return OptimizationResult(
|
||||||
|
exec(command, wasmInput),
|
||||||
|
runIf(withText) { exec(command + "-S", wasmInput).toString(Charsets.UTF_8) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun exec(command: Array<String>, input: ByteArray): ByteArray {
|
||||||
|
val timestamp = System.currentTimeMillis()
|
||||||
|
val savedInput = File
|
||||||
|
.createTempFile("binaryen_input_$timestamp", ".wasm")
|
||||||
|
.apply { writeBytes(input) }
|
||||||
|
val savedOutput = File
|
||||||
|
.createTempFile("binaryen_output_$timestamp", ".wasm")
|
||||||
|
|
||||||
|
val processBuilder = ProcessBuilder(*(command + listOf("-o", savedOutput.absolutePath, savedInput.absolutePath)))
|
||||||
|
.redirectErrorStream(true)
|
||||||
|
|
||||||
|
val exitValue = processBuilder.start().waitFor()
|
||||||
|
|
||||||
|
if (exitValue != 0) {
|
||||||
|
val commandString = command.joinToString(" ") { escapeShellArgument(it) }
|
||||||
|
fail("Command \"$commandString\" terminated with exit code $exitValue")
|
||||||
|
}
|
||||||
|
|
||||||
|
return savedOutput.readBytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -119,5 +119,5 @@ internal class ExternalTool(val path: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun escapeShellArgument(arg: String): String =
|
internal fun escapeShellArgument(arg: String): String =
|
||||||
"'${arg.replace("'", "'\\''")}'"
|
"'${arg.replace("'", "'\\''")}'"
|
||||||
|
|||||||
Reference in New Issue
Block a user