在Most efficient way to represent memory buffers in Z3關於如何使嵌套存儲操作更高效的問題被回答爲可以用選擇替換(嵌套)存儲操作,如(assert (= (select A i1) v1))
。但是,我需要商店操作,因爲之前的約束必須用新的約束來替換。嵌套存儲操作的有效方式
例如:下面的約束模擬以下組件的程序:
mov qword ptr [rax], rbx
mov rcx, qword ptr [rax]
我喜歡證明RBX和RCX是相等的,我斷言(= RBX 2 RCX 2!)並期望該模型可以滿足。這工作完美。我斷言(不是(= RBX!2 RCX!2))並且期望模型不能被滿足。當我將以下約束提供給Z3時(例如here),它會給出幾乎即時的答案:UNSAT。但是,如果我在C#程序中證明了同樣的問題(見下文),則無法推斷UNSAT(在合理的時間內)。
問題:我該如何儘快使C#程序與SMT2.0程序一樣快?
(declare-fun RAX!0() (_ BitVec 64))
(declare-fun RAX!1() (_ BitVec 64))
(declare-fun RAX!2() (_ BitVec 64))
(declare-fun RBX!0() (_ BitVec 64))
(declare-fun RBX!1() (_ BitVec 64))
(declare-fun RBX!2() (_ BitVec 64))
(declare-fun RCX!0() (_ BitVec 64))
(declare-fun RCX!1() (_ BitVec 64))
(declare-fun RCX!2() (_ BitVec 64))
(declare-fun MEM!0() (Array (_ BitVec 64) (_ BitVec 8)))
(declare-fun MEM!1() (Array (_ BitVec 64) (_ BitVec 8)))
(declare-fun MEM!2() (Array (_ BitVec 64) (_ BitVec 8)))
(assert (= RAX!1 RAX!0))
(assert (= RBX!1 RBX!0))
(assert (= RCX!1 RCX!0))
(assert (let ((a!1 (store (store (store MEM!0 RAX!0 ((_ extract 7 0) RBX!0))
(bvadd #x0000000000000001 RAX!0)
((_ extract 15 8) RBX!0))
(bvadd #x0000000000000002 RAX!0)
((_ extract 23 16) RBX!0))))
(let ((a!2 (store (store (store a!1
(bvadd #x0000000000000003 RAX!0)
((_ extract 31 24) RBX!0))
(bvadd #x0000000000000004 RAX!0)
((_ extract 39 32) RBX!0))
(bvadd #x0000000000000005 RAX!0)
((_ extract 47 40) RBX!0))))
(= MEM!1
(store (store a!2
(bvadd #x0000000000000006 RAX!0)
((_ extract 55 48) RBX!0))
(bvadd #x0000000000000007 RAX!0)
((_ extract 63 56) RBX!0))))))
(assert (= RAX!2 RAX!1))
(assert (= RBX!2 RBX!1))
(assert (= RCX!2
(concat (select MEM!1 (bvadd #x0000000000000007 RAX!1))
(select MEM!1 (bvadd #x0000000000000006 RAX!1))
(select MEM!1 (bvadd #x0000000000000005 RAX!1))
(select MEM!1 (bvadd #x0000000000000004 RAX!1))
(select MEM!1 (bvadd #x0000000000000003 RAX!1))
(select MEM!1 (bvadd #x0000000000000002 RAX!1))
(select MEM!1 (bvadd #x0000000000000001 RAX!1))
(select MEM!1 RAX!1))))
(assert (= MEM!2 MEM!1))
(assert (not (= RBX!2 RCX!2)))
C#代碼:
Dictionary<string, string> settings = new Dictionary<string, string>
{
{ "unsat-core", "false" }, // enable generation of unsat cores
{ "model", "false" }, // enable model generation
{ "proof", "false" }, // enable proof generation
{ "timeout", "60000" } // 60000=1min
};
Context ctx = new Context(settings);
Solver solver = ctx.MkSolver(ctx.MkTactic("qfbv"));
BitVecExpr rax0 = ctx.MkBVConst("RAX!0", 64);
BitVecExpr rax1 = ctx.MkBVConst("RAX!1", 64);
BitVecExpr rax2 = ctx.MkBVConst("RAX!2", 64);
BitVecExpr rbx0 = ctx.MkBVConst("RBX!0", 64);
BitVecExpr rbx1 = ctx.MkBVConst("RBX!1", 64);
BitVecExpr rbx2 = ctx.MkBVConst("RBX!2", 64);
BitVecExpr rcx0 = ctx.MkBVConst("RCX!0", 64);
BitVecExpr rcx1 = ctx.MkBVConst("RCX!1", 64);
BitVecExpr rcx2 = ctx.MkBVConst("RCX!2", 64);
ArrayExpr mem0 = ctx.MkArrayConst("MEM!0", ctx.MkBitVecSort(64), ctx.MkBitVecSort(8));
ArrayExpr mem1 = ctx.MkArrayConst("MEM!1", ctx.MkBitVecSort(64), ctx.MkBitVecSort(8));
ArrayExpr mem2 = ctx.MkArrayConst("MEM!2", ctx.MkBitVecSort(64), ctx.MkBitVecSort(8));
solver.Assert(ctx.MkEq(rax1, rax0));
solver.Assert(ctx.MkEq(rbx1, rbx0));
solver.Assert(ctx.MkEq(rcx1, rcx0));
ArrayExpr memX0 = ctx.MkStore(mem0, ctx.MkBVAdd(ctx.MkBV(0, 64), rax0), ctx.MkExtract((1 * 8) - 1, 0 * 8, rbx0));
ArrayExpr memX1 = ctx.MkStore(memX0, ctx.MkBVAdd(ctx.MkBV(1, 64), rax0), ctx.MkExtract((2 * 8) - 1, 1 * 8, rbx0));
ArrayExpr memX2 = ctx.MkStore(memX1, ctx.MkBVAdd(ctx.MkBV(2, 64), rax0), ctx.MkExtract((3 * 8) - 1, 2 * 8, rbx0));
ArrayExpr memX3 = ctx.MkStore(memX2, ctx.MkBVAdd(ctx.MkBV(3, 64), rax0), ctx.MkExtract((4 * 8) - 1, 3 * 8, rbx0));
ArrayExpr memX4 = ctx.MkStore(memX3, ctx.MkBVAdd(ctx.MkBV(4, 64), rax0), ctx.MkExtract((5 * 8) - 1, 4 * 8, rbx0));
ArrayExpr memX5 = ctx.MkStore(memX4, ctx.MkBVAdd(ctx.MkBV(5, 64), rax0), ctx.MkExtract((6 * 8) - 1, 5 * 8, rbx0));
ArrayExpr memX6 = ctx.MkStore(memX5, ctx.MkBVAdd(ctx.MkBV(6, 64), rax0), ctx.MkExtract((7 * 8) - 1, 6 * 8, rbx0));
memX7 = ctx.MkStore(memX6, ctx.MkBVAdd(ctx.MkBV(7, 64), rax0), ctx.MkExtract((8 * 8) - 1, 7 * 8, rbx0));
solver.Assert(ctx.MkEq(mem1, memX7).Simplify() as BoolExpr);
solver.Assert(ctx.MkEq(rax2, rax1));
solver.Assert(ctx.MkEq(rbx2, rbx1));
BitVecExpr y0 = ctx.MkSelect(mem1, ctx.MkBVAdd(ctx.MkBV(0, 64), rax1)) as BitVecExpr;
BitVecExpr y1 = ctx.MkSelect(mem1, ctx.MkBVAdd(ctx.MkBV(1, 64), rax1)) as BitVecExpr;
BitVecExpr y2 = ctx.MkSelect(mem1, ctx.MkBVAdd(ctx.MkBV(2, 64), rax1)) as BitVecExpr;
BitVecExpr y3 = ctx.MkSelect(mem1, ctx.MkBVAdd(ctx.MkBV(3, 64), rax1)) as BitVecExpr;
BitVecExpr y4 = ctx.MkSelect(mem1, ctx.MkBVAdd(ctx.MkBV(4, 64), rax1)) as BitVecExpr;
BitVecExpr y5 = ctx.MkSelect(mem1, ctx.MkBVAdd(ctx.MkBV(5, 64), rax1)) as BitVecExpr;
BitVecExpr y6 = ctx.MkSelect(mem1, ctx.MkBVAdd(ctx.MkBV(6, 64), rax1)) as BitVecExpr;
BitVecExpr y7 = ctx.MkSelect(mem1, ctx.MkBVAdd(ctx.MkBV(7, 64), rax1)) as BitVecExpr;
BitVecExpr y = ctx.MkConcat(y7, ctx.MkConcat(y6, ctx.MkConcat(y5, ctx.MkConcat(y4, ctx.MkConcat(y3, ctx.MkConcat(y2, ctx.MkConcat(y1, y0)))))));
solver.Assert(ctx.MkEq(rcx2, y).Simplify() as BoolExpr);
solver.Assert(ctx.MkEq(mem2, mem1));
Status status_Neg = solver.Check(ctx.MkNot(ctx.MkEq(rbx2, rcx2)));
Console.WriteLine("Status Neg = "+status_Neg); // Go on holiday...
簡化是一個實驗,它並沒有改變它被調用的Expr,我(錯誤地)認爲它沒有太大影響。它確實很重要,但它仍然無法解釋爲什麼SMT2.0版本是0.05秒而c#版本是20秒。 – HJLebbink
我的函數式編程經驗(Lambda微積分和Haskell)有點生疏。您能否提供一個提示,如何將這樣一個未解釋的函數用於將值(8位bitvec)存儲到地址(64位bitvec)中,看起來會如何覆蓋之前的值。破壞性的寫作讓人難以理解(對我而言)。 – HJLebbink
對不起,我的評論是誤導。通過「未解釋的函數」,我不是指SMTLib UF。相反,我的意思是跟蹤自己的內存結構;只生成被訪問的值。從某種意義上說,您將在SMTLib之外進行編碼,並將執行的「跟蹤」轉換爲SMTLib。我不熟悉C#接口,但如果你願意嘗試Haskell,我可以幫助使用SBV編碼。 (http://leventerkok.github.io/sbv/)。特別是,這個例子看起來很相關:https://hackage.haskell.org/package/sbv-6.1/docs/Data-SBV-Examples-BitPrecise-Legato.html –