Skip to content

Commit 91cceb8

Browse files
committed
finite set. sig on ir.blocks
1 parent 6e4f4e6 commit 91cceb8

File tree

4 files changed

+97
-16
lines changed

4 files changed

+97
-16
lines changed

src/kdrag/contrib/ir/__init__.py

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,34 +34,50 @@ class Block:
3434
insns: list[smt.ExprRef]
3535

3636
@classmethod
37-
def of_expr(cls, e: smt.ExprRef) -> "Block":
37+
def of_defined_fun(cls, f: smt.FuncDeclRef) -> "Block":
38+
"""
39+
>>> x, y = smt.Ints("x y")
40+
>>> f = kd.define("f809", [x,y], x + x + y)
41+
>>> Block.of_defined_fun(f)
42+
^(Int,Int):
43+
%0 = + %var0, %var0
44+
%1 = + %0, %var1
45+
"""
46+
defn = kd.kernel.defns.get(f)
47+
if defn is None:
48+
raise ValueError(f"Function {f} is not defined to knuckledragger")
49+
else:
50+
body = defn._subst_fun_body
51+
return cls.of_expr(body, sig=[f.domain(i) for i in range(f.arity())])
52+
53+
@classmethod
54+
def of_expr(cls, e: smt.ExprRef, sig=[]) -> "Block":
3855
"""
3956
>>> x,y = smt.BitVecs("x y", 64)
4057
>>> x,y = smt.Var(1, smt.BitVecSort(64)), smt.Var(0, smt.BitVecSort(64))
4158
>>> z = smt.BitVec("z", 64)
42-
>>> Block.of_expr(smt.If(True, (x + y)*42, x - y + z))
59+
>>> Block.of_expr(smt.If(True, (x + y)*42, x - y + z), [smt.BitVecSort(64), smt.BitVecSort(64)])
4360
^(bv64,bv64):
4461
%0 = bvadd %var1, %var0
4562
%1 = bvmul %0, 42
4663
%2 = bvsub %var1, %var0
4764
%3 = bvadd %2, z
4865
%4 = if True, %1, %3
4966
"""
50-
sig = {}
51-
insns = []
52-
seen = set()
53-
todo = [e]
67+
if not smt.is_if(e):
68+
insns = []
69+
seen = set()
70+
todo = [e]
71+
else:
72+
insns = [e]
73+
seen = set(e.children())
74+
todo = list(e.children())
5475
while todo:
5576
e = todo.pop()
5677
# if smt.is_const(e) and not kd.utils.is_value(e):
5778
# args.append(e)
5879
if smt.is_var(e):
59-
idx = smt.get_var_index(e)
60-
sort = sig.get(idx)
61-
if sort is None:
62-
sig[idx] = e.sort()
63-
else:
64-
assert sig[idx] == e.sort()
80+
pass
6581
elif smt.is_const(e):
6682
continue
6783
else:
@@ -70,7 +86,6 @@ def of_expr(cls, e: smt.ExprRef) -> "Block":
7086
if arg not in seen:
7187
seen.add(arg)
7288
todo.append(arg)
73-
sig = [sig[idx] for idx in range(len(sig))]
7489
insns.reverse()
7590
return cls(sig=sig, insns=insns)
7691

@@ -98,7 +113,7 @@ def __repr__(self) -> str:
98113
rhs = str(insn)
99114
else:
100115
rhs = f"{insn.decl().name()} {", ".join(self.vname(arg) for arg in insn.children())}"
101-
res.append(f"%{i} = {rhs}")
116+
res.append(f"\t%{i} = {rhs}")
102117
return "\n".join(res)
103118

104119
def succ_calls(self) -> list[smt.ExprRef]:
@@ -119,6 +134,12 @@ class Function:
119134
entry: Label # smt.FuncDeclRef?
120135
blocks: dict[Label, Block] # 0th block is entry. Or "entry" is entry? Naw. 0th.
121136

