Commit f31651a4 authored by Kunshan Wang's avatar Kunshan Wang

Basic block and branching checks.

parent 0bc09644
......@@ -107,7 +107,7 @@ globalVar
;
funcBody
: '{' basicBlock* '}'
: '{' basicBlock+ '}'
;
basicBlock
......
......@@ -24,7 +24,7 @@ class Function extends GlobalVariable {
*/
class FuncVer extends IdentifiedSettable {
var func: Function = null
var bbs: Seq[BasicBlock] = null
var bbs: IndexedSeq[BasicBlock] = null
var entry: BasicBlock = null
def sig: FuncSig = func.sig
......@@ -33,9 +33,9 @@ class FuncVer extends IdentifiedSettable {
}
class BasicBlock extends IdentifiedSettable {
var norParams: Seq[NorParam] = null
var norParams: IndexedSeq[NorParam] = null
var excParam: Option[ExcParam] = null
var insts: Seq[Instruction] = null
var insts: IndexedSeq[Instruction] = null
var localVarNs: NestedNamespace[LocalVariable] = null // sub-namespace of allNs
var localInstNs: NestedNamespace[Instruction] = null // sub-namespace of allNs
......
......@@ -337,12 +337,12 @@ private[textinput] class InstanceUIRTextReader(idFactory: IDFactory, source: Str
param
}
bb.norParams = label.bbParam.map{ p =>
bb.norParams = label.bbParam.map { p =>
val param = mkNorParam(p.`type`(), p.name())
val sourceInfo = toSourceInfo(p)
addLocalVar(param, sourceInfo)
param
}
}.toIndexedSeq
bb.excParam = Option(label.excParam).map { p =>
val param = mkExcParam(p.name())
val sourceInfo = toSourceInfo(p)
......@@ -598,12 +598,12 @@ private[textinput] class InstanceUIRTextReader(idFactory: IDFactory, source: Str
return inst
}
bb.insts = bbCtx.inst.map(i => mkInst(bb, i))
bb.insts = bbCtx.inst.map(i => mkInst(bb, i)).toIndexedSeq
return bb
}
val bbs = fDefCtx.funcBody.basicBlock().map(makeBB)
val bbs = fDefCtx.funcBody.basicBlock().map(makeBB).toIndexedSeq
ver.bbs = bbs
ver.entry = bbs.head
......
......@@ -91,8 +91,12 @@ import uvm.ssavariables.AtomicRMWOptr.AtomicRMWOptr
/// Abstract instructions and traits
trait MaybeTerminator extends Instruction
trait Terminator extends MaybeTerminator
trait MaybeTerminator extends Instruction {
def canTerminate: Boolean
}
trait Terminator extends MaybeTerminator {
override def canTerminate: Boolean = true
}
trait OSRPoint extends Instruction
......@@ -115,6 +119,7 @@ case class ExcClause(val nor: DestClause, val exc: DestClause)
trait HasExcClause extends Instruction with MaybeTerminator {
var excClause: Option[ExcClause]
override def canTerminate: Boolean = excClause.isDefined
}
trait HasKeepAliveClause extends Instruction {
......
......@@ -31,6 +31,9 @@ class StaticAnalyzer {
checkTypes()
checkSigs()
checkConsts()
checkGlobals()
checkExpFuncs()
checkFuncs()
}
def checkTypes(): Unit = {
......@@ -234,6 +237,119 @@ class StaticAnalyzer {
val prettyMsgs = pretty.map(o => lookupSourceInfo(o).prettyPrint())
new StaticCheckingException("%s\n%s".format(msg, prettyMsgs.mkString("\n")), cause)
}
def checkGlobals(): Unit = {
for (g <- bundle.globalCellNs.all) {
g.cellTy match {
case ty: TypeVoid => throw error("Global cell %s: Global cell cannot have void type.".format(g.repr),
pretty = Seq(g, ty))
case ty: TypeHybrid => throw error("Global cell %s: Global cell cannot have hybrid type.".format(g.repr),
pretty = Seq(g, ty))
}
}
}
def checkExpFuncs(): Unit = {
for (ef <- bundle.expFuncNs.all) {
ef.cookie.constTy match {
case TypeInt(64) =>
case ty => throw error("Exposed function %s: cookie must be a 64-bit int. %s found.".format(ef.repr, ty.repr),
pretty = Seq(ef, ty))
}
}
}
def checkFuncs(): Unit = {
for (fv <- bundle.funcVerNs.all) {
checkFuncVer(fv)
}
}
def checkFuncVer(fv: FuncVer): Unit = {
val sig = fv.sig
val fsig = fv.func.sig
if (fsig.paramTys.length != sig.paramTys.length || fsig.retTys.length != sig.retTys.length) {
throw error("Function version %s has different parameter or return value arity as its function %s".format(
fv.repr, fv.func.repr), pretty = Seq(fv, sig, fv.func, fsig))
}
val entry = fv.entry
if (entry.norParams.length != sig.paramTys.length) {
throw error("Function version %s: the entry block has %d parameters, but the function takes %s parameters."
.format(fv.repr, entry.norParams.length, sig.paramTys.length),
pretty = Seq(fv, entry, sig))
}
if (entry.excParam.isDefined) {
throw error("Function version %s: the entry block should not have exceptional parameter."
.format(fv.repr),
pretty = Seq(fv, entry))
}
for (bb <- fv.bbs) {
checkBasicBlock(fv, entry, bb)
}
}
def checkBasicBlock(fv: FuncVer, entry: BasicBlock, bb: BasicBlock): Unit = {
if (bb.insts.isEmpty) {
throw error("Function version %s: basic block %s is empty"
.format(fv.repr, bb.repr),
pretty = Seq(fv, bb))
}
val lastInst = bb.insts.last match {
case i: MaybeTerminator if i.canTerminate => i
case i => throw error("FuncVer %s BB %s: The last instruction %s is not a valid basic block terminator"
.format(fv.repr, bb.repr, i.repr),
pretty = Seq(fv, bb, i))
}
for ((dest, isNormal) <- bbDests(lastInst)) {
if (dest.bb == entry) {
throw error("FuncVer %s BB %s Inst %s: Cannot branch to the entry block"
.format(fv.repr, bb.repr, lastInst.repr),
pretty = Seq(fv, bb, lastInst))
}
val destBB = dest.bb
val nParams = destBB.norParams.length
val nArgs = dest.args.length
if (nParams != nArgs) {
throw error(("FuncVer %s BB %s Inst %s: Destination %s has %d normal parameters, but %d arguments found.\n" +
"DestClause: %s")
.format(fv.repr, bb.repr, lastInst.repr, destBB.repr, nParams, nArgs, dest),
pretty = Seq(lastInst, destBB))
}
if (isNormal) {
if (destBB.excParam.isDefined) {
throw error(("FuncVer %s BB %s Inst %s: Normal destination %s should not have exceptional parameter.\n" +
"DestClause: %s")
.format(fv.repr, bb.repr, lastInst.repr, destBB.repr, dest),
pretty = Seq(lastInst, destBB))
}
} else {
if (!destBB.excParam.isDefined) {
throw error(("FuncVer %s BB %s Inst %s: Exceptional destination %s must have exceptional parameter.\n" +
"DestClause: %s")
.format(fv.repr, bb.repr, lastInst.repr, destBB.repr, dest),
pretty = Seq(lastInst, destBB))
}
}
}
}
def bbDests(lastInst: MaybeTerminator): Seq[(DestClause, Boolean)] = lastInst match {
case i: InstBranch => Seq(i.dest).map(d => (d, true))
case i: InstBranch2 => Seq(i.ifTrue, i.ifFalse).map(d => (d, true))
case i: InstSwitch => i.cases.map(_._2).map(d => (d, true))
case i: InstTailCall => Seq()
case i: InstRet => Seq()
case i: InstThrow => Seq()
case i: InstWatchPoint => Seq(i.dis, i.ena).map(d => (d, true)) ++ i.exc.map(d => (d, false)).toSeq
case i: InstWPBranch => Seq(i.dis, i.ena).map(d => (d, true))
case i: HasExcClause => i.excClause.map(e => Seq((e.nor, true), (e.exc, false))).getOrElse(Seq())
}
}
}
......
......@@ -192,4 +192,98 @@ class StaticAnalysisTest extends FlatSpec with Matchers {
.const @C <@s> = { @C @C @C }
""")
}
it should "complain if a global cell is void" in {
catchExceptionWhenAnalyzing("""
.typedef @void = void
.global @g <@void>
""")
}
it should "complain if a global cell is hybrid" in {
catchExceptionWhenAnalyzing("""
.typedef @i64 = int<64>
.typedef @h = hybrid<@i64>
.global @g <@h>
""")
}
it should "complain if an exposed function's cookie is not int64" in {
catchExceptionWhenAnalyzing("""
.typedef @i32 = int<32>
.const @c <@i32> = 42
.funcsig @sig = () -> ()
.funcdecl @f <@sig>
.expose @fe = @f #DEFAULT @c
""")
}
it should "complain if the entry block has the wrong number of arguments" in {
catchExceptionWhenAnalyzing("""
.typedef @i32 = int<32>
.funcsig @sig = (@i32 @i32 @i32) -> ()
.funcdef @f VERSION %v1 <@sig> {
%entry(<@i32> %x <@i32> %y):
RET ()
}
""")
}
it should "complain if attempt to branch to entry" in {
catchExceptionWhenAnalyzing("""
.funcsig @sig = () -> ()
.funcdef @f VERSION %v1 <@sig> {
%entry():
BRANCH %b1()
%b1():
BRANCH %entry()
}
""")
}
it should "complain if branching with the wrong number of arguments" in {
catchExceptionWhenAnalyzing("""
.typedef @i32 = int<32>
.const @1 <@i32> = 1
.funcsig @sig = () -> ()
.funcdef @f VERSION %v1 <@sig> {
%entry():
BRANCH %b1(@1)
%b1():
RET ()
}
""")
}
it should "complain if an exceptional dest does not have exc param" in {
catchExceptionWhenAnalyzing("""
.typedef @i32 = int<32>
.const @1 <@i32> = 1
.funcsig @sig = () -> ()
.funcdef @f VERSION %v1 <@sig> {
%entry():
CALL <@sig> @f () EXC(%b1() %b2())
%b1():
RET ()
%b2():
RET ()
}
""")
}
it should "complain if a normal dest has exc param" in {
catchExceptionWhenAnalyzing("""
.typedef @i32 = int<32>
.const @1 <@i32> = 1
.funcsig @sig = () -> ()
.funcdef @f VERSION %v1 <@sig> {
%entry():
CALL <@sig> @f () EXC(%b1() %b2())
%b1() [%e]:
RET ()
%b2() [%e]:
RET ()
}
""")
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment