Skip to content

Commit a6083bb

Browse files
committed
Simplify: fold comparisons between integers
This deserves a look to make sure we're not messing up wrt overflow, promotion, etc. I think this is correct since 1) we keep the invariant that integer constants are in bounds (see the asserts) and 2) we are only folding comparisons where the width of the operator and the width of both constants exactly match.
1 parent 51ce3e9 commit a6083bb

3 files changed

Lines changed: 237 additions & 0 deletions

File tree

lib/Simplify.ml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,24 @@ let constant_fold = object (self)
553553
| _ -> EApp (e, [e1])
554554
)
555555

556+
| EOp ((K.Eq | K.Neq | K.Lt | K.Lte | K.Gt | K.Gte) as cmp, TInt w), [ e1; e2 ] -> (
557+
let e1 = self#visit_expr env e1 in
558+
let e2 = self#visit_expr env e2 in
559+
match e1.node, e2.node with
560+
| EConstant (w1, s1), EConstant (w2, s2) when K.is_int w && w = w1 && w1 = w2 ->
561+
assert (K.is_valid_int (w1, s1) && K.is_valid_int (w2, s2));
562+
let c = Z.compare (Z.of_string s1) (Z.of_string s2) in
563+
EBool (match cmp with
564+
| K.Eq -> c = 0
565+
| K.Neq -> c <> 0
566+
| K.Lt -> c < 0
567+
| K.Lte -> c <= 0
568+
| K.Gt -> c > 0
569+
| K.Gte -> c >= 0
570+
| _ -> assert false)
571+
| _ -> EApp (e, [ e1; e2 ])
572+
)
573+
556574
| _ ->
557575
EApp (self#visit_expr env e, List.map (self#visit_expr env) es)
558576

test/pulse/IntCmpFold.expected.c

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/* krml header omitted for test repeatability */
2+
3+
4+
#include "IntCmpFold.h"
5+
6+
void IntCmpFold_main(void)
7+
{
8+
IntCmpFold_f(false);
9+
IntCmpFold_f(true);
10+
IntCmpFold_f(true);
11+
IntCmpFold_f(false);
12+
IntCmpFold_f(false);
13+
IntCmpFold_f(true);
14+
IntCmpFold_f(true);
15+
IntCmpFold_f(false);
16+
IntCmpFold_f(true);
17+
}
18+
19+
void IntCmpFold_main_signed(void)
20+
{
21+
IntCmpFold_f(false);
22+
IntCmpFold_f(true);
23+
IntCmpFold_f(true);
24+
IntCmpFold_f(false);
25+
IntCmpFold_f(false);
26+
IntCmpFold_f(true);
27+
IntCmpFold_f(true);
28+
IntCmpFold_f(false);
29+
IntCmpFold_f(true);
30+
}
31+
32+
void IntCmpFold_main_uint8(void)
33+
{
34+
IntCmpFold_f(true);
35+
IntCmpFold_f(false);
36+
}
37+
38+
void IntCmpFold_main_int8(void)
39+
{
40+
IntCmpFold_f(true);
41+
IntCmpFold_f(false);
42+
}
43+
44+
void IntCmpFold_main_uint16(void)
45+
{
46+
IntCmpFold_f(true);
47+
IntCmpFold_f(false);
48+
}
49+
50+
void IntCmpFold_main_int16(void)
51+
{
52+
IntCmpFold_f(true);
53+
IntCmpFold_f(false);
54+
}
55+
56+
void IntCmpFold_main_uint64(void)
57+
{
58+
IntCmpFold_f(true);
59+
IntCmpFold_f(false);
60+
}
61+
62+
void IntCmpFold_main_int64(void)
63+
{
64+
IntCmpFold_f(true);
65+
IntCmpFold_f(false);
66+
}
67+
68+
void IntCmpFold_main_sizet(void)
69+
{
70+
IntCmpFold_f(true);
71+
IntCmpFold_f(false);
72+
}
73+
74+
void IntCmpFold_main_var(uint32_t z)
75+
{
76+
IntCmpFold_f(1U == z);
77+
IntCmpFold_f(z < 1U);
78+
}
79+
80+
void IntCmpFold_main_var_signed(int32_t z)
81+
{
82+
IntCmpFold_f(-1 == z);
83+
IntCmpFold_f(z < -1);
84+
}
85+
86+
void IntCmpFold_main_var_narrow(uint8_t z)
87+
{
88+
IntCmpFold_f(255U == z);
89+
IntCmpFold_f(z < 255U);
90+
}
91+
92+
void IntCmpFold_cast_prevents_fold(void)
93+
{
94+
IntCmpFold_f((uint16_t)1U < 2U);
95+
}
96+

test/pulse/IntCmpFold.fst

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
module IntCmpFold
2+
3+
assume
4+
val f : bool -> Dv unit
5+
6+
let main () =
7+
let open FStar.UInt32 in
8+
[@@CInline] let x = 1ul in
9+
[@@CInline] let y = 2ul in
10+
f (x = y);
11+
f (x < y);
12+
f (x <= y);
13+
f (x > y);
14+
f (x >= y);
15+
f (x <> y);
16+
f (x = x);
17+
f (x < x);
18+
f (x <= x);
19+
()
20+
21+
(* Same, but signed, and with a negative constant: the comparisons must be
22+
folded according to the *signed* order. *)
23+
let main_signed () =
24+
let open FStar.Int32 in
25+
[@@CInline] let x = (-1l) in
26+
[@@CInline] let y = 1l in
27+
f (x = y);
28+
f (x < y);
29+
f (x <= y);
30+
f (x > y);
31+
f (x >= y);
32+
f (x <> y);
33+
f (x = x);
34+
f (x < x);
35+
f (x <= x);
36+
()
37+
38+
(* The extremal values of every width. The [@@CInline] lets are what keeps F*
39+
from doing the folding itself, before krml even sees the comparison. *)
40+
let main_uint8 () =
41+
let open FStar.UInt8 in
42+
[@@CInline] let x = 0uy in
43+
[@@CInline] let y = 255uy in
44+
f (x < y);
45+
f (y < x);
46+
()
47+
48+
let main_int8 () =
49+
let open FStar.Int8 in
50+
[@@CInline] let x = (-128y) in
51+
[@@CInline] let y = 127y in
52+
f (x < y);
53+
f (y < x);
54+
()
55+
56+
let main_uint16 () =
57+
let open FStar.UInt16 in
58+
[@@CInline] let x = 0us in
59+
[@@CInline] let y = 65535us in
60+
f (x < y);
61+
f (y < x);
62+
()
63+
64+
let main_int16 () =
65+
let open FStar.Int16 in
66+
[@@CInline] let x = (-32768s) in
67+
[@@CInline] let y = 32767s in
68+
f (x < y);
69+
f (y < x);
70+
()
71+
72+
let main_uint64 () =
73+
let open FStar.UInt64 in
74+
[@@CInline] let x = 0uL in
75+
[@@CInline] let y = 18446744073709551615uL in
76+
f (x < y);
77+
f (y < x);
78+
()
79+
80+
(* Note: reading these two as unsigned would flip both answers. *)
81+
let main_int64 () =
82+
let open FStar.Int64 in
83+
[@@CInline] let x = (-9223372036854775808L) in
84+
[@@CInline] let y = 9223372036854775807L in
85+
f (x < y);
86+
f (y < x);
87+
()
88+
89+
let main_sizet () =
90+
let open FStar.SizeT in
91+
[@@CInline] let x = 1sz in
92+
f (x = x);
93+
f (x < x);
94+
()
95+
96+
(* Nothing to fold here: only one side is a constant. *)
97+
let main_var (z : UInt32.t) =
98+
let open FStar.UInt32 in
99+
[@@CInline] let x = 1ul in
100+
f (x = z);
101+
f (z < x);
102+
()
103+
104+
(* Idem, at a signed type and with a negative constant. *)
105+
let main_var_signed (z : Int32.t) =
106+
let open FStar.Int32 in
107+
[@@CInline] let x = (-1l) in
108+
f (x = z);
109+
f (z < x);
110+
()
111+
112+
(* Idem, at a narrow width, where C would promote both operands to int. *)
113+
let main_var_narrow (z : UInt8.t) =
114+
let open FStar.UInt8 in
115+
[@@CInline] let x = 255uy in
116+
f (x = z);
117+
f (z < x);
118+
()
119+
120+
let cast_prevents_fold () =
121+
[@@CInline] let x = 1uy in
122+
[@@CInline] let y = 2us in
123+
f (FStar.Int.Cast.uint8_to_uint16 x `FStar.UInt16.lt` y)

0 commit comments

Comments
 (0)