diff --git a/docs/basil-ir.md b/docs/basil-ir.md index 4c152db8d..7bbd1a60c 100644 --- a/docs/basil-ir.md +++ b/docs/basil-ir.md @@ -25,8 +25,9 @@ BlockID ::=&~ String \\ \\ Jump ::=&~ GoTo ~|~ Unreachable ~|~ Return \\ GoTo ::=&~ \text{goto } BlockID* \\ +Return::=&! \text{return } (outparams) Call ::=&~ DirectCall ~|~ IndirectCall \\ -DirectCall ::=&~ \text{call } ProcID \\ +DirectCall ::=&~ (outparams) := \text{ call } ProcID \; (inparams) \\ IndirectCall ::=&~ \text{call } Expr \\ \\ &~ loads(e: Expr) = \{x | x:MemoryLoad, x \in e \} \\ @@ -55,8 +56,17 @@ Endian ::=&~ BigEndian ~|~ LittleEndian \\ - The `Unreachable` jump is used to signify the absence of successors, it has the semantics of `assume false`. - The `Return` jump passes control to the calling function, often this is over-approximated to all functions which call the statement's parent procedure. +### Indirect Calls + +An indirect call is a dynamic jump, to either a procedure or a block. + ## Translation Phases +We have invariant checkers to validate the structure of the IR's bidirectional CFG is correct, see `src/main/scala/ir/invariant`. This includes: + +- blocks belong to exactly one procedure: `invariant/BlocksUniqueToProcedure.scala` +- forwards block CFG links match backwards block CFG links: `invariant/CFGCorrect.scala` + #### IR With Returns - Immediately after loading the IR return statements may appear in any block, or may be represented by indirect calls. @@ -73,10 +83,21 @@ This ensures that all returning, non-stub procedures have exactly one return sta #### Calls appear only as the last statement in a block +- Checked by `invariant/SingleCallBlockEnd.scala` - The structure of the IR allows a call may appear anywhere in the block but for all the analysis passes we hold the invariant that it only appears as the last statement. This is checked with the function `singleCallBlockEnd(p: Program)`. And it means for any call statement `c` we may `assert(c.parent.statements.lastOption.contains(c))`. +## IR With Parameters + +The higher level IR containing parameters is established by `ir.transforms.liftProcedureCallAbstraction(ctx)`. +This makes registers local variables, which are passed into procedures through prameters, and then returned from +procedures. Calls to these procedure must provide as input parameters the local variables corresponding to the +values passed, and assign the output parameters to local variables also. Note now we must consider indirect calls +as possibly assigning to everything, even though this is not explicitly represented syntactically. + +- Actual parameters to calls and returns match formal parameters is checked by `invariant/CorrectCallParameters.scala` + ## Interaction with BASIL IR ### Constructing Programs in Code @@ -127,20 +148,27 @@ label, the dsl constructor will likely throw a match error. Some additional constants are defined for convenience, Eg. `R0 = Register(R0, 64)`, see [the source file](../src/main/scala/ir/dsl/DSL.scala) for the full list. -### Static Analysis / Abstract Interpretation -- For static analysis the Il-CFG-Iterator is the current well-supported way to iterate the IR. - This currently uses the TIP framework, so you do not need to interact with the IR visitor directly. - See [BasicIRConstProp.scala](../src/main/scala/analysis/BasicIRConstProp.scala) for an example on its useage. -- This visits all procedures, blocks and statements in the IR program. +### Pretty printing -### Modifying and Visiting the IR with Visitor Pattern +The ir can be printed with the overloaded function below, which can take a procedure, block, or statement and returns a string. -[src/main/scala/ir/Visitor.scala](../src/main/scala/ir/Visitor.scala) defines visitors which can be used -for extracting specific features from an IR program. This is useful if you want to modify all instances of a specific -IR construct. - -### CFG +```scala +translating.BasilIRPrettyPrinter()(b) +``` + +It is also possible to dump a `dot/graphviz` digraph containing just the blocks in the program +using the functions: + +```scala +ir.dotBlockGraph(prog: Program) : String +ir.dotBlockGraph(proc: Procedure) : String +``` -The cfg is a control-flow graph constructed from the IR, it wraps each statement in a `Node`. +### Static Analysis / Abstract Interpretation / IR Rewriting and modification +- See [development/simplification-solvers.md](development/simplification-solvers.md) +- For static analysis the Il-CFG-Iterator is the current well-supported way to iterate the IR. + This currently uses the TIP framework, so you do not need to interact with the IR visitor directly. + See [BasicIRConstProp.scala](../src/main/scala/analysis/BasicIRConstProp.scala) for an example on its useage. +- This visits all procedures, blocks and statements in the IR program. diff --git a/docs/development/interpreter.md b/docs/development/interpreter.md new file mode 100644 index 000000000..a2854bb73 --- /dev/null +++ b/docs/development/interpreter.md @@ -0,0 +1,203 @@ +# BASIL IR Interpreter + +The interpreter is designed for testing, debugging, and validation of static analyses and code transforms. +This page describes first how it can be used for this purpose, and secondly its design. + +## Basic Usage + +The interpreter can be invoked from the command line, via the interpret flag, by default this prints a trace and checks that the interpreter +exited on a non-error stop state. + +```shell +./mill run -i src/test/correct/indirect_call/gcc/indirect_call.adt -r src/test/correct/indirect_call/gcc/indirect_call.relf --interpret +[INFO] Interpreter Trace: + StoreVar(#5,Local,0xfd0:bv64) + StoreMem(mem,HashMap(0xfd0:bv64 -> 0xf0:bv8, 0xfd6:bv64 -> 0x0:bv8, 0xfd2:bv64 -> 0x0:bv8, 0xfd3:bv64 -> 0x0:bv8, 0xfd4:bv64 -> 0x0:bv8, 0xfd7:bv64 -> 0x0:bv8, 0xfd5:bv64 -> 0x0:bv8, 0xfd1 +... +[INFO] Interpreter stopped normally. +``` + +The `--verbose` flag can also be used, which may print interpreter trace events as they are executed, but not this may not correspond to the actual +execution trace, and contain additional events not corresponding to the program. +E.g. this shows the memory intialisation events that precede the program execution. This is mainly useful for debugging the interpreter. + +### Testing with Interpreter + +The interpreter is invoked with `interpret(p: IRContext)` to interpret normally and return an `InterpreterState` object +containing the final state. + +#### Traces + +There is also, `interpretTrace(p: IRContext)` which returns a tuple of `(InterpreterState, Trace(t: List[ExecEffect]))`, +where the second argument contains a list of all the events generated by the interpreter in order. +This is useful for asserting a stronger equivalence between program executions, but in most cases events describing "unobservable" +behaviour, such as register accesses should be filtered out from this list before comparison. + +To see an example of this used to validate the constant prop analysis see [/src/test/scala/DifferentialAnalysis.scala](../../src/test/scala/DifferentialAnalysis.scala). + +#### BreakPoints + +Finally `interpretBreakPoints(p: IRContext, breakpoints: List[BreakPoint])` is used to +run an interpreter and perform additional actions at specified code points. For example, this may be invoked such as: + +```scala +val watch = IRWalk.firstInProc((program.procedures.find(_.name == "main")).get).get +val bp = BreakPoint("entrypoint", BreakPointLoc.CMD(watch), BreakPointAction(saveState=true, stop=true, evalExprs=List(("R0", Register("R0", 64))), log=true)) +val res = interpretBreakPoints(program, List(bp)) +``` + +The structure of a breakpoint is as follows: + +```scala +case class BreakPoint(name: String = "", location: BreakPointLoc, action: BreakPointAction) + +// the place to perform the breakpoint action +enum BreakPointLoc: + case CMD(c: Command) // at a command c + case CMDCond(c: Command, condition: Expr) // at a command c, when condition evaluates to TrueLiteral + +// describes what to do when the breakpoint is triggered +case class BreakPointAction( + saveState: Boolean = true, // stash the state of the interpreter + stop: Boolean = false, // stop the interpreter with an error state + evalExprs: List[(String, Expr)] = List(), // Evaluate the rhs of the list of expressions, and stash them (lhs is an arbitrary human-readable name) + log: Boolean = false // Print a log message about passing the breakpoint describing the results of this action +) +``` + +To see an example of this used to validate the constant prop analysis see [/src/test/scala/InterpretTestConstProp.scala](../../src/test/scala/InterpretTestConstProp.scala). + +### Resource Limit + +This kills the interpreter in an error state once a specified instruction count is reached, to avoid the interpreter running forever on infinite loops. + +It can be used simply with the function `interptretRLimit`, this automatically ignores the initialisation instructions. + +```scala +def interpretRLimit(p: IRContext, instructionLimit: Int) : InterpreterState +``` + +It can also be combined with other interpreters as shown: + +```scala +def interp(p: IRContext, instructionLimit: Int) : (InterpreterState, Trace) = { + val interpreter = LayerInterpreter(tracingInterpreter(NormalInterpreter), EffectsRLimit(instructionLimit)) + val initialState = InterpFuns.initProgState(NormalInterpreter)(p, InterpreterState()) + BASILInterpreter(interpreter).run((initialState, Trace(List())), 0)._1 +} +``` + +## Implementation / Code Structure + +### Summary + +- [Bitvector.scala](../../src/main/scala/ir/eval/Bitvector.scala) + - Evaluation of bitvector operations, throws `IllegalArgumentException` on violation of contract + (e.g negative divisor, type mismatch) +- [ExprEval.scala](../../src/main/scala/ir/eval/ExprEval.scala) + - Evaluation of expressions, defined in terms of partial evaluation down to a Literal + - This can also be used to evaluate expressions in static analyses, by passing a function to query variable assignments and memory state from the value domain. +- [Interpreter.scala](../../src/main/scala/ir/eval/Interpreter.scala) + - Definition of core `Effects[S, E]` and `Interpreter[S, E]` types describing state transitions in + the interpreter + - Instantiation/definition of `Effects` for concrete state `InterpreterState` +- [InterpreterProduct.scala](../../src/main/scala/ir/eval/InterpreterProduct.scala) + - Definition of product and layering composition of generic `Effects[S, E]`s interpreters +- [InterpretBasilIR.scala](../../src/main/scala/ir/eval/InterpretBasilIR.scala) + - Definition of `Eval` object defining expression evaluation in terms of `Effects[S, InterpreterError]` + - Definition of `Interpreter` instance for BASIL IR, using a generic `Effects` instance and concrete state. + - Definition of ELF initialisation in terms of generic `Effects[S, InterpreterError]` +- [InterpretBreakpoints.scala](../../src/main/scala/ir/eval/InterpretBreakpoints.scala) + - Definition of a generic interpreter with a breakpoint checker layered on top +- [interpretRLimit.scala](../../src/main/scala/ir/eval/InterpretRLimit.scala) + - Definition of layered interpreter which terminates after a specified cycle count +- [InterpretTrace.scala](../../src/main/scala/ir/eval/InterpretTrace.scala) + - Definition of a generic interpreter which records a trace of calls to the `Effects[]` instance. + +### Explanation + +The interpreter is structured for compositionality, at its core is the `Effects[S, E]` type, defined in [Interpreter.scala](../../src/main/scala/ir/eval/Interpreter.scala). +This type defines a small set of functions which describe all the possible state transformations, over a concrete state `S`, and error type `E` (always `InterpreterError` in practice). + +This is implemented using the state Monad, `State[S,V,E]` where `S` is the state, `V` the value, and `E` the error type. +This is a flattened `State[S, Either[E]]`, defined in [util/functional.scala](../../src/main/scala/util/functional.scala). +`Effects` methods return delayed computations, functions from an input state (`S`) to a resulting state and a value (`(S, Either[E, V])`). +These are sequenced using `flatMap` (monad bind), or the `for{} yield()` syntax sugar for flatMap. + +This `Effects[S, E]` is instantiated for a given concrete state, the main example of which is `NormalInterpreter <: Effects[InterpreterState, InterpreterError]`, +also defined in `Interpreter.scala`. The memory component of the state is abstracted further into the `MemoryState` object. + +The actual execution of code is defined on top of this, in the `Interpreter[S, E]` type, which takes an instance of the `Effects` by parameter, +and defines both the small step (`interpretOne`) over on instruction, and the fixed point to termination from some in initial state in `run()`. +The fact that the stepping is defined outside the effects is important, as it allows concrete states, and state transitions over them to be +composed somewhat arbitrarily, and the interpretatation of the language compiled down to calls to resulting instance of `Effects`. + +This is defined in [InterpretBasilIR.scala](../../src/main/scala/ir/eval/InterpretBasilIR.scala). `BASILInterpreter` defines an +`Interpreter` over an arbitrary instance of `Effects[S, InterpreterError]`, encoding BASIL IR commands as effects. +This file also contains definitions of the initial memory state setup of the interpreter, based on the ELF sections and symbol table. + +### Composition of interpreters + +There are two ways to compose `Effects`, product and layer. Both produce an instance of `Effects[(L, R), E]`, +where `L` and `R` are the concrete state types of the two Effects being composed. + +Product runs the two effects, over two different concrete state types, simultaneously without interaction. + +Layer runs the `before` effect first, and passes its state to the `inner` effect whose value is returned. + +```scala +case class ProductInterpreter[L, T, E](val inner: Effects[L, E], val before: Effects[T, E]) extends Effects[(L, T), E] { +case class LayerInterpreter[L, T, E](val inner: Effects[L, E], val before: Effects[(L, T), E]) +``` + +Examples of using these are in the `interpretTrace` and `interpretWithBreakPoints` interpreters respectively. + +Note, this only works by the aforementioned requirement that all effect calls come from outside the `Effects[]` +instance itself. In the simple case, the `Interpreter` instance is the only object calling `Effects`. +This means, `Effects` triggered by an inner `Effects[]` instance do not flow back to the `ProductInterpreter`, +but only appear from when `Interpreter` above the `ProductInterpreter` interprets the program via effect calls. +For this reason if, for example, `NormalInterpreter` makes effect calls they will not appear in a trace emitted by `interptretTrace`. + +### Note on memory space initialisation + +Most of the interpret functions are overloaded such that there is a version taking a program `interpret(p: Program)`, +and a version taking `IRContext`. The variant taking IRContext uses the ELF symbol information to initialise the +memory before interpretation. If you are interpreting a real program (i.e. not a synthetic example created through +the DSL), this is most likely required. + +We initialise: + +- The general interpreter state, stack and memory regions, stack pointer, a symbolic mapping from addresses functions +- The initial and readonly memory sections stored in Program +- The `.bss` section to zero +- The relocation table. Each listed offset is stored an address to either a real procedure in the program, or a + location storing a symbolic function pointer to an intrinsic function. + +`.bss` is generally the top of the initialised data, the ELF symbol `__bss_end__` being equal to the symbol `__end__`. +Above this we can somewhat choose arbitrarily where to put things, usually the heap is above, followed by +dynamically linked symbols, then the stack. There is currently no stack overflow checking, or heap implemented in the +interpreter. + +Unfortunately these details are defined by the load-time linker and the system's linker script, and it is hard to find a good description +of their behaviour. Some details are described here https://refspecs.linuxfoundation.org/elf/elf.pdf, and here +https://dl.acm.org/doi/abs/10.1145/2983990.2983996. + +### Missing features + +- There is functionality to implement external function calls via intrinsics written in Scala code, but currently only + basic printf style functions are implemented as no-ops. These can be extended to use a file IO abstraction, where + a memory region is created for each file (e.g. stdout), with a variable to keep track of the current write-point + such that a file write operation stores to the write-point address, and increments it by the size of the store. + Importantly, an implementation of malloc() and free() is needed, which can implement a simple greedy allocation + algorithm. +- Despite the presence of procedure parameters in the current IR, they are not used for by the boogie translation and + are hence similarly ignored in the interpreter. +- The interpreter's immutable state representation is motivated by the ability to easily implement a sound approach + to non-determinism, e.g. to implement GoTos with guessing and rollback rather than look-ahead. This is more + useful for checking specification constructs than executing real programs, so is not yet implemented. +- The trace does not clearly distinguish internal vs external calls, or observable + and non-observable behaviour. +- While the interpreter semantics supports memory regions, we do not initialise the memory regions (or the initial memory state) + based on those present in the program, we simply assume a flat `mem` and `stack` memory partitioning. + + diff --git a/docs/development/readme.md b/docs/development/readme.md index 4e220c162..b92fbeaab 100644 --- a/docs/development/readme.md +++ b/docs/development/readme.md @@ -5,6 +5,7 @@ - [tool-installation](tool-installation.md) Guide to lifter, etc. tool installation - [scala](scala.md) Advice on Scala programming. - [cfg](cfg.md) Explanation of the old CFG datastructure +- [interpreter](interpreter.md) Explanation of IR interpreter ## Scala diff --git a/docs/development/simplification-solvers.md b/docs/development/simplification-solvers.md new file mode 100644 index 000000000..b283ed701 --- /dev/null +++ b/docs/development/simplification-solvers.md @@ -0,0 +1,172 @@ +## New Anslysis & Transforms + +This page describes the framework for static analysis used by the `--simplify` pass. + +## DSA Form + +We have a transform that establishes a dynamic single assignment form. +The DSA form makes it possible to perform a flow-insensitive analysis with some flow-sensitive precision, +with a single global abstract state, and one pass over the IR in control-flow order. + +This form is established once, and should be maintained by subsequent transforms. + +``` +transforms.OnePassDSA().applyTransform(ctx.program) +``` + +This provides a property similar to SSA: that every use of a variable is defined by all syntactic definitions of the variable. +I.e. you never have the pattern below, where `x` has a different identity at different points in the program: +this only occurs if `x` is involved in a join, in which case all definitions are reached by the subsequent uses. + +```c +x = 2 +y = f(x) +x = 3 +z = f(x) +``` + +i.e. the below is allowed + +```c +if (c) { + x = 2 +} else { + x = 3 +} +y = f(x) + +``` + +```c +x = 0 +while (c) { + x = x + 1 +} +y = x + +``` + +The analysis `transforms.rdDSAProperty(p: Procedure)` uses a relatively expensive +reaching definitions analysis to check that this property is satisfied, and is useful for +debugging. + +## Dataflow analysis + +See also : abstract interpretation + +Most of the dataflow analyses in BASIL---those under `/src/main/scala/analysis`---use the +TIP frameork. +This framework uses a lot of generics in order to compose abstract domains and can be unweildy +to write new analyses for. + +The new framework in `src/main/scala/transforms/Absint.scala` is both simpler to use and more performant. + +A good example of usning this framework is the simple live variables analysis. The abstract domain must +define a join, a bottom value, and a transfer function. + +This similar analyses using a powerset can use the same root domain: + +```scala +trait PowerSetDomain[T] extends AbstractDomain[Set[T]] { + def bot = Set() + def top = ??? + def join(a: Set[T], b: Set[T], pos: Block) = a.union(b) +} +``` + +Then live variables only has to define the transfer function for commands and jumps. + +```scala +class IntraLiveVarsDomain extends PowerSetDomain[Variable] { + // expected backwards + + def transfer(s: Set[Variable], a: Command): Set[Variable] = { + a match { + case a: Assign => (s - a.lhs) ++ a.rhs.variables + case m: MemoryAssign => s ++ m.index.variables ++ m.value.variables + case a: Assume => s ++ a.body.variables + case a: Assert => s ++ a.body.variables + case i: IndirectCall => s + i.target + case c: DirectCall => (s -- c.outParams.map(_._2)) ++ c.actualParams.flatMap(_._2.variables) + case g: GoTo => s + case r: Return => s ++ r.outParams.flatMap(_._2.variables) + case r: Unreachable => s + } + } +} +``` + +We then create a solver using this domain, and pass it a procedure to solve to a fixed point. +The solver returns the abstract state at the beginning and end of every block. +This solver visits blocks in control flow order, which reduces the number of joins. +Passing the backwards flag makes it visit blocks (and statements) in the reverse control +flow order. + +Ensure the order indexes are defined for every block using the function `transforms.applyRPO(program)`. + +```scala +val liveVarsDom = IntraLiveVarsDomain() +val liveVarsSolver = worklistSolver(liveVarsDom) +val (beforeState, afterState) = liveVarsSolver.solveProc(procedure, backwards = true) +``` + +### IR AST Transforms + +Expressions are immutable so can be freely modified without modifying the IR. +Statements are mutable however, so they can be changed by simply assigning to their members. + +IR transforms can be applied using the `ir.cilvisitor.CILVisitor`. + +This is just a tool for visiting the ast, and optionally modifying it. + +At each node, e.g. a statement you can perform the actions: + +- `DoChildren()` -- continue recursing +- `SkipChildren()` -- stop recursing +- `ChangeTo(e)` -- replace the node with the argument +- `ChangeDoChildrenPost(e, f: E => E)` -- first replace with the first argument, visit its children, then call function f on the result + +Note that using `ChangeTo` on statements is often not desirable as it removes the old statement from the IR; invalidating references +to the statement that may be floating around in analysis results; instead the arguments to the statement may be changed by direct assignmet. + +#### Variable substitution + +The class `ir.transforms.Substitute` can be used to substitute variables into an expression. + +This can be used as a twice-applied function, it first takes a function `Variable => Option[Expr]` +which defines the substitutions to perform. Note this matches the type signature of +`Map[Variable, Expr].get`, but can also be used to efficiently extract values from an `AbstractDomain[T]`. +The second application takes an expression and returns the expression with the values substituted. + +``` +val substitutions = Map((LocalVar("R3", ...) -> BitVecLiteral(0, 64))) +Substitute(substitutions.get)(expression) +``` + +#### Expression Evaluation / Simplification + +The function `ir.eval.evaluateSimp` will perform concrete evaluation of an expression, +and returning `Some[Literal]` if it succeeds or `None` if it fails. + +The function `ir.eval.simplifyExprFixpoint(e: Expr)` will simplify, +canonicalise, and perform partial evaluation on the expression, returning a new expression. + +The function `ir.eval.cleanupSimplify(p: Procedure)` will further simplify known bits and redundant extends and +extracts within an expression. + +##### Verification / Testing + +Basil IR expressions can be converted so smt queries, for example to prove a basic tautology x = x we can write: + +```scala +import tramslating.BasilIRToSMT2 +val result = BasilIRToSMT2.proveExpr(BinaryExpr(BVEQ, R0, R0)) +assert(result == Some(true)) +``` + +This will negate the expression, translate it SMT2, invoke Z3, and check whether the result is "unsat". + +We can also simply create the smt query with `BasilIRToSMT2.exprUnsat(expr, None, false)` + +Note that statements and control-flow cannot be translated to SMT2 yet. +This system is currently used to validate the tranforms in the previous section. diff --git a/docs/readme.md b/docs/readme.md index f7b331b23..3bb6905aa 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -12,6 +12,7 @@ To get started on development, see [development](development). - [editor-setup](development/editor-setup.md) Guide to basil development in IDEs - [tool-installation](development/tool-installation.md) Guide to lifter, etc. tool installation - [cfg](development/cfg.md) Explanation of the old CFG datastructure + - [interpreter](development/interpreter.md) Explanation of IR interpreter - [basil-ir](basil-ir.md) explanation of BASIL's intermediate representation - [compiler-explorer](compiler-explorer.md) guide to the compiler explorer basil interface - [il-cfg](il-cfg.md) explanation of the IL cfg iterator design diff --git a/examples/conds/Makefile b/examples/conds/Makefile new file mode 100644 index 000000000..fecb0afbf --- /dev/null +++ b/examples/conds/Makefile @@ -0,0 +1,15 @@ + + +.PHONY=all +all: conds.out conds.adt conds.relf + +conds.out : conds.c + aarch64-unknown-linux-gnu-gcc conds.c -o conds.out + +conds.adt : conds.out + bap conds.out -d adt:conds.adt + +conds.relf : conds.out + readelf -s -r -W conds.out > conds.relf + + diff --git a/examples/conds/conds.adt b/examples/conds/conds.adt new file mode 100644 index 000000000..1b84fd97c --- /dev/null +++ b/examples/conds/conds.adt @@ -0,0 +1,1651 @@ +Project(Attrs([Attr("filename","\"conds.out\""), +Attr("image-specification","(declare abi (name str))\n(declare arch (name str))\n(declare base-address (addr int))\n(declare bias (off int))\n(declare bits (size int))\n(declare code-region (addr int) (size int) (off int))\n(declare code-start (addr int))\n(declare entry-point (addr int))\n(declare external-reference (addr int) (name str))\n(declare format (name str))\n(declare is-executable (flag bool))\n(declare is-little-endian (flag bool))\n(declare llvm:base-address (addr int))\n(declare llvm:code-entry (name str) (off int) (size int))\n(declare llvm:coff-import-library (name str))\n(declare llvm:coff-virtual-section-header (name str) (addr int) (size int))\n(declare llvm:elf-program-header (name str) (off int) (size int))\n(declare llvm:elf-program-header-flags (name str) (ld bool) (r bool) \n (w bool) (x bool))\n(declare llvm:elf-virtual-program-header (name str) (addr int) (size int))\n(declare llvm:entry-point (addr int))\n(declare llvm:macho-symbol (name str) (value int))\n(declare llvm:name-reference (at int) (name str))\n(declare llvm:relocation (at int) (addr int))\n(declare llvm:section-entry (name str) (addr int) (size int) (off int))\n(declare llvm:section-flags (name str) (r bool) (w bool) (x bool))\n(declare llvm:segment-command (name str) (off int) (size int))\n(declare llvm:segment-command-flags (name str) (r bool) (w bool) (x bool))\n(declare llvm:symbol-entry (name str) (addr int) (size int) (off int)\n (value int))\n(declare llvm:virtual-segment-command (name str) (addr int) (size int))\n(declare mapped (addr int) (size int) (off int))\n(declare named-region (addr int) (size int) (name str))\n(declare named-symbol (addr int) (name str))\n(declare require (name str))\n(declare section (addr int) (size int))\n(declare segment (addr int) (size int) (r bool) (w bool) (x bool))\n(declare subarch (name str))\n(declare symbol-chunk (addr int) (size int) (root int))\n(declare symbol-value (addr int) (value int))\n(declare system (name str))\n(declare vendor (name str))\n\n(abi unknown)\n(arch aarch64)\n(base-address 4194304)\n(bias 0)\n(bits 64)\n(code-region 4197380 20 3076)\n(code-region 4195904 1476 1600)\n(code-region 4195808 80 1504)\n(code-region 4195776 24 1472)\n(code-start 4195972)\n(code-start 4195968)\n(code-start 4195904)\n(code-start 4196164)\n(entry-point 4195904)\n(external-reference 4325328 _ITM_deregisterTMCloneTable)\n(external-reference 4325336 __gmon_start__)\n(external-reference 4325344 _ITM_registerTMCloneTable)\n(external-reference 4325376 __libc_start_main)\n(external-reference 4325384 __gmon_start__)\n(external-reference 4325392 abort)\n(format elf)\n(is-executable true)\n(is-little-endian true)\n(llvm:base-address 4194304)\n(llvm:code-entry abort 0 0)\n(llvm:code-entry __libc_start_main 0 0)\n(llvm:code-entry _init 1472 0)\n(llvm:code-entry main 1860 1216)\n(llvm:code-entry _start 1600 60)\n(llvm:code-entry _dl_relocate_static_pie 1664 4)\n(llvm:code-entry abort@GLIBC_2.17 0 0)\n(llvm:code-entry _fini 3076 0)\n(llvm:code-entry __libc_start_main@GLIBC_2.34 0 0)\n(llvm:code-entry frame_dummy 1856 0)\n(llvm:code-entry __do_global_dtors_aux 1808 0)\n(llvm:code-entry register_tm_clones 1744 0)\n(llvm:code-entry deregister_tm_clones 1696 0)\n(llvm:code-entry call_weak_fn 1668 20)\n(llvm:code-entry .fini 3076 20)\n(llvm:code-entry .text 1600 1476)\n(llvm:code-entry .plt 1504 80)\n(llvm:code-entry .init 1472 24)\n(llvm:elf-program-header 08 64968 568)\n(llvm:elf-program-header 07 0 0)\n(llvm:elf-program-header 06 3100 68)\n(llvm:elf-program-header 05 680 32)\n(llvm:elf-program-header 04 64984 496)\n(llvm:elf-program-header 03 64968 608)\n(llvm:elf-program-header 02 0 3360)\n(llvm:elf-program-header 01 568 110)\n(llvm:elf-program-header 00 64 504)\n(llvm:elf-program-header-flags 08 false true false false)\n(llvm:elf-program-header-flags 07 false true true false)\n(llvm:elf-program-header-flags 06 false true false false)\n(llvm:elf-program-header-flags 05 false true false false)\n(llvm:elf-program-header-flags 04 false true true false)\n(llvm:elf-program-header-flags 03 true true true false)\n(llvm:elf-program-header-flags 02 true true false true)\n(llvm:elf-program-header-flags 01 false true false false)\n(llvm:elf-program-header-flags 00 false true false false)\n(llvm:elf-virtual-program-header 08 4324808 568)\n(llvm:elf-virtual-program-header 07 0 0)\n(llvm:elf-virtual-program-header 06 4197404 68)\n(llvm:elf-virtual-program-header 05 4194984 32)\n(llvm:elf-virtual-program-header 04 4324824 496)\n(llvm:elf-virtual-program-header 03 4324808 632)\n(llvm:elf-virtual-program-header 02 4194304 3360)\n(llvm:elf-virtual-program-header 01 4194872 110)\n(llvm:elf-virtual-program-header 00 4194368 504)\n(llvm:entry-point 4195904)\n(llvm:name-reference 4325392 abort)\n(llvm:name-reference 4325384 __gmon_start__)\n(llvm:name-reference 4325376 __libc_start_main)\n(llvm:name-reference 4325344 _ITM_registerTMCloneTable)\n(llvm:name-reference 4325336 __gmon_start__)\n(llvm:name-reference 4325328 _ITM_deregisterTMCloneTable)\n(llvm:section-entry .shstrtab 0 240 68438)\n(llvm:section-entry .strtab 0 558 67880)\n(llvm:section-entry .symtab 0 2280 65600)\n(llvm:section-entry .comment 0 18 65576)\n(llvm:section-entry .bss 4325416 24 65576)\n(llvm:section-entry .data 4325400 16 65560)\n(llvm:section-entry .got.plt 4325352 48 65512)\n(llvm:section-entry .got 4325320 32 65480)\n(llvm:section-entry .dynamic 4324824 496 64984)\n(llvm:section-entry .fini_array 4324816 8 64976)\n(llvm:section-entry .init_array 4324808 8 64968)\n(llvm:section-entry .eh_frame 4197472 192 3168)\n(llvm:section-entry .eh_frame_hdr 4197404 68 3100)\n(llvm:section-entry .rodata 4197400 4 3096)\n(llvm:section-entry .fini 4197380 20 3076)\n(llvm:section-entry .text 4195904 1476 1600)\n(llvm:section-entry .plt 4195808 80 1504)\n(llvm:section-entry .init 4195776 24 1472)\n(llvm:section-entry .rela.plt 4195704 72 1400)\n(llvm:section-entry .rela.dyn 4195632 72 1328)\n(llvm:section-entry .gnu.version_r 4195584 48 1280)\n(llvm:section-entry .gnu.version 4195570 12 1266)\n(llvm:section-entry .dynstr 4195240 329 936)\n(llvm:section-entry .dynsym 4195096 144 792)\n(llvm:section-entry .gnu.hash 4195064 28 760)\n(llvm:section-entry .hash 4195016 44 712)\n(llvm:section-entry .note.ABI-tag 4194984 32 680)\n(llvm:section-entry .interp 4194872 110 568)\n(llvm:section-flags .shstrtab true false false)\n(llvm:section-flags .strtab true false false)\n(llvm:section-flags .symtab true false false)\n(llvm:section-flags .comment true false false)\n(llvm:section-flags .bss true true false)\n(llvm:section-flags .data true true false)\n(llvm:section-flags .got.plt true true false)\n(llvm:section-flags .got true true false)\n(llvm:section-flags .dynamic true true false)\n(llvm:section-flags .fini_array true true false)\n(llvm:section-flags .init_array true true false)\n(llvm:section-flags .eh_frame true false false)\n(llvm:section-flags .eh_frame_hdr true false false)\n(llvm:section-flags .rodata true false false)\n(llvm:section-flags .fini true false true)\n(llvm:section-flags .text true false true)\n(llvm:section-flags .plt true false true)\n(llvm:section-flags .init true false true)\n(llvm:section-flags .rela.plt true false false)\n(llvm:section-flags .rela.dyn true false false)\n(llvm:section-flags .gnu.version_r true false false)\n(llvm:section-flags .gnu.version true false false)\n(llvm:section-flags .dynstr true false false)\n(llvm:section-flags .dynsym true false false)\n(llvm:section-flags .gnu.hash true false false)\n(llvm:section-flags .hash true false false)\n(llvm:section-flags .note.ABI-tag true false false)\n(llvm:section-flags .interp true false false)\n(llvm:symbol-entry abort 0 0 0 0)\n(llvm:symbol-entry __libc_start_main 0 0 0 0)\n(llvm:symbol-entry _init 4195776 0 1472 4195776)\n(llvm:symbol-entry main 4196164 1216 1860 4196164)\n(llvm:symbol-entry _start 4195904 60 1600 4195904)\n(llvm:symbol-entry _dl_relocate_static_pie 4195968 4 1664 4195968)\n(llvm:symbol-entry abort@GLIBC_2.17 0 0 0 0)\n(llvm:symbol-entry _fini 4197380 0 3076 4197380)\n(llvm:symbol-entry __libc_start_main@GLIBC_2.34 0 0 0 0)\n(llvm:symbol-entry frame_dummy 4196160 0 1856 4196160)\n(llvm:symbol-entry __do_global_dtors_aux 4196112 0 1808 4196112)\n(llvm:symbol-entry register_tm_clones 4196048 0 1744 4196048)\n(llvm:symbol-entry deregister_tm_clones 4196000 0 1696 4196000)\n(llvm:symbol-entry call_weak_fn 4195972 20 1668 4195972)\n(mapped 4194304 3360 0)\n(mapped 4324808 608 64968)\n(named-region 4194304 3360 02)\n(named-region 4324808 632 03)\n(named-region 4194872 110 .interp)\n(named-region 4194984 32 .note.ABI-tag)\n(named-region 4195016 44 .hash)\n(named-region 4195064 28 .gnu.hash)\n(named-region 4195096 144 .dynsym)\n(named-region 4195240 329 .dynstr)\n(named-region 4195570 12 .gnu.version)\n(named-region 4195584 48 .gnu.version_r)\n(named-region 4195632 72 .rela.dyn)\n(named-region 4195704 72 .rela.plt)\n(named-region 4195776 24 .init)\n(named-region 4195808 80 .plt)\n(named-region 4195904 1476 .text)\n(named-region 4197380 20 .fini)\n(named-region 4197400 4 .rodata)\n(named-region 4197404 68 .eh_frame_hdr)\n(named-region 4197472 192 .eh_frame)\n(named-region 4324808 8 .init_array)\n(named-region 4324816 8 .fini_array)\n(named-region 4324824 496 .dynamic)\n(named-region 4325320 32 .got)\n(named-region 4325352 48 .got.plt)\n(named-region 4325400 16 .data)\n(named-region 4325416 24 .bss)\n(named-region 0 18 .comment)\n(named-region 0 2280 .symtab)\n(named-region 0 558 .strtab)\n(named-region 0 240 .shstrtab)\n(named-symbol 4195972 call_weak_fn)\n(named-symbol 4196000 deregister_tm_clones)\n(named-symbol 4196048 register_tm_clones)\n(named-symbol 4196112 __do_global_dtors_aux)\n(named-symbol 4196160 frame_dummy)\n(named-symbol 0 __libc_start_main@GLIBC_2.34)\n(named-symbol 4197380 _fini)\n(named-symbol 0 abort@GLIBC_2.17)\n(named-symbol 4195968 _dl_relocate_static_pie)\n(named-symbol 4195904 _start)\n(named-symbol 4196164 main)\n(named-symbol 4195776 _init)\n(named-symbol 0 __libc_start_main)\n(named-symbol 0 abort)\n(require libc.so.6)\n(section 4194872 110)\n(section 4194984 32)\n(section 4195016 44)\n(section 4195064 28)\n(section 4195096 144)\n(section 4195240 329)\n(section 4195570 12)\n(section 4195584 48)\n(section 4195632 72)\n(section 4195704 72)\n(section 4195776 24)\n(section 4195808 80)\n(section 4195904 1476)\n(section 4197380 20)\n(section 4197400 4)\n(section 4197404 68)\n(section 4197472 192)\n(section 4324808 8)\n(section 4324816 8)\n(section 4324824 496)\n(section 4325320 32)\n(section 4325352 48)\n(section 4325400 16)\n(section 4325416 24)\n(section 0 18)\n(section 0 2280)\n(section 0 558)\n(section 0 240)\n(segment 4194304 3360 true false true)\n(segment 4324808 632 true true false)\n(subarch v8)\n(symbol-chunk 4195972 20 4195972)\n(symbol-chunk 4195968 4 4195968)\n(symbol-chunk 4195904 60 4195904)\n(symbol-chunk 4196164 1216 4196164)\n(symbol-value 4195972 4195972)\n(symbol-value 4196000 4196000)\n(symbol-value 4196048 4196048)\n(symbol-value 4196112 4196112)\n(symbol-value 4196160 4196160)\n(symbol-value 4197380 4197380)\n(symbol-value 4195968 4195968)\n(symbol-value 4195904 4195904)\n(symbol-value 4196164 4196164)\n(symbol-value 4195776 4195776)\n(symbol-value 0 0)\n(system \"\")\n(vendor \"\")\n"), +Attr("abi-name","\"aarch64-linux-gnu-elf\"")]), +Sections([Section(".interp", 0x400238, "\x2f\x6e\x69\x78\x2f\x73\x74\x6f\x72\x65\x2f\x61\x31\x33\x61\x31\x71\x69\x32\x6b\x6e\x71\x61\x30\x73\x64\x31\x67\x76\x6d\x35\x78\x33\x34\x70\x72\x35\x33\x77\x67\x30\x69\x6c\x2d\x67\x6c\x69\x62\x63\x2d\x61\x61\x72\x63\x68\x36\x34\x2d\x75\x6e\x6b\x6e\x6f\x77\x6e\x2d\x6c\x69\x6e\x75\x78\x2d\x67\x6e\x75\x2d\x32\x2e\x33\x38\x2d\x34\x34\x2f\x6c\x69\x62\x2f\x6c\x64\x2d\x6c\x69\x6e\x75\x78\x2d\x61\x61\x72\x63\x68\x36\x34\x2e\x73\x6f\x2e\x31\x00"), +Section(".note.ABI-tag", 0x4002A8, "\x04\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\x47\x4e\x55\x00\x00\x00\x00\x00\x03\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00"), +Section(".hash", 0x4002C8, "\x03\x00\x00\x00\x06\x00\x00\x00\x03\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00"), +Section(".gnu.hash", 0x4002F8, "\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), +Section(".dynsym", 0x400318, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x01\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x01\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x00\x00\x00\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x2f\x01\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), +Section(".dynstr", 0x4003A8, "\x00\x5f\x5f\x6c\x69\x62\x63\x5f\x73\x74\x61\x72\x74\x5f\x6d\x61\x69\x6e\x00\x61\x62\x6f\x72\x74\x00\x6c\x69\x62\x63\x2e\x73\x6f\x2e\x36\x00\x47\x4c\x49\x42\x43\x5f\x32\x2e\x31\x37\x00\x47\x4c\x49\x42\x43\x5f\x32\x2e\x33\x34\x00\x2f\x6e\x69\x78\x2f\x73\x74\x6f\x72\x65\x2f\x61\x31\x33\x61\x31\x71\x69\x32\x6b\x6e\x71\x61\x30\x73\x64\x31\x67\x76\x6d\x35\x78\x33\x34\x70\x72\x35\x33\x77\x67\x30\x69\x6c\x2d\x67\x6c\x69\x62\x63\x2d\x61\x61\x72\x63\x68\x36\x34\x2d\x75\x6e\x6b\x6e\x6f\x77\x6e\x2d\x6c\x69\x6e\x75\x78\x2d\x67\x6e\x75\x2d\x32\x2e\x33\x38\x2d\x34\x34\x2f\x6c\x69\x62\x3a\x2f\x6e\x69\x78\x2f\x73\x74\x6f\x72\x65\x2f\x70\x70\x39\x31\x35\x70\x35\x6d\x68\x62\x62\x36\x31\x37\x30\x63\x63\x67\x66\x30\x69\x35\x39\x32\x70\x35\x30\x76\x79\x39\x39\x32\x2d\x61\x61\x72\x63\x68\x36\x34\x2d\x75\x6e\x6b\x6e\x6f\x77\x6e\x2d\x6c\x69\x6e\x75\x78\x2d\x67\x6e\x75\x2d\x67\x63\x63\x2d\x31\x33\x2e\x32\x2e\x30\x2d\x6c\x69\x62\x2f\x61\x61\x72\x63\x68\x36\x34\x2d\x75\x6e\x6b\x6e\x6f\x77\x6e\x2d\x6c\x69\x6e\x75\x78\x2d\x67\x6e\x75\x2f\x6c\x69\x62\x00\x5f\x49\x54\x4d\x5f\x64\x65\x72\x65\x67\x69\x73\x74\x65\x72\x54\x4d\x43\x6c\x6f\x6e\x65\x54\x61\x62\x6c\x65\x00\x5f\x5f\x67\x6d\x6f\x6e\x5f\x73\x74\x61\x72\x74\x5f\x5f\x00\x5f\x49\x54\x4d\x5f\x72\x65\x67\x69\x73\x74\x65\x72\x54\x4d\x43\x6c\x6f\x6e\x65\x54\x61\x62\x6c\x65\x00"), +Section(".gnu.version", 0x4004F2, "\x00\x00\x02\x00\x01\x00\x01\x00\x03\x00\x01\x00"), +Section(".gnu.version_r", 0x400500, "\x01\x00\x02\x00\x19\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x97\x91\x96\x06\x00\x00\x03\x00\x23\x00\x00\x00\x10\x00\x00\x00\xb4\x91\x96\x06\x00\x00\x02\x00\x2e\x00\x00\x00\x00\x00\x00\x00"), +Section(".rela.dyn", 0x400530, "\xd0\xff\x41\x00\x00\x00\x00\x00\x01\x04\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd8\xff\x41\x00\x00\x00\x00\x00\x01\x04\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\xff\x41\x00\x00\x00\x00\x00\x01\x04\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), +Section(".rela.plt", 0x400578, "\x00\x00\x42\x00\x00\x00\x00\x00\x02\x04\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x42\x00\x00\x00\x00\x00\x02\x04\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x42\x00\x00\x00\x00\x00\x02\x04\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), +Section(".init", 0x4005C0, "\x1f\x20\x03\xd5\xfd\x7b\xbf\xa9\xfd\x03\x00\x91\x2e\x00\x00\x94\xfd\x7b\xc1\xa8\xc0\x03\x5f\xd6"), +Section(".plt", 0x4005E0, "\xf0\x7b\xbf\xa9\xf0\x00\x00\xf0\x11\xfe\x47\xf9\x10\xe2\x3f\x91\x20\x02\x1f\xd6\x1f\x20\x03\xd5\x1f\x20\x03\xd5\x1f\x20\x03\xd5\x10\x01\x00\x90\x11\x02\x40\xf9\x10\x02\x00\x91\x20\x02\x1f\xd6\x10\x01\x00\x90\x11\x06\x40\xf9\x10\x22\x00\x91\x20\x02\x1f\xd6\x10\x01\x00\x90\x11\x0a\x40\xf9\x10\x42\x00\x91\x20\x02\x1f\xd6"), +Section(".text", 0x400640, "\x1f\x20\x03\xd5\x1d\x00\x80\xd2\x1e\x00\x80\xd2\xe5\x03\x00\xaa\xe1\x03\x40\xf9\xe2\x23\x00\x91\xe6\x03\x00\x91\x00\x00\x00\x90\x00\xd0\x19\x91\x03\x00\x80\xd2\x04\x00\x80\xd2\xe5\xff\xff\x97\xec\xff\xff\x97\x1f\x20\x03\xd5\x33\x00\x00\x14\x1f\x20\x03\xd5\xc0\x03\x5f\xd6\xe0\x00\x00\xf0\x00\xec\x47\xf9\x40\x00\x00\xb4\xe0\xff\xff\x17\xc0\x03\x5f\xd6\x1f\x20\x03\xd5\x1f\x20\x03\xd5\x00\x01\x00\x90\x00\xa0\x00\x91\x01\x01\x00\x90\x21\xa0\x00\x91\x3f\x00\x00\xeb\xc0\x00\x00\x54\xe1\x00\x00\xf0\x21\xe8\x47\xf9\x61\x00\x00\xb4\xf0\x03\x01\xaa\x00\x02\x1f\xd6\xc0\x03\x5f\xd6\x00\x01\x00\x90\x00\xa0\x00\x91\x01\x01\x00\x90\x21\xa0\x00\x91\x21\x00\x00\xcb\x22\xfc\x7f\xd3\x41\x0c\x81\x8b\x21\xfc\x41\x93\xc1\x00\x00\xb4\xe2\x00\x00\xf0\x42\xf0\x47\xf9\x62\x00\x00\xb4\xf0\x03\x02\xaa\x00\x02\x1f\xd6\xc0\x03\x5f\xd6\x1f\x20\x03\xd5\xfd\x7b\xbe\xa9\xfd\x03\x00\x91\xf3\x0b\x00\xf9\x13\x01\x00\x90\x60\xa2\x40\x39\x80\x00\x00\x37\xde\xff\xff\x97\x20\x00\x80\x52\x60\xa2\x00\x39\xf3\x0b\x40\xf9\xfd\x7b\xc2\xa8\xc0\x03\x5f\xd6\xe4\xff\xff\x17\xff\x43\x00\xd1\xe0\x0f\x00\xb9\xe1\x03\x00\xf9\x00\x01\x00\x90\x00\xb0\x00\x91\xe1\x0f\x40\xb9\x01\x00\x00\xb9\xe1\x0f\x40\xb9\x00\x01\x00\x90\x00\xe0\x00\x91\x01\x00\x00\xb9\xe1\x0f\x40\xb9\x00\x01\x00\x90\x00\xd0\x00\x91\x01\x00\x00\xb9\x00\x01\x00\x90\x00\xb0\x00\x91\x00\x00\x40\xb9\x1f\x00\x00\x71\xea\x00\x00\x54\x00\x01\x00\x90\x00\xc0\x00\x91\x01\x00\x40\xb9\x00\x01\x00\x90\x00\xb0\x00\x91\x01\x00\x00\xb9\x00\x01\x00\x90\x00\xb0\x00\x91\x00\x00\x40\xb9\x1f\x00\x00\x71\xed\x00\x00\x54\x00\x01\x00\x90\x00\xc0\x00\x91\x01\x00\x40\xb9\x00\x01\x00\x90\x00\xb0\x00\x91\x01\x00\x00\xb9\x00\x01\x00\x90\x00\xb0\x00\x91\x00\x00\x40\xb9\x1f\x10\x00\x71\x6c\x01\x00\x54\x00\x01\x00\x90\x00\xc0\x00\x91\x01\x00\x40\xb9\x00\x01\x00\x90\x00\xb0\x00\x91\x00\x00\x40\xb9\x21\x00\x00\x0b\x00\x01\x00\x90\x00\xb0\x00\x91\x01\x00\x00\xb9\x00\x01\x00\x90\x00\xb0\x00\x91\x00\x00\x40\xb9\x1f\x20\x00\x71\x6c\x01\x00\x54\x00\x01\x00\x90\x00\xc0\x00\x91\x01\x00\x40\xb9\x00\x01\x00\x90\x00\xb0\x00\x91\x00\x00\x40\xb9\x21\x00\x00\x0b\x00\x01\x00\x90\x00\xb0\x00\x91\x01\x00\x00\xb9\x00\x01\x00\x90\x00\xb0\x00\x91\x00\x00\x40\xb9\x1f\x8c\x01\x71\x6d\x01\x00\x54\x00\x01\x00\x90\x00\xc0\x00\x91\x01\x00\x40\xb9\x00\x01\x00\x90\x00\xb0\x00\x91\x00\x00\x40\xb9\x21\x00\x00\x0b\x00\x01\x00\x90\x00\xb0\x00\x91\x01\x00\x00\xb9\x00\x01\x00\x90\x00\xb0\x00\x91\x00\x00\x40\xb9\x1f\xa0\x0f\x71\x6d\x01\x00\x54\x00\x01\x00\x90\x00\xc0\x00\x91\x01\x00\x40\xb9\x00\x01\x00\x90\x00\xb0\x00\x91\x00\x00\x40\xb9\x21\x00\x00\x0b\x00\x01\x00\x90\x00\xb0\x00\x91\x01\x00\x00\xb9\x00\x01\x00\x90\x00\xb0\x00\x91\x01\x00\x40\xb9\x00\x01\x00\x90\x00\xc0\x00\x91\x00\x00\x40\xb9\x3f\x00\x00\x6b\x0a\x02\x00\x54\x00\x01\x00\x90\x00\xc0\x00\x91\x01\x00\x40\xb9\xe0\x03\x01\x2a\x00\x74\x1e\x53\x00\x00\x01\x0b\x00\x78\x1f\x53\xe1\x03\x00\x2a\x00\x01\x00\x90\x00\xb0\x00\x91\x00\x00\x40\xb9\x21\x00\x00\x0b\x00\x01\x00\x90\x00\xb0\x00\x91\x01\x00\x00\xb9\x00\x01\x00\x90\x00\xc0\x00\x91\x00\x00\x40\xb9\x01\x90\x01\x11\x00\x01\x00\x90\x00\xb0\x00\x91\x00\x00\x40\xb9\x3f\x00\x00\x6b\xeb\x01\x00\x54\x00\x01\x00\x90\x00\xc0\x00\x91\x01\x00\x40\xb9\x00\x01\x00\x90\x00\xc0\x00\x91\x00\x00\x40\xb9\x21\x7c\x00\x1b\x00\x01\x00\x90\x00\xb0\x00\x91\x00\x00\x40\xb9\x21\x00\x00\x0b\x00\x01\x00\x90\x00\xb0\x00\x91\x01\x00\x00\xb9\x00\x01\x00\x90\x00\xc0\x00\x91\x00\x00\x40\xb9\x01\xa0\x0f\x11\x00\x01\x00\x90\x00\xb0\x00\x91\x00\x00\x40\xb9\x3f\x00\x00\x6b\x0a\x02\x00\x54\x00\x01\x00\x90\x00\xc0\x00\x91\x00\x00\x40\xb9\x01\x78\x1f\x53\x00\x01\x00\x90\x00\xc0\x00\x91\x00\x00\x40\xb9\x21\x00\x00\x0b\x00\x01\x00\x90\x00\xb0\x00\x91\x00\x00\x40\xb9\x21\x00\x00\x0b\x00\x01\x00\x90\x00\xb0\x00\x91\x01\x00\x00\xb9\x00\x01\x00\x90\x00\xc0\x00\x91\x00\x00\x40\xb9\x01\x3c\x1f\x11\x00\x01\x00\x90\x00\xb0\x00\x91\x00\x00\x40\xb9\x3f\x00\x00\x6b\xea\x01\x00\x54\x00\x01\x00\x90\x00\xc0\x00\x91\x01\x00\x40\xb9\x00\x01\x00\x90\x00\xc0\x00\x91\x00\x00\x40\xb9\x21\x00\x00\x0b\x00\x01\x00\x90\x00\xb0\x00\x91\x00\x00\x40\xb9\x21\x00\x00\x0b\x00\x01\x00\x90\x00\xb0\x00\x91\x01\x00\x00\xb9\x00\x01\x00\x90\x00\xe0\x00\x91\x00\x00\x40\xb9\x00\x01\x00\x90\x00\xe0\x00\x91\x00\x00\x40\xb9\x1f\x00\x00\x71\x01\x01\x00\x54\x00\x01\x00\x90\x00\xe0\x00\x91\x00\x00\x40\xb9\x01\x08\x00\x11\x00\x01\x00\x90\x00\xe0\x00\x91\x01\x00\x00\xb9\x00\x01\x00\x90\x00\xe0\x00\x91\x00\x00\x40\xb9\x1f\xa0\x0f\x71\x08\x01\x00\x54\x00\x01\x00\x90\x00\xe0\x00\x91\x00\x00\x40\xb9\x01\x04\x00\x11\x00\x01\x00\x90\x00\xe0\x00\x91\x01\x00\x00\xb9\x00\x01\x00\x90\x00\xe0\x00\x91\x00\x00\x40\xb9\x1f\x04\x00\x31\x01\x01\x00\x54\x00\x01\x00\x90\x00\xe0\x00\x91\x00\x00\x40\xb9\x01\x04\x00\x11\x00\x01\x00\x90\x00\xe0\x00\x91\x01\x00\x00\xb9\x00\x01\x00\x90\x00\xe0\x00\x91\x01\x00\x40\xb9\x00\x01\x00\x90\x00\xd0\x00\x91\x00\x00\x40\xb9\x3f\x00\x00\x6b\x63\x01\x00\x54\x00\x01\x00\x90\x00\xd0\x00\x91\x01\x00\x40\xb9\x00\x01\x00\x90\x00\xe0\x00\x91\x00\x00\x40\xb9\x21\x00\x00\x0b\x00\x01\x00\x90\x00\xe0\x00\x91\x01\x00\x00\xb9\x00\x01\x00\x90\x00\xe0\x00\x91\x01\x00\x40\xb9\x00\x01\x00\x90\x00\xd0\x00\x91\x00\x00\x40\xb9\x3f\x00\x00\x6b\x68\x01\x00\x54\x00\x01\x00\x90\x00\xd0\x00\x91\x01\x00\x40\xb9\x00\x01\x00\x90\x00\xe0\x00\x91\x00\x00\x40\xb9\x21\x00\x00\x0b\x00\x01\x00\x90\x00\xe0\x00\x91\x01\x00\x00\xb9\x00\x01\x00\x90\x00\xe0\x00\x91\x01\x00\x40\xb9\x00\x01\x00\x90\x00\xd0\x00\x91\x00\x00\x40\xb9\x3f\x00\x00\x6b\x62\x01\x00\x54\x00\x01\x00\x90\x00\xd0\x00\x91\x01\x00\x40\xb9\x00\x01\x00\x90\x00\xe0\x00\x91\x00\x00\x40\xb9\x21\x00\x00\x0b\x00\x01\x00\x90\x00\xe0\x00\x91\x01\x00\x00\xb9\x00\x01\x00\x90\x00\xe0\x00\x91\x01\x00\x40\xb9\x00\x01\x00\x90\x00\xd0\x00\x91\x00\x00\x40\xb9\x3f\x00\x00\x6b\x69\x01\x00\x54\x00\x01\x00\x90\x00\xd0\x00\x91\x01\x00\x40\xb9\x00\x01\x00\x90\x00\xe0\x00\x91\x00\x00\x40\xb9\x21\x00\x00\x0b\x00\x01\x00\x90\x00\xe0\x00\x91\x01\x00\x00\xb9\x00\x00\x80\x52\xff\x43\x00\x91\xc0\x03\x5f\xd6"), +Section(".fini", 0x400C04, "\x1f\x20\x03\xd5\xfd\x7b\xbf\xa9\xfd\x03\x00\x91\xfd\x7b\xc1\xa8\xc0\x03\x5f\xd6"), +Section(".rodata", 0x400C18, "\x01\x00\x02\x00"), +Section(".eh_frame_hdr", 0x400C1C, "\x01\x1b\x03\x3b\x40\x00\x00\x00\x07\x00\x00\x00\x24\xfa\xff\xff\x58\x00\x00\x00\x64\xfa\xff\xff\x6c\x00\x00\x00\x84\xfa\xff\xff\x84\x00\x00\x00\xb4\xfa\xff\xff\x98\x00\x00\x00\xf4\xfa\xff\xff\xac\x00\x00\x00\x24\xfb\xff\xff\xd0\x00\x00\x00\x28\xfb\xff\xff\xe4\x00\x00\x00"), +Section(".eh_frame", 0x400C60, "\x10\x00\x00\x00\x00\x00\x00\x00\x01\x7a\x52\x00\x04\x78\x1e\x01\x1b\x0c\x1f\x00\x10\x00\x00\x00\x18\x00\x00\x00\xc4\xf9\xff\xff\x3c\x00\x00\x00\x00\x41\x07\x1e\x14\x00\x00\x00\x2c\x00\x00\x00\xf0\xf9\xff\xff\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x44\x00\x00\x00\xf8\xf9\xff\xff\x30\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x58\x00\x00\x00\x14\xfa\xff\xff\x3c\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x6c\x00\x00\x00\x40\xfa\xff\xff\x30\x00\x00\x00\x00\x41\x0e\x20\x9d\x04\x9e\x03\x42\x93\x02\x48\xde\xdd\xd3\x0e\x00\x00\x00\x00\x10\x00\x00\x00\x90\x00\x00\x00\x4c\xfa\xff\xff\x04\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\xa4\x00\x00\x00\x3c\xfa\xff\xff\xc0\x04\x00\x00\x00\x41\x0e\x10\x03\x2e\x01\x0e\x00\x00\x00\x00\x00\x00\x00\x00"), +Section(".fini_array", 0x41FDD0, "\x10\x07\x40\x00\x00\x00\x00\x00"), +Section(".dynamic", 0x41FDD8, "\x01\x00\x00\x00\x00\x00\x00\x00\x19\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x00\x00\x00\x00\x00\x00\x39\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\xc0\x05\x40\x00\x00\x00\x00\x00\x0d\x00\x00\x00\x00\x00\x00\x00\x04\x0c\x40\x00\x00\x00\x00\x00\x19\x00\x00\x00\x00\x00\x00\x00\xc8\xfd\x41\x00\x00\x00\x00\x00\x1b\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x1a\x00\x00\x00\x00\x00\x00\x00\xd0\xfd\x41\x00\x00\x00\x00\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\xc8\x02\x40\x00\x00\x00\x00\x00\xf5\xfe\xff\x6f\x00\x00\x00\x00\xf8\x02\x40\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\xa8\x03\x40\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x18\x03\x40\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x49\x01\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\xe8\xff\x41\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x48\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x17\x00\x00\x00\x00\x00\x00\x00\x78\x05\x40\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x30\x05\x40\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x48\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\xfe\xff\xff\x6f\x00\x00\x00\x00\x00\x05\x40\x00\x00\x00\x00\x00\xff\xff\xff\x6f\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xf0\xff\xff\x6f\x00\x00\x00\x00\xf2\x04\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), +Section(".init_array", 0x41FDC8, "\x40\x07\x40\x00\x00\x00\x00\x00"), +Section(".got", 0x41FFC8, "\xd8\xfd\x41\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), +Section(".got.plt", 0x41FFE8, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x05\x40\x00\x00\x00\x00\x00\xe0\x05\x40\x00\x00\x00\x00\x00\xe0\x05\x40\x00\x00\x00\x00\x00"), +Section(".data", 0x420018, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")]), +Memmap([Annotation(Region(0x400000,0x400D1F), +Attr("segment","02 0x400000 3360")), Annotation(Region(0x400238,0x4002A5), +Attr("section","\".interp\"")), Annotation(Region(0x4002A8,0x4002C7), +Attr("section","\".note.ABI-tag\"")), Annotation(Region(0x4002C8,0x4002F3), +Attr("section","\".hash\"")), Annotation(Region(0x4002F8,0x400313), +Attr("section","\".gnu.hash\"")), Annotation(Region(0x400318,0x4003A7), +Attr("section","\".dynsym\"")), Annotation(Region(0x4003A8,0x4004F0), +Attr("section","\".dynstr\"")), Annotation(Region(0x4004F2,0x4004FD), +Attr("section","\".gnu.version\"")), Annotation(Region(0x400500,0x40052F), +Attr("section","\".gnu.version_r\"")), Annotation(Region(0x400530,0x400577), +Attr("section","\".rela.dyn\"")), Annotation(Region(0x400578,0x4005BF), +Attr("section","\".rela.plt\"")), Annotation(Region(0x4005C0,0x4005D7), +Attr("section","\".init\"")), Annotation(Region(0x4005C0,0x4005D7), +Attr("code-region","()")), Annotation(Region(0x4005E0,0x40062F), +Attr("section","\".plt\"")), Annotation(Region(0x400640,0x40067B), +Attr("symbol","\"_start\"")), Annotation(Region(0x400640,0x40067B), +Attr("symbol-info","_start 0x400640 60")), +Annotation(Region(0x400680,0x400683), +Attr("symbol","\"_dl_relocate_static_pie\"")), +Annotation(Region(0x4005E0,0x40062F), Attr("code-region","()")), +Annotation(Region(0x400640,0x400C03), Attr("code-region","()")), +Annotation(Region(0x400680,0x400683), +Attr("symbol-info","_dl_relocate_static_pie 0x400680 4")), +Annotation(Region(0x400684,0x400697), Attr("symbol","\"call_weak_fn\"")), +Annotation(Region(0x400684,0x400697), +Attr("symbol-info","call_weak_fn 0x400684 20")), +Annotation(Region(0x400640,0x400C03), Attr("section","\".text\"")), +Annotation(Region(0x400744,0x400C03), Attr("symbol","\"main\"")), +Annotation(Region(0x400744,0x400C03), +Attr("symbol-info","main 0x400744 1216")), +Annotation(Region(0x400C04,0x400C17), Attr("section","\".fini\"")), +Annotation(Region(0x400C04,0x400C17), Attr("code-region","()")), +Annotation(Region(0x400C18,0x400C1B), Attr("section","\".rodata\"")), +Annotation(Region(0x400C1C,0x400C5F), Attr("section","\".eh_frame_hdr\"")), +Annotation(Region(0x400C60,0x400D1F), Attr("section","\".eh_frame\"")), +Annotation(Region(0x41FDC8,0x420027), Attr("segment","03 0x41FDC8 632")), +Annotation(Region(0x41FDD0,0x41FDD7), Attr("section","\".fini_array\"")), +Annotation(Region(0x41FDD8,0x41FFC7), Attr("section","\".dynamic\"")), +Annotation(Region(0x41FDC8,0x41FDCF), Attr("section","\".init_array\"")), +Annotation(Region(0x41FFC8,0x41FFE7), Attr("section","\".got\"")), +Annotation(Region(0x41FFE8,0x420017), Attr("section","\".got.plt\"")), +Annotation(Region(0x420018,0x420027), Attr("section","\".data\""))]), +Program(Tid(4_959, "%0000135f"), Attrs([]), Subs([Sub(Tid(4_901, "@.plt"), + Attrs([Attr("c.proto","signed (*)(void)"), Attr("address","0x4005E0"), +Attr("stub","()")]), ".plt", Args([Arg(Tid(4_960, "%00001360"), + Attrs([Attr("c.layout","[signed : 32]"), Attr("c.data","Top:u32"), +Attr("c.type","signed")]), Var("\.plt_result",Imm(32)), +LOW(32,Var("R0",Imm(64))), Out())]), Blks([Blk(Tid(4_325, "@.plt"), + Attrs([Attr("address","0x4005E0")]), Phis([]), +Defs([Def(Tid(4_329, "%000010e9"), Attrs([Attr("address","0x4005E0"), +Attr("insn","stp x16, x30, [sp, #-0x10]!")]), Var("#29",Imm(64)), +PLUS(Var("R31",Imm(64)),Int(18446744073709551600,64))), +Def(Tid(4_335, "%000010ef"), Attrs([Attr("address","0x4005E0"), +Attr("insn","stp x16, x30, [sp, #-0x10]!")]), Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("#29",Imm(64)),Var("R16",Imm(64)),LittleEndian(),64)), +Def(Tid(4_341, "%000010f5"), Attrs([Attr("address","0x4005E0"), +Attr("insn","stp x16, x30, [sp, #-0x10]!")]), Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),PLUS(Var("#29",Imm(64)),Int(8,64)),Var("R30",Imm(64)),LittleEndian(),64)), +Def(Tid(4_345, "%000010f9"), Attrs([Attr("address","0x4005E0"), +Attr("insn","stp x16, x30, [sp, #-0x10]!")]), Var("R31",Imm(64)), +Var("#29",Imm(64))), Def(Tid(4_350, "%000010fe"), + Attrs([Attr("address","0x4005E4"), Attr("insn","adrp x16, #0x1f000")]), + Var("R16",Imm(64)), Int(4321280,64)), Def(Tid(4_357, "%00001105"), + Attrs([Attr("address","0x4005E8"), Attr("insn","ldr x17, [x16, #0xff8]")]), + Var("R17",Imm(64)), +Load(Var("mem",Mem(64,8)),PLUS(Var("R16",Imm(64)),Int(4088,64)),LittleEndian(),64)), +Def(Tid(4_363, "%0000110b"), Attrs([Attr("address","0x4005EC"), +Attr("insn","add x16, x16, #0xff8")]), Var("R16",Imm(64)), +PLUS(Var("R16",Imm(64)),Int(4088,64)))]), Jmps([Call(Tid(4_368, "%00001110"), + Attrs([Attr("address","0x4005F0"), Attr("insn","br x17")]), Int(1,1), +(Indirect(Var("R17",Imm(64))),))]))])), +Sub(Tid(4_902, "@__do_global_dtors_aux"), + Attrs([Attr("c.proto","signed (*)(void)"), Attr("address","0x400710")]), + "__do_global_dtors_aux", Args([Arg(Tid(4_961, "%00001361"), + Attrs([Attr("c.layout","[signed : 32]"), Attr("c.data","Top:u32"), +Attr("c.type","signed")]), Var("__do_global_dtors_aux_result",Imm(32)), +LOW(32,Var("R0",Imm(64))), Out())]), +Blks([Blk(Tid(4_065, "@__do_global_dtors_aux"), + Attrs([Attr("address","0x400710")]), Phis([]), +Defs([Def(Tid(4_069, "%00000fe5"), Attrs([Attr("address","0x400710"), +Attr("insn","stp x29, x30, [sp, #-0x20]!")]), Var("#28",Imm(64)), +PLUS(Var("R31",Imm(64)),Int(18446744073709551584,64))), +Def(Tid(4_075, "%00000feb"), Attrs([Attr("address","0x400710"), +Attr("insn","stp x29, x30, [sp, #-0x20]!")]), Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("#28",Imm(64)),Var("R29",Imm(64)),LittleEndian(),64)), +Def(Tid(4_081, "%00000ff1"), Attrs([Attr("address","0x400710"), +Attr("insn","stp x29, x30, [sp, #-0x20]!")]), Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),PLUS(Var("#28",Imm(64)),Int(8,64)),Var("R30",Imm(64)),LittleEndian(),64)), +Def(Tid(4_085, "%00000ff5"), Attrs([Attr("address","0x400710"), +Attr("insn","stp x29, x30, [sp, #-0x20]!")]), Var("R31",Imm(64)), +Var("#28",Imm(64))), Def(Tid(4_091, "%00000ffb"), + Attrs([Attr("address","0x400714"), Attr("insn","mov x29, sp")]), + Var("R29",Imm(64)), Var("R31",Imm(64))), Def(Tid(4_099, "%00001003"), + Attrs([Attr("address","0x400718"), Attr("insn","str x19, [sp, #0x10]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),PLUS(Var("R31",Imm(64)),Int(16,64)),Var("R19",Imm(64)),LittleEndian(),64)), +Def(Tid(4_104, "%00001008"), Attrs([Attr("address","0x40071C"), +Attr("insn","adrp x19, #0x20000")]), Var("R19",Imm(64)), Int(4325376,64)), +Def(Tid(4_111, "%0000100f"), Attrs([Attr("address","0x400720"), +Attr("insn","ldrb w0, [x19, #0x28]")]), Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),PLUS(Var("R19",Imm(64)),Int(40,64)),LittleEndian(),8)))]), +Jmps([Goto(Tid(4_117, "%00001015"), Attrs([Attr("address","0x400724"), +Attr("insn","tbnz w0, #0x0, #0x10")]), + EQ(Extract(0,0,Var("R0",Imm(64))),Int(1,1)), +Direct(Tid(4_115, "%00001013"))), Goto(Tid(4_903, "%00001327"), Attrs([]), + Int(1,1), Direct(Tid(4_152, "%00001038")))])), Blk(Tid(4_152, "%00001038"), + Attrs([Attr("address","0x400728")]), Phis([]), +Defs([Def(Tid(4_155, "%0000103b"), Attrs([Attr("address","0x400728"), +Attr("insn","bl #-0x88")]), Var("R30",Imm(64)), Int(4196140,64))]), +Jmps([Call(Tid(4_157, "%0000103d"), Attrs([Attr("address","0x400728"), +Attr("insn","bl #-0x88")]), Int(1,1), +(Direct(Tid(4_917, "@deregister_tm_clones")),Direct(Tid(4_159, "%0000103f"))))])), +Blk(Tid(4_159, "%0000103f"), Attrs([Attr("address","0x40072C")]), Phis([]), +Defs([Def(Tid(4_162, "%00001042"), Attrs([Attr("address","0x40072C"), +Attr("insn","mov w0, #0x1")]), Var("R0",Imm(64)), Int(1,64)), +Def(Tid(4_170, "%0000104a"), Attrs([Attr("address","0x400730"), +Attr("insn","strb w0, [x19, #0x28]")]), Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),PLUS(Var("R19",Imm(64)),Int(40,64)),Extract(7,0,Var("R0",Imm(64))),LittleEndian(),8))]), +Jmps([Goto(Tid(4_904, "%00001328"), Attrs([]), Int(1,1), +Direct(Tid(4_115, "%00001013")))])), Blk(Tid(4_115, "%00001013"), + Attrs([Attr("address","0x400734")]), Phis([]), +Defs([Def(Tid(4_125, "%0000101d"), Attrs([Attr("address","0x400734"), +Attr("insn","ldr x19, [sp, #0x10]")]), Var("R19",Imm(64)), +Load(Var("mem",Mem(64,8)),PLUS(Var("R31",Imm(64)),Int(16,64)),LittleEndian(),64)), +Def(Tid(4_132, "%00001024"), Attrs([Attr("address","0x400738"), +Attr("insn","ldp x29, x30, [sp], #0x20")]), Var("R29",Imm(64)), +Load(Var("mem",Mem(64,8)),Var("R31",Imm(64)),LittleEndian(),64)), +Def(Tid(4_137, "%00001029"), Attrs([Attr("address","0x400738"), +Attr("insn","ldp x29, x30, [sp], #0x20")]), Var("R30",Imm(64)), +Load(Var("mem",Mem(64,8)),PLUS(Var("R31",Imm(64)),Int(8,64)),LittleEndian(),64)), +Def(Tid(4_141, "%0000102d"), Attrs([Attr("address","0x400738"), +Attr("insn","ldp x29, x30, [sp], #0x20")]), Var("R31",Imm(64)), +PLUS(Var("R31",Imm(64)),Int(32,64)))]), Jmps([Call(Tid(4_146, "%00001032"), + Attrs([Attr("address","0x40073C"), Attr("insn","ret")]), Int(1,1), +(Indirect(Var("R30",Imm(64))),))]))])), Sub(Tid(4_905, "@__gmon_start__"), + Attrs([Attr("c.proto","signed (*)(void)"), Attr("address","0x400610"), +Attr("stub","()")]), "__gmon_start__", Args([Arg(Tid(4_962, "%00001362"), + Attrs([Attr("c.layout","[signed : 32]"), Attr("c.data","Top:u32"), +Attr("c.type","signed")]), Var("__gmon_start___result",Imm(32)), +LOW(32,Var("R0",Imm(64))), Out())]), Blks([Blk(Tid(4_233, "@__gmon_start__"), + Attrs([Attr("address","0x400610")]), Phis([]), +Defs([Def(Tid(4_400, "%00001130"), Attrs([Attr("address","0x400610"), +Attr("insn","adrp x16, #0x20000")]), Var("R16",Imm(64)), Int(4325376,64)), +Def(Tid(4_407, "%00001137"), Attrs([Attr("address","0x400614"), +Attr("insn","ldr x17, [x16, #0x8]")]), Var("R17",Imm(64)), +Load(Var("mem",Mem(64,8)),PLUS(Var("R16",Imm(64)),Int(8,64)),LittleEndian(),64)), +Def(Tid(4_413, "%0000113d"), Attrs([Attr("address","0x400618"), +Attr("insn","add x16, x16, #0x8")]), Var("R16",Imm(64)), +PLUS(Var("R16",Imm(64)),Int(8,64)))]), Jmps([Call(Tid(4_418, "%00001142"), + Attrs([Attr("address","0x40061C"), Attr("insn","br x17")]), Int(1,1), +(Indirect(Var("R17",Imm(64))),))]))])), Sub(Tid(4_906, "@__libc_start_main"), + Attrs([Attr("c.proto","signed (*)(signed (*)(signed , char** , char** );* main, signed , char** , \nvoid* auxv)"), +Attr("address","0x400600"), Attr("stub","()")]), "__libc_start_main", + Args([Arg(Tid(4_963, "%00001363"), + Attrs([Attr("c.layout","**[ : 64]"), +Attr("c.data","Top:u64 ptr ptr"), +Attr("c.type","signed (*)(signed , char** , char** );*")]), + Var("__libc_start_main_main",Imm(64)), Var("R0",Imm(64)), In()), +Arg(Tid(4_964, "%00001364"), Attrs([Attr("c.layout","[signed : 32]"), +Attr("c.data","Top:u32"), Attr("c.type","signed")]), + Var("__libc_start_main_arg2",Imm(32)), LOW(32,Var("R1",Imm(64))), In()), +Arg(Tid(4_965, "%00001365"), Attrs([Attr("c.layout","**[char : 8]"), +Attr("c.data","Top:u8 ptr ptr"), Attr("c.type","char**")]), + Var("__libc_start_main_arg3",Imm(64)), Var("R2",Imm(64)), Both()), +Arg(Tid(4_966, "%00001366"), Attrs([Attr("c.layout","*[ : 8]"), +Attr("c.data","{} ptr"), Attr("c.type","void*")]), + Var("__libc_start_main_auxv",Imm(64)), Var("R3",Imm(64)), Both()), +Arg(Tid(4_967, "%00001367"), Attrs([Attr("c.layout","[signed : 32]"), +Attr("c.data","Top:u32"), Attr("c.type","signed")]), + Var("__libc_start_main_result",Imm(32)), LOW(32,Var("R0",Imm(64))), +Out())]), Blks([Blk(Tid(1_623, "@__libc_start_main"), + Attrs([Attr("address","0x400600")]), Phis([]), +Defs([Def(Tid(4_378, "%0000111a"), Attrs([Attr("address","0x400600"), +Attr("insn","adrp x16, #0x20000")]), Var("R16",Imm(64)), Int(4325376,64)), +Def(Tid(4_385, "%00001121"), Attrs([Attr("address","0x400604"), +Attr("insn","ldr x17, [x16]")]), Var("R17",Imm(64)), +Load(Var("mem",Mem(64,8)),Var("R16",Imm(64)),LittleEndian(),64)), +Def(Tid(4_391, "%00001127"), Attrs([Attr("address","0x400608"), +Attr("insn","add x16, x16, #0x0")]), Var("R16",Imm(64)), +Var("R16",Imm(64)))]), Jmps([Call(Tid(4_396, "%0000112c"), + Attrs([Attr("address","0x40060C"), Attr("insn","br x17")]), Int(1,1), +(Indirect(Var("R17",Imm(64))),))]))])), Sub(Tid(4_907, "@__wrap_main"), + Attrs([Attr("c.proto","signed (*)(void)"), Attr("address","0x400674")]), + "__wrap_main", Args([Arg(Tid(4_968, "%00001368"), + Attrs([Attr("c.layout","[signed : 32]"), Attr("c.data","Top:u32"), +Attr("c.type","signed")]), Var("__wrap_main_result",Imm(32)), +LOW(32,Var("R0",Imm(64))), Out())]), Blks([Blk(Tid(1_635, "@__wrap_main"), + Attrs([Attr("address","0x400674")]), Phis([]), Defs([]), +Jmps([Call(Tid(1_640, "%00000668"), Attrs([Attr("address","0x400678"), +Attr("insn","b #0xcc")]), Int(1,1), (Direct(Tid(4_921, "@main")),))]))])), +Sub(Tid(4_908, "@_dl_relocate_static_pie"), + Attrs([Attr("c.proto","signed (*)(void)"), Attr("address","0x400680")]), + "_dl_relocate_static_pie", Args([Arg(Tid(4_969, "%00001369"), + Attrs([Attr("c.layout","[signed : 32]"), Attr("c.data","Top:u32"), +Attr("c.type","signed")]), Var("_dl_relocate_static_pie_result",Imm(32)), +LOW(32,Var("R0",Imm(64))), Out())]), +Blks([Blk(Tid(3_905, "@_dl_relocate_static_pie"), + Attrs([Attr("address","0x400680")]), Phis([]), Defs([]), +Jmps([Call(Tid(3_908, "%00000f44"), Attrs([Attr("address","0x400680"), +Attr("insn","ret")]), Int(1,1), (Indirect(Var("R30",Imm(64))),))]))])), +Sub(Tid(4_909, "@_fini"), Attrs([Attr("c.proto","signed (*)(void)"), +Attr("address","0x400C04")]), "_fini", Args([Arg(Tid(4_970, "%0000136a"), + Attrs([Attr("c.layout","[signed : 32]"), Attr("c.data","Top:u32"), +Attr("c.type","signed")]), Var("_fini_result",Imm(32)), +LOW(32,Var("R0",Imm(64))), Out())]), Blks([Blk(Tid(32, "@_fini"), + Attrs([Attr("address","0x400C04")]), Phis([]), +Defs([Def(Tid(38, "%00000026"), Attrs([Attr("address","0x400C08"), +Attr("insn","stp x29, x30, [sp, #-0x10]!")]), Var("#0",Imm(64)), +PLUS(Var("R31",Imm(64)),Int(18446744073709551600,64))), +Def(Tid(44, "%0000002c"), Attrs([Attr("address","0x400C08"), +Attr("insn","stp x29, x30, [sp, #-0x10]!")]), Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("#0",Imm(64)),Var("R29",Imm(64)),LittleEndian(),64)), +Def(Tid(50, "%00000032"), Attrs([Attr("address","0x400C08"), +Attr("insn","stp x29, x30, [sp, #-0x10]!")]), Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),PLUS(Var("#0",Imm(64)),Int(8,64)),Var("R30",Imm(64)),LittleEndian(),64)), +Def(Tid(54, "%00000036"), Attrs([Attr("address","0x400C08"), +Attr("insn","stp x29, x30, [sp, #-0x10]!")]), Var("R31",Imm(64)), +Var("#0",Imm(64))), Def(Tid(60, "%0000003c"), + Attrs([Attr("address","0x400C0C"), Attr("insn","mov x29, sp")]), + Var("R29",Imm(64)), Var("R31",Imm(64))), Def(Tid(67, "%00000043"), + Attrs([Attr("address","0x400C10"), +Attr("insn","ldp x29, x30, [sp], #0x10")]), Var("R29",Imm(64)), +Load(Var("mem",Mem(64,8)),Var("R31",Imm(64)),LittleEndian(),64)), +Def(Tid(72, "%00000048"), Attrs([Attr("address","0x400C10"), +Attr("insn","ldp x29, x30, [sp], #0x10")]), Var("R30",Imm(64)), +Load(Var("mem",Mem(64,8)),PLUS(Var("R31",Imm(64)),Int(8,64)),LittleEndian(),64)), +Def(Tid(76, "%0000004c"), Attrs([Attr("address","0x400C10"), +Attr("insn","ldp x29, x30, [sp], #0x10")]), Var("R31",Imm(64)), +PLUS(Var("R31",Imm(64)),Int(16,64)))]), Jmps([Call(Tid(81, "%00000051"), + Attrs([Attr("address","0x400C14"), Attr("insn","ret")]), Int(1,1), +(Indirect(Var("R30",Imm(64))),))]))])), Sub(Tid(4_910, "@_init"), + Attrs([Attr("c.proto","signed (*)(void)"), Attr("address","0x4005C0")]), + "_init", Args([Arg(Tid(4_971, "%0000136b"), + Attrs([Attr("c.layout","[signed : 32]"), Attr("c.data","Top:u32"), +Attr("c.type","signed")]), Var("_init_result",Imm(32)), +LOW(32,Var("R0",Imm(64))), Out())]), Blks([Blk(Tid(4_468, "@_init"), + Attrs([Attr("address","0x4005C0")]), Phis([]), +Defs([Def(Tid(4_474, "%0000117a"), Attrs([Attr("address","0x4005C4"), +Attr("insn","stp x29, x30, [sp, #-0x10]!")]), Var("#30",Imm(64)), +PLUS(Var("R31",Imm(64)),Int(18446744073709551600,64))), +Def(Tid(4_480, "%00001180"), Attrs([Attr("address","0x4005C4"), +Attr("insn","stp x29, x30, [sp, #-0x10]!")]), Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("#30",Imm(64)),Var("R29",Imm(64)),LittleEndian(),64)), +Def(Tid(4_486, "%00001186"), Attrs([Attr("address","0x4005C4"), +Attr("insn","stp x29, x30, [sp, #-0x10]!")]), Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),PLUS(Var("#30",Imm(64)),Int(8,64)),Var("R30",Imm(64)),LittleEndian(),64)), +Def(Tid(4_490, "%0000118a"), Attrs([Attr("address","0x4005C4"), +Attr("insn","stp x29, x30, [sp, #-0x10]!")]), Var("R31",Imm(64)), +Var("#30",Imm(64))), Def(Tid(4_496, "%00001190"), + Attrs([Attr("address","0x4005C8"), Attr("insn","mov x29, sp")]), + Var("R29",Imm(64)), Var("R31",Imm(64))), Def(Tid(4_501, "%00001195"), + Attrs([Attr("address","0x4005CC"), Attr("insn","bl #0xb8")]), + Var("R30",Imm(64)), Int(4195792,64))]), Jmps([Call(Tid(4_503, "%00001197"), + Attrs([Attr("address","0x4005CC"), Attr("insn","bl #0xb8")]), Int(1,1), +(Direct(Tid(4_915, "@call_weak_fn")),Direct(Tid(4_505, "%00001199"))))])), +Blk(Tid(4_505, "%00001199"), Attrs([Attr("address","0x4005D0")]), Phis([]), +Defs([Def(Tid(4_510, "%0000119e"), Attrs([Attr("address","0x4005D0"), +Attr("insn","ldp x29, x30, [sp], #0x10")]), Var("R29",Imm(64)), +Load(Var("mem",Mem(64,8)),Var("R31",Imm(64)),LittleEndian(),64)), +Def(Tid(4_515, "%000011a3"), Attrs([Attr("address","0x4005D0"), +Attr("insn","ldp x29, x30, [sp], #0x10")]), Var("R30",Imm(64)), +Load(Var("mem",Mem(64,8)),PLUS(Var("R31",Imm(64)),Int(8,64)),LittleEndian(),64)), +Def(Tid(4_519, "%000011a7"), Attrs([Attr("address","0x4005D0"), +Attr("insn","ldp x29, x30, [sp], #0x10")]), Var("R31",Imm(64)), +PLUS(Var("R31",Imm(64)),Int(16,64)))]), Jmps([Call(Tid(4_524, "%000011ac"), + Attrs([Attr("address","0x4005D4"), Attr("insn","ret")]), Int(1,1), +(Indirect(Var("R30",Imm(64))),))]))])), Sub(Tid(4_911, "@_start"), + Attrs([Attr("c.proto","signed (*)(void)"), Attr("address","0x400640"), +Attr("entry-point","()")]), "_start", Args([Arg(Tid(4_972, "%0000136c"), + Attrs([Attr("c.layout","[signed : 32]"), Attr("c.data","Top:u32"), +Attr("c.type","signed")]), Var("_start_result",Imm(32)), +LOW(32,Var("R0",Imm(64))), Out())]), Blks([Blk(Tid(1_561, "@_start"), + Attrs([Attr("address","0x400640")]), Phis([]), +Defs([Def(Tid(1_566, "%0000061e"), Attrs([Attr("address","0x400644"), +Attr("insn","mov x29, #0x0")]), Var("R29",Imm(64)), Int(0,64)), +Def(Tid(1_571, "%00000623"), Attrs([Attr("address","0x400648"), +Attr("insn","mov x30, #0x0")]), Var("R30",Imm(64)), Int(0,64)), +Def(Tid(1_577, "%00000629"), Attrs([Attr("address","0x40064C"), +Attr("insn","mov x5, x0")]), Var("R5",Imm(64)), Var("R0",Imm(64))), +Def(Tid(1_584, "%00000630"), Attrs([Attr("address","0x400650"), +Attr("insn","ldr x1, [sp]")]), Var("R1",Imm(64)), +Load(Var("mem",Mem(64,8)),Var("R31",Imm(64)),LittleEndian(),64)), +Def(Tid(1_590, "%00000636"), Attrs([Attr("address","0x400654"), +Attr("insn","add x2, sp, #0x8")]), Var("R2",Imm(64)), +PLUS(Var("R31",Imm(64)),Int(8,64))), Def(Tid(1_596, "%0000063c"), + Attrs([Attr("address","0x400658"), Attr("insn","mov x6, sp")]), + Var("R6",Imm(64)), Var("R31",Imm(64))), Def(Tid(1_601, "%00000641"), + Attrs([Attr("address","0x40065C"), Attr("insn","adrp x0, #0x0")]), + Var("R0",Imm(64)), Int(4194304,64)), Def(Tid(1_607, "%00000647"), + Attrs([Attr("address","0x400660"), Attr("insn","add x0, x0, #0x674")]), + Var("R0",Imm(64)), PLUS(Var("R0",Imm(64)),Int(1652,64))), +Def(Tid(1_612, "%0000064c"), Attrs([Attr("address","0x400664"), +Attr("insn","mov x3, #0x0")]), Var("R3",Imm(64)), Int(0,64)), +Def(Tid(1_617, "%00000651"), Attrs([Attr("address","0x400668"), +Attr("insn","mov x4, #0x0")]), Var("R4",Imm(64)), Int(0,64)), +Def(Tid(1_622, "%00000656"), Attrs([Attr("address","0x40066C"), +Attr("insn","bl #-0x6c")]), Var("R30",Imm(64)), Int(4195952,64))]), +Jmps([Call(Tid(1_625, "%00000659"), Attrs([Attr("address","0x40066C"), +Attr("insn","bl #-0x6c")]), Int(1,1), +(Direct(Tid(4_906, "@__libc_start_main")),Direct(Tid(1_627, "%0000065b"))))])), +Blk(Tid(1_627, "%0000065b"), Attrs([Attr("address","0x400670")]), Phis([]), +Defs([Def(Tid(1_630, "%0000065e"), Attrs([Attr("address","0x400670"), +Attr("insn","bl #-0x50")]), Var("R30",Imm(64)), Int(4195956,64))]), +Jmps([Call(Tid(1_633, "%00000661"), Attrs([Attr("address","0x400670"), +Attr("insn","bl #-0x50")]), Int(1,1), +(Direct(Tid(4_914, "@abort")),Direct(Tid(4_912, "%00001330"))))])), +Blk(Tid(4_912, "%00001330"), Attrs([]), Phis([]), Defs([]), +Jmps([Call(Tid(4_913, "%00001331"), Attrs([]), Int(1,1), +(Direct(Tid(4_907, "@__wrap_main")),))]))])), Sub(Tid(4_914, "@abort"), + Attrs([Attr("noreturn","()"), Attr("c.proto","void (*)(void)"), +Attr("address","0x400620"), Attr("stub","()")]), "abort", Args([]), +Blks([Blk(Tid(1_631, "@abort"), Attrs([Attr("address","0x400620")]), + Phis([]), Defs([Def(Tid(4_422, "%00001146"), + Attrs([Attr("address","0x400620"), Attr("insn","adrp x16, #0x20000")]), + Var("R16",Imm(64)), Int(4325376,64)), Def(Tid(4_429, "%0000114d"), + Attrs([Attr("address","0x400624"), Attr("insn","ldr x17, [x16, #0x10]")]), + Var("R17",Imm(64)), +Load(Var("mem",Mem(64,8)),PLUS(Var("R16",Imm(64)),Int(16,64)),LittleEndian(),64)), +Def(Tid(4_435, "%00001153"), Attrs([Attr("address","0x400628"), +Attr("insn","add x16, x16, #0x10")]), Var("R16",Imm(64)), +PLUS(Var("R16",Imm(64)),Int(16,64)))]), Jmps([Call(Tid(4_440, "%00001158"), + Attrs([Attr("address","0x40062C"), Attr("insn","br x17")]), Int(1,1), +(Indirect(Var("R17",Imm(64))),))]))])), Sub(Tid(4_915, "@call_weak_fn"), + Attrs([Attr("c.proto","signed (*)(void)"), Attr("address","0x400684")]), + "call_weak_fn", Args([Arg(Tid(4_973, "%0000136d"), + Attrs([Attr("c.layout","[signed : 32]"), Attr("c.data","Top:u32"), +Attr("c.type","signed")]), Var("call_weak_fn_result",Imm(32)), +LOW(32,Var("R0",Imm(64))), Out())]), Blks([Blk(Tid(3_910, "@call_weak_fn"), + Attrs([Attr("address","0x400684")]), Phis([]), +Defs([Def(Tid(3_913, "%00000f49"), Attrs([Attr("address","0x400684"), +Attr("insn","adrp x0, #0x1f000")]), Var("R0",Imm(64)), Int(4321280,64)), +Def(Tid(3_920, "%00000f50"), Attrs([Attr("address","0x400688"), +Attr("insn","ldr x0, [x0, #0xfd8]")]), Var("R0",Imm(64)), +Load(Var("mem",Mem(64,8)),PLUS(Var("R0",Imm(64)),Int(4056,64)),LittleEndian(),64))]), +Jmps([Goto(Tid(3_926, "%00000f56"), Attrs([Attr("address","0x40068C"), +Attr("insn","cbz x0, #0x8")]), EQ(Var("R0",Imm(64)),Int(0,64)), +Direct(Tid(3_924, "%00000f54"))), Goto(Tid(4_916, "%00001334"), Attrs([]), + Int(1,1), Direct(Tid(4_232, "%00001088")))])), Blk(Tid(3_924, "%00000f54"), + Attrs([Attr("address","0x400694")]), Phis([]), Defs([]), +Jmps([Call(Tid(3_932, "%00000f5c"), Attrs([Attr("address","0x400694"), +Attr("insn","ret")]), Int(1,1), (Indirect(Var("R30",Imm(64))),))])), +Blk(Tid(4_232, "%00001088"), Attrs([Attr("address","0x400690")]), Phis([]), +Defs([]), Jmps([Call(Tid(4_235, "%0000108b"), + Attrs([Attr("address","0x400690"), Attr("insn","b #-0x80")]), Int(1,1), +(Direct(Tid(4_905, "@__gmon_start__")),))]))])), +Sub(Tid(4_917, "@deregister_tm_clones"), + Attrs([Attr("c.proto","signed (*)(void)"), Attr("address","0x4006A0")]), + "deregister_tm_clones", Args([Arg(Tid(4_974, "%0000136e"), + Attrs([Attr("c.layout","[signed : 32]"), Attr("c.data","Top:u32"), +Attr("c.type","signed")]), Var("deregister_tm_clones_result",Imm(32)), +LOW(32,Var("R0",Imm(64))), Out())]), +Blks([Blk(Tid(3_938, "@deregister_tm_clones"), + Attrs([Attr("address","0x4006A0")]), Phis([]), +Defs([Def(Tid(3_941, "%00000f65"), Attrs([Attr("address","0x4006A0"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_947, "%00000f6b"), Attrs([Attr("address","0x4006A4"), +Attr("insn","add x0, x0, #0x28")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(40,64))), Def(Tid(3_952, "%00000f70"), + Attrs([Attr("address","0x4006A8"), Attr("insn","adrp x1, #0x20000")]), + Var("R1",Imm(64)), Int(4325376,64)), Def(Tid(3_958, "%00000f76"), + Attrs([Attr("address","0x4006AC"), Attr("insn","add x1, x1, #0x28")]), + Var("R1",Imm(64)), PLUS(Var("R1",Imm(64)),Int(40,64))), +Def(Tid(3_964, "%00000f7c"), Attrs([Attr("address","0x4006B0"), +Attr("insn","cmp x1, x0")]), Var("#26",Imm(64)), NOT(Var("R0",Imm(64)))), +Def(Tid(3_969, "%00000f81"), Attrs([Attr("address","0x4006B0"), +Attr("insn","cmp x1, x0")]), Var("#27",Imm(64)), +PLUS(Var("R1",Imm(64)),NOT(Var("R0",Imm(64))))), Def(Tid(3_975, "%00000f87"), + Attrs([Attr("address","0x4006B0"), Attr("insn","cmp x1, x0")]), + Var("VF",Imm(1)), +NEQ(SIGNED(65,PLUS(Var("#27",Imm(64)),Int(1,64))),PLUS(PLUS(SIGNED(65,Var("R1",Imm(64))),SIGNED(65,Var("#26",Imm(64)))),Int(1,65)))), +Def(Tid(3_981, "%00000f8d"), Attrs([Attr("address","0x4006B0"), +Attr("insn","cmp x1, x0")]), Var("CF",Imm(1)), +NEQ(UNSIGNED(65,PLUS(Var("#27",Imm(64)),Int(1,64))),PLUS(PLUS(UNSIGNED(65,Var("R1",Imm(64))),UNSIGNED(65,Var("#26",Imm(64)))),Int(1,65)))), +Def(Tid(3_985, "%00000f91"), Attrs([Attr("address","0x4006B0"), +Attr("insn","cmp x1, x0")]), Var("ZF",Imm(1)), +EQ(PLUS(Var("#27",Imm(64)),Int(1,64)),Int(0,64))), +Def(Tid(3_989, "%00000f95"), Attrs([Attr("address","0x4006B0"), +Attr("insn","cmp x1, x0")]), Var("NF",Imm(1)), +Extract(63,63,PLUS(Var("#27",Imm(64)),Int(1,64))))]), +Jmps([Goto(Tid(3_995, "%00000f9b"), Attrs([Attr("address","0x4006B4"), +Attr("insn","b.eq #0x18")]), EQ(Var("ZF",Imm(1)),Int(1,1)), +Direct(Tid(3_993, "%00000f99"))), Goto(Tid(4_918, "%00001336"), Attrs([]), + Int(1,1), Direct(Tid(4_202, "%0000106a")))])), Blk(Tid(4_202, "%0000106a"), + Attrs([Attr("address","0x4006B8")]), Phis([]), +Defs([Def(Tid(4_205, "%0000106d"), Attrs([Attr("address","0x4006B8"), +Attr("insn","adrp x1, #0x1f000")]), Var("R1",Imm(64)), Int(4321280,64)), +Def(Tid(4_212, "%00001074"), Attrs([Attr("address","0x4006BC"), +Attr("insn","ldr x1, [x1, #0xfd0]")]), Var("R1",Imm(64)), +Load(Var("mem",Mem(64,8)),PLUS(Var("R1",Imm(64)),Int(4048,64)),LittleEndian(),64))]), +Jmps([Goto(Tid(4_217, "%00001079"), Attrs([Attr("address","0x4006C0"), +Attr("insn","cbz x1, #0xc")]), EQ(Var("R1",Imm(64)),Int(0,64)), +Direct(Tid(3_993, "%00000f99"))), Goto(Tid(4_919, "%00001337"), Attrs([]), + Int(1,1), Direct(Tid(4_221, "%0000107d")))])), Blk(Tid(3_993, "%00000f99"), + Attrs([Attr("address","0x4006CC")]), Phis([]), Defs([]), +Jmps([Call(Tid(4_001, "%00000fa1"), Attrs([Attr("address","0x4006CC"), +Attr("insn","ret")]), Int(1,1), (Indirect(Var("R30",Imm(64))),))])), +Blk(Tid(4_221, "%0000107d"), Attrs([Attr("address","0x4006C4")]), Phis([]), +Defs([Def(Tid(4_225, "%00001081"), Attrs([Attr("address","0x4006C4"), +Attr("insn","mov x16, x1")]), Var("R16",Imm(64)), Var("R1",Imm(64)))]), +Jmps([Call(Tid(4_230, "%00001086"), Attrs([Attr("address","0x4006C8"), +Attr("insn","br x16")]), Int(1,1), (Indirect(Var("R16",Imm(64))),))]))])), +Sub(Tid(4_920, "@frame_dummy"), Attrs([Attr("c.proto","signed (*)(void)"), +Attr("address","0x400740")]), "frame_dummy", + Args([Arg(Tid(4_975, "%0000136f"), Attrs([Attr("c.layout","[signed : 32]"), +Attr("c.data","Top:u32"), Attr("c.type","signed")]), + Var("frame_dummy_result",Imm(32)), LOW(32,Var("R0",Imm(64))), Out())]), +Blks([Blk(Tid(4_148, "@frame_dummy"), Attrs([Attr("address","0x400740")]), + Phis([]), Defs([]), Jmps([Call(Tid(4_150, "%00001036"), + Attrs([Attr("address","0x400740"), Attr("insn","b #-0x70")]), Int(1,1), +(Direct(Tid(4_956, "@register_tm_clones")),))]))])), Sub(Tid(4_921, "@main"), + Attrs([Attr("c.proto","signed (*)(signed argc, const char** argv)"), +Attr("address","0x400744")]), "main", Args([Arg(Tid(4_976, "%00001370"), + Attrs([Attr("c.layout","[signed : 32]"), Attr("c.data","Top:u32"), +Attr("c.type","signed")]), Var("main_argc",Imm(32)), +LOW(32,Var("R0",Imm(64))), In()), Arg(Tid(4_977, "%00001371"), + Attrs([Attr("c.layout","**[char : 8]"), Attr("c.data","Top:u8 ptr ptr"), +Attr("c.type"," const char**")]), Var("main_argv",Imm(64)), +Var("R1",Imm(64)), Both()), Arg(Tid(4_978, "%00001372"), + Attrs([Attr("c.layout","[signed : 32]"), Attr("c.data","Top:u32"), +Attr("c.type","signed")]), Var("main_result",Imm(32)), +LOW(32,Var("R0",Imm(64))), Out())]), Blks([Blk(Tid(1_638, "@main"), + Attrs([Attr("address","0x400744")]), Phis([]), +Defs([Def(Tid(1_645, "%0000066d"), Attrs([Attr("address","0x400744"), +Attr("insn","sub sp, sp, #0x10")]), Var("R31",Imm(64)), +PLUS(Var("R31",Imm(64)),Int(18446744073709551600,64))), +Def(Tid(1_653, "%00000675"), Attrs([Attr("address","0x400748"), +Attr("insn","str w0, [sp, #0xc]")]), Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),PLUS(Var("R31",Imm(64)),Int(12,64)),Extract(31,0,Var("R0",Imm(64))),LittleEndian(),32)), +Def(Tid(1_661, "%0000067d"), Attrs([Attr("address","0x40074C"), +Attr("insn","str x1, [sp]")]), Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R31",Imm(64)),Var("R1",Imm(64)),LittleEndian(),64)), +Def(Tid(1_666, "%00000682"), Attrs([Attr("address","0x400750"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(1_672, "%00000688"), Attrs([Attr("address","0x400754"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(1_679, "%0000068f"), + Attrs([Attr("address","0x400758"), Attr("insn","ldr w1, [sp, #0xc]")]), + Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),PLUS(Var("R31",Imm(64)),Int(12,64)),LittleEndian(),32))), +Def(Tid(1_687, "%00000697"), Attrs([Attr("address","0x40075C"), +Attr("insn","str w1, [x0]")]), Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32)), +Def(Tid(1_694, "%0000069e"), Attrs([Attr("address","0x400760"), +Attr("insn","ldr w1, [sp, #0xc]")]), Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),PLUS(Var("R31",Imm(64)),Int(12,64)),LittleEndian(),32))), +Def(Tid(1_699, "%000006a3"), Attrs([Attr("address","0x400764"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(1_705, "%000006a9"), Attrs([Attr("address","0x400768"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(1_713, "%000006b1"), + Attrs([Attr("address","0x40076C"), Attr("insn","str w1, [x0]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32)), +Def(Tid(1_720, "%000006b8"), Attrs([Attr("address","0x400770"), +Attr("insn","ldr w1, [sp, #0xc]")]), Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),PLUS(Var("R31",Imm(64)),Int(12,64)),LittleEndian(),32))), +Def(Tid(1_725, "%000006bd"), Attrs([Attr("address","0x400774"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(1_731, "%000006c3"), Attrs([Attr("address","0x400778"), +Attr("insn","add x0, x0, #0x34")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(52,64))), Def(Tid(1_739, "%000006cb"), + Attrs([Attr("address","0x40077C"), Attr("insn","str w1, [x0]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32)), +Def(Tid(1_744, "%000006d0"), Attrs([Attr("address","0x400780"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(1_750, "%000006d6"), Attrs([Attr("address","0x400784"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(1_757, "%000006dd"), + Attrs([Attr("address","0x400788"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(1_763, "%000006e3"), Attrs([Attr("address","0x40078C"), +Attr("insn","cmp w0, #0x0")]), Var("#1",Imm(32)), +PLUS(Extract(31,0,Var("R0",Imm(64))),Int(4294967295,32))), +Def(Tid(1_768, "%000006e8"), Attrs([Attr("address","0x40078C"), +Attr("insn","cmp w0, #0x0")]), Var("VF",Imm(1)), +NEQ(SIGNED(33,PLUS(Var("#1",Imm(32)),Int(1,32))),PLUS(SIGNED(33,Extract(31,0,Var("R0",Imm(64)))),Int(0,33)))), +Def(Tid(1_773, "%000006ed"), Attrs([Attr("address","0x40078C"), +Attr("insn","cmp w0, #0x0")]), Var("CF",Imm(1)), +NEQ(UNSIGNED(33,PLUS(Var("#1",Imm(32)),Int(1,32))),PLUS(UNSIGNED(33,Extract(31,0,Var("R0",Imm(64)))),Int(4294967296,33)))), +Def(Tid(1_777, "%000006f1"), Attrs([Attr("address","0x40078C"), +Attr("insn","cmp w0, #0x0")]), Var("ZF",Imm(1)), +EQ(PLUS(Var("#1",Imm(32)),Int(1,32)),Int(0,32))), +Def(Tid(1_781, "%000006f5"), Attrs([Attr("address","0x40078C"), +Attr("insn","cmp w0, #0x0")]), Var("NF",Imm(1)), +Extract(31,31,PLUS(Var("#1",Imm(32)),Int(1,32))))]), +Jmps([Goto(Tid(1_788, "%000006fc"), Attrs([Attr("address","0x400790"), +Attr("insn","b.ge #0x1c")]), EQ(Var("NF",Imm(1)),Var("VF",Imm(1))), +Direct(Tid(1_786, "%000006fa"))), Goto(Tid(4_922, "%0000133a"), Attrs([]), + Int(1,1), Direct(Tid(3_866, "%00000f1a")))])), Blk(Tid(3_866, "%00000f1a"), + Attrs([Attr("address","0x400794")]), Phis([]), +Defs([Def(Tid(3_869, "%00000f1d"), Attrs([Attr("address","0x400794"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_875, "%00000f23"), Attrs([Attr("address","0x400798"), +Attr("insn","add x0, x0, #0x30")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(48,64))), Def(Tid(3_882, "%00000f2a"), + Attrs([Attr("address","0x40079C"), Attr("insn","ldr w1, [x0]")]), + Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_887, "%00000f2f"), Attrs([Attr("address","0x4007A0"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_893, "%00000f35"), Attrs([Attr("address","0x4007A4"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(3_901, "%00000f3d"), + Attrs([Attr("address","0x4007A8"), Attr("insn","str w1, [x0]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32))]), +Jmps([Goto(Tid(4_923, "%0000133b"), Attrs([]), Int(1,1), +Direct(Tid(1_786, "%000006fa")))])), Blk(Tid(1_786, "%000006fa"), + Attrs([Attr("address","0x4007AC")]), Phis([]), +Defs([Def(Tid(1_794, "%00000702"), Attrs([Attr("address","0x4007AC"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(1_800, "%00000708"), Attrs([Attr("address","0x4007B0"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(1_807, "%0000070f"), + Attrs([Attr("address","0x4007B4"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(1_813, "%00000715"), Attrs([Attr("address","0x4007B8"), +Attr("insn","cmp w0, #0x0")]), Var("#2",Imm(32)), +PLUS(Extract(31,0,Var("R0",Imm(64))),Int(4294967295,32))), +Def(Tid(1_818, "%0000071a"), Attrs([Attr("address","0x4007B8"), +Attr("insn","cmp w0, #0x0")]), Var("VF",Imm(1)), +NEQ(SIGNED(33,PLUS(Var("#2",Imm(32)),Int(1,32))),PLUS(SIGNED(33,Extract(31,0,Var("R0",Imm(64)))),Int(0,33)))), +Def(Tid(1_823, "%0000071f"), Attrs([Attr("address","0x4007B8"), +Attr("insn","cmp w0, #0x0")]), Var("CF",Imm(1)), +NEQ(UNSIGNED(33,PLUS(Var("#2",Imm(32)),Int(1,32))),PLUS(UNSIGNED(33,Extract(31,0,Var("R0",Imm(64)))),Int(4294967296,33)))), +Def(Tid(1_827, "%00000723"), Attrs([Attr("address","0x4007B8"), +Attr("insn","cmp w0, #0x0")]), Var("ZF",Imm(1)), +EQ(PLUS(Var("#2",Imm(32)),Int(1,32)),Int(0,32))), +Def(Tid(1_831, "%00000727"), Attrs([Attr("address","0x4007B8"), +Attr("insn","cmp w0, #0x0")]), Var("NF",Imm(1)), +Extract(31,31,PLUS(Var("#2",Imm(32)),Int(1,32))))]), +Jmps([Goto(Tid(1_839, "%0000072f"), Attrs([Attr("address","0x4007BC"), +Attr("insn","b.le #0x1c")]), + NOT(AND(EQ(Var("NF",Imm(1)),Var("VF",Imm(1))),EQ(Var("ZF",Imm(1)),Int(0,1)))), +Direct(Tid(1_837, "%0000072d"))), Goto(Tid(4_924, "%0000133c"), Attrs([]), + Int(1,1), Direct(Tid(3_829, "%00000ef5")))])), Blk(Tid(3_829, "%00000ef5"), + Attrs([Attr("address","0x4007C0")]), Phis([]), +Defs([Def(Tid(3_832, "%00000ef8"), Attrs([Attr("address","0x4007C0"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_838, "%00000efe"), Attrs([Attr("address","0x4007C4"), +Attr("insn","add x0, x0, #0x30")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(48,64))), Def(Tid(3_845, "%00000f05"), + Attrs([Attr("address","0x4007C8"), Attr("insn","ldr w1, [x0]")]), + Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_850, "%00000f0a"), Attrs([Attr("address","0x4007CC"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_856, "%00000f10"), Attrs([Attr("address","0x4007D0"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(3_864, "%00000f18"), + Attrs([Attr("address","0x4007D4"), Attr("insn","str w1, [x0]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32))]), +Jmps([Goto(Tid(4_925, "%0000133d"), Attrs([]), Int(1,1), +Direct(Tid(1_837, "%0000072d")))])), Blk(Tid(1_837, "%0000072d"), + Attrs([Attr("address","0x4007D8")]), Phis([]), +Defs([Def(Tid(1_845, "%00000735"), Attrs([Attr("address","0x4007D8"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(1_851, "%0000073b"), Attrs([Attr("address","0x4007DC"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(1_858, "%00000742"), + Attrs([Attr("address","0x4007E0"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(1_864, "%00000748"), Attrs([Attr("address","0x4007E4"), +Attr("insn","cmp w0, #0x4")]), Var("#3",Imm(32)), +PLUS(Extract(31,0,Var("R0",Imm(64))),Int(4294967291,32))), +Def(Tid(1_869, "%0000074d"), Attrs([Attr("address","0x4007E4"), +Attr("insn","cmp w0, #0x4")]), Var("VF",Imm(1)), +NEQ(SIGNED(33,PLUS(Var("#3",Imm(32)),Int(1,32))),PLUS(SIGNED(33,Extract(31,0,Var("R0",Imm(64)))),Int(8589934588,33)))), +Def(Tid(1_874, "%00000752"), Attrs([Attr("address","0x4007E4"), +Attr("insn","cmp w0, #0x4")]), Var("CF",Imm(1)), +NEQ(UNSIGNED(33,PLUS(Var("#3",Imm(32)),Int(1,32))),PLUS(UNSIGNED(33,Extract(31,0,Var("R0",Imm(64)))),Int(4294967292,33)))), +Def(Tid(1_878, "%00000756"), Attrs([Attr("address","0x4007E4"), +Attr("insn","cmp w0, #0x4")]), Var("ZF",Imm(1)), +EQ(PLUS(Var("#3",Imm(32)),Int(1,32)),Int(0,32))), +Def(Tid(1_882, "%0000075a"), Attrs([Attr("address","0x4007E4"), +Attr("insn","cmp w0, #0x4")]), Var("NF",Imm(1)), +Extract(31,31,PLUS(Var("#3",Imm(32)),Int(1,32))))]), +Jmps([Goto(Tid(1_890, "%00000762"), Attrs([Attr("address","0x4007E8"), +Attr("insn","b.gt #0x2c")]), + AND(EQ(Var("NF",Imm(1)),Var("VF",Imm(1))),EQ(Var("ZF",Imm(1)),Int(0,1))), +Direct(Tid(1_888, "%00000760"))), Goto(Tid(4_926, "%0000133e"), Attrs([]), + Int(1,1), Direct(Tid(3_767, "%00000eb7")))])), Blk(Tid(3_767, "%00000eb7"), + Attrs([Attr("address","0x4007EC")]), Phis([]), +Defs([Def(Tid(3_770, "%00000eba"), Attrs([Attr("address","0x4007EC"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_776, "%00000ec0"), Attrs([Attr("address","0x4007F0"), +Attr("insn","add x0, x0, #0x30")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(48,64))), Def(Tid(3_783, "%00000ec7"), + Attrs([Attr("address","0x4007F4"), Attr("insn","ldr w1, [x0]")]), + Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_788, "%00000ecc"), Attrs([Attr("address","0x4007F8"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_794, "%00000ed2"), Attrs([Attr("address","0x4007FC"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(3_801, "%00000ed9"), + Attrs([Attr("address","0x400800"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_808, "%00000ee0"), Attrs([Attr("address","0x400804"), +Attr("insn","add w1, w1, w0")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R1",Imm(64))),Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(3_813, "%00000ee5"), Attrs([Attr("address","0x400808"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_819, "%00000eeb"), Attrs([Attr("address","0x40080C"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(3_827, "%00000ef3"), + Attrs([Attr("address","0x400810"), Attr("insn","str w1, [x0]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32))]), +Jmps([Goto(Tid(4_927, "%0000133f"), Attrs([]), Int(1,1), +Direct(Tid(1_888, "%00000760")))])), Blk(Tid(1_888, "%00000760"), + Attrs([Attr("address","0x400814")]), Phis([]), +Defs([Def(Tid(1_896, "%00000768"), Attrs([Attr("address","0x400814"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(1_902, "%0000076e"), Attrs([Attr("address","0x400818"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(1_909, "%00000775"), + Attrs([Attr("address","0x40081C"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(1_915, "%0000077b"), Attrs([Attr("address","0x400820"), +Attr("insn","cmp w0, #0x8")]), Var("#4",Imm(32)), +PLUS(Extract(31,0,Var("R0",Imm(64))),Int(4294967287,32))), +Def(Tid(1_920, "%00000780"), Attrs([Attr("address","0x400820"), +Attr("insn","cmp w0, #0x8")]), Var("VF",Imm(1)), +NEQ(SIGNED(33,PLUS(Var("#4",Imm(32)),Int(1,32))),PLUS(SIGNED(33,Extract(31,0,Var("R0",Imm(64)))),Int(8589934584,33)))), +Def(Tid(1_925, "%00000785"), Attrs([Attr("address","0x400820"), +Attr("insn","cmp w0, #0x8")]), Var("CF",Imm(1)), +NEQ(UNSIGNED(33,PLUS(Var("#4",Imm(32)),Int(1,32))),PLUS(UNSIGNED(33,Extract(31,0,Var("R0",Imm(64)))),Int(4294967288,33)))), +Def(Tid(1_929, "%00000789"), Attrs([Attr("address","0x400820"), +Attr("insn","cmp w0, #0x8")]), Var("ZF",Imm(1)), +EQ(PLUS(Var("#4",Imm(32)),Int(1,32)),Int(0,32))), +Def(Tid(1_933, "%0000078d"), Attrs([Attr("address","0x400820"), +Attr("insn","cmp w0, #0x8")]), Var("NF",Imm(1)), +Extract(31,31,PLUS(Var("#4",Imm(32)),Int(1,32))))]), +Jmps([Goto(Tid(1_941, "%00000795"), Attrs([Attr("address","0x400824"), +Attr("insn","b.gt #0x2c")]), + AND(EQ(Var("NF",Imm(1)),Var("VF",Imm(1))),EQ(Var("ZF",Imm(1)),Int(0,1))), +Direct(Tid(1_939, "%00000793"))), Goto(Tid(4_928, "%00001340"), Attrs([]), + Int(1,1), Direct(Tid(3_705, "%00000e79")))])), Blk(Tid(3_705, "%00000e79"), + Attrs([Attr("address","0x400828")]), Phis([]), +Defs([Def(Tid(3_708, "%00000e7c"), Attrs([Attr("address","0x400828"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_714, "%00000e82"), Attrs([Attr("address","0x40082C"), +Attr("insn","add x0, x0, #0x30")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(48,64))), Def(Tid(3_721, "%00000e89"), + Attrs([Attr("address","0x400830"), Attr("insn","ldr w1, [x0]")]), + Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_726, "%00000e8e"), Attrs([Attr("address","0x400834"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_732, "%00000e94"), Attrs([Attr("address","0x400838"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(3_739, "%00000e9b"), + Attrs([Attr("address","0x40083C"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_746, "%00000ea2"), Attrs([Attr("address","0x400840"), +Attr("insn","add w1, w1, w0")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R1",Imm(64))),Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(3_751, "%00000ea7"), Attrs([Attr("address","0x400844"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_757, "%00000ead"), Attrs([Attr("address","0x400848"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(3_765, "%00000eb5"), + Attrs([Attr("address","0x40084C"), Attr("insn","str w1, [x0]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32))]), +Jmps([Goto(Tid(4_929, "%00001341"), Attrs([]), Int(1,1), +Direct(Tid(1_939, "%00000793")))])), Blk(Tid(1_939, "%00000793"), + Attrs([Attr("address","0x400850")]), Phis([]), +Defs([Def(Tid(1_947, "%0000079b"), Attrs([Attr("address","0x400850"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(1_953, "%000007a1"), Attrs([Attr("address","0x400854"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(1_960, "%000007a8"), + Attrs([Attr("address","0x400858"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(1_966, "%000007ae"), Attrs([Attr("address","0x40085C"), +Attr("insn","cmp w0, #0x63")]), Var("#5",Imm(32)), +PLUS(Extract(31,0,Var("R0",Imm(64))),Int(4294967196,32))), +Def(Tid(1_971, "%000007b3"), Attrs([Attr("address","0x40085C"), +Attr("insn","cmp w0, #0x63")]), Var("VF",Imm(1)), +NEQ(SIGNED(33,PLUS(Var("#5",Imm(32)),Int(1,32))),PLUS(SIGNED(33,Extract(31,0,Var("R0",Imm(64)))),Int(8589934493,33)))), +Def(Tid(1_976, "%000007b8"), Attrs([Attr("address","0x40085C"), +Attr("insn","cmp w0, #0x63")]), Var("CF",Imm(1)), +NEQ(UNSIGNED(33,PLUS(Var("#5",Imm(32)),Int(1,32))),PLUS(UNSIGNED(33,Extract(31,0,Var("R0",Imm(64)))),Int(4294967197,33)))), +Def(Tid(1_980, "%000007bc"), Attrs([Attr("address","0x40085C"), +Attr("insn","cmp w0, #0x63")]), Var("ZF",Imm(1)), +EQ(PLUS(Var("#5",Imm(32)),Int(1,32)),Int(0,32))), +Def(Tid(1_984, "%000007c0"), Attrs([Attr("address","0x40085C"), +Attr("insn","cmp w0, #0x63")]), Var("NF",Imm(1)), +Extract(31,31,PLUS(Var("#5",Imm(32)),Int(1,32))))]), +Jmps([Goto(Tid(1_992, "%000007c8"), Attrs([Attr("address","0x400860"), +Attr("insn","b.le #0x2c")]), + NOT(AND(EQ(Var("NF",Imm(1)),Var("VF",Imm(1))),EQ(Var("ZF",Imm(1)),Int(0,1)))), +Direct(Tid(1_990, "%000007c6"))), Goto(Tid(4_930, "%00001342"), Attrs([]), + Int(1,1), Direct(Tid(3_643, "%00000e3b")))])), Blk(Tid(3_643, "%00000e3b"), + Attrs([Attr("address","0x400864")]), Phis([]), +Defs([Def(Tid(3_646, "%00000e3e"), Attrs([Attr("address","0x400864"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_652, "%00000e44"), Attrs([Attr("address","0x400868"), +Attr("insn","add x0, x0, #0x30")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(48,64))), Def(Tid(3_659, "%00000e4b"), + Attrs([Attr("address","0x40086C"), Attr("insn","ldr w1, [x0]")]), + Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_664, "%00000e50"), Attrs([Attr("address","0x400870"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_670, "%00000e56"), Attrs([Attr("address","0x400874"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(3_677, "%00000e5d"), + Attrs([Attr("address","0x400878"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_684, "%00000e64"), Attrs([Attr("address","0x40087C"), +Attr("insn","add w1, w1, w0")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R1",Imm(64))),Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(3_689, "%00000e69"), Attrs([Attr("address","0x400880"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_695, "%00000e6f"), Attrs([Attr("address","0x400884"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(3_703, "%00000e77"), + Attrs([Attr("address","0x400888"), Attr("insn","str w1, [x0]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32))]), +Jmps([Goto(Tid(4_931, "%00001343"), Attrs([]), Int(1,1), +Direct(Tid(1_990, "%000007c6")))])), Blk(Tid(1_990, "%000007c6"), + Attrs([Attr("address","0x40088C")]), Phis([]), +Defs([Def(Tid(1_998, "%000007ce"), Attrs([Attr("address","0x40088C"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_004, "%000007d4"), Attrs([Attr("address","0x400890"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(2_011, "%000007db"), + Attrs([Attr("address","0x400894"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_017, "%000007e1"), Attrs([Attr("address","0x400898"), +Attr("insn","cmp w0, #0x3e8")]), Var("#6",Imm(32)), +PLUS(Extract(31,0,Var("R0",Imm(64))),Int(4294966295,32))), +Def(Tid(2_022, "%000007e6"), Attrs([Attr("address","0x400898"), +Attr("insn","cmp w0, #0x3e8")]), Var("VF",Imm(1)), +NEQ(SIGNED(33,PLUS(Var("#6",Imm(32)),Int(1,32))),PLUS(SIGNED(33,Extract(31,0,Var("R0",Imm(64)))),Int(8589933592,33)))), +Def(Tid(2_027, "%000007eb"), Attrs([Attr("address","0x400898"), +Attr("insn","cmp w0, #0x3e8")]), Var("CF",Imm(1)), +NEQ(UNSIGNED(33,PLUS(Var("#6",Imm(32)),Int(1,32))),PLUS(UNSIGNED(33,Extract(31,0,Var("R0",Imm(64)))),Int(4294966296,33)))), +Def(Tid(2_031, "%000007ef"), Attrs([Attr("address","0x400898"), +Attr("insn","cmp w0, #0x3e8")]), Var("ZF",Imm(1)), +EQ(PLUS(Var("#6",Imm(32)),Int(1,32)),Int(0,32))), +Def(Tid(2_035, "%000007f3"), Attrs([Attr("address","0x400898"), +Attr("insn","cmp w0, #0x3e8")]), Var("NF",Imm(1)), +Extract(31,31,PLUS(Var("#6",Imm(32)),Int(1,32))))]), +Jmps([Goto(Tid(2_043, "%000007fb"), Attrs([Attr("address","0x40089C"), +Attr("insn","b.le #0x2c")]), + NOT(AND(EQ(Var("NF",Imm(1)),Var("VF",Imm(1))),EQ(Var("ZF",Imm(1)),Int(0,1)))), +Direct(Tid(2_041, "%000007f9"))), Goto(Tid(4_932, "%00001344"), Attrs([]), + Int(1,1), Direct(Tid(3_581, "%00000dfd")))])), Blk(Tid(3_581, "%00000dfd"), + Attrs([Attr("address","0x4008A0")]), Phis([]), +Defs([Def(Tid(3_584, "%00000e00"), Attrs([Attr("address","0x4008A0"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_590, "%00000e06"), Attrs([Attr("address","0x4008A4"), +Attr("insn","add x0, x0, #0x30")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(48,64))), Def(Tid(3_597, "%00000e0d"), + Attrs([Attr("address","0x4008A8"), Attr("insn","ldr w1, [x0]")]), + Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_602, "%00000e12"), Attrs([Attr("address","0x4008AC"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_608, "%00000e18"), Attrs([Attr("address","0x4008B0"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(3_615, "%00000e1f"), + Attrs([Attr("address","0x4008B4"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_622, "%00000e26"), Attrs([Attr("address","0x4008B8"), +Attr("insn","add w1, w1, w0")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R1",Imm(64))),Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(3_627, "%00000e2b"), Attrs([Attr("address","0x4008BC"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_633, "%00000e31"), Attrs([Attr("address","0x4008C0"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(3_641, "%00000e39"), + Attrs([Attr("address","0x4008C4"), Attr("insn","str w1, [x0]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32))]), +Jmps([Goto(Tid(4_933, "%00001345"), Attrs([]), Int(1,1), +Direct(Tid(2_041, "%000007f9")))])), Blk(Tid(2_041, "%000007f9"), + Attrs([Attr("address","0x4008C8")]), Phis([]), +Defs([Def(Tid(2_049, "%00000801"), Attrs([Attr("address","0x4008C8"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_055, "%00000807"), Attrs([Attr("address","0x4008CC"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(2_062, "%0000080e"), + Attrs([Attr("address","0x4008D0"), Attr("insn","ldr w1, [x0]")]), + Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_067, "%00000813"), Attrs([Attr("address","0x4008D4"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_073, "%00000819"), Attrs([Attr("address","0x4008D8"), +Attr("insn","add x0, x0, #0x30")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(48,64))), Def(Tid(2_080, "%00000820"), + Attrs([Attr("address","0x4008DC"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_086, "%00000826"), Attrs([Attr("address","0x4008E0"), +Attr("insn","cmp w1, w0")]), Var("#7",Imm(32)), +NOT(Extract(31,0,Var("R0",Imm(64))))), Def(Tid(2_091, "%0000082b"), + Attrs([Attr("address","0x4008E0"), Attr("insn","cmp w1, w0")]), + Var("#8",Imm(32)), +PLUS(Extract(31,0,Var("R1",Imm(64))),NOT(Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(2_097, "%00000831"), Attrs([Attr("address","0x4008E0"), +Attr("insn","cmp w1, w0")]), Var("VF",Imm(1)), +NEQ(SIGNED(33,PLUS(Var("#8",Imm(32)),Int(1,32))),PLUS(PLUS(SIGNED(33,Extract(31,0,Var("R1",Imm(64)))),SIGNED(33,Var("#7",Imm(32)))),Int(1,33)))), +Def(Tid(2_103, "%00000837"), Attrs([Attr("address","0x4008E0"), +Attr("insn","cmp w1, w0")]), Var("CF",Imm(1)), +NEQ(UNSIGNED(33,PLUS(Var("#8",Imm(32)),Int(1,32))),PLUS(PLUS(UNSIGNED(33,Extract(31,0,Var("R1",Imm(64)))),UNSIGNED(33,Var("#7",Imm(32)))),Int(1,33)))), +Def(Tid(2_107, "%0000083b"), Attrs([Attr("address","0x4008E0"), +Attr("insn","cmp w1, w0")]), Var("ZF",Imm(1)), +EQ(PLUS(Var("#8",Imm(32)),Int(1,32)),Int(0,32))), +Def(Tid(2_111, "%0000083f"), Attrs([Attr("address","0x4008E0"), +Attr("insn","cmp w1, w0")]), Var("NF",Imm(1)), +Extract(31,31,PLUS(Var("#8",Imm(32)),Int(1,32))))]), +Jmps([Goto(Tid(2_118, "%00000846"), Attrs([Attr("address","0x4008E4"), +Attr("insn","b.ge #0x40")]), EQ(Var("NF",Imm(1)),Var("VF",Imm(1))), +Direct(Tid(2_116, "%00000844"))), Goto(Tid(4_934, "%00001346"), Attrs([]), + Int(1,1), Direct(Tid(3_488, "%00000da0")))])), Blk(Tid(3_488, "%00000da0"), + Attrs([Attr("address","0x4008E8")]), Phis([]), +Defs([Def(Tid(3_491, "%00000da3"), Attrs([Attr("address","0x4008E8"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_497, "%00000da9"), Attrs([Attr("address","0x4008EC"), +Attr("insn","add x0, x0, #0x30")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(48,64))), Def(Tid(3_504, "%00000db0"), + Attrs([Attr("address","0x4008F0"), Attr("insn","ldr w1, [x0]")]), + Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_510, "%00000db6"), Attrs([Attr("address","0x4008F4"), +Attr("insn","mov w0, w1")]), Var("R0",Imm(64)), +UNSIGNED(64,Extract(31,0,Var("R1",Imm(64))))), Def(Tid(3_516, "%00000dbc"), + Attrs([Attr("address","0x4008F8"), Attr("insn","lsl w0, w0, #2")]), + Var("R0",Imm(64)), +UNSIGNED(64,Concat(Extract(29,0,Var("R0",Imm(64))),Int(0,2)))), +Def(Tid(3_523, "%00000dc3"), Attrs([Attr("address","0x4008FC"), +Attr("insn","add w0, w0, w1")]), Var("R0",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R0",Imm(64))),Extract(31,0,Var("R1",Imm(64)))))), +Def(Tid(3_529, "%00000dc9"), Attrs([Attr("address","0x400900"), +Attr("insn","lsl w0, w0, #1")]), Var("R0",Imm(64)), +UNSIGNED(64,Concat(Extract(30,0,Var("R0",Imm(64))),Int(0,1)))), +Def(Tid(3_535, "%00000dcf"), Attrs([Attr("address","0x400904"), +Attr("insn","mov w1, w0")]), Var("R1",Imm(64)), +UNSIGNED(64,Extract(31,0,Var("R0",Imm(64))))), Def(Tid(3_540, "%00000dd4"), + Attrs([Attr("address","0x400908"), Attr("insn","adrp x0, #0x20000")]), + Var("R0",Imm(64)), Int(4325376,64)), Def(Tid(3_546, "%00000dda"), + Attrs([Attr("address","0x40090C"), Attr("insn","add x0, x0, #0x2c")]), + Var("R0",Imm(64)), PLUS(Var("R0",Imm(64)),Int(44,64))), +Def(Tid(3_553, "%00000de1"), Attrs([Attr("address","0x400910"), +Attr("insn","ldr w0, [x0]")]), Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_560, "%00000de8"), Attrs([Attr("address","0x400914"), +Attr("insn","add w1, w1, w0")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R1",Imm(64))),Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(3_565, "%00000ded"), Attrs([Attr("address","0x400918"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_571, "%00000df3"), Attrs([Attr("address","0x40091C"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(3_579, "%00000dfb"), + Attrs([Attr("address","0x400920"), Attr("insn","str w1, [x0]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32))]), +Jmps([Goto(Tid(4_935, "%00001347"), Attrs([]), Int(1,1), +Direct(Tid(2_116, "%00000844")))])), Blk(Tid(2_116, "%00000844"), + Attrs([Attr("address","0x400924")]), Phis([]), +Defs([Def(Tid(2_124, "%0000084c"), Attrs([Attr("address","0x400924"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_130, "%00000852"), Attrs([Attr("address","0x400928"), +Attr("insn","add x0, x0, #0x30")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(48,64))), Def(Tid(2_137, "%00000859"), + Attrs([Attr("address","0x40092C"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_143, "%0000085f"), Attrs([Attr("address","0x400930"), +Attr("insn","add w1, w0, #0x64")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R0",Imm(64))),Int(100,32)))), +Def(Tid(2_148, "%00000864"), Attrs([Attr("address","0x400934"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_154, "%0000086a"), Attrs([Attr("address","0x400938"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(2_161, "%00000871"), + Attrs([Attr("address","0x40093C"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_167, "%00000877"), Attrs([Attr("address","0x400940"), +Attr("insn","cmp w1, w0")]), Var("#9",Imm(32)), +NOT(Extract(31,0,Var("R0",Imm(64))))), Def(Tid(2_172, "%0000087c"), + Attrs([Attr("address","0x400940"), Attr("insn","cmp w1, w0")]), + Var("#10",Imm(32)), +PLUS(Extract(31,0,Var("R1",Imm(64))),NOT(Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(2_178, "%00000882"), Attrs([Attr("address","0x400940"), +Attr("insn","cmp w1, w0")]), Var("VF",Imm(1)), +NEQ(SIGNED(33,PLUS(Var("#10",Imm(32)),Int(1,32))),PLUS(PLUS(SIGNED(33,Extract(31,0,Var("R1",Imm(64)))),SIGNED(33,Var("#9",Imm(32)))),Int(1,33)))), +Def(Tid(2_184, "%00000888"), Attrs([Attr("address","0x400940"), +Attr("insn","cmp w1, w0")]), Var("CF",Imm(1)), +NEQ(UNSIGNED(33,PLUS(Var("#10",Imm(32)),Int(1,32))),PLUS(PLUS(UNSIGNED(33,Extract(31,0,Var("R1",Imm(64)))),UNSIGNED(33,Var("#9",Imm(32)))),Int(1,33)))), +Def(Tid(2_188, "%0000088c"), Attrs([Attr("address","0x400940"), +Attr("insn","cmp w1, w0")]), Var("ZF",Imm(1)), +EQ(PLUS(Var("#10",Imm(32)),Int(1,32)),Int(0,32))), +Def(Tid(2_192, "%00000890"), Attrs([Attr("address","0x400940"), +Attr("insn","cmp w1, w0")]), Var("NF",Imm(1)), +Extract(31,31,PLUS(Var("#10",Imm(32)),Int(1,32))))]), +Jmps([Goto(Tid(2_199, "%00000897"), Attrs([Attr("address","0x400944"), +Attr("insn","b.lt #0x3c")]), NEQ(Var("NF",Imm(1)),Var("VF",Imm(1))), +Direct(Tid(2_197, "%00000895"))), Goto(Tid(4_936, "%00001348"), Attrs([]), + Int(1,1), Direct(Tid(3_401, "%00000d49")))])), Blk(Tid(3_401, "%00000d49"), + Attrs([Attr("address","0x400948")]), Phis([]), +Defs([Def(Tid(3_404, "%00000d4c"), Attrs([Attr("address","0x400948"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_410, "%00000d52"), Attrs([Attr("address","0x40094C"), +Attr("insn","add x0, x0, #0x30")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(48,64))), Def(Tid(3_417, "%00000d59"), + Attrs([Attr("address","0x400950"), Attr("insn","ldr w1, [x0]")]), + Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_422, "%00000d5e"), Attrs([Attr("address","0x400954"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_428, "%00000d64"), Attrs([Attr("address","0x400958"), +Attr("insn","add x0, x0, #0x30")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(48,64))), Def(Tid(3_435, "%00000d6b"), + Attrs([Attr("address","0x40095C"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_442, "%00000d72"), Attrs([Attr("address","0x400960"), +Attr("insn","mul w1, w1, w0")]), Var("R1",Imm(64)), +UNSIGNED(64,Extract(31,0,TIMES(UNSIGNED(64,Extract(31,0,Var("R1",Imm(64)))),UNSIGNED(64,Extract(31,0,Var("R0",Imm(64)))))))), +Def(Tid(3_447, "%00000d77"), Attrs([Attr("address","0x400964"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_453, "%00000d7d"), Attrs([Attr("address","0x400968"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(3_460, "%00000d84"), + Attrs([Attr("address","0x40096C"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_467, "%00000d8b"), Attrs([Attr("address","0x400970"), +Attr("insn","add w1, w1, w0")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R1",Imm(64))),Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(3_472, "%00000d90"), Attrs([Attr("address","0x400974"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_478, "%00000d96"), Attrs([Attr("address","0x400978"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(3_486, "%00000d9e"), + Attrs([Attr("address","0x40097C"), Attr("insn","str w1, [x0]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32))]), +Jmps([Goto(Tid(4_937, "%00001349"), Attrs([]), Int(1,1), +Direct(Tid(2_197, "%00000895")))])), Blk(Tid(2_197, "%00000895"), + Attrs([Attr("address","0x400980")]), Phis([]), +Defs([Def(Tid(2_205, "%0000089d"), Attrs([Attr("address","0x400980"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_211, "%000008a3"), Attrs([Attr("address","0x400984"), +Attr("insn","add x0, x0, #0x30")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(48,64))), Def(Tid(2_218, "%000008aa"), + Attrs([Attr("address","0x400988"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_224, "%000008b0"), Attrs([Attr("address","0x40098C"), +Attr("insn","add w1, w0, #0x3e8")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R0",Imm(64))),Int(1000,32)))), +Def(Tid(2_229, "%000008b5"), Attrs([Attr("address","0x400990"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_235, "%000008bb"), Attrs([Attr("address","0x400994"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(2_242, "%000008c2"), + Attrs([Attr("address","0x400998"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_248, "%000008c8"), Attrs([Attr("address","0x40099C"), +Attr("insn","cmp w1, w0")]), Var("#11",Imm(32)), +NOT(Extract(31,0,Var("R0",Imm(64))))), Def(Tid(2_253, "%000008cd"), + Attrs([Attr("address","0x40099C"), Attr("insn","cmp w1, w0")]), + Var("#12",Imm(32)), +PLUS(Extract(31,0,Var("R1",Imm(64))),NOT(Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(2_259, "%000008d3"), Attrs([Attr("address","0x40099C"), +Attr("insn","cmp w1, w0")]), Var("VF",Imm(1)), +NEQ(SIGNED(33,PLUS(Var("#12",Imm(32)),Int(1,32))),PLUS(PLUS(SIGNED(33,Extract(31,0,Var("R1",Imm(64)))),SIGNED(33,Var("#11",Imm(32)))),Int(1,33)))), +Def(Tid(2_265, "%000008d9"), Attrs([Attr("address","0x40099C"), +Attr("insn","cmp w1, w0")]), Var("CF",Imm(1)), +NEQ(UNSIGNED(33,PLUS(Var("#12",Imm(32)),Int(1,32))),PLUS(PLUS(UNSIGNED(33,Extract(31,0,Var("R1",Imm(64)))),UNSIGNED(33,Var("#11",Imm(32)))),Int(1,33)))), +Def(Tid(2_269, "%000008dd"), Attrs([Attr("address","0x40099C"), +Attr("insn","cmp w1, w0")]), Var("ZF",Imm(1)), +EQ(PLUS(Var("#12",Imm(32)),Int(1,32)),Int(0,32))), +Def(Tid(2_273, "%000008e1"), Attrs([Attr("address","0x40099C"), +Attr("insn","cmp w1, w0")]), Var("NF",Imm(1)), +Extract(31,31,PLUS(Var("#12",Imm(32)),Int(1,32))))]), +Jmps([Goto(Tid(2_280, "%000008e8"), Attrs([Attr("address","0x4009A0"), +Attr("insn","b.ge #0x40")]), EQ(Var("NF",Imm(1)),Var("VF",Imm(1))), +Direct(Tid(2_278, "%000008e6"))), Goto(Tid(4_938, "%0000134a"), Attrs([]), + Int(1,1), Direct(Tid(3_308, "%00000cec")))])), Blk(Tid(3_308, "%00000cec"), + Attrs([Attr("address","0x4009A4")]), Phis([]), +Defs([Def(Tid(3_311, "%00000cef"), Attrs([Attr("address","0x4009A4"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_317, "%00000cf5"), Attrs([Attr("address","0x4009A8"), +Attr("insn","add x0, x0, #0x30")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(48,64))), Def(Tid(3_324, "%00000cfc"), + Attrs([Attr("address","0x4009AC"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_330, "%00000d02"), Attrs([Attr("address","0x4009B0"), +Attr("insn","lsl w1, w0, #1")]), Var("R1",Imm(64)), +UNSIGNED(64,Concat(Extract(30,0,Var("R0",Imm(64))),Int(0,1)))), +Def(Tid(3_335, "%00000d07"), Attrs([Attr("address","0x4009B4"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_341, "%00000d0d"), Attrs([Attr("address","0x4009B8"), +Attr("insn","add x0, x0, #0x30")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(48,64))), Def(Tid(3_348, "%00000d14"), + Attrs([Attr("address","0x4009BC"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_355, "%00000d1b"), Attrs([Attr("address","0x4009C0"), +Attr("insn","add w1, w1, w0")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R1",Imm(64))),Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(3_360, "%00000d20"), Attrs([Attr("address","0x4009C4"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_366, "%00000d26"), Attrs([Attr("address","0x4009C8"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(3_373, "%00000d2d"), + Attrs([Attr("address","0x4009CC"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_380, "%00000d34"), Attrs([Attr("address","0x4009D0"), +Attr("insn","add w1, w1, w0")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R1",Imm(64))),Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(3_385, "%00000d39"), Attrs([Attr("address","0x4009D4"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_391, "%00000d3f"), Attrs([Attr("address","0x4009D8"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(3_399, "%00000d47"), + Attrs([Attr("address","0x4009DC"), Attr("insn","str w1, [x0]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32))]), +Jmps([Goto(Tid(4_939, "%0000134b"), Attrs([]), Int(1,1), +Direct(Tid(2_278, "%000008e6")))])), Blk(Tid(2_278, "%000008e6"), + Attrs([Attr("address","0x4009E0")]), Phis([]), +Defs([Def(Tid(2_286, "%000008ee"), Attrs([Attr("address","0x4009E0"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_292, "%000008f4"), Attrs([Attr("address","0x4009E4"), +Attr("insn","add x0, x0, #0x30")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(48,64))), Def(Tid(2_299, "%000008fb"), + Attrs([Attr("address","0x4009E8"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_305, "%00000901"), Attrs([Attr("address","0x4009EC"), +Attr("insn","add w1, w0, #0x7cf")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R0",Imm(64))),Int(1999,32)))), +Def(Tid(2_310, "%00000906"), Attrs([Attr("address","0x4009F0"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_316, "%0000090c"), Attrs([Attr("address","0x4009F4"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(2_323, "%00000913"), + Attrs([Attr("address","0x4009F8"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_329, "%00000919"), Attrs([Attr("address","0x4009FC"), +Attr("insn","cmp w1, w0")]), Var("#13",Imm(32)), +NOT(Extract(31,0,Var("R0",Imm(64))))), Def(Tid(2_334, "%0000091e"), + Attrs([Attr("address","0x4009FC"), Attr("insn","cmp w1, w0")]), + Var("#14",Imm(32)), +PLUS(Extract(31,0,Var("R1",Imm(64))),NOT(Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(2_340, "%00000924"), Attrs([Attr("address","0x4009FC"), +Attr("insn","cmp w1, w0")]), Var("VF",Imm(1)), +NEQ(SIGNED(33,PLUS(Var("#14",Imm(32)),Int(1,32))),PLUS(PLUS(SIGNED(33,Extract(31,0,Var("R1",Imm(64)))),SIGNED(33,Var("#13",Imm(32)))),Int(1,33)))), +Def(Tid(2_346, "%0000092a"), Attrs([Attr("address","0x4009FC"), +Attr("insn","cmp w1, w0")]), Var("CF",Imm(1)), +NEQ(UNSIGNED(33,PLUS(Var("#14",Imm(32)),Int(1,32))),PLUS(PLUS(UNSIGNED(33,Extract(31,0,Var("R1",Imm(64)))),UNSIGNED(33,Var("#13",Imm(32)))),Int(1,33)))), +Def(Tid(2_350, "%0000092e"), Attrs([Attr("address","0x4009FC"), +Attr("insn","cmp w1, w0")]), Var("ZF",Imm(1)), +EQ(PLUS(Var("#14",Imm(32)),Int(1,32)),Int(0,32))), +Def(Tid(2_354, "%00000932"), Attrs([Attr("address","0x4009FC"), +Attr("insn","cmp w1, w0")]), Var("NF",Imm(1)), +Extract(31,31,PLUS(Var("#14",Imm(32)),Int(1,32))))]), +Jmps([Goto(Tid(2_361, "%00000939"), Attrs([Attr("address","0x400A00"), +Attr("insn","b.ge #0x3c")]), EQ(Var("NF",Imm(1)),Var("VF",Imm(1))), +Direct(Tid(2_359, "%00000937"))), Goto(Tid(4_940, "%0000134c"), Attrs([]), + Int(1,1), Direct(Tid(3_221, "%00000c95")))])), Blk(Tid(3_221, "%00000c95"), + Attrs([Attr("address","0x400A04")]), Phis([]), +Defs([Def(Tid(3_224, "%00000c98"), Attrs([Attr("address","0x400A04"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_230, "%00000c9e"), Attrs([Attr("address","0x400A08"), +Attr("insn","add x0, x0, #0x30")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(48,64))), Def(Tid(3_237, "%00000ca5"), + Attrs([Attr("address","0x400A0C"), Attr("insn","ldr w1, [x0]")]), + Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_242, "%00000caa"), Attrs([Attr("address","0x400A10"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_248, "%00000cb0"), Attrs([Attr("address","0x400A14"), +Attr("insn","add x0, x0, #0x30")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(48,64))), Def(Tid(3_255, "%00000cb7"), + Attrs([Attr("address","0x400A18"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_262, "%00000cbe"), Attrs([Attr("address","0x400A1C"), +Attr("insn","add w1, w1, w0")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R1",Imm(64))),Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(3_267, "%00000cc3"), Attrs([Attr("address","0x400A20"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_273, "%00000cc9"), Attrs([Attr("address","0x400A24"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(3_280, "%00000cd0"), + Attrs([Attr("address","0x400A28"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_287, "%00000cd7"), Attrs([Attr("address","0x400A2C"), +Attr("insn","add w1, w1, w0")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R1",Imm(64))),Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(3_292, "%00000cdc"), Attrs([Attr("address","0x400A30"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_298, "%00000ce2"), Attrs([Attr("address","0x400A34"), +Attr("insn","add x0, x0, #0x2c")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(44,64))), Def(Tid(3_306, "%00000cea"), + Attrs([Attr("address","0x400A38"), Attr("insn","str w1, [x0]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32))]), +Jmps([Goto(Tid(4_941, "%0000134d"), Attrs([]), Int(1,1), +Direct(Tid(2_359, "%00000937")))])), Blk(Tid(2_359, "%00000937"), + Attrs([Attr("address","0x400A3C")]), Phis([]), +Defs([Def(Tid(2_367, "%0000093f"), Attrs([Attr("address","0x400A3C"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_373, "%00000945"), Attrs([Attr("address","0x400A40"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(2_380, "%0000094c"), + Attrs([Attr("address","0x400A44"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_385, "%00000951"), Attrs([Attr("address","0x400A48"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_391, "%00000957"), Attrs([Attr("address","0x400A4C"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(2_398, "%0000095e"), + Attrs([Attr("address","0x400A50"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_404, "%00000964"), Attrs([Attr("address","0x400A54"), +Attr("insn","cmp w0, #0x0")]), Var("#15",Imm(32)), +PLUS(Extract(31,0,Var("R0",Imm(64))),Int(4294967295,32))), +Def(Tid(2_409, "%00000969"), Attrs([Attr("address","0x400A54"), +Attr("insn","cmp w0, #0x0")]), Var("VF",Imm(1)), +NEQ(SIGNED(33,PLUS(Var("#15",Imm(32)),Int(1,32))),PLUS(SIGNED(33,Extract(31,0,Var("R0",Imm(64)))),Int(0,33)))), +Def(Tid(2_414, "%0000096e"), Attrs([Attr("address","0x400A54"), +Attr("insn","cmp w0, #0x0")]), Var("CF",Imm(1)), +NEQ(UNSIGNED(33,PLUS(Var("#15",Imm(32)),Int(1,32))),PLUS(UNSIGNED(33,Extract(31,0,Var("R0",Imm(64)))),Int(4294967296,33)))), +Def(Tid(2_418, "%00000972"), Attrs([Attr("address","0x400A54"), +Attr("insn","cmp w0, #0x0")]), Var("ZF",Imm(1)), +EQ(PLUS(Var("#15",Imm(32)),Int(1,32)),Int(0,32))), +Def(Tid(2_422, "%00000976"), Attrs([Attr("address","0x400A54"), +Attr("insn","cmp w0, #0x0")]), Var("NF",Imm(1)), +Extract(31,31,PLUS(Var("#15",Imm(32)),Int(1,32))))]), +Jmps([Goto(Tid(2_428, "%0000097c"), Attrs([Attr("address","0x400A58"), +Attr("insn","b.ne #0x20")]), NEQ(Var("ZF",Imm(1)),Int(1,1)), +Direct(Tid(2_426, "%0000097a"))), Goto(Tid(4_942, "%0000134e"), Attrs([]), + Int(1,1), Direct(Tid(3_178, "%00000c6a")))])), Blk(Tid(3_178, "%00000c6a"), + Attrs([Attr("address","0x400A5C")]), Phis([]), +Defs([Def(Tid(3_181, "%00000c6d"), Attrs([Attr("address","0x400A5C"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_187, "%00000c73"), Attrs([Attr("address","0x400A60"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(3_194, "%00000c7a"), + Attrs([Attr("address","0x400A64"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_200, "%00000c80"), Attrs([Attr("address","0x400A68"), +Attr("insn","add w1, w0, #0x2")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R0",Imm(64))),Int(2,32)))), +Def(Tid(3_205, "%00000c85"), Attrs([Attr("address","0x400A6C"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_211, "%00000c8b"), Attrs([Attr("address","0x400A70"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(3_219, "%00000c93"), + Attrs([Attr("address","0x400A74"), Attr("insn","str w1, [x0]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32))]), +Jmps([Goto(Tid(4_943, "%0000134f"), Attrs([]), Int(1,1), +Direct(Tid(2_426, "%0000097a")))])), Blk(Tid(2_426, "%0000097a"), + Attrs([Attr("address","0x400A78")]), Phis([]), +Defs([Def(Tid(2_434, "%00000982"), Attrs([Attr("address","0x400A78"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_440, "%00000988"), Attrs([Attr("address","0x400A7C"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(2_447, "%0000098f"), + Attrs([Attr("address","0x400A80"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_453, "%00000995"), Attrs([Attr("address","0x400A84"), +Attr("insn","cmp w0, #0x3e8")]), Var("#16",Imm(32)), +PLUS(Extract(31,0,Var("R0",Imm(64))),Int(4294966295,32))), +Def(Tid(2_458, "%0000099a"), Attrs([Attr("address","0x400A84"), +Attr("insn","cmp w0, #0x3e8")]), Var("VF",Imm(1)), +NEQ(SIGNED(33,PLUS(Var("#16",Imm(32)),Int(1,32))),PLUS(SIGNED(33,Extract(31,0,Var("R0",Imm(64)))),Int(8589933592,33)))), +Def(Tid(2_463, "%0000099f"), Attrs([Attr("address","0x400A84"), +Attr("insn","cmp w0, #0x3e8")]), Var("CF",Imm(1)), +NEQ(UNSIGNED(33,PLUS(Var("#16",Imm(32)),Int(1,32))),PLUS(UNSIGNED(33,Extract(31,0,Var("R0",Imm(64)))),Int(4294966296,33)))), +Def(Tid(2_467, "%000009a3"), Attrs([Attr("address","0x400A84"), +Attr("insn","cmp w0, #0x3e8")]), Var("ZF",Imm(1)), +EQ(PLUS(Var("#16",Imm(32)),Int(1,32)),Int(0,32))), +Def(Tid(2_471, "%000009a7"), Attrs([Attr("address","0x400A84"), +Attr("insn","cmp w0, #0x3e8")]), Var("NF",Imm(1)), +Extract(31,31,PLUS(Var("#16",Imm(32)),Int(1,32))))]), +Jmps([Goto(Tid(2_478, "%000009ae"), Attrs([Attr("address","0x400A88"), +Attr("insn","b.hi #0x20")]), + AND(EQ(Var("CF",Imm(1)),Int(1,1)),EQ(Var("ZF",Imm(1)),Int(0,1))), +Direct(Tid(2_476, "%000009ac"))), Goto(Tid(4_944, "%00001350"), Attrs([]), + Int(1,1), Direct(Tid(3_135, "%00000c3f")))])), Blk(Tid(3_135, "%00000c3f"), + Attrs([Attr("address","0x400A8C")]), Phis([]), +Defs([Def(Tid(3_138, "%00000c42"), Attrs([Attr("address","0x400A8C"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_144, "%00000c48"), Attrs([Attr("address","0x400A90"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(3_151, "%00000c4f"), + Attrs([Attr("address","0x400A94"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_157, "%00000c55"), Attrs([Attr("address","0x400A98"), +Attr("insn","add w1, w0, #0x1")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R0",Imm(64))),Int(1,32)))), +Def(Tid(3_162, "%00000c5a"), Attrs([Attr("address","0x400A9C"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_168, "%00000c60"), Attrs([Attr("address","0x400AA0"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(3_176, "%00000c68"), + Attrs([Attr("address","0x400AA4"), Attr("insn","str w1, [x0]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32))]), +Jmps([Goto(Tid(4_945, "%00001351"), Attrs([]), Int(1,1), +Direct(Tid(2_476, "%000009ac")))])), Blk(Tid(2_476, "%000009ac"), + Attrs([Attr("address","0x400AA8")]), Phis([]), +Defs([Def(Tid(2_484, "%000009b4"), Attrs([Attr("address","0x400AA8"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_490, "%000009ba"), Attrs([Attr("address","0x400AAC"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(2_497, "%000009c1"), + Attrs([Attr("address","0x400AB0"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_503, "%000009c7"), Attrs([Attr("address","0x400AB4"), +Attr("insn","cmn w0, #0x1")]), Var("#17",Imm(32)), +PLUS(Extract(31,0,Var("R0",Imm(64))),Int(1,32))), +Def(Tid(2_508, "%000009cc"), Attrs([Attr("address","0x400AB4"), +Attr("insn","cmn w0, #0x1")]), Var("VF",Imm(1)), +NEQ(SIGNED(33,Var("#17",Imm(32))),PLUS(SIGNED(33,Extract(31,0,Var("R0",Imm(64)))),Int(1,33)))), +Def(Tid(2_513, "%000009d1"), Attrs([Attr("address","0x400AB4"), +Attr("insn","cmn w0, #0x1")]), Var("CF",Imm(1)), +NEQ(UNSIGNED(33,Var("#17",Imm(32))),PLUS(UNSIGNED(33,Extract(31,0,Var("R0",Imm(64)))),Int(1,33)))), +Def(Tid(2_517, "%000009d5"), Attrs([Attr("address","0x400AB4"), +Attr("insn","cmn w0, #0x1")]), Var("ZF",Imm(1)), +EQ(Var("#17",Imm(32)),Int(0,32))), Def(Tid(2_521, "%000009d9"), + Attrs([Attr("address","0x400AB4"), Attr("insn","cmn w0, #0x1")]), + Var("NF",Imm(1)), Extract(31,31,Var("#17",Imm(32))))]), +Jmps([Goto(Tid(2_527, "%000009df"), Attrs([Attr("address","0x400AB8"), +Attr("insn","b.ne #0x20")]), NEQ(Var("ZF",Imm(1)),Int(1,1)), +Direct(Tid(2_525, "%000009dd"))), Goto(Tid(4_946, "%00001352"), Attrs([]), + Int(1,1), Direct(Tid(3_092, "%00000c14")))])), Blk(Tid(3_092, "%00000c14"), + Attrs([Attr("address","0x400ABC")]), Phis([]), +Defs([Def(Tid(3_095, "%00000c17"), Attrs([Attr("address","0x400ABC"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_101, "%00000c1d"), Attrs([Attr("address","0x400AC0"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(3_108, "%00000c24"), + Attrs([Attr("address","0x400AC4"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_114, "%00000c2a"), Attrs([Attr("address","0x400AC8"), +Attr("insn","add w1, w0, #0x1")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R0",Imm(64))),Int(1,32)))), +Def(Tid(3_119, "%00000c2f"), Attrs([Attr("address","0x400ACC"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_125, "%00000c35"), Attrs([Attr("address","0x400AD0"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(3_133, "%00000c3d"), + Attrs([Attr("address","0x400AD4"), Attr("insn","str w1, [x0]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32))]), +Jmps([Goto(Tid(4_947, "%00001353"), Attrs([]), Int(1,1), +Direct(Tid(2_525, "%000009dd")))])), Blk(Tid(2_525, "%000009dd"), + Attrs([Attr("address","0x400AD8")]), Phis([]), +Defs([Def(Tid(2_533, "%000009e5"), Attrs([Attr("address","0x400AD8"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_539, "%000009eb"), Attrs([Attr("address","0x400ADC"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(2_546, "%000009f2"), + Attrs([Attr("address","0x400AE0"), Attr("insn","ldr w1, [x0]")]), + Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_551, "%000009f7"), Attrs([Attr("address","0x400AE4"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_557, "%000009fd"), Attrs([Attr("address","0x400AE8"), +Attr("insn","add x0, x0, #0x34")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(52,64))), Def(Tid(2_564, "%00000a04"), + Attrs([Attr("address","0x400AEC"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_570, "%00000a0a"), Attrs([Attr("address","0x400AF0"), +Attr("insn","cmp w1, w0")]), Var("#18",Imm(32)), +NOT(Extract(31,0,Var("R0",Imm(64))))), Def(Tid(2_575, "%00000a0f"), + Attrs([Attr("address","0x400AF0"), Attr("insn","cmp w1, w0")]), + Var("#19",Imm(32)), +PLUS(Extract(31,0,Var("R1",Imm(64))),NOT(Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(2_581, "%00000a15"), Attrs([Attr("address","0x400AF0"), +Attr("insn","cmp w1, w0")]), Var("VF",Imm(1)), +NEQ(SIGNED(33,PLUS(Var("#19",Imm(32)),Int(1,32))),PLUS(PLUS(SIGNED(33,Extract(31,0,Var("R1",Imm(64)))),SIGNED(33,Var("#18",Imm(32)))),Int(1,33)))), +Def(Tid(2_587, "%00000a1b"), Attrs([Attr("address","0x400AF0"), +Attr("insn","cmp w1, w0")]), Var("CF",Imm(1)), +NEQ(UNSIGNED(33,PLUS(Var("#19",Imm(32)),Int(1,32))),PLUS(PLUS(UNSIGNED(33,Extract(31,0,Var("R1",Imm(64)))),UNSIGNED(33,Var("#18",Imm(32)))),Int(1,33)))), +Def(Tid(2_591, "%00000a1f"), Attrs([Attr("address","0x400AF0"), +Attr("insn","cmp w1, w0")]), Var("ZF",Imm(1)), +EQ(PLUS(Var("#19",Imm(32)),Int(1,32)),Int(0,32))), +Def(Tid(2_595, "%00000a23"), Attrs([Attr("address","0x400AF0"), +Attr("insn","cmp w1, w0")]), Var("NF",Imm(1)), +Extract(31,31,PLUS(Var("#19",Imm(32)),Int(1,32))))]), +Jmps([Goto(Tid(2_601, "%00000a29"), Attrs([Attr("address","0x400AF4"), +Attr("insn","b.lo #0x2c")]), NEQ(Var("CF",Imm(1)),Int(1,1)), +Direct(Tid(2_599, "%00000a27"))), Goto(Tid(4_948, "%00001354"), Attrs([]), + Int(1,1), Direct(Tid(3_030, "%00000bd6")))])), Blk(Tid(3_030, "%00000bd6"), + Attrs([Attr("address","0x400AF8")]), Phis([]), +Defs([Def(Tid(3_033, "%00000bd9"), Attrs([Attr("address","0x400AF8"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_039, "%00000bdf"), Attrs([Attr("address","0x400AFC"), +Attr("insn","add x0, x0, #0x34")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(52,64))), Def(Tid(3_046, "%00000be6"), + Attrs([Attr("address","0x400B00"), Attr("insn","ldr w1, [x0]")]), + Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_051, "%00000beb"), Attrs([Attr("address","0x400B04"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_057, "%00000bf1"), Attrs([Attr("address","0x400B08"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(3_064, "%00000bf8"), + Attrs([Attr("address","0x400B0C"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_071, "%00000bff"), Attrs([Attr("address","0x400B10"), +Attr("insn","add w1, w1, w0")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R1",Imm(64))),Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(3_076, "%00000c04"), Attrs([Attr("address","0x400B14"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_082, "%00000c0a"), Attrs([Attr("address","0x400B18"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(3_090, "%00000c12"), + Attrs([Attr("address","0x400B1C"), Attr("insn","str w1, [x0]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32))]), +Jmps([Goto(Tid(4_949, "%00001355"), Attrs([]), Int(1,1), +Direct(Tid(2_599, "%00000a27")))])), Blk(Tid(2_599, "%00000a27"), + Attrs([Attr("address","0x400B20")]), Phis([]), +Defs([Def(Tid(2_607, "%00000a2f"), Attrs([Attr("address","0x400B20"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_613, "%00000a35"), Attrs([Attr("address","0x400B24"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(2_620, "%00000a3c"), + Attrs([Attr("address","0x400B28"), Attr("insn","ldr w1, [x0]")]), + Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_625, "%00000a41"), Attrs([Attr("address","0x400B2C"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_631, "%00000a47"), Attrs([Attr("address","0x400B30"), +Attr("insn","add x0, x0, #0x34")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(52,64))), Def(Tid(2_638, "%00000a4e"), + Attrs([Attr("address","0x400B34"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_644, "%00000a54"), Attrs([Attr("address","0x400B38"), +Attr("insn","cmp w1, w0")]), Var("#20",Imm(32)), +NOT(Extract(31,0,Var("R0",Imm(64))))), Def(Tid(2_649, "%00000a59"), + Attrs([Attr("address","0x400B38"), Attr("insn","cmp w1, w0")]), + Var("#21",Imm(32)), +PLUS(Extract(31,0,Var("R1",Imm(64))),NOT(Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(2_655, "%00000a5f"), Attrs([Attr("address","0x400B38"), +Attr("insn","cmp w1, w0")]), Var("VF",Imm(1)), +NEQ(SIGNED(33,PLUS(Var("#21",Imm(32)),Int(1,32))),PLUS(PLUS(SIGNED(33,Extract(31,0,Var("R1",Imm(64)))),SIGNED(33,Var("#20",Imm(32)))),Int(1,33)))), +Def(Tid(2_661, "%00000a65"), Attrs([Attr("address","0x400B38"), +Attr("insn","cmp w1, w0")]), Var("CF",Imm(1)), +NEQ(UNSIGNED(33,PLUS(Var("#21",Imm(32)),Int(1,32))),PLUS(PLUS(UNSIGNED(33,Extract(31,0,Var("R1",Imm(64)))),UNSIGNED(33,Var("#20",Imm(32)))),Int(1,33)))), +Def(Tid(2_665, "%00000a69"), Attrs([Attr("address","0x400B38"), +Attr("insn","cmp w1, w0")]), Var("ZF",Imm(1)), +EQ(PLUS(Var("#21",Imm(32)),Int(1,32)),Int(0,32))), +Def(Tid(2_669, "%00000a6d"), Attrs([Attr("address","0x400B38"), +Attr("insn","cmp w1, w0")]), Var("NF",Imm(1)), +Extract(31,31,PLUS(Var("#21",Imm(32)),Int(1,32))))]), +Jmps([Goto(Tid(2_676, "%00000a74"), Attrs([Attr("address","0x400B3C"), +Attr("insn","b.hi #0x2c")]), + AND(EQ(Var("CF",Imm(1)),Int(1,1)),EQ(Var("ZF",Imm(1)),Int(0,1))), +Direct(Tid(2_674, "%00000a72"))), Goto(Tid(4_950, "%00001356"), Attrs([]), + Int(1,1), Direct(Tid(2_968, "%00000b98")))])), Blk(Tid(2_968, "%00000b98"), + Attrs([Attr("address","0x400B40")]), Phis([]), +Defs([Def(Tid(2_971, "%00000b9b"), Attrs([Attr("address","0x400B40"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_977, "%00000ba1"), Attrs([Attr("address","0x400B44"), +Attr("insn","add x0, x0, #0x34")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(52,64))), Def(Tid(2_984, "%00000ba8"), + Attrs([Attr("address","0x400B48"), Attr("insn","ldr w1, [x0]")]), + Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_989, "%00000bad"), Attrs([Attr("address","0x400B4C"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_995, "%00000bb3"), Attrs([Attr("address","0x400B50"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(3_002, "%00000bba"), + Attrs([Attr("address","0x400B54"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(3_009, "%00000bc1"), Attrs([Attr("address","0x400B58"), +Attr("insn","add w1, w1, w0")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R1",Imm(64))),Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(3_014, "%00000bc6"), Attrs([Attr("address","0x400B5C"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(3_020, "%00000bcc"), Attrs([Attr("address","0x400B60"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(3_028, "%00000bd4"), + Attrs([Attr("address","0x400B64"), Attr("insn","str w1, [x0]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32))]), +Jmps([Goto(Tid(4_951, "%00001357"), Attrs([]), Int(1,1), +Direct(Tid(2_674, "%00000a72")))])), Blk(Tid(2_674, "%00000a72"), + Attrs([Attr("address","0x400B68")]), Phis([]), +Defs([Def(Tid(2_682, "%00000a7a"), Attrs([Attr("address","0x400B68"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_688, "%00000a80"), Attrs([Attr("address","0x400B6C"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(2_695, "%00000a87"), + Attrs([Attr("address","0x400B70"), Attr("insn","ldr w1, [x0]")]), + Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_700, "%00000a8c"), Attrs([Attr("address","0x400B74"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_706, "%00000a92"), Attrs([Attr("address","0x400B78"), +Attr("insn","add x0, x0, #0x34")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(52,64))), Def(Tid(2_713, "%00000a99"), + Attrs([Attr("address","0x400B7C"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_719, "%00000a9f"), Attrs([Attr("address","0x400B80"), +Attr("insn","cmp w1, w0")]), Var("#22",Imm(32)), +NOT(Extract(31,0,Var("R0",Imm(64))))), Def(Tid(2_724, "%00000aa4"), + Attrs([Attr("address","0x400B80"), Attr("insn","cmp w1, w0")]), + Var("#23",Imm(32)), +PLUS(Extract(31,0,Var("R1",Imm(64))),NOT(Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(2_730, "%00000aaa"), Attrs([Attr("address","0x400B80"), +Attr("insn","cmp w1, w0")]), Var("VF",Imm(1)), +NEQ(SIGNED(33,PLUS(Var("#23",Imm(32)),Int(1,32))),PLUS(PLUS(SIGNED(33,Extract(31,0,Var("R1",Imm(64)))),SIGNED(33,Var("#22",Imm(32)))),Int(1,33)))), +Def(Tid(2_736, "%00000ab0"), Attrs([Attr("address","0x400B80"), +Attr("insn","cmp w1, w0")]), Var("CF",Imm(1)), +NEQ(UNSIGNED(33,PLUS(Var("#23",Imm(32)),Int(1,32))),PLUS(PLUS(UNSIGNED(33,Extract(31,0,Var("R1",Imm(64)))),UNSIGNED(33,Var("#22",Imm(32)))),Int(1,33)))), +Def(Tid(2_740, "%00000ab4"), Attrs([Attr("address","0x400B80"), +Attr("insn","cmp w1, w0")]), Var("ZF",Imm(1)), +EQ(PLUS(Var("#23",Imm(32)),Int(1,32)),Int(0,32))), +Def(Tid(2_744, "%00000ab8"), Attrs([Attr("address","0x400B80"), +Attr("insn","cmp w1, w0")]), Var("NF",Imm(1)), +Extract(31,31,PLUS(Var("#23",Imm(32)),Int(1,32))))]), +Jmps([Goto(Tid(2_750, "%00000abe"), Attrs([Attr("address","0x400B84"), +Attr("insn","b.hs #0x2c")]), EQ(Var("CF",Imm(1)),Int(1,1)), +Direct(Tid(2_748, "%00000abc"))), Goto(Tid(4_952, "%00001358"), Attrs([]), + Int(1,1), Direct(Tid(2_906, "%00000b5a")))])), Blk(Tid(2_906, "%00000b5a"), + Attrs([Attr("address","0x400B88")]), Phis([]), +Defs([Def(Tid(2_909, "%00000b5d"), Attrs([Attr("address","0x400B88"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_915, "%00000b63"), Attrs([Attr("address","0x400B8C"), +Attr("insn","add x0, x0, #0x34")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(52,64))), Def(Tid(2_922, "%00000b6a"), + Attrs([Attr("address","0x400B90"), Attr("insn","ldr w1, [x0]")]), + Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_927, "%00000b6f"), Attrs([Attr("address","0x400B94"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_933, "%00000b75"), Attrs([Attr("address","0x400B98"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(2_940, "%00000b7c"), + Attrs([Attr("address","0x400B9C"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_947, "%00000b83"), Attrs([Attr("address","0x400BA0"), +Attr("insn","add w1, w1, w0")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R1",Imm(64))),Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(2_952, "%00000b88"), Attrs([Attr("address","0x400BA4"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_958, "%00000b8e"), Attrs([Attr("address","0x400BA8"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(2_966, "%00000b96"), + Attrs([Attr("address","0x400BAC"), Attr("insn","str w1, [x0]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32))]), +Jmps([Goto(Tid(4_953, "%00001359"), Attrs([]), Int(1,1), +Direct(Tid(2_748, "%00000abc")))])), Blk(Tid(2_748, "%00000abc"), + Attrs([Attr("address","0x400BB0")]), Phis([]), +Defs([Def(Tid(2_756, "%00000ac4"), Attrs([Attr("address","0x400BB0"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_762, "%00000aca"), Attrs([Attr("address","0x400BB4"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(2_769, "%00000ad1"), + Attrs([Attr("address","0x400BB8"), Attr("insn","ldr w1, [x0]")]), + Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_774, "%00000ad6"), Attrs([Attr("address","0x400BBC"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_780, "%00000adc"), Attrs([Attr("address","0x400BC0"), +Attr("insn","add x0, x0, #0x34")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(52,64))), Def(Tid(2_787, "%00000ae3"), + Attrs([Attr("address","0x400BC4"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_793, "%00000ae9"), Attrs([Attr("address","0x400BC8"), +Attr("insn","cmp w1, w0")]), Var("#24",Imm(32)), +NOT(Extract(31,0,Var("R0",Imm(64))))), Def(Tid(2_798, "%00000aee"), + Attrs([Attr("address","0x400BC8"), Attr("insn","cmp w1, w0")]), + Var("#25",Imm(32)), +PLUS(Extract(31,0,Var("R1",Imm(64))),NOT(Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(2_804, "%00000af4"), Attrs([Attr("address","0x400BC8"), +Attr("insn","cmp w1, w0")]), Var("VF",Imm(1)), +NEQ(SIGNED(33,PLUS(Var("#25",Imm(32)),Int(1,32))),PLUS(PLUS(SIGNED(33,Extract(31,0,Var("R1",Imm(64)))),SIGNED(33,Var("#24",Imm(32)))),Int(1,33)))), +Def(Tid(2_810, "%00000afa"), Attrs([Attr("address","0x400BC8"), +Attr("insn","cmp w1, w0")]), Var("CF",Imm(1)), +NEQ(UNSIGNED(33,PLUS(Var("#25",Imm(32)),Int(1,32))),PLUS(PLUS(UNSIGNED(33,Extract(31,0,Var("R1",Imm(64)))),UNSIGNED(33,Var("#24",Imm(32)))),Int(1,33)))), +Def(Tid(2_814, "%00000afe"), Attrs([Attr("address","0x400BC8"), +Attr("insn","cmp w1, w0")]), Var("ZF",Imm(1)), +EQ(PLUS(Var("#25",Imm(32)),Int(1,32)),Int(0,32))), +Def(Tid(2_818, "%00000b02"), Attrs([Attr("address","0x400BC8"), +Attr("insn","cmp w1, w0")]), Var("NF",Imm(1)), +Extract(31,31,PLUS(Var("#25",Imm(32)),Int(1,32))))]), +Jmps([Goto(Tid(2_825, "%00000b09"), Attrs([Attr("address","0x400BCC"), +Attr("insn","b.ls #0x2c")]), + NOT(AND(EQ(Var("CF",Imm(1)),Int(1,1)),EQ(Var("ZF",Imm(1)),Int(0,1)))), +Direct(Tid(2_823, "%00000b07"))), Goto(Tid(4_954, "%0000135a"), Attrs([]), + Int(1,1), Direct(Tid(2_844, "%00000b1c")))])), Blk(Tid(2_844, "%00000b1c"), + Attrs([Attr("address","0x400BD0")]), Phis([]), +Defs([Def(Tid(2_847, "%00000b1f"), Attrs([Attr("address","0x400BD0"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_853, "%00000b25"), Attrs([Attr("address","0x400BD4"), +Attr("insn","add x0, x0, #0x34")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(52,64))), Def(Tid(2_860, "%00000b2c"), + Attrs([Attr("address","0x400BD8"), Attr("insn","ldr w1, [x0]")]), + Var("R1",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_865, "%00000b31"), Attrs([Attr("address","0x400BDC"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_871, "%00000b37"), Attrs([Attr("address","0x400BE0"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(2_878, "%00000b3e"), + Attrs([Attr("address","0x400BE4"), Attr("insn","ldr w0, [x0]")]), + Var("R0",Imm(64)), +UNSIGNED(64,Load(Var("mem",Mem(64,8)),Var("R0",Imm(64)),LittleEndian(),32))), +Def(Tid(2_885, "%00000b45"), Attrs([Attr("address","0x400BE8"), +Attr("insn","add w1, w1, w0")]), Var("R1",Imm(64)), +UNSIGNED(64,PLUS(Extract(31,0,Var("R1",Imm(64))),Extract(31,0,Var("R0",Imm(64)))))), +Def(Tid(2_890, "%00000b4a"), Attrs([Attr("address","0x400BEC"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(2_896, "%00000b50"), Attrs([Attr("address","0x400BF0"), +Attr("insn","add x0, x0, #0x38")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(56,64))), Def(Tid(2_904, "%00000b58"), + Attrs([Attr("address","0x400BF4"), Attr("insn","str w1, [x0]")]), + Var("mem",Mem(64,8)), +Store(Var("mem",Mem(64,8)),Var("R0",Imm(64)),Extract(31,0,Var("R1",Imm(64))),LittleEndian(),32))]), +Jmps([Goto(Tid(4_955, "%0000135b"), Attrs([]), Int(1,1), +Direct(Tid(2_823, "%00000b07")))])), Blk(Tid(2_823, "%00000b07"), + Attrs([Attr("address","0x400BF8")]), Phis([]), +Defs([Def(Tid(2_831, "%00000b0f"), Attrs([Attr("address","0x400BF8"), +Attr("insn","mov w0, #0x0")]), Var("R0",Imm(64)), Int(0,64)), +Def(Tid(2_837, "%00000b15"), Attrs([Attr("address","0x400BFC"), +Attr("insn","add sp, sp, #0x10")]), Var("R31",Imm(64)), +PLUS(Var("R31",Imm(64)),Int(16,64)))]), Jmps([Call(Tid(2_842, "%00000b1a"), + Attrs([Attr("address","0x400C00"), Attr("insn","ret")]), Int(1,1), +(Indirect(Var("R30",Imm(64))),))]))])), +Sub(Tid(4_956, "@register_tm_clones"), + Attrs([Attr("c.proto","signed (*)(void)"), Attr("address","0x4006D0")]), + "register_tm_clones", Args([Arg(Tid(4_979, "%00001373"), + Attrs([Attr("c.layout","[signed : 32]"), Attr("c.data","Top:u32"), +Attr("c.type","signed")]), Var("register_tm_clones_result",Imm(32)), +LOW(32,Var("R0",Imm(64))), Out())]), +Blks([Blk(Tid(4_003, "@register_tm_clones"), + Attrs([Attr("address","0x4006D0")]), Phis([]), +Defs([Def(Tid(4_006, "%00000fa6"), Attrs([Attr("address","0x4006D0"), +Attr("insn","adrp x0, #0x20000")]), Var("R0",Imm(64)), Int(4325376,64)), +Def(Tid(4_012, "%00000fac"), Attrs([Attr("address","0x4006D4"), +Attr("insn","add x0, x0, #0x28")]), Var("R0",Imm(64)), +PLUS(Var("R0",Imm(64)),Int(40,64))), Def(Tid(4_017, "%00000fb1"), + Attrs([Attr("address","0x4006D8"), Attr("insn","adrp x1, #0x20000")]), + Var("R1",Imm(64)), Int(4325376,64)), Def(Tid(4_023, "%00000fb7"), + Attrs([Attr("address","0x4006DC"), Attr("insn","add x1, x1, #0x28")]), + Var("R1",Imm(64)), PLUS(Var("R1",Imm(64)),Int(40,64))), +Def(Tid(4_030, "%00000fbe"), Attrs([Attr("address","0x4006E0"), +Attr("insn","sub x1, x1, x0")]), Var("R1",Imm(64)), +PLUS(PLUS(Var("R1",Imm(64)),NOT(Var("R0",Imm(64)))),Int(1,64))), +Def(Tid(4_036, "%00000fc4"), Attrs([Attr("address","0x4006E4"), +Attr("insn","lsr x2, x1, #63")]), Var("R2",Imm(64)), +Concat(Int(0,63),Extract(63,63,Var("R1",Imm(64))))), +Def(Tid(4_043, "%00000fcb"), Attrs([Attr("address","0x4006E8"), +Attr("insn","add x1, x2, x1, asr #3")]), Var("R1",Imm(64)), +PLUS(Var("R2",Imm(64)),ARSHIFT(Var("R1",Imm(64)),Int(3,3)))), +Def(Tid(4_049, "%00000fd1"), Attrs([Attr("address","0x4006EC"), +Attr("insn","asr x1, x1, #1")]), Var("R1",Imm(64)), +SIGNED(64,Extract(63,1,Var("R1",Imm(64)))))]), +Jmps([Goto(Tid(4_055, "%00000fd7"), Attrs([Attr("address","0x4006F0"), +Attr("insn","cbz x1, #0x18")]), EQ(Var("R1",Imm(64)),Int(0,64)), +Direct(Tid(4_053, "%00000fd5"))), Goto(Tid(4_957, "%0000135d"), Attrs([]), + Int(1,1), Direct(Tid(4_172, "%0000104c")))])), Blk(Tid(4_172, "%0000104c"), + Attrs([Attr("address","0x4006F4")]), Phis([]), +Defs([Def(Tid(4_175, "%0000104f"), Attrs([Attr("address","0x4006F4"), +Attr("insn","adrp x2, #0x1f000")]), Var("R2",Imm(64)), Int(4321280,64)), +Def(Tid(4_182, "%00001056"), Attrs([Attr("address","0x4006F8"), +Attr("insn","ldr x2, [x2, #0xfe0]")]), Var("R2",Imm(64)), +Load(Var("mem",Mem(64,8)),PLUS(Var("R2",Imm(64)),Int(4064,64)),LittleEndian(),64))]), +Jmps([Goto(Tid(4_187, "%0000105b"), Attrs([Attr("address","0x4006FC"), +Attr("insn","cbz x2, #0xc")]), EQ(Var("R2",Imm(64)),Int(0,64)), +Direct(Tid(4_053, "%00000fd5"))), Goto(Tid(4_958, "%0000135e"), Attrs([]), + Int(1,1), Direct(Tid(4_191, "%0000105f")))])), Blk(Tid(4_053, "%00000fd5"), + Attrs([Attr("address","0x400708")]), Phis([]), Defs([]), +Jmps([Call(Tid(4_061, "%00000fdd"), Attrs([Attr("address","0x400708"), +Attr("insn","ret")]), Int(1,1), (Indirect(Var("R30",Imm(64))),))])), +Blk(Tid(4_191, "%0000105f"), Attrs([Attr("address","0x400700")]), Phis([]), +Defs([Def(Tid(4_195, "%00001063"), Attrs([Attr("address","0x400700"), +Attr("insn","mov x16, x2")]), Var("R16",Imm(64)), Var("R2",Imm(64)))]), +Jmps([Call(Tid(4_200, "%00001068"), Attrs([Attr("address","0x400704"), +Attr("insn","br x16")]), Int(1,1), +(Indirect(Var("R16",Imm(64))),))]))]))]))) \ No newline at end of file diff --git a/examples/conds/conds.c b/examples/conds/conds.c new file mode 100644 index 000000000..faf221829 --- /dev/null +++ b/examples/conds/conds.c @@ -0,0 +1,86 @@ + + +#include +int x = 0; +volatile int r = 0; +volatile unsigned z; +volatile unsigned y; + +int main(int argc, char **argv) { + x = argc; + y = argc; + z = argc; + + if (x < 0) { + x = r; + } + + if (x > 0) { + x = r; + } + + if (x < 5) { + x += r; + } + + if (x <= 8) { + x += r; + } + + + if (x >= 100) { + x += r; + } + + + if (x > 1000) { + x += r; + } + + + if (x < r) { + x += r * 10; + } + if (x <= r + 100) { + x += r * r; + } + if (x > r + 1000) { + x += 2 * r + r; + } + if (x >= r + 2000) { + x += r + r; + } + + if (y < 0) { + y += 1; + } + + if (y <= 0) { + y += 2; + } + + if (y <= 1000) { + y += 1; + } + + if (y >= -1) { + y += 1; + } + + if (y >= z) { + y += z; + } + + if (y <= z) { + y += z; + } + if (y < z) { + y += z; + } + if (y > z) { + y += z; + } + + +} + diff --git a/examples/conds/conds.gts b/examples/conds/conds.gts new file mode 100644 index 000000000..39e8ad2a5 Binary files /dev/null and b/examples/conds/conds.gts differ diff --git a/examples/conds/conds.relf b/examples/conds/conds.relf new file mode 100644 index 000000000..ee6b37db4 --- /dev/null +++ b/examples/conds/conds.relf @@ -0,0 +1,119 @@ + +Relocation section '.rela.dyn' at offset 0x530 contains 3 entries: + Offset Info Type Symbol's Value Symbol's Name + Addend +000000000041ffd0 0000000200000401 R_AARCH64_GLOB_DAT 0000000000000000 _ITM_deregisterTMCloneTable + 0 +000000000041ffd8 0000000300000401 R_AARCH64_GLOB_DAT 0000000000000000 __gmon_start__ + 0 +000000000041ffe0 0000000500000401 R_AARCH64_GLOB_DAT 0000000000000000 _ITM_registerTMCloneTable + 0 + +Relocation section '.rela.plt' at offset 0x578 contains 3 entries: + Offset Info Type Symbol's Value Symbol's Name + Addend +0000000000420000 0000000100000402 R_AARCH64_JUMP_SLOT 0000000000000000 __libc_start_main@GLIBC_2.34 + 0 +0000000000420008 0000000300000402 R_AARCH64_JUMP_SLOT 0000000000000000 __gmon_start__ + 0 +0000000000420010 0000000400000402 R_AARCH64_JUMP_SLOT 0000000000000000 abort@GLIBC_2.17 + 0 + +Symbol table '.dynsym' contains 6 entries: + Num: Value Size Type Bind Vis Ndx Name + 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND + 1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.34 (2) + 2: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTable + 3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__ + 4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND abort@GLIBC_2.17 (3) + 5: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable + +Symbol table '.symtab' contains 95 entries: + Num: Value Size Type Bind Vis Ndx Name + 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND + 1: 0000000000400238 0 SECTION LOCAL DEFAULT 1 .interp + 2: 00000000004002a8 0 SECTION LOCAL DEFAULT 2 .note.ABI-tag + 3: 00000000004002c8 0 SECTION LOCAL DEFAULT 3 .hash + 4: 00000000004002f8 0 SECTION LOCAL DEFAULT 4 .gnu.hash + 5: 0000000000400318 0 SECTION LOCAL DEFAULT 5 .dynsym + 6: 00000000004003a8 0 SECTION LOCAL DEFAULT 6 .dynstr + 7: 00000000004004f2 0 SECTION LOCAL DEFAULT 7 .gnu.version + 8: 0000000000400500 0 SECTION LOCAL DEFAULT 8 .gnu.version_r + 9: 0000000000400530 0 SECTION LOCAL DEFAULT 9 .rela.dyn + 10: 0000000000400578 0 SECTION LOCAL DEFAULT 10 .rela.plt + 11: 00000000004005c0 0 SECTION LOCAL DEFAULT 11 .init + 12: 00000000004005e0 0 SECTION LOCAL DEFAULT 12 .plt + 13: 0000000000400640 0 SECTION LOCAL DEFAULT 13 .text + 14: 0000000000400c04 0 SECTION LOCAL DEFAULT 14 .fini + 15: 0000000000400c18 0 SECTION LOCAL DEFAULT 15 .rodata + 16: 0000000000400c1c 0 SECTION LOCAL DEFAULT 16 .eh_frame_hdr + 17: 0000000000400c60 0 SECTION LOCAL DEFAULT 17 .eh_frame + 18: 000000000041fdc8 0 SECTION LOCAL DEFAULT 18 .init_array + 19: 000000000041fdd0 0 SECTION LOCAL DEFAULT 19 .fini_array + 20: 000000000041fdd8 0 SECTION LOCAL DEFAULT 20 .dynamic + 21: 000000000041ffc8 0 SECTION LOCAL DEFAULT 21 .got + 22: 000000000041ffe8 0 SECTION LOCAL DEFAULT 22 .got.plt + 23: 0000000000420018 0 SECTION LOCAL DEFAULT 23 .data + 24: 0000000000420028 0 SECTION LOCAL DEFAULT 24 .bss + 25: 0000000000000000 0 SECTION LOCAL DEFAULT 25 .comment + 26: 0000000000000000 0 FILE LOCAL DEFAULT ABS crt1.o + 27: 00000000004002a8 0 NOTYPE LOCAL DEFAULT 2 $d + 28: 00000000004002a8 32 OBJECT LOCAL DEFAULT 2 __abi_tag + 29: 0000000000400640 0 NOTYPE LOCAL DEFAULT 13 $x + 30: 0000000000400674 0 NOTYPE LOCAL DEFAULT 13 __wrap_main + 31: 0000000000400c74 0 NOTYPE LOCAL DEFAULT 17 $d + 32: 0000000000400c18 0 NOTYPE LOCAL DEFAULT 15 $d + 33: 0000000000400680 0 NOTYPE LOCAL DEFAULT 13 $x + 34: 0000000000400c88 0 NOTYPE LOCAL DEFAULT 17 $d + 35: 0000000000000000 0 FILE LOCAL DEFAULT ABS crti.o + 36: 0000000000400684 0 NOTYPE LOCAL DEFAULT 13 $x + 37: 0000000000400684 20 FUNC LOCAL DEFAULT 13 call_weak_fn + 38: 00000000004005c0 0 NOTYPE LOCAL DEFAULT 11 $x + 39: 0000000000400c04 0 NOTYPE LOCAL DEFAULT 14 $x + 40: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtn.o + 41: 00000000004005d0 0 NOTYPE LOCAL DEFAULT 11 $x + 42: 0000000000400c10 0 NOTYPE LOCAL DEFAULT 14 $x + 43: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtbegin.o + 44: 00000000004006a0 0 NOTYPE LOCAL DEFAULT 13 $x + 45: 00000000004006a0 0 FUNC LOCAL DEFAULT 13 deregister_tm_clones + 46: 00000000004006d0 0 FUNC LOCAL DEFAULT 13 register_tm_clones + 47: 0000000000420020 0 NOTYPE LOCAL DEFAULT 23 $d + 48: 0000000000400710 0 FUNC LOCAL DEFAULT 13 __do_global_dtors_aux + 49: 0000000000420028 1 OBJECT LOCAL DEFAULT 24 completed.0 + 50: 000000000041fdd0 0 NOTYPE LOCAL DEFAULT 19 $d + 51: 000000000041fdd0 0 OBJECT LOCAL DEFAULT 19 __do_global_dtors_aux_fini_array_entry + 52: 0000000000400740 0 FUNC LOCAL DEFAULT 13 frame_dummy + 53: 000000000041fdc8 0 NOTYPE LOCAL DEFAULT 18 $d + 54: 000000000041fdc8 0 OBJECT LOCAL DEFAULT 18 __frame_dummy_init_array_entry + 55: 0000000000400ca0 0 NOTYPE LOCAL DEFAULT 17 $d + 56: 0000000000420028 0 NOTYPE LOCAL DEFAULT 24 $d + 57: 0000000000000000 0 FILE LOCAL DEFAULT ABS conds.c + 58: 000000000042002c 0 NOTYPE LOCAL DEFAULT 24 $d + 59: 0000000000400744 0 NOTYPE LOCAL DEFAULT 13 $x + 60: 0000000000400d00 0 NOTYPE LOCAL DEFAULT 17 $d + 61: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtend.o + 62: 0000000000400d1c 0 NOTYPE LOCAL DEFAULT 17 $d + 63: 0000000000400d1c 0 OBJECT LOCAL DEFAULT 17 __FRAME_END__ + 64: 0000000000000000 0 FILE LOCAL DEFAULT ABS + 65: 000000000041fdd8 0 OBJECT LOCAL DEFAULT 20 _DYNAMIC + 66: 0000000000400c1c 0 NOTYPE LOCAL DEFAULT 16 __GNU_EH_FRAME_HDR + 67: 000000000041ffc8 0 OBJECT LOCAL DEFAULT 21 _GLOBAL_OFFSET_TABLE_ + 68: 00000000004005e0 0 NOTYPE LOCAL DEFAULT 12 $x + 69: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.34 + 70: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTable + 71: 0000000000420018 0 NOTYPE WEAK DEFAULT 23 data_start + 72: 0000000000420028 0 NOTYPE GLOBAL DEFAULT 24 __bss_start__ + 73: 0000000000420040 0 NOTYPE GLOBAL DEFAULT 24 _bss_end__ + 74: 0000000000420030 4 OBJECT GLOBAL DEFAULT 24 r + 75: 0000000000420028 0 NOTYPE GLOBAL DEFAULT 23 _edata + 76: 0000000000420034 4 OBJECT GLOBAL DEFAULT 24 z + 77: 000000000042002c 4 OBJECT GLOBAL DEFAULT 24 x + 78: 0000000000400c04 0 FUNC GLOBAL HIDDEN 14 _fini + 79: 0000000000420040 0 NOTYPE GLOBAL DEFAULT 24 __bss_end__ + 80: 0000000000420018 0 NOTYPE GLOBAL DEFAULT 23 __data_start + 81: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__ + 82: 0000000000420020 0 OBJECT GLOBAL HIDDEN 23 __dso_handle + 83: 0000000000000000 0 FUNC GLOBAL DEFAULT UND abort@GLIBC_2.17 + 84: 0000000000400c18 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used + 85: 0000000000420040 0 NOTYPE GLOBAL DEFAULT 24 _end + 86: 0000000000400680 4 FUNC GLOBAL HIDDEN 13 _dl_relocate_static_pie + 87: 0000000000400640 60 FUNC GLOBAL DEFAULT 13 _start + 88: 0000000000420040 0 NOTYPE GLOBAL DEFAULT 24 __end__ + 89: 0000000000420038 4 OBJECT GLOBAL DEFAULT 24 y + 90: 0000000000420028 0 NOTYPE GLOBAL DEFAULT 24 __bss_start + 91: 0000000000400744 1216 FUNC GLOBAL DEFAULT 13 main + 92: 0000000000420028 0 OBJECT GLOBAL HIDDEN 23 __TMC_END__ + 93: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable + 94: 00000000004005c0 0 FUNC GLOBAL HIDDEN 11 _init diff --git a/src/main/scala/Main.scala b/src/main/scala/Main.scala index 4a9e73751..59df08f45 100644 --- a/src/main/scala/Main.scala +++ b/src/main/scala/Main.scala @@ -28,8 +28,10 @@ object Main { lambdaStores: Flag, @arg(name = "boogie-procedure-rg", doc = "Switch version of procedure rely/guarantee checks to emit. (function|ifblock)") procedureRG: Option[String], - @arg(name = "verbose", short = 'v', doc = "Show extra debugging logs.") + @arg(name = "verbose", short = 'v', doc = "Show extra debugging logs (the same as -vl log)") verbose: Flag, + @arg(name = "vl", doc = s"Show extra debugging logs for a specific logger (${Logger.allLoggers.map(_.name).mkString(", ")}).") + verboseLog: Seq[String] = Seq(), @arg(name = "analyse", doc = "Run static analysis pass.") analyse: Flag, @arg(name = "interpret", doc = "Run BASIL IL interpreter.") @@ -40,6 +42,8 @@ object Main { mainProcedureName: String = "main", @arg(name = "procedure-call-depth", doc = "Cull procedures beyond this call depth from the main function (defaults to Int.MaxValue)") procedureDepth: Int = Int.MaxValue, + @arg(name = "trim-early", doc = "Cull procedures BEFORE running analysis") + trimEarly: Flag, @arg(name = "help", short = 'h', doc = "Show this help message.") help: Flag, @arg(name = "analysis-results", doc = "Log analysis results in files at specified path.") @@ -48,8 +52,14 @@ object Main { analysisResultsDot: Option[String], @arg(name = "threads", short = 't', doc = "Separates threads into multiple .bpl files with given output filename as prefix (requires --analyse flag)") threadSplit: Flag, + @arg(name = "parameter-form", doc = "Lift registers to local variables passed by parameter") + parameterForm: Flag, @arg(name = "summarise-procedures", doc = "Generates summaries of procedures which are used in pre/post-conditions (requires --analyse flag)") summariseProcedures: Flag, + @arg(name = "simplify", doc = "Partial evaluate / simplify BASIL IR before output (requires --analyse flag)") + simplify: Flag, + @arg(name = "validate-simplify", doc = "Emit SMT2 check for algebraic simplification translation validation to 'rewrites.smt2'") + validateSimplify: Flag, @arg(name = "memory-regions", doc = "Performs static analysis to separate memory into discrete regions in Boogie output (requires --analyse flag) (mra|dsa)") memoryRegions: Option[String] ) @@ -71,7 +81,19 @@ object Main { Logger.setLevel(LogLevel.INFO) if (conf.verbose.value) { - Logger.setLevel(LogLevel.DEBUG) + Logger.setLevel(LogLevel.DEBUG, true) + } + for (v <- conf.verboseLog) { + Logger.findLoggerByName(v) match { + case None => throw Exception(s"Unknown logger: '${v}': allowed are ${Logger.allLoggers.map(_.name).mkString(", ")}") + case Some(v) => v.setLevel(LogLevel.DEBUG, true) + } + } + + if (conf.analysisResults.isDefined || conf.analysisResultsDot.isDefined) { + DebugDumpIRLogger.setLevel(LogLevel.INFO) + } else { + DebugDumpIRLogger.setLevel(LogLevel.OFF) } val rely = conf.procedureRG match { @@ -107,8 +129,10 @@ object Main { val boogieGeneratorConfig = BoogieGeneratorConfig(boogieMemoryAccessMode, true, rely, conf.threadSplit.value) val q = BASILConfig( - loading = ILLoadingConfig(conf.inputFileName, conf.relfFileName, conf.specFileName, conf.dumpIL, conf.mainProcedureName, conf.procedureDepth), + loading = ILLoadingConfig(conf.inputFileName, conf.relfFileName, conf.specFileName, conf.dumpIL, conf.mainProcedureName, conf.procedureDepth, conf.parameterForm.value, conf.trimEarly.value), runInterpret = conf.interpret.value, + simplify = conf.simplify.value, + validateSimp = conf.validateSimplify.value, staticAnalysis = staticAnalysis, boogieTranslation = boogieGeneratorConfig, outputPrefix = conf.outFileName, diff --git a/src/main/scala/analysis/ANR.scala b/src/main/scala/analysis/ANR.scala index c318bbaee..02fc90974 100644 --- a/src/main/scala/analysis/ANR.scala +++ b/src/main/scala/analysis/ANR.scala @@ -9,7 +9,7 @@ import scala.collection.immutable * Calculates the set of variables that are not read after being written up to that point in the program. * Useful for detecting dead stores, constants and which variables are passed as parameters in a function call. */ -trait ANRAnalysis(program: Program) { +trait ANRAnalysis(program: Program, ignoreStackPtrs: Boolean = false) { val powersetLattice: PowersetLattice[Variable] = PowersetLattice() @@ -21,7 +21,7 @@ trait ANRAnalysis(program: Program) { private val linkRegister = Register("R30", 64) private val framePointer = Register("R29", 64) - private val ignoreRegions: Set[Expr] = Set(linkRegister, framePointer, stackPointer) + private val ignoreRegions: Set[Expr] = if (ignoreStackPtrs) then Set(linkRegister, framePointer, stackPointer) else Set() /** Default implementation of eval. */ @@ -32,9 +32,12 @@ trait ANRAnalysis(program: Program) { case assert: Assert => s.diff(assert.body.variables) case memoryStore: MemoryStore => - s.diff(memoryStore.index.variables) + s.diff(memoryStore.index.variables ++ memoryStore.value.variables) case indirectCall: IndirectCall => s - indirectCall.target + case call: DirectCall => + s.diff(call.actualParams.flatMap(_._2.variables).toSet.filterNot(ignoreRegions.contains(_))) + ++ call.outParams.map(_._2).toSet case assign: LocalAssign => val m = s.diff(assign.rhs.variables) if (ignoreRegions.contains(assign.lhs)) { @@ -63,8 +66,8 @@ trait ANRAnalysis(program: Program) { } } -class ANRAnalysisSolver(program: Program) extends ANRAnalysis(program) +class ANRAnalysisSolver(program: Program, ignoreStack : Boolean = true) extends ANRAnalysis(program, ignoreStack) with IRIntraproceduralForwardDependencies with Analysis[Map[CFGPosition, Set[Variable]]] with SimpleWorklistFixpointSolver[CFGPosition, Set[Variable], PowersetLattice[Variable]] { -} \ No newline at end of file +} diff --git a/src/main/scala/analysis/UtilMethods.scala b/src/main/scala/analysis/ExprSSAEval.scala similarity index 65% rename from src/main/scala/analysis/UtilMethods.scala rename to src/main/scala/analysis/ExprSSAEval.scala index be32662f8..95397869a 100644 --- a/src/main/scala/analysis/UtilMethods.scala +++ b/src/main/scala/analysis/ExprSSAEval.scala @@ -1,6 +1,7 @@ package analysis import ir.* import util.Logger +import ir.eval.BitVectorEval /** Evaluate an expression in a hope of finding a global variable. * @@ -12,62 +13,14 @@ import util.Logger * The evaluated expression (e.g. 0x69632) */ def evaluateExpression(exp: Expr, constantPropResult: Map[Variable, FlatElement[BitVecLiteral]]): Option[BitVecLiteral] = { - exp match { - case binOp: BinaryExpr => - val lhs = evaluateExpression(binOp.arg1, constantPropResult) - val rhs = evaluateExpression(binOp.arg2, constantPropResult) - - (lhs, rhs) match { - case (Some(l: BitVecLiteral), Some(r: BitVecLiteral)) => - val result = binOp.op match { - case BVADD => BitVectorEval.smt_bvadd(l, r) - case BVSUB => BitVectorEval.smt_bvsub(l, r) - case BVMUL => BitVectorEval.smt_bvmul(l, r) - case BVUDIV => BitVectorEval.smt_bvudiv(l, r) - case BVSDIV => BitVectorEval.smt_bvsdiv(l, r) - case BVSREM => BitVectorEval.smt_bvsrem(l, r) - case BVUREM => BitVectorEval.smt_bvurem(l, r) - case BVSMOD => BitVectorEval.smt_bvsmod(l, r) - case BVAND => BitVectorEval.smt_bvand(l, r) - case BVOR => BitVectorEval.smt_bvxor(l, r) - case BVXOR => BitVectorEval.smt_bvxor(l, r) - case BVNAND => BitVectorEval.smt_bvnand(l, r) - case BVNOR => BitVectorEval.smt_bvnor(l, r) - case BVXNOR => BitVectorEval.smt_bvxnor(l, r) - case BVSHL => BitVectorEval.smt_bvshl(l, r) - case BVLSHR => BitVectorEval.smt_bvlshr(l, r) - case BVASHR => BitVectorEval.smt_bvashr(l, r) - case BVCOMP => BitVectorEval.smt_bvcomp(l, r) - case BVCONCAT => BitVectorEval.smt_concat(l, r) - case x => throw RuntimeException("Binary operation support not implemented: " + binOp.op) - } - Some(result) - case _ => None - } - case extend: ZeroExtend => - evaluateExpression(extend.body, constantPropResult) match { - case Some(b: BitVecLiteral) => Some(BitVectorEval.smt_zero_extend(extend.extension, b)) - case None => None - } - case extend: SignExtend => - evaluateExpression(extend.body, constantPropResult) match { - case Some(b: BitVecLiteral) => Some(BitVectorEval.smt_sign_extend(extend.extension, b)) - case None => None - } - case e: Extract => - evaluateExpression(e.body, constantPropResult) match { - case Some(b: BitVecLiteral) => Some(BitVectorEval.boogie_extract(e.end, e.start, b)) - case None => None - } - case variable: Variable => - constantPropResult(variable) match { + def value(v: Variable) = constantPropResult(v) match { case FlatEl(value) => Some(value) - case Top => None - case Bottom => None - } - case b: BitVecLiteral => Some(b) - case _ => //throw new RuntimeException("ERROR: CASE NOT HANDLED: " + exp + "\n") - None + case _ => None + } + + ir.eval.evalBVExpr(exp, value) match { + case Right(v) => Some(v) + case Left(_) => None } } @@ -134,9 +87,7 @@ def evaluateExpressionWithSSA(exp: Expr, constantPropResult: Map[RegisterWrapper Logger.debug("getUse: " + getUse(variable, n, reachingDefs)) constantPropResult(RegisterWrapperEqualSets(variable, getUse(variable, n, reachingDefs))) case b: BitVecLiteral => Set(b) - case Repeat(_, body) => evaluateExpressionWithSSA(body, constantPropResult, n, reachingDefs) - case _: UninterpretedFunction => Set.empty - case _ => throw RuntimeException("ERROR: CASE NOT HANDLED: " + exp + "\n") + case _ => Logger.error("ERROR: CASE NOT HANDLED: " + exp + "\n"); Set() } } @@ -173,4 +124,4 @@ def bitVectorOpToBigIntOp(op: BinOp, lhs: BigInt, rhs: BigInt): BigInt = { case BVSUB => lhs - rhs case _ => throw RuntimeException("Binary operation support not implemented: " + op) } -} \ No newline at end of file +} diff --git a/src/main/scala/analysis/GlobalRegionAnalysis.scala b/src/main/scala/analysis/GlobalRegionAnalysis.scala index 2157b4d80..4aad43ba0 100644 --- a/src/main/scala/analysis/GlobalRegionAnalysis.scala +++ b/src/main/scala/analysis/GlobalRegionAnalysis.scala @@ -95,7 +95,9 @@ trait GlobalRegionAnalysis(val program: Program, if (i != n) { val regions: Set[DataRegion] = vsaResult.get(i) match { case Some(Lift(el)) => - el.getOrElse(i.lhs, Set()).flatMap { + // FIXME: not correct for directall + assert(i.assignees.toSet.contains(variable)) + el.getOrElse(variable, Set()).flatMap { case AddressValue(region) => el.getOrElse(region, Set()).flatMap { case AddressValue(dataRegion: DataRegion) => Some(dataRegion) @@ -193,4 +195,4 @@ class GlobalRegionAnalysisSolver( ) extends GlobalRegionAnalysis(program, domain, constantProp, reachingDefs, mmm, vsaResult) with IRIntraproceduralForwardDependencies with Analysis[Map[CFGPosition, Set[DataRegion]]] - with SimpleWorklistFixpointSolver[CFGPosition, Set[DataRegion], PowersetLattice[DataRegion]] \ No newline at end of file + with SimpleWorklistFixpointSolver[CFGPosition, Set[DataRegion], PowersetLattice[DataRegion]] diff --git a/src/main/scala/analysis/InterprocSteensgaardAnalysis.scala b/src/main/scala/analysis/InterprocSteensgaardAnalysis.scala index ea34505f1..f5e6d416c 100644 --- a/src/main/scala/analysis/InterprocSteensgaardAnalysis.scala +++ b/src/main/scala/analysis/InterprocSteensgaardAnalysis.scala @@ -2,7 +2,7 @@ package analysis import analysis.solvers.{Cons, Term, UnionFindSolver, Var} import ir.* -import util.Logger +import util.SteensLogger import scala.collection.mutable /** Wrapper for variables so we can have Steensgaard-specific equals method indirectly @@ -42,7 +42,9 @@ class InterprocSteensgaardAnalysis( ctx.flatMap { i => if (i != n) { vsaResult.get(i) match { - case Some(Lift(el)) => el.get(i.lhs) match { + case Some(Lift(el)) => + assert(i.assignees.contains(variable)) + el.get(variable) match { case Some(values) => values.flatMap { case addressValue: AddressValue => Some(addressValue.region) @@ -140,7 +142,7 @@ class InterprocSteensgaardAnalysis( } private def unify(t1: Term[StTerm], t2: Term[StTerm]): Unit = { - //Logger.info(s"univfying constraint $t1 = $t2\n") + //SteensLogger.info(s"univfying constraint $t1 = $t2\n") solver.unify(t1, t2) // note that unification cannot fail, because there is only one kind of term constructor and no constants } @@ -150,8 +152,8 @@ class InterprocSteensgaardAnalysis( def pointsTo(): Map[RegisterWrapperEqualSets, Set[RegisterWrapperEqualSets | MemoryRegion]] = { val solution = solver.solution() val unifications = solver.unifications() - Logger.debug(s"Solution: \n${solution.mkString(",\n")}\n") - Logger.debug(s"Sets: \n${unifications.values.map { s => s"{ ${s.mkString(",")} }"}.mkString(", ")}") + SteensLogger.debug(s"Solution: \n${solution.mkString(",\n")}\n") + SteensLogger.debug(s"Sets: \n${unifications.values.map { s => s"{ ${s.mkString(",")} }"}.mkString(", ")}") val vars = solution.keys.collect { case id: IdentifierVariable => id } val emptyMap = Map[RegisterWrapperEqualSets, Set[RegisterWrapperEqualSets | MemoryRegion]]() @@ -162,7 +164,7 @@ class InterprocSteensgaardAnalysis( }.toSet a + (v.id -> pt) } - Logger.debug(s"\nPoints-to:\n${pointsto.map((k, v) => s"$k -> { ${v.mkString(",")} }").mkString("\n")}\n") + SteensLogger.debug(s"\nPoints-to:\n${pointsto.map((k, v) => s"$k -> { ${v.mkString(",")} }").mkString("\n")}\n") pointsto } } @@ -219,4 +221,4 @@ case class PointerRef(of: Term[StTerm]) extends StTerm with Cons[StTerm] { def subst(v: Var[StTerm], t: Term[StTerm]): Term[StTerm] = PointerRef(of.subst(v, t)) override def toString: String = s"$of" -} \ No newline at end of file +} diff --git a/src/main/scala/analysis/IntraLiveVarsAnalysis.scala b/src/main/scala/analysis/IntraLiveVarsAnalysis.scala index 1271aa720..46015d9a8 100644 --- a/src/main/scala/analysis/IntraLiveVarsAnalysis.scala +++ b/src/main/scala/analysis/IntraLiveVarsAnalysis.scala @@ -1,7 +1,7 @@ package analysis import analysis.solvers.SimpleWorklistFixpointSolver -import ir.{Assert, Assume, Block, CFGPosition, Call, DirectCall, GoTo, IndirectCall, Jump, LocalAssign, MemoryLoad, MemoryStore, Procedure, Program, Statement, Variable, Return, Unreachable} +import ir.* abstract class LivenessAnalysis(program: Program) extends Analysis[Any] { val lattice: MapLattice[CFGPosition, Set[Variable], PowersetLattice[Variable]] = MapLattice(PowersetLattice()) @@ -9,25 +9,24 @@ abstract class LivenessAnalysis(program: Program) extends Analysis[Any] { def transfer(n: CFGPosition, s: Set[Variable]): Set[Variable] = { n match { - case _: Procedure => s - case _: Block => s - case LocalAssign(variable, expr, _) => (s - variable) ++ expr.variables - case MemoryStore(_, index, value, _, _, _) => s ++ index.variables ++ value.variables - case MemoryLoad(lhs, _, index, _, _, _) => (s - lhs) ++ index.variables - case Assume(expr, _, _, _) => s ++ expr.variables - case Assert(expr, _, _) => s ++ expr.variables - case IndirectCall(variable, _) => s + variable - case _: DirectCall => s - case _: GoTo => s - case _: Return => s - case _: Unreachable => s - case _ => ??? + case p: Procedure => s + case b: Block => s + case a: LocalAssign => (s - a.lhs) ++ a.rhs.variables + case m: MemoryStore => s ++ m.index.variables ++ m.value.variables + case m: MemoryLoad => (s - m.lhs) ++ m.index.variables + case a: Assume => s ++ a.body.variables + case a: Assert => s ++ a.body.variables + case i: IndirectCall => s + i.target + case c: DirectCall => (s -- c.outParams.map(_._2)) ++ c.actualParams.flatMap(_._2.variables) + case g: GoTo => s + case r: Return => s ++ r.outParams.flatMap(_._2.variables) + case r: Unreachable => s + case n: NOP => s } } } - class IntraLiveVarsAnalysis(program: Program) - extends LivenessAnalysis(program) + extends LivenessAnalysis(program) with SimpleWorklistFixpointSolver[CFGPosition, Set[Variable], PowersetLattice[Variable]] with IRIntraproceduralBackwardDependencies diff --git a/src/main/scala/analysis/IrreducibleLoops.scala b/src/main/scala/analysis/IrreducibleLoops.scala index f3d3bb44e..22c6a94ed 100644 --- a/src/main/scala/analysis/IrreducibleLoops.scala +++ b/src/main/scala/analysis/IrreducibleLoops.scala @@ -2,7 +2,7 @@ package analysis import ir.{CFGPosition, Command, IntraProcIRCursor, Program, Procedure, Block, GoTo, IRWalk} import util.intrusive_list.IntrusiveList -import util.Logger +import util.StaticAnalysisLogger import scala.collection.mutable @@ -325,8 +325,8 @@ class LoopTransform(loops: Set[Loop]) { case _ => } case _ => - Logger.error("Unexpected loop originating node - 1") - Logger.error(origNode) + StaticAnalysisLogger.error("Unexpected loop originating node - 1") + StaticAnalysisLogger.error(origNode) } } @@ -353,11 +353,11 @@ class LoopTransform(loops: Set[Loop]) { case _ => } case _ => - Logger.error("Unexpected loop originating node - 1") - Logger.error(origNode) + StaticAnalysisLogger.error("Unexpected loop originating node - 1") + StaticAnalysisLogger.error(origNode) } } newLoop } -} \ No newline at end of file +} diff --git a/src/main/scala/analysis/Lattice.scala b/src/main/scala/analysis/Lattice.scala index 415bc6ddd..8d57c85a7 100644 --- a/src/main/scala/analysis/Lattice.scala +++ b/src/main/scala/analysis/Lattice.scala @@ -1,9 +1,9 @@ package analysis import ir._ -import analysis.BitVectorEval +import ir.eval.BitVectorEval import math.pow -import util.Logger +import util.StaticAnalysisLogger /** Basic lattice */ @@ -294,6 +294,7 @@ class ValueSetLattice[T] extends Lattice[ValueSet[T]] { op match case bvOp: BVBinOp => bvOp match + case BVSADDO => ??? case BVAND => ??? case BVOR => ??? case BVADD => rhs match @@ -349,6 +350,7 @@ class ValueSetLattice[T] extends Lattice[ValueSet[T]] { case boolOp: BoolUnOp => boolOp match case BoolNOT => ??? + case BoolToBV1 => ??? case intOp: IntUnOp => applyOp(intOp.toBV, rhs) case _ => ??? @@ -684,7 +686,7 @@ class ConstantPropagationLattice extends FlatLattice[BitVecLiteral] { case (Top, _) => Top } catch { case e: Exception => - Logger.error(s"Failed on op $op with $a and $b") + StaticAnalysisLogger.error(s"Failed on op $op with $a and $b") throw e } @@ -766,4 +768,4 @@ class ConstantPropagationLatticeWithSSA extends PowersetLattice[BitVecLiteral] { apply(BitVectorEval.boogie_extract(high, low, _: BitVecLiteral), a) def concat(a: Set[BitVecLiteral], b: Set[BitVecLiteral]): Set[BitVecLiteral] = apply(BitVectorEval.smt_concat, a, b) -} \ No newline at end of file +} diff --git a/src/main/scala/analysis/MemoryModelMap.scala b/src/main/scala/analysis/MemoryModelMap.scala index 0df82014c..ccd98a2d6 100644 --- a/src/main/scala/analysis/MemoryModelMap.scala +++ b/src/main/scala/analysis/MemoryModelMap.scala @@ -2,7 +2,7 @@ package analysis import analysis.* import ir.* -import util.Logger +import util.MRALogger import scala.collection.immutable.TreeMap import scala.collection.mutable @@ -157,7 +157,7 @@ class MemoryModelMap(val globalOffsets: Map[BigInt, BigInt]) { for (dr <- oldRegions) { val obj = findDataObject(dr.start) if (obj.isEmpty) { - Logger.debug(s"Data region $dr not found in the new data map") + MRALogger.debug(s"Data region $dr not found in the new data map") } else { val isRelocated = relocatedDataRegion(dr.start) if (isRelocated.isDefined) { @@ -284,34 +284,34 @@ class MemoryModelMap(val globalOffsets: Map[BigInt, BigInt]) { case _: HeapRegion => " " case _: DataRegion => " " } - Logger.debug(s"$spacing$range -> $region") + MRALogger.debug(s"$spacing$range -> $region") if content.contains(region) then if content.contains(region) then for value <- content(region) do - Logger.debug(s"$spacing $value") + MRALogger.debug(s"$spacing $value") } - Logger.debug("Stack:") + MRALogger.debug("Stack:") for name <- localStacks.keys do popContext() pushContext(name) - Logger.debug(s" Function: $name") - if stackMap.nonEmpty then Logger.debug(s" Local:") + MRALogger.debug(s" Function: $name") + if stackMap.nonEmpty then MRALogger.debug(s" Local:") // must sort by ranges for ((range, region) <- stackMap) { logRegion(range, region) } - if sharedStackMap.nonEmpty then Logger.debug(s" Shared:") + if sharedStackMap.nonEmpty then MRALogger.debug(s" Shared:") for ((parent, treeMap) <- sharedStackMap) { - Logger.debug(s" Parent: ${parent.name}") + MRALogger.debug(s" Parent: ${parent.name}") for ((range, region) <- treeMap) { logRegion(range, region, true) } } - Logger.debug("Stack Union-Find Roots:") + MRALogger.debug("Stack Union-Find Roots:") for name <- localStacks.keys do popContext() pushContext(name) - Logger.debug(s" Function: $name") + MRALogger.debug(s" Function: $name") var parentCount = 0 // get root regions for ((range, region) <- stackMap) { @@ -320,17 +320,17 @@ class MemoryModelMap(val globalOffsets: Map[BigInt, BigInt]) { logRegion(range, root) parentCount += 1 } - if parentCount == 0 then Logger.debug(" No root regions") else Logger.debug(s" Parents: $parentCount/${stackMap.size}") - Logger.debug("Shared Stacks:") + if parentCount == 0 then MRALogger.debug(" No root regions") else MRALogger.debug(s" Parents: $parentCount/${stackMap.size}") + MRALogger.debug("Shared Stacks:") for (name, sharedStacks) <- sharedStacks do - Logger.debug(s" Function: $name") + MRALogger.debug(s" Function: $name") for region <- sharedStacks do - Logger.debug(s" $region") - Logger.debug("Heap:") + MRALogger.debug(s" $region") + MRALogger.debug("Heap:") for ((range, region) <- heapMap) { logRegion(range, region) } - Logger.debug("Data:") + MRALogger.debug("Data:") for ((range, region) <- dataMap) { logRegion(range, region) } @@ -441,4 +441,4 @@ class UnionFind { } } -} \ No newline at end of file +} diff --git a/src/main/scala/analysis/MemoryRegionAnalysis.scala b/src/main/scala/analysis/MemoryRegionAnalysis.scala index d40957842..e498ec3a2 100644 --- a/src/main/scala/analysis/MemoryRegionAnalysis.scala +++ b/src/main/scala/analysis/MemoryRegionAnalysis.scala @@ -1,9 +1,9 @@ package analysis -import analysis.BitVectorEval.bv2SignedInt +import ir.eval.BitVectorEval.bv2SignedInt import analysis.solvers.SimpleWorklistFixpointSolver import ir.* -import util.Logger +import util.MRALogger import scala.collection.mutable import scala.collection.mutable.ListBuffer @@ -59,8 +59,8 @@ trait MemoryRegionAnalysis(val program: Program, } private def stackDetection(stmt: Statement): Unit = { - Logger.debug("Stack detection") - Logger.debug(spList) + MRALogger.debug("Stack detection") + MRALogger.debug(spList) stmt match { case assign: LocalAssign => if (spList.contains(assign.rhs)) { @@ -174,7 +174,7 @@ trait MemoryRegionAnalysis(val program: Program, Set.empty // we cannot evaluate this to a concrete value, we need VSA for this case _ => - Logger.debug(s"type: ${exp.getClass} $exp\n") + MRALogger.debug(s"type: ${exp.getClass} $exp\n") throw new Exception("Unknown type") } } @@ -253,4 +253,4 @@ class MemoryRegionAnalysisSolver( ) extends MemoryRegionAnalysis(program, domain, globals, globalOffsets, subroutines, constantProp, ANRResult, RNAResult, reachingDefs, graResult, mmm) with IRIntraproceduralForwardDependencies with Analysis[Map[CFGPosition, Set[StackRegion]]] - with SimpleWorklistFixpointSolver[CFGPosition, Set[StackRegion], PowersetLattice[StackRegion]] \ No newline at end of file + with SimpleWorklistFixpointSolver[CFGPosition, Set[StackRegion], PowersetLattice[StackRegion]] diff --git a/src/main/scala/analysis/RNA.scala b/src/main/scala/analysis/RNA.scala index e15dbe914..3dc3e0f7b 100644 --- a/src/main/scala/analysis/RNA.scala +++ b/src/main/scala/analysis/RNA.scala @@ -10,7 +10,7 @@ import scala.collection.immutable * This helps to identify the set of variables that are read from memory before they have been initialised. * This could be used on callee side to identify what parameters where passed to the function. */ -trait RNAAnalysis(program: Program) { +trait RNAAnalysis(program: Program, ignoreStackPtrs: Boolean = true) { val powersetLattice: PowersetLattice[Variable] = PowersetLattice() @@ -32,6 +32,9 @@ trait RNAAnalysis(program: Program) { s ++ (assert.body.variables -- ignoreRegions) case memoryStore: MemoryStore => s ++ (memoryStore.index.variables -- ignoreRegions) + case call: DirectCall=> + (s ++ call.actualParams.flatMap(_._2.variables).toSet.filterNot(ignoreRegions.contains(_))) + .diff(call.outParams.map(_._2).toSet) case indirectCall: IndirectCall => if (ignoreRegions.contains(indirectCall.target)) { s @@ -62,4 +65,4 @@ trait RNAAnalysis(program: Program) { class RNAAnalysisSolver(program: Program) extends RNAAnalysis(program) with IRIntraproceduralBackwardDependencies with Analysis[Map[CFGPosition, Set[Variable]]] - with SimpleWorklistFixpointSolver[CFGPosition, Set[Variable], PowersetLattice[Variable]] \ No newline at end of file + with SimpleWorklistFixpointSolver[CFGPosition, Set[Variable], PowersetLattice[Variable]] diff --git a/src/main/scala/analysis/ReachingDefinitionsAnalysis.scala b/src/main/scala/analysis/ReachingDefinitionsAnalysis.scala index 07fe443d2..455f4554e 100644 --- a/src/main/scala/analysis/ReachingDefinitionsAnalysis.scala +++ b/src/main/scala/analysis/ReachingDefinitionsAnalysis.scala @@ -65,6 +65,10 @@ trait ReachingDefinitionsAnalysis(program: Program) { transformUses(assume.body.variables, s) case indirectCall: IndirectCall => transformUses(indirectCall.target.variables, s) + case r: DirectCall => + transformUses(r.actualParams.toSet.flatMap(_._2.variables), s) + case r: Return => + transformUses(r.outParams.toSet.flatMap(_._2.variables), s) case _ => s } } @@ -72,4 +76,4 @@ trait ReachingDefinitionsAnalysis(program: Program) { class InterprocReachingDefinitionsAnalysisSolver(program: Program) extends ReachingDefinitionsAnalysis(program) with SimpleWorklistFixpointSolver[CFGPosition, (Map[Variable, Set[Assign]], Map[Variable, Set[Assign]]), TupleElement] - with IRInterproceduralForwardDependencies \ No newline at end of file + with IRInterproceduralForwardDependencies diff --git a/src/main/scala/analysis/ReachingDefs.scala b/src/main/scala/analysis/ReachingDefs.scala index f60e9ffc6..5931f6f3f 100644 --- a/src/main/scala/analysis/ReachingDefs.scala +++ b/src/main/scala/analysis/ReachingDefs.scala @@ -15,9 +15,9 @@ abstract class ReachingDefs(program: Program, writesTo: Map[Procedure, Set[Regis s + (loc.lhs -> Set(n)) case load: MemoryLoad => s + (load.lhs -> Set(n)) - case DirectCall(target, _) if target.name == "malloc" => + case DirectCall(target, _, _, _) if target.name == "malloc" => s + (mallocRegister -> Set(n)) - case DirectCall(target, _) if writesTo.contains(target) => + case DirectCall(target, _, _, _) if writesTo.contains(target) => val result: Map[Variable, Set[CFGPosition]] = writesTo(target).foldLeft(Map[Variable, Set[CFGPosition]]()) { (m, register) => m + (register -> Set(n)) diff --git a/src/main/scala/analysis/RegionInjector.scala b/src/main/scala/analysis/RegionInjector.scala index c2199ed8c..2bb3fcaa3 100644 --- a/src/main/scala/analysis/RegionInjector.scala +++ b/src/main/scala/analysis/RegionInjector.scala @@ -2,7 +2,6 @@ package analysis import analysis.data_structure_analysis.{AddressRange, Cell, Graph, Slice} import ir.* -import util.Logger import scala.collection.mutable import scala.collection.mutable.ArrayBuffer @@ -307,4 +306,4 @@ class RegionInjectorDSA(override val program: Program, DSATopDown: mutable.Map[P mergedRegions.values.filter(region => !region.stack) } -} \ No newline at end of file +} diff --git a/src/main/scala/analysis/SummaryGenerator.scala b/src/main/scala/analysis/SummaryGenerator.scala index 1a878e21d..7acec3e4c 100644 --- a/src/main/scala/analysis/SummaryGenerator.scala +++ b/src/main/scala/analysis/SummaryGenerator.scala @@ -3,8 +3,7 @@ package analysis import analysis.* import ir.* import boogie.* -import util.Logger -import specification.SpecGlobal +import boogie.SpecGlobal /** * A temporary copy of RNA analysis which works on Taintables. @@ -180,4 +179,4 @@ class SummaryGenerator( } } } -} \ No newline at end of file +} diff --git a/src/main/scala/analysis/TaintAnalysis.scala b/src/main/scala/analysis/TaintAnalysis.scala index d18e27d48..c4872b0c0 100644 --- a/src/main/scala/analysis/TaintAnalysis.scala +++ b/src/main/scala/analysis/TaintAnalysis.scala @@ -3,7 +3,6 @@ package analysis import analysis.solvers.ForwardIDESolver import ir.* import boogie.* -import util.Logger /** * A value which can propogate taint/be tainted. diff --git a/src/main/scala/analysis/VSA.scala b/src/main/scala/analysis/VSA.scala index 4a9f11a14..5b50b9f7a 100644 --- a/src/main/scala/analysis/VSA.scala +++ b/src/main/scala/analysis/VSA.scala @@ -7,7 +7,8 @@ import scala.collection.mutable.{ArrayBuffer, HashMap, ListBuffer} import java.io.{File, PrintWriter} import scala.collection.mutable import scala.collection.immutable -import util.Logger +import util.VSALogger + /** ValueSets are PowerSet of possible values */ trait Value @@ -68,7 +69,7 @@ trait ValueSetAnalysis(program: Program, case Some(v: Variable) => s + (localAssign.lhs -> s(v)) case None => - Logger.debug(s"Too Complex: ${localAssign.rhs}") // do nothing + VSALogger.debug(s"Too Complex: ${localAssign.rhs}") // do nothing s } } @@ -84,7 +85,7 @@ trait ValueSetAnalysis(program: Program, case Some(v: Variable) => s + (load.lhs -> s(v)) case None => - Logger.debug(s"Too Complex: ${load.index}") // do nothing + VSALogger.debug(s"Too Complex: ${load.index}") // do nothing s } } @@ -105,7 +106,7 @@ trait ValueSetAnalysis(program: Program, case Some(v: Variable) => s ++ regions.map(r => r -> s(v)) case None => - Logger.debug(s"Too Complex: $store.value") // do nothing + VSALogger.debug(s"Too Complex: $store.value") // do nothing s } } diff --git a/src/main/scala/analysis/VariableDependencyAnalysis.scala b/src/main/scala/analysis/VariableDependencyAnalysis.scala index 643bf5b6f..9ae4fa498 100644 --- a/src/main/scala/analysis/VariableDependencyAnalysis.scala +++ b/src/main/scala/analysis/VariableDependencyAnalysis.scala @@ -3,8 +3,8 @@ package analysis import analysis.solvers.ForwardIDESolver import ir.* import boogie.* -import util.Logger -import specification.SpecGlobal +import util.StaticAnalysisLogger +import boogie.SpecGlobal import scala.collection.mutable @@ -122,7 +122,7 @@ class VariableDependencyAnalysis( var varDepsSummariesTransposed = Map[Procedure, Map[Taintable, Set[Taintable]]]() scc.flatten.filter(_.blocks.nonEmpty).foreach { procedure => { - Logger.debug("Generating variable dependencies for " + procedure) + StaticAnalysisLogger.debug("Generating variable dependencies for " + procedure) val varDepResults = ProcVariableDependencyAnalysis(program, varDepVariables, globals, constProp, varDepsSummariesTransposed, procedure).analyze() val varDepMap = varDepResults.getOrElse(IRWalk.lastInProc(procedure).getOrElse(procedure), Map()) varDepsSummaries += procedure -> varDepMap diff --git a/src/main/scala/analysis/WriteToAnalysis.scala b/src/main/scala/analysis/WriteToAnalysis.scala index cc4e3eba2..52e0142ac 100644 --- a/src/main/scala/analysis/WriteToAnalysis.scala +++ b/src/main/scala/analysis/WriteToAnalysis.scala @@ -30,10 +30,10 @@ class WriteToAnalysis(program: Program) extends Analysis[Map[Procedure, Set[Regi writtenTo.add(variable) case MemoryLoad(lhs: Register, _, _, _, _, _) if paramRegisters.contains(lhs) => writtenTo.add(lhs) - case DirectCall(target, _) if target.name == "malloc" => + case DirectCall(target, _, _, _) if target.name == "malloc" => writtenTo.add(mallocRegister) - case DirectCall(target, _) if program.procedures.contains(target) => - writtenTo.addAll(getWritesTos(target)) + case d: DirectCall if program.procedures.contains(d.target) => + writtenTo.addAll(getWritesTos(d.target)) case _ => } } diff --git a/src/main/scala/analysis/data_structure_analysis/DataStructureAnalysis.scala b/src/main/scala/analysis/data_structure_analysis/DataStructureAnalysis.scala index 29d1a7bbe..2877dc1f1 100644 --- a/src/main/scala/analysis/data_structure_analysis/DataStructureAnalysis.scala +++ b/src/main/scala/analysis/data_structure_analysis/DataStructureAnalysis.scala @@ -2,7 +2,8 @@ package analysis.data_structure_analysis import analysis.* import ir.* -import specification.{ExternalFunction, SpecGlobal, SymbolTableEntry} +import specification.{ExternalFunction, SymbolTableEntry} +import boogie.SpecGlobal import scala.collection.mutable diff --git a/src/main/scala/analysis/data_structure_analysis/Graph.scala b/src/main/scala/analysis/data_structure_analysis/Graph.scala index e263625d6..fe45b50dc 100644 --- a/src/main/scala/analysis/data_structure_analysis/Graph.scala +++ b/src/main/scala/analysis/data_structure_analysis/Graph.scala @@ -5,7 +5,8 @@ import analysis.solvers.DSAUnionFindSolver import analysis.evaluateExpression import cfg_visualiser.* import ir.* -import specification.{ExternalFunction, FuncEntry, SpecGlobal, SymbolTableEntry} +import specification.{ExternalFunction, SymbolTableEntry} +import boogie.{FuncEntry, SpecGlobal} import scala.collection.mutable import scala.collection.mutable.ArrayBuffer @@ -787,10 +788,10 @@ class Graph(val proc: Procedure, } val node = Node(Some(this)) varToCell(pos) = mutable.Map(lhs -> Slice(node.cells(0), 0)) - case pos @ DirectCall(target, _) if target.name == "malloc" => + case pos @ DirectCall(target, _, _, _) if target.name == "malloc" => val node = Node(Some(this)) varToCell(pos) = mutable.Map(mallocRegister -> Slice(node.cells(0), 0)) - case pos @ DirectCall(target, _) if writesTo.contains(target) => + case pos @ DirectCall(target, _, _, _) if writesTo.contains(target) => val result = mutable.Map[Variable, Slice]() writesTo(target).foreach { variable => val node = Node(Some(this)) diff --git a/src/main/scala/analysis/data_structure_analysis/LocalPhase.scala b/src/main/scala/analysis/data_structure_analysis/LocalPhase.scala index 9091fb5fd..c8f6b0d96 100644 --- a/src/main/scala/analysis/data_structure_analysis/LocalPhase.scala +++ b/src/main/scala/analysis/data_structure_analysis/LocalPhase.scala @@ -1,9 +1,10 @@ package analysis.data_structure_analysis -import analysis.BitVectorEval.{bv2SignedInt, isNegative} +import ir.eval.BitVectorEval.{bv2SignedInt, isNegative} import analysis.* import ir.* -import specification.{ExternalFunction, SpecGlobal, SymbolTableEntry} +import boogie.SpecGlobal +import specification.{ExternalFunction, SymbolTableEntry} import util.writeToFile import java.math.BigInteger @@ -221,7 +222,7 @@ class LocalPhase(proc: Procedure, else visited.add(n) n match - case DirectCall(target, _) if target.name == "malloc" => // R0 = Malloc() + case DirectCall(target, _, _, _) if target.name == "malloc" => // R0 = Malloc() val size: BigInt = evaluateExpression(mallocRegister, constProp(n)) match case Some(value) => value.value case None => 0 diff --git a/src/main/scala/analysis/data_structure_analysis/SymbolicAddressAnalysis.scala b/src/main/scala/analysis/data_structure_analysis/SymbolicAddressAnalysis.scala index 737f6e410..d55990a4e 100644 --- a/src/main/scala/analysis/data_structure_analysis/SymbolicAddressAnalysis.scala +++ b/src/main/scala/analysis/data_structure_analysis/SymbolicAddressAnalysis.scala @@ -1,6 +1,6 @@ package analysis.data_structure_analysis -import analysis.BitVectorEval.{bv2SignedInt, isNegative} +import ir.eval.BitVectorEval.{bv2SignedInt, isNegative} import analysis.solvers.ForwardIDESolver import analysis.* import ir.* @@ -148,7 +148,7 @@ trait SymbolicAddressFunctions(constProp: Map[CFGPosition, Map[Variable, FlatEle case Left(value) if value.accessor == lhs => Map() case Left(_) => Map(d -> IdEdge()) case Right(_) => Map(d -> IdEdge(), Left(SymbolicAddress(lhs, UnknownLocation(nextunknownCount, IRWalk.procedure(n)), 0)) -> ConstEdge(TwoElementTop)) - case DirectCall(target, _) if target.name == "malloc" => + case DirectCall(target, _, _, _) if target.name == "malloc" => d match case Left(value) if value.accessor == mallocVariable => Map() case Left(_) => Map(d -> IdEdge()) @@ -157,7 +157,7 @@ trait SymbolicAddressFunctions(constProp: Map[CFGPosition, Map[Variable, FlatEle case Some(value) => value.value case None => -1 Map(d -> IdEdge(), Left(SymbolicAddress(mallocVariable, HeapLocation(nextMallocCount, IRWalk.procedure(n), size), 0)) -> ConstEdge(TwoElementTop)) - case DirectCall(target, _) if target.returnBlock.isEmpty => // for when calls are non returning, kills the stack dataflow facts + case DirectCall(target, _, _, _) if target.returnBlock.isEmpty => // for when calls are non returning, kills the stack dataflow facts d match case Left(value) => value.symbolicBase match @@ -168,4 +168,4 @@ trait SymbolicAddressFunctions(constProp: Map[CFGPosition, Map[Variable, FlatEle } class SymbolicAddressAnalysis(program: Program, constProp: Map[CFGPosition, Map[Variable, FlatElement[BitVecLiteral]]]) - extends ForwardIDESolver[SymbolicAddress, TwoElement, TwoElementLattice](program), SymbolicAddressFunctions(constProp) \ No newline at end of file + extends ForwardIDESolver[SymbolicAddress, TwoElement, TwoElementLattice](program), SymbolicAddressFunctions(constProp) diff --git a/src/main/scala/analysis/data_structure_analysis/Utility.scala b/src/main/scala/analysis/data_structure_analysis/Utility.scala index 170a6dc0c..fdfeaf50b 100644 --- a/src/main/scala/analysis/data_structure_analysis/Utility.scala +++ b/src/main/scala/analysis/data_structure_analysis/Utility.scala @@ -4,7 +4,8 @@ import analysis.solvers.{DSAUniTerm, DSAUnionFindSolver, UnionFindSolver, Var} import analysis.* import cfg_visualiser.{DotStruct, DotStructElement, StructArrow, StructDotGraph} import ir.* -import specification.{ExternalFunction, SpecGlobal, SymbolTableEntry} +import specification.{ExternalFunction, SymbolTableEntry} +import boogie.SpecGlobal import util.Logger import scala.collection.mutable @@ -259,4 +260,4 @@ def unwrapPaddingAndSlicing(expr: Expr): Expr = case variable: Variable => variable case Extract(_, _, body) /*if start == 0 && end == 32*/ => unwrapPaddingAndSlicing(body) // this may make it unsound case ZeroExtend(_, body) => unwrapPaddingAndSlicing(body) - case _ => expr \ No newline at end of file + case _ => expr diff --git a/src/main/scala/bap/BAPProgram.scala b/src/main/scala/bap/BAPProgram.scala index ed1f65400..095d206f4 100644 --- a/src/main/scala/bap/BAPProgram.scala +++ b/src/main/scala/bap/BAPProgram.scala @@ -45,6 +45,8 @@ case class BAPBlock(label: String, address: Option[BigInt], statements: List[BAP } -case class BAPParameter(name: String, size: Int, value: BAPVar) +case class BAPParameter(name: String, size: Int, value: BAPVar) { + +} case class BAPMemorySection(name: String, address: BigInt, size: Int, bytes: Seq[BAPLiteral]) diff --git a/src/main/scala/boogie/BCmd.scala b/src/main/scala/boogie/BCmd.scala index 63a1d19d9..cc1b4b929 100644 --- a/src/main/scala/boogie/BCmd.scala +++ b/src/main/scala/boogie/BCmd.scala @@ -101,7 +101,7 @@ case class GoToCmd(destinations: Seq[String], comment: Option[String] = None) ex case object ReturnCmd extends BCmd { override def comment: Option[String] = None - override def toString: String = "return;" + override def toString: String = s"return;" } case class Comment(actualComment: String) extends BCmd { diff --git a/src/main/scala/boogie/BExpr.scala b/src/main/scala/boogie/BExpr.scala index d334d6800..fcc89a473 100644 --- a/src/main/scala/boogie/BExpr.scala +++ b/src/main/scala/boogie/BExpr.scala @@ -5,7 +5,7 @@ import collection.mutable import java.io.Writer -trait BExpr { +sealed trait BExpr { def getType: BType def functionOps: Set[FunctionOp] = Set() def locals: Set[BVar] = Set() @@ -229,6 +229,7 @@ case class BFunctionCall(name: String, args: List[BExpr], outType: BType, uninte case class UnaryBExpr(op: UnOp, arg: BExpr) extends BExpr { override def getType: BType = (op, arg.getType) match { + case (BoolToBV1, BoolBType) => BitVecBType(1) case (_: BoolUnOp, BoolBType) => BoolBType case (_: BVUnOp, bv: BitVecBType) => bv case (_: IntUnOp, IntBType) => IntBType @@ -241,6 +242,7 @@ case class UnaryBExpr(op: UnOp, arg: BExpr) extends BExpr { } override def toString: String = op match { + case BoolToBV1 => s"$op($arg)" case uOp: BoolUnOp => s"($uOp$arg)" case uOp: BVUnOp => s"bv$uOp$inSize($arg)" case uOp: IntUnOp => s"($uOp$arg)" @@ -248,6 +250,7 @@ case class UnaryBExpr(op: UnOp, arg: BExpr) extends BExpr { override def functionOps: Set[FunctionOp] = { val thisFn = op match { + case b @ BoolToBV1 => Set(BoolToBV1Op(arg)) case b: BVUnOp => Set(BVFunctionOp(s"bv$b$inSize", s"bv$b", List(BParam(arg.getType)), BParam(getType))) case _ => Set() @@ -285,7 +288,7 @@ case class BinaryBExpr(op: BinOp, arg1: BExpr, arg2: BExpr) extends BExpr { } else { throw new Exception(s"bitvector size mismatch: $arg1, $arg2") } - case BVULT | BVULE | BVUGT | BVUGE | BVSLT | BVSLE | BVSGT | BVSGE => + case BVULT | BVULE | BVUGT | BVUGE | BVSLT | BVSLE | BVSGT | BVSGE | BVSADDO => if (bv1.size == bv2.size) { BoolBType } else { @@ -460,6 +463,7 @@ case class BVFunctionOp(name: String, bvbuiltin: String, in: List[BVar], out: BV case class MemoryLoadOp(addressSize: Int, valueSize: Int, endian: Endian, bits: Int) extends FunctionOp { val accesses: Int = bits / valueSize + assert(accesses > 0) val fnName: String = endian match { case Endian.LittleEndian => s"memory_load${bits}_le" @@ -537,7 +541,7 @@ case class BInBounds(base: BExpr, len: BExpr, endian: Endian, i: BExpr) extends case _ => throw new Exception(s"InBounds does not have Bitvector type: $this") } - val fnName: String = s"in_bounds${baseSize}" + val fnName: String = s"in_bounds${baseSize}_${if endian == Endian.LittleEndian then "le" else "be"}" override val getType: BType = BoolBType override def functionOps: Set[FunctionOp] = @@ -548,8 +552,14 @@ case class BInBounds(base: BExpr, len: BExpr, endian: Endian, i: BExpr) extends override def loads: Set[BExpr] = base.loads ++ len.loads ++ i.loads } + +case class BoolToBV1Op(arg: BExpr) extends FunctionOp { + val fnName: String = "bool2bv1" +} + case class BMemoryLoad(memory: BMapVar, index: BExpr, endian: Endian, bits: Int) extends BExpr { override def toString: String = s"$fnName($memory, $index)" + assert(bits >= 8) val fnName: String = endian match { case Endian.LittleEndian => s"memory_load${bits}_le" @@ -660,3 +670,50 @@ case class L(memories: List[BMapVar], index: BExpr) extends BExpr { override def params: Set[BVar] = index.params ++ memories.flatMap(_.params) override def loads: Set[BExpr] = index.loads } + +/** spec **/ + +trait SpecVar extends BExpr { + val address: BigInt + override def getType: BType = { + throw new Exception("getType called on SpecVar") + } +} + +trait SpecGlobalOrAccess extends SpecVar with Ordered[SpecGlobalOrAccess] { + val toAddrVar: BExpr + val toOldVar: BVar + val toOldGamma: BVar + val size: Int + + def compare(that: SpecGlobalOrAccess): Int = address.compare(that.address) +} + +case class FuncEntry(override val name: String, override val size: Int, override val address: BigInt) extends SymbolTableEntry + +case class SpecGlobal(override val name: String, override val size: Int, arraySize: Option[Int], override val address: BigInt) + extends SymbolTableEntry, SpecGlobalOrAccess { + override def specGlobals: Set[SpecGlobalOrAccess] = Set(this) + override val toAddrVar: BVar = BVariable("$" + s"${name}_addr", BitVecBType(64), Scope.Const) + override val toOldVar: BVar = BVariable(s"${name}_old", BitVecBType(size), Scope.Local) + override val toOldGamma: BVar = BVariable(s"Gamma_${name}_old", BoolBType, Scope.Local) + val toAxiom: BAxiom = BAxiom(BinaryBExpr(BoolEQ, toAddrVar, BitVecBLiteral(address, 64)), List.empty) + override def acceptVisit(visitor: BVisitor): BExpr = visitor.visitSpecGlobal(this) +} + +case class SpecGamma(global: SpecGlobal) extends SpecVar { + override val address = global.address + val size = global.size + override def acceptVisit(visitor: BVisitor): BExpr = visitor.visitSpecGamma(this) +} + +case class ArrayAccess(global: SpecGlobal, index: Int) extends SpecGlobalOrAccess { + val offset = index * (global.size / 8) + override val address = global.address + offset + override val size: Int = global.size + override val toOldVar: BVar = BVariable(s"${global.name}$$${index}_old", BitVecBType(global.size), Scope.Local) + override val toAddrVar: BExpr = BinaryBExpr(BVADD, global.toAddrVar, BitVecBLiteral(offset, 64)) + override val toOldGamma: BVar = BVariable(s"Gamma_${global.name}$$${index}_old", BoolBType, Scope.Local) + override def specGlobals: Set[SpecGlobalOrAccess] = Set(this) + override def acceptVisit(visitor: BVisitor): BExpr = visitor.visitArrayAccess(this) +} diff --git a/src/main/scala/boogie/BProgram.scala b/src/main/scala/boogie/BProgram.scala index b8e276bf6..2ca25e54c 100644 --- a/src/main/scala/boogie/BProgram.scala +++ b/src/main/scala/boogie/BProgram.scala @@ -55,8 +55,8 @@ case class BProcedure( val ensuresStrs = ensures.map(e => s" ensures $e;") ++ ensuresDirect.map(e => s" ensures $e;") val freeRequiresStrs = freeRequires.map(r => s" free requires $r;") val freeEnsuresStrs = freeEnsures.map(e => s" free ensures $e;") - val locals = body.flatMap(l => l.locals).distinct.sorted - val localDefs = locals.map(l => " " + BVarDecl(l).toString) + val locals : Set[BVar] = (body.flatMap(l => l.locals).toSet) -- (in.toSet ++ out.toSet) + val localDefs = locals.toList.sorted.map(l => " " + BVarDecl(l).toString) val bodyStr = if (body.nonEmpty) { List("{") ++ localDefs ++ body.flatMap(x => x.toBoogie).map(s => " " + s) ++ List("}") } else { diff --git a/src/main/scala/boogie/BVisitor.scala b/src/main/scala/boogie/BVisitor.scala index 0ea93e65a..f88bcd5a4 100644 --- a/src/main/scala/boogie/BVisitor.scala +++ b/src/main/scala/boogie/BVisitor.scala @@ -2,7 +2,7 @@ package boogie import analysis.RegionInjector import ir.{Endian, IntBinOp, IntUnOp} -import specification.{ArrayAccess, SpecGamma, SpecGlobal} +import boogie.{ArrayAccess, SpecGamma, SpecGlobal} trait BVisitor { def visitBExpr(node: BExpr): BExpr = node.acceptVisit(this) @@ -260,4 +260,4 @@ object ResolveSpecInvOld extends SpecResolutionVisitor { ) } -} \ No newline at end of file +} diff --git a/src/main/scala/cfg_visualiser/DotTools.scala b/src/main/scala/cfg_visualiser/DotTools.scala index d89cf8333..d4542f04d 100644 --- a/src/main/scala/cfg_visualiser/DotTools.scala +++ b/src/main/scala/cfg_visualiser/DotTools.scala @@ -11,21 +11,34 @@ object IDGenerator { } } -def wrap(input: String, width: Integer = 20): String = +def wrap(_input: String, width: Integer = 20, first : Boolean = true): String = + var input = _input + + def cannotSplit(c:Char) = { + c.isLetterOrDigit || ("_$".contains(c)) + } + if (input.length() <= width) { - input + input.replace("\n", "\\l") + "\\l" + } else if ({ + val index = input.indexOf('\n') + index != -1 && index <= width + }) { + var splitPoint = input.indexOf('\n') + val (line, rest) = (input.substring(0, splitPoint).replace("\n", "\\l"), input.substring(splitPoint + 1)) + (if (!first) then " " else "") + line + "\\l" + wrap(rest, width=width, true) } else { var splitPoint = width - while (input.charAt(splitPoint).isLetterOrDigit && splitPoint > width / 2) { + while (cannotSplit(input.charAt(splitPoint)) && splitPoint > width / 3) { // search backwards for a non alphanumeric charcter to split on splitPoint -= 1 } - if (input.charAt(splitPoint).isLetterOrDigit) { + if (cannotSplit(input.charAt(splitPoint))) { // didn't find a character to split on splitPoint = width } - val line = input.substring(0, splitPoint) - line + "\\l" + wrap(input.substring(splitPoint), width) + val (line, rest) = (input.substring(0, splitPoint).replace("\n", "\\l"), input.substring(splitPoint)) + (if (!first) then " " else "") + line + "\\l" + wrap(rest, width=width, false) } @@ -51,7 +64,7 @@ class DotNode(val id: String, val label: String) extends DotElement { override def toString: String = toDotString def toDotString: String = - s"\"$id\"" + "[label=\"" + wrap(label, 80) + "\"]" + s"\"$id\"" + "[label=\"" + wrap(label, 100) + "\", shape=\"box\", fontname=\"Mono\", fontsize=\"5\"]" } @@ -134,7 +147,8 @@ class DotGraph(val title: String, val nodes: Iterable[DotNode], val edges: Itera override def toString: String = toDotString - def toDotString: String = "digraph " + title + " {\n" + (nodes ++ edges).foldLeft("")((str, elm) => str + elm.toDotString + "\n") + "}" + val graph = "graph [ fontsize=18 ];" + def toDotString: String = "digraph " + title + " {\n" + graph + "\n" + (nodes ++ edges).foldLeft("")((str, elm) => str + elm.toDotString + "\n") + "}" } diff --git a/src/main/scala/ir/Expr.scala b/src/main/scala/ir/Expr.scala index d250ca9fc..6ab939a4b 100644 --- a/src/main/scala/ir/Expr.scala +++ b/src/main/scala/ir/Expr.scala @@ -1,5 +1,4 @@ package ir - import boogie._ import scala.collection.mutable @@ -9,27 +8,41 @@ sealed trait Expr { def gammas: Set[Variable] = Set() // variables not including those inside a load's index def variables: Set[Variable] = Set() def acceptVisit(visitor: Visitor): Expr = throw new Exception("visitor " + visitor + " unimplemented for: " + this) + + lazy val variablesCached = variables +} + +def size(e: Expr) = { + e.getType match { + case BitVecType(s) => Some(s) + case _ => None + } } sealed trait Literal extends Expr { override def acceptVisit(visitor: Visitor): Literal = visitor.visitLiteral(this) } -sealed trait BoolLit extends Literal +sealed trait BoolLit extends Literal { + def value: Boolean +} case object TrueLiteral extends BoolLit { override def toBoogie: BoolBLiteral = TrueBLiteral override def getType: IRType = BoolType override def toString: String = "true" + override def value = true } case object FalseLiteral extends BoolLit { override def toBoogie: BoolBLiteral = FalseBLiteral override def getType: IRType = BoolType override def toString: String = "false" + override def value = false } case class BitVecLiteral(value: BigInt, size: Int) extends Literal { + assert(size >= 0) override def toBoogie: BitVecBLiteral = BitVecBLiteral(value, size) override def getType: IRType = BitVecType(size) override def toString: String = s"${value}bv$size" @@ -41,9 +54,10 @@ case class IntLiteral(value: BigInt) extends Literal { override def toString: String = value.toString } -/** - * @param end : high bit exclusive - * @param start : low bit inclusive +/** @param end + * : high bit exclusive + * @param start + * : low bit inclusive * @param body */ case class Extract(end: Int, start: Int, body: Expr) extends Expr { @@ -99,6 +113,7 @@ case class UnaryExpr(op: UnOp, arg: Expr) extends Expr { override def gammas: Set[Variable] = arg.gammas override def variables: Set[Variable] = arg.variables override def getType: IRType = (op, arg.getType) match { + case (BoolToBV1, BoolType) => BitVecType(1) case (_: BoolUnOp, BoolType) => BoolType case (_: BVUnOp, bv: BitVecType) => bv case (_: IntUnOp, IntType) => IntType @@ -126,6 +141,7 @@ sealed trait BoolUnOp(op: String) extends UnOp { } case object BoolNOT extends BoolUnOp("!") +case object BoolToBV1 extends BoolUnOp("bool2bv1") sealed trait IntUnOp(op: String) extends UnOp { override def toString: String = op @@ -134,7 +150,6 @@ sealed trait IntUnOp(op: String) extends UnOp { case object IntNEG extends IntUnOp("-") - sealed trait BVUnOp(op: String) extends UnOp { override def toString: String = op } @@ -165,7 +180,7 @@ case class BinaryExpr(op: BinOp, arg1: Expr, arg2: Expr) extends Expr { } else { throw new Exception("bitvector size mismatch") } - case BVULT | BVULE | BVUGT | BVUGE | BVSLT | BVSLE | BVSGT | BVSGE => + case BVULT | BVULE | BVUGT | BVUGE | BVSLT | BVSLE | BVSGT | BVSGE | BVSADDO => if (bv1.size == bv2.size) { BoolType } else { @@ -180,7 +195,9 @@ case class BinaryExpr(op: BinOp, arg1: Expr, arg2: Expr) extends Expr { case IntEQ | IntNEQ | IntLT | IntLE | IntGT | IntGE => BoolType } case _ => - throw new Exception("type mismatch, operator " + op + " type doesn't match args: (" + arg1 + ", " + arg2 + ")") + throw new Exception( + "type mismatch, operator " + op.getClass.getSimpleName + s" type doesn't match args: (" + arg1 + ", " + arg2 + ")" + ) } private def inSize = arg1.getType match { @@ -204,10 +221,13 @@ case class BinaryExpr(op: BinOp, arg1: Expr, arg2: Expr) extends Expr { } -trait BinOp +trait BinOp { + def opName: String +} sealed trait BoolBinOp(op: String) extends BinOp { override def toString: String = op + def opName = op } case object BoolEQ extends BoolBinOp("==") @@ -219,8 +239,10 @@ case object BoolEQUIV extends BoolBinOp("<==>") sealed trait BVBinOp(op: String) extends BinOp { override def toString: String = op + def opName = op } +case object BVSADDO extends BVBinOp("saddo") case object BVAND extends BVBinOp("and") case object BVOR extends BVBinOp("or") case object BVADD extends BVBinOp("add") @@ -253,6 +275,7 @@ case object BVCONCAT extends BVBinOp("++") sealed trait IntBinOp(op: String) extends BinOp { override def toString: String = op + def opName = op def toBV: BVBinOp = this match { case IntADD => BVADD case IntMUL => BVMUL @@ -290,6 +313,7 @@ case class UninterpretedFunction(name: String, params: Seq[Expr], returnType: IR override def toBoogie: BFunctionCall = BFunctionCall(name, params.map(_.toBoogie).toList, returnType.toBoogie, true) override def acceptVisit(visitor: Visitor): Expr = visitor.visitUninterpretedFunction(this) override def variables: Set[Variable] = params.flatMap(_.variables).toSet + override def toString = s"$name(${params.mkString(", ")})" } // Means something has a global scope from the perspective of the IR and Boogie @@ -313,6 +337,10 @@ sealed trait Variable extends Expr { throw new Exception("visitor " + visitor + " unimplemented for: " + this) } +object Variable { + implicit def ordering[V <: Variable]: Ordering[V] = Ordering.by(_.name) +} + // Variable with global scope (in a 'accessible from any procedure' sense), not related to the concurrent shared memory sense // These are all hardware registers case class Register(override val name: String, size: Int) extends Variable with Global { @@ -324,13 +352,20 @@ case class Register(override val name: String, size: Int) extends Variable with } // Variable with scope local to the procedure, typically a temporary variable created in the lifting process -case class LocalVar(override val name: String, override val irType: IRType) extends Variable { +case class LocalVar(varName: String, override val irType: IRType, val index: Int = 0) extends Variable { + override val name = varName + (if (index > 0) then s"_$index" else "") override def toGamma: BVar = BVariable(s"Gamma_$name", BoolBType, Scope.Local) override def toBoogie: BVar = BVariable(s"$name", irType.toBoogie, Scope.Local) - override def toString: String = s"LocalVar($name, $irType)" + override def toString: String = s"LocalVar(${name}, $irType)" override def acceptVisit(visitor: Visitor): Variable = visitor.visitLocalVar(this) } +object LocalVar { + + def unapply(l: LocalVar): Option[(String, IRType)] = Some((s"${l.name}_${l.index}", l.irType)) + +} + // A memory section sealed trait Memory extends Global { val name: String @@ -346,11 +381,13 @@ sealed trait Memory extends Global { } // A stack section of memory, which is local to a thread -case class StackMemory(override val name: String, override val addressSize: Int, override val valueSize: Int) extends Memory { +case class StackMemory(override val name: String, override val addressSize: Int, override val valueSize: Int) + extends Memory { override def acceptVisit(visitor: Visitor): Memory = visitor.visitStackMemory(this) } // A non-stack region of memory, which is shared between threads -case class SharedMemory(override val name: String, override val addressSize: Int, override val valueSize: Int) extends Memory { +case class SharedMemory(override val name: String, override val addressSize: Int, override val valueSize: Int) + extends Memory { override def acceptVisit(visitor: Visitor): Memory = visitor.visitSharedMemory(this) -} \ No newline at end of file +} diff --git a/src/main/scala/ir/IRCursor.scala b/src/main/scala/ir/IRCursor.scala index 3d0fc902e..b61f7a9d1 100644 --- a/src/main/scala/ir/IRCursor.scala +++ b/src/main/scala/ir/IRCursor.scala @@ -247,26 +247,56 @@ def stronglyConnectedComponents[T <: CFGPosition, O <: T](walker: IRWalk[T, O], def toDot(program: Program, labels: Map[CFGPosition, String] = Map.empty, inter: Boolean = false): String = { if (inter) { - val domain = computeDomain[CFGPosition, CFGPosition](InterProcIRCursor, program.procedures) + val domain = computeDomain[CFGPosition, CFGPosition](InterProcIRCursor, program.procedures).toSet toDot[CFGPosition](domain, InterProcIRCursor, labels) } else { - val domain = computeDomain[CFGPosition, CFGPosition](IntraProcIRCursor, program.procedures) + val domain = computeDomain[CFGPosition, CFGPosition](IntraProcIRCursor, program.procedures).toSet toDot[CFGPosition](domain, IntraProcIRCursor, labels) } } + def dotCallGraph(program: Program, labels: Map[CFGPosition, String] = Map.empty): String = { val domain = computeDomain[Procedure, Procedure](CallGraph, program.procedures) - toDot[Procedure](domain, CallGraph, labels) + toDot[Procedure](domain.toSet, CallGraph, labels) +} + + +def dotBlockGraph(proc: Procedure) : String = { + dotBlockGraph(proc.collect { + case b: Block => b + }) +} + +def dotBlockGraph(prog: Program) : String = { + dotBlockGraph(prog.collect { + case b: Block => b + }) +} + +def dotBlockGraph(blocks: Iterable[Block]) : String = { + val printer = translating.BasilIRPrettyPrinter() + val labels : Map[CFGPosition, String] = (blocks.collect { + case b : Block => b -> { + (b.statements.toList.map(printer.apply(_) + ";") ++ { + b.jump match { + case g: GoTo => List() + case o => List(printer(o) + ";") + } + }).map(" " + _).mkString("\n") + } + }).toMap + + toDot[Block](blocks.toSet, IntraProcBlockIRCursor, labels) } def dotBlockGraph(program: Program, labels: Map[CFGPosition, String] = Map.empty): String = { val domain = computeDomain[CFGPosition, Block](IntraProcBlockIRCursor, program.procedures.flatMap(_.blocks).toSet) - toDot[Block](domain, IntraProcBlockIRCursor, labels) + toDot[Block](domain.toSet, IntraProcBlockIRCursor, labels) } def toDot[T <: CFGPosition]( - domain: mutable.Set[T], + domain: Set[T], iterator: IRWalk[? >: T, ?], labels: Map[CFGPosition, String] ): String = { @@ -292,7 +322,7 @@ def toDot[T <: CFGPosition]( case s => s.toString } if (labels.contains(node)) { - text = labels(node) ++ "\n" ++ text + text = text + "\n\n" + labels(node) } text } diff --git a/src/main/scala/ir/Interpreter.scala b/src/main/scala/ir/Interpreter.scala deleted file mode 100644 index 204a3fda7..000000000 --- a/src/main/scala/ir/Interpreter.scala +++ /dev/null @@ -1,356 +0,0 @@ -package ir -import analysis.BitVectorEval.* -import util.Logger - -import scala.collection.mutable -import scala.util.control.Breaks.{break, breakable} - -class Interpreter() { - val regs: mutable.Map[Variable, BitVecLiteral] = mutable.Map() - val mems: mutable.Map[BigInt, BitVecLiteral] = mutable.Map() - private val SP: BitVecLiteral = BitVecLiteral(4096 - 16, 64) - private val FP: BitVecLiteral = BitVecLiteral(4096 - 16, 64) - private val LR: BitVecLiteral = BitVecLiteral(BigInt("FF", 16), 64) - private var nextCmd: Option[Command] = None - private val returnCmd: mutable.Stack[Command] = mutable.Stack() - - def eval(exp: Expr, env: mutable.Map[Variable, BitVecLiteral]): BitVecLiteral = { - exp match { - case id: Variable => - env.get(id) match { - case Some(value) => - Logger.debug(s"\t${id.name} == 0x${value.value.toString(16)}[u${value.size}]") - value - case _ => throw new Exception(s"$id not found in env") - } - - case n: Literal => - n match { - case bv: BitVecLiteral => - Logger.debug(s"\tBitVecLiteral(0x${bv.value.toString(16)}[u${bv.size}])") - bv - case _ => ??? - } - - case ze: ZeroExtend => - Logger.debug(s"\t$ze") - smt_zero_extend(ze.extension, eval(ze.body, env)) - - case se: SignExtend => - Logger.debug(s"\t$se") - smt_sign_extend(se.extension, eval(se.body, env)) - - case e: Extract => - Logger.debug(s"\tExtract($e, ${e.start}, ${e.end})") - boogie_extract(e.end, e.start, eval(e.body, env)) - - case r: Repeat => - Logger.debug(s"\t$r") - ??? // TODO - - case bin: BinaryExpr => - val left = eval(bin.arg1, env) - val right = eval(bin.arg2, env) - Logger.debug( - s"\tBinaryExpr(0x${left.value.toString(16)}[u${left.size}] ${bin.op} 0x${right.value.toString(16)}[u${right.size}])" - ) - bin.op match { - case BVAND => smt_bvand(left, right) - case BVOR => smt_bvor(left, right) - case BVADD => smt_bvadd(left, right) - case BVMUL => smt_bvmul(left, right) - case BVUDIV => smt_bvudiv(left, right) - case BVUREM => smt_bvurem(left, right) - case BVSHL => smt_bvshl(left, right) - case BVLSHR => smt_bvlshr(left, right) - case BVNAND => smt_bvnand(left, right) - case BVNOR => smt_bvnor(left, right) - case BVXOR => smt_bvxor(left, right) - case BVXNOR => smt_bvxnor(left, right) - case BVCOMP => smt_bvcomp(left, right) - case BVSUB => smt_bvsub(left, right) - case BVSDIV => smt_bvsdiv(left, right) - case BVSREM => smt_bvsrem(left, right) - case BVSMOD => smt_bvsmod(left, right) - case BVASHR => smt_bvashr(left, right) - case BVCONCAT => smt_concat(left, right) - case _ => ??? - } - - case un: UnaryExpr => - val arg = eval(un.arg, env) - Logger.debug(s"\tUnaryExpr($un)") - un.op match { - case BVNEG => smt_bvneg(arg) - case BVNOT => smt_bvnot(arg) - } - - case u: UninterpretedFunction => - Logger.debug(s"\t$u") - ??? - } - } - - def evalBool(exp: Expr, env: mutable.Map[Variable, BitVecLiteral]): BoolLit = { - exp match { - case n: BoolLit => n - case bin: BinaryExpr => - bin.op match { - case b: BoolBinOp => - val arg1 = evalBool(bin.arg1, env) - val arg2 = evalBool(bin.arg2, env) - b match { - case BoolEQ => - if (arg1 == arg2) { - TrueLiteral - } else { - FalseLiteral - } - case BoolNEQ => - if (arg1 != arg2) { - TrueLiteral - } else { - FalseLiteral - } - case BoolAND => - (arg1, arg2) match { - case (TrueLiteral, TrueLiteral) => TrueLiteral - case _ => FalseLiteral - } - case BoolOR => - (arg1, arg2) match { - case (FalseLiteral, FalseLiteral) => FalseLiteral - case _ => TrueLiteral - } - case BoolIMPLIES => - (arg1, arg2) match { - case (TrueLiteral, FalseLiteral) => FalseLiteral - case _ => TrueLiteral - } - case BoolEQUIV => - if (arg1 == arg2) { - TrueLiteral - } else { - FalseLiteral - } - } - case b: BVBinOp => - val left = eval(bin.arg1, env) - val right = eval(bin.arg2, env) - b match { - case BVULT => smt_bvult(left, right) - case BVULE => smt_bvule(left, right) - case BVUGT => smt_bvugt(left, right) - case BVUGE => smt_bvuge(left, right) - case BVSLT => smt_bvslt(left, right) - case BVSLE => smt_bvsle(left, right) - case BVSGT => smt_bvsgt(left, right) - case BVSGE => smt_bvsge(left, right) - case BVEQ => smt_bveq(left, right) - case BVNEQ => smt_bvneq(left, right) - case _ => ??? - } - case _ => ??? - } - - case un: UnaryExpr => - un.op match { - case BoolNOT => if evalBool(un.arg, env) == TrueLiteral then FalseLiteral else TrueLiteral - case _ => ??? - } - case _ => ??? - } - } - - def getMemory(index: BigInt, size: Int, endian: Endian, env: mutable.Map[BigInt, BitVecLiteral]): BitVecLiteral = { - val end = index + size / 8 - 1 - val memoryChunks = (index to end).map(i => env.getOrElse(i, BitVecLiteral(0, 8))) - - val (newValue, newSize) = memoryChunks.foldLeft(("", 0)) { (acc, current) => - val currentString: String = current.value.toString(2).reverse.padTo(8, '0').reverse - endian match { - case Endian.LittleEndian => (currentString + acc._1, acc._2 + current.size) - case Endian.BigEndian => (acc._1 + currentString, acc._2 + current.size) - } - } - - BitVecLiteral(BigInt(newValue, 2), newSize) - } - - def setMemory( - index: BigInt, - size: Int, - endian: Endian, - value: BitVecLiteral, - env: mutable.Map[BigInt, BitVecLiteral] - ): BitVecLiteral = { - val binaryString: String = value.value.toString(2).reverse.padTo(size, '0').reverse - - val data: List[BitVecLiteral] = endian match { - case Endian.LittleEndian => - binaryString.grouped(8).toList.map(chunk => BitVecLiteral(BigInt(chunk, 2), 8)).reverse - case Endian.BigEndian => - binaryString.grouped(8).toList.map(chunk => BitVecLiteral(BigInt(chunk, 2), 8)) - } - - data.zipWithIndex.foreach { case (bv, i) => - env(index + i) = bv - } - - value - } - - private def interpretProcedure(p: Procedure): Unit = { - Logger.debug(s"Procedure(${p.name}, ${p.address.getOrElse("None")})") - - // Procedure.in - for ((in, index) <- p.in.zipWithIndex) { - Logger.debug(s"\tin[$index]:${in.name} ${in.size} ${in.value}") - } - - // Procedure.out - for ((out, index) <- p.out.zipWithIndex) { - Logger.debug(s"\tout[$index]:${out.name} ${out.size} ${out.value}") - } - - // Procedure.Block - p.entryBlock match { - case Some(block) => nextCmd = Some(block.statements.headOption.getOrElse(block.jump)) - case None => nextCmd = Some(returnCmd.pop()) - } - } - - private def interpretJump(j: Jump) : Unit = { - Logger.debug(s"jump:") - breakable { - j match { - case gt: GoTo => - Logger.debug(s"$gt") - for (g <- gt.targets) { - val condition: Option[Expr] = g.statements.headOption.collect { case a: Assume => a.body } - condition match { - case Some(e) => evalBool(e, regs) match { - case TrueLiteral => - nextCmd = Some(g.statements.headOption.getOrElse(g.jump)) - break - case _ => - } - case None => - nextCmd = Some(g.statements.headOption.getOrElse(g.jump)) - break - } - } - case r: Return => { - nextCmd = Some(returnCmd.pop()) - } - case h: Unreachable => { - Logger.debug("Unreachable") - nextCmd = None - } - } - } - } - - private def interpretStatement(s: Statement): Unit = { - Logger.debug(s"statement[$s]:") - s match { - case assign: LocalAssign => - Logger.debug(s"LocalAssign ${assign.lhs} = ${assign.rhs}") - val evalRight = eval(assign.rhs, regs) - Logger.debug(s"LocalAssign ${assign.lhs} := 0x${evalRight.value.toString(16)}[u${evalRight.size}]\n") - regs += (assign.lhs -> evalRight) - - case store: MemoryStore => - Logger.debug(s"MemoryStore ${store.mem}[${store.index}] = ${store.value}") - - val index: Int = eval(store.index, regs).value.toInt - val value: BitVecLiteral = eval(store.value, regs) - Logger.debug(s"\tMemoryStore(mem:${store.mem}, index:0x${index.toHexString}, value:0x${ - value.value.toString(16) - }[u${value.size}], size:${store.size})") - - val evalStore = setMemory(index, store.size, store.endian, value, mems) - evalStore match { - case BitVecLiteral(value, size) => - Logger.debug(s"MemoryStore ${store.mem} := 0x${value.toString(16)}[u$size]\n") - } - case load: MemoryLoad => - Logger.debug(s"MemoryLoad ${load.lhs} = ${load.mem}[${load.index}]") - val index: Int = eval(load.index, regs).value.toInt - Logger.debug(s"MemoryLoad ${load.lhs} := ${load.mem}[0x${index.toHexString}[u${load.size}]\n") - val evalLoad = getMemory(index, load.size, load.endian, mems) - regs += (load.lhs -> evalLoad) - evalLoad match { - case BitVecLiteral(value, size) => - Logger.debug(s"MemoryStore ${load.lhs} := 0x${value.toString(16)}[u$size]\n") - } - case _ : NOP => () - case assert: Assert => - // TODO - Logger.debug(assert) - evalBool(assert.body, regs) match { - case TrueLiteral => () - case FalseLiteral => throw Exception(s"Assertion failed ${assert}") - } - case assume: Assume => - // TODO, but already taken into effect if it is a branch condition - Logger.debug(assume) - evalBool(assume.body, regs) match { - case TrueLiteral => () - case FalseLiteral => { - nextCmd = None - Logger.debug(s"Assumption not satisfied: $assume") - } - } - case dc: DirectCall => - Logger.debug(s"$dc") - returnCmd.push(dc.successor) - interpretProcedure(dc.target) - break - case ic: IndirectCall => - Logger.debug(s"$ic") - if (ic.target == Register("R30", 64)) { - if (returnCmd.nonEmpty) { - nextCmd = Some(returnCmd.pop()) - } else { - //Exit Interpreter - nextCmd = None - } - break - } else { - ??? - } - } - } - - def interpret(IRProgram: Program): mutable.Map[Variable, BitVecLiteral] = { - // initialize memory array from IRProgram - var currentAddress = BigInt(0) - IRProgram.initialMemory.values.foreach { im => - if (im.address + im.size > currentAddress) { - val start = im.address.max(currentAddress) - val data = if (im.address < currentAddress) im.bytes.slice((currentAddress - im.address).toInt, im.size) else im.bytes - data.zipWithIndex.foreach { (byte, index) => - mems(start + index) = byte - } - currentAddress = im.address + im.size - } - } - - // Initial SP, FP and LR to regs - regs += (Register("R31", 64) -> SP) - regs += (Register("R29", 64) -> FP) - regs += (Register("R30", 64) -> LR) - - // Program.Procedure - interpretProcedure(IRProgram.mainProcedure) - while (nextCmd.isDefined) { - nextCmd.get match { - case c: Statement => interpretStatement(c) - case c: Jump => interpretJump(c) - } - } - - regs - } -} diff --git a/src/main/scala/ir/Program.scala b/src/main/scala/ir/Program.scala index 5ff441799..bf69019a0 100644 --- a/src/main/scala/ir/Program.scala +++ b/src/main/scala/ir/Program.scala @@ -3,22 +3,69 @@ package ir import scala.collection.mutable.ArrayBuffer import scala.collection.{IterableOnceExtensionMethods, View, immutable, mutable} import boogie.* -import analysis.{BitVectorEval, MergedRegion} +import analysis.{MergedRegion} import util.intrusive_list.* import translating.serialiseIL +import eval.BitVectorEval -class Program(var procedures: ArrayBuffer[Procedure], + +/** + * Iterator in approximate syntactic pre-order of procedures, blocks, and commands. Blocks and procedures are + * not guaranteed to be in any defined order. + */ +class ILUnorderedIterator(private val begin: Iterable[CFGPosition]) extends Iterator[CFGPosition] { + private val stack = mutable.Stack[CFGPosition]() + stack.addAll(begin) + + override def hasNext: Boolean = { + stack.nonEmpty + } + + override def next(): CFGPosition = { + val n: CFGPosition = stack.pop() + + stack.pushAll(n match { + case p: Procedure => p.blocks + case b: Block => Seq() ++ b.statements.toSeq ++ Seq(b.jump) + case s: Command => Seq() + }) + n + } + +} + +class Program(val procedures: ArrayBuffer[Procedure], var mainProcedure: Procedure, val initialMemory: mutable.TreeMap[BigInt, MemorySection]) extends Iterable[CFGPosition] { val threads: ArrayBuffer[ProgramThread] = ArrayBuffer() - val usedMemory: mutable.Map[BigInt, MemorySection] = mutable.TreeMap() + def removeProcedure(i: Int) : Unit = { + val p = procedures(i) + for (b <- p.blocks) { + b.deParent() + } + procedures.remove(i) + } + + def removeProcedure(p: Procedure) : Unit = { + removeProcedure(procedures.indexOf(p)) + } + + def addProcedure(p: Procedure) = { + for (b <- p.blocks) { + b.setParent(p) + } + procedures += p + } + + override def toString(): String = { serialiseIL(this) } + def setModifies(specModifies: Map[String, List[String]]): Unit = { val procToCalls: mutable.Map[Procedure, Set[Procedure]] = mutable.Map() for (p <- procedures) { @@ -92,37 +139,13 @@ class Program(var procedures: ArrayBuffer[Procedure], } - /** - * Iterator in approximate syntactic pre-order of procedures, blocks, and commands. Blocks and procedures are - * not guaranteed to be in any defined order. - */ - private class ILUnorderedIterator(private val begin: Program) extends Iterator[CFGPosition] { - private val stack = mutable.Stack[CFGPosition]() - stack.addAll(begin.procedures) - - override def hasNext: Boolean = { - stack.nonEmpty - } - - override def next(): CFGPosition = { - val n: CFGPosition = stack.pop() - - stack.pushAll(n match { - case p: Procedure => p.blocks - case b: Block => Seq() ++ b.statements.toSeq ++ Seq(b.jump) - case s: Command => Seq() - }) - n - } - - } /** * Get an Iterator in approximate syntactic pre-order of procedures, blocks, and commands. Blocks and procedures are * not guaranteed to be in any defined order. */ def iterator: Iterator[CFGPosition] = { - ILUnorderedIterator(this) + ILUnorderedIterator(this.procedures) } private def memoryLookup(memory: mutable.TreeMap[BigInt, MemorySection], address: BigInt) = { @@ -153,32 +176,57 @@ class Program(var procedures: ArrayBuffer[Procedure], class ProgramThread(val entry: Procedure, val procedures: mutable.LinkedHashSet[Procedure], val creationSite: Option[DirectCall]) { - } +/* + * R0 := call procname(R0, R1, R2) + * + * procname (R0a, R1a, R2a): + * ... + * return (R0) + * +*/ + class Procedure private ( - var name: String, + var procName: String, var address: Option[BigInt], private var _entryBlock: Option[Block], private var _returnBlock: Option[Block], private val _blocks: mutable.LinkedHashSet[Block], - var in: ArrayBuffer[Parameter], - var out: ArrayBuffer[Parameter], + var formalInParam: mutable.SortedSet[LocalVar], + var formalOutParam: mutable.SortedSet[LocalVar], + var inParamDefaultBinding: immutable.SortedMap[LocalVar, Expr], + var outParamDefaultBinding: immutable.SortedMap[LocalVar, Variable], var requires: List[BExpr], var ensures: List[BExpr], - ) { + ) extends Iterable[CFGPosition] { + + def name = procName + address.map("_" + _).getOrElse("") + private val _callers = mutable.HashSet[DirectCall]() _blocks.foreach(_.parent = this) // class invariant require(_returnBlock.forall(b => _blocks.contains(b)) && _entryBlock.forall(b => _blocks.contains(b))) require(_blocks.isEmpty == _entryBlock.isEmpty) // blocks.nonEmpty <==> entryBlock.isDefined - def this(name: String, address: Option[BigInt] = None , entryBlock: Option[Block] = None, returnBlock: Option[Block] = None, blocks: Iterable[Block] = ArrayBuffer(), in: IterableOnce[Parameter] = ArrayBuffer(), out: IterableOnce[Parameter] = ArrayBuffer(), requires: IterableOnce[BExpr] = ArrayBuffer(), ensures: IterableOnce[BExpr] = ArrayBuffer()) = { - this(name, address, entryBlock, returnBlock, mutable.LinkedHashSet.from(blocks), ArrayBuffer.from(in), ArrayBuffer.from(out), List.from(requires), List.from(ensures)) + def this(name: String, address: Option[BigInt] = None , entryBlock: Option[Block] = None, + returnBlock: Option[Block] = None, blocks: Iterable[Block] = ArrayBuffer(), + formalInParam: IterableOnce[LocalVar] = ArrayBuffer(), formalOutParam: IterableOnce[LocalVar] = ArrayBuffer(), + inParamDefaultBinding: Map[LocalVar, Expr] = Map(), outParamDefaultBinding: Map[LocalVar, Variable] = Map(), + requires: IterableOnce[BExpr] = ArrayBuffer(), ensures: IterableOnce[BExpr] = ArrayBuffer()) = { + this(name, address, entryBlock, returnBlock, mutable.LinkedHashSet.from(blocks), mutable.SortedSet.from(formalInParam), mutable.SortedSet.from(formalOutParam), + immutable.SortedMap.from(inParamDefaultBinding), immutable.SortedMap.from(outParamDefaultBinding), + List.from(requires), List.from(ensures)) + } + + def makeCall(label: Option[String] = None) = DirectCall(this, label, outParamDefaultBinding, inParamDefaultBinding) + + def iterator: Iterator[CFGPosition] = { + ILUnorderedIterator(Seq(this)) } override def toString: String = { - s"Procedure $name at ${address.getOrElse("None")} with ${blocks.size} blocks and ${in.size} in and ${out.size} out parameters" + s"Procedure $name at ${address.getOrElse("None")} with ${blocks.size} blocks and ${formalInParam.size} in and ${formalOutParam.size} out parameters" } def calls: Set[Procedure] = blocks.iterator.flatMap(_.calls).toSet @@ -263,11 +311,10 @@ class Procedure private ( * @return the removed block */ def removeBlocks(block: Block): Block = { + require(_blocks.contains(block)) require(block.incomingJumps.isEmpty) // don't leave jumps dangling - if (_blocks.contains(block)) { - block.deParent() - _blocks.remove(block) - } + block.deParent() + _blocks.remove(block) if (_entryBlock.contains(block)) { _entryBlock = None } @@ -332,11 +379,6 @@ class Procedure private ( } } -class Parameter(var name: String, var size: Int, var value: Register) { - def toBoogie: BVariable = BParam(name, BitVecBType(size)) - def toGamma: BVariable = BParam(s"Gamma_$name", BoolBType) -} - class Block private ( val label: String, val address: Option[BigInt], @@ -359,6 +401,8 @@ class Block private ( def jump: Jump = _jump + var rpoOrder : Long = -1 + private def jump_=(j: Jump): Unit = { require(!j.hasParent) if (j ne _jump) { @@ -445,11 +489,17 @@ class Block private ( override def linkParent(p: Procedure): Unit = { // to connect call() links that reference jump.parent.parent + for (s <- statements) { + s.setParent(this) + } jump.setParent(this) } override def unlinkParent(): Unit = { // to disconnect call() links that reference jump.parent.parent + for (s <- statements) { + s.deParent() + } jump.deParent() } } @@ -479,4 +529,4 @@ case class MemorySection(name: String, address: BigInt, size: Int, bytes: Seq[Bi } } -} \ No newline at end of file +} diff --git a/src/main/scala/ir/Statement.scala b/src/main/scala/ir/Statement.scala index c1862a9ff..e6fac78fa 100644 --- a/src/main/scala/ir/Statement.scala +++ b/src/main/scala/ir/Statement.scala @@ -1,6 +1,8 @@ package ir +import util.Logger import util.intrusive_list.IntrusiveListElement import boogie.{BMapVar, GammaStore} +import collection.immutable.SortedMap import collection.mutable @@ -27,10 +29,15 @@ sealed trait Statement extends Command, IntrusiveListElement[Statement] { } sealed trait Assign extends Statement { - var lhs: Variable + def assignees: Set[Variable] } -class LocalAssign(var lhs: Variable, var rhs: Expr, override val label: Option[String] = None) extends Assign { +sealed trait SingleAssign extends Assign { + def lhs: Variable + override def assignees = Set(lhs) +} + +class LocalAssign(var lhs: Variable, var rhs: Expr, override val label: Option[String] = None) extends SingleAssign { override def modifies: Set[Global] = lhs match { case r: Register => Set(r) case _ => Set() @@ -52,7 +59,7 @@ object MemoryStore { def unapply(m: MemoryStore): Option[(Memory, Expr, Expr, Endian, Int, Option[String])] = Some(m.mem, m.index, m.value, m.endian, m.size, m.label) } -class MemoryLoad(var lhs: Variable, var mem: Memory, var index: Expr, var endian: Endian, var size: Int, override val label: Option[String] = None) extends Assign { +class MemoryLoad(var lhs: Variable, var mem: Memory, var index: Expr, var endian: Endian, var size: Int, override val label: Option[String] = None) extends SingleAssign { override def modifies: Set[Global] = lhs match { case r: Register => Set(r) case _ => Set() @@ -101,16 +108,18 @@ class Unreachable(override val label: Option[String] = None) extends Jump { override def acceptVisit(visitor: Visitor): Jump = this } -object Unreachable { - def unapply(u: Unreachable): Option[Option[String]] = Some(u.label) +class Return(override val label: Option[String] = None, var outParams : SortedMap[LocalVar, Expr] = SortedMap()) extends Jump { + override def acceptVisit(visitor: Visitor): Jump = this + override def toString = s"Return(${outParams.mkString(",")})" } -class Return(override val label: Option[String] = None) extends Jump { - override def acceptVisit(visitor: Visitor): Jump = this + +object Unreachable { + def unapply(u: Unreachable): Option[Option[String]] = Some(u.label) } object Return { - def unapply(r: Return): Option[Option[String]] = Some(r.label) + def unapply(r: Return): Option[(Option[String], SortedMap[LocalVar, Expr])] = Some((r.label, r.outParams)) } class GoTo private (private val _targets: mutable.LinkedHashSet[Block], override val label: Option[String]) extends Jump { @@ -166,16 +175,20 @@ sealed trait Call extends Statement { } class DirectCall(val target: Procedure, - override val label: Option[String] = None - ) extends Call { + override val label: Option[String] = None, + var outParams: SortedMap[LocalVar, Variable] = SortedMap(), // out := formal + var actualParams: SortedMap[LocalVar, Expr] = SortedMap(), // formal := actual + ) extends Call with Assign { /* override def locals: Set[Variable] = condition match { case Some(c) => c.locals case None => Set() } */ def calls: Set[Procedure] = Set(target) - override def toString: String = s"${labelStr}DirectCall(${target.name})" + override def toString: String = s"${labelStr}${outParams.map(_._2.name).mkString(",")} := DirectCall(${target.name})(${actualParams.map(_._2).mkString(",")})" override def acceptVisit(visitor: Visitor): Statement = visitor.visitDirectCall(this) + def assignees = outParams.map(_._2).toSet + override def linkParent(p: Block): Unit = { super.linkParent(p) target.addCaller(this) @@ -189,7 +202,7 @@ class DirectCall(val target: Procedure, } object DirectCall: - def unapply(i: DirectCall): Option[(Procedure, Option[String])] = Some(i.target, i.label) + def unapply(i: DirectCall): Option[(Procedure, Map[LocalVar, Variable], Map[LocalVar, Expr], Option[String])] = Some(i.target, i.outParams, i.actualParams, i.label) class IndirectCall(var target: Variable, override val label: Option[String] = None diff --git a/src/main/scala/ir/Visitor.scala b/src/main/scala/ir/Visitor.scala index c649dc4f3..f07c3d266 100644 --- a/src/main/scala/ir/Visitor.scala +++ b/src/main/scala/ir/Visitor.scala @@ -48,7 +48,9 @@ abstract class Visitor { } def visitDirectCall(node: DirectCall): Statement = { - node + val ins = node.actualParams.map(i => i._1 -> visitExpr(i._2)) + val outs = node.outParams.map(i => i._1 -> visitVariable(i._2)) + DirectCall(node.target, node.label, outs, ins) } def visitIndirectCall(node: IndirectCall): Statement = { @@ -68,17 +70,8 @@ abstract class Visitor { for (b <- node.blocks) { node.replaceBlock(b, visitBlock(b)) } - for (i <- node.in.indices) { - node.in(i) = visitParameter(node.in(i)) - } - for (i <- node.out.indices) { - node.out(i) = visitParameter(node.out(i)) - } - node - } - - def visitParameter(node: Parameter): Parameter = { - node.value = visitRegister(node.value) + node.formalInParam = node.formalInParam.map(visitLocalVar) + node.formalOutParam = node.formalOutParam.map(visitLocalVar) node } @@ -205,6 +198,8 @@ abstract class ReadOnlyVisitor extends Visitor { } override def visitDirectCall(node: DirectCall): Statement = { + node.actualParams.foreach(i => visitExpr(i._2)) + node.outParams.foreach(i => visitVariable(i._2)) node } @@ -225,19 +220,15 @@ abstract class ReadOnlyVisitor extends Visitor { for (i <- node.blocks) { visitBlock(i) } - for (i <- node.in) { - visitParameter(i) + for (i <- node.formalInParam) { + visitLocalVar(i) } - for (i <- node.out) { - visitParameter(i) + for (i <- node.formalOutParam) { + visitLocalVar(i) } node } - override def visitParameter(node: Parameter): Parameter = { - visitRegister(node.value) - node - } override def visitProgram(node: Program): Program = { for (i <- node.procedures) { @@ -306,16 +297,25 @@ class StackSubstituter extends IntraproceduralControlFlowVisitor { // reset for each procedure stackRefs.clear() stackRefs.add(stackPointer) + stackRefs.add(LocalVar("R31_in", BitVecType(64))) + stackRefs.add(LocalVar("R31", BitVecType(64))) super.visitProcedure(node) } + def isStackPtr(v: Variable) = { + (v match { + case l: LocalVar if l.varName == "R31" => true + case r: Variable if r.name == "R31" => true + case _ => false + }) || stackRefs.contains(v) + } + override def visitMemoryLoad(node: MemoryLoad): MemoryLoad = { // replace mem with stack in load if index contains stack references - val loadStackRefs = node.index.variables.intersect(stackRefs) - - if (loadStackRefs.nonEmpty) { + if (node.index.variables.exists(isStackPtr)) { node.mem = stackMemory - } + } + if (stackRefs.contains(node.lhs) && node.lhs != stackPointer) { stackRefs.remove(node.lhs) } @@ -328,18 +328,16 @@ class StackSubstituter extends IntraproceduralControlFlowVisitor { val variableVisitor = VariablesWithoutStoresLoads() variableVisitor.visitExpr(node.rhs) - val rhsStackRefs = variableVisitor.variables.toSet.intersect(stackRefs) - if (rhsStackRefs.nonEmpty) { + if (variableVisitor.variables.exists(isStackPtr)) { stackRefs.add(node.lhs) - } else if (stackRefs.contains(node.lhs) && node.lhs != stackPointer) { + } else if (stackRefs.contains(node.lhs) && node.lhs.name != stackPointer.name) { stackRefs.remove(node.lhs) } node } override def visitMemoryStore(node: MemoryStore): Statement = { - val indexStackRefs = node.index.variables.intersect(stackRefs) - if (indexStackRefs.nonEmpty) { + if (node.index.variables.exists(isStackPtr)) { node.mem = stackMemory } node @@ -377,8 +375,8 @@ class Renamer(reserved: Set[String]) extends Visitor { } override def visitLocalVar(node: LocalVar): LocalVar = { - if (reserved.contains(node.name)) { - node.copy(name = s"#${node.name}") + if (reserved.contains(node.varName)) { + node.copy(varName = s"#${node.varName}") } else { node } @@ -400,16 +398,9 @@ class Renamer(reserved: Set[String]) extends Visitor { } } - override def visitParameter(node: Parameter): Parameter = { - if (reserved.contains(node.name)) { - node.name = s"#${node.name}" - } - super.visitParameter(node) - } - override def visitProcedure(node: Procedure): Procedure = { if (reserved.contains(node.name)) { - node.name = s"#${node.name}" + node.procName = s"#${node.name}" } super.visitProcedure(node) } @@ -418,7 +409,7 @@ 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)) { + if (external.contains(node.procName)) { // update the modifies set before removing the body node.modifies.addAll(node.blocks.flatMap(_.modifies)) node.replaceBlocks(Seq()) diff --git a/src/main/scala/ir/cilvisitor/CILVisitor.scala b/src/main/scala/ir/cilvisitor/CILVisitor.scala index 372b500de..714a2d9d3 100644 --- a/src/main/scala/ir/cilvisitor/CILVisitor.scala +++ b/src/main/scala/ir/cilvisitor/CILVisitor.scala @@ -16,24 +16,21 @@ case class ChangeTo[T](e: T) extends VisitAction[T] // changes to e, then visits children of e, then applies f to the result case class ChangeDoChildrenPost[T](e: T, f: T => T) extends VisitAction[T] - trait CILVisitor: def vprog(e: Program): VisitAction[Program] = DoChildren() def vproc(e: Procedure): VisitAction[List[Procedure]] = DoChildren() - def vparams(e: ArrayBuffer[Parameter]): VisitAction[ArrayBuffer[Parameter]] = DoChildren() def vblock(e: Block): VisitAction[Block] = DoChildren() def vstmt(e: Statement): VisitAction[List[Statement]] = DoChildren() def vjump(j: Jump): VisitAction[Jump] = DoChildren() - def vfallthrough(j: Option[GoTo]): VisitAction[Option[GoTo]] = DoChildren() def vexpr(e: Expr): VisitAction[Expr] = DoChildren() - def vvar(e: Variable): VisitAction[Variable] = DoChildren() + def vrvar(e: Variable): VisitAction[Variable] = DoChildren() + def vlvar(e: Variable): VisitAction[Variable] = DoChildren() def vmem(e: Memory): VisitAction[Memory] = DoChildren() - def enter_scope(params: ArrayBuffer[Parameter]): Unit = () - def leave_scope(outparam: ArrayBuffer[Parameter]): Unit = () - + def enter_scope(params: Map[LocalVar, Expr]): Unit = () + def leave_scope(): Unit = () def doVisitList[T](v: CILVisitor, a: VisitAction[List[T]], n: T, continue: T => T): List[T] = { a match { @@ -55,62 +52,98 @@ def doVisit[T](v: CILVisitor, a: VisitAction[T], n: T, continue: T => T): T = { class CILVisitorImpl(val v: CILVisitor) { - def visit_parameters(p: ArrayBuffer[Parameter]): ArrayBuffer[Parameter] = { - doVisit(v, v.vparams(p), p, n => n) + def visit_rvar(n: Variable): Variable = { + // variable in right expression + doVisit(v, v.vrvar(n), n, (n) => n) } - def visit_var(n: Variable): Variable = { - doVisit(v, v.vvar(n), n, n => n) + def visit_lvar(n: Variable): Variable = { + // variable in left expression + doVisit(v, v.vlvar(n), n, (n) => n) } - def visit_mem(n: Memory): Memory = { doVisit(v, v.vmem(n), n, n => n) } - def visit_jump(j: Jump): Jump = { - doVisit(v, v.vjump(j), j, j => j) + def continue(j: Jump) = j match { + case r: Return => { + val outs = r.outParams.map(o => o._1 -> visit_expr(o._2)) + v.leave_scope() + r.outParams = outs + r + } + case x => x + } + doVisit(v, v.vjump(j), j, continue) } - def visit_fallthrough(j: Option[GoTo]): Option[GoTo] = { - doVisit(v, v.vfallthrough(j), j, j => j) - } - def visit_expr(n: Expr): Expr = { + + def visit_expr(n: Expr): Expr = { def continue(n: Expr): Expr = n match { - case n: Literal => n - case Extract(end, start, arg) => Extract(end, start, visit_expr(arg)) - case Repeat(repeats, arg) => Repeat(repeats, visit_expr(arg)) - case ZeroExtend(bits, arg) => ZeroExtend(bits, visit_expr(arg)) - case SignExtend(bits, arg) => SignExtend(bits, visit_expr(arg)) - case BinaryExpr(op, arg, arg2) => BinaryExpr(op, visit_expr(arg), visit_expr(arg2)) - case UnaryExpr(op, arg) => UnaryExpr(op, visit_expr(arg)) - case v: Variable => visit_var(v) - case UninterpretedFunction(n, params, rt) => UninterpretedFunction(n, params.map(visit_expr), rt) + case n: Literal => n + case Extract(end, start, arg) => { + val narg = visit_expr(arg) + if (narg ne arg) Extract(end, start, narg) else n + } + case Repeat(repeats, arg) => { + val narg = visit_expr(arg) + if (narg ne arg) Repeat(repeats, arg) else n + } + case ZeroExtend(bits, arg) => { + val narg = visit_expr(arg) + if (narg ne arg) ZeroExtend(bits, narg) else n + } + case SignExtend(bits, arg) => { + val narg = visit_expr(arg) + if (narg ne arg) SignExtend(bits, narg) else n + } + case BinaryExpr(op, arg, arg2) => { + val narg1 = visit_expr(arg) + val narg2 = visit_expr(arg2) + if ((narg1 ne arg) || (narg2 ne arg2)) BinaryExpr(op, narg1, narg2) else n + } + case UnaryExpr(op, arg) => { + val narg = visit_expr(arg) + if (narg ne arg) UnaryExpr(op, narg) else n + } + case v: Variable => visit_rvar(v) + case UninterpretedFunction(name, params, rt) => { + val nparams = params.map(visit_expr) + val updated = (params.zip(nparams).map((a, b) => a ne b)).contains(true) + if (updated) UninterpretedFunction(name, nparams, rt) else n + } } doVisit(v, v.vexpr(n), n, continue) } def visit_stmt(s: Statement): List[Statement] = { def continue(n: Statement) = n match { - case d: DirectCall => d + case d: DirectCall => + val actuals = d.actualParams.map(i => i._1 -> visit_expr(i._2)) + val outs = d.outParams.map(i => i._1 -> visit_lvar(i._2)) + v.enter_scope(actuals) + d.outParams = outs + d.actualParams = actuals + d case i: IndirectCall => - i.target = visit_var(i.target) + i.target = visit_rvar(i.target) i case m: MemoryStore => - m.mem = visit_mem(m.mem) - m.index = visit_expr(m.index) m.value = visit_expr(m.value) + m.index = visit_expr(m.index) + m.mem = visit_mem(m.mem) m case m: MemoryLoad => - m.mem = visit_mem(m.mem) m.index = visit_expr(m.index) - m.lhs = visit_var(m.lhs) + m.mem = visit_mem(m.mem) + m.lhs = visit_lvar(m.lhs) m case m: LocalAssign => m.rhs = visit_expr(m.rhs) - m.lhs = visit_var(m.lhs) + m.lhs = visit_lvar(m.lhs) m case s: Assert => s.body = visit_expr(s.body) @@ -143,13 +176,12 @@ class CILVisitorImpl(val v: CILVisitor) { def visit_proc(p: Procedure): List[Procedure] = { def continue(p: Procedure) = { - p.in = visit_parameters(p.in) - v.enter_scope(p.in) + // manage scope on call/return + // v.enter_scope(ArrayBuffer()) for (b <- p.blocks) { p.replaceBlock(b, visit_block(b)) } - p.out = visit_parameters(p.out) - v.leave_scope(p.out) + // v.leave_scope(ArrayBuffer()) p } @@ -158,7 +190,23 @@ class CILVisitorImpl(val v: CILVisitor) { def visit_prog(p: Program): Program = { def continue(p: Program) = { - p.procedures = p.procedures.flatMap(visit_proc) + for (i <- (0 until p.procedures.size)) { + visit_proc(p.procedures(i)) match { + case h::Nil if p.procedures(i) eq h => {} + case h::Nil if !(p.procedures(i) eq h) => { + // TODO: need some better approximation of knowing whether this procedure requires relinking? + p.removeProcedure(i) + p.addProcedure(h) + } + case Nil => p.removeProcedure(i) + case h::tl => { + p.removeProcedure(i) + for (x <- h::tl) { + p.addProcedure(x) + } + } + } + } p } doVisit(v, v.vprog(p), p, continue) diff --git a/src/main/scala/ir/dsl/DSL.scala b/src/main/scala/ir/dsl/DSL.scala index a0d87f5f9..03688084e 100644 --- a/src/main/scala/ir/dsl/DSL.scala +++ b/src/main/scala/ir/dsl/DSL.scala @@ -12,12 +12,21 @@ val R4: Register = Register("R4", 64) val R5: Register = Register("R5", 64) val R6: Register = Register("R6", 64) val R7: Register = Register("R7", 64) +val R8: Register = Register("R8", 64) val R29: Register = Register("R29", 64) val R30: Register = Register("R30", 64) val R31: Register = Register("R31", 64) +def exprEq(l: Expr, r: Expr) : Expr = (l, r) match { + case (l, r) if l.getType != r.getType => FalseLiteral + case (l, r) if l.getType == BoolType => BinaryExpr(BoolEQ, l, r) + case (l, r) if l.getType.isInstanceOf[BitVecType] => BinaryExpr(BVEQ, l, r) + case (l, r) if l.getType == IntType => BinaryExpr(IntEQ, l, r) + case _ => FalseLiteral +} + def bv32(i: Int): BitVecLiteral = BitVecLiteral(i, 32) def bv64(i: Int): BitVecLiteral = BitVecLiteral(i, 64) @@ -26,6 +35,8 @@ def bv8(i: Int): BitVecLiteral = BitVecLiteral(i, 8) def bv16(i: Int): BitVecLiteral = BitVecLiteral(i, 16) +def R(i: Int): Register = Register(s"R$i", 64) + case class DelayNameResolve(ident: String) { def resolveProc(prog: Program): Option[Procedure] = prog.collectFirst { case b: Procedure if b.name == ident => b diff --git a/src/main/scala/analysis/BitVectorEval.scala b/src/main/scala/ir/eval/BitVectorEval.scala similarity index 91% rename from src/main/scala/analysis/BitVectorEval.scala rename to src/main/scala/ir/eval/BitVectorEval.scala index ee2d09512..89cee4af0 100644 --- a/src/main/scala/analysis/BitVectorEval.scala +++ b/src/main/scala/ir/eval/BitVectorEval.scala @@ -1,7 +1,5 @@ -package analysis +package ir.eval import ir.* -import analysis.BitVectorEval.* - import scala.annotation.tailrec import scala.math.pow @@ -168,15 +166,15 @@ object BitVectorEval { /** (bvneq (_ BitVec m) (_ BitVec m)) * - not equal too */ - def smt_bveq(s: BitVecLiteral, t: BitVecLiteral): BoolLit = { - bool2BoolLit(s == t) + def smt_bveq(s: BitVecLiteral, t: BitVecLiteral): Boolean = { + s == t } /** (bvneq (_ BitVec m) (_ BitVec m)) * - not equal too */ - def smt_bvneq(s: BitVecLiteral, t: BitVecLiteral): BoolLit = { - bool2BoolLit(s != t) + def smt_bvneq(s: BitVecLiteral, t: BitVecLiteral): Boolean = { + s != t } /** (bvshl (_ BitVec m) (_ BitVec m) (_ BitVec m)) @@ -270,55 +268,55 @@ object BitVectorEval { /** (bvult (_ BitVec m) (_ BitVec m) Bool) * - binary predicate for unsigned less-than */ - def smt_bvult(s: BitVecLiteral, t: BitVecLiteral): BoolLit = { - bool2BoolLit(bv2nat(s) < bv2nat(t)) + def smt_bvult(s: BitVecLiteral, t: BitVecLiteral): Boolean = { + bv2nat(s) < bv2nat(t) } /** (bvule (_ BitVec m) (_ BitVec m) Bool) * - binary predicate for unsigned less than or equal */ - def smt_bvule(s: BitVecLiteral, t: BitVecLiteral): BoolLit = { - bool2BoolLit(bv2nat(s) <= bv2nat(t)) + def smt_bvule(s: BitVecLiteral, t: BitVecLiteral): Boolean = { + bv2nat(s) <= bv2nat(t) } /** (bvugt (_ BitVec m) (_ BitVec m) Bool) * - binary predicate for unsigned greater than */ - def smt_bvugt(s: BitVecLiteral, t: BitVecLiteral): BoolLit = { + def smt_bvugt(s: BitVecLiteral, t: BitVecLiteral): Boolean = { smt_bvult(t, s) } /** (bvuge (_ BitVec m) (_ BitVec m) Bool) * - binary predicate for unsigned greater than or equal */ - def smt_bvuge(s: BitVecLiteral, t: BitVecLiteral): BoolLit = smt_bvule(t, s) + def smt_bvuge(s: BitVecLiteral, t: BitVecLiteral): Boolean = smt_bvule(t, s) /** (bvslt (_ BitVec m) (_ BitVec m) Bool) * - binary predicate for signed less than */ - def smt_bvslt(s: BitVecLiteral, t: BitVecLiteral): BoolLit = { + def smt_bvslt(s: BitVecLiteral, t: BitVecLiteral): Boolean = { val sNeg = isNegative(s) val tNeg = isNegative(t) - bool2BoolLit((sNeg && !tNeg) || ((sNeg == tNeg) && (smt_bvult(s, t) == TrueLiteral))) + (sNeg && !tNeg) || ((sNeg == tNeg) && (smt_bvult(s, t))) } /** (bvsle (_ BitVec m) (_ BitVec m) Bool) * - binary predicate for signed less than or equal */ - def smt_bvsle(s: BitVecLiteral, t: BitVecLiteral): BoolLit = + def smt_bvsle(s: BitVecLiteral, t: BitVecLiteral): Boolean = val sNeg = isNegative(s) val tNeg = isNegative(t) - bool2BoolLit((sNeg && !tNeg) || ((sNeg == tNeg) && (smt_bvule(s, t) == TrueLiteral))) + (sNeg && !tNeg) || ((sNeg == tNeg) && (smt_bvule(s, t))) /** (bvsgt (_ BitVec m) (_ BitVec m) Bool) * - binary predicate for signed greater than */ - def smt_bvsgt(s: BitVecLiteral, t: BitVecLiteral): BoolLit = smt_bvslt(t, s) + def smt_bvsgt(s: BitVecLiteral, t: BitVecLiteral): Boolean = smt_bvslt(t, s) /** (bvsge (_ BitVec m) (_ BitVec m) Bool) * - binary predicate for signed greater than or equal */ - def smt_bvsge(s: BitVecLiteral, t: BitVecLiteral): BoolLit = smt_bvsle(t, s) + def smt_bvsge(s: BitVecLiteral, t: BitVecLiteral): Boolean = smt_bvsle(t, s) def smt_bvashr(s: BitVecLiteral, t: BitVecLiteral): BitVecLiteral = if (!isNegative(s)) { diff --git a/src/main/scala/ir/eval/ExprEval.scala b/src/main/scala/ir/eval/ExprEval.scala new file mode 100644 index 000000000..7e1373996 --- /dev/null +++ b/src/main/scala/ir/eval/ExprEval.scala @@ -0,0 +1,306 @@ +package ir.eval +import ir.eval.BitVectorEval +import ir.cilvisitor.* +import util.functional.State +import ir._ + +/** We generalise the expression evaluator to a partial evaluator to simplify evaluating casts. + * + * - Program state is taken via a function from var -> value and for loads a function from (mem,addr,endian,size) -> + * value. + * - For conrete evaluators we prefer low-level representations (bool vs BoolLit) and wrap them at the expression + * eval level + * - Avoid using any default cases so we have some idea of complete coverage + */ + +def evalBVBinExpr(b: BVBinOp, l: BitVecLiteral, r: BitVecLiteral): BitVecLiteral = { + b match { + case BVADD => BitVectorEval.smt_bvadd(l, r) + case BVSUB => BitVectorEval.smt_bvsub(l, r) + case BVMUL => BitVectorEval.smt_bvmul(l, r) + case BVUDIV => BitVectorEval.smt_bvudiv(l, r) + case BVSDIV => BitVectorEval.smt_bvsdiv(l, r) + case BVSREM => BitVectorEval.smt_bvsrem(l, r) + case BVUREM => BitVectorEval.smt_bvurem(l, r) + case BVSMOD => BitVectorEval.smt_bvsmod(l, r) + case BVAND => BitVectorEval.smt_bvand(l, r) + case BVOR => BitVectorEval.smt_bvxor(l, r) + case BVXOR => BitVectorEval.smt_bvxor(l, r) + case BVNAND => BitVectorEval.smt_bvnand(l, r) + case BVNOR => BitVectorEval.smt_bvnor(l, r) + case BVXNOR => BitVectorEval.smt_bvxnor(l, r) + case BVSHL => BitVectorEval.smt_bvshl(l, r) + case BVLSHR => BitVectorEval.smt_bvlshr(l, r) + case BVASHR => BitVectorEval.smt_bvashr(l, r) + case BVCOMP => BitVectorEval.smt_bvcomp(l, r) + case BVCONCAT => BitVectorEval.smt_concat(l, r) + case BVULE | BVULT | BVUGT | BVUGE | BVSLT | BVSLE | BVSGT | BVSGE | BVEQ | BVNEQ => + throw IllegalArgumentException("Did not expect logical op") + } +} + +def evalBVLogBinExpr(b: BVBinOp, l: BitVecLiteral, r: BitVecLiteral): Boolean = b match { + case BVULE => BitVectorEval.smt_bvule(l, r) + case BVUGT => BitVectorEval.smt_bvugt(l, r) + case BVUGE => BitVectorEval.smt_bvuge(l, r) + case BVULT => BitVectorEval.smt_bvult(l, r) + case BVSLT => BitVectorEval.smt_bvslt(l, r) + case BVSLE => BitVectorEval.smt_bvsle(l, r) + case BVSGT => BitVectorEval.smt_bvsgt(l, r) + case BVSGE => BitVectorEval.smt_bvsge(l, r) + case BVEQ => BitVectorEval.smt_bveq(l, r) + case BVNEQ => BitVectorEval.smt_bvneq(l, r) + case BVADD | BVSUB | BVMUL | BVUDIV | BVSDIV | BVSREM | BVUREM | BVSMOD | BVAND | BVOR | BVXOR | BVNAND | BVNOR | + BVXNOR | BVSHL | BVLSHR | BVASHR | BVCOMP | BVCONCAT => + throw IllegalArgumentException("Did not expect non-logical op") +} + +def evalIntLogBinExpr(b: IntBinOp, l: BigInt, r: BigInt): Boolean = b match { + case IntEQ => l == r + case IntNEQ => l != r + case IntLT => l < r + case IntLE => l <= r + case IntGT => l > r + case IntGE => l >= r + case IntADD | IntSUB | IntMUL | IntDIV | IntMOD => throw IllegalArgumentException("Did not expect non-logical op") +} + +def evalIntBinExpr(b: IntBinOp, l: BigInt, r: BigInt): BigInt = b match { + case IntADD => l + r + case IntSUB => l - r + case IntMUL => l * r + case IntDIV => l / r + case IntMOD => l % r + case IntEQ | IntNEQ | IntLT | IntLE | IntGT | IntGE => throw IllegalArgumentException("Did not expect logical op") +} + +def evalBoolLogBinExpr(b: BoolBinOp, l: Boolean, r: Boolean): Boolean = b match { + case BoolEQ => l == r + case BoolEQUIV => l == r + case BoolNEQ => l != r + case BoolAND => l && r + case BoolOR => l || r + case BoolIMPLIES => l || (!r) +} + +def evalUnOp(op: UnOp, body: Literal): Expr = { + (body, op) match { + case (b: BitVecLiteral, BVNOT) => BitVectorEval.smt_bvnot(b) + case (b: BitVecLiteral, BVNEG) => BitVectorEval.smt_bvneg(b) + case (i: IntLiteral, IntNEG) => IntLiteral(-i.value) + case (FalseLiteral, BoolNOT) => TrueLiteral + case (TrueLiteral, BoolNOT) => FalseLiteral + case (TrueLiteral, BoolToBV1) => BitVecLiteral(1, 1) + case (FalseLiteral, BoolToBV1) => BitVecLiteral(0, 1) + case (_, _) => throw Exception(s"Unreachable ${(body, op)}") + } +} + +def evalIntExpr( + exp: Expr, + variableAssignment: Variable => Option[Literal], + memory: (Memory, Expr, Endian, Int) => Option[Literal] = ((a, b, c, d) => None) +): Either[Expr, BigInt] = { + partialEvalExpr(exp, variableAssignment, memory) match { + case i: IntLiteral => Right(i.value) + case o => Left(o) + } +} + +def evalBVExpr( + exp: Expr, + variableAssignment: Variable => Option[Literal], + memory: (Memory, Expr, Endian, Int) => Option[Literal] = ((a, b, c, d) => None) +): Either[Expr, BitVecLiteral] = { + partialEvalExpr(exp, variableAssignment, memory) match { + case b: BitVecLiteral => Right(b) + case o => Left(o) + } +} + +def evalLogExpr( + exp: Expr, + variableAssignment: Variable => Option[Literal], + memory: (Memory, Expr, Endian, Int) => Option[Literal] = ((a, b, c, d) => None) +): Either[Expr, Boolean] = { + partialEvalExpr(exp, variableAssignment, memory) match { + case TrueLiteral => Right(true) + case FalseLiteral => Right(false) + case o => Left(o) + } +} + +def evalExpr( + exp: Expr, + variableAssignment: Variable => Option[Literal], + memory: (Memory, Expr, Endian, Int) => Option[Literal] = ((d, a, b, c) => None) +): Option[Literal] = { + partialEvalExpr match { + case l: Literal => Some(l) + case _ => None + } +} + +/** typeclass defining variable and memory laoding from state S + */ +trait Loader[S, E] { + def getVariable(v: Variable): State[S, Option[Literal], E] + def loadMemory(m: Memory, addr: Expr, endian: Endian, size: Int): State[S, Option[Literal], E] = { + State.pure(None) + } +} + +def evaluateExpr(exp: Expr): Option[Literal] = { + val (e, _) = simpFixedPoint(SimpExpr(fastPartialEvalExpr).apply)(exp) + e match { + case l: Literal => Some(l) + case _ => None + } +} + +def fastPartialEvalExpr(exp: Expr): (Expr, Boolean) = { + /* + * Ignore substitutions and parital eval + */ + + var didAnything = true + val r = exp match { + case UnaryExpr(op, l: Literal) => logSimp(exp, evalUnOp(op, l)) + case BinaryExpr(op: BVBinOp, l: BitVecLiteral, r: BitVecLiteral) if exp.getType.isInstanceOf[BitVecType] => + logSimp(exp, evalBVBinExpr(op, l, r)) + case BinaryExpr(op: IntBinOp, l: IntLiteral, r: IntLiteral) if exp.getType == IntType => + logSimp(exp, IntLiteral(evalIntBinExpr(op, l.value, r.value))) + case BinaryExpr(op: IntBinOp, l: IntLiteral, r: IntLiteral) if exp.getType == BoolType => + logSimp(exp, if evalIntLogBinExpr(op, l.value, r.value) then TrueLiteral else FalseLiteral) + case BinaryExpr(op: BVBinOp, l: BitVecLiteral, r: BitVecLiteral) if exp.getType == BoolType => + logSimp(exp, if evalBVLogBinExpr(op, l, r) then TrueLiteral else FalseLiteral) + case BinaryExpr(op: BoolBinOp, l: BoolLit, r: BoolLit) => + logSimp(exp, if (evalBoolLogBinExpr(op, l.value, r.value)) then TrueLiteral else FalseLiteral) + case ZeroExtend(e, l: BitVecLiteral) => logSimp(exp, BitVectorEval.smt_zero_extend(e, l)) + case SignExtend(e, l: BitVecLiteral) => logSimp(exp, BitVectorEval.smt_sign_extend(e, l)) + case Extract(e, b, l: BitVecLiteral) => logSimp(exp, BitVectorEval.boogie_extract(e, b, l)) + case Repeat(reps, b: BitVecLiteral) => { + assert(reps > 0) + if (reps == 1) logSimp(exp, b) + else { + logSimp(exp, (2 to reps).foldLeft(b)((acc, r) => BitVectorEval.smt_concat(acc, b))) + } + } + case o => { + didAnything = false + o + } + } + (r, didAnything) +} + +def statePartialEvalExpr[S](l: Loader[S, InterpreterError])(exp: Expr): State[S, Expr, InterpreterError] = { + val eval = statePartialEvalExpr(l) + val ns = exp match { + case f: UninterpretedFunction => State.pure(f) + case unOp: UnaryExpr => + for { + body <- eval(unOp.arg) + } yield (body match { + case l: Literal => evalUnOp(unOp.op, l) + case o => UnaryExpr(unOp.op, body) + }) + case binOp: BinaryExpr => + for { + lhs <- eval(binOp.arg1) + rhs <- eval(binOp.arg2) + } yield (binOp.getType match { + case m: MapType => binOp + case b: BitVecType => { + (binOp.op, lhs, rhs) match { + case (o: BVBinOp, l: BitVecLiteral, r: BitVecLiteral) => evalBVBinExpr(o, l, r) + case _ => BinaryExpr(binOp.op, lhs, rhs) + } + } + case BoolType => { + def bool2lit(b: Boolean) = if b then TrueLiteral else FalseLiteral + (binOp.op, lhs, rhs) match { + case (o: BVBinOp, l: BitVecLiteral, r: BitVecLiteral) => bool2lit(evalBVLogBinExpr(o, l, r)) + case (o: IntBinOp, l: IntLiteral, r: IntLiteral) => bool2lit(evalIntLogBinExpr(o, l.value, r.value)) + case (o: BoolBinOp, l: BoolLit, r: BoolLit) => bool2lit(evalBoolLogBinExpr(o, l.value, r.value)) + case _ => BinaryExpr(binOp.op, lhs, rhs) + } + } + case IntType => { + (binOp.op, lhs, rhs) match { + case (o: IntBinOp, l: IntLiteral, r: IntLiteral) => IntLiteral(evalIntBinExpr(o, l.value, r.value)) + case _ => BinaryExpr(binOp.op, lhs, rhs) + } + } + }) + case extend: ZeroExtend => + for { + body <- eval(extend.body) + } yield (body match { + case b: BitVecLiteral => BitVectorEval.smt_zero_extend(extend.extension, b) + case o => extend.copy(body = o) + }) + case extend: SignExtend => + for { + body <- eval(extend.body) + } yield (body match { + case b: BitVecLiteral => BitVectorEval.smt_sign_extend(extend.extension, b) + case o => extend.copy(body = o) + }) + case e: Extract => + for { + body <- eval(e.body) + } yield (body match { + case b: BitVecLiteral => BitVectorEval.boogie_extract(e.end, e.start, b) + case o => e.copy(body = o) + }) + case r: Repeat => + for { + body <- eval(r.body) + } yield (body match { + case b: BitVecLiteral => { + assert(r.repeats > 0) + if (r.repeats == 1) b + else { + (2 to r.repeats).foldLeft(b)((acc, r) => BitVectorEval.smt_concat(acc, b)) + } + } + case o => r.copy(body = o) + }) + case variable: Variable => + for { + v: Option[Literal] <- l.getVariable(variable) + } yield (v.getOrElse(variable)) + case b: BitVecLiteral => State.pure(b) + case b: IntLiteral => State.pure(b) + case b: BoolLit => State.pure(b) + } + State.protect( + () => ns, + { case e => + Errored(e.toString) + }: PartialFunction[Exception, InterpreterError] + ) + +} + +class StatelessLoader[E]( + getVar: Variable => Option[Literal], + loadMem: (Memory, Expr, Endian, Int) => Option[Literal] = ((a, b, c, d) => None) +) extends Loader[Unit, E] { + def getVariable(v: Variable): State[Unit, Option[Literal], E] = State.pure(getVar(v)) + override def loadMemory(m: Memory, addr: Expr, endian: Endian, size: Int): State[Unit, Option[Literal], E] = + State.pure(loadMem(m, addr, endian, size)) +} + +def partialEvalExpr( + exp: Expr, + variableAssignment: Variable => Option[Literal], + memory: (Memory, Expr, Endian, Int) => Option[Literal] = ((a, b, c, d) => None) +): Expr = { + val l = StatelessLoader[InterpreterError](variableAssignment, memory) + State.evaluate((), statePartialEvalExpr(l)(exp)) match { + case Right(e) => e + case Left(e) => throw Exception(s"Unable to evaluate expr $exp :" + e.toString) + } +} diff --git a/src/main/scala/ir/eval/InterpretBasilIR.scala b/src/main/scala/ir/eval/InterpretBasilIR.scala new file mode 100644 index 000000000..7cf8f6453 --- /dev/null +++ b/src/main/scala/ir/eval/InterpretBasilIR.scala @@ -0,0 +1,672 @@ +package ir.eval +import ir.transforms.Substitute +import ir._ +import ir.eval.BitVectorEval.* +import ir.* +import util.IRContext +import util.Logger +import util.functional.* +import util.functional.State.* +import boogie.Scope +import collection.mutable.ArrayBuffer + +import scala.annotation.tailrec +import scala.collection.mutable +import scala.collection.immutable +import scala.util.control.Breaks.{break, breakable} +import translating.ELFSymbol + +/** Abstraction for memload and variable lookup used by the expression evaluator. + */ +case class StVarLoader[S, F <: Effects[S, InterpreterError]](f: F) extends Loader[S, InterpreterError] { + + def getVariable(v: Variable): State[S, Option[Literal], InterpreterError] = { + for { + v <- f.loadVar(v.name) + } yield ((v match { + case Scalar(l) => Some(l) + case _ => None + })) + } + + override def loadMemory( + m: Memory, + addr: Expr, + endian: Endian, + size: Int + ): State[S, Option[Literal], InterpreterError] = { + for { + r <- addr match { + case l: Literal if size == 1 => + Eval + .loadSingle(f)(m.name, Scalar(l)) + .map((v: BasilValue) => + v match { + case Scalar(l) => Some(l) + case _ => None + } + ) + case l: Literal => Eval.loadBV(f)(m.name, Scalar(l), endian, size).map(Some(_)) + case _ => get((s: S) => None) + } + } yield (r) + } + +} + +/* + * Helper functions for compiling high level structures to the interpreter effects. + * All are parametric in concrete state S and Effects[S] + */ +case object Eval { + + /*--------------------------------------------------------------------------------*/ + /* Eval functions */ + /*--------------------------------------------------------------------------------*/ + + // def evalExpr[S, T <: Effects[S, InterpreterError]](f: T)(e: Expr): State[S, Expr, InterpreterError] = { + // val ldr = StVarLoader[S, T](f) + + // val varbs = e.variables.toList + // val vars = varbs.map(v => ldr.getVariable(v)) + + // for { + // vs <- State.mapM(ldr.getVariable, varbs) + // vvs = varbs.zip(vs).filter(_._2.isDefined).map(l => (l._1, l._2.get)).toMap + // substed = Substitute(vvs.get)(e).flatMap(evaluateExpr) + // res <- substed match { + // case Some(r) => State.pure(r) + // case None => State.pure(e) + // } + // } yield (res) + // } + // + + def evalExpr[S, T <: Effects[S, InterpreterError]](f: T)(e: Expr): State[S, Expr, InterpreterError] = { + val ldr = StVarLoader[S, T](f) + for { + res <- ir.eval.statePartialEvalExpr[S](ldr)(e) + } yield (res) + } + + def evalLiteral[S, T <: Effects[S, InterpreterError]](f: T)(e: Expr): State[S, Literal, InterpreterError] = { + for { + res <- evalExpr(f)(e) + r <- State.pureE(res match { + case l: Literal => Right(l) + case _ => Left((Errored(s"Eval BV residual $e"))) + }) + } yield (r) + } + + def evalBV[S, T <: Effects[S, InterpreterError]](f: T)(e: Expr): State[S, BitVecLiteral, InterpreterError] = { + for { + res <- evalExpr(f)(e) + r <- State.pureE(res match { + case l: BitVecLiteral => Right(l) + case _ => Left((Errored(s"Eval BV residual $e"))) + }) + } yield (r) + } + + def evalInt[S, T <: Effects[S, InterpreterError]](f: T)(e: Expr): State[S, BigInt, InterpreterError] = { + for { + res <- evalExpr(f)(e) + r <- State.pureE(res match { + case l: IntLiteral => Right(l.value) + case _ => Left((Errored(s"Eval Int residual $e"))) + }) + } yield (r) + } + + def evalBool[S, T <: Effects[S, InterpreterError]](f: T)(e: Expr): State[S, Boolean, InterpreterError] = { + for { + res <- evalExpr(f)(e) + r <- State.pureE(res match { + case l: BoolLit => Right(l == TrueLiteral) + case _ => Left((Errored(s"Eval Bool residual $e"))) + }) + } yield (r) + } + + /*--------------------------------------------------------------------------------*/ + /* Load functions */ + /*--------------------------------------------------------------------------------*/ + + def load[S, T <: Effects[S, InterpreterError]]( + f: T + )(vname: String, addr: Scalar, endian: Endian, count: Int): State[S, List[BasilValue], InterpreterError] = { + for { + _ <- + if (count == 0) then State.setError((Errored(s"Attempted fractional load"))) else State.pure(()) + keys <- State.mapM(((i: Int) => State.pureE(BasilValue.unsafeAdd(addr, i))), (0 until count)) + values <- f.loadMem(vname, keys.toList) + vals = endian match { + case Endian.LittleEndian => values.reverse + case Endian.BigEndian => values + } + } yield (vals.toList) + } + + /** Load and concat bitvectors */ + def loadBV[S, T <: Effects[S, InterpreterError]]( + f: T + )(vname: String, addr: Scalar, endian: Endian, size: Int): State[S, BitVecLiteral, InterpreterError] = for { + mem <- f.loadVar(vname) + x <- mem match { + case mapv @ BasilMapValue(_, MapType(_, BitVecType(sz))) => State.pure((sz, mapv)) + case _ => State.setError((Errored("Trued to load-concat non bv"))) + } + (valsize, mapv) = x + + cells = size / valsize + + res <- load(f)(vname, addr, endian, cells) // actual load + bvs: List[BitVecLiteral] <- ( + State.mapM( + (c: BasilValue) => + c match { + case Scalar(bv @ BitVecLiteral(v, sz)) if sz == valsize => State.pure(bv) + case c => + State.setError( + TypeError(s"Loaded value of type ${c.irType} did not match expected type bv$valsize") + ) + }, + res + ) + ) + } yield (bvs.foldLeft(BitVecLiteral(0, 0))((acc, r) => eval.evalBVBinExpr(BVCONCAT, acc, r))) + + def loadSingle[S, T <: Effects[S, InterpreterError]]( + f: T + )(vname: String, addr: Scalar): State[S, BasilValue, InterpreterError] = { + for { + m <- load(f)(vname, addr, Endian.LittleEndian, 1) + } yield (m.head) + } + + /*--------------------------------------------------------------------------------*/ + /* Store functions */ + /*--------------------------------------------------------------------------------*/ + + /* Expand addr for number of values to store */ + def store[S, T <: Effects[S, InterpreterError]](f: T)( + vname: String, + addr: BasilValue, + values: List[BasilValue], + endian: Endian + )(implicit + line: sourcecode.Line, + file: sourcecode.FileName, + name: sourcecode.Name + ): State[S, Unit, InterpreterError] = + monlog.debug(s"store ${vname} ${values.size} bytes ${file.value}:${line.value}") + for { + mem <- f.loadVar(vname) + x <- mem match { + case m @ BasilMapValue(_, MapType(kt, vt)) + if Some(kt) == addr.irType && values.forall(v => v.irType == Some(vt)) => + State.pure((m, kt, vt)) + case v => State.setError((TypeError(s"Invalid map store operation to $vname : $v"))) + } + (mapval, keytype, valtype) = x + keys <- State.mapM((i: Int) => State.pureE(BasilValue.unsafeAdd(addr, i)), (0 until values.size)) + vals = endian match { + case Endian.LittleEndian => values.reverse + case Endian.BigEndian => values + } + x <- f.storeMem(vname, keys.zip(vals).toMap) + } yield (x) + + /** Extract bitvec to bytes and store bytes */ + def storeBV[S, T <: Effects[S, InterpreterError]](f: T)( + vname: String, + addr: BasilValue, + value: BitVecLiteral, + endian: Endian + )(implicit line: sourcecode.Line, file: sourcecode.FileName, name: sourcecode.Name): State[S, Unit, InterpreterError] = + + monlog.debug(s"storeBV ${vname} ${size(value).get / 8} bytes")(line, file, name) + for { + mem <- f.loadVar(vname) + mr <- mem match { + case m @ BasilMapValue(_, MapType(kt, BitVecType(size))) if Some(kt) == addr.irType => State.pure((m, size)) + case v => + State.setError( + TypeError( + s"Invalid map store operation to $vname : ${v.irType} (expect [${addr.irType}] <- ${value.getType})" + ) + ) + } + (mapval, vsize) = mr + cells = value.size / vsize + _ = { + if (cells < 1) { + State.setError((MemoryError("Tried to execute fractional store"))) + } else { + State.pure(()) + } + } + + extractVals = (0 until cells).map(i => BitVectorEval.boogie_extract((i + 1) * vsize, i * vsize, value)).toList + vs = endian match { + case Endian.LittleEndian => extractVals.map(Scalar(_)) + case Endian.BigEndian => extractVals.reverse.map(Scalar(_)) + } + + keys <- State.mapM((i: Int) => State.pureE(BasilValue.unsafeAdd(addr, i)), (0 until cells)) + s <- f.storeMem(vname, keys.zip(vs).toMap) + } yield (s) + + def storeSingle[S, E, T <: Effects[S, E]]( + f: T + )(vname: String, addr: BasilValue, value: BasilValue): State[S, Unit, E] = { + f.storeMem(vname, Map((addr -> value))) + } + + /** Helper functions * */ + + /** Load all memory cells from pointer until reaching cell containing 0. Ptr -> List[Bitvector] + */ + def getNullTerminatedString[S, T <: Effects[S, InterpreterError]]( + f: T + )(rgn: String, src: BasilValue, acc: List[BitVecLiteral] = List()): State[S, List[BitVecLiteral], InterpreterError] = + for { + srv: BitVecLiteral <- src match { + case Scalar(b: BitVecLiteral) => State.pure(b) + case _ => State.setError(Errored(s"Not pointer : $src")) + } + c <- f.loadMem(rgn, List(src)) + res <- c.head match { + case Scalar(BitVecLiteral(0, 8)) => State.pure(acc) + case Scalar(b: BitVecLiteral) => { + for { + nsrc <- State.pureE(BasilValue.unsafeAdd(src, 1)) + r <- getNullTerminatedString(f)(rgn, nsrc, acc.appended(b)) + } yield (r) + } + case _ => State.setError(Errored(s"not byte $c")) + } + } yield (res) +} + +class BASILInterpreter[S](f: Effects[S, InterpreterError]) extends Interpreter[S, InterpreterError](f) { + + def interpretOne: State[S, Boolean, InterpreterError] = { + val next = for { + next <- f.getNext + _ <- State.pure(Logger.debug(s"$next")) + r: Boolean <- (next match { + case Intrinsic(tgt) => LibcIntrinsic.intrinsics(tgt)(f).map(_ => true) + case Run(c: Statement) => interpretStatement(f)(c).map(_ => true) + case ReturnTo(c) => interpretReturn(f)(c).map(_ => true) + case Run(c: Jump) => interpretJump(f)(c).map(_ => true) + case Stopped() => State.pure(false) + case ErrorStop(e) => State.pure(false) + }) + } yield (r) + + next.flatMapE((e: InterpreterError) => { + f.setNext(ErrorStop(e)).map(_ => false) + }) + } + + def interpretJump[S, T <: Effects[S, InterpreterError]](f: T)(j: Jump): State[S, Unit, InterpreterError] = { + j match { + case gt: GoTo if gt.targets.size == 1 => { + f.setNext(Run(IRWalk.firstInBlock(gt.targets.head))) + } + case gt: GoTo => + val assumes = gt.targets.flatMap(_.statements.headOption).collect { case a: Assume => + a + } + for { + _ <- + if (assumes.size != gt.targets.size) { + State.setError((Errored(s"Some goto target missing guard $gt"))) + } else { + State.pure(()) + } + chosen: List[Assume] <- filterM((a: Assume) => Eval.evalBool(f)(a.body), assumes) + + res <- chosen match { + case Nil => State.setError(Errored(s"No jump target satisfied $gt")) + case h :: Nil => f.setNext(Run(h)) + case h :: tl => State.setError(Errored(s"More than one jump guard satisfied $gt")) + } + } yield (res) + case r: Return => { + // eval return values, return to caller, then bind return values to formal out params + for { + outs <- State.mapM( + ((bindout: (LocalVar, Expr)) => { + for { + rhs <- Eval.evalLiteral(f)(bindout._2) + } yield (bindout._1, rhs) + }), + r.outParams + ) + _ <- f.doReturn() + _ <- State.sequence(State.pure(()), outs.map(m => f.storeVar(m._1.name, m._1.toBoogie.scope, Scalar(m._2)))) + } yield () + } + case h: Unreachable => State.setError(EscapedControlFlow(h)) + } + } + + def interpretReturn[S, T <: Effects[S, InterpreterError]](f: T)(s: DirectCall): State[S, Unit, InterpreterError] = { + for { + outs <- State.mapM( + ((bindout: (LocalVar, Variable)) => { + for { + rhs <- Eval.evalLiteral(f)(bindout._1) + } yield (bindout._2, rhs) + }), + s.outParams + ) + c <- State.sequence(State.pure(()), outs.map(m => f.storeVar(m._1.name, m._1.toBoogie.scope, Scalar(m._2)))) + _ <- f.setNext(Run(s.successor)) + } yield (c) + } + + def interpretStatement[S, T <: Effects[S, InterpreterError]](f: T)(s: Statement): State[S, Unit, InterpreterError] = { + s match { + case assign: LocalAssign => { + for { + rhs <- Eval.evalLiteral(f)(assign.rhs) + st <- f.storeVar(assign.lhs.name, assign.lhs.toBoogie.scope, Scalar(rhs)) + n <- f.setNext(Run(s.successor)) + } yield (st) + } + case assign: MemoryLoad => { + for { + index <- Eval.evalBV(f)(assign.index) + loaded <- Eval.loadBV(f)(assign.mem.name, Scalar(index), assign.endian, assign.size) + st <- f.storeVar(assign.lhs.name, assign.lhs.toBoogie.scope, Scalar(loaded)) + n <- f.setNext(Run(s.successor)) + } yield (st) + } + case assign: MemoryStore => + for { + index: BitVecLiteral <- Eval.evalBV(f)(assign.index) + value: BitVecLiteral <- Eval.evalBV(f)(assign.value) + _ <- Eval.storeBV(f)(assign.mem.name, Scalar(index), value, assign.endian) + n <- f.setNext(Run(s.successor)) + } yield (n) + case assert: Assert => + for { + b <- Eval.evalBool(f)(assert.body) + _ <- + (if (!b) then { + State.setError(FailedAssertion(assert)) + } else { + f.setNext(Run(s.successor)) + }) + } yield () + case assume: Assume => + for { + b <- Eval.evalBool(f)(assume.body) + n <- + (if (!b) { + State.setError(Errored(s"Assumption not satisfied: $assume")) + } else { + f.setNext(Run(s.successor)) + }) + } yield (n) + case dc: DirectCall => { + for { + actualParams <- State.mapM( + (p: (LocalVar, Expr)) => + for { + v <- Eval.evalLiteral(f)(p._2) + } yield (p._1, v), + dc.actualParams + ) + _ <- { + if (LibcIntrinsic.intrinsics.contains(dc.target.procName)) { + f.call(dc.target.name, Intrinsic(dc.target.procName), ReturnTo(dc)) + } else if (dc.target.entryBlock.isDefined) { + val block = dc.target.entryBlock.get + f.call(dc.target.name, Run(block.statements.headOption.getOrElse(block.jump)), ReturnTo(dc)) + } else { + State.setError(EscapedControlFlow(dc)) + } + } + _ <- State.sequence( + State.pure(()), + actualParams.map(m => f.storeVar(m._1.name, m._1.toBoogie.scope, Scalar(m._2))) + ) + } yield () + } + case ic: IndirectCall => { + if (ic.target == Register("R30", 64)) { + f.doReturn() + } else { + for { + addr <- Eval.evalBV(f)(ic.target) + fp <- f.evalAddrToProc(addr.value.toInt) + _ <- fp match { + case Some(fp) => f.call(fp.name, fp.call, Run(ic.successor)) + case none => State.setError(EscapedControlFlow(ic)) + } + } yield () + } + } + case _: NOP => f.setNext(Run(s.successor)) + } + } +} + +object InterpFuns { + + def initRelocTable[S, T <: Effects[S, InterpreterError]](s: T)(ctx: IRContext): State[S, Unit, InterpreterError] = { + + val p = ctx.program + + val base = ctx.symbols.find(_.name == "__end__").get + var addr = base.value + var done = false + var x = List[(String, FunPointer)]() + + def newAddr(): BigInt = { + addr += 8 + addr + } + + for ((fname, fun) <- LibcIntrinsic.intrinsics) { + val name = fname.takeWhile(c => c != '@') + x = (name, FunPointer(BitVecLiteral(newAddr(), 64), name, Intrinsic(name))) :: x + } + + val intrinsics = x.toMap + + val procs = p.procedures.filter(proc => proc.address.isDefined) + + val fptrs = ctx.externalFunctions.toList + .sortBy(_.name) + .flatMap(f => { + intrinsics + .get(f.name) + .map(fp => (f.offset, fp)) + .orElse( + procs + .find(p => p.name == f.name) + .map(proc => + ( + f.offset, + FunPointer( + BitVecLiteral(proc.address.getOrElse(newAddr().toInt), 64), + proc.name, + Run(DirectCall(proc)) + ) + ) + ) + ) + }) + + // sort for deterministic trace + val stores = fptrs + .sortBy(f => f._1) + .map((p) => { + val (offset, fptr) = p + Eval.storeSingle(s)("ghost-funtable", Scalar(fptr.addr), fptr) + >> (Eval.storeBV(s)( + "mem", + Scalar(BitVecLiteral(offset, 64)), + fptr.addr, + Endian.LittleEndian + )) + }) + + for { + _ <- State.sequence(State.pure(()), stores) + malloc_top = BitVecLiteral(newAddr() + 1024, 64) + _ <- s.storeVar("ghost_malloc_top", Scope.Global, Scalar(malloc_top)) + } yield (()) + } + + /** Functions which compile BASIL IR down to the minimal interpreter effects. + * + * Each function takes as parameter an implementation of Effects[S] + */ + + def initialState[S, E, T <: Effects[S, E]](s: T): State[S, Unit, E] = { + val SP: BitVecLiteral = BitVecLiteral(0x78000000, 64) + val FP: BitVecLiteral = SP + val LR: BitVecLiteral = BitVecLiteral(BigInt("78000000", 16), 64) + + for { + h <- State.pure(Logger.debug("DEFINE MEMORY REGIONS")) + h <- s.storeVar("mem", Scope.Global, BasilMapValue(Map.empty, MapType(BitVecType(64), BitVecType(8)))) + i <- s.storeVar("stack", Scope.Global, BasilMapValue(Map.empty, MapType(BitVecType(64), BitVecType(8)))) + j <- s.storeVar("R31", Scope.Global, Scalar(SP)) + k <- s.storeVar("R29", Scope.Global, Scalar(FP)) + l <- s.storeVar("R30", Scope.Global, Scalar(LR)) + j <- s.storeVar("R31_in", Scope.Global, Scalar(SP)) + k <- s.storeVar("R29_in", Scope.Global, Scalar(FP)) + l <- s.storeVar("R30_in", Scope.Global, Scalar(LR)) + l <- s.storeVar("R0", Scope.Global, Scalar(BitVecLiteral(0, 64))) + l <- s.storeVar("R1", Scope.Global, Scalar(BitVecLiteral(0, 64))) + /** callee saved **/ + l <- s.storeVar("R19", Scope.Global, Scalar(BitVecLiteral(0, 64))) + l <- s.storeVar("R20", Scope.Global, Scalar(BitVecLiteral(0, 64))) + l <- s.storeVar("R21", Scope.Global, Scalar(BitVecLiteral(0, 64))) + l <- s.storeVar("R22", Scope.Global, Scalar(BitVecLiteral(0, 64))) + l <- s.storeVar("R23", Scope.Global, Scalar(BitVecLiteral(0, 64))) + l <- s.storeVar("R24", Scope.Global, Scalar(BitVecLiteral(0, 64))) + l <- s.storeVar("R25", Scope.Global, Scalar(BitVecLiteral(0, 64))) + l <- s.storeVar("R26", Scope.Global, Scalar(BitVecLiteral(0, 64))) + l <- s.storeVar("R27", Scope.Global, Scalar(BitVecLiteral(0, 64))) + l <- s.storeVar("R28", Scope.Global, Scalar(BitVecLiteral(0, 64))) + /** end callee saved **/ + _ <- s.storeVar("ghost-funtable", Scope.Global, BasilMapValue(Map.empty, MapType(BitVecType(64), BitVecType(64)))) + _ <- IntrinsicImpl.initFileGhostRegions(s) + } yield (l) + } + + def initialiseProgram[S, T <: Effects[S, InterpreterError]]( + f: T + )(is: S, p: Program): S = { + + def initMemory(is: S, mem: String, mems: Iterable[MemorySection]) : S = { + var s = is + for (memory <- mems.filter(m => m.address != 0 && m.bytes.size != 0)) { + val bytes = memory.bytes.toList.map(Scalar(_)) + val addrs = memory.address until memory.address + bytes.size + for (store <- addrs.zip(bytes)) { + val (addr,value) = store + s = State.execute( + s, + Eval.store(f)( + mem, + Scalar(BitVecLiteral(addr, 64)), + List(value), + Endian.BigEndian + ) + ) + } + } + s + } + + var s = State.execute(is, initialState(f)) + + for (proc <- p.procedures.filter(p => p.blocks.nonEmpty && p.address.isDefined)) { + s = State.execute(s, Eval.storeSingle(f)( + "ghost-funtable", + Scalar(BitVecLiteral(proc.address.get, 64)), + FunPointer(BitVecLiteral(proc.address.get, 64), proc.name, Run(IRWalk.firstInBlock(proc.entryBlock.get))) + ) + ) + } + + val mainfun = p.mainProcedure + s = State.execute(s, f.call("init_activation", Stopped(), Stopped())) + s = initMemory(s, "mem", p.initialMemory.values) + s = initMemory(s, "stack", p.initialMemory.values) + s = State.execute(s, f.call(mainfun.name, Run(IRWalk.firstInBlock(mainfun.entryBlock.get)), Stopped())) + // l <- State.sequence(State.pure(()), mainfun.formalInParam.toList.map(i => f.storeVar(i.name, i.toBoogie.scope, Scalar(BitVecLiteral(0, size(i).get))))) + s + } + + def initBSS[S, T <: Effects[S, InterpreterError]](f: T)(is: S, p: IRContext): S = { + val bss = for { + first <- p.symbols.find(s => s.name == "__bss_start__").map(_.value) + last <- p.symbols.find(s => s.name == "__bss_end__").map(_.value) + r <- (if (first == last) then None else Some((first, (last - first) * 8))) + (addr, sz) = r + st = { + var s = is + for (rgn <- Seq("mem", "stack")) { + for (addr <- (first until last)) { + s = State.execute(s, + Eval.storeBV(f)(rgn, Scalar(BitVecLiteral(addr, 64)), BitVecLiteral(0, 8), Endian.LittleEndian) + ) + } + } + s + } + + } yield (st) + + + bss match { + case None => Logger.error("No BSS initialised"); is + case Some(init) => init + } + } + + def initProgState[S, T <: Effects[S, InterpreterError]](f: T)(p: IRContext, is: S): S = { + var ist = initialiseProgram(f)(is, p.program) + ist = initBSS(f)(ist, p) + ist = State.execute(ist, InterpFuns.initRelocTable(f)(p)) + val st = State.putS(ist) + val (fs, v) = st.f(is) + v match { + case Right(r) => fs + case Left(e) => throw Exception(s"Init failed $e") + } + } + + /* Intialise from ELF and Interpret program */ + def interpretProg[S, T <: Effects[S, InterpreterError]](f: T)(p: IRContext, is: S): S = { + val begin = initProgState(f)(p, is) + val interp = BASILInterpreter(f) + interp.run(begin) + } + + /* Interpret IR program */ + def interpretProg[S, T <: Effects[S, InterpreterError]](f: T)(p: Program, is: S): S = { + val begin = initialiseProgram(f)(is, p) + val interp = BASILInterpreter(f) + interp.run(begin) + } +} + +def interpret(IRProgram: Program): InterpreterState = { + InterpFuns.interpretProg(NormalInterpreter)(IRProgram, InterpreterState()) +} + +def interpret(IRProgram: IRContext): InterpreterState = { + InterpFuns.interpretProg(NormalInterpreter)(IRProgram, InterpreterState()) +} diff --git a/src/main/scala/ir/eval/InterpretBreakpoints.scala b/src/main/scala/ir/eval/InterpretBreakpoints.scala new file mode 100644 index 000000000..ced1eecb8 --- /dev/null +++ b/src/main/scala/ir/eval/InterpretBreakpoints.scala @@ -0,0 +1,134 @@ +package ir.eval +import ir._ +import ir.eval.BitVectorEval.* +import ir.* +import util.Logger +import util.IRContext +import util.functional.* +import util.functional.State.* +import boogie.Scope +import scala.collection.WithFilter + +import scala.annotation.tailrec +import scala.collection.mutable +import scala.collection.immutable +import scala.util.control.Breaks.{break, breakable} + +enum BreakPointLoc: + case CMD(c: Command) + case CMDCond(c: Command, condition: Expr) + +case class BreakPointAction( + saveState: Boolean = true, + stop: Boolean = false, + evalExprs: List[(String, Expr)] = List(), + log: Boolean = false +) + +case class BreakPoint(name: String = "", location: BreakPointLoc, action: BreakPointAction) + +case class RememberBreakpoints[T, I <: Effects[T, InterpreterError]](f: I, breaks: List[BreakPoint]) + extends NopEffects[(T, List[(BreakPoint, Option[T], List[(String, Expr, Expr)])]), InterpreterError] { + + def findBreaks[R](c: Command): State[(T, R), List[BreakPoint], InterpreterError] = { + State.filterM( + b => + b.location match { + case BreakPointLoc.CMD(bc) if (bc == c) => State.pure(true) + case BreakPointLoc.CMDCond(bc, e) if bc == c => doLeft(Eval.evalBool(f)(e)) + case _ => State.pure(false) + }, + breaks + ) + } + + override def getNext: State[ + (T, List[(BreakPoint, Option[T], List[(String, Expr, Expr)])]), + ExecutionContinuation, + InterpreterError + ] = { + for { + v: ExecutionContinuation <- doLeft(f.getNext) + n <- v match { + case Run(s) => + for { + breaks: List[BreakPoint] <- findBreaks(s) + res <- State + .sequence[(T, List[(BreakPoint, Option[T], List[(String, Expr, Expr)])]), Unit, InterpreterError]( + State.pure(()), + breaks.map((breakpoint: BreakPoint) => + (breakpoint match { + case breakpoint @ BreakPoint(name, stopcond, action) => ( + for { + saved <- doLeft( + if action.saveState then State.getS[T, InterpreterError].map(s => Some(s)) + else State.pure(None) + ) + evals <- (State.mapM( + (e: (String, Expr)) => + for { + ev <- doLeft(Eval.evalExpr(f)(e._2)) + } yield (e._1, e._2, ev), + action.evalExprs + )) + _ <- State.pure({ + if (action.log) { + val bpn = breakpoint.name + val bpcond = breakpoint.location match { + case BreakPointLoc.CMD(c) => s"${c.parent.label}:$c" + case BreakPointLoc.CMDCond(c, e) => s"${c.parent.label}:$c when $e" + } + val saving = if action.saveState then " stashing state, " else "" + val stopping = if action.stop then " stopping. " else "" + val evalstr = evals.map(e => s"\n ${e._1} : eval(${e._2}) = ${e._3}").mkString("") + Logger.warn(s"Breakpoint $bpn@$bpcond.$saving$stopping$evalstr") + } + }) + _ <- + if action.stop then doLeft(f.setNext(ErrorStop(Errored(s"Stopped at breakpoint ${name}")))) + else doLeft(State.pure(())) + _ <- State.modify((istate: (T, List[(BreakPoint, Option[T], List[(String, Expr, Expr)])])) => + (istate._1, ((breakpoint, saved, evals) :: istate._2)) + ) + } yield () + ) + }) + ) + ) + } yield () + case _ => State.pure(()) + } + } yield (v) + } +} + +def interpretWithBreakPoints[I]( + p: IRContext, + breakpoints: List[BreakPoint], + innerInterpreter: Effects[I, InterpreterError], + innerInitialState: I +): (I, List[(BreakPoint, Option[I], List[(String, Expr, Expr)])]) = { + val interp = LayerInterpreter(innerInterpreter, RememberBreakpoints(innerInterpreter, breakpoints)) + val res = InterpFuns.interpretProg(interp)(p, (innerInitialState, List())) + res +} + +def interpretWithBreakPoints[I]( + p: Program, + breakpoints: List[BreakPoint], + innerInterpreter: Effects[I, InterpreterError], + innerInitialState: I +): (I, List[(BreakPoint, Option[I], List[(String, Expr, Expr)])]) = { + val interp = LayerInterpreter(innerInterpreter, RememberBreakpoints(innerInterpreter, breakpoints)) + val res = InterpFuns.interpretProg(interp)(p, (innerInitialState, List())) + res +} + +def interpretBreakPoints(p: IRContext, breakpoints: List[BreakPoint]) = { + interpretWithBreakPoints(p, breakpoints, NormalInterpreter, InterpreterState()) +} + + +def interpretBreakPoints(p: Program, breakpoints: List[BreakPoint]) = { + interpretWithBreakPoints(p, breakpoints, NormalInterpreter, InterpreterState()) +} diff --git a/src/main/scala/ir/eval/InterpretRLimit.scala b/src/main/scala/ir/eval/InterpretRLimit.scala new file mode 100644 index 000000000..a381e5d8f --- /dev/null +++ b/src/main/scala/ir/eval/InterpretRLimit.scala @@ -0,0 +1,52 @@ + +package ir.eval +import ir._ +import ir.eval.BitVectorEval.* +import ir.* +import util.IRContext +import util.Logger +import util.functional.* +import util.functional.State.* +import boogie.Scope +import scala.collection.WithFilter + +import scala.annotation.tailrec +import scala.collection.mutable +import scala.collection.immutable +import scala.util.control.Breaks.{break, breakable} + + +case class EffectsRLimit[T, E, I <: Effects[T, InterpreterError]](val limit: Int) extends NopEffects[(T, Int), InterpreterError] { + + override def getNext :State[(T, Int), ExecutionContinuation, InterpreterError] = { + for { + c : (T, Int) <- State.getS + (is, resources) = c + _ <- if (resources >= limit && limit >= 0) { + State.setError(Errored(s"Resource limit $limit reached")) + } else { + State.modify ((s : (T, Int)) => (s._1, s._2 + 1)) + } + } yield (Stopped()) // thrown away by LayerInterpreter + } +} + +def interpretWithRLimit[I](p: Program, instructionLimit: Int, innerInterpreter: Effects[I, InterpreterError], innerInitialState: I): (I, Int) = { + val rlimitInterpreter = LayerInterpreter(innerInterpreter, EffectsRLimit(instructionLimit)) + InterpFuns.interpretProg(rlimitInterpreter)(p, (innerInitialState, 0)) +} + +def interpretWithRLimit[I](p: IRContext, instructionLimit: Int, innerInterpreter: Effects[I, InterpreterError], innerInitialState: I): (I, Int) = { + val rlimitInterpreter = LayerInterpreter(innerInterpreter, EffectsRLimit(instructionLimit)) + val begin = InterpFuns.initProgState(rlimitInterpreter)(p, (innerInitialState, 0)) + // throw away initialisation trace + BASILInterpreter(rlimitInterpreter).run((begin._1, 0)) +} + +def interpretRLimit(p: Program, instructionLimit: Int) : (InterpreterState, Int) = { + interpretWithRLimit(p, instructionLimit, NormalInterpreter, InterpreterState()) +} + +def interpretRLimit(p: IRContext, instructionLimit: Int) : (InterpreterState, Int) = { + interpretWithRLimit(p, instructionLimit, NormalInterpreter, InterpreterState()) +} diff --git a/src/main/scala/ir/eval/InterpretTrace.scala b/src/main/scala/ir/eval/InterpretTrace.scala new file mode 100644 index 000000000..89b0fc976 --- /dev/null +++ b/src/main/scala/ir/eval/InterpretTrace.scala @@ -0,0 +1,78 @@ +package ir.eval +import ir._ +import ir.eval.BitVectorEval.* +import ir.* +import util.IRContext +import util.Logger +import util.functional.* +import util.functional.State.* +import boogie.Scope +import scala.collection.WithFilter + +import scala.annotation.tailrec +import scala.collection.mutable +import scala.collection.immutable +import scala.util.control.Breaks.{break, breakable} + +enum ExecEffect: + case Call(target: String, begin: ExecutionContinuation, returnTo: ExecutionContinuation) + case Return + case StoreVar(v: String, s: Scope, value: BasilValue) + case LoadVar(v: String) + case StoreMem(vname: String, update: Map[BasilValue, BasilValue]) + case LoadMem(vname: String, addrs: List[BasilValue]) + case FindProc(addr: Int) + +case class Trace(val t: List[ExecEffect]) + +case object Trace { + def add[E](e: ExecEffect): State[Trace, Unit, E] = { + State.modify((t: Trace) => Trace(t.t.appended(e))) + } +} + +case class TraceGen[E]() extends NopEffects[Trace, E] { + + /** Values are discarded by ProductInterpreter so do not matter */ + override def loadMem(v: String, addrs: List[BasilValue]) = for { + s <- Trace.add(ExecEffect.LoadMem(v, addrs)) + } yield (List()) + + override def call(target: String, beginFrom: ExecutionContinuation, returnTo: ExecutionContinuation) = for { + s <- Trace.add(ExecEffect.Call(target, beginFrom, returnTo)) + } yield (()) + + override def doReturn() = for { + s <- Trace.add(ExecEffect.Return) + } yield (()) + + override def storeVar(v: String, scope: Scope, value: BasilValue) = for { + s <- if (!v.startsWith("ghost")) Trace.add(ExecEffect.StoreVar(v, scope, value)) else State.pure(()) + } yield (()) + + override def storeMem(vname: String, update: Map[BasilValue, BasilValue]) = for { + s <- if (!vname.startsWith("ghost")) Trace.add(ExecEffect.StoreMem(vname, update)) else State.pure(()) + } yield (()) + +} + +def tracingInterpreter[I, E](innerInterpreter: Effects[I, E]) = ProductInterpreter(innerInterpreter, TraceGen()) + +def interpretWithTrace[I](p: Program, innerInterpreter: Effects[I, InterpreterError], innerInitialState: I): (I, Trace) = { + InterpFuns.interpretProg(tracingInterpreter(innerInterpreter))(p, (innerInitialState, Trace(List()))) +} + +def interpretWithTrace[I](p: IRContext, innerInterpreter: Effects[I, InterpreterError], innerInitialState: I): (I, Trace) = { + val tracingInterpreter = ProductInterpreter(innerInterpreter, TraceGen()) + val begin = InterpFuns.initProgState(tracingInterpreter)(p, (innerInitialState, Trace(List()))) + // throw away initialisation trace + BASILInterpreter(tracingInterpreter).run((begin._1, Trace(List()))) +} + +def interpretTrace(p: Program) = { + interpretWithTrace(p, NormalInterpreter, InterpreterState()) +} + +def interpretTrace(p: IRContext) = { + interpretWithTrace(p, NormalInterpreter, InterpreterState()) +} diff --git a/src/main/scala/ir/eval/Interpreter.scala b/src/main/scala/ir/eval/Interpreter.scala new file mode 100644 index 000000000..0690d8a54 --- /dev/null +++ b/src/main/scala/ir/eval/Interpreter.scala @@ -0,0 +1,615 @@ +package ir.eval +import ir.eval.BitVectorEval.* +import ir.* +import util.Logger +import util.functional.* +import util.functional.State.* +import boogie.Scope +import scala.collection.WithFilter +import translating.PrettyPrinter.* + +import scala.annotation.tailrec +import scala.collection.mutable +import scala.collection.immutable +import scala.util.control.Breaks.{break, breakable} + +/** Interpreter status type, either stopped, run next command or error + */ +sealed trait ExecutionContinuation +case class Stopped() extends ExecutionContinuation /* normal program stop */ +case class ErrorStop(error: InterpreterError) extends ExecutionContinuation /* program stop in error state */ +case class Run(next: Command) extends ExecutionContinuation /* continue by executing next command */ +case class ReturnTo(call: DirectCall) extends ExecutionContinuation /* continue by executing next command */ +case class Intrinsic(name: String) extends ExecutionContinuation /* a named intrinsic instruction */ + +sealed trait InterpreterError +case class FailedAssertion(a: Assert) extends InterpreterError +case class EscapedControlFlow(call: Jump | Call) + extends InterpreterError /* controlflow has reached somewhere eunrecoverable */ +case class Errored(message: String = "") extends InterpreterError +case class TypeError(message: String = "") extends InterpreterError /* type mismatch appeared */ +case class EvalError(message: String = "") + extends InterpreterError /* failed to evaluate an expression to a concrete value */ +case class MemoryError(message: String = "") extends InterpreterError /* An error to do with memory */ + +/* Concrete value type of the interpreter. */ +sealed trait BasilValue(val irType: Option[IRType]) +case class Scalar(value: Literal) extends BasilValue(Some(value.getType)) { + override def toString = pp_expr(value) +} + +/* Abstract callable function address */ +case class FunPointer(addr: BitVecLiteral, name: String, call: ExecutionContinuation) + extends BasilValue(Some(addr.getType)) + +sealed trait MapValue { + def value: Map[BasilValue, BasilValue] +} + +/* We erase the type of basil values and enforce the invariant that + \exists i . \forall v \in value.keys , v.irType = i and + \exists j . \forall v \in value.values, v.irType = j + */ +case class BasilMapValue(value: Map[BasilValue, BasilValue], mapType: MapType) + extends MapValue + with BasilValue(Some(mapType)) { + override def toString = s"MapValue : $irType" +} + +case class GenMapValue(val value: Map[BasilValue, BasilValue]) extends BasilValue(None) with MapValue { + override def toString = s"GenMapValue : $irType" +} + +case class Symbol(val value: String) extends BasilValue(None) + +case object BasilValue { + + def size(v: IRType): Int = { + v match { + case BitVecType(sz) => sz + case _ => 1 + } + } + + def toBV[S, E](l: BasilValue): Either[InterpreterError, BitVecLiteral] = { + l match { + case Scalar(b1: BitVecLiteral) => Right(b1) + case _ => Left((TypeError(s"Not a bitvector add $l"))) + } + } + + def unsafeAdd[S, E](l: BasilValue, vr: Int): Either[InterpreterError, BasilValue] = { + l match { + case _ if vr == 0 => Right(l) + case Scalar(IntLiteral(vl)) => Right(Scalar(IntLiteral(vl + vr))) + case Scalar(b1: BitVecLiteral) => Right(Scalar(eval.evalBVBinExpr(BVADD, b1, BitVecLiteral(vr, b1.size)))) + case _ => Left((TypeError(s"Operation add $vr undefined on $l"))) + } + } + + def add[S, E](l: BasilValue, r: BasilValue): Either[InterpreterError, BasilValue] = { + (l, r) match { + case (Scalar(IntLiteral(vl)), Scalar(IntLiteral(vr))) => Right(Scalar(IntLiteral(vl + vr))) + case (Scalar(b1: BitVecLiteral), Scalar(b2: BitVecLiteral)) => Right(Scalar(eval.evalBVBinExpr(BVADD, b1, b2))) + case _ => Left((TypeError(s"Operation add undefined $l + $r"))) + } + } + +} + +/** Minimal language defining all state transitions in the interpreter, defined for the interpreter's concrete state T. + */ +trait Effects[T, E] { + /* expression eval */ + + def loadVar(v: String): State[T, BasilValue, E] + + def loadMem(v: String, addrs: List[BasilValue]): State[T, List[BasilValue], E] + + def evalAddrToProc(addr: Int): State[T, Option[FunPointer], E] + + def getNext: State[T, ExecutionContinuation, E] + + /** state effects */ + + /* High-level implementation of a program counter that leverages the intrusive CFG. */ + def setNext(c: ExecutionContinuation): State[T, Unit, E] + + /* Perform a call: + * target: arbitrary target name + * beginFrom: ExecutionContinuation which begins executing the procedure + * returnTo: ExecutionContinuation which begins executing after procedure return + */ + def call(target: String, beginFrom: ExecutionContinuation, returnTo: ExecutionContinuation): State[T, Unit, E] + + def callIntrinsic(name: String, args: List[BasilValue]): State[T, Option[BasilValue], E] + + def doReturn(): State[T, Unit, E] + + def storeVar(v: String, scope: Scope, value: BasilValue): State[T, Unit, E] + + def storeMem(vname: String, update: Map[BasilValue, BasilValue]): State[T, Unit, E] +} + +trait NopEffects[T, E] extends Effects[T, E] { + def loadVar(v: String) = State.pure(Scalar(FalseLiteral)) + def loadMem(v: String, addrs: List[BasilValue]) = State.pure(List()) + def evalAddrToProc(addr: Int) = State.pure(None) + def getNext = State.pure(Stopped()) + def setNext(c: ExecutionContinuation) = State.pure(()) + + def call(target: String, beginFrom: ExecutionContinuation, returnTo: ExecutionContinuation) = State.pure(()) + def callIntrinsic(name: String, args: List[BasilValue]) = State.pure(None) + def doReturn() = State.pure(()) + + def storeVar(v: String, scope: Scope, value: BasilValue) = State.pure(()) + def storeMem(vname: String, update: Map[BasilValue, BasilValue]) = State.pure(()) +} + +/*-------------------------------------------------------------------------------- + * Definition of concrete state + *--------------------------------------------------------------------------------*/ + +type StackFrameID = String +val globalFrame: StackFrameID = "GLOBAL" + +case class MemoryState( + /* We have a very permissive value reprsentation and store all dynamic state in `stackFrames`. + * - activations is the call stack, the top of which indicates the current stackFrame. + * - activationCount: (procedurename -> int) is used to create uniquely-named stackframes. + */ + val stackFrames: Map[StackFrameID, Map[String, BasilValue]] = Map((globalFrame -> Map.empty)), + val activations: List[StackFrameID] = List.empty, + val activationCount: Map[String, Int] = Map.empty.withDefault(_ => 0) +) { + + /** Debug return useful values * */ + + def getGlobalVals: Map[String, BitVecLiteral] = { + stackFrames(globalFrame).collect { case (k, Scalar(b: BitVecLiteral)) => + k -> b + } + } + + def getMem(name: String): Map[BitVecLiteral, BitVecLiteral] = { + stackFrames(globalFrame)(name) match { + case BasilMapValue(innerMap, MapType(BitVecType(ks), BitVecType(vs))) => { + def unwrap(v: BasilValue): BitVecLiteral = v match { + case Scalar(b: BitVecLiteral) => b + case v => throw Exception(s"Failed to convert map value to bitvector: $v (interpreter type error somewhere)") + } + innerMap.map((k, v) => unwrap(k) -> unwrap(v)) + } + case v => throw Exception(s"$name not a bitvec map variable: ${v.irType}") + } + } + + /** Local Variable Stack * */ + + def pushStackFrame(function: String): MemoryState = { + val counts = activationCount + (function -> (activationCount(function) + 1)) + val frameName: StackFrameID = s"AR_${function}_${activationCount(function)}" + val frames = stackFrames + (frameName -> Map.empty) + MemoryState(frames, frameName :: activations, counts) + } + + def popStackFrame(): Either[InterpreterError, MemoryState] = { + val hv = activations match { + case Nil => Left((Errored("No stack frame to pop"))) + case h :: Nil if h == globalFrame => Left((Errored("tried to pop global scope"))) + case h :: tl => Right((h, tl)) + } + hv.map((hv) => { + val (frame, remactivs) = hv + val frames = stackFrames.removed(frame) + MemoryState(frames, remactivs, activationCount) + }) + } + + /* Variable retrieval and setting */ + + /* Set variable in a given frame */ + def setVar(frame: StackFrameID, varname: String, value: BasilValue): MemoryState = { + val nv = stackFrames + (frame -> (stackFrames(frame) + (varname -> value))) + MemoryState(nv, activations, activationCount) + } + + /* Find variable definition scope and set it in the correct frame */ + def setVar(v: String, value: BasilValue): MemoryState = { + val frame = findVarOpt(v).map(_._1).getOrElse(activations.head) + setVar(frame, v, value) + } + + /* Define a variable in the scope specified + * ignoring whether it may already be defined + */ + def defVar(name: String, s: Scope, value: BasilValue): MemoryState = { + val frame = s match { + case Scope.Global => globalFrame + case _ => activations.head + } + setVar(frame, name, value) + } + + /* Lookup the value of a variable */ + def findVarOpt(name: String): Option[(StackFrameID, BasilValue)] = { + val searchScopes = globalFrame :: activations.headOption.toList + searchScopes.foldRight(None: Option[(StackFrameID, BasilValue)])((r, acc) => + acc match { + case None => stackFrames(r).get(name).map(v => (r, v)) + case s => s + } + ) + } + + def findVar(name: String): Either[InterpreterError, (StackFrameID, BasilValue)] = { + findVarOpt(name: String) + .map(Right(_)) + .getOrElse(Left((Errored(s"Access to undefined variable $name")))) + } + + def getVarOpt(name: String): Option[BasilValue] = findVarOpt(name).map(_._2) + + def getVar(name: String): Either[InterpreterError, BasilValue] = { + getVarOpt(name).map(Right(_)).getOrElse(Left((Errored(s"Access undefined variable $name")))) + } + + def getVar(v: Variable): Either[InterpreterError, BasilValue] = { + val value = getVar(v.name) + value match { + case Right(dv: BasilValue) if Some(v.getType) != dv.irType => + Left(Errored(s"Type mismatch on variable definition and load: defined ${dv.irType}, variable ${v.getType}")) + case Right(o) => Right(o) + case o => o + } + } + + /* Map variable accessing ; load and store operations */ + def doLoad(vname: String, addr: List[BasilValue]): Either[InterpreterError, List[BasilValue]] = for { + v <- findVar(vname) + mapv: MapValue <- v._2 match { + case m: MapValue => Right(m) + case m => Left((TypeError(s"Load from nonmap ${m.irType}"))) + } + rs: List[Option[BasilValue]] = addr.map(k => mapv.value.get(k)) + xs <- + (if (rs.forall(_.isDefined)) { + Right(rs.map(_.get)) + } else { + Left((MemoryError(s"Read from uninitialised $vname[${addr.head} .. ${addr.last}]"))) + }) + } yield (xs) + + /** typecheck and some fields of a map variable */ + def doStore(vname: String, values: Map[BasilValue, BasilValue]): Either[InterpreterError, MemoryState] = for { + + _ <- if (values.size == 0) then Left(MemoryError("Tried to store size 0")) else Right(()) + v <- findVar(vname) + (frame, mem) = v + mapval <- mem match { + case m @ BasilMapValue(_, MapType(kt, vt)) => + for { + m <- (values.find((k, v) => k.irType != Some(kt) || v.irType != Some(vt))) match { + case Some(v) => + Left( + TypeError( + s"Invalid addr or value type (${v._1.irType}, ${v._2.irType}) does not match map type $vname : ($kt, $vt)" + ) + ) + case None => Right(m) + } + nm = BasilMapValue(m.value ++ values, m.mapType) + } yield (nm) + case m @ GenMapValue(_) => { + Right(GenMapValue(m.value ++ values)) + } + case v => Left((TypeError(s"Invalid map store operation to $vname : ${v.irType}"))) + } + + ms <- Right(setVar(frame, vname, mapval)) + } yield (ms) +} + +object LibcIntrinsic { + // TODO: make parameter passing work + + /** + * Part of the intrinsics implementation that lives above the Effects interface + * (i.e. we are defining the observable part of the intrinsics behaviour) + */ + + def singleArg[S, E, T <: Effects[S, E]](name: String)(s: T): State[S, Unit, E] = for { + c <- s.loadVar("R0") + res <- s.callIntrinsic(name, List(c)) + _ <- if res.isDefined then s.storeVar("R0", Scope.Global, res.get) else State.pure(()) + _ <- s.doReturn() + } yield (()) + + def twoArg[S, E, T <: Effects[S, E]](name: String)(s: T): State[S, Unit, E] = for { + c1 <- s.loadVar("R0") + c2 <- s.loadVar("R1") + res <- s.callIntrinsic(name, List(c1, c2)) + _ <- if res.isDefined then s.storeVar("R0", Scope.Global, res.get) else State.pure(()) + _ <- s.doReturn() + } yield (()) + + def calloc[S, T <: Effects[S, InterpreterError]](s: T): State[S, Unit, InterpreterError] = for { + size <- s.loadVar("R0") + res <- s.callIntrinsic("malloc", List(size)) + ptr = res.get + isize <- size match { + case Scalar(b: BitVecLiteral) => State.pure(b.value * 8) + case _ => State.setError(Errored("programmer error")) + } + cl <- Eval.storeBV(s)("mem", ptr, BitVecLiteral(0, isize.toInt), Endian.LittleEndian) + _ <- s.doReturn() + } yield (()) + + def intrinsics[S, T <: Effects[S, InterpreterError]] = + Map[String, T => State[S, Unit, InterpreterError]]( + "putc" -> singleArg("putc"), + "putchar" -> singleArg("putc"), + "puts" -> singleArg("puts"), + "printf" -> singleArg("print"), + "write" -> twoArg("write"), + "malloc" -> singleArg("malloc"), + "__libc_malloc_impl" -> singleArg("malloc"), + "free" -> singleArg("free"), + "#free" -> singleArg("free"), + "calloc" -> calloc + ) + +} + +object IntrinsicImpl { + + /** state initialisation for file modelling */ + def initFileGhostRegions[S, E, T <: Effects[S, E]](f: T): State[S, Unit, E] = for { + _ <- f.storeVar("ghost-file-bookkeeping", Scope.Global, GenMapValue(Map.empty)) + _ <- f.storeVar("ghost-fd-mapping", Scope.Global, GenMapValue(Map.empty)) + _ <- f.storeMem("ghost-file-bookkeeping", Map(Symbol("$$filecount") -> Scalar(BitVecLiteral(0, 64)))) + _ <- f.callIntrinsic("fopen", List(Symbol("stderr"))) + _ <- f.callIntrinsic("fopen", List(Symbol("stdout"))) + } yield (()) + + /** Intrinsics defined over arbitrary effects + * + * We call these from Effects[T, E] rather than the Interpreter so their implementation does not appear in the trace. + */ + def putc[S, T <: Effects[S, InterpreterError]](f: T)(arg: BasilValue): State[S, Option[BasilValue], InterpreterError] = { + for { + addr <- f.loadMem("ghost-file-bookkeeping", List(Symbol("stdout-ptr"))) + byte <- State.pureE(BasilValue.toBV(arg)) + c <- Eval.evalBV(f)(Extract(8, 0, byte)) + _ <- f.storeMem("stdout", Map(addr.head -> Scalar(c))) + naddr <- State.pureE(BasilValue.unsafeAdd(addr.head, 1)) + _ <- f.storeMem("ghost-file-bookkeeping", Map(Symbol("stdout-ptr") -> naddr)) + } yield (None) + } + + def fopen[S, T <: Effects[S, InterpreterError]](f: T)(file: BasilValue): State[S, Option[BasilValue], InterpreterError] = { + for { + fname <- file match { + case Symbol(name) => State.pure(name) + case _ => State.setError(Errored("Intrinsic fopen open not given filename")) + } + _ <- f.storeVar(fname, Scope.Global, BasilMapValue(Map.empty, MapType(BitVecType(64), BitVecType(8)))) + filecount <- f.loadMem("ghost-file-bookkeeping", List(Symbol("$$filecount"))) + _ <- f.storeMem("ghost-file-bookkeeping", Map(Symbol(fname + "-ptr") -> Scalar(BitVecLiteral(0, 64)))) + _ <- f.storeMem("ghost-fd-mapping", Map(filecount.head -> Symbol(fname + "-ptr"))) + _ <- f.storeVar("R0", Scope.Global, filecount.head) + nfilecount <- State.pureE(BasilValue.unsafeAdd(filecount.head, 1)) + _ <- f.storeMem("ghost-file-bookkeeping", Map(Symbol("$$filecount") -> nfilecount)) + } yield (Some(filecount.head)) + } + + + def write[S, T <: Effects[S, InterpreterError]](f: T)(fd: BasilValue, strptr: BasilValue): State[S, Option[BasilValue], InterpreterError] = { + for { + str <- Eval.getNullTerminatedString(f)("mem", strptr) + // TODO: fd mapping in state + file = fd match { + case Scalar(BitVecLiteral(1, 64)) => "stdout" + case Scalar(BitVecLiteral(2, 64)) => "stderr" + case _ => "unknown" + } + baseptr: List[BasilValue] <- f.loadMem("ghost-file-bookkeeping", List(Symbol(s"${file}-ptr"))) + offs: List[BasilValue] <- State.mapM( + ((i: Int) => State.pureE(BasilValue.unsafeAdd(baseptr.head, i))), + (0 until (str.size + 1)) + ) + _ <- f.storeMem(file, offs.zip(str.map(Scalar(_))).toMap) + naddr <- State.pureE(BasilValue.unsafeAdd(baseptr.head, str.size)) + _ <- f.storeMem("ghost-file-bookkeeping", Map(Symbol(s"${file}-ptr") -> naddr)) + } yield (None) + } + + def print[S, T <: Effects[S, InterpreterError]](f: T)(strptr: BasilValue): State[S, Option[BasilValue], InterpreterError] = { + for { + str <- Eval.getNullTerminatedString(f)("mem", strptr) + baseptr: List[BasilValue] <- f.loadMem("ghost-file-bookkeeping", List(Symbol("stdout-ptr"))) + offs: List[BasilValue] <- State.mapM( + ((i: Int) => State.pureE(BasilValue.unsafeAdd(baseptr.head, i))), + (0 until (str.size + 1)) + ) + _ <- f.storeMem("stdout", offs.zip(str.map(Scalar(_))).toMap) + naddr <- State.pureE(BasilValue.unsafeAdd(baseptr.head, str.size)) + _ <- f.storeMem("ghost-file-bookkeeping", Map(Symbol("stdout-ptr") -> naddr)) + } yield (None) + } + + def malloc[S, T <: Effects[S, InterpreterError]](f: T)(size: BasilValue): State[S, Option[BasilValue], InterpreterError] = { + for { + size <- (size match { + case (x @ Scalar(_: BitVecLiteral)) => State.pure(x) + case (Scalar(x: IntLiteral)) => State.pure(Scalar(BitVecLiteral(x.value, 64))) + case _ => State.setError(Errored("illegal prim arg")) + }) + x <- f.loadVar("ghost_malloc_top") + x_gap <- State.pureE(BasilValue.unsafeAdd(x, 128)) // put a gap around allocations to catch buffer overflows + x_end <- State.pureE(BasilValue.add(x_gap, size)) + _ <- f.storeVar("ghost_malloc_top", Scope.Global, x_end) + _ <- f.storeVar("R0", Scope.Global, x_gap) + } yield (Some(x_gap)) + } +} + +case class InterpreterState( + val nextCmd: ExecutionContinuation = Stopped(), + val callStack: List[ExecutionContinuation] = List.empty, + val memoryState: MemoryState = MemoryState() +) + +/** Implementation of Effects for InterpreterState concrete state representation. + */ +object NormalInterpreter extends Effects[InterpreterState, InterpreterError] { + + + def callIntrinsic( + name: String, + args: List[BasilValue] + ): State[InterpreterState, Option[BasilValue], InterpreterError] = { + name match { + case "free" => State.pure(None) + case "malloc" => IntrinsicImpl.malloc(this)(args.head) + case "fopen" => IntrinsicImpl.fopen(this)(args.head) + case "putc" => IntrinsicImpl.putc(this)(args.head) + case "strlen" => + for { + str <- Eval.getNullTerminatedString(this)("mem", args.head) + r = Scalar(BitVecLiteral(str.length, 64)) + _ <- storeVar("R0", Scope.Global, r) + } yield (Some(r)) + case "print" => IntrinsicImpl.print(this)(args.head) + case "puts" => IntrinsicImpl.print(this)(args.head) >> IntrinsicImpl.putc(this)(Scalar(BitVecLiteral('\n'.toInt, 64))) + case "write" => IntrinsicImpl.write(this)(args(1), args(2)) + case _ => State.setError(Errored(s"Call undefined intrinsic $name")) + } + } + + def loadVar(v: String) = { + State.getE((s: InterpreterState) => { + s.memoryState.getVar(v) + }) + } + + def evalAddrToProc(addr: Int) = + Logger.debug(s" eff : FIND PROC $addr") + for { + res: List[BasilValue] <- getE((s: InterpreterState) => + s.memoryState.doLoad("ghost-funtable", List(Scalar(BitVecLiteral(addr, 64)))) + ) + } yield { + res match { + case ((f: FunPointer) :: Nil) => Some(f) + case _ => None + } + } + + def formatStore(varname: String, update: Map[BasilValue, BasilValue]): String = { + val ks = update.toList.sortWith((x, y) => { + def conv(v: BasilValue): BigInt = v match { + case (Scalar(b: BitVecLiteral)) => b.value + case (Scalar(b: IntLiteral)) => b.value + case _ => BigInt(0) + } + conv(x._1) <= conv(y._1) + }) + + val rs = ks.foldLeft(Some((None, List[BitVecLiteral]())): Option[(Option[BigInt], List[BitVecLiteral])])((acc, v) => + v match { + case (Scalar(bv: BitVecLiteral), Scalar(bv2: BitVecLiteral)) => { + acc match { + case None => None + case Some(None, l) => Some(Some(bv.value), bv2 :: l) + case Some(Some(v), l) if bv.value == v + 1 => Some(Some(bv.value), bv2 :: l) + case Some(Some(v), l) => { + None + } + } + } + case (bv, bv2) => None + } + ) + + rs match { + case Some(_, l) => { + val vs = Scalar(l.foldLeft(BitVecLiteral(0, 0))((acc, r) => eval.evalBVBinExpr(BVCONCAT, acc, r))).toString + s"$varname[${ks.headOption.map(_._1).getOrElse("null")}] := $vs" + } + case None if ks.length < 8 => s"$varname[${ks.map(_._1).mkString(",")}] := ${ks.map(_._2).mkString(",")}" + case None => s"$varname[${ks.map(_._1).take(8).mkString(",")}...] := ${ks.map(_._2).take(8).mkString(", ")}... " + } + + } + + def loadMem(v: String, addrs: List[BasilValue]) = { + State.getE((s: InterpreterState) => { + val r = s.memoryState.doLoad(v, addrs) + Logger.debug(s" eff : LOAD ${addrs.head} x ${addrs.size}") + r + }) + } + + def getNext = State.get((s: InterpreterState) => s.nextCmd) + + /** effects * */ + def setNext(c: ExecutionContinuation) = State.modify((s: InterpreterState) => { + s.copy(nextCmd = c) + }) + + def call(target: String, beginFrom: ExecutionContinuation, returnTo: ExecutionContinuation) = + modify((s: InterpreterState) => { + Logger.debug(s" eff : CALL $target") + s.copy( + nextCmd = beginFrom, + callStack = returnTo :: s.callStack, + memoryState = s.memoryState.pushStackFrame(target) + ) + }) + + def doReturn() = { + Logger.debug(s" eff : RETURN") + modifyE((s: InterpreterState) => { + s.callStack match { + case Nil => Right(s.copy(nextCmd = Stopped())) + case h :: tl => + for { + ms <- s.memoryState.popStackFrame() + } yield (s.copy(nextCmd = h, callStack = tl, memoryState = ms)) + } + }) + } + + def storeVar(v: String, scope: Scope, value: BasilValue): State[InterpreterState, Unit, InterpreterError] = { + Logger.debug(s" eff : SET $v := $value") + State.modify((s: InterpreterState) => s.copy(memoryState = s.memoryState.defVar(v, scope, value))) + } + + def storeMem(vname: String, update: Map[BasilValue, BasilValue]) = + State.modifyE((s: InterpreterState) => { + Logger.debug(s" eff : STORE ${formatStore(vname, update)}") + for { + ms <- s.memoryState.doStore(vname, update) + } yield (s.copy(memoryState = ms)) + }) +} + +trait Interpreter[S, E](val f: Effects[S, E]) { + + /* + * Returns value deciding whether to continue. + */ + def interpretOne: State[S, Boolean, E] + + @tailrec + final def run(begin: S): S = { + val (fs, cont) = interpretOne.f(begin) + + if (cont.contains(true)) then { + run(fs) + } else { + fs + } + } +} diff --git a/src/main/scala/ir/eval/InterpreterProduct.scala b/src/main/scala/ir/eval/InterpreterProduct.scala new file mode 100644 index 000000000..ccf4fb682 --- /dev/null +++ b/src/main/scala/ir/eval/InterpreterProduct.scala @@ -0,0 +1,139 @@ +package ir.eval +import ir._ +import ir.eval.BitVectorEval.* +import ir.* +import util.Logger +import util.functional.* +import util.functional.State.* +import boogie.Scope +import scala.collection.WithFilter + +import scala.annotation.tailrec +import scala.collection.mutable +import scala.collection.immutable +import scala.util.control.Breaks.{break, breakable} + +def doLeft[L, T, V, E](f: State[L, V, E]): State[(L, T), V, E] = for { + n <- State[(L, T), V, E]((s: (L, T)) => { + val r = f.f(s._1) + ((r._1, s._2), r._2) + }) +} yield (n) + +def doRight[L, T, V, E](f: State[T, V, E]): State[(L, T), V, E] = for { + n <- State[(L, T), V, E]((s: (L, T)) => { + val r = f.f(s._2) + ((s._1, r._1), r._2) + }) +} yield (n) + +/** Runs two interpreters "inner" and "before" simultaneously, returning the value from inner, and ignoring before + */ +case class ProductInterpreter[L, T, E](inner: Effects[L, E], before: Effects[T, E]) extends Effects[(L, T), E] { + + def loadVar(v: String) = for { + n <- doRight(before.loadVar(v)) + f <- doLeft(inner.loadVar(v)) + } yield (f) + + def loadMem(v: String, addrs: List[BasilValue]) = for { + n <- doRight(before.loadMem(v, addrs)) + f <- doLeft(inner.loadMem(v, addrs)) + } yield (f) + + def evalAddrToProc(addr: Int) = for { + n <- doRight(before.evalAddrToProc(addr: Int)) + f <- doLeft(inner.evalAddrToProc(addr)) + } yield (f) + + def getNext = for { + n <- doRight(before.getNext) + f <- doLeft(inner.getNext) + } yield (f) + + /** state effects */ + def setNext(c: ExecutionContinuation) = for { + n <- doRight(before.setNext(c)) + f <- doLeft(inner.setNext(c)) + } yield (f) + + def call(target: String, beginFrom: ExecutionContinuation, returnTo: ExecutionContinuation) = for { + n <- doRight(before.call(target, beginFrom, returnTo)) + f <- doLeft(inner.call(target, beginFrom, returnTo)) + } yield (f) + + def callIntrinsic(name: String, args: List[BasilValue]) = for { + n <- doRight(before.callIntrinsic(name, args)) + f <- doLeft(inner.callIntrinsic(name, args)) + } yield (f) + + def doReturn() = for { + n <- doRight(before.doReturn()) + f <- doLeft(inner.doReturn()) + } yield (f) + + def storeVar(v: String, scope: Scope, value: BasilValue) = for { + n <- doRight(before.storeVar(v, scope, value)) + f <- doLeft(inner.storeVar(v, scope, value)) + } yield (f) + + def storeMem(vname: String, update: Map[BasilValue, BasilValue]) = for { + n <- doRight(before.storeMem(vname, update)) + f <- doLeft(inner.storeMem(vname, update)) + } yield (f) +} + +case class LayerInterpreter[L, T, E](inner: Effects[L, E], before: Effects[(L, T), E]) + extends Effects[(L, T), E] { + + def loadVar(v: String) = for { + n <- (before.loadVar(v)) + f <- doLeft(inner.loadVar(v)) + } yield (f) + + def loadMem(v: String, addrs: List[BasilValue]) = for { + n <- (before.loadMem(v, addrs)) + f <- doLeft(inner.loadMem(v, addrs)) + } yield (f) + + def evalAddrToProc(addr: Int) = for { + n <- (before.evalAddrToProc(addr: Int)) + f <- doLeft(inner.evalAddrToProc(addr)) + } yield (f) + + def getNext = for { + n <- (before.getNext) + f <- doLeft(inner.getNext) + } yield (f) + + /** state effects */ + def setNext(c: ExecutionContinuation) = for { + n <- (before.setNext(c)) + f <- doLeft(inner.setNext(c)) + } yield (f) + + def call(target: String, beginFrom: ExecutionContinuation, returnTo: ExecutionContinuation) = for { + n <- (before.call(target, beginFrom, returnTo)) + f <- doLeft(inner.call(target, beginFrom, returnTo)) + } yield (f) + + def callIntrinsic(name: String, args: List[BasilValue]) = for { + n <- before.callIntrinsic(name, args) + f <- doLeft(inner.callIntrinsic(name, args)) + } yield (f) + + def doReturn() = for { + n <- (before.doReturn()) + f <- doLeft(inner.doReturn()) + } yield (f) + + def storeVar(v: String, scope: Scope, value: BasilValue) = for { + n <- (before.storeVar(v, scope, value)) + f <- doLeft(inner.storeVar(v, scope, value)) + } yield (f) + + def storeMem(vname: String, update: Map[BasilValue, BasilValue]) = for { + n <- (before.storeMem(vname, update)) + f <- doLeft(inner.storeMem(vname, update)) + } yield (f) +} diff --git a/src/main/scala/ir/eval/SimplifyExpr.scala b/src/main/scala/ir/eval/SimplifyExpr.scala new file mode 100644 index 000000000..22156ccc6 --- /dev/null +++ b/src/main/scala/ir/eval/SimplifyExpr.scala @@ -0,0 +1,1154 @@ +package ir.eval +import ir.* +import util.Logger +import sourcecode.Line, sourcecode.FileName +import scala.collection.mutable + +import java.io.{BufferedWriter} +import ir.cilvisitor.* + +val assocOps: Set[BinOp] = + Set(BVADD, BVMUL, BVOR, BVAND, BVEQ, BoolAND, BoolEQ, BoolOR, BoolEQUIV, BoolEQ, IntADD, IntMUL, IntEQ) + +var trace = false + +class SimpExpr(simplifier: Expr => (Expr, Boolean)) extends CILVisitor { + var changedAnything = false + var count = 0 + override def vexpr(e: Expr) = + val (ne, changed) = simplifier(e) + changedAnything = changedAnything || changed + ChangeDoChildrenPost( + ne, + (e: Expr) => { + val one = e + val (ne, c) = simplifier(e) + changedAnything = changedAnything || c + ne + } + ) + + def apply(e: Expr) = { + val ns = SimpExpr(simplifier) + val ne = visit_expr(ns, e) + (ne, ns.changedAnything) + } +} + +def sequenceSimp(a: Expr => (Expr, Boolean), b: Expr => (Expr, Boolean))(e: Expr): (Expr, Boolean) = { + val (ne1, changed1) = a(e) + if (ne1 == e && changed1) { + Logger.error(s" ${SimplifyValidation.debugTrace.last}") + throw Exception(s"No simp $e \n -> $ne1") + } + val (ne2, changed2) = b(ne1) + if (ne2 == ne1 && changed2) { + // throw Exception("No simp") + Logger.error(s" ${SimplifyValidation.debugTrace.last}") + throw Exception(s"No simp $ne1 \n -> $ne2") + } + if (ne2 == e && (changed1 || changed2)) { + Logger.error(s" ${SimplifyValidation.debugTrace.last}") + throw Exception(s"No simp $e \n -> $ne1\n -> $ne2") + } + (ne2, changed1 || changed2) +} + +def simpFixedPoint(s: Expr => (Expr, Boolean))(e: Expr): (Expr, Boolean) = { + var expr = e + var changed = true + var changedAny = false + var count = 0 + while (changed) { + val (ne, ce) = s(expr) + count += 1 + changedAny = changedAny || ce + if ((expr == ne) && ce) { + Logger.error("Rewrite trace:\n" + SimplifyValidation.debugTrace.map(x => " " + x).mkString("\n")) + throw Exception(s"changed flag set but no change \n $expr\n $ne\n, ${s.getClass.getSimpleName}") + } + if (count > 100) { + Logger.error(s"$ne, ${SimplifyValidation.debugTrace.last}") + } + changed = ce + expr = ne + } + (expr, changedAny) +} + +def simplifyExprVis = SimpExpr(simpFixedPoint(sequenceSimp(simplifyExpr, SimpExpr(fastPartialEvalExpr).apply))) +def simplifyCondVis = SimpExpr( + simpFixedPoint( + sequenceSimp( + simpFixedPoint(SimpExpr(simpFixedPoint(simplifyCmpInequalities)).apply), + simplifyExprVis.apply, + ) + ) +) +def simplifyExprFixpoint = simplifyExprVis.apply +def simplifyCondFixpoint = simplifyCondVis.apply + +object AssumeConditionSimplifications extends CILVisitor { + override def vstmt(s: Statement) = s match { + case a: Assert => { + a.body = simplifyCondFixpoint(a.body)._1 + SkipChildren() + } + case a: Assume => { + Logger.debug(s"before : " + a.body) + a.body = simplifyCondFixpoint(a.body)._1 + Logger.debug("after : " + a.body) + SkipChildren() + } + + case _ => SkipChildren() + } + + def apply(p: Procedure) = { + visit_proc(this, p) + } +} + +def AlgebraicSimplifications(p: Procedure) = { + visit_proc(simplifyExprVis, p) + () +} + +def cleanupSimplify(p: Procedure) = { + visit_proc(SimpExpr(simpFixedPoint(cleanupExtends)), p) +} + +object SimplifyValidation { + var traceLog = mutable.LinkedHashSet[(Expr, Expr, String)]() + var validate: Boolean = false + var debugTrace = mutable.ArrayBuffer[(Expr, Expr, sourcecode.Line, sourcecode.FileName, sourcecode.Name)]() + + def makeValidation(writer: BufferedWriter) = { + + def makeEQ(a: Expr, b: Expr) = { + require(a.getType == b.getType) + a.getType match { + case BitVecType(sz) => BinaryExpr(BVEQ, a, b) + case IntType => BinaryExpr(IntEQ, a, b) + case BoolType => BinaryExpr(BoolEQ, a, b) + case m: MapType => ??? + } + } + + var ind = 0 + + for ((o, n, sname) <- traceLog) { + ind += 1 + if (ir.transforms.ExprComplexity()(n) > 5000) { + Logger.warn(s"Skipping simplification proof $ind because too large (> 5000)!") + } else { + if (ind % 100 == 0) Logger.info(s"Wrote simplification proof $ind / ${traceLog.size}") + val equal = UnaryExpr(BoolNOT, makeEQ(o, n)) + val expr = translating.BasilIRToSMT2.exprUnsat(equal, Some(s"simp.$ind$sname")) + writer.write(expr) + writer.write("\n\n") + } + } + } +} + +def logSimp(e: Expr, ne: Expr, actual: Boolean = true)(implicit + line: sourcecode.Line, + file: sourcecode.FileName, + name: sourcecode.Name +): Expr = { + if (!actual) { + return ne + } + + if (SimplifyValidation.debugTrace.length > 50) { + SimplifyValidation.debugTrace.drop(SimplifyValidation.debugTrace.length - 50) + } + SimplifyValidation.debugTrace.append((e, ne, line, file, name)) + if (e == ne) { + Logger.error(s"NOP simplification $e")(line, file, name) + } + + if (e != ne) { + val normer = VarNameNormalise() + val a = visit_expr(normer, e) + val b = visit_expr(normer, ne) + val s = s"${file.value}..${line.value}" + + SimplifyValidation.traceLog.add((a, b, s)) + } + ne +} + + +class VarNameNormalise() extends CILVisitor { + var count = 1 + val assigned = mutable.Map[Variable, Variable]() + + def rename(v: Variable, newName: String) = { + v match { + case l: LocalVar => LocalVar(newName, l.irType) + case Register(n, sz) => Register(newName, sz) + } + } + + override def vrvar(v: Variable) = { + if (assigned.contains(v)) { + ChangeTo(assigned(v)) + } else { + count += 1 + val newName = "Var" + count + val nv = rename(v, newName) + assigned(v) = nv + ChangeTo(nv) + } + } + + def apply(e: Expr) = { + count = 1 + assigned.clear() + val ne = visit_expr(this, e) + count = 1 + assigned.clear() + ne + } +} +def bvLogOpToBoolOp = Map[BinOp, BinOp]( + // logical ops when bv1 + BVAND -> BoolAND, + BVOR -> BoolOR +) + +def simplifyCmpInequalities(e: Expr): (Expr, Boolean) = { + + var didAnything = true + def simplifyCond(e: Expr): Expr = { + simplifyCondFixpoint(e)._1 + } + + val r = e match { + /** canonicalising to boolean operations */ + /* remove bool2bv in boolean context */ + case BinaryExpr(BVEQ, UnaryExpr(BoolToBV1, body), BitVecLiteral(1, 1)) => logSimp(e, body) + case BinaryExpr(BVEQ, UnaryExpr(BoolToBV1, l), UnaryExpr(BoolToBV1, r)) => logSimp(e, BinaryExpr(BoolEQ, (l), (r))) + + case BinaryExpr(BVADD, l @ UnaryExpr(BVNEG, x), r) if !r.isInstanceOf[Literal] && !{r match { + case UnaryExpr(BVNEG, _) => true + case _ => false + }} => BinaryExpr(BVADD, r, l) + + case BinaryExpr(BoolAND, l @ BinaryExpr(BVEQ, _, _), r @ BinaryExpr(relop, _, _)) + if ineqToStrict.contains(relop) || strictIneq.contains(relop) => { + BinaryExpr(BoolAND, r, l) + } + case BinaryExpr(BoolAND, l @ UnaryExpr(BoolNOT, BinaryExpr(BVEQ, _, _)), r @ BinaryExpr(relop, _, _)) + if ineqToStrict.contains(relop) || strictIneq.contains(relop) => { + BinaryExpr(BoolAND, r, l) + } + + /* intro bool2bv */ + case BinaryExpr( + BVCOMP, + l, + r + ) => { + logSimp(e, bool2bv1(BinaryExpr(BVEQ, l, r))) + } + /* push bool2bv upwards */ + case BinaryExpr( + bop, + BitVecLiteral(x, 1), + UnaryExpr(BoolToBV1, r) + ) if bvLogOpToBoolOp.contains(bop) => { + val l = if x == 1 then TrueLiteral else FalseLiteral + logSimp(e, bool2bv1(BinaryExpr(bvLogOpToBoolOp(bop), (l), (r)))) + } + + case BinaryExpr( + bop, + UnaryExpr(BoolToBV1, l), + BitVecLiteral(x, 1) + ) if bvLogOpToBoolOp.contains(bop) => { + val r = if x == 1 then TrueLiteral else FalseLiteral + logSimp(e, bool2bv1(BinaryExpr(bvLogOpToBoolOp(bop), (l), (r)))) + } + case BinaryExpr( + bop, + UnaryExpr(BoolToBV1, l), + UnaryExpr(BoolToBV1, r) + ) if bvLogOpToBoolOp.contains(bop) => { + logSimp(e, bool2bv1(BinaryExpr(bvLogOpToBoolOp(bop), (l), (r)))) + } + case BinaryExpr( + bop, + UnaryExpr(BoolToBV1, l), + UnaryExpr(BoolToBV1, r) + ) if bvLogOpToBoolOp.contains(bop) => { + logSimp(e, bool2bv1(BinaryExpr(bvLogOpToBoolOp(bop), (l), (r)))) + } + + // tautologies + case BinaryExpr(BVUGT, x, t @ BitVecLiteral(y, s)) if t == BitVectorEval.smt_bvnot(BitVecLiteral(0, s)) => + logSimp(e, FalseLiteral) + case BinaryExpr(BVULT, x, BitVecLiteral(0, s)) => logSimp(e, FalseLiteral) + + // subsume constant bound + case BinaryExpr(BoolOR, l @ BinaryExpr(BVUGE, x, y: BitVecLiteral), BinaryExpr(BVEQ, x2, y2: BitVecLiteral)) + if x == x2 && y2.value >= y.value => { + logSimp(e, l) + } + case BinaryExpr(BoolOR, l @ BinaryExpr(BVULE, x, y: BitVecLiteral), BinaryExpr(BVEQ, x2, y2: BitVecLiteral)) + if x == x2 && y2.value <= y.value => { + logSimp(e, l) + } + case BinaryExpr(BoolOR, l @ BinaryExpr(BVUGT, x, y: BitVecLiteral), BinaryExpr(BVEQ, x2, y2: BitVecLiteral)) + if x == x2 && y2.value > y.value => { + logSimp(e, l) + } + case BinaryExpr(BoolOR, l @ BinaryExpr(BVULT, x, y: BitVecLiteral), BinaryExpr(BVEQ, x2, y2: BitVecLiteral)) + if x == x2 && y2.value < y.value => { + logSimp(e, l) + } + + // relax bound by 1 + case BinaryExpr(BoolOR, BinaryExpr(BVULE, x, y: BitVecLiteral), (BinaryExpr(BVEQ, x2, z: BitVecLiteral))) + if x == x2 && ((y.value + 1) == z.value) => { + logSimp(e, BinaryExpr(BVULE, x, z)) + } + case BinaryExpr(BoolOR, BinaryExpr(BVULT, x, y: BitVecLiteral), (BinaryExpr(BVEQ, x2, z: BitVecLiteral))) + if x == x2 && y.value == z.value => { + logSimp(e, BinaryExpr(BVULE, x, z)) + } + case BinaryExpr(BoolOR, BinaryExpr(BVUGT, x, y: BitVecLiteral), (BinaryExpr(BVEQ, x2, z: BitVecLiteral))) + if x == x2 && y.value == z.value => { + logSimp(e, BinaryExpr(BVUGE, x, z)) + } + case BinaryExpr(BoolOR, BinaryExpr(BVUGE, x, y: BitVecLiteral), (BinaryExpr(BVEQ, x2, z: BitVecLiteral))) + if x == x2 && y.value - 1 == z.value => { + logSimp(e, BinaryExpr(BVULE, x, z)) + } + + case BinaryExpr( + BoolAND, + l @ BinaryExpr(BVUGT, e1, BitVecLiteral(x1, _)), + r @ BinaryExpr(BVUGT, e2, BitVecLiteral(x2, _)) + ) if e1 == e2 && (x2 >= x1) => + logSimp(e, r) + case BinaryExpr( + BoolAND, + l @ BinaryExpr(BVUGT, e1, BitVecLiteral(x1, _)), + r @ BinaryExpr(BVUGT, e2, BitVecLiteral(x2, _)) + ) if e1 == e2 && (x1 >= x2) => + logSimp(e, l) + case BinaryExpr( + BoolAND, + l @ BinaryExpr(BVUGE, e1, BitVecLiteral(x1, _)), + r @ BinaryExpr(BVUGE, e2, BitVecLiteral(x2, _)) + ) if e1 == e2 && (x2 >= x1) => + logSimp(e, r) + + case BinaryExpr( + BoolAND, + l @ BinaryExpr(BVUGE, e1, BitVecLiteral(x1, _)), + r @ UnaryExpr(BoolNOT, BinaryExpr(BVEQ, e2, BitVecLiteral(x2, _))) + ) if e1 == e2 && (x1 > x2) => + logSimp(e, l) + case BinaryExpr( + BoolAND, + l @ BinaryExpr(BVUGT, e1, BitVecLiteral(x1, _)), + r @ UnaryExpr(BoolNOT, BinaryExpr(BVEQ, e2, BitVecLiteral(x2, _))) + ) if e1 == e2 && (x1 >= x2) => + logSimp(e, l) + + case BinaryExpr( + BoolAND, + l @ BinaryExpr(BVUGE, e1, BitVecLiteral(x1, _)), + r @ BinaryExpr(BVEQ, e2, BitVecLiteral(x2, _)) + ) if e1 == e2 && (x1 <= x2) => + logSimp(e, r) + case BinaryExpr( + BoolAND, + l @ BinaryExpr(BVUGT, e1, BitVecLiteral(x1, _)), + r @ BinaryExpr(BVEQ, e2, BitVecLiteral(x2, _)) + ) if e1 == e2 && (x1 > x2) => + logSimp(e, r) + case BinaryExpr( + BoolAND, + l @ BinaryExpr(BVUGE, e1, BitVecLiteral(x1, _)), + r @ BinaryExpr(BVEQ, e2, BitVecLiteral(x2, _)) + ) if e1 == e2 && (x1 > x2) => + logSimp(e, FalseLiteral) + case BinaryExpr( + BoolAND, + l @ BinaryExpr(BVUGT, e1, BitVecLiteral(x1, _)), + r @ BinaryExpr(BVEQ, e2, BitVecLiteral(x2, _)) + ) if e1 == e2 && (x2 <= x1) => + logSimp(e, FalseLiteral) + + // tighten bound by 1 + case BinaryExpr( + BoolAND, + BinaryExpr(BVUGT, x, y: BitVecLiteral), + UnaryExpr(BoolNOT, (BinaryExpr(BVEQ, x2, z: BitVecLiteral))) + ) if x == x2 && z.value == y.value + 1 => { + logSimp(e, BinaryExpr(BVUGT, x, z)) + } + case e @ BinaryExpr( + BoolAND, + BinaryExpr(BVULT, x, y: BitVecLiteral), + UnaryExpr(BoolNOT, (BinaryExpr(BVEQ, x2, z: BitVecLiteral))) + ) if x == x2 && z.value == y.value - 1 => { + logSimp(e, BinaryExpr(BVULT, x, z)) + } + case e @ BinaryExpr( + BoolAND, + BinaryExpr(BVUGE, x, y: BitVecLiteral), + UnaryExpr(BoolNOT, (BinaryExpr(BVEQ, x2, z: BitVecLiteral))) + ) if x == x2 && z.value == y.value + 1 => { + logSimp(e, BinaryExpr(BVUGT, x, z)) + } + case e @ BinaryExpr( + BoolAND, + BinaryExpr(BVULE, x, y: BitVecLiteral), + UnaryExpr(BoolNOT, (BinaryExpr(BVEQ, x2, z: BitVecLiteral))) + ) if x == x2 && z.value == y.value - 1 => { + logSimp(e, BinaryExpr(BVULT, x, z)) + } + case BinaryExpr(BVEQ, BinaryExpr(BVADD, x, y), BitVecLiteral(0, _)) => + logSimp(e, BinaryExpr(BVEQ, x, UnaryExpr(BVNEG, y))) + + case UnaryExpr(BVNOT, UnaryExpr(BoolToBV1, arg)) => { + logSimp(e, bool2bv1(UnaryExpr(BoolNOT, arg))) + } + + /* COMPARISON FLAG HANDLING + * + * We quite precisely pattern match ASLp's output for C and V, + * these are computed by comparing the test to a higher-precision calculation of the test. + */ + + // NF check on expr + case Extract(upper, lower, b) if size(b).contains(upper) && (upper == (lower + 1)) && size(b).get >= 8 => { + logSimp(e, bool2bv1(BinaryExpr(BVSLT, (b), BitVecLiteral(0, size(b).get)))) + } + // sliced negative + case Extract(upper, lower, b) + if lower == upper - 1 && (upper % 8) == 0 && size(b).get % upper == 0 && size(b).get > upper => { + logSimp(e, bool2bv1(BinaryExpr(BVSLT, Extract(upper, 0, b), BitVecLiteral(0, upper)))) + } + + /** https://developer.arm.com/documentation/dui0801/l/Condition-Codes/Condition-code-suffixes-and-related-flags + * + * match NF == VF + * + * (declare-const Var2 (_ BitVec 64)) (declare-const Var3 (_ BitVec 64)) (assert (! (not (= (= (bvslt (bvadd Var2 + * Var3) (_ bv0 64)) (not (= (concat ((_ extract 63 63) (bvadd Var2 Var3)) (bvadd Var2 Var3)) (bvadd (concat ((_ + * extract 63 63) Var2) Var2) (concat ((_ extract 63 63) Var3) Var3))))) (bvsgt Var2 (bvnot Var3)))) :named + * simp105)) + */ + case BinaryExpr( + // add case + BoolEQ, + // N set + (BinaryExpr(BVSLT, lhs, BitVecLiteral(0, sz))), + // V set + UnaryExpr( + BoolNOT, + BinaryExpr( + BVEQ, + SignExtend(exts, orig @ BinaryExpr(BVADD, x1, y1)), + compar @ BinaryExpr(BVADD, x2, y2) + ) // high precision op + ) + ) + if sz > 1 && lhs == orig + && simplifyCond(SignExtend(exts, x1)) == x2 + && simplifyCond(SignExtend(exts, y1)) == y2 => { + logSimp(e, BinaryExpr(BVSGE, x1, UnaryExpr(BVNEG, y1))) + } + case BinaryExpr( + // add case + BoolEQ, + // N set + (BinaryExpr(BVSLT, BinaryExpr(BVADD, UnaryExpr(BVNEG, x0), y0), BitVecLiteral(0, sz))), + // V set + UnaryExpr( + BoolNOT, + BinaryExpr( + BVEQ, + SignExtend(exts, orig @ BinaryExpr(BVADD, UnaryExpr(BVNEG, x1), y1)), + compar @ BinaryExpr(BVADD, UnaryExpr(BVNEG, x2), y2) + ) // high precision op + ) + ) + if sz > 1 + && x0 == x1 && y0 == y1 + && simplifyCond(SignExtend(exts, x1)) == x2 + && simplifyCond(SignExtend(exts, y1)) == y2 => { + logSimp(e, BinaryExpr(BVSGE, y0, x0), true) + } + + // special case for collapsed cmp 0 x + case BinaryExpr( + // add case + BoolEQ, + // N set + (BinaryExpr(BVSLT, lhs @ UnaryExpr(BVNOT, _), BitVecLiteral(0, sz))), + // V set + UnaryExpr( + BoolNOT, + BinaryExpr( + BVEQ, + SignExtend(exts, orig @ UnaryExpr(BVNOT, x2)), + compar @ BinaryExpr(BVADD, SignExtend(_, UnaryExpr(BVNOT, x3)), BitVecLiteral(2, _)) + ) // high precision op + ) + ) if sz >= 8 && lhs == orig && x2 == x3 => { + logSimp(e, BinaryExpr(BVSLE, BitVecLiteral(0, sz), x2)) + } + + case BinaryExpr( + // sub case (with two args) + BoolEQ, + // N set + (BinaryExpr(BVSLT, lhs, BitVecLiteral(0, sz))), + // V set + UnaryExpr( + BoolNOT, + BinaryExpr( + BVEQ, + SignExtend(exts, orig @ BinaryExpr(o1, x1, UnaryExpr(BVNEG, y1))), + compar @ BinaryExpr(o2, SignExtend(ext1, x2), UnaryExpr(BVNEG, SignExtend(ext2, y2))) + ) // high precision op + ) + ) + if (o1 == o2) && o1 == BVADD && (lhs) == (orig) && sz >= 8 + && exts == ext1 && exts == ext2 + && x2 == x1 + && y2 == y1 => { + + logSimp(e, BinaryExpr(BVSGE, x1, y1)) + } + + // NF == VF + case BinaryExpr( + // this matches sub case a - b ===> (x1 + (bvneg y1)) + 1 + BoolEQ, + // N set + (BinaryExpr(BVSLT, lhs, BitVecLiteral(0, sz))), + // V set + UnaryExpr( + BoolNOT, + BinaryExpr( + BVEQ, + SignExtend(exts, orig @ BinaryExpr(o1, BinaryExpr(o3, x1, y1), z1)), + BinaryExpr(o2, compar @ BinaryExpr(o4, x2, y2), z2) // high precision op + ) + ) + ) + if sz >= 8 && (o1 == o2) && o2 == o4 && o1 == BVADD && (lhs) == (orig) + && simplifyCond(x2) == simplifyCond(SignExtend(exts, x1)) + && simplifyCond(y2) == simplifyCond(SignExtend(exts, y1)) + && simplifyCond(z2) == simplifyCond(SignExtend(exts, z1)) => { + logSimp(e, BinaryExpr(BVSGE, x1, UnaryExpr(BVNEG, BinaryExpr(BVADD, y1, z1)))) + } + + case BinaryExpr( + BVEQ, + ZeroExtend(exts, orig @ BinaryExpr(o1, x1, y1)), + compar @ BinaryExpr(o2, x2, y2) + ) + if size(x1).get > 1 && (o1 == o2) && o1 == BVADD + && simplifyCond(x2) == simplifyCond(ZeroExtend(exts, x1)) + && simplifyCond(y2) == simplifyCond(ZeroExtend(exts, y1)) => { + // C not Set + logSimp(e, UnaryExpr(BoolNOT, BinaryExpr(BVUGT, x1, UnaryExpr(BVNOT, y1)))) + } + + case BinaryExpr( + BVEQ, + ZeroExtend(exts, BinaryExpr(BVADD, UnaryExpr(BVNEG, x1), y1)), + BinaryExpr(BVADD, ZeroExtend(sz, UnaryExpr(BVNOT, x2)), z2) + ) + if size(x1).get > 1 + && exts == sz && x1 == x2 + && simplifyCond(BinaryExpr(BVSUB, z2, BitVecLiteral(1, size(z2).get))) == simplifyCond( + ZeroExtend(exts, y1) + ) => { + // C not Set + logSimp(e, UnaryExpr(BoolNOT, BinaryExpr(BVUGE, y1, x1)), true) + } + + case BinaryExpr(BVEQ, ZeroExtend(sz, v), BinaryExpr(BVADD, ZeroExtend(sz2, v2), BitVecLiteral(mv, _))) + if sz == sz2 && v == v2 && mv == BigInt(2).pow(size(v).get) => { + // special case for comparison collapsed cmp 0 - v + logSimp(e, UnaryExpr(BoolNOT, BinaryExpr(BVUGT, v, UnaryExpr(BVNEG, BitVecLiteral(1, size(v).get))))) + } + + case BinaryExpr( + BVEQ, + ZeroExtend(exts, orig @ BinaryExpr(o1, BinaryExpr(o3, x1, y1), z1)), + BinaryExpr(o2, compar @ BinaryExpr(o4, x2, y2), z2) // high precision op + ) + if size(x1).get >= 8 && (o1 == o2) && o2 == o4 && o1 == BVADD + && (x2) == (ZeroExtend(exts, x1)) + && (y2) == (ZeroExtend(exts, y1)) + && (z2) == (ZeroExtend(exts, z1)) => { + // C not Set + // TODO: dead + Logger.error("HIT1") + logSimp(e, UnaryExpr(BoolNOT, BinaryExpr(BVUGT, x1, UnaryExpr(BVNOT, BinaryExpr(BVADD, y1, z1))))) + } + + case BinaryExpr( + BVEQ, + extended @ ZeroExtend(exts, orig @ BinaryExpr(o1, x1, z1)), + BinaryExpr(o2, compar @ ZeroExtend(ext2, BinaryExpr(o4, x2, y2)), z2) + ) + if exts == ext2 && size(x1).get >= 8 && (o1 == o2) && o2 == o4 && o1 == BVADD + && simplifyCond(BinaryExpr(o1, ZeroExtend(exts, x1), ZeroExtend(exts, z1))) + == simplifyCond( + BinaryExpr(BVADD, ZeroExtend(exts, x2), (BinaryExpr(BVADD, ZeroExtend(exts, y2), z2))) + ) => { + // C not Set + logSimp(e, UnaryExpr(BoolNOT, BinaryExpr(BVUGT, x1, UnaryExpr(BVNOT, z1)))) + } + + case BinaryExpr( + BVEQ, + ZeroExtend(exts, orig @ BinaryExpr(o1, x1, UnaryExpr(BVNEG, y1))), + BinaryExpr( + o2, + compar @ BinaryExpr(o4, ZeroExtend(ext1, x2), ZeroExtend(ext2, UnaryExpr(BVNOT, y2))), + BitVecLiteral(1, _) + ) // high precision op + ) + if size(x1).get >= 8 && (o1 == o2) && o2 == o4 && o1 == BVADD + && exts == ext1 && exts == ext2 + && x1 == x2 && y1 == y2 => { + // C not Set + logSimp(e, UnaryExpr(BoolNOT, BinaryExpr(BVUGE, x1, y1))) + } + + case BinaryExpr( + BVEQ, + ZeroExtend(exts, orig @ BinaryExpr(BVADD, x1, y1: BitVecLiteral)), + BinaryExpr(BVADD, ZeroExtend(ext1, BinaryExpr(BVADD, x2, y3neg: BitVecLiteral)), y4neg: BitVecLiteral) + ) + if size(x1).get >= 8 + && exts == ext1 + && simplifyCond(UnaryExpr(BVNEG, y1)) + == simplifyCond( + BinaryExpr(BVADD, UnaryExpr(BVNEG, y3neg), UnaryExpr(BVNEG, Extract(size(y4neg).get - exts, 0, y4neg))) + ) + && simplifyCond(ZeroExtend(exts, Extract(size(y4neg).get - exts, 0, y4neg))) == y4neg + && { + val l = simplifyCond(BinaryExpr(BVSUB, UnaryExpr(BVNEG, y1), UnaryExpr(BVNEG, (y3neg)))) + val r = simplifyCond(UnaryExpr(BVNEG, Extract(size(y4neg).get - exts, 0, y4neg))) + l == r + } + && x1 == x2 => { + // somehow we get three-way inequality + logSimp( + e, + BinaryExpr(BoolAND, BinaryExpr(BVULT, x1, UnaryExpr(BVNEG, y1)), BinaryExpr(BVUGE, x1, UnaryExpr(BVNEG, y3neg))) + ) + } + + /* generic comparison simplification */ + // redundant inequality + // x < y && x != z when z <= y + case BinaryExpr( + BoolAND, + l @ BinaryExpr(BoolAND, _, BinaryExpr(op, lhs, rhs: BitVecLiteral)), + UnaryExpr(BoolNOT, BinaryExpr(BVEQ, lhs2, rhs2: BitVecLiteral)) + ) + if (ineqToStrict.contains(op) || strictIneq.contains(op)) && simplifyCond( + BinaryExpr(ineqToStrict.get(op).getOrElse(op), rhs, rhs2) + ) == TrueLiteral + && lhs == lhs2 => { + logSimp(e, l) + } + case BinaryExpr( + BoolAND, + l @ BinaryExpr(BoolAND, _, BinaryExpr(op, lhs, rhs)), + UnaryExpr(BoolNOT, BinaryExpr(BVEQ, lhs2, rhs2)) + ) if strictIneq.contains(op) && rhs == rhs2 && lhs == lhs2 => { + logSimp(e, l) + } + + case BinaryExpr( + BoolAND, + l @ BinaryExpr(BVUGT, lhs, UnaryExpr(BVNOT, rhs)), + UnaryExpr(BoolNOT, BinaryExpr(BVEQ, lhs2, nrhs @ UnaryExpr(BVNEG, rhs2))) + ) if rhs == rhs2 && lhs == lhs2 => { + logSimp(e, BinaryExpr(BVUGT, lhs, nrhs)) + } + case BinaryExpr( + BoolAND, + l @ BinaryExpr(BVULT, lhs, UnaryExpr(BVNEG, rhs)), + UnaryExpr(BoolNOT, BinaryExpr(BVEQ, lhs2, nrhs @ UnaryExpr(BVNOT, rhs2))) + ) if rhs == rhs2 && lhs == lhs2 => { + logSimp(e, BinaryExpr(BVULT, lhs, nrhs)) + } + + // weak to strict inequality + // x >= 0 && x != 0 ===> x > 0 + case BinaryExpr(BoolAND, BinaryExpr(op, lhs, BitVecLiteral(0, sz)), UnaryExpr(BoolNOT, rhs)) + if ineqToStrict.contains(op) && + size(lhs).isDefined && (simplifyCond( + BinaryExpr(BVEQ, lhs, BitVecLiteral(0, size(lhs).get)) + ) == rhs) => { + logSimp(e, BinaryExpr(ineqToStrict(op), lhs, BitVecLiteral(0, size(lhs).get))) + } + case BinaryExpr(BoolAND, BinaryExpr(op, lhs, rhs), UnaryExpr(BoolNOT, BinaryExpr(BVEQ, lhs2, rhs2))) + if ineqToStrict.contains(op) && + lhs == lhs2 && (simplifyCond(UnaryExpr(BVNEG, rhs)) == simplifyCond(rhs2)) => { + logSimp(e, BinaryExpr(ineqToStrict(op), lhs, rhs)) + } + + case BinaryExpr(BoolEQ, UnaryExpr(BoolNOT, x), UnaryExpr(BoolNOT, y)) => logSimp(e, BinaryExpr(BoolEQ, x, y)) + + case BinaryExpr(BoolOR, BinaryExpr(strictOp, x, y), r @ BinaryExpr(BVEQ, a, b)) + if x == a && y == b && strictToNonStrict.contains(strictOp) => { + logSimp(e, BinaryExpr(strictToNonStrict(strictOp), x, y)) + } + + case BinaryExpr( + BoolOR, + BinaryExpr(strictOp, BinaryExpr(BVADD, x, UnaryExpr(BVNEG, y)), BitVecLiteral(0, _)), + r @ BinaryExpr(BVEQ, a, b) + ) if x == a && y == b && strictToNonStrict.contains(strictOp) => { + logSimp(e, BinaryExpr(strictToNonStrict(strictOp), x, y)) + } + + case BinaryExpr(BoolAND, BinaryExpr(BoolAND, x, y), r @ UnaryExpr(BoolNOT, BinaryExpr(BVEQ, a, b))) => { + logSimp(e, BinaryExpr(BoolAND, BinaryExpr(BoolAND, x, r), BinaryExpr(BoolAND, y, r))) + } + + case BinaryExpr(BoolOR, BinaryExpr(op, lhs, rhs), BinaryExpr(BVEQ, lhs2, rhs2)) + if strictToNonStrict.contains(op) && rhs == rhs2 && lhs == lhs2 => { + logSimp(e, BinaryExpr(strictToNonStrict(op), lhs, rhs)) + } + + case BinaryExpr(BoolAND, lhs @ BinaryExpr(op, l, r), UnaryExpr(BoolNOT, BinaryExpr(BVEQ, l2, r2))) + if strictIneq.contains(op) && l == l2 && r == r2 => { + logSimp(e, lhs) + } + + case BinaryExpr( + BoolAND, + BinaryExpr(BoolAND, a @ BinaryExpr(op1, lhs1, lb: Literal), b @ BinaryExpr(op2, lhs3, rb: Literal)), + BinaryExpr(BoolAND, c @ BinaryExpr(op4, lhs2, lb2: Literal), d @ BinaryExpr(op3, lhs4, rb2: Literal)) + ) if isIneq(op1) && isIneq(op2) && isIneq(op3) && isIneq(op4) && lhs1 == lhs2 && lhs3 != lhs1 => { + logSimp(e, BinaryExpr(BoolAND, BinaryExpr(BoolAND, a, c), BinaryExpr(BoolAND, b, d))) + } + + case BinaryExpr(BoolOR, BinaryExpr(BVUGT, x, y), BinaryExpr(BVULE, x1, y1)) if x1 == x && y1 == y => TrueLiteral + case BinaryExpr(BoolOR, BinaryExpr(BVULT, x, y), BinaryExpr(BVUGE, x1, y1)) if x1 == x && y1 == y => TrueLiteral + case BinaryExpr(BoolOR, BinaryExpr(BVULE, x, y), BinaryExpr(BVUGT, x1, y1)) if x1 == x && y1 == y => TrueLiteral + + // case orig @ BinaryExpr( + // BoolAND, + // BinaryExpr(BoolAND, a, b), + // BinaryExpr(BoolOR, c, d) + // ) if { + // //val simpeda = BinaryExpr(BoolAND, BinaryExpr(BoolOR, a, c), BinaryExpr(BoolOR, a, d)) + // //val simpedb = BinaryExpr(BoolAND, BinaryExpr(BoolOR, b, c), BinaryExpr(BoolOR, b, d)) + // //val s = BinaryExpr(BoolAND, simpeda, simpedb) + // //val r = simplifyCond(s) + // true + // } => { + // trace = true + // val simpeda = (BinaryExpr(BoolAND, (BinaryExpr(BoolAND, c, a)), (BinaryExpr(BoolAND, c, b)))) + // val simpedb = (BinaryExpr(BoolAND, (BinaryExpr(BoolAND, d, a)), (BinaryExpr(BoolAND, d, b)))) + // val s = simplifyCond(BinaryExpr(BoolOR, simpeda, simpedb)) + // val nr = logSimp(e, s) + // trace = false + // nr + // } + + case BinaryExpr( + BoolOR, + BinaryExpr(BoolAND, l @ BinaryExpr(op1, lhs1, lb: Literal), r @ BinaryExpr(op2, lhs3, rb: Literal)), + d @ BinaryExpr(BVEQ, lhs2, rhs2: Literal) + ) + if isIneq(op1) && isIneq(op2) && lhs1 == lhs2 && lhs3 == lhs2 + && { + val ls = simplifyCond(BinaryExpr(BoolOR, l, d)) + val rs = simplifyCond(BinaryExpr(BoolOR, r, d)) + (ls, rs) match { + case (BinaryExpr(_, l, r: Literal), BinaryExpr(_, ll, rr: Literal)) => true + case _ => false + } + } => { + logSimp( + e, + BinaryExpr( + BoolAND, + simplifyCond(BinaryExpr(BoolOR, l, d)), + simplifyCond(BinaryExpr(BoolOR, r, d)) + ) + ) + } + + + case BinaryExpr( + BoolAND, + BinaryExpr(op, lhs, rhs), + UnaryExpr(BoolNOT, BinaryExpr(BVEQ, BinaryExpr(BVADD, lhs2, rhs2), BitVecLiteral(0, _))) + ) + if ineqToStrict.contains(op) && + rhs == rhs2 && (simplifyCond(lhs) == simplifyCond(UnaryExpr(BVNEG, lhs2))) => { + logSimp(e, BinaryExpr(ineqToStrict(op), lhs, rhs)) + } + + // TODO: below are possibly redundant due to changed canonical form + case BinaryExpr(BoolAND, BinaryExpr(op, lhs, rhs), UnaryExpr(BoolNOT, BinaryExpr(BVEQ, lhs2, rhs2))) + if ineqToStrict.contains(op) && + lhs == lhs2 && rhs == rhs2 => { + logSimp(e, BinaryExpr(ineqToStrict(op), lhs, rhs)) + } + case BinaryExpr( + BoolAND, + BinaryExpr(op, lhs, rhs), + UnaryExpr(BoolNOT, BinaryExpr(BVEQ, BinaryExpr(BVADD, lhs2, rhs2), BitVecLiteral(0, _))) + ) + if ineqToStrict.contains(op) && + lhs == lhs2 && simplifyCond(rhs) == simplifyCond(UnaryExpr(BVNEG, rhs2)) => { + logSimp(e, BinaryExpr(ineqToStrict(op), lhs, rhs)) + } + + // tighten inequality bounds + case e @ BinaryExpr(BoolAND, BinaryExpr(BVSLT, x, y), (BinaryExpr(BVSLT, z, y2))) if y == y2 => { + logSimp(e, BinaryExpr(BVSLT, x, z)) + } + case e @ BinaryExpr(BoolAND, BinaryExpr(BVULT, x, y), (BinaryExpr(BVULT, z, y2))) if y == y2 => { + logSimp(e, BinaryExpr(BVULT, x, z)) + } + case e @ BinaryExpr(BoolAND, BinaryExpr(BVULT, x, y), (BinaryExpr(BVULT, x2, z))) + if x == x2 /* && simplifyCond(BinaryExpr(BVULT, y, z)).isInstanceOf[BoolLit] */ => { + logSimp( + e, + simplifyCond(BinaryExpr(BVULT, y, z)) match { + case TrueLiteral => BinaryExpr(BVULT, x, y) + case FalseLiteral => BinaryExpr(BVULT, x, z) + case _ => e + } + ) + } + case e @ BinaryExpr(BoolAND, BinaryExpr(BVSLT, x, y), (BinaryExpr(BVSLT, x2, z))) + if x == x2 /*&& simplifyCond(BinaryExpr(BVSLT, y, z)).isInstanceOf[BoolLit]*/ => { + logSimp( + e, + simplifyCond(BinaryExpr(BVSLT, y, z)) match { + case TrueLiteral => BinaryExpr(BVSLT, x, y) + case FalseLiteral => BinaryExpr(BVSLT, x, z) + case _ => e + } + ) + } + + case o => { + didAnything = false + o + } + } + (r, didAnything) +} + +def bool2bv1(e: Expr) = { + e.getType match { + case BitVecType(1) => e + case BoolType => UnaryExpr(BoolToBV1, e) + case _ => ??? + } + +} + +def isRel(b: BinOp) = { + isIneq(b) || b == BVEQ || b == BoolEQ +} + +def isIneq(b: BinOp) = { + ineqToStrict.contains(b) || strictIneq.contains(b) +} + +val ineqToStrict = Map[BinOp, BinOp]( + BVSGE -> BVSGT, + BVUGE -> BVUGT, + BVSLE -> BVSLT, + BVULE -> BVULT +) + +val strictToNonStrict = Map[BinOp, BinOp]( + BVSGT -> BVSGE, + BVUGT -> BVUGE, + BVSLT -> BVSLE, + BVULT -> BVULE +) + +val strictIneq = Set[BinOp]( + BVSGT, + BVUGT, + BVSLT, + BVULT +) + +def cleanupExtends(e: Expr): (Expr, Boolean) = { + // this 'de-canonicalises' to some extent, but makes the resulting program simpler, so we perform as a post pass + + var changedAnything = true + + val res = e match { + case Extract(ed, 0, body) if size(body).get == ed => (body) + case ZeroExtend(0, body) => (body) + case SignExtend(0, body) => (body) + case BinaryExpr(BVADD, body, BitVecLiteral(0, _)) => (body) + case BinaryExpr(BVMUL, body, BitVecLiteral(1, _)) => (body) + case Repeat(1, body) => (body) + case Extract(ed, 0, ZeroExtend(extension, body)) if (body.getType == BitVecType(ed)) => (body) + case Extract(ed, 0, SignExtend(extension, body)) if (body.getType == BitVecType(ed)) => (body) + case Extract(ed, 0, ZeroExtend(exts, body)) if exts + size(body).get >= ed && ed > size(body).get => + ZeroExtend(ed - size(body).get, body) + + case BinaryExpr(BVEQ, ZeroExtend(x, body), y: BitVecLiteral) if y.value <= BigInt(2).pow(size(body).get) - 1 => + BinaryExpr(BVEQ, body, BitVecLiteral(y.value, size(body).get)) + + case BinaryExpr(BVEQ, ZeroExtend(sz, expr), BitVecLiteral(0, sz2)) => + BinaryExpr(BVEQ, expr, BitVecLiteral(0, size(expr).get)) + + // compose slices + case Extract(ed1, be1, Extract(ed2, be2, body)) => Extract(ed1 + be2, be1 + be2, (body)) + case SignExtend(sz1, SignExtend(sz2, exp)) => SignExtend(sz1 + sz2, exp) + case ZeroExtend(sz1, ZeroExtend(sz2, exp)) => ZeroExtend(sz1 + sz2, exp) + + // make subs more readable + //case BinaryExpr(BVADD, x, b: BitVecLiteral) if eval.BitVectorEval.isNegative(b) => { + // BinaryExpr(BVSUB, x, eval.BitVectorEval.smt_bvneg(b)) + //} + + // extract(hi, m+e, 0) ++ zeroextend(e, extract(m, 0, r0)) to bitmask + case BinaryExpr(BVCONCAT, Extract(hi1, lo1, x1), ZeroExtend(ext, Extract(hi2, 0, x2))) if lo1 == ext + hi2 => { + val b = "1" * (hi1 - lo1) ++ ("0" * ext) ++ "1" * hi2 + val n = BinaryExpr(BVAND, Extract(hi1, 0, x2), BitVecLiteral(BigInt(b, 2), hi1)) + n + } + + // redundant extends + // extract extended zero part + case Extract(ed, bg, ZeroExtend(x, expr)) if (bg > size(expr).get) => BitVecLiteral(0, ed - bg) + // extract below extend + case Extract(ed, bg, ZeroExtend(x, expr)) if (bg < size(expr).get) && (ed < size(expr).get) => Extract(ed, bg, expr) + case Extract(ed, bg, SignExtend(x, expr)) if (bg < size(expr).get) && (ed < size(expr).get) => Extract(ed, bg, expr) + + case BinaryExpr(BVSHL, body, BitVecLiteral(n, _)) if size(body).get <= n => BitVecLiteral(0, size(body).get) + + // simplify convoluted bit test + case BinaryExpr(BVEQ, BinaryExpr(BVSHL, ZeroExtend(n1, body), BitVecLiteral(n, _)), BitVecLiteral(0, _)) + if n1 == n => { + BinaryExpr(BVEQ, body, BitVecLiteral(0, size(body).get)) + } + // assume (!(bvshl8(zero_extend6_2(R3_19[8:6]), 6bv8) == 128bv8)); + case BinaryExpr( + BVEQ, + BinaryExpr(BVSHL, ZeroExtend(n1, body @ Extract(hi, lo, v)), BitVecLiteral(n, _)), + c @ BitVecLiteral(cval, _) + ) if lo == n && cval >= BigInt(2).pow(lo + n.toInt) => { + BinaryExpr(BVEQ, body, Extract(hi + n.toInt, lo + n.toInt, c)) + } + case BinaryExpr(BVEQ, BinaryExpr(BVSHL, b, BitVecLiteral(n, _)), c @ BitVecLiteral(0, _)) => { + // b low bits are all zero due to shift + BinaryExpr(BVEQ, b, BitVecLiteral(0, size(b).get)) + } + case BinaryExpr(BVEQ, BinaryExpr(BVSHL, b, BitVecLiteral(n, _)), c @ BitVecLiteral(cval, _)) + if cval != 0 && cval < BigInt(2).pow(n.toInt) => { + // low bits are all zero due to shift, and cval low bits are not zero + FalseLiteral + } + case BinaryExpr( + BVEQ, + BinaryExpr(BVSHL, ZeroExtend(n1, body @ Extract(hi, lo, v)), BitVecLiteral(n, _)), + c @ BitVecLiteral(cval, _) + ) if lo == n && cval >= BigInt(2).pow(n.toInt) => { + // extract the part of cval we are testing and remove the shift on the lhs operand + BinaryExpr(BVEQ, body, Extract((hi - lo) + n.toInt, n.toInt, c)) + } + + // bvnot to bvneg + // case BinaryExpr(BVADD, UnaryExpr(BVNOT, x), BitVecLiteral(1, _)) => logSimp(e, UnaryExpr(BVNEG, x)) + + case r => { + changedAnything = false + r + } + } + + (res, changedAnything) +} + +def simplifyExpr(e: Expr): (Expr, Boolean) = { + // println((0 until indent).map(" ").mkString("") + e) + + /** Apply the rewrite rules once. Note some rules expect a canonical form produced by other rules, and hence this is + * more effective when applied iteratively until a fixed point. + */ + var didAnything = true + val simped = e match { + // constant folding + // const + (expr + const) -> expr + (const + const) + // + // + // (comp (comp x y) 1) = (comp x y) + case BinaryExpr(BVEQ, UnaryExpr(BoolToBV1, body), BitVecLiteral(1, 1)) => logSimp(e, body) + case BinaryExpr( + BVEQ, + BinaryExpr(BVCOMP, body @ BinaryExpr(BVCOMP, _, _), BitVecLiteral(0, 1)), + BitVecLiteral(1, 1) + ) => + logSimp(e, BinaryExpr(BVEQ, (body), BitVecLiteral(0, 1))) + + case BinaryExpr(BVEQ, BinaryExpr(BVCOMP, e1: Expr, e2: Expr), BitVecLiteral(0, 1)) => + logSimp(e, UnaryExpr(BoolNOT, BinaryExpr(BVEQ, (e1), (e2)))) + + case BinaryExpr(BVADD, BinaryExpr(BVADD, body, l: BitVecLiteral), r: BitVecLiteral) + if !body.isInstanceOf[Literal] => + logSimp(e, BinaryExpr(BVADD, body, (BinaryExpr(BVADD, r, l)))) + + case BinaryExpr(BVMUL, BinaryExpr(BVMUL, body, l: BitVecLiteral), r: BitVecLiteral) => + logSimp(e, BinaryExpr(BVMUL, BinaryExpr(BVMUL, l, r), (body))) + + case BinaryExpr(BVOR, BinaryExpr(BVOR, body, l: BitVecLiteral), r: BitVecLiteral) => + logSimp(e, BinaryExpr(BVOR, BinaryExpr(BVOR, l, r), (body))) + + case BinaryExpr(BVAND, BinaryExpr(BVAND, body, l: BitVecLiteral), r: BitVecLiteral) => + logSimp(e, BinaryExpr(BVAND, BinaryExpr(BVAND, l, r), (body))) + + case BinaryExpr(BVADD, BinaryExpr(BVADD, l, lc: BitVecLiteral), BinaryExpr(BVADD, r, rc: BitVecLiteral)) + if !l.isInstanceOf[BitVecLiteral] => + logSimp(e, BinaryExpr(BVADD, (BinaryExpr(BVADD, l, r)), (BinaryExpr(BVADD, lc, rc)))) + // normalise + // make all comparisons positive so double negatives can be cleaned up + + case BinaryExpr(BVEQ, BinaryExpr(BVCOMP, e1: Expr, e2: Expr), BitVecLiteral(1, 1)) => + logSimp(e, BinaryExpr(BVEQ, (e1), (e2))) + + case BinaryExpr(BVNEQ, e1, e2) => logSimp(e, UnaryExpr(BoolNOT, BinaryExpr(BVEQ, (e1), (e2)))) + + case BinaryExpr(op, BinaryExpr(op1, a, b: Literal), BinaryExpr(op2, c, d: Literal)) + if !a.isInstanceOf[Literal] && !c.isInstanceOf[Literal] + && assocOps.contains(op) && op == op1 && op == op2 => + logSimp(e, BinaryExpr(op, BinaryExpr(op, a, c), BinaryExpr(op, b, d))) + + case BinaryExpr(op, x: Literal, y: Expr) if !y.isInstanceOf[Literal] && assocOps.contains(op) => + logSimp(e, BinaryExpr(op, (y), (x))) + + case BinaryExpr(BVADD, ed @ SignExtend(sz, UnaryExpr(BVNOT, x)), bo @ BitVecLiteral(bv, sz2)) + if size(ed).contains(sz2) && !BitVectorEval.isNegative(bo) => + logSimp(e, BinaryExpr(BVADD, UnaryExpr(BVNEG, SignExtend(sz, x)), BitVecLiteral(bv - 1, sz2)), actual = false) + + case BinaryExpr(BVADD, BinaryExpr(BVADD, y, ed @ UnaryExpr(BVNOT, x)), bo @ BitVecLiteral(off, sz2)) + if size(ed).contains(sz2) && !(y.isInstanceOf[BitVecLiteral]) && !BitVectorEval.isNegative(bo) => + logSimp( + e, + BinaryExpr(BVADD, BinaryExpr(BVADD, y, UnaryExpr(BVNEG, x)), BitVecLiteral(off - 1, sz2)), + actual = false + ) + + case BinaryExpr(BVADD, BinaryExpr(BVADD, y, ed @ SignExtend(sz, UnaryExpr(BVNOT, x))), BitVecLiteral(off, sz2)) + if size(ed).contains(sz2) && !(y.isInstanceOf[BitVecLiteral]) => + logSimp( + e, + BinaryExpr(BVADD, BinaryExpr(BVADD, y, UnaryExpr(BVNEG, SignExtend(sz, x))), BitVecLiteral(off - 1, sz2)), + actual = false + ) + + //case BinaryExpr(BVADD, UnaryExpr(BVNEG, x), BitVecLiteral(c, sz)) if c == BitVectorEval.smt_bvneg(BitVecLiteral(1, sz)).value => logSimp(e, UnaryExpr(BVNOT, x)) + //case BinaryExpr(BVADD, BitVecLiteral(1, _), UnaryExpr(BVNEG, x)) => logSimp(e, UnaryExpr(BVNOT, x)) + // case BinaryExpr(BVADD, UnaryExpr(BVNEG, x), BitVecLiteral(c, sz)) => logSimp(e, BinaryExpr(UnaryExpr(BVNOT, x), )) + + case BinaryExpr(BVADD, UnaryExpr(BVNOT, x), BitVecLiteral(1, _)) => UnaryExpr(BVNEG, x) + + //case BinaryExpr(BVEQ, BinaryExpr(BVADD, x, y: BitVecLiteral), BitVecLiteral(0, _)) + // if (eval.BitVectorEval.isNegative(y)) => + // logSimp(e, BinaryExpr(BVEQ, x, eval.BitVectorEval.smt_bvneg(y))) + + /* + * Simplify BVop to Bool ops in a boolean context. + */ + + case BinaryExpr(BVCOMP, body @ BinaryExpr(BVCOMP, _, _), BitVecLiteral(1, 1)) => logSimp(e, body) + case BinaryExpr(BVCOMP, body @ BinaryExpr(BVCOMP, _, _), BitVecLiteral(0, 1)) => + logSimp(e, UnaryExpr(BVNOT, (body))) + case BinaryExpr(BVEQ, l, BitVecLiteral(0, 1)) => + logSimp(e, UnaryExpr(BoolNOT, BinaryExpr(BVEQ, l, BitVecLiteral(1, 1)))) + + case BinaryExpr(BoolAND, x, TrueLiteral) => logSimp(e, x) + case BinaryExpr(BoolAND, x, FalseLiteral) => logSimp(e, FalseLiteral) + case BinaryExpr(BoolOR, x, FalseLiteral) => logSimp(e, x) + case BinaryExpr(BoolOR, x, TrueLiteral) => logSimp(e, TrueLiteral) + + case BinaryExpr(BVCONCAT, BitVecLiteral(0, sz), expr) => logSimp(e, ZeroExtend(sz, expr)) + case BinaryExpr(BVCONCAT, expr, BitVecLiteral(0, sz)) if (BigInt(2).pow(sz + size(expr).get) > sz) => + logSimp(e, BinaryExpr(BVSHL, ZeroExtend(sz, expr), BitVecLiteral(sz, sz + size(expr).get))) + + // identities + case BinaryExpr(BVXOR, l, r) if l == r => + logSimp( + e, + e.getType match { + case BitVecType(sz) => BitVecLiteral(0, sz) + case _ => throw Exception("Type error (should be unreachable)") + } + ) + case BinaryExpr(BoolEQ, x, FalseLiteral) => logSimp(e, UnaryExpr(BoolNOT, x)) + case BinaryExpr(BVADD, x, BitVecLiteral(0, _)) => logSimp(e, x) + + // double negation + case UnaryExpr(BVNOT, UnaryExpr(BVNOT, body)) => logSimp(e, body) + case UnaryExpr(BVNEG, UnaryExpr(BVNEG, body)) => logSimp(e, body) + case UnaryExpr(BoolNOT, UnaryExpr(BoolNOT, body)) => logSimp(e, body) + + // syntactic equality + case BinaryExpr(BVEQ, a, b) if a == b => logSimp(e, TrueLiteral) + + case BinaryExpr(BVADD, BinaryExpr(BVADD, y, UnaryExpr(BVNOT, x)), BitVecLiteral(1, _)) + if !(y.isInstanceOf[BitVecLiteral]) => + logSimp(e, BinaryExpr(BVADD, y, UnaryExpr(BVNEG, x))) + + //case BinaryExpr(BVEQ, BinaryExpr(BVADD, x, y: BitVecLiteral), BitVecLiteral(0, s)) => + // logSimp(e, BinaryExpr(BVEQ, x, UnaryExpr(BVNEG, y))) + // + //case BinaryExpr(BVEQ, BinaryExpr(BVADD, x, UnaryExpr(BVNEG, y)), BitVecLiteral(0, _)) => + // logSimp(e, BinaryExpr(BVEQ, x, y)) + + //case BinaryExpr(op, BinaryExpr(BVADD, x, UnaryExpr(BVNEG, y)), BitVecLiteral(0, _)) if strictIneq.contains(op) || op == BVEQ || ineqToStrict.contains(op) => + // logSimp(e, BinaryExpr(op, x, y)) + + case BinaryExpr(BVSUB, x: Expr, y: BitVecLiteral) => logSimp(e, BinaryExpr(BVADD, x, UnaryExpr(BVNEG, y))) + // DeMorgans + case UnaryExpr(BoolNOT, BinaryExpr(BoolAND, a, b)) => + logSimp(e, BinaryExpr(BoolOR, UnaryExpr(BoolNOT, a), UnaryExpr(BoolNOT, b))) + case UnaryExpr(BoolNOT, BinaryExpr(BoolOR, a, b)) => + logSimp(e, BinaryExpr(BoolAND, UnaryExpr(BoolNOT, a), UnaryExpr(BoolNOT, b))) + + // inequality negation + case BinaryExpr(BVCOMP, body @ BinaryExpr(BVCOMP, _, _), BitVecLiteral(0, 1)) => + logSimp(e, UnaryExpr(BVNOT, (body))) + case UnaryExpr(BoolNOT, BinaryExpr(BVSLT, lhs, rhs)) => logSimp(e, BinaryExpr(BVSGE, lhs, rhs)) + case UnaryExpr(BoolNOT, BinaryExpr(BVSGT, lhs, rhs)) => logSimp(e, BinaryExpr(BVSLE, lhs, rhs)) + case UnaryExpr(BoolNOT, BinaryExpr(BVULT, lhs, rhs)) => logSimp(e, BinaryExpr(BVUGE, lhs, rhs)) + case UnaryExpr(BoolNOT, BinaryExpr(BVUGT, lhs, rhs)) => logSimp(e, BinaryExpr(BVULE, lhs, rhs)) + case UnaryExpr(BoolNOT, BinaryExpr(BVSLE, lhs, rhs)) => logSimp(e, BinaryExpr(BVSGT, lhs, rhs)) + case UnaryExpr(BoolNOT, BinaryExpr(BVSGE, lhs, rhs)) => logSimp(e, BinaryExpr(BVSLT, lhs, rhs)) + case UnaryExpr(BoolNOT, BinaryExpr(BVULE, lhs, rhs)) => logSimp(e, BinaryExpr(BVUGT, lhs, rhs)) + case UnaryExpr(BoolNOT, BinaryExpr(BVUGE, lhs, rhs)) => logSimp(e, BinaryExpr(BVULT, lhs, rhs)) + + + case r => { + didAnything = false + r + } + } + (simped, didAnything) +} diff --git a/src/main/scala/ir/invariant/BlocksUniqueToProcedure.scala b/src/main/scala/ir/invariant/BlocksUniqueToProcedure.scala new file mode 100644 index 000000000..6250327dc --- /dev/null +++ b/src/main/scala/ir/invariant/BlocksUniqueToProcedure.scala @@ -0,0 +1,72 @@ +package ir.invariant +import ir.* +import ir.cilvisitor.* +import util.Logger +import scala.collection.mutable + +private class BVis extends CILVisitor { + var proc: Procedure = null + var block: Block = null + val gotoViolations = mutable.Set[GoTo]() + val blockMultiProcViolations = mutable.Set[Block]() + + override def vproc(p: Procedure) = { + proc = p + DoChildren() + } + + override def vblock(b: Block) = { + block = b + if (b.parent != proc) { + blockMultiProcViolations.add(b) + } + DoChildren() + } + + override def vstmt(s: Statement) = { + SkipChildren() + } + + override def vjump(j: Jump) = { + j match { + case g @ GoTo(targets, _) if !(targets.forall(t => t.parent == proc)) => gotoViolations.add(g) + case _ => () + } + + SkipChildren() + } +} + + +def blockUniqueLabels(p: Program) : Boolean = { + p.procedures.forall(blockUniqueLabels) +} + +def blockUniqueLabels(p: Procedure) : Boolean = { + val blockNames = mutable.Set[String]() + var passed = true + + for (block <- p.blocks) { + if (blockNames.contains(block.label)) { + passed = false + Logger.error("Duplicate block name: " + block.label) + } + blockNames.add(block.label) + } + passed +} + + +def blocksUniqueToEachProcedure(p: Program) : Boolean = { + val v = BVis() + visit_prog(v, p) + for (g <- v.gotoViolations) { + Logger.error(s"$g has target outside parent procedure ${g.parent.parent.name}") + } + + for (b <- v.blockMultiProcViolations) { + Logger.error(s"block ${b.label} is referenced in multiple procedures.") + } + + v.gotoViolations.isEmpty && v.blockMultiProcViolations.isEmpty +} diff --git a/src/main/scala/ir/invariant/CFGCorrect.scala b/src/main/scala/ir/invariant/CFGCorrect.scala new file mode 100644 index 000000000..caf61a677 --- /dev/null +++ b/src/main/scala/ir/invariant/CFGCorrect.scala @@ -0,0 +1,57 @@ +package ir.invariant +import ir.* +import ir.cilvisitor.* +import util.Logger +import scala.collection.mutable + + +def cfgCorrect(p: Program | Procedure) = { + + val forwardsInter = p.collect { + case d @ DirectCall(tgt, _ , _, _) => (d.parent.parent, tgt) + } + val revForwardsInter = forwardsInter.groupBy(_._2).map((dest, origs) => (dest, origs.map(_._1).toSet)).toMap + val forwardsInterMap = forwardsInter.groupBy(_._1).map((orig, dests) => (orig, dests.map(_._2).toSet)).toMap + + val forwardsIntra = p.collect { + case g @ GoTo(targets, _) => targets.map((t: Block) => (g.parent, t)) + }.flatten + + val revForwardsIntra = forwardsIntra.groupBy(_._2).map((dest, origs) => (dest, origs.map(_._1).toSet)).toMap + val forwardsIntraMap = forwardsIntra.groupBy(_._1).map((orig, dests) => (orig, dests.map(_._2).toSet)).toMap + + p.forall { + case b: Block => { + val backwards = (b.prevBlocks.toSet == revForwardsIntra.get(b).getOrElse(Set())) + val forwards = b.nextBlocks.toSet == forwardsIntraMap.get(b).getOrElse(Set()) + val c = forwards && backwards + if (!forwards) { + Logger.error(s"Forwards block cfg does not match : ${b.nextBlocks.toSet.map(_.label)} == ${forwardsIntraMap.get(b).getOrElse(Set()).map(_.label)}") + } + if (!backwards) { + Logger.error(s"Backward block cfg does not match : ${b.prevBlocks.toSet.map(_.label)} == ${revForwardsIntra.get(b).getOrElse(Set()).map(_.label)}") + } + c + } + case p: Procedure => { + val factual = p.calls.toSet + val fexpected = forwardsInterMap.get(p).getOrElse(Set()).toSet + val bactual = p.callers().toSet + val bexpected = revForwardsInter.get(p).getOrElse(Set()).toSet + if (factual != fexpected) { + Logger.error(s"${p.name} forwards proc cfg does not match : ${factual.map(_.name)} == ${fexpected.map(_.name)} ") + } + if (bactual != bexpected) { + Logger.error(p.incomingCalls().map(_.parent).toList.toString) + Logger.error(p.incomingCalls().map(_.parent.parent).toList.toString) + Logger.error(s"${p.name} backward proc cfg does not match : ${bactual.map(_.name)} == ${bexpected.map(_.name)} ") + } + + factual == fexpected && bactual == bexpected + } + case _ => true + } + + + +} diff --git a/src/main/scala/ir/invariant/CorrectCallParams.scala b/src/main/scala/ir/invariant/CorrectCallParams.scala new file mode 100644 index 000000000..d5a934340 --- /dev/null +++ b/src/main/scala/ir/invariant/CorrectCallParams.scala @@ -0,0 +1,56 @@ +package ir.invariant +import ir.* +import util.Logger + + +/* + * DirectCalls are provided actual and return parameters matchign their target's parameters + */ + + +def correctCalls(p: Program) : Boolean = { + p.forall { + case c: DirectCall => { + val t = c.target + val inparams = (c.actualParams.keySet == t.formalInParam) + val outparams = (c.outParams.keySet == t.formalOutParam) + val typecheck = c.actualParams.forall((k, v) => k.getType == v.getType) && c.outParams.forall((k,v) => k.getType == v.getType) + val r = inparams && outparams && typecheck + if (!inparams) { + Logger.error(s"call in arguments don't match formal params: ${c.target.name}(${c.actualParams}) != ${c.target.formalInParam}") + } + if (!outparams) { + Logger.error(s"call return lvalue doesnt match out params: ${c.outParams} := ${c.target.name}() = ${c.target.formalOutParam}") + } + if (!typecheck) { + val inp = c.actualParams.collect { + case (k,v) if k.getType != v.getType => s"$k := $v" + } + val out = c.outParams.collect { + case (k,v) if k.getType != v.getType => s"$v := $k" + } + Logger.error(s"Call $c doesn't typecheck${if inp.nonEmpty then " in : " else ""}${inp.mkString(" ")}${if out.nonEmpty then " out : " else ""}${out.mkString(" ")}") + } + + r + } + case c: Return => { + val t = c.parent.parent + val outparams = (c.outParams.keySet == t.formalOutParam) + val typecheck = c.outParams.forall((k, v) => k.getType == v.getType) && c.outParams.forall((k,v) => k.getType == v.getType) + if (!outparams) { + Logger.error(s"Return formal out params do do not match procedure formal out params: ${c.outParams} != ${t.formalOutParam}") + } + if (!typecheck) { + val out = c.outParams.collect { + case (k,v) if k.getType != v.getType => s"$k := $v" + } + Logger.error(s"doesn't typecheck:$c \n${if out.nonEmpty then " out : " else ""}${out.mkString(" ")}") + } + typecheck && outparams + } + case _ => true + } +} + + diff --git a/src/main/scala/ir/invariant/EarlyCallStatement.scala b/src/main/scala/ir/invariant/SingleCallBlockEnd.scala similarity index 96% rename from src/main/scala/ir/invariant/EarlyCallStatement.scala rename to src/main/scala/ir/invariant/SingleCallBlockEnd.scala index bb4504343..52e6e6ac9 100644 --- a/src/main/scala/ir/invariant/EarlyCallStatement.scala +++ b/src/main/scala/ir/invariant/SingleCallBlockEnd.scala @@ -1,5 +1,5 @@ package ir.invariant -import ir._ +import ir.* def singleCallBlockEnd(p: Program) : Boolean = { diff --git a/src/main/scala/ir/transforms/AbsInt.scala b/src/main/scala/ir/transforms/AbsInt.scala new file mode 100644 index 000000000..32f06de3a --- /dev/null +++ b/src/main/scala/ir/transforms/AbsInt.scala @@ -0,0 +1,173 @@ +package ir.transforms +import translating.serialiseIL + +import util.Logger +import ir.eval.AlgebraicSimplifications +import ir.eval.AssumeConditionSimplifications +import ir.eval.simplifyExprFixpoint +import ir.cilvisitor.* +import ir.* +import scala.collection.mutable +import analysis._ +import scala.concurrent.{Await, ExecutionContext, Future} +import scala.concurrent.duration.* +import scala.util.{Failure, Success} +import ExecutionContext.Implicits.global + + +trait AbstractDomain[L] { + def join(a: L, b: L, pos: Block): L + def widen(a: L, b: L, pos: Block): L = join(a, b, pos) /* not used */ + def narrow(a: L, b: L): L = a + def transfer(a: L, b: Command): L + + def isFixed(prev: L, next: L) : Boolean = (prev == next) + + def transferBlockFwd(a: L, b: Block): L = { + transfer(b.statements.foldLeft(a)(transfer), b.jump) + } + def transferBlockBwd(a: L, b: Block): L = { + b.statements.toList.reverse.foldLeft(transfer(a, b.jump))(transfer) + } + + def top: L + def bot: L +} + +trait PowerSetDomain[T] extends AbstractDomain[Set[T]] { + def bot = Set() + def top = ??? + def join(a: Set[T], b: Set[T], pos: Block) = a.union(b) +} + + +def onePassForwardGlobalStateSolver[L, D <: AbstractDomain[L]](initial: Iterable[Block], domain: D) = { + val worklist = mutable.PriorityQueue[Block]()(Ordering.by(_.rpoOrder)) + worklist.addAll(initial) + var state = domain.bot + + while (worklist.nonEmpty) { + val b: Block = worklist.dequeue + val p = state + + for (l <- b.statements) { + state = domain.transfer(state, l) + } + state = domain.transfer(state, b.jump) + } + +} + +class worklistSolver[L, A <: AbstractDomain[L]](domain: A) { + + def solveProc(p: Procedure, backwards: Boolean = false) = { + solve(p.blocks, Set(), Set(), backwards) + } + + def solveProg( + p: Program, + widenpoints: Set[Block], // set of loop heads + narrowpoints: Set[Block] // set of conditions + ): Map[Procedure, Map[Block, L]] = { + val initDom = p.procedures.map(p => (p, p.blocks)) + + val work = initDom.map(d => { + ( + d._1, + Future { + val t = util.PerformanceTimer(s"solve ${d._1.name}") + Logger.info(s"begin ${t.timerName}") + val r = solve(d._2, Set(), Set()) + t.checkPoint("finished") + r + } + ) + }) + work + .map((prog, x) => + try { + (prog, Await.result(x, 10000.millis)._2) + } catch { + case t: Exception => { + Logger.error(s"${prog.name} : $t") + (prog, Map()) + } + } + ) + .toMap + // Await.result(Future.sequence(work), Duration.Inf).toMap + } + + def solve( + initial: IterableOnce[Block], + widenpoints: Set[Block], // set of loop heads + narrowpoints: Set[Block], // set of conditions + backwards: Boolean = false + ): (Map[Block, L], Map[Block, L]) = { + val savedAfter: mutable.HashMap[Block, L] = mutable.HashMap() + val savedBefore: mutable.HashMap[Block, L] = mutable.HashMap() + val saveCount: mutable.HashMap[Block, Int] = mutable.HashMap() + val worklist = { + if (backwards) { + mutable.PriorityQueue[Block]()(Ordering.by(b => -b.rpoOrder)) + } else { + mutable.PriorityQueue[Block]()(Ordering.by(b => b.rpoOrder)) + } + } + worklist.addAll(initial) + + def successors(b: Block) = if backwards then b.prevBlocks else b.nextBlocks + def predecessors(b: Block) = if backwards then b.nextBlocks else b.prevBlocks + + while (worklist.nonEmpty) { + val b = worklist.dequeue + + while ( + worklist.nonEmpty && (if backwards then (worklist.head.rpoOrder <= b.rpoOrder) + else (worklist.head.rpoOrder >= b.rpoOrder)) + ) do { + // drop rest of blocks with same priority + val m = worklist.dequeue() + assert( + m == b, + s"Different nodes with same priority ${m.rpoOrder} ${b.rpoOrder}, violates PriorityQueueWorklist assumption: $b and $m" + ) + } + + val prev = savedAfter.get(b) + val x = { + predecessors(b).toList.flatMap(b => savedAfter.get(b).toList) match { + case Nil => domain.bot + case h :: Nil => h + case h :: tl => tl.foldLeft(h)((acc, nb) => domain.join(acc, nb, b)) + } + } + savedBefore(b) = x + val todo = List(b) + + val lastBlock = b // todo.last + var nx = todo.foldLeft(x)((x, b) => { + savedBefore(b) = x + if (backwards) { + val ojmp = domain.transfer(x, b.jump) + savedAfter(b) = b.statements.toList.reverse.foldLeft(ojmp)(domain.transfer) + } else { + val stmts = b.statements.foldLeft(x)(domain.transfer) + savedAfter(b) = domain.transfer(stmts, b.jump) + } + savedAfter(b) + }) + savedAfter(lastBlock) = nx + saveCount(lastBlock) = saveCount.get(lastBlock).getOrElse(0) + 1 + if (!prev.contains(nx)) then { + if (saveCount(lastBlock) >= 50) { + Logger.warn(s"Large join count on block ${lastBlock.label}, no fix point? (-v for mor info)") + Logger.debug(lastBlock.label + " ==> " + x) + Logger.debug(lastBlock.label + " <== " + nx) + } + worklist.addAll(successors(lastBlock)) + } + } + if backwards then (savedAfter.toMap, savedBefore.toMap) else (savedBefore.toMap, savedAfter.toMap) + } +} diff --git a/src/main/scala/ir/transforms/DynamicSingleAssignment.scala b/src/main/scala/ir/transforms/DynamicSingleAssignment.scala new file mode 100644 index 000000000..9f33ac2d9 --- /dev/null +++ b/src/main/scala/ir/transforms/DynamicSingleAssignment.scala @@ -0,0 +1,535 @@ +package ir.transforms + +import util.Logger +import ir.cilvisitor.* +import translating.* +import ir.* +import scala.collection.mutable +import analysis._ + +val phiAssignLabel = Some("phi") + +/** This transforms the program by adding no-op copies and renaming local variable indices to establish the property + * that + * + * \forall variables v, forall uses of v : u, No subset of definitions of v defines the use u. + */ + +class OnePassDSA( + /** Check our (faster) live var result against the TIP sovler solution + */ +) { + + val liveVarsDom = transforms.IntraLiveVarsDomain() + val liveVarsSolver = transforms.worklistSolver(liveVarsDom) + + case class BlockState( + renamesBefore: mutable.Map[Variable, Int] = mutable.Map[Variable, Int](), + renamesAfter: mutable.Map[Variable, Int] = mutable.Map[Variable, Int](), + var filled: Boolean = false, /* have given local value numbering */ + var completed: Boolean = false, /* have filled and processed all incoming */ + var isPhi: Boolean = false /* begins filled */ + ) + + def renameLHS(c: Command, variable: Variable, index: Int) = { + c match { + case s: Statement => visit_stmt(StmtRenamer(Map((variable -> index)), Map()), s) + case j: Jump => visit_jump(StmtRenamer(Map((variable -> index)), Map()), j) + } + } + + def renameRHS(c: Command, variable: Variable, index: Int) = { + c match { + case s: Statement => visit_stmt(StmtRenamer(Map(), Map((variable -> index))), s) + case j: Jump => visit_jump(StmtRenamer(Map(), Map((variable -> index))), j) + } + } + + def appendAssign(b: Block, s: LocalAssign) = { + // maintain call end of lock invariant + if (b.statements.size > 0 && b.statements.last.isInstanceOf[Call]) { + b.statements.insertBefore(b.statements.last, s) + } else { + b.statements.append(s) + } + } + + def withDefault(_st: mutable.Map[Block, BlockState])(b: Block) = { + if _st.contains(b) then _st(b) + else { + _st(b) = BlockState() + _st(b) + } + } + + def localProcessBlock( + state: mutable.Map[Block, BlockState], + count: mutable.Map[Variable, Int], + block: Block + ): Unit = { + def st(b: Block) = withDefault(state)(b) + + var renames = st(block).renamesBefore + + for (s <- block.statements) { + visit_stmt(StmtRenamer(Map(), renames.toMap), s) + s match { + case d: Assign => { + val vars = d.assignees + for (lhs <- vars) { + count(lhs) = count(lhs) + 1 + renameLHS(d, lhs, count(lhs)) + renames = renames + (lhs -> count(lhs)) + } + } + case _ => () + } + } + + visit_jump(StmtRenamer(Map(), renames.toMap), block.jump) + st(block).renamesAfter.addAll(renames) + st(block).filled = true + + } + + def applyTransform(p: Program): Unit = { + for (proc <- p.procedures) { + applyTransform(proc) + } + } + + def createBlockBetween(b1: Block, b2: Block, label: String = "_phi_"): Block = { + require(b1.nextBlocks.toSet.contains(b2)) + val nb = Block(b1.label + label + b2.label) + b1.parent.addBlocks(nb) + b1.jump match { + case g: GoTo => { + g.addTarget(nb) + g.removeTarget(b2) + } + case _ => ??? + } + nb.replaceJump(GoTo(b2)) + nb + } + + def fixPredecessors( + _st: mutable.Map[Block, BlockState], + count: mutable.Map[Variable, Int], + liveBefore: mutable.Map[Block, Set[Variable]], + block: Block + ) = { + def state(b: Block) = withDefault(_st)(b) + + val preds = block.prevBlocks.toList + val toJoin = preds.filter(state(_).filled) + assert(!(toJoin.isEmpty && preds.nonEmpty), s"should always have at least one processed predecessor ${preds}") + + { + val definedVars = toJoin.flatMap(state(_).renamesAfter.keySet).toSet.intersect(liveBefore(block)) + val toUnify = definedVars + .map(v => v -> toJoin.map(state(_).renamesAfter.get(v).getOrElse(0))) + .filter((v, rns) => { + rns.toList match { + case Nil => false + case h :: Nil => false + // if there is no renaming such that all the incoming renames agree + // then we create a new copy + case h :: tl => + tl.foldLeft(Some(h): Option[Int])((acc: Option[Int], rn: Int) => + acc match { + case Some(v) if v == rn => Some(v) + case _ => None + } + ).isEmpty + } + }) + + if (toUnify.nonEmpty) { + val blocks = toJoin.map(b => b -> createBlockBetween(b, block, "_phi_back_")).toMap + for (v <- toUnify.map(_._1)) { + count(v) = count(v) + 1 + // new index for new copy of v (definition added to all incoming edges) + + for (b <- toJoin) { + val nb = blocks(b) + assert(state(b).filled) + state(nb).renamesBefore.addAll(state(b).renamesAfter) + + val assign = LocalAssign(v, v, Some("phiback")) + state(nb).renamesAfter(v) = count(v) + appendAssign(nb, assign) + renameLHS(assign, v, state(nb).renamesAfter(v)) + renameRHS(assign, v, state(nb).renamesBefore.get(v).getOrElse(0)) + state(block).renamesBefore.addAll(state(nb).renamesAfter) + } + + } + } else { + // all our incoming are equal, or we have only one predecessor etc + for (b <- toJoin) { + state(block).renamesBefore.addAll(state(b).renamesAfter) + } + } + } + // set completed + if (toJoin.size == preds.size) { + state(block).completed = true + } else { + state(block).completed = false + } + + } + + def fixSuccessors( + _st: mutable.Map[Block, BlockState], + count: mutable.Map[Variable, Int], + liveBefore: mutable.Map[Block, Set[Variable]], + liveAfter: mutable.Map[Block, Set[Variable]], + block: Block + ) = { + def state(b: Block) = withDefault(_st)(b) + + val next = block.nextBlocks.toList + val anyNextFilled = next.exists(state(_).filled) + val anyNextPrevNotFilled = next.exists(_.prevBlocks.exists(b => !state(b).filled)) + for (b <- next) { + val definedVars = state(block).renamesAfter.keySet.intersect(liveAfter(block)) + + if (definedVars.size > 0 && (anyNextPrevNotFilled)) { + val nb = createBlockBetween(block, b, "_phi_") + + state(nb).renamesBefore.addAll(state(block).renamesAfter) + if (state(b).filled) { + // if filled we have chosen an incoming rename + state(nb).renamesAfter.addAll(state(b).renamesBefore) + } + + for (v <- definedVars) { + if (!state(nb).renamesAfter.contains(v)) { + count(v) = count(v) + 1 + state(nb).renamesAfter(v) = count(v) + } + if (state(nb).renamesBefore(v) != state(nb).renamesAfter(v)) { + val assign = LocalAssign(v, v, phiAssignLabel) + appendAssign(nb, assign) + renameLHS(assign, v, state(nb).renamesAfter(v)) + renameRHS(assign, v, state(nb).renamesBefore(v)) + } + } + state(nb).filled = true + state(nb).isPhi = true + liveBefore(nb) = liveBefore(b) + liveAfter(nb) = liveBefore(b) + } + } + } + + def visitBlock( + _st: mutable.Map[Block, BlockState], + count: mutable.Map[Variable, Int], + liveBefore: mutable.Map[Block, Set[Variable]], + liveAfter: mutable.Map[Block, Set[Variable]], + block: Block + ) = { + + /** VisitBlock: + * + * 1. for all complete incoming + * - if there is an incoming rename that is not equal across the predecessors + * - add back phi block to each incoming edge + * - create a fresh copy of each non-uniform renamed variable + * - add copies to each phi block unify the incoming rename with the nominated new rename 2. add local value + * numbering to this block 3. if all predecessors are filled, mark this complete, otherwise mark this + * filled. 4. for all successors + * - if marked filled, add phi block to unify our outgoing with its incoming + * - if > 1 total predecessors for each unmarked , add phi block to nominate a new copy 5. for all successors, if + * all predecessors are now filled, mark complete + */ + + def state(b: Block) = withDefault(_st)(b) + + // add copies on incoming edges to make the outgoing rename of all filled precesessors + // the same, and define `block`'s incoming rename + fixPredecessors(_st, count, liveBefore, block) + + // apply local value numbering to `block` + var seenBefore = true + if (!(state(block).filled)) { + localProcessBlock(_st, count, block) + state(block).filled = true + assert(state(block).filled) + seenBefore = false + } + + // mark successors complete which are completed as a result of processing `block` + for (b <- block.nextBlocks) { + if (b.prevBlocks.forall(state(_).filled)) { + state(b).completed = true + } + } + + // add outgoing copies for e.g. loop headers + fixSuccessors(_st, count, liveBefore, liveAfter, block) + } + + def applyTransform(p: Procedure): Unit = { + val _st = mutable.Map[Block, BlockState]() + // ensure order is defined + reversePostOrder(p) + + val (liveBeforeIn, liveAfterIn) = liveVarsSolver.solveProc(p, backwards = true) + val liveBefore = mutable.Map.from(liveBeforeIn) + val liveAfter = mutable.Map.from(liveAfterIn) + + for (b <- p.blocks.filterNot(liveBeforeIn.contains)) { + liveBefore(b) = liveVarsDom.bot + } + for (b <- p.blocks.filterNot(liveAfterIn.contains)) { + liveAfter(b) = liveVarsDom.bot + } + + val worklist = mutable.PriorityQueue[Block]()(Ordering.by(b => b.rpoOrder)) + worklist.addAll(p.blocks) + var seen = Set[Block]() + val count = mutable.Map[Variable, Int]().withDefaultValue(0) + + while (worklist.nonEmpty) { + while (worklist.nonEmpty) { + val block = worklist.dequeue + assert(worklist.headOption.map(_.rpoOrder < block.rpoOrder).getOrElse(true)) + + visitBlock(_st, count, liveBefore, liveAfter, block) + } + } + + // fix up rpo index of added phi blocks + reversePostOrder(p) + } + +} + +class StmtRenamer(renamesL: Map[Variable, Int] = Map(), renames: Map[Variable, Int] = Map()) extends CILVisitor { + + private def addIndex(v: Variable, idx: Int) = { + assert(idx != -1) + v match { + case Register(n, sz) => { + throw Exception("Should not SSA registers") + Register(n + "_" + idx, sz) + } + case v: LocalVar => LocalVar(v.varName, v.irType, idx) + } + } + + override def vrvar(v: Variable) = v match { + case v if renames.contains(v) && renames(v) != -1 => ChangeTo(addIndex(v, renames(v))) + case _ => DoChildren() + } + + override def vlvar(v: Variable) = v match { + case v if renamesL.contains(v) && renamesL(v) != -1 => ChangeTo(addIndex(v, renamesL(v))) + case _ => DoChildren() + } +} + +def rdDSAProperty(p: Procedure): Boolean = { + /* + * Check the DSA property using a reaching definitions analysis. + * DSA Property: Every use of a variable v has every definition of v as a reaching definition + * / no strict subset of defintions of v defines any use of v, forall v. + */ + val defs: Map[Variable, Set[Assign]] = p + .flatMap { + case a: SingleAssign => Seq((a.lhs, (a: Assign))) + case a: DirectCall => a.outParams.map(_._2).map((l: Variable) => (l, (a: Assign))).toSeq + case _ => Seq() + } + .groupBy(_._1) + .map((v, vs) => (v, vs.map(_._2).toSet)) + + Logger.debug(s"Reaching defs ${p.name}") + val reachingDefs = basicReachingDefs(p) + Logger.debug(s"Reaching defs ${p.name} DONE") + + class CheckDSAProperty( + defs: Map[Variable, Set[Assign]], + reaching: Map[Command, Map[Variable, Set[Assign]]] + ) extends CILVisitor { + var passed = true + var stmt: Command = null + val violations = mutable.HashSet[(Command, Variable)]() + + override def vrvar(v: Variable) = { + val allDefs = defs.get(v).toSet.flatten + val reachDefs = reachingDefs.get(stmt).flatMap(_.get(v)).toSet.flatten + + val check = allDefs == reachDefs + if (!check) { + val vil = (stmt, v) + if (!violations.contains(vil)) { + violations.add(vil) + // Logger.error(s"DSA Property violated on $v at $stmt @ ${stmt.parent.parent.name}::${stmt.parent.label}\n\t ${allDefs.diff(reachDefs)} defs not reached") + Logger.error( + s"DSA Property violated on $v at $stmt @ ${stmt.parent.parent.name}::${stmt.parent.label}\n\t ${allDefs + .diff(reachDefs)} defs not reached\n\t${reachDefs}" + ) + } + } + passed = passed && check + SkipChildren() + } + override def vstmt(v: Statement) = { + stmt = v + DoChildren() + } + override def vjump(j: Jump) = { + stmt = j + DoChildren() + } + + } + + val vis = CheckDSAProperty(defs, reachingDefs) + visit_proc(vis, p) + if (vis.passed) { + Logger.debug(s"${p.name} DSA check OK") + } + vis.passed +} + +object DSAPropCheck { + // check the property that no strict subset of definitions dominates a use + // + // This attempts to generate an SMT proof of this, by encoding the reaching definitions in SMT2 + // Likely this does not work. + + def getUses(s: CFGPosition): Set[Variable] = { + s match { + case a: LocalAssign => a.rhs.variables + case a: MemoryStore => a.index.variables ++ a.value.variables + case a: MemoryLoad => a.index.variables + case a: DirectCall => a.actualParams.flatMap(_._2.variables).toSet + case a: Return => a.outParams.flatMap(_._2.variables).toSet + case a: IndirectCall => Set(a.target) + case a: Assert => a.body.variables + case a: Assume => a.body.variables + case _ => Set() + } + } + + def getDefinitions(s: CFGPosition): Set[Variable] = { + s match { + case a: Assign => a.assignees + case _ => Set() + } + } + + def emitProof(proc: Procedure) = { + + var fresh = 0 + val vartoIdx = mutable.Map[Variable, Int]() + val nodeToIdx = mutable.Map[CFGPosition, Int]() + + val blockcfg = proc.blocks.flatMap(b => { + val pred = IRWalk.lastInBlock(b) + b.statements.map(s => (s, s.successor)) ++ + b.nextBlocks + .map(IRWalk.firstInBlock) + .map(succ => { + (pred, succ) + }) + }) + + def getVarIdx(n: Variable): Int = { + if (vartoIdx.contains(n)) { + vartoIdx(n) + } else { + fresh += 1 + vartoIdx(n) = fresh + vartoIdx(n) + } + } + + def getGraphNode(p: CFGPosition): Int = { + // val idx = getVarIdx(v) // put in map + val n = p + if (nodeToIdx.contains(n)) { + nodeToIdx(n) + } else { + fresh += 1 + nodeToIdx(n) = fresh + nodeToIdx(n) + } + } + + def defTruePredicate(name: String, args: List[IRType]) = + list( + sym("declare-fun"), + sym(name), + Sexp.Slist(args.map(i => BasilIRToSMT2.basilTypeToSMTType(i)).toList), + BasilIRToSMT2.basilTypeToSMTType(BoolType) + ) + + def dominates[T](pred: Sexp[T], succ: Sexp[T]) = { + list(sym("assert"), list(sym("dominates"), pred, succ)) + } + def notDominates[T](pred: Sexp[T], succ: Sexp[T]) = { + list(sym("assert"), list(sym("not"), list(sym("dominates"), pred, succ))) + } + + val edges = blockcfg.map((p, s) => ((getGraphNode(p)), (getGraphNode(s)))).toSet + val nodes = edges.flatMap((a, b) => Seq(a, b)) + + val doms = nodes.flatMap(nn1 => { + nodes.flatMap(nn2 => { + val (n1, n2) = (BasilIRToSMT2.int2smt(nn1), BasilIRToSMT2.int2smt(nn2)) + if (edges.contains((nn1, nn2))) { + Seq(dominates(n1, n2)) + } else { + Seq() + // notDominates(n1, n2) + } + }) + }) + + val res: List[Sexp[_]] = List( + defTruePredicate("isUse", List(IntType, IntType)), // node, var + defTruePredicate("isDef", List(IntType, IntType)), // node, var + defTruePredicate("dominates", List(IntType, IntType)) // node, node + ) ++ doms + + val written = res.map(Sexp.print) + + val vars = nodeToIdx.flatMap((p, nodeID) => { + getUses(p).map(vn => { + val v = getVarIdx(vn) + list(sym("assert"), list(sym("isUse"), BasilIRToSMT2.int2smt(nodeID), BasilIRToSMT2.int2smt(v))) + }) + ++ + getDefinitions(p).map(vn => { + val v = getVarIdx(vn) + list(sym("assert"), list(sym("isUse"), BasilIRToSMT2.int2smt(nodeID), BasilIRToSMT2.int2smt(v))) + }) + }) + val axioms = List( + "(assert (forall ((x Int) (y Int) (z Int)) (implies (and (dominates x y) (dominates y z)) (dominates x z))))", + "(assert (not (forall ((n Int) (v Int) (v2 Int)) (and (isDef n v) (isUse n v2)))))", + // check of dsa property + """(declare-fun defines (Int Int) Bool) +(assert (forall ((d Int) (u Int) (i Int) (v Int)) (implies (and (isUse u v) (isDef d v) (dominates d u) (not (and (dominates d i) (dominates i u) (isDef i v)))) (defines d u)))) +(assert (not (forall ((usenode Int) (variable Int)) + (implies (isUse usenode variable) + (forall ((defnode Int)) + (implies (isDef defnode variable) + (defines defnode usenode))))))) + """ + ) + + util.writeToFile( + (written ++ vars.map(Sexp.print) ++ axioms).mkString("\n") + "\n(check-sat)", + s"proofs/${proc.name}-dsa-graph.smt2" + ) + } + +} diff --git a/src/main/scala/ir/transforms/IndirectCallResolution.scala b/src/main/scala/ir/transforms/IndirectCallResolution.scala index a643062cb..cbd3a4c57 100644 --- a/src/main/scala/ir/transforms/IndirectCallResolution.scala +++ b/src/main/scala/ir/transforms/IndirectCallResolution.scala @@ -1,11 +1,12 @@ package ir.transforms +import scala.collection.mutable +import scala.collection.mutable.{ArrayBuffer, ListBuffer} + import analysis.{AddressValue, DataRegion, Lift, LiftedElement, LiteralValue, MemoryModelMap, MemoryRegion, RegisterWrapperEqualSets, StackRegion, Value, getUse} import ir.* import util.Logger - -import scala.collection.mutable -import scala.collection.mutable.{ArrayBuffer, ListBuffer} +import cilvisitor.* class SteensgaardIndirectCallResolution( @@ -152,7 +153,7 @@ trait IndirectCallResolution { } if (targets.size == 1) { - val newCall = DirectCall(targets.head, indirectCall.label) + val newCall = targets.head.makeCall(indirectCall.label) block.statements.replace(indirectCall, newCall) true } else if (targets.size > 1) { @@ -168,7 +169,7 @@ trait IndirectCallResolution { } val assume = Assume(BinaryExpr(BVEQ, indirectCall.target, BitVecLiteral(address, 64))) val newLabel: String = block.label + t.name - val directCall = DirectCall(t) + val directCall = t.makeCall() /* copy the goto node resulting */ val fallthrough = oft match { @@ -198,4 +199,4 @@ trait IndirectCallResolution { def resolveAddresses(variable: Variable, i: IndirectCall): Set[String] -} \ No newline at end of file +} diff --git a/src/main/scala/ir/transforms/ProcedureParameters.scala b/src/main/scala/ir/transforms/ProcedureParameters.scala new file mode 100644 index 000000000..f0db75e9b --- /dev/null +++ b/src/main/scala/ir/transforms/ProcedureParameters.scala @@ -0,0 +1,423 @@ +package ir.transforms +import ir.cilvisitor.* +import ir.* +import scala.collection.mutable.ArrayBuffer +import scala.collection.{mutable, immutable} +import collection.immutable.SortedMap +import specification.Specification +import analysis.{TwoElement, TwoElementTop, TwoElementBottom} +import ir.CallGraph + +case class FunSig(inArgs: List[Register], outArgs: List[Register]) + +def R(n: Int) = { + Register(s"R$n", 64) +} + +val builtinSigs : Map[String, FunSig] = Map( + "#free" -> FunSig(List(R(0)), List(R(0))), + "malloc" -> FunSig(List(R(0)), List(R(0))), + "strlen" -> FunSig(List(R(0)), List(R(0))), + "strchr" -> FunSig(List(R(0), R(1)), List(R(0))), + "strlcpy" -> FunSig(List(R(0), R(1), R(2)), List(R(0))), + "strlcat" -> FunSig(List(R(0), R(1), R(2)), List(R(0))) + ) + +def fnsigToBinding(f: FunSig) = (f.inArgs.map(a => LocalVar(a.name + "_in", a.getType) -> LocalVar(a.name, a.getType)), + f.outArgs.map(a => LocalVar(a.name + "_out", a.getType) -> LocalVar(a.name, a.getType))) + +def liftProcedureCallAbstraction(ctx: util.IRContext): util.IRContext = { + + val liveVars = + if (ctx.program.mainProcedure.blocks.nonEmpty && ctx.program.mainProcedure.returnBlock.isDefined && ctx.program.mainProcedure.entryBlock.isDefined) { + analysis.InterLiveVarsAnalysis(ctx.program).analyze() + } else { + Map.empty + } + + val params = inOutParams(ctx.program, liveVars) + + // functions for which we don't know their behaviour and assume they modify all registers + val external = ctx.externalFunctions.map(_.name) ++ ctx.program.collect { + case b: Procedure if b.blocks.isEmpty => b.name + } + + val formalParams = SetFormalParams(params, external) + visit_prog(formalParams, ctx.program) + val actualParams = SetActualParams(formalParams.mappingInparam, formalParams.mappingOutparam, external) + visit_prog(actualParams, ctx.program) + + ctx.copy(specification = specToProcForm(ctx.specification, formalParams.mappingInparam, formalParams.mappingOutparam)) +} + +def clearParams(p: Program) = { + + class RemoveCallParams extends CILVisitor { + override def vstmt(s: Statement) = s match { + case d: DirectCall => + ChangeTo(List(DirectCall(d.target, d.label, immutable.SortedMap.empty, immutable.SortedMap.empty))) + case _ => SkipChildren() + } + } + + for (c <- p) { + c match { + case proc: Procedure => { + proc.formalInParam.clear + proc.formalOutParam.clear + proc.inParamDefaultBinding = immutable.SortedMap.empty + proc.outParamDefaultBinding = immutable.SortedMap.empty + } + case _ => () + } + } + + visit_prog(RemoveCallParams(), p) +} + +def collectVariables(p: Procedure): (Set[Variable], Set[Variable]) = { + val lvars = p.blocks.toSet.flatMap(_.statements.flatMap(s => { + s match { + case LocalAssign(l, _, _) => Set(l) + case DirectCall(t, o, _, _) => o.toSet.map(_._2) + case _ => Set() + } + })) ++ p.blocks + .map(_.jump) + .collect { case r: Return => + r.outParams.toSet.map(_._1) + } + .flatten + val rvars = p.blocks.toSet.flatMap(_.statements.flatMap(s => { + s match { + case LocalAssign(l, r, _) => r.variables + case Assume(l, _, _, _) => l.variables + case Assert(l, _, _) => l.variables + case MemoryStore(m, i, v, _, _, _) => i.variables ++ v.variables + case MemoryLoad(lhs, m, index, endian, size, label) => index.variables ++ Seq(lhs) + case IndirectCall(l, _) => Set(l) + case DirectCall(t, o, l, _) => l.toSet.flatMap(_._2.variables) + case _ => Set() + } + })) + + (lvars.toSet, rvars.toSet) +} + +class SetFormalParams( + val inoutparams: Map[Procedure, (Set[Variable], Set[Variable])], + val externalFunctions: Set[String] +) extends CILVisitor { + // expects programs to be in single return form + + var mappingOutparam = Map[Procedure, Map[LocalVar, Variable]]() + var mappingInparam = Map[Procedure, Map[LocalVar, Variable]]() + + def externalIn: Map[LocalVar, Variable] = { + (0 to 31).map(i => LocalVar(s"R${i}_in", BitVecType(64)) -> LocalVar(s"R$i", BitVecType(64))).toMap + } + def externalOut: Map[LocalVar, Variable] = { + (0 to 31).map(i => LocalVar(s"R${i}_out", BitVecType(64)) -> LocalVar(s"R$i", BitVecType(64))).toMap + } + + override def vproc(p: Procedure) = { + if (externalFunctions.contains(p.name)) { + + + + p.formalInParam = mutable.SortedSet.from(externalIn.map(_._1)) + p.formalOutParam = mutable.SortedSet.from(externalOut.map(_._1)) + p.inParamDefaultBinding = immutable.SortedMap.from(externalIn) + p.outParamDefaultBinding = immutable.SortedMap.from(externalOut) + mappingInparam = mappingInparam.updated(p, externalIn) + mappingOutparam = mappingOutparam.updated(p, externalOut) + SkipChildren() + } else { + val (lvars, rvars) = collectVariables(p) + + val in = IRWalk.firstInProc(p) + if (in.isDefined) { + val inparams = inoutparams(p)._1.map(v => (LocalVar(v.name + "_in", v.getType), LocalVar(v.name, v.getType))) + p.formalInParam.addAll(inparams.map(_._1)) + mappingInparam = mappingInparam.updated(p, inparams.toMap) + p.inParamDefaultBinding = immutable.SortedMap.from(inparams) + } + + // outparams is everything touched + val outparams = inoutparams(p)._2.map(v => LocalVar(v.name + "_out", v.getType) -> LocalVar(v.name, v.getType)) + p.formalOutParam = mutable.SortedSet.from(outparams.map(_._1)) + mappingOutparam = mappingOutparam.updated(p, outparams.toMap) + p.outParamDefaultBinding = immutable.SortedMap.from(outparams) + + SkipChildren() + } + } +} + +object ReadWriteAnalysis { + sealed trait RW { + def getRWSet: Option[RWSet] = { + this match { + case Top => None + case r: RWSet => Some(r) + } + } + def map[B](f: RW => RW) = { + f(this) + } + } + case class RWSet(reads: Set[Variable], writes: Set[Variable]) extends RW + case object Top extends RW + + type st = Map[Procedure, RW] + + def addReads(r: Iterable[Variable])(i: RW) = { + i match { + case Top => Top + case i: RWSet => i.copy(reads = i.reads ++ r) + } + } + def addWrites(w: Iterable[Variable])(i: RW) = { + i match { + case Top => Top + case i: RWSet => i.copy(writes = i.writes ++ w) + } + } + + def join(a: RW, b: RW): RW = { + (a, b) match { + case (Top, _) => Top + case (_, Top) => Top + case (a: RWSet, b: RWSet) => RWSet(a.reads ++ b.reads, a.writes ++ b.writes) + } + } + + def processProc(state: st, p: Procedure): RW = { + p.foldLeft(state(p))((ir, s) => { + s match { + case s: LocalAssign => { + ir.map(addWrites(Seq(s.lhs))) + .map(addReads(s.rhs.variables)) + } + case s: MemoryLoad => { + ir.map(addWrites(Seq(s.lhs))) + .map(addReads(s.index.variables)) + } + case s: Return => { + ir.map(addReads(s.outParams.flatMap(_._2.variables))) + } + case s: MemoryStore => { + ir.map(addReads(s.index.variables ++ s.value.variables)) + } + case s: DirectCall => { + ir.map(x => join(x, state(s.target))) + .map(addReads(s.actualParams.flatMap(_._2.variables))) + .map(addWrites(s.outParams.flatMap(_._2.variables))) + } + case s: IndirectCall => Top + case s: Assert => ir.map(addReads(s.body.variables)) + case s: Assume => ir.map(addReads(s.body.variables)) + case p: Procedure => ir + case b: Block => ir + case b: NOP => ir + case b: Unreachable => ir + case b: GoTo => ir + } + }) + } + + def readWriteSets(p: Program): Map[Procedure, Option[RWSet]] = { + var state: st = Map[Procedure, RW]().withDefaultValue(RWSet(Set(), Set())) + val worklist = mutable.Stack.from(p.procedures) + + while (worklist.nonEmpty) { + val proc = worklist.pop + val o = state(proc) + val n = processProc(state, proc) + if (o != n) { + worklist.addAll(CallGraph.pred(proc)) + worklist.addAll(CallGraph.succ(proc)) + state = state.updated(proc, n) + } + } + + state.map(x => (x._1, x._2.getRWSet)) + } +} + +def inOutParams( + p: Program, + interLiveVarsResults: Map[CFGPosition, Map[Variable, TwoElement]] +): Map[Procedure, (Set[Variable], Set[Variable])] = { + val overapprox = ((0 to 31).toSet -- (19 to 28).toSet).map(i => Register(s"R${i}", 64)).toSet[Variable] + // in: live at entry & in procedure read set + + val readWrites = ReadWriteAnalysis.readWriteSets(p: Program).collect { + case (p, None) => (p, ReadWriteAnalysis.RWSet(overapprox, overapprox)) + case (p, Some(x)) => (p, x) + } + + val procEnd = p.procedures.map { case p => + p -> p.returnBlock.getOrElse(p) + }.toMap + + val lives : Map[Procedure, (Set[Variable], Set[Variable])] = p.procedures + .map(p => { + val in = (interLiveVarsResults.get(p)) + val out = (interLiveVarsResults.get(procEnd(p))) + + def toLiveSet(p: Option[Map[Variable, TwoElement]]): Set[Variable] = { + p.map(p => { + p.collect { case (v, TwoElementTop) => + v + }.toSet + }).getOrElse(overapprox) + } + p -> (toLiveSet(in), toLiveSet(out)) + }) + .toMap + + val inout = readWrites.collect { + case (proc, rws) if p.mainProcedure == proc => { + // no callers of main procedure so keep the whole read/write set + // of registers + proc -> ((lives(proc)._1.intersect(rws.reads)), (overapprox.intersect(rws.writes))) + } + case (proc, rws) => { + val liveStart = lives(proc)._1 + val liveEnd = lives(proc)._2 + + proc -> (liveStart.intersect(rws.reads), liveEnd.intersect(rws.writes)) + } + }.toMap + + inout.withDefaultValue((overapprox, overapprox)) +} + +class SetActualParams( + val inBinding: Map[Procedure, Map[LocalVar, Variable]], + val outBinding: Map[Procedure, Map[LocalVar, Variable]], + val externalFunctions: Set[String] +) extends CILVisitor { + // expects programs to be in single return form + var currStmt: Option[Statement] = None + + def externalIn: Map[LocalVar, Variable] = { + (0 to 31).map(i => LocalVar(s"R${i}_in", BitVecType(64)) -> LocalVar(s"R$i", BitVecType(64))).toMap + } + def externalOut: Map[LocalVar, Variable] = { + (0 to 31).map(i => LocalVar(s"R${i}_out", BitVecType(64)) -> LocalVar(s"R$i", BitVecType(64))).toMap + } + + override def vproc(p: Procedure) = { + val incoming = + p.formalInParam.toList.flatMap(param => inBinding.get(p).flatMap(_.get(param)).map(p => LocalAssign(p, param)).toList) + p.entryBlock.foreach(b => b.statements.prependAll(incoming)) + DoChildren() + } + + override def vstmt(s: Statement) = { + currStmt = Some(s) + s match { + // case d: DirectCall if !externalFunctions.contains(d.target.name) => { + // // we have changed the parameter-passed variable to locals so we have LocalVar(n) -> LocalVar(n) + // for (binding <- inBinding.get(d.target)) { + // if (externalFunctions.contains(d.target.name)) { + // d.actualParams = SortedMap.from(binding) + // } else { + // d.actualParams = d.actualParams ++ SortedMap.from(binding) + // } + // } + // for (binding <- outBinding.get(d.target)) { + // d.outParams = SortedMap.from(binding) + // } + // } + case d: DirectCall => { + d.actualParams = SortedMap.from(d.target.inParamDefaultBinding) + d.outParams = SortedMap.from(d.target.outParamDefaultBinding) + } + case _ => () + } + DoChildren() + } + + override def vjump(j: Jump) = { + j match { + case r: Return => { + r.outParams = SortedMap.from(r.parent.parent.outParamDefaultBinding) + DoChildren() + } + case _ => DoChildren() + } + } + + override def vlvar(v: Variable) = { + ChangeTo(LocalVar(v.name, v.getType)) + } + override def vrvar(v: Variable) = { + ChangeTo(LocalVar(v.name, v.getType)) + } + +} + +def specToProcForm( + spec: Specification, + mappingInparam: Map[Procedure, Map[LocalVar, Variable]], + mappingOutparam: Map[Procedure, Map[LocalVar, Variable]] +): Specification = { + import boogie.* + + def toNameMapping(v: Map[LocalVar, Variable]): Map[String, String] = { + v.map(v => (v._2.name, v._1.name)) ++ v.map(v => ("Gamma_" + v._2.name, "Gamma_" + v._1.name)) + } + val varToInVar: Map[String, Map[String, String]] = mappingInparam.map(p => (p._1.procName -> toNameMapping(p._2))) + val varToOutVar: Map[String, Map[String, String]] = mappingOutparam.map(p => (p._1.procName -> toNameMapping(p._2))) + + def convVarToOld(varInPre: Map[String, String], varInPost: Map[String, String], isPost: Boolean = false)( + b: BExpr + ): BExpr = { + val varToOld = convVarToOld(varInPre, varInPost, isPost) + b match { + case b: BVariable if isPost && varInPost.contains(b.name) => BVariable(varInPost(b.name), b.getType, b.scope) + case b: BVariable if !isPost && varInPre.contains(b.name) => BVariable(varInPre(b.name), b.getType, b.scope) + case b: BVar => b + // case b : _ => varToOld(b) + case b: BLiteral => b + case b: BVExtract => b.copy(body = varToOld(b.body)) + case b: BVRepeat => b.copy(body = varToOld(b.body)) + case b: BVZeroExtend => b.copy(body = varToOld(b.body)) + case b: BVSignExtend => b.copy(body = varToOld(b.body)) + case b: BFunctionCall => b.copy(args = b.args.map(varToOld)) + case b: UnaryBExpr => b.copy(arg = varToOld(b.arg)) + case b: BinaryBExpr => b.copy(arg1 = varToOld(b.arg1), arg2 = varToOld(b.arg2)) + case b: IfThenElse => IfThenElse(varToOld(b.guard), varToOld(b.thenExpr), varToOld(b.elseExpr)) + case b: QuantifierExpr => b + case b: Old => { + if (isPost) { + convVarToOld(varInPre, varInPost, false)(b.body) + } else { + throw Exception("Illegal nested or non-relation Old()") + } + } + case b: MapAccess => b.copy(index = varToOld(b.index)) + case b: MapUpdate => b.copy(index = varToOld(b.index), value = varToOld(b.value)) + case b: BByteExtract => b.copy(value = varToOld(b.value), offset = varToOld(b.offset)) + case b: BInBounds => b.copy(base = varToOld(b.base), len = varToOld(b.len), i = varToOld(b.i)) + case b: BMemoryLoad => b.copy(index = varToOld(b.index)) + case b: BMemoryStore => b.copy(index = varToOld(b.index), value = varToOld(b.value)) + case b: BDirectExpr => b + case b: GammaLoad => b.copy(index = varToOld(b.index)) + case b: GammaStore => b.copy(index = varToOld(b.index), value = varToOld(b.value)) + case b: L => b.copy(index = varToOld(b.index)) + case b: SpecVar => b + } + } + + val ns = spec.copy(subroutines = spec.subroutines.map(s => { + s.copy( + requires = s.requires.map(convVarToOld(varToInVar(s.name), varToOutVar(s.name), false)), + ensures = s.ensures.map(convVarToOld(varToInVar(s.name), varToOutVar(s.name), true)) + ) + })) + ns +} diff --git a/src/main/scala/ir/transforms/ReplaceReturn.scala b/src/main/scala/ir/transforms/ReplaceReturn.scala index f41c25e3d..392b8c27e 100644 --- a/src/main/scala/ir/transforms/ReplaceReturn.scala +++ b/src/main/scala/ir/transforms/ReplaceReturn.scala @@ -1,8 +1,8 @@ package ir.transforms import util.Logger -import ir.cilvisitor._ -import ir._ +import ir.cilvisitor.* +import ir.* class ReplaceReturns extends CILVisitor { diff --git a/src/main/scala/ir/transforms/Simp.scala b/src/main/scala/ir/transforms/Simp.scala new file mode 100644 index 000000000..4225ba8bc --- /dev/null +++ b/src/main/scala/ir/transforms/Simp.scala @@ -0,0 +1,925 @@ +package ir.transforms +import translating.serialiseIL + +import util.SimplifyLogger +import ir.eval.AlgebraicSimplifications +import ir.eval.AssumeConditionSimplifications +import ir.eval.simplifyExprFixpoint +import ir.cilvisitor.* +import ir.* +import scala.collection.mutable +import analysis._ +import scala.concurrent.{Await, ExecutionContext, Future} +import scala.concurrent.duration.* +import scala.util.{Failure, Success} +import ExecutionContext.Implicits.global + +def getLiveVars(p: Procedure): (Map[Block, Set[Variable]], Map[Block, Set[Variable]]) = { + val liveVarsDom = IntraLiveVarsDomain() + val liveVarsSolver = worklistSolver(liveVarsDom) + liveVarsSolver.solveProc(p, backwards = true) +} + +def difftestLiveVars(p: Procedure, compareResult: Map[CFGPosition, Set[Variable]]) = { + val (liveBefore, liveAfter) = getLiveVars(p) + var passed = true + + for ((b, s) <- liveBefore) { + val c = (compareResult(b) == s) + passed = passed && c + if (!c) { + SimplifyLogger.error( + s"LiveVars unequal ${b.label}: ${compareResult(b)} == $s (differing ${compareResult(b).diff(s)})" + ) + } + } + passed +} + +def basicReachingDefs(p: Procedure): Map[Command, Map[Variable, Set[Assign | DirectCall]]] = { + val (beforeLive, afterLive) = getLiveVars(p) + val dom = DefUseDomain(beforeLive) + val solver = worklistSolver(dom) + // type rtype = Map[Block, Map[Variable, Set[Assign | DirectCall]]] + val (beforeRes, afterRes) = solver.solveProc(p) + + val merged: Map[Command, Map[Variable, Set[Assign | DirectCall]]] = + beforeRes + .flatMap((block, sts) => { + val b = Seq(IRWalk.firstInBlock(block) -> sts) + val stmts = + if (block.statements.nonEmpty) then + (block.statements.toList: List[Command]).zip(block.statements.toList.tail ++ List(block.jump)) + else List() + val transferred = stmts + .foldLeft((sts, List[(Command, Map[Variable, Set[Assign | DirectCall]])]()))((st, s) => { + // map successor to transferred predecessor + val x = dom.transfer(st._1, s._1) + (x, (s._2 -> x) :: st._2) + }) + ._2 + .toMap + b ++ transferred + }) + .toMap + merged +} + +case class DefUse(defined: Map[Variable, Assign]) + +// map v -> definitions reached here +class DefUseDomain(liveBefore: Map[Block, Set[Variable]]) + extends AbstractDomain[Map[Variable, Set[Assign]]] { + + override def transfer(s: Map[Variable, Set[Assign]], b: Command) = { + b match { + case a: LocalAssign => s.updated(a.lhs, Set(a)) + case a: MemoryLoad => s.updated(a.lhs, Set(a)) + case d: DirectCall => d.outParams.map(_._2).foldLeft(s)((s, r) => s.updated(r, Set(d))) + case _ => s + } + } + override def top = ??? + def bot = Map[Variable, Set[Assign]]() + def join(l: Map[Variable, Set[Assign]], r: Map[Variable, Set[Assign]], pos: Block) = { + l.keySet + .union(r.keySet) + .filter(k => liveBefore(pos).contains(k)) + .map(k => { + k -> (l.get(k).getOrElse(Set()) ++ r.get(k).getOrElse(Set())) + }) + .toMap + } + +} + +class IntraLiveVarsDomain extends PowerSetDomain[Variable] { + // expected backwards + + def transfer(s: Set[Variable], a: Command): Set[Variable] = { + a match { + case a: LocalAssign => (s - a.lhs) ++ a.rhs.variables + case a: MemoryLoad => (s - a.lhs) ++ a.index.variables + case m: MemoryStore => s ++ m.index.variables ++ m.value.variables + case a: Assume => s ++ a.body.variables + case a: Assert => s ++ a.body.variables + case i: IndirectCall => s + i.target + case c: DirectCall => (s -- c.outParams.map(_._2)) ++ c.actualParams.flatMap(_._2.variables) + case g: GoTo => s + case r: Return => s ++ r.outParams.flatMap(_._2.variables) + case r: Unreachable => s + case n: NOP => s + } + } +} + +def removeSlices(p: Program): Unit = { + p.procedures.foreach(removeSlices) +} + +def removeSlices(p: Procedure): Unit = { + + /** if for each variable v there is some i:int such that (i) all its assignments have a ZeroExtend(i, x) and (ii) all + * its uses have Extract(size(v) - i, 0, v) Then we replace v by a variable of bitvector size (size(v) - i) + * + * We check this flow-insensitively and recover precision using DSA form. + */ + val assignments: Map[LocalVar, Iterable[Assign]] = p + .collect { + case a: SingleAssign => Seq(a.lhs -> a) + case a: DirectCall => a.assignees.map(e => e -> a) + } + .flatten + .groupBy(_._1) + .map((k,v) => (k, v.map(_._2).toSet)) + .collect { case (k: LocalVar, v) => + (k, v) + } + enum HighZeroBits: + case Bits(n: Int) // (i) and (ii) hold; the n highest bits are redundant + case False // property is false + case Bot // don't know anything + def size(e: Expr) = { + e.getType match { + case BitVecType(s) => Some(s) + case _ => None + } + } + case class LVTerm(v: LocalVar) extends analysis.solvers.Var[LVTerm] + // unify variable uses across direct assignments + val ufsolver = analysis.solvers.UnionFindSolver[LVTerm]() + val unioned = assignments.foreach { + case (lv, LocalAssign(lhs: LocalVar, rhs: LocalVar, _)) => ufsolver.unify(LVTerm(lhs), LVTerm(rhs)) + case _ => () + } + val unifiedAssignments = ufsolver + .unifications() + .map { + case (v @ LVTerm(_), rvs) => + v.v -> (rvs.map { + case LVTerm(rv) => + rv + case _ => ??? /* unreachable */ + }).toSet + case _ => ??? /* unreachable */ + } + .map((repr: LocalVar, elems: Set[LocalVar]) => + repr -> elems.flatMap(assignments(_).filter(_ match { + // filter out the direct assignments we used to build the unif class + case LocalAssign(lhs: LocalVar, rhs: LocalVar, _) if elems.contains(lhs) && elems.contains(rhs) => false + case _ => true + })) + ) + // try and find a single extension size for all rhs of assignments to all variables in the assigned equality class + val varHighZeroBits: Map[LocalVar, HighZeroBits] = assignments.map((v, assigns) => + // note: this overapproximates on x := y when x and y may both be smaller than their declared size + val allRHSExtended = assigns.foldLeft(HighZeroBits.Bot: HighZeroBits)((e, assign) => + (e, assign) match { + case (HighZeroBits.Bot, LocalAssign(_, ZeroExtend(i, lhs), _)) => HighZeroBits.Bits(i) + case (b @ HighZeroBits.Bits(ei), LocalAssign(_, ZeroExtend(i, _), _)) if i == ei => b + case (b @ HighZeroBits.Bits(ei), LocalAssign(_, ZeroExtend(i, _), _)) if i != ei => HighZeroBits.False + case (b @ HighZeroBits.Bits(ei), m: MemoryLoad) => HighZeroBits.False + case (b @ HighZeroBits.Bits(ei), m: DirectCall) => HighZeroBits.False + case (HighZeroBits.False, _) => HighZeroBits.False + case (_, other) => HighZeroBits.False + } + ) + (v, allRHSExtended) + ) + val varsWithExtend: Map[LocalVar, HighZeroBits] = assignments + .map((lhs, _) => { + // map all lhs to the result for their representative + val rep = ufsolver.find(LVTerm(lhs)) match { + case LVTerm(r) => r + case _ => ??? /* unreachable */ + } + lhs -> varHighZeroBits.get(rep) + }) + .collect { case (l, Some(x)) /* remove anything we have no information on */ => + (l, x) + } + class CheckUsesHaveExtend() extends CILVisitor { + val result: mutable.HashMap[LocalVar, HighZeroBits] = + mutable.HashMap[LocalVar, HighZeroBits]() + override def vexpr(v: Expr) = { + v match { + case Extract(i, 0, v: LocalVar) + if size(v).isDefined && result.get(v).contains(HighZeroBits.Bits(size(v).get - i)) => + SkipChildren() + case v: LocalVar => { + result.remove(v) + SkipChildren() + } + case _ => DoChildren() + } + } + def apply(assignHighZeroBits: Map[LocalVar, HighZeroBits])(p: Procedure): Map[LocalVar, HighZeroBits] = { + result.clear() + result.addAll(assignHighZeroBits) + visit_proc(this, p) + result.toMap + } + } + val toSmallen = CheckUsesHaveExtend()(varsWithExtend)(p).collect { case (v, HighZeroBits.Bits(x)) => + v -> x + }.toMap + class ReplaceAlwaysSlicedVars(varHighZeroBits: Map[LocalVar, Int]) extends CILVisitor { + override def vexpr(v: Expr) = { + v match { + case Extract(i, 0, v: LocalVar) if size(v).isDefined && varHighZeroBits.contains(v) => { + ChangeTo(LocalVar(v.name, BitVecType(size(v).get - varHighZeroBits(v)))) + } + case _ => DoChildren() + } + } + override def vstmt(s: Statement) = { + s match { + case a @ LocalAssign(lhs: LocalVar, ZeroExtend(sz, rhs), _) + if size(lhs).isDefined && varHighZeroBits.contains(lhs) => { + assert(varHighZeroBits(lhs) == sz) + a.lhs = LocalVar(lhs.name, BitVecType(size(lhs).get - varHighZeroBits(lhs))) + a.rhs = rhs + DoChildren() + } + case _ => DoChildren() + } + } + } + visit_proc(ReplaceAlwaysSlicedVars(toSmallen), p) +} + +def getRedundantAssignments(procedure: Procedure): Set[Assign] = { + + /** Get all assign statements which define a variable never used, assuming ssa form and proc parameters so that + * interprocedural check is not required. + */ + + enum VS: + case Bot + case Assigned(definition: Set[Assign]) + case Read(definition: Set[Assign], uses: Set[CFGPosition]) + + def joinVS(a: VS, b: VS) = { + (a, b) match { + case (VS.Bot, o) => o + case (o, VS.Bot) => o + case (VS.Read(d, u), VS.Read(d1, u1)) => VS.Read(d ++ d1, u ++ u1) + case (VS.Assigned(d), VS.Read(d1, u1)) => VS.Read(d ++ d1, u1) + case (VS.Read(d1, u1), VS.Assigned(d)) => VS.Read(d ++ d1, u1) + case (VS.Assigned(d1), VS.Assigned(d2)) => VS.Assigned(d1 ++ d2) + } + } + + val assignedNotRead = mutable.Map[Variable, VS]().withDefaultValue(VS.Bot) + + for (c <- procedure) { + c match { + case a: LocalAssign => { + assignedNotRead(a.lhs) = joinVS(assignedNotRead(a.lhs), VS.Assigned(Set(a))) + a.rhs.variables.foreach(v => { + assignedNotRead(v) = joinVS(assignedNotRead(v), VS.Read(Set(), Set(a))) + }) + } + case a: MemoryLoad => { + assignedNotRead(a.lhs) = joinVS(assignedNotRead(a.lhs), VS.Assigned(Set(a))) + a.index.variables.foreach(v => { + assignedNotRead(v) = joinVS(assignedNotRead(v), VS.Read(Set(), Set(a))) + }) + } + case m: MemoryStore => { + m.index.variables.foreach(v => { + assignedNotRead(v) = joinVS(assignedNotRead(v), VS.Read(Set(), Set(m))) + }) + m.value.variables.foreach(v => { + assignedNotRead(v) = joinVS(assignedNotRead(v), VS.Read(Set(), Set(m))) + }) + } + case m: IndirectCall => { + assignedNotRead(m.target) = joinVS(assignedNotRead(m.target), VS.Read(Set(), Set(m))) + } + case m: Assert => { + m.body.variables.foreach(v => { + assignedNotRead(v) = joinVS(assignedNotRead(v), VS.Read(Set(), Set(m))) + }) + } + case m: Assume => { + for (v <- m.body.variables) { + assignedNotRead(v) = joinVS(assignedNotRead(v), VS.Read(Set(), Set(m))) + } + } + case c: DirectCall => { + c.actualParams + .flatMap(_._2.variables) + .foreach(v => { + assignedNotRead(v) = joinVS(assignedNotRead(v), VS.Read(Set(), Set(c))) + }) + } + case p: Return => { + p.outParams + .flatMap(_._2.variables) + .foreach(v => { + assignedNotRead(v) = joinVS(assignedNotRead(v), VS.Read(Set(), Set(p))) + }) + } + case p: GoTo => () + case p: NOP => () + case p: Unreachable => () + case p: Procedure => () + case b: Block => () + } + } + + var toRemove = assignedNotRead + var removeOld = toRemove + + val r = toRemove + .collect { case (v, VS.Assigned(d)) => + d + } + .toSet + .flatten + r +} + +class CleanupAssignments() extends CILVisitor { + var redundantAssignments = Set[Assign]() + + def isRedundant(a: LocalAssign) = { + a.lhs == a.rhs || redundantAssignments.contains(a) + } + + override def vproc(p: Procedure) = { + redundantAssignments = getRedundantAssignments(p) + DoChildren() + } + + override def vstmt(s: Statement) = s match { + case a: LocalAssign if isRedundant(a) => ChangeTo(List()) + case _ => SkipChildren() + } + +} + +def copypropTransform(p: Procedure) = { + val t = util.PerformanceTimer(s"simplify ${p.name} (${p.blocks.size} blocks)") + // val dom = ConstCopyProp() + // val solver = worklistSolver(dom) + + // SimplifyLogger.info(s"${p.name} ExprComplexity ${ExprComplexity()(p)}") + // val result = solver.solveProc(p, true).withDefaultValue(dom.bot) + val result = CopyProp.DSACopyProp(p) + val solve = t.checkPoint("Solve CopyProp") + + if (result.nonEmpty) { + val vis = Simplify(CopyProp.toResult(result, true)) + visit_proc(vis, p) + + val condResult = CopyProp.PropFlagCalculations(p, result.toMap) + val condVis = Simplify(CopyProp.toResult(condResult, false)) + visit_proc(condVis, p) + + } + + val xf = t.checkPoint("transform") + // SimplifyLogger.info(s" ${p.name} after transform expr complexity ${ExprComplexity()(p)}") + + visit_proc(CleanupAssignments(), p) + t.checkPoint("redundant assignments") + // SimplifyLogger.info(s" ${p.name} after dead var cleanup expr complexity ${ExprComplexity()(p)}") + + AlgebraicSimplifications(p) + AssumeConditionSimplifications(p) + + AlgebraicSimplifications(p) + // SimplifyLogger.info(s" ${p.name} after simp expr complexity ${ExprComplexity()(p)}") + val sipm = t.checkPoint("algebraic simp") + + // SimplifyLogger.info("[!] Simplify :: RemoveSlices") + removeSlices(p) + ir.eval.cleanupSimplify(p) + AlgebraicSimplifications(p) + +} + +def removeEmptyBlocks(p: Program) = { + for (proc <- p.procedures) { + val blocks = proc.blocks.toList + for (b <- blocks) { + b match { + case b: Block if b.statements.size == 0 && b.prevBlocks.size == 1 && b.jump.isInstanceOf[GoTo] => { + val prev = b.prevBlocks + val next = b.nextBlocks + for (p <- prev) { + p.jump match { + case g: GoTo => { + for (n <- next) { + g.addTarget(n) + } + g.removeTarget(b) + } + case _ => throw Exception("Must have goto") + } + } + b.replaceJump(Unreachable()) + b.parent.removeBlocks(b) + } + case _ => () + } + } + } +} + +def coalesceBlocks(p: Program) = { + var didAny = false + for (proc <- p.procedures) { + val blocks = proc.blocks.toList + for (b <- blocks.sortBy(_.rpoOrder)) { + if ( + b.prevBlocks.size == 1 && b.prevBlocks.head.statements.nonEmpty && b.statements.nonEmpty + && b.prevBlocks.head.nextBlocks.size == 1 + && b.prevBlocks.head.statements.lastOption.map(s => !(s.isInstanceOf[Call])).getOrElse(true) + && !(b.parent.entryBlock.contains(b) || b.parent.returnBlock.contains(b)) + ) { + didAny = true + // append topredecessor + // we know prevBlock is only jumping to b and has no call at the end + val prevBlock = b.prevBlocks.head + val stmts = b.statements.map(b.statements.remove).toList + prevBlock.statements.appendAll(stmts) + // leave empty block b and cleanup with removeEmptyBlocks + } else if ( + b.nextBlocks.size == 1 && b.nextBlocks.head.statements.nonEmpty && b.statements.nonEmpty + && b.nextBlocks.head.prevBlocks.size == 1 + && b.statements.lastOption.map(s => !(s.isInstanceOf[Call])).getOrElse(true) + && !(b.parent.entryBlock.contains(b) || b.parent.returnBlock.contains(b)) + ) { + didAny = true + // append to successor + // we know b is only jumping to nextBlock and does not end in a call + val nextBlock = b.nextBlocks.head + val stmts = b.statements.map(b.statements.remove).toList + nextBlock.statements.prependAll(stmts) + // leave empty block b and cleanup with removeEmptyBlocks + } + } + } + didAny +} + +def doCopyPropTransform(p: Program) = { + + applyRPO(p) + + SimplifyLogger.info("[!] Simplify :: Expr/Copy-prop Transform") + val work = p.procedures + .filter(_.blocks.size > 0) + .map(p => + p -> //Future + { + SimplifyLogger + .debug(s"CopyProp Transform ${p.name} (${p.blocks.size} blocks, expr complexity ${ExprComplexity()(p)})") + copypropTransform(p) + } + ) + + work.foreach((p, job) => { + try { + //Await.result(job, 10000.millis) + job + } catch { + case e => { + SimplifyLogger.error("Simplify :: CopyProp " + p.name + ": " + e.toString) + } + } + }) + + SimplifyLogger.info("[!] Simplify :: Dead variable elimination") + + // cleanup + visit_prog(CleanupAssignments(), p) + SimplifyLogger.info("[!] Simplify :: Merge empty blocks") + + removeEmptyBlocks(p) + coalesceBlocks(p) + removeEmptyBlocks(p) + coalesceBlocks(p) + removeEmptyBlocks(p) + coalesceBlocks(p) + removeEmptyBlocks(p) + coalesceBlocks(p) + removeEmptyBlocks(p) + +} + +def reversePostOrder(p: Procedure): Unit = { + /* Procedures may contain disconnected sets of blocks so we arbitrarily order these with respect to eachother. */ + for (b <- p.blocks) { + b.rpoOrder = -1 + } + var left = p.entryBlock.map(reversePostOrder(_)).getOrElse(0) + 1 + for (b <- p.blocks.filter(_.rpoOrder == -1)) { + left = reversePostOrder(b, true, left) + 1 + } +} + +def reversePostOrder(startBlock: Block, fixup: Boolean = false, begin: Int = 0): Int = { + var count = begin + val seen = mutable.HashSet[Block]() + val vcs = mutable.HashMap[Block, Int]() + + def walk(b: Block): Unit = { + seen += b + for (s <- b.nextBlocks) { + if (!seen.contains(s)) { + walk(s) + } + } + if (!fixup || b.rpoOrder < count) { + b.rpoOrder = count + } + count += 1 + } + + walk(startBlock) + count +} + +def applyRPO(p: Program) = { + for (proc <- p.procedures) { + reversePostOrder(proc) + } +} + +// case class CopyProp(from: Expr, expr: Expr, deps: Set[Variable]) + +enum CopyProp { + case Bot + case Prop(expr: Expr, deps: Set[Variable]) + case Clobbered +} + +case class CCP( + val state: Map[Variable, CopyProp] = Map() +) + +object CCP { + + def toSubstitutions(c: CCP): Map[Variable, Expr] = { + c.state.collect { case (v, CopyProp.Prop(e, _)) => + v -> e + } + } + + def clobberFull(c: CCP, l: Variable) = { + val p = clobber(c, l) + p.copy(state = p.state + (l -> CopyProp.Clobbered)) + } + + def clobber(c: CCP, l: Variable) = { + CCP( + c.state + .map((k, v) => + k -> (v match { + case CopyProp.Prop(_, deps) if deps.contains(l) => CopyProp.Clobbered + case o => o + }) + ) + .withDefaultValue(CopyProp.Bot) + ) + } +} + +object CopyProp { + + def forwardFlagCalc(p: Procedure) = { + val (beforeLive, afterLive) = getLiveVars(p) + val dom = DefUseDomain(beforeLive) + val solver = worklistSolver(dom) + val (beforeRes, afterRes) = solver.solveProc(p) + + } + + def isFlagVar(l: Variable) = { + val flagNames = Set("ZF", "VF", "CF", "NF") + l match { + case l: LocalVar => flagNames.contains(l.varName) + case l => flagNames.contains(l.name) + } + } + + case class PropState( + var e: Expr, + val deps: mutable.Set[Variable], + var clobbered: Boolean, + var useCount: Int, + var isFlagDep: Boolean + ) + + def PropFlagCalculations(p: Procedure, initialState: Map[Variable, PropState]) = { + val state = mutable.HashMap[Variable, PropState]() + + val flagDeps = initialState.collect { + case (v, e) if e.isFlagDep => v -> e + } + + val worklist = mutable.PriorityQueue[Block]()(Ordering.by(_.rpoOrder)) + worklist.addAll(p.blocks) + var poisoned = false + + def transfer(s: Statement) = { + s match { + case a: MemoryLoad => clobberFull(state, a.lhs) + case a: LocalAssign if !state.contains(a.lhs) && flagDeps.contains(a.lhs) => { + val r = canPropTo(state, a.rhs, true).get + state(a.lhs) = PropState(r, mutable.Set.from(r.variables), false, 0, true) + } + case a: LocalAssign if state.contains(a.lhs) && state(a.lhs).clobbered => { + () + } + case a: LocalAssign + if state.contains(a.lhs) && (a.rhs != state(a.lhs).e) && (canPropTo(state, a.rhs, true) != canPropTo( + state, + state(a.lhs).e, + true + )) => { + clobberFull(state, a.lhs) + } + case a: LocalAssign if state.contains(a.lhs) => { + state(a.lhs) = state(a.lhs).copy(e = canPropTo(state, a.rhs, true).get) + } + case i: IndirectCall => { + poisoned = true + } + case d: DirectCall => { + for (l <- d.outParams.map(_._2)) { + clobberFull(state, l) + } + } + case _ => () + } + } + + while (worklist.nonEmpty && !poisoned) { + val b: Block = worklist.dequeue + + for (l <- b.statements) { + transfer(l) + } + } + if (poisoned) then mutable.HashMap() else state + } + + def clobberFull(c: mutable.HashMap[Variable, PropState], l: Variable): Unit = { + if (c.contains(l)) { + c(l).clobbered = true + } else { + c(l) = PropState(FalseLiteral, mutable.Set(), true, 0, false) + } + } + + + def isTrivial(e: Expr): Boolean = e match { + case l: Literal => true + case l: Variable => true + case BinaryExpr(op, e: Variable, b: Literal) => true + case ZeroExtend(_, e) if isTrivial(e) => true + case SignExtend(_, e) if isTrivial(e) => true + case Extract(_, _, e) if isTrivial(e) => true + case UnaryExpr(_, e) if isTrivial(e) => true + case _ => false + } + + def canPropTo(s: mutable.HashMap[Variable, PropState], e: Expr, isFlag: Boolean = false): Option[Expr] = { + + def proped(e: Expr) = { + val ne = + if e.variables.size > 1 && !isFlag then Some(e) + else + Substitute( + v => { + s.get(v) match { + case Some(vs) if !vs.clobbered => Some(vs.e) + case _ => None + } + }, + true + )(e) + + // partial eval after prop + simplifyExprFixpoint(ne.getOrElse(e))._1 + } + + def propfp(e: Expr) = { + var o = e + var p = proped(e) + + while (o != p) { + o = p + p = proped(o) + } + p + } + + proped(e) match { + case l: Literal => Some(l) + case l: Variable => Some(l) + case e @ BinaryExpr(o, v: Variable, c: Literal) => Some(e) + case e: Expr if isFlag || e.variables.size <= 1 => Some(e) + case e => None + } + } + + def DSACopyProp(p: Procedure) = { + + val state = mutable.HashMap[Variable, PropState]() + var poisoned = false // we have an indirect call + + def transfer(c: mutable.HashMap[Variable, PropState], s: Statement): Unit = { + // val callClobbers = ((0 to 7) ++ (19 to 30)).map("R" + _).map(c => Register(c, 64)) + s match { + case l: MemoryLoad => + clobberFull(c, l.lhs) + case LocalAssign(l, r, lb) => { + val isFlag = isFlagVar(l) || r.variables.exists(isFlagVar) + val isFlagDep = isFlag || c.get(l).map(v => v.isFlagDep).getOrElse(false) + + c.get(l).foreach(v => v.isFlagDep = v.isFlagDep || isFlagDep) + for (l <- r.variables) { + c.get(l).foreach(v => v.isFlagDep = v.isFlagDep || isFlagDep) + } + + var prop = canPropTo(c, r, isFlagDep) + val existing = c.get(l) + + (prop, existing) match { + case (Some(evaled), None) => { + c(l) = PropState(evaled, mutable.Set.from(evaled.variables), false, 0, isFlagDep) + } + case (_, Some(ps)) if ps.clobbered => { + () + } + case (Some(evaled), Some(ps)) + if ps.e == r || ps.e == evaled || (canPropTo(c, ps.e, isFlagDep).contains(evaled)) => { + c(l) = c(l).copy(e = evaled) + } + case (Some(evaled), Some(ps)) => { + clobberFull(c, l) + } + case _ => { + // ps.e != evaled and have prop + clobberFull(c, l) + } + } + } + case x: DirectCall => { + val lhs = x.outParams.map(_._2) + for (l <- lhs) { + clobberFull(c, l) + } + } + case x: IndirectCall => { + // not really correct + poisoned = true + for ((i, v) <- c) { + v.clobbered = true + } + } + case _ => () + } + } + + // sort by precedence + val worklist = mutable.PriorityQueue[Block]()(Ordering.by(_.rpoOrder)) + worklist.addAll(p.blocks) + + while (worklist.nonEmpty && !poisoned) { + val b: Block = worklist.dequeue + + for (l <- b.statements) { + transfer(state, l) + } + } + + val trivialOnly = false + + if poisoned then mutable.HashMap() else state + } + + def toResult(s: mutable.Map[Variable, PropState], trivialOnly: Boolean = true)(v: Variable): Option[Expr] = { + s.get(v) match { + case Some(c) if !c.clobbered && (!trivialOnly || isTrivial(c.e)) => Some(c.e) + case _ => None + } + } + +} + + +class ExprComplexity extends CILVisitor { + // count the nodes in the expression AST + var count = 0 + override def vexpr(e: Expr) = { + count += 1 + DoChildren() + } + + def apply(e: Procedure) = { + count = 0 + visit_proc(this, e) + count + } + + def apply(e: Expr) = { + count = 0 + visit_expr(this, e) + count + } +} + +/** Use this as a partially applied function. Substitute(Map.from(substs).get, recurse = false) + * + * @res: + * defines the substitutions to make + * @recurse: + * continue substituting with `res` into each substituted expression + * @complexityThreshold: + * Stop substituting after the AST node count has increased by this much + */ +class Substitute( + val res: Variable => Option[Expr], + val recurse: Boolean = true, + val complexityThreshold: Int = 0 +) extends CILVisitor { + var madeAnyChange = false + var complexity = 0 + + override def vexpr(e: Expr) = { + e match { + case v: Variable if res(v).isDefined => { + val changeTo = res(v).get + if (complexityThreshold > 0) { + complexity += ExprComplexity()(changeTo) - ExprComplexity()(e) + } + if (complexityThreshold > 0 && complexity > complexityThreshold) { + SkipChildren() + } else if (recurse) { + madeAnyChange = true + ChangeDoChildrenPost(changeTo, x => x) + } else { + madeAnyChange = true + ChangeTo(changeTo) + } + } + case e => DoChildren() + } + } + + def apply(e: Expr): Option[Expr] = { + madeAnyChange = false + val ne = visit_expr(this, e) + val changed = madeAnyChange + madeAnyChange = false + if (changed) { + Some(ne) + } else { + None + } + } + +} + +class Simplify( + val res: Variable => Option[Expr], + val initialBlock: Block = null, +) extends CILVisitor { + + var madeAnyChange = false + var block: Block = initialBlock + var skipped = Set[String]() + + override def vexpr(e: Expr) = { + val threshold = 500 + val variables = e.variables.toSet + val subst = Substitute(res, true, threshold) + var old: Expr = e + var result = subst(e).getOrElse(e) + while (old != result) { + old = result + result = subst(e).getOrElse(e) + } + + if (subst.complexity > threshold) { + val bl = s"${block.parent.name}::${block.label}" + if (!skipped.contains(bl)) { + skipped = skipped + bl + SimplifyLogger.warn(s"Some skipped substitution at $bl due to resulting expr size > ${threshold} threshold") + } + } + ChangeDoChildrenPost(result, x => x) + } + + override def vblock(b: Block) = { + block = b + DoChildren() + } +} diff --git a/src/main/scala/ir/transforms/StripUnreachableFunctions.scala b/src/main/scala/ir/transforms/StripUnreachableFunctions.scala index 96784cc28..ab6279406 100644 --- a/src/main/scala/ir/transforms/StripUnreachableFunctions.scala +++ b/src/main/scala/ir/transforms/StripUnreachableFunctions.scala @@ -1,21 +1,23 @@ package ir.transforms -import ir._ +import ir.* import collection.mutable +import util.Logger +import ir.cilvisitor.* // This shouldn't be run before indirect calls are resolved def stripUnreachableFunctions(p: Program, depth: Int = Int.MaxValue): Unit = { - val procedureCalleeNames = p.procedures.map(f => f.name -> f.calls.map(_.name)).toMap + val procedureCalleeNames = p.procedures.map(f => f -> f.calls).toMap - val toVisit: mutable.LinkedHashSet[(Int, String)] = mutable.LinkedHashSet((0, p.mainProcedure.name)) + val toVisit: mutable.LinkedHashSet[(Int, Procedure)] = mutable.LinkedHashSet((0, p.mainProcedure)) var reachableFound = true - val reachableNames = mutable.HashMap[String, Int]() + val reachableNames = mutable.HashMap[Procedure, Int]() while (toVisit.nonEmpty) { val next = toVisit.head toVisit.remove(next) if (next._1 <= depth) { - def addName(depth: Int, name: String): Unit = { + def addName(depth: Int, name: Procedure): Unit = { val oldDepth = reachableNames.getOrElse(name, Integer.MAX_VALUE) reachableNames.put(next._2, if depth < oldDepth then depth else oldDepth) } @@ -27,12 +29,23 @@ def stripUnreachableFunctions(p: Program, depth: Int = Int.MaxValue): Unit = { callees.foreach(c => addName(next._1 + 1, c)) } } - p.procedures = p.procedures.filter(f => reachableNames.keySet.contains(f.name)) + assert(invariant.cfgCorrect(p)) + val removed = p.procedures.filterNot(f => reachableNames.keySet.contains(f)).toSet + // p.procedures = p.procedures.filter(f => reachableNames.keySet.contains(f.name)) + for (proc <- removed) { + p.removeProcedure(proc) + } - for (elem <- p.procedures.filter(c => c.calls.exists(s => !p.procedures.contains(s)))) { + for (elem <- p.procedures.filter(c => c.calls.exists(s => removed.contains(s)))) { // last layer is analysed only as specifications so we remove the body for anything that calls // a function we have removed elem.clearBlocks() + assert(elem.entryBlock.isEmpty) + assert(elem.returnBlock.isEmpty) + } + assert(invariant.blocksUniqueToEachProcedure(p)) + assert(invariant.cfgCorrect(p)) + } diff --git a/src/main/scala/specification/Specification.scala b/src/main/scala/specification/Specification.scala index 9c80f1039..0674682a2 100644 --- a/src/main/scala/specification/Specification.scala +++ b/src/main/scala/specification/Specification.scala @@ -4,56 +4,6 @@ import boogie.* import ir.* import util.Logger -trait SpecVar extends BExpr { - val address: BigInt - override def getType: BType = { - throw new Exception("getType called on SpecVar") - } -} - -trait SpecGlobalOrAccess extends SpecVar with Ordered[SpecGlobalOrAccess] { - val toAddrVar: BExpr - val toOldVar: BVar - val toOldGamma: BVar - val size: Int - - def compare(that: SpecGlobalOrAccess): Int = address.compare(that.address) -} - -trait SymbolTableEntry{ - val name: String - val size: Int - val address: BigInt -} - -case class FuncEntry(override val name: String, override val size: Int, override val address: BigInt) extends SymbolTableEntry - -case class SpecGlobal(override val name: String, override val size: Int, arraySize: Option[Int], override val address: BigInt) - extends SymbolTableEntry, SpecGlobalOrAccess { - override def specGlobals: Set[SpecGlobalOrAccess] = Set(this) - override val toAddrVar: BVar = BVariable("$" + s"${name}_addr", BitVecBType(64), Scope.Const) - override val toOldVar: BVar = BVariable(s"${name}_old", BitVecBType(size), Scope.Local) - override val toOldGamma: BVar = BVariable(s"Gamma_${name}_old", BoolBType, Scope.Local) - val toAxiom: BAxiom = BAxiom(BinaryBExpr(BoolEQ, toAddrVar, BitVecBLiteral(address, 64)), List.empty) - override def acceptVisit(visitor: BVisitor): BExpr = visitor.visitSpecGlobal(this) -} - -case class SpecGamma(global: SpecGlobal) extends SpecVar { - override val address = global.address - val size = global.size - override def acceptVisit(visitor: BVisitor): BExpr = visitor.visitSpecGamma(this) -} - -case class ArrayAccess(global: SpecGlobal, index: Int) extends SpecGlobalOrAccess { - val offset = index * (global.size / 8) - override val address = global.address + offset - override val size: Int = global.size - override val toOldVar: BVar = BVariable(s"${global.name}$$${index}_old", BitVecBType(global.size), Scope.Local) - override val toAddrVar: BExpr = BinaryBExpr(BVADD, global.toAddrVar, BitVecBLiteral(offset, 64)) - override val toOldGamma: BVar = BVariable(s"Gamma_${global.name}$$${index}_old", BoolBType, Scope.Local) - override def specGlobals: Set[SpecGlobalOrAccess] = Set(this) - override def acceptVisit(visitor: BVisitor): BExpr = visitor.visitArrayAccess(this) -} case class Specification( funcs: Set[FuncEntry], @@ -71,6 +21,13 @@ case class Specification( val controlled: Set[SpecGlobal] = controls.values.flatten.toSet } + +trait SymbolTableEntry{ + val name: String + val size: Int + val address: BigInt +} + case class SubroutineSpec( name: String, requires: List[BExpr], diff --git a/src/main/scala/translating/BAPToIR.scala b/src/main/scala/translating/BAPToIR.scala index 9793022cb..1d2750f6b 100644 --- a/src/main/scala/translating/BAPToIR.scala +++ b/src/main/scala/translating/BAPToIR.scala @@ -4,8 +4,10 @@ import bap.* import boogie.UnaryBExpr import ir.{UnaryExpr, BinaryExpr, *} import specification.* +import ir.cilvisitor.* import scala.collection.mutable +import scala.collection.immutable import scala.collection.mutable.Map import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.TreeMap @@ -31,12 +33,11 @@ class BAPToIR(var program: BAPProgram, mainAddress: BigInt) { } labelToBlock.addOne(b.label, block) } - for (p <- s.in) { - procedure.in.append(translateParameter(p)) - } - for (p <- s.out) { - procedure.out.append(translateParameter(p)) - } + procedure.formalInParam = mutable.SortedSet.from(s.in.map(translateParam)) + procedure.formalOutParam = mutable.SortedSet.from(s.out.filterNot(_.name.endsWith("_result")).map(translateParam)) + procedure.inParamDefaultBinding = immutable.SortedMap.from(s.in.map(s => translateParam(s)-> paramRegisterRVal(s))) + procedure.outParamDefaultBinding = immutable.SortedMap.from(s.out.filterNot(_.name.endsWith("_result")).map(s => translateParam(s) -> paramRegisterLVal(s))) + if (s.address.get == mainAddress) { mainProcedure = Some(procedure) } @@ -79,7 +80,25 @@ class BAPToIR(var program: BAPProgram, mainAddress: BigInt) { memorySections.addOne(m.address, MemorySection(m.name, m.address, m.size, bytes, readOnly, None)) } - Program(procedures, mainProcedure.get, memorySections) + class FixCallParams(subroutines: immutable.Map[String, BAPSubroutine]) extends CILVisitor { + override def vstmt(st: Statement) = st match { + case d: DirectCall => { + if (subroutines.contains(d.target.name)) { + val s = subroutines(d.target.name) + ChangeTo(List(DirectCall(d.target, d.label, + d.target.outParamDefaultBinding, + d.target.inParamDefaultBinding))) + } else { + SkipChildren() + } + } + case _ => SkipChildren() + } + } + + var prog = Program(procedures, mainProcedure.get, memorySections) + visit_prog(FixCallParams(program.subroutines.map(s => s.name -> s).toMap), prog) + prog } private def translateStatement(s: BAPStatement): Seq[Statement] = s match { @@ -186,7 +205,7 @@ class BAPToIR(var program: BAPProgram, mainAddress: BigInt) { } case b: BAPVar => (translateVar(b), None) case BAPMemAccess(memory, index, endian, size) => - val temp = LocalVar("$load$" + loadCounter, BitVecType(size)) + val temp = LocalVar("load" + loadCounter, BitVecType(size)) loadCounter += 1 val load = MemoryLoad(temp, translateMemory(memory), translateExprOnly(index), endian, size, None) (temp, Some(load)) @@ -200,6 +219,58 @@ class BAPToIR(var program: BAPProgram, mainAddress: BigInt) { expr } + private def paramRegisterLVal(param: BAPParameter): Variable = translateVar(param.value) + private def toIROutParam(param: BAPParameter) = { + paramRegisterLVal(param) match { + case r: Register => { + if (r.size == param.size) then { + translateParam(param) + } else { + LocalVar(param.name, BitVecType(r.size)) + } + } + case _ => throw Exception(s"subroutine parameter $this refers to non-register variable ${param.value}") + } + + } + + private def paramRegisterRVal(p: BAPParameter): Expr = { + paramRegisterLVal(p) match { + case r: Register => { + if (r.size == p.size) then { + r + } else if (r.size > p.size){ + Extract(p.size, 0, r) + } else { + ZeroExtend(p.size - r.size, r) + } + } + case _ => throw Exception(s"subroutine parameter $this refers to non-register variable ${p.value}") + } + } + def paramVariableRVal(p: BAPParameter): Expr = { + paramRegisterLVal(p) match { + case r: Register => { + if (r.size == p.size) then { + translateParam(p) + } else { + ZeroExtend(r.size - p.size, translateParam(p)) + } + } + case _ => throw Exception(s"subroutine parameter $this refers to non-register variable ${p.value}") + } + } + + def toAssignOut(p: BAPParameter) : LocalAssign = { + LocalAssign(translateParam(p), paramRegisterRVal(p)) + } + + def toAssignIn(p: BAPParameter) : LocalAssign = { + LocalAssign(paramRegisterLVal(p), paramVariableRVal(p)) + } + + def translateParam(p: BAPParameter): LocalVar = LocalVar(p.name, BitVecType(p.size)) + private def translateVar(variable: BAPVar): Variable = variable match { case BAPRegister(name, size) => Register(name, size) case BAPLocalVar(name, size) => LocalVar(name, BitVecType(size)) @@ -209,13 +280,8 @@ class BAPToIR(var program: BAPProgram, mainAddress: BigInt) { SharedMemory(memory.name, memory.addressSize, memory.valueSize) } - private def translateParameter(parameter: BAPParameter): Parameter = { - val register = translateExprOnly(parameter.value) - register match { - case r: Register => Parameter(parameter.name, parameter.size, r) - case _ => throw Exception(s"subroutine parameter $this refers to non-register variable ${parameter.value}") - } - } + + private def translateLiteral(literal: BAPLiteral) = { BitVecLiteral(literal.value, literal.size) @@ -279,7 +345,7 @@ class BAPToIR(var program: BAPProgram, mainAddress: BigInt) { } else { jumps.head match { case b: BAPDirectCall => - val call = Some(DirectCall(nameToProcedure(b.target),Some(b.line))) + val call = Some(DirectCall(nameToProcedure(b.target), Some(b.line))) val ft = b.returnTarget.map(t => labelToBlock(t)).map(x => GoTo(Set(x))).getOrElse(Unreachable()) (call, ft, ArrayBuffer()) case b: BAPIndirectCall => diff --git a/src/main/scala/translating/GTIRBLoader.scala b/src/main/scala/translating/GTIRBLoader.scala index 836ed5de0..445fe655b 100644 --- a/src/main/scala/translating/GTIRBLoader.scala +++ b/src/main/scala/translating/GTIRBLoader.scala @@ -236,7 +236,7 @@ class GTIRBLoader(parserMap: immutable.Map[String, Array[Array[StmtContext]]]) { private def visitExprOnly(ctx: ExprContext): Option[Expr] = { val (expr, load) = visitExpr(ctx) if (load.isDefined) { - throw Exception("") + throw Exception(s"found load $expr $load") } else { expr } @@ -287,7 +287,7 @@ class GTIRBLoader(parserMap: immutable.Map[String, Array[Array[StmtContext]]]) { throw Exception(s"inconsistent size parameters in Mem.read.0: ${ctx.getText}") } - val temp = LocalVar("$load" + loadCounter, BitVecType(size.toInt)) + val temp = LocalVar("load" + loadCounter, BitVecType(size.toInt)) loadCounter += 1 if (index.isDefined) { @@ -299,39 +299,39 @@ class GTIRBLoader(parserMap: immutable.Map[String, Array[Array[StmtContext]]]) { case "cvt_bool_bv.0" => checkArgs(function, 0, 1, typeArgs.size, args.size, ctx.getText) - val expr = visitExprOnly(args.head) - val result = expr.map { - case b: BinaryExpr if b.op == BVEQ => BinaryExpr(BVCOMP, b.arg1, b.arg2) - case FalseLiteral => BitVecLiteral(0, 1) - case TrueLiteral => BitVecLiteral(1, 1) - case _ => throw Exception(s"unhandled conversion from bool to bitvector: ${ctx.getText}") - } - (result, None) + val (expr, load) = visitExpr(args.head) + val result : Option[Expr] = expr.map { + case b: BinaryExpr if b.op == BVEQ => BinaryExpr(BVCOMP, b.arg1, b.arg2) + case FalseLiteral => BitVecLiteral(0, 1) + case TrueLiteral => BitVecLiteral(1, 1) + case o => UnaryExpr(BoolToBV1, o) + } + (result, load) case "not_bool.0" => (resolveUnaryOp(BoolNOT, function, 0, typeArgs, args, ctx.getText), None) - case "eq_enum.0" => (resolveBinaryOp(BoolEQ, function, 0, typeArgs, args, ctx.getText), None) - case "or_bool.0" => (resolveBinaryOp(BoolOR, function, 0, typeArgs, args, ctx.getText), None) - case "and_bool.0" => (resolveBinaryOp(BoolAND, function, 0, typeArgs, args, ctx.getText), None) + case "eq_enum.0" => (resolveBinaryOp(BoolEQ, function, 0, typeArgs, args, ctx.getText)) + case "or_bool.0" => (resolveBinaryOp(BoolOR, function, 0, typeArgs, args, ctx.getText)) + case "and_bool.0" => (resolveBinaryOp(BoolAND, function, 0, typeArgs, args, ctx.getText)) + + case "or_bits.0" => (resolveBinaryOp(BVOR, function, 1, typeArgs, args, ctx.getText)) + case "and_bits.0" => (resolveBinaryOp(BVAND, function, 1, typeArgs, args, ctx.getText)) + case "eor_bits.0" => (resolveBinaryOp(BVXOR, function, 1, typeArgs, args, ctx.getText)) + case "eq_bits.0" => (resolveBinaryOp(BVEQ, function, 1, typeArgs, args, ctx.getText)) + case "add_bits.0" => (resolveBinaryOp(BVADD, function, 1, typeArgs, args, ctx.getText)) + case "sub_bits.0" => (resolveBinaryOp(BVSUB, function, 1, typeArgs, args, ctx.getText)) + case "mul_bits.0" => (resolveBinaryOp(BVMUL, function, 1, typeArgs, args, ctx.getText)) + case "sdiv_bits.0" => (resolveBinaryOp(BVSDIV, function, 1, typeArgs, args, ctx.getText)) + case "slt_bits.0" => (resolveBinaryOp(BVSLT, function, 1, typeArgs, args, ctx.getText)) + case "sle_bits.0" => (resolveBinaryOp(BVSLE, function, 1, typeArgs, args, ctx.getText)) case "not_bits.0" => (resolveUnaryOp(BVNOT, function, 1, typeArgs, args, ctx.getText), None) - case "or_bits.0" => (resolveBinaryOp(BVOR, function, 1, typeArgs, args, ctx.getText), None) - case "and_bits.0" => (resolveBinaryOp(BVAND, function, 1, typeArgs, args, ctx.getText), None) - case "eor_bits.0" => (resolveBinaryOp(BVXOR, function, 1, typeArgs, args, ctx.getText), None) - case "eq_bits.0" => (resolveBinaryOp(BVEQ, function, 1, typeArgs, args, ctx.getText), None) - case "add_bits.0" => (resolveBinaryOp(BVADD, function, 1, typeArgs, args, ctx.getText), None) - case "sub_bits.0" => (resolveBinaryOp(BVSUB, function, 1, typeArgs, args, ctx.getText), None) - case "mul_bits.0" => (resolveBinaryOp(BVMUL, function, 1, typeArgs, args, ctx.getText), None) - case "sdiv_bits.0" => (resolveBinaryOp(BVSDIV, function, 1, typeArgs, args, ctx.getText), None) - - case "slt_bits.0" => (resolveBinaryOp(BVSLT, function, 1, typeArgs, args, ctx.getText), None) - case "sle_bits.0" => (resolveBinaryOp(BVSLE, function, 1, typeArgs, args, ctx.getText), None) case "lsl_bits.0" => (resolveBitShiftOp(BVSHL, function, typeArgs, args, ctx.getText), None) case "lsr_bits.0" => (resolveBitShiftOp(BVLSHR, function, typeArgs, args, ctx.getText), None) case "asr_bits.0" => (resolveBitShiftOp(BVASHR, function, typeArgs, args, ctx.getText), None) case "append_bits.0" => - (resolveBinaryOp(BVCONCAT, function, 2, typeArgs, args, ctx.getText), None) + (resolveBinaryOp(BVCONCAT, function, 2, typeArgs, args, ctx.getText)) case "replicate_bits.0" => checkArgs(function, 2, 2, typeArgs.size, args.size, ctx.getText) @@ -482,16 +482,18 @@ class GTIRBLoader(parserMap: immutable.Map[String, Array[Array[StmtContext]]]) { typeArgs: mutable.Buffer[ExprContext], args: mutable.Buffer[ExprContext], token: String - ): Option[BinaryExpr] = { + ): (Option[BinaryExpr], Option[MemoryLoad]) = { checkArgs(function, typeArgsExpected, 2, typeArgs.size, args.size, token) // we don't currently check the size for BV ops which is the type arg // memory loads shouldn't appear inside binary operations? - val arg0 = visitExprOnly(args(0)) - val arg1 = visitExprOnly(args(1)) + val (arg0, l0) = visitExpr(args(0)) + val (arg1, l1) = visitExpr(args(1)) + val l = l0.orElse(l1) + assert(!(l0.isDefined && l1.isDefined), "Multiple loads in expression") if (arg0.isDefined && arg1.isDefined) { - Some(BinaryExpr(operator, arg0.get, arg1.get)) + (Some(BinaryExpr(operator, arg0.get, arg1.get)), l) } else { - None + (None, l) } } diff --git a/src/main/scala/translating/GTIRBToIR.scala b/src/main/scala/translating/GTIRBToIR.scala index 31049706c..ceab95cbd 100644 --- a/src/main/scala/translating/GTIRBToIR.scala +++ b/src/main/scala/translating/GTIRBToIR.scala @@ -131,17 +131,19 @@ class GTIRBToIR(mods: Seq[Module], parserMap: immutable.Map[String, Array[Array[ // TODO this is a hack to imitate BAP so that the existing specifications relying on this will work // we cannot and should not rely on this at all - private def createArguments(name: String): (ArrayBuffer[Parameter], ArrayBuffer[Parameter]) = { - val args = ArrayBuffer.newBuilder[Parameter] + private def createArguments(name: String): (Map[LocalVar, Expr], ArrayBuffer[LocalVar]) = { var regNum = 0 - val in = if (name == "main") { - ArrayBuffer(Parameter("main_argc", 32, Register("R0", 64)), Parameter("main_argv", 64, Register("R1", 64))) + val in : Map[LocalVar, Expr] = if (name == "main") { + Map( + (LocalVar("main_argc", BitVecType(32)) -> Extract(32,0, Register("R0", (64)))), + (LocalVar("main_argv", BitVecType(32)) -> Extract(32,0, Register("R1", (64)))), + ) } else { - ArrayBuffer() + Map() } - val out = ArrayBuffer(Parameter(name + "_result", 32, Register("R0", 64))) + val out = ArrayBuffer[LocalVar]() (in, out) } @@ -264,7 +266,8 @@ class GTIRBToIR(mods: Seq[Module], parserMap: immutable.Map[String, Array[Array[ val (in, out) = createArguments(name) - val procedure = Procedure(name, address, in = in, out = out) + val procedure = Procedure(name, address, formalInParam = in.map(_._1), formalOutParam = out, inParamDefaultBinding=in.toMap) + procedure.inParamDefaultBinding = immutable.SortedMap.from(in.map((l,r) => l -> LocalVar(l.name, BitVecType(64)))) uuidToProcedure += (functionUUID -> procedure) entranceUUIDtoProcedure += (entranceUUID -> procedure) @@ -278,6 +281,7 @@ class GTIRBToIR(mods: Seq[Module], parserMap: immutable.Map[String, Array[Array[ createBlock(blockUUID, procedure, entranceUUID, blockCount) blockCount += 1 } + procedure } @@ -374,7 +378,7 @@ class GTIRBToIR(mods: Seq[Module], parserMap: immutable.Map[String, Array[Array[ val jumpCopy = currentBlock.jump match { case GoTo(targets, label) => GoTo(targets, label) case Unreachable(label) => Unreachable(label) - case Return(label) => Return(label) + case Return(label, args) => Return(label, args) case _ => throw Exception("this shouldn't be reachable") } trueBlock.replaceJump(currentBlock.jump) @@ -652,5 +656,5 @@ class GTIRBToIR(mods: Seq[Module], parserMap: immutable.Map[String, Array[Array[ val assume = Assume(condition, checkSecurity = true) Block(newLabel, None, ArrayBuffer(assume), GoTo(ArrayBuffer(target))) } - } + diff --git a/src/main/scala/translating/ILtoIL.scala b/src/main/scala/translating/ILtoIL.scala index 9e17aee9e..2177b84f6 100644 --- a/src/main/scala/translating/ILtoIL.scala +++ b/src/main/scala/translating/ILtoIL.scala @@ -1,4 +1,5 @@ package translating +import ir.cilvisitor.* import ir.* private class ILSerialiser extends ReadOnlyVisitor { @@ -53,6 +54,14 @@ private class ILSerialiser extends ReadOnlyVisitor { node } + + override def visitAssume(node: Assume): Statement = { + program ++= "Assume(" + visitExpr(node.body) + program ++= ")" + node + } + override def visitMemoryLoad(node: MemoryLoad): Statement = { program ++= "MemoryLoad(" visitVariable(node.lhs) @@ -74,8 +83,8 @@ private class ILSerialiser extends ReadOnlyVisitor { override def visitJump(node: Jump): Jump = { node match { case j: GoTo => program ++= s"goTo(${j.targets.map(_.label).mkString(", ")})" - case _: Unreachable => program ++= "halt" - case _: Return => program ++= "return" + case h: Unreachable => program ++= "halt" + case h: Return => program ++= s"return (${h.outParams.map(_._2).mkString(", ")})" } node @@ -90,7 +99,9 @@ private class ILSerialiser extends ReadOnlyVisitor { override def visitDirectCall(node: DirectCall): Statement = { program ++= "DirectCall(" + program ++= "(" + node.outParams.map(_._2).mkString(", ") + ") := call " program ++= procedureIdentifier(node.target) + program ++= "(" + node.actualParams + ")" program ++= ")" // DirectCall node } @@ -135,19 +146,10 @@ private class ILSerialiser extends ReadOnlyVisitor { indentLevel += 1 program ++= "in(" - for (i <- node.in.indices) { - visitParameter(node.in(i)) - if (i != node.in.size - 1) { - program ++= ", " - } - } + program ++= node.formalInParam.mkString(", ") program ++= "), " program ++= "out(" - for (i <- node.out.indices) { - visitParameter(node.out(i)) - if (i != node.out.size - 1) - program ++= ", " - } + program ++= node.formalOutParam.mkString(", ") program ++= "), " program ++= "blocks(\n" for (b <- node.blocks) { @@ -158,13 +160,6 @@ private class ILSerialiser extends ReadOnlyVisitor { node } - override def visitParameter(node: Parameter): Parameter = { - program ++= "Parameter(" - visitRegister(node.value) - program ++= ")" - node - } - override def visitProgram(node: Program): Program = { for (i <- node.procedures) { visitProcedure(i) @@ -250,6 +245,12 @@ private class ILSerialiser extends ReadOnlyVisitor { } +def serialiseIL(p: Procedure): String = { + val s = ILSerialiser() + s.visitProcedure(p) + s.program.toString() +} + def serialiseIL(p: Program): String = { val s = ILSerialiser() s.visitProgram(p) diff --git a/src/main/scala/translating/IRExpToSMT2.scala b/src/main/scala/translating/IRExpToSMT2.scala new file mode 100644 index 000000000..ec5dcb8e1 --- /dev/null +++ b/src/main/scala/translating/IRExpToSMT2.scala @@ -0,0 +1,351 @@ +package translating +import ir.* +import boogie.* +import specification.* +import util.{BoogieGeneratorConfig, BoogieMemoryAccessMode, ProcRelyVersion} +import ir.cilvisitor.* +import scala.sys.process.* + +trait BasilIR[Repr[+_]] extends BasilIRExp[Repr] { + // def vstmt(s: Statement) : Repr[Statement] + + def vstmt(s: Statement): Repr[Statement] = { + s match { + case a: LocalAssign => vassign(vlvar(a.lhs), vexpr(a.rhs)) + case m @ MemoryLoad(lhs, mem, index, endian, size, _) => vload(vlvar(lhs), mem.name, vexpr(index), endian, size) + case m: MemoryStore => vstore(m.mem.name, vexpr(m.index), vexpr(m.value), m.endian, m.size) + case c: DirectCall => + vcall( + c.outParams.toList.map((l, r) => (l, vexpr(r))), + c.target.name, + c.actualParams.toList.map((l, r) => (l, vexpr(r))) + ) + case i: IndirectCall => vindirect(vrvar(i.target)) + case a: Assert => vassert(vexpr(a.body)) + case a: Assume => vassume(vexpr(a.body)) + case n: NOP => vnop() + } + } + + def vjump(j: Jump): Repr[Jump] = { + j match { + case g: GoTo => vgoto(g.targets.toList.map(_.label)) + case g: Unreachable => vunreachable() + case r: Return => vreturn(r.outParams.toList.map((l, r) => (vlvar(l), vexpr(r)))) + } + } + + + def vexpr(e: Expr): Repr[Expr] = { + e match { + case n: Literal => vliteral(n) + case Extract(ed, start, arg) => vextract(ed, start, vexpr(arg)) + case Repeat(repeats, arg) => vrepeat(repeats, vexpr(arg)) + case ZeroExtend(bits, arg) => vzeroextend(bits, vexpr(arg)) + case SignExtend(bits, arg) => vsignextend(bits, vexpr(arg)) + case BinaryExpr(op, arg, arg2) => vbinary_expr(op, vexpr(arg), vexpr(arg2)) + case UnaryExpr(op, arg) => vunary_expr(op, vexpr(arg)) + case v: Variable => vrvar(v) + case f @ UninterpretedFunction(n, params, rt) => vuninterp_function(n, params.map(vexpr)) + } + } + + def vblock(b: Block): Repr[Block] = vblock(b.label, b.address, b.statements.toList.map(vstmt), vjump(b.jump)) + def vproc(p: Procedure): Repr[Procedure] = vproc( + p.name, + p.formalInParam.toList.map(vlvar), + p.formalOutParam.toList.map(vlvar), + p.entryBlock.map(vblock), + (p.blocks.toSet -- p.entryBlock.toSet -- p.returnBlock.toSet).toList.sortBy( x => -x.rpoOrder).map(vblock), + p.returnBlock.map(vblock) + ) + + def vblock(label: String, address:Option[BigInt], statements: List[Repr[Statement]], terminator: Repr[Jump]): Repr[Block] + + def vprog(p: Program) : Repr[Program] = vprog(p.mainProcedure.name, p.procedures.toList.map(vproc)) + def vprog(mainProc: String, procedures: List[Repr[Procedure]]) : Repr[Program] + + def vproc( + name: String, + inParams: List[Repr[Variable]], + outParams: List[Repr[Variable]], + entryBlock: Option[Repr[Block]], + middleBlocks: List[Repr[Block]], + returnBlock: Option[Repr[Block]] + ): Repr[Procedure] + + def vassign(lhs: Repr[Variable], rhs: Repr[Expr]): Repr[LocalAssign] + def vload(lhs: Repr[Variable], mem: String, index: Repr[Expr], endian: Endian, size: Int): Repr[MemoryLoad] + def vstore(mem: String, index: Repr[Expr], value: Repr[Expr], endian: Endian, size: Int): Repr[MemoryStore] + def vcall( + outParams: List[(Variable, Repr[Expr])], + procname: String, + inparams: List[(Variable, Repr[Expr])] + ): Repr[DirectCall] + def vindirect(target: Repr[Variable]): Repr[IndirectCall] + def vassert(body: Repr[Expr]): Repr[Assert] + def vassume(body: Repr[Expr]): Repr[Assume] + def vnop(): Repr[NOP] + + def vgoto(t: List[String]): Repr[GoTo] + def vunreachable(): Repr[Unreachable] + def vreturn(outs: List[(Repr[Variable], Repr[Expr])]): Repr[Return] + + def vlvar(e: Variable): Repr[Variable] + +} + +trait BasilIRExp[Repr[+_]] { + def vexpr(e: Expr): Repr[Expr] + def vextract(ed: Int, start: Int, a: Repr[Expr]): Repr[Expr] + def vzeroextend(bits: Int, b: Repr[Expr]): Repr[Expr] + def vsignextend(bits: Int, b: Repr[Expr]): Repr[Expr] + def vbinary_expr(e: BinOp, l: Repr[Expr], r: Repr[Expr]): Repr[Expr] + def vunary_expr(e: UnOp, arg: Repr[Expr]): Repr[Expr] + def vliteral(l: Literal) : Repr[Literal] = { + l match { + case TrueLiteral => vboollit(true) + case FalseLiteral => vboollit(false) + case v: IntLiteral => vintlit(v.value) + case b: BitVecLiteral => vbvlit(b) + } + } + + def vboollit(b: Boolean): Repr[BoolLit] + def vbvlit(b: BitVecLiteral): Repr[BitVecLiteral] + def vintlit(b: BigInt): Repr[IntLiteral] + def vrepeat(reps: Int, value: Repr[Expr]): Repr[Expr] + + def vuninterp_function(name: String, args: Seq[Repr[Expr]]): Repr[Expr] + + def vrvar(e: Variable): Repr[Variable] +} + +trait BasilIRExpWithVis[Repr[+_]] extends BasilIRExp[Repr] { + + /** Performs some simple reductions to fit basil IR into SMT2. + */ + + + def vexpr(e: Expr): Repr[Expr] = { + e match { + case n: Literal => vliteral(n) + case Extract(ed, start, arg) => vextract(ed, start, vexpr(arg)) + case Repeat(repeats, arg) => { + vexpr((0 until (repeats - 1)).foldLeft(arg)((acc, n) => BinaryExpr(BVCONCAT, acc, arg))) + } + case ZeroExtend(bits, arg) => vexpr(BinaryExpr(BVCONCAT, BitVecLiteral(0, bits), arg)) + case SignExtend(bits, arg) => + vexpr(BinaryExpr(BVCONCAT, Repeat(bits, Extract(size(arg).get, size(arg).get - 1, arg)), arg)) + case BinaryExpr(op, arg, arg2) => + op match { + case BVNEQ => vunary_expr(BoolNOT, vbinary_expr(BVEQ, vexpr(arg), vexpr(arg2))) + case IntNEQ => vunary_expr(BoolNOT, vbinary_expr(IntEQ, vexpr(arg), vexpr(arg2))) + case BoolNEQ => vunary_expr(BoolNOT, vbinary_expr(BoolEQ, vexpr(arg), vexpr(arg2))) + case _ => vbinary_expr(op, vexpr(arg), vexpr(arg2)) + } + case UnaryExpr(op, arg) => vunary_expr(op, vexpr(arg)) + case v: Variable => vrvar(v) + case f @ UninterpretedFunction(n, params, rt) => vuninterp_function(n, params.map(vexpr)) + } + } + +} + +enum Sexp[+T] { + case Symb(v: String) + case Slist(v: List[Sexp[T]]) +} + +object Sexp { + + def print[T](s: Sexp[T]): String = s match { + case Sexp.Symb(a) => a + case Sexp.Slist(v) => "(" + v.map(print).mkString(" ") + ")" + } +} + +def sym[T](l: String): Sexp[T] = Sexp.Symb[T](l) +def list[T](l: Sexp[T]*): Sexp[T] = Sexp.Slist(l.toList) + +object BasilIRToSMT2 extends BasilIRExpWithVis[Sexp] { + def vload(lhs: Sexp[Variable], mem: String, index: Sexp[Expr], endian: Endian, size: Int): Sexp[MemoryLoad] = ??? + def vstore(mem: String, index: Sexp[Expr], value: Sexp[Expr], endian: Endian, size: Int): Sexp[MemoryStore] = ??? + + def vprog(mainProc: String, procedures: List[Sexp[Procedure]]) : Sexp[Program] = ??? + def vrepeat(reps: Int, value: Sexp[Expr]): Sexp[Expr] = ??? + def vzeroextend(bits: Int, b: Sexp[Expr]): Sexp[Expr] = ??? + def vsignextend(bits: Int, b: Sexp[Expr]): Sexp[Expr] = ??? + def vboollit(b: Boolean): Sexp[BoolLit] = ??? + def vbvlit(b: BitVecLiteral): Sexp[BitVecLiteral] = ??? + def vintlit(b: BigInt): Sexp[IntLiteral] = ??? + + /** + * Immediately invoke z3 and block until it returns a result. + * + * Return Some(true) when proven, Some(false) when counterexample found, and None when unknown. + */ + def proveExpr(e: Expr, softTimeoutMillis: Option[Int] = None) : Option[Boolean] = { + val query = exprUnsat(e, None, false) + val res = util.z3.checkSATSMT2(query, softTimeoutMillis) + res match { + case util.z3.SatResult.UNSAT => Some(true) + case util.z3.SatResult.SAT => Some(false) + case util.z3.SatResult.Unknown(_) => None + } + } + + def exprUnsat(e: Expr, name: Option[String] = None, getModel : Boolean = true): String = { + val assert = if (name.isDefined) { + list(sym("assert"), list(sym("!"), BasilIRToSMT2.vexpr(e), sym(":named"), sym(name.get))) + } else { + list(sym("assert"), BasilIRToSMT2.vexpr(e)) + } + + val terms = list(sym("push")) :: BasilIRToSMT2.extractDecls(e) + ++ List( + assert, + list(sym("set-option"), sym(":smt.timeout"), sym("1")), + list(sym("check-sat")) + ) + ++ (if (getModel) then List(list(sym("echo"), sym("\"" + name.getOrElse("") + " :: " + e + "\"")), list(sym("get-model"))) else List()) + ++ List(list(sym("pop"))) + + (terms.map(Sexp.print)).mkString("\n") + } + + def unaryOpnameToFun(b: UnOp) = { + b match { + case BoolNOT => "not" + case BVNOT => "bvnot" + case BVNEG => "bvneg" + case IntNEG => "-" + case BoolToBV1 => "bool2bv1" + } + } + + def opnameToFun(b: BinOp) = { + b match { + case IntEQ => "=" + case BoolEQ => "=" + case BVEQ => "=" + case BVNEQ => ??? + case IntNEQ => ??? + case BoolNEQ => ??? + case BoolOR => "or" + case BVCONCAT => "concat" + case b: BVBinOp => "bv" + b.opName + case b: BoolBinOp => b.opName + case b: IntBinOp => b.opName + } + } + + def fixVname(n: String): String = { + n.map(c => + c match { + case '#' => 'x' + case c => c + } + ).mkString("") + } + + def int2smt(i: Int) = sym(i.toString) + def bv2smt(i: BitVecLiteral) = list(sym("_"), sym(s"bv${i.value}"), sym(i.size.toString)) + + override def vextract(ed: Int, start: Int, a: Sexp[Expr]): Sexp[Expr] = + list(list(sym("_"), sym("extract"), int2smt(ed - 1), int2smt(start)), a) + override def vbinary_expr(e: BinOp, l: Sexp[Expr], r: Sexp[Expr]): Sexp[Expr] = list(sym(opnameToFun(e)), l, r) + override def vunary_expr(e: UnOp, arg: Sexp[Expr]): Sexp[Expr] = list(sym(unaryOpnameToFun(e)), arg) + + override def vliteral(arg: Literal): Sexp[Literal] = arg match { + case bv @ BitVecLiteral(value, size) => bv2smt(bv) + case IntLiteral(i) => sym(i.toString) + case TrueLiteral => sym("true") + case FalseLiteral => sym("false") + } + + def endianToBool(endian: Endian): Sexp[Expr] = { + if endian == Endian.LittleEndian then vexpr(FalseLiteral) else vexpr(TrueLiteral) + } + override def vuninterp_function(name: String, args: Seq[Sexp[Expr]]): Sexp[Expr] = { + if (args.size == 1) { + list(sym(name), args.head) + } else { + list(sym(name), Sexp.Slist(args.toList)) + } + } + + override def vrvar(e: Variable): Sexp[Variable] = sym(fixVname(e.name)) + + def basilTypeToSMTType(v: IRType): Sexp[Expr] = { + v match { + case BoolType => sym("Bool") + case IntType => sym("Int") + case BitVecType(sz) => list(sym("_"), sym("BitVec"), int2smt(sz)) + case MapType(pt, rt) => list(sym("Array"), basilTypeToSMTType(pt), basilTypeToSMTType(rt)) + } + } + + def booltoBVDef: Sexp[Expr] = { + list( + sym("define-fun"), + sym("bool2bv1"), + list(list(sym("arg"), basilTypeToSMTType(BoolType))), + basilTypeToSMTType(BitVecType(1)), + list(sym("ite"), sym("arg"), bv2smt(BitVecLiteral(1, 1)), bv2smt(BitVecLiteral(0, 1))) + ) + } + + def interpretFun(x: UninterpretedFunction): Option[Sexp[Expr]] = { + x.name match { + case "bool2bv1" => { + Some(booltoBVDef) + } + case "bvsaddo" => None + case _ => { + Some( + list( + sym("declare-fun"), + sym(x.name), + Sexp.Slist(x.params.toList.map(a => basilTypeToSMTType(a.getType))), + basilTypeToSMTType(x.returnType) + ) + ) + } + } + } + + def extractDecls(e: Expr): List[Sexp[Expr]] = { + + class ToDecl extends CILVisitor { + var decled = Set[Sexp[Expr]]() + + override def vexpr(e: Expr) = e match { + case f: UninterpretedFunction => { + val decl = interpretFun(f) + decled = decled ++ decl.toSet + DoChildren() // get variables out of args + } + case UnaryExpr(BoolToBV1, _) => { + decled = decled + booltoBVDef + DoChildren() + } + case v: Variable => { + val decl = list(sym("declare-const"), sym(fixVname(v.name)), basilTypeToSMTType(v.getType)) + decled = decled + decl + SkipChildren() + } + case _ => DoChildren() + } + + def getDecls(e: Expr): Set[Sexp[Expr]] = { + decled = Set() + visit_expr(this, e) + decled + } + } + + ToDecl().getDecls(e).toList + } + +} diff --git a/src/main/scala/translating/IRToBoogie.scala b/src/main/scala/translating/IRToBoogie.scala index 81bfd52f5..feb34d6d6 100644 --- a/src/main/scala/translating/IRToBoogie.scala +++ b/src/main/scala/translating/IRToBoogie.scala @@ -3,7 +3,7 @@ import analysis.{RegionInjector, DataRegion, HeapRegion, MergedRegion} import ir.{BoolOR, *} import boogie.* import specification.* -import util.{BoogieGeneratorConfig, BoogieMemoryAccessMode, ProcRelyVersion} +import util.{BoogieGeneratorConfig, BoogieMemoryAccessMode, ProcRelyVersion, Logger} import scala.collection.mutable import scala.collection.mutable.ArrayBuffer @@ -89,10 +89,10 @@ class IRToBoogie(var program: Program, var spec: Specification, var thread: Opti val procedures: ArrayBuffer[BProcedure] = thread match { case None => - program.procedures.map(f => translateProcedure(f, readOnlyMemory, initialMemory)) + program.procedures.map(f => translateProcedure(f, readOnlyMemory)) case Some(t) => val translatedProcedures: ArrayBuffer[BProcedure] = ArrayBuffer[BProcedure]() - t.procedures.foreach(p => translatedProcedures.addOne(translateProcedure(p, readOnlyMemory, initialMemory))) + t.procedures.foreach(p => translatedProcedures.addOne(translateProcedure(p, readOnlyMemory))) translatedProcedures } @@ -125,7 +125,8 @@ class IRToBoogie(var program: Program, var spec: Specification, var thread: Opti val functionsUsed2 = functionsUsed1.map(p => functionOpToDefinition(p)) val functionsUsed3 = functionsUsed2.flatMap(p => p.functionOps).map(p => functionOpToDefinition(p)) val functionsUsed4 = functionsUsed3.flatMap(p => p.functionOps).map(p => functionOpToDefinition(p)) - val functionsUsed = (functionsUsed2 ++ functionsUsed3 ++ functionsUsed4).toList.sorted + val functionsUsed5 = functionsUsed4.flatMap(p => p.functionOps).map(p => functionOpToDefinition(p)) + val functionsUsed = (functionsUsed2 ++ functionsUsed3 ++ functionsUsed4 ++ functionsUsed5).toList.sorted val globalVars = procedures.flatMap(_.globals) ++ rgProcs.flatMap(_.globals) val globalDecls = globalVars.map(b => BVarDecl(b, List(externAttr))).distinct.sorted.toList @@ -257,6 +258,12 @@ class IRToBoogie(var program: Program, var spec: Specification, var thread: Opti def functionOpToDefinition(f: FunctionOp): BFunction = { f match { + case b @ BoolToBV1Op(arg) => { + val invar = BParam("arg", BoolBType) + val outvar = BParam(BitVecBType(1)) + val body = IfThenElse(invar, BitVecBLiteral(1,1), BitVecBLiteral(0, 1)) + BFunction(b.fnName, List(invar), outvar, Some(body), List(externAttr)) + } case b: BVFunctionOp => BFunction(b.name, b.in, b.out, None, List(externAttr, b.attribute)) case m: MemoryLoadOp => val memVar = BMapVar("memory", MapBType(BitVecBType(m.addressSize), BitVecBType(m.valueSize)), Scope.Parameter) @@ -275,8 +282,12 @@ class IRToBoogie(var program: Program, var spec: Specification, var thread: Opti case Endian.LittleEndian => accesses } - val body: BExpr = accessesEndian.tail.foldLeft(accessesEndian.head) { (concat: BExpr, next: MapAccess) => - BinaryBExpr(BVCONCAT, next, concat) + val body: BExpr = accessesEndian.toList match { + case h::Nil => h + case h::tail => tail.foldLeft(h) { + (concat: BExpr, next: MapAccess) => BinaryBExpr(BVCONCAT, next, concat) + } + case Nil => throw Exception(s"Zero byte access: $f") } BFunction(m.fnName, in, out, Some(body), List(externAttr)) @@ -475,8 +486,8 @@ class IRToBoogie(var program: Program, var spec: Specification, var thread: Opti } - def translateProcedure(p: Procedure, readOnlyMemory: List[BExpr], initialMemory: List[BExpr]): BProcedure = { - val body = (p.entryBlock.view ++ p.blocks.filterNot(x => p.entryBlock.contains(x))).map(translateBlock).toList + def translateProcedure(p: Procedure, readOnlyMemory: List[BExpr]): BProcedure = { + val body = (p.entryBlock.view ++ ArrayBuffer.from(p.blocks).sortBy( x => -x.rpoOrder).filterNot(x => p.entryBlock.contains(x))).map(translateBlock).toList val modifies: Seq[BVar] = p.modifies.toSeq.flatMap { case m: Memory => Seq(m.toBoogie, m.toGamma) @@ -486,24 +497,27 @@ class IRToBoogie(var program: Program, var spec: Specification, var thread: Opti val modifiedPreserve = modifies.collect { case m: BVar if modifiedCheck.contains(m) => m } val modifiedPreserveEnsures: List[BExpr] = modifiedPreserve.map(m => BinaryBExpr(BoolEQ, m, Old(m))).toList - val procRequires: List[BExpr] = p.requires ++ requires.getOrElse(p.name, List()) - val procEnsures: List[BExpr] = p.ensures ++ ensures.getOrElse(p.name, List()) + val procRequires: List[BExpr] = p.requires ++ requires.getOrElse(p.procName, List()) + val procEnsures: List[BExpr] = p.ensures ++ ensures.getOrElse(p.procName, List()) - val procRequiresDirect: List[String] = requiresDirect.getOrElse(p.name, List()) - val procEnsuresDirect: List[String] = ensuresDirect.getOrElse(p.name, List()) + val procRequiresDirect: List[String] = requiresDirect.getOrElse(p.procName, List()) + val procEnsuresDirect: List[String] = ensuresDirect.getOrElse(p.procName, List()) val freeRequires: List[BExpr] = if (p == program.mainProcedure) { - initialMemory ++ readOnlyMemory + memoryToCondition(program.initialMemory.values) ++ readOnlyMemory } else { readOnlyMemory } val freeEnsures = modifiedPreserveEnsures ++ readOnlyMemory + val inparams = p.formalInParam.toList.flatMap(para => Seq(para.toBoogie, para.toGamma)) + val outparams = p.formalOutParam.toList.flatMap(para => Seq(para.toBoogie, para.toGamma)) + BProcedure( p.name, - List(), - List(), + inparams, + outparams, procEnsures, procRequires, procEnsuresDirect, @@ -518,7 +532,8 @@ class IRToBoogie(var program: Program, var spec: Specification, var thread: Opti private def memoryToCondition(memorySections: Iterable[MemorySection]): List[BExpr] = { def coalesced: List[BExpr] = { - val sections = memorySections.flatMap { s => + val sections = memorySections.filter(_.size >= 8) + .flatMap { s => // Phrase the memory condition in terms of 64-bit operations, as long as the memory // section's size is a multiple of 64-bits and 64-bits (8 bytes) aligned // If the memory section is not aligned, the initial unaligned part of it will not be coalesced into a 64-bit @@ -622,7 +637,7 @@ class IRToBoogie(var program: Program, var spec: Specification, var thread: Opti def translateBlock(b: Block): BBlock = { val captureState = captureStateStatement(s"${b.label}") - val cmds = List(captureState) ++ b.statements.flatMap(s => translate(s)) ++ translate(b.jump) + val cmds = List() ++ b.statements.flatMap(s => translate(s)) ++ translate(b.jump) BBlock(b.label, cmds) } @@ -705,13 +720,22 @@ class IRToBoogie(var program: Program, var spec: Specification, var thread: Opti } val jump = GoToCmd(g.targets.map(_.label).toSeq) conditionAssert :+ jump - case _: Return => List(ReturnCmd) + case r: Return => { + val out = r.outParams.toList + if (out.nonEmpty) then List( + AssignCmd(out.map(_._1.toBoogie), out.map(_._2.toBoogie)), + AssignCmd(out.map(_._1.toGamma), out.map(c => exprToGamma(c._2))), + ReturnCmd) else List(ReturnCmd) + } case _: Unreachable => List(BAssume(FalseBLiteral)) } def translate(j: Call): List[BCmd] = j match { case d: DirectCall => - val call = BProcedureCall(d.target.name) + val call = BProcedureCall(d.target.name, + d.outParams.toList.flatMap(c => Seq(c._2.toBoogie, c._2.gammas.head.toGamma)), + d.actualParams.toList.flatMap(c => Seq(c._2.toBoogie, exprToGamma(c._2))) + ) (config.procedureRely match { case Some(ProcRelyVersion.Function) => @@ -738,11 +762,12 @@ class IRToBoogie(var program: Program, var spec: Specification, var thread: Opti val lhsGamma = m.mem.toGamma val rhsGamma = GammaStore(m.mem.toGamma, m.index.toBoogie, exprToGamma(m.value), m.size, m.size / m.mem.valueSize) val store = AssignCmd(List(lhs, lhsGamma), List(rhs, rhsGamma)) + val stateSplit = List.empty /*s match { val stateSplit = s match { case MemoryStore(_, _, _, _, _, Some(label)) => List(captureStateStatement(s"$label")) case LocalAssign(_, _, Some(label)) => List(captureStateStatement(s"$label")) case _ => List.empty - } + } */ m.mem match { case _: StackMemory => List(store) ++ stateSplit diff --git a/src/main/scala/translating/PrettyPrinter.scala b/src/main/scala/translating/PrettyPrinter.scala new file mode 100644 index 000000000..747459006 --- /dev/null +++ b/src/main/scala/translating/PrettyPrinter.scala @@ -0,0 +1,381 @@ +package translating +import ir.* +import ir.cilvisitor.* +import scala.collection.immutable.ListMap +import scala.collection.mutable + +object PrettyPrinter { + def pp_expr(e: Expr) = BasilIRPrettyPrinter()(e) + def pp_stmt(s: Statement) = BasilIRPrettyPrinter()(s) + def pp_cmd(c: Command) = c match { + case j: Jump => pp_jump(j) + case j: Statement => pp_stmt(j) + } + def pp_block(s: Block) = BasilIRPrettyPrinter()(s) + def pp_jump(s: Jump) = BasilIRPrettyPrinter()(s) + def pp_prog(s: Program) = BasilIRPrettyPrinter()(s) + def pp_proc(s: Procedure) = BasilIRPrettyPrinter()(s) +} + + +def indent(s: String, indent: String = " "): String = { + s.flatMap(c => + c match { + case '\n' => s"\n$indent" + case c => "" + c + } + ) +} + +trait PPProg[+T] + +case class BST[T <: Expr | Command](v: String) extends PPProg[T] { + override def toString = v +} + +case class Prog(mainProc: String, globalDecls: List[String], procedures: List[Proc]) extends PPProg[Program] { + override def toString = globalDecls.map(_ + ";").mkString("\n") + "\n\n" + procedures.map(_.toString + ";\n").mkString("") +} + +case class Proc( + signature: String, + localDecls: List[String], + blocks: String +) extends PPProg[Procedure] { + override def toString = { + if (blocks.size > 0) { + signature + "\n{" + "\n" + blocks + "\n}" + } else { + signature + " {}" + } + } +} + +case class PBlock(label: String, address: Option[String], commands: List[String]) extends PPProg[Block] { + override def toString = { + val indent = " " + val addr = address.map(" " + _).getOrElse("") + s"block ${label}${addr} [\n" + ++ commands.map(" " + _).mkString(";\n") + ++ "\n]" + } +} + +// case class BST[+T](val v: String) { +// def ++(s: String) = BST(v ++ s) +// override def toString = v +// } + +class BasilIRPrettyPrinter() extends BasilIR[PPProg] { + val blockIndent = " " + val statementIndent = " " + val seenVars = mutable.HashSet[Variable]() + + def apply(x: Block): String = { + vblock(x).toString + } + def apply(x: Procedure): String = { + vproc(x).toString + } + def apply(x: Statement): String = { + vstmt(x).toString + } + def apply(x: Jump): String = { + vjump(x).toString + } + def apply(x: Expr): String = { + vexpr(x).toString + } + def apply(x: Program): String = { + vprog(x).toString + } + + class Vars(val global: Boolean = true) extends CILVisitor { + val vars = mutable.Set[Variable]() + + override def vlvar(v: Variable) = { + v match { + case v: Global if global => vars.add(v) + case v: LocalVar if (!global) => vars.add(v) + case _ => () + } + + SkipChildren() + } + + override def vrvar(v: Variable) = { + v match { + case v: Global if global => vars.add(v) + case v: LocalVar if (!global) => vars.add(v) + case _ => () + } + + SkipChildren() + } + } + + def globals(prog: Program): Set[Variable] = { + val v = Vars(true) + visit_prog(v, prog) + v.vars.toSet + } + + def locals(prog: Procedure): Set[Variable] = { + val v = Vars(false) + visit_proc(v, prog) + v.vars.toSet + } + + def memoryRegions(prog: Program): Set[Memory] = { + prog.collect { + case m: MemoryLoad => m.mem + case m: MemoryStore => m.mem + }.toSet + } + + override def vprog(p: Program): PPProg[Program] = { + Prog( + p.mainProcedure.name, + memoryRegions(p).toList.sortBy(_.name).map(memdecl) ++ + globals(p).toList.sorted.map(vardecl) + ++ List("\nlet entry_procedure = " + p.mainProcedure.name) + // ++ List(initialMemory(p.initialMemory.values)) + , + p.procedures.toList.map(vproc).collect { + case p: Proc => p + case _ => ??? + } + ) + } + + def vprog(mainProc: String, procedures: List[PPProg[Procedure]]): PPProg[Program] = { + // shouldn't be used + assert(false) + } + + override def vblock( + label: String, + address: Option[BigInt], + statements: List[PPProg[Statement]], + terminator: PPProg[Jump] + ): PPProg[Block] = { + val addr = address.map(a => s"{address = ${vaddress(a)}}") + PBlock(label, addr, statements.map(_.toString) ++ Seq(terminator.toString)) + } + + def vardecl(v: Variable): String = { + s"var ${v.name} : ${vtype(v.getType)}" + } + + def memdecl(m: Memory): String = { + s"memory ${m.name} : ${vtype(m.getType)}" + + } + + trait Record + case class Val(val v: String) extends Record { + override def toString = v + } + case class Lst(val v: List[Record]) extends Record { + override def toString = { + if v.size == 0 then { + "[]" + } else if v.size == 1 then { + s"[${v.head}]" + } else { + val x = mutable.ArrayBuffer[String]() + x.addAll(v.map(_.toString)) + var res = "" + while (x.size > 0) { + var totake = 1 + var size = x.head.size + + while (size < (78 - totake) && totake < x.size) { + size += x(totake).size + totake += 1 + } + + val lot = x.take(totake) + + val bsep = if (res == "") then "" else ";\n" + res = res + bsep + lot.mkString(";") + x.remove(0, totake) + } + "[\n " + indent(res) + "\n]" + } + } + } + case class Rec(val v: ListMap[String, Record]) extends Record { + override def toString = { + if v.isEmpty then "{}" + else if v.size == 1 then { + s"{" + v.map((k, v) => k + " = " + v).mkString("\n") + "}" + } else { + s"{\n " + indent(v.map((k, v) => k + " = " + v).mkString(";\n")) + "\n}" + } + } + } + + def initialMemory(mems: Iterable[MemorySection]) = { + + val initmem = Lst( + mems + .map(s => + Rec( + ListMap( + "address" -> Val(vaddress(s.address).toString), + "name" -> Val(s.name), + "size" -> Val(vaddress(s.address).toString), + "readonly" -> Val(vboollit(s.readOnly).toString), + "bytes" -> Lst(s.bytes.map(b => Val(vintlit(b.value).toString)).toList) + ) + ++ s.region.map(r => "region" -> Val(r.name)) + ) + ) + .toList + ) + + s"let initial_memory = $initmem" + + } + + def vaddress(a: BigInt) = vintlit(a) + + def vparam(l: Variable) : String = { + s"${l.name}:${vtype{l.getType}}" + } + + override def vproc(p: Procedure): PPProg[Procedure] = { + seenVars.clear() + val decls = locals(p).map(vardecl) + + + val name = p.name + val inParams = p.formalInParam.toList.map(vparam) + val outParams = p.formalOutParam.toList.map(vparam) + val entryBlock = p.entryBlock.map(vblock) + val middleBlocks = + (p.blocks.toSet -- p.entryBlock.toSet -- p.returnBlock.toSet).toList.sortBy(x => -x.rpoOrder).map(vblock) + val returnBlock = p.returnBlock.map(vblock) + + val localDecls = decls.toList.sorted + + val iblocks = entryBlock.map(b => (s" entry = ${indent(b.toString)}")).toList + ++ returnBlock.map(b => (s" exit = ${indent(b.toString)}")).toList + + val addr = p.address.map(l => vaddress(l).toString).map(" address = " + _).toList + + val mblocks = + if (middleBlocks.size == 0) then None + else { + Some(s" blocks = [\n " + indent(middleBlocks.mkString(";\n"), " ") + "\n ]") + } + + val pname = Seq(s" name = \"${p.procName}\"") + + val blocks = (pname ++ addr ++ iblocks ++ mblocks.toList).map(_ + ";").mkString("\n") + + Proc( + s"proc $name(${inParams.mkString(", ")}) -> (${outParams.mkString(", ")})", + localDecls, + blocks + ) + } + + def vproc( + name: String, + inParams: List[PPProg[Variable]], + outParams: List[PPProg[Variable]], + entryBlock: Option[PPProg[Block]], + middleBlocks: List[PPProg[Block]], + returnBlock: Option[PPProg[Block]] + ): PPProg[Procedure] = ??? + + override def vassign(lhs: PPProg[Variable], rhs: PPProg[Expr]): PPProg[LocalAssign] = BST(s"${lhs} := ${rhs}") + + override def vstore( + mem: String, + index: PPProg[Expr], + value: PPProg[Expr], + endian: Endian, + size: Int + ): BST[MemoryStore] = { + val le = if endian == Endian.LittleEndian then "le" else "be" + BST(s"store $le ${mem} ${index} ${value} ${size}") + } + + def vload(lhs: PPProg[Variable], mem: String, index: PPProg[Expr], endian: Endian, size: Int): PPProg[MemoryLoad] = { + val le = if endian == Endian.LittleEndian then "le" else "be" + BST(s"$lhs := load $le ${mem} ${index} ${size}") + } + + override def vcall( + outParams: List[(Variable, PPProg[Expr])], + procname: String, + inparams: List[(Variable, PPProg[Expr])] + ): PPProg[DirectCall] = { + + + val op = { + if (outParams.forall(_._1.isInstanceOf[LocalVar])) { + "var (" + outParams.map((l, r) => vparam(l)).mkString(", ") + ")" + } else { + "(" + outParams.map((l, r) => vlvar(l)).mkString(", ") + ")" + } + } + + if (outParams.size > 5) { + BST(s"$op\n := call $procname (${inparams.map((l, r) => r).mkString(", ")})") + } else if (outParams.size > 0) { + BST(s"$op := call $procname (${inparams.map((l, r) => r).mkString(", ")})") + } else { + BST(s"call $procname (${inparams.map((l, r) => r).mkString(", ")})") + } + } + + override def vindirect(target: PPProg[Variable]): PPProg[IndirectCall] = BST(s"indirect call ${target} ") + override def vassert(body: PPProg[Expr]): PPProg[Assert] = BST(s"assert $body") + override def vassume(body: PPProg[Expr]): PPProg[Assume] = BST(s"assume $body") + override def vnop(): PPProg[NOP] = BST("nop") + + override def vgoto(t: List[String]): PPProg[GoTo] = BST[GoTo](s"goto(${t.mkString(", ")})") + override def vunreachable(): PPProg[Unreachable] = BST[Unreachable]("unreachable") + override def vreturn(outs: List[(PPProg[Variable], PPProg[Expr])]) = BST( + s"return (${outs.map((l, r) => r).mkString(", ")})" + ) + + def vtype(t: IRType): String = t match { + case BitVecType(sz) => s"bv$sz" + case IntType => "nat" + case BoolType => "bool" + case m: MapType => s"map ${vtype(m.result)}[${vtype(m.param)}]" + } + + override def vrvar(e: Variable): PPProg[Variable] = BST(s"${e.name}:${vtype(e.getType)}") + override def vlvar(e: Variable): PPProg[Variable] = { + e match { + case l: LocalVar => BST("var " + e.name + s": ${vtype(e.getType)}") + case l => BST(e.name + s": ${vtype(e.getType)}") + } + } + + override def vextract(ed: Int, start: Int, a: PPProg[Expr]): PPProg[Expr] = BST(s"extract($ed, $start, ${a})") + override def vzeroextend(bits: Int, b: PPProg[Expr]): PPProg[Expr] = BST(s"zero_extend($bits, $b)") + override def vsignextend(bits: Int, b: PPProg[Expr]): PPProg[Expr] = BST(s"sign_extend($bits, $b)") + override def vrepeat(reps: Int, b: PPProg[Expr]) = BST(s"repeat($reps, $b)") + override def vbinary_expr(e: BinOp, l: PPProg[Expr], r: PPProg[Expr]): PPProg[Expr] = { + val opn = e.getClass.getSimpleName.toLowerCase.stripSuffix("$") + BST(s"$opn($l, $r)") + } + override def vunary_expr(e: UnOp, arg: PPProg[Expr]): PPProg[Expr] = { + val opn = e.getClass.getSimpleName.toLowerCase.stripSuffix("$") + BST(s"$opn($arg)") + } + + override def vboollit(b: Boolean) = BST(b.toString) + override def vintlit(i: BigInt) = BST("0x%x".format(i)) + override def vbvlit(i: BitVecLiteral) = BST("0x%x".format(i.value) + s":bv${i.size}") + override def vuninterp_function(name: String, args: Seq[PPProg[Expr]]): PPProg[Expr] = BST( + s"$name(${args.mkString(", ")})" + ) +} diff --git a/src/main/scala/translating/ReadELFLoader.scala b/src/main/scala/translating/ReadELFLoader.scala index 5c958d8d1..cea924901 100644 --- a/src/main/scala/translating/ReadELFLoader.scala +++ b/src/main/scala/translating/ReadELFLoader.scala @@ -1,6 +1,8 @@ package translating +import util.Logger import Parsers.ReadELFParser.* +import boogie.* import specification.* import util.ILLoadingConfig @@ -114,11 +116,14 @@ object ReadELFLoader { private def getFunctionAddress(ctx: SymbolTableContext, functionName: String): Option[BigInt] = { if (ctx.symbolTableHeader.tableName.STRING.getText == ".symtab") { val rows = ctx.symbolTableRow.asScala - val mainAddress = rows.collectFirst { - case r if r.entrytype.getText == "FUNC" && r.bind.getText == "GLOBAL" && r.name.getText == functionName => + val mainAddress = rows.collect { + case r if r.entrytype.getText == "FUNC" && r.name.getText == functionName => hexToBigInt(r.value.getText) } - mainAddress + if (mainAddress.size > 1) { + Logger.warn(s"Multiple procedures with name $functionName") + } + mainAddress.headOption } else { None } @@ -136,11 +141,11 @@ object ReadELFLoader { val num = ctx.num.getText.toInt val vis = ELFVis.valueOf(ctx.vis.getText) - val ndx = ctx.ndx.getText match { + val ndx = (ctx.ndx.getText match { case "ABS" => ELFNDX.ABS case "UND" => ELFNDX.UND case o => ELFNDX.Section(o.toInt) - } + }) ELFSymbol(num, value, size, etype, bind, vis, ndx, name) } diff --git a/src/main/scala/translating/SpecificationLoader.scala b/src/main/scala/translating/SpecificationLoader.scala index eaca37ef6..557c369f4 100644 --- a/src/main/scala/translating/SpecificationLoader.scala +++ b/src/main/scala/translating/SpecificationLoader.scala @@ -4,6 +4,7 @@ import Parsers.SpecificationsParser._ import boogie._ import specification._ import ir._ +import util.Logger import scala.collection.mutable.ArrayBuffer import scala.jdk.CollectionConverters._ @@ -152,7 +153,7 @@ case class SpecificationLoader(symbols: Set[SpecGlobal], program: Program) { def visitExpr( ctx: ExprContext, nameToGlobals: Map[String, SpecGlobal], - params: Map[String, Parameter] = Map() + params: Map[String, Expr] = Map() ): BExpr = { val exprs = ctx.impliesExpr.asScala.map(e => visitImpliesExpr(e, nameToGlobals, params)) if (exprs.size > 1) { @@ -165,7 +166,7 @@ case class SpecificationLoader(symbols: Set[SpecGlobal], program: Program) { def visitImpliesExpr( ctx: ImpliesExprContext, nameToGlobals: Map[String, SpecGlobal], - params: Map[String, Parameter] = Map() + params: Map[String, Expr] = Map() ): BExpr = Option(ctx.arg2) match { case Some(_) => BinaryBExpr( @@ -179,7 +180,7 @@ case class SpecificationLoader(symbols: Set[SpecGlobal], program: Program) { def visitLogicalExpr( ctx: LogicalExprContext, nameToGlobals: Map[String, SpecGlobal], - params: Map[String, Parameter] = Map() + params: Map[String, Expr] = Map() ): BExpr = { val rels = ctx.relExpr.asScala.map(r => visitRelExpr(r, nameToGlobals, params)) if (rels.size > 1) { @@ -197,7 +198,7 @@ case class SpecificationLoader(symbols: Set[SpecGlobal], program: Program) { def visitRelExpr( ctx: RelExprContext, nameToGlobals: Map[String, SpecGlobal], - params: Map[String, Parameter] = Map() + params: Map[String, Expr] = Map() ): BExpr = Option(ctx.arg2) match { case Some(_) => BinaryBExpr( @@ -211,7 +212,7 @@ case class SpecificationLoader(symbols: Set[SpecGlobal], program: Program) { def visitTerm( ctx: TermContext, nameToGlobals: Map[String, SpecGlobal], - params: Map[String, Parameter] = Map() + params: Map[String, Expr] = Map() ): BExpr = Option(ctx.arg2) match { case Some(_) => BinaryBExpr( @@ -225,7 +226,7 @@ case class SpecificationLoader(symbols: Set[SpecGlobal], program: Program) { def visitFactor( ctx: FactorContext, nameToGlobals: Map[String, SpecGlobal], - params: Map[String, Parameter] = Map() + params: Map[String, Expr] = Map() ): BExpr = Option(ctx.arg2) match { case Some(_) => BinaryBExpr( @@ -239,7 +240,7 @@ case class SpecificationLoader(symbols: Set[SpecGlobal], program: Program) { def visitUnaryExpr( ctx: UnaryExprContext, nameToGlobals: Map[String, SpecGlobal], - params: Map[String, Parameter] = Map() + params: Map[String, Expr] = Map() ): BExpr = ctx match { case n: NegExprContext => UnaryBExpr(BVNEG, visitUnaryExpr(n.unaryExpr, nameToGlobals, params)) case a: AtomUnaryExprContext => visitAtomExpr(a.atomExpr, nameToGlobals, params) @@ -249,7 +250,7 @@ case class SpecificationLoader(symbols: Set[SpecGlobal], program: Program) { def visitAtomExpr( ctx: AtomExprContext, nameToGlobals: Map[String, SpecGlobal], - params: Map[String, Parameter] = Map() + params: Map[String, Expr] = Map() ): BExpr = ctx match { case b: BoolLitExprContext => visitBoolLit(b.boolLit) case i: IdExprContext => visitId(i.id, nameToGlobals, params) @@ -264,7 +265,7 @@ case class SpecificationLoader(symbols: Set[SpecGlobal], program: Program) { def visitArrayAccess( ctx: ArrayAccessContext, nameToGlobals: Map[String, SpecGlobal], - params: Map[String, Parameter] = Map() + params: Map[String, Expr] = Map() ): ArrayAccess = { val global = visitId(ctx.id, nameToGlobals, params) match { case g: SpecGlobal => g @@ -294,13 +295,13 @@ case class SpecificationLoader(symbols: Set[SpecGlobal], program: Program) { def visitOldExpr( ctx: OldExprContext, nameToGlobals: Map[String, SpecGlobal], - params: Map[String, Parameter] = Map() + params: Map[String, Expr] = Map() ): Old = Old(visitExpr(ctx.expr, nameToGlobals, params)) def visitIfThenElseExpr( ctx: IfThenElseExprContext, nameToGlobals: Map[String, SpecGlobal], - params: Map[String, Parameter] = Map() + params: Map[String, Expr] = Map() ): IfThenElse = { IfThenElse( visitExpr(ctx.guard, nameToGlobals, params), @@ -316,38 +317,36 @@ case class SpecificationLoader(symbols: Set[SpecGlobal], program: Program) { case "false" => FalseBLiteral } - def visitId(ctx: IdContext, nameToGlobals: Map[String, SpecGlobal], params: Map[String, Parameter] = Map()): BExpr = { + def visitId(ctx: IdContext, nameToGlobals: Map[String, SpecGlobal], params: Map[String, Expr] = Map()): BExpr = { ctx.getText match { - case id if id.startsWith("Gamma_R") => + case id if id.startsWith("Gamma_R") => { BVariable(id, BoolBType, Scope.Global) - case id if id.startsWith("Gamma_") => + } + case id if (id.startsWith("Gamma_")) => { val gamma_id = id.stripPrefix("Gamma_") params.get(gamma_id) match { - case Some(p: Parameter) => p.value.toGamma + case Some(p: LocalVar) => p.toGamma + case Some(p: Expr) => p.gammas.map(_.toGamma).head case None => nameToGlobals.get(gamma_id) match { case Some(g: SpecGlobal) => SpecGamma(g) case None => throw new Exception(s"unresolvable reference to '$id' in specification") } - } + } + } case id if id.startsWith("R") => BVariable(id, BitVecBType(64), Scope.Global) case id => params.get(id) match { - case Some(p: Parameter) => - val registerSize = p.value.size - val paramSize = p.size - if (paramSize == registerSize) { - p.value.toBoogie - } else if (registerSize > paramSize) { - BVExtract(registerSize - p.size, 0, p.value.toBoogie) - } else { - throw Exception(s"parameter $p doesn't fit in register ${p.value} for ID $id") - } + case Some(p: LocalVar) => p.toBoogie + case Some(p: Expr) => p.toBoogie case None => nameToGlobals.get(ctx.getText) match { case Some(g: SpecGlobal) => g - case None => throw new Exception(s"unresolvable reference to '$id' in specification") + case None => { + Logger.error(s"$params \n\n $nameToGlobals") + throw new Exception(s"unresolvable reference to '$id' in specification") + } } } } @@ -376,12 +375,14 @@ case class SpecificationLoader(symbols: Set[SpecGlobal], program: Program) { def visitSubroutine(ctx: SubroutineContext, nameToGlobals: Map[String, SpecGlobal]): SubroutineSpec = { val name = ctx.id.getText - val irProc = program.procedures.collectFirst { case p if p.name == name => p } + val irProc = program.procedures.collectFirst { case p if p.procName == name => p } - val params: Map[String, Parameter] = irProc match { + val params: Map[String, Expr] = irProc match { case None => Map() case Some(p) => - p.in.map(p => p.name -> p).toMap ++ p.out.map(p => p.name -> p).toMap + val r = p.inParamDefaultBinding.map(p => p._1.name -> p._2).toMap ++ p.outParamDefaultBinding.map(p => p._1.name -> p._2).toMap + + (p.procName + "_result" -> (Extract(32,0,Register("R0", 64)))) + r } val requires = ctx.requires.asScala.collect { case r: ParsedRequiresContext => diff --git a/src/main/scala/util/BASILConfig.scala b/src/main/scala/util/BASILConfig.scala index 2598ff0c7..65626405c 100644 --- a/src/main/scala/util/BASILConfig.scala +++ b/src/main/scala/util/BASILConfig.scala @@ -17,7 +17,9 @@ case class ILLoadingConfig( specFile: Option[String] = None, dumpIL: Option[String] = None, mainProcedureName: String = "main", - procedureTrimDepth: Int = Int.MaxValue + procedureTrimDepth: Int = Int.MaxValue, + parameterForm: Boolean = false, + trimEarly: Boolean = false, ) case class StaticAnalysisConfig( @@ -40,6 +42,8 @@ enum MemoryRegionsMode { case class BASILConfig( loading: ILLoadingConfig, runInterpret: Boolean = false, + simplify: Boolean = false, + validateSimp: Boolean = false, staticAnalysis: Option[StaticAnalysisConfig] = None, boogieTranslation: BoogieGeneratorConfig = BoogieGeneratorConfig(), outputPrefix: String diff --git a/src/main/scala/util/Logging.scala b/src/main/scala/util/Logging.scala index dcdd148bd..161154f79 100644 --- a/src/main/scala/util/Logging.scala +++ b/src/main/scala/util/Logging.scala @@ -1,19 +1,86 @@ package util import sourcecode.Line, sourcecode.FileName import scala.io.AnsiColor -import java.io.File as JFile +import collection.mutable.HashSet +import java.io.* enum LogLevel(val id: Int): case DEBUG extends LogLevel(0) case INFO extends LogLevel(1) case WARN extends LogLevel(2) case ERROR extends LogLevel(3) + case OFF extends LogLevel(4) + +class GenericLogger( + val name : String, + val defaultLevel: LogLevel = LogLevel.INFO, + var output: PrintStream = System.out, + var ANSIColour: Boolean = true +) { + + val children: HashSet[GenericLogger] = HashSet() -object Logger: - private var level: LogLevel = LogLevel.INFO import LogLevel.* - def write( + private var level: LogLevel = defaultLevel + + private def setColour(value: Boolean, setChildren: Boolean = false): Unit = { + ANSIColour = value + if (setChildren) { + for (c <- children) { + c.setColour(value, setChildren) + } + } + } + + def disableColour(setChildren: Boolean = false) = setColour(false, setChildren) + def enableColour(setChildren: Boolean = false): Unit = setColour(true, setChildren) + + + def deriveLogger(sname: String, stream: PrintStream): GenericLogger = { + val l = GenericLogger(name + "." + sname, level, stream, ANSIColour) + children.add(l) + l + } + + def deriveLogger(name: String, file: File): GenericLogger = { + deriveLogger(name, PrintStream(file)) + } + + def deriveLogger(name: String): GenericLogger = deriveLogger(name, output) + + def setOutput(stream: PrintStream) = output = stream + + def writeToFile(file: File, content: => String) = { + if (level.id < LogLevel.OFF.id) { + val l = deriveLogger(file.getName(), file) + l.print(content) + l.close() + children.remove(l) + } + } + + def print(s: String) = { + if (level.id < LogLevel.OFF.id) { + output.print(s) + } + } + + def println(s: String) = { + if (level.id < LogLevel.OFF.id) { + output.println(s) + } + } + + def close() = { + output.close() + } + + def flush() = { + output.flush() + } + + private def writeLog( logLevel: LogLevel, arg: Any, line: sourcecode.Line, @@ -21,46 +88,83 @@ object Logger: name: sourcecode.Name ): Unit = { - val colour = logLevel match - case DEBUG => AnsiColor.RESET - case INFO => AnsiColor.GREEN - case WARN => AnsiColor.YELLOW - case ERROR => AnsiColor.RED - - val showPosition = (logLevel, level) match - case (_, DEBUG) => true - case (ERROR, _) => true - case (WARN, _) => true - case (INFO, _) => false - case (DEBUG, _) => false + if (level.id <= logLevel.id) { + val colour = if (!ANSIColour) then "" else logLevel match + case DEBUG => AnsiColor.RESET + case INFO => AnsiColor.GREEN + case WARN => AnsiColor.YELLOW + case ERROR => AnsiColor.RED + case OFF => ??? - val position = if showPosition then s" [${name.value}@${file.value}:${line.value}]" else "" + val showPosition = (logLevel, level) match + case (_, DEBUG) => true + case (ERROR, _) => true + case (WARN, _) => true + case (INFO, _) => false + case (DEBUG, _) => false + case (OFF, _) => ??? - val space = " " - val prefix = s"[$colour$logLevel${AnsiColor.RESET}]$space" - val text = arg.toString().replace("\n", "\n " + (" " * (logLevel.toString).length()) + " " + space ) + val position = if showPosition then s" [${name.value}@${file.value}:${line.value}]" else "" - if (level.id <= logLevel.id) { - System.err.println(s"$prefix $text$position") + val resetColour = if !ANSIColour then "" else AnsiColor.RESET + val space = " " + val prefix = s"[$colour$logLevel${resetColour}]$space" + val text = arg.toString().replace("\n", "\n " + (" " * (logLevel.toString).length()) + " " + space) + output.println(s"$prefix $text$position") } } - def warn(arg: Any)(implicit line: sourcecode.Line, file: sourcecode.FileName, name: sourcecode.Name): Unit = { - write(LogLevel.WARN, arg, line, file, name) + def warn(arg: => Any)(implicit line: sourcecode.Line, file: sourcecode.FileName, name: sourcecode.Name): Unit = { + writeLog(LogLevel.WARN, arg, line, file, name) } - def error(arg: Any)(implicit line: sourcecode.Line, file: sourcecode.FileName, name: sourcecode.Name): Unit = { - write(LogLevel.ERROR, arg, line, file, name) + def error(arg: => Any)(implicit line: sourcecode.Line, file: sourcecode.FileName, name: sourcecode.Name): Unit = { + writeLog(LogLevel.ERROR, arg, line, file, name) } - def debug(arg: Any)(implicit line: sourcecode.Line, file: sourcecode.FileName, name: sourcecode.Name): Unit = { - write(LogLevel.DEBUG, arg, line, file, name) + def debug(arg: => Any)(implicit line: sourcecode.Line, file: sourcecode.FileName, name: sourcecode.Name): Unit = { + writeLog(LogLevel.DEBUG, arg, line, file, name) } - def info(arg: Any)(implicit line: sourcecode.Line, file: sourcecode.FileName, name: sourcecode.Name): Unit = { - write(LogLevel.INFO, arg, line, file, name) + def info(arg: => Any)(implicit line: sourcecode.Line, file: sourcecode.FileName, name: sourcecode.Name): Unit = { + writeLog(LogLevel.INFO, arg, line, file, name) } - def setLevel(logLevel: LogLevel): Unit = { + def setLevel(logLevel: LogLevel, setChildren: Boolean = true): Unit = { level = logLevel + if (setChildren) { + for (c <- children) { + c.setLevel(logLevel, setChildren) + } + } } + + def findLoggerByName(s: String) : Option[GenericLogger] = allLoggers.find(_.name == s) + + def allLoggers: Iterable[GenericLogger] = { + (for { + c <- children + cc <- c.allLoggers + } yield (cc)) + ++ Seq(this) + } + +} + +// doesn't work with `mill run` +def isAConsole = System.console() != null + +val Logger = GenericLogger("log", LogLevel.DEBUG, System.out, isAConsole) +val StaticAnalysisLogger = Logger.deriveLogger("analysis", System.out) +val SimplifyLogger = Logger.deriveLogger("simplify", System.out) +val DebugDumpIRLogger = { + val l = Logger.deriveLogger("debugdumpir") + l.setLevel(LogLevel.OFF) + l +} +val VSALogger = StaticAnalysisLogger.deriveLogger("vsa") +val MRALogger = StaticAnalysisLogger.deriveLogger("mra") +val SteensLogger = StaticAnalysisLogger.deriveLogger("steensgaard") + + + diff --git a/src/main/scala/util/PerformanceTimer.scala b/src/main/scala/util/PerformanceTimer.scala index a79b1d3b7..50b106e37 100644 --- a/src/main/scala/util/PerformanceTimer.scala +++ b/src/main/scala/util/PerformanceTimer.scala @@ -12,10 +12,10 @@ case class PerformanceTimer(timerName: String = "") { val delta = elapsed() lastCheckpoint = System.currentTimeMillis() checkpoints.put(name, delta) - Logger.debug(s"PerformanceTimer $timerName [$name]: ${delta}ms") + // Logger.debug(s"PerformanceTimer $timerName [$name]: ${delta}ms") delta } - private def elapsed() : Long = { + def elapsed() : Long = { System.currentTimeMillis() - lastCheckpoint } diff --git a/src/main/scala/util/RunUtils.scala b/src/main/scala/util/RunUtils.scala index 08cb17f53..e17c3bb71 100644 --- a/src/main/scala/util/RunUtils.scala +++ b/src/main/scala/util/RunUtils.scala @@ -5,7 +5,9 @@ import com.grammatech.gtirb.proto.IR.IR import com.grammatech.gtirb.proto.Module.Module import com.grammatech.gtirb.proto.Section.Section import spray.json.* +import ir.eval import gtirb.* +import translating.PrettyPrinter.* import scala.collection.mutable.ListBuffer import scala.collection.mutable.ArrayBuffer @@ -23,7 +25,7 @@ import org.antlr.v4.runtime.tree.ParseTreeWalker import org.antlr.v4.runtime.BailErrorStrategy import org.antlr.v4.runtime.{CharStreams, CommonTokenStream, Token} import translating.* -import util.Logger +import util.{Logger, DebugDumpIRLogger, SimplifyLogger} import java.util.Base64 import spray.json.DefaultJsonProtocol.* @@ -191,7 +193,7 @@ object IRTransform { /** Initial cleanup before analysis. */ def doCleanup(ctx: IRContext): IRContext = { - Logger.debug("[!] Removing external function calls") + Logger.info("[!] Removing external function calls") // Remove external function references (e.g. @printf) val externalNames = ctx.externalFunctions.map(e => e.name) val externalNamesLibRemoved = mutable.Set[String]() @@ -210,6 +212,9 @@ object IRTransform { val externalRemover = ExternalRemover(externalNamesLibRemoved.toSet) externalRemover.visitProgram(ctx.program) + assert(invariant.singleCallBlockEnd(ctx.program)) + assert(invariant.cfgCorrect(ctx.program)) + assert(invariant.blocksUniqueToEachProcedure(ctx.program)) ctx } @@ -221,19 +226,14 @@ object IRTransform { ctx.program.determineRelevantMemory(ctx.globalOffsets) } - Logger.debug("[!] Stripping unreachable") + Logger.info("[!] Stripping unreachable") val before = ctx.program.procedures.size transforms.stripUnreachableFunctions(ctx.program, config.loading.procedureTrimDepth) - Logger.debug( + Logger.info( s"[!] Removed ${before - ctx.program.procedures.size} functions (${ctx.program.procedures.size} remaining)" ) val dupProcNames = ctx.program.procedures.groupBy(_.name).filter((_, p) => p.size > 1).toList.flatMap(_(1)) - - var dupCounter = 0 - for (p <- dupProcNames) { - dupCounter += 1 - p.name = p.name + "$" + p.address.map(_.toString).getOrElse(dupCounter.toString) - } + assert(dupProcNames.isEmpty) if (!config.staticAnalysis.isDefined || (config.staticAnalysis.get.memoryRegions == MemoryRegionsMode.Disabled)) { val stackIdentification = StackSubstituter() @@ -287,16 +287,23 @@ object StaticAnalysis { /** Run all static analysis passes on the provided IRProgram. */ def analyse( - ctx: IRContext, + ictx: IRContext, config: StaticAnalysisConfig, iteration: Int, previousResults: Option[StaticAnalysisContext] = None ): StaticAnalysisContext = { + var ctx = ictx val IRProgram: Program = ctx.program val externalFunctions: Set[ExternalFunction] = ctx.externalFunctions val globals: Set[SpecGlobal] = ctx.globals val globalOffsets: Map[BigInt, BigInt] = ctx.globalOffsets + assert(invariant.singleCallBlockEnd(ctx.program)) + assert(invariant.cfgCorrect(ctx.program)) + assert(invariant.blocksUniqueToEachProcedure(ctx.program)) + assert(invariant.correctCalls(ctx.program)) + + val subroutines = IRProgram.procedures .filter(p => p.address.isDefined) .map(p => p.address.get -> p.name) @@ -304,29 +311,30 @@ object StaticAnalysis { val globalAddresses = globals.map(s => s.address -> s.name).toMap val globalSizes = globals.map(s => s.name -> s.size).toMap val externalAddresses = externalFunctions.map(e => e.offset -> e.name).toMap - Logger.debug("Globals:") - Logger.debug(globalAddresses) - Logger.debug("Global Offsets: ") - Logger.debug(globalOffsets) - Logger.debug("Global Sizes: ") - Logger.debug(globalSizes) - Logger.debug("External: ") - Logger.debug(externalAddresses) - Logger.debug("Subroutine Addresses:") - Logger.debug(subroutines) - + StaticAnalysisLogger.debug("Globals:") + StaticAnalysisLogger.debug(globalAddresses) + StaticAnalysisLogger.debug("Global Offsets: ") + StaticAnalysisLogger.debug(globalOffsets) + StaticAnalysisLogger.debug("Global Sizes: ") + StaticAnalysisLogger.debug(globalSizes) + StaticAnalysisLogger.debug("External: ") + StaticAnalysisLogger.debug(externalAddresses) + StaticAnalysisLogger.debug("Subroutine Addresses:") + StaticAnalysisLogger.debug(subroutines) + + StaticAnalysisLogger.debug("reducible loops") // reducible loops val detector = LoopDetector(IRProgram) val foundLoops = detector.identify_loops() - foundLoops.foreach(l => Logger.debug(s"Loop found: ${l.name}")) + foundLoops.foreach(l => StaticAnalysisLogger.debug(s"Loop found: ${l.name}")) val transformer = LoopTransform(foundLoops) val newLoops = transformer.llvm_transform() - newLoops.foreach(l => Logger.debug(s"Loop found: ${l.name}")) + newLoops.foreach(l => StaticAnalysisLogger.debug(s"Loop found: ${l.name}")) config.analysisDotPath.foreach { s => - writeToFile(dotBlockGraph(IRProgram, IRProgram.map(b => b -> b.toString).toMap), s"${s}_graph-after-reduce-$iteration.dot") - writeToFile(dotBlockGraph(IRProgram, IRProgram.filter(_.isInstanceOf[Block]).map(b => b -> b.toString).toMap), s"${s}_blockgraph-after-reduce-$iteration.dot") + DebugDumpIRLogger.writeToFile(File(s"${s}_graph-after-reduce-$iteration.dot"), dotBlockGraph(IRProgram, IRProgram.map(b => b -> b.toString).toMap)) + DebugDumpIRLogger.writeToFile(File(s"${s}_blockgraph-after-reduce-$iteration.dot"), dotBlockGraph(IRProgram, IRProgram.filter(_.isInstanceOf[Block]).map(b => b -> b.toString).toMap)) } val mergedSubroutines = subroutines ++ externalAddresses @@ -334,23 +342,23 @@ object StaticAnalysis { val domain = computeDomain(IntraProcIRCursor, IRProgram.procedures) val interDomain = computeDomain(InterProcIRCursor, IRProgram.procedures) - Logger.debug("[!] Running ANR") + StaticAnalysisLogger.debug("[!] Running ANR") val ANRSolver = ANRAnalysisSolver(IRProgram) val ANRResult = ANRSolver.analyze() - Logger.debug("[!] Running RNA") + StaticAnalysisLogger.debug("[!] Running RNA") val RNASolver = RNAAnalysisSolver(IRProgram) val RNAResult = RNASolver.analyze() - Logger.debug("[!] Running Inter-procedural Constant Propagation") + StaticAnalysisLogger.debug("[!] Running Inter-procedural Constant Propagation") val interProcConstProp = InterProcConstantPropagation(IRProgram) val interProcConstPropResult: Map[CFGPosition, Map[Variable, FlatElement[BitVecLiteral]]] = interProcConstProp.analyze() config.analysisResultsPath.foreach { s => - writeToFile(printAnalysisResults(IRProgram, interProcConstPropResult), s"${s}OGconstprop$iteration.txt") + DebugDumpIRLogger.writeToFile(File(s"${s}OGconstprop$iteration.txt"), printAnalysisResults(IRProgram, interProcConstPropResult)) } - Logger.debug("[!] Variable dependency summaries") + StaticAnalysisLogger.debug("[!] Variable dependency summaries") val scc = stronglyConnectedComponents(CallGraph, List(IRProgram.mainProcedure)) val specGlobalAddresses = ctx.specification.globals.map(s => s.address -> s.name).toMap val varDepsSummaries = VariableDependencyAnalysis(IRProgram, ctx.specification.globals, specGlobalAddresses, interProcConstPropResult, scc).analyze() @@ -359,21 +367,21 @@ object StaticAnalysis { val intraProcConstPropResult: Map[CFGPosition, Map[Variable, FlatElement[BitVecLiteral]]] = intraProcConstProp.analyze() config.analysisResultsPath.foreach { s => - writeToFile(printAnalysisResults(IRProgram, intraProcConstPropResult), s"${s}_new_ir_constprop$iteration.txt") + DebugDumpIRLogger.writeToFile(File(s"${s}_new_ir_constprop$iteration.txt"), printAnalysisResults(IRProgram, intraProcConstPropResult)) } config.analysisDotPath.foreach { f => val dumpdomain = computeDomain[CFGPosition, CFGPosition](InterProcIRCursor, IRProgram.procedures) - writeToFile(toDot(dumpdomain, InterProcIRCursor, Map.empty), s"${f}_new_ir_intercfg$iteration.dot") + DebugDumpIRLogger.writeToFile(File(s"${f}_new_ir_intercfg$iteration.dot"), toDot(dumpdomain.toSet, InterProcIRCursor, Map.empty)) } val reachingDefinitionsAnalysisSolver = InterprocReachingDefinitionsAnalysisSolver(IRProgram) val reachingDefinitionsAnalysisResults = reachingDefinitionsAnalysisSolver.analyze() config.analysisDotPath.foreach { s => - writeToFile( - toDot(IRProgram, IRProgram.filter(_.isInstanceOf[Command]).map(b => b -> reachingDefinitionsAnalysisResults(b).toString).toMap, true), - s"${s}_reachingDefinitions$iteration.dot" + DebugDumpIRLogger.writeToFile( + File(s"${s}_reachingDefinitions$iteration.dot"), + toDot(IRProgram, IRProgram.filter(_.isInstanceOf[Command]).map(b => b -> reachingDefinitionsAnalysisResults(b).toString).toMap, true) ) } @@ -386,58 +394,58 @@ object StaticAnalysis { Map[CFGPosition, LiftedElement[Map[Variable | MemoryRegion, Set[Value]]]]() } - Logger.debug("[!] Running GRA") + StaticAnalysisLogger.debug("[!] Running GRA") val graSolver = GlobalRegionAnalysisSolver(IRProgram, domain.toSet, interProcConstPropResult, reachingDefinitionsAnalysisResults, mmm, previousVSAResults) val graResult = graSolver.analyze() - Logger.debug("[!] Running MRA") + StaticAnalysisLogger.debug("[!] Running MRA") val mraSolver = MemoryRegionAnalysisSolver(IRProgram, domain.toSet, globalAddresses, globalOffsets, mergedSubroutines, interProcConstPropResult, ANRResult, RNAResult, reachingDefinitionsAnalysisResults, graResult, mmm) val mraResult = mraSolver.analyze() config.analysisDotPath.foreach { s => - writeToFile(dotCallGraph(IRProgram), s"${s}_callgraph$iteration.dot") - writeToFile( - dotBlockGraph(IRProgram, IRProgram.filter(_.isInstanceOf[Block]).map(b => b -> b.toString).toMap), - s"${s}_blockgraph$iteration.dot" + DebugDumpIRLogger.writeToFile(File(s"${s}_callgraph$iteration.dot"), dotCallGraph(IRProgram)) + DebugDumpIRLogger.writeToFile( + File(s"${s}_blockgraph$iteration.dot"), + dotBlockGraph(IRProgram, IRProgram.filter(_.isInstanceOf[Block]).map(b => b -> b.toString).toMap) ) - writeToFile( - toDot(IRProgram, IRProgram.filter(_.isInstanceOf[Command]).map(b => b -> intraProcConstPropResult(b).toString).toMap), - s"${s}_new_ir_constprop$iteration.dot" + DebugDumpIRLogger.writeToFile( + File(s"${s}_new_ir_constprop$iteration.dot"), + toDot(IRProgram, IRProgram.filter(_.isInstanceOf[Command]).map(b => b -> intraProcConstPropResult(b).toString).toMap) ) - writeToFile( - toDot(IRProgram, IRProgram.filter(_.isInstanceOf[Command]).map(b => b -> mraResult(b).toString).toMap), - s"${s}_MRA$iteration.dot" + DebugDumpIRLogger.writeToFile( + File(s"${s}_MRA$iteration.dot"), + toDot(IRProgram, IRProgram.filter(_.isInstanceOf[Command]).map(b => b -> mraResult(b).toString).toMap) ) - writeToFile( - toDot(IRProgram, IRProgram.filter(_.isInstanceOf[Command]).map(b => b -> graResult(b).toString).toMap), - s"${s}_GRA$iteration.dot" + DebugDumpIRLogger.writeToFile( + File(s"${s}_GRA$iteration.dot"), + toDot(IRProgram, IRProgram.filter(_.isInstanceOf[Command]).map(b => b -> graResult(b).toString).toMap) ) } - Logger.debug("[!] Running MMM") + StaticAnalysisLogger.debug("[!] Running MMM") mmm.convertMemoryRegions(mraSolver.procedureToStackRegions, mraSolver.procedureToHeapRegions, mraResult, mraSolver.procedureToSharedRegions, graSolver.getDataMap, graResult) mmm.logRegions() - Logger.debug("[!] Running Steensgaard") + StaticAnalysisLogger.debug("[!] Running Steensgaard") val steensgaardSolver = InterprocSteensgaardAnalysis(interDomain.toSet, mmm, reachingDefinitionsAnalysisResults, previousVSAResults) steensgaardSolver.analyze() val steensgaardResults = steensgaardSolver.pointsTo() - Logger.debug("[!] Running VSA") + StaticAnalysisLogger.debug("[!] Running VSA") val vsaSolver = ValueSetAnalysisSolver(IRProgram, mmm, interProcConstPropResult) val vsaResult: Map[CFGPosition, LiftedElement[Map[Variable | MemoryRegion, Set[Value]]]] = vsaSolver.analyze() config.analysisDotPath.foreach { s => - writeToFile( - toDot(IRProgram, IRProgram.filter(_.isInstanceOf[Command]).map(b => b -> vsaResult(b).toString).toMap), - s"${s}_VSA$iteration.dot" + DebugDumpIRLogger.writeToFile( + File(s"${s}_VSA$iteration.dot"), + toDot(IRProgram, IRProgram.filter(_.isInstanceOf[Command]).map(b => b -> vsaResult(b).toString).toMap) ) } - Logger.debug("[!] Injecting regions") + StaticAnalysisLogger.debug("[!] Injecting regions") val regionInjector = if (config.memoryRegions == MemoryRegionsMode.MRA) { val injector = RegionInjectorMRA(IRProgram, mmm) injector.injectRegions() @@ -515,6 +523,7 @@ object RunUtils { def run(q: BASILConfig): Unit = { val result = loadAndTranslate(q) + Logger.info("Writing output") writeOutput(result) } @@ -527,26 +536,167 @@ object RunUtils { } } - def loadAndTranslate(q: BASILConfig): BASILResult = { - Logger.debug("[!] Loading Program") - val ctx = IRLoading.load(q.loading) + def doSimplify(ctx: IRContext, config: Option[StaticAnalysisConfig]) : Unit = { + + // writeToFile(dotBlockGraph(ctx.program, ctx.program.filter(_.isInstanceOf[Block]).map(b => b -> b.toString).toMap), s"blockgraph-before-simp.dot") + Logger.info("[!] Running Simplify") + val timer = PerformanceTimer("Simplify") + + transforms.applyRPO(ctx.program) + + transforms.removeEmptyBlocks(ctx.program) + transforms.coalesceBlocks(ctx.program) + transforms.removeEmptyBlocks(ctx.program) + + DebugDumpIRLogger.writeToFile(File("blockgraph-before-dsa.dot"), dotBlockGraph(ctx.program.mainProcedure)) + + Logger.info(s"RPO ${timer.checkPoint("RPO")} ms ") + Logger.info("[!] Simplify :: DynamicSingleAssignment") + DebugDumpIRLogger.writeToFile(File("il-before-dsa.il"), serialiseIL(ctx.program)) + + // transforms.DynamicSingleAssignment.applyTransform(ctx.program, liveVars) + transforms.OnePassDSA().applyTransform(ctx.program) + Logger.info(s"DSA ${timer.checkPoint("DSA ")} ms ") + + DebugDumpIRLogger.writeToFile(File(s"blockgraph-after-dsa.dot"), + dotBlockGraph(ctx.program, (ctx.program.collect { + case b : Block => b -> pp_block(b) + }).toMap)) + + if (ir.eval.SimplifyValidation.validate) { + // Logger.info("Live vars difftest") + // val tipLiveVars : Map[CFGPosition, Set[Variable]] = analysis.IntraLiveVarsAnalysis(ctx.program).analyze() + // assert(ctx.program.procedures.forall(transforms.difftestLiveVars(_, tipLiveVars))) + + Logger.info("DSA Check") + val x = ctx.program.procedures.forall(transforms.rdDSAProperty) + assert(x) + Logger.info("DSA Check passed") + } + assert(invariant.singleCallBlockEnd(ctx.program)) + assert(invariant.cfgCorrect(ctx.program)) + assert(invariant.blocksUniqueToEachProcedure(ctx.program)) + + DebugDumpIRLogger.writeToFile(File("il-before-copyprop.il"), pp_prog(ctx.program)) + + // brute force run the analysis twice because it cleans up more stuff + //assert(ctx.program.procedures.forall(transforms.rdDSAProperty)) + transforms.doCopyPropTransform(ctx.program) + DebugDumpIRLogger.writeToFile(File("blockgraph-after-simp.dot"), dotBlockGraph(ctx.program.mainProcedure)) + + // assert(ctx.program.procedures.forall(transforms.rdDSAProperty)) + + assert(invariant.blockUniqueLabels(ctx.program)) + Logger.info(s"CopyProp ${timer.checkPoint("CopyProp")} ms ") + DebugDumpIRLogger.writeToFile(File("il-after-copyprop.il"), pp_prog(ctx.program)) + + + // val x = ctx.program.procedures.forall(transforms.rdDSAProperty) + //assert(x) + if (ir.eval.SimplifyValidation.validate) { + Logger.info("DSA Check (after transform)") + val x = ctx.program.procedures.forall(transforms.rdDSAProperty) + assert(x) + Logger.info("passed") + } + + // run this after cond recovery because sign bit calculations often need high bits + // which go away in high level conss + DebugDumpIRLogger.writeToFile(File("il-after-slices.il"), pp_prog(ctx.program)) + + // re-apply dsa + // transforms.OnePassDSA().applyTransform(ctx.program) + + + if (ir.eval.SimplifyValidation.validate) { + Logger.info("[!] Simplify :: Writing simplification validation") + val w = BufferedWriter(FileWriter("rewrites.smt2")) + ir.eval.SimplifyValidation.makeValidation(w) + w.close() + } + + Logger.info("[!] Simplify :: finished") + } + + def loadAndTranslate(conf: BASILConfig): BASILResult = { + Logger.info("[!] Loading Program") + var q = conf + + var ctx = IRLoading.load(q.loading) + + assert(invariant.singleCallBlockEnd(ctx.program)) + assert(invariant.cfgCorrect(ctx.program)) + assert(invariant.blocksUniqueToEachProcedure(ctx.program)) + + ctx = IRTransform.doCleanup(ctx) + + if (q.loading.trimEarly) { + val before = ctx.program.procedures.size + transforms.stripUnreachableFunctions(ctx.program, q.loading.procedureTrimDepth) + Logger.info( + s"[!] Removed ${before - ctx.program.procedures.size} functions (${ctx.program.procedures.size} remaining)" + ) + } + + if (q.loading.parameterForm) { + ir.transforms.clearParams(ctx.program) + ctx = ir.transforms.liftProcedureCallAbstraction(ctx) + } else { + ir.transforms.clearParams(ctx.program) + } + assert(invariant.correctCalls(ctx.program)) + - IRTransform.doCleanup(ctx) + assert(invariant.singleCallBlockEnd(ctx.program)) + assert(invariant.cfgCorrect(ctx.program)) + assert(invariant.blocksUniqueToEachProcedure(ctx.program)) + assert(invariant.correctCalls(ctx.program)) - q.loading.dumpIL.foreach(s => writeToFile(serialiseIL(ctx.program), s"$s-before-analysis.il")) + q.loading.dumpIL.foreach(s => DebugDumpIRLogger.writeToFile(File(s"$s-before-analysis.il"), pp_prog(ctx.program))) val analysis = q.staticAnalysis.map { conf => staticAnalysis(conf, ctx) } - q.loading.dumpIL.foreach(s => writeToFile(serialiseIL(ctx.program), s"$s-after-analysis.il")) + q.loading.dumpIL.foreach(s => DebugDumpIRLogger.writeToFile(File(s"$s-after-analysis.il"), pp_prog(ctx.program))) + + ir.eval.SimplifyValidation.validate = conf.validateSimp + if (conf.simplify) { + + if (!q.loading.parameterForm) { + ir.transforms.clearParams(ctx.program) + ctx = ir.transforms.liftProcedureCallAbstraction(ctx) + } + + doSimplify(ctx, conf.staticAnalysis) + } if (q.runInterpret) { - val interpreter = Interpreter() - interpreter.interpret(ctx.program) + Logger.info("Start interpret") + val fs = eval.interpretTrace(ctx) + + val stdout = fs._1.memoryState.getMem("stdout").toList.sortBy(_._1.value).map(_._2.value.toChar).mkString("") + + Logger.info(s"Interpreter stdout:\n${stdout}") + + q.loading.dumpIL.foreach(f => { + val tf = f"${f}-interpret-trace.txt" + writeToFile((fs._2.t.mkString("\n")), tf) + Logger.info(s"Finished interpret: trace written to $tf") + }) + + val stopState = fs._1.nextCmd + if (stopState != eval.Stopped()) { + Logger.error(s"Interpreter exited with $stopState") + } else { + Logger.info("Interpreter stopped normally.") + } } IRTransform.prepareForTranslation(q, ctx) - Logger.debug("[!] Translating to Boogie") + q.loading.dumpIL.foreach(s => + writeToFile(pp_prog(ctx.program), s"$s-output.il") + ) + Logger.info("[!] Translating to Boogie") val regionInjector = analysis.flatMap(a => a.regionInjector) @@ -564,6 +714,7 @@ object RunUtils { } assert(invariant.singleCallBlockEnd(ctx.program)) + BASILResult(ctx, analysis, boogiePrograms) } @@ -574,10 +725,10 @@ object RunUtils { var modified: Boolean = true val analysisResult = mutable.ArrayBuffer[StaticAnalysisContext]() while (modified || (analysisResult.size < 2 && config.memoryRegions == MemoryRegionsMode.MRA)) { - Logger.debug("[!] Running Static Analysis") + StaticAnalysisLogger.info("[!] Running Static Analysis") val result = StaticAnalysis.analyse(ctx, config, iteration, analysisResult.lastOption) analysisResult.append(result) - Logger.debug("[!] Replacing Indirect Calls") + StaticAnalysisLogger.info("[!] Replacing Indirect Calls") /* modified = transforms.SteensgaardIndirectCallResolution( @@ -593,14 +744,14 @@ object RunUtils { result.mmmResults ).resolveIndirectCalls() - Logger.debug("[!] Generating Procedure Summaries") + StaticAnalysisLogger.info("[!] Generating Procedure Summaries") if (config.summariseProcedures) { IRTransform.generateProcedureSummaries(ctx, ctx.program, result.intraProcConstProp, result.varDepsSummaries) } if (modified) { iteration += 1 - Logger.debug(s"[!] Analysing again (iter $iteration)") + StaticAnalysisLogger.info(s"[!] Analysing again (iter $iteration)") } } @@ -610,29 +761,29 @@ object RunUtils { transforms.splitThreads(ctx.program, analysisResult.last.steensgaardResults, analysisResult.last.reachingDefs) } - Logger.debug("[!] Running Writes To") + StaticAnalysisLogger.info("[!] Running Writes To") val writesTo = WriteToAnalysis(ctx.program).analyze() val reachingDefs = ReachingDefsAnalysis(ctx.program, writesTo).analyze() config.analysisDotPath.foreach { s => - writeToFile(toDot(ctx.program), s"${s}_ct.dot") + DebugDumpIRLogger.writeToFile(File(s"${s}_ct.dot"), toDot(ctx.program)) } - Logger.debug("[!] Running Symbolic Access Analysis") + StaticAnalysisLogger.info("[!] Running Symbolic Access Analysis") val symResults: Map[CFGPosition, Map[SymbolicAddress, TwoElement]] = SymbolicAddressAnalysis(ctx.program, analysisResult.last.interProcConstProp).analyze() config.analysisDotPath.foreach { s => val labels = symResults.map { (k, v) => k -> v.toString } - writeToFile(toDot(ctx.program, labels), s"${s}_saa.dot") + DebugDumpIRLogger.writeToFile(File(s"${s}_saa.dot"), toDot(ctx.program, labels)) } - Logger.debug("[!] Running DSA Analysis") + StaticAnalysisLogger.info("[!] Running DSA Analysis") val symbolTableEntries: Set[SymbolTableEntry] = ctx.globals ++ ctx.funcEntries val dsa = DataStructureAnalysis(ctx.program, symResults, analysisResult.last.interProcConstProp, symbolTableEntries, ctx.globalOffsets, ctx.externalFunctions, reachingDefs, writesTo, analysisResult.last.paramResults) dsa.analyze() config.analysisDotPath.foreach { s => dsa.topDown(ctx.program.mainProcedure).toDot - writeToFile(dsa.topDown(ctx.program.mainProcedure).toDot, s"${s}_main_dsg.dot") + DebugDumpIRLogger.writeToFile(File(s"${s}_main_dsg.dot"), dsa.topDown(ctx.program.mainProcedure).toDot) } val regionInjector = if (config.memoryRegions == MemoryRegionsMode.DSA) { @@ -644,7 +795,7 @@ object RunUtils { } assert(invariant.singleCallBlockEnd(ctx.program)) - Logger.debug(s"[!] Finished indirect call resolution after $iteration iterations") + StaticAnalysisLogger.info(s"[!] Finished indirect call resolution after $iteration iterations") analysisResult.last.copy( symbolicAddresses = symResults, localDSA = dsa.local.toMap, @@ -656,6 +807,7 @@ object RunUtils { } def writeToFile(content: String, fileName: String): Unit = { + Logger.debug(s"Writing $fileName (${content.size} bytes)") val outFile = File(fileName) val pw = PrintWriter(outFile, "UTF-8") pw.write(content) diff --git a/src/main/scala/util/functional/State.scala b/src/main/scala/util/functional/State.scala new file mode 100644 index 000000000..07306a81f --- /dev/null +++ b/src/main/scala/util/functional/State.scala @@ -0,0 +1,127 @@ +package util.functional +import util.Logger +import sourcecode.Line, sourcecode.FileName +import java.io.* + +val monlog = Logger.deriveLogger("statemonad", PrintStream(File("monad"))) + +/* + * Flattened state monad with error. + */ +case class State[S, A, E](f: S => (S, Either[E, A])) { + + def unit[A](a: A): State[S, A, E] = State(s => (s, Right(a))) + + def >>(o: State[S,A,E]) = for { + _ <- this + x <- o + } yield (x) + + + def flatMap[B](f: A => State[S, B, E])(implicit line: sourcecode.Line, file: sourcecode.FileName, name: sourcecode.Name): State[S, B, E] = State(s => { + monlog.debug(s"State.flatMap ${file.value}:${line.value}") + val (s2, a) = this.f(s) + val r = a match { + case Left(l) => (s2, Left(l)) + case Right(a) => f(a).f(s2) + } + r + }) + + + def map[B](f: A => B): State[S, B, E] = { + State(s => { + val (s2, a) = this.f(s) + a match { + case Left(l) => (s2, Left(l)) + case Right(a) => (s2, Right(f(a))) + } + }) + } + + def flatMapE(f: E => State[S, A, E]): State[S, A, E] = { + State(s => { + val (s2, a) = this.f(s) + a match { + case Left(l) => f(l).f(s2) + case Right(_) => (s2, a) + } + }) + } +} + + +object State { + def get[S, A, E](f: S => A) : State[S, A, E] = State(s => (s, Right(f(s)))) + def getE[S, A, E](f: S => Either[E,A]) : State[S, A, E] = State(s => (s, f(s))) + def getS[S,E] : State[S,S,E] = State((s:S) => (s,Right(s))) + def putS[S,E](s: S) : State[S,Unit,E] = State((_) => (s,Right(()))) + def modify[S, E](f: S => S) : State[S, Unit, E] = State(s => (f(s), Right(()))) + def modifyE[S, E](f: S => Either[E, S]) : State[S, Unit, E] = State(s => f(s) match { + case Right(ns) => (ns, Right(())) + case Left(e) => (s, Left(e)) + }) + def execute[S, A, E](s: S, c: State[S,A, E])(implicit line: sourcecode.Line, file: sourcecode.FileName, name: sourcecode.Name) : S = { + Logger.debug(s"state.execute")(line, file, name) + c.f(s) match { + case (s, Left(r)) => { + monlog.info(s"ERROR: $r") + s + } + case (s, _) => s + } + } + def evaluate[S, A, E](s: S, c: State[S,A, E]) : Either[E,A] = c.f(s)._2 + + def setError[S,A,E](e: E) : State[S,A,E] = State(s => (s, Left(e))) + + def pure[S, A, E](a: A) : State[S, A, E] = State((s:S) => (s, Right(a))) + def pureE[S, A, E](a: Either[E, A])(implicit line: sourcecode.Line, file: sourcecode.FileName, name: sourcecode.Name) : State[S, A, E] = { + a match { + case Left(l) => monlog.info(s"pureE error $l")(line, file, name) + case _ => () + } + State((s:S) => (s, a)) + } + + def sequence[S, V, E](ident: State[S,V, E], xs: Iterable[State[S,V, E]]) : State[S, V, E] = { + xs.foldRight(ident)((l,r) => for { + x <- l + y <- r + } yield(y)) + } + + def filterM[A, S, E](m : (A => State[S, Boolean, E]), xs: Iterable[A]): State[S, List[A], E] = { + xs.foldRight(pure(List[A]()))((b,acc) => acc.flatMap(c => m(b).map(v => if v then b::c else c))) + } + + def mapM[A, B, S, E](m : (A => State[S, B, E]), xs: Iterable[A])(implicit line: sourcecode.Line, file: sourcecode.FileName, name: sourcecode.Name): State[S, List[B], E] = { + monlog.debug(s"State.mapM (${xs.size} items) ${file.value}:${line.value}") + xs.foldRight(pure(List[B]()))((b,acc) => acc.flatMap(c => m(b).map(v => v::c))) + } + + def protect[S, V, E](f : () => State[S, V, E], fnly: PartialFunction[Exception, E]) : State[S, V, E] = { + State((s: S) => try { + f().f(s) + } catch { + case e: Exception if fnly.isDefinedAt(e) => (s, Left(fnly(e))) + }) + } + + def protectPure[S,V,E](f : () => V, fnly : PartialFunction[Exception, E]) : State[S, V, E] = { + State((s: S) => try { + (s, Right(f())) + } catch { + case e: Exception if fnly.isDefinedAt(e) => (s, Left(fnly(e))) + }) + } + +} + +def protect[T](x: () => T, fnly: PartialFunction[Exception, T]): T = { + try { + x() + } catch { + case e: Exception if fnly.isDefinedAt(e) => fnly(e) + } +} diff --git a/src/main/scala/util/intrusive_list/IntrusiveList.scala b/src/main/scala/util/intrusive_list/IntrusiveList.scala index 9ba9e14e5..3a79be03e 100644 --- a/src/main/scala/util/intrusive_list/IntrusiveList.scala +++ b/src/main/scala/util/intrusive_list/IntrusiveList.scala @@ -192,6 +192,16 @@ final class IntrusiveList[T <: IntrusiveListElement[T]] private ( newElem } + def prependAll(elems: Iterable[T]) = { + // first == None ==> empty list + insertAllBefore(firstElem, elems) + } + + def appendAll(elems: Iterable[T]) = { + // last == None ==> empty list + insertAllAfter(lastElem, elems) + } + /** * Add an element to the end of the list. * The element must not be a member of any other IntrusiveList. diff --git a/src/main/scala/util/z3/z3process.scala b/src/main/scala/util/z3/z3process.scala new file mode 100644 index 000000000..cda2f97ad --- /dev/null +++ b/src/main/scala/util/z3/z3process.scala @@ -0,0 +1,23 @@ +package util.z3 +import scala.sys.process.* +import java.io.ByteArrayInputStream +import util.Logger + +enum SatResult { + case SAT + case UNSAT + case Unknown(s: String) +} + + +def checkSATSMT2(smt: String, softTimeoutMillis: Option[Int] = None) : SatResult = { + val cmd = Seq("z3", "-smt2", "-in") ++ (if (softTimeoutMillis.isDefined) then Seq(s"-t:${softTimeoutMillis.get}") else Seq()) + val output = (cmd #< ByteArrayInputStream(smt.getBytes("UTF-8"))).!! + if (output == "sat\n") { + SatResult.SAT + } else if (output == "unsat\n") { + SatResult.UNSAT + } else { + SatResult.Unknown(output) + } +} diff --git a/src/test/correct/basic_arrays_write/basic_arrays_write.spec b/src/test/correct/basic_arrays_write/basic_arrays_write.spec index cfe441557..6e181ea4b 100644 --- a/src/test/correct/basic_arrays_write/basic_arrays_write.spec +++ b/src/test/correct/basic_arrays_write/basic_arrays_write.spec @@ -6,4 +6,4 @@ Rely: true Guarantee: old(arr[0]) == arr[0] Subroutine: main -Requires: Gamma_main_argc == false \ No newline at end of file +Requires: Gamma_R0 == false diff --git a/src/test/correct/basic_function_call_caller/basic_function_call_caller.spec b/src/test/correct/basic_function_call_caller/basic_function_call_caller.spec index 16bedaaa2..b7b91e75e 100644 --- a/src/test/correct/basic_function_call_caller/basic_function_call_caller.spec +++ b/src/test/correct/basic_function_call_caller/basic_function_call_caller.spec @@ -7,7 +7,7 @@ Rely: x == old(x), y == old(y) Guarantee: old(x) == 0bv32 ==> x == 0bv32, old(Gamma_y) ==> x == 0bv32 || Gamma_y Subroutine: main -Requires: Gamma_main_argc == false +Requires: Gamma_R0 == false Subroutine: zero -Ensures: zero_result == 0bv32 && Gamma_zero_result \ No newline at end of file +Ensures: zero_result == 0bv32 && Gamma_R0 diff --git a/src/test/correct/basic_lock_security_write/basic_lock_security_write.spec b/src/test/correct/basic_lock_security_write/basic_lock_security_write.spec index 5804dfbcc..f7515ed10 100644 --- a/src/test/correct/basic_lock_security_write/basic_lock_security_write.spec +++ b/src/test/correct/basic_lock_security_write/basic_lock_security_write.spec @@ -8,4 +8,4 @@ Guarantee: old(z) == 0bv32 ==> x == old(x) && z == old(z) Subroutine: main Requires: z != 0bv32 -Requires: Gamma_main_argc == false \ No newline at end of file +Requires: Gamma_R0 == false diff --git a/src/test/correct/basic_sec_policy_write/basic_sec_policy_write.spec b/src/test/correct/basic_sec_policy_write/basic_sec_policy_write.spec index a66060980..3780fc89a 100644 --- a/src/test/correct/basic_sec_policy_write/basic_sec_policy_write.spec +++ b/src/test/correct/basic_sec_policy_write/basic_sec_policy_write.spec @@ -7,4 +7,4 @@ Rely: old(z) == z Guarantee: old(z) != 0bv32 ==> z != 0bv32 Subroutine: main -Requires: Gamma_main_argc == false \ No newline at end of file +Requires: Gamma_R0 == false diff --git a/src/test/correct/functionpointer/functionpointer.spec b/src/test/correct/functionpointer/functionpointer.spec index 84cc2d8c6..f49fabf7f 100644 --- a/src/test/correct/functionpointer/functionpointer.spec +++ b/src/test/correct/functionpointer/functionpointer.spec @@ -1,2 +1,2 @@ Subroutine: main -Requires: Gamma_main_argc == true \ No newline at end of file +Requires: Gamma_R0 == true diff --git a/src/test/correct/ifbranches/ifbranches.spec b/src/test/correct/ifbranches/ifbranches.spec index 84cc2d8c6..f49fabf7f 100644 --- a/src/test/correct/ifbranches/ifbranches.spec +++ b/src/test/correct/ifbranches/ifbranches.spec @@ -1,2 +1,2 @@ Subroutine: main -Requires: Gamma_main_argc == true \ No newline at end of file +Requires: Gamma_R0 == true diff --git a/src/test/indirect_calls/functionpointer/functionpointer.spec b/src/test/indirect_calls/functionpointer/functionpointer.spec index 84cc2d8c6..f49fabf7f 100644 --- a/src/test/indirect_calls/functionpointer/functionpointer.spec +++ b/src/test/indirect_calls/functionpointer/functionpointer.spec @@ -1,2 +1,2 @@ Subroutine: main -Requires: Gamma_main_argc == true \ No newline at end of file +Requires: Gamma_R0 == true diff --git a/src/test/indirect_calls/jumptable3/jumptable3.spec b/src/test/indirect_calls/jumptable3/jumptable3.spec index 84cc2d8c6..f49fabf7f 100644 --- a/src/test/indirect_calls/jumptable3/jumptable3.spec +++ b/src/test/indirect_calls/jumptable3/jumptable3.spec @@ -1,2 +1,2 @@ Subroutine: main -Requires: Gamma_main_argc == true \ No newline at end of file +Requires: Gamma_R0 == true diff --git a/src/test/indirect_calls/switch2/switch2.spec b/src/test/indirect_calls/switch2/switch2.spec index 84cc2d8c6..f49fabf7f 100644 --- a/src/test/indirect_calls/switch2/switch2.spec +++ b/src/test/indirect_calls/switch2/switch2.spec @@ -1,2 +1,2 @@ Subroutine: main -Requires: Gamma_main_argc == true \ No newline at end of file +Requires: Gamma_R0 == true diff --git a/src/test/make/lift-directories.mk b/src/test/make/lift-directories.mk index a113f2a91..cbb55a300 100644 --- a/src/test/make/lift-directories.mk +++ b/src/test/make/lift-directories.mk @@ -8,11 +8,11 @@ NAME=$(notdir $(shell pwd)) GIT_ROOT?=$(realpath ../../../../) +#CFLAGS=-fno-pic -fno-plt +TARGET=aarch64-linux-gnu GCC ?= aarch64-linux-gnu-gcc CLANG ?= clang-15 -target $(TARGET) CC ?= $(GCC) -#CFLAGS=-fno-pic -fno-plt -TARGET=aarch64-linux-gnu BAP?=bap READELF ?= aarch64-linux-gnu-readelf diff --git a/src/test/readme.md b/src/test/readme.md deleted file mode 100644 index 5d86cc94b..000000000 --- a/src/test/readme.md +++ /dev/null @@ -1,34 +0,0 @@ -## Writing and Running Tests - -See [../../docs/development/readme.md] - -## Lifting SystemTest examples - -Lifting options specified in make/lift-directories.mk can be overriden using this file, for example to specify the lifting templates: - -``` -$ cat correct/test_name/config.mk -ENABLED_COMPILERS = clang clang_pic gcc gcc_pic -``` - -To force recompile and lift all: - -``` -make cleanall -make -``` - -Lift one: - -``` - # relative to correct/secret_write -make -C correct/secret_write -f ../../make/lift-directories.mk -``` - -or - -``` -cd correct/secret_write -make -f ../../make/lift-directories.mk -``` - diff --git a/src/test/scala/BitVectorAnalysisTests.scala b/src/test/scala/BitVectorAnalysisTests.scala index 485ba6a83..08710a940 100644 --- a/src/test/scala/BitVectorAnalysisTests.scala +++ b/src/test/scala/BitVectorAnalysisTests.scala @@ -1,4 +1,4 @@ -import analysis.BitVectorEval.* +import ir.eval.BitVectorEval._ import ir.* import org.scalatest.funsuite.AnyFunSuite import util.Logger @@ -181,20 +181,20 @@ class BitVectorAnalysisTests extends AnyFunSuite { // smt_bveq test("BitVector Equal - should return true if two BitVectors are equal") { val result = smt_bveq(BitVecLiteral(255, 8), BitVecLiteral(255, 8)) - assert(result == TrueLiteral) + assert(result) } test("BitVector Equal - should return false if two BitVectors are not equal") { val result = smt_bveq(BitVecLiteral(255, 8), BitVecLiteral(254, 8)) - assert(result == FalseLiteral) + assert(!result) } // smt_bvneq test("BitVector Not Equal - should return false if two BitVectors are equal") { val result = smt_bvneq(BitVecLiteral(255, 8), BitVecLiteral(255, 8)) - assert(result == FalseLiteral) + assert(!result) } test("BitVector Not Equal - should return true if two BitVectors are not equal") { val result = smt_bvneq(BitVecLiteral(255, 8), BitVecLiteral(254, 8)) - assert(result == TrueLiteral) + assert(result) } // smt_bvshl test("BitVector Shift Left - should shift bits left") { @@ -239,14 +239,14 @@ class BitVectorAnalysisTests extends AnyFunSuite { test("BitVector unsigned less then - should return true if first argument is less than second argument") { val result = smt_bvult(BitVecLiteral(254, 8), BitVecLiteral(255, 8)) - assert(result == TrueLiteral) + assert(result) } test( "BitVector unsigned less then - should return false if first argument is greater than or equal to second argument" ) { val result = smt_bvult(BitVecLiteral(255, 8), BitVecLiteral(254, 8)) - assert(result == FalseLiteral) + assert(!result) } // smt_bvule @@ -255,14 +255,14 @@ class BitVectorAnalysisTests extends AnyFunSuite { "BitVector unsigned less then or equal to - should return true if first argument is less equal to second argument" ) { val result = smt_bvule(BitVecLiteral(254, 8), BitVecLiteral(255, 8)) - assert(result == TrueLiteral) + assert(result) } test( "BitVector unsigned less then or equal to - should return false if first argument is greater than second argument" ) { val result = smt_bvule(BitVecLiteral(255, 8), BitVecLiteral(254, 8)) - assert(result == FalseLiteral) + assert(!result) } // smt_bvugt @@ -270,14 +270,14 @@ class BitVectorAnalysisTests extends AnyFunSuite { "BitVector unsinged greater than - should return true if first argument is greater equal to than second argument" ) { val result = smt_bvugt(BitVecLiteral(255, 8), BitVecLiteral(254, 8)) - assert(result == TrueLiteral) + assert(result) } test( "BitVector unsinged greater than - should return false if first argument is less than or equal to second argument" ) { val result = smt_bvugt(BitVecLiteral(254, 8), BitVecLiteral(255, 8)) - assert(result == FalseLiteral) + assert(!result) } // smt_bvuge @@ -285,27 +285,27 @@ class BitVectorAnalysisTests extends AnyFunSuite { "BitVector unsinged greater than or equal to - should return true if first argument is greater equal or equal to second argument" ) { val result = smt_bvuge(BitVecLiteral(255, 8), BitVecLiteral(254, 8)) - assert(result == TrueLiteral) + assert(result) } test( "BitVector unsinged greater than or equal to - should return false if first argument is less than second argument" ) { val result = smt_bvuge(BitVecLiteral(254, 8), BitVecLiteral(255, 8)) - assert(result == FalseLiteral) + assert(!result) } // smt_bvslt test("BitVector signed less than - should return true if first argument is less than second argument") { val result = smt_bvslt(BitVecLiteral(254, 8), BitVecLiteral(255, 8)) - assert(result == TrueLiteral) + assert(result) } test( "BitVector signed less than - should return false if first argument is greater than or equal to second argument" ) { val result = smt_bvslt(BitVecLiteral(254, 8), BitVecLiteral(254, 8)) - assert(result == FalseLiteral) + assert(!result) } // smt_bvsle @@ -313,25 +313,25 @@ class BitVectorAnalysisTests extends AnyFunSuite { "BitVector signed less than or equal to - should return true if first argument is less than or equal to second argument" ) { val result = smt_bvsle(BitVecLiteral(254, 8), BitVecLiteral(255, 8)) - assert(result == TrueLiteral) + assert(result) } test( "BitVector signed less than or equal to - should return false if first argument is greater than second argument" ) { val result = smt_bvsle(BitVecLiteral(255, 8), BitVecLiteral(254, 8)) - assert(result == FalseLiteral) + assert(!result) } // smt_bvsgt test("BitVector signed greater than - should return true if first argument is greater than second argument") { val result = smt_bvsgt(BitVecLiteral(255, 8), BitVecLiteral(254, 8)) - assert(result == TrueLiteral) + assert(result) } test("BitVector signed greater than - should return false if first argument is less than second argument") { val result = smt_bvsgt(BitVecLiteral(254, 8), BitVecLiteral(255, 8)) - assert(result == FalseLiteral) + assert(!result) } // smt_bvsge @@ -339,14 +339,14 @@ class BitVectorAnalysisTests extends AnyFunSuite { "BitVector signed greater than or equal to - should return true if first argument is greater than or equal to second argument" ) { val result = smt_bvsge(BitVecLiteral(255, 8), BitVecLiteral(254, 8)) - assert(result == TrueLiteral) + assert(result) } test( "BitVector signed greater than or equal to - should return false if first argument is less than second argument" ) { val result = smt_bvsge(BitVecLiteral(254, 8), BitVecLiteral(255, 8)) - assert(result == FalseLiteral) + assert(!result) } // smt_bvashr test("BitVector Arithmetic shift right - should return shift right a positive number") { diff --git a/src/test/scala/DataStructureAnalysisTest.scala b/src/test/scala/DataStructureAnalysisTest.scala index 04ca1dd8c..d52510ae4 100644 --- a/src/test/scala/DataStructureAnalysisTest.scala +++ b/src/test/scala/DataStructureAnalysisTest.scala @@ -2,7 +2,8 @@ import analysis.data_structure_analysis.* import ir.* import org.scalatest.funsuite.AnyFunSuite import ir.dsl.* -import specification.{Specification, SpecGlobal} +import specification.Specification +import boogie.SpecGlobal import util.{BASILConfig, BASILResult, BoogieGeneratorConfig, ILLoadingConfig, IRContext, RunUtils, StaticAnalysisConfig, StaticAnalysisContext, writeToFile} /** diff --git a/src/test/scala/DifferentialAnalysisTest.scala b/src/test/scala/DifferentialAnalysisTest.scala new file mode 100644 index 000000000..59e591a42 --- /dev/null +++ b/src/test/scala/DifferentialAnalysisTest.scala @@ -0,0 +1,163 @@ + +import ir.* +import ir.eval._ +import java.io.{BufferedWriter, File, FileWriter} +import ir.Endian.LittleEndian +import org.scalatest.* +import org.scalatest.funsuite.* +import specification.* +import util.{BASILConfig, IRLoading, ILLoadingConfig, IRContext, RunUtils, StaticAnalysis, StaticAnalysisConfig, StaticAnalysisContext, BASILResult, Logger, LogLevel, IRTransform} +import ir.eval.{interpretTrace, interpret, ExecEffect, Stopped} +import test_util.* + + +import java.io.IOException +import java.nio.file.* +import java.nio.file.attribute.BasicFileAttributes +import ir.dsl.* +import util.RunUtils.loadAndTranslate + +import scala.collection.mutable + +class DifferentialTest extends AnyFunSuite { + + Logger.setLevel(LogLevel.WARN) + + def diffTest(initial: IRContext, transformed: IRContext) = { + + val instructionLimit = 1000000 + + def interp(p: IRContext) : (InterpreterState, Trace) = { + val interpreter = LayerInterpreter(tracingInterpreter(NormalInterpreter), EffectsRLimit(instructionLimit)) + val initialState = InterpFuns.initProgState(NormalInterpreter)(p, InterpreterState()) + //Logger.setLevel(LogLevel.DEBUG) + val r = BASILInterpreter(interpreter).run((initialState, Trace(List())), 0)._1 + //Logger.setLevel(LogLevel.WARN) + r + } + + val (initialRes,traceInit) = interp(initial) + val (result,traceRes) = interp(transformed) + + def filterEvents(trace: List[ExecEffect]) = { + trace.collect { + case e @ ExecEffect.Call(_, _, _) => e + case e @ ExecEffect.StoreMem("mem", _) => e + case e @ ExecEffect.LoadMem("mem", _) => e + } + } + + Logger.info(traceInit.t.map(_.toString.take(80)).mkString("\n")) + val initstdout = initialRes.memoryState.getMem("stdout").toList.sortBy(_._1.value).map(_._2.value.toChar).mkString("") + val comparstdout = result.memoryState.getMem("stdout").toList.sortBy(_._1.value).map(_._2.value.toChar).mkString("") + info("STDOUT: \"" + initstdout + "\"") + // Logger.info(initialRes.memoryState.getMem("stderr").toList.sortBy(_._1.value).map(_._2).mkString("")) + assert(initstdout == comparstdout) + assert(initialRes.nextCmd == Stopped()) + assert(result.nextCmd == Stopped()) + assert(Set.empty == initialRes.memoryState.getMem("mem").toSet.diff(result.memoryState.getMem("mem").toSet)) + assert(traceInit.t.nonEmpty) + assert(traceRes.t.nonEmpty) + assert(filterEvents(traceInit.t).mkString("\n") == filterEvents(traceRes.t).mkString("\n")) + } + + def testProgram(testName: String, examplePath: String, suffix: String =".adt", staticAnalysisConfig : StaticAnalysisConfig = StaticAnalysisConfig(None, None, None), simplify: Boolean = false) = { + + val loading = ILLoadingConfig(inputFile = examplePath + testName + suffix, + relfFile = examplePath + testName + ".relf", + dumpIL = None, + ) + + var ictx = IRLoading.load(loading) + ictx = IRTransform.doCleanup(ictx) + + var comparectx = IRLoading.load(loading) + comparectx = IRTransform.doCleanup(comparectx) + + ir.transforms.clearParams(ictx.program) + + ir.transforms.clearParams(comparectx.program) + + val analysisres = RunUtils.staticAnalysis(staticAnalysisConfig, comparectx) + + if (simplify) { + ictx = ir.transforms.liftProcedureCallAbstraction(ictx) + comparectx = ir.transforms.liftProcedureCallAbstraction(comparectx) + RunUtils.doSimplify(ictx, Some(staticAnalysisConfig)) + } + + + diffTest(ictx, comparectx) + } +} + + +class DifferentialAnalysisTest extends DifferentialTest { + + def runSystemTests(): Unit = { + + val path = System.getProperty("user.dir") + s"/src/test/correct/" + val programs: Array[String] = getSubdirectories(path) + + // get all variations of each program + for (p <- programs) { + val programPath = path + "/" + p + val variations = getSubdirectories(programPath) + variations.foreach(variation => { + val bapPath = path + "/" + p + "/" + variation + "/" + p + ".adt" + val gtirbPath = path + "/" + p + "/" + variation + "/" + p + ".gts" + + if (File(bapPath).exists) { + test("analysis_differential:" + p + "/" + variation + ":BAP") { + testProgram(p, path + "/" + p + "/" + variation + "/", suffix=".adt") + } + } + if (File(gtirbPath).exists) { + test("analysis_differential:" + p + "/" + variation + ":GTIRB") { + testProgram(p, path + "/" + p + "/" + variation + "/", suffix=".gts") + } + } + + } + ) + } + } + + + runSystemTests() +} + +class DifferentialAnalysisTestSimplification extends DifferentialTest { + + def runSystemTests(): Unit = { + + val path = System.getProperty("user.dir") + s"/src/test/correct/" + val programs: Array[String] = getSubdirectories(path) + + // get all variations of each program + for (p <- programs) { + val programPath = path + "/" + p + val variations = getSubdirectories(programPath) + variations.foreach(variation => { + + val bapPath = path + "/" + p + "/" + variation + "/" + p + ".adt" + val gtirbPath = path + "/" + p + "/" + variation + "/" + p + ".gts" + if (File(bapPath).exists) { + test("analysis_differential:" + p + "/" + variation + ":BAP") { + testProgram(p, path + "/" + p + "/" + variation + "/", suffix=".adt") + } + } + if (File(gtirbPath).exists) { + test("analysis_differential:" + p + "/" + variation + ":GTIRB") { + testProgram(p, path + "/" + p + "/" + variation + "/", suffix=".gts") + } + } + + } + ) + } + } + runSystemTests() +} + + diff --git a/src/test/scala/IndirectCallTests.scala b/src/test/scala/IndirectCallTests.scala index 02d0e614b..10679efe6 100644 --- a/src/test/scala/IndirectCallTests.scala +++ b/src/test/scala/IndirectCallTests.scala @@ -171,7 +171,7 @@ class IndirectCallTests extends AnyFunSuite, BASILTest { if (targets.size == resolution.procTargets.size) { val targetNames = targets.flatMap { _.statements.lastElem match { - case Some(DirectCall(target, _)) => Some(target.name) + case Some(DirectCall(target, _, _, _)) => Some(target.name) case _ => None } } @@ -416,4 +416,4 @@ class IndirectCallTests extends AnyFunSuite, BASILTest { runTest("switch2", "clang", GTIRBConfig, resolvedCalls) } -} \ No newline at end of file +} diff --git a/src/test/scala/InterpretTestConstProp.scala b/src/test/scala/InterpretTestConstProp.scala new file mode 100644 index 000000000..7385e6d9a --- /dev/null +++ b/src/test/scala/InterpretTestConstProp.scala @@ -0,0 +1,137 @@ +import ir.* +import ir.eval.* +import analysis.* +import java.io.{BufferedWriter, File, FileWriter} +import ir.Endian.LittleEndian +import org.scalatest.* +import org.scalatest.funsuite.* +import specification.* +import util.{BASILConfig, IRLoading, ILLoadingConfig, IRContext, RunUtils, StaticAnalysis, StaticAnalysisConfig, StaticAnalysisContext, BASILResult, Logger, LogLevel, IRTransform} +import ir.eval.{interpretTrace, interpret, ExecEffect, Stopped} +import ir.dsl + + +import java.io.IOException +import java.nio.file.* +import java.nio.file.attribute.BasicFileAttributes +import ir.dsl.* +import util.RunUtils.loadAndTranslate + +import scala.collection.mutable + +class ConstPropInterpreterValidate extends AnyFunSuite { + + Logger.setLevel(LogLevel.ERROR) + + def testInterpretConstProp(testName: String, examplePath: String) = { + val loading = ILLoadingConfig(inputFile = examplePath + testName + ".adt", + relfFile = examplePath + testName + ".relf", + dumpIL = None, + ) + + var ictx = IRLoading.load(loading) + ictx = IRTransform.doCleanup(ictx) + val analysisres = RunUtils.staticAnalysis(StaticAnalysisConfig(None, None, None), ictx) + + val breaks : List[BreakPoint] = analysisres.intraProcConstProp.collect { + // convert analysis result to a list of breakpoints, each which evaluates an expression describing + // the invariant inferred by the analysis (the assignment of registers) at a corresponding program point + + case (command: Command, v) => { + val expectedPredicates : List[(String, Expr)] = v.toList.map(r => { + val (variable, value) = r + val assertion = value match { + case Top => TrueLiteral + case Bottom => FalseLiteral /* unreachable */ + case FlatEl(value) => BinaryExpr(BVEQ, variable, value) + } + (variable.name, assertion) + }) + BreakPoint(location=BreakPointLoc.CMD(command), BreakPointAction(saveState=false,evalExprs=expectedPredicates)) + } + }.toList + + assert(breaks.nonEmpty) + + // run the interpreter evaluating the analysis result at each command with a breakpoint + val interpretResult = interpretWithBreakPoints(ictx, breaks.toList, NormalInterpreter, InterpreterState()) + val breakres : List[(BreakPoint, _, List[(String, Expr, Expr)])] = interpretResult._2 + assert(interpretResult._1.nextCmd == Stopped()) + assert(breakres.nonEmpty) + + // assert all the collected breakpoint watches have evaluated to true + for (b <- breakres) { + val (_, _, evaluatedexprs) = b + evaluatedexprs.forall(c => { + val (n, before, evaled) = c + evaled == TrueLiteral + }) + } + } + + test("indirect_call_example") { + val testName = "indirect_call" + val examplePath = System.getProperty("user.dir") + s"/examples/$testName/" + testInterpretConstProp(testName, examplePath) + } + + test("indirect_call_gcc_example") { + val testName = "indirect_call" + val examplePath = System.getProperty("user.dir") + s"/src/test/correct/$testName/gcc/" + testInterpretConstProp(testName, examplePath) + } + + test("indirect_call_clang_example") { + val testName = "indirect_call" + val examplePath = System.getProperty("user.dir") + s"/src/test/correct/$testName/clang/" + testInterpretConstProp(testName, examplePath) + } + + test("jumptable2_example") { + val testName = "jumptable2" + val examplePath = System.getProperty("user.dir") + s"/examples/$testName/" + testInterpretConstProp(testName, examplePath) + } + + test("jumptable2_gcc_example") { + val testName = "jumptable2" + val examplePath = System.getProperty("user.dir") + s"/src/test/correct/$testName/gcc/" + testInterpretConstProp(testName, examplePath) + } + + test("jumptable2_clang_example") { + val testName = "jumptable2" + val examplePath = System.getProperty("user.dir") + s"/src/test/correct/$testName/clang/" + testInterpretConstProp(testName, examplePath) + } + + test("functionpointer_example") { + val testName = "functionpointer" + val examplePath = System.getProperty("user.dir") + s"/examples/$testName/" + testInterpretConstProp(testName, examplePath) + } + + test("functionpointer_gcc_example") { + val testName = "functionpointer" + val examplePath = System.getProperty("user.dir") + s"/src/test/correct/$testName/gcc/" + testInterpretConstProp(testName, examplePath) + } + + test("functionpointer_clang_example") { + val testName = "functionpointer" + val examplePath = System.getProperty("user.dir") + s"/src/test/correct/$testName/clang/" + testInterpretConstProp(testName, examplePath) + } + + test("secret_write_clang") { + val testName = "secret_write" + val examplePath = System.getProperty("user.dir") + s"/src/test/correct/$testName/clang/" + testInterpretConstProp(testName, examplePath) + } + + test("secret_write_gcc") { + val testName = "secret_write" + val examplePath = System.getProperty("user.dir") + s"/src/test/correct/$testName/gcc/" + testInterpretConstProp(testName, examplePath) + } +} diff --git a/src/test/scala/LiveVarsAnalysisTests.scala b/src/test/scala/LiveVarsAnalysisTests.scala index bd7a5ed8e..c3da7e297 100644 --- a/src/test/scala/LiveVarsAnalysisTests.scala +++ b/src/test/scala/LiveVarsAnalysisTests.scala @@ -6,9 +6,10 @@ import org.scalatest.funsuite.AnyFunSuite import test_util.BASILTest import util.{BASILResult, StaticAnalysisConfig} + class LiveVarsAnalysisTests extends AnyFunSuite, BASILTest { + Logger.setLevel(LogLevel.ERROR) private val correctPath = "./src/test/correct/" - def runExample(name: String): BASILResult = { val inputFile = correctPath + s"/$name/gcc/$name.adt" val relfFile = correctPath + s"/$name/gcc/$name.relf" diff --git a/src/test/scala/PointsToTest.scala b/src/test/scala/PointsToTest.scala index 4b267d7e8..7c39c303b 100644 --- a/src/test/scala/PointsToTest.scala +++ b/src/test/scala/PointsToTest.scala @@ -3,6 +3,7 @@ import ir.Endian.LittleEndian import org.scalatest.* import org.scalatest.funsuite.* import specification.* +import boogie.* import util.{RunUtils, StaticAnalysisConfig, StaticAnalysis, StaticAnalysisContext, IRContext} import java.io.IOException @@ -275,4 +276,4 @@ class PointsToTest extends AnyFunSuite with OneInstancePerTest { // // runSteensgaardAnalysis(program, globals = globals, globalOffsets = globalOffsets) // } -} \ No newline at end of file +} diff --git a/src/test/scala/SATTest.scala b/src/test/scala/SATTest.scala new file mode 100644 index 000000000..ff4a727b0 --- /dev/null +++ b/src/test/scala/SATTest.scala @@ -0,0 +1,18 @@ +import analysis.ParamAnalysis +import ir.dsl.* +import ir.* +import org.scalatest.funsuite.AnyFunSuite +import test_util.BASILTest +import util.* +import translating.BasilIRToSMT2 + + + +class SATTest extends AnyFunSuite { + test(" basic taut ") { + Logger.setLevel(LogLevel.DEBUG) + val e = BinaryExpr(BoolEQ, BinaryExpr(BVNEQ, R0, bv64(0)), BinaryExpr(BVEQ, bv64(0), R0)) + val r = BasilIRToSMT2.proveExpr(e) + assert(r == Some(true)) + } +} diff --git a/src/test/scala/SystemTests.scala b/src/test/scala/SystemTests.scala index 45004391d..5a9798d0d 100644 --- a/src/test/scala/SystemTests.scala +++ b/src/test/scala/SystemTests.scala @@ -137,7 +137,7 @@ trait SystemTests extends AnyFunSuite, BASILTest { Logger.info(s"$name/$variation$testSuffix") val timer = PerformanceTimer(s"test $name/$variation$testSuffix") - runBASIL(inputPath, RELFPath, Some(specPath), BPLPath, conf.staticAnalysisConfig) + runBASIL(inputPath, RELFPath, Some(specPath), BPLPath, conf.staticAnalysisConfig, conf.simplify) val translateTime = timer.checkPoint("translate-boogie") Logger.info(s"$name/$variation$testSuffix DONE") @@ -203,6 +203,27 @@ class ExtraSpecTests extends SystemTests { } } + +class NoSimplifySystemTests extends SystemTests { + runTests("correct", TestConfig(simplify=false, useBAPFrontend = true, expectVerify = true, logResults = true)) + runTests("incorrect", TestConfig(simplify=false, useBAPFrontend = true, expectVerify = false, logResults = true)) + runTests("correct", TestConfig(simplify=false, useBAPFrontend = false, expectVerify = true, logResults = true)) + runTests("incorrect", TestConfig(simplify=false, useBAPFrontend = false, expectVerify = false, logResults = true)) + test("summary-nosimplify") { + summary("nosimplify") + } +} +class SimplifySystemTests extends SystemTests { + runTests("correct", TestConfig(simplify=true, useBAPFrontend = true, expectVerify = true, logResults = true)) + runTests("incorrect", TestConfig(simplify=true, useBAPFrontend = true, expectVerify = false, logResults = true)) + runTests("correct", TestConfig(simplify=true, useBAPFrontend = false, expectVerify = true, logResults = true)) + runTests("incorrect", TestConfig(simplify=true, useBAPFrontend = false, expectVerify = false, logResults = true)) + test("summary-simplify") { + summary("simplify") + } +} + + class AnalysisSystemTestsBAP extends SystemTests { runTests("correct", TestConfig(staticAnalysisConfig = Some(StaticAnalysisConfig()), useBAPFrontend = true, expectVerify = true)) runTests("incorrect", TestConfig(staticAnalysisConfig = Some(StaticAnalysisConfig()), useBAPFrontend = true, expectVerify = false)) @@ -245,4 +266,4 @@ class ProcedureSummaryTests extends SystemTests { class UnimplementedTests extends SystemTests { runTests("unimplemented", TestConfig(useBAPFrontend = false, expectVerify = true)) runTests("unimplemented", TestConfig(useBAPFrontend = true, expectVerify = false)) -} \ No newline at end of file +} diff --git a/src/test/scala/TaintAnalysisTests.scala b/src/test/scala/TaintAnalysisTests.scala index 2ffeb3215..774a49381 100644 --- a/src/test/scala/TaintAnalysisTests.scala +++ b/src/test/scala/TaintAnalysisTests.scala @@ -126,7 +126,7 @@ class TaintAnalysisTests extends AnyFunSuite, BASILTest { val varDepResults = getVarDepResults(program, f) - assert(varDepResults.get(IRWalk.lastInProc(f).get).contains(baseRegisterMap + (R0 -> Set(R1, R2)))) + assert(varDepResults.get(IRWalk.lastInProc(f).get) == Some(baseRegisterMap + (R0 -> Set(R1, R2)))) } test("interproc") { diff --git a/src/test/scala/ir/CILVisitorTest.scala b/src/test/scala/ir/CILVisitorTest.scala index 49eae1cb5..200cd3e78 100644 --- a/src/test/scala/ir/CILVisitorTest.scala +++ b/src/test/scala/ir/CILVisitorTest.scala @@ -10,7 +10,11 @@ import ir.cilvisitor.* class FindVars extends CILVisitor { val vars = mutable.ArrayBuffer[Variable]() - override def vvar(v: Variable) = { + override def vrvar(v: Variable) = { + vars.append(v) + SkipChildren() + } + override def vlvar(v: Variable) = { vars.append(v) SkipChildren() } @@ -95,7 +99,14 @@ class CILVisitorTest extends AnyFunSuite { class ExprTrace extends CILVisitor { val res = mutable.ArrayBuffer[String]() - override def vvar(e: Variable) = { + override def vlvar(e: Variable) = { + e match { + case Register(n, _) => res.append(n); + case _ => ??? // only reg in source program + } + DoChildren() + } + override def vrvar(e: Variable) = { e match { case Register(n, _) => res.append(n); case _ => ??? // only reg in source program @@ -135,12 +146,19 @@ class CILVisitorTest extends AnyFunSuite { class VarTrace extends CILVisitor { val res = mutable.ArrayBuffer[String]() - override def vvar(e: Variable) = { res.append(e.name); SkipChildren() } + override def vrvar(e: Variable) = { res.append(e.name); SkipChildren() } + override def vlvar(e: Variable) = { res.append(e.name); SkipChildren() } } class RegReplace extends CILVisitor { - override def vvar(e: Variable) = { + override def vrvar(e: Variable) = { + e match { + case Register(n, _) => ChangeTo(LocalVar("l" + n, e.getType)); + case _ => DoChildren() + } + } + override def vlvar(e: Variable) = { e match { case Register(n, _) => ChangeTo(LocalVar("l" + n, e.getType)); case _ => DoChildren() @@ -151,8 +169,15 @@ class CILVisitorTest extends AnyFunSuite { class RegReplacePost extends CILVisitor { val res = mutable.ArrayBuffer[String]() + override def vlvar(e: Variable) = { + e match { + case LocalVar(n, _) => + ChangeDoChildrenPost(LocalVar("e" + n, e.getType), e => { res.append(e.name); e }); + case _ => DoChildren() + } + } - override def vvar(e: Variable) = { + override def vrvar(e: Variable) = { e match { case LocalVar(n, _) => ChangeDoChildrenPost(LocalVar("e" + n, e.getType), e => { res.append(e.name); e }); diff --git a/src/test/scala/ir/IRTest.scala b/src/test/scala/ir/IRTest.scala index f156a83c0..069fa914e 100644 --- a/src/test/scala/ir/IRTest.scala +++ b/src/test/scala/ir/IRTest.scala @@ -131,13 +131,12 @@ class IRTest extends AnyFunSuite { val aftercallGotos = p.collect { case c: Command if isAfterCall(c) => c }.toSet - // assert(aftercallGotos == Set(blocks("l_main_1").fallthrough.get)) assert(1 == aftercallGotos.count(b => IntraProcIRCursor.pred(b).contains(blocks("l_main_1").jump))) assert(1 == aftercallGotos.count(b => IntraProcIRCursor.succ(b).contains(blocks("l_main_1").jump match { case GoTo(targets, _) => targets.head + case _ => throw Exception("unreachable") }))) - } test("addblocks") { @@ -195,7 +194,7 @@ class IRTest extends AnyFunSuite { LocalAssign(R0, bv64(22)), LocalAssign(R0, bv64(22)), directCall("main"), - unreachable + unreachable ).resolve(p) val b2 = block("newblock1", LocalAssign(R0, bv64(22)), @@ -304,6 +303,10 @@ class IRTest extends AnyFunSuite { transforms.addReturnBlocks(p) cilvisitor.visit_prog(transforms.ConvertSingleReturn(), p) + cilvisitor.visit_prog(transforms.ReplaceReturns(), p) + transforms.addReturnBlocks(p) + cilvisitor.visit_prog(transforms.ConvertSingleReturn(), p) + val blocks = p.labelToBlock val procs = p.nameToProcedure @@ -311,10 +314,10 @@ class IRTest extends AnyFunSuite { val prev = InterProcIRCursor.pred(blocks("returntarget")) assert(prev.size == 1 && prev.collect { - case c: GoTo => (c.parent == blocks("l_main")) + case c : GoTo => (c.parent == p.labelToBlock("l_main")) }.contains(true)) - // assert(next == Set(procs("p1"), blocks("l_main").fallthrough.get)) + // assert(next == Set(p.procs("p1"), p.labelToBlock("l_main").fallthrough.get)) val prevB: Command = (blocks("l_main").statements.lastOption match case Some(c: IndirectCall) => c.returnTarget diff --git a/src/test/scala/ir/InterpreterTests.scala b/src/test/scala/ir/InterpreterTests.scala index 92f728ee8..41e8a28f5 100644 --- a/src/test/scala/ir/InterpreterTests.scala +++ b/src/test/scala/ir/InterpreterTests.scala @@ -1,45 +1,69 @@ package ir -import analysis.BitVectorEval.* +import util.PerformanceTimer +import util.functional._ +import ir.eval._ +import boogie.Scope +import ir.dsl._ import org.scalatest.funsuite.AnyFunSuite import org.scalatest.BeforeAndAfter -import specification.SpecGlobal +import boogie.SpecGlobal import translating.BAPToIR import util.{LogLevel, Logger} import util.IRLoading.{loadBAP, loadReadELF} -import util.ILLoadingConfig +import util.{ILLoadingConfig, IRContext, IRLoading, IRTransform} -class InterpreterTests extends AnyFunSuite with BeforeAndAfter { +def load(s: InterpreterState, global: SpecGlobal): Option[BitVecLiteral] = { + val f = NormalInterpreter + + State.evaluate( + s, + Eval.loadBV(f)("mem", Scalar(BitVecLiteral(global.address, 64)), Endian.LittleEndian, global.size) + ) match { + case Right(e) => Some(e) + case Left(e) => { + None + } + } +} - var i: Interpreter = Interpreter() - Logger.setLevel(LogLevel.DEBUG) +def mems[E, T <: Effects[T, E]](m: MemoryState): Map[BigInt, BitVecLiteral] = { + m.getMem("mem").map((k, v) => k.value -> v) +} + +class InterpreterTests extends AnyFunSuite with BeforeAndAfter { - def getProgram(name: String): (Program, Set[SpecGlobal]) = { + Logger.setLevel(LogLevel.WARN) + def getProgram(name: String): IRContext = { + val compiler = "gcc" val loading = ILLoadingConfig( - inputFile = s"src/test/correct/$name/gcc/$name.adt", - relfFile = s"src/test/correct/$name/gcc/$name.relf", + inputFile = s"src/test/correct/$name/$compiler/$name.adt", + relfFile = s"src/test/correct/$name/$compiler/$name.relf", specFile = None, dumpIL = None ) - val bapProgram = loadBAP(loading.inputFile) - val (_, externalFunctions, globals, _, _, mainAddress) = loadReadELF(loading.relfFile, loading) - val IRTranslator = BAPToIR(bapProgram, mainAddress) - var IRProgram = IRTranslator.translate - IRProgram = ExternalRemover(externalFunctions.map(e => e.name)).visitProgram(IRProgram) - IRProgram = Renamer(Set("free")).visitProgram(IRProgram) - transforms.stripUnreachableFunctions(IRProgram) - val stackIdentification = StackSubstituter() - stackIdentification.visitProgram(IRProgram) - IRProgram.setModifies(Map()) - - (IRProgram, globals) + val p = IRLoading.load(loading) + val ctx = IRTransform.doCleanup(p) + // val bapProgram = loadBAP(loading.inputFile) + // val (symbols, externalFunctions, globals, _, mainAddress) = loadReadELF(loading.relfFile, loading) + // val IRTranslator = BAPToIR(bapProgram, mainAddress) + // var IRProgram = IRTranslator.translate + // IRProgram = ExternalRemover(externalFunctions.map(e => e.name)).visitProgram(IRProgram) + // IRProgram = Renamer(Set("free")).visitProgram(IRProgram) + //IRProgram.stripUnreachableFunctions() + // val stackIdentification = StackSubstituter() + // stackIdentification.visitProgram(IRProgram) + ctx.program.setModifies(Map()) + ctx } def testInterpret(name: String, expected: Map[String, Int]): Unit = { - val (program, globals) = getProgram(name) - val regs = i.interpret(program) + val ctx = getProgram(name) + val fstate = interpret(ctx) + val regs = fstate.memoryState.getGlobalVals + val globals = ctx.globals // Show interpreted result Logger.info("Registers:") @@ -48,69 +72,64 @@ class InterpreterTests extends AnyFunSuite with BeforeAndAfter { } Logger.info("Globals:") + // def loadBV(vname: String, addr: BasilValue, valueSize: Int, endian: Endian, size: Int): List[BitVecLiteral] = { globals.foreach { global => - val mem = i.getMemory(global.address.toInt, global.size, Endian.LittleEndian, i.mems) - Logger.info(s"$global := $mem") + val mem = load(fstate, global) + mem.foreach(mem => Logger.info(s"$global := $mem")) } // Test expected value - expected.foreach { (name, expected) => - globals.find(_.name == name) match { - case Some(global) => - val actual = i.getMemory(global.address.toInt, global.size, Endian.LittleEndian, i.mems).value.toInt - assert(actual == expected) - case None => assert("None" == name) - } - } + val actual: Map[String, Int] = expected.flatMap((name, expected) => + globals.find(_.name == name).flatMap(global => load(fstate, global).map(gv => name -> gv.value.toInt)) + ) + assert(fstate.nextCmd == Stopped()) + assert(expected == actual) } - before { - i = Interpreter() - } + test("initialise") { - test("getMemory in LittleEndian") { - i.mems(0) = BitVecLiteral(BigInt("0D", 16), 8) - i.mems(1) = BitVecLiteral(BigInt("0C", 16), 8) - i.mems(2) = BitVecLiteral(BigInt("0B", 16), 8) - i.mems(3) = BitVecLiteral(BigInt("0A", 16), 8) - val expected: BitVecLiteral = BitVecLiteral(BigInt("0A0B0C0D", 16), 32) - val actual: BitVecLiteral = i.getMemory(0, 32, Endian.LittleEndian, i.mems) - assert(actual == expected) - } + val init = InterpFuns.initialState(NormalInterpreter) + + val s = State.execute(InterpreterState(), init) + assert(s.memoryState.getVarOpt("mem").isDefined) + assert(s.memoryState.getVarOpt("stack").isDefined) + assert(s.memoryState.getVarOpt("R31").isDefined) + assert(s.memoryState.getVarOpt("R29").isDefined) - test("getMemory in BigEndian") { - i.mems(0) = BitVecLiteral(BigInt("0A", 16), 8) - i.mems(1) = BitVecLiteral(BigInt("0B", 16), 8) - i.mems(2) = BitVecLiteral(BigInt("0C", 16), 8) - i.mems(3) = BitVecLiteral(BigInt("0D", 16), 8) - val expected: BitVecLiteral = BitVecLiteral(BigInt("0A0B0C0D", 16), 32) - val actual: BitVecLiteral = i.getMemory(0, 32, Endian.BigEndian, i.mems) - assert(actual == expected) } - test("setMemory in LittleEndian") { - i.mems(0) = BitVecLiteral(BigInt("FF", 16), 8) - i.mems(1) = BitVecLiteral(BigInt("FF", 16), 8) - i.mems(2) = BitVecLiteral(BigInt("FF", 16), 8) - i.mems(3) = BitVecLiteral(BigInt("FF", 16), 8) - val expected: BitVecLiteral = BitVecLiteral(BigInt("0A0B0C0D", 16), 32) - i.setMemory(0, 32, Endian.LittleEndian, expected, i.mems) - val actual: BitVecLiteral = i.getMemory(0, 32, Endian.LittleEndian, i.mems) - assert(actual == expected) + test("var load store") { + val s = for { + s <- InterpFuns.initialState(NormalInterpreter) + v <- NormalInterpreter.storeVar("R1", Scope.Global, Scalar(BitVecLiteral(1024, 64))) + v <- NormalInterpreter.loadVar("R1") + } yield (v) + val l = State.evaluate(InterpreterState(), s) + + assert(l == Right(Scalar(BitVecLiteral(1024, 64)))) } - test("setMemory in BigEndian") { - i.mems(0) = BitVecLiteral(BigInt("FF", 16), 8) - i.mems(1) = BitVecLiteral(BigInt("FF", 16), 8) - i.mems(2) = BitVecLiteral(BigInt("FF", 16), 8) - i.mems(3) = BitVecLiteral(BigInt("FF", 16), 8) - val expected: BitVecLiteral = BitVecLiteral(BigInt("0A0B0C0D", 16), 32) - i.setMemory(0, 32, Endian.BigEndian, expected, i.mems) - val actual: BitVecLiteral = i.getMemory(0, 32, Endian.BigEndian, i.mems) - assert(actual == expected) + test("Store = Load LittleEndian") { + val ts = List( + BitVecLiteral(BigInt("0D", 16), 8), + BitVecLiteral(BigInt("0C", 16), 8), + BitVecLiteral(BigInt("0B", 16), 8), + BitVecLiteral(BigInt("0A", 16), 8) + ) + + val loader = StVarLoader(NormalInterpreter) + + val s = for { + _ <- InterpFuns.initialState(NormalInterpreter) + _ <- Eval.store(NormalInterpreter)("mem", Scalar(BitVecLiteral(0, 64)), ts.map(Scalar(_)), Endian.LittleEndian) + r <- Eval.loadBV(NormalInterpreter)("mem", Scalar(BitVecLiteral(0, 64)), Endian.LittleEndian, 32) + } yield (r) + val expected: BitVecLiteral = BitVecLiteral(BigInt("0D0C0B0A", 16), 32) + val actual = State.evaluate(InterpreterState(), s) + assert(actual == Right(expected)) + } - /* test("basic_arrays_read") { val expected = Map( "arr" -> 0 @@ -132,21 +151,6 @@ class InterpreterTests extends AnyFunSuite with BeforeAndAfter { testInterpret("basic_assign_increment", expected) } - test("basic_loop_loop") { - val expected = Map( - "x" -> 10 - ) - testInterpret("basic_loop_loop", expected) - } - - test("basicassign") { - val expected = Map( - "x" -> 0, - "z" -> 0, - "secret" -> 0 - ) - testInterpret("basicassign", expected) - } test("function") { val expected = Map( @@ -173,6 +177,11 @@ class InterpreterTests extends AnyFunSuite with BeforeAndAfter { testInterpret("secret_write", expected) } + test("indirect_call") { + val expected = Map[String, Int]() + testInterpret("indirect_call", expected) + } + test("ifglobal") { val expected = Map( "x" -> 1 @@ -188,6 +197,17 @@ class InterpreterTests extends AnyFunSuite with BeforeAndAfter { testInterpret("cjump", expected) } + test("initialisation") { + + // Logger.setLevel(LogLevel.WARN) + val expected = Map( + "x" -> 6, + "y" -> ('b'.toInt) + ) + + testInterpret("initialisation", expected) + } + test("no_interference_update_x") { val expected = Map( "x" -> 1 @@ -201,5 +221,127 @@ class InterpreterTests extends AnyFunSuite with BeforeAndAfter { ) testInterpret("no_interference_update_y", expected) } - */ + + def fib(n: Int): Int = { + n match { + case 0 => 0 + case 1 => 1 + case n => fib(n - 1) + fib(n - 2) + } + } + + def fibonacciProg(n: Int) = { + prog( + proc( + "begin", + block("entry", LocalAssign(R8, Register("R31", 64)), LocalAssign(R0, bv64(n)), directCall("fib"), goto("done")), + block("done", Assert(BinaryExpr(BVEQ, R0, bv64(fib(n)))), ret) + ), + proc( + "fib", + block("base", goto("base1", "base2", "dofib")), + block("base1", Assume(BinaryExpr(BVEQ, R0, bv64(0))), ret), + block("base2", Assume(BinaryExpr(BVEQ, R0, bv64(1))), ret), + block( + "dofib", + Assume(BinaryExpr(BoolAND, BinaryExpr(BVNEQ, R0, bv64(0)), BinaryExpr(BVNEQ, R0, bv64(1)))), + // R8 stack pointer preserved across calls + LocalAssign(R7, BinaryExpr(BVADD, R8, bv64(8))), + MemoryStore(stack, R7, R8, Endian.LittleEndian, 64), // sp + LocalAssign(R8, R7), + LocalAssign(R8, BinaryExpr(BVADD, R8, bv64(8))), // sp + 8 + MemoryStore(stack, R8, R0, Endian.LittleEndian, 64), // [sp + 8] = arg0 + LocalAssign(R0, BinaryExpr(BVSUB, R0, bv64(1))), + directCall("fib"), + LocalAssign(R2, R8), // sp + 8 + LocalAssign(R8, BinaryExpr(BVADD, R8, bv64(8))), // sp + 16 + MemoryStore(stack, R8, R0, Endian.LittleEndian, 64), // [sp + 16] = r1 + MemoryLoad(R0, stack, R2, Endian.LittleEndian, 64), // [sp + 8] + LocalAssign(R0, BinaryExpr(BVSUB, R0, bv64(2))), + directCall("fib"), + MemoryLoad(R2, stack, R8, Endian.LittleEndian, 64), // [sp + 16] (r1) + LocalAssign(R0, BinaryExpr(BVADD, R0, R2)), + MemoryLoad(R8, stack, BinaryExpr(BVSUB, R8, bv64(16)), Endian.LittleEndian, 64), + ret + ) + ) + ) + } + + test("fibonacci") { + + Logger.setLevel(LogLevel.ERROR) + val fib = fibonacciProg(8) + val r = interpret(fib) + assert(r.nextCmd == Stopped()) + // Show interpreted result + // r.regs.foreach { (key, value) => + // Logger.info(s"$key := $value") + // } + + } + + test("fibonaccistress") { + + Logger.setLevel(LogLevel.ERROR) + var res = List[(Int, Double, Double, Int)]() + + for (i <- 0 to 20) { + val prog = fibonacciProg(i) + + val t = PerformanceTimer("native") + val r = fib(i) + val native = t.elapsed() + + val intt = PerformanceTimer("interp") + val ir = interpretRLimit(prog, 100000000) + val it = intt.elapsed() + + res = (i, native.toDouble, it.toDouble, ir._2) :: res + + } + + info(("fibonacci runtime table:\nFibNumber,ScalaRunTime,interpreterRunTime,instructionCycleCount" :: (res.map(x => s"${x._1},${x._2},${x._3},${x._4}"))).mkString("\n")) + + } + + test("fibonacci Trace") { + + val fib = fibonacciProg(8) + + val r = interpretTrace(fib) + + assert(r._1.nextCmd == Stopped()) + // Show interpreted result + // + + } + + test("fib breakpoints") { + + Logger.setLevel(LogLevel.INFO) + val fib = fibonacciProg(8) + val watch = IRWalk.firstInProc((fib.procedures.find(_.name == "fib")).get).get + val bp = BreakPoint( + "Fibentry", + BreakPointLoc.CMDCond(watch, BinaryExpr(BVEQ, BitVecLiteral(5, 64), Register("R0", 64))), + BreakPointAction(true, true, List(("R0", Register("R0", 64))), true) + ) + val bp2 = BreakPoint("Fibentry", BreakPointLoc.CMD(watch), BreakPointAction(true, true , List(("R0", Register("R0", 64))), true)) + val res = interpretWithBreakPoints(fib, List(bp), NormalInterpreter, InterpreterState()) + assert(res._1.nextCmd.isInstanceOf[ErrorStop]) + assert(res._2.nonEmpty) + } + + test("Capture IllegalArg") { + + val tp = prog( + proc("begin", block("shouldfail", LocalAssign(R0, ZeroExtend(-1, BitVecLiteral(0, 64))), ret)) + ) + + val ir = interpret(tp) + assert(ir.nextCmd.isInstanceOf[ErrorStop]) + + } + } diff --git a/src/test/scala/test_util/BASILTest.scala b/src/test/scala/test_util/BASILTest.scala index b9003ade4..2885399fb 100644 --- a/src/test/scala/test_util/BASILTest.scala +++ b/src/test/scala/test_util/BASILTest.scala @@ -13,11 +13,12 @@ case class TestConfig(boogieFlags: Seq[String] = Seq("/timeLimit:10", "/useArray useBAPFrontend: Boolean, expectVerify: Boolean, checkExpected: Boolean = false, - logResults: Boolean = false + logResults: Boolean = false, + simplify: Boolean = false, ) trait BASILTest { - def runBASIL(inputPath: String, RELFPath: String, specPath: Option[String], BPLPath: String, staticAnalysisConf: Option[StaticAnalysisConfig]): BASILResult = { + def runBASIL(inputPath: String, RELFPath: String, specPath: Option[String], BPLPath: String, staticAnalysisConf: Option[StaticAnalysisConfig], simplify: Boolean=false): BASILResult = { val specFile = if (specPath.isDefined && File(specPath.get).exists) { specPath } else { @@ -27,10 +28,13 @@ trait BASILTest { loading = ILLoadingConfig( inputFile = inputPath, relfFile = RELFPath, - specFile = specFile + specFile = specFile, + parameterForm = simplify, ), + simplify = simplify, staticAnalysis = staticAnalysisConf, - outputPrefix = BPLPath + boogieTranslation = util.BoogieGeneratorConfig().copy(memoryFunctionType=util.BoogieMemoryAccessMode.LambdaStoreSelect), + outputPrefix = BPLPath, ) val result = RunUtils.loadAndTranslate(config) RunUtils.writeOutput(result) diff --git a/src/test/scala/test_util/TestUtil.scala b/src/test/scala/test_util/TestUtil.scala new file mode 100644 index 000000000..abb243ab0 --- /dev/null +++ b/src/test/scala/test_util/TestUtil.scala @@ -0,0 +1,64 @@ +package test_util +import java.io.{BufferedWriter, File, FileWriter} + +import ir.{Block, Procedure, Program} +import util.{BASILConfig, BASILResult, BoogieGeneratorConfig, ILLoadingConfig, RunUtils, StaticAnalysisConfig} + +import java.io.File + +trait TestUtil { + val correctPath = "./src/test/correct/" + val correctPrograms: Array[String] = getSubdirectories(correctPath) + val incorrectPath = "./src/test/incorrect/" + val incorrectPrograms: Array[String] = getSubdirectories(incorrectPath) + extension (p: Program) { + def procs: Map[String, Procedure] = p.collect { + case b: Procedure => b.name -> b + }.toMap + + def blocks: Map[String, Block] = p.collect { + case b: Block => b.label -> b + }.toMap + } + + def getSubdirectories(directoryName: String): Array[String] = { + File(directoryName).listFiles.filter(_.isDirectory).map(_.getName) + } + + def runExample(name: String, path: String = correctPath, variation: String = "gcc/"): BASILResult = { + RunUtils.loadAndTranslate( + BASILConfig( + loading = ILLoadingConfig( + inputFile = path + s"/$name/$variation$name.adt", + relfFile = path + s"/$name/$variation$name.relf", + specFile = None, + dumpIL = None + ), + staticAnalysis = Some(StaticAnalysisConfig(None)), + boogieTranslation = BoogieGeneratorConfig(), + outputPrefix = "boogie_out", + ) + ) + } +} + + +/** @param directoryName + * of the parent directory + * @return + * the names all subdirectories of the given parent directory + */ +def getSubdirectories(directoryName: String): Array[String] = { + Option(File(directoryName).listFiles(_.isDirectory)) match { + case None => throw java.io.IOException(s"failed to read directory '$directoryName'") + case Some(subdirs) => subdirs.map(_.getName) + } +} + +def log(text: String, path: String): Unit = { + val writer = BufferedWriter(FileWriter(path, false)) + writer.write(text) + writer.flush() + writer.close() +} + diff --git a/src/test/scala/util/StateMonad.scala b/src/test/scala/util/StateMonad.scala new file mode 100644 index 000000000..3fb882600 --- /dev/null +++ b/src/test/scala/util/StateMonad.scala @@ -0,0 +1,29 @@ +import ir._ +import util.functional._ + + +import ir.eval._ +import ir.dsl._ +import org.scalatest.funsuite.AnyFunSuite +import org.scalatest.BeforeAndAfter +import translating.BAPToIR +import util.{LogLevel, Logger} +import util.IRLoading.{loadBAP, loadReadELF} +import util.ILLoadingConfig + +def add: State[Int, Unit, Unit] = State(s => (s+1, Right(()))) + +class StateMonadTest extends AnyFunSuite { + + test("forcompre") { + val s = for { + _ <- add + _ <- add + _ <- add + } yield () + + + val res = State.execute(0, s) + assert(res == 3) + } +}