From 28fb1087edc1ba2795958597c2f24749ea361dae Mon Sep 17 00:00:00 2001 From: firefried-fries <102931771+firefried-fries@users.noreply.github.com> Date: Fri, 18 Jul 2025 16:48:34 +1000 Subject: [PATCH 1/4] Implemented MeetEdges in EdgeFunctionLattice. --- .../scala/analysis/EdgeFunctionLattice.scala | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/main/scala/analysis/EdgeFunctionLattice.scala b/src/main/scala/analysis/EdgeFunctionLattice.scala index 544787e049..4b64115199 100644 --- a/src/main/scala/analysis/EdgeFunctionLattice.scala +++ b/src/main/scala/analysis/EdgeFunctionLattice.scala @@ -78,9 +78,32 @@ class EdgeFunctionLattice[T, L <: Lattice[T]](val valuelattice: L) extends Latti case IdEdge() => this case ConstEdge(d) => JoinEdge(valuelattice.lub(c, d)) case JoinEdge(d) => JoinEdge(valuelattice.lub(c, d)) + case _ => e.joinWith(this) } override def toString = s"JoinEdge($c)" } + /** Edge that is the meet of an IdEdge and ConstEdge. `\l . gub(l, c)` as a lambda expression. */ + case class MeetEdge(c: T) extends EdgeFunction[T] { + def apply(x: T): T = valuelattice.glb(x, c) + + def composeWith(e: EdgeFunction[T]): EdgeFunction[T] = + e match { + case IdEdge() => this + case ConstEdge(d) => ConstEdge(valuelattice.glb(c, d)) + case JoinEdge(d) => ??? // The JoinEdge case for composition is ignored for now + case MeetEdge(d) => MeetEdge(valuelattice.glb(c, d)) + } + + def joinWith(e: EdgeFunction[T]): EdgeFunction[T] = + e match { + case IdEdge() => this + case ConstEdge(d) => MeetEdge(valuelattice.glb(c,d)) + case JoinEdge(d) => JoinEdge(d) + case MeetEdge(d) => MeetEdge(valuelattice.glb(c, d)) + } + + override def toString(): String = s"MeetEdge($c)" + } } From 9dfb03337f094b3143d481e45e75e9cf618fb7fe Mon Sep 17 00:00:00 2001 From: firefried-fries <102931771+firefried-fries@users.noreply.github.com> Date: Fri, 18 Jul 2025 16:51:09 +1000 Subject: [PATCH 2/4] Created FlatIntLattice in Lattice.scala. Created file StronglyLiveBitsAnalysis.scala. --- src/main/scala/analysis/Lattice.scala | 23 ++++++ .../analysis/StronglyLiveBitsAnalysis.scala | 70 +++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 src/main/scala/analysis/StronglyLiveBitsAnalysis.scala diff --git a/src/main/scala/analysis/Lattice.scala b/src/main/scala/analysis/Lattice.scala index f50ae68778..2f5d9db996 100644 --- a/src/main/scala/analysis/Lattice.scala +++ b/src/main/scala/analysis/Lattice.scala @@ -821,3 +821,26 @@ class ConstantPropagationLatticeWithSSA extends PowersetLattice[BitVecLiteral] { def concat(a: Set[BitVecLiteral], b: Set[BitVecLiteral]): Set[BitVecLiteral] = apply(BitVectorEval.smt_concat, a, b) } + +class FlatIntLattice(minimum: FlatElement[BigInt] = Bottom, maximum: FlatElement[BigInt] = Top) + extends FlatLattice[BigInt] { + override val bottom: FlatElement[BigInt] = minimum + + override val top: FlatElement[BigInt] = maximum + + def lub(x: FlatElement[BigInt], y: FlatElement[BigInt]): FlatElement[BigInt] = (x, y) match { + case (a, bottom) => a + case (bottom, b) => b + case (FlatEl(a), FlatEl(b)) if a >= b => FlatEl(a) else FlatEl(b) + case (top, _) => top + case (_, top) => top + } + + def glb(x: FlatElement[BigInt], y: FlatElement[BigInt]): FlatElement[BigInt] = (x, y) match { + case (_, bottom) => bottom + case (bottom, _) => bottom + case (FlatEl(a), FlatEl(b)) if a <= b => FlatEl(a) else FlatEl(b) + case (a, top) => a + case (top, b) => b + } +} \ No newline at end of file diff --git a/src/main/scala/analysis/StronglyLiveBitsAnalysis.scala b/src/main/scala/analysis/StronglyLiveBitsAnalysis.scala new file mode 100644 index 0000000000..84f4287090 --- /dev/null +++ b/src/main/scala/analysis/StronglyLiveBitsAnalysis.scala @@ -0,0 +1,70 @@ +package analysis + +import analysis.solvers.BackwardIDESolver +import ir.* + +/** + * + */ + + +/** + * + * LiveBits Analysis + * + * + */ + +trait StronglyLiveBitsAnalysisFunctions() + extends BackwardIDEAnalysis[Variable, FlatElement[BigInt], IntLattice] { + val valuelattice: IntLattice = IntLattice(minimum = FlatEl(0)) + + /** The edge lattice. + */ + val edgelattice: EdgeFunctionLattice[FlatElement[BigInt], IntLattice] = EdgeFunctionLattice(valuelattice) + import edgelattice.{IdEdge, ConstEdge} + + /** Edges for call-to-entry. + */ + def edgesCallToEntry(call: Command, entry: Return)(d: DL): Map[DL, EdgeFunction[FlatElement[BigInt]]] = ??? + + /** Edges for exit-to-aftercall. + */ + def edgesExitToAfterCall(exit: Procedure, aftercall: DirectCall)(d: DL): Map[DL, EdgeFunction[FlatElement[BigInt]]] = ??? + + /** Edges for call-to-aftercall. + */ + def edgesCallToAfterCall(call: Command, aftercall: DirectCall)(d: DL): Map[DL, EdgeFunction[FlatElement[BigInt]]] = ??? + + def edgesOther(n: CFGPosition)(d: DL): Map[DL, EdgeFunction[FlatElement[BigInt]]] = { + n match { + case LocalAssign(variable: Variable, expr: Expr, _) => // (s - variable) ++ for each variable in expr.variables + // if variable is mapped to top, then + d match { + case Left(value) => + if value == variable then ??? //remove variable from the set -- optimize later? + else Map(d -> IdEdge()) + case Right(_) => ??? // Check if the variable is in the set. + } + case MemoryLoad(lhs, _, index, _, _, _) => ??? + case MemoryStore(_, index, value, _, _, _) => ??? + case Assume(expr, _, _, _) => ??? + case Assert(expr, _, _) => ??? + case IndirectCall(variable, _) => ??? + // case c: DirectCall if addExternals && (c.target.isExternal.contains(true) || c.target.blocks.isEmpty) => ??? + case c: DirectCall => ??? + } + } +} + +// TODO: figure out the lattice +// class StronglyLiveBitsAnalysis(program: Program) +// extends BackwardIDESolver[Variable, FlatElement[BigInt], IntLattice](program), +// StronglyLiveBitsAnalysisFunctions() + +class StronglyLiveBitsAnalysis(program: Program) + extends BackwardIDESolver[Variable, FlatElement[BigInt], IntLattice](program), + StronglyLiveBitsAnalysisFunctions() { + /** The edge lattice. + */ +} From 734d993c3dbb94b7abb0c8b9d64020cff16bee9c Mon Sep 17 00:00:00 2001 From: firefried-fries <102931771+firefried-fries@users.noreply.github.com> Date: Fri, 18 Jul 2025 16:52:53 +1000 Subject: [PATCH 3/4] Fixed syntax error in MeetEdge toString --- src/main/scala/analysis/EdgeFunctionLattice.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/analysis/EdgeFunctionLattice.scala b/src/main/scala/analysis/EdgeFunctionLattice.scala index 4b64115199..f27902a1e6 100644 --- a/src/main/scala/analysis/EdgeFunctionLattice.scala +++ b/src/main/scala/analysis/EdgeFunctionLattice.scala @@ -104,6 +104,6 @@ class EdgeFunctionLattice[T, L <: Lattice[T]](val valuelattice: L) extends Latti case MeetEdge(d) => MeetEdge(valuelattice.glb(c, d)) } - override def toString(): String = s"MeetEdge($c)" + override def toString = s"MeetEdge($c)" } } From 13a604bb3e6f6f5286dfdef5a8bbd64dbe48c3ff Mon Sep 17 00:00:00 2001 From: firefried-fries <102931771+firefried-fries@users.noreply.github.com> Date: Sat, 26 Jul 2025 00:32:35 +1000 Subject: [PATCH 4/4] Created specialised lattice for StronglyLiveBitsAnalysis and specialised edgeFunctions. Created Test for StronglyLiveBitsAnalysis. TODO: Fix bug in edge Functions for return statements --- .../scala/analysis/EdgeFunctionLattice.scala | 25 +- src/main/scala/analysis/Lattice.scala | 33 ++- .../analysis/StronglyLiveBitsAnalysis.scala | 224 +++++++++++++----- .../scala/StronglyLiveBitAnalysisTests.scala | 67 ++++++ 4 files changed, 262 insertions(+), 87 deletions(-) create mode 100644 src/test/scala/StronglyLiveBitAnalysisTests.scala diff --git a/src/main/scala/analysis/EdgeFunctionLattice.scala b/src/main/scala/analysis/EdgeFunctionLattice.scala index f27902a1e6..2be88959b7 100644 --- a/src/main/scala/analysis/EdgeFunctionLattice.scala +++ b/src/main/scala/analysis/EdgeFunctionLattice.scala @@ -84,26 +84,29 @@ class EdgeFunctionLattice[T, L <: Lattice[T]](val valuelattice: L) extends Latti override def toString = s"JoinEdge($c)" } - /** Edge that is the meet of an IdEdge and ConstEdge. `\l . gub(l, c)` as a lambda expression. */ - case class MeetEdge(c: T) extends EdgeFunction[T] { - def apply(x: T): T = valuelattice.glb(x, c) + /** Edge that has `\l . lub (glb(l, a), b)` as a lambda expression. */ + case class MeetThenJoinEdge(a: T, b: T) extends EdgeFunction[T] { + def apply(x: T): T = valuelattice.lub(valuelattice.glb(a, x), b) def composeWith(e: EdgeFunction[T]): EdgeFunction[T] = e match { case IdEdge() => this - case ConstEdge(d) => ConstEdge(valuelattice.glb(c, d)) - case JoinEdge(d) => ??? // The JoinEdge case for composition is ignored for now - case MeetEdge(d) => MeetEdge(valuelattice.glb(c, d)) + case ConstEdge(c) => ConstEdge(valuelattice.lub(b, valuelattice.glb(a, c))) + case JoinEdge(_) => ??? // The JoinEdge case for composition is ignored + case MeetThenJoinEdge(c, d) => MeetThenJoinEdge(valuelattice.lub(b, d), + valuelattice.lub(b, valuelattice.glb(a, valuelattice.lub(c, d)))) + case _ => ??? } def joinWith(e: EdgeFunction[T]): EdgeFunction[T] = e match { - case IdEdge() => this - case ConstEdge(d) => MeetEdge(valuelattice.glb(c,d)) - case JoinEdge(d) => JoinEdge(d) - case MeetEdge(d) => MeetEdge(valuelattice.glb(c, d)) + case IdEdge() => MeetThenJoinEdge(valuelattice.top, b) + case ConstEdge(c) => MeetThenJoinEdge(a, valuelattice.lub(b, c)) + case JoinEdge(d) => ??? + case MeetThenJoinEdge(c, d) => MeetThenJoinEdge(valuelattice.lub(a, c), valuelattice.lub(b, d)) + case _ => e.joinWith(this) } - override def toString = s"MeetEdge($c)" + override def toString = s"MeetThenJoinEdge($a, $b)" } } diff --git a/src/main/scala/analysis/Lattice.scala b/src/main/scala/analysis/Lattice.scala index 2f5d9db996..7f416b0e18 100644 --- a/src/main/scala/analysis/Lattice.scala +++ b/src/main/scala/analysis/Lattice.scala @@ -822,25 +822,24 @@ class ConstantPropagationLatticeWithSSA extends PowersetLattice[BitVecLiteral] { def concat(a: Set[BitVecLiteral], b: Set[BitVecLiteral]): Set[BitVecLiteral] = apply(BitVectorEval.smt_concat, a, b) } -class FlatIntLattice(minimum: FlatElement[BigInt] = Bottom, maximum: FlatElement[BigInt] = Top) - extends FlatLattice[BigInt] { - override val bottom: FlatElement[BigInt] = minimum +class StronglyLiveBitsLattice extends MapLattice[Int, TwoElement, TwoElementLattice](TwoElementLattice()) { - override val top: FlatElement[BigInt] = maximum + override def top: Map[Int, TwoElement] = Map().withDefaultValue(sublattice.top) + def shift(value: Int, originalMap: Map[Int, TwoElement]): Map[Int, TwoElement] = originalMap.map({ + case (x, d) => + x + value -> d + }).withDefaultValue(sublattice.bottom) - def lub(x: FlatElement[BigInt], y: FlatElement[BigInt]): FlatElement[BigInt] = (x, y) match { - case (a, bottom) => a - case (bottom, b) => b - case (FlatEl(a), FlatEl(b)) if a >= b => FlatEl(a) else FlatEl(b) - case (top, _) => top - case (_, top) => top + def shiftMap(values: Set[Int], originalMap: Map[Int, TwoElement]): Map[Int, TwoElement] = { + values.foldLeft(Map(): Map[Int, TwoElement]){ + (x, y) => x ++ originalMap.map({ + case (m, n) => + m + y -> n + }) + }.withDefaultValue(sublattice.bottom) } - def glb(x: FlatElement[BigInt], y: FlatElement[BigInt]): FlatElement[BigInt] = (x, y) match { - case (_, bottom) => bottom - case (bottom, _) => bottom - case (FlatEl(a), FlatEl(b)) if a <= b => FlatEl(a) else FlatEl(b) - case (a, top) => a - case (top, b) => b - } + def cartesianAdd(as: Set[Int], bs: Set[Int]): Set[Int] = + for {a <- as; b <- bs} yield a + b + } \ No newline at end of file diff --git a/src/main/scala/analysis/StronglyLiveBitsAnalysis.scala b/src/main/scala/analysis/StronglyLiveBitsAnalysis.scala index 84f4287090..1aa7034ba4 100644 --- a/src/main/scala/analysis/StronglyLiveBitsAnalysis.scala +++ b/src/main/scala/analysis/StronglyLiveBitsAnalysis.scala @@ -3,68 +3,174 @@ package analysis import analysis.solvers.BackwardIDESolver import ir.* -/** - * - */ - - -/** - * - * LiveBits Analysis - * - * - */ - -trait StronglyLiveBitsAnalysisFunctions() - extends BackwardIDEAnalysis[Variable, FlatElement[BigInt], IntLattice] { - val valuelattice: IntLattice = IntLattice(minimum = FlatEl(0)) - - /** The edge lattice. - */ - val edgelattice: EdgeFunctionLattice[FlatElement[BigInt], IntLattice] = EdgeFunctionLattice(valuelattice) - import edgelattice.{IdEdge, ConstEdge} - - /** Edges for call-to-entry. - */ - def edgesCallToEntry(call: Command, entry: Return)(d: DL): Map[DL, EdgeFunction[FlatElement[BigInt]]] = ??? - - /** Edges for exit-to-aftercall. - */ - def edgesExitToAfterCall(exit: Procedure, aftercall: DirectCall)(d: DL): Map[DL, EdgeFunction[FlatElement[BigInt]]] = ??? - - /** Edges for call-to-aftercall. - */ - def edgesCallToAfterCall(call: Command, aftercall: DirectCall)(d: DL): Map[DL, EdgeFunction[FlatElement[BigInt]]] = ??? - - def edgesOther(n: CFGPosition)(d: DL): Map[DL, EdgeFunction[FlatElement[BigInt]]] = { +class StronglyLiveBitsEdgeFunctionLattice(valuelattice: StronglyLiveBitsLattice) + extends EdgeFunctionLattice[Map[Int, TwoElement], StronglyLiveBitsLattice](valuelattice) { + case class ShiftThenJoinEdge(values: Set[Int], constant: Map[Int, TwoElement]) + extends EdgeFunction[Map[Int, TwoElement]] { + def apply(x: Map[Int, TwoElement]): Map[Int, TwoElement] = + valuelattice.lub(valuelattice.shiftMap(values, x), constant) + + /** Composes this function with the given one. The resulting function first applies `e` then this function. + */ + def composeWith(e: EdgeFunction[Map[Int, TwoElement]]): EdgeFunction[Map[Int, TwoElement]] = + e match { + case IdEdge() => this + case ConstEdge(c) => ConstEdge(this.apply(c)) + case ShiftThenJoinEdge(b, d) => ShiftThenJoinEdge(valuelattice.cartesianAdd(values, b), + valuelattice.lub(valuelattice.shiftMap(values, d), constant)) + case _ => ??? // Other edges will not be used + } + + /** Finds the least upper bound of this function and the given one. + */ + def joinWith(e: EdgeFunction[Map[Int, TwoElement]]): EdgeFunction[Map[Int, TwoElement]] = + e match { + case IdEdge() => ShiftThenJoinEdge(values + 0, constant) + case ConstEdge(c) => ShiftThenJoinEdge(values, valuelattice.lub(constant, c)) + case ShiftThenJoinEdge(b, d) => ShiftThenJoinEdge(values ++ b, valuelattice.lub(constant, d)) + case _ => ??? // Unimplemented as other edges will not be used + } + + override def toString(): String = s"ShiftThenJoinEdge($values, $constant)" + } +} + +trait StronglyLiveBitsAnalysisFunctions + extends BackwardIDEAnalysis[Variable, Map[Int, TwoElement], StronglyLiveBitsLattice] { + override val valuelattice: StronglyLiveBitsLattice = StronglyLiveBitsLattice() + + override val edgelattice: StronglyLiveBitsEdgeFunctionLattice = + StronglyLiveBitsEdgeFunctionLattice(valuelattice) + + val subBot = valuelattice.sublattice.bottom + val subTop = valuelattice.sublattice.top + + import edgelattice.{IdEdge, ConstEdge, ShiftThenJoinEdge} + + def edgesCallToAfterCall(call: Command, aftercall: DirectCall)(d: DL): Map[DL, EdgeFunction[Map[Int, TwoElement]]] = ??? + + def edgesCallToEntry(call: Command, entry: Return)(d: DL): Map[DL, EdgeFunction[Map[Int, TwoElement]]] = ??? + + def edgesExitToAfterCall(exit: Procedure, aftercall: DirectCall)(d: DL): Map[DL, EdgeFunction[Map[Int, TwoElement]]] = + Map(d -> IdEdge()) + + def mapToTop(body: Expr): Map[Variable, EdgeFunction[Map[Int, TwoElement]]] = { + body.variables.foldLeft(Map(): Map[Variable, EdgeFunction[Map[Int, TwoElement]]]) + ((x, y) => x + (y -> ConstEdge(valuelattice.top))) // TODO: Make this more precise + } + + // Helps with assignment operation + def assignHelper(expr: Expr): Map[Variable, EdgeFunction[Map[Int, TwoElement]]] = { + expr match { + case literal: Literal => Map() + case Extract(end, start, body) => body match { + case literal: Literal => Map() + case variable: Variable => Map(variable -> ShiftThenJoinEdge(Set(start), Map().withDefaultValue(subBot))) + case expr: Expr => + assignHelper(expr).map({ + case (x, edgeFunc) => x -> edgeFunc.composeWith(ShiftThenJoinEdge(Set(start), Map().withDefaultValue(subBot))) + }) + } + case Repeat(repeats, body) => mapToTop(body) + case ZeroExtend(extension, body) => assignHelper(body) + case SignExtend(extension, body) => ??? + case UnaryExpr(op, arg) => arg match { + case variable: Variable => Map(variable->IdEdge()) + case literal: Literal => Map() + case expr: Expr => assignHelper(expr) + } + case BinaryExpr(op, arg1, arg2) => expr.getType match { + case BitVecType(size) => op match { + case op: BVBinOp => op match { + case op: BVCmpOp => ??? + case BVAND => (arg1, arg2) match { + case (var1: Variable, var2: Variable) => Map(var1 -> IdEdge(), var2 -> IdEdge()) + case (var1: Variable, var2: Literal) => Map(var1 -> IdEdge()) // TODO: More precise? + case (var1: Literal, var2: Variable) => Map(var2 -> IdEdge()) + case (var1: Literal, var2: Literal) => Map() + case _ => mapToTop(arg1) ++ mapToTop(arg2) + } + case BVOR => ??? + case BVADD => ??? + case BVSHL => ??? + case BVLSHR => ??? + case BVCONCAT => ??? + case BVMUL => ??? + case BVUDIV => ??? + case BVUREM => ??? + case BVNAND => ??? + case BVNOR => ??? + case BVXOR => ??? + case BVXNOR => ??? + case BVCOMP => ??? + case BVSUB => ??? + case BVSDIV => ??? + case BVSREM => ??? + case BVSMOD => ??? + case BVASHR => ??? + } + case _ => Map() + } + case _ => Map() + } + case variable: Variable => Map(variable -> IdEdge()) + case _ => mapToTop(expr) + } + } + + def edgesOther(n: CFGPosition)(d: DL): Map[DL, EdgeFunction[Map[Int, TwoElement]]] = n match { - case LocalAssign(variable: Variable, expr: Expr, _) => // (s - variable) ++ for each variable in expr.variables - // if variable is mapped to top, then + case LocalAssign(variable, expr, _) => d match { - case Left(value) => - if value == variable then ??? //remove variable from the set -- optimize later? - else Map(d -> IdEdge()) - case Right(_) => ??? // Check if the variable is in the set. + case Left(value) => value.getType match { + case BitVecType(_) => assignHelper(expr).foldLeft(Map[DL, EdgeFunction[Map[Int, TwoElement]]]()) + ((x, y) => x + (y match { + case (a, b) => Left(a) -> b + })) + case _ => Map() // Non-BitVector type does not have live bits + } + case Right(value) => Map().withDefaultValue(edgelattice.bottom) + } + case MemoryAssign(variable, expr, _) => + d match { + case Left(value) => value.getType match { + case BitVecType(_) => assignHelper(expr).foldLeft(Map[DL, EdgeFunction[Map[Int, TwoElement]]]()) + ((x, y) => x + (y match { + case (a, b) => Left(a) -> b + })) + case _ => Map() // Non-BitVector type does not have live bits + } + case Right(value) => Map().withDefaultValue(edgelattice.bottom) + } + case MemoryLoad(variable, _, index, _, _, _) => + d match { + case Left(value) => value.getType match { + case BitVecType(_) => assignHelper(index).foldLeft(Map[DL, EdgeFunction[Map[Int, TwoElement]]]()) + ((x, y) => x + (y match { + case (a, b) => Left(a) -> b + })) + case _ => Map() // Non-BitVector type does not have live bits + } + case Right(value) => Map().withDefaultValue(edgelattice.bottom) } - case MemoryLoad(lhs, _, index, _, _, _) => ??? case MemoryStore(_, index, value, _, _, _) => ??? - case Assume(expr, _, _, _) => ??? case Assert(expr, _, _) => ??? - case IndirectCall(variable, _) => ??? - // case c: DirectCall if addExternals && (c.target.isExternal.contains(true) || c.target.blocks.isEmpty) => ??? - case c: DirectCall => ??? + case Assume(expr, _, _, _) => ??? + case Return(_, varExpr) => varExpr.foldLeft(Map[DL, EdgeFunction[Map[Int, TwoElement]]]()) + ((x, y) => y match { + case (_, retExpr) => x ++ mapToTop(retExpr).foldLeft(Map[DL, EdgeFunction[Map[Int, TwoElement]]]())( + (a, b) => b match { + case (var1, eFunc) => a + (Left[Variable, Nothing](var1) -> eFunc) + } + ) + } + // TODO: Make this more precise + + ) + case _ => Map(d -> IdEdge()).withDefaultValue(edgelattice.bottom) } - } -} - -// TODO: figure out the lattice -// class StronglyLiveBitsAnalysis(program: Program) -// extends BackwardIDESolver[Variable, FlatElement[BigInt], IntLattice](program), -// StronglyLiveBitsAnalysisFunctions() - -class StronglyLiveBitsAnalysis(program: Program) - extends BackwardIDESolver[Variable, FlatElement[BigInt], IntLattice](program), - StronglyLiveBitsAnalysisFunctions() { - /** The edge lattice. - */ -} +} + +class StronglyLiveBitsAnalysis(program: Program) extends + BackwardIDESolver[Variable, Map[Int, TwoElement], StronglyLiveBitsLattice](program), + StronglyLiveBitsAnalysisFunctions \ No newline at end of file diff --git a/src/test/scala/StronglyLiveBitAnalysisTests.scala b/src/test/scala/StronglyLiveBitAnalysisTests.scala new file mode 100644 index 0000000000..fcf9ce4903 --- /dev/null +++ b/src/test/scala/StronglyLiveBitAnalysisTests.scala @@ -0,0 +1,67 @@ +import analysis.{StronglyLiveBitsEdgeFunctionLattice, StronglyLiveBitsLattice, StronglyLiveBitsAnalysis, + TwoElement, TwoElementBottom, TwoElementTop} +import ir.BitVecType +import org.scalatest.funsuite.AnyFunSuite +import test_util.{BASILTest, CaptureOutput} +import ir.{dsl, *} +import ir.dsl.* + +class StronglyLiveBitAnalysisTests extends AnyFunSuite, CaptureOutput, BASILTest{ + + val lattice = StronglyLiveBitsLattice() + val bot: TwoElement = TwoElementBottom + val top: TwoElement = TwoElementTop + + val edgeFuncLat = StronglyLiveBitsEdgeFunctionLattice(lattice) + + // Lattice Tests + test("Shifting Empty map - should return Empty map") { + val result = lattice.shiftMap(Set(1), Map().withDefaultValue(bot)) + assert(result == Map().withDefaultValue(bot)) + } + + test("Shifting a Map with 1 element to the left by one - should return shifted map") { + val result = lattice.shiftMap(Set(1), Map(1->top).withDefaultValue(bot)) + assert(result == Map(2->top).withDefaultValue(bot)) + } + + test("Shifting a Map with a set of two elements - should return a set of two elements") { + val result = lattice.shiftMap(Set(1, 2), Map(1->top).withDefaultValue(bot)) + assert(result == Map(2->top, 3->top).withDefaultValue(bot)) + } + + // End lattice tests + // Start EdgeFunction Tests + + test("The composition of an empty constant edge with empty constant edge - should return an empty constant edge") { + val result = edgeFuncLat.ConstEdge(Map().withDefaultValue(bot)).composeWith( + edgeFuncLat.ConstEdge(Map().withDefaultValue(bot))) + assert(result == edgeFuncLat.ConstEdge(Map().withDefaultValue(bot))) + } + + // Actual tests + + + def basicIntraCase(): Unit = { + val program = prog( + proc("main", + block( + "main", + LocalAssign(LocalVar("R1", BitVecType(64), 0), BitVecLiteral(BigInt("7"), 32)), + LocalAssign(LocalVar("R0", BitVecType(64), 2), ZeroExtend(32, Extract(32, 0, LocalVar("R1", BitVecType(64), 0)))), + goto("main_return") + ), + block( + "main_return", + ret("R0_out" -> LocalVar("R0", BitVecType(64), 2)) + )) + ) + val liveBitsAnalysisResults = StronglyLiveBitsAnalysis(program).analyze() + println(liveBitsAnalysisResults(program.mainProcedure)) + + assert(liveBitsAnalysisResults(program.mainProcedure) == Map(R0 -> Map())) + } + test("Basic Intra Live Bits"){ + basicIntraCase() + } +}