WASM: Implement wasm-native strings part 1

There are several changes here but they all required for at least one test to pass.
- Implemented String class and several utility functions using built-in CharArray
- Added new constant memory segment to hold string literals and required funcs to work with them
- Added very crude mostly incorrect rudimentary ability to pass strings back to javascript
This commit is contained in:
Igor Laevsky
2021-07-23 16:11:45 +03:00
committed by TeamCityServer
parent 9ccdffe8ad
commit 0f84525bdc
20 changed files with 229 additions and 84 deletions
@@ -0,0 +1,18 @@
/*
* Copyright 2010-2021 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 kotlin.wasm.internal
// This is called when exported function returns a string. It writes [i32 length, [i16 chars ...]] into a temporary raw memory area and
// returns pointer to the start of it.
// Note: currently there is a single temporary raw memory area so it's not possible to export more than one string at a time.
internal fun exportStringRet(src: String): Int {
val retAddr = unsafeGetScratchRawMemory(INT_SIZE_BYTES + src.length * CHAR_SIZE_BYTES)
wasm_i32_store(retAddr, src.length)
unsafeCharArrayToRawMemory(src.toCharArray(), retAddr + INT_SIZE_BYTES)
return retAddr
}
// See importStringToJs for the JS-side import for strings
@@ -5,10 +5,34 @@
package kotlin.wasm.internal
@WasmImport("runtime", "String_getLiteral")
internal fun stringLiteral(index: Int): String =
internal const val CHAR_SIZE_BYTES = 2
internal const val INT_SIZE_BYTES = 4
internal fun unsafeRawMemoryToChar(addr: Int) = wasm_i32_load16_u(addr).toChar()
internal fun unsafeRawMemoryToCharArray(startAddr: Int, length: Int): CharArray {
val ret = CharArray(length)
for (i in 0 until length) {
ret[i] = unsafeRawMemoryToChar(startAddr + i * CHAR_SIZE_BYTES)
}
return ret
}
// Returns a pointer into a temporary scratch segment in the raw wasm memory. Aligned by 4.
// Note: currently there is single such segment for a whole wasm module, so use with care.
@ExcludedFromCodegen
internal fun unsafeGetScratchRawMemory(sizeBytes: Int): Int =
implementedAsIntrinsic
// Assumes there is enough space at the destination, fails with wasm trap otherwise.
internal fun unsafeCharArrayToRawMemory(src: CharArray, dstAddr: Int) {
var curAddr = dstAddr
for (i in src) {
wasm_i32_store16(curAddr, i)
curAddr += CHAR_SIZE_BYTES
}
}
@WasmReinterpret
internal fun unsafeNotNull(x: Any?): Any =
implementedAsIntrinsic
@@ -308,4 +308,13 @@ public external fun wasm_i64_trunc_sat_f32_s(a: Float): Long
public external fun wasm_i64_trunc_sat_f64_s(a: Double): Long
@WasmOp(WasmOp.I32_LOAD)
public external fun wasm_i32_load(x: Int): Int
public external fun wasm_i32_load(x: Int): Int
@WasmOp(WasmOp.I32_LOAD16_U)
public external fun wasm_i32_load16_u(x: Int): Int
@WasmOp(WasmOp.I32_STORE)
public external fun wasm_i32_store(addr: Int, i: Int): Unit
@WasmOp(WasmOp.I32_STORE16)
public external fun wasm_i32_store16(addr: Int, c: Char): Unit
@@ -12,7 +12,3 @@ fun wasm_i32_compareTo(x: Int, y: Int): Int =
fun wasm_i64_compareTo(x: Long, y: Long): Int =
wasm_i64_ge_s(x, y).toInt() - wasm_i64_le_s(x, y).toInt()
@WasmImport("runtime", "String_equals")
fun wasm_string_eq(x: String, y: String): Boolean =
implementedAsIntrinsic