[WASM] Fast load string literals from data section
This commit is contained in:
@@ -168,6 +168,7 @@ class WasmSymbols(
|
|||||||
val refTest = getInternalFunction("wasm_ref_test")
|
val refTest = getInternalFunction("wasm_ref_test")
|
||||||
val refCast = getInternalFunction("wasm_ref_cast")
|
val refCast = getInternalFunction("wasm_ref_cast")
|
||||||
val wasmArrayCopy = getInternalFunction("wasm_array_copy")
|
val wasmArrayCopy = getInternalFunction("wasm_array_copy")
|
||||||
|
val wasmArrayNewData0 = getInternalFunction("array_new_data0")
|
||||||
|
|
||||||
val intToLong = getInternalFunction("wasm_i64_extend_i32_s")
|
val intToLong = getInternalFunction("wasm_i64_extend_i32_s")
|
||||||
|
|
||||||
|
|||||||
+7
@@ -491,6 +491,13 @@ class BodyGenerator(
|
|||||||
body.buildInstr(WasmOp.ARRAY_COPY, immediate, immediate)
|
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 -> {
|
wasmSymbols.stringGetPoolSize -> {
|
||||||
body.buildConstI32Symbol(context.stringPoolSize)
|
body.buildConstI32Symbol(context.stringPoolSize)
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -492,7 +492,6 @@ fun generateConstExpression(expression: IrConst<*>, body: WasmExpressionBuilder,
|
|||||||
body.buildConstI32Symbol(literalPoolId)
|
body.buildConstI32Symbol(literalPoolId)
|
||||||
body.buildConstI32Symbol(literalAddress)
|
body.buildConstI32Symbol(literalAddress)
|
||||||
body.buildConstI32(stringValue.length)
|
body.buildConstI32(stringValue.length)
|
||||||
body.buildConstI32(stringValue.hashCode())
|
|
||||||
body.buildCall(context.referenceFunction(context.backendContext.wasmSymbols.stringGetLiteral))
|
body.buildCall(context.referenceFunction(context.backendContext.wasmSymbols.stringGetLiteral))
|
||||||
}
|
}
|
||||||
else -> error("Unknown constant kind")
|
else -> error("Unknown constant kind")
|
||||||
|
|||||||
+21
-17
@@ -124,19 +124,6 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
|
|||||||
currentDataSectionAddress += typeInfoElement.sizeInBytes
|
currentDataSectionAddress += typeInfoElement.sizeInBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
val stringDataSectionStart = currentDataSectionAddress
|
|
||||||
val stringDataSectionBytes = mutableListOf<Byte>()
|
|
||||||
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'
|
// Reserve some memory to pass complex exported types (like strings). It's going to be accessible through 'unsafeGetScratchRawMemory'
|
||||||
// runtime call from stdlib.
|
// runtime call from stdlib.
|
||||||
currentDataSectionAddress = alignUp(currentDataSectionAddress, INT_SIZE_BYTES)
|
currentDataSectionAddress = alignUp(currentDataSectionAddress, INT_SIZE_BYTES)
|
||||||
@@ -146,8 +133,22 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
|
|||||||
bind(classIds.unbound, klassIds)
|
bind(classIds.unbound, klassIds)
|
||||||
interfaceId.unbound.onEachIndexed { index, entry -> entry.value.bind(index) }
|
interfaceId.unbound.onEachIndexed { index, entry -> entry.value.bind(index) }
|
||||||
|
|
||||||
val data = typeInfo.buildData(address = { klassIds.getValue(it) }) +
|
val stringDataSectionBytes = mutableListOf<Byte>()
|
||||||
WasmData(WasmDataMode.Active(0, stringDataSectionStart), stringDataSectionBytes.toByteArray())
|
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<WasmData>()
|
||||||
|
data.add(WasmData(WasmDataMode.Passive, stringDataSectionBytes.toByteArray()))
|
||||||
|
typeInfo.buildData(data, address = { klassIds.getValue(it) })
|
||||||
|
|
||||||
val masterInitFunctionType = WasmFunctionType(emptyList(), emptyList())
|
val masterInitFunctionType = WasmFunctionType(emptyList(), emptyList())
|
||||||
val masterInitFunction = WasmFunction.Defined("__init", WasmSymbol(masterInitFunctionType))
|
val masterInitFunction = WasmFunction.Defined("__init", WasmSymbol(masterInitFunctionType))
|
||||||
@@ -227,8 +228,11 @@ private fun irSymbolDebugDump(symbol: Any?): String =
|
|||||||
else -> symbol.toString()
|
else -> symbol.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun WasmCompiledModuleFragment.ReferencableAndDefinable<IrClassSymbol, ConstantDataElement>.buildData(address: (IrClassSymbol) -> Int): List<WasmData> {
|
inline fun WasmCompiledModuleFragment.ReferencableAndDefinable<IrClassSymbol, ConstantDataElement>.buildData(
|
||||||
return elements.map {
|
into: MutableList<WasmData>,
|
||||||
|
address: (IrClassSymbol) -> Int
|
||||||
|
) {
|
||||||
|
elements.mapTo(into) {
|
||||||
val id = address(wasmToIr.getValue(it))
|
val id = address(wasmToIr.getValue(it))
|
||||||
val offset = mutableListOf<WasmInstr>()
|
val offset = mutableListOf<WasmInstr>()
|
||||||
WasmIrExpressionBuilder(offset).buildConstI32(id)
|
WasmIrExpressionBuilder(offset).buildConstI32(id)
|
||||||
|
|||||||
@@ -130,15 +130,14 @@ public class String internal @WasmPrimitiveConstructor constructor(
|
|||||||
internal inline fun WasmCharArray.createString(): String =
|
internal inline fun WasmCharArray.createString(): String =
|
||||||
String(null, this.len(), this)
|
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]
|
val cached = stringPool[poolId]
|
||||||
if (cached !== null) {
|
if (cached !== null) {
|
||||||
return cached
|
return cached
|
||||||
}
|
}
|
||||||
val chars = WasmCharArray(length)
|
|
||||||
unsafeRawMemoryToWasmCharArray(startAddress, 0, length, chars)
|
val chars = array_new_data0<WasmCharArray>(startAddress, length)
|
||||||
val newString = String(null, length, chars)
|
val newString = String(null, length, chars)
|
||||||
newString._hashCode = hashCode
|
|
||||||
stringPool[poolId] = newString
|
stringPool[poolId] = newString
|
||||||
return newString
|
return newString
|
||||||
}
|
}
|
||||||
@@ -28,8 +28,8 @@ internal fun getTypeInfoTypeDataByPtr(typeInfoPtr: Int): TypeInfoData {
|
|||||||
val simpleNameLength = wasm_i32_load(typeInfoPtr + TYPE_INFO_TYPE_SIMPLE_NAME_LENGTH_OFFSET)
|
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 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 simpleNamePtr = wasm_i32_load(typeInfoPtr + TYPE_INFO_TYPE_SIMPLE_NAME_PRT_OFFSET)
|
||||||
val packageName = stringLiteral(fqNameId, fqNamePtr, fqNameLength, 0)
|
val packageName = stringLiteral(fqNameId, fqNamePtr, fqNameLength)
|
||||||
val simpleName = stringLiteral(simpleNameId, simpleNamePtr, simpleNameLength, 0)
|
val simpleName = stringLiteral(simpleNameId, simpleNamePtr, simpleNameLength)
|
||||||
return TypeInfoData(typeInfoPtr, isInterface = false, packageName, simpleName)
|
return TypeInfoData(typeInfoPtr, isInterface = false, packageName, simpleName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ internal fun <To> wasm_ref_test(a: Any?): Boolean =
|
|||||||
internal fun <T> wasm_array_copy(destination: T, destinationIndex: Int, source: T, sourceIndex: Int, length: Int): Unit =
|
internal fun <T> wasm_array_copy(destination: T, destinationIndex: Int, source: T, sourceIndex: Int, length: Int): Unit =
|
||||||
implementedAsIntrinsic
|
implementedAsIntrinsic
|
||||||
|
|
||||||
|
internal fun <T> array_new_data0(address: Int, length: Int): WasmCharArray =
|
||||||
|
implementedAsIntrinsic
|
||||||
|
|
||||||
@WasmOp(WasmOp.I32_EQ)
|
@WasmOp(WasmOp.I32_EQ)
|
||||||
public external fun wasm_i32_eq(a: Int, b: Int): Boolean
|
public external fun wasm_i32_eq(a: Int, b: Int): Boolean
|
||||||
|
|
||||||
|
|||||||
@@ -355,6 +355,7 @@ enum class WasmOp(
|
|||||||
ARRAY_SET("array.set", 0xFB_16, listOf(STRUCT_TYPE_IDX)),
|
ARRAY_SET("array.set", 0xFB_16, listOf(STRUCT_TYPE_IDX)),
|
||||||
ARRAY_LEN("array.len", 0xFB_17, 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_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_NEW("i31.new", 0xFB_20),
|
||||||
I31_GET_S("i31.get_s", 0xFB_21),
|
I31_GET_S("i31.get_s", 0xFB_21),
|
||||||
|
|||||||
@@ -114,6 +114,10 @@ class WasmIrToBinary(outputStream: OutputStream, val module: WasmModule, val mod
|
|||||||
data.forEach { appendData(it) }
|
data.forEach { appendData(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
appendSection(12u) {
|
||||||
|
b.writeVarUInt32(data.size)
|
||||||
|
}
|
||||||
|
|
||||||
//text section (should be placed after data)
|
//text section (should be placed after data)
|
||||||
if (emitNameSection) {
|
if (emitNameSection) {
|
||||||
appendTextSection(definedFunctions)
|
appendTextSection(definedFunctions)
|
||||||
|
|||||||
Reference in New Issue
Block a user