https://github.com/copy/lowcaml
An experimental OCaml-to-C compiler for type-safe accesss to SIMD (unreleased)
https://github.com/copy/lowcaml
Last synced: about 1 year ago
JSON representation
An experimental OCaml-to-C compiler for type-safe accesss to SIMD (unreleased)
- Host: GitHub
- URL: https://github.com/copy/lowcaml
- Owner: copy
- Created: 2023-04-04T10:15:09.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2024-01-25T01:37:09.000Z (over 2 years ago)
- Last Synced: 2025-03-25T15:01:41.429Z (about 1 year ago)
- Language: OCaml
- Homepage:
- Size: 115 KB
- Stars: 16
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: readme.md
- Changelog: changes.md
Awesome Lists containing this project
README
Lowcaml is an experimental OCaml-to-C compiler. It generates C files as well as
the corresponding OCaml bindings. Its primary goal is writing typesafe SIMD
code, but it can also be used to accelerate simple OCaml code and create
bindings to C libraries.
The following features are supported:
- built-in OCaml types (`int`, `int32`, `int64`, `unit`, `bool`, `char`, `string`, `bytes` and `Bigarray.Array1.t`)
- a subset of the OCaml standard library, mostly functions on the above types that don't allocate
- top-level functions
- for and while loops
- if-else expressions
- let bindings (currently only directly below functions)
- externals, which call external C functions directly
- calling other lowcaml functions
- some libc types
- stack-allocated `int` (like OCaml's `ref`), currently called `int Mut.t`
- generating `#include` using `[@@@include "header"]`
The following features are *not* supported, but *may* be supported in the future:
- C array, struct, enum, union, typedef or bindings to types from external libraries
- `static` (non-exported) C functions
- stack-allocated values
- OCaml variants, records, tuples
- `ref` and `array` values
- bytecode stubs
- bounds checks
- sub-modules
- named and optional parameters
- match expressions
- top-level constants
- string literals
- a pure OCaml implementation of `Lowcaml_stdlib` (for jsoo support)
The following features are *not* supported, and are out of scope for the project:
- allocating from lowcaml or calling into the OCaml runtime: All functions generated by lowcaml are marked `[@@noalloc]`
- closures, partial application, exceptions or effects
- cross-platform SIMD bindings (but could be implemented as a third-party library)
- complete libc bindings
- 32-bit platforms
- any particular support for shared memory parallelism
A subset of the OCaml stdlib, as well as some libc and SIMD methods are
exposed. You can browse the interface (wip):
[`lowcaml_stdlib.mli`](lowcaml_stdlib.mli).
Currently, only OCaml 5.0 is supported.
Usage
-----
Dune users can use this library by vendoring it in their project. opam users
can run `dune install` which will install `lowcaml.exe` in their current opam
switch.
You will need a custom rule that invokes `lowcaml.exe` and a library with C
stubs. In the following, `my_stubs_lowcaml.ml` is the input while `lstubs.ml` and
`cstubs.c` are generated files. Dune users can use something similar to this:
```
(rule
(targets lstubs.ml cstubs.c)
(deps my_stubs_lowcaml.ml)
(action (run lowcaml.exe -source my_stubs_lowcaml.ml -o-ml lstubs.ml -o-c cstubs.c)))
(library
(name lstubs)
(modules lstubs)
(foreign_stubs
(language c)
(names cstubs)
(flags
:standard
-Wall -Wpedantic -Wconversion -Werror
-mavx2 ; Note: for SIMD instructions, requires x86_64 with AVX2
)))
```
Merlin is supported by defining a dummy library. Dune users can use the
following library stanza and run `dune build lowcaml_test_dummy.cma`.
```
(library
(name lowcaml_merlin_dummy)
(modules my_stubs_lowcaml)
(libraries lowcaml.stdlib)
(flags :standard -nopervasives -open Lowcaml_stdlib))
```
Examples
--------
An implementation of [Sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes):
```ocaml
let sieve b =
let len = Bytes.length b in
for i = 2 to len - 1 do
if Bytes.get_uint8 b i = 0 then (
let j = Mut.int (2 * i) in
while !j < len do
Bytes.set_uint8 b !j 1;
j := !j + i;
done
)
done
```
```c
// generated by lowcaml
void sieve(const value b)
{
const int64_t len = (int64_t)caml_string_length(b);
const int64_t upto = (len-1);
for(int64_t i = 2; (i<=upto); (i+=1))
{
if((*(uint8_t*)&Byte(b, i)==0))
{
int64_t j = (2*i);
while((j