Supported Features.

Translator supports only subset of F# language and this subset is described here.

Basic constructions

Array access

Array "by index" access is supported.

1: 
2: 
3: 
4: 
<@
    fun (range: Range1D) (buf: ClArray<_>) ->
        buf.[1] <- buf.[0]
@>

Binding

Basic "let" binding is supported. Note, that now we support only "variable bindings". Nested functions, closures are not supported.

1: 
2: 
3: 
4: 
5: 
6: 
<@
    fun (range: Range1D) (buf: ClArray<_>) ->
        let x = 1
        let y = (x - 1) * (x + 2)
        buf.[x] <- y
@>

Mutable binding

Mutability is available by using of "let mutable" binding.

1: 
2: 
3: 
4: 
5: 
<@
    fun (range: Range1D) (buf: ClArray<_>) ->
        let mutable x = 1
        x <- x * 2
@>

Note, that scopes are supported. So, you can "rebind" any name and "F#-style" visibility will be emuleted in target code. For example, next code will be translated correctly.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
<@
    fun (range: Range1D) (buf: ClArray<_>) ->
        let i = 2
        for i in 1 .. 3 do
            buf.[i] <- buf.[i] + 1
        buf.[0] <- i
@>


<@
    fun (range: Range1D) (buf: ClArray<_>) ->
        for i in 1 .. 3 do
            let i = i * 2
            buf.[i] <- 0
@>

Local funtions

You can also use local funtions inside kernel. When compiled, they will be converted to OpenCL C functions.

1: 
2: 
3: 
4: 
5: 
6: 
<@
    fun (range: Range1D) (buffer: int clarray) ->
        let gid = range.GlobalID0
        let f x = x + 10
        buffer.[gid] <- f buffer.[gid]
@>

Expression ignore by |> ignore

1: 
2: 
3: 
4: 
5: 
<@
    fun (range: Range1D) (buffer: int clarray) ->
        let gid = range.GlobalID0
        atomic inc buffer.[gid] |> ignore
@>

Control flow

Almost all basic control flow operators are supported.

Sequential operations

1: 
2: 
3: 
4: 
5: 
<@
    fun (range: Range1D) (buf: ClArray<int>) ->
        buf.[0] <- 2
        buf.[1] <- 4
@>

WHILE loop

1: 
2: 
3: 
4: 
5: 
<@
    fun (range: Range1D) (buf: ClArray<_>) ->
        while buf.[0] < 5 do
            buf.[0] <- buf.[0] + 1
@>

FOR integer range loop

1: 
2: 
3: 
4: 
5: 
<@
    fun (range: Range1D) (buf: ClArray<_>) ->
        for i in 1 .. 3 do
            buf.[i] <- 0
@>

Quotations injection

You can use "quotations injection" for code reusing or parameterization. For example, you can write something like this:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
let myFun = <@ fun x y -> y - x @>
let command =
    <@
        fun (range: Range1D) (buf: ClArray<int>) ->
            buf.[0] <- (%myFun) 2 5
            buf.[1] <- (%myFun) 4 9
    @>

let commandTemplate f =
    <@
        fun (range: Range1D) (buf: ClArray<int>) ->
            buf.[0] <- (%f) 2 5
            buf.[1] <- (%f) 4 9
    @>

let cmd1 = commandTemplate  <@ fun x y -> y - x @>
let cmd2 = commandTemplate  <@ fun x y -> y + x @>

Data types

Both host-kernel transferring and processing in kernels are supporeted for the following types. - Primitive types - Booleans - Custom structs - Records - Tuples - Discriminated unions

Structs

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
[<Struct>]
type MyStruct =
    val X: int
    val Y: int
    new (x, y) = { X = x; Y = y }
    new (x) = { X = x; Y = 0 }

let command1 =
    <@
        fun(range: Range1D) (buf: ClArray<int>) (str: MyStruct) ->
            buf.[0] <- str.X + str.Y
            let s2 = new MyStruct(6)
            let s3 = new MyStruct(s2.Y + 6)
            buf.[1] <- s2.X
    @>

let command2 =
    <@
        fun(range: Range1D) (buf: ClArray<int>) (arr: ClArray<MyStruct>) ->
            buf.[0] <- arr.[0].X
    @>

[<Struct>]
type StructOfIntInt64 =
    val mutable X: int
    val mutable Y: int64
    new(x, y) = { X = x; Y = y }

<@
    fun (range: Range1D) (buffer: ClArray<StructOfIntInt64>) ->
        let gid = range.GlobalID0
        let tmp = buffer.[gid]
        let x = tmp.X
        let y = tmp.Y
        let mutable innerStruct = StructOfIntInt64(x, y)
        innerStruct.X <- x
        innerStruct.Y <- y
        buffer.[gid] <- StructOfIntInt64(innerStruct.X, innerStruct.Y)
@>

Tuples

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
<@
    fun (range: Range1D) (buf: ClArray<int>) (k1: int * int) (k2: int64 * byte) (k3: float32 * int) ->
        let x = fst k1
        buf.[0] <- x
        buf.[1] <- int (fst k3)
@>

<@
    fun (range: Range1D) (buf: ClArray<int>) ->
        let (a, b) = (1, 2)
        buf.[0] <- a
@>

Math operations

