24
24
except ImportError :
25
25
_is_pypy = False
26
26
27
+ def z3_expr_to_smt2 (f , status = "unknown" , name = "benchmark" , logic = "" ):
28
+ # from https://stackoverflow.com/a/14629021/9719920
29
+ v = (z3 .Ast * 0 )()
30
+ return z3 .Z3_benchmark_to_smtlib_string (f .ctx_ref (), name , logic , status , "" , 0 , v , f .as_ast ())
31
+
27
32
def _add_memory_pressure (p ):
28
33
"""
29
34
PyPy's garbage collector is not aware of memory uses happening inside native code. When performing memory-intensive
@@ -46,6 +51,9 @@ def _add_memory_pressure(p):
46
51
47
52
supports_fp = hasattr (z3 , 'fpEQ' )
48
53
54
+ # you can toggle this flag if you want. I don't think it matters
55
+ #z3.set_param('rewriter.hi_fp_unspecified', 'true')
56
+
49
57
#
50
58
# Utility functions
51
59
#
@@ -69,18 +77,8 @@ def raw_caller(*args, **kwargs):
69
77
return raw_caller
70
78
71
79
def _z3_decl_name_str (ctx , decl ):
72
- # reimplementation of Z3_get_symbol_string to not try to unicode-decode
73
- lib = z3 .lib ()
74
-
75
- decl_name = lib .Z3_get_decl_name (ctx , decl )
76
- err = lib .Z3_get_error_code (ctx )
77
- if err != z3 .Z3_OK :
78
- raise z3 .Z3Exception (lib .Z3_get_error_msg (ctx , err ))
79
-
80
- symbol_name = lib .Z3_get_symbol_string (ctx , decl_name )
81
- err = lib .Z3_get_error_code (ctx )
82
- if err != z3 .Z3_OK :
83
- raise z3 .Z3Exception (z3 .lib ().Z3_get_error_msg (ctx , err ))
80
+ decl_name = z3 .Z3_get_decl_name (ctx , decl )
81
+ symbol_name = z3 .Z3_get_symbol_string_bytes (ctx , decl_name )
84
82
return symbol_name
85
83
86
84
@@ -562,20 +560,54 @@ def _abstract_to_primitive(self, ctx, ast):
562
560
op_name = op_map [z3_op_nums [decl_num ]]
563
561
564
562
if op_name == 'BitVecVal' :
565
- if z3 .Z3_get_numeral_uint64 (ctx , ast , self ._c_uint64_p ):
566
- return self ._c_uint64_p .contents .value
567
- else :
568
- bv_num = int (z3 .Z3_get_numeral_string (ctx , ast ))
569
- return bv_num
563
+ return self ._abstract_bv_val (ctx , ast )
570
564
elif op_name == 'True' :
571
565
return True
572
566
elif op_name == 'False' :
573
567
return False
574
568
elif op_name in ('FPVal' , 'MinusZero' , 'MinusInf' , 'PlusZero' , 'PlusInf' , 'NaN' ):
575
569
return self ._abstract_fp_val (ctx , ast , op_name )
570
+ elif op_name == 'Concat' :
571
+ # Quirk in how z3 might handle NaN encodings - it will not give us a fully evaluated model
572
+ # https://github.com/Z3Prover/z3/issues/518
573
+ # this case will be triggered if the z3 rewriter.hi_fp_unspecified is set to true
574
+ nargs = z3 .Z3_get_app_num_args (ctx , ast )
575
+ res = 0
576
+ for i in range (nargs ):
577
+ arg_ast = z3 .Z3_get_app_arg (ctx , ast , i )
578
+ arg_decl = z3 .Z3_get_app_decl (ctx , arg_ast )
579
+ arg_decl_num = z3 .Z3_get_decl_kind (ctx , arg_decl )
580
+ arg_size = z3 .Z3_get_bv_sort_size (ctx , z3 .Z3_get_sort (ctx , arg_ast ))
581
+
582
+ neg = False
583
+ if arg_decl_num == z3 .Z3_OP_BNEG :
584
+ arg_ast = z3 .Z3_get_app_arg (ctx , arg_ast , 0 )
585
+ arg_decl = z3 .Z3_get_app_decl (ctx , arg_ast )
586
+ arg_decl_num = z3 .Z3_get_decl_kind (ctx , arg_decl )
587
+ neg = True
588
+ if arg_decl_num != z3 .Z3_OP_BNUM :
589
+ raise BackendError ("Weird z3 model" )
590
+
591
+ arg_int = self ._abstract_bv_val (ctx , arg_ast )
592
+ if neg :
593
+ arg_int = (1 << arg_size )- arg_int
594
+ res <<= arg_size
595
+ res |= arg_int
596
+ return res
597
+ elif op_name == 'fpToIEEEBV' :
598
+ # Another quirk in the way z3 might handle nan encodings. see above
599
+ # this case will be triggered if the z3 rewriter.hi_fp_unspecified is set to false
600
+ arg_ast = z3 .Z3_get_app_arg (ctx , ast , 0 )
601
+ return self ._abstract_fp_encoded_val (ctx , arg_ast )
576
602
else :
577
603
raise BackendError ("Unable to abstract Z3 object to primitive" )
578
604
605
+ def _abstract_bv_val (self , ctx , ast ):
606
+ if z3 .Z3_get_numeral_uint64 (ctx , ast , self ._c_uint64_p ):
607
+ return self ._c_uint64_p .contents .value
608
+ else :
609
+ return int (z3 .Z3_get_numeral_string (ctx , ast ))
610
+
579
611
def _abstract_fp_val (self , ctx , ast , op_name ):
580
612
if op_name == 'FPVal' :
581
613
# TODO: do better than this
@@ -599,6 +631,47 @@ def _abstract_fp_val(self, ctx, ast, op_name):
599
631
else :
600
632
raise BackendError ("Called _abstract_fp_val with unknown type" )
601
633
634
+ def _abstract_fp_encoded_val (self , ctx , ast ):
635
+ decl = z3 .Z3_get_app_decl (ctx , ast )
636
+ decl_num = z3 .Z3_get_decl_kind (ctx , decl )
637
+ op_name = op_map [z3_op_nums [decl_num ]]
638
+ sort = z3 .Z3_get_sort (ctx , ast )
639
+ ebits = z3 .Z3_fpa_get_ebits (ctx , sort )
640
+ sbits = z3 .Z3_fpa_get_sbits (ctx , sort ) - 1 # includes sign bit
641
+
642
+ if op_name == 'FPVal' :
643
+ # TODO: do better than this
644
+ fp_mantissa = int (z3 .Z3_fpa_get_numeral_significand_string (ctx , ast ))
645
+ fp_exp = int (z3 .Z3_fpa_get_numeral_exponent_string (ctx , ast , True ))
646
+ fp_sign_c = ctypes .c_int ()
647
+ z3 .Z3_fpa_get_numeral_sign (ctx , ast , ctypes .byref (fp_sign_c ))
648
+ fp_sign = 1 if fp_sign_c .value != 0 else 0
649
+ elif op_name == 'MinusZero' :
650
+ fp_sign = 1
651
+ fp_exp = 0
652
+ fp_mantissa = 0
653
+ elif op_name == 'MinusInf' :
654
+ fp_sign = 1
655
+ fp_exp = (1 << ebits ) - 1
656
+ fp_mantissa = 0
657
+ elif op_name == 'PlusZero' :
658
+ fp_sign = 0
659
+ fp_exp = 0
660
+ fp_mantissa = 0
661
+ elif op_name == 'PlusInf' :
662
+ fp_sign = 0
663
+ fp_exp = (1 << ebits ) - 1
664
+ fp_mantissa = 0
665
+ elif op_name == 'NaN' :
666
+ fp_sign = 0
667
+ fp_exp = (1 << ebits ) - 1
668
+ fp_mantissa = 1
669
+ else :
670
+ raise BackendError ("Called _abstract_fp_val with unknown type" )
671
+
672
+ value = (fp_sign << (ebits + sbits )) | (fp_exp << sbits ) | fp_mantissa
673
+ return value
674
+
602
675
def solver (self , timeout = None ):
603
676
if not self .reuse_z3_solver or getattr (self ._tls , 'solver' , None ) is None :
604
677
s = z3 .Solver (ctx = self ._context )
@@ -1047,6 +1120,14 @@ def _op_raw_fpGEQ(self, a, b):
1047
1120
def _op_raw_fpEQ (self , a , b ):
1048
1121
return z3 .BoolRef (z3 .Z3_mk_fpa_eq (self ._context .ref (), a .as_ast (), b .as_ast ()), self ._context )
1049
1122
1123
+ @condom
1124
+ def _op_raw_fpIsNaN (self , a ):
1125
+ return z3 .BoolRef (z3 .Z3_mk_fpa_is_nan (self ._context .ref (), a .as_ast ()), self ._context )
1126
+
1127
+ @condom
1128
+ def _op_raw_fpIsInf (self , a ):
1129
+ return z3 .BoolRef (z3 .Z3_mk_fpa_is_inf (self ._context .ref (), a .as_ast ()), self ._context )
1130
+
1050
1131
@condom
1051
1132
def _op_raw_fpFP (self , sgn , exp , sig ):
1052
1133
return z3 .FPRef (z3 .Z3_mk_fpa_fp (self ._context .ref (), sgn .ast , exp .ast , sig .ast ), self ._context )
0 commit comments