GitLab will continue to be upgraded from 11.4.5-ce.0 on November 25th 2019 at 4.00pm (AEDT) to 5.00pm (AEDT) due to Critical Security Patch Availability. During the update, GitLab and Mattermost services will not be available.

Commit 886a718f authored by Kunshan Wang's avatar Kunshan Wang

Call and return checks

parent 55fdbd3c
......@@ -117,6 +117,10 @@ class StaticAnalyzer {
}
}
def sigArityMatch(sig1: FuncSig, sig2: FuncSig): Boolean = {
sig1.paramTys.length == sig2.paramTys.length && sig1.retTys.length == sig2.retTys.length
}
def checkConsts(): Unit = {
for (c <- bundle.constantNs.all) {
checkScalarConst(c)
......@@ -269,7 +273,7 @@ class StaticAnalyzer {
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) {
if (!sigArityMatch(sig, fsig)) {
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))
}
......@@ -284,7 +288,7 @@ class StaticAnalyzer {
if (entry.excParam.isDefined) {
throw error("Function version %s: the entry block should not have exceptional parameter."
.format(fv.repr),
pretty = Seq(fv, entry))
pretty = Seq(entry))
}
for (bb <- fv.bbs) {
......@@ -296,41 +300,70 @@ class StaticAnalyzer {
if (bb.insts.isEmpty) {
throw error("Function version %s: basic block %s is empty"
.format(fv.repr, bb.repr),
pretty = Seq(fv, bb))
pretty = Seq(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))
pretty = Seq(i))
}
implicit val _fv = fv
implicit val _bb = bb
implicit val _i: Instruction = lastInst
lastInst match {
case i: InstRet => {
val retVals = i.retVals
val nrv = retVals.length
val nrvSig = fv.sig.retTys.length
if (nrv != nrvSig) {
throw errorFBI("Returning wrong number of values. expected: %d, found: %d"
.format(nrvSig, nrv),
pretty = Seq(i, fv, fv.sig))
}
}
case _ =>
}
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))
pretty = Seq(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" +
throw errorFBI(("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),
.format(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" +
throw errorFBI(("Normal destination %s should not have exceptional parameter.\n" +
"DestClause: %s")
.format(fv.repr, bb.repr, lastInst.repr, destBB.repr, dest),
.format(destBB.repr, dest),
pretty = Seq(lastInst, destBB))
}
}
}
for (i <- bb.insts.init) {
checkInst(fv, bb, i)
}
}
/** Error in a funcver, basic block and an instruction. */
def errorFBI(msg: String, pretty: Seq[AnyRef] = Seq(), cause: Throwable = null)(
implicit fv: FuncVer, bb: BasicBlock, inst: Instruction): StaticCheckingException = {
val appendedMsg = msg + ("\nIn FuncVer %s BB %s Inst %s".format(fv.repr, bb.repr, inst.repr))
error(appendedMsg, pretty, cause)
}
def bbDests(lastInst: MaybeTerminator): Seq[(DestClause, Boolean)] = lastInst match {
......@@ -344,6 +377,38 @@ class StaticAnalyzer {
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())
}
def checkInst(fv: FuncVer, bb: BasicBlock, i: Instruction): Unit = {
implicit val _fv = fv
implicit val _bb = bb
implicit val _inst = i
i match {
case i: MaybeTerminator if i.canTerminate =>
throw errorFBI("Instruction %s is a terminator and must be the last instruction of a basic block."
.format(i.repr),
pretty = Seq(i))
case i: CallLike => {
i match {
case c: AbstractCall =>
i.callee match {
case sf: Function if (!sigArityMatch(i.sig, sf.sig)) =>
throw error("Static callee %s has different parameter or return value arity as expected by the call site %s".format(
sf.repr, i), pretty = Seq(i, i.sig, sf, sf.sig))
case _ => // Only check for static call sites
}
case _ => // Only check Mu-to-Mu calls, not CCALL
}
val nargs = i.argList.length
val nparams = i.sig.paramTys.length
if (nargs != nparams) {
throw errorFBI("Call site %s has %d arguments, but %d parameters are expected.\nInstruction: %s"
.format(i.repr, nargs, nparams, i.toString),
pretty = Seq(i, i.sig))
}
}
case _ =>
}
}
}
}
......
......@@ -33,8 +33,7 @@ class StaticAnalysisTest extends FlatSpec with Matchers {
new StaticAnalyzer().checkBundle(b, Some(gb))
fail()
} catch {
case e: TestFailedException => throw e
case e: Exception => // expected
case e: StaticCheckingException => // expected
e.printStackTrace()
}
......@@ -270,4 +269,57 @@ class StaticAnalysisTest extends FlatSpec with Matchers {
}
""")
}
it should "complain if a terminating instruction is in the middle of a basic block" in {
catchExceptionWhenAnalyzing("""
.funcsig @sig = () -> ()
.funcdef @f VERSION %v1 <@sig> {
%entry():
COMMINST @uvm.thread_exit
RET ()
}
""")
}
it should "complain if a call site has the wrong number of argument" in {
catchExceptionWhenAnalyzing("""
.typedef @i64 = int<64>
.funcsig @g.sig = (@i64 @i64 @i64) -> ()
.funcdecl @g <@g.sig>
.const @ZERO <@i64> = 0
.funcsig @sig = () -> ()
.funcdef @f VERSION %v1 <@sig> {
%entry():
CALL <@g.sig> @g (@ZERO @ZERO)
RET ()
}
""")
}
it should "complain if a call site has the wrong signature" in {
catchExceptionWhenAnalyzing("""
.typedef @i64 = int<64>
.funcsig @g.sig = (@i64 @i64 @i64) -> ()
.funcdecl @g <@g.sig>
.const @ZERO <@i64> = 0
.funcsig @sig = () -> ()
.funcdef @f VERSION %v1 <@sig> {
%entry():
CALL <@sig> @g ()
RET ()
}
""")
}
it should "complain if returning the wrong number of values" in {
catchExceptionWhenAnalyzing("""
.typedef @i64 = int<64>
.const @ZERO <@i64> = 0
.funcsig @sig = () -> (@i64 @i64 @i64)
.funcdef @f VERSION %v1 <@sig> {
%entry():
RET (@ZERO @ZERO)
}
""")
}
}
\ No newline at end of file
// requires "primitives.uir"
.funcsig @v_i = (@i64) -> ()
.funcsig @i_v = (@i64) -> ()
.funcsig @v_i = () -> (@i64)
.funcdef @intro_test_base VERSION @intro_test_base_v1 <@v_i> {
.funcdef @intro_test_base VERSION @intro_test_base_v1 <@i_v> {
%entry(<@i64> %n):
%rv = [%call] CALL <@i_i> @intro_rec (%n)
......@@ -25,7 +26,7 @@
RET %rv
}
.funcdef @osr_test_base VERSION @osr_test_base_v1 <@v_i> {
.funcdef @osr_test_base VERSION @osr_test_base_v1 <@i_v> {
%entry(<@i64> %n):
%rv = [%call] CALL <@i_i> @sum (%n)
[%trap_base_exit] TRAP <> KEEPALIVE (%rv)
......@@ -68,7 +69,7 @@
.const @I64_42 <@i64> = 42
.funcdef @forty_two_returner VERSION %v1 <@v_v> {
.funcdef @forty_two_returner VERSION %v1 <@v_i> {
%entry():
RET @I64_42
}
......
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