Skip to content

Commit

Permalink
Release of spnc version 0.0.3;
Browse files Browse the repository at this point in the history
  • Loading branch information
sommerlukas committed Jun 26, 2019
2 parents d3355f2 + 94d9811 commit 6eb49a6
Show file tree
Hide file tree
Showing 54 changed files with 1,375 additions and 216 deletions.
20 changes: 14 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,22 @@
`spnc` is a multi-target compiler for Sum-Product Networks,
a class of machine learning models.

`spnc` currently supports generation of serial C++ code for SPNs,
support for generating multi-threaded OpenMP code and CUDA code for
Nvidia GPUs is under way. Additionally, `spnc` allows to compute
`spnc` currently supports three different kinds of code generation for SPNs:
* Serial C++ code, using `-t cpp`.
* OpenMP thread parallel C++ code, using `-t cpp` and `--openmp-parallel`
in combination.
* CUDA code for Nvidia GPUs, using `-t cuda`.

Support for generating SIMD execution with OpenMP is under way.
Additionally, `spnc` allows to compute
statistics about an SPNs graph and output them to JSON.

### Prerequisites ###

`spnc` requires a recent version of Java to run and
at least one of `g++` or `clang++` to be installed on your machine.
`spnc` requires a recent version of Java to run. For compilation and execution
of C++/OpenMP code, at least one of `g++` or `clang++` needs to be installed on
your machine. If you want to use CUDA compilation for Nvidia GPUs, you need to
have `nvcc`.

### Installation ###

Expand All @@ -30,7 +37,8 @@ The executable contains an automatically generated `main`-method which will
read input-data from a plain-text file and, if a second file-name is given,
compare them to the reference data from the reference-file (also plain text).
The executable will also track the time spent to execute the SPN inference
for all examples in the input sample.
for all examples in the input sample. This works independent from the actual
execution mode (C++ serial, OpenMP, CUDA on Nvidia GPU).

Run `spnc --help` to see additional options.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package spn_compiler.backend.software.ast.construct

import spn_compiler.backend.software.ast.construct.util.UniqueNameCreator
import spn_compiler.backend.software.ast.nodes.function.{ASTFunction, ASTFunctionParameter, ASTFunctionPrototype}
import spn_compiler.backend.software.ast.extensions.cpp.value.{CPPAddressOfOperator, CPPSizeOfOperator}
import spn_compiler.backend.software.ast.nodes.function.{ASTExternalFunction, ASTFunction, ASTFunctionParameter, ASTFunctionPrototype}
import spn_compiler.backend.software.ast.nodes.reference._
import spn_compiler.backend.software.ast.nodes.statement.control_flow.{ASTCallStatement, ASTForLoop, ASTIfStatement, ASTReturnStatement}
import spn_compiler.backend.software.ast.nodes.statement.variable.{ASTVariableAssignment, ASTVariableDeclaration}
Expand Down Expand Up @@ -113,7 +114,7 @@ trait ASTBuilder {
}
}

