Skip to content

Commit baa04b8

Browse files
committed
saw-client: Support setting SAW options
This adds a `set_option` function to the `saw_client` module that controls whether various global SAW options should be enabled or disabled. The possible options that can be controlled are found in the `saw_client.option` module. Fixes #1493.
1 parent 5745d37 commit baa04b8

File tree

9 files changed

+131
-2
lines changed

9 files changed

+131
-2
lines changed

saw-remote-api/python/CHANGELOG.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
## 0.9.1 -- YYYY-MM-DD
44

5-
* NEW CHANGELOG ENTRIES SINCE 0.9.0 GO HERE
5+
* Add a `set_option` command to `saw_client` that allows enablig and disabling
6+
global SAW options. For example, SAWScript's `enable_lax_pointer_ordering` is
7+
equivalent to `set_option(LaxPointerOrdering(), True)` in `saw-client.
8+
`LaxPointerOrdering`, as well as all other possible options, can be imported
9+
from the `saw_client.option` module.
610

711

812
## 0.9.0 -- 2021-11-19

saw-remote-api/python/saw_client/__init__.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from argo_client.connection import ServerConnection
1515
from . import llvm
1616
from . import exceptions
17+
from . import option
1718
from . import proofscript
1819

1920
__designated_connection = None # type: Optional[connection.SAWConnection]
@@ -357,7 +358,7 @@ def on_finish(self) -> None:
357358
def on_abort(self) -> None:
358359
print("🛑 Aborting proof script.", file=self.file)
359360

360-
361+
361362
def view(v: View) -> None:
362363
"""Add a view to the global list of views. Future verification results will
363364
be handed to this view, and its on_finish() handler will be called at
@@ -648,6 +649,12 @@ def prove(goal: cryptoltypes.CryptolJSON,
648649
pr.counterexample = None
649650
return pr
650651

652+
def set_option(option : option.SAWOption, value : bool) -> None:
653+
"""Set a boolean-valued SAW option."""
654+
global __designated_connection
655+
if __designated_connection is not None:
656+
__designated_connection.set_option(option=option,value=value)
657+
651658
@atexit.register
652659
def script_exit() -> None:
653660
global __designated_views

saw-remote-api/python/saw_client/commands.py

+13
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from cryptol import cryptoltypes
55
from . import exceptions
66
from .proofscript import *
7+
from .option import *
78

89
from typing import Any, List
910

@@ -199,3 +200,15 @@ def __init__(self, connection : argo.HasProtocolState) -> None:
199200
{},
200201
connection
201202
)
203+
204+
205+
class SAWSetOption(SAWCommand):
206+
def __init__(self, connection : argo.HasProtocolState,
207+
option : SAWOption, value : bool,
208+
timeout : Optional[float]) -> None:
209+
params = {'option': str(option),
210+
'value': value}
211+
super(SAWSetOption, self).__init__('SAW/set option', params, connection, timeout=timeout)
212+
213+
def process_result(self, _res : Any) -> Any:
214+
return None

saw-remote-api/python/saw_client/connection.py

+9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from argo_client.connection import ServerConnection, DynamicSocketProcess, HttpProcess, ManagedProcess
77
from argo_client.interaction import Interaction, Command
88
from .commands import *
9+
from .option import *
910
from typing import Optional, Union, Any, List, TextIO
1011

1112
# FIXME cryptol_path isn't always used...?
@@ -237,3 +238,11 @@ def prove(self,
237238
"""
238239
self.most_recent_result = Prove(self, goal, proof_script, timeout)
239240
return self.most_recent_result
241+
242+
def set_option(self,
243+
option : SAWOption,
244+
value : bool,
245+
timeout : Optional[float] = None) -> Command:
246+
"""Set a boolean-valued SAW option."""
247+
self.most_recent_result = SAWSetOption(self, option, value, timeout)
248+
return self.most_recent_result
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from abc import ABCMeta, abstractmethod
2+
from typing import Any
3+
4+
class SAWOption(metaclass=ABCMeta):
5+
@abstractmethod
6+
def __str__(self) -> str: pass
7+
8+
9+
class LaxArithmetic(SAWOption):
10+
def __str__(self) -> str:
11+
return "lax arithmetic"
12+
13+
14+
class LaxPointerOrdering(SAWOption):
15+
def __str__(self) -> str:
16+
return "lax pointer ordering"
17+
18+
19+
class DebugIntrinsics(SAWOption):
20+
def __str__(self) -> str:
21+
return "debug intrinsics"
22+
23+
24+
class SMTArrayMemoryModel(SAWOption):
25+
def __str__(self) -> str:
26+
return "SMT array memory model"
27+
28+
29+
class What4HashConsing(SAWOption):
30+
def __str__(self) -> str:
31+
return "What4 hash consing"

saw-remote-api/python/tests/saw/test-files/Makefile

+4
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@ all: $(BC_FILES)
55

66
%.bc: %.c
77
clang -g -c -emit-llvm -o $@ $<
8+
9+
# This test case crucially relies on the use of -O2.
10+
llvm_lax_pointer_ordering.bc: llvm_lax_pointer_ordering.c
11+
clang -O2 -g -c -emit-llvm -o $@ $<
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include <stdint.h>
2+
#include <stdlib.h>
3+
4+
const size_t LEN = 42;
5+
6+
void zip_with_add(uint64_t c[LEN], const uint64_t a[LEN], const uint64_t b[LEN]) {
7+
for (size_t i = 0; i < LEN; i++) {
8+
c[i] = a[i] + b[i];
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from pathlib import Path
2+
import unittest
3+
from saw_client import *
4+
from saw_client.llvm import Contract, FreshVar, LLVMType, SetupVal, array_ty, cryptol, i64, void
5+
from saw_client.option import LaxPointerOrdering
6+
7+
8+
LEN = 42
9+
10+
11+
def ptr_to_fresh(c : Contract, ty : LLVMType, name : Optional[str] = None,
12+
read_only : bool = False) -> Tuple[FreshVar, SetupVal]:
13+
"""Add to ``Contract`` ``c`` an allocation of a pointer of type ``ty`` initialized to an unknown fresh value.
14+
If ``read_only == True`` then the allocated memory is immutable.
15+
16+
:returns A fresh variable bound to the pointers initial value and the newly allocated pointer. (The fresh
17+
variable will be assigned ``name`` if provided/available.)"""
18+
var = c.fresh_var(ty, name)
19+
ptr = c.alloc(ty, points_to = var, read_only = read_only)
20+
return (var, ptr)
21+
22+
23+
class ZipWithAddContract(Contract):
24+
def specification(self):
25+
array_t = array_ty(LEN, i64)
26+
c_ptr = self.alloc(array_t)
27+
(a, a_ptr) = ptr_to_fresh(self, name='a', ty=array_t, read_only=True)
28+
(b, b_ptr) = ptr_to_fresh(self, name='b', ty=array_t, read_only=True)
29+
30+
self.execute_func(c_ptr, a_ptr, b_ptr)
31+
32+
self.points_to(c_ptr, cryptol(f'zipWith`{{ {LEN} }} (+) {a.name()} {b.name()}'))
33+
self.returns(void)
34+
35+
36+
class LLVMPointerTest(unittest.TestCase):
37+
38+
def test_llvm_pointer(self):
39+
connect(reset_server=True)
40+
if __name__ == "__main__": view(LogResults())
41+
bcname = str(Path('tests','saw','test-files', 'llvm_lax_pointer_ordering.bc'))
42+
mod = llvm_load_module(bcname)
43+
44+
set_option(LaxPointerOrdering(), True)
45+
46+
result = llvm_verify(mod, 'zip_with_add', ZipWithAddContract())
47+
self.assertIs(result.is_success(), True)
48+
49+
50+
if __name__ == "__main__":
51+
unittest.main()

0 commit comments

Comments
 (0)