Skip to content

Commit 403ae95

Browse files
authored
BridgeJS: Optimize numeric array transfer with bulk TypedArray copy (#745)
1 parent fc672e7 commit 403ae95

55 files changed

Lines changed: 1183 additions & 361 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ public struct BridgeJSLink {
347347
"let \(JSGlueVariableScope.reservedF32Stack) = [];",
348348
"let \(JSGlueVariableScope.reservedF64Stack) = [];",
349349
"let \(JSGlueVariableScope.reservedPointerStack) = [];",
350+
"let \(JSGlueVariableScope.reservedTaStack) = [];",
350351
"const \(JSGlueVariableScope.reservedEnumHelpers) = {};",
351352
"const \(JSGlueVariableScope.reservedStructHelpers) = {};",
352353
"",
@@ -489,6 +490,21 @@ public struct BridgeJSLink {
489490
printer.write("return \(JSGlueVariableScope.reservedI64Stack).pop();")
490491
}
491492
printer.write("}")
493+
// Typed array constructors indexed by kind (must match _BridgedNumericArrayKind)
494+
printer.write(
495+
"const taCtors = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array];"
496+
)
497+
printer.write("bjs[\"swift_js_push_typed_array\"] = function(kind, ptr, count) {")
498+
printer.indent {
499+
printer.write("const Ctor = taCtors[kind];")
500+
printer.write("const byteLen = count * Ctor.BYTES_PER_ELEMENT;")
501+
// slice() copies the bytes into a new ArrayBuffer that is properly aligned
502+
printer.write(
503+
"const copy = \(JSGlueVariableScope.reservedMemory).buffer.slice(ptr, ptr + byteLen);"
504+
)
505+
printer.write("\(JSGlueVariableScope.reservedTaStack).push(Array.from(new Ctor(copy)));")
506+
}
507+
printer.write("}")
492508
if !allStructs.isEmpty {
493509
for structDef in allStructs {
494510
printer.write("bjs[\"swift_js_struct_lower_\(structDef.abiName)\"] = function(objectId) {")

Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ final class JSGlueVariableScope {
3434
static let reservedStructHelpers = "structHelpers"
3535
static let reservedSwiftClosureRegistry = "swiftClosureRegistry"
3636
static let reservedMakeSwiftClosure = "makeClosure"
37+
static let reservedTaStack = "taStack"
3738

3839
private let intrinsicRegistry: JSIntrinsicRegistry
3940

@@ -63,6 +64,7 @@ final class JSGlueVariableScope {
6364
reservedStructHelpers,
6465
reservedSwiftClosureRegistry,
6566
reservedMakeSwiftClosure,
67+
reservedTaStack,
6668
]
6769

6870
init(intrinsicRegistry: JSIntrinsicRegistry) {
@@ -1896,20 +1898,31 @@ struct IntrinsicJSFragment: Sendable {
18961898
let (scope, printer) = (context.scope, context.printer)
18971899
let resultVar = scope.variable("arrayResult")
18981900
let lenVar = scope.variable("arrayLen")
1899-
let iVar = scope.variable("i")
19001901

19011902
printer.write("const \(lenVar) = \(scope.popI32());")
1902-
printer.write("const \(resultVar) = [];")
1903-
printer.write("for (let \(iVar) = 0; \(iVar) < \(lenVar); \(iVar)++) {")
1903+
printer.write("let \(resultVar);")
1904+
printer.write("if (\(lenVar) === -1) {")
1905+
printer.indent {
1906+
// Bulk path: Swift pushed a typed array onto the typed-array stack
1907+
printer.write("\(resultVar) = \(JSGlueVariableScope.reservedTaStack).pop();")
1908+
}
1909+
printer.write("} else {")
19041910
try printer.indent {
1905-
let elementFragment = try stackLiftFragment(elementType: elementType)
1906-
let elementResults = try elementFragment.printCode([], context)
1907-
if let elementExpr = elementResults.first {
1908-
printer.write("\(resultVar).push(\(elementExpr));")
1911+
// Element-by-element path (original behavior)
1912+
let iVar = scope.variable("i")
1913+
printer.write("\(resultVar) = [];")
1914+
printer.write("for (let \(iVar) = 0; \(iVar) < \(lenVar); \(iVar)++) {")
1915+
try printer.indent {
1916+
let elementFragment = try stackLiftFragment(elementType: elementType)
1917+
let elementResults = try elementFragment.printCode([], context)
1918+
if let elementExpr = elementResults.first {
1919+
printer.write("\(resultVar).push(\(elementExpr));")
1920+
}
19091921
}
1922+
printer.write("}")
1923+
printer.write("\(resultVar).reverse();")
19101924
}
19111925
printer.write("}")
1912-
printer.write("\(resultVar).reverse();")
19131926
return [resultVar]
19141927
}
19151928
)

0 commit comments

Comments
 (0)