Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specification Improvements #93

Merged
merged 3 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
102 changes: 59 additions & 43 deletions src/main/antlr4/Specifications.g4
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,17 @@ relies: 'Rely:' expr (COMMA expr)*;
guarantees: 'Guarantee:' expr (COMMA expr)*;

directFunctions: 'DIRECT functions:' directFunction (COMMA directFunction)*;
directFunction: 'memory_load' size=nat endian #memoryLoad
| 'memory_store' size=nat endian #memoryStore
| 'gamma_load' size=nat #gammaLoad
| 'gamma_store' size=nat #gammaStore
| 'zero_extend' size1=nat UNDERSCORE size2=nat #zeroExtend
| 'sign_extend' size1=nat UNDERSCORE size2=nat #signExtend
| BV OPNAME size=nat #bvOp
directFunction: MEMORY_LOAD_DIRECT #memoryLoad
| MEMORY_STORE_DIRECT #memoryStore
| GAMMA_LOAD_DIRECT #gammaLoad
| GAMMA_STORE_DIRECT #gammaStore
| ZERO_EXTEND_DIRECT #zeroExtend
| SIGN_EXTEND_DIRECT #signExtend
| BVOP_DIRECT #bvOp
;

subroutine: 'Subroutine:' id requires* ensures*;
subroutine: 'Subroutine:' id modifies? requires* ensures*;
modifies: 'Modifies:' id (COMMA id)*;
requires: 'Requires:' expr #parsedRequires
| 'Requires DIRECT:' QUOTESTRING #directRequires
;
Expand All @@ -41,7 +42,6 @@ arrayAccess: id '[' nat ']';
id : ID;
nat: (DIGIT)+ ;
bv: value=nat BVSIZE;
endian: LE | BE;

// based upon boogie grammar: https://boogie-docs.readthedocs.io/en/latest/LangRef.html#grammar
expr : impliesExpr ( EQUIV_OP impliesExpr )* ;
Expand Down Expand Up @@ -70,7 +70,25 @@ atomExpr : boolLit #boolLitExpr
QUOTE : '"';
QUOTESTRING : QUOTE (~( '"' | '\n' | '\r'))+ QUOTE;

BV: 'bv';
BVOP_DIRECT: BV OPNAME DIGIT+;

MEMORY_LOAD_DIRECT: MEMORY_LOAD DIGIT+ ENDIAN;
MEMORY_STORE_DIRECT: MEMORY_STORE DIGIT+ ENDIAN;
GAMMA_LOAD_DIRECT: GAMMA_LOAD DIGIT+;
GAMMA_STORE_DIRECT: GAMMA_STORE DIGIT+;
ZERO_EXTEND_DIRECT: ZERO_EXTEND DIGIT+ UNDERSCORE DIGIT+;
SIGN_EXTEND_DIRECT: SIGN_EXTEND DIGIT+ UNDERSCORE DIGIT+;

fragment MEMORY_LOAD: 'memory_load';
fragment MEMORY_STORE: 'memory_store';
fragment GAMMA_LOAD: 'gamma_load';
fragment GAMMA_STORE: 'gamma_store';
fragment ZERO_EXTEND: 'zero_extend';
fragment SIGN_EXTEND: 'sign_extend';

ENDIAN: LE | BE;

fragment BV: 'bv';

LONG: 'long';
SHORT: 'short';
Expand All @@ -88,7 +106,7 @@ ELSE: 'else';
DIV_OP : 'div';
MOD_OP : 'mod';

BVSIZE: BV DIGIT DIGIT?;
BVSIZE: BV DIGIT+;

ID : NON_DIGIT ( NON_DIGIT | DIGIT )* ;
NON_DIGIT : ( [A-Z] | [a-z] | '\'' | '~' | '#' | '$' | '^' | '_' | '.' | '?' | '`') ;
Expand All @@ -115,13 +133,11 @@ SUB_OP : '-';
MUL_OP : '*';

COLON: ':';
UNDERSCORE: '_';
LE: '_le';
BE: '_be';


fragment UNDERSCORE: '_';
fragment LE: '_le';
fragment BE: '_be';

