Skip to content

Commit 3369b55

Browse files
committed
Wasm: specialization of bigarray accesses
1 parent 8eaab2c commit 3369b55

File tree

7 files changed

+646
-19
lines changed

7 files changed

+646
-19
lines changed

compiler/lib-wasm/code_generation.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ module Arith = struct
373373
(match e, e' with
374374
| W.Const (I32 n), W.Const (I32 n') when Int32.(n' < 31l) ->
375375
W.Const (I32 (Int32.shift_left n (Int32.to_int n')))
376+
| _, W.Const (I32 0l) -> e
376377
| _ -> W.BinOp (I32 Shl, e, e'))
377378

378379
let ( lsr ) = binary (Shr U)

compiler/lib-wasm/gc_target.ml

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,38 @@ module Type = struct
430430
}
431431
])
432432
})
433+
434+
let int_array_type =
435+
register_type "int_array" (fun () ->
436+
return
437+
{ supertype = None
438+
; final = true
439+
; typ = W.Array { mut = true; typ = Value I32 }
440+
})
441+
442+
let bigarray_type =
443+
register_type "bigarray" (fun () ->
444+
let* custom_operations = custom_operations_type in
445+
let* int_array = int_array_type in
446+
let* custom = custom_type in
447+
return
448+
{ supertype = Some custom
449+
; final = true
450+
; typ =
451+
W.Struct
452+
[ { mut = false
453+
; typ = Value (Ref { nullable = false; typ = Type custom_operations })
454+
}
455+
; { mut = true; typ = Value (Ref { nullable = false; typ = Extern }) }
456+
; { mut = true; typ = Value (Ref { nullable = false; typ = Extern }) }
457+
; { mut = false
458+
; typ = Value (Ref { nullable = false; typ = Type int_array })
459+
}
460+
; { mut = false; typ = Packed I8 }
461+
; { mut = false; typ = Packed I8 }
462+
; { mut = false; typ = Packed I8 }
463+
]
464+
})
433465
end
434466

435467
module Value = struct
@@ -1373,6 +1405,235 @@ module Math = struct
13731405
let exp2 x = power (return (W.Const (F64 2.))) x
13741406
end
13751407

