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

Commit f31651a4 authored by Kunshan Wang's avatar Kunshan Wang
Browse files

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