OPNAME : AND
fragment OPNAME : AND
| OR
| ADD
| MUL
Expand Down Expand Up @@ -149,32 +165,32 @@ OPNAME : AND
| COMP
;

AND : 'and';
OR : 'or';
ADD : 'add';
MUL : 'mul';
UDIV : 'udiv';
UREM : 'urem';
SHL : 'shl';
LSHR : 'lshr';
ULT : 'ult';
NAND : 'nand';
NOR : 'nor';
XOR : 'xor';
XNOR : 'xnor';
SUB : 'sub';
SREM : 'srem';
SDIV : 'sdiv';
SMOD : 'smod';
ASHR : 'ashr';
ULE : 'ule';
UGT : 'ugt';
UGE : 'uge';
SLT : 'slt';
SLE : 'sle';
SGT : 'sgt';
SGE : 'sge';
COMP : 'comp';
fragment AND : 'and';
fragment OR : 'or';
fragment ADD : 'add';
fragment MUL : 'mul';
fragment UDIV : 'udiv';
fragment UREM : 'urem';
fragment SHL : 'shl';
fragment LSHR : 'lshr';
fragment ULT : 'ult';
fragment NAND : 'nand';
fragment NOR : 'nor';
fragment XOR : 'xor';
fragment XNOR : 'xnor';
fragment SUB : 'sub';
fragment SREM : 'srem';
fragment SDIV : 'sdiv';
fragment SMOD : 'smod';
fragment ASHR : 'ashr';
fragment ULE : 'ule';
fragment UGT : 'ugt';
fragment UGE : 'uge';
fragment SLT : 'slt';
fragment SLE : 'sle';
fragment SGT : 'sgt';
fragment SGE : 'sge';
fragment COMP : 'comp';

// Ignored
NEWLINE : '\r'? '\n' -> skip;
Expand Down
3 changes: 2 additions & 1 deletion src/main/scala/boogie/BProgram.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ case class BProcedure(
body.flatMap(c => c.functionOps).toSet ++ ensures.flatMap(c => c.functionOps).toSet ++ requires
.flatMap(c => c.functionOps)
.toSet ++ freeEnsures.flatMap(c => c.functionOps).toSet ++ freeRequires.flatMap(c => c.functionOps).toSet
def globals: Set[BVar] = body.flatMap(c => c.globals).toSet

def globals: Set[BVar] = body.flatMap(c => c.globals).toSet ++ modifies
}

case class BAxiom(body: BExpr) extends BDeclaration {
Expand Down
52 changes: 18 additions & 34 deletions src/main/scala/ir/Program.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,20 @@ class Program(
procedures = procedures.filter(f => reachableNames.contains(f.name))
}

def setModifies(): Unit = {
//val procToModifies: mutable.Map[Procedure, mutable.Set[Global]] = mutable.Map()
def setModifies(specModifies: Map[String, List[String]]): Unit = {

val procToCalls: mutable.Map[Procedure, Set[Procedure]] = mutable.Map()
for (p <- procedures) {
//procToModifies(p) = mutable.Set()
//procToModifies(p).addAll(p.blocks.flatMap(_.modifies))
p.modifies.addAll(p.blocks.flatMap(_.modifies))
procToCalls(p) = p.calls
}

for (p <- procedures) {
if (specModifies.contains(p.name)) {
p.modifies.addAll(specModifies(p.name).map(nameToGlobal))
}
}

// very naive implementation but will work for now
var hasChanged: Boolean = true
while (hasChanged) {
Expand All @@ -58,39 +62,19 @@ class Program(
}
}
}
}

/*
val visited: mutable.Set[Procedure] = mutable.Set()
val waiting: mutable.Set[Procedure] = mutable.Set()
val loops: mutable.Set[Set[Procedure]] = mutable.Set()
// need to add support for back edges - do a fixed point on them so all procedures in a loop have the same modifies
DFSVisit(mainProcedure, Vector(mainProcedure))
def DFSVisit(p: Procedure, path: Vector[Procedure]): Vector[Procedure] = {
val children = procToCalls(p)
if (visited.contains(p)) {
return path
}
if (waiting.contains(p)) {
val loopPath = path.slice(path.indexOf(p), path.size)
loops.add(loopPath.toSet)
return path
//throw new Exception("back edge in intraprocedural control flow graph, not currently supported")
}
waiting.add(p)
p.modifies.addAll(procToModifies(p))
for (child <- children) {
if (child != p) {
DFSVisit(child, path :+ p)
}
}
for (child <- children) {
p.modifies.addAll(child.modifies)
// this is very crude but the simplest thing for now until we have a more sophisticated specification system that can relate to the IR instead of the Boogie
def nameToGlobal(name: String): Global = {
if ((name.startsWith("R") || name.startsWith("V")) && (name.length == 2 || name.length == 3) && name.substring(1).forall(_.isDigit)) {
if (name.startsWith("R")) {
Register(name, BitVecType(64))
} else {
Register(name, BitVecType(128))
}
waiting.remove(p)
visited.add(p)
path :+ p
} else {
Memory(name, 64, 8)
}
*/
}

