To protect your data, the CISO officer has suggested users to enable GitLab 2FA as soon as possible.

Commit c161413e authored by Adam R. Nelson's avatar Adam R. Nelson
Browse files

Added pre-SSA to SSA conversion

parent 4c11b17c
......@@ -3,39 +3,36 @@ lazy val genSrc = taskKey[List[File]]("generate sources")
genSrc <<= (sourceGenerators in Compile) { _.join.map(_.flatten.toList) }
lazy val root = (project in file(".")).settings(
organization := "org.microvm",
organization := "org.microvm"
name := "microvm-refimpl2",
version := "2.1.0",
name := "microvm-refimpl2"
description := "The second reference implementation of Mu, the micro virtual machine",
version := "2.1.0"
licenses := Seq("CC BY-SA 4.0" -> url("https://creativecommons.org/licenses/by-sa/4.0/legalcode")),
description := "The second reference implementation of Mu, the micro virtual machine"
scalaVersion := "2.11.7",
licenses := Seq("CC BY-SA 4.0" -> url("https://creativecommons.org/licenses/by-sa/4.0/legalcode"))
libraryDependencies ++= Seq(
"org.antlr" % "antlr4" % "4.5.1-1",
"com.typesafe.scala-logging" %% "scala-logging" % "3.1.0",
"ch.qos.logback" % "logback-classic" % "1.1.3",
"com.github.jnr" % "jnr-ffi" % "2.0.7",
"com.github.jnr" % "jffi" % "1.2.10",
"com.github.jnr" % "jnr-posix" % "3.0.23",
"org.scalatest" %% "scalatest" % "2.2.4" % "test",
"junit" % "junit" % "4.12" % "test"
),
scalaVersion := "2.11.7"
testOptions in Test += Tests.Argument("-oF"), // print full stack trace when testing
antlr4Settings,
libraryDependencies ++= Seq(
"org.antlr" % "antlr4" % "4.5.1",
"com.typesafe.scala-logging" %% "scala-logging" % "3.1.0",
"ch.qos.logback" % "logback-classic" % "1.1.3",
"com.github.jnr" % "jnr-ffi" % "2.0.7",
"com.github.jnr" % "jffi" % "1.2.10",
"com.github.jnr" % "jnr-posix" % "3.0.23",
"org.scalatest" %% "scalatest" % "2.2.4" % "test",
"junit" % "junit" % "4.12" % "test"
)
antlr4PackageName in Antlr4 := Some("uvm.ir.textinput.gen"),
testOptions in Test += Tests.Argument("-oF") // print full stack trace when testing
antlr4GenListener in Antlr4 := false,
antlr4Settings
antlr4GenVisitor in Antlr4 := false
)
antlr4PackageName in Antlr4 := Some("uvm.ir.textinput.gen")
antlr4GenListener in Antlr4 := false
antlr4GenVisitor in Antlr4 := false
package uvm.clientsupport.text
import java.util
import scala.collection.JavaConversions._
/**
* A Mu IR bundle. This class is more representation-oriented rather than semantic oriented. For example, there are
* "type definitions" rather than "types". Similarly "function definitions" and "function declarations" are separated
* rather than contained in each other.
*/
class Bundle(
cTypeDefs: TraversableOnce[(TypeName, TypeCtor)],
cFuncSigDefs: TraversableOnce[(FuncSigName, FuncSig)],
cConstDefs: TraversableOnce[(GlobalVarName, Const)],
cGlobalCellDefs: TraversableOnce[(GlobalVarName, TypeName)],
cFuncDecls: TraversableOnce[(GlobalVarName, FuncSigName)],
cFuncVers: TraversableOnce[(FuncVerName, PostFuncVer)],
cFuncExpDefs: TraversableOnce[(GlobalVarName, Expose)],
cComments: TraversableOnce[(GlobalName, String)]
) {
private def buildMap[K, V](seq: TraversableOnce[(K, V)]): util.Map[K, V] = {
val map = new util.LinkedHashMap[K, V]
seq foreach { case (k, v) => map.put(k, v) }
map
}
lazy val typeDefs = buildMap(cTypeDefs)
lazy val funcSigDefs = buildMap(cFuncSigDefs)
lazy val constDefs = buildMap(cConstDefs)
lazy val globalCellDefs = buildMap(cGlobalCellDefs)
lazy val funcDecls = buildMap(cFuncDecls)
lazy val funcVers = buildMap(cFuncVers)
lazy val funcExpDefs = buildMap(cFuncExpDefs)
lazy val comments = buildMap(cComments)
override lazy val toString = {
val sb = new StringBuilder
def printBlock[N <: GlobalName, V](block: util.Map[N, V])(fn: (N, V) => Unit): Unit =
if (!block.isEmpty) {
sb append "\n"
block foreach { case (name, value) =>
Option(comments get name) foreach (_ split '\n' map ("// " + _ + "\n") foreach sb.append)
fn(name, value)
}
}
printBlock(typeDefs)((name, ctor) => sb append s".typedef $name = $ctor\n")
printBlock(funcSigDefs)((name, sig) => sb append s".funcsig $name = $sig\n")
printBlock(constDefs)((name, const) => sb append s".const $name <${const.ty}> = $const\n")
printBlock(globalCellDefs)((name, ty) => sb append s".global $name <$ty>\n")
printBlock(funcDecls)((name, sig) => sb append s".funcdecl $name <$sig>\n")
printBlock(funcExpDefs)((name, exp) => sb append s".expose $name = $exp\n")
printBlock(funcVers)((name, ver) =>
sb append s".funcdef ${ver.func} VERSION $name <${ver.sig}> $ver\n\n")
sb.toString
}
}
package uvm.clientsupport.text
import java.util
import scala.collection.JavaConversions
class IList[T](members: TraversableOnce[T]) extends util.AbstractList[T] with Traversable[T] {
def this(membersIterable: java.lang.Iterable[T]) =
this(JavaConversions iterableAsScalaIterable membersIterable)
private val vector = members.toVector
override def get(index: Int): T = vector(index)
override def size: Int = vector.length
override def isEmpty: Boolean = vector.isEmpty
override def toString = vector mkString " "
override def foreach[U](f: (T) => U): Unit = vector foreach f
def apply(idx: Int): T = vector(idx)
}
object IList {
def apply[T](members: T*): IList[T] = new IList(members)
def empty[T]: IList[T] = new IList[T](Nil)
}
package uvm.clientsupport.text
import java.util
import scala.collection.{JavaConversions, mutable}
import scala.annotation.tailrec
import scala.collection.mutable
import scala.collection.JavaConversions._
import java.util
class BundleBuilder(baseName: String) {
protected val typeDefs = mutable.AnyRefMap.empty[TypeName, TypeCtor]
protected val funcSigDefs = mutable.AnyRefMap.empty[FuncSigName, FuncSig]
protected val constDefs = mutable.AnyRefMap.empty[GlobalVarName, Const]
protected val globalCellDefs = mutable.AnyRefMap.empty[GlobalVarName, TypeName]
protected val funcDecls = mutable.AnyRefMap.empty[GlobalVarName, FuncSigName]
protected val funcVers = mutable.AnyRefMap.empty[FuncVerName, FunctionBuilder]
protected val funcExpDefs = mutable.AnyRefMap.empty[GlobalVarName, Expose]
protected val comments = mutable.AnyRefMap.empty[GlobalName, String]
class BundleBuilder(baseName: String) extends Context {
protected val typeDefs = new util.LinkedHashMap[TypeName, TypeCtor]
protected val funcSigDefs = new util.LinkedHashMap[FuncSigName, FuncSig]
protected val constDefs = new util.LinkedHashMap[GlobalVarName, Const]
protected val globalCellDefs = new util.LinkedHashMap[GlobalVarName, TypeName]
protected val funcDecls = new util.LinkedHashMap[GlobalVarName, FuncSigName]
protected val funcVers = new util.LinkedHashMap[FuncVerName, FunctionBuilder]
protected val funcExpDefs = new util.LinkedHashMap[GlobalVarName, Expose]
protected val comments = new util.LinkedHashMap[GlobalName, String]
protected val usedNames = mutable.Set.empty[String]
private var nextTypeNumber = 0
......@@ -23,6 +23,15 @@ class BundleBuilder(baseName: String) {
private var typePrefix = baseName
private var constPrefix = baseName
override def resolve(typeName: TypeName) = {
typeDefs.get(typeName) match {
case next: TypeName => resolve(next)
case res => Option(res)
}
}
override def resolve(funcSigName: FuncSigName) =
Option(funcSigDefs.get(funcSigName))
// Name generation
// ------------------------------------------------------------
......@@ -86,46 +95,45 @@ class BundleBuilder(baseName: String) {
private def descriptiveTypeName(ctor: TypeCtor): TypeName = ctor match {
case TypeCtor.Int(n) => newTypeName("i" + n)
case TypeCtor.Float() => newTypeName("float")
case TypeCtor.Double() => newTypeName("double")
case TypeCtor.Stack() => newTypeName("stack")
case TypeCtor.Thread() => newTypeName("thread")
case TypeCtor.Void() => newTypeName("void")
case TypeCtor.Ref(TypeCtor.Void()) => newTypeName("voidref")
case TypeCtor.IRef(TypeCtor.Void()) => newTypeName("voidiref")
case TypeCtor.Ptr(TypeCtor.Void()) => newTypeName("voidptr")
case TypeCtor.Singletons.Float => newTypeName("float")
case TypeCtor.Singletons.Double => newTypeName("double")
case TypeCtor.Singletons.StackRef => newTypeName("stack")
case TypeCtor.Singletons.ThreadRef => newTypeName("thread")
case TypeCtor.Singletons.Void => newTypeName("void")
case TypeCtor.Ref(TypeCtor.Singletons.Void) => newTypeName("voidref")
case TypeCtor.IRef(TypeCtor.Singletons.Void) => newTypeName("voidiref")
case TypeCtor.UPtr(TypeCtor.Singletons.Void) => newTypeName("voidptr")
case TypeCtor.Ref(TypeCtor.Int(n)) => newTypeName("i" + n + "ref")
case TypeCtor.IRef(TypeCtor.Int(n)) => newTypeName("i" + n + "iref")
case TypeCtor.Ptr(TypeCtor.Int(n)) => newTypeName("i" + n + "ptr")
case TypeCtor.UPtr(TypeCtor.Int(n)) => newTypeName("i" + n + "ptr")
case _ => newTypeName()
}
def typeDef(ctor: TypeCtor): TypeName =
typeDefs collectFirst { case (name, existing) if ctor == existing => name } getOrElse
typeDef(descriptiveTypeName(ctor), ctor)
override def typeDef(ctor: TypeCtor): TypeName = ctor match {
case name: TypeName if resolve(name).isDefined => typeDef(resolve(name).get)
case _ =>
typeDefs collectFirst {
case (name, existing) if ctor == existing => name
} getOrElse
typeDef (descriptiveTypeName (ctor), ctor)
}
def typeDef(name: TypeName, ctor: TypeCtor): TypeName = {
typeDefs.put(name, ctor)
name
}
def funcSig(ret: TypeName, args: Traversable[TypeName]): FuncSigName =
override def funcSig(args: IList[TypeName], ret: IList[TypeName]): FuncSigName =
funcSigDefs collectFirst {
case (name, sig) if sig.retTy == ret && sig.paramTy.toSeq == args.toSeq => name
} getOrElse funcSig(newFuncSigName(), ret, args)
def funcSig(ret: TypeName, args: util.List[TypeName]): FuncSigName =
funcSig(ret, JavaConversions.asScalaBuffer(args))
} getOrElse funcSig(newFuncSigName(), args, ret)
def funcSig(name: FuncSigName, ret: TypeName, args: Traversable[TypeName]): FuncSigName = {
funcSigDefs.put(name, new FuncSig(ret, args))
def funcSig(name: FuncSigName, args: IList[TypeName], ret: IList[TypeName]): FuncSigName = {
funcSigDefs.put(name, new FuncSig(args, ret))
name
}
def funcSig(name: FuncSigName, ret: TypeName, args: util.List[TypeName]): FuncSigName =
funcSig(name, ret, JavaConversions.asScalaBuffer(args))
def constDef(c: Const): GlobalVarName =
override def constDef(c: Const): GlobalVarName =
constDefs collectFirst { case (name, existing) if c == existing => name } getOrElse {
var nextName: String = null
do {
......@@ -164,67 +172,40 @@ class BundleBuilder(baseName: String) {
def newFuncVersion(
funcName: GlobalVarName,
retTy: TypeName,
paramTy: Traversable[TypeName]
): FunctionBuilder = newFuncVersion(funcName, newFuncVerName(funcName), retTy, paramTy)
def newFuncVersion(
funcName: GlobalVarName,
retTy: TypeName,
paramTy: util.List[TypeName]
): FunctionBuilder = newFuncVersion(funcName, retTy, JavaConversions asScalaBuffer paramTy)
paramTy: IList[TypeName],
retTy: IList[TypeName]
): FunctionBuilder = newFuncVersion(funcName, newFuncVerName(funcName), paramTy, retTy)
def newFuncVersion(
funcName: GlobalVarName,
versionName: FuncVerName,
retTy: TypeName,
paramTy: Traversable[TypeName]
paramTy: IList[TypeName],
retTy: IList[TypeName]
): FunctionBuilder = {
val sig = funcSig(retTy, paramTy)
val sig = funcSig(paramTy, retTy)
val builder = new FunctionBuilderImpl(
this, funcName, sig, versionName, paramTy.toSeq.indices.map("param"+_))
this, funcName, sig, versionName, new IList(paramTy.toSeq.indices.map("param"+_)))
funcVers.put(versionName, builder)
builder
}
def newFuncVersion(
funcName: GlobalVarName,
versionName: FuncVerName,
retTy: TypeName,
paramTy: util.List[TypeName]
): FunctionBuilder = newFuncVersion(funcName, versionName, retTy, JavaConversions asScalaBuffer paramTy)
def newFuncVersion(
funcName: GlobalVarName,
sigName: FuncSigName,
paramNames: Traversable[String]
paramNames: IList[String]
): FunctionBuilder = newFuncVersion(funcName, sigName, newFuncVerName(funcName), paramNames)
def newFuncVersion(
funcName: GlobalVarName,
sigName: FuncSigName,
paramNames: util.List[String]
): FunctionBuilder = newFuncVersion(funcName, sigName, JavaConversions asScalaBuffer paramNames)
def newFuncVersion(
funcName: GlobalVarName,
sigName: FuncSigName,
versionName: FuncVerName,
paramNames: Traversable[String]
paramNames: IList[String]
): FunctionBuilder = {
val builder = new FunctionBuilderImpl(this, funcName, sigName, versionName, paramNames)
funcVers.put(versionName, builder)
builder
}
def newFuncVersion(
funcName: GlobalVarName,
sigName: FuncSigName,
versionName: FuncVerName,
paramNames: util.List[String]
): FunctionBuilder =
newFuncVersion(funcName, sigName, versionName, JavaConversions asScalaBuffer paramNames)
def comment(name: GlobalName, comment: String): Unit = comments.put(name, comment)
def build(): Bundle = new Bundle(
......@@ -233,41 +214,39 @@ class BundleBuilder(baseName: String) {
cConstDefs = constDefs,
cGlobalCellDefs = globalCellDefs,
cFuncDecls = funcDecls,
cFuncVers = funcVers.mapValues(_.build()),
cFuncVers = funcVers map { case (k, v) => k -> v.build() },
cFuncExpDefs = funcExpDefs,
cComments = comments
)
}
trait FunctionBuilder {
trait FunctionBuilder extends Context {
def functionName: GlobalVarName
def functionSignature: FuncSigName
def versionName: FuncVerName
def paramNames: util.List[LocalVarName]
def paramName(index: Int): LocalVarName
def paramNames: IList[LocalVarName]
def newVarName(): LocalVarName
def newVarName(name: String): LocalVarName
def newLabelName(): LabelName
def newLabelName(name: String): LabelName
def typeDef(ctor: TypeCtor): TypeName
def constDef(c: Const): VarName
def currentBlockLabel: LabelName
def startNewBlock(): LabelName
def startNewBlock(label: LabelName): LabelName
def inst(inst: NameableInst): LocalVarName
def inst(name: LocalVarName, inst: Inst): LocalVarName
def build(): FuncVer
def inst(inst: PreInst): LocalVarName
def inst(names: IList[LocalVarName], inst: PreInst): Unit
def preBuild(): PreFuncVer
def build(): PostFuncVer
}
private[text] class FunctionBuilderImpl(
bundleBuilder: BundleBuilder,
context: Context,
override val functionName: GlobalVarName,
override val functionSignature: FuncSigName,
override val versionName: FuncVerName,
cParamNames: TraversableOnce[String]
val paramNameBases: IList[String]
) extends FunctionBuilder {
type Block = (LabelName, mutable.ArrayBuffer[NameableInst])
type Block = (LabelName, mutable.ArrayBuffer[(IList[LocalVarName], PreInst)])
protected val blocks = mutable.ArrayBuffer.empty[Block]
protected val usedNames = mutable.Set.empty[String]
......@@ -275,9 +254,7 @@ private[text] class FunctionBuilderImpl(
private var nextLabelNumber = 0
private var currentBlock: Block = createBlock(newLabelName("entry"))
override lazy val paramNames = Helpers.buildList(cParamNames map newVarName)
override def paramName(index: Int): LocalVarName = paramNames get index
override lazy val paramNames = new IList(paramNameBases map newVarName)
private def generateName(n: String): String = {
var n_ = n
......@@ -308,13 +285,9 @@ private[text] class FunctionBuilderImpl(
override def newLabelName(name: String): LabelName = LabelName(generateName(name))
override def typeDef(ctor: TypeCtor): TypeName = bundleBuilder.typeDef(ctor)
override def constDef(c: Const): VarName = bundleBuilder.constDef(c)
private def createBlock(name: LabelName): Block = {
val newBlock = (name, mutable.ArrayBuffer.empty[NameableInst])
blocks add newBlock
val newBlock = (name, mutable.ArrayBuffer.empty[(IList[LocalVarName], PreInst)])
blocks += newBlock
newBlock
}
......@@ -327,24 +300,211 @@ private[text] class FunctionBuilderImpl(
label
}
override def inst(inst: NameableInst): LocalVarName = inst match {
case NamedInst(name, _) =>
currentBlock._2 add inst
name
case i: Inst =>
override def inst(inst: PreInst): LocalVarName = {
val name = newVarName()
currentBlock._2 add NamedInst(name, i)
currentBlock._2 += (IList(name) -> inst)
name
}
override def inst(name: LocalVarName, inst: Inst): LocalVarName = {
currentBlock._2 add NamedInst(name, inst)
name
override def inst(names: IList[LocalVarName], inst: PreInst): Unit = {
currentBlock._2 += (names -> inst)
}
override def build(): FuncVer = new FuncVer(
override def preBuild() = new PreFuncVer(
functionName, functionSignature, paramNames,
blocks map { case (name, insts) => new BasicBlock(name, insts) }
new IList(blocks map { case (name, insts) => new PreBasicBlock(name, new IList(insts)) })
)
def convertFunction(preFunc: PreFuncVer)(implicit context: Context): PostFuncVer = {
// Step 1: Cache some data about each block (incoming jumps, local variable names, etc.)
case class BlockData(
incoming: Set[LabelName],
locals: Map[LocalVarName, () => TypeCtor],
args: mutable.Map[LocalVarName, Option[TypeCtor]])
val blockData = blocks.map {
case (label, insts) =>
val locals = (for ((labels, inst) <- insts; (l, i) <- labels.toSeq.zipWithIndex) yield {
lazy val x = context.resolve(inst.returnTypes get i).get
l -> (() => x)
}).toMap
label -> BlockData(
incoming = blocks.filter(_._2 flatMap (_._2.possibleJumps) contains label).map(_._1).toSet,
locals = locals,
args = mutable.Map((for ((_, inst) <- insts; label <- inst.referencedVars) yield label) collect {
case v: LocalVarName if !locals.contains(v) => v -> None
}: _*)
)
}.toMap
for ((name, ty) <- paramNames.toSeq zip resolve(functionSignature).get.paramTy.toSeq) {
blockData(blocks(0)._1).args.put(name, Some(ty))
}
// Step 2: Determine block arities and argument types
// Iteratively propagate passthrough args and arg types until a fixpoint is reached.
var continue = true
while (continue) {
continue = false
for (
(to, BlockData(froms, _, args)) <- blockData;
from <- froms;
BlockData(_, fromLocals, fromArgs) = blockData(from);
arg <- args.keys
) {
(fromLocals ++ fromArgs.collect{case (k, Some(v)) => (k, () => v)}).get(arg) match {
case Some(fn) =>
val ctor = fn()
args(arg) match {
case Some(current) if ctor == current => // Do nothing.
case Some(current) =>
// FIXME: This may throw unnecessary exceptions in the case of TypeNames inside TypeCtors
// For example: iref<@a> and iref<@b> would be a type mismatch even if @a and @b
// are the same type. Context.resolve doesn't check nested names yet.
throw new IllegalStateException(
s"Pre-SSA variable type mismatch for $arg in ($from, $to): $ctor != $current")
case None =>
continue = true
args.put(arg, Some(ctor))
}
case None =>
if (!(fromArgs contains arg)) {
continue = true
fromArgs.put(arg, None)
}
}
}
}
// Step 3: Process preinsts into postinsts, renaming reused labels along the way.
val newBlocks = blocks map { case (label, insts) =>
import Inst._
val BlockData(_, locals, args) = blockData(label)
val used = mutable.Set(args.keys.toSeq: _*)
val replacements = mutable.Map.empty[LocalVarName, VarName]
@tailrec def gensym(n: Int = 0): LocalVarName = {
val sym = LocalVarName(s"v$n")
if ((used contains sym) || (locals contains sym)) gensym(n+1) else {
used += sym
sym
}
}
@tailrec def r(v: VarName): VarName = v match {
case lv: LocalVarName => replacements get lv match {
case Some(lv2) => r(lv2)
case None => v
}
case _ => v
}
def l(n: LocalVarName): LocalVarName =
if (used contains n) {
val n2 = gensym()
replacements.put(n, n2)
n2
} else { used += n; n }
def mapKeepAlive(ka: Option[KeepAliveClause]) =
ka map (c => KeepAliveClause(new IList(c.vars map r collect {case lv: LocalVarName => lv})))
def mapNewStackClause(c: NewStackClause): NewStackClause = c match {
case NewStackClause.PassValue(argTy, arg) => NewStackClause.PassValue(argTy, r(arg))
case i: NewStackClause.PassVoid => i
case NewStackClause.ThrowExc(exc) => NewStackClause.ThrowExc(r(exc))
}
def mapDest(lbl: LabelName): DestClause =
DestClause(lbl, new IList(blockData(lbl).args.keys.toSeq.sortBy(_.name).map(r)))
def mapInst(names: Traversable[LocalVarName], inst: PreInst):
Option[(IList[LocalVarName], PostInst)] = inst match {
case ppinst: PrePostInst =>
val postInst: PostInst = ppinst match {
case BinOp(op, opndTy, op1, op2) => BinOp(op, opndTy, r(op1), r(op2))
case Cmp(op, opndTy, op1, op2) => Cmp(op, opndTy, r(op1), r(op2))
case Conv(op, fromTy, toTy, opnd) => Conv(op, fromTy, toTy, r(opnd))
case Select(condTy, opndTy, cond, ifTrue, ifFalse) =>
Select(condTy, opndTy, r(cond), r(ifTrue), r(ifFalse))
case Call(sig, callee, argList, ka) =>
Call(sig, r(callee), new IList(argList map r), mapKeepAlive(ka))
case TailCall(sig, callee, argList) =>
TailCall(sig, r(callee), new IList(argList map r))
case Ret(rv) => Ret(new IList(rv map r))
case i: RetVoid => i
case Throw(exc) => Throw(r(exc))
case i: LandingPad => i
case ExtractValue(strTy, index, opnd) => ExtractValue(strTy, index, r(opnd))
case InsertValue(strTy, index, opnd, newVal) =>
InsertValue(strTy, index, r(opnd), r(newVal))
case ExtractElement(vecTy, indTy, opnd, index) =>
ExtractElement(vecTy, indTy, r(opnd), r(index))
case InsertElement(vecTy, indTy, opnd, index, newVal) =>
InsertElement(vecTy, indTy, r(opnd), r(index), r(newVal))
case ShuffleVector(vecTy, maskTy, vec1</