1408+
module Bigarray = struct
1409+
let dimension n a =
1410+
let* ty = Type.bigarray_type in
1411+
Memory.wasm_array_get
1412+
~ty:Type.int_array_type
1413+
(Memory.wasm_struct_get ty (Memory.wasm_cast ty a) 3)
1414+
(Arith.const (Int32.of_int n))
1415+
1416+
let get_at_offset ~(kind : Typing.Bigarray.kind) a i =
1417+
let name, (typ : Wasm_ast.value_type), size, box =
1418+
match kind with
1419+
| Float32 ->
1420+
( "dv_get_f32"
1421+
, F32
1422+
, 2
1423+
, fun x ->
1424+
let* x = x in
1425+
return (W.F64PromoteF32 x) )
1426+
| Float64 -> "dv_get_f64", F64, 3, Fun.id
1427+
| Int8_signed -> "dv_get_i8", I32, 0, Fun.id
1428+
| Int8_unsigned -> "dv_get_ui8", I32, 0, Fun.id
1429+
| Int16_signed -> "dv_get_i16", I32, 1, Fun.id
1430+
| Int16_unsigned -> "dv_get_ui16", I32, 1, Fun.id
1431+
| Int32 -> "dv_get_i32", I32, 2, Fun.id
1432+
| Nativeint -> "dv_get_i32", I32, 2, Fun.id
1433+
| Int64 -> "dv_get_i64", I64, 3, Fun.id
1434+
| Int -> "dv_get_i32", I32, 2, Fun.id
1435+
| Float16 ->
1436+
( "dv_get_i16"
1437+
, I32
1438+
, 1
1439+
, fun x ->
1440+
let* conv =
1441+
register_import
1442+
~name:"caml_float16_to_double"
1443+
(Fun { W.params = [ I32 ]; result = [ F64 ] })
1444+
in
1445+
let* x = x in
1446+
return (W.Call (conv, [ x ])) )
1447+
| Complex32 ->
1448+
( "dv_get_f32"
1449+
, F32
1450+
, 3
1451+
, fun x ->
1452+
let* x = x in
1453+
return (W.F64PromoteF32 x) )
1454+
| Complex64 -> "dv_get_f64", F64, 4, Fun.id
1455+
in
1456+
let* little_endian =
1457+
register_import
1458+
~import_module:"bindings"
1459+
~name:"littleEndian"
1460+
(Global { mut = false; typ = I32 })
1461+
in
1462+
let* f =
1463+
register_import
1464+
~import_module:"bindings"
1465+
~name
1466+
(Fun
1467+
{ W.params =
1468+
Ref { nullable = true; typ = Extern }
1469+
:: I32
1470+
:: (if size = 0 then [] else [ I32 ])
1471+
; result = [ typ ]
1472+
})
1473+
in
1474+
let* ty = Type.bigarray_type in
1475+
let* ta = Memory.wasm_struct_get ty (Memory.wasm_cast ty a) 2 in
1476+
let* ofs = Arith.(i lsl const (Int32.of_int size)) in
1477+
match kind with
1478+
| Float32
1479+
| Float64
1480+
| Int8_signed
1481+
| Int8_unsigned
1482+
| Int16_signed
1483+
| Int16_unsigned
1484+
| Int32
1485+
| Int64
1486+
| Int
1487+
| Nativeint
1488+
| Float16 ->
1489+
box
1490+
(return
1491+
(W.Call
1492+
(f, ta :: ofs :: (if size = 0 then [] else [ W.GlobalGet little_endian ]))))
1493+
| Complex32 | Complex64 ->
1494+
let delta = Int32.shift_left 1l (size - 1) in
1495+
let* ofs' = Arith.(return ofs + const delta) in
1496+
let* x = box (return (W.Call (f, [ ta; ofs; W.GlobalGet little_endian ]))) in
1497+
let* y = box (return (W.Call (f, [ ta; ofs'; W.GlobalGet little_endian ]))) in
1498+
let* ty = Type.float_array_type in
1499+
return (W.ArrayNewFixed (ty, [ x; y ]))
1500+
1501+
let set_at_offset ~kind a i v =
1502+
let name, (typ : Wasm_ast.value_type), size, unbox =
1503+
match (kind : Typing.Bigarray.kind) with
1504+
| Float32 ->
1505+
( "dv_set_f32"
1506+
, F32
1507+
, 2
1508+
, fun x ->
1509+
let* x = x in
1510+
return (W.F32DemoteF64 x) )
1511+
| Float64 -> "dv_set_f64", F64, 3, Fun.id
1512+
| Int8_signed | Int8_unsigned -> "dv_set_i8", I32, 0, Fun.id
1513+
| Int16_signed | Int16_unsigned -> "dv_set_i16", I32, 1, Fun.id
1514+
| Int32 -> "dv_set_i32", I32, 2, Fun.id
1515+
| Nativeint -> "dv_set_i32", I32, 2, Fun.id
1516+
| Int64 -> "dv_set_i64", I64, 3, Fun.id
1517+
| Int -> "dv_set_i32", I32, 2, Fun.id
1518+
| Float16 ->
1519+
( "dv_set_i16"
1520+
, I32
1521+
, 1
1522+
, fun x ->
1523+
let* conv =
1524+
register_import
1525+
~name:"caml_double_to_float16"
1526+
(Fun { W.params = [ F64 ]; result = [ I32 ] })
1527+
in
1528+
let* x = Fun.id x in
1529+
return (W.Call (conv, [ x ])) )
1530+
| Complex32 ->
1531+
( "dv_set_f32"
1532+
, F32
1533+
, 3
1534+
, fun x ->
1535+
let* x = x in
1536+
return (W.F32DemoteF64 x) )
1537+
| Complex64 -> "dv_set_f64", F64, 4, Fun.id
1538+
in
1539+
let* ty = Type.bigarray_type in
1540+
let* ta = Memory.wasm_struct_get ty (Memory.wasm_cast ty a) 2 in
1541+
let* ofs = Arith.(i lsl const (Int32.of_int size)) in
1542+
let* little_endian =
1543+
register_import
1544+
~import_module:"bindings"
1545+
~name:"littleEndian"
1546+
(Global { mut = false; typ = I32 })
1547+
in
1548+
let* f =
1549+
register_import
1550+
~import_module:"bindings"
1551+
~name
1552+
(Fun
1553+
{ W.params =
1554+
Ref { nullable = true; typ = Extern }
1555+
:: I32
1556+
:: typ
1557+
:: (if size = 0 then [] else [ I32 ])
1558+
; result = []
1559+
})
1560+
in
1561+
match kind with
1562+
| Float32
1563+
| Float64
1564+
| Int8_signed
1565+
| Int8_unsigned
1566+
| Int16_signed
1567+
| Int16_unsigned
1568+
| Int32
1569+
| Int64
1570+
| Int
1571+
| Nativeint
1572+
| Float16 ->
1573+
let* v = unbox v in
1574+
instr
1575+
(W.CallInstr
1576+
( f
1577+
, ta :: ofs :: v :: (if size = 0 then [] else [ W.GlobalGet little_endian ])
1578+
))
1579+
| Complex32 | Complex64 ->
1580+
let delta = Int32.shift_left 1l (size - 1) in
1581+
let* ofs' = Arith.(return ofs + const delta) in
1582+
let ty = Type.float_array_type in
1583+
let* x = unbox (Memory.wasm_array_get ~ty v (Arith.const 0l)) in
1584+
let* () = instr (W.CallInstr (f, [ ta; ofs; x; W.GlobalGet little_endian ])) in
1585+
let* y = unbox (Memory.wasm_array_get ~ty v (Arith.const 1l)) in
1586+
instr (W.CallInstr (f, [ ta; ofs'; y; W.GlobalGet little_endian ]))
1587+
1588+
let offset ~bound_error_index ~(layout : Typing.Bigarray.layout) ta ~indices =
1589+
let l =
1590+
List.mapi
1591+
~f:(fun pos i ->
1592+
let i =
1593+
match layout with
1594+
| C -> i
1595+
| Fortran -> Arith.(i - const 1l)
1596+
in
1597+
let i' = Code.Var.fresh () in
1598+
let dim = Code.Var.fresh () in
1599+
( (let* () = store ~typ:I32 i' i in
1600+
let* () = store ~typ:I32 dim (dimension pos ta) in
1601+
let* cond = Arith.uge (load i') (load dim) in
1602+
instr (W.Br_if (bound_error_index, cond)))
1603+
, i'
1604+
, dim ))
1605+
indices
1606+
in
1607+
let l =
1608+
match layout with
1609+
| C -> l
1610+
| Fortran -> List.rev l
1611+
in
1612+
match l with
1613+
| (instrs, i', _) :: rem ->
1614+
List.fold_left
1615+
~f:(fun (instrs, ofs) (instrs', i', dim) ->
1616+
let ofs' = Code.Var.fresh () in
1617+
( (let* () = instrs in
1618+
let* () = instrs' in
1619+
store ~typ:I32 ofs' Arith.((ofs * load dim) + load i'))
1620+
, load ofs' ))
1621+
~init:(instrs, load i')
1622+
rem
1623+
| [] -> return (), Arith.const 0l
1624+
1625+
let get ~bound_error_index ~kind ~layout ta ~indices =
1626+
let instrs, ofs = offset ~bound_error_index ~layout ta ~indices in
1627+
seq instrs (get_at_offset ~kind ta ofs)
1628+
1629+
let set ~bound_error_index ~kind ~layout ta ~indices v =
1630+
let instrs, ofs = offset ~bound_error_index ~layout ta ~indices in
1631+
seq
1632+
(let* () = instrs in
1633+
set_at_offset ~kind ta ofs v)
1634+
Value.unit
1635+
end
1636+
13761637
module JavaScript = struct
13771638
let anyref = W.Ref { nullable = true; typ = Any }
13781639

0 commit comments

Comments
 (0)