Skip to content
Open
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions src/main/scala/lms/core/types.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package lms.core

import lms.core.Backend.{Const, Exp, Node, Sym}
import lms.core.TypeMap.TypeExp

import scala.collection.mutable


object Reflect {
def unapply(x: Any)(implicit tm: TypeMap): Option[(String, List[Exp])] = x match {
case s@Sym(_) =>
tm.g.findDefinition(s).map(n => (n.op, n.rhs.filter(_.isInstanceOf[Exp]).map(_.asInstanceOf[Exp])))
case _ => None
}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this extractor needed? Can't we use Def from GraphBuilder?


abstract class Type[T] {
def reify(x: Exp, tm: TypeMap): TypeExp
}

abstract class SimpleType[T] extends Type[T] {
def reify(x: Exp, tm: TypeMap): TypeExp = reify(tm)
def reify(tm: TypeMap): TypeExp
Comment thread
Angelogeb marked this conversation as resolved.
Outdated
}

object TypeExp {
def of[T](x: Exp)(implicit t: Type[T], typeMap: TypeMap): TypeExp = t.reify(x, typeMap)
}

object TypeNames extends Enumeration {
val Unit, Boolean, Char, Short, Int, Float, Double = Value
}

// Primitive types corresponding to Scala ones
object Types {

val Unit: TypeExp = Const(TypeNames.Unit)
val Boolean: TypeExp = Const(TypeNames.Boolean)
val Char: TypeExp = Const(TypeNames.Char)
val Short: TypeExp = Const(TypeNames.Short)
val Int: TypeExp = Const(TypeNames.Int)
val Float: TypeExp = Const(TypeNames.Float)
val Double: TypeExp = Const(TypeNames.Double)


object Array {
val name = "Array"

def apply(et: TypeExp)(implicit tm: TypeMap): TypeExp = tm.g.reflect(Array.name, et)

def unapply(x: Any)(implicit tm: TypeMap): Option[TypeExp] = x match {
case Reflect(Array.name, t :: Nil) => Some(t)
}
}

}

case class TypeMap(g: GraphBuilder) extends mutable.HashMap[Exp, TypeExp]

object TypeMap {

type TypeExp = Exp
def apply(): TypeMap = {
// TODO: should we have the same GraphBuilder for types
// and backend or can we refactor it
// to share some code?
val g = new GraphBuilder
// TODO: why is `g` initialized without any `curLocalDefs`?
// What about `curLocalReads` etc which are not used yet.
g.curLocalDefs = Set()
TypeMap(g)
}

}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting model -- I was thinking more along the lines of putting the HashMap into the GraphBuilder class. What are pros and cons?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current typeMap is part of the global state in Adapter. The new version of TypeMap probably won't have a GraphBuilder and will use the one that is used for reflecting terms Adapter.g.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A GraphBuilder object is not long-living. After a Graph is constructed, the GraphBuilder object is used to generate a Graph object, and then discarded. If we have a HashMap in the GraphBuilder, we might also need to save it to the Graph object. It might not be a bad idea.

So far we have been using Metadata to track additional information for Graph. When doing transformation, the handling of Metadata gets hacky (using Adapter.oldTypeMap, for instance). If this Metadata is part of the Graph, then that looks cleaner imo.

object ImplicitScalaTypes {
import Types._
implicit val mkUnitType: SimpleType[Unit] = _ => Unit
implicit val mkBooleanType: SimpleType[Boolean] = _ => Boolean
implicit val mkCharType: SimpleType[Char] = _ => Char
implicit val mkShortType: SimpleType[Short] = _ => Short
implicit val mkIntType: SimpleType[Int] = _ => Int
implicit val mkFloatType: SimpleType[Float] = _ => Float
implicit val mkDoubleType: SimpleType[Double] = _ => Double

implicit def mkArrayType[T](implicit te: SimpleType[T]): SimpleType[Array[T]] =
(tm: TypeMap) => Array(te.reify(tm))(tm)
}


// Types and IR nodes defined outside of LMS core
case class Tensor[T:Type](shape: Const)

object TensorType {
val name = "Tensor"

def apply(et: TypeExp, shape: TypeExp)(implicit tm: TypeMap): TypeExp = tm.g.reflect(TensorType.name, et, shape)

def unapply(x: Any)(implicit tm: TypeMap): Option[(TypeExp, TypeExp)] = x match {
case Reflect(TensorType.name, t :: shape :: Nil) => Some((t, shape))
}

implicit def mkTensorType[T:SimpleType]: Type[Tensor[T]] = (t: Exp, tm: TypeMap) => {
val deff = tm.g.findDefinition(t)
val shape = deff match {
case Some(Node(_, "Tensor", rhs, eff)) => Const(rhs.head)
case None => throw new Exception()
}
TensorType(implicitly[SimpleType[T]].reify(tm), shape)(tm)
}
}

object Main {

def main(args: Array[String]): Unit = {
import ImplicitScalaTypes._
import TensorType.mkTensorType
val ty = Types
implicit val tm: TypeMap = TypeMap()

def Wrap[A:Type](x: lms.core.Backend.Exp): Unit = tm(x) = TypeExp.of[A](x)

val s1 = Sym(1)
val s2 = Sym(2)
val s3 = tm.g.reflect("Tensor", Const(/* shape = */List(130, 20)))

Wrap[Array[Array[Float]]](s1)
Wrap[Array[Array[Int]]](s2)
Wrap[Tensor[Float]](s3)

tm(s1) match {
case ty.Array(ty.Array(t)) => println(s"element type $t")
}

tm(s2) match {
case ty.Array(ty.Array(ty.Int)) => println("is Int")
}

tm(s3) match {
case TensorType(et, shape) => println(s"Tensor type $et $shape")
}

println(tm)
println(tm.g.globalDefsCache)
}

}
Comment thread
Angelogeb marked this conversation as resolved.
Outdated