137+
@classmethod
138+
def of_defined_funs(cls, funs: list[smt.FuncDeclRef]):
139+
blocks = {f.name(): Block.of_defined_fun(f) for f in funs}
140+
entry = funs[0].name()
141+
return cls(entry=entry, blocks=blocks)
142+
122143
def calls_of(self) -> dict[str, list[tuple[Label, smt.ExprRef]]]:
123144
"""
124145
Returns a mapping from labels to a list of calls to that label
@@ -140,6 +161,14 @@ def phis(self):
140161
phis[label] = zip(*[call.children() for _, call in preds[label]])
141162
return phis
142163

164+
def __repr__(self) -> str:
165+
res = [f"fn {self.entry} {{"]
166+
for label, blk in self.blocks.items():
167+
res.append(f"@{label}:")
168+
res.append(str(blk))
169+
res.append("}")
170+
return "\n".join(res)
171+
143172

144173
@dataclass
145174
class Spec:

src/kdrag/contrib/pcode/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,18 @@ def sym_execute(
753753
todo.append((memstate1, pc1, max_insns, path_cond)) # type: ignore
754754
return res
755755

756+
def execute_block(self, memstate: MemState, addr: int) -> list[SimState]:
757+
path_cond = None
758+
while True:
759+
states = self.sym_execute(memstate, addr, path_cond=path_cond)
760+
if len(states) != 1 or states[0].pc[0] < addr:
761+
return states
762+
else:
763+
memstate = states[0].memstate
764+
(addr, pc) = states[0].pc
765+
assert pc == 0
766+
path_cond = states[0].path_cond
767+
756768
def get_reg(self, memstate: MemState, regname: str) -> smt.BitVecRef:
757769
"""
758770
Get the value of a register from the memstate.

src/kdrag/theories/int.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import kdrag.smt as smt
77

88
Z = smt.IntSort()
9+
ZSet = smt.SetSort(Z)
10+
ISet = ZSet
911

1012

1113
def induct_nat(x, P):
@@ -156,7 +158,7 @@ def induct_nat_strong(x, P):
156158
dvd_fact = _l.qed().forall([m, n])
157159

158160

159-
P = smt.Array("P", Z, smt.BoolSort())
161+
P = smt.Const("P", ZSet) # smt.Array("P", Z, smt.BoolSort())
160162
search = kd.define("search", [P, n], smt.If(P[n], n, smt.If(P[-n], -n, P[n + 1])))
161163
choose = kd.notation.choose.define([P], search(P, 0))
162164
# A choice operator. Also a relative of Kleene Mu
@@ -178,3 +180,16 @@ def induct_nat_strong(x, P):
178180

179181
# 0 <= bchoose() <= N
180182
bexists = kd.define("bexists", [P, N], P[bchoose(P, N)])
183+
184+
185+
A = smt.Const("A", ZSet)
186+
has_ub = kd.define("ZSet.has_ub", [A, y], kd.QForAll([x], A[x], x <= y))
187+
# closed under union,inter,etc.
188+
# closed upward has_ub_le
189+
190+
191+
is_ub = kd.define("ZSet.is_ub", [A], smt.Exists([x], has_ub(A, x)))
192+
has_lb = kd.define("ZSet.has_lb", [A, y], kd.QForAll([x], A[x], y <= x))
193+
is_lb = kd.define("ZSet.is_lb", [A], smt.Exists([x], has_lb(A, x)))
194+
# has_bounds = kd.define("has_bounds", [A,x,y], smt.And(has_ub(A,x), has_lb(A,y)))
195+
finite = kd.define("ZSet.finite", [A], smt.And(is_ub(A), is_lb(A)))

src/kdrag/theories/set.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,9 @@ def Finite(A: smt.ArrayRef) -> smt.BoolRef:
315315
"""
316316
A set is finite if it has a finite number of elements.
317317
318-
See https://cvc5.github.io/docs/cvc5-1.1.2/theories/sets-and-relations.html#finite-sets
318+
See:
319+
- https://cvc5.github.io/docs/cvc5-1.1.2/theories/sets-and-relations.html#finite-sets
320+
- https://isabelle.in.tum.de/library/HOL/HOL-Library/FSet.html
319321
320322
>>> IntSet = Set(smt.IntSort())
321323
>>> kd.prove(Finite(IntSet.empty))
@@ -334,6 +336,29 @@ def Finite(A: smt.ArrayRef) -> smt.BoolRef:
334336
# return A == smt.Lambda([x], smt.Contains(finwit(A), smt.Unit(x))
335337

336338

339+
@functools.cache
340+
def finite_decl(T: smt.SortRef) -> smt.FuncDeclRef:
341+
"""
342+
Abstracted finite predicate for sets of T.
343+
"""
344+
A = smt.Const("A", smt.SetSort(T))
345+
return kd.define(f"{T.name()}.finite", [A], Finite(A))
346+
347+
348+
def finite(A):
349+
doms = smt.domains(A)
350+
assert len(doms) == 1
351+
return finite_decl(doms[0])(A)
352+
353+
354+
@functools.cache
355+
def finite_empty(T: smt.SortRef) -> kd.Proof:
356+
"""
357+
Prove that the empty set is finite for sets of T.
358+
"""
359+
return kd.prove(finite(Set(T).empty), unfold=True)
360+
361+
337362
RSet = Set(smt.RealSort())
338363
IntSet = Set(smt.IntSort())
339364
BoolSet = Set(smt.BoolSort())

0 commit comments

Comments
 (0)