def stackIdentification(): Unit = {
Expand Down
2 changes: 2 additions & 0 deletions src/main/scala/ir/Visitor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ class Renamer(reserved: Set[String]) extends Visitor {
class ExternalRemover(external: Set[String]) extends Visitor {
override def visitProcedure(node: Procedure): Procedure = {
if (external.contains(node.name)) {
// update the modifies set before removing the body
node.modifies.addAll(node.blocks.flatMap(_.modifies))
node.blocks = ArrayBuffer()
}
super.visitProcedure(node)
Expand Down
3 changes: 2 additions & 1 deletion src/main/scala/specification/Specification.scala
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ case class SubroutineSpec(
requires: List[BExpr],
requiresDirect: List[String],
ensures: List[BExpr],
ensuresDirect: List[String]
ensuresDirect: List[String],
modifies: List[String]
)

case class ExternalFunction(name: String, offset: BigInt)
67 changes: 41 additions & 26 deletions src/main/scala/translating/SpecificationLoader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,35 +51,41 @@ case class SpecificationLoader(symbols: Set[SpecGlobal], program: Program) {
}

def visitDirectFunction(ctx: DirectFunctionContext): FunctionOp = ctx match {
case m: MemoryLoadContext => MemoryLoadOp(64, 8, visitEndian(m.endian), Integer.parseInt(m.size.getText))
case m: MemoryStoreContext => MemoryStoreOp(64, 8, visitEndian(m.endian), Integer.parseInt(m.size.getText))
case m: MemoryLoadContext =>
val suffix = m.getText.stripPrefix("memory_load")
val size = suffix.stripSuffix("_le").stripSuffix("_be")
val endian = suffix.stripPrefix(size)
MemoryLoadOp(64, 8, parseEndian(endian), Integer.parseInt(size))
case m: MemoryStoreContext =>
val suffix = m.getText.stripPrefix("memory_store")
val size = suffix.stripSuffix("_le").stripSuffix("_be")
val endian = suffix.stripPrefix(size)
MemoryStoreOp(64, 8, parseEndian(endian), Integer.parseInt(size))
case g: GammaLoadContext =>
val size = Integer.parseInt(g.size.getText)
val sizeText = g.getText.stripPrefix("gamma_load")
val size = Integer.parseInt(sizeText)
GammaLoadOp(64, size, size / 8)
case g: GammaStoreContext =>
val size = Integer.parseInt(g.size.getText)
val sizeText = g.getText.stripPrefix("gamma_store")
val size = Integer.parseInt(sizeText)
GammaStoreOp(64, size, size / 8)
case z: ZeroExtendContext =>
val extension = Integer.parseInt(z.size1.getText)
val bodySize = Integer.parseInt(z.size2.getText)
BVFunctionOp(
s"zero_extend${extension}_$bodySize",
s"zero_extend $extension",
List(BParam(BitVecBType(bodySize))),
BParam(BitVecBType(bodySize + extension))
)
val suffix = z.getText.stripPrefix("zero_extend")
val sizes = suffix.split("_")
val extension = Integer.parseInt(sizes(0))
val bodySize = Integer.parseInt(sizes(1))
BVFunctionOp(s"zero_extend${extension}_$bodySize", s"zero_extend $extension", List(BParam(BitVecBType(bodySize))), BParam(BitVecBType(bodySize + extension)))
case s: SignExtendContext =>
val extension = Integer.parseInt(s.size1.getText)
val bodySize = Integer.parseInt(s.size2.getText)
BVFunctionOp(
s"sign_extend${extension}_$bodySize",
s"sign_extend $extension",
List(BParam(BitVecBType(bodySize))),
BParam(BitVecBType(bodySize + extension))
)
val suffix = s.getText.stripPrefix("sign_extend")
val sizes = suffix.split("_")
val extension = Integer.parseInt(sizes(0))
val bodySize = Integer.parseInt(sizes(1))
BVFunctionOp(s"sign_extend${extension}_$bodySize", s"sign_extend $extension", List(BParam(BitVecBType(bodySize))), BParam(BitVecBType(bodySize + extension)))
case b: BvOpContext =>
val size = Integer.parseInt(b.size.getText)
val op = b.OPNAME.getText
val body = b.getText.stripPrefix("bv")
val sizeText = body.replaceAll("\\D+","")
val size = Integer.parseInt(sizeText)
val op = body.stripSuffix(sizeText)
val outType = op match {
case "and" | "or" | "add" | "mul" | "udiv" | "urem" | "shl" | "lshr" | "nand" | "nor" | "xor" | "xnor" | "sub" |
"srem" | "sdiv" | "smod" | "ashr" =>
Expand All @@ -94,7 +100,7 @@ case class SpecificationLoader(symbols: Set[SpecGlobal], program: Program) {
BVFunctionOp(s"bv$op$size", s"bv$op", List(BParam(BitVecBType(size)), BParam(BitVecBType(size))), BParam(outType))
}

def visitEndian(ctx: EndianContext): Endian = ctx.getText match {
def parseEndian(endian: String): Endian = endian match {
case "_le" => Endian.LittleEndian
case "_be" => Endian.BigEndian
}
Expand Down Expand Up @@ -362,8 +368,13 @@ case class SpecificationLoader(symbols: Set[SpecGlobal], program: Program) {
visitExpr(r.expr, nameToGlobals, params)
}.toList

val ensures = ctx.ensures.asScala.collect { case e: ParsedEnsuresContext =>
visitExpr(e.expr, nameToGlobals, params)
val modifies = Option(ctx.modifies) match {
case Some(_) => visitModifies(ctx.modifies)
case None => List()
}

val ensures = ctx.ensures.asScala.collect {
case e: ParsedEnsuresContext => visitExpr(e.expr, nameToGlobals, params)
}.toList

val requiresDirect = ctx.requires.asScala.collect { case r: DirectRequiresContext =>
Expand All @@ -374,7 +385,11 @@ case class SpecificationLoader(symbols: Set[SpecGlobal], program: Program) {
r.QUOTESTRING.getText.stripPrefix("\"").stripSuffix("\"")
}.toList

SubroutineSpec(ctx.id.getText, requires, requiresDirect, ensures, ensuresDirect)
SubroutineSpec(ctx.id.getText, requires, requiresDirect, ensures, ensuresDirect, modifies)
}

def visitModifies(ctx: ModifiesContext): List[String] = {
ctx.id.asScala.map(_.getText).toList
}

}
17 changes: 11 additions & 6 deletions src/main/scala/util/RunUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -91,18 +91,23 @@ object RunUtils {
IRProgram = externalRemover.visitProgram(IRProgram)
IRProgram = renamer.visitProgram(IRProgram)

if (performAnalysis) {
iterations += 1;
IRProgram = analyse(IRProgram, externalFunctions, globals, globalOffsets)
}

if (dumpIL) {
dump_file(serialiseIL(IRProgram), "before-analysis.il")
}


if (performAnalysis) {
iterations += 1;
IRProgram = analyse(IRProgram, externalFunctions, globals, globalOffsets)
if (dumpIL) {
dump_file(serialiseIL(IRProgram), "after-analysis.il")
}
}
IRProgram.stripUnreachableFunctions()
IRProgram.stackIdentification()
IRProgram.setModifies()

val specModifies = specification.subroutines.map(s => s.name -> s.modifies).toMap
IRProgram.setModifies(specModifies)

Logger.info("[!] Translating to Boogie")
val boogieTranslator = IRToBoogie(IRProgram, specification)
Expand Down