From 0e16889f45a875c53934a335484b4a4c3329783b Mon Sep 17 00:00:00 2001 From: Igor Yakovlev Date: Fri, 14 Oct 2022 00:03:51 +0200 Subject: [PATCH] [WASM] Fast load string literals from data section --- .../kotlin/backend/wasm/WasmSymbols.kt | 1 + .../backend/wasm/ir2wasm/BodyGenerator.kt | 7 ++++ .../wasm/ir2wasm/DeclarationGenerator.kt | 1 - .../ir2wasm/WasmCompiledModuleFragment.kt | 38 ++++++++++--------- .../stdlib/wasm/builtins/kotlin/String.kt | 7 ++-- .../internal/kotlin/wasm/internal/TypeInfo.kt | 4 +- .../kotlin/wasm/internal/WasmInstructions.kt | 3 ++ .../org/jetbrains/kotlin/wasm/ir/Operators.kt | 1 + .../wasm/ir/convertors/WasmIrToBinary.kt | 4 ++ 9 files changed, 42 insertions(+), 24 deletions(-) diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt index 3ecfe4feb18..8aae753bf8a 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt @@ -168,6 +168,7 @@ class WasmSymbols( val refTest = getInternalFunction("wasm_ref_test") val refCast = getInternalFunction("wasm_ref_cast") val wasmArrayCopy = getInternalFunction("wasm_array_copy") + val wasmArrayNewData0 = getInternalFunction("array_new_data0") val intToLong = getInternalFunction("wasm_i64_extend_i32_s") diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt index b288ec11a4d..f1b65f27e82 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt @@ -491,6 +491,13 @@ class BodyGenerator( body.buildInstr(WasmOp.ARRAY_COPY, immediate, immediate) } + wasmSymbols.wasmArrayNewData0 -> { + val immediate = WasmImmediate.GcType( + context.referenceGcType(call.getTypeArgument(0)!!.getRuntimeClass(irBuiltIns).symbol) + ) + body.buildInstr(WasmOp.ARRAY_NEW_DATA, immediate, WasmImmediate.DataIdx(0)) + } + wasmSymbols.stringGetPoolSize -> { body.buildConstI32Symbol(context.stringPoolSize) } diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt index 00fae05abef..15e36294e5d 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt @@ -492,7 +492,6 @@ fun generateConstExpression(expression: IrConst<*>, body: WasmExpressionBuilder, body.buildConstI32Symbol(literalPoolId) body.buildConstI32Symbol(literalAddress) body.buildConstI32(stringValue.length) - body.buildConstI32(stringValue.hashCode()) body.buildCall(context.referenceFunction(context.backendContext.wasmSymbols.stringGetLiteral)) } else -> error("Unknown constant kind") diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt index 3fa04ab519f..6fbae86f294 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt @@ -124,19 +124,6 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) { currentDataSectionAddress += typeInfoElement.sizeInBytes } - val stringDataSectionStart = currentDataSectionAddress - val stringDataSectionBytes = mutableListOf() - var stringLiteralCount = 0 - for ((string, symbol) in stringLiteralAddress.unbound) { - val constData = ConstantDataCharArray("string_literal", string.toCharArray()) - stringDataSectionBytes += constData.toBytes().toList() - symbol.bind(currentDataSectionAddress) - stringLiteralPoolId.reference(string).bind(stringLiteralCount) - currentDataSectionAddress += constData.sizeInBytes - stringLiteralCount++ - } - stringPoolSize.bind(stringLiteralCount) - // Reserve some memory to pass complex exported types (like strings). It's going to be accessible through 'unsafeGetScratchRawMemory' // runtime call from stdlib. currentDataSectionAddress = alignUp(currentDataSectionAddress, INT_SIZE_BYTES) @@ -146,8 +133,22 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) { bind(classIds.unbound, klassIds) interfaceId.unbound.onEachIndexed { index, entry -> entry.value.bind(index) } - val data = typeInfo.buildData(address = { klassIds.getValue(it) }) + - WasmData(WasmDataMode.Active(0, stringDataSectionStart), stringDataSectionBytes.toByteArray()) + val stringDataSectionBytes = mutableListOf() + var stringDataSectionStart = 0 + var stringLiteralCount = 0 + for ((string, symbol) in stringLiteralAddress.unbound) { + symbol.bind(stringDataSectionStart) + stringLiteralPoolId.reference(string).bind(stringLiteralCount) + val constData = ConstantDataCharArray("string_literal", string.toCharArray()) + stringDataSectionBytes += constData.toBytes().toList() + stringDataSectionStart += constData.sizeInBytes + stringLiteralCount++ + } + stringPoolSize.bind(stringLiteralCount) + + val data = mutableListOf() + data.add(WasmData(WasmDataMode.Passive, stringDataSectionBytes.toByteArray())) + typeInfo.buildData(data, address = { klassIds.getValue(it) }) val masterInitFunctionType = WasmFunctionType(emptyList(), emptyList()) val masterInitFunction = WasmFunction.Defined("__init", WasmSymbol(masterInitFunctionType)) @@ -227,8 +228,11 @@ private fun irSymbolDebugDump(symbol: Any?): String = else -> symbol.toString() } -inline fun WasmCompiledModuleFragment.ReferencableAndDefinable.buildData(address: (IrClassSymbol) -> Int): List { - return elements.map { +inline fun WasmCompiledModuleFragment.ReferencableAndDefinable.buildData( + into: MutableList, + address: (IrClassSymbol) -> Int +) { + elements.mapTo(into) { val id = address(wasmToIr.getValue(it)) val offset = mutableListOf() WasmIrExpressionBuilder(offset).buildConstI32(id) diff --git a/libraries/stdlib/wasm/builtins/kotlin/String.kt b/libraries/stdlib/wasm/builtins/kotlin/String.kt index 540e63539db..abded878e49 100644 --- a/libraries/stdlib/wasm/builtins/kotlin/String.kt +++ b/libraries/stdlib/wasm/builtins/kotlin/String.kt @@ -130,15 +130,14 @@ public class String internal @WasmPrimitiveConstructor constructor( internal inline fun WasmCharArray.createString(): String = String(null, this.len(), this) -internal fun stringLiteral(poolId: Int, startAddress: Int, length: Int, hashCode: Int): String { +internal fun stringLiteral(poolId: Int, startAddress: Int, length: Int): String { val cached = stringPool[poolId] if (cached !== null) { return cached } - val chars = WasmCharArray(length) - unsafeRawMemoryToWasmCharArray(startAddress, 0, length, chars) + + val chars = array_new_data0(startAddress, length) val newString = String(null, length, chars) - newString._hashCode = hashCode stringPool[poolId] = newString return newString } \ No newline at end of file diff --git a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/TypeInfo.kt b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/TypeInfo.kt index 848cf82c0c2..91174fa3e6d 100644 --- a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/TypeInfo.kt +++ b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/TypeInfo.kt @@ -28,8 +28,8 @@ internal fun getTypeInfoTypeDataByPtr(typeInfoPtr: Int): TypeInfoData { val simpleNameLength = wasm_i32_load(typeInfoPtr + TYPE_INFO_TYPE_SIMPLE_NAME_LENGTH_OFFSET) val simpleNameId = wasm_i32_load(typeInfoPtr + TYPE_INFO_TYPE_SIMPLE_NAME_ID_OFFSET) val simpleNamePtr = wasm_i32_load(typeInfoPtr + TYPE_INFO_TYPE_SIMPLE_NAME_PRT_OFFSET) - val packageName = stringLiteral(fqNameId, fqNamePtr, fqNameLength, 0) - val simpleName = stringLiteral(simpleNameId, simpleNamePtr, simpleNameLength, 0) + val packageName = stringLiteral(fqNameId, fqNamePtr, fqNameLength) + val simpleName = stringLiteral(simpleNameId, simpleNamePtr, simpleNameLength) return TypeInfoData(typeInfoPtr, isInterface = false, packageName, simpleName) } diff --git a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt index 1785bb493f5..f865ba7493a 100644 --- a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt +++ b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt @@ -28,6 +28,9 @@ internal fun wasm_ref_test(a: Any?): Boolean = internal fun wasm_array_copy(destination: T, destinationIndex: Int, source: T, sourceIndex: Int, length: Int): Unit = implementedAsIntrinsic +internal fun array_new_data0(address: Int, length: Int): WasmCharArray = + implementedAsIntrinsic + @WasmOp(WasmOp.I32_EQ) public external fun wasm_i32_eq(a: Int, b: Int): Boolean diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt index 5a491106fa6..5b5aedfe95e 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt @@ -355,6 +355,7 @@ enum class WasmOp( ARRAY_SET("array.set", 0xFB_16, listOf(STRUCT_TYPE_IDX)), ARRAY_LEN("array.len", 0xFB_17, listOf(STRUCT_TYPE_IDX)), ARRAY_COPY("array.copy", 0xFB_18, listOf(STRUCT_TYPE_IDX, STRUCT_TYPE_IDX)), + ARRAY_NEW_DATA("array.new_data", 0xFB_1D, listOf(STRUCT_TYPE_IDX, DATA_IDX)), I31_NEW("i31.new", 0xFB_20), I31_GET_S("i31.get_s", 0xFB_21), diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt index 7fb741929d3..663fc58fd57 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt @@ -114,6 +114,10 @@ class WasmIrToBinary(outputStream: OutputStream, val module: WasmModule, val mod data.forEach { appendData(it) } } + appendSection(12u) { + b.writeVarUInt32(data.size) + } + //text section (should be placed after data) if (emitNameSection) { appendTextSection(definedFunctions)