Skip to content

Commit fecb42b

Browse files
committed
Wasm: specialization of bigarray accesses
1 parent 36c3387 commit fecb42b

File tree

7 files changed

+651
-19
lines changed

7 files changed

+651
-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: 263 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,237 @@ 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 | Char -> "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+
| Char
1489+
| Float16 ->
1490+
box
1491+
(return
1492+
(W.Call
1493+
(f, ta :: ofs :: (if size = 0 then [] else [ W.GlobalGet little_endian ]))))
1494+
| Complex32 | Complex64 ->
1495+
let delta = Int32.shift_left 1l (size - 1) in
1496+
let* ofs' = Arith.(return ofs + const delta) in
1497+
let* x = box (return (W.Call (f, [ ta; ofs; W.GlobalGet little_endian ]))) in
1498+
let* y = box (return (W.Call (f, [ ta; ofs'; W.GlobalGet little_endian ]))) in
1499+
let* ty = Type.float_array_type in
1500+
return (W.ArrayNewFixed (ty, [ x; y ]))
1501+
1502+
let set_at_offset ~kind a i v =
1503+
let name, (typ : Wasm_ast.value_type), size, unbox =
1504+
match (kind : Typing.Bigarray.kind) with
1505+
| Float32 ->
1506+
( "dv_set_f32"
1507+
, F32
1508+
, 2
1509+
, fun x ->
1510+
let* x = x in
1511+
return (W.F32DemoteF64 x) )
1512+
| Float64 -> "dv_set_f64", F64, 3, Fun.id
1513+
| Int8_signed | Int8_unsigned | Char -> "dv_set_i8", I32, 0, Fun.id
1514+
| Int16_signed | Int16_unsigned -> "dv_set_i16", I32, 1, Fun.id
1515+
| Int32 -> "dv_set_i32", I32, 2, Fun.id
1516+
| Nativeint -> "dv_set_i32", I32, 2, Fun.id
1517+
| Int64 -> "dv_set_i64", I64, 3, Fun.id
1518+
| Int -> "dv_set_i32", I32, 2, Fun.id
1519+
| Float16 ->
1520+
( "dv_set_i16"
1521+
, I32
1522+
, 1
1523+
, fun x ->
1524+
let* conv =
1525+
register_import
1526+
~name:"caml_double_to_float16"
1527+
(Fun { W.params = [ F64 ]; result = [ I32 ] })
1528+
in
1529+
let* x = Fun.id x in
1530+
return (W.Call (conv, [ x ])) )
1531+
| Complex32 ->
1532+
( "dv_set_f32"
1533+
, F32
1534+
, 3
1535+
, fun x ->
1536+
let* x = x in
1537+
return (W.F32DemoteF64 x) )
1538+
| Complex64 -> "dv_set_f64", F64, 4, Fun.id
1539+
in
1540+
let* ty = Type.bigarray_type in
1541+
let* ta = Memory.wasm_struct_get ty (Memory.wasm_cast ty a) 2 in
1542+
let* ofs = Arith.(i lsl const (Int32.of_int size)) in
1543+
let* little_endian =
1544+
register_import
1545+
~import_module:"bindings"
1546+
~name:"littleEndian"
1547+
(Global { mut = false; typ = I32 })
1548+
in
1549+
let* f =
1550+
register_import
1551+
~import_module:"bindings"
1552+
~name
1553+
(Fun
1554+
{ W.params =
1555+
Ref { nullable = true; typ = Extern }
1556+
:: I32
1557+
:: typ
1558+
:: (if size = 0 then [] else [ I32 ])
1559+
; result = []
1560+
})
1561+
in
1562+
match kind with
1563+
| Float32
1564+
| Float64
1565+
| Int8_signed
1566+
| Int8_unsigned
1567+
| Int16_signed
1568+
| Int16_unsigned
1569+
| Int32
1570+
| Int64
1571+
| Int
1572+
| Nativeint
1573+
| Char
1574+
| Float16 ->
1575+
let* v = unbox v in
1576+
instr
1577+
(W.CallInstr
1578+
( f
1579+
, ta :: ofs :: v :: (if size = 0 then [] else [ W.GlobalGet little_endian ])
1580+
))
1581+
| Complex32 | Complex64 ->
1582+
let delta = Int32.shift_left 1l (size - 1) in
1583+
let* ofs' = Arith.(return ofs + const delta) in
1584+
let ty = Type.float_array_type in
1585+
let* x = unbox (Memory.wasm_array_get ~ty v (Arith.const 0l)) in
1586+
let* () = instr (W.CallInstr (f, [ ta; ofs; x; W.GlobalGet little_endian ])) in
1587+
let* y = unbox (Memory.wasm_array_get ~ty v (Arith.const 1l)) in
1588+
instr (W.CallInstr (f, [ ta; ofs'; y; W.GlobalGet little_endian ]))
1589+
1590+
let offset ~bound_error_index ~(layout : Typing.Bigarray.layout) ta ~indices =
1591+
let l =
1592+
List.mapi
1593+
~f:(fun pos i ->
1594+
let i =
1595+
match layout with
1596+
| C -> i
1597+
| Fortran -> Arith.(i - const 1l)
1598+
in
1599+
let i' = Code.Var.fresh () in
1600+
let dim = Code.Var.fresh () in
1601+
( (let* () = store ~typ:I32 i' i in
1602+
let* () = store ~typ:I32 dim (dimension pos ta) in
1603+
let* cond = Arith.uge (load i') (load dim) in
1604+
instr (W.Br_if (bound_error_index, cond)))
1605+
, i'
1606+
, dim ))
1607+
indices
1608+
in
1609+
let l =
1610+
match layout with
1611+
| C -> l
1612+
| Fortran -> List.rev l
1613+
in
1614+
match l with
1615+
| (instrs, i', _) :: rem ->
1616+
List.fold_left
1617+
~f:(fun (instrs, ofs) (instrs', i', dim) ->
1618+
let ofs' = Code.Var.fresh () in
1619+
( (let* () = instrs in
1620+
let* () = instrs' in
1621+
store ~typ:I32 ofs' Arith.((ofs * load dim) + load i'))
1622+
, load ofs' ))
1623+
~init:(instrs, load i')
1624+
rem
1625+
| [] -> return (), Arith.const 0l
1626+
1627+
let get ~bound_error_index ~kind ~layout ta ~indices =
1628+
let instrs, ofs = offset ~bound_error_index ~layout ta ~indices in
1629+
seq instrs (get_at_offset ~kind ta ofs)
1630+
1631+
let set ~bound_error_index ~kind ~layout ta ~indices v =
1632+
let instrs, ofs = offset ~bound_error_index ~layout ta ~indices in
1633+
seq
1634+
(let* () = instrs in
1635+
set_at_offset ~kind ta ofs v)
1636+
Value.unit
1637+
end
1638+
13761639
module JavaScript = struct
13771640
let anyref = W.Ref { nullable = true; typ = Any }
13781641

0 commit comments

Comments
 (0)