protected def insertStatement[Stmt <: ASTStatement](stmt : Stmt) : Stmt =
def insertStatement[Stmt <: ASTStatement](stmt : Stmt) : Stmt =
(insertionPoint.block, insertionPoint.stmt) match {
case (Some(block), Some(insertBefore)) => block.insertBefore(insertBefore, stmt)
case (Some(block), None) => block.append(stmt)
Expand All @@ -129,7 +130,7 @@ trait ASTBuilder {
* @param testExpression Boolean test expression.
* @return New [[ASTIfStatement]]
*/
def createIf(testExpression : ASTValue): ASTIfStatement = insertStatement(new ASTIfStatement(testExpression))
def createIf(testExpression : ASTValue): ASTIfStatement = new ASTIfStatement(testExpression)

/**
* Create a for-loop with the following header: for(IVar = IVal; TVal; IncrVar = IncrVal).
Expand All @@ -142,29 +143,31 @@ trait ASTBuilder {
*/
def forLoop(initVar : Option[ASTReference], initValue : Option[ASTValue], testValue : ASTValue,
incrVar : Option[ASTReference], incrValue : Option[ASTValue]) : ASTForLoop =
insertStatement(new ASTForLoop(initVar, initValue, testValue, incrVar, incrValue))
new ASTForLoop(initVar, initValue, testValue, incrVar, incrValue)

/**
* Create a for-loop with the following header: for(Var = LB; Var < UB; Var = Var + Stride).
* @param variable Var
* @param variable Var
* @param lowerBound LB
* @param upperBound UB
* @param stride Stride
* @param stride Stride
* @return New [[ASTForLoop]]
*/
def forLoop(variable : ASTVariable, lowerBound : ASTValue, upperBound : ASTValue, stride : ASTValue) : ASTForLoop = {
val ref = referenceVariable(variable)
val comparison = cmpLT(readVariable(ref), upperBound)
val increment = add(readVariable(ref), stride)
insertStatement(new ASTForLoop(Some(ref), Some(lowerBound), comparison, Some(ref), Some(increment)))
new ASTForLoop(Some(ref), Some(lowerBound), comparison, Some(ref), Some(increment))
}

protected var externalHeaders : Set[String] = Set()

/**
* Create a call '''statement''' from a call '''expression''', discarding the return value if necessary.
* @param call [[ASTCallExpression]] for the actual call.
* @return New [[ASTCallStatement]]
*/
def createCallStatement(call : ASTCallExpression) : ASTCallStatement = insertStatement(new ASTCallStatement(call))
def createCallStatement(call : ASTCallExpression) : ASTCallStatement = new ASTCallStatement(call)

/**
* Create a call '''statement''' for the given function with the given parameters, discarding the return value
Expand All @@ -173,24 +176,34 @@ trait ASTBuilder {
* @param parameters Actual parameter values.
* @return New [[ASTCallStatement]].
*/
def createCallStatement(function : ASTFunctionPrototype, parameters : ASTValue*) : ASTCallStatement =
insertStatement(new ASTCallStatement(new ASTCallExpression(function, parameters:_*)))
def createCallStatement(function : ASTFunctionPrototype, parameters : ASTValue*) : ASTCallStatement = {
externalHeaders = function match {
case external : ASTExternalFunction => externalHeaders + external.header
case _ => externalHeaders
}
new ASTCallStatement(new ASTCallExpression(function, parameters:_*))
}

/**
* Create a call '''expression''', calling the given function with the given parameters.
* @param function [[ASTFunctionPrototype]] to call.
* @param parameters Actual parameter values.
* @return New [[ASTCallExpression]].
*/
def call(function : ASTFunctionPrototype, parameters : ASTValue*) : ASTCallExpression =
def call(function : ASTFunctionPrototype, parameters : ASTValue*) : ASTCallExpression = {
externalHeaders = function match {
case external : ASTExternalFunction => externalHeaders + external.header
case _ => externalHeaders
}
new ASTCallExpression(function, parameters:_*)
}

/**
* Create a return statement, returning the given value.
* @param returnValue Return value.
* @return New [[ASTReturnStatement]].
*/
def ret(returnValue : ASTValue) : ASTReturnStatement = insertStatement(new ASTReturnStatement(returnValue))
def ret(returnValue : ASTValue) : ASTReturnStatement = new ASTReturnStatement(returnValue)


//
Expand Down Expand Up @@ -223,7 +236,7 @@ trait ASTBuilder {
if(!variables.contains(variable)){
throw new ASTBuildingException("Can only declare variable created with this builder before!")
}
insertStatement(new ASTVariableDeclaration(variable))
new ASTVariableDeclaration(variable)
}

/**
Expand All @@ -236,7 +249,7 @@ trait ASTBuilder {
if(!variables.contains(variable)){
throw new ASTBuildingException("Can only declare variable created with this builder before!")
}
insertStatement(new ASTVariableDeclaration(variable, Some(initValue)))
new ASTVariableDeclaration(variable, Some(initValue))
}

protected val globalVariables : ListBuffer[ASTVariableDeclaration] = ListBuffer()
Expand Down Expand Up @@ -323,7 +336,7 @@ trait ASTBuilder {
* @return [[ASTVariableAssignment]].
*/
def assignVariable(reference : ASTReference, value : ASTValue) : ASTVariableAssignment =
insertStatement(new ASTVariableAssignment(reference, value))
new ASTVariableAssignment(reference, value)

/**
* Assign the given value to the given index of the given reference to some entity.
Expand All @@ -333,7 +346,7 @@ trait ASTBuilder {
* @return [[ASTVariableAssignment]].
*/
def assignIndex(reference : ASTReference, index : ASTValue, value : ASTValue) : ASTVariableAssignment =
insertStatement(new ASTVariableAssignment(referenceIndex(reference, index), value))
new ASTVariableAssignment(referenceIndex(reference, index), value)

/**
* Assign the given value to the given element (by name) of the given reference to some entity.
Expand All @@ -343,7 +356,7 @@ trait ASTBuilder {
* @return [[ASTVariableAssignment]].
*/
def assignElement(reference : ASTReference, element : String, value : ASTValue) : ASTVariableAssignment =
insertStatement(new ASTVariableAssignment(referenceElement(reference, element), value))
new ASTVariableAssignment(referenceElement(reference, element), value)

//
// Constants and literals.
Expand Down Expand Up @@ -436,4 +449,27 @@ trait ASTBuilder {
*/
def convert(op : ASTValue, targetType : ASTType) : ASTTypeConversion = new ASTTypeConversion(op, targetType)

//
// Address-of operator
//
/**
* Apply the C++-specific address-of operator to some reference, yielding a pointer to the underlying
* entity.
* @param ref Reference some entity.
* @return [[CPPAddressOfOperator]], essentially a array-type value representing the pointer.
*/
def addressOf(ref : ASTReference) : CPPAddressOfOperator = new CPPAddressOfOperator(ref)

//
// Size-of operator
//
/**
* Apply the C++-specific sizeof-operator to some reference, yielding the number of bytes necessary
* to store one entity of the type of the reference.
* @param ref Reference to some variable.
* @return [[CPPSizeOfOperator]], essentially a integer value giving the number of bytes
* requires to store one entity of the type of the given variable.
*/
def sizeOf(ref : ASTReference) : CPPSizeOfOperator = new CPPSizeOfOperator(ref)

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ trait ASTTypeContext {
protected val structTypes : ListBuffer[StructType] = ListBuffer[StructType]()

def createStructType(name : String, _elements : (String, ASTType)*): StructType = {
val structType = StructType(structNameCreator.makeUniqueName(name), _elements.toList)
val structType = new StructType(structNameCreator.makeUniqueName(name), _elements.toList)
structTypes += structType
structType
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package spn_compiler.backend.software.ast.extensions.cpp.value

import spn_compiler.backend.software.ast.nodes.reference.ASTReference
import spn_compiler.backend.software.ast.nodes.types.{ASTType, ArrayType}
import spn_compiler.backend.software.ast.nodes.value.ASTValue

class CPPAddressOfOperator private[ast](val ref : ASTReference) extends ASTValue {
override def getType: ASTType = ArrayType(ref.getType)
}

object CPPAddressOfOperator {

def unapply(arg: CPPAddressOfOperator): Option[ASTReference] = Some(arg.ref)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package spn_compiler.backend.software.ast.extensions.cpp.value

import spn_compiler.backend.software.ast.nodes.reference.ASTReference
import spn_compiler.backend.software.ast.nodes.types.{ASTType, IntegerType}
import spn_compiler.backend.software.ast.nodes.value.ASTValue

class CPPSizeOfOperator private[ast](val ref : ASTReference) extends ASTValue {
override def getType: ASTType = IntegerType
}

object CPPSizeOfOperator {
def unapply(arg: CPPSizeOfOperator): Option[ASTReference] = Some(arg.ref)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package spn_compiler.backend.software.ast.extensions.cuda

import spn_compiler.backend.software.ast.construct.ASTBuilder
import spn_compiler.backend.software.ast.extensions.cuda.function.CUDAFunction
import spn_compiler.backend.software.ast.extensions.cuda.function.CUDAFunction.CUDAFunctionScope
import spn_compiler.backend.software.ast.extensions.cuda.statement.{CUDADim3Init, CUDAKernelInvocation}
import spn_compiler.backend.software.ast.nodes.function.ASTFunctionParameter
import spn_compiler.backend.software.ast.nodes.types.{ASTType, IntegerType}
import spn_compiler.backend.software.ast.nodes.value.ASTValue
import spn_compiler.backend.software.ast.nodes.value.constant.ASTConstant
import spn_compiler.backend.software.ast.nodes.variable.ASTVariable

trait CUDAASTBuilder extends ASTBuilder {

def dim3(variable : ASTVariable,
x : ASTValue = new ASTConstant(IntegerType, 1),
y : ASTValue = new ASTConstant(IntegerType, 1),
z : ASTValue = new ASTConstant(IntegerType, 1)) : CUDADim3Init =
new CUDADim3Init(variable, x, y, z)

def invokeKernel(gridLayout : ASTValue, blockLayout : ASTValue, kernel : CUDAFunction,
params : ASTValue*) : CUDAKernelInvocation =
new CUDAKernelInvocation(gridLayout, blockLayout, kernel, params:_*)

/**
* Define a local (i.e. defined in this module) function.
* @param name Name of the function.
* @param returnType Return type of the function.
* @param parameters [[ASTFunctionParameter]]s as formal parameters of the function.
* @return [[CUDAFunction]] with given name, return type and formal parameters.
*/
def defineLocalCUDAFunction(scope : CUDAFunctionScope, name : String, returnType : ASTType,
parameters : ASTFunctionParameter*) : CUDAFunction = {
val func = new CUDAFunction(scope, name, returnType, parameters:_*)
localFunctions += func
func
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package spn_compiler.backend.software.ast.extensions.cuda

import spn_compiler.backend.software.ast.nodes.function.ASTFunction
import spn_compiler.backend.software.ast.nodes.module.ASTModule
import spn_compiler.backend.software.ast.nodes.statement.variable.ASTVariableDeclaration
import spn_compiler.backend.software.ast.nodes.types.StructType

class CUDAModule(name : String) extends ASTModule(name) with CUDAASTBuilder

object CUDAModule {
def unapply(arg: CUDAModule): Option[(String, List[StructType], List[ASTVariableDeclaration], List[ASTFunction])] =
Some(arg.name, arg.structTypes.toList, arg.globalVariables.toList, arg.localFunctions.toList)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package spn_compiler.backend.software.ast.extensions.cuda.function

import spn_compiler.backend.software.ast.extensions.cuda.function.CUDAFunction.CUDAFunctionScope
import spn_compiler.backend.software.ast.nodes.function.{ASTFunction, ASTFunctionParameter}
import spn_compiler.backend.software.ast.nodes.types.ASTType

class CUDAFunction private[ast](val scope : CUDAFunctionScope, name : String, returnType : ASTType,
params : ASTFunctionParameter*) extends ASTFunction(name, returnType, params:_*)

object CUDAFunction {

sealed abstract class CUDAFunctionScope(val prefix : String)
case object Host extends CUDAFunctionScope("__host__")
case object Global extends CUDAFunctionScope("__global__")
case object Device extends CUDAFunctionScope("__device__")

def unapplySeq(func : CUDAFunction) : Option[(CUDAFunctionScope, String, ASTType, Seq[ASTFunctionParameter])] =
Some(func.scope, func.name, func.returnType, func.getParameters.seq)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package spn_compiler.backend.software.ast.extensions.cuda.predef

import spn_compiler.backend.software.ast.extensions.cuda.predef.CUDAMemCpyKind.{Device2Device, Device2Host, Host2Device, Host2Host}
import spn_compiler.backend.software.ast.nodes.function.ASTExternalFunction
import spn_compiler.backend.software.ast.nodes.types._
import spn_compiler.backend.software.ast.nodes.value.constant.ASTConstant
import spn_compiler.backend.software.ast.nodes.variable.ASTVariable

case object CUDADim3Type extends StructType("dim3", List(("x", IntegerType), ("y", IntegerType), ("z", IntegerType)))

case object CUDAGridDim extends ASTVariable(CUDADim3Type, "gridDim")

case object CUDAGridID extends ASTVariable(CUDADim3Type, "gridIdx")

case object CUDABlockDim extends ASTVariable(CUDADim3Type, "blockDim")

case object CUDABlockID extends ASTVariable(CUDADim3Type, "blockIdx")

case object CUDAThreadID extends ASTVariable(CUDADim3Type, "threadIdx")



object CUDAMemCpyKind {
sealed trait CUDACopyDirection extends EnumBaseType
case object Host2Host extends CUDACopyDirection {
override def toString: String = "cudaMemcpyHostToHost"
}
case object Host2Device extends CUDACopyDirection {
override def toString: String = "cudaMemcpyHostToDevice"
}

case object Device2Host extends CUDACopyDirection {
override def toString: String = "cudaMemcpyDeviceToHost"
}
case object Device2Device extends CUDACopyDirection {
override def toString: String = "cudaMemcpyDeviceToDevice"
}
val enumType = new EnumType[CUDACopyDirection](Host2Host, Host2Device, Device2Host, Device2Device)
}

sealed abstract class CUDAMemCpyKind(val kind : CUDAMemCpyKind.CUDACopyDirection)
extends ASTConstant(CUDAMemCpyKind.enumType, kind)
case object CUDAMemCpyHostToHost extends CUDAMemCpyKind(Host2Host)
case object CUDAMemCpyHostToDevice extends CUDAMemCpyKind(Host2Device)
case object CUDAMemCpyDeviceToHost extends CUDAMemCpyKind(Device2Host)
case object CUDAMemCpyDeviceToDevice extends CUDAMemCpyKind(Device2Device)

case object CUDAMemCpy extends ASTExternalFunction("cuda.h", "cudaMemcpy",
IntegerType, ArrayType(VoidType), ArrayType(VoidType), IntegerType, CUDAMemCpyKind.enumType)

case object CUDAMalloc extends ASTExternalFunction("cuda.h", "cudaMalloc", IntegerType,
ArrayType(ArrayType(VoidType)), IntegerType)

case object CUDAFree extends ASTExternalFunction("cuda.h", "cudaFree", IntegerType,
ArrayType(VoidType))


Loading

0 comments on commit 6eb49a6

Please sign in to comment.