Supported functions from System.Math and Microsoft.FSharp.Core.Operators: abs acos asin atan cos cosh exp floor log log10 pow sin sinh sqrt tan tanh

OpenCL specific extensions

Global and local memory space

Use clarray and clcell types to use data located in global memory inside the kernel. Use localArray and local to allocate data in local memory inside the kernel.

1: 
2: 
3: 
4: 
5: 
6: 
7: 
<@
    fun (range: Range1D) (globalBuffer: int clarray) ->
        let localBuffer = localArray<int> 10

        let gid = range.GlobalID0
        globalBuffer.[gid] <- globalBuffer.[gid] + 10
@>

Synchronization barriers

  • barrierLocal corresponds to barrier(CLK_LOCAL_MEM_FENCE)
  • barrierGlobal corresponds to barrier(CLK_GLOBAL_MEM_FENCE)
  • barrierFull corresponds to barrier(CLK_LOCAL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE)

Atomic functions

Use atomic for atomic function call.

1: 
2: 
3: 
4: 
<@
    fun (range: Range1D) (buffer: int clarray) ->
        atomic (+) buffer.[0] 10 |> ignore
@>
namespace Brahma
namespace Brahma.FSharp
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
Multiple items
namespace FSharp.Quotations

--------------------
namespace Microsoft.FSharp.Quotations
val range : Range1D
Multiple items
type Range1D =
  interface INDRange
  new : globalWorkSize:int -> Range1D
  new : globalWorkSize:int * localWorkSize:int -> Range1D
  private new : globalWorkSize:int * localWorkSize:int * __:unit -> Range1D
  member GlobalID0 : int
  member GlobalWorkSize : int
  member LocalID0 : int
  member LocalWorkSize : int
  static member CreateValid : neededSize:int * localWorkSize:int -> Range1D

--------------------
new : globalWorkSize:int -> Range1D
new : globalWorkSize:int * localWorkSize:int -> Range1D
val buf : ClArray<obj>
Multiple items
module ClArray

from Brahma.FSharp

--------------------
type ClArray<'a> =
  interface IBuffer<'a>
  interface IClMem
  interface IDisposable
  private new : buffer:ClBuffer<'a> -> ClArray<'a>
  member Dispose : unit -> unit
  override ToString : unit -> string
  member private Buffer : ClBuffer<'a>
  member Item : idx:int -> 'a with get
  member Length : int
  member Item : idx:int -> 'a with set
val buf : ClArray<int>
val x : int
val y : int
val mutable x : int
val i : int
val i : int32
val buffer : clarray<int>
Multiple items
val int : value:'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
type clarray<'a> = ClArray<'a>
val gid : int
property Range1D.GlobalID0: int with get
val f : (int -> int)
val atomic : f:('a -> 'b) -> ('a -> 'b)
val inc : p:'a -> 'a0 (requires member ( + ) and member get_One)
val ignore : value:'T -> unit
val myFun : Expr<(int -> int -> int)>
val command : Expr<(Range1D -> ClArray<int> -> unit)>
val commandTemplate : f:Expr<(int -> int -> int)> -> Expr<(Range1D -> ClArray<int> -> unit)>
val f : Expr<(int -> int -> int)>
val cmd1 : Expr<(Range1D -> ClArray<int> -> unit)>
val cmd2 : Expr<(Range1D -> ClArray<int> -> unit)>
Multiple items
type StructAttribute =
  inherit Attribute
  new : unit -> StructAttribute

--------------------
new : unit -> StructAttribute
MyStruct.X: int
MyStruct.Y: int
val command1 : Expr<(Range1D -> ClArray<int> -> MyStruct -> unit)>
val str : MyStruct
Multiple items
type MyStruct =
  struct
    new : x:int -> MyStruct
    new : x:int * y:int -> MyStruct
    val X: int
    val Y: int
  end

--------------------
MyStruct ()
new : x:int -> MyStruct
new : x:int * y:int -> MyStruct
val s2 : MyStruct
val s3 : MyStruct
val command2 : Expr<(Range1D -> ClArray<int> -> ClArray<MyStruct> -> unit)>
val arr : ClArray<MyStruct>
StructOfIntInt64.X: int
StructOfIntInt64.Y: int64
Multiple items
val int64 : value:'T -> int64 (requires member op_Explicit)

--------------------
type int64 = System.Int64

--------------------
type int64<'Measure> = int64
val y : int64
val buffer : ClArray<StructOfIntInt64>
Multiple items
type StructOfIntInt64 =
  struct
    new : x:int * y:int64 -> StructOfIntInt64
    val mutable X: int
    val mutable Y: int64
  end

--------------------
StructOfIntInt64 ()
new : x:int * y:int64 -> StructOfIntInt64
val tmp : StructOfIntInt64
val mutable innerStruct : StructOfIntInt64
val k1 : int * int
val k2 : int64 * byte
Multiple items
val byte : value:'T -> byte (requires member op_Explicit)

--------------------
type byte = System.Byte

--------------------
type byte<'Measure> = byte
val k3 : float32 * int
Multiple items
val float32 : value:'T -> float32 (requires member op_Explicit)

--------------------
type float32 = System.Single

--------------------
type float32<'Measure> = float32
val fst : tuple:('T1 * 'T2) -> 'T1
val a : int
val b : int
val globalBuffer : clarray<int>
val localBuffer : int array
val localArray : size:int -> 'a array