diff --git a/hpvm/docs/hpvm-specification.md b/hpvm/docs/hpvm-specification.md index 360cf106e63c46387484aa755a93d53a2f02813d..157e57452aedd94d2b06b56958d44c07ede17fed 100644 --- a/hpvm/docs/hpvm-specification.md +++ b/hpvm/docs/hpvm-specification.md @@ -1,5 +1,5 @@ # HPVM Abstraction -An HPVM program is a combination of host code plus a set of one or more distinct dataflow graphs. Each dataflow graph (DFG) is a hierarchical graph with side effects. Nodes represent units of execution, and edges between nodes describe the explicit data transfer requirements. A node can begin execution once a data item becomes available on every one of its input edges. Repeated transfer of data items between nodes (if more inputs are provided) yields a pipelined execution of different nodes in the graph. The execution of a DFG is initiated and terminated by host code that launches the graph. Nodes may access globally shared memory through load and store instructions (side-effects). +An HPVM program is a combination of host code plus a set of one or more distinct dataflow graphs. Each dataflow graph (DFG) is a hierarchical graph with side effects. The DFG must be acyclic. Nodes represent units of execution, and edges between nodes describe the explicit data transfer requirements. A node can begin execution once a data item becomes available on every one of its input edges. Repeated transfer of data items between nodes (if more inputs are provided) yields a pipelined execution of different nodes in the graph. The execution of a DFG is initiated and terminated by host code that launches the graph. Nodes may access globally shared memory through load and store instructions (side-effects). ## Dataflow Node A *dataflow node* represents unit of computation in the DFG. A node can begin execution once a data item becomes available on every one of its input edges. @@ -90,13 +90,13 @@ Create a static dataflow node replicated in two dimensions, namely ```x``` and ` Create a static dataflow node replicated in three dimensions, namely ```x```, ```y``` and ```z```, with ```n1```, ```n2``` and ```n3``` dynamic instances in each dimension respectively, executing node function ```F```. Return a handle to the created node. ```i8* llvm.hpvm.createEdge(i8* Src, i8* Dst, i1 ReplType, i32 sp, i32 dp, i1 isStream)``` -Create edge from output ```sp``` of node ```Src``` to input ```dp``` of node ```Dst```. ```ReplType``` chooses between a one-to-one (0) or all-to-all (1) edge. ```isStream``` chooses a streaming (1) or non streaming (0) edge. Return a handle to the created edge. +Create edge from output ```sp``` of node ```Src``` to input ```dp``` of node ```Dst```. Argument ```dp``` of ```Dst```'s node function and field ```sp``` of the return struct in ```Src```'s node function must have matching types. ```ReplType``` chooses between a one-to-one (0) or all-to-all (1) edge. ```isStream``` chooses a streaming (1) or non streaming (0) edge. Return a handle to the created edge. ```void llvm.hpvm.bind.input(i8* N, i32 ip, i32 ic, i1 isStream)``` -Bind input ```ip``` of current node to input ```ic``` of child node ```N```. ```isStream``` chooses a streaming (1) or non streaming (0) bind. +Bind input ```ip``` of current node to input ```ic``` of child node ```N```. Argument ```ic``` of ```N```'s node function and argument ```ip``` of the current node function must have matching types. ```isStream``` chooses a streaming (1) or non streaming (0) bind. ```void llvm.hpvm.bind.output(i8* N, i32 oc, i32 op, i1 isStream)``` -Bind output ```oc``` of child node ```N``` to output ```op``` of current node. ```isStream``` chooses a streaming (1) or non streaming (0) bind. +Bind output ```oc``` of child node ```N``` to output ```op``` of current node. Field ```oc``` of the return struct in ```N```'s node function and field ```op``` of the return struct in the current node function must have matching types. ```isStream``` chooses a streaming (1) or non streaming (0) bind. ## Intrinsics for Querying Graphs @@ -172,7 +172,7 @@ Stop tracking memory object with key ```ptr```, and remove it from memory tracke If memory object with key ```ptr``` is not located in host memory, copy it to host memory. ```i8* llvm.hpvm.launch(i8* RootGraph, i8* Args, i1 isStream)``` -Launch the execution of DFG with node function ```RootGraph```. ```Args``` is a pointer to packed struct, containing one field per argument of the ```RootGraph``` function, consecutively. For non-streaming DFGs with a non empty result type, ```Args``` must contain an additional field of the type ```RootGraph.returnTy```, where the result of the graph will be returned. ```isStream``` chooses between a non streaming (0) or streaming (1) graph execution. Return a handle to the invoked DFG. +Launch the execution of a top-level DFG with root node function ```RootGraph```. ```Args``` is a pointer to a packed struct, containing one field per argument of the ```RootGraph``` function, consecutively. For non-streaming DFGs with a non empty result type, ```Args``` must contain an additional field of the type ```RootGraph.returnTy```, where the result of the graph will be returned. ```isStream``` chooses between a non streaming (0) or streaming (1) graph execution. Return a handle to the invoked DFG. ```void llvm.hpvm.wait(i8* GraphID)``` Wait for completion of execution of DFG with handle ```GraphID```. @@ -181,9 +181,10 @@ Wait for completion of execution of DFG with handle ```GraphID```. Push set of input data ```args``` (same as type included in launch) to streaming DFG with handle ```GraphID```. ```i8* llvm.hpvm.pop(i8* GraphID)``` -Pop and return data from streaming DFG with handle ```GraphID```. +Pop and return data from streaming DFG with handle ```GraphID```. The return type is a struct containing a field for every output of DFG. ## Implementation Limitations Due to limitations of our current prototype implementation, the following restrictions are imposed: - In HPVM, a memory object is represented as a (pointer, size) pair that includes the address of memory object, and the size (in bytes) of the pointed-to object. Therefore, when an edge/bind carries a pointer, it must be followed by an i64 size value. - Pointers cannot be transferred between nodes using dataflow edges. Instead, they should be passed using the bind operation from the (common) parent of the source and sink nodes. +- Instantiation of dataflow nodes is supported in up to three dimensions.